administration function * * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Taggic */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); /** Implicit data type: * * ^Lang is an array that looks like the following * { "core": [ $lang... ], * "templates": [ $tpl_name: [ $lang... ], ... ], * "plugins": [ $plugin_name: [ $lang... ], ... ] * } * where $lang is a DokuWiki language code * $tpl_name is the template name * $plugin_name is the plugin name * The $lang arrays are zero-indexed */ /** CSS Classes: * * ul.languages is an inline list of language codes * if li.active is set, the text will be highlighted * if li.enabled is set, the text is normal, * otherwise it's red and striked-out * .module is set on text that represent module names: template names, * plugin names and "dokuwiki" * * #langshortlist is the list of language with checkboxes * #langlonglist is the list of list of languages available for each module * .langdelete__text is the class set on the section wrapper around all the text */ /** * All DokuWiki plugins to extend the admin function * need to inherit from this class */ class admin_plugin_langdelete extends DokuWiki_Admin_Plugin { /** Fallback language */ const DEFAULT_LANG = 'en'; /** data stdObject assigned by ->handle() and used in ->html() */ private $d; /** return sort order for position in admin menu */ function getMenuSort() { return 20; } /** Called when dispatching the DokuWiki action; * Puts the required data for ->html() in $->d */ function handle() { $d =& $this->d; $d = new stdClass; // reset $d->submit = isset($_REQUEST['submit']); $submit =& $d->submit; /* Check security token */ if ($submit) { $valid =& $d->valid; $valid = True; if (!checkSecurityToken()) { $valid = False; return; } } /* Set DokuWiki language info */ $d->langs = $this->list_languages(); $langs =& $d->langs; // $u_langs is in alphabetical (?) order because directory listing $d->u_langs = $this->lang_unique($langs); $u_langs =& $d->u_langs; /* Grab form data */ if ($submit) { $d->dryrun = $_REQUEST['dryrun']; $lang_str = $_REQUEST['langdelete_w']; } /* What languages do we keep ? */ $lang_keep[] = self::DEFAULT_LANG; // add 'en', the fallback $lang_keep[] = $conf['lang']; // add current lang if ($submit) { /* Add form data to languages to keep */ if (strlen ($lang_str) > 0) { $lang_keep = array_merge ($lang_keep, explode(',', $lang_str)); } } else { // Keep every language on first run $lang_keep = $u_langs; } $lang_keep = array_values(array_filter(array_unique($lang_keep))); $d->lang_keep =& $lang_keep; /* Does the language we want to keep actually exist ? */ $non_langs = array_diff ($lang_keep, $u_langs); if ($non_langs) { $d->nolang_s = implode (",", $non_langs); } /* Prepare data for deletion */ if ($submit) { $d->langs_to_delete = $this->_filter_out_lang ($langs, $lang_keep); } /* What do the checkboxes say ? */ if ($submit) { /* Grab checkboxes */ $d->shortlang = array_keys ($_REQUEST['shortlist']); $shortlang =& $d->shortlang; /* Prevent discrepancy between shortlist and text form */ if (array_diff ($lang_keep, $shortlang) || array_diff ($shortlang, $lang_keep)) { $d->discrepancy = True; } } else { // Keep every language on first run $d->shortlang = $u_langs; } } /** * langdelete Output function * * Prints a table with all found language folders. * HTML and data processing are done here at the same time * * @author Taggic */ function html() { global $conf; // access DW configuration array $d =& $this->d; // from ->handle() // In case we want to fetch the files from gh #$version = getVersionData(); // langdelete__intro echo $this->locale_xhtml('intro'); // input anchor echo ''.NL; echo $this->locale_xhtml('guide'); // input form $this->_html_form($d); $langs = $this->list_languages(); $u_langs = $this->lang_unique($langs); /* Switch on form submission state */ if (!$d->submit) { /* Show available languages */ echo '
'; echo $this->getLang('available_langs'); $this->print_shortlist ($d); $this->html_print_langs($d->langs); echo '
'; } else { /* Process form */ /* Check token */ if (!$d->valid) { echo "

Invalid security token

"; return; } if ($d->discrepancy) { msg($this->getLang('discrepancy_warn'), 2); } if ($d->nolang_s) { msg($this->getLang('nolang') . $d->nolang_s , 2); } echo '

'.$this->getLang('h2_output').'

'.NL; if ($d->dryrun) { /* Display what will be deleted */ msg($this->getLang('langdelete_willmsg'), 2); echo '
'; echo $this->getLang('available_langs'); $this->print_shortlist ($d); $this->html_print_langs($d->langs, $d->lang_keep); echo '
'; msg($this->getLang('langdelete_attention'), 2); echo ''.$this->getLang('backto_inputbox').''.NL; } else { /* Delete and report what was deleted */ msg($this->getLang('langdelete_delmsg'), 0); echo '
'; $this->html_print_langs($d->langs_to_delete); echo '
'; echo '
';
                $this->remove_langs($d->langs_to_delete);
                echo '
'; } } } /** * Display the form with input control to let the user specify, * which languages to be kept beside en * * @author Taggic */ private function _html_form (&$d) { global $ID, $conf; echo '
'; echo ''.NL; echo ''.NL; formSecurityToken(); echo '
'.$this->getLang('i_legend').''.NL; echo ''; echo '
'.$conf['lang'].'
'.NL; echo ''; echo ''.NL; echo ''; echo '
'.NL; echo ' '; echo ''.NL; echo '
'.NL; echo ''.NL; echo '
'.NL; echo '
'.NL; } /** Print the language shortlist and cross-out those not in $keep */ function print_shortlist (&$d) { $shortlang =& $d->shortlang; echo '
    '; # As the disabled input won't POST echo ''; foreach ($d->u_langs as $l) { $checked = in_array($l, $shortlang) || $l == self::DEFAULT_LANG; echo ''; echo ''; echo ''; echo ''; } echo '
'; } /** Display the languages in $langs for each module as a HTML list; * Cross-out those not in $keep * * Signature: ^Lang, Array => () */ private function html_print_langs ($langs, $keep = null) { /* Print language list, $langs being an array; * Cross out those not in $keep */ $print_lang_li = function ($langs) use ($keep) { echo '
    '; foreach ($langs as $val) { // If $keep is null, we keep everything $enabled = is_null($keep) || in_array ($val, $keep); echo '
  • '; if ($enabled) { echo $val; } else { echo ''.$val.''; } echo '
  • '; } echo '
'; }; echo '
    '; // Core echo '
  • '.$this->getLang('dokuwiki_core').''; $print_lang_li ($langs['core']); echo '
  • '; // Templates echo '
  • '.$this->getLang('templates'); echo '
      '; foreach ($langs['templates'] as $name => $l) { echo '
    • '.$name.':'; $print_lang_li ($l); echo '
    • '; } echo '
    '; echo '
  • '; // Plugins echo '
  • '.$this->getLang('plugins'); echo '
      '; foreach ($langs['plugins'] as $name => $l) { echo '
    • '.$name.':'; $print_lang_li ($l); echo '
    • '; } echo '
    '; echo '
  • '; echo '
'; } /** Returns the available languages for each module * (core, template or plugin) * * Signature: () => ^Lang */ private function list_languages () { // See https://www.dokuwiki.org/devel:localization /* Returns the subfolders of $dir as an array */ $dir_subfolders = function ($dir) { $sub = scandir($dir); $sub = array_filter ($sub, function ($e) use ($dir) { return is_dir ("$dir/$e") && !in_array ($e, array('.', '..')); } ); return $sub; }; /* Return an array of template names */ $list_templates = function () use ($dir_subfolders) { return $dir_subfolders (DOKU_INC."lib/tpl"); }; /* Return an array of languages available for the module * (core, template or plugin) given its $root directory */ $list_langs = function ($root) use ($dir_subfolders) { $dir = "$root/lang"; if (!is_dir ($dir)) return; return $dir_subfolders ($dir); }; /* Get templates and plugins names */ global $plugin_controller; $plugins = $plugin_controller->getList(); $templates = $list_templates(); return array( "core" => $list_langs (DOKU_INC."inc"), "templates" => array_combine ($templates, array_map ($list_langs, array_prefix ($templates, DOKU_INC."lib/tpl/"))), "plugins" => array_combine ($plugins, array_map ($list_langs, array_prefix ($plugins, DOKU_PLUGIN))) ); } /** Remove $lang_keep from the module languages $e * * Signature: ^Lang, Array => ^Lang */ private function _filter_out_lang ($e, $lang_keep) { // Recursive function with cases being an array of arrays, or an array if (count ($e) > 0 && is_array (array_values($e)[0])) { foreach ($e as $k => $elt) { $out[$k] = $this->_filter_out_lang ($elt, $lang_keep); } return $out; } else { return array_filter ($e, function ($v) use ($lang_keep) { return !in_array ($v, $lang_keep); }); } } /** Return an array of the languages in $l * * Signature: ^Lang => Array */ private function lang_unique ($l) { foreach ($l['core'] as $lang) { $count[$lang]++; } foreach ($l['templates'] as $tpl => $arr) { foreach ($arr as $lang) { $count[$lang]++; } } foreach ($l['plugins'] as $plug => $arr) { foreach ($arr as $lang) { $count[$lang]++; } } return array_keys ($count); } /** Delete the languages from the modules as specified by $langs * * Signature: ^Lang => () */ private function remove_langs($langs) { foreach ($langs['core'] as $l) { $this->rrm(DOKU_INC."inc/lang/$l"); } foreach ($langs['templates'] as $tpl => $arr) { foreach ($arr as $l) { $this->rrm(DOKU_INC."lib/tpl/$tpl/lang/$l"); } } foreach ($langs['plugins'] as $plug => $arr) { foreach ($arr as $l) { $this->rrm(DOKU_INC."lib/plugins/$plug/lang/$l"); } } } /** Recursive file removal of $path with reporting */ private function rrm ($path) { if (is_dir ($path)) { $objects = scandir ($path); foreach ($objects as $object) { if (!in_array ($object, array('.', '..'))) { $this->rrm("$path/$object"); } } $sucess = @rmdir ($path); if (!$sucess) { echo "Failed to delete $path/\n"; } else echo "Delete $path\n"; } else { $sucess = @unlink ($path); if (!$sucess) { echo "Failed to delete $path\n"; } else echo "Delete $path\n"; } } } /** Returns an array with each element of $arr prefixed with $prefix */ function array_prefix ($arr, $prefix) { return array_map ( function ($p) use ($prefix) { return $prefix.$p; }, $arr); }