Difference between revisions of "Mediawiki RawFile"
m (→Hook on Raw) |
m (→The code) |
||
Line 51: | Line 51: | ||
* Tells the browser NOT to cache the raw |
* Tells the browser NOT to cache the raw |
||
==The code== |
==The code== |
||
− | Which you can of course download just by following [{{# |
+ | Which you can of course download just by following [{{#rawsnippetlink: RawSnippet.php}} this link :-)] |
+ | |||
+ | So let's explain a bit the code in a Literate Programming way... |
||
+ | |||
+ | First some hooks for our functions... |
||
+ | {{#rawsnippetanchor: RawSnippet.php}} |
||
<source lang=php> |
<source lang=php> |
||
<?php |
<?php |
||
Line 64: | Line 69: | ||
$wgHooks['RawPageViewBeforeOutput'][] = 'fnRawSnippet_Strip'; |
$wgHooks['RawPageViewBeforeOutput'][] = 'fnRawSnippet_Strip'; |
||
+ | </source> |
||
+ | Then the function to strip the code out of the raw wiki page, quite heavy as we've to parse the wiki page ourselves... |
||
+ | {{#rawsnippetanchor: RawSnippet.php}} |
||
+ | <source lang=php> |
||
function fnRawSnippet_Strip(&$rawPage, &$text) { |
function fnRawSnippet_Strip(&$rawPage, &$text) { |
||
+ | // if our ext wasn't used, just exit |
||
if (!isset($_GET['rawsnippet'])) |
if (!isset($_GET['rawsnippet'])) |
||
return true; |
return true; |
||
$filename=$_GET['rawsnippet']; |
$filename=$_GET['rawsnippet']; |
||
+ | // debug: |
||
+ | if (0) { |
||
header("Content-disposition:filename=".$filename); |
header("Content-disposition:filename=".$filename); |
||
header("Content-type:application/octetstream"); |
header("Content-type:application/octetstream"); |
||
⚫ | |||
header("Content-Transfer-Encoding: binary"); |
header("Content-Transfer-Encoding: binary"); |
||
+ | } else { |
||
+ | header("Content-type:text/plain"); |
||
⚫ | |||
⚫ | |||
header("Pragma: no-cache"); |
header("Pragma: no-cache"); |
||
header("Cache-Control: no-store"); |
header("Cache-Control: no-store"); |
||
// First: find the right anchor |
// First: find the right anchor |
||
// We mask nowiki sections |
// We mask nowiki sections |
||
+ | //TODO also mask source, js, css, pre, what else? |
||
⚫ | |||
+ | //test2 will contain interpretable content, all static content is blanked out |
||
⚫ | |||
create_function( |
create_function( |
||
'$matches', |
'$matches', |
||
Line 83: | Line 100: | ||
$text); |
$text); |
||
// We search rawsnippet anchor position |
// We search rawsnippet anchor position |
||
− | if (preg_match_all('/{{#rawsnippetanchor: +'.$filename.' *}}/i', $ |
+ | if (preg_match_all('/{{#rawsnippetanchor: +'.$filename.' *}}/i', $maskedtext, $matches, PREG_OFFSET_CAPTURE)) |
− | $ |
+ | $offsets=$matches[0]; |
− | else if (preg_match_all('/{{#rawsnippet: +'.$filename.' *}}/i', $ |
+ | else if (preg_match_all('/{{#rawsnippet: +'.$filename.' *}}/i', $maskedtext, $matches, PREG_OFFSET_CAPTURE)) |
+ | // If the shortcut "rawsnippet" is used, no nuweb, just the first hit is considered |
||
− | $ |
+ | $offsets=array($matches[0][0]); |
else |
else |
||
// We didn't find our anchor, let's output all the raw... |
// We didn't find our anchor, let's output all the raw... |
||
+ | // TODO change headers & send error msg |
||
return true; |
return true; |
||
+ | // free some mem |
||
⚫ | |||
− | + | unset($maskedtext); |
|
+ | $textorig=$text; |
||
− | // What's the type of tag do we have? |
||
− | $text |
+ | $text=''; |
+ | foreach ($offsets as $offset) { |
||
− | if (!preg_match('/^<([^> ]+)/', $text, $matches)) |
||
⚫ | |||
⚫ | |||
− | $ |
+ | $out = substr($textorig, $offset[1]); |
− | // |
+ | // What's the type of tag do we have? |
− | $ |
+ | $out = substr($out, strpos($out, '<')); |
− | if ( |
+ | if (!preg_match('/^<([^> ]+)/', $out, $matches)) |
+ | // TODO send error, we could not find end of bloc |
||
⚫ | |||
⚫ | |||
⚫ | |||
− | $ |
+ | $key = $matches[1]; |
+ | // Let's extract the text up to the closing tag |
||
⚫ | |||
− | $ |
+ | $begin = strpos($out, '>')+1; |
+ | if (ord(substr($out,$begin,1))==10) |
||
⚫ | |||
⚫ | |||
+ | $text .= substr($out, $begin, $matches[0][0][1]-$begin); |
||
⚫ | |||
+ | // TODO send error, we could not find end of bloc |
||
+ | $text .= substr($out, $begin); |
||
+ | } |
||
+ | |||
header("Content-Length: ".strlen($text)); |
header("Content-Length: ".strlen($text)); |
||
return true; |
return true; |
||
//TODO: downloadAs.. |
//TODO: downloadAs.. |
||
} |
} |
||
+ | </source> |
||
− | |||
+ | That's again part of the hooks... |
||
+ | {{#rawsnippetanchor: RawSnippet.php}} |
||
+ | <source lang=php> |
||
function efRawSnippet_Setup() { |
function efRawSnippet_Setup() { |
||
global $wgParser; |
global $wgParser; |
||
Line 117: | Line 148: | ||
$wgParser->setFunctionHook( 'rawsnippetanchor', 'efRawSnippet_Empty' ); |
$wgParser->setFunctionHook( 'rawsnippetanchor', 'efRawSnippet_Empty' ); |
||
} |
} |
||
+ | </source> |
||
− | |||
+ | This part as well... |
||
+ | {{#rawsnippetanchor: RawSnippet.php}} |
||
+ | <source lang=php> |
||
function efRawSnippet_Magic( &$magicWords, $langCode ) { |
function efRawSnippet_Magic( &$magicWords, $langCode ) { |
||
# Add the magic word |
# Add the magic word |
||
Line 128: | Line 162: | ||
return true; |
return true; |
||
} |
} |
||
+ | </source> |
||
− | |||
+ | The transformation rule to replace link shortcuts to actual links for download |
||
+ | {{#rawsnippetanchor: RawSnippet.php}} |
||
+ | <source lang=php> |
||
function efRawSnippet_Render( &$parser, $filename = '') { |
function efRawSnippet_Render( &$parser, $filename = '') { |
||
# The parser function itself |
# The parser function itself |
||
Line 136: | Line 173: | ||
//TODO+support for other pages |
//TODO+support for other pages |
||
} |
} |
||
+ | </source> |
||
− | function efRawSnippet_Empty( &$parser, $filename = '') { |
||
+ | Credits :-) |
||
− | return ''; |
||
+ | {{#rawsnippetanchor: RawSnippet.php}} |
||
⚫ | |||
+ | <source lang=php> |
||
− | |||
$wgExtensionCredits['parserhook'][] = array('name' => 'RawSnippet', |
$wgExtensionCredits['parserhook'][] = array('name' => 'RawSnippet', |
||
'version' => '0.1', |
'version' => '0.1', |
||
Line 146: | Line 183: | ||
'url' => 'http://wiki.yobi.be/wiki/Mediawiki_RawSnippet', |
'url' => 'http://wiki.yobi.be/wiki/Mediawiki_RawSnippet', |
||
'description' => 'Downloads a RAW copy of <nowiki><tag>data</tag></nowiki> in a file<br>'. |
'description' => 'Downloads a RAW copy of <nowiki><tag>data</tag></nowiki> in a file<br>'. |
||
− | 'Useful e.g. to download an example code or a patch' |
+ | 'Useful e.g. to download an example code or a patch<br>'. |
+ | 'It also opens the path to [http://en.wikipedia.org/wiki/Literate_programming Literate Programming]'); |
||
} |
} |
||
Revision as of 15:01, 3 April 2008
Introduction
The idea is to be able to download directly a portion of code as a file.
I've numerous code examples in my wiki and I wast an easy way to download them, easier than a copy/paste!
- It must work with pre, nowiki, js, css, code, source, so let's make it general: take the tag that comes after the parser function we'll create and select data up to the closing tag.
2 parts:
- the parser magic word that will be converted into a "Save it as <filename>"
- an extended action=raw that will strip the raw output to keep the desired code
Documentation
- http://www.mediawiki.org/wiki/Manual:Extensions
- http://www.mediawiki.org/wiki/Manual:Magic_words
- http://www.mediawiki.org/wiki/Manual:Parser_functions
- http://meta.wikimedia.org/wiki/Help:Parser_function
- http://www.mediawiki.org/wiki/Manual:Hooks/RawPageViewBeforeOutput
Syntax
{{#rawsnippet: myscript.sh}}
Do we need a MIME type?
Transformation:
{{fullurl:{{PAGENAME}}|action=raw&rawsnippet=myscript.sh}}
Test: save the following code [{{#rawsnippet: myscript.sh}} as myscript.sh]
#!/bin/bash
echo 'Hello world!'
exit 0
{{#rawsnippet: myscript.sh}}
combines in fact 2 elements: the text that will be replaced by the link and the anchor just before the code section.
We can separate both functionalities with:
One such declaration, just before the code section:
{{#rawsnippetAnchor: myscript.sh}}
One or many such declarations to create the download links:
{{#rawsnippetLink: myscript.sh}}
Example: {{#rawsnippetanchor: myotherscript.sh}}
#!/bin/bash
echo 'Hello earth!'
exit 0
[{{#rawsnippetlink: myotherscript.sh}} myotherscript.sh is available now below the code]
Hook on Raw
- Must extract the right paragraph
- Strip all up to the right
rawsnippet: filename
tag - Find the next tag
- Select up to the closure tag
- Strip all up to the right
- Must provide the filename to the browser
- Tells the browser NOT to cache the raw
The code
Which you can of course download just by following [{{#rawsnippetlink: RawSnippet.php}} this link :-)]
So let's explain a bit the code in a Literate Programming way...
First some hooks for our functions... {{#rawsnippetanchor: RawSnippet.php}}
<?php
if (defined('MEDIAWIKI')) {
# Define a setup function
$wgExtensionFunctions[] = 'efRawSnippet_Setup';
# Add a hook to initialise the magic word
$wgHooks['LanguageGetMagic'][] = 'efRawSnippet_Magic';
# Add a hook to intercept the raw output
$wgHooks['RawPageViewBeforeOutput'][] = 'fnRawSnippet_Strip';
Then the function to strip the code out of the raw wiki page, quite heavy as we've to parse the wiki page ourselves... {{#rawsnippetanchor: RawSnippet.php}}
function fnRawSnippet_Strip(&$rawPage, &$text) {
// if our ext wasn't used, just exit
if (!isset($_GET['rawsnippet']))
return true;
$filename=$_GET['rawsnippet'];
// debug:
if (0) {
header("Content-disposition:filename=".$filename);
header("Content-type:application/octetstream");
header("Content-Transfer-Encoding: binary");
} else {
header("Content-type:text/plain");
}
header("Expires: 0");
header("Pragma: no-cache");
header("Cache-Control: no-store");
// First: find the right anchor
// We mask nowiki sections
//TODO also mask source, js, css, pre, what else?
//test2 will contain interpretable content, all static content is blanked out
$maskedtext=preg_replace_callback('/<nowiki>(.*?)<\/nowiki>/',
create_function(
'$matches',
'return ereg_replace(".","X",$matches[0]);'
),
$text);
// We search rawsnippet anchor position
if (preg_match_all('/{{#rawsnippetanchor: +'.$filename.' *}}/i', $maskedtext, $matches, PREG_OFFSET_CAPTURE))
$offsets=$matches[0];
else if (preg_match_all('/{{#rawsnippet: +'.$filename.' *}}/i', $maskedtext, $matches, PREG_OFFSET_CAPTURE))
// If the shortcut "rawsnippet" is used, no nuweb, just the first hit is considered
$offsets=array($matches[0][0]);
else
// We didn't find our anchor, let's output all the raw...
// TODO change headers & send error msg
return true;
// free some mem
unset($maskedtext);
$textorig=$text;
$text='';
foreach ($offsets as $offset) {
// Now let's remove the text up to our anchor
$out = substr($textorig, $offset[1]);
// What's the type of tag do we have?
$out = substr($out, strpos($out, '<'));
if (!preg_match('/^<([^> ]+)/', $out, $matches))
// TODO send error, we could not find end of bloc
return true;
$key = $matches[1];
// Let's extract the text up to the closing tag
$begin = strpos($out, '>')+1;
if (ord(substr($out,$begin,1))==10)
$begin++;
if (preg_match_all('/<\/'.$key.'>/', $out, $matches, PREG_OFFSET_CAPTURE))
$text .= substr($out, $begin, $matches[0][0][1]-$begin);
else
// TODO send error, we could not find end of bloc
$text .= substr($out, $begin);
}
header("Content-Length: ".strlen($text));
return true;
//TODO: downloadAs..
}
That's again part of the hooks... {{#rawsnippetanchor: RawSnippet.php}}
function efRawSnippet_Setup() {
global $wgParser;
# Set a function hook associating the "rawsnippet" magic word with our function
$wgParser->setFunctionHook( 'rawsnippet', 'efRawSnippet_Render' );
$wgParser->setFunctionHook( 'rawsnippetlink', 'efRawSnippet_Render' );
$wgParser->setFunctionHook( 'rawsnippetanchor', 'efRawSnippet_Empty' );
}
This part as well... {{#rawsnippetanchor: RawSnippet.php}}
function efRawSnippet_Magic( &$magicWords, $langCode ) {
# Add the magic word
# The first array element is case sensitive, in this case it is not case sensitive
# All remaining elements are synonyms for our parser function
$magicWords['rawsnippet'] = array( 0, 'rawsnippet', 'downloadAs' );
$magicWords['rawsnippetlink'] = array( 0, 'rawsnippetlink', 'downloadLink' );
$magicWords['rawsnippetanchor'] = array( 0, 'rawsnippetanchor', 'downloadAnchor' );
# unless we return true, other parser functions extensions will not get loaded.
return true;
}
The transformation rule to replace link shortcuts to actual links for download {{#rawsnippetanchor: RawSnippet.php}}
function efRawSnippet_Render( &$parser, $filename = '') {
# The parser function itself
# The input parameters are wikitext with templates expanded
# The output should be wikitext too
return '{{fullurl:{{PAGENAME}}|action=raw&rawsnippet='.$filename.'}}';
//TODO+support for other pages
}
Credits :-) {{#rawsnippetanchor: RawSnippet.php}}
$wgExtensionCredits['parserhook'][] = array('name' => 'RawSnippet',
'version' => '0.1',
'author' => 'Philippe Teuwen',
// 'url' => 'http://www.mediawiki.org/wiki/Extension:LocalServer',
'url' => 'http://wiki.yobi.be/wiki/Mediawiki_RawSnippet',
'description' => 'Downloads a RAW copy of <nowiki><tag>data</tag></nowiki> in a file<br>'.
'Useful e.g. to download an example code or a patch<br>'.
'It also opens the path to [http://en.wikipedia.org/wiki/Literate_programming Literate Programming]');
}
?>