You are here

field_collection_fieldset.module in Field Collection Fieldset 7.2

Same filename and directory in other branches
  1. 7 field_collection_fieldset.module

Module implementing a field-collection fieldset formatter.

File

field_collection_fieldset.module
View source
<?php

/**
 * @file
 * Module implementing a field-collection fieldset formatter.
 */

/**
 * Implements hook_field_formatter_info().
 */
function field_collection_fieldset_field_formatter_info() {
  return array(
    'field_collection_fieldset_view' => array(
      'label' => t('Fieldset of field collection items'),
      'field types' => array(
        'field_collection',
      ),
      'settings' => array(
        'edit' => t('Edit'),
        'delete' => t('Delete'),
        'add' => t('Add'),
        'description' => TRUE,
        'view_mode' => 'full',
        'legend' => '',
        'collapsible' => TRUE,
        'collapsed' => FALSE,
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function field_collection_fieldset_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $element = field_collection_field_formatter_settings_form($field, $instance, $view_mode, $form, $form_state);
  if ($display['type'] == 'field_collection_fieldset_view') {
    $fields_info = field_info_instances('field_collection_item', $field['field_name']);
    $options = array(
      t('None'),
    );
    foreach ($fields_info as $field_info) {
      $options[$field_info['field_name']] = $field_info['label'];
    }
    $element['legend'] = array(
      '#type' => 'select',
      '#title' => t('Legend'),
      '#options' => $options,
      '#default_value' => $settings['legend'],
    );
    $element['collapsible'] = array(
      '#type' => 'checkbox',
      '#title' => t('Collapsible'),
      '#default_value' => $settings['collapsible'],
    );
    $element['collapsed'] = array(
      '#type' => 'checkbox',
      '#title' => t('Collapsed'),
      '#default_value' => $settings['collapsed'],
      '#states' => array(
        'enabled' => array(
          ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][collapsible]"]' => array(
            'checked' => TRUE,
          ),
        ),
      ),
    );
  }
  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function field_collection_fieldset_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $output = field_collection_field_formatter_settings_summary($field, $instance, $view_mode);
  if ($display['type'] == 'field_collection_fieldset_view') {
    $output .= '<br />';
    $output .= $settings['description'] ? t('Show the field description beside the add link: Yes') : t('Show the field description beside the add link: No');
    $output .= '<br />';
    $output .= t('Legend: @field', array(
      '@field' => $settings['legend'],
    ));
    $output .= '<br />';
    $output .= $settings['collapsible'] ? t('Collapsible: Yes') : t('Collapsible: No');
    $output .= '<br />';
    $output .= $settings['collapsed'] ? t('Collapsed: Yes') : t('Collapsed: No');
  }
  return $output;
}

/**
 * Implements hook_field_formatter_view().
 */
function field_collection_fieldset_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $settings = $display['settings'];
  switch ($display['type']) {
    case 'field_collection_fieldset_view':
      $view_mode = !empty($display['settings']['view_mode']) ? $display['settings']['view_mode'] : 'full';
      foreach ($items as $delta => $item) {
        if ($field_collection = field_collection_field_get_entity($item)) {
          $view = $field_collection
            ->view($view_mode);
          $content = $view['field_collection_item'][$field_collection
            ->identifier()];

          // Classes array used for fieldset.
          $class = array();
          if ($settings['legend'] && !empty($content[$settings['legend']])) {
            if ($settings['collapsible']) {
              $class[] = 'collapsible';
              $class[] = $settings['collapsed'] ? 'collapsed' : '';
            }

            // Hide the field, which is used as a legend.
            $view['field_collection_item'][$field_collection
              ->identifier()][$settings['legend']]['#access'] = FALSE;
          }
          $element[$delta]['#theme_wrappers'] = array(
            'field_collection_view',
          );
          $element[$delta]['#attributes']['class'][] = 'field-collection-view';
          $element[$delta]['#attributes']['class'][] = 'clearfix';
          $element[$delta]['entity'] = array(
            '#theme' => 'fieldset',
            '#title' => $settings['legend'] ? render($content[$settings['legend']]) : '',
            '#value' => render($view),
            '#collapsible' => $settings['collapsible'],
            '#collapsed' => $settings['collapsed'],
            '#attributes' => array(
              'class' => $class,
            ),
          );
          $links = array(
            '#theme' => 'links__field_collection_view',
          );
          $links['#attributes']['class'][] = 'field-collection-view-links';
          foreach (array(
            'edit',
            'delete',
          ) as $op) {
            if ($settings[$op] && field_collection_item_access($op == 'edit' ? 'update' : $op, $field_collection)) {
              $links['#links'][$op] = array(
                'title' => entity_i18n_string("field:{$field['field_name']}:{$instance['bundle']}:setting_{$op}", $settings[$op]),
                'href' => $field_collection
                  ->path() . '/' . $op,
                'query' => drupal_get_destination(),
              );
            }
          }
          $element[$delta]['links'] = $links;
        }
      }

      // Attach the js to work fieldset and css for fix legend's arrow.
      if (!empty($element) && $settings['collapsible'] && $settings['legend']) {
        $element['#attached'] = array(
          'css' => array(
            drupal_get_path('module', 'field_collection_fieldset') . '/css/field-collection-fieldset.css',
          ),
          'library' => array(
            array(
              'system',
              'drupal.form',
            ),
            array(
              'system',
              'drupal.collapse',
            ),
          ),
        );
      }

      // Attach a css for styling op links.
      if (!empty($links['#links'])) {
        $element['#attached']['css'][] = drupal_get_path('module', 'field_collection') . '/field_collection.theme.css';
      }
      field_collection_field_formatter_links($element, $entity_type, $entity, $field, $instance, $langcode, $items, $display);
      break;
  }
  return $element;
}

