This blog post introduces nine ways to make a module fit for purpose to SilverStripe newbies. For more experienced SilverStripe coders, it may serve as a 'module hack cheat sheet'.
There are a myriad of modules on addons.SilverStripe.org. Often times, you will find a module that does most of what you need, but not quite what is required for the job at hand. The steps below introduce ‘easy, lightweight, hassle-free’ solutions through to ‘advanced, invasive, and hard-work’ ways of customising a module.
1. Open the CMS
For many modules, you should be able to make them work as you see fit by editing your data in the CMS. Here are a few places to check:
- Should any objects or pages be created? Check individual pages for setting. Don't forget the settings tab for individual pages.
- Are there any site wide configs to be set (see /admin/settings/ )?
- Were any new items added to the main CMS menu options when you installed the module?
To avoid having to update every instance of your project, you can write a bit of code that will run every time you do a dev/build. For this, the requireDefaultRecords method is ideal. Here is a basic example:
More elaborate database updates can be achieved by writing a BuildTask:
You can run this task from the /dev/tasks/ExampleBuildTask URL (or sake /dev/tasks/ExampleBuildTask from the command line) whenever required.
2. Wording Change
It is amazing what can be achieved with a simple wording change. The first step is to find out where the text is defined. Search all files (use grep or your editor’s search functionality) will give you some quick answers. When I searched for “log in as someone else”, I found this code snippet in our beloved LeftAndMain class.
You will also find this phrase in /framework/lang/en.yml and some other language files.
To change this phrase, you need to know what is the default locale for your site and any other likely locales set for individual users (in most cases this will be the same as the default locale).
To change the wording, add the following file: mysite/lang/en.yml to your project. In that file, you can provide alternative wording like this:
The name of the language file depends on the locale you want to update (e.g. en.yml may need to be changed to es_AR.yml (Argentine Spanish) when you are building a site offering hotel bookings in Buenos Aires). Have a look at “i18n - setting the locale” and also an example language file.
By default, translations stored in the mysite folder have top priority (see: i18n - module priority) so there is no need to create the epic zzzzz-language-fix-folder.
3. Change configs
Most modules make use of the config layer for settings such. For example, the SiteTree class sets the generator tag content using this approach. In short, private static variables in PHP classes store configuration options and they are adjusted using the yml files. To change settings, you can add and edit these yml files.
For example, create a file mysite/_config/module_name.yml and add content like this:
This code will override the private static variable my_private_static_var in MyClass and set it to 21.
To get an idea of the options, have a look in the module README.md file, the _config folder in the root directory of the module and also search for $this->Config() and Config::inst()->get in the module code to see what configs are available. You can also use this private static discovery tool.
For more information on the Silverstripe configuration options, see the configuration documentation.
Alternatively, you can simply block a CSS file in your PHP code:
5. Can it be fixed with HTML?
Just like we themed CSS files, you can also theme a template file. Firstly, check the source of the output HTML to see what file needs theme-ing. Searching in files for a class or a unique snippet of html that you would like to change is usually the fastest way to find the template file. You can also add the following to the mysite/_config/module-name.yml file:
Once you have found the file in the module, you can create the equivalent in the theme folder (e.g. themes/themename_module_name/templates/Includes/MySnippet.ss). This new file will then be used instead of the original MySnippet.ss file in the module.
For more information have a look at the templating documentation.
6. Is there a module that can add the functionality?
It is definitely worthwhile to find out what other modules require the module you are trying to adjust. Some of these other modules may actually add the functionality you are looking for already. Here is an example of the modules that require the SilverStripe blog. You can replace SilverStripe:blog in the URI with your module at hand.
Of course, it also pays to have a look at addons.SilverStripe.org and also ask the Silverstripe Community to see if anyone else has done something similar to what you need to do.
7. Extend a class
If none of the options listed so far are any good, then it is time for the real McCoy
… class MyClass extends ModuleClass ….
Most of the time this works a treat, but here are a few issues you may encounter:
(1) The class is hard-coded in the module. For example, in the module there is a line:
$obj = ModuleClass::create();
which results in your objects being created as ModuleClasses rather than a MyClasses. For example, if you extend Member as MyMember, then you still may end up with Member objects in the database that do not have the functionality you added in the MyMember class. See recommendation 9 for a possible solution to this problem (replacing a class).
(2) In other cases, the class name is used in the HTML/CSS which means that default formatting does not work anymore. This is usually fairly easy to fix, but definitely something to watch for as in some circumstances this can create an unexpected challenge.
(3) If you are extending a page type then make sure to hide the original page type using the static variable hide_ancestor.
8. Call in the Decorators
There are two key classes in SilverStripe that allow you to “decorate” classes. Basically these are what the future calls PHP Traits, or what we may call clipons or mixins. They add functionality to a class without extending it in the traditional way. Creating a class extension comes in two steps:
1. create your extension
2. add your extension to the original class using the config layer (yml files)
DataExtensions can decorate any class that extends DataObject and Extensions can decorate any instance of Object (almost every class in the SilverStripe Framework has Object as a (distant) ancestor).
To find out where the “hooks” are, do a search for ->extend in the module you are working on. Most key methods will have an extend call, like the updateCMSFields call (or hook) in the getCMSFields method. Many private static configuration variables, such as $db, $has_many, $field_labels, etc…, in DataExtensions are added to the DataObject being extended.
For more information, have a look at the extensions documentation.
9. Create a custom class
A lesser known option is that you replace a module class with your own class, as opposed to extending it. The injector documentation explains how this works. Here is an example on how to replace a class, ModuleClass, with a custom class MyClass
Still no luck?
Other methods to consider include:
- Making a fork of the module and pointing your composer file to this “custom repository” - stack overflow provides the basics on how to do this.
- Submitting a pull request to the original module.
- Using a composer script in your composer.json file to, for example, remove a file from a module.