diff --git a/CHANGELOG.md b/CHANGELOG.md index 99003abc..eea05762 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,12 @@ - Updated DiscoverController, fixes #2009 ([b04c7170](https://github.com/pixelfed/pixelfed/commit/b04c7170)) - Updated DeleteAccountPipeline, fixes [#2016](https://github.com/pixelfed/pixelfed/issues/2016), a bug affecting account deletion. - Updated PlaceController, fixes [#2017](https://github.com/pixelfed/pixelfed/issues/2017), a postgres bug affecting country pagination in the places directory ([dd5fa3a4](https://github.com/pixelfed/pixelfed/commit/dd5fa3a4)) +- Updated confirm email blade view, remove html5 entity that doesn't display properly ([aa26fa1d](https://github.com/pixelfed/pixelfed/commit/aa26fa1d)) +- Updated ApiV1Controller, fix update_credentials endpoint ([a73fad75](https://github.com/pixelfed/pixelfed/commit/a73fad75)) +- Updated AdminUserController, add moderation method ([a4cf21ea](https://github.com/pixelfed/pixelfed/commit/a4cf21ea)) +- Updated BaseApiController, invalidate session after account deletion ([826978ce](https://github.com/pixelfed/pixelfed/commit/826978ce)) +- Updated AdminUserController, add account deletion handler ([9be19ad8](https://github.com/pixelfed/pixelfed/commit/9be19ad8)) +- ([](https://github.com/pixelfed/pixelfed/commit/)) ## [v0.10.8 (2020-01-29)](https://github.com/pixelfed/pixelfed/compare/v0.10.7...v0.10.8) ### Added diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 37ccbba3..dac8078a 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -270,7 +270,6 @@ class AccountController extends Controller return redirect()->back(); } - public function unblock(Request $request) { $this->validate($request, [ @@ -362,6 +361,13 @@ class AccountController extends Controller public function sudoMode(Request $request) { + if($request->session()->has('sudoModeAttempts') && $request->session()->get('sudoModeAttempts') >= 3) { + $request->session()->pull('2fa.session.active'); + $request->session()->pull('redirectNext'); + $request->session()->pull('sudoModeAttempts'); + Auth::logout(); + return redirect(route('login')); + } return view('auth.sudo'); } @@ -373,6 +379,12 @@ class AccountController extends Controller $user = Auth::user(); $password = $request->input('password'); $next = $request->session()->get('redirectNext', '/'); + if($request->session()->has('sudoModeAttempts')) { + $count = (int) $request->session()->get('sudoModeAttempts'); + $request->session()->put('sudoModeAttempts', $count + 1); + } else { + $request->session()->put('sudoModeAttempts', 1); + } if(password_verify($password, $user->password) === true) { $request->session()->put('sudoMode', time()); return redirect($next); diff --git a/app/Http/Controllers/Admin/AdminUserController.php b/app/Http/Controllers/Admin/AdminUserController.php new file mode 100644 index 00000000..a5f19f4d --- /dev/null +++ b/app/Http/Controllers/Admin/AdminUserController.php @@ -0,0 +1,284 @@ +query('col') ?? 'id'; + $dir = $request->query('dir') ?? 'desc'; + $users = User::select('id', 'username', 'status') + ->withCount('statuses') + ->orderBy($col, $dir) + ->simplePaginate(10); + + return view('admin.users.home', compact('users')); + } + + public function userShow(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + return view('admin.users.show', compact('user', 'profile')); + } + + public function userEdit(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + return view('admin.users.edit', compact('user', 'profile')); + } + + public function userEditSubmit(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + $changed = false; + $fields = []; + + if($request->filled('name') && $request->input('name') != $user->name) { + $fields['name'] = ['old' => $user->name, 'new' => $request->input('name')]; + $user->name = $profile->name = $request->input('name'); + $changed = true; + } + if($request->filled('username') && $request->input('username') != $user->username) { + $fields['username'] = ['old' => $user->username, 'new' => $request->input('username')]; + $user->username = $profile->username = $request->input('username'); + $changed = true; + } + if($request->filled('email') && $request->input('email') != $user->email) { + if(filter_var($request->input('email'), FILTER_VALIDATE_EMAIL) == false) { + abort(500, 'Invalid email address'); + } + $fields['email'] = ['old' => $user->email, 'new' => $request->input('email')]; + $user->email = $request->input('email'); + $changed = true; + } + if($request->input('bio') != $profile->bio) { + $fields['bio'] = ['old' => $user->bio, 'new' => $request->input('bio')]; + $profile->bio = $request->input('bio'); + $changed = true; + } + if($request->input('website') != $profile->website) { + $fields['website'] = ['old' => $user->website, 'new' => $request->input('website')]; + $profile->website = $request->input('website'); + $changed = true; + } + + if($changed == true) { + ModLogService::boot() + ->objectUid($user->id) + ->objectId($user->id) + ->objectType('App\User::class') + ->user($request->user()) + ->action('admin.user.edit') + ->metadata([ + 'fields' => $fields + ]) + ->accessLevel('admin') + ->save(); + $profile->save(); + $user->save(); + } + + + return redirect('/i/admin/users/show/' . $user->id); + } + + public function userActivity(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + $logs = $user->accountLog()->orderByDesc('created_at')->paginate(10); + return view('admin.users.activity', compact('user', 'profile', 'logs')); + } + + public function userMessage(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + return view('admin.users.message', compact('user', 'profile')); + } + + public function userMessageSend(Request $request, $id) + { + $this->validate($request, [ + 'message' => 'required|string|min:5|max:500' + ]); + $user = User::findOrFail($id); + $profile = $user->profile; + $message = $request->input('message'); + Mail::to($user->email)->send(new AdminMessage($message)); + ModLogService::boot() + ->objectUid($user->id) + ->objectId($user->id) + ->objectType('App\User::class') + ->user($request->user()) + ->action('admin.user.mail') + ->metadata([ + 'message' => $message + ]) + ->accessLevel('admin') + ->save(); + return redirect('/i/admin/users/show/' . $user->id); + } + + public function userModTools(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + return view('admin.users.modtools', compact('user', 'profile')); + } + + public function userModLogs(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + $logs = ModLog::whereObjectUid($user->id) + ->orderByDesc('created_at') + ->simplePaginate(10); + return view('admin.users.modlogs', compact('user', 'profile', 'logs')); + } + + public function userModLogsMessage(Request $request, $id) + { + $this->validate($request, [ + 'message' => 'required|string|min:5|max:500' + ]); + $user = User::findOrFail($id); + $profile = $user->profile; + $msg = $request->input('message'); + ModLogService::boot() + ->objectUid($user->id) + ->objectId($user->id) + ->objectType('App\User::class') + ->user($request->user()) + ->message($msg) + ->accessLevel('admin') + ->save(); + return redirect('/i/admin/users/modlogs/' . $user->id); + } + + public function userDelete(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + return view('admin.users.delete', compact('user', 'profile')); + } + + public function userDeleteProcess(Request $request, $id) + { + $user = User::findOrFail($id); + $profile = $user->profile; + + if(config('pixelfed.account_deletion') == false) { + abort(404); + } + + if($user->is_admin == true) { + $mid = $request->user()->id; + abort_if($user->id < $mid, 403); + } + + $ts = now()->addMonth(); + $user->status = 'delete'; + $profile->status = 'delete'; + $user->delete_after = $ts; + $profile->delete_after = $ts; + $user->save(); + $profile->save(); + + ModLogService::boot() + ->objectUid($user->id) + ->objectId($user->id) + ->objectType('App\User::class') + ->user($request->user()) + ->action('admin.user.delete') + ->accessLevel('admin') + ->save(); + + Cache::forget('profiles:private'); + DeleteAccountPipeline::dispatch($user)->onQueue('high'); + + $msg = "Successfully deleted {$user->username}!"; + $request->session()->flash('status', $msg); + return redirect('/i/admin/users/list'); + } + + public function userModerate(Request $request) + { + $this->validate($request, [ + 'profile_id' => 'required|exists:profiles,id', + 'action' => 'required|in:cw,no_autolink,unlisted' + ]); + + $pid = $request->input('profile_id'); + $action = $request->input('action'); + $profile = Profile::findOrFail($pid); + + if($profile->user->is_admin == true) { + $mid = $request->user()->id; + abort_if($profile->user_id < $mid, 403); + } + + switch ($action) { + case 'cw': + $profile->cw = !$profile->cw; + $msg = "Success!"; + break; + + case 'no_autolink': + $profile->no_autolink = !$profile->no_autolink; + $msg = "Success!"; + break; + + case 'unlisted': + $profile->unlisted = !$profile->unlisted; + $msg = "Success!"; + break; + } + + $profile->save(); + + ModLogService::boot() + ->objectUid($profile->user_id) + ->objectId($profile->user_id) + ->objectType('App\User::class') + ->user($request->user()) + ->action('admin.user.moderate') + ->metadata([ + 'action' => $action, + 'message' => $msg + ]) + ->accessLevel('admin') + ->save(); + + $request->session()->flash('status', $msg); + return redirect('/i/admin/users/modtools/' . $profile->user_id); + } + + public function userModLogDelete(Request $request, $id) + { + $this->validate($request, [ + 'mid' => 'required|integer|exists:mod_logs,id' + ]); + $user = User::findOrFail($id); + $uid = $request->user()->id; + $mid = $request->input('mid'); + $ml = ModLog::whereUserId($uid)->findOrFail($mid)->delete(); + $msg = "Successfully deleted modlog comment!"; + $request->session()->flash('status', $msg); + return redirect('/i/admin/users/modlogs/' . $user->id); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 921312b3..f6b69343 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -21,7 +21,8 @@ use App\Http\Controllers\Admin\{ AdminReportController, AdminMediaController, AdminSettingsController, - AdminSupportController + AdminSupportController, + AdminUserController }; use Illuminate\Validation\Rule; use App\Services\AdminStatsService; @@ -32,11 +33,13 @@ class AdminController extends Controller AdminDiscoverController, AdminMediaController, AdminSettingsController, - AdminInstanceController; + AdminInstanceController, + AdminUserController; public function __construct() { $this->middleware('admin'); + $this->middleware('dangerzone'); $this->middleware('twofactor'); } @@ -46,25 +49,6 @@ class AdminController extends Controller return view('admin.home', compact('data')); } - public function users(Request $request) - { - $col = $request->query('col') ?? 'id'; - $dir = $request->query('dir') ?? 'desc'; - $users = User::select('id', 'username', 'status') - ->withCount('statuses') - ->orderBy($col, $dir) - ->simplePaginate(10); - - return view('admin.users.home', compact('users')); - } - - public function editUser(Request $request, $id) - { - $user = User::findOrFail($id); - $profile = $user->profile; - return view('admin.users.edit', compact('user', 'profile')); - } - public function statuses(Request $request) { $statuses = Status::orderBy('id', 'desc')->simplePaginate(10); @@ -109,22 +93,25 @@ class AdminController extends Controller 'nullable', 'string', Rule::in(['all', 'local', 'remote']) - ], - 'limit' => 'nullable|integer|min:1|max:50' + ] ]); $search = $request->input('search'); $filter = $request->input('filter'); $limit = 12; - if($search) { - $profiles = Profile::select('id','username') - ->where('username', 'like', "%$search%") - ->orderBy('id','desc') + $profiles = Profile::select('id','username') + ->whereNull('status') + ->when($search, function($q, $search) { + return $q->where('username', 'like', "%$search%"); + })->when($filter, function($q, $filter) { + if($filter == 'local') { + return $q->whereNull('domain'); + } + if($filter == 'remote') { + return $q->whereNotNull('domain'); + } + return $q; + })->orderByDesc('id') ->simplePaginate($limit); - } else if($filter) { - $profiles = Profile::select('id','username')->withCount(['likes','statuses','followers'])->orderBy($filter, $order)->simplePaginate($limit); - } else { - $profiles = Profile::select('id','username')->orderBy('id','desc')->simplePaginate($limit); - } return view('admin.profiles.home', compact('profiles')); } diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index f414dcf9..8eb4163d 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -152,7 +152,7 @@ class ApiV1Controller extends Controller $this->validate($request, [ 'display_name' => 'nullable|string', 'note' => 'nullable|string', - 'locked' => 'nullable|boolean', + 'locked' => 'nullable', // 'source.privacy' => 'nullable|in:unlisted,public,private', // 'source.sensitive' => 'nullable|boolean' ]); diff --git a/app/Http/Controllers/Api/BaseApiController.php b/app/Http/Controllers/Api/BaseApiController.php index a3a450c6..6b18f835 100644 --- a/app/Http/Controllers/Api/BaseApiController.php +++ b/app/Http/Controllers/Api/BaseApiController.php @@ -314,6 +314,10 @@ class BaseApiController extends Controller { $user = $request->user(); abort_if(!$user, 403); + if($user->status != null) { + Auth::logout(); + return redirect('/login'); + } $resource = new Fractal\Resource\Item($user->profile, new AccountTransformer()); $res = $this->fractal->createData($resource)->toArray(); return response()->json($res); diff --git a/app/Http/Controllers/CollectionController.php b/app/Http/Controllers/CollectionController.php index 4fc7dfe6..e8354367 100644 --- a/app/Http/Controllers/CollectionController.php +++ b/app/Http/Controllers/CollectionController.php @@ -50,13 +50,13 @@ class CollectionController extends Controller return $request->all(); } - public function store(Request $request, int $id) + public function store(Request $request, $id) { abort_if(!Auth::check(), 403); $this->validate($request, [ 'title' => 'nullable', 'description' => 'nullable', - 'visibility' => 'required|alpha|in:public,private' + 'visibility' => 'nullable|string|in:public,private' ]); $profile = Auth::user()->profile; @@ -140,7 +140,7 @@ class CollectionController extends Controller return 200; } - public function get(Request $request, int $id) + public function get(Request $request, $id) { $profile = Auth::check() ? Auth::user()->profile : []; diff --git a/app/Http/Controllers/CommentController.php b/app/Http/Controllers/CommentController.php index ec8fb920..acf41df4 100644 --- a/app/Http/Controllers/CommentController.php +++ b/app/Http/Controllers/CommentController.php @@ -23,11 +23,7 @@ class CommentController extends Controller { public function showAll(Request $request, $username, int $id) { - $profile = Profile::whereNull(['status', 'domain'])->whereUsername($username)->firstOrFail(); - $status = Status::whereProfileId($profile->id)->findOrFail($id); - $replies = Status::whereInReplyToId($id)->simplePaginate(40); - - return view('status.comments', compact('profile', 'status', 'replies')); + abort(404); } public function store(Request $request) diff --git a/app/Http/Controllers/NewsroomController.php b/app/Http/Controllers/NewsroomController.php index 7c2f7adb..507ffeb2 100644 --- a/app/Http/Controllers/NewsroomController.php +++ b/app/Http/Controllers/NewsroomController.php @@ -65,7 +65,7 @@ class NewsroomController extends Controller ->map(function($post) { return [ 'id' => $post->id, - 'title' => Str::limit($post->title, 25), + 'title' => Str::limit($post->title, 40), 'summary' => $post->summary, 'url' => $post->show_link ? $post->permalink() : null, 'published_at' => $post->published_at->format('F m, Y') diff --git a/app/Http/Controllers/TimelineController.php b/app/Http/Controllers/TimelineController.php index df9fb5e5..52267b2d 100644 --- a/app/Http/Controllers/TimelineController.php +++ b/app/Http/Controllers/TimelineController.php @@ -20,11 +20,19 @@ class TimelineController extends Controller public function local(Request $request) { - return view('timeline.local'); + $this->validate($request, [ + 'layout' => 'nullable|string|in:grid,feed' + ]); + $layout = $request->input('layout', 'feed'); + return view('timeline.local', compact('layout')); } public function network(Request $request) { - return view('timeline.network'); + $this->validate($request, [ + 'layout' => 'nullable|string|in:grid,feed' + ]); + $layout = $request->input('layout', 'feed'); + return view('timeline.network', compact('layout')); } } diff --git a/app/Http/Middleware/DangerZone.php b/app/Http/Middleware/DangerZone.php index d1a1b4af..5a43d6e6 100644 --- a/app/Http/Middleware/DangerZone.php +++ b/app/Http/Middleware/DangerZone.php @@ -16,6 +16,12 @@ class DangerZone */ public function handle($request, Closure $next) { + if( $request->session()->get('sudoModeAttempts') > 3) { + $request->session()->pull('redirectNext'); + $request->session()->pull('sudoModeAttempts'); + Auth::logout(); + return redirect(route('login')); + } if(!Auth::check()) { return redirect(route('login')); } diff --git a/app/Mail/AdminMessage.php b/app/Mail/AdminMessage.php new file mode 100644 index 00000000..249f6694 --- /dev/null +++ b/app/Mail/AdminMessage.php @@ -0,0 +1,37 @@ +msg = $msg; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $admins = config('pixelfed.domain.app') . ' admins'; + return $this->markdown('emails.notification.admin_message') + ->with(['msg' => $this->msg]) + ->subject('Message from ' . $admins); + } +} diff --git a/app/ModLog.php b/app/ModLog.php new file mode 100644 index 00000000..0e0b2b5e --- /dev/null +++ b/app/ModLog.php @@ -0,0 +1,48 @@ +belongsTo(User::class, 'user_id'); + } + + public function actionToText() + { + $msg = 'Unknown action'; + + switch ($this->action) { + case 'admin.user.mail': + $msg = "Sent Message"; + break; + + case 'admin.user.action.cw.warn': + $msg = "Sent CW reminder"; + break; + + case 'admin.user.edit': + $msg = "Changed Profile"; + break; + + case 'admin.user.moderate': + $msg = "Moderation"; + break; + + case 'admin.user.delete': + $msg = "Deleted Account"; + break; + + default: + $msg = 'Unknown action'; + break; + } + + return $msg; + } +} diff --git a/app/Services/ModLogService.php b/app/Services/ModLogService.php new file mode 100644 index 00000000..cdbf7212 --- /dev/null +++ b/app/Services/ModLogService.php @@ -0,0 +1,98 @@ +log = new \StdClass; + } + + public static function boot() + { + return new self; + } + + public function user(User $user) + { + $this->log->user = $user; + return $this; + } + + public function objectUid($val = null) + { + $this->log->object_uid = $val; + return $this; + } + + public function objectId($val = null) + { + $this->log->object_id = $val; + return $this; + } + + public function objectType($val = null) + { + $this->log->object_type = $val; + return $this; + } + + public function action($val = null) + { + $this->log->action = $val; + return $this; + } + + public function message($val = null) + { + $this->log->message = $val; + return $this; + } + + public function metadata(array $val = null) + { + $this->log->metadata = json_encode($val); + return $this; + } + + public function accessLevel($val = null) + { + if(!in_array($val, ['admin', 'mod'])) { + return $this; + } + $this->log->access_level = $val; + return $this; + } + + public function save($res = false) + { + $log = $this->log; + if(!isset($log->user)) { + throw new \Exception('Invalid ModLog attribute.'); + } + + $ml = new ModLog(); + $ml->user_id = $log->user->id; + $ml->user_username = $log->user->username; + $ml->object_uid = $log->object_uid ?? null; + $ml->object_id = $log->object_id ?? null; + $ml->object_type = $log->object_type ?? null; + $ml->action = $log->action ?? null; + $ml->message = $log->message ?? null; + $ml->metadata = $log->metadata ?? null; + $ml->access_level = $log->access_level ?? 'admin'; + $ml->save(); + + if($res == true) { + return $ml; + } else { + return; + } + } +} \ No newline at end of file diff --git a/app/Transformer/Api/StoryItemTransformer.php b/app/Transformer/Api/StoryItemTransformer.php index 3d10cb97..1fbd9c37 100644 --- a/app/Transformer/Api/StoryItemTransformer.php +++ b/app/Transformer/Api/StoryItemTransformer.php @@ -12,14 +12,15 @@ class StoryItemTransformer extends Fractal\TransformerAbstract public function transform(StoryItem $item) { return [ - 'id' => (string) Str::uuid(), + 'id' => (string) $item->id, 'type' => $item->type, - 'length' => $item->duration, + 'length' => $item->duration != 0 ? $item->duration : 3, 'src' => $item->url(), 'preview' => null, 'link' => null, 'linkText' => null, - 'time' => $item->updated_at->format('U'), + 'time' => $item->created_at->format('U'), + 'expires_at' => $item->created_at->addHours(24)->format('U'), 'seen' => $item->story->seen(), ]; } diff --git a/app/Transformer/Api/StoryTransformer.php b/app/Transformer/Api/StoryTransformer.php index e362470c..cf19ed80 100644 --- a/app/Transformer/Api/StoryTransformer.php +++ b/app/Transformer/Api/StoryTransformer.php @@ -16,19 +16,16 @@ class StoryTransformer extends Fractal\TransformerAbstract return [ 'id' => (string) $story->id, 'photo' => $story->profile->avatarUrl(), - 'name' => '', - 'link' => '', + 'name' => $story->profile->username, + 'link' => $story->profile->url(), 'lastUpdated' => $story->updated_at->format('U'), 'seen' => $story->seen(), - 'items' => [], ]; } public function includeItems(Story $story) { - $items = $story->items; - - return $this->collection($items, new StoryItemTransformer()); + return $this->item($story, new StoryItemTransformer()); } } diff --git a/app/User.php b/app/User.php index 72963400..717c4f31 100644 --- a/app/User.php +++ b/app/User.php @@ -83,4 +83,9 @@ class User extends Authenticatable return 'profile:storage:used:' . $this->id; } + public function accountLog() + { + return $this->hasMany(AccountLog::class); + } + } diff --git a/app/Util/Site/Config.php b/app/Util/Site/Config.php index 09662508..8304b73e 100644 --- a/app/Util/Site/Config.php +++ b/app/Util/Site/Config.php @@ -10,6 +10,7 @@ class Config { public static function get() { return Cache::remember('api:site:configuration', now()->addMinutes(30), function() { return [ + 'open_registration' => config('pixelfed.open_registration'), 'uploader' => [ 'max_photo_size' => config('pixelfed.max_photo_size'), 'max_caption_length' => config('pixelfed.max_caption_length'), @@ -35,6 +36,7 @@ class Config { ], 'site' => [ + 'name' => config('app.name', 'pixelfed'), 'domain' => config('pixelfed.domain.app'), 'url' => config('app.url'), 'description' => config('instance.description') diff --git a/database/migrations/2020_02_14_063209_create_mod_logs_table.php b/database/migrations/2020_02_14_063209_create_mod_logs_table.php new file mode 100644 index 00000000..9443452f --- /dev/null +++ b/database/migrations/2020_02_14_063209_create_mod_logs_table.php @@ -0,0 +1,40 @@ +bigIncrements('id'); + $table->bigInteger('user_id')->unsigned()->index(); + $table->string('user_username')->nullable(); + $table->bigInteger('object_uid')->nullable()->unsigned()->index(); + $table->bigInteger('object_id')->nullable()->unsigned()->index(); + $table->string('object_type')->nullable()->index(); + $table->string('action')->nullable(); + $table->text('message')->nullable(); + $table->json('metadata')->nullable(); + $table->string('access_level')->default('admin')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('mod_logs'); + } +} diff --git a/resources/views/admin/users/activity.blade.php b/resources/views/admin/users/activity.blade.php new file mode 100644 index 00000000..7634188e --- /dev/null +++ b/resources/views/admin/users/activity.blade.php @@ -0,0 +1,84 @@ +@extends('admin.partial.template-full') + +@section('section') +
+ Back + +

