mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-23 11:04:17 +08:00
Added view, deletion and permissions for files
This commit is contained in:
parent
673c74ddfc
commit
ac0b29fb6d
10
app/File.php
10
app/File.php
|
@ -7,12 +7,20 @@ class File extends Ownable
|
|||
|
||||
/**
|
||||
* Get the page this file was uploaded to.
|
||||
* @return mixed
|
||||
* @return Page
|
||||
*/
|
||||
public function page()
|
||||
{
|
||||
return $this->belongsTo(Page::class, 'uploaded_to');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url of this file.
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return '/files/' . $this->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Http\Controllers;
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Exceptions\FileUploadException;
|
||||
use BookStack\File;
|
||||
use BookStack\Page;
|
||||
use BookStack\Repos\PageRepo;
|
||||
use BookStack\Services\FileService;
|
||||
use Illuminate\Http\Request;
|
||||
|
@ -37,16 +34,18 @@ class FileController extends Controller
|
|||
*/
|
||||
public function upload(Request $request)
|
||||
{
|
||||
// TODO - Add file upload permission check
|
||||
// TODO - ensure user has permission to edit relevant page.
|
||||
// TODO - ensure uploads are deleted on page delete.
|
||||
|
||||
$this->validate($request, [
|
||||
'uploaded_to' => 'required|integer|exists:pages,id'
|
||||
]);
|
||||
|
||||
$uploadedFile = $request->file('file');
|
||||
$pageId = $request->get('uploaded_to');
|
||||
$page = $this->pageRepo->getById($pageId);
|
||||
|
||||
$this->checkPermission('file-create-all');
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
|
||||
$uploadedFile = $request->file('file');
|
||||
|
||||
try {
|
||||
$file = $this->fileService->saveNewUpload($uploadedFile, $pageId);
|
||||
|
@ -62,10 +61,10 @@ class FileController extends Controller
|
|||
* @param $pageId
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFilesForPage($pageId)
|
||||
public function listForPage($pageId)
|
||||
{
|
||||
// TODO - check view permission on page?
|
||||
$page = $this->pageRepo->getById($pageId);
|
||||
$this->checkOwnablePermission('page-view', $page);
|
||||
return response()->json($page->files);
|
||||
}
|
||||
|
||||
|
@ -75,17 +74,47 @@ class FileController extends Controller
|
|||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function sortFilesForPage($pageId, Request $request)
|
||||
public function sortForPage($pageId, Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'files' => 'required|array',
|
||||
'files.*.id' => 'required|integer',
|
||||
]);
|
||||
$page = $this->pageRepo->getById($pageId);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
|
||||
$files = $request->get('files');
|
||||
$this->fileService->updateFileOrderWithinPage($files, $pageId);
|
||||
return response()->json(['message' => 'File order updated']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file from storage.
|
||||
* @param $fileId
|
||||
*/
|
||||
public function get($fileId)
|
||||
{
|
||||
$file = $this->file->findOrFail($fileId);
|
||||
$page = $this->pageRepo->getById($file->uploaded_to);
|
||||
$this->checkOwnablePermission('page-view', $page);
|
||||
|
||||
$fileContents = $this->fileService->getFile($file);
|
||||
return response($fileContents, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="'. $file->name .'"'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a specific file in the system.
|
||||
* @param $fileId
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete($fileId)
|
||||
{
|
||||
$file = $this->file->findOrFail($fileId);
|
||||
$this->checkOwnablePermission($file, 'file-delete');
|
||||
$this->fileService->deleteFile($file);
|
||||
return response()->json(['message' => 'File deleted']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,24 @@
|
|||
use BookStack\Exceptions\FileUploadException;
|
||||
use BookStack\File;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
||||
class FileService extends UploadService
|
||||
{
|
||||
|
||||
/**
|
||||
* Get a file from storage.
|
||||
* @param File $file
|
||||
* @return string
|
||||
*/
|
||||
public function getFile(File $file)
|
||||
{
|
||||
$filePath = $this->getStorageBasePath() . $file->path;
|
||||
return $this->getStorage()->get($filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new file upon user upload.
|
||||
* @param UploadedFile $uploadedFile
|
||||
|
@ -76,4 +88,22 @@ class FileService extends UploadService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file and any empty folders the deletion leaves.
|
||||
* @param File $file
|
||||
*/
|
||||
public function deleteFile(File $file)
|
||||
{
|
||||
$storedFilePath = $this->getStorageBasePath() . $file->path;
|
||||
$storage = $this->getStorage();
|
||||
$dirPath = dirname($storedFilePath);
|
||||
|
||||
$storage->delete($storedFilePath);
|
||||
if (count($storage->allFiles($dirPath)) === 0) {
|
||||
$storage->deleteDirectory($dirPath);
|
||||
}
|
||||
|
||||
$file->delete();
|
||||
}
|
||||
|
||||
}
|
|
@ -28,6 +28,26 @@ class CreateFilesTable extends Migration
|
|||
$table->index('uploaded_to');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Get roles with permissions we need to change
|
||||
$adminRoleId = DB::table('roles')->where('system_name', '=', 'admin')->first()->id;
|
||||
|
||||
// Create & attach new entity permissions
|
||||
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
|
||||
$entity = 'File';
|
||||
foreach ($ops as $op) {
|
||||
$permissionId = DB::table('role_permissions')->insertGetId([
|
||||
'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
|
||||
'display_name' => $op . ' ' . $entity . 's',
|
||||
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
|
||||
'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
|
||||
]);
|
||||
DB::table('permission_role')->insert([
|
||||
'role_id' => $adminRoleId,
|
||||
'permission_id' => $permissionId
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,5 +58,17 @@ class CreateFilesTable extends Migration
|
|||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('files');
|
||||
|
||||
// Get roles with permissions we need to change
|
||||
$adminRoleId = DB::table('roles')->where('system_name', '=', 'admin')->first()->id;
|
||||
|
||||
// Create & attach new entity permissions
|
||||
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
|
||||
$entity = 'File';
|
||||
foreach ($ops as $op) {
|
||||
$permName = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
|
||||
$permission = DB::table('role_permissions')->where('name', '=', $permName)->get();
|
||||
DB::table('permission_role')->where('permission_id', '=', $permission->id)->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -575,9 +575,9 @@ module.exports = function (ngApp, events) {
|
|||
*/
|
||||
function getFiles() {
|
||||
let url = window.baseUrl(`/files/get/page/${pageId}`)
|
||||
$http.get(url).then(responseData => {
|
||||
$scope.files = responseData.data;
|
||||
currentOrder = responseData.data.map(file => {return file.id}).join(':');
|
||||
$http.get(url).then(resp => {
|
||||
$scope.files = resp.data;
|
||||
currentOrder = resp.data.map(file => {return file.id}).join(':');
|
||||
});
|
||||
}
|
||||
getFiles();
|
||||
|
@ -595,6 +595,17 @@ module.exports = function (ngApp, events) {
|
|||
events.emit('success', 'File uploaded');
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a file from the server and, on success, the local listing.
|
||||
* @param file
|
||||
*/
|
||||
$scope.deleteFile = function(file) {
|
||||
$http.delete(`/files/${file.id}`).then(resp => {
|
||||
events.emit('success', resp.data.message);
|
||||
$scope.files.splice($scope.files.indexOf(file), 1);
|
||||
});
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
};
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<tr ng-repeat="file in files track by $index">
|
||||
<td width="20" ><i class="handle zmdi zmdi-menu"></i></td>
|
||||
<td ng-bind="file.name"></td>
|
||||
<td width="10" class="text-center text-neg" style="padding: 0;"><i class="zmdi zmdi-close"></i></td>
|
||||
<td width="10" ng-click="deleteFile(file)" class="text-center text-neg" style="padding: 0;"><i class="zmdi zmdi-close"></i></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
|
||||
<div class="book-tree" ng-non-bindable>
|
||||
|
||||
@if ($page->files->count() > 0)
|
||||
<h6 class="text-muted">Attachments</h6>
|
||||
@foreach($page->files as $file)
|
||||
<div class="attachment">
|
||||
<a href="{{ $file->getUrl() }}"><i class="zmdi zmdi-file"></i> {{ $file->name }}</a>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
@if (isset($pageNav) && $pageNav)
|
||||
<h6 class="text-muted">Page Navigation</h6>
|
||||
<div class="sidebar-page-nav menu">
|
||||
|
@ -10,8 +19,6 @@
|
|||
</li>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
|
||||
@endif
|
||||
|
||||
<h6 class="text-muted">Book Navigation</h6>
|
||||
|
|
|
@ -106,6 +106,19 @@
|
|||
<label>@include('settings/roles/checkbox', ['permission' => 'image-delete-all']) All</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attached <br>Files</td>
|
||||
<td>@include('settings/roles/checkbox', ['permission' => 'file-create-all'])</td>
|
||||
<td style="line-height:1.2;"><small class="faded">Controlled by the asset they are uploaded to</small></td>
|
||||
<td>
|
||||
<label>@include('settings/roles/checkbox', ['permission' => 'file-update-own']) Own</label>
|
||||
<label>@include('settings/roles/checkbox', ['permission' => 'file-update-all']) All</label>
|
||||
</td>
|
||||
<td>
|
||||
<label>@include('settings/roles/checkbox', ['permission' => 'file-delete-own']) Own</label>
|
||||
<label>@include('settings/roles/checkbox', ['permission' => 'file-delete-all']) All</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -88,9 +88,11 @@ Route::group(['middleware' => 'auth'], function () {
|
|||
});
|
||||
|
||||
// File routes
|
||||
Route::get('/files/{id}', 'FileController@get');
|
||||
Route::post('/files/upload', 'FileController@upload');
|
||||
Route::get('/files/get/page/{pageId}', 'FileController@getFilesForPage');
|
||||
Route::put('/files/sort/page/{pageId}', 'FileController@sortFilesForPage');
|
||||
Route::get('/files/get/page/{pageId}', 'FileController@listForPage');
|
||||
Route::put('/files/sort/page/{pageId}', 'FileController@sortForPage');
|
||||
Route::delete('/files/{id}', 'FileController@delete');
|
||||
|
||||
// AJAX routes
|
||||
Route::put('/ajax/page/{id}/save-draft', 'PageController@saveDraft');
|
||||
|
|
Loading…
Reference in New Issue
Block a user