FEATURE #1337: add file upload

This commit registers new items into admin menu and adds functionality for uploading files.
This commit is contained in:
Michal Kváček
2016-02-08 14:27:43 +01:00
parent 58a3386277
commit 24144f6aa4
19 changed files with 489 additions and 53 deletions
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class UploadFile extends Event
{
use SerializesModels;
/**
* @var UploadedFile
*/
private $uploadedFile;
/**
* @var
*/
private $id;
/**
* Create a new event instance.
*
* @param File $uploadedFile
* @param $id
*/
public function __construct(File $uploadedFile, $id)
{
$this->uploadedFile = $uploadedFile;
$this->id = $id;
}
/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return [];
}
}
+97
View File
@@ -0,0 +1,97 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests\UploadFileRequest;
use App\Http\Requests\UploadImageRequest;
use App\Models\File;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
class UploadFile extends Controller
{
/**
* @var File
*/
private $file;
function __construct(File $file)
{
parent::__construct();
$this->file = $file;
}
public function getFile()
{
return view('admin/fileManager.newFile');
}
public function getImage()
{
}
/**
* Uploads any file
*
* @param UploadFileRequest $request
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postFile(UploadFileRequest $request)
{
if (!$request->hasFile('file')) {
return $this->invalidUpload('noFileSpecified');
}
try {
$uploadedFile = $this->file->upload($request->file('file'), $request);
// we do not want to redirect
if ($request->ajax()) {
return $this->jsonResponse($uploadedFile);
}
// upload was not performed via ajax, redirect user
return redirect()
->route('upload.file')
->with('success', trans('admin/fileManager.uploadSuccessfull'));
} catch (FileException $e) {
return $this->invalidUpload('invalidFile');
}
}
protected function invalidUpload($message)
{
return redirect()
->route('upload.file')
->with('error', trans('admin/fileManager.' . $message));
}
/**
* Uploads only images
*
* @param UploadImageRequest $request
*/
public function postImage(UploadImageRequest $request)
{
}
/**
* Send response about uploaded file as JSON
*
* @param $uploadedFile
*
* @return \Illuminate\Http\JsonResponse
*/
private function jsonResponse($uploadedFile)
{
$filename = $uploadedFile['file']->getFilename();
$url = config('app.upload_path') . DIRECTORY_SEPARATOR . $filename;
$data = [
'filename' => $filename,
'url' => $url,
'id' => $uploadedFile['id']
];
return response()->json($data);
}
}
-26
View File
@@ -1,26 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests\UploadFileRequest;
use App\Http\Requests\UploadImageRequest;
class Uploader extends Controller
{
/**
* Uploads any file
*
* @param UploadFileRequest $request
*/
public function postFile(UploadFileRequest $request)
{
}
/**
* Uploads only images
*
* @param UploadImageRequest $request
*/
public function postImage(UploadImageRequest $request)
{
}
}
+4 -8
View File
@@ -2,17 +2,13 @@
namespace App\Http\Requests;
class UploadFileRequest extends Request
class UploadFileRequest extends UploadRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
* @internal param array $rules
*/
public function rules()
protected function setRules()
{
return [
'file' => 'required'
];
$this->rules = ['file' => 'required|image'];
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
abstract class UploadRequest extends Request
{
protected $rules = [];
abstract protected function setRules();
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
// some globaly defined rules
$rules = [
'keywords' => 'max:255'
];
// setup additional rules
$this->setRules();
foreach ($this->rules as $field => $rule) {
$rules[$field] = $rule;
}
return $rules;
}
}
+5 -1
View File
@@ -90,12 +90,16 @@ Route::group([
->name('history.diff');
// uploader methods
Route::post('upload/file', 'Uploader@postFile')
Route::get('upload/file', 'UploadFile@getFile')
->name('upload.file');
Route::post('upload/image', 'Uploader@postImage')
->name('upload.image');
// uploader methods
Route::post('upload/file', 'UploadFile@postFile')
->name('upload.postFile');
// custom fields
// ------------------------------
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Listeners;
use App\Events\Uploadfile;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class LogUploadFile
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param Uploadfile $event
* @return void
*/
public function handle(Uploadfile $event)
{
//
}
}
+103 -6
View File
@@ -2,24 +2,121 @@
namespace App\Models;
use App\Events\UploadFile;
use App\Http\Requests\UploadRequest;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Event;
use Symfony\Component\HttpFoundation\File\File as SymphonyFile;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Class File
*/
class File extends Model
{
/**
* Hash algorithm used for hashing file
*/
const HASH_ALGORITHM = 'sha256';
/**
* @var bool $timstamps have created_at and updated_at fields
*/
public $timestamps = true;
protected $fillable = [
'user_id',
'filename',
'sha256_hash',
'mime_type'
'original_filename',
'file_hash',
'hash_algorithm',
'mime_type',
'description',
'keywords',
'directory'
];
protected $guarded = [];
/**
* Getter for filename of uploaded file.
*
* This filename is used for localy stored file.
*
* @param UploadedFile $file
*
* @return string
*/
public function getFilename(UploadedFile $file)
{
return uniqid() . '.' . $file->getClientOriginalExtension();
}
/**
* Saves file into FS and creates new record in database
*
* @param UploadedFile $file
* @param UploadRequest $request
*
* @return array associative array - key 'file' is type of SymphonyFile and contains info about real file, key 'id'
* is ID of row in files table
*/
public function upload(UploadedFile $file, UploadRequest $request)
{
$destination = config('app.upload_path');
$filename = $this->getFilename($file);
// save file into physical location (local FS)
$uploadedFile = $file->move($destination, $filename);
// insert record into database
$id = $this->saveIntoDatabase($uploadedFile, $request, $file->getClientOriginalName());
// fire event about new file
Event::fire(new UploadFile($uploadedFile, $id));
return [
'file' => $file,
'id' => $id
];
}
/**
* Save file into database
*
* This file saves new record into database. It also calculates hash of given file, obtains MIME type etc.
*
* @param SymphonyFile $file
* @param UploadRequest $request
* @param $originalFilename
*
* @return mixed
*/
protected function saveIntoDatabase(SymphonyFile $file, UploadRequest $request, $originalFilename)
{
// get path and hash of uploaded file
$path = config('app.upload_path') . DIRECTORY_SEPARATOR . $file->getFilename();
$hash = hash_file(self::HASH_ALGORITHM, $path);
// define mandatory fields
$params = [
'user_id' => Auth::user()->id,
'filename' => $file->getFilename(),
'original_filename' => $originalFilename,
'file_hash' => $hash,
'hash_algorithm' => self::HASH_ALGORITHM,
'mime_type' => $file->getMimeType(),
];
// save optional fields
$optionalFields = ['description', 'keywords', 'directory'];
foreach ($optionalFields as $field) {
if ($request->has($field)) {
$params[$field] = $request->$field;
}
}
// create new database row
$newFile = File::create($params);
return $newFile->id;
}
}
+3
View File
@@ -32,6 +32,9 @@ class AdminMenusProvider extends ServiceProvider
$sub->route('users.overview', trans('admin/mainMenu.usersOverview'), [], 30, ['icon' => 'fa fa-users']);
}, 20, ['icon' => 'fa fa-users']);
$menu->dropdown(trans('admin/mainMenu.fileManager'), function ($sub) {
$sub->route('upload.file', trans('admin/mainMenu.uploadFile'), [], 10, ['icon' => 'fa fa-file']);
}, 25, ['icon' => 'fa fa-film']);
$menu->dropdown(trans('admin/mainMenu.customFields'), function ($sub) {
$sub->route('customfields.new', trans('admin/mainMenu.customFieldsNew'), [], 10, ['icon' => 'fa fa-code']);
$sub->route('customfields.overview', trans('admin/mainMenu.customFieldsOverview'), [], 50, ['icon' => 'fa fa-code-fork']);
+5 -2
View File
@@ -31,6 +31,9 @@ class EventServiceProvider extends ServiceProvider
'App\Events\ChangeSettings' => [
'App\Listeners\LogChangeSettings',
],
'App\Events\UploadFile' => [
'App\Listeners\LogUploadFile'
],
// internal Laravel events - should work as auth.login and auth.logout, however it doesn't...
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogLogin'
@@ -43,13 +46,13 @@ class EventServiceProvider extends ServiceProvider
/**
* Register any other events for your application.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @param \Illuminate\Contracts\Events\Dispatcher $events
*
* @return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
//
}
}