Конвертируем мод в расширение phpBB 3.1 / Ascraeus

В данном подфоруме будут располагаться все статьи, помогающие освоиться в администрировании форума phpBB 3.1 / Ascraeus.

Модераторы: Vl@d1m1r, Lorem Ipsum, Atlas

Ответить
Аватара пользователя
DeaDRoMeO
Старожил Форума
Старожил Форума
Сообщения: 16763
Стаж: 13 лет
Откуда: Витебск
Контактная информация:

Конвертируем мод в расширение phpBB 3.1 / Ascraeus

Сообщение DeaDRoMeO »

Всем доброго времени суток :-00):
Итак, поняв и разобравшись с вопросом , мы приступаем к освоению новой, более сложной задачи, а именно Как сконвертировать модификацию phpBB 3.0 в расширение phpBB 3.1 :co_ol:

Разбираться с этим мы будем на примере модификации NV Newspage от отличного разработчика nickvergessen.

План действий

  • Структура расширения
    • Расположение
    • Создание новых файлов (composer.json, ext.php)
    • Встроенные классы и переменные
    • Модуль в администраторском разделе
  • Изменения в Базе Данных, замена UMIL на Миграции (Migrations)
    • Изменения в Схеме (Schema)
    • Изменения в Данных
    • Зависимости и окончание миграции
  • Подключение к расширению языковых файлов
  • Использование Событий и Слушателей (Events and Listeners)
    • php события
    • События шаблонов
    • Добавление событий
  • Совместимость
    1. Пагинация (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)
         {
            \\\\\здесь ваши функции для модуля в админке\\\\
         }
      }
      Таким образом мы создали (сконвертировали) модуль для админки, двигаем дальше
  • 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()

      Код: Выделить всё

      public function effectively_installed()
         {
            return isset($this->config['newspage_mod_version']) && version_compare($this->config['newspage_mod_version'], '1.0.0', '>=');
         }
      Так как миграционные файлы могут иметь различные имена и движок должен сам определить, что конкретный файл - миграционный, в 3.1 добавлена еще функция 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')),
            );
         }
      }
  • 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 нашей статьи):

      Код: Выделить всё

       nickvergessen.newspage.listener:
              class: nickvergessen\newspage\event\main_listener
              arguments:
                  - @controller.helper
                  - @template
                  - @user
              tags:
                  - { name: event.listener }
      И с этим шагом мы завершили правки php-кода.
    • 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. А сделать это можно
  • 5. Совместимость
    С течением времени в разработке старые функции сменяются на новые, внимательно следите за всем этим в новостях разработки (можно еще посмотреть)
    • 5.1 Пагинация
      При создании пагинации по старому способу из тройки вы получите такую ошибку
      Fatal error: Call to undefined function generate_pagination() in ...\phpBB3\ext\nickvergessen\newspage\controller\main.php on line 534
      Проблема в том, что пагинация страниц теперь не возвращается функцией, вместо того, чтобы автоматически генерироваться в шаблоне. В том же шаге, имя функции было обновлено ​​с PHPBB-префиксом. Старый код пагинации страниц был таким (приблизительно):

      Код: Выделить всё

      $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),
         ));
На вопросы, связанные с phpBB, по ЛС не отвечаю !!!
#1
Ответить
  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение