Compare commits

...

13 commits

Author SHA1 Message Date
ro
8ce9a7744a composer json tweak
composer json wasn't validating, so that needed to be fixed
2024-10-08 11:37:51 -06:00
ro
f7bd675b5d updated composer file; update versioning
a quick update to the composer file to have proper versioning and some
details about the project,

also updated the template to reflect the current version
2024-10-08 11:15:25 -06:00
ro
ccd0a7a3a9 added notes to locations, exports comments fix
locations needed another data point for notes to additional information
about instances that aren't covered by public comments, i.e. if the site
is still live, additional concerns, etc, so that's been added and the
appropriate template files added

also, public comments where not being included in the CSV exports, so that's
been patched as well
2024-10-04 14:04:49 -06:00
ro
80191a2e46 commented out old decription reference in loc repo
there was a reference to storing descriptions in the database that no
longer has a corresponding table since it was changed

just commenting it out for now because it will be implemented again at a
later date
2024-10-01 11:39:55 -06:00
ro
8353d154c4 DB edit: description to public_comments
the update process now compiles public comments from instance blocklists
and displays them, so the name of the table that stores these comments
has been changed so it's not confusing.

also made the appropriate changes on the backend and in the template
that shows that information
2024-09-29 16:34:52 -06:00
ro
098cd72186 form validation for member editing
added some form validation in the member controller to make sure all
necessary info is provided before editing/creating member
2024-09-29 16:11:50 -06:00
ro
31f45c4af5 add role checks for admin function
admin functions are not shown to member with incorrect roles, but added
a bit more padding in the controller itself to check if the role is
correct before running an admin action for a little extra security
2024-09-29 15:55:55 -06:00
ro
3c0762344e added member create ui
now that editing member work, that process needed to be fleshed out by
adding an admin method to add new members as well. now new members can
be added by an admin

also changed the name of a blade file that wasn't following the template
naming convention
2024-09-27 12:39:56 -06:00
ro
d0c8def297 added member edit admin ui
needed way to edit existing members info if needed, so simple ui was
created for admins of the site to change specific info, not including a
members avi or password.

also consolidated memeber action and ui into it's own controller for the
sake of clearer organization
2024-09-24 16:21:59 -06:00
ro
99e22f5697 added member profile editing
added a basic ui so logged in members can change their info/password
when needed
2024-09-20 15:09:45 -06:00
ro
2932af0d3f show multiple instance comments
the previous update cycle only showed the latest comment from Sources
concerning a specific instance

now all available comments are pulled from denylist data to be displayed
under Description on the front end
2024-09-17 15:41:31 -06:00
ro
0eeab6355e updated image uploading, edited html templates
changed the the way files are uploaded to go into their own directory
called 'references' organized by location, identified by uuid. the
'references' directory was added to git ignore to those images are not
saved in the repo, since every install will have their own set of images

also updated reference links to be shown on the front end if they have
been added to a location

unnecessary links where moved from the admin member template since they
have been incorporated into the appropriate area.

a template for editing member account information has also been added.
2024-09-13 15:12:43 -06:00
ro
43e0004ac5 Updated blocklist retrival process, template edits
The process for updating blocklists per source wasn't effecient, so it
has been rewritten to process each source individually before moving on,
relieving some processing load on the server and ensuring every source
comes back with data, even in the event it doesn't grab it the first
time.

also removed the recent list from the index page as the recently updated
list doesn't reflect what's been the last to get updated and changed the
theme color to match the current palette.
2024-09-12 19:20:13 -06:00
28 changed files with 691 additions and 147 deletions

2
.gitignore vendored
View file

@ -4,6 +4,8 @@
/public/hot /public/hot
/public/storage /public/storage
/public/reference /public/reference
/public/assets/images/references
/public/assets/images/members
/storage/*.key /storage/*.key
/vendor /vendor
.env .env

View file

@ -34,14 +34,6 @@ class DenController extends Controller
]); ]);
} }
public function member(Request $request)
{
$member = Auth::user();
return view('back.member', [
'handle' => $member->handle,
'title' => "Manage Members"]);
}
public function locations(Request $request) public function locations(Request $request)
{ {
$member = Auth::user(); $member = Auth::user();

View file

@ -60,7 +60,7 @@ class ExportController extends Controller
if ($rate * 100 >= $percent) { if ($rate * 100 >= $percent) {
if ($type == 'mastodon') { if ($type == 'mastodon') {
//comman break teh CSV so just take them out //comman break teh CSV so just take them out
$comments = str_replace(",", ";", $location->description); $comments = str_replace(",", ";", $location->public_comments);
//remove extra white space //remove extra white space
$comments = str_replace(["\n\r", "\n", "\r"], " ", $comments); $comments = str_replace(["\n\r", "\n", "\r"], " ", $comments);

View file

@ -76,15 +76,19 @@ class FrontIndexController extends Controller
} }
if (isset($member->role)) { if (isset($member->role)) {
($member->role == 1 || $member->role == 2) ? $edit = true : $edit = false; ($member->role == 0 || $member->role == 1) ? $edit = true : $edit = false;
} }
$links = explode(',', $location->archive_links);
$comments = explode('+', $location->public_comments);
return view('front.location', [ return view('front.location', [
'title' => str_replace(".", " ", $name), 'title' => str_replace(".", " ", $name),
'location' => $location, 'location' => $location,
'comments' => $comments,
'actions' => $location->block_count + $location->silence_count, 'actions' => $location->block_count + $location->silence_count,
'sources_count' => count($sources), 'sources_count' => count($sources),
'images' => json_decode($location->images), 'images' => json_decode($location->images),
'links' => $links,
'updated' => $location->updated_at->format('Y M d'), 'updated' => $location->updated_at->format('Y M d'),
'edit' => $edit 'edit' => $edit
]); ]);

View file

@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Services\UpdateService; use App\Services\UpdateService;
use App\Repositories\LocationRepository; use App\Repositories\LocationRepository;
use Illuminate\Support\Facades\Auth;
class LocationController extends Controller class LocationController extends Controller
{ {
@ -18,34 +19,51 @@ class LocationController extends Controller
$this->location = $locationRepository; $this->location = $locationRepository;
} }
//actions
public function updateLocations() public function updateLocations()
{ {
$result = $this->update->data(); //role check
$member = Auth::user();
return back()->with( if ($member->role == 0) {
'message', $result = $this->update->data();
$result return back()->with(
); 'message',
$result
);
} else {
return back()->withErrors('message', 'Nah, you don\'t have permission to do this');
}
} }
public function compileLocations() public function compileLocations()
{ {
$result = $this->update->list(); //role check
$member = Auth::user();
return back()->with( if ($member->role == 0) {
'message', $result = $this->update->list();
$result return back()->with(
); 'message',
$result
);
} else {
return back()->withErrors('message', 'Nah, you don\'t have permission to do this');
}
} }
public function editLocation(Request $request) public function editLocation(Request $request)
{ {
$token = csrf_token(); $token = csrf_token();
$response = $this->location->editLocation($request); //role check
if ($response['status']) { $member = Auth::user();
return back()->with('message', $response['message']); if ($member->role == 0 || $member->role == 1) {
$response = $this->location->editLocation($request);
if ($response['status']) {
return back()->with('message', $response['message']);
} else {
return back()->withErrors('message', $response['message']);
}
} else { } else {
return back()->withErrors('message', $response['message']); return back()->withErrors('message', 'Nah, you don\'t have permission to do this');
} }
} }
} }

View file

@ -0,0 +1,154 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Repositories\MemberRepository;
class MemberController extends Controller
{
protected $member;
public function __construct(
MemberRepository $memberRepo
) {
$this->member = $memberRepo;
}
public function index(Request $request)
{
$member = Auth::user();
return view('back.member', [
'handle' => $member->handle,
'members' => $this->member->getAll(),
'mode' => 'index',
'title' => "Manage Members"]);
}
public function profile(Request $request)
{
$member = Auth::user();
$avi = '';
if ($member->avatar == 'default-member-avatar') {
$avi = '/assets/images/global/default-avi.png';
} else {
$avi = $member->avatar;
}
return view('back.profile', [
'title' => "Hey, it's you!",
'handle' => $member->handle,
'email' => $member->email,
'avatar' => $avi,
'pronouns' => $member->pronoun,
'uuid' => $member->uuid,
'role' => $member->role
]);
}
public function editMember(Request $request, $uuid = 0)
{
$member = $this->member->get($uuid);
$avi = '';
if ($member->avatar == 'default-member-avatar') {
$avi = '/assets/images/global/default-avi.png';
} else {
$avi = $member->avatar;
}
return view('back.member', [
'member' => $member,
'avatar' => $avi,
'mode' => 'member-edit',
'title' => "Edit Member Info"]);
}
public function createMember(Request $Request)
{
return view('back.member', [
'mode' => 'member-create',
'title' => "Make a new friend"]);
}
//actions
public function profileEdit(Request $request)
{
$token = csrf_token();
//check if logged in member id matches profile request id
$member = Auth::user();
if ($member->uuid == $request->id) {
//validate required fields
$valid = $request->validate([
'handle' => ['required'],
'email' => ['required'],
]);
if ($valid) {
$response = $this->member->editProfile($request);
if ($response['status'] == true) {
return back()->with('message', $response['message']);
} else {
return back()->withErrors([$response['message']]);
}
} else {
return back()->withErrors(['Misssing some required info, homie.']);
}
} else {
return back()->withErrors(['This is not your profile to edit.']);
}
}
public function memberEdit(Request $request)
{
$token = csrf_token();
//role check
$member = Auth::user();
if ($member->role == 0) {
$valid = $request->validate([
'handle' => ['required'],
'email' => ['required'],
'role' => ['required']
]);
if ($valid) {
$response = $this->member->edit($request);
if ($response['status'] == true) {
return back()->with('message', $response['message']);
} else {
return back()->withErrors([$response['message']]);
}
} else {
return back()->withErrors(['Misssing some required info, homie.']);
}
} else {
return back()->withErrors(['Nah, you can\'t do this. Wrong permissions.']);
}
}
public function memberCreate(Request $request)
{
$token = csrf_token();
$member = Auth::user();
if ($member->role == 0) {
$valid = $request->validate([
'handle' => ['required'],
'email' => ['required'],
'role' => ['required'],
'pronouns' => ['required'],
'fresh_pass' => ['required'],
'fresh_pass_confirm' => ['required'],
]);
if ($valid) {
$response = $this->member->add($request);
if ($response['status'] == true) {
return redirect('/den/member')->with('message', $response['message']);
} else {
return back()->withErrors([$response['message']]);
}
} else {
return back()->withErrors(['Misssing some required info, homie.']);
}
} else {
return back()->withErrors(['Nah, you can\'t do this. Wrong permissions.']);
}
}
}

View file

@ -24,7 +24,7 @@ class Location extends Model
"uuid", "uuid",
"name", "name",
"url", "url",
"description", "public_comments",
"images", "images",
"active", "active",
"rating", "rating",
@ -35,6 +35,7 @@ class Location extends Model
"created_at", "created_at",
"updated_at", "updated_at",
"actions_count", "actions_count",
"archive_links" "archive_links",
"notes",
]; ];
} }

View file

@ -9,6 +9,20 @@ class Member extends Authenticatable
{ {
use HasFactory; use HasFactory;
protected $table = "member"; public $timestamps = false;
protected $fillable = ["uuid", "handle", "email", "password", "active", "role", "avatar", "pronoun", "gender"]; protected $table = "member";
protected $primaryKey = 'id';
public $incrementing = true;
protected $fillable = [
"uuid",
"handle",
"email",
"password",
"active",
"role",
"avatar",
"pronoun",
"created_at",
"last_login"
];
} }

View file

@ -5,10 +5,12 @@ namespace App\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use App\Repositories\LocationRepository; use App\Repositories\LocationRepository;
use App\Repositories\SourceRepository; use App\Repositories\SourceRepository;
use App\Repositories\MemberRepository;
use App\Services\UpdateService; use App\Services\UpdateService;
use App\Services\MaintenanceService; use App\Services\MaintenanceService;
use App\Models\Location; use App\Models\Location;
use App\Models\Source; use App\Models\Source;
use App\Models\Member;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@ -25,6 +27,10 @@ class AppServiceProvider extends ServiceProvider
return new SourceRepository(new Source()); return new SourceRepository(new Source());
}); });
$this->app->bind(MemberRepository::class, function ($app) {
return new MemberRepository(new Member());
});
$this->app->bind(UpdateService::class, function ($app) { $this->app->bind(UpdateService::class, function ($app) {
return new UpdateService( return new UpdateService(
new LocationRepository(new Location()), new LocationRepository(new Location()),

View file

@ -56,24 +56,34 @@ class LocationRepository
public function editLocation($request) public function editLocation($request)
{ {
$location = $this->getLocation($request->id); $location = $this->getLocation($request->id);
$images = []; $publicPath = '../public/';
$refPath = 'assets/images/references/' . $location->uuid;
$images = [];
if ($request->hasfile("references")) { if ($request->hasfile("references")) {
foreach ($request->references as $file) { foreach ($request->references as $file) {
$path = $file->store('reference'); if (!is_dir($publicPath . $refPath)) {
array_push($images, ["path" => $path]); mkdir($publicPath . $refPath, 0755, true);
}
$filename = urlencode($file->getClientOriginalName());
$file->move($publicPath . $refPath, $filename);
//$path = $file->store('reference');
array_push($images, ["path" => '/' . $refPath . '/' . $filename]);
} }
} }
$request->merge(['images' => json_encode($images)]); if (!empty($images)) {
$request->merge(['images' => json_encode($images)]);
$location->images = json_encode($images);
}
$location->name = $request->name; $location->name = $request->name;
$location->description = $request->description; $location->notes = $request->notes;
$location->archive_links = $request->archive_links; $location->archive_links = $request->archive_links;
$location->images = json_encode($images);
$result = []; $result = [];
if ($location->save()) { if ($location->save()) {
return ['status' => true, 'message' => "Location Editited -IMG- " . $request->hasfile("references")]; return ['status' => true, 'message' => "Location Editited" . $request->hasfile("references")];
} else { } else {
return ['status' => false, 'message' => "Location Not Editited"]; return ['status' => false, 'message' => "Location Not Editited"];
} }

View file

@ -0,0 +1,127 @@
<?php
namespace App\Repositories;
use App\Models\Member;
use Illuminate\Support\Facades\Hash;
use Ramsey\Uuid\Uuid;
use Carbon\Carbon;
class MemberRepository
{
protected $model;
public function __construct(Member $model)
{
$this->model = $model;
}
public function getAll()
{
return $this->model::all();
}
public function get($uuid)
{
return $this->model::where("uuid", $uuid)->first();
}
public function edit($request)
{
//get member to edit
$member = $this->get($request->id);
//save new avi if available
$publicPath = '../public/';
$refPath = 'assets/images/members/' . $member->uuid;
if ($request->hasfile("fresh_avi")) {
$file = $request->fresh_avi;
if (!is_dir($publicPath . $refPath)) {
mkdir($publicPath . $refPath, 0755, true);
}
$filename = urlencode($file->getClientOriginalName());
$file->move($publicPath . $refPath, $filename);
$freshAvi = '/' . $refPath . '/' . $filename;
$member->avatar = $freshAvi;
}
$member->handle = $request->handle;
$member->email = $request->email;
$member->pronoun = $request->pronouns;
$member->role = $request->role;
$member->active = $request->status;
if ($member->save()) {
return ['status' => true, 'message' => "Member Editited"];
} else {
return ['status' => false, 'message' => "Member Not Editited"];
}
}
public function add($request)
{
$password = [];
if ($request->fresh_pass === $request->fresh_pass_confirm) {
$password = Hash::make($request->fresh_pass);
} else {
return ['status' => false, 'message' => "Passwords Do Not Match"];
}
$newFriend = $this->model::create([
'uuid' => Uuid::uuid4(),
'avatar' => 'default-member-avatar',
'handle' => $request->handle,
'email' => $request->email,
'pronoun' => $request->pronouns,
'role' => $request->role,
'active' => $request->status,
'password' => $password,
'created_at' => Carbon::now(),
'last_login' => Carbon::now(),
]);
if ($newFriend) {
return ['status' => true, 'message' => "New Friend Made!"];
} else {
return ['status' => false, 'message' => "Uh oh, New Friend Delay!"];
}
}
public function editProfile($request)
{
//get member to edit
$member = $this->get($request->id);
//save new avi if available
$publicPath = '../public/';
$refPath = 'assets/images/members/' . $member->uuid;
if ($request->hasfile("fresh_avi")) {
$file = $request->fresh_avi;
if (!is_dir($publicPath . $refPath)) {
mkdir($publicPath . $refPath, 0755, true);
}
$filename = urlencode($file->getClientOriginalName());
$file->move($publicPath . $refPath, $filename);
$freshAvi = '/' . $refPath . '/' . $filename;
$member->avatar = $freshAvi;
}
//changing password
if (isset($request->fresh_pass) && $request->fresh_pass !== '') {
if ($request->fresh_pass === $request->fresh_pass_confirm) {
$member->password = Hash::make($request->fresh_pass);
} else {
return ['status' => false, 'message' => "Passwords Do Not Match"];
}
}
$member->handle = $request->handle;
$member->email = $request->email;
$member->pronoun = $request->pronouns;
if ($member->save()) {
return ['status' => true, 'message' => "Profile Editited"];
} else {
return ['status' => false, 'message' => "Profile Not Editited"];
}
}
}

View file

@ -9,10 +9,16 @@ use GuzzleHttp\Exception\ConnectException;
class SourceRepository class SourceRepository
{ {
protected $source; protected $source;
protected $missing;
protected $updated;
protected $sources;
public function __construct(Source $source) public function __construct(Source $source)
{ {
$this->source = $source; $this->source = $source;
$this->missing = [];
$this->updated = [];
$this->sources = $source::where("active", true)->get();
} }
public function getActive() public function getActive()
@ -20,51 +26,80 @@ class SourceRepository
return $this->source::where("active", true)->get(); return $this->source::where("active", true)->get();
} }
public function updateSourceData() public function updateSourceData($index = 0)
{ {
$sources = $this->getActive();
$missing = [];
$checked = [];
//checks all the sources to refresh data //checks all the sources to refresh data
foreach ($sources as $source) { $count = count($this->sources);
$result = []; if ($count == 0) {
if ($source['type'] == 'mastodon') { return [
if ($source['token'] == null) { 'checked' => $this->updated,
try { 'notchecked' => $this->missing,
$result = \Mastodon::domain('https://' . $source['url']) 'count' => $count,
->get('/instance/domain_blocks'); 'updated' => 'false',
array_push($checked, ['source' => $source->url]); ];
} catch (ConnectException $e) { } else {
array_push($missing, ['source' => $source->url]); //check index
} if ($index <= $count - 1) {
$source = $this->sources[$index];
$result = $this->getMastoBlocklist($source);
if (count($result) > 0) {
$source->list_data = json_encode($result);
$source->last_updated = Carbon::now();
$source->save();
array_push($this->updated, ['source' => $source->url]);
$index++;
$result = $this->updateSourceData($index);
} else { } else {
try { //if empty run the same index again
$result = \Mastodon::domain('https://' . $source['url']) array_push($this->missing, ['source' => $source->url]);
->token($source['token']) $result = $this->updateSourceData($index);
->get('/instance/domain_blocks');
array_push($checked, ['source' => $source->url]);
} catch (ConnectException $e) {
}
} }
} elseif ($source['type'] == 'custom' && $source['format'] == 'csv') { } else {
//continue
}
return [
'checked' => $this->updated,
'notchecked' => $this->missing,
'count' => $count,
'updated' => 'true',
];
}
}
private function getMastoBlocklist($source)
{
$result = [];
if ($source['type'] == 'mastodon') {
if ($source['token'] == null) {
try { try {
$denylist = array_map('str_getcsv', file('https://' . $source['url'])); $result = \Mastodon::domain('https://' . $source['url'])
foreach ($denylist as $item) { ->get('/instance/domain_blocks');
array_push($result, [ } catch (ConnectException $e) {
'domain' => $item[0], //TODO: Logo Errors
'severity' => $item[1], }
'comment' => $item[2]]); } else {
} try {
array_push($checked, ['source' => $source->url]); $result = \Mastodon::domain('https://' . $source['url'])
} catch (Exception $e) { ->token($source['token'])
array_push($missing, ['source' => $source->url]); ->get('/instance/domain_blocks');
} catch (ConnectException $e) {
//TODO: Logo Errors
} }
} }
} elseif ($source['type'] == 'custom' && $source['format'] == 'csv') {
$source->list_data = json_encode($result); try {
$source->last_updated = Carbon::now(); $denylist = array_map('str_getcsv', file('https://' . $source['url']));
$source->save(); foreach ($denylist as $item) {
array_push($result, [
'domain' => $item[0],
'severity' => $item[1],
'comment' => $item[2]]);
}
array_push($checked, ['source' => $source->url]);
} catch (Exception $e) {
array_push($missing, ['source' => $source->url]);
}
} }
return ['checked' => $checked, 'notchecked' => $missing]; return $result;
} }
} }

View file

@ -23,8 +23,11 @@ class UpdateService
public function data() public function data()
{ {
$response = $this->source->updateSourceData(); $response = $this->source->updateSourceData();
return count($response['checked']) . ' SOURCES UPDATED - ' . if ($response['updated'] == 'true') {
count($response['notchecked']) . ' SOURCES NOT CHECKED'; return count($response['checked']) . ' SOURCES UPDATED';
} else {
return 'NO SOURCES PRESENT';
}
} }
public function list() public function list()
@ -33,14 +36,15 @@ class UpdateService
$fresh = 0; $fresh = 0;
$unified = []; $unified = [];
$sources = $this->source->getActive(); $sources = $this->source->getActive();
$locations = $this->location->getActiveLocations();
foreach ($sources as $source) { foreach ($sources as $source) {
//$listData = json_decode(); //$listData = json_decode();
foreach (json_decode($source->list_data) as $item) { foreach (json_decode($source->list_data) as $item) {
$index = array_search($item->domain, array_column($unified, 'url')); $index = array_search($item->domain, array_column($unified, 'url'));
if ($index) { if ($index) {
//if there is a match, update the count //if there is a match, update the count and comment
if ($item->severity == "suspend" || $item->severity == "defederate") { if ($item->severity == "suspend" || $item->severity == "defederate") {
++$unified[$index]['block_count']; ++$unified[$index]['block_count'];
array_push($unified[$index]['block_vote'], $source->url); array_push($unified[$index]['block_vote'], $source->url);
@ -48,6 +52,9 @@ class UpdateService
++$unified[$index]['silence_count']; ++$unified[$index]['silence_count'];
array_push($unified[$index]['silence_vote'], $source->url); array_push($unified[$index]['silence_vote'], $source->url);
} }
if (!is_null($item->comment) && $item->comment != ' ' && $item->comment != '') {
$unified[$index]['comment'] = $item->comment . '+' . $unified[$index]['comment'];
}
} else { } else {
$silence = 0; $silence = 0;
$suspend = 0; $suspend = 0;
@ -74,6 +81,12 @@ class UpdateService
} }
} }
//clear out all previous descriptions
foreach ($locations as $loc) {
$loc->public_comments = ' ';
$loc->save();
}
foreach ($unified as $item) { foreach ($unified as $item) {
$location = $this->location->getLocation($item['url']); $location = $this->location->getLocation($item['url']);
if ($location) { if ($location) {
@ -86,6 +99,12 @@ class UpdateService
$location->silence_count = $item['silence_count']; $location->silence_count = $item['silence_count'];
$location->silence_vote = []; $location->silence_vote = [];
$location->silence_vote = $item['silence_vote']; $location->silence_vote = $item['silence_vote'];
//clear descriptions
if (!is_null($item['comment']) || !$item['comment'] != " ") {
$location->public_comments = $item['comment'];
} else {
$location->public_comments = 'Comments Pending';
}
$location->actions_count = $item['block_count'] + $item['silence_count']; $location->actions_count = $item['block_count'] + $item['silence_count'];
@ -112,20 +131,20 @@ class UpdateService
} }
$new = Location::create([ $new = Location::create([
'uuid' => Uuid::uuid4(), 'uuid' => Uuid::uuid4(),
'name' => $item['url'], 'name' => $item['url'],
'url' => $item['url'], 'url' => $item['url'],
'description' => ($item['comment'] != null) ? $item['comment'] : "no description", 'public_comments' => ($item['comment'] != null) ? $item['comment'] : "comments pending",
'active' => $status, 'active' => $status,
'rating' => $rating, 'rating' => $rating,
'added_by' => 1, 'added_by' => 1,
'tags' => 'poor moderation, hate speech', 'tags' => 'poor moderation, hate speech',
'images' => json_encode($images), 'images' => json_encode($images),
'block_count' => $item['block_count'], 'block_count' => $item['block_count'],
'block_vote' => $item['block_vote'], 'block_vote' => $item['block_vote'],
'silence_count' => $item['silence_count'], 'silence_count' => $item['silence_count'],
'silence_vote' => $item['silence_vote'], 'silence_vote' => $item['silence_vote'],
'actions_cont' => $item['block_count'] + $item['silence_count'] 'actions_cont' => $item['block_count'] + $item['silence_count']
]); ]);
} }
} }

View file

@ -1,16 +1,33 @@
{ {
"name": "laravel/laravel", "name": "project/thebadspace",
"type": "project", "type": "moderation",
"description": "The skeleton application for the Laravel framework.", "description": "A tool for improving independent social media curation",
"keywords": ["laravel", "framework"], "version": "0.7-alpha",
"license": "MIT", "keywords": [
"require": { "thebadspace",
"php": "^8.2", "tbs",
"guzzlehttp/guzzle": "^7.2", "activty-pub",
"laravel/framework": "^11.0", "laravel",
"laravel/sanctum": "^4.0", "framework",
"laravel/tinker": "^2.8", "moderation",
"revolution/laravel-mastodon-api": "^3.0" "safety",
"curation",
"tooling",
"fediverse"
],
"license": [
"GPL-3.0-only"
],
"authors": [
{
"name": "Ro",
"homepage": "https://roiskinda.cool"
}
],
"support": {
"source": "https://koodu.h-i.works/projects/thebadspace",
"wiki": "https://koodu.h-i.works/projects/thebadspace/wiki/?action=_pages",
"issues": "https://koodu.h-i.works/projects/thebadspace/issues"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.9.1", "fakerphp/faker": "^1.9.1",
@ -50,7 +67,9 @@
}, },
"extra": { "extra": {
"laravel": { "laravel": {
"dont-discover": [] "dont-discover": [
]
} }
}, },
"config": { "config": {

View file

@ -36,6 +36,7 @@ input[type="submit"] {
border: 0; border: 0;
transition: all 0.3s linear; transition: all 0.3s linear;
height: 35px; height: 35px;
margin-top: 15px;
} }
select { select {
@ -45,4 +46,5 @@ select {
appearance: none; appearance: none;
color: var(--primary); color: var(--primary);
background: var(--secondary); background: var(--secondary);
height: 35px;
} }

View file

@ -199,9 +199,7 @@ footer {
padding: 10px; padding: 10px;
gap: 10px; gap: 10px;
height: auto; height: auto;
width: 80%; width: auto;
margin: 20px auto;
max-width: 1000px;
position: relative; position: relative;
} }
@ -213,6 +211,15 @@ footer > div:nth-child(2) {
text-align: right; text-align: right;
} }
/*
member stuff
*/
.your-avatar {
width: 250px;
border-radius: 5px;
}
/* /*
responsive responsive
*/ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -11,8 +11,10 @@
@csrf @csrf
<label>Edit Location Name</label><br> <label>Edit Location Name</label><br>
<input type="text" name="name" value="{{$location->name}}" /><br> <input type="text" name="name" value="{{$location->name}}" /><br>
<label>Edit Location Comments</label><br>
<textarea name="description">{{$location->description}}</textarea><br> <label>Edit Location Notes</label><br>
<textarea name="notes">{{$location->notes}}</textarea><br>
<label>Edit Reference Links (comma seperated)</label><br> <label>Edit Reference Links (comma seperated)</label><br>
<textarea name="archive_links">{{$location->archive_links}}</textarea><br> <textarea name="archive_links">{{$location->archive_links}}</textarea><br>
<label>Edit Reference Images</label><br> <label>Edit Reference Images</label><br>
@ -24,7 +26,7 @@
<h3>Images</h3> <h3>Images</h3>
@if($images != null) @if($images != null)
@foreach($images as $image) @foreach($images as $image)
<a href="/{{$image->path}}" class="location-image" style="background: url(/{{$image->path}}) no-repeat center center / cover #fc6399" /> <a href="{{$image->path}}" class="location-image" style="background: url({{$image->path}}) no-repeat center center / cover #fc6399" />
</a> </a>
@endforeach @endforeach
@endif @endif

View file

@ -2,12 +2,38 @@
@section('title', 'Den | Member Admin') @section('title', 'Den | Member Admin')
@php
if($mode == 'member-create')
{
$action_url = '/den/member/create';
}else{
$action_url = '/den/member/edit';
}
@endphp
@section('main-content') @section('main-content')
<section> <section>
<article> <article>
<h2>Member Listing </h2> @switch($mode)
<a href="/den/admin/update">UPDATE LOCATIONS</a><br /> @case('member-edit')
<a href="/den/admin/compile">COMPILE LOCATIONS</a> <h2>Edit Info for {{$member->handle}}</h2>
@include('forms.member-edit')
<br />
@break
@case('member-create')
<h2>New Member Info</h2>
@include('forms.member-edit')
<br />
@break
@default
<h2>Member Listing </h2>
@foreach($members as $member)
<a href="/den/member/{{$member->uuid}}">{{$member->handle}}</a><br />
@endforeach
<h2>Add Member </h2>
<a href="/den/member/edit/create">Make a new friend</a><br />
@endswitch
</article> </article>
</section> </section>
@endsection @endsection

View file

@ -0,0 +1,13 @@
@extends('frame')
@section('title', 'Den | Your Profile')
@section('main-content')
<section>
<article>
<h2>Edit Profile Deets </h2>
@include('forms.profile-edit')
</article>
</section>
<br />
@endsection

View file

@ -8,8 +8,8 @@
<article> <article>
<h2>Hey {{$handle}} </h2> <h2>Hey {{$handle}} </h2>
<a href="/den/you">Edit Your Account</a><br /> <a href="/den/you">Edit Your Account</a><br />
<a href="/den/locations">Manage Locations</a><br /> @if($role==0)
@if($role==1) <a href="/den/locations">Manage Locations</a><br />
<a href="/den/member">Manage Members</a><br /> <a href="/den/member">Manage Members</a><br />
@endif @endif
</article> </article>

View file

@ -0,0 +1,61 @@
<form action="{{$action_url}}" method="post" enctype="multipart/form-data">
<div>
@php
isset($avatar) ? $avi = $avatar : $avi = '';
@endphp
<img class="your-avatar" src='{{$avi}}'>
<br />
<label>Handle</label><br />
@php
isset($member->handle) ? $handle = $member->handle : $handle = '';
@endphp
<input type="text" name="handle" value="{{$handle}}" />
<br />
@php
isset($member->email) ? $email = $member->email : $email = '';
@endphp
<label>Email</label><br />
<input type="text" name="email" value="{{$email}}" />
<br />
@php
isset($member->pronoun) ? $pronoun = $member->pronoun : $pronoun = '';
@endphp
<label>Pronouns</label><br />
<input type="text" name="pronouns" value="{{$pronoun}}" />
<br />
@php
isset($member->role) ? $role = $member->role : $role = 2;
@endphp
<label>Role</label><br />
<input type="text" name="role" value="{{$role}}" />
<br />
@if($mode == 'member-create')
<label>Fresh Password</label><br />
<input type="password" id="fresh_pass" name="fresh_pass" value="" />
<br />
<label>Confirm Fresh Password</label><br />
<input type="password" id="fresh_pass_confirm" name="fresh_pass_confirm" value="" />
<br />
@endif
@php
isset($member->active) ? $status = $member->active : $status = false;
@endphp
<label>Status</label><br />
<select name="status">
@if($status)
<option value="true" selected>Active</option>
<option value="false">Not Active</option>
@else
<option value="true">Active</option>
<option value="false" selected>Not Active</option>
@endif
</select>
<br />
</div>
@csrf
@php
isset($member->uuid) ? $uuid = $member->uuid : $uuid = 0;
@endphp
<input type="hidden" name="id" value="{{$uuid}}" />
<input type="submit" value="Edit Member" name="submit_button">
</form>

