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

Contents of /contributions/modules/panels/panels.module

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


Revision 1.28 - (show annotations) (download) (as text)
Fri Nov 21 00:05:49 2008 UTC (12 months, 1 week ago) by sdboyer
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--3
Changes since 1.27: +27 -6 lines
File MIME type: text/x-php
Merging DRUPAL-6--2 back into HEAD before branching to DRUPAL-6--3.
1 <?php
2 // $Id: panels.module,v 1.27.2.5 2008/11/17 20:46:01 merlinofchaos Exp $
3
4 /**
5 * @file panels.module
6 *
7 * Core functionality for the Panels engine.
8 */
9
10 /**
11 * Error code bitmask used for identifying argument behavior at runtime.
12 */
13 define('PANELS_ARG_IS_BAD', 1);
14 define('PANELS_ARG_USE_FALLBACK', 1 << 1);
15
16 /**
17 * Returns the API version of Panels. This didn't exist in 1.
18 *
19 * @return An array with the major and minor versions
20 */
21 function panels_api_version() {
22 return array(2, 0);
23 }
24
25 function panels_theme() {
26 $theme = array();
27 $theme['panels_layout_link'] = array(
28 'arguments' => array('title', 'id', 'image', 'link'),
29 );
30 $theme['panels_layout_icon'] = array(
31 'arguments' => array('id', 'image', 'title' => NULL),
32 );
33 $theme['panels_imagebutton'] = array(
34 'arguments' => array('element'),
35 );
36 $theme['panels_edit_display_form'] = array(
37 'arguments' => array('form'),
38 'file' => 'includes/display-edit.inc',
39 );
40 $theme['panels_edit_layout_form_choose'] = array(
41 'arguments' => array('form'),
42 'file' => 'includes/display-edit.inc',
43 );
44 $theme['panels_pane'] = array(
45 'arguments' => array('content', 'pane', 'display'),
46 'file' => 'includes/display-render.inc',
47 );
48 $theme['panels_common_content_list'] = array(
49 'arguments' => array('display'),
50 'file' => 'includes/common.inc',
51 );
52 $theme['panels_common_context_list'] = array(
53 'arguments' => array('object'),
54 'file' => 'includes/common.inc',
55 );
56 $theme['panels_common_context_item_form'] = array(
57 'arguments' => array('form'),
58 'file' => 'includes/common.inc',
59 );
60 $theme['panels_common_context_item_row'] = array(
61 'arguments' => array('type', 'form', 'position', 'count', 'with_tr' => TRUE),
62 'file' => 'includes/common.inc',
63 );
64 $theme['panels_dnd'] = array(
65 'arguments' => array('content'),
66 'file' => 'includes/display-edit.inc',
67 'function' => 'theme_panels_dnd',
68 );
69 $theme['panels_panel_dnd'] = array(
70 'arguments' => array('content', 'area', 'label', 'footer'),
71 'file' => 'includes/display-edit.inc',
72 'function' => 'theme_panels_panel_dnd',
73 );
74 $theme['panels_pane_dnd'] = array(
75 'arguments' => array('block', 'id', 'label', 'left_buttons' => NULL, 'buttons' => NULL),
76 'file' => 'includes/display-edit.inc',
77 );
78 $theme['panels_pane_collapsible'] = array(
79 'arguments' => array('block'),
80 'file' => 'includes/display-edit.inc',
81 );
82 $theme['profile_fields_pane'] = array(
83 'arguments' => array('category' => NULL, 'vars' => NULL),
84 'template' => 'content_types/profile_fields_pane',
85 );
86
87 // Register layout and style themes on behalf of all of these items.
88 panels_load_include('plugins');
89
90 // No need to worry about files; the plugin has to already be loaded for us
91 // to even know what the theme function is, so files will be auto included.
92 $layouts = panels_get_layouts();
93 foreach ($layouts as $name => $data) {
94 if (!empty($data['theme'])) {
95 $theme[$data['theme']] = array(
96 'arguments' => array('css_id' => NULL, 'content' => NULL, 'settings' => NULL),
97 'path' => $data['path'],
98 );
99
100 // if no theme function exists, assume template.
101 if (!function_exists("theme_$data[theme]")) {
102 $theme[$data['theme']]['template'] = str_replace('_', '-', $data['theme']);
103 }
104 }
105 }
106
107 $styles = panels_get_styles();
108 foreach ($styles as $name => $data) {
109 if (!empty($data['render pane'])) {
110 $theme[$data['render pane']] = array(
111 'arguments' => array('content' => NULL, 'pane' => NULL, 'display' => NULL),
112 );
113 }
114 if (!empty($data['render panel'])) {
115 $theme[$data['render panel']] = array(
116 'arguments' => array('display' => NULL, 'panel_id' => NULL, 'panes' => NULL, 'settings' => NULL),
117 );
118 }
119
120 }
121
122 return $theme;
123 }
124
125 /**
126 * Implementation of hook_menu
127 */
128 function panels_menu() {
129 $items = array();
130
131 // Provide some common options to reduce code repetition.
132 // By using array addition and making sure these are the rightmost
133 // value, they won't override anything already set.
134 $base = array(
135 'access arguments' => array('access content'),
136 'type' => MENU_CALLBACK,
137 'file' => 'includes/display-edit.inc',
138 );
139
140 $items['panels/ajax/add-pane'] = array(
141 'page callback' => 'panels_ajax_add_pane_choose',
142 ) + $base;
143 $items['panels/ajax/add-pane-config'] = array(
144 'page callback' => 'panels_ajax_add_pane_config',
145 ) + $base;
146 $items['panels/ajax/configure'] = array(
147 'page callback' => 'panels_ajax_configure_pane',
148 ) + $base;
149 $items['panels/ajax/show'] = array(
150 'page callback' => 'panels_ajax_toggle_shown',
151 'page arguments' => array('show'),
152 ) + $base;
153 $items['panels/ajax/hide'] = array(
154 'page callback' => 'panels_ajax_toggle_shown',
155 'page arguments' => array('hide'),
156 ) + $base;
157 $items['panels/ajax/cache-method'] = array(
158 'page callback' => 'panels_ajax_cache_method',
159 ) + $base;
160 $items['panels/ajax/cache-settings'] = array(
161 'page callback' => 'panels_ajax_cache_settings',
162 ) + $base;
163
164 // For panel settings on the edit layout settings page
165 $items['panels/ajax/style-settings'] = array(
166 'page callback' => 'panels_ajax_style_settings',
167 'file' => 'includes/display-layout-settings.inc',
168 ) + $base;
169
170 // Non-display editor callbacks
171 $items['panels/node/autocomplete'] = array(
172 'title' => 'Autocomplete node',
173 'page callback' => 'panels_node_autocomplete',
174 'file' => 'includes/callbacks.inc',
175 ) + $base;
176
177 // For context add/configure calls in common-context.inc
178 $items['panels/ajax/context-add'] = array(
179 'page callback' => 'panels_ajax_context_item_add',
180 'file' => 'includes/common-context.inc',
181 ) + $base;
182 $items['panels/ajax/context-configure'] = array(
183 'page callback' => 'panels_ajax_context_item_edit',
184 'file' => 'includes/common-context.inc',
185 ) + $base;
186 $items['panels/ajax/context-delete'] = array(
187 'page callback' => 'panels_ajax_context_item_delete',
188 'file' => 'includes/common-context.inc',
189 ) + $base;
190
191 // Provide a nice location for a panels admin panel.
192 $items['admin/panels'] = array(
193 'title' => 'Panels',
194 'access arguments' => array('access administration pages'),
195 'page callback' => 'panels_admin_page',
196 'file' => 'includes/callbacks.inc',
197 'description' => 'Administer items related to the Panels module.',
198 );
199
200 return $items;
201 }
202
203 /**
204 * Implementation of hook_init()
205 */
206 function panels_init() {
207 drupal_add_css(panels_get_path('css/panels.css'));
208 drupal_add_js(panels_get_path('js/panels.js'));
209 }
210
211 /**
212 * Load a panels include file.
213 */
214 function panels_load_include($include, $path = 'includes/') {
215 static $loaded = array();
216 if (empty($loaded["$path$include.inc"])) {
217 require_once './' . panels_get_path("$path$include.inc");
218 $loaded["$path$include.inc"] = TRUE;
219 }
220 }
221
222 /**
223 * panels path helper function
224 */
225 function panels_get_path($file, $base_path = FALSE, $module = 'panels') {
226 $output = $base_path ? base_path() : '';
227 return $output . drupal_get_path('module', $module) . '/' . $file;
228 }
229
230 /**
231 * Implementation of hook_perm
232 */
233 function panels_perm() {
234 return array(
235 'view all panes',
236 'view pane admin links',
237 'administer pane visibility',
238 'administer pane access',
239 'administer advanced pane settings',
240 'use panels caching features'
241 );
242 }
243
244 /**
245 * Get an object from cache.
246 */
247 function panels_cache_get($obj, $did, $skip_cache = FALSE) {
248 static $cache = array();
249 $key = "$obj:$did";
250 if ($skip_cache) {
251 unset($cache[$key]);
252 }
253
254 if (!array_key_exists($key, $cache)) {
255 $data = db_fetch_object(db_query("SELECT * FROM {panels_object_cache} WHERE sid = '%s' AND obj = '%s' AND did = %d", session_id(), $obj, $did));
256 if ($data) {
257 $cache[$key] = unserialize($data->data);
258 }
259 }
260 return isset($cache[$key]) ? $cache[$key] : NULL;
261 }
262
263 /**
264 * Save the edited object into the cache.
265 */
266 function panels_cache_set($obj, $did, $cache) {
267 panels_cache_clear($obj, $did);
268 db_query("INSERT INTO {panels_object_cache} (sid, obj, did, data, timestamp) VALUES ('%s', '%s', %d, '%s', %d)", session_id(), $obj, $did, serialize($cache), time());
269 }
270
271 /**
272 * Clear a object from the cache; used if the editing is aborted.
273 */
274 function panels_cache_clear($obj, $did) {
275 db_query("DELETE FROM {panels_object_cache} WHERE sid = '%s' AND obj = '%s' AND did = %d", session_id(), $obj, $did);
276 }
277
278 /**
279 * Implementation of hook_cron. Clean up old caches.
280 */
281 function panels_cron() {
282 // delete anything 7 days old or more.
283 db_query("DELETE FROM {panels_object_cache} WHERE timestamp < %d", time() - (86400 * 7));
284 }
285
286 // ---------------------------------------------------------------------------
287 // panels display editing
288
289 /**
290 * @defgroup mainapi Functions comprising the main panels API
291 * @{
292 */
293
294 /**
295 * Main API entry point to edit a panel display.
296 *
297 * Sample implementations utiltizing the the complex $destination behavior can be found
298 * in panels_page_edit_content() and, in a separate contrib module, OG Blueprints
299 * (http://drupal.org/project/og_blueprints), og_blueprints_blueprint_edit().
300 *
301 * @ingroup mainapi
302 *
303 * @param object $display instanceof panels_display \n
304 * A fully loaded panels $display object, as returned from panels_load_display().
305 * Merely passing a did is NOT sufficient. \n
306 * Note that 'fully loaded' means the $display must already be loaded with any contexts
307 * the caller wishes to have set for the display.
308 * @param mixed $destination \n
309 * The redirect destination that the user should be taken to on form submission or
310 * cancellation. With panels_edit, $destination has complex effects on the return
311 * values of panels_edit() once the form has been submitted. See the explanation of
312 * the return value below to understand the different types of values returned by panels_edit()
313 * at different stages of FAPI. Under most circumstances, simply passing in
314 * drupal_get_destination() is all that's necessary.
315 * @param array $content_types \n
316 * An associative array of allowed content types, typically as returned from
317 * panels_common_get_allowed_types(). Note that context partially governs available content types,
318 * so you will want to create any relevant contexts using panels_create_context() or
319 * panels_create_context_empty() to make sure all the appropriate content types are available.
320 *
321 * @return
322 * Because the functions called by panels_edit() invoke the form API, this function
323 * returns different values depending on the stage of form submission we're at. In Drupal 5,
324 * the phase of form submission is indicated by the contents of $_POST['op']. Here's what you'll
325 * get at different stages:
326 * -# If !$_POST['op']: then we're on on the initial passthrough and the form is being
327 * rendered, so it's the $form itself that's being returned. Because negative margins,
328 * a common CSS technique, bork the display editor's ajax drag-and-drop, it's important
329 * that the $output be printed, not returned. Use this syntax in the caller function: \n
330 * print theme('page', panels_edit($display, $destination, $content_types), FALSE); \n
331 * -# If $_POST['op'] == t('Cancel'): form submission has been cancelled. If empty($destination) == FALSE,
332 * then there is no return value and the panels API takes care of redirecting to $destination.
333 * If empty($destination) == TRUE, then there's still no return value, but the caller function
334 * has to take care of form redirection.
335 * -# If $_POST['op'] == ('Save'): the form has been submitted successfully and has run through
336 * panels_edit_display_submit(). $output depends on the value of $destination:
337 * - If empty($destination) == TRUE: $output contains the modified $display
338 * object, and no redirection will occur. This option is useful if the caller
339 * needs to perform additional operations on or with the modified $display before
340 * the page request is complete. Using hook_form_alter() to add an additional submit
341 * handler is typically the preferred method for something like this, but there
342 * are certain use cases where that is infeasible and $destination = NULL should
343 * be used instead. If this method is employed, the caller will need to handle form
344 * redirection. Note that having $_REQUEST['destination'] set, whether via
345 * drupal_get_destination() or some other method, will NOT interfere with this
346 * functionality; consequently, you can use drupal_get_destination() to safely store
347 * your desired redirect in the caller function, then simply use drupal_goto() once
348 * panels_edit() has done its business.
349 * - If empty($destination) == FALSE: the form will redirect to the URL string
350 * given in $destination and NO value will be returned.
351 */
352 function panels_edit($display, $destination = NULL, $content_types = NULL) {
353 panels_load_include('display-edit');
354 panels_load_include('ajax');
355 panels_load_include('plugins');
356 return _panels_edit($display, $destination, $content_types);
357 }
358
359 /**
360 * API entry point for selecting a layout for a given display.
361 *
362 * Layout selection is nothing more than a list of radio items encompassing the available
363 * layouts for this display, as defined by .inc files in the panels/layouts subdirectory.
364 * The only real complexity occurs when a user attempts to change the layout of a display
365 * that has some content in it.
366 *
367 * @param object $display instanceof panels_display \n
368 * A fully loaded panels $display object, as returned from panels_load_display().
369 * Merely passing a did is NOT sufficient.
370 * @param string $finish
371 * A string that will be used for the text of the form submission button. If no value is provided,
372 * then the form submission button will default to t('Save').
373 * @param mixed $destination
374 * Basic usage is a string containing the URL that the form should redirect to upon submission.
375 * For a discussion of advanced usages, see panels_edit().
376 * @param mixed $allowed_layouts
377 * Allowed layouts has three different behaviors that depend on which of three value types
378 * are passed in by the caller:
379 * #- if $allowed_layouts instanceof panels_allowed_layouts (includes subclasses): the most
380 * complex use of the API. The caller is passing in a loaded panels_allowed_layouts object
381 * that the client module previously created and stored somewhere using a custom storage
382 * mechanism.
383 * #- if is_string($allowed_layouts): the string will be used in a call to variable_get() which
384 * will call the $allowed_layouts . '_allowed_layouts' var. If the data was stored properly
385 * in the system var, the $allowed_layouts object will be unserialized and recreated.
386 * @see panels_common_set_allowed_layouts()
387 * #- if is_null($allowed_layouts): the default behavior, which also provides backwards
388 * compatibility for implementations of the Panels2 API written before beta4. In this case,
389 * a dummy panels_allowed_layouts object is created which does not restrict any layouts.
390 * Subsequent behavior is indistinguishable from pre-beta4 behavior.
391 *
392 * @return
393 * Can return nothing, or a modified $display object, or a redirection string; return values for the
394 * panels_edit* family of functions are quite complex. See panels_edit() for detailed discussion.
395 * @see panels_edit()
396 */
397 function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) {
398 panels_load_include('display-layout');
399 panels_load_include('plugins');
400 return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
401 }
402
403 /**
404 * API entry point for configuring the layout settings for a given display.
405 *
406 * For all layouts except Flexible, the layout settings form allows the user to select styles,
407 * as defined by .inc files in the panels/styles subdirectory, for the panels in their display.
408 * For the Flexible layout, the layout settings form allows the user to provide dimensions
409 * for their flexible layout in addition to applying styles to panels.
410 *
411 * @param object $display instanceof panels_display \n
412 * A fully loaded panels $display object, as returned from panels_load_display().
413 * Merely passing a did is NOT sufficient.
414 * @param string $finish
415 * A string that will be used for the text of (one of) the form submission button(s). Note that
416 * panels will NOT wrap $finish in t() for you, so your caller should make sure to do so. \n
417 * The submit behavior of the form is primarily governed by the value of $destination (see
418 * below), but is secondarily governed by $finish as follows:
419 * -# If $finish != t('Save'), then two #submit buttons will be present: one with the button
420 * text t('Save'), and the other with the button text $finish. .
421 * - Clicking the 'Save' button will save any changes on the form to the $display object and
422 * keep the user on the same editing page.
423 * - Clicking the $finish button will also save the $display object, but the user will be
424 * redirected to the URL specified in $destination.
425 * -# If $finish == t('Save'), then there is only one button, still called t('Save'), but it
426 * mimics the behavior of the $finish button above by redirecting the user away from the form.
427 * @param mixed $destination
428 * Basic usage is a string containing the URL that the form should redirect to upon submission.
429 * For a discussion of advanced usages that rely on NULL values for $destination, see the
430 * panels_edit() documentation.
431 * @param mixed $title
432 * The $title variable has three modes of operation:
433 * -# If $title == FALSE (the default), then no widget will appear on the panels_edit_layout_settings form
434 * allowing the user to select a title, and other means for setting page titles will take precedent. If
435 * no other means are used to provide a title, then the title will be hidden when rendering the $display.
436 * -# If $title == TRUE, then two widgets will appear on the panels_edit_layout_settings form allowing the
437 * user to input a title specific to this $display, as well as a checkbox enabling the user to disable
438 * page titles entirely for this $display object.
439 * -# If $title == (string), then the behavior is very similar to mode 2, but the widget description
440 * on the title textfield will indicate that the $title string will be used as the default page title
441 * if none is provided on this form. When utilizing this option, note that the panels API can only
442 * provide the data for these values; you must implement the appropriate conditionals to make it true.
443 *
444 * @return
445 * Can return nothing, or a modified $display object, or a redirection string; return values for the
446 * panels_edit* family of functions are quite complex. See panels_edit() for detailed discussion.
447 * @see panels_edit()
448 */
449 function panels_edit_layout_settings($display, $finish, $destination = NULL, $title = FALSE) {
450 panels_load_include('display-layout-settings');
451 panels_load_include('ajax');
452 panels_load_include('plugins');
453 return _panels_edit_layout_settings($display, $finish, $destination, $title);
454 }
455
456
457 // ---------------------------------------------------------------------------
458 // panels database functions
459
460 /**
461 * Forms the basis of a panel display
462 *
463 */
464 class panels_display {
465 var $args = array();
466 var $content = array();
467 var $panels = array();
468 var $incoming_content = NULL;
469 var $css_id = NULL;
470 var $context = array();
471 var $layout_settings = array();
472 var $panel_settings = array();
473 var $cache = array();
474 var $title = '';
475 var $hide_title = 0;
476 var $layout = '';
477
478 function add_pane($pane, $location = FALSE) {
479 $pane->pid = $this->next_new_pid();
480 if (!$location || !isset($this->panels[$location])) {
481 foreach ($this->panels as $panel_name => $panel) {
482 if (array_key_exists($pane->pid, $panel)) {
483 $this->panels[$panel_name][] = $pane->pid;
484 }
485 }
486 }
487 else {
488 $this->panels[$location][] = $pane->pid;
489 }
490 }
491
492 function duplicate_pane($pid, $location = FALSE) {
493 $pane = $this->clone_pane($pid);
494 $this->add_pane($pane, $location);
495 }
496
497 function clone_pane($pid) {
498 $pane = drupal_clone($this->content[$pid]);
499 foreach (array_keys($this->content) as $pidcheck) {
500 // necessary?
501 unset($pane->position);
502 }
503 return $pane;
504 }
505
506 function next_new_pid() {
507 // necessary if/until we use this method and ONLY this method for adding temporary pids.
508 // then we can do it with a nice static var.
509 $id = array(0);
510 foreach (array_keys($this->content) as $pid) {
511 if (!is_numeric($pid)) {
512 $id[] = substr($pid, 4);
513 }
514 }
515 $next_id = end($id);
516 return ++$next_id;
517 }
518 }
519
520 /**
521 * }@ End of 'defgroup mainapi', although other functions are specifically added later
522 */
523
524 function panels_export_pane_across_displays($source_display, &$target_display, $pid, $location = FALSE) {
525 $pane = $source_display->clone_pane($pid);
526 $target_display->add_pane($pane, $location);
527 }
528
529 /**
530 * Clean up a display object and add some required information, if missing.
531 *
532 * Currently a display object needs 'args', 'incoming content', 'context'
533 * and a 'css_id'.
534 *
535 * @param &$display
536 * The display object to be sanitized.
537 * @return
538 * The sanitized display object.
539 */
540 function panels_sanitize_display(&$display) {
541 if (!isset($display->args)) {
542 $display->args = array();
543 }
544
545 if (!isset($display->incoming_content)) {
546 $display->incoming_content = NULL;
547 }
548
549 if (!isset($display->context)) {
550 $display->context = array();
551 }
552
553 if (!isset($display->css_id)) {
554 $display->css_id = NULL;
555 }
556 }
557
558 /**
559 * Creates a new display, setting the ID to our magic new id.
560 */
561 function panels_new_display() {
562 $display = new panels_display();
563 $display->did = 'new';
564 return $display;
565 }
566
567 function panels_new_pane($type, $subtype) {
568 $pane = new stdClass();
569 $pane->pid = 'new';
570 $pane->type = $type;
571 $pane->subtype = $subtype;
572 $pane->configuration = array();
573 $pane->access = array();
574 $pane->shown = TRUE;
575 $pane->visibility = '';
576 return $pane;
577 }
578
579 /**
580 * Load and fill the requested $display object(s).
581 *
582 * Helper function primarily for for panels_load_display().
583 *
584 * @param array $dids
585 * An indexed array of dids to be loaded from the database.
586 *
587 * @return $displays
588 * An array of displays, keyed by their display dids.
589 */
590 function panels_load_displays($dids) {
591 $displays = array();
592 if (empty($dids) || !is_array($dids)) {
593 return $displays;
594 }
595
596 $result = db_query("SELECT * FROM {panels_display} WHERE did IN (". db_placeholders($dids) .")", $dids);
597
598 while ($obj = db_fetch_array($result)) {
599 $display = new panels_display();
600
601 foreach ($obj as $key => $value) {
602 $display->$key = $value;
603 // unserialize important bits
604 if (in_array($key, array('layout_settings', 'panel_settings', 'cache'))) {
605 $display->$key = empty($display->$key) ? array() : unserialize($display->$key);
606 }
607 }
608
609 $display->panels = $display->content = array();
610
611 $displays[$display->did] = $display;
612 }
613
614 foreach (module_implements('panels_layout_content_alter') as $module) {
615 $function = $module . '_panels_layout_content_alter';
616 $function($content, $layout, $settings);
617 }
618
619 $result = db_query("SELECT * FROM {panels_pane} WHERE did IN (". db_placeholders($dids) .") ORDER BY did, panel, position", $dids);
620
621 while ($pane = db_fetch_object($result)) {
622 $pane->configuration = unserialize($pane->configuration);
623 $pane->cache = empty($pane->cache) ? array() : unserialize($pane->cache);
624 $pane->access = ($pane->access ? explode(', ', $pane->access) : array());
625 // Old panels may not have shown property, so enable by default when loading.
626 $pane->shown = isset($pane->shown) ? $pane->shown : TRUE;
627
628 $displays[$pane->did]->panels[$pane->panel][] = $pane->pid;
629 $displays[$pane->did]->content[$pane->pid] = $pane;
630 }
631 return $displays;
632 }
633
634 /**
635 * Load a single display.
636 *
637 * @ingroup mainapi
638 *
639 * @param int $did
640 * The display id (did) of the display to be loaded.
641 *
642 * @return object $display instanceof panels_display \n
643 * Returns a partially-loaded panels_display object. $display objects returned from
644 * from this function have only the following data:
645 * - $display->did (the display id)
646 * - $display->name (the 'name' of the display, where applicable - it often isn't)
647 * - $display->layout (a string with the system name of the display's layout)
648 * - $display->panel_settings (custom layout style settings contained in an associative array; NULL if none)
649 * - $display->layout_settings (panel size and configuration settings for Flexible layouts; NULL if none)
650 * - $display->css_id (the special css_id that has been assigned to this display, if any; NULL if none)
651 * - $display->content (an array of pane objects, keyed by pane id (pid))
652 * - $display->panels (an associative array of panel regions, each an indexed array of pids in the order they appear in that region)
653 * - $display->cache (any relevant data from panels_simple_cache)
654 * - $display->args
655 * - $display->incoming_content
656 *
657 * While all of these members are defined, $display->context is NEVER defined in the returned $display;
658 * it must be set using one of the panels_context_create() functions.
659 */
660 function panels_load_display($did) {
661 $displays = panels_load_displays(array($did));
662 if (!empty($displays)) {
663 return array_shift($displays);
664 }
665 }
666
667 /**
668 * Save a display object.
669 *
670 * @ingroup mainapi
671 *
672 * Note a new $display only receives a real did once it is run through this function.
673 * Until then, it uses a string placeholder, 'new', in place of a real did. The same
674 * applies to all new panes (whether on a new $display or not); in addition,
675 * panes have sequential numbers appended, of the form 'new-1', 'new-2', etc.
676 *
677 * @param object $display instanceof panels_display \n
678 * The display object to be saved. Passed by reference so the caller need not use
679 * the return value for any reason except convenience.
680 *
681 * @return object $display instanceof panels_display \n
682 */
683 function panels_save_display(&$display) {
684 // @todo -- update all this to just use drupal_write_record or something like it.
685 if (!empty($display->did) && $display->did != 'new') {
686 db_query("UPDATE {panels_display} SET layout = '%s', layout_settings = '%s', panel_settings = '%s', cache = '%s', title = '%s', hide_title = %d WHERE did = %d", $display->layout, serialize($display->layout_settings), serialize($display->panel_settings), serialize($display->cache), $display->title, $display->hide_title, $display->did);
687 // Get a list of all panes currently in the database for this display so we can know if there
688 // are panes that need to be deleted. (i.e, aren't currently in our list of panes).
689 $result = db_query("SELECT pid FROM {panels_pane} WHERE did = %d", $display->did);
690 while ($pane = db_fetch_object($result)) {
691 $pids[$pane->pid] = $pane->pid;
692 }
693 }
694 else {
695 db_query("INSERT INTO {panels_display} (layout, layout_settings, panel_settings, cache, title, hide_title) VALUES ('%s', '%s', '%s', '%s', '%s', %d)", $display->layout, serialize($display->layout_settings), serialize($display->panel_settings), serialize($display->cache), $display->title, $display->hide_title);
696 $display->did = db_last_insert_id('panels_display', 'did');
697 $pids = array();
698 }
699
700 // update all the panes
701 panels_load_include('plugins');
702
703 foreach ((array) $display->panels as $id => $panes) {
704 $position = 0;
705 $new_panes = array();
706 foreach ((array) $panes as $pid) {
707 $pane = $display->content[$pid];
708 $pane->position = $position++;
709
710 // make variables right.
711 $type = panels_get_content_type($pane->type);
712 $access = isset($pane->access) ? implode(', ', $pane->access) : '';
713 $visibility = !empty($type['visibility serialize']) ? serialize($pane->visibility) : $pane->visibility;
714 $pane->shown = isset($pane->shown) ? $pane->shown : TRUE;
715
716 if (empty($pane->cache)) {
717 $pane->cache = array();
718 }
719
720 $v = array($display->did, $pane->panel, $pane->type, $pane->subtype, serialize($pane->configuration), serialize($pane->cache), $pane->shown, $access, $visibility, $pane->position);
721
722 if (!is_numeric($pid)) {
723 unset($display->content[$pid]);
724 // doin it this way for readability
725 $f = 'did, panel, type, subtype, configuration, cache, shown, access, visibility, position';
726 $q = "%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d";
727
728 db_query("INSERT INTO {panels_pane} ($f) VALUES ($q)", $v);
729 $pane->pid = db_last_insert_id('panels_pane', 'pid');
730 }
731 else {
732 $v[] = $pane->pid;
733 $f = "did = %d, panel = '%s', type = '%s', subtype = '%s', configuration = '%s', cache = '%s', shown = '%s', access = '%s', visibility = '%s', position = '%d'";
734 db_query("UPDATE {panels_pane} SET $f WHERE pid = %d", $v);
735 }
736 // and put it back so our pids and positions can be used
737 $display->content[$pane->pid] = $pane;
738 $new_panes[] = $pane->pid;
739 if (isset($pids[$pane->pid])) {
740 unset($pids[$pane->pid]);
741 }
742 }
743
744 $display->panels[$id] = $new_panes;
745 }
746 if (!empty($pids)) {
747 db_query("DELETE FROM {panels_pane} WHERE pid IN (" . db_placeholders($pids) . ")", $pids);
748 }
749
750 // Clear any cached content for this display.
751 panels_clear_cached_content($display);
752
753 // to be nice, even tho we have a reference.
754 return $display;
755 }
756
757 /**
758 * Delete a display.
759 */
760 function panels_delete_display($display) {
761 if (is_object($display)) {
762 $did = $display->did;
763 }
764 else {
765 $did = $display;
766 }
767 db_query("DELETE FROM {panels_display} WHERE did = %d", $did);
768 db_query("DELETE FROM {panels_pane} WHERE did = %d", $did);
769 }
770
771 /**
772 * Exports the provided display into portable code.
773 *
774 * This function is primarily intended as a mechanism for cloning displays.
775 * It generates an exact replica (in code) of the provided $display, with
776 * the exception that it replaces all ids (dids and pids) with 'new-*' values.
777 * Only once panels_save_display() is called on the code version of $display will
778 * the exported display written to the database and permanently saved.
779 *
780 * @see panels_page_export() or _panels_page_fetch_display() for sample implementations.
781 *
782 * @ingroup mainapi
783 *
784 * @param object $display instanceof panels_display \n
785 * This export function does no loading of additional data about the provided
786 * display. Consequently, the caller should make sure that all the desired data
787 * has been loaded into the $display before calling this function.
788 * @param string $prefix
789 * A string prefix that is prepended to each line of exported code. This is primarily
790 * used for prepending a double space when exporting so that the code indents and lines up nicely.
791 *
792 * @return string $output
793 * The passed-in $display expressed as code, ready to be imported. Import by running
794 * eval($output) in the caller function; doing so will create a new $display variable
795 * with all the exported values. Note that if you have already defined a $display variable in
796 * the same scope as where you eval(), your existing $display variable WILL be overwritten.
797 */
798 function panels_export_display($display, $prefix = '') {
799 $output = '';
800 $output .= $prefix . '$display = new panels_display()' . ";\n";
801 $output .= $prefix . '$display->did = \'new\'' . ";\n";
802 // $fields = array('name', 'layout', 'layout_settings', 'panel_settings');
803 // TODO 'name' field was removed several months ago, so temporarily removing
804 // it from here whil investigations into exactly why it was removed continue
805 $fields = array('layout', 'layout_settings', 'panel_settings');
806 foreach ($fields as $field) {
807 $output .= $prefix . '$display->' . $field . ' = ' . panels_var_export($display->$field, $prefix) . ";\n";
808 }
809
810 $output .= $prefix . '$display->content = array()' . ";\n";
811 $output .= $prefix . '$display->panels = array()' . ";\n";
812 $panels = array();
813
814 if (!empty($display->content)) {
815 $pid_counter = 0;
816 $region_counters = array();
817 foreach ($display->content as $pane) {
818 $pane->pid = 'new-' . ++$pid_counter;
819 $output .= panels_export_pane($pane, $prefix . ' ');
820 $output .= "$prefix " . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
821 if (!isset($region_counters[$pane->panel])) {
822 $region_counters[$pane->panel] = 0;
823 }
824 $output .= "$prefix " . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n";
825 }
826 }
827 return $output;
828 }
829
830 function panels_export_pane($pane, $prefix = '') {
831 $output = '';
832 $output = $prefix . '$pane = new stdClass()' . ";\n";
833 $fields = array('pid', 'panel', 'type', 'shown', 'subtype', 'access', 'configuration', 'cache');
834 foreach ($fields as $field) {
835 $output .= "$prefix " . '$pane->' . $field . ' = ' . panels_var_export($pane->$field, "$prefix ") . ";\n";
836 }
837 return $output;
838 }
839
840 function panels_var_export($object, $prefix = '') {
841 if (is_array($object) && empty($object)) {
842 $output = 'array()';
843 }
844 else {
845 // Remove extra space to match Drupal coding standards.
846 $output = str_replace('array (', 'array(', var_export($object, TRUE));
847 }
848
849 if ($prefix) {
850 $output = str_replace("\n", "\n$prefix", $output);
851 }
852 return $output;
853 }
854
855 /**
856 * Render a display by loading the content into an appropriate
857 * array and then passing through to panels_render_layout.
858 *
859 * if $incoming_content is NULL, default content will be applied. Use
860 * an empty string to indicate no content.
861 * @render
862 * @ingroup hook_invocations
863 */
864 function panels_render_display(&$display) {
865 panels_load_include('display-render');
866 panels_load_include('plugins');
867 return _panels_render_display($display);
868 }
869
870 /**
871 * For external use: Given a layout ID and a $content array, return the
872 * panel display. The content array is filled in based upon the content
873 * available in the layout. If it's a two column with a content
874 * array defined like array('left' => t('Left side'), 'right' =>
875 * t('Right side')), then the $content array should be array('left' =>
876 * $output_left, 'right' => $output_right)
877 * @render
878 */
879 function panels_print_layout($id, $content) {
880 panels_load_include('plugins');
881 return _panels_print_layout($id, $content);
882 }
883
884 // @layout
885 function panels_print_layout_icon($id, $layout, $title = NULL) {
886 drupal_add_css(panels_get_path('css/panels_admin.css'));
887 $file = $layout['path'] . '/' . $layout['icon'];
888 return theme('panels_layout_icon', $id, theme('image', $file), $title);
889 }
890
891 /**
892 * Theme the layout icon image
893 * @layout
894 * @todo move to theme.inc
895 */
896 function theme_panels_layout_icon($id, $image, $title = NULL) {
897 $output = '<div class="layout-icon">';
898 $output .= $image;
899 if ($title) {
900 $output .= '<div class="caption">' . $title . '</div>';
901 }
902 $output .= '</div>';
903 return $output;
904 }
905
906 /**
907 * Theme the layout link image
908 * @layout
909 */
910 function theme_panels_layout_link($title, $id, $image, $link) {
911 $output = '<div class="layout-link">';
912 $output .= $image;
913 $output .= '<div>' . $title . '</div>';
914 $output .= '</div>';
915 return $output;
916 }
917
918 /**
919 * Print the layout link. Sends out to a theme function.
920 * @layout
921 */
922 function panels_print_layout_link($id, $layout, $link) {
923 drupal_add_css(panels_get_path('css/panels_admin.css'));
924 $file = $layout['path'] . '/' . $layout['icon'];
925 $image = l(theme('image', $file), $link, array('html' => true));
926 $title = l($layout['title'], $link);
927 return theme('panels_layout_link', $title, $id, $image, $link);
928 }
929
930 /**
931 * Implementation of hook_ctools_plugin_directory() to let the system know
932 * we implement task and task_handler plugins.
933 */
934 function panels_ctools_plugin_directory($plugin) {
935 if ($plugin == 'tasks' || $plugin == 'task_handlers') {
936 return 'plugins/' . $plugin;
937 }
938 }

  ViewVC Help
Powered by ViewVC 1.1.2