You are here

language_sections.module in Language Sections 7.2

File

language_sections.module
View source
<?php

/*
 * @file
 * Language Sections 2.x module for Drupal 7
 * Allows text for several languages to be stored in a single text area.
 *
 * Project page: http://drupal.org/project/language_sections
 * Contact for commercial support and custom development: http://www.netgenius.co.uk/contact
 */
function language_sections_menu() {

  // Get $mod_name, and $mod_id;
  extract(_language_sections_get_ids());
  $items[$mod_config_path] = array(
    'title' => $mod_name,
    'description' => 'Configure the %name module.',
    'title arguments' => array(
      '%name' => $mod_name,
    ),
    'page callback' => 'language_sections_admin',
    'access arguments' => array(
      'administer filters',
    ),
  );
  return $items;
}

// Return the config form - just a wrapper for drupal_get_form
function language_sections_admin() {
  return drupal_get_form('_language_sections_settings');
}

/*
 * Implements hook_filter_info().
 */
function language_sections_filter_info() {
  $filters['language_sections'] = array(
    'title' => t('Language Sections'),
    'description' => t('Allows you to define content for several languages in a single text area.'),
    'process callback' => '_filter_language_sections',
    'settings callback' => '_filter_language_sections_settings',
    'tips callback' => '_filter_language_sections_tips',
    // We must disallow caching. Drupal caches by $node->language, not current selected language.
    // @todo We had a patch for the D6 version to allow caching - do the same for D7.
    'cache' => FALSE,
  );
  return $filters;
}

/*
 * Implements hook_filter_tips().
 */
function _filter_language_sections_tips($filter, $format, $long) {
  $tvars = array(
    '%all' => t('all'),
    '%other' => t('other'),
  );
  $short_help = t(_language_sections_setting('short_help'), $tvars);
  $long_help = $short_help;
  return t($long ? $long_help : $short_help, $tvars);
}

// Get mod_name, mod_id, mod_prefix
function _language_sections_get_ids() {
  $mod_id = 'language_sections';
  $ids = array(
    'mod_name' => 'Language Sections',
    'mod_id' => $mod_id,
    // In D6, $mod_prefix provided for different config per filter-format. Now we always use "shared".
    'mod_prefix' => $mod_id . '_shared_',
    'mod_config_path' => sprintf('admin/config/regional/%s', $mod_id),
  );
  return $ids;
}
function _filter_language_sections($text, $filter, $format, $langcode, $cache, $cache_id) {

  // Quick-exit if nothing to process, or disabled (set by LS Search from ls_extras module.)
  if (!$text || isset($GLOBALS['language_sections_disable'])) {
    return $text;
  }

  // Note: $langcode parameter passed by caller comes from $node->language - not much use!
  global $language;

  //drupal_set_message('language: ' . $language->language . ' langcode: ' . $langcode);

  // Get $pattern and $triggers.
  $pattern = _language_sections_setting('pattern');
  $triggers = _language_sections_get_triggers();

  // Get values for $current_language, $all_languages, $other_languages
  extract(_language_sections_context('match_types'));
  $n1 = $n2 = 2;
  $n3 = 4;

  // indexes to use with array from preg_split().
  $matches = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);

  //drupal_set_message(print_r($matches, 1), 'warning');

  // Build the output string, keeping only the parts we want...
  $out = $matches[0];
  $show_default = TRUE;
  for ($i = $n1; $i < count($matches); $i += $n3) {

    // Convert to lower case.
    $trigger = drupal_strtolower($matches[$i]);

    // No matching language trigger so continue with next section.
    if (!isset($triggers[$trigger])) {
      continue;
    }
    switch ($triggers[$trigger]) {

      // case: a section for the current language, use it and clear "show default" flag.
      case $current_language:
        $out .= $matches[$i + $n2];
        $show_default = FALSE;
        break;

      // case: a section for "all languages", use it.
      case $all_languages:
        $out .= $matches[$i + $n2];
        break;

      // case: a "default" section, use it if we haven't already used a language-specific section...
      case $other_languages:
        if ($show_default) {
          $out .= $matches[$i + $n2];
        }
        else {
          $show_default = TRUE;
        }
        break;
    }
  }

  // Extract title if present.
  if (function_exists('ls_titles_process')) {
    $out = ls_titles_process('set', $out, $format, $cache_id);
  }
  return $out;
}

