You are here

panelizer.module in Panelizer 7.3

The Panelizer module attaches panels to entities, providing default panels and allowing each panel to be configured independently by privileged users.

File

panelizer.module
View source
<?php

/**
 * @file
 * The Panelizer module attaches panels to entities, providing default panels
 * and allowing each panel to be configured independently by privileged users.
 */
define('PANELIZER_VERSION', '3.0');

// -----------------------------------------------------------------------
// Drupal core hooks

/**
 * Implements hook_help().
 */
function panelizer_help($path, $arg) {
  if ($path == 'admin/structure/panelizer') {
    return '<p>' . t('Other than "Full page override" or "Default" (when applicable), only view modes enabled through the Custom Display Settings section of the <em>Manage Display</em> settings for that entity or bundle will be available for use.') . '</p>';
  }
}

/**
 * Implements hook_hook_info().
 */
function panelizer_hook_info() {
  $hooks = array(
    'panelizer_defaults_override_alter',
    'panelizer_entity_plugin_process_alter',
    'panelizer_operations_alter',
  );
  return array_fill_keys($hooks, array(
    'group' => 'panelizer',
  ));
}

/**
 * Implements hook_permission().
 */
function panelizer_permission() {
  $items = array(
    'administer panelizer' => array(
      'title' => t('administer panelizer'),
      'description' => t('Fully administer Panelizer - customize which entities can be managed with Panelizer, manage their default displays, etc.'),
    ),
  );

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('permission') as $handler) {
    $handler
      ->hook_permission($items);
  }
  return $items;
}

/**
 * Implements hook_og_permission().
 */
function panelizer_og_permission() {
  $items = array();

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('permission') as $handler) {
    if ($handler->entity_type == 'node') {
      $handler
        ->hook_permission($items);
    }
  }
  $final = array(
    'administer panelizer og_group defaults' => array(
      'title' => t('Group: Administer Panelizer default panels, allowed content and settings.'),
      'description' => t('Users with this permission can fully administer panelizer for this entity bundle.'),
    ),
    'administer panelizer og_group overview' => array(
      'title' => t('Group: Administer Panelizer overview.'),
      'description' => t('Allow access to the panelizer overview page for the entity type/bundle. Note: This permission will be required for panelizer tabs to appear on an entity.'),
    ),
  );
  foreach (panelizer_operations() as $path => $operation) {
    $final["administer panelizer og_group {$path}"] = array(
      'title' => t('Group: Administer Panelizer @operation', array(
        '@operation' => $operation['link title'],
      )),
    );
  }
  foreach ($items as $key => $item) {

    // Get node bundle.
    $words = explode(' ', $key);
    $bundle = $words[3];
    if (og_is_group_content_type('node', $bundle)) {
      $final[$key] = $item;
    }
  }
  return $final;
}

/**
 * Implements hook_theme().
 */
function panelizer_theme() {
  $items = array();
  $items['panelizer_settings_page_table'] = array(
    'render element' => 'element',
    'file' => 'includes/admin.inc',
  );
  $items['panelizer_view_mode'] = array(
    'render element' => 'element',
    'template' => 'panelizer-view-mode',
    'path' => drupal_get_path('module', 'panelizer') . '/templates',
  );

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('theme') as $handler) {
    $handler
      ->hook_theme($items);
  }
  return $items;
}

/**
 * Implements hook_menu().
 */
function panelizer_menu() {
  $items = array();

  // Delegate admin menu stuff to admin.inc.
  ctools_include('admin', 'panelizer');
  panelizer_admin_hook_menu($items);

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('menu') as $handler) {
    $handler
      ->hook_menu($items);
  }
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function panelizer_menu_alter(&$items) {

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('menu_alter') as $handler) {
    $handler
      ->hook_menu_alter($items);
  }
}

/**
 * Implements hook_admin_paths().
 */
function panelizer_admin_paths() {
  $items = array();

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('admin_paths') as $handler) {
    $handler
      ->hook_admin_paths($items);
  }
  return $items;
}

/**
 * Implements hook_panels_ipe_access().
 */
function panelizer_panels_ipe_access($display) {

  // We only care about Panels displays from panelizer.
  if (isset($display->context['panelizer'])) {

    // The type array contains 3 elements, where the first is the full context
    // type (ie. 'entity:ENTITY_TYPE'), and the remaining two are the parts
    // separated by ':', so 'entity' and the entity type name.
    $entity_type = $display->context['panelizer']->type[2];
    if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {

      // Deny access to the IPE if the user doesn't have 'update' access to
      // the underlying entity.
      $entity = $display->context['panelizer']->data;
      if (!$handler
        ->entity_access('update', $entity)) {
        return FALSE;
      }

      // Also deny access to the IPE if the user doesn't have either the
      // Administer Panelizer content or Administer Panelizer layout permission
      // for this bundle.
      if (!empty($entity->panelizer_view_mode)) {
        $view_mode = $entity->panelizer_view_mode;
        if (!$handler
          ->panelizer_access("layout", $entity, $view_mode) && !$handler
          ->panelizer_access("content", $entity, $view_mode)) {
          return FALSE;
        }
      }
    }
    return TRUE;
  }
  return NULL;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Check whether a given view mode is panelized. Set an error message if there
 * are un-hidden fields because they won't be printed anyway.
 */
function panelizer_form_field_ui_display_overview_form_alter(&$form, &$form_state, $form_id) {
  if (!empty($form['#entity_type']) && !empty($form['#bundle']) && !empty($form['#view_mode'])) {
    module_load_install('panelizer');
    if (panelizer_view_mode_extra_field_displays($form['#entity_type'], $form['#bundle'], $form['#view_mode'])) {
      $message = t("This view mode is being controlled via Panelizer. For performance reasons, it is recommended to move all fields to 'hidden'. Fields not marked as hidden will be prepared for output but will not actually output, thus needlessly increasing render and page load time.");
      drupal_set_message($message, 'error', FALSE);
    }
  }
}

/**
 * Implements hook_form_alter().
 */
function panelizer_form_alter(&$form, &$form_state, $form_id) {

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('form_alter') as $handler) {
    $handler
      ->hook_form_alter($form, $form_state, $form_id);

    // Support default content and layout settings.
    foreach ($handler->plugin['bundles'] as $bundle_name => $bundle) {
      if ($form_id == 'panels_common_settings' && $form_state['build_info']['args'][0] == 'panelizer_' . $handler->entity_type . ':' . $bundle_name) {

        // Provide settings for the default content and layout options.
        $form['default_settings'] = array(
          '#type' => 'fieldset',
          '#title' => t('Default settings'),
          '#group' => 'additional_settings',
          '#weight' => -20,
        );
        $form['default_settings']['default_content_settings'] = array(
          '#title' => t('Use the same allowed content as standard Panels pages?'),
          '#type' => 'checkbox',
          '#default_value' => variable_get($form_state['build_info']['args'][0] . '_allowed_types_default', FALSE),
        );
        $form['default_settings']['default_layout_settings'] = array(
          '#title' => t('Use the same allowed  layouts as standard Panels pages?'),
          '#type' => 'checkbox',
          '#default_value' => variable_get($form_state['build_info']['args'][0] . '_allowed_layouts_default', FALSE),
        );

        // Disable the layout options when the default layout setting is enabled
        if (!empty($form['layout_selection']['layouts']) && variable_get($form_state['build_info']['args'][0] . '_allowed_layouts_default', FALSE)) {
          $form['layout_selection']['layouts']['#disabled'] = TRUE;
        }

        // Disable the content options when the default content setting is
        // enabled.
        if (variable_get($form_state['build_info']['args'][0] . '_allowed_types_default', FALSE)) {
          $content_types = ctools_content_get_all_types();
          $content_types['other'] = array(
            'title' => t('Other'),
            'weight' => 10,
          );
          foreach ($content_types as $content_type => $content_type_value) {
            if (!empty($form['content_types'][$content_type]['options'])) {
              $form['content_types'][$content_type]['options']['#disabled'] = TRUE;
            }
          }
          $form['common']['panels_common_default']['#disabled'] = TRUE;
        }
        $form['#submit'][] = 'panelizer_panels_default_settings_submit';
      }
    }
  }
}

/**
 * Custom submission handler for setting default content and layout settings.
 */
function panelizer_panels_default_settings_submit($form, &$form_state) {
  variable_set($form_state['values']['module_name'] . '_allowed_types_default', $form_state['values']['default_content_settings']);
  variable_set($form_state['values']['module_name'] . '_allowed_layouts_default', $form_state['values']['default_layout_settings']);
}

/**
 * Implements hook_process_page().
 */
function panelizer_process_page(&$variables) {

  // Delegate.
  // Target the theme layer to ensure we can manipulate the overview table.
  foreach (panelizer_get_plugins_with_hook('page_alter') as $handler) {
    $handler
      ->hook_page_alter($variables['page']);
  }
}

/**
 * Implements hook_entity_load().
 */
function panelizer_entity_load(&$entities, $entity_type) {

  // Delegate to the handler.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $handler
      ->hook_entity_load($entities);
  }
}

/**
 * Implements hook_entity_update().
 */
function panelizer_entity_update($entity, $entity_type) {

  // Delegate to the handler.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $handler
      ->hook_entity_update($entity);
  }
}

/**
 * Implements hook_entity_insert().
 */
function panelizer_entity_insert($entity, $entity_type) {

  // Delegate to the handler.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $handler
      ->hook_entity_insert($entity);
  }
}

/**
 * Implements hook_entity_delete().
 */
function panelizer_entity_delete($entity, $entity_type) {

  // Delegate to the handler.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $handler
      ->hook_entity_delete($entity);
  }
}

/**
 * Implements hook_field_attach_delete_revision().
 */
function panelizer_field_attach_delete_revision($entity_type, $entity) {

  // Delegate to the handler.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $handler
      ->hook_field_attach_delete_revision($entity);
  }
}
function panelizer_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {

  // Delegate to the handler.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $handler
      ->hook_field_attach_form($entity, $form, $form_state, $langcode);
  }
}
function panelizer_field_attach_submit($entity_type, $entity, &$form, &$form_state) {

  // Delegate to the handler.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $handler
      ->hook_field_attach_submit($entity, $form, $form_state);
  }
}

/**
 * Implements hook_entity_view_alter().
 */
function panelizer_entity_view_alter(&$build, $entity_type) {
  static $recursion_prevention = array();

  // Prepare variables.
  $handler = panelizer_entity_plugin_get_handler($entity_type);
  if (!$handler) {
    return;
  }
  $entity = $handler
    ->get_entity_view_entity($build);

  // Safety check in case the entity can't be loaded.
  if (!$entity) {
    return;
  }
  list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);

  // If the requested view mode does not exist, check if a substitute is
  // assigned, otherwise rendering will fall back to 'default' and we should
  // check that one instead.
  $view_mode = $handler
    ->get_view_mode($build['#view_mode'], $bundle);

  // Make sure the bundle + view mode is actually panelized!
  if (!$handler
    ->is_panelized($bundle . '.' . $view_mode)) {
    return;
  }

  // Also verify that the configuration exists. This may happen if a display is
  // improperly configured.
  if (empty($entity->panelizer[$view_mode])) {
    return;
  }
  if (!empty($recursion_prevention[$entity_type][$entity_id][$view_mode])) {
    return;
  }
  $recursion_prevention[$entity_type][$entity_id][$view_mode] = TRUE;
  if ($info = $handler
    ->render_entity($entity, $view_mode)) {

    // Change theming function and add the content on the $build array.
    $build['#theme'] = 'panelizer_view_mode';
    $build['#panelizer'] = $entity->panelizer[$view_mode];
    $build['#panelizer_content'] = $info;
    $build['#panelizer_handler'] = $handler;
    $build['#panelizer_entity'] = $entity;
    $build['#panelizer_bundle'] = $bundle;
    $build['#panelizer_entity_id'] = $entity_id;
  }
  $recursion_prevention[$entity_type][$entity_id][$view_mode] = FALSE;
}

/**
 * Implements hook_node_update_index().
 */
function panelizer_node_update_index($node) {

  // Populate search index for nodes managed via Panelizer if 'search_index'
  // view mode is configured to do so.
  if (($handler = panelizer_entity_plugin_get_handler('node')) && ($view_mode = $handler
    ->get_view_mode('search_index', $node->type)) && $handler
    ->is_panelized($node->type . '.' . $view_mode) && !empty($node->panelizer[$view_mode]) && ($info = $handler
    ->render_entity($node, $view_mode))) {
    $build['#view_mode'] = $view_mode;
    $build['#theme'] = 'panelizer_view_mode';
    $build['#panelizer'] = $node->panelizer[$view_mode];
    $build['#panelizer_content'] = $info;
    $build['#panelizer_handler'] = $handler;
    $build['#panelizer_entity'] = $node;
    $build['#panelizer_bundle'] = $node->type;
    $build['#panelizer_entity_id'] = $node->nid;
    return drupal_render($build);
  }
}

/**
 * Implements hook_node_search_result().
 */
function panelizer_node_search_result($node) {

  // Populate search result highlighting input for nodes managed via Panelizer
  // if 'search_result' view mode is configured to do so.
  if (($handler = panelizer_entity_plugin_get_handler('node')) && ($view_mode = $handler
    ->get_view_mode('search_result', $node->type)) && $handler
    ->is_panelized($node->type . '.' . $view_mode) && !empty($node->panelizer[$view_mode]) && ($info = $handler
    ->render_entity($node, $view_mode))) {
    $build['#view_mode'] = $view_mode;
    $build['#theme'] = 'panelizer_view_mode';
    $build['#panelizer'] = $node->panelizer[$view_mode];
    $build['#panelizer_content'] = $info;
    $build['#panelizer_handler'] = $handler;
    $build['#panelizer_entity'] = $node;
    $build['#panelizer_bundle'] = $node->type;
    $build['#panelizer_entity_id'] = $node->nid;
    $node->rendered .= drupal_render($build);
  }
}

// -----------------------------------------------------------------------
// Panels and CTools hooks

/**
 * Implements hook_ctools_plugin_type().
 */
function panelizer_ctools_plugin_type() {
  $items['entity'] = array(
    'cache' => FALSE,
    'process' => array(
      'function' => 'panelizer_entity_plugin_process',
    ),
    'classes' => array(
      'handler',
    ),
  );
  return $items;
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function panelizer_ctools_plugin_directory($module, $plugin) {
  if (in_array($module, array(
    'panelizer',
    'ctools',
    'page_manager',
  ))) {
    return 'plugins/' . $plugin;
  }
  if ($module == 'panels' && $plugin == 'panels_storage') {
    return 'plugins/' . $plugin;
  }
}

/**
 * Implements hook_ctools_plugin_api().
 */
function panelizer_ctools_plugin_api($module, $api) {
  if ($module == 'page_manager' && $api == 'pages_default' || $module == 'panelizer') {
    return array(
      'version' => 1,
      'path' => drupal_get_path('module', 'panelizer') . '/includes',
    );
  }
}

/**
 * Implements hook_features_api().
 */
function panelizer_features_api() {
  $api = array();
  if (function_exists('_ctools_features_get_info') && defined('FEATURES_ALTER_TYPE_NONE')) {
    $api['panelizer_defaults'] = _ctools_features_get_info('panelizer_defaults');

    // CTools calls our hook_panelizer_defaults_alter so prevent Features from
    // calling it too. FEATURES_ALTER_TYPE_INLINE means we are handling alter
    // hooks ourselves here.
    $api['panelizer_defaults']['alter_type'] = FEATURES_ALTER_TYPE_INLINE;

    // Provide a separate alter hook for features_override.
    $api['panelizer_defaults']['alter_hook'] = 'panelizer_defaults_override';
  }
  return $api;
}

/**
 * Implementation of hook_views_api().
 */
function panelizer_views_api() {
  return array(
    'api' => 2.0,
    'path' => drupal_get_path('module', 'panelizer') . '/plugins/views',
  );
}

/**
 * Implements hook_panelizer_defaults_alter().
 */
function panelizer_panelizer_defaults_alter(&$items) {

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('panelizer_defaults') as $handler) {
    $handler
      ->hook_panelizer_defaults($items);
  }

  // If a default Panels display has no storage type, set it.
  foreach ($items as &$panelizer) {
    $display =& $panelizer->display;
    if (empty($display->storage_type)) {
      $display->storage_type = 'panelizer_default';
      $display->storage_id = $panelizer->name;
    }
  }

  // Allow features_overrides to alter the config.
  drupal_alter('panelizer_defaults_override', $items);
}

/**
 * Implements hook_default_page_manager_handlers().
 */
function panelizer_default_page_manager_handlers() {
  $items = array();

  // Delegate.
  foreach (panelizer_get_plugins_with_hook('default_page_manager_handlers') as $handler) {
    $handler
      ->hook_default_page_manager_handlers($items);
  }
  return $items;
}

/**
 * Implement CTools access form caching callback: get.
 */
function panelizer_ctools_access_get($argument) {
  list($entity_type, $bundle, $name) = explode(':', $argument);
  $handler = panelizer_entity_plugin_get_handler($entity_type);
  $panelizer = $handler
    ->get_default_panelizer_object($bundle, $name);
  if (empty($panelizer)) {
    return;
  }
  if (!$handler
    ->access_default_panelizer_object($panelizer)) {
    return;
  }

  // First, see if there's a cache.
  ctools_include('object-cache');
  $access = ctools_object_cache_get('panelizer_access', $argument);
  if (!$access) {
    $access = $panelizer->access;
  }
  $context = $handler
    ->get_contexts($panelizer);
  return array(
    $access,
    $context,
  );
}

/**
 * Implement CTools access form caching callback: set.
 */
function panelizer_ctools_access_set($argument, $access) {
  list($entity_type, $bundle, $name) = explode(':', $argument);
  $handler = panelizer_entity_plugin_get_handler($entity_type);
  $panelizer = $handler
    ->get_default_panelizer_object($bundle, $name);
  if (empty($panelizer)) {
    return;
  }
  if (!$handler
    ->access_default_panelizer_object($panelizer)) {
    return;
  }
  ctools_include('object-cache');
  ctools_object_cache_set('panelizer_access', $argument, $access);
}

