[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/panels/ -> panels.module (source)

   1  <?php
   2  
   3  /**
   4   * @file panels.module
   5   *
   6   * Core functionality for the Panels engine.
   7   */
   8  
   9  define('PANELS_REQUIRED_CTOOLS_API', '1.8');
  10  
  11  define('PANELS_TITLE_FIXED', 0); // Hide title use to be true/false. So false remains old behavior.
  12  define('PANELS_TITLE_NONE', 1); // And true meant no title.
  13  define('PANELS_TITLE_PANE', 2); // And this is the new behavior, where the title field will pick from a pane.
  14  
  15  /**
  16   * Returns the API version of Panels. This didn't exist in 1.
  17   *
  18   * @return An array with the major and minor versions
  19   */
  20  function panels_api_version() {
  21    return array(3, 1);
  22  }
  23  
  24  // --------------------------------------------------------------------------
  25  // Core Drupal hook implementations
  26  
  27  /**
  28   * Implementation of hook_theme()
  29   */
  30  function panels_theme() {
  31    // Safety: go away if CTools is not at an appropriate version.
  32    if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
  33      return array();
  34    }
  35  
  36    $theme = array();
  37    $theme['panels_layout_link'] = array(
  38      'arguments' => array('title', 'id', 'image', 'link'),
  39    );
  40    $theme['panels_layout_icon'] = array(
  41      'arguments' => array('id', 'image', 'title' => NULL),
  42    );
  43    $theme['panels_pane'] = array(
  44      'arguments' => array('output' => array(), 'pane' => array(), 'display' => array()),
  45      'path' => drupal_get_path('module', 'panels') . '/templates',
  46      'template' => 'panels-pane',
  47    );
  48    $theme['panels_common_content_list'] = array(
  49      'arguments' => array('display'),
  50      'file' => 'includes/common.inc',
  51    );
  52    $theme['panels_render_display_form'] = array(
  53      'arguments' => array('form' => NULL),
  54    );
  55  
  56    $theme['panels_dashboard'] = array(
  57      'arguments' => array(),
  58      'path' => drupal_get_path('module', 'panels') . '/templates',
  59      'file' => '../includes/callbacks.inc',
  60      'template' => 'panels-dashboard',
  61    );
  62  
  63    $theme['panels_dashboard_link'] = array(
  64      'arguments' => array('link' => array()),
  65      'path' => drupal_get_path('module', 'panels') . '/templates',
  66      'file' => '../includes/callbacks.inc',
  67      'template' => 'panels-dashboard-link',
  68    );
  69  
  70    $theme['panels_dashboard_block'] = array(
  71      'arguments' => array('block' => array()),
  72      'path' => drupal_get_path('module', 'panels') . '/templates',
  73      'file' => '../includes/callbacks.inc',
  74      'template' => 'panels-dashboard-block',
  75    );
  76  
  77    // We don't need layout and style themes in maintenance mode.
  78    // Disabling this: See http://drupal.org/node/979912 for information.
  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  


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7