Skip to content

[How To] Create a module for Board3 Portal 2.1.x

Benjamin Hogl edited this page Jul 28, 2018 · 2 revisions

Introduction

Due to several changes in the extension system and the way Board3 Portal loads modules, the module system had to be overhauled and partially reworked for Board3 Portal 2.1.x. As a result of this, modules for Board3 Portal 2.0.x will no longer work for 2.1.x.

The new system does also have some perks that previously didn't exist. While one previously had to copy the modules into the Board3 Portal folders, they can be used as extensions now. This means that the Board3 Portal module itself will be a phpBB extension.

Setting up the extension

In this example, we'll create a Weather module for Board3 Portal 2.1.x.

To start out, we'll have to create a new extension folder first. We'll be using the namespace marc/b3pweather. The vendor name and extension name mustn't contain spaces or underscores. We'll have to create the folder structure marc/b3pweather/ inside the phpBB ext folder.

In order to allow phpBB to find the extension, we'll have to create a new file called composer.json. This will tell phpBB the name of our extension.

{
	"name": "marc/b3pweather",
	"type": "phpbb-extension",
	"description": "Adds a weather module for Board3 Portal",
	"homepage": "https://www.m-a-styles.de",
	"version": "0.0.1",
	"time": "2015-03-01 19:44:39",
	"license": "GPL-2.0",
	"authors": [{
		"name": "Marc Alexander",
		"email": "admin@m-a-styles.de",
		"homepage": "http://www.m-a-styles.de",
		"role": "Lead Developer"
	}],
	"require": {
		"php": ">=5.3.3"
	},
	"extra": {
		"display-name": "Board3 Portal Weather Module",
		"soft-require": {
			"phpbb/phpbb": ">=3.1.3,<3.2.*@dev"
		}
	}
}

It needs to be located at marc/b3pweather/composer.json

In addition to this file, we'll also need a services.yml that tells phpBB about our module's class. It needs to be created at marc/b3pweather/config/services.yml. We'll create the actual content of this file a little bit further down.

Creating a module file

After having created the basic extension structure for our module, we can start with creating a new module files. Generally speaking, we could put this file anywhere inside our extension structure as long as it's located somewhere inside the marc/b3pweather folder. In this case, we'll put the module file at the location marc/b3pweather/weather.php. In order to have a basic structure, we'll be using the rather simple clock module as our base file:

<?php
/**
*
* @package Board3 Portal v2.1
* @copyright (c) 2013 Board3 Group ( www.board3.de )
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/

namespace board3\portal\modules;

/**
* @package Clock
*/
class clock extends module_base
{
    /**
    * Allowed columns: Just sum up your options (Exp: left + right = 10)
    * top        1
    * left        2
    * center    4
    * right        8
    * bottom    16
    */
    public $columns = 10;

    /**
    * Default modulename
    */
    public $name = 'CLOCK';

    /**
    * Default module-image:
    * file must be in "{T_THEME_PATH}/images/portal/"
    */
    public $image_src = 'portal_clock.png';

    /**
    * module-language file
    * file must be in "language/{$user->lang}/mods/portal/"
    */
    public $language = 'portal_clock_module';

    /** @var \phpbb\config\config */
    protected $config;

    /** @var \phpbb\template\template */
    protected $template;

    /**
     * Constructor for clock module
     *
     * @param \phpbb\config\config $config phpBB config
     * @param \phpbb\template\template $template phpBB template
     */
    public function __construct($config, $template)
    {
        $this->config = $config;
        $this->template = $template;
    }

    /**
    * {@inheritdoc}
    */
    public function get_template_side($module_id)
    {
        if (isset($this->config['board3_clock_src_' . $module_id]) && !empty($this->config['board3_clock_src_' . $module_id]))
        {
            $this->template->assign_var('B3P_CLOCK_SRC', $this->config['board3_clock_src_' . $module_id]);
        }
        return 'clock_side.html';
    }

    /**
    * {@inheritdoc}
    */
    public function get_template_acp($module_id)
    {
        return array(
            'title'    => 'ACP_PORTAL_CLOCK_SETTINGS',
            'vars'    => array(
                'legend1'    => 'ACP_PORTAL_CLOCK_SETTINGS',
                'board3_clock_src_' . $module_id    => array('lang' => 'ACP_PORTAL_CLOCK_SRC', 'validate' => 'string', 'type' => 'text:50:200', 'explain' => true, 'submit_type' => 'custom', 'submit' => 'check_file_src'),
            ),
        );
    }

