Внимание! Статья была актуальна до запуска программы Marketplace и на данный момент представляет исторический интерес. Узнать о том как создавать свой модуль в текущей версии Bitrix Framework можно в учебном курсе Разработчик Bitrix Framework.
Автор: Сергей Востриков
Зачем я везде расписывался?! Сжечь! Сжечь!
Бургомистр /Убить Дракона/
Эта статья предназначена для тех разработчиков сайтов, кто уже работает с системой «Битрикс: Управление сайтом» (в дальнейшем, БУС). Огромный набор функциональных возможностей системы зачастую покрывает 95% всех задач, которые возникают у заказчика обычного корпоративного сайта. Эти вопросы решаются написанием специфических шаблонов для инфоблоков, меню или строки навигации и, в целом, не требуют серьезного программирования на PHP.
Оставшиеся 5% задач состоят из задач автоматизации той или иной деятельности, которую заказчик хочет видеть на своем сайте. Очевидно, что компании, имеющие в штате PHP-программистов, так или иначе, справляются с задачей, однако само наличие БУС создает у разработчиков естественное желание написать автоматизацию «интегрированном с Битрикс виде». Под интеграцией в таких случаях, как правило, понимается требование, чтобы «самописная» часть «смотрелась» частью общей системы, не мешала получению автоматических обновлений через SiteUpdate, настраивалась через раздел Администрирования и так далее. Иными словами, хочется, чтобы «доработки» были сделаны в виде полноценного модуля БУС.
В нашей статье мы покажем, каким образом описывать модуль, чтобы система работала с ним, как с «родным». Расскажем, как разработать класс модуля и управлять опциями при помощи функций ядра системы. Поясним, как правильно располагать файлы с текстовыми константами, описывать компоненты для визуального HTML-редактора страницы и генерировать почтовые нотификации на определенные события.
Комментарий Юрия Тушинского, технического директора компании «Битрикс»: «Статья подробно описывает процесс создания нового модуля, и она, несомненно, будет полезна в качестве полноценного примера для тех, кто собирается написать свой модуль. Но, на мой взгляд, с практической точки зрения не совсем удачно выбран предмет для нового модуля, такая надстройка над модулем информационных блоков намного проще реализуется, если написать свой компонент». | ||
Комментарий Алексея Кирсанова, ведущего разработчика компании «Битрикс»: Перед реализацией собственного модуля обязательно изучите раздел системы помощи Создание нового модуля. В этом разделе описаны ключевые моменты, которые необходимы для правильного функционирования модулей системы. | ||
Прежде чем приступить к особенностям реализации модуля, надо решить, а что мы, собственно, хотим получить в результате. В качестве примера приближенного к реальности мы решили попробовать создать простую гостевую книгу. Она должна быть доступна в виде компонентов в HTML-редакторе, позволять регулировать права доступа к записям книги при помощи специальных ролей (как это сделано в модуле Поддержка, например) и генерировать почтовые нотификации на такое основное событие как создание записи в гостевой книге. Данная задача является простой, но в тоже время позволит нам затронуть практически все вопросы создания собственного модуля в БУС.
Если бы мы решили делать гостевую книгу с нуля, то очевидным решением было бы создание пары таблиц, которые позволили бы описать связи между некими сущностями «Гостевая книга» и «Запись в гостевую книгу». Мы бы вооружились MySQL (или Oracle, раз уж БУС нам это позволяет) и начали описывать таблицы, придумывать запросы и делать все то, чем обычно занимаются разработчики сайтов «на коленках». Но мы-то уже не они, не правда ли? У нас уже есть прекрасная основа для моделирования очень широкого спектра данных, с которыми потом будет работать сайт. Чудо называется - «Информационные блоки», и этот модуль, как мы знаем, доступен даже в самой минимальной конфигурации БУС: редакции Старт. Давайте воспользуемся этим прекрасным механизмом, тем более что для него существует масса готового кода в разделе «Администрирование», который мы сможем позаимствовать для своих целей. Помимо всего прочего, мы сможем работать на уровне Битрикс API, что уже само по себе приятно.
Начнем с главного вопроса: что надо сделать, чтобы создать свой модуль в БУС? Как это ни странно будет звучать, но практически ничего. Посмотрев раздел «Управление модулями» в Битрикс SDK мы обнаружим, что формально для описания своего модуля нам понадобится всего три файла:
«/include.php»
«/options.php»
«/install/index.php»
Не так много работы, верно? Тем не менее, в этой статье мы постараемся дать более широкие требования к оформлению модуля. Во-первых, это даст более точное представление о том, что нам придется каждый раз делать при создании своих модулей, а во-вторых, позволит делать эти модули максимально похожими на стандартные, что упростит потом их поддержку и развитие. Забегая вперед, мы можем показать дерево каталогов внутри нашего модуля гостевой книги:
Рис. 1. Дерево каталога модуля гостевой книги.
Коротко опишем, для чего понадобятся те, или иные каталоги.
«/classes/general» понадобится для описания класса CIX_Guestbook, который будет регулировать роли пользователей.
Каталог «/install» понадобится нам для описания класса ix_guestbook, который как раз и будет заниматься регистрацией модуля в БУС. Он же зарегистрирует шаблоны для почтовых нотификаций, описанных в разделе «/install/events/».
В каталоге «/install/templates» мы опишем компоненты для использования нашей гостевой книги в визуальном HTML-редакторе.
Особое внимание обратим на каталог «/install/lang». Обычно возникают определенные трудности с пониманием, куда и какие константы класть. Например, если мы хотим, чтобы русские константы были доступны внутри файла
«/bitrix/modules/ix_guestbook/install/templates/ix_guestbook/index.php»
надо было создать файл
«/bitrix/modules/ix_guestbook/install/templates/lang/ru/ix_guestbook/index.php».
Для создателей БУС это вполне очевидно, а для нас, увы, оказалось нет. Теперь перейдем к подробностям.
Опишем файл «/ix_guestbook/include.php»:
<? require_once ($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/ix_guestbook/classes/general/ix_guestbook.php"); ?>
Это описание позволит в дальнейшем автоматически использовать собственный класс с какой-то функциональностью в программном коде, если в публичной части сайта написать что-то вроде
if (CModule::IncludeModule(“ix_guestbook”)) …
Мелочь, а приятно. На реальном содержимом ix_guestbook.php пока подробно останавливаться не будем.
Пусть там будет некая пустышка, необходимая лишь в качестве заготовки:
<? class CIX_Guestbook { function Dummy() { return; } } ?>
Теперь переходим к файлу «/ix_guestbook/install/index.php».
Это основной файл для регистрации собственного модуля. Приведем его код целиком:
<? global $MESS; $strPath2Lang = str_replace("\\", "/", __FILE__); $strPath2Lang = substr($strPath2Lang, 0, strlen($strPath2Lang)-strlen("/install/index.php")); include(GetLangFileName($strPath2Lang."/lang/", "/install/index.php")); include($strPath2Lang."/install/version.php"); Class ix_guestbook extends CModule { var $MODULE_ID = "ix_guestbook"; var $MODULE_VERSION; var $MODULE_VERSION_DATE; var $MODULE_NAME; var $MODULE_DESCRIPTION; function ix_guestbook() { $this->MODULE_VERSION = IX_GB_VERSION; $this->MODULE_VERSION_DATE = IX_GB_VERSION_DATE; $this->MODULE_NAME = GetMessage("IX_GB_MODULE_NAME"); $this->MODULE_DESCRIPTION = GetMessage("IX_GB_MODULE_DESC"); } function DoInstall() { RegisterModule("ix_guestbook"); } function DoUninstall() { UnRegisterModule("ix_guestbook"); } } ?>
Код подключения текстовых констант целиком заимствован из существующих модулей БУС, и мы бы рекомендовали использовать его и для ваших модулей. Сами константы необходимо положить в каталоги, привязанные к конкретным языкам системы по следующему шаблону:
«/[module_name]/lang/[language]/install/index.php»
То есть, для русских констант путь к файлу будет таким:
«/ix_guestbook/lang/ru/install/index.php».
Содержимое файла включает в себя всего пять строковых констант:
<? $MESS ['IX_GB_MODULE_NAME'] = "Гостевые книги"; $MESS ['IX_GB_MODULE_DESC'] = "Модуль для создания гостевых книг"; $MESS ['IX_GB_DENIED'] = "Доступ закрыт"; $MESS ['IX_GB_ADD_RECORD'] = "Посетитель"; $MESS ['IX_GB_MODERATOR'] = "Модератор"; ? >
Обратите внимание на «/ix_guestbook/install/version.php»:
<? define("IX_GB_VERSION","1.0.0"); define("IX_GB_VERSION_DATE","2005-02-25 16:00:00"); ?>
Этот файл подключается строчкой include($strPath2Lang."/install/version.php") и позволяет нам удобно менять номер версии модуля в случае дальнейших обновлений.
В файле «/ix_guestbook/install/index.php» описывается один единственный класс,название которого должно совпадать с названием модуля. Этот класс обязан иметь конструктор (в нашем случае, function ix_guestbook()), в котором присваиваются такие основные параметры модуля, как его название, описание, номер и дата версии. Помимо этого, класс модуля обязан иметь два метода: DoInstall и DoUninstall. Полагаем, что их смысл очевиден – они подключают и отключают модуль в ядре БУС.
На этом приготовления модуля, в общем-то, заканчиваются. Если мы зайдем в список доступных модулей в разделе «Администрирование», то обнаружим, что нам уже предлагают установить наш модуль в системе.
Рис. 2. Список модулей системы.
Любой серьезный модуль имеет пользовательские настройки. Например, для гостевой книги мы решили, что ее необходимо (или нет) модерировать, что мы можем разрешить или запретить HTML в сообщениях от пользователей, и что мы предоставим разработчикам возможность указывать тип сообщений (text или html). Для описания наших опций мы должны создать файл «/ix_guestbook/options.php»:
<? global $MESS; include(GetLangFileName($GLOBALS["DOCUMENT_ROOT"]."/bitrix/modules/ix_guestbook/lang/", "/options.php")); $module_id = "ix_guestbook"; CModule::IncludeModule($module_id); $MOD_RIGHT = $APPLICATION->GetGroupRight($module_id); if($MOD_RIGHT>="R") $arAllOptions = Array( Array("ix_gb_moderate", GetMessage("IX_GB_MODERATE"), "Y", Array("checkbox", "Y")), Array("ix_gb_html", GetMessage("IX_GB_HTML"), "N", Array("checkbox", "N")), Array("ix_gb_html_default", GetMessage("IX_GB_DEFAULT_HTML"), "N", Array("checkbox", "N")) ); if($MOD_RIGHT>="W"): if ($REQUEST_METHOD=="GET" && strlen($RestoreDefaults)>0) { COption::RemoveOption($module_id); reset($arGROUPS); while(list(,$value)=each($arGROUPS)) $APPLICATION->DelGroupRight($module_id, array($value["ID"])); } if($REQUEST_METHOD=="POST" && strlen($Update)>0) { for($i=0; $i<count($arAllOptions); $i++) { $name=$arAllOptions[$i][0]; $val=$$name; if($arAllOptions[$i][3][0]=="checkbox" && $val!="Y") $val="N"; COption::SetOptionString($module_id, $name, $val, $arAllOptions[$i][1]); } } endif; //if($MOD_RIGHT>="W"): ?> <form method="POST" action="<?echo $APPLICATION->GetCurPage()?>?mid=<?=htmlspecialchars($mid)?>&lang=<?echo LANG?>"> <table border="0" cellspacing="1" cellpadding="3" class="edittable"> <? for($i=0; $i<count($arAllOptions); $i++): $Option = $arAllOptions[$i]; $val = COption::GetOptionString($module_id, $Option[0], $Option[2]); $type = $Option[3]; ?> <tr> <td valign="top"><font class="tablefieldtext"><?echo $Option[1]?></font></td> <td valign="top"> <font class="tablebodytext"> <?if($type[0]=="checkbox"):?> <input type="checkbox" name="<?echo htmlspecialchars($Option[0])?>" value="Y"<?if($val=="Y")echo" checked";?>> <?elseif($type[0]=="text"):?> <input type="text" class="typeinput" size="<?echo $type[1]?>" maxlength="255" value="<?echo htmlspecialchars($val)?>" name="<?echo htmlspecialchars($Option[0])?>"> <?elseif($type[0]=="textarea"):?> <textarea rows="<?echo $type[1]?>" class="typearea" cols="<?echo $type[2]?>" name="<?echo htmlspecialchars($Option[0])?>"><? echo htmlspecialchars($val)?></textarea> <?endif?> <?if($Option[0] == "spam_rating")echo "%"?> </font> </td> </tr> <? endfor; ?> </table> <?require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/admin/group_rights.php");?> <br> <script language="JavaScript"> function RestoreDefaults() { if(confirm('<?echo AddSlashes(GetMessage("MAIN_HINT_RESTORE_DEFAULTS_WARNING"))?>')) window.location = "<?echo $APPLICATION->GetCurPage()?>?RestoreDefaults=Y&lang=<?echo LANG?>&mid=<?echo urlencode($mid)?>"; } </script> <div align="left"> <input type="submit" class="button" name="Update" <?if ($MOD_RIGHT<"W") echo "disabled" ?> value="<?echo GetMessage("IX_GB_OPTIONS_SAVE")?>"> <input type="reset" class="button" name="reset" value="<?echo GetMessage("IX_GB_OPTIONS_RESET")?>"><input type="hidden" name="Update" value="Y"> <input type="button" <?if ($MOD_RIGHT<"W") echo "disabled" ?> class="button" title="<?echo GetMessage("MAIN_HINT_RESTORE_DEFAULTS")?>" OnClick="RestoreDefaults();" value="<?echo GetMessage("MAIN_RESTORE_DEFAULTS")?>"> </div> </form> <?endif;?>
Код выглядит сложным, но на самом деле он целиком заимствован из аналогичного файла настроек какого-то стандартного модуля, и нам пришлось изменить лишь его малую часть. Сначала обратим внимание на вызов файлов со строковыми константами (это придется делать постоянно). В нашем случае, вызывается файл «ix_guestbook/lang/ru/options.php» следующего содержания:
<? $MESS ['IX_GB_MODERATE'] = "Модерировать гостевые:"; $MESS ['IX_GB_HTML'] = "Разрешить html в сообщениях"; $MESS ['IX_GB_DEFAULT_HTML'] = "Включить по умолчанию HTML в сообщениях"; $MESS ['IX_GB_OPTIONS_SAVE'] = "Сохранить"; ?>
Далее следует код обращения к конкретному модулю ix_guestbook. И самое главное, что нам понадобится сделать – это описать собственный массив $arAllOptions. Разберем лишь одну строку:
Array("ix_gb_moderate", GetMessage("IX_GB_MODERATE"), "Y", Array("checkbox", "Y")),
Первый параметр массива – это название опции. Второй – текстовое описание опции, взятое из $MESS ['IX_GB_MODERATE']. Третий параметр – это значение опции по умолчанию. Последний параметр служит для описания типа визуального элемента формы, который позволит пользователю редактировать значение опции. В нашем случае, это checkbox со значением “Y” по умолчанию. Другими вариантами могли бы быть текстовые поля (Array("text", 2)), списки значений (selectbox, пример можно посмотреть в «\modules\main\options.php»), и т.д.
Описав свои собственные опции, вставим наше название для кнопки сохранения (см. GetMessage("IX_GB_OPTIONS_SAVE") выше) – и это окажется последним, что нам необходимо сделать для подключения собственных опций модуля. Результат будет выглядеть следующим образом:
Рис. 3. Опции модуля «Гостевые книги»
БУС предоставляет широкую систему прав на модули и файлы сайта. В большинстве случаев хватает банальных разрешений типа «Чтение», «Запись», «Изменение», но как нам кажется, в бизнес моделях проще и удобнее использовать Роли. Так сделано, например, в модуле «Поддержка», где введены роли «Клиент поддержки», «Сотрудник поддержки» и «Администратор поддержки». Их смысл намного очевиднее, чем попытка распределить права на «чтение» и «запись» тикетов.
Аналогичным образом попробуем ввести роли для наших гостевых книг. Представляется удобным ввести всего три роли – «Доступ закрыт», «Пользователь» и «Модератор». Названия ролей мы уже описали в файле «ix_guestbook/lang/ru/options.php», а для подключения их в раздел настроек модуля необходимо добавить следующий метод:
function GetModuleRightList() { $arr = array( "reference_id" => array("D","R","W"), "reference" => array( GetMessage("IX_GB_DENIED"), GetMessage("IX_GB_ADD_RECORD"), GetMessage("IX_GB_MODERATOR")) ); return $arr; }
в класс ix_guestbook, описанный в файле «/ix_guestbook/install/index.php». После этих изменений можно будет указывать конкретные роли для отдельных групп пользователей в настройках модуля:
Рис. 4. Распределение прав по ролям.
Для реального использования этих ролей придется делать проверку прав пользователей в своем коде самостоятельно. Поскольку хочется свой код в дальнейшем максимально упростить, мы распишем распределение прав в классе CIX_Guestbook в файле «/ix_guestbook/classes/general/ix_guestbook.php»:
<? class CIX_Guestbook { /***************************************************************** Группа функций по работе с ролями на модуль Идентификаторы ролей: D - доступ закрыт R - пользователь W - модератор *****************************************************************/ function GetDeniedRoleID() { return "D"; } function GetUserRoleID() { return "R"; } function GetModeratorRoleID() { return "W"; } // возвращает true если заданный пользователь имеет заданную роль на модуль function HaveRole($role, $USER_ID=false) { global $DB, $USER, $APPLICATION; if (!is_object($USER)) $USER = new CUser; if ($USER_ID===false && is_object($USER)) $USER_ID = $USER->GetID(); $USER_ID=intval($USER_ID); if ($USER_ID>0) { $arrGroups = array(); $arrGroups = CUser::GetUserGroup($USER_ID); if (count($arrGroups)<=0) $arrGroups[] = 2; $arRoles = $APPLICATION->GetUserRoles("ix_guestbook", $arrGroups); if (in_array($role, $arRoles)) return true; } return false; } // true - если пользователь имеет роль "модератор" // false - в противном случае function IsModerator($USER_ID=false) { if ($USER_ID===false && is_object($USER)) { if ($USER->IsAdmin()) return true; } return CIX_Guestbook::HaveRole(CIX_Guestbook::GetModeratorRoleID(), $USER_ID); } // true - если пользователь имеет роль "пользователь" // false - в противном случае function IsUser($USER_ID=false) { return CIX_Guestbook::HaveRole(CIX_Guestbook::GetUserRoleID(), $USER_ID); } // возвращает массив ID групп для которых задана роль // $role - идентификатор роли function GetGroupsByRole($role) { global $APPLICATION, $USER; if (!is_object($USER)) $USER = new CUser; $arGroups = array(); $z = CGroup::GetList($v1, $v2, array("ACTIVE" => "Y")); while($zr = $z->Fetch()) { $arRoles = $APPLICATION->GetUserRoles("ix_guestbook", array(intval($zr["ID"])), "Y", "N"); if (in_array($role, $arRoles)) $arGroups[] = intval($zr["ID"]); } return array_unique($arGroups); } // возвращает массив групп с ролью "модератор" function GetModeratorGroups() { return CIX_Guestbook::GetGroupsByRole(CIX_Guestbook::GetModeratorRoleID()); } // возвращает массив групп с ролью "пользователь" function GetUserGroups() { return CIX_Guestbook::GetGroupsByRole(CIX_Guestbook::GetUserRoleID()); } // возвращает массив EMail адресов всех пользователей, имеющих заданную роль function GetEmailsByRole($role) { global $DB, $APPLICATION, $USER; if (!is_object($USER)) $USER = new CUser; $arrEMail = array(); $arGroups = CIX_Guestbook::GetGroupsByRole($role); if (is_array($arGroups) && count($arGroups)>0) { $rsUser = CUser::GetList($v1="id", $v2="desc", array("ACTIVE" => "Y", "GROUPS_ID" => $arGroups)); while ($arUser = $rsUser->Fetch()) $arrEMail[$arUser["EMAIL"]] = $arUser["EMAIL"]; } return array_unique($arrEMail); } // возвращает массив EMail'ов всех пользователей, имеющих роль "модератор" function GetModeratorEmails() { return CIX_Guestbook::GetEmailsByRole(CIX_Guestbook::GetModeratorRoleID()); } } ?>
Смысл кода очевиден, и для своих целей вам понадобится лишь заменить обращения к “ix_guestbook” на свои модули, а также возможно описать свои методы типа function GetModeratorRoleID(), function IsModerator($USER_ID=false), function GetModeratorGroups() и function GetModeratorEmails(). Они все реализуются по методике copy/paste, что не может не радовать.
Теперь в своем коде мы сможем реализовывать проверки прав конкретного пользователя при помощи конструкций вида:
$bModerator = (CIX_Guestbook::IsModerator()) ? "Y" : "N"; $bUser = (CIX_Guestbook::IsUser()) ? "Y" : "N"; if($bModerator!="Y" && $bUser!="Y") $APPLICATION->AuthForm(GetMessage("ACCESS_DENIED"));
Фактически, реализация модуля «Гостевые книги» уже сделана. Нам остается только подготовить компоненты для публичной части сайта, чтобы упростить использование модуля на реальном сайте.
Нам потребуются два компонента публичной части – список записей гостевой книги и форма редактирования/добавления новой записи. Начнем с простого, то есть, со списка записей. Этот компонент реализуется кодом, заимствованным из списка новостей:
<? IncludeTemplateLangFile(__FILE__); require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/ix_guestbook/include.php"); $ID = (isset($ID) ? $ID : $_REQUEST["ID"]); $IBLOCK_TYPE = (isset($IBLOCK_TYPE) ? $IBLOCK_TYPE : "guestbook"); $NEWS_COUNT = (strlen($NEWS_COUNT)>0 ? intval($NEWS_COUNT) : "20"); $SORT_BY1 = (isset($SORT_BY1) ? $SORT_BY1 : "ACTIVE_FROM"); $SORT_ORDER1 = (isset($SORT_ORDER1) ? $SORT_ORDER1 : "DESC"); $SORT_BY2 = (isset($SORT_BY2) ? $SORT_BY2 : "SORT"); $SORT_ORDER2 = (isset($SORT_ORDER2) ? $SORT_ORDER2 : "ASC"); $SORT = Array($SORT_BY1=>$SORT_ORDER1, $SORT_BY2=>$SORT_ORDER2); if(CModule::IncludeModule("iblock") && ($arIBlock = GetIBlock($ID, $IBLOCK_TYPE))): $APPLICATION->SetTitle($arIBlock["NAME"]); $APPLICATION->AddChainItem($arIBlock["NAME"]); $items = GetIBlockElementList($ID, false, $SORT, $NEWS_COUNT); $items->NavPrint(GetMessage("T_IX_GB_NAVIG")); while($obItem = $items->GetNextElement()): $arItem = $obItem->GetFields(); ?> <?echo $arItem["DATE_ACTIVE_FROM"];?> :: <b><?echo $arItem["NAME"]?></b></a><br> <?echo $arItem["PREVIEW_TEXT"];?><br> </p> <? endwhile; $items->NavPrint(GetMessage("T_IX_GB_NAVIG")); else: ShowError(GetMessage("T_IX_GB_NA")); endif; ?>
Этот код мы вставим в файле «/ix_guestbook/install/templates/ix_guestbook/index.php». Самое важное здесь – это разместить файл в каталоге «templates/ix_guestbook/», а собственно название файла не является обязательным. Данный код самым банальным образом выводит список элементов заданного инфоблока. «А мне больше и не надо!»
Второй компонент должен управлять записями гостевой книги – добавлять новые, редактировать и удалять существующие. Поскольку, как уже было сказано, наша основная методика – это copy/paste, то и сейчас нужный нам код мы возьмем готовый и лишь слегка адаптируем его под наши нужды. Нам нужна форма добавления и редактирования элемента инфоблока. Совершенно очевидно, что такой код в системе уже есть, поскольку мы можем редактировать элементы в разделе «Администрирование». Нужный нам файл мы нашли в модуле iblock: «/iblock/admin/iblock_element_edit.php».
Скопируем этот файл в наш модуль под другим названием: «/ix_guestbook/install/templates/ix_guestbook/edit_record.php». Теперь нам надо сократить его, поскольку вся функциональность нам не нужна. Что войдет в запись гостевой книги? Имя пользователя (или его псевдоним и т.д.) и текст. Проецируя это на свойства инфоблоков, становится очевидным, что нам нет нужды заводить дополнительные параметры. Для имени пользователя мы воспользуемся полем «Название» элемента инфоблока (поскольку пользователь может быть анонимом), а для текста нам хватит поля «PREVIEW_TEXT». Для модерирования мы пользуемся свойством Active, таким образом, мы избежим сложностей с использованием статусов инфоблоков в рамках документооборота.
«edit_record.php» должен реализовывать простую логику:
<? require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/iblock/iblock.php"); require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/iblock/prolog.php"); IncludeModuleLangFile(__FILE__); require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/ix_guestbook/include.php");$bModerator = (CIX_Guestbook::IsModerator()) ? "Y" : "N"; $bUser = (CIX_Guestbook::IsUser()) ? "Y" : "N";
if($bModerator!="Y" && $bUser!="Y") $APPLICATION->AuthForm(GetMessage("ACCESS_DENIED"));
$strWarning = ""; $ID = IntVal($ID); //код постоянной записи $del_id = IntVal($del_id); $WF_ID = $ID; //код текущей редактируемой записи $IBLOCK_ID = IntVal($IBLOCK_ID); //код информационного раздела $WF = "N"; $view = "N";
if(strlen($return_url)>0 && strtolower(substr($return_url, strlen($APPLICATION->GetCurPage())))== strtolower($APPLICATION->GetCurPage())) $return_url = ""; class _CIBlockError { var $err_type, $err_text, $err_level; function _CIBlockError($err_level, $err_type, $err_text) { $this->err_type = $err_type; $this->err_text = $err_text; $this->err_level = $err_level; } } $error = false; if ($del_id > 0) { $DB->StartTransaction(); if(!CIBlockElement::Delete($del_id)) { $strWarning.=GetMessage("IBLOCK_DELETE_ERROR"); $DB->Rollback(); } else $DB->Commit(); LocalRedirect($return_url); } elseif ($_POST["form_filled"] == "") { $str_ACTIVE="Y"; $str_SORT="500"; if (COption::GetOptionString("ix_guestbook", "ix_gb_html_default", "N")=="Y") { $str_PREVIEW_TEXT_TYPE="html"; } else {$str_PREVIEW_TEXT_TYPE="text"; } // $str_PREVIEW_TEXT_TYPE="html"; $str_DETAIL_TEXT_TYPE="html"; if(!$error && CModule::IncludeModule("workflow") && $view!="Y") CIblockElement::WF_Lock($ID); $str_IBLOCK_ELEMENT_SECTION = Array(); $result = CIBlockElement::GetByID($WF_ID); $arIBTYPE = CIBlockType::GetByIDLang($type, LANG); if($arIBTYPE===false) { $error = new _CIBlockError(1, "BAD_IBLOCK_TYPE", GetMessage("IBLOCK_BAD_BLOCK_TYPE_ID")); break; } $bBadBlock = true; $iblock = CIBlock::GetByID($IBLOCK_ID); if($arIBlock=$iblock->Fetch()) { $bBadBlock = ($bModerator == "Y") OR ($bUser = "Y"); } if($ar = $result->ExtractFields("str_")) { if($str_IN_SECTIONS=="N") $str_IBLOCK_ELEMENT_SECTION[] = 0; else { $result = CIBlockElement::GetElementGroups($WF_ID); while($ar = $result->Fetch()) $str_IBLOCK_ELEMENT_SECTION[] = $ar["ID"]; } } else { $WF_ID=0; $ID=0; if($IBLOCK_SECTION_ID>0) $str_IBLOCK_ELEMENT_SECTION[] = $IBLOCK_SECTION_ID; } if($ID>0) { if($view=="Y") $APPLICATION->SetTitle($arIBlock["NAME"].": ".$arIBTYPE["ELEMENT_NAME"]. ": ".$str_NAME." - ".GetMessage("IBLOCK_ELEMENT_EDIT_VIEW")); else $APPLICATION->SetTitle($arIBlock["NAME"]. ": ".$arIBTYPE["ELEMENT_NAME"].": ".$str_NAME." - ".GetMessage("IBLOCK_EDIT_TITLE")); } else $APPLICATION->SetTitle($arIBlock["NAME"].": ".$arIBTYPE["ELEMENT_NAME"].": ".GetMessage("IBLOCK_NEW_TITLE")); require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_after.php"); // Далее форма редактирования записи if ($error) ShowError($error->err_text); //НАЧАЛО настраиваемой формы if (strlen($arIBTYPE["EDIT_FILE_AFTER"])>0 && is_file($_SERVER["DOCUMENT_ROOT"].$arIBTYPE["EDIT_FILE_AFTER"])): include($_SERVER["DOCUMENT_ROOT"].$arIBTYPE["EDIT_FILE_AFTER"]); else: $form_filled = "Y"; ?> <form method="POST" action="<? echo $APPLICATION->GetCurPage()?>" ENCTYPE="multipart/form-data" name="form_element"> <? echo GetFilterHiddens("filter_"); ?> <input type="hidden" name="form_filled" value="<? echo $form_filled;?>"> <input type="hidden" name="from" value="<?echo htmlspecialchars($from)?>"> <input type="hidden" name="WF" value="<?echo htmlspecialchars($WF)?>"> <input type="hidden" name="return_url" value="<?echo $return_url?>"> <input type="hidden" name="ID" value="<?echo $ID?>"> <input type="hidden" name="type" value="<?echo $type?>"> <input type="hidden" name="IBLOCK_SECTION_ID" value="<?echo IntVal($IBLOCK_SECTION_ID)?>"> <input type="hidden" name="IBLOCK_ID" value="<?echo IntVal($IBLOCK_ID)?>"> <table border="0" cellspacing="1" cellpadding="3" class="edittable" width="100%"> <? if($ID > 0): $p = CIblockElement::GetByID($ID); $pr = $p->Fetch();> $str_NAME = $pr["NAME"]; $str_PREVIEW_TEXT = $pr["PREVIEW_TEXT"]; endif;?> <tr> <td valign="top" align="left"><font class="tablefieldtext"><font class="required">*</font><?echo GetMessage("IX_GB_USERNAME")?></font></td> <td valign="top"> <? if ($str_NAME == "") $str_NAME = $USER->GetFullName()?> <input type="text" name="NAME" size="50" maxlength="255" class="typeinput" value="<?echo $str_NAME?>"> </td> </tr> <?if(COption::GetOptionString("ix_guestbook", "ix_gb_html", "N")=="Y"):?> <?if(ereg('(MSIE|Internet Explorer) ([0-9]).([0-9])+', $_SERVER['HTTP_USER_AGENT'], $version) && IntVal($version[2])>=5 && COption::GetOptionString("iblock", "use_htmledit", "Y")=="Y" && CModule::IncludeModule("fileman")):?> <tr> <td valign="top" colspan="2"> <?CFileMan::AddHTMLEditorFrame("PREVIEW_TEXT", $str_PREVIEW_TEXT, "PREVIEW_TEXT_TYPE", $str_PREVIEW_TEXT_TYPE, 300);?> </td> </tr> <?else:?> <tr> <td valign="top" align="right"><font class="tablefieldtext"><?echo GetMessage("IBLOCK_DESC_TYPE")?></font></td> <td valign="top"> <font class="tablebodytext"> <input type="radio" name="PREVIEW_TEXT_TYPE" value="text"<?if($str_PREVIEW_TEXT_TYPE!="html")echo " checked"?>> <?echo GetMessage("IBLOCK_DESC_TYPE_TEXT")?> / <input type="radio" name="PREVIEW_TEXT_TYPE" value="html"<?if($str_PREVIEW_TEXT_TYPE=="html")echo " checked"?>> <?echo GetMessage("IBLOCK_DESC_TYPE_HTML")?> </font> </td> </tr> <tr> <td valign="top" align="center"colspan="2" width="100%"> <textarea cols="110" class="typearea" rows="10" name="PREVIEW_TEXT" wrap="virtual"><?echo $str_PREVIEW_TEXT?></textarea> </td> </tr> <?endif?> <?else:?> <tr> <td valign="top" align="left" colspan="2" width="100%"> <textarea cols="80" class="typearea" rows="10" name="PREVIEW_TEXT" wrap="virtual"><?echo $str_PREVIEW_TEXT?></textarea> </td> </tr> <?endif?> </table> <br> <input type="submit" class="button" name="save" value="<?echo (($ID > 0)?GetMessage('IBLOCK_SAVE'):GetMessage('IBLOCK_ADD'))?>"> </form> <? endif; // Окончание формы } else {< do{ //цикл из одной итерации для выхода по break $arIBTYPE = CIBlockType::GetByIDLang($type, LANG); if($arIBTYPE===false) { $error = new _CIBlockError(1, "BAD_IBLOCK_TYPE", GetMessage("IBLOCK_BAD_BLOCK_TYPE_ID")); break; } $iblock = CIBlock::GetByID($IBLOCK_ID); $arIBlock=$iblock->Fetch(); if(strlen($arIBTYPE["EDIT_FILE_BEFORE"])>0 && is_file($_SERVER["DOCUMENT_ROOT"].$arIBTYPE["EDIT_FILE_BEFORE"])) { include($_SERVER["DOCUMENT_ROOT"].$arIBTYPE["EDIT_FILE_BEFORE"]); } if ($view!="Y" && (!$error) && empty($dontsave)) { if ($WF=="Y" && CModule::IncludeModule("workflow") && intval($_POST["WF_STATUS_ID"])<=0) $strWarning .= GetMessage("IBLOCK_WRONG_WF_STATUS")."<br>"; elseif($WF=="Y" && CModule::IncludeModule("workflow") && CIBlockElement::WF_GetStatusPermission($_POST["WF_STATUS_ID"])<1) $strWarning .= GetMessage("IBLOCK_ACCESS_DENIED_STATUS")." [".$_POST["WF_STATUS_ID"]."]."."<br>"; else { $DB->StartTransaction(); $bs = new CIBlockElement; // Для премодерируемой книги Active = "N" if ($ID == 0) { $today = getdate(); $str_today = $today["mon"]."/".$today["mday"]."/".$today["year"]; $str_today = $DB->FormatDate($str_today, "MM/DD/YYYY", CLang::GetDateFormat("SHORT")); $str_today .= " ".$today["hours"].":".$today["minutes"].":".$today["seconds"]; } else {$str_today = $_POST["ACTIVE_FROM"]; } $arFields = Array( "ACTIVE" => "Y", "MODIFIED_BY" => $USER->GetID(), "IBLOCK_SECTION" => $_POST["IBLOCK_SECTION"], "IBLOCK_ID" => $IBLOCK_ID, "ACTIVE_FROM" => $str_today, "NAME" => $_POST["NAME"], "PREVIEW_TEXT" => $_POST["PREVIEW_TEXT"], "PREVIEW_TEXT_TYPE" => $_POST["PREVIEW_TEXT_TYPE"] ); if(COption::GetOptionString("iblock", "show_xml_id", "N")=="Y" && is_set($_POST, "XML_ID")) { $arFields["XML_ID"] = $_POST["XML_ID"]; } if($ID>0) $res = $bs->Update($ID, $arFields, $WF=="Y"); else { $ID = $bs->Add($arFields, CModule::IncludeModule("workflow")); $res = ($ID>0); $PARENT_ID = $ID; } if(!$res) $strWarning .= $bs->LAST_ERROR."<br>"; else CIBlockElement::RecalcSections($ID); } if(strlen($strWarning)>0) { $error = new _CIBlockError(2, "BAD_SAVE", $strWarning); $bVarsFromForm = true; $DB->Rollback(); } else { if(CModule::IncludeModule("workflow")) CIBlockElement::WF_UnLock($ID); $arFields['ID'] = $ID; if(function_exists('BXIBlockAfterSave')) BXIBlockAfterSave($arFields); $DB->Commit(); LocalRedirect($return_url); } if(!empty($dontsave)) { if(CModule::IncludeModule("workflow")) CIBlockElement::WF_UnLock($ID); LocalRedirect($return_url); } }while(false);< >echo $error; } require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog.php"); ?>
В параметре $del_id нам будут передавать идентификатор записи для удаления, в массиве $_POST передаются значения свойств записи, при этом, если $ID (идентификатор записи) отличен от нуля, то это означает, что мы редактируем существующую запись. В противном случае речь идет про добавление новой. Сохранение записи происходит при помощи кода:
$arFields = Array( "ACTIVE" => "Y", "MODIFIED_BY" => $USER->GetID(), "IBLOCK_SECTION" => $_POST["IBLOCK_SECTION"], "IBLOCK_ID" => $IBLOCK_ID, "ACTIVE_FROM" => $str_today, "NAME" => $_POST["NAME"], "PREVIEW_TEXT" => $_POST["PREVIEW_TEXT"], "PREVIEW_TEXT_TYPE" => $_POST["PREVIEW_TEXT_TYPE"] ); if($ID>0) $res = $bs->Update($ID, $arFields, $WF=="Y"); else { $ID = $bs->Add($arFields, CModule::IncludeModule("workflow")); $res = ($ID>0); $PARENT_ID = $ID; } Обратим внимание на код, связанный с распределением прав: $bModerator = (CIX_Guestbook::IsModerator()) ? "Y" : "N"; $bUser = (CIX_Guestbook::IsUser()) ? "Y" : "N"; if($bModerator!="Y" && $bUser!="Y") $APPLICATION->AuthForm(GetMessage("ACCESS_DENIED"));
Очевидно, что он позволяет добавлять записи только пользователям, имеющим соответствующие роли. Ради этих трех строчек мы и описывали методы класса CIX_Guestbook.
Дальнейший анализ кода приведенного выше поручаем вам самим, тем более, что его можно еще долго оптимизировать. Наша задача – продемонстрировать интеграцию с БУС, поэтому мы переходим к следующему пункту – регистрации компонентов в HTML-редакторе.
Фактически, код компонентов у нас уже есть – это файлы
«/ix_guestbook/install/templates/ix_guestbook/index.php» и «/ix_guestbook/install/templates/ix_guestbook/edit_record.php»
Осталось только описать их таким образом, чтобы ядро БУС распознавало в них компоненты. Для этого нам надо создать файл
«/ix_guestbook/install/templates/ix_guestbook/.description.php»:
<? IncludeTemplateLangFile(__FILE__); $sSectionName = GetMessage("T_IX_GB_DESC_NAME"); $arTypes = Array(); if(CModule::IncludeModule("iblock")): $db_iblock_type = CIBlockType::GetList(Array("SORT"=>"ASC")); while($arRes = $db_iblock_type->Fetch()) if($arIBType = CIBlockType::GetByIDLang($arRes["ID"], LANG)) $arTypes[$arRes["ID"]] = $arIBType["NAME"]; $arIBlocks=Array(); $db_iblock = CIBlock::GetList(Array("SORT"=>"ASC"), Array("SITE_ID"=>$_REQUEST["site"])); while($arRes = $db_iblock->Fetch()) $arIBlocks[$arRes["ID"]] = $arRes["NAME"]; $arSorts = Array("ASC"=>GetMessage("T_IX_GB_DESC_ASC"), "DESC"=>GetMessage("T_IX_GB_DESC_DESC")); $arSortFields = Array( "NAME"=>GetMessage("T_IX_GB_DESC_FNAME"), "ACTIVE_FROM"=>GetMessage("T_IX_GB_DESC_FACT"), "SORT"=>GetMessage("T_IX_GB_DESC_FSORT"), "TIMESTAMP_X"=>GetMessage("T_IX_GB_DESC_FTSAMP") ); $arSectSortFields = Array( "NAME"=>GetMessage("T_IX_GB_DESC_FNAME"), "SORT"=>GetMessage("T_IX_GB_DESC_FSORT"), "TIMESTAMP_X"=>GetMessage("T_IX_GB_DESC_FTSAMP") ); $arTemplateDescription = Array( ".separator" => Array( "NAME" => GetMessage("T_IX_GB_DESC_GB"), "DESCRIPTION" => "", "SEPARATOR" => "Y", ), "index.php" => Array( "NAME" => GetMessage("T_IX_GB_DESC_REC_LIST"), "DESCRIPTION" => GetMessage("T_IX_GB_DESC_REC_LIST_DESC"), "ICON" => "/bitrix/images/iblock/comp_list.gif", "PARAMS" => Array( "ID" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBID"), "TYPE"=>"LIST", "VALUES"=>$arIBlocks, "DEFAULT"=>"", "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "IBLOCK_TYPE" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBTYPE"), "TYPE"=>"LIST", "VALUES"=>$arTypes, "DEFAULT"=>"guestbook", "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "NEWS_COUNT" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBCNT"),"TYPE"=>"STRING", "DEFAULT"=>"5", "COLS"=>"3"), "SORT_BY1" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBORD1"), "TYPE"=>"LIST", "DEFAULT"=>"ACTIVE_FROM", "VALUES"=>$arSortFields, "ADDITIONAL_VALUES"=>"N"), "SORT_ORDER1" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBBY1"), "TYPE"=>"LIST", "MULTIPLE"=>"N", "DEFAULT"=>"DESC", "VALUES"=>$arSorts, "ADDITIONAL_VALUES"=>"N"), "SORT_BY2" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBORD2"), "TYPE"=>"LIST", "DEFAULT"=>"SORT", "VALUES"=>$arSortFields, "ADDITIONAL_VALUES"=>"N"), "SORT_ORDER2" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBBY2"), "TYPE"=>"LIST", "DEFAULT"=>"ASC", "VALUES"=>$arSorts, "ADDITIONAL_VALUES"=>"N"), "edit_url" => Array("NAME"=>GetMessage("T_IX_GB_DESC_EDITURL"), "TYPE"=>"STRING", "DEFAULT"=>'={$APPLICATION->GetCurPage()}', "COLS"=>"20"), "return_url" => Array("NAME"=>GetMessage("T_IX_GB_DESC_RETURNURL"), "TYPE"=>"STRING", "DEFAULT"=>'={$APPLICATION->GetCurPage()}', "COLS"=>"20") ) ), "edit_record.php" => Array( "NAME" => GetMessage("T_IX_GB_DESC_EDIT"), "DESCRIPTION" => GetMessage("T_IX_GB_DESC_EDIT_DESC"), "ICON" => "/bitrix/images/iblock/comp_detail.gif", "PARAMS" => Array( "type" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBTYPE"), "TYPE"=>"LIST", "VALUES"=>$arTypes, "DEFAULT"=>"guestbook", "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "ID" => Array("NAME"=>GetMessage("T_IX_GB_DESC_ID"), "TYPE"=>"STRING", "DEFAULT"=>'={$ID}', "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "IBLOCK_ID" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBID"), "TYPE"=>"LIST", "VALUES"=>$arIBlocks, "DEFAULT"=>"", "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "del_id" => Array("NAME"=>GetMessage("T_IX_GB_DESC_DEL_ID"), "TYPE"=>"STRING", "DEFAULT"=>'={$del_id}', "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "return_url" => Array(
"NAME"=>GetMessage("T_IX_GB_DESC_RETURNURL"), "TYPE"=>"STRING", "DEFAULT"=>'={$APPLICATION->GetCurPage()}', "COLS"=>"20") ) ) );< endif; ?>
Необходимые файлы со строковыми константами находятся в файле «/ix_guestbook/install/templates/lang/ru/ix_guestbook/.description.php»:
<? $MESS ['T_IX_GB_DESC_NAME'] = "Гостевые книги"; $MESS ['T_IX_GB_DESC_ASC'] = "По возрастанию"; $MESS ['T_IX_GB_DESC_DESC'] = "По убыванию"; $MESS ['T_IX_GB_DESC_FNAME'] = "Название"; $MESS ['T_IX_GB_DESC_FACT'] = "Дата начала активности"; $MESS ['T_IX_GB_DESC_FSORT'] = "Сортировка"; $MESS ['T_IX_GB_DESC_FTSAMP'] = "Дата последнего изменения"; $MESS ['T_IX_GB_DESC_GB'] = "Гостевые книги"; $MESS ['T_IX_GB_DESC_REC_LIST'] = "Список записей"; $MESS ['T_IX_GB_DESC_REC_LIST_DESC'] = "Показывает список записей книги"; $MESS ['T_IX_GB_DESC_IBID'] = "Идентификатор информационного блока"; $MESS ['T_IX_GB_DESC_EDITURL'] = "URL для формы редактирования записи"; $MESS ['T_IX_GB_DESC_SAVEURL'] = "URL страницы для сохранения записи"; $MESS ['T_IX_GB_DESC_RETURNURL'] = "URL для возвращения к списку записей"; $MESS ['T_IX_GB_DESC_IBTYPE'] = "Тип информационных блоков"; $MESS ['T_IX_GB_DESC_IBSORT'] = "Поле для cортировки информационных блоков"; $MESS ['T_IX_GB_DESC_SORT'] = "Cортировка"; $MESS ['T_IX_GB_DESC_ID'] = "Идентификатор"; $MESS ['T_IX_GB_DESC_IBSORTBY'] = "Направление для сортировки информационных блоков"; $MESS ['T_IX_GB_DESC_IBCNT'] = "Количество записей на странице"; $MESS ['T_IX_GB_DESC_IBORD1'] = "Поле для первой сортировки записей"; $MESS ['T_IX_GB_DESC_IBBY1'] = "Направление для первой сортировки записей"; $MESS ['T_IX_GB_DESC_IBORD2'] = "Поле для второй сортировки записей"; $MESS ['T_IX_GB_DESC_IBBY2'] = "Направление для второй сортировки записей"; $MESS ['T_IX_GB_DESC_EDIT'] = "Добавить/Редактировать"; $MESS ['T_IX_GB_DESC_EDIT_DESC'] = "Форма для добавления или редактирования записи"; $MESS ['T_IX_GB_DESC_SAVE'] = "Сохранить/Удалить"; $MESS ['T_IX_GB_DESC_SAVE_DESC'] = "Страница для сохранения/удаления записи"; $MESS ['T_IX_GB_DESC_DEL_ID'] = "Идентификатор записи для удаления"; ?>
Собственно регистрация компонентов заключается в заполнении массива $arTemplateDescription. Первый элемент массива служит в качестве разделителя, остальные представляют собой описания компонентов. Рассмотрим подробнее компонент «список записей книги»:
"index.php" => Array( "NAME" => GetMessage("T_IX_GB_DESC_REC_LIST"), "DESCRIPTION" => GetMessage("T_IX_GB_DESC_REC_LIST_DESC"), "ICON" => "/bitrix/images/iblock/comp_list.gif", "PARAMS" => Array( "ID" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBID"), "TYPE"=>"LIST", "VALUES"=>$arIBlocks, "DEFAULT"=>"", "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "IBLOCK_TYPE" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBTYPE"), "TYPE"=>"LIST", "VALUES"=>$arTypes, "DEFAULT"=>"guestbook", "MULTIPLE"=>"N", "ADDITIONAL_VALUES"=>"N"), "NEWS_COUNT" => Array( "NAME"=>GetMessage("T_IX_GB_DESC_IBCNT"), "TYPE"=>"STRING", "DEFAULT"=>"5", "COLS"=>"3"), "SORT_BY1" => Array( "NAME"=>GetMessage("T_IX_GB_DESC_IBORD1"), "TYPE"=>"LIST", "DEFAULT"=>"ACTIVE_FROM", "VALUES"=>$arSortFields, "ADDITIONAL_VALUES"=>"N"), "SORT_ORDER1" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBBY1"), "TYPE"=>"LIST", "MULTIPLE"=>"N", "DEFAULT"=>"DESC", "VALUES"=>$arSorts, "ADDITIONAL_VALUES"=>"N"), "SORT_BY2" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBORD2"), "TYPE"=>"LIST", "DEFAULT"=>"SORT", "VALUES"=>$arSortFields, "ADDITIONAL_VALUES"=>"N"), "SORT_ORDER2" => Array("NAME"=>GetMessage("T_IX_GB_DESC_IBBY2"), "TYPE"=>"LIST", "DEFAULT"=>"ASC", "VALUES"=>$arSorts, "ADDITIONAL_VALUES"=>"N"), "edit_url" => Array("NAME"=>GetMessage("T_IX_GB_DESC_EDITURL"), "TYPE"=>"STRING", "DEFAULT"=>'={$APPLICATION->GetCurPage()}', "COLS"=>"20"), "return_url" => Array("NAME"=>GetMessage("T_IX_GB_DESC_RETURNURL"), "TYPE"=>"STRING", "DEFAULT"=>'={$APPLICATION->GetCurPage()}', "COLS"=>"20") ) ),
Первые три параметра служат для описания компонента в HTML-редакторе. Последний параметр - массив PARAMS - описывает синтаксис вызова компонента и привязывает к свойствам компонента визуальные элементы, в которых пользователь сможет вводить значения.
Например, свойство $ID с идентификатором инфоблока гостевой книги, описывается как список ("TYPE"=>"LIST"), который заполняется названиями доступных инфоблоков ("VALUES"=>$arIBlocks) с запретом множественного выбора ("MULTIPLE"=>"N"). Обратите внимание на синтаксис вызова PHP кода в качестве значения параметра по умолчанию ("DEFAULT"):
"edit_url" => Array("NAME"=>GetMessage("T_IX_GB_DESC_EDITURL"), "TYPE"=>"STRING", "DEFAULT"=>'={$APPLICATION->GetCurPage()}', "COLS"=>"20")
Мы можем добавлять неограниченное количество своих компонентов в данном файле, описывая их параметры, указывая их тип, значения и элементы редактирования. В результате HTML-редактор позволит нам вставлять при помощи механизма drag&drop код, аналогичный следующему:
<?$APPLICATION->IncludeFile("ix_guestbook/index.php", Array( "ID" => "11", // Идентификатор информационного блока "IBLOCK_TYPE" => "guestbook", // Тип информационных блоков "NEWS_COUNT" => "5", // Количество записей на странице "SORT_BY1" => "ACTIVE_FROM", // Поле для первой сортировки записей "SORT_ORDER1" => "DESC", // Направление для первой сортировки записей "SORT_BY2" => "SORT", // Поле для второй сортировки записей "SORT_ORDER2" => "ASC", // Направление для второй сортировки записей "edit_url" => $APPLICATION->GetCurPage(), // URL для формы редактирования записи "return_url" => $APPLICATION->GetCurPage(),// URL для возвращения к списку записей ) );?>
Иными словами, чтобы сделать компонент для своего модуля БУС мы должны:
Рис. 5. HTML-редактор с компонентами гостевой книги.
Нам осталось решить только один вопрос, чтобы модуль «Гостевые книги» мог считаться полноценным механизмом – необходимо зарегистрировать свои почтовые шаблоны и генерировать нотификации при добавлении записей гостевых книг. Для этого нам тоже не придется изобретать велосипед, поскольку мы сможем воспользоваться готовыми примерами кода существующих модулей БУС. В частности, можно взять за основу код, который разработан для модуля «Голосования». Скопируем каталог “bitrix/modules/vote/install/events/” в “bitrix/modules/ix_guestbook/install/”. Нам надо лишь слегка переделать описанные там шаблоны для своих целей. Начнем с файла del_events.php:
<? $DB->Query("DELETE FROM b_event_type WHERE EVENT_NAME in ('IX_RECORD_NEW')"); $DB->Query("DELETE FROM b_event_message WHERE EVENT_NAME in ('IX_RECORD_NEW')"); ?> Здесь мы заменили название нотификации VOTE_NEW на IX_RECORD_NEW. Именно так будут называться наши шаблоны, которые мы опишем в файле «set_events.php»: <? include($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/ix_guestbook/install/events/del_events.php"); /************************************************************************************************ Типы сообщений ************************************************************************************************ function UET($EVENT_NAME, $NAME, $LID, $DESCRIPTION) { global $DB, $I; $et = new CEventType; $et->Add( Array( "LID" => $LID, "EVENT_NAME" => $EVENT_NAME, "NAME" => $NAME, "DESCRIPTION" => $DESCRIPTION ) ); } UET( "IX_RECORD_NEW","Новая запись в книге","ru", " #GB_TITLE# - Название гостевой книги #ID# - ID записи #DATETIME# - дата и время добавления записи #USER_NAME# - имя пользователя #RECORD_TEXT# - текст сообщения #LOGIN# - логин #USER_ID# - ID пользователя #STAT_GUEST_ID# - ID посетителя модуля статистики #SESSION_ID# - ID сессии модуля статистики #IP# - IP адрес #IBLOCK_TYPE# - тип инфоблока #IBLOCK_ID# - ID инфоблока " ); UET( "IX_RECORD_NEW","New guestbook record","en", " #GB_TITLE# - guestbook name #ID# - record ID #DATETIME# - date and time of record publication #USER_NAME# - user's name #RECORD_TEXT# - record text #LOGIN# - login #USER_ID# - user's ID #STAT_GUEST_ID# - guest ID of statistics module #SESSION_ID# - session ID of statistics module #IP# - IP address #IBLOCK_TYPE# - information block type #IBLOCK_ID# - information block ID " ); /************************************************************************************************ Шаблоны сообщений ************************************************************************************************/ //************************************************************************************************ $arr["EVENT_NAME"] = "IX_RECORD_NEW"; $arr["SITE_ID"] = "ru"; $arr["EMAIL_FROM"] = "#DEFAULT_EMAIL_FROM#"; $arr["EMAIL_TO"] = "#DEFAULT_EMAIL_FROM#"; $arr["BCC"] = ""; $arr["SUBJECT"] = "#SITE_NAME#: Новая запись в гостевой книге \"[#IBLOCK_ID#] #GB_TITLE#\""; $arr["BODY_TYPE"] = "text"; $arr["MESSAGE"] = " Новая запись Гостевая книга - [#GB_ID#] #GB_TITLE# Сообщение: #RECORD_TEXT# -------------------------------------------------------------- Посетитель - [#USER_ID#] (#LOGIN#) #USER_NAME# [#STAT_GUEST_ID#] Сессия - #SESSION_ID# IP адрес - #IP# Дата/Время - #DATETIME# Для редактирования данной записи воспользуйтесь ссылкой: http://#SERVER_NAME#/bitrix/admin/iblock_element_edit.php?WF=Y&ID=#ID#&type=#IBLOCK_TYPE#&IBLOCK_ID=#IBLOCK_ID#&lang=ru Письмо сгенерировано автоматически. $arTemplates[] = $arr; //************************************************************************************************ $arr["EVENT_NAME"] = "IX_RECORD_NEW"; $arr["SITE_ID"] = "en"; $arr["EMAIL_FROM"] = "#DEFAULT_EMAIL_FROM#"; $arr["EMAIL_TO"] = "#DEFAULT_EMAIL_FROM#"; $arr["BCC"] = ""; $arr["SUBJECT"] = "#SITE_NAME#: New record of guestbook \"[#IBLOCK_ID#] #GB_TITLE#\""; $arr["BODY_TYPE"] = "text"; $arr["MESSAGE"] = " New record Guestbook - [#GB_ID#] #GB_TITLE# Message: #RECORD_TEXT# -------------------------------------------------------------- Guest - [#USER_ID#] (#LOGIN#) #USER_NAME# [#STAT_GUEST_ID#] Session - #SESSION_ID# IP address - #IP# Date/Time - #DATETIME# To edit the record visit link: http://#SERVER_NAME#/bitrix/admin/iblock_element_edit.php?WF=Y&ID=#ID#&type=#IBLOCK_TYPE#&IBLOCK_ID=#IBLOCK_ID#&lang=en Automatically generated message. "; $arTemplates[] = $arr; //************************************************************************************************ $emess = new CEventMessage; foreach ($arTemplates as $Template) { $arFields = Array( "ACTIVE" => "Y", "EVENT_NAME" => $Template["EVENT_NAME"], "LID" => $Template["SITE_ID"], "EMAIL_FROM" => $Template["EMAIL_FROM"], "EMAIL_TO" => $Template["EMAIL_TO"], "BCC" => $Template["BCC"], "SUBJECT" => $Template["SUBJECT"], "MESSAGE" => $Template["MESSAGE"], "BODY_TYPE" => $Template["BODY_TYPE"] ); $emess->Add($arFields); } ?>
Основной здесь является функция с загадочным названием UET, которая регистрирует тип шаблона в системе. Мы создаем два типа – на русском и английском языках, дважды вызывая UET с разными параметрами. В описании каждого типа шаблона желательно перечислить те параметры, которые потом будут доступны администратору. Мы сами придумываем эти параметры, никаких ограничений здесь нет, но они должны быть осмыслены.
После создания типов мы создаем два готовых шаблона, которые будут доступны в разделе администрирования. Заполняем массив $arr и добавляем его в $arTemplates[], чтобы потом создать шаблоны при помощи вызова «new CEventMessage».
Добавим пару строк в «/ix_guestbook/install/index.php», чтобы наши шаблоны автоматически создавались при установке модуля:
function DoInstall() { global $DB, $APPLICATION; // добавлено RegisterModule("ix_guestbook"); // Сообщения include($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/ix_guestbook/install/events/set_events.php"); // добавлено } function DoUninstall() { global $DB, $APPLICATION; // добавлено UnRegisterModule("ix_guestbook"); // Сообщения include($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/ix_guestbook/install/events/del_events.php"); // добавлено }
Все очень просто. Удалив модуль, и снова установив его, мы обнаружим новые шаблоны в настройках системы, что и требовалось.
Нам остается сделать последний шаг, а именно, воспользоваться шаблонами и отправить почтовую нотификацию администраторам сайта при добавлении новой записи в гостевую книгу. Для этого придется вносить дополнения в файл
«/ix_guestbook/install/templates/ix_guestbook/edit_record.php»
Реальное добавление записи в инфоблоки происходит в следующем коде:
if($ID>0) $res = $bs->Update($ID, $arFields, $WF=="Y"); else { $ID = $bs->Add($arFields, CModule::IncludeModule("workflow")); $res = ($ID>0); $PARENT_ID = $ID; … Сюда мы и добавим генерацию сообщения: // посылаем сообщение о новой записи в книге $iblock = CIblock::GetByID($IBLOCK_ID); $iblock_r = $iblock->Fetch();> global $REMOTE_ADDR; $arEventFields = array( "ID" => $ID, "IBLOCK_ID" => $IBLOCK_ID, "IBLOCK_TYPE" => $type, "DATETIME" => $str_today, "USER_NAME" => $_POST["NAME"], "RECORD_TEXT" => HtmlToTxt($_POST["PREVIEW_TEXT"]), "LOGIN" => $USER->GetLogin(), "USER_ID" => $USER->GetID(), "STAT_GUEST_ID" => intval($_SESSION["SESS_GUEST_ID"]), "SESSION_ID" => intval($_SESSION["SESS_SESSION_ID"]), "IP" => $REMOTE_ADDR, "GB_TITLE" => $iblock_r["NAME"] ); CEvent::Send("IX_RECORD_NEW", $iblock_r["LID"], $arEventFields);
Теперь, при добавлении записи в книгу, на почтовый адрес по умолчанию (он указан в настройках основного модуля системы) будет приходить письмо с текстом новой записи, дополнительными сведениями и ссылкой на административный раздел для редактирования элемента инфоблока. Фактически, именно этот механизм позволит вам модерировать гостевые книги.
Мы рассмотрели почти все основные вопросы, которые возникают при создании собственного модуля для «Битрикс: Управление сайтом»:
За кадром осталась, пожалуй, лишь технология добавления своих пунктов меню в разделе Администрирования. Возможно, мы еще к ней вернемся.