diff --git a/bootstrap.php b/bootstrap.php index 6ffb52e..a0a3ce5 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -1,5 +1,6 @@ listen('texture.uploaded', OnTextureUploaded::class); + $events->listen('texture.uploaded', function (Texture $texture) use ($events) { + return OnTextureUploaded::handle($texture, $events); + }); $events->listen('texture.deleted', OnTextureDeleted::class); $filter->add('can_update_texture_privacy', function ($can, $texture) { @@ -41,7 +44,7 @@ ->prefix('admin/texture-moderation') ->group(function () { Route::get('', 'TextureModerationController@show'); - Route::post('review', 'TextureModerationController@review'); + Route::put('review/{record}', 'TextureModerationController@review'); Route::get('list', 'TextureModerationController@manage'); }); @@ -53,6 +56,14 @@ Route::post('', 'WhitelistController@add'); Route::delete('', 'WhitelistController@delete'); }); + + Route::namespace('LittleSkin\TextureModeration\Controllers') + ->middleware(['api', 'auth:oauth', 'role:admin']) + ->prefix('api/admin/texture-moderation') + ->group(function () { + Route::get('', 'TextureModerationController@manage')->middleware(['scope:TextureModeration.Read,TextureModeration.ReadWrite']); + Route::put('{record}', 'TextureModerationController@review')->middleware(['scope:TextureModeration.ReadWrite']); + }); }); Hook::addMenuItem('admin', 4001, [ diff --git a/callbacks.php b/callbacks.php index da2fb8d..da7f53d 100644 --- a/callbacks.php +++ b/callbacks.php @@ -1,5 +1,7 @@ function () { if (!Schema::hasTable('moderation_whitelist')) { @@ -25,5 +27,19 @@ $table->timestamps(); }); } + + if (!Scope::where('name', 'TextureModeration.Read')->exists()) { + Scope::create([ + 'name' => 'TextureModeration.Read', + 'description' => 'Ability to read texture moderation records' + ]); + } + + if (!Scope::where('name', 'TextureModeration.ReadWrite')->exists()) { + Scope::create([ + 'name' => 'TextureModeration.ReadWrite', + 'description' => 'Ability to read and write texture moderation records' + ]); + } }, ]; diff --git a/src/Controllers/ModerationController.php b/src/Controllers/ModerationController.php index 2f0244b..bf7c91d 100644 --- a/src/Controllers/ModerationController.php +++ b/src/Controllers/ModerationController.php @@ -5,6 +5,7 @@ use App\Models\Texture; use Blessing\Rejection; use Exception; +use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Storage; use LittleSkin\TextureModeration\Models\ModerationRecord; @@ -16,12 +17,14 @@ class ModerationController extends Controller { - public static function start(Texture $texture, $source) + public static function start(Texture $texture, $source, Dispatcher $dispatcher) { $disk = Storage::disk('textures'); $hash = $texture->hash; $file = $disk->get($hash); + $dispatcher->dispatch('texture-moderation.starting', [$texture]); + $record = new ModerationRecord(); $record->tid = $texture->tid; $record->source = $source; @@ -30,6 +33,8 @@ public static function start(Texture $texture, $source) $record->review_state = ReviewState::MISS; $record->save(); + $dispatcher->dispatch('texture-moderation.finished', [$record]); + return; } @@ -38,6 +43,8 @@ public static function start(Texture $texture, $source) $record->review_state = ReviewState::USER; $record->save(); + $dispatcher->dispatch('texture-moderation.finished', [$record]); + return; } @@ -50,6 +57,8 @@ public static function start(Texture $texture, $source) $record = $itsRecord->review_state; $record->save(); + $dispatcher->dispatch('texture-moderation.finished', [$record]); + return; } } @@ -96,14 +105,19 @@ public static function start(Texture $texture, $source) $record->review_state = ReviewState::APPROVED; $record->save(); + $dispatcher->dispatch('texture-moderation.finished', [$record]); + return; } $texture->public = false; $texture->save(); + $record->review_state = ReviewState::MANUAL; $record->save(); + $dispatcher->dispatch('texture-moderation.onegai', [$record]); + return new Rejection(trans('LittleSkin\TextureModeration::skinlib.manual_tip')); } } diff --git a/src/Controllers/TextureModerationController.php b/src/Controllers/TextureModerationController.php index 1602b3e..aef2ac5 100644 --- a/src/Controllers/TextureModerationController.php +++ b/src/Controllers/TextureModerationController.php @@ -4,6 +4,7 @@ use App\Models\Texture; use App\Services\Hook; +use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Auth; @@ -34,129 +35,116 @@ public function manage(Request $request) $q = $request->input('q'); return ModerationRecord::usingSearchString($q) - ->leftJoin('users as operator', 'operator.uid', '=', 'moderation_records.operator') - ->leftJoin('textures', 'textures.tid', '=', 'moderation_records.tid') - ->leftJoin('users', 'users.uid', '=', 'textures.uploader') - ->select(['textures.uploader', 'users.uid', 'users.nickname', 'moderation_records.*', 'operator.nickname as operator_nickname']) + ->with(['texture:tid,name,type,uploader', 'texture.owner:uid,nickname', 'operator:uid,nickname']) ->paginate(9); } - public function review(Request $request) + public function review(ModerationRecord $record, Request $request, Dispatcher $dispatcher) { $data = $request->validate([ - 'id' => ['required', 'integer'], 'action' => ['required', Rule::in(['approve', 'reject', 'private'])], ]); - $tid = $data['id']; + $tid = $record->tid; $action = $data['action']; + $record->operator = Auth::user()->uid; + switch ($action) { case 'approve': - $record = ModerationRecord::where('tid', $tid)->first(); + $record->review_state = ReviewState::APPROVED; - if ($record) { - $record->operator = Auth::user()->uid; - $record->review_state = ReviewState::APPROVED; + $record->save(); - $record->save(); + $texture = Texture::where('tid', $tid)->first(); + $texture->public = true; - $texture = Texture::where('tid', $tid)->first(); - $texture->public = true; + $texture->save(); - $texture->save(); + $dispatcher->dispatch('texture-moderation.finished', [$record]); - $uploader = $texture->owner; - if ($uploader) { - Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.approved', [ - 'name' => $texture->name, - ])); - } - - return json(trans('general.op-success'), 0); - } else { - return json(trans('LittleSkin\TextureModeration::manage.message.texture-not-exist'), 1); + $uploader = $texture->owner; + if ($uploader) { + Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.approved', [ + 'name' => $texture->name, + ])); } + return json(trans('general.op-success'), 0); + break; case 'reject': - $record = ModerationRecord::where('tid', $tid)->first(); + $record->review_state = ReviewState::REJECTED; - if ($record) { - $record->operator = Auth::user()->uid; - $record->review_state = ReviewState::REJECTED; + $record->save(); - $record->save(); - - $texture = Texture::where('tid', $tid)->first(); - - if($record->source === RecordSource::ON_PRIVACY_UPDATED){ - $texture->public = false; - $texture->save(); - - return json(trans('LittleSkin\TextureModeration::manage.message.keep-privacy'), 0); - } + $texture = Texture::where('tid', $tid)->first(); - $texture->delete(); + if ($record->source === RecordSource::ON_PRIVACY_UPDATED) { + $texture->public = false; + $texture->save(); - $uploader = $texture->owner; - if ($uploader) { - $uploader->score += $texture->size * option('score_per_storage'); - $uploader->save(); + $dispatcher->dispatch('texture-moderation.finished', [$record]); - Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.deleted', [ - 'name' => $texture->name, - ])); - } + return json(trans('LittleSkin\TextureModeration::manage.message.keep-privacy'), 0); + } + + $texture->delete(); + + $uploader = $texture->owner; + if ($uploader) { + $uploader->score += $texture->size * option('score_per_storage'); + $uploader->save(); - return json(trans('LittleSkin\TextureModeration::manage.message.deleted'), 0); - } else { - return json(trans('LittleSkin\TextureModeration::manage.message.texture-not-exist'), 1); + Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.deleted', [ + 'name' => $texture->name, + ])); } - break; + $dispatcher->dispatch('texture-moderation.finished', [$record]); + + return json(trans('LittleSkin\TextureModeration::manage.message.deleted'), 0); + + break; case 'private': - $record = ModerationRecord::where('tid', $tid)->first(); + $record->review_state = ReviewState::REJECTED; - if ($record) { - $record->operator = Auth::user()->uid; - $record->review_state = ReviewState::REJECTED; + $record->save(); - $record->save(); + $texture = Texture::where('tid', $tid)->first(); - $texture = Texture::where('tid', $tid)->first(); + $uploader = $texture->owner; + if ($uploader) { + $diff = $texture->size * (option('private_score_per_storage') - option('score_per_storage')); - $uploader = $texture->owner; - if ($uploader) { - $diff = $texture->size * (option('private_score_per_storage') - option('score_per_storage')); + if ($uploader->score >= $diff) { + $uploader->score -= $diff; + $uploader->save(); - if ($uploader->score >= $diff) { - $uploader->score -= $diff; - $uploader->save(); + $texture->public = false; + $texture->save(); - $texture->public = false; - $texture->save(); + Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.private', [ + 'name' => $texture->name, + ])); - Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.private', [ - 'name' => $texture->name, - ])); + $dispatcher->dispatch('texture-moderation.finished', [$record]); + + return json(trans('LittleSkin\TextureModeration::manage.message.privacy'), 0); + } else { + $uploader->score += $texture->size * option('score_per_storage'); + $uploader->save(); - return json(trans('LittleSkin\TextureModeration::manage.message.privacy'), 0); - } else { - $uploader->score += $texture->size * option('score_per_storage'); - $uploader->save(); + $texture->delete(); - $texture->delete(); + Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.deleted', [ + 'name' => $texture->name, + ])); - Hook::sendNotification([$uploader], trans('LittleSkin\TextureModeration::skinlib.notification.title'), trans('LittleSkin\TextureModeration::skinlib.notification.deleted', [ - 'name' => $texture->name, - ])); + $dispatcher->dispatch('texture-moderation.finished', [$record]); - return json(trans('LittleSkin\TextureModeration::manage.message.privacy-failed'), 1); - } + return json(trans('LittleSkin\TextureModeration::manage.message.privacy-failed'), 1); } - } else { - return json(trans('LittleSkin\TextureModeration::manage.message.texture-not-exist'), 1); } break; diff --git a/src/Listeners/OnTextureUploaded.php b/src/Listeners/OnTextureUploaded.php index 04fa1b9..ffac58e 100644 --- a/src/Listeners/OnTextureUploaded.php +++ b/src/Listeners/OnTextureUploaded.php @@ -3,15 +3,16 @@ namespace LittleSkin\TextureModeration\Listeners; use App\Models\Texture; +use Illuminate\Contracts\Events\Dispatcher; use LittleSkin\TextureModeration\Controllers\ModerationController; use LittleSkin\TextureModeration\RecordSource; class OnTextureUploaded { - public function handle(Texture $texture) + public static function handle(Texture $texture, Dispatcher $dispatcher) { if ($texture->public) { - return ModerationController::start($texture, RecordSource::ON_PUBLIC_UPLOAD); + return ModerationController::start($texture, RecordSource::ON_PUBLIC_UPLOAD, $dispatcher); } } } diff --git a/src/Models/ModerationRecord.php b/src/Models/ModerationRecord.php index 58db645..92cbb30 100644 --- a/src/Models/ModerationRecord.php +++ b/src/Models/ModerationRecord.php @@ -34,6 +34,11 @@ class ModerationRecord extends Model public function texture() { - return $this->belongsTo('App\Models\Texture'); + return $this->belongsTo('App\Models\Texture', 'tid', 'tid'); + } + + public function operator() + { + return $this->belongsTo('App\Models\User', 'operator', 'uid'); } } diff --git a/views/moderation.jsx b/views/moderation.jsx index 8338fe6..ef4abd9 100644 --- a/views/moderation.jsx +++ b/views/moderation.jsx @@ -74,8 +74,7 @@ const App = () => { setMaxPage(last_page) } const submit = async (action) => { - let r = await bsFetch.post('/admin/texture-moderation/review', { - id: viewing.tid, + let r = await bsFetch.put('/admin/texture-moderation/review/' + viewing.id, { action }) if (r.code === 0) { @@ -107,11 +106,11 @@ const App = () => { {list.map(v => (
setViewing(v)}>
- {v.uploader ? ( + {v.texture ? ( <>{t('texture-moderation.uploader')}: - {v.nickname} + {v.texture.owner.nickname} (UID: - {v.uploader}) + {v.texture.owner.uid}) ) : {t('texture-moderation.deleted')}} @@ -134,8 +133,8 @@ const App = () => {
{t('texture-moderation.operator')}: {v.operator ? ( - <>{v.operator_nickname} (UID: - {v.operator}) + <>{v.operator.nickname} (UID: + {v.operator.uid}) ) : <>{t('texture-moderation.machine-review')}}