From 3cb91940c5bf2655a6bc36c600f326d1f980bfc8 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Tue, 22 Jul 2025 16:08:21 +0200 Subject: [PATCH 1/5] WIP: Handle Embeds --- composer.json | 1 + .../views/components/embedded-image.blade.php | 8 +++ resources/views/components/quote.blade.php | 12 +++++ resources/views/components/video.blade.php | 14 ++++++ src/Exceptions/OzuClientException.php | 10 ++++ src/OzuCms/Form/OzuEditorField.php | 29 +++++++++++ src/OzuCms/Form/OzuEditorToolbarEnum.php | 3 ++ src/OzuServiceProvider.php | 8 +++ src/Support/Database/OzuSeeder.php | 42 ++++++++++++++++ src/View/Components/EmbeddedImage.php | 50 +++++++++++++++++++ src/View/Components/Quote.php | 26 ++++++++++ src/View/Components/Video.php | 29 +++++++++++ 12 files changed, 232 insertions(+) create mode 100644 resources/views/components/embedded-image.blade.php create mode 100644 resources/views/components/quote.blade.php create mode 100644 resources/views/components/video.blade.php create mode 100644 src/Exceptions/OzuClientException.php create mode 100644 src/View/Components/EmbeddedImage.php create mode 100644 src/View/Components/Quote.php create mode 100644 src/View/Components/Video.php diff --git a/composer.json b/composer.json index 3e6a78f..6ed0c87 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ ], "require": { "php": "8.4.*", + "code16/embed": "^2.3", "code16/laravel-content-renderer": "^1.1.0", "guzzlehttp/guzzle": "^7.5", "illuminate/contracts": "^11.0|^12.0", diff --git a/resources/views/components/embedded-image.blade.php b/resources/views/components/embedded-image.blade.php new file mode 100644 index 0000000..111852e --- /dev/null +++ b/resources/views/components/embedded-image.blade.php @@ -0,0 +1,8 @@ +
+ + @if($legend) +
+ {{ $legend }} +
+ @endif +
diff --git a/resources/views/components/quote.blade.php b/resources/views/components/quote.blade.php new file mode 100644 index 0000000..1387c2c --- /dev/null +++ b/resources/views/components/quote.blade.php @@ -0,0 +1,12 @@ +@props([ + 'quote', + 'author' => null, +]) +
+ {{$quote}} + @if($author) + + {{$author}} + + @endif +
diff --git a/resources/views/components/video.blade.php b/resources/views/components/video.blade.php new file mode 100644 index 0000000..a52a343 --- /dev/null +++ b/resources/views/components/video.blade.php @@ -0,0 +1,14 @@ +@props([ + 'url', + 'legend' => null, +]) +
+ + @if($legend) +

{{$legend}}

+ @endif +
diff --git a/src/Exceptions/OzuClientException.php b/src/Exceptions/OzuClientException.php new file mode 100644 index 0000000..42544e1 --- /dev/null +++ b/src/Exceptions/OzuClientException.php @@ -0,0 +1,10 @@ +withoutParagraphs = true; @@ -49,6 +54,28 @@ public function setHeight(int $height, ?int $maxHeight = null): self return $this; } + public function setMaxFileSize(int $maxFileSize): self + { + if(!in_array(OzuEditorToolbarEnum::Image, $this->toolbar)) { + throw new OzuClientException("You should allow Image Uploads by adding OzuEditorToolbarEnum::Image in toolbar configuration before setting max file size"); + } + + $this->maxFileSize = $maxFileSize; + + return $this; + } + + public function setCropRatio(string $cropRatio): self + { + if(!in_array(OzuEditorToolbarEnum::Image, $this->toolbar)) { + throw new OzuClientException("You should allow Image Uploads by adding OzuEditorToolbarEnum::Image in toolbar configuration before setting image crop ratio"); + } + + $this->cropRatio = $cropRatio; + + return $this; + } + public function type(): string { return 'editor'; @@ -62,6 +89,8 @@ public function toArray(): array 'toolbar' => collect($this->toolbar)->map(fn ($item) => $item->value)->toArray(), 'height' => $this->height, 'maxHeight' => $this->maxHeight, + 'maxFileSize' => $this->maxFileSize, + 'cropRatio' => $this->cropRatio, ]); } } diff --git a/src/OzuCms/Form/OzuEditorToolbarEnum.php b/src/OzuCms/Form/OzuEditorToolbarEnum.php index 1b51b8e..7f9c605 100644 --- a/src/OzuCms/Form/OzuEditorToolbarEnum.php +++ b/src/OzuCms/Form/OzuEditorToolbarEnum.php @@ -14,4 +14,7 @@ enum OzuEditorToolbarEnum: string case Heading1 = 'heading-1'; case Heading2 = 'heading-2'; case Iframe = 'iframe'; + case Image = 'upload-image'; + case Video = 'video'; + case Quote = 'blockquote'; } diff --git a/src/OzuServiceProvider.php b/src/OzuServiceProvider.php index c23a0d8..3af7e53 100644 --- a/src/OzuServiceProvider.php +++ b/src/OzuServiceProvider.php @@ -9,8 +9,11 @@ use Code16\OzuClient\Support\Thumbnails\LocalThumbnail; use Code16\OzuClient\Support\Thumbnails\Thumbnail; use Code16\OzuClient\View\Components\Content; +use Code16\OzuClient\View\Components\EmbeddedImage; use Code16\OzuClient\View\Components\File; use Code16\OzuClient\View\Components\Image; +use Code16\OzuClient\View\Components\Quote; +use Code16\OzuClient\View\Components\Video; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\Paginator; @@ -70,12 +73,17 @@ public function boot() $this->publishes([ __DIR__.'/../resources/views/components/file.blade.php' => resource_path('views/vendor/ozu/components/file.blade.php'), __DIR__.'/../resources/views/components/image.blade.php' => resource_path('views/vendor/ozu/components/image.blade.php'), + __DIR__.'/../resources/views/components/video.blade.php' => resource_path('views/vendor/ozu/components/video.blade.php'), + __DIR__.'/../resources/views/components/quote.blade.php' => resource_path('views/vendor/ozu/components/quote.blade.php'), ], 'ozu-views'); Blade::componentNamespace('Code16\\OzuClient\\View\\Components\\Content', 'ozu-content'); Blade::component(Content::class, 'ozu-content'); Blade::component(Image::class, 'ozu-image'); + Blade::component(EmbeddedImage::class, 'sharp-image'); Blade::component(File::class, 'ozu-file'); + Blade::component(Video::class, 'ozu-content-video'); + Blade::component(Quote::class, 'ozu-content-quote'); Paginator::currentPageResolver(function () { return request()->route()->parameter('page'); diff --git a/src/Support/Database/OzuSeeder.php b/src/Support/Database/OzuSeeder.php index 1fd4d59..91586d0 100644 --- a/src/Support/Database/OzuSeeder.php +++ b/src/Support/Database/OzuSeeder.php @@ -2,6 +2,7 @@ namespace Code16\OzuClient\Support\Database; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Seeder; class OzuSeeder extends Seeder @@ -16,4 +17,45 @@ protected function clearMediaDirectory(): void ->each(fn ($file) => unlink($mediaDirectory.'/'.$file)); } } + + public function seedVideoEmbed(Model &$model, string $editorColumnName, ?string $videoUrl = null, ?string $legend = null): void { + $editorContent = $model->$editorColumnName; + $randomVideoUrl = collect([ + 'https://www.youtube.com/watch?v=lXKDu6cdXLI', + 'https://www.youtube.com/watch?v=ZBYZHeB67O4', + 'https://www.youtube.com/watch?v=7nQ2oiVqKHw', + ])->random(); + + $videoTag = sprintf( + '', + $videoUrl ?? $randomVideoUrl, + $legend ? sprintf('legend="%s"', $legend) : '' + ); + + $model->$editorColumnName = $editorContent . ' ' . $videoTag; + if($model->id !== null) { + $model->save(); + } + } + + public function seedQuoteEmbed(Model &$model, string $editorColumnName, ?string $quote = null, ?string $author = null): void { + $editorContent = $model->$editorColumnName; + $randomQuote = collect([ + ["The only thing we have to fear is fear itself.", "Franklin D. Roosevelt"], + ["I think, therefore I am.", "René Descartes"], + ["Be the change that you wish to see in the world.", "Gandhi"], + ["In the middle of difficulty lies opportunity.", "Albert Einstein"], + ])->random(); + + $quoteTag = sprintf( + '', + $quote ?? $randomQuote[0], + $author ?? $randomQuote[1] + ); + + $model->$editorColumnName = $editorContent . ' ' . $quoteTag; + if($model->id !== null) { + $model->save(); + } + } } diff --git a/src/View/Components/EmbeddedImage.php b/src/View/Components/EmbeddedImage.php new file mode 100644 index 0000000..6108081 --- /dev/null +++ b/src/View/Components/EmbeddedImage.php @@ -0,0 +1,50 @@ +file = json_decode(htmlspecialchars_decode($file), true)) { + $this->fileModel = Media::make([ + 'file_name' => $this->file['file_name'], + 'disk' => $this->file['disk'] ?? null, + 'filters' => $this->file['filters'] ?? null, + ]); + $this->disk = Storage::disk($this->fileModel->disk); + $this->exists = $this->disk->exists($this->fileModel->file_name); + $this->name = $this->file['name'] ?? basename($this->fileModel->file_name); + } + + if (! $this->thumbnailWidth && ! $this->thumbnailHeight) { + $this->thumbnailWidth = 500; + } + } + + public function render(): \Illuminate\View\View + { + if(!$this->fileModel){ + throw new OzuClientException("Unable to render embedded image: invalid file"); + } + return view('ozu::components.embedded-image'); + } +} diff --git a/src/View/Components/Quote.php b/src/View/Components/Quote.php new file mode 100644 index 0000000..f5ef675 --- /dev/null +++ b/src/View/Components/Quote.php @@ -0,0 +1,26 @@ + $this->quote, + 'author' => $this->author, + ]); + } +} diff --git a/src/View/Components/Video.php b/src/View/Components/Video.php new file mode 100644 index 0000000..16b705b --- /dev/null +++ b/src/View/Components/Video.php @@ -0,0 +1,29 @@ +url); + $this->service = ServiceFactory::getByUrl($url) ?: ServiceFactory::getFallback($url); + } + + public function render() + { + return view('ozu::components.video', [ + 'url' => $this->url, + 'legend' => $this->legend, + ]); + } +} From f873fcef6565a6a8e8448cd15845fb3b0ad7bba1 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Tue, 22 Jul 2025 14:08:48 +0000 Subject: [PATCH 2/5] Fix styling --- src/Exceptions/OzuClientException.php | 5 +---- src/OzuCms/Form/OzuEditorField.php | 9 +++++---- src/Support/Database/OzuSeeder.php | 22 ++++++++++++---------- src/View/Components/EmbeddedImage.php | 11 +++++++---- src/View/Components/Quote.php | 2 -- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/Exceptions/OzuClientException.php b/src/Exceptions/OzuClientException.php index 42544e1..ff14758 100644 --- a/src/Exceptions/OzuClientException.php +++ b/src/Exceptions/OzuClientException.php @@ -4,7 +4,4 @@ use Exception; -class OzuClientException extends Exception -{ - -} +class OzuClientException extends Exception {} diff --git a/src/OzuCms/Form/OzuEditorField.php b/src/OzuCms/Form/OzuEditorField.php index c391cea..64bb95a 100644 --- a/src/OzuCms/Form/OzuEditorField.php +++ b/src/OzuCms/Form/OzuEditorField.php @@ -23,6 +23,7 @@ class OzuEditorField extends OzuField private ?int $maxHeight = null; private int $maxFileSize = 5; + private ?string $cropRatio = null; public function setWithoutParagraphs(): self @@ -56,8 +57,8 @@ public function setHeight(int $height, ?int $maxHeight = null): self public function setMaxFileSize(int $maxFileSize): self { - if(!in_array(OzuEditorToolbarEnum::Image, $this->toolbar)) { - throw new OzuClientException("You should allow Image Uploads by adding OzuEditorToolbarEnum::Image in toolbar configuration before setting max file size"); + if (! in_array(OzuEditorToolbarEnum::Image, $this->toolbar)) { + throw new OzuClientException('You should allow Image Uploads by adding OzuEditorToolbarEnum::Image in toolbar configuration before setting max file size'); } $this->maxFileSize = $maxFileSize; @@ -67,8 +68,8 @@ public function setMaxFileSize(int $maxFileSize): self public function setCropRatio(string $cropRatio): self { - if(!in_array(OzuEditorToolbarEnum::Image, $this->toolbar)) { - throw new OzuClientException("You should allow Image Uploads by adding OzuEditorToolbarEnum::Image in toolbar configuration before setting image crop ratio"); + if (! in_array(OzuEditorToolbarEnum::Image, $this->toolbar)) { + throw new OzuClientException('You should allow Image Uploads by adding OzuEditorToolbarEnum::Image in toolbar configuration before setting image crop ratio'); } $this->cropRatio = $cropRatio; diff --git a/src/Support/Database/OzuSeeder.php b/src/Support/Database/OzuSeeder.php index 91586d0..1092b03 100644 --- a/src/Support/Database/OzuSeeder.php +++ b/src/Support/Database/OzuSeeder.php @@ -18,7 +18,8 @@ protected function clearMediaDirectory(): void } } - public function seedVideoEmbed(Model &$model, string $editorColumnName, ?string $videoUrl = null, ?string $legend = null): void { + public function seedVideoEmbed(Model &$model, string $editorColumnName, ?string $videoUrl = null, ?string $legend = null): void + { $editorContent = $model->$editorColumnName; $randomVideoUrl = collect([ 'https://www.youtube.com/watch?v=lXKDu6cdXLI', @@ -32,19 +33,20 @@ public function seedVideoEmbed(Model &$model, string $editorColumnName, ?string $legend ? sprintf('legend="%s"', $legend) : '' ); - $model->$editorColumnName = $editorContent . ' ' . $videoTag; - if($model->id !== null) { + $model->$editorColumnName = $editorContent.' '.$videoTag; + if ($model->id !== null) { $model->save(); } } - public function seedQuoteEmbed(Model &$model, string $editorColumnName, ?string $quote = null, ?string $author = null): void { + public function seedQuoteEmbed(Model &$model, string $editorColumnName, ?string $quote = null, ?string $author = null): void + { $editorContent = $model->$editorColumnName; $randomQuote = collect([ - ["The only thing we have to fear is fear itself.", "Franklin D. Roosevelt"], - ["I think, therefore I am.", "René Descartes"], - ["Be the change that you wish to see in the world.", "Gandhi"], - ["In the middle of difficulty lies opportunity.", "Albert Einstein"], + ['The only thing we have to fear is fear itself.', 'Franklin D. Roosevelt'], + ['I think, therefore I am.', 'René Descartes'], + ['Be the change that you wish to see in the world.', 'Gandhi'], + ['In the middle of difficulty lies opportunity.', 'Albert Einstein'], ])->random(); $quoteTag = sprintf( @@ -53,8 +55,8 @@ public function seedQuoteEmbed(Model &$model, string $editorColumnName, ?string $author ?? $randomQuote[1] ); - $model->$editorColumnName = $editorContent . ' ' . $quoteTag; - if($model->id !== null) { + $model->$editorColumnName = $editorContent.' '.$quoteTag; + if ($model->id !== null) { $model->save(); } } diff --git a/src/View/Components/EmbeddedImage.php b/src/View/Components/EmbeddedImage.php index 6108081..3e3f900 100644 --- a/src/View/Components/EmbeddedImage.php +++ b/src/View/Components/EmbeddedImage.php @@ -4,18 +4,20 @@ use Code16\OzuClient\Eloquent\Media; use Code16\OzuClient\Exceptions\OzuClientException; -use Code16\Sharp\Form\Eloquent\Uploads\SharpUploadModel; use Illuminate\Contracts\Filesystem\Filesystem; -use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Storage; use Illuminate\View\Component; class EmbeddedImage extends Component { public array $file; + public ?string $name = null; + public ?Media $fileModel = null; + public ?Filesystem $disk = null; + public bool $exists = false; public function __construct( @@ -42,9 +44,10 @@ public function __construct( public function render(): \Illuminate\View\View { - if(!$this->fileModel){ - throw new OzuClientException("Unable to render embedded image: invalid file"); + if (! $this->fileModel) { + throw new OzuClientException('Unable to render embedded image: invalid file'); } + return view('ozu::components.embedded-image'); } } diff --git a/src/View/Components/Quote.php b/src/View/Components/Quote.php index f5ef675..17a5204 100644 --- a/src/View/Components/Quote.php +++ b/src/View/Components/Quote.php @@ -3,8 +3,6 @@ namespace Code16\OzuClient\View\Components; use Code16\Embed\ServiceContract; -use Code16\Embed\ServiceFactory; -use Code16\Embed\ValueObjects\Url; use Illuminate\View\Component; class Quote extends Component From 05f84fd9d09fed12b2baf7ee6ba249e08a9d4393 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Tue, 22 Jul 2025 16:42:52 +0200 Subject: [PATCH 3/5] Handle embedded images --- README.md | 46 +++++++++++++++++-- .../views/components/embedded-image.blade.php | 6 +++ src/OzuServiceProvider.php | 1 + 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4cb087b..455a731 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,6 @@ Publish the config file: php artisan vendor:publish --tag="ozu-client-config" ``` -> [!IMPORTANT] -> As our beta builder only works with PHP 8.4 for now, your project must use PHP 8.4. - ## Usage ### Models are Ozu collections @@ -109,6 +106,49 @@ class Project extends Model > [!NOTE] > This configuration will be used by Ozu to properly display the collection in the content management tool. It has no effect in your local codebase. +### Configure your content editor +``` + public static function configureOzuCollectionForm(OzuCollectionFormConfig $config): OzuCollectionFormConfig + { + return $config + ->configureContentField(fn(OzuEditorField $field) => $field + ->setLabel('Contenu') + ->setToolbar([ + OzuEditorToolbarEnum::Heading1, + OzuEditorToolbarEnum::Heading2, + OzuEditorToolbarEnum::Separator, + OzuEditorToolbarEnum::Bold, + OzuEditorToolbarEnum::Italic, + OzuEditorToolbarEnum::Link, + OzuEditorToolbarEnum::Image, + OzuEditorToolbarEnum::Separator, + OzuEditorToolbarEnum::Iframe, + OzuEditorToolbarEnum::Quote, + OzuEditorToolbarEnum::Video, + OzuEditorToolbarEnum::Separator, + OzuEditorToolbarEnum::BulletList, + OzuEditorToolbarEnum::OrderedList, + OzuEditorToolbarEnum::Separator, + ]) + ->setMaxFileSize(12) + ->setCropRatio('1:1') + ); + } +``` + +Ozu uses a supercharged version of the sharp editor field. You can declare multiple toolbar element to enrich your content, including : + +- `OzuEditorToolbarEnum::Image` to embed images directly inside your content +- `OzuEditorToolbarEnum::Quote` to embed nice quotes with an (optional) author +- `OzuEditorToolbarEnum::Video` to embed videos from different providers (Youtube, Vimeo, Dailymotion) + +Ozu will render very basically theses content but you can override the views by publishing them with: +```bash +php artisan vendor:publish --tag="ozu-views" +``` +> [!WARNING] +> To allow ozu to renders rich content in your front, please use the `` component. + ### Handle `BelongsTo` relationships A common use case is to have a `BelongsTo` relationship between two Ozu Models. There are two possibilities: diff --git a/resources/views/components/embedded-image.blade.php b/resources/views/components/embedded-image.blade.php index 111852e..1f1ddc2 100644 --- a/resources/views/components/embedded-image.blade.php +++ b/resources/views/components/embedded-image.blade.php @@ -1,3 +1,9 @@ +@php use Code16\OzuClient\Eloquent\Media; @endphp +@props([ + /** @var Media $fileModel */ + 'fileModel', + 'legend' => null, +])
@if($legend) diff --git a/src/OzuServiceProvider.php b/src/OzuServiceProvider.php index 3af7e53..530fbbc 100644 --- a/src/OzuServiceProvider.php +++ b/src/OzuServiceProvider.php @@ -73,6 +73,7 @@ public function boot() $this->publishes([ __DIR__.'/../resources/views/components/file.blade.php' => resource_path('views/vendor/ozu/components/file.blade.php'), __DIR__.'/../resources/views/components/image.blade.php' => resource_path('views/vendor/ozu/components/image.blade.php'), + __DIR__.'/../resources/views/components/embedded-image.blade.php' => resource_path('views/vendor/ozu/components/embedded-image.blade.php'), __DIR__.'/../resources/views/components/video.blade.php' => resource_path('views/vendor/ozu/components/video.blade.php'), __DIR__.'/../resources/views/components/quote.blade.php' => resource_path('views/vendor/ozu/components/quote.blade.php'), ], 'ozu-views'); From a66aab72013600987fbe708bc94e1c7f2f2cbe84 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Tue, 22 Jul 2025 16:44:11 +0200 Subject: [PATCH 4/5] Fix README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 455a731..823753b 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ class Project extends Model > This configuration will be used by Ozu to properly display the collection in the content management tool. It has no effect in your local codebase. ### Configure your content editor -``` +```php public static function configureOzuCollectionForm(OzuCollectionFormConfig $config): OzuCollectionFormConfig { return $config From 4bd07ff2ef5c41f8b0cbc57880deb7172873e592 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Tue, 22 Jul 2025 17:28:43 +0200 Subject: [PATCH 5/5] Fix tests --- tests/Unit/OzuFieldTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/Unit/OzuFieldTest.php b/tests/Unit/OzuFieldTest.php index e11b2ca..d57c5df 100644 --- a/tests/Unit/OzuFieldTest.php +++ b/tests/Unit/OzuFieldTest.php @@ -102,6 +102,8 @@ ], 'height' => 200, 'maxHeight' => null, + 'maxFileSize' => 5, + 'cropRatio' => null, ]); }); @@ -132,6 +134,8 @@ ], 'height' => 200, 'maxHeight' => null, + 'maxFileSize' => 5, + 'cropRatio' => null, ]); $field->hideToolbar(); @@ -167,6 +171,8 @@ ], 'height' => 300, 'maxHeight' => 500, + 'maxFileSize' => 5, + 'cropRatio' => null, ]); });