Как написать плагин для modx revo

Что такое плагин?¶

Плагины похожи на Cниппеты тем, что они также представляют собой фрагменты PHP кода, которые имеют доступ к API MODX. Наибольшая разница, однако, заключается в том, когда именно выполняется этот код. Вы помещаете сниппеты внутри страницы или внутри шаблона, и они запускаются при просмотре страницы, тогда как плагины настроены на выполнение во время определенных системных событий, например сохранение чанка или очистка кеша. Поэтому, когда данное событие «срабатывает», любой плагин «прослушивает» это событие. После выполнения кода плагина управление возвращается к точке после того места, где было инициировано системное событие.

Другие CMS
Каждая CMS использует некое понятие «плагин», но точная номенклатура может отличаться. Например, в WordPress плагины «подключаются» к событиям, называемым «действиями» или «фильтрами».

Поскольку они выполняются во время различных событий, плагины не ограничиваются обработкой внешнего интерфейса. Многие события запускаются событиями, которые происходят только в менеджере MODX. Список системных событий MODX можно посмотреть здесь.

Любой закрывающий тег php?> будет удален из кода вашего плагина при его сохранении. Это не нужно (и нежелательно), потому что код плагина при запуске будет находиться внутри другого кода PHP.

Модель события¶

MODX вызывает системные события в своих процессах кода, чтобы вы могли изменять функциональность ядра без взлома ядра. К этим системным событиям может быть прикреплено любое количество плагинов, и они будут запускать каждый плагин в соответствии с его приоритетом (сначала самые низкие номера)

Обработка события¶

В вашем плагине способ обработки выходных данных зависит от системного события, в котором вы находитесь. Для некоторых системных событий вы возвращаете значение из плагина. Для других вы получаете доступ к выводу по ссылке и изменяете его.

Если вам нужно знать, какое событие вызвало ваш плагин (скажем, для плагина, который прослушивает более одного события), вы можете получить доступ к имени события следующим образом:

$eventName = $modx->event->name;

Код для плагина, прослушивающего более одного события, выглядит следующим образом:

$eventName = $modx->event->name;
switch($eventName) {
    case 'OnWebPageInit':
        /* сделать что-то */
        break;
    case 'OnWebPagePrerender':
        /* сделать что-то другое */
        break;
}

Примеры плагинов¶

Плагины могут быть использованы для различных целей, ниже приведены несколько примеров:

Сообщение пользователю¶

Описание: Отправьте сообщение пользователю при создании/редактировании страницы.
Системные события: OnDocFormPrerender

$modx->event->output('Привет, пользователь!');

Пользовательская проверка¶

Описание: Выполните некоторую пользовательскую проверку при сохранении ресурса страницы.
Системные события: OnBeforeDocFormSave

// Сделать какую-нибудь логичную штуку... если проверка не удалась:
$modx->event->output('Что-то не прошло проверку!');
return "Этот текст пойдет в логи";

Хитрость в том, что то, что вы хотите сообщить пользователю, должно быть передано функции $modx->event->output(); любой текст, который вы хотите записать в журналы, может быть просто возвращен плагином. Если вы прошли валидацию, просто верните ноль.

HTML код не разрешен
Выходные данные, которые вы устанавливаете в $modx->&event->output(), не должны содержать HTML! Используйте только простой текст! Это связано с тем, что сообщение передается пользователю через модальное окно JavaScript.

Возвращаемое значение должно быть строкой. Если возвращаемое значение будет числом, объедините его с пустой строкой.

Фильтр слов¶

Описание: Отфильтруйте слова из документа перед его отображением в Интернете.
Системные события: OnWebPagePrerender

$words = array("snippet", "template"); // слова для фильтрации
$output = &$modx->resource->_output; // получить ссылку для вывода
$output = str_replace($words,"<b>[отфильтровано]</b>",$output);

Перенаправитель страницы «Не найден

Описание: Перенаправляет пользователя на выбранный документ и отправляет сообщение.
Системные события: OnPageNotFound
Системные настройки:

  • pnf.page: Идентификатор ресурса (ID)
  • pnf.mailto: E-mail адрес получателя
  • pnf.mailfrom: E-mail адрес отправителя
if ($modx->event->name == 'OnPageNotFound') {
     $errorPage = $modx->getOption('pnf.page');
     if (empty($errorPage)) {
         $modx->sendErrorPage();
     } else {
         $mailto = $modx->getOption('pnf.mailto');
         if (!empty($mailto)) {
            // отправить сообщение на локальный профиль
            $resourceId = $modx->resource->get('id');
            $subject = 'Страница не найдена';
            $body = 'Кто-то пытался зайти на страницу с ID '.$resourceId;
            $modx->getService('mail', 'mail.modPHPMailer');
            $modx->mail->set(modMail::MAIL_BODY, $body);
            $modx->mail->set(modMail::MAIL_FROM, $modx->getOption('pnf.mailfrom'));
            $modx->mail->set(modMail::MAIL_FROM_NAME, 'MODX');
            $modx->mail->set(modMail::MAIL_SENDER, 'MODX');
            $modx->mail->set(modMail::MAIL_SUBJECT, $subject);
            $modx->mail->address('to',$mailto);
            $modx->mail->setHTML(true);
            $modx->mail->send();
         }
         $url = $this->makeUrl($scriptProperties['page']);
         $modx->sendRedirect($url, 1);
         exit;
    }
}

Смотрите также¶

  1. Системные события
  2. OnBeforeCacheUpdate
  3. OnBeforeChunkFormDelete
  4. OnBeforeChunkFormSave
  5. OnBeforeDocFormDelete
  6. OnBeforeDocFormSave
  7. OnBeforeManagerLogout
  8. OnBeforeSaveWebPageCache
  9. OnBeforeWebLogout
  10. OnCacheUpdate
  11. OnChunkFormDelete
  12. OnChunkFormPrerender
  13. OnChunkFormRender
  14. OnChunkFormSave
  15. OnDocFormDelete
  16. OnDocFormPrerender
  17. OnDocFormRender
  18. OnDocFormSave
  19. OnDocPublished
  20. OnDocUnPublished
  21. OnLoadWebPageCache
  22. OnManagerLogin
  23. OnManagerLogout
  24. OnSiteRefresh
  25. OnUserChangePassword
  26. OnWebLogin
  27. OnWebLogout
  28. OnWebPagePrerender
  29. OnManagerPageBeforeRender
  30. OnTemplateVarBeforeSave
  31. OnTemplateVarSave
  32. OnTemplateVarBeforeRemove
  33. OnTemplateVarRemove
  34. OnBeforeEmptyTrash
  35. OnBeforeManagerLogin
  36. OnBeforeManagerPageInit
  37. OnBeforePluginFormDelete
  38. OnBeforePluginFormSave
  39. OnBeforeSnipFormDelete
  40. OnBeforeSnipFormSave
  41. OnBeforeTempFormDelete
  42. OnBeforeTempFormSave
  43. OnBeforeTVFormDelete
  44. OnBeforeTVFormSave
  45. OnBeforeUserActivate
  46. OnBeforeUserFormDelete
  47. OnBeforeUserFormSave
  48. OnBeforeWebLogin
  49. OnCategoryBeforeRemove
  50. OnCategoryBeforeSave
  51. OnCategoryRemove
  52. OnCategorySave
  53. OnChunkBeforeRemove
  54. OnChunkBeforeSave
  55. OnChunkRemove
  56. OnChunkSave
  57. OnContextBeforeRemove
  58. OnContextBeforeSave
  59. OnContextFormPrerender
  60. OnContextFormRender
  61. OnContextRemove
  62. OnContextSave
  63. OnEmptyTrash
  64. OnFileManagerUpload
  65. OnHandleRequest
  66. OnInitCulture
  67. OnLoadWebDocument
  68. OnManagerAuthentication
  69. OnManagerLoginFormPrerender
  70. OnManagerLoginFormRender
  71. OnManagerPageAfterRender
  72. OnManagerPageInit
  73. OnPageNotFound
  74. OnPageUnauthorized
  75. OnParseDocument
  76. OnPluginBeforeRemove
  77. OnPluginBeforeSave
  78. OnPluginEventRemove
  79. OnPluginFormDelete
  80. OnPluginFormPrerender
  81. OnPluginFormRender
  82. OnPluginFormSave
  83. OnPluginRemove
  84. OnPluginSave
  85. OnPropertySetBeforeRemove
  86. OnPropertySetBeforeSave
  87. OnPropertySetRemove
  88. OnPropertySetSave
  89. OnResourceGroupBeforeRemove
  90. OnResourceGroupBeforeSave
  91. OnResourceGroupRemove
  92. OnResourceGroupSave
  93. OnRichTextBrowserInit
  94. OnRichTextEditorInit
  95. OnRichTextEditorRegister
  96. OnSiteSettingsRender
  97. OnUserActivate
  98. OnUserBeforeRemove
  99. OnUserBeforeSave
  100. OnUserFormDelete
  101. OnUserFormSave
  102. OnUserNotFound
  103. OnUserRemove
  104. OnUserSave
  105. OnWebAuthentication
  106. OnWebPageComplete
  107. OnWebPageInit

Open COllective

Support the team building MODX with a monthly donation.

The budget raised through OpenCollective is transparent, including payouts, and any contributor can apply to be paid for their work on MODX.

Backers

Budget

$292 per month—let’s make that $500!

Learn more

Programming in MODX Revolution¶

MODX Revolution is an OOP Framework, built around the database ORM xPDO.

3rd-Party Components (3PCs)¶

3rd-Party Components (3PCs) are collections of any sort of MODX Objects. They can be a collection of Snippets, Plugins and Chunks, or a single Snippet, or just a collection of files. They are usually transported and installed via Transport Packages.

core/components and assets/components¶

MODX doesn’t necessarily limit where you can put your custom 3rd party component files, but we do have some recommendations. For files that don’t need to be in the webroot (config files, .php, etc), we recommend putting them in:

core/components/myname

So, if you had a component named ‘test’, you would put its non-webroot files in «core/components/test/». For files that need to be web-accessible, such as css, js and other files, we recommend:

assets/components/myname

Ergo, for ‘test’, «assets/components/test». This standardization of paths makes it easier for other developers using your components to find your files easily.

Snippets¶

Snippets are simply php scripts that can be executed on any page or other Element. They are the cornerstone of MODX Development and dynamic customization. You can read more about Snippets here.

Plugins¶

Plugins are similar to snippets in that they are snippets of code that have access to the MODX API — however the big difference is that plugins are associated to specific system events. For example, in an average MODX page request, several events happen at certain points within the page parsing process and plugins can be attached to any of these events to fulfill a desired function. Plugins aren’t just limited to front-end processing though, there are many events that are available in the MODX Manager.

Properties and Property Sets¶

Properties are simply placeholders on Elements (Snippets/Plugins/Chunks/TVs/Templates), which can be parsed by each individual Element. They allow customization and argument passing for each Element.

Property Sets are user-defined groupings of Properties that can be used to quickly centralize custom tag syntax calls.

More on Property Sets can be found here.

Custom Manager Pages (CMPs)¶

Custom Manager Pages, or CMPs, are custom pages in the manager built by 3rd Party developers to allow backend management of Components. They use the modAction and modMenu objects to dynamically create manager pages that can be easily found and added with no hacking of the core.

Using MODX Externally¶

Using the MODX object (and all of its respective classes) is quite simple. All you need is this code:

require_once '/absolute/path/to/modx/config.core.php';
require_once MODX_CORE_PATH.'model/modx/modx.class.php';
$modx = new modX();
$modx->initialize('web');
$modx->getService('error', 'error.modError');

This will initialize the MODX object into the ‘web’ Context. Now, if you want to access it under a different Context (and thereby changing its access permissions, policies, etc), you’ll just need to change ‘web’ to whatever Context you want to load.

Open COllective

Support the team building MODX with a monthly donation.

The budget raised through OpenCollective is transparent, including payouts, and any contributor can apply to be paid for their work on MODX.

Backers

Budget

$292 per month—let’s make that $500!

Learn more

Что такое плагин?

Между плагином и сниппетом очень много общего. Как и сниппет, плагин содержит код на PHP. Разница кроется в способе вызова: если вызов сниппета определяется местом, где вы указали его тег (в шаблоне, чанке, другом сниппете или ресурсе), то плагин может вызываться при срабатывании определённых системных событий (обработка запроса, очистка кэша, сохранение ресурса, вывод страницы и т. д.). Список системных событий можно посмотреть

здесь

.

Что за системные события?

Во время работы MODX срабатывают определённые события. И за этими событиями могут быть закреплены плагины. За каждым событием может быть закреплено неопределённое количество плагинов, приоритет запуска которых настраивается.

Обработка событий

Поведение плагина зависит от типа события: для каких-то событий нужно возвращать значение через оператор return, для других — обращаться к данным и изменять их по ссылке.

Узнать название события можно следующим способом:

$eventName = $modx->event->name;

Пример

Впервые мне пришлось написать плагин для новостей на одном сайте. Суть в том, чтобы для изображений внутри статьи автоматически создавались миниатюры. Допустим, есть статья, внутри которой 5 изображений с определённым классом, а в атрибуте src указан путь до оригинала изображения. Написанный мной плагин перед выводом отбирает все теги изображения с определённым классом и подменяет у них значение атрибута src на адрес созданной миниатюры. Миниатюра создаётся с помощью phpThumb. Код далеко не идеальный, но пока нареканий не было.

$content = $modx->resource->get('content');
preg_match_all('/]+>/im', $content, $result);
if (isset($result[0]) && count($result[0]) > 0) {
    $images = $result[0];
    foreach ($images as $image) {
        if (containResizeClass($image)) {
            $imageLink = genThumb($image);
            $content = str_replace($image, $imageLink, $content);
        }
    }
}
$modx->resource->set('content', $content);

Итак, что же здесь происходит? Сначала в переменную $content копируется конечный HTML-код. Затем в массив $result заносятся результаты поиска по регулярному выражению. В данном случае искались абсолютно все изображения страницы. Затем скрипт в цикле начинает обрабатывать все полученные изображения. Если тег изображения содержит определённый класс (проверка внутри функции containResizeClass), то в переменную $imageThumb заносится тег с адресом миниатюры, а затем тег оригинала заменяется на тег миниатюры.

Таким образом, если на странице есть следующий тег,

<img alt="Котики" class="thumb_me article_image_left" src="assets/images/fat_cat.jpg" />

то с помощью плагина можно автоматически заменять этот код на всё, что угодно:

<a class="fancybox-thumbs" data-fancybox-group="news_thumb" href="fat_cat.jpg">
<img alt="Котики" class="thumb_me article_image_left" src="/assets/components/phpthumbof/cache/fat_cat.2b43bb836baec1488cd299.jpg" />
</a>

В последней строке идёт замена содержимого страницы с оригинального на то, что мы получили после обработки изображений. Код функций нет смысла показывать, так как это статья про плагины, а не строковые функции PHP или MODX API. Данный плагин запускается при срабатывании события OnLoadWebDocument.

Возможно, не самый лучший код, но уже несколько месяцев работает без нареканий.

В качестве ещё одного примера могу привести более простой плагин для минификации HTML страницы. 

$html = &$modx->resource->_output;
$html = preg_replace('|s+|', ' ', $html);
$modx->resource->set('content', $html);

Данный сниппет привязан к событию «OnWebPagePrerender».

Примеры плагинов

  • MODX: отправка новостей в Telegram-канал
  • Queeg
  • Автоматизация изменения цифрового отпечатка URL
  • MODX Revolution: плагин для отслеживания ошибки 404

Время на прочтение
14 мин

Количество просмотров 13K

Это перевод третьей части урока. Как я писал в предыдущей статье, переводить вторую часть особого смысла нет, т.к. информации на русском по созданию страницы компонента уже достаточно. Поэтому я перескочил на третью.

В этом уроке будет рассказано как упаковать дополнение в транспортный пакет, который затем можно будет легко установить через «Управление пакетами». Упаковывать будем всё, что относится к, разработанному нами, дополнению: сниппет; файлы из core/components/ и assets/components/; действия; пункт в меню и пространство имен нашей CMP (страницы компонента); значения по умолчанию для сниппета с поддержкой интернационализации (i18n). А также добавим резольвер, который создаст пользовательские таблицы в БД.

Справка:
Для упаковки простых дополнений можно использовать PackMan. Но в данном случае мы хотим сделать это самостоятельно и полностью разобраться что из себя представляет транспортный пакет.

Настройка директории для сборки

В конце урока каталог _build будет выглядеть так:

Мы уже знакомы с файлами build.config.php и build.schema.php из первой части урока, а сейчас давайте просто посмотрим на другие части:

data — Здесь мы собираемся поместить все наши скрипты для упаковки данных пакета.
resolvers — Папка содержит резольверы для транспортного пакета.
build.transport.php — Это главный скрипт упаковщика, который нужно будет запустить для создания пакета.
setup.options.php — Настройки установщика. Позже кратко рассмотрим для чего это нужно.

Создание скрипта упаковщика

Создадим файл /www/doodles/_build/build.transport.php с таким содержинием:

<?php

$tstart = explode(' ', microtime());
$tstart = $tstart[1] + $tstart[0];
set_time_limit(0);
 
/* задаем имя пакета */
define('PKG_NAME','Doodles');
define('PKG_NAME_LOWER','doodles');
define('PKG_VERSION','1.0');
define('PKG_RELEASE','rc1');
 
/* задаем пути для упаковщика */
$root = dirname(dirname(__FILE__)).'/';
$sources = array(
    'root' => $root,
    'build' => $root . '_build/',
    'data' => $root . '_build/data/',
    'resolvers' => $root . '_build/resolvers/',
    'chunks' => $root.'core/components/'.PKG_NAME_LOWER.'/chunks/',
    'lexicon' => $root . 'core/components/'.PKG_NAME_LOWER.'/lexicon/',
    'docs' => $root.'core/components/'.PKG_NAME_LOWER.'/docs/',
    'elements' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/',
    'source_assets' => $root.'assets/components/'.PKG_NAME_LOWER,
    'source_core' => $root.'core/components/'.PKG_NAME_LOWER,
);
unset($root);
 
/* override with your own defines here (see build.config.sample.php) */
require_once $sources['build'] . 'build.config.php';
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
 
$modx= new modX();
$modx->initialize('mgr');
echo '<pre>'; /* used for nice formatting of log messages */
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
$modx->setLogTarget('ECHO');
 
$modx->loadClass('transport.modPackageBuilder','',false, true);
$builder = new modPackageBuilder($modx);
$builder->createPackage(PKG_NAME_LOWER,PKG_VERSION,PKG_RELEASE);
$builder->registerNamespace(PKG_NAME_LOWER,false,true,'{core_path}components/'.PKG_NAME_LOWER.'/');
 
/* zip up package */
$modx->log(modX::LOG_LEVEL_INFO,'Packing up transport package zip...');
$builder->pack();
 
$tend= explode(" ", microtime());
$tend= $tend[1] + $tend[0];
$totalTime= sprintf("%2.4f s",($tend - $tstart));
$modx->log(modX::LOG_LEVEL_INFO,"n<br />Package Built.<br />nExecution time: {$totalTime}n");
exit ();

Тут довольно много всего, но заметим, что это всё, что нужно для упаковки нашего пространства имен и создания файла транспортного пакета «doodles-1.0-rc1.zip» (только основа). Разберем подробно.

$tstart = explode(' ', microtime());
$tstart = $tstart[1] + $tstart[0];
set_time_limit(0);
 
/* задаем имя пакета */
define('PKG_NAME','Doodles');
define('PKG_NAME_LOWER','doodles');
define('PKG_VERSION','1.0');
define('PKG_RELEASE','rc1');

Во-первых мы собираемся получить время начала сборки, чтобы в конце вывести сколько времени потребовалось на сборку. Это совсем не обязательно, просто полезная информация. Затем мы указываем название, версию и тип релиза. Далее:

/* задаем пути для упаковщика */
$root = dirname(dirname(__FILE__)).'/';
$sources = array(
    'root' => $root,
    'build' => $root . '_build/',
    'data' => $root . '_build/data/',
    'resolvers' => $root . '_build/resolvers/',
    'chunks' => $root.'core/components/'.PKG_NAME_LOWER.'/chunks/',
    'lexicon' => $root . 'core/components/'.PKG_NAME_LOWER.'/lexicon/',
    'docs' => $root.'core/components/'.PKG_NAME_LOWER.'/docs/',
    'elements' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/',
    'source_assets' => $root.'assets/components/'.PKG_NAME_LOWER,
    'source_core' => $root.'core/components/'.PKG_NAME_LOWER,
);
unset($root);
 
/* override with your own defines here (see build.config.sample.php) */
require_once $sources['build'] . 'build.config.php';
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';

Здесь мы определяем пути, где найти все части нашего пакета для упаковки.

Наконец, мы подключили файл build.config.php и класс MODx. Теперь пришло время загрузить объект MODx:

$modx = new modX();
$modx->initialize('mgr');
echo '<pre>'; /* used for nice formatting of log messages */
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
$modx->setLogTarget('ECHO');
 
$modx->loadClass('transport.modPackageBuilder','',false, true);
$builder = new modPackageBuilder($modx);
$builder->createPackage(PKG_NAME_LOWER,PKG_VERSION,PKG_RELEASE);
$builder->registerNamespace(PKG_NAME_LOWER,false,true,'{core_path}components/'.PKG_NAME_LOWER.'/');

Здесь мы создаем объект modX и инициализируем контекст «mgr». Далее мы просим MODX быть более многословным в его сообщениях об ощибках во время работы нашего скрипта. Просим выводить сообщения на экран.

Затем загружаем класс «modPackageBuilder» и получаем два полезных метода createPackage и registerNamespace.

$modx->createPackage(key,version,release)

Тут задается имя нашего пакета (оно должно быть в нижнем регистре и не должно содержать точку или дефис), версию и тип релиза. Теперь modPackageBuilder автоматически упакует наше пространство имен:

$builder->registerNamespace(namespace_name,autoincludes,packageNamespace,namespacePath)

Первым параметром является имя пространства имен («doodles» в нашем случае). Вторым — массив классов, связанных с нашим пространством имен (нам это не нужно, поэтому устанавливаем false). Третим параметром мы говорим, что хотим упаковать пространство имен в пакет (устанавливаем в true). И третим параметром задаем путь до нашего пространства имен. Этот последний параметр является ключевым. Обратите внимание на плейсхолдер «{core_path}», он будет заменен на реальный путь во время установки пакета, что позволит сделать пакет более гибким. Не нужно указывать пути жестко.

И вот несколько последних строк нашего упаковщика:

/* zip up package */
$modx->log(modX::LOG_LEVEL_INFO,'Packing up transport package zip...');
$builder->pack();
 
$tend= explode(" ", microtime());
$tend= $tend[1] + $tend[0];
$totalTime= sprintf("%2.4f s",($tend - $tstart));
$modx->log(modX::LOG_LEVEL_INFO,"n<br />Package Built.<br />nExecution time: {$totalTime}n");
exit ();

