![Улыбка :-00):](./images/smilies/ab.gif)
Итак, поняв и разобравшись с вопросом , мы приступаем к освоению новой, более сложной задачи, а именно Как сконвертировать модификацию phpBB 3.0 в расширение phpBB 3.1
![Круто :co_ol:](./images/smilies/ay.gif)
Разбираться с этим мы будем на примере модификации NV Newspage от отличного разработчика nickvergessen.
План действий
- Структура расширения
- Расположение
- Создание новых файлов (composer.json, ext.php)
- Встроенные классы и переменные
- Модуль в администраторском разделе
- Изменения в Базе Данных, замена UMIL на Миграции (Migrations)
- Изменения в Схеме (Schema)
- Изменения в Данных
- Зависимости и окончание миграции
- Подключение к расширению языковых файлов
- Использование Событий и Слушателей (Events and Listeners)
- php события
- События шаблонов
- Добавление событий
- Совместимость
- Пагинация (Pagination)
Начинаем по порядку ....
- 1. Структура расширения
Очевидное различие между модификациями phpBB 3.0 и расширениями phpBB 3.1 - это папка, в которой хранятся файлы. В phpBB 3.0 файлы модификаций раскидываются по всем папкам форума, а в phpBB 3.1 все файлы расширений хранятся в папкеext/ . А это как минимум безпроблемное удаление ненужных расширений
- 1.1. Расположение
Хоть и для всех расширений существует единая папка, каждое расширение должно находиться в отдельной папке внутри этой папки (извиняюсь за каламбур ). Папка расширения включает имя автора, в этой папке уже папка с именем расширения. На нашем примере модификации NV Newspage от nickvergessen расположение будет такимНа том же примере, сравним расположение файлов по папкамКод: Выделить всё
корень форума/ext/nickvergessen/newspage/
В расширении все выгодно и компактно. Добавление новых файлов (которых нет в модификациях) будет описано ниже.Код: Выделить всё
Расположение файлов в расширении | Расположение файлов в модификации ------------------------+---------------- .../newspage/ | acp/ | корень форума/includes/acp/ | корень форума/includes/acp/info/ adm/style/ | корень форума/adm/style/ config/ | --- controller/ | --- event/ | --- language/ | корень форума/language/ migrations/ | --- styles/ | корень форума/styles/
- 1.2. Создание новых файлов (composer.json, ext.php)
При конвертации модификации в расширение необходимо создать файлcomposer.json , нужно это для того, чтобы система увидела (распознала) расширение. Содержание этого файла для нашего примера будет следующимСледующим новым файлом будетКод: Выделить всё
{ "name": "nickvergessen/newspage", "type": "phpbb-extension", "description": "Adds a extra-page to the board where a switchable number of news are displayed. The text can be shorten to a certain number of chars. Also the Icons can be switched of (post icons, user icons)", "homepage": "https://github.com/nickvergessen/phpbb-ext-newspage", "version": "1.1.0", "time": "2013-03-16", "licence": "GPL-2.0", "authors": [ { "name": "Joas Schilling", "email": "nickvergessen@gmx.de", "homepage": "https://github.com/nickvergessen", "role": "Lead Developer" } ], "require": { "php": ">=5.3" }, "extra": { "display-name": "phpBB 3.1 NV Newspage Extension", "soft-require": { "phpbb/phpbb": "3.1.*@dev" } } }
ext.php , позволяющий системе включать\выключать расширение, его содержаниеКод: Выделить всё
<?php // this file is not really needed, when empty it can be ommitted // however you can override the default methods and add custom // installation logic namespace nickvergessen\newspage; class ext extends \phpbb\extension\base { }
- 1.3. Встроенные классы и переменные
В то время как на phpBB 3.0 при обращении к модификации (на пользовательском уровне) вы переходили только по адресуадрес форума/newspage.php , в phpBB 3.1 есть больше свободы, по стандарту обращение идет по адресуадрес форума/app.php/newspage , но после небольшой правочки в корневом.htaccess адрес будет таким -адрес форума/newspage/
Для правильного формирования внутренних ссылок в расширении необходимо прописать так называемые Правила в файлеconfig/routing.yml . Для нашего сконвертированного расширения хватит 2-х правил: первое правило устанавливает основной страницей/newspage.php и требует от остальных видаnewspage.php?start=N , гдеN - это целое число, которое требует второе правило. Все это приводит к формированию ссылок вида:/newspage - для главной страницы,/newspage/1 ,/newspage/2 ,/newspage/3 - и так далее для последующих страниц. Содержание файлаconfig/routing.yml при таких правилахЕще один необходимый файл из папкиКод: Выделить всё
newspage_base_controller: pattern: /newspage defaults: { _controller: nickvergessen.newspage.controller:base, page: 1 } newspage_page_controller: pattern: /newspage/{page} defaults: { _controller: nickvergessen.newspage.controller:base } requirements: page: \d+
config/ называетсяservices.yml , он содержит информацию о том, какие классы будет использовать контроллер в нашем расширении. Его содержимое в нашем случаегде,Код: Выделить всё
services: nickvergessen.newspage.controller: class: nickvergessen\newspage\controller\main arguments: - @auth - @cache - @config - @dbal.conn - @request - @template - @user - @controller.helper - %core.root_path% - %core.php_ext%
nickvergessen.newspage.controller - имя нашего контроллера, аclass: nickvergessen\newspage\controller\main путь до файлаmain.php . Который в свою очередь и содержит в себе все функции и переменные, использующиеся в расширении. Его содержание для нашего случаяКак видно из строкиКод: Выделить всё
<?php /** * * @package NV Newspage Extension * @copyright (c) 2013 nickvergessen * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ namespace nickvergessen\newspage\controller; class main { /** * Constructor * NOTE: The parameters of this method must match in order and type with * the dependencies defined in the services.yml file for this service. * * @param \phpbb\config $config Config object * @param \phpbb\template $template Template object * @param \phpbb\user $user User object * @param \phpbb\controller\helper $helper Controller helper object * @param string $root_path phpBB root path * @param string $php_ext phpEx */ public function __construct(\phpbb\config\config $config, \phpbb\template\template $template, \phpbb\user $user, \phpbb\controller\helper $helper, $root_path, $php_ext) { $this->config = $config; $this->template = $template; $this->user = $user; $this->helper = $helper; $this->root_path = $root_path; $this->php_ext = $php_ext; } /** * Base controller to be accessed with the URL /newspage/{page} * (where {page} is the placeholder for a value) * * @param int $page Page number taken from the URL * @return Symfony\Component\HttpFoundation\Response A Symfony Response object */ public function base($page = 1) { /* * Do some magic here, * load your data and send it to the template. */ /* * The render method takes up to three other arguments * @param string Name of the template file to display * Template files are searched for two places: * - phpBB/styles/<style_name>/template/ * - phpBB/ext/<all_active_extensions>/styles/<style_name>/template/ * @param string Page title * @param int Status code of the page (200 - OK [ default ], 403 - Unauthorized, 404 - Page not found, etc.) */ return $this->helper->render('newspage_body.html'); } }
В нашем расширении используется шаблон стиляКод: Выделить всё
return $this->helper->render('newspage_body.html');
newspage_body.html , он должен располагаться в папке расширенияstyles/имя стиля/template/ - 1.4 Модуль в администраторском разделе
Информация из этого пункта относится и к Модераторскому и к Пользовательскому разделу. Ранее, в модификации к 3.0 файл для модуля в админке инфо-файл находился по путиphpBB/includes/acp/info/acp_newspage.php , в расширении же имя файла и путь будут такими -ext/nickvergessen/newspage/acp/main_info.php , аналогично для файла самого модуля - путь в 3.0 -phpBB/includes/acp/acp_newspage.php , в расширении же имя файла и путь будут такими -ext/nickvergessen/newspage/acp/main_module.php . Т.е. теперь оба файла модуля располагаются в одной папке и имеют схожие имена. Файлmain_info.php (для нашего случая)где строкаКод: Выделить всё
<?php /** * * @package NV Newspage Extension * @copyright (c) 2013 nickvergessen * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ namespace nickvergessen\newspage\acp; class main_info { function module() { return array( 'filename' => '\nickvergessen\newspage\acp\main_module', 'title' => 'ACP_NEWSPAGE_TITLE', 'version' => '1.0.1', 'modes' => array( 'config_newspage' => array('title' => 'ACP_NEWSPAGE_CONFIG', 'auth' => 'acl_a_board', 'cat' => array('ACP_NEWSPAGE_TITLE')), ), ); } }
Отвечает за расположение и имя основного файла модуля в админке, аКод: Выделить всё
'filename' => '\nickvergessen\newspage\acp\main_module',
Переменная-имя нашего модуля в админке, аКод: Выделить всё
'title' => 'ACP_NEWSPAGE_TITLE',
Версия расширения, аКод: Выделить всё
'version' => '1.0.1',
Переменная-имя нашего модуля и подмодуля, метод авторизации.Код: Выделить всё
'config_newspage' => array('title' => 'ACP_NEWSPAGE_CONFIG', 'auth' => 'acl_a_board', 'cat' => array('ACP_NEWSPAGE_TITLE')),
Файлmain_module.php Таким образом мы создали (сконвертировали) модуль для админки, двигаем дальшеКод: Выделить всё
<?php /** * * @package NV Newspage Extension * @copyright (c) 2013 nickvergessen * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ namespace nickvergessen\newspage\acp; class main_module { var $u_action; function main($id, $mode) { \\\\\здесь ваши функции для модуля в админке\\\\ } }
- 1.1. Расположение
- 2. Изменения в Базе Данных, замена UMIL на Миграции
Миграция выполняет те же функции что и UMIL в тройке, автоматические запросы к БД. Самое важное отличие - для миграции необходим один файл, вместо кучи от UMIL. В нашем случае UMIL содержал в себе основуПосмотрим, каким образом воспроизвести это же, но с помощью миграцииКод: Выделить всё
$versions = array( '1.0.0' => array( 'config_add' => array( array('news_number', 5), array('news_forums', '0'), array('news_char_limit', 500), array('news_user_info', 1), array('news_post_buttons', 1), ), 'module_add' => array( array('acp', 'ACP_CAT_DOT_MODS', 'NEWS'), array('acp', 'NEWS', array( 'module_basename' => 'newspage', 'module_langname' => 'NEWS_CONFIG', 'module_mode' => 'overview', 'module_auth' => 'acl_a_board', ), ), ), ), '1.0.1' => array( 'config_add' => array( array('news_pages', 1), ), ), '1.0.2' => array(), '1.0.3' => array( 'config_add' => array( array('news_attach_show', 1), array('news_cat_show', 1), array('news_archive_per_year', 1), ), ), );
- 2.1 Изменения в Схеме (Schema)
В миграции используются 2 метода-функции, один -public function update_schema() - для добавления данных в БДДругой же -Код: Выделить всё
public function update_schema() { return array( 'add_columns' => array( $this->table_prefix . 'groups' => array( 'group_teampage' => array('UINT', 0, 'after' => 'group_legend'), ), $this->table_prefix . 'profile_fields' => array( 'field_show_on_pm' => array('BOOL', 0), ), ), 'change_columns' => array( $this->table_prefix . 'groups' => array( 'group_legend' => array('UINT', 0), ), ), ); }
public function revert_schema() - для удаления данных из БД при деинсталляции расширенияКод: Выделить всё
public function revert_schema() { return array( 'drop_columns' => array( $this->table_prefix . 'groups' => array( 'group_teampage', ), $this->table_prefix . 'profile_fields' => array( 'field_show_on_pm', ), ), 'change_columns' => array( $this->table_prefix . 'groups' => array( 'group_legend' => array('BOOL', 0), ), ), ); }
- 2.2 Изменения в Данных
Данные могут быть нескольких типов - создание модулей (Админка, Модераторский раздел, Пользовательский раздел)module.add , обновление версии расширенияconfig.add , создание прав доступаpermission.add и заполнение первоначальными даннымиconfig.add . Суммарно все для нашего случая будет такимКод: Выделить всё
public function update_data() { return array( array('config.add', array('news_number', 5)), array('config.add', array('news_forums', '0')), array('config.add', array('news_char_limit', 500)), array('config.add', array('news_user_info', 1)), array('config.add', array('news_post_buttons', 1)), array('module.add', array( 'acp', 'ACP_CAT_DOT_MODS', 'ACP_NEWSPAGE_TITLE' )), array('module.add', array( 'acp', 'ACP_NEWSPAGE_TITLE', array( 'module_basename' => '\nickvergessen\newspage\acp\main_module', 'modes' => array('config_newspage'), ), )), array('config.add', array('newspage_mod_version', '1.0.0')), ); }
- 2.3 Зависимости и окончание миграции
Финишными операциями являются проверки БД на предмет уже существующих записей о нашем расширении (ведь неизвестно, есть ли на конкретном форуме данное расширение. Обычно это и применяется при конвертации модификаций в расширения). Проверяется это с помощью функцииpublic function effectively_installed() Так как миграционные файлы могут иметь различные имена и движок должен сам определить, что конкретный файл - миграционный, в 3.1 добавлена еще функцияКод: Выделить всё
public function effectively_installed() { return isset($this->config['newspage_mod_version']) && version_compare($this->config['newspage_mod_version'], '1.0.0', '>='); }
static public function depends_on() .Отвечает она как раз таки за проверку корректности файлов в папке расширенияКод: Выделить всё
static public function depends_on() { return array('\phpbb\db\migration\data\v310\alpha1'); }
migrations/ и теперь, с учетом всех функций, наш файл миграции имеет следующий видКод: Выделить всё
<?php /** * * @package migration * @copyright (c) 2013 phpBB Group * @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 * */ namespace nickvergessen\newspage\migrations\v10x; class release_1_0_0 extends \phpbb\db\migration\migration { public function effectively_installed() { return isset($this->config['newspage_mod_version']) && version_compare($this->config['newspage_mod_version'], '1.0.0', '>='); } static public function depends_on() { return array('\phpbb\db\migration\data\v310\dev'); } public function update_data() { return array( array('config.add', array('news_number', 5)), array('config.add', array('news_forums', '0')), array('config.add', array('news_char_limit', 500)), array('config.add', array('news_user_info', 1)), array('config.add', array('news_post_buttons', 1)), array('module.add', array( 'acp', 'ACP_CAT_DOT_MODS', 'ACP_NEWSPAGE_TITLE' )), array('module.add', array( 'acp', 'ACP_NEWSPAGE_TITLE', array( 'module_basename' => '\nickvergessen\newspage\acp\main_module', 'modes' => array('config_newspage'), ), )), array('config.add', array('newspage_mod_version', '1.0.0')), ); } }
- 2.1 Изменения в Схеме (Schema)
- 3. Подключение к расширению языковых файлов
Метод тройки$user->add_lang() в 3.1 не распознается, вместо него используется метод$user->add_lang_ext() , для примераСтрока прописывается в файл событий (об этом чуть ниже), в нейКод: Выделить всё
$user->add_lang_ext('nickvergessen/newspage', 'newspage');
nickvergessen/newspage определяет папку расширения в общей папкеext/ , аnewspage - имя языкового файла, в итоге, для нашего случая, языковой файл будет по адресу и с именем -/ext/nickvergessen/newspage/language/языковая папка(en, ru и тд.)/newspage.php - 4. Использование Событий и Слушателей (Events and Listeners) ()
Забудьте в phpBB 3.1 / Ascraeus что такое правки файлов, с новой системой расширений вам не нужно будет проводить долгие часы устанавливая нужный функционал. Вместо правок используйте события и слушателей- 4.1 php события
Вообщем, опять же на примере нашего мода и его конвертации, вместо добавления в код форума такой строкидля функцииКод: Выделить всё
$template->assign_vars(array( 'U_NEWSPAGE' => append_sid($phpbb_root_path . 'app.' . $phpEx, 'controller=newspage/'), ));
page_header() мы создадим файл слушателейevent/main_listener.php со следующим содержимым (с некоторыми классами из Symfony)где в методеКод: Выделить всё
<?php /** * * @package NV Newspage Extension * @copyright (c) 2013 nickvergessen * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ namespace nickvergessen\newspage\event; /** * Event listener */ use Symfony\Component\EventDispatcher\EventSubscriberInterface; class main_listener implements EventSubscriberInterface { /** * Instead of using "global $user;" in the function, we use dependencies again. */ public function __construct(\phpbb\controller\helper $helper, \phpbb\template\template $template, \phpbb\user $user) { $this->helper = $helper; $this->template = $template; $this->user = $user; } }
getSubscribedEvents() мы говорим системе какой набор слушателей ("подписчиков") нам нужен для нашего расширения, в нашем случае нам необходим такой "подписчик" какcore.page_header (аналог того жеpage_header() из тройки) и загрузчик языковых файлов.Теперь для ранее указанных слушателей добавим функцииКод: Выделить всё
static public function getSubscribedEvents() { return array( 'core.user_setup' => 'load_language_on_setup', 'core.page_header' => 'add_page_header_link', ); }
Последним этапом является регистрация наших слушателей в системе. Это делается с помощью тегаКод: Выделить всё
public function load_language_on_setup($event) { $lang_set_ext = $event['lang_set_ext']; $lang_set_ext[] = array( 'ext_name' => 'nickvergessen/newspage', 'lang_set' => 'newspage', ); $event['lang_set_ext'] = $lang_set_ext; } public function add_page_header_link($event) { // I use a second language file here, so I only load the strings global which are required globally. // This includes the name of the link, aswell as the ACP module names. $this->user->add_lang_ext('nickvergessen/newspage', 'newspage_global'); $this->template->assign_vars(array( 'U_NEWSPAGE' => $this->helper->route('newspage_base_controller'), ));
event.listener вservice.yml (См. пункт 1.3 нашей статьи):И с этим шагом мы завершили правки php-кода.Код: Выделить всё
nickvergessen.newspage.listener: class: nickvergessen\newspage\event\main_listener arguments: - @controller.helper - @template - @user tags: - { name: event.listener }
- 4.2 События шаблонов
Теперь нам остается добавить правки в html-код, опять таки с помощью событий в шаблонах. Использование событий в шаблонах рассмотрим на примере:
- для нашего расширения мы хотим добавить ссылку в шапку ссылку рядом с FAQ-ссылкой, потому среди мы находим нужное нам событиеoverall_header_navigation_prepend . Чтобы его применить нужно создать и поместить по следующему адресу следующий файл/ext/nickvergessen/newspage/styles/prosilver/template/event/overall_header_navigation_prepend_listener.html . То есть для применения события нужно создать файл с аналогичным событию именем, а вот в его содержании будет код, который вы хотите вставить в конкретное место в шаблоне, для нашего примера этоАналогичным образом пихаем нужные куски кода в любое место на форуме. Можно для этих целей создавать мини-расширения дабы не загаживать код форума, используя знания из статьиКод: Выделить всё
<li class="icon-newspage"><a href="{U_NEWSPAGE}">{L_NEWSPAGE}</a></li>
- 4.3 Добавление событий
Разумеется команда разработчиков не в состоянии учесть все возможные пожелания для событий, потому вы сами вольны подсказать им какое событие необходимо включить в следующие версии phpBB 3.1 / Ascraeus. А сделать это можно
- 4.1 php события
- 5. Совместимость
С течением времени в разработке старые функции сменяются на новые, внимательно следите за всем этим в новостях разработки (можно еще посмотреть)- 5.1 Пагинация
При создании пагинации по старому способу из тройки вы получите такую ошибку
Проблема в том, что пагинация страниц теперь не возвращается функцией, вместо того, чтобы автоматически генерироваться в шаблоне. В том же шаге, имя функции было обновлено с PHPBB-префиксом. Старый код пагинации страниц был таким (приблизительно):Fatal error: Call to undefined function generate_pagination() in ...\phpBB3\ext\nickvergessen\newspage\controller\main.php on line 534Правильный код таковКод: Выделить всё
$pagination = generate_pagination(append_sid("{$phpbb_root_path}app.$phpEx", 'controller=newspage/'), $total_paginated, $config['news_number'], $start); $this->template->assign_vars(array( 'PAGINATION' => $pagination, 'PAGE_NUMBER' => on_page($total_paginated, $config['news_number'], $start), 'TOTAL_NEWS' => $this->user->lang('VIEW_TOPIC_POSTS', $total_paginated), ));
Код: Выделить всё
$pagination = $phpbb_container->get('pagination'); $pagination->generate_template_pagination( array( 'routes' => array( 'newspage_base_controller', 'newspage_page_controller', ), 'params' => array(), ), 'pagination', 'page', $total_paginated, $this->config['news_number'], $start); $this->template->assign_vars(array( 'PAGE_NUMBER' => $pagination->on_page($total_paginated, $this->config['news_number'], $start), 'TOTAL_NEWS' => $this->user->lang('VIEW_TOPIC_POSTS', $total_paginated), ));
- 5.1 Пагинация