更改Imagick数据处理
把Imagick获取到的GD数据转为二进制交给Imagick处理
This commit is contained in:
parent
33055ecbf9
commit
77a58bbe4a
|
|
@ -2,460 +2,191 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Player;
|
||||
use App\Models\Texture;
|
||||
use App\Models\User;
|
||||
use Blessing\Filter;
|
||||
use Blessing\Rejection;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Blessing\Minecraft;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||
|
||||
class SkinlibController extends Controller
|
||||
class TextureController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
/** @var User */
|
||||
$user = $request->user();
|
||||
/** @var Texture */
|
||||
$texture = $request->route('texture');
|
||||
$this->middleware('cache.headers:public;max_age='.option('cache_expire_time'))
|
||||
->only(['json']);
|
||||
|
||||
if ($texture->uploader != $user->uid && !$user->isAdmin()) {
|
||||
return json(trans('skinlib.no-permission'), 1)
|
||||
->setStatusCode(403);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
})->only(['rename', 'privacy', 'type', 'delete']);
|
||||
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
/** @var User */
|
||||
$user = $request->user();
|
||||
/** @var Texture */
|
||||
$texture = $request->route('texture');
|
||||
|
||||
if (!$texture->public) {
|
||||
if (!Auth::check() || ($user->uid != $texture->uploader && !$user->isAdmin())) {
|
||||
$statusCode = (int) option('status_code_for_private');
|
||||
if ($statusCode === 404) {
|
||||
abort($statusCode, trans('skinlib.show.deleted'));
|
||||
} else {
|
||||
abort(403, trans('skinlib.show.private'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
})->only(['show', 'info']);
|
||||
$this->middleware('cache.headers:etag;public;max_age='.option('cache_expire_time'))
|
||||
->only([
|
||||
'preview',
|
||||
'raw',
|
||||
'texture',
|
||||
'avatarByPlayer',
|
||||
'avatarByUser',
|
||||
'avatarByTexture',
|
||||
]);
|
||||
}
|
||||
|
||||
public function library(Request $request)
|
||||
public function json($player)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$player = Player::where('name', $player)->firstOrFail();
|
||||
$isBanned = $player->user->permission === User::BANNED;
|
||||
abort_if($isBanned, 403, trans('general.player-banned'));
|
||||
|
||||
// Available filters: skin, steve, alex, cape
|
||||
$type = $request->input('filter', 'skin');
|
||||
$uploader = $request->input('uploader');
|
||||
$keyword = $request->input('keyword');
|
||||
$sort = $request->input('sort', 'time');
|
||||
$sortBy = $sort == 'time' ? 'upload_at' : $sort;
|
||||
|
||||
return Texture::orderBy($sortBy, 'desc')
|
||||
->when(
|
||||
$type === 'skin',
|
||||
fn (Builder $query) => $query->whereIn('type', ['steve', 'alex']),
|
||||
fn (Builder $query) => $query->where('type', $type),
|
||||
)
|
||||
->when($keyword, fn (Builder $query, $keyword) => $query->like('name', $keyword))
|
||||
->when($uploader, fn (Builder $query, $uploader) => $query->where('uploader', $uploader))
|
||||
->when($user, function (Builder $query, User $user) {
|
||||
if (!$user->isAdmin()) {
|
||||
// use closure-style `where` clause to lift up SQL priority
|
||||
return $query->where(function (Builder $query) use ($user) {
|
||||
$query
|
||||
->where('public', true)
|
||||
->orWhere('uploader', $user->uid);
|
||||
});
|
||||
}
|
||||
}, function (Builder $query) {
|
||||
// show public textures only to anonymous visitors
|
||||
return $query->where('public', true);
|
||||
})
|
||||
->join('users', 'uid', 'uploader')
|
||||
->select(['tid', 'name', 'type', 'uploader', 'public', 'likes', 'nickname'])
|
||||
->paginate(20);
|
||||
return response()->json($player)->setLastModified($player->last_modified);
|
||||
}
|
||||
|
||||
public function show(Filter $filter, Texture $texture)
|
||||
public function previewByHash(Minecraft $minecraft, Request $request, $hash)
|
||||
{
|
||||
$texture = Texture::where('hash', $hash)->firstOrFail();
|
||||
|
||||
return $this->preview($minecraft, $request, $texture);
|
||||
}
|
||||
|
||||
public function preview(Minecraft $minecraft, Request $request, Texture $texture)
|
||||
{
|
||||
$tid = $texture->tid;
|
||||
$hash = $texture->hash;
|
||||
$usePNG = $request->has('png') || !(imagetypes() & IMG_WEBP);
|
||||
$format = $usePNG ? 'png' : 'webp';
|
||||
|
||||
$disk = Storage::disk('textures');
|
||||
abort_if($disk->missing($hash), 404);
|
||||
|
||||
$height = (int) $request->query('height', 200);
|
||||
$now = Carbon::now();
|
||||
$response = Cache::remember(
|
||||
'preview-t'.$tid."-$format",
|
||||
option('enable_preview_cache') ? $now->addYear() : $now->addMinute(),
|
||||
function () use ($minecraft, $disk, $texture, $hash, $height, $usePNG) {
|
||||
$file = $disk->get($hash);
|
||||
if ($texture->type === 'cape') {
|
||||
$gdResource = $minecraft->renderCape($file, $height);
|
||||
} else {
|
||||
$gdResource = $minecraft->renderSkin($file, 12, $texture->type === 'alex');
|
||||
}
|
||||
|
||||
ob_start();
|
||||
imagepng($gdResource);
|
||||
$imageBlob = ob_get_clean();
|
||||
imagedestroy($gdResource);
|
||||
$imagick = new \Imagick();
|
||||
$imagick->readImageBlob($imageBlob);
|
||||
|
||||
|
||||
if (!$usePNG) {
|
||||
$imagick->setImageFormat('webp');
|
||||
}
|
||||
|
||||
|
||||
$response = response($imagick->getImageBlob(), 200, [
|
||||
'Content-Type' => $usePNG ? 'image/png' : 'image/webp',
|
||||
'Content-Length' => strlen($imagick->getImageBlob()),
|
||||
]);
|
||||
$response->setLastModified(Carbon::createFromTimestamp($disk->lastModified($hash)));
|
||||
return $response;
|
||||
}
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function raw($tid)
|
||||
{
|
||||
abort_unless(option('allow_downloading_texture'), 403);
|
||||
|
||||
$texture = Texture::findOrFail($tid);
|
||||
|
||||
return $this->texture($texture->hash);
|
||||
}
|
||||
|
||||
public function texture(string $hash)
|
||||
{
|
||||
/** @var User */
|
||||
$user = Auth::user();
|
||||
/** @var FilesystemAdapter */
|
||||
$disk = Storage::disk('textures');
|
||||
abort_if($disk->missing($hash), 404);
|
||||
|
||||
if ($disk->missing($texture->hash)) {
|
||||
if (option('auto_del_invalid_texture')) {
|
||||
$texture->delete();
|
||||
}
|
||||
abort(404, trans('skinlib.show.deleted'));
|
||||
}
|
||||
$lastModified = Carbon::createFromTimestamp($disk->lastModified($hash));
|
||||
|
||||
$badges = [];
|
||||
$uploader = $texture->owner;
|
||||
if ($uploader) {
|
||||
if ($uploader->isAdmin()) {
|
||||
$badges[] = ['text' => 'STAFF', 'color' => 'primary'];
|
||||
}
|
||||
|
||||
$badges = $filter->apply('user_badges', $badges, [$uploader]);
|
||||
}
|
||||
|
||||
$grid = [
|
||||
'layout' => [
|
||||
['md-8', 'md-4'],
|
||||
],
|
||||
'widgets' => [
|
||||
[
|
||||
['shared.previewer'],
|
||||
['skinlib.widgets.show.side'],
|
||||
],
|
||||
],
|
||||
];
|
||||
$grid = $filter->apply('grid:skinlib.show', $grid);
|
||||
|
||||
return view('skinlib.show')
|
||||
->with('texture', $texture)
|
||||
->with('grid', $grid)
|
||||
->with('extra', [
|
||||
'download' => (bool) option('allow_downloading_texture'),
|
||||
'currentUid' => $user ? $user->uid : 0,
|
||||
'admin' => $user && $user->isAdmin(),
|
||||
'inCloset' => $user && $user->closet()->where('tid', $texture->tid)->count() > 0,
|
||||
'uploaderExists' => (bool) $uploader,
|
||||
'nickname' => optional($uploader)->nickname ?? trans('general.unexistent-user'),
|
||||
'report' => intval(option('reporter_score_modification', 0)),
|
||||
'badges' => $badges,
|
||||
]);
|
||||
return response($disk->get($hash))
|
||||
->withHeaders([
|
||||
'Content-Type' => 'image/png',
|
||||
'Content-Length' => $disk->size($hash),
|
||||
])
|
||||
->setLastModified($lastModified);
|
||||
}
|
||||
|
||||
public function info(Texture $texture)
|
||||
public function avatarByPlayer(Minecraft $minecraft, Request $request, $name)
|
||||
{
|
||||
return $texture;
|
||||
$player = Player::where('name', $name)->firstOrFail();
|
||||
|
||||
return $this->avatar($minecraft, $request, $player->skin);
|
||||
}
|
||||
|
||||
public function upload(Filter $filter)
|
||||
public function avatarByUser(Minecraft $minecraft, Request $request, $uid)
|
||||
{
|
||||
$grid = [
|
||||
'layout' => [
|
||||
['md-6', 'md-6'],
|
||||
],
|
||||
'widgets' => [
|
||||
[
|
||||
['skinlib.widgets.upload.input'],
|
||||
['shared.previewer'],
|
||||
],
|
||||
],
|
||||
];
|
||||
$grid = $filter->apply('grid:skinlib.upload', $grid);
|
||||
$texture = Texture::find(optional(User::find($uid))->avatar);
|
||||
|
||||
$converter = new GithubFlavoredMarkdownConverter();
|
||||
|
||||
return view('skinlib.upload')
|
||||
->with('grid', $grid)
|
||||
->with('extra', [
|
||||
'rule' => ($regexp = option('texture_name_regexp'))
|
||||
? trans('skinlib.upload.name-rule-regexp', compact('regexp'))
|
||||
: trans('skinlib.upload.name-rule'),
|
||||
'privacyNotice' => trans(
|
||||
'skinlib.upload.private-score-notice',
|
||||
['score' => option('private_score_per_storage')]
|
||||
),
|
||||
'score' => (int) auth()->user()->score,
|
||||
'scorePublic' => (int) option('score_per_storage'),
|
||||
'scorePrivate' => (int) option('private_score_per_storage'),
|
||||
'closetItemCost' => (int) option('score_per_closet_item'),
|
||||
'award' => (int) option('score_award_per_texture'),
|
||||
'contentPolicy' => $converter->convertToHtml(option_localized('content_policy'))->getContent(),
|
||||
]);
|
||||
return $this->avatar($minecraft, $request, $texture);
|
||||
}
|
||||
|
||||
public function handleUpload(
|
||||
Request $request,
|
||||
Filter $filter,
|
||||
Dispatcher $dispatcher,
|
||||
) {
|
||||
$file = $request->file('file');
|
||||
if ($file && !$file->isValid()) {
|
||||
Log::error($file->getErrorMessage());
|
||||
public function avatarByHash(Minecraft $minecraft, Request $request, $hash)
|
||||
{
|
||||
$texture = Texture::where('hash', $hash)->first();
|
||||
|
||||
return $this->avatar($minecraft, $request, $texture);
|
||||
}
|
||||
|
||||
public function avatarByTexture(Minecraft $minecraft, Request $request, $tid)
|
||||
{
|
||||
$texture = Texture::find($tid);
|
||||
|
||||
return $this->avatar($minecraft, $request, $texture);
|
||||
}
|
||||
|
||||
protected function avatar(Minecraft $minecraft, Request $request, ?Texture $texture)
|
||||
{
|
||||
if (!empty($texture) && $texture->type !== 'steve' && $texture->type !== 'alex') {
|
||||
return abort(422);
|
||||
}
|
||||
|
||||
$data = $request->validate([
|
||||
'name' => [
|
||||
'required',
|
||||
option('texture_name_regexp') ? 'regex:'.option('texture_name_regexp') : 'string',
|
||||
],
|
||||
'file' => 'required|mimes:png|max:'.option('max_upload_file_size'),
|
||||
'type' => ['required', Rule::in(['steve', 'alex', 'cape'])],
|
||||
'public' => 'required|boolean',
|
||||
]);
|
||||
$size = (int) $request->query('size', 100);
|
||||
$mode = $request->has('3d') ? '3d' : '2d';
|
||||
$usePNG = $request->has('png') || !(imagetypes() & IMG_WEBP);
|
||||
$format = $usePNG ? 'png' : 'webp';
|
||||
|
||||
/** @var UploadedFile */
|
||||
$file = $filter->apply('uploaded_texture_file', $file);
|
||||
$disk = Storage::disk('textures');
|
||||
if (is_null($texture) || $disk->missing($texture->hash)) {
|
||||
// TODO: refactor
|
||||
return Image::configure(['driver' => 'imagick'])->make(resource_path("misc/textures/avatar$mode.png"))
|
||||
->resize($size, $size)
|
||||
->response($usePNG ? 'png' : 'webp', 100);
|
||||
}
|
||||
|
||||
$name = $data['name'];
|
||||
$name = $filter->apply('uploaded_texture_name', $name, [$file]);
|
||||
$hash = $texture->hash;
|
||||
$now = Carbon::now();
|
||||
$response = Cache::remember(
|
||||
'avatar-'.$mode.'-t'.$texture->tid.'-s'.$size."-$format",
|
||||
option('enable_avatar_cache') ? $now->addYear() : $now->addMinute(),
|
||||
function () use ($minecraft, $disk, $hash, $size, $mode, $usePNG) {
|
||||
$file = $disk->get($hash);
|
||||
if ($mode === '3d') {
|
||||
$image = $minecraft->render3dAvatar($file, 25);
|
||||
} else {
|
||||
$image = $minecraft->render2dAvatar($file, 25);
|
||||
}
|
||||
|
||||
$can = $filter->apply('can_upload_texture', true, [$file, $name]);
|
||||
if ($can instanceof Rejection) {
|
||||
return json($can->getReason(), 1);
|
||||
}
|
||||
$lastModified = Carbon::createFromTimestamp($disk->lastModified($hash));
|
||||
|
||||
$type = $data['type'];
|
||||
$size = getimagesize($file);
|
||||
|
||||
$maxWidth = option('max_texture_width', 8192);
|
||||
if ($size[0] > $maxWidth) {
|
||||
$message = trans('skinlib.upload.too-wide', [
|
||||
'width' => $size[0],
|
||||
'maxWidth' => $maxWidth,
|
||||
]);
|
||||
|
||||
return json($message, 1);
|
||||
}
|
||||
|
||||
if ($size[0] % 64 != 0 || $size[1] % 32 != 0) {
|
||||
$message = trans('skinlib.upload.invalid-size', [
|
||||
'type' => $type === 'cape' ? trans('general.cape') : trans('general.skin'),
|
||||
'width' => $size[0],
|
||||
'height' => $size[1],
|
||||
]);
|
||||
|
||||
return json($message, 1);
|
||||
}
|
||||
|
||||
$ratio = $size[0] / $size[1];
|
||||
if ($type == 'steve' || $type == 'alex') {
|
||||
if ($ratio != 2 && $ratio != 1 || $type === 'alex' && $ratio === 2) {
|
||||
$message = trans('skinlib.upload.invalid-size', [
|
||||
'type' => trans('general.skin'),
|
||||
'width' => $size[0],
|
||||
'height' => $size[1],
|
||||
]);
|
||||
|
||||
return json($message, 1);
|
||||
return \Intervention\Image\ImageManagerStatic::configure(['driver' => 'gd'])->make($image)
|
||||
->resize($size, $size)
|
||||
->response($usePNG ? 'png' : 'webp', 100)
|
||||
->setLastModified($lastModified);
|
||||
}
|
||||
} elseif ($type == 'cape') {
|
||||
if ($ratio != 2) {
|
||||
$message = trans('skinlib.upload.invalid-size', [
|
||||
'type' => trans('general.cape'),
|
||||
'width' => $size[0],
|
||||
'height' => $size[1],
|
||||
]);
|
||||
|
||||
return json($message, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$image = Image::make($file);
|
||||
$imagick = $image->getCore();
|
||||
$imagick->setOption('png:compression-filter', '0');
|
||||
$imagick->setOption('png:compression-level', '9');
|
||||
$imagick->setOption('png:compression-strategy', '0');
|
||||
$imagick->setOption('png:exclude-chunk', 'all');
|
||||
$imagick->stripImage();
|
||||
$sanitized = $image->encode('png')->getEncoded();
|
||||
|
||||
$hash = hash('sha256', $image->encoded);
|
||||
$hash = $filter->apply('uploaded_texture_hash', $hash, [$image]);
|
||||
|
||||
/** @var User */
|
||||
$user = Auth::user();
|
||||
|
||||
$duplicated = Texture::where('hash', $hash)
|
||||
->where(
|
||||
fn (Builder $query) => $query->where('public', true)->orWhere('uploader', $user->uid)
|
||||
)
|
||||
->first();
|
||||
if ($duplicated) {
|
||||
// if the texture already uploaded was set to private,
|
||||
// then allow to re-upload it.
|
||||
return json(trans('skinlib.upload.repeated'), 2, ['tid' => $duplicated->tid]);
|
||||
}
|
||||
|
||||
$fileSize = ceil(strlen($sanitized) / 1024);
|
||||
$isPublic = is_string($data['public'])
|
||||
? $data['public'] === '1'
|
||||
: $data['public'];
|
||||
$cost = $fileSize * (
|
||||
$isPublic
|
||||
? option('score_per_storage')
|
||||
: option('private_score_per_storage')
|
||||
);
|
||||
$cost += option('score_per_closet_item');
|
||||
$cost -= option('score_award_per_texture', 0);
|
||||
if ($user->score < $cost) {
|
||||
return json(trans('skinlib.upload.lack-score'), 1);
|
||||
}
|
||||
|
||||
$dispatcher->dispatch('texture.uploading', [$image, $name, $hash]);
|
||||
|
||||
$texture = new Texture();
|
||||
$texture->name = $name;
|
||||
$texture->type = $type;
|
||||
$texture->hash = $hash;
|
||||
$texture->size = $fileSize;
|
||||
$texture->public = $isPublic;
|
||||
$texture->uploader = $user->uid;
|
||||
$texture->likes = 1;
|
||||
$texture->save();
|
||||
|
||||
/** @var FilesystemAdapter */
|
||||
$disk = Storage::disk('textures');
|
||||
if ($disk->missing($hash)) {
|
||||
$disk->put($hash, $sanitized);
|
||||
}
|
||||
|
||||
$user->score -= $cost;
|
||||
$user->closet()->attach($texture->tid, ['item_name' => $name]);
|
||||
$user->save();
|
||||
|
||||
$dispatcher->dispatch('texture.uploaded', [$texture, $image]);
|
||||
|
||||
return json(trans('skinlib.upload.success', ['name' => $name]), 0, [
|
||||
'tid' => $texture->tid,
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete(Texture $texture, Dispatcher $dispatcher, Filter $filter)
|
||||
{
|
||||
$can = $filter->apply('can_delete_texture', true, [$texture]);
|
||||
if ($can instanceof Rejection) {
|
||||
return json($can->getReason(), 1);
|
||||
}
|
||||
|
||||
$dispatcher->dispatch('texture.deleting', [$texture]);
|
||||
|
||||
// check if file occupied
|
||||
if (Texture::where('hash', $texture->hash)->count() === 1) {
|
||||
Storage::disk('textures')->delete($texture->hash);
|
||||
}
|
||||
|
||||
$texture->delete();
|
||||
|
||||
$dispatcher->dispatch('texture.deleted', [$texture]);
|
||||
|
||||
return json(trans('skinlib.delete.success'), 0);
|
||||
}
|
||||
|
||||
public function privacy(Texture $texture, Dispatcher $dispatcher, Filter $filter)
|
||||
{
|
||||
$can = $filter->apply('can_update_texture_privacy', true, [$texture]);
|
||||
if ($can instanceof Rejection) {
|
||||
return json($can->getReason(), 1);
|
||||
}
|
||||
|
||||
$uploader = $texture->owner;
|
||||
$score_diff = $texture->size
|
||||
* (option('private_score_per_storage') - option('score_per_storage'))
|
||||
* ($texture->public ? -1 : 1);
|
||||
if ($texture->public && option('take_back_scores_after_deletion', true)) {
|
||||
$score_diff -= option('score_award_per_texture', 0);
|
||||
}
|
||||
if ($uploader->score + $score_diff < 0) {
|
||||
return json(trans('skinlib.upload.lack-score'), 1);
|
||||
}
|
||||
|
||||
if (!$texture->public) {
|
||||
$duplicated = Texture::where('hash', $texture->hash)
|
||||
->where('public', true)
|
||||
->first();
|
||||
if ($duplicated) {
|
||||
return json(trans('skinlib.upload.repeated'), 2, ['tid' => $duplicated->tid]);
|
||||
}
|
||||
}
|
||||
|
||||
$dispatcher->dispatch('texture.privacy.updating', [$texture]);
|
||||
|
||||
$uploader->score += $score_diff;
|
||||
$uploader->save();
|
||||
|
||||
$texture->public = !$texture->public;
|
||||
$texture->save();
|
||||
|
||||
$dispatcher->dispatch('texture.privacy.updated', [$texture]);
|
||||
|
||||
$message = trans('skinlib.privacy.success', [
|
||||
'privacy' => (
|
||||
$texture->public
|
||||
? trans('general.public')
|
||||
: trans('general.private')),
|
||||
]);
|
||||
|
||||
return json($message, 0);
|
||||
}
|
||||
|
||||
public function rename(
|
||||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Texture $texture,
|
||||
) {
|
||||
$data = $request->validate(['name' => [
|
||||
'required',
|
||||
option('texture_name_regexp')
|
||||
? 'regex:'.option('texture_name_regexp')
|
||||
: 'string',
|
||||
]]);
|
||||
$name = $data['name'];
|
||||
|
||||
$can = $filter->apply('can_update_texture_name', true, [$texture, $name]);
|
||||
if ($can instanceof Rejection) {
|
||||
return json($can->getReason(), 1);
|
||||
}
|
||||
|
||||
$dispatcher->dispatch('texture.name.updating', [$texture, $name]);
|
||||
|
||||
$old = $texture->replicate();
|
||||
$texture->name = $name;
|
||||
$texture->save();
|
||||
|
||||
$dispatcher->dispatch('texture.name.updated', [$texture, $old]);
|
||||
|
||||
return json(trans('skinlib.rename.success', ['name' => $name]), 0);
|
||||
}
|
||||
|
||||
public function type(
|
||||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Texture $texture,
|
||||
) {
|
||||
$data = $request->validate([
|
||||
'type' => ['required', Rule::in(['steve', 'alex', 'cape'])],
|
||||
]);
|
||||
$type = $data['type'];
|
||||
|
||||
$can = $filter->apply('can_update_texture_type', true, [$texture, $type]);
|
||||
if ($can instanceof Rejection) {
|
||||
return json($can->getReason(), 1);
|
||||
}
|
||||
|
||||
$dispatcher->dispatch('texture.type.updating', [$texture, $type]);
|
||||
|
||||
$old = $texture->replicate();
|
||||
$texture->type = $type;
|
||||
$texture->save();
|
||||
|
||||
$dispatcher->dispatch('texture.type.updated', [$texture, $old]);
|
||||
|
||||
return json(trans('skinlib.model.success', ['model' => $type]), 0);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user