/**
 * Implement CTools access form caching callback: get.
 */
function panelizer_ctools_access_clear($argument) {
  list($entity_type, $bundle, $name) = explode(':', $argument);
  $handler = panelizer_entity_plugin_get_handler($entity_type);
  $panelizer = $handler
    ->get_default_panelizer_object($bundle, $name);
  if (empty($panelizer)) {
    return;
  }
  if (!$handler
    ->access_default_panelizer_object($panelizer)) {
    return;
  }
  ctools_include('object-cache');
  ctools_object_cache_clear('panelizer', $argument);
}

// -----------------------------------------------------------------------
// CTools entity plugin support code.

/**
 * CTools process callback for an entity plugin.
 *
 * This adds configuration data to the plugin so that we know what bundles it is
 * enabled for.
 */
function panelizer_entity_plugin_process(&$plugin, $info) {
  $entity_type = $plugin['name'];
  $entity_info = entity_get_info($entity_type);
  $plugin['bundles'] = array();
  if ($entity_info) {
    foreach ($entity_info['bundles'] as $bundle => $label) {
      if ($settings = variable_get('panelizer_defaults_' . $entity_type . '_' . $bundle, array())) {

        // Translate from settings that existed prior to view mode support.
        if (empty($settings['view modes'])) {
          $old_settings = $settings;
          $settings = array(
            'view modes' => array(),
          );
          if (empty($plugin['uses page manager'])) {
            $settings['view modes']['default'] = $old_settings;
          }
          else {
            $settings['view modes']['page_manager'] = $old_settings;
          }
          $settings['status'] = $old_settings['status'];
        }
        $plugin['bundles'][$bundle] = $settings;

        // Build the custom settings of the view modes for this bundle.
        $view_mode_settings = field_view_mode_settings($entity_type, $bundle);
        foreach ($entity_info['view modes'] as $view_mode_name => $view_mode_info) {
          $plugin['view mode status'][$bundle][$view_mode_name] = !empty($view_mode_settings[$view_mode_name]['custom_settings']);
        }
      }
    }

    // Add our fake view modes.
    $plugin['view modes'] = array(
      'page_manager' => array(
        'label' => t('Full page override'),
      ),
      'default' => array(
        'label' => t('Default'),
      ),
    );
    if (!empty($entity_info['view modes'])) {
      foreach ($entity_info['view modes'] as $view_mode => $view_mode_info) {
        $plugin['view modes'][$view_mode] = $view_mode_info;
      }
    }

    // It seems silly to unset this after but the logic is cleaner to read.
    if (empty($plugin['uses page manager'])) {
      unset($plugin['view modes']['page_manager']);
    }
  }
  drupal_alter('panelizer_entity_plugin_process', $plugin, $info);
}

/**
 * Fetch a single entity plugin.
 */
function panelizer_get_entity_plugin($entity_type) {
  ctools_include('plugins');
  return ctools_get_plugins('panelizer', 'entity', $entity_type);
}

/**
 * Fetch all entity plugin.
 */
function panelizer_get_entity_plugins() {
  ctools_include('plugins');
  return ctools_get_plugins('panelizer', 'entity');
}

/**
 * Get the class to handle custom code for a given entity type plugin.
 *
 * If a plugin does not define a class at all, then the default class.
 *
 * @return
 *   Either the instantiated handler or FALSE if one could not be had.
 */
function panelizer_entity_plugin_get_handler($plugin) {

  // The default plugin handler is abstract and cannot be loaded.
  if ($plugin == 'default') {
    return;
  }
  $cache =& drupal_static(__FUNCTION__, array());

  // If a string was passed, turn it into a plugin.
  if (is_string($plugin)) {
    $plugin = panelizer_get_entity_plugin($plugin);
    if (!$plugin) {
      return FALSE;
    }
  }

  // Get the class name from the 'handler' property if we have not already
  // cached a handler.
  if (empty($cache[$plugin['name']]) && ($class = ctools_plugin_get_class($plugin, 'handler'))) {

    // @todo is there a good reason to use ->init instead of __construct?
    $cache[$plugin['name']] = new $class();
    $cache[$plugin['name']]
      ->init($plugin);
  }
  return !empty($cache[$plugin['name']]) ? $cache[$plugin['name']] : FALSE;
}

/**
 * Load handler to get a plugin as a menu callback.
 */
function panelizer_handler_load($entity_type) {
  return panelizer_entity_plugin_get_handler($entity_type);
}

/**
 * Fetch handler objects for all plugins that implement the named hook.
 *
 * These plugins must set $plugin['hooks'][$hook] = TRUE in order to be
 * instantiated.
 *
 * This is only called for system wide hooks such as hook_menu and
 * hook_menu_alter; entity specific hooks will always be called.
 */
function panelizer_get_plugins_with_hook($hook) {
  $objects = array();
  $plugins = panelizer_get_entity_plugins();
  foreach ($plugins as $entity_type => $plugin) {
    if (!empty($plugin['hooks'][$hook])) {
      if ($handler = panelizer_entity_plugin_get_handler($plugin)) {
        $objects[$entity_type] = $handler;
      }
    }
  }
  return $objects;
}

/**
 * Page callback for entity menu callbacks.
 *
 * This function is to be used as a menu callback for menu items that are to be
 * handled by a method on the handler object. It loads the object defined in the
 * plugin and hands it off to a method based upon the name of the operation in
 * use.
 *
 * For example, if the 'op' is 'revision' then the callback method will be
 * 'page_revisions', with all of the arguments *except* the $op and the plugin
 * name.
 */
function panelizer_entity_plugin_switcher_page($entity_type, $op) {
  $args = func_get_args();

  // Load the $plugin information.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {

    // Replace the first two arguments.
    $args[0] = !empty($_REQUEST['js']);
    $args[1] = $_POST;
    if (empty($args[3])) {
      $args[3] = 'page_manager';
    }
    $method = 'page_' . $op;
    if (method_exists($handler, $method)) {
      return call_user_func_array(array(
        $handler,
        $method,
      ), $args);
    }

    // Check to see if this is an operation from panelizer_operations with a
    // callback instead.
    $operations = panelizer_operations();
    if (isset($operations[$op]) && isset($operations[$op]['entity callback']) && function_exists($operations[$op]['entity callback'])) {
      array_unshift($args, $handler);
      return call_user_func_array($operations[$op]['entity callback'], $args);
    }
  }
  else {
    return t('Configuration error. No handler found.');
  }
}

/**
 * Callback used for switching callbacks into the proper plugin.
 */
function panelizer_entity_plugin_callback_switcher($entity_type, $switcher_type, $op) {
  $args = func_get_args();
  if (count($args) < 3) {
    return FALSE;
  }
  $entity_type = array_shift($args);
  $switcher_type = array_shift($args);
  $op = array_shift($args);

  // Load the $plugin information.
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    $method = $switcher_type . '_' . $op;
    if (method_exists($handler, $method)) {
      return call_user_func_array(array(
        $handler,
        $method,
      ), $args);
    }
  }
  else {
    return FALSE;
  }
}

/**
 * Page callback to delegate to either the settings page or list page.
 */
function panelizer_default_list_or_settings_page($handler, $bundle, $name, $view_mode, $operation = 'list', $item = NULL) {
  if (is_string($handler)) {
    $handler = panelizer_entity_plugin_get_handler($handler);
  }

  // We need a version of $bundle with $view_mode but we need to retain one
  // without it so we can pass straight $bundle to the settings page.
  $test_bundle = $bundle;
  if ($view_mode) {
    $test_bundle .= '.' . $view_mode;
  }
  if ($handler
    ->has_panel_choice($test_bundle)) {

    // Call through to the UI switcher for the list.
    ctools_include('export-ui');
    return panelizer_export_ui_switcher_page($handler, $test_bundle, 'panelizer_defaults', $operation, $item);
  }
  else {
    return panelizer_default_settings_page($handler, $bundle, $name, $view_mode);
  }
}

/**
 * Specialized version of ctools_export_ui_switcher_page().
 *
 * This one is designed to set our entity handler and bundle on the object so we
 * can refer to it later without having to override all of the entry points.
 */
function panelizer_export_ui_switcher_page($entity_handler, $bundle, $plugin_name, $op) {
  $args = func_get_args();

  // Remove the handler and the bundle.
  array_shift($args);
  array_shift($args);
  $js = !empty($_REQUEST['js']);

  // Break our bundle up as necessary.
  if (strpos($bundle, '.') !== FALSE) {
    list($bundle, $view_mode) = explode('.', $bundle);
  }
  else {
    $view_mode = 'page_manager';
  }

  // Load the $plugin information.
  ctools_include('export-ui');
  $plugin = ctools_get_export_ui($plugin_name);
  $handler = ctools_export_ui_get_handler($plugin);
  if ($handler) {
    if (is_string($entity_handler)) {
      $entity_handler = panelizer_entity_plugin_get_handler($entity_handler);
    }
    $handler->entity_handler = $entity_handler;
    $handler->entity_bundle = $bundle;
    $handler->entity_view_mode = $view_mode;
    if (empty($entity_handler->entity_admin_root) || substr($_GET['q'], 30) == 'admin/structure/panelizer') {
      $handler->plugin['menu']['menu prefix'] = 'admin/structure/panelizer' . $entity_handler->entity_type;
      $handler->plugin['menu']['menu item'] = $bundle;
    }
    else {
      $base_path = $entity_handler->entity_admin_root . '/panelizer/' . $view_mode;
      if (is_numeric($entity_handler->entity_admin_bundle)) {
        $bits = explode('/', $base_path);
        $bits[$entity_handler->entity_admin_bundle] = $bundle;
        $base_path = implode('/', $bits);
      }
      $handler->plugin['menu']['menu prefix'] = dirname($base_path);
      $handler->plugin['menu']['menu item'] = basename($base_path);
      foreach ($handler->plugin['menu']['items'] as $key => &$item) {
        $item['path'] = str_replace('list/', '', $item['path']);
      }
    }
    $path = $handler->plugin['menu']['menu prefix'] . '/' . $handler->plugin['menu']['menu item'];
    foreach ($handler->plugin['redirect'] as $key => $old_path) {
      if ($key == 'add') {
        $handler->plugin['redirect'][$key] = $path . '/%ctools_export_ui/settings';
      }
      else {
        $handler->plugin['redirect'][$key] = $path;
      }
    }
    $method = $op . '_page';
    if (method_exists($handler, $method)) {

      // Replace the first two arguments.
      $args[0] = $js;
      $args[1] = $_POST;
      return call_user_func_array(array(
        $handler,
        $method,
      ), $args);
    }
  }
  else {
    return t('Configuration error. No handler found.');
  }
}

// ---------------------------------------------------------------------------
// Menu callbacks

/**
 * Title callback to properly set the tile when editing panelizer defaults.
 */
function panelizer_default_title_callback($handler, $bundle) {
  if (is_string($handler)) {
    $handler = panelizer_entity_plugin_get_handler($handler);
  }
  if (!$handler) {
    return '';
  }
  $entity_info = entity_get_info($handler->entity_type);
  $title = $entity_info['label'];
  if (strpos($bundle, '.') === FALSE) {
    $bundle = $bundle;
    $view_mode = '';
  }
  else {
    list($bundle, $view_mode) = explode('.', $bundle);
  }
  $title .= ' | ' . $handler
    ->get_bundle_title($bundle);
  if ($view_mode && !empty($handler->plugin['view modes'][$view_mode]['label'])) {
    $title .= ' | ' . $handler->plugin['view modes'][$view_mode]['label'];
  }
  return $title;
}

/**
 * Menu callback to determine if a type has a choice of defaults.
 *
 * We use this to make sure the right tabs appear.
 */
function panelizer_has_choice_callback($handler, $bundle, $name = NULL) {
  if (is_string($handler)) {
    $handler = panelizer_entity_plugin_get_handler($handler);
  }
  if (empty($handler)) {
    return FALSE;
  }
  if (!panelizer_administer_entity_bundle($handler, $bundle)) {
    return FALSE;
  }

  // Check to see if $name is valid.
  if (!empty($name) && !$handler
    ->get_default_panelizer_object($bundle, $name)) {
    return FALSE;
  }
  return $handler
    ->has_panel_choice($bundle);
}

/**
 * Menu callback to determine if a type+view_mode has a choice of defaults.
 */
function panelizer_has_choice_callback_view_mode($handler, $bundle, $view_mode) {
  return panelizer_has_choice_callback($handler, $bundle . '.' . $view_mode);
}

/**
 * Menu callback to determine if a type has a choice of defaults.
 *
 * Use to make sure the right tabs appear.
 */
function panelizer_has_no_choice_callback($handler, $bundle, $view_mode = NULL) {
  if (is_string($handler)) {
    $handler = panelizer_entity_plugin_get_handler($handler);
  }
  if (empty($handler)) {
    return FALSE;
  }
  if (!empty($view_mode)) {
    $bundle .= '.' . $view_mode;
  }
  if (!panelizer_administer_entity_bundle($handler, $bundle)) {
    return FALSE;
  }
  return !$handler
    ->has_panel_choice($bundle);
}

/**
 * Menu callback to determine if a type has a choice of defaults.
 *
 * Used to make sure the right tabs appear.
 */
function panelizer_is_panelized($handler, $bundle, $view_mode = NULL) {
  if (is_string($handler)) {
    $handler = panelizer_entity_plugin_get_handler($handler);
  }
  if (!$handler) {
    return FALSE;
  }
  if ($view_mode) {
    $bundle .= '.' . $view_mode;
  }
  if (!panelizer_administer_entity_bundle($handler, $bundle)) {
    return FALSE;
  }
  return $handler
    ->is_panelized($bundle);
}

/**
 * Access callback to see if a user can administer a particular bundle.
 */
function panelizer_administer_entity_bundle($handler, $bundle) {
  if (is_string($handler)) {
    $handler = panelizer_entity_plugin_get_handler($handler);
  }

  // Adjust for the presence of a view mode.
  if (strpos($bundle, '.') !== FALSE) {
    list($bundle, $view_mode) = explode('.', $bundle);
  }
  return user_access('administer panelizer') || user_access("administer panelizer {$handler->entity_type} {$bundle} defaults");
}

/**
 * Access callback to see if a user can administer a particular default.
 */
function panelizer_administer_panelizer_default($handler, $bundle, $name, $view_mode = NULL) {
  if (is_string($handler)) {
    $handler = panelizer_entity_plugin_get_handler($handler);
  }
  if ($view_mode) {
    $bundle .= '.' . $view_mode;
  }
  $panelizer = $handler
    ->get_default_panelizer_object($bundle, $name);
  if (!$panelizer) {
    return FALSE;
  }
  return $handler
    ->access_default_panelizer_object($panelizer);
}

/**
 * Menu load callback to scrub a node bundle from the URL safe equivalent.
 */
function panelizer_node_type_load($name) {
  if ($type = node_type_get_type(strtr($name, array(
    '-' => '_',
  )))) {
    return $type->type;
  }
}

// ---------------------------------------------------------------------------
// export.inc callbacks to handle proper in/out of our defaults.

/**
 * export.inc callback to properly save a panelizer default.
 */
function panelizer_export_save_callback(&$object) {
  if (!empty($object->display)) {
    $object->display->storage_id = $object->name;

    // First write the display.
    panels_save_display($object->display);

    // Make sure we have the did.
    $object->did = $object->display->did;
  }

  // Then write the default
  if ($object->export_type & EXPORT_IN_DATABASE) {

    // Existing record.
    $update = array(
      'pnid',
    );
  }
  else {

    // New record.
    $update = array();
    $object->export_type = EXPORT_IN_DATABASE;
  }

  // Reset the entity's cache. If the EntityCache module is enabled, this also
  // resets its permanent cache.
  list($entity_type, ) = explode(':', $object->name);
  entity_get_controller($entity_type)
    ->resetCache();
  return drupal_write_record('panelizer_defaults', $object, $update);
}

/**
 * export.inc callback to properly export a panelizer default.
 */
function panelizer_export_export_callback($object, $indent) {
  $object->did = NULL;
  $output = ctools_export_object('panelizer_defaults', $object, $indent);
  $output .= panels_export_display($object->display, $indent);
  $output .= $indent . '$panelizer->display = $display;' . "\n";
  return $output;
}

/**
 * export.inc callback to properly delete a panelizer default.
 */
function panelizer_export_delete_callback($object) {
  if (!empty($object->did)) {
    panels_delete_display($object->did);
  }

  // Allow modules to react on a default Panelizer object before deletion.
  // Triggers hook_panelizer_delete_default().
  module_invoke_all('panelizer_delete_default', $object);
  db_delete('panelizer_defaults')
    ->condition('name', $object->name)
    ->execute();
}

/**
 * export.inc callback to delete sub records for an object.
 */
function panelizer_export_delete_callback_subrecords($objects) {
  $dids = array();
  foreach ($objects as $panelizer) {
    if (!empty($panelizer->did)) {
      $dids[$panelizer->did] = $panelizer->did;
    }
  }
  if ($dids) {
    $displays = panels_load_displays($dids);
    foreach ($objects as $panelizer) {
      if (!empty($panelizer->did) && !empty($displays[$panelizer->did])) {
        $panelizer->display = $displays[$panelizer->did];
      }
    }
  }
}

// ---------------------------------------------------------------------------
// Context cache callbacks -- this really needs a less lame system someday.

/**
 * Fetch the panelizer object from the object cache.
 *
 * CTools clumsy context editing system requires caching. This lets us do it
 * reasonably.
 *
 * @param $entity_type
 *   Can be something like 'node' or 'user' or 'default'.
 * @param $key
 *   Depends on the $entity_type. Can be a nid, a uid or a default key.
 */
function panelizer_context_cache_get($entity_type, $key) {
  ctools_include('object-cache');
  $cache = ctools_object_cache_get('panelizer_context_cache', $entity_type . ':' . $key);
  if (!empty($cache)) {
    $cache->cached = TRUE;
    return $cache;
  }
  if ($entity_type == 'default') {
    list($entity_type, $bundle, $name) = @explode(':', $key, 3);
    $get_default = TRUE;
  }
  if ($handler = panelizer_entity_plugin_get_handler($entity_type)) {
    if (!empty($get_default)) {
      $panelizer = $handler
        ->get_default_panelizer_object($bundle, $name);
      $panelizer->base_contexts = $handler
        ->get_base_contexts();
      return $panelizer;
    }
    else {
      list($entity_id, $view_mode) = explode('.', $key);
      $entities = entity_load($entity_type, array(
        $entity_id,
      ));
      if (!empty($entities[$entity_id]) && !empty($entities[$entity_id]->panelizer[$view_mode])) {
        $panelizer = $entities[$entity_id]->panelizer[$view_mode];
        $panelizer->base_contexts = $handler
          ->get_base_contexts($entities[$entity_id]);
        return $panelizer;
      }
    }
  }
}

/**
 * Store the panelizer object in the object cache.
 *
 * CTools clumsy context editing system requires caching. This lets us do it
 * reasonably.
 *
 * @param $entity_type
 *   Can be something like 'node' or 'user' or 'default'.
 * @param $key
 *   Either the node type or the nid.
 * @param $object
 *   The cached object.
 */
function panelizer_context_cache_set($entity_type, $key, $object) {
  ctools_include('object-cache');
  ctools_object_cache_set('panelizer_context_cache', $entity_type . ':' . $key, $object);
}

/**
 * Clear the panelizer object in the object cache.
 *
 * CTools clumsy context editing system requires caching. This lets us do it
 * reasonably.
 *
 * @param $entity_type
 *   Can be something like 'node' or 'user' or 'default'.
 * @param $key
 *   Either the node type or the nid.
 */
function panelizer_context_cache_clear($entity_type, $key) {
  ctools_include('object-cache');
  ctools_object_cache_clear('panelizer_context_cache', $entity_type . ':' . $key);
}

// --------------------------------------------------------------------------
// Panels edit cache contexts.

/**
 * Implements hook_panels_cache_get().
 *
 * Get display edit cache for a display being edited.
 *
 * The key is the second half of the key in this form: panelizer:TYPE:KEY
 */
function panelizer_panels_cache_get($argument) {
  ctools_include('object-cache');
  list($entity_type, $key) = explode(':', $argument, 2);
  $cache = ctools_object_cache_get('panelizer_display_cache', $entity_type . ':' . $key);

  // Keep $type because $entity_type can be 'default' which is not actually an
  // entity type in that case.
  $type = $entity_type;
  if ($entity_type == 'default') {
    list($entity_type, $bundle, $name) = @explode(':', $key, 3);
    $get_default = TRUE;
  }
  $handler = panelizer_entity_plugin_get_handler($entity_type);
  if (!$handler) {
    return;
  }
  $entity_info = entity_get_info($entity_type);
  $revision_key = isset($entity_info['entity keys']['revision']) ? $entity_info['entity keys']['revision'] : 'vid';

  // Extract the entity ID and view mode.
  list($entity_id, $view_mode) = explode(':', $key, 2);

  // If this entity supports revisions, and a custom revision is being
  // displayed, it's possible for the key to also include the revision ID.
  if ($handler->supports_revisions && strpos($view_mode, ':') !== FALSE) {
    list($entity_id, $view_mode, $vid) = explode(':', $key);
  }

  // If it's already cached, we still need to restore our contexts.
  if (!empty($cache)) {
    $cache->cached = TRUE;
    if (!empty($get_default)) {
      $panelizer = $handler
        ->get_default_panelizer_object($bundle, $name);
      $cache->display->context = $handler
        ->get_contexts($panelizer);

      // Set the storage_type and storage_id if either is empty.
      if (empty($cache->display->storage_type)) {
        $cache->display->storage_type = 'panelizer_default';
      }
      if (empty($cache->display->storage_id)) {
        $cache->display->storage_id = $panelizer->name;
      }
    }
    else {
      $conditions = isset($vid) ? array(
        $revision_key => $vid,
      ) : array();
      $entities = entity_load($entity_type, array(
        $entity_id,
      ), $conditions);
      if (!empty($entities[$entity_id]) && !empty($entities[$entity_id]->panelizer[$view_mode])) {
        $panelizer = $entities[$entity_id]->panelizer[$view_mode];
        $cache->display->context = $handler
          ->get_contexts($panelizer, $entities[$entity_id]);
      }
    }
    return $cache;
  }
  $cache = new stdClass();

  // If it wasn't cached, create a new cache.
  if (!empty($get_default)) {
    $panelizer = $handler
      ->get_default_panelizer_object($bundle, $name);
    $cache->display = $panelizer->display;
    $cache->display->context = $handler
      ->get_contexts($panelizer);

    // Set the storage_type and storage_id if either is empty.
    if (empty($cache->display->storage_type)) {
      $cache->display->storage_type = 'panelizer_default';
    }
    if (empty($cache->display->storage_id)) {
      $cache->display->storage_id = $panelizer->name;
    }
  }
  else {
    $conditions = isset($vid) ? array(
      $revision_key => $vid,
    ) : array();
    $entities = entity_load($entity_type, array(
      $entity_id,
    ), $conditions);
    if (empty($entities[$entity_id]) || empty($entities[$entity_id]->panelizer[$view_mode])) {
      return $cache;
    }
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entities[$entity_id]);
    $panelizer = $entities[$entity_id]->panelizer[$view_mode];
    $cache->display = $panelizer->display;
    $cache->display->context = $handler
      ->get_contexts($panelizer, $entities[$entity_id]);
  }
  ctools_include('common', 'panels');
  ctools_include('plugins', 'panels');
  $cache_keys = array(
    'panelizer',
    $type,
    $key,
  );
  if (isset($vid) && is_numeric($vid) && substr($key, strlen($vid) * -1) != $vid) {
    $cache_keys[] = $vid;
  }
  $cache->display->cache_key = implode(':', $cache_keys);

  // Set the allowed content types.
  if (variable_get('panelizer_' . $type . ':' . $bundle . '_allowed_types_default', FALSE)) {
    $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
  }
  else {
    $cache->content_types = panels_common_get_allowed_types('panelizer_' . $type . ':' . $bundle, $cache->display->context);
  }

  // Set the allowed layout options.
  $cache->display->allowed_layouts = panels_common_get_allowed_layouts(panelizer_get_allowed_layouts_option($type, $bundle));
  return $cache;
}

/**
 * Implements hook_panels_cache_set().
 *
 * Store a display edit in progress in the page cache.
 */
function panelizer_panels_cache_set($argument, $cache) {
  ctools_include('object-cache');
  ctools_object_cache_set('panelizer_display_cache', $argument, $cache);
}

/**
 * Implements hook_panels_cache_clear().
 *
 * Save all changes made to a display using the Page Manager page cache.
 */
function panelizer_panels_cache_clear($argument, $cache) {
  ctools_include('object-cache');
  ctools_object_cache_clear('panelizer_display_cache', $argument);
}

/**
 * Implements hook_panels_cache_save().
 *
 * Save all changes made to a display using the Page Manager page cache.
 */
function panelizer_panels_cache_save($argument, $cache) {

  // If this is set, they clicked a button that saves a different panelizer
  // than was being edited, such as saving to default rather than customizing
  // an entity.
  $original = $argument;
  if (isset($cache->display->swap_cache_key)) {
    $argument = $cache->display->swap_cache_key;
  }
  list($entity_type, $key) = explode(':', $argument, 2);
  $type = $entity_type;
  if ($entity_type == 'default') {
    list($entity_type, $bundle, $name) = @explode(':', $key, 3);
    $get_default = TRUE;
  }
  $handler = panelizer_entity_plugin_get_handler($entity_type);
  if (!$handler) {
    return;
  }
  if (!empty($get_default)) {
    $panelizer = $handler
      ->get_default_panelizer_object($bundle, $name);
    $panelizer->display = $cache->display;
    ctools_include('export');
    ctools_export_crud_save('panelizer_defaults', $panelizer);
  }
  else {
    list($entity_id, $view_mode, $vid) = explode(':', $key);
    $entity = $cache->display->context['panelizer']->data;
    if ($entity->panelizer[$view_mode]) {
      $entity->panelizer[$view_mode]->display = $cache->display;
      $entity->panelizer[$view_mode]->display_is_modified = TRUE;
      $handler
        ->entity_save($entity);

      // The display may have been cloned in the save process, so we need
      // to be sure to put the old display back, and its contexts.
      $cache->display = $entity->panelizer[$view_mode]->display;
      $cache->display->context = $handler
        ->get_contexts($entity->panelizer[$view_mode], $entity);
    }
  }
  panelizer_panels_cache_clear($original, $cache);
}

// ---------------------------------------------------------------------------
// Contrib module hooks to provide needed functionality.

/**
 * Implements hook_export_node_alter().
 *
 * Integrate with export.module for saving panel_nodes into code.
 */
function panelizer_export_node_alter(&$node, $original_node, $method) {

  // @todo
}

/**
 * Implements hook_panelizer_defaults_alter().
 *
 * Remove the panels node because there is no point to panelizing it.
 */
function panelizer_panelizer_default_types_alter(&$bundles, $entity_type) {
  switch ($entity_type) {
    case 'node':

      // Disallow the panel node type, since it's already a panel.
      if (module_exists('panels_node') && !empty($bundles['panel'])) {
        unset($bundles['panel']);
      }
      break;
  }
}

/**
 * Implements hook_features_pipe_panelizer_defaults_alter().
 */
function panelizer_features_pipe_panelizer_defaults_alter(&$more, $data, $export) {
  foreach ($data as $machine_name) {
    list($entity_type, $bundle) = explode(':', $machine_name);
    $variables = array(
      'panelizer_defaults_' . $entity_type . '_' . $bundle,
      'panelizer_' . $entity_type . ':' . $bundle . '_allowed_layouts',
      'panelizer_' . $entity_type . ':' . $bundle . '_allowed_layouts_default',
      'panelizer_' . $entity_type . ':' . $bundle . '_allowed_types',
      'panelizer_' . $entity_type . ':' . $bundle . '_allowed_types_default',
      'panelizer_' . $entity_type . ':' . $bundle . '_default',
    );

    // Add default display variables for each view mode.
    $entity_info = entity_get_info($entity_type);
    $default_base = 'panelizer_' . $entity_type . ':' . $bundle . ':';
    foreach ($entity_info['view modes'] as $view_mode => $view_info) {
      $variables[] = $default_base . $view_mode . '_selection';
    }
    $variables[] = $default_base . 'page_manager' . '_selection';
    $variables[] = $default_base . 'default' . '_selection';
    foreach ($variables as $variable) {
      $more['variable'][$variable] = $variable;
    }
  }
  return $more;
}

/**
 * Implements hook_search_api_alter_callback_info().
 */
function panelizer_search_api_alter_callback_info() {
  $info = array();
  $info['panelizer'] = array(
    'name' => t('Panelizer'),
    'description' => t('Add the content from the Panelizer "Full page override" to the search index.'),
    'class' => 'PanelizerSearchApiAlterCallback',
  );
  return $info;
}

// -----------------------------------------------------------------------
// Theme functions where necessary.

/**
 * Panelizer view mode theme function.
 */
function template_preprocess_panelizer_view_mode(&$vars) {
  $element = $vars['element'];
  $entity = $element['#panelizer_entity'];
  $panelizer = $element['#panelizer'];
  $handler = $element['#panelizer_handler'];
  $info = $element['#panelizer_content'];
  $handler
    ->preprocess_panelizer_view_mode($vars, $entity, $element, $panelizer, $info);
}

// -----------------------------------------------------------------------
// Drupal actions integration for VBO.

/**
 * Implements hook_action_info().
 */
function panelizer_action_info() {
  return array(
    'panelizer_set_status_action' => array(
      'type' => 'entity',
      'label' => t('Set panelizer status'),
      'vbo_configurable' => TRUE,
      'configurable' => FALSE,
      'behavior' => array(
        'changes_property',
      ),
      'configurable' => TRUE,
    ),
  );
}

/**
 * Executes the panelizer_set_status action.
 */
function panelizer_set_status_action($entity, $context) {
  $view_mode = 'page_manager';
  if (isset($context['view_mode'])) {
    $view_mode = $context['view_mode'];
  }
  list($entity_id, $revision_id, $bundle) = entity_extract_ids($context['entity_type'], $entity);
  if (isset($context['panelizer_default'])) {
    $entity->panelizer[$view_mode] = $context['panelizer_default'];
    $entity->panelizer[$view_mode]->did = NULL;

    // Ensure original values are maintained.
    $entity->panelizer[$view_mode]->entity_id = $entity_id;
    $entity->panelizer[$view_mode]->revision_id = $revision_id;
  }
  else {
    $entity->panelizer[$view_mode]->name = NULL;
    $entity->panelizer[$view_mode]->did = NULL;
  }
}

/**
 * Provides the panelizer_set_status_action form.
 */
function panelizer_set_status_action_form($context, &$form_state) {
  $form = array();
  $entity_info = entity_get_info($context['entity_type']);
  $entities = entity_load($context['entity_type'], $form_state['selection']);
  $bundles = array();
  $handler = panelizer_entity_plugin_get_handler($context['entity_type']);

  // Collect our list of bundles.
  foreach ($entities as $entity) {
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($context['entity_type'], $entity);
    $bundles[$bundle] = $bundle;
  }
  $conditions = array(
    'panelizer_type' => $context['entity_type'],
    'panelizer_key' => $bundles,
  );
  ctools_include('export');
  $defaults = ctools_export_load_object('panelizer_defaults', 'conditions', $conditions);
  foreach ($defaults as $name => $default) {
    if (empty($default->title)) {
      $default->title = t('Default');
    }
    $options[$default->view_mode][$name] = t('@bundle: @title', array(
      '@bundle' => $entity_info['bundles'][$default->panelizer_key]['label'],
      '@title' => $default->title,
    ));
  }
  $view_modes = array();
  foreach ($handler->plugin['view modes'] as $view_mode => $view_mode_info) {
    $view_modes[$view_mode] = $view_mode_info['label'];
  }
  $form['panelizer']['#tree'] = TRUE;
  foreach ($view_modes as $view_mode => $label) {
    if (empty($options[$view_mode])) {
      unset($view_modes[$view_mode]);
      continue;
    }
    natcasesort($options[$view_mode]);
    $panelizers = array(
      'not' => t('Not panelized'),
    ) + $options[$view_mode];
    $form['panelizer'][$view_mode] = array(
      '#type' => 'select',
      '#title' => t('Panelizer status'),
      '#options' => $panelizers,
      '#states' => array(
        'visible' => array(
          '#panelizer-view-mode' => array(
            'value' => $view_mode,
          ),
        ),
      ),
    );
  }
  $form['view_mode'] = array(
    '#type' => 'select',
    '#title' => t('View mode'),
    '#options' => $view_modes,
    '#id' => 'panelizer-view-mode',
    '#weight' => -10,
  );
  $form['#panelizer_defaults'] = $defaults;
  return $form;
}

/**
 * FormAPI submission callback.
 */
function panelizer_set_status_action_submit($form, $form_state) {
  $view_mode = $form_state['values']['view_mode'];
  $panelizer = $form_state['values']['panelizer'][$view_mode];
  $retval = array(
    'panelizer' => $panelizer,
    'view_mode' => $view_mode,
  );
  if ($form_state['values']['panelizer'] != 'not') {
    $retval['panelizer_default'] = $form['#panelizer_defaults'][$panelizer];
  }
  return $retval;
}

// --------------------------------------------------------------------------
// Miscellaneous helper functions

/**
 * Return list of operations.
 *
 * @see hook_panelizer_operations_alter().
 */
function panelizer_operations() {
  $operations = array(
    'settings' => array(
      'menu title' => 'Settings',
      'link title' => t('settings'),
      'admin callback' => 'panelizer_default_settings_page',
      // ctools export ui thinks this is 'edit'.
      'ui path' => 'edit',
    ),
    'context' => array(
      'menu title' => 'Context',
      'link title' => t('context'),
      'admin callback' => 'panelizer_default_context_page',
    ),
    'layout' => array(
      'menu title' => 'Layout',
      'link title' => t('layout'),
      'admin callback' => 'panelizer_default_layout_page',
    ),
    'content' => array(
      'menu title' => 'Content',
      'link title' => t('content'),
      'admin callback' => 'panelizer_default_content_page',
    ),
  );
  drupal_alter('panelizer_operations', $operations);
  return $operations;
}

/**
 * Gets the current layout options approach for an entity bundle.
 *
 * @param string $type
 *   The entity type.
 * @param string $bundle
 *   The entity bundle.
 *
 * @return string
 *   The name of the layout approach to check (default or per bundle).
 */
function panelizer_get_allowed_layouts_option($type, $bundle) {
  if (variable_get('panelizer_' . $type . ':' . $bundle . '_allowed_layouts_default', FALSE)) {
    return 'panels_page';
  }
  else {
    return 'panelizer_' . $type . ':' . $bundle;
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for panels_ipe_edit_control_form().
 *
 * Alter the IPE save control form to handle extra Panelizer functionality.
 */
function panelizer_form_panels_ipe_edit_control_form_alter(&$form, &$form_state) {
  if (empty($form_state['renderer'])) {
    return;
  }
  $renderer = $form_state['renderer'];
  $cache_key = $renderer->display->cache_key;
  $parts = explode(':', $cache_key, 3);
  if ($parts[0] != 'panelizer') {
    return;
  }
  list($module, $type, $key) = $parts;
  if ($type == 'default') {
    return;
  }

  // Load the $plugin information.
  $handler = panelizer_entity_plugin_get_handler($type);
  if (!$handler) {
    return;
  }

  // Get the entity that's being edited.
  list($entity_id, $view_mode) = explode(':', $key);
  $entities = entity_load($handler->entity_type, array(
    $entity_id,
  ));
  if (empty($entities[$entity_id])) {
    return;
  }
  $entity = $entities[$entity_id];
  list($entity_id, $revision_id, $bundle) = entity_extract_ids($handler->entity_type, $entity);
  $entity_info = entity_get_info($handler->entity_type);
  if (empty($entity->panelizer[$view_mode])) {
    return;
  }
  $panelizer = $entities[$entity_id]->panelizer[$view_mode];
  module_load_include('inc', 'panelizer', 'includes/common');
  $revision_info = $handler
    ->entity_allows_revisions($entity);

  // Set panelizer info regardless of revision or panelizer layout default
  // status.
  $form_state['panelizer entity'] = $entity;
  $form_state['panelizer bundle'] = $bundle;
  $form_state['panelizer handler'] = $handler;
  $form_state['panelizer view_mode'] = $view_mode;

  // If this entity has revisions enabled we can assume that they have
  // permissions to add revisions via IPE.
  if (!empty($revision_info)) {
    $form_state['entity'] = $entity;
    $form_state['revision info'] = $revision_info;
    panelizer_add_revision_info_form($form, $form_state);

    // Tweak the log message field to make it fit better.
    if (isset($form['revision_information']['log'])) {
      $form['revision_information']['log']['#type'] = 'textfield';
      $form['revision_information']['log']['#description'] = '';
    }
  }

  // If the entity already has customized panelizer data, add the revert button.
  if (!empty($panelizer->did)) {
    if ($handler
      ->has_default_panel($bundle . '.' . $view_mode)) {
      $uri = entity_uri($handler->entity_type, $entity);
      $url = $uri['path'];

      // Support for Workbench Moderation to redirect to the newest revision for
      // this node.
      if ($handler->entity_type == 'node' && module_exists('workbench_moderation')) {
        $url = 'node/' . $entity->nid . '/current-revision';
      }
      $redirect_url = url($uri['path'] . '/panelizer/' . $view_mode . '/reset', array(
        'query' => array(
          'destination' => $url,
        ),
      ));
      drupal_add_js(array(
        'panelizer' => array(
          'revert_default_url' => $redirect_url,
        ),
      ), 'setting');
      $form['buttons']['revert_default'] = array(
        '#type' => 'submit',
        '#value' => t('Revert to @bundle_name default', array(
          '@bundle_name' => $entity_info['bundles'][$bundle]['label'],
        )),
        '#submit' => array(
          'panels_ipe_revert_to_default',
        ),
        '#attributes' => array(
          'class' => array(
            'panels-ipe-cancel',
          ),
        ),
        '#id' => 'panelizer-ipe-revert',
        '#access' => $handler
          ->panelizer_access('defaults', $entity, $panelizer->view_mode),
      );
    }
    return;
  }

  // Change the default button to say "Save as custom".
  $form['buttons']['submit']['#value'] = t('Save as custom');

  // Calculate the proper name to add to the cache key, which has some data
  // stripped off of the true name.
  $name = 'default';
  if (!empty($panelizer->name)) {
    $pieces = explode(':', $panelizer->name);
    if (isset($pieces[2])) {
      $name = $pieces[2];
    }
  }

  // Add another button to save as the default instead.
  $form['buttons']['save_default'] = array(
    '#type' => 'submit',
    '#value' => t('Save as @bundle_name default', array(
      '@bundle_name' => $entity_info['bundles'][$bundle]['label'],
    )),
    '#submit' => array(
      'panels_ipe_change_to_default',
      'panels_edit_display_form_submit',
    ),
    '#save-display' => TRUE,
    '#weight' => -1,
    // Precalculate the cache key so we don't have to unwind a bunch of stuff in
    // the submit handler.
    '#cache_key' => 'default:' . $handler->entity_type . ':' . $bundle . '.' . $panelizer->view_mode . ':' . $name,
    '#attributes' => array(
      'class' => array(
        'panels-ipe-save',
      ),
    ),
    '#id' => 'panelizer-save-default',
    '#access' => $handler
      ->panelizer_access('defaults', $entity, $panelizer->view_mode),
  );
}

/**
 * Implements hook_form_FORM_ID_alter() for panels_change_layout().
 *
 * Alter the change layout form to support saving as default.
 */
function panelizer_form_panels_change_layout_alter(&$form, &$form_state) {

  // Only alter on initial form build.
  if (empty($form_state['executed'])) {
    if (isset($form_state['back'])) {

      // Do nothing when moving backwards.
      return;
    }

    // Extract the display from the form state.
    $display = $form_state['display'];
    $cache_key = $display->cache_key;
    $parts = explode(':', $cache_key, 3);
    if ($parts[0] != 'panelizer') {
      return;
    }
    list($module, $type, $key) = $parts;

    // If the display is not owned by Panelizer, or is default handler, exit.
    if ($module != 'panelizer' || $type == 'default') {
      return;
    }

    // Load the $plugin information.
    $handler = panelizer_entity_plugin_get_handler($type);
    if (!$handler) {
      return;
    }

    // Get the entity that's being edited through Panelizer context.
    $entity = $display->context['panelizer']->data;

    // Check if the view mode is configured.
    list($entity_id, $view_mode) = explode(':', $key);
    if (empty($entity->panelizer[$view_mode])) {
      return;
    }
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($handler->entity_type, $entity);
    $entity_info = entity_get_info($handler->entity_type);
    $panelizer = $entity->panelizer[$view_mode];

    // Calculate the proper name to add to the cache key, which has some data
    // stripped off of the true name.
    $name = 'default';
    if (!empty($panelizer->name)) {
      $pieces = explode(':', $panelizer->name);
      if (isset($pieces[2])) {
        $name = $pieces[2];
      }
    }
    module_load_include('inc', 'panelizer', 'includes/common');
    $revision_info = $handler
      ->entity_allows_revisions($entity);

    // If this entity has revisions enabled we can assume that they have
    // permissions to add revisions via IPE.
    if (!empty($revision_info)) {

      // Re-load the entity so we get one from the entity cache, which is
      // necessary for panelizer_panels_cache_save() to get our changes.
      $entities = entity_load($handler->entity_type, array(
        $entity_id,
      ));
      $form_state['entity'] = $entities[$entity_id];
      $form_state['revision info'] = $revision_info;
      panelizer_add_revision_info_form($form, $form_state);

      // Make it clear from description that this only affects custom displays.
      if (isset($form['revision_information']['revision'])) {
        $form['revision_information']['revision']['#description'] = t('This only affects changes saved as custom.');
      }
    }

    // Move the submit buttons down so they'll appear after the revision info.
    $form['back']['#weight'] = 20;
    $form['submit']['#weight'] = 25;

    // Change the default button to say "Save as custom".
    $form['submit']['#value'] = t('Save as custom');

    // Add another button to save as the default instead.
    $form['save_default'] = array(
      '#type' => 'submit',
      '#value' => t('Save as @bundle_name default', array(
        '@bundle_name' => $entity_info['bundles'][$bundle]['label'],
      )),
      '#submit' => array(
        'panels_ipe_change_to_default',
        'panels_change_layout_submit',
      ),
      '#save-display' => TRUE,
      '#cache_key' => 'default:' . $handler->entity_type . ':' . $bundle . '.' . $panelizer->view_mode . ':' . $name,
      '#access' => $handler
        ->panelizer_access('defaults', $entity, $panelizer->view_mode),
      '#weight' => 30,
    );
  }
}

/**
 * Implement template_preprocess_panels_ipe_toolbar().
 */
function panelizer_preprocess_panels_ipe_toolbar(&$variables) {

  // Some custom CSS to improve the UX.
  // $variables['#attached'] = array(
  //   'css' => array(
  //     drupal_get_path('module', 'panelizer') . '/css/panelizer.ipe.css',
  //   ),
  // );
  // dpm($variables);
}

/**
 * Implements hook_panels_ipe_ajax_save_commands_alter().
 *
 * If Workbench Moderation is enabled, and this is for the form submission on a
 * Panelized node, redirect to the latest revision upon save.
 */
function panelizer_panels_ipe_ajax_save_commands_alter(&$ipe, $form_state) {

  // This requires Workbench Moderation.
  if (module_exists('workbench_moderation')) {

    // The form was actually saved.
    if (!empty($form_state['submitted']) && !empty($ipe->meta_location)) {

      // This is for a revisions-enabled Panelizer entity.
      if (!empty($form_state['use revisions'])) {
        $entity = $form_state['entity'];
        $ipe->commands[0] = ctools_ajax_command_redirect('node/' . $entity->nid . '/current-revision');
      }
    }
  }
}

/**
 * Submit handler to save the panelizer default instead of customizing the
 * entity.
 */
function panels_ipe_change_to_default($form, &$form_state) {

  // Change the cache key so that it saves to the default instead of the
  // entity. This works because we're actually editing the original display
  // anyway, and everything else keys off the cache key.
  $form_state['display']->swap_cache_key = $form_state['triggering_element']['#cache_key'];
}

/**
 * Submit handler to revert to the default panelizer.
 */
function panels_ipe_revert_to_default($form, &$form_state) {

  // Reduce code complexity due to indirection.
  $handler = $form_state['panelizer handler'];
  $entity = $form_state['panelizer entity'];
  $bundle = $form_state['panelizer bundle'];
  $view_mode = $form_state['panelizer view_mode'];
  $renderer = $form_state['renderer'];
  $handler
    ->delete_entity_panelizer($entity, $view_mode);
  $name = implode(':', array(
    $handler->entity_type,
    $bundle,
    'default',
  ));
  if ($view_mode != 'page_manager') {
    $name .= ':' . $view_mode;
  }
  $cache_key = $form_state['display']->cache_key;

  // Now load the original default display and force a rerender.
  $panelizer = $handler
    ->get_default_panelizer_object($bundle . '.' . $view_mode, $name);
  $renderer->display = $display = $panelizer->display;
  $display->cache_key = $cache_key;
  $display->context = $handler
    ->get_contexts($panelizer, $entity);
  $renderer->commands[] = ajax_command_replace("#panels-ipe-display-{$renderer->clean_key}", panels_render_display($display, $renderer));
}

/**
 * Implements hook_panelizer_pre_render_alter().
 *
 * Panelizer fails to communicate to the theme layer what view mode an entity's
 * fields are being rendered in, so we unfortunately have to do that ourselves.
 */
function panelizer_panelizer_pre_render_alter(&$panelizer, &$display, &$entity) {
  foreach ($entity->panelizer as $view_mode => $reference_panelizer) {
    if ($reference_panelizer === $panelizer) {
      $entity->panelizer_view_mode = $view_mode;
      break;
    }
  }
}

/**
 * Implements hook_workbench_moderation_node_history_view_alter().
 *
 * This is a little kludgy as the data in the row is stored as final HTML for
 * display.
 */
function panelizer_workbench_moderation_node_history_view_alter(&$rows) {

  // Load the node.
  $node = node_load(arg(1));

  // Verify the user has access to the Panelizer configuration.
  if (panelizer_is_panelized('node', $node->type)) {

    // Loop over each table row.
    foreach ($rows as $key => &$row) {

      // Published.
      $path = 'node/' . $node->nid . '/panelizer';

      // Not published.
      if (!in_array('published-revision', $row['class'])) {
        $path = 'node/' . $node->nid . '/revisions/' . $row['data']['vid'] . '/panelizer';
      }

      // Add a link to the Panelizer page.
      $row['data']['revision'] .= ' | ' . l('Customize display', $path);
    }
  }
}

/**
 * Menu callback load method to either return the requested revision ID or FALSE
 * to prevent the tab from being processed.
 *
 * @param string $vid
 *   An entity's revision ID.
 *
 * @return mixed
 *   Either the revision_id or FALSE if the revision was empty.
 */
function panelizer_node_revision_load($revision_id = NULL) {
  if (!empty($revision_id)) {
    return $revision_id;
  }
  return FALSE;
}

/**
 * Implements hook_clone_node_alter().
 *
 * Custom integration for Node Clone to handle objects that might be embedded in
 * a custom Panelizer display. Currently supports Fieldable Panels Panes.
 */
function panelizer_clone_node_alter(&$node, $context) {
  if (!empty($node->panelizer)) {
    if ($handler = panelizer_entity_plugin_get_handler('node')) {
      foreach ($node->panelizer as $view_mode => $panelizer) {
        $node->panelizer[$view_mode] = $handler
          ->clone_panelizer($panelizer, $node);
      }
    }
  }
}

Functions

Namesort descending Description
panelizer_action_info Implements hook_action_info().
panelizer_administer_entity_bundle Access callback to see if a user can administer a particular bundle.
panelizer_administer_panelizer_default Access callback to see if a user can administer a particular default.
panelizer_admin_paths Implements hook_admin_paths().
panelizer_clone_node_alter Implements hook_clone_node_alter().
panelizer_context_cache_clear Clear the panelizer object in the object cache.
panelizer_context_cache_get Fetch the panelizer object from the object cache.
panelizer_context_cache_set Store the panelizer object in the object cache.
panelizer_ctools_access_clear Implement CTools access form caching callback: get.
panelizer_ctools_access_get Implement CTools access form caching callback: get.
panelizer_ctools_access_set Implement CTools access form caching callback: set.
panelizer_ctools_plugin_api Implements hook_ctools_plugin_api().
panelizer_ctools_plugin_directory Implements hook_ctools_plugin_directory().
panelizer_ctools_plugin_type Implements hook_ctools_plugin_type().
panelizer_default_list_or_settings_page Page callback to delegate to either the settings page or list page.
panelizer_default_page_manager_handlers Implements hook_default_page_manager_handlers().
panelizer_default_title_callback Title callback to properly set the tile when editing panelizer defaults.
panelizer_entity_delete Implements hook_entity_delete().
panelizer_entity_insert Implements hook_entity_insert().
panelizer_entity_load Implements hook_entity_load().
panelizer_entity_plugin_callback_switcher Callback used for switching callbacks into the proper plugin.
panelizer_entity_plugin_get_handler Get the class to handle custom code for a given entity type plugin.
panelizer_entity_plugin_process CTools process callback for an entity plugin.
panelizer_entity_plugin_switcher_page Page callback for entity menu callbacks.
panelizer_entity_update Implements hook_entity_update().
panelizer_entity_view_alter Implements hook_entity_view_alter().
panelizer_export_delete_callback export.inc callback to properly delete a panelizer default.
panelizer_export_delete_callback_subrecords export.inc callback to delete sub records for an object.
panelizer_export_export_callback export.inc callback to properly export a panelizer default.
panelizer_export_node_alter Implements hook_export_node_alter().
panelizer_export_save_callback export.inc callback to properly save a panelizer default.
panelizer_export_ui_switcher_page Specialized version of ctools_export_ui_switcher_page().
panelizer_features_api Implements hook_features_api().
panelizer_features_pipe_panelizer_defaults_alter Implements hook_features_pipe_panelizer_defaults_alter().
panelizer_field_attach_delete_revision Implements hook_field_attach_delete_revision().
panelizer_field_attach_form
panelizer_field_attach_submit
panelizer_form_alter Implements hook_form_alter().
panelizer_form_field_ui_display_overview_form_alter Implements hook_form_FORM_ID_alter().
panelizer_form_panels_change_layout_alter Implements hook_form_FORM_ID_alter() for panels_change_layout().
panelizer_form_panels_ipe_edit_control_form_alter Implements hook_form_FORM_ID_alter() for panels_ipe_edit_control_form().
panelizer_get_allowed_layouts_option Gets the current layout options approach for an entity bundle.
panelizer_get_entity_plugin Fetch a single entity plugin.
panelizer_get_entity_plugins Fetch all entity plugin.
panelizer_get_plugins_with_hook Fetch handler objects for all plugins that implement the named hook.
panelizer_handler_load Load handler to get a plugin as a menu callback.
panelizer_has_choice_callback Menu callback to determine if a type has a choice of defaults.
panelizer_has_choice_callback_view_mode Menu callback to determine if a type+view_mode has a choice of defaults.
panelizer_has_no_choice_callback Menu callback to determine if a type has a choice of defaults.
panelizer_help Implements hook_help().
panelizer_hook_info Implements hook_hook_info().
panelizer_is_panelized Menu callback to determine if a type has a choice of defaults.
panelizer_menu Implements hook_menu().
panelizer_menu_alter Implements hook_menu_alter().
panelizer_node_revision_load Menu callback load method to either return the requested revision ID or FALSE to prevent the tab from being processed.
panelizer_node_search_result Implements hook_node_search_result().
panelizer_node_type_load Menu load callback to scrub a node bundle from the URL safe equivalent.
panelizer_node_update_index Implements hook_node_update_index().
panelizer_og_permission Implements hook_og_permission().
panelizer_operations Return list of operations.
panelizer_panelizer_defaults_alter Implements hook_panelizer_defaults_alter().
panelizer_panelizer_default_types_alter Implements hook_panelizer_defaults_alter().
panelizer_panelizer_pre_render_alter Implements hook_panelizer_pre_render_alter().
panelizer_panels_cache_clear Implements hook_panels_cache_clear().
panelizer_panels_cache_get Implements hook_panels_cache_get().
panelizer_panels_cache_save Implements hook_panels_cache_save().
panelizer_panels_cache_set Implements hook_panels_cache_set().
panelizer_panels_default_settings_submit Custom submission handler for setting default content and layout settings.
panelizer_panels_ipe_access Implements hook_panels_ipe_access().
panelizer_panels_ipe_ajax_save_commands_alter Implements hook_panels_ipe_ajax_save_commands_alter().
panelizer_permission Implements hook_permission().
panelizer_preprocess_panels_ipe_toolbar Implement template_preprocess_panels_ipe_toolbar().
panelizer_process_page Implements hook_process_page().
panelizer_search_api_alter_callback_info Implements hook_search_api_alter_callback_info().
panelizer_set_status_action Executes the panelizer_set_status action.
panelizer_set_status_action_form Provides the panelizer_set_status_action form.
panelizer_set_status_action_submit FormAPI submission callback.
panelizer_theme Implements hook_theme().
panelizer_views_api Implementation of hook_views_api().
panelizer_workbench_moderation_node_history_view_alter Implements hook_workbench_moderation_node_history_view_alter().
panels_ipe_change_to_default Submit handler to save the panelizer default instead of customizing the entity.
panels_ipe_revert_to_default Submit handler to revert to the default panelizer.
template_preprocess_panelizer_view_mode Panelizer view mode theme function.

Constants

Namesort descending Description
PANELIZER_VERSION @file The Panelizer module attaches panels to entities, providing default panels and allowing each panel to be configured independently by privileged users.