diff --git a/composer.json b/composer.json index adf2356..03aee07 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "doctrine/doctrine-bundle": "^2.7", "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.13", + "league/csv": "^9.0", "rbdwllr/reallysimplejwt": "^5.0", "sensio/framework-extra-bundle": "^6.2", "symfony/console": "6.1.*", diff --git a/composer.lock b/composer.lock index 8963d5a..dc6a7fe 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "eb3c50bec813d049150ad9f4cf2b9617", + "content-hash": "f31b264b29ff1c91409f2abfcd475ad0", "packages": [ { "name": "doctrine/annotations", @@ -1524,6 +1524,90 @@ ], "time": "2022-11-21T01:32:31+00:00" }, + { + "name": "league/csv", + "version": "9.8.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/9d2e0265c5d90f5dd601bc65ff717e05cec19b47", + "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^v3.4.0", + "phpstan/phpstan": "^1.3.0", + "phpstan/phpstan-phpunit": "^1.0.0", + "phpstan/phpstan-strict-rules": "^1.1.0", + "phpunit/phpunit": "^9.5.11" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "https://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2022-01-04T00:13:07+00:00" + }, { "name": "psr/cache", "version": "3.0.0", diff --git a/public/assets/css/front/frame.css b/public/assets/css/front/frame.css index e7bd387..be623b0 100644 --- a/public/assets/css/front/frame.css +++ b/public/assets/css/front/frame.css @@ -50,5 +50,6 @@ sup { color: var(--white); padding: 2px; border-radius: 3px; - vertical-align: text-bottom; + vertical-align: baseline; + font-family: var(--mono-type); } diff --git a/public/assets/css/front/index-den.css b/public/assets/css/front/index-den.css index 8005d6b..dbf7c3a 100644 --- a/public/assets/css/front/index-den.css +++ b/public/assets/css/front/index-den.css @@ -18,5 +18,10 @@ section[role="loc-index"] { section a { color: var(--white); + border-bottom: 1px solid var(--highlight); +} + +section a:hover { border-bottom: 1px solid var(--secondary); + padding-bottom: 2px; } diff --git a/src/Controller/Routes/Back/Locations.php b/src/Controller/Routes/Back/Locations.php index ddb160a..c3da8cb 100644 --- a/src/Controller/Routes/Back/Locations.php +++ b/src/Controller/Routes/Back/Locations.php @@ -12,6 +12,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RequestStack; use Doctrine\Persistence\ManagerRegistry; use App\Service\HandleLocations; +use Doctrine\DBAL\Connection; //use App\Utils\PageRender; //use App\Utils\StringTools; use App\Service\Auth; @@ -36,6 +37,8 @@ class Locations extends AbstractController RequestStack $requestStack, Auth $auth, HandleLocations $locations, + ManagerRegistry $doctrine, + Connection $connection, string $pageNum ): Response { $result = $auth->status(); @@ -43,6 +46,11 @@ class Locations extends AbstractController $session = $requestStack->getSession(); $member = $session->get("member"); $list = $locations->getLocationsPage($pageNum); + + //$search = $connection->fetchAllAssociative("SELECT * FROM searchlocations('agenda')"); + + //var_dump($search[0]["name"]); + return $this->render("back/locations.twig", [ "title" => "Bad Space | Locations", "handle" => $member->getHandle(), @@ -140,6 +148,86 @@ class Locations extends AbstractController } } + /** + * @Route("/den/locations/bulk-add", name="location-bulk-add") + */ + public function bulkAddLocation( + Request $request, + Auth $auth, + HandleLocations $locations, + ManagerRegistry $doctrine, + FileUploader $uploader + ): Response { + $result = $auth->status(); + if ($result["status"]) { + if ($request->getMethod() == "GET") { + return $this->render("back/locations.twig", [ + "title" => "Bad Space | Locations | Bulk Add", + "mode" => "bulk-add" + ]); + } else { + // do posting stuff + $token = $request->get("token"); + $entityManager = $doctrine->getManager(); + $notice = ''; + + if (!$this->isCsrfTokenValid("upload", $token)) { + $logger->info("CSRF failure"); + return new Response( + "Operation not allowed", + Response::HTTP_BAD_REQUEST, + [ + "content-type" => "text/plain", + ] + ); + } + //get file from post + $file = $request->files->get("myfile"); + //grab extension + if (!empty($file)) { + $extention = substr(strrchr($file->getClientOriginalName(), "."), 1); + } + + //check it out to make sure it's cool + if ( + empty($file) || + $extention != "csv" + ) { + if (empty($file)) { + $notice = 'You didn\'t select a file, boss'; + } elseif ($extention != "csv") { + $notice = "Only files of type .csv are accepted, slick. " . $extention; + } + + return $this->render("back/locations.twig", [ + "title" => "Bad Space | Locations | Add", + "notice" => $notice, + "mode" => "bulk-add" + ]); + } + //if it's cool, send it to be processed + $response = $locations->addMultipleLocations($file, $result["id"]); + if ($response["status"]) { + $notice = "New locations added! Take a break."; + return $this->render("back/locations.twig", [ + "title" => "Bad Space | Locations | Add", + "notice" => $response["message"], + "mode" => "bulk-add" + ]); + } else { + return $this->render("back/locations.twig", [ + "title" => "Bad Space | Locations | Add", + "notice" => $response["message"], + "mode" => "bulk-add" + ]); + } + } + } else { + header("Location:/den"); + return new Response("LOGGED IN"); + } + } + /** * @Route("/den/locations/edit/{uuid}", name="location-edit") */ diff --git a/src/Service/HandleLocations.php b/src/Service/HandleLocations.php index ccd1faa..d9f10d7 100644 --- a/src/Service/HandleLocations.php +++ b/src/Service/HandleLocations.php @@ -12,6 +12,7 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Uid\Uuid; use App\Entity\Location; +use League\Csv\Reader; //use App\Utils\StringTools; @@ -139,4 +140,97 @@ class HandleLocations return $response = ["status" => false, "message" => $errorMessage]; } } + + /** + * Add new location to db + * + * @param Request $file object containing posted data + * @return Object + */ + public function addMultipleLocations($file, $memberId) + { + //read csv + $csv = Reader::createFromPath($file, "r"); + $csv->setHeaderOffset(0); + $records = $csv->getRecords(); + $recordCount = count($csv); + $duplicates = 0; + $errorMessage = null; + // Save image + + //extract data row by row + foreach ($records as $offset => $row) { + $name = $row["Name"]; + $url = $row["Url"]; + $images = $row["Images"]; + $desc = $row["Description"]; + $tags = $row["Tags"]; + $ratings = $row["Rating"]; + $imgs = explode(',', $images); + $examples = []; + + //check to see if location already exists + $list = $this->entityManager->getRepository(Location::class); + $entry = $list->findOneBy(["name" => $name]); + if ($entry) { + ++$duplicates; + } else { + $errorMessage = null; + $location = new Location(); + + $location->setName($name); + $location->setUrl($url); + $location->setDescription($desc); + $location->setTags($tags); + $location->setRating($ratings); + + //grab images, move them to dir and set image array + foreach ($imgs as $img) { + $imageName = uniqid() . ".jpg"; + $path = "../public/assets/images/examples/" . $imageName; + array_push($examples, $imageName); + file_put_contents($path, file_get_contents(trim($img))); + } + $location->setImages($examples); + + //set defaults + $location->setUuid(Uuid::v4()); + $location->setActive(false); + $location->setCreatedAt(new \DateTimeImmutable()); + $location->setUpdatedAt(new \DateTimeImmutable()); + $location->setAddedBy($memberId); + + $this->entityManager->persist($location); + } + } + + try { + $this->entityManager->flush(); + } catch (PDOException $error) { + $errorMessage = $error->getMessage(); + } catch (DBALException $error) { + $errorMessage = $error->getMessage(); + } catch (ORMException $error) { + $errorMessage = $error->getMessage(); + } catch (Exception $error) { + $errorMessage = $error->getMessage(); + } catch (SyntaxErrorException $e) { + $errorMessage = $error->getMessage(); + } + + if ($duplicates > 0) { + $message = $duplicates . " of " . $recordCount . " location entries were duplicates"; + } else { + $message = "Locations Added. Nice Job!"; + } + // return result status + if ($errorMessage == null) { + return $response = [ + "status" => true, + "message" => $message, + ]; + } else { + return $response = ["status" => false, "message" => $errorMessage]; + } + } } diff --git a/templates/back/locations.twig b/templates/back/locations.twig index dbbeb5c..c6997c2 100644 --- a/templates/back/locations.twig +++ b/templates/back/locations.twig @@ -17,18 +17,27 @@ {% if mode == "add" %}

Add New Location

{{ include("forms/add-location.twig") }} + {% elseif mode =='bulk-add' %} +

Add Multiple Locations

+ {{ include("forms/bulk-add-location.twig") }} {% elseif mode == "edit" %}

Editing {{ location.name }}

{{ include("forms/edit-location.twig") }} {% else %}

Take care. These are bad places.

- {% for location in list.locations %} - + Add Location + | + Add Multiple Locations +
+

Bad Spaces

+ {% for location in list.locations %} ID:{{ location.id }} - {{ location.name }}
- {% endfor %} - {% endif %} + - - {% endblock %} + {{ location.name }}
+ {% endfor %} + {% endif %} + + + {% endblock %} diff --git a/templates/forms/bulk-add-location.twig b/templates/forms/bulk-add-location.twig new file mode 100644 index 0000000..a58e426 --- /dev/null +++ b/templates/forms/bulk-add-location.twig @@ -0,0 +1,8 @@ +
+ +
+ +
+ + +
diff --git a/templates/forms/edit-location.twig b/templates/forms/edit-location.twig index 9b74a86..3771bd4 100644 --- a/templates/forms/edit-location.twig +++ b/templates/forms/edit-location.twig @@ -21,7 +21,16 @@ {% endif %} - +
+
+