You are here

languagefield.module in Custom Language field 7

Same filename and directory in other branches
  1. 8 languagefield.module

Provides a list of languages from ISO 639 standard. All variables of a $language-object can be found here: http://api.drupal.org/api/drupal/developer--globals.php/global/language

File

languagefield.module
View source
<?php

/**
 * @file
 * Provides a list of languages from ISO 639 standard.
 * All variables of a $language-object can be found here: http://api.drupal.org/api/drupal/developer--globals.php/global/language
 */

// The following are from Languagefield.
define('LANGUAGEFIELD_LANGUAGES_ALL', '1');
define('LANGUAGEFIELD_LANGUAGES_ENABLED', '2');
define('LANGUAGEFIELD_LANGUAGES_INSTALLED', '3');

// The following are copied from D8 LanguageConfiguration::getDefaultOptions().
define('LANGUAGEFIELD_SITE_DEFAULT', 'site_default');
define('LANGUAGEFIELD_CURRENT_INTERFACE', 'current_interface');
define('LANGUAGEFIELD_AUTHORS_DEFAULT', 'authors_default');

/**
 * Implements hook_field_info().
 */
function languagefield_field_info() {
  return array(
    'language_field' => array(
      'label' => t('Language'),
      'description' => t('This field stores a language reference in the database.'),
      'settings' => array(
        'enabled' => array(
          LANGUAGEFIELD_LANGUAGES_ALL => LANGUAGEFIELD_LANGUAGES_ALL,
        ),
        'format' => array(
          'name' => 'name',
        ),
        'languages' => array(),
        'excluded_languages' => array(),
        'groups' => '',
      ),
      //      'instance_settings' => array(
      //                               'format' => array('name' => 'name'),

      ////                             'text_processing' => 0,  // avoids error from text.module: function _text_sanitize()

      //                             ),
      'default_widget' => 'options_select',
      'default_formatter' => 'language_default',
      'property_type' => 'text',
    ),
  );
}

/**
 * Prevents a fatal error in the upgrade from 7.x-1.4 to 7.x-1.5.
 *
 * @see https://www.drupal.org/node/1312374
 * @see https://www.drupal.org/node/2821791
 */
function languagefield_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
  return array();
}

/**
 * Implements hook_field_settings_form().
 */
