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

View File

@ -1,36 +1,36 @@
<?php namespace BookStack\Http\Controllers; <?php namespace BookStack\Http\Controllers;
use BookStack\Exceptions\FileUploadException; use BookStack\Exceptions\FileUploadException;
use BookStack\File; use BookStack\Attachment;
use BookStack\Repos\PageRepo; use BookStack\Repos\PageRepo;
use BookStack\Services\FileService; use BookStack\Services\AttachmentService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use BookStack\Http\Requests; class AttachmentController extends Controller
class FileController extends Controller
{ {
protected $fileService; protected $attachmentService;
protected $file; protected $attachment;
protected $pageRepo; protected $pageRepo;
/** /**
* FileController constructor. * AttachmentController constructor.
* @param FileService $fileService * @param AttachmentService $attachmentService
* @param File $file * @param Attachment $attachment
* @param PageRepo $pageRepo * @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->attachmentService = $attachmentService;
$this->file = $file; $this->attachment = $attachment;
$this->pageRepo = $pageRepo; $this->pageRepo = $pageRepo;
parent::__construct();
} }
/** /**
* Endpoint at which files are uploaded to. * Endpoint at which attachments are uploaded to.
* @param Request $request * @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
*/ */
public function upload(Request $request) public function upload(Request $request)
{ {
@ -42,27 +42,27 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to'); $pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId); $page = $this->pageRepo->getById($pageId);
$this->checkPermission('file-create-all'); $this->checkPermission('attachment-create-all');
$this->checkOwnablePermission('page-update', $page); $this->checkOwnablePermission('page-update', $page);
$uploadedFile = $request->file('file'); $uploadedFile = $request->file('file');
try { try {
$file = $this->fileService->saveNewUpload($uploadedFile, $pageId); $attachment = $this->attachmentService->saveNewUpload($uploadedFile, $pageId);
} catch (FileUploadException $e) { } catch (FileUploadException $e) {
return response($e->getMessage(), 500); return response($e->getMessage(), 500);
} }
return response()->json($file); return response()->json($attachment);
} }
/** /**
* Update an uploaded file. * Update an uploaded attachment.
* @param int $fileId * @param int $attachmentId
* @param Request $request * @param Request $request
* @return mixed * @return mixed
*/ */
public function uploadUpdate($fileId, Request $request) public function uploadUpdate($attachmentId, Request $request)
{ {
$this->validate($request, [ $this->validate($request, [
'uploaded_to' => 'required|integer|exists:pages,id', 'uploaded_to' => 'required|integer|exists:pages,id',
@ -71,33 +71,33 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to'); $pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId); $page = $this->pageRepo->getById($pageId);
$file = $this->file->findOrFail($fileId); $attachment = $this->attachment->findOrFail($attachmentId);
$this->checkOwnablePermission('page-update', $page); $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'); return $this->jsonError('Page mismatch during attached file update');
} }
$uploadedFile = $request->file('file'); $uploadedFile = $request->file('file');
try { try {
$file = $this->fileService->saveUpdatedUpload($uploadedFile, $file); $attachment = $this->attachmentService->saveUpdatedUpload($uploadedFile, $attachment);
} catch (FileUploadException $e) { } catch (FileUploadException $e) {
return response($e->getMessage(), 500); return response($e->getMessage(), 500);
} }
return response()->json($file); return response()->json($attachment);
} }
/** /**
* Update the details of an existing file. * Update the details of an existing file.
* @param $fileId * @param $attachmentId
* @param Request $request * @param Request $request
* @return File|mixed * @return Attachment|mixed
*/ */
public function update($fileId, Request $request) public function update($attachmentId, Request $request)
{ {
$this->validate($request, [ $this->validate($request, [
'uploaded_to' => 'required|integer|exists:pages,id', 'uploaded_to' => 'required|integer|exists:pages,id',
@ -107,21 +107,21 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to'); $pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId); $page = $this->pageRepo->getById($pageId);
$file = $this->file->findOrFail($fileId); $attachment = $this->attachment->findOrFail($attachmentId);
$this->checkOwnablePermission('page-update', $page); $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'); return $this->jsonError('Page mismatch during attachment update');
} }
$file = $this->fileService->updateFile($file, $request->all()); $attachment = $this->attachmentService->updateFile($attachment, $request->all());
return $file; return $attachment;
} }
/** /**
* Attach a link to a page as a file. * Attach a link to a page.
* @param Request $request * @param Request $request
* @return mixed * @return mixed
*/ */
@ -136,18 +136,18 @@ class FileController extends Controller
$pageId = $request->get('uploaded_to'); $pageId = $request->get('uploaded_to');
$page = $this->pageRepo->getById($pageId); $page = $this->pageRepo->getById($pageId);
$this->checkPermission('file-create-all'); $this->checkPermission('attachment-create-all');
$this->checkOwnablePermission('page-update', $page); $this->checkOwnablePermission('page-update', $page);
$fileName = $request->get('name'); $attachmentName = $request->get('name');
$link = $request->get('link'); $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 * @param $pageId
* @return mixed * @return mixed
*/ */
@ -159,7 +159,7 @@ class FileController extends Controller
} }
/** /**
* Update the file sorting. * Update the attachment sorting.
* @param $pageId * @param $pageId
* @param Request $request * @param Request $request
* @return mixed * @return mixed
@ -173,42 +173,43 @@ class FileController extends Controller
$page = $this->pageRepo->getById($pageId); $page = $this->pageRepo->getById($pageId);
$this->checkOwnablePermission('page-update', $page); $this->checkOwnablePermission('page-update', $page);
$files = $request->get('files'); $attachments = $request->get('files');
$this->fileService->updateFileOrderWithinPage($files, $pageId); $this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
return response()->json(['message' => 'Attachment order updated']); return response()->json(['message' => 'Attachment order updated']);
} }
/** /**
* Get a file from storage. * Get an attachment from storage.
* @param $fileId * @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); $attachment = $this->attachment->findOrFail($attachmentId);
$page = $this->pageRepo->getById($file->uploaded_to); $page = $this->pageRepo->getById($attachment->uploaded_to);
$this->checkOwnablePermission('page-view', $page); $this->checkOwnablePermission('page-view', $page);
if ($file->external) { if ($attachment->external) {
return redirect($file->path); return redirect($attachment->path);
} }
$fileContents = $this->fileService->getFile($file); $attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
return response($fileContents, 200, [ return response($attachmentContents, 200, [
'Content-Type' => 'application/octet-stream', 'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename="'. $file->getFileName() .'"' 'Content-Disposition' => 'attachment; filename="'. $attachment->getFileName() .'"'
]); ]);
} }
/** /**
* Delete a specific file in the system. * Delete a specific attachment in the system.
* @param $fileId * @param $attachmentId
* @return mixed * @return mixed
*/ */
public function delete($fileId) public function delete($attachmentId)
{ {
$file = $this->file->findOrFail($fileId); $attachment = $this->attachment->findOrFail($attachmentId);
$this->checkOwnablePermission('file-delete', $file); $this->checkOwnablePermission('attachment-delete', $attachment);
$this->fileService->deleteFile($file); $this->attachmentService->deleteFile($attachment);
return response()->json(['message' => 'Attachment deleted']); 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 * @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\Chapter;
use BookStack\Entity; use BookStack\Entity;
use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\NotFoundException;
use BookStack\Services\FileService; use BookStack\Services\AttachmentService;
use Carbon\Carbon; use Carbon\Carbon;
use DOMDocument; use DOMDocument;
use DOMXPath; use DOMXPath;
@ -636,9 +636,9 @@ class PageRepo extends EntityRepo
$this->permissionService->deleteJointPermissionsForEntity($page); $this->permissionService->deleteJointPermissionsForEntity($page);
// Delete AttachedFiles // Delete AttachedFiles
$fileService = app(FileService::class); $attachmentService = app(AttachmentService::class);
foreach ($page->files as $file) { foreach ($page->attachments as $attachment) {
$fileService->deleteFile($file); $attachmentService->deleteFile($attachment);
} }
$page->delete(); $page->delete();
@ -647,6 +647,7 @@ class PageRepo extends EntityRepo
/** /**
* Get the latest pages added to the system. * Get the latest pages added to the system.
* @param $count * @param $count
* @return mixed
*/ */
public function getRecentlyCreatedPaginated($count = 20) public function getRecentlyCreatedPaginated($count = 20)
{ {
@ -656,6 +657,7 @@ class PageRepo extends EntityRepo
/** /**
* Get the latest pages added to the system. * Get the latest pages added to the system.
* @param $count * @param $count
* @return mixed
*/ */
public function getRecentlyUpdatedPaginated($count = 20) 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\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
class CreateFilesTable extends Migration class CreateAttachmentsTable extends Migration
{ {
/** /**
* Run the migrations. * Run the migrations.
@ -13,7 +13,7 @@ class CreateFilesTable extends Migration
*/ */
public function up() public function up()
{ {
Schema::create('files', function (Blueprint $table) { Schema::create('attachments', function (Blueprint $table) {
$table->increments('id'); $table->increments('id');
$table->string('name'); $table->string('name');
$table->string('path'); $table->string('path');
@ -35,7 +35,7 @@ class CreateFilesTable extends Migration
// Create & attach new entity permissions // Create & attach new entity permissions
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own']; $ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
$entity = 'File'; $entity = 'Attachment';
foreach ($ops as $op) { foreach ($ops as $op) {
$permissionId = DB::table('role_permissions')->insertGetId([ $permissionId = DB::table('role_permissions')->insertGetId([
'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)), 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
@ -58,11 +58,11 @@ class CreateFilesTable extends Migration
*/ */
public function down() public function down()
{ {
Schema::dropIfExists('files'); Schema::dropIfExists('attachments');
// Create & attach new entity permissions // Create & attach new entity permissions
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own']; $ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
$entity = 'File'; $entity = 'Attachment';
foreach ($ops as $op) { foreach ($ops as $op) {
$permName = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)); $permName = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
DB::table('role_permissions')->where('name', '=', $permName)->delete(); DB::table('role_permissions')->where('name', '=', $permName)->delete();

View File

@ -570,7 +570,7 @@ export default function (ngApp, events) {
if (newOrder === currentOrder) return; if (newOrder === currentOrder) return;
currentOrder = newOrder; 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); events.emit('success', resp.data.message);
}, checkError('sort')); }, checkError('sort'));
} }
@ -581,14 +581,14 @@ export default function (ngApp, events) {
*/ */
$scope.getUploadUrl = function (file) { $scope.getUploadUrl = function (file) {
let suffix = (typeof file !== 'undefined') ? `/${file.id}` : ''; 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. * Get files for the current page from the server.
*/ */
function getFiles() { function getFiles() {
let url = window.baseUrl(`/files/get/page/${pageId}`) let url = window.baseUrl(`/attachments/get/page/${pageId}`)
$http.get(url).then(resp => { $http.get(url).then(resp => {
$scope.files = resp.data; $scope.files = resp.data;
currentOrder = resp.data.map(file => {return file.id}).join(':'); currentOrder = resp.data.map(file => {return file.id}).join(':');
@ -636,7 +636,7 @@ export default function (ngApp, events) {
file.deleting = true; file.deleting = true;
return; 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); events.emit('success', resp.data.message);
$scope.files.splice($scope.files.indexOf(file), 1); $scope.files.splice($scope.files.indexOf(file), 1);
}, checkError('delete')); }, checkError('delete'));
@ -648,7 +648,7 @@ export default function (ngApp, events) {
*/ */
$scope.attachLinkSubmit = function(file) { $scope.attachLinkSubmit = function(file) {
file.uploaded_to = pageId; 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); $scope.files.push(resp.data);
events.emit('success', 'Link attached'); events.emit('success', 'Link attached');
$scope.file = getCleanFile(); $scope.file = getCleanFile();
@ -676,7 +676,7 @@ export default function (ngApp, events) {
* @param file * @param file
*/ */
$scope.updateFile = function(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); let search = filesIndexOf(resp.data);
if (search !== -1) $scope.files[search] = 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. * Get the url of a file.
*/ */
$scope.getFileUrl = function(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"> <div class="tabs primary-background-light">
<span toolbox-toggle><i class="zmdi zmdi-caret-left-circle"></i></span> <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> <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> <span toolbox-tab-button="files" title="Attachments"><i class="zmdi zmdi-attachment"></i></span>
@endif @endif
</div> </div>
@ -37,7 +37,7 @@
</div> </div>
</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 }}"> <div toolbox-tab-content="files" ng-controller="PageAttachmentController" page-id="{{ $page->id or 0 }}">
<h4>Attachments</h4> <h4>Attachments</h4>
<div class="padded files"> <div class="padded files">

View File

@ -1,11 +1,11 @@
<div class="book-tree" ng-non-bindable> <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> <h6 class="text-muted">Attachments</h6>
@foreach($page->files as $file) @foreach($page->attachments as $attachment)
<div class="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> </div>
@endforeach @endforeach
@endif @endif

View File

@ -107,16 +107,16 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Attached <br>Files</td> <td>Attachments</td>
<td>@include('settings/roles/checkbox', ['permission' => 'file-create-all'])</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 style="line-height:1.2;"><small class="faded">Controlled by the asset they are uploaded to</small></td>
<td> <td>
<label>@include('settings/roles/checkbox', ['permission' => 'file-update-own']) Own</label> <label>@include('settings/roles/checkbox', ['permission' => 'attachment-update-own']) Own</label>
<label>@include('settings/roles/checkbox', ['permission' => 'file-update-all']) All</label> <label>@include('settings/roles/checkbox', ['permission' => 'attachment-update-all']) All</label>
</td> </td>
<td> <td>
<label>@include('settings/roles/checkbox', ['permission' => 'file-delete-own']) Own</label> <label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-own']) Own</label>
<label>@include('settings/roles/checkbox', ['permission' => 'file-delete-all']) All</label> <label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-all']) All</label>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -87,15 +87,15 @@ Route::group(['middleware' => 'auth'], function () {
Route::delete('/{imageId}', 'ImageController@destroy'); Route::delete('/{imageId}', 'ImageController@destroy');
}); });
// File routes // Attachments routes
Route::get('/files/{id}', 'FileController@get'); Route::get('/attachments/{id}', 'AttachmentController@get');
Route::post('/files/upload', 'FileController@upload'); Route::post('/attachments/upload', 'AttachmentController@upload');
Route::post('/files/upload/{id}', 'FileController@uploadUpdate'); Route::post('/attachments/upload/{id}', 'AttachmentController@uploadUpdate');
Route::post('/files/link', 'FileController@attachLink'); Route::post('/attachments/link', 'AttachmentController@attachLink');
Route::put('/files/{id}', 'FileController@update'); Route::put('/attachments/{id}', 'AttachmentController@update');
Route::get('/files/get/page/{pageId}', 'FileController@listForPage'); Route::get('/attachments/get/page/{pageId}', 'AttachmentController@listForPage');
Route::put('/files/sort/page/{pageId}', 'FileController@sortForPage'); Route::put('/attachments/sort/page/{pageId}', 'AttachmentController@sortForPage');
Route::delete('/files/{id}', 'FileController@delete'); Route::delete('/attachments/{id}', 'AttachmentController@delete');
// AJAX routes // AJAX routes
Route::put('/ajax/page/{id}/save-draft', 'PageController@saveDraft'); 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) protected function uploadFile($name, $uploadedTo = 0)
{ {
$file = $this->getTestFile($name); $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() protected function deleteUploads()
{ {
$fileService = $this->app->make(\BookStack\Services\FileService::class); $fileService = $this->app->make(\BookStack\Services\AttachmentService::class);
foreach (\BookStack\File::all() as $file) { foreach (\BookStack\Attachment::all() as $file) {
$fileService->deleteFile($file); $fileService->deleteFile($file);
} }
} }
@ -66,7 +66,7 @@ class AttachmentTest extends TestCase
$this->uploadFile($fileName, $page->id); $this->uploadFile($fileName, $page->id);
$this->assertResponseOk(); $this->assertResponseOk();
$this->seeJsonContains($expectedResp); $this->seeJsonContains($expectedResp);
$this->seeInDatabase('files', $expectedResp); $this->seeInDatabase('attachments', $expectedResp);
$this->deleteUploads(); $this->deleteUploads();
} }
@ -94,7 +94,7 @@ class AttachmentTest extends TestCase
$admin = $this->getAdmin(); $admin = $this->getAdmin();
$this->asAdmin(); $this->asAdmin();
$this->call('POST', 'files/link', [ $this->call('POST', 'attachments/link', [
'link' => 'https://example.com', 'link' => 'https://example.com',
'name' => 'Example Attachment Link', 'name' => 'Example Attachment Link',
'uploaded_to' => $page->id, 'uploaded_to' => $page->id,
@ -113,7 +113,7 @@ class AttachmentTest extends TestCase
$this->assertResponseOk(); $this->assertResponseOk();
$this->seeJsonContains($expectedResp); $this->seeJsonContains($expectedResp);
$this->seeInDatabase('files', $expectedResp); $this->seeInDatabase('attachments', $expectedResp);
$this->visit($page->getUrl())->seeLink('Example Attachment Link') $this->visit($page->getUrl())->seeLink('Example Attachment Link')
->click('Example Attachment Link')->seePageIs('https://example.com'); ->click('Example Attachment Link')->seePageIs('https://example.com');
@ -126,15 +126,15 @@ class AttachmentTest extends TestCase
$page = \BookStack\Page::first(); $page = \BookStack\Page::first();
$this->asAdmin(); $this->asAdmin();
$this->call('POST', 'files/link', [ $this->call('POST', 'attachments/link', [
'link' => 'https://example.com', 'link' => 'https://example.com',
'name' => 'Example Attachment Link', 'name' => 'Example Attachment Link',
'uploaded_to' => $page->id, '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, 'uploaded_to' => $page->id,
'name' => 'My new attachment name', 'name' => 'My new attachment name',
'link' => 'https://test.example.com' 'link' => 'https://test.example.com'
@ -148,7 +148,7 @@ class AttachmentTest extends TestCase
$this->assertResponseOk(); $this->assertResponseOk();
$this->seeJsonContains($expectedResp); $this->seeJsonContains($expectedResp);
$this->seeInDatabase('files', $expectedResp); $this->seeInDatabase('attachments', $expectedResp);
$this->deleteUploads(); $this->deleteUploads();
} }
@ -164,10 +164,10 @@ class AttachmentTest extends TestCase
$this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist'); $this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
$attachmentId = \BookStack\File::first()->id; $attachmentId = \BookStack\Attachment::first()->id;
$this->call('DELETE', 'files/' . $attachmentId); $this->call('DELETE', 'attachments/' . $attachmentId);
$this->dontSeeInDatabase('files', [ $this->dontSeeInDatabase('attachments', [
'name' => $fileName 'name' => $fileName
]); ]);
$this->assertFalse(file_exists($filePath), 'File at path ' . $filePath . ' was not deleted as expected'); $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)); $filePath = base_path('storage/' . $this->getUploadPath($fileName));
$this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist'); $this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
$this->seeInDatabase('files', [ $this->seeInDatabase('attachments', [
'name' => $fileName 'name' => $fileName
]); ]);
$this->call('DELETE', $page->getUrl()); $this->call('DELETE', $page->getUrl());
$this->dontSeeInDatabase('files', [ $this->dontSeeInDatabase('attachments', [
'name' => $fileName 'name' => $fileName
]); ]);
$this->assertFalse(file_exists($filePath), 'File at path ' . $filePath . ' was not deleted as expected'); $this->assertFalse(file_exists($filePath), 'File at path ' . $filePath . ' was not deleted as expected');