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.
- 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;
}
?>
- Install the extension by adding a single line to the end of your
LocalSettings.php file (above the ?>):
include("extensions/ApolloTranscript.php");
- 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 |
- 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>
<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>
<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