Skip to content

Commit

Permalink
Merge pull request #168 from wintercms/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
LukeTowers authored Dec 28, 2023
2 parents 361ac90 + 8db5657 commit ae9d38f
Show file tree
Hide file tree
Showing 20 changed files with 226 additions and 66 deletions.
20 changes: 11 additions & 9 deletions architecture/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,13 +228,15 @@ Element classes names should use hyphen-case (dashes)
Partial views should begin with an underscore character. Whereas Controller and Layout views do not begin with an underscore character. Since views are often found in a single folder, the underscore (_) and dash (-) characters can be used to organise the files. A dash is used as a substitute for a space character. An underscore is used as a substitute for a slash character (folder or namespace).

```
index_fancy-layout.htm <== Index\Fancy layout
form-with-sidebar.htm <== Form with sidebar
_field-container.htm <== Field container (partial)
_field_baloon-selector.htm <== Field\Baloon Selector (partial)
index_fancy-layout.php <== Index\Fancy layout
form-with-sidebar.php <== Form with sidebar
_field-container.php <== Field container (partial)
_field_baloon-selector.php <== Field\Baloon Selector (partial)
```

View files must end with the `.htm` file extension.
View files must end with the `.php` file extension.

> **NOTE:** For backwards compatibilty, we still support the `.htm` legacy file extension. It is recommended to use `.php` for any new files.
### Class naming

Expand Down Expand Up @@ -540,7 +542,7 @@ folder/
| |-- sub2folder/
| | `-- sub3file # A comment about this file
| `-- sub2folder2/
|-- index.htm
|-- index.php
`-- .hidden_file # This file is hidden and should be slightly transparent
```</code></pre>

Expand All @@ -557,7 +559,7 @@ folder/
| |-- sub2folder/
| | `-- sub3file # A comment about this file
| `-- sub2folder2/
|-- index.htm
|-- index.php
`-- .hidden_file # This file is hidden and should be slightly transparent
```

Expand All @@ -577,7 +579,7 @@ This feature also supports the output of the `tree` command-line utility which i

