Merge pull request #2474 from pixelfed/staging

Staging
pull/2511/head
daniel 2020-12-09 22:28:49 -07:00 zatwierdzone przez GitHub
commit f13ba6d08f
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
60 zmienionych plików z 1534 dodań i 61 usunięć

Wyświetl plik

@ -19,6 +19,8 @@
- Add Password change email notification ([de1cca4f](https://github.com/pixelfed/pixelfed/commit/de1cca4f))
- Add shared inbox ([4733ca9f](https://github.com/pixelfed/pixelfed/commit/4733ca9f))
- Add federated photo filters ([0a5a0e86](https://github.com/pixelfed/pixelfed/commit/0a5a0e86))
- Add AccountInterstitial model and controller ([8766ccfe](https://github.com/pixelfed/pixelfed/commit/8766ccfe))
- Add Blurhash encoder ([fad102bf](https://github.com/pixelfed/pixelfed/commit/fad102bf))
### Updated
- Updated PostComponent, fix remote urls ([42716ccc](https://github.com/pixelfed/pixelfed/commit/42716ccc))
@ -106,6 +108,20 @@
- Updated federation config, make sharedInbox enabled by default. ([6e3522c0](https://github.com/pixelfed/pixelfed/commit/6e3522c0))
- Updated PostComponent, change timestamp format. ([e51665f6](https://github.com/pixelfed/pixelfed/commit/e51665f6))
- Updated PostComponent, use proper username context for reply mentions. Fixes ([#2421](https://github.com/pixelfed/pixelfed/issues/2421)). ([dac06088](https://github.com/pixelfed/pixelfed/commit/dac06088))
- Updated Navbar, added profile avatar. ([19abf1b4](https://github.com/pixelfed/pixelfed/commit/19abf1b4))
- Updated package.json, add blurhash. ([cc1b081a](https://github.com/pixelfed/pixelfed/commit/cc1b081a))
- Updated Status model, fix thumb nsfw caching. ([327ef138](https://github.com/pixelfed/pixelfed/commit/327ef138))
- Updated User model, add interstitial relation. ([bd321a72](https://github.com/pixelfed/pixelfed/commit/bd321a72))
- Updated StatusStatelessTransformer, add missing attributes. ([4d22426d](https://github.com/pixelfed/pixelfed/commit/4d22426d))
- Updated media pipeline, add blurhash support. ([473e0495](https://github.com/pixelfed/pixelfed/commit/473e0495))
- Updated DeleteAccountPipeline, add AccountInterstitial and DirectMessage purging. ([b3078f27](https://github.com/pixelfed/pixelfed/commit/b3078f27))
- Updated ComposeModal.vue component, reuse sharedData. ([e28d022f](https://github.com/pixelfed/pixelfed/commit/e28d022f))
- Updated ApiController, return status object after deletion. ([0718711d](https://github.com/pixelfed/pixelfed/commit/0718711d))
- Updated InternalApiController, add interstitial logic. ([20681bcf](https://github.com/pixelfed/pixelfed/commit/20681bcf))
- Updated PublicApiController, improve stateless object caching. ([342e7a50](https://github.com/pixelfed/pixelfed/commit/342e7a50))
- Updated StatusController, add interstitial logic. ([003caf7e](https://github.com/pixelfed/pixelfed/commit/003caf7e))
- Updated middleware, add AccountInterstitial support. ([19d6e7df](https://github.com/pixelfed/pixelfed/commit/19d6e7df))
- Updated BaseApiController, add favourites method. ([76353ca9](https://github.com/pixelfed/pixelfed/commit/76353ca9))
## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9)
### Added

Wyświetl plik

@ -0,0 +1,30 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AccountInterstitial extends Model
{
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['read_at', 'appeal_requested_at'];
public const JSON_MESSAGE = 'Please use web browser to proceed.';
public function user()
{
return $this->belongsTo(User::class);
}
public function status()
{
if($this->item_type != 'App\Status') {
return;
}
return $this->hasOne(Status::class, 'id', 'item_id');
}
}

Wyświetl plik

@ -0,0 +1,76 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Status;
use App\AccountInterstitial;
class AccountInterstitialController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function get(Request $request)
{
$interstitial = $request->user()
->interstitials()
->whereNull('read_at')
->first();
if(!$interstitial) {
$user = $request->user();
$user->has_interstitial = false;
$user->save();
return redirect('/');
}
$meta = json_decode($interstitial->meta);
$view = $interstitial->view;
return view($view, compact('interstitial', 'meta'));
}
public function read(Request $request)
{
$this->validate($request, [
'id' => 'required',
'type' => 'required|in:post.cw,post.removed,post.unlist',
'action' => 'required|in:appeal,confirm',
'appeal_message' => 'nullable|max:500'
]);
$redirect = '/';
$id = decrypt($request->input('id'));
$action = $request->input('action');
$user = $request->user();
$ai = AccountInterstitial::whereUserId($user->id)
->whereType($request->input('type'))
->findOrFail($id);
if($action == 'appeal') {
$ai->appeal_requested_at = now();
$ai->appeal_message = $request->input('appeal_message');
}
$ai->read_at = now();
$ai->save();
$more = AccountInterstitial::whereUserId($user->id)
->whereNull('read_at')
->exists();
if(!$more) {
$user->has_interstitial = false;
$user->save();
}
if(in_array($ai->type, ['post.cw', 'post.unlist'])) {
$redirect = Status::findOrFail($ai->item_id)->url();
}
return redirect($redirect);
}
}

Wyświetl plik

@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\{
AccountInterstitial,
Contact,
Hashtag,
Newsroom,
@ -85,6 +86,67 @@ class AdminController extends Controller
return view('admin.reports.show', compact('report'));
}
public function appeals(Request $request)
{
$appeals = AccountInterstitial::whereNotNull('appeal_requested_at')
->whereNull('appeal_handled_at')
->latest()
->paginate(6);
return view('admin.reports.appeals', compact('appeals'));
}
public function showAppeal(Request $request, $id)
{
$appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
->whereNull('appeal_handled_at')
->findOrFail($id);
$meta = json_decode($appeal->meta);
return view('admin.reports.show_appeal', compact('appeal', 'meta'));
}
public function updateAppeal(Request $request, $id)
{
$this->validate($request, [
'action' => 'required|in:dismiss,approve'
]);
$action = $request->input('action');
$appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
->whereNull('appeal_handled_at')
->findOrFail($id);
if($action == 'dismiss') {
$appeal->appeal_handled_at = now();
$appeal->save();
return redirect('/i/admin/reports/appeals');
}
switch ($appeal->type) {
case 'post.cw':
$status = $appeal->status;
$status->is_nsfw = false;
$status->save();
break;
case 'post.unlist':
$status = $appeal->status;
$status->scope = 'public';
$status->visibility = 'public';
$status->save();
break;
default:
# code...
break;
}
$appeal->appeal_handled_at = now();
$appeal->save();
return redirect('/i/admin/reports/appeals');
}
public function profiles(Request $request)
{
$this->validate($request, [

Wyświetl plik

@ -1761,6 +1761,7 @@ class ApiV1Controller extends Controller
NewStatusPipeline::dispatch($status);
Cache::forget('user:account:id:'.$user->id);
Cache::forget('_api:statuses:recent_9:'.$user->profile_id);
Cache::forget('profile:status_count:'.$user->profile_id);
Cache::forget($user->storageUsedKey());
@ -1783,10 +1784,15 @@ class ApiV1Controller extends Controller
$status = Status::whereProfileId($request->user()->profile->id)
->findOrFail($id);
$resource = new Fractal\Resource\Item($status, new StatusTransformer());
Cache::forget('profile:status_count:'.$status->profile_id);
StatusDelete::dispatch($status);
return response()->json(['Status successfully deleted.']);
$res = $this->fractal->createData($resource)->toArray();
$res['text'] = $res['content'];
unset($res['content']);
return response()->json($res);
}
/**

Wyświetl plik

@ -11,8 +11,9 @@ use Auth, Cache, Storage, URL;
use Carbon\Carbon;
use App\{
Avatar,
Notification,
Like,
Media,
Notification,
Profile,
Status
};
@ -21,7 +22,8 @@ use App\Transformer\Api\{
NotificationTransformer,
MediaTransformer,
MediaDraftTransformer,
StatusTransformer
StatusTransformer,
StatusStatelessTransformer
};
use League\Fractal;
use App\Util\Media\Filter;
@ -338,4 +340,29 @@ class BaseApiController extends Controller
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
}
public function accountLikes(Request $request)
{
$user = $request->user();
abort_if(!$request->user(), 403);
$limit = 10;
$page = (int) $request->input('page', 1);
if($page > 20) {
return [];
}
$favourites = $user->profile->likes()
->latest()
->simplePaginate($limit)
->pluck('status_id');
$statuses = Status::find($favourites)->reverse();
$resource = new Fractal\Resource\Collection($statuses, new StatusStatelessTransformer());
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
}
}

Wyświetl plik

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\{
AccountInterstitial,
DirectMessage,
DiscoverCategory,
Hashtag,
@ -213,6 +214,35 @@ class InternalApiController extends Controller
])
->accessLevel('admin')
->save();
if($status->uri == null) {
$media = $status->media;
$ai = new AccountInterstitial;
$ai->user_id = $status->profile->user_id;
$ai->type = 'post.cw';
$ai->view = 'account.moderation.post.cw';
$ai->item_type = 'App\Status';
$ai->item_id = $status->id;
$ai->has_media = (bool) $media->count();
$ai->blurhash = $media->count() ? $media->first()->blurhash : null;
$ai->meta = json_encode([
'caption' => $status->caption,
'created_at' => $status->created_at,
'type' => $status->type,
'url' => $status->url(),
'is_nsfw' => $status->is_nsfw,
'scope' => $status->scope,
'reblog' => $status->reblog_of_id,
'likes_count' => $status->likes_count,
'reblogs_count' => $status->reblogs_count,
]);
$ai->save();
$u = $status->profile->user;
$u->has_interstitial = true;
$u->save();
}
break;
case 'remcw':
@ -231,6 +261,14 @@ class InternalApiController extends Controller
])
->accessLevel('admin')
->save();
if($status->uri == null) {
$ai = AccountInterstitial::whereUserId($status->profile->user_id)
->whereType('post.cw')
->whereItemId($status->id)
->whereItemType('App\Status')
->first();
$ai->delete();
}
break;
case 'unlist':
@ -250,6 +288,34 @@ class InternalApiController extends Controller
])
->accessLevel('admin')
->save();
if($status->uri == null) {
$media = $status->media;
$ai = new AccountInterstitial;
$ai->user_id = $status->profile->user_id;
$ai->type = 'post.unlist';
$ai->view = 'account.moderation.post.unlist';
$ai->item_type = 'App\Status';
$ai->item_id = $status->id;
$ai->has_media = (bool) $media->count();
$ai->blurhash = $media->count() ? $media->first()->blurhash : null;
$ai->meta = json_encode([
'caption' => $status->caption,
'created_at' => $status->created_at,
'type' => $status->type,
'url' => $status->url(),
'is_nsfw' => $status->is_nsfw,
'scope' => $status->scope,
'reblog' => $status->reblog_of_id,
'likes_count' => $status->likes_count,
'reblogs_count' => $status->reblogs_count,
]);
$ai->save();
$u = $status->profile->user;
$u->has_interstitial = true;
$u->save();
}
break;
}
return ['msg' => 200];
@ -364,6 +430,7 @@ class InternalApiController extends Controller
NewStatusPipeline::dispatch($status);
Cache::forget('user:account:id:'.$profile->user_id);
Cache::forget('_api:statuses:recent_9:'.$profile->id);
Cache::forget('profile:status_count:'.$profile->id);
Cache::forget($user->storageUsedKey());
return $status->url();

Wyświetl plik

@ -66,7 +66,9 @@ class ProfileController extends Controller
'list' => $settings->show_profile_followers
]
];
return view('profile.show', compact('profile', 'settings'));
$ui = $request->has('ui') && $request->input('ui') == 'memory' ? 'profile.memory' : 'profile.show';
return view($ui, compact('profile', 'settings'));
} else {
$key = 'profile:settings:' . $user->id;
$ttl = now()->addHours(6);
@ -103,7 +105,8 @@ class ProfileController extends Controller
'list' => $settings->show_profile_followers
]
];
return view('profile.show', compact('profile', 'settings'));
$ui = $request->has('ui') && $request->input('ui') == 'memory' ? 'profile.memory' : 'profile.show';
return view($ui, compact('profile', 'settings'));
}
}

Wyświetl plik

@ -20,7 +20,8 @@ use League\Fractal;
use App\Transformer\Api\{
AccountTransformer,
RelationshipTransformer,
StatusTransformer
StatusTransformer,
StatusStatelessTransformer
};
use App\Services\{
AccountService,
@ -86,6 +87,24 @@ class PublicApiController extends Controller
$profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
$status = Status::whereProfileId($profile->id)->findOrFail($postid);
$this->scopeCheck($profile, $status);
if(!Auth::check()) {
$res = Cache::remember('wapi:v1:status:stateless_byid:' . $status->id, now()->addMinutes(30), function() use($status) {
$item = new Fractal\Resource\Item($status, new StatusStatelessTransformer());
$res = [
'status' => $this->fractal->createData($item)->toArray(),
'user' => [],
'likes' => [],
'shares' => [],
'reactions' => [
'liked' => false,
'shared' => false,
'bookmarked' => false,
],
];
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
});
return $res;
}
$item = new Fractal\Resource\Item($status, new StatusTransformer());
$res = [
'status' => $this->fractal->createData($item)->toArray(),
@ -419,7 +438,6 @@ class PublicApiController extends Controller
}
public function networkTimelineApi(Request $request)
{
return response()->json([]);
@ -543,6 +561,50 @@ class PublicApiController extends Controller
}
}
$tag = in_array('private', $visibility) ? 'private' : 'public';
if($min_id == 1 && $limit == 9 && $tag == 'public') {
$limit = 9;
$scope = ['photo', 'photo:album', 'video', 'video:album'];
$key = '_api:statuses:recent_9:'.$profile->id;
$res = Cache::remember($key, now()->addHours(24), function() use($profile, $scope, $visibility, $limit) {
$dir = '>';
$id = 1;
$timeline = Status::select(
'id',
'uri',
'caption',
'rendered',
'profile_id',
'type',
'in_reply_to_id',
'reblog_of_id',
'is_nsfw',
'likes_count',
'reblogs_count',
'scope',
'visibility',
'local',
'place_id',
'comments_disabled',
'cw_summary',
'created_at',
'updated_at'
)->whereProfileId($profile->id)
->whereIn('type', $scope)
->where('id', $dir, $id)
->whereIn('visibility', $visibility)
->limit($limit)
->orderByDesc('id')
->get();
$resource = new Fractal\Resource\Collection($timeline, new StatusStatelessTransformer());
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
});
return $res;
}
$dir = $min_id ? '>' : '<';
$id = $min_id ?? $max_id;
$timeline = Status::select(
@ -560,6 +622,8 @@ class PublicApiController extends Controller
'scope',
'visibility',
'local',
'place_id',
'comments_disabled',
'cw_summary',
'created_at',
'updated_at'

Wyświetl plik

@ -6,6 +6,7 @@ use App\Jobs\ImageOptimizePipeline\ImageOptimize;
use App\Jobs\StatusPipeline\NewStatusPipeline;
use App\Jobs\StatusPipeline\StatusDelete;
use App\Jobs\SharePipeline\SharePipeline;
use App\AccountInterstitial;
use App\Media;
use App\Profile;
use App\Status;
@ -162,14 +163,49 @@ class StatusController extends Controller
$status = Status::findOrFail($request->input('item'));
if ($status->profile_id === Auth::user()->profile->id || Auth::user()->is_admin == true) {
$user = Auth::user();
if($status->profile_id != $user->profile->id &&
$user->is_admin == true &&
$status->uri == null
) {
$media = $status->media;
$ai = new AccountInterstitial;
$ai->user_id = $status->profile->user_id;
$ai->type = 'post.removed';
$ai->view = 'account.moderation.post.removed';
$ai->item_type = 'App\Status';
$ai->item_id = $status->id;
$ai->has_media = (bool) $media->count();
$ai->blurhash = $media->count() ? $media->first()->blurhash : null;
$ai->meta = json_encode([
'caption' => $status->caption,
'created_at' => $status->created_at,
'type' => $status->type,
'url' => $status->url(),
'is_nsfw' => $status->is_nsfw,
'scope' => $status->scope,
'reblog' => $status->reblog_of_id,
'likes_count' => $status->likes_count,
'reblogs_count' => $status->reblogs_count,
]);
$ai->save();
$u = $status->profile->user;
$u->has_interstitial = true;
$u->save();
}
if ($status->profile_id == $user->profile->id || $user->is_admin == true) {
Cache::forget('profile:status_count:'.$status->profile_id);
StatusDelete::dispatch($status);
}
if($request->wantsJson()) {
return response()->json(['Status successfully deleted.']);
} else {
return redirect(Auth::user()->url());
return redirect($user->url());
}
}

Wyświetl plik

@ -66,6 +66,7 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'twofactor' => \App\Http\Middleware\TwoFactorAuth::class,
'validemail' => \App\Http\Middleware\EmailVerificationCheck::class,
'interstitial' => \App\Http\Middleware\AccountInterstitial::class,
// 'restricted' => \App\Http\Middleware\RestrictedAccess::class,
];
}

Wyświetl plik

@ -0,0 +1,48 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
use App\User;
class AccountInterstitial
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$ar = [
'login',
'logout',
'password*',
'loginAs*',
'i/warning*',
'i/auth/checkpoint',
'i/auth/sudo',
'site/privacy',
'site/terms',
'site/kb/community-guidelines',
];
if(Auth::check() && !$request->is($ar)) {
if($request->user()->has_interstitial) {
if($request->wantsJson()) {
$res = ['_refresh'=>true,'error' => 403, 'message' => \App\AccountInterstitial::JSON_MESSAGE];
return response()->json($res, 403);
} else {
return redirect('/i/warning');
}
} else {
return $next($request);
}
} else {
return $next($request);
}
}
}

Wyświetl plik

@ -10,6 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use DB;
use Illuminate\Support\Str;
use App\{
AccountInterstitial,
AccountLog,
Activity,
Avatar,
@ -68,6 +69,10 @@ class DeleteAccountPipeline implements ShouldQueue
});
});
DB::transaction(function() use ($user) {
AccountInterstitial::whereUserId($user->id)->delete();
});
DB::transaction(function() use ($user) {
if($user->profile) {
$avatar = $user->profile->avatar;
@ -79,6 +84,7 @@ class DeleteAccountPipeline implements ShouldQueue
Bookmark::whereProfileId($user->profile_id)->forceDelete();
EmailVerification::whereUserId($user->id)->forceDelete();
StatusHashtag::whereProfileId($id)->delete();
DirectMessage::whereFromId($user->profile_id)->delete();
FollowRequest::whereFollowingId($id)
->orWhere('follower_id', $id)
->forceDelete();

Wyświetl plik

@ -89,7 +89,8 @@ class Status extends Model
public function thumb($showNsfw = false)
{
return Cache::remember('status:thumb:'.$this->id, now()->addMinutes(15), function() use ($showNsfw) {
$key = $showNsfw ? 'status:thumb:nsfw1'.$this->id : 'status:thumb:nsfw0'.$this->id;
return Cache::remember($key, now()->addMinutes(15), function() use ($showNsfw) {
$type = $this->type ?? $this->setType();
$is_nsfw = !$showNsfw ? $this->is_nsfw : false;
if ($this->media->count() == 0 || $is_nsfw || !in_array($type,['photo', 'photo:album', 'video'])) {

Wyświetl plik

@ -5,6 +5,8 @@ namespace App\Transformer\Api;
use App\Status;
use League\Fractal;
use Cache;
use App\Services\HashidService;
use App\Services\MediaTagService;
class StatusStatelessTransformer extends Fractal\TransformerAbstract
{
@ -17,8 +19,11 @@ class StatusStatelessTransformer extends Fractal\TransformerAbstract
public function transform(Status $status)
{
$taggedPeople = MediaTagService::get($status->id);
return [
'id' => (string) $status->id,
'shortcode' => HashidService::encode($status->id),
'uri' => $status->url(),
'url' => $status->url(),
'in_reply_to_id' => $status->in_reply_to_id,
@ -42,13 +47,17 @@ class StatusStatelessTransformer extends Fractal\TransformerAbstract
'language' => null,
'pinned' => null,
'mentions' => [],
'tags' => [],
'pf_type' => $status->type ?? $status->setType(),
'reply_count' => (int) $status->reply_count,
'comments_disabled' => $status->comments_disabled ? true : false,
'thread' => false,
'replies' => [],
'parent' => $status->parent() ? $this->transform($status->parent()) : [],
'parent' => [],
'place' => $status->place,
'local' => (bool) $status->local,
'taggedPeople' => $taggedPeople
];
}

Wyświetl plik

@ -88,4 +88,9 @@ class User extends Authenticatable
return $this->hasMany(AccountLog::class);
}
public function interstitials()
{
return $this->hasMany(AccountInterstitial::class);
}
}

Wyświetl plik

@ -0,0 +1,34 @@
<?php
namespace App\Util\Blurhash;
final class AC {
public static function encode(array $value, float $max_value): float {
$quant_r = static::quantise($value[0] / $max_value);
$quant_g = static::quantise($value[1] / $max_value);
$quant_b = static::quantise($value[2] / $max_value);
return $quant_r * 19 * 19 + $quant_g * 19 + $quant_b;
}
public static function decode(int $value, float $max_value): array {
$quant_r = floor($value / (19 * 19));
$quant_g = floor($value / 19) % 19;
$quant_b = $value % 19;
return [
static::signPow(($quant_r - 9) / 9, 2) * $max_value,
static::signPow(($quant_g - 9) / 9, 2) * $max_value,
static::signPow(($quant_b - 9) / 9, 2) * $max_value
];
}
private static function quantise(float $value): float {
return floor(max(0, min(18, floor(static::signPow($value, 0.5) * 9 + 9.5))));
}
private static function signPow(float $base, float $exp): float {
$sign = $base <=> 0;
return $sign * pow(abs($base), $exp);
}
}

Wyświetl plik

@ -0,0 +1,39 @@
<?php
namespace App\Util\Blurhash;
use InvalidArgumentException;
class Base83 {
private const ALPHABET = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '#', '$', '%', '*', '+', ',', '-', '.',
':', ';', '=', '?', '@', '[', ']', '^', '_', '{', '|', '}', '~'
];
private const BASE = 83;
public static function encode(int $value, int $length): string {
if (floor($value / (self::BASE ** $length)) != 0) {
throw new InvalidArgumentException('Specified length is too short to encode given value.');
}
$result = '';
for ($i = 1; $i <= $length; $i++) {
$digit = floor($value / (self::BASE ** ($length - $i))) % self::BASE;
$result .= self::ALPHABET[$digit];
}
return $result;
}
public static function decode(string $hash): int {
$result = 0;
foreach (str_split($hash) as $char) {
$result = $result * self::BASE + (int) array_search($char, self::ALPHABET, true);
}
return (int) $result;
}
}

Wyświetl plik

@ -0,0 +1,139 @@
<?php
namespace App\Util\Blurhash;
use InvalidArgumentException;
class Blurhash {
public static function encode(array $image, int $components_x = 4, int $components_y = 4, bool $linear = false): string {
if (($components_x < 1 || $components_x > 9) || ($components_y < 1 || $components_y > 9)) {
throw new InvalidArgumentException("x and y component counts must be between 1 and 9 inclusive.");
}
$height = count($image);
$width = count($image[0]);
$image_linear = $image;
if (!$linear) {
$image_linear = [];
for ($y = 0; $y < $height; $y++) {
$line = [];
for ($x = 0; $x < $width; $x++) {
$pixel = $image[$y][$x];
$line[] = [
Color::toLinear($pixel[0]),
Color::toLinear($pixel[1]),
Color::toLinear($pixel[2])
];
}
$image_linear[] = $line;
}
}
$components = [];
$scale = 1 / ($width * $height);
for ($y = 0; $y < $components_y; $y++) {
for ($x = 0; $x < $components_x; $x++) {
$normalisation = $x == 0 && $y == 0 ? 1 : 2;
$r = $g = $b = 0;
for ($i = 0; $i < $width; $i++) {
for ($j = 0; $j < $height; $j++) {
$color = $image_linear[$j][$i];
$basis = $normalisation
* cos(M_PI * $i * $x / $width)
* cos(M_PI * $j * $y / $height);
$r += $basis * $color[0];
$g += $basis * $color[1];
$b += $basis * $color[2];
}
}
$components[] = [
$r * $scale,
$g * $scale,
$b * $scale
];
}
}
$dc_value = DC::encode(array_shift($components) ?: []);
$max_ac_component = 0;
foreach ($components as $component) {
$component[] = $max_ac_component;
$max_ac_component = max ($component);
}
$quant_max_ac_component = (int) max(0, min(82, floor($max_ac_component * 166 - 0.5)));
$ac_component_norm_factor = ($quant_max_ac_component + 1) / 166;
$ac_values = [];
foreach ($components as $component) {
$ac_values[] = AC::encode($component, $ac_component_norm_factor);
}
$blurhash = Base83::encode($components_x - 1 + ($components_y - 1) * 9, 1);
$blurhash .= Base83::encode($quant_max_ac_component, 1);
$blurhash .= Base83::encode($dc_value, 4);
foreach ($ac_values as $ac_value) {
$blurhash .= Base83::encode((int) $ac_value, 2);
}
return $blurhash;
}
public static function decode (string $blurhash, int $width, int $height, float $punch = 1.0, bool $linear = false): array {
if (empty($blurhash) || strlen($blurhash) < 6) {
throw new InvalidArgumentException("Blurhash string must be at least 6 characters");
}
$size_info = Base83::decode($blurhash[0]);
$size_y = floor($size_info / 9) + 1;
$size_x = ($size_info % 9) + 1;
$length = (int) strlen($blurhash);
$expected_length = (int) (4 + (2 * $size_y * $size_x));
if ($length !== $expected_length) {
throw new InvalidArgumentException("Blurhash length mismatch: length is {$length} but it should be {$expected_length}");
}
$colors = [DC::decode(Base83::decode(substr($blurhash, 2, 4)))];
$quant_max_ac_component = Base83::decode($blurhash[1]);
$max_value = ($quant_max_ac_component + 1) / 166;
for ($i = 1; $i < $size_x * $size_y; $i++) {
$value = Base83::decode(substr($blurhash, 4 + $i * 2, 2));
$colors[$i] = AC::decode($value, $max_value * $punch);
}
$pixels = [];
for ($y = 0; $y < $height; $y++) {
$row = [];
for ($x = 0; $x < $width; $x++) {
$r = $g = $b = 0;
for ($j = 0; $j < $size_y; $j++) {
for ($i = 0; $i < $size_x; $i++) {
$color = $colors[$i + $j * $size_x];
$basis =
cos((M_PI * $x * $i) / $width) *
cos((M_PI * $y * $j) / $height);
$r += $color[0] * $basis;
$g += $color[1] * $basis;
$b += $color[2] * $basis;
}
}
$row[] = $linear ? [$r, $g, $b] : [
Color::toSRGB($r),
Color::toSRGB($g),
Color::toSRGB($b)
];
}
$pixels[] = $row;
}
return $pixels;
}
}

Wyświetl plik

@ -0,0 +1,19 @@
<?php
namespace App\Util\Blurhash;
final class Color {
public static function toLinear(int $value): float {
$value = $value / 255;
return ($value <= 0.04045)
? $value / 12.92
: pow(($value + 0.055) / 1.055, 2.4);
}
public static function tosRGB(float $value): int {
$normalized = max(0, min(1, $value));
return ($normalized <= 0.0031308)
? (int) round($normalized * 12.92 * 255 + 0.5)
: (int) round((1.055 * pow($normalized, 1 / 2.4) - 0.055) * 255 + 0.5);
}
}

Wyświetl plik

@ -0,0 +1,24 @@
<?php
namespace App\Util\Blurhash;
final class DC {
public static function encode(array $value): int {
$rounded_r = Color::tosRGB($value[0]);
$rounded_g = Color::tosRGB($value[1]);
$rounded_b = Color::tosRGB($value[2]);
return ($rounded_r << 16) + ($rounded_g << 8) + $rounded_b;
}
public static function decode(int $value): array {
$r = $value >> 16;
$g = ($value >> 8) & 255;
$b = $value & 255;
return [
Color::toLinear($r),
Color::toLinear($g),
Color::toLinear($b)
];
}
}

Wyświetl plik

@ -0,0 +1,47 @@
<?php
namespace App\Util\Media;
use App\Util\Blurhash\Blurhash as BlurhashEngine;
use App\Media;
class Blurhash {
public static function generate(Media $media)
{
if(!in_array($media->mime, ['image/png', 'image/jpeg'])) {
return;
}
$file = storage_path('app/' . $media->thumbnail_path);
if(!is_file($file)) {
return;
}
$image = imagecreatefromstring(file_get_contents($file));
$width = imagesx($image);
$height = imagesy($image);
$pixels = [];
for ($y = 0; $y < $height; ++$y) {
$row = [];
for ($x = 0; $x < $width; ++$x) {
$index = imagecolorat($image, $x, $y);
$colors = imagecolorsforindex($image, $index);
$row[] = [$colors['red'], $colors['green'], $colors['blue']];
}
$pixels[] = $row;
}
$components_x = 4;
$components_y = 4;
$blurhash = BlurhashEngine::encode($pixels, $components_x, $components_y);
if(strlen($blurhash) > 191) {
return;
}
return $blurhash;
}
}

Wyświetl plik

@ -182,6 +182,10 @@ class Image
$media->save();
if($thumbnail) {
$this->generateBlurhash($media);
}
Cache::forget('status:transformer:media:attachments:'.$media->status_id);
Cache::forget('status:thumb:'.$media->status_id);
} catch (Exception $e) {
@ -198,4 +202,13 @@ class Image
return ['path' => $basePath, 'png' => $png];
}
protected function generateBlurhash($media)
{
$blurhash = Blurhash::generate($media);
if($blurhash) {
$media->blurhash = $blurhash;
$media->save();
}
}
}

Wyświetl plik

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIndexesToLikesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('likes', function (Blueprint $table) {
$table->index('profile_id', 'likes_profile_id_index');
$table->index('status_id', 'likes_status_id_index');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('likes', function (Blueprint $table) {
$table->dropIndex('likes_profile_id_index');
$table->dropIndex('likes_status_id_index');
});
}
}

Wyświetl plik

@ -0,0 +1,53 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAccountInterstitialsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('account_interstitials', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('user_id')->nullable()->index();
$table->string('type')->nullable();
$table->string('view')->nullable();
$table->bigInteger('item_id')->unsigned()->nullable();
$table->string('item_type')->nullable();
$table->boolean('has_media')->default(false)->nullable();
$table->string('blurhash')->nullable();
$table->text('message')->nullable();
$table->text('violation_header')->nullable();
$table->text('violation_body')->nullable();
$table->json('meta')->nullable();
$table->text('appeal_message')->nullable();
$table->timestamp('appeal_requested_at')->nullable()->index();
$table->timestamp('appeal_handled_at')->nullable()->index();
$table->timestamp('read_at')->nullable()->index();
$table->timestamps();
});
Schema::table('users', function(Blueprint $table) {
$table->boolean('has_interstitial')->default(false)->index();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('account_interstitials');
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('has_interstitial');
});
}
}

5
package-lock.json wygenerowano
Wyświetl plik

@ -1649,6 +1649,11 @@
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
"integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg=="
},
"blurhash": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/blurhash/-/blurhash-1.1.3.tgz",
"integrity": "sha512-yUhPJvXexbqbyijCIE/T2NCXcj9iNPhWmOKbPTuR/cm7Q5snXYIfnVnz6m7MWOXxODMz/Cr3UcVkRdHiuDVRDw=="
},
"bn.js": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz",

Wyświetl plik

@ -27,6 +27,7 @@
"dependencies": {
"@trevoreyre/autocomplete-vue": "^2.2.0",
"animate.css": "^4.1.0",
"blurhash": "^1.1.3",
"bootstrap-vue": "^2.16.0",
"filesize": "^3.6.1",
"howler": "^2.2.0",

File diff suppressed because one or more lines are too long

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/direct.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/memoryprofile.js vendored 100644

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{19:function(t,e,s){t.exports=s("ETg6")},"7wkd":function(t,e,s){"use strict";s.r(e);var o={data:function(){return{loaded:!1,showLoadMore:!0,profiles:[],page:1}},beforeMount:function(){this.fetchData()},methods:{fetchData:function(){var t=this;axios.get("/api/pixelfed/v2/discover/profiles",{params:{page:this.page}}).then((function(e){if(0==e.data.length)return t.showLoadMore=!1,void(t.loaded=!0);t.profiles=e.data,t.showLoadMore=8==t.profiles.length,t.loaded=!0}))},prettyCount:function(t){return App.util.format.count(t)},loadMore:function(){this.loaded=!1,this.page++,this.fetchData()},thumbUrl:function(t){return t.media_attachments[0].url}}},a=s("KHd+"),n=Object(a.a)(o,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",[s("div",{staticClass:"col-12"},[s("p",{staticClass:"font-weight-bold text-lighter text-uppercase"},[t._v("Profiles Directory")]),t._v(" "),t.loaded?s("div",{},[s("div",{staticClass:"row"},[t._l(t.profiles,(function(e,o){return s("div",{staticClass:"col-12 col-md-6 p-1"},[s("div",{staticClass:"card card-body border shadow-none py-2"},[s("div",{staticClass:"media"},[s("a",{attrs:{href:e.url}},[s("img",{staticClass:"rounded-circle border mr-3",attrs:{src:e.avatar,alt:"...",width:"40px",height:"40px"}})]),t._v(" "),s("div",{staticClass:"media-body"},[s("p",{staticClass:"mt-0 mb-0 font-weight-bold"},[s("a",{staticClass:"text-dark",attrs:{href:e.url}},[t._v(t._s(e.username))])]),t._v(" "),s("p",{staticClass:"mb-1 small text-lighter d-flex justify-content-between font-weight-bold"},[s("span",[s("span",[t._v(t._s(t.prettyCount(e.statuses_count)))]),t._v(" POSTS\n\t\t\t\t\t\t\t\t\t")]),t._v(" "),s("span",[s("span",[t._v(t._s(t.prettyCount(e.followers_count)))]),t._v(" FOLLOWERS\n\t\t\t\t\t\t\t\t\t")])]),t._v(" "),s("p",{staticClass:"mb-1"},t._l(e.posts,(function(e,o){return s("span",{key:"profile_posts_"+o,staticClass:"shadow-sm"},[s("a",{staticClass:"text-decoration-none mr-1",attrs:{href:e.url}},[s("img",{staticClass:"border rounded",attrs:{src:t.thumbUrl(e),width:"62.3px",height:"62.3px"}})])])})),0)])])])])})),t._v(" "),t.showLoadMore?s("div",{staticClass:"col-12"},[s("p",{staticClass:"text-center mb-0 pt-3"},[s("button",{staticClass:"btn btn-outline-secondary btn-sm px-4 py-1 font-weight-bold",on:{click:function(e){return t.loadMore()}}},[t._v("Load More")])])]):t._e()],2)]):s("div",[t._m(0)])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"row"},[e("div",{staticClass:"col-12 d-flex justify-content-center align-items-center"},[e("div",{staticClass:"spinner-border",attrs:{role:"status"}},[e("span",{staticClass:"sr-only"},[this._v("Loading...")])])])])}],!1,null,"7b3eea1c",null);e.default=n.exports},ETg6:function(t,e,s){Vue.component("profile-directory",s("7wkd").default)},"KHd+":function(t,e,s){"use strict";function o(t,e,s,o,a,n,r,i){var c,d="function"==typeof t?t.options:t;if(e&&(d.render=e,d.staticRenderFns=s,d._compiled=!0),o&&(d.functional=!0),n&&(d._scopeId="data-v-"+n),r?(c=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),a&&a.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},d._ssrRegister=c):a&&(c=i?function(){a.call(this,this.$root.$options.shadowRoot)}:a),c)if(d.functional){d._injectStyles=c;var l=d.render;d.render=function(t,e){return c.call(e),l(t,e)}}else{var u=d.beforeCreate;d.beforeCreate=u?[].concat(u,c):[c]}return{exports:t,options:d}}s.d(e,"a",(function(){return o}))}},[[19,0]]]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{19:function(t,e,s){t.exports=s("ETg6")},"7wkd":function(t,e,s){"use strict";s.r(e);var o={data:function(){return{loaded:!1,showLoadMore:!0,profiles:[],page:1}},beforeMount:function(){this.fetchData()},methods:{fetchData:function(){var t=this;axios.get("/api/pixelfed/v2/discover/profiles",{params:{page:this.page}}).then((function(e){if(0==e.data.length)return t.showLoadMore=!1,void(t.loaded=!0);t.profiles=e.data,t.showLoadMore=8==t.profiles.length,t.loaded=!0}))},prettyCount:function(t){return App.util.format.count(t)},loadMore:function(){this.loaded=!1,this.page++,this.fetchData()},thumbUrl:function(t){return t.media_attachments[0].url}}},a=s("KHd+"),n=Object(a.a)(o,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",[s("div",{staticClass:"col-12"},[s("p",{staticClass:"font-weight-bold text-lighter text-uppercase"},[t._v("Profiles Directory")]),t._v(" "),t.loaded?s("div",{},[s("div",{staticClass:"row"},[t._l(t.profiles,(function(e,o){return s("div",{staticClass:"col-12 col-md-6 p-1"},[s("div",{staticClass:"card card-body border shadow-none py-2"},[s("div",{staticClass:"media"},[s("a",{attrs:{href:e.url}},[s("img",{staticClass:"rounded-circle border mr-3",attrs:{src:e.avatar,alt:"...",width:"40px",height:"40px"}})]),t._v(" "),s("div",{staticClass:"media-body"},[s("p",{staticClass:"mt-0 mb-0 font-weight-bold"},[s("a",{staticClass:"text-dark",attrs:{href:e.url}},[t._v(t._s(e.username))])]),t._v(" "),s("p",{staticClass:"mb-1 small text-lighter d-flex justify-content-between font-weight-bold"},[s("span",[s("span",[t._v(t._s(t.prettyCount(e.statuses_count)))]),t._v(" POSTS\n\t\t\t\t\t\t\t\t\t")]),t._v(" "),s("span",[s("span",[t._v(t._s(t.prettyCount(e.followers_count)))]),t._v(" FOLLOWERS\n\t\t\t\t\t\t\t\t\t")])]),t._v(" "),s("p",{staticClass:"mb-1"},t._l(e.posts,(function(e,o){return s("span",{key:"profile_posts_"+o,staticClass:"shadow-sm"},[s("a",{staticClass:"text-decoration-none mr-1",attrs:{href:e.url}},[s("img",{staticClass:"border rounded",attrs:{src:t.thumbUrl(e),width:"62.3px",height:"62.3px"}})])])})),0)])])])])})),t._v(" "),t.showLoadMore?s("div",{staticClass:"col-12"},[s("p",{staticClass:"text-center mb-0 pt-3"},[s("button",{staticClass:"btn btn-outline-secondary btn-sm px-4 py-1 font-weight-bold",on:{click:function(e){return t.loadMore()}}},[t._v("Load More")])])]):t._e()],2)]):s("div",[t._m(0)])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"row"},[e("div",{staticClass:"col-12 d-flex justify-content-center align-items-center"},[e("div",{staticClass:"spinner-border",attrs:{role:"status"}},[e("span",{staticClass:"sr-only"},[this._v("Loading...")])])])])}],!1,null,"7b3eea1c",null);e.default=n.exports},ETg6:function(t,e,s){Vue.component("profile-directory",s("7wkd").default)},"KHd+":function(t,e,s){"use strict";function o(t,e,s,o,a,n,r,i){var c,d="function"==typeof t?t.options:t;if(e&&(d.render=e,d.staticRenderFns=s,d._compiled=!0),o&&(d.functional=!0),n&&(d._scopeId="data-v-"+n),r?(c=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),a&&a.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},d._ssrRegister=c):a&&(c=i?function(){a.call(this,this.$root.$options.shadowRoot)}:a),c)if(d.functional){d._injectStyles=c;var l=d.render;d.render=function(t,e){return c.call(e),l(t,e)}}else{var u=d.beforeCreate;d.beforeCreate=u?[].concat(u,c):[c]}return{exports:t,options:d}}s.d(e,"a",(function(){return o}))}},[[19,0]]]);

File diff suppressed because one or more lines are too long

2
public/js/quill.js vendored

File diff suppressed because one or more lines are too long

2
public/js/rempos.js vendored

File diff suppressed because one or more lines are too long

2
public/js/rempro.js vendored

File diff suppressed because one or more lines are too long

2
public/js/search.js vendored

File diff suppressed because one or more lines are too long

2
public/js/status.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{15:function(e,a,o){e.exports=o("YMO/")},"YMO/":function(e,a,o){(function(e){function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}ace.define("ace/theme/monokai",["require","exports","module","ace/lib/dom"],(function(e,a,o){a.isDark=!0,a.cssClass="ace-monokai",a.cssText=".ace-monokai .ace_gutter {background: #2F3129;color: #8F908A}.ace-monokai .ace_print-margin {width: 1px;background: #555651}.ace-monokai {background-color: #272822;color: #F8F8F2}.ace-monokai .ace_cursor {color: #F8F8F0}.ace-monokai .ace_marker-layer .ace_selection {background: #49483E}.ace-monokai.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #272822;}.ace-monokai .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-monokai .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #49483E}.ace-monokai .ace_marker-layer .ace_active-line {background: #202020}.ace-monokai .ace_gutter-active-line {background-color: #272727}.ace-monokai .ace_marker-layer .ace_selected-word {border: 1px solid #49483E}.ace-monokai .ace_invisible {color: #52524d}.ace-monokai .ace_entity.ace_name.ace_tag,.ace-monokai .ace_keyword,.ace-monokai .ace_meta.ace_tag,.ace-monokai .ace_storage {color: #F92672}.ace-monokai .ace_punctuation,.ace-monokai .ace_punctuation.ace_tag {color: #fff}.ace-monokai .ace_constant.ace_character,.ace-monokai .ace_constant.ace_language,.ace-monokai .ace_constant.ace_numeric,.ace-monokai .ace_constant.ace_other {color: #AE81FF}.ace-monokai .ace_invalid {color: #F8F8F0;background-color: #F92672}.ace-monokai .ace_invalid.ace_deprecated {color: #F8F8F0;background-color: #AE81FF}.ace-monokai .ace_support.ace_constant,.ace-monokai .ace_support.ace_function {color: #66D9EF}.ace-monokai .ace_fold {background-color: #A6E22E;border-color: #F8F8F2}.ace-monokai .ace_storage.ace_type,.ace-monokai .ace_support.ace_class,.ace-monokai .ace_support.ace_type {font-style: italic;color: #66D9EF}.ace-monokai .ace_entity.ace_name.ace_function,.ace-monokai .ace_entity.ace_other,.ace-monokai .ace_entity.ace_other.ace_attribute-name,.ace-monokai .ace_variable {color: #A6E22E}.ace-monokai .ace_variable.ace_parameter {font-style: italic;color: #FD971F}.ace-monokai .ace_string {color: #E6DB74}.ace-monokai .ace_comment {color: #75715E}.ace-monokai .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ0FD0ZXBzd/wPAAjVAoxeSgNeAAAAAElFTkSuQmCC) right repeat-y}",e("../lib/dom").importCssString(a.cssText,a.cssClass)})),ace.require(["ace/theme/monokai"],(function(c){"object"==o(e)&&"object"==o(a)&&e&&(e.exports=c)}))}).call(this,o("YuTi")(e))},YuTi:function(e,a){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}}},[[15,0]]]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{15:function(e,a,o){e.exports=o("YMO/")},"YMO/":function(e,a,o){(function(e){function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}ace.define("ace/theme/monokai",["require","exports","module","ace/lib/dom"],(function(e,a,o){a.isDark=!0,a.cssClass="ace-monokai",a.cssText=".ace-monokai .ace_gutter {background: #2F3129;color: #8F908A}.ace-monokai .ace_print-margin {width: 1px;background: #555651}.ace-monokai {background-color: #272822;color: #F8F8F2}.ace-monokai .ace_cursor {color: #F8F8F0}.ace-monokai .ace_marker-layer .ace_selection {background: #49483E}.ace-monokai.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #272822;}.ace-monokai .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-monokai .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #49483E}.ace-monokai .ace_marker-layer .ace_active-line {background: #202020}.ace-monokai .ace_gutter-active-line {background-color: #272727}.ace-monokai .ace_marker-layer .ace_selected-word {border: 1px solid #49483E}.ace-monokai .ace_invisible {color: #52524d}.ace-monokai .ace_entity.ace_name.ace_tag,.ace-monokai .ace_keyword,.ace-monokai .ace_meta.ace_tag,.ace-monokai .ace_storage {color: #F92672}.ace-monokai .ace_punctuation,.ace-monokai .ace_punctuation.ace_tag {color: #fff}.ace-monokai .ace_constant.ace_character,.ace-monokai .ace_constant.ace_language,.ace-monokai .ace_constant.ace_numeric,.ace-monokai .ace_constant.ace_other {color: #AE81FF}.ace-monokai .ace_invalid {color: #F8F8F0;background-color: #F92672}.ace-monokai .ace_invalid.ace_deprecated {color: #F8F8F0;background-color: #AE81FF}.ace-monokai .ace_support.ace_constant,.ace-monokai .ace_support.ace_function {color: #66D9EF}.ace-monokai .ace_fold {background-color: #A6E22E;border-color: #F8F8F2}.ace-monokai .ace_storage.ace_type,.ace-monokai .ace_support.ace_class,.ace-monokai .ace_support.ace_type {font-style: italic;color: #66D9EF}.ace-monokai .ace_entity.ace_name.ace_function,.ace-monokai .ace_entity.ace_other,.ace-monokai .ace_entity.ace_other.ace_attribute-name,.ace-monokai .ace_variable {color: #A6E22E}.ace-monokai .ace_variable.ace_parameter {font-style: italic;color: #FD971F}.ace-monokai .ace_string {color: #E6DB74}.ace-monokai .ace_comment {color: #75715E}.ace-monokai .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ0FD0ZXBzd/wPAAjVAoxeSgNeAAAAAElFTkSuQmCC) right repeat-y}",e("../lib/dom").importCssString(a.cssText,a.cssClass)})),ace.require(["ace/theme/monokai"],(function(c){"object"==o(e)&&"object"==o(a)&&e&&(e.exports=c)}))}).call(this,o("YuTi")(e))},YuTi:function(e,a){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}}},[[15,0]]]);

File diff suppressed because one or more lines are too long

2
public/js/vendor.js vendored

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -1,32 +1,33 @@
{
"/js/manifest.js": "/js/manifest.js?id=7db827d654313dce4250",
"/js/vendor.js": "/js/vendor.js?id=d6ba75064c2942474259",
"/js/vendor.js": "/js/vendor.js?id=b9760eb405c3c32b09cf",
"/js/ace.js": "/js/ace.js?id=11e5550a450fece75c33",
"/js/activity.js": "/js/activity.js?id=838e242ddbe91e559f00",
"/js/app.js": "/js/app.js?id=049fdddbb594117f646e",
"/js/activity.js": "/js/activity.js?id=85479715e399b3489d25",
"/js/app.js": "/js/app.js?id=1c3620016c676056663c",
"/css/app.css": "/css/app.css?id=77729cabd5c8a0ad09b8",
"/css/appdark.css": "/css/appdark.css?id=995ec87dd4aff426cd1c",
"/css/landing.css": "/css/landing.css?id=3092e86721fa8b922c06",
"/css/quill.css": "/css/quill.css?id=e3741782d15a3031f785",
"/js/collectioncompose.js": "/js/collectioncompose.js?id=3fd79944492361ec7347",
"/js/collections.js": "/js/collections.js?id=38be4150f3d2ebb15f50",
"/js/collectioncompose.js": "/js/collectioncompose.js?id=72e8fa48c00604a44eb9",
"/js/collections.js": "/js/collections.js?id=915db7c1d080d61edcc9",
"/js/components.js": "/js/components.js?id=88296701f1382d285031",
"/js/compose.js": "/js/compose.js?id=661030734f9228389822",
"/js/compose.js": "/js/compose.js?id=e74fdb55681c6d21f4d5",
"/js/compose-classic.js": "/js/compose-classic.js?id=283f19c895f4118a2a8b",
"/js/developers.js": "/js/developers.js?id=f75deca5ccf47d43eb07",
"/js/direct.js": "/js/direct.js?id=6a80114ecc453b2b994a",
"/js/discover.js": "/js/discover.js?id=dd07b617f5f5f4714164",
"/js/hashtag.js": "/js/hashtag.js?id=eb4ce037114cd78a8678",
"/js/direct.js": "/js/direct.js?id=e1e4a830bfedc1870db1",
"/js/discover.js": "/js/discover.js?id=87071f23fc6d7118c66a",
"/js/hashtag.js": "/js/hashtag.js?id=4ebd78fde7fe65f5772c",
"/js/loops.js": "/js/loops.js?id=1dcb3790eb9ea4ea5848",
"/js/mode-dot.js": "/js/mode-dot.js?id=d54ad862baf30ee756f8",
"/js/profile.js": "/js/profile.js?id=746f776639e98cd4102a",
"/js/profile-directory.js": "/js/profile-directory.js?id=3a3be60e979308f9958e",
"/js/quill.js": "/js/quill.js?id=e47c5b9f47415104bb6a",
"/js/rempos.js": "/js/rempos.js?id=0c655b6b8dca92d06c79",
"/js/rempro.js": "/js/rempro.js?id=d26f22f402cd6aab5f20",
"/js/search.js": "/js/search.js?id=2af4d1de55be62523b71",
"/js/status.js": "/js/status.js?id=142b5d5d938c67abdcf6",
"/js/story-compose.js": "/js/story-compose.js?id=6308c4b7cb5baa4517d3",
"/js/theme-monokai.js": "/js/theme-monokai.js?id=ece67a5ba28761df4d88",
"/js/timeline.js": "/js/timeline.js?id=97f92ccb18f86c57dfad"
"/js/memoryprofile.js": "/js/memoryprofile.js?id=828e26d71b4330fedc1b",
"/js/mode-dot.js": "/js/mode-dot.js?id=dd9c87024fbaa8e75ac4",
"/js/profile.js": "/js/profile.js?id=559d809df9a10adc091a",
"/js/profile-directory.js": "/js/profile-directory.js?id=855a548efdf56b8594bf",
"/js/quill.js": "/js/quill.js?id=866b31b9b9540305751d",
"/js/rempos.js": "/js/rempos.js?id=29dacb6e0ce2c936e5cd",
"/js/rempro.js": "/js/rempro.js?id=6e7fc68a65bf82d5aec8",
"/js/search.js": "/js/search.js?id=5467143312ebae07695b",
"/js/status.js": "/js/status.js?id=70f5597c1069d7ead4bb",
"/js/story-compose.js": "/js/story-compose.js?id=13f9606a3c4ce7acf17b",
"/js/theme-monokai.js": "/js/theme-monokai.js?id=8842103833ba4861bcfa",
"/js/timeline.js": "/js/timeline.js?id=8b144724e85e8bef50bf"
}

Wyświetl plik

@ -6,6 +6,7 @@ require('bootstrap');
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
require('readmore-js');
window.blurhash = require("blurhash");
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {

Wyświetl plik

@ -635,15 +635,23 @@ export default {
methods: {
fetchProfile() {
let self = this;
axios.get('/api/pixelfed/v1/accounts/verify_credentials').then(res => {
self.profile = res.data;
window.pixelfed.currentUser = res.data;
if(res.data.locked == true) {
self.visibility = 'private';
self.visibilityTag = 'Followers Only';
if(window._sharedData.curUser) {
self.profile = window._sharedData.curUser;
if(self.profile.locked == true) {
self.visibility = 'private';
self.visibilityTag = 'Followers Only';
}
}).catch(err => {
});
} else {
axios.get('/api/pixelfed/v1/accounts/verify_credentials').then(res => {
self.profile = res.data;
window.pixelfed.currentUser = res.data;
if(res.data.locked == true) {
self.visibility = 'private';
self.visibilityTag = 'Followers Only';
}
}).catch(err => {
});
}
},
addMedia(event) {

Wyświetl plik

@ -0,0 +1,130 @@
@extends('layouts.blank')
@section('content')
<div class="container mt-5">
<div class="row">
<div class="col-12 col-md-6 offset-md-3 text-center">
<p class="h1 pb-2" style="font-weight: 200">Your Post Contains Sensitive or Offensive Material</p>
<p class="lead py-3">We applied a Content Warning to your post because it doesn't follow our <a class="font-weight-bold text-dark" href="{{route('help.community-guidelines')}}">Community Guidelines</a>.</p>
<p class="font-weight-bold alert alert-danger text-left">To continue you must click the "I Understand" button or "REQUEST APPEAL" button at the bottom of this page.</p>
</div>
<div class="col-12 col-md-6 offset-md-3">
<hr>
</div>
<div class="col-12 col-md-6 offset-md-3 mt-3">
<p class="h4 font-weight-bold">Post Details</p>
@if($interstitial->has_media)
<div class="py-4 align-items-center">
<div class="d-block text-center text-truncate">
@if($interstitial->blurhash)
<canvas id="mblur" width="400" height="400" class="rounded shadow"></canvas>
@else
<img src="/storage/no-preview.png" class="mr-3 img-fluid" alt="No preview available">
@endif
</div>
<div class="mt-2 border rounded p-3">
@if($meta->caption)
<p class="text-break">
Caption: <span class="font-weight-bold">{{$meta->caption}}</span>
</p>
@endif
<p class="mb-0">
Like Count: <span class="font-weight-bold">{{$meta->likes_count}}</span>
</p>
<p class="mb-0">
Share Count: <span class="font-weight-bold">{{$meta->reblogs_count}}</span>
</p>
<p class="">
Timestamp: <span class="font-weight-bold">{{now()->parse($meta->created_at)->format('r')}}</span>
</p>
<p class="mb-0" style="word-break: break-all !important;">
URL: <span class="font-weight-bold text-primary">{{$meta->url}}</span>
</p>
</div>
</div>
@else
<div class="py-4 align-items-center">
<div class="mt-2 border rounded p-3">
@if($meta->caption)
<p class="text-break">
Comment: <span class="font-weight-bold">{{$meta->caption}}</span>
</p>
@endif
<p class="mb-0">
Like Count: <span class="font-weight-bold">{{$meta->likes_count}}</span>
</p>
<p class="mb-0">
Share Count: <span class="font-weight-bold">{{$meta->reblogs_count}}</span>
</p>
<p class="">
Timestamp: <span class="font-weight-bold">{{now()->parse($meta->created_at)->format('r')}}</span>
</p>
<p class="mb-0" style="word-break: break-all !important;">
URL: <span class="font-weight-bold text-primary">{{$meta->url}}</span>
</p>
</div>
</div>
@endif
</div>
<div class="col-12 col-md-6 offset-md-3 my-3">
<div class="border rounded p-3 border-primary">
<p class="h4 font-weight-bold pt-2 text-primary">Review the Community Guidelines</p>
<p class="lead pt-4 text-primary">We want to keep {{config('app.name')}} a safe place for everyone, and we created these <a class="font-weight-bold text-primary" href="{{route('help.community-guidelines')}}">Community Guidelines</a> to support and protect our community.</p>
</div>
</div>
<div id="appealButton" class="col-12 col-md-6 offset-md-3 mt-3">
<button type="button" class="btn btn-outline-primary btn-block font-weight-bold" onclick="requestAppeal()">REQUEST APPEAL</button>
</div>
<div id="appealForm" class="col-12 col-md-6 offset-md-3 d-none mt-3">
<form method="post" action="/i/warning">
@csrf
<p class="h4 font-weight-bold">Request Appeal</p>
<p class="pt-4">
<div class="form-group">
<textarea class="form-control" rows="4" placeholder="Write your appeal request message here" name="appeal_message"></textarea>
</div>
</p>
{{-- <p class="lead"><span class="font-weight-bold">Learn more</span> about what we remove.</p> --}}
<input type="hidden" name="id" value="{{encrypt($interstitial->id)}}">
<input type="hidden" name="type" value="{{$interstitial->type}}">
<input type="hidden" name="action" value="appeal">
<button type="submit" class="btn btn-outline-primary btn-block font-weight-bold">REQUEST APPEAL</button>
</form>
</div>
<div class="col-12 col-md-6 offset-md-3 mt-4 mb-4">
<form method="post" action="/i/warning">
@csrf
<input type="hidden" name="id" value="{{encrypt($interstitial->id)}}">
<input type="hidden" name="type" value="{{$interstitial->type}}">
<input type="hidden" name="action" value="confirm">
<button type="submit" class="btn btn-primary btn-block font-weight-bold">I Understand</button>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function requestAppeal() {
$('#appealButton').addClass('d-none');
$('#appealForm').removeClass('d-none');
}
</script>
@if($interstitial->blurhash)
<script type="text/javascript">
const pixels = window.blurhash.decode("{{$interstitial->blurhash}}", 400, 400);
const canvas = document.getElementById("mblur");
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(400, 400);
imageData.data.set(pixels);
ctx.putImageData(imageData, 0, 0);
</script>
@endif
@endpush

Wyświetl plik

@ -0,0 +1,99 @@
@extends('layouts.blank')
@section('content')
<div class="container mt-5">
<div class="row">
<div class="col-12 col-md-6 offset-md-3 text-center">
<p class="h1 pb-2" style="font-weight: 200">Your Post Has Been Deleted</p>
<p class="lead py-1">We removed your post because it doesn't follow our <a class="font-weight-bold text-dark" href="{{route('help.community-guidelines')}}">Community Guidelines</a>. If you violate our guidelines again, your account may be restricted or disabled.</p>
<p class="font-weight-bold alert alert-danger text-left">To continue you must click the "I Understand" button at the bottom of this page.</p>
</div>
<div class="col-12 col-md-6 offset-md-3">
<hr>
</div>
<div class="col-12 col-md-6 offset-md-3 mt-3">
<p class="h4 font-weight-bold">Post Details</p>
@if($interstitial->has_media)
<div class="py-4 align-items-center">
<div class="d-block text-center text-truncate">
@if($interstitial->blurhash)
<canvas id="mblur" width="400" height="400" class="rounded shadow"></canvas>
@else
<img src="/storage/no-preview.png" class="mr-3 img-fluid" alt="No preview available">
@endif
</div>
<div class="mt-2 border rounded p-3">
@if($meta->caption)
<p class="text-break">
Caption: <span class="font-weight-bold">{{$meta->caption}}</span>
</p>
@endif
<p class="mb-0">
Like Count: <span class="font-weight-bold">{{$meta->likes_count}}</span>
</p>
<p class="mb-0">
Share Count: <span class="font-weight-bold">{{$meta->reblogs_count}}</span>
</p>
<p class="">
Timestamp: <span class="font-weight-bold">{{now()->parse($meta->created_at)->format('r')}}</span>
</p>
<p class="mb-0" style="word-break: break-all !important;">
URL: <span class="font-weight-bold text-primary">{{$meta->url}}</span>
</p>
</div>
</div>
@else
<div class="media py-4 align-items-center">
<div class="media-body ml-2">
<p class="">
Comment: <span class="lead text-break font-weight-bold">{{$meta->caption}}</span>
</p>
<p class="mb-0 small">
Posted on {{$meta->created_at}}
</p>
<p class="mb-0 font-weight-bold text-primary">
{{$meta->url}}
</p>
</div>
</div>
@endif
</div>
<div class="col-12 col-md-6 offset-md-3 my-3">
<div class="border rounded p-3 border-primary">
<p class="h4 font-weight-bold pt-2 text-primary">Review the Community Guidelines</p>
<p class="lead pt-4 text-primary">We want to keep {{config('app.name')}} a safe place for everyone, and we created these <a class="font-weight-bold text-primary" href="{{route('help.community-guidelines')}}">Community Guidelines</a> to support and protect our community.</p>
</div>
</div>
<div class="col-12 col-md-6 offset-md-3 mt-4 mb-5">
<form method="post" action="/i/warning">
@csrf
<input type="hidden" name="id" value="{{encrypt($interstitial->id)}}">
<input type="hidden" name="type" value="{{$interstitial->type}}">
<input type="hidden" name="action" value="confirm">
<button type="submit" class="btn btn-primary btn-block font-weight-bold">I Understand</button>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function requestAppeal() {
$('#appealButton').addClass('d-none');
$('#appealForm').removeClass('d-none');
}
</script>
@if($interstitial->blurhash)
<script type="text/javascript">
const pixels = window.blurhash.decode("{{$interstitial->blurhash}}", 400, 400);
const canvas = document.getElementById("mblur");
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(400, 400);
imageData.data.set(pixels);
ctx.putImageData(imageData, 0, 0);
</script>
@endif
@endpush

Wyświetl plik

@ -0,0 +1,128 @@
@extends('layouts.blank')
@section('content')
<div class="container mt-5">
<div class="row">
<div class="col-12 col-md-6 offset-md-3 text-center">
<p class="h1 pb-2" style="font-weight: 200">Your Post Was Unlisted</p>
<p class="lead py-3">We removed your post from public timelines because it doesn't follow our <a class="font-weight-bold text-dark" href="{{route('help.community-guidelines')}}">Community Guidelines</a>.</p>
</div>
<div class="col-12 col-md-6 offset-md-3">
<hr>
</div>
<div class="col-12 col-md-6 offset-md-3">
<p class="h4 font-weight-bold">Post Details</p>
@if($interstitial->has_media)
<div class="py-4 align-items-center">
<div class="d-block text-center text-truncate">
@if($interstitial->blurhash)
<canvas id="mblur" width="400" height="400" class="rounded shadow"></canvas>
@else
<img src="/storage/no-preview.png" class="mr-3 img-fluid" alt="No preview available">
@endif
</div>
<div class="mt-2 border rounded p-3">
@if($meta->caption)
<p class="text-break">
Caption: <span class="font-weight-bold">{{$meta->caption}}</span>
</p>
@endif
<p class="mb-0">
Like Count: <span class="font-weight-bold">{{$meta->likes_count}}</span>
</p>
<p class="mb-0">
Share Count: <span class="font-weight-bold">{{$meta->reblogs_count}}</span>
</p>
<p class="">
Timestamp: <span class="font-weight-bold">{{now()->parse($meta->created_at)->format('r')}}</span>
</p>
<p class="mb-0" style="word-break: break-all !important;">
URL: <span class="font-weight-bold text-primary">{{$meta->url}}</span>
</p>
</div>
</div>
@else
<div class="py-4 align-items-center">
<div class="mt-2 border rounded p-3">
@if($meta->caption)
<p class="text-break">
Comment: <span class="font-weight-bold">{{$meta->caption}}</span>
</p>
@endif
<p class="mb-0">
Like Count: <span class="font-weight-bold">{{$meta->likes_count}}</span>
</p>
<p class="mb-0">
Share Count: <span class="font-weight-bold">{{$meta->reblogs_count}}</span>
</p>
<p class="">
Timestamp: <span class="font-weight-bold">{{now()->parse($meta->created_at)->format('r')}}</span>
</p>
<p class="mb-0" style="word-break: break-all !important;">
URL: <span class="font-weight-bold text-primary">{{$meta->url}}</span>
</p>
</div>
</div>
@endif
</div>
<div class="col-12 col-md-6 offset-md-3 my-3">
<div class="border rounded p-3 border-primary">
<p class="h4 font-weight-bold pt-2 text-primary">Review the Community Guidelines</p>
<p class="lead pt-4 text-primary">We want to keep {{config('app.name')}} a safe place for everyone, and we created these <a class="font-weight-bold text-primary" href="{{route('help.community-guidelines')}}">Community Guidelines</a> to support and protect our community.</p>
</div>
</div>
<div id="appealButton" class="col-12 col-md-6 offset-md-3">
<button type="button" class="btn btn-outline-primary btn-block font-weight-bold" onclick="requestAppeal()">REQUEST APPEAL</button>
</div>
<div id="appealForm" class="col-12 col-md-6 offset-md-3 d-none">
<form method="post" action="/i/warning">
@csrf
<p class="h4 font-weight-bold">Request Appeal</p>
<p class="pt-4">
<div class="form-group">
<textarea class="form-control" rows="4" placeholder="Write your appeal request message here" name="appeal_message"></textarea>
</div>
</p>
<input type="hidden" name="id" value="{{encrypt($interstitial->id)}}">
<input type="hidden" name="type" value="{{$interstitial->type}}">
<input type="hidden" name="action" value="appeal">
<button type="submit" class="btn btn-outline-primary btn-block font-weight-bold">REQUEST APPEAL</button>
</form>
</div>
<div class="col-12 col-md-6 offset-md-3 mt-4 mb-4">
<form method="post" action="/i/warning">
@csrf
<input type="hidden" name="id" value="{{encrypt($interstitial->id)}}">
<input type="hidden" name="type" value="{{$interstitial->type}}">
<input type="hidden" name="action" value="confirm">
<button type="submit" class="btn btn-primary btn-block font-weight-bold">I Understand</button>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function requestAppeal() {
$('#appealButton').addClass('d-none');
$('#appealForm').removeClass('d-none');
}
</script>
@if($interstitial->blurhash)
<script type="text/javascript">
const pixels = window.blurhash.decode("{{$interstitial->blurhash}}", 400, 400);
const canvas = document.getElementById("mblur");
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(400, 400);
imageData.data.set(pixels);
ctx.putImageData(imageData, 0, 0);
</script>
@endif
@endpush

Wyświetl plik

@ -0,0 +1,63 @@
@extends('admin.partial.template-full')
@section('section')
<div class="title mb-3">
<h3 class="font-weight-bold d-inline-block">Appeals</h3>
<span class="float-right">
</span>
</div>
<div class="row">
<div class="col-12 col-md-3 mb-3">
<div class="card border bg-primary text-white rounded-pill shadow">
<div class="card-body pl-4 ml-3">
<p class="h1 font-weight-bold mb-1" style="font-weight: 700">{{App\AccountInterstitial::whereNull('appeal_handled_at')->whereNotNull('appeal_requested_at')->count()}}</p>
<p class="lead mb-0 font-weight-lighter">active appeals</p>
</div>
</div>
<div class="mt-3 card border bg-warning text-dark rounded-pill shadow">
<div class="card-body pl-4 ml-3">
<p class="h1 font-weight-bold mb-1" style="font-weight: 700">{{App\AccountInterstitial::whereNotNull('appeal_handled_at')->whereNotNull('appeal_requested_at')->count()}}</p>
<p class="lead mb-0 font-weight-lighter">closed appeals</p>
</div>
</div>
</div>
<div class="col-12 col-md-8 offset-md-1">
<ul class="list-group">
@if($appeals->count() == 0)
<li class="list-group-item text-center py-5">
<p class="mb-0 py-5 font-weight-bold">No appeals found!</p>
</li>
@endif
@foreach($appeals as $appeal)
<a class="list-group-item text-decoration-none text-dark" href="/i/admin/reports/appeal/{{$appeal->id}}">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<img src="{{$appeal->has_media ? $appeal->status->thumb(true) : '/storage/no-preview.png'}}" width="64" height="64" class="rounded border">
<div class="ml-2">
<span class="d-inline-block text-truncate">
<p class="mb-0 small font-weight-bold text-primary">{{$appeal->type}}</p>
@if($appeal->item_type)
<p class="mb-0 font-weight-bold">{{starts_with($appeal->item_type, 'App\\') ? explode('\\',$appeal->item_type)[1] : $appeal->item_type}}</p>
@endif
</span>
</div>
</div>
<div class="d-block">
<p class="mb-0 font-weight-bold">&commat;{{$appeal->user->username}}</p>
<p class="mb-0 small text-muted font-weight-bold">{{$appeal->created_at->diffForHumans(null, null, true)}}</p>
</div>
<div class="d-inline-block">
<p class="mb-0 small">
<i class="fas fa-chevron-right fa-2x text-lighter"></i>
</p>
</div>
</div>
</a>
@endforeach
</ul>
<p>{!!$appeals->render()!!}</p>
</div>
</div>
@endsection

Wyświetl plik

@ -15,6 +15,15 @@
</a>
</span>
</div>
@php($ai = App\AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count())
@if($ai)
<div class="mb-4">
<a class="btn btn-outline-primary px-5 py-3" href="/i/admin/reports/appeals">
<p class="font-weight-bold h4 mb-0">{{$ai}}</p>
Appeal {{$ai == 1 ? 'Request' : 'Requests'}}
</a>
</div>
@endif
@if($reports->count())
<div class="card shadow-none border">
<div class="list-group list-group-flush">

Wyświetl plik

@ -0,0 +1,125 @@
@extends('admin.partial.template-full')
@section('section')
<div class="d-flex justify-content-between title mb-3">
<div>
<p class="font-weight-bold h3">Moderation Appeal</p>
<p class="text-muted mb-0 lead">From <a href="{{$appeal->user->url()}}" class="text-muted font-weight-bold">&commat;{{$appeal->user->username}}</a> about {{$appeal->appeal_requested_at->diffForHumans()}}.</p>
</div>
<div>
</div>
</div>
<div class="row">
<div class="col-12 col-md-8 mt-3">
@if($appeal->type == 'post.cw')
<div class="card shadow-none border">
<div class="card-header bg-light h5 font-weight-bold py-4">Content Warning applied to {{$appeal->has_media ? 'Post' : 'Comment'}}</div>
@if($appeal->has_media)
<img class="card-img-top border-bottom" src="{{$appeal->status->thumb(true)}}">
@endif
<div class="card-body">
<div class="mt-2 p-3">
@if($meta->caption)
<p class="text-break">
{{$appeal->has_media ? 'Caption' : 'Comment'}}: <span class="font-weight-bold">{{$meta->caption}}</span>
</p>
@endif
<p class="mb-0">
Like Count: <span class="font-weight-bold">{{$meta->likes_count}}</span>
</p>
<p class="mb-0">
Share Count: <span class="font-weight-bold">{{$meta->reblogs_count}}</span>
</p>
<p class="mb-0">
Timestamp: <span class="font-weight-bold">{{now()->parse($meta->created_at)->format('r')}}</span>
</p>
<p class="" style="word-break: break-all !important;">
URL: <span class="font-weight-bold text-primary"><a href="{{$meta->url}}">{{$meta->url}}</a></span>
</p>
<p class="mb-0">
Message: <span class="font-weight-bold">{{$appeal->appeal_message}}</span>
</p>
</div>
</div>
</div>
@elseif($appeal->type == 'post.unlist')
<div class="card shadow-none border">
<div class="card-header bg-light h5 font-weight-bold py-4">{{$appeal->has_media ? 'Post' : 'Comment'}} was unlisted from timelines</div>
@if($appeal->has_media)
<img class="card-img-top border-bottom" src="{{$appeal->status->thumb(true)}}">
@endif
<div class="card-body">
<div class="mt-2 p-3">
@if($meta->caption)
<p class="text-break">
{{$appeal->has_media ? 'Caption' : 'Comment'}}: <span class="font-weight-bold">{{$meta->caption}}</span>
</p>
@endif
<p class="mb-0">
Like Count: <span class="font-weight-bold">{{$meta->likes_count}}</span>
</p>
<p class="mb-0">
Share Count: <span class="font-weight-bold">{{$meta->reblogs_count}}</span>
</p>
<p class="mb-0">
Timestamp: <span class="font-weight-bold">{{now()->parse($meta->created_at)->format('r')}}</span>
</p>
<p class="" style="word-break: break-all !important;">
URL: <span class="font-weight-bold text-primary"><a href="{{$meta->url}}">{{$meta->url}}</a></span>
</p>
<p class="mb-0">
Message: <span class="font-weight-bold">{{$appeal->appeal_message}}</span>
</p>
</div>
</div>
</div>
@endif
</div>
<div class="col-12 col-md-4 mt-3">
<form method="post">
@csrf
<input type="hidden" name="action" value="dismiss">
<button type="submit" class="btn btn-primary btn-block font-weight-bold mb-3">Dismiss Appeal Request</button>
</form>
<button type="button" class="btn btn-light border btn-block font-weight-bold mb-3" onclick="approveWarning()">Approve Appeal</button>
<div class="card shadow-none border mt-5">
<div class="card-header text-center font-weight-bold bg-light">
&commat;{{$appeal->user->username}} stats
</div>
<div class="card-body">
<p class="">
Open Appeals: <span class="font-weight-bold">{{App\AccountInterstitial::whereUserId($appeal->user_id)->whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count()}}</span>
</p>
<p class="">
Total Appeals: <span class="font-weight-bold">{{App\AccountInterstitial::whereUserId($appeal->user_id)->whereNotNull('appeal_requested_at')->count()}}</span>
</p>
<p class="">
Total Warnings: <span class="font-weight-bold">{{App\AccountInterstitial::whereUserId($appeal->user_id)->count()}}</span>
</p>
<p class="">
Status Count: <span class="font-weight-bold">{{$appeal->user->statuses()->count()}}</span>
</p>
<p class="mb-0">
Joined: <span class="font-weight-bold">{{$appeal->user->created_at->diffForHumans(null, null, false)}}</span>
</p>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function approveWarning() {
if(window.confirm('Are you sure you want to approve this appeal?') == true) {
axios.post(window.location.href, {
action: 'approve'
}).then(res => {
window.location.href = '/i/admin/reports/appeals';
}).catch(err => {
swal('Oops!', 'An error occured, please try again later.', 'error');
});
}
}
</script>
@endpush

Wyświetl plik

@ -8,6 +8,5 @@
@push('scripts')
<script type="text/javascript" src="{{ mix('js/discover.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">App.boot();</script>
@endpush

Wyświetl plik

@ -0,0 +1,35 @@
@extends('layouts.app',['title' => $profile->username . " on " . config('app.name')])
@section('content')
@if (session('error'))
<div class="alert alert-danger text-center font-weight-bold mb-0">
{{ session('error') }}
</div>
@endif
<memory-profile profile-id="{{$profile->id}}" profile-username="{{$profile->username}}" :profile-settings="{{json_encode($settings)}}" profile-layout="{{$profile->profile_layout ?? 'metro'}}"></memory-profile>
@if($profile->website)
<a class="d-none" href="{{$profile->website}}" rel="me">{{$profile->website}}</a>
@endif
<noscript>
<div class="container">
<p class="pt-5 text-center lead">Please enable javascript to view this content.</p>
</div>
</noscript>
@endsection
@push('meta')<meta property="og:description" content="{{$profile->bio}}">
@if(false == $settings['crawlable'] || $profile->remote_url)
<meta name="robots" content="noindex, nofollow">
@else <meta property="og:image" content="{{$profile->avatarUrl()}}">
<link href="{{$profile->permalink('.atom')}}" rel="alternate" title="{{$profile->username}} on Pixelfed" type="application/atom+xml">
<link href='{{$profile->permalink()}}' rel='alternate' type='application/activity+json'>
@endif
@endpush
@push('scripts')<script type="text/javascript" src="{{ mix('js/memoryprofile.js') }}"></script>
<script type="text/javascript" defer>App.boot();</script>
@endpush

Wyświetl plik

@ -8,6 +8,9 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio
Route::get('reports/show/{id}', 'AdminController@showReport');
Route::post('reports/show/{id}', 'AdminController@updateReport');
Route::post('reports/bulk', 'AdminController@bulkUpdateReport');
Route::get('reports/appeals', 'AdminController@appeals');
Route::get('reports/appeal/{id}', 'AdminController@showAppeal');
Route::post('reports/appeal/{id}', 'AdminController@updateAppeal');
Route::redirect('statuses', '/statuses/list');
Route::get('statuses/list', 'AdminController@statuses')->name('admin.statuses');
Route::get('statuses/show/{id}', 'AdminController@showStatus');
@ -73,7 +76,7 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio
Route::post('newsroom/create', 'AdminController@newsroomStore');
});
Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofactor', 'localization'])->group(function () {
Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofactor', 'localization','interstitial'])->group(function () {
Route::get('/', 'SiteController@home')->name('timeline.personal');
Route::post('/', 'StatusController@store');
@ -125,6 +128,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::get('discover/tag', 'DiscoverController@getHashtags');
Route::post('status/compose', 'InternalApiController@composePost')->middleware('throttle:maxPostsPerHour,60')->middleware('throttle:maxPostsPerDay,1440');
});
Route::group(['prefix' => 'pixelfed'], function() {
Route::group(['prefix' => 'v1'], function() {
Route::get('accounts/verify_credentials', 'ApiController@verifyCredentials');
@ -146,6 +150,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::get('timelines/home', 'PublicApiController@homeTimelineApi');
Route::get('newsroom/timeline', 'NewsroomController@timelineApi');
Route::post('newsroom/markasread', 'NewsroomController@markAsRead');
Route::get('favourites', 'Api\BaseApiController@accountLikes');
});
Route::group(['prefix' => 'v2'], function() {
@ -169,6 +174,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::get('discover/posts/places', 'DiscoverController@trendingPlaces');
});
});
Route::group(['prefix' => 'local'], function () {
// Route::get('accounts/verify_credentials', 'ApiController@verifyCredentials');
// Route::get('accounts/relationships', 'PublicApiController@relationships');
@ -295,6 +301,9 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::get('redirect', 'SiteController@redirectUrl');
Route::post('admin/media/block/add', 'MediaBlocklistController@add');
Route::post('admin/media/block/delete', 'MediaBlocklistController@delete');
Route::get('warning', 'AccountInterstitialController@get');
Route::post('warning', 'AccountInterstitialController@read');
});
Route::group(['prefix' => 'account'], function () {