Extension:Terminology

From MediaWiki.org

Jump to: navigation, search

         

Manual on MediaWiki Extensions
List of MediaWiki Extensions
Crystal Clear action run.png
Terminology

Release status: stable

Implementation  Extended syntax
Description Enable a Glossary or define Terminology within MediaWiki using tooltips.
Author(s)  BarkerJr with modifications by Benjamin Kahn (xkahn <at> zoned <dot> net)
Last Version  2007.12.16+20090529Patches (8-28-08)
MediaWiki  1.6 (and above)
License LGPL 2+
Download See files below

check usage (experimental)

Contents

[edit] Related Extensions

An alternative version of this extension can be found at Extension:Glossary

[edit] What can this extension do?

Ever been reading a technical article with lots and lots of acronyms? Unless you know all the terminology they are using, it can be really frustrating trying to understand what the author means.

This extension allows wiki authors to define a list of acronyms and their definitions on a special page. Whenever that acronym is found, it will be highlighted and mousing over it will reveal the definition in a tooltip.

Additionally, using Walter Zorn's "JavaScript, DHTML Tooltips" library (found at: http://www.walterzorn.com/tooltip/tooltip_e.htm ) you can customize the appearance of the tooltips.

[edit] Usage

By default, the page "Terminology" without a namespace will be used as the source of acronyms and their definitions. It's also possible to modify the extension to put the definitions in a protected namespace.

[edit] Changelog

  • Patches from May 29, 2009:
    • Better support for the FCKEditor extension
    • Do not show underlines when printing wiki pages
  • Patches from Aug 28, 2008:
    • Make the regex case sensitive for acronyms (Thanks Abrillon)
    • Add support for the FCKEditor extension
    • Allow a single newline in between the acronym and its definition

[edit] Installation

1. Create the file:

[edit] For PHP5: extensions/terminology.php

<?php
/*
 * Copyright (C) 2007  BarkerJr and (C) 2008 Benjamin Kahn
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
/*
 * For nicer tooltips, download JavaScript, DHTML Tooltips from Walter Zorn at
 * http://www.walterzorn.com/tooltip/tooltip_e.htm and extract it to $wgScriptPath/extensions/tooltip/
 *
 * Use definition-lists in the Terminology page.  Place one definition on each line.  For example:
 * ;CS-S:CounterStrike Source, a game by Valve
 * ;HTML:HyperText Markup Language
 */
$wgExtensionCredits['parserhook'][] = array(
  'name' => 'Terminology',
  'description' => 'Provides tooltips from the [[Terminology]] defined for all instances of the given term',
  'version' => '2007.12.16+20080828Patches',
  'author' => 'BarkerJr modified by Benjamin Kahn (xkahn@zoned.net)'
);
 
$wgExtensionFunctions[] = 'terminologySetup';
function terminologySetup() {
  global $wgOut, $wgScriptPath;
  $wgOut->addHTML("<style text=\"text/css\" media=\"screen\"><!-- .terminologydef {border-bottom: 1px dashed green;} --></style>");
  if (is_file ('extensions/tooltip/wz_tooltip.js')) {
    $wgOut->addHTML("<script type='text/javascript' src='$wgScriptPath/extensions/tooltip/wz_tooltip.js'></script>");
  }
}
 
$wgHooks['ParserBeforeTidy'][] = 'terminologyParser';
function terminologyParser(&$parser, &$text) {
  global $wgRequest;
 
  $action = $wgRequest->getVal( 'action', 'view' );             
  if ($action=="edit" || $action=="ajax") return false;
 
  $rev = Revision::newFromTitle(Title::makeTitle(null, 'Terminology'));
  if ($rev) {
    $content = $rev->getText();
    if ($content != "") {
      $changed = false;
      $doc = new DOMDocument();
@     $doc->loadHTML('<meta http-equiv="content-type" content="charset=utf-8"/>' . $text);
      $c = explode("\n", $content);
      reset($c);
      while (list($key, $entry) = each($c)) { 
        $terms = explode(':', $entry, 2);
        if ($terms[0][0] == ';') {
 
          // It's possible that the definition is on the next line
	  if (count($terms) == 1) {
	    list($k1, $e1) = each($c);
            if ($e1[0] == ':') {
	      $term = trim(substr($terms[0], 1));
	      $definition = trim(substr($e1, 1));
	    } else {
	      continue;
	    }
 
	  } elseif (count($terms) == 2) {
            $term = trim(substr($terms[0], 1));
            $definition = trim($terms[1]);
	  } else {
	    continue;
	  }
 
          if (terminologyParseThisNode($doc, $doc->documentElement, $term, $definition)) {
              $changed = true;
          }
        }
      }
      if ($changed) {
        $text = $doc->saveHTML();
      }
    }
  }
  return true;
}
 
