diff --git a/backend/forms.md b/backend/forms.md index 7bb51bc5..6dcece9b 100644 --- a/backend/forms.md +++ b/backend/forms.md @@ -393,6 +393,45 @@ status: showSearch: false ``` +Dropdowns can also allow users to provide a new value. This can be activated by setting the `allowCustom` option to `true`. + +```yaml +status: + label: Blog Post Status + type: dropdown + allowCustom: true +``` + +The `allowCustom` option can be useful to provide a preset list of options without preventing the user from adding a new or custom value. + +When using the `get*Options` method for defining options, you would be able to take into consideration any custom options present in the data: + +```yaml +status: + label: Blog Post Status + type: dropdown + allowCustom: true +``` + +```php +public function getStatusOptions($value, $formData) +{ + // Prefill the dropdown with the already used statuses: [['status' => 'draft'], ['status' => 'published']] + $statuses = self::distinct('status')->get(); + + // Insert the actual form's model value to avoid it to vanish + // on eventual AJAX call that would refresh the field partial like dependsOn + if ($this->status) { + + // The actual form's status could be a custom like ['status' => 'need review'] + $statuses->add(['status' => $this->status]); + } + + // Return a list of statuses: [['status' => 'draft'], ['status' => 'published'], ['status' => 'need review']] + return $statuses->pluck('status', 'status'); +} +``` + ### Email `email` - renders a single line text box with the type of `email`, triggering an email-specialised keyboard in mobile browsers. @@ -837,6 +876,9 @@ Option | Description > **NOTE:** Unlike the [Media Finder FormWidget](#media-finder), the File Upload FormWidget uses [database file attachments](../database/attachments); so the field name must match a valid `attachOne` or `attachMany` relationship on the Model associated with the Form. **IMPORTANT:** Having a database column with the name used by this field type (i.e. a database column with the name of an existing `attachOne` or `attachMany` relationship) **will** cause this FormWidget to break. Use database columns with the Media Finder FormWidget and file attachment relationships with the File Upload FormWidget. +By default, the File Upload FormWidget only allows a limited set of file extensions. You can extend this list by adding a `fileDefinitions` config in `config/cms.php` file. +See [Allowed file types](../setup/configuration#allowed-file-types) for more information. + ### Icon Picker `iconpicker` - renders an icon picker that is by default powered by the Font Awesome icons included by WinterCMS. diff --git a/cms/mediamanager.md b/cms/mediamanager.md index 2e6c8445..08b18436 100644 --- a/cms/mediamanager.md +++ b/cms/mediamanager.md @@ -241,6 +241,11 @@ Parameter | Value `videoExtensions` | file extensions corresponding to the Video document type. The default value is `['mp4', 'avi', 'mov', 'mpg']`. `audioExtensions` | file extensions corresponding to the Audio document type. The default value is `['mp3', 'wav', 'wma', 'm4a']`. +### Allowing more specific file extensions + +By default, the Media Manager only allows a limited set of file extensions. You can extend this list by adding a `fileDefinitions` config in `config/cms.php` file. +See [Allowed file types](../setup/configuration#allowed-file-types) for more information. + ## Events The Media Manager provides a few [events](../events/introduction) that you can listen for in order to improve extensibility. diff --git a/console/asset-compilation.md b/console/asset-compilation.md index 54fee544..75ad9cb0 100644 --- a/console/asset-compilation.md +++ b/console/asset-compilation.md @@ -268,13 +268,15 @@ Please see the `mix:install` documentation for the available arguments and optio ### List registered Mix packages ```bash -php artisan mix:list +php artisan mix:list [--json] ``` The `mix:list` command will list all registered Mix packages found in the Winter installation. This is useful for determining if your plugin or theme is correctly registered. The command will list all packages, as well as the directory for the asset and the configuration file that has been defined during registration. +A json formatted list of packages can be printed by specifying the `--json` flag. + ### Compile a Mix packages ```bash @@ -285,6 +287,8 @@ The `mix:compile` command compiles all registered Mix packages, running each pac By specifying the `-p` flag, you can compile one or more selected packages. To define multiple packages, simply add more `-p` flags to the end of the command. +The `--no-progress` flag can be added in order to suppress the mix progress output. Useful if you want to only view the webpack output. + By default, all packages are built in "development" mode. If you wish to compile in "production" mode, which may include more optimisations for production sites, add the `-f` or `--production` flag to the command. The command will generate a report of all compiled files and their final size once complete. diff --git a/database/model.md b/database/model.md index 58527f86..687d1c96 100644 --- a/database/model.md +++ b/database/model.md @@ -4,18 +4,18 @@ Winter provides a beautiful and simple Active Record implementation for working with your database, based on [Eloquent by Laravel](http://laravel.com/docs/eloquent). Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table. -Model classes reside in the **models** subdirectory of a plugin directory. An example of a model directory structure: - -```css -📂 plugins - ┗ 📂 acme - ┗ 📂 blog - ┣ 📂 models - ┃ ┣ 📂 user <=== Model config directory - ┃ ┃ ┣ 📜 columns.yaml <=== Model config files - ┃ ┃ ┗ 📜 fields.yaml <==^ - ┃ ┗ 📜 User.php <=== Model class - ┗ 📜 Plugin.php +Model classes reside in the `models` subdirectory of a plugin directory. An example of a model directory structure: + +```treeview +plugins/ +`-- acme/ + `-- blog/ + |-- models/ # Plugin models directory + | |-- user/ # Model configuration directory + | | |-- columns.yaml # Model list columns config file + | | `-- fields.yaml # Model form fields config file + | `-- User.php # Model class + `-- Plugin.php ``` The model configuration directory could contain the model's [list column](../backend/lists#defining-list-columns) and [form field](../backend/forms#defining-form-fields) definitions. The model configuration directory name matches the model class name written in lowercase. diff --git a/database/relations.md b/database/relations.md index 6e1890dd..4010124d 100644 --- a/database/relations.md +++ b/database/relations.md @@ -791,6 +791,8 @@ You may also specify an operator and count to further customize the query: $posts = Post::has('comments', '>=', 3)->get(); ``` +> **NOTE**: MySQL strict mode can sometimes complain when using a `GROUP` column without a `GROUP BY` clause (in the above case, `COUNT()`). You can set the `strict` option to `false` in your database's connection configuration (i.e. `database.connections.mysql.strict`) to ignore this warning message. + Nested `has` statements may also be constructed using "dot" notation. For example, you may retrieve all posts that have at least one comment and vote: ```php diff --git a/markup/filters/str.md b/markup/filters/str.md new file mode 100644 index 00000000..84bba371 --- /dev/null +++ b/markup/filters/str.md @@ -0,0 +1,75 @@ +# Strings + +Winter CMS provides Twig filters for working with strings. These filters are identical to the global PHP helper functions that are documented in [Helpers > Strings](../../docs/services/helpers#strings) section. + +## camel + +The `camel` filter converts the given string to camelCase: + +```twig +{{ 'test test'|camel }} + +{# testTest #} +``` + +## finish + +The `finish` filter adds a single instance of the given value to a string: + +```twig +{{ 'test'|finish('/') }} + +{# test/ #} +``` + +## plural + +The `plural` filter converts a string to its plural form. This filter currently only supports the English language: + +```twig +{{ 'test'|plural }} + +{# tests #} +``` + +## singular + +The `singular` filter converts a string to its singular form. This filter currently only supports the English language: + +```twig +{{ 'letters'|singular }} + +{# letter #} +``` + +## slug + +The `slug` filter generates a URL friendly "slug" from the given string: + +```twig +{{ 'test test'|slug }} {# test-test #} + +{{ 'test_test'|slug }} {# test-test #} + +{{ 'test test'|slug('+') }} {# test+test #} +``` + +## snake + +The `snake` filter converts the given string to snake_case: + +```twig +{{ 'test test'|snake }} + +{# test_test #} +``` + +## studly + +The `studly` filter converts the given string to StudlyCase: + +```twig +{{ 'test test'|studly }} + +{# TestTest #} +``` diff --git a/markup/filters/time.md b/markup/filters/time.md new file mode 100644 index 00000000..42a915cc --- /dev/null +++ b/markup/filters/time.md @@ -0,0 +1,23 @@ +# time_since and time_tense + +The `time_since` and `time_tense` filters format time. + +## time_since + +Formats a human readable time difference from the value to the current time. Eg: **10 minutes ago** + +```twig +{{ '2021-03-06 14:25:55'|time_since }} + +{{ 'February 14, 2024 14:30'|time_since }} +``` + +## time_tense + +Formats 24-hour time and the day using the grammatical tense of the current time. Eg: Today at 12:49, Yesterday at 4:00 or 18 Sep 2015 at 14:33. + +```twig +{{ '2021-03-06 14:25:55'|time_tense }} +``` + +> **NOTE:** To format dates in Twig there is a [date filter](https://twig.symfony.com/doc/3.x/filters/date.html) diff --git a/markup/filters/trans.md b/markup/filters/trans.md index 5a1980a2..491aea49 100644 --- a/markup/filters/trans.md +++ b/markup/filters/trans.md @@ -4,10 +4,10 @@ description: "Documentation on the 'trans' Twig filter." --- # trans -The `| trans` and `| trans_choice` filters translate the value passed in using the applications localization configuration. The localization strings can be loaded by passing the default translation of your string. +The `| trans` and `| transchoice` filters translate the value passed in using the applications localization configuration. The localization strings can be loaded by passing the default translation of your string. ```twig -{{ 'I love Winter CMS.' | trans }}; +{{ 'I love Winter CMS.' | trans }} ``` or an example using a [language variable](../../docs/plugin/localization): @@ -24,14 +24,14 @@ Replacing parameters in translation strings is possible by passing an array as t ## Pluralization -The `trans_choice` function is used to process pluralized values. +The `transchoice` function is used to process pluralized values. ```twig -{{ 'There is one snowflake|There are many snowflakes' | trans_choice(7) }} +{{ 'There is one snowflake|There are many snowflakes' | transchoice(7) }} ``` The second argument can contain the parameters. ```twig -{{ '{1} :value minute ago|[2,*] :value minutes ago' | trans_choice(5, { value: 5 }) }} +{{ '{1} :value minute ago|[2,*] :value minutes ago' | transchoice(5, { value: 5 }) }} ``` diff --git a/markup/functions/url.md b/markup/functions/url.md new file mode 100644 index 00000000..ef232a07 --- /dev/null +++ b/markup/functions/url.md @@ -0,0 +1,51 @@ +# url_*() + +## url() + +The `url()` function generates a fully qualified URL to the given path. + +```twig +{{ url('blog') }} + +{# https://site.com/blog #} +``` + +You can specify a variable as a function parameter: + +```twig +{{ url(category.slug) }} + +{# https://site.com/slug-value #} +``` + +In function parameters you can use concatenation: + +```twig +{{ url('blog/post/' ~ post.id) }} + +{# https://site.com/blog/post/123 #} +``` + +### Base URL + +You can get the base url like this: + +```twig +{{ url('/') }} + +{# https://site.com/ #} +``` + +## url_current() + +The `url_current()` function generates a fully qualified URL to the current path. Syntax: + +```twig +{{ url_current() }} +``` + +An example of generating a canonical link using the `url_current()` function and the `|lower` Twig filter: + +```twig + +``` diff --git a/markup/toc.yaml b/markup/toc.yaml index fb640cad..df9987d9 100644 --- a/markup/toc.yaml +++ b/markup/toc.yaml @@ -42,7 +42,9 @@ sections: filters/page: "| page" filters/raw: "| raw" filters/resize: "| resize" + filters/str: "| str*" filters/theme: "| theme" + filters/time: "| time_[since|tense]" filters/trans: "| trans" Functions: @@ -51,3 +53,4 @@ sections: functions/form: "form_*()" functions/html: "html()" functions/dump: "dump()" + functions/url: "url_*()" diff --git a/plugin/components.md b/plugin/components.md index 4119bec8..841d58ae 100644 --- a/plugin/components.md +++ b/plugin/components.md @@ -28,7 +28,7 @@ namespace Acme\Blog\Components; class BlogPosts extends \Cms\Classes\ComponentBase { - public function componentDetails() + public function componentDetails(): array { return [ 'name' => 'Blog Posts', @@ -68,10 +68,10 @@ You would be able to access its `posts` method through the `blogPosts` variable. Components must be registered by overriding the `registerComponents` method inside the [Plugin registration class](registration#registration-file). This tells the CMS about the Component and provides a **short name** for using it. An example of registering a component: ```php -public function registerComponents() +public function registerComponents(): array { return [ - 'Winter\Demo\Components\Todo' => 'demoTodo' + \Acme\Blog\Components\Todo::class => 'demoTodo' ]; } ``` @@ -83,16 +83,16 @@ This will register the Todo component class with the default alias name **demoTo When you add a component to a page or layout you can configure it using properties. The properties are defined with the `defineProperties` method of the component class. The next example shows how to define a component property: ```php -public function defineProperties() +public function defineProperties(): array { return [ 'maxItems' => [ - 'title' => 'Max items', - 'description' => 'The most amount of todo items allowed', - 'default' => 10, - 'type' => 'string', - 'validationPattern' => '^[0-9]+$', - 'validationMessage' => 'The Max Items property can contain only numeric symbols' + 'title' => 'Max items', + 'description' => 'The most amount of todo items allowed', + 'default' => 10, + 'type' => 'string', + 'validationPattern' => '^[0-9]+$', + 'validationMessage' => 'The Max Items property can contain only numeric symbols', ] ]; } @@ -146,7 +146,7 @@ A `dropdown` allows you to select a single value from a series of options. A `se The option list for `dropdown` and `set` properties can be static or dynamic. Static options are defined with the `options` property for dropdowns and the `items` property for sets. Example: ```php -public function defineProperties() +public function defineProperties(): array { return [ 'units' => [ @@ -173,7 +173,7 @@ public function defineProperties() The list of options or items could be fetched dynamically from the server when the Inspector is displayed. If the `options` parameter is omitted for dropdowns or the `items` parameter is omitted for sets, the list is considered dynamic. The component class must define a method returning this list. The method should have a name in the following format: `get*Property*Options`, where **Property** is the property name, for example: `getCountryOptions`. The method returns an array of options with the option values as keys and option labels as values. Example of a dynamic dropdown list definition: ```php -public function defineProperties() +public function defineProperties(): array { return [ 'country' => [ @@ -193,7 +193,7 @@ public function getCountryOptions() Dynamic `dropdown` and `set` lists can depend on other properties. For example, the state list could depend on the selected country. The dependencies are declared with the `depends` parameter in the property definition. The next example defines two dynamic dropdown properties and the state list depends on the country: ```php -public function defineProperties() +public function defineProperties(): array { return [ 'country' => [ @@ -233,7 +233,7 @@ public function getStateOptions() Sometimes components need to create links to the website pages. For example, the blog post list contains links to the blog post details page. In this case the component should know the post details page file name (then it can use the [page Twig filter](/docs/v1.2/markup/filters/page)). Winter includes a helper for creating dynamic dropdown page lists. The next example defines the postPage property which displays a list of pages: ```php -public function defineProperties() +public function defineProperties(): array { return [ 'postPage' => [ diff --git a/services/asset-compilation.md b/services/asset-compilation.md index 4992e547..8182e260 100644 --- a/services/asset-compilation.md +++ b/services/asset-compilation.md @@ -105,7 +105,7 @@ The following aliases are supported: Alias | Description ------------- | ------------- -`@jquery` | Reference to the jQuery library (v3.4.0) used in the backend. (JavaScript) +`@jquery` | Reference to the jQuery library (v3.7.1) used in the backend. (JavaScript) `@framework` | AJAX framework extras, subsitute for `{% framework %}` tag. (JavaScript) `@framework.extras` | AJAX framework extras, subsitute for `{% framework extras %}` tag. (JavaScript, CSS) `@framework.extras.js` | AJAX framework extras, (JavaScript) diff --git a/services/image-resizing.md b/services/image-resizing.md index 9022dd62..b5ba65f5 100644 --- a/services/image-resizing.md +++ b/services/image-resizing.md @@ -42,6 +42,7 @@ Key | Description | Default | Options `offset` | Offset the crop of the resized image | `[0,0]` | `[left, top]` `quality` | Quality of the resized image | `90` | `0-100` `sharpen` | Amount to sharpen the image | `0` | `0-100` +`extension` | Converts an image to the specified format | Inherits | `webp`, `jpg`, `png`, `gif` ## Available Modes diff --git a/setup/configuration.md b/setup/configuration.md index 5001e9eb..a3c86668 100644 --- a/setup/configuration.md +++ b/setup/configuration.md @@ -327,6 +327,29 @@ The `trustedProxyHeaders` value specifies which headers will be allowed to defin > 'trustedProxyHeaders' => Illuminate\Http\Request::HEADER_X_FORWARDED_AWS_ELB > ``` +### Allowed file types + +Winter CMS is preset to allow only certain file types in the media manager and file upload form widgets (see: `WinterStorm\Filesystem\Definitions`), but you can configure them to suit your needs by adding a `fileDefinitions` configuration value to your `config/cms.php` file. + +File types are grouped into five categories: + +- defaultExtensions: Extensions that are particularly benign. +- assetExtensions: Extensions seen as public assets. +- imageExtensions: Extensions typically used as images. +- videoExtensions: Extensions typically used as videos. +- audioExtensions: Extensions typically used as audios. + +The following example shows how to extend the default extensions group to add the `fig` and `jam` file extensions. + +```php +'fileDefinitions' => [ + 'defaultExtensions' => array_merge( + \Winter\Storm\Filesystem\Definitions::get('defaultExtensions'), + ['fig', 'jam'] + ), +] +``` + ## Environment configuration ### Defining a base environment diff --git a/setup/upgrade-guide.md b/setup/upgrade-guide.md index 033850ed..ac8e6bae 100644 --- a/setup/upgrade-guide.md +++ b/setup/upgrade-guide.md @@ -96,7 +96,7 @@ The `server.php` file located in your project root folder is no longer required > **Impacts:** Most users. -There have been signifcant changes to the default configuration files, mainly in those that are based on the Laravel framework's configuration files. You should review the changes to the [default configuration files](https://github.com/wintercms/winter/tree/wip/1.2/config) and implement any changes as desired. +There have been significant changes to the default configuration files, mainly in those that are based on the Laravel framework's configuration files. You should review the changes to the [default configuration files](https://github.com/wintercms/winter/tree/1.2/config) and implement any changes as desired. The following items have the highest likelihood of having an impact on your projects: diff --git a/ui/controls/form.md b/ui/controls/form.md index 64bb7217..1a8f3358 100644 --- a/ui/controls/form.md +++ b/ui/controls/form.md @@ -3,7 +3,7 @@ title: "Controls: Form" --- # Form -The form is the base element of data entry within Winter CMS. A form is made up of controls, which can be input fields or widgets. In general, the form is generally created by the [Form Widget](/v1.2/docs/backend/forms), but can also be manually created for custom uses. +The form is the base element of data entry within Winter CMS. A form is made up of controls, which can be input fields or widgets. In general, the form is generally created by the [Form Widget](../../backend/forms), but can also be manually created for custom uses. ## The basics