/[drupal]/contributions/modules/menutrails/menutrails.module
ViewVC logotype

Contents of /contributions/modules/menutrails/menutrails.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.22 - (show annotations) (download) (as text)
Mon Aug 17 16:37:50 2009 UTC (3 months, 1 week ago) by sun
Branch: MAIN
CVS Tags: HEAD
Changes since 1.21: +7 -6 lines
File MIME type: text/x-php
#430290 eojthebrave, sun: Added parent item selection from any menu.
1 <?php
2 // $Id: menutrails.module,v 1.21 2009/07/24 17:26:27 sun Exp $
3
4 /**
5 * @file
6 * Menutrails allows the assignment of "trails" which will keep menu items
7 * active for individual node views.
8 */
9
10 /**
11 * Implementation of hook_menu().
12 */
13 function menutrails_menu() {
14 $items = array();
15 $items['admin/build/menu/trails'] = array(
16 'title' => 'Trails',
17 'page callback' => 'drupal_get_form',
18 'page arguments' => array('menutrails_settings_form'),
19 'access arguments' => array('administer menu'),
20 'type' => MENU_LOCAL_TASK,
21 'weight' => 6,
22 );
23 return $items;
24 }
25
26 /**
27 * Implementation of hook_init().
28 *
29 * Detect menutrails for non-node-view pages.
30 * Currently supports og sub-pages only.
31 *
32 * @todo Replace TRUE with settings check.
33 */
34 function menutrails_init() {
35 if (module_exists('og') && variable_get('menutrails_og_sub_pages', TRUE)) {
36 if ($group = og_get_group_context()) {
37 $item = menu_get_item();
38 if ($item['page_callback'] != 'node_page_view') {
39 $item = menutrails_node_location($group);
40 if ($item) {
41 menu_set_item(NULL, $item);
42 if (variable_get('menutrails_breadcrumbs', 1)) {
43 $crumbs = menutrails_get_breadcrumbs();
44 $crumbs[] = l($group->title, 'node/'. $group->nid);
45 drupal_set_breadcrumb($crumbs);
46 }
47 }
48 }
49 }
50 }
51 }
52
53 /**
54 * Implementation of hook_enable().
55 *
56 * Default menutrails to run after core/og modules for fuller control.
57 */
58 function menutrails_enable() {
59 db_query("UPDATE {system} SET weight = 1 WHERE name = 'menutrails' AND type = 'module'");
60 }
61
62 /**
63 * Implementation of hook_nodeapi().
64 *
65 * This will evaluate individual nodes when being viewed and take the necessary
66 * steps to set the active_trail for menus.
67 *
68 * This will retain menu state at the node/view level. For instance, forum nodes
69 * would maintain an active trail to the forum menu item.
70 */
71 function menutrails_nodeapi(&$node, $op, $a3 = NULL, $page = FALSE) {
72 if ($op == 'view' && $page == TRUE) {
73 $item = menutrails_node_location($node);
74 if ($item) {
75 menu_set_item(NULL, $item);
76 if (variable_get('menutrails_breadcrumbs', 1)) {
77 drupal_set_breadcrumb(menutrails_get_breadcrumbs());
78 }
79 }
80 }
81 }
82
83 /**
84 * Set Breadcrumbs based on active menu trail.
85 */
86 function menutrails_get_breadcrumbs() {
87 $item = menu_get_item();
88 // Give first priority to the selected menu.
89 $menu = variable_get('menutrails_menu', FALSE);
90 if (!$menu) {
91 $menu = db_result(db_query("SELECT menu_name FROM {menu_links} WHERE link_path = '%s' AND module = 'menu'", $item['href']));
92 }
93 $tree = menu_tree_page_data($menu);
94 $crumbs = array(l(t('Home'), '<front>'));
95 _menutrails_recurse_crumbs($tree, $item, $crumbs);
96
97 return $crumbs;
98 }
99
100 function _menutrails_recurse_crumbs($tree, $item, &$crumbs, $above = array()) {
101 foreach ($tree as $menu_item) {
102 if (!$menu_item['link']['in_active_trail']) {
103 continue;
104 }
105 if ($menu_item['link']['link_path'] == $item['href']) {
106 foreach ($above as $trail_item) {
107 $crumbs[] = l($trail_item['link']['link_title'], $trail_item['link']['link_path']);
108 }
109 $crumbs[] = l($menu_item['link']['link_title'], $menu_item['link']['link_path']);
110 break;
111 }
112 if (is_array($menu_item['below'])) {
113 _menutrails_recurse_crumbs($menu_item['below'], $item, $crumbs, array_merge($above, array($menu_item)));
114 }
115 }
116 }
117
118 /**
119 * Determine the menu location of a node.
120 *
121 * Inspired by _menu_get_active_trail().
122 */
123 function menutrails_node_location($node) {
124 // This should only fire if the menu isn't already active.
125 $item = menu_get_item();
126 if (db_result(db_query("SELECT count(mlid) FROM {menu_links} WHERE link_path = '%s' AND module = 'menu'", $item['href'])) == 0) {
127 $type_trails = variable_get('menutrails_node_types', array());
128 $href = $type_trails[$node->type] ? $type_trails[$node->type] : FALSE;
129 $term_trails = variable_get('menutrails_terms', array());
130 if (!empty($node->taxonomy)) {
131 foreach ($node->taxonomy as $term) {
132 if (isset($term_trails[$term->tid])) {
133 $href = $term_trails[$term->tid];
134 }
135 }
136 }
137 }
138 else {
139 // We may want to do some breadcrumbing.
140 return $item;
141 }
142 // Organic groups support.
143 if (module_exists('og') && !empty($node->og_groups)) {
144 // We can only do one, so we take the first.
145 $group = array_shift($node->og_groups);
146 if (variable_get('menutrails_og_group_menu', FALSE) != FALSE) {
147 if (db_result(db_query("SELECT count(mlid) FROM {menu_links} WHERE link_path = '%s'", $item['href'])) == 0) {
148 $href = 'node/'. $group;
149 }
150 }
151 else {
152 $group_trails = variable_get('menutrails_og_node', FALSE);
153 if ($group_trails[$group] > 0) {
154 $href = 'node/'. $group;
155 }
156 elseif (variable_get('menutrails_og_post_default', FALSE)) {
157 $href = variable_get('menutrails_og_post_default', FALSE);
158 }
159 }
160 }
161 if (isset($href)) {
162 $item['href'] = $href;
163 return $item;
164 }
165 return FALSE;
166 }
167
168 /**
169 * This implements the same functionality as the nodeapi, but for comment urls.
170 */
171 function menutrails_comment($comment, $op) {
172 if ($op == 'form' && arg(0) == 'comment') {
173 $node = node_load($comment['nid']['#value']);
174 $item = menutrails_node_location($node);
175 if ($item) {
176 menu_set_item(NULL, $item);
177 }
178 }
179 }
180
181 /**
182 * Form builder function for settings.
183 *
184 * This is where menutrails rules are set. The interface here could definitely
185 * stand for some improvement. It's especially unhelpful for tagging
186 * vocabularies with lots and lots of terms.
187 */
188 function menutrails_settings_form() {
189 $options = array('' => '<none>');
190 $limit = MENU_MAX_DEPTH - 1;
191
192 // Load up menus.
193 foreach (menu_get_menus() as $menu_id => $menu_name) {
194 $tree = menu_tree_all_data($menu_id, NULL);
195 _menutrails_parents_recurse($tree, $menu_name, '', $options, 0, $limit);
196 }
197
198 $form['description'] = array(
199 '#type' => 'markup',
200 '#weight' => '-10',
201 '#value' => t('Use these settings to configure the "menu trails" for your nodes. This determines what menu items are activated when viewing an individual node. For instance, if you have a menu item for "Blog," you may want to have all blog posts fall under that menu.'),
202 '#prefix' => '<p>',
203 '#suffix' => '</p>',
204 );
205 $form['menutrails_menu'] = array(
206 '#type' => 'select',
207 '#weight' => '-6',
208 '#options' => menu_get_menus(),
209 '#default_value' => variable_get('menutrails_menu', 'primary-links'),
210 '#title' => t('Menutrails Menu'),
211 '#description' => t('What menu are you most interested in assigning trails to? This menu will be used if there is ambiguity about what menu a node falls under.'),
212 );
213 $form['menutrails_breadcrumbs'] = array(
214 '#type' => 'checkbox',
215 '#weight' => '-5',
216 '#default_value' => variable_get('menutrails_breadcrumbs', 1),
217 '#title' => t('Set breadcrumbs?'),
218 '#description' => t('If checked, menutrails will also synchronize the default drupal breadcrumbs with the menu trails, again giving priority to the menu selected above.'),
219 );
220 $form['order'] = array(
221 '#type' => 'markup',
222 '#weight' => '-1',
223 '#value' => t('Menu trails are evaluated in the order they are shown below.'),
224 '#prefix' => '<p>',
225 '#suffix' => '</p>',
226 );
227 $form = array_merge($form, module_invoke_all('menutrails_settings', $options));
228
229 return system_settings_form($form);
230 }
231
232 /**
233 * Implementation of hook_menutrails_settings().
234 *
235 * Allows other modules to define their own menutrail behavior.
236 *
237 * Please define your input as a fieldset and do not assign a weight. This will
238 * keep the groups of menutrails settings in order.
239 *
240 * @param $options
241 * Options array to be used by other modules to define their own menutrails.
242 *
243 * @return
244 * A form element (or array) for the menutrails system settings form.
245 */
246 function menutrails_menutrails_settings($options) {
247 $form = array();
248 $node_types = node_get_types('names');
249 $node_trails = variable_get('menutrails_node_types', array());
250 $vocabs = module_exists('taxonomy') ? taxonomy_get_vocabularies() : array();
251 $term_trails = variable_get('menutrails_terms', array());
252 $form['menutrails_node_types'] = array(
253 '#tree' => TRUE,
254 '#type' => 'fieldset',
255 '#collapsible' => TRUE,
256 '#collapsed' => TRUE,
257 '#title' => t('Node types'),
258 );
259 foreach ($node_types as $key => $value) {
260 $form['menutrails_node_types'][$key] = array('#type' => 'select',
261 '#title' => t('Parent item for') ." $value",
262 '#default_value' => isset($node_trails[$key]) ? $node_trails[$key] : NULL,
263 '#options' => $options,
264 );
265 }
266 foreach ($vocabs as $vocab) {
267 // Tagging gets out of hand too fast, so we disallow.
268 if ($vocab->tags != 1) {
269 $form[$vocab->vid]['menutrails_terms'] = array(
270 '#tree' => TRUE,
271 '#type' => 'fieldset',
272 '#collapsible' => TRUE,
273 '#collapsed' => TRUE,
274 '#title' => t('Categories: @vocabulary', array('@vocabulary' => $vocab->name)),
275 );
276 $terms = taxonomy_get_tree($vocab->vid);
277 foreach ($terms as $term) {
278 $form[$vocab->vid]['menutrails_terms'][$term->tid] = array('#type' => 'select',
279 '#title' => t('Parent item for @term', array('@term' => $term->name)),
280 '#default_value' => isset($term_trails[$term->tid]) ? $term_trails[$term->tid] : NULL,
281 '#options' => $options,
282 );
283 }
284 }
285 }
286 // Organic groups support.
287 if (module_exists('og')) {
288 $form['menutrails_og'] = array(
289 '#type' => 'fieldset',
290 '#collapsible' => TRUE,
291 '#collapsed' => TRUE,
292 '#title' => t('Organic groups'),
293 '#description' => t('Settings for nodes withing Organic groups: these override node and taxonomy settings.'),
294 );
295 $form['menutrails_og']['menutrails_og_sub_pages'] = array(
296 '#type' => 'checkbox',
297 '#title' => t('Use group node menu trail for pages in groups'),
298 '#default_value' => variable_get('menutrails_og_sub_pages', TRUE),
299 '#description' => t('Use menutrails present for an organic group node for OG sub pages (e.g. "manage subscription" or "members").'),
300 );
301 $form['menutrails_og']['menutrails_og_post_default'] = array(
302 '#type' => 'select',
303 '#title' => t('Default menu trail for all nodes with group audience'),
304 '#default_value' => variable_get('menutrails_og_post_default', 0),
305 '#description' => t('Default menu trail for any posts in a group.'),
306 '#options' => $options,
307 );
308 $form['menutrails_og']['menutrails_og_node'] = array(
309 '#type' => 'fieldset',
310 '#collapsible' => TRUE,
311 '#collapsed' => TRUE,
312 '#tree' => TRUE,
313 '#title' => t('Individual organic group trails'),
314 '#description' => t('Specific trails for posts within specific groups.'),
315 );
316 $result = db_query('SELECT n.nid, n.title FROM {node} n INNER JOIN {og} og ON n.nid = og.nid ORDER BY n.title');
317 $og_trails = variable_get('menutrails_og_node', array());
318 while ($node = db_fetch_object($result)) {
319 $form['menutrails_og']['menutrails_og_node'][$node->nid] = array(
320 '#type' => 'select',
321 '#title' => t('Group @group', array('@group' => $node->title)),
322 '#default_value' => isset($og_trails[$node->nid]) ? $og_trails[$node->nid] : NULL,
323 '#options' => $options,
324 );
325 }
326 $form['menutrails_og']['menutrails_og_group_menu'] = array(
327 '#type' => 'checkbox',
328 '#title' => t("Use group's menu item for posts"),
329 '#default_value' => variable_get('menutrails_og_group_menu', FALSE),
330 '#description' => t('If a specific group node has an assigned menu item, use this as the trail for nodes which have that group as an audience. If present, this will override all other group settings.'),
331 );
332 }
333 return $form;
334 }
335
336 function menutrails_token_values($type, $object = NULL, $options = array()) {
337 if ($type == 'node') {
338 $node = $object;
339 $mlid = db_result(db_query("SELECT mlid FROM {menu_links} WHERE link_path = '%s'", 'node/'. $node->nid));
340 if (!empty($mlid) || !empty($node->menu['mlid']) || !empty($node->menu['plid'])) {
341 $menu_link = menu_link_load($mlid);
342 $trail_raw = _menu_titles($menu_link, $node->nid);
343 // Remove the node itself.
344 array_pop($trail_raw);
345 }
346 elseif ($item = menutrails_node_location($node)) {
347 $trail_raw = drupal_map_assoc(explode('/', $item['href']));
348 }
349 $trail = array();
350 if (!empty($trail_raw)) {
351 foreach ($trail_raw as $title) {
352 $trail[] = check_plain($title);
353 }
354 $tokens['menu-trail-parents-path-raw'] = implode('/', $trail_raw);
355 $tokens['menu-trail-parents-path'] = implode('/', $trail);
356 }
357 // Return NULL in case there is no trail.
358 else {
359 $tokens['menu-trail-parents-path-raw'] = NULL;
360 $tokens['menu-trail-parents-path'] = NULL;
361 }
362 return $tokens;
363 }
364 }
365
366 function menutrails_token_list($type = 'all') {
367 if ($type == 'node' || $type == 'all') {
368 $tokens['menutrails']['menu-trail-parents-path-raw'] = t("The menu trail leading up to but NOT including the node -- RAW");
369 $tokens['menutrails']['menu-trail-parents-path'] = t("The menu trail leading up to but NOT including the node");
370 return $tokens;
371 }
372 }
373
374 /**
375 * Inspired by _menu_parents_recurse().
376 *
377 * The same as above, except it delivers hrefs rather than coded ids.
378 */
379 function _menutrails_parents_recurse($tree, $menu_name, $indent, &$options, $exclude, $depth_limit) {
380 foreach ($tree as $data) {
381 if ($data['link']['depth'] > $depth_limit) {
382 // Don't iterate over any links on this level.
383 break;
384 }
385 if ($data['link']['mlid'] != $exclude && $data['link']['hidden'] >= 0) {
386 $title = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
387 if (!$data['link']['hidden']) {
388 $options[$menu_name][$data['link']['href']] = $title;
389 }
390 if ($data['below']) {
391 _menutrails_parents_recurse($data['below'], $menu_name, $indent .'--', $options, $exclude, $depth_limit);
392 }
393 }
394 }
395 }
396
397 /**
398 * Recursion to find the top tree.
399 */
400 function _menutrails_recurse($tree, $href) {
401 foreach ($tree as $link) {
402 if ($link['link']['link_path'] == $href) {
403 $found = $link;
404 break;
405 }
406 if (is_array($link['below'])) {
407 $found = _menutrails_recurse($link['below'], $href);
408 }
409 }
410 return $found;
411 }
412
413 /**
414 * Return a themed set of links.
415 *
416 * The important difference is that we use the in_active_trail bit here to set
417 * an "active" CSS class, which is what most themes (e.g. garland) use to
418 * denote an active/open menu item. You should alter/override this as your
419 * design needs dictate.
420 *
421 * @param $links
422 * A keyed array of links to be themed.
423 * @param $attributes
424 * A keyed array of attributes
425 * @return
426 * A string containing an unordered list of links.
427 */
428 function phptemplate_links($links, $attributes = array('class' => 'links')) {
429 global $language;
430 $output = '';
431
432 if (count($links) > 0) {
433 $output = '<ul'. drupal_attributes($attributes) .'>';
434
435 $num_links = count($links);
436 $i = 1;
437
438 foreach ($links as $key => $link) {
439 $class = $key;
440
441 // Add first, last and active classes to the list of links to help out themers.
442 if ($i == 1) {
443 $class .= ' first';
444 }
445 if ($i == $num_links) {
446 $class .= ' last';
447 }
448 if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
449 && (empty($link['language']) || $link['language']->language == $language->language)) {
450 $class .= ' active';
451 }
452
453 $a = '';
454 if (isset($link['href'])) {
455 // Add active class for containing <li> and <a> if 'active-trail' is set
456 // on the link itself.
457 if (isset($link['attributes']['class']) && strpos($link['attributes']['class'], 'active-trail') !== FALSE && strpos($class, 'active') === FALSE) {
458 $class .= ' active';
459 $link['attributes']['class'] .= ' active';
460 }
461 // Pass in $link as $options, they share the same keys.
462 $a = l($link['title'], $link['href'], $link);
463 }
464 else if (!empty($link['title'])) {
465 // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
466 if (empty($link['html'])) {
467 $link['title'] = check_plain($link['title']);
468 }
469 $span_attributes = '';
470 if (isset($link['attributes'])) {
471 $span_attributes = drupal_attributes($link['attributes']);
472 }
473 $a = '<span'. $span_attributes .'>'. $link['title'] .'</span>';
474 }
475
476 $i++;
477 $output .= '<li'. drupal_attributes(array('class' => $class)) .'>';
478 $output .= $a;
479 $output .= "</li>\n";
480 }
481
482 $output .= '</ul>';
483 }
484
485 return $output;
486 }
487
488 /**
489 * Implementation of hook_views_pre_view().
490 *
491 * This is invoked for every view before the it is themed, even if the view is
492 * cached.
493 */
494 function menutrails_views_pre_view(&$view) {
495 if ($view->display_handler->display->display_plugin == 'page') {
496 drupal_set_breadcrumb(menutrails_get_breadcrumbs());
497 }
498 }
499

  ViewVC Help
Powered by ViewVC 1.1.2