app = $app; $this->pageSettingsDefaults = [ 'title' => '', # заголовок страницы 'custom' => false, # обвертка для основного контента не требуется 'attr' => [], # доп. атрибуты блока 'link' => [], # ссылка в шапке блока, справа 'icon' => null, # ключ иконки в шапке блока (false - список; true - форма; string - ключ) 'fordev' => [], # список доп. ссылок в режиме разработчика 'linkout' => [], # внешняя ссылка ['title', 'link'] ]; $this->pageSettings = $this->pageSettingsDefaults; } public function onNewRequest($request) { $this->pageSettings = $this->pageSettingsDefaults; $this->pageSettingsOnce = []; } /** * Путь в админ-панель * @param bool $asUri * @return string */ public function path(bool $asUri = false) { if (is_null($this->path)) { $this->path = trim($this->app->config('session.admin.path', 'admin', TYPE_STR), " \t\n\r\0\x0B/?"); if (mb_strlen($this->path) <= 2) { $this->path = 'admin'; } } if ($asUri) { return '/' . $this->path . '/'; } return $this->path; } /** * Determine if request to admin panel * @param \bff\http\Request $request * @return bool */ public function isAdminRequest($request) { if (defined('BFF_ADMINPANEL')) { return true; } $path = $request->hostSubdir($this->path()); $uri = trim(preg_replace("/^(.*)\?.*$/U", '$1', $request->uri()), '/'); return ( $uri === $path || $uri === $path . '/index.php' ); } /** * Determine if admin developers mode is enabled. * @return bool */ public function fordev() { return $this->app->adminFordev(); } /** * Версия оформления admin-панели * @param string|null $compare * @return string|bool */ public function theme(?string $compare = null) { return $this->app->adminTheme($compare); } /** * Текущая версия оформления admin-панели - v2 * @return bool */ public function themeV2() { return $this->app->adminTheme('v2'); } /** * Формирование URL * @param string|null $path в формате /{module}/{method}/{action}?{param}= * @param array $query параметры передаваемые в URL * @param string|bool $escape выполнять квотирование, false - не выполнять, 'html', 'js' * @return string строка вида index.php?s={module}&ev={method}&act={action}&{param}= */ public function url(?string $path = '', array $query = [], $escape = false) { return Url::admin($path, $query, $escape); } /** * Page crumbs * @param array|string $crumbs * @param array $opts [separator] * @return string */ public function crumbs($crumbs = [], array $opts = []) { if (! is_array($crumbs)) { $crumbs = [$crumbs]; } foreach ($crumbs as $key => $crumb) { if ($crumb instanceof Closure) { $crumbs[$key] = $crumb(); } } return join($opts['separator'] ?? ' / ', $crumbs); } /** * Меню * @return \bff\admin\Menu */ public function menu() { return $this->app->adminMenu(); } /** * Настройки активной страницы * @param array|null $settings * @param bool $rewrite * @return array */ public function pageSettings(?array $settings = null, bool $rewrite = true) { if (is_array($settings)) { foreach ($settings as $key => $value) { if (! $rewrite && in_array($key, $this->pageSettingsOnce, true)) { continue; } $this->pageSettings[$key] = $value; $this->pageSettingsOnce[] = $key; } } return $this->pageSettings; } /** * Форма * @param Module|BlockList $controller объект контроллера или объект списка, в который будет добавлятся форма * @param string|null $template название php шаблона контроллера в котором выполняется инициализация формы * @param array $opts доп. параметры: [ * string|bool 'action' название метода контроллера или false функция в которой данный метод был вызван * string 'version' версия шаблонов для рендеринга блока * string 'templateDir' путь к шаблону или false - используем путь к шаблонам текущего модуля * array 'data' массив данных, доступных в шаблоне * BlockList 'list' объект списка, в который будет добавлятся форма * (указывать если не совпадают модули списка и формы ) * @return Form */ public function form($controller, ?string $template = null, array $opts = []) { $opts = $this->defaults($opts, [ 'action' => false, 'version' => $this->theme(), 'id' => false, 'title' => '', ]); $action = $opts['action'] ?? ''; $list = false; if ($controller instanceof BlockList) { $list = $controller; $controller = $controller->getController(); if (empty($action)) { $action = $list->getControllerAction(); } } if (isset($opts['list'])) { $list = $opts['list']; if ($list instanceof BlockList) { if (empty($action)) { $action = $list->getControllerAction(); } } unset($opts['list']); } if (empty($action) && method_exists($controller, 'getControllerAction')) { $action = $controller->getControllerAction(); } if (empty($action)) { $action = $this->app->router()->controllerMethod(); } if (empty($action)) { $action = $this->detectAction($controller, $opts['depth'] ?? 5); } if ($opts['id'] == false) { if (! isset($this->formCounter[$action])) { $this->formCounter[$action] = 1; } $opts['id'] = $this->formCounter[$action]++; } if (isset($opts['_create']) && is_callable($opts['_create'])) { $form = call_user_func($opts['_create'], [ 'id' => $opts['id'], 'controller' => $list ? $list->getController() : $controller, 'action' => $action, ]); } else { $form = new Form($opts['id'], $list ? $list->getController() : $controller, $action); } $form->init(); if (! empty($template)) { $data = $opts['data'] ?? []; $data['form'] = $form; $dir = $opts['templateDir'] ?? ''; $controller->template($template, $data, ['path' => $dir]); } $form->trigger('inited', ['opts' => $opts]); return $form; } /** * Определить action методом debug_backtrace * @param Module $controller объект контроллера * @param int $depth * @return string */ public function detectAction(Module $controller, int $depth = 5) { $i = $depth; $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $i); $i--; $class = get_class($controller); if (isset($bt[$i]['function']) && isset($bt[$i]['class'])) { if ($bt[$i]['class'] == $class) { return $bt[$i]['function']; } $parents = class_parents($controller); if (isset($parents[ $bt[$i]['class'] ])) { return $bt[$i]['function']; } } $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); foreach ($bt as $v) { if (empty($v['function']) || mb_strpos($v['function'], 'closure') !== false) { continue; } if (empty($v['class'])) { continue; } if ($v['class'] == $class) { return $v['function']; } } if (empty($parents)) { $parents = class_parents($controller); } foreach ($bt as $v) { if (empty($v['function']) || mb_strpos($v['function'], 'closure') !== false) { continue; } if (empty($v['class'])) { continue; } if (isset($parents[$v['class']])) { return $v['function']; } } throw new AdminException('Can\'t detect action for controller: ' . $class); } /** * Список * @param Module $controller объект контроллера * @param string|null $template название php шаблона контроллера в котором выполняется инициализация списка * @param array $opts доп. параметры: [ * string|bool 'action' название метода контроллера или false функция в которой данный метод был вызван * string 'version' версия шаблонов для рендеринга блока * string 'templateDir' путь к шаблону или false - используем путь к шаблонам текущего модуля * array 'data' массив данных, доступных в шаблоне * @return BlockList */ public function list(Module $controller, ?string $template = null, array $opts = []) { $opts = $this->defaults($opts, [ 'id' => '', 'action' => false, 'version' => $this->theme(), ]); $action = $opts['action'] ?? ''; if (empty($action)) { $action = $this->app->router()->controllerMethod(); } if (empty($action)) { $action = $this->detectAction($controller, $opts['depth'] ?? 5); } $list = new BlockList($controller, $action, $opts['id'] ?: ''); $list->setTemplateDir($this->app->corePath('tpl/admin/' . $opts['version'])); $list->init(); if (isset($opts['onBeforeView']) && is_callable($opts['onBeforeView'])) { $list->onBeforeView($opts['onBeforeView']); } if (! empty($template)) { $data = $opts['data'] ?? []; $data['list'] = $list; $dir = $opts['templateDir'] ?? ''; $controller->template($template, $data, ['path' => $dir]); } $list->trigger('inited', ['opts' => $opts]); return $list; } /** * Блок: открытие * @param string $title заголовок блока * @param string|bool|null $icon true - иконка списка; false - иконка формы; string - ключ иконки; null - определяем * @param array $attr доп. атрибуты блока * @param array $link ссылка * @param array $fordev доп. ссылки в режиме fordev() * @param array $opts опции * @return string */ public function blockStart( string $title, $icon = true, array $attr = [], array $link = [], array $fordev = [], array $opts = [] ): string { return tplAdmin::blockStart($title, $icon, $attr, $link, $fordev, $opts); } /** * Блок: закрытие * @return string */ public function blockStop(): string { return tplAdmin::blockStop(); } /** * Формирование access scopes списка * @param Module|string $module * @param string|array $title * @return AccessScopes */ public function accessScopes($module, $title = []) { return new AccessScopes($module, $title); } }