mirror of
https://koodu.h-i.works/projects/thebadspace
synced 2025-06-30 16:07:37 -05:00
Compare commits
34 Commits
a0.7
...
moiety/fav
Author | SHA1 | Date | |
---|---|---|---|
4c3f97cc8a | |||
d310cf4fb9 | |||
fed8748923 | |||
04ac27ea04 | |||
4dafad447d | |||
d165d0c2bd | |||
3e070676e4 | |||
c9c3b88f4d | |||
66b43b2d4e | |||
c2be80c005 | |||
7cdd284968 | |||
7f7f324163 | |||
67d40bd166 | |||
aeaca3e0f0 | |||
0ad5208638 | |||
8ed702bd59 | |||
a0fee9b6b1 | |||
cf11d246d1 | |||
2c88d45e52 | |||
5515503117 | |||
0f16b275a0 | |||
d138198305 | |||
c0e05c13e6 | |||
dbea3ff219 | |||
1420d61892 | |||
bfce3c82ec | |||
3e937c5083 | |||
b7e2bbb334 | |||
56f445572f | |||
515de4c56b | |||
73a0abf898 | |||
07793a413a | |||
8fbf927f2d | |||
8ce9a7744a |
@ -55,20 +55,15 @@ return $config
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'no_multiline_whitespace_around_double_arrow' => true,
|
'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_before_comma_in_array' => true,
|
||||||
'no_whitespace_in_blank_line' => true,
|
'no_whitespace_in_blank_line' => true,
|
||||||
'object_operator_without_whitespace' => true,
|
'object_operator_without_whitespace' => true,
|
||||||
'single_blank_line_before_namespace' => true,
|
|
||||||
'ternary_operator_spaces' => true,
|
'ternary_operator_spaces' => true,
|
||||||
'trim_array_spaces' => true,
|
'trim_array_spaces' => true,
|
||||||
'unary_operator_spaces' => true,
|
'unary_operator_spaces' => true,
|
||||||
'whitespace_after_comma_in_array' => true,
|
'whitespace_after_comma_in_array' => true,
|
||||||
'single_line_after_imports' => true,
|
'single_line_after_imports' => true,
|
||||||
'ordered_imports' => [
|
//'single_blank_line_before_namespace' => true, php fixer doesn't like this rule?
|
||||||
'sort_algorithm' => 'none',
|
|
||||||
],
|
|
||||||
//Other rules here...
|
//Other rules here...
|
||||||
])
|
])
|
||||||
->setLineEnding("\n");
|
->setLineEnding("\n");
|
||||||
|
88
README.md
88
README.md
@ -1,9 +1,91 @@
|
|||||||
# The Bad Space
|
# The Bad Space
|
||||||
|
|
||||||
A searcable catalog of the worst places on the web.
|
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.
|
||||||
|
|
||||||
More features incoming
|
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.
|
||||||
|
|
||||||
An Hi Project =)
|
## 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.
|
||||||
|
|
||||||
|
*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.
|
||||||
|
@ -6,34 +6,50 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use App\Repositories\LocationRepository;
|
use App\Repositories\LocationRepository;
|
||||||
use App\Repositories\SourceRepository;
|
use App\Repositories\SourceRepository;
|
||||||
|
use App\Repositories\MemberRepository;
|
||||||
use App\Services\PaginationService;
|
use App\Services\PaginationService;
|
||||||
|
|
||||||
class FrontIndexController extends Controller
|
class FrontIndexController extends Controller
|
||||||
{
|
{
|
||||||
protected $location;
|
protected $location;
|
||||||
protected $source;
|
protected $source;
|
||||||
|
protected $member;
|
||||||
protected $pagination;
|
protected $pagination;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
LocationRepository $locationRepository,
|
LocationRepository $locationRepository,
|
||||||
SourceRepository $sourceRepository,
|
SourceRepository $sourceRepository,
|
||||||
|
MemberRepository $memberRepository,
|
||||||
PaginationService $paginationService
|
PaginationService $paginationService
|
||||||
) {
|
) {
|
||||||
$this->location = $locationRepository;
|
$this->location = $locationRepository;
|
||||||
$this->source = $sourceRepository;
|
$this->source = $sourceRepository;
|
||||||
|
$this->member = $memberRepository;
|
||||||
$this->pagination = $paginationService;
|
$this->pagination = $paginationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start()
|
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', [
|
return view('front.index', [
|
||||||
'count' => count($this->location->getActiveLocations()),
|
'count' => count($this->location->getActiveLocations()),
|
||||||
'sources' => count($this->source->getActive()),
|
'sources' => count($this->source->getActive()),
|
||||||
'recent' => $this->location->getRecent(),
|
'recent' => $this->location->getRecent(),
|
||||||
'latest_date' => $this->location->getRecent()[0]->updated_at->format('Y M d'),
|
'latest_date' => $latest_update,
|
||||||
'title' => "The Bad Space"
|
'title' => "The Bad Space"
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function indexSearch(Request $request)
|
public function indexSearch(Request $request)
|
||||||
{
|
{
|
||||||
|
@ -151,4 +151,32 @@ class MemberController extends Controller
|
|||||||
return back()->withErrors(['Nah, you can\'t do this. Wrong permissions.']);
|
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.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
102
app/Http/Controllers/SourceController.php
Normal file
102
app/Http/Controllers/SourceController.php
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?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.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -38,4 +38,13 @@ class Location extends Model
|
|||||||
"archive_links",
|
"archive_links",
|
||||||
"notes",
|
"notes",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ class LocationRepository
|
|||||||
$rawSearch = $terms;
|
$rawSearch = $terms;
|
||||||
$terms = str_replace(",", "", $terms);
|
$terms = str_replace(",", "", $terms);
|
||||||
$terms = str_replace(" ", "|", $terms);
|
$terms = str_replace(" ", "|", $terms);
|
||||||
$raw = DB::select("SELECT * FROM searchlocations(?)", [$terms]);
|
//$raw = DB::select("SELECT * FROM searchlocations(?)", [$terms]);
|
||||||
|
$raw = Location::search($terms)->get();
|
||||||
$results = [];
|
$results = [];
|
||||||
foreach ($raw as $item) {
|
foreach ($raw as $item) {
|
||||||
if (($item->block_count + $item->silence_count) >= 2) {
|
if (($item->block_count + $item->silence_count) >= 2) {
|
||||||
|
@ -61,12 +61,15 @@ class MemberRepository
|
|||||||
public function add($request)
|
public function add($request)
|
||||||
{
|
{
|
||||||
$password = [];
|
$password = [];
|
||||||
|
$newFriend = [];
|
||||||
if ($request->fresh_pass === $request->fresh_pass_confirm) {
|
if ($request->fresh_pass === $request->fresh_pass_confirm) {
|
||||||
$password = Hash::make($request->fresh_pass);
|
$password = Hash::make($request->fresh_pass);
|
||||||
} else {
|
} else {
|
||||||
return ['status' => false, 'message' => "Passwords Do Not Match"];
|
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([
|
$newFriend = $this->model::create([
|
||||||
'uuid' => Uuid::uuid4(),
|
'uuid' => Uuid::uuid4(),
|
||||||
'avatar' => 'default-member-avatar',
|
'avatar' => 'default-member-avatar',
|
||||||
@ -79,6 +82,21 @@ class MemberRepository
|
|||||||
'created_at' => Carbon::now(),
|
'created_at' => Carbon::now(),
|
||||||
'last_login' => 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) {
|
if ($newFriend) {
|
||||||
return ['status' => true, 'message' => "New Friend Made!"];
|
return ['status' => true, 'message' => "New Friend Made!"];
|
||||||
|
@ -21,11 +21,61 @@ class SourceRepository
|
|||||||
$this->sources = $source::where("active", true)->get();
|
$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()
|
public function getActive()
|
||||||
{
|
{
|
||||||
return $this->source::where("active", true)->get();
|
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($index = 0)
|
||||||
{
|
{
|
||||||
//checks all the sources to refresh data
|
//checks all the sources to refresh data
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "project/thebadspace",
|
"name": "project/thebadspace",
|
||||||
"type": "moderation and safety",
|
"type": "moderation",
|
||||||
"descriptions": "A tool for improving independent social media curation.",
|
"description": "A tool for improving independent social media curation",
|
||||||
"version": "0.7-alpha",
|
"version": "0.7-alpha",
|
||||||
"keywords": ["thebadspace","tbs","activty-pub","laravel", "framework", "moderation", "safety", "curation", "tooling", "fediverse"],
|
"keywords": [
|
||||||
|
"thebadspace",
|
||||||
|
"tbs",
|
||||||
|
"activty-pub",
|
||||||
|
"laravel",
|
||||||
|
"framework",
|
||||||
|
"moderation",
|
||||||
|
"safety",
|
||||||
|
"curation",
|
||||||
|
"tooling",
|
||||||
|
"fediverse"
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"GPL-3.0-only"
|
||||||
|
],
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Ro",
|
"name": "Ro",
|
||||||
@ -15,13 +29,21 @@
|
|||||||
"wiki": "https://koodu.h-i.works/projects/thebadspace/wiki/?action=_pages",
|
"wiki": "https://koodu.h-i.works/projects/thebadspace/wiki/?action=_pages",
|
||||||
"issues": "https://koodu.h-i.works/projects/thebadspace/issues"
|
"issues": "https://koodu.h-i.works/projects/thebadspace/issues"
|
||||||
},
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.2",
|
||||||
|
"guzzlehttp/guzzle": "^7.2",
|
||||||
|
"laravel/framework": "^12.0",
|
||||||
|
"laravel/sanctum": "^4.0",
|
||||||
|
"laravel/tinker": "^2.8",
|
||||||
|
"revolution/laravel-mastodon-api": "^3.0"
|
||||||
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
"laravel/pint": "^1.0",
|
"laravel/pint": "^1.0",
|
||||||
"laravel/sail": "^1.18",
|
"laravel/sail": "^1.18",
|
||||||
"mockery/mockery": "^1.4.4",
|
"mockery/mockery": "^1.4.4",
|
||||||
"nunomaduro/collision": "^8.1",
|
"nunomaduro/collision": "^8.1",
|
||||||
"phpunit/phpunit": "^10.1",
|
"phpunit/phpunit": "^11.0",
|
||||||
"spatie/laravel-ignition": "^2.0"
|
"spatie/laravel-ignition": "^2.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -49,11 +71,17 @@
|
|||||||
],
|
],
|
||||||
"post-create-project-cmd": [
|
"post-create-project-cmd": [
|
||||||
"@php artisan key:generate --ansi"
|
"@php artisan key:generate --ansi"
|
||||||
|
],
|
||||||
|
"post-install-cmd": [
|
||||||
|
"php -r \"copy('.env.example', '.env');\"",
|
||||||
|
"php artisan key:generate"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"laravel": {
|
"laravel": {
|
||||||
"dont-discover": []
|
"dont-discover": [
|
||||||
|
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
2162
composer.lock
generated
2162
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,32 +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('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');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,28 +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('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');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,32 +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('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');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,33 +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('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');
|
|
||||||
}
|
|
||||||
};
|
|
@ -0,0 +1,57 @@
|
|||||||
|
<?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');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
<?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');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,34 @@
|
|||||||
|
<?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');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,35 @@
|
|||||||
|
<?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,6 +3,12 @@ section.index-search {
|
|||||||
background: var(--white);
|
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 {
|
section.start a.search-link {
|
||||||
color: var(--highlight);
|
color: var(--highlight);
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
@ -12,36 +18,57 @@ section.start a.search-link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form.index-search-form {
|
form.index-search-form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto max-content;
|
||||||
|
gap: 15px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 30px 0;
|
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"] {
|
form.index-search-form > input[type="text"] {
|
||||||
width: 88%;
|
|
||||||
height: 50px;
|
|
||||||
font: 44px var(--base-type);
|
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 {
|
form.index-search-form > button {
|
||||||
height: 60px;
|
display: grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
position: relative;
|
text-transform: uppercase;
|
||||||
top: 9px;
|
|
||||||
right: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form.index-search-form > button > img#search-icon {
|
form.index-search-form > button > svg {
|
||||||
float: none;
|
justify-self: center;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.index-search-form > button > label {
|
form.index-search-form > button > span {
|
||||||
font-weight: 500;
|
|
||||||
top: 15px;
|
|
||||||
position: relative;
|
|
||||||
font-size: 1.5em;
|
|
||||||
display: none;
|
display: none;
|
||||||
|
margin-top: 3px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
@ -50,59 +77,65 @@ form.index-search-form > button > label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
section.index-meta article {
|
section.index-meta article {
|
||||||
margin-top: 20px;
|
padding-block: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.index-meta {
|
table.index-meta {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 50% 50%;
|
|
||||||
gap: 10px;
|
|
||||||
width: 98%;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
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%;
|
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;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.index-meta th {
|
||||||
|
color: var(--secondary);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.index-meta td {
|
||||||
|
padding-inline-start: 10px;
|
||||||
|
color: var(--white);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 800px) {
|
@media only screen and (max-width: 800px) {
|
||||||
form.index-search-form > input[type="text"] {
|
form.index-search-form > input[type="text"] {
|
||||||
width: 85%;
|
|
||||||
height: 50px;
|
|
||||||
font: 34px var(--base-type);
|
font: 34px var(--base-type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 650px) {
|
@media only screen and (max-width: 650px) {
|
||||||
form.index-search-form > input[type="text"] {
|
form.index-search-form > input[type="text"] {
|
||||||
width: 80%;
|
|
||||||
height: 50px;
|
|
||||||
font: 34px var(--base-type);
|
font: 34px var(--base-type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 480px) {
|
@media only screen and (max-width: 480px) {
|
||||||
|
form.index-search-form {
|
||||||
|
grid-template-columns: auto;
|
||||||
|
}
|
||||||
form.index-search-form > input[type="text"] {
|
form.index-search-form > input[type="text"] {
|
||||||
width: 99%;
|
|
||||||
height: 50px;
|
|
||||||
font: 27px var(--base-type);
|
font: 27px var(--base-type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.index-search-form > input[type="text"],
|
||||||
form.index-search-form > button {
|
form.index-search-form > button {
|
||||||
width: 99%;
|
width: 100%;
|
||||||
top: 15px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form.index-search-form > button > label {
|
form.index-search-form > button {
|
||||||
display: inline;
|
grid-template-columns: auto 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.index-search-form > button > img#search-icon {
|
form.index-search-form > button span {
|
||||||
float: right;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ a.list-link > .item-block > .item-icon {
|
|||||||
a.list-link {
|
a.list-link {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||||
grid-template-rows: 100% 100px 30px 30px;
|
grid-template-rows: 1fr auto 30px 30px;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
height: auto;
|
height: auto;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
@import "../global/utilities.css";
|
||||||
@import "../global/colors.css";
|
@import "../global/colors.css";
|
||||||
@import "../global/forms.css";
|
@import "../global/forms.css";
|
||||||
@import "../global/typography.css";
|
@import "../global/typography.css";
|
||||||
|
@ -8,16 +8,25 @@ input[type="text"] {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: var(--white);
|
background: var(--white);
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
transition: all 0.2s linear;
|
transition: 0.2s linear;
|
||||||
|
transition-property: color, background-color;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"]:focus,
|
input[type="text"]:focus,
|
||||||
input[type="password"]:focus {
|
input[type="password"]:focus {
|
||||||
outline: solid var(--highlight);
|
|
||||||
background-color: 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 {
|
textarea {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@ -34,7 +43,8 @@ input[type="submit"] {
|
|||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 0;
|
border: 0;
|
||||||
transition: all 0.3s linear;
|
transition: 0.3s linear;
|
||||||
|
transition-property: color, background-color;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,11 @@ h3 {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3.strong {
|
||||||
|
color: var(--secondary);
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 800px) {
|
@media only screen and (max-width: 800px) {
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
|
13
public/assets/css/global/utilities.css
Normal file
13
public/assets/css/global/utilities.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
7
public/assets/images/global/favicon.svg
Normal file
7
public/assets/images/global/favicon.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||||
|
<style>
|
||||||
|
path{fill:#140d0d}
|
||||||
|
@media (prefers-color-scheme:dark){path{fill:#efded5}}
|
||||||
|
</style>
|
||||||
|
<path d="M486.93 74.2 460 101.15 433.07 74.2a254.03 254.03 0 0 0-358.86 0 254.03 254.03 0 0 0 0 358.86L101.14 460 74.2 486.93a254.03 254.03 0 0 0 0 358.86 254.03 254.03 0 0 0 358.86 0L460 818.86l26.93 26.93a254.03 254.03 0 0 0 358.86 0 254.03 254.03 0 0 0 0-358.86L818.86 460l26.93-26.93a254.03 254.03 0 0 0 0-358.86 254.03 254.03 0 0 0-358.86 0Zm-367.98 314a190.51 190.51 0 0 1 0-269.12 190.51 190.51 0 0 1 269.13 0l228.81 228.81-67.28 67.28-233.29-233.29a95.26 95.26 0 0 0-134.56 0 95.25 95.25 0 0 0 0 134.57l31.4 31.4-67.28 67.28-26.93-26.93Zm497.9-130.09 31.4-31.4a31.74 31.74 0 0 1 44.87 0 31.78 31.78 0 0 1 0 44.87l-31.4 31.4-44.87-44.87ZM504.7 459.96l-44.87 44.87-44.86-44.87 44.86-44.87 44.87 44.87ZM302.86 258.1l-44.87 44.87-31.4-31.4a31.74 31.74 0 0 1 44.86-44.87l31.4 31.4Zm85.22 542.77a190.52 190.52 0 0 1-269.13 0 190.5 190.5 0 0 1 0-269.12l228.82-228.82 67.28 67.28-233.3 233.3a95.25 95.25 0 0 0 0 134.56 95.26 95.26 0 0 0 134.57 0l31.4-31.4 67.29 67.27-26.93 26.93ZM257.98 616.9l44.88 44.87-31.4 31.4a31.78 31.78 0 0 1-44.87 0 31.74 31.74 0 0 1 0-44.86l31.4-31.4Zm542.78-85.22a190.51 190.51 0 0 1 0 269.13 190.51 190.51 0 0 1-269.13 0L302.98 572.07l67.28-67.28 233.3 233.29a95.25 95.25 0 0 0 134.56 0 95.26 95.26 0 0 0 0-134.57l-31.4-31.4L774 504.83l26.76 26.84Zm-184 130.1 44.88-44.88 31.4 31.4a31.77 31.77 0 0 1 0 44.88 31.78 31.78 0 0 1-44.87 0l-31.4-31.4Zm184-273.65L571.94 616.94l-67.28-67.29 233.3-233.29a95.26 95.26 0 0 0 0-134.56 95.26 95.26 0 0 0-134.57 0l-31.4 31.4-67.29-67.28L531.63 119a190.5 190.5 0 0 1 269.13 0 190.5 190.5 0 0 1 .17 269.21l-.17-.08Z" transform="translate(52 52)"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -1,8 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" id="search">
|
||||||
<!-- Generated by Pixelmator Pro 3.3.8 -->
|
<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"/>
|
||||||
<svg width="101" height="100" viewBox="0 0 101 100" xmlns="http://www.w3.org/2000/svg">
|
<path fill="none" stroke="currentcolor" stroke-width="8.777778" stroke-linecap="round" stroke-linejoin="round" d="M 90 90 L 63.666668 63.666668"/>
|
||||||
<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>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 729 B After Width: | Height: | Size: 571 B |
@ -3,11 +3,17 @@
|
|||||||
@section('title', 'Den | Member Admin')
|
@section('title', 'Den | Member Admin')
|
||||||
|
|
||||||
@php
|
@php
|
||||||
if($mode == 'member-create')
|
switch($mode)
|
||||||
{
|
{
|
||||||
|
case 'member-create':
|
||||||
$action_url = '/den/member/create';
|
$action_url = '/den/member/create';
|
||||||
}else{
|
break;
|
||||||
|
case 'member-edit':
|
||||||
$action_url = '/den/member/edit';
|
$action_url = '/den/member/edit';
|
||||||
|
break;
|
||||||
|
case 'admin-create':
|
||||||
|
$action_url = '/den/member/admin-create';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
@section('main-content')
|
@section('main-content')
|
||||||
@ -26,6 +32,13 @@
|
|||||||
<br />
|
<br />
|
||||||
@break
|
@break
|
||||||
|
|
||||||
|
@case('admin-create')
|
||||||
|
<h2>Make your first account</h2>
|
||||||
|
*This will be your administrator account.
|
||||||
|
@include('forms.member-edit')
|
||||||
|
<br />
|
||||||
|
@break
|
||||||
|
|
||||||
@default
|
@default
|
||||||
<h2>Member Listing </h2>
|
<h2>Member Listing </h2>
|
||||||
@foreach($members as $member)
|
@foreach($members as $member)
|
||||||
|
42
resources/views/back/sources.blade.php
Normal file
42
resources/views/back/sources.blade.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
@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
|
@ -11,6 +11,7 @@
|
|||||||
@if($role==0)
|
@if($role==0)
|
||||||
<a href="/den/locations">Manage Locations</a><br />
|
<a href="/den/locations">Manage Locations</a><br />
|
||||||
<a href="/den/member">Manage Members</a><br />
|
<a href="/den/member">Manage Members</a><br />
|
||||||
|
<a href="/den/sources">Manage Sources</a><br />
|
||||||
@endif
|
@endif
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
@ -25,11 +25,20 @@
|
|||||||
<br />
|
<br />
|
||||||
@php
|
@php
|
||||||
isset($member->role) ? $role = $member->role : $role = 2;
|
isset($member->role) ? $role = $member->role : $role = 2;
|
||||||
|
//for creation of initial admin account
|
||||||
|
if($mode == 'admin-create')
|
||||||
|
{
|
||||||
|
$role = 0;
|
||||||
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
|
@if($mode != 'admin-create')
|
||||||
<label>Role</label><br />
|
<label>Role</label><br />
|
||||||
<input type="text" name="role" value="{{$role}}" />
|
<input type="text" name="role" value="{{$role}}" />
|
||||||
<br />
|
<br />
|
||||||
@if($mode == 'member-create')
|
@endif
|
||||||
|
|
||||||
|
@if($mode == 'member-create' || $mode == 'admin-create')
|
||||||
<label>Fresh Password</label><br />
|
<label>Fresh Password</label><br />
|
||||||
<input type="password" id="fresh_pass" name="fresh_pass" value="" />
|
<input type="password" id="fresh_pass" name="fresh_pass" value="" />
|
||||||
<br />
|
<br />
|
||||||
@ -40,6 +49,8 @@
|
|||||||
@php
|
@php
|
||||||
isset($member->active) ? $status = $member->active : $status = false;
|
isset($member->active) ? $status = $member->active : $status = false;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
|
@if($mode != 'admin-create')
|
||||||
<label>Status</label><br />
|
<label>Status</label><br />
|
||||||
<select name="status">
|
<select name="status">
|
||||||
@if($status)
|
@if($status)
|
||||||
@ -51,6 +62,8 @@
|
|||||||
@endif
|
@endif
|
||||||
</select>
|
</select>
|
||||||
<br />
|
<br />
|
||||||
|
@endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@csrf
|
@csrf
|
||||||
@php
|
@php
|
||||||
|
66
resources/views/forms/source-edit.blade.php
Normal file
66
resources/views/forms/source-edit.blade.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<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>
|
@ -13,7 +13,7 @@
|
|||||||
@elseif(!isset($front) || $front == true)
|
@elseif(!isset($front) || $front == true)
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/css/front/start.css?=sdfsdf">
|
<link rel="stylesheet" type="text/css" href="/assets/css/front/start.css?=sdfsdf">
|
||||||
@endif
|
@endif
|
||||||
|
<link rel="icon" href="/assets/images/global/favicon.svg" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<img src="/assets/images/global/logo-dark.svg" title="bad-space-logo" />
|
<img src="/assets/images/global/logo-dark.svg" alt="The Bad Space" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
@ -90,7 +90,7 @@
|
|||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<div>
|
<div>
|
||||||
The Bad Space © 2024<br />
|
The Bad Space © <?php echo date("Y"); ?><br />
|
||||||
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>
|
||||||
@ -98,6 +98,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -5,67 +5,60 @@
|
|||||||
<section>
|
<section>
|
||||||
<article>
|
<article>
|
||||||
<h2 id="what">What is The Bad Space?</h2>
|
<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>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>
|
<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>
|
<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>
|
||||||
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>
|
<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>
|
<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>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>
|
<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>
|
<h2>Adding Locations</h2>
|
||||||
<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.</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>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>
|
||||||
|
|
||||||
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>
|
<h2>Removing Locations</h2>
|
||||||
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>.</p>
|
||||||
|
|
||||||
<p><strong>Current Sources:</strong></p>
|
<h3 class="strong">Current Sources:</h3>
|
||||||
Maston:<br />
|
|
||||||
|
<h4>Maston:</h4>
|
||||||
|
<ul>
|
||||||
@foreach($sources as $source)
|
@foreach($sources as $source)
|
||||||
@if($source->format == 'json')
|
@if($source->format == 'json')
|
||||||
<a href="https://{{$source->url}}">{{$source->url}}</a><br />
|
<li><a href="https://{{$source->url}}">{{$source->url}}</a></li>
|
||||||
|
@else
|
||||||
|
<li>None</li>
|
||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
Custom CSV<br />
|
</ul>
|
||||||
|
|
||||||
|
<h4>Custom CSV</h4>
|
||||||
|
<ul>
|
||||||
@foreach($sources as $source)
|
@foreach($sources as $source)
|
||||||
@if($source->format == 'csv')
|
@if($source->format == 'csv')
|
||||||
<a href="{{$source->url}}">{{$source->url}}</a><br />
|
<li><a href="{{$source->url}}">{{$source->url}}</a></li>
|
||||||
|
@else
|
||||||
|
<!--
|
||||||
|
<li>None</li>
|
||||||
|
-->
|
||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>How do I use it?</h2>
|
<h2>How do I use it?</h2>
|
||||||
<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.</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>
|
<h3>Search</h3>
|
||||||
To see if a site is listed in the database, use the
|
<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>
|
||||||
<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>
|
<h3>CSV Exports</h3>
|
||||||
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>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>
|
||||||
<br />
|
<p><a href="/exports">Exports</a></p>
|
||||||
<a href="/exports/mastodon">For Mastodon</a>
|
|
||||||
<h3>API</h3>
|
<h3>API</h3>
|
||||||
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 />
|
<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>
|
||||||
<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>
|
<pre>
|
||||||
<code>{
|
<code>{
|
||||||
@ -83,7 +76,6 @@
|
|||||||
}
|
}
|
||||||
}</code>
|
}</code>
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
@endsection
|
@endsection
|
@ -5,9 +5,11 @@
|
|||||||
<section class="index-search">
|
<section class="index-search">
|
||||||
<form class="index-search-form" action="/search" method="post" enctype="multipart/form-data">
|
<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." />
|
<input type="text" name="index_search" value="" placeholder="Hi! This is where you search." />
|
||||||
<button aria-label="search-button">
|
<button aria-labelledby="search-label">
|
||||||
<label id="search-label">LOOK FOR IT</label>
|
<span id="search-label">Look for it</span>
|
||||||
<img id="search-icon" class="button-icon" src="assets/images/global/icon-search.svg" />
|
<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>
|
</button>
|
||||||
@csrf
|
@csrf
|
||||||
</form>
|
</form>
|
||||||
@ -38,14 +40,21 @@
|
|||||||
@endisset
|
@endisset
|
||||||
<section class="index-meta">
|
<section class="index-meta">
|
||||||
<article>
|
<article>
|
||||||
<div class="index-meta">
|
<table class="index-meta">
|
||||||
<label>Active Locations Tracked</label>
|
<caption class="visually-hidden">Meta</caption>
|
||||||
<label>{{$count}}</label>
|
<tr>
|
||||||
<label>Total Sources</label>
|
<th>Active Locations Tracked</th>
|
||||||
<label>{{$sources}}</label>
|
<td>{{$count}}</td>
|
||||||
<label>Latest Update</label>
|
</tr>
|
||||||
<label>{{$latest_date}}</label>
|
<tr>
|
||||||
</div>
|
<th>Total Sources</th>
|
||||||
|
<td>{{$sources}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Latest Update</th>
|
||||||
|
<td>{{$latest_date}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
@endsection
|
@endsection
|
@ -7,7 +7,7 @@
|
|||||||
<h2>Page {{$pageNum}}</h2>
|
<h2>Page {{$pageNum}}</h2>
|
||||||
<a href="/listings/{{$prev}}">PREV</a>
|
<a href="/listings/{{$prev}}">PREV</a>
|
||||||
{{$pageNum}} of {{$totalPages}}
|
{{$pageNum}} of {{$totalPages}}
|
||||||
<a href="/listings/{{$next}}">NEXT</a><br /><br />
|
<a href="/listings/{{$next}}">NEXT</a>
|
||||||
@foreach($locations as $location)
|
@foreach($locations as $location)
|
||||||
@php
|
@php
|
||||||
$action = $location->block_count + $location->silence_count;
|
$action = $location->block_count + $location->silence_count;
|
||||||
@ -26,10 +26,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
<br />
|
|
||||||
<a href="/listings/{{$prev}}">PREV</a>
|
<a href="/listings/{{$prev}}">PREV</a>
|
||||||
{{$pageNum}} of {{$totalPages}}
|
{{$pageNum}} of {{$totalPages}}
|
||||||
<a href="/listings/{{$next}}">NEXT</a>
|
<a href="/listings/{{$next}}">NEXT</a>
|
||||||
|
<br /><br />
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
@endsection
|
@endsection
|
@ -8,6 +8,7 @@ use App\Http\Controllers\LocationController;
|
|||||||
use App\Http\Controllers\MemberController;
|
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;
|
||||||
|
use App\Http\Controllers\SourceController;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -28,6 +29,7 @@ Route::get("/location/{uuid}", [FrontIndexController::class, 'location']);
|
|||||||
Route::get("/appeals", [FrontIndexController::class, 'appeals']);
|
Route::get("/appeals", [FrontIndexController::class, 'appeals']);
|
||||||
Route::post("/search", [FrontIndexController::class, 'indexSearch']);
|
Route::post("/search", [FrontIndexController::class, 'indexSearch']);
|
||||||
Route::post("/appeal", [AppealController::class, 'sendAppeal']);
|
Route::post("/appeal", [AppealController::class, 'sendAppeal']);
|
||||||
|
Route::post("/den/member/admin-create", [MemberController::class, 'adminCreate']);
|
||||||
|
|
||||||
//exports
|
//exports
|
||||||
Route::get("/exports", [ExportController::class, 'exportIndex']);
|
Route::get("/exports", [ExportController::class, 'exportIndex']);
|
||||||
@ -53,8 +55,14 @@ Route::group(['prefix' => 'den', 'middleware' => 'member.check'], function () {
|
|||||||
Route::get("/member", [MemberController::class, 'index']);
|
Route::get("/member", [MemberController::class, 'index']);
|
||||||
Route::get("/member/{uuid}", [MemberController::class, 'editMember']);
|
Route::get("/member/{uuid}", [MemberController::class, 'editMember']);
|
||||||
Route::get("/member/edit/create", [MemberController::class, 'createMember']);
|
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
|
//actions
|
||||||
Route::post("/profile/edit", [MemberController::class, 'profileEdit']);
|
Route::post("/profile/edit", [MemberController::class, 'profileEdit']);
|
||||||
Route::post("/member/edit", [MemberController::class, 'memberEdit']);
|
Route::post("/member/edit", [MemberController::class, 'memberEdit']);
|
||||||
Route::post("/member/create", [MemberController::class, 'memberCreate']);
|
Route::post("/member/create", [MemberController::class, 'memberCreate']);
|
||||||
|
Route::post("/source/edit", [SourceController::class, 'sourceEdit']);
|
||||||
|
Route::post("/source/create", [SourceController::class, 'sourceCreate']);
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user