You are here

panopoly_magic.module in Panopoly Magic 7

Same filename and directory in other branches
  1. 8.2 panopoly_magic.module

File

panopoly_magic.module
View source
<?php

include_once 'panopoly_magic.features.inc';
define('PANOPOLY_ADD_PREVIEW_MANUAL', 0);
define('PANOPOLY_ADD_PREVIEW_AUTOMATIC', 1);
define('PANOPOLY_ADD_PREVIEW_DISABLED', 2);
define('PANOPOLY_ADD_PREVIEW_SINGLE', 3);
define('PANOPOLY_ADD_PREVIEW_DEFAULT', PANOPOLY_ADD_PREVIEW_SINGLE);

/**
 * The default list of view modes to exclude from pane configuration forms.
 */
define('PANOPOLY_MAGIC_HIDDEN_VIEW_MODE_OPTIONS', "rss\nsearch_index\nsearch_result\ntoken\npreview\ndiff_standard");

/**
 * Implements hook_init()
 */
function panopoly_magic_init() {

  // Override the Chaos Tools Modal Default Settings
  $default_style = array(
    'CToolsModal' => array(
      'modalSize' => array(
        'type' => 'scale',
        'width' => '.9',
        'height' => '.9',
        'addWidth' => 0,
        'addHeight' => 0,
        'contentRight' => 25,
        'contentBottom' => 75,
      ),
      'modalOptions' => array(
        'opacity' => '.55',
        'background-color' => '#FFF',
      ),
      'animationSpeed' => 'fast',
      'modalTheme' => 'CToolsModalDialog',
      'throbberTheme' => 'CToolsModalThrobber',
    ),
  );
  $modal_style = variable_get('panopoly_magic_modal_style', $default_style);
  drupal_add_js($modal_style, 'setting');

  // Tell Javascript which 'Add content' preview mode we're using.
  drupal_add_js(array(
    'panopoly_magic' => array(
      'pane_add_preview_mode' => _panopoly_magic_add_content_preview_mode_name(),
    ),
  ), 'setting');
}

/**
 * Implements hook_menu()
 */
function panopoly_magic_menu() {
  $items = array();
  $items['system/panopoly-magic'] = array(
    'title' => 'AHAH callback',
    'page callback' => 'panopoly_magic_ajax_form_callback',
    'delivery callback' => 'ajax_deliver',
    'access callback' => TRUE,
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
    'file path' => 'includes',
    'file' => 'form.inc',
  );
  return $items;
}

/**
 * Replaces system/ajax for pane configuration preview callback to work with
 * ctools multi step form.
 */
function panopoly_magic_ajax_form_callback() {
  list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
  ctools_include('content');
  ctools_include('modal');
  if (isset($form_state['no_redirect']) && !isset($form_state['form_info'])) {
    $form_state += array(
      're_render' => FALSE,
      'no_redirect' => !empty($form_state['ajax']),
    );
    $form = drupal_build_form($form_state['build_info']['form_id'], $form_state);
  }
  else {

    // These ensure the class files/form definations are included.
    $content_type = ctools_get_content_type($form_state['pane']->type);
    $subtype = ctools_content_get_subtype($content_type, $form_state['subtype_name']);
    $form = ctools_content_form($form_state['op'], $form_state['form_info'], $form_state, $form_state['plugin'], $form_state['subtype_name'], $form_state['conf'], $form_state['step']);
  }

  // Hack to set the 'triggering_element' when previewing field formatter
  // settings (where it strangely doesn't get set).
  if (empty($form_state['triggering_element']) && !empty($form_state['input']['_triggering_element_value']) && !empty($form_state['complete form']['buttons'])) {
    foreach ($form_state['complete form']['buttons'] as $button) {
      if (is_array($button) && isset($button['#value']) && $button['#value'] == $form_state['input']['_triggering_element_value']) {
        $form_state['triggering_element'] = $button;
      }
    }
  }
  if (!empty($form_state['triggering_element'])) {
    $callback = $form_state['triggering_element']['#ajax']['callback'];
  }
  if (!empty($callback) && function_exists($callback)) {
    return $callback($form, $form_state);
  }
}

/**
 * Implements hook_apps_app_info()
 */
function panopoly_magic_apps_app_info() {
  return array(
    'configure form' => 'panopoly_magic_configure_form',
  );
}

/**
 * Implements hook_theme().
 */
function panopoly_magic_theme($existing, $type, $theme, $path) {
  return array(
    'panopoly_magic_stylizer_preview_form' => array(
      'render element' => 'form',
    ),
    'panopoly_magic_preview' => array(
      'variables' => array(
        'title' => NULL,
        'add_link' => NULL,
        'preview' => NULL,
        'single' => FALSE,
      ),
      'template' => 'panopoly-magic-preview',
    ),
    'panopoly_magic_preview_link' => array(
      'variables' => array(
        'preview_link' => NULL,
        'add_link' => NULL,
        'description' => '',
      ),
    ),
  );
}

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

  // Include entity_field.inc from ctools to ensure that files can be properly removed
  // from within the panelizer edit forms.
  $items['file/ajax']['file'] = 'entity_field.inc';
  $items['file/ajax']['file path'] = drupal_get_path('module', 'ctools') . '/plugins/content_types/entity_context';
}

/**
 * Configuration Form for Panopoly Magic
 */
