mirror of https://github.com/pixelfed/pixelfed
Merge branch 'frontend-ui-refactor' into 598-avatar-sizing
commit
e5fe7eb8a9
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use DB, Cache;
|
||||
use App\{Instance, Profile};
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
trait AdminInstanceController
|
||||
{
|
||||
|
||||
public function instances(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'filter' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:20',
|
||||
Rule::in(['autocw', 'unlisted', 'banned'])
|
||||
],
|
||||
]);
|
||||
if($request->has('filter') && $request->filled('filter')) {
|
||||
switch ($request->filter) {
|
||||
case 'autocw':
|
||||
$instances = Instance::whereAutoCw(true)->orderByDesc('id')->paginate(5);
|
||||
break;
|
||||
case 'unlisted':
|
||||
$instances = Instance::whereUnlisted(true)->orderByDesc('id')->paginate(5);
|
||||
break;
|
||||
case 'banned':
|
||||
$instances = Instance::whereBanned(true)->orderByDesc('id')->paginate(5);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$instances = Instance::orderByDesc('id')->paginate(5);
|
||||
}
|
||||
return view('admin.instances.home', compact('instances'));
|
||||
}
|
||||
|
||||
public function instanceScan(Request $request)
|
||||
{
|
||||
DB::transaction(function() {
|
||||
Profile::whereNotNull('domain')
|
||||
->groupBy('domain')
|
||||
->chunk(50, function($domains) {
|
||||
foreach($domains as $domain) {
|
||||
Instance::firstOrCreate([
|
||||
'domain' => $domain->domain
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function instanceShow(Request $request, $id)
|
||||
{
|
||||
$instance = Instance::findOrFail($id);
|
||||
return view('admin.instances.show', compact('instance'));
|
||||
}
|
||||
|
||||
public function instanceEdit(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:20',
|
||||
Rule::in(['autocw', 'unlist', 'ban'])
|
||||
],
|
||||
]);
|
||||
|
||||
$instance = Instance::findOrFail($id);
|
||||
$unlisted = $instance->unlisted;
|
||||
$autocw = $instance->auto_cw;
|
||||
$banned = $instance->banned;
|
||||
|
||||
switch ($request->action) {
|
||||
case 'autocw':
|
||||
$instance->auto_cw = $autocw == true ? false : true;
|
||||
$instance->save();
|
||||
break;
|
||||
|
||||
case 'unlist':
|
||||
$instance->unlisted = $unlisted == true ? false : true;
|
||||
$instance->save();
|
||||
break;
|
||||
|
||||
case 'ban':
|
||||
$instance->banned = $banned == true ? false : true;
|
||||
$instance->save();
|
||||
break;
|
||||
}
|
||||
|
||||
return response()->json([]);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use DB, Cache;
|
||||
use App\{
|
||||
Media,
|
||||
Profile,
|
||||
Status
|
||||
};
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
trait AdminMediaController
|
||||
{
|
||||
public function media(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'layout' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:4',
|
||||
Rule::in(['grid','list'])
|
||||
],
|
||||
'search' => 'nullable|string|min:1|max:20'
|
||||
]);
|
||||
if($request->filled('search')) {
|
||||
$profiles = Profile::where('username', 'like', '%'.$request->input('search').'%')->pluck('id')->toArray();
|
||||
$media = Media::whereHas('status')
|
||||
->with('status')
|
||||
->orderby('id', 'desc')
|
||||
->whereIn('profile_id', $profiles)
|
||||
->orWhere('mime', $request->input('search'))
|
||||
->paginate(12);
|
||||
} else {
|
||||
$media = Media::whereHas('status')->with('status')->orderby('id', 'desc')->paginate(12);
|
||||
}
|
||||
return view('admin.media.home', compact('media'));
|
||||
}
|
||||
|
||||
public function mediaShow(Request $request, $id)
|
||||
{
|
||||
$media = Media::findOrFail($id);
|
||||
return view('admin.media.show', compact('media'));
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\{Profile, Status, User};
|
||||
use Cache;
|
||||
|
||||
class InstanceApiController extends Controller {
|
||||
|
||||
protected function getData()
|
||||
{
|
||||
$contact = Cache::remember('api:v1:instance:contact', 1440, function() {
|
||||
$admin = User::whereIsAdmin(true)->first()->profile;
|
||||
return [
|
||||
'id' => $admin->id,
|
||||
'username' => $admin->username,
|
||||
'acct' => $admin->username,
|
||||
'display_name' => e($admin->name),
|
||||
'locked' => (bool) $admin->is_private,
|
||||
'bot' => false,
|
||||
'created_at' => $admin->created_at->format('c'),
|
||||
'note' => e($admin->bio),
|
||||
'url' => $admin->url(),
|
||||
'avatar' => $admin->avatarUrl(),
|
||||
'avatar_static' => $admin->avatarUrl(),
|
||||
'header' => null,
|
||||
'header_static' => null,
|
||||
'moved' => null,
|
||||
'fields' => null,
|
||||
'bot' => null,
|
||||
];
|
||||
});
|
||||
|
||||
$res = [
|
||||
'uri' => config('pixelfed.domain.app'),
|
||||
'title' => config('app.name'),
|
||||
'description' => '',
|
||||
'version' => config('pixelfed.version'),
|
||||
'urls' => [],
|
||||
'stats' => [
|
||||
'user_count' => User::count(),
|
||||
'status_count' => Status::whereNull('uri')->count(),
|
||||
'domain_count' => Profile::whereNotNull('domain')
|
||||
->groupBy('domain')
|
||||
->pluck('domain')
|
||||
->count()
|
||||
],
|
||||
'thumbnail' => '',
|
||||
'languages' => [],
|
||||
'contact_account' => $contact
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function instance()
|
||||
{
|
||||
$res = Cache::remember('api:v1:instance', 60, function() {
|
||||
return json_encode($this->getData());
|
||||
});
|
||||
|
||||
return response($res)->header('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\FollowPipeline;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use Cache, Log, Redis;
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use App\FollowRequest;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use App\Transformer\ActivityPub\Verb\Follow;
|
||||
|
||||
class FollowActivityPubDeliver implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $followRequest;
|
||||
|
||||
/**
|
||||
* Delete the job if its models no longer exist.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(FollowRequest $followRequest)
|
||||
{
|
||||
$this->followRequest = $followRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$follow = $this->followRequest;
|
||||
$actor = $follow->actor;
|
||||
$target = $follow->target;
|
||||
|
||||
if($target->domain == null || $target->inbox_url == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fractal = new Fractal\Manager();
|
||||
$fractal->setSerializer(new ArraySerializer());
|
||||
$resource = new Fractal\Resource\Item($follow, new Follow());
|
||||
$activity = $fractal->createData($resource)->toArray();
|
||||
$url = $target->sharedInbox ?? $target->inbox_url;
|
||||
|
||||
Helpers::sendSignedObject($actor, $url, $activity);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Storage;
|
||||
|
||||
class StoryItem extends Model
|
||||
{
|
||||
public function story()
|
||||
{
|
||||
return $this->belongsTo(Story::class);
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
return Storage::url($this->media_path);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class StoryView extends Model
|
||||
{
|
||||
public function story()
|
||||
{
|
||||
return $this->belongsTo(Story::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\ActivityPub\Verb;
|
||||
|
||||
use App\Status;
|
||||
use League\Fractal;
|
||||
|
||||
class Note extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(Status $status)
|
||||
{
|
||||
|
||||
$mentions = $status->mentions->map(function ($mention) {
|
||||
return [
|
||||
'type' => 'Mention',
|
||||
'href' => $mention->permalink(),
|
||||
'name' => $mention->emailUrl()
|
||||
];
|
||||
})->toArray();
|
||||
$hashtags = $status->hashtags->map(function ($hashtag) {
|
||||
return [
|
||||
'type' => 'Hashtag',
|
||||
'href' => $hashtag->url(),
|
||||
'name' => "#{$hashtag->name}",
|
||||
];
|
||||
})->toArray();
|
||||
$tags = array_merge($mentions, $hashtags);
|
||||
|
||||
return [
|
||||
'@context' => [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1',
|
||||
],
|
||||
'id' => $status->url(),
|
||||
'type' => 'Note',
|
||||
'summary' => null,
|
||||
'content' => $status->rendered ?? $status->caption,
|
||||
'inReplyTo' => $status->in_reply_to_id ? $status->parent()->url() : null,
|
||||
'published' => $status->created_at->toAtomString(),
|
||||
'url' => $status->url(),
|
||||
'attributedTo' => $status->profile->permalink(),
|
||||
'to' => $status->scopeToAudience('to'),
|
||||
'cc' => $status->scopeToAudience('cc'),
|
||||
'sensitive' => (bool) $status->is_nsfw,
|
||||
'attachment' => $status->media->map(function ($media) {
|
||||
return [
|
||||
'type' => $media->activityVerb(),
|
||||
'mediaType' => $media->mime,
|
||||
'url' => $media->url(),
|
||||
'name' => null,
|
||||
];
|
||||
})->toArray(),
|
||||
'tag' => $tags,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use League\Fractal;
|
||||
|
||||
class AttachmentTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(Media $media)
|
||||
{
|
||||
return [
|
||||
'id' => $media->id,
|
||||
'type' => $media->activityVerb(),
|
||||
'url' => $media->url(),
|
||||
'remote_url' => null,
|
||||
'preview_url' => $media->thumbnailUrl(),
|
||||
'text_url' => null,
|
||||
'meta' => null,
|
||||
'description' => $media->caption,
|
||||
'license' => $media->license,
|
||||
'is_nsfw' => $media->is_nsfw,
|
||||
'orientation' => $media->orientation,
|
||||
'filter_name' => $media->filter_name,
|
||||
'filter_class' => $media->filter_class,
|
||||
'mime' => $media->mime,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use League\Fractal;
|
||||
|
||||
class ContextTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform()
|
||||
{
|
||||
return [
|
||||
'ancestors' => [],
|
||||
'descendants' => []
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use League\Fractal;
|
||||
|
||||
class FilterTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform()
|
||||
{
|
||||
return [
|
||||
'id' => (string) '',
|
||||
'phrase' => (string) '',
|
||||
'context' => [],
|
||||
'expires_at' => null,
|
||||
'irreversible' => (bool) false,
|
||||
'whole_word' => (bool) false
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use League\Fractal;
|
||||
|
||||
class ResultsTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
|
||||
protected $defaultIncludes = [
|
||||
'accounts',
|
||||
'statuses',
|
||||
'hashtags',
|
||||
];
|
||||
|
||||
public function transform($results)
|
||||
{
|
||||
return [
|
||||
'accounts' => [],
|
||||
'statuses' => [],
|
||||
'hashtags' => []
|
||||
];
|
||||
}
|
||||
|
||||
public function includeAccounts($results)
|
||||
{
|
||||
$accounts = $results->accounts;
|
||||
return $this->collection($accounts, new AccountTransformer());
|
||||
}
|
||||
|
||||
public function includeStatuses($results)
|
||||
{
|
||||
$statuses = $results->statuses;
|
||||
return $this->collection($statuses, new StatusTransformer());
|
||||
}
|
||||
|
||||
public function includeTags($results)
|
||||
{
|
||||
$hashtags = $status->hashtags;
|
||||
return $this->collection($hashtags, new HashtagTransformer());
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\StoryItem;
|
||||
use League\Fractal;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class StoryItemTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
|
||||
public function transform(StoryItem $item)
|
||||
{
|
||||
return [
|
||||
'id' => (string) Str::uuid(),
|
||||
'type' => $item->type,
|
||||
'length' => $item->duration,
|
||||
'src' => $item->url(),
|
||||
'preview' => null,
|
||||
'link' => null,
|
||||
'linkText' => null,
|
||||
'time' => $item->updated_at->format('U'),
|
||||
'seen' => $item->story->seen(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\Story;
|
||||
use League\Fractal;
|
||||
|
||||
class StoryTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
protected $defaultIncludes = [
|
||||
'items',
|
||||
];
|
||||
|
||||
public function transform(Story $story)
|
||||
{
|
||||
return [
|
||||
'id' => $story->id,
|
||||
'photo' => $story->profile->avatarUrl(),
|
||||
'name' => '',
|
||||
'link' => '',
|
||||
'lastUpdated' => $story->updated_at->format('U'),
|
||||
'seen' => $story->seen(),
|
||||
'items' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function includeItems(Story $story)
|
||||
{
|
||||
$items = $story->items;
|
||||
|
||||
return $this->collection($items, new StoryItemTransformer());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Util\ActivityPub\Validator;
|
||||
|
||||
use Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class Announce {
|
||||
|
||||
public static function validate($payload)
|
||||
{
|
||||
$valid = Validator::make($payload, [
|
||||
'@context' => 'required',
|
||||
'id' => 'required|string',
|
||||
'type' => [
|
||||
'required',
|
||||
Rule::in(['Announce'])
|
||||
],
|
||||
'actor' => 'required|url|active_url',
|
||||
'published' => 'required|date',
|
||||
'to' => 'required',
|
||||
'cc' => 'required',
|
||||
'object' => 'required|url|active_url'
|
||||
])->passes();
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Util\ActivityPub\Validator;
|
||||
|
||||
use Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class Follow {
|
||||
|
||||
public static function validate($payload)
|
||||
{
|
||||
$valid = Validator::make($payload, [
|
||||
'@context' => 'required',
|
||||
'id' => 'required|string',
|
||||
'type' => [
|
||||
'required',
|
||||
Rule::in(['Follow'])
|
||||
],
|
||||
'actor' => 'required|url|active_url',
|
||||
'object' => 'required|url|active_url'
|
||||
])->passes();
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Util\ActivityPub\Validator;
|
||||
|
||||
use Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class Like {
|
||||
|
||||
public static function validate($payload)
|
||||
{
|
||||
$valid = Validator::make($payload, [
|
||||
'@context' => 'required',
|
||||
'id' => 'required|string',
|
||||
'type' => [
|
||||
'required',
|
||||
Rule::in(['Like'])
|
||||
],
|
||||
'actor' => 'required|url|active_url',
|
||||
'object' => 'required|url|active_url'
|
||||
])->passes();
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateProfilesTableUseTextForBio extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('profiles', function (Blueprint $table) {
|
||||
$table->text('bio')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('profiles', function (Blueprint $table) {
|
||||
$table->string('bio')->nullable()->change();
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateProfilesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('profiles', function (Blueprint $table) {
|
||||
$table->boolean('unlisted')->default(false)->index()->after('bio');
|
||||
$table->boolean('cw')->default(false)->index()->after('unlisted');
|
||||
$table->boolean('no_autolink')->default(false)->index()->after('cw');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$table->dropColumn('unlisted');
|
||||
$table->dropColumn('cw');
|
||||
$table->dropColumn('no_autolink');
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class Stories extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('story_items', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('story_id')->unsigned()->index();
|
||||
$table->string('media_path')->nullable();
|
||||
$table->string('media_url')->nullable();
|
||||
$table->tinyInteger('duration')->unsigned();
|
||||
$table->string('filter')->nullable();
|
||||
$table->string('link_url')->nullable()->index();
|
||||
$table->string('link_text')->nullable();
|
||||
$table->tinyInteger('order')->unsigned()->nullable();
|
||||
$table->string('type')->default('photo');
|
||||
$table->json('layers')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('story_views', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('story_id')->unsigned()->index();
|
||||
$table->bigInteger('profile_id')->unsigned()->index();
|
||||
$table->unique(['story_id', 'profile_id']);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('stories', function (Blueprint $table) {
|
||||
$table->string('title')->nullable()->after('profile_id');
|
||||
$table->boolean('preview_photo')->default(false)->after('title');
|
||||
$table->boolean('local_only')->default(false)->after('preview_photo');
|
||||
$table->boolean('is_live')->default(false)->after('local_only');
|
||||
$table->string('broadcast_url')->nullable()->after('is_live');
|
||||
$table->string('broadcast_key')->nullable()->after('broadcast_url');
|
||||
});
|
||||
|
||||
Schema::table('story_reactions', function (Blueprint $table) {
|
||||
$table->bigInteger('story_id')->unsigned()->index()->after('profile_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('story_items');
|
||||
Schema::dropIfExists('story_views');
|
||||
|
||||
Schema::table('stories', function (Blueprint $table) {
|
||||
$table->dropColumn('title');
|
||||
$table->dropColumn('preview_photo');
|
||||
$table->dropColumn('local_only');
|
||||
$table->dropColumn('is_live');
|
||||
$table->dropColumn('broadcast_url');
|
||||
$table->dropColumn('broadcast_key');
|
||||
});
|
||||
|
||||
Schema::table('story_reactions', function (Blueprint $table) {
|
||||
$table->dropColumn('story_id');
|
||||
});
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
!function(t){var n={};function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}e.m=t,e.c=n,e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e.p="/",e(e.s=1)}({1:function(t,n,e){t.exports=e("nr3X")},nr3X:function(t,n){$(document).ready(function(){$(".pagination").hide();var t=document.querySelector(".notification-page .list-group");new InfiniteScroll(t,{path:".pagination__next",append:".notification-page .list-group",status:".page-load-status",history:!0})})}});
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=1)}({1:function(e,t,n){e.exports=n("hUgz")},hUgz:function(e,t){$(document).ready(function(){$(".pagination").hide();var e=document.querySelector(".notification-page .list-group");new InfiniteScroll(e,{path:".pagination__next",append:".notification-page .list-group",status:".page-load-status",history:!0})})}});
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
!function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:i})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=3)}({3:function(e,t,n){e.exports=n("uOOV")},uOOV:function(e,t){$(document).ready(function(){$(".pagination").hide(),$(".container.timeline-container").removeClass("d-none");var e=document.querySelector(".timeline-feed"),t=e.getAttribute("data-timeline");$('.timeline-sidenav .nav-link[data-type="'+t+'"]').addClass("active"),pixelfed.readmore(),pixelfed.fetchLikes(),$("video").on("play",function(){activated=this,$("video").each(function(){this!=activated&&this.pause()})}),new InfiniteScroll(e,{path:".pagination__next",append:".timeline-feed",status:".page-load-status",history:!1}).on("append",function(e,t,n){pixelfed.hydrateLikes(),$(".status-card > .card-footer").each(function(){var e=$(this);e.hasClass("d-none")||e.find('input[name="comment"]').val()||$(this).addClass("d-none")}),$("video").on("play",function(){activated=this,$("video").each(function(){this!=activated&&this.pause()})})})}),$(document).on("DOMContentLoaded",function(){var e=!1,t=function(){if(pixelfed.readmore(),!1===e){e=!0;var t=[].slice.call(document.querySelectorAll("img.lazy"));t.forEach(function(e){e.getBoundingClientRect().top<=window.innerHeight&&e.getBoundingClientRect().bottom>=0&&"none"!==getComputedStyle(e).display&&(e.src=e.dataset.src,e.srcset=e.dataset.srcset,e.classList.remove("lazy"),t=t.filter(function(t){return t!==e}))}),e=!1}};document.addEventListener("scroll",t),window.addEventListener("resize",t),window.addEventListener("orientationchange",t)})}});
|
||||
!function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=3)}({3:function(e,t,n){e.exports=n("KqaD")},KqaD:function(e,t){$(document).ready(function(){$(".pagination").hide(),$(".container.timeline-container").removeClass("d-none");var e=document.querySelector(".timeline-feed"),t=e.getAttribute("data-timeline");$('.timeline-sidenav .nav-link[data-type="'+t+'"]').addClass("active"),pixelfed.readmore(),pixelfed.fetchLikes(),$("video").on("play",function(){activated=this,$("video").each(function(){this!=activated&&this.pause()})}),new InfiniteScroll(e,{path:".pagination__next",append:".timeline-feed",status:".page-load-status",history:!1}).on("append",function(e,t,n){pixelfed.hydrateLikes(),$(".status-card > .card-footer").each(function(){var e=$(this);e.hasClass("d-none")||e.find('input[name="comment"]').val()||$(this).addClass("d-none")}),$("video").on("play",function(){activated=this,$("video").each(function(){this!=activated&&this.pause()})})})}),$(document).on("DOMContentLoaded",function(){var e=!1,t=function(){if(pixelfed.readmore(),!1===e){e=!0;var t=[].slice.call(document.querySelectorAll("img.lazy"));t.forEach(function(e){e.getBoundingClientRect().top<=window.innerHeight&&e.getBoundingClientRect().bottom>=0&&"none"!==getComputedStyle(e).display&&(e.src=e.dataset.src,e.srcset=e.dataset.srcset,e.classList.remove("lazy"),t=t.filter(function(t){return t!==e}))}),e=!1}};document.addEventListener("scroll",t),window.addEventListener("resize",t),window.addEventListener("orientationchange",t)})}});
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"/js/components.js": "/js/components.js?id=866258c03c0ba1ad76f0",
|
||||
"/js/app.js": "/js/app.js?id=4721785cceb8eacd55f1",
|
||||
"/css/app.css": "/css/app.css?id=c477745c80d3370e7b29",
|
||||
"/js/timeline.js": "/js/timeline.js?id=415bfde862ab8c5b4548",
|
||||
"/js/activity.js": "/js/activity.js?id=723dfb98bbbc96a9d39f"
|
||||
"/js/activity.js": "/js/activity.js?id=7915246c3bc2b7e9770e",
|
||||
"/js/app.js": "/js/app.js?id=ea57709b5a7ef8657ff4",
|
||||
"/css/app.css": "/css/app.css?id=e8047f495e5dfbca1445",
|
||||
"/js/components.js": "/js/components.js?id=b4f163bc071166f5d2bb",
|
||||
"/js/timeline.js": "/js/timeline.js?id=83c85c7144756ee9a72e"
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024"><defs><linearGradient id="a" x1="50.31%" x2="50%" y1="74.74%" y2="0%"><stop offset="0%" stop-color="#FFE98A"/><stop offset="67.7%" stop-color="#B63E59"/><stop offset="100%" stop-color="#68126F"/></linearGradient><circle id="c" cx="603" cy="682" r="93"/><filter id="b" width="203.2%" height="203.2%" x="-51.6%" y="-51.6%" filterUnits="objectBoundingBox"><feOffset in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="32"/><feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><linearGradient id="d" x1="49.48%" x2="49.87%" y1="11.66%" y2="77.75%"><stop offset="0%" stop-color="#F7EAB9"/><stop offset="100%" stop-color="#E5765E"/></linearGradient><linearGradient id="e" x1="91.59%" x2="66.97%" y1="5.89%" y2="100%"><stop offset="0%" stop-color="#A22A50"/><stop offset="100%" stop-color="#EE7566"/></linearGradient><linearGradient id="f" x1="49.48%" x2="49.61%" y1="11.66%" y2="98.34%"><stop offset="0%" stop-color="#F7EAB9"/><stop offset="100%" stop-color="#E5765E"/></linearGradient><linearGradient id="g" x1="78.5%" x2="36.4%" y1="106.76%" y2="26.41%"><stop offset="0%" stop-color="#A22A50"/><stop offset="100%" stop-color="#EE7566"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><rect width="1024" height="1024" fill="url(#a)"/><use fill="black" filter="url(#b)" xlink:href="#c"/><use fill="#FFF6CB" xlink:href="#c"/><g fill="#FFFFFF" opacity=".3" transform="translate(14 23)"><circle cx="203" cy="255" r="3" fill-opacity=".4"/><circle cx="82" cy="234" r="2"/><circle cx="22" cy="264" r="2" opacity=".4"/><circle cx="113" cy="65" r="3"/><circle cx="202" cy="2" r="2"/><circle cx="2" cy="114" r="2"/><circle cx="152" cy="144" r="2"/><circle cx="362" cy="224" r="2"/><circle cx="453" cy="65" r="3" opacity=".4"/><circle cx="513" cy="255" r="3"/><circle cx="593" cy="115" r="3"/><circle cx="803" cy="5" r="3" opacity=".4"/><circle cx="502" cy="134" r="2"/><circle cx="832" cy="204" r="2"/><circle cx="752" cy="114" r="2"/><circle cx="933" cy="255" r="3" opacity=".4"/><circle cx="703" cy="225" r="3"/><circle cx="903" cy="55" r="3"/><circle cx="982" cy="144" r="2"/><circle cx="632" cy="14" r="2"/></g><g transform="translate(0 550)"><path fill="#8E2C15" d="M259 5.47c0 5.33 3.33 9.5 10 12.5s9.67 9.16 9 18.5h1c.67-6.31 1-11.8 1-16.47 8.67 0 13.33-1.33 14-4 .67 4.98 1.67 8.3 3 9.97 1.33 1.66 2 5.16 2 10.5h1c0-5.65.33-9.64 1-11.97 1-3.5 4-10.03-1-14.53S295 7 290 3c-5-4-10-3-13 2s-5 7-9 7-5-3.53-5-5.53c0-2 2-5-1.5-5s-7.5 0-7.5 2c0 1.33 1.67 2 5 2z"/><path fill="url(#d)" d="M1024 390H0V105.08C77.3 71.4 155.26 35 297.4 35c250 0 250.76 125.25 500 125 84.03-.08 160.02-18.2 226.6-40.93V390z"/><path fill="url(#d)" d="M1024 442H0V271.82c137.51-15.4 203.1-50.49 356.67-60.1C555.24 199.3 606.71 86.59 856.74 86.59c72.78 0 124.44 10.62 167.26 25.68V442z"/><path fill="url(#e)" d="M1024 112.21V412H856.91c99.31-86.5 112.63-140.75 39.97-162.78C710.24 192.64 795.12 86.58 856.9 86.58c72.7 0 124.3 10.6 167.09 25.63z"/><path fill="url(#e)" d="M1024 285.32V412H857c99.31-86.6 112.63-140.94 39.97-163L1024 285.32z"/><path fill="url(#f)" d="M0 474V223.93C67.12 190.69 129.55 155 263 155c250 0 331.46 162.6 530 175 107.42 6.71 163-26.77 231-58.92V474H0z"/><path fill="url(#e)" d="M353.02 474H0V223.93C67.12 190.69 129.55 155 263 155c71.14 0 151.5 12.76 151.5 70.5 0 54.5-45.5 79.72-112.5 109-82.26 35.95-54.57 111.68 51.02 139.5z"/><path fill="url(#g)" d="M353.02 474H0v-14.8l302-124.7c-82.26 35.95-54.57 111.68 51.02 139.5z"/></g><g fill="#FFFFFF" opacity=".2" transform="translate(288 523)"><circle cx="250" cy="110" r="110"/><circle cx="420" cy="78" r="60"/><circle cx="70" cy="220" r="70"/></g><g fill="#FFFFFF" fill-rule="nonzero" opacity=".08" transform="translate(135 316)"><path d="M10 80.22a14.2 14.2 0 0 1 20 0 14.2 14.2 0 0 0 20 0l20-19.86a42.58 42.58 0 0 1 60 0l15 14.9a21.3 21.3 0 0 0 30 0 21.3 21.3 0 0 1 30 0l.9.9A47.69 47.69 0 0 1 220 110H0v-5.76c0-9.02 3.6-17.67 10-24.02zm559.1-66.11l5.9-5.86c11.07-11 28.93-11 40 0l10 9.94a14.19 14.19 0 0 0 20 0 14.19 14.19 0 0 1 20 0 16.36 16.36 0 0 0 21.3 1.5l8.7-6.47a33.47 33.47 0 0 1 40 0l4.06 3.03A39.6 39.6 0 0 1 755 48H555a47.77 47.77 0 0 1 14.1-33.89z"/></g></g></svg>
|
After Width: | Height: | Size: 4.2 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 15 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.4 KiB |
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-body" id="stories">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style type="text/css" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
stories: [],
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchStories() {
|
||||
axios.get('/api/v2/stories')
|
||||
.then(res => {
|
||||
this.stories = res.data
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,223 @@
|
||||
@extends('admin.partial.template')
|
||||
|
||||
@section('section')
|
||||
<div class="title">
|
||||
<h3 class="font-weight-bold d-inline-block">Instances</h3>
|
||||
<span class="float-right">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-light btn-sm dropdown-toggle font-weight-bold" type="button" id="filterDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="filterDropdown">
|
||||
<a class="dropdown-item font-weight-light" href="{{route('admin.instances')}}?filter=unlisted">Show only Unlisted</a>
|
||||
<a class="dropdown-item font-weight-light" href="{{route('admin.instances')}}?filter=autocw">Show only Auto CW</a>
|
||||
<a class="dropdown-item font-weight-light" href="{{route('admin.instances')}}?filter=banned">Show only Banned</a>
|
||||
<a class="dropdown-item font-weight-light" href="{{route('admin.instances')}}">Show all</a>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
@if($instances->count() == 0 && request()->has('filter') == false)
|
||||
<div class="alert alert-warning mb-3">
|
||||
<p class="lead font-weight-bold mb-0">Warning</p>
|
||||
<p class="font-weight-lighter mb-0">No instances were found.</p>
|
||||
</div>
|
||||
<p class="font-weight-lighter">Do you want to scan and populate instances from Profiles and Statuses?</p>
|
||||
<p>
|
||||
<form method="post">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-primary py-1 font-weight-bold">Run Scan</button>
|
||||
</form>
|
||||
</p>
|
||||
@else
|
||||
<ul class="list-group">
|
||||
@foreach($instances as $instance)
|
||||
<li class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<p class="h4 font-weight-normal mb-1">
|
||||
{{$instance->domain}}
|
||||
</p>
|
||||
<p class="mb-0">
|
||||
<a class="btn btn-outline-primary btn-sm py-0 font-weight-normal" href="{{$instance->getUrl()}}">Overview</a>
|
||||
<button class="btn btn-outline-secondary btn-sm py-0 font-weight-normal btn-action mr-3"
|
||||
data-instance-id="{{$instance->id}}"
|
||||
data-instance-domain="{{$instance->domain}}"
|
||||
data-instance-unlisted="{{$instance->unlisted}}"
|
||||
data-instance-autocw="{{$instance->auto_cw}}"
|
||||
data-instance-banned="{{$instance->banned}}"
|
||||
>Actions</button>
|
||||
@if($instance->unlisted)
|
||||
<i class="fas fa-minus-circle text-danger" data-toggle="tooltip" title="Unlisted from timelines"></i>
|
||||
@endif
|
||||
@if($instance->auto_cw)
|
||||
<i class="fas fa-eye-slash text-danger" data-toggle="tooltip" title="CW applied to all media"></i>
|
||||
@endif
|
||||
@if($instance->banned)
|
||||
<i class="fas fa-shield-alt text-danger" data-toggle="tooltip" title="Instance is banned"></i>
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-inline-block pr-4">
|
||||
<p class="h4 font-weight-light text-center">{{$instance->profiles()->count()}}</p>
|
||||
<p class="mb-0 small font-weight-normal text-muted">Profiles</p>
|
||||
</div>
|
||||
<div class="d-inline-block pr-4">
|
||||
<p class="h4 font-weight-light text-center">{{$instance->statuses()->count()}}</p>
|
||||
<p class="mb-0 small font-weight-normal text-muted">Statuses</p>
|
||||
</div>
|
||||
<div class="d-inline-block pr-4">
|
||||
<p class="h4 font-weight-light text-center text-muted">{{$instance->reported()->count()}}</p>
|
||||
<p class="mb-0 small font-weight-normal text-muted">Reports</p>
|
||||
</div>
|
||||
<div class="d-inline-block">
|
||||
<p class="h4 font-weight-light text-center text-muted filesize" data-size="{{$instance->media()->sum('size')}}">0</p>
|
||||
<p class="mb-0 small font-weight-normal text-muted">Storage Used</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<div class="d-flex justify-content-center mt-5 small">
|
||||
{{$instances->links()}}
|
||||
</div>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('.filesize').each(function(k,v) {
|
||||
$(this).text(filesize(v.getAttribute('data-size')))
|
||||
});
|
||||
|
||||
$('.btn-action').on('click', function(e) {
|
||||
let id = this.getAttribute('data-instance-id');
|
||||
let instanceDomain = this.getAttribute('data-instance-domain');
|
||||
let text = 'Domain: ' + instanceDomain;
|
||||
let unlisted = this.getAttribute('data-instance-unlisted');
|
||||
let autocw = this.getAttribute('data-instance-autocw');
|
||||
let banned = this.getAttribute('data-instance-banned');
|
||||
swal({
|
||||
title: 'Instance Actions',
|
||||
text: text,
|
||||
icon: 'warning',
|
||||
buttons: {
|
||||
unlist: {
|
||||
text: unlisted == 0 ? "Unlist" : "Re-list",
|
||||
className: "bg-warning",
|
||||
value: "unlisted",
|
||||
},
|
||||
cw: {
|
||||
text: autocw == 0 ? "CW Media" : "Remove AutoCW",
|
||||
text: autocw == 0 ? "CW Media" : "Remove AutoCW",
|
||||
className: "bg-warning",
|
||||
value: "autocw",
|
||||
},
|
||||
ban: {
|
||||
text: banned == 0 ? "Ban" : "Unban",
|
||||
className: "bg-danger",
|
||||
value: "ban",
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((value) => {
|
||||
switch (value) {
|
||||
case "unlisted":
|
||||
swal({
|
||||
title: "Are you sure?",
|
||||
text: unlisted == 0 ?
|
||||
"Are you sure you want to unlist " + instanceDomain + " ?" :
|
||||
"Are you sure you want to remove the unlisted rule of " + instanceDomain + " ?",
|
||||
icon: "warning",
|
||||
buttons: true,
|
||||
dangerMode: true,
|
||||
})
|
||||
.then((unlist) => {
|
||||
if (unlist) {
|
||||
axios.post('/i/admin/instances/edit/' + id, {
|
||||
action: 'unlist'
|
||||
}).then((res) => {
|
||||
swal("Domain action was successful! The page will now refresh.", {
|
||||
icon: "success",
|
||||
});
|
||||
setTimeout(function() {
|
||||
window.location.href = window.location.href;
|
||||
}, 5000);
|
||||
}).catch((err) => {
|
||||
swal("Something went wrong!", "Please try again later.", "error");
|
||||
})
|
||||
} else {
|
||||
swal("Action Cancelled", "You successfully cancelled this action.", "error");
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "autocw":
|
||||
swal({
|
||||
title: "Are you sure?",
|
||||
text: autocw == 0 ?
|
||||
"Are you sure you want to auto CW all media from " + instanceDomain + " ?" :
|
||||
"Are you sure you want to remove the auto cw rule for " + instanceDomain + " ?",
|
||||
icon: "warning",
|
||||
buttons: true,
|
||||
dangerMode: true,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
axios.post('/i/admin/instances/edit/' + id, {
|
||||
action: 'autocw'
|
||||
}).then((res) => {
|
||||
swal("Domain action was successful! The page will now refresh.", {
|
||||
icon: "success",
|
||||
});
|
||||
setTimeout(function() {
|
||||
window.location.href = window.location.href;
|
||||
}, 5000);
|
||||
}).catch((err) => {
|
||||
swal("Something went wrong!", "Please try again later.", "error");
|
||||
})
|
||||
} else {
|
||||
swal("Action Cancelled", "You successfully cancelled this action.", "error");
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "ban":
|
||||
swal({
|
||||
title: "Are you sure?",
|
||||
text: autocw == 0 ?
|
||||
"Are you sure you want to ban " + instanceDomain + " ?" :
|
||||
"Are you sure you want unban " + instanceDomain + " ?",
|
||||
icon: "warning",
|
||||
buttons: true,
|
||||
dangerMode: true,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
axios.post('/i/admin/instances/edit/' + id, {
|
||||
action: 'ban'
|
||||
}).then((res) => {
|
||||
swal("Domain action was successful! The page will now refresh.", {
|
||||
icon: "success",
|
||||
});
|
||||
setTimeout(function() {
|
||||
window.location.href = window.location.href;
|
||||
}, 5000);
|
||||
}).catch((err) => {
|
||||
swal("Something went wrong!", "Please try again later.", "error");
|
||||
})
|
||||
} else {
|
||||
swal("Action Cancelled", "You successfully cancelled this action.", "error");
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
</script>
|
||||
@endpush
|
@ -0,0 +1,113 @@
|
||||
@extends('admin.partial.template')
|
||||
|
||||
@section('section')
|
||||
<div class="title">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h3 class="font-weight-bold mb-0">Instance Overview</h3>
|
||||
<p class="font-weight-lighter mb-0">domain: {{$instance->domain}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<a class="btn btn-outline-primary btn-sm py-1" href="{{route('admin.instances')}}">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<p class="font-weight-lighter mb-0">unlisted: {{$instance->unlisted ? 'true' : 'false'}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-weight-lighter mb-0">CW media: {{$instance->auto_cw ? 'true' : 'false'}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-weight-lighter mb-0">banned: {{$instance->banned ? 'true' : 'false'}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body text-center">
|
||||
<p class="mb-0 font-weight-lighter display-4">
|
||||
{{$instance->profiles->count()}}
|
||||
</p>
|
||||
<p class="mb-0 text-muted">Profiles</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-body text-center">
|
||||
<p class="mb-0 font-weight-lighter display-4">
|
||||
{{$instance->reports->count()}}
|
||||
</p>
|
||||
<p class="mb-0 text-muted">Reports</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body text-center">
|
||||
<p class="mb-0 font-weight-lighter display-4">
|
||||
{{$instance->statuses->count()}}
|
||||
</p>
|
||||
<p class="mb-0 text-muted">Statuses</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-body text-center">
|
||||
<p class="mb-0 font-weight-lighter display-4 filesize" data-size="{{$instance->media()->sum('size')}}">
|
||||
0
|
||||
</p>
|
||||
<p class="mb-0 text-muted">Storage Used</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light h4 font-weight-lighter">
|
||||
Profiles
|
||||
<span class="float-right">
|
||||
<a class="btn btn-outline-secondary btn-sm py-0" href="#">View All</a>
|
||||
</span>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach($instance->profiles()->latest()->take(5)->get() as $profile)
|
||||
<li class="list-group-item">
|
||||
<a class="btn btn-outline-primary btn-block btn-sm" href="{{$profile->url()}}">{{$profile->emailUrl()}}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light h4 font-weight-lighter">
|
||||
Statuses
|
||||
<span class="float-right">
|
||||
<a class="btn btn-outline-secondary btn-sm py-0" href="#">View All</a>
|
||||
</span>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach($instance->statuses()->latest()->take(5)->get() as $status)
|
||||
<li class="list-group-item">
|
||||
<a class="btn btn-outline-primary btn-block btn-sm" href="{{$status->url()}}">Status ID: {{$status->id}}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('.filesize').each(function(k,v) {
|
||||
$(this).text(filesize(v.getAttribute('data-size')))
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
@ -0,0 +1,41 @@
|
||||
@extends('admin.partial.template')
|
||||
|
||||
@section('section')
|
||||
<div class="title">
|
||||
<h3 class="font-weight-bold d-inline-block">Media</h3>
|
||||
<p class="font-weight-lighter mb-0">ID: {{$media->id}}</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-8 offset-md-2">
|
||||
<div class="card">
|
||||
<img class="card-img-top" src="{{$media->thumb()}}">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<div>
|
||||
<p class="mb-0 small">status id: <a href="{{$media->status->url()}}" class="font-weight-bold">{{$media->status_id}}</a></p>
|
||||
<p class="mb-0 small">username: <a href="{{$media->profile->url()}}" class="font-weight-bold">{{$media->profile->username}}</a></p>
|
||||
<p class="mb-0 small">size: <span class="filesize font-weight-bold" data-size="{{$media->size}}">0</span></p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 small">mime: <span class="font-weight-bold">{{$media->mime}}</span></p>
|
||||
<p class="mb-0 small">content warning: <i class="fas {{$media->is_nsfw ? 'fa-check text-danger':'fa-times text-success'}}"></i></p>
|
||||
<p class="mb-0 small">
|
||||
remote media: <i class="fas {{$media->remote_media ? 'fa-check text-danger':'fa-times text-success'}}"></i></p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('.filesize').each(function(k,v) {
|
||||
$(this).text(filesize(v.getAttribute('data-size')))
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\ActivityPub\Verb;
|
||||
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use App\Util\ActivityPub\Validator\Announce;
|
||||
|
||||
class AnnounceTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->validAnnounce = [
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce",
|
||||
"actor" => "https://example.org/users/alice",
|
||||
"published" => "2018-12-31T23:59:59Z",
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
"https://example.org/users/bob",
|
||||
"https://example.org/users/alice/followers"
|
||||
],
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
|
||||
$this->invalidAnnounce = [
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce2",
|
||||
"actor" => "https://example.org/users/alice",
|
||||
"published" => "2018-12-31T23:59:59Z",
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
"https://example.org/users/bob",
|
||||
"https://example.org/users/alice/followers"
|
||||
],
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
|
||||
$this->invalidDate = [
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce",
|
||||
"actor" => "https://example.org/users/alice",
|
||||
"published" => "2018-12-31T23:59:59ZEZE",
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
"https://example.org/users/bob",
|
||||
"https://example.org/users/alice/followers"
|
||||
],
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
|
||||
$this->contextMissing = [
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce",
|
||||
"actor" => "https://example.org/users/alice",
|
||||
"published" => "2018-12-31T23:59:59Z",
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
"https://example.org/users/bob",
|
||||
"https://example.org/users/alice/followers"
|
||||
],
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
|
||||
$this->audienceMissing = [
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce",
|
||||
"actor" => "https://example.org/users/alice",
|
||||
"published" => "2018-12-31T23:59:59Z",
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
|
||||
$this->audienceMissing2 = [
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce",
|
||||
"actor" => "https://example.org/users/alice",
|
||||
"published" => "2018-12-31T23:59:59Z",
|
||||
"to" => null,
|
||||
"cc" => null,
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
|
||||
$this->invalidActor = [
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce",
|
||||
"actor" => "10000",
|
||||
"published" => "2018-12-31T23:59:59Z",
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
"https://example.org/users/bob",
|
||||
"https://example.org/users/alice/followers"
|
||||
],
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
|
||||
$this->invalidActor2 = [
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||
"type" => "Announce",
|
||||
"published" => "2018-12-31T23:59:59Z",
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
"https://example.org/users/bob",
|
||||
"https://example.org/users/alice/followers"
|
||||
],
|
||||
"object" => "https://example.org/p/bob/100000000000000",
|
||||
];
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function basic_accept()
|
||||
{
|
||||
$this->assertTrue(Announce::validate($this->validAnnounce));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function invalid_accept()
|
||||
{
|
||||
$this->assertFalse(Announce::validate($this->invalidAnnounce));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function invalid_date()
|
||||
{
|
||||
$this->assertFalse(Announce::validate($this->invalidDate));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function context_missing()
|
||||
{
|
||||
$this->assertFalse(Announce::validate($this->contextMissing));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function audience_missing()
|
||||
{
|
||||
$this->assertFalse(Announce::validate($this->audienceMissing));
|
||||
$this->assertFalse(Announce::validate($this->audienceMissing2));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function invalid_actor()
|
||||
{
|
||||
$this->assertFalse(Announce::validate($this->invalidActor));
|
||||
$this->assertFalse(Announce::validate($this->invalidActor2));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue