<?php /** * @license http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.html * @author Francois Merciol <dokuplugin@merciol.fr> * * Plugin Glossary: manage forms for glossary */ if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); /* Sort glossary by definition */ function cmpGlossary ($a, $b) { if ($a['word'] == $b['word']) return 0; return ($a['word'] < $b['word']) ? -1 : 1; } class glossary { // ============================================================ // Config attributs // ============================================================ var $cacheRootDir; // root cache directory var $cacheDir; // cache directory for specific namespace var $dataRootDir; // root data directory var $dataDir; // data directory for specific namespace for xml file var $recentDays; // time during the clock is display to indicate a nex definition var $maxIP; // max proposition per guest user var $propGroup; // user group without limits of proposition var $adminGroup; // admin group who validate proposition var $transSep; // line separator for translate if not empty // ============================================================ // Constant attributs // ============================================================ var $configFile = "config.xml"; // file config name var $prop = "prop-"; // prefix for proposal var $def = "def-"; // prefix for definition // XXXX var $poll = "poll-"; // prefix for polling (evaluation per definition) var $form = "form-"; // subset used in form var $hide = "hide-"; // subset not used in form var $statusFields = // fields managed per step (status) array ("prop-" => array ("date", "ip", "email", "ns", "ticket", "word", "translate", "why"), "def-" => array ("date", "ip", "email", "ns", "ticket", "useTicket", "word", "translate"), "poll-" => array ("word", "view", "up", "down"), "form-" => array ("ticket", "useTicket", "word", "translate", "why"), "hide-" => array ("date", "ip", "email", "ns")); var $oddEven = // for row color array ("odd", "even"); var $imgDir; // ============================================================ // Transcient attributs // ============================================================ var $message = array (); // "notify" =>, "info" =>, "success" =>, "error" => var $plugin; // link to wiki plugin information (conf, lang, ...) var $NS; // namespace where definition could be found var $lastNotification; // time of last proposal notification var $lastNotificationReset; // time of last administrators acknowledgement var $md5ns; // the ns directory var $md5id; // the wiki id var $sampleFile; // cache for sample var $listFile; // cache for sample // ============================================================ function createDirIsNeeded ($dir) { if (is_dir ($dir)) return; @mkdir ($dir); @chmod ($dir, 0775); } function __construct ($plugin, $ns) { $this->plugin = $plugin; $this->imgDir = DOKU_REL.'lib/plugins/glossary/images/'; global $conf; $savedir = ((!$conf['savedir'] || strpos ($conf['savedir'], '.') === 0) ? DOKU_INC : "").$conf['savedir']."/"; $this->cacheRootDir = $savedir."cache/glossary/"; $this->dataRootDir = $savedir.trim ($this->plugin->getConf ('dataDir').'/'); glossary::createDirIsNeeded ($this->cacheRootDir); glossary::createDirIsNeeded ($this->dataRootDir); $this->NS = cleanId (trim ($ns)); $this->md5ns = md5 ($this->NS); $this->cacheDir = $this->cacheRootDir.$this->md5ns."/"; glossary::createDirIsNeeded ($this->cacheDir); $this->dataDir = $this->dataRootDir.$this->md5ns."/"; glossary::createDirIsNeeded ($this->dataDir); $this->sampleFile = $this->cacheDir."sample.cache"; $this->listFile = $this->cacheDir."list.cache"; $this->recentDays = $this->getConf ('recentDays'); $this->maxIP = $this->getConf ('maxIP'); $this->adminGroup = trim ($this->getConf ('adminGroup')); $this->propGroup = trim ($this->getConf ('propGroup')); $this->transSep = trim ($this->getConf ('transSep')); } function getConf ($name) { return $this->plugin->getConf ($name); } function getLang ($name) { return $this->plugin->getLang ($name); } // function isAdmin ($name) { // return $this->plugin->isAdmin ($name); // } function localFN ($name) { return $this->plugin->localFN ($name); } /* messages container to be display before plugin */ function message ($type, $text) { if (isset ($this->message[$type])) $this->message[$type] .= '<br/>'; $this->message[$type] .= $text; } /* debug messages for admin only */ function debug ($text) { global $INFO; if (in_array ('admin', $INFO ['userinfo'] ['grps'])) $this->message ('notify', '<pre>'.$text.'</pre>'); } /* get next parity for row color */ function nextOddEven (&$even) { $result = $this->oddEven [$even]; $even = 1 - $even; return $result; } // ============================================================ // Control rights // ============================================================ /* true if the user has the admin rights */ function testAdminGroup () { global $INFO; return isset ($INFO ['userinfo']['grps']) && in_array ($this->adminGroup, $INFO ['userinfo']['grps']); } /* true if the user has no proposition limits */ function testPropGroup () { global $INFO; return isset ($INFO ['userinfo']['grps']) && in_array ($this->propGroup, $INFO ['userinfo']['grps']); } /* true if limit occured (i.e. problems => can't continued)*/ function maxIP (&$request) { $ip = $request['ip']; $count = 1; $all = $this->readAllGlossary ($this->prop); if (isset ($all[$this->md5id])) { $this->message ('success', $this->getLang ('update')); return false; } if ($this->testPropGroup ()) return false; foreach ($all as $record) if ($record['ip'] == $ip) $count++; if ($count <= $this->maxIP) { if ($count == $this->maxIP) $this->message ('notify', $this->getLang ('lastIP')); return false; } $this->message ('error', $this->getLang ('maxIP')); return true; } // ============================================================ // Control fields // ============================================================ function testNotEmpty () { return $this->wordOk () || $this->translateOk () || $this->translateOk () || $this->ticketOk (); } function wordOk () { return isset ($_REQUEST ['glossary']['word']) && trim ($_REQUEST ['glossary']['word']) != ""; } function translateOk () { return isset ($_REQUEST ['glossary']['translate']) && trim ($_REQUEST ['glossary']['translate']) != ""; } function ticketOk () { return isset ($_REQUEST ['glossary']['ticket']) && trim ($_REQUEST ['glossary']['ticket']) != ""; } // ============================================================ // Manage XML file // ============================================================ /* read lodging config */ function readConfig ($dir) { $fileName = $dir.$this->configFile; if (!file_exists ($fileName)) return false; $xml = new DOMDocument ("1.0", "utf8"); $xml->load ($fileName); $root = $xml->documentElement; $this->lastNotification = $root->getElementsByTagName ("lastNotification")->item (0)->nodeValue; $this->lastNotificationReset = $root->getElementsByTagName ("lastNotificationReset")->item (0)->nodeValue; return $root->getElementsByTagName ("nameSpace")->item (0)->nodeValue; } /* write lodging config */ function writeConfig () { if ($this->NS == false) return; $fileName = $this->dataDir.$this->configFile; @mkdir ($this->dataDir); @chmod ($this->dataDir, 0775); $xml = new DOMDocument ("1.0", "utf8"); $root = $xml->createElement ("glossary"); $xml->appendChild ($root); $root->appendChild ($xml->createElement ("nameSpace", htmlspecialchars ($this->NS))); $root->appendChild ($xml->createElement ("lastNotification", htmlspecialchars ($this->lastNotification))); $root->appendChild ($xml->createElement ("lastNotificationReset", htmlspecialchars ($this->lastNotificationReset))); $xml->formatOutput = true; $xml->save ($fileName); chmod ($fileName, 0664); } /* read all propositions, definitions, polls */ function readAllGlossary ($status) { if (!is_dir ($this->dataDir)) return; if ($this->NS == false) return; $result = array (); $exclude_array = explode ("|", ".|..|".$this->configFile); $pathDirObj = opendir ($this->dataDir); while (false !== ($file = readdir ($pathDirObj))) { if (in_array (strtolower ($file), $exclude_array) || !preg_match ('#'.$status.'(.*)\.xml$#i', $file, $regs)) continue; $result[$regs[1]] = $this->readGlossary ($regs[1], $status, LOCK_SH); } uasort ($result, "cmpGlossary"); return $result; } /* read one proposition, definition, poll */ function readGlossary ($md5id, $status, $lock) { $fileName = $this->dataDir.$status.$md5id.".xml"; if (!file_exists ($fileName)) return false; $lock = LOCK_SH; if ($lock != LOCK_SH) { $fp = fopen ($fileName, "r+"); if (!flock ($fp, $lock)) { $this->message ("error", "can't lock file ".$fileName."."); return; } } $xml = new DOMDocument ("1.0", "utf8"); $xml->load ($fileName); $root = $xml->documentElement; $result = array (); foreach ($this->statusFields [$status] as $field) $result [$field] = $root->getElementsByTagName ($field)->item (0)->nodeValue; return $result; } /* write one proposition, definition, poll */ function writeGlossary ($md5id, &$values, $status) { if (! isset ($values['ns']) || $values['ns'] == "") $values['ns'] = $this->NS; // XXX compatibilité $xml = new DOMDocument ("1.0", "utf8"); $root = $xml->createElement ("glossary"); $xml->appendChild ($root); foreach ($this->statusFields [$status] as $field) $root->appendChild ($xml->createElement ($field, htmlspecialchars ($values [$field]))); $xml->formatOutput = true; $fileName = $this->dataDir.$status.$md5id.".xml"; $xml->save ($fileName); chmod ($fileName, 0664); //flock (fopen ($fileName, "r+"), LOCK_UN); } /* remove one proposition, definition, poll */ function removeGlossary ($md5id, $status) { $fileName = $this->dataDir.$status.$md5id.".xml"; @unlink ($fileName); } /* count file propositions, definitions, polls */ function getGlosarySize ($status, $md5ns) { $subDir = $this->dataRootDir.$md5ns."/"; if (!is_dir ($subDir)) return 0; $result = 0; $exclude_array = explode ("|", ".|..|".$this->configFile); $pathDirObj = opendir ($subDir); while (false !== ($file = readdir ($pathDirObj))) { if (in_array (strtolower ($file), $exclude_array) || !eregi ($status.'(.*)\.xml$', $file, $regs)) continue; $result++; } return $result; } // ============================================================ // Actions to performed // ============================================================ /* update proposition, definition */ function updateRequest (&$request, $status) { $reread = false; $update = false; $this->md5id = md5 (trim ($request['ticket'])); $oldValues = $this->readGlossary ($this->md5id, $status, LOCK_SH); if ($oldValues) { foreach ($this->statusFields [$this->form] as $field) { if ((!isset ($request[$field]) || $request[$field] == "") && $request[$field] != $oldValues [$field]) { $request[$field] = $oldValues [$field]; $reread = true; } elseif (isset ($request[$field]) && $request[$field] != "" && $request[$field] != $oldValues [$field]) { $update = true; } } foreach ($this->statusFields [$this->hide] as $field) $request[$field] = $oldValues [$field]; if ($reread) $this->message ('success', $this->getLang ('readData')); return $update; } return true; } /* display poll */ function printPoll ($arg) { echo '<div class="glossary toolTip">'.NL; $this->printPollAjax (md5 (trim ($arg))); echo '</div>'; } function printPollAjax ($md5id) { $filename = $this->cacheDir."poll-".md5($md5id).".cache"; if (file_exists ($filename)) { echo file_get_contents ($filename); return; } $poll = $this->readGlossary ($md5id, $this->poll, LOCK_SH); list ($scoreVal, $scoreImg) = $this->getScore ($poll); $text = '<a onClick="glossaryPoll(this,\''.$md5id.'\',\'down\',\''.$this->NS.'\');">'.NL. ' <img src="'.$this->imgDir.'face-sad.png" /><span>'.$this->getLang ('tipPollDown').'</span>'.NL. '</a>'. $scoreImg.NL. '<a onClick="glossaryPoll(this,\''.$md5id.'\',\'up\',\''.$this->NS.'\');">'.NL. ' <img src="'.$this->imgDir.'face-smile.png" /><span>'.$this->getLang ('tipPollUp').'</span>'.NL. '</a>'; if (!$poll|| ($poll['up']+$poll['down'] < 1)) $text .= '<br/><b>'.$this->getLang ('notPolledYet').'</b>'; file_put_contents ($filename, $text); echo $text; } function getScore ($poll) { $scoreImg = '<img src="'.$this->imgDir.'score'; if ($poll && $poll["up"] + $poll["down"] > 0) { $scoreVal = (($poll["up"] - $poll["down"]) / (max (5, $poll["up"] + $poll["down"]))); $scoreImg .= '-'.intval (round (($scoreVal+1)*3)); } else $scoreVal = 0; $scoreImg .= '.png"/>'; return array ($scoreVal, $scoreImg); } /* update poll */ function poll () { $request = &$_REQUEST ['glossary']; $md5id = $request['ticket']; $opinion = $request['opinion']; $all = $this->readAllGlossary ($this->def); if (! isset ($all[$md5id]) || !in_array ($opinion, $this->statusFields [$this->poll])) { $this->message ('error', $this->getLang ('noDef')); return; } $poll = $this->readGlossary ($md5id, $this->poll, LOCK_EX); if (!$poll) $poll = array ("word" => $all[$md5id]['word'], "up" => 0, "down" => 0); if ($poll [$opinion] != PHP_INT_MAX) $poll [$opinion]++; $this->writeGlossary ($md5id, $poll, $this->poll); $this->message ('success', $this->getLang ('writePoll')); @unlink ($this->cacheDir."poll-".md5($md5id).".cache"); $this->printPollAjax ($md5id); } function incView ($md5id, $word) { $poll = $this->readGlossary ($md5id, $this->poll, LOCK_EX); if (!$poll) $poll = array ("word" => $word, "view" => 0, "up" => 0, "down" => 0); $poll ["view"]++; $this->writeGlossary ($md5id, $poll, $this->poll); return $poll; } function clearCache ($nsMd5) { $exclude = ".|.."; $exclude_array = explode("|", $exclude); $allNsMd5 = array (); if ($nsMd5) $allNsMd5[] = $nsMd5; else { $pathDirObj = opendir ($cacheRootDir); while (false !== ($file = readdir ($pathDirObj))) { if (in_array (strtolower ($file), $exclude_array)) continue; $pathFile = $pathDir.$file; if (!is_dir ($pathFile)) continue; $allNsMd5[] = $file; } } foreach ($allNsMd5 as $nsMd5) { $cacheDir = $this->cacheRootDir.$nsMd5."/"; if (!is_dir ($cacheDir)) break; $pathDirObj = opendir ($cacheDir); while (false !== ($file = readdir ($pathDirObj))) { if (in_array (strtolower ($file), $exclude_array)) continue; $pathFile = $cacheDir.$file; if (!is_file ($pathFile)) continue; if (eregi ('.*\.cache$', $file, $b)) unlink ($pathFile); } @rmdir ($cacheDir); } } // ============================================================ function manageRecord (&$request, $status, $needCaptcha) { if (!$this->testNotEmpty ()) return; if ($this->ticketOk ()) { if (!$this->updateRequest ($request, $status)) return; } else { $request ['ticket'] = substr (md5 (date ('YmdHis')), 0, 12); $this->md5id = md5 (trim ($request ['ticket'])); } if ($needCaptcha && (!$captcha =& plugin_load ('helper', 'captcha') || !$captcha->check ())) { $this->message ('error', $this->getLang ('badCaptcha')); return; } if (!$this->wordOk ()) { $this->message ('error', $this->getLang ('wordMandatory')); return; } if (!$this->translateOk ()) { $this->message ('error', $this->getLang ('translateMandatory')); return; } if (! isset ($request['date']) || $request['date'] == "") { $request['date'] = date ('YmdHis'); $request['ip'] = $_SERVER ['REMOTE_ADDR']; global $INFO; if (isset ($INFO['userinfo']['mail'])) $request['email'] = $INFO['userinfo']['mail']; $request['ns'] = $this->NS; } unset ($request['operation']); sleep (1); if ($status == $this->prop && $this->maxIP ($request)) return; $this->writeGlossary ($this->md5id, $request, $status); $this->message ('success', $this->getLang ('proposalRecorded').'<b>'.$request['ticket']."</b>."); $this->adminNotification ($request, $status); } // ============================================================ // Result for display // ============================================================ function getDefinitionSize () { return $this->getGlosarySize ($this->def, $this->md5ns); } function getProposalSize () { return $this->getGlosarySize ($this->prop, $this->md5ns); } function getPollSize () { return $this->getGlosarySize ($this->poll, $this->md5ns); } function printProposal () { global $INFO; $needCaptcha = !(isset ($INFO ['userinfo']) && isset ($INFO ['userinfo']['grps']) && $INFO ['userinfo']['grps']); $request = &$_REQUEST ['glossary']; if ($request['operation'] == "record") $this->manageRecord ($request, $this->prop, $needCaptcha); echo '<form method="post" action="" onsubmit="return glossaryAjax(this);">'.NL. ' <table>'.NL. ' <tr class="title">'.NL. ' <th><img src="'.$this->imgDir.'stop.png" /> '.$this->getLang ('word').'</th>'.NL. ' <th><img src="'.$this->imgDir.'one-way.png" /> '.$this->getLang ('translate').'</th>'.NL. ' </tr><tr class="odd">'.NL. ' <td>'.NL. ' * <input type="text" name="glossary[word]" value="'.$request['word'].'" class="text" />'.NL. ' </td><td>'.NL. ' * <input type="text" name="glossary[translate]" value="'.$request['translate'].'" class="text" />'.NL. ' </td>'.NL. ' <tr class="title">'.NL. ' <th>'.$this->getLang ('why').'</th>'.NL. ' <th>'.NL. ' <input type="hidden" name="glossary[operation]" value="record">'.NL. ' <input type="hidden" name="glossary[ns]" value="'.$this->NS.'">'.NL. ' <input type="submit" name="glossary[action]" value="'.$this->getLang (empty ($request['ticket']) ? 'proposal' : 'update').'" />'.NL. ' <input type="reset" onClick="glossaryReset(this);glossaryUpdateProposalLabel(this.form)" value="'.$this->getLang ('new').'" />'.NL. ' </th>'.NL. ' </tr><tr class="odd">'.NL. ' <td colspan="2"><textarea name="glossary[why]" class="why" />'.$request['why'].'</textarea></td>'.NL. ' </tr><tr class="even">'.NL. ' <td class="right">'.NL. ' '.$this->getLang ('ticketIfUpdate').NL. ' </td><td>'.NL. ' <input type="text" name="glossary[ticket]" value="'.$request['ticket'].'" class="text" onChange="glossaryUpdateProposalLabel(this.form)" onKeypress="glossaryUpdateProposalLabel(this.form)" />'.NL. ' </td>'.NL. ' </tr>'.NL; if ($needCaptcha && $captcha =& plugin_load ('helper', 'captcha')) echo '<tr><td colspan=2>'.$captcha->getHTML ().'</td></tr>'.NL; echo ' </table>'.NL; '</form>'; } // ============================================================ function printSample () { if (file_exists ($this->sampleFile) && (time () - filemtime ($this->sampleFile) < $this->getConf ('sampleDelai'))) { echo file_get_contents ($this->sampleFile); return; } $all = $this->readAllGlossary ($this->def); $keys = array_keys ($all); $rand = array_rand ($keys); $record = $all [$keys [$rand]]; $imgDir = DOKU_REL.'lib/plugins/glossary/images/'; foreach (explode ($this->transSep, $record['word']) as $word) foreach (explode ($this->transSep, $record['translate']) as $translate) { $word = trim ($word); $translate = trim ($translate); $pageId = trim ($record['useTicket']); if (!$pageId) $pageId = trim ($record['ticket']); resolve_pageid ($this->NS, $pageId, $exists); $cleanId = cleanId ($pageId); $link = '<a class="wikilink1" href="'.DOKU_REL.$cleanId.'">'; $text = '<div>'.$this->getLang ('word').'<br/><img src="'.$imgDir.'stop.png" align="left"/> <span class="glossaryWord"> '.$link.$word.' </a></span></div>'.NL. '<div>'.$this->getLang ('translate').'<br/><img src="'.$imgDir.'one-way.png" align="left"/> <span class="glossaryTranslate"> '.$link.$translate.' </a></span></div>'.NL; file_put_contents ($this->sampleFile, $text); echo $text; return; } } // ============================================================ function clearListFile () { @unlink ($this->listFile); } function printList () { if ($this->testAdminGroup ()) { echo '<div><form>'; foreach (array ('clear', 'clearAll') as $action) echo '<input value="'.$this->getLang ($action).'" onclick="javascript:glossarySendClear (\''.$action.'\', \''.$this->NS.'\')" type="button">'; echo '</form></div>'; } if (file_exists ($this->listFile) && (time () - filemtime ($this->listFile) < $this->getConf ('listDelai'))) { echo file_get_contents ($this->listFile); return; } $all = $this->readAllGlossary ($this->def); $poll = $this->readAllGlossary ($this->poll); $text = '<table>'.NL. ' <tr class="title">'.NL. ' <th></th>'.NL. ' <th class="toolTip">'.NL. ' <a onClick="glossarySort(this,glossaryComparatorWord);"><span>'.$this->getLang ('tipWord').'</span><img src="'.$this->imgDir.'stop.png" /></a>'.NL. ' <a onClick="glossarySort(this,glossaryComparatorTranslate);"><span>'.$this->getLang ('tipTranslate').'</span><img src="'.$this->imgDir.'one-way.png" /></a>'.NL. ' <a onClick="glossarySort(this,glossaryComparatorDate);"><span>'.$this->getLang ('tipDate').'</span><img src="'.$this->imgDir.'clock.png" /></a>'.NL. ' <a onClick="glossarySort(this,glossaryComparatorView);"><span>'.$this->getLang ('tipView').'</span><img src="'.$this->imgDir.'eye.png" /></a>'.NL. ' <a onClick="glossarySort(this,glossaryComparatorScore);"><span>'.$this->getLang ('tipScore').'</span><img src="'.$this->imgDir.'score-all.png" width="32" /></a>'.NL. ' </th>'.NL. ' <th>'.$this->getLang ('why').'</th>'.NL. ' <th><img src="'.$this->imgDir.'stop.png" /> '.$this->getLang ('word').'</th>'.NL. ' <th colspan="2" class="toolTip" style="white-space: nowrap;">'.NL. ' <form onsubmit="javascript:glossarySearch(this.elements[0]);return false;">'.NL. ' <span>'.$this->getLang ('tipSearch').'</span><img src="'.$this->imgDir.'search.png" />'.NL. ' <input type="text" name="glossary[search]" value="" class="search" keyup="glossarySearch(this);" onChange="glossarySearch(this);" />'.NL. ' </form>'.NL. ' </th>'.NL. ' <th><img src="'.$this->imgDir.'one-way.png" /> '.$this->getLang ('translate').'</th>'.NL. ' <th>'.$this->getLang ('poll').'</th>'.NL. ' </tr>'; $even = 0; $recentTime = mktime (0, 0, 0, date ("n"), date ("j")-$this->recentDays, date ("Y")); $recent = date ("YmdHis", $recentTime); global $ID; // XXX bug si empty ($this->transSep) $canInc = true; foreach ($all as $md5id => $record) foreach (explode ($this->transSep, $record['word']) as $word) foreach (explode ($this->transSep, $record['translate']) as $translate) { $word = trim ($word); $translate = trim ($translate); $pageId = trim ($record['useTicket']); if (!$pageId) $pageId = trim ($record['ticket']); resolve_pageid ($this->NS, $pageId, $exists); $style = false; $imgClock = ''; if ($record['date'] > $recent) { $opacity = 1; if (preg_match ("#(?<Y>[0-9]{4})(?<m>[0-9]{1,2})(?<d>[0-9]{1,2})#", $record['date'], $dt_date)) { $delta = (mktime (0, 0, 0, $dt_date["m"], $dt_date["d"], $dt_date["Y"]) - $recentTime) / 86400; $opacity = ($delta)/($this->recentDays+1); } $imgClock = '<img src="'.$this->imgDir.'clock.png" style="opacity:'.$opacity.';" />'; } $cleanId = cleanId ($pageId); if ($canInc && $ID == $cleanId) { $poll [$md5id] = $this->incView ($md5id, $word); $canInc = false; } $link = '<a href="'.DOKU_REL.$cleanId.'">'; list ($scoreVal, $scoreImg) = $this->getScore ($poll [$md5id]); $text .= '<tr class="'.$this->nextOddEven ($even).'"'.NL. ' word="'.$word.'" translate="'.$translate.'"'.NL. ' date="'.$record['date'].'" view="'.$poll [$md5id]['view'].'" score="'.$scoreVal.'">'.NL. ' <td class="count"></td>'.NL. ' <td>'.$imgClock.'</td>'.NL. ' <td class="why toolTip">'.$link.'<img src="'.$this->imgDir.'help.png"/><span>'.$this->getLang ('tipWhy').'</span></a></td>'.NL. ' <td class="word" colspan="2">'.$link.$word.'</a></td>'.NL. ' <td class="translate" colspan="2">'.$link.$translate.'</a></td>'.NL. ' <td class="poll toolTip">'.$link.'<span>'.$this->getLang ('tipPoll').'</span>'.$scoreImg.'</a></td>'.NL. ' </tr>'; } $text .= NL. '</table>'; file_put_contents ($this->listFile, $text); echo $text; } // ============================================================ function manageUpdate (&$request) { if (!$this->testAdminGroup ()) return; $this->manageRecord ($request, $this->def, false); } function manageRemove (&$request) { if (!$this->testAdminGroup ()) return; foreach (array ($this->prop, $this->def) as $status) { if (empty ($request[$status.'tickets'])) continue; foreach ($request[$status.'tickets'] as $ticket) { $this->removeGlossary (md5 (trim ($ticket)), $status); $this->message ('info', $ticket." ".$this->getLang ('ticketDeleted')); if ($status == $this->def) { $pageId = $ticket; resolve_pageid ($this->NS, $pageId, $exists); if ($exists) { saveWikiText ($pageId, "", "wizard remove"); $this->message ('info', $pageId." ".$this->getLang ('pageDeleted')); } } } } } // ============================================================ function printManagedList ($status) { $all = $this->readAllGlossary ($status); echo '<form method="post" action="" onsubmit="return glossaryAjax(this);">'.NL. ' <table>'.NL. ' <tr class="title">'.NL. ' <th></th>'.NL; foreach (array_diff ($this->statusFields [$status], array ("ns")) as $field) echo ' <th>'.$this->getLang ($field).'</th>'.NL; echo ' </tr>'; $even = 0; foreach ($all as $record) { echo '<tr class="'.$this->nextOddEven ($even).'">'.NL. ' <td><input type="checkbox" name="glossary['.$status.'tickets][]" value="'.$record['ticket'].'"/></td>'.NL. ' <td>'.substr ($record['date'],0, 8).'</td>'.NL. ' <td>'.$record['ip'].'</td>'.NL. ' <td>'.$record['email'].'</td>'.NL. ' <td>'.$record['ticket'].'</td>'.NL; if ($status == $this->def) echo ' <td>'.$record['useTicket'].'</td>'.NL; echo ' <td>'.$record['word'].'</td>'.NL. ' <td>'.$record['translate'].'</td>'.NL; if ($status == $this->prop) echo ' <td>'.str_replace ("\n", "<br/>\n", $record['why']).'</td>'.NL; echo ' </tr>'; } echo '<tr>'.NL. ' <td colspan="2">'.NL. ' <input type="hidden" name="glossary[operation]" value="'.$status.'remove">'.NL. ' <input type="hidden" name="glossary[ns]" value="'.$this->NS.'">'.NL. ' <input type="submit" name="glossary[action]" value="'.$this->getLang ('remove').'"/></td>'.NL. ' <td colspan="6"></td>'.NL. ' </tr>'.NL. ' </table>'.NL. ' </form>'.NL; } // ============================================================ function printManagedForm (&$request) { echo '<form method="post" action="" onsubmit="return glossaryAjax(this);">'.NL. ' <table>'.NL. ' <tr class="title">'.NL. ' <th></th>'.NL. ' <th><img src="'.$this->imgDir.'stop.png" /> '.$this->getLang ('word').'</th>'.NL. ' <th><img src="'.$this->imgDir.'one-way.png" /> '.$this->getLang ('translate').'</th>'.NL. ' </tr><tr class="'.$this->oddEven [0].'">'.NL. ' <td><input type="reset" onClick="glossaryReset(this)" value="'.$this->getLang ('new').'" /></td>'.NL. ' <td><input type="text" name="glossary[word]" value="'.$request['word'].'" class="text" /></td>'.NL. ' <td><input type="text" name="glossary[translate]" value="'.$request['translate'].'" class="text" /></td>'.NL. ' </tr><tr class="title">'.NL. ' <th></th>'.NL. ' <th>'.$this->getLang ('ticket').'</th>'.NL. ' <th>'.$this->getLang ('useTicket').'</th>'.NL. ' </tr><tr class="'.$this->oddEven [0].'">'.NL. ' <td>'.NL. ' <input type="hidden" name="glossary[operation]" value="'.$this->def.'update">'.NL. ' <input type="hidden" name="glossary[ns]" value="'.$this->NS.'">'.NL. ' <input type="submit" name="glossary[action]" value="'.$this->getLang ('update').'"/></td>'.NL. ' <td><input type="text" name="glossary[ticket]" value="'.$request['ticket'].'" class="text" /></td>'.NL. ' <td><input type="text" name="glossary[useTicket]" value="'.$request['useTicket'].'" class="text" /></td>'.NL. ' </tr>'.NL. ' </table>'.NL. '</form>'.NL; } // ============================================================ function createPage (&$request) { if (!$this->ticketOk ()) return; $pageId = trim ($request['useTicket']); if (!$pageId) $pageId = trim ($request['ticket']); $this->md5id = md5 ($pageId); $values = $this->readGlossary ($this->md5id, $this->def, LOCK_SH); if (!$values) return; resolve_pageid ($this->NS, $pageId, $exists); if ($exists) return; $pageTpl = io_readfile ($this->localFN ('pageTemplate')); $wordTpl = io_readfile ($this->localFN ('wordTemplate')); $translateTpl = io_readfile ($this->localFN ('translateTemplate')); $wordList = ''; foreach (explode ($this->transSep, $values['word']) as $word) $wordList .= str_replace ("@@WORD@@", $word, $wordTpl); $translateList = ''; foreach (explode ($this->transSep, $values['word']) as $word) $translateList .= str_replace ("@@TRANSLATE@@", $word, $translateTpl); $replace = array ('@@WORD@@' => $wordList, '@@TRANSLATE@@' => $translateList, '@@POL@@' => $values['ticket'], '@@PROPOSITIONPAGE@@' => $this->getConf ('propositionPage')); $content = str_replace (array_keys ($replace), array_values ($replace), $pageTpl); saveWikiText ($pageId, $content, "wizard creation"); $this->message ('success', $this->getLang ('createPage')); } // ============================================================ function glossariesRemove (&$request) { if (!$this->testAdminGroup ()) return; if (empty ($request['dir'])) return; foreach ($request['dir'] as $md5ns) { $subDir = $this->dataRootDir.$md5ns.'/'; $ns = $this->readConfig ($subDir); if ($this->getGlosarySize ($this->def, $md5ns)+$this->getGlosarySize ($this->prop, $md5ns) > 0) $this->message ('error', $this->getLang ('glossaryNotEmpty').$ns." (".$md5ns.")."); else { if (!is_dir ($subDir)) continue; $pathDirObj = opendir ($subDir); while (false !== ($file = readdir ($pathDirObj))) { if (!eregi ($this->poll.'(.*)\.xml$', $file, $regs)) continue; @unlink ($subDir.$file); } @unlink ($subDir.$this->configFile); @rmdir ($subDir); $this->message ('success', $this->getLang ('glossaryRemoved').$ns." (".$md5ns.")."); } } } function printGlossariesList () { if (!is_dir ($this->dataRootDir)) return; $list = array (); $exclude_array = explode ("|", ".|.."); $pathDirObj = opendir ($this->dataRootDir); while (false !== ($file = readdir ($pathDirObj))) { $subDir = $this->dataRootDir.$file.'/'; if (in_array (strtolower ($file), $exclude_array) || !is_dir ($subDir)) continue; $ns = $this->readConfig ($subDir); $list [$file] = array ($ns, $this->getGlosarySize ($this->def, $file), $this->getGlosarySize ($this->prop, $file), $this->getGlosarySize ($this->poll, $file)); } $even = 0; echo '<form method="post" action="" onsubmit="return glossaryAjax(this);">'.NL. ' <table>'.NL. ' <tr class="title">'.NL. ' <th></th><th>directory</th><th>nameSpace</th><th>definition</th><th>proposal</th><th>poll</th>'.NL. ' </tr>'; foreach ($list as $md5ns => $data) { list ($ns, $def, $prop, $poll) = $data; echo '<tr class="'.$this->nextOddEven ($even).'">'.NL. ' <td><input type="checkbox" name="glossary[dir][]" value="'.$md5ns.'"/></td> '.NL. ' <td>'.$md5ns.'</td>'.NL. ' <td>'.$ns.'</td>'.NL. ' <td>'.$def.'</td>'.NL. ' <td>'.$prop.'</td>'.NL. ' <td>'.$poll.'</td>'.NL. ' </tr>'; } echo '<tr class="'.$this->nextOddEven ($even).'">'.NL. ' <td colspan="2">'.NL. ' <input type="hidden" name="glossary[operation]" value="glos-remove">'.NL. ' <input type="submit" name="glossary[action]" value="'.$this->getLang ('remove').'"/></td>'.NL. ' <td colspan="4"></td>'.NL. ' </tr>'.NL. ' </table>'.NL. '</form>'; } // ============================================================ function adminProposal () { if (!$this->testAdminGroup ()) return; $request = &$_REQUEST ['glossary']; if ($request['operation'] == $this->prop."remove") $this->manageRemove ($request); $this->printManagedList ($this->prop); $this->resetAdminNotification (); } function adminDefinition () { if (!$this->testAdminGroup ()) return; $request = &$_REQUEST ['glossary']; if ($request['operation'] == $this->def."remove") $this->manageRemove ($request); if ($request['operation'] == $this->def."update") { $this->manageUpdate ($request); $this->createPage ($request); } $this->clearListFile (); $this->printManagedForm ($request); $this->printManagedList ($this->def); } function adminGlossaries () { if (!$this->testAdminGroup ()) return; $request = &$_REQUEST ['glossary']; if ($request['operation'] == "glos-remove") $this->glossariesRemove ($request); $this->printGlossariesList (); } // ============================================================ function resetAdminNotification () { $subDir = $this->dataRootDir.$this->md5ns.'/'; $this->readConfig ($subDir); $this->lastNotificationReset = date ('YmdHis'); $this->writeConfig (); } function adminNotification ($request, $status) { $this->readConfig ($this->dataRootDir.$this->md5ns.'/'); if ($this->lastNotification <= $this->lastNotificationReset) { $this->lastNotification = date ('YmdHis'); global $auth; $users = $auth->retrieveUsers (); foreach ($users as $user => $userinfo) { $mailSubject = $this->getLang ('notifySubject'); $mailContent = $this->getLang ('notifyContent'); $mailContent = str_replace ('@WORD@', $request['word'], $mailContent); $mailContent = str_replace ('@TRANSLATE@', $request['translate'], $mailContent); if (in_array ($this->adminGroup, $userinfo ['grps'])) { $mailTo = $userinfo['mail']; mail_send ($mailTo, $mailSubject, $mailContent); // XXX $this->debug (print_r (array ($mailTo, $mailSubject, $mailContent), 1)); } } $this->writeConfig (); } } // ============================================================ }