From 6bead2fc577345eedfd4e29d5755a733ac0cd6c5 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Tue, 17 Mar 2020 08:30:54 +0800 Subject: [PATCH] Initial commit --- Annotation/Menu.php | 13 ++ CatalystMenuBundle.php | 10 ++ DependencyInjection/CatalystMenuExtension.php | 30 ++++ EventListener/MenuAnnotationListener.php | 76 ++++++++++ Menu/Collection.php | 71 +++++++++ Menu/Item.php | 138 ++++++++++++++++++ Resources/config/services.yaml | 18 +++ Service/Generator.php | 120 +++++++++++++++ composer.json | 14 ++ 9 files changed, 490 insertions(+) create mode 100644 Annotation/Menu.php create mode 100644 CatalystMenuBundle.php create mode 100644 DependencyInjection/CatalystMenuExtension.php create mode 100644 EventListener/MenuAnnotationListener.php create mode 100644 Menu/Collection.php create mode 100644 Menu/Item.php create mode 100644 Resources/config/services.yaml create mode 100644 Service/Generator.php create mode 100644 composer.json diff --git a/Annotation/Menu.php b/Annotation/Menu.php new file mode 100644 index 0000000..e8001ae --- /dev/null +++ b/Annotation/Menu.php @@ -0,0 +1,13 @@ +load('services.yaml'); + + // NOTE: cannot use processConfiguration here since having the key as an identifier + // will break across multiple configuration files. + // Issue can be found here: https://github.com/symfony/symfony/issues/29817 + + // $data = $this->processConfigs($configs); + + // set acl data for main acl generator + $def = $container->getDefinition('catalyst_menu.generator'); + $def->replaceArgument('$menu_data', $configs); + } +} diff --git a/EventListener/MenuAnnotationListener.php b/EventListener/MenuAnnotationListener.php new file mode 100644 index 0000000..9ba9c6e --- /dev/null +++ b/EventListener/MenuAnnotationListener.php @@ -0,0 +1,76 @@ +annot_reader = $annot_reader; + $this->menu_gen = $menu_gen; + $this->twig = $twig; + $this->menu_id = $menu_id; + } + + public function onKernelController(ControllerEvent $event) + { + if (!$event->isMasterRequest()) + return; + + // get controller + $event_controller = $event->getController(); + if (!is_array($event_controller)) + return; + + list($controller, $method_name) = $event_controller; + + // get reflection class + try + { + $ref_controller = new ReflectionClass($controller); + } + catch (ReflectionException $e) + { + throw new RuntimeException('Cannot read menu annotation.'); + } + + // get method annotations + $ref_method = $ref_controller->getMethod($method_name); + $annotation = $this->annot_reader->getMethodAnnotation($ref_method, MenuAnnotation::class); + + // check if we get anything + if ($annotation == null) + return; + + $this->selectMenu($annotation->selected); + } + + protected function selectMenu($selected) + { + // get menu + $menu = $this->menu_gen->getMenu($this->menu_id); + + // set menu selected + $sel = $menu['index']->get($selected); + if ($sel != null) + $sel->setSelected(); + + // create twig global variable + $this->twig->addGlobal('menu_' . $this->menu_id, $menu); + } +} diff --git a/Menu/Collection.php b/Menu/Collection.php new file mode 100644 index 0000000..2ddfc50 --- /dev/null +++ b/Menu/Collection.php @@ -0,0 +1,71 @@ +position = 0; + $this->array = array(); + $this->index_array = array(); + } + + // iterator stuff + public function rewind() + { + $this->position = 0; + } + + public function current() + { + return $this->array[$this->index_array[$this->position]]; + } + + public function key() + { + return $this->position; + } + + public function next() + { + return ++$this->position; + } + + public function valid() + { + return isset($this->index_array[$this->position]); + } + // end of iterator stuff + + public function add(Item $mi) + { + $id = $mi->getID(); + $this->array[$id] = $mi; + $this->index_array[] = $id; + return $this; + } + + public function get($id) + { + if (isset($this->array[$id])) + return $this->array[$id]; + + return null; + } + + public function unselectAll() + { + foreach ($this->array as $mi) + $mi->setSelected(false, false); + + return $this; + } +} diff --git a/Menu/Item.php b/Menu/Item.php new file mode 100644 index 0000000..14e178f --- /dev/null +++ b/Menu/Item.php @@ -0,0 +1,138 @@ +id = ''; + $this->icon = null; + $this->link = null; + $this->label = ''; + $this->children = []; + $this->selected = false; + $this->parent = null; + $this->acl_key = null; + } + + // setters + public function setID($id) + { + $this->id = $id; + return $this; + } + + public function setIcon($icon) + { + $this->icon = $icon; + return $this; + } + + public function setLink($link) + { + $this->link = $link; + return $this; + } + + public function setLabel($label) + { + $this->label = $label; + return $this; + } + + public function setParent(self $parent) + { + $this->parent = $parent; + return $this; + } + + public function addChild(self $child) + { + $child->setParent($this); + + // check if selected + if ($child->isSelected()) + $this->setSelected(); + + $this->children[] = $child; + return $this; + } + + public function setSelected($sel = true, $to_bubble = true) + { + if ($sel) + { + $this->selected = true; + // bubble up to parents + if ($this->parent != null && $to_bubble) + $this->parent->setSelected(true, true); + } + else + $this->selected = false; + return $this; + } + + public function setACLKey($key) + { + $this->acl_key = $key; + return $this; + } + + // getters + public function getID() + { + return $this->id; + } + + public function getIcon() + { + return $this->icon; + } + + public function getLink() + { + return $this->link; + } + + public function getLabel() + { + return $this->label; + } + + public function getChildren() + { + return $this->children; + } + + public function hasChildren() + { + if (count($this->children) > 0) + return true; + return false; + } + + public function isSelected() + { + return $this->selected; + } + + public function getParent() + { + return $this->parent; + } + + public function getACLKey() + { + return $this->acl_key; + } +} diff --git a/Resources/config/services.yaml b/Resources/config/services.yaml new file mode 100644 index 0000000..d037fa1 --- /dev/null +++ b/Resources/config/services.yaml @@ -0,0 +1,18 @@ +services: + _defaults: + autowire: true + autoconfigure: true + + catalyst_menu.generator: + class: Catalyst\MenuBundle\Service\Generator + autowire: true + arguments: + $router: "@router.default" + $menu_data: [] + + Catalyst\MenuBundle\EventListener\MenuAnnotationListener: + arguments: + $menu_gen: "@catalyst_menu.generator" + $menu_id: "main" + tags: + - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } diff --git a/Service/Generator.php b/Service/Generator.php new file mode 100644 index 0000000..72a1271 --- /dev/null +++ b/Service/Generator.php @@ -0,0 +1,120 @@ +index = new Collection(); + $this->menu = new Collection(); + $this->router = $router; + $this->menu_data = $this->processConfigs($menu_data); + } + + public function getMenu($menu_key) + { + if (!isset($this->menu_data[$menu_key])) + return null; + + return $this->menu_data[$menu_key]; + } + + protected function processConfigs($data) + { + $pdata = []; + + error_log(print_r($data, true)); + + // first layer contains all the instances in config + foreach ($data as $instance_data) + { + // 2nd layer are the groups and the parents + foreach ($instance_data as $group => $group_data) + { + // initialize group data + if (!isset($pdata[$group])) + { + $pdata[$group] = [ + 'menu' => new Collection(), + 'index' => new Collection(), + ]; + } + + $index = $pdata[$group]['index']; + $menu = $pdata[$group]['menu']; + + foreach ($group_data as $mi_data) + { + // check params + if (!isset($mi_data['icon'])) + $mi_data['icon'] = null; + + // instantiate + $mi = $this->newItem($index, $mi_data['id'], $mi_data['label'], $mi_data['icon']); + + // acl + if (isset($mi_data['acl'])) + $mi->setACLKey($mi_data['acl']); + + // check parent + if (isset($mi_data['parent']) && $mi_data['parent'] != null) + { + $parent = $index->get($mi_data['parent']); + if ($parent == null) + continue; + + $parent->addChild($mi); + } + else + $menu->add($mi); + } + } + } + + error_log(print_r($pdata, true)); + + return $pdata; + } + + protected function newItem($index, $id, $label, $icon = null) + { + $mi = new Item(); + $mi->setID($id) + ->setLabel($label); + + // TODO: have a way to set manual links or specify route parameters + try + { + $mi->setLink($this->router->generate($id)); + } + catch (RouteNotFoundException $e) + { + // no route, set to # + $mi->setLink('javascript:;'); + } + + if ($icon != null) + $mi->setIcon($icon); + + $index->add($mi); + + return $mi; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c04cb41 --- /dev/null +++ b/composer.json @@ -0,0 +1,14 @@ +{ + "name": "jankstudio/catalyst-menu-bundle", + "type": "symfony-bundle", + "license": "Apache-2.0", + "authors": [ + { + "name": "Kendrick Chan", + "email": "kc@jankstudio.com" + } + ], + "autoload": { + "psr-4": { "Catalyst\\MenuBundle\\": "" } + } +}