You are here

fieldable_panels_panes.module in Fieldable Panels Panes (FPP) 7

Same filename and directory in other branches
  1. 1.0.x fieldable_panels_panes.module

Maintains an entity that appears as panel pane content.

File

fieldable_panels_panes.module
View source
<?php

/**
 * @file
 * Maintains an entity that appears as panel pane content.
 */

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

/**
 * Implements hook_entity_info().
 */
function fieldable_panels_panes_entity_info() {
  $info = array();
  ctools_include('export');
  $bundles = array();
  foreach (ctools_export_crud_load_all('fieldable_panels_pane_type') as $type) {
    $bundles[$type->name] = array(
      'label' => $type->title,
      'description' => $type->description,
      'admin' => array(
        'path' => 'admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type',
        'bundle argument' => 3,
        'real path' => 'admin/structure/fieldable-panels-panes/' . $type->name,
        'access arguments' => array(
          'administer fieldable panels panes',
        ),
      ),
    );
  }
  $info['fieldable_panels_pane'] = array(
    'label' => t('Fieldable panel pane'),
    'controller class' => 'PanelsPaneController',
    'base table' => 'fieldable_panels_panes',
    'revision table' => 'fieldable_panels_panes_revision',
    'fieldable' => TRUE,
    'uuid' => TRUE,
    'entity keys' => array(
      'id' => 'fpid',
      'revision' => 'vid',
      'bundle' => 'bundle',
      'label' => 'title',
      // This key is required for proper integration with Title module.
      'language' => 'language',
      'uuid' => 'uuid',
      'revision uuid' => 'vuuid',
    ),
    'bundles' => $bundles,
    'bundle keys' => array(
      'bundle' => 'name',
    ),
    'label callback' => 'fieldable_panels_panes_entity_label_callback',
    'uri callback' => 'fieldable_panels_panes_entity_uri_callback',
    'view modes' => array(
      // @todo Should support view modes.
      'full' => array(
        'label' => t('Full'),
        'custom settings' => FALSE,
      ),
      'preview' => array(
        'label' => t('Preview'),
        'custom settings' => FALSE,
      ),
    ),
    'inline entity form' => array(
      'controller' => 'FieldablePanelsPaneInlineEntityFormController',
    ),
    // Entity API module callbacks.
    'view callback' => 'entity_metadata_view_single',
    'creation callback' => 'fieldable_panels_panes_create',
    'access callback' => 'fieldable_panels_panes_access',
    'save callback' => 'fieldable_panels_panes_save',
    'deletion callback' => 'fieldable_panels_panes_delete',
    // Entity translation support.
    'translation' => array(
      'entity_translation' => array(
        'class' => 'EntityTranslationFieldablePanelsPaneHandler',
        'base path' => 'admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes',
        'edit path' => 'admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/edit',
        'path wildcard' => '%fieldable_panels_panes',
      ),
    ),
    // Title module support.
    'field replacement' => array(
      'title' => array(
        'field' => array(
          'type' => 'text',
          'cardinality' => 1,
          'translatable' => TRUE,
        ),
        'instance' => array(
          'label' => t('Title'),
          'description' => t('A field replacing fieldable panel pane title.'),
          'required' => FALSE,
          'settings' => array(
            'text_processing' => 0,
          ),
          'widget' => array(
            'weight' => -10,
          ),
        ),
      ),
    ),
    // Disable support for the Redirect module.
    'redirect' => FALSE,
  );

  // Optional support for the entitycache module.
  if (module_exists('entitycache')) {
    $info['fieldable_panels_pane']['field cache'] = FALSE;
    $info['fieldable_panels_pane']['entity cache'] = TRUE;
  }
  return $info;
}

/**
 * Implements hook_module_implements_alter().
 */
function fieldable_panels_panes_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'entity_info_alter') {

    // Fieldable Panels Panes will do a bit of clean-up to prevent issues with
    // obsolete paths found in legacy bundles that are provided by modules via
    // hook_entity_info_alter(), but we need to make sure that our
    // implementation is called last.
    $group = $implementations['fieldable_panels_panes'];
    unset($implementations['fieldable_panels_panes']);
    $implementations['fieldable_panels_panes'] = $group;
  }
}

/**
 * Implements hook_entity_info_alter().
 */
function fieldable_panels_panes_entity_info_alter(&$info) {
  $old_base_path = 'admin/structure/panels/entity/manage';
  $new_base_path = 'admin/structure/fieldable-panels-panes';
  if (is_array($info) && !empty($info)) {
    foreach ($info['fieldable_panels_pane']['bundles'] as $name => &$bundle) {
      if (isset($bundle['admin']['path']) && strpos($bundle['admin']['path'], $old_base_path) !== FALSE) {
        $bundle['admin']['path'] = str_replace($old_base_path, $new_base_path, $bundle['admin']['path']);
        $bundle['admin']['real path'] = str_replace($old_base_path, $new_base_path, $bundle['admin']['real path']);
        $bundle['admin']['bundle argument'] = $bundle['admin']['bundle argument'] - 2;
      }
    }
  }
}

/**
 * Implements hook_hook_info().
 */
function fieldable_panels_panes_hook_info() {
  $defaults = array(
    'group' => 'fieldable_panels_panes',
  );
  $hooks = array(
    'fieldable_panels_pane_delete' => $defaults,
    'fieldable_panels_pane_insert' => $defaults,
    'fieldable_panels_pane_presave' => $defaults,
    'fieldable_panels_pane_update' => $defaults,
    'fieldable_panels_pane_view' => $defaults,
  );
  return $hooks;
}

/**
 * Implements hook_field_extra_fields().
 */
function fieldable_panels_panes_field_extra_fields() {
  $extra = array();
  $entity_info = entity_get_info('fieldable_panels_pane');
  foreach ($entity_info['bundles'] as $bundle => $info) {
    $extra['fieldable_panels_pane'][$bundle] = array(
      'form' => array(
        'title' => array(
          'label' => t('Title'),
          'description' => t('The displayed title of the entity.'),
          'weight' => -5,
        ),
      ),
      'display' => array(
        'title' => array(
          'label' => t('Title'),
          'description' => t('The displayed title of the entity.'),
          'weight' => -5,
        ),
      ),
    );
  }
  return $extra;
}

/**
 * Implements hook_field_extra_fields_display_alter().
 */
function fieldable_panels_panes_field_extra_fields_display_alter(&$displays, $context) {
  if ($context['entity_type'] == 'fieldable_panels_pane' && $context['view_mode'] == 'full') {

    // Hide display of the title field on the 'full' view mode (because it gets
    // displayed as the Pane title). Ensure the 'title' key exists, since some
    // other modules may have remove it. This is the case of the Title module
    // for example.
    if (isset($displays['title'])) {
      $displays['title']['visible'] = FALSE;
    }
  }
}

/**
 * Implements hook_menu().
 */
function fieldable_panels_panes_menu() {
  $items = array();
  $base = array(
    'access arguments' => array(
      'administer fieldable panels panes',
    ),
    'file' => 'includes/admin.inc',
  );

  // Legacy paths to support the old method of providing bundles via
  // entity_hook_info().
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes'] = array(
    'title callback' => 'fieldable_panels_panes_entity_title',
    'title arguments' => array(
      4,
    ),
    'page callback' => 'fieldable_panels_panes_entity_view_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'view',
      4,
    ),
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/view'] = array(
    'title' => 'Preview',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'page callback' => 'fieldable_panels_panes_entity_view_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'view',
      4,
    ),
    'weight' => -10,
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/edit'] = array(
    'title' => 'Edit',
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
    'page callback' => 'fieldable_panels_panes_entity_edit_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'update',
      4,
    ),
    'weight' => -9,
  ) + $base;

  // Access control.
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/access'] = array(
    'title' => 'Access control',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'fieldable_panels_panes_entity_edit_access_page',
    'page arguments' => array(
      'view',
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'update',
      4,
    ),
    'weight' => 10,
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/access/view'] = array(
    'title' => 'View',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/access/update'] = array(
    'title' => 'Edit',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'fieldable_panels_panes_entity_edit_access_page',
    'page arguments' => array(
      'edit',
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'update',
      4,
    ),
    'weight' => 10,
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/delete'] = array(
    'title' => 'Delete',
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fieldable_panels_panes_entity_delete_form',
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'delete',
      4,
    ),
    'weight' => -8,
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/revision'] = array(
    'title' => 'Revisions',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'fieldable_panels_panes_entity_list_revisions_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'delete',
      4,
    ),
    'weight' => -7,
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/revision/%'] = array(
    'title callback' => 'fieldable_panels_panes_entity_title',
    'title arguments' => array(
      4,
    ),
    'page callback' => 'fieldable_panels_panes_entity_view_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'view',
      4,
    ),
    'load arguments' => array(
      6,
    ),
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/revision/%/view'] = array(
    'title' => 'View',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'page callback' => 'fieldable_panels_panes_entity_view_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'view',
      4,
    ),
    'load arguments' => array(
      6,
    ),
    'weight' => -10,
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/revision/%/edit'] = array(
    'title' => 'Edit',
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
    'page callback' => 'fieldable_panels_panes_entity_edit_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'update',
      4,
    ),
    'weight' => -8,
    'load arguments' => array(
      6,
    ),
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/revision/%/delete'] = array(
    'title' => 'Delete',
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fieldable_panels_panes_entity_delete_revision_form',
      4,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'delete',
      4,
    ),
    'weight' => -7,
    'load arguments' => array(
      6,
    ),
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/revision/%/make-current'] = array(
    'title' => 'Make current',
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_INLINE,
    'page callback' => 'fieldable_panels_panes_entity_make_current_page',
    'page arguments' => array(
      4,
    ),
    'access callback' => 'fieldable_panels_panes_entity_make_current_access',
    'access arguments' => array(
      4,
    ),
    'weight' => -8,
    'load arguments' => array(
      6,
    ),
  ) + $base;
  if (module_exists('devel')) {
    $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/devel'] = array(
      'title' => 'Devel',
      'page callback' => 'devel_load_object',
      'page arguments' => array(
        'fieldable_panels_pane',
        4,
      ),
      'access arguments' => array(
        'access devel information',
      ),
      'type' => MENU_LOCAL_TASK,
      'weight' => 100,
      'file' => 'devel.pages.inc',
      'file path' => drupal_get_path('module', 'devel'),
    );
    $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/devel/load'] = array(
      'title' => 'Load',
      'type' => MENU_DEFAULT_LOCAL_TASK,
    );
    $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/devel/render'] = array(
      'title' => 'Render',
      'page callback' => 'devel_render_object',
      // Normally this would be the name of the entity type, but slightly
      // modified in order to call the right function.
      'page arguments' => array(
        'fieldable_panels_panes',
        4,
      ),
      'access arguments' => array(
        'access devel information',
      ),
      'file' => 'devel.pages.inc',
      'file path' => drupal_get_path('module', 'devel'),
      'type' => MENU_LOCAL_TASK,
      'weight' => 100,
    );
  }
  $items['admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type'] = array(
    'title callback' => 'fieldable_panels_panes_entities_title',
    'title arguments' => array(
      3,
    ),
    'page callback' => 'fieldable_panels_panes_entities_list_page',
    'page arguments' => array(
      3,
    ),
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type/add'] = array(
    'title' => 'Add fieldable panels pane',
    'page callback' => 'fieldable_panels_panes_entities_add_page',
    'page arguments' => array(
      3,
    ),
    'access callback' => 'fieldable_panels_panes_access',
    'access arguments' => array(
      'create',
      3,
    ),
    'type' => MENU_LOCAL_ACTION,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
  ) + $base;
  $items['admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type/list'] = array(
    'title' => 'List',
    'page callback' => 'fieldable_panels_panes_entities_list_page',
    'page arguments' => array(
      3,
    ),
    'access callback' => 'fieldable_panels_panes_access_callback',
    'access arguments' => array(),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
    'weight' => -10,
  ) + $base;

  // Settings.
  $items['admin/structure/fieldable-panels-panes/settings'] = array(
    'title' => 'Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fieldable_panels_panes_settings',
    ),
    'type' => MENU_LOCAL_TASK,
  ) + $base;
  return $items;
}

/**
 * Check if user has administrator or list permission.
 */
function fieldable_panels_panes_access_callback() {
  return user_access('administer fieldable panels panes') || user_access('access fieldable panels panes master list');
}

/**
 * Implements hook_permission().
 */
function fieldable_panels_panes_permission() {
  $perms = array(
    'administer fieldable panels panes' => array(
      'title' => t('Administer fieldable panels panes'),
      'description' => t('Allows users complete control over fieldable panel pane entities. This permission overrides any other permission.'),
    ),
    'access fieldable panels panes master list' => array(
      'title' => t('Access list pages for fieldable panels panes'),
      'description' => t('Allows users to see fieldable panel panes bundles and entities.'),
    ),
  );
  $entity_info = entity_get_info('fieldable_panels_pane');
  foreach ($entity_info['bundles'] as $bundle => $info) {
    $perms["create fieldable {$bundle}"] = array(
      'title' => t('Create new %type', array(
        '%type' => $info['label'],
      )),
      'description' => t('Allows users to create new fieldable panel pane entities of bundle %type.', array(
        '%type' => $info['label'],
      )),
    );
    $perms["edit fieldable {$bundle}"] = array(
      'title' => t('Edit %type', array(
        '%type' => $info['label'],
      )),
      'description' => t('Allows users to edit fieldable panel pane entities of bundle %type. This is a minimum permission; it is required to be able to edit a fieldable pane at all, but higher access requirements on an individual pane can override it.', array(
        '%type' => $info['label'],
      )),
    );
    $perms["delete fieldable {$bundle}"] = array(
      'title' => t('Delete %type', array(
        '%type' => $info['label'],
      )),
      'description' => t('Allows users to delete fieldable panel pane entities of bundle %type. Users must also be able to edit the pane to delete it.', array(
        '%type' => $info['label'],
      )),
    );
  }
  return $perms;
}

/**
 * Implements hook_theme().
 */
function fieldable_panels_panes_theme() {
  return array(
    'fieldable_panels_pane' => array(
      'render element' => 'elements',
      'template' => 'fieldable-panels-pane',
    ),
  );
}

/**
 * Implements hook_admin_menu_map().
 */