View file

@ -0,0 +1,30 @@
<form action="/den/profile/edit" method="post" enctype="multipart/form-data">
<div>
<img class="your-avatar" src='{{$avatar}}'>
<br />
<label>New Avatar</label><br />
<input type="file" id="fresh_avi" name="fresh_avi" />
<br />
<label>Handle</label><br />
<input type="text" name="handle" value="{{$handle}}" />
<br />
<label>Email</label><br />
<input type="text" name="email" value="{{$email}}" />
<br />
<label>Pronouns</label><br />
<input type="text" name="pronouns" value="{{$pronouns}}" />
<br />
<h2>Change Password</h2>
<label>Fresh Password</label><br />
<input type="password" id="fresh_pass" name="fresh_pass" value="" />
<br />
<label>Confirm Fresh Password</label><br />
<input type="password" id="fresh_pass_confirm" name="fresh_pass_confirm" value="" />
<br />
</div>
@csrf
<input type="hidden" name="id" value="{{$uuid}}" />
<input type="submit" value="Edit Profile" name="submit_button">
</form>

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="theme-color" content="#d66365" /> <meta name="theme-color" content="#c3639e" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title> <title>
@yield('title') @yield('title')
@ -94,7 +94,7 @@
an <a href="https://h-i.works">h.i.</a> project an <a href="https://h-i.works">h.i.</a> project
</div> </div>
<div> <div>
a0.6 a0.7
</div> </div>
</footer> </footer>
</body> </body>

