Glossaire custom Drupal

Dans cet article, nous allons voir comment créer un outil de glossaire, cet-à-dire, parcourir les mots pré-remplis (à partir d'un type de contenu par exemple) pour créer des liens (avec un filtre) vers une page glossaire.

D'abord il faut créer un filtre depuis notre module custom.

/**
 * Implements hook_filter_info().
 */
function netapsys_glossaire_filter_info() {
  $filters['netapsys_glossaire_wordlink'] = array(
    'title' => t('Netapsys Glossaire - Word link'),
    'description' => t('Automatically converts words into links.'),
    'process callback' => '_netapsys_glossaire_wordlink_process', 
    'settings callback' => '_netapsys_glossaire_wordlink_settings', 
    'default settings' => array(
      'word_link_highlight' => FALSE,
      'word_link_wrap_tag' => NULL,
      'word_link_boundary' => FALSE,
      'word_link_tags_except' => '<h1> <h2> <h3> <h4> <h5> <h6> <code>',
      'word_link_content_types' => array(),
    ),
    'weight' => 15,
  );
  return $filters;
}
Création du filtre custom

 

Nous créons le Process callback:

/**
 * Helper function for filter process.
 */
function _netapsys_glossaire_wordlink_process($text, $filter) {
  $text = netapsys_glossaire_wordlink_convert_text($text, $filter->settings);
  return $text;
}
Process callback

 

Juste après, pour chaque filtre (full_html, filtered html, plain text), il faut l'activer et le placer (par exemple en dernière position)

 

Dans notre module il faut placer une fonction pour trouver et transformer les mots en liens


/**
 * Find and convert defined word to link.
 *
 * @param string $text
 *   Input text.
 * @param array $settings
 *   Array of filter settings.
 *
 * @return string
 *   String with converted words.
 */
function netapsys_glossaire_wordlink_convert_text($text, $settings) {

	global $base_url;

	//Avoid empty
	if ($text == '' || !$text) {   
		return $text;
	}

	//Avoid > URL-like to avoid + avoid homepage
	$posAdmin = strpos($_GET['q'], 'admin');
	// check strpos of glossaire / public pages 
	// check that page aint the view-admin one of the glossaire view ++ it contains glossaire in the path so we check /glossaire/* with it
	if ($posAdmin !== FALSE || drupal_is_front_page()) {   
		return $text;
	}

	// Get array of words and check whether there's a glossary page or not
	$words = null;
	$words = netapsys_glossaire_wordlink_load_all($termid);

	// Exit if there are no words to replace.
	if (empty($words)) {
		return $text;
	}

	// Default HTML tag used in theme.
	$tag = 'a';
	// Get disallowed html tags and convert it for Xpath.
	if (!empty($settings['word_link_tags_except'])) {
		$disallowed = &drupal_static('word_link_disallowed_tags');
		if (!isset($disallowed)) {
		  $disallowed_tags = preg_split('/\s+|<|>/', $settings['word_link_tags_except'], -1, PREG_SPLIT_NO_EMPTY);
		  $disallowed = array();
		  foreach ($disallowed_tags as $ancestor) {
		    $disallowed[] = 'and not(ancestor::' . $ancestor . ')';
		  }
		  $disallowed = implode(' ', $disallowed);
		}
	}
	else {
		$disallowed = '';
	}

	// Create pattern.
	$patterns = &drupal_static('word_link_patterns');
	if (!isset($patterns)) {

		$path = drupal_strtolower(drupal_get_path_alias());
		$pattern = array();
		foreach ($words as $word) {
		  $url = str_replace($base_url . '/', '', 'node/'. $word->nid);		 
		  $url = drupal_get_path_alias($url);
		  if ($url != $path) {
		    $title = preg_replace('/\s+/', ' ', trim($word->title));
		    $pattern[] = preg_replace('/ /', '\\s+', preg_quote($title, '/'));
		  }
		}

		// Chunk pattern string to avoid preg_match_all() limits.
		$patterns = array();
		foreach (array_chunk($pattern, 1000, TRUE) as $pattern_chunk) {
		  if ($settings['word_link_boundary']) {
		    $patterns[] = '/(?<=)(' . implode('|', $pattern_chunk) . ')/ui';
		  }
		  else {
		    $patterns[] = '/((\b)|(?<=))(' . implode('|', $pattern_chunk) . ')\b/ui';
		  }
		}
	}
	foreach ($patterns as $pattern) {
		netapsys_glossaire_wordlink_convert_text_recursively($text, $pattern, $words, $disallowed, $settings, $tag);
	}


  return $text;
}
Chercher et convertir les mots en liens

 

Ici nous montrons la fonction d'aide (helper function) pour charger les mots pré-remplis depuis un type de contenu avec le machine_name "contenu_glossaire"

/**
 * Loads words from the database. Actually all node titles of "content_glossaire" content type
 *
 * @param bool $enabled
 *   If TRUE load only enabled words.
 *
 * @param bool $termid
 *   If TRUE load only for a $tid site
 *
 * @return array
 *   An array of words objects indexed by text.
 */
function netapsys_glossaire_wordlink_load_all($termid = false, $enabled = TRUE) {

	$words = &drupal_static(__FUNCTION__);
	if (!isset($words)) {
		if ($cache = cache_get('netapsys_glossaire_wordlink_words_' . $termid )) {
			$words = $cache->data;
		}
		else {
			$query = db_select('node', 'n');
			$query->fields('n', array('title', 'nid'));
			$query->condition('n.type', 'contenu_glossaire');
			if ($enabled) {
				$query->condition('status', 1);
			}	
			$results = $query->execute();
			$words = array();
			foreach ($results as $word) {
				if (!isset($words[$word->title])) {
					$words[drupal_strtolower($word->title)] = $word;
				}       
			}
			cache_set('netapsys_glossaire_wordlink_words_' . $termid , $words, 'cache');
		}
	}
  return $words;

}
Chargement des titres des contenus glossaire

 

Dans cette fonction, nous transformons le texte récursivement.


/**
 * Helper function for converting text.
 *
 * @param string $text
 *   Input text.
 * @param string $pattern
 *   Regular expression pattern.
 * @param array $words
 *   Array of all words.
 * @param string $disallowed
 *   Disallowed tags.
 * @param array $settings
 *   Array of filter settings.
 * @param string $tag
 *   Tag that will be used to replace word.
 */
function netapsys_glossaire_wordlink_convert_text_recursively(&$text, $pattern, $words, $disallowed, $settings, $tag) {
  // Create DOM object.
  $dom = filter_dom_load($text);
  $xpath = new DOMXPath($dom);
  $text_nodes = $xpath->query('//text()[not(ancestor::a) ' . $disallowed . ']');

  foreach ($text_nodes as $original_node) {

    $text = $original_node->nodeValue;
    $match_count = preg_match_all($pattern, $text, $matches, PREG_OFFSET_CAPTURE);

    if ($match_count > 0) {
      $offset = 0;
      $parent = $original_node->parentNode;
      $next = $original_node->nextSibling;
      $parent->removeChild($original_node);

      foreach ($matches[0] as $delta => $match) {

        $match_text = $match[0];
        $match_pos = $match[1];
        $text_lower = drupal_strtolower($match_text);
        $word = $words[$text_lower];	

        //check all strtolower
         if ( mb_strtolower($word->title) == mb_strtolower($match_text) ) {
          $prefix = substr($text, $offset, $match_pos - $offset);
          $parent->insertBefore($dom->createTextNode($prefix), $next);
          $link = $dom->createDocumentFragment();
          $word_link_rendered = &drupal_static('word_link_rendered');


          if (!isset($word_link_rendered[$word->nid])) {

            if ($cache = cache_get('word_link_rendered_' . $word->nid)) { //id changed for nid
              $word_link_rendered[$word->nid] = $cache->data;
            }
            else {
              $target = url_is_external('node/' . $word->nid) ? '_blank' : '';
              $url_external = url_is_external('node/' . $word->nid);
              $url_options = array();
              $url_path = NULL;
  			  
  			      //hack url > instead of node/ + $word->nid we set to glossaire?title=$word->title
              if ($url_external) {
                $url_path = 'glossaire?title=' . $word->title;
              }
              else {
                $url_parts = parse_url('glossaire?title=' . $word->title);
                $url_query = array();
                
                if (isset($url_parts['query'])) {
                  parse_str($url_parts['query'], $url_query);
                }
                $url_options = array(
                  'query' => $url_query,
                  'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : '',
                );

                if (empty($url_parts['path'])) {
                  // Assuming that URL starts with #.
                  $url_options['external'] = TRUE;
                  $url_path = NULL;
                }
                else {
                  $url_path = $url_parts['path'];
                }
              }

              $attributes = array(
                'href' => url($url_path, $url_options),
                'title' => $word->title,                
                'class' => 'netapsys_glossaire_wordlink', //$word->class
                'target' => $target,
              );

              if ($settings['word_link_highlight']) {
                $tag = 'span';
                unset($attributes['href'], $attributes['target'], $attributes['rel']);
              }

              $word_link_rendered[$word->nid] = theme(
                'netapsys_glossaire_wordlink',
                array(
                  'text' => $match_text,
                  'tag' => $tag,
                  'attributes' => array_filter($attributes),
                )
              );

              if (!empty($settings['word_link_wrap_tag'])) {
                $word_link_rendered[$word->nid] = theme(
                  'html_tag',
                  array(
                    'element' => array(
                      '#tag' => $settings['word_link_wrap_tag'],
                      '#value' => $word_link_rendered[$word->nid],
                    ),
                  )
                );
              }
              cache_set('word_link_rendered_' . $word->nid, $word_link_rendered[$word->nid], 'cache');
            }
          }

          $link->appendXML($word_link_rendered[$word->nid]);
          $parent->insertBefore($link, $next);
          $offset = $match_pos + strlen($match_text);
        }
        else {
          $prefix = substr($text, $offset, $match_pos - $offset);
          $parent->insertBefore($dom->createTextNode($prefix), $next);
          $parent->insertBefore($dom->createTextNode($match_text), $next);
          $offset = $match_pos + strlen($match_text);
        }

        if ($delta == $match_count - 1) {
          $suffix = substr($text, $offset);
          $parent->insertBefore($dom->createTextNode($suffix), $next);
        }

      }
    }
  }

  $text = filter_dom_serialize($dom);
}
Transformation du texte

Sinon il faudrait une vue qui filtre les contenus du type de contenu "contenu_glossaire" et qui sera accessible depuis l'url /glossaire

 

Nous pourrons de la même manière customiser l'affichage de chaque mot, depuis le hook_theme


/**
 * Implements hook_theme().
 */
function netapsys_glossaire_theme() {
  return array(
    'netapsys_glossaire_wordlink' => array(
      'variables' => array(
        'text' => NULL,
        'tag' => NULL,
        'attributes' => array(),
      ),
      'file' => 'theme/netapsys_glossaire_wordlink.theme.inc',
    ),
  );
}
hook_theme

 

Contenu du fichier netapsys_glossaire_wordlink.theme.inc

/**
 * @file
 * Theme for netapsys_glossaire_wordlink.
 */

/**
 * Render a wordlink link.
 */
function theme_netapsys_glossaire_wordlink($vars) {

  $attributes = drupal_attributes($vars['attributes']);
  return '<' . $vars['tag'] . $attributes . '>' . check_plain($vars['text']) . '</' . $vars['tag'] . '>';

}
netapsys_glossaire_wordlink

 

 

 

 

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.