installing modules - uniqcle/Bitrix GitHub Wiki
index.php
local/modules/uniqcle.d7/install/index.php
use \Bitrix\Main\Localization\Loc;
use \Bitrix\Main\Config as Conf;
use \Bitrix\Main\Config\Option;
use \Bitrix\Main\Loader;
use \Bitrix\Main\Entity\Base;
use \Bitrix\Main\Application;
Loc::loadMessages(__FILE__); // Подключаем Loc файлы
Class uniqcle_d7 extends CModule {
var $exclusionAdminFiles;
function __construct()
{
$arModuleVersion = array();
include(__DIR__."/version.php");
$this->exclusionAdminFiles=array(
'..',
'.',
'menu.php',
'operation_description.php',
'task_description.php'
);
$this->MODULE_ID = 'uniqcle.d7';
$this->MODULE_VERSION = $arModuleVersion["VERSION"];
$this->MODULE_VERSION_DATE = $arModuleVersion["VERSION_DATE"];
$this->MODULE_NAME = Loc::getMessage("UNIQCLE_D7_MODULE_NAME");
$this->MODULE_DESCRIPTION = Loc::getMessage("UNIQCLE_D7_MODULE_DESC");
$this->PARTNER_NAME = Loc::getMessage("UNIQCLE_D7_PARTNER_NAME");
$this->PARTNER_URI = Loc::getMessage("UNIQCLE_D7_PARTNER_URI");
$this->MODULE_SORT = 1;
$this->SHOW_SUPER_ADMIN_GROUP_RIGHTS='Y';
$this->MODULE_GROUP_RIGHTS = "Y";
}
//Проверяем что система поддерживает D7
public function isVersionD7()
{
return CheckVersion(\Bitrix\Main\ModuleManager::getVersion('main'), '14.00.00');
}
//Определяем место размещения модуля
public function GetPath($notDocumentRoot=false)
{
if($notDocumentRoot)
return str_ireplace(Application::getDocumentRoot(),'',dirname(__DIR__));
else
return dirname(__DIR__);
}
function InstallDB($install_wizard = true){
Loader::includeModule($this->MODULE_ID);
//Проверяем существует ли таблица в БД
if(!Application::getConnection()-> // В качестве параметра может быть \Academy\D7\BookTable::getConnectionName()
isTableExists( Base::getInstance('\Uniqcle\D7\BookTable')->getDBTableName() )){
Base::getInstance('\Uniqcle\D7\BookTable')->createDbTable(); // При описании ORM-сущности, создает таблицу в БД
}
}
function UnInstallDB($arParams = Array()){
Loader::includeModule($this->MODULE_ID);
Application::getConnection()-> // В качестве параметра \Academy\D7\BookTable::getConnectionName()
queryExecute('drop table if exists '.Base::getInstance('\Uniqcle\D7\BookTable')->getDBTableName());
Option::delete($this->MODULE_ID);
}
function InstallEvents()
{
\Bitrix\Main\EventManager::getInstance()->registerEventHandler($this->MODULE_ID, 'TestEventD7', $this->MODULE_ID, '\Uniqcle\D7\Event', 'eventHandler');
}
function UnInstallEvents()
{
\Bitrix\Main\EventManager::getInstance()->unRegisterEventHandler($this->MODULE_ID, 'TestEventD7', $this->MODULE_ID, '\Uniqcle\D7\Event', 'eventHandler');
}
function InstallFiles()
{
$path=$this->GetPath()."/install/components";
if(\Bitrix\Main\IO\Directory::isDirectoryExists($path))
CopyDirFiles($path, $_SERVER["DOCUMENT_ROOT"]."/bitrix/components", true, true);
else
throw new \Bitrix\Main\IO\InvalidPathException($path);
if (\Bitrix\Main\IO\Directory::isDirectoryExists($path = $this->GetPath() . '/admin'))
{
CopyDirFiles($this->GetPath() . "/install/admin/", $_SERVER["DOCUMENT_ROOT"] . "/bitrix/admin"); //если есть файлы для копирования
if ($dir = opendir($path))
{
while (false !== $item = readdir($dir))
{
if (in_array($item,$this->exclusionAdminFiles))
continue;
file_put_contents($_SERVER['DOCUMENT_ROOT'].'/bitrix/admin/'.$this->MODULE_ID.'_'.$item,
'<'.'? require($_SERVER["DOCUMENT_ROOT"]."'.$this->GetPath(true).'/admin/'.$item.'");?'.'>');
}
closedir($dir);
}
}
return true;
}
function UnInstallFiles()
{
\Bitrix\Main\IO\Directory::deleteDirectory($_SERVER["DOCUMENT_ROOT"] . '/bitrix/components/uniqcle/');
if (\Bitrix\Main\IO\Directory::isDirectoryExists($path = $this->GetPath() . '/admin')) {
DeleteDirFiles($_SERVER["DOCUMENT_ROOT"] . $this->GetPath() . '/install/admin/', $_SERVER["DOCUMENT_ROOT"] . '/bitrix/admin');
if ($dir = opendir($path)) {
while (false !== $item = readdir($dir)) {
if (in_array($item, $this->exclusionAdminFiles))
continue;
\Bitrix\Main\IO\File::deleteFile($_SERVER['DOCUMENT_ROOT'] . '/bitrix/admin/' . $this->MODULE_ID . '_' . $item);
}
closedir($dir);
}
}
return true;
}
function DoInstall()
{
global $APPLICATION;
if($this->isVersionD7())
{
\Bitrix\Main\ModuleManager::registerModule($this->MODULE_ID); // Регистрация модуля
$this->InstallDB(); // Создаются таблицы в БД
$this->InstallEvents(); // Регистрация обработчиков
$this->InstallFiles(); // Копирование файлов
#работа с .settings.php
// Кол-во установок хранятся в секции install
$configuration = Conf\Configuration::getInstance();
$uniqcle_module_d7=$configuration->get('uniqcle_module_d7');
$uniqcle_module_d7['install']=$uniqcle_module_d7['install']+1;
$configuration->add('uniqcle_module_d7', $uniqcle_module_d7);
$configuration->saveConfiguration();
#работа с .settings.php
}
else
{
$APPLICATION->ThrowException(Loc::getMessage("UNIQCLE_D7_INSTALL_ERROR_VERSION"));
}
$APPLICATION->IncludeAdminFile(Loc::getMessage("UNIQCLE_D7_INSTALL_TITLE"), $this->GetPath()."/install/step.php");
}
function DoUninstall()
{
global $APPLICATION;
$context = Application::getInstance()->getContext();
$request = $context->getRequest();
if($request["step"]<2)
{
$APPLICATION->IncludeAdminFile(Loc::getMessage("UNIQCLE_D7_UNINSTALL_TITLE"), $this->GetPath()."/install/unstep1.php");
}
elseif($request["step"]==2)
{
$this->UnInstallFiles();
$this->UnInstallEvents();
if($request["savedata"] != "Y")
$this->UnInstallDB();
\Bitrix\Main\ModuleManager::unRegisterModule($this->MODULE_ID);
#работа с .settings.php
// Подсчитываем кол-во удалений
$configuration = Conf\Configuration::getInstance();
$uniqcle_module_d7=$configuration->get('uniqcle_module_d7');
$uniqcle_module_d7['uninstall']=$uniqcle_module_d7['uninstall']+1;
$configuration->add('uniqcle_module_d7', $uniqcle_module_d7);
$configuration->saveConfiguration();
#работа с .settings.php
$APPLICATION->IncludeAdminFile(Loc::getMessage("UNIQCLE_D7_UNINSTALL_TITLE"), $this->GetPath()."/install/unstep2.php");
}
}
function GetModuleRightList()
{
return array(
"reference_id" => array("D","K","S","W"),
"reference" => array(
"[D] ".Loc::getMessage("UNIQCLE_D7_DENIED"), // Доступ закрыт
"[K] ".Loc::getMessage("UNIQCLE_D7_READ_COMPONENT"), // Доступ к компонентам, видеть результат работы компонента
"[S] ".Loc::getMessage("UNIQCLE_D7_WRITE_SETTINGS"), // Доступ к настройкам модуля
"[W] ".Loc::getMessage("UNIQCLE_D7_FULL")) // Полный доступ
);
}
}
step.php
local/modules/uniqcle.d7/install/step.php
use \Bitrix\Main\Localization\Loc;
if (!check_bitrix_sessid())
return;
#работа с .settings.php
// id секции uniqcle_module_d7 для хранения данных
$install_count=\Bitrix\Main\Config\Configuration::getInstance()->get('uniqcle_module_d7');
// Проверяем установлено ли кеширование и выводим на последнем шаге ошибку, если не установлено кеш-е. ID сексции
$cache_type=\Bitrix\Main\Config\Configuration::getInstance()->get('cache');
#работа с .settings.php
// получаем объект, кот. содержит последнее сообщение об ошибке. Метод old core
if ($ex = $APPLICATION->GetException())
echo CAdminMessage::ShowMessage(array(
"TYPE" => "ERROR",
"MESSAGE" => Loc::getMessage("MOD_INST_ERR"), // Сообщение в языковой коснтанте
"DETAILS" => $ex->GetString(), // Текст сообщения об ошибке
"HTML" => true,
));
else // Если не смогли получить объект с последней ошибкой, то выводим сообщение об успешной установке
echo CAdminMessage::ShowNote(Loc::getMessage("MOD_INST_OK"));
#работа с .settings.php
// Вывод инфо о кол-во установок
echo CAdminMessage::ShowMessage(array("MESSAGE"=>Loc::getMessage("UNIQCLE_D7_INSTALL_COUNT").$install_count['install'],"TYPE"=>"OK"));
//Проверка массива и вывод ошибки
if(!$cache_type['type'] || $cache_type['type']=='none')
echo CAdminMessage::ShowMessage(array("MESSAGE"=>Loc::getMessage("UNIQCLE_D7_NO_CACHE"),"TYPE"=>"ERROR"));
#работа с .settings.php
?>
<form action="<?echo $APPLICATION->GetCurPage(); ?>">
<input type="hidden" name="lang" value="<?echo LANGUAGE_ID ?>">
<input type="submit" name="" value="<?echo Loc::getMessage("MOD_BACK"); ?>">
<form>
unstep1.php
local/modules/uniqcle.d7/install/unstep1.php
use \Bitrix\Main\Localization\Loc;
if (!check_bitrix_sessid())
return;
Loc::loadMessages(__FILE__);
?>
<?echo $APPLICATION->GetCurPage()?>
<form action="<?echo $APPLICATION->GetCurPage()?>">
<?=bitrix_sessid_post()?>
<input type="hidden" name="lang" value="<?echo LANGUAGE_ID?>">
<input type="hidden" name="id" value="uniqcle.d7">
<input type="hidden" name="uninstall" value="Y">
<input type="hidden" name="step" value="2">
<?echo CAdminMessage::ShowMessage(Loc::getMessage("MOD_UNINST_WARN"))?>
<p><?echo Loc::getMessage("MOD_UNINST_SAVE")?></p>
<p>
<input type="checkbox" name="savedata" id="savedata" value="Y" checked>
<label for="savedata">
<?echo Loc::getMessage("MOD_UNINST_SAVE_TABLES")?>
</label>
</p>
<input type="submit" name="" value="<?echo Loc::getMessage("MOD_UNINST_DEL")?>">
</form>
unstep2.php
local/modules/uniqcle.d7/install/unstep2.php
use \Bitrix\Main\Localization\Loc;
if (!check_bitrix_sessid())
return;
#работа с .settings.php
$install_count=\Bitrix\Main\Config\Configuration::getInstance()->get('uniqcle_module_d7');
#работа с .settings.php
if ($ex = $APPLICATION->GetException())
echo CAdminMessage::ShowMessage(array(
"TYPE" => "ERROR",
"MESSAGE" => Loc::getMessage("MOD_UNINST_ERR"),
"DETAILS" => $ex->GetString(),
"HTML" => true,
));
else
echo CAdminMessage::ShowNote(Loc::getMessage("MOD_UNINST_OK"));
#работа с .settings.php
echo CAdminMessage::ShowMessage(array("MESSAGE"=>Loc::getMessage("UNIQCLE_D7_UNINSTALL_COUNT").$install_count['uninstall'],"TYPE"=>"OK"));
#работа с .settings.php
?>
<form action="<?echo $APPLICATION->GetCurPage(); ?>">
<input type="hidden" name="lang" value="<?echo LANGUAGE_ID?>">
<input type="submit" name="" value="<?echo Loc::getMessage("MOD_BACK"); ?>">
<form>
явное
if (Loader::includeModule("uniqcle.d7")){
Test::test();
}
с помощью событий
// регистрация обработчика: когда в модуле init_module возникнет событие OnSomeEvent
// будет вызван метод CMyModuleClass::Handler модуля handler_module
RegisterModuleDependences(
"init_module", "OnSomeEvent",
"handler_module", "CMyModuleClass", "Handler"
);
UnRegisterModuleDependences
//при установке
function InstallDB($arModuleParams = array()){
RegisterModule("mymodule");
RegisterModuleDependences("iblock", "OnAfterIBlockElementAdd", "aristov.vtenders", '\Aristov\VTenders\Tools', "saveEventHandler");
...
}
//при деинсталляции модуля
function UnInstallDB() {
COption::RemoveOption("mymodule");
UnRegisterModuleDependences("iblock", "OnAfterIBlockElementAdd", "aristov.vtenders", "Tools", "saveEventHandler");
UnRegisterModule("mymodule");
return true;
}
Bitrix\Main\Config\Configuration
Проверка секции кеширования
local/modules/uniqcle.d7/install.php
// Проверяем установлено ли кеширование и выводим на последнем шаге ошибку, если не установлено кеш-е. ID сексции
$cache_type=\Bitrix\Main\Config\Configuration::getInstance()->get('cache');
...
//Проверка массива и вывод ошибки
if(!$cache_type['type'] || $cache_type['type']=='none')
echo CAdminMessage::ShowMessage(array("MESSAGE"=>Loc::getMessage("UNIQCLE_D7_NO_CACHE"),"TYPE"=>"ERROR"));
Запись и получение своих данных в .settings.php
local/modules/uniqcle.d7/install/index.php
function DoInstall(){
...
// Кол-во установок хранятся в секции install
$configuration = Conf\Configuration::getInstance();
$uniqcle_module_d7=$configuration->get('uniqcle_module_d7');
$uniqcle_module_d7['install']=$uniqcle_module_d7['install']+1;
$configuration->add('uniqcle_module_d7', $uniqcle_module_d7);
$configuration->saveConfiguration();
...
}
function DoUninstall(){
...
// Подсчитываем кол-во удалений
$configuration = Conf\Configuration::getInstance();
$uniqcle_module_d7=$configuration->get('uniqcle_module_d7');
$uniqcle_module_d7['uninstall']=$uniqcle_module_d7['uninstall']+1;
$configuration->add('uniqcle_module_d7', $uniqcle_module_d7);
$configuration->saveConfiguration();
...
}
local/modules/uniqcle.d7/install/step.php
// id секции uniqcle_module_d7 для хранения данных
$install_count=\Bitrix\Main\Config\Configuration::getInstance()->get('uniqcle_module_d7');
...
// Вывод инфо о кол-во установок
echo CAdminMessage::ShowMessage(array("MESSAGE"=>Loc::getMessage("UNIQCLE_D7_INSTALL_COUNT").$install_count['install'],"TYPE"=>"OK"));
...
local/modules/uniqcle.d7/install/unstep2.php
$install_count=\Bitrix\Main\Config\Configuration::getInstance()->get('uniqcle_module_d7');
...
echo CAdminMessage::ShowMessage(array("MESSAGE"=>Loc::getMessage("UNIQCLE_D7_UNINSTALL_COUNT").$install_count['uninstall'],"TYPE"=>"OK"));
.settings.php
...
'uniqcle_module_d7' =>
array (
'value' =>
array (
'uninstall' => 1,
'install' => 1,
),
'readonly' => false,
),
use \Bitrix\Main\Loader;
use \Bitrix\Main\Localization\Loc;
class D7Class extends CBitrixComponent
{
var $test;
protected function checkModules()
{
if (!Loader::includeModule('uniqcle.d7'))
{
ShowError(Loc::getMessage('UNIQCLE_D7_MODULE_NOT_INSTALLED'));
return false;
}
return true;
}
public function executeComponent()
{
$this -> includeComponentLang('class.php');
if($this -> checkModules())
{
/*Ваш код*/
$this->includeComponentTemplate();
}
}
}
Хранение в БД, таблица
b_option
Доступ - класс Bitrix\Main\Config\Option
get($moduleId,$name,$default="",$siteId=false)
set($moduleId,$name,$value="",$siteId=false)
delete($moduleId,$filter=array())
use \Bitrix\Main\Config\Option;
...
echo Option::get('uniqcle.d7', "field_list");
...
function UnInstallDB($arParams = Array())
{
Option::delete($this->MODULE_ID);
return true;
}
Отображение и запись options.php
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Config\Option;
$module_id = 'uniqcle.d7'; //обязательно, иначе права доступа не работают!
// Для того чтобы использовать языковые константы главного модуля
Loc::loadMessages($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/modules/main/options.php");
Loc::loadMessages(__FILE__);
// Проверяем доступ к настройкам модуля, кот. дана группе которой принадлежит текущ. пользователь. Вернет букву
if ($APPLICATION->GetGroupRight($module_id)<"S"){
$APPLICATION->AuthForm(Loc::getMessage("ACCESS_DENIED"));
}
\Bitrix\Main\Loader::includeModule($module_id);
$request = \Bitrix\Main\HttpApplication::getInstance()->getContext()->getRequest();
// Выборка групп
$groups = array();
$z = CGroup::GetList(($v1=""), ($v2=""), array("ACTIVE"=>"Y", "ADMIN"=>"N", "ANONYMOUS"=>"N"));
while($zr = $z->Fetch())
{
$groups[$zr["ID"]] = $zr["NAME"]." [".$zr["ID"]."]";
}
#Описание опций
$aTabs = array(
array(
'DIV' => 'edit1', // id ячейки
'TAB' => Loc::getMessage('UNIQCLE_D7_TAB_SETTINGS'),
'OPTIONS' => array(
// СТРОКИ, ТЕКСТ
Loc::getMessage('UNIQCLE_D7_TAB_STRINGS'),
array('field_line', // Ключ под которым будет сохраняться опция
Loc::getMessage('UNIQCLE_D7_FIELD_LINE_TITLE'), // Подпись поля
'', // Значение по умолчанию
array('text', 10)), // Описывает поле формы
array('field_line1',
Loc::getMessage('UNIQCLE_D7_FIELD_LINE_TITLE'),
'default',
array('text', 10),
'',
'поясняющий текст'),
array('field_line2',
Loc::getMessage('UNIQCLE_D7_FIELD_LINE_TITLE'),
'default',
array('text', 10),
'Y'), // readonly
// пароль
array('field_password', Loc::getMessage('UNIQCLE_D7_FIELD_PASSWORD_TITLE'),
'',
array('password', 10)),
// Текстовая область
array('field_text', Loc::getMessage('UNIQCLE_D7_FIELD_TEXT_TITLE'),
'',
array('textarea', 10, 50)),
Loc::getMessage('UNIQCLE_D7_TAB_LISTS_CHECKBOX'), // Списки, чекбоксы
// SELECT, MULTISELECT
array('field_list',
Loc::getMessage('UNIQCLE_D7_FIELD_MULTILIST_TITLE'),
'', // Выбранное значение по умолч. var2
array('multiselectbox',
$groups)
),
array('field_list', Loc::getMessage('UNIQCLE_D7_FIELD_LIST_TITLE'),
'var2',
array('selectbox',
array('var1'=>'Первый',
'var2'=>'Второй',
'var3'=>'Третий',
'var4'=>'Четвертый'))
),
array("field_checkbox_clicked",
'Подпись чекбокс',
"", // Отмеченная галка Y
array("checkbox",
'',
'onclick="alert(\'testing\');"')
),
array("field_checkbox_inactive",
'Подпись чекбокс',
"",
array("checkbox"),
'Y' // Флаг неактивности
),
// СТАТИЧЕСКИЙ ТЕКСТ, HTML, ПОДПИСИ
Loc::getMessage('UNIQCLE_D7_STATIC_TEXT'),
array("field_statictxt",
'Подпись текст:',
"текст по <b>умолч.</b>",
array("statictext")
),
array("field_statictxt",
'Подпись html:',
"текст по <b>умолч.</b>",
array("statichtml")
),
array("note" => "Поясняющий текст")
)
),
array(
"DIV" => "edit2",
"TAB" => Loc::getMessage("MAIN_TAB_RIGHTS"),
"TITLE" => Loc::getMessage("MAIN_TAB_TITLE_RIGHTS")
),
);
#СОХРАНЕНИЕ
// Проверяем была ли передача методом пост, была ли передана переменная save, а также актуальность сессии
if ($request->isPost() && $request['save'] && check_bitrix_sessid())
{
foreach ($aTabs as $aTab)
{
foreach ((array)$aTab['OPTIONS'] as $arOption)
{
if (!is_array($arOption)) //Исключаем разделения настроек в одной вкладке
continue;
if ($arOption['note']) //Исключаем уведомление с подсветкой
continue;
// __AdmSettingsSaveOption($module_id, $arOption); // Сохраняет не пустые значения
// или так, но сохраняет и пустые
$optionName = $arOption[0];
$optionValue = $request->getPost($optionName);
Option::set($module_id, $optionName, is_array($optionValue) ? implode(",", $optionValue):$optionValue);
}
}
CAdminMessage::ShowMessage(array("MESSAGE" => "Значения сохранены", "TYPE" => "OK"));
//LocalRedirect($APPLICATION->GetCurPage()."?mid=".$module_id."&lang=".LANG);
}
#ВИЗУАЛЬНЫЙ ВЫВОД
$tabControl = new CAdminTabControl('tabControl', $aTabs); // id формы, вкладки
$tabControl->Begin(); // Открываем форму
?>
<form method='post' action='<?echo $APPLICATION->GetCurPage()?>?mid=<?=htmlspecialcharsbx($request['mid'])?>&lang=<?=$request['lang']?>' name='uniqcle_d7_settings'>
<?php
foreach ($aTabs as $aTab):
if($aTab['OPTIONS']):
$tabControl->BeginNextTab(); // Завершает предыдущую закладку, если она есть, и начинает следующую.
__AdmSettingsDrawList($module_id, $aTab['OPTIONS']);
endif;
endforeach;
$tabControl->BeginNextTab(); // Вывод следующей вкладки
//Добавляем управление правами доступа
require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/admin/group_rights.php");
$tabControl->Buttons(); ?>
<input type="submit" name="save" value="<?echo GetMessage('MAIN_SAVE')?>">
<input type="reset" name="reset" value="<?echo GetMessage('MAIN_RESET')?>">
<?=bitrix_sessid_post();?>
</form>
<? $tabControl->End(); // Закрываем форму ?>
...
// Проверяем доступ к настройкам модуля, кот. дана группе которой принадлежит текущ. пользователь. Вернет букву
if ($APPLICATION->GetGroupRight($module_id)<"S"){
$APPLICATION->AuthForm(Loc::getMessage("ACCESS_DENIED"));
}
Группы пользователя
->Доступы
меняет уровень доступа к модулю.
- Доступ закрыт
- Доступ к компонентам // Отображает результат работы компонента
- Изменение настроек доступа
- Полный доступ
Если у группы, напр. "редакторы" уровень доступа к компонентам, то при наличии в коде компонента проверки, работа будет отображена для данной группы
if($APPLICATION->GetGroupRight("uniqcle.d7")<"K"){
ShowError(Loc::getMessage("ACCESS_DENIED"));
} else {
$this->arResult = $this->var1();
$this->includeComponentTemplate();
}
Класс компонента с проверкой уровня доступа
use \Bitrix\Main;
use \Bitrix\Main\Localization\Loc;
class d7Right extends CBitrixComponent
{
/**
* проверяет подключение необходиимых модулей
* @throws LoaderException
*/
protected function checkModules()
{
if (!Main\Loader::includeModule('uniqcle.d7'))
throw new Main\LoaderException(Loc::getMessage('UNIQCLE_D7_MODULE_NOT_INSTALLED'));
}
function var1()
{
$arResult='У вас есть доступ к компоненту и здесь может быть ваш исполняемый код';
return $arResult;
}
public function executeComponent()
{
global $APPLICATION;
$this -> includeComponentLang('class.php');
$this -> checkModules();
if($APPLICATION->GetGroupRight("uniqcle.d7")<"K")
{
ShowError(Loc::getMessage("ACCESS_DENIED"));
}
else
{
$this->arResult = $this->var1();
$this->includeComponentTemplate();
}
}
}
https://tokmakov.msk.ru/blog/item/207