Elgg  Version 2.3
ElggMenuBuilder.php
Go to the documentation of this file.
1 <?php
10 
14  protected $menu = array();
15 
16  protected $selected = null;
17 
23  public function __construct(array $menu) {
24  $this->menu = $menu;
25  }
26 
33  public function getMenu($sort_by = 'text') {
34 
35  $this->selectFromContext();
36 
37  $this->selected = $this->findSelected();
38 
39  $this->setupSections();
40 
41  $this->setupTrees();
42 
43  $this->sort($sort_by);
44 
45  return $this->menu;
46  }
47 
53  public function getSelected() {
54  return $this->selected;
55  }
56 
62  protected function selectFromContext() {
63  if (!isset($this->menu)) {
64  $this->menu = array();
65  return;
66  }
67 
68  // get menu items for this context
69  $selected_menu = array();
70  foreach ($this->menu as $menu_item) {
71  if (!is_object($menu_item)) {
72  _elgg_services()->logger->error("A non-object was passed to \ElggMenuBuilder");
73  continue;
74  }
75  if ($menu_item->inContext()) {
76  $selected_menu[] = $menu_item;
77  }
78  }
79 
80  $this->menu = $selected_menu;
81  }
82 
88  protected function setupSections() {
89  $sectioned_menu = array();
90  foreach ($this->menu as $menu_item) {
91  if (!isset($sectioned_menu[$menu_item->getSection()])) {
92  $sectioned_menu[$menu_item->getSection()] = array();
93  }
94  $sectioned_menu[$menu_item->getSection()][] = $menu_item;
95  }
96  $this->menu = $sectioned_menu;
97  }
98 
105  protected function setupTrees() {
106  $menu_tree = array();
107 
108  foreach ($this->menu as $key => $section) {
109  $parents = array();
110  $children = array();
111  $all_menu_items = array();
112 
113  // divide base nodes from children
114  foreach ($section as $menu_item) {
115  /* @var \ElggMenuItem $menu_item */
116  $parent_name = $menu_item->getParentName();
117  $menu_item_name = $menu_item->getName();
118 
119  if (!$parent_name) {
120  // no parents so top level menu items
121  $parents[$menu_item_name] = $menu_item;
122  } else {
123  $children[$menu_item_name] = $menu_item;
124  }
125 
126  $all_menu_items[$menu_item_name] = $menu_item;
127  }
128 
129  if (empty($all_menu_items)) {
130  // empty sections can be skipped
131  continue;
132  }
133 
134  if (empty($parents)) {
135  // menu items without parents? That is sad.. report to the log
136  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:NoParents');
137  _elgg_services()->logger->notice($message);
138 
139  // skip section as without parents menu can not be drawn
140  continue;
141  }
142 
143  foreach ($children as $menu_item_name => $menu_item) {
144  $parent_name = $menu_item->getParentName();
145 
146  if (!array_key_exists($parent_name, $all_menu_items)) {
147  // orphaned child, inform authorities and skip to next item
148  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:OrphanedChild', array($menu_item_name, $parent_name));
149  _elgg_services()->logger->notice($message);
150 
151  continue;
152  }
153 
154  if (!in_array($menu_item, $all_menu_items[$parent_name]->getData('children'))) {
155  $all_menu_items[$parent_name]->addChild($menu_item);
156  $menu_item->setParent($all_menu_items[$parent_name]);
157  } else {
158  // menu item already existed in parents children, report the duplicate registration
159  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:DuplicateChild', array($menu_item_name));
160  _elgg_services()->logger->notice($message);
161 
162  continue;
163  }
164  }
165 
166  // convert keys to indexes for first level of tree
167  $parents = array_values($parents);
168 
169  $menu_tree[$key] = $parents;
170  }
171 
172  $this->menu = $menu_tree;
173  }
174 
180  protected function findSelected() {
181 
182  // do we have a selected menu item already
183  foreach ($this->menu as $menu_item) {
184  if ($menu_item->getSelected()) {
185  return $menu_item;
186  }
187  }
188 
189  // scan looking for a selected item
190  foreach ($this->menu as $menu_item) {
191  if ($menu_item->getHref()) {
192  if (elgg_http_url_is_identical(current_page_url(), $menu_item->getHref())) {
193  $menu_item->setSelected(true);
194  return $menu_item;
195  }
196  }
197  }
198 
199  return null;
200  }
201 
208  protected function sort($sort_by) {
209 
210  // sort sections
211  ksort($this->menu);
212 
213  switch ($sort_by) {
214  case 'text':
215  $sort_callback = [\ElggMenuBuilder::class, 'compareByText'];
216  break;
217  case 'name':
218  $sort_callback = [\ElggMenuBuilder::class, 'compareByName'];
219  break;
220  case 'priority':
221  $sort_callback = [\ElggMenuBuilder::class, 'compareByPriority'];
222  break;
223  case 'register':
224  // use registration order - usort breaks this
225  return;
226  break;
227  default:
228  if (is_callable($sort_by)) {
229  $sort_callback = $sort_by;
230  } else {
231  return;
232  }
233  break;
234  }
235 
236  // sort each section
237  foreach ($this->menu as $index => $section) {
238  foreach ($section as $key => $node) {
239  $section[$key]->setData('original_order', $key);
240  }
241  usort($section, $sort_callback);
242  $this->menu[$index] = $section;
243 
244  // depth first traversal of tree
245  foreach ($section as $root) {
246  $stack = array();
247  array_push($stack, $root);
248  while (!empty($stack)) {
249  $node = array_pop($stack);
250  /* @var \ElggMenuItem $node */
251  $node->sortChildren($sort_callback);
252  $children = $node->getChildren();
253  if ($children) {
254  $stack = array_merge($stack, $children);
255  }
256  }
257  }
258  }
259  }
260 
269  public static function compareByText($a, $b) {
270  $at = strip_tags($a->getText());
271  $bt = strip_tags($b->getText());
272 
273  $result = strnatcmp($at, $bt);
274  if ($result === 0) {
275  return $a->getData('original_order') - $b->getData('original_order');
276  }
277  return $result;
278  }
279 
287  public static function compareByName($a, $b) {
288  $an = $a->getName();
289  $bn = $b->getName();
290 
291  $result = strnatcmp($an, $bn);
292  if ($result === 0) {
293  return $a->getData('original_order') - $b->getData('original_order');
294  }
295  return $result;
296  }
297 
306  public static function compareByPriority($a, $b) {
307  $aw = $a->getPriority();
308  $bw = $b->getPriority();
309 
310  if ($aw == $bw) {
311  return $a->getData('original_order') - $b->getData('original_order');
312  }
313  return $aw - $bw;
314  }
315 
324  public static function compareByWeight($a, $b) {
325  elgg_deprecated_notice("\ElggMenuBuilder::compareByWeight() deprecated by \ElggMenuBuilder::compareByPriority", 1.9);
326  $aw = $a->getPriority();
327  $bw = $b->getPriority();
328 
329  if ($aw == $bw) {
330  return $a->getData('original_order') - $b->getData('original_order');
331  }
332  return $aw - $bw;
333  }
334 }
__construct(array $menu)
constructor
elgg_http_url_is_identical($url1, $url2, $ignore_params=array('offset', 'limit'))
Test if two URLs are functionally identical.
Definition: elgglib.php:1243
static compareByWeight($a, $b)
Compare two menu items by their priority.
current_page_url()
Returns the current page&#39;s complete URL.
Definition: input.php:65
static compareByText($a, $b)
Compare two menu items by their display text HTML tags are stripped before comparison.
setupTrees()
Create trees for each menu section.
getMenu($sort_by= 'text')
Get a prepared menu array.
getSelected()
Get the selected menu item.
$key
Definition: summary.php:34
static compareByName($a, $b)
Compare two menu items by their identifiers.
elgg_deprecated_notice($msg, $dep_version, $backtrace_level=1)
Log a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:1098
findSelected()
Find the menu item that is currently selected.
_elgg_services(\Elgg\Di\ServiceProvider $services=null)
Get the global service provider.
Definition: autoloader.php:17
sort($sort_by)
Sort the menu sections and trees.
if($item->getSelected()) $children
Definition: item.php:21
class
Definition: placeholder.php:21
$index
Definition: gallery.php:49
setupSections()
Group the menu items into sections.
static compareByPriority($a, $b)
Compare two menu items by their priority.
selectFromContext()
Select menu items for the current context.