Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions formwork/config/system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ fields:

files:
allowedExtensions: []
metadataExtension: .meta.yaml

images:
jpegQuality: 85
Expand Down
32 changes: 31 additions & 1 deletion formwork/src/Files/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,64 @@

namespace Formwork\Files;

use Formwork\App;
use Formwork\Data\Contracts\Arrayable;
use Formwork\Files\Exceptions\FileUriGenerationException;
use Formwork\Model\Attributes\ReadonlyModelProperty;
use Formwork\Model\Model;
use Formwork\Parsers\Yaml;
use Formwork\Utils\FileSystem;
use Formwork\Utils\MimeType;
use Formwork\Utils\Str;
use RuntimeException;
use Stringable;

class File implements Arrayable, Stringable
class File extends Model implements Arrayable, Stringable
{
protected const MODEL_IDENTIFIER = 'file';

protected const SCHEME_IDENTIFIER = 'files.file';

/**
* File name
*/
#[ReadonlyModelProperty]
protected string $name;

/**
* File extension
*/
#[ReadonlyModelProperty]
protected string $extension;

/**
* File MIME type
*/
#[ReadonlyModelProperty]
protected string $mimeType;

/**
* File type in a human-readable format
*/
#[ReadonlyModelProperty]
protected ?string $type = null;

/**
* File size in a human-readable format
*/
#[ReadonlyModelProperty]
protected string $size;

/**
* File last modified time
*/
#[ReadonlyModelProperty]
protected int $lastModifiedTime;

/**
* File hash
*/
#[ReadonlyModelProperty]
protected string $hash;

protected FileUriGenerator $uriGenerator;
Expand All @@ -58,6 +73,7 @@ public function __construct(protected string $path)
{
$this->name = basename($path);
$this->extension = FileSystem::extension($path);
$this->loadData();
}

public function __toString(): string
Expand Down Expand Up @@ -190,6 +206,20 @@ public function toArray(): array
];
}

private function loadData(): void
{
$app = App::instance();

$this->scheme = $app->schemes()->get(static::SCHEME_IDENTIFIER);
$this->fields = $this->scheme->fields();

$metadataFile = $this->path . $app->config()->get('system.files.metadataExtension');

$this->data = FileSystem::exists($metadataFile) ? Yaml::parseFile($metadataFile) : [];

$this->fields->setValues($this->data);
}

