Extension:Terminology

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manual - list
Crystal Clear action run.png
Terminology

Release status: stable

Implementation Extended syntax, Skin
Description Enable a Glossary or define Terminology within MediaWiki using tooltips.
Author(s) BarkerJr with modifications by Benjamin Kahn (xkahn <at> zoned <dot> net)
Latest version 2010-07-29 (8-28-08)
MediaWiki 1.6 (and above)
Database changes no
License LGPL 2+
Download PHP 5.x PHP less than 4.3 (See Known issues for PHP 4.3 or above)
Hooks used
ParserBeforeTidy

Translate the Terminology extension if possible

Check usage and version matrix; code metrics

The Terminology 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: here) you can customize the appearance of the tooltips.

Because of its architecture, this extension becomes VERY inefficient for large dictionaries. A more efficient alternative is available and under development: Extension:Lingo

Installation[edit | edit source]

1. Create the file:

For PHP5: Terminology.php[edit | edit source]

(You can also download it at: http://xkahn.zoned.net/software/mediawiki/php5/terminology.php.txt )

<?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.de/en/tooltip/tooltip_e.htm or
 * http://gualtierozorni.altervista.org/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' => '20100729',
  'author' => 'BarkerJr modified by Benjamin Kahn ([email protected])'
);
 
$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" || isset($_POST['wpPreview'])) 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, WIDTH, -600)");
            $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;
}

For PHP4: Terminology.php[edit | edit source]

(You can also download it at: http://xkahn.zoned.net/software/mediawiki/php4/terminology.php.txt )

<?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 or
 * http://gualtierozorni.altervista.org/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' => '20100729',
  'author' => 'BarkerJr modified (and ported to PHP4) by Benjamin Kahn ([email protected])'
);
 
$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" || isset($_POST['wpPreview'])) 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, WIDTH, -600)");
            $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;
}

LocalSettings.php[edit | edit source]

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: here or there). 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.

Usage[edit | edit source]

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.

Sample Terminology Page[edit | edit source]

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

Parameters[edit | edit source]

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.

4. You can choose to ignore case for matched words. (So the definition for FTP would match ftp, FtP, FTp, etc.)

Find the line that says:

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

And add an 'i' in it. Like this:

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

Known problems[edit | edit source]

Conflicts with the HeaderTabs extension[edit | edit source]

Use Extension:Lingo instead

Conflicts with the FCKeditor extension[edit | edit source]

    • 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.

PHP 4.3.0 and newer[edit | edit source]

    • 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

Changelog[edit | edit source]

  • Patches from Jan 16, 2010:
    • Fix a conflict with FCKEditor extension. (Stop littering the editor with span tags)
  • Patches from Jan 07, 2010:
    • Constrain the width of the javascript based tooltips to 600px
    • Fix a bug where empty definitions could display an error
    • Document how to change case sensitivity
  • 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

See also[edit | edit source]