Some nifty Views improvements
[project/panels.git] / panels.module
CommitLineData
9f3609c9
EM
1<?php
2// $Id$
3
4/**
5 * Implementation of hook_help()
6 */
7function panels_help($section = '') {
8 switch ($section) {
9 case 'admin/modules#description':
10 return t('The panels module allows the creation of pages with flexible layouts.');
11 case 'admin/panels':
12 case 'admin/panels/list':
13 return t('<p>You may peruse a list of your current panels layouts and edit them, or click add to create a new page.</p>');
14 case 'admin/panels/add':
15 return t('<p>Choose a layout for your new page from the list below.</p>');
16 }
17}
18
19/**
20 * Implementation of hook_perm()
21 */
22function panels_perm() {
a2df027f 23 return array('create panels');
9f3609c9
EM
24}
25
26/**
27 * Implementation of hook_menu()
28 */
29function panels_menu($may_cache) {
30 if ($may_cache) {
a2df027f 31 $access = user_access('create panels');
9f3609c9
EM
32 $items[] = array(
33 'path' => 'admin/panels',
34 'title' => t('panels'),
35 'access' => $access,
36 'callback' => 'panels_list_page',
37 );
38 $items[] = array(
39 'path' => 'admin/panels/list',
40 'title' => t('list'),
41 'access' => $access,
42 'callback' => 'panels_list_page',
43 'weight' => -10,
44 'type' => MENU_DEFAULT_LOCAL_TASK,
45 );
46 $items[] = array(
47 'path' => 'admin/panels/add',
48 'title' => t('add'),
49 'access' => $access,
50 'callback' => 'panels_add_page',
51 'type' => MENU_LOCAL_TASK,
52 );
53 $items[] = array(
54 'path' => 'admin/panels/add/layout',
55 'title' => t('add'),
56 'access' => $access,
57 'callback' => 'panels_add_layout_page',
58 'type' => MENU_LOCAL_TASK,
59 );
60 $items[] = array(
61 'path' => 'admin/panels/edit',
62 'title' => t('edit panels'),
63 'access' => $access,
64 'callback' => 'panels_edit_page',
65 'type' => MENU_CALLBACK,
66 );
67 $items[] = array(
68 'path' => 'admin/panels/delete',
69 'title' => t('delete panels'),
70 'access' => $access,
71 'callback' => 'panels_delete_page',
72 'type' => MENU_CALLBACK,
73 );
74
75 $items[] = array(
76 'path' => 'panels/node/autocomplete',
77 'title' => t('autocomplete node'),
78 'callback' => 'panels_node_autocomplete',
79 'access' => user_access('access content'),
80 'type' => MENU_CALLBACK
81 );
82
a2df027f 83 // load panels from database
9f3609c9
EM
84 $result = db_query("SELECT * FROM {panels_info}");
85 // FIXME: Fow now we're making these all callbacks, but we
86 // should steal code from Views so they can be normal, tabs,
87 // etc
88 while ($panels = db_fetch_object($result)) {
89 $items[] = array(
90 'path' => $panels->path,
91 'title' => $panels->title,
92 'access' => panels_access(unserialize($panels->access)),
93 'callback' => 'panels_panels_page',
94 'callback arguments' => array($panels->did),
95 'type' => MENU_CALLBACK
96 );
97 }
98 }
99 return $items;
100}
101
102/**
103 * Determine whether or not the current user has access to this
104 * panels.
105 */
106function panels_access($access) {
107 // for now
108 return TRUE;
109}
110
111/**
112 * panels path helper function
113 */
114function panels_get_file_path($module, $file, $base_path = true) {
115 if ($base_path) {
116 $output = base_path();
117 }
118 return $output . drupal_get_path('module', $module) . '/' . $file;
119}
120
121// ---------------------------------------------------------------------------
122// panels custom image button
123
124/**
125 * Custom form element to do our nice images.
126 */
127function panels_elements() {
128 $type['panels_imagebutton'] = array('#input' => TRUE, '#button_type' => 'submit',);
129 return $type;
130}
131
132/**
133 * Theme our image button.
134 */
135function theme_panels_imagebutton($element) {
136 return '<input type="image" class="form-'. $element['#button_type'] .'" name="'. $element['#name'] .'" value="'. check_plain($element['#default_value']) .'" '. drupal_attributes($element['#attributes']) . ' src="' . $element['#image'] . '" alt="' . $element['#title'] . '" title="' . $element['#title'] . "\" />\n";
137}
138
139function panels_imagebutton_value() {
140 // null function guarantees default_value doesn't get moved to #value.
141}
142
143/**
144 * Add a single button to a form.
145 */
146function panels_add_button($image, $name, $text) {
147 $module_path = base_path() . drupal_get_path('module', 'panels');
148
149 return array(
150 '#type' => 'panels_imagebutton',
151 '#image' => $module_path . '/images/' . $image,
152 '#title' => $text,
153 '#default_value' => $name,
154 );
155}
156
157/**
158 * Set a button to a blank image -- used for placeholders when buttons are
159 * not relevant but just removing it would be visually unappealing.
160 */
161function panels_set_blank(&$form) {
162 $form['#type'] = 'markup';
163 $form['#value'] = theme('image', drupal_get_path('module', 'panels') . '/images/blank.gif');
164}
165
166// ---------------------------------------------------------------------------
167// panels administrative pages
168
169/**
a2df027f 170 * Provide a list of panels, with links to edit or delete them.
9f3609c9
EM
171 */
172function panels_list_page() {
173 $result = db_query("SELECT * FROM {panels_info} ORDER BY title");
174 while ($panels = db_fetch_object($result)) {
175 $item = array();
176 $item[] = check_plain($panels->title);
177 $item[] = l($panels->path, $panels->path);
178 $item[] = implode(' | ', array(
179 l('edit', "admin/panels/edit/$panels->did"),
180 l('delete', "admin/panels/delete/$panels->did"),
181 ));
182 $items[] = $item;
183 }
184 $header = array(
b691d1ca 185 t('Panel title'),
9f3609c9
EM
186 t('URL'),
187 t('Operations'),
188 );
189 $output = theme('table', $header, $items);
190 return $output;
191}
192
193/*
b691d1ca 194 * Provide a form to confirm deletion of a panel page.
9f3609c9
EM
195 */
196function panels_delete_page($did = '') {
197 $panels = panels_load_panels($did);
198
199 if (!$panels) {
200 return 'admin/panels';
201 }
202
203 $form['did'] = array('#type' => 'value', '#value' => $panels->did);
204 return confirm_form('panels_delete_confirm', $form,
205 t('Are you sure you want to delete %title?', array('%title' => $panels->title)),
206 $_GET['destination'] ? $_GET['destination'] : 'admin/panels',
207 t('This action cannot be undone.'),
208 t('Delete'), t('Cancel')
209 );
210}
211
212/*
b691d1ca 213 * Handle the submit button to delete a panel page.
9f3609c9
EM
214 */
215function panels_delete_confirm_submit($formid, $form) {
216 if ($form['confirm']) {
217 panels_delete_panels((object) $form);
218 drupal_goto('admin/panels');
219 }
220}
221
222/**
223 * Handle the add panels page
224 */
225function panels_add_page($layout = NULL) {
226 $layouts = panels_get_layouts();
227 theme_add_style(drupal_get_path('module', 'panels') . '/panels_admin.css');
228 if (!$layout) {
229 foreach ($layouts as $id => $layout) {
230 if (!$default_id) {
231 // grab the first one for our default.
232 $default_id = $id;
233 }
234 $file = panels_get_file_path($layout['module'], $layout['icon'], false);
235 $output .= theme('panels_add_image', $layout[title], $id, l(theme('image', $file), $_GET['q'] . '/' . $id, NULL, NULL, NULL, NULL, TRUE));
236 }
237 return $output;
238 }
239
240 if (!$layouts[$layout]) {
241 return drupal_not_found();
242 }
243
244 $panels->layout = $layout;
245 return panels_edit_form($panels);
246}
247
248function theme_panels_add_image($title, $id, $image) {
249 $output .= '<div class="layout-link">';
250 $output .= $image;
251 $output .= '<div>' . l($title, $_GET['q'] . '/' . $id) . '</div>';
252 $output .= '</div>';
253 return $output;
254}
255// ---------------------------------------------------------------------------
256// panels administrative pages
257
258function panels_edit_page($did = NULL) {
259 if (!$did || !($panels = panels_load_panels($did))) {
260 return drupal_not_found();
261 }
262 return panels_edit_form($panels);
263}
264
265/**
266 * shortcut to ease the syntax of the various form builder tricks we use.
267 */
268function panels_form_builder(&$form, $form_id = 'panels_edit_form') {
269 $form = form_builder($form_id, $form);
270}
271
272/**
273 * Edit an already loaded panels.
274 */
275function panels_edit_form($panels) {
276 theme_add_style(drupal_get_path('module', 'panels') . '/panels_admin.css');
277 $layouts = panels_get_layouts();
278 $layout = $layouts[$panels->layout];
279
280 $content_types = panels_get_content_types();
281
282 // Process all our add button stuff first so we can add stuff to the
283 // form semi dynamically.
284
285 $form['add'] = array(
286 '#type' => 'fieldset',
287 '#title' => t('Add content'),
288 '#collapsible' => false,
289 '#description' => t('Select an area to add content to, then select a type of content and click the appropriate button. The content will be added.'),
290 );
291
292 $default_radio = array_shift(array_keys($layout['content areas']));
293 $form['add']['area'] = array(
294 '#type' => 'radios',
295 '#title' => t('Area'),
296 '#options' => $layout['content areas'],
297 '#prefix' => '<div class="container-inline">',
298 '#suffix' => '</div>',
299 '#default_value' => $default_radio,
300 );
301 foreach ($content_types as $id => $type) {
302 $function = $type['admin'];
303 if (function_exists($function)) {
304 global $form_values;
305 $form['add'][$id] = $function('add button', $dummy);
306
307 // $dummy needed for cause you can't have default args on a reference.
308 $form['add'][$id]['#parents'] = array('add', $id);
309 $form['add'][$id]['#tree'] = true;
310 panels_form_builder($form['add'][$id]);
311
312 if ($conf = $function('add', $form_values['add'][$id])) {
2b02136c 313 $add->configuration = $conf;
9f3609c9
EM
314 $add->type = $id;
315 $form['add']['area']['#parents'] = array('area');
316 panels_form_builder($form['add']['area']);
317 $add->area = $form_values['area'];
318 }
319 }
320 }
321
322 $form['layout'] = array(
323 '#type' => 'value',
324 '#value' => $panels->layout
325 );
326
327 $form['did'] = array(
328 '#type' => 'value',
329 '#value' => $panels->did,
330 );
331
332 $form['info'] = array(
333 '#type' => 'fieldset',
334 '#title' => t('General information'),
335 '#collapsible' => false,
336 );
337
338 $file = panels_get_file_path($layout['module'], $layout['icon'], false);
339 $icon .= theme('image', $file);
340 $form['info']['layout-icon'] = array(
341 '#value' => '<div class="layout-icon">' . $icon . '</div>',
342 );
343
344 $form['info']['layout-display'] = array(
345 '#value' => '<strong>Layout</strong>: ' . $layout['title'],
346 );
347
348 $form['info']['title'] = array(
349 '#type' => 'textfield',
350 '#default_value' => $panels->title,
351 '#title' => t('Page title'),
352 '#description' => t('The page title for this panels layout'),
353 );
354
355 $form['info']['css_id'] = array(
356 '#type' => 'textfield',
357 '#default_value' => $panels->css_id,
358 '#title' => t('CSS ID'),
359 '#description' => t('The CSS ID to apply to this page'),
360 );
361
362 $form['info']['path'] = array(
363 '#type' => 'textfield',
364 '#default_value' => $panels->path,
365 '#title' => t('Path'),
366 '#description' => t('The URL path to give this page, i.e, path/to/page'),
367 '#required' => TRUE,
368 );
369
370 $form['content'] = array(
371 '#tree' => true,
372 );
373
374 // Go through our content areas and display what we have.
375 foreach ($layout['content areas'] as $area => $title) {
376 $list = array();
377 $form['content'][$area] = array(
378 '#tree' => true,
379 );
380
381 // Construct the order, feeding it the default order for what
382 // we know about. When we pull it back out, it may well be
383 // different due to past submits.
384 $order = array();
385 if (is_array($panels->content[$area])) {
386 $order = array_keys($panels->content[$area]);
387 }
388 $form['content'][$area]['order'] = array(
389 '#type' => 'hidden',
390 '#default_value' => serialize($order),
391 '#parents' => array('content', $area, 'order'),
392 );
393
394 // If an add button has added an item to the area, put it in and update
395 // the $order.
396 panels_form_builder($form['content'][$area]['order']);
397 $order = unserialize($form['content'][$area]['order']['#value']);
398 if ($add->area == $area) {
399 // say THIS 5 times real fast
400 if ($panels->content[$area]) {
401 $position = max(max(array_keys($order)), max(array_keys($panels->content[$area]))) + 1;
402 }
403 else if ($order) {
404 $position = max(array_keys($order)) + 1;
405 }
406 else {
407 $position = 0;
408 }
9f3609c9 409
2b02136c 410 $panels->content[$area][$position] = $add;
9f3609c9
EM
411 $order[] = $position;
412 $form['content'][$area]['order']['#value'] = serialize($order);
413 }
414
415 // Go through each item in the area and render it.
416 $count = count($order);
417 foreach ($order as $position => $id) {
418 // place buttons to re-order content.
419 $form['content'][$area][$id]['buttons'] = array(
420 '#parents' => array('content', $area, $id, 'buttons'),
421 '#tree' => TRUE
422 );
423 panels_add_buttons($form['content'][$area][$id]['buttons'], $count, $position);
424
425 // figure out if one of those buttons was pressed
426 panels_form_builder($form['content'][$area][$id]['buttons']);
427 $deleted = false;
428 foreach ($GLOBALS['form_values']['content'][$area][$id]['buttons'] as $button => $value) {
429 if ($value) {
430 $function = 'panels_move_' . $button;
431 $function($order, $position);
432 $form['content'][$area]['order']['#value'] = serialize($order);
433 if ($button == 'delete')
434 $deleted = true;
435 }
436 }
437 // If a content item was deleted, it still has buttons. Get rid of them.
438 // It had buttons because we needed to give it buttons to see if its
439 // buttons were clicked.
440 if ($deleted) {
441 unset($form['content'][$area][$id]['buttons']);
442 }
443 else {
444 // we finally get to add the conent item's content to the form.
445 $area_record = $panels->content[$area][$id];
446 $form['content'][$area][$id]['type'] = array(
447 '#type' => 'hidden',
448 '#default_value' => $area_record->type,
449 '#parents' => array('content', $area, $id, 'type'),
450 );
451 // retrieve what was already there -- so we can retain edits and
452 // content items that were added.
453 panels_form_builder($form['content'][$area][$id]['type']);
454 $type = $form['content'][$area][$id]['type']['#value'];
455 $function = $content_types[$type]['admin'];
456 if (function_exists($function)) {
457 $array = array(
458 '#tree' => true,
459 '#parents' => array('content', $area, $id, 'configuration'),
460 );
461 $form['content'][$area][$id]['configuration'] = array_merge($array, $function('edit', $area_record->configuration, array('content', $area, $id, 'configuration')));
462 panels_form_builder($form['content'][$area][$id]['configuration']);
463 }
464 }
465 }
466 }
467
468 $form['submit'] = array(
469 '#type' => 'submit',
470 '#value' => t('Save'),
471 );
472
473 return drupal_get_form('panels_edit_form', $form);
474}
475
476/**
477 * Display the form to edit a panels.
478 */
479function theme_panels_edit_form($form) {
480 $layouts = panels_get_layouts();
481 $layout = $layouts[$form['layout']['#value']];
482
483 $content_types = panels_get_content_types();
484
485 $output .= form_render($form['info']);
486 foreach ($layout['content areas'] as $area => $title) {
487 $order = unserialize($form['content'][$area]['order']['#value']);
488 if (!$order) {
489 $area_content = t('This area has no content.');
490 }
491 else {
492 $area_content = '';
493 $count = count($order);
494 foreach ($order as $position => $id) {
495 if ($count > 1) {
496 if ($position == 0 ) {
497 panels_set_blank($form['content'][$area][$id]['buttons']['up']);
498 }
499 else if ($position == ($count - 1)) {
500 panels_set_blank($form['content'][$area][$id]['buttons']['down']);
501 }
502 }
503 $type = $form['content'][$area][$id]['type']['#value'];
504 $function = $content_types[$type]['admin'];
505 if (function_exists($function)) {
506 // figure out the actual values; using the global because we need it
507 // to be in the same format it'll be in 'submit'.
508 global $form_values;
509 $conf_form = $form_values['content'][$area][$id]['configuration'];
510 $conf = $function('save', $conf_form);
511 $fieldset = array(
512 '#title' => t('Configure'),
513 '#children' => form_render($form['content'][$area][$id]['configuration']),
514 '#collapsible' => true,
515 '#collapsed' => true
516 );
517 $buttons = form_render($form['content'][$area][$id]['buttons']);
518 $area_content .= $buttons . '&nbsp;' . $function('list', $conf) .
519 theme('fieldset', $fieldset) /* . '<br />' */;
520 }
521 }
522 }
523 $content[$area] = theme('fieldset', array('#title' => check_plain($title), '#value' => $area_content));
524 }
525
526 $output .= panels_get_layout($layout, $content);
527
528 $output .= form_render($form);
529 return $output;
530}
531
532function panels_edit_form_validate($form_id, $form_values, $form) {
533 $content_types = panels_get_content_types();
534 foreach ($form_values['content'] as $area => $content) {
535 foreach ($content as $id => $item) {
536 if (is_numeric($id)) {
537 $function = $content_types[$item['type']]['admin'];
538 if (function_exists($function)) {
539 $function('validate', $item['configuration'], $form['content'][$area][$id]['configuration']);
540 }
541 }
542 }
543 }
544}
545
546function panels_edit_form_submit($form_id, $form_values) {
547 $panels = (object) $form_values;
548 // be sure we get the order right.
549 foreach ($form_values['content'] as $area => $content) {
550 $array = array();
551 $order = unserialize($content['order']);
552 if (is_array($order)) {
553 foreach($order as $id) {
554 $array[] = $content[$id];
555 }
556 }
557 $panels->content[$area] = $array;
558 }
559 panels_save_panels($panels);
560 drupal_set_message(t('The panels has been saved.'));
561 return 'admin/panels';
562}
563
564/**
565 * add the buttons to a content item
566 */
567function panels_add_buttons(&$form, $count, $position) {
568 $form['delete'] = panels_add_button('user-trash.png', 'delete', t('Delete this item'));
569 // Leaving these in but commented out as I'm not convinced we don't want them.
570// if ($count > 2) {
571// $form['top'] = panels_add_button('go-top.png', 'top', t('Move item to top'));
572// }
573 if ($count > 1) {
574 $form['up'] = panels_add_button('go-up.png', 'up', t('Move item up'));
575 $form['down'] = panels_add_button('go-down.png', 'down', t('Move item down'));
576 }
577// if ($count > 2) {
578// $form['bottom'] = panels_add_button('go-bottom.png', 'bottom', t('Move item to bottom'));
579// }
580// if ($count > 1) {
581// $form['spacer'] = panels_add_blank();
582// }
583 return $form;
584}
585
586/**
587 * move an item in an array to the top
588 */
589function panels_move_top(&$array, &$position) {
590 $value = $array[$position];
591 unset($array[$position]);
592 array_unshift($array, $value);
593 // reindex the array now
594 $array = array_values($array);
595}
596
597/**
598 * move an item in an array to the bottom
599 */
600function panels_move_bottom(&$array, &$position) {
601 $value = $array[$position];
602 unset($array[$position]);
603 $array[] = $value;
604 // reindex the array now
605 $array = array_values($array);
606}
607
608/**
609 * move an item in an array up one position
610 */
611function panels_move_up(&$array, &$position) {
612 $value = $array[$position];
613 $array[$position] = $array[$position - 1];
614 $array[$position - 1] = $value;
615}
616
617/**
618 * move an item in an array up one position
619 */
620function panels_move_down(&$array, &$position) {
621 $value = $array[$position];
622 $array[$position] = $array[$position + 1];
623 $array[$position + 1] = $value;
624}
625
626/**
627 * Remove an item from an array
628 */
629function panels_move_delete(&$array, &$position) {
630 unset($array[$position]);
631 // reindex the array now
632 $array = array_values($array);
633}
634
635// ---------------------------------------------------------------------------
636// panels database functions
637
638function panels_load_panels($did) {
639 $panels = db_fetch_object(db_query("SELECT * FROM {panels_info} WHERE did = %d", $did));
640 if (!$panels) {
641 return NULL;
642 }
643 $result = db_query("SELECT * FROM {panels_area} WHERE did = %d ORDER BY area, position", $did);
644 while ($area = db_fetch_object($result)) {
645 $area->configuration = unserialize($area->configuration);
646 $panels->content[$area->area][] = $area;
647 }
648 return $panels;
649}
650
651function panels_save_panels($panels) {
652 if ($panels->did) {
653 db_query("UPDATE {panels_info} SET title = '%s', access = '%s', path = '%s', css_id = '%s', layout = '%s' WHERE did = %d", $panels->title, $panels->access, $panels->path, $panels->css_id, $panels->layout, $panels->did);
654 db_query("DELETE FROM {panels_area} WHERE did = %d", $panels->did);
655 }
656 else {
657 $panels->did = db_next_id("{panels_info_id}");
658 db_query("INSERT INTO {panels_info} (did, title, access, path, css_id, layout) VALUES (%d, '%s', '%s', '%s', '%s', '%s')", $panels->did, $panels->title, $panels->access, $panels->path, $panels->css_id, $panels->layout);
659 }
660 foreach ($panels->content as $area => $info) {
661 foreach ($info as $position => $block) {
662 if (is_numeric($position)) { // don't save some random form stuff that may've been here.
663 $block = (object) $block;
664 db_query("INSERT INTO {panels_area} (did, area, type, configuration, position) VALUES(%d, '%s', '%s', '%s', %d)", $panels->did, $area, $block->type, serialize($block->configuration), $position);
665 }
666 }
667 }
668 cache_clear_all('menu:', TRUE);
669}
670
671function panels_delete_panels($panels) {
672 db_query("DELETE FROM {panels_info} WHERE did = %d", $panels->did);
673 db_query("DELETE FROM {panels_area} WHERE did = %d", $panels->did);
674 cache_clear_all('menu:', TRUE);
675}
676// ---------------------------------------------------------------------------
677// panels page
678
679function panels_panels_page($did) {
680 $panels = panels_load_panels($did);
681 if (!$panels) {
682 return drupal_not_found();
683 }
684
685 $layouts = panels_get_layouts();
686 $layout = $layouts[$panels->layout];
687
688 if (!$layout) {
689 watchdog('panels', t('Unable to find requested layout %s', array('%s' => check_plain($panels->layout))));
690 return drupal_not_found();
691 }
692
693 $content_types = panels_get_content_types();
694
695 foreach ($panels->content as $location => $list) {
696 foreach ($list as $area) {
697 $function = $content_types[$area->type]['callback'];
698 if (function_exists($function)) {
699 $content[$area->area] .= $function($area->configuration);
700 }
701 }
702 }
703 $output = panels_get_layout($layout, $content);
704 return $output;
705}
706
707function panels_get_layout($layout, $content) {
708 $output = theme($layout['theme'], check_plain($layout['css_id']), $content);
709
710 if ($output) {
711 if (file_exists(path_to_theme() . '/' . $layout['css'])) {
712 theme_add_style(path_to_theme() . '/' . $layout['css']);
713 }
714 else {
715 theme_add_style(drupal_get_path('module', $layout['module']) . '/' . $layout['css']);
716 }
717 }
718 return $output;
719}
720
721/**
722 * For external use: Given a layout ID and a $content array, return the
723 * finished layout.
724 */
725function panels_print_layout($id, $content) {
726 $layouts = panels_get_layouts();
727 $layout = $layouts[$id];
728 if (!$layout) {
729 return;
730 }
731
732 return panels_get_layout($layout, $content);
733}
734
735// ---------------------------------------------------------------------------
736// panels data loading
737
738function panels_load_includes($directory, $callback) {
739 // Load all our module 'on behalfs'.
740 $path = drupal_get_path('module', 'panels') . '/' . $directory;
741 $files = system_listing('.inc$', $path, 'name', 0);
742
743 foreach($files as $file) {
744 require_once($file->filename);
745 }
746 $output = module_invoke_all($callback);
747 foreach ($files as $file) {
748 $function = 'panels_' . $file->name . '_' . $callback;
749 if (function_exists($function)) {
750 $result = $function();
751 if (isset($result) && is_array($result)) {
752 $output = array_merge($output, $result);
753 }
754 }
755 }
756 return $output;
757}
758
759function panels_get_layouts() {
760 static $layout = NULL;
761 if (!$layout) {
762 $layouts = panels_load_includes('layouts', 'panels_layouts');
763 }
764 return $layouts;
765}
766
767function panels_get_content_types() {
768 static $layout = NULL;
769 if (!$layout) {
770 $layouts = panels_load_includes('content_types', 'panels_content_types');
771 }
772 return $layouts;
773}
774
775/**
776 * Helper function for autocompletion of node titles.
777 * This is mostly stolen from clipper.
778 */
779function panels_node_autocomplete($string) {
780 if ($string != '') { // if there are node_types passed, we'll use those in a MySQL IN query.
781 $result = db_query_range(db_rewrite_sql('SELECT n.title, u.name FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE LOWER(title) LIKE LOWER("%%%s%%")'), $string, 0, 10);
782 $prefix = count($array) ? implode(', ', $array) .', ' : '';
783
784 $matches = array();
785 while ($node = db_fetch_object($result)) {
786 $n = $node->title;
787 // Commas and quotes in terms are special cases, so encode 'em.
788 if (preg_match('/,/', $node->title) || preg_match('/"/', $node->title)) {
789 $n = '"'. preg_replace('/"/', '""', $node->title) .'"';
790 }
791 $matches[$prefix . $n] = '<span class="autocomplete_title">'. check_plain($node->title) .'</span> <span class="autocomplete_user">('. t('by %user', array('%user' => check_plain($node->name))) .')</span>';
792 }
793 print drupal_to_js($matches);
794 exit();
795 }
796}