list = $list; $this->setParent($list); } /** * Вкючение / отключения режима массовых операций со списком * @param bool|null $enabled * @return bool|self */ public function enabled(?bool $enabled = null) { if (is_null($enabled)) { return $this->enabled; } $this->enabled = $enabled; return $this; } /** * Установка / получения id key * @param string|null $idKey * @return string|self */ public function idKey(?string $idKey = null) { if (is_null($idKey)) { return $this->idKey; } $this->idKey = $idKey; return $this; } /** * Установка / получения action key * @param string|null $actionKey * @return string|self */ public function actionKey(?string $actionKey = null) { if (is_null($actionKey)) { return $this->ajaxActionKey; } $this->ajaxActionKey = $actionKey; return $this; } /** * Установка / получения типа ID для валидации * @param int|null $type * @return int|self */ public function idType(?int $type = null) { if (is_null($type)) { return $this->idType; } $this->idType = $type; return $this; } /** * Вкючение / отключение возможности выделения всех строк * @param bool|null $selectAll * @return bool|self */ public function selectAll(?bool $selectAll = null) { if (is_null($selectAll)) { return $this->selectAll; } $this->selectAll = $selectAll; return $this; } /** * Инициализация событий */ public function initActions() { if ($this->actionsInited) { return; } $this->actionsInited = true; func::sortByPriority($this->actions); foreach ($this->actions as & $v) { static::attrAdd($v['attr'], 'data-id', $v['id']); if (! empty($v['tabs'])) { $tabs = $v['tabs']; if (is_callable($tabs)) { $tabs = call_user_func($tabs); } if (is_scalar($tabs)) { $tabs = [$tabs]; } Block::attrAdd($v['attr'], 'data-tabs', join(',', $tabs)); } if (! empty($v['alias'])) { Block::attrAdd($v['attr'], 'data-alias', $v['alias']); } if (! empty($v['form'])) { /** @var Form $form */ $form = $v['form']; $controller = $form->getController(); $action = $form->getControllerAction(); if (! method_exists($controller, $action)) { $form->setController($this->list->getController()); $form->setControllerAction($this->list->getControllerAction()); } if (! $form->issetSave()) { $form->onSave(function ($id, $data){ return 1; }); } $form->wrapper()->popup(true); if ($form->buttonsDefault()) { $form->buttonSubmit('', null, ['priority' => 10]); $form->buttonCancel('', ['priority' => 100]); } $v['form_id'] = '#' . $form->cssClass('form'); $v['form_html'] = $form->view(); } if (! empty($v['afterJS'])) { $js = ''; static::obCallable($js, $v['afterJS'], function($callable) { return call_user_func($callable); }, ['js' => true, 'function' => true]); $v['afterJS'] = function () use ($js) { return $js; }; } } unset($v); } /** * Получение массива событий (для отрисовки) * @return array */ public function getActions() { $this->initActions(); return $this->actions; } /** * @param string $id ID операции * @return bool */ public function isAllowed($id) { if (! isset($this->actions[$id])) { return false; } if (empty($this->actions[$id]['access'])) { return true; } if (is_string($this->actions[$id]['access'])) { return $this->controller->haveAccessTo($this->actions[$id]['access']); } if (is_callable($this->actions[$id]['access'])) { return (bool)call_user_func($this->actions[$id]['access']); } return (bool)$this->actions[$id]['access']; } /** * Установить заначение свойства для текущей массовой операции * @param string $name имя свойства * @param mixed $value значение, если null - вернуть значение свойства для текущего массовые операции * @return self|mixed */ protected function _prop($name, $value = null) { if (isset($this->actions[$this->current])) { if (is_null($value)) { if (isset($this->actions[$this->current][$name])) { return $this->actions[$this->current][$name]; } return null; } else { $this->actions[$this->current][$name] = $value; } } return $this; } /** * Добавление массовой операции * @param string $id ID операции * @param string $title название операции * @param array $opts параметры * @return $this */ public function add($id, $title, array $opts = []) { if (isset($this->actions[$id])) { return $this->to($id); } $opts['id'] = $id; $opts['title'] = $title; if(! isset($opts['priority'])) { $opts['priority'] = (count($this->actions) + 1) * 10; } $opts = array_merge($opts, [ 'attr' => [], 'action' => null, 'form' => null, 'tabs' => false, 'alias' => '', 'afterJS' => null, 'access' => null, ]); $this->actions[ $opts['id'] ] = $opts; $this->current = $opts['id']; return $this; } /** * Выбор операции по ID, для добавления свойств * @param $id * @return $this */ public function to($id) { if (isset($this->actions[$id])) { $this->current = $id; } return $this; } /** * Установить или получить название текущей операции * @param null $title null - получить, $title - установить название операции * @return $this|mixed|null */ public function title($title = null) { return $this->_prop('title', $title); } /** * Установить или получить порядок сортировки текущей операции * @param null $priority null - получить, $priority - установить * @return $this|mixed|null */ public function priority($priority = null) { return $this->_prop('priority', $priority); } /** * Установить или получить список ID табов, в которых доступна текущая массовая операция * @param null $tabs null - получить, $tabs - установить * @return $this|mixed|null */ public function tabs($tabs = null) { if (! is_null($tabs) && empty($tabs)) { $tabs = [$tabs]; } return $this->_prop('tabs', $tabs); } /** * Установить или получить синоним текущей операции (будет использоваться обработчик (action) от основной операции, для алиаса экшин устанавливать не надо) * @param null $id null - получить, $id - установить * @return $this|mixed|null */ public function alias($id = null) { return $this->_prop('alias', $id); } /** * Установить или получить доступ текущей операции * @param string|callable|null $access доступ null - получить * @return $this|mixed|null */ public function access($access = null) { return $this->_prop('access', $access); } /** * Установка функции javascript, выполняемой после отправки экшена * @param callable $callback функция, генерирующая javascript => * function(resp, params){}; resp - ajaxResponse от action, params - ajax параметры, которые использовались при вызове action * @param array $opts доп. параметры ['ob'] * bool 'ob' - форсировать использование буферизации вывода * @return self */ public function afterJS(callable $callback, array $opts = []) { if (isset($this->actions[$this->current])) { $opts['callable'] = $callback; $this->actions[$this->current]['afterJS'] = $opts; } return $this; } /** * Добавить класс для текущей операции (обычно для выделения цвета) * @param string $class * @return $this */ public function classAdd($class) { if (isset($this->actions[$this->current])) { static::attrAdd($this->actions[$this->current]['attr'], 'class', $class); } return $this; } /** * Удалить класс для текущей операции * @param $class * @return $this */ public function classRemove($class) { if (isset($this->actions[$this->current])) { static::attrRemove($this->actions[$this->current]['attr'], 'class', $class); } return $this; } /** * Установка обработчика действия для масовой операции * @param string|int|callable $id ID операции или обработчик для текущей операции * @param callable|null $action обработчик для операции аргументы($list, $opts) $list = array(список выделенных id) или string 'all', * должен вернуть array['message' - сообщение об успешной операции] * @return self */ public function action($id, callable $action = null) { if (is_callable($id)) { $action = $id; $id = $this->current; } if (isset($this->actions[$id])) { if (is_callable($action)) { $this->actions[$id]['action'] = $action; } } if ($this->list->subAction() == $this->list::ACTION_MASS) { $act = $this->input->postget($this->ajaxActionKey, TYPE_NOTAGS); if ($act == $id) { $response = []; do { if (! $this->isAllowed($id)) { $this->errors->accessDenied(); break; } $opts = ['action' => $id]; $all = $this->input->postget('all', TYPE_UINT); if ($all) { $list = static::ALL; } else { $type = $this->idType(); if ($type < TYPE_CONVERT_SINGLE) { $type += TYPE_CONVERT_SINGLE; } $list = $this->input->postget('list', $type); if (empty($list)) { $this->errors->impossible(); break; } } if ($this->actions[$id]['form']) { /** @var Form $form */ $form = $this->actions[$id]['form']; $opts['form'] = $form->_validate(); } $response = call_user_func($this->actions[$id]['action'], $list, $opts); if ($response === false) { return $this; } } while(false); $this->ajaxResponseForm($response ?? []); } } return $this; } /** * Установка единного обработчика действия для всех массовых операции * @param callable $handler обработчик для операции аргументы($list, $opts) $list = array(список выделенных id) или string 'all', * должен вернуть array['message' - сообщение об успешной операции] * @return self */ public function allActionsHandler(callable $handler) { foreach ($this->actions as $k => $v) { $this->action($k, $handler); } return $this; } /** * Установить форму для текущей масовой операции. Форма откроется в виде попапа, для задания доп. параметров * Вначале произойдет событие сабмита формы, и если не ошибок, то вызовется action операции. * @param Form $form объект с формой * @return $this */ public function form(Form $form) { if (isset($this->actions[$this->current])) { $this->actions[$this->current]['form'] = $form; } return $this; } }