Renamed files to attachments

This commit is contained in:
Dan Brown 2016-11-12 14:12:26 +00:00
parent d439fb459b
commit e639600ba5
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
13 changed files with 318 additions and 318 deletions

View File

@ -1,7 +1,7 @@
<?php namespace BookStack;
class File extends Ownable
class Attachment extends Ownable
{
protected $fillable = ['name', 'order'];
@ -30,7 +30,7 @@ class File extends Ownable
*/
public function getUrl()
{
return baseUrl('/files/' . $this->id);
return baseUrl('/attachments/' . $this->id);
}
}

View File

@ -1,36 +1,36 @@
<?php namespace BookStack\Http\Controllers;
use BookStack\Exceptions\FileUploadException;
use BookStack\File;
use BookStack\Attachment;
use BookStack\Repos\PageRepo;
use BookStack\Services\FileService;
use BookStack\Services\AttachmentService;
use Illuminate\Http\Request;
use BookStack\Http\Requests;
class FileController extends Controller
class AttachmentController extends Controller
{
protected $fileService;
protected $file;
protected $attachmentService;
protected $attachment;
protected $pageRepo;
/**
* FileController constructor.
* @param FileService $fileService
* @param File $file
* AttachmentController constructor.
* @param AttachmentService $attachmentService
* @param Attachment $attachment
* @param PageRepo $pageRepo
*/
public function __construct(FileService $fileService, File $file, PageRepo $pageRepo)
public function __construct(AttachmentService $attachmentService, Attachment $attachment, PageRepo $pageRepo)
{
$this->fileService = $fileService;
$this->file = $file;
$this->attachmentService = $attachmentService;
$this->attachment = $attachment;
$this->pageRepo = $pageRepo;
parent::__construct();
}
/**
* Endpoint at which files are uploaded to.
* Endpoint at which attachments are uploaded to.
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
*/
public function upload(Request $request)
{
@ -42,27 +42,27 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId);
$this->checkPermission('file-create-all');
$this->checkPermission('attachment-create-all');
$this->checkOwnablePermission('page-update', $page);
$uploadedFile = $request->file('file');
try {
$file = $this->fileService->saveNewUpload($uploadedFile, $pageId);
$attachment = $this->attachmentService->saveNewUpload($uploadedFile, $pageId);
} catch (FileUploadException $e) {
return response($e->getMessage(), 500);
}
return response()->json($file);
return response()->json($attachment);
}
/**
* Update an uploaded file.
* @param int $fileId
* Update an uploaded attachment.
* @param int $attachmentId
* @param Request $request
* @return mixed
*/
public function uploadUpdate($fileId, Request $request)
public function uploadUpdate($attachmentId, Request $request)
{
$this->validate($request, [
'uploaded_to' => 'required|integer|exists:pages,id',
@ -71,33 +71,33 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId);
$file = $this->file->findOrFail($fileId);
$attachment = $this->attachment->findOrFail($attachmentId);
$this->checkOwnablePermission('page-update', $page);
$this->checkOwnablePermission('file-create', $file);
$this->checkOwnablePermission('attachment-create', $attachment);
if (intval($pageId) !== intval($file->uploaded_to)) {
if (intval($pageId) !== intval($attachment->uploaded_to)) {
return $this->jsonError('Page mismatch during attached file update');
}
$uploadedFile = $request->file('file');
try {
$file = $this->fileService->saveUpdatedUpload($uploadedFile, $file);
$attachment = $this->attachmentService->saveUpdatedUpload($uploadedFile, $attachment);
} catch (FileUploadException $e) {
return response($e->getMessage(), 500);
}
return response()->json($file);
return response()->json($attachment);
}
/**
* Update the details of an existing file.
* @param $fileId
* @param $attachmentId
* @param Request $request
* @return File|mixed
* @return Attachment|mixed
*/
public function update($fileId, Request $request)
public function update($attachmentId, Request $request)
{
$this->validate($request, [
'uploaded_to' => 'required|integer|exists:pages,id',
@ -107,21 +107,21 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId);
$file = $this->file->findOrFail($fileId);
$attachment = $this->attachment->findOrFail($attachmentId);
$this->checkOwnablePermission('page-update', $page);
$this->checkOwnablePermission('file-create', $file);
$this->checkOwnablePermission('attachment-create', $attachment);
if (intval($pageId) !== intval($file->uploaded_to)) {
if (intval($pageId) !== intval($attachment->uploaded_to)) {
return $this->jsonError('Page mismatch during attachment update');
}
$file = $this->fileService->updateFile($file, $request->all());
return $file;
$attachment = $this->attachmentService->updateFile($attachment, $request->all());
return $attachment;
}
/**
* Attach a link to a page as a file.
* Attach a link to a page.
* @param Request $request
* @return mixed
*/
@ -136,18 +136,18 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId);
$this->checkPermission('file-create-all');
$this->checkPermission('attachment-create-all');
$this->checkOwnablePermission('page-update', $page);
$fileName = $request->get('name');
$attachmentName = $request->get('name');
$link = $request->get('link');
$file = $this->fileService->saveNewFromLink($fileName, $link, $pageId);
$attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, $pageId);
return response()->json($file);
return response()->json($attachment);
}
/**
* Get the files for a specific page.
* Get the attachments for a specific page.
* @param $pageId
* @return mixed
*/
@ -159,7 +159,7 @@ class FileController extends Controller
}
/**
* Update the file sorting.
* Update the attachment sorting.
* @param $pageId
* @param Request $request
* @return mixed
@ -173,42 +173,43 @@ class FileController extends Controller
$page = $this->pageRepo->getById($pageId);
$this->checkOwnablePermission('page-update', $page);
$files = $request->get('files');
$this->fileService->updateFileOrderWithinPage($files, $pageId);
$attachments = $request->get('files');
$this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
return response()->json(['message' => 'Attachment order updated']);
}
/**
* Get a file from storage.
* @param $fileId
* Get an attachment from storage.
* @param $attachmentId
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Symfony\Component\HttpFoundation\Response
*/
public function get($fileId)
public function get($attachmentId)
{
$file = $this->file->findOrFail($fileId);
$page = $this->pageRepo->getById($file->uploaded_to);
$attachment = $this->attachment->findOrFail($attachmentId);
$page = $this->pageRepo->getById($attachment->uploaded_to);
$this->checkOwnablePermission('page-view', $page);
if ($file->external) {
return redirect($file->path);
if ($attachment->external) {
return redirect($attachment->path);
}
$fileContents = $this->fileService->getFile($file);
return response($fileContents, 200, [
$attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
return response($attachmentContents, 200, [
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename="'. $file->getFileName() .'"'
'Content-Disposition' => 'attachment; filename="'. $attachment->getFileName() .'"'
]);
}
/**
* Delete a specific file in the system.
* @param $fileId
* Delete a specific attachment in the system.
* @param $attachmentId
* @return mixed
*/
public function delete($fileId)
public function delete($attachmentId)
{
$file = $this->file->findOrFail($fileId);
$this->checkOwnablePermission('file-delete', $file);
$this->fileService->deleteFile($file);
$attachment = $this->attachment->findOrFail($attachmentId);
$this->checkOwnablePermission('attachment-delete', $attachment);
$this->attachmentService->deleteFile($attachment);
return response()->json(['message' => 'Attachment deleted']);
}
}

View File

@ -55,12 +55,12 @@ class Page extends Entity
}
/**
* Get the files attached to this page.
* Get the attachments assigned to this page.
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function files()
public function attachments()
{
return $this->hasMany(File::class, 'uploaded_to')->orderBy('order', 'asc');
return $this->hasMany(Attachment::class, 'uploaded_to')->orderBy('order', 'asc');
}
/**

View File

@ -5,7 +5,7 @@ use BookStack\Book;
use BookStack\Chapter;
use BookStack\Entity;
use BookStack\Exceptions\NotFoundException;
use BookStack\Services\FileService;
use BookStack\Services\AttachmentService;
use Carbon\Carbon;
use DOMDocument;
use DOMXPath;
@ -636,9 +636,9 @@ class PageRepo extends EntityRepo
$this->permissionService->deleteJointPermissionsForEntity($page);
// Delete AttachedFiles
$fileService = app(FileService::class);
foreach ($page->files as $file) {
$fileService->deleteFile($file);
$attachmentService = app(AttachmentService::class);
foreach ($page->attachments as $attachment) {
$attachmentService->deleteFile($attachment);
}
$page->delete();
@ -647,6 +647,7 @@ class PageRepo extends EntityRepo
/**
* Get the latest pages added to the system.
* @param $count
* @return mixed
*/
public function getRecentlyCreatedPaginated($count = 20)
{
@ -656,6 +657,7 @@ class PageRepo extends EntityRepo
/**
* Get the latest pages added to the system.
* @param $count
* @return mixed
*/
public function getRecentlyUpdatedPaginated($count = 20)
{

View File

@ -0,0 +1,201 @@
<?php namespace BookStack\Services;
use BookStack\Exceptions\FileUploadException;
use BookStack\Attachment;
use Exception;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class AttachmentService extends UploadService
{
/**
* Get an attachment from storage.
* @param Attachment $attachment
* @return string
*/
public function getAttachmentFromStorage(Attachment $attachment)
{
$attachmentPath = $this->getStorageBasePath() . $attachment->path;
return $this->getStorage()->get($attachmentPath);
}
/**
* Store a new attachment upon user upload.
* @param UploadedFile $uploadedFile
* @param int $page_id
* @return Attachment
* @throws FileUploadException
*/
public function saveNewUpload(UploadedFile $uploadedFile, $page_id)
{
$attachmentName = $uploadedFile->getClientOriginalName();
$attachmentPath = $this->putFileInStorage($attachmentName, $uploadedFile);
$largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order');
$attachment = Attachment::forceCreate([
'name' => $attachmentName,
'path' => $attachmentPath,
'extension' => $uploadedFile->getClientOriginalExtension(),
'uploaded_to' => $page_id,
'created_by' => user()->id,
'updated_by' => user()->id,
'order' => $largestExistingOrder + 1
]);
return $attachment;
}
/**
* Store a upload, saving to a file and deleting any existing uploads
* attached to that file.
* @param UploadedFile $uploadedFile
* @param Attachment $attachment
* @return Attachment
* @throws FileUploadException
*/
public function saveUpdatedUpload(UploadedFile $uploadedFile, Attachment $attachment)
{
if (!$attachment->external) {
$this->deleteFileInStorage($attachment);
}
$attachmentName = $uploadedFile->getClientOriginalName();
$attachmentPath = $this->putFileInStorage($attachmentName, $uploadedFile);
$attachment->name = $attachmentName;
$attachment->path = $attachmentPath;
$attachment->external = false;
$attachment->extension = $uploadedFile->getClientOriginalExtension();
$attachment->save();
return $attachment;
}
/**
* Save a new File attachment from a given link and name.
* @param string $name
* @param string $link
* @param int $page_id
* @return Attachment
*/
public function saveNewFromLink($name, $link, $page_id)
{
$largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order');
return Attachment::forceCreate([
'name' => $name,
'path' => $link,
'external' => true,
'extension' => '',
'uploaded_to' => $page_id,
'created_by' => user()->id,
'updated_by' => user()->id,
'order' => $largestExistingOrder + 1
]);
}
/**
* Get the file storage base path, amended for storage type.
* This allows us to keep a generic path in the database.
* @return string
*/
private function getStorageBasePath()
{
return $this->isLocal() ? 'storage/' : '';
}
/**
* Updates the file ordering for a listing of attached files.
* @param array $attachmentList
* @param $pageId
*/
public function updateFileOrderWithinPage($attachmentList, $pageId)
{
foreach ($attachmentList as $index => $attachment) {
Attachment::where('uploaded_to', '=', $pageId)->where('id', '=', $attachment['id'])->update(['order' => $index]);
}
}
/**
* Update the details of a file.
* @param Attachment $attachment
* @param $requestData
* @return Attachment
*/
public function updateFile(Attachment $attachment, $requestData)
{
$attachment->name = $requestData['name'];
if (isset($requestData['link']) && trim($requestData['link']) !== '') {
$attachment->path = $requestData['link'];
if (!$attachment->external) {
$this->deleteFileInStorage($attachment);
$attachment->external = true;
}
}
$attachment->save();
return $attachment;
}
/**
* Delete a File from the database and storage.
* @param Attachment $attachment
*/
public function deleteFile(Attachment $attachment)
{
if ($attachment->external) {
$attachment->delete();
return;
}
$this->deleteFileInStorage($attachment);
$attachment->delete();
}
/**
* Delete a file from the filesystem it sits on.
* Cleans any empty leftover folders.
* @param Attachment $attachment
*/
protected function deleteFileInStorage(Attachment $attachment)
{
$storedFilePath = $this->getStorageBasePath() . $attachment->path;
$storage = $this->getStorage();
$dirPath = dirname($storedFilePath);
$storage->delete($storedFilePath);
if (count($storage->allFiles($dirPath)) === 0) {
$storage->deleteDirectory($dirPath);
}
}
/**
* Store a file in storage with the given filename
* @param $attachmentName
* @param UploadedFile $uploadedFile
* @return string
* @throws FileUploadException
*/
protected function putFileInStorage($attachmentName, UploadedFile $uploadedFile)
{
$attachmentData = file_get_contents($uploadedFile->getRealPath());
$storage = $this->getStorage();
$attachmentBasePath = 'uploads/files/' . Date('Y-m-M') . '/';
$storageBasePath = $this->getStorageBasePath() . $attachmentBasePath;
$uploadFileName = $attachmentName;
while ($storage->exists($storageBasePath . $uploadFileName)) {
$uploadFileName = str_random(3) . $uploadFileName;
}
$attachmentPath = $attachmentBasePath . $uploadFileName;
$attachmentStoragePath = $this->getStorageBasePath() . $attachmentPath;
try {
$storage->put($attachmentStoragePath, $attachmentData);
} catch (Exception $e) {
throw new FileUploadException('File path ' . $attachmentStoragePath . ' could not be uploaded to. Ensure it is writable to the server.');
}
return $attachmentPath;
}
}

View File

@ -1,204 +0,0 @@
<?php namespace BookStack\Services;
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
* @param int $page_id
* @return File
* @throws FileUploadException
*/
public function saveNewUpload(UploadedFile $uploadedFile, $page_id)
{
$fileName = $uploadedFile->getClientOriginalName();
$filePath = $this->putFileInStorage($fileName, $uploadedFile);
$largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
$file = File::forceCreate([
'name' => $fileName,
'path' => $filePath,
'extension' => $uploadedFile->getClientOriginalExtension(),
'uploaded_to' => $page_id,
'created_by' => user()->id,
'updated_by' => user()->id,
'order' => $largestExistingOrder + 1
]);
return $file;
}
/**
* Store a upload, saving to a file and deleting any existing uploads
* attached to that file.
* @param UploadedFile $uploadedFile
* @param File $file
* @return File
* @throws FileUploadException
*/
public function saveUpdatedUpload(UploadedFile $uploadedFile, File $file)
{
if (!$file->external) {
$this->deleteFileInStorage($file);
}
$fileName = $uploadedFile->getClientOriginalName();
$filePath = $this->putFileInStorage($fileName, $uploadedFile);
$file->name = $fileName;
$file->path = $filePath;
$file->external = false;
$file->extension = $uploadedFile->getClientOriginalExtension();
$file->save();
return $file;
}
/**
* Save a new File attachment from a given link and name.
* @param string $name
* @param string $link
* @param int $page_id
* @return File
*/
public function saveNewFromLink($name, $link, $page_id)
{
$largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
return File::forceCreate([
'name' => $name,
'path' => $link,
'external' => true,
'extension' => '',
'uploaded_to' => $page_id,
'created_by' => user()->id,
'updated_by' => user()->id,
'order' => $largestExistingOrder + 1
]);
}
/**
* Get the file storage base path, amended for storage type.
* This allows us to keep a generic path in the database.
* @return string
*/
private function getStorageBasePath()
{
return $this->isLocal() ? 'storage/' : '';
}
/**
* Updates the file ordering for a listing of attached files.
* @param array $fileList
* @param $pageId
*/
public function updateFileOrderWithinPage($fileList, $pageId)
{
foreach ($fileList as $index => $file) {
File::where('uploaded_to', '=', $pageId)->where('id', '=', $file['id'])->update(['order' => $index]);
}
}
/**
* Update the details of a file.
* @param File $file
* @param $requestData
* @return File
*/
public function updateFile(File $file, $requestData)
{
$file->name = $requestData['name'];
if (isset($requestData['link']) && trim($requestData['link']) !== '') {
$file->path = $requestData['link'];
if (!$file->external) {
$this->deleteFileInStorage($file);
$file->external = true;
}
}
$file->save();
return $file;
}
/**
* Delete a File from the database and storage.
* @param File $file
*/
public function deleteFile(File $file)
{
if ($file->external) {
$file->delete();
return;
}
$this->deleteFileInStorage($file);
$file->delete();
}
/**
* Delete a file from the filesystem it sits on.
* Cleans any empty leftover folders.
* @param File $file
*/
protected function deleteFileInStorage(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);
}
}
/**
* Store a file in storage with the given filename
* @param $fileName
* @param UploadedFile $uploadedFile
* @return string
* @throws FileUploadException
*/
protected function putFileInStorage($fileName, UploadedFile $uploadedFile)
{
$fileData = file_get_contents($uploadedFile->getRealPath());
$storage = $this->getStorage();
$fileBasePath = 'uploads/files/' . Date('Y-m-M') . '/';
$storageBasePath = $this->getStorageBasePath() . $fileBasePath;
$uploadFileName = $fileName;
while ($storage->exists($storageBasePath . $uploadFileName)) {
$uploadFileName = str_random(3) . $uploadFileName;
}
$filePath = $fileBasePath . $uploadFileName;
$fileStoragePath = $this->getStorageBasePath() . $filePath;
try {
$storage->put($fileStoragePath, $fileData);
} catch (Exception $e) {
throw new FileUploadException('File path ' . $fileStoragePath . ' could not be uploaded to. Ensure it is writable to the server.');
}
return $filePath;
}
}

