Add image upload to user edit [ch10508] (#7877)

* Use correct Request include

* Updated to use additional form request

* Added SVG sanitizer

* Added response method to form request

* Allow ImageUploadRequest to accept fieldname params, added SVG sanitization, fixed delete

* Fixed upload path for avatars

* Added fieldname variable to blade partial for image upload

* Added enctype="multipart/form-data"  to form to allow uploads

* Added image field

* Updated Request::old() to use $request->old()

* Fixed derp in edit blade referring to $item when it should be $user

* Added svg+xml to image rule
This commit is contained in:
snipe 2020-03-05 18:00:24 -08:00 committed by GitHub
parent 9aed12c5aa
commit 039f5da0e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 25 deletions

View file

@ -5,6 +5,7 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Controllers\UserNotFoundException; use App\Http\Controllers\UserNotFoundException;
use App\Http\Requests\SaveUserRequest; use App\Http\Requests\SaveUserRequest;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Asset; use App\Models\Asset;
use App\Models\Company; use App\Models\Company;
use App\Models\Group; use App\Models\Group;
@ -19,7 +20,7 @@ use Redirect;
use Str; use Str;
use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\StreamedResponse;
use View; use View;
use Request; use Illuminate\Http\Request;
/** /**
@ -65,12 +66,12 @@ class UsersController extends Controller
$userGroups = collect(); $userGroups = collect();
if (Request::old('groups')) { if ($request->old('groups')) {
$userGroups = Group::whereIn('id', Request::old('groups'))->pluck('name', 'id'); $userGroups = Group::whereIn('id', $request->old('groups'))->pluck('name', 'id');
} }
$permissions = config('permissions'); $permissions = config('permissions');
$userPermissions = Helper::selectedPermissionsArray($permissions, Request::old('permissions', array())); $userPermissions = Helper::selectedPermissionsArray($permissions, $request->old('permissions', array()));
$permissions = $this->filterDisplayable($permissions); $permissions = $this->filterDisplayable($permissions);
$user = new User; $user = new User;
@ -125,6 +126,8 @@ class UsersController extends Controller
} }
$user->permissions = json_encode($permissions_array); $user->permissions = json_encode($permissions_array);
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, '', 'avatar', 'avatars');
if ($user->save()) { if ($user->save()) {
if ($request->filled('groups')) { if ($request->filled('groups')) {
$user->groups()->sync($request->input('groups')); $user->groups()->sync($request->input('groups'));
@ -201,7 +204,7 @@ class UsersController extends Controller
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Illuminate\Auth\Access\AuthorizationException
*/ */
public function update(SaveUserRequest $request, $id = null) public function update(Request $request, $id = null)
{ {
// We need to reverse the UI specific logic for our // We need to reverse the UI specific logic for our
// permissions here before we update the user. // permissions here before we update the user.
@ -218,6 +221,7 @@ class UsersController extends Controller
try { try {
$user = User::findOrFail($id); $user = User::findOrFail($id);
app('App\Http\Requests\SaveUserRequest');
if ($user->id == $request->input('manager_id')) { if ($user->id == $request->input('manager_id')) {
return redirect()->back()->withInput()->with('error', 'You cannot be your own manager.'); return redirect()->back()->withInput()->with('error', 'You cannot be your own manager.');
@ -291,6 +295,9 @@ class UsersController extends Controller
$user->permissions = json_encode($permissions_array); $user->permissions = json_encode($permissions_array);
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, '', 'avatar', 'avatars');
// Was the user updated? // Was the user updated?
if ($user->save()) { if ($user->save()) {
// Redirect to the user page // Redirect to the user page

View file

@ -4,6 +4,7 @@ namespace App\Http\Requests;
use App\Models\SnipeModel; use App\Models\SnipeModel;
use Intervention\Image\Facades\Image; use Intervention\Image\Facades\Image;
use enshrined\svgSanitize\Sanitizer;
use Storage; use Storage;
class ImageUploadRequest extends Request class ImageUploadRequest extends Request
@ -26,8 +27,8 @@ class ImageUploadRequest extends Request
public function rules() public function rules()
{ {
return [ return [
'image' => 'mimes:png,gif,jpg,jpeg,svg', 'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml',
'avatar' => 'mimes:png,gif,jpg,jpeg,svg', 'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml',
]; ];
} }
@ -42,46 +43,65 @@ class ImageUploadRequest extends Request
* @param String $path location for uploaded images, defaults to uploads/plural of item type. * @param String $path location for uploaded images, defaults to uploads/plural of item type.
* @return SnipeModel Target asset is being checked out to. * @return SnipeModel Target asset is being checked out to.
*/ */
public function handleImages($item, $w = 550, $path = null) public function handleImages($item, $w = 550, $fieldname = 'image', $path = null)
{ {
$type = strtolower(class_basename(get_class($item))); $type = strtolower(class_basename(get_class($item)));
if(is_null($path)) { if (is_null($path)) {
$path = str_plural($type); $path = str_plural($type);
} }
\Log::debug('Image path is: '.$path);
\Log::debug('Image fieldname is: '.$fieldname);
if ($this->hasFile($fieldname)) {
if ($this->hasFile('image')) {
if (!config('app.lock_passwords')) { if (!config('app.lock_passwords')) {
if(!Storage::disk('public')->exists($path)) Storage::disk('public')->makeDirectory($path, 775); if (!Storage::disk('public')->exists($path)) Storage::disk('public')->makeDirectory($path, 775);
$upload = $image = $this->file('image'); $image = $this->file($fieldname);
$ext = $image->getClientOriginalExtension(); $ext = $image->getClientOriginalExtension();
$file_name = $type.'-'.str_random(18).'.'.$ext; $file_name = $type.'-'.str_random(18).'.'.$ext;
if ($image->getClientOriginalExtension()!='svg') { if ($image->getClientOriginalExtension()!='svg') {
$upload = Image::make($image->getRealPath())->resize(null, $w, function ($constraint) { $upload = Image::make($image->getRealPath())->resize(null, $w, function ($constraint) {
$constraint->aspectRatio(); $constraint->aspectRatio();
$constraint->upsize(); $constraint->upsize();
}); });
// This requires a string instead of an object, so we use ($string)
Storage::disk('public')->put($path.'/'.$file_name, (string)$upload->encode());
// If the file is an SVG, we need to clean it and NOT encode it
} else {
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($image->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
Storage::disk('public')->put($path.'/'.$file_name, $cleanSVG);
} }
// This requires a string instead of an object, so we use ($string) // Remove current image if it exists and we're uploading a new one
Storage::disk('public')->put($path.'/'.$file_name, (string)$upload->encode()); if (($item->{$fieldname}) && (Storage::disk('public')->exists($path.'/'.$item->{$fieldname}))) {
Storage::disk('public')->delete($path.'/'.$item->{$fieldname});
// Remove Current image if exists } else {
if (($item->image) && (file_exists($path.'/'.$item->image))) { \Log::debug('Could not delete old file. '.$path.'/'.$item->{$fieldname}.' does not exist?');
Storage::disk('public')->delete($path.'/'.$file_name);
} }
$item->image = $file_name; // Assign the new filename as the fieldname
$item->{$fieldname} = $file_name;
} }
// If the user isn't uploading anything new but wants to delete their old image, do so
} elseif ($this->input('image_delete')=='1') { } elseif ($this->input('image_delete')=='1') {
Storage::disk('public')->delete($path.'/'.$item->image); Storage::disk('public')->delete($path.'/'.$item->{$fieldname});
$item->image = null; $item->{$fieldname} = null;
} }
return $item; return $item;
} }
} }

View file

@ -19,6 +19,12 @@ class SaveUserRequest extends FormRequest
return true; return true;
} }
public function response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *

View file

@ -76,7 +76,7 @@ class SettingsServiceProvider extends ServiceProvider
// Users // Users
\App::singleton('users_upload_path', function(){ \App::singleton('users_upload_path', function(){
return 'users/'; return 'avatars/';
}); });
\App::singleton('users_upload_url', function(){ \App::singleton('users_upload_url', function(){

View file

@ -1,9 +1,9 @@
<div class="form-group {{ $errors->has('image') ? 'has-error' : '' }}"> <div class="form-group {{ $errors->has((isset($fieldname) ? $fieldname : 'image')) ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="image">{{ trans('general.image_upload') }}</label> <label class="col-md-3 control-label" for="image">{{ trans('general.image_upload') }}</label>
<div class="col-md-9"> <div class="col-md-9">
<label class="btn btn-default"> <label class="btn btn-default">
{{ trans('button.select_file') }} {{ trans('button.select_file') }}
<input type="file" name="image" class="js-uploadFile" id="uploadFile" data-maxsize="{{ \App\Helpers\Helper::file_upload_max_size() }}" accept="image/gif,image/jpeg,image/png,image/svg" style="display:none; max-width: 90%"> <input type="file" name="{{ (isset($fieldname) ? $fieldname : 'image') }}" class="js-uploadFile" id="uploadFile" data-maxsize="{{ \App\Helpers\Helper::file_upload_max_size() }}" accept="image/gif,image/jpeg,image/png,image/svg,image/svg+xml,image/bmp" style="display:none; max-width: 90%">
</label> </label>
<span class='label label-default' id="uploadFile-info"></span> <span class='label label-default' id="uploadFile-info"></span>

View file

@ -67,7 +67,7 @@
<div class="row"> <div class="row">
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<form class="form-horizontal" method="post" autocomplete="off" action="{{ (isset($user->id)) ? route('users.update', ['user' => $user->id]) : route('users.store') }}" id="userForm"> <form class="form-horizontal" method="post" autocomplete="off" action="{{ (isset($user->id)) ? route('users.update', ['user' => $user->id]) : route('users.store') }}" enctype="multipart/form-data" id="userForm">
{{csrf_field()}} {{csrf_field()}}
@if($user->id) @if($user->id)
@ -213,6 +213,22 @@
@include ('partials.forms.edit.company-select', ['translated_name' => trans('general.select_company'), 'fieldname' => 'company_id']) @include ('partials.forms.edit.company-select', ['translated_name' => trans('general.select_company'), 'fieldname' => 'company_id'])
@endif @endif
<!-- Image -->
@if ($user->avatar)
<div class="form-group {{ $errors->has('image_delete') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="image_delete">{{ trans('general.image_delete') }}</label>
<div class="col-md-5">
{{ Form::checkbox('image_delete') }}
<img src="{{ Storage::disk('public')->url(app('users_upload_path').e($user->avatar)) }}" class="img-responsive" />
{!! $errors->first('image_delete', '<span class="alert-msg"><br>:message</span>') !!}
</div>
</div>
@endif
@include ('partials.forms.edit.image-upload', ['fieldname' => 'avatar'])
<!-- language --> <!-- language -->
<div class="form-group {{ $errors->has('locale') ? 'has-error' : '' }}"> <div class="form-group {{ $errors->has('locale') ? 'has-error' : '' }}">
<label class="col-md-3 control-label" for="locale">{{ trans('general.language') }}</label> <label class="col-md-3 control-label" for="locale">{{ trans('general.language') }}</label>