// Get triggers array for specified language.
function _language_sections_get_triggers($language = FALSE, $rebuild = FALSE) {
  if (!$language) {
    global $language;
  }

  // Get saved $triggers.
  extract(_language_sections_get_ids());
  $varname = $mod_prefix . 'triggers_' . $language->language;
  $triggers = variable_get($varname, NULL);

  // Check if saved $triggers is valid - $language settings may have been edited.
  $check = crc32(serialize($language));
  if ($rebuild || $triggers['check'] !== $check) {

    //drupal_set_message(sprintf('rebuild %s: %s', $mod_prefix, $language->name));
    $triggers = array(
      'types' => _language_sections_build_triggers($mod_prefix, $language),
      'check' => $check,
    );
    variable_set($varname, $triggers);
  }

  //drupal_set_message(sprintf('triggers for %s: %s ', $lang, print_r($triggers, 1)), 'warning');
  return $triggers['types'];
}

// Build triggers arrays for specified language.
// The idea here is to do processing here rather than every time that the LS filter gets called,
// so as to maximise performance during node display etc.
// Results here are normally cached by _language_sections_get_triggers().
function _language_sections_build_triggers($mod_prefix, $language) {

  // Get $elements, $specials, $match_types.
  extract(_language_sections_context());

  // Build triggers using $elements.
  foreach ($elements as $element) {
    $key = 'trigger_' . $element;

    // If trigger is in use, add it.
    if (_language_sections_setting($key)) {
      $triggers[$language->{$element}] = $match_types['current_language'];
    }
  }

  // Append from $specials (currently $name values will be 'all' and 'other')
  foreach ($specials as $name => $desc) {
    $key = 'trigger_special_' . $name;
    $matches = explode('|', _language_sections_setting($key));
    foreach ($matches as $match) {

      //drupal_set_message(sprintf('name: %s, match: %s, type: %s', $name, $match, $match_types[$name.'_languages'], 'error'));
      $triggers[strtolower($match)] = $match_types[$name . '_languages'];
    }
  }
  return $triggers;
}

// Define default values for settings, get setting.
function _language_sections_setting($key, $delete = FALSE) {

  /*
  // Make sure settings have been updated to Version 2.x
  // @todo Remove this or put it into the .install file.
  if (!variable_get($mod_prefix.'version', FALSE)) {
    variable_set($mod_prefix.'version', '2.5');
    // This is needed when upgrading from 1.x.
    if (!variable_get($mod_prefix.'alt', FALSE)) {
      variable_del($mod_prefix.'pattern');
    }
  }
  */

  // These are here to help consider backward compatibility.
  // pattern  v1.5: '/(=+ *([a-z]{2}|[a-z]{2}-[a-z]{2}) *=+\s?)(.*?)/'
  // pattern  v1.7: '/(=+ *([a-z]{2}|[a-z]{2}-[a-z]{2,5}|all|other) *=+\s?)(.*?)/i'
  // original v2.x: '/(=+ *([a-z]{2}[a-z\-]*) *=+\s?)([.\s]*)/i'
  // pattern  v2.4: '/(=+ *([a-z]{2}[a-z\-]*) *=+\s?)(.*?)/i',
  // Get $mod_prefix (etc.)
  extract(_language_sections_get_ids());
  if ($delete) {
    variable_del($mod_prefix . $key);
    $setting = NULL;
  }
  else {
    $setting = variable_get($mod_prefix . $key);
  }
  if ($setting === NULL) {
    $defaults = array(
      'pattern' => '/(=+ *([a-z]{2}[a-z\\-]*) *=+\\s?)(.*?)/i',
      'trigger_language' => 1,
      'trigger_special_all' => 'all',
      'trigger_special_other' => 'other',
      'short_help' => 'Mark language-dependent sections with <strong>== marker ==</strong> ' . 'where <em>marker</em> is either a valid language code or a special code such as %all or %other.',
    );
    $setting = isset($defaults[$key]) ? $defaults[$key] : FALSE;
  }
  return $setting;
}

// Define some values needed in various places.
function _language_sections_context($topic = FALSE) {
  $context = array(
    // Elements of $language that we use as possible triggers.
    'elements' => array(
      'language',
      'name',
      'prefix',
    ),
    // "Special" triggers and descriptions.
    'specials' => array(
      'all' => t('all languages'),
      'other' => t('other languages'),
    ),
    'match_types' => array(
      'current_language' => 1,
      'all_languages' => 2,
      'other_languages' => 3,
    ),
  );
  return $topic ? $context[$topic] : $context;
}

