Prevent modifications from being overwritten by a CMS update or theme update?

Silverstripe Version: 4

Question: What is the best way to modify files (template files, css,…), where to put them etc., so that they are not overwritten with a CMS update or a theme update?

Any workflows and tips or tricks how to achieve this would be greatly appreciated.

Themes and templates use a system on inheritance (as described in the link above). So you can create your own theme for any overrides you wish to make. Your theme only has to contain the things you want to change. You configure Silverstripe to give priority to your theme.

Let’s say you have a project that includes a theme and also some external modules which have their own templates.

As an example, I’m going to use the Silverstripe Blog module.

So, you’d create your own theme with its own version of files in the themes directory (I’m calling it “app”) and add some templates in there, Once you are in your templates directory, you need to use the same directory structure as the thing you’re overriding.

themes/app/templates/SilverStripe/Blog/Model/Layout/Blog.ss

In your yml config, you tell Silverstripe to give priority to your theme:

---
Name: mytheme
---
SilverStripe\View\SSViewer:
  themes:
    - '$public'
    - 'app'
    - 'silverstripe-base-theme'
    - '$default'

You’ll see in there I also have ‘silverstripe-base-theme’ in the list… this is an external theme which also may contain files.

When Silverstripe processes a request, it will work down that list looking for the first instance of the template it needs. If we’re rendering the blog page, it will find the template in your ‘app’ theme and use that. When we look at a Blog Post, because we didn’t add that template file to our theme, it will move on to the next entry in the list above, and will look in the base theme that was installed… if it finds the template, it uses it. If not, it tries the next entry ($default). This looks in the default location for the template (in this case it would be vendor directory for the blog module)

Using this inheritance, changes to the default templates or the imported theme won’t override your ‘app’ version of the templates.

1 Like

In terms of CSS and JS… that is a little more complicated, depending on how the original assets are being included.

There are ways for assets to be included which also take advantage of the inheritance system above, these use the ThemedResourceLoader functionality to find the first instance of an asset… but there’s no guarantees that they will be in place.

Asset files could be included in PHP code, or from the template. Ultimately, though, you’re looking to achieve a similar concept as before: maintain your own version of the CSS or JS that you want to control and tell Silverstripe to use that instead of the defaults.

If assets are included in template files, you can simply remove them using the template inheritance described above. If they’re included via PHP, you can use Requirements::block() to remove them, if they’re using the ThemedResourceLoader, you can simply replicate the path in your theme (in the same was as templates) and they should be picked up automatically.

I’m having to give some generic answers to what may be a specific question… so if you have some more details about your particular issue, please add them here.

1 Like

Hi @Tim - thanks so much for taking the time to reply in so much detail to my question - it is very helpful and so much appreciated!

In regards to your tip:

Could you let me know what the the ‘$public’ does as the first entry since I am not sure about that. Keeping the same directory structure for template overrides then works pretty similar to Joomla.

In regards to ‘override-safe’ adding of external CSS/JS files - would adding these in the ‘PageController.php’ be a good option - like so:

protected function init() {
            parent::init();

            $cssfiles = [
                'public/css/file1.css',
                'public/css/file2.css'
            ];
            Requirements::combine_files("pagecss.css", $cssfiles);
....

I guess I am struggling a little with understanding which files would get overwritten in say a Silverstripe update from version 4 to version 5 (or a theme update) and which directories and/or files would be left untouched and would be safe for code customizations?

Thanks again!

That tells the resource loader to check in the public directory first. It just allows you to have files in the public directory without needing to have them exposed via vendor-expose… it’s probably not needed most of the time to be fair, but I just have it in there through habit!

The docs mention it here:

Anything which is not under your direct control has the potential to be changed in an update. Anything you have put in place, only gets updated when you choose. So, if you have files in your example, and you put them there, they won’t be affected during any upgrade. The same applies if you have the CSS / javascript as part of a theme which you have made.

If there are asset files coming from the core code or an added module, then there is a chance they could be changed during an upgrade.

If you maintain control over your own asset files, either in /public directly or through a theme, you should be safe.

Thanks again for your feedback @Tim

Quick question - is it possible to overwrite the ‘PageController.php’ or the ‘Page.ss’ file by placing them in the public folder - meaning have a copy of the file there which is picked first (before using the system versions)?