You are here

language_hierarchy_form.inc in Language Hierarchy 7

File

language_hierarchy_form.inc
View source
<?php

/**
 * Build the parent-child Language hierarchy form.
 *
 * @param $form_state
 *   The form state.
 *
 * @return array
 *   A form array set for theming by theme_language_hierarchy_form()
 *
 */
function language_hierarchy_form($form_state) {

  // Identify that the elements in 'language_items' are a collection, to
  // prevent Form API from flattening the array when submitted.
  $form['language_items']['#tree'] = TRUE;

  // Tabledrag will take care of updating the parent_id relationship on each
  // row of our table when we drag items around, but we need to build out the
  // initial tree structure ourselves.  This means ordering our items such
  // that children items come directly after their parent items, and all items
  // are sorted by weight relative to their siblings.
  // To keep this from cluttering the actual tabledrag code, we have moved
  // this to a dedicated function.
  // Fetch the language data from the database, ordered by parent/child/weight.
  $languages = language_hierarchy_language_list();

  // Initialise checkboxes array.
  $options = array();

  // Iterate through each database result.
  foreach ($languages as $item) {

    // Create list of enabled languages and the language checkboxes options for later use.
    $options[$item->language] = '';
    if ($item->enabled) {
      $enabled[] = $item->language;
    }

    // Create a form entry for this item.
    //
    // Each entry will be an array using the the unique language code for that item as
    // the array key, and an array of table row data as the value.
    $form['language_items'][$item->language] = array(
      // We'll use a form element of type '#markup' to display the language name.
      'name' => array(
        '#markup' => $item->name,
      ),
      // Add the native name.
      'native' => array(
        '#markup' => $item->native,
      ),
      // Add the language code. We use a seperate markup code column for display as
      // the hidden one is used in the table drag.
      'code' => array(
        '#markup' => $item->language,
      ),
      // Add language direction
      'direction' => array(
        '#markup' => $item->direction == LANGUAGE_RTL ? t('Right to left') : t('Left to right'),
      ),
      // For parent/child relationships, we also need to add form items to
      // store the current item's unique id and parent item's unique id.
      //
      // We would normally use a hidden element for this, but for this example
      // we'll use a disabled textfield element called 'id' so that we can
      // display the current item's id in the table.
      //
      // Because tabledrag modifies the #value of this element, we use
      // '#default_value' instead of '#value' when defining a hidden element.
      // Also, because tabledrag modifies '#value', we cannot use a markup
      // element, which does not support the '#value' property. (Markup
      // elements use the '#markup' property instead.)
      'id' => array(
        '#type' => 'hidden',
        '#size' => 3,
        '#default_value' => $item->language,
        '#disabled' => TRUE,
      ),
      // The same information holds true for the parent id field as for the
      // item id field, described above.
      'pid' => array(
        '#type' => 'textfield',
        '#size' => 3,
        '#default_value' => $item->parent,
      ),
      // The 'weight' field will be manipulated as we move the items around in
      // the table using the tabledrag activity.  We use the 'weight' element
      // defined in Drupal's Form API.
      'weight' => array(
        '#type' => 'weight',
        '#title' => t('Weight'),
        '#default_value' => $item->weight,
        '#delta' => 10,
        '#title_display' => 'invisible',
      ),
      // We'll use a hidden form element to pass the current 'depth' of each
      // item within our parent/child tree structure to the theme function.
      // This will be used to calculate the initial amount of indentation to
      // add before displaying any child item rows.
      'depth' => array(
        '#type' => 'hidden',
        '#value' => $item->depth,
      ),
    );
  }

  // Create language enabled checkboxes items. We'll render it by row later.
  $form['enabled'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Enabled languages'),
    '#title_display' => 'invisible',
    '#options' => $options,
    '#default_value' => $enabled,
  );

  // Site default language.
  $form['site_default'] = array(
    '#type' => 'radios',
    '#title' => t('Default language'),
    '#title_display' => 'invisible',
    '#options' => $options,
    '#default_value' => language_default('language'),
  );

  // Now we add our submit button, for submitting the form results.
  //
  // The 'actions' wrapper used here isn't strictly necessary for tabledrag,
  // but is included as a Form API recommended practice.
  $form['actions'] = array(
    '#type' => 'actions',
  );
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save Changes'),
  );
  return $form;
}

