Tutorial: Modules¶
Introduction¶
In phpBB you can create your own modules for the User, Moderator and Administration Control Panels. This modular approach has many advantages over creating settings pages from scratch:
You can manage modules in the ACP.
You don’t have to directly handle authorisation.
You get a navigation tree built-in to the system.
The module templates already contain features like pagination, breadcrumbs and confirmation messages.
This tutorial explains:
Note
This tutorial describes how to create an ACP module. To create MCP or UCP modules, you usually only need to replace all occurrences of “acp” with “mcp” or “ucp”.
Creating Control Panel Modules¶
A module requires three files:
Module info¶
The module info class simply contains a method that returns an array of information describing the module. Information like the name of the module, available modes and required permissions.
This file must always end in *_info.php
and
should be stored in the acp/
folder of the extension.
For this tutorial, we will use ext/acme/demo/acp/main_info.php
:
<?php
namespace acme\demo\acp;
class main_info
{
public function module()
{
return array(
'filename' => '\acme\demo\acp\main_module',
'title' => 'ACP_DEMO_TITLE',
'modes' => array(
'settings' => array(
'title' => 'ACP_DEMO',
'auth' => 'ext_acme/demo && acl_a_board',
'cat' => array('ACP_DEMO_TITLE'),
),
),
);
}
}
The main_info
class has only one method module()
which returns an array
with the following information:
Key |
Content |
---|---|
|
Fully name-spaced path to the Module class, starting with a leading slash. |
|
Language key for the module-title that is displayed in the ACP module management section. |
|
Contains an array of modes for the module. See the next table for more details. |
Note that we have defined one mode named settings
with the following information:
Key |
Content |
---|---|
|
Language key of the mode-title that is displayed in the ACP module management section. |
|
An authorisation key used to control access to the mode. The extension
vendor/name |
|
References the parent category the mode belongs to. Should typically contain the language key of the parent category. |
Module class¶
The module class contains the actual code for the module. This
file will also be stored in the acp/
folder of the extension.
For this tutorial, we will use ext/acme/demo/acp/main_module.php
:
<?php
namespace acme\demo\acp;
class main_module
{
public $u_action;
public $tpl_name;
public $page_title;
public function main($id, $mode)
{
global $language, $template, $request, $config;
$this->tpl_name = 'acp_demo_body';
$this->page_title = $language->lang('ACP_DEMO_TITLE');
add_form_key('acme_demo_settings');
if ($request->is_set_post('submit'))
{
if (!check_form_key('acme_demo_settings'))
{
trigger_error('FORM_INVALID');
}
$config->set('acme_demo_goodbye', $request->variable('acme_demo_goodbye', 0));
trigger_error($language->lang('ACP_DEMO_SETTING_SAVED') . adm_back_link($this->u_action));
}
$template->assign_vars(array(
'ACME_DEMO_GOODBYE' => $config['acme_demo_goodbye'],
'U_ACTION' => $this->u_action,
));
}
}
The module itself must contain a main($id, $mode)
method,
which takes the id
of the module in the database and the mode
being called as arguments.
In the code of the main
method, we first set the name of the
Module template file that is used to render the module, and the page
title is assigned a language key.
To strengthen the form against security vulnerabilities, we
use a form key check to verify that the form being submitting
is valid. This is done by calling add_form_key('acme_demo_settings')
when displaying the form and then later checking the form key
when it is being submitted:
if (!check_form_key('acme_demo_settings'))
{
trigger_error('FORM_INVALID');
}
Warning
The form key should be unique for every form. The key can be any string value, but extensions should include their vendor and extension names.
If the form key passes, we set the configuration value to the submitted value and display a success message to the user:
$config->set('acme_demo_goodbye', $request->variable('acme_demo_goodbye', 0));
trigger_error($language->lang('ACP_DEMO_SETTING_SAVED') . adm_back_link($this->u_action));
At the end of the method we assign two template variables.
The first contains the current value of the config value.
The second contains the u_action
class property which holds
the URL of the current form action.
Module template¶
Our ACP module now needs the template file we assigned to it in the Module class.
We will use ext/acme/demo/adm/style/acp_demo_body.html
.
Note
The ACP differs from the MCP and UCP in that it has its own
style. The MCP and UCP use the main board style, i.e. prosilver.
Therefore, ACP template files must be stored in ./adm/style/
while MCP and UCP template files are stored in ./styles/prosilver/template/
.
{% INCLUDE 'overall_header.html' %}
<h1>{{ lang('SETTINGS') }}</h1>
<form id="acp_board" method="post" action="{{ U_ACTION }}">
<fieldset>
<dl>
<dt><label for="acme_demo_goodbye">{{ lang('ACP_DEMO_GOODBYE') }}</label></dt>
<dd><input type="radio" class="radio" name="acme_demo_goodbye" value="1" {% if ACME_DEMO_GOODBYE %}checked="checked" {% endif %}/> {{ lang('YES') }}
<input type="radio" class="radio" name="acme_demo_goodbye" value="0" {% if not ACME_DEMO_GOODBYE %}checked="checked" {% endif %}/> {{ lang('NO') }}</dd>
</dl>
<p class="submit-buttons">
<input class="button1" type="submit" id="submit" name="submit" value="{{ lang('SUBMIT') }}" />
<input class="button2" type="reset" id="reset" name="reset" value="{{ lang('RESET') }}" />
</p>
{{ S_FORM_TOKEN }}
</fieldset>
</form>
{% INCLUDE 'overall_footer.html' %}
This template renders out a form with a single option for toggling the
acme_demo_goodbye setting via two radio buttons, and two input buttons
to submit or reset the form. Note that the {{ S_FORM_TOKEN }}
template
variable is required as part of the form key security check.
Module language keys¶
Between our module class and template files, we have added some new language keys.
We can add them to our language array in acme/demo/language/en/demo.php
:
'ACP_DEMO_TITLE' => 'Demo Module',
'ACP_DEMO' => 'Settings',
'ACP_DEMO_GOODBYE' => 'Should say goodbye?',
'ACP_DEMO_SETTING_SAVED' => 'Settings have been saved successfully!',
Note
Recall that we load our language file globally throughout phpBB
via the core.user_setup
event in our event listener. Since we do
not recommend doing this all the time, an alternative method to
autoload a language file in the ACP is to prefix the file
name with info_acp_*.php
for module language keys or permissions_*.php
for
permission language keys.
Installing Control Panel Modules¶
The module is now complete, but it will not show up in the ACP yet. To install the module to the database when the extension is enabled, we need a Migration.
Migration files must be stored in the migrations/
folder of the extension.
For the Acme Demo, we need a migration that will install the following data:
A configuration value named
acme_demo_goodbye
that can be set by the administrator.The ACP module data.
<?php
namespace acme\demo\migrations;
class add_module extends \phpbb\db\migration\migration
{
/**
* If our config variable already exists in the db
* skip this migration.
*/
public function effectively_installed()
{
return isset($this->config['acme_demo_goodbye']);
}
/**
* This migration depends on phpBB's v314 migration
* already being installed.
*/
static public function depends_on()
{
return array('\phpbb\db\migration\data\v31x\v314');
}
public function update_data()
{
return array(
// Add the config variable we want to be able to set
array('config.add', array('acme_demo_goodbye', 0)),
// Add a parent module (ACP_DEMO_TITLE) to the Extensions tab (ACP_CAT_DOT_MODS)
array('module.add', array(
'acp',
'ACP_CAT_DOT_MODS',
'ACP_DEMO_TITLE'
)),
// Add our main_module to the parent module (ACP_DEMO_TITLE)
array('module.add', array(
'acp',
'ACP_DEMO_TITLE',
array(
'module_basename' => '\acme\demo\acp\main_module',
'modes' => array('settings'),
),
)),
);
}
}
See also
To learn more about migrations, please have a look at the Migrations documentation.
At this point we have completed the Acme Demo extension! There is more that extensions can do, however, than what we learned from the Acme Demo. Continue on to the next sections to learn how to do more with extensions.