1<?php 2/** 3 * Admin for LaTeX plugin. 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Mark Lundeberg <nanite@gmail.com> 7 */ 8 9if(!defined('DOKU_INC')) die(); 10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11require_once(DOKU_PLUGIN.'admin.php'); 12 13require_once(dirname(__FILE__).'/latexinc.php'); 14 15/** 16 * All DokuWiki plugins to extend the admin function 17 * need to inherit from this class 18 */ 19class admin_plugin_latex extends DokuWiki_Admin_Plugin { 20 var $output; 21 /** 22 * return some info 23 */ 24 function getInfo(){ 25 $a = ''; 26 if(method_exists(DokuWiki_Admin_Plugin,"getInfo")) { 27 $a = parent::getInfo(); /// this will grab the data from the plugin.info.txt 28 $a['name'] = 'LaTeX plugin administration'; 29 return $a; 30 } else 31 // Otherwise return some hardcoded data for old dokuwikis 32 return array( 33 'author' => 'Alexander Kraus, Michael Boyle, and Mark Lundeberg)', 34 'email' => '.', 35 'date' => '???', 36 'name' => 'LaTeX plugin', 37 'desc' => 'LaTeX rendering plugin; requires LaTeX, dvips, ImageMagick.', 38 'url' => 'http://www.dokuwiki.org/plugin:latex' 39 ); 40 } 41 42 /** 43 * return sort order for position in admin menu 44 */ 45 function getMenuSort() { 46 return 999; 47 } 48 49 // Purgers. 50 function vio_atime($fname) { 51 if(time() - fileatime($fname) - $this->_timelimit > 0) 52 { 53 unlink($fname); 54 return $this->_timelimit; 55 } 56 return false; 57 } 58 function vio_mtime($fname) { 59 if(time() - filemtime($fname) - $this->_timelimit > 0) 60 { 61 unlink($fname); 62 return true; 63 } 64 return false; 65 } 66 function vio_all($fname) { 67 unlink($fname); 68 return true; 69 } 70 71 72 // purge all files older than $timelimit (in seconds) 73 // $mode = 74 // atime: age based on fileatime(). 75 // mtime: age based on filemtime(). 76 // all: delete all cached files. 77 function latexpurge($mode, $timelimit) 78 { 79 global $conf, $config_cascade; 80 $meddir = $conf['mediadir'] . '/' . strtr($this->getConf('latex_namespace'),':','/'); 81 $images = glob($meddir.'/img*'); 82 $this->_timelimit = $timelimit; 83 switch($mode) { 84 case 'atime': 85 $vio = array_map(array($this,'vio_atime'),$images); 86 break; 87 case 'mtime': 88 $vio = array_map(array($this,'vio_mtime'),$images); 89 break; 90 case 'all': 91 $vio = array_map(array($this,'vio_all'),$images); 92 break; 93 default: 94 return false; 95 } 96 return array_combine($images,$vio); 97 } 98 99 /** 100 * handle user request 101 */ 102 function handle() { 103 global $conf, $config_cascade; 104 $this->output = ""; 105 if(isset($_POST['latexpurge'])) 106 { 107 $mode = $_POST['purgemode']; 108 $days = $_POST['purgedays']; 109 if(is_numeric($days) && $days == 0) 110 $mode = 'all'; 111 if($mode == 'all') { 112 // If the admin wants to delete all of the images, probably it's good to print this reminder 113 // since they are likely doing it after changing the colour or something. 114 // (I don't know how many hours I spent trying to fix LaTeX heisenbugs that were just cached... grr.) 115 $this->output .= '<div class="info">'.$this->getLang('refresh_note').'</div>'; 116 } 117 $numdeleted = 0; 118 $numkept = 0; 119 $this->output .= "<pre>Purge result ([x] = deleted):\n"; 120 if($mode == 'all' || (is_numeric($days) && $days >= 0)) { 121 $res = $this->latexpurge($mode, $days*86400); 122 foreach($res as $img => $vio){ 123 if($vio) { 124 $this->output .= '[x] '.$img . "\n"; 125 $numdeleted += 1; 126 } else { 127 // $this->output .= '[ ] '.$img . "\n"; 128 $numkept += 1; 129 } 130 } 131 } else { 132 $this->output = "<div class=\"error\">Purger: Bad form inputs. No action taken.</div>".$this->output; 133 } 134 $this->output .= "Totals: $numdeleted deleted, $numkept kept (kept files not shown).\n"; 135 if ($numdeleted > 0) { 136 touch($config_cascade['main']['local']); 137 } 138 $this->output .= "</pre>"; 139 } 140 } 141 142 143 /** 144 * output appropriate html 145 */ 146 function html() { 147 global $ID,$INFO; 148 ptln('<p>'.$this->output.'</p>'); 149 ptln('<h1>LaTeX plugin administrator tasks</h1>'); 150 ptln('<h2>'.$this->getLang('legend_purge').'</h2>'); 151 ptln('<div class="level2">'); 152 153 ////////////// PURGE FORM 154 ptln('<form action="'.wl($INFO['id']).'?do=admin&page='.$this->getPluginName().'" method="post">'); 155 ptln('<table class="inline"><tr>'); 156 ptln('<td rowspan="2"><input type="submit" class="button" name="latexpurge" value="'.$this->getLang('btn_purge').'" /></td>'); 157 ptln('<TD>'); 158 $labtimes = $this->getLang('label_times'); 159 ptln('(<LABEL><INPUT type="radio" name="purgemode" value="atime" checked />'.$labtimes['atime'].'</LABEL>'); 160 ptln(' | <LABEL><INPUT type="radio" name="purgemode" value="mtime" />'.$labtimes['mtime'].'</LABEL>)'); 161 echo $this->getLang('label_olderthan'); 162 echo '<input type="text" name="purgedays" size="3" value="30">'; 163 echo $this->getLang('label_days'); 164 ptln('</TD><TR><TD>'); 165 echo '<LABEL><INPUT type="radio" name="purgemode" value="all" />'.$this->getLang('label_all').'</LABEL>'; 166 ptln('</TD></TR></TABLE>'); 167 ptln('</form>'); 168 169 ptln('</div>'); 170 171 /////////////// DIAGNOSER 172 ptln('<h2>LaTeX troubleshooter</h2>'); 173 ptln('<div class="level2">'); 174 ptln('<form action="'.wl($INFO['id']).'" method="get">'); 175 ptln(' <input type="hidden" name="do" value="admin" />'); 176 ptln(' <input type="hidden" name="page" value="'.$this->getPluginName().'" />'); 177 ptln('Push this button to diagnose your LaTeX/ImageMagick installation: <input type="submit" class="button" name="dotest" value="Test" /><br/>'); 178 ptln('<input type="checkbox" name="keep_tmp">Check this button to keep the temporary files used during compilation.</input><br/>'); 179 ptln('The following latex code will be inserted into the template and compiled:'); 180 ptln('<br />'); 181 if(isset($_REQUEST['testformula'])) 182 $testformula = $_REQUEST['testformula']; 183 else 184 $testformula = '$$\underbrace{{\it f}({\rm DokuWiki}) = \overbrace{[a+b=c]}^\textrm{\LaTeX}}_{Success!}$$'; 185 ptln(' <textarea cols=70 rows=6 type="text" name="testformula">'.htmlspecialchars($testformula).'</textarea>'); 186 ptln('</form>'); 187 ptln('</div>'); 188 if($_REQUEST['dotest']) { 189 ptln('<h3>Versions</h3>'); 190 ptln('<div class="level3">'); 191 ptln('This is a test of the acessibility of your programs and their versions.'); 192 ptln('<table class="inline">'); 193 ptln('<tr><th>command</th><th>output</th></tr>'); 194 foreach(array($this->getConf("latex_path"),$this->getConf("dvips_path"), 195 $this->getConf("convert_path"),$this->getConf("identify_path")) as $path) { 196 ptln('<tr><td><pre>'); 197 $cmd = $path." --version 2>&1"; 198 echo htmlspecialchars($cmd); 199 ptln('</pre></td><td>'); 200 unset($execout); 201 exec($cmd,$execout,$statuscode); 202 if($statuscode == 0) 203 echo '<pre>'; 204 else 205 echo '<pre style="background-color:#FCC;">'; //pink for error status 206 echo htmlspecialchars(implode(PHP_EOL,$execout)); 207 ptln('</pre></td></tr>'); 208 } 209 ptln('</table>'); 210 ptln('</div>'); 211 212 ptln('<h3>Test run</h3>'); 213 $plug = new syntax_plugin_latex_common(); 214 215 /// Directory sanity checks 216 if(is_writable($plug->_latex->getPicturePath()) && is_dir($plug->_latex->getPicturePath())) 217 ptln('<div class="success">Media directory is writable: <code>'.$plug->_latex->getPicturePath().'</code></div>'); 218 else 219 ptln('<div class="error">Media directory not writable or nonexistant! <code>'.$plug->_latex->getPicturePath().'</code> 220 <br />Recommendation: This media namespace must be writable on the file system.</div>'); 221 if(is_writable($plug->_latex->_tmp_dir) && is_dir($plug->_latex->_tmp_dir)) 222 ptln('<div class="success">Temporary directory is writable: <code>'.$plug->_latex->_tmp_dir.'</code></div>'); 223 else 224 ptln('<div class="error">Temporary directory not writable or nonexistant! <code>'.$plug->_latex->_tmp_dir.'</code> 225 <br />Recommendation: This media namespace must be writable on the file system.</div>'); 226 227 // simulate a call to the syntax plugin; force render, keep temp files. 228 $md5 = md5($testformula); 229 $outname = $plug->_latex->getPicturePath()."/img".$md5.'.'.$plug->_latex->_image_format; 230 if(file_exists($outname)) { 231 if(unlink($outname)) 232 ptln('<div class="info">Removed cache file for test: <code>'.$outname.'</code><br/> 233 <b>WARNING: You may need to refresh your browser\'s cache to see changes in the resulting image.</b></div>'); 234 else 235 ptln('<div class="error">Could not remove cached file for test! <code>'.$outname.'</code><br /> 236 the following tests will not work (renderer will just reuse the cached file)</div>'); 237 } 238 ptln('<div class="info">Attempting to render to target <code>'.$outname.'</code></div>'); 239 $plug->_latex->_keep_tmp = true; 240 $plug->_latex->_cmdoutput = ''; // activate command log. 241 $data = array($testformula,DOKU_LEXER_UNMATCHED,'class'=>"latex_inline", 'title'=>"Math", NULL); 242 $this->doc = ''; 243 $xhtml = new Doku_Renderer_xhtml(); 244 $plug->render('xhtml', $xhtml, $data); 245 $tmpw = $this->getConf('latex_namespace').':tmp:'.$plug->_latex->_tmp_filename; 246 $tmpf = $plug->_latex->_tmp_dir."/".$plug->_latex->_tmp_filename; 247 $tmpext = array('tex','log','aux','dvi','ps',$plug->_latex->_image_format); 248 foreach($tmpext as $ext) { 249 $fname = $tmpf.'.'.$ext; 250 if(is_file($fname)) { 251 if(isset($_REQUEST['keep_tmp'])) { 252 $rendstr = $plug->render('xhtml', $xhtml, '{{'.$tmpw.'.'.$ext.'?linkonly&nocache|'.$fname.'}}'); 253 $rendstr = preg_replace('/<\\/?p>/','',$rendstr); 254 } else 255 $rendstr = $fname; 256 ptln('<div class="success">File created: <code>'.$rendstr.'</code></div>'); 257 } else 258 ptln('<div class="error">File missing! <code>'.$fname.'</code></div>'); 259 } 260 if(! isset($_REQUEST['keep_tmp'])) 261 ptln('<div class="info">These files <code>'.$tmpf.'.*</code> will be deleted at the end of this script.</div>'); 262 if(is_file($outname)) 263 ptln('<div class="success">Successfully moved to media: <code>'.$outname.'</code></div>'); 264 else 265 ptln('<div class="error">File missing from media! <code>'.$outname.'</code></div>'); 266 267 ptln('<div class="level3">'); 268 ptln('<table class="inline"><tr><th>Input LaTeX file</th><th>Final result</th></tr>'); 269 ptln('<tr><td><pre>'); 270 if(is_readable($tmpf.'.tex') && is_file($tmpf.'.tex')) 271 echo htmlspecialchars(file_get_contents($tmpf.'.tex')); 272 else 273 echo 'MISSING'; 274 ptln('</pre></td><td>'); 275// ptln(htmlspecialchars($plug->_url)); 276// ptln('<br /><br />'); 277 ptln('<center>'); 278 ptln($this->doc); 279 ptln('</center></td></tr>'); 280 ptln('</table>'); 281 282 ptln('Command log:'); 283 echo '<pre>'; 284 echo $plug->_latex->_cmdoutput; 285 echo '</pre>'; 286 287 ptln('Contents of '.$tmpf.'.log:'); 288 echo '<pre>'; 289 echo htmlspecialchars(file_get_contents($tmpf.'.log')); 290 echo '</pre>'; 291 292 if(! isset($_REQUEST['keep_tmp'])) 293 $plug->_latex->cleanTemporaryDirectory(); 294 ptln('</div>'); 295 } 296 } 297}