/*
 * Build and return the settings form (can define format-specific settings - currently there aren't any.)
 * hook_filter_FILTER_settings($form, &$form_state, $filter, $format, $defaults, $filters)
 * http://api.drupal.org/api/drupal/modules--filter--filter.api.php/function/hook_filter_FILTER_settings/7
 */
function _filter_language_sections_settings($form, &$form_state, $filter, $format, $defaults, $filters) {
  extract(_language_sections_get_ids());
  $elements['info'] = array(
    '#markup' => l('Configuration', $mod_config_path, array(
      'query' => drupal_get_destination(),
    )),
  );
  return $elements;
}

/*
 * Build and return the main configuration form.
 */
function _language_sections_settings($form, &$form_state) {
  global $language;
  $textsize = 40;

  // Get $mod_name, $mod_id and $mod_prefix;
  extract(_language_sections_get_ids());

  // Get $elements and $specials.
  extract(_language_sections_context());
  $title = sprintf('%s (%s)', t($mod_name), t('global configuration'));
  drupal_set_title($title);

  // Use the whole form (we used to have a fieldset in D6).
  $section =& $form;
  $fieldset =& $section['triggers'];
  $fieldset = array(
    '#type' => 'fieldset',
    '#title' => t('Current language triggers'),
    '#description' => t('Which elements from current language can be used in language-section markers?'),
  );

  // Create a checkbox for each $element.
  $title = '%element';
  $desc = 'Language:%element is currently %value.';
  foreach ($elements as $element) {
    $key = 'trigger_' . $element;
    $tvars = array(
      '%element' => $element,
      '%value' => $language->{$element} ? $language->{$element} : t('<blank>'),
    );
    $fieldset[$mod_prefix . $key] = array(
      '#type' => 'checkbox',
      '#title' => t($title, $tvars),
      '#default_value' => _language_sections_setting($key),
      '#description' => t($desc, $tvars),
    );
  }

  // Create an entry field for special codes ("all" and "other").
  $examples = array(
    'all' => 'all|todos|toutes',
    'other' => 'english|other|otro|autre',
  );
  foreach ($specials as $type => $desc) {
    $key = 'trigger_special_' . $type;
    $tvars = array(
      '%desc' => $desc,
      '%ex' => t($examples[$type]),
    );
    $section[$mod_prefix . $key] = array(
      '#type' => 'textfield',
      '#title' => t('Triggers for %desc sections', $tvars),
      '#size' => $textsize,
      '#default_value' => _language_sections_setting($key),
      '#description' => t('Text that may mark an %desc section. Leave blank to disable, multiple entries allowed, e.g: %ex', $tvars),
    );
  }
  $fieldset =& $section['advanced'];
  $fieldset = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $key = 'short_help';
  $fieldset[$mod_prefix . $key] = array(
    '#type' => 'textarea',
    '#title' => t('User help'),
    '#rows' => 2,
    '#default_value' => _language_sections_setting($key),
    '#description' => t('Filter-help shown to the user. This text is passed through t().'),
  );
  $key = 'pattern';
  $pattern = _language_sections_setting($key);

  // Use default pattern if pattern has been set blank here.
  if (!$pattern) {
    $pattern = _language_sections_setting($key, TRUE);
  }
  $fieldset[$mod_prefix . $key] = array(
    '#type' => 'textfield',
    '#title' => t('Alternative pattern'),
    '#size' => $textsize,
    '#default_value' => $pattern,
    '#description' => t('This pattern will be used for finding sections in the text.' . ' You should not change the number of parenthesised groups in the expression.' . ' Set blank to reset to default.'),
  );

  // If form is being posted, process new settings.
  if (!empty($_POST)) {
  }

  //return $form;
  $form = system_settings_form($form);
  $form['#submit'][] = 'language_sections_rebuild_triggers';
  return $form;
}

// Rebuild stored triggers.
function language_sections_rebuild_triggers() {
  foreach (language_list() as $lang) {
    _language_sections_get_triggers($lang, TRUE);
  }
}

// Return true if LS is active for the given filter $format.
function language_sections_format_check($format) {
  static $formats;
  if (!isset($formats[$format])) {
    $formats[$format] = FALSE;
    $filters = filter_list_format($format);
    foreach ($filters as $filter) {
      if ($filter->module == 'language_sections') {
        $formats[$format] = TRUE;
        break;
      }
    }
  }
  return $formats[$format];
}

// --- Drupal docs advise NOT closing PHP tags ---