Метод pack() говорит MODX, что нужно создать файл ZIP транспотного пакета. Остальные строки просто выводят время, которое потребовалось для сборки. Вот И всё. Если вы запустите
это в браузере (у меня адрес http ://localhost/doodles/_build/build.transport.php), вы получите отладочную информацию и в папке core/packages/ это:

Это наш транспортный пакет! Однако конкретно для нашего дополнения этого не достаточно.

Добавление данных

Мы хотим добавить в пакет наш сниппет в отдельной категории «Doodles». В файле build.transport.php добавим ниже registerNamespace такой код:

<?php
$category= $modx->newObject('modCategory');
$category->set('id',1);
$category->set('category',PKG_NAME);
 
/* добавляем сниппет */
//$modx->log(modX::LOG_LEVEL_INFO,'Packaging in snippets...');
//$snippets = include $sources['data'].'transport.snippets.php';
//if (empty($snippets)) $modx->log(modX::LOG_LEVEL_ERROR,'Could not package in snippets.');
//$category->addMany($snippets);
 
/* create category vehicle */
$attr = array(
    xPDOTransport::UNIQUE_KEY => 'category',
    xPDOTransport::PRESERVE_KEYS => false,
    xPDOTransport::UPDATE_OBJECT => true,
    xPDOTransport::RELATED_OBJECTS => true,
    xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array (
        'Snippets' => array(
            xPDOTransport::PRESERVE_KEYS => false,
            xPDOTransport::UPDATE_OBJECT => true,
            xPDOTransport::UNIQUE_KEY => 'name',
        ),
    ),
);
$vehicle = $builder->createVehicle($category,$attr);
$builder->putVehicle($vehicle);

Во-первых мы создаем объект modCategory (категория) с именем «Doodles». Обратите внимание, что мы не сохраняем ->save(), а только создаем объект. Далее у нас есть код для упаковки снипета, но пока проигнорируем его, мы вернемся к нему позже.

Затем мы создали большой массив атрибутов — атрибутов транспортного средства (Vehicle) категории. Что за транспортное средство? Ну, это транспортное средство, которое несет объект к транспортному пакету. Каждый объект (сниппет, пункт меню, категория и т.п.) должен иметь транспортное средство для «перевозки» в транспортный пакет. Таким образом мы создали один из них, но сначало присвоили несколько атрибутов, которые говорят MODX как это транспортное средство должно вести себя когда пользователь устанавливает пакет.

  • xPDOTransport::UNIQUE_KEY => ‘category’ — здесь мы говорим MODX, что уникальным ключём для этой категории является поле «category».
  • xPDOTransport::PRESERVE_KEYS => false — иногда мы хотим чтобы первичный ключ нашего объекта был «сохранен». Это полезно для не автоинкрементных ключей (PKs), таких как у меню, которое мы получим позже. Нашей категории это не нужно, поэтому устанавливаем в false.
  • xPDOTransport::UPDATE_OBJECT => true — это говорит MODX, что если категория уже существует, нужно обновить её нашей версией. Если установить в false, MODX просто пропустит категорию, если найдет её. Мы хотим чтобы категория обновилась.
  • xPDOTransport::RELATED_OBJECTS => true — это указывает связанные объекты (указываем объект сниппета). Наш случай хороший пример. Любые сниппеты, которые будут установлены, будут помещены в категорию.
  • xPDOTransport::RELATED_OBJECT_ATTRIBUTES — Это ассоциативный массив с атрибутами связанных объектов. В нашем случае это только сниппет, но это могут быть плагины, TV-параметры (дополнительные поля), чанки и т.д.

Задаем свойства объекту сниппета:

'Snippets' => array(
   xPDOTransport::PRESERVE_KEYS => false,
   xPDOTransport::UPDATE_OBJECT => true,
   xPDOTransport::UNIQUE_KEY => 'name',
),

Здесь мы говорим, что сохранять первичный ключ не требуется (аналогично категории). Затем мы хотим обновить объект, если он уже существует. И, наконец, мы говорим MODX, что поле «name» является первичным ключем.

Далее делаем так:

$vehicle = $builder->createVehicle($category,$attr);
$builder->putVehicle($vehicle);

Это упаковывает наш объект категории в небольшое транспортное средство с атрибутами, которые мы только что определили. Это и добавляет его в транспортный пакет. Готово! Наша категория упакована. Теперь добавим к ней сниппет.

Добавление сниппета

Идем дальше и создаем папку /www/doodles/_build/data/. Теперь создаем в ней файл /www/doodles/_build/data/transport.snippets.php. Поместите в него такой код:

<?php
function getSnippetContent($filename) {
    $o = file_get_contents($filename);
    $o = trim(str_replace(array('<?php','?>'),'',$o));
    return $o;
}
$snippets = array();
 
$snippets[1]= $modx->newObject('modSnippet');
$snippets[1]->fromArray(array(
    'id' => 1,
    'name' => 'Doodles',
    'description' => 'Displays a list of Doodles.',
    'snippet' => getSnippetContent($sources['elements'].'snippets/snippet.doodles.php'),
),'',true,true);
$properties = include $sources['data'].'properties/properties.doodles.php';
$snippets[1]->setProperties($properties);
unset($properties);
 
return $snippets;

Во-первых мы создали небольшой вспомогательный метод, который будет захватывать наши куски кода из файлов и убирать из него теги «<?php». Затем мы создаем объект сниппета. Помните: не нужно сохранять, только создаем. Настало время вернуться к массиву $snippets. Помните закомментированнную часть из файла build.transport.php? Вот эта часть:

/* добавляем сниппет */
$modx->log(modX::LOG_LEVEL_INFO,'Packaging in snippets...');
$snippets = include $sources['data'].'transport.snippets.php';
if (empty($snippets)) $modx->log(modX::LOG_LEVEL_ERROR,'Could not package in snippets.');
$category->addMany($snippets);

Убираем комментирование. Теперь наш сниппет загружается в транспортное средство категории. Теперь добавим свойства, которые мы упоминали ранее.

Добавление свойств сниппета

Создайте файл /www/doodles/_build/data/properties/properties.doodles.php с таким содержанием:

<?php
$properties = array(
    array(
        'name' => 'tpl',
        'desc' => 'prop_doodles.tpl_desc',
        'type' => 'textfield',
        'options' => '',
        'value' => 'rowTpl',
        'lexicon' => 'doodles:properties',
    ),
    array(
        'name' => 'sort',
        'desc' => 'prop_doodles.sort_desc',
        'type' => 'textfield',
        'options' => '',
        'value' => 'name',
        'lexicon' => 'doodles:properties',
    ),
    array(
        'name' => 'dir',
        'desc' => 'prop_doodles.dir_desc',
        'type' => 'list',
        'options' => array(
            array('text' => 'prop_doodles.ascending','value' => 'ASC'),
            array('text' => 'prop_doodles.descending','value' => 'DESC'),
        ),
        'value' => 'DESC',
        'lexicon' => 'doodles:properties',
    ),
);
return $properties;

Это PHP-представление свойств (параметров) сниппета по умолчанию. Давайте рассмотрим все его ключи:

  • name — имя сниппета. Именно это имя указывается в вызове:
    [[Doodles? &tpl=`rowTpl`]]

  • desc — описание сниппета.
  • type — это ‘xtype’ поля свойства. В настоящее время доступны 4 типа: «textfield» (текстовое поле), «textarea», «combo-boolean» (выпадающий список «Да/Нет») и «list» (список значений).
  • options — используется только для списка значений. Это массив массивов. Каждый из них имеет два значений: ‘text’ (текст) и ‘value’ (значение). Текст выводится пользователю в списке, а значение сохраняется в БД. Текст может быть ключем из лексикона.
  • value — значение свойства по умолчанию.
  • lexicon — При желании, свойства могут быть i18n-совместимыми. Просто укажите название лексикона и MODX сделает остальное.

Итак, у нас есть свойства. Но как вы видите мы сделали ссылку на новый раздел лексикона «doodles:properties». Давайте создадим файл лексикона /www/doodles/core/components/doodles/lexicon/en/properties.inc.php с таким содержанием:

<?php
$_lang['prop_doodles.ascending'] = 'Ascending';
$_lang['prop_doodles.descending'] = 'Descending';
$_lang['prop_doodles.dir_desc'] = 'The direction to sort by.';
$_lang['prop_doodles.sort_desc'] = 'The field to sort by.';
$_lang['prop_doodles.tpl_desc'] = 'The chunk for displaying each row.';

Как вы можете видеть тут содержание подобно разделу «default».

Если вы запустите скрипт сейчас, то наша категория и сниппет со своими свойствами будет упакован в какет. Отлично! Но мы пропустили сами файлы нашего дополнения. Давайте исправим это.

Добавление файловых резольверов (Resolvers)

Давайте добавим в пакет папки с файлами /www/doodles/core/components/doodles/ и /www/doodles/assets/components/doodles/ нашего дополнения. Мы добавим файлы в наше транспортное средство категории с помощью т.н. файловых резольверов.

Итак, в build.transport.php сразу после добавления транспортного средства категории:

$vehicle = $builder->createVehicle($category,$attr);

добавим это:

$modx->log(modX::LOG_LEVEL_INFO,'Adding file resolvers to category...');
$vehicle->resolve('file',array(
    'source' => $sources['source_assets'],
    'target' => "return MODX_ASSETS_PATH . 'components/';",
));
$vehicle->resolve('file',array(
    'source' => $sources['source_core'],
    'target' => "return MODX_CORE_PATH . 'components/';",
));

Стоит разобрать два атрибута:

source — это путь, по которому можно найти файлы. Используем наши source_assets и source_core, которые были определены нами ранее.

target — это eval-строка, которая возвращает путь где будут находиться файлы нашего дополнения.

Первый параметр в resolve() говорит MODX, что это файловый резольвер. Мы рассмотрим подробнее резольверы позже в этом уроке.

Если вы запустите упаковщик сейчас, он упакует папки doodles/core/ и doodles/assets/.

Добавление пункта меню и действия

Теперь давайте добавим пункт меню и действие для страницы компонента, которую мы сделали ранее

Добавим такой код:

$modx->log(modX::LOG_LEVEL_INFO,'Packaging in menu...');
$menu = include $sources['data'].'transport.menu.php';
if (empty($menu)) $modx->log(modX::LOG_LEVEL_ERROR,'Could not package in menu.');
$vehicle= $builder->createVehicle($menu,array (
    xPDOTransport::PRESERVE_KEYS => true,
    xPDOTransport::UPDATE_OBJECT => true,
    xPDOTransport::UNIQUE_KEY => 'text',
    xPDOTransport::RELATED_OBJECTS => true,
    xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array (
        'Action' => array (
            xPDOTransport::PRESERVE_KEYS => false,
            xPDOTransport::UPDATE_OBJECT => true,
            xPDOTransport::UNIQUE_KEY => array ('namespace','controller'),
        ),
    ),
));
$modx->log(modX::LOG_LEVEL_INFO,'Adding in PHP resolvers...');
$builder->putVehicle($vehicle);
unset($vehicle,$menu);

Тут всё аналогично транстпортному средству (vehicle) категории. Создается объект меню и связанные объект действия.

  • PRESERVE_KEYS установлено в true, т.к. меню имеют уникальные ключи и мы хотим сохранить ключ нашего пункта меню.
  • UNIQUE_KEY связанного объекта действия является массивом. Это говорит MODX, что нужно искать объект modAction, который имеет пространство имен ‘namespace’ => ‘doodles’ и контроллер ‘controllers/index’.

Как вы, наверное, догадались, мы должны добавить файл transport.menu.php. Создадим его /www/doodles/_build/data/transport.menu.php:

<?php
$action= $modx->newObject('modAction');
$action->fromArray(array(
    'id' => 1,
    'namespace' => 'doodles',
    'parent' => 0,
    'controller' => 'controllers/index',
    'haslayout' => true,
    'lang_topics' => 'doodles:default',
    'assets' => '',
),'',true,true);
 
$menu= $modx->newObject('modMenu');
$menu->fromArray(array(
    'text' => 'doodles',
    'parent' => 'components',
    'description' => 'doodles.desc',
    'icon' => 'images/icons/plugin.gif',
    'menuindex' => 0,
    'params' => '',
    'handler' => '',
),'',true,true);
$menu->addOne($action);
unset($menus);
 
return $menu;

Тут всё аналогично transport.snippets.php, за исключением того, что вызвали метод addOne() объекта menu. Обратите внимание, что все элементы массива fromArray() соответствуют полям в таблицах БД.

Итак, пункт меню и действие упакованы.

Добавление резольвера

Когда мы установим наше дополнение в системе, мы столкнемся с одной проблемой — таблицы БД modx_doodles не будет существовать. Давайте напишем PHP резольвер, который будет запускаться после транстпортного средства. Добавим этот резольвер к нашему транспортному средству меню. Сразу после $vehicle = $builder->createVehicle($menu) добавим такой код:

$modx->log(modX::LOG_LEVEL_INFO,'Adding in PHP resolvers...');
$vehicle->resolve('php',array(
    'source' => $sources['resolvers'] . 'resolve.tables.php',
));

Создадим файл /www/doodles/_build/resolvers/resolve.tables.php с таким содержанием:

<?php
if ($object->xpdo) {
    switch ($options[xPDOTransport::PACKAGE_ACTION]) {
        case xPDOTransport::ACTION_INSTALL:
            $modx =& $object->xpdo;
            $modelPath = $modx->getOption('doodles.core_path',null,$modx->getOption('core_path').'components/doodles/').'model/';
            $modx->addPackage('doodles',$modelPath);
 
            $manager = $modx->getManager();
 
            $manager->createObjectContainer('Doodle');
 
            break;
        case xPDOTransport::ACTION_UPGRADE:
            break;
    }
}
return true;

Отлично. Думаю тут всё понятно. Мы имеем конструкцию switch, благодаря которой можем выполнять задачи в зависимости от текущего действия. Указываем путь до нашей модели и вызываем метод addPackage(), который добавляет нашу xpdo схему (помните из первого урока?). Наконец мы запускаем $modx->getManager() и далее $manager->createObjectContainer(‘Doodle’). Этот метод дает MODX команду запустить SQL и создать таблицу в БД для нашего класса Doodle. Теперь можно убрать проверку на существование таблицы БД, как мы сделали в первой части (использование резольвера не обязательно, но это удобно). И в конце мы вернем true, чтобы MODX знал, что всё прошло гладко.

Теперь при установке пакета будет создаваться таблица нашего дополнения в БД.

Добавление файлов cangelog, readme, лицензии и параметров установки

Давайте создадим файл readme.txt в папке docs/ с таким содержинием:

--------------------
Extra: Doodles
--------------------
Version: 1.0
 
A simple demo extra for creating robust 3rd-Party Components in MODx Revolution.

Также создайте файлы license.txt (содержит описание лицензии) и changelog.txt (лог изменений), если их ещё нет.

Теперь давайте вернемся в скрипт build.transport.php и перед $builder->pack() добавим такие строки:

$modx->log(modX::LOG_LEVEL_INFO,'Adding package attributes and setup options...');
$builder->setPackageAttributes(array(
    'license' => file_get_contents($sources['docs'] . 'license.txt'),
    'readme' => file_get_contents($sources['docs'] . 'readme.txt'),
    'changelog' => file_get_contents($sources['docs'] . 'changelog.txt'),
    'setup-options' => array(
        'source' => $sources['build'].'setup.options.php',
    ),
));

Как видите, вызывается метод setPackageAttributes(), который устанавливает атрибуты нашему упаковщику. Также тут есть новый для нас массив ‘setup-options’. У этого массива есть элемент с ключем ‘source’ — путь до PHP файла (подобно резольверу).

Создадим файл /www/doodles/_build/setup.options.php с таким содержинием:

<?php
$output = '';
switch ($options[xPDOTransport::PACKAGE_ACTION]) {
    case xPDOTransport::ACTION_INSTALL:
        $output = '<h2>Doodles Installer</h2>
<p>Thanks for installing Doodles! Please review the setup options below before proceeding.</p><br />';
        break;
    case xPDOTransport::ACTION_UPGRADE:
    case xPDOTransport::ACTION_UNINSTALL:
        break;
}
return $output;

Знакомо выглядит, да? Этот кусок кода позволяет нам вывести «Параметры установки», когда пользователь будет устанавливать пакет. Сейчас мы только выводим сообщение, чтобы сказать людям «Спасибо» за установку нашего дополнения.

Здесь можно добавить элементы формы, которые будут выводиться при установке пакета и далее обрабатываться установщиком. Пример можно увидеть у компонента Quip: github.com/splittingred/Quip/blob/develop/_build/resolvers/setupoptions.resolver.php.

На этом всё. Запустите упаковщик (http ://localhost/doodles/_build/build.transport.php) и в папке core/packages/ появится файл транспортного пакета «doodles-1.0-rc1.zip». Этот файл можно загрузить в репозиторий дополнений MODX и потом можно будет его установить через «Управление пакетами».

Все файлы, созданного нами, упаковщика можно найти здесь: github.com/splittingred/Doodles/tree/develop/_build.

UPD: так как изначальная идея компонента в курсе изменилась, мы с вами будем реализовывать компонент под названием DARTSocials, где будем управлять списком ссылок на чаты в соц. сетях.

Приступим к настройке нашего рабочего места. При разработке компонента я использую IDE phpStorm от JetBrains. Данная программа платная, но у нее есть бесплатный демонстрационный период в размере 30 дней, чего нам должно хватить. Думаю, что с установкой данной IDE у вас проблем не возникнет.

Открываем программу и создаем новый проект SupplyManager.

Введение и настройка рабочего места

Далее нам необходимо создать сайт на любом из хостингов (я советую Timeweb, у них есть еще бесплатный период в 10 дней, да и хостинг не особо дорогой, нам подойдет самый дешевый тариф). В общем, создаем сайт и устанавливаем туда чистый MODX (скачиваем версию Advanced).

Когда мы установили MODX, то нам нужно настроить выгрузку нашего кода на сервер.

Введение и настройка рабочего места

Настраиваем наше подключение.

Введение и настройка рабочего места

И на вкладке Mappings, настраиваем куда именно будут заливаться файлы. Я создаю для этого папку extras.

Введение и настройка рабочего места

Далее нам нужно настроить Git (конечно, также стоит зарегистрироваться на Github) у себя на компьютере. Для этого необходимо скачать набор утилит git с официального сайта. Когда мы установим данную утилиту, то уже можно работать с git. Но нам нужно еще связать это дело с phpStorm. Для этого нам нужно перейти в раздел File -> Settings -> Version Control -> Git и настроить все примерно так:

Введение и настройка рабочего места

Все! Теперь мы можем подготовить наш проект для разработки. Для того, чтобы избежать рутинного начала у нас есть замечательное дополнение modExtra. Поэтому мы открываем консоль в phpStorm и пишем команду:


    git clone https://github.com/mot9igit/modExtra.git

Введение и настройка рабочего места

И у нас появится содержимое данного репозитория в проекте. По поводу назначения тех или иных папок мы с вами поговорим в следующей статье.  

Введение и настройка рабочего места

Замечательно! Сейчас мы можем переименовать наш проект с помощью скрипта rename_it.php. Но для начала нам необходимо загрузить все на сервер. Для этого кликаем правой кнопкой мыши по проекту и выбираем Deployment -> Upload to SupplyManager.

Введение и настройка рабочего места

Если вы все сделали верно, то у вас содержимое проекта загрузится на сервер. Проверить вы это сможете через FTP клиент, панель управления хостингом или админку MODX.

Введение и настройка рабочего места

Для переименования нашего проекта нужно перейти в адресной строке по URL: http://ваш.сайт/extras/modExtra/rename_it.php?name=нименования компонента латинницей. В моем случае ссылка приняла следующий вид — http://test7.dart.agency/extras/modExtra/rename_it.php?name=SupplyManager . Если переименование пройдет успешно на экране вы увидите строки с задействованными файлами. Отлично! Теперь папку с modExtra мы можем удалить и скачать наш новый дистрибутив с сервера предварительно немного изменив Mappings.

Тогда у нас получится красивый проект, который мы можем закоммитить на GitHub.

Чтобы сделать первый коммит нам необходимо создать репозиторий на GitHub. После создания репозитория GitHub нам подскажет как связать его с уже существующим.

Введение и настройка рабочего места

Выполняем данные команды в терминале phpStorm. Commit лучше делать через phpStorm, там мы можем отметить все файлы, которые попадут в коммит.

Введение и настройка рабочего места

Отлично! У нас с вами все получилось. Результаты данной статьи вы можете посмотреть в этом коммите. На этом мы закончили все подготовительные работы, я вас поздравляю!

Петропавловский Артем

Петропавловский Артем

автор

Основатель Dart Agency, web-разработчик, блоггер.

Как написать плагин для MODX Revolution, который помимо всего прочего будет нести некоторую пользу.

Пусть при сохранении новой записи будет автоматически сформировано значение стандартного поля

[[*description]]

. Формироваться оно будет на основе основного содержимого материала

[[*content]]

— контента. Только при том условии, что из последнего будут отобраны первые 160 символов, исключая HTML разметку. Итак, начнём.

  1. Авторизовались, OK. Переключаемся в левой колонке на вкладку «Элементы».
  2. Находим пункт плагины. Кликаем правой кнопкой мышки, выбирая пункт «Новый плагин».
  3. В поле Имя вписываем наименование нашего творения символами латинского алфавита. Я назвал AutoDescription.
  4. По желанию напечатываем краткое описание (для себя любимого разумеется) на кириллице.
  5. А теперь внимание, программный код займёт всего 4(!) строчки:
<?php
$content = $resource->get('content'); /* Вытягиваем контент. */
$content = mb_substr(strip_tags($content), 0, 160);
$resource->set('description', $content); /* Устанавливаем значение Описания. */
  1. Третью строку поясню отдельно — тут всего лишь штатный PHP, не имеющий значения к API MODX Revolution. Просто косит все HTML-теги, выбирая первые 160 символов от исходной строки $content.
  2. Завершающим шагом будет проставление галочки на вкладке системных событий. Ставим на OnBeforeDocFormSave:

Далее вам остаётся только проверить получившееся решение в действии.

Рассылка по расписанию

Последнее занятие будет самым коротким. Неожиданно обнаружилось, что мы уже всё сделали, и осталось только добавить скрипт отправки писем по расписанию.

На всякий случай напоминаю алгоритм работы компонента:

  1. Создаём рассылку и указываём ей свойства. Обязательно указать шаблон или сниппет.

  2. Добавляем пользователей (или они добавляются самостоятельно, через сайт)

  3. На странице очереди писем добавляем новые, путем выбора рассылки. В зависимости от того, что в ней указано, текст письма генерируется сниппетом или шаблоном.

  4. Отправляем письма. Можно вручную, из админки, или скриптом, по расписанию.

Всё, кроме скрипта мы уже сделали.

Читать далее

Самостоятельная подписка и отписка пользователя

Мы плавно подходим к окончанию разработки компонента и проведения уроков. Сегодня не будет ничего нового просто делаем самостоятельную подписку и отписку пользователя.

Для этого нужно будет добавить новое поле code в объект sxSubscriber (для ссылки «отписаться от рассылки»), прописать в классе sxNewsletter новые методы для проверки почты и подпискиотписки и добавить обработку этих действий в сниппет Sendex.

В общем, ничего интересного, обычное программирование на PHP.

Читать далее

Сниппет Sendex и формы подписки\отписки

На прошлых занятиях мы закончили написание административного интерфейса нашего компонента и теперь переходим на фронтенд.

Нам нужено организовать возможно самостоятельной работы авторизованным пользователям с подписками. Определяем минимальный функционал:

  1. Вывод формы подписки на определенную рассылку — её мы укажем по id

  2. Если юзер уже подписан — тогда показываем форму отписки

  3. При том и другом действие происходит отправка писем с кодом, для подтверждения

  4. При переходе по коду, его ловит плагин и выполняет что нужно

Для сниппета мы будем использовать pdoTools. Во-первых, это быстро и удобно, а во вторых велика вероятность, что он уже установлен на сайте. Если же нет — нужно предусмотреть и автоматическую установку, при инсталляции пакета.

Читать далее

Пишем интерфейс: таблица очереди писем

На этом уроке мы закрепляем работу с ExtJS. Здесь не будет ничего нового, мы рисуем очередную таблицу и задаём для неё процессоры.

Логика работы такая:

  1. У нас есть рассылка

  2. К ней прикреплены подписчики

  3. Нужно взять рассылку, сгенерировать письмо и поставить в очередь

  4. Один подписчик — одно письмо для каждой рассылки

  5. После создания письма его можно удалить или отправить

В итоге у меня получилась вот такая таблица:

Читать далее

Пишем интерфейс: окно редактирования подписки

На прошлом занятии мы сделали таблицу подписок и окно создания.

Теперь нам нужно добавить окно изменения подписки, и сложность здесь в управлении подписчиками — нам нужно окно с вкладками.

На первой те же поля, что и при создании, а на второй — добавление и удаление пользователей.

Для работы с пользователями, нужно написать свои процессоры: выборка, поиск и добавлениеудаление к подписке.

Читать далее

Пишем интерфейс: таблица подписок и окошко создания

В принципе, всё необходимое для написания приличного дополнения к MODX я уже рассказал.

Мы знаем структуру компонента, умеем собирать его в пакет, управляем контроллерами и меняем интерфейс. Даже немного научились работать с GitHub. Дело за малым — собственно написать функционал.

Этот урок очень объёмный, здесь много листингов кода, с пояснениями. Если что-то непонятно — не нужно переживать и расстраиваться, просто помните, что всегда можно посмотреть исходный код уже готовых дополнений и самого MODX — там есть примеры на все случаи жизни.

Итак, сегодня нам нужно улучшить в админке таблицу и всплывающее окно для создния подписки. Вот, что у нас получится в итоге

Читать далее

Пишем интерфейс: виджеты ExtJS и процессоры

На прошлом уроке мы разобрались с контроллерами Custom Manager Page (CMP) нашего компонента, и выяснили, что основной смысл их существования — подготовить все нужные файлы для вывода страницы.

Пока у нас используются js файлы от modExtra, и сегодня нам нужно их изучить и переписать для Sendex.

Первым делом нужно понять, что ExtJs — это javascript фреймфорк, куда более мощный чем jQuery. Он не требует HTML верстки, он все генерирует на лету. То есть, вы пишете javascript код, а на странице получаете готовые таблички, кнопочки, пагинацию и т.д.

Причем, все эти элементы уже подчиняются какой-то логике: одна кнопка переключает странички, другая вызывает модальное окно и т.д.

Сначала довольно трудно въехать в ExtJS, особенно если не знаешь javascript, но со временем начинаешь понимать его преимущества, и реально экономить время.

Читать далее

Собираем и устанавливаем первую версию пакета

На прошлом занятии мы определились с примерным функционалом, написали схему таблиц и сгенерировали модель xPDO для работы с БД MySql.

А сегодня нам нужно собрать и установить первую версию пакета и разобраться, как работают Custom Manager Pages (CMP).

Учитывая, что мы используем заготовку modExtra, и уже разобрали, как она работает — сборка пакета заключается в выполнении скрипта build.transport.php на сервере.

Если с конфиге build.config.php выставлена константа PKG_AUTO_INSTALL, то компонент будет сразу установлен на сайт.

Читать далее

Продумываем логику работы, определяем схему и модель БД

Подготовительные работы закончены, и сегодня мы приступаем непосредственно к разработке компонента.

Мы пишем компонент рассылок, поэтому нужно продумать основную логику работы. Обращаю ваше внимание на то, что наша цель — научиться писать компоненты для MODX, а не написать лучшую рассылку в мире. Поэтому прошу вас сразу поумерить амбиции и не предлагать добавить мега-функционал.

Логика работы мне видится такой:

  • У нас есть объект Рассылка — в нём всё, что нужно для формирования писем: тема, шаблон, отправитель и т.д.

  • Объект Подписчик подписывается на рассылку. Пока будем считать, что это должен быть авторизованный юзер, но в голове держим, что можно добавить и гостей.

  • При наступлении каких-то событий, вызывается объект Подписка и выполняет определённый код, который генерирует письма и сохраняет их как объект Очередь. Для большей универсальности, можно выносить этот код в отдельный сниппет.

  • Сервер выполняет скрипт рассылки и отправляет письма из Очереди по расписанию, хоть каждые 5 минут. Если очередь пуста, значит все уже отправлено.

Соответственно, в админке у нас будет страница управления подписками, добавление к ним подписчиков, и просмотр очереди сообщений, с возможность что-то удалить или отправить «прямо сейчас». Возможно, еще добавим страницу проверки отправки сообщений, чтобы слать себе тестовые письма для отладки.

С функционалом примерно определились, теперь нужно написать схему БД, чтобы хранить наши данные.

Читать далее

Основы Git и первый коммит компонента на Github

Заканчиваем с подготовкой к началу активной разработки.

Сегодня нам нужно выгрузить заготовку на сервер, переименовать, создать репозиторий на GitHub и отправить в него первый коммит. А для этого нужно будет настроить PhpStorm на работу с git.

Синхронизация с сервером

После того, как мы связали наш локальный проект с удалённым сервером, в PhpStorm появились новые пункты в контектном меню:

  • Выгрузить файлы на сервер

  • Скачать файлы с сервера

  • Синхронизировать удалённые и локальные файлы

Важно понимать, что первый и второй пункт могут перезаписывать существующие файлы, поэтому использовать их нужно очень аккуратно.

В основном, лучше использовать синхронизацию — она покажет различия в файлах и позволит выбрать действия с ними. Правда есть и недостаток — при синхронизации нужно прочитать все удаленные файлы, а это занимает время. Хорошо, что PhpStorm позволяет синхронизировать не только весь проект, но и каждую отдельную директорию, и даже файл.

Читать далее

Разбор структуры компонента, зачем нужны assets, core и _build

Все приличные дополнения в MODX распространяются транспортными пакетами — это такие zip файлы с определенной структурой.

При установке они могут совершать различные действия: создавать таблицы, менять системные настройки, копировать файлы и т.п.

Писать транспортный пакет с нуля очень долго, муторно и чревато ошибками. Гораздо лучше использовать проверенную заготовку modExtra — именно с её помощью написаны почти все мои дополнения.

Поэтому, сегодня нам нужно скачать modExtra из репозитория и разобрать структуру будущего компонента: зачем там так много файлов и директорий?

Конечно, мы разберемся и со сборщиком пакета — как он работает и конфигурируется.

Читать далее

Настраиваем рабочее место: MODXCloud + PhpStorm

Для комфротной разработки нам нужно хорошее окружение. Лично я использую локальный веб-сервер Nginx + Php5-fpm + Mysql на Mac Os X, но это далеко не обычная конфигурация.

Гораздо проще и доступнее использовать любой хостинг с доступом к сайту по SFTP. Неважно, какой именно: shared, vps или cloud.

Конкретно для нашей задачи, чтобы сделать рабочее окружение максимально одинаковым и доступным для всех участников обучения, мы будем использовать бесплатный аккаунт на MODXCloud и 30 дневную пробную версию IDE PhpStorm.

Читать далее

Понравилась статья? Поделить с друзьями:
  • Как написать плагин для minecraft на python
  • Как написать плагин для minecraft на java
  • Как написать плагин для kodi
  • Как написать плагин для intellij idea
  • Как написать плагин для cinema 4d