From e0c7292d35275a1ab59b05a628260aa4ee7a99ef Mon Sep 17 00:00:00 2001 From: printempw Date: Tue, 24 Jul 2018 16:13:51 +0800 Subject: [PATCH] Support changing texture model from skinlib --- app/Http/Controllers/SkinlibController.php | 29 ++++++++ app/helpers.php | 12 ++++ .../assets/src/js/__tests__/skinlib.test.js | 51 ++++++++++++++ resources/assets/src/js/skinlib/operations.js | 42 +++++++++++ resources/lang/en/locale.js | 3 + resources/lang/en/skinlib.yml | 8 ++- resources/lang/zh_CN/locale.js | 3 + resources/lang/zh_CN/skinlib.yml | 6 +- resources/views/skinlib/show.tpl | 16 +++-- routes/web.php | 1 + tests/SkinlibControllerTest.php | 69 +++++++++++++++++++ 11 files changed, 230 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/SkinlibController.php b/app/Http/Controllers/SkinlibController.php index 9529d22a..f029a3f9 100644 --- a/app/Http/Controllers/SkinlibController.php +++ b/app/Http/Controllers/SkinlibController.php @@ -295,6 +295,35 @@ class SkinlibController extends Controller } } // @codeCoverageIgnore + public function model(Request $request) { + $this->validate($request, [ + 'tid' => 'required|integer', + 'model' => 'required|in:steve,alex,cape' + ]); + + $t = Texture::find($request->input('tid')); + + if (! $t) + return json(trans('skinlib.non-existent'), 1); + + if ($t->uploader != $this->user->uid && !$this->user->isAdmin()) + return json(trans('skinlib.no-permission'), 1); + + $duplicate = Texture::where('hash', $t->hash) + ->where('type', $request->input('model')) + ->where('tid', '<>', $t->tid) + ->first(); + + if ($duplicate && $duplicate->public) { + return json(trans('skinlib.model.duplicate', ['tid' => $duplicate->tid]), 1); + } + + $t->type = $request->input('model'); + $t->save(); + + return json(trans('skinlib.model.success', ['model' => request('model')]), 0); + } + /** * Check Uploaded Files * diff --git a/app/helpers.php b/app/helpers.php index 8c591391..524bd0bb 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -506,3 +506,15 @@ if (! function_exists('report')) { app(Illuminate\Contracts\Debug\ExceptionHandler::class)->report($exception); } } + +if (! function_exists('can_moderate_texture')) { + + function can_moderate_texture($user, $texture) { + if (! $user) { + return false; + } + + // Only uploader and admins can moderate textures + return ($texture->uploader == $user->uid || $user->isAdmin()); + } +} diff --git a/resources/assets/src/js/__tests__/skinlib.test.js b/resources/assets/src/js/__tests__/skinlib.test.js index 7e09eca1..e2be2ea3 100644 --- a/resources/assets/src/js/__tests__/skinlib.test.js +++ b/resources/assets/src/js/__tests__/skinlib.test.js @@ -503,6 +503,57 @@ describe('tests for "operations" module', () => { expect(showAjaxError).toBeCalled(); }); + it('change texture model', async () => { + const fetch = jest.fn() + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); + window.fetch = fetch; + const url = jest.fn(path => path); + window.url = url; + const trans = jest.fn(key => key); + window.trans = trans; + const swal = jest.fn() + .mockImplementationOnce(() => Promise.reject()) + .mockImplementationOnce(() => Promise.resolve('alex')); + window.swal = swal; + const modal = jest.fn(); + const toastr = { + success: jest.fn(), + warning: jest.fn() + }; + window.toastr = toastr; + const showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; + + document.body.innerHTML = '
'; + const changeTextureModel = require(modulePath).changeTextureModel; + + await changeTextureModel(1, 'steve'); + expect(fetch).not.toBeCalled(); + + await changeTextureModel(1, 'steve'); + expect(swal).toBeCalledWith(expect.objectContaining({ + text: trans('skinlib.setNewTextureModel'), + input: 'select', + inputValue: 'steve', + })); + expect(fetch).toBeCalledWith({ + type: 'POST', + url: 'skinlib/model', + dataType: 'json', + data: { tid: 1, model: 'alex' } + }); + expect($('div').text()).toBe('alex'); + expect(toastr.success).toBeCalledWith('success'); + + await changeTextureModel(1, 'steve'); + expect(toastr.warning).toBeCalledWith('warning'); + + await changeTextureModel(1, 'steve'); + expect(showAjaxError).toBeCalled(); + }); + it('update texture status', () => { window.trans = jest.fn(key => key); document.body.innerHTML = ` diff --git a/resources/assets/src/js/skinlib/operations.js b/resources/assets/src/js/skinlib/operations.js index dc4c962f..9853b7cb 100644 --- a/resources/assets/src/js/skinlib/operations.js +++ b/resources/assets/src/js/skinlib/operations.js @@ -130,6 +130,47 @@ async function changeTextureName(tid, oldName) { } } +async function changeTextureModel(tid, oldModel) { + const models = { + steve: 'steve', + alex: 'alex', + cape: trans('general.cape') + }; + + let newTextureModel = ''; + + try { + newTextureModel = await swal({ + text: trans('skinlib.setNewTextureModel'), + input: 'select', + inputValue: oldModel, + inputOptions: models, + showCancelButton: true, + inputClass: 'form-control' + }); + } catch (error) { + return; + } + + try { + const { errno, msg } = await fetch({ + type: 'POST', + url: url('skinlib/model'), + dataType: 'json', + data: { tid: tid, model: newTextureModel } + }); + + if (errno === 0) { + $('#model').text(models[newTextureModel]); + toastr.success(msg); + } else { + toastr.warning(msg); + } + } catch (error) { + showAjaxError(error); + } +} + /** * Update button action & likes of texture. * @@ -231,6 +272,7 @@ if (process.env.NODE_ENV === 'test') { ajaxAddToCloset, removeFromCloset, changeTextureName, + changeTextureModel, updateTextureStatus, }; } diff --git a/resources/lang/en/locale.js b/resources/lang/en/locale.js index 769f0ffe..e5189a04 100644 --- a/resources/lang/en/locale.js +++ b/resources/lang/en/locale.js @@ -47,6 +47,9 @@ setNewTextureName: 'Please enter the new texture name:', emptyNewTextureName: 'Empty new texture name.', + // Change Model + setNewTextureModel: 'Please select a new texture model:', + // Skinlib filter: { skin: '(Any Model)', diff --git a/resources/lang/en/skinlib.yml b/resources/lang/en/skinlib.yml index abe0e0f1..4b7243d4 100644 --- a/resources/lang/en/skinlib.yml +++ b/resources/lang/en/skinlib.yml @@ -36,7 +36,7 @@ show: detail: Details name: Texture Name - edit-name: Edit Name + edit: Edit model: Applicable Model download-raw: Click to download raw texture size: File Size @@ -85,10 +85,14 @@ privacy: change-privacy: Change Privacy set-as-private: Set as Private set-as-public: Set as Public - success: The texture was setted to :privacy successfully. + success: The texture was set to :privacy successfully. rename: success: The texture was renamed to :name successfully. +model: + success: The texture's model was changed to :model successfully. + duplicate: "The same texture available for the chosen model already exists in skinlib (TID: :tid). You can add it to your closet directly." + no-permission: You aren't the uploader of this texture. non-existent: Non-existent texture. diff --git a/resources/lang/zh_CN/locale.js b/resources/lang/zh_CN/locale.js index 9c1edef4..31745de0 100644 --- a/resources/lang/zh_CN/locale.js +++ b/resources/lang/zh_CN/locale.js @@ -67,6 +67,9 @@ setNewTextureName: '请输入新的材质名称:', emptyNewTextureName: '你还没有输入新名称啊', + // Change Model + setNewTextureModel: '请选择新的材质适用模型:', + // Upload emptyTextureName: '给你的材质起个名字吧', emptyTextureType: '请选择材质的类型', diff --git a/resources/lang/zh_CN/skinlib.yml b/resources/lang/zh_CN/skinlib.yml index f598a48e..beb179d9 100644 --- a/resources/lang/zh_CN/skinlib.yml +++ b/resources/lang/zh_CN/skinlib.yml @@ -36,7 +36,7 @@ show: detail: 详细信息 name: 名称 - edit-name: 修改名称 + edit: 修改 model: 适用模型 download-raw: 右键另存为即可下载原始皮肤文件 size: 文件大小 @@ -89,5 +89,9 @@ privacy: rename: success: 材质名称已被成功设置为 :name +model: + success: 材质的适用模型已被修改为 :model + duplicate: 已经有人上传过适用于该模型的相同材质了,直接去皮肤库收藏使用吧(TID::tid) + no-permission: 你不是这个材质的上传者哦 non-existent: 材质不存在 diff --git a/resources/views/skinlib/show.tpl b/resources/views/skinlib/show.tpl index ed16ce8d..b7230dd3 100644 --- a/resources/views/skinlib/show.tpl +++ b/resources/views/skinlib/show.tpl @@ -48,10 +48,11 @@ {{ trans('skinlib.show.name') }} - {{ $texture->name }} - @if (!is_null($user) && ($texture->uploader == $user->uid || $user->isAdmin())) + + {{ $texture->name }} + @if (can_moderate_texture($user, $texture)) - {{ trans('skinlib.show.edit-name') }} + {{ trans('skinlib.show.edit') }} @endif @@ -59,10 +60,11 @@ {{ trans('skinlib.show.model') }} - @if ($texture->type == 'cape') - {{ trans('general.cape') }} - @else - {{ $texture->type }} + {{ $texture->type == 'cape' ? trans('general.cape') : $texture->type }} + @if (can_moderate_texture($user, $texture)) + + {{ trans('skinlib.show.edit') }} + @endif diff --git a/routes/web.php b/routes/web.php index 43d95ed6..7ef3eb5d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -86,6 +86,7 @@ Route::group(['prefix' => 'skinlib'], function () Route::post('/upload', 'SkinlibController@handleUpload'); Route::post('/rename', 'SkinlibController@rename'); + Route::post('/model', 'SkinlibController@model'); Route::post('/privacy', 'SkinlibController@privacy'); Route::post('/delete', 'SkinlibController@delete'); }); diff --git a/tests/SkinlibControllerTest.php b/tests/SkinlibControllerTest.php index a64b7d72..0b36857f 100644 --- a/tests/SkinlibControllerTest.php +++ b/tests/SkinlibControllerTest.php @@ -933,4 +933,73 @@ class SkinlibControllerTest extends TestCase ]); $this->assertEquals('new_name', Texture::find($texture->tid)->name); } + + public function testChangeModel() + { + $uploader = factory(User::class)->create(); + $other = factory(User::class)->create(); + $texture = factory(Texture::class)->create(['uploader' => $uploader->uid]); + + // Non-existed texture + $this->actAs($uploader) + ->post('/skinlib/model', [ + 'tid' => -1, + 'model' => 'alex' + ]) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('skinlib.non-existent') + ]); + + // Other user should not be able to change model + $this->actAs($other) + ->post('/skinlib/model', [ + 'tid' => $texture->tid, + 'model' => 'alex' + ]) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('skinlib.no-permission') + ]); + + // Administrators should be able to change model + $this->actAs('admin') + ->post('/skinlib/model', [ + 'tid' => $texture->tid, + 'model' => 'alex' + ]) + ->seeJson([ + 'errno' => 0, + 'msg' => trans('skinlib.model.success', ['model' => 'alex']) + ]); + $this->assertEquals('alex', Texture::find($texture->tid)->type); + + // Uploader should be able to change model + $this->actAs($uploader) + ->post('/skinlib/model', [ + 'tid' => $texture->tid, + 'model' => 'steve' + ]) + ->seeJson([ + 'errno' => 0, + 'msg' => trans('skinlib.model.success', ['model' => 'steve']) + ]); + $this->assertEquals('steve', Texture::find($texture->tid)->type); + + $duplicate = factory(Texture::class, 'alex')->create([ + 'uploader' => $other->uid, + 'hash' => $texture->hash + ]); + + // Should fail if there is already a texture with same hash and chosen model + $this->actAs($uploader) + ->post('/skinlib/model', [ + 'tid' => $texture->tid, + 'model' => 'alex' + ]) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('skinlib.model.duplicate', ['tid' => $duplicate->tid]) + ]); + } }