Cleaned up location controller, responsive fix

The Location Controller was getting too heavy so an Update and
Maintenance service class was created to offload most of it's
functionality. Location upating was moved to LocationRepository

There was also a small issue with responsive list links not adapting
properly in Safari that was fixed
This commit is contained in:
ro 2024-02-15 15:00:03 -06:00
parent 03fbd00db1
commit 573054e7d8
6 changed files with 302 additions and 249 deletions

View file

@ -2,262 +2,27 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request; use App\Services\UpdateService;
use App\Models\Location;
use Ramsey\Uuid\Uuid;
use Illuminate\Support\Facades\Auth;
use App\Models\Source;
class LocationController extends Controller class LocationController extends Controller
{ {
public function addLocation(Request $request) protected $update;
{
$fields = $request->validate([
'name' => ['required'],
'url' => ['required'],
'description' => ['required'],
'tags' => ['required'],
]);
if ($fields) { public function __construct(UpdateService $updateService)
$examples = []; {
$files = $request->files->get("loc_examples"); $this->update = $updateService;
if ($request->hasfile('loc_examples')) {
foreach ($request->file('loc_examples') as $file) {
$path = $file->store('reference');
array_push($examples, ["path" => $path]);
}
}
$request->merge(['active' => true]);
$request->merge(['uuid' => Uuid::uuid4()]);
$request->merge(['images' => json_encode($examples)]);
$request->merge(['added_by' => Auth::user()->id]);
//NOTE: Laravel gets funky if sequencing isn't explicitly set
$new = Location::create($request->all());
if ($new) {
return back()->with('message', 'New Location Added. Take a break!');
} else {
return back()->withErrors([
'error' => 'Uh oh. There was an inssue',
]);
}
} else {
return back()->withErrors([
'error' => 'All fields are required',
]);
}
} }
public function updateLocations() public function updateLocations()
{ {
$duplicates = 0; $result = $this->update->locations();
$fresh = 0;
$missing = [];
$unified = []; return back()->with(
$cleanSources = []; 'message',
$sources = Source::where("active", true)->get(); $result['duplicates'] .
' UPDATED - ' . $result['fresh'] .
//checks source url to make sure they valid ' CREATED - ' . count($result['missing']) .
foreach ($sources as $source) { ' SOURCE(S) NOT CHECKED'
if ($source->type == 'mastodon') { );
$url = 'https://' . $source->url;
} else {
$url = $source->url;
}
if ($this->urlExists($url)) {
array_push($cleanSources, [
'url' => $source->url,
'token' => $source->token,
'type' => $source->type,
'format' => $source->format]);
} else {
var_dump($url);
array_push($missing, ['source' => $url]);
}
}
//valid source url get compiled for unified
foreach ($cleanSources as $source) {
//check url to make sure it's cool
//parsing for mastodon
if ($source['type'] == 'mastodon') {
$result = [];
if ($source['token'] == null) {
$result = \Mastodon::domain('https://' . $source['url'])
->get('/instance/domain_blocks');
} else {
$result = \Mastodon::domain('https://' . $source['url'])
->token($source['token'])
->get('/instance/domain_blocks');
}
foreach ($result as $item) {
$index = array_search($item['domain'], array_column($unified, 'url'));
if ($index) {
//if there is a match, update the count
if ($item['severity'] == "suspend" || $item['severity'] == "defederate") {
++$unified[$index]['block_count'];
} else {
++$unified[$index]['silence_count'];
}
} else {
$silence = 0;
$suspend = 0;
if ($item['severity'] == "suspend" || $item['severity'] == "defederate") {
++$suspend;
} else {
++$silence;
}
array_push($unified, [
'name' => $item['domain'],
'url' => $item['domain'],
'rating' => $item['severity'],
'comment' => $item['comment'],
'block_count' => $suspend,
'silence_count' => $silence,
]);
}
}
}
//parsing for custom csv
if ($source['type'] == 'custom' && $source['format'] == 'csv') {
$denylist = array_map('str_getcsv', file($source['url']));
foreach ($denylist as $item) {
$index = array_search($item[0], array_column($unified, 'url'));
if ($index) {
//if there is a match, update the count
if ($item[1] == "suspend" || $item['severity'] == "defederate") {
++$unified[$index]['block_count'];
} else {
++$unified[$index]['silence_count'];
}
} else {
$silence = 0;
$suspend = 0;
if ($item[1] == "suspend" || $item[1] == "defederate") {
++$suspend;
} else {
++$silence;
}
array_push($unified, [
'name' => $item[0],
'url' => $item[0],
'rating' => $item[1],
'comment' => $item[2],
'block_count' => $suspend,
'silence_count' => $silence,
]);
}
}
}
}
//TODO: maintenance script to set locations to inactive if they haven't been updated
// over 90 days
//$diff=date_diff($location->updated_at, new DateTime());
//$days = $diff->format("%R%a days")
//$interval = $location->updated_at->diff(new DateTime());
//$days = $interval->format("%a");
//get all locations and sort which are present in unified or not
/*
$sorted = [];
$listed = 0;
$notlisted = 0;
foreach (Location::all() as $location) {
if (array_search($location->url, array_column($unified, 'url'))) {
++$listed;
// locations present in unfied, so updated
array_push($sorted, [
'location' => $location,
'listed' => true
]);
} else {
++$notlisted;
//locations not present
array_push($sorted, [
'location' => $location,
'listed' => false
]);
}
};
*/
//once the unified list is created, update current entries or create fresh ones
foreach ($unified as $item) {
$location = Location::where("url", $item['url'])->first();
if ($location) {
++$duplicates;
//update block count for existing item
$location->block_count = $item['block_count'];
$location->silence_count = $item['silence_count'];
$location->actions_count = $item['block_count'] + $item['silence_count'];
if (($item['block_count'] + $item['silence_count']) < 2) {
$location->active = false;
}
//replace null with empty array
if ($location->images == null) {
$location->images = [];
};
$location->save();
} else {
// make new entries for instances not present
++$fresh;
$images = [];
$rating = ($item['rating'] == 'defederate') ? 'suspend' : $item['rating'];
$status = true;
if (($item['block_count'] + $item['silence_count']) < 2) {
$status = false;
}
$new = Location::create([
'uuid' => Uuid::uuid4(),
'name' => $item['url'],
'url' => $item['url'],
'description' => ($item['comment'] != null) ? $item['comment'] : "no description",
'active' => $status,
'rating' => $rating,
'added_by' => 1,
'tags' => 'poor moderation, hate speech',
'images' => json_encode($images),
'block_count' => $item['block_count'],
'silence_count' => $item['silence_count'],
'actions_cont' => $item['block_count'] + $item['silence_count']
]);
}
}
//TODO: Send update post to TBS social account
return back()->with('message', $duplicates . ' UPDATED - ' . $fresh . ' CREATED - ' . count($missing) . ' SOURCE(S) NOT CHECKED');
}
public function urlExists($url)
{
// Remove all illegal characters from a url
$url = filter_var($url, FILTER_SANITIZE_URL);
// Validate URI
if (
filter_var($url, FILTER_VALIDATE_URL) === false || // check only for http/https schemes.
!in_array(
strtolower(parse_url($url, PHP_URL_SCHEME)),
["http", "https"],
true
)
) {
return false;
} // Check that URL exists
$file_headers = @get_headers($url);
return !(!$file_headers || $file_headers[0] === "HTTP/1.1 404 Not Found");
} }
} }

View file

@ -4,6 +4,8 @@ namespace App\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use App\Repositories\LocationRepository; use App\Repositories\LocationRepository;
use App\Services\UpdateService;
use App\Services\MaintenanceService;
use App\Models\Location; use App\Models\Location;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
@ -16,6 +18,14 @@ class AppServiceProvider extends ServiceProvider
$this->app->bind(LocationRepository::class, function ($app) { $this->app->bind(LocationRepository::class, function ($app) {
return new LocationRepository(new Location()); return new LocationRepository(new Location());
}); });
$this->app->bind(UpdateService::class, function ($app) {
return new UpdateService(new Location());
});
$this->app->bind(MaintenanceService::class, function ($app) {
return new MaintenanceService(new Location());
});
} }
/** /**

View file

@ -4,6 +4,7 @@ namespace App\Repositories;
use App\Models\Location; use App\Models\Location;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class LocationRepository class LocationRepository
{ {
@ -70,4 +71,42 @@ class LocationRepository
return $result = [$locations, $pageCount, $prev, $next]; return $result = [$locations, $pageCount, $prev, $next];
} }
public function addLocation(Request $request)
{
$fields = $request->validate([
'name' => ['required'],
'url' => ['required'],
'description' => ['required'],
'tags' => ['required'],
]);
if ($fields) {
$examples = [];
$files = $request->files->get("loc_examples");
if ($request->hasfile('loc_examples')) {
foreach ($request->file('loc_examples') as $file) {
$path = $file->store('reference');
array_push($examples, ["path" => $path]);
}
}
$request->merge(['active' => true]);
$request->merge(['uuid' => Uuid::uuid4()]);
$request->merge(['images' => json_encode($examples)]);
$request->merge(['added_by' => Auth::user()->id]);
//NOTE: Laravel gets funky if sequencing isn't explicitly set
$new = Location::create($request->all());
if ($new) {
return back()->with('message', 'New Location Added. Take a break!');
} else {
return back()->withErrors([
'error' => 'Uh oh. There was an inssue',
]);
}
} else {
return back()->withErrors([
'error' => 'All fields are required',
]);
}
}
} }

View file

@ -0,0 +1,41 @@
<?php
namespace App\Services;
use App\Models\Location;
class MaintenanceService
{
//TODO: maintenance script to set locations to inactive if they haven't been updated
// over 90 days
//$diff=date_diff($location->updated_at, new DateTime());
//$days = $diff->format("%R%a days")
//$interval = $location->updated_at->diff(new DateTime());
//$days = $interval->format("%a");
//get all locations and sort which are present in unified or not
/*
$sorted = [];
$listed = 0;
$notlisted = 0;
foreach (Location::all() as $location) {
if (array_search($location->url, array_column($unified, 'url'))) {
++$listed;
// locations present in unfied, so updated
array_push($sorted, [
'location' => $location,
'listed' => true
]);
} else {
++$notlisted;
//locations not present
array_push($sorted, [
'location' => $location,
'listed' => false
]);
}
};
*/
}

View file

@ -0,0 +1,198 @@
<?php
namespace App\Services;
use App\Models\Location;
use App\Models\Source;
use Ramsey\Uuid\Uuid;
class UpdateService
{
protected $model;
private $limit = 15;
public function __construct(Location $model)
{
$this->model = $model;
}
public function locations()
{
$duplicates = 0;
$fresh = 0;
$missing = [];
$unified = [];
$cleanSources = [];
$sources = Source::where("active", true)->get();
//checks source url to make sure they valid
foreach ($sources as $source) {
if ($source->type == 'mastodon') {
$url = 'https://' . $source->url;
} else {
$url = $source->url;
}
if ($this->urlExists($url)) {
array_push($cleanSources, [
'url' => $source->url,
'token' => $source->token,
'type' => $source->type,
'format' => $source->format]);
} else {
var_dump($url);
array_push($missing, ['source' => $url]);
}
}
//valid source url get compiled for unified
foreach ($cleanSources as $source) {
//check url to make sure it's cool
//parsing for mastodon
if ($source['type'] == 'mastodon') {
$result = [];
if ($source['token'] == null) {
$result = \Mastodon::domain('https://' . $source['url'])
->get('/instance/domain_blocks');
} else {
$result = \Mastodon::domain('https://' . $source['url'])
->token($source['token'])
->get('/instance/domain_blocks');
}
foreach ($result as $item) {
$index = array_search($item['domain'], array_column($unified, 'url'));
if ($index) {
//if there is a match, update the count
if ($item['severity'] == "suspend" || $item['severity'] == "defederate") {
++$unified[$index]['block_count'];
} else {
++$unified[$index]['silence_count'];
}
} else {
$silence = 0;
$suspend = 0;
if ($item['severity'] == "suspend" || $item['severity'] == "defederate") {
++$suspend;
} else {
++$silence;
}
array_push($unified, [
'name' => $item['domain'],
'url' => $item['domain'],
'rating' => $item['severity'],
'comment' => $item['comment'],
'block_count' => $suspend,
'silence_count' => $silence,
]);
}
}
}
//parsing for custom csv
if ($source['type'] == 'custom' && $source['format'] == 'csv') {
$denylist = array_map('str_getcsv', file($source['url']));
foreach ($denylist as $item) {
$index = array_search($item[0], array_column($unified, 'url'));
if ($index) {
//if there is a match, update the count
if ($item[1] == "suspend" || $item['severity'] == "defederate") {
++$unified[$index]['block_count'];
} else {
++$unified[$index]['silence_count'];
}
} else {
$silence = 0;
$suspend = 0;
if ($item[1] == "suspend" || $item[1] == "defederate") {
++$suspend;
} else {
++$silence;
}
array_push($unified, [
'name' => $item[0],
'url' => $item[0],
'rating' => $item[1],
'comment' => $item[2],
'block_count' => $suspend,
'silence_count' => $silence,
]);
}
}
}
}
//once the unified list is created, update current entries or create fresh ones
foreach ($unified as $item) {
$location = Location::where("url", $item['url'])->first();
if ($location) {
++$duplicates;
//update block count for existing item
$location->block_count = $item['block_count'];
$location->silence_count = $item['silence_count'];
$location->actions_count = $item['block_count'] + $item['silence_count'];
if (($item['block_count'] + $item['silence_count']) < 2) {
$location->active = false;
}
//replace null with empty array
if ($location->images == null) {
$location->images = [];
};
$location->save();
} else {
// make new entries for instances not present
++$fresh;
$images = [];
$rating = ($item['rating'] == 'defederate') ? 'suspend' : $item['rating'];
$status = true;
if (($item['block_count'] + $item['silence_count']) < 2) {
$status = false;
}
$new = Location::create([
'uuid' => Uuid::uuid4(),
'name' => $item['url'],
'url' => $item['url'],
'description' => ($item['comment'] != null) ? $item['comment'] : "no description",
'active' => $status,
'rating' => $rating,
'added_by' => 1,
'tags' => 'poor moderation, hate speech',
'images' => json_encode($images),
'block_count' => $item['block_count'],
'silence_count' => $item['silence_count'],
'actions_cont' => $item['block_count'] + $item['silence_count']
]);
}
}
//TODO: Send update post to TBS social account
return ['duplicates' => $duplicates, 'fresh' => $fresh, 'missing' => $missing];
}
public function urlExists($url)
{
// Remove all illegal characters from a url
$url = filter_var($url, FILTER_SANITIZE_URL);
// Validate URI
if (
filter_var($url, FILTER_VALIDATE_URL) === false || // check only for http/https schemes.
!in_array(
strtolower(parse_url($url, PHP_URL_SCHEME)),
["http", "https"],
true
)
) {
return false;
} // Check that URL exists
$file_headers = @get_headers($url);
return !(!$file_headers || $file_headers[0] === "HTTP/1.1 404 Not Found");
}
}

View file

@ -128,7 +128,7 @@ a.list-link > .item-block > .item-icon {
a.list-link { a.list-link {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 100% auto 30px 30px; grid-template-rows: auto auto 30px 30px;
gap: 5px; gap: 5px;
height: auto; height: auto;
padding-bottom: 20px; padding-bottom: 20px;