View file

@ -38,27 +38,8 @@
@endisset @endisset
<section class="index-meta"> <section class="index-meta">
<article> <article>
<h2>Recent Updates</h2>
@foreach($recent as $item)
<a class="list-link" role="listitem" href="/location/{{$item->uuid}}">
@php
$rating = floor(($item->actions_count / $sources)*100);
@endphp
<span class="item-rating">{{$rating}}%</span>
<label class="item-name">{{$item->name}}</label>
<div class="item-silence">
<img class="item-icon" src="/assets/images/global/status-silence.svg" title="silenced" />
{{$item->silence_count}}
</div>
<div class="item-block">
<img class="item-icon" src="/assets/images/global/status-suspend.svg" title="suspended" />
{{$item->block_count}}
</div>
</a>
@endforeach
<h2>Info</h2>
<div class="index-meta"> <div class="index-meta">
<label>Locations Tracked</label> <label>Active Locations Tracked</label>
<label>{{$count}}</label> <label>{{$count}}</label>
<label>Total Sources</label> <label>Total Sources</label>
<label>{{$sources}}</label> <label>{{$sources}}</label>

View file

@ -6,13 +6,19 @@
@parent @parent
<section> <section>
<article> <article>
<h2>Description</h2> <h2>Public Comments</h2>
{{$location->description}}<br /> @foreach($comments as $comment)
@if($comment != " " && $comment != '')
{{trim($comment)}}<br /><br />
@endif
@endforeach
<h2>Notes</h2>
{{$location->notes}}
<h2>References</h2> <h2>References</h2>
<h3>Images</h3> <h3>Images</h3>
@if($images != null) @if($images != null)
@foreach($images as $image) @foreach($images as $image)
<a href="/{{$image->path}}" class="location-image" style="background: url(/{{$image->path}}) no-repeat center center / cover #fc6399" /> <a href="{{$image->path}}" class="location-image" style="background: url({{$image->path}}) no-repeat center center / cover #fc6399" />
</a> </a>
@endforeach @endforeach
@endif @endif
@ -21,6 +27,9 @@
$rating = floor(($action / $sources_count)*100); $rating = floor(($action / $sources_count)*100);
@endphp @endphp
<h3>Links</h3> <h3>Links</h3>
@foreach($links as $link)
<a href="{{$link}}">{{$link}}</a><br />
@endforeach
<div class="location-rating"> <div class="location-rating">
<div> <div>
<img class="rating-icon" src="/assets/images/global/heat.svg" title="heat-rating" /> <img class="rating-icon" src="/assets/images/global/heat.svg" title="heat-rating" />
@ -47,8 +56,11 @@
<br /> <br />
Heat Rating is the percentage of <a href="/about#how">Current Sources</a> that have taken action against an instance. The higher the number of Sources that have silenced and/or suspended an instance, the higher the Heat Rating. Heat Rating is the percentage of <a href="/about#how">Current Sources</a> that have taken action against an instance. The higher the number of Sources that have silenced and/or suspended an instance, the higher the Heat Rating.
<br />
<br />UPDATED : {{$updated}} <br />UPDATED : {{$updated}}
<br />
<br />
</article> </article>
</section> </section>
@endsection @endsection

