Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 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
namespace core\output\requirements;
18
 
19
use cache;
20
use core_component;
21
use core_minify;
22
use core\exception\coding_exception;
23
use DirectoryIterator;
24
 
25
/**
26
 * This class represents the YUI configuration.
27
 *
28
 * @copyright 2013 Andrew Nicols
29
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30
 * @since Moodle 2.5
31
 * @package core
32
 * @category output
33
 */
34
class yui {
35
    /**
36
     * These settings must be public so that when the object is converted to json they are exposed.
37
     * Note: Some of these are camelCase because YUI uses camelCase variable names.
38
     *
39
     * The settings are described and documented in the YUI API at:
40
     * - http://yuilibrary.com/yui/docs/api/classes/config.html
41
     * - http://yuilibrary.com/yui/docs/api/classes/Loader.html
42
     */
43
    public $debug = false;
44
    public $base;
45
    public $comboBase;
46
    public $combine;
47
    public $filter = null;
48
    public $insertBefore = 'firstthemesheet';
49
    public $groups = [];
50
    public $modules = [];
51
    /** @var array The log sources that should be not be logged. */
52
    public $logInclude = [];
53
    /** @var array Tog sources that should be logged. */
54
    public $logExclude = [];
55
    /** @var string The minimum log level for YUI logging statements. */
56
    public $logLevel;
57
 
58
    /**
59
     * @var array List of functions used by the YUI Loader group pattern recognition.
60
     */
61
    protected $jsconfigfunctions = [];
62
 
63
    /**
64
     * Create a new group within the YUI_config system.
65
     *
66
     * @param string $name The name of the group. This must be unique and
67
     * not previously used.
68
     * @param array $config The configuration for this group.
69
     * @return void
70
     */
71
    public function add_group($name, $config) {
72
        if (isset($this->groups[$name])) {
73
            throw new coding_exception(
74
                "A YUI configuration group for '{$name}' already exists. " .
75
                    'To make changes to this group use YUI_config->update_group().',
76
            );
77
        }
78
        $this->groups[$name] = $config;
79
    }
80
 
81
    /**
82
     * Update an existing group configuration
83
     *
84
     * Note, any existing configuration for that group will be wiped out.
85
     * This includes module configuration.
86
     *
87
     * @param string $name The name of the group. This must be unique and
88
     * not previously used.
89
     * @param array $config The configuration for this group.
90
     * @return void
91
     */
92
    public function update_group($name, $config) {
93
        if (!isset($this->groups[$name])) {
94
            throw new coding_exception(
95
                'The Moodle YUI module does not exist. ' .
96
                    'You must define the moodle module config using YUI_config->add_module_config first.',
97
            );
98
        }
99
        $this->groups[$name] = $config;
100
    }
101
 
102
    /**
103
     * Set the value of a configuration function used by the YUI Loader's pattern testing.
104
     *
105
     * Only the body of the function should be passed, and not the whole function wrapper.
106
     *
107
     * The JS function your write will be passed a single argument 'name' containing the
108
     * name of the module being loaded.
109
     *
110
     * @param string $function String the body of the JavaScript function. This should be used i
111
     * @return string the name of the function to use in the group pattern configuration.
112
     */
113
    public function set_config_function($function) {
114
        $configname = 'yui' . (count($this->jsconfigfunctions) + 1) . 'ConfigFn';
115
        if (isset($this->jsconfigfunctions[$configname])) {
116
            throw new coding_exception(
117
                "A YUI config function with this name already exists. Config function names must be unique.",
118
            );
119
        }
120
        $this->jsconfigfunctions[$configname] = $function;
121
        return '@' . $configname . '@';
122
    }
123
 
124
    /**
125
     * Allow setting of the config function described in {@see set_config_function} from a file.
126
     * The contents of this file are then passed to set_config_function.
127
     *
128
     * When jsrev is positive, the function is minified and stored in a MUC cache for subsequent uses.
129
     *
130
     * @param string $file The path to the JavaScript function used for YUI configuration.
131
     * @return string the name of the function to use in the group pattern configuration.
132
     */
133
    public function set_config_source($file) {
134
        global $CFG;
135
        $cache = cache::make('core', 'yuimodules');
136
 
137
        // Attempt to get the metadata from the cache.
138
        $keyname = 'configfn_' . $file;
139
        $fullpath = $CFG->dirroot . '/' . $file;
140
        if (!isset($CFG->jsrev) || $CFG->jsrev == -1) {
141
            $cache->delete($keyname);
142
            $configfn = file_get_contents($fullpath);
143
        } else {
144
            $configfn = $cache->get($keyname);
145
            if ($configfn === false) {
146
                require_once($CFG->libdir . '/jslib.php');
147
                $configfn = core_minify::js_files([$fullpath]);
148
                $cache->set($keyname, $configfn);
149
            }
150
        }
151
        return $this->set_config_function($configfn);
152
    }
153
 
154
    /**
155
     * Retrieve the list of JavaScript functions for YUI_config groups.
156
     *
157
     * @return string The complete set of config functions
158
     */
159
    public function get_config_functions() {
160
        $configfunctions = '';
161
        foreach ($this->jsconfigfunctions as $functionname => $function) {
162
            $configfunctions .= "var {$functionname} = function(me) {";
163
            $configfunctions .= $function;
164
            $configfunctions .= "};\n";
165
        }
166
        return $configfunctions;
167
    }
168
 
169
    /**
170
     * Update the header JavaScript with any required modification for the YUI Loader.
171
     *
172
     * @param string $js String The JavaScript to manipulate.
173
     * @return string the modified JS string.
174
     */
175
    public function update_header_js($js) {
176
        // Update the names of the the configFn variables.
177
        // The PHP json_encode function cannot handle literal names so we have to wrap
178
        // them in @ and then replace them with literals of the same function name.
179
        foreach ($this->jsconfigfunctions as $functionname => $function) {
180
            $js = str_replace('"@' . $functionname . '@"', $functionname, $js);
181
        }
182
        return $js;
183
    }
184
 
185
    /**
186
     * Add configuration for a specific module.
187
     *
188
     * @param string $name The name of the module to add configuration for.
189
     * @param array $config The configuration for the specified module.
190
     * @param string $group The name of the group to add configuration for.
191
     * If not specified, then this module is added to the global
192
     * configuration.
193
     * @return void
194
     */
195
    public function add_module_config($name, $config, $group = null) {
196
        if ($group) {
197
            if (!isset($this->groups[$name])) {
198
                throw new coding_exception(
199
                    'The Moodle YUI module does not exist. ' .
200
                        'You must define the moodle module config using YUI_config->add_module_config first.',
201
                );
202
            }
203
            if (!isset($this->groups[$group]['modules'])) {
204
                $this->groups[$group]['modules'] = [];
205
            }
206
            $modules = &$this->groups[$group]['modules'];
207
        } else {
208
            $modules = &$this->modules;
209
        }
210
        $modules[$name] = $config;
211
    }
212
 
213
    /**
214
     * Add the moodle YUI module metadata for the moodle group to the YUI_config instance.
215
     *
216
     * If js caching is disabled, metadata will not be served causing YUI to calculate
217
     * module dependencies as each module is loaded.
218
     *
219
     * If metadata does not exist it will be created and stored in a MUC entry.
220
     *
221
     * @return void
222
     */
223
    public function add_moodle_metadata() {
224
        global $CFG;
225
        if (!isset($this->groups['moodle'])) {
226
            throw new coding_exception(
227
                'The Moodle YUI module does not exist. ' .
228
                    'You must define the moodle module config using YUI_config->add_module_config first.',
229
            );
230
        }
231
 
232
        if (!isset($this->groups['moodle']['modules'])) {
233
            $this->groups['moodle']['modules'] = [];
234
        }
235
 
236
        $cache = cache::make('core', 'yuimodules');
237
        if (!isset($CFG->jsrev) || $CFG->jsrev == -1) {
238
            $metadata = [];
239
            $metadata = $this->get_moodle_metadata();
240
            $cache->delete('metadata');
241
        } else {
242
            // Attempt to get the metadata from the cache.
243
            if (!$metadata = $cache->get('metadata')) {
244
                $metadata = $this->get_moodle_metadata();
245
                $cache->set('metadata', $metadata);
246
            }
247
        }
248
 
249
        // Merge with any metadata added specific to this page which was added manually.
250
        $this->groups['moodle']['modules'] = array_merge(
251
            $this->groups['moodle']['modules'],
252
            $metadata
253
        );
254
    }
255
 
256
    /**
257
     * Determine the module metadata for all moodle YUI modules.
258
     *
259
     * This works through all modules capable of serving YUI modules, and attempts to get
260
     * metadata for each of those modules.
261
     *
262
     * @return array of module metadata
263
     */
264
    private function get_moodle_metadata() {
265
        $moodlemodules = [];
266
        // Core isn't a plugin type or subsystem - handle it seperately.
267
        if ($module = $this->get_moodle_path_metadata(core_component::get_component_directory('core'))) {
268
            $moodlemodules = array_merge($moodlemodules, $module);
269
        }
270
 
271
        // Handle other core subsystems.
272
        $subsystems = core_component::get_core_subsystems();
273
        foreach ($subsystems as $subsystem => $path) {
274
            if (is_null($path)) {
275
                continue;
276
            }
277
            if ($module = $this->get_moodle_path_metadata($path)) {
278
                $moodlemodules = array_merge($moodlemodules, $module);
279
            }
280
        }
281
 
282
        // And finally the plugins.
283
        $plugintypes = core_component::get_plugin_types();
284
        foreach ($plugintypes as $plugintype => $pathroot) {
285
            $pluginlist = core_component::get_plugin_list($plugintype);
286
            foreach ($pluginlist as $plugin => $path) {
287
                if ($module = $this->get_moodle_path_metadata($path)) {
288
                    $moodlemodules = array_merge($moodlemodules, $module);
289
                }
290
            }
291
        }
292
 
293
        return $moodlemodules;
294
    }
295
 
296
    /**
297
     * Helper function process and return the YUI metadata for all of the modules under the specified path.
298
     *
299
     * @param string $path the UNC path to the YUI src directory.
300
     * @return array the complete array for frankenstyle directory.
301
     */
302
    private function get_moodle_path_metadata($path) {
303
        // Add module metadata is stored in frankenstyle_modname/yui/src/yui_modname/meta/yui_modname.json.
304
        $baseyui = $path . '/yui/src';
305
        $modules = [];
306
        if (is_dir($baseyui)) {
307
            $items = new DirectoryIterator($baseyui);
308
            foreach ($items as $item) {
309
                if ($item->isDot() || !$item->isDir()) {
310
                    continue;
311
                }
312
                $metafile = realpath($baseyui . '/' . $item . '/meta/' . $item . '.json');
313
                if (!is_readable($metafile)) {
314
                    continue;
315
                }
316
                $metadata = file_get_contents($metafile);
317
                $modules = array_merge($modules, (array) json_decode($metadata));
318
            }
319
        }
320
        return $modules;
321
    }
322
 
323
    /**
324
     * Define YUI modules which we have been required to patch between releases.
325
     *
326
     * We must do this because we aggressively cache content on the browser, and we must also override use of the
327
     * external CDN which will serve the true authoritative copy of the code without our patches.
328
     *
329
     * @param string $combobase The local combobase
330
     * @param string $yuiversion The current YUI version
331
     * @param int $patchlevel The patch level we're working to for YUI
332
     * @param array $patchedmodules An array containing the names of the patched modules
333
     * @return void
334
     */
335
    public function define_patched_core_modules($combobase, $yuiversion, $patchlevel, $patchedmodules) {
336
        // The version we use is suffixed with a patchlevel so that we can get additional revisions between YUI releases.
337
        $subversion = $yuiversion . '_' . $patchlevel;
338
 
339
        if ($this->comboBase == $combobase) {
340
            // If we are using the local combobase in the loader, we can add a group and still make use of the combo
341
            // loader. We just need to specify a different root which includes a slightly different YUI version number
342
            // to include our patchlevel.
343
            $patterns = [];
344
            $modules = [];
345
            foreach ($patchedmodules as $modulename) {
346
                // We must define the pattern and module here so that the loader uses our group configuration instead of
347
                // the standard module definition. We may lose some metadata provided by upstream but this will be
348
                // loaded when the module is loaded anyway.
349
                $patterns[$modulename] = [
350
                    'group' => 'yui-patched',
351
                ];
352
                $modules[$modulename] = [];
353
            }
354
 
355
            // Actually add the patch group here.
356
            $this->add_group('yui-patched', [
357
                'combine' => true,
358
                'root' => $subversion . '/',
359
                'patterns' => $patterns,
360
                'modules' => $modules,
361
            ]);
362
        } else {
363
            // The CDN is in use - we need to instead use the local combobase for this module and override the modules
364
            // definition. We cannot use the local base - we must use the combobase because we cannot invalidate the
365
            // local base in browser caches.
366
            $fullpathbase = $combobase . $subversion . '/';
367
            foreach ($patchedmodules as $modulename) {
368
                $this->modules[$modulename] = [
369
                    'fullpath' => $fullpathbase . $modulename . '/' . $modulename . '-min.js',
370
                ];
371
            }
372
        }
373
    }
374
}
375
 
376
// Alias this class to the old name.
377
// This file will be autoloaded by the legacyclasses autoload system.
378
// In future all uses of this class will be corrected and the legacy references will be removed.
379
class_alias(yui::class, \YUI_config::class);