Eric Hartwell's InfoDabble

 
Welcome to Eric Hartwell's InfoDabble
About | Site Map
Home Tech Notes Apollo 17: Blue Marble Apollo 17 Flight Journal   Calendars About me  

HowTo: Create a MediaWiki Extension for Apollo Transcripts

By Eric Hartwell - last updated March 26, 2006

MediaWiki, the software that runs the various Wikimedia projects, allows developers to write their own extensions to the wiki markup. An extension defines an XML-style tag which can be used in the wiki editor like this:

<tagname attribute="some attribute"> some text </tagname>

The attributes and the text between the tags get passed on to a PHP function you implement. This function can then return a HTML string that gets inserted into the output in place of the tags and text. Note that the return string should be HTML, not wiki markup.

Related Articles

Existing Extensions

As you might expect with such a widely used open source project, there are a number of user-developed extensions already available, some of which could be useful for this project:

  • Biblio: Citation manager
  • EasyTimeline: Graphical timelines
  • ScreenPlay Extension: Text-formatting add-on which allows screenwriters and hobbyists the ability to use MediaWiki as a screen writing tool.
  • Interwiki linking: By adding a prefix to another project, internal link style ("prefixed internal link style") can be used to link to a page of another project. For example, [[wikipedia:interWiki]] links to the wikipedia:interWiki article on the English Wikipedia.

At the very least, their code could be used for reference.

Apollo Flight Journal styles

The AFJ uses paragraph styles to identify sources, as show in this extract from the Apollo 16 Flight Journal:

<style type="text/css">
.pao     {color: #006600;}
.ob       {color:#660000;}
.tech    {color: #000000;
.ed       {color: #000066; margin-left:40px}
</style>

<p class="ob"><b>000:00:01 Duke (onboard)</b>: Man, we're on our way!</p>
<
p class="tech"><b>000:00:06 Young</b>: Yaw program [garble, probably tower] clear.</p>
<
p class="pao"><b>Public Affairs Officer:</b> "We clear the tower."</p>
<
p class="ed">[Once the stack is clear of the tower, control of the mission switches to the Mission Control Center]</p>

MediaWiki is CSS friendly, and adding in-line CSS to the contents part of the page works.

Creating a new extension: ApolloTranscript.php

Step 1: Example Extension

Just to verify everything works, follow the example in Extending wiki markup.

  1. Create a new file called ApolloTranscript.php in the MediaWiki installation's extensions/ subdirectory containing the following template code:

    <?php
    # ApolloTranscript WikiMedia extension
    # With WikiMedia's extension mechanism it is possible to define new tags of the form
    # <ApolloTranscript> some text </ApolloTranscript>
    # The function registered by the extension gets the text between the tags as input and can transform it into arbitrary HTML code.
    # Note: The output is not interpreted as WikiText but directly included in the HTML output. So Wiki markup is not supported.
    # To activate the extension, include it from your LocalSettings.php with: include("extensions/ApolloTranscript.php");

    $wgExtensionFunctions[] = "wfApolloTranscript";

    function
    wfApolloTranscript() {
        global
    $wgParser;
        
    # Register the extension with the WikiText parser.
        # The first parameter is the name of the new tag. In this case it defines the tag <ApolloTranscript> ... </ApolloTranscript>
        # The second parameter is the callback function for processing the text between the tags
        
    $wgParser->setHook( "ApolloTranscript", "renderApolloTranscript" );
    }

    # The callback function for converting the input text to HTML output
    function renderApolloTranscript( $input, $argv ) {
        
    # $argv is an array containing any arguments passed to the extension like <example argument="foo" bar>..
        # Put this on the sandbox page:  (works in MediaWiki 1.5.5)
        #   <ApolloTranscript argument="foo" argument2="bar">Testing text **example** in between the new tags</ApolloTranscript>
        
    $output = "Text passed into example extension: <br/>$input";
        
    $output .= " <br/> and the value for the arg 'argument' is " . $argv["argument"];
        
    $output .= " <br/> and the value for the arg 'argument2' is: " . $argv["argument2"];
        return
    $output;
    }
    ?>

  2. Install the extension by adding a single line to the end of your LocalSettings.php file (above the ?>):

    include("extensions/ApolloTranscript.php");

  3. Reference your extension on a sandbox or other page:
    <ApolloTranscript argument="foo" argument2="bar">Testing text **example** in between the new tags</ApolloTranscript>
    Text passed into example extension:
    Testing text **example** in between the new tags
    and the value for the arg 'argument' is foo
    and the value for the arg 'argument2' is: bar

    Note: The contents and closing tag must all be on the same line, otherwise the formatting gets a bit odd, presumably because the $input text has a line break :
    <ApolloTranscript argument="foo" argument2="bar">
    Testing text **example** in between the new tags
    </ApolloTranscript>
    Text passed into example extension:
    Testing text **example** in between the new tags
     and the value for the arg 'argument' is foo 
     and the value for the arg 'argument2' is: bar
     
  4. Add the Apollo 17 transcript excerpt above to the test page. The page should appear with no special formatting for this section.
    Note: When you change the code for an extension, all pages that use the extension should immediately reflect the results of new code. However, sometimes you need to force the parser cache to be flushed: click on edit, replace "action=edit" in the URL shown in the address bar of your browser by "action=purge" and press the Preview button.

Step 2: Add transcript tags

First add a translator for the <event> and <quote> tags.

function wfApolloTranscript() {
    global $wgParser;
    # Register the extension with the WikiText parser.
    # The first parameter is the name of the new tag. In this case it defines the tag <ApolloTranscript> ... </ApolloTranscript>
    # The second parameter is the callback function for processing the text between the tags
    $wgParser->setHook( "ApolloTranscript", "renderApolloTranscript" );
    $wgParser->setHook( "event", "renderTranscriptEvent" );
    $wgParser->setHook( "quote", "renderTranscriptQuote" );
}

# <event> callback function for converting the input text to HTML output
function renderTranscriptEvent( $input, $argv ) {
    return "</p><p><b>" . $argv["met"] . "</b>" . $input;
}

# <quote> callback function for converting the input text to HTML output
function renderTranscriptQuote( $input, $argv ) {
    return "</p><p><b>" . $argv["who"] . "</b>: " . $input;
}

Now add some transcript XML to the contents of the page:

<ApolloTranscript argument="foo" argument2="bar">
<event met="000:00:00">
  <quote who="LCC">we have a liftoff. We have a liftoff and it's lighting up the area, its just like daylight here at Kennedy Space Center as the Saturn V is moving off the pad. It has now cleared the tower.</quote>
</event>
<h3>Ascent: First Stage S-IC</h3>
<event met="000:00:03">
  <quote who="CDR">Roger. The clock has started. We have you. (Laughter) Clear the tower. Roger; tower. Yaw's complete. We're into roll, Bob.</quote>
  <quote src="TechDebrief" who="CDR">Countdown - It was dark and we didn't see anything until S-IC ignition.</quote>
  <quote src="TechDebrief" who="CDR">The S-IC ignition - The lights started going out at 7 seconds, and somewhere around 3 seconds they were completely out. You could feel the ignition. You could feel the engines come up to speed. Just prior to lift-off and during the first few seconds of lift- off when we were near the pad, both the CMP and I could see the reflection of the engine ignition out the left-hand window and the hatch window in the BPC. We could not see the fire but could see a red glow through the windows reflecting apparently off the surface. Ignition was like a big old freight train sort of starting to rumble and shake and rattle and as she lifted off. We got a good tower clear.</quote>
  <quote src="TechDebrief" who="CMP">I really wasn't watching the lights because I guess I didn't expect the thing to shake quite as much as it did. To me, I felt like I was really vibrating. I wanted to find out what was making me vibrate. I wasn't expecting that much vibration when the S-IC lit off. At lift-off, again, once it got vibrating, I didn't feel the yaw. I was watching the needle on the thing but didn't feel the yaw, though.</quote>
  <quote src="TechDebrief" who="CDR">Powered flight - During the actual powered flight of the S-IC you could not see anything at all. You couldn't see out the cockpit, as we had the lights up fairly bright.</quote>
</event>
<event met="000:00:17">
  <quote who="CCo">Roger, Geno. Looking great. Thrust good on all five engines.</quote>
</event>
<event met="000:00:20">
  <quote who="CDR">Okay, babe. It's looking good here; roll is complete. We are pitching.</quote>
  <quote who="(unidentified)">Wow woozle!</quote>
  <quote who="CMP">Okay, babe. Let's check the angles.</quote>
  <quote who="PAO">This is Mission Control. Gene Cernan reporting the launch vehicle maneuvering to the proper attitude, everything looking good at this point.</quote>
  <quote who="CMP">Thirty seconds. We're going up. Man, oh, man!</quote>
</event>
<event met="000:00:36">
  <quote who="CDR">Thirty seconds, and 17 is GO.</quote>
  </event>
<event met="000:00:38">
  <quote who="CCo">Roger, 17, you're GO.</quote>
  <quote who="PAO">First stage looks good. Altitude 1.1 miles. Booster says we look good. We are now at 2.5 miles.</quote>
</event>
</ApolloTranscript>

Here's how the transcript output looks:

000:00:00

  we have a liftoff. We have a liftoff and it's lighting up the area, its just like daylight here at Kennedy Space Center as the Saturn V is moving off the pad. It has now cleared the tower.

Ascent: First Stage S-IC

000:00:03

  Roger. The clock has started. We have you. (Laughter) Clear the tower. Roger; tower. Yaw's complete. We're into roll, Bob.   Countdown - It was dark and we didn't see anything until S-IC ignition.   The S-IC ignition - The lights started going out at 7 seconds, and somewhere around 3 seconds they were completely out. You could feel the ignition. You could feel the engines come up to speed. Just prior to lift-off and during the first few seconds of lift- off when we were near the pad, both the CMP and I could see the reflection of the engine ignition out the left-hand window and the hatch window in the BPC. We could not see the fire but could see a red glow through the windows reflecting apparently off the surface. Ignition was like a big old freight train sort of starting to rumble and shake and rattle and as she lifted off. We got a good tower clear.   I really wasn't watching the lights because I guess I didn't expect the thing to shake quite as much as it did. To me, I felt like I was really vibrating. I wanted to find out what was making me vibrate. I wasn't expecting that much vibration when the S-IC lit off. At lift-off, again, once it got vibrating, I didn't feel the yaw. I was watching the needle on the thing but didn't feel the yaw, though.   Powered flight - During the actual powered flight of the S-IC you could not see anything at all. You couldn't see out the cockpit, as we had the lights up fairly bright.

000:00:17

  Roger, Geno. Looking great. Thrust good on all five engines.

000:00:20

  Okay, babe. It's looking good here; roll is complete. We are pitching.   Wow woozle!   Okay, babe. Let's check the angles.   This is Mission Control. Gene Cernan reporting the launch vehicle maneuvering to the proper attitude, everything looking good at this point.   Thirty seconds. We're going up. Man, oh, man!

000:00:36

  Thirty seconds, and 17 is GO.  

000:00:38

  Roger, 17, you're GO.   First stage looks good. Altitude 1.1 miles. Booster says we look good. We are now at 2.5 miles.

The actual HTML for the output looks like this:

<b>000:00:00</b><p>&nbsp;
<QUOTE who="LCC">we have a liftoff. We have a
liftoff and it's lighting up the area, its just like
daylight here at Kennedy Space Center as the Saturn V is
moving off the pad. It has now cleared the tower.</QUOTE>
</p>
<h3>Ascent: First Stage S-IC</h3>
<p><b>000:00:03</b></p>
<p>&nbsp;
<QUOTE who="CDR">Roger. The clock has started. We
have you. (Laughter) Clear the tower. Roger; tower.
Yaw's complete. We're into roll, Bob.</QUOTE>

The <quote> tags are not being parsed, probably because they're nested inside the <event> tags. MediaWiki has a built-in hook to render wikitext within extensions. A quick experiment shows it now catches the custom tags:

# <event> callback function for converting the input text to HTML output
function renderTranscriptEvent( $input, $argv ) {
     global
$wgOut;
     return
"</p><p><b>" . $argv["met"] . "</b>" . $wgOut->parse($input, false
);
}

We don't need to worry about infinite recursion because there are a finite number of levels for nested tags.

Note: the <h3> tag is not being translated; MediaWiki interprets it as a standard HTML header tag.

Step 3: Activate transcript tags only on transcript pages

A fundamental problem with MediaWiki extensions is that they are registered globally in LocalSettings.php, so they're automatically applied to all pages in the wiki. This means either we have to use a strict namespace separation to prevent collisions.

A cleaner approach would be to register the custom element tags only when processing pages that are tagged as ApolloTranscript. We can examine the MediaWiki source code in Parser.php to see what happens when we register a tag hook:

$wgParser->setHook( "ApolloTranscript", "renderApolloTranscript" );

=== Parser.php ===
2973:
    
function setHook( $tag, $callback ) {
2974:         
$oldVal = @$this->mTagHooks[$tag];
2975:         
$this->mTagHooks[$tag] = $callback;
2976:         return
$oldVal;
2977:     }

The mTagHooks variable is an array of callback functions, indexed by the tag name. MediaWiki doesn't have a clearHook() function, but we can use the PHP unset() statement to remove a particular element from the array:

global $wgParser;
unset($wgParser->mTagHooks[$arg]);

The hook should be removed when we read the closing extension tag </ApolloTranscript>.

Since the user might forget the closing tag, it should also be cleared before the page is rendered. MediaWiki provides a number of likely hooks:

A potential problem with step 3:

According to the PHP manual,

If a globalized variable is unset() inside of a function, only the local variable is destroyed.

Does this mean that if you try to unset() the hooks from inside the page, the global one is left unchanged? Or does it mean that if you add the hooks inside the page, they're automatically deleted at the end of the page rendering function?

The manual goes on to say,

To unset() a global variable inside of a function, you can use the $GLOBALS array to do so.

This means the following should work:

unset($GLOBALS['wgParser']);


Time for a different approach?

This custom tag approach requires an event callback function for every tag we want to show. Adding new tags means adding extra code. Each different transcript would require its own custom code and extension.

A better approach is to write a simple XML/XSL extension. Since XML and XSL are totally generic, different content can be handled simply by changing the XSL. This would only require a single tag, reducing the possibility of namespace collisions. It could also be added to the MediaWiki distribution as a standard extension, requiring no coding on the user's part.

Since the point of using MediaWiki for editing is to end up with a final, definitive document, we'll need the XSL anyway.

At this point, I decided to create a generic MediaWiki extension for XML data instead.


Related Articles

Revision History

  • March 26, 2006 - initial version
Creative Commons License

Unless otherwise noted, this work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License

 

Site Map | About Me