Allow file metadata editing from panel

This commit is contained in:
Giuseppe Criscione 2024-10-13 12:42:06 +02:00
parent 57c7798976
commit ec9bc5c40c
10 changed files with 146 additions and 78 deletions

View File

@ -5,6 +5,7 @@ namespace Formwork\Panel\Controllers;
use Formwork\Exceptions\TranslatedException; use Formwork\Exceptions\TranslatedException;
use Formwork\Fields\Exceptions\ValidationException; use Formwork\Fields\Exceptions\ValidationException;
use Formwork\Fields\FieldCollection; use Formwork\Fields\FieldCollection;
use Formwork\Files\File;
use Formwork\Files\FileUploader; use Formwork\Files\FileUploader;
use Formwork\Http\Files\UploadedFile; use Formwork\Http\Files\UploadedFile;
use Formwork\Http\JsonResponse; use Formwork\Http\JsonResponse;
@ -509,10 +510,32 @@ class PagesController extends AbstractController
$files = $page->files(); $files = $page->files();
$file = $files->get($filename); $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->panel()->notify($this->translate('panel.files.metadata.updated'), 'success');
return $this->redirect($this->generateRoute('panel.pages.file', ['page' => $page->route(), 'filename' => $filename]));
}
$fileIndex = $files->indexOf($file); $fileIndex = $files->indexOf($file);
$this->modal('renameFile'); $this->modal('renameFile');
$this->modal('deleteFile'); $this->modal('deleteFile');
$this->modal('changes');
return new Response($this->view('pages.file', [ return new Response($this->view('pages.file', [
'title' => $file->name(), 'title' => $file->name(),
@ -586,6 +609,33 @@ class PagesController extends AbstractController
return $this->site()->retrievePage($path); return $this->site()->retrievePage($path);
} }
protected 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));
}
/** /**
* Update a page * Update a page
*/ */

View File

@ -163,7 +163,7 @@ return [
'panel.pages.file' => [ 'panel.pages.file' => [
'path' => '/pages/{page}/file/{filename}/', 'path' => '/pages/{page}/file/{filename}/',
'action' => 'Formwork\Panel\Controllers\PagesController@file', 'action' => 'Formwork\Panel\Controllers\PagesController@file',
'methods' => ['GET'], 'methods' => ['GET', 'POST'],
], ],
'panel.pages.delete' => [ 'panel.pages.delete' => [

View File

@ -35,6 +35,7 @@ panel.errors.error.notFound.status: Nicht gefunden
panel.files.actions: Aktionen panel.files.actions: Aktionen
panel.files.metadata: Metadaten panel.files.metadata: Metadaten
panel.files.metadata.alternativeText: Alternativtext panel.files.metadata.alternativeText: Alternativtext
panel.files.metadata.updated: Dateimetadaten aktualisiert
panel.files.viewAsList: Als Liste anzeigen panel.files.viewAsList: Als Liste anzeigen
panel.files.viewAsThumbnails: Als Miniaturansichten anzeigen panel.files.viewAsThumbnails: Als Miniaturansichten anzeigen
panel.login.attempt.failed: Anmeldeversuch fehlgeschlagen! Versuchen Sie es erneut. panel.login.attempt.failed: Anmeldeversuch fehlgeschlagen! Versuchen Sie es erneut.

View File

@ -35,6 +35,7 @@ panel.errors.error.notFound.status: Not found
panel.files.actions: Actions panel.files.actions: Actions
panel.files.metadata: Metadata panel.files.metadata: Metadata
panel.files.metadata.alternativeText: Alternative text panel.files.metadata.alternativeText: Alternative text
panel.files.metadata.updated: File metadata updated
panel.files.viewAsList: View as list panel.files.viewAsList: View as list
panel.files.viewAsThumbnails: View as thumbnails panel.files.viewAsThumbnails: View as thumbnails
panel.login.attempt.failed: Login attempt failed! Try again. panel.login.attempt.failed: Login attempt failed! Try again.

View File

@ -35,6 +35,7 @@ panel.errors.error.notFound.status: No encontrado
panel.files.actions: Acciones panel.files.actions: Acciones
panel.files.metadata: Metadatos panel.files.metadata: Metadatos
panel.files.metadata.alternativeText: Texto alternativo panel.files.metadata.alternativeText: Texto alternativo
panel.files.metadata.updated: Metadatos del archivo actualizados
panel.files.viewAsList: Ver como lista panel.files.viewAsList: Ver como lista
panel.files.viewAsThumbnails: Ver como miniaturas panel.files.viewAsThumbnails: Ver como miniaturas
panel.login.attempt.failed: ¡Intento de inicio de sesión fallido! Intenta de nuevo. panel.login.attempt.failed: ¡Intento de inicio de sesión fallido! Intenta de nuevo.

View File

@ -35,6 +35,7 @@ panel.errors.error.notFound.status: Pas trouvé
panel.files.actions: Actions panel.files.actions: Actions
panel.files.metadata: Métadonnées panel.files.metadata: Métadonnées
panel.files.metadata.alternativeText: Texte alternatif panel.files.metadata.alternativeText: Texte alternatif
panel.files.metadata.updated: Métadonnées du fichier mises à jour
panel.files.viewAsList: Afficher en liste panel.files.viewAsList: Afficher en liste
panel.files.viewAsThumbnails: Afficher en vignettes panel.files.viewAsThumbnails: Afficher en vignettes
panel.login.attempt.failed: La tentative de connexion a échoué! Réessayer. panel.login.attempt.failed: La tentative de connexion a échoué! Réessayer.

View File

@ -35,6 +35,7 @@ panel.errors.error.notFound.status: Non trovato
panel.files.actions: Azioni panel.files.actions: Azioni
panel.files.metadata: Metadati panel.files.metadata: Metadati
panel.files.metadata.alternativeText: Testo alternativo panel.files.metadata.alternativeText: Testo alternativo
panel.files.metadata.updated: Metadati del file aggiornati
panel.files.viewAsList: Visualizza come lista panel.files.viewAsList: Visualizza come lista
panel.files.viewAsThumbnails: Visualizza come miniature panel.files.viewAsThumbnails: Visualizza come miniature
panel.login.attempt.failed: Tentativo di accesso fallito! Riprova. panel.login.attempt.failed: Tentativo di accesso fallito! Riprova.

View File

@ -35,6 +35,7 @@ panel.errors.error.notFound.status: Não encontrado
panel.files.actions: Ações panel.files.actions: Ações
panel.files.metadata: Metadados panel.files.metadata: Metadados
panel.files.metadata.alternativeText: Texto alternativo panel.files.metadata.alternativeText: Texto alternativo
panel.files.metadata.updated: Metadados do ficheiro atualizados
panel.files.viewAsList: Ver como lista panel.files.viewAsList: Ver como lista
panel.files.viewAsThumbnails: Ver como miniaturas panel.files.viewAsThumbnails: Ver como miniaturas
panel.login.attempt.failed: Falha ao tentar efetuar login! Tente novamente. panel.login.attempt.failed: Falha ao tentar efetuar login! Tente novamente.

View File

@ -35,6 +35,7 @@ panel.errors.error.notFound.status: Не обнаружена
panel.files.actions: Действия panel.files.actions: Действия
panel.files.metadata: Метаданные panel.files.metadata: Метаданные
panel.files.metadata.alternativeText: Альтернативный текст panel.files.metadata.alternativeText: Альтернативный текст
panel.files.metadata.updated: Метаданные файла обновлены
panel.files.viewAsList: Просмотр в виде списка panel.files.viewAsList: Просмотр в виде списка
panel.files.viewAsThumbnails: Просмотр в виде миниатюр panel.files.viewAsThumbnails: Просмотр в виде миниатюр
panel.login.attempt.failed: Войти попытка не удалась! Попробуйте еще раз. panel.login.attempt.failed: Войти попытка не удалась! Попробуйте еще раз.

View File

@ -1,94 +1,105 @@
<?php $this->layout('panel') ?> <?php $this->layout('panel') ?>
<div class="header"> <form method="post" enctype="multipart/form-data" data-form="page-file-form">
<div class="min-w-0 flex-grow-1"> <div class="header">
<div class="header-title"><?= $this->icon(is_null($file->type()) ? 'file' : 'file-' . $file->type()) ?> <?= $file->name() ?></div> <div class="min-w-0 flex-grow-1">
<div><a class="link-secondary text-size-sm" href="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/edit/') ?>"><span class="mr-2"><?= $this->icon('arrow-left-circle') ?></span><?= $this->translate('panel.pages.file.backToPage') ?></a></div> <div class="header-title"><?= $this->icon(is_null($file->type()) ? 'file' : 'file-' . $file->type()) ?> <?= $file->name() ?></div>
<div><a class="link-secondary text-size-sm" href="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/edit/') ?>"><span class="mr-2"><?= $this->icon('arrow-left-circle') ?></span><?= $this->translate('panel.pages.file.backToPage') ?></a></div>
</div>
<div>
<a class="<?= $this->classes(['button', 'button-link', 'show-from-md', 'disabled' => !$previousFile]) ?>" role="button" <?php if ($previousFile) : ?>href="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . ($previousFile->name()) . '/') ?>" <?php endif ?> title="<?= $this->translate('panel.pages.previousFile') ?>" aria-label="<?= $this->translate('panel.pages.previousFile') ?>"><?= $this->icon('chevron-left') ?></a>
<a class="<?= $this->classes(['button', 'button-link', 'show-from-md', 'disabled' => !$nextFile]) ?>" role="button" <?php if ($nextFile) : ?>href="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . ($nextFile->name()) . '/') ?>" <?php endif ?> title="<?= $this->translate('panel.pages.nextFile') ?>" aria-label="<?= $this->translate('panel.pages.nextFile') ?>"><?= $this->icon('chevron-right') ?></a>
<?php if ($panel->user()->permissions()->has('pages.renameFiles')) : ?>
<button type="button" class="button button-link" data-modal="renameFileModal" data-modal-action="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . $file->name() . '/rename/') ?>" data-filename="<?= $file->name() ?>" title="<?= $this->translate('panel.pages.renameFile') ?>" aria-label="<?= $this->translate('panel.pages.renameFile') ?>"><?= $this->icon('pencil') ?></button>
<?php endif ?>
<?php if ($panel->user()->permissions()->has('pages.replaceFiles')) : ?>
<button type="button" class="button button-link" data-command="replaceFile" data-action="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . $file->name() . '/replace/') ?>" data-extension=".<?= $file->extension() ?>" title="<?= $this->translate('panel.pages.replaceFile') ?>" aria-label="<?= $this->translate('panel.pages.replaceFile') ?>"><?= $this->icon('cloud-upload') ?></button>
<?php endif ?>
<?php if ($panel->user()->permissions()->has('pages.deleteFiles')) : ?>
<button type="button" class="button button-link" data-modal="deleteFileModal" data-modal-action="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . $file->name() . '/delete/') ?>" title="<?= $this->translate('panel.pages.deleteFile') ?>" aria-label="<?= $this->translate('panel.pages.deleteFile') ?>"><?= $this->icon('trash') ?></button>
<?php endif ?>
<?php if (!$file->fields()->isEmpty()): ?>
<button type="submit" class="button button-accent" data-command="save"><?= $this->icon('check-circle') ?> <?= $this->translate('panel.modal.action.save') ?></button>
<?php endif ?>
</div>
</div> </div>
<a class="<?= $this->classes(['button', 'button-link', 'show-from-md', 'disabled' => !$previousFile]) ?>" role="button" <?php if ($previousFile) : ?>href="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . ($previousFile->name()) . '/') ?>" <?php endif ?> title="<?= $this->translate('panel.pages.previousFile') ?>" aria-label="<?= $this->translate('panel.pages.previousFile') ?>"><?= $this->icon('chevron-left') ?></a> <?php if ($file->type() === 'image') : ?>
<a class="<?= $this->classes(['button', 'button-link', 'show-from-md', 'disabled' => !$nextFile]) ?>" role="button" <?php if ($nextFile) : ?>href="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . ($nextFile->name()) . '/') ?>" <?php endif ?> title="<?= $this->translate('panel.pages.nextFile') ?>" aria-label="<?= $this->translate('panel.pages.nextFile') ?>"><?= $this->icon('chevron-right') ?></a> <div class="sections">
<?php if ($panel->user()->permissions()->has('pages.renameFiles')) : ?> <section class="section">
<button type="button" class="button button-link" data-modal="renameFileModal" data-modal-action="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . $file->name() . '/rename/') ?>" data-filename="<?= $file->name() ?>" title="<?= $this->translate('panel.pages.renameFile') ?>" aria-label="<?= $this->translate('panel.pages.renameFile') ?>"><?= $this->icon('pencil') ?></button> <div class="section-header">
<span class="caption"><?= $this->translate('panel.pages.file.preview') ?></span>
</div>
<div class="section-content page-file-preview-container">
<a class="page-file-preview-link" href="<?= $file->uri() ?>"><img class="page-file-preview-image"" src=" <?= $file->uri() ?>"></a>
</div>
</section>
</div>
<?php endif ?> <?php endif ?>
<?php if ($panel->user()->permissions()->has('pages.replaceFiles')) : ?> <?php if ($file->type() === 'video') : ?>
<button type="button" class="button button-link" data-command="replaceFile" data-action="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . $file->name() . '/replace/') ?>" data-extension=".<?= $file->extension() ?>" title="<?= $this->translate('panel.pages.replaceFile') ?>" aria-label="<?= $this->translate('panel.pages.replaceFile') ?>"><?= $this->icon('cloud-upload') ?></button>
<?php endif ?>
<?php if ($panel->user()->permissions()->has('pages.deleteFiles')) : ?>
<button type="button" class="button button-link" data-modal="deleteFileModal" data-modal-action="<?= $panel->uri('/pages/' . trim($page->route(), '/') . '/file/' . $file->name() . '/delete/') ?>" title="<?= $this->translate('panel.pages.deleteFile') ?>" aria-label="<?= $this->translate('panel.pages.deleteFile') ?>"><?= $this->icon('trash') ?></button>
<?php endif ?>
</div>
<?php if ($file->type() === 'image') : ?>
<div class="sections">
<section class="section"> <section class="section">
<div class="section-header"> <div class="section-header">
<span class="caption"><?= $this->translate('panel.pages.file.preview') ?></span> <span class="caption"><?= $this->translate('panel.pages.file.preview') ?></span>
</div> </div>
<div class="section-content page-file-preview-container"> <div class="section-content page-file-preview-container">
<a class="page-file-preview-link" href="<?= $file->uri() ?>"><img class="page-file-preview-image"" src=" <?= $file->uri() ?>"></a> <video style="width: 100%" controls playsinline>
<source src="<?= $file->uri() ?>" type="<?= $file->mimeType() ?>" />
</video>
</div> </div>
</section> </section>
</div> <?php endif ?>
<?php endif ?>
<?php if ($file->type() === 'video') : ?>
<section class="section"> <section class="section">
<div class="section-header"> <div class="section-header">
<span class="caption"><?= $this->translate('panel.pages.file.preview') ?></span> <span class="caption"><?= $this->translate('panel.pages.file.info') ?></span>
</div> </div>
<div class="section-content page-file-preview-container"> <div class="section-content">
<video style="width: 100%" controls playsinline> <div class="row">
<source src="<?= $file->uri() ?>" type="<?= $file->mimeType() ?>" /> <div class="col-sm-1-2 col-md-1-4 mb-4">
</video> <div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.mimeType') ?>:</div>
<?= $file->mimeType() ?>
</div>
<div class="col-sm-1-2 col-md-1-4 mb-4">
<div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.size') ?>:</div>
<?= $file->size() ?>
</div>
<div class="col-sm-1-2 col-md-1-4 mb-4">
<div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.lastModifiedTime') ?>:</div>
<?= $this->datetime($file->lastModifiedTime()) ?>
</div>
<div class="col-sm-1-2 col-md-1-4 mb-4">
<div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.uri') ?>:</div>
<a class="page-file-info-entry-uri" href="<?= $file->uri() ?>"><?= $file->uri() ?></a>
</div>
<?php if ($file->type() === 'image') : ?>
<?php $this->insert('_files/images/info/info', ['file' => $file]) ?>
<?php endif ?>
</div>
</div> </div>
</section> </section>
<?php endif ?> <?php if ($file->type() === 'image') : ?>
<section class="section"> <?php if ($file->hasExifData() && $file->getExifData()->hasPositionData()) : ?>
<div class="section-header"> <section class="section collapsible">
<span class="caption"><?= $this->translate('panel.pages.file.info') ?></span> <div class="section-header">
</div> <button type="button" class="button section-toggle mr-2" title="<?= $this->translate('panel.sections.toggle') ?>" aria-label="<?= $this->translate('panel.sections.toggle') ?>"><?= $this->icon('chevron-up') ?></button>
<div class="section-content"> <span class="caption"><?= $this->translate('panel.pages.file.position') ?></span>
<div class="row"> </div>
<div class="col-sm-1-2 col-md-1-4 mb-4"> <div class="section-content">
<div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.mimeType') ?>:</div> <?php $this->insert('_files/images/position/map', ['exif' => $file->getExifData()]) ?>
<?= $file->mimeType() ?> </div>
</div> </section>
<div class="col-sm-1-2 col-md-1-4 mb-4"> <?php endif ?>
<div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.size') ?>:</div> <?php if ($file->hasExifData()) : ?>
<?= $file->size() ?> <section class="section collapsible collapsed">
</div> <div class="section-header">
<div class="col-sm-1-2 col-md-1-4 mb-4"> <button type="button" class="button section-toggle mr-2" title="<?= $this->translate('panel.sections.toggle') ?>" aria-label="<?= $this->translate('panel.sections.toggle') ?>"><?= $this->icon('chevron-up') ?></button>
<div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.lastModifiedTime') ?>:</div> <span class="caption"><?= $this->translate('panel.pages.file.exif') ?></span>
<?= $this->datetime($file->lastModifiedTime()) ?> </div>
</div> <div class="section-content">
<div class="col-sm-1-2 col-md-1-4 mb-4"> <?php $this->insert('_files/images/exif/data', ['exif' => $file->getExifData()]) ?>
<div class="page-file-info-entry-title"><?= $this->translate('panel.pages.file.info.uri') ?>:</div> </div>
<a class="page-file-info-entry-uri" href="<?= $file->uri() ?>"><?= $file->uri() ?></a> </section>
</div> <?php endif ?>
<?php if ($file->type() === 'image') : ?>
<?php $this->insert('_files/images/info/info', ['file' => $file]) ?>
<?php endif ?>
</div>
</div>
</section>
<?php if ($file->type() === 'image') : ?>
<?php if ($file->hasExifData() && $file->getExifData()->hasPositionData()) : ?>
<section class="section collapsible">
<div class="section-header">
<button type="button" class="button section-toggle mr-2" title="<?= $this->translate('panel.sections.toggle') ?>" aria-label="<?= $this->translate('panel.sections.toggle') ?>"><?= $this->icon('chevron-up') ?></button>
<span class="caption"><?= $this->translate('panel.pages.file.position') ?></span>
</div>
<div class="section-content">
<?php $this->insert('_files/images/position/map', ['exif' => $file->getExifData()]) ?>
</div>
</section>
<?php endif ?> <?php endif ?>
<?php if ($file->hasExifData()) : ?> <input type="hidden" name="csrf-token" value="<?= $csrfToken ?>">
<section class="section collapsible collapsed"> <?php if (!$file->fields()->isEmpty()): ?>
<div class="section-header"> <?php $this->insert('fields', ['fields' => $file->fields()]) ?>
<button type="button" class="button section-toggle mr-2" title="<?= $this->translate('panel.sections.toggle') ?>" aria-label="<?= $this->translate('panel.sections.toggle') ?>"><?= $this->icon('chevron-up') ?></button>
<span class="caption"><?= $this->translate('panel.pages.file.exif') ?></span>
</div>
<div class="section-content">
<?php $this->insert('_files/images/exif/data', ['exif' => $file->getExifData()]) ?>
</div>
</section>
<?php endif ?> <?php endif ?>
<?php endif ?> </form>