function panopoly_magic_configure_form($form, &$form_state) {
  $form = array();
  $form['panopoly_magic_live_preview'] = array(
    '#title' => t('Live Preview'),
    '#type' => 'select',
    '#required' => TRUE,
    '#options' => array(
      '1' => 'Automatic',
      '2' => 'Manual',
      '0' => 'Disabled',
    ),
    '#default_value' => variable_get('panopoly_magic_live_preview', 1),
    '#description' => t('Do you want Panopoly to provide a live preview for modal content through the in place editor?'),
  );
  $form['panopoly_magic_pane_add_preview'] = array(
    '#title' => t('Enable previews when adding panes'),
    '#type' => 'select',
    '#required' => TRUE,
    '#options' => array(
      PANOPOLY_ADD_PREVIEW_AUTOMATIC => 'Automatic',
      PANOPOLY_ADD_PREVIEW_MANUAL => 'Manual',
      PANOPOLY_ADD_PREVIEW_SINGLE => 'Single',
      PANOPOLY_ADD_PREVIEW_DISABLED => 'Disabled',
    ),
    '#default_value' => variable_get('panopoly_magic_pane_add_preview', PANOPOLY_ADD_PREVIEW_DEFAULT),
    '#description' => t('Do you want to automatically generate previews when adding existing panels panes through the IPE?'),
  );
  $form['panopoly_magic_pane_show_description'] = array(
    '#title' => t('Show pane descriptions when adding panes'),
    '#type' => 'checkbox',
    '#states' => array(
      'invisible' => array(
        ':input[name="panopoly_magic_pane_add_preview"]' => array(
          'value' => PANOPOLY_ADD_PREVIEW_DISABLED,
        ),
      ),
    ),
    '#default_value' => variable_get('panopoly_magic_pane_show_description', 1),
    '#description' => t('Do you want to show text descriptions for available panes in the Add Content dialog?'),
  );
  $form['panopoly_magic_show_panels_styles'] = array(
    '#title' => t('Show seldom used panels pane styles'),
    '#type' => 'select',
    '#required' => TRUE,
    '#options' => array(
      '1' => t('Yes'),
      '0' => t('No'),
    ),
    '#default_value' => variable_get('panopoly_magic_show_panels_styles', 0),
    '#description' => t('Do you want to show seldom used pane styles such as "no markup at all"?'),
  );
  $form['panopoly_magic_strip_js_from_preview'] = array(
    '#title' => t('Strip embedded .js from previews'),
    '#type' => 'select',
    '#required' => TRUE,
    '#options' => array(
      '1' => t('Enabled'),
      '0' => t('Disabled'),
    ),
    '#default_value' => variable_get('panopoly_magic_strip_js_from_preview', 0),
    '#description' => t('Some embedded javascript may cause issues with panopoly and the IPE, stripping the .js from your previews will fix this issue, but may alter the look of the preview.'),
  );
  $form['panopoly_magic_preview_use_legacy_fieldset'] = array(
    '#title' => t('Use legacy &lt;fieldset&gt; markup in preview'),
    '#type' => 'select',
    '#required' => TRUE,
    '#options' => array(
      '1' => t('Yes'),
      '0' => t('No'),
    ),
    '#default_value' => variable_get('panopoly_magic_preview_use_legacy_fieldset', 0),
    '#description' => t('The preview used to appear in a &lt;fieldset&gt;, however, that turned out to be problematic in some browsers. But if your theme depends on the old markup, you can enabled it.'),
  );
  $form['advanced'] = array(
    '#title' => t('Advanced'),
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['advanced']['panopoly_magic_hidden_view_mode_options'] = array(
    '#title' => t('Hidden view mode options'),
    '#type' => 'textarea',
    '#default_value' => variable_get('panopoly_magic_hidden_view_mode_options', PANOPOLY_MAGIC_HIDDEN_VIEW_MODE_OPTIONS),
    '#description' => t('List the machine names, one per line, of view modes you want removed from the list of possible view modes on widget configuration forms.'),
    '#rows' => 6,
  );
  return system_settings_form($form);
}

/**
 * Implements hook_module_implements_alter()
 */
function panopoly_magic_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'form_alter') {
    $group = $implementations['panopoly_magic'];
    unset($implementations['panopoly_magic']);
    $implementations['panopoly_magic'] = $group;
  }
}

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

  /**
   * Add a custom preview to the content type edit form and style edit form. This is applied in all cases, except for cases where
   * we are customizing an entity (i.e. fieldable panels panes) because we cannot reliable regenerate a preview without saving.
   */
  $extra_form_ids_with_preview = array(
    'panels_edit_style_settings_form',
    'panels_edit_style_type_form',
    'ctools_entity_field_content_type_formatter_options',
    'ctools_entity_field_content_type_formatter_styles',
  );
  if (variable_get('panopoly_magic_live_preview', 1) && (strpos($form_id, '_content_type_edit_form') || in_array($form_id, $extra_form_ids_with_preview))) {
    ctools_include('plugins', 'panels');
    ctools_include('content');

    // Get the Panels display and the current Pane (if there is one).
    $display = $form_state['display'];
    if ($form_id == 'panels_edit_style_settings_form') {
      if ($form_state['type'] == 'pane') {
        $pane = $display->content[$form_state['pid']];
      }
      else {

        // For region styles, we don't have a pane.
        $pane = NULL;
      }
    }
    else {
      $pane = $form_state['pane'];
    }

    // To store arbitrary extra data for generating previews.
    $extra = array();

    // Do some special processing for stylizer settings forms to ensure previews work correctly.
    if ($form_id == 'panels_edit_style_settings_form') {
      if (!empty($form_state['style']) && is_array($form_state['style'])) {
        $form_state['style'] = $form_state['style']['name'];
      }
      if (!empty($form_state['values']['settings'])) {
        $form_state['conf'] = $form_state['values']['settings'];
      }
      if (!empty($form_state['values']['settings'])) {
        $pane->style = array(
          'settings' => $form_state['values']['settings'],
          'style' => $form_state['style'],
        );
      }
    }

    // Determine the style
    if (strpos($form_id, '_content_type_edit_form') || $form_id == 'ctools_entity_field_content_type_formatter_options') {
      $style = isset($pane->style['style']) ? panels_get_style($pane->style['style']) : NULL;
    }
    elseif ($form_id == 'panels_edit_style_type_form') {
      $style = $form_state['rebuild'] && $form_id == 'panels_edit_style_type_form' ? panels_get_style($form_state['input']['style']) : panels_get_style($form_state['style']);
    }
    elseif ($form_id == 'panels_edit_style_settings_form') {
      $style = panels_get_style($form_state['style']);
    }
    else {
      $style = NULL;
    }

    // EXPERIEMENTAL: Handling Saving a Fieldable Panel Pane To Create a Live Preview. The trick
    // here is to make sure we properly save a version to get a vid, but don't make it the real one.
    // NOTE - I doubt this will be very good for performance. Let's call it RC1.
    $clicked_button = empty($form_state['triggering_element']['#value']) ? '' : $form_state['triggering_element']['#value'];
    if ($form_id == 'fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form' && ($clicked_button == 'Update Preview' || $form_state['op'] == 'add' && empty($form_state['input']['form_build_id']))) {

      // Run entity information through standard submission form if this is a new or updated object
      // Update the pane settngs with the VID afterwards
      if (!$form_state['entity']->reusable || $form_state['op'] != 'add' || $form_state['rebuild'] == TRUE) {

        // Process the entity to create a preview.
        $default_values = array(
          'language' => '',
          'title' => '',
          'link' => '',
          'path' => '',
          'reusable' => '',
          'category' => '',
          'admin_title' => '',
          'admin_description' => '',
          'revision' => '',
        );
        if (!empty($form_state['values'])) {
          $form_state['values'] = array_merge($default_values, $form_state['values']);
        }
        else {
          $form_state['values'] = $default_values;
        }

        //
        // This code is copied from fieldable_panels_panes_entity_edit_form_submit():
        //
        $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'];
        }

        //
        // End code copied from fieldable_panels_panes_entity_edit_form_submit().
        //
        if (!isset($entity->fpid)) {
          $entity->fpid = NULL;
        }

        // Process any field collection fields that may be present.
        $field_collection_fields = _panopoly_magic_process_field_collection_fields('fieldable_panels_pane', $entity->bundle, $entity, $form, $form_state);
        field_attach_form_validate('fieldable_panels_pane', $entity, $form, $form_state);
        field_attach_submit('fieldable_panels_pane', $entity, $form, $form_state);

        // Add back any field collection fields.
        foreach ($field_collection_fields as $field_collection_field => $field_collection_value) {
          $entity->{$field_collection_field} = $field_collection_value;
        }

        // Set the callback to use for rendering the preview.
        $preview_callback = 'panopoly_magic_render_fieldable_panels_pane_preview';

        // Stash the entity stub for the preview content type.
        $extra['fieldable_panels_pane'] = $entity;
      }
    }
    if ($form_id == 'fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form') {

      // Hide the revision fieldset.
      $form['revision']['#access'] = FALSE;

      // Hide the 'Admin' fieldset.
      $form['admin']['#access'] = FALSE;

      // Move the 'Admin title' under reusable like in FPP 1.8 and earlier.
      $form['reusable']['admin_title'] = $form['admin']['admin_title'];
      $form['reusable']['admin_title']['#states'] = array(
        'visible' => array(
          ':input[name="reusable"]' => array(
            'checked' => TRUE,
          ),
        ),
      );
      unset($form['admin']['admin_title']);

      // Move the warning down.
      $form['reusable']['warning']['#weight'] = 100;

      // Add our own submit handler ALWAYS, not just when we are generating
      // a preview.
      $form['#submit'][] = '_panopoly_magic_fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form_submit';
    }

    // Set the configuration and determine the content for the pane
    $configuration_ids = array(
      'ctools_entity_field_content_type_formatter_options',
      'ctools_entity_field_content_type_formatter_styles',
      'panels_edit_style_settings_form',
    );
    if (!empty($pane->configuration)) {
      $configuration = $form_state['rebuild'] && (strpos($form_id, 'content_type_edit_form') || in_array($form_id, $configuration_ids)) ? array_merge($pane->configuration, $form_state['input']) : $pane->configuration;
    }
    else {
      $configuration = $form_state['input'];
    }
    $keywords = array();
    $args = array();
    if (!empty($form_state['contexts'])) {
      $context = $form_state['contexts'];
    }
    elseif (!empty($form_state['display']->context)) {
      $context = $form_state['display']->context;
    }
    else {
      $context = array();
    }

    // Special case for panelizer fields being edited through the IPE. For some reason images are not being fully loaded
    if (!empty($pane->configuration['formatter']) && $pane->configuration['formatter'] == 'image') {
      list($entity_type, $field_name) = explode(':', $pane->subtype);
      if (!empty($form_state['entity']) && !empty($form_state['entity']->{$field_name})) {
        $form_state['entity']->{$field_name}[LANGUAGE_NONE][0] = array_merge((array) file_load($form_state['entity']->{$field_name}[LANGUAGE_NONE][0]['fid']), $form_state['entity']->{$field_name}[LANGUAGE_NONE][0]);
      }
      if (!empty($configuration['image_style'])) {
        $configuration['formatter_settings']['image_style'] = $configuration['image_style'];
        unset($configuration['image_style']);
      }
      if (!empty($configuration['image_link'])) {
        $configuration['formatter_settings']['image_link'] = $configuration['image_link'];
        unset($configuration['image_link']);
      }
    }

    // only render preview for Panes and not Regions
    if (isset($pane)) {

      // Creates preview outside of form itself to fix various bugs, like form
      // inside form and double rendering.
      $form['#post_render'][] = 'panopoly_magic_form_post_render_preview';
      $form['#panopoly_magic_preview_info'] = array(
        'preview_callback' => isset($preview_callback) ? $preview_callback : NULL,
        'pane' => $pane,
        'configuration' => $configuration,
        'keywords' => $keywords,
        'args' => $args,
        'context' => $context,
        'style' => $style,
        'display' => $display,
        'extra' => $extra,
      );

      // Remove the clearfix for preview floating
      if (isset($form['aligner_start'])) {
        $form['aligner_start']['#markup'] = str_replace('clearfix', '', $form['aligner_start']['#markup']);
      }
      $preview_attributes = array(
        'class' => array(
          'panopoly-magic-preview-button',
          // Keeping old class name for backcompat with 3rd party themes.
          'widget-preview-button',
        ),
      );

      // If live previewing, don't show button, and autoupdate.
      if (variable_get('panopoly_magic_live_preview', 1) == 1) {
        $preview_attributes['style'] = 'display: none';
        $preview_attributes['class'][] = 'ctools-auto-submit-click';
      }

      // Create the preview refresh button
      $form['buttons']['preview'] = array(
        '#type' => 'button',
        '#value' => t('Update Preview'),
        '#attributes' => $preview_attributes,
        '#weight' => -100,
        '#ajax' => array(
          'callback' => 'panopoly_magic_ajax_update_preview',
          'wrapper' => 'panopoly-form-widget-preview',
          'path' => 'system/panopoly-magic',
        ),
      );

      // Autosubmit the form
      ctools_add_js('auto-submit');
      $form['#attributes']['class'][] = 'ctools-auto-submit-full-form';
    }

    // Convert any other #ajax enabled buttons to use system/panopoly-magic.
    _panopoly_magic_add_path_to_ajax($form);
  }

  /**
   * Globally improve the buttons for the Chaos Tools Content Type Settings Forms
   */
  if (strpos($form_id, 'content_type_edit_form') || $form_id == 'ctools_entity_field_content_type_formatter_options') {
    $form['buttons']['cancel']['#access'] = FALSE;
    if (!empty($form['buttons']['return'])) {
      if ($form_state['op'] == 'add') {
        $form['buttons']['return']['#value'] = t('Add');
      }
      else {
        $form['buttons']['return']['#value'] = t('Save');
      }
    }
    $form['buttons']['#weight'] = -99;
  }

  /**
   * Globally make sure that all options are inside a fieldset. Currently just supporting a known list
   * of forms, but persumably this can be applied to all items eventually.
   */
  $forms_to_improve = array(
    'panopoly_search_search_box_content_type_edit_form',
    'menu_block_menu_tree_content_type_edit_form',
    'ctools_node_content_content_type_edit_form',
    'ctools_entity_field_content_type_formatter_options',
    'panopoly_search_facet_content_type_edit_form',
    'ctools_entity_field_content_type_formatter_styles',
    'panels_edit_style_settings_form',
  );
  drupal_alter('panopoly_magic_forms_to_improve', $forms_to_improve);
  if (in_array($form_id, $forms_to_improve)) {

    // Grab child elements and create the fieldset.
    $children = element_children($form);
    if (count($children) > 1) {
      $form['general_settings'] = array(
        '#type' => 'fieldset',
        '#title' => t('General Settings'),
        '#weight' => -30,
      );

      // Move all elements which aren't fieldsets or the submit buttons into the general settings fieldset.
      foreach ($children as $child) {
        if ((!empty($form[$child]['#tree']) || !empty($form[$child]['#type']) && $form[$child]['#type'] != 'fieldset') && !in_array($child, array(
          'buttons',
          'submit',
          'cancel_style',
          'form_build_id',
          'form_token',
          'form_id',
        ))) {

          // If the form has #tree => TRUE set, then we need to make sure the
          // submitted data has the same keys.
          if (empty($form[$child]['#parents'])) {
            $form[$child]['#parents'] = array(
              $child,
            );
          }
          $form['general_settings'][$child] = $form[$child];
          unset($form[$child]);
        }
      }

      // Don't show the context when it is the only one
      if (!empty($form['general_settings']['context']) && count($form['general_settings']['context']['#options']) == 1) {
        $form['general_settings']['context']['#type'] = 'value';
        $form['general_settings']['context']['#value'] = $form['general_settings']['context']['#default_value'];
      }

      // Don't show the override title text field unless we are overriding
      if (!empty($form['general_settings']['override_title_text'])) {
        $form['general_settings']['override_title_text']['#dependency_type'] = 'hide';
      }

      // Deal with cases where there is override title markup
      if (!empty($form['override_title_markup'])) {
        $form['override_title_markup']['#access'] = FALSE;
      }
    }
  }
}