function fieldable_panels_panes_admin_menu_map() {
  $map = array();
  $bundle_keys = array();
  $info = entity_get_info('fieldable_panels_pane');
  foreach ($info['bundles'] as $bundle_name => $bundle_info) {
    $bundle_keys[] = $bundle_name;
  }
  $map['admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type'] = array(
    'parent' => 'admin/structure/fieldable-panels-panes',
    'arguments' => array(
      array(
        '%ctools_export_ui' => $bundle_keys,
      ),
    ),
  );
  return $map;
}

/**
 * Implements hook_admin_menu_map_alter().
 */
function fieldable_panels_panes_admin_menu_map_alter(array &$map) {
  if (!module_exists('field_ui')) {
    return;
  }

  // Add mapping for the individual fields if the Field UI module is enabled,
  // and the mappings are not already added by field_ui_admin_menu_map().
  $bundles = array();
  $info = entity_get_info('fieldable_panels_pane');
  foreach ($info['bundles'] as $bundle_name => $bundle_info) {
    if (isset($bundle_info['admin'])) {
      $admin_path = $bundle_info['admin']['path'];

      // Fields mapping may already be supported by field_ui_admin_menu_map()
      // so only add it if it doesn't exist.
      if (isset($map["{$admin_path}/fields/%field_ui_menu"])) {
        continue;
      }
      $fields = array();
      foreach (field_info_instances('fieldable_panels_pane', $bundle_name) as $field) {
        $fields[] = $field['field_name'];
      }
      if (!empty($fields)) {
        $map["{$admin_path}/fields/%field_ui_menu"]['parent'] = "{$admin_path}/fields";
        $map["{$admin_path}/fields/%field_ui_menu"]['arguments'][] = array(
          '%ctools_export_ui' => array(
            $bundle_name,
          ),
          '%field_ui_menu' => $fields,
        );
      }
    }
  }
}

/**
 * Implements hook_flush_caches().
 */
function fieldable_panels_panes_flush_caches() {
  return array(
    'cache_entity_fieldable_panels_pane',
  );
}

/**
 * Implements hook_panels_dashboard_blocks().
 */
function fieldable_panels_panes_panels_dashboard_blocks(&$vars) {
  ctools_include('export');
  $vars['links']['fieldable_panels_panes'] = array(
    'title' => l(t('Fieldable Panels Panes'), 'admin/structure/fieldable-panels-panes'),
    'description' => t('Fieldable Panels Panes are fieldable entities that can be created directly in the Panels UI or created in a separate administrative UI to reuse later in a panel pane.'),
  );
  $count = 0;
  $rows = array();
  foreach (ctools_export_crud_load_all('fieldable_panels_pane_type') as $type) {
    $items = array(
      $type->title,
      l(t('List'), 'admin/structure/fieldable-panels-panes/' . $type->name),
      l(t('Edit'), 'admin/structure/fieldable-panels-panes/' . $type->name . '/edit'),
      l(t('Add'), 'admin/structure/fieldable-panels-panes/' . $type->name . '/add'),
    );
    if (module_exists('field_ui')) {
      $items[] = l(t('Manage Fields'), 'admin/structure/fieldable-panels-panes/' . $type->name . '/fields');
      $items[] = l(t('Manage Display'), 'admin/structure/fieldable-panels-panes/' . $type->name . '/display');
    }
    $rows[] = $items;

    // Only display 10.
    if (++$count >= 10) {
      break;
    }
  }
  if ($rows) {
    $content = theme('table', array(
      'rows' => $rows,
      'attributes' => array(
        'class' => 'panels-manage',
      ),
    ));
  }
  else {
    $content = '<p>' . t('There are no fieldable panel pane types.') . '</p>';
  }
  $vars['blocks']['fieldable_panels_panes'] = array(
    'weight' => -100,
    'title' => t('Manage fieldable panels panes'),
    'link' => l(t('Go to list'), 'admin/structure/fieldable-panels-panes'),
    'content' => $content,
    'class' => 'dashboard-fieldable-panels-panes',
    'section' => 'right',
  );
}

// -------------------------------------------------------------------------
// Menu callbacks for things like titles, access control, etc.

/**
 * Properly format the type from the URL version to the internal version.
 */
function fieldable_panels_pane_type_load($type) {

  // If the type is not set or equals 'view', return FALSE. This appears to be
  // caused by incorrect menu item ordering, but it is still unclear why it
  // happens.
  // @todo Work out the root cause of this happening.
  if (empty($type) || $type == 'view') {
    return FALSE;
  }
  $type = str_replace('-', '_', $type);
  $entity_info = entity_get_info('fieldable_panels_pane');
  if (isset($entity_info['bundles'][$type])) {
    return $type;
  }
  else {

    // Special handling for two legacy paths:
    // * admin/structure/fieldable-panels-panes/manage/FPPTYPE/fields
    // * admin/structure/fieldable-panels-panes/manage/FPPTYPE/display
    // Redirect these paths as appropriate. Need to be extra verbose here to
    // avoid breaking other paths.
    if ($type == 'manage' && arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'fieldable-panels-panes' && (arg(5) == 'fields' || arg(5) == 'display')) {
      drupal_goto('admin/structure/fieldable-panels-panes/' . arg(4) . '/' . arg(5));
    }
    else {

      // If nothing else, return false.
      return FALSE;
    }
  }
}

/**
 * @depricated Backwards compatability layer.
 *
 * @see fieldable_panels_pane_type_load()
 */
function fieldable_panels_panes_type_load($type) {
  return fieldable_panels_pane_type_load($type);
}

/**
 * Provide a safe title for an entity pane type based upon the URL.
 */
function fieldable_panels_panes_entities_title($type) {
  $type = str_replace('-', '_', $type);
  $entity_info = entity_get_info('fieldable_panels_pane');
  if (isset($entity_info['bundles'][$type])) {
    return $entity_info['bundles'][$type]['label'];
  }
}

/**
 * Title callback for fieldable panels panes exportable items.
 *
 * @param object $item
 *   A %ctools_export_ui object.
 *
 * @return string
 *   The title.
 */
function fieldable_panels_pane_type_title($item) {
  return $item->title;
}

/**
 * Provide a safe title for an entity from the entity.
 */
function fieldable_panels_panes_entity_title($entity) {
  if (!empty($entity->admin_title)) {
    return $entity->admin_title;
  }
  if (!empty($entity->title)) {
    return $entity->title;
  }
  return t('No title');
}

/**
 * Access callback to set a revision of a fieldable panel pane as current.
 *
 * @param object $entity
 *   A Fieldable Panels Pane object.
 *
 * @return bool
 *   TRUE if the specific revision of the panel pane can be set as the current
 *   revision, otherwise, FALSE.
 */
function fieldable_panels_panes_entity_make_current_access($entity) {
  return fieldable_panels_panes_access('update', $entity) && $entity->vid != $entity->current_vid;
}

// -------------------------------------------------------------------------
// CTools hooks.

/**
 * Implements hook_ctools_plugin_directory().
 */
function fieldable_panels_panes_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'ctools' && $plugin_type == 'export_ui') {
    return 'plugins/' . $plugin_type;
  }
  if ($owner == 'ctools' && $plugin_type == 'content_types') {
    return 'plugins/' . $plugin_type;
  }
  if ($owner == 'panelizer' && defined('PANELIZER_VERSION') && version_compare(PANELIZER_VERSION, '3.0', '>=')) {
    return 'plugins/' . $plugin_type;
  }
}

