thierry-b
2/25/2015 - 11:04 AM

Teaser met html support > nog een Regex update (met dank aan Kurt)

Teaser met html support > nog een Regex update (met dank aan Kurt)

<?php
/**
 * @file
 *
 * Main module file for The AIM User Interface
 */

/**
 * Implements hook_menu()
 */
function the_aim_ui_menu() {
  $items = array();

  $items['admin/config/the-aim'] = array(
    'title' => 'The AIM',
    'description' => 'Configure The AIM modules',
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array('access administration pages'),
    'type' => MENU_NORMAL_ITEM,
    'weight' => 50,
    'file path' => drupal_get_path('module', 'system'),
    'file' => 'system.admin.inc',
  );

  return $items;
}

/**
 * Implements hook_field_formatter_info()
 */
function the_aim_ui_field_formatter_info() {
  return array(
    'trim_to_char' => array(
      'label' => t('Trim to # chars'),
      'field types' => array('text_long', 'text_with_summary'),
      'description' => t('Trim the output of a field to a specific number of characters.'),
      'settings' => array(
        'number_of_chars' => 300,
        'wordsafe' => (int) FALSE,
        'min_wordsafe_length' => 4,
        'add_ellipsis' => (int) TRUE,
        'ellipsis' => '',
        'clean_cbox' => (int) FALSE,
        'clean_tables' => (int) FALSE,
        'allowed_tags' => '',
        'break_tags' => '',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function the_aim_ui_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  switch ($display['type']) {
    case 'trim_to_char':
      $max_length = max((int) $display['settings']['number_of_chars'], 0);
      $wordsafe = (!empty($display['settings']['wordsafe'])) ? (bool) $display['settings']['wordsafe'] : FALSE ;
      $min_wordsafe_length = (!empty($display['settings']['min_wordsafe_length'])) ? (int) $display['settings']['min_wordsafe_length'] : 1 ;
      $add_ellipsis = (!empty($display['settings']['add_ellipsis'])) ? (bool) $display['settings']['add_ellipsis'] : FALSE ;

      $options = array();
      if (!empty($display['settings']['ellipsis'])) $options['ellipsis'] = $display['settings']['ellipsis'];
      if (!empty($display['settings']['allowed_tags'])) $options['allowed_tags'] = $display['settings']['allowed_tags'];
      if (!empty($display['settings']['break_tags'])) $options['break_tags'] = $display['settings']['break_tags'];
      if (!empty($display['settings']['clean_cbox'])) $options['clean_cbox'] = $display['settings']['clean_cbox'];
      if (!empty($display['settings']['clean_tables'])) $options['clean_tables'] = $display['settings']['clean_tables'];

      $element = array();
      foreach ($items as $delta => $item) {
        $string = (!empty($item['summary'])) ? $item['summary'] : $item['value'];
        $string = _truncate_utf8_xml($string, $max_length, $wordsafe, $add_ellipsis, $min_wordsafe_length, $options);

        $element[] = array(
          '#type' => 'markup',
          '#markup' => $string,
        );
      }
      break;
  }
  return $element;
}

/**
 * Truncates a UTF-8-encoded XML string
 *
 * @param  string $string
 * @param  int    $max_length
 * @param  bool   $wordsafe
 * @param  bool   $add_ellipsis
 * @param  int    $min_wordsafe_length
 * @param  array  $options
 *
 * @return string Returns a valid truncated XML string.
 */
function _truncate_utf8_xml($string, $max_length, $wordsafe = FALSE, $add_ellipsis = FALSE, $min_wordsafe_length = 1, $options = array()) {
  // Define defaults
  $ellipsis = '&hellip;';
  $allowed_tags = '';
  $break_tags = '';
  $clean_cbox = FALSE;
  $clean_tables = FALSE;

  // Set the defined field formatter options
  extract($options);

  // Get the ellipsis length
  if ($add_ellipsis) {
    // Truncate ellipsis in case $max_length is small.
    $ellipsis = drupal_substr(html_entity_decode($ellipsis), 0, $max_length);
    $max_length -= drupal_strlen($ellipsis);
    $max_length = max($max_length, 0);
  }

  if ($max_length <= $min_wordsafe_length) {
    // Do not attempt word-safe if lengths are bad.
    $wordsafe = FALSE;
  }

  // Add a break after some tags
  if (!empty($break_tags)) {
    preg_match_all('/[a-z]+/i', $break_tags, $break_tags);
    $break_tags = reset($break_tags);
    foreach ($break_tags as $tag) {
      $string = preg_replace('/<\/' . $tag . '>/im', '<br>$0', $string);
    }
  }

  // Strip colorbox links
  if ((bool)$clean_cbox) {
    $string = preg_replace('/(<a class="cboxElement".*?>)(.+)(<\/a>)/ims', '$2', $string);
  }

  // Strip any (nested) tables
  if ((bool)$clean_tables) {
    $string = preg_replace('%<table\b[^>]*+>(?:(?R)|[^<]*+(?:(?!</?table\b)<[^<]*+)*+)*+</table>%im', '', $string);
  }

  // Strip the string from tags, keeping the allowed tags
  $string = trim(strip_tags($string, $allowed_tags));

  // Strip empty links
  $string = preg_replace('/<a[^>]*>(\s|<\/?\s*br\s*\/?>)*<\/a>/im', '', $string);

  // Strip empty paragraph
  $string = preg_replace('/<p[^>]*>(\s|<\/?\s*br\s*\/?>)*<\/p>/im', '', $string);

  // Replace tabs, newlines and carriage returns with a regular space.
  $string = preg_replace('~[[:cntrl:]]~', ' ', $string);

  // Replace multiple spaces with a single one
  $string = preg_replace('!\s+!', ' ', $string);

  // If the length without tags is less than our target we are done
  if (strlen(strip_tags($string)) < $max_length)
    return $string;

  // Get the validated xml string
  $string = _valid_xml($string);

  // Get each single word / tag
  preg_match_all('/<[^>]++>|[^<>\s]++/', $string, $tokens);

  // Loop over the words / tags
  $counter = 0;
  $newtext = array();
  foreach ($tokens[0] as $i => $token) {
    // Ignore tags in the string length
    if (mb_substr($token, 0, 1, 'utf-8') === '<') {
      $newtext[] = $token;
      continue;
    }

    // Get the token length
    $token_length = mb_strlen(html_entity_decode($token));

    // Set the string length till this word (count in the leading space)
    $counter += $token_length + 1;

    // Adding the word would exceed the allowed string length
    if ($counter > $max_length) {
      // Add a word fragment if allowed
      if (!$wordsafe) {
        $delta = $counter - $max_length;
        $fragment = mb_substr($token, 0, $delta);
        $newtext[] = $fragment;
      }
      else {
        // Check if the last word respects the minimum wordsafe length
        while (mb_strlen(end($newtext)) < $min_wordsafe_length) {
          array_pop($newtext);
        }
      }

      // Prevent to add the word
      break;
    }

    $newtext[] = $token;
  }

  // Build the new string
  $string = implode(' ', $newtext);

  // Get the validated xml string
  $string = _valid_xml($string);

  // Add the ellipsis at the end
  if ($add_ellipsis)
    $string .= $ellipsis;

  // Return the processed string
  return $string;
}

/**
 * Generate a valid XML string
 *
 * @param  string $text
 *
 * @return string Return a valid XML string.
 */
function _valid_xml($text) {
  // Create a new xml document object
  $document = new DOMDocument();
  // Disable libxml errors
  libxml_use_internal_errors(true);
  // Load HTML from a string
  $document->loadHTML('<?xml version="1.0" encoding="UTF-8"?><body>' . $text . '</body>');
  // Clear libxml error buffer
  libxml_clear_errors();
  // Get the body from the xml document
  $text = substr($document->saveXML($document->getElementsByTagName('body')->item(0)), strlen('<body>'), -strlen('</body>'));
  // Return the text
  return $text;
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function the_aim_ui_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  $element = array();
  $element['number_of_chars'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of characters'),
    // '#description' => t('The maximum number of characters'),
    '#default_value' => $settings['number_of_chars'],
  );

  $element['wordsafe'] = array(
    '#type' => 'checkbox',
    '#title' => t('Wordsafe'),
    '#description' => t('Don\'t cut words while shortening text'),
    '#default_value' => $settings['wordsafe'],
  );

  $element['min_wordsafe_length'] = array(
    '#type' => 'textfield',
    '#title' => t('Wordsafe length'),
    '#description' => t('Make sure the latest word, from a shortened text, has a certain length'),
    '#default_value' => $settings['min_wordsafe_length'],
  );

  $element['add_ellipsis'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add ellipsis'),
    // '#description' => t('Add an ellipsis to the end of a shortened text'),
    '#default_value' => $settings['add_ellipsis'],
  );

  $element['ellipsis'] = array(
    '#type' => 'textfield',
    '#title' => t('Ellipsis substitution'),
    // '#description' => t('Sign or signs to indicate the text was shortened'),
    '#default_value' => $settings['ellipsis'],
  );

  $element['clean_cbox'] = array(
    '#type' => 'checkbox',
    '#title' => t('Remove colorboxes'),
    '#description' => t('Remove colorbox links even though links are allowed'),
    '#default_value' => $settings['clean_cbox'],
  );

  $element['clean_tables'] = array(
    '#type' => 'checkbox',
    '#title' => t('Remove tables'),
    // '#description' => t('Remove tables from the output'),
    '#default_value' => $settings['clean_tables'],
  );

  $element['allowed_tags'] = array(
    '#type' => 'textfield',
    '#title' => t('Allowed tags'),
    '#description' => t('Don\'t strip the above tags. For example: %example', array('%example' => '<a><b><i><strong><em><br><br/>')),
    '#default_value' => $settings['allowed_tags'],
  );

  $element['break_tags'] = array(
    '#type' => 'textfield',
    '#title' => t('Break tags'),
    '#description' => t('Add a break tag at the end of the above tags. For example: %example', array('%example' => '</p></li>')),
    '#default_value' => $settings['break_tags'],
  );

  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function the_aim_ui_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $summary = t('Maximum number of chars: @number_of_chars', array(
    '@number_of_chars' => $settings['number_of_chars'],
  ));
  return $summary;
}