* Original Authors: Mark Lundeberg, Alexander Kraus, Michael Boyle * * @license GPL 2 */ if(!defined('DOKU_INC')) die(); require_once(__DIR__ . '/latexinc.php'); class admin_plugin_latexwas extends DokuWiki_Admin_Plugin { protected $output = ''; /** * DokuWiki Admin Menu Position */ public function getMenuSort() { return 40; } /** * DOM Generator Helper */ protected function el($tag, $attr = [], $content = '', $selfClosing = false) { $html = "<{$tag}"; foreach ($attr as $k => $v) { $html .= " {$k}=\"" . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . "\""; } if ($selfClosing) return $html . ' />'; // Allow content to be an array of elements $inner = is_array($content) ? implode('', $content) : $content; return $html . ">" . $inner . ""; } /** * Handle form submissions */ public function handle() { if (!isset($_POST['latexpurge'])) return; $mode = $_POST['purgemode']; $days = (int)$_POST['purgedays']; $timelimit = $days * 86400; global $conf; $meddir = $conf['mediadir'] . '/' . strtr($this->getConf('latex_namespace'), ':', '/'); $images = glob($meddir . '/img*'); $deleted = 0; foreach ($images as $img) { $age = time() - ($mode === 'atime' ? fileatime($img) : filemtime($img)); if ($mode === 'all' || $age > $timelimit) { if (@unlink($img)) $deleted++; } } $this->output = $this->el('div', ['class' => 'success'], "Purge Result: $deleted files deleted from cache."); // Invalidate DokuWiki cache if ($deleted > 0) { global $config_cascade; @touch($config_cascade['main']['local']); } } /** * Generate Admin Interface */ public function html() { echo $this->el('h1', [], 'LaTeX Administration'); echo $this->output; // --- SECTION 1: PURGE CACHE --- echo $this->el('h2', [], $this->getLang('legend_purge') ?: 'Purge Image Cache'); $purgeForm = $this->el('div', ['class' => 'no'], [ $this->el('p', [], [ $this->el('label', [], [ $this->el('input', ['type' => 'radio', 'name' => 'purgemode', 'value' => 'atime', 'checked' => 'checked']), ' Access Time ' ]), $this->el('label', [], [ $this->el('input', ['type' => 'radio', 'name' => 'purgemode', 'value' => 'all']), ' Delete All ' ]) ]), $this->el('p', [], [ 'Older than: ', $this->el('input', ['type' => 'text', 'name' => 'purgedays', 'value' => '30', 'size' => '3']), ' days' ]), $this->el('input', ['type' => 'submit', 'name' => 'latexpurge', 'class' => 'button', 'value' => $this->getLang('btn_purge') ?: 'Purge Now']) ]); echo $this->el('form', ['method' => 'post', 'action' => wl()], $purgeForm); // --- SECTION 2: TROUBLESHOOTER --- echo $this->el('h2', [], 'LaTeX Troubleshooter'); $diagForm = $this->el('div', ['class' => 'no'], [ $this->el('p', [], 'Use this section to test your LaTeX and ImageMagick paths.'), $this->el('textarea', [ 'name' => 'testformula', 'rows' => '3', 'cols' => '60', 'style' => 'display:block; margin-bottom:10px;' ], '$$\sum_{i=0}^n i^2 = \frac{(n^2+n)(2n+1)}{6}$$'), $this->el('input', ['type' => 'submit', 'name' => 'dotest', 'class' => 'button', 'value' => 'Run Diagnostics']) ]); echo $this->el('form', ['method' => 'get', 'action' => wl()], [ $this->el('input', ['type' => 'hidden', 'name' => 'do', 'value' => 'admin']), $this->el('input', ['type' => 'hidden', 'name' => 'page', 'value' => 'latexwas']), $diagForm ]); if (isset($_REQUEST['dotest'])) { $this->runDiagnostics(); } } /** * System diagnostic logic using DOM Generator */ protected function runDiagnostics() { $binaries = [ 'LaTeX' => $this->getConf("latex_path"), 'Dvips' => $this->getConf("dvips_path"), 'Convert' => $this->getConf("convert_path"), 'Identify' => $this->getConf("identify_path") ]; echo $this->el('h3', [], 'Binary Version Check'); $rows = []; foreach ($binaries as $name => $path) { unset($out); exec($path . " --version 2>&1", $out, $status); $isOk = ($status === 0); $msg = $isOk ? ($out[0] ?? 'Found') : 'ERROR: Binary not found or returned error code ' . $status; $rows[] = $this->el('tr', [], [ $this->el('td', ['style' => 'font-weight:bold;'], $name), $this->el('td', ['style' => 'font-family:monospace; color:' . ($isOk ? 'green' : 'red')], $msg) ]); } echo $this->el('table', ['class' => 'inline'], [ $this->el('thead', [], $this->el('tr', [], [ $this->el('th', [], 'Tool'), $this->el('th', [], 'Status / Version Output') ])), $this->el('tbody', [], $rows) ]); // Rendering Test echo $this->el('h3', [], 'Rendering Test'); $formula = $_REQUEST['testformula'] ?? 'E=mc^2'; // We use a temporary syntax object to trigger a render $plug = new syntax_plugin_latexwas_common_proxy(); $url = $plug->_latex->getFormulaURL($formula); if ($url) { echo $this->el('div', ['class' => 'success'], [ $this->el('p', [], 'Success! Rendered image:'), $this->el('img', ['src' => $url, 'alt' => 'test render', 'style' => 'background:white; padding:10px; border:1px solid #ccc;']) ]); } else { echo $this->el('div', ['class' => 'error'], [ $this->el('p', [], 'Rendering failed.'), $this->el('p', [], 'Error Code: ' . $plug->_latex->_errorcode), $this->el('pre', [], $plug->_latex->_cmdoutput) ]); } } } /** * Proxy class to access the common renderer logic */ class syntax_plugin_latexwas_common_proxy extends syntax_plugin_latexwas_common { public function getType() { return 'protected'; } public function getSort() { return 0; } public function connectTo($mode) {} public function handle($match, $state, $pos, Doku_Handler $handler) { return []; } }