From 09e22483554b574c0137c824829ac5fedc2cd33e Mon Sep 17 00:00:00 2001 From: Quentin Renard Date: Tue, 3 Mar 2020 04:54:25 -0500 Subject: [PATCH] 2.0 documentation updates --- CONTRIBUTING.md | 13 +- docs/.sections/block-editor.md | 182 ++-- docs/.sections/crud-modules.md | 551 ++---------- docs/.sections/form-fields.md | 805 ++++++++++++++++++ .../getting-started/configuration.md | 33 +- .../.sections/getting-started/installation.md | 95 +-- docs/.sections/media-library.md | 2 +- docs/.sections/other-cms-features.md | 2 +- docs/.sections/preface.md | 2 +- docs/generate_readme.js | 3 +- 10 files changed, 982 insertions(+), 706 deletions(-) create mode 100644 docs/.sections/form-fields.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 88c8e9696..019badf0d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,22 +9,21 @@ Remember, bug reports are created in the hope that others with the same problem If you discover a security vulnerability within Twill, please email us at [security@twill.io](mailto:security@twill.io). All security vulnerabilities will be promptly addressed. # Versioning scheme -Twill's versioning scheme maintains the following convention: `paradigm.major.minor`. Minor releases should never contain breaking changes. -When referencing Twill from your application, you should always use a version constraint such as `1.2.*`, since major releases of Twill do include breaking changes. +Twill follows [Semantic Versioning](https://semver.org/). Major releases are released only when breaking changes are necessary, while minor and patch releases may be released as often as every week. Minor and patch releases should never contain breaking changes. -The `VERSION` file at the root of the project needs to be updated and a Git tag created to properly release a new version. +When referencing Twill from your application, you should always use a version constraint such as `^2.0`, since major releases of Twill do include breaking changes. # Which branch? -All bug fixes should be sent to the latest stable branch (1.2). Bug fixes should never be sent to the master branch unless they fix features that exist only in the upcoming release. +All bug fixes should be sent to the latest stable branch (`2.x`). Bug fixes should never be sent to the `master` branch unless they fix features that exist only in the upcoming release. -Minor features that are fully backwards compatible with the current Twill release may be sent to the latest stable branch (1.2). +Minor features that are fully backwards compatible with the current Twill release may be sent to the latest stable branch (`2.x`). -Major new features should always be sent to the master branch, which contains the upcoming Twill release. +Major new features should always be sent to the `master` branch, which contains the upcoming Twill release. Please send coherent history — make sure each individual commit in your pull request is meaningful. If you had to make a lot of intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. # Coding style - PHP: [PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md). -- Javascript: [Standard](https://standardjs.com/), [Vue ESLint Essentials](https://github.com/vuejs/eslint-plugin-vue). \ No newline at end of file +- Javascript: [Standard](https://standardjs.com/), [Vue ESLint Essentials](https://github.com/vuejs/eslint-plugin-vue). diff --git a/docs/.sections/block-editor.md b/docs/.sections/block-editor.md index 8e919d64a..a119bcd2b 100644 --- a/docs/.sections/block-editor.md +++ b/docs/.sections/block-editor.md @@ -1,7 +1,7 @@ ## Block editor ### Adding blocks -The block editor form field lets you add content freely to your module. The blocks can be easily added and rearranged. -Once a block is created, it can be used/added to any module by adding the corresponding traits. +The block editor form field allows you to add content freely to your module. The blocks can be easily added and rearranged. +Once a block is created, it can be used/added to any module. In order to add a block editor you need to add the `block_editor` field to your module form. e.g.: @@ -27,7 +27,7 @@ By adding the `@formField('block_editor')` you've enabled all the available bloc ``` The *blocks* that can be added need to be defined under the `views/admin/blocks` folder. -The blocks can be defined exactly like a regular form. e.g.: +The blocks can be defined exactly like a regular form, but without any Blade layout or section. e.g.: filename: ```admin/blocks/quote.blade.php``` ```php @@ -40,45 +40,66 @@ filename: ```admin/blocks/quote.blade.php``` ]) ``` -Once the form is created an _artisan_ task needs to be run to generate the _Vue_ component for this block. +A more complex block could look like the following: -`php artisan twill:blocks` - -Example output: -```shell -$ php artisan twill:blocks -Starting to scan block views directory... -Block Quote generated successfully -All blocks have been generated! -$ -``` - -The task will generate a file inside the folder `resources/assets/js/blocks/`. Do not ignore those files in Git. +filename: ```admin/blocks/media.blade.php``` +```php +@formField('medias', [ + 'name' => 'image', + 'label' => 'Images', + 'withVideoUrl' => false, + 'max' => 20, +]) -filename: ```resources/assets/js/blocks/BlockQuote.vue``` +@formField('files', [ + 'name' => 'video', + 'label' => 'Video', + 'note' => 'Video will overwrite previously selected images', + 'max' => 1 +]) -```js - +@formField('input', [ + 'name' => 'caption', + 'label' => 'Caption', + 'maxlength' => 250, + 'translated' => true, +]) - +@formField('color', [ + 'name' => 'bg', + 'label' => 'Background color', + 'note' => 'Default is light grey (#E6E6E6)', +]) +@formField('input', [ + 'name' => 'timing', + 'label' => 'Timing', + 'maxlength' => 250, + 'note' => 'Timing in ms (default is 4000ms)', +]) ``` -With that the *block* is ready to be used on the form, it needs to be enabled in the CMS configuration. +A block needs to be enabled in your Twill configuration. For it a `block_editor` key is required and inside you can define the list of `blocks` available in your project. filename: ```config/twill.php``` - ```php 'block_editor' => [ 'blocks' => [ @@ -88,13 +109,18 @@ filename: ```config/twill.php``` 'icon' => 'text', 'component' => 'a17-block-quote', ], + 'media' => [ + 'title' => 'Media', + 'icon' => 'image', + 'component' => 'a17-block-media', + ], .. ] ] ``` -Please note the naming convention. If the *block* added is _quote_ then the component should be prefixed with _a17-block-_. -If you added a block like *my_awesome_block* then you will need to keep the same name as _key_ and the _component name_ with the prefix. e.g.: +Please note the naming convention. If the *block* added is `quote` then the component should be prefixed with `a17-block-`. +If you added a block named *my_awesome_block*, your configuration would look like this: ```php 'block_editor' => [ 'blocks' => [ @@ -108,9 +134,7 @@ If you added a block like *my_awesome_block* then you will need to keep the same ] ``` - -After having the blocks added and the configuration set it is required to have the traits added inside your module (Laravel Model). -Add the corresponding traits to your model and repository, respectively `HasBlocks` and `HandleBlocks`. +Make sure to use blocks traits in your model and repository, respectively `HasBlocks` and `HandleBlocks`. filename: ```app/Models/Article.php``` ```php @@ -147,46 +171,19 @@ class ArticleRepository extends ModuleRepository } ``` -#### Common Errors -- Make sure your project has the blocks table migration. If not, you can find the `create_blocks_table` migration in Twill's source in `migrations`. - -- Not running the _twill:blocks_ task. - -- Not adding the *block* to the configuration. - -- Not using the same name of the block inside the configuration. - -- Not running npm run twill-build - ### Adding repeater blocks -Lets say that it is requested to have an Accordion on Articles, where each item should have a _Header_ and a _Description_. -This accordion can be moved around along with the rest of the blocks. -On the Article (module) form we have: - -filename: ```views/admin/articles/form.blade.php``` -```php -@extends('twill::layouts.form') - -@section('contentFields') - @formField('input', [ - 'name' => 'description', - 'label' => 'Description', - ]) -... - @formField('block_editor') -@stop -``` +Inside of a block, repeaters can be used too. -- Inside the *container block* file, add a repeater form field: +- Create a *container* block file, using a repeater form field: filename: ```admin/blocks/accordion.blade.php``` ```php @formField('repeater', ['type' => 'accordion_item']) ``` +You can add other fields before or after your repeater, or even multiple repeaters to the same block. - -- Add it on the config/twill.php +- Add your block to the configuration: ```php 'block_editor' => [ 'blocks' => [ @@ -201,9 +198,9 @@ filename: ```views/admin/articles/form.blade.php``` ] ``` -- Add the *item block*, the one that will be reapeated inside the *container block* - filename: ```admin/blocks/accordion_item.blade.php``` +- Create an *item* block, the one that will be reapeated inside the *container* block +filename: ```admin/blocks/accordion_item.blade.php``` ```php @formField('input', [ 'name' => 'header', @@ -218,7 +215,7 @@ filename: ```views/admin/articles/form.blade.php``` ]) ``` -- Add it on the config/twill.php on the repeaters section +- Add it to the configuration, in the repeaters section ```php 'block_editor' => [ @@ -245,7 +242,7 @@ filename: ```views/admin/articles/form.blade.php``` ``` #### Common errors: -- If you add the *container block* to the _repeaters_ section inside the config, it won't work, e.g.: +- If you add the *container* block to the _repeaters_ section inside the config, it won't work, e.g.: ```php 'repeaters' => [ ... @@ -273,30 +270,13 @@ filename: ```views/admin/articles/form.blade.php``` ] ``` -- Not adding the *item block* to the _repeaters_ section. +- Not adding the *item* block to the _repeaters_ section. ### Adding browser fields -If you are requested to enable the possibility to add a related model, then the browser fields are the match. -If you have an Article that can have related products. - -On the Article (entity) form we have: -filename: ```views/admin/articles/form.blade.php``` -```php -@extends('twill::layouts.form') - -@section('contentFields') - @formField('input', [ - 'name' => 'description', - 'label' => 'Description', - ]) -... - @formField('block_editor') -@stop - -``` +To attach other records to inside of a block, it is possible to use the `browser` field. -- Add the block editors that will handle the `Browser Field` +- In a block, use the `browser` field: filename: ```views/admin/blocks/products.blade.php``` ```php @formField('browser', [ @@ -308,7 +288,7 @@ filename: ```views/admin/blocks/products.blade.php``` ]) ``` -- Define the block in the configuration like any other block in the config/twill.php. +- Define the block in configuration like any other block: ```php 'blocks' => [ ... @@ -319,7 +299,7 @@ filename: ```views/admin/blocks/products.blade.php``` ], ``` -- After that, it is required to add the Route Prefixes. e.g.: +- If the module you are browsing is not at the root of your admin, you can use the `browser_route_prefixes` array: ```php 'block_editor' => [ 'blocks' => [ @@ -335,7 +315,7 @@ filename: ```views/admin/blocks/products.blade.php``` ... ], 'browser_route_prefixes' => [ - 'products' => 'content', + 'products' => 'shop', ], ] ``` @@ -390,10 +370,15 @@ To give an example: ```php return [ 'block_editor' => [ - 'block_single_layout' => 'site.layouts.block', - 'block_views_path' => 'site.blocks', - 'block_views_mappings' => [], - 'block_preview_render_childs' => true, + 'block_single_layout' => 'site.layouts.block', // layout to use when rendering a single block in the editor + 'block_views_path' => 'site.blocks', // path where a view file per block type is stored + 'block_views_mappings' => [], // custom mapping of block types and views + 'block_preview_render_childs' => true, // indicates if childs should be rendered when using repeater in blocks + 'block_presenter_path' => null, // allow to set a custom presenter to a block model + // Indicates if blocks templates should be inlined in HTML. + // When setting to false, make sure to build Twill with your all your custom blocks using php artisan twill:build. + 'inline_blocks_templates' => true, + 'custom_vue_blocks_resource_path' => 'assets/js/blocks', // path to custom vue blocks in your resources directory 'blocks' => [ 'text' => [ 'title' => 'Body text', @@ -440,6 +425,7 @@ return [ ], ], ], + 'repeaters' => [], ], ... ]; diff --git a/docs/.sections/crud-modules.md b/docs/.sections/crud-modules.md index 88660eee1..f60c7023b 100644 --- a/docs/.sections/crud-modules.md +++ b/docs/.sections/crud-modules.md @@ -1,36 +1,62 @@ ## CRUD modules +Twill core functionnality is the ability to setup what we call modules. A module is set of files that define a content model and its associated business logic in your application. Those module can be configured to enable several features for publishers, from the ability to translate content, to the ability to attach images and more complex data structure to your records. ### CLI Generator You can generate all the files needed in your application to create a new CRUD module using Twill's Artisan generator: ```bash -php artisan twill:module yourPluralModuleName +php artisan twill:module moduleName ``` -The command has a couple of options : -- `--hasBlocks (-B)`, -- `--hasTranslation (-T)`, -- `--hasSlug (-S)`, -- `--hasMedias (-M)`, -- `--hasFiles (-F)`, -- `--hasPosition (-P)` -- `--hasRevisions(-R)`. +The command accepts several options: +- `--hasBlocks (-B)`, to use the block editor on your module form +- `--hasTranslation (-T)`, to add content in multiple languages +- `--hasSlug (-S)`, to generate slugs based on one or multiple fields in your model +- `--hasMedias (-M)`, to attach images to your records +- `--hasFiles (-F)`, to attach files to your records +- `--hasPosition (-P)`, to allow manually reordering of records in the listing screen +- `--hasRevisions(-R)`, to allow comparing and restoring past revisions of records -This will generate a migration file, a model, a repository, a controller, a form request object and a form view. +The `twill:module` command will generate a migration file, a model, a repository, a controller, a form request object and a form view. -Start by filling in the migration and models using the documentation below. +Add the route to your admin routes file(`routes/admin.php`). -Add `Route::module('yourPluralModuleName');` to your admin routes file. +```php + [ + 'title' => 'Module name', + 'module' => true + ] + ... +] +``` + +With that in place, after migrating the database using `php artisan migrate`, you should be able to start creating content. By default, a module only have a title and a description, the ability to be published, and any other feature you added through the CLI generator. + +If you provided the `hasBlocks` option, you will be able to use the `block_editor` form field in the form of that module. + +If you provided the `hasTranslation` option, and have multiple languages specified in your `translatable.php` configuration file, the UI will react automatically and allow publishers to translate content and manage publication at the language level. + +If you provided the `hasSlug` option, slugs will automatically be generated from the title field. -Setup your form fields in `resources/views/admin/moduleName/form.blade.php`. +If you provided the `hasMedias` or `hasFiles` option, you will be able to respectively add several `medias` or `files` form fields to the form of that module. -Setup your index options and columns in your controller if needed. +If you provided the `hasPosition` option, publishers will be able to manually order records from the module's listing screen (after enabling the `reorder` option in the module's controller `indexOptions` array). -Enjoy. +If you provided the `hasRevisions` option, each form submission will create a new revision in your database so that publishers can compare and restore them in the CMS UI. + +Depending on the depth of your module in your navigation, you'll need to wrap your route declaration in one or multiple nested route groups. + +You can setup your index options and columns in the generated controller if needed. ### Migrations Generated migrations are regular Laravel migrations. A few helpers are available to create the default fields any CRUD module will use: @@ -111,9 +137,9 @@ Depending on the features you need on your model, include the available traits a - HasPosition: implement the `A17\Twill\Models\Behaviors\Sortable` interface and add a position field to your fillables. -- HasTranslation: add translated fields in the `translatedAttributes` array and in the `fillable` array of the generated translatable model in `App/Models/Translations` (always keep the `active` and `locale` fields). +- HasTranslation: add translated fields in the `translatedAttributes` array. -When using Twill's `HasTranslation` trait on a model, you are actually using the popular `dimsav/translatable` package. A default configuration will be automatically published to your `config` directory when you run the `twill:install` command. +When using Twill's `HasTranslation` trait on a model, you are actually using the popular `astronomic/laravel-translatable` package. A default configuration will be automatically published to your `config` directory when you run the `twill:install` command. To setup your list of available languages for translated fields, modify the `locales` array in `config/translatable.php`, using ISO 639-1 two-letter languages codes as in the following example: @@ -347,6 +373,8 @@ public function hydrate($object, $fields) 'permalink' => true, 'bulkEdit' => true, 'editInModal' => false, + 'forceDelete' => true, + 'bulkForceDelete' => true, ]; /* @@ -441,13 +469,15 @@ public function hydrate($object, $fields) return []; } - // Optional, if the automatic way is not working for you (default is ucfirst(Str::singular($moduleName))) + // Optional, if the automatic way is not working for you (default is ucfirst(str_singular($moduleName))) protected $modelName = 'model'; // Optional, to specify a different feature field name than the default 'featured' protected $featureField = 'featured'; // Optional, specify number of items per page in the listing view (-1 to disable pagination) + // If you are implementing Sortable, this parameter is ignored given reordering is not implemented + // along with pagination. protected $perPage = 20; // Optional, specify the default listing order @@ -459,11 +489,11 @@ public function hydrate($object, $fields) You can also override all actions and internal functions, checkout the ModuleController source in `A17\Twill\Http\Controllers\Admin\ModuleController`. -#### Example Sort by Relationship Field. +#### Example: sorting by a relationship field Let's say we have a controller with certain fields displayed: -File: `app/Http/Controllers/Admin/PlayController.php` +File: `app/Http/Controllers/Admin/PlayController.php` ```php protected $indexColumns = [ 'image' => [ @@ -486,19 +516,16 @@ File: `app/Http/Controllers/Admin/PlayController.php` ]; ``` -For creating the Sorting mechanism for the relationship we need to overwrite the order method on the proper repository. -In there we verify for the parameter sent which per convention should be *relationship + field* in this case `festivalsTitle`. -Once applied we remove that parameter to avoid the application crash due to not being able to find the field on the table. +To order by the relationship we need to overwrite the order method in the module's repository. File: `app/Repositories/PlayRepository.php` - ```php ... public function order($query, array $orders = []) { if (array_key_exists('festivalsTitle', $orders)){ $sort_method = $orders['festivalsTitle']; - //Remove the UNEXISTING column from the orders array + // remove the unexisting column from the orders array unset($orders['festivalsTitle']); $query = $query->orderByFestival($sort_method); } @@ -508,9 +535,9 @@ File: `app/Repositories/PlayRepository.php` ... ``` -Then add the custom `sort` scope into your Model, it should be something like this: -File: `app/Models/Play.php` +Then, add a custom `sort` scope to your model, it could be something like this: +File: `app/Models/Play.php` ```php public function scopeOrderByFestival($query, $sort_method = 'ASC') { return $query @@ -577,471 +604,14 @@ Route::module('yourModulePluralName'); // You can add an array of only/except action names as a second parameter // By default, the following routes are created : 'reorder', 'publish', 'browser', 'bucket', 'feature', 'restore', 'bulkFeature', 'bulkPublish', 'bulkDelete', 'bulkRestore' -Route::module('yourModulePluralName', ['except' => ['reorder', 'feature', 'bucket', 'browser']]) +Route::module('yourModulePluralName', ['except' => ['reorder', 'feature', 'bucket', 'browser']]); // You can add an array of only/except action names for the resource controller as a third parameter // By default, the following routes are created : 'index', 'store', 'show', 'edit', 'update', 'destroy' -Route::module('yourModulePluralName', [], ['only' => ['index', 'edit', 'store', 'destroy']]) +Route::module('yourModulePluralName', [], ['only' => ['index', 'edit', 'store', 'destroy']]); // The last optional parameter disable the resource controller actions on the module -Route::module('yourPluralModuleName', [], [], false) -``` - -### Form fields - - -Wrap them into the following in your module `form` view (`resources/views/admin/moduleName/form.blade.php`): - -```php -@extends('twill::layouts.form') -@section('contentFields') - @formField('...', [...]) - ... -@stop -``` - -The idea of the `contentFields` section is to contain the most important fields and the block editor as the last field. - -If you have attributes, relationships, extra images, file attachments or repeaters, you'll want to add a `fieldsets` section after the `contentFields` section and use the `a17-fieldset` Vue component to create new ones like in the following example: - -```php -@extends('twill::layouts.form', [ - 'additionalFieldsets' => [ - ['fieldset' => 'attributes', 'label' => 'Attributes'], - ] -]) - -@section('contentFields') - @formField('...', [...]) - ... -@stop - -@section('fieldsets') - - @formField('...', [...]) - ... - -@stop -``` - -The additional fieldsets array passed to the form layout will display a sticky navigation of your fieldset on scroll. -You can also rename the content section by passing a `contentFieldsetLabel` property to the layout. - -#### Input -![screenshot](/docs/_media/input.png) - -```php -@formField('input', [ - 'name' => 'subtitle', - 'label' => 'Subtitle', - 'maxlength' => 100, - 'required' => true, - 'note' => 'Hint message goes here', - 'placeholder' => 'Placeholder goes here', -]) - -@formField('input', [ - 'translated' => true, - 'name' => 'subtitle_translated', - 'label' => 'Subtitle (translated)', - 'maxlength' => 250, - 'required' => true, - 'note' => 'Hint message goes here', - 'placeholder' => 'Placeholder goes here', - 'type' => 'textarea', - 'rows' => 3 -]) -``` - -#### WYSIWYG -![screenshot](/docs/_media/wysiwyg.png) - -```php -@formField('wysiwyg', [ - 'name' => 'case_study', - 'label' => 'Case study text', - 'toolbarOptions' => ['list-ordered', 'list-unordered'], - 'placeholder' => 'Case study text', - 'maxlength' => 200, - 'note' => 'Hint message', -]) - -@formField('wysiwyg', [ - 'name' => 'case_study', - 'label' => 'Case study text', - 'toolbarOptions' => [ [ 'header' => [1, 2, false] ], 'list-ordered', 'list-unordered', [ 'indent' => '-1'], [ 'indent' => '+1' ] ], - 'placeholder' => 'Case study text', - 'maxlength' => 200, - 'editSource' => true, - 'note' => 'Hint message', -]) -``` -WYSIWYG field is based on [Quill](https://quilljs.com/) Rich Text Editor. - -You can add all [toolbar options](https://quilljs.com/docs/modules/toolbar/) from Quill with the `toolbarOptions` key. - -For example, this configuration will render a `wysiwyg` field with almost all features from Quill and Snow theme. - -``` - @formField('wysiwyg', [ - 'name' => 'case_study', - 'label' => 'Case study text', - 'toolbarOptions' => [ - ["font" => ["serif", "sans-serif", "monospace"]], - ['header' => [2, 3, 4, 5, 6, false]], - 'bold', - 'italic', - 'underline', - 'strike', - ["color" => []], - ["background" => []], - ["script" => "super"], - ["script" => "sub"], - "blockquote", - "code-block", - ['list' => 'ordered'], - ['list' => 'bullet'], - ['indent' => '-1'], - ['indent' => '+1'], - ["align" => []], - ["direction" => "rtl"], - 'link', - 'image', - 'video', - "clean", - ], - 'placeholder' => 'Case study text', - 'maxlength' => 200, - 'editSource' => true, - 'note' => 'Hint message`', - ]) -``` - -Note that Quill outputs CSS classes in the HTML for certain toolbar modules (indent, font, align, etc.), and that the image module is not integrated with Twill's media library. It outputs the base64 representation of the uploaded image. It is not a recommended way of using and storing images, prefer using one or multiple `medias` form fields or blocks fields for flexible content. This will give you greater control over your frontend output. - -#### Medias -![screenshot](/docs/_media/medias.png) - -```php -@formField('medias', [ - 'name' => 'cover', - 'label' => 'Cover image', - 'note' => 'Minimum image width 1300px' -]) - -@formField('medias', [ - 'name' => 'slideshow', - 'label' => 'Slideshow', - 'max' => 5, - 'note' => 'Minimum image width: 1500px' -]) -``` - -Right after declaring the media formField in the blade template file, you still need to do a few things to make it works properly. - -If the formField is in a static content form, you have to include the `HasMedias` Trait in your module's [Model](https://twill.io/docs/#models) and inlcude `HandleMedias` in your module's [Repository](https://twill.io/docs/#repositories), in addition, you have to uncomment the `$mediasParams` section in your Model file to let the model know about fields you'd like to save from the form. - -Learn more about how Twill's media configurations work at [Model](https://twill.io/docs/#models), [Repository](https://twill.io/docs/#repositories), [Media Library Role & Crop Params](https://twill.io/docs/#image-rendering-service) - -If the formField is used inside a block, you need to define the `mediasParams` at `config/twill.php` under `crops` key, and you are good to go. You could checkout [Twill Default Configuration](https://twill.io/docs/#default-configuration) and [Rendering Blocks](https://twill.io/docs/#rendering-blocks) for references. - -If you need medias fields to be translatable (ie. publishers can select different images for each locale), set the `twill.media_library.translated_form_fields` configuration value to `true`. - -#### Datepicker -![screenshot](/docs/_media/datepicker.png) - -```php -@formField('date_picker', [ - 'name' => 'event_date', - 'label' => 'Event date', - 'minDate' => '2017-09-10 12:00', - 'maxDate' => '2017-12-10 12:00' -]) -``` - -#### Select -![screenshot](/docs/_media/select.png) - -```php -@formField('select', [ - 'name' => 'office', - 'label' => 'Office', - 'placeholder' => 'Select an office', - 'options' => [ - [ - 'value' => 1, - 'label' => 'New York' - ], - [ - 'value' => 2, - 'label' => 'London' - ], - [ - 'value' => 3, - 'label' => 'Berlin' - ] - ] -]) -``` - -#### Select unpacked -![screenshot](/docs/_media/selectunpacked.png) - -```php -@formField('select', [ - 'name' => 'discipline', - 'label' => 'Discipline', - 'unpack' => true, - 'options' => [ - [ - 'value' => 'arts', - 'label' => 'Arts & Culture' - ], - [ - 'value' => 'finance', - 'label' => 'Banking & Finance' - ], - [ - 'value' => 'civic', - 'label' => 'Civic & Public' - ], - [ - 'value' => 'design', - 'label' => 'Design & Architecture' - ], - [ - 'value' => 'education', - 'label' => 'Education' - ], - [ - 'value' => 'entertainment', - 'label' => 'Entertainment' - ], - ] -]) -``` - -#### Multi select -![screenshot](/docs/_media/multiselect.png) - -```php -@formField('multi_select', [ - 'name' => 'sectors', - 'label' => 'Sectors', - 'options' => [ - [ - 'value' => 'arts', - 'label' => 'Arts & Culture' - ], - [ - 'value' => 'finance', - 'label' => 'Banking & Finance' - ], - [ - 'value' => 'civic', - 'label' => 'Civic & Public' - ], - [ - 'value' => 'design', - 'label' => 'Design & Architecture' - ], - [ - 'value' => 'education', - 'label' => 'Education' - ] - ] -]) - -@formField('multi_select', [ - 'name' => 'sectors_bis', - 'label' => 'Sectors bis', - 'min' => 1, - 'max' => 2, - 'options' => [ - [ - 'value' => 'arts', - 'label' => 'Arts & Culture' - ], - [ - 'value' => 'finance', - 'label' => 'Banking & Finance' - ], - [ - 'value' => 'civic', - 'label' => 'Civic & Public' - ], - [ - 'value' => 'design', - 'label' => 'Design & Architecture' - ], - [ - 'value' => 'education', - 'label' => 'Education' - ], - [ - 'value' => 'entertainment', - 'label' => 'Entertainment' - ], - ] -]) -``` - -#### Block editor -![screenshot](/docs/_media/blockeditor.png) - -```php -@formField('block_editor', [ - 'blocks' => ['title', 'quote', 'text', 'image', 'grid', 'test', 'publications', 'news'] -]) -``` - -#### Repeater -![screenshot](/docs/_media/repeater.png) - -```php - - @formField('repeater', ['type' => 'video']) - -``` - -#### Browser -![screenshot](/docs/_media/browser.png) - -```php - - @formField('browser', [ - 'label' => 'Publications', - 'max' => 4, - 'name' => 'publications', - 'moduleName' => 'publications' - ]) - -``` - -#### Files -![screenshot](/docs/_media/files.png) - -```php -@formField('files', [ - 'name' => 'single_file', - 'label' => 'Single file', - 'note' => 'Add one file (per language)' -]) - -@formField('files', [ - 'name' => 'single_file_no_translate', - 'label' => 'Single file (no translate)', - 'note' => 'Add one file', - 'noTranslate' => true, -]) - -@formField('files', [ - 'name' => 'files', - 'label' => 'Files', - 'noTranslate' => true, - 'max' => 4, -]) -``` - -Similar to the media formField, to make the file field works, you have to include the `HasFiles` trait in your module's [Model](https://twill.io/docs/#models), and include `HandleFiles` trait in your module's [Repository](https://twill.io/docs/#repositories). At last, add the `filesParams` configuration array in your model. -```php -public $filesParams = ['file_role', ...]; // a list of file roles -``` - -Learn more at [Model](https://twill.io/docs/#models), [Repository](https://twill.io/docs/#repositories). - -If you are using the file formField in a block, you have to define the `files` key in `config/twill.php` and you are all set, put it under `block_editor` key and at the same level as `crops` key: -```php -return [ - 'block_editor' => [ - 'crops' => [ - ... - ], - 'files' => ['file_role1', 'file_role2', ...] - ] -``` - -#### Map -![screenshot](/docs/_media/map.png) - -```php -@formField('map', [ - 'name' => 'location', - 'label' => 'Location', - 'showMap' => false, -]) -``` - -This field requires that you provide a `GOOGLE_MAPS_API_KEY` variable in your .env file. - -#### Color - -```php -@formField('color', [ - 'name' => 'main-color', - 'label' => 'Main color' -]) -``` - -#### Single checkbox - -```php -@formField('checkbox', [ - 'name' => 'featured', - 'label' => 'Featured' -]) -``` - -#### Multiple checkboxes (multi select as checkboxes) - -```php -@formField('checkboxes', [ - 'name' => 'sectors', - 'label' => 'Sectors', - 'note' => '3 sectors max & at least 1 sector', - 'min' => 1, - 'max' => 3, - 'inline' => true, - 'options' => [ - [ - 'value' => 'arts', - 'label' => 'Arts & Culture' - ], - [ - 'value' => 'finance', - 'label' => 'Banking & Finance' - ], - [ - 'value' => 'civic', - 'label' => 'Civic & Public' - ], - ] -]) -``` - -#### Radios - -```php -@formField('radios', [ - 'name' => 'discipline', - 'label' => 'Discipline', - 'default' => 'civic', - 'inline' => true/false, - 'options' => [ - [ - 'value' => 'arts', - 'label' => 'Arts & Culture' - ], - [ - 'value' => 'finance', - 'label' => 'Banking & Finance' - ], - [ - 'value' => 'civic', - 'label' => 'Civic & Public' - ], - ] -]) +Route::module('yourPluralModuleName', [], [], false); ``` ### Revisions and previewing @@ -1096,7 +666,7 @@ To create a nested module with parent/child relationships, you should include th To install the package: `composer require kalnoy/nestedset` -Then add nested set columns to your database table: +Then add nested set columns to your database table. For Laravel 5.5 and above users: @@ -1132,7 +702,6 @@ Schema::table('pages', function (Blueprint $table) { Your model should use the `Kalnoy\Nestedset\NodeTrait` trait to enable nested sets, as well as the `HasPosition` trait and some helper functions to save a new tree organisation from Twill's drag and drop UI: ```php -use Illuminate\Support\Arr; use A17\Twill\Models\Behaviors\HasPosition; use Kalnoy\Nestedset\NodeTrait; ... diff --git a/docs/.sections/form-fields.md b/docs/.sections/form-fields.md new file mode 100644 index 000000000..a6e4dcb58 --- /dev/null +++ b/docs/.sections/form-fields.md @@ -0,0 +1,805 @@ +## Form fields + +Your module `form` view should look something like this (`resources/views/admin/moduleName/form.blade.php`): + +```php +@extends('twill::layouts.form') +@section('contentFields') + @formField('...', [...]) + ... +@stop +``` + +The idea of the `contentFields` section is to contain your most important fields and, if applicable, the block editor as the last field. + +If you have other fields, like attributes, relationships, extra images, file attachments or repeaters, you'll want to add a `fieldsets` section after the `contentFields` section and use the `@formFieldset` directive to create new ones like in the following example: + +```php +@extends('twill::layouts.form', [ + 'additionalFieldsets' => [ + ['fieldset' => 'attributes', 'label' => 'Attributes'], + ] +]) + +@section('contentFields') + @formField('...', [...]) + ... +@stop + +@section('fieldsets') + @formFieldset([ 'id' => 'attributes', 'title' => 'Attributes']) + @formField('...', [...]) + ... + @endformFieldset +@stop +``` + +The additional fieldsets array passed to the form layout will display a sticky navigation of your fieldset on scroll. +You can also rename the content section by passing a `contentFieldsetLabel` property to the layout. + +### Input +![screenshot](/docs/_media/input.png) + +```php +@formField('input', [ + 'name' => 'subtitle', + 'label' => 'Subtitle', + 'maxlength' => 100, + 'required' => true, + 'note' => 'Hint message goes here', + 'placeholder' => 'Placeholder goes here', +]) + +@formField('input', [ + 'translated' => true, + 'name' => 'subtitle_translated', + 'label' => 'Subtitle (translated)', + 'maxlength' => 250, + 'required' => true, + 'note' => 'Hint message goes here', + 'placeholder' => 'Placeholder goes here', + 'type' => 'textarea', + 'rows' => 3 +]) +``` + +| Option | Description | Type/values | Default value | +| :---------- | :------------------------------------------------------------------------------------------------------------------------| :----------------------------------------| :------------ | +| name | Name of the field | string | | +| label | Label of the field | string | | +| type | Type of input field | text
texarea
number
password | text | +| translated | Defines if the field is translatable | true
false | false | +| maxlength | Max character count of the field | integer | | +| note | Hint message displayed above the field | string | | +| placeholder | Text displayed as a placeholder in the field | string | | +| rows | Sets the number of rows in a textarea | integer | 5 | +| required | Displays an indicator that this field is required
A backend validation rule is required to prevent users from saving | true
false | false | +| disabled | Disables the field | true
false | false | +| readonly | Sets the field as readonly | true
false | false | +| default | Sets a default value if empty | string | | + + +A migration to save an `input` field would be: + +```php +Schema::table('articles', function (Blueprint $table) { + ... + $table->string('subtitle', 100)->nullable(); + ... + +}); +// OR +Schema::table('article_translations', function (Blueprint $table) { + ... + $table->string('subtitle', 250)->nullable(); + ... +}); +``` + +If this `input` field is used for longer strings then the migration would be: + +```php +Schema::table('articles', function (Blueprint $table) { + ... + $table->text('subtitle')->nullable(); + ... +}); +``` + +When used in a [block](https://twill.io/docs/#adding-blocks), no migration is needed. + + +### WYSIWYG +![screenshot](/docs/_media/wysiwyg.png) + +```php +@formField('wysiwyg', [ + 'name' => 'case_study', + 'label' => 'Case study text', + 'toolbarOptions' => ['list-ordered', 'list-unordered'], + 'placeholder' => 'Case study text', + 'maxlength' => 200, + 'note' => 'Hint message', +]) + +@formField('wysiwyg', [ + 'name' => 'case_study', + 'label' => 'Case study text', + 'toolbarOptions' => [ [ 'header' => [1, 2, false] ], 'list-ordered', 'list-unordered', [ 'indent' => '-1'], [ 'indent' => '+1' ] ], + 'placeholder' => 'Case study text', + 'maxlength' => 200, + 'editSource' => true, + 'note' => 'Hint message', +]) +``` +By default, the WYSIWYG field is based on [Quill](https://quilljs.com/). + +You can add all [toolbar options](https://quilljs.com/docs/modules/toolbar/) from Quill with the `toolbarOptions` key. + +For example, this configuration will render a `wysiwyg` field with almost all features from Quill and Snow theme. + +```php + @formField('wysiwyg', [ + 'name' => 'case_study', + 'label' => 'Case study text', + 'toolbarOptions' => [ + ['header' => [2, 3, 4, 5, 6, false]], + 'bold', + 'italic', + 'underline', + 'strike', + ["script" => "super"], + ["script" => "sub"], + "blockquote", + "code-block", + ['list' => 'ordered'], + ['list' => 'bullet'], + ['indent' => '-1'], + ['indent' => '+1'], + ["align" => []], + ["direction" => "rtl"], + 'link', + "clean", + ], + 'placeholder' => 'Case study text', + 'maxlength' => 200, + 'editSource' => true, + 'note' => 'Hint message`', + ]) +``` + +Note that Quill outputs CSS classes in the HTML for certain toolbar modules (indent, font, align, etc.), and that the image module is not integrated with Twill's media library. It outputs the base64 representation of the uploaded image. It is not a recommended way of using and storing images, prefer using one or multiple `medias` form fields or blocks fields for flexible content. This will give you greater control over your frontend output. + + +| Option | Description | Type/values | Default value | +| :------------- | :----------------------------------------------------------- | :--------------------------------------------------------- | :-------------------------------------- | +| name | Name of the field | string | | +| label | Label of the field | string | | +| type | Type of input field | text
texarea
number
email | text | +| toolbarOptions | Array of options/tools that will be displayed in the editor | [Quill options](https://quilljs.com/docs/modules/toolbar/) | bold
italic
underline
link | +| editSource | Displays a button to view source code | true
false | false | +| translated | Defines if the field is translatable | true
false | false | +| maxlength | Max character count of the field | integer | 255 | +| note | Hint message displayed above the field | string | | +| placeholder | Text displayed as a placeholder in the field | string | | +| rows | Sets the number of rows in a textarea | integer | | +| required | Displays an indicator that this field is required
A backend validation rule is required to prevent users from saving | true
false | false | + + +A migration to save a `wysiwyg` field would be: + +```php +Schema::table('articles', function (Blueprint $table) { + ... + $table->text('case_study')->nullable(); + ... + +}); +// OR +Schema::table('article_translations', function (Blueprint $table) { + ... + $table->text('case_study')->nullable(); + ... +}); +``` +When used in a [block](https://twill.io/docs/#adding-blocks), no migration is needed. + +### Medias +![screenshot](/docs/_media/medias.png) + +```php +@formField('medias', [ + 'name' => 'cover', + 'label' => 'Cover image', + 'note' => 'Also used in listings' + 'attachmentNote' => 'Minimum image width: 1500px' +]) + +@formField('medias', [ + 'name' => 'slideshow', + 'label' => 'Slideshow', + 'max' => 5, + 'attachmentNote' => 'Minimum image width: 1500px' +]) +``` + +| Option | Description | Type/values | Default value | +| :------------- | :------------------------------------- | :------------- | :------------ | +| name | Name of the field | string | | +| label | Label of the field | string | | +| translated | Defines if the field is translatable | true
false | false | +| max | Max number of attached items | integer | 1 | +| note | Hint message displayed above the field | string | | +| attachmentNote | Hint message displayed in the field | string | | + + +Right after declaring the `medias` formField in the blade template file, you still need to do a few things to make it works properly. + +If the formField is in a static content form, you have to include the `HasMedias` Trait in your module's [Model](https://twill.io/docs/#models) and inlcude `HandleMedias` in your module's [Repository](https://twill.io/docs/#repositories), in addition, you have to uncomment the `$mediasParams` section in your Model file to let the model know about fields you'd like to save from the form. + +Learn more about how Twill's media configurations work at [Model](https://twill.io/docs/#models), [Repository](https://twill.io/docs/#repositories), [Media Library Role & Crop Params](https://twill.io/docs/#image-rendering-service) + +If the formField is used inside a block, you need to define the `mediasParams` at `config/twill.php` under `crops` key, and you are good to go. You could checkout [Twill Default Configuration](https://twill.io/docs/#default-configuration) and [Rendering Blocks](https://twill.io/docs/#rendering-blocks) for references. + +If you need medias fields to be translatable (ie. publishers can select different images for each locale), set the `twill.media_library.translated_form_fields` configuration value to `true`. + +##### Example: +To add a `medias` form field in a form, first add `$mediaParams` to the model. + +```php + [ + 'default' => [ + [ + 'name' => 'default', + 'ratio' => 16 / 9, + ], + ], + 'mobile' => [ + [ + 'name' => 'mobile', + 'ratio' => 1, + ], + ], + ], + ]; + + ... +} +``` + +Then, add the form field to the `form.blade.php` file. + +```php +@extends('twill::layouts.form') + +@section('contentFields') + + ... + + @formField('medias', [ + 'name' => 'cover', + 'label' => 'Cover image', + ]) + + ... +@stop +``` + +No migration is needed to save `medias` form fields. + + +### Files +![screenshot](/docs/_media/files.png) + +```php +@formField('files', [ + 'name' => 'single_file', + 'label' => 'Single file', + 'note' => 'Add one file (per language)' +]) + +@formField('files', [ + 'name' => 'single_file_no_translate', + 'label' => 'Single file (no translate)', + 'note' => 'Add one file', + 'noTranslate' => true, +]) + +@formField('files', [ + 'name' => 'files', + 'label' => 'Files', + 'noTranslate' => true, + 'max' => 4, +]) +``` + +Similar to the media formField, to make the file field works, you have to include the `HasFiles` trait in your module's [Model](https://twill.io/docs/#models), and include `HandleFiles` trait in your module's [Repository](https://twill.io/docs/#repositories). At last, add the `filesParams` configuration array in your model. +```php +public $filesParams = ['file_role', ...]; // a list of file roles +``` + +Learn more at [Model](https://twill.io/docs/#models), [Repository](https://twill.io/docs/#repositories). + +If you are using the file formField in a block, you have to define the `files` key in `config/twill.php`, put it under `block_editor` key and at the same level as `crops` key: +```php +return [ + 'block_editor' => [ + 'crops' => [ + ... + ], + 'files' => ['file_role1', 'file_role2', ...] + ] +``` + +No migration is needed to save `files` form fields. + + +### Datepicker +![screenshot](/docs/_media/datepicker.png) + +```php +@formField('date_picker', [ + 'name' => 'event_date', + 'label' => 'Event date', + 'minDate' => '2017-09-10 12:00', + 'maxDate' => '2017-12-10 12:00' +]) +``` + +| Option | Description | Type/values | Default value | +| :---------- | :----------------------------------------------------------- | :-------------- | :------------ | +| name | Name of the field | string | | +| label | Label of the field | string | | +| minDate | Minimum selectable date | string | | +| maxDate | Maximum selectable date | string | | +| withTime | Define if the field will display the time selector | true
false | true | +| note | Hint message displayed above the field | string | | +| required | Displays an indicator that this field is required
A backend validation rule is required to prevent users from saving | true
false | false | + + +A migration to save a `date_picker` field would be: + +```php +Schema::table('posts', function (Blueprint $table) { + ... + $table->date('event_date')->nullable(); + ... +}); +// OR +Schema::table('posts', function (Blueprint $table) { + ... + $table->date_time('event_date')->nullable(); + ... +}); +``` + +When used in a [block](https://twill.io/docs/#adding-blocks), no migration is needed. + + +### Select +![screenshot](/docs/_media/select.png) + +```php +@formField('select', [ + 'name' => 'office', + 'label' => 'Office', + 'placeholder' => 'Select an office', + 'options' => [ + [ + 'value' => 1, + 'label' => 'New York' + ], + [ + 'value' => 2, + 'label' => 'London' + ], + [ + 'value' => 3, + 'label' => 'Berlin' + ] + ] +]) +``` + +A migration to save a `select` field would be: + +```php +Schema::table('posts', function (Blueprint $table) { + ... + $table->integer('office')->nullable(); + ... +}); +``` + +When used in a [block](https://twill.io/docs/#adding-blocks), no migration is needed. + +### Select unpacked +![screenshot](/docs/_media/selectunpacked.png) + +```php +@formField('select', [ + 'name' => 'discipline', + 'label' => 'Discipline', + 'unpack' => true, + 'options' => [ + [ + 'value' => 'arts', + 'label' => 'Arts & Culture' + ], + [ + 'value' => 'finance', + 'label' => 'Banking & Finance' + ], + [ + 'value' => 'civic', + 'label' => 'Civic & Public' + ], + [ + 'value' => 'design', + 'label' => 'Design & Architecture' + ], + [ + 'value' => 'education', + 'label' => 'Education' + ], + [ + 'value' => 'entertainment', + 'label' => 'Entertainment' + ], + ] +]) +``` + +| Option | Description | Type/values | Default value | +| :---------- | :----------------------------------------------------------- | :-------------- | :------------ | +| name | Name of the field | string | | +| label | Label of the field | string | | +| options | Array of options for the dropdown, must include _value_ and _label_ | array | | +| unpack | Defines if the select will be displayed as an open list of options | true
false | false | +| note | Hint message displayed above the field | string | | +| placeholder | Text displayed as a placeholder in the field | string | | +| required | Displays an indicator that this field is required
A backend validation rule is required to prevent users from saving | true
false | false | + + +A migration to save the above `select` field would be: + +```php +Schema::table('posts', function (Blueprint $table) { + ... + $table->string('discipline')->nullable(); + ... +}); +``` + +When used in a [block](https://twill.io/docs/#adding-blocks), no migration is needed. + + +### Multi select +![screenshot](/docs/_media/multiselect.png) + +```php +@formField('multi_select', [ + 'name' => 'sectors', + 'label' => 'Sectors', + 'options' => [ + [ + 'value' => 'arts', + 'label' => 'Arts & Culture' + ], + [ + 'value' => 'finance', + 'label' => 'Banking & Finance' + ], + [ + 'value' => 'civic', + 'label' => 'Civic & Public' + ], + [ + 'value' => 'design', + 'label' => 'Design & Architecture' + ], + [ + 'value' => 'education', + 'label' => 'Education' + ] + ] +]) + +@formField('multi_select', [ + 'name' => 'sectors_bis', + 'label' => 'Sectors bis', + 'min' => 1, + 'max' => 2, + 'options' => [ + [ + 'value' => 'arts', + 'label' => 'Arts & Culture' + ], + [ + 'value' => 'finance', + 'label' => 'Banking & Finance' + ], + [ + 'value' => 'civic', + 'label' => 'Civic & Public' + ], + [ + 'value' => 'design', + 'label' => 'Design & Architecture' + ], + [ + 'value' => 'education', + 'label' => 'Education' + ], + [ + 'value' => 'entertainment', + 'label' => 'Entertainment' + ], + ] +]) +``` + +| Option | Description | Type/values | Default value | +| :---------- | :----------------------------------------------------------- | :-------------- | :------------ | +| name | Name of the field | string | | +| label | Label of the field | string | | +| min | Minimum number of selectable options | integer | | +| max | Maximum number of selectable options | integer | | +| options | Array of options for the dropdown, must include _value_ and _label_ | array | | +| note | Hint message displayed above the field | string | | +| placeholder | Text displayed as a placeholder in the field | string | | +| required | Displays an indicator that this field is required
A backend validation rule is required to prevent users from saving | true
false | false | +| disabled | Disables the field | true
false | false | + + +There are several ways to implement a `multi_select` form field. + +##### Multiselect with static values +Sometimes you just have a set of values that are static. + +In this case that it can be implemented as follows: + +- Create the database migration to store a JSON or LONGTEXT: +```php +Schema::table('posts', function (Blueprint $table) { + ... + $table->json('sectors')->nullable(); + ... +}); + +// OR +Schema::table('posts', function (Blueprint $table) { + ... + $table->longtext('sectors')->nullable(); + ... +}); +``` + +- In your model add an accessor and a mutator: +```php +public function getSectorsAttribute($value) +{ + return collect(json_decode($value))->map(function($item) { + return ['id' => $item]; + })->all(); +} + +public function setSectorsAttribute($value) +{ + $this->attributes['sectors'] = collect($value)->filter()->values(); +} +``` + +- Cast the field to `array`: +```php +protected $casts = [ + 'sectors' => 'array' +] +``` + +##### Multiselect with dynamic values + +Sometimes the content for the `multi_select` is coming from another model. + +In this case that it can be implemented as follows: + +- Create a Sectors [module](https://twill.io/docs/#cli-generator) + +``` +php artisan twill:module sectors +``` + +- Create a migration for a pivote table. + +``` +php artisan make:migration create_post_sector_table +``` + +- Use Twill's `createDefaultRelationshipTableFields` to set it up: + +```php +public function up() +{ + Schema::create('post_sector', function (Blueprint $table) { + createDefaultRelationshipTableFields($table, 'sector', 'post'); + $table->integer('position')->unsigned()->index(); + }); +} +``` + +- In your model, add a `belongsToMany` relationship: + +```php +public function sectors() { + return $this->belongsToMany('App\Models\Sector'); +} +``` + +- In your repository, make sure to sync the association when saving: + +```php +public function afterSave($object, $fields) +{ + $object->sectors()->sync($fields['sectors'] ?? []); + + parent::afterSave($object, $fields); +} +``` + +- In your controller, add to the formData the collection of options: +```php +protected function formData($request) +{ + return [ + 'sectors' => app()->make(SectorRepository::class)->listAll() + ]; +} +``` + +- In the form, we can now add the field: +```php +@formField('multi_select', [ + 'name' => 'sectors', + 'label' => 'Sectors', + 'options' => $sectors +]) +``` + +When used in a [block](https://twill.io/docs/#adding-blocks), no migration is needed. + + +### Browser +![screenshot](/docs/_media/browser.png) + +```php +@formFieldset([ 'id' => 'related', 'title' => 'Related']) + @formField('browser', [ + 'label' => 'Publications', + 'max' => 4, + 'name' => 'publications', + 'moduleName' => 'publications' + ]) +@endformFieldset +``` + +### Map +![screenshot](/docs/_media/map.png) + +```php +@formField('map', [ + 'name' => 'location', + 'label' => 'Location', + 'showMap' => false, +]) +``` + +This field requires that you provide a `GOOGLE_MAPS_API_KEY` variable in your .env file. + +### Color + +```php +@formField('color', [ + 'name' => 'main-color', + 'label' => 'Main color' +]) +``` + +### Checkboxes + +```php +@formField('checkbox', [ + 'name' => 'featured', + 'label' => 'Featured' +]) + +@formField('checkboxes', [ + 'name' => 'sectors', + 'label' => 'Sectors', + 'note' => '3 sectors max & at least 1 sector', + 'min' => 1, + 'max' => 3, + 'inline' => true, + 'options' => [ + [ + 'value' => 'arts', + 'label' => 'Arts & Culture' + ], + [ + 'value' => 'finance', + 'label' => 'Banking & Finance' + ], + [ + 'value' => 'civic', + 'label' => 'Civic & Public' + ], + ] +]) +``` + +### Radios + +```php +@formField('radios', [ + 'name' => 'discipline', + 'label' => 'Discipline', + 'default' => 'civic', + 'inline' => true/false, + 'options' => [ + [ + 'value' => 'arts', + 'label' => 'Arts & Culture' + ], + [ + 'value' => 'finance', + 'label' => 'Banking & Finance' + ], + [ + 'value' => 'civic', + 'label' => 'Civic & Public' + ], + ] +]) +``` + +### Block editor +![screenshot](/docs/_media/blockeditor.png) + +```php +@formField('block_editor', [ + 'blocks' => ['title', 'quote', 'text', 'image', 'grid', 'test', 'publications', 'news'] +]) +``` + +See [Block editor](https://twill.io/docs/#block-editor-3) + + +| Option | Description | Type/values | Default value | +| :--------------- | :----------------------------------------------------------- | :------------- | :------------ | +| blocks | Array of blocks | array | | +| withoutSeparator | Defines if a separator before the block editor container should be rendered | true
false | false | + + +### Repeater +![screenshot](/docs/_media/repeater.png) + +```php +@formFieldset([ 'id' => 'videos', 'title' => 'Videos']) + @formField('repeater', ['type' => 'video']) +@endformFieldset + +``` diff --git a/docs/.sections/getting-started/configuration.md b/docs/.sections/getting-started/configuration.md index 387863faa..adfa37b53 100644 --- a/docs/.sections/getting-started/configuration.md +++ b/docs/.sections/getting-started/configuration.md @@ -85,15 +85,16 @@ return [ 'users-management' => true, 'media-library' => true, 'file-library' => true, - 'dashboard' => true, - 'search' => true, 'block-editor' => true, 'buckets' => true, 'users-image' => false, + 'settings' => true, + 'dashboard' => true, + 'search' => true, 'users-description' => false, - 'site-link' => false, - 'settings' => false, 'activitylog' => true, + 'users-2fa' => false, + 'users-oauth' => false, ], ]; ``` @@ -121,15 +122,16 @@ The `media_library` configuration array in `config/twill.php` allows you to prov return [ 'media_library' => [ - 'disk' => 'libraries', + 'disk' => 'twill_media_library', 'endpoint_type' => env('MEDIA_LIBRARY_ENDPOINT_TYPE', 's3'), 'cascade_delete' => env('MEDIA_LIBRARY_CASCADE_DELETE', false), - 'local_path' => env('MEDIA_LIBRARY_LOCAL_PATH'), + 'local_path' => env('MEDIA_LIBRARY_LOCAL_PATH', 'uploads'), 'image_service' => env('MEDIA_LIBRARY_IMAGE_SERVICE', 'A17\Twill\Services\MediaLibrary\Imgix'), 'acl' => env('MEDIA_LIBRARY_ACL', 'private'), 'filesize_limit' => env('MEDIA_LIBRARY_FILESIZE_LIMIT', 50), 'allowed_extensions' => ['svg', 'jpg', 'gif', 'png', 'jpeg'], 'init_alt_text_from_filename' => true, + 'prefix_uuid_with_local_path' => config('twill.file_library.prefix_uuid_with_local_path', false), 'translated_form_fields' => false, ], ]; @@ -167,11 +169,11 @@ AZURE_CONTAINER=AZURE_CONTAINER **Local endpoint** -If you want your uploads to be stored on the server where your Laravel application is running, use the `local` endpoint type. Define the `MEDIA_LIBRARY_LOCAL_PATH` environment variable or the `media_library.local_path` configuration option to provide Twill with your prefered upload path. Always include a trailing slash like in the following example: +If you want your uploads to be stored on the server where your Laravel application is running, use the `local` endpoint type. Define the `MEDIA_LIBRARY_LOCAL_PATH` environment variable or the `media_library.local_path` configuration option to provide Twill with your prefered upload path: ```bash MEDIA_LIBRARY_ENDPOINT_TYPE=local -MEDIA_LIBRARY_LOCAL_PATH=uploads/ +MEDIA_LIBRARY_LOCAL_PATH=uploads ``` To avoid running into `too large` errors when uploading to your server, you can choose to limit uploads through Twill using the `MEDIA_LIBRARY_FILESIZE_LIMIT` environment variable or `filesize_limit` configuration option. It is set to 50mb by default. Make sure to setup your PHP and webserver (apache, nginx, ....) to allow for the upload size specified here. @@ -191,12 +193,16 @@ MEDIA_LIBRARY_CASCADE_DELETE=false The `allowed_extensions` configuration option is an array of file extensions that Twill's media library's uploader will accept. By default, `svg`, `jpg`, `gif`, `png` and `jpeg` extensions are allowed. -**Images url rendering** +**Images rendering** -To render uploaded image urls, Twill's prefered service is [Imgix](https://imgix.com). You can change it using the `MEDIA_LIBRARY_IMAGE_SERVICE` environment variable or the `media_library.image_service` configuration option. +To render uploaded images, Twill's prefered service is [Imgix](https://imgix.com). -A simple local service is available (`A17\Twill\Services\MediaLibrary\Local`) but it will not make use of any cropping or resizing parameters when rendering urls. As noted in the [media library's documentation](#media-library-3), you can implement other third party services (eg. Cloudinary) or open source libraries (eg. Croppa) for that purpose if you do not want or cannot use Imgix for your project. +If you do not want or cannot use a third party service, or have very limited image rendering needs, Twill also provides a local image rendering service powered by [Glide](https://glide.thephpleague.com/). The following .env variables should get you up and running: +```bash +MEDIA_LIBRARY_ENDPOINT_TYPE=local +MEDIA_LIBRARY_IMAGE_SERVICE=A17\Twill\Services\MediaLibrary\Glide +``` #### Imgix @@ -279,14 +285,15 @@ The `file_library` configuration array in `config/twill.php` allows you to provi return [ 'file_library' => [ - 'disk' => 'libraries', + 'disk' => 'twill_file_library', 'endpoint_type' => env('FILE_LIBRARY_ENDPOINT_TYPE', 's3'), 'cascade_delete' => env('FILE_LIBRARY_CASCADE_DELETE', false), - 'local_path' => env('FILE_LIBRARY_LOCAL_PATH'), + 'local_path' => env('FILE_LIBRARY_LOCAL_PATH', 'uploads'), 'file_service' => env('FILE_LIBRARY_FILE_SERVICE', 'A17\Twill\Services\FileLibrary\Disk'), 'acl' => env('FILE_LIBRARY_ACL', 'public-read'), 'filesize_limit' => env('FILE_LIBRARY_FILESIZE_LIMIT', 50), 'allowed_extensions' => [], + 'prefix_uuid_with_local_path' => false, ], ]; ``` diff --git a/docs/.sections/getting-started/installation.md b/docs/.sections/getting-started/installation.md index 7f3e6abae..0956d5ff8 100644 --- a/docs/.sections/getting-started/installation.md +++ b/docs/.sections/getting-started/installation.md @@ -4,36 +4,7 @@ Twill is a package for Laravel applications, installable through Composer: ```bash -composer require area17/twill:"1.2.*" -``` - -Twill will automatically register its service provider if you are using Laravel `>=5.5`. -If you are using Twill with Laravel `5.3` or `5.4`, add Twill's service provider in your application's `config/app.php` file: - -```php{11} - [ - ... - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - - /* - * Package Service Providers... - */ - A17\Twill\TwillServiceProvider::class, - ... - - /* - * Application Service Providers... - */ - App\Providers\AppServiceProvider::class, - App\Providers\AuthServiceProvider::class, - // App\Providers\BroadcastServiceProvider::class, - App\Providers\EventServiceProvider::class, - App\Providers\RouteServiceProvider::class, - ... -]; +composer require area17/twill:"^2.0" ``` #### Artisan @@ -52,13 +23,11 @@ Make sure to setup your .env file with your database credentials and to run it w Twill's `install` command consists of: - creating an `admin.php` routes files in your application's `routes` directory. This is where you will declare your own admin console routes. -- publishing Twill's database migrations to your application's `database/migrations` directory. -- migrating your database with those new migrations. +- migrating your database with Twill's migrations. - publishing Twill's configuration files to your application's `config` directory. - publishing Twill's assets for the admin console UI. - prompting you to create a superadmin user. - #### .env By default, Twill's admin console is available at `admin.domain.test`. This is assuming that your .env `APP_URL` variable does not include a scheme (`http`/`https`): @@ -124,66 +93,6 @@ MEDIA_LIBRARY_IMAGE_SERVICE=A17\Twill\Services\MediaLibrary\Glide See the [media library's configuration documentation](#media-library-2) for more information. -#### npm - -Once you create custom blocks for your admin console, Twill's assets needs to be recompiled to include your generated Vue components. -In order to do that, add the following npm scripts to your project's `package.json`: - -```json -"scripts": { - "twill-build": "rm -f public/hot && npm run twill-copy-blocks && cd vendor/area17/twill && npm ci && npm run prod && cp -R public/* ${INIT_CWD}/public", - "twill-copy-blocks": "npm run twill-clean-blocks && mkdir -p resources/assets/js/blocks/ && cp -R resources/assets/js/blocks/ vendor/area17/twill/frontend/js/components/blocks/customs/", - "twill-clean-blocks": "rm -rf vendor/area17/twill/frontend/js/components/blocks/customs" -} -``` - -Build Twill's admin console UI assets using: - -```bash -npm run twill-build -``` - -:::tip TIP -On Windows, depending on your configuration, you might want to add the `--script-shell bash` option when running `npm` commands. -Read more [here](https://github.com/area17/twill/issues/31#issuecomment-437557464). -::: - -If you don't want to store Twill's compiled assets in Git, add the following to your project `.gitignore` : -``` -public/assets/admin -public/mix-manifest.json -public/hot -``` - -If you are working on adding/modifying blocks for your application, or contributing to Twill itself, and would like to use Hot Module Reloading to propagate changes when recompiling blocks or modifying Twill, add and install the following dev dependencies to your project's `package.json`: - -```json -"devDependencies": { - "concurrently": "^3.5.1", - "watch": "^1.0.2" -} -``` - -And the following npm scripts: - -```json -"scripts": { - "twill-dev": "mkdir -p vendor/area17/twill/public && npm run twill-copy-blocks && concurrently \"cd vendor/area17/twill && npm ci && npm run hot\" \"npm run twill-watch\" && npm run twill-clean-blocks", - "twill-watch": "concurrently \"watch 'npm run twill-hot' vendor/area17/twill/public --wait=2 --interval=0.1\" \"npm run twill-watch-blocks\"", - "twill-hot": "cd vendor/area17/twill && cp -R public/* ${INIT_CWD}/public", - "twill-watch-blocks": "watch 'npm run twill-copy-blocks' resources/assets/js/blocks --wait=2 --interval=0.1" -} -``` - -You can now start a Webpack HMR server on Twill's admin console UI assets using: - -```bash -npm run twill-dev -``` - -This will refresh your browser tab or hot reload code when possible (keeping state when possible too) on any changes to your compiled blocks or Twill's frontend sources. - - #### A note about the frontend On your frontend domain (`domain.test`), nothing changed, and that's ok! Twill does not make any assumptions regarding how you might want to build your own applications. It is up to you to setup Laravel routes that queries content created through Twill's admin console. You can decide to use server side rendering with Laravel's Blade templating and/or to define API endpoints to build your frontend application using any client side solution (eg. Vue, React, Angular, ...). diff --git a/docs/.sections/media-library.md b/docs/.sections/media-library.md index 39d5567b9..cad5d7778 100644 --- a/docs/.sections/media-library.md +++ b/docs/.sections/media-library.md @@ -5,7 +5,7 @@ The media and files libraries currently support S3, Azure and local storage. Head over to the `twill` configuration file to setup your storage disk and configurations. Also check out the direct upload section of this documentation to setup your IAM users and bucket / container if you want to use S3 or Azure as a storage provider. ### Image rendering service -This package currently ships with only one rendering service, [Imgix](https://www.imgix.com/). It is very simple to implement another one like [Cloudinary](http://cloudinary.com/) or even a local service like [Glide](http://glide.thephpleague.com/) or [Croppa](https://github.com/BKWLD/croppa). +This package currently ships with 2 rendering services, [Imgix](https://www.imgix.com/) and [Glide](http://glide.thephpleague.com/). It is very simple to implement another one like [Cloudinary](http://cloudinary.com/) or even another local service like or [Croppa](https://github.com/BKWLD/croppa). You would have to implement the `ImageServiceInterface` and modify your `twill` configuration value `media_library.image_service` with your implementation class. Here are the methods you would have to implement: diff --git a/docs/.sections/other-cms-features.md b/docs/.sections/other-cms-features.md index f76bcb0c0..56309c8b9 100644 --- a/docs/.sections/other-cms-features.md +++ b/docs/.sections/other-cms-features.md @@ -424,7 +424,7 @@ Finally, in your `AuthServiceProvider` class, redefine [Twill's default permissi ]); }); - Gate::define('edit', function ($user) { + Gate::define('edit', function ($user) { return in_array($user->role_value, [ UserRole::CUSTOM3, UserRole::ADMIN, diff --git a/docs/.sections/preface.md b/docs/.sections/preface.md index 4798edd90..916b6decf 100644 --- a/docs/.sections/preface.md +++ b/docs/.sections/preface.md @@ -60,7 +60,7 @@ Built to get out of your way, Twill offers: #### Developer experience * Maintain a Laravel application, not a Twill application -* Support for Laravel 5.3 to 5.8 and will be updated to support all future versions +* Support for Laravel 5.6 to 6 and will be updated to support all future versions * Support for both MySQL and PostgreSQL databases * No conflict with other Laravel packages – keep building with your tools of choice * No specific server requirements, if you can deploy a Laravel application, you can deploy Twill diff --git a/docs/generate_readme.js b/docs/generate_readme.js index 1fb6cee78..63a992b9d 100644 --- a/docs/generate_readme.js +++ b/docs/generate_readme.js @@ -7,6 +7,7 @@ const sections = [ 'getting-started/installation', 'getting-started/configuration', 'crud-modules', + 'form-fields', 'media-library', 'block-editor', 'other-cms-features', @@ -23,4 +24,4 @@ const content = settings + sections.map((section) => { return '\n\n' + fs.readFileSync('./.sections/' + section + '.md', 'utf8') + '\n\n'; }).join(''); -fs.writeFileSync('README.md', content); \ No newline at end of file +fs.writeFileSync('README.md', content);