/**
 * Implements hook_field_widget_info().
 */
function field_collection_fieldset_field_widget_info() {
  return array(
    'field_collection_fieldset' => array(
      'label' => t('Fieldset'),
      'field types' => array(
        'field_collection',
      ),
      'settings' => array(
        'field_as_label' => array(),
        'replace_label' => FALSE,
        'collapsed' => FALSE,
        'last_item_collapsed' => FALSE,
        'new_item_open' => FALSE,
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_NONE,
      ),
    ),
  );
}

/**
 * Implements hook_field_collection_fieldset_field_as_label_info().
 */
function field_collection_fieldset_field_collection_fieldset_field_as_label_info($field) {
  $field_types = _field_collection_fieldset_field_as_label_callbacks();
  $field_as_label_info = array();
  $entity_label = t('Field Collection field:');
  $fields_info = field_info_instances('field_collection_item', $field['field_name']);
  foreach ($fields_info as $field_info) {
    $info = field_info_field($field_info['field_name']);
    if (isset($field_types[$info['type']])) {
      $field_as_label_info[$field_info['field_name']] = $field_types[$info['type']];
      $field_as_label_info[$field_info['field_name']]['label'] = $entity_label . ' ' . $field_info['label'];
    }
  }
  return $field_as_label_info;
}

/**
 * Implements hook_field_widget_settings_form().
 */
function field_collection_fieldset_field_widget_settings_form($field, $instance) {
  $widget = $instance['widget'];
  $settings = $widget['settings'];
  $form = array();
  if ($widget['type'] == 'field_collection_fieldset') {
    $form['field_as_label'] = array(
      '#title' => t('Field as label'),
      '#description' => t('Set a field to display as a label'),
      '#type' => 'select',
      '#default_value' => isset($settings['field_as_label']) ? $settings['field_as_label'] : array(),
      '#empty_option' => t('None'),
      '#options' => _field_collection_fieldset_get_fields($field),
      '#weight' => 5,
    );
    $form['replace_label'] = array(
      '#type' => 'checkbox',
      '#title' => t('Replace a label on "@label @delta" pattern.'),
      '#default_value' => $settings['replace_label'],
      '#description' => t('If checked, the label of fieldset will be overwritten on "@label @delta" pattern.'),
      '#weight' => 6,
      '#states' => array(
        'enabled' => array(
          ':input[name="instance[widget][settings][field_as_label]"]' => array(
            'value' => '',
          ),
        ),
      ),
    );
    $form['collapsed'] = array(
      '#type' => 'checkbox',
      '#title' => t('Collapsed'),
      '#default_value' => $settings['collapsed'],
      '#description' => t('If checked, items are collapsed by default.'),
      '#weight' => 7,
    );
    $form['last_item_collapsed'] = array(
      '#type' => 'checkbox',
      '#title' => t('Last item collapsed'),
      '#default_value' => $settings['last_item_collapsed'],
      '#description' => t('If checked, last item is collapsed by default.'),
      '#weight' => 8,
      '#states' => array(
        'enabled' => array(
          ':input[name="instance[widget][settings][collapsed]"]' => array(
            'checked' => TRUE,
          ),
        ),
      ),
    );
    $form['new_item_open'] = array(
      '#type' => 'checkbox',
      '#title' => t('New item force open'),
      '#default_value' => $settings['new_item_open'],
      '#description' => t('If checked, new item added will be open.'),
      '#weight' => 9,
      '#states' => array(
        'enabled' => array(
          ':input[name="instance[widget][settings][collapsed]"]' => array(
            'checked' => TRUE,
          ),
          ':input[name="instance[widget][settings][last_item_collapsed]"]' => array(
            'checked' => TRUE,
          ),
        ),
      ),
    );
  }
  return $form;
}