/**
 * Submission callback for fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form().
 */
function _panopoly_magic_fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form_submit($form, &$form_state) {
  $entity = $form_state['entity'];

  // The live preview causes a stale version of the entity to exist in the
  // entity controller's static cache, so we much clear it on submit.
  entity_get_controller('fieldable_panels_pane')
    ->resetCache(array(
    $entity->fpid,
  ));
}

/**
 * Content panes should not use default system/ajax. Use our own for now.
 */
function _panopoly_magic_add_path_to_ajax($element) {
  if (!empty($element['#ajax']) && !isset($element['#ajax']['path'])) {
    $element['#ajax']['path'] = 'system/panopoly-magic';
  }
  foreach (element_children($element) as $key) {
    _panopoly_magic_add_path_to_ajax($element[$key]);
  }
}

/**
 * Process any field collection fields, returning psuedo values for them.
 */
function _panopoly_magic_process_field_collection_fields($entity_type, $bundle, $entity, $form, &$form_state) {
  $field_collection_fields = array();
  $instances = field_info_instances($entity_type, $bundle);
  foreach ($instances as $instance) {
    $field_name = $instance['field_name'];
    $field = field_info_field($field_name);
    if ($field['type'] == 'field_collection') {
      $langcode = entity_language($entity_type, $entity);
      if (!$langcode) {
        $langcode = LANGUAGE_NONE;
      }
      foreach (element_children($form[$field_name][$langcode]) as $index) {

        // Only address the numeric children.
        if (!is_numeric($index)) {
          continue;
        }
        $element = $form[$field_name][$langcode][$index];

        // Create a new field collection entity with the values.
        $field_collection_item = entity_create('field_collection_item', array(
          'field_name' => $field_name,
        ));
        $field_collection_item
          ->setHostEntity($entity_type, $entity, $langcode, FALSE);
        field_attach_form_validate('field_collection_item', $field_collection_item, $element, $form_state);
        field_attach_submit('field_collection_item', $field_collection_item, $element, $form_state);
        _field_invoke_multiple('load', 'field_collection_item', array(
          $field_collection_item->item_id => $field_collection_item,
        ));

        // Stash it for later use.
        $field_collection_fields[$field_name][$langcode][] = array(
          // Use 'field_collection' for field_collection 7.x-1.0 or later.
          // @see https://www.drupal.org/project/field_collection/issues/3111102
          'field_collection' => $field_collection_item,
          // Use 'entity' for all earlier versions.
          'entity' => $field_collection_item,
        );
      }

      // Clear out the field so the values don't get submitted.
      $entity->{$field_name}[$langcode] = array();
    }
  }
  return $field_collection_fields;
}

/**
 * Ajax callback that just returns the rendered preview.
 */
function panopoly_magic_ajax_update_preview($form, $form_state) {
  if (isset($form_state['values']) && isset($form['#panopoly_magic_preview_info']['configuration'])) {
    $form['#panopoly_magic_preview_info']['configuration'] = $form_state['values'] + $form['#panopoly_magic_preview_info']['configuration'];

    // For some reason, the formatter options aren't in $form_state['values']
    // but only $form_state['input']. Copy those explicitly.
    if ($form['#form_id'] == 'ctools_entity_field_content_type_formatter_options') {
      foreach ($form['#panopoly_magic_preview_info']['configuration']['formatter_settings'] as $formatter_setting_key => $formatter_setting_value) {
        $form['#panopoly_magic_preview_info']['configuration']['formatter_settings'][$formatter_setting_key] = isset($form_state['input'][$formatter_setting_key]) ? $form_state['input'][$formatter_setting_key] : $formatter_setting_value;
      }
    }
  }

  // If this is a field is being editted via FAPE, then we need to update the
  // entity in context with the temporary value of the field, in order for it
  // to be rendered correctly in the preview.
  if (isset($form_state['values']) && $form_state['plugin']['name'] == 'entity_field' && isset($form_state['subform_id']) && $form_state['subform_id'] == 'fape_field_edit_field_form' && isset($form['#panopoly_magic_preview_info']['context']['panelizer'])) {
    $entity_type = $form['#panopoly_magic_preview_info']['context']['panelizer']->type[2];
    $entity =& $form['#panopoly_magic_preview_info']['context']['panelizer']->data;
    foreach ($form_state['field'] as $field_name => $lang_field) {
      field_attach_form_validate($entity_type, $entity, $form, $form_state, array(
        'field_name' => $field_name,
      ));
      field_attach_submit($entity_type, $entity, $form, $form_state, array(
        'field_name' => $field_name,
      ));

      // Run the hook_field_load() before rendering. This is necessary for some
      // fields, like file and image fields.
      list($entity_id, , ) = entity_extract_ids($entity_type, $entity);
      $a = FIELD_LOAD_CURRENT;
      $b = NULL;
      _field_invoke_multiple('load', $entity_type, array(
        $entity_id => $entity,
      ), $a, $b, array(
        'field_name' => $field_name,
      ));
    }
  }

  // If this is a preview for a View, and it's using any caching, then clear
  // the cache before rendering the View.
  if (isset($form_state['view']) && get_class($form_state['view']) == 'view') {
    $view = $form_state['view'];
    if ($cache = $view->display_handler
      ->get_plugin('cache')) {
      $cache
        ->cache_flush();
    }
  }
  return panopoly_magic_form_post_render_preview('', $form);
}

/**
 * Add the preview to the form output.
 *
 * It is done here so the form is fully processed.
 */
function panopoly_magic_form_post_render_preview($output, $form) {
  extract($form['#panopoly_magic_preview_info']);

  // If no preview type was specified, render the pane as normal.
  if (empty($preview_callback)) {
    $preview_callback = 'ctools_content_render';
  }

  // If there is a 'destination', temporarily change the 'q' parameter so the
  // pane renders as if we are on that path.
  // @see https://www.drupal.org/node/2177417
  $original_path = $_GET['q'];
  if (!empty($_GET['destination'])) {
    $_GET['q'] = $_GET['destination'];
  }
  $content = $preview_callback($pane->type, $pane->subtype, $configuration, $keywords, $args, $context, $extra);

  // Restore the 'q' to its original value.
  $_GET['q'] = $original_path;
  if (!empty($content)) {

    // Wrap the widget content in the style plugin.
    if (!empty($style['render pane'])) {
      $content = theme($style['render pane'], array(
        'content' => $content,
        'pane' => $pane,
        'display' => $display,
        'style' => $style,
        'settings' => $pane->style['settings'],
      ));
    }
    else {
      $content = theme('panels_pane', array(
        'content' => $content,
        'pane' => $pane,
        'display' => $display,
      ));
    }
  }
  else {
    $content = t('[no preview]');
  }

  // Render the preview.
  return theme('panopoly_magic_preview', array(
    'title' => t('Preview'),
    'preview' => $content,
    'single' => TRUE,
  )) . $output;
}

/**
 * Renders previews for fieldable_panels_pane content types.
 */
function panopoly_magic_render_fieldable_panels_pane_preview($type, $subtype, $conf, $keywords, $args, $context, $extra) {
  $entity = $extra['fieldable_panels_pane'];

  // Prevents PHP notice when not admin user (see issue #2463389).
  if (!isset($entity->view_access)) {
    $entity->view_access = NULL;
  }
  if ($entity && fieldable_panels_panes_access('view', $entity)) {
    $settings = field_bundle_settings('fieldable_panels_pane', $entity->bundle);
    $block = new stdClass();
    $block->type = $type;
    $block->subtype = $subtype;
    $block->title = '';
    if (empty($settings['extra_fields']['display']) || !empty($settings['extra_fields']['display']['title']['default']['visible'])) {
      if (!empty($entity->title)) {
        $block->title = filter_xss_admin($entity->title);
      }
    }

    // Necessary to allow 'tablefield' to work per Issue #2479569, but it breaks
    // the Spotlight widget per Issue #2533064. So, for now, only run on the
    // Table widget.
    if ($entity->bundle == 'table') {
      _field_invoke('presave', 'fieldable_panels_pane', $entity);
    }

    // Some magic necessary to make sure fields are in the same state they
    // would be in if they were loaded from the database. Found this by looking
    // at how node generates it's previews in node_preview().
    _field_invoke_multiple('load', 'fieldable_panels_pane', array(
      $entity->fpid => $entity,
    ));
    $view_mode = isset($conf['view_mode']) ? $conf['view_mode'] : 'full';
    $block->content = fieldable_panels_panes_view($entity, $view_mode);
    return $block;
  }
}

/**
 * Recursively parse form elements to add special autosubmit handling on a per field-type basis.
 */
function panopoly_magic_autosubmit_configure(&$element) {
  if (!empty($element['#type'])) {
    switch ($element['#type']) {
      case 'textfield':

        // Special handling for autosubmit.
        if (!empty($element['#autocomplete_path'])) {
          $element['#attributes']['class'][] = 'ctools-auto-submit-exclude panopoly-autocomplete-autosubmit';
        }
        else {
          $element['#attributes']['class'][] = 'ctools-auto-submit-exclude panopoly-textfield-autosubmit';
        }
        break;
      case 'text_format':
        $element['#attributes']['class'][] = 'ctools-auto-submit-exclude panopoly-textarea-autosubmit';
        break;
      default:
        break;
    }
  }
  $children = element_children($element);
  if (!empty($children) && is_array($children)) {
    foreach ($children as $child) {
      panopoly_magic_autosubmit_configure($element[$child]);
    }
  }
}

/** 
 * Implementation of hook_form_FORM_ID_alter()
 */
function panopoly_magic_form_fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form_alter(&$form, &$form_state, $form_id) {

  // Setup the live preview
  if (variable_get('panopoly_magic_live_preview', 1)) {
    panopoly_magic_autosubmit_configure($form);

    // Special case exclusions for tablefield specific elements.
    if ($form_state['entity']->bundle == 'table') {
      $form['field_basic_table_table'][LANGUAGE_NONE][0]['tablefield']['rebuild']['count_cols']['#attributes']['class'] = array(
        'ctools-auto-submit-exclude',
      );
      $form['field_basic_table_table'][LANGUAGE_NONE][0]['tablefield']['rebuild']['count_rows']['#attributes']['class'] = array(
        'ctools-auto-submit-exclude',
      );
    }
  }

  // Add custom validation function for the reusable title
  $form['reusable']['admin_title']['#element_validate'][] = 'panopoly_magic_reusable_entity_validate';

  // Customize the view mode setting
  if (!empty($form['view_mode'])) {
    $form['view_mode']['#options'] = panopoly_magic_view_mode_options('fieldable_panels_pane');
    if (count($form['view_mode']['#options']) < 2) {
      $form['view_mode']['#type'] = 'value';
      $form['view_mode']['#value'] = key($form['view_mode']['#options']);
    }
    $form['widget_settings']['view_mode'] = $form['view_mode'];
    unset($form['view_mode']);
  }

  // Improving the general title setting
  if (!empty($form['widget_settings']['title'])) {
    $form['widget_settings']['title']['#size'] = '';
  }

  // Improving the link Options
  if (!empty($form['widget_settings']['link'])) {
    $form['widget_settings']['link']['path']['#size'] = '';
    $form['widget_settings']['link']['path']['#field_prefix'] = $form['widget_settings']['link']['path']['#title'];
    $form['widget_settings']['link']['path']['#title'] = '';
    $form['widget_settings']['link']['path']['#description'] = t('The URL or Drupal path from which to build the link.');
    $form['widget_settings']['link']['link']['#description'] = '';
  }

  // Move the reusable option to the general settings
  if (!empty($form['reusable'])) {
    $form['reusable']['#type'] = '';
    $form['reusable']['#title'] = '';
    $form['reusable']['reusable']['#title'] = t('Make this reusable');
    $form['reusable']['admin_title']['#field_prefix'] = t('Admin Title');
    $form['reusable']['admin_title']['#title'] = '';
    $form['reusable']['admin_title']['#size'] = '';
    $form['reusable']['admin_title']['#description'] = '';
    $form['widget_settings']['reusable'] = $form['reusable'];
    unset($form['reusable']);
  }
}

/**
 * Find visible element children.
 *
 * @param array $element
 *   Form API element.
 *
 * @return array
 *   Array of the element children that are visible.
 */
function _panopoly_magic_visible_element_children($element) {
  $children = array();
  foreach (element_children($element) as $name) {
    if (isset($element[$name]['#type']) && $element[$name]['#type'] != 'hidden' && (!isset($element[$name]['#access']) || $element[$name]['#access'])) {
      $children[] = $name;
    }
  }
  return $children;
}

/** 
 * Implementation of hook_form_FORM_ID_alter()
 *
 * Provides customizations to the views content type settings form
 */
function panopoly_magic_form_views_content_views_panes_content_type_edit_form_alter(&$form, &$form_state, $form_id) {

  // Get the configuration
  $conf = $form_state['conf'];

  // Add a widget title setting if views allows this to be customized
  if (!empty($form['override_title'])) {
    $form['override_title_markup']['#access'] = FALSE;
    $form['override_title']['#access'] = FALSE;
    $form['override_title_text']['#access'] = FALSE;
    $form['override_title_heading']['#access'] = FALSE;
    $form['exposed']['widget_title'] = array(
      '#field_prefix' => t('Title'),
      '#type' => 'textfield',
      '#size' => '',
      '#weight' => -100,
      '#states' => array(
        'visible' => array(
          ':input[name="override_title"]' => array(
            'value' => '1',
          ),
        ),
      ),
      '#default_value' => isset($conf['widget_title']) ? $conf['widget_title'] : $form_state['view']->display_handler->options['title'],
      '#parents' => array(
        'widget_title',
      ),
    );
  }

  // Add an option to make the widget title a link if views allows this to happen
  if (!empty($form['link_to_view']) && !empty($form['path'])) {
    $form['link_to_view']['#title'] = t('Make title a link');
    $form['link_to_view']['#description'] = '';
    $form['link_to_view']['#id'] = 'edit-link';
    $form['exposed']['link_to_view'] = $form['link_to_view'];
    $form['exposed']['link_to_view']['#parents'] = array(
      'link_to_view',
    );
    unset($form['link_to_view']);
  }

  // Add an option to make the more link available if views allows this to happen
  if (!empty($form['more_link'])) {
    $form['more_link']['#id'] = 'more-link';
    $form['more_link']['#description'] = '';
    $form['exposed']['more_link'] = $form['more_link'];
    $form['exposed']['more_link']['#parents'] = array(
      'more_link',
    );
    unset($form['more_link']);
  }

  // Add an option to the more link text if views allows this to happen
  if (!empty($form['more_text'])) {
    $form['exposed']['more_text'] = $form['more_text'];
    $form['exposed']['more_text']['#parents'] = array(
      'more_text',
    );
    unset($form['more_text']);
    $form['exposed']['more_text']['#states'] = array(
      'visible' => array(
        ':input[name="more_link"]' => array(
          'checked' => TRUE,
        ),
      ),
    );
  }

  // Update the field settings for pagers
  if (!empty($form['use_pager']) && !empty($form['pager_id'])) {
    $form['use_pager']['#prefix'] = '<div class="form-item container-inline">';
    $form['exposed']['use_pager'] = $form['use_pager'];
    $form['exposed']['use_pager']['#parents'] = array(
      'use_pager',
    );
    $form['exposed']['pager_id'] = $form['pager_id'];
    $form['exposed']['pager_id']['#parents'] = array(
      'pager_id',
    );
    unset($form['use_pager']);
    unset($form['pager_id']);
  }

  // Handle the path value that views gives us
  if (!empty($form['path'])) {
    $form['path']['#description'] = t('The URL path used for linking and paging purposes. Leave blank to use the current page.');
    $form['path']['#title'] = '';
    $form['path']['#dependency'] = array(
      'edit-link' => array(
        1,
      ),
      'more-link' => array(
        1,
      ),
      'use-pager-checkbox' => array(
        1,
      ),
    );
    $form['exposed']['path'] = $form['path'];
    $form['exposed']['path']['#parents'] = array(
      'path',
    );
    unset($form['path']);
  }

  // Adjust the items per page and offset settings
  if (!empty($form['items_per_page']) && !empty($form['offset'])) {
    $form['items_per_page']['#field_prefix'] = t('Items to Show');
    $form['items_per_page']['#title'] = '';
    $form['items_per_page']['#description'] = t('Enter 0 to display all.');
    $form['display_settings']['items_per_page'] = $form['items_per_page'];
    unset($form['items_per_page']);
    $form['offset']['#field_prefix'] = t('Items to Skip');
    $form['offset']['#title'] = '';
    $form['offset']['#description'] = t('Enter 0 to skip none.');
    $form['offset']['#prefix'] = '<div class="clearfix">';
    $form['offset']['#suffix'] = '</div>';
    $form['display_settings']['offset'] = $form['offset'];
    unset($form['offset']);
  }
  elseif (!empty($form['items_per_page']) && empty($form['offset'])) {
    $form['items_per_page']['#field_prefix'] = t('Items to Show');
    $form['items_per_page']['#title'] = '';
    $form['items_per_page']['#description'] = t('Enter 0 to display all.');
    $form['items_per_page']['#prefix'] = '<div class="clearfix">';
    $form['items_per_page']['#suffix'] = '</div>';
    $form['display_settings']['items_per_page'] = $form['items_per_page'];
    unset($form['items_per_page']);
  }
  elseif (empty($form['items_per_page']) && !empty($form['offset'])) {
    $form['offset']['#field_prefix'] = t('Items to Skip');
    $form['offset']['#title'] = '';
    $form['offset']['#description'] = t('Enter 0 to skip none.');
    $form['offset']['#prefix'] = '<div class="clearfix">';
    $form['offset']['#suffix'] = '</div>';
    $form['display_settings']['offset'] = $form['offset'];
    unset($form['offset']);
  }

  // Adjust the sort ordering and sort by options
  if (!empty($form['exposed']['sort_order']) && !empty($form['exposed']['sort_by'])) {
    $form['exposed']['sort_order']['#title'] = t('Sort order');
    $form['display_settings']['sort_order'] = $form['exposed']['sort_order'];
    $form['display_settings']['sort_order']['#parents'] = array(
      'exposed',
      'sort_order',
    );
    unset($form['exposed']['sort_order']);
    $form['exposed']['sort_by']['#prefix'] = '<div class="clearfix">';
    $form['exposed']['sort_by']['#suffix'] = '</div>';
    $form['display_settings']['sort_by'] = $form['exposed']['sort_by'];
    $form['display_settings']['sort_by']['#parents'] = array(
      'exposed',
      'sort_by',
    );
    unset($form['exposed']['sort_by']);
  }
  elseif (!empty($form['exposed']['sort_order']) && empty($form['exposed']['sort_by'])) {
    $form['exposed']['sort_order']['#title'] = t('Sort order');
    $form['exposed']['#prefix'] = '<div class="clearfix">';
    $form['exposed']['#suffix'] = '</div>';
    $form['display_settings']['sort_order'] = $form['exposed']['sort_order'];
    $form['display_settings']['sort_order']['#parents'] = array(
      'exposed',
      'sort_order',
    );
    unset($form['exposed']['sort_order']);
  }
  elseif (empty($form['exposed']['sort_order']) && !empty($form['exposed']['sort_by'])) {
    $form['exposed']['sort_by']['#prefix'] = '<div class="clearfix">';
    $form['exposed']['sort_by']['#suffix'] = '</div>';
    $form['display_settings']['sort_by'] = $form['exposed']['sort_by'];
    $form['display_settings']['sort_by']['#parents'] = array(
      'exposed',
      'sort_by',
    );
    unset($form['exposed']['sort_by']);
  }

  // Adjust the field setting options
  if (isset($form['fields_override'])) {
    $form['fields_override']['#title'] = t('Field Settings');
    $form['fields_override']['#collapsible'] = FALSE;
    foreach ($form['fields_override'] as &$field) {
      if (is_array($field)) {
        $field['#title'] = t('Display') . ' ' . $field['#title'];
      }
    }
  }

  // Determine if this is a fielded view. If so, add magic display type changer
  $view_handler = $form_state['view']->display_handler;
  if ($view_handler
    ->get_option('row_plugin') == 'fields') {

    // Set to default view settings if there isn't one.
    if (empty($conf['view_settings'])) {
      if ($form_state['view']->style_plugin->plugin_name == 'table') {
        $conf['view_settings'] = 'table';
      }
      else {
        $conf['view_settings'] = 'fields';
      }
    }

    // Deal with legacy 'nodes' and others (such as 'files') view settings so
    // that other entity types can be included.
    $conf['view_settings'] = panopoly_magic_convert_view_settings($conf['view_settings']);

    // Add information about the View Mode
    $form['display_settings']['view_settings'] = array(
      '#type' => 'radios',
      '#prefix' => '<div class="view-settings-wrapper">',
      '#suffix' => '</div>',
      '#title' => t('Display Type'),
      '#default_value' => $conf['view_settings'],
      '#weight' => 10,
      '#options' => array(
        'fields' => t('Fields'),
        'rendered_entity' => t('Content'),
        'table' => t('Table'),
      ),
    );

    // Add header column options for table views.
    $form['display_settings']['header_type'] = array(
      '#type' => 'select',
      '#title' => t('Column Header'),
      '#options' => array(
        'none' => t('None'),
        'titles' => t('Titles'),
      ),
      '#default_value' => !empty($conf['header_type']) ? $conf['header_type'] : 'none',
      '#states' => array(
        'visible' => array(
          ':input[name="view_settings"]' => array(
            'value' => 'table',
          ),
        ),
      ),
      '#weight' => 11,
    );

    // Update the field overrides to be dependent on the view settings selection.
    $form['fields_override']['#states'] = array(
      // The inverted logic here isn't optimal, and in the future may be better achieved via OR'd conditions.
      // @link http://drupal.org/node/735528 @endlink
      'invisible' => array(
        ':input[name="view_settings"]' => array(
          'value' => 'rendered_entity',
        ),
      ),
    );

    // If the View has opted out of 'Display type' then we hide it from the form,
    // but still allow our JavaScript #states to work. We do the is_a() check to
    // not break things in the case the user has upgraded panopoly_magic, but
    // hasn't yet rebuilt the Views cache.
    $view_allow = $view_handler
      ->get_option('allow');
    if (is_a($view_handler, 'panopoly_magic_plugin_display_panel_pane') && empty($view_allow['panopoly_magic_display_type'])) {
      $form['display_settings']['view_settings']['#type'] = 'hidden';
      $form['display_settings']['header_type']['#type'] = 'hidden';

      // Since we're not giving the user the choice, default to keeping the titles.
      $form['display_settings']['header_type']['#default_value'] = 'titles';
    }
  }

  // Get view modes for entity
  $view_modes = panopoly_magic_view_mode_options(panopoly_magic_get_entity_type($form_state['view']));

  // Get the default view mode.
  $options_default_view_mode = $view_handler
    ->get_option('row_plugin') == 'fields' ? 'teaser' : 'full';
  $row_options = $view_handler
    ->get_option('row_options');
  if (isset($form_state['view']->style_plugin->row_plugin->options['view_mode'])) {
    $options_default_view_mode = $form_state['view']->style_plugin->row_plugin->options['view_mode'];
  }
  if (!array_key_exists($options_default_view_mode, $view_modes)) {
    $options_default_view_mode = current(array_keys($view_modes));
  }

  // Add specific style options.
  $form['content_settings']['view_mode'] = array(
    '#type' => 'radios',
    '#options' => $view_modes,
    '#default_value' => !empty($conf['view_mode']) ? $conf['view_mode'] : $options_default_view_mode,
    '#states' => array(
      'visible' => array(
        ':input[name="view_settings"]' => array(
          'value' => 'rendered_entity',
        ),
      ),
    ),
  );

  // Show view mode selection if the display type setting exists because the
  // view mode will get shown/hidden by Javascript.
  if (isset($form['display_settings']['view_settings'])) {
    $form['content_settings']['view_mode']['#access'] = TRUE;
  }
  elseif (!in_array($view_handler
    ->get_option('row_plugin'), array(
    'node',
    'entity',
  )) || $form_state['view']->style_plugin->plugin_name == 'table') {
    $form['content_settings']['view_mode']['#access'] = FALSE;
  }

  // If the View has opted out of 'View mode' then we hide it from the form.
  // We do the is_a() check to not break things in the case the user has
  // upgraded panopoly_magic, but hasn't yet rebuilt the Views cache.
  $view_allow = $view_handler
    ->get_option('allow');
  if (is_a($view_handler, 'panopoly_magic_plugin_display_panel_pane') && empty($view_allow['panopoly_magic_view_mode'])) {
    $form['content_settings']['view_mode']['#access'] = FALSE;
  }

  // Define a general settings fieldset if we have exposed values
  if (!empty($form['exposed']) && count(_panopoly_magic_visible_element_children($form['exposed'])) > 0) {
    $form['exposed']['#type'] = 'fieldset';
    $form['exposed']['#title'] = t('General Settings');
    $form['exposed']['#weight'] = -30;
    $form['exposed']['#attributes'] = array(
      'class' => array(
        'general-settings-fieldset',
      ),
    );
  }

  // Define a context settings fieldset if we have exposed values
  if (!empty($form['context']) && count(_panopoly_magic_visible_element_children($form['context'])) > 0) {
    $form['context']['#type'] = 'fieldset';
    $form['context']['#title'] = t('Context Settings');
    $form['context']['#weight'] = -29;
  }

  // Define a content settings fieldset if we have exposed values
  if (!empty($form['content_settings']) && count(_panopoly_magic_visible_element_children($form['content_settings'])) > 0) {
    $form['content_settings']['#type'] = 'fieldset';
    $form['content_settings']['#title'] = t('Content Settings');
    $form['content_settings']['#weight'] = -27;
  }

  // Define a display settings fieldset if we have display options
  if (!empty($form['display_settings']) && count(_panopoly_magic_visible_element_children($form['display_settings'])) > 0) {
    $form['display_settings']['#type'] = 'fieldset';
    $form['display_settings']['#title'] = t('Display Settings');
    $form['display_settings']['#weight'] = -28;
  }

  // Add auto submit functionality
  if (variable_get('panopoly_magic_live_preview', 1)) {
    panopoly_magic_autosubmit_configure($form);
  }

  // Add a custom submit handler to our preview and submit option
  $form['#submit'][] = 'panopoly_magic_views_content_type_modal_submit';
}

/**
 * Implementation of hook_form_FORM_ID_alter()
 *
 * Provides customization to entity formatter option form
 */
function panopoly_magic_form_ctools_entity_field_content_type_formatter_options_alter(&$form, &$form_state, $form_id) {

  // Get the configuration
  $conf = $form_state['conf'];

  // Move the title for the label and formatter to be the field prefix
  foreach (array(
    'label',
    'formatter',
  ) as $field) {
    if (!empty($form['general_settings'][$field]) && !empty($form['general_settings'][$field]['#title'])) {
      $form['general_settings'][$field]['#field_prefix'] = $form['general_settings'][$field]['#title'];
      $form['general_settings'][$field]['#title'] = '';
    }
  }

  // Add in the field edit fields with FAPE if we are not editing a default
  if (empty($form_state['entity']) && !empty($form_state['display']->context['panelizer']->data)) {

    // Determine what entity type and field name we are editing based on the form subtype and URL args.
    $arg_parts = explode(':', arg(4));
    list($entity_type, $field_name) = explode(':', $form_state['subtype_name']);
    $entities = entity_load($entity_type, array(
      $arg_parts[2],
    ));
    $entity = reset($entities);

    // Unfortunately the above code is not as reliable as we'd like. There are cases where the field we're editing
    // doesn't have an corrosponding entity (say, comments). Let's make sure we have an entity before adding FAPE specific
    // data into the form.
    if (!empty($entity)) {
      $form_state['entity'] = $entity;
      $form_state['entity_type'] = $entity_type;
      $form_state['field_name'] = $field_name;
      $form_state['bundle'] = empty($entity->bundle) ? $entity->type : $entity->bundle;
      $form_state['field_instance'] = field_info_instance($entity_type, $field_name, $form_state['bundle']);
      $form_state['langcode'] = LANGUAGE_NONE;
      $form_state['subform_id'] = 'fape_field_edit_field_form';
    }
  }
  if (!empty($form_state['entity'])) {

    // Add the field edit form.
    fape_field_edit_field_form($form, $form_state);

    // Remove the default submitter in favor of our own custom submit callback.
    array_pop($form['#submit']);
    $form['#submit'][] = 'panopoly_magic_fape_submit';
  }

  // Pre-rende fields into a fieldset.
  $form['#pre_render'][] = 'panopoly_magic_panelizer_pre_render';

  // When moving backwards through form steps, ensure image widgets properly retain their value.
  if (!empty($form_state['field_instance']) && isset($form_state['triggering_element']['#value']) && $form_state['triggering_element']['#value'] == 'Back') {
    $field_name = $form_state['field_instance']['field_name'];
    $field_type = $form_state['field_instance']['widget']['module'];
    $entity = $form_state['entity'];

    // Reset the #default_value to the (currently) unsaved entity value.
    if ($field_type == 'image' && !empty($form[$field_name])) {
      $field = $entity->{$field_name};
      foreach ($field[LANGUAGE_NONE] as $delta => $value) {
        $form[$field_name][LANGUAGE_NONE][$delta]['#default_value'] = $value;
      }
    }
  }

  // Enable auto submit functionality
  if (variable_get('panopoly_magic_live_preview', 1)) {
    panopoly_magic_autosubmit_configure($form);
  }

  // Add a custom submit handler to our preview and submit option
  $form['#submit'][] = 'panopoly_magic_views_content_type_modal_submit';
  $form['buttons']['preview']['#submit'][] = 'panopoly_magic_views_content_type_modal_submit';
}

/**
 * Implement the "Content Settings" fieldset in a pre-render. This fixes issues with image caused by initially
 * doing this in a form_alter.
 *
 * @see http://drupal.org/node/1567704
 */
function panopoly_magic_panelizer_pre_render($element) {
  $exclude_list = array(
    'form_id',
    'form_token',
    'form_build_id',
    'buttons',
  );

  // Add any remaining fields to the content settings fieldset.
  foreach (element_children($element) as $key) {
    $value = $element[$key];
    if (!in_array($key, $exclude_list) && !empty($value['#type']) && $value['#type'] != 'fieldset') {
      if (empty($element['content_settings'])) {

        // Add a content settings fieldset.
        $element['content_settings'] = array(
          '#type' => 'fieldset',
          '#title' => t('Content Settings'),
          '#weight' => 1,
        );
      }
      $element['content_settings'][$key] = $value;
      unset($element[$key]);
    }
  }
  return $element;
}
function panopoly_magic_form_ctools_entity_field_content_type_formatter_styles_alter(&$form, &$form_state, $form_id) {

  // Push the panopoly magic submitter onto the front of the submit array.
  // We need it at the beginning so the entity is saved before the IPE
  // reloads pane content.
  if (!empty($form_state['entity'])) {
    $form['#submit'][] = 'panopoly_magic_fape_submit';
  }
}

/**
 * Custom submit callback for FAPE enabled stylizer forms.
 */
function panopoly_magic_fape_submit(&$form, &$form_state) {

  // If there isn't a next step save the entity.
  if ((empty($form_state['triggering_element']['#next']) || !empty($form_state['field']['body'])) && !empty($form_state['entity'])) {
    entity_save($form_state['entity_type'], $form_state['entity']);
    if (!empty($form_state['display_cache'])) {
      $form_state['display_cache']->display->context['panelizer']->data = $form_state['entity'];
    }
  }
  else {
    fape_field_edit_field_form_submit($form, $form_state);
  }
}

/** 
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Provides customization to panel pane style form
 */
function panopoly_magic_form_panels_edit_style_type_form_alter(&$form, &$form_state, $form_id) {
  if (!variable_get('panopoly_magic_show_panels_styles', 0)) {

    // Unset options for which we have no need
    unset($form['style']['#options']['block']);
    unset($form['style']['#options']['rounded_corners']);
    unset($form['style']['#options']['naked']);
  }

  // Better explain that "No Style" means "Default Pane Style"
  // Move the default option to the top. Also I should have learned
  // how to manage arrays in PHP.
  if (!empty($form['style']['#options']['default'])) {
    $form['style']['#options']['default'] = t('Default Pane Style');
    $default = $form['style']['#options']['default'];
    unset($form['style']['#options']['default']);
    $form['style']['#options'] = array_reverse($form['style']['#options']);
    $form['style']['#options']['default'] = $default;
    $form['style']['#options'] = array_reverse($form['style']['#options']);
  }

  // Better title for the List Style region style plugin
  if (!empty($form['style']['#options']['list'])) {
    $form['style']['#options']['list'] = t('List Style');
  }

  // Improve the UI around region style selections
  if (!empty($form['style']['#options'][0])) {
    $form['style']['#options'][0] = t('Default Region Style');
    if (empty($form['style']['#default_value']) || $form['style']['#default_value'] == '-1') {
      $form['style']['#default_value'] = '0';
    }
    unset($form['style']['#options']['default']);
  }

  // Move custom style option to bottom of list.
  if (!empty($form['style']['style']['#options']['stylizer'])) {
    unset($form['style']['style']['#options']['stylizer']);
    $form['style']['style']['#options']['stylizer'] = t('Custom Style');
  }

  // Adding a fieldset around styling
  $form['style'] = array(
    'style' => $form['style'],
  );
  $form['style']['#type'] = 'fieldset';
  $form['style']['#title'] = t('Style Settings');
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Provide customizations for the ctools stylizer edit form.
 */
function panopoly_magic_form_ctools_stylizer_edit_style_form_default_alter(&$form, &$form_state, $form_id) {

  // If Auto-submit is enabled.
  if (variable_get('panopoly_magic_live_preview', 1)) {

    // Customize the settings preview button.
    $form['top box']['preview']['submit']['#attributes'] = array(
      // Keeping 'widget-preview-button' class for backcompat with 3rd party themes.
      'class' => array(
        'panopoly-magic-preview-button',
        'widget-preview-button',
        'ctools-use-ajax',
        'ctools-auto-submit-click',
      ),
    );

    // Autosubmit the form.
    ctools_add_js('auto-submit');
    $form['#attributes']['class'][] = 'ctools-auto-submit-full-form';
    $form['top box']['preview']['#theme'] = 'panopoly_magic_stylizer_preview_form';
  }

  // Change the Weight Around
  $form['top box']['preview']['#weight'] = -50;
}

/**
 * Theme the stylizer preview form.
 */
function theme_panopoly_magic_stylizer_preview_form($vars) {
  $form =& $vars['form'];
  $plugin = $form['#form_state']['base_style_plugin'];
  $settings = $form['#form_state']['settings'];
  if (!empty($form['#form_state']['settings']['old_settings'])) {
    ctools_stylizer_cleanup_style($plugin, $form['#form_state']['settings']['old_settings']);
  }
  $preview = '';
  if (!empty($plugin['preview'])) {
    $preview = $plugin['preview'];
  }
  else {
    $base_types = ctools_get_style_base_types();
    if (!empty($base_types[$plugin['module']][$plugin['type']]['preview'])) {
      $preview = $base_types[$plugin['module']][$plugin['type']]['preview'];
    }
  }
  if (!empty($preview) && function_exists($preview)) {
    return theme('panopoly_magic_preview', array(
      'title' => t('Preview'),
      'preview' => $preview($plugin, $settings) . drupal_render_children($form),
      'single' => TRUE,
    ));
  }
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Provide customizations for the ctools stylizer edit choose form.
 */
function panopoly_magic_form_ctools_stylizer_edit_style_form_choose_alter(&$form, &$form_state, $form_id) {

  // Remove the rounded corners options
  if (!empty($form['style_base']['Basic-styles']['pane_rounded_shadow'])) {
    unset($form['style_base']['Basic-styles']['pane_rounded_shadow']);
  }
  if (!empty($form['style_base']['Basic-styles']['region_rounded_shadow'])) {
    unset($form['style_base']['Basic-styles']['region_rounded_shadow']);
  }
}

/**
 *  Validator to ensure that reusable entites have titles.
 */
function panopoly_magic_reusable_entity_validate($element, &$form_state, $form) {

  // Don't validate if we're just updating the preview.
  if (!empty($form_state['triggering_element']) && $form_state['triggering_element']['#value'] == 'Update Preview') {
    return;
  }

  // If the reusable entity checkbox is selected.
  if (!empty($form_state['values']['reusable'])) {

    // Ensure a title is present.
    if (empty($element['#value'])) {
      form_error($element, t('If you would like this entity to be reusable, please add a title.'));
    }
  }
}

/**
 * Custom submit handler to save panels pane configuration for styling
 */
function panopoly_magic_views_content_type_modal_submit(&$form, &$form_state) {
  $move = array(
    'view_settings',
    'header_type',
    'view_mode',
    'widget_title',
  );
  foreach ($move as $key) {
    if (isset($form_state['values'][$key])) {
      $form_state['conf'][$key] = $form_state['values'][$key];
    }
  }
}

/**
 * Implements hook_views_pre_view()
 */
function panopoly_magic_views_pre_view(&$view) {
  if (isset($view->display_handler->options['pane_conf'])) {
    $conf = $view->display_handler->options['pane_conf'];
    if (isset($conf['widget_title'])) {
      $view->display_handler->options['defaults']['title'] = FALSE;
      $view->display_handler->options['title'] = $conf['widget_title'];
      $view->build_info['title'] = $conf['widget_title'];
    }

    // Deal with legacy 'nodes' and others (such as 'files') view settings so
    // that other entity types can be included.
    if (!empty($conf['view_settings'])) {
      $conf['view_settings'] = panopoly_magic_convert_view_settings($conf['view_settings']);
    }

    // Set the style plugin to a table style.
    // Determine that this was previously a field view, which has been overridden to a node view in the pane config.
    if (!empty($conf['view_settings']) && $conf['view_settings'] == 'rendered_entity') {
      $view->display_handler->options['defaults']['row_plugin'] = FALSE;
      $view->display_handler->options['row_plugin'] = 'entity';
    }
    elseif (!empty($conf['view_settings']) && $conf['view_settings'] == 'table') {

      // Find the currently active field defination, else break out as table
      // needs fields.
      if (empty($view->display_handler->options['defaults']['fields']) && isset($view->display_handler->options['fields'])) {
        $fields =& $view->display_handler->options['fields'];
      }
      elseif (!empty($view->display_handler->default_display->options['fields'])) {
        $fields =& $view->display_handler->default_display->options['fields'];
      }
      else {

        // If no fields, don't try to display as table.
        return;
      }
      $view->display_handler->options['defaults']['style_plugin'] = FALSE;
      $view->display_handler->options['style_plugin'] = 'table';

      // Set or remove header labels depending on user selection.
      $use_header_titles = !empty($conf['header_type']) && $conf['header_type'] == 'titles';
      foreach ($fields as $field_key => &$field) {
        if ($use_header_titles && !empty($field['ui_name']) && empty($field['label'])) {
          $field['label'] = $field['ui_name'];
        }
        elseif (!$use_header_titles) {
          $field['label'] = '';
        }

        // Hide empty columns.
        if (!empty($view->display_handler->options['row_plugin']['hide_empty'])) {
          $view->display_handler->options['style_options'][$field_key]['empty_column'] = TRUE;
        }
      }
    }
    if ((empty($conf['view_settings']) || $conf['view_settings'] == 'rendered_entity') && !empty($conf['view_mode'])) {

      // Transfer over the row options from default if set to use.
      if (!empty($view->display_handler->options['defaults']['row_options'])) {
        $view->display_handler->options['defaults']['row_options'] = FALSE;
        $view->display_handler->options['row_options'] = $view->display_handler->default_display->options['row_options'];
      }
      $view->display_handler->options['row_options']['view_mode'] = $conf['view_mode'];
    }
  }
}

/**
 * Default 'preview callback' for widgets when we can't find something more specific.
 */
function panopoly_magic_preview_callback_default($type, $subtype, $plugin, $renderer) {
  $pane = panels_new_pane($type, $subtype, TRUE);

  // Mix in requested settings.
  if (!empty($plugin['preview settings'])) {
    $pane->configuration = array_merge($pane->configuration, $plugin['preview settings']);
  }
  $display = $renderer->display;
  $context = $renderer->display->context;
  $args = $renderer->display->args;
  $incoming_content = $renderer->display->incoming_content;
  $keywords = !empty($renderer->display->keywords) ? $renderer->display->keywords : array();
  $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, $keywords, $args, $context, $incoming_content);
  if ($content) {
    return theme('panels_pane', array(
      'content' => $content,
      'pane' => $pane,
      'display' => $display,
    ));
  }
}

/**
 * Default 'preview callback' for FPP widgets.
 */
function panopoly_magic_preview_callback_fpp($type, $subtype, $plugin, $renderer) {
  if (!empty($plugin['entity_id'])) {
    $fpp = fieldable_panels_panes_load_from_subtype($plugin['entity_id']);
  }
  elseif (!empty($plugin['preview settings'])) {
    $values = $plugin['preview settings'];
    $values['bundle'] = $plugin['bundle'];
    $fpp = fieldable_panels_panes_create($values);
    $fpp->fpid = NULL;
  }
  else {

    // For a new (non-existant) FPP with no 'preview settings', don't show
    // any preview.
    return NULL;
  }

  // Set a property so it's possible to tell that the FPP is being rendered
  // in a preview, for example, in the template.
  $fpp->panopoly_magic_preview = TRUE;
  $content = fieldable_panels_pane_view($fpp, 'full', NULL);
  if ($content) {
    $block = (object) array(
      'title' => check_plain($fpp->title),
      'content' => $content,
      'type' => $type,
      'subtype' => $subtype,
    );
    $pane = panels_new_pane($type, $subtype, TRUE);
    return theme('panels_pane', array(
      'content' => $block,
      'pane' => $pane,
      'display' => $renderer->display,
    ));
  }
}

/**
 * Default 'preview callback' for when a 'preview image' is used.
 */
function panopoly_magic_preview_callback_image($type, $subtype, $plugin, $renderer) {
  if (empty($plugin['preview image alt'])) {
    $plugin['preview image alt'] = NULL;
  }
  return theme('image', array(
    'path' => $plugin['preview image'],
    'alt' => $plugin['preview image alt'],
  ));
}

/**
 * Helper function to display the pane for showing previews in the add_content modal
 */
function _panopoly_magic_render_preview_pane(&$widget_vars, $renderer) {
  $type = $widget_vars['type_name'];
  $subtype = $widget_vars['subtype_name'];
  $plugin = ctools_content_get_subtype($type, $subtype);

  // Special case for reusable FPPs: by default we force the FPP callback so
  // that the entity is rendered, and the custom callback or image isn't used.
  // However, if a special key on the plugin subtype ('preview always') is set,
  // then we won't force the FPP callback and the custom preview will always
  // be used (regardless if it's a reusable FPP or not).
  if ($type == 'fieldable_panels_pane' && !empty($plugin['entity_id']) && empty($plugin['preview always'])) {
    $plugin['preview callback'] = 'panopoly_magic_preview_callback_fpp';
  }
  if (!empty($plugin['preview callback'])) {
    $preview_callback = $plugin['preview callback'];
  }
  elseif (!empty($plugin['preview image'])) {
    $preview_callback = 'panopoly_magic_preview_callback_image';
  }
  elseif ($type == 'fieldable_panels_pane') {
    $preview_callback = 'panopoly_magic_preview_callback_fpp';
  }
  else {
    $preview_callback = 'panopoly_magic_preview_callback_default';
  }
  if ($content = $preview_callback($type, $subtype, $plugin, $renderer)) {

    // Permit the callback to return a render array.
    if (is_array($content)) {
      $content = drupal_render($content);
    }

    // Optionally strip JavaScript.
    $strip_js = variable_get('panopoly_magic_strip_js_from_preview', 0);
    if (!empty($strip_js) && !empty($content)) {
      $content = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $content);
    }
    $widget_vars['preview'] = $content;
    drupal_add_js(array(
      'panopoly_magic' => array(
        'pane_add_preview_type' => $type,
        'pane_add_preview_subtype' => $subtype,
      ),
    ), 'setting');
  }
}

/**
 * Preprocess the panels_add_content_modal() function to add the HTML for the preview
 */
function panopoly_magic_preprocess_panels_add_content_modal(&$vars) {

  // Generate the pane preview
  if (!empty($vars['categories'][$vars['category']]['content'])) {
    $use_preview = variable_get('panopoly_magic_pane_add_preview', PANOPOLY_ADD_PREVIEW_DEFAULT);
    if ($use_preview == PANOPOLY_ADD_PREVIEW_DISABLED) {
      return;
    }

    // Generate the preview for the single widget if requested.
    if ($use_preview == PANOPOLY_ADD_PREVIEW_SINGLE) {
      $query = drupal_get_query_parameters();
      $type_name = !empty($query['type_name']) ? $query['type_name'] : '';
      $sub_type = !empty($query['subtype_name']) ? $query['subtype_name'] : '';
      $widget_vars = array(
        'type_name' => $type_name,
        'subtype_name' => $sub_type,
      );
      _panopoly_magic_render_preview_pane($widget_vars, $vars['renderer']);
      $vars['column_count'] = 1;
      $vars['preview_single'] = isset($widget_vars['preview']) ? $widget_vars['preview'] : '';
      $vars['preview_single_title'] = '';
      foreach ($vars['categories'][$vars['category']]['content'] as $key => $widget_vars) {
        if ($widget_vars['type_name'] == $type_name && $widget_vars['subtype_name'] == $sub_type) {
          $vars['preview_single_title'] = $key;
          break;
        }
      }
    }

    // Process each widget option, either adding the preview itself or a link to generate it.
    foreach ($vars['categories'][$vars['category']]['content'] as $key => &$widget_vars) {
      $query = drupal_get_query_parameters();
      $preview_panes = !empty($query['preview_panes']) ? explode(',', $query['preview_panes']) : array();

      // Determine if we should show a preview for this pane.
      if ($use_preview == PANOPOLY_ADD_PREVIEW_SINGLE) {

        // Convert the link to generate a preview of itself.
        $options = array(
          'query' => array(
            'panopoly_magic_preview' => _panopoly_magic_add_content_preview_mode_name($use_preview),
            'type_name' => $widget_vars['type_name'],
            'subtype_name' => $widget_vars['subtype_name'],
          ),
          'attributes' => array(
            'class' => array(
              'use-ajax button',
            ),
          ),
          'html' => TRUE,
        );
        $widget_vars['title'] = l(t('<span class="element-invisible">Preview</span> !widget <span class="element-invisible">widget</span>', array(
          '!widget' => filter_xss_admin($widget_vars['title']),
        )), current_path(), $options);
      }
      elseif ($use_preview == PANOPOLY_ADD_PREVIEW_AUTOMATIC || in_array($widget_vars['subtype_name'], $preview_panes)) {
        _panopoly_magic_render_preview_pane($widget_vars, $vars['renderer']);
      }
      else {

        // If we can't preview then generate a preview link.
        $preview = empty($query['preview_panes']) ? $widget_vars['subtype_name'] : $query['preview_panes'] . ',' . $widget_vars['subtype_name'];
        $options = array(
          'query' => array(
            'panopoly_magic_preview' => _panopoly_magic_add_content_preview_mode_name($use_preview),
            'preview_panes' => $preview,
          ),
          'attributes' => array(
            'class' => array(
              'use-ajax button',
            ),
            'title' => t('Preview @widget widget', array(
              '@widget' => $key,
            )),
          ),
          'html' => TRUE,
        );
        $widget_vars['preview'] = '<div class="modal-content-preview-button">' . l(t('Preview <span class="element-invisible">!widget widget</span>', array(
          '!widget' => filter_xss_admin($key),
        )), current_path(), $options) . '</div>';
      }
    }
  }
}

/**
 * Helper function to render the Panels add content link.
 */
function _panopoly_magic_render_add_content_link($vars, $content_type, $title) {
  $content_type['title'] = t('Add <span class="element-invisible">@widget_name</span>', array(
    '@widget_name' => $title,
  ));
  $content_type['panopoly_magic_add_button'] = TRUE;

  // @todo: Investigate why view_panes are missing description.
  if (empty($content_type['description'])) {
    $content_type['description'] = $title;
  }
  return theme('panels_add_content_link', array(
    'renderer' => $vars['renderer'],
    'region' => $vars['region'],
    'content_type' => $content_type,
  ));
}

/**
 * Prepares variables for theme_panopoly_magic_preview().
 */
function template_preprocess_panopoly_magic_preview(&$vars) {
  if (!empty($vars['single'])) {

    // The id really should be 'panopoly-magic-preview' but don't want to
    // break 3rd party themes or modules that depend on it.
    $vars['attributes_array']['id'] = 'panopoly-form-widget-preview';
    $vars['classes_array'][] = 'panopoly-magic-preview-single';
  }

  // For backcompat with themes that depend on the old class names.
  $vars['classes_array'][] = 'widget-preview';
  if (!empty($vars['single'])) {
    $vars['classes_array'][] = 'widget-preview-single';
  }

  // A class identifying the widget.
  if (isset($vars['preview_subtype_name'])) {
    $vars['classes_array'][] = 'panopoly-magic-preview-' . str_replace(':', '-', $vars['preview_subtype_name']);
  }

  // For backcompat with themes that depend on the fieldset markup.
  $vars['use_legacy_fieldset'] = variable_get('panopoly_magic_preview_use_legacy_fieldset', FALSE);
}

/**
 * Prepares variables for theme_panopoly_magic_preview_link().
 */
function template_preprocess_panopoly_magic_preview_link(&$vars) {
  $vars['classes_array'][] = 'panopoly-magic-preview-link';
  $vars['classes_array'][] = 'clearfix';
}

/** 
 * Returns HTML for a preview link.
 */
function theme_panopoly_magic_preview_link($vars) {

  // Since we're a theme function and not a template, template_preprocess()
  // won't run and we have to deal with 'classes_array' directly.
  $classes = implode(' ', $vars['classes_array']);
  $html = '';
  $html .= '<div class="' . $classes . '">';
  $html .= $vars['preview_link'];
  $html .= $vars['add_link'];
  if (!empty($vars['description'])) {
    $html .= '<div class="help-block">' . $vars['description'] . '</div>';
  }
  $html .= '</div>';
  return $html;
}

/**
 * Process the panels_add_content_modal() to adjust the markup to present the preview
 */
function panopoly_magic_process_panels_add_content_modal(&$vars) {
  $use_preview = variable_get('panopoly_magic_pane_add_preview', PANOPOLY_ADD_PREVIEW_DEFAULT);
  if ($use_preview == PANOPOLY_ADD_PREVIEW_DISABLED) {
    return;
  }
  $content = !empty($vars['categories'][$vars['category']]['content']) ? $vars['categories'][$vars['category']]['content'] : array();

  // If no category is selected or the category is empty or our special empty
  // category render a 'header' that will appear instead of the columns.
  if (empty($vars['category']) || empty($content) || $vars['category'] == 'root') {

    // Nothing to do since there is no preview
  }
  else {
    $titles = array_keys($content);
    natcasesort($titles);
    $col_size = count($titles) / $vars['column_count'];

    // Zero out the existing column data
    $count = 0;
    foreach ($titles as $title) {
      $which = floor($count++ / $col_size) + 1;
      $vars['columns'][$which] = '';
    }

    // Render the single preview if it's requested.
    if ($use_preview == PANOPOLY_ADD_PREVIEW_SINGLE) {
      $title = $vars['preview_single_title'];
      $legend_title = !empty($title) ? $title : t('Select a widget to show its preview');
      $add_content_link = !empty($title) ? _panopoly_magic_render_add_content_link($vars, $content[$title], $title) : '';
      $preview = !empty($vars['preview_single']) ? $vars['preview_single'] : t('No Preview');
      $vars['columns'][0] = theme('panopoly_magic_preview', array(
        'title' => $legend_title,
        'add_link' => $add_content_link,
        'preview' => $preview,
      ));
    }

    // Read the column data with our preview functionality
    $count = 0;
    foreach ($titles as $title) {
      $which = floor($count++ / $col_size) + 1;
      $legend_title = $content[$title]['title'];
      $add_content_link = _panopoly_magic_render_add_content_link($vars, $content[$title], $title);
      $description = !empty($content[$title]['description']) && variable_get('panopoly_magic_pane_show_description', 1) ? $content[$title]['description'] : '';
      if ($use_preview == PANOPOLY_ADD_PREVIEW_SINGLE) {
        $vars['columns'][$which] .= theme('panopoly_magic_preview_link', array(
          'preview_link' => $legend_title,
          'add_link' => $add_content_link,
          'description' => $description,
        ));
      }
      else {
        $preview = !empty($content[$title]['preview']) ? $content[$title]['preview'] : t('No Preview');
        $vars['columns'][$which] .= theme('panopoly_magic_preview', array(
          'title' => $legend_title,
          'add_link' => $add_content_link,
          'preview' => $preview,
          'preview_subtype_name' => $content[$title]['subtype_name'],
          'description' => $description,
        ));
      }
    }
  }
}

/*
 * Convert with legacy 'nodes' and others (such as 'files') view settings.
 */
function panopoly_magic_convert_view_settings($view_settings) {

  // The 'fields' and 'table' view settings apply to any entity type.
  if (in_array($view_settings, array(
    'fields',
    'table',
  ))) {
    return $view_settings;
  }

  // We convert other view settings to 'rendered_entity' (which could be
  // 'node' or 'files' or others that are specific to an entity type).
  return 'rendered_entity';
}

/**
 * Helper function to get the entity type retrieved by a view
 */
function panopoly_magic_get_entity_type($view) {
  $result = 'node';
  if (isset($view->base_table) && $view->base_table != 'node') {

    // Find the entity type with the corresponding base table.
    $base_table = $view->base_table;
    $entity_types = entity_get_info();
    foreach ($entity_types as $entity_type => $entity_type_info) {
      if ($entity_type_info['base table'] == $base_table) {
        $result = $entity_type;
        break;
      }
    }
  }
  return $result;
}

/**
 * Helper function to get view modes
 *
 * @param (optional) string $entity_type
 *   The type of entity for which to load view modes.
 */
function panopoly_magic_view_mode_options($entity_type = 'node') {
  $entity_info = entity_get_info($entity_type);
  $hidden_view_modes = array_filter(array_map('trim', explode("\n", variable_get('panopoly_magic_hidden_view_mode_options', PANOPOLY_MAGIC_HIDDEN_VIEW_MODE_OPTIONS))));
  $options = array();
  if (!empty($entity_info['view modes'])) {
    foreach ($entity_info['view modes'] as $mode => $settings) {
      if (!in_array($mode, $hidden_view_modes)) {
        $options[$mode] = $settings['label'];
      }
    }
  }
  return $options;
}

/**
 * Helper function to get name of the 'Add content' preview mode.
 */
function _panopoly_magic_add_content_preview_mode_name($value = NULL) {
  if (is_null($value)) {
    $value = variable_get('panopoly_magic_pane_add_preview', PANOPOLY_ADD_PREVIEW_DEFAULT);
  }
  switch ($value) {
    case PANOPOLY_ADD_PREVIEW_SINGLE:
      return 'single';
    case PANOPOLY_ADD_PREVIEW_DISABLED:
      return 'disabled';
    case PANOPOLY_ADD_PREVIEW_AUTOMATIC:
      return 'automatic';
    case PANOPOLY_ADD_PREVIEW_MANUAL:
      return 'manual';
  }
}

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

Functions

Namesort descending Description
panopoly_magic_ajax_form_callback Replaces system/ajax for pane configuration preview callback to work with ctools multi step form.
panopoly_magic_ajax_update_preview Ajax callback that just returns the rendered preview.
panopoly_magic_apps_app_info Implements hook_apps_app_info()
panopoly_magic_autosubmit_configure Recursively parse form elements to add special autosubmit handling on a per field-type basis.
panopoly_magic_configure_form Configuration Form for Panopoly Magic
panopoly_magic_convert_view_settings
panopoly_magic_fape_submit Custom submit callback for FAPE enabled stylizer forms.
panopoly_magic_form_alter Implements hook_form_alter()
panopoly_magic_form_ctools_entity_field_content_type_formatter_options_alter Implementation of hook_form_FORM_ID_alter()
panopoly_magic_form_ctools_entity_field_content_type_formatter_styles_alter
panopoly_magic_form_ctools_stylizer_edit_style_form_choose_alter Implementation of hook_form_FORM_ID_alter().
panopoly_magic_form_ctools_stylizer_edit_style_form_default_alter Implementation of hook_form_FORM_ID_alter().
panopoly_magic_form_fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form_alter Implementation of hook_form_FORM_ID_alter()
panopoly_magic_form_panels_edit_style_type_form_alter Implementation of hook_form_FORM_ID_alter().
panopoly_magic_form_post_render_preview Add the preview to the form output.
panopoly_magic_form_views_content_views_panes_content_type_edit_form_alter Implementation of hook_form_FORM_ID_alter()
panopoly_magic_get_entity_type Helper function to get the entity type retrieved by a view
panopoly_magic_init Implements hook_init()
panopoly_magic_menu Implements hook_menu()
panopoly_magic_menu_alter Implements hook_menu_alter().
panopoly_magic_module_implements_alter Implements hook_module_implements_alter()
panopoly_magic_panelizer_pre_render Implement the "Content Settings" fieldset in a pre-render. This fixes issues with image caused by initially doing this in a form_alter.
panopoly_magic_preprocess_panels_add_content_modal Preprocess the panels_add_content_modal() function to add the HTML for the preview
panopoly_magic_preview_callback_default Default 'preview callback' for widgets when we can't find something more specific.
panopoly_magic_preview_callback_fpp Default 'preview callback' for FPP widgets.
panopoly_magic_preview_callback_image Default 'preview callback' for when a 'preview image' is used.
panopoly_magic_process_panels_add_content_modal Process the panels_add_content_modal() to adjust the markup to present the preview
panopoly_magic_render_fieldable_panels_pane_preview Renders previews for fieldable_panels_pane content types.
panopoly_magic_reusable_entity_validate Validator to ensure that reusable entites have titles.
panopoly_magic_theme Implements hook_theme().
panopoly_magic_views_api Implements hook_views_api().
panopoly_magic_views_content_type_modal_submit Custom submit handler to save panels pane configuration for styling
panopoly_magic_views_pre_view Implements hook_views_pre_view()
panopoly_magic_view_mode_options Helper function to get view modes
template_preprocess_panopoly_magic_preview Prepares variables for theme_panopoly_magic_preview().
template_preprocess_panopoly_magic_preview_link Prepares variables for theme_panopoly_magic_preview_link().
theme_panopoly_magic_preview_link Returns HTML for a preview link.
theme_panopoly_magic_stylizer_preview_form Theme the stylizer preview form.
_panopoly_magic_add_content_preview_mode_name Helper function to get name of the 'Add content' preview mode.
_panopoly_magic_add_path_to_ajax Content panes should not use default system/ajax. Use our own for now.
_panopoly_magic_fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form_submit Submission callback for fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form().
_panopoly_magic_process_field_collection_fields Process any field collection fields, returning psuedo values for them.
_panopoly_magic_render_add_content_link Helper function to render the Panels add content link.
_panopoly_magic_render_preview_pane Helper function to display the pane for showing previews in the add_content modal
_panopoly_magic_visible_element_children Find visible element children.

Constants