Extension:RawCodeFile

From MediaWiki.org
Jump to navigation Jump to search
MediaWiki extensions manual
Crystal Clear action run.svg
RawCodeFile
Release status: experimental
Implementation Hook, Parser extension
Description Allows to syntax highlighting and download link for embed sources codes
Author(s) Philippe Teuwen, Michael Peeters, Paul Grinberg, Tuxun (Tuxuntalk)
Latest version 0.1a (2015/01/03)
MediaWiki 1.25+
PHP 5.3+
Database changes No
License WTFPL 2.0
Download external wiki page
Hooks used
ParserFirstCallInit
RawPageViewBeforeOutput
Translate the RawCodeFile extension if it is available at translatewiki.net
Check usage and version matrix.

The RawCodeFile extension allows to embed a source code in a downloadable form, between two <code></code> tags. It also should permit literate_programming, as it's a mashup of Extension:Code and Extension:RawFile

Installation[edit]

Extension:SyntaxHighlight is required for this extension to work.
  • Go to [1] and copy the code in to a file called "RawCodeFile.php". and place the file(s) in a directory called RawCodeFile in your extensions/ folder.
  • Add the following code at the bottom of your LocalSettings.php:
    require_once "$IP/extensions/RawCodeFile/RawCodeFile.php";
    
  • Yes Done – Navigate to Special:Version on your wiki to verify that the extension is successfully installed.

bugs[edit]

BUG: probably allows php remote injection!!!

code[edit]

<?php
if(!defined('MEDIAWIKI')) {
    echo("This is an extension to the MediaWiki package and cannot be run standalone.\n");
    die(-1);
}
else {
    //Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980
    if(defined('MW_SUPPORTS_PARSERFIRSTCALLINIT')) {
        $wgHooks['ParserFirstCallInit'][]='efRawFile_Setup';
        $wgHooks['ParserFirstCallInit'][]='efCodeExtensionInit';
    }
    else {
        // Otherwise do things the old fashioned way
        $wgExtensionFunctions[]='efRawFile_Setup';
        $wgExtensionFunctions[]='efCodeExtensionInit';
    }
    $wgHooks['LanguageGetMagic'][]='efRawFile_Magic';
    $wgHooks['RawPageViewBeforeOutput'][]='fnRawFile_Strip';
    $wgExtensionCredits['parserhook'][]=array('name'=>'RawCodeFile','path'=>__FILE__,'author'=>'Philippe Teuwen, Michael Peeters, Paul Grinberg, Tuxun','version'=>'0.1a','url'=>'http://www.mediawiki.org/wiki/Extension:RawCodeFile','description'=>'Allows syntax highlighting using GeSHi<br>'.'Downloads a RAW copy of <tag>data</tag> in a file<br>'.'Useful e.g. to download a script or a patch<br>'.'It also allows what is called [http://en.wikipedia.org/wiki/Literate_programming Literate Programming]');
    function efRawFile_Setup() {
        global $wgParser;
        $wgParser->setFunctionHook('file','efRawFile_Render');
        $wgParser->setFunctionHook('filelink','efRawFile_Render');
        $wgParser->setFunctionHook('fileanchor','efRawFile_Empty');
        $wgParser->setHook('file','efRawFile_FileTagRender');
        $wgParser->setFunctionHook('code','efRawFile_Render');
        return true;
    }
    function efRawFile_Magic(&$magicWords,$langCode) {
        $magicWords['file']=array(0,'file');
        $magicWords['code']=array(0,'code');
        $magicWords['filelink']=array(0,'filelink');
        $magicWords['fileanchor']=array(0,'fileanchor');
        return true;
    }
    function efRawFile_Render(&$parser,$filename='',$titleText='') {
        if($titleText=='') 
            $title=$parser->mTitle;
        else 
            $title=Title::newFromText($titleText);
        //Don't expand templates or we'll lose our anchors {{#...}}
        return $title->getFullURL('action=raw&anchor='.urlencode($filename));
    }
    function efRawFile_Empty(&$parser,$filename='') {
        return '';
    }
    function efRawFile_FileTagRender($input,$args,$parser,$frame='') {
        $title=$parser->mTitle;
        if(isset($args['title'])) {
            if($args['title']!='') {
                $title=Title::newFromText($parser->recursiveTagParse($args['name'],$frame));
                //$title = $args['title'];
            }
        }
        //cf l304
        //$title = Title::newFromText($parser->recursiveTagParse( $args['name'], $frame ));
        //We expand templates, so <file> tag cannot be mixed with {{#fileanchor}} anchors
        $link=$title->getFullURL('action=raw&templates=expand');
        if($args['name']!='') 
            $link.='&name='.urlencode($parser->recursiveTagParse($args['name'],$frame));
        if($args['anchor']!='') 
            $link.='&anchor='.urlencode($parser->recursiveTagParse($args['anchor'],$frame));
        if($args['tag']!='') 
            $link.='&tag='.urlencode($parser->recursiveTagParse($args['tag'],$frame));
        return $parser->recursiveTagParse("[$link ".$args['name']."]",$frame);
    }
    function fnRawFile_Strip_Error($msg,$out,&$text) {
        $text=$msg;
        if($out!='') 
            $text.="\nCandidate match: $out";
        return true;
    }
    function fnRawFile_Strip(&$rawPage,&$text) {
        $filename=$_GET['name'];
        $anchor=$_GET['anchor'];
        // for backward compatibility, accept also URLs with parameter 'file'
        if($anchor=='') 
            $anchor=$_GET['file'];
        $tag=$_GET['tag'];
        // Either anchor or name must be specified
        if($filename=='') 
            $filename=$anchor;
        if($filename=='') 
            return true;
        // Uncomment the following line to avoid output buffering and gzipping:
        // wfResetOutputBuffers();
        header("Content-disposition: attachment;filename={$filename}");
        header("Content-type: application/octet-stream");
        header("Content-Transfer-Encoding: binary");
        header("Expires: 0");
        header("Pragma: no-cache");
        header("Cache-Control: no-store");
        $maskedtext=preg_replace_callback('!.*?!s',function($m) {
            return ereg_replace(".","X",$m[0]);
        },$text);
        //echo $maskedtext;
        if(($anchor!='')&&preg_match_all('/({{#fileanchor: *'.$anchor.' *}})|(<[^>]+ class *= *"([^"]*\w)?'.$anchor.'(\w[^"]*)?"[^>]*>)/i',$maskedtext,$matches,PREG_OFFSET_CAPTURE)) 
            $offsets=$matches[0];
        elseif(preg_match_all('/{{#file: *'.$anchor.' *}}/i',$maskedtext,$matches,PREG_OFFSET_CAPTURE)) 
            $offsets=array($matches[0][0]);
        elseif(preg_match_all('/<file( [^>]*)? name *= *"'.$filename.'"[^>]*>/i',$maskedtext,$matches,PREG_OFFSET_CAPTURE)) 
            $offsets=array($matches[0][0]);
        elseif(preg_match_all('/<code( [^>]*)? name *= *"'.$filename.'"[^>]*>/i',$maskedtext,$matches,PREG_OFFSET_CAPTURE)) 
            $offsets=array($matches[0][0]);
        elseif(preg_match_all('/<code( [^>]*)? name *="'.$filename,$maskedtext,$matches,PREG_OFFSET_CAPTURE)) {
            $offsets=array($matches[0][0]);
        }
        else {
            //var_dump($matches);
            // We didn't find our anchor
            return fnRawFile_Strip_Error("FIRST ERROR - RawFile: anchor not found (anchor=$anchor, name=$filename, tag=$tag)","",$text);
        }
        unset($maskedtext);
        $textorig=$text;
        $text='';
        foreach($offsets as $offset) {
            $out=substr($textorig,$offset[1]);
            // If no tag specified, we take the first one
            if($tag=='') {
                // With a regex assertion, we can easily ignore 'br' and 'file' tags
                if(!preg_match('/<((?!br\b|code\b)\w+\b)/',$out,$matches)) 
                    return fnRawFile_Strip_Error("ERROR - RawFile: Can't find opening tag after anchor '$offset[0]' (anchor=$anchor, name=$filename, tag=$tag)",$out,$text);
                $tag=$matches[1];
            }
            // Find the first tag matching $tag, and return enclosed text
            if(!preg_match('/<'.$tag.'( [^>]*)?>\n?(.*?)<\/'.$tag.'>/s',$out,$matches)) 
                return fnRawFile_Strip_Error("ERROR - RawFile: no closing '$tag' found after anchor '$offset[0]' (anchor=$anchor, name=$filename, tag=$tag)",$out,$text);
            $text.=$matches[2];
        }
        return true;
    }
    //end rawfile
    //stattr code
    function efCodeExtensionInit(Parser&$parser) {
        $parser->setHook("Code","efCodeExtensionRenderCode");
        return true;
    }
    function efCodeExtensionRenderCode($input,$argv,$parser) {
	// Vérifie que la classe existe avant de l'utiliser
	if (!class_exists('SyntaxHighlight_GeSHi')) {
            echo("[RawCodeFile]:This extension need SyntaxHighlight_GeSHi.\n");
            die(-1);
        }
        $filename="";
        global $wgShowHideDivi,$wgOut;
        // default values
        $language='text';
        $showLineNumbers=false;
        $showDownloadLink=false;
        $source=$input;
        $tabwidth=4;
        foreach($argv as $key=>$value) {
            switch($key) {
                case 'lang':
                $language=$value;
                break;
                case 'linenumbers':
                $showLineNumbers=true;
                break;
                case 'tabwidth':
                $tabwidth=$value;
                break;
                case 'name':
                $filename=$value;
                case 'download':
                $showDownloadLink=true;
                break;
                case 'fileurl':
                $html=$parser->unstrip($parser->recursiveTagParse($value),$parser->mStripState);
                $i=preg_match('/<a.*?>(.*?)<\/a>/',$html,$matches);
                $url=$matches[1];
                //print("URL is '$url'");
                #$source = "file_get_contents disabled! Contact your wiki admin with questions.";
                $source=file_get_contents($url);
                break;
                default:
                wfDebug(__METHOD__.": Requested '$key ==> $value'\n");
                break;
            }
        }
	
        $output='';
        $status=SyntaxHighlight_GeSHi::highlight($source,$language,$argv);
        if(!$status->isOK()) {
            return true;
        }
        $out=$status->getValue();
        if($showDownloadLink) {
            $output.=efRawFile_FileTagRender($output,array('tag'=>"code",'name'=>$filename,'anchor'=>$filename),$parser);
        }
        $output.='<div dir="ltr">'.$out.'</div>';
        return $output;
    }
}
?>


Usage with line numbers[edit]

coloration+dl link[edit]

<code lang="cpp" name="file1.php" line>
#include<stdio.h>
    printf("Hello World");</code>

test source[edit]

<source lang="cpp" name="file7.ino" line>
#include<stdio.h>
    printf("Hello World");
</source>

coloration+dl link 2nd file[edit]

<code lang="cpp" name="file2.php" line>
#include<stdio.h>
    printf("Hello World");
</code>

no color + dl link[edit]

<code name="file3.ino" line>
#include<stdio.h>
    printf("Hello World");
</code>

color+no dl link[edit]

<code lang="cpp" line>
#include<stdio.h>
    printf("Hello World");
</code>

parameters[edit]

please only use double quotes as this: parametername="parametervalue".

Single quotes WILL NOT WORK AS EXPECTED!

  • lang="cpp","java","perl","html4" etc
  • name
  • line

History[edit]

When I tested Mw1.25a, I searched how to get downloads link on embeded codes, to simply share some snippet of it like Arduino sketch, or shell scripts.

I originally found Extension:Code which did syntax highlighting between two "code" tags, and Extension:RawFile which allowed to create the download link on two "file" tags.

Extension:Code was (and still is) using Geshi 1.0.8 to do syntax highlighting, and RawFile used "MagicWord" system to parse add the link.

See also[edit]