View file

@ -5,6 +5,7 @@ use App\Http\Controllers\FrontIndexController;
use App\Http\Controllers\AuthController; use App\Http\Controllers\AuthController;
use App\Http\Controllers\DenController; use App\Http\Controllers\DenController;
use App\Http\Controllers\LocationController; use App\Http\Controllers\LocationController;
use App\Http\Controllers\MemberController;
use App\Http\Controllers\ExportController; use App\Http\Controllers\ExportController;
use App\Http\Controllers\AppealController; use App\Http\Controllers\AppealController;
@ -40,7 +41,6 @@ Route::get("/logout", [AuthController::class, 'leave']);
//back //back
Route::group(['prefix' => 'den', 'middleware' => 'member.check'], function () { Route::group(['prefix' => 'den', 'middleware' => 'member.check'], function () {
Route::get("/", [DenController::class, 'start']); Route::get("/", [DenController::class, 'start']);
Route::get("/member", [DenController::class, 'member']);
Route::get("/listings/{pageNum}", [DenController::class, 'location']); Route::get("/listings/{pageNum}", [DenController::class, 'location']);
Route::get("/location/edit/{uuid}", [DenController::class, 'locationEdit']); Route::get("/location/edit/{uuid}", [DenController::class, 'locationEdit']);
Route::get("/locations", [DenController::class, 'locations']); Route::get("/locations", [DenController::class, 'locations']);
@ -48,4 +48,13 @@ Route::group(['prefix' => 'den', 'middleware' => 'member.check'], function () {
Route::post("/locations/edit", [LocationController::class, 'editLocation']); Route::post("/locations/edit", [LocationController::class, 'editLocation']);
Route::get("/admin/update", [LocationController::class, 'updateLocations']); Route::get("/admin/update", [LocationController::class, 'updateLocations']);
Route::get("/admin/compile", [LocationController::class, 'compileLocations']); Route::get("/admin/compile", [LocationController::class, 'compileLocations']);
//member stuff
Route::get("/you", [MemberController::class, 'profile']);
Route::get("/member", [MemberController::class, 'index']);
Route::get("/member/{uuid}", [MemberController::class, 'editMember']);
Route::get("/member/edit/create", [MemberController::class, 'createMember']);
//actions
Route::post("/profile/edit", [MemberController::class, 'profileEdit']);
Route::post("/member/edit", [MemberController::class, 'memberEdit']);
Route::post("/member/create", [MemberController::class, 'memberCreate']);
}); });