1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * The menu context class |
||||
5 | * |
||||
6 | * @package ElkArte Forum |
||||
7 | * @copyright ElkArte Forum contributors |
||||
8 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||
9 | * |
||||
10 | * @version 2.0 dev |
||||
11 | * |
||||
12 | */ |
||||
13 | |||||
14 | namespace ElkArte\Menu; |
||||
15 | |||||
16 | use ElkArte\Cache\Cache; |
||||
17 | use ElkArte\Helper\HttpReq; |
||||
18 | use ElkArte\Helper\ValuesContainer; |
||||
19 | use ElkArte\User; |
||||
20 | |||||
21 | /** |
||||
22 | * Class MenuContext |
||||
23 | * |
||||
24 | * The MenuContext class is responsible for setting up the context for the menu on each page load. |
||||
25 | */ |
||||
26 | class MenuContext |
||||
27 | { |
||||
28 | /** @var ValuesContainer details of the user to which we are building the menu */ |
||||
29 | private $user; |
||||
30 | |||||
31 | /** @var Cache|object The cache variable. */ |
||||
32 | private $cache; |
||||
33 | |||||
34 | /** @var int cache age */ |
||||
35 | private $cacheTime; |
||||
36 | |||||
37 | /** @var bool if the action needs to call a hook to determine the real action */ |
||||
38 | private $needs_action_hook; |
||||
39 | |||||
40 | public function __construct() |
||||
41 | { |
||||
42 | global $modSettings; |
||||
43 | |||||
44 | $this->user = User::$info; |
||||
45 | $this->cache = Cache::instance(); |
||||
46 | $this->cacheTime = $modSettings['lastActive'] * 60; |
||||
47 | } |
||||
48 | |||||
49 | /** |
||||
50 | * Sets up all the top menu buttons |
||||
51 | * |
||||
52 | * What it does: |
||||
53 | * |
||||
54 | * - Defines every master item in the menu, as well as any sub-items |
||||
55 | * - Sets the counter for the menu items |
||||
56 | * - Ensures the chosen action is set so the menu is highlighted |
||||
57 | * - Saves them in the cache if it is available and on |
||||
58 | * - Places the results in $context |
||||
59 | */ |
||||
60 | public function setupMenuContext() |
||||
61 | { |
||||
62 | global $context; |
||||
63 | |||||
64 | $this->setupUserPermissions(); |
||||
65 | |||||
66 | call_integration_hook('integrate_setup_allow'); |
||||
67 | |||||
68 | $this->setupHeaderCallbacks(); |
||||
69 | |||||
70 | // Update the Moderation menu items with action item totals |
||||
71 | if ($context['allow_moderation_center']) |
||||
72 | { |
||||
73 | // Get the numbers for the menu ... |
||||
74 | require_once(SUBSDIR . '/Moderation.subs.php'); |
||||
75 | $menu_count = loadModeratorMenuCounts(); |
||||
76 | } |
||||
77 | |||||
78 | $menu_count['unread_messages'] = $context['user']['unread_messages']; |
||||
79 | $menu_count['mentions'] = $context['user']['mentions']; |
||||
80 | |||||
81 | // All the buttons we can possibly want and then some, try pulling the final list of buttons from cache first. |
||||
82 | $this->setupMenuButtons($menu_count); |
||||
83 | |||||
84 | $this->setupCurrentAction(); |
||||
85 | |||||
86 | // Not all actions are simple. |
||||
87 | if (!empty($this->needs_action_hook)) |
||||
88 | { |
||||
89 | call_integration_hook('integrate_current_action', [&$current_action]); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||||
90 | } |
||||
91 | } |
||||
92 | |||||
93 | /** |
||||
94 | * Sets up some core menu item permissions based on the user |
||||
95 | */ |
||||
96 | private function setupUserPermissions() |
||||
97 | { |
||||
98 | global $context, $modSettings; |
||||
99 | |||||
100 | $context['allow_search'] = empty($modSettings['allow_guestAccess']) ? $this->user->is_guest === false && allowedTo('search_posts') : (allowedTo('search_posts')); |
||||
0 ignored issues
–
show
The property
is_guest does not exist on ElkArte\Helper\ValuesContainer . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
101 | $context['allow_admin'] = allowedTo(['admin_forum', 'manage_boards', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_attachments', 'manage_smileys']); |
||||
102 | $context['allow_edit_profile'] = $this->user->is_guest === false && allowedTo(['profile_view_own', 'profile_view_any', 'profile_identity_own', 'profile_identity_any', 'profile_extra_own', 'profile_extra_any', 'profile_remove_own', 'profile_remove_any', 'moderate_forum', 'manage_membergroups', 'profile_title_own', 'profile_title_any']); |
||||
103 | $context['allow_memberlist'] = allowedTo('view_mlist'); |
||||
104 | $context['allow_calendar'] = allowedTo('calendar_view') && !empty($modSettings['cal_enabled']); |
||||
105 | $context['allow_moderation_center'] = $context['user']['can_mod']; |
||||
106 | $context['allow_pm'] = allowedTo('pm_read'); |
||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * Sets up the header callbacks. |
||||
111 | * |
||||
112 | * @return void |
||||
113 | */ |
||||
114 | private function setupHeaderCallbacks() |
||||
115 | { |
||||
116 | global $context; |
||||
117 | |||||
118 | if ($context['allow_search']) |
||||
119 | { |
||||
120 | $context['theme_header_callbacks'] = elk_array_insert($context['theme_header_callbacks'], 'login_bar', ['search_bar'], 'after'); |
||||
121 | } |
||||
122 | |||||
123 | // Add in a top section notice callback |
||||
124 | $context['theme_header_callbacks'][] = 'header_bar'; |
||||
125 | } |
||||
126 | |||||
127 | /** |
||||
128 | * Set up the menu buttons. |
||||
129 | * |
||||
130 | * @param array $menu_count The count of menus. |
||||
131 | * |
||||
132 | * @return void |
||||
133 | */ |
||||
134 | private function setUpMenuButtons($menu_count) |
||||
135 | { |
||||
136 | global $context, $modSettings; |
||||
137 | |||||
138 | // Check the cache |
||||
139 | if ((time() - $this->cacheTime <= $modSettings['settings_updated']) |
||||
140 | || ($menu_buttons = $this->cache->get('menu_buttons-' . implode('_', $this->user->groups) . '-' . $this->user->language, $this->cacheTime)) === null) |
||||
0 ignored issues
–
show
It seems like
$this->user->groups can also be of type null ; however, parameter $pieces of implode() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() The property
language does not exist on ElkArte\Helper\ValuesContainer . Since you implemented __get , consider adding a @property annotation.
![]() The property
groups does not exist on ElkArte\Helper\ValuesContainer . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
141 | { |
||||
142 | // Start things up: this is what we know by default |
||||
143 | require_once(SUBSDIR . '/Menu.subs.php'); |
||||
144 | $buttons = loadDefaultMenuButtons(); |
||||
145 | |||||
146 | // Allow editing menu buttons easily. |
||||
147 | call_integration_hook('integrate_menu_buttons', [&$buttons, &$menu_count]); |
||||
148 | |||||
149 | // Now we put the buttons in the context so the theme can use them. |
||||
150 | $menu_buttons = $this->initializeButtonProperties($buttons, $menu_count); |
||||
151 | |||||
152 | if ($this->cache->levelHigherThan(1)) |
||||
153 | { |
||||
154 | $this->cache->put('menu_buttons-' . implode('_', $this->user->groups) . '-' . $this->user->language, $menu_buttons, $this->cacheTime); |
||||
155 | } |
||||
156 | } |
||||
157 | |||||
158 | if (!empty($menu_buttons['profile']['sub_buttons']['logout'])) |
||||
159 | { |
||||
160 | $menu_buttons['profile']['sub_buttons']['logout']['href'] .= ';' . $context['session_var'] . '=' . $context['session_id']; |
||||
161 | } |
||||
162 | |||||
163 | $context['menu_buttons'] = $menu_buttons; |
||||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | * Initializes the properties of the buttons. |
||||
168 | * |
||||
169 | * @param array $buttons The array of buttons. |
||||
170 | * @param array $menu_count The count of menus. |
||||
171 | * |
||||
172 | * @return array The array of buttons with initialized properties. |
||||
173 | */ |
||||
174 | private function initializeButtonProperties($buttons, $menu_count) |
||||
175 | { |
||||
176 | $menu_buttons = []; |
||||
177 | foreach ($buttons as $act => $button) |
||||
178 | { |
||||
179 | if (!empty($button['show'])) |
||||
180 | { |
||||
181 | $button = $this->setButtonProperties($button, $menu_count); |
||||
182 | $menu_buttons[$act] = $button; |
||||
183 | } |
||||
184 | } |
||||
185 | |||||
186 | return $menu_buttons; |
||||
187 | } |
||||
188 | |||||
189 | /** |
||||
190 | * Set the properties of a button based on the menu count and other criteria. |
||||
191 | * |
||||
192 | * @param array $button The button that needs to be updated. |
||||
193 | * @param array $menu_count The menu count data. |
||||
194 | * @return array The updated button. |
||||
195 | */ |
||||
196 | private function setButtonProperties($button, $menu_count) |
||||
197 | { |
||||
198 | $button['active_button'] = false; |
||||
199 | |||||
200 | $button = $this->setButtonActionHook($button); |
||||
201 | $button = $this->setButtonCounter($button, $menu_count); |
||||
202 | |||||
203 | return $this->setSubButtonCounter($button, $menu_count); |
||||
204 | } |
||||
205 | |||||
206 | /** |
||||
207 | * Sets the action hook flag for the button. |
||||
208 | * |
||||
209 | * @param array $button The button that needs to be checked. |
||||
210 | * @return array The updated button. |
||||
211 | */ |
||||
212 | private function setButtonActionHook($button) |
||||
213 | { |
||||
214 | if (isset($button['action_hook'])) |
||||
215 | { |
||||
216 | $this->needs_action_hook = true; |
||||
217 | } |
||||
218 | |||||
219 | return $button; |
||||
220 | } |
||||
221 | |||||
222 | /** |
||||
223 | * Set the counter and indicator of the button based on the menu count. |
||||
224 | * |
||||
225 | * @param array $button The button that needs to be updated. |
||||
226 | * @param array $menu_count The menu count data. |
||||
227 | * @return array The updated button. |
||||
228 | */ |
||||
229 | private function setButtonCounter($button, $menu_count) |
||||
230 | { |
||||
231 | if (isset($button['counter']) && !empty($menu_count[$button['counter']])) |
||||
232 | { |
||||
233 | $button['alttitle'] = $button['title'] . ' [' . $menu_count[$button['counter']] . ']'; |
||||
234 | $this->addCountsToTitle($button['title'], $menu_count[$button['counter']], 0); |
||||
235 | $button['indicator'] = true; |
||||
236 | } |
||||
237 | |||||
238 | return $button; |
||||
239 | } |
||||
240 | |||||
241 | /** |
||||
242 | * Sets the counter for sub buttons of a given button |
||||
243 | * |
||||
244 | * @param array $button The button containing sub buttons |
||||
245 | * @param array $menu_count The count of items for each sub button |
||||
246 | * |
||||
247 | * @return array The modified button with updated counters for sub buttons |
||||
248 | */ |
||||
249 | private function setSubButtonCounter($button, $menu_count) |
||||
250 | { |
||||
251 | if (isset($button['sub_buttons'])) |
||||
252 | { |
||||
253 | foreach ($button['sub_buttons'] as $key => $subButton) |
||||
254 | { |
||||
255 | if (empty($subButton['show'])) |
||||
256 | { |
||||
257 | unset($button['sub_buttons'][$key]); |
||||
258 | continue; |
||||
259 | } |
||||
260 | |||||
261 | if (isset($subButton['counter']) && !empty($menu_count[$subButton['counter']])) |
||||
262 | { |
||||
263 | $button['sub_buttons'][$key]['alttitle'] = $subButton['title'] . ' [' . $menu_count[$subButton['counter']] . ']'; |
||||
264 | $this->addCountsToTitle($button['sub_buttons'][$key]['title'], $menu_count[$subButton['counter']], 1); |
||||
265 | |||||
266 | // And any counter on its sub menu |
||||
267 | $button = $this->setSubButtonCounts($button, $key, $subButton, $menu_count); |
||||
268 | } |
||||
269 | } |
||||
270 | } |
||||
271 | |||||
272 | return $button; |
||||
273 | } |
||||
274 | |||||
275 | /** |
||||
276 | * Sets the sub button counts for a given button. |
||||
277 | * |
||||
278 | * @param array $button The original button array. |
||||
279 | * @param int $key The key of the sub button. |
||||
280 | * @param array $subButton The sub button array. |
||||
281 | * @param array $menu_count The count of menus. |
||||
282 | * |
||||
283 | * @return array The updated button array with sub button counts set. |
||||
284 | */ |
||||
285 | private function setSubButtonCounts($button, $key, $subButton, $menu_count) |
||||
286 | { |
||||
287 | if (empty($subButton['sub_buttons'])) |
||||
288 | { |
||||
289 | return $button; |
||||
290 | } |
||||
291 | |||||
292 | foreach ($subButton['sub_buttons'] as $key2 => $subButton2) |
||||
293 | { |
||||
294 | $button['sub_buttons'][$key]['sub_buttons'][$key2] = $subButton2; |
||||
295 | |||||
296 | if (empty($subButton2['show'])) |
||||
297 | { |
||||
298 | unset($button['sub_buttons'][$key]['sub_buttons'][$key2]); |
||||
299 | } |
||||
300 | elseif (isset($subButton2['counter']) && !empty($menu_count[$subButton2['counter']])) |
||||
301 | { |
||||
302 | $button['sub_buttons'][$key]['sub_buttons'][$key2]['alttitle'] = $subButton2['title'] . ' [' . $menu_count[$subButton2['counter']] . ']'; |
||||
303 | $this->addCountsToTitle($button['sub_buttons'][$key]['sub_buttons'][$key2]['title'], $menu_count[$subButton2['counter']], 1); |
||||
304 | unset($menu_count[$subButton2['counter']]); |
||||
305 | } |
||||
306 | } |
||||
307 | |||||
308 | return $button; |
||||
309 | } |
||||
310 | |||||
311 | /** |
||||
312 | * Adds counts to the title. |
||||
313 | * |
||||
314 | * @param string $title The title to add counts to. |
||||
315 | * @param array $counts The array of counts. |
||||
316 | * @param string $notice The notice to use for formatting. |
||||
317 | * |
||||
318 | * @return void Does not return anything. |
||||
319 | */ |
||||
320 | private function addCountsToTitle(&$title, $counts, $notice) |
||||
321 | { |
||||
322 | global $settings; |
||||
323 | |||||
324 | if (!empty($settings['menu_numeric_notice'][$notice])) |
||||
325 | { |
||||
326 | $title .= sprintf($settings['menu_numeric_notice'][$notice], $counts); |
||||
0 ignored issues
–
show
$counts of type array is incompatible with the type double|integer|string expected by parameter $values of sprintf() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
327 | } |
||||
328 | } |
||||
329 | |||||
330 | /** |
||||
331 | * Sets up the current action. |
||||
332 | * |
||||
333 | * @return void |
||||
334 | * @global array $context The global context array. |
||||
335 | * |
||||
336 | */ |
||||
337 | private function setupCurrentAction() |
||||
338 | { |
||||
339 | global $context; |
||||
340 | |||||
341 | if (isset($context['menu_buttons'][$context['current_action']])) |
||||
342 | { |
||||
343 | $current_action = $context['current_action']; |
||||
344 | } |
||||
345 | elseif ($context['current_action'] === 'profile') |
||||
346 | { |
||||
347 | $current_action = 'pm'; |
||||
348 | } |
||||
349 | elseif ($context['current_action'] === 'theme') |
||||
350 | { |
||||
351 | |||||
352 | $sa = HttpReq::instance()->getRequest('sa', 'trim', ''); |
||||
353 | $current_action = $sa === 'pick' ? 'profile' : 'admin'; |
||||
354 | } |
||||
355 | else |
||||
356 | { |
||||
357 | $current_action = 'home'; |
||||
358 | } |
||||
359 | |||||
360 | // Set the current action |
||||
361 | $context['current_action'] = $current_action; |
||||
362 | |||||
363 | // Not all actions are simple. |
||||
364 | if (!empty($this->needs_action_hook)) |
||||
365 | { |
||||
366 | call_integration_hook('integrate_current_action', [&$current_action]); |
||||
367 | } |
||||
368 | |||||
369 | if (isset($context['menu_buttons'][$current_action])) |
||||
370 | { |
||||
371 | $context['menu_buttons'][$current_action]['active_button'] = true; |
||||
372 | } |
||||
373 | } |
||||
374 | } |
||||
375 |