function languagefield_field_settings_form($field, $instance, $has_data) {
  $settings = $field['settings'];

  // oct 2014: choice of languages from radio to checkbox.
  if (!is_array($settings['enabled'])) {
    $settings['enabled'] = array(
      $settings['enabled'] => $settings['enabled'],
    );
  }
  $options_list = languagefield_options_list($field, NULL, NULL, NULL);
  $form['enabled'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Enabled Languages'),
    '#default_value' => $settings['enabled'],
    '#required' => TRUE,
    '#options' => array(
      LANGUAGEFIELD_LANGUAGES_ALL => t('All Predefined languages'),
      LANGUAGEFIELD_LANGUAGES_INSTALLED => t('All installed languages'),
      LANGUAGEFIELD_LANGUAGES_ENABLED => t('Enabled installed languages'),
      // The following are copied from LanguageConfiguration::getDefaultOptions()
      LANGUAGEFIELD_SITE_DEFAULT => t("Site's default language (!language)", array(
        '!language' => language_default()->name,
      )) . '*',
      LANGUAGEFIELD_CURRENT_INTERFACE => t('Current interface language') . '*',
      LANGUAGEFIELD_AUTHORS_DEFAULT => t("Author's preferred language") . '*',
      LANGUAGE_NONE => t("Language neutral ('und')"),
    ),
    '#description' => t("Installed languages can be maintained on the\n      <a href='@languages-page'>@languages</a>\n      page, when Locale module is enabled. Options marked with '*' are\n      typically used as default value in a hidden widget.", array(
      '@languages' => t('Languages'),
      '@languages-page' => url('admin/config/regional/language'),
    )),
  );

  // oct 2012: new options for display in widget.
  $form['format'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Display in widget'),
    '#description' => t('Select the elements you want to show. The elements will be concatenated when showing the field.'),
    '#default_value' => $settings['format'],
    '#options' => _languagefield_settings_options('widget'),
    '#required' => TRUE,
  );

  // oct 2012: old options for display in widget: move old data and hide old options (not removed, until proper upgrade path.).
  if (!empty($settings['include_native'])) {
    $form['format']['#default_value']['name_native'] = 'name_native';
    unset($settings['include_native']);
    $settings['include_native'] = '0';
    $form['include_native'] = array(
      '#type' => 'hidden',
      //      '#type' => 'checkbox',
      //      '#title' => t('Show native language name in widget (E.g., German vs. German (Deutsch))'),
      '#default_value' => !empty($settings['include_native']),
    );
  }
  $form['languages'] = array(
    '#type' => 'select',
    '#title' => t('Restrict by language'),
    '#default_value' => $settings['languages'],
    '#options' => array(
      '' => t('--'),
    ) + $options_list,
    '#description' => t('If no languages are selected, this filter will not be used.'),
    '#multiple' => TRUE,
    '#size' => 10,
  );
  $form['excluded_languages'] = array(
    '#type' => 'select',
    '#title' => t('Excluded languages'),
    '#default_value' => $settings['excluded_languages'],
    '#options' => array(
      '' => t('--'),
    ) + $options_list,
    '#description' => t('This removes individual languages from the list.'),
    '#multiple' => TRUE,
    '#size' => 10,
  );
  $form['groups'] = array(
    '#type' => 'textarea',
    '#title' => t('Language groups'),
    '#default_value' => $settings['groups'],
    '#description' => t("Provides a simple way to group common languages. If no groups are provided, no groupings will be used. Enter in the following format:<br/><code>cn,en,ep,ru<br/>African languages|bs,br<br/>Asian languages|cn,km,fil,ja</code>"),
    '#multiple' => TRUE,
    '#size' => 10,
  );
  $form['groups_help'] = array(
    '#type' => 'fieldset',
    '#title' => t('Group help'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $items = array();
  foreach (_languagefield_options() as $language) {
    $items[] = t('<strong>!key</strong>: %title', array(
      '!key' => $language['langcode'],
      '%title' => $language['name'],
    ));
  }
  $form['groups_help']['keys'] = array(
    '#type' => 'item',
    '#title' => t('Full language / key listing'),
    '#markup' => theme('item_list', array(
      'items' => $items,
    )),
  );
  $form['groups_help']['all'] = array(
    '#type' => 'item',
    '#title' => t('Available keys'),
    '#markup' => '<p>' . implode(',', array_keys(_languagefield_options())) . '</p>',
  );
  return $form;
}

/**
 * Implements hook_field_prepare_view().
 */
function languagefield_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {

  // Get all possible languages, even if not settable.
  $languages = _languagefield_options(NULL, $langcode);
  foreach ($entities as $id => $object) {
    foreach ($items[$id] as $delta => $item) {

      // Check if item value is set, otherwise continue to next item.
      if (!empty($item['value'])) {
        $items[$id][$delta] = $languages[$item['value']];
      }
    }
  }
}

/**
 * Implements hook_field_is_empty().
 */
function languagefield_field_is_empty($item, $field) {
  return empty($item['value']);
}

/**
 * Implements hook_field_formatter_info().
 *
 * @TODO: add suffix settings for icon formatter,
 *        see hook_field_formatter_settings_form() in countries.field.inc.
 */
function languagefield_field_formatter_info() {
  $formatters = array(
    'language_default' => array(
      'label' => t('Language field'),
      'field types' => array(
        'language_field',
      ),
      'settings' => array(
        'format' => array(
          'name' => 'name',
        ),
      ),
    ),
  );
  return $formatters;
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function languagefield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $element['format'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Display'),
    '#description' => t('Select the elements you want to show. The elements will be concatenated when showing the field.'),
    '#default_value' => $settings['format'],
    '#options' => _languagefield_settings_options(),
  );
  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function languagefield_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings']['format'];
  $options = _languagefield_settings_options();
  $summary = array();
  if (empty($settings)) {
    $summary[] = t('** Not set **');
  }
  else {
    foreach ($settings as $value) {
      switch ($value) {
        case '0':

          // Option is not selected.
          break;
        default:
          $summary[] = isset($options[$value]) ? $options[$value] : '...';
          break;
      }
    }
  }
  return implode(' + ', $summary);
}

/**
 * Implements hook_field_formatter_view().
 * @param $items  is set in hook_field_prepare_view()
 */
function languagefield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $settings = $display['settings'];
  $element = array();
  switch ($display['type']) {
    case 'language_default':

      //      $settings['format'] = isset($settings['format']) ? $settings['format'] : array('name' =>  'name');
      break;
  }
  foreach ($items as $delta => $item) {
    $markup = _languagefield_theme_option($item, $settings);
    $element[$delta]['#markup'] = $markup;
  }
  return $element;
}

/**
 * Implements hook_views_api().
 */
function languagefield_views_api() {
  return array(
    'api' => '3',
    'path' => drupal_get_path('module', 'languagefield'),
  );
}

/**
 * Helper function to theme an option in widget and formatter.
 * @TODO: turn into a real theme-function. Elements are now shown in fixed order.
 * @param $settings: taken from field-, widget- or formatter-settings.
 */
function _languagefield_theme_option($item, &$settings) {
  $markup = array();

  // Add a language icon. We might better use languageicons_link_add().
  // @TODO: doesn't work for the Widget, even though hook_options_list says the <img>-tab is allowed.
  if (!empty($settings['format']['icon']) && module_exists('languageicons')) {
    $language['language'] = $item['value'];
    $variables = array(
      'language' => (object) $language,
      // TODO: what happens when no icon for this language code
      'title' => !empty($item['name']) ? $item['name'] : '',
    );
    $markup[] = theme('languageicons_icon', $variables);
  }
  if (!empty($settings['format']['iso']) && !empty($item['value'])) {
    $markup[] = $item['value'];
  }
  if (!empty($settings['format']['name']) && !empty($item['name'])) {
    $markup[] = $item['name'];
  }
  if (!empty($settings['format']['name_native']) && !empty($item['name_native'])) {
    $markup[] = $item['name_native'];
  }
  $markup = empty($markup) && isset($item['name']) ? $item['name'] : implode(' ', $markup);
  return $markup;
}

/**
 * Implements hook_field_widget_info_alter().
 */
function languagefield_field_widget_info_alter(&$info) {
  if (isset($info['options_buttons'])) {
    $info['options_buttons']['field types'][] = 'language_field';

    //    $info['options_buttons']['#value_callback'][] = '_languagefield_widget_value';
  }
  if (isset($info['options_select'])) {
    $info['options_select']['field types'][] = 'language_field';

    //    $info['options_select']['#value_callback'][] = '_languagefield_widget_value';
  }
  if (isset($info['multiselect'])) {
    $info['multiselect']['field types'][] = 'language_field';

    //    $info['multiselect']['#value_callback'][] = '_languagefield_widget_value';
  }
}

/**
 * Adds a callback function to widget.
 *
 * @param $element
 * @param $form_state
 * @param $context
 *
 * Only for language_field widgets, but not on the field settings form.
 */
function languagefield_field_widget_form_alter(&$element, &$form_state, $context) {
  if ($context['field']['type'] == 'language_field' && $form_state['build_info']['form_id'] != 'field_ui_field_edit_form') {
    $element['#value_callback'] = '_languagefield_widget_value';
  }
}

/**
 * Callback function for widget.
 */
function _languagefield_widget_value($element, $input = FALSE, $form_state) {
  if (!$input) {

    // First call the default value_callback function.
    $value_callback = 'form_type_' . $element['#type'] . '_value';
    if (function_exists($value_callback)) {
      $input = $value_callback($element, $input, $form_state);
    }
  }
  else {

    // We do the following, to ensure that the LANGUAGEFIELD_* default values
    // are converted before showing the widget.
    // (Widgets are not always processed when they are hidden.)
    $return = array();

    // The form is submitted.
    if (!is_array($input)) {
      $return = _languagefield_getLanguageConfigurationValues($input);
    }
    else {

      // Convert the values to real languagecodes,
      // but ONLY on Entity form, NOT in the 'field settings - default value'.
      // That is done via a filter in languagefield_field_widget_form_alter().
      foreach ($input as &$value) {
        $value = _languagefield_getLanguageConfigurationValues($value);
        $return[$value] = $value;
      }
    }
    return $return;
  }
  return $input;
}

/**
 * Implements hook_options_list().
 * Returns 'all' or 'enabled' languages, according to field settings.
 */
function languagefield_options_list($field, $instance, $entity_type, $entity) {
  $settings =& $field['settings'];

  // oct 2012: old options for display in widget: move old data and hide old options, to be used in languagefield_theme_option().
  if (!empty($settings['include_native'])) {
    $settings['format']['name_native'] = 'name_native';
  }
  $all_languages = _languagefield_options($settings['enabled']);
  $languages = array_filter($settings['languages']);
  if (!empty($languages)) {
    $all_languages = array_intersect_key($all_languages, $languages);
  }
  if (!empty($settings['excluded_languages'])) {
    $all_languages = array_diff_key($all_languages, $settings['excluded_languages']);
  }
  foreach ($all_languages as $delta => $item) {
    $markup = _languagefield_theme_option($item, $settings);
    $all_languages[$delta] = $markup;
  }
  asort($all_languages);
  if (!empty($settings['groups'])) {
    $grouped_languages = array();
    $found_languages = array();
    $all_languages += array(
      'other' => t('Other languages'),
    );
    foreach (explode("\n", $settings['groups']) as $line) {
      if (strpos($line, '|') !== FALSE) {
        list($group, $langs) = explode('|', $line, 2);
        $langs = array_filter(array_map('trim', explode(',', $langs)));
        $langs = array_intersect_key($all_languages, array_combine($langs, $langs));
        $found_languages += $langs;
        $grouped_languages[$group] = $langs;
      }
      else {
        $langs = array_filter(array_map('trim', explode(',', $line)));
        if (!empty($langs)) {
          $langs = array_intersect_key($all_languages, array_combine($langs, $langs));
          $found_languages += (array) $langs;
          $grouped_languages += (array) $langs;
        }
      }
    }
    $missing_languages = array_diff_key($all_languages, $found_languages);
    foreach ($grouped_languages as $index => $options) {
      if (is_array($options)) {
        if (isset($options['other'])) {
          unset($options['other']);
          if ($missing_languages) {
            $grouped_languages[$index] = array_merge($grouped_languages[$index], $missing_languages);
            $missing_languages = FALSE;
          }
        }
      }
    }
    if (isset($grouped_languages['other'])) {
      unset($grouped_languages['other']);
      if ($missing_languages) {
        $grouped_languages = array_merge($grouped_languages, $missing_languages);
      }
    }
    return $grouped_languages;
  }
  else {
    return $all_languages;
  }
}

/**
 * Implements hook_migrate_api().
 */
function languagefield_migrate_api() {
  return array(
    'api' => 2,
    'field handlers' => array(
      'MigrateLanguagefieldFieldHandler',
    ),
  );
}

/**
 * Helper function to generate the language options list.
 *
 * static $predefined: array of language subsets.
 */
function _languagefield_options($subsets = NULL, $langcode = LANGUAGE_NONE) {
  static $predefined = NULL;

  // @see _locale_prepare_predefined_list()
  require_once DRUPAL_ROOT . '/includes/iso.inc';
  $allsets = array(
    LANGUAGE_NONE => TRUE,
    LANGUAGEFIELD_LANGUAGES_ALL => TRUE,
    LANGUAGEFIELD_LANGUAGES_ENABLED => TRUE,
    LANGUAGEFIELD_LANGUAGES_INSTALLED => TRUE,
    // The following values are copied from D8 LanguageConfiguration::getDefaultOptions().
    LANGUAGEFIELD_SITE_DEFAULT => TRUE,
    LANGUAGEFIELD_CURRENT_INTERFACE => TRUE,
    LANGUAGEFIELD_AUTHORS_DEFAULT => TRUE,
  );

  // oct 2014: choice of languages from radio to checkbox.
  if (!$subsets) {
    $subsets = $allsets;
  }
  elseif (!is_array($subsets)) {
    $subsets = array(
      $subsets => $subsets,
    );
  }

  // Use a cache for each user-language.
  if (!isset($predefined[$langcode])) {
    foreach ($allsets as $subset => $active) {

      // Get the untranslated language list, using the (array-format) of _locale_get_predefined_list().
      // @TODO: One day we should use the (objects-)format of language_list().
      switch ($subset) {
        case LANGUAGE_NONE:
          $predefined[$langcode][$subset][LANGUAGE_NONE]['value'] = LANGUAGE_NONE;
          $predefined[$langcode][$subset][LANGUAGE_NONE]['langcode'] = LANGUAGE_NONE;
          $predefined[$langcode][$subset][LANGUAGE_NONE]['name'] = t('Language neutral');
          $predefined[$langcode][$subset][LANGUAGE_NONE]['native'] = '';
          break;
        case LANGUAGEFIELD_SITE_DEFAULT:
        case LANGUAGEFIELD_CURRENT_INTERFACE:
        case LANGUAGEFIELD_AUTHORS_DEFAULT:
          $language = _languagefield_getLanguageConfigurationOptions($subset);
          $predefined[$langcode][$subset][$subset]['value'] = $subset;
          $predefined[$langcode][$subset][$subset]['langcode'] = $subset;
          $predefined[$langcode][$subset][$subset]['name'] = t($language[$subset]);
          $predefined[$langcode][$subset][$subset]['native'] = '';
          break;

        // The following are from Languagefield module.
        case LANGUAGEFIELD_LANGUAGES_INSTALLED:
        case LANGUAGEFIELD_LANGUAGES_ENABLED:
          $predefined[$langcode][$subset] = array();

          // Get all possible languages with English and native name.
          $languages = language_list();
          foreach ($languages as $language) {
            if ($language->enabled || $subset == LANGUAGEFIELD_LANGUAGES_INSTALLED) {

              // Prepare for D8. The next line can be removed in D8. See http://drupal.org/node/1399806
              $language->langcode = isset($language->langcode) ? $language->langcode : $language->language;
              $predefined[$langcode][$subset][$language->langcode]['value'] = $language->langcode;
              $predefined[$langcode][$subset][$language->langcode]['langcode'] = $language->langcode;
              $predefined[$langcode][$subset][$language->langcode]['name'] = t($language->name);
              $predefined[$langcode][$subset][$language->langcode]['native'] = isset($language->native) ? $language->native : '';
            }
          }
          break;
        case LANGUAGEFIELD_LANGUAGES_ALL:
        default:
          $subset = LANGUAGEFIELD_LANGUAGES_ALL;

          // @see _locale_prepare_predefined_list()
          $languages = _locale_get_predefined_list();
          foreach ($languages as $id => $language) {
            $predefined[$langcode][$subset][$id]['value'] = $id;
            $predefined[$langcode][$subset][$id]['langcode'] = $id;
            $predefined[$langcode][$subset][$id]['name'] = t($language[0]);
            $predefined[$langcode][$subset][$id]['native'] = isset($language[1]) ? $language[1] : $language[0];
          }
          break;
      }

      // Translate the language list.
      $t_options = $langcode != LANGUAGE_NONE ? array(
        'langcode' => $langcode,
      ) : array();
      foreach ($predefined[$langcode][$subset] as $key => $value) {

        // Translate the language name, if possible.
        $tname = t($value['name'], array(), $t_options);

        // Append native name in output, if possible.
        if (!empty($value['native']) && $tname != $value['native']) {
          $predefined[$langcode][$subset][$key]['name_native'] = t('!language (!native)', array(
            '!language' => $tname,
            '!native' => $value['native'],
          ));
        }
        else {
          $predefined[$langcode][$subset][$key]['name_native'] = $tname;
        }
      }
    }
  }

  // Compile the list of languages.
  $languages = array();
  foreach ($subsets as $subset => $active) {
    if ($active) {
      $languages += $predefined[$langcode][$subset];
    }
  }
  asort($languages);
  return $languages;
}

/**
 * Helper function for the formatter and widget options.
 * @param: $usage: languageicons do not work in widget.
 *                 The icon-option will be omitted for Widgets, not for Formatters
 */
function _languagefield_settings_options($usage = 'formatter') {
  $options = array();
  if ($usage != 'widget' && module_exists('languageicons')) {
    $options += array(
      'icon' => t('Language icon'),
    );
  }
  $options += array(
    'iso' => t('ISO 639-code'),
    'name' => t('Name'),
    'name_native' => t('Name and native'),
  );
  return $options;
}
function _languagefield_getLanguageConfigurationOptions($code) {
  switch ($code) {
    case LANGUAGEFIELD_SITE_DEFAULT:
      $values = array(
        LANGUAGEFIELD_SITE_DEFAULT => t("Site's default language (!language)", array(
          '!language' => language_default()->name,
        )),
      );
      break;
    case LANGUAGEFIELD_CURRENT_INTERFACE:
      $values = array(
        LANGUAGEFIELD_CURRENT_INTERFACE => t('Current interface language'),
      );
      break;
    case LANGUAGEFIELD_AUTHORS_DEFAULT:
      $values = array(
        LANGUAGEFIELD_AUTHORS_DEFAULT => t("Author's preferred language"),
      );
      break;
  }
  return $values;
}

/**
 * @param $code
 * @return string
 */
function _languagefield_getLanguageConfigurationValues($code) {
  $value = LANGUAGE_NONE;
  global $user;
  global $language;
  switch ($code) {
    case LANGUAGEFIELD_SITE_DEFAULT:
      $default_language = language_default();
      $value = $default_language->language;
      break;
    case LANGUAGEFIELD_CURRENT_INTERFACE:
      $value = $language->language;
      break;
    case LANGUAGEFIELD_AUTHORS_DEFAULT:

      // Language is not set for Anonymous user or if locale is not enabled.
      $value = !empty($user->language) ? $user->language : $language->language;
      break;
    default:
      $value = $code;
  }
  return $value;
}

Functions

Namesort descending Description
languagefield_field_formatter_info Implements hook_field_formatter_info().
languagefield_field_formatter_settings_form Implements hook_field_formatter_settings_form().
languagefield_field_formatter_settings_summary Implements hook_field_formatter_settings_summary().
languagefield_field_formatter_view Implements hook_field_formatter_view().
languagefield_field_info Implements hook_field_info().
languagefield_field_is_empty Implements hook_field_is_empty().
languagefield_field_prepare_view Implements hook_field_prepare_view().
languagefield_field_settings_form Implements hook_field_settings_form().
languagefield_field_widget_form_alter Adds a callback function to widget.
languagefield_field_widget_info_alter Implements hook_field_widget_info_alter().
languagefield_migrate_api Implements hook_migrate_api().
languagefield_options_list Implements hook_options_list(). Returns 'all' or 'enabled' languages, according to field settings.
languagefield_property_info_callback Prevents a fatal error in the upgrade from 7.x-1.4 to 7.x-1.5.
languagefield_views_api Implements hook_views_api().
_languagefield_getLanguageConfigurationOptions
_languagefield_getLanguageConfigurationValues
_languagefield_options Helper function to generate the language options list.
_languagefield_settings_options Helper function for the formatter and widget options. @param: $usage: languageicons do not work in widget. The icon-option will be omitted for Widgets, not for Formatters
_languagefield_theme_option Helper function to theme an option in widget and formatter. @TODO: turn into a real theme-function. Elements are now shown in fixed order.
_languagefield_widget_value Callback function for widget.

Constants