Skip to content

Commit 18f6941

Browse files
committed
Merge branch 'develop' into 1.2
2 parents 1246411 + 55007a8 commit 18f6941

10 files changed

Lines changed: 111 additions & 12 deletions

File tree

architecture/maintainer-guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The project is considered a flat-level of management with equal responsibility o
1616
- [@bennothommo](https://github.com/bennothommo) - Ben Thomson
1717
- [@mjauvin](https://github.com/mjauvin) - Marc Jauvin
1818
- [@jaxwilko](https://github.com/jaxwilko) - Jack Wilkinson
19+
- [@damsfx](https://github.com/damsfx) - Damien Mathieu
1920

2021
Luke Towers has been designated the Lead Maintainer by the maintainer group, as the longest-serving maintainer of October CMS.
2122

architecture/using-composer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ Laravel packages will often provide configuration files, and they will usually c
185185

186186
However, this can create problems with Winter's plugin oriented design, since there would now be random config files in the core `/config` directory. In order to solve this problem, it is recommended that you proxy the included package's configuration through your plugin instead.
187187

188-
You may place this code in your Plugin registration file and call it from the the `boot()` method.
188+
You may place this code in your Plugin registration file and call it from the `boot()` method.
189189

190190
```php
191191
public function bootPackages()

backend/controllers-ajax.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
The Winter CMS backend implements the MVC pattern. Controllers manage backend pages and implement various features like forms and lists. This article describes how to develop backend controllers and how to configure controller behaviors.
66

7-
Each controller consists of a PHP file which resides in the the **/controllers** subdirectory of a Plugin directory. Controller views are `.php` files that reside in the controller view directory. The controller view directory name matches the controller class name written in lowercase. The view directory can also contain controller configuration files. An example of a controller directory structure:
7+
Each controller consists of a PHP file which resides in the **/controllers** subdirectory of a Plugin directory. Controller views are `.php` files that reside in the controller view directory. The controller view directory name matches the controller class name written in lowercase. The view directory can also contain controller configuration files. An example of a controller directory structure:
88

99
```treeview
1010
plugins/
@@ -44,7 +44,7 @@ The backend controller base class defines a number of properties that allow to c
4444
Property | Description
4545
------------- | -------------
4646
`$fatalError` | allows to store a fatal exception generated in an action method in order to display it in the view.
47-
`$user` | contains a reference to the the backend user object.
47+
`$user` | contains a reference to the backend user object.
4848
`$suppressView` | allows to prevent the view display. Can be updated in the action method or in the controller constructor.
4949
`$params` | an array of the routed parameters.
5050
`$action` | a name of the action method being executed in the current request.

backend/forms.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,10 @@ There are various native field types that can be used for the **type** setting.
282282
- [Range](#range)
283283
- [Section](#section)
284284
- [Switch](#switch)
285+
- [Tel](#tel)
285286
- [Text](#text)
286287
- [Textarea](#textarea)
288+
- [URL](#url)
287289
- [Widget](#widget)
288290

289291
</div>
@@ -681,6 +683,34 @@ show_content:
681683
off: myauthor.myplugin::lang.models.mymodel.show_content.off
682684
```
683685

686+
### Tel
687+
688+
`tel` – renders a single-line input for phone values with built-in browser validation.
689+
690+
```yaml
691+
telephone:
692+
label: Phone number
693+
type: tel
694+
pattern: "[0-9]{3}-[0-9]{3}-[0-9]{4}"
695+
placeholder: xxx-xxx-xxxx
696+
maxlength: 20
697+
minlength: 12
698+
size: 20
699+
required: true
700+
options:
701+
514-123-4567: First Test Phone Number
702+
800-111-2222: Second Test Phone Number
703+
```
704+
705+
- Displays a phone icon on the left in both edit and preview modes.
706+
- In preview mode, renders as a clickable link that opens a phone dialing screen on mobile.
707+
- Supports all standard HTML5 attributes for `<input type="tel">`:
708+
- `placeholder`, `maxlength`, `minlength`, `pattern`, `size`, `list`, `autocomplete`, `required`, `readonly`, `disabled`
709+
- `options` will be rendered in a `<datalist>` element, enabling autocomplete suggestions.
710+
- If an option's value and label are identical, the label is omitted for brevity.
711+
712+
See [Defining field options](#defining-field-options) for the different methods to specify the options.
713+
684714
### Text
685715

686716
`text` - renders a single line text box. This is the default type used if none is specified.
@@ -1816,7 +1846,7 @@ Sometimes you may wish to modify the default form behavior and there are several
18161846

18171847
Several controller methods can called at various points during the lifecycle of the `FormController` to provide injection points for custom logic. See the [API docs](/docs/v1.2/api/Backend/Behaviors/FormController#method-formbeforesave) for a full reference of what they are. Generally speaking any method in the API docs prefixed with `form` can be overridden in your controller to change the default behaviour or act as an injection point for custom logic.
18181848

1819-
> **NOTE:** It may be more desirable to use [model events](/docs/v1.2/api/events/model/beforeSave) to implement your logic instead as those are always run when applicable if the model is being affected, no matter where the interaction with the model is occuring.
1849+
> **NOTE:** It may be more desirable to use [model events](/docs/v1.2/api/events/model/beforeSave) to implement your logic instead as those are always run when applicable if the model is being affected, no matter where the interaction with the model is occurring.
18201850

18211851
### Overriding controller action
18221852

backend/lists.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ Option | Description
7676
`showTotals` | displays the summed values for the columns in the form of `totalOnPage (totalForQuery)` in the list header and footer. Default: `true`.
7777
`treeExpanded` | if tree nodes should be expanded by default. Default: `false`.
7878
`customViewPath` | specify a custom view path to override partials used by the list, optional.
79+
`sortable` | enables drag-and-drop reordering of records directly in the list, see [reordering records](#reordering-records). Default: `false`.
80+
81+
### Reordering records
82+
83+
Set `sortable` to `true` to let backend users reorder the list with drag-and-drop. The list model must use the [`Sortable` trait](../database/traits#sortable) so it has a `sort_order` column.
84+
85+
```yaml
86+
sortable: true
87+
```
88+
89+
When enabled, a drag handle is shown on each row, column header sorting is disabled, the list is shown in its stored order without pagination, and dropping a row persists the new order to the model's sort order column via AJAX.
90+
91+
Because the whole list must be visible in a single fixed order, `sortable` cannot be combined with searching, filtering, pagination, or a custom `defaultSort` — configuring any of these alongside it raises a configuration error.
92+
93+
> **NOTE:** Reordering applies to flat lists. For reordering tree structures, or for a dedicated standalone reordering page, use the [Reorder behavior](reorder).
7994

8095
### Adding a toolbar
8196

backend/relations.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ Option | Type | Description
114114
`recordUrl` | List | link each list record to another page. Eg: **users/update/:id**. The `:id` part is replaced with the record identifier.
115115
`customViewPath` | List | specify a custom view path to override partials used by the list.
116116
`recordOnClick` | List | custom JavaScript code to execute when clicking on a record.
117+
`sortable` | List | enables drag-and-drop reordering of the related records, see [reordering relations](#reordering-relations). Requires the parent model to use the [`HasSortableRelations` trait](../database/traits#hassortablerelations). Default: `false`.
117118
`toolbarPartial` | Both | a reference to a controller partial file with the toolbar buttons. Eg: **_relation_toolbar.htm**. This option overrides the *toolbarButtons* option.
118119
`toolbarButtons` | Both | the set of buttons to display. This can be formatted as an array or a pipe separated string, or set to `false` to show no buttons. Available options are: `create`, `update`, `delete`, `add`, `remove`, `refresh`, `link`, & `unlink`. Example: `add\|remove`. <br/> Additionally, you can customize the text inside these buttons by setting this property to an associative array, with the key being the button type and the value being the text for that button. Example: `create: 'Assign User'`. The value also supports translation.
119120

@@ -289,6 +290,23 @@ phone:
289290
list: $/acme/user/models/phone/columns.yaml
290291
```
291292

293+
### Reordering relations
294+
295+
Pivot-based relations (`belongsToMany`, `morphToMany`, `morphedByMany`) can be reordered with drag-and-drop directly in the relation manager. The parent model must use the [`HasSortableRelations` trait](../database/traits#hassortablerelations) and declare the relation in its `$sortableRelations` property, and the pivot table must have a sort order column. Then set `sortable: true` on the relation's `view` configuration:
296+
297+
```yaml
298+
authors:
299+
label: Author
300+
view:
301+
list: $/acme/blog/models/author/columns.yaml
302+
toolbarButtons: link|unlink
303+
sortable: true
304+
```
305+
306+
A drag handle is shown on each related record; dropping persists the new order to the pivot's sort order column. Reordering also works while the parent record is being created, before it is saved — the order is stored against the [deferred binding](../database/relations#deferred-binding) and committed together with the record.
307+
308+
As with sortable lists, the related records are shown as a single unpaginated set in their stored order, so `sortable` cannot be combined with searching, filtering, pagination, or a custom `defaultSort` on the relation's `view` configuration.
309+
292310
## Displaying a relation manager
293311

294312
Before relations can be managed on any page, the target model must first be initialized in the controller by calling the `initRelation` method.

backend/reorder.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
The **Reorder behavior** is a controller [behavior](../services/behaviors) that provides features for sorting and reordering database records. The behavior provides a page called Reorder using the controller action `reorder`. This page displays a list of records with a drag handle allowing them to be sorted and in some cases restructured.
66

7+
> **NOTE:** To let users reorder records inline with drag-and-drop without a dedicated page — directly in a [list](lists#reordering-records) or a [relation manager](relations#reordering-relations) — see those sections. The Reorder behavior documented here provides a dedicated standalone page, which is best suited to models with deep tree structures.
8+
79
The behavior depends on a [model class](../database/model) which must implement one of the following [model traits](../database/traits):
810

911
1. [`Winter\Storm\Database\Traits\Sortable`](../database/traits#sortable)

database/traits.md

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -838,16 +838,49 @@ class ApiData extends Model
838838

839839
## HasSortableRelations
840840

841-
Add this trait to your model in order to allow its relations to be sorted/reordered.
841+
Sorted relations store a sort order value in the pivot table of a `belongsToMany`, `morphToMany`, or `morphedByMany` relation, so the related records keep a custom order for each parent record. Apply the `Winter\Storm\Database\Traits\HasSortableRelations` trait and define a `$sortableRelations` property that maps each relation name to its pivot sort order column.
842842

843843
```php
844-
class MyModel extends model
844+
class Article extends \Winter\Storm\Database\Model
845845
{
846846
use \Winter\Storm\Database\Traits\HasSortableRelations;
847847

848848
/**
849-
* @var array Relations that can be sorted/reordered and the column name to use for sorting/reordering.
849+
* @var array Relations that can be reordered and the pivot column used for sorting.
850850
*/
851-
public $sortableRelations = ['relation_name' => 'sort_order_column'];
852-
...
851+
public $sortableRelations = ['authors' => 'sort_order'];
852+
853+
public $belongsToMany = [
854+
'authors' => [
855+
\Acme\Blog\Models\Author::class,
856+
'table' => 'acme_blog_articles_authors',
857+
],
858+
];
853859
}
860+
```
861+
862+
Ensure the pivot table has the sort order column, for example in a migration:
863+
864+
```php
865+
$table->integer('sort_order')->default(0);
866+
```
867+
868+
You must create the column yourself (as in the migration above) — the trait does not create it. When the model boots, the trait includes that column in the relation's pivot data, so its value is loaded onto each record's `pivot`, and applies an `order by {pivot_table}.{column} asc` clause so the relation is always returned in its stored order. The trait also assigns the next sort order value to newly attached records, appending them to the end of the relation.
869+
870+
Use the `setRelationOrder` method to reorder a relation programmatically. The second argument is the related record ids in their new order; the optional third argument provides the sort order value to assign to each (when omitted, a sequential `1..N` order is assigned in the given order):
871+
872+
```php
873+
// Reorder by ids only — assigns sort orders 1, 2, 3 in the given order
874+
$article->setRelationOrder('authors', [$author3->id, $author1->id, $author2->id]);
875+
876+
// Reorder by ids (second argument) with explicit sort order values (third argument)
877+
$article->setRelationOrder('authors', [$author1->id, $author2->id, $author3->id], [3, 2, 1]);
878+
```
879+
880+
You can check whether a relation is configured as sortable with `isSortableRelation`:
881+
882+
```php
883+
$article->isSortableRelation('authors'); // true
884+
```
885+
886+
> **NOTE:** To let backend users reorder a relation with drag-and-drop directly in a form, see [reordering relations](../backend/relations#reordering-relations) in the RelationController documentation.

snowboard/extras.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ class Gallery extends Snowboard.PluginBase {
413413
}
414414
```
415415

416-
In the example above, even if the gallery element defines a `data-num-images` data attribute, this will be overriden by the `userNumImages` parameter that is used in constructing the gallery. This `userNumImages` parameter could be populated by a user configuration source.
416+
In the example above, even if the gallery element defines a `data-num-images` data attribute, this will be overridden by the `userNumImages` parameter that is used in constructing the gallery. This `userNumImages` parameter could be populated by a user configuration source.
417417

418418
### Methods
419419

@@ -429,7 +429,7 @@ this.config.get(); // Returns an object of all configuration options and their v
429429

430430
#### `get(configName: string)`
431431

432-
Gets the configuration value for the given configuration name. This will be retrieved from the local configuration first, then the data attribute of the element providing the configuration then finally from the the defaults if not specified on the element.
432+
Gets the configuration value for the given configuration name. This will be retrieved from the local configuration first, then the data attribute of the element providing the configuration then finally from the defaults if not specified on the element.
433433

434434
If the configuration has been provided a local configuration value for the config name, it will be returned over all other sources.
435435

snowboard/request.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Finally, the following option parameters define override functionality for vario
7171
Option | Parameters | Description
7272
------ | ---------- | -----------
7373
`handleConfirmMessage` | `(string) confirmationMessage` | Handles any confirmations requested of the user.
74-
`handleErrorMessage` | `(string) errorMessage` | Handles any errors occuring during the request
74+
`handleErrorMessage` | `(string) errorMessage` | Handles any errors occurring during the request
7575
`handleValidationMessage` | `(string) message, (Object) fieldMessages` | Handles validation errors occurring during the request. `fieldMessages` has field names as the key and messages as the value.
7676
`handleFlashMessage` | `(string) message, (string) type` | Handles flash messages.
7777
`handleRedirectResponse` | `(string) redirectUrl` | Handles redirect responses.

0 commit comments

Comments
 (0)