Merge remote-tracking branch 'origin/4.0' into 4

# Conflicts:
#	src/Core/CoreKernel.php
This commit is contained in:
Damian Mooyman 2018-02-05 17:52:38 +13:00
commit e359948eb3
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
13 changed files with 594 additions and 342 deletions

View File

@ -3,7 +3,8 @@ introduction: Keep your SilverStripe installations up to date with the latest fi
# Upgrading to SilverStripe 4 # Upgrading to SilverStripe 4
SilverStripe applications should be kept up to date with the latest security releases. Usually an update or upgrade to your SilverStripe installation means overwriting files, flushing the cache and updating your database schema. SilverStripe applications should be kept up to date with the latest security releases. Usually an update or upgrade to
your SilverStripe installation means overwriting files, flushing the cache and updating your database schema.
<div class="info" markdown="1"> <div class="info" markdown="1">
See our [upgrade notes and changelogs](/changelogs/4.0.0) for 4.0.0 specific information, bugfixes and API changes. See our [upgrade notes and changelogs](/changelogs/4.0.0) for 4.0.0 specific information, bugfixes and API changes.
@ -11,40 +12,32 @@ See our [upgrade notes and changelogs](/changelogs/4.0.0) for 4.0.0 specific inf
## Composer ## Composer
For projects managed through Composer, update the version number of `framework` and `cms` to `^4.0` in your `composer.json` file and run `composer update`. SilverStripe CMS is becoming more modular, and
[composer is becoming the preferred way to manage your code](/getting_started/composer).
For projects managed through Composer, we recommend using `recipe-cms` in your `composer.json` file to help you keep
up to date and run `composer update`.
```json ```json
"require": { {
"silverstripe/framework": "^4.0", "require": {
"silverstripe/cms": "^4.0" "silverstripe/recipe-cms": "^1"
}
} }
``` ```
<div class="info" markdown="1"> This will also add extra dependencies, such as the `admin`, `asset-admin`, `reports`, `errorpage` and `siteconfig`
Please note that until SilverStripe 4 is stable you will need to also add `"minimum-stability": "dev"` and `"prefer-stable": true` to your `composer.json` to be able to pull these modules. modules.
</div>
This will also add extra dependencies, the `reports` and `siteconfig` modules. SilverStripe CMS is becoming more modular, and [composer is becoming the preferred way to manage your code](/getting_started/composer). If you want more granular control over what gets installed,
reading through the README documentation in the [recipe plugin repository](https://github.com/silverstripe/recipe-plugin)
and also checking the `composer.json` files in [recipe-core](https://github.com/silverstripe/recipe-core) and
[recipe-cms](https://github.com/silverstripe/recipe-cms).
### Asset-admin For a description on how to handle issues with pre-existing composer installs or upgrading other modules, please read
through the [Composer dependency update section](/changelogs/4.0.0#deps)
SilverStripe 4 comes with a new asset administration module. While it is installed by default for new projects, if you are upgrading you will need to install it manually: ## Manual upgrades
```
composer require silverstripe/asset-admin ^1.0
```
This will also install the `graphql` module for GraphQL API access to your SilverStripe system, which powers the `asset-admin` module.
## Migrate to dotenv
SilverStripe 4 requires the use of `.env` and "real" environment variables instead of `_ss_environment.php` for your environment configuration.
You'll need to move your constants to a new `.env` file before SilverStripe will build successfully.
If you are not able to move your webserver away from using `_ss_environment.php` files, you can use [this example file](https://gist.github.com/robbieaverill/74fbfff6f438c94f6325107e4d7b2a45) and include it at the top of your `mysite/_config.php` file. This will export your constants as environment variables.
## Manual
* Check if any modules (e.g. `blog` or `forum`) in your installation are incompatible and need to be upgraded as well. * Check if any modules (e.g. `blog` or `forum`) in your installation are incompatible and need to be upgraded as well.
* Backup your database content. * Backup your database content.
@ -53,10 +46,9 @@ If you are not able to move your webserver away from using `_ss_environment.php`
* Leave custom folders like *mysite* or *themes* in place. * Leave custom folders like *mysite* or *themes* in place.
* Identify system folders in your webroot (`cms`, `framework` and any additional modules). * Identify system folders in your webroot (`cms`, `framework` and any additional modules).
* Delete existing system folders (or move them outside of your webroot). * Delete existing system folders (or move them outside of your webroot).
* Rename your `Page_Controller` class to `PageController`. * Add a `private static $table_name = 'MyDataObject'` for any custom DataObjects in your code that are namespaced. This ensures that your database table name will be `MyDataObject` instead of `Me_MyPackage_Model_MyDataObject` (converts your namespace to the table_name).
* Add a `private static $table_name = 'MyDataObject'` for any custom DataObjects in your code that are namespaced. This ensures that your database table name will be `MyDataObject` instead of `Me\MyPackage\Model\MyDataObject` (your namespace for the class). * Ensure you add [namespaces](http://php.net/manual/en/language.namespaces.php) to any custom classes in your `mysite` folder. Your namespaces should follow the pattern of `Vendor\Package` with anything additional defined at your discretion. **Note:** The `Page` and `PageController` classes *must* be defined in the global namespace (or without a namespace).
* Ensure you add [namespaces](http://php.net/manual/en/language.namespaces.php) to any custom classes in your `mysite` folder. Your namespaces should follow the pattern of `Vendor\Package` with anything additional defined at your discretion. **Note:** The `Page` and `PageController` classes *must* be defined in the global namespace (or; without a namespace). * Install the updated framework, CMS and any other modules you require by updating your `composer.json` configuration and running `composer update`. Some features have been split into their own modules, such as `asset-admin` and `errorpage`. Please refer to [`recipe-cms` composer.json](https://github.com/silverstripe/recipe-cms) and [`recipe-core` composer.json](https://github.com/silverstripe/recipe-core) for a list of recommended modules to include.
* Install the updated framework, CMS and any other modules you require by updating your `composer.json` configuration and running `composer update`. As of SilverStripe 4.0.0 you should also include the `asset-admin` module to power your asset management in the CMS.
* Check if you need to adapt your code to changed PHP APIs. For more information please refer to [the changelog](/changelogs/4.0.0). There is an upgrader tool available to help you with most of the changes required (see below). * Check if you need to adapt your code to changed PHP APIs. For more information please refer to [the changelog](/changelogs/4.0.0). There is an upgrader tool available to help you with most of the changes required (see below).
* Visit http://yoursite.com/dev/build/?flush=1 to rebuild the website database. * Visit http://yoursite.com/dev/build/?flush=1 to rebuild the website database.
* Check if you have overwritten any core templates or styles which might need an update. * Check if you have overwritten any core templates or styles which might need an update.
@ -65,20 +57,138 @@ If you are not able to move your webserver away from using `_ss_environment.php`
Never update a website on the live server without trying it on a development copy first! Never update a website on the live server without trying it on a development copy first!
</div> </div>
## Environment variables file changed to dotenv
SilverStripe 4 requires the use of `.env` and no longer supports using `_ss_environment.php` for your
environment configuration.
You'll need to move your constants to a new `.env` file before SilverStripe will build successfully.
For further details about the `.env` migration, read through the
[`_ss_environment.php` changed to `.env` section](/changelogs/4.0.0#env)
## Using the upgrader tool ## Using the upgrader tool
We've developed [an upgrader tool](https://github.com/silverstripe/silverstripe-upgrader) which you can use to help you with the upgrade process to SilverStripe 4. [See the upgrading notes](/changelogs/4.0.0/#a-name-upgrading-a-upgrading) in the changelog for more detailed instructions on how to use it. We've developed [an upgrader tool](https://github.com/silverstripe/silverstripe-upgrader) which you can use to help
with the upgrade process to SilverStripe 4. See the README documentation in the repository for more detailed
instructions on how to use it.
## Quick tips
### `index.php` and `.htaccess` rewrites
The location of SilverStripe's "entry file" has changed. Your project and server environment will need
to adjust the path to this file from `framework/main.php` to `public/index.php`.
For more details, please read through the [`index.php` and `.htaccess` rewrites section](/changelogs/4.0.0#index-php-rewrites)
After installing, run the upgrader doctor command:
```
cd ~/my-project-root
upgrade-code doctor
```
This will ensure that your `.htaccess` and `index.php` are set to a reasonable default value for a clean installation.
### Renamed and namespaced classes
Nearly all core PHP classes in SilverStripe have been namespaced. For example, `DataObject` is now called `SilverStripe\ORM\DataObject`.
For a full list of renamed classes, check the `.upgrade.yml` definitions in each module.
After installing, run the upgrader upgrade command:
```
cd ~/my-project-root
~/.composer/vendor/bin/upgrade-code upgrade ./mysite --write
```
## Migrating files
Since the structure of `File` dataobjects has changed, a new task `MigrateFileTask`
has been added to assist in migration of legacy files (see [file migration documentation](/developer_guides/files/file_migration)).
```
$ ./vendor/bin/sake dev/tasks/MigrateFileTask
```
## Upgrade tips
While there's some code we can automatically rewrite, other uses of changed SilverStripe APIs aren't that obvious.
You can use our `inspect` to get some hints on where you need to review code manually.
Hints will generally point to more detail about a specific upgrade in this guide.
This task requires [the upgrader tool](https://github.com/silverstripe/silverstripe-upgrader).
```
~/.composer/vendor/bin/upgrade-code inspect ./mysite
```
These hints only cover a part of the upgrade work, but can serve as a good indicator for where to start.
If you've already had a look over the changelog, you will see that there are some fundamental changes that need to be implemented to upgrade from 3.x. Here's a couple of the most important ones to consider: If you've already had a look over the changelog, you will see that there are some fundamental changes that need to be implemented to upgrade from 3.x. Here's a couple of the most important ones to consider:
* PHP 5.5 is now the minimum required version (and PHP 7.x is supported!). * PHP 5.6 is now the minimum required version (and up to PHP 7.1.x is supported!).
* All SilverStripe classes are now namespaced, and some have been renamed. Most of your modules will also have been namespaced, and you will need to consider this when updating class references (including YAML configuration) in your own code.
* CMS CSS has been re-developed using Bootstrap 4 as a base. * CMS CSS has been re-developed using Bootstrap 4 as a base.
* SilverStripe code _should_ now be [PSR-2 compliant](http://www.php-fig.org/psr/psr-2/). While this is not a requirement, we strongly suggest you switch over now. You can use tools such as [`phpcbf`](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Fixing-Errors-Automatically) to do most of it automatically. * SilverStripe code _should_ now be [PSR-2 compliant](http://www.php-fig.org/psr/psr-2/). While this is not a requirement, we strongly suggest you switch over now. You can use tools such as [`phpcbf`](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Fixing-Errors-Automatically) to do most of it automatically.
* We've also introduced some best practices for module development. [See the Modules article](/developer_guides/extending/modules) for more information.
## Additional tips easily missed
[Object class replaced by traits](/changelogs/4.0.0#object-replace)
The `Object` class has been superceded by three traits:
- `Injectable`: Provides `MyClass::create()` and `MyClass::singleton()`
- `Configurable`: Provides `MyClass::config()`
- `Extensible`: Provides all methods related to extensions (E.g. add_extension()).
`$this->class` no longer recommended, should use `static::class` or `get_class($classObject)` instead.
[Rewrite literal table names](/changelogs/4.0.0#literal-table-names)
Use `$table = SilverStripe\ORM\DataObject::getSchema()->tableForField($model, $field)` instead of `$model` directly.
[Rewrite literal class names](/changelogs/4.0.0#literal-class-names)
For example, referencing the class name `'Member'` should be `Member::class` or if you're in YML config it should be `SilverStripe\Security\Member`.
[Template locations and references](/changelogs/4.0.0#template-locations)
Templates require the folder path inside the templates folder, and Core templates are placed in paths following the class namespace, e.g. `FormField` is now `SilverStripe/Forms/FormField`.
When using the `<% include %>` syntax, you can leave out the `Includes` folder in the path.
[Config settings should be set to `private static`](/changelogs/4.0.0#private-static)
We no longer support `public static $config_item` on classes, it now needs to be `private static $config_item`.
[Module paths can't be hardcoded](/changelogs/4.0.0#module-paths)
Modules may not be placed in a deterministic folder (e.g. `/framework`),
you should use getters on the [Module](api:SilverStripe\Core\Manifest\Module) object instead.
Please see the changelogs for more details on ways that the getters on the `Module` object could be used.
[Adapt tooling to modules in vendor folder](#vendor-folder)
SilverStripe modules are now placed in the `vendor` folder like many other composer package.
Modules need to declare which files need to be exposed via the new [vendor-plugin](https://github.com/silverstripe/vendor-plugin), using symlinks to link to files from the publically accessible `resources` folder.
[SS_Log replaced with PSR-3 logging](/changelogs/4.0.0#psr3-logging)
SilverStripe 4 introduces [PSR-3](http://www.php-fig.org/psr/psr-3/) compatible logger interfaces. Services can access the logger using the LoggerInterface::class service.
Please see the changelogs for more details on how to implement logging.
[Upgrade `mysite/_config.php`](/changelogs/4.0.0#config-php)
The globals `$database` and `$databaseConfig` are deprecated. You should upgrade your site `_config.php` files to use the [.env configuration](#env).
`conf/ConfigureFromEnv.php` is no longer used, and references to this file should be deleted.
[Session object removes static methods](/changelogs/4.0.0#session)
Session object is no longer statically accessible via `Session::inst()`. Instead, `Session` is a member of the current request.
[Extensions are now singletons](#extensions-singletons)
This means that state stored in private/protected variables are now shared across all objects which use this extension.
It is recommended to refactor the variables to be stored against the owner object.
[Explicit text casting on template variables](/changelogs/4.0.0#template-casting)
Calling `$MyField` on a DataObject in templates will by default cast MyField as `Text` which means it will be safely encoded.
You can change the casting for this by defining a casting config on the DataObject:
```php
private static $casting = [
'MyField' => 'HTMLText'
];
```
We've also introduced some best practices for module development. [See the Modules article](/developer_guides/extending/modules) for more information.
## Decision Helpers ## Decision Helpers

View File

@ -73,22 +73,24 @@ Backup your existing `composer.json` and overwrite it with the following content
{ {
"name": "myvendor/myproject", "name": "myvendor/myproject",
"require": { "require": {
"silverstripe/recipe-cms": "^1" "silverstripe/recipe-cms": "^1.0"
}, },
"prefer-stable": true, "prefer-stable": true,
"minimum-stability": "dev" "minimum-stability": "dev"
} }
``` ```
This composer file uses the new [recipe](https://github.com/silverstripe/recipe-plugin) approach This composer file uses the new [recipe](https://github.com/silverstripe/recipe-plugin) approach
which bundles all core dependencies in a meta package. If you want more granular control over what gets installed, which bundles all core dependencies in a meta package. If you want more granular control over what gets installed,
check the `composer.json` files in [recipe-core](https://github.com/silverstripe/recipe-core) and [recipe-cms](https://github.com/silverstripe/recipe-cms). reading through the README documentation in the [recipe plugin repo](https://github.com/silverstripe/recipe-plugin)
and also checking the `composer.json` files in [recipe-core](https://github.com/silverstripe/recipe-core) and
[recipe-cms](https://github.com/silverstripe/recipe-cms).
Now run a `composer update`. This will remove all existing modules from your local codebase Now run a `composer update`. This will remove all existing modules from your local codebase
since we replaced your project's `composer.json`. Now you can move back your modules since we replaced your project's `composer.json`. Now you can move your modules back
one by one, checking for compatible versions on [packagist.org](http://packagist.org). one by one, checking for compatible versions on [packagist.org](http://packagist.org).
Note: If you have issues with a pre-existing composer install you can force a clean re-install with the Note: If you have issues with a pre-existing composer install you can force a clean re-install with the
below commands: below commands:
```bash ```bash
@ -122,75 +124,15 @@ once the module is stable.
} }
``` ```
For modules that don't have a pre-release branch started, For modules that don't have a pre-release branch started, you should raise an issue on the repository asking for 4.0
you should raise an issue on the repository asking for 4.0 compatibility. compatibility.
For now, you should attempt to continue the upgrade without the module For now, you should attempt to continue the upgrade without the module and temporarily disable its functionality.
and temporarily disable its functionality.
### Install the upgrader tool {#upgrader-tool} ### Manual upgrades
A lot of upgrade work can be automated, and we've written an Please follow
[upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) for this purpose.
Install it via composer:
``` ### Environment variables file changed to dotenv {#env}
composer global require silverstripe/upgrader
```
### index.php and .htaccess rewrites {#index-php-rewrites}
The location of SilverStripe's "entry file" has changed. Your project and server environment will need
to adjust the path to this file from `framework/main.php` to `index.php`.
If you are running Apache, adjust your `.htaccess` file. For other webservers,
please consult the [installation guides](getting_started/installation/).
Since 4.0, URL rewrite capabilities are required,
unless you PHP's built-in webserver through [silverstripe/serve](https://github.com/silverstripe/silverstripe-serve).
The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) has a task runner which
automates this process for you.
Install the upgrader:
```
export PATH=$PATH:~/.composer/vendor/bin/
composer global require silverstripe/upgrader
```
Run the upgrader:
```
cd ~/my-project-root
upgrade-code doctor
```
This will ensure that your `.htaccess` and `index.php` are set to a reasonable default value
for a clean installation. If you have applied customisations to your `.htaccess`
file (e.g. a custom `main.php`, HTTP header configuration, deny file access),
you'll need to manually reapply these to the copied default file.
### Renamed and namespaced classes {#namespaced-classes}
Nearly all core PHP classes have been namespaced. For example, `DataObject` is now called `SilverStripe\ORM\DataObject`.
The below tasks describe how to upgrade an existing site to remain compatible with the newly upgraded classes.
We have developed an [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) to (semi-)automatically
update your 3.x code to the new naming. Here's an example how to upgrade your `mysite` folder:
```
cd ~/my-project-root
~/.composer/vendor/bin/upgrade-code upgrade ./mysite --write
```
If you want to do a dry-run, omit the `--write` option to see a preview of a diff of
all changed project files.
This will resolve the majority of upgrading work, but we strongly recommend reviewing the diff
running some regression testing on your functionality. SilverStripe core classes can be referenced
in your PHP files, but also in YAML configuration and SilverStripe templates.
For a full list of renamed classes, check the `.upgrade.yml` definitions in each module.
The rename won't affect class-based permission codes or database table names.
### `_ss_environment.php` changed to`.env` {#env}
The php configuration `_ss_environment.php` file has been replaced in favour of a non-executable The php configuration `_ss_environment.php` file has been replaced in favour of a non-executable
`.env` file, which follows a syntax similar to an `.ini` file for key/value pair assignment. Like `.env` file, which follows a syntax similar to an `.ini` file for key/value pair assignment. Like
@ -218,14 +160,14 @@ define('SS_DATABASE_SERVER', '127.0.0.1');
`.env`: `.env`:
``` ```bash
## Environment ## Environment
SS_ENVIRONMENT_TYPE="dev" SS_ENVIRONMENT_TYPE="dev"
SS_DEFAULT_ADMIN_USERNAME="admin" SS_DEFAULT_ADMIN_USERNAME="admin"
SS_DEFAULT_ADMIN_PASSWORD="password" SS_DEFAULT_ADMIN_PASSWORD="password"
SS_BASE_URL="http://localhost/" SS_BASE_URL="http://localhost/"
### Database ## Database
SS_DATABASE_CHOOSE_NAME="true" SS_DATABASE_CHOOSE_NAME="true"
SS_DATABASE_CLASS="MySQLDatabase" SS_DATABASE_CLASS="MySQLDatabase"
SS_DATABASE_USERNAME="root" SS_DATABASE_USERNAME="root"
@ -253,6 +195,66 @@ To access environment variables you can use the `SilverStripe\Core\Environment::
See [Environment Management docs](/getting_started/environment_management/) for full details. See [Environment Management docs](/getting_started/environment_management/) for full details.
If you are not able to move your webserver away from using `_ss_environment.php` files, you can use
[this example file](https://gist.github.com/robbieaverill/74fbfff6f438c94f6325107e4d7b2a45) and include it at the top
of your `mysite/_config.php` file. This will export your constants as environment variables.
### Using the upgrader tool {#upgrader-tool}
We've developed [an upgrader tool](https://github.com/silverstripe/silverstripe-upgrader) which you can use to help
with the upgrade process to SilverStripe 4. See the README documentation in the repository for more detailed
instructions on how to use it.
### `index.php` and `.htaccess` rewrites {#index-php-rewrites}
The location of SilverStripe's "entry file" has changed. Your project and server environment will need
to adjust the path to this file from `framework/main.php` to `public/index.php`.
Use [`recipe-core` index.php](https://github.com/silverstripe/recipe-core) as a reference to ensure the contents of
your `index.php` file is correct.
If you are running Apache, adjust your `.htaccess` file. For other webservers,
please consult the [installation guides](getting_started/installation/).
Since 4.0, URL rewrite capabilities are required,
unless you use PHP's built-in webserver through [silverstripe/serve](https://github.com/silverstripe/silverstripe-serve).
The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) has a task runner which
automates this process for you.
After installing, run the upgrader:
```
cd ~/my-project-root
upgrade-code doctor
```
This will ensure that your `.htaccess` and `index.php` are set to a reasonable default value
for a clean installation. If you have applied customisations to your `.htaccess`
file (e.g. a custom `main.php`, HTTP header configuration, deny file access),
you'll need to manually reapply these to the copied default file.
### Renamed and namespaced classes {#namespaced-classes}
Nearly all core PHP classes in SilverStripe have been namespaced. For example, `DataObject` is now called `SilverStripe\ORM\DataObject`.
The below tasks describe how to upgrade an existing site to remain compatible with the newly upgraded classes.
We have developed an [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) to (semi-)automatically
update your 3.x code to the new naming. Here's an example how to upgrade your `mysite` folder:
```
cd ~/my-project-root
~/.composer/vendor/bin/upgrade-code upgrade ./mysite --write
```
If you want to do a dry-run, omit the `--write` option to see a preview of a diff of
all changed project files.
This will resolve the majority of upgrading work, but we strongly recommend reviewing the diff
running some regression testing on your functionality. SilverStripe core classes can be referenced
in your PHP files, but also in YAML configuration and SilverStripe templates.
For a full list of renamed classes, check the `.upgrade.yml` definitions in each module.
The rename won't affect class-based permission codes or database table names.
### Migrate File DataObject {#migrate-file} ### Migrate File DataObject {#migrate-file}
Since the structure of `File` dataobjects has changed, a new task `MigrateFileTask` Since the structure of `File` dataobjects has changed, a new task `MigrateFileTask`
@ -262,10 +264,13 @@ has been added to assist in migration of legacy files (see [file migration docum
$ ./vendor/bin/sake dev/tasks/MigrateFileTask $ ./vendor/bin/sake dev/tasks/MigrateFileTask
``` ```
Any `File` dataobject which is not in the `File.allowed_extensions` config will be deleted Any `File` in the database with an extension which is not in the `File.allowed_extensions` config will be considered
from the database during migration. Any invalid file on the filesystem will not be deleted, invalid and deleted from the database during migration.
but will no longer be attached to a dataobject, and should be cleaned up manually.
To disable this, set the following config: Any invalid file on the filesystem will not be deleted in the filesystem, but will no longer be recorded in the
database, and should be cleaned up manually.
To disable deleting from the database, set the following config:
```yaml ```yaml
SilverStripe\Assets\FileMigrationHelper: SilverStripe\Assets\FileMigrationHelper:
@ -286,6 +291,75 @@ This task should be run *after* `upgrade-code upgrade`.
These hints only cover a part of the upgrade work, These hints only cover a part of the upgrade work,
but can serve as a good indicator for where to start. but can serve as a good indicator for where to start.
### Object class replaced by traits {#object-replace}
Object has been superseded by traits.
- `Injectable`: Provides `MyClass::create()` and `MyClass::singleton()`
- `Configurable`: Provides `MyClass::config()`
- `Extensible`: Provides all methods related to extensions (E.g. add_extension()).
Upgrade subclass use
```diff
-class MyClass extends Object
-{
-}
+use SilverStripe\Core\Extensible;
+use SilverStripe\Core\Injector\Injectable;
+use SilverStripe\Core\Config\Configurable;
+class MyClass
+{
+ use Extensible;
+ use Injectable;
+ use Configurable;
+}
```
Upgrade references to $this->class
```diff
-$obj->class
+get_class($obj);
-$this->class;
+static::class;
```
Upgrade parse_class_spec()
```diff
-$spec = Object::parse_class_spec($spec);
+$spec = ClassInfo::parse_class_spec($spec);
```
Upgrade create_from_string()
```diff
-$obj = Object::create_from_string('Varchar(100)');
+$obj = Injector::inst()->create('Varchar(100)');
```
Upgrade extension use
```diff
-Object::add_extension('File', 'Versioned');
+File::add_extension(Versioned::class);
+DataObject::add_extension(File::class, Versioned::class); // alternate
-$has = Object::has_extension('File', 'Versioned');
+$has = File::has_extension(Versioned::class);
+$has = DataObject::has_extension(File::class, Versioned::class); // alternate
-$extensions = Object::get_extensions('File');
+$extensions = File::get_extensions();
+$extensions = DataObject::get_extensions(File::class); // alternate
```
### Rewrite literal table names {#literal-table-names} ### Rewrite literal table names {#literal-table-names}
In 3.x the class name of any DataObject matched the table name, but in 4.x all classes are namespaced, and it is In 3.x the class name of any DataObject matched the table name, but in 4.x all classes are namespaced, and it is
@ -339,7 +413,7 @@ In the context of YAML, the magic constant `::class` does not apply. Fully quali
property: value property: value
``` ```
### Move controllers to their own files {#controllers-own-files} ### Controllers renamed {#controllers-renamed}
The convention for naming controllers is now `[MyPageType]Controller`, where it used to be `[MyPageType]_Controller`. This change was made to be more compatible with the PSR-2 standards. The convention for naming controllers is now `[MyPageType]Controller`, where it used to be `[MyPageType]_Controller`. This change was made to be more compatible with the PSR-2 standards.
@ -358,12 +432,12 @@ Either include the folder in the template name (`renderWith('MyEmail.ss')` => `r
move the template into the correct directory, or both. move the template into the correct directory, or both.
Core template locations have moved - if you're including or overriding these Core template locations have moved - if you're including or overriding these
(e.g. for FormField templates) please adjust to the new paths. The `forms` folder (e.g. for `FormField` templates) please adjust to the new paths. The `forms` folder
no longer exists, and instead template locations will be placed in paths that match no longer exists, and instead template locations will be placed in paths that match
the `SilverStripe\Forms` namespace. the class namespace, `SilverStripe\Forms`.
When using `<% include %>` template tag you can continue to leave out the `Includes` folder, When using the `<% include %>` template tag you can continue to leave out the `Includes` folder,
but this now will also search templates in the base folder if no Include can be found. but this will search templates in the base folder if no Include can be found.
`<% include Sidebar %>` will match `Includes/Sidebar.ss`, but will also match `Sidebar.ss` `<% include Sidebar %>` will match `Includes/Sidebar.ss`, but will also match `Sidebar.ss`
if the former is not present. if the former is not present.
@ -562,75 +636,6 @@ SilverStripe\Core\Manifest\ModuleManifest:
project: mysite project: mysite
``` ```
### Object class replaced by traits {#object-replace}
Object has been superseded by traits.
- `Injectable`: Provides `MyClass::create()` and `MyClass::singleton()`
- `Configurable`: Provides `MyClass::config()`
- `Extensible`: Provides all methods related to extensions (E.g. add_extension()).
Upgrade subclass use
```diff
-class MyClass extends Object
-{
-}
+use SilverStripe\Core\Extensible;
+use SilverStripe\Core\Injector\Injectable;
+use SilverStripe\Core\Config\Configurable;
+class MyClass
+{
+ use Extensible;
+ use Injectable;
+ use Configurable;
+}
```
Upgrade references to $this->class
```diff
-$obj->class
+get_class($obj);
-$this->class;
+static::class;
```
Upgrade parse_class_spec()
```diff
-$spec = Object::parse_class_spec($spec);
+$spec = ClassInfo::parse_class_spec($spec);
```
Upgrade create_from_string()
```diff
-$obj = Object::create_from_string('Varchar(100)');
+$obj = Injector::inst()->create('Varchar(100)');
```
Upgrade extension use
```diff
-Object::add_extension('File', 'Versioned');
+File::add_extension(Versioned::class);
+DataObject::add_extension(File::class, Versioned::class); // alternate
-$has = Object::has_extension('File', 'Versioned');
+$has = File::has_extension(Versioned::class);
+$has = DataObject::has_extension(File::class, Versioned::class); // alternate
-$extensions = Object::get_extensions('File');
+$extensions = File::get_extensions();
+$extensions = DataObject::get_extensions(File::class); // alternate
```
### Session object removes static methods {#session} ### Session object removes static methods {#session}
Session object is no longer statically accessible via `Session::inst()`. Instead, Session Session object is no longer statically accessible via `Session::inst()`. Instead, Session
@ -708,57 +713,12 @@ class MyClass extends DataObject {
} }
``` ```
### Static references to asset paths {#static-asset-paths} ### Javascript files in framework/admin {#js-in-framework}
All static files (images, javascript, stylesheets, fonts) used for the CMS and forms interfaces
in `framework` and `cms` have moved locations. These assets are now placed in a `client/` subfolder,
which are also sub-folders of the modules which now reside in `vendor`.
This will affect you if you have used `Requirements::block()` on files in the `framework/` or `cms/` folder.
In order to identify resources it is preferred to use the new module-root prefixed string form when
adding requirements.
Usage in Requirements:
```diff
-Requirements::css('framework/admin/css/styles.css');
+Requirements::css('silverstripe/admin: client/dist/styles/bundle.css');
```
The location of these resources is determined from the `name` field in the `composer.json`
for each module. `silverstripe/admin:` will be mapped to the folder `vendor/silverstripe/admin`
where this module is installed, based on the `vendor/silverstripe/admin/composer.json` name
matching the prefix in `Requirements::css()`.
Care should also be taken when referencing images in these folders from your own stylesheets (`url()`),
or via SilverStripe templates (`<img>` tags).
`Requirements` now throws an exception then a file is not found, rather than
failing silently, so check your `Requirements` are pointing to files that exist.
```
framework/javascript => silverstripe/admin:client/dist/
framework/javascript/lang => silverstripe/admin:client/lang/
framework/images => silverstripe/admin:client/dist/images/
framework/css => silverstripe/admin:client/dist/css/
framework/scss => silverstripe/admin:client/src/styles/
admin/javascript/ => silverstripe/admin:client/src/
admin/javascript/src/ => silverstripe/admin:client/src/legacy/ (mostly)
admin/javascript/lang/ => silverstripe/admin:client/lang/
admin/scss/ => silverstripe/admin:client/styles/legacy/
admin/css/ => silverstripe/admin:client/dist/css/
admin/css/screen.css => silverstripe/admin:client/dist/css/bundle.css
admin/images/ => silverstripe/admin:client/dist/images/
admin/images/sprites/src/ => silverstripe/admin:client/src/sprites/
admin/images/sprites/dist/ => silverstripe/admin:client/dist/sprites/
admin/font/ => silverstripe/admin:client/dist/font/
```
Most JavaScript files in `framework/javascript` have been removed, Most JavaScript files in `framework/javascript` have been removed,
and are bundled through [Webpack](http://webpack.github.io/) into a combined file instead. and are bundled through [Webpack](http://webpack.github.io/) into a combined file instead.
If you have referenced these files elsewhere, please consider If you have referenced these files elsewhere, please consider
running the ES6 source files in `admin/client/src/legacy` running the ES6 source files in `vendor/silverstripe/admin/client/src/legacy`
through your own transpiling and bundle process. through your own transpiling and bundle process.
This also includes JavaScript i18n support, and the removal of the `i18n::js_i18n` This also includes JavaScript i18n support, and the removal of the `i18n::js_i18n`
@ -1229,6 +1189,8 @@ class MyTask extends BuildTask
### Moved ErrorPage into a new module {#errorpage} ### Moved ErrorPage into a new module {#errorpage}
**NOTE**: This is included automatically if you switch to using the `recipe-cms` module.
ErrorPage has been moved to a separate [silverstripe/errorpage module](http://addons.silverstripe.org/add-ons/silverstripe/errorpage) ErrorPage has been moved to a separate [silverstripe/errorpage module](http://addons.silverstripe.org/add-ons/silverstripe/errorpage)
to allow for alternative approaches to managing error responses. to allow for alternative approaches to managing error responses.
The module is installed by default on new projects, but needs to be added to existing projects The module is installed by default on new projects, but needs to be added to existing projects
@ -1590,6 +1552,7 @@ Cache::pick_backend('primary_memcached', 'any', 10);
After (`mysite/_config/config.yml`): After (`mysite/_config/config.yml`):
```yml ```yml
# setup cache
--- ---
After: After:
- '#corecache' - '#corecache'
@ -1614,13 +1577,13 @@ will assist you with bringing yourself up to speed.
Please note that before upgrading user code style it is necessary to run the standard upgrade path Please note that before upgrading user code style it is necessary to run the standard upgrade path
to fix references and usages of framework API. to fix references and usages of framework API.
### Upgrading user-code to use namespaces #### Upgrading user-code to use namespaces
Upgrading code to use namespaces is quite a complex process, and as such we have provided Upgrading code to use namespaces is quite a complex process, and as such we have provided
several development tools and processes to help make the upgrade user friendly and as several development tools and processes to help make the upgrade user friendly and as
automated as possible. automated as possible.
### Using the upgrader tool to automatically apply namespaces #### Using the upgrader tool to automatically apply namespaces
The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) provides a feature The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) provides a feature
to not only automatically namespace code, but will provide automatic upgrade of other code to not only automatically namespace code, but will provide automatic upgrade of other code
@ -1653,7 +1616,7 @@ This task will not do the following, and must be done manually:
Please see the following steps for more information. Please see the following steps for more information.
### Using the upgrader tool to update references to namespaced user classes #### Using the upgrader tool to update references to namespaced user classes
Once a project has been namespaced all newly renamed classes will have a mapping included in the `mysite/.upgrade.yml` Once a project has been namespaced all newly renamed classes will have a mapping included in the `mysite/.upgrade.yml`
file. If you have any user-code that references these, you may need to run the upgrader again (as you did did file. If you have any user-code that references these, you may need to run the upgrader again (as you did did
@ -1665,7 +1628,7 @@ cd ~/Project/Root
~/.composer/vendor/bin/upgrade-code upgrade ./othercode --write ~/.composer/vendor/bin/upgrade-code upgrade ./othercode --write
``` ```
### Updating custom dataobjects to use existing table names #### Updating custom dataobjects to use existing table names
Once you have namespaced your user code it will be necessary to customise the `table_name` config Once you have namespaced your user code it will be necessary to customise the `table_name` config
for your dataobjects, in order to ensure the correct table is used after upgrade. It is recommended for your dataobjects, in order to ensure the correct table is used after upgrade. It is recommended
@ -1681,7 +1644,7 @@ class GalleryPage extends Page
} }
``` ```
### Class name remapping {#class-name-remapping} #### Class name remapping {#class-name-remapping}
If you've namespaced one of your custom page types, you may notice a message in the CMS If you've namespaced one of your custom page types, you may notice a message in the CMS
telling you it's obsolete. This is likely because the `ClassName` telling you it's obsolete. This is likely because the `ClassName`
@ -1705,7 +1668,7 @@ SilverStripe\ORM\DatabaseAdmin:
The next time you run a dev/build the class name for all `GalleryPage` pages will The next time you run a dev/build the class name for all `GalleryPage` pages will
be automatically updated to the new `WebDesignGroup\ShopSite\GalleryPage` be automatically updated to the new `WebDesignGroup\ShopSite\GalleryPage`
### PSR-2 Coding Standard compliance {#psr2} #### PSR-2 Coding Standard compliance {#psr2}
You can use the [php codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) tool You can use the [php codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) tool
to not only detect and lint PSR-2 coding errors, but also do some minimal automatic to not only detect and lint PSR-2 coding errors, but also do some minimal automatic
@ -1723,7 +1686,7 @@ code style migration.
Repeat the final step and manually repair suggested changes, as necessary, Repeat the final step and manually repair suggested changes, as necessary,
until you no longer have any linting issues. until you no longer have any linting issues.
### PSR-4 autoloading for project code {#psr4} #### PSR-4 autoloading for project code {#psr4}
While not critical to an upgrade, SilverStripe 4.0 has adopted the [PS-4 autoloading](http://www.php-fig.org/psr/psr-4/) While not critical to an upgrade, SilverStripe 4.0 has adopted the [PS-4 autoloading](http://www.php-fig.org/psr/psr-4/)
standard for the core modules, so it's probably a good idea to be consistent. standard for the core modules, so it's probably a good idea to be consistent.

View File

@ -0,0 +1,23 @@
# 4.0.3
<!--- Changes below this line will be automatically regenerated -->
## Change Log
### Features and Enhancements
* 2018-02-01 [58840bf](https://github.com/silverstripe/silverstripe-admin/commit/58840bfba5fb5398d695b2668828ad25dabf9d2a) Capture changes on keyup with debounce (Damian Mooyman)
* 2017-11-06 [8dfa9a0](https://github.com/silverstripe/silverstripe-admin/commit/8dfa9a0f4b877d30a89c44a66809e934917409e8) debounce change events in changetracker - to reduce change event load build up with every keystroke (Christopher Joe)
### Bugfixes
* 2018-02-04 [288aaf083](https://github.com/silverstripe/silverstripe-framework/commit/288aaf083ceff944402b2877880130a54bd04a95) Fix issue with DebugView failing on class name of existing class (Damian Mooyman)
* 2018-02-01 [740c3326e](https://github.com/silverstripe/silverstripe-framework/commit/740c3326e9da7cb902778f46e209319e6dfd67a7) Fix critical issue with incorrectly saved session data (Damian Mooyman)
* 2018-02-01 [1251f62](https://github.com/silverstripe/silverstripe-asset-admin/commit/1251f62ed484045700b057890e35d8b9ad7c5bdc) Fix issue with non-asset-admin users encountering errors embedding files (Damian Mooyman)
* 2018-02-01 [b05a306](https://github.com/silverstripe/silverstripe-assets/commit/b05a306981f34181983863c4c5dd904b9ec347b3) Ensure CMS authors can all see draft files by default (Damian Mooyman)
* 2018-01-30 [cd6faac7a](https://github.com/silverstripe/silverstripe-framework/commit/cd6faac7a9cf1ea95893c14900cdd5ade0e256dc) Fix typo in error message (Raissa North)
* 2018-01-30 [0d9e20d](https://github.com/silverstripe/silverstripe-asset-admin/commit/0d9e20d1a561c4f5f8afcf9057acbddb3a90bdac) entwine+react in case they rely on the redux store (Christopher Joe)
* 2018-01-29 [771e938](https://github.com/silverstripe/silverstripe-admin/commit/771e938374832d2831da53f030e8263b7983a35f) TreeMultiselectField in Entwine sections (Christopher Joe)
* 2018-01-29 [3d7ecc524](https://github.com/silverstripe/silverstripe-framework/commit/3d7ecc524000aa0173d03c06e96d7c60a9505b5e) Allow cleanup marker regex to handle self closing HTML5 tags (Robbie Averill)
* 2018-01-29 [df71756](https://github.com/silverstripe/silverstripe-asset-admin/commit/df717562afc4d01ecac251d4d7b2e391c9e3e4cc) remove uploaded items when executing or removing search (#726) (Chris Joe)
* 2018-01-26 [8a31db5](https://github.com/silverstripe/silverstripe-errorpage/commit/8a31db5183fd28ac3eefb88bbe9ab25b414736d0) 'Error code' dropdown was misplaced (Loz Calver)

View File

@ -270,8 +270,6 @@ eo:
SilverStripe\Security\MemberAuthenticator\MemberAuthenticator: SilverStripe\Security\MemberAuthenticator\MemberAuthenticator:
ERRORWRONGCRED: 'La donitaj detaloj ŝajnas malĝustaj. Bonvole reprovu.' ERRORWRONGCRED: 'La donitaj detaloj ŝajnas malĝustaj. Bonvole reprovu.'
NoPassword: 'Mankas pasvorto por ĉi tiu membro.' NoPassword: 'Mankas pasvorto por ĉi tiu membro.'
SilverStripe\Security\MemberAuthenticator\MemberLoginForm:
AUTHENTICATORNAME: 'Retpoŝto &amp; pasvorto'
SilverStripe\Security\MemberPassword: SilverStripe\Security\MemberPassword:
PLURALNAME: 'Membraj pasvortoj' PLURALNAME: 'Membraj pasvortoj'
PLURALS: PLURALS:

View File

@ -4,6 +4,7 @@ namespace SilverStripe\Control;
use BadMethodCallException; use BadMethodCallException;
use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Config\Configurable;
use SilverStripe\Dev\Deprecation;
/** /**
* Handles all manipulation of the session. * Handles all manipulation of the session.
@ -136,6 +137,24 @@ class Session
protected $data = null; protected $data = null;
/** /**
* List of keys changed. This is a nested array which represents the
* keys modified in $this->data. The value of each item is either "true"
* or a nested array.
*
* If a value is in changedData but not in data, it must be removed
* from the destination during save().
*
* Only highest level changes are stored. E.g. changes to `Base.Sub`
* and then `Base` only records `Base` as the change.
*
* E.g.
* [
* 'Base' => true,
* 'Key' => [
* 'Nested' => true,
* ],
* ]
*
* @var array * @var array
*/ */
protected $changedData = array(); protected $changedData = array();
@ -305,38 +324,40 @@ class Session
if (!$this->isStarted()) { if (!$this->isStarted()) {
throw new BadMethodCallException("Session cannot be modified until it's started"); throw new BadMethodCallException("Session cannot be modified until it's started");
} }
$var = &$this->nestedValueRef($name, $this->data);
// Quicker execution path for "."-free names // Mark changed
if (strpos($name, '.') === false) { if ($var !== $val) {
$this->data[$name] = $val; $var = $val;
$this->changedData[$name] = $val; $this->markChanged($name);
} else {
$names = explode('.', $name);
// We still want to do this even if we have strict path checking for legacy code
$var = &$this->data;
$diffVar = &$this->changedData;
// Iterate twice over the names - once to see if the value needs to be changed,
// and secondly to get the changed data value. This is done to solve a problem
// where iterating over the diff var would create empty arrays, and the value
// would then not be set, inadvertently clearing session values.
foreach ($names as $n) {
$var = &$var[$n];
}
if ($var !== $val) {
foreach ($names as $n) {
$diffVar = &$diffVar[$n];
}
$var = $val;
$diffVar = $val;
}
} }
return $this; return $this;
} }
/**
* Mark key as changed
*
* @internal
* @param string $name
*/
protected function markChanged($name)
{
$diffVar = &$this->changedData;
foreach (explode('.', $name) as $namePart) {
if (!isset($diffVar[$namePart])) {
$diffVar[$namePart] = [];
}
$diffVar = &$diffVar[$namePart];
// Already diffed
if ($diffVar === true) {
return;
}
}
// Mark changed
$diffVar = true;
}
/** /**
* Merge value with array * Merge value with array
* *
@ -375,31 +396,7 @@ class Session
if (!$this->isStarted()) { if (!$this->isStarted()) {
throw new BadMethodCallException("Session cannot be accessed until it's started"); throw new BadMethodCallException("Session cannot be accessed until it's started");
} }
return $this->nestedValue($name, $this->data);
// Quicker execution path for "."-free names
if (strpos($name, '.') === false) {
if (isset($this->data[$name])) {
return $this->data[$name];
}
return null;
} else {
$names = explode('.', $name);
if (!isset($this->data)) {
return null;
}
$var = $this->data;
foreach ($names as $n) {
if (!isset($var[$n])) {
return null;
}
$var = $var[$n];
}
return $var;
}
} }
/** /**
@ -414,28 +411,21 @@ class Session
throw new BadMethodCallException("Session cannot be modified until it's started"); throw new BadMethodCallException("Session cannot be modified until it's started");
} }
$names = explode('.', $name); // Get var by path
$var = $this->nestedValue($name, $this->data);
// We still want to do this even if we have strict path checking for legacy code
$var = &$this->data;
$diffVar = &$this->changedData;
foreach ($names as $n) {
// don't clear a record that doesn't exist
if (!isset($var[$n])) {
return $this;
}
$var = &$var[$n];
}
// only loop to find data within diffVar if var is proven to exist in the above loop
foreach ($names as $n) {
$diffVar = &$diffVar[$n];
}
// Unset var
if ($var !== null) { if ($var !== null) {
$var = null; // Unset parent key
$diffVar = null; $parentParts = explode('.', $name);
$basePart = array_pop($parentParts);
if ($parentParts) {
$parent = &$this->nestedValueRef(implode('.', $parentParts), $this->data);
unset($parent[$basePart]);
} else {
unset($this->data[$name]);
}
$this->markChanged($name);
} }
return $this; return $this;
} }
@ -491,7 +481,8 @@ class Session
$this->start($request); $this->start($request);
} }
$this->recursivelyApply($this->changedData, $_SESSION); // Apply all changes recursively
$this->recursivelyApplyChanges($this->changedData, $this->data, $_SESSION);
} }
} }
@ -499,11 +490,13 @@ class Session
* Recursively apply the changes represented in $data to $dest. * Recursively apply the changes represented in $data to $dest.
* Used to update $_SESSION * Used to update $_SESSION
* *
* @deprecated 4.1...5.0 Use recursivelyApplyChanges() instead
* @param array $data * @param array $data
* @param array $dest * @param array $dest
*/ */
protected function recursivelyApply($data, &$dest) protected function recursivelyApply($data, &$dest)
{ {
Deprecation::notice('5.0', 'Use recursivelyApplyChanges() instead');
foreach ($data as $k => $v) { foreach ($data as $k => $v) {
if (is_array($v)) { if (is_array($v)) {
if (!isset($dest[$k]) || !is_array($dest[$k])) { if (!isset($dest[$k]) || !is_array($dest[$k])) {
@ -517,7 +510,7 @@ class Session
} }
/** /**
* Return the changed data, for debugging purposes. * Returns the list of changed keys
* *
* @return array * @return array
*/ */
@ -525,4 +518,78 @@ class Session
{ {
return $this->changedData; return $this->changedData;
} }
/**
* Navigate to nested value in source array by name,
* creating a null placeholder if it doesn't exist.
*
* @internal
* @param string $name
* @param array $source
* @return mixed Reference to value in $source
*/
protected function &nestedValueRef($name, &$source)
{
// Find var to change
$var = &$source;
foreach (explode('.', $name) as $namePart) {
if (!isset($var)) {
$var = [];
}
if (!isset($var[$namePart])) {
$var[$namePart] = null;
}
$var = &$var[$namePart];
}
return $var;
}
/**
* Navigate to nested value in source array by name,
* returning null if it doesn't exist.
*
* @internal
* @param string $name
* @param array $source
* @return mixed Value in array in $source
*/
protected function nestedValue($name, $source)
{
// Find var to change
$var = $source;
foreach (explode('.', $name) as $namePart) {
if (!isset($var[$namePart])) {
return null;
}
$var = $var[$namePart];
}
return $var;
}
/**
* Apply all changes using separate keys and data sources and a destination
*
* @internal
* @param array $changes
* @param array $source
* @param array $destination
*/
protected function recursivelyApplyChanges($changes, $source, &$destination)
{
foreach ($changes as $key => $changed) {
if ($changed === true) {
// Determine if replacement or removal
if (array_key_exists($key, $source)) {
$destination[$key] = $source[$key];
} else {
unset($destination[$key]);
}
} else {
// Recursively apply
$destVal = &$this->nestedValueRef($key, $destination);
$sourceVal = $this->nestedValue($key, $source);
$this->recursivelyApplyChanges($changed, $sourceVal, $destVal);
}
}
}
} }

View File

@ -288,7 +288,7 @@ class CoreKernel implements Kernel
$body = $body =
$dv->renderHeader() . $dv->renderHeader() .
$dv->renderInfo( $dv->renderInfo(
"Configuraton Error", "Configuration Error",
Director::absoluteBaseURL() Director::absoluteBaseURL()
) . ) .
$dv->renderParagraph( $dv->renderParagraph(

View File

@ -421,7 +421,7 @@ class DebugView
public function debugVariableText($val) public function debugVariableText($val)
{ {
// Check debug // Check debug
if (ClassInfo::hasMethod($val, 'debug')) { if (is_object($val) && ClassInfo::hasMethod($val, 'debug')) {
return $val->debug(); return $val->debug();
} }

View File

@ -490,7 +490,7 @@ class FormField extends RequestHandler
* Gets the contextual label than can be used for additional field description. * Gets the contextual label than can be used for additional field description.
* Can be shown to the right or under the field in question. * Can be shown to the right or under the field in question.
* *
* @return string Contextual label text. * @return string Contextual label text
*/ */
public function RightTitle() public function RightTitle()
{ {
@ -499,9 +499,8 @@ class FormField extends RequestHandler
/** /**
* Sets the right title for this formfield * Sets the right title for this formfield
* Note: This expects escaped HTML.
* *
* @param string $rightTitle Escaped HTML for title * @param string|DBField Plain text string, or a DBField with appropriately escaped HTML
* @return $this * @return $this
*/ */
public function setRightTitle($rightTitle) public function setRightTitle($rightTitle)

View File

@ -671,7 +671,7 @@ class Security extends Controller implements TemplateGlobalProvider
{ {
if ($request) { if ($request) {
$this->setRequest($request); $this->setRequest($request);
} elseif ($request) { } elseif ($this->getRequest()) {
$request = $this->getRequest(); $request = $this->getRequest();
} else { } else {
throw new HTTPResponse_Exception("No request available", 500); throw new HTTPResponse_Exception("No request available", 500);

View File

@ -726,7 +726,7 @@ class ShortcodeParser
$content = preg_replace_callback( $content = preg_replace_callback(
// Not a general-case parser; assumes that the HTML generated in replaceElementTagsWithMarkers() // Not a general-case parser; assumes that the HTML generated in replaceElementTagsWithMarkers()
// hasn't been heavily modified // hasn't been heavily modified
'/<img[^>]+class="' . preg_quote(self::$marker_class) . '"[^>]+data-tagid="([^"]+)"[^>]+>/i', '/<img[^>]+class="' . preg_quote(self::$marker_class) . '"[^>]+data-tagid="([^"]+)"[^>]*>/i',
function ($matches) use ($tags, $parser) { function ($matches) use ($tags, $parser) {
$tag = $tags[$matches[1]]; $tag = $tags[$matches[1]];
return $parser->getShortcodeReplacementText($tag); return $parser->getShortcodeReplacementText($tag);

View File

@ -78,18 +78,20 @@ class SessionTest extends SapphireTest
*/ */
public function testClearElementThatDoesntExist() public function testClearElementThatDoesntExist()
{ {
$s = new Session(array('something' => array('does' => 'exist'))); $s = new Session(['something' => ['does' => 'exist']]);
$s->clear('something.doesnt.exist'); $s->clear('something.doesnt.exist');
$result = $s->changedData();
unset($result['HTTP_USER_AGENT']);
$this->assertEquals(array(), $result);
// Clear without existing data
$data = $s->get('something.doesnt.exist');
$this->assertEquals(array(), $s->changedData());
$this->assertNull($data);
// Clear with existing change
$s->set('something-else', 'val'); $s->set('something-else', 'val');
$s->clear('something-new'); $s->clear('something-new');
$result = $s->changedData(); $data = $s->get('something-else');
unset($result['HTTP_USER_AGENT']); $this->assertEquals(['something-else' => true], $s->changedData());
$this->assertEquals(array('something-else' => 'val'), $result); $this->assertEquals('val', $data);
} }
/** /**
@ -97,12 +99,29 @@ class SessionTest extends SapphireTest
*/ */
public function testClearElementThatDoesExist() public function testClearElementThatDoesExist()
{ {
$s = new Session(array('something' => array('does' => 'exist'))); $s = new Session(['something' => ['does' => 'exist']]);
// Ensure keys are properly removed and not simply nullified
$s->clear('something.does'); $s->clear('something.does');
$result = $s->changedData(); $this->assertEquals(
unset($result['HTTP_USER_AGENT']); ['something' => ['does' => true]],
$this->assertEquals(array('something' => array('does' => null)), $result); $s->changedData()
);
$this->assertEquals(
[], // 'does' removed
$s->get('something')
);
// Clear at more specific level should also clear other changes
$s->clear('something');
$this->assertEquals(
['something' => true],
$s->changedData()
);
$this->assertEquals(
null, // Should be removed not just empty array
$s->get('something')
);
} }
public function testUserAgentLockout() public function testUserAgentLockout()
@ -126,4 +145,52 @@ class SessionTest extends SapphireTest
$s2->init($req2); $s2->init($req2);
$this->assertNotEquals($s2->get('val'), 123); $this->assertNotEquals($s2->get('val'), 123);
} }
public function testSave()
{
$request = new HTTPRequest('GET', '/');
// Test change of nested array type
$s = new Session($_SESSION = ['something' => ['some' => 'value', 'another' => 'item']]);
$s->set('something', 'string');
$s->save($request);
$this->assertEquals(
['something' => 'string'],
$_SESSION
);
// Test multiple changes combine safely
$s = new Session($_SESSION = ['something' => ['some' => 'value', 'another' => 'item']]);
$s->set('something.another', 'newanother');
$s->clear('something.some');
$s->set('something.newkey', 'new value');
$s->save($request);
$this->assertEquals(
[
'something' => [
'another' => 'newanother',
'newkey' => 'new value',
]
],
$_SESSION
);
// Test cleared keys are restorable
$s = new Session($_SESSION = ['bookmarks' => [ 1 => 1, 2 => 2]]);
$s->clear('bookmarks');
$s->set('bookmarks', [
1 => 1,
3 => 3,
]);
$s->save($request);
$this->assertEquals(
[
'bookmarks' => [
1 => 1,
3 => 3,
]
],
$_SESSION
);
}
} }

View File

@ -31,7 +31,7 @@ class DebugViewTest extends SapphireTest
<<<EOS <<<EOS
<div style="background-color: white; text-align: left;"> <div style="background-color: white; text-align: left;">
<hr> <hr>
<h3>Debug <span style="font-size: 65%">(DebugViewTest.php:17 - SilverStripe\Dev\Tests\DebugViewTest::setUp())</span> <h3>Debug <span style="font-size: 65%">(DebugViewTest.php:17 - SilverStripe\\Dev\\Tests\\DebugViewTest::setUp())</span>
</h3> </h3>
<pre style="font-family: Courier new, serif">string</pre> <pre style="font-family: Courier new, serif">string</pre>
</div> </div>
@ -44,7 +44,7 @@ EOS
<<<EOS <<<EOS
<div style="background-color: white; text-align: left;"> <div style="background-color: white; text-align: left;">
<hr> <hr>
<h3>Debug <span style="font-size: 65%">(DebugViewTest.php:17 - SilverStripe\Dev\Tests\DebugViewTest::setUp())</span> <h3>Debug <span style="font-size: 65%">(DebugViewTest.php:17 - SilverStripe\\Dev\\Tests\\DebugViewTest::setUp())</span>
</h3> </h3>
<ul> <ul>
<li>key = <pre style="font-family: Courier new, serif">value</pre> <li>key = <pre style="font-family: Courier new, serif">value</pre>
@ -62,12 +62,26 @@ EOS
<<<EOS <<<EOS
<div style="background-color: white; text-align: left;"> <div style="background-color: white; text-align: left;">
<hr> <hr>
<h3>Debug <span style="font-size: 65%">(DebugViewTest.php:17 - SilverStripe\Dev\Tests\DebugViewTest::setUp())</span> <h3>Debug <span style="font-size: 65%">(DebugViewTest.php:17 - SilverStripe\\Dev\\Tests\\DebugViewTest::setUp())</span>
</h3> </h3>
SilverStripe\Dev\Tests\DebugViewTest\ObjectWithDebug::debug() custom content</div> SilverStripe\\Dev\\Tests\\DebugViewTest\\ObjectWithDebug::debug() custom content</div>
EOS EOS
, ,
$view->debugVariable(new ObjectWithDebug(), $this->caller) $view->debugVariable(new ObjectWithDebug(), $this->caller)
); );
$this->assertEquals(
<<<EOS
<div style="background-color: white; text-align: left;">
<hr>
<h3>Debug <span style="font-size: 65%">(DebugViewTest.php:17 - SilverStripe\\Dev\\Tests\\DebugViewTest::setUp())</span>
</h3>
<pre style="font-family: Courier new, serif">SilverStripe\\Dev\\Tests\\DebugViewTest\\ObjectWithDebug</pre>
</div>
EOS
,
$view->debugVariable(ObjectWithDebug::class, $this->caller)
);
} }
} }

View File

@ -328,6 +328,17 @@ class ShortcodeParserTest extends SapphireTest
$stub->parse('<p>test</p>'); $stub->parse('<p>test</p>');
} }
public function testSelfClosingHtmlTags()
{
$this->parser->register('img', function () {
return '<img src="http://example.com/image.jpg">';
});
$result = $this->parser->parse('[img]');
$this->assertContains('http://example.com/image.jpg', $result);
}
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
/** /**