mirror of
https://koodu.h-i.works/projects/thebadspace
synced 2025-05-06 14:41:02 -05:00
Compare commits
No commits in common. "develop" and "a0.06" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,8 +4,6 @@
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/reference
|
||||
/public/assets/images/references
|
||||
/public/assets/images/members
|
||||
/storage/*.key
|
||||
/vendor
|
||||
.env
|
||||
|
@ -55,15 +55,20 @@ return $config
|
||||
]
|
||||
],
|
||||
'no_multiline_whitespace_around_double_arrow' => true,
|
||||
'no_spaces_around_offset' => true,
|
||||
'no_unused_imports' => true,
|
||||
'no_whitespace_before_comma_in_array' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'object_operator_without_whitespace' => true,
|
||||
'single_blank_line_before_namespace' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'trim_array_spaces' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
'whitespace_after_comma_in_array' => true,
|
||||
'single_line_after_imports' => true,
|
||||
//'single_blank_line_before_namespace' => true, php fixer doesn't like this rule?
|
||||
'ordered_imports' => [
|
||||
'sort_algorithm' => 'none',
|
||||
],
|
||||
//Other rules here...
|
||||
])
|
||||
->setLineEnding("\n");
|
||||
|
88
README.md
88
README.md
@ -1,91 +1,9 @@
|
||||
# The Bad Space
|
||||
|
||||
As the fediverse is a largely unregulated space, The Bad Space arose from a need for collaborative safety tools that can be used identify instances that house bad actors, are poorly moderated, and/or contain inappropriate/offensive content (CSAM, hate speech, fascist ideology, etc.) that puts marginalized communities at risk for harassment and abuse.
|
||||
A searcable catalog of the worst places on the web.
|
||||
|
||||
The goal of TBS is to provide a way for communities to work together based on their specific needs and not a one-size-fits-all moderation approach for a more tailored and nuanced experience.
|
||||
More features incoming
|
||||
|
||||
## Requirements
|
||||
TBS is built with the [Laravel](https://laravel.com/) framework, so PHP 8.2 and [Composer](https://getcomposer.org/doc/00-intro.md) is required, as well as [PostgreSQL](https://www.postgresql.org/download/) 14+ for the database. [Git](https://git-scm.com/downloads) is not strictly required as the code base can be manually downloaded, but it does make installing and updating easier.
|
||||
An Hi Project =)
|
||||
|
||||
*NOTE*: On Linux, some additional PHP extensions need to be installed, but don't worry it's easy. Run `sudo apt-get install php-mbstring`, `sudo apt-get install php-xml` and `sudo apt-get install php-zip `to get them and you're good to go.
|
||||
|
||||
*NOTE*: Environment set up on a local machine can be a pain in the ass, so to make that easier [Herd](https://herd.laravel.com/) can be used for MacOS and Windows and [FlyEnv](https://www.macphpstudy.com/) for Linux folks that don't want to do it all by hand. [DBNgin](https://dbngin.com/) can be used to make database setup painless. Yes, and they are all free.
|
||||
|
||||
## Install
|
||||
Use `git clone https://koodu.h-i.works/projects/thebadspace.git` to install or just go grab the zip from the repo and unzip it.
|
||||
|
||||
Create your database in PostgreSQL and remember the name of the database and the username and password used to access it.
|
||||
|
||||
Go into the folder through your terminal and run `composer install` to grab all the dependencies needed and create the `.env` file that stores all project specific variables.
|
||||
|
||||
Open the `.env` file and add your database information so it resembles the following:
|
||||
|
||||
```
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=your_database_name
|
||||
DB_USERNAME=database_username
|
||||
DB_PASSWORD=database_password
|
||||
```
|
||||
|
||||
Now that the app is aware of what database is being used, it's time to set up the tables.
|
||||
|
||||
Back in your terminal, run `php artisan migrate` and if your database is set up correctly, all the tables will be created automatically.
|
||||
|
||||
Congrats, you're all set up.
|
||||
|
||||
## Usage
|
||||
TBS can be run locally or on a remote server.
|
||||
|
||||
To get up and running on your local machine, hop back in your terminal and run `php -S localhost:8000 -t public/ ` in the folder of the project.
|
||||
|
||||
If you're running it on a server, use these configs to get going.
|
||||
|
||||
**For Nginx**
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourcoolassdomain.com;
|
||||
client_max_body_size 20M //Change to whatever to limit/increase file upload size
|
||||
location / {
|
||||
try_files $uri /index.php$is_args$args;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**For Apache**
|
||||
```
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin admin@yourcoolassdomain.com
|
||||
ServerName yourcoolassdomain.com
|
||||
ServerAlias www.yourcoolassdomain.com
|
||||
DocumentRoot /path-to-fipamo-folder/public
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
Remember to restart Apache/NGINX once these configs have been plugged in.
|
||||
|
||||
Go the root domain of the site you just set up (or http://localhost:8000 if running locally) and you'll see a screen to set up an admin account.
|
||||
|
||||
Once the admin account has been created, go to The Den section in the menu and login in.
|
||||
|
||||
### Adding Sources
|
||||
The core of TBS is adding sources of the instances (currently just Mastodon, but more coming soon) to be used to compile exportable block lists. *Note:* While many instance block lists are publicly available, it is good practice to ask for permission to use them.
|
||||
|
||||
Sources can be added under the `Manage Sources` link. Fill in the appropriate information and then save. *Note:* Some instances keep their block lists private for safety reasons, so you will need an access token to use them. These tokens cannot be created by TBS and must be obtained from the instance itself.
|
||||
|
||||
Once everything is filled in, save the source.
|
||||
|
||||
To remove a source from the compilation process, mark its status as active and save.
|
||||
|
||||
## Compiling Sources
|
||||
Now it's time to grab the block list data from the individual sources entered needed to compile curation information.
|
||||
|
||||
This can be done under `Manage Locations` on the Den front page. Click `Update Source Data` to grab block list info from the sources added earlier.
|
||||
|
||||
When that is completed, then click `Update Locations Data` to compile individual sources data into one that the site uses to display info about moderated instances. If this is being run for the first time, this must be clicked *twice*.
|
||||
|
||||
Once that's done, go to the main domain again, and you'll see the results of the compilation process.
|
||||
|
@ -34,6 +34,14 @@ 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)
|
||||
{
|
||||
$member = Auth::user();
|
||||
|
@ -46,12 +46,12 @@ class ExportController extends Controller
|
||||
$sources = Source::where("active", true)->get();
|
||||
if ($type == 'mastodon') {
|
||||
$columns = [
|
||||
'#domain',
|
||||
'#severity',
|
||||
'#public_comment',
|
||||
'#reject_media',
|
||||
'#reject_reports',
|
||||
'#obfuscate',
|
||||
'domain',
|
||||
'severity',
|
||||
'public_comment',
|
||||
'reject_media',
|
||||
'reject_reports',
|
||||
'obfuscate',
|
||||
];
|
||||
};
|
||||
|
||||
@ -60,18 +60,13 @@ class ExportController extends Controller
|
||||
if ($rate * 100 >= $percent) {
|
||||
if ($type == 'mastodon') {
|
||||
//comman break teh CSV so just take them out
|
||||
$comments = str_replace(",", ";", $location->public_comments);
|
||||
$comments = str_replace(",", ";", $location->description);
|
||||
|
||||
//remove extra white space
|
||||
$comments = str_replace(["\n\r", "\n", "\r"], " ", $comments);
|
||||
$comments = str_replace(['"', "'"], "", $comments);
|
||||
if ($location->rating == 'defederate') {
|
||||
$rating = 'suspend';
|
||||
} else {
|
||||
$rating = $location->rating;
|
||||
}
|
||||
//add to the export list
|
||||
array_push($list, [$location->url, $rating, $comments, "FALSE", "FALSE", "FALSE"]);
|
||||
array_push($list, [$location->url, $location->rating, $comments, "FALSE", "FALSE", "FALSE"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,50 +6,34 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Repositories\LocationRepository;
|
||||
use App\Repositories\SourceRepository;
|
||||
use App\Repositories\MemberRepository;
|
||||
use App\Services\PaginationService;
|
||||
|
||||
class FrontIndexController extends Controller
|
||||
{
|
||||
protected $location;
|
||||
protected $source;
|
||||
protected $member;
|
||||
protected $pagination;
|
||||
|
||||
public function __construct(
|
||||
LocationRepository $locationRepository,
|
||||
SourceRepository $sourceRepository,
|
||||
MemberRepository $memberRepository,
|
||||
PaginationService $paginationService
|
||||
) {
|
||||
$this->location = $locationRepository;
|
||||
$this->source = $sourceRepository;
|
||||
$this->member = $memberRepository;
|
||||
$this->pagination = $paginationService;
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
//check to see if there are any accounts
|
||||
if (count($this->member->getAll()) == 0) {
|
||||
return view('back.member', [
|
||||
'mode' => 'admin-create',
|
||||
'title' => "Welcome, welcome"]);
|
||||
} else {
|
||||
//for fresh installs that dont have any source data yet
|
||||
$latest_update = 'Never Run';
|
||||
if (count($this->location->getRecent()) != 0) {
|
||||
$latest_update = $this->location->getRecent()[0]->updated_at->format('Y M d');
|
||||
}
|
||||
return view('front.index', [
|
||||
'count' => count($this->location->getActiveLocations()),
|
||||
'sources' => count($this->source->getActive()),
|
||||
'recent' => $this->location->getRecent(),
|
||||
'latest_date' => $latest_update,
|
||||
'latest_date' => $this->location->getRecent()[0]->updated_at->format('Y M d'),
|
||||
'title' => "The Bad Space"
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function indexSearch(Request $request)
|
||||
{
|
||||
@ -92,19 +76,15 @@ class FrontIndexController extends Controller
|
||||
}
|
||||
|
||||
if (isset($member->role)) {
|
||||
($member->role == 0 || $member->role == 1) ? $edit = true : $edit = false;
|
||||
($member->role == 1 || $member->role == 2) ? $edit = true : $edit = false;
|
||||
}
|
||||
|
||||
$links = explode(',', $location->archive_links);
|
||||
$comments = explode('+', $location->public_comments);
|
||||
return view('front.location', [
|
||||
'title' => str_replace(".", " ", $name),
|
||||
'location' => $location,
|
||||
'comments' => $comments,
|
||||
'actions' => $location->block_count + $location->silence_count,
|
||||
'sources_count' => count($sources),
|
||||
'images' => json_decode($location->images),
|
||||
'links' => $links,
|
||||
'updated' => $location->updated_at->format('Y M d'),
|
||||
'edit' => $edit
|
||||
]);
|
||||
|
@ -5,7 +5,6 @@ namespace App\Http\Controllers;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\UpdateService;
|
||||
use App\Repositories\LocationRepository;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class LocationController extends Controller
|
||||
{
|
||||
@ -19,51 +18,34 @@ class LocationController extends Controller
|
||||
$this->location = $locationRepository;
|
||||
}
|
||||
|
||||
//actions
|
||||
public function updateLocations()
|
||||
{
|
||||
//role check
|
||||
$member = Auth::user();
|
||||
if ($member->role == 0) {
|
||||
$result = $this->update->data();
|
||||
|
||||
return back()->with(
|
||||
'message',
|
||||
$result
|
||||
);
|
||||
} else {
|
||||
return back()->withErrors('message', 'Nah, you don\'t have permission to do this');
|
||||
}
|
||||
}
|
||||
|
||||
public function compileLocations()
|
||||
{
|
||||
//role check
|
||||
$member = Auth::user();
|
||||
if ($member->role == 0) {
|
||||
$result = $this->update->list();
|
||||
|
||||
return back()->with(
|
||||
'message',
|
||||
$result
|
||||
);
|
||||
} else {
|
||||
return back()->withErrors('message', 'Nah, you don\'t have permission to do this');
|
||||
}
|
||||
}
|
||||
|
||||
public function editLocation(Request $request)
|
||||
{
|
||||
$token = csrf_token();
|
||||
//role check
|
||||
$member = Auth::user();
|
||||
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 {
|
||||
return back()->withErrors('message', 'Nah, you don\'t have permission to do this');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,182 +0,0 @@
|
||||
<?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.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function adminCreate(Request $request)
|
||||
{
|
||||
//should only be run of no members exist
|
||||
if (count($this->member->getAll()) == 0) {
|
||||
$token = csrf_token();
|
||||
$valid = $request->validate([
|
||||
'handle' => ['required'],
|
||||
'email' => ['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(['Shame on you for even trying that.']);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Repositories\SourceRepository;
|
||||
|
||||
class SourceController extends Controller
|
||||
{
|
||||
protected $sources;
|
||||
|
||||
public function __construct(
|
||||
SourceRepository $sourceRepo
|
||||
) {
|
||||
$this->sources = $sourceRepo;
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$member = Auth::user();
|
||||
return view('back.sources', [
|
||||
'mode' => 'index',
|
||||
'handle' => $member->handle,
|
||||
'sources' => $this->sources->getAll(),
|
||||
'title' => "Manage Sources"]);
|
||||
}
|
||||
|
||||
public function editSource(Request $request, $id = 0)
|
||||
{
|
||||
$source = $this->sources->get($id);
|
||||
|
||||
return view('back.sources', [
|
||||
'mode' => 'source-edit',
|
||||
'source' => $source,
|
||||
'title' => "Edit Source Info"]);
|
||||
}
|
||||
|
||||
public function createSource(Request $Request)
|
||||
{
|
||||
return view('back.sources', [
|
||||
'mode' => 'source-create',
|
||||
'title' => "Enter a new Source"]);
|
||||
}
|
||||
|
||||
//actions
|
||||
|
||||
public function sourceEdit(Request $request)
|
||||
{
|
||||
$token = csrf_token();
|
||||
//role check
|
||||
$member = Auth::user();
|
||||
if ($member->role == 0) {
|
||||
$valid = $request->validate([
|
||||
'url' => ['required'],
|
||||
'type' => ['required'],
|
||||
'status' => ['required'],
|
||||
'format' => ['required'],
|
||||
]);
|
||||
|
||||
if ($valid) {
|
||||
$response = $this->sources->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 sourceCreate(Request $request)
|
||||
{
|
||||
$token = csrf_token();
|
||||
$member = Auth::user();
|
||||
if ($member->role == 0) {
|
||||
$valid = $request->validate([
|
||||
'url' => ['required'],
|
||||
'type' => ['required'],
|
||||
'status' => ['required'],
|
||||
'format' => ['required'],
|
||||
]);
|
||||
|
||||
if ($valid) {
|
||||
$response = $this->sources->add($request);
|
||||
if ($response['status'] == true) {
|
||||
return redirect('/den/sources')->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.']);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ class Location extends Model
|
||||
"uuid",
|
||||
"name",
|
||||
"url",
|
||||
"public_comments",
|
||||
"description",
|
||||
"images",
|
||||
"active",
|
||||
"rating",
|
||||
@ -35,16 +35,6 @@ class Location extends Model
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"actions_count",
|
||||
"archive_links",
|
||||
"notes",
|
||||
"archive_links"
|
||||
];
|
||||
|
||||
public function scopeSearch($query, $search)
|
||||
{
|
||||
if (!$search) {
|
||||
return $query;
|
||||
}
|
||||
return $query->whereRaw('searchtext @@ to_tsquery(\'english\', ?)', [$search])
|
||||
->orderByRaw('ts_rank(searchtext, to_tsquery(\'english\', ?)) DESC', [$search]);
|
||||
}
|
||||
}
|
||||
|
@ -9,20 +9,6 @@ class Member extends Authenticatable
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
protected $table = "member";
|
||||
protected $primaryKey = 'id';
|
||||
public $incrementing = true;
|
||||
protected $fillable = [
|
||||
"uuid",
|
||||
"handle",
|
||||
"email",
|
||||
"password",
|
||||
"active",
|
||||
"role",
|
||||
"avatar",
|
||||
"pronoun",
|
||||
"created_at",
|
||||
"last_login"
|
||||
];
|
||||
protected $fillable = ["uuid", "handle", "email", "password", "active", "role", "avatar", "pronoun", "gender"];
|
||||
}
|
||||
|
@ -5,12 +5,10 @@ namespace App\Providers;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use App\Repositories\LocationRepository;
|
||||
use App\Repositories\SourceRepository;
|
||||
use App\Repositories\MemberRepository;
|
||||
use App\Services\UpdateService;
|
||||
use App\Services\MaintenanceService;
|
||||
use App\Models\Location;
|
||||
use App\Models\Source;
|
||||
use App\Models\Member;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@ -27,10 +25,6 @@ class AppServiceProvider extends ServiceProvider
|
||||
return new SourceRepository(new Source());
|
||||
});
|
||||
|
||||
$this->app->bind(MemberRepository::class, function ($app) {
|
||||
return new MemberRepository(new Member());
|
||||
});
|
||||
|
||||
$this->app->bind(UpdateService::class, function ($app) {
|
||||
return new UpdateService(
|
||||
new LocationRepository(new Location()),
|
||||
|
@ -23,8 +23,7 @@ class LocationRepository
|
||||
$rawSearch = $terms;
|
||||
$terms = str_replace(",", "", $terms);
|
||||
$terms = str_replace(" ", "|", $terms);
|
||||
//$raw = DB::select("SELECT * FROM searchlocations(?)", [$terms]);
|
||||
$raw = Location::search($terms)->get();
|
||||
$raw = DB::select("SELECT * FROM searchlocations(?)", [$terms]);
|
||||
$results = [];
|
||||
foreach ($raw as $item) {
|
||||
if (($item->block_count + $item->silence_count) >= 2) {
|
||||
@ -58,33 +57,23 @@ class LocationRepository
|
||||
public function editLocation($request)
|
||||
{
|
||||
$location = $this->getLocation($request->id);
|
||||
$publicPath = '../public/';
|
||||
$refPath = 'assets/images/references/' . $location->uuid;
|
||||
$images = [];
|
||||
if ($request->hasfile("references")) {
|
||||
foreach ($request->references as $file) {
|
||||
if (!is_dir($publicPath . $refPath)) {
|
||||
mkdir($publicPath . $refPath, 0755, true);
|
||||
}
|
||||
$filename = urlencode($file->getClientOriginalName());
|
||||
$file->move($publicPath . $refPath, $filename);
|
||||
//$path = $file->store('reference');
|
||||
array_push($images, ["path" => '/' . $refPath . '/' . $filename]);
|
||||
$path = $file->store('reference');
|
||||
array_push($images, ["path" => $path]);
|
||||
}
|
||||
}
|
||||
if (!empty($images)) {
|
||||
$request->merge(['images' => json_encode($images)]);
|
||||
$location->images = json_encode($images);
|
||||
}
|
||||
|
||||
$location->name = $request->name;
|
||||
$location->notes = $request->notes;
|
||||
$location->description = $request->description;
|
||||
$location->archive_links = $request->archive_links;
|
||||
$location->images = json_encode($images);
|
||||
|
||||
$result = [];
|
||||
|
||||
if ($location->save()) {
|
||||
return ['status' => true, 'message' => "Location Editited" . $request->hasfile("references")];
|
||||
return ['status' => true, 'message' => "Location Editited -IMG- " . $request->hasfile("references")];
|
||||
} else {
|
||||
return ['status' => false, 'message' => "Location Not Editited"];
|
||||
}
|
||||
|
@ -1,145 +0,0 @@
|
||||
<?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 = [];
|
||||
$newFriend = [];
|
||||
if ($request->fresh_pass === $request->fresh_pass_confirm) {
|
||||
$password = Hash::make($request->fresh_pass);
|
||||
} else {
|
||||
return ['status' => false, 'message' => "Passwords Do Not Match"];
|
||||
}
|
||||
|
||||
//if role paramter is set, not an admin add
|
||||
if (isset($request->role)) {
|
||||
$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(),
|
||||
]);
|
||||
} else {
|
||||
//set up admin
|
||||
$newFriend = $this->model::create([
|
||||
'uuid' => Uuid::uuid4(),
|
||||
'avatar' => 'default-member-avatar',
|
||||
'handle' => $request->handle,
|
||||
'email' => $request->email,
|
||||
'pronoun' => $request->pronouns,
|
||||
'role' => 0,
|
||||
'active' => true,
|
||||
'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"];
|
||||
}
|
||||
}
|
||||
}
|
@ -9,26 +9,10 @@ use GuzzleHttp\Exception\ConnectException;
|
||||
class SourceRepository
|
||||
{
|
||||
protected $source;
|
||||
protected $missing;
|
||||
protected $updated;
|
||||
protected $sources;
|
||||
|
||||
public function __construct(Source $source)
|
||||
{
|
||||
$this->source = $source;
|
||||
$this->missing = [];
|
||||
$this->updated = [];
|
||||
$this->sources = $source::where("active", true)->get();
|
||||
}
|
||||
|
||||
public function getAll()
|
||||
{
|
||||
return $this->source::all();
|
||||
}
|
||||
|
||||
public function get($id)
|
||||
{
|
||||
return $this->source::where("id", $id)->first();
|
||||
}
|
||||
|
||||
public function getActive()
|
||||
@ -36,104 +20,30 @@ class SourceRepository
|
||||
return $this->source::where("active", true)->get();
|
||||
}
|
||||
|
||||
public function edit($request)
|
||||
{
|
||||
$source = $this->get($request->id);
|
||||
$source->url = $request->url;
|
||||
$source->type = $request->type;
|
||||
$source->format = $request->format;
|
||||
$source->active = $request->status;
|
||||
$source->last_updated = Carbon::now();
|
||||
//token check
|
||||
if ($request->token != '' && $request->token != 'none') {
|
||||
$source->token = $request->token;
|
||||
} else {
|
||||
$source->token = '';
|
||||
}
|
||||
|
||||
if ($source->save()) {
|
||||
return ['status' => true, 'message' => "Source Editited"];
|
||||
} else {
|
||||
return ['status' => false, 'message' => "Source Not Editited"];
|
||||
}
|
||||
}
|
||||
|
||||
public function add($request)
|
||||
{
|
||||
$newSource = $this->source::create([
|
||||
'url' => $request->url,
|
||||
'type' => $request->type,
|
||||
'format' => $request->format,
|
||||
'active' => $request->status,
|
||||
'token' => $request->token,
|
||||
'last_udated' => Carbon::now(),
|
||||
]);
|
||||
|
||||
if ($newSource) {
|
||||
return ['status' => true, 'message' => "New Source Created!"];
|
||||
} else {
|
||||
return ['status' => false, 'message' => "Uh oh, New Source Not Created!"];
|
||||
}
|
||||
}
|
||||
|
||||
public function updateSourceData($index = 0)
|
||||
public function updateSourceData()
|
||||
{
|
||||
$sources = $this->getActive();
|
||||
$missing = [];
|
||||
$checked = [];
|
||||
//checks all the sources to refresh data
|
||||
$count = count($this->sources);
|
||||
if ($count == 0) {
|
||||
return [
|
||||
'checked' => $this->updated,
|
||||
'notchecked' => $this->missing,
|
||||
'count' => $count,
|
||||
'updated' => 'false',
|
||||
];
|
||||
} else {
|
||||
//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 {
|
||||
//if empty run the same index again
|
||||
array_push($this->missing, ['source' => $source->url]);
|
||||
$result = $this->updateSourceData($index);
|
||||
}
|
||||
} else {
|
||||
//continue
|
||||
}
|
||||
return [
|
||||
'checked' => $this->updated,
|
||||
'notchecked' => $this->missing,
|
||||
'count' => $count,
|
||||
'updated' => 'true',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function getMastoBlocklist($source)
|
||||
{
|
||||
foreach ($sources as $source) {
|
||||
$result = [];
|
||||
if ($source['type'] == 'mastodon') {
|
||||
if ($source['token'] == null) {
|
||||
try {
|
||||
$result = \Mastodon::domain('https://' . $source['url'])
|
||||
->get('/instance/domain_blocks');
|
||||
array_push($checked, ['source' => $source->url]);
|
||||
} catch (ConnectException $e) {
|
||||
//TODO: Logo Errors
|
||||
array_push($missing, ['source' => $source->url]);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$result = \Mastodon::domain('https://' . $source['url'])
|
||||
->token($source['token'])
|
||||
->get('/instance/domain_blocks');
|
||||
array_push($checked, ['source' => $source->url]);
|
||||
} catch (ConnectException $e) {
|
||||
//TODO: Logo Errors
|
||||
}
|
||||
}
|
||||
} elseif ($source['type'] == 'custom' && $source['format'] == 'csv') {
|
||||
@ -150,6 +60,11 @@ class SourceRepository
|
||||
array_push($missing, ['source' => $source->url]);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
|
||||
$source->list_data = json_encode($result);
|
||||
$source->last_updated = Carbon::now();
|
||||
$source->save();
|
||||
}
|
||||
return ['checked' => $checked, 'notchecked' => $missing];
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,8 @@ class UpdateService
|
||||
public function data()
|
||||
{
|
||||
$response = $this->source->updateSourceData();
|
||||
if ($response['updated'] == 'true') {
|
||||
return count($response['checked']) . ' SOURCES UPDATED';
|
||||
} else {
|
||||
return 'NO SOURCES PRESENT';
|
||||
}
|
||||
return count($response['checked']) . ' SOURCES UPDATED - ' .
|
||||
count($response['notchecked']) . ' SOURCES NOT CHECKED';
|
||||
}
|
||||
|
||||
public function list()
|
||||
@ -37,14 +34,13 @@ class UpdateService
|
||||
$unified = [];
|
||||
|
||||
$sources = $this->source->getActive();
|
||||
$locations = $this->location->getActiveLocations();
|
||||
|
||||
foreach ($sources as $source) {
|
||||
//$listData = json_decode();
|
||||
foreach (json_decode($source->list_data) as $item) {
|
||||
$index = array_search($item->domain, array_column($unified, 'url'));
|
||||
if ($index) {
|
||||
//if there is a match, update the count and comment
|
||||
//if there is a match, update the count
|
||||
if ($item->severity == "suspend" || $item->severity == "defederate") {
|
||||
++$unified[$index]['block_count'];
|
||||
array_push($unified[$index]['block_vote'], $source->url);
|
||||
@ -52,9 +48,6 @@ class UpdateService
|
||||
++$unified[$index]['silence_count'];
|
||||
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 {
|
||||
$silence = 0;
|
||||
$suspend = 0;
|
||||
@ -81,12 +74,6 @@ class UpdateService
|
||||
}
|
||||
}
|
||||
|
||||
//clear out all previous descriptions
|
||||
foreach ($locations as $loc) {
|
||||
$loc->public_comments = ' ';
|
||||
$loc->save();
|
||||
}
|
||||
|
||||
foreach ($unified as $item) {
|
||||
$location = $this->location->getLocation($item['url']);
|
||||
if ($location) {
|
||||
@ -99,12 +86,6 @@ class UpdateService
|
||||
$location->silence_count = $item['silence_count'];
|
||||
$location->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'];
|
||||
|
||||
@ -134,7 +115,7 @@ class UpdateService
|
||||
'uuid' => Uuid::uuid4(),
|
||||
'name' => $item['url'],
|
||||
'url' => $item['url'],
|
||||
'public_comments' => ($item['comment'] != null) ? $item['comment'] : "comments pending",
|
||||
'description' => ($item['comment'] != null) ? $item['comment'] : "no description",
|
||||
'active' => $status,
|
||||
'rating' => $rating,
|
||||
'added_by' => 1,
|
||||
|
@ -1,39 +1,14 @@
|
||||
{
|
||||
"name": "project/thebadspace",
|
||||
"type": "moderation",
|
||||
"description": "A tool for improving independent social media curation",
|
||||
"version": "0.7-alpha",
|
||||
"keywords": [
|
||||
"thebadspace",
|
||||
"tbs",
|
||||
"activty-pub",
|
||||
"laravel",
|
||||
"framework",
|
||||
"moderation",
|
||||
"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"
|
||||
},
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The skeleton application for the Laravel framework.",
|
||||
"keywords": ["laravel", "framework"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"php": "^8.1",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/framework": "^10.10",
|
||||
"laravel/sanctum": "^3.2",
|
||||
"laravel/tinker": "^2.8",
|
||||
"revolution/laravel-mastodon-api": "^3.0"
|
||||
},
|
||||
@ -42,8 +17,8 @@
|
||||
"laravel/pint": "^1.0",
|
||||
"laravel/sail": "^1.18",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^8.1",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"nunomaduro/collision": "^7.0",
|
||||
"phpunit/phpunit": "^10.1",
|
||||
"spatie/laravel-ignition": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
@ -71,17 +46,11 @@
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi"
|
||||
],
|
||||
"post-install-cmd": [
|
||||
"php -r \"copy('.env.example', '.env');\"",
|
||||
"php artisan key:generate"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": [
|
||||
|
||||
]
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
|
2775
composer.lock
generated
2775
composer.lock
generated
File diff suppressed because it is too large
Load Diff
32
database/migrations/2014_10_12_000000_create_users_table.php
Normal file
32
database/migrations/2014_10_12_000000_create_users_table.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
}
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->string('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('location', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->uuid('uuid');
|
||||
$table->string('name', length: 255);
|
||||
$table->string('url', length: 255);
|
||||
$table->text('public_comments');
|
||||
$table->json('images')->nullable();
|
||||
$table->boolean('active');
|
||||
$table->string('rating', length: 255);
|
||||
$table->integer('added_by');
|
||||
$table->timestamps(precision: 0);
|
||||
$table->timestamp('deleted_at', precision: 0)->nullable();
|
||||
$table->string('tags', length: 255);
|
||||
$table->integer('block_count')->nullable();
|
||||
$table->integer('silence_count')->nullable();
|
||||
$table->integer('actions_count')->nullable();
|
||||
$table->text('archive_links')->nullable();
|
||||
$table->json('block_vote')->nullable();
|
||||
$table->json('silence_vote')->nullable();
|
||||
$table->text('notes')->nullable();
|
||||
});
|
||||
|
||||
DB::statement("ALTER TABLE location ADD COLUMN searchtext TSVECTOR");
|
||||
DB::statement("UPDATE location SET searchtext = to_tsvector('english', name)");
|
||||
DB::statement("UPDATE location SET searchtext = to_tsvector('english', url)");
|
||||
DB::statement("UPDATE location SET searchtext = to_tsvector('english', public_comments)");
|
||||
DB::statement("UPDATE location SET searchtext = to_tsvector('english', tags)");
|
||||
DB::statement("CREATE INDEX searchtext_gin ON location USING GIN(searchtext)");
|
||||
DB::statement("CREATE TRIGGER ts_searchtext BEFORE INSERT OR UPDATE ON location FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('searchtext', 'pg_catalog.english', 'name', 'url', 'public_comments', 'tags')");
|
||||
}
|
||||
|
||||
//'name', 'url', 'public_comments', 'tags'
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
DB::statement("DROP TRIGGER IF EXISTS tsvector_update_trigger ON location");
|
||||
DB::statement("DROP INDEX IF EXISTS searchtext_gin");
|
||||
DB::statement("ALTER TABLE location DROP COLUMN searchtext");
|
||||
Schema::dropIfExists('location');
|
||||
}
|
||||
};
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('member', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->uuid('uuid');
|
||||
$table->string('handle', length: 255);
|
||||
$table->string('email', length: 255);
|
||||
$table->string('password', length: 255);
|
||||
$table->string('avatar', length: 255)->nullable();
|
||||
$table->string('pronoun', length: 255);
|
||||
$table->string('gender', length: 255)->nullable();
|
||||
$table->boolean('active');
|
||||
$table->integer('role')->nullable();
|
||||
$table->timestamp('created_at', precision: 0);
|
||||
$table->timestamp('last_login', precision: 0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('member');
|
||||
}
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('source', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('url', length: 255);
|
||||
$table->string('type', length: 255);
|
||||
$table->boolean('active');
|
||||
$table->integer('admin_id')->nullable();
|
||||
$table->string('format', length: 255);
|
||||
$table->string('token', length: 255)->nullable();
|
||||
$table->timestamp('last_updated', precision: 0)->nullable();
|
||||
$table->json('list_data')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('source');
|
||||
}
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('appeal', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->uuid('uuid');
|
||||
$table->string('location', length: 255);
|
||||
$table->string('location_admin', length: 255);
|
||||
$table->string('sponsor', length: 255);
|
||||
$table->text('description');
|
||||
$table->boolean('reviewed')->default(false);
|
||||
$table->boolean('approved')->default(false);
|
||||
$table->timestamps(precision: 0);
|
||||
$table->timestamp('deleted_at', precision: 0)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('appeal');
|
||||
}
|
||||
};
|
@ -3,12 +3,6 @@ section.index-search {
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
/* TODO: move to a global file? it’s not only for forms. */
|
||||
section.index-search :focus-visible {
|
||||
outline: 2px solid var(--white);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
section.start a.search-link {
|
||||
color: var(--highlight);
|
||||
font-size: 2.5em;
|
||||
@ -18,57 +12,36 @@ section.start a.search-link {
|
||||
}
|
||||
|
||||
form.index-search-form {
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
gap: 15px;
|
||||
width: 80%;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
form.index-search-form > input,
|
||||
form.index-search-form > button {
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
form.index-search-form > input:focus {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
form.index-search-form > input[type="text"] {
|
||||
width: 88%;
|
||||
height: 50px;
|
||||
font: 44px var(--base-type);
|
||||
}
|
||||
|
||||
form.index-search-form > input[type="hidden"] {
|
||||
/* This removes it from the grid calculations */
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
form.index-search-form > button {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
top: 9px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
form.index-search-form > button > svg {
|
||||
justify-self: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
form.index-search-form > button > img#search-icon {
|
||||
float: none;
|
||||
}
|
||||
|
||||
form.index-search-form > button > span {
|
||||
display: none;
|
||||
margin-top: 3px;
|
||||
form.index-search-form > button > label {
|
||||
font-weight: 500;
|
||||
top: 15px;
|
||||
position: relative;
|
||||
font-size: 1.5em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
@ -77,65 +50,59 @@ form.index-search-form > button > span {
|
||||
}
|
||||
|
||||
section.index-meta article {
|
||||
padding-block: 30px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
table.index-meta {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
table.index-meta tr + tr > * {
|
||||
padding-block-start: 10px;
|
||||
}
|
||||
|
||||
table.index-meta :is(th, td) {
|
||||
padding: 0;
|
||||
div.index-meta {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
gap: 10px;
|
||||
width: 98%;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
table.index-meta th {
|
||||
color: var(--secondary);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.index-meta td {
|
||||
padding-inline-start: 10px;
|
||||
div.index-meta > label:nth-child(2),
|
||||
div.index-meta > label:nth-child(4),
|
||||
div.index-meta > label:nth-child(6) {
|
||||
color: var(--white);
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
form.index-search-form > input[type="text"] {
|
||||
width: 85%;
|
||||
height: 50px;
|
||||
font: 34px var(--base-type);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 650px) {
|
||||
form.index-search-form > input[type="text"] {
|
||||
width: 80%;
|
||||
height: 50px;
|
||||
font: 34px var(--base-type);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
form.index-search-form {
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
form.index-search-form > input[type="text"] {
|
||||
width: 99%;
|
||||
height: 50px;
|
||||
font: 27px var(--base-type);
|
||||
}
|
||||
|
||||
form.index-search-form > input[type="text"],
|
||||
form.index-search-form > button {
|
||||
width: 100%;
|
||||
width: 99%;
|
||||
top: 15px;
|
||||
}
|
||||
|
||||
form.index-search-form > button {
|
||||
grid-template-columns: auto 60px;
|
||||
form.index-search-form > button > label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
form.index-search-form > button span {
|
||||
display: block;
|
||||
form.index-search-form > button > img#search-icon {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ a.list-link > .item-block > .item-icon {
|
||||
a.list-link {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr auto 30px 30px;
|
||||
grid-template-rows: 100% 100px 30px 30px;
|
||||
gap: 5px;
|
||||
height: auto;
|
||||
padding-bottom: 20px;
|
||||
|
@ -1,4 +1,3 @@
|
||||
@import "../global/utilities.css";
|
||||
@import "../global/colors.css";
|
||||
@import "../global/forms.css";
|
||||
@import "../global/typography.css";
|
||||
|
@ -8,25 +8,16 @@ input[type="text"] {
|
||||
display: inline-block;
|
||||
background: var(--white);
|
||||
color: var(--primary);
|
||||
transition: 0.2s linear;
|
||||
transition-property: color, background-color;
|
||||
transition: all 0.2s linear;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus {
|
||||
outline: solid var(--highlight);
|
||||
background-color: var(--highlight);
|
||||
}
|
||||
|
||||
/* TODO: generalise this a bit */
|
||||
button:focus,
|
||||
input[type="submit"]:focus,
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus {
|
||||
outline: 2px solid var(--white);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
@ -43,10 +34,8 @@ input[type="submit"] {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
transition: 0.3s linear;
|
||||
transition-property: color, background-color;
|
||||
transition: all 0.3s linear;
|
||||
height: 35px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
select {
|
||||
@ -56,5 +45,4 @@ select {
|
||||
appearance: none;
|
||||
color: var(--primary);
|
||||
background: var(--secondary);
|
||||
height: 35px;
|
||||
}
|
||||
|
@ -199,7 +199,9 @@ footer {
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
width: 80%;
|
||||
margin: 20px auto;
|
||||
max-width: 1000px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -211,15 +213,6 @@ footer > div:nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/*
|
||||
member stuff
|
||||
*/
|
||||
|
||||
.your-avatar {
|
||||
width: 250px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/*
|
||||
responsive
|
||||
*/
|
||||
|
@ -67,11 +67,6 @@ h3 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h3.strong {
|
||||
color: var(--secondary);
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Utilities
|
||||
*/
|
||||
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
@ -1,4 +1,8 @@
|
||||
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" id="search">
|
||||
<path fill="none" stroke="currentcolor" stroke-width="8.777778" stroke-linecap="round" stroke-linejoin="round" d="M 11 41.722221 C 11 58.68964 24.754808 72.444443 41.722221 72.444443 C 58.68964 72.444443 72.444443 58.68964 72.444443 41.722221 C 72.444443 24.754807 58.68964 11 41.722221 11 C 24.754808 11 11 24.754807 11 41.722221"/>
|
||||
<path fill="none" stroke="currentcolor" stroke-width="8.777778" stroke-linecap="round" stroke-linejoin="round" d="M 90 90 L 63.666668 63.666668"/>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated by Pixelmator Pro 3.3.8 -->
|
||||
<svg width="101" height="100" viewBox="0 0 101 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group-copy">
|
||||
<path id="Path-copy-2" fill="none" stroke="#efebe3" stroke-width="8.777778" stroke-linecap="round" stroke-linejoin="round" d="M 11 41.722221 C 11 58.68964 24.754808 72.444443 41.722221 72.444443 C 58.68964 72.444443 72.444443 58.68964 72.444443 41.722221 C 72.444443 24.754807 58.68964 11 41.722221 11 C 24.754808 11 11 24.754807 11 41.722221"/>
|
||||
<path id="Path-copy" fill="none" stroke="#efebe3" stroke-width="8.777778" stroke-linecap="round" stroke-linejoin="round" d="M 90 90 L 63.666668 63.666668"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 571 B After Width: | Height: | Size: 729 B |
@ -11,10 +11,8 @@
|
||||
@csrf
|
||||
<label>Edit Location Name</label><br>
|
||||
<input type="text" name="name" value="{{$location->name}}" /><br>
|
||||
|
||||
<label>Edit Location Notes</label><br>
|
||||
<textarea name="notes">{{$location->notes}}</textarea><br>
|
||||
|
||||
<label>Edit Location Comments</label><br>
|
||||
<textarea name="description">{{$location->description}}</textarea><br>
|
||||
<label>Edit Reference Links (comma seperated)</label><br>
|
||||
<textarea name="archive_links">{{$location->archive_links}}</textarea><br>
|
||||
<label>Edit Reference Images</label><br>
|
||||
@ -26,7 +24,7 @@
|
||||
<h3>Images</h3>
|
||||
@if($images != null)
|
||||
@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>
|
||||
@endforeach
|
||||
@endif
|
||||
|
@ -2,51 +2,12 @@
|
||||
|
||||
@section('title', 'Den | Member Admin')
|
||||
|
||||
@php
|
||||
switch($mode)
|
||||
{
|
||||
case 'member-create':
|
||||
$action_url = '/den/member/create';
|
||||
break;
|
||||
case 'member-edit':
|
||||
$action_url = '/den/member/edit';
|
||||
break;
|
||||
case 'admin-create':
|
||||
$action_url = '/den/member/admin-create';
|
||||
break;
|
||||
}
|
||||
@endphp
|
||||
@section('main-content')
|
||||
<section>
|
||||
<article>
|
||||
@switch($mode)
|
||||
@case('member-edit')
|
||||
<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
|
||||
|
||||
@case('admin-create')
|
||||
<h2>Make your first account</h2>
|
||||
*This will be your administrator account.
|
||||
@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
|
||||
<a href="/den/admin/update">UPDATE LOCATIONS</a><br />
|
||||
<a href="/den/admin/compile">COMPILE LOCATIONS</a>
|
||||
</article>
|
||||
</section>
|
||||
@endsection
|
@ -1,13 +0,0 @@
|
||||
@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
|
@ -1,42 +0,0 @@
|
||||
@extends('frame')
|
||||
|
||||
@section('title', 'Den | Sources Admin')
|
||||
|
||||
@php
|
||||
switch($mode)
|
||||
{
|
||||
case 'source-create':
|
||||
$action_url = '/den/source/create';
|
||||
break;
|
||||
case 'source-edit':
|
||||
$action_url = '/den/source/edit';
|
||||
break;
|
||||
}
|
||||
@endphp
|
||||
@section('main-content')
|
||||
<section>
|
||||
<article>
|
||||
@switch($mode)
|
||||
@case('source-edit')
|
||||
<h2>Edit Info for {{$source->url}}</h2>
|
||||
@include('forms.source-edit')
|
||||
<br />
|
||||
@break
|
||||
|
||||
@case('source-create')
|
||||
<h2>New Source Info</h2>
|
||||
@include('forms.source-edit')
|
||||
<br />
|
||||
@break
|
||||
|
||||
@default
|
||||
<h2>Current Sources </h2>
|
||||
@foreach($sources as $source)
|
||||
<a href="/den/source/{{$source->id}}">{{$source->url}}</a><br />
|
||||
@endforeach
|
||||
<h2>Add Source </h2>
|
||||
<a href="/den/source/edit/create">Add a new Source</a><br />
|
||||
@endswitch
|
||||
</article>
|
||||
</section>
|
||||
@endsection
|
@ -8,10 +8,9 @@
|
||||
<article>
|
||||
<h2>Hey {{$handle}} </h2>
|
||||
<a href="/den/you">Edit Your Account</a><br />
|
||||
@if($role==0)
|
||||
<a href="/den/locations">Manage Locations</a><br />
|
||||
@if($role==1)
|
||||
<a href="/den/member">Manage Members</a><br />
|
||||
<a href="/den/sources">Manage Sources</a><br />
|
||||
@endif
|
||||
</article>
|
||||
</section>
|
||||
|
@ -1,74 +0,0 @@
|
||||
<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;
|
||||
//for creation of initial admin account
|
||||
if($mode == 'admin-create')
|
||||
{
|
||||
$role = 0;
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if($mode != 'admin-create')
|
||||
<label>Role</label><br />
|
||||
<input type="text" name="role" value="{{$role}}" />
|
||||
<br />
|
||||
@endif
|
||||
|
||||
@if($mode == 'member-create' || $mode == 'admin-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
|
||||
|
||||
@if($mode != 'admin-create')
|
||||
<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 />
|
||||
@endif
|
||||
|
||||
</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>
|
@ -1,30 +0,0 @@
|
||||
<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>
|
@ -1,66 +0,0 @@
|
||||
<form action="{{$action_url}}" method="post" enctype="multipart/form-data">
|
||||
<div>
|
||||
@php
|
||||
isset($source->url) ? $url = $source->url : $url = '';
|
||||
@endphp
|
||||
<label>URL</label><br />
|
||||
<input type="text" name="url" value="{{$url}}" />
|
||||
<br />
|
||||
@php
|
||||
isset($source->type) ? $type = $source->type : $type = '';
|
||||
@endphp
|
||||
<label>Type</label><br />
|
||||
<select name="type">
|
||||
@if($type == 'mastodon')
|
||||
<option value="mastodon" selected>Mastodon</option>
|
||||
<option value="gotosocial">GoToSocial</option>
|
||||
@else
|
||||
<option value="mastodon">Mastodon</option>
|
||||
<option value="gotosocial" selected>GoToSocial</option>
|
||||
@endif
|
||||
</select>
|
||||
<br />
|
||||
@php
|
||||
isset($source->format) ? $format = $source->format : $format = '';
|
||||
@endphp
|
||||
<label>Format</label><br />
|
||||
<select name="format">
|
||||
@if($format == 'json')
|
||||
<option value="json" selected>JSON</option>
|
||||
<option value="csv">CSV</option>
|
||||
@else
|
||||
<option value="json">JSON</option>
|
||||
<option value="csv" selected>CSV</option>
|
||||
@endif
|
||||
</select>
|
||||
<br />
|
||||
|
||||
@php
|
||||
isset($source->active) ? $status = $source->active : $status = false;
|
||||
@endphp
|
||||
<label>Active?</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 />
|
||||
@php
|
||||
isset($source->token) ? $token = $source->token : $token = '';
|
||||
@endphp
|
||||
<label>Access Token (enter 'none' to clear)</label><br />
|
||||
<input type="text" name="token" value="{{$token}}" />
|
||||
<br />
|
||||
|
||||
</div>
|
||||
@csrf
|
||||
@php
|
||||
isset($source->id) ? $id = $source->id : $id = 0;
|
||||
@endphp
|
||||
<input type="hidden" name="id" value="{{$id}}" />
|
||||
<input type="submit" value="Edit Source" name="submit_button">
|
||||
</form>
|
@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="theme-color" content="#c3639e" />
|
||||
<meta name="theme-color" content="#d66365" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
@yield('title')
|
||||
@ -17,14 +17,12 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
0
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<div>
|
||||
<div class="header-left">
|
||||
<a href="/">
|
||||
<img src="/assets/images/global/logo-dark.svg" alt="The Bad Space" />
|
||||
<img src="/assets/images/global/logo-dark.svg" title="bad-space-logo" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
@ -90,13 +88,14 @@
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
The Bad Space © <?php echo date("Y"); ?><br />
|
||||
The Bad Space © 2024<br />
|
||||
an <a href="https://h-i.works">h.i.</a> project
|
||||
</div>
|
||||
<div>
|
||||
a0.7
|
||||
a0.6
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -5,60 +5,67 @@
|
||||
<section>
|
||||
<article>
|
||||
<h2 id="what">What is The Bad Space?</h2>
|
||||
<p>The Bad Space arose from a need to identify instances that house bad actors, are poorly moderated, and/or contain inappropriate/offensive content (CSAM, hate speech, fascist ideology, etc.) that puts marginalized communities at risk.</p>
|
||||
<p>It is an extension of the <strong>#fediblock</strong> hashtag - orginally created by <a href="https://www.artistmarciax.com/">Artist Marcia X</a> with additional support from <a href="https://digital.rooting.garden">Ginger</a> to provide a catalog of instances that seek to cause harm and reduce the quality of experience in the fediverse.</p>
|
||||
<p>The searchable online catalog is built and maintained by <a href="https://roiskinda.cool/profile.html">Ro</a>. The repo can be found <a href="https://koodu.h-i.works/projects/thebadspace">here</a>.</p>
|
||||
<p>The Bad Space arose from a need to identify instances that house bad actors, are poorly moderated, and/or contain inappropriate/offensive content (CSAM, hate speech, fascist ideology, etc.) that puts marginalized communities at risk.
|
||||
</p>
|
||||
<p>
|
||||
It is an extension of the
|
||||
<strong>#fediblock</strong>
|
||||
hashtag - orginally created by
|
||||
<a href="https://www.artistmarciax.com/">Artist Marcia X</a>
|
||||
with additional support from
|
||||
<a href="https://digital.rooting.garden">Ginger</a>
|
||||
to provide a catalog of instances that seek to cause harm and reduce the quality of experience in the fediverse.
|
||||
</p>
|
||||
<p>
|
||||
The searchable online catalog is built and maintained by
|
||||
<a href="https://roiskinda.cool/profile.html">Ro</a>. The repo can be found <a href="https://koodu.h-i.works/projects/thebadspace">here</a>.
|
||||
</p>
|
||||
<p>Custom silence and suspend icons graciously provided by <a href="https://rage.love/@puf">puf</a>.</p>
|
||||
|
||||
<h2 id="how">How does it work?</h2>
|
||||
<p>The Bad Space is a collaboration of communities, referred to as Current Sources, committed to actively moderating against racism, sexism, heterosexism, transphobia, ableism, casteism, or religion.</p>
|
||||
|
||||
<p>These communities have permitted The Bad Space to ingest their respective blocklists detailing their silences and suspension to create a composite directory of sites that engage in the behavior(s) listed in the section above. For each behavior, the directory of locations can be searched and, through The Bad Space's public API, integrated into external services.</p>
|
||||
|
||||
<h2>Adding Locations</h2>
|
||||
<p>Current Sources continually review the #fediblock hashtag and update their silences and suspensions when warranted. If an instance meets the criteria of a Current Source to be suspended or silenced, The Bad Space will automatically be updated according to said Current Sources' curated data.</p>
|
||||
<p>For an instance to be listed on The Bad Space, at least two (2) Current Sources must have that location silenced and/or suspended. Instances will not display in the directory until two (2) Current Sources have taken moderation action against them.</p>
|
||||
<p>
|
||||
Current Sources continually review the #fediblock hashtag and update their silences and suspensions when warranted. If an instance meets the criteria of a Current Source to be suspended or silenced, The Bad Space will automatically be updated according to said Current Sources' curated data.
|
||||
|
||||
For an instance to be listed on The Bad Space, at least two (2) Current Sources must have that location silenced and/or suspended. Instances will not display in the directory until two (2) Current Sources have taken moderation action against them.
|
||||
</p>
|
||||
<h2>Removing Locations</h2>
|
||||
<p>Locations that are displayed in The Bad Space may petition to be removed from the catalog by sending an appeal request to The Bad Space. The appeal process is outlined <a href="/appeals">here</a>.</p>
|
||||
Locations that are displayed in The Bad Space may petition to be removed from the catalog by sending an appeal request to The Bad Space. The appeal process is outlined <a href="/appeals">here</a>.
|
||||
|
||||
<h3 class="strong">Current Sources:</h3>
|
||||
|
||||
<h4>Maston:</h4>
|
||||
<ul>
|
||||
<p><strong>Current Sources:</strong></p>
|
||||
Maston:<br />
|
||||
@foreach($sources as $source)
|
||||
@if($source->format == 'json')
|
||||
<li><a href="https://{{$source->url}}">{{$source->url}}</a></li>
|
||||
@else
|
||||
<li>None</li>
|
||||
<a href="https://{{$source->url}}">{{$source->url}}</a><br />
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<h4>Custom CSV</h4>
|
||||
<ul>
|
||||
Custom CSV<br />
|
||||
@foreach($sources as $source)
|
||||
@if($source->format == 'csv')
|
||||
<li><a href="{{$source->url}}">{{$source->url}}</a></li>
|
||||
@else
|
||||
<!--
|
||||
<li>None</li>
|
||||
-->
|
||||
<a href="{{$source->url}}">{{$source->url}}</a><br />
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<h2>How do I use it?</h2>
|
||||
<p>The Bad Space is meant to be a resource for anyone looking to improve the quality of their online experience by creating a tool that catalogs sources for harassment and abuse. There are several options for how it can be used.</p>
|
||||
|
||||
<p>
|
||||
The Bad Space is meant to be a resource for anyone looking to improve the quality of their online experience by creating a tool that catalogs sources for harassment and abuse. There are several options for how it can be used.
|
||||
<h3>Search</h3>
|
||||
<p>To see if a site is listed in the database, use the <a href="/">search feature</a> to search for that URL. If it is in the database, information for that instance will be returned and associated instances if applicable.</p>
|
||||
|
||||
To see if a site is listed in the database, use the
|
||||
<a href="/">search feature</a>
|
||||
to search for that URL. If it is in the database, information for that instance will be returned and associated instances if applicable.
|
||||
<h3>CSV Exports</h3>
|
||||
<p>For a list of the current locations being tracked, click on one of the links below to download a dynamically generated CSV file that can be consumed as a blocklist. More formats will be added over time.</p>
|
||||
<p><a href="/exports">Exports</a></p>
|
||||
|
||||
For a list of the current locations being tracked, click on one of the links below to download a dynamically generated CSV file that can be consumed as a blocklist. More formats will be added over time.
|
||||
<br />
|
||||
<a href="/exports/mastodon">For Mastodon</a>
|
||||
<h3>API</h3>
|
||||
<p>The Bad Space has a public api that can be used to search the database programatically and return results in the JSON format. The API can be accsess at <code>https://thebad.space/api/v1/search</code> by posting a JSON object with the following format: <code>{"url":"search.url"}</code>. Data from API request will be returned in the follow format:</p>
|
||||
The Bad Space has a public api that can be used to search the database programatically and return results in the JSON format. The API can be accsess at<br />
|
||||
<code>https://thebad.space/api/v1/search</code>
|
||||
by posting a JSON object with the following format:
|
||||
<code>{"url":"search.url"}</code><br />
|
||||
Data from API request will be returned in the follow format:<br />
|
||||
|
||||
<pre>
|
||||
<code>{
|
||||
@ -76,6 +83,7 @@
|
||||
}
|
||||
}</code>
|
||||
</pre>
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
@endsection
|
@ -5,11 +5,9 @@
|
||||
<section class="index-search">
|
||||
<form class="index-search-form" action="/search" method="post" enctype="multipart/form-data">
|
||||
<input type="text" name="index_search" value="" placeholder="Hi! This is where you search." />
|
||||
<button aria-labelledby="search-label">
|
||||
<span id="search-label">Look for it</span>
|
||||
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="trye">
|
||||
<use href="assets/images/global/icon-search.svg#search" />
|
||||
</svg>
|
||||
<button aria-label="search-button">
|
||||
<label id="search-label">LOOK FOR IT</label>
|
||||
<img id="search-icon" class="button-icon" src="assets/images/global/icon-search.svg" />
|
||||
</button>
|
||||
@csrf
|
||||
</form>
|
||||
@ -40,21 +38,33 @@
|
||||
@endisset
|
||||
<section class="index-meta">
|
||||
<article>
|
||||
<table class="index-meta">
|
||||
<caption class="visually-hidden">Meta</caption>
|
||||
<tr>
|
||||
<th>Active Locations Tracked</th>
|
||||
<td>{{$count}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total Sources</th>
|
||||
<td>{{$sources}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Update</th>
|
||||
<td>{{$latest_date}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<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">
|
||||
<label>Locations Tracked</label>
|
||||
<label>{{$count}}</label>
|
||||
<label>Total Sources</label>
|
||||
<label>{{$sources}}</label>
|
||||
<label>Latest Update</label>
|
||||
<label>{{$latest_date}}</label>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
@endsection
|
@ -7,7 +7,7 @@
|
||||
<h2>Page {{$pageNum}}</h2>
|
||||
<a href="/listings/{{$prev}}">PREV</a>
|
||||
{{$pageNum}} of {{$totalPages}}
|
||||
<a href="/listings/{{$next}}">NEXT</a>
|
||||
<a href="/listings/{{$next}}">NEXT</a><br /><br />
|
||||
@foreach($locations as $location)
|
||||
@php
|
||||
$action = $location->block_count + $location->silence_count;
|
||||
@ -26,10 +26,10 @@
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
<br />
|
||||
<a href="/listings/{{$prev}}">PREV</a>
|
||||
{{$pageNum}} of {{$totalPages}}
|
||||
<a href="/listings/{{$next}}">NEXT</a>
|
||||
<br /><br />
|
||||
</article>
|
||||
</section>
|
||||
@endsection
|
@ -6,19 +6,13 @@
|
||||
@parent
|
||||
<section>
|
||||
<article>
|
||||
<h2>Public Comments</h2>
|
||||
@foreach($comments as $comment)
|
||||
@if($comment != " " && $comment != '')
|
||||
{{trim($comment)}}<br /><br />
|
||||
@endif
|
||||
@endforeach
|
||||
<h2>Notes</h2>
|
||||
{{$location->notes}}
|
||||
<h2>Description</h2>
|
||||
{{$location->description}}<br />
|
||||
<h2>References</h2>
|
||||
<h3>Images</h3>
|
||||
@if($images != null)
|
||||
@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>
|
||||
@endforeach
|
||||
@endif
|
||||
@ -27,9 +21,6 @@
|
||||
$rating = floor(($action / $sources_count)*100);
|
||||
@endphp
|
||||
<h3>Links</h3>
|
||||
@foreach($links as $link)
|
||||
<a href="{{$link}}">{{$link}}</a><br />
|
||||
@endforeach
|
||||
<div class="location-rating">
|
||||
<div>
|
||||
<img class="rating-icon" src="/assets/images/global/heat.svg" title="heat-rating" />
|
||||
@ -56,11 +47,8 @@
|
||||
|
||||
<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.
|
||||
<br />
|
||||
<br />UPDATED : {{$updated}}
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<br />UPDATED : {{$updated}}
|
||||
</article>
|
||||
</section>
|
||||
@endsection
|
@ -5,10 +5,8 @@ use App\Http\Controllers\FrontIndexController;
|
||||
use App\Http\Controllers\AuthController;
|
||||
use App\Http\Controllers\DenController;
|
||||
use App\Http\Controllers\LocationController;
|
||||
use App\Http\Controllers\MemberController;
|
||||
use App\Http\Controllers\ExportController;
|
||||
use App\Http\Controllers\AppealController;
|
||||
use App\Http\Controllers\SourceController;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@ -29,7 +27,6 @@ Route::get("/location/{uuid}", [FrontIndexController::class, 'location']);
|
||||
Route::get("/appeals", [FrontIndexController::class, 'appeals']);
|
||||
Route::post("/search", [FrontIndexController::class, 'indexSearch']);
|
||||
Route::post("/appeal", [AppealController::class, 'sendAppeal']);
|
||||
Route::post("/den/member/admin-create", [MemberController::class, 'adminCreate']);
|
||||
|
||||
//exports
|
||||
Route::get("/exports", [ExportController::class, 'exportIndex']);
|
||||
@ -43,6 +40,7 @@ Route::get("/logout", [AuthController::class, 'leave']);
|
||||
//back
|
||||
Route::group(['prefix' => 'den', 'middleware' => 'member.check'], function () {
|
||||
Route::get("/", [DenController::class, 'start']);
|
||||
Route::get("/member", [DenController::class, 'member']);
|
||||
Route::get("/listings/{pageNum}", [DenController::class, 'location']);
|
||||
Route::get("/location/edit/{uuid}", [DenController::class, 'locationEdit']);
|
||||
Route::get("/locations", [DenController::class, 'locations']);
|
||||
@ -50,19 +48,4 @@ Route::group(['prefix' => 'den', 'middleware' => 'member.check'], function () {
|
||||
Route::post("/locations/edit", [LocationController::class, 'editLocation']);
|
||||
Route::get("/admin/update", [LocationController::class, 'updateLocations']);
|
||||
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']);
|
||||
//source stuff
|
||||
Route::get("/sources", [SourceController::class, 'index']);
|
||||
Route::get("/source/{id}", [SourceController::class, 'editSource']);
|
||||
Route::get("/source/edit/create", [SourceController::class, 'createSource']);
|
||||
//actions
|
||||
Route::post("/profile/edit", [MemberController::class, 'profileEdit']);
|
||||
Route::post("/member/edit", [MemberController::class, 'memberEdit']);
|
||||
Route::post("/member/create", [MemberController::class, 'memberCreate']);
|
||||
Route::post("/source/edit", [SourceController::class, 'sourceEdit']);
|
||||
Route::post("/source/create", [SourceController::class, 'sourceCreate']);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user