diff --git a/formwork/fields/upload.php b/formwork/fields/upload.php index bfe2e62f..5bfcf2c9 100644 --- a/formwork/fields/upload.php +++ b/formwork/fields/upload.php @@ -25,6 +25,10 @@ return $field->get('collection', new FileCollection()); }, + 'autoUpload' => function (Field $field): bool { + return $field->is('autoUpload'); + }, + 'validate' => function (Field $field, $value) use ($app) { if (Constraint::isEmpty($value)) { return null; diff --git a/formwork/src/Panel/Controllers/FilesController.php b/formwork/src/Panel/Controllers/FilesController.php new file mode 100644 index 00000000..dd7847cc --- /dev/null +++ b/formwork/src/Panel/Controllers/FilesController.php @@ -0,0 +1,275 @@ +hasPermission('pages.file')) { + return $this->forward(ErrorsController::class, 'forbidden'); + } + + $page = $this->site->findPage($routeParams->get('page')); + + $filename = $routeParams->get('filename'); + + if ($page === null) { + $this->panel->notify($this->translate('panel.pages.page.cannotGetFileInfo.pageNotFound'), 'error'); + return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + } + + if (!$page->files()->has($filename)) { + $this->panel->notify($this->translate('panel.pages.page.cannotGetFileInfo.fileNotFound'), 'error'); + return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + } + + $files = $page->files(); + + $file = $files->get($filename); + + switch ($this->request->method()) { + case RequestMethod::GET: + $data = $file->data(); + + $file->fields()->setValues($data); + + break; + + case RequestMethod::POST: + $data = $this->request->input(); + + $file->fields()->setValues($data)->validate(); + + $this->updateFileMetadata($file, $file->fields()); + + $this->updateLastModifiedTime($page); + + $this->panel->notify($this->translate('panel.files.metadata.updated'), 'success'); + + return $this->redirect($this->generateRoute('panel.files.index', ['page' => $page->route(), 'filename' => $filename])); + } + + return new Response($this->view('files.index', [ + 'title' => $file->name(), + 'page' => $page, + 'file' => $file, + ...$this->getPreviousAndNextFile($files, $file), + ])); + } + + /** + * FilesController@delete action + */ + public function delete(RouteParams $routeParams): Response + { + if (!$this->hasPermission('pages.deleteFiles')) { + return $this->forward(ErrorsController::class, 'forbidden'); + } + + $page = $this->site->findPage($routeParams->get('page')); + + if ($page === null) { + $this->panel->notify($this->translate('panel.files.cannotDelete.pageNotFound'), 'error'); + return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + } + + if (!$page->files()->has($routeParams->get('filename'))) { + $this->panel->notify($this->translate('panel.files.cannotDelete.fileNotFound'), 'error'); + return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + } + + FileSystem::delete($page->contentPath() . $routeParams->get('filename')); + + $this->updateLastModifiedTime($page); + + $this->panel->notify($this->translate('panel.pages.page.fileDeleted'), 'success'); + return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + } + + /** + * FilesController@rename action + */ + public function rename(RouteParams $routeParams): Response + { + if (!$this->hasPermission('pages.renameFiles')) { + return $this->forward(ErrorsController::class, 'forbidden'); + } + + $page = $this->site->findPage($routeParams->get('page')); + + $fields = $this->modal('renameFile')->fields(); + + $fields->setValues($this->request->input())->validate(); + + $data = $fields->everyItem()->value(); + + if ($page === null) { + $this->panel->notify($this->translate('panel.pages.page.cannotRenameFile.pageNotFound'), 'error'); + return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + } + + if (!$page->files()->has($routeParams->get('filename'))) { + $this->panel->notify($this->translate('panel.files.cannotRename.fileNotFound'), 'error'); + return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + } + + $name = Str::slug(FileSystem::name($data->get('filename'))); + $extension = FileSystem::extension($routeParams->get('filename')); + + $newName = $name . '.' . $extension; + + $previousName = $routeParams->get('filename'); + + if ($newName !== $previousName) { + if ($page->files()->has($newName)) { + $this->panel->notify($this->translate('panel.pages.page.cannotRenameFile.fileAlreadyExists'), 'error'); + } else { + FileSystem::move($page->contentPath() . $previousName, $page->contentPath() . $newName); + $this->updateLastModifiedTime($page); + + $this->panel->notify($this->translate('panel.pages.page.fileRenamed'), 'success'); + } + } + + return $this->redirect($this->generateRoute('panel.files.index', ['page' => $routeParams->get('page'), 'filename' => $newName])); + } + + /** + * FilesController@replace action + */ + public function replace(RouteParams $routeParams): Response + { + if (!$this->hasPermission('pages.replaceFiles')) { + return $this->forward(ErrorsController::class, 'forbidden'); + } + + $page = $this->site->findPage($routeParams->get('page')); + + $filename = $routeParams->get('filename'); + + if ($page === null) { + $this->panel->notify($this->translate('panel.pages.page.cannotReplaceFile.pageNotFound'), 'error'); + return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + } + + if (!$page->files()->has($filename)) { + $this->panel->notify($this->translate('panel.pages.page.cannotReplaceFile.fileNotFound'), 'error'); + return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + } + + if (!$this->request->files()->isEmpty()) { + $files = $this->request->files()->getAll(); + + if (count($files) > 1) { + $this->panel->notify($this->translate('panel.pages.page.cannotReplaceFile.multipleFiles'), 'error'); + return $this->redirect($this->generateRoute('panel.files.index', ['page' => $routeParams->get('page'), 'filename' => $filename])); + } + + try { + $this->processFileUpload($this->request->files()->getAll(), $page, [$page->files()->get($filename)->mimeType()], FileSystem::name($filename), true); + } catch (TranslatedException $e) { + $this->panel->notify($this->translate('upload.error', $this->translate($e->getLanguageString())), 'error'); + return $this->redirect($this->generateRoute('panel.files.index', ['page' => $routeParams->get('page'), 'filename' => $filename])); + } + } + + $this->updateLastModifiedTime($page); + + $this->panel->notify($this->translate('panel.uploader.uploaded'), 'success'); + return $this->redirect($this->generateRoute('panel.files.index', ['page' => $routeParams->get('page'), 'filename' => $filename])); + } + + /** + * Update file metadata + */ + private function updateFileMetadata(File $file, FieldCollection $fieldCollection): void + { + $data = $file->data(); + + $scheme = $file->scheme(); + + $defaults = $scheme->fields()->pluck('default'); + + foreach ($fieldCollection as $field) { + if ($field->isEmpty() || (Arr::has($defaults, $field->name()) && Arr::get($defaults, $field->name()) === $field->value())) { + unset($data[$field->name()]); + continue; + } + + $data[$field->name()] = $field->value(); + } + + $metaFile = $file->path() . $this->config->get('system.files.metadataExtension'); + + if ($data === [] && FileSystem::exists($metaFile)) { + FileSystem::delete($metaFile); + return; + } + + FileSystem::write($metaFile, Yaml::encode($data)); + } + + /** + * Process page uploads + * + * @param array $files + * @param list $mimeTypes + */ + private function processFileUpload(array $files, Page $page, ?array $mimeTypes = null, ?string $name = null, bool $overwrite = false): void + { + $fileUploader = $this->app->getService(FileUploader::class); + + if ($page->contentPath() === null) { + throw new UnexpectedValueException('Unexpected missing page path'); + } + + foreach ($files as $file) { + $fileUploader->upload($file, $page->contentPath(), $name, overwrite: $overwrite, allowedMimeTypes: $mimeTypes); + } + } + + /** + * Update last modified time of the given page + */ + private function updateLastModifiedTime(Page $page): void + { + if ($page->contentFile()?->path() !== null) { + FileSystem::touch($page->contentFile()->path()); + } + } + + /** + * Get previous and next file helper + * + * @return array{previousFile: ?File, nextFile: ?File} + */ + private function getPreviousAndNextFile(FileCollection $fileCollection, File $file): array + { + $fileIndex = $fileCollection->indexOf($file); + + return [ + 'previousFile' => $fileCollection->nth($fileIndex - 1), + 'nextFile' => $fileCollection->nth($fileIndex + 1), + ]; + } +} diff --git a/formwork/src/Panel/Controllers/PagesController.php b/formwork/src/Panel/Controllers/PagesController.php index 9343c1c6..cac9e3f6 100644 --- a/formwork/src/Panel/Controllers/PagesController.php +++ b/formwork/src/Panel/Controllers/PagesController.php @@ -7,7 +7,6 @@ use Formwork\Exceptions\TranslatedException; use Formwork\Fields\FieldCollection; use Formwork\Files\File; -use Formwork\Files\FileCollection; use Formwork\Files\Services\FileUploader; use Formwork\Http\Files\UploadedFile; use Formwork\Http\JsonResponse; @@ -19,7 +18,6 @@ use Formwork\Pages\PageFactory; use Formwork\Panel\ContentHistory\ContentHistory; use Formwork\Panel\ContentHistory\ContentHistoryEvent; -use Formwork\Parsers\Yaml; use Formwork\Router\RouteParams; use Formwork\Utils\Arr; use Formwork\Utils\Constraint; @@ -331,9 +329,9 @@ public function delete(RouteParams $routeParams): Response } /** - * Pages@uploadFile action + * Pages@upload action */ - public function uploadFile(RouteParams $routeParams): Response + public function upload(RouteParams $routeParams): Response|JsonResponse { if (!$this->hasPermission('pages.uploadFiles')) { return $this->forward(ErrorsController::class, 'forbidden'); @@ -342,29 +340,40 @@ public function uploadFile(RouteParams $routeParams): Response $page = $this->site->findPage($routeParams->get('page')); if ($page === null) { - $this->panel->notify($this->translate('panel.pages.page.cannotUploadFile.pageNotFound'), 'error'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + return JsonResponse::error($this->translate('panel.files.cannotUpload.pageNotFound'), ResponseStatus::InternalServerError); } + $uploadedFiles = []; + if (!$this->request->files()->isEmpty()) { try { - $this->processPageUploads($this->request->files()->getAll(), $page); + $uploadedFiles = $this->processPageUploads($this->request->files()->getAll(), $page); } catch (TranslatedException $e) { - $this->panel->notify($this->translate('upload.error', $this->translate($e->getLanguageString())), 'error'); - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + return JsonResponse::error($this->translate('upload.error', $this->translate($e->getLanguageString())), ResponseStatus::InternalServerError); } } $this->updateLastModifiedTime($page); - $this->panel->notify($this->translate('panel.uploader.uploaded'), 'success'); - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + return JsonResponse::success($this->translate('panel.uploader.uploaded'), data: Arr::map($uploadedFiles, fn(File $file) => [ + 'name' => $file->name(), + 'size' => $file->size(), + 'type' => $file->type(), + 'mimeType' => $file->mimeType(), + 'hash' => $file->hash(), + 'uri' => $file->uri(), + 'thumbnail' => match ($file->type()) { + 'image' => $file->square(300, 'contain')->uri(), // @phpstan-ignore method.notFound + 'video' => $file->uri(), + default => null, + }, + ]), ); } /** * Pages@deleteFile action */ - public function deleteFile(RouteParams $routeParams): Response + public function deleteFile(RouteParams $routeParams): JsonResponse|Response { if (!$this->hasPermission('pages.deleteFiles')) { return $this->forward(ErrorsController::class, 'forbidden'); @@ -372,82 +381,63 @@ public function deleteFile(RouteParams $routeParams): Response $page = $this->site->findPage($routeParams->get('page')); - if ($page === null) { - $this->panel->notify($this->translate('panel.pages.page.cannotDeleteFile.pageNotFound'), 'error'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); - } + $filename = $this->request->input()->get('filename'); - if (!$page->files()->has($routeParams->get('filename'))) { - $this->panel->notify($this->translate('panel.pages.page.cannotDeleteFile.fileNotFound'), 'error'); - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + if ($page === null || !$page->files()->has($filename)) { + return JsonResponse::error($this->translate('panel.files.cannotDelete.fileNotFound'), ResponseStatus::InternalServerError); } - FileSystem::delete($page->contentPath() . $routeParams->get('filename')); + FileSystem::delete($page->contentPath() . $this->request->input()->get('filename')); $this->updateLastModifiedTime($page); - $this->panel->notify($this->translate('panel.pages.page.fileDeleted'), 'success'); - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + return JsonResponse::success($this->translate('panel.files.deleted')); } /** * Pages@renameFile action */ - public function renameFile(RouteParams $routeParams): Response + public function renameFile(RouteParams $routeParams): JsonResponse|Response { if (!$this->hasPermission('pages.renameFiles')) { return $this->forward(ErrorsController::class, 'forbidden'); } + $previousName = $this->request->input()->get('filename'); + $page = $this->site->findPage($routeParams->get('page')); - $fields = $this->modal('renameFile')->fields(); + $fields = $this->modal('renameFileItem')->fields(); $fields->setValues($this->request->input())->validate(); $data = $fields->everyItem()->value(); - if ($page === null) { - $this->panel->notify($this->translate('panel.pages.page.cannotRenameFile.pageNotFound'), 'error'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); - } - - if (!$page->files()->has($routeParams->get('filename'))) { - $this->panel->notify($this->translate('panel.pages.page.cannotRenameFile.fileNotFound'), 'error'); - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + if ($page === null || !$page->files()->has($previousName)) { + return JsonResponse::error($this->translate('panel.files.cannotRename.fileNotFound'), ResponseStatus::InternalServerError); } $name = Str::slug(FileSystem::name($data->get('filename'))); - $extension = FileSystem::extension($routeParams->get('filename')); + $extension = FileSystem::extension($previousName); $newName = $name . '.' . $extension; - $previousName = $routeParams->get('filename'); - if ($newName !== $previousName) { if ($page->files()->has($newName)) { - $this->panel->notify($this->translate('panel.pages.page.cannotRenameFile.fileAlreadyExists'), 'error'); + $message = $this->translate('panel.files.cannotRename.fileAlreadyExists'); } else { FileSystem::move($page->contentPath() . $previousName, $page->contentPath() . $newName); $this->updateLastModifiedTime($page); - - $this->panel->notify($this->translate('panel.pages.page.fileRenamed'), 'success'); } } - $previousFileRoute = $this->generateRoute('panel.pages.file', ['page' => $routeParams->get('page'), 'filename' => $previousName]); - - if (Str::removeEnd((string) Uri::path((string) $this->request->referer()), '/') === $this->site->uri($previousFileRoute)) { - return $this->redirect($this->generateRoute('panel.pages.file', ['page' => $routeParams->get('page'), 'filename' => $newName])); - } - - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + return JsonResponse::success($message ?? $this->translate('panel.files.renamed'), data: ['filename' => $newName]); } /** * Pages@replaceFile action */ - public function replaceFile(RouteParams $routeParams): Response + public function replaceFile(RouteParams $routeParams): Response|JsonResponse { if (!$this->hasPermission('pages.replaceFiles')) { return $this->forward(ErrorsController::class, 'forbidden'); @@ -455,95 +445,41 @@ public function replaceFile(RouteParams $routeParams): Response $page = $this->site->findPage($routeParams->get('page')); - $filename = $routeParams->get('filename'); - - if ($page === null) { - $this->panel->notify($this->translate('panel.pages.page.cannotReplaceFile.pageNotFound'), 'error'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); - } + $filename = $this->request->input()->get('filename'); - if (!$page->files()->has($filename)) { - $this->panel->notify($this->translate('panel.pages.page.cannotReplaceFile.fileNotFound'), 'error'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + if ($page === null || !$page->files()->has($filename)) { + return JsonResponse::error($this->translate('panel.files.cannotReplace.fileNotFound'), ResponseStatus::InternalServerError); } if (!$this->request->files()->isEmpty()) { $files = $this->request->files()->getAll(); if (count($files) > 1) { - $this->panel->notify($this->translate('panel.pages.page.cannotReplaceFile.multipleFiles'), 'error'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); + return JsonResponse::error($this->translate('panel.files.cannotReplace.multipleFiles'), ResponseStatus::InternalServerError); } try { $this->processPageUploads($this->request->files()->getAll(), $page, [$page->files()->get($filename)->mimeType()], FileSystem::name($filename), true); } catch (TranslatedException $e) { - $this->panel->notify($this->translate('upload.error', $this->translate($e->getLanguageString())), 'error'); - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); + return JsonResponse::error($this->translate('upload.error', $this->translate($e->getLanguageString())), ResponseStatus::InternalServerError); } } $this->updateLastModifiedTime($page); - $this->panel->notify($this->translate('panel.uploader.uploaded'), 'success'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); - } - - /** - * Pages@file action - */ - public function file(RouteParams $routeParams): Response - { - if (!$this->hasPermission('pages.file')) { - return $this->forward(ErrorsController::class, 'forbidden'); - } - - $page = $this->site->findPage($routeParams->get('page')); - - $filename = $routeParams->get('filename'); - - if ($page === null) { - $this->panel->notify($this->translate('panel.pages.page.cannotGetFileInfo.pageNotFound'), 'error'); - return $this->redirectToReferer(default: $this->generateRoute('panel.pages'), base: $this->panel->panelRoot()); - } - - if (!$page->files()->has($filename)) { - $this->panel->notify($this->translate('panel.pages.page.cannotGetFileInfo.fileNotFound'), 'error'); - return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')])); - } - - $files = $page->files(); - - $file = $files->get($filename); - - switch ($this->request->method()) { - case RequestMethod::GET: - $data = $file->data(); + $file = $page->files()->get($filename); - $file->fields()->setValues($data); + $thumbnail = match ($file->type()) { + 'image' => $file->square(300, 'contain')->uri(), + 'video' => $file->uri(), + default => null, + }; - break; - - case RequestMethod::POST: - $data = $this->request->input(); - - $file->fields()->setValues($data)->validate(); - - $this->updateFileMetadata($file, $file->fields()); - - $this->updateLastModifiedTime($page); - - $this->panel->notify($this->translate('panel.files.metadata.updated'), 'success'); - - return $this->redirect($this->generateRoute('panel.pages.file', ['page' => $page->route(), 'filename' => $filename])); - } - - return new Response($this->view('pages.file', [ - 'title' => $file->name(), - 'page' => $page, - 'file' => $file, - ...$this->getPreviousAndNextFile($files, $file), - ])); + return JsonResponse::success($this->translate('panel.uploader.uploaded'), data: [ + 'size' => $file->size(), + 'type' => $file->type(), + 'thumbnail' => $thumbnail, + ]); } /** @@ -605,43 +541,15 @@ private function updatePage(Page $page, RequestData $requestData, FieldCollectio return $page; } - /** - * Update file metadata - */ - private function updateFileMetadata(File $file, FieldCollection $fieldCollection): void - { - $data = $file->data(); - - $scheme = $file->scheme(); - - $defaults = $scheme->fields()->pluck('default'); - - foreach ($fieldCollection as $field) { - if ($field->isEmpty() || (Arr::has($defaults, $field->name()) && Arr::get($defaults, $field->name()) === $field->value())) { - unset($data[$field->name()]); - continue; - } - - $data[$field->name()] = $field->value(); - } - - $metaFile = $file->path() . $this->config->get('system.files.metadataExtension'); - - if ($data === [] && FileSystem::exists($metaFile)) { - FileSystem::delete($metaFile); - return; - } - - FileSystem::write($metaFile, Yaml::encode($data)); - } - /** * Process page uploads * * @param array $files * @param list $mimeTypes + * + * @return array */ - private function processPageUploads(array $files, Page $page, ?array $mimeTypes = null, ?string $name = null, bool $overwrite = false): void + private function processPageUploads(array $files, Page $page, ?array $mimeTypes = null, ?string $name = null, bool $overwrite = false): array { $fileUploader = $this->app->getService(FileUploader::class); @@ -649,11 +557,15 @@ private function processPageUploads(array $files, Page $page, ?array $mimeTypes throw new UnexpectedValueException('Unexpected missing page path'); } + $uploadedFiles = []; + foreach ($files as $file) { - $fileUploader->upload($file, $page->contentPath(), $name, overwrite: $overwrite, allowedMimeTypes: $mimeTypes); + $uploadedFiles[] = $fileUploader->upload($file, $page->contentPath(), $name, overwrite: $overwrite, allowedMimeTypes: $mimeTypes); } $page->reload(); + + return $uploadedFiles; } /** @@ -705,19 +617,4 @@ private function getPreviousAndNextPage(Page $page): array 'nextPage' => $inclusiveSiblings->nth($pageIndex + 1), ]; } - - /** - * Get previous and next file helper - * - * @return array{previousFile: ?File, nextFile: ?File} - */ - private function getPreviousAndNextFile(FileCollection $fileCollection, File $file): array - { - $fileIndex = $fileCollection->indexOf($file); - - return [ - 'previousFile' => $fileCollection->nth($fileIndex - 1), - 'nextFile' => $fileCollection->nth($fileIndex + 1), - ]; - } } diff --git a/panel/modals/deleteFileItem.yaml b/panel/modals/deleteFileItem.yaml new file mode 100644 index 00000000..5858a6ae --- /dev/null +++ b/panel/modals/deleteFileItem.yaml @@ -0,0 +1,18 @@ +title: '{{panel.pages.deleteFile}}' + +message: '{{panel.pages.deleteFile.prompt}}' + +buttons: + dismiss: + action: dismiss + icon: times-circle + label: '{{panel.modal.action.cancel}}' + variant: secondary + + delete: + action: command + icon: trash + label: '{{panel.modal.action.delete}}' + align: right + variant: danger + command: delete-file diff --git a/panel/modals/renameFileItem.yaml b/panel/modals/renameFileItem.yaml new file mode 100644 index 00000000..01bb90f2 --- /dev/null +++ b/panel/modals/renameFileItem.yaml @@ -0,0 +1,21 @@ +title: '{{panel.pages.renameFile}}' + +fields: + filename: + type: text + label: '{{panel.pages.renameFile.name}}' + required: true + +buttons: + dismiss: + action: dismiss + icon: times-circle + label: '{{panel.modal.action.cancel}}' + variant: secondary + + submit: + action: command + icon: pencil + label: '{{panel.modal.action.rename}}' + align: right + command: rename-file diff --git a/panel/routes.php b/panel/routes.php index cf9f8331..6db79bc4 100644 --- a/panel/routes.php +++ b/panel/routes.php @@ -43,6 +43,30 @@ 'action' => 'Formwork\Panel\Controllers\DashboardController@index', ], + 'panel.files.delete' => [ + 'path' => '/files/pages/{page:all}/{filename}/delete/', + 'action' => 'Formwork\Panel\Controllers\FilesController@delete', + 'methods' => ['POST'], + ], + + 'panel.files.rename' => [ + 'path' => '/files/pages/{page:all}/{filename}/rename/', + 'action' => 'Formwork\Panel\Controllers\FilesController@rename', + 'methods' => ['POST'], + ], + + 'panel.files.replace' => [ + 'path' => '/files/pages/{page:all}/{filename}/replace/', + 'action' => 'Formwork\Panel\Controllers\FilesController@replace', + 'methods' => ['POST'], + ], + + 'panel.files.index' => [ + 'path' => '/files/pages/{page:all}/{filename}/', + 'action' => 'Formwork\Panel\Controllers\FilesController@index', + 'methods' => ['GET', 'POST'], + ], + 'panel.pages' => [ 'path' => '/pages/', 'action' => 'Formwork\Panel\Controllers\PagesController@index', @@ -60,6 +84,13 @@ 'methods' => ['GET', 'POST'], ], + 'panel.pages.upload' => [ + 'path' => '/pages/{page:all}/edit/', + 'action' => 'Formwork\Panel\Controllers\PagesController@upload', + 'methods' => ['POST'], + 'types' => ['XHR'], + ], + 'panel.pages.preview' => [ 'path' => '/pages/{page:all}/preview/', 'action' => 'Formwork\Panel\Controllers\PagesController@preview', @@ -79,34 +110,25 @@ 'types' => ['XHR'], ], - 'panel.pages.file' => [ - 'path' => '/pages/{page:all}/file/{filename}/', - 'action' => 'Formwork\Panel\Controllers\PagesController@file', - 'methods' => ['GET', 'POST'], - ], - - 'panel.pages.uploadFile' => [ - 'path' => '/pages/{page:all}/file/upload/', - 'action' => 'Formwork\Panel\Controllers\PagesController@uploadFile', - 'methods' => ['POST'], - ], - 'panel.pages.deleteFile' => [ - 'path' => '/pages/{page:all}/file/{filename}/delete/', + 'path' => '/pages/{page:all}/file/delete/', 'action' => 'Formwork\Panel\Controllers\PagesController@deleteFile', 'methods' => ['POST'], + 'types' => ['XHR'], ], 'panel.pages.renameFile' => [ - 'path' => '/pages/{page:all}/file/{filename}/rename/', + 'path' => '/pages/{page:all}/file/rename/', 'action' => 'Formwork\Panel\Controllers\PagesController@renameFile', 'methods' => ['POST'], + 'types' => ['XHR'], ], 'panel.pages.replaceFile' => [ - 'path' => '/pages/{page:all}/file/{filename}/replace/', + 'path' => '/pages/{page:all}/file/replace/', 'action' => 'Formwork\Panel\Controllers\PagesController@replaceFile', 'methods' => ['POST'], + 'types' => ['XHR'], ], 'panel.pages.delete' => [ diff --git a/panel/src/scss/components/_files-list.scss b/panel/src/scss/components/_files-list.scss index 64992c3c..efaffa32 100644 --- a/panel/src/scss/components/_files-list.scss +++ b/panel/src/scss/components/_files-list.scss @@ -35,7 +35,7 @@ margin-bottom: 0; } -.file-name { +.file-item-label { flex-grow: 1; } @@ -81,7 +81,7 @@ display: block; } -.is-thumbnails .file-name { +.is-thumbnails .file-item-label { position: absolute; right: 0.75rem; bottom: 0.5rem; diff --git a/panel/src/scss/components/_header.scss b/panel/src/scss/components/_header.scss index 6e2ee4fc..ae462872 100644 --- a/panel/src/scss/components/_header.scss +++ b/panel/src/scss/components/_header.scss @@ -29,6 +29,11 @@ } } +.header-icon { + align-self: center; + margin-right: 0.375rem; +} + .header-title { overflow: hidden; flex-grow: 1; diff --git a/panel/src/scss/components/forms/_forms-file.scss b/panel/src/scss/components/forms/_forms-file.scss index e4721e58..eedcdedc 100644 --- a/panel/src/scss/components/forms/_forms-file.scss +++ b/panel/src/scss/components/forms/_forms-file.scss @@ -5,7 +5,7 @@ display: none; } -.form-input-file-label { +.form-file-drop-target { display: block; padding: 0.5rem; border-radius: $border-radius; @@ -15,11 +15,11 @@ font-size: 0.875rem; } -.form-input-file-label:focus { +.form-file-drop-target:focus { @include focusring; } -.form-input-file-label > span { +.form-file-drop-target > span { display: block; padding: 1.5rem 1rem; border: 2px dashed var(--color-base-500); @@ -33,6 +33,6 @@ } } -.form-input-file-label.drag > span { +.form-file-drop-target.drag > span { background-color: var(--color-base-700); } diff --git a/panel/src/ts/app.ts b/panel/src/ts/app.ts index 08f646d3..eee84f3f 100644 --- a/panel/src/ts/app.ts +++ b/panel/src/ts/app.ts @@ -60,10 +60,10 @@ class App { this.loadComponent(ColorScheme); this.loadComponent(Notifications); this.loadComponent(Sections); - this.loadComponent(Files); this.loadComponent(Dashboard); this.loadComponent(Pages); + this.loadComponent(Files); this.loadComponent(Statistics); this.loadComponent(Backups); this.loadComponent(Updates); diff --git a/panel/src/ts/components/files.ts b/panel/src/ts/components/files.ts index 40942801..0a66732f 100644 --- a/panel/src/ts/components/files.ts +++ b/panel/src/ts/components/files.ts @@ -1,35 +1,59 @@ -import { $, $$ } from "../utils/selectors"; +import { $ } from "../utils/selectors"; +import { app } from "../app"; export class Files { constructor() { - $$(".files-list").forEach((filesList) => { - const toggle = $(".form-togglegroup.files-list-view-as", filesList); - - if (toggle) { - const fieldName = toggle.dataset.fieldName; - const viewAs = window.localStorage.getItem(`formwork.filesListViewAs[${fieldName}]`); - - if (viewAs) { - $$("input", toggle).forEach((input: HTMLInputElement) => (input.checked = false)); - ($(`input[value=${viewAs}]`, filesList) as HTMLInputElement).checked = true; - filesList.classList.toggle("is-thumbnails", viewAs === "thumbnails"); - } - - $$("input", toggle).forEach((input: HTMLInputElement) => { - input.addEventListener("input", () => { - filesList.classList.toggle("is-thumbnails", input.value === "thumbnails"); - window.localStorage.setItem(`formwork.filesListViewAs[${fieldName}]`, input.value); - }); + const fileForm = $("[data-form=file-form]"); + + if (fileForm) { + const renameFileModal = app.modals["renameFileModal"]; + + if (renameFileModal) { + renameFileModal.form?.element.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + renameFileModal.form?.element.submit(); + event.preventDefault(); + } }); - } - $$(".files-item", filesList).forEach((item: HTMLElement) => { - item.addEventListener("click", (event) => { - if (!(event.target as HTMLElement).closest(".dropdown") && typeof item.dataset.href === "string") { - location.href = item.dataset.href; + renameFileModal.onOpen((modal, trigger) => { + if (trigger) { + const input = $('[id="renameFileModal.filename"]', modal.element) as HTMLInputElement; + input.value = trigger.dataset.filename as string; + input.setSelectionRange(0, input.value.lastIndexOf(".")); } }); - }); - }); + } + + const replaceFileCommand = $("[data-command=replaceFile]"); + + if (replaceFileCommand) { + replaceFileCommand.addEventListener("click", () => { + const form = document.createElement("form"); + form.hidden = true; + form.action = replaceFileCommand.dataset.action as string; + form.method = "post"; + form.enctype = "multipart/form-data"; + + const fileInput = document.createElement("input"); + fileInput.name = "file"; + fileInput.type = "file"; + fileInput.accept = replaceFileCommand.dataset.extension as string; + form.appendChild(fileInput); + + const csrfInput = document.createElement("input"); + csrfInput.name = "csrf-token"; + csrfInput.value = ($("meta[name=csrf-token]") as HTMLMetaElement).content; + form.appendChild(csrfInput); + + fileInput.click(); + + fileInput.addEventListener("change", () => { + document.body.appendChild(form); + form.submit(); + }); + }); + } + } } } diff --git a/panel/src/ts/components/form.ts b/panel/src/ts/components/form.ts index 4e4bb49f..c8d9429a 100644 --- a/panel/src/ts/components/form.ts +++ b/panel/src/ts/components/form.ts @@ -4,103 +4,92 @@ import { app } from "../app"; import { Inputs } from "./inputs"; import { serializeForm } from "../utils/forms"; +interface FormOptions { + preventUnloadOnChanges?: boolean; +} export class Form { inputs: Inputs; originalData: string; element: HTMLFormElement; + options: FormOptions = { + preventUnloadOnChanges: true, + }; - constructor(form: HTMLFormElement) { + constructor(form: HTMLFormElement, options: Partial = {}) { this.element = form; - this.inputs = new Inputs(form); + this.inputs = new Inputs(this); // Serialize after inputs are loaded this.originalData = serializeForm(form); - const handleBeforeunload = (event: Event) => { - if (this.hasChanged()) { - event.preventDefault(); - event.returnValue = false; + this.options = { ...this.options, ...options }; + + if (this.options.preventUnloadOnChanges) { + this.preventUnloadOnChanges(); + } + } + + hasChanged(checkFileInputs: boolean = true) { + const fileInputs = $$("input[type=file]", this.element) as NodeListOf; + + if (checkFileInputs === true && fileInputs.length > 0) { + for (const fileInput of Array.from(fileInputs)) { + if (fileInput.files && fileInput.files.length > 0) { + return true; + } } - }; + } - const removeBeforeUnload = () => { - window.removeEventListener("beforeunload", handleBeforeunload); - }; + return serializeForm(this.element) !== this.originalData; + } - window.addEventListener("beforeunload", handleBeforeunload); + private preventUnloadOnChanges() { + window.addEventListener("beforeunload", this.handleBeforeunload); - form.addEventListener("submit", removeBeforeUnload); + this.element.addEventListener("submit", this.removeBeforeUnload); - $$('a[href]:not([href^="#"]):not([target="_blank"]):not([target^="formwork-"])').forEach((element: HTMLAnchorElement) => { - if (element.closest(".editor-wrap")) { - return; - } - element.addEventListener("click", (event) => { - if (this.hasChanged()) { - event.preventDefault(); - - app.modals["changesModal"].onOpen((modal) => { - const continueCommand = $("[data-command=continue]", modal.element); - if (continueCommand) { - continueCommand.dataset.href = element.href; - } - }); - - app.modals["changesModal"].open(); + const changesModal = app.modals["changesModal"]; + + if (changesModal) { + changesModal.onCommand("continue", (_, button) => { + this.removeBeforeUnload(); + if (button?.dataset.href) { + window.location.href = button.dataset.href; } }); - }); - $$("input[type=file][data-auto-upload=true]", form).forEach((element) => { - element.addEventListener("change", () => { - if (!this.hasChanged(false)) { - form.requestSubmit($("[type=submit]", form)); + $$('a[href]:not([href^="#"]):not([target="_blank"]):not([target^="formwork-"])').forEach((element: HTMLAnchorElement) => { + if (element.closest(".editor-wrap")) { + return; } - }); - }); - registerModalExceptions(); + element.addEventListener("click", (event) => { + if (this.hasChanged()) { + event.preventDefault(); - function registerModalExceptions() { - const changesModal = app.modals["changesModal"]; - const deletePageModal = app.modals["deletePageModal"]; - const deleteUserModal = app.modals["deleteUserModal"]; + app.modals["changesModal"].onOpen((modal) => { + const continueCommand = $("[data-command=continue]", modal.element); + if (continueCommand) { + continueCommand.dataset.href = element.href; + } + }); - if (changesModal) { - changesModal.onCommand("continue", (_, button) => { - removeBeforeUnload(); - if (button?.dataset.href) { - window.location.href = button.dataset.href; + app.modals["changesModal"].open(); } }); - } - - if (deletePageModal) { - deletePageModal.onCommand("delete", () => { - removeBeforeUnload(); - }); - } - - if (deleteUserModal) { - deleteUserModal.onCommand("delete", () => { - removeBeforeUnload(); - }); - } + }); } } - hasChanged(checkFileInputs: boolean = true) { - const fileInputs = $$("input[type=file]", this.element) as NodeListOf; - - if (checkFileInputs === true && fileInputs.length > 0) { - for (const fileInput of Array.from(fileInputs)) { - if (fileInput.files && fileInput.files.length > 0) { - return true; - } - } + private handleBeforeunload(event: Event) { + if (this.hasChanged()) { + event.preventDefault(); + event.returnValue = false; } + } - return serializeForm(this.element) !== this.originalData; + private removeBeforeUnload() { + window.removeEventListener("beforeunload", this.handleBeforeunload); } } diff --git a/panel/src/ts/components/inputs.ts b/panel/src/ts/components/inputs.ts index b8437767..08cfb2ed 100644 --- a/panel/src/ts/components/inputs.ts +++ b/panel/src/ts/components/inputs.ts @@ -5,6 +5,7 @@ import { DateInput } from "./inputs/date-input"; import { DurationInput } from "./inputs/duration-input"; import { EditorInput } from "./inputs/editor-input"; import { FileInput } from "./inputs/file-input"; +import { Form } from "./form"; import { ImagePicker } from "./inputs/image-picker"; import { RangeInput } from "./inputs/range-input"; import { SelectInput } from "./inputs/select-input"; @@ -15,7 +16,9 @@ import { TogglegroupInput } from "./inputs/togglegroup-input"; export class Inputs { [name: string]: object; - constructor(parent: HTMLElement) { + constructor(form: Form) { + const parent = form.element; + $$(".editor-textarea", parent).forEach((element: HTMLTextAreaElement) => (this[element.name] = new EditorInput(element))); $$(".form-input-array", parent).forEach((element: HTMLInputElement) => (this[element.name] = new ArrayInput(element))); @@ -32,7 +35,7 @@ export class Inputs { $$(".image-picker", parent).forEach((element: HTMLSelectElement) => (this[element.name] = new ImagePicker(element))); - $$("input[type=file]", parent).forEach((element: HTMLInputElement) => (this[element.name] = new FileInput(element))); + $$("input[type=file]", parent).forEach((element: HTMLInputElement) => (this[element.name] = new FileInput(element, form))); $$("input[type=range]", parent).forEach((element: HTMLInputElement) => (this[element.name] = new RangeInput(element))); diff --git a/panel/src/ts/components/inputs/file-input.ts b/panel/src/ts/components/inputs/file-input.ts index bda93f1d..e1c6b084 100644 --- a/panel/src/ts/components/inputs/file-input.ts +++ b/panel/src/ts/components/inputs/file-input.ts @@ -1,31 +1,40 @@ -import { $ } from "../../utils/selectors"; +import { $, $$ } from "../../utils/selectors"; +import { app } from "../../app"; +import { Form } from "../form"; +import { insertIcon } from "../icons"; +import { Notification } from "../notification"; +import { Request } from "../../utils/request"; export class FileInput { - constructor(input: HTMLInputElement) { + constructor(input: HTMLInputElement, form: Form) { const label = $(`label[for="${input.id}"]`) as HTMLElement; - const span = $("span", label) as HTMLElement; - const defaultLabel = span.innerHTML ?? ""; + const dropTarget = input.closest(".form-file-drop-target") as HTMLElement; + const dropTargetLabel = $("span", dropTarget) as HTMLElement; + const defaultDropLabel = dropTargetLabel.innerHTML ?? ""; let isSubmitted = false; - input.addEventListener("change", updateLabel); - input.addEventListener("input", updateLabel); + label?.addEventListener("click", (event) => { + dropTarget.focus(); + event.preventDefault(); + }); + + input.addEventListener("change", updateDropTargetLabel); + input.addEventListener("input", updateDropTargetLabel); input.form?.addEventListener("submit", () => { - if (input.files && input.files.length > 0) { - span.innerHTML += ' '; - } isSubmitted = true; + updateDropTargetLabel(); }); - label.addEventListener("drag", preventDefault); - label.addEventListener("dragstart", preventDefault); - label.addEventListener("dragend", preventDefault); - label.addEventListener("dragover", handleDragenter); - label.addEventListener("dragenter", handleDragenter); - label.addEventListener("dragleave", handleDragleave); + dropTarget.addEventListener("drag", (event) => event.preventDefault()); + dropTarget.addEventListener("dragstart", (event) => event.preventDefault()); + dropTarget.addEventListener("dragend", (event) => event.preventDefault()); + dropTarget.addEventListener("dragover", handleDragenter); + dropTarget.addEventListener("dragenter", handleDragenter); + dropTarget.addEventListener("dragleave", handleDragleave); - label.addEventListener("drop", (event) => { + dropTarget.addEventListener("drop", (event) => { event.preventDefault(); if (isSubmitted) { return; @@ -37,40 +46,274 @@ export class FileInput { } }); - label.addEventListener("click", (event) => { + dropTarget.addEventListener("click", (event) => { if (isSubmitted) { event.preventDefault(); } }); - label.addEventListener("keydown", (event) => { + dropTarget.addEventListener("keydown", (event) => { if (event.key === "Enter") { input.click(); } }); + const filesList = $(`.files-list[data-for="${input.id}"]`) as HTMLElement; + + if (!filesList && input.dataset.autoUpload === "true") { + input.addEventListener("change", () => { + if (!form.hasChanged(false)) { + form.element.requestSubmit($("[type=submit]", form.element)); + } + }); + } + + if (filesList) { + const toggle = $(".form-togglegroup.files-list-view-as", filesList); + + if (toggle) { + const fieldName = toggle.dataset.for; + const viewAs = window.localStorage.getItem(`formwork.filesListViewAs[${fieldName}]`); + + if (viewAs) { + $$("input", toggle).forEach((input: HTMLInputElement) => (input.checked = false)); + ($(`input[value=${viewAs}]`, filesList) as HTMLInputElement).checked = true; + filesList.classList.toggle("is-thumbnails", viewAs === "thumbnails"); + } + + $$("input", toggle).forEach((input: HTMLInputElement) => { + input.addEventListener("input", () => { + filesList.classList.toggle("is-thumbnails", input.value === "thumbnails"); + window.localStorage.setItem(`formwork.filesListViewAs[${fieldName}]`, input.value); + }); + }); + } + + document.addEventListener("click", (event) => { + const target = event.target as HTMLElement; + if (!target.closest(".dropdown") && target.closest(".files-item")) { + const item = target.closest(".files-item") as HTMLElement; + if (typeof item.dataset.href === "string") { + location.href = item.dataset.href; + } + } + }); + + if (input.dataset.autoUpload === "true") { + input.addEventListener("change", () => { + if (input.files?.length) { + for (const file of Array.from(input.files)) { + const formData = new FormData(); + formData.append("csrf-token", ($("meta[name=csrf-token]") as HTMLMetaElement).content); + formData.append("file", file); + + dropTargetLabel.innerHTML += ' '; + + new Request( + { + method: "POST", + data: formData, + }, + (response) => { + const notification = new Notification(response.message, response.status); + + if (response.status === "success") { + const template = $("template[id=files-item]") as HTMLTemplateElement; + + addFilesItem(response.data[0], template); + sortFilesList(filesList, ".file-name"); + + input.value = ""; + updateDropTargetLabel(); + } + + notification.show(); + }, + ); + } + } + }); + } + + const renameFileItemModal = app.modals["renameFileItemModal"]; + + if (renameFileItemModal) { + $('[id="renameFileItemModal.filename"]', renameFileItemModal.element)?.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + renameFileItemModal.triggerCommand("rename-file"); + event.preventDefault(); + } + }); + + renameFileItemModal.onOpen((modal, trigger) => { + if (trigger) { + const input = $('[id="renameFileItemModal.filename"]', modal.element) as HTMLInputElement; + input.value = (trigger.closest("[data-filename]") as HTMLElement)?.dataset.filename as string; + input.setSelectionRange(0, input.value.lastIndexOf(".")); + + Object.assign(modal.data, { + action: trigger.dataset.action, + item: trigger.closest(".files-item"), + filename: (trigger.closest("[data-filename]") as HTMLElement)?.dataset.filename, + input, + }); + } + }); + + renameFileItemModal.onCommand("rename-file", (modal) => { + const { action, item, filename, input } = modal.data; + + new Request( + { + method: "POST", + url: action as string, + data: { + filename, + "renameFileItemModal[filename]": (input as HTMLInputElement).value, + "csrf-token": ($("meta[name=csrf-token]") as HTMLMetaElement).content, + }, + }, + (response) => { + if (response.status === "success") { + (item as HTMLElement).dataset.filename = response.data.filename; + + ($(".file-name", item as HTMLElement) as HTMLElement).innerHTML = response.data.filename; + + const template = $("template[id=files-item]") as HTMLTemplateElement; + const uri = ($(".files-item", template.content) as HTMLElement).dataset.href as string; + (item as HTMLElement).dataset.href = `${uri}${response.data.filename}`; + + sortFilesList(filesList, ".file-name"); + } + + const notification = new Notification(response.message, response.status); + notification.show(); + + modal.close(); + }, + ); + + modal.close(); + }); + } + + const deleteFileItemModal = app.modals["deleteFileItemModal"]; + + if (deleteFileItemModal) { + deleteFileItemModal.onOpen((modal, trigger) => { + if (trigger) { + Object.assign(modal.data, { + action: trigger.dataset.action, + item: trigger.closest(".files-item"), + filename: (trigger.closest("[data-filename]") as HTMLElement)?.dataset.filename, + }); + } + }); + + deleteFileItemModal.onCommand("delete-file", (modal) => { + const { action, item, filename } = modal.data; + + new Request( + { + method: "POST", + url: action as string, + data: { + filename, + "csrf-token": ($("meta[name=csrf-token]") as HTMLMetaElement).content, + }, + }, + (response) => { + if (response.status === "success") { + (item as HTMLElement).remove(); + } + + const notification = new Notification(response.message, response.status); + notification.show(); + + modal.close(); + }, + ); + + modal.close(); + }); + } + + filesList.addEventListener("click", (event) => { + const element = (event.target as HTMLElement).closest("[data-command=replaceFile]") as HTMLElement; + if (element) { + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.accept = element.dataset.mimetype as string; + fileInput.click(); + + fileInput.addEventListener("change", () => { + if (fileInput.files?.length) { + const formData = new FormData(); + formData.append("filename", (element.closest("[data-filename]") as HTMLElement).dataset.filename as string); + formData.append("csrf-token", ($("meta[name=csrf-token]") as HTMLMetaElement).content); + formData.append("file", fileInput.files[0]); + + new Request( + { + method: "POST", + url: element.dataset.action as string, + data: formData, + }, + (response) => { + const notification = new Notification(response.message, response.status); + + if (response.status === "success") { + if (element.closest("[data-form=page-file-form]")) { + window.location.reload(); + } else if (response.data.thumbnail) { + const thumbnail = $(".file-thumbnail", element.closest(".files-item") as HTMLElement) as HTMLImageElement; + const fileSize = $(".file-size", element.closest(".files-item") as HTMLElement) as HTMLImageElement; + + if (response.data.type === "image") { + thumbnail.style.backgroundImage = `url(${response.data.thumbnail})`; + } else if (response.data.type === "video") { + thumbnail.src = response.data.thumbnail; + } + + fileSize.textContent = `(${response.data.size})`; + } + } + + notification.show(); + }, + ); + } + + fileInput.remove(); + }); + } + }); + } + function formatFileSize(size: number) { const units = ["B", "KB", "MB", "GB", "TB"]; const exp = Math.min(Math.floor(Math.log(size) / Math.log(1024)), units.length - 1); return `${(size / 1024 ** exp).toFixed(2)} ${units[exp]}`; } - function updateLabel() { + function updateDropTargetLabel() { if (input.files && input.files.length > 0) { const filenames: string[] = []; for (const file of Array.from(input.files)) { filenames.push(`${file.name} (${formatFileSize(file.size)})`); } - span.innerHTML = filenames.join(", "); + dropTargetLabel.innerHTML = filenames.join(", "); + + if (isSubmitted && !$(".spinner", dropTargetLabel)) { + const spinner = document.createElement("span"); + spinner.classList.add("spinner", "ml-3"); + dropTargetLabel.appendChild(spinner); + } } else { - span.innerHTML = defaultLabel; + dropTargetLabel.innerHTML = defaultDropLabel; } } - function preventDefault(event: Event) { - event.preventDefault(); - } - function handleDragenter(this: HTMLInputElement, event: DragEvent) { this.classList.add("drag"); event.preventDefault(); @@ -81,8 +324,54 @@ export class FileInput { event.preventDefault(); } - $(`.form-label[data-for="${input.id}"]`)?.addEventListener("click", () => { - label.focus(); - }); + function sortFilesList(filesList: HTMLElement, selector: string = ".file-name") { + const filesItems = $$(".files-item", filesList); + Array.from(filesItems) + .sort((a: HTMLElement, b: HTMLElement) => { + const keyA = $(selector, a)?.textContent; + const keyB = $(selector, b)?.textContent; + return keyA?.localeCompare(keyB ?? "") ?? 0; + }) + .forEach((element: HTMLElement) => { + element.parentElement?.appendChild(element); + }); + } + + function addFilesItem(info: { [key: string]: string }, template: HTMLTemplateElement) { + const node = template.content.cloneNode(true) as HTMLElement; + const filesItem = $(".files-item", node) as HTMLElement; + + filesItem.dataset.filename = info.name; + filesItem.dataset.href += `${info.name}/`; + + if (info.type === "image") { + ($(".file-thumbnail", filesItem) as HTMLElement).style.backgroundImage = `url(${info.thumbnail})`; + } else if (info.type === "video") { + const video = document.createElement("video"); + video.classList.add("file-thumbnail"); + video.src = info.thumbnail; + video.preload = "metadata"; + $(".file-thumbnail", filesItem)?.replaceWith(video); + } else { + $(".file-thumbnail", filesItem)?.remove(); + } + + insertIcon(info.type ? `file-${info.type}` : "file", $(".file-icon", filesItem) as HTMLElement); + + ($(".file-name", filesItem) as HTMLElement).textContent = info.name; + ($(".file-size", filesItem) as HTMLElement).textContent = `(${info.size})`; + + ($(".dropdown-button", filesItem) as HTMLElement).dataset.dropdown = `dropdown-${info.hash}`; + ($(".dropdown-menu", filesItem) as HTMLElement).id = `dropdown-${info.hash}`; + + ($("[data-command=infoFile", filesItem) as HTMLAnchorElement).href += `${info.name}/`; + + ($("[data-command=previewFile"), filesItem as HTMLAnchorElement).href = info.uri; + ($("[data-command=previewFile"), filesItem as HTMLAnchorElement).target = `formwork-preview-file-${info.hash}`; + + ($("[data-command=replaceFile", filesItem) as HTMLElement).dataset.extension = `${info.mimeType}`; + + $(".files-items", filesList)?.appendChild(node); + } } } diff --git a/panel/src/ts/components/modal.ts b/panel/src/ts/components/modal.ts index a867593a..07e7d54c 100644 --- a/panel/src/ts/components/modal.ts +++ b/panel/src/ts/components/modal.ts @@ -1,4 +1,5 @@ import { $, $$ } from "../utils/selectors"; +import { Form } from "./form"; import { Inputs } from "./inputs"; interface ModalShowOptions { @@ -17,6 +18,8 @@ type ModalState = "open" | "closed"; export class Modal { readonly element: HTMLElement; + readonly form: Form | null; + readonly inputs: Inputs; readonly data: { [key: string]: unknown } = {}; @@ -28,7 +31,13 @@ export class Modal { constructor(element: HTMLElement) { this.element = element; - new Inputs(this.element); + const formElement = $("form", this.element) as HTMLFormElement | null; + + this.form = formElement + ? new Form(formElement, { + preventUnloadOnChanges: false, + }) + : null; this.registerEvents(); } @@ -47,9 +56,8 @@ export class Modal { this.element.classList.add("open"); if (options.action) { - const form = $("form", this.element) as HTMLFormElement | null; - if (form) { - form.action = options.action; + if (this.form) { + this.form.element.action = options.action; } } diff --git a/panel/src/ts/components/views/pages.ts b/panel/src/ts/components/views/pages.ts index d40adb40..9457114c 100644 --- a/panel/src/ts/components/views/pages.ts +++ b/panel/src/ts/components/views/pages.ts @@ -180,46 +180,6 @@ export class Pages { } } - const renameFileModal = app.modals["renameFileModal"]; - - if (renameFileModal) { - renameFileModal.onOpen((modal, trigger) => { - if (trigger) { - const input = $('[id="renameFileModal.filename"]', modal.element) as HTMLInputElement; - input.value = trigger.dataset.filename as string; - input.setSelectionRange(0, input.value.lastIndexOf(".")); - } - }); - } - - $$("[data-command=replaceFile]").forEach((element) => { - element.addEventListener("click", () => { - const form = document.createElement("form"); - form.hidden = true; - form.action = element.dataset.action as string; - form.method = "post"; - form.enctype = "multipart/form-data"; - - const fileInput = document.createElement("input"); - fileInput.name = "file"; - fileInput.type = "file"; - fileInput.accept = element.dataset.extension as string; - form.appendChild(fileInput); - - const csrfInput = document.createElement("input"); - csrfInput.name = "csrf-token"; - csrfInput.value = ($("meta[name=csrf-token]") as HTMLMetaElement).content; - form.appendChild(csrfInput); - - fileInput.click(); - - fileInput.addEventListener("change", () => { - document.body.appendChild(form); - form.submit(); - }); - }); - }); - function expandAllPages() { $$(".pages-tree-item").forEach((element) => { element.classList.add("is-expanded"); diff --git a/panel/src/ts/utils/request.ts b/panel/src/ts/utils/request.ts index 4987f7d8..e6ce161e 100644 --- a/panel/src/ts/utils/request.ts +++ b/panel/src/ts/utils/request.ts @@ -1,26 +1,51 @@ -import { serializeObject } from "./forms"; - -type RequestOptions = { +interface RequestOptions { method: string; url: string; - data: Record; + data: Record | FormData; + headers: Record; +} + +const defaultOptions: RequestOptions = { + method: "GET", + url: "", + data: {}, + headers: {}, }; export class Request { - constructor(options: RequestOptions, callback: (response: Record, request: XMLHttpRequest) => void) { + constructor(userOptions: Partial, callback: (response: Record, request: XMLHttpRequest) => void) { const request = new XMLHttpRequest(); + const options: RequestOptions = Object.assign({}, defaultOptions, userOptions); + + if (!options.headers["X-Requested-With"]) { + options.headers["X-Requested-With"] = "XMLHttpRequest"; + } + request.open(options.method, options.url, true); - request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - request.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - request.send(serializeObject(options.data)); + + for (const key in options.headers) { + request.setRequestHeader(key, options.headers[key]); + } + + switch (true) { + case options.data instanceof FormData: + case options.data instanceof URLSearchParams: + case options.data instanceof Blob: + request.send(options.data); + break; + + default: + request.send(new URLSearchParams(options.data)); + break; + } if (typeof callback === "function") { const handler = () => { const response = JSON.parse(request.response); const code = response.code || request.status; if (parseInt(code) === 400) { - location.reload(); + // location.reload(); } else { callback(response, request); } diff --git a/panel/translations/de.yaml b/panel/translations/de.yaml index 469ff43d..c874f1d9 100644 --- a/panel/translations/de.yaml +++ b/panel/translations/de.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: Die Seite existiert nicht oder die Anfr panel.errors.error.notFound.heading: Oops, Seite nicht gefunden! panel.errors.error.notFound.status: Nicht gefunden panel.files.actions: Aktionen +panel.files.cannotDelete.fileNotFound: Datei kann nicht gelöscht werden, Datei nicht gefunden +panel.files.cannotDelete.pageNotFound: Datei kann nicht gelöscht werden, Datei nicht gefunden +panel.files.cannotRename.fileAlreadyExists: Datei kann nicht umbenannt werden, eine Datei mit demselben Namen existiert bereits +panel.files.cannotRename.fileNotFound: Datei kann nicht umbenannt werden, Datei nicht gefunden +panel.files.cannotRename.pageNotFound: Datei kann nicht umbenannt werden, Seite nicht gefunden +panel.files.cannotReplace.fileNotFound: Datei kann nicht ersetzt werden, Datei nicht gefunden +panel.files.cannotReplace.multipleFiles: Datei kann nicht ersetzt werden, mehrere Dateien angegeben +panel.files.cannotReplace.pageNotFound: Datei kann nicht ersetzt werden, Seite nicht gefunden +panel.files.cannotUpload.pageNotFound: Datei kann nicht hochgeladen werden, Seite nicht gefunden +panel.files.deleted: Datei gelöscht +panel.files.fileNotFound: Dateiinfo kann nicht abgerufen werden, Datei nicht gefunden panel.files.metadata.updated: Dateimetadaten aktualisiert +panel.files.pageNotFound: Dateiinfo kann nicht abgerufen werden, Seite nicht gefunden +panel.files.renamed: Datei umbenannt panel.files.viewAsList: Als Liste anzeigen panel.files.viewAsThumbnails: Als Miniaturansichten anzeigen panel.login.attempt.failed: Anmeldeversuch fehlgeschlagen! Versuchen Sie es erneut. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Seite kann nicht erstellt werden, eine panel.pages.page.cannotDelete.invalidLanguage: "Seite kann nicht gelöscht werden, ungültige Sprache: %s" panel.pages.page.cannotDelete.notDeletable: Seite kann nicht gelöscht werden, die Seite ist nicht löschbar panel.pages.page.cannotDelete.pageNotFound: Seite kann nicht gelöscht werden, Seite nicht gefunden -panel.pages.page.cannotDeleteFile.fileNotFound: Datei kann nicht gelöscht werden, Datei nicht gefunden -panel.pages.page.cannotDeleteFile.pageNotFound: Datei kann nicht gelöscht werden, Datei nicht gefunden panel.pages.page.cannotEdit.alreadyExists: Seite kann nicht bearbeitet werden, eine Seite mit demselben URI existiert bereits panel.pages.page.cannotEdit.indexOrErrorPageSlug: Slug von Index- und Fehlerseiten kann nicht bearbeitet werden panel.pages.page.cannotEdit.invalidLanguage: "Seite kann nicht bearbeitet werden, ungültige Sprache: %s" @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Seiten-Slug kann nicht bearbeitet werde panel.pages.page.cannotEdit.invalidTemplate: Seite kann nicht bearbeitet werden, ungültige Vorlage panel.pages.page.cannotEdit.pageNotFound: Seite kann nicht bearbeitet werden, Seite nicht gefunden panel.pages.page.cannotEdit.varMissing: Seite kann nicht bearbeitet werden, es fehlt eine Variable -panel.pages.page.cannotGetFileInfo.fileNotFound: Dateiinfo kann nicht abgerufen werden, Datei nicht gefunden -panel.pages.page.cannotGetFileInfo.pageNotFound: Dateiinfo kann nicht abgerufen werden, Seite nicht gefunden panel.pages.page.cannotMove: Seite kann nicht verschoben werden panel.pages.page.cannotPreview.pageNotFound: Vorschau der Seite nicht möglich, Seite nicht gefunden panel.pages.page.cannotPreview.parentChanged: Vorschau der Seite nicht möglich, übergeordnete Seite wurde geändert -panel.pages.page.cannotRenameFile.fileAlreadyExists: Datei kann nicht umbenannt werden, eine Datei mit demselben Namen existiert bereits -panel.pages.page.cannotRenameFile.fileNotFound: Datei kann nicht umbenannt werden, Datei nicht gefunden -panel.pages.page.cannotRenameFile.pageNotFound: Datei kann nicht umbenannt werden, Seite nicht gefunden -panel.pages.page.cannotReplaceFile.fileNotFound: Datei kann nicht ersetzt werden, Datei nicht gefunden -panel.pages.page.cannotReplaceFile.multipleFiles: Datei kann nicht ersetzt werden, mehrere Dateien angegeben -panel.pages.page.cannotReplaceFile.pageNotFound: Datei kann nicht ersetzt werden, Seite nicht gefunden -panel.pages.page.cannotUploadFile.pageNotFound: Datei kann nicht hochgeladen werden, Seite nicht gefunden panel.pages.page.created: Seite erstellt! panel.pages.page.deleted: Seite gelöscht panel.pages.page.edited: Seite bearbeitet -panel.pages.page.fileDeleted: Datei gelöscht -panel.pages.page.fileRenamed: Datei umbenannt panel.pages.page.lastModified: Zuletzt geändert panel.pages.page.moved: Seite verschoben! panel.pages.page.notFound: Seite nicht gefunden diff --git a/panel/translations/en.yaml b/panel/translations/en.yaml index 62e8fd11..e5a3f6ea 100644 --- a/panel/translations/en.yaml +++ b/panel/translations/en.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: The page does not exist or the request panel.errors.error.notFound.heading: Oops, page not found! panel.errors.error.notFound.status: Not found panel.files.actions: Actions +panel.files.cannotDelete.fileNotFound: Cannot delete file, file not found +panel.files.cannotDelete.pageNotFound: Cannot delete file, file not found +panel.files.cannotRename.fileAlreadyExists: Cannot rename file, a file with the same name already exists +panel.files.cannotRename.fileNotFound: Cannot rename file, file not found +panel.files.cannotRename.pageNotFound: Cannot rename file, page not found +panel.files.cannotReplace.fileNotFound: Cannot replace file, file not found +panel.files.cannotReplace.multipleFiles: Cannot replace file, multiple file given +panel.files.cannotReplace.pageNotFound: Cannot replace file, page not found +panel.files.cannotUpload.pageNotFound: Cannot upload file, page not found +panel.files.deleted: File deleted +panel.files.fileNotFound: Cannot get file info, file not found panel.files.metadata.updated: File metadata updated +panel.files.pageNotFound: Cannot get file info, page not found +panel.files.renamed: File renamed panel.files.viewAsList: View as list panel.files.viewAsThumbnails: View as thumbnails panel.login.attempt.failed: Login attempt failed! Try again. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Cannot create page, missing a variable panel.pages.page.cannotDelete.invalidLanguage: 'Cannot delete page, invalid language: %s' panel.pages.page.cannotDelete.notDeletable: Cannot delete page, the page is not deletable panel.pages.page.cannotDelete.pageNotFound: Cannot delete page, page not found -panel.pages.page.cannotDeleteFile.fileNotFound: Cannot delete file, file not found -panel.pages.page.cannotDeleteFile.pageNotFound: Cannot delete file, file not found panel.pages.page.cannotEdit.alreadyExists: Cannot edit page, a page with the same uri already exists panel.pages.page.cannotEdit.indexOrErrorPageSlug: Cannot edit page slug of index and error pages panel.pages.page.cannotEdit.invalidLanguage: 'Cannot edit page, invalid language: %s' @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Cannot edit page slug, it must contain panel.pages.page.cannotEdit.invalidTemplate: Cannot edit page, invalid template panel.pages.page.cannotEdit.pageNotFound: Cannot edit page, page not found panel.pages.page.cannotEdit.varMissing: Cannot edit page, missing a variable -panel.pages.page.cannotGetFileInfo.fileNotFound: Cannot get file info, file not found -panel.pages.page.cannotGetFileInfo.pageNotFound: Cannot get file info, page not found panel.pages.page.cannotMove: Cannot move page panel.pages.page.cannotPreview.pageNotFound: Cannot preview page, page not found panel.pages.page.cannotPreview.parentChanged: Cannot preview page, parent page has been changed -panel.pages.page.cannotRenameFile.fileAlreadyExists: Cannot rename file, a file with the same name already exists -panel.pages.page.cannotRenameFile.fileNotFound: Cannot rename file, file not found -panel.pages.page.cannotRenameFile.pageNotFound: Cannot rename file, page not found -panel.pages.page.cannotReplaceFile.fileNotFound: Cannot replace file, file not found -panel.pages.page.cannotReplaceFile.multipleFiles: Cannot replace file, multiple file given -panel.pages.page.cannotReplaceFile.pageNotFound: Cannot replace file, page not found -panel.pages.page.cannotUploadFile.pageNotFound: Cannot upload file, page not found panel.pages.page.created: Page created! panel.pages.page.deleted: Page deleted panel.pages.page.edited: Page edited -panel.pages.page.fileDeleted: File deleted -panel.pages.page.fileRenamed: File renamed panel.pages.page.lastModified: Last modified panel.pages.page.moved: Page moved! panel.pages.page.notFound: Page not found diff --git a/panel/translations/es.yaml b/panel/translations/es.yaml index 1c8b8fb1..f81669c1 100644 --- a/panel/translations/es.yaml +++ b/panel/translations/es.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: La página no existe o la solicitud no panel.errors.error.notFound.heading: ¡Ups, página no encontrada! panel.errors.error.notFound.status: No encontrado panel.files.actions: Acciones +panel.files.cannotDelete.fileNotFound: No se puede borrar el archivo, archivo no encontrado +panel.files.cannotDelete.pageNotFound: No se puede borrar el archivo, página no encontrada +panel.files.cannotRename.fileAlreadyExists: No se puede renombrar el archivo, ya existe un archivo con el mismo nombre +panel.files.cannotRename.fileNotFound: No se puede renombrar el archivo, archivo no encontrado +panel.files.cannotRename.pageNotFound: No se puede renombrar el archivo, página no encontrada +panel.files.cannotReplace.fileNotFound: No se puede reemplazar el archivo, archivo no encontrado +panel.files.cannotReplace.multipleFiles: No se puede reemplazar el archivo, se proporcionaron varios archivos +panel.files.cannotReplace.pageNotFound: No se puede reemplazar el archivo, página no encontrada +panel.files.cannotUpload.pageNotFound: No se puede subir el archivo, página no encontrada +panel.files.deleted: Archivo eliminado +panel.files.fileNotFound: No se pueden obtener las información del archivo, archivo no encontrado panel.files.metadata.updated: Metadatos del archivo actualizados +panel.files.pageNotFound: No se pueden obtener las información del archivo, página no encontrada +panel.files.renamed: Archivo renombrado panel.files.viewAsList: Ver como lista panel.files.viewAsThumbnails: Ver como miniaturas panel.login.attempt.failed: ¡Intento de inicio de sesión fallido! Intenta de nuevo. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: No se puede crear la página, falta un panel.pages.page.cannotDelete.invalidLanguage: 'No se puede borrar la página, idioma no válido: %s' panel.pages.page.cannotDelete.notDeletable: No se puede borrar la página, la página no es eliminable panel.pages.page.cannotDelete.pageNotFound: No se puede borrar la página, página no encontrada -panel.pages.page.cannotDeleteFile.fileNotFound: No se puede borrar el archivo, archivo no encontrado -panel.pages.page.cannotDeleteFile.pageNotFound: No se puede borrar el archivo, página no encontrada panel.pages.page.cannotEdit.alreadyExists: No se puede editar la página, ya existe una página con la misma URI panel.pages.page.cannotEdit.indexOrErrorPageSlug: No se puede editar el slug de la página de índice y error panel.pages.page.cannotEdit.invalidLanguage: 'No se puede editar la página, idioma no válido: %s' @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: No se puede editar el slug de la págin panel.pages.page.cannotEdit.invalidTemplate: No se puede editar la página, plantilla no válida panel.pages.page.cannotEdit.pageNotFound: No se puede editar la página, página no encontrada panel.pages.page.cannotEdit.varMissing: No se puede editar la página, falta una variable -panel.pages.page.cannotGetFileInfo.fileNotFound: No se pueden obtener las información del archivo, archivo no encontrado -panel.pages.page.cannotGetFileInfo.pageNotFound: No se pueden obtener las información del archivo, página no encontrada panel.pages.page.cannotMove: No se puede mover la página panel.pages.page.cannotPreview.pageNotFound: No se puede previsualizar la página, página no encontrada panel.pages.page.cannotPreview.parentChanged: No se puede previsualizar la página, la página principal ha sido cambiada -panel.pages.page.cannotRenameFile.fileAlreadyExists: No se puede renombrar el archivo, ya existe un archivo con el mismo nombre -panel.pages.page.cannotRenameFile.fileNotFound: No se puede renombrar el archivo, archivo no encontrado -panel.pages.page.cannotRenameFile.pageNotFound: No se puede renombrar el archivo, página no encontrada -panel.pages.page.cannotReplaceFile.fileNotFound: No se puede reemplazar el archivo, archivo no encontrado -panel.pages.page.cannotReplaceFile.multipleFiles: No se puede reemplazar el archivo, se proporcionaron varios archivos -panel.pages.page.cannotReplaceFile.pageNotFound: No se puede reemplazar el archivo, página no encontrada -panel.pages.page.cannotUploadFile.pageNotFound: No se puede subir el archivo, página no encontrada panel.pages.page.created: ¡Página creada! panel.pages.page.deleted: Página eliminada panel.pages.page.edited: Página editada -panel.pages.page.fileDeleted: Archivo eliminado -panel.pages.page.fileRenamed: Archivo renombrado panel.pages.page.lastModified: Última modificación panel.pages.page.moved: ¡Página movida! panel.pages.page.notFound: Página no encontrada diff --git a/panel/translations/fr.yaml b/panel/translations/fr.yaml index 9d87d664..b4919901 100644 --- a/panel/translations/fr.yaml +++ b/panel/translations/fr.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: La page n’existe pas ou la demande n panel.errors.error.notFound.heading: Oups, page non trouvée! panel.errors.error.notFound.status: Pas trouvé panel.files.actions: Actions +panel.files.cannotDelete.fileNotFound: Impossible de supprimer le fichier, fichier introuvable +panel.files.cannotDelete.pageNotFound: Impossible de supprimer le fichier, page non trouvée +panel.files.cannotRename.fileAlreadyExists: Impossible de renommer le fichier, un fichier portant le même nom existe déjà +panel.files.cannotRename.fileNotFound: Impossible de renommer le fichier, fichier introuvable +panel.files.cannotRename.pageNotFound: Impossible de renommer le fichier, page introuvable +panel.files.cannotReplace.fileNotFound: Impossible de remplacer le fichier, fichier non trouvé +panel.files.cannotReplace.multipleFiles: Impossible de remplacer le fichier, plusieurs fichiers spécifiés +panel.files.cannotReplace.pageNotFound: Impossible de remplacer le fichier, page non trouvée +panel.files.cannotUpload.pageNotFound: Impossible de téléverser le fichier, page introuvable +panel.files.deleted: Fichier supprimé +panel.files.fileNotFound: Impossible de récupérer les informations sur le fichier, fichier non trouvé panel.files.metadata.updated: Métadonnées du fichier mises à jour +panel.files.pageNotFound: Impossible de récupérer les informations sur le fichier, page non trouvée +panel.files.renamed: Fichier renommé panel.files.viewAsList: Afficher en liste panel.files.viewAsThumbnails: Afficher en vignettes panel.login.attempt.failed: La tentative de connexion a échoué! Réessayer. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Impossible de créer une page, il manq panel.pages.page.cannotDelete.invalidLanguage: 'Impossible de supprimer la page, terme non défini : %s' panel.pages.page.cannotDelete.notDeletable: Impossible de supprimer la page, la page ne peut être supprimée panel.pages.page.cannotDelete.pageNotFound: Impossible de supprimer la page, page introuvable -panel.pages.page.cannotDeleteFile.fileNotFound: Impossible de supprimer le fichier, fichier introuvable -panel.pages.page.cannotDeleteFile.pageNotFound: Impossible de supprimer le fichier, page non trouvée panel.pages.page.cannotEdit.alreadyExists: Impossible de modifier la page, une page avec la même url existe déjà panel.pages.page.cannotEdit.indexOrErrorPageSlug: Impossible de modifier l’identifiant de la page d’acceuil et celle de la page d’erreur panel.pages.page.cannotEdit.invalidLanguage: 'Impossible de modifier la page, terme non défini : %s' @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Impossible de modifier l’identifiant panel.pages.page.cannotEdit.invalidTemplate: Impossible de modifier la page, modèle non valide panel.pages.page.cannotEdit.pageNotFound: Impossible de modifier la page, page non trouvée panel.pages.page.cannotEdit.varMissing: Impossible de modifier la page, il manque une variable -panel.pages.page.cannotGetFileInfo.fileNotFound: Impossible de récupérer les informations sur le fichier, fichier non trouvé -panel.pages.page.cannotGetFileInfo.pageNotFound: Impossible de récupérer les informations sur le fichier, page non trouvée panel.pages.page.cannotMove: Impossible de déplacer la page panel.pages.page.cannotPreview.pageNotFound: Impossible de prévisualiser la page, page non trouvée panel.pages.page.cannotPreview.parentChanged: Impossible de prévisualiser la page, la page parente a été modifiée -panel.pages.page.cannotRenameFile.fileAlreadyExists: Impossible de renommer le fichier, un fichier portant le même nom existe déjà -panel.pages.page.cannotRenameFile.fileNotFound: Impossible de renommer le fichier, fichier introuvable -panel.pages.page.cannotRenameFile.pageNotFound: Impossible de renommer le fichier, page introuvable -panel.pages.page.cannotReplaceFile.fileNotFound: Impossible de remplacer le fichier, fichier non trouvé -panel.pages.page.cannotReplaceFile.multipleFiles: Impossible de remplacer le fichier, plusieurs fichiers spécifiés -panel.pages.page.cannotReplaceFile.pageNotFound: Impossible de remplacer le fichier, page non trouvée -panel.pages.page.cannotUploadFile.pageNotFound: Impossible de téléverser le fichier, page introuvable panel.pages.page.created: Page créée ! panel.pages.page.deleted: Page supprimée panel.pages.page.edited: Page éditée -panel.pages.page.fileDeleted: Fichier supprimé -panel.pages.page.fileRenamed: Fichier renommé panel.pages.page.lastModified: Dernière modification panel.pages.page.moved: Page déplacée ! panel.pages.page.notFound: Page non trouvée diff --git a/panel/translations/it.yaml b/panel/translations/it.yaml index 57b5510d..3043d687 100644 --- a/panel/translations/it.yaml +++ b/panel/translations/it.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: La pagina non esiste o la richiesta non panel.errors.error.notFound.heading: Oops, pagina non trovata! panel.errors.error.notFound.status: Non trovato panel.files.actions: Azioni +panel.files.cannotDelete.fileNotFound: Impossibile eliminare il file, file non trovato +panel.files.cannotDelete.pageNotFound: Impossibile eliminare il file, pagina non trovata +panel.files.cannotRename.fileAlreadyExists: Impossibile rinominare il file, un file con lo stesso nome esiste già +panel.files.cannotRename.fileNotFound: Impossibile rinominare il file, file non trovato +panel.files.cannotRename.pageNotFound: Impossibile rinominare il file, pagina non trovata +panel.files.cannotReplace.fileNotFound: Impossibile sostituire il file, file non trovato +panel.files.cannotReplace.multipleFiles: Impossibile sostituire il file, forniti più file +panel.files.cannotReplace.pageNotFound: Impossibile sostituire il file, pagina non trovata +panel.files.cannotUpload.pageNotFound: Impossibile caricare il file, pagina non trovata +panel.files.deleted: File eliminato +panel.files.fileNotFound: Impossibile ottenere le informazioni sul file, file non trovato panel.files.metadata.updated: Metadati del file aggiornati +panel.files.pageNotFound: Impossibile ottenere le informazioni sul file, pagina non trovata +panel.files.renamed: File rinominato panel.files.viewAsList: Visualizza come lista panel.files.viewAsThumbnails: Visualizza come miniature panel.login.attempt.failed: Tentativo di accesso fallito! Riprova. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Impossibile creare la pagina, manca un panel.pages.page.cannotDelete.invalidLanguage: Impossibile eliminare la pagina, lingua %s non valida panel.pages.page.cannotDelete.notDeletable: Impossibile eliminare la pagina, la pagina non è eliminabile panel.pages.page.cannotDelete.pageNotFound: Impossibile eliminare la pagina, pagina non trovata -panel.pages.page.cannotDeleteFile.fileNotFound: Impossibile eliminare il file, file non trovato -panel.pages.page.cannotDeleteFile.pageNotFound: Impossibile eliminare il file, pagina non trovata panel.pages.page.cannotEdit.alreadyExists: Impossibile modificare la pagina, una pagina con lo stesso indirizzo esiste già panel.pages.page.cannotEdit.indexOrErrorPageSlug: Impossibile cambiare l’indirizzo della pagina iniziale o di errore panel.pages.page.cannotEdit.invalidLanguage: Impossibile modificare la pagina, lingua %s non valida @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Impossibile cambiare l’indirizzo, l panel.pages.page.cannotEdit.invalidTemplate: Impossibile modificare la pagina, template non valido panel.pages.page.cannotEdit.pageNotFound: Impossibile modificare la pagina, pagina non trovata panel.pages.page.cannotEdit.varMissing: Impossibile modificare la pagina, manca una variabile -panel.pages.page.cannotGetFileInfo.fileNotFound: Impossibile ottenere le informazioni sul file, file non trovato -panel.pages.page.cannotGetFileInfo.pageNotFound: Impossibile ottenere le informazioni sul file, pagina non trovata panel.pages.page.cannotMove: Impossibile spostare la pagina panel.pages.page.cannotPreview.pageNotFound: Impossibile visualizzare l’anteprima, pagina non trovata panel.pages.page.cannotPreview.parentChanged: Impossible visualizzare l’anteprima, la pagina superiore è stata modificata -panel.pages.page.cannotRenameFile.fileAlreadyExists: Impossibile rinominare il file, un file con lo stesso nome esiste già -panel.pages.page.cannotRenameFile.fileNotFound: Impossibile rinominare il file, file non trovato -panel.pages.page.cannotRenameFile.pageNotFound: Impossibile rinominare il file, pagina non trovata -panel.pages.page.cannotReplaceFile.fileNotFound: Impossibile sostituire il file, file non trovato -panel.pages.page.cannotReplaceFile.multipleFiles: Impossibile sostituire il file, forniti più file -panel.pages.page.cannotReplaceFile.pageNotFound: Impossibile sostituire il file, pagina non trovata -panel.pages.page.cannotUploadFile.pageNotFound: Impossibile caricare il file, pagina non trovata panel.pages.page.created: Pagina creata! panel.pages.page.deleted: Pagina eliminata panel.pages.page.edited: Pagina modificata! -panel.pages.page.fileDeleted: File eliminato -panel.pages.page.fileRenamed: File rinominato panel.pages.page.lastModified: Ultime modifiche panel.pages.page.moved: Pagina spostata! panel.pages.page.notFound: Pagina non trovata diff --git a/panel/translations/pl.yaml b/panel/translations/pl.yaml index e8ca394a..1e3491f7 100644 --- a/panel/translations/pl.yaml +++ b/panel/translations/pl.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: Strona nie istnieje lub żądanie jest panel.errors.error.notFound.heading: Ups, nie znaleziono strony! panel.errors.error.notFound.status: Nie znaleziono panel.files.actions: Akcje +panel.files.cannotDelete.fileNotFound: Nie można usunąć pliku, plik nie został znaleziony +panel.files.cannotDelete.pageNotFound: Nie można usunąć pliku, strona nie została znaleziona +panel.files.cannotRename.fileAlreadyExists: Nie można zmienić nazwy pliku, plik o tej samej nazwie już istnieje +panel.files.cannotRename.fileNotFound: Nie można zmienić nazwy pliku, plik nie został znaleziony +panel.files.cannotRename.pageNotFound: Nie można zmienić nazwy pliku, strona nie została znaleziona +panel.files.cannotReplace.fileNotFound: Nie można zamienić pliku, plik nie został znaleziony +panel.files.cannotReplace.multipleFiles: Nie można zamienić pliku, podano wiele plików +panel.files.cannotReplace.pageNotFound: Nie można zamienić pliku, strona nie została znaleziona +panel.files.cannotUpload.pageNotFound: Nie można przesłać pliku, strona nie została znaleziona +panel.files.deleted: Plik usunięty +panel.files.fileNotFound: Nie można pobrać informacji o pliku, plik nie został znaleziony panel.files.metadata.updated: Metadane pliku zaktualizowane +panel.files.pageNotFound: Nie można pobrać informacji o pliku, strona nie została znaleziona +panel.files.renamed: Plik zmieniony panel.files.viewAsList: Wyświetl jako lista panel.files.viewAsThumbnails: Wyświetl jako miniatury panel.login.attempt.failed: Próba logowania nieudana! Spróbuj ponownie. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Nie można utworzyć strony, brak zmie panel.pages.page.cannotDelete.invalidLanguage: 'Nie można usunąć strony, nieprawidłowy język: %s' panel.pages.page.cannotDelete.notDeletable: Nie można usunąć strony, strona nie jest do usunięcia panel.pages.page.cannotDelete.pageNotFound: Nie można usunąć strony, strona nie została znaleziona -panel.pages.page.cannotDeleteFile.fileNotFound: Nie można usunąć pliku, plik nie został znaleziony -panel.pages.page.cannotDeleteFile.pageNotFound: Nie można usunąć pliku, strona nie została znaleziona panel.pages.page.cannotEdit.alreadyExists: Nie można edytować strony, strona o tym samym URI już istnieje panel.pages.page.cannotEdit.indexOrErrorPageSlug: Nie można edytować slug strony dla strony indeksu lub strony błędu panel.pages.page.cannotEdit.invalidLanguage: 'Nie można edytować strony, nieprawidłowy język: %s' @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Nie można edytować slug strony, musi panel.pages.page.cannotEdit.invalidTemplate: Nie można edytować strony, nieprawidłowy szablon panel.pages.page.cannotEdit.pageNotFound: Nie można edytować strony, strona nie została znaleziona panel.pages.page.cannotEdit.varMissing: Nie można edytować strony, brak zmiennej -panel.pages.page.cannotGetFileInfo.fileNotFound: Nie można pobrać informacji o pliku, plik nie został znaleziony -panel.pages.page.cannotGetFileInfo.pageNotFound: Nie można pobrać informacji o pliku, strona nie została znaleziona panel.pages.page.cannotMove: Nie można przenieść strony panel.pages.page.cannotPreview.pageNotFound: Nie można wyświetlić podglądu strony, strona nie została znaleziona panel.pages.page.cannotPreview.parentChanged: Nie można wyświetlić podglądu strony, strona nadrzędna została zmieniona -panel.pages.page.cannotRenameFile.fileAlreadyExists: Nie można zmienić nazwy pliku, plik o tej samej nazwie już istnieje -panel.pages.page.cannotRenameFile.fileNotFound: Nie można zmienić nazwy pliku, plik nie został znaleziony -panel.pages.page.cannotRenameFile.pageNotFound: Nie można zmienić nazwy pliku, strona nie została znaleziona -panel.pages.page.cannotReplaceFile.fileNotFound: Nie można zamienić pliku, plik nie został znaleziony -panel.pages.page.cannotReplaceFile.multipleFiles: Nie można zamienić pliku, podano wiele plików -panel.pages.page.cannotReplaceFile.pageNotFound: Nie można zamienić pliku, strona nie została znaleziona -panel.pages.page.cannotUploadFile.pageNotFound: Nie można przesłać pliku, strona nie została znaleziona panel.pages.page.created: Strona utworzona! panel.pages.page.deleted: Strona usunięta panel.pages.page.edited: Strona edytowana -panel.pages.page.fileDeleted: Plik usunięty -panel.pages.page.fileRenamed: Plik zmieniony panel.pages.page.lastModified: Ostatnia modyfikacja panel.pages.page.moved: Strona przeniesiona! panel.pages.page.notFound: Strona nie została znaleziona diff --git a/panel/translations/pt.yaml b/panel/translations/pt.yaml index 469fa322..72632fd4 100644 --- a/panel/translations/pt.yaml +++ b/panel/translations/pt.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: A página não existe ou a solicitaçã panel.errors.error.notFound.heading: Ops, página não encontrada! panel.errors.error.notFound.status: Não encontrado panel.files.actions: Ações +panel.files.cannotDelete.fileNotFound: Não é possível excluir o ficheiro, ficheiro não encontrado +panel.files.cannotDelete.pageNotFound: Não é possível excluir a página, página não encontrado +panel.files.cannotRename.fileAlreadyExists: Não é possível renomear o arquivo, um arquivo com o mesmo nome já existe +panel.files.cannotRename.fileNotFound: Não é possível renomear o arquivo, arquivo não encontrado +panel.files.cannotRename.pageNotFound: Não é possível renomear o arquivo, página não encontrada +panel.files.cannotReplace.fileNotFound: Não é possível substituir o ficheiro, ficheiro não encontrado +panel.files.cannotReplace.multipleFiles: Não é possível substituir o ficheiro, vários ficheiros fornecidos +panel.files.cannotReplace.pageNotFound: Não é possível substituir o ficheiro, página não encontrada +panel.files.cannotUpload.pageNotFound: Não é possível efectuar envio do ficheiro, página não encontrada +panel.files.deleted: Ficheiro removido +panel.files.fileNotFound: Não é possível obter informações do arquivo, arquivo não encontrado panel.files.metadata.updated: Metadados do ficheiro atualizados +panel.files.pageNotFound: Não é possível obter informações do arquivo, página não encontrada +panel.files.renamed: Arquivo renomeado panel.files.viewAsList: Ver como lista panel.files.viewAsThumbnails: Ver como miniaturas panel.login.attempt.failed: Falha ao tentar efetuar login! Tente novamente. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Não é possível criar página, falta panel.pages.page.cannotDelete.invalidLanguage: 'Não é possível excluir página, idioma inválido: %s' panel.pages.page.cannotDelete.notDeletable: Não é possível excluir a página, a página não pode ser excluída panel.pages.page.cannotDelete.pageNotFound: Não é possível excluir página, página não encontrada -panel.pages.page.cannotDeleteFile.fileNotFound: Não é possível excluir o ficheiro, ficheiro não encontrado -panel.pages.page.cannotDeleteFile.pageNotFound: Não é possível excluir a página, página não encontrado panel.pages.page.cannotEdit.alreadyExists: Não é possível editar a página, já existe uma página com o mesmo uri panel.pages.page.cannotEdit.indexOrErrorPageSlug: Não é possível editar slug de página de índice e páginas de erro panel.pages.page.cannotEdit.invalidLanguage: 'Não é possível editar a página, idioma inválido: %s' @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Não é possível editar o slug da pág panel.pages.page.cannotEdit.invalidTemplate: Não é possível editar a página, template inválido panel.pages.page.cannotEdit.pageNotFound: Não é possível editar a página, página não encontrada panel.pages.page.cannotEdit.varMissing: Não é possível editar a página, falta uma variável -panel.pages.page.cannotGetFileInfo.fileNotFound: Não é possível obter informações do arquivo, arquivo não encontrado -panel.pages.page.cannotGetFileInfo.pageNotFound: Não é possível obter informações do arquivo, página não encontrada panel.pages.page.cannotMove: Não é possível mover a página panel.pages.page.cannotPreview.pageNotFound: Não é possível pré-visualizar a página, página não encontrada panel.pages.page.cannotPreview.parentChanged: Não é possível pré-visualizar a página, a página mãe foi alterada -panel.pages.page.cannotRenameFile.fileAlreadyExists: Não é possível renomear o arquivo, um arquivo com o mesmo nome já existe -panel.pages.page.cannotRenameFile.fileNotFound: Não é possível renomear o arquivo, arquivo não encontrado -panel.pages.page.cannotRenameFile.pageNotFound: Não é possível renomear o arquivo, página não encontrada -panel.pages.page.cannotReplaceFile.fileNotFound: Não é possível substituir o ficheiro, ficheiro não encontrado -panel.pages.page.cannotReplaceFile.multipleFiles: Não é possível substituir o ficheiro, vários ficheiros fornecidos -panel.pages.page.cannotReplaceFile.pageNotFound: Não é possível substituir o ficheiro, página não encontrada -panel.pages.page.cannotUploadFile.pageNotFound: Não é possível efectuar envio do ficheiro, página não encontrada panel.pages.page.created: Página criada! panel.pages.page.deleted: Página removida panel.pages.page.edited: Página editada -panel.pages.page.fileDeleted: Ficheiro removido -panel.pages.page.fileRenamed: Arquivo renomeado panel.pages.page.lastModified: Última modificação panel.pages.page.moved: Página movida! panel.pages.page.notFound: Página não encontrada diff --git a/panel/translations/ru.yaml b/panel/translations/ru.yaml index c572a3b2..841c050f 100644 --- a/panel/translations/ru.yaml +++ b/panel/translations/ru.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: Страница не существу panel.errors.error.notFound.heading: К сожалению, страница не найдена! panel.errors.error.notFound.status: Не обнаружена panel.files.actions: Действия +panel.files.cannotDelete.fileNotFound: Не удается удалить файл, файл не найден +panel.files.cannotDelete.pageNotFound: Не удается удалить файл, файл не найден +panel.files.cannotRename.fileAlreadyExists: Невозможно переименовать файл, файл с таким именем уже существует +panel.files.cannotRename.fileNotFound: Невозможно переименовать файл, файл не найден +panel.files.cannotRename.pageNotFound: Невозможно переименовать файл, страница не найдена +panel.files.cannotReplace.fileNotFound: Невозможно заменить файл, файл не найден +panel.files.cannotReplace.multipleFiles: Невозможно заменить файл, указаны несколько файлов +panel.files.cannotReplace.pageNotFound: Невозможно заменить файл, страница не найдена +panel.files.cannotUpload.pageNotFound: Невозможно загрузить файл, страница не найдена +panel.files.deleted: Файл удален +panel.files.fileNotFound: Невозможно получить информацию о файле, файл не найден panel.files.metadata.updated: Метаданные файла обновлены +panel.files.pageNotFound: Невозможно получить информацию о файле, страница не найдена +panel.files.renamed: Файл переименован panel.files.viewAsList: Просмотр в виде списка panel.files.viewAsThumbnails: Просмотр в виде миниатюр panel.login.attempt.failed: Войти попытка не удалась! Попробуйте еще раз. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Не удается создать с panel.pages.page.cannotDelete.invalidLanguage: 'Невозможно удалить страницу, недопустимый язык: %s' panel.pages.page.cannotDelete.notDeletable: Невозможно удалить страницу, страница не файл удаляемая panel.pages.page.cannotDelete.pageNotFound: Невозможно удалить страницу, страница не найдена -panel.pages.page.cannotDeleteFile.fileNotFound: Не удается удалить файл, файл не найден -panel.pages.page.cannotDeleteFile.pageNotFound: Не удается удалить файл, файл не найден panel.pages.page.cannotEdit.alreadyExists: Не могу редактировать страницу, страница с той же URI уже существует panel.pages.page.cannotEdit.indexOrErrorPageSlug: Не могу редактировать страницу слизняка индекса и ошибок страниц panel.pages.page.cannotEdit.invalidLanguage: 'Не могу редактировать страницы, недопустимый язык: %s' @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Не можете редактиров panel.pages.page.cannotEdit.invalidTemplate: Не могу редактировать страницы, неверный шаблон panel.pages.page.cannotEdit.pageNotFound: Не могу редактировать страницу, страница не найдена panel.pages.page.cannotEdit.varMissing: Не могу редактировать страницу, отсутствует переменный -panel.pages.page.cannotGetFileInfo.fileNotFound: Невозможно получить информацию о файле, файл не найден -panel.pages.page.cannotGetFileInfo.pageNotFound: Невозможно получить информацию о файле, страница не найдена panel.pages.page.cannotMove: Невозможно переместить страницу panel.pages.page.cannotPreview.pageNotFound: Невозможно показать предварительный просмотр, страница не найдена panel.pages.page.cannotPreview.parentChanged: Невозможно показать предварительный просмотр, родительская страница изменена -panel.pages.page.cannotRenameFile.fileAlreadyExists: Невозможно переименовать файл, файл с таким именем уже существует -panel.pages.page.cannotRenameFile.fileNotFound: Невозможно переименовать файл, файл не найден -panel.pages.page.cannotRenameFile.pageNotFound: Невозможно переименовать файл, страница не найдена -panel.pages.page.cannotReplaceFile.fileNotFound: Невозможно заменить файл, файл не найден -panel.pages.page.cannotReplaceFile.multipleFiles: Невозможно заменить файл, указаны несколько файлов -panel.pages.page.cannotReplaceFile.pageNotFound: Невозможно заменить файл, страница не найдена -panel.pages.page.cannotUploadFile.pageNotFound: Невозможно загрузить файл, страница не найдена panel.pages.page.created: Страница сгенерирована! panel.pages.page.deleted: Страница удалена panel.pages.page.edited: Страница отредактирован -panel.pages.page.fileDeleted: Файл удален -panel.pages.page.fileRenamed: Файл переименован panel.pages.page.lastModified: Последнее изменение panel.pages.page.moved: Страница переехала! panel.pages.page.notFound: Страница не найдена diff --git a/panel/translations/uk.yaml b/panel/translations/uk.yaml index aa44e1ae..a4859a25 100644 --- a/panel/translations/uk.yaml +++ b/panel/translations/uk.yaml @@ -54,7 +54,20 @@ panel.errors.error.notFound.description: Сторінка не існує або panel.errors.error.notFound.heading: Ой, сторінка не знайдена! panel.errors.error.notFound.status: Не знайдено panel.files.actions: Дії +panel.files.cannotDelete.fileNotFound: Неможливо видалити файл, файл не знайдено +panel.files.cannotDelete.pageNotFound: Неможливо видалити файл, сторінка не знайдена +panel.files.cannotRename.fileAlreadyExists: Неможливо перейменувати файл, файл з таким іменем вже існує +panel.files.cannotRename.fileNotFound: Неможливо перейменувати файл, файл не знайдено +panel.files.cannotRename.pageNotFound: Неможливо перейменувати файл, сторінка не знайдена +panel.files.cannotReplace.fileNotFound: Неможливо замінити файл, файл не знайдено +panel.files.cannotReplace.multipleFiles: Неможливо замінити файл, надано кілька файлів +panel.files.cannotReplace.pageNotFound: Неможливо замінити файл, сторінка не знайдена +panel.files.cannotUpload.pageNotFound: Неможливо завантажити файл, сторінка не знайдена +panel.files.deleted: Файл видалено +panel.files.fileNotFound: Неможливо отримати інформацію про файл, файл не знайдено panel.files.metadata.updated: Метадані файлу оновлено +panel.files.pageNotFound: Неможливо отримати інформацію про файл, сторінка не знайдена +panel.files.renamed: Файл перейменовано panel.files.viewAsList: Переглядати як список panel.files.viewAsThumbnails: Переглядати як ескізи panel.login.attempt.failed: Не вдалося увійти! Спробуйте ще раз. @@ -201,8 +214,6 @@ panel.pages.page.cannotCreate.varMissing: Неможливо створити с panel.pages.page.cannotDelete.invalidLanguage: 'Неможливо видалити сторінку, недійсна мова: %s' panel.pages.page.cannotDelete.notDeletable: Неможливо видалити сторінку, сторінка не може бути видалена panel.pages.page.cannotDelete.pageNotFound: Неможливо видалити сторінку, сторінка не знайдена -panel.pages.page.cannotDeleteFile.fileNotFound: Неможливо видалити файл, файл не знайдено -panel.pages.page.cannotDeleteFile.pageNotFound: Неможливо видалити файл, сторінка не знайдена panel.pages.page.cannotEdit.alreadyExists: Неможливо редагувати сторінку, сторінка з таким URI вже існує panel.pages.page.cannotEdit.indexOrErrorPageSlug: Неможливо редагувати slug сторінки індексу та помилок panel.pages.page.cannotEdit.invalidLanguage: 'Неможливо редагувати сторінку, недійсна мова: %s' @@ -211,23 +222,12 @@ panel.pages.page.cannotEdit.invalidSlug: Неможливо редагувати panel.pages.page.cannotEdit.invalidTemplate: Неможливо редагувати сторінку, недійсна шаблон panel.pages.page.cannotEdit.pageNotFound: Неможливо редагувати сторінку, сторінка не знайдена panel.pages.page.cannotEdit.varMissing: Неможливо редагувати сторінку, відсутня змінна -panel.pages.page.cannotGetFileInfo.fileNotFound: Неможливо отримати інформацію про файл, файл не знайдено -panel.pages.page.cannotGetFileInfo.pageNotFound: Неможливо отримати інформацію про файл, сторінка не знайдена panel.pages.page.cannotMove: Неможливо перемістити сторінку panel.pages.page.cannotPreview.pageNotFound: Неможливо попередньо переглянути сторінку, сторінка не знайдена panel.pages.page.cannotPreview.parentChanged: Неможливо попередньо переглянути сторінку, батьківська сторінка змінилася -panel.pages.page.cannotRenameFile.fileAlreadyExists: Неможливо перейменувати файл, файл з таким іменем вже існує -panel.pages.page.cannotRenameFile.fileNotFound: Неможливо перейменувати файл, файл не знайдено -panel.pages.page.cannotRenameFile.pageNotFound: Неможливо перейменувати файл, сторінка не знайдена -panel.pages.page.cannotReplaceFile.fileNotFound: Неможливо замінити файл, файл не знайдено -panel.pages.page.cannotReplaceFile.multipleFiles: Неможливо замінити файл, надано кілька файлів -panel.pages.page.cannotReplaceFile.pageNotFound: Неможливо замінити файл, сторінка не знайдена -panel.pages.page.cannotUploadFile.pageNotFound: Неможливо завантажити файл, сторінка не знайдена panel.pages.page.created: Сторінка створена! panel.pages.page.deleted: Сторінка видалена panel.pages.page.edited: Сторінка відредагована -panel.pages.page.fileDeleted: Файл видалено -panel.pages.page.fileRenamed: Файл перейменовано panel.pages.page.lastModified: Останнє редагування panel.pages.page.moved: Сторінка переміщена! panel.pages.page.notFound: Сторінка не знайдена diff --git a/panel/views/fields/partials/filelist.php b/panel/views/fields/partials/filelist.php index b0c2b19c..3a628ae8 100644 --- a/panel/views/fields/partials/filelist.php +++ b/panel/views/fields/partials/filelist.php @@ -1,8 +1,8 @@ modals()->addMultiple(['deleteFile', 'renameFile']) ?> -
+
-
+
@@ -10,33 +10,31 @@
-
+
type() === 'image') : ?>
type() === 'video') : ?> - +
icon(is_null($file->type()) ? 'file' : 'file-' . $file->type()) ?>
-
name() ?> (size() ?>)
+
name() ?> (size() ?>)
+ + diff --git a/panel/views/fields/upload.php b/panel/views/fields/upload.php index cc635ef4..dde2a7a9 100644 --- a/panel/views/fields/upload.php +++ b/panel/views/fields/upload.php @@ -2,22 +2,22 @@ insert('fields.partials.filelist', ['model' => $model, 'files' => $field->collection()]); ?> has('label')) : ?> -
escape($this->append($field->label(), ':')) ?>
+ has('suggestion')) : ?>(escape($field->get('suggestion')) ?>) -