function terminologyParseThisNode($doc, $node, $term, $definition) {
  $changed = false;
  if ($node->nodeType == XML_TEXT_NODE) {
    $texts = preg_split('/\b('.preg_quote($term).'s?)\b/u', $node->textContent, -1, PREG_SPLIT_DELIM_CAPTURE);
    if (count($texts) > 1) {
      $container = $doc->createElement('span');
      for ($x = 0; $x < count($texts); $x++) {
        if ($x % 2) {
          $span = $doc->createElement('span', $texts[$x]);
 
	  if (!is_file ('extensions/tooltip/wz_tooltip.js')) {
	    $span->setAttribute('title', $term . ": " . $definition);
            $span->setAttribute('class', 'terminologydef');
	  } else {
            $bad = array ("\"", "'");
            $good = array ("\\\"", "\'");
	    $span->setAttribute('onmouseover', "Tip('".str_replace ($bad, $good, $definition)."', STICKY, true, DURATION, -1000)");
	    $span->setAttribute('class', 'terminologydef');
	    $span->setAttribute('onmouseout', "UnTip()");
	  }
 
          $span->setAttribute('style', 'cursor:help');
          $container->appendChild($span);
        } else {
          $container->appendChild($doc->createTextNode($texts[$x]));
        }
      }
      $node->parentNode->replaceChild($container, $node);
      $changed = true;
    }
  } elseif ($node->hasChildNodes()) {
    // We have to do this because foreach gets confused by changing data
    $nodes = $node->childNodes;
    $previousLength = $nodes->length;
    for ($x = 0; $x < $nodes->length; $x++) {
      if ($nodes->length <> $previousLength) {
        $x += $nodes->length - $previousLength;
      }
      $previousLength = $nodes->length;
      $child = $nodes->item($x);
      if (terminologyParseThisNode($doc, $child, $term, $definition)) {
        $changed = true;
      }
    }
  }
  return $changed;
}

[edit] For PHP4: extensions/terminology.php

<?php
/*
 * Copyright (C) 2007  BarkerJr and (C) 2008 Benjamin Kahn
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
/*
 * For nicer tooltips, download JavaScript, DHTML Tooltips from Walter Zorn at
 * http://www.walterzorn.com/tooltip/tooltip_e.htm and extract it to $wgScriptPath/extensions/tooltip/
 *
 * Use definition-lists in the Terminology page.  Place one definition on each line.  For example:
 * ;CS-S:CounterStrike Source, a game by Valve
 * ;HTML:HyperText Markup Language
 */
$wgExtensionCredits['parserhook'][] = array(
  'name' => 'Terminology',
  'description' => 'Provides tooltips from the [[Terminology]] defined for all instances of the given term',
  'version' => '2007.12.16+20080828Patches',
  'author' => 'BarkerJr modified (and ported to PHP4) by Benjamin Kahn (xkahn@zoned.net)'
);
 
$wgExtensionFunctions[] = 'terminologySetup';
function terminologySetup() {
  global $wgOut, $wgScriptPath;
  $wgOut->addHTML("<style text=\"text/css\" media=\"screen\"><!-- .terminologydef {border-bottom: 1px dashed green;} --></style>");
  if (is_file ('extensions/tooltip/wz_tooltip.js')) {
    $wgOut->addHTML("<script type='text/javascript' src='$wgScriptPath/extensions/tooltip/wz_tooltip.js'></script>");
  }
}
 
$wgHooks['ParserBeforeTidy'][] = 'terminologyParser';
function terminologyParser(&$parser, &$text) {
  global $wgRequest;
 
  $action = $wgRequest->getVal( 'action', 'view' );             
  if ($action=="edit" || $action=="ajax") return false;
 
  $rev = Revision::newFromTitle(Title::makeTitle(null, 'Terminology'));
  if ($rev) {
    $content = $rev->getText();
    if ($content != "" && $text != "") {
      $changed = false;
@      $doc = xmldoc ($text);
      $c = explode("\n", $content);
      reset($c);
      while (list($key, $entry) = each($c)) { 
        $terms = explode(':', $entry, 2);
        if ($terms[0][0] == ';') {
 
          // It's possible that the definition is on the next line
	  if (count($terms) == 1) {
	    list($k1, $e1) = each($c);
            if ($e1[0] == ':') {
	      $term = trim(substr($terms[0], 1));
	      $definition = trim(substr($e1, 1));
	    } else {
	      continue;
	    }
 
	  } elseif (count($terms) == 2) {
            $term = trim(substr($terms[0], 1));
            $definition = trim($terms[1]);
	  } else {
	    continue;
	  }
 
          if (terminologyParseThisNode($doc, $doc->root(), $term, $definition)) {
            $changed = true;
          }
        }
      }
      if ($changed) {
        $text = $doc->dump_mem(true);
      }
    }
  }
  return true;
}
 
