В Ubilling починаючи зі стабільного релізу 1.2.0 з'явилася можливість використовувати у ваших модулях загальну реалізацію стигматизації об'єктів. Власне, стигми являють собою якісь стани якихось рандомних об'єктів (items) у межах якоїсь рандомної категорії (scope). Використання API Stigma найближче за духом до API ADcomments.
Найпростіше пояснити роботу цього механізму на прикладі “а давайте зробимо можливість десь, у якомусь нашому модулі, відзначати, чи купив користувач у нас роутер, чи він жадібна свиня?”. Конфігурація цієї стигми починається з написання конфігураційного файлу, який зберігатиметься в config/stigma/, нехай це буде userbuyrouter.ini через запланований нами SCOPE “USERBUYROUTER”. Так, ім'я файлика конфігурації стигми для конкретного scope-а за замовчуванням - це цей самий scope у lowercase із розширенням *.ini. Власне він і буде у нас читатися при створенні об'єкта стигми з відповідним SCOPE.
[stigmasettings] TYPE=radiolist BASECLASS=dashtask ACTIVECLASS=todaysig ANIMATION=1 RENDERER=iconic [buy] NAME="Bought a router" ICON=sellrouter [nobuy] NAME="Greedy pig" ICON=pig [unknown] NAME="We dont know"
Секція “stigmasettings” є службовою та обов'язковою. Вона вказує на базову поведінку контролів у конкретному скоупі. Можливі типи (TYPE) на даний момент “radiolist” (вибір одного з усіх) або “checklist” (множинна вибірка). ANIMATION просто вказує, чи оновлювати з супутньою анімацією контейнер стигмати під час зміни стану, чи робити це “тихо і непомітно”. Оскільки стан “купив у нас роутер” не може бути одночасно і “купив”, і “не купив”, ми використовуємо тип “radiolist”. Опція ACTIVECLASS вказує просто, яким CSS-класом будуть підсвічуватися активні/вибрані стани об'єктів у межах цього scope. Починаючи з Ubilling 1.4.1 також можна опційно вказувати і BASECLASS, який буде використовуватись для всіх контролів за замовчуванням. Так, це теж ім'я CSS класу.
Далі всі секції, які не є stigmasettings, описують конкретні стани об'єктів (items) у межах конкретного SCOPE. Їх може бути скільки завгодно. Ім'я секції власне буде ідентифікатором стану, що зберігається в БД, і має бути якомога коротшим та унікальним. Базовими характеристиками стану крім його ідентифікатора є NAME (ім'я) і ICON (ви не повірите! іконка). Іконки станів за замовчуванням зберігаються в skins/stigma/ з розширенням *.png.
Крім усього цього, реалізовано механіку підвантаження кастомних файлів конфігурації та іконок, на випадок якщо користувачі самі захочуть розширити набір станів, або замінити іконки на якісь свої, і щоб це все пережило оновлення. Кастомні файли конфігурації та іконки, що мають пріоритет над замовчуваними, зберігатимуться в content/documents/mystigma у сабдиректоріях confs і icons
Повертаючись до початкової задачі, ми хочемо ловити GET параметром логін якогось користувача і виставляти йому стан.
if (ubRouting::checkGet('username')) { $userLogin = ubRouting::get('username'); //створюємо інстанс із потрібним нам scope-ом. $userRouter = new Stigma('USERBUYROUTER'); //викличемо обробник фонових коллбеків на випадок, якщо у нас зміниться стан $userRouter->stigmaController(); //показуємо інтерфейс зміни стану для нашого конкретного користувача show_window(__('User bought a router'), $userRouter->render($userLogin)); }
Ну і власне одразу отримуємо результат у вигляді панелі управління станами користувача.
А може хочемо меншу панельку?
show_window(__('User bought a router'), $userRouter->render($userLogin, '64'));
типу такої?
А може хочемо read-only панельку для адміністраторів без якогось права, але яка нормально працює для адміністраторів, наділених цими правами? Тоді якось так
if (ubRouting::checkGet('username')) { $userLogin = ubRouting::get('username'); $userRouter = new Stigma('USERBUYROUTER'); $userRouterReadOnlyFlag = true; if (cfr('USERROUTEREDIT')) { $userRouter->stigmaController(); $userRouterReadOnlyFlag = false; } show_window(__('User bought a router'), $userRouter->render($userLogin, '64', $userRouterReadOnlyFlag)); }
А якщо ми хочемо просто десь в іншому місці показати тупо список наявних станів цього користувача?
show_window(__('User bought a router as text'), $userRouter->textRender($userLogin);
типу так
чи взагалі тупо перевірити чи є якісь встановлені стани й отримати їх у вигляді масиву для наших подальших дофіга концептуальних бізнес-додатків?
if ($userRouter->haveState($userLogin)) { $routerStates = $userRouter->getItemStates($userLogin); }
А ще ми можемо отримати всі можливі для скоупа стани з їхніми текстовими описами ось якось так
$userRouter = new Stigma('USERBUYROUTER'); $allStates=$userRouter->getAllStates(); debarr($allStates);
Коротше все настільки прямолінійно і брутально, наскільки це можливо. А взагалі поглянути на публічні методи і що у них у параметрах буде набагато продуктивніше і зрозуміліше.
У прикладі вище, ми створювали інстанс стигми як
$userRouter = new Stigma('USERBUYROUTER');
Що зі свого боку завантажувало з бази всі стани всіх айтемів у цьому scop-і. Це може бути корисно, коли ми одним і тим самим інстансом намагаємося працювати з різними айтемами одним інстансом. Наприклад читаючи/перевіряючи наявність станів для різних айтемів одним і тим же інстансом. Типу як
$allUsers = zb_UserGetAllDataCache(); $userRouter = new Stigma('USERBUYROUTER'); if (!empty($allUsers)) { foreach ($allUsers as $eachLogin => $eachData) { if ($userRouter->haveState($eachLogin)) { $routerStates = $userRouter->getItemStates($eachLogin); if (isset($routerStates['buy'])) { show_success($eachLogin); } if (isset($routerStates['nobuy'])) { show_warning($eachLogin . ' ' . __('gotcha!')); } } } }
але в нашому першому прикладі, логічно було б завантажувати з БД тільки стан одного конкретного, що цікавить нас айтема, а точніше, конкретного користувача, якого ми зловили в GET-параметрі username. У такому разі створення нашого інстансу мало б виглядати так:
$userRouter = new Stigma('USERBUYROUTER', $userLogin);
що призведе до завантаження даних про стани тільки для айтема $userLogin у межах scop-а USERBUYROUTER у процесі створення інстансу, і цілком собі дасть нам змогу з ними працювати. Але тільки ось для цього конкретного $userLogin.
Базово, підтримується три різноманітні режими логування змін станів, якщо вам це необхідно: TASKMAN, SYSTEM, CUSTOM.
Для логування до довільної таблиці, її структура має мати наступний формат:
CREATE TABLE IF NOT EXISTS `somecustomlog` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `date` datetime NOT NULL, `admin` VARCHAR(64) DEFAULT NULL, `scope` VARCHAR(64) DEFAULT NULL, `itemid` VARCHAR(128) NOT NULL, `action` VARCHAR(32) DEFAULT NULL, `state` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `scope` (`scope`), KEY `itemid` (`itemid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
Логування TASKMAN: ви не використовуватимете з ймовірністю 99%, з логуванням SYSTEM:[стан] який ви можете ввімкнути при виклику обробника коллбеків, наступним чином:
$leadSource->stigmaController('SYSTEM:якийсь_стан');
в системному лозі з'являтимуться записи наступного вигляду
STIGMA [поточний_SCOPE] CHANGE [ідентифікатор_айтема] `[якийсь_стан]` ON `[ідентифікатор_стану]`
Чим буде заповнюватись табличка somecustomlog при логуванні CUSTOM:somecustomlog викликаному отак:
$leadSource->stigmaController('CUSTOM:somecustomlog');
теж доволі зрозуміло з іменування її полів.
Починаючи з Ubilling 1.4.1 в секції конфігурації [stigmasettings] також можна опційно встановити опцію RENDERER, яка прямо вказує на тип відображення контролів конкретної стигми.
Можливі значення опції на даний момент: