Version 4 supported
This version of Silverstripe CMS is still supported though will not receive any additional features. Go to documentation for the most recent stable version.

4.1.0

Overview

  • Support for public webroot folder public/
  • Better support for cross-platform filesystem path manipulation

Upgrading

Read Upgrading to Silverstripe 4 for a detailed step-by-step guide on upgrading your Silverstripe 3 project to Silverstripe 4. It describes how to use an automated upgrader tool to make the job easier. Please also note the 4.0.0 and 4.2.0 changelogs for a complete list of changes.

Use public/ folder (optional)

This release allows the maintenance of a public webroot folder which separates all web-accessible files from protected project files (like the vendor folder and all of your PHP files). This makes your web hosting more secure, and less likely to leak information through accidentally deployed files like a project README. New projects will default to this behaviour. Existing projects (updating from 3.x or 4.0) will continue working as-is, but we strongly recommend switching to the public webroot structure in order to get the security benefits.

This folder name is not configurable, but is turned on by creating this folder, and off by ensuring this folder doesn't exist.

When separating the public webroot from the BASE_PATH it is necessary to move a few files during migration:

  • Move your existing .htaccess from base to public/
  • Update the RewriteRule directives in the .htaccess (check the default .htaccess)
  • Create a new .htaccess in base that contains:

    RewriteEngine On
    RewriteRule ^(.*)$ public/$1
  • Move index.php from base to public/
  • If upgrading from 4.0, replace the index.php file from recipe-core in order to fix autoloading paths
  • Move assets folder (including the nested assets/.protected folder) into public/. This is the only folder which needs write permissions.
  • Ensure that the public/resources folder exists; If this folder already exists in root, you should delete this, and re-generate it by running composer vendor-expose in your root path.
  • Any public assets committed directly to your project intended to be served directly to the webserver. For example move mysite/javascript/script.js to public/javascript/script.js. You can then use Requirements::css('javascript/script.js'); / <% require css('javascript/script.js') %> to include this file.
  • Ensure that the web-root configured for your server of choice points to the public/ folder instead of the base path. e.g. an apache virtualhost configuration would look like:
<VirtualHost *:80>
  ServerName mywebsite.com
  ServerAlias *.mywebsite.com
  VirtualDocumentRoot "/var/www/Sites/mywebsite/public/"
</VirtualHost>

You may also need to add various changes to your code if you reference the BASE_PATH directly:

  • You should use Director::publicFolder() instead of Director::baseFolder() if referring to the public folder.
  • You can check if a public folder exists with Director::publicDir()

Example public/ folder structure

For example, this is an existing folder structure:

/var/www/mysite
├── assets/
│   └── .protected/
├── mysite/
│   ├── code/
│   │   ├── Page.php
│   │   └── PageController.php
│   └── css/
│       └── projectstyle.css
├── resources/ _(auto-generated by vendor-plugin `composer vendor-expose` command)_
│   └── silverstripe/
│       └── blog/
│           └── css/ _(symlink)_
│               └── blog.css
├── themes/
│   └── mytheme/
│       ├── css/
│       │   └── theme.css
│       └── templates/
│           └── BlogPage.ss
├── vendor/
│   └── silverstripe/
│       └── blog/
│           ├── css/ _(exposed in blog composer.json)_
│           │   └── blog.css
│           └── composer.json
├── .htaccess
├── composer.json
├── favicon.ico
├── index.php
└── install.php

After migration the folder structure would look like:

/var/www/mysite
├── mysite/
│   └── code/
│       ├── Page.php
│       └── PageController.php
├── public/
│   ├── assets/
│   │   └── .protected/
│   ├── css/
│   │   └── somestyle.css
│   ├── resources/ _(auto-generated by vendor-plugin `composer vendor-expose` command)_
│   │   ├── themes/
│   │   │   └── mytheme/
│   │   │       └── css/ _(symlink)_
│   │   │           └── theme.css
│   │   └── vendor/
│   │       └── silverstripe/
│   │           └── blog/
│   │               └── css/ _(symlink)_
│   │                   └── blog.css
│   ├── .htaccess
│   ├── favicon.ico
│   ├── index.php
│   └── install.php
├── themes/
│   └── mytheme/
│       ├── css/ _(exposed in root composer.json)_
│       │   └── theme.css
│       └── templates/
│           └── BlogPage.ss
├── vendor/
│   └── silverstripe/
│       └── blog/
│           ├── css/ _(exposed in blog composer.json)_
│           │   └── blog.css
│           └── composer.json
└── composer.json

Use new $public theme set

In addition there is a new helper pseudo-theme that you can configure to expose files in the public/ folder to the themed CSS / JavaScript file lookup. For instance, this is how you can prioritise those files:

---
Name: mytheme
---
SilverStripe\View\SSViewer:
  themes:
    - '$public'
    - 'simple'
    - '$default'

This would allow <% require themedCSS('style.css') %> to find a file committed to public/css/style.css.

Note that Requirements calls will look in both the public folder (first) and then the base path when resolving CSS or JavaScript files. Any files that aren't in the public folder must be exposed using the composer.json "expose" mechanism described below.

Expose root project files

If you have files committed to source control outside of the public folder, but you need them to be available to the web server, you can also use the composer.json expose directive to symlink / copy these to public/resources/.

// composer.json
{
  // ...
  "extra": {
    "expose": [
      "mysite/client"
    ]
  }
}

You will also need to set your project type to silverstripe-something, for example silverstripe-project like so:

// composer.json
{
  // ...
  "type": "silverstripe-project"
}

Then run the composer helper composer vendor-expose in your project root. This will symlink (or copy) the mysite/client directory to public/resources/mysite/client.

If you are using third party modules which may not have explicit expose directives, you can also expose those assets manually by declaring the full path to the directory to expose. This works the same for silverstripe-module and silverstripe-vendormodule types.

{
  // ...
  "extra": {
    "expose": [
      "vendor/somevendor/somemodule/css",
      "anothermodule/css"
    ]
  }
}

For more information on how vendor modules work please see the documentation on the vendor plugin page or the publishing a module documentation.

Path manipulation helpers

The following filesystem helpers have been added in order to better support working with cross-platform path manipulation:

  • SilverStripe\Core\Convert::slashes() to convert directory separators to either / or \
  • SilverStripe\Core\Path::join() which will join one or more relative or absolute paths.
  • SilverStripe\Core\Path::normalise() which will normalise and trim directory separators in a relative or absolute path

For example: normalising Convert::normalise('/some\\dir/') will convert to /some/dir. Setting the second arg to true will also trim leading slashes. For example Convert::normalise('/sub\\dir/', true) will convert to sub/dir.

It is preferable to use these helpers in your code instead of assuming DIRECTORY_SEPARATOR === /

Change log