/**
* Match MIME type with an array of extensions
*
Expand Down
12 changes: 9 additions & 3 deletions formwork/src/Images/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,28 @@
use Formwork\Images\Transform\Sharpen;
use Formwork\Images\Transform\Smoothen;
use Formwork\Images\Transform\TransformCollection;
use Formwork\Model\Attributes\ReadonlyModelProperty;
use Formwork\Utils\FileSystem;
use Formwork\Utils\MimeType;
use Formwork\Utils\Uri;
use RuntimeException;

class Image extends File
{
protected string $path;
protected const MODEL_IDENTIFIER = 'image';

protected const SCHEME_IDENTIFIER = 'files.image';

#[ReadonlyModelProperty]
protected AbstractHandler $handler;

#[ReadonlyModelProperty]
protected ImageInfo $info;

#[ReadonlyModelProperty]
protected TransformCollection $transforms;

protected string $mimeType;

#[ReadonlyModelProperty]
protected ?string $type = 'image';

/**
Expand Down Expand Up @@ -312,6 +317,7 @@ public function process(?string $mimeType = null, bool $forceCache = false): Ima
}

$image = new Image($path, $this->options);
$image->data = $this->data;
$image->uriGenerator = $this->uriGenerator;
$image->transforms = $this->transforms;
$image->handler = $this->handler;
Expand Down
10 changes: 10 additions & 0 deletions formwork/src/Model/Attributes/ReadonlyModelProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Formwork\Model\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_PROPERTY)]
class ReadonlyModelProperty
{
}
12 changes: 12 additions & 0 deletions formwork/src/Model/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
use Formwork\Data\Traits\DataMultipleGetter;
use Formwork\Data\Traits\DataMultipleSetter;
use Formwork\Fields\FieldCollection;
use Formwork\Model\Attributes\ReadonlyModelProperty;
use Formwork\Schemes\Scheme;
use Formwork\Utils\Arr;
use ReflectionAttribute;
use ReflectionProperty;

class Model implements Arrayable
Expand Down Expand Up @@ -104,6 +106,10 @@ public function get(string $key, mixed $default = null): mixed
public function set(string $key, mixed $value): void
{
if (property_exists($this, $key) && !(new ReflectionProperty($this, $key))->isPromoted()) {
if ($this->isReadonly($key)) {
throw new BadMethodCallException(sprintf('Cannot set readonly model property %s::$%s', static::class, $key));
}

// If defined use a setter
if (method_exists($this, $setter = 'set' . ucfirst($key))) {
$this->{$setter}($value);
Expand Down Expand Up @@ -140,4 +146,10 @@ public function data(): array
{
return $this->data;
}

private function isReadonly(string $property): bool
{
$attributes = (new ReflectionProperty($this, $property))->getAttributes(ReadonlyModelProperty::class, ReflectionAttribute::IS_INSTANCEOF);
return $attributes !== [];
}
}
9 changes: 7 additions & 2 deletions formwork/src/Pages/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -568,8 +568,13 @@ protected function loadFiles(): void
$languages[] = $language;
}
}
} elseif (in_array($extension, $config->get('system.files.allowedExtensions'), true)) {
$files[] = App::instance()->getService(FileFactory::class)->make(FileSystem::joinPaths($this->path, $file));
} else {
if (Str::endsWith($file, $config->get('system.files.metadataExtension'))) {
continue;
}
if (in_array($extension, $config->get('system.files.allowedExtensions'), true)) {
$files[] = App::instance()->getService(FileFactory::class)->make(FileSystem::joinPaths($this->path, $file));
}
}
}
}
Expand Down
50 changes: 50 additions & 0 deletions formwork/src/Panel/Controllers/PagesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Formwork\Exceptions\TranslatedException;
use Formwork\Fields\Exceptions\ValidationException;
use Formwork\Fields\FieldCollection;
use Formwork\Files\File;
use Formwork\Files\FileUploader;
use Formwork\Http\Files\UploadedFile;
use Formwork\Http\JsonResponse;
Expand Down Expand Up @@ -509,10 +510,32 @@ public function file(RouteParams $routeParams): Response

$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->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);

$this->modal('renameFile');
$this->modal('deleteFile');
$this->modal('changes');

return new Response($this->view('pages.file', [
'title' => $file->name(),
Expand Down Expand Up @@ -586,6 +609,33 @@ protected function createPage(FieldCollection $fieldCollection): Page
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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
use League\Config\ConfigurationBuilderInterface;
use Nette\Schema\Expect;

class LinkBaseExtension implements ConfigurableExtensionInterface
class FormworkExtension implements ConfigurableExtensionInterface
{
public function configureSchema(ConfigurationBuilderInterface $configurationBuilder): void
{
$configurationBuilder->addSchema('formwork', Expect::structure([
'baseRoute' => Expect::string('/'),
'imageAltProperty' => Expect::string('alt'),
'baseRoute' => Expect::string('/'),
]));
}

public function register(EnvironmentBuilderInterface $environmentBuilder): void
{
$environmentBuilder->addEventListener(DocumentParsedEvent::class, new ImageAltProcessor($environmentBuilder->getConfiguration()));
$environmentBuilder->addEventListener(DocumentParsedEvent::class, new LinkBaseProcessor($environmentBuilder->getConfiguration()));
}
}
38 changes: 38 additions & 0 deletions formwork/src/Parsers/Extensions/CommonMark/ImageAltProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Formwork\Parsers\Extensions\CommonMark;

use Formwork\App;
use League\CommonMark\Event\DocumentParsedEvent;
use League\CommonMark\Extension\CommonMark\Node\Inline\Image;
use League\Config\ConfigurationInterface;

class ImageAltProcessor
{
public function __construct(protected ConfigurationInterface $configuration)
{
}

public function __invoke(DocumentParsedEvent $documentParsedEvent): void
{
foreach ($documentParsedEvent->getDocument()->iterator() as $node) {
if (!$node instanceof Image) {
continue;
}

$baseRoute = $this->configuration->get('formwork/baseRoute');

$site = App::instance()->site();

$uri = $node->getUrl();

$key = $this->configuration->get('formwork/imageAltProperty');

$alt = $site->findPage($baseRoute)?->files()->get($uri)?->get($key);

if ($alt !== null) {
$node->data->set('attributes/alt', $alt);
}
}
}
}
Loading