/**
 * Theme callback for the language hierarchy form.
 *
 * The theme callback will format the $form data structure into a table and
 * add our tabledrag functionality.  (Note that drupal_add_tabledrag should be
 * called from the theme layer, and not from a form declaration.  This helps
 * keep template files clean and readable, and prevents tabledrag.js from
 * being added twice accidently.
 *
 */
function theme_language_hierarchy_form($variables) {
  $form = $variables['form'];

  // Initialize the variable which will store our table rows.
  $rows = array();

  // Iterate over each element in our $form['language_items'] array.
  foreach (element_children($form['language_items']) as $id) {

    // Before we add our 'weight' column to the row, we need to give the
    // element a custom class so that it can be identified in the
    // drupal_add_tabledrag call.
    //
    // This could also have been done during the form declaration by adding
    // @code
    //   '#attributes' => array('class' => 'language-item-weight'),
    // @endcode
    // directly to the 'weight' element in tabledrag_example_simple_form().
    $form['language_items'][$id]['weight']['#attributes']['class'] = array(
      'language-item-weight',
    );

    // In the parent/child example, we must also set this same custom class on
    // our id and parent_id columns (which could also have been done within
    // the form declaration, as above).
    $form['language_items'][$id]['id']['#attributes']['class'] = array(
      'language-item-id',
    );
    $form['language_items'][$id]['pid']['#attributes']['class'] = array(
      'language-item-pid',
    );

    // To support the tabledrag behaviour, we need to assign each row of the
    // table a class attribute of 'draggable'. This will add the 'draggable'
    // class to the <tr> element for that row when the final table is
    // rendered.
    $class = array(
      'draggable',
    );

    // If this is a child element, we need to add some indentation to the row,
    // so that it appears nested under its parent.  Our $depth parameter was
    // calculated while building the language data.
    $indent = theme('indentation', array(
      'size' => $form['language_items'][$id]['depth']['#value'],
    ));
    unset($form['language_items'][$id]['depth']);

    // Disable checkbox for the default language, because it cannot be disabled.
    if ($id == language_default()->language) {
      $form['enabled'][$id]['#attributes']['disabled'] = 'disabled';
    }

    // We are now ready to add each element of our $form data to the $rows
    // array, so that they end up as individual table cells when rendered
    // in the final table.  We run each element through the drupal_render()
    // function to generate the final html markup for that element.
    // Create operations links.
    $operations_links = l(t('edit'), 'admin/config/regional/language/edit/' . $id) . ($id != 'en' && $id != language_default()->language ? ' ' . l(t('delete'), 'admin/config/regional/language/delete/' . $id) : '');

    // Add our hidden 'id' column into the operations links column.
    $operations_links .= drupal_render($form['language_items'][$id]['id']);
    $rows[] = array(
      'data' => array(
        // Add our 'name' column, being sure to include our indentation.
        $indent . drupal_render($form['language_items'][$id]['name']),
        // Add our 'native' column.
        drupal_render($form['language_items'][$id]['native']),
        // Add 'code' column.
        drupal_render($form['language_items'][$id]['code']),
        // Add 'direction' column.
        drupal_render($form['language_items'][$id]['direction']),
        // Add 'enabled' checkboxes.
        array(
          'data' => drupal_render($form['enabled'][$id]),
          'align' => 'center',
        ),
        // Add our 'site_default' column.
        drupal_render($form['site_default'][$id]),
        // Add operations links column with hidden IDs form elements.
        $operations_links,
        // Add our 'weight' column.
        drupal_render($form['language_items'][$id]['weight']),
        // Add our hidden 'parent id' column.
        drupal_render($form['language_items'][$id]['pid']),
      ),
      // To support the tabledrag behaviour, we need to assign each row of the
      // table a class attribute of 'draggable'. This will add the 'draggable'
      // class to the <tr> element for that row when the final table is
      // rendered.
      'class' => $class,
    );
  }

  // We now define the table header values.  Ensure that the 'header' count
  // matches the final column count for your table.
  //
  // Normally, we would hide the headers on our hidden columns, but we are
  // leaving them visible in this example.
  // $header = array(t('Name'), t('Description'), '', '', '');
  $header = array(
    t('Name'),
    t('Native name'),
    t('Code'),
    t('Direction'),
    t('Enabled'),
    t('Default'),
    t('Operations'),
    t('Weight'),
    t('PID'),
  );

  // We also need to pass the drupal_add_tabledrag() function an id which will
  // be used to identify the <table> element containing our tabledrag form.
  // Because an element's 'id' should be unique on a page, make sure the value
  // you select is NOT the same as the form ID used in your form declaration.
  $table_id = 'language-items-table';

  // We can render our tabledrag table for output.
  $output = theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => array(
      'id' => $table_id,
    ),
  ));

  // And then render any remaining form elements (such as our submit button).
  $output .= drupal_render_children($form);

  // We now call the drupal_add_tabledrag() function in order to add the
  // tabledrag.js goodness onto our page.
  //
  // For our parent/child tree table, we need to pass it:
  // - the $table_id of our <table> element (language-items-table),
  // - the $action to be performed on our form items ('match'),
  // - a string describing where $action should be applied ('parent'),
  // - the $group value (pid column) class name ('language-item-pid'),
  // - the $subgroup value (pid column) class name ('language-item-pid'),
  // - the $source value (id column) class name ('language-item-id'),
  // - an optional $hidden flag identifying if the columns should be hidden,
  // - an optional $limit parameter to control the max parenting depth.
  drupal_add_tabledrag($table_id, 'match', 'parent', 'language-item-pid', 'language-item-pid', 'language-item-id', TRUE);

  // Because we also want to sort in addition to providing parenting, we call
  // the drupal_add_tabledrag function again, instructing it to update the
  // weight field as items at the same level are re-ordered.
  drupal_add_tabledrag($table_id, 'order', 'sibling', 'language-item-weight', NULL, NULL, TRUE);
  return $output;
}

