Создание модулей
Что такое модуль?
Введение
ImageCMS позиционируется как модульная система управления контентом, что, несомненно, является большим плюсом. Именно такая архитектура обеспечивает гибкость системе и дарит разработчику возможность проявить свой талант и изобретательность. Предлагаем посмотреть на модульность как на детали конструктора: множество разных за формой, построением и предназначением деталей. Система у нас будет площадкой, к которой они крепятся, а документация, API от разработчиков и эта статья - соединяющие их механизмы.
Техническая сторона модуля
Расширяемость системы напрямую зависит от модулей, которые представляют собой небольшие программы. Они часто являются сторонними решениями, но также используют функциональное ядро ImageCMS или могут взаимодействовать с другими инструментами системы. Одной из главных задач модульной системы есть расширение функциональных возможностей CMS без необходимости редактирования других файлов, то есть автономность. Такой подход упрощает будущую поддержку функционала и обновление. При создании модуля мы всегда должны стремиться вносить изменение на стороне своих файлов, не изменяя ядро или другие наработки. Но иногда это может оказаться очень трудной задачей.
Структура файлов ImageCMS
Все файлы системы разделены по директориям для упрощения навигации. Большинство из них было продиктовано фреймворком и мы решили здесь не вмешиваться. Стандартная сборка ImageCMS состоит из следующих основных элементов:
/application
- общая папка, в которой совмещены основные файлы системы./application/config
- собраны файлы самых разных конфигураций. От записей о реквизитах доступа к базе данных, до манипулирования автозагрузками некоторых частей системы./application/modules
- здесь собраны доступные системе модули./system
- системная папка фреймворка CodeIgniter. Большинство классов из этой папки могут быть унаследованы либо использованы./templates
- папка, где размещены глобальные шаблоны, которые могут быть использованы для построения сайта./uploads
- медиа-папка, где, в большинстве своем, размещаются пользовательские графические элементы сайта (картинки галереи, товаров, графический контент для статей и т.д.). Так же могут содержаться другие файлы. Обладает правами на запись.- index.php - файл является точкой входа для работы с системой, на его плечах лежит задача инициализации основных глобальных переменных.
- .htaccess - содержит записи для сервера о правилах запуска системы и решает другие задачи (в частности, обработку ЧПУ).
В системе существует большое количество других системных или зарезервированных папок и файлов. Все они документированы фреймворком. Больше об их предназначении вы сможете прочитать на официальном сайте CodeIgniter (http://ellislab.com/codeigniter/user-guide).
Структура папки модуля
Мы уже вспоминали о папке, где располагаются модули системы, а именно директория /application/modules
. Дальше будут описаны наши рекомендации по структурированию вашего модуля. Мы остановились на таком подходе, потому что сочли его удобным для навигации по структуре и интуитивно понятным для разработчика. Вот как файлы будут организованы:
/application/modules/sample
- папка-контейнер для вашего модуля. Представим, что он носит название "Sample"..../sample/assets
- общая директория, в которой собраны все средства, отвечающие за внешний вид вашего функционала..../sample/assets/admin
- папка содержит файлы-шаблоны для административной части модуля..../sample/assets/js
- здесь располагаются файлы со скриптами клиентской части. Например JavaScript..../sample/assets/css
- тут могут быть собраны файлы стилей..../sample/assets/index.tpl
- в корне размещены файлы шаблонов для построения пользовательских интерфейсов..../sample/admin.php
- основной файл, который ищет система при запуске модуля из административной части..../sample/sample.php
- основной файл, который ищет система при запуске модуля из пользовательской части..../sample/module_info
- содержит техническую информацию и описание модуля.
Дальше мы разберем все это более подробно и подкрепим примером, что прольет свет на все темные уголки, если таковы остались. Но перед этим - последний шаг. Давайте посмотрим другие рекомендации и спецификации от разработчиков перед написанием своего модуля.
Спецификация. Coding Standart
Здесь заключены основные инструкции, которым надо следовать чтобы не натолкнуться на одну из распространенных ошибок.
- папка, в которой будут храниться файлы вашего модуля, должна иметь уникальное имя, сопоставимое с названием модуля, не содержать заглавных букв. Может содержать символ нижнего подчеркивания "_". Должна быть размещена в
/application/modules
; - основной PHP-файл должен иметь такое же имя, как и родительская папка, и такое же имя должен иметь класс, задекларированный в нем.
- класс "Admin" в одноименном файле admin.php, который отвечает за работу с модулем со стороны административной части, должен быть унаследован от родительского класса "
BaseAdminController
"
Создание собственного модуля
Ставим цели
Рассмотреть принцип работы можно на примере такой вот задачи: "Нужен модуль, который будет присылать администратору на почту сообщение о поступлении нового комментария на сайт. В письме должны присутствовать ссылки, используя которые, можно мгновенно изменить статус комментария (одобрить, отметить как спам). Предоставить администратору возможность управлять основными настройками модуля."
Организация файловой структуры
Начнем с организации файловой структуры. Будем считать наш модуль примером, а значит присвоим ему точно такое имя. Не очень оригинально, но вполне удобно. Создадим директорию "sample_module" и разместим её в "/application/modules/
". Надо создать другие необходимые директории и файлы, по примеру, описанному в разделе " Структура папки модуля". Существует альтернативный вариант, который освободит вас от лишней работы: сделайте копию папки "module_skeleton" и займитесь переименованием нужных элементов. "Module_skeleton
" является ничем другим, кроме как каркасом модуля с отображенной структурой и всеми нужными декларациями внутри. Воспользуйтесь методом, который сочли удобным для себя - и этот шаг мы с вами уже завершили.
Готовим программный каркас
Первым делом разберем, что будет содержать в себе основной класс Sample_Module.php
__construct()
- здесь останавливаться не станем. Будет вызван при создании экземпляра класса.index()
- функция будет доступна при обращении пользователя по ссылке http://sitename.com/sample_module.autoload()
- внутренний метод ImageCMS. Будет вызван при каждом обращении пользователя к системе при условии, что в административной части включена соответственная опция.changeStatus()
- еще один интересный метод, в который мы поместим код.handler()
- здесь мы создадим обработчик события о появлении нового комментария. Его задача - создание и отправка письма администратору. После вызова управление будет передано на приватный метод "composeAndSendEmail", где и реализуем всю логику.composeAndSendEmail()
- разместим функционал создания тела письма-уведомления о новом комментарии и непосредственно саму логику.initSettings()
- наш метод, на плечи которого положим инициализацию переменных. Значения для переменных мы будем брать из базы данных. Поместим его вызов в конструктор класса чтобы быть уверенными, что переменные будут инициализированы каждый раз при создании экземпляра класса._install()
- метод сработает при установке модуля пользователем._deinstall()
- метод сработает при удалении модуля пользователем.
Подведем итог. При создании пользователем нового комментария, мы:
- Перехватим это событие.
- Запросим с БД значение настроек, чтобы знать о необходимости отправления письма, адрес, на который оно будет отослано, и секретный ключ для защиты от злоумышленников.
- Подготовимся к формированию тела письма, собрав информацию о комментарии и странице, на которой он был размещен.
- Скомпонуем все в письмо и отошлем администратору.
- Подготовим метод, при обращении на который мы изменим статус комментария относительно входных данных.
Возможно, с первого взгляда задача покажется слишком запутанная или сложная, но дальше вы увидите, как просто все это реализовывать с ImageCMS в паре с CodeIgniter. На этом все подготовительные работы мы разобрали, можем приступить к первому шагу - подготовке модуля к установке.
Этап 1. Установка модуля
Когда пользователь из административной части установит модуль, будет вызван системный метод _install().
Здесь мы можем описать все необходимые функции, которые обуславливают стабильную работу модуля, например обновление нужных таблиц или создание новых. В нашем случае есть необходимость создать таблицу для модуля, в которой будем хранить настройки. Назовем ее "mod_sample_settings". Префикс "mod_" является обязательным, и применяется для облегчения визуального сепарирования таблиц для системы и модулей.
Этот код создаст нам таблицу и вставит в ее поля записи "mailTo", "useEmailNotification", и "key".
Этап 2. Создаем интерфейс администратора
Напомним, что отображение интерфейса администратора берет начало с файла admin.php
, который унаследует общий класс BaseAdminController.php
. Метод index.php
в нашей реализации будет отображать шаблон settings.tpl
и предоставлять пользователю возможность просмотреть сохраненные данные. При смене этих данных мы с помощью формы отправим запрос на метод updateSettings()
, где наша логика обновит значения полей в таблице БД. Начнем с отображения sample_module/admin.php:
Шаблон settings.tpl
будет отображать 3 поля для ввода информации. Чтобы заполнить их, нам нужно запросить значения с базы данных, где они хранятся (были созданы при установке модуля). Используем класс работы с шаблонами. Мы передаем массив значений в шаблон с помощью метода setData()
. Заметьте, что в шаблоне в качестве переменной будет именно ключ массива. Методом renderAdmin() мы дадим команду на отображение шаблона, указанного в первом аргументе. Больше об использовании класса работы с шаблонами и несколько примеров можно посмотреть в разделе assetManager Class или на странице обсуждений Idea.ImageCMS.net (CMSFactoryassetManager Class). Посмотрим, что у нас получилось.
В форме в качестве параметра "action" указан метод updateSettings(), именно он будет сохранять обновленные данные. Запрос ничем вас не должен удивить, разве что отсутствием валидации. Но позвольте оставить это на следующий урок. Попробуйте изменить данные в форме и подтвердить сохранение. Система вас уведомила о том, что действие прошло успешно. Все потому, что в завершении работы метода мы формируем ответ и возвращаем его обратно с помощью "showMessage".
Этап 3. Обработка запроса на смену статуса комментария
Теперь нам нужен метод, который будет обрабатывать запросы на смену статуса комментария, основываясь на входных данных. Назовем его changeStatus
. Входными параметрами для него будут:
- commentId - ID комментария, который будет обрабатываться;
- status - статус, который будет присвоен комментарию;
- key - ключ проверки подлинности запроса (должен совпадать с ключом, указанным в настройках модуля, и быть секретным).
Немного затрону тему, как эти данные будут переданы. В ImageCMS это осуществляется обращением к правильно сформированному URL. В нашем случае это будет похоже на: www.sitename.com/sample_module/changeStatus/144/0/UUUsssTTTeee
Для более глубокого понимания разделим URL на сегменты и разберем их:
- www.sitename.com - доменное имя сайта;
- UUUsssTTTeee - имя модуля, также имя класса;
- changeStatus - метод, который будет вызван при обращении по выше указанному адресу. Будет искаться в классе sample_module.
- 144 - параметр $commentId, который принимает метод "changeStatus".
- 0 - параметр $satus, указатель статуса (0, 1 или 2 - одобрен, отклонен или в спам).
- UUUsssTTTeee - параметр $key секретный ключ, будет сопоставлен с ключом, введенным в админке для предотвращения управления комментариями злоумышленником.
Ну что ж, мы теперь знаем как будет происходит управление по URL. В следующем разделе мы затронем тему компонования письма, в теле которого будет правильно сформирован этот URL. Эта тема тянет за собой немного больше знаний, поэтому сейчас предлагаю рассмотреть процесс написания обработчика.
- Для начала проверим на достоверность входных данных;
- Обратимся к БД для изменения статуса;
- Покажем пользователю сообщение об успешной операции;
application/modules/sample_module/sample_module.php:
Ничего сложного. Для большей уверенности в том, что мануал будет достаточно доступен для чтения и изучения - мы написали много комментариев к коду, надеемся смогли добиться простоты и прозрачности. Пришло время открыть последнюю тему, перехват события создания нового комментария, формирования и отправки письма модератору.
Этап 4. Перехват события и отправка письма
Для начала немного разберем сам принцип работы событий ImageCMS. Больше вы сможете почитать в разделе документации Event Class или на странице обсуждений Idea.ImageCMS (Events), но базовые принципы я попытаюсь раскрыть в достаточной степени прямо сейчас. Функция системных событий стала доступна в ImageCMS с версии 4.3 и выше. Принцип достаточно прост: при срабатывании события, информация о нем забивается в некий общий пулл. Вам остается указать какое событие вы ждете и какой метод будет запущен в качестве обработчика. Еще одним важным моментом является место, где должен описываться обработчик. Для ImageCMS это методы автозагрузки autoload (запускается при обращении к фронтовой части) и adminAutoload (будет запущен при обращении к административной части). В нашем случае, подходит метод autoload. Нас интересует момент когда пользователь добавит новый комментарий, а случится это на фронтовой части. Поэтому мы в метод "autoload" огласим обработчик:
CMSFactoryEvents::create()->setListener('handler', 'Sample_Module:__construct');
Это означает, что будет вызвано событие "Создание нового комментария", система так же запустит метод handler
класса Sample_Module
, с массивом параметров в качестве аргумента. Параметры передаются разные, в зависимости от события, поэтому для уточнения какие из параметров вам следует ожидать, обратитесь к документации. В нашем случае, для комментариев придет лишь единое значение commentId - ID ново-созданного комментария. Обладая этой информацией, мы можем приступить к завершающей стадии - созданию письма и отправка письма администратору. Логику формирования и отправки письма мы положим на метод composeAndSendEmail и вызвем его из 'handler', который, как мы уже говорили, будет вызван после создания нового комментария.
В handler
будет всего две строчки:
$instance = new self();
$instance->composeAndSendEmail($param);
Все дело в том, что метод - обработчик статический. Поэтому мы создаем экземпляр класса Sample_Module
и запустим composeAndSendEmail
;Для отправки письма первым делом возьмем информацию по комментарию из БД
$comment = $this->db->where('id', $arg['commentId'])->get('comments')->row();
Добавим информацию о странице - носителе модуля
Код:
/** Используем помощник get_page($id), который аргументом принимает ID страницы. * Помощник включен по умолчанию.
Больше о функции помощника читайте на сайте CodeIgniter
http://ellislab.com/codeigniter/user-guide/general/helpers.html */
if ($comment->module'core')
$comment->source = get_page($comment->item_id);
Тело письма мы сформируем из шаблона. Для примера я взял первый попавшийся в поиске шаблон письма.
/** Теперь переменная содержит HTML тело нашего письма */
$message = \CMSFactory\assetManager::create()
->setData(array('comment' => $comment, 'key' => $this->key))
->fetchTemplate('emailPattern');
Тело письма мы сформируем из шаблона. Для примера я взял первый попавшийся в поиске шаблон письма.
/** Теперь переменная содержит HTML тело нашего письма */
$message = \CMSFactory\assetManager::create()
->setData(array('comment' => $comment, 'key' => $this->key))
->fetchTemplate('emailPattern');
После шаблонизирования переменная $message будет содержать текст письма, а само письмо готово к отправке. Настраиваем отправку письма
/** Настраиваем отправку Email http://ellislab.com/codeigniter/user-guide/libraries/email.html */
$this->load->library('email');
$this->email->initialize(array('mailtype' => 'html'));
$this->email->from('robot@sitename.com
', 'Comments Robot'); $this->email->to($this->mailTo); $this->email->subject('New Comment received'); $this->email->message($message); $this->email->send();
Вернемся к тому, с чего начинали (итоги)
Могу вас поздравить - мы справились с заданием. Решение наше было таким (последовательность):
- пользователь оставляет комментарий;
- система вызывает событие;
- модуль реагирует на событие, вызывая метод handler;
- получив информацию о комментарии, формируем письмо;
- отправляем письмо;
- администратор, получив письмо, переходит по ссылке в нем (Одобрить, Отклонить, Спам);
- модуль обрабатывает один из этих запросов и меняет статус комментария.
Дополнительная информация
О файле module_info.php
Данный файл лежит в корневой папке модуля, и служит неким описанием для системных и пользовательских нужд. Содержит массив данных $com_info
c следующими парами ключ-значение:
<?php (defined('BASEPATH')) OR exit('No direct script access allowed');
$com_info = array( 'menu_name' => 'Название модуля’,
'description' => 'Описание модуля',
'version' => 'Версия модуля, состоит из цифр, разделенных точкой (1.2)',
'author' => ‘Контактный е-mail автора’);
Отображение шаблонов
Для работы с шаблонами в модулях ImageCMS используется assetManager
класс с пространства имен CMSFactory
. Вот список методов, представленных в классе:
setData($arg1, $arg2)
- передает данные в шаблон;registerScript($name)
- подключает JS файл в конец документа;registerStyle($name)
- подключает CSS файл в начало документа;renderAdmin($tpl_name)
- отображает указанный шаблон. Используется для отображения шаблонов в административной части;render($tpl_name, $ignoreWrap = FALSE)
- отображает указанный шаблон. Второй параметр является булевым, и указывает отображать шаблон в контексте основного, либо игнорировать его;fetchTemplate($tpl_name)
- в отличии от render, возвращает результат шаблонизирования.
При работе с шаблоном допускается метод липкой связки. Для оглашения:
- пишем класс;
- статический метод create();
- дальше следуют нужные указания;
- обязательно закрепить все одним из методов отображения шаблонов.
Например:
CMSFactoryassetManager::create() ->setData('comment', $comment) ->render('successful');
В этом случае будет отображен шаблон "successful.tpl", и передана переменная comment
, которая будет доступна непосредственно в шаблоне как $comment
. Также возможны другие варианты использования. Например, если хотите добавить JavaScript файл или файл стилей, добавьте в связку registerScript
или registerStyle
:
CMSFactoryassetManager::create()
->setData($data)
->registerScript('scriptfile')
->registerStyle('mycss')
->renderAdmin('settings');
Файлы скриптов будут размещены внизу файла, перед закрывающим тегом, а файлы стилей будут помещены в конструкцию.
Заключение
Хочу подкрепить выложенную информацию ссылками на источники, где можно почерпнуть больше знаний.
- аssetManager - работу с assetManager мы описали здесь http://idea.imagecms.net/topic/162402-cmsfactoryassetmanager-class, также можно принять участие в идеях будущего развития класса.
- Events Class - всё о работе класса событий мы обсуждаем на идее http://idea.imagecms.net/topic/163721-cmsfactoryevents-class
- CodeIgniter Framework Manual - конечно, никуда без опыта работы с фремворком. http://ellislab.com/codeigniter/user-guide