Overview

Packages

  • ClipIt
    • clipit
      • api
    • urjc
      • backend
  • Elgg
    • Core
      • Access
      • Authentication
      • Cache
      • Caches
      • Core
      • DataMode
        • Site
      • DataModel
        • Annotations
        • Entities
        • Extender
        • File
        • Importable
        • Loggable
        • Notable
        • Object
        • User
      • DataStorage
      • Exception
      • Exceptions
        • Stub
      • FileStore
        • Disk
      • Groups
      • Helpers
      • HMAC
      • Memcache
      • Metadata
      • Navigation
      • ODD
      • Output
      • Plugins
        • Settings
      • Sessions
      • SocialModel
        • Friendable
        • Locatable
      • WebServicesAPI
      • Widgets
      • XML
      • XMLRPC
    • Exceptions
      • Stub
  • None
  • PHP

Classes

  • ElggMenuBuilder
  • ElggMenuItem
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Elgg Menu Builder
  4:  *
  5:  * @package    Elgg.Core
  6:  * @subpackage Navigation
  7:  * @since      1.8.0
  8:  */
  9: class ElggMenuBuilder {
 10: 
 11:     /**
 12:      * @var ElggMenuItem[]
 13:      */
 14:     protected $menu = array();
 15: 
 16:     protected $selected = null;
 17: 
 18:     /**
 19:      * ElggMenuBuilder constructor
 20:      *
 21:      * @param ElggMenuItem[] $menu Array of ElggMenuItem objects
 22:      */
 23:     public function __construct(array $menu) {
 24:         $this->menu = $menu;
 25:     }
 26: 
 27:     /**
 28:      * Get a prepared menu array
 29:      *
 30:      * @param mixed $sort_by Method to sort the menu by. @see ElggMenuBuilder::sort()
 31:      * @return array
 32:      */
 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: 
 48:     /**
 49:      * Get the selected menu item
 50:      *
 51:      * @return ElggMenuItem
 52:      */
 53:     public function getSelected() {
 54:         return $this->selected;
 55:     }
 56: 
 57:     /**
 58:      * Select menu items for the current context
 59:      *
 60:      * @return void
 61:      */
 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_log("A non-object was passed to ElggMenuBuilder", "ERROR");
 73:                 continue;
 74:             }
 75:             if ($menu_item->inContext()) {
 76:                 $selected_menu[] = $menu_item;
 77:             }
 78:         }
 79: 
 80:         $this->menu = $selected_menu;
 81:     }
 82: 
 83:     /**
 84:      * Group the menu items into sections
 85:      * 
 86:      * @return void
 87:      */
 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: 
 99:     /**
100:      * Create trees for each menu section
101:      *
102:      * @internal The tree is doubly linked (parent and children links)
103:      * @return void
104:      */
105:     protected function setupTrees() {
106:         $menu_tree = array();
107: 
108:         foreach ($this->menu as $key => $section) {
109:             $parents = array();
110:             $children = array();
111:             // divide base nodes from children
112:             foreach ($section as $menu_item) {
113:                 /* @var ElggMenuItem $menu_item */
114:                 $parent_name = $menu_item->getParentName();
115:                 if (!$parent_name) {
116:                     $parents[$menu_item->getName()] = $menu_item;
117:                 } else {
118:                     $children[] = $menu_item;
119:                 }
120:             }
121: 
122:             // attach children to parents
123:             $iteration = 0;
124:             $current_gen = $parents;
125:             $next_gen = null;
126:             while (count($children) && $iteration < 5) {
127:                 foreach ($children as $index => $menu_item) {
128:                     $parent_name = $menu_item->getParentName();
129:                     if (array_key_exists($parent_name, $current_gen)) {
130:                         $next_gen[$menu_item->getName()] = $menu_item;
131:                         if (!in_array($menu_item, $current_gen[$parent_name]->getData('children'))) {
132:                             $current_gen[$parent_name]->addChild($menu_item);
133:                             $menu_item->setParent($current_gen[$parent_name]);
134:                         }
135:                         unset($children[$index]);
136:                     }
137:                 }
138:                 $current_gen = $next_gen;
139:                 $iteration += 1;
140:             }
141: 
142:             // convert keys to indexes for first level of tree
143:             $parents = array_values($parents);
144: 
145:             $menu_tree[$key] = $parents;
146:         }
147: 
148:         $this->menu = $menu_tree;
149:     }
150: 
151:     /**
152:      * Find the menu item that is currently selected
153:      *
154:      * @return ElggMenuItem
155:      */
156:     protected function findSelected() {
157: 
158:         // do we have a selected menu item already
159:         foreach ($this->menu as $menu_item) {
160:             if ($menu_item->getSelected()) {
161:                 return $menu_item;
162:             }
163:         }
164: 
165:         // scan looking for a selected item
166:         foreach ($this->menu as $menu_item) {
167:             if ($menu_item->getHref()) {
168:                 if (elgg_http_url_is_identical(full_url(), $menu_item->getHref())) {
169:                     $menu_item->setSelected(true);
170:                     return $menu_item;
171:                 }
172:             }
173:         }
174: 
175:         return null;
176:     }
177: 
178:     /**
179:      * Sort the menu sections and trees
180:      *
181:      * @param mixed $sort_by Sort type as string or php callback
182:      * @return void
183:      */
184:     protected function sort($sort_by) {
185: 
186:         // sort sections
187:         ksort($this->menu);
188: 
189:         switch ($sort_by) {
190:             case 'text':
191:                 $sort_callback = array('ElggMenuBuilder', 'compareByText');
192:                 break;
193:             case 'name':
194:                 $sort_callback = array('ElggMenuBuilder', 'compareByName');
195:                 break;
196:             case 'priority':
197:                 $sort_callback = array('ElggMenuBuilder', 'compareByWeight');
198:                 break;
199:             case 'register':
200:                 // use registration order - usort breaks this
201:                 return;
202:                 break;
203:             default:
204:                 if (is_callable($sort_by)) {
205:                     $sort_callback = $sort_by;
206:                 } else {
207:                     return;
208:                 }
209:                 break;
210:         }
211: 
212:         // sort each section
213:         foreach ($this->menu as $index => $section) {
214:             foreach ($section as $key => $node) {
215:                 $section[$key]->setData('original_order', $key);
216:             }
217:             usort($section, $sort_callback);
218:             $this->menu[$index] = $section;
219: 
220:             // depth first traversal of tree
221:             foreach ($section as $root) {
222:                 $stack = array();
223:                 array_push($stack, $root);
224:                 while (!empty($stack)) {
225:                     $node = array_pop($stack);
226:                     /* @var ElggMenuItem $node */
227:                     $node->sortChildren($sort_callback);
228:                     $children = $node->getChildren();
229:                     if ($children) {
230:                         $stack = array_merge($stack, $children);
231:                     }
232:                 }
233:             }
234:         }
235:     }
236: 
237:     /**
238:      * Compare two menu items by their display text
239:      *
240:      * @param ElggMenuItem $a Menu item
241:      * @param ElggMenuItem $b Menu item
242:      * @return bool
243:      */
244:     public static function compareByText($a, $b) {
245:         $at = $a->getText();
246:         $bt = $b->getText();
247: 
248:         $result = strnatcmp($at, $bt);
249:         if ($result === 0) {
250:             return $a->getData('original_order') - $b->getData('original_order');
251:         }
252:         return $result;
253:     }
254: 
255:     /**
256:      * Compare two menu items by their identifiers
257:      *
258:      * @param ElggMenuItem $a Menu item
259:      * @param ElggMenuItem $b Menu item
260:      * @return bool
261:      */
262:     public static function compareByName($a, $b) {
263:         $an = $a->getName();
264:         $bn = $b->getName();
265: 
266:         $result = strcmp($an, $bn);
267:         if ($result === 0) {
268:             return $a->getData('original_order') - $b->getData('original_order');
269:         }
270:         return $result;
271:     }
272: 
273:     /**
274:      * Compare two menu items by their priority
275:      *
276:      * @param ElggMenuItem $a Menu item
277:      * @param ElggMenuItem $b Menu item
278:      * @return bool
279:      *
280:      * @todo change name to compareByPriority
281:      */
282:     public static function compareByWeight($a, $b) {
283:         $aw = $a->getWeight();
284:         $bw = $b->getWeight();
285: 
286:         if ($aw == $bw) {
287:             return $a->getData('original_order') - $b->getData('original_order');
288:         }
289:         return $aw - $bw;
290:     }
291: }
292: 
API documentation generated by ApiGen 2.8.0