You are here

function tablefield_field_widget_form in TableField 7.3

Same name and namespace in other branches
  1. 7 tablefield.module \tablefield_field_widget_form()
  2. 7.2 tablefield.module \tablefield_field_widget_form()

Implements hook_field_widget_form().

File

./tablefield.module, line 1248
Provides a set of fields that can be used to store tabular data with a node.

Code

function tablefield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $form['#after_build'][] = 'tablefield_after_build';
  $settings = isset($instance['display']['default']['settings']) ? $instance['display']['default']['settings'] : FALSE;
  $element['#type'] = 'tablefield';
  $form['#attributes']['enctype'] = 'multipart/form-data';
  if ($settings) {
    $hide_header = isset($settings['hide_header']) ? $settings['hide_header'] : FALSE;
  }

  /* Tablefield is sometimes embedded within another form by other modules, such
   * as Field Collection. Because of that, we cannot rely on field_name and
   * $delta to provide a unique ID for this element. Instead we use a
   * concatenation of the field parents along with the current field name,
   * language, delta and tablefield key.
   */
  $tablefield_parents = isset($element['#field_parents']) ? $element['#field_parents'] : array();
  array_push($tablefield_parents, $element['#field_name'], $element['#language'], $element['#delta'], 'tablefield');
  $id = drupal_clean_css_identifier(implode('-', $tablefield_parents));

  // IDs to use for various buttons/wrapper for this element. When processing
  // an AJAX request, these IDs are used to build the field stack so we know
  // where the value we're adjusting is in the FormAPI array.
  $ajax_wrapper_id = "{$id}-wrapper";
  $rebuild_id = "{$id}-rebuild";
  $import_id = "{$id}-import";
  $pasted_id = "{$id}-pasted";

  // Default table size.
  $default_size['count_cols'] = isset($instance['default_value'][0]['tablefield']['rebuild']['count_cols']) ? $instance['default_value'][0]['tablefield']['rebuild']['count_cols'] : 5;
  $default_size['count_rows'] = isset($instance['default_value'][0]['tablefield']['rebuild']['count_rows']) ? $instance['default_value'][0]['tablefield']['rebuild']['count_rows'] : 5;
  $default_size['header_orientation'] = isset($instance['default_value'][0]['tablefield']['rebuild']['header_orientation']) ? $instance['default_value'][0]['tablefield']['rebuild']['header_orientation'] : 'Horizontal';

  // Default table cell values.
  $default_value = NULL;

  // If there's a triggering element get the values from form state.
  if (isset($form_state['triggering_element'])) {
    if ($form_state['triggering_element']['#name'] == $rebuild_id) {

      // Rebuilding table rows/cols.
      $default_value = drupal_array_get_nested_value($form_state['tablefield_rebuild'], $tablefield_parents);
      drupal_set_message(t('Table structure rebuilt.'), 'status', FALSE);
    }
    elseif ($form_state['triggering_element']['#name'] == $import_id) {

      // Importing CSV data.
      tablefield_import_csv($form, $form_state, $langcode, $import_id, $tablefield_parents);
      $default_value = drupal_array_get_nested_value($form_state['input'], $tablefield_parents);
      $identifier = implode('--', $tablefield_parents);
      $form_state['_tablefield_imported_values'][$identifier] = $default_value;
    }
    elseif ($form_state['triggering_element']['#name'] == $pasted_id) {

      // Importing pasted data.
      tablefield_import_pasted($form, $form_state, $langcode, $pasted_id, $tablefield_parents);
      $default_value = drupal_array_get_nested_value($form_state['input'], $tablefield_parents);
      if (empty($default_value['rebuild'])) {
        $default_value['rebuild'] = $default_size;
      }
    }
    else {

      // The triggering element is neither a rebuild nor an import
      // e.g. a file upload.
      $default_value = drupal_array_get_nested_value($form_state['input'], $tablefield_parents);
    }
  }

  // If no values by now, get tablefield item value stored in database, if any.
  if (!$default_value) {

    // This could be set e.g. when using paragraphs module.
    if (isset($items[$delta]['tablefield'])) {
      $default_value = $items[$delta]['tablefield'];
    }
    elseif (isset($items[$delta]['value'])) {
      $default_value = unserialize($items[$delta]['value']);
    }
    elseif ($delta === 0 && isset($instance['default_value'][0]['tablefield'])) {
      $default_value = $instance['default_value'][0]['tablefield'];
    }
  }
  if (empty($default_value['rebuild'])) {
    $default_value['rebuild'] = $default_size;
  }
  else {

    // If this is data that was saved before 'header_orientation' was a
    // per-value setting, then we need to keep the valuef from $default_size.
    if (empty($default_value['rebuild']['header_orientation'])) {
      $default_value['rebuild']['header_orientation'] = $default_size['header_orientation'];
    }
    $default_size = $default_value['rebuild'];
  }
  $count_rows = $default_size['count_rows'];
  $count_cols = $default_size['count_cols'];
  $header_orientation = $default_size['header_orientation'];

  // Now we can build the widget.
  $help_text = '';
  if (!empty($instance['description'])) {
    $help_text = $instance['description'];
  }
  else {
    if ($settings) {
      $display_settings = l(t('default display settings'), 'admin/structure/types/manage/' . $element['#bundle'] . '/display', array(
        'attributes' => array(
          'title' => t('Manage display settings'),
        ),
      ));
      if ($hide_header == TRUE) {
        $help_text = t('This table will not have a header according to the !display_settings.', array(
          '!display_settings' => $display_settings,
        ));
      }
      else {
        $help_text = t('The first row will appear as the table header. Leave the first row blank if you do not need a header.');
      }
    }
  }
  $element['tablefield'] = array(
    '#title' => $element['#title'],
    '#description' => filter_xss_admin($help_text),
    '#attributes' => array(
      'id' => $id,
      'class' => array(
        'form-tablefield',
      ),
    ),
    '#type' => 'fieldset',
    '#tree' => TRUE,
    '#collapsible' => FALSE,
    '#prefix' => '<div id="' . $ajax_wrapper_id . '">',
    '#suffix' => '</div>',
  );
  if ($settings) {
    if ($hide_header) {
      $element['tablefield']['#attributes']['class'][] = 'table-no-headers';
    }
  }
  if (in_array($header_orientation, array(
    'Horizontal',
    'Both',
  ))) {
    $element['tablefield']['#attributes']['class'][] = 'tablefield-header-orientation-horizontal';
  }
  if (in_array($header_orientation, array(
    'Vertical',
    'Both',
  ))) {
    $element['tablefield']['#attributes']['class'][] = 'tablefield-header-orientation-vertical';
  }

  // Give the fieldset the appropriate class if it is required.
  if ($element['#required']) {
    $element['tablefield']['#title'] .= ' <span class="form-required" title="';
    $element['tablefield']['#title'] .= t('This field is required');
    $element['tablefield']['#title'] .= '">*</span>';
  }
  if (arg(0) == 'admin') {
    $element['tablefield']['#description'] = t('This form defines the table field defaults, but the number of rows/columns and content can be overridden.');
    if ($settings) {
      if (!$hide_header) {
        $element['tablefield']['#description'] .= ' ' . t('The first row will appear as the table header. Leave the first row blank if you do not need a header.');
      }
    }
  }

  // Render the form table.
  $element['tablefield']['tabledata']['a_break'] = array(
    '#markup' => '<table id="tablefield-editor">',
  );
  $default_value = isset($default_value['tabledata']) ? $default_value['tabledata'] : $default_value;

  // Make added default values visible when editing existing content.
  if (isset($instance['widget']['settings']['restrict_rebuild']) && $instance['widget']['settings']['restrict_rebuild'] && $instance['widget']['settings']['lock_values'] && arg(0) !== 'admin' && arg(0) !== 'system') {

    // Use the default number of cols/rows if they changed.
    $count_cols = $count_cols != $instance['default_value'][0]['tablefield']['rebuild']['count_cols'] ? $instance['default_value'][0]['tablefield']['rebuild']['count_cols'] : $count_cols;
    $count_rows = $count_rows != $instance['default_value'][0]['tablefield']['rebuild']['count_rows'] ? $instance['default_value'][0]['tablefield']['rebuild']['count_rows'] : $count_rows;
  }

  // Loop over all the rows.
  for ($i = 0; $i < $count_rows; $i++) {
    $zebra = $i % 2 == 0 ? 'even' : 'odd';

    // Disable table drag functionality in case of locked default cells under
    // the first row or completely on the field settings form.
    $draggable = 'draggable ';
    unset($instance['default_value'][0]['tablefield']['tabledata']["row_{$i}"]['weight']);
    if (arg(0) == 'admin' || isset($instance['default_value'][0]['tablefield']['tabledata']["row_{$i}"]) && array_filter($instance['default_value'][0]['tablefield']['tabledata']["row_{$i}"])) {
      $draggable = FALSE;
    }
    $element['tablefield']['tabledata']['b_break' . $i] = array(
      '#markup' => '<tr class="' . $draggable . 'tablefield-row-' . $i . ' ' . $zebra . '"><td class="tablefield-row-count">' . ($i + 1) . '</td>',
    );

    // Loop over all the columns.
    for ($ii = 0; $ii < $count_cols; $ii++) {
      $instance_default = isset($instance['default_value'][0]['tablefield']['tabledata']["row_{$i}"]["col_{$ii}"]) ? $instance['default_value'][0]['tablefield']['tabledata']["row_{$i}"]["col_{$ii}"] : array();
      if (!empty($instance_default) && !empty($instance['widget']['settings']['lock_values']) && arg(0) != 'admin') {

        // The value still needs to be send on every load in order for the
        // table to be saved correctly.
        $element['tablefield']['tabledata']['row_' . $i]['col_' . $ii] = array(
          '#type' => 'value',
          '#value' => $instance_default,
        );

        // Display the default value, since it's not editable.
        $element['tablefield']['tabledata']['row_' . $i]['col_' . $ii . '_display'] = array(
          '#type' => 'item',
          '#title' => $instance_default,
          '#prefix' => '<td class="col-' . $ii . '">',
          '#suffix' => '</td>',
        );
      }
      else {
        $input_type = isset($instance['widget']['settings']['input_type']) ? $instance['widget']['settings']['input_type'] : 'textfield';
        $max_length = isset($instance['widget']['settings']['max_length']) ? abs($instance['widget']['settings']['max_length']) : '2048';
        $cell_default = isset($default_value['row_' . $i]['col_' . $ii]) ? $default_value['row_' . $i]['col_' . $ii] : '';

        // If the field does not contain HTML, is not plain text and a text area
        // then covert linebreaks to <br />.
        $format = isset($items[$delta]['format']) ? $items[$delta]['format'] : 'plain_text';
        if (!($cell_default != strip_tags($cell_default)) && $format !== 'plain_text') {
          $nl2br = array(
            '_cell_validate',
          );
        }
        else {
          $nl2br = NULL;
        }
        $element['tablefield']['tabledata']['row_' . $i]['col_' . $ii] = array(
          '#type' => $input_type,
          '#maxlength' => $max_length,
          '#size' => 0,
          '#attributes' => array(
            'id' => $id . '-cell-' . $i . '-' . $ii,
            'class' => array(
              'tablefield-row-' . $i,
              'tablefield-col-' . $ii,
            ),
            'style' => 'min-width: 100%',
          ),
          '#default_value' => $cell_default,
          '#prefix' => '<td class="col-' . $ii . '">',
          '#suffix' => '</td>',
          '#element_validate' => $nl2br,
        );
      }
    }

    // Add an extra column for the weight.
    $row_weight_default = isset($default_value[$i]['weight']) ? $default_value[$i]['weight'] : $i + 1;
    $element['tablefield']['tabledata']['row_' . $i]['weight'] = array(
      '#type' => 'weight',
      '#title' => t('Weight'),
      '#default_value' => $row_weight_default,
      '#delta' => $count_rows,
      '#attributes' => array(
        'class' => array(
          'tablefield-weight',
        ),
      ),
      '#prefix' => '<td class="tabledrag-hide">',
      '#suffix' => '</td>',
    );
    $element['tablefield']['c_break' . $i] = array(
      '#markup' => '</tr>',
    );
  }
  $element['tablefield']['t_break' . $i] = array(
    '#markup' => '</table>',
  );

  // Provide caption field to describe the data contained in the table.
  $element['tablefield']['caption'] = array(
    '#type' => 'textfield',
    '#title' => t('Table description'),
    '#default_value' => '',
    '#description' => t('This brief caption will be associated with the table and will help Assistive Technology, like screen readers, better describe the content within.'),
  );

  // Could be the Paragraphs module.
  if (isset($items[$delta]['tablefield'])) {
    $raw = $items[$delta]['tablefield'];
  }
  elseif (isset($items[$delta]['value'])) {
    $raw = unserialize($items[$delta]['value']);
  }
  elseif ($delta === 0 && isset($instance['default_value'][0]['tablefield'])) {
    $raw = $instance['default_value'][0]['tablefield'];
  }
  if (isset($raw['caption'])) {
    $element['tablefield']['caption']['#default_value'] = check_plain($raw['caption']);
  }

  // If the user doesn't have rebuild perms, we pass along the data as a value.
  // Otherwise we provide form elements to specify the size and ajax rebuild.
  if (isset($instance['widget']['settings']['restrict_rebuild']) && $instance['widget']['settings']['restrict_rebuild'] && !user_access('rebuild tablefield') || isset($instance['widget']['settings']['restrict_rebuild']) && $instance['widget']['settings']['restrict_rebuild'] && $instance['widget']['settings']['lock_values'] && arg(0) !== 'admin' && arg(0) !== 'system') {
    $element['tablefield']['rebuild'] = array(
      '#type' => 'value',
      '#tree' => TRUE,
      'count_cols' => array(
        '#type' => 'value',
        '#value' => $count_cols,
      ),
      'count_rows' => array(
        '#type' => 'value',
        '#value' => $count_rows,
      ),
      'header_orientation' => array(
        '#type' => 'value',
        '#value' => $header_orientation,
      ),
      'rebuild' => array(
        '#type' => 'value',
        '#value' => t('Rebuild Table'),
      ),
    );
  }
  else {
    $element['tablefield']['rebuild'] = array(
      '#type' => 'fieldset',
      '#tree' => TRUE,
      '#title' => t('Number of rows/columns and header orientation'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $element['tablefield']['rebuild']['count_cols'] = array(
      '#title' => t('How many Columns'),
      '#type' => 'textfield',
      '#size' => 5,
      '#prefix' => '<div class="clearfix">',
      '#suffix' => '</div>',
      '#value' => $count_cols,
    );
    $element['tablefield']['rebuild']['count_rows'] = array(
      '#title' => t('How many Rows'),
      '#type' => 'textfield',
      '#size' => 5,
      '#prefix' => '<div class="clearfix">',
      '#suffix' => '</div>',
      '#value' => $count_rows,
    );
    $element['tablefield']['rebuild']['header_orientation'] = array(
      '#type' => 'select',
      '#title' => t('Header orientation'),
      '#options' => array(
        'Horizontal' => t('First row (horizontal)'),
        'Vertical' => t('First column (vertical)'),
        'Both' => t('Both first row and first column (two headers, horizontal and vertical)'),
      ),
      '#default_value' => $header_orientation,
    );
    $element['tablefield']['rebuild']['rebuild'] = array(
      '#type' => 'button',
      '#validate' => array(),
      '#limit_validation_errors' => array(),
      '#executes_submit_callback' => TRUE,
      '#submit' => array(
        'tablefield_rebuild_form',
      ),
      '#value' => t('Rebuild Table'),
      '#name' => $rebuild_id,
      '#attributes' => array(
        'class' => array(
          'tablefield-rebuild',
        ),
      ),
      '#ajax' => array(
        'callback' => 'tablefield_rebuild_form_ajax',
        'wrapper' => $ajax_wrapper_id,
        'effect' => 'fade',
      ),
    );
  }
  if (user_access('always use additional datasources') || isset($instance['widget']['settings']['data_sources']['upload']) && $instance['widget']['settings']['data_sources']['upload'] === 'upload') {

    // Allow the user to import a csv file.
    $element['tablefield']['import'] = array(
      '#type' => 'fieldset',
      '#tree' => TRUE,
      '#title' => t('Upload CSV file'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $element['tablefield']['import']['file'] = array(
      '#name' => 'files[' . $import_id . ']',
      '#title' => t('File upload'),
      '#type' => 'file',
    );
    $element['tablefield']['import']['import'] = array(
      '#type' => 'button',
      '#validate' => array(),
      '#limit_validation_errors' => array(),
      '#executes_submit_callback' => TRUE,
      '#submit' => array(
        'tablefield_rebuild_form',
      ),
      '#value' => t('Upload CSV'),
      '#name' => $import_id,
      '#attributes' => array(
        'class' => array(
          'tablefield-rebuild',
        ),
      ),
      '#ajax' => array(
        'callback' => 'tablefield_rebuild_form_ajax',
        'wrapper' => $ajax_wrapper_id,
        'effect' => 'fade',
      ),
    );
  }
  if (user_access('always use additional datasources') || isset($instance['widget']['settings']['data_sources']['paste']) && $instance['widget']['settings']['data_sources']['paste'] === 'paste') {

    // Allow user to paste data (e.g. from Excel).
    $element['tablefield']['paste'] = array(
      '#type' => 'fieldset',
      '#tree' => TRUE,
      '#title' => t('Copy & Paste'),
      '#attributes' => array(
        'class' => array(
          'tablefield-extra tablefield-paste',
        ),
      ),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $delimiters = array(
      'TAB' => t('TAB'),
      ',' => t('Comma ,'),
      ';' => t('Semicolon ;'),
      '|' => t('Pipe |'),
      '+' => t('Plus +'),
      ':' => t('Colon :'),
    );
    $element['tablefield']['paste']['paste_delimiter'] = array(
      '#type' => 'select',
      '#tree' => TRUE,
      '#title' => t('Column separator'),
      '#name' => 'delimiter[' . $pasted_id . ']',
      '#options' => $delimiters,
      '#description' => t('Data copied from Excel will use TAB.'),
    );
    $element['tablefield']['paste']['data'] = array(
      '#type' => 'textarea',
      '#tree' => TRUE,
      '#name' => 'data[' . $pasted_id . ']',
      '#title' => t('Paste table data here:'),
    );
    $element['tablefield']['paste']['paste_import'] = array(
      '#type' => 'button',
      '#validate' => array(),
      '#limit_validation_errors' => array(),
      '#executes_submit_callback' => TRUE,
      '#submit' => array(
        'tablefield_rebuild_form',
      ),
      '#value' => t('Import & Rebuild'),
      '#name' => $pasted_id,
      '#ajax' => array(
        'callback' => 'tablefield_rebuild_form_ajax',
        'wrapper' => $ajax_wrapper_id,
        'effect' => 'fade',
      ),
    );
  }

  // Allow the user to select input filters.
  if (!empty($instance['widget']['settings']['cell_processing'])) {
    $element['#base_type'] = $element['#type'];
    $element['#type'] = 'text_format';
    $element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
    if (module_exists('wysiwyg')) {
      $element['#wysiwyg'] = FALSE;
    }
  }
  $form['element']['#element_validate'][] = 'tablefield_field_widget_form_validate';
  return $element;
}