Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5fb6bab
Improving usability
WebVPF Jan 4, 2022
1b8a371
Update markup-filter-media.md
WebVPF Jan 4, 2022
4d343f1
Update markup-filter-app.md
WebVPF Jan 4, 2022
4244afc
Update markup-function-form.md
WebVPF Jan 4, 2022
8b02f99
Update markup-function-html.md
WebVPF Jan 4, 2022
d71f1e8
Update markup-function-str.md
WebVPF Jan 4, 2022
7a60344
Update markup-tag-for.md
WebVPF Jan 4, 2022
a8aa8ab
Update markup-tag-page.md
WebVPF Jan 4, 2022
1da937e
Update markup-templating.md
WebVPF Jan 4, 2022
2b46186
Update markup-templating.md
WebVPF Jan 4, 2022
06452fd
Update services-parser.md
WebVPF Jan 4, 2022
ab2c92f
Update markup-templating.md
WebVPF Jan 4, 2022
76d1c61
Update plugin-components.md
WebVPF Jan 4, 2022
af8bc57
Update plugin-components.md
WebVPF Jan 4, 2022
d6a82d8
Update services-response-view.md
WebVPF Jan 4, 2022
43b5b69
Update plugin-components.md
WebVPF Jan 4, 2022
3041916
Update plugin-components.md
WebVPF Jan 4, 2022
49b82ce
Update plugin-scheduling.md
WebVPF Jan 4, 2022
038d060
Update plugin-scheduling.md
WebVPF Jan 4, 2022
1b54e42
Update plugin-scheduling.md
WebVPF Jan 4, 2022
d56146d
Update plugin-scheduling.md
WebVPF Jan 4, 2022
a74a89f
Update services-mail.md
WebVPF Jan 4, 2022
7b23195
Update services-parser.md
WebVPF Jan 4, 2022
d8b3c54
Update services-parser.md
WebVPF Jan 4, 2022
3401266
Update services-parser.md
WebVPF Jan 4, 2022
686c6d4
Update services-parser.md
WebVPF Jan 4, 2022
bbcb32b
Update services-parser.md
WebVPF Jan 4, 2022
28fb878
Update services-response-view.md
WebVPF Jan 4, 2022
7d916fd
Update services-response-view.md
WebVPF Jan 4, 2022
9021b7f
Update services-response-view.md
WebVPF Jan 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 62 additions & 54 deletions help-unit-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,82 +12,90 @@ Individual plugin test cases can be run by running [the `winter:test` command](.

Plugins can be tested by creating a file called `phpunit.xml` in the base directory with the following content, for example, in a file **/plugins/acme/blog/phpunit.xml**:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="../../../tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<testsuites>
<testsuite name="Plugin Unit Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>
```xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="../../../tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<testsuites>
<testsuite name="Plugin Unit Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>
```

Then a **tests/** directory can be created to contain the test classes. The file structure should mimic the base directory with classes having a `Test` suffix. Using a namespace for the class is also recommended.

<?php namespace Acme\Blog\Tests\Models;
```php
<?php namespace Acme\Blog\Tests\Models;

use Acme\Blog\Models\Post;
use PluginTestCase;
use Acme\Blog\Models\Post;
use PluginTestCase;

class PostTest extends PluginTestCase
class PostTest extends PluginTestCase
{
public function testCreateFirstPost()
{
public function testCreateFirstPost()
{
$post = Post::create(['title' => 'Hi!']);
$this->assertEquals(1, $post->id);
}
$post = Post::create(['title' => 'Hi!']);
$this->assertEquals(1, $post->id);
}
}
```

The test class should extend the base class `PluginTestCase` and this is a special class that will set up the Winter database stored in memory, as part of the `setUp` method. It will also refresh the plugin being tested, along with any of the defined dependencies in the plugin registration file. This is the equivalent of running the following before each test:

php artisan winter:up
php artisan plugin:refresh Acme.Blog
[php artisan plugin:refresh <dependency>, ...]
```bash
php artisan winter:up
php artisan plugin:refresh Acme.Blog
[php artisan plugin:refresh <dependency>, ...]
```

> **NOTE:** If your plugin uses [configuration files](../plugin/settings#file-configuration), then you will need to run `System\Classes\PluginManager::instance()->registerAll(true);` in the `setUp` method of your tests. Below is an example of a base test case class that should be used if you need to test your plugin working with other plugins instead of in isolation.

use System\Classes\PluginManager;
```php
use System\Classes\PluginManager;

class BaseTestCase extends PluginTestCase
class BaseTestCase extends PluginTestCase
{
public function setUp(): void
{
public function setUp(): void
{
parent::setUp();
parent::setUp();

// Get the plugin manager
$pluginManager = PluginManager::instance();
// Get the plugin manager
$pluginManager = PluginManager::instance();

// Register the plugins to make features like file configuration available
$pluginManager->registerAll(true);
// Register the plugins to make features like file configuration available
$pluginManager->registerAll(true);

// Boot all the plugins to test with dependencies of this plugin
$pluginManager->bootAll(true);
}
// Boot all the plugins to test with dependencies of this plugin
$pluginManager->bootAll(true);
}

public function tearDown(): void
{
parent::tearDown();
public function tearDown(): void
{
parent::tearDown();

// Get the plugin manager
$pluginManager = PluginManager::instance();
// Get the plugin manager
$pluginManager = PluginManager::instance();

// Ensure that plugins are registered again for the next test
$pluginManager->unregisterAll();
}
// Ensure that plugins are registered again for the next test
$pluginManager->unregisterAll();
}
}
```

#### Changing database engine for plugins tests

Expand Down
108 changes: 57 additions & 51 deletions help-using-composer.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ In order to use Composer with a Winter CMS instance that has been installed usin

If you plan on submitting pull requests to the Winter CMS project via GitHub, or are actively developing a project based on Winter CMS and want to stay up to date with the absolute latest version, we recommend switching your composer dependencies to point to the `develop` branch where all the latest improvements and bug fixes take place. Doing this will allow you to catch any potential issues that may be introduced (as rare as they are) right when they happen and get them fixed while you're still actively working on your project instead of only discovering them several months down the road if they eventually make it into production.

```
```json
"winter/storm": "dev-develop as 1.1",
"winter/wn-system-module": "dev-develop",
"winter/wn-backend-module": "dev-develop",
Expand Down Expand Up @@ -89,24 +89,26 @@ composer require --dev <your package name> "<version constraint>"

When publishing your plugins or themes to the marketplace, you may wish to also make them available via Composer. An example `composer.json` file for a plugin is included below:

{
"name": "winter/wn-demo-plugin",
"type": "winter-plugin",
"description": "Demo Winter CMS plugin",
"keywords": ["winter", "cms", "demo", "plugin"],
"license": "MIT",
"authors": [
{
"name": "Winter CMS Maintainers",
"url": "https://wintercms.com",
"role": "Maintainer"
}
],
"require": {
"php": ">=7.2",
"composer/installers": "~1.0"
```json
{
"name": "winter/wn-demo-plugin",
"type": "winter-plugin",
"description": "Demo Winter CMS plugin",
"keywords": ["winter", "cms", "demo", "plugin"],
"license": "MIT",
"authors": [
{
"name": "Winter CMS Maintainers",
"url": "https://wintercms.com",
"role": "Maintainer"
}
],
"require": {
"php": ">=7.2",
"composer/installers": "~1.0"
}
}
```

Be sure to start your package `name` with **wn-** and end it with **-plugin** or **-theme** respectively - this will help others find your package and is in accordance with the [quality guidelines](../help/developer/guide#repository-naming).

Expand Down Expand Up @@ -166,47 +168,51 @@ However, this can create problems with Winter's plugin oriented design, since th

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

public function bootPackages()
{
// Get the namespace code of the current plugin
$pluginNamespace = str_replace('\\', '.', strtolower(__NAMESPACE__));

// Locate the packages to boot
$packages = \Config::get($pluginNamespace . '::packages');

// Boot each package
foreach ($packages as $name => $options) {
// Apply the configuration for the package
if (
!empty($options['config']) &&
!empty($options['config_namespace'])
) {
Config::set($options['config_namespace'], $options['config']);
}
```php
public function bootPackages()
{
// Get the namespace code of the current plugin
$pluginNamespace = str_replace('\\', '.', strtolower(__NAMESPACE__));

// Locate the packages to boot
$packages = \Config::get($pluginNamespace . '::packages');

// Boot each package
foreach ($packages as $name => $options) {
// Apply the configuration for the package
if (
!empty($options['config']) &&
!empty($options['config_namespace'])
) {
Config::set($options['config_namespace'], $options['config']);
}
}
}
```

Now you are free to provide the packages configuration values the same way you would with regular plugin configuration values.

return [
// Laravel Package Configuration
'packages' => [
'packagevendor/packagename' => [
// The accessor for the config item, for example,
// to access via Config::get('purifier.' . $key)
'config_namespace' => 'purifier',

// The configuration file for the package itself.
// Copy this from the package configuration.
'config' => [
'encoding' => 'UTF-8',
'finalize' => true,
'cachePath' => storage_path('app/purifier'),
'cacheFileMode' => 0755,
],
```php
return [
// Laravel Package Configuration
'packages' => [
'packagevendor/packagename' => [
// The accessor for the config item, for example,
// to access via Config::get('purifier.' . $key)
'config_namespace' => 'purifier',

// The configuration file for the package itself.
// Copy this from the package configuration.
'config' => [
'encoding' => 'UTF-8',
'finalize' => true,
'cachePath' => storage_path('app/purifier'),
'cacheFileMode' => 0755,
],
],
];
],
];
```

Now the package configuration has been included natively in Winter CMS and the values can be changed normally using the [standard configuration approach](../plugin/settings#file-configuration).

Expand Down
24 changes: 16 additions & 8 deletions markup-filter-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@

The `| app` filter returns an address relative to the public path of the website. The result is an absolute URL, including the domain name and protocol, to the location specified in the filter parameter. The filter can be applied to any path.

<link rel="icon" href="{{ '/favicon.ico' | app }}" />
```twig
<link rel="icon" href="{{ '/favicon.ico' | app }}" />
```

If the website address is __https://example.com__ the above example would output the following:

<link rel="icon" href="https://example.com/favicon.ico" />
```html
<link rel="icon" href="https://example.com/favicon.ico" />
```

It can also be used for static URLs:

<a href="{{ '/about-us' | app }}">
About Us
</a>
```twig
<a href="{{ '/about-us' | app }}">
About Us
</a>
```

The above would output:

<a href="https://example.com/about-us">
About us
</a>
```html
<a href="https://example.com/about-us">
About us
</a>
```

> **NOTE**: The `| page` filter is recommended for linking to other pages.
14 changes: 9 additions & 5 deletions markup-filter-default.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

The `| default` filter returns the value passed as the first argument if the filtered value is undefined or empty, otherwise the filtered value is returned.

{{ variable | default('The variable is not defined') }}
```twig
{{ variable | default('The variable is not defined') }}

{{ variable.foo | default('The foo property on variable is not defined') }}
{{ variable.foo | default('The foo property on variable is not defined') }}

{{ variable['foo'] | default('The foo key in variable is not defined') }}
{{ variable['foo'] | default('The foo key in variable is not defined') }}

{{ '' | default('The variable is empty') }}
{{ '' | default('The variable is empty') }}
```

When using the `default` filter on an expression that uses variables in some method calls, be sure to use the `default` filter whenever a variable can be undefined:

{{ variable.method(foo | default('bar')) | default('bar') }}
```twig
{{ variable.method(foo | default('bar')) | default('bar') }}
```
8 changes: 5 additions & 3 deletions markup-filter-image-height.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ The `| imageHeight` filter attempts to identify the width in pixels of the provi

> **NOTE:** This filter does not support thumbnail (already resized) versions of FileModels being passed as the image source.

{% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %}
<img src="{{ resizedImage }}" height="{{ resizedImage | imageHeight }}" />
```twig
{% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %}
<img src="{{ resizedImage }}" height="{{ resizedImage | imageHeight }}" />
```

See the [image resizing docs](../services/image-resizing#resize-sources) for more information on what image sources are supported.

> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality.
> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality.
8 changes: 5 additions & 3 deletions markup-filter-image-width.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ The `| imageWidth` filter attempts to identify the width in pixels of the provid

> **NOTE:** This filter does not support thumbnail (already resized) versions of FileModels being passed as the image source.

{% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %}
<img src="{{ resizedImage }}" width="{{ resizedImage | imageWidth }}" />
```twig
{% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %}
<img src="{{ resizedImage }}" width="{{ resizedImage | imageWidth }}" />
```

See the [image resizing docs](../services/image-resizing#resize-sources) for more information on what image sources are supported.

> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality.
> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality.
8 changes: 6 additions & 2 deletions markup-filter-md.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

The `| md` filter converts the value from Markdown to HTML format.

{{ '**Text** is bold.' | md }}
```twig
{{ '**Text** is bold.' | md }}
```

The above will output the following:

<p><strong>Text</strong> is bold.</p>
```html
<p><strong>Text</strong> is bold.</p>
```
Loading