crufty, residual code from when we had a router quickref table removed
[project/panels.git] / panels_page / panels_page.menu.inc
1 <?php
2 // $Id$
3
4 /**
5 * @file panels_page.menu.inc
6 *
7 * Functions resposnible for constructing the panels_page menu routing &
8 * overriding system.
9 */
10
11 function panels_page_admin_static_menu_items() {
12 $items = array();
13 $admin = array(
14 // TODO is 'create panel-pages' still the best name for this perm?
15 // TODO we'll need to granulate this perm significantly in panels3.
16 'access arguments' => array('create panel-pages'),
17 'file' => 'panels_page.admin.inc',
18 'type' => MENU_LOCAL_TASK,
19 );
20
21 $items['admin/panels/panel-page'] = array(
22 'title' => 'Panels Pages',
23 'page callback' => 'panels_page_list_page',
24 'type' => MENU_NORMAL_ITEM,
25 ) + $admin;
26 $items['admin/panels/panel-page/list'] = array(
27 'title' => 'List',
28 'type' => MENU_DEFAULT_LOCAL_TASK,
29 'weight' => -10,
30 ) + $admin;
31 $items['admin/panels/panel-page/settings'] = array(
32 'title' => 'Settings',
33 'page callback' => 'panels_page_settings',
34 'weight' => -5,
35 ) + $admin;
36 $items['admin/panels/panel-page/add'] = array(
37 'title' => 'Add',
38 'page callback' => 'panels_page_add_handler',
39 'weight' => 0,
40 ) + $admin;
41 $items['admin/panels/panel-page/import'] = array(
42 'title' => 'Import',
43 'page callback' => 'panels_page_import_page',
44 'weight' => 5,
45 ) + $admin;
46 return $items;
47 }
48
49 function panels_page_admin_dynamic_menu_items($items = array(), $path_prefix = NULL) {
50 // TODO allowing a dynamic path prefix may be superfluous
51 if (is_null($path_prefix)) {
52 $path_prefix = 'admin/panels/panel-page/';
53 }
54 $loader_arg = count(explode('/', $path_prefix)) - 1;
55
56 $admin = array(
57 'access arguments' => array('create panel-pages'),
58 'file' => 'panels_page.admin.inc',
59 'page arguments' => array($loader_arg),
60 'type' => MENU_LOCAL_TASK,
61 );
62
63 $items[$path_prefix . '%panels_page_admin/edit'] = array(
64 'title' => 'Settings',
65 'page callback' => 'panels_page_edit',
66 'weight' => -10,
67 'type' => MENU_CALLBACK,
68 ) + $admin;
69 $items[$path_prefix . '%panels_page_admin/edit/settings'] = array(
70 'title' => 'Settings',
71 'weight' => -10,
72 'type' => MENU_DEFAULT_LOCAL_TASK,
73 ) + $admin;
74
75 // Alternate method for organizing the settings/advanced tabs. I think this is more
76 // intuitive; commenting for now until the menu work is done.
77 /*
78 $items['admin/panels/panel-page/%panels_page_admin/settings/general'] = array(
79 'title' => 'General Settings',
80 'page callback' => 'panels_page_edit',
81 'page arguments' => array(3),
82 'weight' => -10,
83 'type' => MENU_DEFAULT_LOCAL_TASK,
84 );
85 $items['admin/panels/panel-page/%panels_page_admin/settings/advanced'] = array(
86 'title' => 'Settings',
87 'page callback' => 'panels_page_edit_advanced',
88 'page arguments' => array(3),
89 'weight' => -8,
90 );
91 */
92 $items[$path_prefix . '%panels_page_admin/edit/advanced'] = array(
93 'title' => 'Advanced',
94 'page callback' => 'panels_page_edit_advanced',
95 'weight' => -8,
96 ) + $admin;
97 $items[$path_prefix . '%panels_page_admin_cache/edit/context'] = array(
98 'title' => 'Context',
99 'page callback' => 'panels_page_edit_context',
100 'weight' => -6,
101 ) + $admin;
102 $items[$path_prefix . '%panels_page_admin/edit/layout'] = array(
103 'title' => 'Layout',
104 'page callback' => 'panels_page_edit_layout',
105 'load arguments' => array($loader_arg + 4),
106 'weight' => -4,
107 ) + $admin;
108 $items[$path_prefix . '%panels_page_admin_cache/edit/layout-settings'] = array(
109 'title' => 'Layout settings',
110 'page callback' => 'panels_page_edit_layout_settings',
111 'load arguments' => array($loader_arg + 4),
112 'weight' => -2,
113 ) + $admin;
114 $items[$path_prefix . '%panels_page_admin_cache/edit/content'] = array(
115 'title' => 'Content',
116 'page callback' => 'panels_page_edit_content',
117 'load arguments' => array($loader_arg + 4),
118 'weight' => 0,
119 ) + $admin;
120 $items[$path_prefix . '%panels_page_admin/edit/preview'] = array(
121 'title' => 'Preview',
122 'page callback' => 'drupal_get_form',
123 'page arguments' => array('panels_page_preview_page', $loader_arg),
124 'weight' => 2,
125 ) + $admin;
126 $items[$path_prefix . '%panels_page_admin/edit/export'] = array(
127 'title' => 'Export',
128 'page callback' => 'drupal_get_form',
129 'page arguments' => array('panels_page_export_page', $loader_arg),
130 'weight' => 4,
131 ) + $admin;
132 return $items;
133 }
134
135 function panels_page_create_menu_structure($new_items = NULL) {
136 static $items = array();
137 // The first time through, we _always_ construct the menu tree.
138 if (empty($items)) {
139 $items = _panels_page_create_menu_structure();
140 }
141 if (!is_null($new_items)) {
142 $items = $new_items;
143 }
144 return $items;
145 }
146
147 function _panels_page_menu_alter(&$callbacks) {
148 $panels_items = panels_page_create_menu_structure();
149 $matches = array();
150 while (list($path, $item) = each($callbacks)) {
151 if (strpos($path, '%') === FALSE) {
152 continue;
153 }
154 $raw_path = panels_page_get_raw_path($path);
155 if (isset($panels_items['menu items'][$raw_path])) {
156 $panels_items['menu items'][$path] = array_merge($item, $panels_items['menu items'][$raw_path]);
157 $matches[$path] = TRUE;
158 // TODO this is another point that hardcodes one item per path.
159 // Only unset if the raw path is different (e.g. taxonomy isn't)
160 if ($raw_path != $path) {
161 unset($panels_items['menu items'][$raw_path]);
162 }
163 }
164 }
165 // Insert all the overridden routers into our separate table.
166 _panels_page_menu_router_build($callbacks, $matches);
167 // Replace all the overridden routers with our own.
168 $callbacks = array_merge($callbacks, $panels_items['menu items']);
169
170 foreach ($panels_items['menu items'] as $path => $item) {
171 $raw_path = panels_page_get_raw_path($path);
172 // Only save a router quickref if the item has metadata
173 if (!empty($panels_items['metadata'][$raw_path])) {
174 $metadata = &$panels_items['metadata'][$raw_path];
175 $metadata->loader_flags |= isset($matches[$path]) ? PANELS_HAS_FALLBACK_ROUTER : 0;
176 db_query('UPDATE {panels_page} SET loader_flags = %d WHERE pid = %d', $metadata->loader_flags, $metadata->pid);
177 }
178 }
179 // Re-call our helper function and store updated items into the static cache.
180 panels_page_create_menu_structure($panels_items);
181 }
182
183 function _panels_page_create_menu_structure() {
184 panels_load_include('plugins');
185 $items = array();
186 $panels = panels_page_load_all();
187 foreach ($panels as $panel_page) {
188 if (empty($panel_page->disabled)) {
189 $map = explode('/', $panel_page->path);
190 if (strpos($panel_page->path, '%') === FALSE) {
191 panels_page_construct_static_menu_link($items, $panel_page, $map);
192 }
193 else {
194 panels_page_construct_dynamic_menu_link($items, $panel_page, $map);
195 }
196 }
197 }
198 return $items;
199 }
200
201 function panels_page_construct_dynamic_menu_link(&$items, $panel_page, $map) {
202 $type = _panels_page_menu_type($panel_page);
203 $primary_wildcard = array_search('%', $map);
204 // FIXME (this data) quickref should be entirely absorbed into the panels_page table itself.
205 panels_page_construct_menu_item_metadata($items, $panel_page, PANELS_IS_DYNAMIC);
206
207 // Construct the dynamic menu router item. If/when we get to multiple
208 // panels_pages per dynamic path, we needn't worry about overwriting here.
209 $items['menu items'][$panel_page->path] = _panels_page_construct_dynamic_menu_link(
210 $panel_page, array('pid-' . $panel_page->pid, $primary_wildcard),
211 array('arguments' => array('pid-' . $panel_page->pid, $primary_wildcard)),
212 $type);
213
214 // FIXME parents are borked
215 _panels_page_construct_parent_menu_item($items, $panel_page, $panel_page->path, $type);
216 }
217
218 /**
219 * Helper function to create a menu item for a panel.
220 */
221 function _panels_page_construct_dynamic_menu_link($panel_page, $page_arguments, $access, $type, $weight = 0) {
222 return array(
223 'title callback' => 'panels_page_title_handler',
224 'title arguments' => $page_arguments,
225 'page callback' => 'panels_page_render_handler',
226 'page arguments' => $page_arguments,
227 'type' => $type,
228 'weight' => $weight,
229 'module' => 'panels_page',
230 'file' => NULL, // Ensure we don't get the overriddee's file property
231 );
232 }
233
234 /**
235 * Build a panels_page menu entry for a static panels_page.
236 */
237 function panels_page_construct_static_menu_link(&$items, $panel_page, $map) {
238 $type = _panels_page_menu_type($panel_page);
239 panels_page_construct_menu_item_metadata($items, $panel_page);
240
241 $items['menu items'][$panel_page->path] = array(
242 'title' => filter_xss_admin(panels_page_get_title($panel_page, 'menu')),
243 'access callback' => 'panels_page_access_handler',
244 'access arguments' => array('pid-' . $panel_page->pid),
245 'page callback' => 'panels_page_static_render_handler',
246 'page arguments' => array($panel_page->name),
247 'type' => $type,
248 'module' => 'panels_page',
249 'file' => 'panels_page.render.inc',
250 );
251 _panels_page_construct_parent_menu_item($items, $panel_page, $panel_page->path, $type);
252 }
253
254 function panels_page_construct_menu_item_metadata(&$items, $panel_page, $flags = 0) {
255 $metadata = new stdClass();
256 $metadata->pid = $panel_page->pid;
257 $metadata->name = $panel_page->name;
258 $metadata->type = _panels_page_menu_type($panel_page);
259 $metadata->path = $panel_page->path;
260 $metadata->loader_flags = $flags;
261
262 $items['metadata'][$panel_page->path] = $metadata;
263 }
264
265 /**
266 * Create a parent menu item for a panel page.
267 */
268 function _panels_page_construct_parent_menu_item(&$items, $panel_page, $path, $type) {
269 if ($type == MENU_DEFAULT_LOCAL_TASK && dirname($path) && dirname($path) != '.') {
270 // FIXME this is currently completely borked - if we end up inside this
271 // control statement, everything will break. However, we should also be eliminating
272 // the statement later.
273 switch ($panel_page->menu_tab_default_parent_type) {
274 case 'tab':
275 $parent_type = MENU_LOCAL_TASK;
276 break;
277
278 case 'normal':
279 $parent_type = MENU_NORMAL_ITEM;
280 break;
281
282 default:
283 case 'existing':
284 $parent_type = 0;
285 break;
286 }
287 if ($parent_type) {
288 $title = filter_xss_admin(panels_page_get_title($panel_page, 'menu-parent'));
289 $weight = $panel_page->menu_parent_tab_weight;
290 // FIXME this function doesn't even exist anymore.
291 $items[$path] = _panels_page_menu_item($path, $title, $panel_page, $args, $access, $parent_type, $weight);
292 }
293 }
294 }
295
296 /**
297 * Determine what menu type a panel needs to use.
298 */
299 function _panels_page_menu_type($panel_page) {
300 if ($panel_page->menu) {
301 if ($panel_page->menu_tab_default) {
302 $type = MENU_DEFAULT_LOCAL_TASK;
303 }
304 else if ($panel_page->menu_tab) {
305 $type = MENU_LOCAL_TASK;
306 }
307 else {
308 $type = MENU_NORMAL_ITEM;
309 }
310 }
311 else {
312 $type = MENU_CALLBACK;
313 }
314 return $type;
315 }
316
317 function panels_page_router_table_query_fields($table) {
318 static $query_building_blocks = array();
319 if (empty($query_building_blocks[$table])) {
320 $schema = drupal_get_schema($table);
321 foreach ($schema['fields'] as $field => $data) {
322 $query_building_blocks[$table][$field] = db_type_placeholder($data['type']);
323 }
324 }
325 return $query_building_blocks[$table];
326 }
327
328 function panels_page_get_raw_path($path) {
329 return preg_replace('/%([a-z_]*)/', '%', $path);
330 }
331
332 /**
333 * Modified version of _menu_router_build();
334 */
335 function _panels_page_menu_router_build($callbacks, $matches) {
336 $menu = array();
337 foreach ($matches as $path => $match) {
338 $item = $callbacks[$path];
339 $load_functions = array();
340 $to_arg_functions = array();
341 $fit = 0;
342 $move = FALSE;
343
344 $parts = explode('/', $path, MENU_MAX_PARTS);
345 $number_parts = count($parts);
346 // We store the highest index of parts here to save some work in the fit
347 // calculation loop.
348 $slashes = $number_parts - 1;
349 // Extract load and to_arg functions.
350 foreach ($parts as $k => $part) {
351 $match = FALSE;
352 if (preg_match('/^%([a-z_]*)$/', $part, $matches)) {
353 if (empty($matches[1])) {
354 $match = TRUE;
355 $load_functions[$k] = NULL;
356 }
357 else {
358 if (function_exists($matches[1] .'_to_arg')) {
359 $to_arg_functions[$k] = $matches[1] .'_to_arg';
360 $load_functions[$k] = NULL;
361 $match = TRUE;
362 }
363 if (function_exists($matches[1] .'_load')) {
364 $function = $matches[1] .'_load';
365 // Create an array of arguments that will be passed to the _load
366 // function when this menu path is checked, if 'load arguments'
367 // exists.
368 $load_functions[$k] = isset($item['load arguments']) ? array($function => $item['load arguments']) : $function;
369 $match = TRUE;
370 }
371 }
372 }
373 if ($match) {
374 $parts[$k] = '%';
375 }
376 else {
377 $fit |= 1 << ($slashes - $k);
378 }
379 }
380 if ($fit) {
381 $move = TRUE;
382 }
383 else {
384 // If there is no %, it fits maximally.
385 $fit = (1 << $number_parts) - 1;
386 }
387 $masks[$fit] = 1;
388 $item['load_functions'] = empty($load_functions) ? '' : serialize($load_functions);
389 $item['to_arg_functions'] = empty($to_arg_functions) ? '' : serialize($to_arg_functions);
390 $item += array(
391 'title' => '',
392 'weight' => 0,
393 'type' => MENU_NORMAL_ITEM,
394 '_number_parts' => $number_parts,
395 '_parts' => $parts,
396 '_fit' => $fit,
397 );
398 $item += array(
399 '_visible' => (bool)($item['type'] & MENU_VISIBLE_IN_BREADCRUMB),
400 '_tab' => (bool)($item['type'] & MENU_IS_LOCAL_TASK),
401 );
402 if ($move) {
403 $new_path = implode('/', $item['_parts']);
404 $menu[$new_path] = $item;
405 $sort[$new_path] = $number_parts;
406 }
407 else {
408 $menu[$path] = $item;
409 $sort[$path] = $number_parts;
410 }
411 }
412 array_multisort($sort, SORT_NUMERIC, $menu);
413 if (!$menu) {
414 // We must have a serious error - there is no data to save.
415 watchdog('php', 'The specialized Panels menu router rebuild failed. Some paths may not work correctly.', array(), WATCHDOG_ERROR);
416 return array();
417 }
418 db_query('DELETE FROM {panels_page_router_store}');
419 foreach ($menu as $path => $v) {
420 $item = &$menu[$path];
421 if (!$item['_tab']) {
422 // Non-tab items.
423 $item['tab_parent'] = '';
424 $item['tab_root'] = $path;
425 }
426 for ($i = $item['_number_parts'] - 1; $i; $i--) {
427 $parent_path = implode('/', array_slice($item['_parts'], 0, $i));
428 if (isset($menu[$parent_path])) {
429
430 $parent = $menu[$parent_path];
431
432 if (!isset($item['tab_parent'])) {
433 // Parent stores the parent of the path.
434 $item['tab_parent'] = $parent_path;
435 }
436 if (!isset($item['tab_root']) && !$parent['_tab']) {
437 $item['tab_root'] = $parent_path;
438 }
439 // If an access callback is not found for a default local task we use
440 // the callback from the parent, since we expect them to be identical.
441 // In all other cases, the access parameters must be specified.
442 if (($item['type'] == MENU_DEFAULT_LOCAL_TASK) && !isset($item['access callback']) && isset($parent['access callback'])) {
443 $item['access callback'] = $parent['access callback'];
444 if (!isset($item['access arguments']) && isset($parent['access arguments'])) {
445 $item['access arguments'] = $parent['access arguments'];
446 }
447 }
448 // Same for page callbacks.
449 if (!isset($item['page callback']) && isset($parent['page callback'])) {
450 $item['page callback'] = $parent['page callback'];
451 if (!isset($item['page arguments']) && isset($parent['page arguments'])) {
452 $item['page arguments'] = $parent['page arguments'];
453 }
454 if (!isset($item['file']) && isset($parent['file'])) {
455 $item['file'] = $parent['file'];
456 }
457 if (!isset($item['file path']) && isset($parent['file path'])) {
458 $item['file path'] = $parent['file path'];
459 }
460 }
461 }
462 }
463 if (!isset($item['access callback']) && isset($item['access arguments'])) {
464 // Default callback.
465 $item['access callback'] = 'user_access';
466 }
467 if (!isset($item['access callback']) || empty($item['page callback'])) {
468 $item['access callback'] = 0;
469 }
470 if (is_bool($item['access callback'])) {
471 $item['access callback'] = intval($item['access callback']);
472 }
473
474 $item += array(
475 'access arguments' => array(),
476 'access callback' => '',
477 'page arguments' => array(),
478 'page callback' => '',
479 'block callback' => '',
480 'title arguments' => array(),
481 'title callback' => 't',
482 'description' => '',
483 'position' => '',
484 'tab_parent' => '',
485 'tab_root' => $path,
486 'path' => $path,
487 'file' => '',
488 'file path' => '',
489 'include file' => '',
490 );
491
492 // Calculate out the file to be included for each callback, if any.
493 if ($item['file']) {
494 $file_path = $item['file path'] ? $item['file path'] : drupal_get_path('module', $item['module']);
495 $item['include file'] = $file_path .'/'. $item['file'];
496 }
497
498 $title_arguments = $item['title arguments'] ? serialize($item['title arguments']) : '';
499 db_query("INSERT INTO {panels_page_router_store}
500 (path, load_functions, to_arg_functions, access_callback,
501 access_arguments, page_callback, page_arguments, fit,
502 number_parts, tab_parent, tab_root,
503 title, title_callback, title_arguments,
504 type, block_callback, description, position, weight, file)
505 VALUES ('%s', '%s', '%s', '%s',
506 '%s', '%s', '%s', %d,
507 %d, '%s', '%s',
508 '%s', '%s', '%s',
509 %d, '%s', '%s', '%s', %d, '%s')",
510 $path, $item['load_functions'], $item['to_arg_functions'], $item['access callback'],
511 serialize($item['access arguments']), $item['page callback'], serialize($item['page arguments']), $item['_fit'],
512 $item['_number_parts'], $item['tab_parent'], $item['tab_root'],
513 $item['title'], $item['title callback'], $title_arguments,
514 $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight'], $item['include file']);
515 }
516 }