View File

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateFilesTable extends Migration
class CreateAttachmentsTable extends Migration
{
/**
* Run the migrations.
@ -13,7 +13,7 @@ class CreateFilesTable extends Migration
*/
public function up()
{
Schema::create('files', function (Blueprint $table) {
Schema::create('attachments', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('path');
@ -35,7 +35,7 @@ class CreateFilesTable extends Migration
// Create & attach new entity permissions
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
$entity = 'File';
$entity = 'Attachment';
foreach ($ops as $op) {
$permissionId = DB::table('role_permissions')->insertGetId([
'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
@ -58,11 +58,11 @@ class CreateFilesTable extends Migration
*/
public function down()
{
Schema::dropIfExists('files');
Schema::dropIfExists('attachments');
// Create & attach new entity permissions
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
$entity = 'File';
$entity = 'Attachment';
foreach ($ops as $op) {
$permName = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
DB::table('role_permissions')->where('name', '=', $permName)->delete();

View File

@ -570,7 +570,7 @@ export default function (ngApp, events) {
if (newOrder === currentOrder) return;
currentOrder = newOrder;
$http.put(window.baseUrl(`/files/sort/page/${pageId}`), {files: $scope.files}).then(resp => {
$http.put(window.baseUrl(`/attachments/sort/page/${pageId}`), {files: $scope.files}).then(resp => {
events.emit('success', resp.data.message);
}, checkError('sort'));
}
@ -581,14 +581,14 @@ export default function (ngApp, events) {
*/
$scope.getUploadUrl = function (file) {
let suffix = (typeof file !== 'undefined') ? `/${file.id}` : '';
return window.baseUrl(`/files/upload${suffix}`);
return window.baseUrl(`/attachments/upload${suffix}`);
};
/**
* Get files for the current page from the server.
*/
function getFiles() {
let url = window.baseUrl(`/files/get/page/${pageId}`)
let url = window.baseUrl(`/attachments/get/page/${pageId}`)
$http.get(url).then(resp => {
$scope.files = resp.data;
currentOrder = resp.data.map(file => {return file.id}).join(':');
@ -636,7 +636,7 @@ export default function (ngApp, events) {
file.deleting = true;
return;
}
$http.delete(window.baseUrl(`/files/${file.id}`)).then(resp => {
$http.delete(window.baseUrl(`/attachments/${file.id}`)).then(resp => {
events.emit('success', resp.data.message);
$scope.files.splice($scope.files.indexOf(file), 1);
}, checkError('delete'));
@ -648,7 +648,7 @@ export default function (ngApp, events) {
*/
$scope.attachLinkSubmit = function(file) {
file.uploaded_to = pageId;
$http.post(window.baseUrl('/files/link'), file).then(resp => {
$http.post(window.baseUrl('/attachments/link'), file).then(resp => {
$scope.files.push(resp.data);
events.emit('success', 'Link attached');
$scope.file = getCleanFile();
@ -676,7 +676,7 @@ export default function (ngApp, events) {
* @param file
*/
$scope.updateFile = function(file) {
$http.put(window.baseUrl(`/files/${file.id}`), file).then(resp => {
$http.put(window.baseUrl(`/attachments/${file.id}`), file).then(resp => {
let search = filesIndexOf(resp.data);
if (search !== -1) $scope.files[search] = resp.data;
@ -692,7 +692,7 @@ export default function (ngApp, events) {
* Get the url of a file.
*/
$scope.getFileUrl = function(file) {
return window.baseUrl('/files/' + file.id);
return window.baseUrl('/attachments/' + file.id);
};
/**

View File

@ -4,7 +4,7 @@
<div class="tabs primary-background-light">
<span toolbox-toggle><i class="zmdi zmdi-caret-left-circle"></i></span>
<span toolbox-tab-button="tags" title="Page Tags" class="active"><i class="zmdi zmdi-tag"></i></span>
@if(userCan('file-create-all'))
@if(userCan('attachment-create-all'))
<span toolbox-tab-button="files" title="Attachments"><i class="zmdi zmdi-attachment"></i></span>
@endif
</div>
@ -37,7 +37,7 @@
</div>
</div>
@if(userCan('file-create-all'))
@if(userCan('attachment-create-all'))
<div toolbox-tab-content="files" ng-controller="PageAttachmentController" page-id="{{ $page->id or 0 }}">
<h4>Attachments</h4>
<div class="padded files">

View File

@ -1,11 +1,11 @@
<div class="book-tree" ng-non-bindable>
@if (isset($page) && $page->files->count() > 0)
@if (isset($page) && $page->attachments->count() > 0)
<h6 class="text-muted">Attachments</h6>
@foreach($page->files as $file)
@foreach($page->attachments as $attachment)
<div class="attachment">
<a href="{{ $file->getUrl() }}" @if($file->external) target="_blank" @endif><i class="zmdi zmdi-{{ $file->external ? 'open-in-new' : 'file' }}"></i> {{ $file->name }}</a>
<a href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif><i class="zmdi zmdi-{{ $attachment->external ? 'open-in-new' : 'file' }}"></i> {{ $attachment->name }}</a>
</div>
@endforeach
@endif

View File

@ -107,16 +107,16 @@
</td>
</tr>
<tr>
<td>Attached <br>Files</td>
<td>@include('settings/roles/checkbox', ['permission' => 'file-create-all'])</td>
<td>Attachments</td>
<td>@include('settings/roles/checkbox', ['permission' => 'attachment-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>
<label>@include('settings/roles/checkbox', ['permission' => 'attachment-update-own']) Own</label>
<label>@include('settings/roles/checkbox', ['permission' => 'attachment-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>
<label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-own']) Own</label>
<label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-all']) All</label>
</td>
</tr>
</table>

View File

@ -87,15 +87,15 @@ Route::group(['middleware' => 'auth'], function () {
Route::delete('/{imageId}', 'ImageController@destroy');
});
// File routes
Route::get('/files/{id}', 'FileController@get');
Route::post('/files/upload', 'FileController@upload');
Route::post('/files/upload/{id}', 'FileController@uploadUpdate');
Route::post('/files/link', 'FileController@attachLink');
Route::put('/files/{id}', 'FileController@update');
Route::get('/files/get/page/{pageId}', 'FileController@listForPage');
Route::put('/files/sort/page/{pageId}', 'FileController@sortForPage');
Route::delete('/files/{id}', 'FileController@delete');
// Attachments routes
Route::get('/attachments/{id}', 'AttachmentController@get');
Route::post('/attachments/upload', 'AttachmentController@upload');
Route::post('/attachments/upload/{id}', 'AttachmentController@uploadUpdate');
Route::post('/attachments/link', 'AttachmentController@attachLink');
Route::put('/attachments/{id}', 'AttachmentController@update');
Route::get('/attachments/get/page/{pageId}', 'AttachmentController@listForPage');
Route::put('/attachments/sort/page/{pageId}', 'AttachmentController@sortForPage');
Route::delete('/attachments/{id}', 'AttachmentController@delete');
// AJAX routes
Route::put('/ajax/page/{id}/save-draft', 'PageController@saveDraft');

View File

@ -21,7 +21,7 @@ class AttachmentTest extends TestCase
protected function uploadFile($name, $uploadedTo = 0)
{
$file = $this->getTestFile($name);
return $this->call('POST', '/files/upload', ['uploaded_to' => $uploadedTo], [], ['file' => $file], []);
return $this->call('POST', '/attachments/upload', ['uploaded_to' => $uploadedTo], [], ['file' => $file], []);
}
/**
@ -40,8 +40,8 @@ class AttachmentTest extends TestCase
*/
protected function deleteUploads()
{
$fileService = $this->app->make(\BookStack\Services\FileService::class);
foreach (\BookStack\File::all() as $file) {
$fileService = $this->app->make(\BookStack\Services\AttachmentService::class);
foreach (\BookStack\Attachment::all() as $file) {
$fileService->deleteFile($file);
}
}
@ -66,7 +66,7 @@ class AttachmentTest extends TestCase
$this->uploadFile($fileName, $page->id);
$this->assertResponseOk();
$this->seeJsonContains($expectedResp);
$this->seeInDatabase('files', $expectedResp);
$this->seeInDatabase('attachments', $expectedResp);
$this->deleteUploads();
}
@ -94,7 +94,7 @@ class AttachmentTest extends TestCase
$admin = $this->getAdmin();
$this->asAdmin();
$this->call('POST', 'files/link', [
$this->call('POST', 'attachments/link', [
'link' => 'https://example.com',
'name' => 'Example Attachment Link',
'uploaded_to' => $page->id,
@ -113,7 +113,7 @@ class AttachmentTest extends TestCase
$this->assertResponseOk();
$this->seeJsonContains($expectedResp);
$this->seeInDatabase('files', $expectedResp);
$this->seeInDatabase('attachments', $expectedResp);
$this->visit($page->getUrl())->seeLink('Example Attachment Link')
->click('Example Attachment Link')->seePageIs('https://example.com');
@ -126,15 +126,15 @@ class AttachmentTest extends TestCase
$page = \BookStack\Page::first();
$this->asAdmin();
$this->call('POST', 'files/link', [
$this->call('POST', 'attachments/link', [
'link' => 'https://example.com',
'name' => 'Example Attachment Link',
'uploaded_to' => $page->id,
]);
$attachmentId = \BookStack\File::first()->id;
$attachmentId = \BookStack\Attachment::first()->id;
$this->call('PUT', 'files/' . $attachmentId, [
$this->call('PUT', 'attachments/' . $attachmentId, [
'uploaded_to' => $page->id,
'name' => 'My new attachment name',
'link' => 'https://test.example.com'
@ -148,7 +148,7 @@ class AttachmentTest extends TestCase
$this->assertResponseOk();
$this->seeJsonContains($expectedResp);
$this->seeInDatabase('files', $expectedResp);
$this->seeInDatabase('attachments', $expectedResp);
$this->deleteUploads();
}
@ -164,10 +164,10 @@ class AttachmentTest extends TestCase
$this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
$attachmentId = \BookStack\File::first()->id;
$this->call('DELETE', 'files/' . $attachmentId);
$attachmentId = \BookStack\Attachment::first()->id;
$this->call('DELETE', 'attachments/' . $attachmentId);
$this->dontSeeInDatabase('files', [
$this->dontSeeInDatabase('attachments', [
'name' => $fileName
]);
$this->assertFalse(file_exists($filePath), 'File at path ' . $filePath . ' was not deleted as expected');
@ -185,13 +185,13 @@ class AttachmentTest extends TestCase
$filePath = base_path('storage/' . $this->getUploadPath($fileName));
$this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
$this->seeInDatabase('files', [
$this->seeInDatabase('attachments', [
'name' => $fileName
]);
$this->call('DELETE', $page->getUrl());
$this->dontSeeInDatabase('files', [
$this->dontSeeInDatabase('attachments', [
'name' => $fileName
]);
$this->assertFalse(file_exists($filePath), 'File at path ' . $filePath . ' was not deleted as expected');