/**
 * Implements hook_field_widget_form().
 */
function field_collection_fieldset_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  static $recursion = 0;
  switch ($instance['widget']['type']) {
    case 'field_collection_fieldset':

      // If the field collection item form contains another field collection,
      // we might ran into a recursive loop. Prevent that.
      if ($recursion++ > 3) {
        drupal_set_message(t('The field collection item form has not been embedded to avoid recursive loops.'), 'error');
        return $element;
      }
      $field_parents = $element['#field_parents'];
      $field_name = $element['#field_name'];
      $language = $element['#language'];

      // Nest the field collection item entity form in a dedicated parent space,
      // by appending [field_name, langcode, delta] to the current parent space.
      // That way the form values of the field collection item are separated.
      $parents = array_merge($field_parents, array(
        $field_name,
        $language,
        $delta,
      ));
      $element += array(
        '#element_validate' => array(
          'field_collection_field_widget_embed_validate',
        ),
        '#parents' => $parents,
      );
      $element['#type'] = 'fieldset';
      $element['#collapsible'] = TRUE;
      $element['#collapsed'] = $instance['widget']['settings']['collapsed'];
      if ($instance['widget']['settings']['replace_label']) {
        $element['#title'] = t('@label @delta', array(
          '@label' => $instance['label'],
          '@delta' => $delta + 1,
        ));
        if (function_exists('i18n_string_object_translate')) {
          $translated = i18n_string_object_translate('field_instance', $instance);
          $element['#title'] = format_string('@label @delta', array(
            '@label' => $translated['label'],
            '@delta' => $delta + 1,
          ));
        }
      }
      else {
        $element['#title'] = $instance['label'];
        if (function_exists('i18n_string_object_translate')) {
          $translated = i18n_string_object_translate('field_instance', $instance);
          $element['#title'] = $translated['label'];
        }
      }
      $field_state = field_form_get_state($field_parents, $field_name, $language, $form_state);
      if (!empty($field['settings']['hide_blank_items']) && $delta == $field_state['items_count'] && $delta > 0 && $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {

        // Do not add a blank item. Also see
        // field_collection_fieldset_field_attach_form() for correcting
        // #max_delta.
        $recursion--;
        return FALSE;
      }
      elseif (!empty($field['settings']['hide_blank_items']) && $field_state['items_count'] == 0) {

        // We show one item, so also specify that as item count. So when the
        // add button is pressed the item count will be 2 and we show to items.
        $field_state['items_count'] = 1;
      }
      if (isset($field_state['entity'][$delta])) {
        $field_collection_item = $field_state['entity'][$delta];
      }
      else {
        if (isset($items[$delta])) {
          $field_collection_item = field_collection_field_get_entity($items[$delta], $field_name);
        }

        // Show an empty collection if we have no existing one or it does not
        // load.
        if (empty($field_collection_item)) {
          if ($instance['widget']['settings']['collapsed'] && $instance['widget']['settings']['last_item_collapsed'] && $instance['widget']['settings']['new_item_open']) {
            $element['#collapsed'] = FALSE;
          }
          $field_collection_item = entity_create('field_collection_item', array(
            'field_name' => $field_name,
          ));
        }

        // Put our entity in the form state, so FAPI callbacks can access it.
        $field_state['entity'][$delta] = $field_collection_item;
      }
      if (!empty($instance['widget']['settings']['field_as_label'])) {
        $field_as_label = $instance['widget']['settings']['field_as_label'];
        $field_collection_item_wrapper = entity_metadata_wrapper('field_collection_item', $field_collection_item);
        $field_as_label_context = array(
          'field_as_label' => $field_as_label,
          'field_collection_item_wrapper' => $field_collection_item_wrapper,
        );
        $field_as_label_info = module_invoke_all('field_collection_fieldset_field_as_label_info', $field);
        $func = $field_as_label_info[$field_as_label]['callback'];
        if (function_exists($func) && ($title = call_user_func($func, $field_as_label_context))) {
          $element['#title'] = $title;
        }
        drupal_alter('field_collection_fieldset_field_as_label', $element['#title'], $field_as_label_context);
      }
      field_form_set_state($field_parents, $field_name, $language, $form_state, $field_state);
      field_attach_form('field_collection_item', $field_collection_item, $element, $form_state, $language);
      if (empty($element['#required'])) {
        $element['#after_build'][] = 'field_collection_field_widget_embed_delay_required_validation';
      }
      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
        $element['remove_button'] = array(
          '#delta' => $delta,
          '#name' => implode('_', $parents) . '_remove_button',
          '#type' => 'submit',
          '#value' => t('Remove'),
          '#validate' => array(),
          '#submit' => array(
            'field_collection_remove_submit',
          ),
          '#limit_validation_errors' => array(),
          '#ajax' => array(
            'callback' => 'field_collection_remove_js',
            'effect' => 'fade',
          ),
          '#weight' => 1000,
        );
      }
      $recursion--;
      return $element;
  }
}

/**
 * Implements hook_field_attach_form().
 *
 * Corrects #max_delta when we hide the blank field collection item.
 *
 * @see field_add_more_js()
 * @see field_collection_fieldset_field_widget_form()
 */
function field_collection_fieldset_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  foreach (field_info_instances($entity_type, $form['#bundle']) as $field_name => $instance) {
    $field = field_info_field($field_name);
    if ($field['type'] == 'field_collection' && $field['settings']['hide_blank_items'] && field_access('edit', $field, $entity_type) && $instance['widget']['type'] == 'field_collection_fieldset' && $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
      $element_langcode = $form[$field_name]['#language'];
      if ($form[$field_name][$element_langcode]['#max_delta'] > 0) {
        $form[$field_name][$element_langcode]['#max_delta']--;
      }

      // Do not collapsed last item.
      if ($instance['widget']['settings']['collapsed'] && !$instance['widget']['settings']['last_item_collapsed']) {
        $max_delta = $form[$field_name][$element_langcode]['#max_delta'];
        $form[$field_name][$element_langcode][$max_delta]['#collapsed'] = FALSE;
      }
      $element_langcode = $form[$field_name]['#language'];
      $element_wrapper = $form[$field_name][$element_langcode]['add_more']['#ajax']['wrapper'];
      for ($i = 0; $i <= $form[$field_name][$element_langcode]['#max_delta']; $i++) {
        if (isset($form[$field_name][$element_langcode][$i]['remove_button'])) {
          $form[$field_name][$element_langcode][$i]['remove_button']['#ajax']['wrapper'] = $element_wrapper;
        }
      }
    }
  }
}

/**
 * Process a field collection label for widget form.
 *
 * @param array $context
 *   An array with contextual information used to build the title:
 *     - field_as_label the name of the field used as label.
 *     - field_collection_item_wrapper the field collection for which
 *     a label is created.
 *
 * @return string
 *   Title for the field collection.
 */
function field_collection_fieldset_field_as_label_callback($context) {
  $title = '';
  if ($field_value = $context['field_collection_item_wrapper']->{$context['field_as_label']}
    ->value()) {
    $info = field_info_field($context['field_as_label']);
    switch ($info['type']) {
      case 'file':
      case 'image':
        if (!empty($field_value['alt'])) {
          $title = $field_value['alt'];
        }
        elseif (!empty($field_value['title'])) {
          $title = $field_value['title'];
        }
        elseif (!empty($field_value['description'])) {
          $title = $field_value['description'];
        }
        else {
          $title = $field_value['filename'];
        }
        break;
      case 'text':
      case 'text_long':
        $title = is_array($field_value) ? strip_tags($field_value['value']) : strip_tags($field_value);
        break;
      case 'list_text':
        $title = $field_value;
        break;
      case 'link_field':
        $title = !empty($field_value['title']) ? $field_value['title'] : $field_value['url'];
        break;
      case 'taxonomy_term_reference':
        $title = $field_value->name;
        break;
      case 'entityreference':
        $target_type = $info['settings']['target_type'];
        $title = entity_access('view', $target_type, $field_value) ? entity_label($target_type, $field_value) : t('- Restricted access -');
        break;
    }
  }
  return $title;
}

/**
 * Get fields from field collection field.
 *
 * @param array $field
 *   The field structure being configured.
 *
 * @return array
 *   An array of fields keyed by the field_name.
 */
function _field_collection_fieldset_get_fields($field) {
  $field_as_label_info = module_invoke_all('field_collection_fieldset_field_as_label_info', $field);
  $options = array_map('_field_collection_fieldset_field_get_label', $field_as_label_info);
  return $options;
}

/**
 * Helper function that return field's label.
 *
 * @param array $field_as_label_option
 *   An array contains callback and label for field.
 *
 * @return string
 *   A field name.
 */
function _field_collection_fieldset_field_get_label($field_as_label_option) {
  return $field_as_label_option['label'];
}

/**
 * Helper function that returns the field as label callbacks.
 *
 * @return array
 *   An array contains callbacks for fields.
 */
function _field_collection_fieldset_field_as_label_callbacks() {
  return array(
    'text' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
    'text_long' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
    'image' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
    'file' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
    'link_field' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
    'list_text' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
    'taxonomy_term_reference' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
    'entityreference' => array(
      'callback' => 'field_collection_fieldset_field_as_label_callback',
    ),
  );
}