Added support of fedifence CSV and pagination

Populated the DB with entries from a fedifence export provided by
@Oliphant (https://codeberg.org/oliphant/blocklists).

As such also updated the appropriate templates with pagination to be
able to peruse through the location directory.

Also added an edit link on the location template front end to make
finding and updating instance info easy.
This commit is contained in:
Ro 2023-01-23 19:59:51 -08:00
parent b9298451b7
commit 245531faf6
10 changed files with 234 additions and 21 deletions

2
.gitignore vendored
View file

@ -1,4 +1,4 @@
/files
###> symfony/framework-bundle ### ###> symfony/framework-bundle ###
.env.local .env.local
/.env.local.php /.env.local.php

View file

@ -9,8 +9,19 @@ section[role="listings"] {
section[role="listings"] a { section[role="listings"] a {
color: var(--highlight); color: var(--highlight);
font-size: 2.5em; font-size: 2em;
font-weight: bold; font-weight: bold;
border: 0; border: 0;
display: block; display: block;
} }
section[role="listings"] a label {
color: var(--secondary);
font-size: 0.3em;
text-decoration: underline;
font-family: var(--mono-type);
}
section[role="listings"] a:hover {
color: var(--white);
}

View file

@ -12,6 +12,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
use App\Service\HandleLocations; use App\Service\HandleLocations;
use App\Service\HandleImports;
use App\Service\Auth; use App\Service\Auth;
use App\Service\FileUploader; use App\Service\FileUploader;
use App\Service\Render; use App\Service\Render;
@ -45,8 +46,27 @@ class Locations extends AbstractController
$member = $session->get("member"); $member = $session->get("member");
$list = $locations->getLocationsPage($pageNum); $list = $locations->getLocationsPage($pageNum);
//$search = $connection->fetchAllAssociative("SELECT * FROM searchlocations('agenda')"); $next = $pageNum + 1;
return $render->page(["list" => $list, "mode" => "index"], "Bad Space | Locations", "back/locations.twig"); if ($next > $list["total"]) {
$next = 1;
}
$prev = $pageNum - 1;
if ($prev <= 0) {
$prev = $list["total"];
}
return $render->page(
[
"list" => $list,
"mode" => "index",
"curentPage" => $pageNum,
"nextPage" => $next,
"prevPage" => $prev
],
"Bad Space | Locations",
"back/locations.twig"
);
} else { } else {
return $render->page([], "The Bad Space | Den", "back/index.twig"); return $render->page([], "The Bad Space | Den", "back/index.twig");
} }
@ -164,6 +184,7 @@ class Locations extends AbstractController
Request $request, Request $request,
Auth $auth, Auth $auth,
HandleLocations $locations, HandleLocations $locations,
HandleImports $imports,
ManagerRegistry $doctrine, ManagerRegistry $doctrine,
FileUploader $uploader, FileUploader $uploader,
Render $render Render $render
@ -181,7 +202,7 @@ class Locations extends AbstractController
$token = $request->get("token"); $token = $request->get("token");
$entityManager = $doctrine->getManager(); $entityManager = $doctrine->getManager();
$notice = ''; $notice = '';
$type = $request->get("input_type");
if (!$this->isCsrfTokenValid("upload", $token)) { if (!$this->isCsrfTokenValid("upload", $token)) {
$logger->info("CSRF failure"); $logger->info("CSRF failure");
return new Response( return new Response(
@ -217,7 +238,12 @@ class Locations extends AbstractController
]); ]);
} }
//if it's cool, send it to be processed //if it's cool, send it to be processed
if ($type == "tbs" || $type == "") {
$response = $locations->addMultipleLocations($file, $result["id"]); $response = $locations->addMultipleLocations($file, $result["id"]);
} else {
$response = $imports->importLocations($file, $result["id"]);
}
if ($response["status"]) { if ($response["status"]) {
$notice = "New locations added! Take a break."; $notice = "New locations added! Take a break.";
return $render->page( return $render->page(

View file

@ -73,9 +73,26 @@ class Index extends AbstractController
Auth $auth, Auth $auth,
Render $render, Render $render,
HandleLocations $locations, HandleLocations $locations,
string $pageNum int $pageNum = 1
): Response { ): Response {
$list = $locations->getLocationsPage($pageNum, "true"); $list = $locations->getLocationsPage($pageNum, "true");
return $render->page(["list" => $list, "page" => $pageNum], "About The Bad Space", "front/listing.twig");
$next = $pageNum + 1;
if ($next > $list["total"]) {
$next = 1;
}
$prev = $pageNum - 1;
if ($prev <= 0) {
$prev = $list["total"];
}
return $render->page([
"list" => $list,
"currentPage" => $pageNum,
"nextPage" => $next,
"prevPage" => $prev
], "About The Bad Space", "front/listing.twig");
} }
} }

View file

@ -0,0 +1,134 @@
<?php
// src/Controller/ProductController.php
namespace App\Service;
use Doctrine\DBAL\DBALException;
use Doctrine\ORM\ORMException;
use PDOException;
use Exception;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Uid\Uuid;
use Doctrine\DBAL\Connection;
use App\Entity\Location;
use League\Csv\Reader;
//use App\Utils\StringTools;
/**
* Members
*
* Data class for importing external CVS blocklists
*/
class HandleImports
{
private $session;
private $entityManager;
private $conn;
private $limit = 4;
public function __construct(
Connection $connection,
EntityManagerInterface $entityManager,
RequestStack $requestStack
) {
$this->connection = $connection;
$this->entityManager = $entityManager;
$this->session = $requestStack->getSession();
}
/**
* Add new Locations to db from fedifence file
* provided by Oliphant
*
* @param string $path location of file
* @param int $memberID member adding locations
* @return Object
*/
public function importLocations($file, $memberId)
{
//read csv
$csv = Reader::createFromPath($file, "r");
$csv->setHeaderOffset(0);
$records = $csv->getRecords();
$recordCount = count($csv);
$duplicates = 0;
$errorMessage = null;
//extract data row by row
//TODO: set name to lowercase for comparison
foreach ($records as $offset => $row) {
$url = $row["domain"];
//$images = $row["Images"];
$desc = $row["public_comment"];
$ratings = $row["severity"];
//check to see if location already exists
$list = $this->entityManager->getRepository(Location::class);
$entry = $list->findOneBy(["url" => trim($url)]);
if ($entry) {
++$duplicates;
} else {
$errorMessage = null;
$location = new Location();
$location->setName($url);
$location->setUrl($url);
$location->setTags("bigotry, hate speech, poor moderation");
if ($ratings == "suspend") {
$location->setRating("defederate");
} else {
$location->setRating("silence");
}
//set defaults
$location->setUuid(Uuid::v4());
if ($desc == "") {
$location->setDescription("needs description");
$location->setActive(false);
} else {
$location->setActive(true);
$location->setDescription($desc);
}
$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];
}
}
}

View file

@ -27,7 +27,7 @@ class HandleLocations
private $session; private $session;
private $entityManager; private $entityManager;
private $conn; private $conn;
private $limit = 4; private $limit = 9;
public function __construct( public function __construct(
Connection $connection, Connection $connection,

View file

@ -30,13 +30,20 @@
<a href="/den/locations/bulk-add">Add Multiple Locations</a> <a href="/den/locations/bulk-add">Add Multiple Locations</a>
<br> <br>
<h3>Bad Spaces</h3> <h3>Bad Spaces</h3>
Page
{{ options.curentPage }}
of
{{ options.list.total }}<br>
{% for location in options.list.locations %} {% for location in options.list.locations %}
<sup>ID:{{ location.id }}</sup> <sup>ID:{{ location.id }}</sup>
<a href="/den/locations/modify/edit/{{ location.uuid }}"> <a href="/den/locations/modify/edit/{{ location.uuid }}">
{{ location.name }}</a><br/> {{ location.name }}</a><br/>
{% endfor %} {% endfor %}
<a href="/den/locations/page/{{ options.prevPage }}">Previous</a>
<a href="/den/locations/page/{{ options.nextPage }}">Next</a>
{% endif %} {% endif %}
</section> </section>
{% endblock %} {% endblock %}

View file

@ -3,6 +3,14 @@
<br> <br>
<input type="file" name="myfile" id="myfile"></div> <input type="file" name="myfile" id="myfile"></div>
<br/> <br/>
<label>Input Type</label><br/>
<select name="input_type">
<option value="" disabled selected>Choose Upload Type
</option>
<option value="tbs">The Bad Space Custom</option>
<option value="fedifence">Fedifence</option>
</select>
<br/>
<input type="hidden" name="token" value="{{ csrf_token('upload') }}"/> <input type="hidden" name="token" value="{{ csrf_token('upload') }}"/>
<button type="submit">Upload Locations</button> <button type="submit">Upload Locations</button>
</form> </form>

View file

@ -7,12 +7,17 @@
<section role="listings"> <section role="listings">
<h1>The Bad Space Listings</h1> <h1>The Bad Space Listings</h1>
<h2>Page <h2>Page
{{ options.page }}</h2> {{ options.currentPage }}
of
{{ options.list.total }}</h2>
{% for location in options.list.locations %} {% for location in options.list.locations %}
<sup>ID:{{ location.id }}</sup>
<a href="/location/{{ location.uuid }}">
{{ location.name }}</a><br/> <a href="/location/{{ location.uuid }}">
<label>{{ location.id }}</label>
{{ location.name }}</a>
<br/>
{% endfor %} {% endfor %}
<a href="/listings/page/{{ options.prevPage }}">Previous</a>
<a href="/listings/page/{{ options.nextPage }}">Next</a>
</section> </section>
{% endblock %} {% endblock %}

View file

@ -17,5 +17,10 @@
<br/> <br/>
<strong>TAGS:</strong> <strong>TAGS:</strong>
{{ options.location.tags }} {{ options.location.tags }}
<br/>
{% if loggedIn %}
<a href="/den/locations/modify/edit/{{ options.location.uuid }}">EDIT
{{ options.location.name }}</a>
{% endif %}
</section> </section>
{% endblock %} {% endblock %}