From 6784cecf5e10d5ee5d032715cd313b85fa1fe7c8 Mon Sep 17 00:00:00 2001 From: dimti Date: Mon, 11 May 2020 06:11:33 +0300 Subject: [PATCH] Initial --- .gitignore | 1 + Plugin.php | 205 +++++++++++++++++++ README-RU.md | 41 ++++ README.md | 41 ++++ assets/css/_wms_extend_menu.css | 11 ++ classes/Extend.php | 218 +++++++++++++++++++++ controllers/Index.php | 27 +++ controllers/index/index.htm | 14 ++ helpers/Menu.php | 16 ++ lang/en/lang.php | 5 + lang/en/plugin.php | 8 + lang/ru/lang.php | 5 + lang/ru/plugin.php | 8 + partials/menu/main/_system_sidebar.phtml | 36 ++++ partials/menu/main/_system_sidebar_menu.phtml | 52 +++++ .../menu/main/_system_sidebar_menu_toolbar.phtml | 14 ++ partials/menu/secondary/_sidenav.phtml | 81 ++++++++ .../menu/secondary/_sidenav_sidebar_menu.phtml | 31 +++ routes.php | 9 + updates/version.yaml | 1 + 20 files changed, 824 insertions(+) create mode 100644 .gitignore create mode 100644 Plugin.php create mode 100644 README-RU.md create mode 100644 README.md create mode 100644 assets/css/_wms_extend_menu.css create mode 100644 classes/Extend.php create mode 100644 controllers/Index.php create mode 100644 controllers/index/index.htm create mode 100644 helpers/Menu.php create mode 100644 lang/en/lang.php create mode 100644 lang/en/plugin.php create mode 100644 lang/ru/lang.php create mode 100644 lang/ru/plugin.php create mode 100644 partials/menu/main/_system_sidebar.phtml create mode 100644 partials/menu/main/_system_sidebar_menu.phtml create mode 100644 partials/menu/main/_system_sidebar_menu_toolbar.phtml create mode 100644 partials/menu/secondary/_sidenav.phtml create mode 100644 partials/menu/secondary/_sidenav_sidebar_menu.phtml create mode 100644 routes.php create mode 100644 updates/version.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/Plugin.php b/Plugin.php new file mode 100644 index 0000000..9eb1305 --- /dev/null +++ b/Plugin.php @@ -0,0 +1,205 @@ + null, + 'sideMenu' => [], + 'subMenus' => [], + 'permissions' => [], + ]; + + public function pluginDetails() + { + return [ + 'name' => 'wms.menu::plugin.name', + 'description' => 'wms.menu::plugin.description', + 'author' => 'WMStudio', + 'icon' => self::ICON, + ]; + } + + public function register() + { + BackendController::extend(function($controller) { + /* @var $controller BackendController */ + $controller->addDynamicMethod('getMenu', function($name) { + return isset($this->navigation['subMenus'][$name]) ? $this->navigation['subMenus'][$name] : null; + }); + }); + + /** + * Скрывает содержимое заголовка или после символа `, или между символами `` + * прим: + * "Заголовок `Скрытый тектс" + * "`Скрытый текст` Заголовок" + */ + CmsPage::extend(function($page) { + /* @var $page CmsPage */ + $page->addDynamicMethod('getTitle', function($withHidden = false) use (&$page) { + $title = $page->title; + if (($pos = mb_strpos($title, '`')) === false) { + return $title; + } + if ($withHidden) { + return str_replace('`', '', $title); + } else { + $result = mb_substr($title, 0, $pos); + $title = mb_substr($title, $pos + 1); + if (($pos = mb_strpos($title, '`')) === false) { + return $result; + } + + return trim($result . mb_substr($title, $pos + 1)); + } + }); + }); + + \Event::listen('cms.page.beforeRenderPage', function($controller, $page) { + /* @var $page CmsPage */ + $config = $page->theme->getConfig(); + $page->theme->menu = $this->getMenu(optional($config)['menu'] ?: [], !!optional($config)['pageTitleMoreImportant']); + }); + } + + public function registerNavigation() + { + list($navigation, $defaultMenuName) = Classes\Extend::collectNavigation($this->defaultMenuName, self::ICON); + if ($navigation) { + $this->navigation = $navigation; + } + if ($defaultMenuName) { + $this->defaultMenuName = $defaultMenuName; + } + + $navigation = []; + if (count($this->navigation['sideMenu'])) { + \BackendMenu::registerContextSidenavPartial('Wms.Menu', 'main', '~/plugins/wms/menu/partials/menu/main/_system_sidebar.phtml'); + $navigation['main'] = [ + 'label' => 'wms.menu::plugin.name', + 'url' => $this->navigation['first'] ?: \Backend::url('wms/menu'), + 'icon' => self::ICON, + 'permissions' => array_keys($this->navigation['permissions']), + 'order' => 999, // before CMS Settings + 'sideMenu' => $this->navigation['sideMenu'], + ]; + + if (is_array($this->navigation['subMenus'])) { + foreach ($this->navigation['subMenus'] as $name => $menu) { + if (!isset($navigation[$name])) { + \BackendMenu::registerContextSidenavPartial('Wms.Menu', $name, '~/plugins/wms/menu/partials/menu/secondary/_sidenav.phtml'); + } + } + } + } + + return $navigation; + } + + protected function getPage($name, $title = null, $pageTitleMoreImportant = false, $active = null, $attributes = []) + { + /* @var $page CmsPage */ + if (!$this->cmsController) { + $this->cmsController = CmsController::getController() ?: new CmsController; + } + + $page = CmsPage::find($name); + if (!$page) { + $result = [ + 'url' => '#', + 'name' => $title ?? $name, + 'class' => optional($attributes)['class'], + 'attributes' => $this->renderAttributes($attributes), + ]; + } else { + $result = [ + 'url' => $this->cmsController->pageUrl($name, false), + 'name' => $pageTitleMoreImportant ? $page->getTitle(true) : (($title ?? $page->getTitle(true)) ?: $name), + 'class' => optional($attributes)['class'], + 'attributes' => $this->renderAttributes($attributes), + ]; + } + $current = CmsPage::url(''); + if ($current == $result['url']) { + $result['active'] = true; + } elseif ($active) { + if (is_string($active) && $current == CmsPage::url($active)) { + $result['active'] = true; + } elseif (is_array($active)) { + $isActive = false; + foreach ($active as $page) { + if ($isActive) { + break; + } + if ($current == CmsPage::url($page)) { + $isActive = true; + } + } + if ($isActive) { + $result['active'] = true; + } + } + } + + return $result; + } + + protected function getMenu($menu, $pageTitleMoreImportant = true) + { + $result = []; + foreach ($menu as $key => $value) { + if (!is_array($value)) { + $result[$key] = $this->getPage($key, $value, $pageTitleMoreImportant); + } else { + $result[$key] = $this->getPage( + $key, + optional($value)['name'], + $pageTitleMoreImportant, + optional($value)['active'], + optional($value)['attributes'] ?: [] + ); + if (isset($value['url'])) { + $result[$key]['url'] = url($value['url']); + } + if (isset($value['pages'])) { + $result[$key]['pages'] = $this->getMenu($value['pages'], $pageTitleMoreImportant); + } + } + } + + return $result; + } + + protected function renderAttributes($attributes) + { + $result = ''; + foreach ($attributes as $name => $attribute) { + if ($name == 'class' || $name == 'href') { + continue; + } + $result .= "$name=\"$attribute\" "; + } + if (strlen($result)) { + $result = substr($result, 0, -1); + } + + return $result; + } +} diff --git a/README-RU.md b/README-RU.md new file mode 100644 index 0000000..c961cfb --- /dev/null +++ b/README-RU.md @@ -0,0 +1,41 @@ +# Menu plugin +Объединяет элементы меню из разных плагинов + +Для добавления пунктов меню добавьте в класс `Plugin` + + public function addToMainNavigation() + { + return [ + '{tab_name}' => [ + '{name}' => [ + 'label' => {label}, + 'url' => {url}, + 'permissions' => [{permission}, ...], + 'icon' => {icon}, + 'order' => {order}, + //'first' => true, + 'subMenu' => [ + '{tab_name}' => [ + '{name}' => [ + 'label' => {label}, + 'url' => {url}, + 'permissions' => [{permission}, ...], + 'icon' => {icon}, + 'order' => {order}, + ], + ... + ], + ... + ], + ... + ], + ... + ]; + } + +Имя вкладки по умолчанию {default_name} + +Для изменения имени вкладки по умолчанию, в файле `Plugin.php`, объявите константу `DEFAULT_MENU_NAME` + +Если указан `первый` элемент, то он будет сразу открываться при клике на меню. +Если нет, то будет открыта страница меню или подменю. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb54ce3 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Menu plugin +Combines menu items from different plugins + +To add menu items add to class`Plugin` + + public function addToMainNavigation() + { + return [ + '{tab_name}' => [ + '{name}' => [ + 'label' => {label}, + 'url' => {url}, + 'permissions' => [{permission}, ...], + 'icon' => {icon}, + 'order' => {order}, + //'first' => true, + 'subMenu' => [ + '{tab_name}' => [ + '{name}' => [ + 'label' => {label}, + 'url' => {url}, + 'permissions' => [{permission}, ...], + 'icon' => {icon}, + 'order' => {order}, + ], + ... + ], + ... + ], + ... + ], + ... + ]; + } + +Default tab name is {default_name} + +For change default tab name, in `Plugin.php` file, declare the constant `DEFAULT_MENU_NAME` + +If the `first` element is specified, it will immediately open when you click on the menu. +If not, the menu or submenu page will be opened. \ No newline at end of file diff --git a/assets/css/_wms_extend_menu.css b/assets/css/_wms_extend_menu.css new file mode 100644 index 0000000..6cfedc3 --- /dev/null +++ b/assets/css/_wms_extend_menu.css @@ -0,0 +1,11 @@ +@media (max-width: 768px) { + .layout-cell.layout-sidenav-container.wms-menu { + display: none; + } + .sidenav-tree-root .sidenav-tree .back-link.wms-menu-back-link { + display: block !important; + } + .layout-cell.layout-sidenav-container ~ .layout-cell.sidenav-tree { + border: none; + } +} \ No newline at end of file diff --git a/classes/Extend.php b/classes/Extend.php new file mode 100644 index 0000000..aedea63 --- /dev/null +++ b/classes/Extend.php @@ -0,0 +1,218 @@ + null, + 'sideMenu' => [], + 'subMenus' => [], + 'permissions' => [], + ]; + + /** + * @param PluginManager $pluginManager + * @param string $defaultMenuName + * @param string $defaultIcon + * @return array + */ + public static function collectNavigation(string $defaultMenuName, string $defaultIcon): array + { + self::$manager = PluginManager::instance(); + self::$defaultMenuName = $defaultMenuName; + self::$icon = $defaultIcon; + self::$user = \BackendAuth::getUser(); + + $navigation = self::getPluginsNavigation(); + self::removeOrder($navigation); + if (!$navCount = count($navigation)) { + return [null, null]; + } + + foreach ($navigation as $group => $pages) { + if ($group == '{default_name}') { + $group = self::$defaultMenuName; + } + foreach ($pages as $name => $page) { + if ($page) { + self::$navigation['sideMenu'][$name] = [ + 'label' => isset($page['label']) ? $page['label'] : 'wms.menu::lang.no-name', + 'group' => $group, + 'url' => isset($page['url']) ? $page['url'] : '#', + 'icon' => isset($page['icon']) ? $page['icon'] : 'icon-leaf', + 'permissions' => isset($page['permissions']) ? $page['permissions'] : ['wms.*'], + ]; + } + } + } + + return [ + self::$navigation, + self::$defaultMenuName, + ]; + } + + protected static function removeOrder(&$array, $name = null) + { + if ($name && isset($array['subMenu']) && is_array($array['subMenu'])) { + $subPermissions = []; + $subFirst = null; + $subCount = 0; + foreach ($array['subMenu'] as $subGroup => $subMenu) { + if ($subGroup == '{default_name}') { + $subGroup = self::$defaultMenuName; + } + foreach ($subMenu as $subName => $subPage) { + $subPermission = isset($subPage['permissions']) ? $subPage['permissions'] : ['wms.*']; + if (!is_array($subPermission)) { + $subPermission = [$subPermission]; + } + $hasPermissions = false; + foreach ($subPermission as $perm) { + $hasPermissions = $hasPermissions || self::$user->hasPermission($perm); + } + if ($hasPermissions) { + $subUrl = isset($subPage['url']) ? $subPage['url'] : '#'; + foreach ($subPermission as $perm) { + $subPermissions[$perm] = true; + } + if (!$subFirst && isset($subPage['first']) && $subPage['first'] && self::$user->hasPermission($subPermission)) { + $subFirst = $subUrl; + } + self::$navigation['subMenus'][$name][$subGroup][$subName] = [ + 'label' => isset($subPage['label']) ? $subPage['label'] : 'wms.menu::lang.no-name', + 'group' => $subGroup, + 'url' => $subUrl, + 'icon' => isset($subPage['icon']) ? $subPage['icon'] : 'icon-leaf', + 'permissions' => $subPermission, + ]; + $subCount++; + } + } + } + $subFirst = $subFirst ?: \Backend::url("wms/menu/$name"); + if ($subFirst && $subCount) { + $array['url'] = $subFirst; + $array['permissions'] = array_keys($subPermissions); + unset($array['subMenu']); + } else { + $array = []; + } + } + foreach ($array as $key => $val) { + if (is_array($array[$key])) { + if (isset($array[$key]['order'])) { + unset($array[$key]['order']); + } + self::removeOrder($array[$key], $key); + } + } + if (isset($array['first']) && $array['first']) { + unset($array['first']); + if (!self::$navigation['first']) { + self::$navigation['first'] = self::getFirstUrl($array); + } + } + if (isset($array['permissions'])) { + foreach ($array['permissions'] as $permission) { + self::$navigation['permissions'][$permission] = true; + } + } + } + + protected static function getFirstUrl($field) + { + if (!isset($field['url'])) { + return null; + } + + $url = null; + if (isset($field['permissions'])) { + $hasPermissions = false; + if (!is_array($field['permissions'])) { + $field['permissions'] = [$field['permissions']]; + } + foreach ($field['permissions'] as $permission) { + $hasPermissions = $hasPermissions || self::$user->hasPermission($permission); + } + if ($hasPermissions) { + $url = $field['url']; + } + } else { + $url = $field['url']; + } + + return $url; + } + + protected static function getPluginsNavigation() + { + $navigation = []; + + foreach (self::$manager->getRegistrationMethodValues('addToMainNavigation') as $plugin => $subNavigation) { + if (empty($subNavigation)) { + continue; + } + + $class = str_replace('.', '\\', $plugin) . '\Plugin'; + + if (defined($class . '::DEFAULT_MENU_NAME') && !empty($class::DEFAULT_MENU_NAME)) { + self::$defaultMenuName = $class::DEFAULT_MENU_NAME; + } + + $details = self::$manager->getRegistrationMethodValues('pluginDetails'); + if (isset($details[$plugin]['icon'])) { + $defaultIcon = $details[$plugin]['icon']; + } else { + $defaultIcon = self::$icon; + } + + foreach ($subNavigation as $group => $pages) { + foreach ($pages as $name => $page) { + if (is_array($page)) { + if (!isset($page['icon'])) { + $subNavigation[$group][$name]['icon'] = $defaultIcon; + } + } + } + } + self::arrayMerge($navigation, $subNavigation); + } + + return $navigation; + } + + protected static function arrayMerge(&$array, $merge) + { + foreach ($merge as $key => $val) { + if (!is_array($val)) { + $array[$key] = $val; + } else { + if (!isset($array[$key])) { + $array[$key] = []; + } + self::arrayMerge($array[$key], $merge[$key]); + } + } + uasort($array, function(&$arr1, &$arr2) { + $order1 = isset($arr1['order']) ? $arr1['order'] : -1; + $order2 = isset($arr2['order']) ? $arr2['order'] : -1; + + return $order1 == $order2 ? 0 : ($order1 < $order2 ? -1 : 1); + }); + } +} \ No newline at end of file diff --git a/controllers/Index.php b/controllers/Index.php new file mode 100644 index 0000000..b347aeb --- /dev/null +++ b/controllers/Index.php @@ -0,0 +1,27 @@ +pageTitle = 'wms.menu::plugin.name'; + $this->bodyClass = 'compact-container sidenav-tree-root'; + } +} diff --git a/controllers/index/index.htm b/controllers/index/index.htm new file mode 100644 index 0000000..461c336 --- /dev/null +++ b/controllers/index/index.htm @@ -0,0 +1,14 @@ +
+
+ +
+
diff --git a/helpers/Menu.php b/helpers/Menu.php new file mode 100644 index 0000000..f566ea8 --- /dev/null +++ b/helpers/Menu.php @@ -0,0 +1,16 @@ + 'No name', +]; \ No newline at end of file diff --git a/lang/en/plugin.php b/lang/en/plugin.php new file mode 100644 index 0000000..6ff6685 --- /dev/null +++ b/lang/en/plugin.php @@ -0,0 +1,8 @@ + 'Menu', + 'description' => 'Combines menus from different plugins', + 'default-menu' => 'Main', + 'slogan' => 'In a secure future', +]; \ No newline at end of file diff --git a/lang/ru/lang.php b/lang/ru/lang.php new file mode 100644 index 0000000..e2620c6 --- /dev/null +++ b/lang/ru/lang.php @@ -0,0 +1,5 @@ + 'Без имени', +]; \ No newline at end of file diff --git a/lang/ru/plugin.php b/lang/ru/plugin.php new file mode 100644 index 0000000..f991891 --- /dev/null +++ b/lang/ru/plugin.php @@ -0,0 +1,8 @@ + 'Меню', + 'description' => 'Объединяет меню из разных плагинов', + 'default-menu' => 'Основное', + 'slogan' => 'В надежное будущее', +]; \ No newline at end of file diff --git a/partials/menu/main/_system_sidebar.phtml b/partials/menu/main/_system_sidebar.phtml new file mode 100644 index 0000000..17ecdae --- /dev/null +++ b/partials/menu/main/_system_sidebar.phtml @@ -0,0 +1,36 @@ +sideMenuCode, '|') !== false) { + $menu = explode('|', $context->sideMenuCode); + $context->mainMenuCode = $menu[0]; + $context->sideMenuCode = $menu[1]; + } + if (isset($menu)): +?> + makePartial(__DIR__ . '/../secondary/_sidenav.phtml', [ + 'context' => $context, + ]) ?> + +
+ + +
+
+ makePartial(__DIR__ . '/_system_sidebar_menu_toolbar.phtml') ?> +
+ +
+
+
+ +
+
+ makePartial(__DIR__ . '/_system_sidebar_menu.phtml') ?> +
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/partials/menu/main/_system_sidebar_menu.phtml b/partials/menu/main/_system_sidebar_menu.phtml new file mode 100644 index 0000000..d140252 --- /dev/null +++ b/partials/menu/main/_system_sidebar_menu.phtml @@ -0,0 +1,52 @@ + $item) { + if(!property_exists($item, 'group')) { + $item->group = 'default'; + } + if(!property_exists($item, 'keywords')) { + $item->keywords = ''; + } + if(!property_exists($item, 'description')) { + $item->description = ''; + } + $categories[$item->group][$sideItemCode] = $item; + } +?> + + \ No newline at end of file diff --git a/partials/menu/main/_system_sidebar_menu_toolbar.phtml b/partials/menu/main/_system_sidebar_menu_toolbar.phtml new file mode 100644 index 0000000..43f4926 --- /dev/null +++ b/partials/menu/main/_system_sidebar_menu_toolbar.phtml @@ -0,0 +1,14 @@ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/partials/menu/secondary/_sidenav.phtml b/partials/menu/secondary/_sidenav.phtml new file mode 100644 index 0000000..9615dce --- /dev/null +++ b/partials/menu/secondary/_sidenav.phtml @@ -0,0 +1,81 @@ + + +
+
+ +
+
+ +
+ mainMenuCode}")) ?>"> + mainMenuCode == $mainMenuCode || empty($context->sideMenuCode) + ? 'wms.menu::plugin.name' + : "wms.site::lang.{$context->mainMenuCode}.title" + )) ?> + +
+ mainMenuCode}")) ?>"> + mainMenuCode == $mainMenuCode || empty($context->sideMenuCode) + ? 'wms.menu::plugin.name' + : "wms.site::lang.{$context->mainMenuCode}.title" + )) ?> + +
+ makePartial(__DIR__ . '/../main/_system_sidebar_menu_toolbar.phtml'); ?> +
+ +
+
+
+ +
+
+ makePartial(__DIR__ . '/_sidenav_sidebar_menu.phtml', [ + 'context' => $context, + ]) ?> +
+
+
+
+
+
+
\ No newline at end of file diff --git a/partials/menu/secondary/_sidenav_sidebar_menu.phtml b/partials/menu/secondary/_sidenav_sidebar_menu.phtml new file mode 100644 index 0000000..adbc59e --- /dev/null +++ b/partials/menu/secondary/_sidenav_sidebar_menu.phtml @@ -0,0 +1,31 @@ +getMenu($context->mainMenuCode)): + $collapsedGroups = explode('|', + isset($_COOKIE['sidenav_treegroupStatus']) + ? $_COOKIE['sidenav_treegroupStatus'] + : null + ); +?> + + \ No newline at end of file diff --git a/routes.php b/routes.php new file mode 100644 index 0000000..3a783aa --- /dev/null +++ b/routes.php @@ -0,0 +1,9 @@ +run('index', ['menu' => $menu]); +})->middleware('web'); \ No newline at end of file diff --git a/updates/version.yaml b/updates/version.yaml new file mode 100644 index 0000000..5b8e540 --- /dev/null +++ b/updates/version.yaml @@ -0,0 +1 @@ +1.0.1: First version of Menu