@{{$profile->username}}

+

+ {{$profile->statuses()->count()}} Posts + | + {{$profile->followers()->count()}} Followers + | + {{$profile->following()->count()}} Following +

+
+ + + +
+
+ +
+
+

Recent Activity

+
+
+
+ @if($logs->count() > 0) +
+ @foreach($logs as $log) +
+
+

{{$log->created_at->diffForHumans()}}

+

{{$log->message}}

+

+ IP: {{$log->ip_address}} +

+
+
+ +
+
+ @endforeach +
+
+ {{$logs->links()}} +
+ @else +
+ No Activity found +
+ @endif +
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/users/delete.blade.php b/resources/views/admin/users/delete.blade.php new file mode 100644 index 00000000..440caea0 --- /dev/null +++ b/resources/views/admin/users/delete.blade.php @@ -0,0 +1,74 @@ +@extends('admin.partial.template-full') + +@section('section') +
+ Back + +

@{{$profile->username}}

+

+ {{$profile->statuses()->count()}} Posts + | + {{$profile->followers()->count()}} Followers + | + {{$profile->following()->count()}} Following +

+
+ + + +
+
+
+
+
+

Are you sure you want to delete this account?

+

+

+ @csrf + +
+

+
+
+
+@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/admin/users/edit.blade.php b/resources/views/admin/users/edit.blade.php index 29148e9d..2c784ccb 100644 --- a/resources/views/admin/users/edit.blade.php +++ b/resources/views/admin/users/edit.blade.php @@ -1,101 +1,100 @@ -@extends('admin.partial.template') +@extends('admin.partial.template-full') @section('section') -
-

Edit User

- Back -
-
+
+ Back + +

@{{$profile->username}}

+

+ {{$profile->statuses()->count()}} Posts + | + {{$profile->followers()->count()}} Followers + | + {{$profile->following()->count()}} Following +

+
+ + + +
+
-
-
-
-
-

{{$profile->statusCount()}}

-

Posts

-
-
-
-
-
-
-

{{$profile->likes()->count()}}

-

Likes

-
-
-
-
-
-
-

{{$profile->reports()->count()}}

-

Reports

-
-
-
-
-
-
-

{{PrettyNumber::size($profile->media()->sum('size'))}}

-

Storage Used

-
-
-
-
+
+

Edit

+
+
+
+
+ @csrf +
+ + +
+
+ + +
+
+ + +

+ @if($user->email_verified_at) + Verified for {{$user->email_verified_at->diffForHumans()}} + @else + Unverified email. + @endif +

+
+
+ + +
+
+ + +
+
+ +
+ is_admin ? 'checked="checked"' : ''}}> + +
+

For security reasons, you cannot change admin status on this form. Use the CLI instead.

+
+
+

+ +

+
+
+
+
-
-
-
-
- -
- -
-
-
-
-
-
-

- {{$profile->username}} -

-

- {{$profile->emailUrl()}} -

-

- Member Since: {{$profile->created_at->format('M Y')}} -

-
-
-
-
-
-
-
-
- Account Settings -
-
-
- - -
-
- - -
-
- - -

- @if($user->email_verified_at) - Verified for {{$user->email_verified_at->diffForHumans()}} - @else - Unverified email. - @endif -

-
-
-
@endsection \ No newline at end of file diff --git a/resources/views/admin/users/home.blade.php b/resources/views/admin/users/home.blade.php index 8bec6c62..70fb7df7 100644 --- a/resources/views/admin/users/home.blade.php +++ b/resources/views/admin/users/home.blade.php @@ -2,141 +2,112 @@ @section('header')
-
-
-
+
+
test
+
@endsection @section('section') -
-

Users

-
-
-
- - - - - - - - - - - - @foreach($users as $user) - - - - - - + + @endif + @endforeach + +
- ID - - Username - - Statuses - - Storage - - Actions -
- {{$user->id}} - - - - {{$user->username}} - @if($user->is_admin) - - @endif - - - {{$user->profile ? $user->profile->statusCount() : 0}} - -

-
- - - View - +
+

Users

+
+
+
+ + + + + + + + + + @foreach($users as $user) + @if($user->status == 'deleted') + + + + + + @else + + + + - - @endforeach - -
+ ID + + Username + + Actions +
+ {{$user->id}} + + + + {{$user->username}} + + + + Account Deleted + +
+ {{$user->id}} + + + + {{$user->username}} + @if($user->is_admin) + + @endif + + + + + Profile + - - Edit - + + Review + - - Delete - - -
-
-
- {{$users->links()}} -
+ + Mod Logs + +
+
+
+
+ {{$users->links()}} +
@endsection @push('styles') @endpush @push('scripts') - + @endpush diff --git a/resources/views/admin/users/message.blade.php b/resources/views/admin/users/message.blade.php new file mode 100644 index 00000000..b13321a6 --- /dev/null +++ b/resources/views/admin/users/message.blade.php @@ -0,0 +1,112 @@ +@extends('admin.partial.template-full') + +@section('section') +
+ Back + +

@{{$profile->username}}

+

+ {{$profile->statuses()->count()}} Posts + | + {{$profile->followers()->count()}} Followers + | + {{$profile->following()->count()}} Following +

+
+ + + +
+
+ +
+
+

Send Message

+
+
+
+ @if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif +
+ @csrf +
+ +

+ Plain text only, html will not be rendered. + 0/500 +

+
+

+ +

+
+
+
+
+
+@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/admin/users/modlogs.blade.php b/resources/views/admin/users/modlogs.blade.php new file mode 100644 index 00000000..acb14ceb --- /dev/null +++ b/resources/views/admin/users/modlogs.blade.php @@ -0,0 +1,159 @@ +@extends('admin.partial.template-full') + +@section('section') +
+ Back + +

@{{$profile->username}}

+

+ {{$profile->statuses()->count()}} Posts + | + {{$profile->followers()->count()}} Followers + | + {{$profile->following()->count()}} Following +

+
+ + + +
+
+ +
+
+

Moderation Logs

+
+
+
+
+
+ @csrf +
+ + @if ($errors->any()) + @foreach ($errors->all() as $error) +

+ {{ $error }} +

+ @endforeach + @endif +
+
+ + 0/500 + + + + +
+
+
+ @if($logs->count() > 0) +
+ @foreach($logs as $log) +
+ @if($log->message != null) +
+
+ +
+
+ @if($log->user_id != Auth::id()) +
+

{{$log->message}}

+
+ @else +
+

{{$log->message}}

+
+ @endif +
+ + @{{$log->user_username}} + + + {{$log->created_at->diffForHumans()}} + +
+
+ @if($log->user_id == Auth::id()) +
+
+ @csrf + + +
+
+ @endif +
+ @else +
+
+ +
+
+

{{$log->created_at->diffForHumans()}}

+

{{$log->actionToText()}}

+

+ by: {{$log->user_username}} +

+
+
+ +
+
+ @endif +
+ @endforeach +
+
+ {{$logs->links()}} +
+ @else +
+ No Activity found +
+ @endif +
+
+
+
+@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/admin/users/modtools.blade.php b/resources/views/admin/users/modtools.blade.php new file mode 100644 index 00000000..76919302 --- /dev/null +++ b/resources/views/admin/users/modtools.blade.php @@ -0,0 +1,90 @@ +@extends('admin.partial.template-full') + +@section('section') +
+ Back + +

@{{$profile->username}}

+

+ {{$profile->statuses()->count()}} Posts + | + {{$profile->followers()->count()}} Followers + | + {{$profile->following()->count()}} Following +

+
+ + + +
+
+ +
+
+

Mod Tools

+
+
+
+
+ @csrf + + + +

Adds a CW to every post made by this account.