# folder/
# ├── .hidden_file
# ├── index.htm
# ├── index.php
# ├── subfolder1/
# ├── subfolder2/
# │ ├── document.pdf
Expand All @@ -601,7 +603,7 @@ folder/
│ └── sub2folder/
│ └── sub3file
├── .hidden_file
└── index.htm
└── index.php
```

> **NOTE:** The `tree` command may print out indented lines using a character that looks to be a space character, but is not. If this is the case, you may need to add the `--charset=ascii` option to the command, which will print a diagram similar to the first example.
8 changes: 4 additions & 4 deletions backend/controllers-ajax.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@

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.

Each controller consists of a PHP file which resides in the the **/controllers** subdirectory of a Plugin directory. Controller views are `.htm` 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:
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:

```treeview
plugins/
`-- acme/
`-- blog/
|-- controllers/
| |-- users/ # Controller view directory
| | |-- _partial.htm # Controller partial file
| | |-- _partial.php # Controller partial file
| | |-- config_form.yaml # Controller config file
| | `-- index.htm # Controller view file
| | `-- index.php # Controller view file
| `-- Users.php # Controller class
`-- Plugin.php
```
Expand Down Expand Up @@ -57,7 +57,7 @@ Property | Description

## Actions, views and routing

Public controller methods, called **actions** are coupled to **view files** which represent the page corresponding the action. Backend view files use PHP syntax. Example of the **index.htm** view file contents, corresponding to the **index** action method:
Public controller methods, called **actions** are coupled to **view files** which represent the page corresponding the action. Backend view files use PHP syntax. Example of the **index.php** view file contents, corresponding to the **index** action method:

```html
<h1>Hello World</h1>
Expand Down
22 changes: 21 additions & 1 deletion backend/forms.md
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ There are various form widgets included as standard, although it is common for p
- [Nested Form](#nested-form)
- [Record finder](#record-finder)
- [Relation](#relation)
- [Relation Manager](#relation-manager)
- [Repeater](#repeater)
- [Rich editor / WYSIWYG](#rich-editor--wysiwyg)
- [Sensitive](#sensitive)
Expand Down Expand Up @@ -831,7 +832,7 @@ Option | Description
`maxFilesize` | file size in Mb that are accepted by the uploader, optional. Default: from "upload_max_filesize" param value
`useCaption` | allows a title and description to be set for the file. Default: true
`prompt` | text to display for the upload button, applies to files only, optional.
`thumbOptions` | options to pass to the thumbnail generating method for the file
`thumbOptions` | options to pass to the thumbnail generating method for the file. See [Image Resizing](../services/image-resizing#available-parameters)
`attachOnUpload` | Automatically attaches the uploaded file on upload if the parent record exists instead of using deferred binding to attach on save of the parent record. Defaults to false.

> **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.
Expand Down Expand Up @@ -1025,6 +1026,25 @@ Option | Description
`emptyOption` | text to display when there is no available selections.
`scope` | specifies a [query scope method](../database/model#query-scopes) defined in the **related form model** to apply to the list query always.

### Relation Manager

`relationmanager` - Renders the [relation controller](../relations) for this relation. This is the equivalent of using a `partial` field that renders the output of the `relationRender()` method from the controller.

```yaml
records:
label: Records
type: relationmanager
```

<div class="attributes-table-precessor"></div>

Option | Description
------------- | -------------
`readOnly` | forces the relation controller in readOnly mode. (defaults to parent previewMode)
`recordUrl` | path to controller action to open a record (e.g. `author/plugin/controller/update/:id`). `:id` will get replaced with the record id.
`recordOnClick` | custom Javascript code to execute when a record is clicked.
`relation` | relation name if different from the field name.

### Repeater

`repeater` - renders a repeating set of form fields defined within.
Expand Down
25 changes: 24 additions & 1 deletion backend/lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ Each definition can then be displayed by passing the definition name as the firs

## Using list filters

Lists can be filtered by [adding a filter definition](#scope-options) to the list configuration. Similarly filters are driven by their own configuration file that contain filter scopes, each scope is an aspect by which the list can be filtered. The next example shows a typical contents of the filter definition file.
Lists can be filtered by [adding a filter definition](#filtering-the-list) to the list configuration. Similarly filters are driven by their own configuration file that contain filter scopes, each scope is an aspect by which the list can be filtered. The next example shows a typical contents of the filter definition file.

```yaml
# ===================================
Expand Down Expand Up @@ -638,6 +638,8 @@ published:

`switch` - used as a switch to toggle between two predefined conditions or queries to the list, either indeterminate, on or off. Use 0 for off, 1 for indeterminate and 2 for on for default value

Using conditions:

```yaml
approved:
label: Approved
Expand All @@ -648,6 +650,27 @@ approved:
- is_approved = true
```

Using a scope method:

```yaml
approved:
label: Approved
type: switch
default: 0
scope: isApproved
```

```php
public function scopeIsApproved($query, $state)
{
return match ($state) {
'0' => $query,
'1' => $query->where('is_approved', false),
'2' => $query->where('is_approved', true),
}
}
```

### Date scope

`date` - displays a date picker for a single date to be selected. The values available to be used in the conditions property are:
Expand Down
1 change: 1 addition & 0 deletions backend/relations.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Option | Type | Description
------------- | ------------- | -------------
`form` | Form | a reference to form field definition file, see [backend form fields](forms#defining-form-fields).
`list` | List | a reference to list column definition file, see [backend list columns](lists#defining-list-columns).
`showSetup` | List | displays the list column set up button.
`showSearch` | List | display an input for searching the records. Default: `false`
`showSorting` | List | displays the sorting link on each column. Default: `true`
`defaultSort` | List | sets a default sorting column and direction when user preference is not defined. Supports a string or an array with keys `column` and `direction`.
Expand Down
4 changes: 2 additions & 2 deletions backend/views-partials.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Partials

Backend partials are files with the extension **htm** that reside in the [controller's views](controllers-ajax#introduction) directory. The partial file names should start with the underscore: *_partial.htm*. Partials can be rendered from a backend page or another partial. Use the controller's `makePartial` method to render a partial. The method takes two parameters - the partial name and the optional array of variables to pass to the partial. Example:
Backend partials are files with the extension **php** that reside in the [controller's views](controllers-ajax#introduction) directory. The partial file names should start with the underscore: *_partial.php*. Partials can be rendered from a backend page or another partial. Use the controller's `makePartial` method to render a partial. The method takes two parameters - the partial name and the optional array of variables to pass to the partial. Example:

```php
<?= $this->makePartial('sidebar', ['showHeader' => true]) ?>
Expand Down Expand Up @@ -98,4 +98,4 @@ This layout uses two placeholders, a primary content area called **form-contents
<?php Block::endPut() ?>
```

The layout is executed in the final section by overriding the **body** placeholder used by every backend layout. It wraps everything with a `<form />` HTML tag and renders the child layout called **form-with-sidebar**. This file is located in `modules\backend\layouts\form-with-sidebar.htm`.
The layout is executed in the final section by overriding the **body** placeholder used by every backend layout. It wraps everything with a `<form />` HTML tag and renders the child layout called **form-with-sidebar**. This file is located in `modules\backend\layouts\form-with-sidebar.php`.
4 changes: 4 additions & 0 deletions cms/layouts.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ Placeholders allow pages to inject content to the layout. Placeholders are defin
<head>
{% placeholder head %}
</head>
<body>
{% placeholder nav default %}
{% partial "nav" %}
{% endplaceholder %}
...
```

Expand Down
5 changes: 5 additions & 0 deletions console/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ Command | Description
[`create:controller`](../console/scaffolding#create-a-backend-controller) | Create a Controller in a plugin.
[`create:formwidget`](../console/scaffolding#create-a-form-widget) | Create a FormWidget in a plugin.
[`create:job`](../console/scaffolding#create-a-job) | Create a Job class in a plugin.
[`create:migration`](../console/scaffolding#create-a-migration) | Create a Migration in a plugin.
[`create:model`](../console/scaffolding#create-a-model) | Create a Model in a plugin.
[`create:plugin`](../console/scaffolding#create-a-plugin) | Create a Plugin.
[`create:reportwidget`](../console/scaffolding#create-a-report-widget) | Create a ReportWidget in a plugin.
[`create:settings`](../console/scaffolding#create-a-settings-model) | Create a Settings model in a plugin.
[`create:test`](../console/scaffolding#create-a-test) | Create a Test case in a plugin.
[`create:theme`](../console/scaffolding#create-a-theme) | Create a Theme.
**Utilities** |
[`winter:test`](../console/utilities#run-unit-tests) | Run unit tests on Winter and plugins.
Expand All @@ -91,6 +93,9 @@ Command | Description
`config:cache` | Create a cache file for faster configuration loading
`config:clear` | Remove the configuration cache file
`down` | Put the application into maintenance / demo mode
`event:cache` | Discover and cache the application's events and listeners
`event:clear` | Clear all cached events and listeners
`event:list` | List the application's events and listeners
`env` | Display the current framework environment
`key:generate` | Set the application key
`optimize` | Cache the framework bootstrap files
Expand Down
37 changes: 37 additions & 0 deletions console/scaffolding.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ php artisan create:component <plugin code> <component name>

The `create:component` command creates a new component class and the default component view. The first argument specifies the plugin code of the plugin that this component will be added into, and the second parameter specifies the component class name, eg. `MyComponent`.

## Create a migration

The `create:migration` command generates the migration file needed for a model database. The first argument specifies the plugin code of the plugin that this migration will be added into. Without any options, a bare migration file gets generated.

```bash
php artisan create:migration <plugin code>
```

In order to create a migration that auto-populates columns for your model, use the `--create` and `--model` options:

```bash
php artisan create:migration <plugin code> --create --model YourModel
```

In order to create an "update" migration, use the `--update` and `--model` options:

```bash
php artisan create:migration <plugin code> --update --model YourModel
```

## Create a model

```bash
Expand Down Expand Up @@ -81,3 +101,20 @@ php artisan create:command <plugin code> <command name>
```

The `create:command` command generates a [new console command](../console/introduction#building-a-command). The first argument specifies the plugin code of the plugin that this console command will be added into, and the second parameter specifies the command name.

## Create a test

```bash
php artisan create:test <plugin code> <path to class to be tested or test name>
```

The `create:test` command generates a test case. The first argument specifies the plugin code of the plugin that this job will be added into, and the second specifies the relative path to the class to be tested (i.e. a test for `\MyAuthor\MyPlugin\Classes\AuthManager` could be generated by calling `php artisan create:test myauthor.myplugin Classes\\AuthManager`) or the test's name (eg. `AuthManager`, which would be automatically expanded to `AuthManagerTest`).

The following options are supported:

short | long | description
----- | ---- | -----------
`-u` | `--unit` | Generates a Unit test (defaults to generating Feature tests)
`-p` | `--pest` | Generates a Pest PHP test (defaults to generating PHPUnit tests)
`-f` | `--force` | Overwrites existing files with generated files
n/a | `--uninspiring` | Disables inspirational quotes
9 changes: 8 additions & 1 deletion database/attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,15 @@ $file = (new System\Models\File)->fromData('Some content', 'sometext.txt');

For multiple attach relations (`$attachMany`), you may use the `create` method on the relationship instead, notice the file object is associated to the `data` attribute. This approach can be used for singular relations too, if you prefer.

```html
<input type='file' name='files[]' multiple>
```

```php
$model->avatar()->create(['data' => Input::file('file_input')]);
$files = Input::file('files');
foreach ($files as $file) {
$model->avatar()->create(['data' => $file]);
}
```

Alternatively, you can prepare a File model before hand, then manually associate the relationship later. Notice the `is_public` attribute must be set explicitly using this approach.
Expand Down
36 changes: 13 additions & 23 deletions database/behaviors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,38 @@

Model behaviors are used to implement common functionality. Unlike [Traits](traits) these can be implemented either directly in a class or by extending the class. You can read more about behaviors [here](../services/behaviors).

## Purgeable
## Encryptable

Purged attributes will not be saved to the database when a model is created or updated. To purge
attributes in your model, implement the `Winter.Storm.Database.Behaviors.Purgeable` behavior and declare
a `$purgeable` property with an array containing the attributes to purge.
Similar to the [hashable trait](traits#hashable), encrypted attributes are encrypted when set but also decrypted when an attribute is retrieved. To encrypt attributes in your model, implement the `Winter\Storm\Database\Behaviors\Encryptable` behavior and declare a protected `$encryptable` property with an array containing the attributes to encrypt.

```php
class User extends Model
{
public $implement = [
'Winter.Storm.Database.Behaviors.Purgeable'
'Winter.Storm.Database.Behaviors.Encryptable',
];

/**
* @var array List of attributes to purge.
* @var array List of attributes to encrypt.
*/
public $purgeable = [];
protected $encryptable = ['api_key', 'api_secret'];
}
```

You can also dynamically implement this behavior in a class.
You can also dynamically implement this behavior on third party models outside of your control:

```php
/**
* Extend the Winter.User user model to implement the purgeable behavior.
* Extend the Winter.User user model to implement the encryptable behavior.
*/
Winter\User\Models\User::extend(function($model) {

// Implement the purgeable behavior dynamically
$model->implement[] = 'Winter.Storm.Database.Behaviors.Purgeable';

// Declare the purgeable property dynamically for the purgeable behavior to use
$model->addDynamicProperty('purgeable', []);
// Implement the sortable behavior dynamically
$model->implement[] = 'Winter.Storm.Database.Behaviors.Encryptable';
$model->addDynamicProperty('encryptable', ['encrypted_metadata']);
});
```

The defined attributes will be purged when the model is saved, before the [model events](model#events) are triggered, including validation. Use the `getOriginalPurgeValue` to find a value that was purged.

```php
return $user->getOriginalPurgeValue($propertyName);
```
> **NOTE:** Encrypted attributes will be serialized and unserialized as a part of the encryption / decryption process. Do not make an attribute that is `encryptable` also [`jsonable`](model#supported-properties) at the same time as the `jsonable` process will attempt to decode a value that has already been unserialized by the encryptor.
## Sortable

Expand All @@ -52,19 +43,18 @@ Sorted models will store a number value in `sort_order` which maintains the sort
class User extends Model
{
public $implement = [
'Winter.Storm.Database.Behaviors.Sortable'
'Winter.Storm.Database.Behaviors.Sortable',
];
}
```

You can also dynamically implement this behavior in a class.
You can also dynamically implement this behavior on third party models outside of your control:

```php
/**
* Extend the Winter.User user model to implement the sortable behavior.
*/
Winter\User\Models\User::extend(function($model) {

// Implement the sortable behavior dynamically
$model->implement[] = 'Winter.Storm.Database.Behaviors.Sortable';
});
Expand Down
Loading

0 comments on commit ae9d38f

Please sign in to comment.