function terminologyParseThisNode($doc, $node, $term, $definition) {
  $changed = false;
  if ($node->node_type() == XML_TEXT_NODE) {
    $texts = preg_split('/\b('.preg_quote($term).'s?)\b/u', $node->get_content(), -1, PREG_SPLIT_DELIM_CAPTURE);
    if (count($texts) > 1) {
      $container = $doc->create_element('span');
      for ($x = 0; $x < count($texts); $x++) {
        if ($x % 2) {
          $span = $doc->create_element('span');
	  $span->set_content($texts[$x]);
 
          if (!is_file ('extensions/tooltip/wz_tooltip.js')) {
            $span->set_attribute('title', $term . ": " . $definition);
            $span->set_attribute('class', 'terminologydef');
          } else {
            $bad = array ("\"", "'");
            $good = array ("\\\"", "\'");
	    $span->set_attribute('onmouseover', "Tip('".str_replace ($bad, $good, $definition)."', STICKY, true, DURATION, -1000)");
            $span->set_attribute('onmouseout', "UnTip()");
            $span->set_attribute('class', 'terminologydef');
          }
 
          $span->set_attribute('style', 'cursor:help');
          $container->append_child($span);
        } else {
          $container->append_child($doc->create_text_node($texts[$x]));
        }
      }
      $parent = $node->parent_node();
      $parent->replace_child($container, $node);
      $changed = true;
    }
  } elseif ($node->has_child_nodes()) {
    // We have to do this because foreach gets confused by changing data
    $nodes = $node->child_nodes();
    $previousLength = count($nodes);
    for ($x = 0; $x < count($nodes); $x++) {
      if (count($nodes) <> $previousLength) {
        $x += count($nodes) - $previousLength;
      }
      $previousLength = count($nodes);
      $child = $nodes[$x];
      if (terminologyParseThisNode($doc, $child, $term, $definition)) {
        $changed = true;
      }
    }
  }
  return $changed;
}

[edit] LocalSettings.php

2. Add the following to the end of your LocalSettings.php:

require_once( "$IP/extensions/terminology.php" );

3. (Optional) If you wish to use the Javascript-based tooltips, you need to download Walter Zorn's "JavaScript, DHTML Tooltips" library. (found at: http://www.walterzorn.com/tooltip/tooltip_e.htm ) Create the directory: extensions/tooltip.

mkdir -p extensions/tooltip

And uncompress the archive "wz_tooltip.zip" into that directory. You should have the latest version of the library (version 5.13 at the time of this writing.) (At the end, you should have 3 files: extensions/tooltip/tip_centerwindow.js, extensions/tooltip/tip_followsscroll.js, and extensions/tooltip/wz_tooltip.js )

4. Create the page "Terminology"

5. Add one line to this page for each acronym. Each line should start with a ';' (semicolon), then the text to be replaced followed by a ':' (colon) followed by the text to replace it with.

[edit] Sample Terminology Page

Support single line definitions
;FTP:File Transport Protocol
;AAAAA:American Association Against Acronym Abuse
;ACK:Acknowledge
;AFAIK:As Far As I Know
;AWGTHTGTATA:Are We Going To Have To Go Through All This Again

And multi-line definitions
;HTTP
:HyperText Transfer Protocol

[edit] Parameters

1. To lock down the terminology, modify the terminology.php file.

Find the line:

$rev = Revision::newFromTitle(Title::makeTitle(null, 'Terminology'));

and change it to:

$rev = Revision::newFromTitle(Title::makeTitle(8, 'Terminology'));

The Terminology Page will now be located at:

MediaWiki:Terminology

You can also change the name of the "Terminology" page by replacing the quoted name with something else.

2. You can modify the options of the javascript tooltip style by editing the line (89) with the options "STICKY, true, DURATION, -1000" to suit your needs.

3. You can change the number of times a matched word will be defined. (The default is every time.) Find the line that says:

$texts = preg_split('/\b('.preg_quote($term).'s?)\b/u', $node->textContent, -1, PREG_SPLIT_DELIM_CAPTURE);

and change the "-1" to 1 or 2.

[edit] Known problems

  • Conflicts with the FCKeditor extension.
    • To correct this issue, add the __NORICHEDITOR__ magic word to the top of the Terminology page. This will cause it to never use FCKeditor to edit the page.
  • Any empty lines in the Terminology page will cause "Notice: Uninitialized string offset" errors displayed on Wiki pages.
    • To correct this issue, change $terms[0][0] to @$terms[0][0] on line number 62 of extensions/terminology.php.
  • PHP 4.3.0 and newer
    • Changes to the xmldoc API require:
      • replace
@      $doc = xmldoc ($text);

with

$doc = domxml_open_mem('<r>'.$text.'</r>');
      • replace
$text = $doc->dump_mem(true);

with

$text = substr($doc->dump_mem(true), 25, -5 );
      • append
 elseif($node->node_name() == 'a') {
    // <a /> nodes confuse browsers, that expect <a ></a>
    $padding = $doc->create_text_node('');
    $node->append_child( $padding );
  }

to terminologyParseThisNode, before return $changed