Пример создания действия Запись в лог
Создадим действие, которое будет писать указанные в действии данные в лог бизнес-процесса или текстовый файл. Действие назовем Запись в лог. Название активити в структуре – write2logactivity.
Структура файлов
Создайте структуру файлов будущего Activity. Папка с Activity должна быть расположена в /bitrix/activities/custom/
. Название папки с действием должно совпадать с именем файла, в котором находится класс с Activity, в данном случае папка должна называться /write2logactivity
.
Структура в общем виде подобна структуре компонентов:
- .description.php – описание будущего действия;
- properties_dialog.php – форма настроек действия;
- write2logactivity.php – код действия;
- icon.gif – иконка, которая будет отображать действие в общем списке действий дизайнера БП;
/lang/
– содержит папки с языковыми сообщениями. Папки должны называться в соответствии с идентификаторами языков. Например, ru, en. В каждой папке содержатся файлы с фразами, одноименные соответствующим файлам действия из основной папки:/ru/
- .description.php – языковые сообщения описывающие действие (например, название действия);
- properties_dialog.php – языковые сообщения для формы настроек действия;
- write2logactivity.php – языковые сообщения, выводимые кодом действия (например, вывод ошибок).
Файлы активити, описанного в этом уроке, можно скачать в архиве: write2logactivity.zip.
Описание действия
Задайте описание будущего действия в файле .description.php:
<? if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die(); $arActivityDescription = array( "NAME" => GetMessage("BPDDA_DESCR_NAME"), "DESCRIPTION" => GetMessage("BPDDA_DESCR_DESCR"), "TYPE" => "activity", // Тип – действие "CLASS" => "Write2LogActivity", // Класс с Activity "JSCLASS" => "BizProcActivity", // Стандартная JS библиотека, которая будет рисовать Activity "CATEGORY" => array( "ID" => "other", // Activity будет располагаться в категории "Прочее" ), ); ?>
В визуальном редакторе получаем иконку своего действия в категории Прочее:
Форма настроек действия
Запрограммируйте форму настроек действия и логику её работы в файле properties_dialog.php:
<? if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); ?> <script> var b_log_counter = -1; function AddCondition(field, val) { var addrowTable = document.getElementById('bwfvc_addrow_table1'); b_log_counter++; var newRow = addrowTable.insertRow(-1); newRow.id = "delete_row_log_" + b_log_counter; var newCell = newRow.insertCell(-1); var newSelect = document.createElement("input"); newSelect.type = 'text'; newCell.align="left"; newSelect.size = '40'; newSelect.setAttribute('b_log_counter', b_log_counter); newSelect.id = "id_var_name_" + b_log_counter; newSelect.name = "fields[var_value_" + b_log_counter + "]"; var i = -1; var i1 = -1; newSelect.value = BWFVCUnHtmlSpecialChars(field); newCell.appendChild(newSelect); var newCell = newRow.insertCell(-1); newCell.innerHTML = "="; var newCell = newRow.insertCell(-1); newCell.id = "id_row_value_" + b_log_counter; newCell.align="right"; newCell.innerHTML = '<input size="30" type="text" id="id_var_value_' + b_log_counter + '" name="values[var_value_' + b_log_counter + ']" value="' + val + '">'; var newCell = newRow.insertCell(-1); newCell.id = "id_dialog_" + b_log_counter; newCell.innerHTML = '<input type="button" value="..." onclick="BPAShowSelector(\'id_var_value_'+b_log_counter+'\', \'string\');">'; var newCell = newRow.insertCell(-1); newCell.align="right"; newCell.innerHTML = '<a href="#" onclick="BWFVCDeleteCondition(' + b_log_counter + '); return false;">Удалить поле</a>'; } function BWFVCUnHtmlSpecialChars(string, quote) { string = string.toString(); if (quote) string = string.replace(/'/g, "'"); string = string.replace(/"/g, "\""); string = string.replace(/>/g, ">"); string = string.replace(/</g, "<"); string = string.replace(/&/g, '&'); return string; } function BWFVCDeleteCondition(ind) { var addrowTable = document.getElementById('bwfvc_addrow_table1'); var cnt = addrowTable.rows.length; for (i = 0; i < cnt; i++) { if (addrowTable.rows[i].id != 'delete_row_log_' + ind) continue; addrowTable.deleteRow(i); break; } // if (addrowTable.rows.length <= 0) // addrowTable.style.display = 'none'; } function Write2File() { var f_table = document.getElementById('write2filetable'); if (f_table.rows.length <= 0) { var newRow = f_table.insertRow(-1); var newCell = newRow.insertCell(-1); //newCell.align="right"; //newCell.style.padding.left = '100px'; newCell.innerHTML = '<input size="40" type="text" id="path2file" name="path2file" value="<?= ($arCurrentValues["path2file"])? $arCurrentValues["path2file"]: "/bitrix/test.log"?>">'; } else f_table.deleteRow("-1"); } </script> <tr id="write_file_form" style="display:line"> <td colspan="2"> <input type="checkbox" id="write2file" name="write2file" onchange="Write2File();"><?= GetMessage("WRITE_2_LOG");?> <table width="100%" border="0" cellpadding="2" cellspacing="2" id="write2filetable"> </table> </td> </tr> <tr id="pd_list" style="display:line"> <td colspan="2"> <table width="100%" border="0" cellpadding="2" cellspacing="2" id="bwfvc_addrow_table1"> </table> <a href="#" onclick="AddCondition('', ''); return false;"><?= GetMessage("ADD_CONDITION");?></a> </td> </tr> <script> <? foreach ($arCurrentValues["MapFields"] as $fieldKey => $documentFieldValue) { ?> AddCondition('<?= CUtil::JSEscape($fieldKey) ?>', '<?= CUtil::JSEscape($documentFieldValue) ?>'); <? } if (count($arCurrentValues) <= 0) { ?>AddCondition("", "");<? } ?> var check = '<?= $arCurrentValues["write2file"];?>'; if (check == "Y") { document.getElementById("write2file").checked = "checked"; Write2File(); } </script>
В результате при открытии формы настроек действия получим:
Если опция Писать в файл отключена – данные запишутся в лог бизнес-процесса. При включении опции появится дополнительное поле для указания пути к файлу. В этот файл и запишутся логи. По умолчанию это /bitrix/test.log.
Далее в форме указываются поля для записи в лог. В левом поле вручную вносится название записи, а в правой – значение, для которого доступна подстановка через форму Вставка значения. Кнопка Добавить поле добавляет новую строку, а Удалить поле – удаляет строку.
Основной код
Задайте код работы действия в файле write2logactivity.php.
<? if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); class CBPWrite2LogActivity extends CBPActivity { public function __construct($name) { parent::__construct($name); $this->arProperties = array( "Title" => "", "MapFields" => null, "write2file" => null, "path2file"=> null ); } public function Execute() { if (is_array($this->MapFields) && count($this->MapFields)) { $printVal = $this->__get("MapFields"); $bFirst = true; foreach($printVal as $key => $val) { if ($this->write2file == "Y") { $f = fopen ($_SERVER["DOCUMENT_ROOT"].$this->path2file, "a+"); if ($bFirst) fwrite ($f, print_r ("\n\n\n===========================================================\n",true)); fwrite ($f, print_r ($key." = ".$val."\n",true)); fclose($f); $bFirst = false; } else $this->WriteToTrackingService($key." = ".$val); } } return CBPActivityExecutionStatus::Closed; } public static function GetPropertiesDialog($documentType, $activityName, $arWorkflowTemplate, $arWorkflowParameters, $arWorkflowVariables, $arCurrentValues = null, $formName = "") { $runtime = CBPRuntime::GetRuntime(); if (!is_array($arCurrentValues)) { $arCurrentValues = array(); $arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName); if (is_array($arCurrentActivity["Properties"]) && array_key_exists("MapFields", $arCurrentActivity["Properties"]) && is_array($arCurrentActivity["Properties"]["MapFields"])) { foreach ($arCurrentActivity["Properties"]["MapFields"] as $k => $v) { $arCurrentValues["MapFields"][$k] = $v; } $arCurrentValues["write2file"] = $arCurrentActivity["Properties"]["write2file"]; $arCurrentValues["path2file"] = $arCurrentActivity["Properties"]["path2file"]; } } $runtime = CBPRuntime::GetRuntime(); return $runtime->ExecuteResourceFile( __FILE__, "properties_dialog.php", array( "arCurrentValues" => $arCurrentValues, "formName" => $formName, ) ); } public static function GetPropertiesDialogValues($documentType, $activityName, &$arWorkflowTemplate, &$arWorkflowParameters, &$arWorkflowVariables, $arCurrentValues, &$arErrors) { $runtime = CBPRuntime::GetRuntime(); $arProperties = array("MapFields" => array()); if (is_array($arCurrentValues) && count($arCurrentValues)>0) { if (is_array($arCurrentValues["fields"]) && count($arCurrentValues["fields"]) > 0 && is_array($arCurrentValues["values"]) && count($arCurrentValues["values"]) > 0) { foreach($arCurrentValues["fields"] as $key => $value) if (strlen($value) > 0 && strlen($arCurrentValues["values"][$key]) > 0) $arProperties["MapFields"][$value] = $arCurrentValues["values"][$key]; } $arProperties ["write2file"] = $arCurrentValues["write2file"] == "Y" ? "Y" : "N"; $arProperties ["path2file"] = $arCurrentValues["path2file"]; } $arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName); $arCurrentActivity["Properties"] = $arProperties; return true; } public static function ValidateProperties($arTestProperties = array(), CBPWorkflowTemplateUser $user = null) { $arErrors = array(); $path = ""; if ($arTestProperties["path2file"]) { $path = $_SERVER["DOCUMENT_ROOT"].$arTestProperties["path2file"]; if (!file_exists($path) && !is_writable(GetDirPath($path))) $arErrors[] = array("code" => "DirNotWritable", "parameter" => "path2file", "message" => GetMessage("DIR_NOT_WRITABLE")); if (file_exists($path) && !!is_writable(GetDirPath($path))) $arErrors[] = array("code" => "FileNotWritable", "parameter" => "path2file", "message" => GetMessage("FILE_NOT_WRITABLE")); } return array_merge($arErrors, parent::ValidateProperties($arTestProperties, $user)); } }
Разъясним чуть подробнее отдельные части этого кода, на которые надо обратить внимание разработчикам.
- Зададим заголовок класса и конструктор. В конструкторе определяются параметры действия:
class CBPWrite2LogActivity extends CBPActivity { public function __construct($name) { parent::__construct($name); $this->arProperties = array( "Title" => "", "MapFields" => null, "write2file" => null, "path2file"=> null ); }
- Выводим диалог настройки параметров действия. Если параметры уже были заданы, то они инициализируются:
public static function GetPropertiesDialog($documentType, $activityName, $arWorkflowTemplate, $arWorkflowParameters, $arWorkflowVariables, $arCurrentValues = null, $formName = "") { $runtime = CBPRuntime::GetRuntime(); if (!is_array($arCurrentValues)) { $arCurrentValues = array(); $arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName); if (is_array($arCurrentActivity["Properties"]) && array_key_exists("MapFields", $arCurrentActivity["Properties"]) && is_array($arCurrentActivity["Properties"]["MapFields"])) { foreach ($arCurrentActivity["Properties"]["MapFields"] as $k => $v) { $arCurrentValues["MapFields"][$k] = $v; } $arCurrentValues["write2file"] = $arCurrentActivity["Properties"]["write2file"]; $arCurrentValues["path2file"] = $arCurrentActivity["Properties"]["path2file"]; } } $runtime = CBPRuntime::GetRuntime(); return $runtime->ExecuteResourceFile( __FILE__, "properties_dialog.php", array( "arCurrentValues" => $arCurrentValues, "formName" => $formName, ) ); }
- Сохранение настроек, сделанных в диалоге настройки действия:
public static function GetPropertiesDialogValues($documentType, $activityName, &$arWorkflowTemplate, &$arWorkflowParameters, &$arWorkflowVariables, $arCurrentValues, &$arErrors) { $runtime = CBPRuntime::GetRuntime(); $arProperties = array("MapFields" => array()); if (is_array($arCurrentValues) && count($arCurrentValues)>0) { if (is_array($arCurrentValues["fields"]) && count($arCurrentValues["fields"]) > 0 && is_array($arCurrentValues["values"]) && count($arCurrentValues["values"]) > 0) { foreach($arCurrentValues["fields"] as $key => $value) if (strlen($value) > 0 && strlen($arCurrentValues["values"][$key]) > 0) $arProperties["MapFields"][$value] = $arCurrentValues["values"][$key]; } $arProperties ["write2file"] = $arCurrentValues["write2file"] == "Y" ? "Y" : "N"; $arProperties ["path2file"] = $arCurrentValues["path2file"]; } $arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName); $arCurrentActivity["Properties"] = $arProperties; return true; }
- Проверка параметров действия:
public static function ValidateProperties($arTestProperties = array(), CBPWorkflowTemplateUser $user = null) { $arErrors = array(); $path = ""; if ($arTestProperties["path2file"]) { $path = $_SERVER["DOCUMENT_ROOT"].$arTestProperties["path2file"]; if (!file_exists($path) && !is_writable(GetDirPath($path))) $arErrors[] = array("code" => "DirNotWritable", "parameter" => "path2file", "message" => GetMessage("DIR_NOT_WRITABLE")); if (file_exists($path) && !!is_writable(GetDirPath($path))) $arErrors[] = array("code" => "FileNotWritable", "parameter" => "path2file", "message" => GetMessage("FILE_NOT_WRITABLE")); } return array_merge($arErrors, parent::ValidateProperties($arTestProperties, $user)); } }
Языковые сообщения
Осталось прописать языковые сообщения, которые отобразятся в публичной части. Переходим в папку /lang/ru
и заполняем файлы:
- Файл .description.php:
<? $MESS ['BPDDA_DESCR_DESCR'] = "Запись в лог"; $MESS ['BPDDA_DESCR_NAME'] = "Запись в лог"; ?>
- Файл properties_dialog.php:
<? $MESS ['WRITE_2_LOG'] = "Писать в файл"; $MESS ['ADD_CONDITION'] = "Добавить поле"; ?>
- Файл write2logactivity.php:
<? $MESS ['DIR_NOT_WRITABLE'] = "Папка, в которую сохраняется файл, не доступна для записи"; $MESS ['FILE_NOT_WRITABLE'] = "Файл недоступен для записи"; ?>
Результаты
Заполненная форма действия может выглядеть так:
При включенной опции Писать в файл, лог будет записан в указанный файл. Если указанный файл не существует – он будет создан. Пример записанного лога в файле:
При отключенной опции Писать файл, вывод мы получим в логе бизнес-процесса:
и Ваше мнение важно для нас