    /**
     * {@inheritdoc}
     */
    public function install($module_id)
    {
        $this->config->set('board3_clock_src_' . $module_id, '');
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function uninstall($module_id, $db)
    {
        $this->config->delete('board3_clock_src_' . $module_id);
        return true;
    }
}

First of, we'll update the copyright info at the top of our new file:

<?php
/**
 *
 * @package Board3 Portal Weather Module
 * @copyright (c) 2015 Marc Alexander ( www.m-a-styles.de )
 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
 *
 */

Afterwards, we'll have to modify the class name and namespace of our module. The namespace is based on your extension structure. As our module is inside the marc/b3pweather folder, its namespace should have the structure. Note that namespaces use backslashes instead of normal slashes and there must not be any preceding slash. Class names should also represent the actual file names, in this case weather.php. All modules should extend the Board3 Portal module base class. You will have to specify the full class name including preceding slash. Alternatively, you can of course make use of the use keyword if you know how to use it.

namespace marc\b3pweather;

/**
 * @package Weather Module
 */
class weather extends \board3\portal\modules\module_base
{

The next few lines define the allowed columns, module name, module image, and language file. We'll only allow the weather module for the left and right columns in this case. This results in the same setting (10) as the clock module already uses. As we'll be using a language variable for the module name, we'll be choosing a rather distinctive name for it (MARC_B3P_WEATHER). By using a distinctive prefix like your vendor name, you can make sure that there are no conflicts between your module name and already existing language entries. Using your own module images from inside the module's extension folder is currently not yet supported. Therefore, we'll be removing the reference to the icon. In order to allow Board3 Portal to properly load your language file, you'll have to specify the extension name of your module and the language file in an array.

    /**
     * Allowed columns: Just sum up your options (Exp: left + right = 10)
     * top        1
     * left        2
     * center    4
     * right        8
     * bottom    16
     */
    public $columns = 10;

    /**
     * Default modulename
     */
    public $name = 'MARC_B3P_WEATHER';

    /**
     * Default module-image:
     * file must be in "{T_THEME_PATH}/images/portal/"
     */
    public $image_src = '';

    /**
     * module-language file
     * file must be in "language/{$user->lang}/mods/portal/"
     */
    public $language = array(
        'vendor'    => 'marc/b3pweather',
        'file'        => 'weather',
    );

The two class members that are defined should be enough for a basic module that takes a config setting and outputs it to the HTML file. You can of course add any other members if you like and/or extend the constructor method for this.

    /** @var \phpbb\config\config */
    protected $config;

    /** @var \phpbb\template\template */
    protected $template;

    /**
     * Constructor for clock module
     *
     * @param \phpbb\config\config $config phpBB config
     * @param \phpbb\template\template $template phpBB template
     */
    public function __construct($config, $template)
    {
        $this->config = $config;
        $this->template = $template;
    }

We'll later create the content of the services.yml based on our constructor method. The method get_template_side() is used for outputting data for the side columns while get_template_center() is used for center columns. Since we're only displaying this module in the side columns, the get_template_side() method is enough. As a simple example, we'll only allow the admin to specify the location for the weather module.

    /**
     * {@inheritdoc}
     */
    public function get_template_side($module_id)
    {
        $this->template->assign_vars(array(
            'MARC_B3P_WEATHER_PLZ'        => $this->config['marc_b3p_weather_plz_' . $module_id],
            'MARC_B3P_WEATHER_COUNTRY'    => $this->config['marc_b3p_weather_country_' . $module_id],
        ));

        return '@marc_b3pweather/marc_b3p_weather_side.html';
    }

The method returns the filename of our module's HTML file prefixed by an @, the vendor, and the extension name. This results in the string @marc_b3pweather/marc_b3p_weather_side.html. The prefixes are required to be able to properly load the template file.

Afterwards, we'll have to modify the get_template_acp() method. This takes care of displaying the module settings in the ACP:

    /**
     * {@inheritdoc}
     */
    public function get_template_acp($module_id)
    {
        return array(
            'title'    => 'MARC_B3P_WEATHER',
            'vars'    => array(
                'legend1'                                    => 'MARC_B3P_WEATHER_SETTINGS',
                'marc_b3p_weather_plz_' . $module_id        => array('lang' => 'MARC_B3P_WEATHER_PLZ',    'validate' => 'string',    'type' => 'text:5:5',    'explain' => true),
                'marc_b3p_weather_country_' . $module_id    => array('lang' => 'MARC_B3P_WEATHER_COUNTRY',    'validate' => 'string',    'type' => 'text:3:3',    'explain' => true),
            ),
        );
    }

Since the config entries do not exist yet, the module has to create them in the install() method and remove them in the uninstall() method:

    /**
     * {@inheritdoc}
     */
    public function install($module_id)
    {
        $this->config->set('marc_b3p_weather_plz_' . $module_id, '80331');
        $this->config->set('marc_b3p_weather_country_' . $module_id, 'DE');
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function uninstall($module_id, $db)
    {
        $this->config->delete('marc_b3p_weather_plz_' . $module_id);
        $this->config->delete('marc_b3p_weather_country_' . $module_id);
        return true;
    }

The complete module looks as follows:

<?php
/**
 *
 * @package Board3 Portal Weather Module
 * @copyright (c) 2015 Marc Alexander ( www.m-a-styles.de )
 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
 *
 */

namespace marc\b3pweather;

/**
 * @package Weather Module
 */
class weather extends \board3\portal\modules\module_base
{
    /**
     * Allowed columns: Just sum up your options (Exp: left + right = 10)
     * top        1
     * left        2
     * center    4
     * right        8
     * bottom    16
     */
    public $columns = 10;

    /**
     * Default modulename
     */
    public $name = 'MARC_B3P_WEATHER';

    /**
     * Default module-image:
     * file must be in "{T_THEME_PATH}/images/portal/"
     */
    public $image_src = '';

    /**
     * module-language file
     * file must be in "language/{$user->lang}/mods/portal/"
     */
    public $language = array(
        'vendor'    => 'marc/b3pweather',
        'file'        => 'weather',
    );

    /** @var \phpbb\config\config */
    protected $config;

    /** @var \phpbb\template\template */
    protected $template;

    /**
     * Constructor for clock module
     *
     * @param \phpbb\config\config $config phpBB config
     * @param \phpbb\template\template $template phpBB template
     */
    public function __construct($config, $template)
    {
        $this->config = $config;
        $this->template = $template;
    }

    /**
     * {@inheritdoc}
     */
    public function get_template_side($module_id)
    {
        $this->template->assign_vars(array(
            'MARC_B3P_WEATHER_PLZ'        => $this->config['marc_b3p_weather_plz_' . $module_id],
            'MARC_B3P_WEATHER_COUNTRY'    => $this->config['marc_b3p_weather_country_' . $module_id],
        ));

        return '@marc_b3pweather/marc_b3p_weather_side.html';
    }

    /**
     * {@inheritdoc}
     */
    public function get_template_acp($module_id)
    {
        return array(
            'title'    => 'MARC_B3P_WEATHER',
            'vars'    => array(
                'legend1'                                    => 'MARC_B3P_WEATHER_SETTINGS',
                'marc_b3p_weather_plz_' . $module_id        => array('lang' => 'MARC_B3P_WEATHER_PLZ',    'validate' => 'string',    'type' => 'text:5:5',    'explain' => true),
                'marc_b3p_weather_country_' . $module_id    => array('lang' => 'MARC_B3P_WEATHER_COUNTRY',    'validate' => 'string',    'type' => 'text:3:3',    'explain' => true),
            ),
        );
    }

    /**
     * {@inheritdoc}
     */
    public function install($module_id)
    {
        $this->config->set('marc_b3p_weather_plz_' . $module_id, '80331');
        $this->config->set('marc_b3p_weather_country_' . $module_id, 'DE');
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function uninstall($module_id, $db)
    {
        $this->config->delete('marc_b3p_weather_plz_' . $module_id);
        $this->config->delete('marc_b3p_weather_country_' . $module_id);
        return true;
    }
}

Creating the module language file

After we have finished creating the module itself, we can now create the needed language file. English language files will need to be added to marc/b3pweather/language/en/weather.php as specified above. The needed language variables include the module title, the entries for the settings, and the explain strings of the settings. Explain strings will have the same name as the language variable of the setting plus an appended _EXP, e.g. MARC_B3P_WEATHER_PLZ_EXP. For this module, the language file should have the following content:

<?php
/**
 *
 * @package Board3 Portal Weather Module
 * @copyright (c) 2015 Marc Alexander ( www.m-a-styles.de )
 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
 *
 */

/**
 * DO NOT CHANGE
 */
if (!defined('IN_PHPBB'))
{
    exit;
}

if (empty($lang) || !is_array($lang))
{
    $lang = array();
}

// DEVELOPERS PLEASE NOTE
//
// All language files should use UTF-8 as their encoding and the files must not contain a BOM.
//
// Placeholders can now contain order information, e.g. instead of
// 'Page %s of %s' you can (and should) write 'Page %1$s of %2$s', this allows
// translators to re-order the output of data while ensuring it remains correct
//
// You do not need this where single placeholders are used, e.g. 'Message %d' is fine
// equally where a string contains only two placeholders which are used to wrap text
// in a url you again do not need to specify an order e.g., 'Click %sHERE%s' is fine

$lang = array_merge($lang, array(
    'MARC_B3P_WEATHER'                => 'Weather',
    'MARC_B3P_WEATHER_SETTINGS'        => 'Weather settings',
    'MARC_B3P_WEATHER_PLZ'            => 'Postal code',
    'MARC_B3P_WEATHER_PLZ_EXP'        => 'Enter the postal code of the location for the weather module.',
    'MARC_B3P_WEATHER_COUNTRY'        => 'Country',
    'MARC_B3P_WEATHER_COUNTRY_EXP'    => 'Enter the country code of your location. For a complete list of country IDs, take a look at the <a href="https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes" alt="country codes">Wikpedia article about ISO 3166-1</a>.'
));

Adding the HTML file

Of course we'll also have to add the HTML file we specified in our module. For prosilver, this needs to be at marc/b3pweather/styles/prosilver/template/marc_b3p_weather_side.html. You can also use styles/all/ instead of the prosilver folder if you want to use the same template for all styles.

{$LR_BLOCK_H_L}<!-- IF $S_BLOCK_ICON --><img src="{$IMAGE_SRC}" width="{$IMAGE_WIDTH}" height="{$IMAGE_HEIGHT}" alt="" />&nbsp;<!-- ENDIF -->{$TITLE}{$LR_BLOCK_H_R}
<div style="text-align: center;">
	<!-- Wettercode Start -->
	<a href="http://www.wetter.com/home/extern/ex_search.php?search={MARC_B3P_WEATHER_PLZ}"><img src="http://www.wetter.com/home/woys/woys.php?,C,1,{MARC_B3P_WEATHER_COUNTRY}PLZ,{MARC_B3P_WEATHER_PLZ}" alt="" /></a>
	<a href="http://www.wetter.com/home/extern/ex_search.php?search={MARC_B3P_WEATHER_PLZ}"><img src="http://www.wetter.com/home/woys/woys.php?,F,1,{MARC_B3P_WEATHER_COUNTRY}PLZ,{MARC_B3P_WEATHER_PLZ}" alt="" /></a>
	<!-- Wettercode Ende -->
</div>
<br />
{$LR_BLOCK_F_L}{$LR_BLOCK_F_R}

Adding the content to services.yml for loading the module

Now that we're done with our basic module, we'll have to properly add the module class to services.yml. services.yml needs to be located at marc/b3pweather/config/services.yml. We'll first add the basic service info to this file:

services:
    marc.b3pweather.weather:
        class: marc\b3pweather\weather
        arguments:
            - @config
            - @template

The service name, marc.b3pweather.weather, should always look like {vendor}.{extension_name}.{class_name} . In this case, the vendor name is marc and the extension name is b3pweather. The class name is of course weather, as we previously decided. Afterwards, the actual class name is specified. This should not contain a preceding backslash.

While this would suffice to have the service available, Board3 Portal does not yet know about this new module. In order to have this module available in the ACP, we'll have to tag it appropriately. For this, we'll just have to add the extra board3.portal.module tag:

services:
    marc.b3pweather.weather:
        class: marc\b3pweather\weather
        arguments:
            - @config
            - @template
        tags:
            - { name: board3.portal.module }

Enabling the module

After the marc/b3pweather folder has been copied to the ext/ folder of phpBB, you can enable the module as an extension in the ACP. The newly added module will appear in the "Add module" drop-down list and you'll be able to add the module.