| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 3 | //
 | 
        
           |  |  | 4 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 5 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 6 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 7 | // (at your option) any later version.
 | 
        
           |  |  | 8 | //
 | 
        
           |  |  | 9 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 12 | // GNU General Public License for more details.
 | 
        
           |  |  | 13 | //
 | 
        
           |  |  | 14 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 15 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | /**
 | 
        
           |  |  | 18 |  * Definition of classes used by language customization admin tool
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * @package    tool
 | 
        
           |  |  | 21 |  * @subpackage customlang
 | 
        
           |  |  | 22 |  * @copyright  2010 David Mudrak <david@moodle.com>
 | 
        
           |  |  | 23 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 24 |  */
 | 
        
           |  |  | 25 |   | 
        
           |  |  | 26 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 27 |   | 
        
           |  |  | 28 | /**
 | 
        
           |  |  | 29 |  * Provides various utilities to be used by the plugin
 | 
        
           |  |  | 30 |  *
 | 
        
           |  |  | 31 |  * All the public methods here are static ones, this class can not be instantiated
 | 
        
           |  |  | 32 |  */
 | 
        
           |  |  | 33 | class tool_customlang_utils {
 | 
        
           |  |  | 34 |   | 
        
           |  |  | 35 |     /**
 | 
        
           |  |  | 36 |      * Rough number of strings that are being processed during a full checkout.
 | 
        
           |  |  | 37 |      * This is used to estimate the progress of the checkout.
 | 
        
           |  |  | 38 |      */
 | 
        
           | 1441 | ariadna | 39 |     const ROUGH_NUMBER_OF_STRINGS = 33000;
 | 
        
           | 1 | efrain | 40 |   | 
        
           |  |  | 41 |     /** @var array cache of {@link self::list_components()} results */
 | 
        
           |  |  | 42 |     private static $components = null;
 | 
        
           |  |  | 43 |   | 
        
           |  |  | 44 |     /**
 | 
        
           |  |  | 45 |      * This class can not be instantiated
 | 
        
           |  |  | 46 |      */
 | 
        
           |  |  | 47 |     private function __construct() {
 | 
        
           |  |  | 48 |     }
 | 
        
           |  |  | 49 |   | 
        
           |  |  | 50 |     /**
 | 
        
           |  |  | 51 |      * Returns a list of all components installed on the server
 | 
        
           |  |  | 52 |      *
 | 
        
           |  |  | 53 |      * @return array (string)legacyname => (string)frankenstylename
 | 
        
           |  |  | 54 |      */
 | 
        
           |  |  | 55 |     public static function list_components() {
 | 
        
           |  |  | 56 |   | 
        
           |  |  | 57 |         if (self::$components === null) {
 | 
        
           |  |  | 58 |             $list['moodle'] = 'core';
 | 
        
           |  |  | 59 |   | 
        
           |  |  | 60 |             $coresubsystems = core_component::get_core_subsystems();
 | 
        
           |  |  | 61 |             ksort($coresubsystems); // Should be but just in case.
 | 
        
           |  |  | 62 |             foreach ($coresubsystems as $name => $location) {
 | 
        
           |  |  | 63 |                 $list[$name] = 'core_' . $name;
 | 
        
           |  |  | 64 |             }
 | 
        
           |  |  | 65 |   | 
        
           |  |  | 66 |             $plugintypes = core_component::get_plugin_types();
 | 
        
           |  |  | 67 |             foreach ($plugintypes as $type => $location) {
 | 
        
           |  |  | 68 |                 $pluginlist = core_component::get_plugin_list($type);
 | 
        
           |  |  | 69 |                 foreach ($pluginlist as $name => $ununsed) {
 | 
        
           |  |  | 70 |                     if ($type == 'mod') {
 | 
        
           |  |  | 71 |                         // Plugin names are now automatically validated.
 | 
        
           |  |  | 72 |                         $list[$name] = $type . '_' . $name;
 | 
        
           |  |  | 73 |                     } else {
 | 
        
           |  |  | 74 |                         $list[$type . '_' . $name] = $type . '_' . $name;
 | 
        
           |  |  | 75 |                     }
 | 
        
           |  |  | 76 |                 }
 | 
        
           |  |  | 77 |             }
 | 
        
           |  |  | 78 |             self::$components = $list;
 | 
        
           |  |  | 79 |         }
 | 
        
           |  |  | 80 |         return self::$components;
 | 
        
           |  |  | 81 |     }
 | 
        
           |  |  | 82 |   | 
        
           |  |  | 83 |     /**
 | 
        
           |  |  | 84 |      * Updates the translator database with the strings from files
 | 
        
           |  |  | 85 |      *
 | 
        
           |  |  | 86 |      * This should be executed each time before going to the translation page
 | 
        
           |  |  | 87 |      *
 | 
        
           |  |  | 88 |      * @param string $lang language code to checkout
 | 
        
           |  |  | 89 |      * @param progress_bar $progressbar optionally, the given progress bar can be updated
 | 
        
           |  |  | 90 |      */
 | 
        
           | 1441 | ariadna | 91 |     public static function checkout($lang, ?progress_bar $progressbar = null) {
 | 
        
           | 1 | efrain | 92 |         global $DB, $CFG;
 | 
        
           |  |  | 93 |   | 
        
           |  |  | 94 |         require_once("{$CFG->libdir}/adminlib.php");
 | 
        
           |  |  | 95 |   | 
        
           |  |  | 96 |         // For behat executions we are going to load only a few components in the
 | 
        
           |  |  | 97 |         // language customisation structures. Using the whole "en" langpack is
 | 
        
           |  |  | 98 |         // too much slow (leads to Selenium 30s timeouts, especially on slow
 | 
        
           |  |  | 99 |         // environments) and we don't really need the whole thing for tests. So,
 | 
        
           |  |  | 100 |         // apart from escaping from the timeouts, we are also saving some good minutes
 | 
        
           |  |  | 101 |         // in tests. See MDL-70014 and linked issues for more info.
 | 
        
           |  |  | 102 |         $behatneeded = ['core', 'core_langconfig', 'tool_customlang'];
 | 
        
           |  |  | 103 |   | 
        
           |  |  | 104 |         // make sure that all components are registered
 | 
        
           |  |  | 105 |         $current = $DB->get_records('tool_customlang_components', null, 'name', 'name,version,id');
 | 
        
           |  |  | 106 |         foreach (self::list_components() as $component) {
 | 
        
           |  |  | 107 |             // Filter out unwanted components when running behat.
 | 
        
           |  |  | 108 |             if (defined('BEHAT_SITE_RUNNING') && !in_array($component, $behatneeded)) {
 | 
        
           |  |  | 109 |                 continue;
 | 
        
           |  |  | 110 |             }
 | 
        
           |  |  | 111 |   | 
        
           |  |  | 112 |             if (empty($current[$component])) {
 | 
        
           |  |  | 113 |                 $record = new stdclass();
 | 
        
           |  |  | 114 |                 $record->name = $component;
 | 
        
           |  |  | 115 |                 if (!$version = get_component_version($component)) {
 | 
        
           |  |  | 116 |                     $record->version = null;
 | 
        
           |  |  | 117 |                 } else {
 | 
        
           |  |  | 118 |                     $record->version = $version;
 | 
        
           |  |  | 119 |                 }
 | 
        
           |  |  | 120 |                 $DB->insert_record('tool_customlang_components', $record);
 | 
        
           |  |  | 121 |             } else if ($version = get_component_version($component)) {
 | 
        
           |  |  | 122 |                 if (is_null($current[$component]->version) or ($version > $current[$component]->version)) {
 | 
        
           |  |  | 123 |                     $DB->set_field('tool_customlang_components', 'version', $version, array('id' => $current[$component]->id));
 | 
        
           |  |  | 124 |                 }
 | 
        
           |  |  | 125 |             }
 | 
        
           |  |  | 126 |         }
 | 
        
           |  |  | 127 |         unset($current);
 | 
        
           |  |  | 128 |   | 
        
           |  |  | 129 |         // initialize the progress counter - stores the number of processed strings
 | 
        
           |  |  | 130 |         $done = 0;
 | 
        
           |  |  | 131 |         $strinprogress = get_string('checkoutinprogress', 'tool_customlang');
 | 
        
           |  |  | 132 |   | 
        
           |  |  | 133 |         // reload components and fetch their strings
 | 
        
           |  |  | 134 |         $stringman  = get_string_manager();
 | 
        
           |  |  | 135 |         $components = $DB->get_records('tool_customlang_components');
 | 
        
           |  |  | 136 |         foreach ($components as $component) {
 | 
        
           |  |  | 137 |             $sql = "SELECT stringid, id, lang, componentid, original, master, local, timemodified, timecustomized, outdated, modified
 | 
        
           |  |  | 138 |                       FROM {tool_customlang} s
 | 
        
           |  |  | 139 |                      WHERE lang = ? AND componentid = ?
 | 
        
           |  |  | 140 |                   ORDER BY stringid";
 | 
        
           |  |  | 141 |             $current = $DB->get_records_sql($sql, array($lang, $component->id));
 | 
        
           |  |  | 142 |             $english = $stringman->load_component_strings($component->name, 'en', true, true);
 | 
        
           |  |  | 143 |             if ($lang == 'en') {
 | 
        
           |  |  | 144 |                 $master =& $english;
 | 
        
           |  |  | 145 |             } else {
 | 
        
           |  |  | 146 |                 $master = $stringman->load_component_strings($component->name, $lang, true, true);
 | 
        
           |  |  | 147 |             }
 | 
        
           |  |  | 148 |             $local = $stringman->load_component_strings($component->name, $lang, true, false);
 | 
        
           |  |  | 149 |   | 
        
           |  |  | 150 |             foreach ($english as $stringid => $stringoriginal) {
 | 
        
           |  |  | 151 |                 $stringmaster = isset($master[$stringid]) ? $master[$stringid] : null;
 | 
        
           |  |  | 152 |                 $stringlocal = isset($local[$stringid]) ? $local[$stringid] : null;
 | 
        
           |  |  | 153 |                 $now = time();
 | 
        
           |  |  | 154 |   | 
        
           |  |  | 155 |                 if (!is_null($progressbar)) {
 | 
        
           |  |  | 156 |                     $done++;
 | 
        
           |  |  | 157 |                     $donepercent = floor(min($done, self::ROUGH_NUMBER_OF_STRINGS) / self::ROUGH_NUMBER_OF_STRINGS * 100);
 | 
        
           |  |  | 158 |                     $progressbar->update_full($donepercent, $strinprogress);
 | 
        
           |  |  | 159 |                 }
 | 
        
           |  |  | 160 |   | 
        
           |  |  | 161 |                 if (isset($current[$stringid])) {
 | 
        
           |  |  | 162 |                     $needsupdate     = false;
 | 
        
           |  |  | 163 |                     $currentoriginal = $current[$stringid]->original;
 | 
        
           |  |  | 164 |                     $currentmaster   = $current[$stringid]->master;
 | 
        
           |  |  | 165 |                     $currentlocal    = $current[$stringid]->local;
 | 
        
           |  |  | 166 |   | 
        
           |  |  | 167 |                     if ($currentoriginal !== $stringoriginal or $currentmaster !== $stringmaster) {
 | 
        
           |  |  | 168 |                         $needsupdate = true;
 | 
        
           |  |  | 169 |                         $current[$stringid]->original       = $stringoriginal;
 | 
        
           |  |  | 170 |                         $current[$stringid]->master         = $stringmaster;
 | 
        
           |  |  | 171 |                         $current[$stringid]->timemodified   = $now;
 | 
        
           |  |  | 172 |                         $current[$stringid]->outdated       = 1;
 | 
        
           |  |  | 173 |                     }
 | 
        
           |  |  | 174 |   | 
        
           |  |  | 175 |                     if ($stringmaster !== $stringlocal) {
 | 
        
           |  |  | 176 |                         $needsupdate = true;
 | 
        
           |  |  | 177 |                         $current[$stringid]->local          = $stringlocal;
 | 
        
           |  |  | 178 |                         $current[$stringid]->timecustomized = $now;
 | 
        
           |  |  | 179 |                     } else if (isset($currentlocal) && $stringlocal !== $currentlocal) {
 | 
        
           |  |  | 180 |                         // If local string has been removed, we need to remove also the old local value from DB.
 | 
        
           |  |  | 181 |                         $needsupdate = true;
 | 
        
           |  |  | 182 |                         $current[$stringid]->local          = null;
 | 
        
           |  |  | 183 |                         $current[$stringid]->timecustomized = $now;
 | 
        
           |  |  | 184 |                     }
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 |                     if ($needsupdate) {
 | 
        
           |  |  | 187 |                         $DB->update_record('tool_customlang', $current[$stringid]);
 | 
        
           |  |  | 188 |                         continue;
 | 
        
           |  |  | 189 |                     }
 | 
        
           |  |  | 190 |   | 
        
           |  |  | 191 |                 } else {
 | 
        
           |  |  | 192 |                     $record                 = new stdclass();
 | 
        
           |  |  | 193 |                     $record->lang           = $lang;
 | 
        
           |  |  | 194 |                     $record->componentid    = $component->id;
 | 
        
           |  |  | 195 |                     $record->stringid       = $stringid;
 | 
        
           |  |  | 196 |                     $record->original       = $stringoriginal;
 | 
        
           |  |  | 197 |                     $record->master         = $stringmaster;
 | 
        
           |  |  | 198 |                     $record->timemodified   = $now;
 | 
        
           |  |  | 199 |                     $record->outdated       = 0;
 | 
        
           |  |  | 200 |                     if ($stringmaster !== $stringlocal) {
 | 
        
           |  |  | 201 |                         $record->local          = $stringlocal;
 | 
        
           |  |  | 202 |                         $record->timecustomized = $now;
 | 
        
           |  |  | 203 |                     } else {
 | 
        
           |  |  | 204 |                         $record->local          = null;
 | 
        
           |  |  | 205 |                         $record->timecustomized = null;
 | 
        
           |  |  | 206 |                     }
 | 
        
           |  |  | 207 |   | 
        
           |  |  | 208 |                     $DB->insert_record('tool_customlang', $record);
 | 
        
           |  |  | 209 |                 }
 | 
        
           |  |  | 210 |             }
 | 
        
           |  |  | 211 |         }
 | 
        
           |  |  | 212 |   | 
        
           |  |  | 213 |         if (!is_null($progressbar)) {
 | 
        
           |  |  | 214 |             $progressbar->update_full(100, get_string('checkoutdone', 'tool_customlang'));
 | 
        
           |  |  | 215 |         }
 | 
        
           |  |  | 216 |     }
 | 
        
           |  |  | 217 |   | 
        
           |  |  | 218 |     /**
 | 
        
           |  |  | 219 |      * Exports the translator database into disk files
 | 
        
           |  |  | 220 |      *
 | 
        
           |  |  | 221 |      * @param mixed $lang language code
 | 
        
           |  |  | 222 |      */
 | 
        
           |  |  | 223 |     public static function checkin($lang) {
 | 
        
           |  |  | 224 |         global $DB, $USER, $CFG;
 | 
        
           |  |  | 225 |         require_once($CFG->libdir.'/filelib.php');
 | 
        
           |  |  | 226 |   | 
        
           |  |  | 227 |         if ($lang !== clean_param($lang, PARAM_LANG)) {
 | 
        
           |  |  | 228 |             return false;
 | 
        
           |  |  | 229 |         }
 | 
        
           |  |  | 230 |   | 
        
           |  |  | 231 |         list($insql, $inparams) = $DB->get_in_or_equal(self::list_components());
 | 
        
           |  |  | 232 |   | 
        
           |  |  | 233 |         // Get all customized strings from updated valid components.
 | 
        
           |  |  | 234 |         $sql = "SELECT s.*, c.name AS component
 | 
        
           |  |  | 235 |                   FROM {tool_customlang} s
 | 
        
           |  |  | 236 |                   JOIN {tool_customlang_components} c ON s.componentid = c.id
 | 
        
           |  |  | 237 |                  WHERE s.lang = ?
 | 
        
           |  |  | 238 |                        AND (s.local IS NOT NULL OR s.modified = 1)
 | 
        
           |  |  | 239 |                        AND c.name $insql
 | 
        
           |  |  | 240 |               ORDER BY componentid, stringid";
 | 
        
           |  |  | 241 |         array_unshift($inparams, $lang);
 | 
        
           |  |  | 242 |         $strings = $DB->get_records_sql($sql, $inparams);
 | 
        
           |  |  | 243 |   | 
        
           |  |  | 244 |         $files = array();
 | 
        
           |  |  | 245 |         foreach ($strings as $string) {
 | 
        
           |  |  | 246 |             if (!is_null($string->local)) {
 | 
        
           |  |  | 247 |                 $files[$string->component][$string->stringid] = $string->local;
 | 
        
           |  |  | 248 |             }
 | 
        
           |  |  | 249 |         }
 | 
        
           |  |  | 250 |   | 
        
           |  |  | 251 |         fulldelete(self::get_localpack_location($lang));
 | 
        
           |  |  | 252 |         foreach ($files as $component => $strings) {
 | 
        
           |  |  | 253 |             self::dump_strings($lang, $component, $strings);
 | 
        
           |  |  | 254 |         }
 | 
        
           |  |  | 255 |   | 
        
           |  |  | 256 |         $DB->set_field_select('tool_customlang', 'modified', 0, 'lang = ?', array($lang));
 | 
        
           |  |  | 257 |         $sm = get_string_manager();
 | 
        
           |  |  | 258 |         $sm->reset_caches();
 | 
        
           |  |  | 259 |     }
 | 
        
           |  |  | 260 |   | 
        
           |  |  | 261 |     /**
 | 
        
           |  |  | 262 |      * Returns full path to the directory where local packs are dumped into
 | 
        
           |  |  | 263 |      *
 | 
        
           |  |  | 264 |      * @param string $lang language code
 | 
        
           |  |  | 265 |      * @return string full path
 | 
        
           |  |  | 266 |      */
 | 
        
           |  |  | 267 |     public static function get_localpack_location($lang) {
 | 
        
           |  |  | 268 |         global $CFG;
 | 
        
           |  |  | 269 |   | 
        
           |  |  | 270 |         return $CFG->langlocalroot.'/'.$lang.'_local';
 | 
        
           |  |  | 271 |     }
 | 
        
           |  |  | 272 |   | 
        
           |  |  | 273 |     /**
 | 
        
           |  |  | 274 |      * Writes strings into a local language pack file
 | 
        
           |  |  | 275 |      *
 | 
        
           |  |  | 276 |      * @param string $component the name of the component
 | 
        
           |  |  | 277 |      * @param array $strings
 | 
        
           |  |  | 278 |      * @return void
 | 
        
           |  |  | 279 |      */
 | 
        
           |  |  | 280 |     protected static function dump_strings($lang, $component, $strings) {
 | 
        
           |  |  | 281 |         global $CFG;
 | 
        
           |  |  | 282 |   | 
        
           |  |  | 283 |         if ($lang !== clean_param($lang, PARAM_LANG)) {
 | 
        
           |  |  | 284 |             throw new moodle_exception('Unable to dump local strings for non-installed language pack .'.s($lang));
 | 
        
           |  |  | 285 |         }
 | 
        
           |  |  | 286 |         if ($component !== clean_param($component, PARAM_COMPONENT)) {
 | 
        
           |  |  | 287 |             throw new coding_exception('Incorrect component name');
 | 
        
           |  |  | 288 |         }
 | 
        
           |  |  | 289 |         if (!$filename = self::get_component_filename($component)) {
 | 
        
           |  |  | 290 |             throw new moodle_exception('Unable to find the filename for the component '.s($component));
 | 
        
           |  |  | 291 |         }
 | 
        
           |  |  | 292 |         if ($filename !== clean_param($filename, PARAM_FILE)) {
 | 
        
           |  |  | 293 |             throw new coding_exception('Incorrect file name '.s($filename));
 | 
        
           |  |  | 294 |         }
 | 
        
           |  |  | 295 |         list($package, $subpackage) = core_component::normalize_component($component);
 | 
        
           |  |  | 296 |         $packageinfo = " * @package    $package";
 | 
        
           |  |  | 297 |         if (!is_null($subpackage)) {
 | 
        
           |  |  | 298 |             $packageinfo .= "\n * @subpackage $subpackage";
 | 
        
           |  |  | 299 |         }
 | 
        
           |  |  | 300 |         $filepath = self::get_localpack_location($lang);
 | 
        
           |  |  | 301 |         $filepath = $filepath.'/'.$filename;
 | 
        
           |  |  | 302 |         if (!is_dir(dirname($filepath))) {
 | 
        
           |  |  | 303 |             check_dir_exists(dirname($filepath));
 | 
        
           |  |  | 304 |         }
 | 
        
           |  |  | 305 |   | 
        
           |  |  | 306 |         if (!$f = fopen($filepath, 'w')) {
 | 
        
           |  |  | 307 |             throw new moodle_exception('Unable to write '.s($filepath));
 | 
        
           |  |  | 308 |         }
 | 
        
           |  |  | 309 |         fwrite($f, <<<EOF
 | 
        
           |  |  | 310 | <?php
 | 
        
           |  |  | 311 |   | 
        
           |  |  | 312 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 313 | //
 | 
        
           |  |  | 314 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 315 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 316 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 317 | // (at your option) any later version.
 | 
        
           |  |  | 318 | //
 | 
        
           |  |  | 319 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 320 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 321 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 322 | // GNU General Public License for more details.
 | 
        
           |  |  | 323 | //
 | 
        
           |  |  | 324 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 325 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 326 |   | 
        
           |  |  | 327 | /**
 | 
        
           |  |  | 328 |  * Local language pack from $CFG->wwwroot
 | 
        
           |  |  | 329 |  *
 | 
        
           |  |  | 330 | $packageinfo
 | 
        
           |  |  | 331 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 332 |  */
 | 
        
           |  |  | 333 |   | 
        
           |  |  | 334 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 335 |   | 
        
           |  |  | 336 |   | 
        
           |  |  | 337 | EOF
 | 
        
           |  |  | 338 |         );
 | 
        
           |  |  | 339 |   | 
        
           |  |  | 340 |         foreach ($strings as $stringid => $text) {
 | 
        
           |  |  | 341 |             if ($stringid !== clean_param($stringid, PARAM_STRINGID)) {
 | 
        
           |  |  | 342 |                 debugging('Invalid string identifier '.s($stringid));
 | 
        
           |  |  | 343 |                 continue;
 | 
        
           |  |  | 344 |             }
 | 
        
           |  |  | 345 |             fwrite($f, '$string[\'' . $stringid . '\'] = ');
 | 
        
           |  |  | 346 |             fwrite($f, var_export($text, true));
 | 
        
           |  |  | 347 |             fwrite($f, ";\n");
 | 
        
           |  |  | 348 |         }
 | 
        
           |  |  | 349 |         fclose($f);
 | 
        
           |  |  | 350 |         @chmod($filepath, $CFG->filepermissions);
 | 
        
           |  |  | 351 |     }
 | 
        
           |  |  | 352 |   | 
        
           |  |  | 353 |     /**
 | 
        
           |  |  | 354 |      * Returns the name of the file where the component's local strings should be exported into
 | 
        
           |  |  | 355 |      *
 | 
        
           |  |  | 356 |      * @param string $component normalized name of the component, eg 'core' or 'mod_workshop'
 | 
        
           |  |  | 357 |      * @return string|boolean filename eg 'moodle.php' or 'workshop.php', false if not found
 | 
        
           |  |  | 358 |      */
 | 
        
           |  |  | 359 |     protected static function get_component_filename($component) {
 | 
        
           |  |  | 360 |   | 
        
           |  |  | 361 |         $return = false;
 | 
        
           |  |  | 362 |         foreach (self::list_components() as $legacy => $normalized) {
 | 
        
           |  |  | 363 |             if ($component === $normalized) {
 | 
        
           |  |  | 364 |                 $return = $legacy.'.php';
 | 
        
           |  |  | 365 |                 break;
 | 
        
           |  |  | 366 |             }
 | 
        
           |  |  | 367 |         }
 | 
        
           |  |  | 368 |         return $return;
 | 
        
           |  |  | 369 |     }
 | 
        
           |  |  | 370 |   | 
        
           |  |  | 371 |     /**
 | 
        
           |  |  | 372 |      * Returns the number of modified strings checked out in the translator
 | 
        
           |  |  | 373 |      *
 | 
        
           |  |  | 374 |      * @param string $lang language code
 | 
        
           |  |  | 375 |      * @return int
 | 
        
           |  |  | 376 |      */
 | 
        
           |  |  | 377 |     public static function get_count_of_modified($lang) {
 | 
        
           |  |  | 378 |         global $DB;
 | 
        
           |  |  | 379 |   | 
        
           |  |  | 380 |         return $DB->count_records('tool_customlang', array('lang'=>$lang, 'modified'=>1));
 | 
        
           |  |  | 381 |     }
 | 
        
           |  |  | 382 |   | 
        
           |  |  | 383 |     /**
 | 
        
           |  |  | 384 |      * Saves filter data into a persistant storage such as user session
 | 
        
           |  |  | 385 |      *
 | 
        
           |  |  | 386 |      * @see self::load_filter()
 | 
        
           |  |  | 387 |      * @param stdclass $data filter values
 | 
        
           |  |  | 388 |      * @param stdclass $persistant storage object
 | 
        
           |  |  | 389 |      */
 | 
        
           |  |  | 390 |     public static function save_filter(stdclass $data, stdclass $persistant) {
 | 
        
           |  |  | 391 |         if (!isset($persistant->tool_customlang_filter)) {
 | 
        
           |  |  | 392 |             $persistant->tool_customlang_filter = array();
 | 
        
           |  |  | 393 |         }
 | 
        
           |  |  | 394 |         foreach ($data as $key => $value) {
 | 
        
           |  |  | 395 |             if ($key !== 'submit') {
 | 
        
           |  |  | 396 |                 $persistant->tool_customlang_filter[$key] = serialize($value);
 | 
        
           |  |  | 397 |             }
 | 
        
           |  |  | 398 |         }
 | 
        
           |  |  | 399 |     }
 | 
        
           |  |  | 400 |   | 
        
           |  |  | 401 |     /**
 | 
        
           |  |  | 402 |      * Loads the previously saved filter settings from a persistent storage
 | 
        
           |  |  | 403 |      *
 | 
        
           |  |  | 404 |      * @see self::save_filter()
 | 
        
           |  |  | 405 |      * @param stdclass $persistant storage object
 | 
        
           |  |  | 406 |      * @return stdclass filter data
 | 
        
           |  |  | 407 |      */
 | 
        
           |  |  | 408 |     public static function load_filter(stdclass $persistant) {
 | 
        
           |  |  | 409 |         $data = new stdclass();
 | 
        
           |  |  | 410 |         if (isset($persistant->tool_customlang_filter)) {
 | 
        
           |  |  | 411 |             foreach ($persistant->tool_customlang_filter as $key => $value) {
 | 
        
           |  |  | 412 |                 $data->{$key} = unserialize($value);
 | 
        
           |  |  | 413 |             }
 | 
        
           |  |  | 414 |         }
 | 
        
           |  |  | 415 |         return $data;
 | 
        
           |  |  | 416 |     }
 | 
        
           |  |  | 417 | }
 | 
        
           |  |  | 418 |   | 
        
           |  |  | 419 | /**
 | 
        
           |  |  | 420 |  * Represents the action menu of the tool
 | 
        
           |  |  | 421 |  */
 | 
        
           |  |  | 422 | class tool_customlang_menu implements renderable {
 | 
        
           |  |  | 423 |   | 
        
           |  |  | 424 |     /** @var menu items */
 | 
        
           |  |  | 425 |     protected $items = array();
 | 
        
           |  |  | 426 |   | 
        
           |  |  | 427 |     public function __construct(array $items = array()) {
 | 
        
           |  |  | 428 |         global $CFG;
 | 
        
           |  |  | 429 |   | 
        
           |  |  | 430 |         foreach ($items as $itemkey => $item) {
 | 
        
           |  |  | 431 |             $this->add_item($itemkey, $item['title'], $item['url'], empty($item['method']) ? 'post' : $item['method']);
 | 
        
           |  |  | 432 |         }
 | 
        
           |  |  | 433 |     }
 | 
        
           |  |  | 434 |   | 
        
           |  |  | 435 |     /**
 | 
        
           |  |  | 436 |      * Returns the menu items
 | 
        
           |  |  | 437 |      *
 | 
        
           |  |  | 438 |      * @return array (string)key => (object)[->(string)title ->(moodle_url)url ->(string)method]
 | 
        
           |  |  | 439 |      */
 | 
        
           |  |  | 440 |     public function get_items() {
 | 
        
           |  |  | 441 |         return $this->items;
 | 
        
           |  |  | 442 |     }
 | 
        
           |  |  | 443 |   | 
        
           |  |  | 444 |     /**
 | 
        
           |  |  | 445 |      * Adds item into the menu
 | 
        
           |  |  | 446 |      *
 | 
        
           |  |  | 447 |      * @param string $key item identifier
 | 
        
           |  |  | 448 |      * @param string $title localized action title
 | 
        
           |  |  | 449 |      * @param moodle_url $url action handler
 | 
        
           |  |  | 450 |      * @param string $method form method
 | 
        
           |  |  | 451 |      */
 | 
        
           |  |  | 452 |     public function add_item($key, $title, moodle_url $url, $method) {
 | 
        
           |  |  | 453 |         if (isset($this->items[$key])) {
 | 
        
           |  |  | 454 |             throw new coding_exception('Menu item already exists');
 | 
        
           |  |  | 455 |         }
 | 
        
           |  |  | 456 |         if (empty($title) or empty($key)) {
 | 
        
           |  |  | 457 |             throw new coding_exception('Empty title or item key not allowed');
 | 
        
           |  |  | 458 |         }
 | 
        
           |  |  | 459 |         $item = new stdclass();
 | 
        
           |  |  | 460 |         $item->title = $title;
 | 
        
           |  |  | 461 |         $item->url = $url;
 | 
        
           |  |  | 462 |         $item->method = $method;
 | 
        
           |  |  | 463 |         $this->items[$key] = $item;
 | 
        
           |  |  | 464 |     }
 | 
        
           |  |  | 465 | }
 | 
        
           |  |  | 466 |   | 
        
           |  |  | 467 | /**
 | 
        
           |  |  | 468 |  * Represents the translation tool
 | 
        
           |  |  | 469 |  */
 | 
        
           |  |  | 470 | class tool_customlang_translator implements renderable {
 | 
        
           |  |  | 471 |   | 
        
           |  |  | 472 |     /** @var int number of rows per page */
 | 
        
           |  |  | 473 |     const PERPAGE = 100;
 | 
        
           |  |  | 474 |   | 
        
           |  |  | 475 |     /** @var int total number of the rows int the table */
 | 
        
           |  |  | 476 |     public $numofrows = 0;
 | 
        
           |  |  | 477 |   | 
        
           |  |  | 478 |     /** @var moodle_url */
 | 
        
           |  |  | 479 |     public $handler;
 | 
        
           |  |  | 480 |   | 
        
           |  |  | 481 |     /** @var string language code */
 | 
        
           |  |  | 482 |     public $lang;
 | 
        
           |  |  | 483 |   | 
        
           |  |  | 484 |     /** @var int page to display, starting with page 0 */
 | 
        
           |  |  | 485 |     public $currentpage = 0;
 | 
        
           |  |  | 486 |   | 
        
           |  |  | 487 |     /** @var array of stdclass strings to display */
 | 
        
           |  |  | 488 |     public $strings = array();
 | 
        
           |  |  | 489 |   | 
        
           |  |  | 490 |     /** @var stdclass */
 | 
        
           |  |  | 491 |     protected $filter;
 | 
        
           |  |  | 492 |   | 
        
           |  |  | 493 |     public function __construct(moodle_url $handler, $lang, $filter, $currentpage = 0) {
 | 
        
           |  |  | 494 |         global $DB;
 | 
        
           |  |  | 495 |   | 
        
           |  |  | 496 |         $this->handler      = $handler;
 | 
        
           |  |  | 497 |         $this->lang         = $lang;
 | 
        
           |  |  | 498 |         $this->filter       = $filter;
 | 
        
           |  |  | 499 |         $this->currentpage  = $currentpage;
 | 
        
           |  |  | 500 |   | 
        
           |  |  | 501 |         if (empty($filter) or empty($filter->component)) {
 | 
        
           |  |  | 502 |             // nothing to do
 | 
        
           |  |  | 503 |             $this->currentpage = 1;
 | 
        
           |  |  | 504 |             return;
 | 
        
           |  |  | 505 |         }
 | 
        
           |  |  | 506 |   | 
        
           |  |  | 507 |         list($insql, $inparams) = $DB->get_in_or_equal($filter->component, SQL_PARAMS_NAMED);
 | 
        
           |  |  | 508 |   | 
        
           |  |  | 509 |         $fsql = "SELECT s.*, c.name AS component";
 | 
        
           |  |  | 510 |         $sql  = "  FROM {tool_customlang_components} c
 | 
        
           |  |  | 511 |                    JOIN {tool_customlang} s ON s.componentid = c.id
 | 
        
           |  |  | 512 |                   WHERE s.lang = :lang
 | 
        
           |  |  | 513 |                         AND c.name $insql";
 | 
        
           |  |  | 514 |   | 
        
           |  |  | 515 |         $params = array_merge(array('lang' => $lang), $inparams);
 | 
        
           |  |  | 516 |   | 
        
           |  |  | 517 |         if (!empty($filter->customized)) {
 | 
        
           |  |  | 518 |             $sql .= "   AND s.local IS NOT NULL";
 | 
        
           |  |  | 519 |         }
 | 
        
           |  |  | 520 |   | 
        
           |  |  | 521 |         if (!empty($filter->modified)) {
 | 
        
           |  |  | 522 |             $sql .= "   AND s.modified = 1";
 | 
        
           |  |  | 523 |         }
 | 
        
           |  |  | 524 |   | 
        
           |  |  | 525 |         if (!empty($filter->stringid)) {
 | 
        
           |  |  | 526 |             $sql .= "   AND s.stringid = :stringid";
 | 
        
           |  |  | 527 |             $params['stringid'] = $filter->stringid;
 | 
        
           |  |  | 528 |         }
 | 
        
           |  |  | 529 |   | 
        
           |  |  | 530 |         if (!empty($filter->substring)) {
 | 
        
           |  |  | 531 |             $sql .= "   AND (".$DB->sql_like('s.original', ':substringoriginal', false)." OR
 | 
        
           |  |  | 532 |                              ".$DB->sql_like('s.master', ':substringmaster', false)." OR
 | 
        
           |  |  | 533 |                              ".$DB->sql_like('s.local', ':substringlocal', false).")";
 | 
        
           |  |  | 534 |             $params['substringoriginal'] = '%'.$filter->substring.'%';
 | 
        
           |  |  | 535 |             $params['substringmaster']   = '%'.$filter->substring.'%';
 | 
        
           |  |  | 536 |             $params['substringlocal']    = '%'.$filter->substring.'%';
 | 
        
           |  |  | 537 |         }
 | 
        
           |  |  | 538 |   | 
        
           |  |  | 539 |         if (!empty($filter->helps)) {
 | 
        
           |  |  | 540 |             $sql .= "   AND ".$DB->sql_like('s.stringid', ':help', false); //ILIKE
 | 
        
           |  |  | 541 |             $params['help'] = '%\_help';
 | 
        
           |  |  | 542 |         } else {
 | 
        
           |  |  | 543 |             $sql .= "   AND ".$DB->sql_like('s.stringid', ':link', false, true, true); //NOT ILIKE
 | 
        
           |  |  | 544 |             $params['link'] = '%\_link';
 | 
        
           |  |  | 545 |         }
 | 
        
           |  |  | 546 |   | 
        
           | 1441 | ariadna | 547 |         $osql = "component, stringid";
 | 
        
           | 1 | efrain | 548 |   | 
        
           | 1441 | ariadna | 549 |         $this->strings = $DB->get_counted_records_sql(
 | 
        
           |  |  | 550 |             sql: $fsql.$sql,
 | 
        
           |  |  | 551 |             fullcountcolumn: 'fullcount',
 | 
        
           |  |  | 552 |             sort: $osql,
 | 
        
           |  |  | 553 |             params: $params,
 | 
        
           |  |  | 554 |             limitfrom: ($this->currentpage) * self::PERPAGE,
 | 
        
           |  |  | 555 |             limitnum: self::PERPAGE,
 | 
        
           |  |  | 556 |         );
 | 
        
           |  |  | 557 |         $this->numofrows = reset($this->strings)->fullcount ?? 0;
 | 
        
           | 1 | efrain | 558 |     }
 | 
        
           |  |  | 559 | }
 |