/**
 * Check if the user has 'update' access for an FPP object.
 *
 * @param mixed $argument
 *   Should contain an array element "entity_id" with the ID of an FPP object.
 *
 * @return bool
 *   Whether or not the user has access to update an FPP object.
 */
function fieldable_panels_panes_check_access_update($argument) {

  // If the argument is empty, either an empty string or an empty array, then
  // CTools is most likely processing a deleted FPP.
  if (empty($argument)) {
    return FALSE;
  }

  // If the argument passed in as an array, try looking for an 'entity_id'
  // element, it should contain the ID of the item that's needed.
  $id = '';
  if (is_array($argument)) {
    if (isset($argument['entity_id'])) {
      $id = $argument['entity_id'];
    }
    else {
      $id = '';
    }
  }
  else {
    $id = $argument;
  }

  // The id should be in the format 'fpid:123', 'vid:123' or 'vuuid:123'.
  if (empty($id) || strpos($id, ':') === FALSE) {
    return FALSE;
  }

  // Try loading the FPP via the ID.
  $entity = fieldable_panels_panes_load_from_subtype_force($id);

  // If either the FPP couldn't be loaded, or the user does not have 'update'
  // access to the FPP, then the visitor does not have access.
  if (empty($entity) || !fieldable_panels_panes_access('update', $entity)) {
    return FALSE;
  }

  // Getting to this point means that the visitor does have access to edit the
  // FPP.
  return TRUE;
}

/**
 * Implement CTools access form caching callback: get.
 */
function fieldable_panels_panes_ctools_access_get($argument) {
  if (!fieldable_panels_panes_check_access_update($argument)) {
    return;
  }
  list($op, $fpid) = explode(':', $argument);
  $entity = fieldable_panels_panes_load($fpid);

  // First, see if there's a cache:
  ctools_include('object-cache');
  $access = ctools_object_cache_get('fieldable_panels_panes', $argument);
  if (!$access) {
    $access = $entity->{$op . '_access'};
  }
  $context = fieldable_panels_panes_get_base_context($entity);
  return array(
    $access,
    $context,
  );
}

/**
 * Implement CTools access form caching callback: set.
 */
function fieldable_panels_panes_ctools_access_set($argument, $access) {
  if (!fieldable_panels_panes_check_access_update($argument)) {
    return;
  }
  ctools_include('object-cache');
  ctools_object_cache_set('fieldable_panels_panes', $argument, $access);
}

/**
 * Implement CTools access form caching callback: get.
 */
function fieldable_panels_panes_ctools_access_clear($argument) {
  if (!fieldable_panels_panes_check_access_update($argument)) {
    return;
  }
  ctools_include('object-cache');
  ctools_object_cache_clear('fieldable_panels_panes', $argument);
}

// -------------------------------------------------------------------------
// Views hooks.

/**
 * Implements hook_views_api().
 */
function fieldable_panels_panes_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'fieldable_panels_panes') . '/plugins/views',
  );
}

// -------------------------------------------------------------------------
// Block hooks.

/**
 * Implements hook_block_info().
 */
