| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: panels.module,v 1.28.2.105 2010/08/30 22:08:57 merlinofchaos Exp $ 3 4 /** 5 * @file panels.module 6 * 7 * Core functionality for the Panels engine. 8 */ 9 10 define('PANELS_REQUIRED_CTOOLS_API', '1.8'); 11 12 define('PANELS_TITLE_FIXED', 0); // Hide title use to be true/false. So false remains old behavior. 13 define('PANELS_TITLE_NONE', 1); // And true meant no title. 14 define('PANELS_TITLE_PANE', 2); // And this is the new behavior, where the title field will pick from a pane. 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(3, 1); 23 } 24 25 // -------------------------------------------------------------------------- 26 // Core Drupal hook implementations 27 28 /** 29 * Implementation of hook_theme() 30 */ 31 function panels_theme() { 32 // Safety: go away if CTools is not at an appropriate version. 33 if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) { 34 return array(); 35 } 36 37 $theme = array(); 38 $theme['panels_layout_link'] = array( 39 'arguments' => array('title', 'id', 'image', 'link'), 40 ); 41 $theme['panels_layout_icon'] = array( 42 'arguments' => array('id', 'image', 'title' => NULL), 43 ); 44 $theme['panels_pane'] = array( 45 'arguments' => array('output' => array(), 'pane' => array(), 'display' => array()), 46 'path' => drupal_get_path('module', 'panels') . '/templates', 47 'template' => 'panels-pane', 48 ); 49 $theme['panels_common_content_list'] = array( 50 'arguments' => array('display'), 51 'file' => 'includes/common.inc', 52 ); 53 $theme['panels_render_display_form'] = array( 54 'arguments' => array('form' => NULL), 55 ); 56 57 $theme['panels_dashboard'] = array( 58 'arguments' => array(), 59 'path' => drupal_get_path('module', 'panels') . '/templates', 60 'file' => '../includes/callbacks.inc', 61 'template' => 'panels-dashboard', 62 ); 63 64 $theme['panels_dashboard_link'] = array( 65 'arguments' => array('link' => array()), 66 'path' => drupal_get_path('module', 'panels') . '/templates', 67 'file' => '../includes/callbacks.inc', 68 'template' => 'panels-dashboard-link', 69 ); 70 71 $theme['panels_dashboard_block'] = array( 72 'arguments' => array('block' => array()), 73 'path' => drupal_get_path('module', 'panels') . '/templates', 74 'file' => '../includes/callbacks.inc', 75 'template' => 'panels-dashboard-block', 76 ); 77 78 // We don't need layout and style themes in maintenance mode. 79 if (defined('MAINTENANCE_MODE')) { 80 return $theme; 81 } 82 83 // Register layout and style themes on behalf of all of these items. 84 ctools_include('plugins', 'panels'); 85 86 // No need to worry about files; the plugin has to already be loaded for us 87 // to even know what the theme function is, so files will be auto included. 88 $layouts = panels_get_layouts(); 89 foreach ($layouts as $name => $data) { 90 foreach (array('theme', 'admin theme') as $callback) { 91 if (!empty($data[$callback])) { 92 $theme[$data[$callback]] = array( 93 'arguments' => array('css_id' => NULL, 'content' => NULL, 'settings' => NULL, 'display' => NULL, 'layout' => NULL, 'renderer' => NULL), 94 'path' => $data['path'], 95 'file' => $data['file'], 96 ); 97 98 // if no theme function exists, assume template. 99 if (!function_exists("theme_$data[theme]")) { 100 $theme[$data[$callback]]['template'] = str_replace('_', '-', $data[$callback]); 101 $theme[$data[$callback]]['file'] = $data['file']; // for preprocess. 102 } 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('output' => NULL, 'pane' => NULL, 'display' => NULL, 'style' => NULL), 112 'path' => $data['path'], 113 'file' => $data['file'], 114 ); 115 } 116 // If we're in legacy mode, include the old callback key for legacy styles. 117 if (variable_get('panels_legacy_rendering_mode', TRUE)) { 118 if (!empty($data['render panel'])) { 119 $theme[$data['render panel']] = array( 120 'arguments' => array('display' => NULL, 'panel_id' => NULL, 'panes' => NULL, 'settings' => NULL, 'style' => NULL), 121 'path' => $data['path'], 122 'file' => $data['file'], 123 ); 124 } 125 } 126 if (!empty($data['render region'])) { 127 $theme[$data['render region']] = array( 128 'arguments' => array('display' => NULL, 'region_id' => NULL, 'panes' => NULL, 'settings' => NULL, 'style' => NULL), 129 'path' => $data['path'], 130 'file' => $data['file'], 131 ); 132 } 133 134 if (!empty($data['hook theme'])) { 135 if (is_array($data['hook theme'])) { 136 $theme += $data['hook theme']; 137 } 138 else if (function_exists($data['hook theme'])) { 139 $data['hook theme']($theme, $data); 140 } 141 } 142 } 143 144 return $theme; 145 } 146 147 /** 148 * Implementation of hook_menu 149 */ 150 function panels_menu() { 151 // Safety: go away if CTools is not at an appropriate version. 152 if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) { 153 return array(); 154 } 155 $items = array(); 156 157 // Base AJAX router callback. 158 $items['panels/ajax'] = array( 159 'access arguments' => array('access content'), 160 'page callback' => 'panels_ajax_router', 161 'type' => MENU_CALLBACK, 162 ); 163 164 $admin_base = array( 165 'file' => 'includes/callbacks.inc', 166 'access arguments' => array('use panels dashboard'), 167 ); 168 // Provide a nice location for a panels admin panel. 169 $items['admin/build/panels'] = array( 170 'title' => 'Panels', 171 'page callback' => 'panels_admin_page', 172 'description' => 'Get a bird\'s eye view of items related to Panels.', 173 ) + $admin_base; 174 175 $items['admin/build/panels/dashboard'] = array( 176 'title' => 'Dashboard', 177 'page callback' => 'panels_admin_page', 178 'type' => MENU_DEFAULT_LOCAL_TASK, 179 'weight' => -10, 180 ) + $admin_base; 181 182 $items['admin/build/panels/settings'] = array( 183 'title' => 'Settings', 184 'page callback' => 'drupal_get_form', 185 'page arguments' => array('panels_admin_settings_page'), 186 'type' => MENU_LOCAL_TASK, 187 ) + $admin_base; 188 189 $items['admin/build/panels/settings/general'] = array( 190 'title' => 'General', 191 'page callback' => 'drupal_get_form', 192 'page arguments' => array('panels_admin_settings_page'), 193 'access arguments' => array('administer page manager'), 194 'type' => MENU_DEFAULT_LOCAL_TASK, 195 'weight' => -10, 196 ) + $admin_base; 197 198 if (module_exists('page_manager')) { 199 $items['admin/build/panels/settings/panel-page'] = array( 200 'title' => 'Panel pages', 201 'page callback' => 'panels_admin_panel_context_page', 202 'type' => MENU_LOCAL_TASK, 203 'weight' => -10, 204 ) + $admin_base; 205 } 206 207 ctools_include('plugins', 'panels'); 208 $layouts = panels_get_layouts(); 209 foreach ($layouts as $name => $data) { 210 if (!empty($data['hook menu'])) { 211 if (is_array($data['hook menu'])) { 212 $items += $data['hook menu']; 213 } 214 else if (function_exists($data['hook menu'])) { 215 $data['hook menu']($items, $data); 216 } 217 } 218 } 219 220 221 return $items; 222 } 223 224 /** 225 * Menu loader function to load a cache item for Panels AJAX. 226 * 227 * This load all of the includes needed to perform AJAX, and loads the 228 * cache object and makes sure it is valid. 229 */ 230 function panels_edit_cache_load($cache_key) { 231 ctools_include('display-edit', 'panels'); 232 ctools_include('plugins', 'panels'); 233 ctools_include('ajax'); 234 ctools_include('modal'); 235 ctools_include('context'); 236 237 return panels_edit_cache_get($cache_key); 238 } 239 240 /** 241 * Implementation of hook_init() 242 */ 243 function panels_init() { 244 // Safety: go away if CTools is not at an appropriate version. 245 if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) { 246 if (user_access('administer site configuration')) { 247 drupal_set_message(t('Panels is enabled but CTools is out of date. All Panels modules are disabled until CTools is updated. See the status page for more information.'), 'error'); 248 } 249 return; 250 } 251 252 ctools_add_css('panels', 'panels'); 253 ctools_add_js('panels', 'panels'); 254 } 255 256 /** 257 * Implementation of hook_perm 258 */ 259 function panels_perm() { 260 return array( 261 'view all panes', 262 'view pane admin links', 263 'administer pane visibility', 264 'administer pane access', 265 'administer advanced pane settings', 266 'administer panels layouts', 267 'use panels caching features', 268 'use panels dashboard', 269 'use panels in place editing', 270 ); 271 } 272 273 /** 274 * Implementation of hook_flush_caches(). 275 * 276 * We implement this so that we can be sure our legacy rendering state setting 277 * in $conf is updated whenever caches are cleared. 278 */ 279 function panels_flush_caches() { 280 $legacy = panels_get_legacy_state(); 281 $legacy->determineStatus(); 282 } 283 284 // --------------------------------------------------------------------------- 285 // CTools hook implementations 286 // 287 // These aren't core Drupal hooks but they are just as important. 288 289 /** 290 * Implementation of hook_ctools_plugin_directory() to let the system know 291 * we implement task and task_handler plugins. 292 */ 293 function panels_ctools_plugin_directory($module, $plugin) { 294 // Safety: go away if CTools is not at an appropriate version. 295 if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) { 296 return; 297 } 298 if ($module == 'page_manager' || $module == 'panels' || $module == 'ctools') { 299 return 'plugins/' . $plugin; 300 } 301 } 302 303 /** 304 * Inform CTools that the layout plugin can be loaded from themes. 305 */ 306 function panels_ctools_plugin_layouts() { 307 return array( 308 'load themes' => TRUE, 309 'use hooks' => TRUE, 310 'process' => 'panels_layout_process', 311 'child plugins' => TRUE, 312 ); 313 } 314 315 /** 316 * Ensure a layout has a minimal set of data. 317 */ 318 function panels_layout_process(&$plugin) { 319 $plugin += array( 320 'category' => t('Miscellaneous'), 321 'description' => '', 322 ); 323 } 324 325 /** 326 * Inform CTools that the style plugin can be loaded from themes. 327 */ 328 function panels_ctools_plugin_styles() { 329 return array( 330 'load themes' => TRUE, 331 'use hooks' => TRUE, 332 'process' => 'panels_plugin_styles_process', 333 'child plugins' => TRUE, 334 ); 335 } 336 337 /** 338 * Implementation of hook_ctools_plugin_api(). 339 * 340 * Inform CTools about version information for various plugins implemented by 341 * Panels. 342 * 343 * @param string $owner 344 * The system name of the module owning the API about which information is 345 * being requested. 346 * @param string $api 347 * The name of the API about which information is being requested. 348 */ 349 function panels_ctools_plugin_api($owner, $api) { 350 if ($owner == 'panels' && $api == 'styles') { 351 // As of 6.x-3.6, Panels has a slightly new system for style plugins. 352 return array('version' => 2.0); 353 } 354 355 if ($owner == 'panels' && $api == 'pipelines') { 356 return array( 357 'version' => 1, 358 'path' => drupal_get_path('module', 'panels') . '/includes', 359 ); 360 } 361 } 362 363 /** 364 * Implementation of hook_views_api(). 365 */ 366 function panels_views_api() { 367 return array( 368 'api' => 2, 369 'path' => drupal_get_path('module', 'panels') . '/plugins/views', 370 ); 371 } 372 373 /** 374 * Perform additional processing on a style plugin. 375 * 376 * Currently this is only being used to apply versioning information to style 377 * plugins in order to ensure the legacy renderer passes the right type of 378 * parameters to a style plugin in a hybrid environment of both new and old 379 * plugins. 380 * 381 * @see _ctools_process_data() 382 * 383 * @param array $plugin 384 * The style plugin that is being processed. 385 * @param array $info 386 * The style plugin type info array. 387 */ 388 function panels_plugin_styles_process(&$plugin, $info) { 389 $plugin += array( 390 'weight' => 0, 391 ); 392 393 $compliant_modules = ctools_plugin_api_info('panels', 'styles', 2.0, 2.0); 394 $plugin['version'] = empty($compliant_modules[$plugin['module']]) ? 1.0 : $compliant_modules[$plugin['module']]['version']; 395 } 396 397 /** 398 * Declare what style types Panels uses. 399 */ 400 function panels_ctools_style_base_types() { 401 return array( 402 'region' => array( 403 'title' => t('Panel region'), 404 'preview' => 'panels_stylizer_region_preview', 405 'theme arguments' => array('settings' => NULL, 'classes' => NULL, 'content' => NULL), 406 ), 407 'pane' => array( 408 'title' => t('Panel pane'), 409 'preview' => 'panels_stylizer_pane_preview', 410 'theme arguments' => array('settings' => NULL, 'content' => NULL, 'pane' => NULL, 'display' => NULL), 411 ), 412 ); 413 } 414 415 function panels_stylizer_lipsum() { 416 return " 417 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at velit dolor. Donec egestas tellus sit amet urna rhoncus adipiscing. Proin nec porttitor sem. Maecenas aliquam, purus nec tempus dignissim, nulla arcu aliquam diam, non tincidunt massa ante vel dolor. Aliquam sapien sapien, tincidunt id tristique at, pretium sagittis libero.</p> 418 419 <p>Nulla facilisi. Curabitur lacinia, tellus sed tristique consequat, diam lorem scelerisque felis, at dictum purus augue facilisis lorem. Duis pharetra dignissim rutrum. Curabitur ac elit id dui dapibus tincidunt. Nulla eget sem quam, non eleifend eros. Cras porttitor tempus lectus ac scelerisque. Curabitur vehicula bibendum lorem, vitae ornare ligula venenatis ut.</p> 420 "; 421 } 422 423 /** 424 * Generate a preview given the current settings. 425 */ 426 function panels_stylizer_region_preview($plugin, $settings) { 427 ctools_stylizer_add_css($plugin, $settings); 428 return theme($plugin['theme'], $settings, ctools_stylizer_get_css_class($plugin, $settings), panels_stylizer_lipsum()); 429 } 430 431 /** 432 * Generate a preview given the current settings. 433 */ 434 function panels_stylizer_pane_preview($plugin, $settings) { 435 ctools_stylizer_add_css($plugin, $settings); 436 $pane = new stdClass(); 437 438 $content = new stdClass; 439 $content->title = t('Lorem ipsum'); 440 $content->content = panels_stylizer_lipsum(); 441 $content->type = 'dummy'; 442 $content->subtype = 'dummy'; 443 444 $content->css_class = ctools_stylizer_get_css_class($plugin, $settings); 445 446 $display = new panels_display(); 447 448 if (!empty($plugin['theme'])) { 449 return theme($plugin['theme'], $settings, $content, $pane, $display); 450 } 451 else { 452 return theme('panels_pane', $content, $pane, $display); 453 } 454 } 455 456 // --------------------------------------------------------------------------- 457 // Panels display editing 458 459 /** 460 * @defgroup mainapi Functions comprising the main panels API 461 * @{ 462 */ 463 464 /** 465 * Main API entry point to edit a panel display. 466 * 467 * Sample implementations utiltizing the the complex $destination behavior can be found 468 * in panels_page_edit_content() and, in a separate contrib module, OG Blueprints 469 * (http://drupal.org/project/og_blueprints), og_blueprints_blueprint_edit(). 470 * 471 * @ingroup mainapi 472 * 473 * @param object $display instanceof panels_display \n 474 * A fully loaded panels $display object, as returned from panels_load_display(). 475 * Merely passing a did is NOT sufficient. \n 476 * Note that 'fully loaded' means the $display must already be loaded with any contexts 477 * the caller wishes to have set for the display. 478 * @param mixed $destination \n 479 * The redirect destination that the user should be taken to on form submission or 480 * cancellation. With panels_edit, $destination has complex effects on the return 481 * values of panels_edit() once the form has been submitted. See the explanation of 482 * the return value below to understand the different types of values returned by panels_edit() 483 * at different stages of FAPI. Under most circumstances, simply passing in 484 * drupal_get_destination() is all that's necessary. 485 * @param array $content_types \n 486 * An associative array of allowed content types, typically as returned from 487 * panels_common_get_allowed_types(). Note that context partially governs available content types, 488 * so you will want to create any relevant contexts using panels_create_context() or 489 * panels_create_context_empty() to make sure all the appropriate content types are available. 490 * 491 * @return 492 * Because the functions called by panels_edit() invoke the form API, this function 493 * returns different values depending on the stage of form submission we're at. In Drupal 5, 494 * the phase of form submission is indicated by the contents of $_POST['op']. Here's what you'll 495 * get at different stages: 496 * -# If !$_POST['op']: then we're on on the initial passthrough and the form is being 497 * rendered, so it's the $form itself that's being returned. Because negative margins, 498 * a common CSS technique, bork the display editor's ajax drag-and-drop, it's important 499 * that the $output be printed, not returned. Use this syntax in the caller function: \n 500 * print theme('page', panels_edit($display, $destination, $content_types), FALSE); \n 501 * -# If $_POST['op'] == t('Cancel'): form submission has been cancelled. If empty($destination) == FALSE, 502 * then there is no return value and the panels API takes care of redirecting to $destination. 503 * If empty($destination) == TRUE, then there's still no return value, but the caller function 504 * has to take care of form redirection. 505 * -# If $_POST['op'] == ('Save'): the form has been submitted successfully and has run through 506 * panels_edit_display_submit(). $output depends on the value of $destination: 507 * - If empty($destination) == TRUE: $output contains the modified $display 508 * object, and no redirection will occur. This option is useful if the caller 509 * needs to perform additional operations on or with the modified $display before 510 * the page request is complete. Using hook_form_alter() to add an additional submit 511 * handler is typically the preferred method for something like this, but there 512 * are certain use cases where that is infeasible and $destination = NULL should 513 * be used instead. If this method is employed, the caller will need to handle form 514 * redirection. Note that having $_REQUEST['destination'] set, whether via 515 * drupal_get_destination() or some other method, will NOT interfere with this 516 * functionality; consequently, you can use drupal_get_destination() to safely store 517 * your desired redirect in the caller function, then simply use drupal_goto() once 518 * panels_edit() has done its business. 519 * - If empty($destination) == FALSE: the form will redirect to the URL string 520 * given in $destination and NO value will be returned. 521 */ 522 function panels_edit($display, $destination = NULL, $content_types = NULL, $title = FALSE) { 523 ctools_include('display-edit', 'panels'); 524 ctools_include('ajax'); 525 ctools_include('plugins', 'panels'); 526 return _panels_edit($display, $destination, $content_types, $title); 527 } 528 529 /** 530 * API entry point for selecting a layout for a given display. 531 * 532 * Layout selection is nothing more than a list of radio items encompassing the available 533 * layouts for this display, as defined by .inc files in the panels/layouts subdirectory. 534 * The only real complexity occurs when a user attempts to change the layout of a display 535 * that has some content in it. 536 * 537 * @param object $display instanceof panels_display \n 538 * A fully loaded panels $display object, as returned from panels_load_display(). 539 * Merely passing a did is NOT sufficient. 540 * @param string $finish 541 * A string that will be used for the text of the form submission button. If no value is provided, 542 * then the form submission button will default to t('Save'). 543 * @param mixed $destination 544 * Basic usage is a string containing the URL that the form should redirect to upon submission. 545 * For a discussion of advanced usages, see panels_edit(). 546 * @param mixed $allowed_layouts 547 * Allowed layouts has three different behaviors that depend on which of three value types 548 * are passed in by the caller: 549 * #- if $allowed_layouts instanceof panels_allowed_layouts (includes subclasses): the most 550 * complex use of the API. The caller is passing in a loaded panels_allowed_layouts object 551 * that the client module previously created and stored somewhere using a custom storage 552 * mechanism. 553 * #- if is_string($allowed_layouts): the string will be used in a call to variable_get() which 554 * will call the $allowed_layouts . '_allowed_layouts' var. If the data was stored properly 555 * in the system var, the $allowed_layouts object will be unserialized and recreated. 556 * @see panels_common_set_allowed_layouts() 557 * #- if is_null($allowed_layouts): the default behavior, which also provides backwards 558 * compatibility for implementations of the Panels2 API written before beta4. In this case, 559 * a dummy panels_allowed_layouts object is created which does not restrict any layouts. 560 * Subsequent behavior is indistinguishable from pre-beta4 behavior. 561 * 562 * @return 563 * Can return nothing, or a modified $display object, or a redirection string; return values for the 564 * panels_edit* family of functions are quite complex. See panels_edit() for detailed discussion. 565 * @see panels_edit() 566 */ 567 function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) { 568 ctools_include('display-layout', 'panels'); 569 ctools_include('plugins', 'panels'); 570 return _panels_edit_layout($display, $finish, $destination, $allowed_layouts); 571 } 572 573 // --------------------------------------------------------------------------- 574 // Panels database functions 575 576 /** 577 * Forms the basis of a panel display 578 * 579 */ 580 class panels_display { 581 var $args = array(); 582 var $content = array(); 583 var $panels = array(); 584 var $incoming_content = NULL; 585 var $css_id = NULL; 586 var $context = array(); 587 var $did = 'new'; 588 var $renderer; 589 590 function panels_display() { 591 // Set the default renderer to either the legacy or the standard renderer, 592 // depending on the legacy rendering state 593 $this->renderer = variable_get('panels_legacy_rendering_mode', TRUE) ? 'legacy' : 'standard'; 594 } 595 596 function add_pane(&$pane, $location = NULL) { 597 // If no location specified, use what's set in the pane. 598 if (empty($location)) { 599 $location = $pane->panel; 600 } 601 else { 602 $pane->panel = $location; 603 } 604 605 // Get a temporary pid for this pane. 606 $pane->pid = "new-" . $this->next_new_pid(); 607 608 // Add the pane to the approprate spots. 609 $this->content[$pane->pid] = &$pane; 610 $this->panels[$location][] = $pane->pid; 611 } 612 613 function duplicate_pane($pid, $location = FALSE) { 614 $pane = $this->clone_pane($pid); 615 $this->add_pane($pane, $location); 616 } 617 618 function clone_pane($pid) { 619 $pane = drupal_clone($this->content[$pid]); 620 return $pane; 621 } 622 623 function next_new_pid() { 624 // We don't use static vars to record the next new pid because 625 // temporary pids can last for years in exports and in caching 626 // during editing. 627 $id = array(0); 628 foreach (array_keys($this->content) as $pid) { 629 if (!is_numeric($pid)) { 630 $id[] = substr($pid, 4); 631 } 632 } 633 $next_id = max($id); 634 return ++$next_id; 635 } 636 637 /** 638 * Get the title from a display. 639 * 640 * The display must have already been rendered, or the setting to set the 641 * display's title from a pane's title will not have worked. 642 * 643 * @return 644 * The title to use. If NULL, this means to let any default title that may be in use 645 * pass through. i.e, do not actually set the title. 646 */ 647 function get_title() { 648 switch ($this->hide_title) { 649 case PANELS_TITLE_NONE: 650 return ''; 651 652 case PANELS_TITLE_PANE: 653 return isset($this->stored_pane_title) ? $this->stored_pane_title : ''; 654 655 case PANELS_TITLE_FIXED: 656 case FALSE; // For old exported panels that are not in the database. 657 if (!empty($this->title)) { 658 return filter_xss_admin(ctools_context_keyword_substitute($this->title, array(), $this->context)); 659 } 660 return NULL; 661 } 662 } 663 664 /** 665 * Render this panels display. 666 * 667 * After checking to ensure the designated layout plugin is valid, a 668 * display renderer object is spawned and runs its rendering logic. 669 * 670 * @param mixed $renderer 671 * An instantiated display renderer object, or the name of a display 672 * renderer plugin+class to be fetched. Defaults to NULL. When NULL, the 673 * predesignated display renderer will be used. 674 */ 675 function render($renderer = NULL) { 676 $layout = panels_get_layout($this->layout); 677 if (!$layout) { 678 return NULL; 679 } 680 681 // If we were not given a renderer object, load it. 682 if (!is_object($renderer)) { 683 // If the renderer was not specified, default to $this->renderer 684 // which is either standard or was already set for us. 685 $renderer = panels_get_renderer_handler(!empty($renderer) ? $renderer : $this->renderer, $this); 686 if (!$renderer) { 687 return NULL; 688 } 689 } 690 691 $output = ''; 692 // Let modules act just prior to render. 693 foreach (module_implements('panels_pre_render') as $module) { 694 $function = $module . '_panels_pre_render'; 695 $output .= $function($this, $renderer); 696 } 697 698 $output .= $renderer->render(); 699 700 // Let modules act just after render. 701 foreach (module_implements('panels_post_render') as $module) { 702 $function = $module . '_panels_post_render'; 703 $output .= $function($this, $renderer); 704 } 705 return $output; 706 } 707 } 708 709 /** 710 * }@ End of 'defgroup mainapi', although other functions are specifically added later 711 */ 712 713 /** 714 * Creates a new display, setting the ID to our magic new id. 715 */ 716 function panels_new_display() { 717 ctools_include('export'); 718 $display = ctools_export_new_object('panels_display', FALSE); 719 $display->did = 'new'; 720 return $display; 721 } 722 723 /** 724 * Create a new pane. 725 * 726 * @todo -- use schema API for some of this? 727 */ 728 function panels_new_pane($type, $subtype, $set_defaults = FALSE) { 729 ctools_include('export'); 730 $pane = ctools_export_new_object('panels_pane', FALSE); 731 $pane->pid = 'new'; 732 $pane->type = $type; 733 $pane->subtype = $subtype; 734 if ($set_defaults) { 735 $content_type = ctools_get_content_type($type); 736 $content_subtype = ctools_content_get_subtype($content_type, $subtype); 737 $pane->configuration = ctools_content_get_defaults($content_type, $content_subtype); 738 } 739 740 return $pane; 741 } 742 743 /** 744 * Load and fill the requested $display object(s). 745 * 746 * Helper function primarily for for panels_load_display(). 747 * 748 * @param array $dids 749 * An indexed array of dids to be loaded from the database. 750 * 751 * @return $displays 752 * An array of displays, keyed by their display dids. 753 * 754 * @todo schema API can drasticly simplify this code. 755 */ 756 function panels_load_displays($dids) { 757 $displays = array(); 758 if (empty($dids) || !is_array($dids)) { 759 return $displays; 760 } 761 762 $result = db_query("SELECT * FROM {panels_display} WHERE did IN (" . db_placeholders($dids) . ")", $dids); 763 764 ctools_include('export'); 765 while ($obj = db_fetch_object($result)) { 766 $displays[$obj->did] = ctools_export_unpack_object('panels_display', $obj); 767 // Modify the hide_title field to go from a bool to an int if necessary. 768 } 769 770 $result = db_query("SELECT * FROM {panels_pane} WHERE did IN (" . db_placeholders($dids) . ") ORDER BY did, panel, position", $dids); 771 772 while ($obj = db_fetch_object($result)) { 773 $pane = ctools_export_unpack_object('panels_pane', $obj); 774 775 $displays[$pane->did]->panels[$pane->panel][] = $pane->pid; 776 $displays[$pane->did]->content[$pane->pid] = $pane; 777 } 778 779 return $displays; 780 } 781 782 /** 783 * Load a single display. 784 * 785 * @ingroup mainapi 786 * 787 * @param int $did 788 * The display id (did) of the display to be loaded. 789 * 790 * @return object $display instanceof panels_display \n 791 * Returns a partially-loaded panels_display object. $display objects returned from 792 * from this function have only the following data: 793 * - $display->did (the display id) 794 * - $display->name (the 'name' of the display, where applicable - it often isn't) 795 * - $display->layout (a string with the system name of the display's layout) 796 * - $display->panel_settings (custom layout style settings contained in an associative array; NULL if none) 797 * - $display->layout_settings (panel size and configuration settings for Flexible layouts; NULL if none) 798 * - $display->css_id (the special css_id that has been assigned to this display, if any; NULL if none) 799 * - $display->content (an array of pane objects, keyed by pane id (pid)) 800 * - $display->panels (an associative array of panel regions, each an indexed array of pids in the order they appear in that region) 801 * - $display->cache (any relevant data from panels_simple_cache) 802 * - $display->args 803 * - $display->incoming_content 804 * 805 * While all of these members are defined, $display->context is NEVER defined in the returned $display; 806 * it must be set using one of the ctools_context_create() functions. 807 */ 808 function panels_load_display($did) { 809 $displays = panels_load_displays(array($did)); 810 if (!empty($displays)) { 811 return array_shift($displays); 812 } 813 } 814 815 /** 816 * Save a display object. 817 * 818 * @ingroup mainapi 819 * 820 * Note a new $display only receives a real did once it is run through this function. 821 * Until then, it uses a string placeholder, 'new', in place of a real did. The same 822 * applies to all new panes (whether on a new $display or not); in addition, 823 * panes have sequential numbers appended, of the form 'new-1', 'new-2', etc. 824 * 825 * @param object $display instanceof panels_display \n 826 * The display object to be saved. Passed by reference so the caller need not use 827 * the return value for any reason except convenience. 828 * 829 * @return object $display instanceof panels_display \n 830 */ 831 function panels_save_display(&$display) { 832 $update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array(); 833 drupal_write_record('panels_display', $display, $update); 834 835 $pids = array(); 836 if ($update) { 837 // Get a list of all panes currently in the database for this display so we can know if there 838 // are panes that need to be deleted. (i.e, aren't currently in our list of panes). 839 $result = db_query("SELECT pid FROM {panels_pane} WHERE did = %d", $display->did); 840 while ($pane = db_fetch_object($result)) { 841 $pids[$pane->pid] = $pane->pid; 842 } 843 } 844 845 // update all the panes 846 ctools_include('plugins', 'panels'); 847 ctools_include('content'); 848 849 foreach ($display->panels as $id => $panes) { 850 $position = 0; 851 $new_panes = array(); 852 foreach ((array) $panes as $pid) { 853 if (!isset($display->content[$pid])) { 854 continue; 855 } 856 $pane = $display->content[$pid]; 857 $type = ctools_get_content_type($pane->type); 858 859 $pane->position = $position++; 860 $pane->did = $display->did; 861 862 $old_pid = $pane->pid; 863 drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array()); 864 865 if ($pane->pid != $old_pid) { 866 // and put it back so our pids and positions can be used 867 unset($display->content[$id]); 868 $display->content[$pane->pid] = $pane; 869 870 // If the title pane was one of our panes that just got its ID changed, 871 // we need to change it in the database, too. 872 if (isset($display->title_pane) && $display->title_pane == $old_pid) { 873 $display->title_pane = $pane->pid; 874 // Do a simple update query to write it so we don't have to rewrite 875 // the whole record. We can't just save writing the whole record here 876 // because it was needed to get the did. Chicken, egg, more chicken. 877 db_query("UPDATE {panels_display} SET title_pane = %d WHERE did = %d", $pane->pid, $display->did); 878 } 879 } 880 881 // re-add this to the list of content for this panel. 882 $new_panes[] = $pane->pid; 883 884 // Remove this from the list of panes scheduled for deletion. 885 if (isset($pids[$pane->pid])) { 886 unset($pids[$pane->pid]); 887 } 888 } 889 890 $display->panels[$id] = $new_panes; 891 } 892 if (!empty($pids)) { 893 db_query("DELETE FROM {panels_pane} WHERE pid IN (" . db_placeholders($pids) . ")", $pids); 894 } 895 896 // Clear any cached content for this display. 897 panels_clear_cached_content($display); 898 899 // Allow other modules to take action when a display is saved. 900 module_invoke_all('panels_display_save', $display); 901 902 // Log the change to watchdog, using the same style as node.module 903 $watchdog_args = array('%did' => $display->did); 904 if (!empty($display->title)) { 905 $watchdog_args['%title'] = $display->title; 906 watchdog('content', 'Panels: saved display "%title" with display id %did', $watchdog_args, WATCHDOG_NOTICE); 907 } 908 else { 909 watchdog('content', 'Panels: saved display with id %did', $watchdog_args, WATCHDOG_NOTICE); 910 } 911 912 // to be nice, even tho we have a reference. 913 return $display; 914 } 915 916 /** 917 * Delete a display. 918 */ 919 function panels_delete_display($display) { 920 if (is_object($display)) { 921 $did = $display->did; 922 } 923 else { 924 $did = $display; 925 } 926 db_query("DELETE FROM {panels_display} WHERE did = %d", $did); 927 db_query("DELETE FROM {panels_pane} WHERE did = %d", $did); 928 } 929 930 /** 931 * Exports the provided display into portable code. 932 * 933 * This function is primarily intended as a mechanism for cloning displays. 934 * It generates an exact replica (in code) of the provided $display, with 935 * the exception that it replaces all ids (dids and pids) with 'new-*' values. 936 * Only once panels_save_display() is called on the code version of $display will 937 * the exported display written to the database and permanently saved. 938 * 939 * @see panels_page_export() or _panels_page_fetch_display() for sample implementations. 940 * 941 * @ingroup mainapi 942 * 943 * @param object $display instanceof panels_display \n 944 * This export function does no loading of additional data about the provided 945 * display. Consequently, the caller should make sure that all the desired data 946 * has been loaded into the $display before calling this function. 947 * @param string $prefix 948 * A string prefix that is prepended to each line of exported code. This is primarily 949 * used for prepending a double space when exporting so that the code indents and lines up nicely. 950 * 951 * @return string $output 952 * The passed-in $display expressed as code, ready to be imported. Import by running 953 * eval($output) in the caller function; doing so will create a new $display variable 954 * with all the exported values. Note that if you have already defined a $display variable in 955 * the same scope as where you eval(), your existing $display variable WILL be overwritten. 956 */ 957 function panels_export_display($display, $prefix = '') { 958 ctools_include('export'); 959 $output = ctools_export_object('panels_display', $display, $prefix); 960 961 // Initialize empty properties. 962 $output .= $prefix . '$display->content = array()' . ";\n"; 963 $output .= $prefix . '$display->panels = array()' . ";\n"; 964 $panels = array(); 965 966 $title_pid = 0; 967 if (!empty($display->content)) { 968 $pid_counter = 0; 969 $region_counters = array(); 970 foreach ($display->content as $pane) { 971 $pid = 'new-' . ++$pid_counter; 972 if ($pane->pid == $display->title_pane) { 973 $title_pid = $pid; 974 } 975 $pane->pid = $pid; 976 $output .= ctools_export_object('panels_pane', $pane, $prefix . ' '); 977 $output .= "$prefix " . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n"; 978 if (!isset($region_counters[$pane->panel])) { 979 $region_counters[$pane->panel] = 0; 980 } 981 $output .= "$prefix " . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n"; 982 } 983 } 984 $output .= $prefix . '$display->hide_title = '; 985 switch ($display->hide_title) { 986 case PANELS_TITLE_FIXED: 987 $output .= 'PANELS_TITLE_FIXED'; 988 break; 989 case PANELS_TITLE_NONE: 990 $output .= 'PANELS_TITLE_NONE'; 991 break; 992 case PANELS_TITLE_PANE: 993 $output .= 'PANELS_TITLE_PANE'; 994 break; 995 } 996 $output .= ";\n"; 997 998 $output .= $prefix . '$display->title_pane =' . " '$title_pid';\n"; 999 return $output; 1000 } 1001 1002 /** 1003 * Render a display by loading the content into an appropriate 1004 * array and then passing through to panels_render_layout. 1005 * 1006 * if $incoming_content is NULL, default content will be applied. Use 1007 * an empty string to indicate no content. 1008 * @ingroup hook_invocations 1009 */ 1010 function panels_render_display(&$display, $renderer = NULL) { 1011 ctools_include('display-render', 'panels'); 1012 ctools_include('plugins', 'panels'); 1013 ctools_include('context'); 1014 1015 if (!empty($display->context)) { 1016 if ($form_context = ctools_context_get_form($display->context)) { 1017 $form_context->form['#theme'] = 'panels_render_display_form'; 1018 $form_context->form['#display'] = &$display; 1019 $form_context->form['#form_context_id'] = $form_context->id; 1020 return drupal_render_form($form_context->form_id, $form_context->form); 1021 } 1022 } 1023 return $display->render($renderer); 1024 } 1025 1026 /** 1027 * Theme function to render our panel as a form. 1028 * 1029 * When rendering a display as a form, the entire display needs to be 1030 * inside the <form> tag so that the form can be spread across the 1031 * panes. This sets up the form system to be the main caller and we 1032 * then operate as a theme function of the form. 1033 */ 1034 function theme_panels_render_display_form($form) { 1035 $form['#children'] = $form['#display']->render(); 1036 drupal_render($form); 1037 return theme('form', $form); 1038 } 1039 1040 // @layout 1041 function panels_print_layout_icon($id, $layout, $title = NULL) { 1042 ctools_add_css('panels_admin', 'panels'); 1043 $file = $layout['path'] . '/' . $layout['icon']; 1044 return theme('panels_layout_icon', $id, theme('image', $file, strip_tags($layout['title']), strip_tags($layout['description'])), $title); 1045 } 1046 1047 /** 1048 * Theme the layout icon image 1049 * @layout 1050 * @todo move to theme.inc 1051 */ 1052 function theme_panels_layout_icon($id, $image, $title = NULL) { 1053 $output = '<div class="layout-icon">'; 1054 $output .= $image; 1055 if ($title) { 1056 $output .= '<div class="caption">' . $title . '</div>'; 1057 } 1058 $output .= '</div>'; 1059 return $output; 1060 } 1061 1062 /** 1063 * Theme the layout link image 1064 * @layout 1065 */ 1066 function theme_panels_layout_link($title, $id, $image, $link) { 1067 $output = '<div class="layout-link">'; 1068 $output .= $image; 1069 $output .= '<div>' . $title . '</div>'; 1070 $output .= '</div>'; 1071 return $output; 1072 } 1073 1074 /** 1075 * Print the layout link. Sends out to a theme function. 1076 * @layout 1077 */ 1078 function panels_print_layout_link($id, $layout, $link, $options = array()) { 1079 if (isset($options['query']['q'])) { 1080 unset($options['query']['q']); 1081 } 1082 1083 ctools_add_css('panels_admin', 'panels'); 1084 $file = $layout['path'] . '/' . $layout['icon']; 1085 $image = l(theme('image', $file), $link, array('html' => true) + $options); 1086 $title = l($layout['title'], $link, $options); 1087 return theme('panels_layout_link', $title, $id, $image, $link); 1088 } 1089 1090 1091 /** 1092 * Gateway to the PanelsLegacyState class/object, which does all legacy state 1093 * checks and provides information about the cause of legacy states as needed. 1094 * 1095 * @return PanelsLegacyState $legacy 1096 */ 1097 function panels_get_legacy_state() { 1098 static $legacy = NULL; 1099 if (!isset($legacy)) { 1100 ctools_include('legacy', 'panels'); 1101 $legacy = new PanelsLegacyState(); 1102 } 1103 return $legacy; 1104 } 1105 1106 /** 1107 * Get the display that is currently being rendered as a page. 1108 * 1109 * Unlike in previous versions of this, this only returns the display, 1110 * not the page itself, because there are a number of different ways 1111 * to get to this point. It is hoped that the page data isn't needed 1112 * at this point. If it turns out there is, we will do something else to 1113 * get that functionality. 1114 */ 1115 function panels_get_current_page_display($change = NULL) { 1116 static $display = NULL; 1117 if ($change) { 1118 $display = $change; 1119 } 1120 1121 return $display; 1122 } 1123 1124 /** 1125 * Clean up the panel pane variables for the template. 1126 */ 1127 function template_preprocess_panels_pane($vars) { 1128 $content = $vars['output']; 1129 // basic classes 1130 $vars['classes'] = 'panel-pane'; 1131 $vars['id'] = ''; 1132 1133 // Add some usable classes based on type/subtype 1134 ctools_include('cleanstring'); 1135 $type_class = $content->type ? 'pane-'. ctools_cleanstring($content->type, array('lower case' => TRUE)) : ''; 1136 $subtype_class = $content->subtype ? 'pane-'. ctools_cleanstring($content->subtype, array('lower case' => TRUE)) : ''; 1137 1138 // Sometimes type and subtype are the same. Avoid redudant classes. 1139 if ($type_class != $subtype_class) { 1140 $vars['classes'] .= " $type_class $subtype_class"; 1141 } 1142 else { 1143 $vars['classes'] .= " $type_class"; 1144 } 1145 1146 // Add id and custom class if sent in. 1147 if (!empty($content->content)) { 1148 if (!empty($content->css_id)) { 1149 $vars['id'] = ' id="' . $content->css_id . '"'; 1150 } 1151 if (!empty($content->css_class)) { 1152 $vars['classes'] .= ' ' . $content->css_class; 1153 } 1154 } 1155 1156 // administrative links, only if there is permission. 1157 $vars['admin_links'] = ''; 1158 if (user_access('view pane admin links') && !empty($content->admin_links)) { 1159 $vars['admin_links'] = theme('links', $content->admin_links); 1160 } 1161 1162 $vars['title'] = !empty($content->title) ? $content->title : ''; 1163 1164 $vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : ''; 1165 $vars['content'] = !empty($content->content) ? $content->content : ''; 1166 1167 $vars['links'] = !empty($content->links) ? theme('links', $content->links) : ''; 1168 $vars['more'] = ''; 1169 if (!empty($content->more)) { 1170 if (empty($content->more['title'])) { 1171 $content->more['title'] = t('more'); 1172 } 1173 $vars['more'] = l($content->more['title'], $content->more['href'], $content->more); 1174 } 1175 } 1176 1177 /** 1178 * Route Panels' AJAX calls to the correct object. 1179 * 1180 * Panels' AJAX is controlled mostly by renderer objects. This menu callback 1181 * accepts the incoming request, figures out which object should handle the 1182 * request, and attempts to route it. If no object can be found, the default 1183 * Panels editor object is used. 1184 * 1185 * Calls are routed via the ajax_* method space. For example, if visiting 1186 * panels/ajax/add-pane then $renderer::ajax_add_pane() will be called. 1187 * This means commands can be added without having to create new callbacks. 1188 * 1189 * The first argument *must always* be the cache key so that a cache object 1190 * can be passed through. Other arguments will be passed through untouched 1191 * so that the method can do whatever it needs to do. 1192 */ 1193 function panels_ajax_router() { 1194 $args = func_get_args(); 1195 if (count($args) < 3) { 1196 return MENU_NOT_FOUND; 1197 } 1198 1199 ctools_include('display-edit', 'panels'); 1200 ctools_include('plugins', 'panels'); 1201 ctools_include('ajax'); 1202 ctools_include('modal'); 1203 ctools_include('context'); 1204 ctools_include('content'); 1205 1206 $plugin_name = array_shift($args); 1207 $method = array_shift($args); 1208 $cache_key = array_shift($args); 1209 1210 $plugin = panels_get_display_renderer($plugin_name); 1211 if (!$plugin) { 1212 // This is the default renderer for handling AJAX commands. 1213 $plugin = panels_get_display_renderer('editor'); 1214 } 1215 1216 $cache = panels_edit_cache_get($cache_key); 1217 if (empty($cache)) { 1218 return MENU_ACCESS_DENIED; 1219 } 1220 1221 $renderer = panels_get_renderer_handler($plugin, $cache->display); 1222 if (!$renderer) { 1223 return MENU_ACCESS_DENIED; 1224 } 1225 1226 $method = 'ajax_' . str_replace('-', '_', $method); 1227 if (!method_exists($renderer, $method)) { 1228 return MENU_NOT_FOUND; 1229 } 1230 1231 $renderer->cache = &$cache; 1232 ctools_include('cleanstring'); 1233 $renderer->clean_key = ctools_cleanstring($cache_key); 1234 1235 $output = call_user_func_array(array($renderer, $method), $args); 1236 if (empty($output) && !empty($renderer->commands)) { 1237 return ctools_ajax_render($renderer->commands); 1238 } 1239 return $output; 1240 } 1241 1242 // -------------------------------------------------------------------------- 1243 // Panels caching functions and callbacks 1244 // 1245 // When editing displays and the like, Panels has a caching system that relies 1246 // on a callback to determine where to get the actual cache. 1247 1248 // @todo This system needs to be better documented so that it can be 1249 // better used. 1250 1251 /** 1252 * Get an object from cache. 1253 */ 1254 function panels_cache_get($obj, $did, $skip_cache = FALSE) { 1255 ctools_include('object-cache'); 1256 // we often store contexts in cache, so let's just make sure we can load 1257 // them. 1258 ctools_include('context'); 1259 return ctools_object_cache_get($obj, 'panels_display:' . $did, $skip_cache); 1260 } 1261 1262 /** 1263 * Save the edited object into the cache. 1264 */ 1265 function panels_cache_set($obj, $did, $cache) { 1266 ctools_include('object-cache'); 1267 return ctools_object_cache_set($obj, 'panels_display:' . $did, $cache); 1268 } 1269 1270 /** 1271 * Clear a object from the cache; used if the editing is aborted. 1272 */ 1273 function panels_cache_clear($obj, $did) { 1274 ctools_include('object-cache'); 1275 return ctools_object_cache_clear($obj, 'panels_display:' . $did); 1276 } 1277 1278 /** 1279 * Create the default cache for editing panel displays. 1280 * 1281 * If an application is using the Panels display editor without having 1282 * specified a cache key, this method can be used to create the default 1283 * cache. 1284 */ 1285 function panels_edit_cache_get_default(&$display, $content_types = NULL, $title = FALSE) { 1286 if (empty($content_types)) { 1287 $content_types = ctools_content_get_available_types(); 1288 } 1289 1290 $display->cache_key = $display->did; 1291 panels_cache_clear('display', $display->did); 1292 1293 $cache = new stdClass(); 1294 $cache->display = &$display; 1295 $cache->content_types = $content_types; 1296 $cache->display_title = $title; 1297 1298 panels_edit_cache_set($cache); 1299 return $cache; 1300 } 1301 1302 /** 1303 * Method to allow modules to provide their own caching mechanism for the 1304 * display editor. 1305 */ 1306 function panels_edit_cache_get($cache_key) { 1307 if (strpos($cache_key, ':') !== FALSE) { 1308 list($module, $argument) = explode(':', $cache_key, 2); 1309 return module_invoke($module, 'panels_cache_get', $argument); 1310 } 1311 1312 // Fall back to our normal method: 1313 return panels_cache_get('display', $cache_key); 1314 } 1315 1316 /** 1317 * Method to allow modules to provide their own caching mechanism for the 1318 * display editor. 1319 */ 1320 function panels_edit_cache_set($cache) { 1321 $cache_key = $cache->display->cache_key; 1322 if (strpos($cache_key, ':') !== FALSE) { 1323 list($module, $argument) = explode(':', $cache_key, 2); 1324 return module_invoke($module, 'panels_cache_set', $argument, $cache); 1325 } 1326 1327 // Fall back to our normal method: 1328 return panels_cache_set('display', $cache_key, $cache); 1329 } 1330 1331 /** 1332 * Method to allow modules to provide their own mechanism to write the 1333 * cache used in the display editor. 1334 */ 1335 function panels_edit_cache_save($cache) { 1336 $cache_key = $cache->display->cache_key; 1337 if (strpos($cache_key, ':') !== FALSE) { 1338 list($module, $argument) = explode(':', $cache_key, 2); 1339 if (function_exists($module . '_panels_cache_save')) { 1340 return module_invoke($module, 'panels_cache_save', $argument, $cache); 1341 } 1342 } 1343 1344 // Fall back to our normal method: 1345 return panels_save_display($cache->display); 1346 } 1347 1348 /** 1349 * Method to allow modules to provide their own mechanism to clear the 1350 * cache used in the display editor. 1351 */ 1352 function panels_edit_cache_clear($cache) { 1353 $cache_key = $cache->display->cache_key; 1354 if (strpos($cache_key, ':') !== FALSE) { 1355 list($module, $argument) = explode(':', $cache_key, 2); 1356 if (function_exists($module . '_panels_cache_clear')) { 1357 return module_invoke($module, 'panels_cache_clear', $argument, $cache); 1358 } 1359 } 1360 1361 // Fall back to our normal method: 1362 return panels_cache_clear('display', $cache_key); 1363 } 1364 1365 /** 1366 * Method to allow modules to provide a mechanism to break locks. 1367 */ 1368 function panels_edit_cache_break_lock($cache) { 1369 if (empty($cache->locked)) { 1370 return; 1371 } 1372 1373 $cache_key = $cache->display->cache_key; 1374 if (strpos($cache_key, ':') !== FALSE) { 1375 list($module, $argument) = explode(':', $cache_key, 2); 1376 if (function_exists($module . '_panels_cache_break_lock')) { 1377 return module_invoke($module, 'panels_cache_break_lock', $argument, $cache); 1378 } 1379 } 1380 1381 // Normal panel display editing has no locks, so we do nothing if there is 1382 // no fallback. 1383 return; 1384 } 1385 1386 // -------------------------------------------------------------------------- 1387 // Callbacks on behalf of the panel_context plugin. 1388 // 1389 // The panel_context plugin lets Panels be used in page manager. These 1390 // callbacks allow the display editing system to use the page manager 1391 // cache rather than the default display cache. They are routed by the cache 1392 // key via panels_edit_cache_* functions. 1393 1394 /** 1395 * Get display edit cache on behalf of panel context. 1396 * 1397 * The key is the second half of the key in this form: 1398 * panel_context:TASK_NAME:HANDLER_NAME; 1399 */ 1400 function panel_context_panels_cache_get($key) { 1401 ctools_include('common', 'panels'); 1402 ctools_include('context'); 1403 ctools_include('context-task-handler'); 1404 // this loads the panel context inc even if we don't use the plugin. 1405 $plugin = page_manager_get_task_handler('panel_context'); 1406 1407 list($task_name, $handler_name) = explode(':', $key, 2); 1408 $page = page_manager_get_page_cache($task_name); 1409 if (isset($page->display_cache[$handler_name])) { 1410 return $page->display_cache[$handler_name]; 1411 } 1412 1413 if ($handler_name) { 1414 $handler = &$page->handlers[$handler_name]; 1415 } 1416 else { 1417 $handler = &$page->new_handler; 1418 } 1419 $cache = new stdClass(); 1420 1421 $cache->display = &panels_panel_context_get_display($handler); 1422 $cache->display->context = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler); 1423 $cache->display->cache_key = 'panel_context:' . $key; 1424 $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context); 1425 $cache->display_title = TRUE; 1426 $cache->locked = $page->locked; 1427 1428 return $cache; 1429 } 1430 1431 /** 1432 * Get the Page Manager cache for the panel_context plugin. 1433 */ 1434 function _panel_context_panels_cache_get_page_cache($key, $cache) { 1435 list($task_name, $handler_name) = explode(':', $key, 2); 1436 $page = page_manager_get_page_cache($task_name); 1437 $page->display_cache[$handler_name] = $cache; 1438 if ($handler_name) { 1439 $page->handlers[$handler_name]->conf['display'] = $cache->display; 1440 $page->handler_info[$handler_name]['changed'] |= PAGE_MANAGER_CHANGED_CACHED; 1441 } 1442 else { 1443 $page->new_handler->conf['display'] = $cache->display; 1444 } 1445 1446 return $page; 1447 } 1448 1449 /** 1450 * Store a display edit in progress in the page cache. 1451 */ 1452 function panel_context_panels_cache_set($key, $cache) { 1453 $page = _panel_context_panels_cache_get_page_cache($key, $cache); 1454 page_manager_set_page_cache($page); 1455 } 1456 1457 /** 1458 * Save all changes made to a display using the Page Manager page cache. 1459 */ 1460 function panel_context_panels_cache_clear($key, $cache) { 1461 $page = _panel_context_panels_cache_get_page_cache($key, $cache); 1462 page_manager_clear_page_cache($page->task_name); 1463 } 1464 1465 /** 1466 * Save all changes made to a display using the Page Manager page cache. 1467 */ 1468 function panel_context_panels_cache_save($key, $cache) { 1469 $page = _panel_context_panels_cache_get_page_cache($key, $cache); 1470 page_manager_save_page_cache($page); 1471 } 1472 1473 /** 1474 * Break the lock on a page manager page. 1475 */ 1476 function panel_context_panels_cache_break_lock($key, $cache) { 1477 $page = _panel_context_panels_cache_get_page_cache($key, $cache); 1478 ctools_object_cache_clear_all('page_manager_page', $page->task_name); 1479 } 1480 1481 // -------------------------------------------------------------------------- 1482 // Callbacks on behalf of the panels page wizards 1483 // 1484 // The page wizards are a pluggable set of 'wizards' to make it easy to create 1485 // specific types of pages based upon whatever someone felt like putting 1486 // together. Since they will very often have content editing, we provide 1487 // a generic mechanism to allow them to store their editing cache in the 1488 // wizard cache. 1489 // 1490 // For them to use this mechanism, they just need to use: 1491 // $cache = panels_edit_cache_get('panels_page_wizard:' . $plugin['name']); 1492 1493 /** 1494 * Get display edit cache for the panels mini export UI 1495 * 1496 * The key is the second half of the key in this form: 1497 * panels_page_wizard:TASK_NAME:HANDLER_NAME; 1498 */ 1499 function panels_page_wizard_panels_cache_get($key) { 1500 ctools_include('page-wizard'); 1501 ctools_include('context'); 1502 $wizard_cache = page_manager_get_wizard_cache($key); 1503 if (isset($wizard_cache->display_cache)) { 1504 return $wizard_cache->display_cache; 1505 } 1506 1507 ctools_include('common', 'panels'); 1508 $cache = new stdClass(); 1509 $cache->display = $wizard_cache->display; 1510 $cache->display->context = !empty($wizard_cache->context) ? $wizard_cache->context : array(); 1511 $cache->display->cache_key = 'panels_page_wizard:' . $key; 1512 $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context); 1513 $cache->display_title = TRUE; 1514 1515 return $cache; 1516 } 1517 1518 /** 1519 * Store a display edit in progress in the page cache. 1520 */ 1521 function panels_page_wizard_panels_cache_set($key, $cache) { 1522 ctools_include('page-wizard'); 1523 $wizard_cache = page_manager_get_wizard_cache($key); 1524 $wizard_cache->display_cache = $cache; 1525 page_manager_set_wizard_cache($wizard_cache); 1526 } 1527 1528 // -------------------------------------------------------------------------- 1529 // General utility functions 1530 1531 /** 1532 * Perform a drupal_goto on a destination that may be an array like url(). 1533 */ 1534 function panels_goto($destination) { 1535 if (!is_array($destination)) { 1536 return drupal_goto($destination); 1537 } 1538 else { 1539 // Prevent notices by adding defaults 1540 $destination += array( 1541 'query' => NULL, 1542 'fragment' => NULL, 1543 'http_response_code' => NULL, 1544 ); 1545 1546 return drupal_goto($destination['path'], $destination['query'], $destination['fragment'], $destination['http_response_code']); 1547 } 1548 } 1549 1550 1551 /** 1552 * For external use: Given a layout ID and a $content array, return the 1553 * panel display. 1554 * 1555 * The content array is filled in based upon the content available in the 1556 * layout. If it's a two column with a content array defined like 1557 * @code 1558 * array( 1559 * 'left' => t('Left side'), 1560 * 'right' => t('Right side') 1561 * ), 1562 * @endcode 1563 * 1564 * Then the $content array should be 1565 * @code 1566 * array( 1567 * 'left' => $output_left, 1568 * 'right' => $output_right, 1569 * ) 1570 * @endcode 1571 * 1572 * The output within each panel region can be either a single rendered 1573 * HTML string or an array of rendered HTML strings as though they were 1574 * panes. They will simply be concatenated together without separators. 1575 */ 1576 function panels_print_layout($layout, $content, $meta = 'standard') { 1577 ctools_include('plugins', 'panels'); 1578 1579 // Create a temporary display for this. 1580 $display = panels_new_display(); 1581 $display->layout = is_array($layout) ? $layout['name'] : $layout; 1582 $display->content = $content; 1583 1584 // Get our simple renderer 1585 $renderer = panels_get_renderer_handler('simple', $display); 1586 $renderer->meta_location = $meta; 1587 1588 return $renderer->render(); 1589 } 1590 1591 // -------------------------------------------------------------------------- 1592 // Deprecated functions 1593 // 1594 // Everything below this line will eventually go away. 1595 1596 /** 1597 * Load a panels include file. 1598 * 1599 * @deprecated This function is deprecated and should no longer be used. It will 1600 * be removed in the next major version of Panels. Use ctools_include() instead. 1601 */ 1602 function panels_load_include($include, $path = 'includes/') { 1603 static $loaded = array(); 1604 if (empty($loaded["$path$include.inc"])) { 1605 require_once './' . panels_get_path("$path$include.inc"); 1606 $loaded["$path$include.inc"] = TRUE; 1607 } 1608 } 1609 1610 /** 1611 * panels path helper function 1612 */ 1613 function panels_get_path($file, $base_path = FALSE, $module = 'panels') { 1614 $output = $base_path ? base_path() : ''; 1615 return $output . drupal_get_path('module', $module) . '/' . $file; 1616 } 1617 1618 /** 1619 * Given a full layout structure and a content array, render a panel display. 1620 * 1621 * @deprecated This function represents an old approach to rendering, and is 1622 * retained only as a temporary support for other modules still using that 1623 * approach. It will be removed in the next major version of Panels. 1624 */ 1625 function panels_render_layout($layout, $content, $css_id = NULL, $settings = array(), $display = NULL) { 1626 if (!empty($layout['css'])) { 1627 if (file_exists(path_to_theme() . '/' . $layout['css'])) { 1628 drupal_add_css(path_to_theme() . '/' . $layout['css']); 1629 } 1630 else { 1631 drupal_add_css($layout['path'] . '/' . $layout['css']); 1632 } 1633 } 1634 // This now comes after the CSS is added, because panels-within-panels must 1635 // have their CSS added in the right order; inner content before outer content. 1636 1637 // If $content is an object, it's a $display and we have to render its panes. 1638 if (is_object($content)) { 1639 $display = $content; 1640 if (empty($display->cache['method'])) { 1641 $content = panels_render_panes($display); 1642 } 1643 else { 1644 $cache = panels_get_cached_content($display, $display->args, $display->context); 1645 if ($cache === FALSE) { 1646 $cache = new panels_cache_object(); 1647 $cache->set_content(panels_render_panes($display)); 1648 panels_set_cached_content($cache, $display, $display->args, $display->context); 1649 } 1650 $content = $cache->content; 1651 } 1652 } 1653 1654 $output = theme($layout['theme'], check_plain($css_id), $content, $settings, $display); 1655 1656 return $output; 1657 } 1658 1659 /** 1660 * Get a list of panel regions available in the layout. 1661 * 1662 * @deprecated Use panels_get_regions instead. 1663 */ 1664 function panels_get_panels($layout, $display) { 1665 return panels_get_regions($layout, $display); 1666 } 1667 1668 /** 1669 * Select a context for a pane. 1670 * 1671 * @param $pane 1672 * A fully populated pane. 1673 * @param $contexts 1674 * A keyed array of available contexts. 1675 * 1676 * @return 1677 * The matching contexts or NULL if none or necessary, or FALSE if 1678 * requirements can't be met. 1679 * 1680 * @deprecated this function will be removed. 1681 */ 1682 function panels_pane_select_context($pane, $contexts) { 1683 return ctools_content_select_context($pane->type, $pane->subtype, $pane->configuration, $contexts); 1684 } 1685
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |