1<?php
2
3namespace Elgg\Menu;
4
5use Elgg\PluginHooksService;
6use Elgg\Config;
7use ElggMenuBuilder;
8use ElggMenuItem;
9
10/**
11 * Methods to construct and prepare menus for rendering
12 */
13class Service {
14
15	/**
16	 * @var PluginHooksService
17	 */
18	private $hooks;
19
20	/**
21	 * @var Config
22	 */
23	private $config;
24
25	/**
26	 * Constructor
27	 *
28	 * @param PluginHooksService $hooks  Plugin hooks
29	 * @param Config             $config Elgg config
30	 */
31	public function __construct(PluginHooksService $hooks, Config $config) {
32		$this->hooks = $hooks;
33		$this->config = $config;
34	}
35
36	/**
37	 * Build a full menu, pulling items from configuration and the "register" menu hooks.
38	 *
39	 * Parameters are filtered by the "parameters" hook.
40	 *
41	 * @param string $name   Menu name
42	 * @param array  $params Hook/view parameters
43	 *
44	 * @return Menu
45	 */
46	public function getMenu($name, array $params = []) {
47		return $this->prepareMenu($this->getUnpreparedMenu($name, $params));
48	}
49
50	/**
51	 * Build an unprepared menu.
52	 *
53	 * @param string $name   Menu name
54	 * @param array  $params Hook/view parameters
55	 *
56	 * @return UnpreparedMenu
57	 */
58	public function getUnpreparedMenu($name, array $params = []) {
59		$items = $this->prepareMenuItems(elgg_extract('items', $params, []));
60		unset($params['items']);
61
62		$registered_items = elgg_extract($name, $this->config->menus);
63		if (is_array($registered_items)) {
64			$items->merge($registered_items);
65		}
66
67		$params['name'] = $name;
68
69		$params = $this->hooks->trigger('parameters', "menu:$name", $params, $params);
70
71		if (!isset($params['sort_by'])) {
72			$params['sort_by'] = 'priority';
73		}
74
75		$items = $this->hooks->trigger('register', "menu:$name", $params, $items);
76
77		return new UnpreparedMenu($params, $items);
78	}
79
80	/**
81	 * Split a menu into sections, and pass it through the "prepare" hook
82	 *
83	 * @param UnpreparedMenu $menu Menu
84	 *
85	 * @return Menu
86	 */
87	public function prepareMenu(UnpreparedMenu $menu) {
88		$name = $menu->getName();
89		$params = $menu->getParams();
90		$sort_by = $menu->getSortBy();
91		$selected_menu_item_name = elgg_extract('selected_item_name', $params, '');
92
93		$builder = new ElggMenuBuilder($menu->getItems());
94		$builder->setSelected($selected_menu_item_name);
95
96		$params['menu'] = $builder->getMenu($sort_by);
97		$params['selected_item'] = $builder->getSelected();
98
99		$params['menu'] = $this->hooks->trigger('prepare', "menu:$name", $params, $params['menu']);
100
101		return new Menu($params);
102	}
103
104	/**
105	 * Combine several menus into one
106	 *
107	 * Unprepared menus will be built separately, then combined, with items reassigned to sections
108	 * named after their origin menu. The returned menu must be prepared before display.
109	 *
110	 * @param string[] $names    Menu names
111	 * @param array    $params   Menu params
112	 * @param string   $new_name Combined menu name (used for the prepare hook)
113	 *
114	 * @return UnpreparedMenu
115	 */
116	public function combineMenus(array $names = [], array $params = [], $new_name = '') {
117		if (!$new_name) {
118			$new_name = implode('__', $names);
119		}
120
121		$all_items = new MenuItems();
122
123		foreach ($names as $name) {
124			$items = $this->getUnpreparedMenu($name, $params)->getItems();
125
126			foreach ($items as $item) {
127				$section = $item->getSection();
128				if ($section == 'default') {
129					$item->setSection($name);
130				}
131				$item->setData('menu_name', $name);
132
133				$all_items->add($item);
134			}
135		}
136
137		$params['name'] = $new_name;
138
139		return new UnpreparedMenu($params, $all_items);
140	}
141
142	/**
143	 * Prepare menu items
144	 *
145	 * @param array $items An array of ElggMenuItem instances or menu item factory options
146	 *
147	 * @return MenuItems
148	 */
149	public function prepareMenuItems($items = []) {
150		$prepared_items = new MenuItems();
151
152		foreach ($items as $item) {
153			if (is_array($item)) {
154				$options = $item;
155				$item = ElggMenuItem::factory($options);
156			}
157
158			if (!$item instanceof ElggMenuItem) {
159				continue;
160			}
161
162			$prepared_items->add($item);
163		}
164
165		return $prepared_items;
166	}
167}
168