From a6f6a84a8ad2c33318f2c4824600866a3f6dc51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Thu, 12 Oct 2023 19:53:50 +0200 Subject: [PATCH] First --- README.md | 4 + ajax.php | 56 ++ conf/default.php | 17 + conf/metadata.php | 18 + glossary.class.php | 938 ++++++++++++++++++++++++++++++++++ images/clock.png | Bin 0 -> 1004 bytes images/eye.png | Bin 0 -> 666 bytes images/face-sad.png | Bin 0 -> 802 bytes images/face-smile.png | Bin 0 -> 806 bytes images/help.png | Bin 0 -> 897 bytes images/new.gif | Bin 0 -> 1175 bytes images/one-way.png | Bin 0 -> 1028 bytes images/score-0.png | Bin 0 -> 1299 bytes images/score-1.png | Bin 0 -> 1233 bytes images/score-2.png | Bin 0 -> 1088 bytes images/score-3.png | Bin 0 -> 772 bytes images/score-4.png | Bin 0 -> 1083 bytes images/score-5.png | Bin 0 -> 1247 bytes images/score-6.png | Bin 0 -> 1296 bytes images/score-all.png | Bin 0 -> 1089 bytes images/score.png | Bin 0 -> 772 bytes images/score.xcf | Bin 0 -> 7356 bytes images/search.png | Bin 0 -> 783 bytes images/stop.png | Bin 0 -> 872 bytes lang/en/lang.php | 67 +++ lang/en/pageTemplate.txt | 11 + lang/en/settings.php | 19 + lang/en/translateTemplate.txt | 1 + lang/en/wordTemplate.txt | 1 + lang/fr/lang.php | 67 +++ lang/fr/pageTemplate.txt | 11 + lang/fr/settings.php | 19 + lang/fr/translateTemplate.txt | 1 + lang/fr/wordTemplate.txt | 1 + plugin.info.txt | 7 + script.js | 227 ++++++++ style.css | 150 ++++++ syntax/div.php | 119 +++++ syntax/span.php | 131 +++++ 39 files changed, 1865 insertions(+) create mode 100644 ajax.php create mode 100644 conf/default.php create mode 100644 conf/metadata.php create mode 100644 glossary.class.php create mode 100644 images/clock.png create mode 100644 images/eye.png create mode 100644 images/face-sad.png create mode 100644 images/face-smile.png create mode 100644 images/help.png create mode 100644 images/new.gif create mode 100644 images/one-way.png create mode 100644 images/score-0.png create mode 100644 images/score-1.png create mode 100644 images/score-2.png create mode 100644 images/score-3.png create mode 100644 images/score-4.png create mode 100644 images/score-5.png create mode 100644 images/score-6.png create mode 100644 images/score-all.png create mode 100644 images/score.png create mode 100644 images/score.xcf create mode 100644 images/search.png create mode 100644 images/stop.png create mode 100644 lang/en/lang.php create mode 100644 lang/en/pageTemplate.txt create mode 100644 lang/en/settings.php create mode 100644 lang/en/translateTemplate.txt create mode 100644 lang/en/wordTemplate.txt create mode 100644 lang/fr/lang.php create mode 100644 lang/fr/pageTemplate.txt create mode 100644 lang/fr/settings.php create mode 100644 lang/fr/translateTemplate.txt create mode 100644 lang/fr/wordTemplate.txt create mode 100644 plugin.info.txt create mode 100644 script.js create mode 100644 style.css create mode 100644 syntax/div.php create mode 100644 syntax/span.php diff --git a/README.md b/README.md index c31ca70..6ac19ca 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,6 @@ # glossary +DokuWiki Extensions : https://www.dokuwiki.org/plugin:glossary + +In this case, French “Newspeak” expression are translate in the real world. + diff --git a/ajax.php b/ajax.php new file mode 100644 index 0000000..4d15ada --- /dev/null +++ b/ajax.php @@ -0,0 +1,56 @@ + + * + * Plugin Glossary + */ +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); +require_once (DOKU_INC.'inc/init.php'); +require_once (DOKU_INC.'inc/common.php'); +require_once (DOKU_INC.'inc/auth.php'); +if (isset ($_SERVER['REMOTE_USER'])) + $INFO['userinfo'] = $auth->getUserData ($auth->cleanUser ($_SERVER['REMOTE_USER'])); +{ + $glossaryPlugin =& plugin_load ('syntax', 'glossary_div'); + $glossary = new glossary ($glossaryPlugin, $_REQUEST ['glossary']['ns']); + + $cacheCmd = true; + switch ($_REQUEST ['glossary']['action']) { + case 'clear': + $glossary->clearCache (md5 ($_REQUEST ['glossary']['ns'])); + break; + case 'clearAll': + $glossary->clearCache (null); + break; + default: + $cacheCmd = false; + } + if (!$cacheCmd) { + ob_start (); + switch ($_REQUEST ['glossary']['operation']) { + case 'poll': + $glossary->poll (); + break; + case 'record': + $glossary->printProposal (); + break; + case $glossary->prop.'remove': + $glossary->adminProposal (); + break; + case $glossary->def.'remove': + case $glossary->def.'update': + $glossary->adminDefinition (); + case 'glos-remove': + $glossary->adminGlossaries (); + break; + } + $text = ob_get_contents (); + ob_end_clean (); + foreach ($glossary->message as $type => $msg) + $text = '
'.$msg.'
'.$text; + ptln ($text); + } +} +?> diff --git a/conf/default.php b/conf/default.php new file mode 100644 index 0000000..baffd6a --- /dev/null +++ b/conf/default.php @@ -0,0 +1,17 @@ + + * + * Options for the Glossary Plugin + */ +$conf['dataDir'] = 'glossary'; +$conf['recentDays'] = 7; +$conf['maxIP'] = 5; +$conf['adminGroup'] = 'admin'; +$conf['propGroup'] = 'glossary'; +$conf['transSep'] = ';'; +$conf['propositionPage'] = '[[.:proposition|proposition]]'; +$conf['sampleDelai'] = 2*60*60; +$conf['listDelai'] = 2*60*60; +?> diff --git a/conf/metadata.php b/conf/metadata.php new file mode 100644 index 0000000..9871d28 --- /dev/null +++ b/conf/metadata.php @@ -0,0 +1,18 @@ + + * + * Metadata for configuration manager plugin + * Additions for the Glossary plugin + */ +$meta['dataDir'] = array('string'); +$meta['recentDays'] = array('numeric'); +$meta['maxIP'] = array('numeric'); +$meta['adminGroup'] = array('string'); +$meta['propGroup'] = array('string'); +$meta['transSep'] = array('string'); +$meta['propositionPage'] = array('string'); +$meta['sampleDelai'] = array('numeric'); +$meta['listDelai'] = array('numeric'); +?> diff --git a/glossary.class.php b/glossary.class.php new file mode 100644 index 0000000..f93d314 --- /dev/null +++ b/glossary.class.php @@ -0,0 +1,938 @@ + + * + * 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] .= '
'; + $this->message[$type] .= $text; + } + /* debug messages for admin only */ + function debug ($text) { + global $INFO; + if (in_array ('admin', $INFO ['userinfo'] ['grps'])) + $this->message ('notify', '
'.$text.'
'); + } + /* 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 + '
'.NL; + $this->printPollAjax (md5 (trim ($arg))); + echo '
'; + } + + 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 = + ''.NL. + ' '.$this->getLang ('tipPollDown').''.NL. + ''. + $scoreImg.NL. + ''.NL. + ' '.$this->getLang ('tipPollUp').''.NL. + ''; + if (!$poll|| ($poll['up']+$poll['down'] < 1)) + $text .= + '
'.$this->getLang ('notPolledYet').''; + file_put_contents ($filename, $text); + echo $text; + } + + function getScore ($poll) { + $scoreImg = ' 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').''.$request['ticket']."."); + $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 + '
'.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL; + if ($needCaptcha && $captcha =& plugin_load ('helper', 'captcha')) + echo ''.NL; + echo + '
'.$this->getLang ('word').' '.$this->getLang ('translate').'
'.NL. + ' * '.NL. + ' '.NL. + ' * '.NL. + '
'.$this->getLang ('why').''.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + '
'.NL. + ' '.$this->getLang ('ticketIfUpdate').NL. + ' '.NL. + ' '.NL. + '
'.$captcha->getHTML ().'
'.NL; + '
'; + } + + // ============================================================ + 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 = ''; + $text = + '
'.$this->getLang ('word').'
'.$link.$word.'
'.NL. + '
'.$this->getLang ('translate').'
'.$link.$translate.'
'.NL; + file_put_contents ($this->sampleFile, $text); + echo $text; + return; + } + } + + // ============================================================ + function clearListFile () { + @unlink ($this->listFile); + } + + function printList () { + if ($this->testAdminGroup ()) { + echo '
'; + foreach (array ('clear', 'clearAll') as $action) + echo ''; + echo '
'; + } + 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 = + ''.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '; + $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 ("#(?[0-9]{4})(?[0-9]{1,2})(?[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 = ''; + } + $cleanId = cleanId ($pageId); + if ($canInc && $ID == $cleanId) { + $poll [$md5id] = $this->incView ($md5id, $word); + $canInc = false; + } + $link = ''; + list ($scoreVal, $scoreImg) = $this->getScore ($poll [$md5id]); + $text .= + ''.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '; + } + $text .= + NL. + '
'.NL. + ' '.$this->getLang ('tipWord').''.NL. + ' '.$this->getLang ('tipTranslate').''.NL. + ' '.$this->getLang ('tipDate').''.NL. + ' '.$this->getLang ('tipView').''.NL. + ' '.$this->getLang ('tipScore').''.NL. + ' '.$this->getLang ('why').' '.$this->getLang ('word').''.NL. + '
'.NL. + ' '.$this->getLang ('tipSearch').''.NL. + ' '.NL. + '
'.NL. + '
'.$this->getLang ('translate').''.$this->getLang ('poll').'
'.$imgClock.''.$link.''.$this->getLang ('tipWhy').''.$link.$word.''.$link.$translate.''.$link.''.$this->getLang ('tipPoll').''.$scoreImg.'
'; + 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 + '
'.NL. + ' '.NL. + ' '.NL. + ' '.NL; + foreach (array_diff ($this->statusFields [$status], array ("ns")) as $field) + echo + ' '.NL; + echo + ' '; + $even = 0; + foreach ($all as $record) { + echo + ''.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL; + if ($status == $this->def) + echo + ' '.NL; + echo + ' '.NL. + ' '.NL; + if ($status == $this->prop) + echo + ' '.NL; + echo + ' '; + } + echo + ''.NL. + ' '.NL. + ' '.NL. + ' '.NL. + '
'.$this->getLang ($field).'
'.substr ($record['date'],0, 8).''.$record['ip'].''.$record['email'].''.$record['ticket'].''.$record['useTicket'].''.$record['word'].''.$record['translate'].''.str_replace ("\n", "
\n", $record['why']).'
'.NL. + ' '.NL. + ' '.NL. + '
'.NL. + '
'.NL; + } + + // ============================================================ + function printManagedForm (&$request) { + echo + '
'.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + '
'.$this->getLang ('word').' '.$this->getLang ('translate').'
'.$this->getLang ('ticket').''.$this->getLang ('useTicket').'
'.NL. + ' '.NL. + ' '.NL. + '
'.NL. + '
'.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 + '
'.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '; + foreach ($list as $md5ns => $data) { + list ($ns, $def, $prop, $poll) = $data; + echo + ''.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '; + } + echo + ''.NL. + ' '.NL. + ' '.NL. + ' '.NL. + '
directorynameSpacedefinitionproposalpoll
'.$md5ns.''.$ns.''.$def.''.$prop.''.$poll.'
'.NL. + ' '.NL. + '
'.NL. + '
'; + } + + // ============================================================ + 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 (); + } + } + + // ============================================================ +} diff --git a/images/clock.png b/images/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..f2e9343c53d44d96b11911b5d29dea7c701891d8 GIT binary patch literal 1004 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe@ z1v3+n%nND&000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0009=Nkl9T1%_u+I^bfZN(hx<@LywMv$bO;-pB10IWAZQiD zn_(64Ld2UP3T}$xm{{CE8Eg|KcGS&jCvA1<_mU<}^LdhUeqL04;k|o!ZysJ@xCSpr z0O0i&ohkRWR@;9%8!?ZjLu@pt7AoyO%eD4*YaRFGpc%jM;nBSS?EmN*!hadAcBX=I z9!!43&rYNy-w-lua4YV1pXa(x1Q*U@Xmu7>(IWe z#kRGgn=&HPMTj~O38Hh$@XM4ZKhM;;QCvCR7hQby{R8(d+Uyx88Q{eS()OYKcUW{M zL_0!AQS@4Y83I{>3Vd;_I-V}KWPQ-UMHG}BDM$y7&p*Dxd&nN zQi=IoohJ|W&`={{(D9ow`|3jMut_`Su^nllJ1&xpD2@$|tz-jmCM?Y68eANikOiG#h=hbo7s`BdB1cymyed5P%I&1qK-*I=f}kYAf@28=EW7G@6ZwYS zKNwww`gg_|+|>tw1x$HR@Rcqj_^g^wMWNJC0)D+=R$Oo7{IpW3t2SN1sM2af+XL5w zTY-55V<2cLr5Y?%RL5pqHpJ^QrqgPDb~)c_U-(OlLyjdVxRm?JWo2e&1NLbIlN`gfI6qgBc1^wMCqI1~%C z&i;RTUN!LPF}<>h5|#OJ35GUd0s!tRM(dmgUI zruXtpywnB2sqcRXUtUT+bhQ|LZpjS|vtG+i7npP?tPE6 a0sa9JQEkNmFmZhV0000Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iph^ z5GyrUXW)bY000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0005?Nkl&ds8>2jkan2`U95W(eL#GAAbCPzb~Bmh73bzJ~?@aVC8TKysS?%t8k3l~Mbd`n!z4`t!mb9p*5BY;Gs zQ7IG(;sCI1yD~jJjcJ;c%ViGEckyv+pRHdu9y5S@;5K$c$Lza`&(+7^;2?oO08P_S z0oK;mx<*GwNu^S_-EJ~}9V~3t_<8K%+RaHq;USEpDp!KNc>N}kNQ7iEsf5E}MFkie z8&l@z=M`Po0jLzR*vAc8_n(kIH^Rp17jF0uY3=Xw=IuKy%W6O4`ue)e&CTKS`6%!1 z@&4;~O3r?c_DWp8bjVPDFN&rzIW@(^#Dvl&*xA{6U9Z>U!C+7U$mjD?EEdr;jaI9L z%jIHxd|Wy8Cjyj8B@q!CjRuBcRJ~qr_t`7kKjgv3eQ}9Tb^rhX07*qoM6N<$g30_f AD*ylh literal 0 HcmV?d00001 diff --git a/images/face-sad.png b/images/face-sad.png new file mode 100644 index 0000000000000000000000000000000000000000..de9969a477002d45b16c24c378538dff30ff0703 GIT binary patch literal 802 zcmV+-1Ks?IP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01egv01egwkZ*aM00007bV*G`2iph^ z4>>Q&i;Z9a00N>(L_t(I%axN~Xw6|5$3O4y{C0N6{M#QoDP^{z7PSp)8HJ|Rh!$aL z6Xgme#f1wB;Yu#lFsu-^lw*^Bp`|vPMq>UgW7^`_v18|)ZRdB+`#vt#PQzUI-n>2E z-tVWL=XqZkM=$+7($vr>03xD9#1fH|f@@h9%5LY!H$EyjKYG_15Ae0VkxvJXkCraL zT6Vq2Js5Q=56?g68XlCYO-&0@{|WG4_mLSIq^T|f8$d9;+ zg_8z$JT0nGe}*4kxhj#`da~Q!(wR4xgKN6nZ-Q@z_80DVMU$fRMn9rsF?s_5yfX*2 zsw=M&6&irA-vNk3A{3{mGh@mmZoTWoHPcIV&I{VJqGXt9%+8yJzbiy?T3phmyK8LQ zlSCxoke;8*;>KpI2{xW=F90QNaTxeQ{)Y^nWZLXq+l#He1WdQ2+ScTj(yp+z?($i+ z<#B_u=H_rGE0dw9#(CSFXsOe!Xr163-oWj#MKsXI#LwSpPKLfz55+$nGDUDYKk_dn*4WzqOswH3 zPdqSX>w4zZouMV$OJGt8{m)+~0_FEtuPD8{YT;jiU`snX6lT`xqbxagNG-}MP`SQ~ z#8xe(|C>JnaCB@}64v9gJ0-aQs^Y5eWjj75}O7`>Ss%tWrmGnXjbbp8daoWXt0l gGETj(q~RXGFBexno+J!6P5=M^07*qoM6N<$f-typY5)KL literal 0 HcmV?d00001 diff --git a/images/face-smile.png b/images/face-smile.png new file mode 100644 index 0000000000000000000000000000000000000000..f765cc9695914b221d233e7333249d940447f533 GIT binary patch literal 806 zcmV+>1KIqEP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01egv01egwkZ*aM00007bV*G`2iph^ z4?7t*2YPz|00O2-L_t(I%axLCNY!@~$3N%yyWanQrKqG`HX)Weim(Tr>xI+?Yr7U` z5+M}?7D7QUYOJ@`i-?H5+g`ZEtYBNKTtV2a%%U3-%j^+}Ea=ICu$xz}_rZL)|9g7T zbZbS>2M&kBIeht^!$DPzs(w^?wD@4n>Ce+^!<7g4AH9(t4TUF zBP0@qoIZ8+Ef>b7ZNqo-wo6^v8csYt!pAc{BNok(k(Ac}aQM#)>NvT? z5VM0{^L%-Y1zx_)uE;0yD1noo#_6kK==ds9sT6@3ODV_=aO>Vlii#|$lwhskx*3eO zi%s?Ai)O~sN%eUyQEv$UA5D}#UQd7j*BGykg{7ZRRA`CEgY@<;je z`b%v%Iy^mgAbjVh7jqIVo#z{6&Rj!raXZPem-O5npR(-t-)GviZVvrkLUZ#<;eD*G z|4XYDKh(Uu!KnxYPH9U^*VHk6?-Pi=7~?_TbR6e0+1UdO4=+Wvkcaoj-}nOnR-CQ= zV)eP&Ws~)_?i)HfYH;04G;Yl0=;8U4mUfYo6Xp8Np*IC;+Jj|Tn2Y1y#Ma41_q72M z36GzfXR&AR5~*BVAqU&asIH#i`SX7TWJPJ#9Ch{(Eu?;93Rz~$ZPZJ4f=p> zMkFjE`Cf(?WF8nY;u(Wgk+dp45i+WxB4qGDkt_@zTor(o9W_C}*pQMrbNt(rb$kE@ kpe-2c@SxE#ZW|xSE0T*1z_cyq-v9sr07*qoM6N<$f+aP1KL7v# literal 0 HcmV?d00001 diff --git a/images/help.png b/images/help.png new file mode 100644 index 0000000000000000000000000000000000000000..75ebaf7f5f73f57df9cc93159479a672b49176e4 GIT binary patch literal 897 zcmV-{1AhF8P)ll^X4{Z>T-^z>G~pp+RTU*DH>_u zACN&16chTPK!1pS$uAKU{TieonFJR0O9X{qP?pLdjm$y~@e9tTuJ-NJx!t+Do?SZB z7rZ$h?mq9&`+1(vQL-%KAWcn8wAE^L7>&j%mSuB7q0k7=^Pikf=Nb+|4ECv~r>D{H z_iu`#C~s_R$fQtFBodLw#>T$3wY6FHexFRx-rlY*E-oIi+wEoHa2S(IK}>og;G=OI zH88MSIUF`<5s$|sE|<$$S6BBM1R6W!=H|XEDJdzN^Q@ue!xBbUInahI==5m_(CK*E z@59IMK9FRb!{KNn4J}E7uCA`C^78U){y-3q`u>2;$bqyO#0|Rvcdz84+I9#=eJTd$ z;~1Rb6VbWC!ov4S1Jb=w5Cpoz?T4Vt#=Kv|R8WPMcT;%M^9?7hnJ{xAyde=CE-zx+ z6!P=)&ye#lsX#E9OsBTCw&3=NSfjTQiwY28fIF(jJv$VJXV)>aA|Wo)7@Ozeu%&|I zxEdva#bU`Od&2_HAhac6JtkpOkU%4jTbGaHRo_nxEy)OOituj;?ema9)$a}avK*eJ>X4h59 zT0xdjR*(isk`j#v6oZwOm0^uWgWG4dV8jSWQ{-haXt!(3(4CCYD51X5t zrQ{@bc6L_O*4FluSjeh>ey#qW#2lTBOSUxJsWd|*#naQ%U#hFC&w;ROkTfYR zE$ucK3??$NP^na)X-C2g7Z-Vt$Ftnq+xwRMZ2bV1iB?Iiv@Zcg0H7vfccdYrhyrYb$00r>V(#mZm0oq#>(IylnMU9h~C2n*&UleDUvL*91z)74;h+$@!Af}2_ ze2h%_;A2KIf=a1G##|Ax?I;v&DKaqBR-jyHFU0}|b!ul@dhf3P!#?ihm-7dl1km3XkLzunHzj^e(pA&W(z8c5&kw-`!lo` z_?9|6ya{VPIvZfqqEiRUEj+vhyAEBA=+VK}2qD>~AYUR)+yb--=={krvhVi|c-xmU3%Q@OP&tK!bhp35r>?AN-9>R!2G&L!2( zfm9-Hak02XRO|1*o>jdd`<$(zR(qmE^20c>q)wZyIdI(GqD0dk%eh5iz0##BJ2X?= z-v=KZhyyPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe` z2M-#N9a)wD000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000ADNkl{T})eb7{-6^Ij0||v<+=Dpg_ylY8Y<9Fa~7a_<_11iIN#HS-@$g#*Yhkv6*nG zamGcR#(3c+#w!>0}1J(be*I%pO+0>EA8xf_zqg(U8PmB)+{6!yJ89jS#Jbo?v%BwH% z?a(N~ZFRKm3|HLK(U)>oygaW}aq-U-_Lc<*;t=k=BS3{UM<~D8KYMAQZ}3t)QBf5{ z2Y8^lrmVjGVEDHdB9{Ty-Ch0yt|OHs1CUr0){o9rPeZs6IvB^KChGl+g2zA z8*5o=O1+#s+Rm9bI(YNxCS3aMz?7|~?Fd<=j_93F9{A|wn)|x5|z5&kfrnZI-8`av^1*Y+E5Bny!Pc z72vW_D5N=B#BTT0uleZ<&kdcwoYY@>@mW4Q_04L;y*gZ5;wh^w@$52#<+8AnMOjJ} zP)3^SwnePPXAJoM?n$aMhO)zm_?!pG^;XW0MvUeoC*ZO9=dr literal 0 HcmV?d00001 diff --git a/images/score-0.png b/images/score-0.png new file mode 100644 index 0000000000000000000000000000000000000000..e6a98f64f90cee20827359f01aa7cdfc6310243e GIT binary patch literal 1299 zcmV+u1?>8XP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01r9<01pxY%4BtD00007bV*G`2iph* z3@#%R`Fw={000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000DTNkl z*@Q7)@}STM2V>owlvxz2b#|gFW2m%E2eMT+LY$U1P0Uqe+G}!?c=yy0n{=(r3}N5n zIUo0&-|u{!^Ia$qS0dxu&!_t|{wEO(2BmZ7&Pj#l#6p9UJ$?N3@xcWz@T3X-C*VRc zI5^0O6DL@(pvd>V*y9RAeP1o#6Y&fiCocbexuBrlI zT0zk1cwjQJ{ZhX$Tj+FG535G55m@Wy?fxENzD*r%+^X5eCRuvuGuFNJ=Dhh{uUAT? zQna+R(Ae1MpAUFwXozGoiP31}z<~n~<#%*+NUEwbH8qvxb-UfzY&I@lyol9mC6!7c ziXu9l4&mgxA86C#A#AQ?ywZ0G(X7Yhc@6;O%rF3V^&*325%bnJNQQ6oUkZKPz&4u= zS(d4+tVB^13-G8py1Kfgk&zJ=FJ8=&B};HP9E^^R5{t!%$K$lN zw&L-4{MkEbOS@NCR8__OWlKp1BU#v6g2swZK4e|P1_9um@4gh?8#pb9?q@h#W9NP{ zgL>^|_Cx~)fkCs#%A<$b*69=UrbQ%Fl~QR9uTREl&WKd5sz7JAu=!q^O;^WQJ8_4U zd>i9SwHOQrlF201)z#STc2=)mO?7oO7K;U^(@9xb85WC$si`TPPAB{K@AvzBKEK^= zN0wz0i3H(rIJ=)%EJh>}p|!O&D+gfb&Yk}9@^Z{(GqNmaWh#n7b8|BukH-(7&n?%a z9HsQeEiM@$YB8}srefDL)Icz=^tZHoh2t%|wEGXh_~$t3U}R3A0R2rq?d;V68*fAr zuieDpUY->iG>O>?o%w$535UaB91ceoIQN8efjc@nq#0mYmJ0&sMm!!z5ClS@5b1Op zhr@x{Y$h6w=D%WtBg2}QNb-vbtnY8<+2cpJys84Ztb{wi{$B8Y$Q63ZX5y}1#I6aX z=McJ(3VqZQo(-%h3ZYPlv9U2!Rm}sQiQ(a4DU-=CF)=}XeLZb$Z77ODCX?a%_3Qb9 z?p_`t6Q5#v=jXik#izo{YwLtHyyQbve04yXRm@lfJ7p}jvYJLy?eL6 zuCC6%Z{I##E?40S(w9HQ3qSQd^z1(Wbd|8J(-9G^HU0Z`{=%_t?Mh@^n`>_N*pHn@v~O!))pDE3E0J-nx6P}4^I}6@es_1b zv}eyADYw}R27{8%=acsC-76Jp2ZF(%G*_$e#QzXJs?a9|_;1A9Fr6LlOC10J002ov JPDHLkV1nKHYNP-F literal 0 HcmV?d00001 diff --git a/images/score-1.png b/images/score-1.png new file mode 100644 index 0000000000000000000000000000000000000000..ca42ed76b3165184e9935e624a95c4cae851bed3 GIT binary patch literal 1233 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 B*ew76 literal 0 HcmV?d00001 diff --git a/images/score-5.png b/images/score-5.png new file mode 100644 index 0000000000000000000000000000000000000000..d6423e417b539bc1d180ccf52d91748210fa7b57 GIT binary patch literal 1247 zcmV<51R(o~P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01r9<01pxY%4BtD00007bV*G`2iph* z3@jwStBa%n000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000C!Nkl5?#m@e^o z`MW21p3mp`-S2(}>=7)>(lawNH2x=X5U*;l#Xul{$;rthDezxJII$#Mz52aWYW_jI z`wG%%G?<&4qaU`wQ3zEE{N2~Fs-%pp9m!qt#470Pd4_)9WgenET zd^rK0H{<%Xg+rwt#!Gm;URo4Iq*5sWKsud9V`C#sCKKl8=V3G&k;~P|N@XN1LsI65aohG1G6VU7L!*17r zqJD6c$}1Ne9UY~E!62%ts!&~B4T_==3WbnNCc*PO+-^6VPABuIg|0?lT2u;r`*s?2 zHWQ|{${cBj*Suyz&|3a=Dnw%1V$V36A4rSV0ib z(a`~?)5!q9;NT$R^Z6LF*$j^3kjZ2akH_VSCX-1d5(&87Zg@N%Myu5#o6W*%wZd#R zqoJVzR;v{Tg8>$c1r-$)Fc=I-rBbk1EYNE;5XB5m(dAfQe+{Za2cEC5L9JPhd-vW% zIQ%-+)|$a_Ih;JH!}$0+#N6Dw#M6x`*zGrv%5GxmmMHh5)kZ*)0gR8;$#RtNYPN4z zp65Xj1Y)rm^7%X{iULWJ*x1-8+WIb+i}8BB^xp+Ij@wu0?uo_YaZnT`3*A03U~5;O zX`hys!#A+H`YM)gsqkE*RtDDVS&Wa>;q)v2oJa2uv=N=>uSlxA>@26RorT2Tbdu?K3g6`?*p|@WqS(c@JJ|ErN+gp@BGV+bo z)HEh-zwCt*OHyB-UpjH(b7`;ZKsd1^?RQmp?0*QA3VmFF{{l2oIJ4gGMG^o2002ov JPDHLkV1lDGM&keg literal 0 HcmV?d00001 diff --git a/images/score-6.png b/images/score-6.png new file mode 100644 index 0000000000000000000000000000000000000000..23bef6490cffefaa0041c6eb1f3cf98e97d51ef8 GIT binary patch literal 1296 zcmV+r1@HQaP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01r9<01pxY%4BtD00007bV*G`2iph* z3@jDyb&H+=000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000DQNklzzazq#skax zKrre<6K7H%6%oO0!K7@*4+BGcgX^fX3BM)sZHs0n= zV-=S#|AhkyQmIs!oSfVjbhd>aCUB;>b}a-!FyQvB8F^1Z9*>94&CNwlP7Wp}Ca_;Y z*%W%1z&m5;n;&GgT8H)Z6ZrJ%YT7*EbUJBCk`NAu0RWLm1XimRdc7V~Q&Z6Cbcn~} zP$(2oDwS}#T+B|PGcz-Y#bQva)#&Z*W%k;X!{MOg@i>~Bn^9k1zvue;`mg2XWj#K- zdJN~@sUh|P{<1%U+1WBwRrxVC_U^Xz%@?oB0#9J|v5QC~0;9eT$8<*g_WLM`i^yNG&MEBX0tH>(9_eyxLhvAU@(B=I7FjS z1cSlkiSj&;P$&eu-42Jt!H^`0SS*H;k`fpU29%eVqokw+TCEl)lL>iwdC+RL2#3Qk znM}}-Do9ckHFPdkSKojl(S(y_MJP5XvA+H`78c&b%1RYDE{@Zu)fgOnkC>c%pLoHl zfW>kL;n)V|?n%l0kfa|JHI2c6(xe;!xYXT16d!*E=HnZfXE&2F3E}~+TseWdGh3gs zDz#h%K>$Gz2m}Jy+}woGXaq%3SX*05EB)5iR>tXc(ockP9Ji~`ofR4k24OTBlR{G~ z6xgcjbB$y2{K6e9EuF>OJq0SQWD+>_gj0c=FJ6}wk2Wy>hZ^(jW=7!D;2y;A=Ta`z z>Yt(2u7T(4Aj<}@ekuJG^Lo8>B9Xw#$_i?0Yth-+2~iZ0NF=bhxR~*N5Cj3DC?XID zV0n2Nk|aG<=zc2npeMW=uwFk3mFnSR;LWuCc&BIOXjB1><|o)c(Ba#mmr+*s3-aK^B#|fW?7crZEok-@bEC*-ri29Hj^yN(k_>a?&#=9Tkm#%FIQ9y z$f?cVLTFCz?DWW|PJJb3Y6liVbMkJj!jby-h00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01r9<01pxY%4BtD00007bV*G`2iph* z4GuH}Y`50{000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000A=NklUPOR=wQ8wo;v0ANER#vjGuuxKh)P$d!jLwrl zu%xO^Or*$UY6u3NyjA};9;vUdPs!zS?C;<8 zX=-b0(RCfO*^JBO0w5NP0g%h(NTpJEJRa(Pif&4oKVWOSkf62t=6M{WQ$f;n zB%_hnas}h>d`4zZVrz?n-eGU!g3ZA%&u4HtjcA&L(I_Fyukm_+t1XA8;C^^`Scya; zoIih_>gsA74hOMVjJ>@*GMNlrU0t-cwkD@P{ak2?Bz9LFmjf#pEGFdE4*<}EVE}eb z20|7C7VmZL@3oOgXw(_WsvGS4I%veyzY>p$#D&LLN({}Yz**qs6aVC>N*L9M~BpVwWJIj56Xbr(2NQ-$hpOM;8NUz~sk`0~yT$ys*}SKTX~sYiwCOVT#Iy%&q% zOXVpT2#3Q;KA&fEbCZ^q7JNP*x~`MY=UH1@JM}>xq<65cZ?jSXGj^F>lYtr`aTHfi z)u*=)_fE@A9}oAzf9;%cswZfkF|H%^Hy9M|9n?aewSX8X6u`Rh8!F zr!R~2F{wc?w-SQW)fVCgP00000NkvXX Hu0mjfXto0B literal 0 HcmV?d00001 diff --git a/images/score.png b/images/score.png new file mode 100644 index 0000000000000000000000000000000000000000..a1f21915a342f7daf7e40710d35fc2fd7a3c2936 GIT binary patch literal 772 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 N002ovPDHLkV1nL%W>^3K literal 0 HcmV?d00001 diff --git a/images/stop.png b/images/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..3582124c011a5ec84ac11373a8ded42416eb25ae GIT binary patch literal 872 zcmV-u1DE`XP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe` z2M-F2s!8$y000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0008RNkl>$FhXTA(hVV_7x2qHKVKt+B$MwnS|tq6vmlS&*CpM;5c+ZN^xpU5xEL@lDR-WJ+4~mOL05aibM|G)qEtr~= z2Dk|1g)~S5TsV1NDoe#1nT^ISfI_j-40yP@nR;5Q{1l9yk{Fx=*-A0E^;4r#uU2l% ztTnm-_@lbwS4*WIgRxQ3E#ALoyTt(uVmwba#>S*pDqWweR@zva%Y7eSoRS!vc}?=G zM`Cc{#jj*Jzj&4U?2LrqbSfo!a8Qg=qWk(pr_*9~B*nBQ#0(6G@jNl@?V|VZ7t`7* z0l4~XMtl&&PY{207Wd{3)UDevm_{8rin=fb8ik9ZbW+NAUAqd~0D_8x#u2v7`9szC^wEE~IwCFL%+MT_g@10Kh|mCk~mIK<(L! z`Q~f9)n}+!qnrb05vu_YOpH1=N$cDF$e(|KQg{fsR0=tB7F%A%d$x)-8XJTNV5X-r zW2Z5>EH(^qmoL%s`4{-kH5pi$c_bmYxW8ZY@UTRFLUb}Iad&s~p~h!=2OxC1JJ8%MBp|)o{&mD_ZzeIr){M-CHXx-U&{Oh~5Gy3t|+a!37_@FZFzWx)4U`m-|LG zvTxNE=I(8L@{w$U)6Yw!o8&diG(IjX^SR7I6ut9ePu>6ZK8J@7qm*LiNx5y;W_TxA ys~+-1{uAI@d>XsE9;_v{9~;=yy9SWWX88wXU*dBJb$-+U0000 + * + * English language file + */ + +// javascript +$lang['js'] = + array ('pleaseWait' => 'Connection to the server in progress ...', + 'proposal' => 'Proposal', + 'update' => 'Update'); + +// commands +$lang['remove'] = 'Remove'; +$lang['update'] = 'Update'; +$lang['proposal'] = 'Proposal'; +$lang['new'] = 'Reset'; +$lang['clear'] = 'Clear cache'; +$lang['clearAll'] = 'Clear all cache'; + +// labels +$lang['ticketIfUpdate'] = 'Ticket (if update) :'; +$lang['ticket'] = 'Ticket'; +$lang['useTicket'] = 'Use ticket'; +$lang['date'] = 'Date'; +$lang['ip'] = 'IP'; +$lang['email'] = 'e-mail'; +$lang['word'] = 'Word'; +$lang['translate'] = 'Translate'; +$lang['why'] = 'Why ?'; +$lang['poll'] = 'Poll ?'; +$lang['why'] = 'Why'; + + +// messages +$lang['readData'] = 'Reread recording data.'; +$lang['wordMandatory'] = '"Word" mandatory.'; +$lang['translateMandatory'] = '"translate" mandatory.'; +$lang['proposalRecorded'] = 'Your proposal is actually recorded or updated with the ticket : '; +$lang['ticketDeleted'] = ' removed.'; +$lang['pageDeleted'] = 'Page removed.'; +$lang['lastIP'] = 'It is your last proposal. We need time to study them.'; +$lang['maxIP'] = 'You send many proposals. Let us time to study them :-).'; +$lang['noDef'] = 'Definition doesn\'t exist anymore.'; +$lang['writePoll'] = 'Poll recorded.'; +$lang['glossaryNotEmpty'] = 'Can\'t remove the no-empty glossary: '; +$lang['glossaryRemoved'] = 'Glossary removed : '; +$lang['notPolledYet'] = 'Be the first to poll!'; +$lang['notifySubject'] = '[glossary] New proposal!'; +$lang['notifyContent'] = "New proposal:\n\nWord: @WORD@\nTranslate: @TRANSLATE@\n\n"; +$lang['badCaptcha'] = "Are you actually a human ?"; +$lang['createPage'] = 'Create page'; + +// toolTip +$lang['tipWord'] = 'Sort by "Word'; +$lang['tipTranslate'] = 'Sort by "Translate"'; +$lang['tipDate'] = 'Sort by creation'; +$lang['tipView'] = 'Sort by view'; +$lang['tipScore'] = 'Sort by score'; +$lang['tipSearch'] = 'Search text'; +$lang['tipWhy'] = 'Click! See the amazing explanation!'; +$lang['tipPoll'] = 'Clik to see the explanation and poll.'; +$lang['tipPollDown'] = 'I poll against!'; +$lang['tipPollUp'] = 'I poll for!'; +?> diff --git a/lang/en/pageTemplate.txt b/lang/en/pageTemplate.txt new file mode 100644 index 0000000..c67dede --- /dev/null +++ b/lang/en/pageTemplate.txt @@ -0,0 +1,11 @@ +New proposition? \\ Share yours **@@PROPOSITIONPAGE@@** ! + +====== Choisissons notre vocabulaire ====== + +@@WORD@@@@TRANSLATE@@| Your score : | {{glossary poll="@@POL@@"}} | +TODO Give us your own @@PROPOSITIONPAGE@@ :-) + + +{{glossary list}} + + diff --git a/lang/en/settings.php b/lang/en/settings.php new file mode 100644 index 0000000..11e5d7e --- /dev/null +++ b/lang/en/settings.php @@ -0,0 +1,19 @@ + + * + * English language file + */ + +// for the configuration manager +$lang['dataDir'] = 'directory where data are placed'; +$lang['recentDays'] = 'number of days the definition is considared new'; +$lang['adminGroup'] = 'group to managed proposition and definition'; +$lang['maxIP'] = 'maximum waitting proposition per IP adresse'; +$lang['propGroup'] = 'group without limite of proposition'; +$lang['transSep'] = 'separator that cause a return in translate list'; +$lang['propositionPage'] = 'page include proposition form (main glossary page)'; +$lang['sampleDelai'] = 'Cache time delay for sample (sec)'; +$lang['sampleDelai'] = 'Cache time delay for list (sec)'; +?> diff --git a/lang/en/translateTemplate.txt b/lang/en/translateTemplate.txt new file mode 100644 index 0000000..5db1bf6 --- /dev/null +++ b/lang/en/translateTemplate.txt @@ -0,0 +1 @@ +| Say: | @@TRANSLATE@@ | diff --git a/lang/en/wordTemplate.txt b/lang/en/wordTemplate.txt new file mode 100644 index 0000000..443c2a5 --- /dev/null +++ b/lang/en/wordTemplate.txt @@ -0,0 +1 @@ +| Don't say: | @@WORD@@ | diff --git a/lang/fr/lang.php b/lang/fr/lang.php new file mode 100644 index 0000000..a5b4085 --- /dev/null +++ b/lang/fr/lang.php @@ -0,0 +1,67 @@ + + * + * French language file + */ + +// javascript +$lang['js'] = + array ('pleaseWait' => 'Connexion avec le serveur en cours ...', + 'proposal' => 'Faire un don', + 'update' => 'Mise à jour'); + +// commands +$lang['remove'] = 'Supprime'; +$lang['update'] = 'Mise à jour'; +$lang['proposal'] = 'Faire un don'; +$lang['new'] = 'Recommencer'; +$lang['clear'] = 'Effacer le cache'; +$lang['clearAll'] = 'Effacer tous les caches'; + +// labels +$lang['ticketIfUpdate'] = 'Ticket (si modification) :'; +$lang['ticket'] = 'Ticket'; +$lang['useTicket'] = 'Utilise ticket'; +$lang['date'] = 'Date'; +$lang['ip'] = 'IP'; +$lang['email'] = 'Mel'; +$lang['word'] = 'Ne dites plus !'; +$lang['translate'] = 'Dites !'; +$lang['why'] = '?'; +$lang['poll'] = 'Avis'; +$lang['createPage'] = 'Création de page'; + +// messages +$lang['readData'] = 'Relecture des données enregistrées.'; +$lang['wordMandatory'] = '"Ne dites plus !" nécessaire.'; +$lang['translateMandatory'] = '"Dites !" nécessaire.'; +$lang['proposalRecorded'] = 'Votre don est bien enregistré (donnez nous du temps pour l\'étudier).
'. + 'Il faut cliquer sur "Recommencer" pour un nouveau ticket et ne pas perdre ce don.
'. + 'Pour modifier votre demande, voici votre ticket : '; +$lang['ticketDeleted'] = ' supprimé.'; +$lang['pageDeleted'] = 'Page supprimée.'; +$lang['lastIP'] = 'C\'est votre dernier don. Il faut nous laisser le temps de les étudier :-).'; +$lang['maxIP'] = 'Vous avez fait beaucoup de dons. Laissez-nous le temps de les étudier :-).'; +$lang['noDef'] = 'La définition n\'éxiste plus.'; +$lang['writePoll'] = 'Avis enregistré.'; +$lang['glossaryNotEmpty'] = 'Impossible de supprimer le glossaire non vide : '; +$lang['glossaryRemoved'] = 'Glossaire supprimé : '; +$lang['notPolledYet'] = 'Soyez le 1er à donner votre avis !'; +$lang['notifySubject'] = '[glossaire] Une nouvelle proposition !'; +$lang['notifyContent'] = "Une nouvelle proposition :\n\nNe dites plus : @WORD@\nDites : @TRANSLATE@\n\n"; +$lang['badCaptcha'] = "Etes-vous vraiment un humain ?"; + +// toolTip +$lang['tipWord'] = 'Tri par "Ne dites plus !"'; +$lang['tipTranslate'] = 'Tri par "Dites !"'; +$lang['tipDate'] = 'Tri par dates'; +$lang['tipView'] = 'Tri par vues'; +$lang['tipScore'] = 'Tri par avis'; +$lang['tipSearch'] = 'Recherche le texte'; +$lang['tipWhy'] = 'Un simple clic et
l\'explication déchire !'; +$lang['tipPoll'] = 'Un clic pour :
voir l\'explication,
et donner un avis.'; +$lang['tipPollDown'] = 'J\'aime pas !
Juste un clic pour le dire.'; +$lang['tipPollUp'] = 'J\'aime !
Juste un clic pour le dire.'; +?> diff --git a/lang/fr/pageTemplate.txt b/lang/fr/pageTemplate.txt new file mode 100644 index 0000000..508196e --- /dev/null +++ b/lang/fr/pageTemplate.txt @@ -0,0 +1,11 @@ +Une idée d'expression ? \\ Partagez-la par un **@@PROPOSITIONPAGE@@** ! + +====== Choisissons notre vocabulaire ====== + +@@WORD@@@@TRANSLATE@@| Votre avis : | {{glossary poll="@@POL@@"}} | +TODO Votre @@PROPOSITIONPAGE@@ nous intéresse :-) + + +{{glossary list}} + + diff --git a/lang/fr/settings.php b/lang/fr/settings.php new file mode 100644 index 0000000..b10c49d --- /dev/null +++ b/lang/fr/settings.php @@ -0,0 +1,19 @@ + + * + * French language file + */ + +// for the configuration manager +$lang['dataDir'] = 'répertoire où sont placer les données'; +$lang['recentDays'] = 'nombre de jours pendant lesquels la définition est considéré comme récente'; +$lang['adminGroup'] = 'groupe de gestion des propositions et des définitions'; +$lang['maxIP'] = 'nombre maximum de proposition en attente par adresse IP'; +$lang['propGroup'] = 'groupe sans limite de proposition'; +$lang['transSep'] = 'spérateur de qui implique un retour à la ligne dans la liste des traductions'; +$lang['propositionPage'] = 'page où se trouve le formulaire de proposition (acceuil du glossaire)'; +$lang['sampleDelai'] = 'Temps de cache pour l\'exemple (en secondes)'; +$lang['listDelai'] = 'Temps de cache pour la liste (en secondes)'; +?> diff --git a/lang/fr/translateTemplate.txt b/lang/fr/translateTemplate.txt new file mode 100644 index 0000000..962e146 --- /dev/null +++ b/lang/fr/translateTemplate.txt @@ -0,0 +1 @@ +| Dites : | @@TRANSLATE@@ | diff --git a/lang/fr/wordTemplate.txt b/lang/fr/wordTemplate.txt new file mode 100644 index 0000000..fd3cc3c --- /dev/null +++ b/lang/fr/wordTemplate.txt @@ -0,0 +1 @@ +| Ne dites plus : | @@WORD@@ | diff --git a/plugin.info.txt b/plugin.info.txt new file mode 100644 index 0000000..620762e --- /dev/null +++ b/plugin.info.txt @@ -0,0 +1,7 @@ +base glossary +author Francois Merciol +email dokuplugin@merciol.fr +date 2019-09-25 +name Glossary Plugin +desc Managed glossary +url http://www.dokuwiki.org/plugin:glossary diff --git a/script.js b/script.js new file mode 100644 index 0000000..87e6e89 --- /dev/null +++ b/script.js @@ -0,0 +1,227 @@ +/** + * @license http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.html + * @author Francois Merciol + * + * Javascript functionality for the glossary plugin + */ + +// ======================================== +// Util +// ======================================== +function trim (str) { + return str.replace(/^\s+/g,'').replace(/\s+$/g,'') ; +} + +function glossaryClearMsgJ (item) { + // efface le popup + item.closest ("div").find ("div.popup").each (function () { + jQuery (this).remove (); + }); +} + +function glossaryZebraJ (jTable) { + // alterne les couleurs des lignes du tableau + var oddEven = new Array ("odd", "even"); + var even = 0; + jTable.find ("tr").each (function () { + var item = jQuery (this); + if (item.hasClass ("title")) + return; + if (item.css ("display") == "none") + return; + this.className = oddEven [even]; + even = 1 - even; + }); +} + +// ======================================== +// Clear form +// ======================================== +function glossaryReset (input) { + + // met à blanc le formulaire + var jForm = jQuery (input).closest ("form"); + jForm.find ("input[type=text]").each (function () { + this.defaultValue = ""; + jQuery (this).val (""); + }); + jForm.find ("input[type=textarea]").each (function () { + this.defaultValue = ""; + jQuery (this).val (""); + }); + glossaryClearMsgJ (jForm); +} + +function glossaryUpdateProposalLabel (form) { + form.elements[4].value = LANG["plugins"]["glossary"][form.elements[7].value ? "update" : "proposal"]; +} + +// ======================================== +// Glossary Util +// ======================================== +function glossaryComparatorWord (a, b) { + return glossaryComparator (a, b, "word", false); +} + +function glossaryComparatorTranslate (a, b) { + return glossaryComparator (a, b, "translate", false); +} + +function glossaryComparatorDate (a, b) { + return -glossaryComparator (a, b, "date", false); +} + +function glossaryComparatorView (a, b) { + return -glossaryComparator (a, b, "view", true); +} + +function glossaryComparatorScore (a, b) { + return -glossaryComparator (a, b, "score", false); +} + +function glossaryComparator (a, b, name, num) { + var valA = ""; + for (var i = 0; i < a.attributes.length; i++) + if (a.attributes [i].name == name) { + valA = a.attributes [i].nodeValue; + break; + } + var valB = ""; + for (var i = 0; i < b.attributes.length; i++) + if (b.attributes [i].name == name) { + valB = b.attributes [i].nodeValue; + break; + } + if (num) { + if (!valA) + valA = 0; + if (!valB) + valB = 0; + return parseInt (valA) - parseInt (valB); + } + if (valB == valA) + return 0; + if (valB > valA) + return -1; + return 1; +} + +function glossaryGetRowsJ (jTable) { + // prend les lignes du tableau + var result = new Array (); + jTable.find ("tr").each (function () { + if (jQuery (this).hasClass ("title")) + return; + result.push (this); + }); + return result; +} + +function glossarySetRowsJ (jTable, rows) { + // fixe les lignes du tableau + var titles = new Array (); + jTable.children ().not ("tr").each (function () { + titles.push (this); + }); + jTable.children (".title").each (function () { + titles.push (this); + }); + jTable.empty (); + for (var i in titles) + jTable.append (titles [i]); + for (var i in rows) + jTable.append (rows [i]); +} + +function glossarySort (input, comparator) { + // trie le tableau + var jTable = jQuery (input).closest ("tr").parent (); + var rows = glossaryGetRowsJ (jTable); + if (rows.length < 2) + return; + rows.sort (comparator); + glossarySetRowsJ (jTable, rows); + glossaryZebraJ (jTable); +} + +// ======================================== +// focus table +// ======================================== +function glossarySearch (input) { + // filtre les lignes du tableau + var jInput = jQuery (input); + glossaryClearMsgJ (jInput); + var value = trim (jInput.val ()).toLowerCase (); + var jTable = jInput.closest ("tr").parent (); + jTable.find ("tr").each (function () { + var item = jQuery (this); + if (item.hasClass ("title")) + return; + item.css ("display", ""); + if (!value) + return; + var hidden = true; + item.find ("td.word,td.translate").each (function () { + if (this.textContent && this.textContent.toLowerCase ().indexOf (value) >= 0) + hidden = false; + }); + if (hidden) + item.css ("display", "none"); + }); + glossaryZebraJ (jTable); + return false; +} + +// ======================================== +// sending form +// ======================================== +function glossaryPoll (ancor, ticket, opinion, NS) { + // vote + var params = "glossary[operation]=poll&glossary[ticket]="+ticket+"&glossary[opinion]="+opinion+"&glossary[ns]="+NS; + glossarySend (ancor, DOKU_BASE+"lib/plugins/glossary/ajax.php", params); +} + +function glossaryAjax (form) { + var params = ""; + for (var idx = 0; idx < form.elements.length; idx++) { + var elem = form.elements[idx]; + if (elem.type == "checkbox") { + if (elem.checked) + params += "&"+elem.name+"="+elem.value; + } else + params += "&"+elem.name+"="+elem.value; + } + glossarySend (form, DOKU_BASE+"lib/plugins/glossary/ajax.php", params); + return false; +} +function glossarySendClear (action, ns) { + jQuery.ajax ({ + type: "POST", + url: DOKU_BASE+"lib/plugins/glossary/ajax.php", + cache: false, + async: true, + data: "glossary[action]="+action+"&glossary[ns]="+ns, + }); +} + +// ======================================== +// Ajax function +// ======================================== +function glossarySend (sender, uri, params) { + var jDiv = jQuery (sender).closest ("div"); + glossaryClearMsgJ (jQuery (sender)); + jQuery (''). + insertBefore (jDiv.children ().first ()); + jQuery.ajax ({ + type: "POST", + url: uri, + cache: false, + async: true, + data: params, + success: function (response) { + jDiv.html (trim (response)); + } + }); +} + +// ======================================== diff --git a/style.css b/style.css new file mode 100644 index 0000000..1b9936a --- /dev/null +++ b/style.css @@ -0,0 +1,150 @@ +/* + * @license http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.html + * @author Francois + */ + +/* ==================== */ +body { +counter-reset: glossary; +} + +div.glossary table { + border-collapse: collapse; + border-spacing: 0; +} + +div.glossary table tr { + vertical-align: middle; +} + +div.glossary table tr.title { + background-color: #DDF; + text-align: center; +} + +div.glossary table tr.odd { +} + +div.glossary table tr.even { + background-color: #DDD; +} + +div.glossary table th, +div.glossary table td { + margin: 2px 4px; + padding: 2px 4px; +} + +div.glossary table input.text { + width: 20em; +} + +span.glossaryWord, +span.glossaryWord a.wikilink1:visited, +span.glossaryWord a.wikilink1:link { + color: #FE0000 !important; + font-weight: bold; +} + +span.glossaryTranslate, +span.glossaryTranslate a.wikilink1:visited, +span.glossaryTranslate a.wikilink1:link { + color: #004C9A !important; + font-weight: bold; +} + +div.glossary table th { + counter-reset: ch1; +} + +div.glossary table td.count:before { + counter-increment: glossary; + content: counter(glossary) " "; +} + +div.glossary table td.count { + color: #BBB; + white-space: nowrap; +} + +div.glossary table td.word, +div.glossary table td.word a:visited, +div.glossary table td.word a:link { + color: #FE0000; + font-weight: bold; +} + +div.glossary table td.translate, +div.glossary table td.translate a:visited, +div.glossary table td.translate a:link { + color: #004C9A; + font-weight: bold; +} + +div.glossary table textarea.why { + width: 50em; + height: 6em; +} + +div.glossary table td.poll { + white-space: nowrap; +} + +div.glossary table td.right { + text-align: right; +} + +div.glossary .popup { + position: absolute; + top: auto; + left: auto; + width: auto; + height: auto; + margin: 1em; + padding: 1em; + overflow: visible; + z-index: 50; + opacity: 0.7; + background: #EDDB31; + color: black; + border: 1px solid #AE090D; + font-weight: bold; +} + +div.glossary .toolTip a, +div.glossary .toolTip form { + cursor: help; + z-index: 1; +} + +div.glossary .toolTip a span, +div.glossary .toolTip form span { + position: absolute; + z-index: 50; + top: -2000em; + left: -2000em; + width: 1px; + height: 1px; + overflow: hidden; + white-space: nowrap; + opacity: 0.8; + border: 1px solid #AE090D; + background-color: #EDDB31; + color: #AE090D; + font-weight: normal; +} + +div.glossary .toolTip a:hover span, +div.glossary .toolTip form:hover span { + top: auto; + left: auto; + left: auto; + width: auto; + height: auto; + overflow: visible; + margin-top: 2em; + margin-left: 0em; + padding: 0 1em; +} + +/* ==================== */ diff --git a/syntax/div.php b/syntax/div.php new file mode 100644 index 0000000..fb22773 --- /dev/null +++ b/syntax/div.php @@ -0,0 +1,119 @@ + + * + * 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/'); +require_once(DOKU_PLUGIN.'syntax.php'); + +require_once (dirname (__FILE__).'/../glossary.class.php'); + +// ============================================================ +class syntax_plugin_glossary_div extends DokuWiki_Syntax_Plugin { + + // ============================================================ + function getType () { return 'substition'; } + function getPType () { return 'block'; } + function getAllowedTypes () { return array ('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); } + function getSort () { return 309; } + function connectTo ($mode) { + $this->Lexer->addSpecialPattern ('\{\{glossary[^}]*\}\}', $mode, 'plugin_glossary_div'); + } + + // ============================================================ + function handle ($match, $state, $pos, Doku_Handler $handler) { + switch ($state) { + case DOKU_LEXER_SPECIAL : + return array ($state, trim (substr ($match, 10, -2))); // "{{glossary" => 10 "}}" => 2 + } + return false; + } + + // ============================================================ + function render ($mode, Doku_Renderer $renderer, $indata) { + if ($mode != 'xhtml') + return false; + list ($state, $data) = $indata; + $data = " ".$data." "; + $renderer->info ['cache'] = FALSE; + $cmd = ""; + $arg = ""; + + if (preg_match_all ("#(\"[^\"]*\")* help (\"[^\"]*\")*#", strtolower ($data), $dumy) > 0) { + $this->help ($renderer); + return true; + } + // namespace + global $ID; + $ns = getNS ($ID); + if (preg_match_all ("#>([^ ]*) .*#", strtolower ($data), $dumy) > 0) { + $ns = $dumy[1][0]; + if (($ns == '*') || ($ns == ':')) + $ns = ''; + elseif ($ns == '.') + $ns = getNS ($ID); + else + $ns = cleanID ($ns); + } + if (preg_match_all ("#(\"[^\"]*\")* sample (\"[^\"]*\")*#", strtolower ($data), $dumy) > 0) + $cmd = "printSample"; + elseif (preg_match_all ("#(\"[^\"]*\")* list (\"[^\"]*\")*#", strtolower ($data), $dumy) > 0) + $cmd = "printList"; + elseif (preg_match_all ("#(\"[^\"]*\")* poll=\"(?[^\"]*)\" (\"[^\"]*\")*#", $data, $dumy) > 0) { + $cmd = "printPoll"; + $arg = $dumy['word'][0]; + } elseif (preg_match_all ("#(\"[^\"]*\")* proposal (\"[^\"]*\")*#", strtolower ($data), $dumy) > 0) + $cmd = "printProposal"; + elseif (preg_match_all ("#(\"[^\"]*\")* admin-proposal (\"[^\"]*\")*#", strtolower ($data), $dumy) > 0) + $cmd = "adminProposal"; + elseif (preg_match_all ("#(\"[^\"]*\")* admin-definition (\"[^\"]*\")*#", strtolower ($data), $dumy) > 0) + $cmd = "adminDefinition"; + elseif (preg_match_all ("#(\"[^\"]*\")* admin-glossaries (\"[^\"]*\")*#", strtolower ($data), $dumy) > 0) + $cmd = "adminGlossaries"; + else { + $this->help ($renderer); + return true; + } + + $glossary = new glossary ($this, $ns); + ob_start (); + $glossary->$cmd ($arg); + $text = ob_get_contents(); + ob_end_clean (); + foreach ($glossary->message as $type => $msg) + $text = '
'.$msg.'
'.$text; + $renderer->doc .= '
'.$text.'
'; + return true; + } + + // ============================================================ + function isAdmin () { + global $INFO; + return + isset ($INFO ['userinfo']) && + isset ($INFO ['userinfo']['grps']) && + in_array (trim ($this->getConf ('adminGroup')), $INFO ['userinfo']['grps']); + } + + // ============================================================ + function help (Doku_Renderer $renderer) { + $url = "http://admin.parlenet.org/plugins/glossary/"; + $renderer->doc .= + '

Help Glossary

'.NL. + '
    '.NL. + '
  • {{glossary>namespace [help] (sample|list|proposal|admin-proposal|admin-definition|admin-glossaries)}}
  • '.NL. + '
  • <glossary (help|clock|face-smile|face-sad|stop|one-way|search) />
  • '.NL. + '
  • <glossary (word|translate) > ... </glossary>
  • '.NL. + '
'.NL. + '

'.$url.'

'.NL; + } + + // ============================================================ +} // syntax_plugin_GLOSSARY +?> diff --git a/syntax/span.php b/syntax/span.php new file mode 100644 index 0000000..d065b9d --- /dev/null +++ b/syntax/span.php @@ -0,0 +1,131 @@ + + * + * 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/'); +require_once (DOKU_PLUGIN.'syntax.php'); + +require_once (dirname (__FILE__).'/../glossary.class.php'); + +// ============================================================ +class syntax_plugin_glossary_span extends DokuWiki_Syntax_Plugin { + + // ============================================================ + function getType () { return 'formatting'; } + function getPType () { return 'normal'; } + function getAllowedTypes() { return array ('formatting', 'substition', 'disabled'); } + function getSort () { return 195; } + function accepts ($mode) { + if ($mode == substr (get_class ($this), 7)) + return true; + return parent::accepts ($mode); + } + + function connectTo ($mode) { + $this->Lexer->addSpecialPattern ('', $mode, 'plugin_glossary_span'); + $this->Lexer->addEntryPattern ('(?=.*?)', $mode, 'plugin_glossary_span'); + } + function postConnect() { + $this->Lexer->addExitPattern ('', 'plugin_glossary_span'); + } + + // ============================================================ + function handle ($match, $state, $pos, Doku_Handler $handler) { + switch ($state) { + case DOKU_LEXER_SPECIAL : + $data = strtolower (trim (substr ($match, 9, -2))); + return array ($state, $data); + case DOKU_LEXER_ENTER: + $data = strtolower (trim (substr ($match, 9, -1))); + return array ($state, $data); + + case DOKU_LEXER_UNMATCHED : + $handler->_addCall ('cdata', array ($match), $pos); + return false; + + case DOKU_LEXER_EXIT : + return array ($state, ''); + } + return false; + } + + // ============================================================ + function render ($mode, Doku_Renderer $renderer, $indata) { + if (empty ($indata)) + return false; + if ($mode != 'xhtml') + false; + list ($state, $data) = $indata; + $data = trim ($data); + switch ($state) { + case DOKU_LEXER_SPECIAL : + $imgDir = DOKU_REL.'lib/plugins/glossary/images/'; + if ($data == "help") + $renderer->doc .= ''; + elseif ($data == "clock") + $renderer->doc .= ''; + elseif ($data == "face-smile") + $renderer->doc .= ''; + elseif ($data == "face-sad") + $renderer->doc .= ''; + elseif ($data == "stop") + $renderer->doc .= ''; + elseif ($data == "one-way") + $renderer->doc .= ''; + elseif ($data == "eye") + $renderer->doc .= ''; + elseif ($data == "score") + $renderer->doc .= ''; + elseif ($data == "search") + $renderer->doc .= ''; + elseif (preg_match_all ("#(?.*)\s+(?definition|proposal|poll)#", $data, $dumy) > 0) { + global $ID; + $ns = getNS ($ID); + if (count ($dumy ['ns']) > 0) { + $ns = $dumy['ns'][0]; + if (($ns == '*') || ($ns == ':')) + $ns = ''; + elseif ($ns == '.') + $ns = getNS ($ID); + else + $ns = cleanID ($ns); + } + $glossary = new glossary ($this, $ns); + switch ($dumy['type'][0]) { + case "definition": + $renderer->doc .= $glossary->getDefinitionSize (); + break; + case "proposal": + $renderer->doc .= $glossary->getProposalSize (); + break; + case "poll": + $renderer->doc .= $glossary->getPollSize (); + break; + } + } + break; + case DOKU_LEXER_ENTER: + if ($data == "word") + $renderer->doc .= ''; + elseif ($data == "translate") + $renderer->doc .= ''; + else + $renderer->doc .= ''; + break; + case DOKU_LEXER_EXIT: + $renderer->doc .= ""; + break; + } + return true; + } + + // ============================================================ +} // syntax_plugin_GLOSSARY +?>