/**
 * Submit callback for the language_hierarchy_form form.
 *
 * Updates the 'weight' column for each element in our table, taking into
 * account that item's new order after the drag and drop actions have been
 * performed.
 *
 */
function language_hierarchy_form_submit($form, &$form_state) {

  // Reset static cache language list is not fetched from database
  drupal_static_reset('language_hierarchy_language_list');
  drupal_static_reset('language_hierarchy_get_children');
  drupal_static_reset('language_hierarchy_get_root_languages');

  // Because the form elements were keyed with the item ids from the database,
  // we can simply iterate through the submitted values.
  foreach ($form_state['values']['language_items'] as $langcode => $item) {

    // Update the language row in the db.
    db_update('languages')
      ->fields(array(
      'parent' => $item['pid'],
    ))
      ->condition('language', $item['id'], '=')
      ->execute();

    // Copy weights data to where locale submit function expects it.
    $form_state['values']['weight'][$item['id']] = $item['weight'];
  }

  // Run the original locale submit function to deal with core locale form functionality.
  // This includes enabling, settings default language and weight saving.
  module_load_include('inc', 'locale', 'locale.admin');
  locale_languages_overview_form_submit($form, $form_state);

  // Normalize weight, so languages are returned in correct order for flat lists.
  _language_hierarchy_normalize_weight();
}

Functions

Namesort descending Description
language_hierarchy_form Build the parent-child Language hierarchy form.
language_hierarchy_form_submit Submit callback for the language_hierarchy_form form.
theme_language_hierarchy_form Theme callback for the language hierarchy form.