summaryrefslogtreecommitdiffstats
path: root/menu_views.module
blob: d7ae8e4b39fee174dbd49713165d5e0a564bc77d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
<?php

/**
 * @file
 * Module to allow Views to be attached as menu items.
 *
 * This module is a utility module and allows an admin to select a view for a menu item instead of a title and link. When
 * the link is rendered, the view is inserted instead of the link. In addition, if the
 * parent item of the menu is a node page, the node id can be passed to the view as an argument using tokens.
 *
 * Original concept by Randall Knutson - LevelTen Interactive.
 * Written and maintained by Mark Carver - LevelTen Interactive.
 * http://www.leveltendesign.com
 */
 
// Include admin form alter hooks.
include_once('menu_views.admin.inc');

/**
 * Implements hook_menu().
 */
function menu_views_menu() {
  // Fake callback, needed for menu item add/edit validation.
  $items['<view>'] = array(
    'page callback' => 'drupal_not_found',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function menu_views_permission() {
  return array(
    'administer menu views' => array(
      'title' => t('Administer menu views'),
      'description' => t('Allows administrators to attach views to individual menu items and alter the view\'s configuration.'),
    ),
  );
}

/**
 * Implements hook_theme_registry_alter().
 * Intercepts theme_menu_link().
 */
function menu_views_theme_registry_alter(&$registry) {
  // Save previous value from registry in case another module/theme overwrites theme_menu_link() as well.
  $registry['menu_views_menu_link_default'] = $registry['menu_link'];
  $registry['menu_link']['function'] = 'menu_views_menu_link';
  // Provide Superfish support.
  if (isset($registry['superfish_menu_item_link'])) {
    $registry['menu_views_superfish_menu_item_link_default'] = $registry['superfish_menu_item_link'];
    $registry['superfish_menu_item_link']['function'] = 'menu_views_superfish_menu_item_link';
  }
}

/**
 * Implements theme_menu_link().
 * Overrides default theming function to intercept views.
 */
function menu_views_menu_link(array $variables) {
  // Only intercept if this menu link is a view.
  if (isset($variables['element']) && $view = _menu_views_replace_menu_item($variables['element'])) {
    return '<li' . drupal_attributes($variables['element']['#attributes']) . '>' . $view . "</li>\n";
  }
  // Otherwise, use the default theming function.
  return theme('menu_views_menu_link_default', $variables);
}

/**
 * Implements theme_superfish_menu_item_link().
 * Overrides default theming function to intercept views.
 */
function menu_views_superfish_menu_item_link(array $variables) {
  // Only intercept if this menu item link is a view.
  if (isset($variables['menu_item']['link']) && $view = _menu_views_replace_menu_item($variables['menu_item']['link'])) {
    return '<div class="menu-view">' . $view . '</div>';
  }
  // Otherwise, use the default theming function.
  return theme('menu_views_superfish_menu_item_link_default', $variables);
}

function _menu_views_replace_menu_item($element) {
  $output = FALSE;
  // Only process menu links that are not on admin pages (see: http://drupal.org/node/1790444).
  if (!path_is_admin(current_path())) {
    $item = _menu_views_item($element);
    if ($item['type'] == 'view' && $item['view']['name'] && $item['view']['display']) {
      $element['#attributes']['class'][] = 'menu-views';
      if ($view = views_get_view($item['view']['name'])) {
        if ($view->access($item['view']['display']) && $view->set_display($item['view']['display'])) {
          $output = '';
          $view->set_arguments(explode('/', _menu_views_tokenize_arguments($item)));
          // Provide title options for the view.
          if ((bool)$item['view']['settings']['title']) {
            $title = $item['view']['settings']['title_override'];
            if (empty($title)) {
              $title = $view->get_title();
            }
            if (!empty($title)) {
              $wrapper = $item['view']['settings']['title_wrapper'];
              if ($wrapper === '0') {
                $wrapper = FALSE;
              }
              elseif ($wrapper === '') {
                $wrapper = 'h3';
              }
              if ($wrapper) {
                $output .= '<' . $wrapper . (!empty($item['view']['settings']['title_classes']) ? ' class="' . $item['view']['settings']['title_classes'] . '"' : '') . '>';
              }
              $output .= $title;
              if ($wrapper) {
                $output .= '</' . $wrapper . '>';
              }
            }
          }
          $output .= $view->preview();
        }
      }
    }
  }
  return $output;
}

function _menu_views_item(&$element = array(), &$form_state = array()) {
  // Default values.
  $item = $default = array(
    'mlid' => 0,
    'type' => 'link',
    'tree' => FALSE,
    'original_path' => '',
    'view' => array(
      'name' => FALSE,
      'display' => FALSE,
      'arguments' => '',
      'settings' => array(
        'title' => FALSE,
        'title_wrapper' => '',
        'title_classes' => '',
        'title_override' => '',
      ),
    ),
  );
  $provided = FALSE;
  // Attempt to load the menu view element for a link.
  if (isset($element['menu_views'])) {
    $provided = &$element['menu_views'];
  }
  if (!$provided && isset($element['localized_options']['menu_views'])) {
    $provided = &$element['localized_options']['menu_views'];
  }
  if (!$provided && isset($element['#localized_options']['menu_views'])) {
    $provided = &$element['#localized_options']['menu_views'];
  }
  // If the menu view element were not set, attempt to determine if this is a form.
  if (!$provided && isset($element['options'])) {
    if (isset($element['options']['#tree']) && $element['options']['#tree']) {
      $item['tree'] = TRUE;
    }
    if (isset($element['original_item']['#value']['options']['menu_views'])) {
      $provided = &$element['original_item']['#value']['options']['menu_views'];
    }
    if ($item['tree'] && isset($form_state['values']['options']['menu_views'])) {
      $provided = &$form_state['values']['options']['menu_views'];
    }
    elseif (isset($form_state['values']['menu_views'])) {
      $provided = &$form_state['values']['menu_views'];
    }
    if (isset($form_state['values']['menu_item_type'])) {
      $item['type'] = $form_state['values']['menu_item_type'];
    }
  }
  // By default, the menu view returns default values (no view). If settings were provided by an element or form item, then use those.
  if ($provided) {
    // Extract available element settings to use for this menu view.
    foreach (array('mlid', 'type', 'original_path', 'view') as $property) {
      if (isset($provided[$property]) && !empty($provided[$property])) {
        if (is_array($item[$property])) {
          $item[$property] = array_merge($item[$property], (array)$provided[$property]);
        }
        else {
          $item[$property] = $provided[$property];
        }
      }
    }
  }
  // Filter out any disabled views.
  $views = array_keys(views_get_enabled_views());
  if (!in_array($item['view']['name'], $views)) {
    $item['view'] = $default['view'];
  }
  return $item;
}

/**
 * Helper function to remove the attached view options from a menu item.
 */
function _menu_views_remove_element($item) {
  if ($item['element']) {
    unset($item['element']);
  }
}

/**
 * Helper function to tokenize an attached menu item view's arguments.
 */
function _menu_views_tokenize_arguments($item, $human_readable = FALSE) {
  $args = explode('/', $item['view']['arguments']);
  if (count($args) && $item['mlid']) {
    $context['menu-link'] = menu_link_load($item['mlid']);
    $options = array(
      'callback' => '_menu_views_tokens_callback',
      'clear' => TRUE,
      'human_readable' => $human_readable,
    );
    foreach ($args as $key => $arg) {
      $args[$key] = token_replace($arg, $context, $options);
    }
  }
  return implode('/', $args);
}

/**
 * Implements hook_token_info().
 */
function menu_views_token_info() {
  $tokens['tokens']['menu-link']['node'] = array(
    'name' => t('Node'),
    'description' => t('The node of the menu link.'),
    'type' => 'node',
  );
  $tokens['tokens']['menu-link']['parent']['node'] = array(
    'name' => t('Node'),
    'description' => t('The node of the menu link\'s parent.'),
    'type' => 'node',
  );
  return $tokens;
}

/**
 * Implements hook_tokens().
 */
function menu_views_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $url_options = array('absolute' => TRUE);
  if (isset($options['language'])) {
    $url_options['language'] = $options['language'];
    $language_code = $options['language']->language;
  }
  else {
    $language_code = NULL;
  }
  $sanitize = !empty($options['sanitize']);

  $replacements = array();
  $node = FALSE;
  $parent = FALSE;
  // Menu link tokens.
  if ($type == 'menu-link' && !empty($data['menu-link'])) {
    $link = (array) $data['menu-link'];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'node':
          $node = menu_get_object('node', 1, $link['link_path']);
          if ($node && $node->nid) {
            $title = $node->title;
            $replacements[$original] = $sanitize ? filter_xss($title) : $title;
          }
          else {
            $replacements[$original] = NULL;
          }
          break;
        case 'parent':
          if (isset($link['plid']) &&
            !empty($link['plid']) &&
            ($parent = menu_link_load($link['plid'])) &&
            ($node = menu_get_object('node', 1, $parent['link_path'])) &&
            $node->nid
          ) {
            $title = $node->title;
            $replacements[$original] = $sanitize ? filter_xss($title) : $title;
          }
          else {
            $replacements[$original] = NULL;
          }
          break;
      }
    }
    // Chained token relationships.
    if (($node_tokens = token_find_with_prefix($tokens, 'node')) &&
      $node = menu_get_object('node', 1, $link['link_path'])
    ) {
      $replacements += token_generate('node', $node_tokens, array('node' => $node), $options);
    }
    if (($parent_tokens = token_find_with_prefix($tokens, 'parent')) &&
      isset($link['plid']) &&
      !empty($link['plid']) &&
      ($parent = menu_link_load($link['plid'])) &&
      ($node = menu_get_object('node', 1, $parent['link_path'])) &&
      $node->nid
    ) {
      $replacements += token_generate('node', $parent_tokens, array('node' => $node), $options);
    }
  }
  return $replacements;
}

/**
 * Callback for human-readable token value replacements.
 */
function _menu_views_tokens_callback(&$replacements, $data, $options) {
  foreach ($replacements as $token => $value) {
    if ($options['human_readable']) {
      if (is_bool($value)) {
        $value = $value ? t('TRUE') : t('FALSE');
      }
      elseif (is_object($value)) {
        $value = t('Object');
      }
      elseif (is_array($value)) {
        $value = t('Array');
      }
      elseif (is_null($value)) {
        $value = t('NULL');
      }
      else {
        $value = (string) $value;
      }
      if ($value === '') {
        $value = t('NULL');
      }
    }
    $replacements[$token] = urlencode($value);
  }
}