# Set to -1 for unlimited recycle bin lifetime.
RECYCLE_BIN_LIFETIME=30
+# File Upload Limit
+# Maximum file size, in megabytes, that can be uploaded to the system.
+FILE_UPLOAD_SIZE_LIMIT=50
+
# Allow <script> tags in page content
# Note, if set to 'true' the page editor may still escape scripts.
ALLOW_CONTENT_SCRIPTS=false
// Set to -1 for unlimited recycle bin lifetime.
'recycle_bin_lifetime' => env('RECYCLE_BIN_LIFETIME', 30),
+ // The limit for all uploaded files, including images and attachments in MB.
+ 'upload_limit' => env('FILE_UPLOAD_SIZE_LIMIT', 50),
+
// Allow <script> tags to entered within page content.
// <script> tags are escaped by default.
// Even when overridden the WYSIWYG editor may still escape script content.
return '';
}
+ // Validate that the content is not over our upload limit
+ $uploadLimitBytes = (config('app.upload_limit') * 1000000);
+ if (strlen($imageInfo['data']) > $uploadLimitBytes) {
+ return '';
+ }
+
// Save image from data with a random name
$imageName = 'embedded-image-' . Str::random(8) . '.' . $imageInfo['extension'];
/**
* Get the validation rules for this controller.
+ * Defaults to a $rules property but can be a rules() method.
*/
public function getValdationRules(): array
{
+ if (method_exists($this, 'rules')) {
+ return $this->rules();
+ }
+
return $this->rules;
}
}
{
protected $attachmentService;
- protected $rules = [
- 'create' => [
- 'name' => ['required', 'min:1', 'max:255', 'string'],
- 'uploaded_to' => ['required', 'integer', 'exists:pages,id'],
- 'file' => ['required_without:link', 'file'],
- 'link' => ['required_without:file', 'min:1', 'max:255', 'safe_url'],
- ],
- 'update' => [
- 'name' => ['min:1', 'max:255', 'string'],
- 'uploaded_to' => ['integer', 'exists:pages,id'],
- 'file' => ['file'],
- 'link' => ['min:1', 'max:255', 'safe_url'],
- ],
- ];
-
public function __construct(AttachmentService $attachmentService)
{
$this->attachmentService = $attachmentService;
public function create(Request $request)
{
$this->checkPermission('attachment-create-all');
- $requestData = $this->validate($request, $this->rules['create']);
+ $requestData = $this->validate($request, $this->rules()['create']);
$pageId = $request->get('uploaded_to');
$page = Page::visible()->findOrFail($pageId);
*/
public function update(Request $request, string $id)
{
- $requestData = $this->validate($request, $this->rules['update']);
+ $requestData = $this->validate($request, $this->rules()['update']);
/** @var Attachment $attachment */
$attachment = Attachment::visible()->findOrFail($id);
return response('', 204);
}
+
+ protected function rules(): array
+ {
+ return [
+ 'create' => [
+ 'name' => ['required', 'min:1', 'max:255', 'string'],
+ 'uploaded_to' => ['required', 'integer', 'exists:pages,id'],
+ 'file' => array_merge(['required_without:link'], $this->attachmentService->getFileValidationRules()),
+ 'link' => ['required_without:file', 'min:1', 'max:255', 'safe_url'],
+ ],
+ 'update' => [
+ 'name' => ['min:1', 'max:255', 'string'],
+ 'uploaded_to' => ['integer', 'exists:pages,id'],
+ 'file' => $this->attachmentService->getFileValidationRules(),
+ 'link' => ['min:1', 'max:255', 'safe_url'],
+ ],
+ ];
+ }
}
use BookStack\Uploads\AttachmentService;
use Exception;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
+use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationException;
{
$this->validate($request, [
'uploaded_to' => ['required', 'integer', 'exists:pages,id'],
- 'file' => ['required', 'file'],
+ 'file' => array_merge(['required'], $this->attachmentService->getFileValidationRules()),
]);
$pageId = $request->get('uploaded_to');
public function uploadUpdate(Request $request, $attachmentId)
{
$this->validate($request, [
- 'file' => ['required', 'file'],
+ 'file' => array_merge(['required'], $this->attachmentService->getFileValidationRules()),
]);
/** @var Attachment $attachment */
/**
* Log an activity in the system.
*
- * @param $detail string|Loggable
+ * @param string|Loggable $detail
*/
protected function logActivity(string $type, $detail = ''): void
{
*/
protected function getImageValidationRules(): array
{
- return ['image_extension', 'mimes:jpeg,png,gif,webp'];
+ return ['image_extension', 'mimes:jpeg,png,gif,webp', 'max:' . (config('app.upload_limit') * 1000)];
}
}
return $attachmentPath;
}
+
+ /**
+ * Get the file validation rules for attachments.
+ */
+ public function getFileValidationRules(): array
+ {
+ return ['file', 'max:' . (config('app.upload_limit') * 1000)];
+ }
}
this.url = this.$opts.url;
this.successMessage = this.$opts.successMessage;
this.removeMessage = this.$opts.removeMessage;
+ this.uploadLimit = Number(this.$opts.uploadLimit);
this.uploadLimitMessage = this.$opts.uploadLimitMessage;
this.timeoutMessage = this.$opts.timeoutMessage;
addRemoveLinks: true,
dictRemoveFile: this.removeMessage,
timeout: Number(window.uploadTimeout) || 60000,
- maxFilesize: Number(window.uploadLimit) || 256,
+ maxFilesize: this.uploadLimit,
url: this.url,
withCredentials: true,
init() {
option:dropzone:url="{{ $url }}"
option:dropzone:success-message="{{ $successMessage ?? '' }}"
option:dropzone:remove-message="{{ trans('components.image_upload_remove') }}"
+ option:dropzone:upload-limit="{{ config('app.upload_limit') }}"
option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}"
option:dropzone:timeout-message="{{ trans('errors.file_upload_timeout') }}"