mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
201 lines
6.4 KiB
PHP
201 lines
6.4 KiB
PHP
<?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;
|
|
}
|
|
|
|
} |