User:Austin J. Che/Extensions/LatexDoc

From OpenWetWare

Jump to: navigation, search

The LatexDoc extension in MediaWiki SVN has been modified as follows:

  • The biggest change is to allow use of other files on the wiki so that bibtex and graphics works.
  • Requires latexmk
    • The default location for latexmk is in the extensions directory of your mediawiki installation.
    • If you wish to use a different location, add $wgLatexDoc->latexmk = "/path/to/latexmk"; in LocalSettings.php after requiring/including LatexDoc.php.
  • Removed DVI output (not useful on web)
  • Security: Removed filter for "obviously dangerous control words" as it isn't a good security mechanism.
    • Instead, change texmf.cnf so that shell_escape=f, openout_any=p, and openin_any=p. This disallows running system commands and reading or writing any files not under the directory of the tex file.
      • texmf.cnf is located in /usr/share/texmf/web2c/texmf.cnf on tetex distributions. Debian users should edit /etc/texmf/texmf.d/95NonPath.cnf and run update-texmf as root.
    • You may also want to run latex under a chroot jail.
    • If anyone notices any other security issues, please let me know.
  • Added link to get the log file
  • If you want automatic syntax highlighting, install the SyntaxHighlight extension (and GeSHi).

Usage:

Any wiki page that looks like a latex document will automatically have links for added to the top. The Get PDF link will generate a PDF or grab it from the cache if it exists and the Regenerate PDF forces the PDF file to be regenerated.

The special command \usewikifile{Wiki Page Name}{texfilename} can be used. All such commands are stripped before being passed to latexmk for generation of pdf. In addition, the contents of Wiki Page Name are saved as texfilename in the same directory as the page. Thus, you can include graphics or use bibtex where the graphics and .bib file are stored on the wiki. Hint: If you wish to use the same file both on and off the wiki, you can put the \usewikifile in a comment or some other place where normal LaTeX won't parse it (such as after \end{document}). The extension does not care at all the context or the location of the command.

See an example.

Code

<?php
 
/*
    This extension has been modified by Austin Che (c) 2006
    See http://openwetware.org/wiki/User:Austin_J._Che/Extensions/LatexDoc for the latest documentation
 
    Changes:
    - Uses syntax highlighting if the SyntaxHighlight extension is installed
    - Uses/requires latexmk
    - Allows use of other files on wiki such as for bibtex and graphics
    - Removed DVI output (isn't very useful on web)
    - Removed filter for "obviously dangerous control words" (not a good security mechanism)
    - Add link to get log file
 
    Security: change texmf.cnf so that
      shell_escape=f
      openout_any=p
      openin_any=p
    and/or run latex under a chroot jail.
 
   Released under the GNU GPL.
 
   ----------------------------------------
 
  Original documentation:
 
INSTALLATION
------------
 
This is an extension for the collaborative editing of LaTeX documents.
Installation is by the usual method, put the following line in your
LocalSettings.php:
 
    require_once( "extensions/LatexDoc/LatexDoc.php" );
 
This creates an object called $wgLatexDoc. Member variables of that object can
be changed in order to customise the behaviour of the extension.
 
    latexCommand
        Path to the latex command
    pdflatexCommand
        Path to the pdflatex command. This must be installed, otherwise the PDF
        links won't work.
    workingDir
        Filesystem directory where all related files go. Must be in the web
        server document root. 
    workingPath
        Relative URI of workingDir
 
All of these variables have sensible defaults, as long as latex and pdflatex are
in the PATH, it should work out of the box. 
 
USE
---
 
The extension operates by searching the text of articles at render time for 
"\begin{document}". If this string is present, the article is not rendered like 
ordinary wikitext. Instead, it's displayed with a fixed-width font, with a 
"Make DVI" and a "Make PDF" link at the top. Clicking on the links will invoke 
latex, generate the requested file, and redirect the browser to it.
 
SECURITY
--------
 
Executing LaTeX documents written by untrusted users is a security risk. I've put 
in a simple filter for some obviously dangerous control words, but there may well be
holes. No guarantee is made. If you're brave enough to make a wiki with this
extension publically editable, it's recommended that you run latex from a chroot 
jail.
 
KEEP BACKUPS OF IMPORTANT DATA!
 
COPYING
-------
 
LatexDoc.php and this documentation were written by Tim Starling, (c) 2005. You 
may choose one of the following two licenses, at your option:
 
1) The GNU General Public License
 
2) You may use or copy this work for any purpose, with the sole restriction 
   that the rights of GPL licensors other than myself are not infringed. 
   Assessment of the extent of those rights is at your own risk.
 
 
*/
 
 
if ( !defined( 'MEDIAWIKI' ) ) {
    die( "Not a valid entry point\n" );
}
 
$wgExtensionFunctions[] = 'wfLatexDocInit';
$wgExtensionCredits['other'][] = array(
                                       'name' => 'LatexDoc',
                                       'version' => '2008/03/09',
                                       'author' => 'Austin Che',
                                       'url' => 'http://openwetware.org/wiki/User:Austin_J._Che/Extensions/LatexDoc',
                                       'description' => 'Write LaTeX documents on the wiki',
                                       );
 
class LatexDoc 
{
    var $latexmk;               // full path to latexmk
    var $args;                  // args to latexmk
    var $workingDir;
    var $workingPath;
 
    function LatexDoc()
    {
        global $wgUploadDirectory, $wgUploadPath, $IP;
 
        $this->latexmk = "$IP/extensions/latexmk";
        $this->args = "-f -silent -pdf";
        if ($wgUploadDirectory)
            $this->workingDir = "$wgUploadDirectory/latexdoc";
        else
            $this->workingDir = "$IP/images/latexdoc";
        if ($wgUploadPath)
            $this->workingPath = "$wgUploadPath/latexdoc";
        else
            $this->workingPath = "$wgScriptPath/images/latexdoc";
    }
 
    function onUnknownAction( $action, &$article ) {
        global $wgOut, $wgRequest, $IP;
 
        // Respond only to latexdoc action
        if ( $action != 'latexdoc' ) {
            return true;
        }
 
        // Check for non-existent article
        if ( !$article || !( $text = $article->fetchContent() ) ) {
            $wgOut->addWikiText( wfMsg( 'latexdoc_no_text' ) );
            return false;
        } 
 
        // Check permissions
        if ( !$article->mTitle->userCanRead() ) {
            $wgOut->loginToUse();
            return false;
        }
 
        $ext = $wgRequest->getText( 'ext' );
 
        $wgOut->setArticleFlag( false );
        $wgOut->setArticleRelated( true );
        $wgOut->setRobotpolicy( 'noindex,nofollow' );
        $wgOut->setPageTitle( $article->mTitle->getPrefixedText() );
 
        // Get path
        if ( !is_dir( $this->workingDir ) ) {
            if ( !mkdir( $this->workingDir, 0777 ) ) {
                $wgOut->addWikiText( wfMsg( 'latexdoc_cant_create_dir', $this->workingDir ) );
                return false;
            }
        }
 
        chdir( $this->workingDir );
 
        $hash = md5( $text );
        $filename = 'ltd_' . $hash;
        $fullpath = $this->workingDir . '/' . $filename;
        $url = $this->workingPath . '/' . $filename;
 
        if ( ! $wgRequest->getBool('cache', true) && file_exists( "$fullpath.$ext"))
            @unlink("$fullpath.$ext");
 
        if ( ! file_exists( "$fullpath.$ext" ) ) 
        {
            // need to generate/regenerate the output 
            if ($this->getHelperFiles($text) && $this->runLatex( $text, $filename ))
            {
                $wgOut->redirect( "$url.$ext" );
            }
            // else it failed
        }
        else
            $wgOut->redirect( "$url.$ext" );
 
        chdir( $IP );
        return false;
    }
 
    function getHelperFiles($text)
    {
        global $wgOut;
        // search for \usewikifile{..}
        preg_match_all("/\\\\usewikifile{(.*)}{(.*)}/", $text, $matches, PREG_SET_ORDER);
        foreach ($matches as $val)
        {
            $page = $val[1];
            $outfile = $val[2];
            if (preg_match("/[^-\w.]/", $outfile) || preg_match("/^[.]/", $outfile))
            {
                $wgOut->addWikiText(wfMsg('latexdoc_invalid_filename', $outfile));
                return false;
            }
 
            $title = Title::newFromText($page);
            if ($title)
            {
                if ($title->getNamespace() == NS_IMAGE)
                {
                    $localFile = wfLocalFile($title);
                    if (! $localFile->exists())
                    {
                        $wgOut->addWikiText(wfMsg('latexdoc_no_page', $page));
                        return false;
                    }
 
                    if (! copy ($localFile->getPath(), $outfile))
                    {
                        $wgOut->addWikiText( wfMsg( 'latexdoc_cant_write', "$outfile" ) );
                        return false;
                    }
                    continue;
                }
 
                $rev = Revision::newFromTitle($title);
            }
            if (!$title || ! $rev)
            {
                $wgOut->addWikiText(wfMsg('latexdoc_no_page', $page));
                return false;
            }
            else
            {
                $inFile = fopen( $this->workingDir . '/' . $outfile, 'w' );
                if ( !$inFile ) 
                {
                    $wgOut->addWikiText( wfMsg( 'latexdoc_cant_write', "$outfile" ) );
                    return false;
                }
                fwrite( $inFile, $rev->getText());
                fclose( $inFile );
            }
        }
        return true;
    }
 
    function runLatex( $text, $file ) 
    {
        global $wgOut, $wgRequest;
 
        // remove all \usewikifile commands
        $text = preg_replace("/\\\\usewikifile{.*}{.*}/", "", $text);
 
        // Write input file
        $inFile = fopen( "$file.tex", 'w' );
        if ( !$inFile ) {
            $wgOut->addWikiText( wfMsg( 'latexdoc_cant_write', "$file.tex" ) );
            return false;
        }
        fwrite( $inFile, $text );
        fclose( $inFile );
 
        // Run LaTeX
        $cmd = $this->latexmk . " $this->args " . wfEscapeShellArg( "$file" ) . " 2>&1";
 
        exec($cmd, $output, $error);
 
        // Report errors
        // we currently use -f with latexmk so that no exit code is returned
        // as we need to distinguish between undefined references (which we still consider success)
        // and genuine latex errors
        foreach ($output as $outputline)
        {
            if (preg_match("/^====.*error.*====$/", $outputline))
            {
                // this is an erorr
                // could have partially generated pdf, delete it (some browsers will crash)
                @unlink("$file.pdf" );
                wfSuppressWarnings();
                $log = '<pre>' . file_get_contents( "$file.log" ) . '</pre>';
                wfRestoreWarnings();
                $wgOut->addWikiText( wfMsg( 'latexdoc_error', $log ) );
                return false;
            }
        }
        return true;
 
        // Delete temporary files
        //@unlink( "$file.tex" );
        //@unlink( "$file.aux" );
        //@unlink( "$file.log" );
 
    }
 
    function onParserBeforeStrip( &$parser, &$text, &$stripState )
    {
        // If the article looks vaguely like TeX, render it with syntax highlighting (if available)
        // with a link for pdf generation
        global $wgSyntaxHighlight;
        if ( strpos( $text, '\begin{document}' ) !== false ) {
            $sk =& $parser->mOptions->getSkin();
            $links = $sk->makeKnownLinkObj( $parser->mTitle, wfMsg( 'latexdoc_get_pdf' ), 
                                            'action=latexdoc&ext=pdf' ) . " | " .
                $sk->makeKnownLinkObj( $parser->mTitle, wfMsg( 'latexdoc_get_log' ), 
                                       'action=latexdoc&ext=log' ) . " | " .
                $sk->makeKnownLinkObj( $parser->mTitle, wfMsg( 'latexdoc_get_pdf_no_cache' ), 
                                       'action=latexdoc&ext=pdf&cache=0' ) . " | " .
                $sk->makeKnownLinkObj( $parser->mTitle, wfMsg( 'latexdoc_get_source' ),
                                       'action=raw&ctype=application/x-tex&filename=wiki_latex_source.tex' );
            $links = $parser->insertStripItem($links, $stripState);
 
            // make list of included files
            preg_match_all("/\\\\usewikifile{(.*)}{(.*)}/", $text, $matches, PREG_SET_ORDER);
            $files = '<div class="latexdoc-files">';
            $files = $files . '<b>Included files:</b>';
            foreach ($matches as $val)
            {
                $files = $files . "\n*[[:" . $val[1] . "]]";                                   
            }
            $files = $files . '</div>';
 
            $text = wordwrap($text);
            if ($wgSyntaxHighlight)
                $text = $wgSyntaxHighlight->highlight($text, "latex");
            else
                $text = "<pre>$text</pre>"; // no syntax highlighting
            $text = $parser->insertStripItem($text, $stripState);
 
            $text = "$links<hr />$files<p>$text";
        }
        return true;
    }
 
    // Needed in some versions to prevent Special:Version from breaking
    function __toString() { return 'LatexDoc'; }
}
 
$wgLatexDoc = new LatexDoc;
 
function wfLatexDocInit() {
    global $wgHooks, $wgLatexDoc, $wgMessageCache;
 
    $wgHooks['UnknownAction'][] = &$wgLatexDoc;
    $wgHooks['ParserBeforeStrip'][] = &$wgLatexDoc;
 
    $wgMessageCache->addMessages( array( 
                                        'latexdoc_invalid_filename' => 'Filename $1 is invalid',
                                        'latexdoc_no_page' => 'Wiki page $1 does not exist',
                                        'latexdoc_no_text' => 'Article contains no text, cannot generate output',
                                        'latexdoc_cant_create_dir' => 'Cannot create temporary directory $1',
                                        'latexdoc_cant_write' => 'Cannot write to file $1',
                                        'latexdoc_error' => "LaTeX error:<br />\n\n$1",
                                        'latexdoc_get_pdf' => 'Get PDF',
                                        'latexdoc_get_log' => 'Get log',
                                        'latexdoc_get_pdf_no_cache' => 'Regenerate PDF',
                                        'latexdoc_get_source' => 'Export Source',
                                        ));
}
?>

Send bugs and comments to Austin Che. Other extensions including sources can be found at User:Austin J. Che/Extensions.

Personal tools