function fieldable_panels_panes_block_info() {
  $blocks = array();

  // Get array of exposed FPP bundles.
  $bundles = fieldable_panels_panes_exposed_bundles();
  if (variable_get('fpp_blocks_expose', FALSE) == TRUE) {

    // Get all reusable entities if $bundles array is empty.
    if (empty($bundles)) {
      $ids = db_query('SELECT fpid FROM {fieldable_panels_panes} WHERE reusable = 1')
        ->fetchCol();
    }
    else {
      $ids = array();
      foreach ($bundles as $bundle => $info) {
        $bundle_ids = db_query('SELECT fpid FROM {fieldable_panels_panes} WHERE reusable = 1 AND bundle = :bundle', array(
          ':bundle' => $bundle,
        ))
          ->fetchCol();
        $ids = array_merge($ids, $bundle_ids);
      }
    }
    $entities = fieldable_panels_panes_load_multiple($ids);
    foreach ($entities as $entity) {
      $blocks[$entity->fpid]['info'] = 'FPP (' . $entity->bundle . '): ' . entity_label('fieldable_panels_pane', $entity);
    }
  }
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function fieldable_panels_panes_block_view($delta = '') {
  $block = array();

  // Get array of exposed FPP bundles.
  $bundles = fieldable_panels_panes_exposed_bundles();
  if (variable_get('fpp_blocks_expose', FALSE) == TRUE) {
    $entity = fieldable_panels_panes_load($delta);
    $bundle = $entity->bundle;
    $block['subject'] = '';
    $block['content'] = '';

    // Render block if its FPP bundle is exposed and block is reusable.
    if ((array_key_exists($bundle, $bundles) || empty($bundles)) && $entity->reusable == TRUE) {
      $content = fieldable_panels_panes_view($entity);
      $block['subject'] = check_plain($entity->title);
      $block['content'] = $content;
    }
  }
  return $block;
}

/**
 * Return array of FPP bundles exposed as blocks.
 */
function fieldable_panels_panes_exposed_bundles() {

  // Get array of all FPP bundles and unset bundles not exposed as blocks.
  $bundles = fieldable_panels_panes_get_bundle_labels();
  foreach ($bundles as $bundle => $info) {
    $expose = variable_get('fpp_expose_' . $bundle, FALSE);
    if ($expose == FALSE) {
      unset($bundles[$bundle]);
    }
  }
  return $bundles;
}

// -------------------------------------------------------------------------
// Theming.

/**
 * Preprocess function for fieldable-panels-pane.tpl.php.
 */
function template_preprocess_fieldable_panels_pane(&$vars) {
  $vars += array(
    'content' => array(),
  );
  foreach (element_children($vars['elements']) as $key) {
    $vars['content'][$key] = $vars['elements'][$key];
  }

  // Make the field variables available with the appropriate language.
  field_attach_preprocess('fieldable_panels_pane', $vars['elements']['#element'], $vars['content'], $vars);
  $vars['theme_hook_suggestions'][] = 'fieldable_panels_pane__' . $vars['elements']['#element']->bundle;
}

/**
 * Add a class for our bundle to the normal panels pane theme.
 */
function fieldable_panels_panes_preprocess_panels_pane(&$vars) {
  if ($vars['pane']->type == 'fieldable_panels_pane') {
    if (!empty($vars['content']['#fieldable_panels_pane']) && is_object($vars['content']['#fieldable_panels_pane'])) {
      $entity = $vars['content']['#fieldable_panels_pane'];
      if (!empty($entity->link) && !empty($vars['title'])) {
        $vars['title'] = l($vars['title'], $entity->path, array(
          'html' => TRUE,
        ));
      }
      ctools_include('cleanstring');
      $vars['classes_array'][] = 'pane-bundle-' . ctools_cleanstring($entity->bundle, array(
        'lower case' => TRUE,
      ));

      // Add pane-fpid-[fpid] class when referenced by revision ID.
      if (strpos($vars['pane']->subtype, 'vid') === 0) {
        $vars['classes_array'][] = 'pane-fpid-' . $vars['content']['#fieldable_panels_pane']->fpid;
      }
    }
  }
}

/**
 * Process variables for fieldable-panels-pane.tpl.php.
 */
function fieldable_panels_panes_process_fieldable_panels_pane(&$variables) {
  if (module_exists('title')) {
    title_field_replacement_hide_label('fieldable_panels_pane', $variables['elements']['#element'], $variables['content']);
  }
}

// -------------------------------------------------------------------------
// Database and general entity API functions.

/**
 * Panel pane entity loader.
 *
 * @see entity_load()
 */
function fieldable_panels_panes_load($fpid, $vid = NULL) {
  if (!is_numeric($fpid)) {
    return FALSE;
  }
  $conditions = isset($vid) ? array(
    'vid' => $vid,
  ) : array();
  $entities = fieldable_panels_panes_load_multiple(array(
    $fpid,
  ), $conditions);
  return !empty($entities) ? reset($entities) : FALSE;
}

/**
 * Properly load the FPP entity via its subtype.
 *
 * @param string $subtype_name
 *   A string combining an indicator of the type of ID, e.g. 'fpid', 'vid' or
 *   'vuuid', and an integer ID, separated by a colon; e.g. "fpid:123".
 *
 * @return object|bool
 *   The requested FPP object, FALSE otherwise.
 */
function fieldable_panels_panes_load_from_subtype($subtype_name) {
  ctools_include('content');
  $plugin = ctools_get_content_type('fieldable_panels_pane');

  // Next, check to see how the subtype is configured.
  $subtype_info = ctools_content_get_subtype($plugin, $subtype_name);

  // If the FPP type doesn't exist then fail.
  if (!isset($subtype_info['bundle'])) {
    return FALSE;
  }

  // This means we're probably in the process of creating a new one.
  if (!isset($subtype_info['entity_id'])) {
    return fieldable_panels_panes_create(array(
      'bundle' => $subtype_info['bundle'],
    ));
  }

  // And try it this way.
  if (isset($subtype_info['entity_id'])) {
    return fieldable_panels_panes_load_from_subtype_force($subtype_info['entity_id']);
  }

  // Finally, try this:
  return fieldable_panels_panes_load_from_subtype_force($subtype_name);
}

/**
 * Properly load the entity via $subtype_name.
 *
 * @param string $subtype_name
 *   A string combining an indicator of the type of ID, e.g. 'fpid', 'vid',
 *   'uuid', 'vuuid' or 'current, and an integer ID, separated by a colon; e.g.
 *   "fpid:123". If the type is 'current' the newest revision will be loaded,
 *   'fpid' and 'uuid' will load the revision considered best, 'vid' and 'vuuid'
 *   will load a specific revision. Both 'uuid' and 'vuuid' require the UUID
 *   module to be present.
 *
 * @return object
 *   The requested FPP object.
 */
function fieldable_panels_panes_load_from_subtype_force($subtype_name) {
  $object = NULL;

  // Split the string up into the object type and the ID.
  list($type, $id) = explode(':', $subtype_name);

  // UUID integration.
  if ($type == 'uuid' && module_exists('uuid')) {
    $ids = entity_get_id_by_uuid('fieldable_panels_pane', array(
      $id,
    ));
    if ($object = entity_load('fieldable_panels_pane', $ids)) {
      $object = reset($object);
    }
  }
  elseif ($type == 'vuuid' && module_exists('uuid')) {
    $vids = entity_get_id_by_uuid('fieldable_panels_pane', array(
      $id,
    ), TRUE);
    if ($vids && ($content = entity_load('fieldable_panels_pane', FALSE, array(
      'vid' => reset($vids),
    )))) {
      $object = reset($content);
    }
  }
  elseif ($type == 'vid') {
    $fpid = db_query('SELECT fpid FROM {fieldable_panels_panes_revision} WHERE vid = :vid', array(
      ':vid' => $id,
    ))
      ->fetchField();
    $object = fieldable_panels_panes_load($fpid, $id);
  }
  elseif ($type == 'current') {
    $vid = db_query('SELECT MAX(vid) FROM {fieldable_panels_panes_revision} WHERE fpid = :fpid', array(
      ':fpid' => $id,
    ))
      ->fetchField();
    $object = fieldable_panels_panes_load($id, $vid);
  }
  else {
    $object = fieldable_panels_panes_load($id);
  }
  return $object;
}

/**
 * Load multiple fieldable panel panes.
 *
 * @see entity_load_multiple()
 */
function fieldable_panels_panes_load_multiple($ids, $conditions = array(), $reset = FALSE) {
  return entity_load('fieldable_panels_pane', $ids, $conditions, $reset);
}

/**
 * Save a fieldable panel pane.
 *
 * @see node_save()
 */
function fieldable_panels_panes_save($entity) {
  return entity_get_controller('fieldable_panels_pane')
    ->save($entity);
}

/**
 * Delete a fieldable panel pane.
 *
 * @param int $fpid
 *   A fieldable panel pane ID.
 */
function fieldable_panels_panes_delete($fpid) {
  fieldable_panels_panes_delete_multiple(array(
    $fpid,
  ));
}

/**
 * Delete a revision for a fieldable panel pane.
 *
 * @param int $fpid
 *   A fieldable panel pane ID.
 * @param int $vid
 *   The revision id to delete.
 *
 * @return bool
 *   Indicates whether the object was successfully deleted or not.
 */
function fieldable_panels_panes_delete_revision($fpid, $vid) {
  if ($revision = fieldable_panels_panes_load($fpid, $vid)) {

    // Prevent deleting the current revision.
    $entity = fieldable_panels_panes_load($revision->fpid);
    if (empty($entity) || empty($entity->vid)) {
      return FALSE;
    }
    db_delete('fieldable_panels_panes_revision')
      ->condition('fpid', $revision->fpid)
      ->condition('vid', $revision->vid)
      ->execute();
    field_attach_delete_revision('fieldable_panels_pane', $revision);
    return TRUE;
  }
  return FALSE;
}

/**
 * Delete multiple fieldable panel panes.
 *
 * @param array $fpids
 *   An array of fieldable panel pane IDs.
 */
function fieldable_panels_panes_delete_multiple(array $fpids) {
  return entity_get_controller('fieldable_panels_pane')
    ->delete($fpids);
}

/**
 * View a fieldable panel pane.
 *
 * @see node_view()
 */
function fieldable_panels_panes_view($entity, $view_mode = 'full', $langcode = NULL) {

  // Defer to the other function.
  return fieldable_panels_pane_view($entity, $view_mode, $langcode);
}

/**
 * Entity API callback to view a fieldable panel pane.
 *
 * This is essentially a duplicate of fieldable_panels_panes_view() but the
 * function name has to match the entity type with is singular.
 *
 * @see entity_view()
 */
function fieldable_panels_pane_view($entity, $view_mode = 'full', $langcode = NULL) {
  return entity_get_controller('fieldable_panels_pane')
    ->view($entity, $view_mode, $langcode);
}

/**
 * Callback to create a new entity.
 */
function fieldable_panels_panes_create($values = array()) {
  return entity_get_controller('fieldable_panels_pane')
    ->create($values);
}

/**
 * Determine if a user has access to a fieldable panel pane entity.
 */
function fieldable_panels_panes_access($op, $entity = NULL, $account = NULL) {
  return entity_get_controller('fieldable_panels_pane')
    ->access($op, $entity, $account);
}

/**
 * Get the safe human readable name of an entity bundle.
 */
function fieldable_panels_panes_get_bundle_label($bundle) {
  $entity_info = entity_get_info('fieldable_panels_pane');
  if (empty($entity_info['bundles'][$bundle]['label'])) {
    $output = t('Unknown content type');
  }
  return $entity_info['bundles'][$bundle]['label'];
}

/**
 * Get an array of entity bundle names, suitable for an options form.
 */
function fieldable_panels_panes_get_bundle_labels() {
  $bundles = array();
  $entity_info = entity_get_info('fieldable_panels_pane');
  foreach ($entity_info['bundles'] as $bundle => $info) {
    $bundles[$bundle] = $info['label'];
  }
  asort($bundles);
  return $bundles;
}

/**
 * Get a "base" context for this pane.
 *
 * This is used to provide the base context for access control.
 */
function fieldable_panels_panes_get_base_context($entity = NULL) {
  ctools_include('context');
  if ($entity) {
    $context = ctools_context_create('entity:fieldable_panels_pane', $entity);
  }
  else {
    $context = ctools_context_create_empty('entity:fieldable_panels_pane');

    // The placeholder is needed to create the form used for the live
    // preview.
    $context->placeholder = array(
      'type' => 'context',
      'conf' => array(
        'name' => 'fieldable_panels_pane',
        'identifier' => t('This entity'),
        'keyword' => 'pane',
        'context_settings' => array(),
      ),
    );
  }
  $context->identifier = t('This entity');
  $context->keyword = 'pane';
  return array(
    'fieldable-pane' => $context,
  );
}

/**
 * Basic edit form for the pane entity.
 *
 * The entity being edited should be stored in $form_state['entity']
 * when this form is built.
 */
function fieldable_panels_panes_entity_edit_form($form, &$form_state) {
  $entity = $form_state['entity'];

  // Map these properties for entity translations.
  $form['#entity_type'] = array(
    '#type' => 'value',
    '#value' => $entity->bundle,
  );
  $form_state['fieldable_panels_pane'] = $form_state['entity'];
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => $entity->title,
    '#weight' => -10,
  );
  if (module_exists('locale')) {
    $form['language'] = array(
      '#type' => 'select',
      '#title' => t('Language'),
      '#default_value' => $entity->language,
      '#options' => array(
        LANGUAGE_NONE => t('Language neutral'),
      ) + locale_language_list('name'),
    );
  }
  else {
    $form['language'] = array(
      '#type' => 'value',
      '#value' => $entity->language,
    );
  }
  $form['link'] = array(
    '#type' => 'container',
    '#states' => array(
      'visible' => array(
        ':input[name="title"]' => array(
          'empty' => FALSE,
        ),
      ),
    ),
  );
  $form['link']['link'] = array(
    '#title' => t('Make title a link'),
    '#type' => 'checkbox',
    '#default_value' => $entity->link,
    '#description' => t('Check here to make the title link to another page.'),
  );
  $form['link']['path'] = array(
    '#type' => 'textfield',
    '#title' => t('Path'),
    '#description' => t('The path for this link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array(
      '%front' => '<front>',
      '%add-node' => 'node/add',
      '%drupal' => 'http://drupal.org',
    )),
    '#states' => array(
      'visible' => array(
        ':input[name="link"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
    '#default_value' => $entity->path,
    '#maxlength' => 255,
  );
  $form['additional_settings'] = array(
    '#type' => 'vertical_tabs',
    '#weight' => 99,
  );
  $form['admin'] = array(
    '#type' => 'fieldset',
    '#title' => t('Admin'),
    '#collapsible' => TRUE,
    '#collapsed' => empty($entity->admin),
    '#group' => 'additional_settings',
    '#attributes' => array(
      'class' => array(
        'fieldable-pane-pane-form-admin-information',
      ),
    ),
    '#attached' => array(
      'js' => array(
        'vertical-tabs' => drupal_get_path('module', 'fieldable_panels_panes') . '/fieldable_panels_panes.vertical-tabs.js',
      ),
    ),
    '#weight' => 10,
  );
  $form['reusable'] = array(
    '#type' => 'fieldset',
    '#title' => t('Reusability'),
    '#collapsible' => TRUE,
    '#collapsed' => empty($entity->reusable),
    '#group' => 'additional_settings',
    '#attributes' => array(
      'class' => array(
        'fieldable-pane-pane-form-reusable-information',
      ),
    ),
    '#attached' => array(
      'js' => array(
        'vertical-tabs' => drupal_get_path('module', 'fieldable_panels_panes') . '/fieldable_panels_panes.vertical-tabs.js',
      ),
    ),
    '#weight' => 10,
    // Cannot change this after the initial creation, but reusable FPPs can be
    // have their category changed.
    '#access' => empty($entity->fpid) || !empty($entity->fpid) && !empty($entity->reusable),
  );
  $form['reusable']['reusable'] = array(
    '#type' => 'checkbox',
    '#title' => t('Make this entity reusable'),
    '#default_value' => $entity->reusable,
    '#description' => t('A reusable pane may be used multiple times on the same page or on other pages. A non-reusable pane may not be added to another page once it is created and added to this page. This option may not be changed after the pane is created.'),
    // It's dangerous to change an FPP's reusability after it is created, so
    // this is disabled when editing existing FPPs.
    '#disabled' => !empty($entity->fpid),
  );
  $form['reusable']['category'] = array(
    '#type' => 'textfield',
    '#title' => t('Category'),
    '#description' => t('The category this content will appear in the "Add content" modal. If left blank the category will be "Miscellaneous".'),
    '#default_value' => $entity->category,
    '#states' => array(
      'visible' => array(
        ':input[name="reusable"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['admin']['admin_title'] = array(
    '#type' => 'textfield',
    '#title' => t('Administrative title'),
    '#description' => t("Title used for administrative purposes. Used to identify panes within admin pages."),
    '#default_value' => $entity->admin_title,
  );
  $form['admin']['admin_description'] = array(
    '#type' => 'textarea',
    '#title' => t('Administrative description'),
    '#description' => t('A description of what this content is, does or is for, for administrative use.'),
    '#default_value' => $entity->admin_description,
  );
  $form['revision'] = array(
    '#type' => 'fieldset',
    '#title' => t('Revision'),
    '#collapsible' => TRUE,
    '#collapsed' => empty($entity->revision),
    '#group' => 'additional_settings',
    '#attributes' => array(
      'class' => array(
        'fieldable-pane-pane-form-revision-information',
      ),
    ),
    '#attached' => array(
      'js' => array(
        'vertical-tabs' => drupal_get_path('module', 'fieldable_panels_panes') . '/fieldable_panels_panes.vertical-tabs.js',
      ),
    ),
    '#weight' => 30,
    // Don't show the revision options if this is a new FPP.
    '#access' => !empty($entity->fpid),
  );
  $form['revision']['revision'] = array(
    '#type' => 'checkbox',
    '#title' => t('Create new revision'),
    '#default_value' => 1,
  );

  // Force a new revision to be created if the user does not have 'admin FPP'
  // access, or the 'lock' mode was enabled and this is not a reusable FPP.
  if (!user_access('administer fieldable panels panes') || $entity->vid != $entity->current_vid || !empty($entity->fpid) && empty($entity->reusable) && variable_get('fpp_revision_locking', 'lock') == 'lock') {
    $form['revision']['revision']['#disabled'] = TRUE;
    $form['revision']['revision']['#value'] = TRUE;

    // Inform the user that the pane will be locked.
    if (!empty($entity->fpid) && empty($entity->reusable) && variable_get('fpp_revision_locking', 'lock') == 'lock') {
      $form['revision']['revision']['#description'] = t('A new revision will be created because this pane is non-reusable and the locking mode has been enabled.');
    }
  }
  $form['revision']['log'] = array(
    '#type' => 'textarea',
    '#title' => t('Log message'),
    '#description' => t('Provide an explanation of the changes being made.'),
    '#default_value' => '',
    '#states' => array(
      'visible' => array(
        ':input[name="revision"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );

  // Override reusable pane settings to be changed after the pane is created.
  if (variable_get('fpp_allow_reusable_access', FALSE)) {

    // Reset field access.
    unset($form['reusable']['#access']);
    $form['reusable']['reusable']['#description'] = t('A reusable pane may be used multiple times on the same page or on other pages. A non-reusable pane may not be added to another page once it is created and added to this page. Once this option has been checked it cannot be turned off.');
    $form['reusable']['reusable']['#disabled'] = !empty($form_state['entity']->reusable);
  }
  $language = NULL;
  if (function_exists('entity_language')) {

    // entity_language() was added in Drupal 7.15.
    $language = entity_language('fieldable_panels_pane', $entity);
  }
  field_attach_form('fieldable_panels_pane', $entity, $form, $form_state, $language);
  if (!empty($form_state['add submit'])) {
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Save'),
    );
  }
  return $form;
}

/**
 * Validate callback for the pane entity.
 */
function fieldable_panels_panes_entity_edit_form_validate($form, &$form_state) {
  field_attach_form_validate('fieldable_panels_pane', $form_state['entity'], $form, $form_state);
}

/**
 * Submit callback for the pane entity.
 */
function fieldable_panels_panes_entity_edit_form_submit($form, &$form_state) {
  $entity = $form_state['entity'];

  // Copy hardcoded fields.
  $entity->title = $form_state['values']['title'];
  $entity->link = $form_state['values']['link'];
  $entity->path = $form_state['values']['path'];
  $entity->language = $form_state['values']['language'];
  $entity->reusable = $form_state['values']['reusable'];
  $entity->category = $form_state['values']['category'];
  $entity->admin_title = $form_state['values']['admin_title'];
  $entity->admin_description = $form_state['values']['admin_description'];
  $entity->revision = $form_state['values']['revision'];

  // Only set a log message if there was a new revision. This prevents
  // overwriting a log message on the current revision.
  if ($entity->revision) {
    $entity->log = $form_state['values']['log'];
  }
  field_attach_submit('fieldable_panels_pane', $entity, $form, $form_state);
  fieldable_panels_panes_save($entity);
  if (!empty($form_state['add submit'])) {
    drupal_set_message(t('The entity has been saved.'));
  }
}

/**
 * Returns whether the current page is the preview view of the passed-in pane.
 *
 * @param object $pane
 *   A fieldable panel pane object.
 *
 * @return bool
 *   TRUE if this is a full pane page view, otherwise FALSE.
 */
function fieldable_panels_pane_is_page($pane) {
  $page_page = menu_get_object('fieldable_panels_panes', 4);
  return !empty($page_page) ? $page_page->fpid == $pane->fpid : FALSE;
}

/**
 * Entity property callback.
 */
function fieldable_panels_panes_metadata_fpp_get_properties($entity, array $options, $name, $entity_type) {
  if ($name == 'path') {
    return url($entity->path);
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for ctools_export_ui_edit_item_form().
 */
function fieldable_panels_panes_form_ctools_export_ui_edit_item_form_alter(&$form, &$form_state, $form_id) {

  // Minor improvements to the FPP bundle/type form.
  if (isset($form_state['plugin']['name']) && $form_state['plugin']['name'] == 'fieldable_panels_pane') {
    $form['info']['description']['#description'] = t('The administrative description of this type. Also used as the icon tooltip when attempting to add an item of this type via the Panels interface.');
  }
}

/**
 * Callback function for the FPP entity label.
 */
function fieldable_panels_panes_entity_label_callback($entity, $type) {

  // First, try the real label.
  if (!empty($entity->admin_title)) {
    return $entity->admin_title;
  }

  // Then, the user visible title.
  if (!empty($entity->title)) {
    return $entity->title;
  }

  // Then, the bundle label with FPID.
  $info = entity_get_info($type);
  if (!empty($info['bundles'][$entity->bundle]['label'])) {
    return t('@label (id: @fpid)', array(
      '@label' => $info['bundles'][$entity->bundle]['label'],
      '@fpid' => $entity->fpid,
    ));
  }

  // Finally, the ultimate fallback: the entity name with bundle and FPID.
  return t('@label (type: @bundle, id: @fpid)', array(
    // FYI, we use $info['label'] rather than the literal string in case it was
    // translated.
    '@label' => $info['label'],
    '@bundle' => $entity->bundle,
    '@fpid' => $entity->fpid,
  ));
}

/**
 * Callback function for the FPP entity uri.
 */
function fieldable_panels_panes_entity_uri_callback($entity) {
  return array(
    'path' => 'admin/structure/fieldable-panels-panes/view/' . $entity->fpid,
    'options' => array(),
  );
}

/**
 * Implements hook_views_plugins().
 */
function fieldable_panels_panes_views_plugins() {
  $plugins = array(
    'access' => array(
      'fieldable_panels_panes_view_access_plugin' => array(
        'title' => t('Fieldable Panels Panes access check'),
        'help' => t('This access plugin checks if a user has either the "administer fieldable panels panes" or the "access fieldable panels panes master list" permission.'),
        'handler' => 'fieldable_panels_panes_access_plugin',
        'path' => drupal_get_path('module', 'fieldable_panels_panes'),
        'no ui' => TRUE,
      ),
    ),
  );
  return $plugins;
}

/**
 * Implements hook_file_download_access().
 */
function fieldable_panels_panes_file_download_access($field, $entity_type, $entity) {
  if ($entity_type == 'fieldable_panels_pane') {

    // Use the CTools API to check access.
    ctools_include('context');
    return ctools_access($entity->view_access, fieldable_panels_panes_get_base_context($entity));
  }
}

/**
 * Determine if an FPP's revision should be locked.
 *
 * @param object $entity
 *   The FPP object being examined.
 *
 * @return bool
 *   Whether the revision can be locked.
 */
function fieldable_panels_panes_revision_is_lockable($entity) {
  $lock = variable_get('fpp_revision_locking', 'lock');

  // Only non-reusable FPPs will be lockable.
  if ($lock == 'lock') {
    $revision_context_aware = empty($entity->reusable);
  }
  else {
    $revision_context_aware = FALSE;
  }
  return $revision_context_aware;
}

/**
 * Implements hook_entity_insert().
 */
function fieldable_panels_panes_entity_insert($entity, $type) {
  if ($type == 'fieldable_panels_pane') {
    $cid = "fieldable_panels_panes_{$entity->bundle}_content_type";
    cache_clear_all($cid, 'cache_panels');
  }
}

/**
 * Implements hook_entity_update().
 */
function fieldable_panels_panes_entity_update($entity, $type) {
  if ($type == 'fieldable_panels_pane') {
    $cid = "fieldable_panels_panes_{$entity->bundle}_content_type";
    cache_clear_all($cid, 'cache_panels');
  }
}

/**
 * Implements hook_entity_delete().
 */
function fieldable_panels_panes_entity_delete($entity, $type) {
  if ($type == 'fieldable_panels_pane') {
    $cid = "fieldable_panels_panes_{$entity->bundle}_content_type";
    cache_clear_all($cid, 'cache_panels');
  }
}

/**
 * Implements hook_panelizer_clone_panelizer().
 */
function fieldable_panels_panes_panelizer_clone_panelizer(&$panelizer) {
  foreach ($panelizer->display->content as $pid => $pane) {
    if ($pane->type == "fieldable_panels_pane") {
      $entity_info = entity_get_info('fieldable_panels_pane');
      $fpp = fieldable_panels_panes_load_from_subtype_force($pane->subtype);

      // Reusable FPPs do not get cloned.
      if ($fpp->reusable) {
        continue;
      }

      // Clone the FPP and make sure it will get saved as a new entity.
      $new_fpp = clone $fpp;

      // Reset the primary keys.
      $new_fpp->fpid = NULL;
      $new_fpp->vid = NULL;
      $new_fpp->vuuid = NULL;
      $new_fpp->uuid = NULL;
      $new_fpp->is_new = TRUE;

      // Reset timestamps.
      $new_fpp->created = NULL;
      $new_fpp->timestamp = NULL;

      // Make sure the status of a cloned exportable is custom.
      if (!empty($entity_info['exportable'])) {
        $status_key = isset($entity_info['entity keys']['status']) ? $entity_info['entity keys']['status'] : 'status';

        // Replaces ENTITY_CUSTOM because we cannot assume that the Entity API
        // module is installed.
        $new_fpp->{$status_key} = 0x1;
      }

      // Save the new FPP object and add it to the display.
      $new_fpp = fieldable_panels_panes_save($new_fpp);
      list($subtype_prefix, ) = explode(':', $pane->subtype);
      $key = $subtype_prefix == 'current' ? 'fpid' : $subtype_prefix;
      $pane->subtype = $subtype_prefix . ':' . $new_fpp->{$key};
    }
  }
}

Functions

Namesort descending Description
fieldable_panels_panes_access Determine if a user has access to a fieldable panel pane entity.
fieldable_panels_panes_access_callback Check if user has administrator or list permission.
fieldable_panels_panes_admin_menu_map Implements hook_admin_menu_map().
fieldable_panels_panes_admin_menu_map_alter Implements hook_admin_menu_map_alter().
fieldable_panels_panes_block_info Implements hook_block_info().
fieldable_panels_panes_block_view Implements hook_block_view().
fieldable_panels_panes_check_access_update Check if the user has 'update' access for an FPP object.
fieldable_panels_panes_create Callback to create a new entity.
fieldable_panels_panes_ctools_access_clear Implement CTools access form caching callback: get.
fieldable_panels_panes_ctools_access_get Implement CTools access form caching callback: get.
fieldable_panels_panes_ctools_access_set Implement CTools access form caching callback: set.
fieldable_panels_panes_ctools_plugin_directory Implements hook_ctools_plugin_directory().
fieldable_panels_panes_delete Delete a fieldable panel pane.
fieldable_panels_panes_delete_multiple Delete multiple fieldable panel panes.
fieldable_panels_panes_delete_revision Delete a revision for a fieldable panel pane.
fieldable_panels_panes_entities_title Provide a safe title for an entity pane type based upon the URL.
fieldable_panels_panes_entity_delete Implements hook_entity_delete().
fieldable_panels_panes_entity_edit_form Basic edit form for the pane entity.
fieldable_panels_panes_entity_edit_form_submit Submit callback for the pane entity.
fieldable_panels_panes_entity_edit_form_validate Validate callback for the pane entity.
fieldable_panels_panes_entity_info Implements hook_entity_info().
fieldable_panels_panes_entity_info_alter Implements hook_entity_info_alter().
fieldable_panels_panes_entity_insert Implements hook_entity_insert().
fieldable_panels_panes_entity_label_callback Callback function for the FPP entity label.
fieldable_panels_panes_entity_make_current_access Access callback to set a revision of a fieldable panel pane as current.
fieldable_panels_panes_entity_title Provide a safe title for an entity from the entity.
fieldable_panels_panes_entity_update Implements hook_entity_update().
fieldable_panels_panes_entity_uri_callback Callback function for the FPP entity uri.
fieldable_panels_panes_exposed_bundles Return array of FPP bundles exposed as blocks.
fieldable_panels_panes_field_extra_fields Implements hook_field_extra_fields().
fieldable_panels_panes_field_extra_fields_display_alter Implements hook_field_extra_fields_display_alter().
fieldable_panels_panes_file_download_access Implements hook_file_download_access().
fieldable_panels_panes_flush_caches Implements hook_flush_caches().
fieldable_panels_panes_form_ctools_export_ui_edit_item_form_alter Implements hook_form_FORM_ID_alter() for ctools_export_ui_edit_item_form().
fieldable_panels_panes_get_base_context Get a "base" context for this pane.
fieldable_panels_panes_get_bundle_label Get the safe human readable name of an entity bundle.
fieldable_panels_panes_get_bundle_labels Get an array of entity bundle names, suitable for an options form.
fieldable_panels_panes_hook_info Implements hook_hook_info().
fieldable_panels_panes_load Panel pane entity loader.
fieldable_panels_panes_load_from_subtype Properly load the FPP entity via its subtype.
fieldable_panels_panes_load_from_subtype_force Properly load the entity via $subtype_name.
fieldable_panels_panes_load_multiple Load multiple fieldable panel panes.
fieldable_panels_panes_menu Implements hook_menu().
fieldable_panels_panes_metadata_fpp_get_properties Entity property callback.
fieldable_panels_panes_module_implements_alter Implements hook_module_implements_alter().
fieldable_panels_panes_panelizer_clone_panelizer Implements hook_panelizer_clone_panelizer().
fieldable_panels_panes_panels_dashboard_blocks Implements hook_panels_dashboard_blocks().
fieldable_panels_panes_permission Implements hook_permission().
fieldable_panels_panes_preprocess_panels_pane Add a class for our bundle to the normal panels pane theme.
fieldable_panels_panes_process_fieldable_panels_pane Process variables for fieldable-panels-pane.tpl.php.
fieldable_panels_panes_revision_is_lockable Determine if an FPP's revision should be locked.
fieldable_panels_panes_save Save a fieldable panel pane.
fieldable_panels_panes_theme Implements hook_theme().
fieldable_panels_panes_type_load @depricated Backwards compatability layer.
fieldable_panels_panes_view View a fieldable panel pane.
fieldable_panels_panes_views_api Implements hook_views_api().
fieldable_panels_panes_views_plugins Implements hook_views_plugins().
fieldable_panels_pane_is_page Returns whether the current page is the preview view of the passed-in pane.
fieldable_panels_pane_type_load Properly format the type from the URL version to the internal version.
fieldable_panels_pane_type_title Title callback for fieldable panels panes exportable items.
fieldable_panels_pane_view Entity API callback to view a fieldable panel pane.
template_preprocess_fieldable_panels_pane Preprocess function for fieldable-panels-pane.tpl.php.