API changes

  • 2018-02-13 eed518b Shift TestAssetStore to non-test dev namespace to ensure this class is available for other modules (Damian Mooyman)
  • 2018-02-01 03cb478 Enable upload / attach options on UploadField to be individually toggled on or off (Damian Mooyman)
  • 2018-01-30 1b13072 Ensure that duplications fail over to owned objects (Damian Mooyman)
  • 2018-01-30 aa2c71424 Implement cascade_duplications (Damian Mooyman)
  • 2018-01-22 04050b275 Director::host() now formally includes port in host (Damian Mooyman)
  • 2018-01-18 4b8140e Refactor GridFieldVersionedState into versioned module (Damian Mooyman)
  • 2018-01-18 f4092f7bc Deprecate GridFieldVersionedState (Damian Mooyman)
  • 2018-01-12 8d077203d Implement support for public/ webroot folder (#7741) (Damian Mooyman)
  • 2017-12-21 8900298 Support public/ webroot folder (Damian Mooyman)
  • 2017-12-19 b2ce6b4 Update to support public-files (Damian Mooyman)
  • 2017-12-13 c4ff8443b Shift basic auth checking into middleware (Damian Mooyman)
  • 2017-12-11 c59c9e0 Split ownership and recursive publishing into RecursivePublishable (Damian Mooyman)
  • 2017-12-11 9170220d2 Add onAfterSave extension point to GridFieldDetailForm_ItemRequest (Damian Mooyman)
  • 2017-11-27 1fedc5fc Make CMSMain::getSiteTreeFor() default to null nodeCount (#2029) (Lee Bradley)
  • 2017-11-14 f863573d1 Add getShortName to DBClassName (Damian Mooyman)
  • 2017-11-07 642cbdafc Allow an array as a param to makeFieldReadOnly() (Reece Alexander)

Features and enhancements

  • 2018-03-05 32637413d Improve upgrade rules to support advanced upgrader rewrites (#7903) (Damian Mooyman)
  • 2018-03-05 8c35e339 Improve upgrade rules to support advanced upgrader rewrites (#2114) (Damian Mooyman)
  • 2018-03-01 61cfcc5 Improve upgrade rules to support advanced upgrader rewrites (Damian Mooyman)
  • 2018-03-01 e77e0f7 Improve upgrade rules to support advanced upgrader rewrites (Damian Mooyman)
  • 2018-02-27 aaf4f5a expose extension point for item props in uploadfield (Christopher Joe)
  • 2018-02-19 9ebea37b Add extension point to CMSMain::Breadcrumbs (Robbie Averill)
  • 2018-02-19 1ae5f46 Allow CMS breadcrumb badge to be extended (Robbie Averill)
  • 2018-02-12 9ce21338a composer.json missing notice (zanderwar)
  • 2018-02-02 dd225215 Use Bootstrap alerts instead of legacy message classes for install.php warning (Robbie Averill)
  • 2018-02-02 f582954a Use Bootstrap alerts instead of legacy message classes for install.php warning (Robbie Averill)
  • 2018-01-31 71f9a30 UploadField uses injector for child components (#722) (Chris Joe)
  • 2018-01-30 5892acf GraphQL versioning support (#83) (Aaron Carlino)
  • 2018-01-30 296d18c Versioning support for scaffolded operations (#128) (Aaron Carlino)
  • 2018-01-24 456871fd9 Updated PasswordValidator to fallback to config options - still retains instance variables (Christopher Joe)
  • 2018-01-24 e99bd6f4 Ensure all base folders are excluded (Damian Mooyman)
  • 2018-01-23 fcf502255 Add support for ordinals in DBDate::Format() (Loz Calver)
  • 2018-01-22 89c8a28 Put gridfield page count in button toolbar for security admin (Damian Mooyman)
  • 2018-01-17 827537f Add version status badge in gridfield detail view (Saophalkun Ponlu)
  • 2018-01-12 9fddb5de9 Add getter and setter for removeRelation in GridFieldDeleteAction (Robbie Averill)
  • 2017-12-22 ce1a3a4 Gridfield uses versioned by default (Saophalkun Ponlu)
  • 2017-12-21 294cfc8 Add data object status badge in gridffield detail view (Saophalkun Ponlu)
  • 2017-12-17 8a29918a Remove duplicated report heading (Robbie Averill)
  • 2017-12-13 31e04c849 Allow html in security failure message (Saophalkun Ponlu)
  • 2017-12-12 06cecc6 Set .gitattributes to ignore tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-12 c342f49 re-instate setAllowedMaxFileNumber() to the new UploadField (Christopher Joe)
  • 2017-12-12 4f946aa Add cache service for file inherited permissions (Aaron Carlino)
  • 2017-12-11 d0e5bc1 Major refactor of validation and error handling on react forms (Aaron Carlino)
  • 2017-12-10 ccece4b update composer with --prefer-source flag so that tests are included (Christopher Joe)
  • 2017-12-10 1c1f9a1 Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 33a68dda Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 4d805361 Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 b959af3 Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 aff4babfa Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 50ed744 Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 2985b4a6 Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 d08d472 Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-10 0d422e9 Set .gitattributes to ignore tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-12-07 b8c9027 Set .gitattributes to ignore docs and tests folder, and some source asset files which are not needed (Christopher Joe)
  • 2017-11-30 8ee5d9f5 Cache canCreate (Aaron Carlino)
  • 2017-11-29 2dcb3fd2a Add documentation for pattern library (Christopher Joe)
  • 2017-11-27 a7847b573 Add the ability to redirect a user to a custom page (Damian Mooyman)
  • 2017-11-15 c7ab5846d Don't infer trace if explicitly provided (Damian Mooyman)
  • 2017-11-07 81aa617 eslint config (Aaron Carlino)
  • 2017-11-07 8adde15 eslint config (Aaron Carlino)
  • 2017-11-07 7f3db6b dependencies for eslint package (Aaron Carlino)
  • 2017-11-06 2e43780a8 Add sort columns to DB index automatically (Daniel Hensby)
  • 2017-11-01 91f1a5801 Allow chain-ability on adding and removing fields (zanderwar)
  • 2017-10-23 5b335ad5 Add support for callable link formatting (Will Rossiter)
  • 2017-09-28 2f0a0cb63 Add (alt text) to title field for clarity (Robbie Averill)
  • 2017-01-19 da9c133c1 Add flattenFields() function to iterate over all deeply nested fields in a form (Jake Bentvelzen)

Bugfixes

  • 2018-03-11 2b9faf46 Fix InSection failing on non-page controllers (Damian Mooyman)
  • 2018-03-07 bf2cee398 - Correct duplicate nesting of 'Content' to be returned to template (Joe Harvey)
  • 2018-03-06 5fee4a81a Files dataobjects with missing asset shouldn't un-attach themselves from parent object on save (Damian Mooyman)
  • 2018-03-05 dde13493 Fix invalid css classname in virtualpage (Damian Mooyman)
  • 2018-03-05 985a0af Fix page icons (Damian Mooyman)
  • 2018-03-02 3bd714d Typo in "audio file" translation (Robbie Averill)
  • 2018-03-01 40c2e299a "mb_stripos(): Empty delimiter" warning when no search-keywords are given for DBText::ContextSummary. (Roman Schmid)
  • 2018-02-27 d91c6ed Fix $CMSVersion appearing visually (Damian Mooyman)
  • 2018-02-26 b27102f81 Fix incorrect assets created when ASSETS_PATH !== BASE_PATH . '/assets' (Damian Mooyman)
  • 2018-02-22 012bfec5 Bug field help text translations no longer need to be HTML encoded (Rick Hambrook)
  • 2018-02-20 83c4ab8d Fix test regressions in CMS page filters (Damian Mooyman)
  • 2018-02-19 cfe82e9 Fix behaviour towards versioned but unstagable records (Damian Mooyman)
  • 2018-02-19 42c6caa Fix behaviour towards versioned but unstagable records (4.1 specific) (Damian Mooyman)
  • 2018-02-19 4fc8166 Fix behaviour towards versioned but unstagable records (Damian Mooyman)
  • 2018-02-19 0e26c0664 Fix behaviour towards versioned but unstagable records (Damian Mooyman)
  • 2018-02-19 3be0478e Fix behaviour towards versioned but unstagable records (Damian Mooyman)
  • 2018-02-19 33165e5 Always run updateBadge extension, even if Versioned has no badges of its own (Robbie Averill)
  • 2018-02-19 8be3930 Fix doRollbackTo() writing old / unsaved version over restored version (Damian Mooyman)
  • 2018-02-14 2fab96b behat tests with new cms button labels (Christopher Joe)
  • 2018-02-13 911a50c tests to use new TestAssetStore (Damian Mooyman)
  • 2018-02-13 c767e472d DataObject singleton creation (Jonathon Menz)
  • 2018-02-13 f2b82b1f7 docs for configuring before/after a specific config file (Christopher Joe)
  • 2018-02-13 0b7cf8033 Fix incorrect convert slashes argument (Damian Mooyman)
  • 2018-02-13 c6095cf word boundary issue with pathname matching (Christopher Joe)
  • 2018-02-13 1d27a14 Remove border-radius add hover states to non-active tabs (Sacha Judd)
  • 2018-02-12 ad52ced Prevent nested permissions from breaking recursive publishing (Damian Mooyman)
  • 2018-02-12 0f08f85 improve the browser warning logic show (Christopher Joe)
  • 2018-02-12 423cbe0 Fix broken createArgs() for scaffolded reads (#138) (Damian Mooyman)
  • 2018-02-08 d86e5dfc remove now superfluous print action destroyer (Dylan Wagstaff)
  • 2018-02-08 d3278d547 Add Nested DB transaction support (#7848) (Daniel Hensby)
  • 2018-02-08 0a486b8f5 Fix issue with CLIDebugView failing on class name of existing class (Damian Mooyman)
  • 2018-02-08 df62ffa Fix createArgs() for Versioned graphql operations (Damian Mooyman)
  • 2018-02-07 dd3fbf2c7 Fix installer checking wrong location for files (Damian Mooyman)
  • 2018-02-07 a0fa38b Fit sort dropdown to icon on smaller sizes to prevent wrapping (Luke Edwards)
  • 2018-02-07 86f2ff4 Align buttons to right to keep consistency (Luke Edwards)
  • 2018-02-07 24dfe11 Asset admin toolbar buttons stacking vertically on small screens (Luke Edwards)
  • 2018-02-07 e5ff48b35 Fix incorrect BASE_URL when webroot is parent of public folder (Damian Mooyman)
  • 2018-02-07 168b8999 Add missing button styles to restore draft action (Luke Edwards)
  • 2018-02-07 b060892 Fix routing error for public assets (Damian Mooyman)
  • 2018-02-06 0094c19 Add text-colour to status-archived, remove span.badge styles (Sacha Judd)
  • 2018-02-06 6b38031a1 Fix Director::test() not persisting removed session keys on teardown (Damian Mooyman)
  • 2018-02-06 660dfd34a Issue where default admin has no password encryption (Daniel Hensby)
  • 2018-02-05 28ca11dd7 Regex range identifier correctly escaped (Daniel Hensby)
  • 2018-02-05 db9aa2c5c Fix regression in has_one getters breaking DataDifferencer (Damian Mooyman)
  • 2018-02-04 dbecf1c Prevent versioned records self-publishing (Damian Mooyman)
  • 2018-02-04 1ff32b3 Ensure lang is detected from html tag (Martin P)
  • 2018-02-02 6a025f7 Remove unnecessary recursivePublish() calls (Damian Mooyman)
  • 2018-02-01 bc2fc7f2d Prevent invalid members being written to database if validation_enabled is false (Damian Mooyman)
  • 2018-01-31 884cee7 Prevent versioned crashing in absence of graphql module (Damian Mooyman)
  • 2018-01-30 07e70148 Add some exist check for tabsets which may not be there (#2079) (Chris Joe)
  • 2018-01-30 206e8b4 Remove duplicate extensible traits (#133) (Damian Mooyman)
  • 2018-01-26 416915b08 tableName is blank in CompositeDBField->addToQuery (Dominik Beerbohm)
  • 2018-01-25 cf69d0486 Fix ping including requirements (Damian Mooyman)
  • 2018-01-24 c2cd6b383 Fix Member_GroupSet::removeAll() (fixes #3948) (Loz Calver)
  • 2018-01-24 f2b4c192e Fix UploadField cuts off “Save” button (closes #2862) (Loz Calver)
  • 2018-01-24 c23acae remove linting error for dependents' benefit (Dylan Wagstaff)
  • 2018-01-23 cbcff97 performance issue with context for injector (#405) (Chris Joe)
  • 2018-01-23 7384e3fc2 Gridfields with dropdowns having lots of overflow (Scott Hutchinson)
  • 2018-01-22 c32b3dd5b Prevent versioned from breaking gridfield tests (Damian Mooyman)
  • 2018-01-18 b32cc57 breadcrumbs casting (Damian Mooyman)
  • 2018-01-17 7eef400 Ensure there is an error response if composers autoloader cant be found (Daniel Hensby)
  • 2018-01-16 ca2fe3b1f Fix themed editor.css url not being resolved properly (Damian Mooyman)
  • 2018-01-16 20348b5f4 Fix upgrade-code doctor with public path (Damian Mooyman)
  • 2018-01-15 15ef769e1 Don't re-declare manually-declared variables for public folder (Damian Mooyman)
  • 2018-01-15 292647d Fix routing error for public assets (Damian Mooyman)
  • 2018-01-12 23a23043 successfullyinstalled() (#2069) (Damian Mooyman)
  • 2018-01-02 b3ae11fe4 Missing Part of Sentence (David Lloyd)
  • 2017-12-22 212178bb0 SSViwer::execute_template should allow requirements to be included (Daniel Hensby)
  • 2017-12-19 2fd81eea no button view type toggle when searching pages (Saophalkun Ponlu)
  • 2017-12-19 646ba571 Amend rules to ignore test files only (Christopher Joe)
  • 2017-12-19 5fdd3a5 Amend rules to ignore test files only (Christopher Joe)
  • 2017-12-19 ae9d016 Amend rules to ignore test files only (Christopher Joe)
  • 2017-12-19 fe319e85c Amend rules to ignore test files only (Christopher Joe)
  • 2017-12-18 74cd028 applyTransforms not using new addErrors() API (Aaron Carlino)
  • 2017-12-14 c5bd9bb42 Fix incorrect BASE_DIR inferred in CLI (Damian Mooyman)
  • 2017-12-14 b1099a9 php lint errors (Saophalkun Ponlu)
  • 2017-12-14 140ed72e2 Fix message casting for html security messages (Damian Mooyman)
  • 2017-12-14 3eda4e0 Clean up i18n._t() (Christopher Joe)
  • 2017-12-14 8b1b9f022 linting issues (Damian Mooyman)
  • 2017-12-14 b4a35293 Minor linting violation in Report (Robbie Averill)
  • 2017-12-13 210213a4 search behaviour (Damian Mooyman)
  • 2017-12-13 c2d54a57 up code style, fix tests (Damian Mooyman)
  • 2017-12-13 749616e linting issues (Damian Mooyman)
  • 2017-12-12 8a04e9c4 wrong/confusing url segment (Saophalkun Ponlu)
  • 2017-12-11 6dd07b52 CI config for 4 branch (Damian Mooyman)
  • 2017-12-10 627b49637 linting issue (Damian Mooyman)
  • 2017-12-07 381ad756f tests (Saophalkun Ponlu)
  • 2017-12-07 b391db49 search view (Saophalkun Ponlu)
  • 2017-12-03 fa2cb40 travis (Christopher Joe)
  • 2017-12-01 682ef89 loading order for modules (Christopher Joe)
  • 2017-11-30 0884f05 Fix broken unit tests (Christopher Joe)
  • 2017-11-29 2717f0134 link to nginx.org wiki (JorisDebonnet)
  • 2017-11-29 4b69fa49e Fix travis (Christopher Joe)
  • 2017-11-29 5b40419 Fix removed scss variables (Christopher Joe)
  • 2017-11-27 86f84597 Actioned feedback (Christopher Joe)
  • 2017-11-14 4ba6056 update lock and eslintrc reference (Christopher Joe)
  • 2017-11-14 b5345fd update lock and eslintrc reference (Christopher Joe)
  • 2017-11-14 861bfa7 update lock and eslintrc reference (Christopher Joe)
  • 2017-11-09 ba2c5b48f Ensure relObject() safely bails on empty objects (Damian Mooyman)
  • 2017-10-26 0dcc9d6 Prevent massive recursion of publish writes (Damian Mooyman)
  • 2017-10-16 cafa3fc29 switch to trigger_error() when a resource is not found (#7468) (Chris Joe)
  • 2017-10-11 7adedd0 Remove duplicate "field" class in FieldGroup holder template (Robbie Averill)
  • 2017-09-27 c95916037 Misnamed test namespaces (Daniel Hensby)
  • 2016-10-21 8e5bb6fbd Fix : relObject() should return null if one of the node is null (Jason)
  • 2016-03-15 22b3a71ec ing val reference to url in https hotlink (Denise Rivera)
  • 2015-04-22 1f63637b9 for #4095, TinyMCE not able to modify props of embed media (bug 1) and invalid HTML inserted (bug 2) (Patrick Nelson)