Experimental feature
This is an experimental feature, so there could be unexpected issues when using this feature.
Extensions are a type of Nova mod that are placed within the application/extensions
folder and enabled in application/config/extensions.php
. As opposed to other types of Nova mods, extensions do not require modifying any Nova code files. This simplifies installation and maintenance.
Installing an extension
When you download an extension, the installation will always follow roughly the same process:
- Place the extension directory within your
application/extensions
directory; - Execute any database commands (or other actions) stipulated by the extension; and
- Add the extension to the end of
application/config/extensions.php
.
For example, if you have extracted an extension named my_extension
into application/extensions/my_extension
, it could be enabled by adding the following to application/config/extensions.php
:
1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');2 3require_once MODPATH.'core/config/nova_extensions.php';4 5$config['extensions']['enabled'][] = 'my_extension';
Some extensions may specify that they require another extension. If this is the case, you should ensure that all required extensions come before the extension that requires them within the application/config/extensions.php
file.
Building an extension
An extension is defined as a directory placed within the application/extensions
directory that, at minimum, has a single file init.php
. The init.php
file serves to bootstrap the file, attaching event listeners, defining other required extensions, etc., as necessary to achieve the extension's purpose.
As long as the extension is enabled within application/config/extensions.php
like this:
1$config['extensions']['enabled'][] = 'my_extension';
Then the extension's application/extensions/my_extension/init.php
will be run. All of the extension's code may be packaged directly within init.php
or it may be included/required from other files.
Extension init.php
The init.php
file has its own scope with a number of properties and methods available within the $this
variable.
To get the name of the extension, such as my_extension
for application/extensions/my_extension/init.php
:
1$this->name
To get the path of the extension directory, such as application/extensions/my_extension
for application/extensions/my_extension/init.php
:
1$this->path
To set another extension that must be loaded first (so specified earlier in application/config/extensions.php), such as requiring the jquery
extension in another extension:
1$this->require_extension('jquery');
The two main reasons for requiring an extension:
- An extension requires behavior enabled by another extension; or
- An extension uses an object exposed by another extension.
For case (2), an extension can expose an object for use by other extensions as:
1$this->attach('generator', new jquery_generator());
Supposing that the above statement was within a jquery
extension, then this exposed object can be leveraged in any extension that requires it (as well as in a sim's application code):
1$this->extension['jquery']['generator']->select('.page-head')->html('Hello World!')
Besides exposing objects, the primary thing an extension does is hook into the CodeIgniter instance to modify behavior.
To interface with the CodeIgniter instance:
1$this->ci
For example, an extension could attach an event listener such as:
1$this->ci->event->listen(['template', 'render', 'data'], function ($event) {2 $event['data']['javascript']3 .= '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/timepicker/1.3.5/jquery.timepicker.min.css">'4 . '<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/timepicker/1.3.5/jquery.timepicker.min.js"></script>'5 . $this->extension['timepicker']->inline_js('timepicker', 'global');6});
For shorthand, calls directly to $this
are call-forwarded to $this->ci
.
Extension controllers and views
An extension can define controllers within a controllers folder inside the extension folder. In order to avoid URL collisions between the extension and other routes in the Nova app, extensions will have a URL like:
1http://my.domain.com/extensions/my_extension/my_controller/my_method
The controller would then be specified in:
1application/extensions/my_extension/controllers/my_controller
To avoid a class name collision, the controller class must also be named in a special way:
- Start the class name with
**extensions
- Next, place two more underscores
**
- Next, place the extension name, such as
my_extension
- Next, place two more underscores
**
- Next, place the controller name
So for the above example, the class would be **extensions**my_extension**my_controller
:
1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3require_once MODPATH.'core/libraries/Nova_controller_main.php'; 4 5class **extensions**my_extension**my_controller extends Nova_controller_main 6{ 7 public function __construct() 8 { 9 parent::__construct();10 $this->_regions['nav_sub'] = Menu::build('sub', 'sim');11 }12 13 public function my_method()14 {15 $this->_regions['title'] = 'Hello World!';16 $this->_regions['content'] = 'This is an extension controller.';17 Template::assign($this->_regions);18 Template::render();19 }20}
To get the URL to my_method within the example above, one may simple call:
1$this->extension['chronological_mission_posts']->url('my_controller/my_method');
The extension makes it possible to load a view from the views folder within the extension folder:
1$this->extension['my_extension']->view('my_method_view', $this->skin, 'main', $data);
This view would be located at applications/extensions/my_extension/views/main/pages/my_method_view.php
.
The extension makes it possible to load CSS from the views folder within the extension folder:
1$this->extension['my_extension']->inline_css('my_method_styles', $this->skin, 'main', $data);
This view would be located at applications/extensions/my_extension/views/main/css/my_method_styles.css
, and the outcome will be returned as inline CSS within a <style>
tag for maximum portability.
The extension makes it possible to load JavaScript from the views folder within the extension folder:
1$this->extension['my_extension']->inline_js('my_method_scripts', $this->skin, 'main', $data);
This view would be located at applications/extensions/my_extension/views/main/js/my_method_scripts.js
, and the outcome will be returned as inline CSS within a <script type="text/javascript">
tag for maximum portability.
Putting it all together, one might end up with a controller like:
1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3require_once MODPATH.'core/libraries/Nova_controller_main.php'; 4 5class **extensions**my_extension**my_controller extends Nova_controller_main 6{ 7 public function __construct() 8 { 9 parent::__construct();10 $this->_regions['nav_sub'] = Menu::build('sub', 'sim');11 }12 13 public function my_method()14 {15 $this->_regions['title'] = 'Hello World!';16 $this->_regions['content'] = $this->extension['my_extension']->view('my_method_view', $this->skin, 'main', $data);17 $this->_regions['javascript'] .= $this->extension['my_extension']->inline_css('my_method_styles', $this->skin, 'main', $data);18 $this->_regions['javascript'] .= $this->extension['my_extension']->inline_js('my_method_scripts', $this->skin, 'main', $data);19 Template::assign($this->_regions);20 Template::render();21 }22}
Overriding extension controllers and views
An application can seamlessly override the behavior of an extension-defined controller without modifying the extension. To do this, suppose the goal is to override my_controller
within my_extension
(defined as above in application/extensions/my_extension/controllers/my_controller.php
). To do this, define the file:
1application/controllers/extensions/my_extension/my_controller.php
In this file, define the controller with the same name as the extension controller has, except without the leading **
(the difference in class names here allows you to inherit the one from the extension). For example:
1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3require_once APPPATH.'extensions/my_extension/controllers/my_controller.php'; 4 5class extensions**my_extension**my_controller extends **extensions**my_extension**my_controller 6{ 7 public function my_method() 8 { 9 $this->_regions['title'] = 'Hello Sky!';10 $this->_regions['content'] = $this->extension['my_extension']->view('my_method_view', $this->skin, 'main', $data);11 $this->_regions['javascript'] .= $this->extension['my_extension']->inline_css('my_method_styles', $this->skin, 'main', $data);12 $this->_regions['javascript'] .= $this->extension['my_extension']->inline_js('my_method_scripts', $this->skin, 'main', $data);13 Template::assign($this->_regions);14 Template::render();15 }16}
A skin can also override an extension view to provide direct support for it. For example, suppose my_skin
wishes to provide a slightly modified version of the my_method_view
used in the above exaple for my_extension
. For the extension view located at , the override for the skin would be in:
1application/views/my_skin/extensions/my_extension/main/pages/my_method_view.php
If a sim wishes to modify the extension view, but the skin the sim uses does not support it, an override may also be specified in:
1application/views/_base_override/extensions/my_extension/main/pages/my_method_view.php