+
+
+
+
+ @csrf + + + +

Removes account from public/network timelines.

+
+
+
+
+ @csrf + + + +

Do not transform mentions, hashtags or urls into HTML.

+
+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/users/show.blade.php b/resources/views/admin/users/show.blade.php new file mode 100644 index 00000000..19513ea5 --- /dev/null +++ b/resources/views/admin/users/show.blade.php @@ -0,0 +1,121 @@ +@extends('admin.partial.template-full') + +@section('section') +
+ Back + +

@{{$profile->username}}

+

+ {{$profile->statuses()->count()}} Posts + | + {{$profile->followers()->count()}} Followers + | + {{$profile->following()->count()}} Following +

+
+ + + +
+
+
+
+
+
+ +

+ {{$profile->name}} +

+ @if($user->is_admin == true) +

+ ADMIN +

+ @endif +

+ Joined {{$profile->created_at->diffForHumans()}} +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
bookmarks{{$profile->bookmarks()->count()}}
collections{{$profile->collections()->count()}}
likes{{$profile->likes()->count()}}
reports{{$profile->reports()->count()}}
reported{{$profile->reported()->count()}}
Active stories{{$profile->stories()->count()}}
storage used{{PrettyNumber::size($profile->media()->sum('size'))}} / {{PrettyNumber::size(config('pixelfed.max_account_size') * 1000)}}
+
+
+
+

Recent Posts

+
+
+ @foreach($profile->statuses()->whereHas('media')->latest()->take(9)->get() as $item) +
+ + + +
+ @endforeach + + @if($profile->statuses()->whereHas('media')->count() == 0) +
+
+

No statuses found

+
+
+ @endif +
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/emails/confirm_email.blade.php b/resources/views/emails/confirm_email.blade.php index 2fc98a75..f239f3eb 100644 --- a/resources/views/emails/confirm_email.blade.php +++ b/resources/views/emails/confirm_email.blade.php @@ -1,7 +1,7 @@ @component('mail::message') # Email Confirmation -Hello @{{$verify->user->username}}, please confirm your email address. +Hello {{ '@' . $verify->user->username}}, please confirm your email address. If you did not create this account, please disregard this email. diff --git a/resources/views/emails/notification/admin_message.blade.php b/resources/views/emails/notification/admin_message.blade.php new file mode 100644 index 00000000..001d2cd4 --- /dev/null +++ b/resources/views/emails/notification/admin_message.blade.php @@ -0,0 +1,20 @@ +@component('mail::message') +# Message from {{ config('pixelfed.domain.app') }}: + + +@component('mail::panel') +{{$msg}} +@endcomponent + + +
+ +Regards,
+{{ config('pixelfed.domain.app') }} + +@component('mail::subcopy') +Please do not reply to this email, this address is not monitored. +@endcomponent + +@endcomponent + diff --git a/resources/views/status/comments.blade.php b/resources/views/status/comments.blade.php deleted file mode 100644 index 6d5da3cb..00000000 --- a/resources/views/status/comments.blade.php +++ /dev/null @@ -1,41 +0,0 @@ -@extends('layouts.app') - -@section('content') - -
-
- -
-
-

- - {{ str_limit($status->profile->username, 15)}} - {!! $status->rendered ?? e($status->caption) !!} {{$status->created_at->diffForHumans(null, true, true ,true)}} -

-

All Comments

-
- @foreach($replies as $item) -

- - - {{ str_limit($item->profile->username, 15)}} - - - {!! $item->rendered ?? e($item->caption) !!} - - {{$item->created_at->diffForHumans(null, true, true ,true)}} - - -

- @endforeach - -
-
-
-
- {{ $replies->links() }} -
-
-
- -@endsection \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index f73dc1f9..8fa1bcfc 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,7 +16,19 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio Route::get('profiles/edit/{id}', 'AdminController@profileShow'); Route::redirect('users', '/users/list'); Route::get('users/list', 'AdminController@users')->name('admin.users'); - Route::get('users/edit/{id}', 'AdminController@editUser'); + Route::get('users/show/{id}', 'AdminController@userShow'); + Route::get('users/edit/{id}', 'AdminController@userEdit'); + Route::post('users/edit/{id}', 'AdminController@userEditSubmit'); + Route::get('users/activity/{id}', 'AdminController@userActivity'); + Route::get('users/message/{id}', 'AdminController@userMessage'); + Route::post('users/message/{id}', 'AdminController@userMessageSend'); + Route::get('users/modtools/{id}', 'AdminController@userModTools'); + Route::get('users/modlogs/{id}', 'AdminController@userModLogs'); + Route::post('users/modlogs/{id}', 'AdminController@userModLogsMessage'); + Route::post('users/modlogs/{id}/delete', 'AdminController@userModLogDelete'); + Route::get('users/delete/{id}', 'AdminController@userDelete'); + Route::post('users/delete/{id}', 'AdminController@userDeleteProcess'); + Route::post('users/moderation/update', 'AdminController@userModerate'); Route::get('media', 'AdminController@media')->name('admin.media'); Route::redirect('media/list', '/i/admin/media'); Route::get('media/show/{id}', 'AdminController@mediaShow');