blessing-skin-server/app/Http/Controllers/SkinlibController.php

402 lines
14 KiB
PHP
Raw Normal View History

2016-07-21 22:01:57 +08:00
<?php
2016-08-28 10:05:21 +08:00
namespace App\Http\Controllers;
2016-07-21 22:01:57 +08:00
use App\Models\Player;
2016-07-21 22:01:57 +08:00
use App\Models\Texture;
2019-12-14 11:10:37 +08:00
use App\Models\User;
use Auth;
use Blessing\Filter;
2020-03-24 18:05:46 +08:00
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
2019-12-14 11:10:37 +08:00
use Illuminate\Http\Request;
use Option;
2019-12-25 15:48:34 +08:00
use Parsedown;
2019-12-14 11:10:37 +08:00
use Storage;
2016-07-21 22:01:57 +08:00
class SkinlibController extends Controller
2016-07-21 22:01:57 +08:00
{
2018-08-17 22:54:26 +08:00
/**
* Map error code of file uploading to human-readable text.
*
* @see http://php.net/manual/en/features.file-upload.errors.php
2019-12-14 11:10:37 +08:00
*
2018-08-17 22:54:26 +08:00
* @var array
*/
public static $phpFileUploadErrors = [
0 => 'There is no error, the file uploaded with success',
1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
3 => 'The uploaded file was only partially uploaded',
4 => 'No file was uploaded',
6 => 'Missing a temporary folder',
7 => 'Failed to write file to disk.',
8 => 'A PHP extension stopped the file upload.',
];
2020-03-24 18:05:46 +08:00
public function library(Request $request)
{
2019-03-14 23:55:49 +08:00
$user = Auth::user();
2016-07-26 13:36:24 +08:00
// Available filters: skin, steve, alex, cape
2020-03-24 18:05:46 +08:00
$type = $request->input('filter', 'skin');
$uploader = $request->input('uploader');
$keyword = $request->input('keyword');
2019-03-19 15:19:33 +08:00
$sort = $request->input('sort', 'time');
$sortBy = $sort == 'time' ? 'upload_at' : $sort;
2020-03-24 18:05:46 +08:00
return Texture::orderBy($sortBy, 'desc')
->when($type === 'skin', function (Builder $query) {
return $query->whereIn('type', ['steve', 'alex']);
}, function (Builder $query) use ($type) {
return $query->where('type', $type);
})
->when($keyword, function (Builder $query, $keyword) {
return $query->like('name', $keyword);
})
->when($uploader, function (Builder $query, $uploader) {
return $query->where('uploader', $uploader);
})
->when($user, function (Builder $query, User $user) {
if (!$user->isAdmin()) {
2020-04-27 18:46:22 +08:00
// 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);
});
2020-03-24 18:05:46 +08:00
}
}, 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);
2016-07-21 22:01:57 +08:00
}
public function show(Filter $filter, $tid)
2016-07-21 22:01:57 +08:00
{
$texture = Texture::find($tid);
$user = Auth::user();
2019-12-14 11:10:37 +08:00
if (!$texture || $texture && !Storage::disk('textures')->has($texture->hash)) {
if (option('auto_del_invalid_texture')) {
if ($texture) {
2016-08-28 20:33:35 +08:00
$texture->delete();
}
2016-08-28 20:33:35 +08:00
2016-09-24 22:49:20 +08:00
abort(404, trans('skinlib.show.deleted'));
}
2016-09-24 22:49:20 +08:00
abort(404, trans('skinlib.show.deleted').trans('skinlib.show.contact-admin'));
2016-08-16 22:58:21 +08:00
}
2019-12-14 11:10:37 +08:00
if (!$texture->public) {
if (!Auth::check() || ($user->uid != $texture->uploader && !$user->isAdmin())) {
abort(option('status_code_for_private'), trans('skinlib.show.private'));
}
2016-07-21 22:01:57 +08:00
}
$badges = [];
2020-04-01 10:07:34 +08:00
$uploader = $texture->owner;
if ($uploader) {
if ($uploader->isAdmin()) {
$badges[] = ['text' => 'STAFF', 'color' => 'primary'];
}
$badges = $filter->apply('user_badges', $badges, [$uploader]);
}
2019-12-16 10:49:09 +08:00
$grid = [
'layout' => [
['md-8', 'md-4'],
],
'widgets' => [
[
['shared.previewer'],
2020-03-20 16:19:18 +08:00
['skinlib.widgets.show.side'],
2019-12-16 10:49:09 +08:00
],
],
];
$grid = $filter->apply('grid:skinlib.show', $grid);
return view('skinlib.show')
->with('texture', $texture)
2019-12-16 10:49:09 +08:00
->with('grid', $grid)
2019-03-23 19:52:14 +08:00
->with('extra', [
'download' => option('allow_downloading_texture'),
'currentUid' => $user ? $user->uid : 0,
'admin' => $user && $user->isAdmin(),
'inCloset' => $user && $user->closet()->where('tid', $texture->tid)->count() > 0,
2020-04-01 10:07:34 +08:00
'uploaderExists' => (bool) $uploader,
'nickname' => optional($uploader)->nickname ?? trans('general.unexistent-user'),
'report' => intval(option('reporter_score_modification', 0)),
'badges' => $badges,
2019-03-23 19:52:14 +08:00
]);
2016-07-21 22:01:57 +08:00
}
public function info($tid)
{
2016-12-10 19:49:45 +08:00
if ($t = Texture::find($tid)) {
2019-04-23 19:14:41 +08:00
return json('', 0, $t->toArray());
2016-12-10 19:49:45 +08:00
} else {
2019-04-23 19:14:41 +08:00
return abort(404);
2016-12-10 19:49:45 +08:00
}
2016-07-21 22:01:57 +08:00
}
2019-12-16 11:02:39 +08:00
public function upload(Filter $filter)
2016-07-21 22:01:57 +08:00
{
2019-12-16 11:02:39 +08:00
$grid = [
'layout' => [
['md-6', 'md-6'],
],
'widgets' => [
[
['skinlib.widgets.upload.input'],
['shared.previewer'],
],
],
];
$grid = $filter->apply('grid:skinlib.upload', $grid);
2019-12-25 15:48:34 +08:00
$parsedown = new Parsedown();
return view('skinlib.upload')
2019-12-16 11:02:39 +08:00
->with('grid', $grid)
2019-03-23 19:52:14 +08:00
->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')]
),
'scorePublic' => intval(option('score_per_storage')),
'scorePrivate' => intval(option('private_score_per_storage')),
'closetItemCost' => intval(option('score_per_closet_item')),
2019-03-23 19:52:14 +08:00
'award' => intval(option('score_award_per_texture')),
2019-12-25 15:48:34 +08:00
'contentPolicy' => $parsedown->text(option_localized('content_policy')),
2019-09-18 23:06:48 +08:00
]);
2016-07-21 22:01:57 +08:00
}
public function handleUpload(Request $request)
2016-07-21 22:01:57 +08:00
{
$user = Auth::user();
2018-07-13 19:02:16 +08:00
if (($response = $this->checkUpload($request)) instanceof JsonResponse) {
return $response;
}
2016-07-21 22:01:57 +08:00
2019-04-04 11:16:04 +08:00
$file = $request->file('file');
$responses = event(new \App\Events\HashingFile($file));
if (isset($responses[0]) && is_string($responses[0])) {
return $responses[0]; // @codeCoverageIgnore
}
$t = new Texture();
$t->name = $request->input('name');
$t->type = $request->input('type');
2019-04-04 11:16:04 +08:00
$t->hash = hash_file('sha256', $file);
$t->size = ceil($request->file('file')->getSize() / 1024);
$t->public = $request->input('public') == 'true';
$t->uploader = $user->uid;
2016-07-21 22:01:57 +08:00
2018-07-16 11:10:01 +08:00
$cost = $t->size * ($t->public ? Option::get('score_per_storage') : Option::get('private_score_per_storage'));
2017-04-22 23:35:25 +08:00
$cost += option('score_per_closet_item');
2019-03-20 23:28:04 +08:00
$cost -= option('score_award_per_texture', 0);
2019-03-23 00:20:28 +08:00
if ($user->score < $cost) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.upload.lack-score'), 7);
}
2016-07-21 22:01:57 +08:00
$repeated = Texture::where('hash', $t->hash)->where('public', true)->first();
if ($repeated) {
// if the texture already uploaded was set to private,
// then allow to re-upload it.
return json(trans('skinlib.upload.repeated'), 2, ['tid' => $repeated->tid]);
2016-07-21 22:01:57 +08:00
}
2019-12-03 15:41:54 +08:00
if (Storage::disk('textures')->missing($t->hash)) {
Storage::disk('textures')->put($t->hash, file_get_contents($request->file('file')));
}
$t->likes++;
2016-07-21 22:01:57 +08:00
$t->save();
2019-07-30 14:29:02 +08:00
$user->score -= $cost;
2019-03-14 23:55:49 +08:00
$user->closet()->attach($t->tid, ['item_name' => $t->name]);
2019-07-30 14:29:02 +08:00
$user->save();
2019-03-14 23:55:49 +08:00
return json(trans('skinlib.upload.success', ['name' => $request->input('name')]), 0, [
'tid' => $t->tid,
]);
}
// @codeCoverageIgnore
2016-07-21 22:01:57 +08:00
2019-03-23 11:06:36 +08:00
public function delete(Request $request)
2016-07-21 22:01:57 +08:00
{
$texture = Texture::find($request->tid);
$user = Auth::user();
2016-07-21 22:01:57 +08:00
2019-12-14 11:10:37 +08:00
if (!$texture) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.non-existent'), 1);
}
2016-07-21 22:01:57 +08:00
2019-12-14 11:10:37 +08:00
if ($texture->uploader != $user->uid && !$user->isAdmin()) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.no-permission'), 1);
}
2016-07-21 22:01:57 +08:00
2016-07-24 15:56:23 +08:00
// check if file occupied
if (Texture::where('hash', $texture->hash)->count() == 1) {
Storage::disk('textures')->delete($texture->hash);
}
2016-07-21 22:01:57 +08:00
2019-05-07 15:16:53 +08:00
$texture->delete();
2019-05-19 13:49:44 +08:00
2019-05-07 15:16:53 +08:00
return json(trans('skinlib.delete.success'), 0);
}
2019-09-07 11:15:23 +08:00
public function privacy(Request $request)
2016-07-21 22:01:57 +08:00
{
$t = Texture::find($request->input('tid'));
$user = $request->user();
2016-07-24 15:56:23 +08:00
2019-12-14 11:10:37 +08:00
if (!$t) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.non-existent'), 1);
}
2016-07-24 15:56:23 +08:00
2019-12-14 11:10:37 +08:00
if ($t->uploader != $user->uid && !$user->isAdmin()) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.no-permission'), 1);
}
2016-07-24 15:56:23 +08:00
2019-08-24 10:22:26 +08:00
$uploader = User::find($t->uploader);
2018-07-16 11:10:01 +08:00
$score_diff = $t->size * (option('private_score_per_storage') - option('score_per_storage')) * ($t->public ? -1 : 1);
2019-03-20 23:28:04 +08:00
if ($t->public && option('take_back_scores_after_deletion', true)) {
$score_diff -= option('score_award_per_texture', 0);
}
2019-03-23 11:06:36 +08:00
if ($uploader->score + $score_diff < 0) {
2017-09-18 19:28:38 +08:00
return json(trans('skinlib.upload.lack-score'), 1);
}
$type = $t->type == 'cape' ? 'cape' : 'skin';
2017-11-24 18:54:30 +08:00
Player::where("tid_$type", $t->tid)
->where('uid', '<>', session('uid'))
2019-03-14 23:55:49 +08:00
->update(["tid_$type" => 0]);
$t->likers()->get()->each(function ($user) use ($t) {
$user->closet()->detach($t->tid);
if (option('return_score')) {
2019-07-30 14:29:02 +08:00
$user->score += option('score_per_closet_item');
$user->save();
2019-03-14 23:55:49 +08:00
}
$t->likes--;
2019-03-14 23:55:49 +08:00
});
2019-07-30 14:29:02 +08:00
$uploader->score += $score_diff;
$uploader->save();
2017-04-21 18:44:11 +08:00
2019-12-14 11:10:37 +08:00
$t->public = !$t->public;
2019-03-02 23:58:35 +08:00
$t->save();
2019-03-03 00:38:44 +08:00
2019-04-23 19:14:41 +08:00
return json(
2019-12-14 11:10:37 +08:00
trans('skinlib.privacy.success', ['privacy' => (!$t->public ? trans('general.private') : trans('general.public'))]),
2019-04-23 19:14:41 +08:00
0
);
}
public function rename(Request $request)
{
$this->validate($request, [
2019-12-14 11:10:37 +08:00
'tid' => 'required|integer',
'new_name' => 'required',
]);
$user = $request->user();
$t = Texture::find($request->input('tid'));
2016-07-24 15:56:23 +08:00
2019-12-14 11:10:37 +08:00
if (!$t) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.non-existent'), 1);
}
2016-07-24 15:56:23 +08:00
2019-12-14 11:10:37 +08:00
if ($t->uploader != $user->uid && !$user->isAdmin()) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.no-permission'), 1);
}
2016-07-24 15:56:23 +08:00
$t->name = $request->input('new_name');
2016-07-24 15:56:23 +08:00
if ($t->save()) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.rename.success', ['name' => $request->input('new_name')]), 0);
2016-07-24 15:56:23 +08:00
}
}
// @codeCoverageIgnore
2016-07-24 15:56:23 +08:00
public function model(Request $request)
{
$user = $request->user();
$data = $this->validate($request, [
2019-12-14 11:10:37 +08:00
'tid' => 'required|integer',
'model' => 'required|in:steve,alex,cape',
]);
$t = Texture::find($request->input('tid'));
2019-12-14 11:10:37 +08:00
if (!$t) {
return json(trans('skinlib.non-existent'), 1);
}
2019-12-14 11:10:37 +08:00
if ($t->uploader != $user->uid && !$user->isAdmin()) {
return json(trans('skinlib.no-permission'), 1);
}
$t->type = $request->input('model');
$t->save();
return json(trans('skinlib.model.success', ['model' => $data['model']]), 0);
}
2018-02-16 17:31:04 +08:00
protected function checkUpload(Request $request)
2016-07-21 22:01:57 +08:00
{
if ($file = $request->files->get('file')) {
if ($file->getError() !== UPLOAD_ERR_OK) {
2018-08-17 22:54:26 +08:00
return json(static::$phpFileUploadErrors[$file->getError()], $file->getError());
}
}
$this->validate($request, [
2019-12-14 11:10:37 +08:00
'name' => [
'required',
option('texture_name_regexp') ? 'regex:'.option('texture_name_regexp') : 'string',
],
2019-12-14 11:10:37 +08:00
'file' => 'required|max:'.option('max_upload_file_size'),
'public' => 'required',
]);
2018-07-20 17:32:27 +08:00
$mime = $request->file('file')->getMimeType();
if ($mime != 'image/png' && $mime != 'image/x-png') {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.upload.type-error'), 1);
}
$type = $request->input('type');
$size = getimagesize($request->file('file'));
$ratio = $size[0] / $size[1];
if ($type == 'steve' || $type == 'alex') {
if ($ratio != 2 && $ratio != 1) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.upload.invalid-size', ['type' => trans('general.skin'), 'width' => $size[0], 'height' => $size[1]]), 1);
}
if ($size[0] % 64 != 0 || $size[1] % 32 != 0) {
2017-01-25 15:34:43 +08:00
return json(trans('skinlib.upload.invalid-hd-skin', ['type' => trans('general.skin'), 'width' => $size[0], 'height' => $size[1]]), 1);
}
} elseif ($type == 'cape') {
if ($ratio != 2) {
2016-09-24 22:49:20 +08:00
return json(trans('skinlib.upload.invalid-size', ['type' => trans('general.cape'), 'width' => $size[0], 'height' => $size[1]]), 1);
}
2016-07-21 22:01:57 +08:00
} else {
2016-09-24 22:49:20 +08:00
return json(trans('general.illegal-parameters'), 1);
2016-07-21 22:01:57 +08:00
}
}
2016-07-21 22:01:57 +08:00
// @codeCoverageIgnore
2016-07-21 22:01:57 +08:00
}