Extension talk:Livelets.php
Contents
Planned features
Polling
This is the basic method of the extension being able to have some livelet containers automatically stay up to date. A parameter would be added to the parser-function syntax specifying a polling period. Polling can accumulate a lot of server traffic if there are many clients displaying live content.
One way to lighten the traffic load is for the request to include the revision ID of the source article for the livelet being requested. The server can then do an early and computationally minimal check to see if that is still the latest revision, and if so exit early with a 204 (No content) response code.
External requests
It would be extremely useful if livelets could be configured to poll an external site and exhibit a regular expression rule for how to extract and format the returned content. To reduce traffic it should first request just the headers and then determine from that whether or not to request and process the content.
Event driven
This is a major upgrade involving a 1px SWF that initially establishes a connection with the server so that the server can push change information down the pipe to the listening clients. When a SWF instance receives information that content has changed in one of its livelets, the SWF prompts the JS to make an AJAX request for the appropriate livelet's content. The SWF part is quite simple, but the server-side part is quite a bit of work.
Current version (for committing to wikimedia repo)
<php> <?php /**
* Extension:Livelets - Allows articles to be transcluded which load after the main page content and can update dynamically with Ajax * * @package MediaWiki * @subpackage Extensions * @author Aran Dunkley, Jack Henderson * @licence GNU General Public Licence 2.0 or later * Started: 2007-10-06 * 1.0: 2010-08-25 */
define( 'LIVELETS_VERSION', '1.0.5, 2010-09-23' );
- the parser-function name for doing live-transclusions
$wgLiveletsMagic = 'live';
- Settings for the event-driven live method
$wgLiveletsUseSWF = false; # Set this to true to use SWF to make livelets fully event-driven (no polling for change) $wgLiveletsSwfBg = '#ffffff'; # The background colour of the embedded SWF $wgLiveletsPort = '1729'; # The port that Livelets.pl can be reached on (using $wgServer:$wgLiveletsPort) $wgLiveletsDelay = '0'; # Default livelet display delay of 0 (immediate). Enable delay by setting $wgLiveletsDelay
# in Localsettings after this extension is included, to some number of milliseconds (1000 = 1 sec)
$wgExtensionCredits['parserhook'][] = array( 'path' => __FILE__, 'name' => 'Livelets', 'author' => 'Aran Dunkley, Jack Henderson', 'descriptionmsg' => 'livelets-desc', 'url' => 'http://www.mediawiki.org/wiki/Extension:Livelets', 'version' => LIVELETS_VERSION ); $dir = dirname( __FILE__ ); $wgExtensionMessagesFiles['Livelets'] = "$dir/Livelets.i18n.php";
class Livelets {
var $container_id = 0; var $request_id = 0;
function __construct() { global $wgHooks, $wgExtensionFunctions;
# Activate the parser-function $wgHooks['LanguageGetMagic'][] = $this;
# Call the setup method at extension setup time $wgExtensionFunctions[] = array( $this, 'setup' );
# Bypass ajax dispatcher if this is a livelet ajax request if( array_key_exists( 'action', $_GET ) && $_GET['action'] == 'ajax' && $_GET['rs'] == 'livelets' ) {
# Extract the data from the ajax request list( $title, $this->request_id ) = $_GET['rsargs'];
# Adjust the request so that it bypasses the ajax dispatcher and executes with action=render $_GET = $_REQUEST = array( 'title' => $title, 'action' => 'render' );
# Add a hook to replace the wikitext with just the requested livelet's content $wgHooks['ArticleAfterFetchContent'][] = $this; } }
# Called at extension setup time
function setup() {
global $wgOut, $wgParser, $wgLiveletsMagic, $wgLiveletsUseSwf;
# Activate the parser-function $wgParser->setFunctionHook( $wgLiveletsMagic, array( $this, 'renderContainer' ) );
# Embed the SWF if enabled (SWF must be requested from Livelets.pl) if ( $wgLiveletsUseSwf ) { global $wgServer, $wgLiveletsPort, $wgLiveletsSwfBg; $swf = "$wgServer:$wgLiveletsPort/Livelets.swf"; $wgOut->addHTML("<object type=\"application/x-shockwave-flash\" data=\"$swf\" width=\"1\" height=\"1\"> <param name=\"movie\" value=\"$swf\" /><param name=\"bgcolor\" value=\"$wgLiveletsSwfBg\"/></object>"); }
}
# Render livelet container
function renderContainer( &$parser ) {
global $wgTitle, $wgJsMimeType, $wgLiveletsDefaultContent, $wgLiveletsDelay;
# Ensure the livelets are all numbered so they can be referred to by the ajax request $id = $this->container_id++;
# Increment the delay by the correct amount so livelets appear one after the other $wgLiveletsDelayInc = $wgLiveletsDelay + ( $wgLiveletsDelay * $id );
# Render a container with a 'livelet-loading' div that will be replaced
$loading = "
"; $html = "
";
# Construct an ajax call to collect the content for this container. $title = $wgTitle->getPrefixedText();
# Call the ajax, applying the delay for each livelet call $script = "var liveletsUpdate = 0; clearTimeout( liveletsUpdate );
liveletsUpdate = setTimeout( \"sajax_do_call('livelets',['$title','$id'], document.getElementById('livelet-$id'))\", $wgLiveletsDelayInc )";
$html .= "<script type='$wgJsMimeType'>$script</script>";
return array( $html, 'isHTML' => true, 'noparse' => true ); }
# This early hook called from the Ajax bypass whereby the parser has yet to process wikitext
function onArticleAfterFetchContent( &$article, &$content ) {
global $wgLiveletsMagic;
# Replace the wikitext with just the content of the requested livelet foreach( $this->examineBraces( $content ) as $brace ) { if ( $brace['NAME'] == "#$wgLiveletsMagic:" && --$this->request_id < 0 ) { $len = strlen( $wgLiveletsMagic ); $content = substr( $content, $brace['OFFSET'] + $len + 4, $brace['LENGTH'] - $len - 6 ); break; } }
return true; }
# Extract recursive braces belonging to templates and parserfunctions
function examineBraces( &$content ) {
$braces = array();
$depths = array();
$depth = 1;
$index = 0;
while( preg_match( '/\\{\\{\\s*([#a-z0-9_]*:?)|\\}\\}/is', $content, $match, PREG_OFFSET_CAPTURE, $index ) ) {
$index = $match[0][1] + 2;
if( $match[0][0] == '}}' ) {
$brace =& $braces[$depths[$depth-1]];
$brace['LENGTH'] = $match[0][1] - $brace['OFFSET'] + 2;
$brace['DEPTH'] = $depth--;
} else {
$depths[$depth++] = count( $braces );
$braces[] = array(
'NAME' => $match[1][0],
'OFFSET' => $match[0][1]
);
}
}
return $braces; }
# Set up magic word
function onLanguageGetMagic( &$magicWords, $langCode = 0 ) {
global $wgLiveletsMagic;
$magicWords[$wgLiveletsMagic] = array( $langCode, $wgLiveletsMagic );
return true; } }
- Instantiate a global instance of the extension
$wgLivelets = new Livelets(); </php>