Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
 * This file is responsible for serving of yui Javascript and CSS
19
 *
20
 * @package   core
21
 * @copyright 2009 Petr Skoda (skodak)  {@link http://skodak.org}
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
 
26
// disable moodle specific debug messages and any errors in output,
27
// comment out when debugging or better look into error log!
28
define('NO_DEBUG_DISPLAY', true);
29
 
30
// we need just the values from config.php and minlib.php
31
define('ABORT_AFTER_CONFIG', true);
32
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
33
 
34
// get special url parameters
35
 
36
list($parts, $slasharguments) = combo_params();
37
if (!$parts) {
38
    combo_not_found();
39
}
40
 
41
$parts = trim($parts, '&');
42
 
43
// Remove any duplicate parts, since each file only needs to be loaded once (which also helps reduce total file size).
44
$parts = implode('&', array_unique(explode('&', $parts)));
45
 
46
// Limit length of parts to match the YUI loader limit of 1024, to prevent loading an arbitrary number of files.
47
if (strlen($parts) > 1024) {
48
    $parts = substr($parts, 0, 1024);
49
 
50
    // If the shortened $parts has been cut off mid-way through a filename, trim back to the end of the previous filename.
51
    if (substr($parts, -3) !== '.js' && substr($parts, -4) !== '.css') {
52
        $parts = substr($parts, 0, strrpos($parts, '&'));
53
    }
54
}
55
 
56
// find out what we are serving - only one type per request
57
$content = '';
58
if (substr($parts, -3) === '.js') {
59
    $mimetype = 'application/javascript';
60
} else if (substr($parts, -4) === '.css') {
61
    $mimetype = 'text/css';
62
} else {
63
    combo_not_found();
64
}
65
 
66
$etag = sha1($parts);
67
 
68
// if they are requesting a revision that's not -1, and they have supplied an
69
// If-Modified-Since header, we can send back a 304 Not Modified since the
70
// content never changes (the rev number is increased any time the content changes)
71
if (strpos($parts, '/-1/') === false and (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))) {
72
    $lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules
73
    header('HTTP/1.1 304 Not Modified');
74
    header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
75
    header('Cache-Control: public, max-age='.$lifetime);
76
    header('Content-Type: '.$mimetype);
77
    header('Etag: "'.$etag.'"');
78
    die;
79
}
80
 
81
$parts = explode('&', $parts);
82
$cache = true;
83
$lastmodified = 0;
84
 
85
while (count($parts)) {
86
    $part = array_shift($parts);
87
    if (empty($part)) {
88
        continue;
89
    }
90
    $filecontent = '';
91
    $part = min_clean_param($part, 'SAFEPATH');
92
    $bits = explode('/', $part);
93
    if (count($bits) < 2) {
94
        $content .= "\n// Wrong combo resource $part!\n";
95
        continue;
96
    }
97
 
98
    $version = array_shift($bits);
99
    if ($version === 'rollup') {
100
        $yuipatchedversion = explode('_', array_shift($bits));
101
        $revision = $yuipatchedversion[0];
102
        $rollupname = array_shift($bits);
103
 
104
        if (strpos($rollupname, 'yui-moodlesimple') !== false) {
105
            if (substr($rollupname, -3) === '.js') {
106
                // Determine which version of this rollup should be used.
107
                $filesuffix = '.js';
108
                preg_match('/(-(debug|min))?\.js/', $rollupname, $matches);
109
                if (isset($matches[1])) {
110
                    $filesuffix = $matches[0];
111
                }
112
 
113
                $type = 'js';
114
            } else if (substr($rollupname, -4) === '.css') {
115
                $type = 'css';
116
            } else {
117
                continue;
118
            }
119
 
120
            // Allow support for revisions on YUI between official releases.
121
            // We can just discard the subrevision since it is only used to invalidate the browser cache.
122
            $yuipatchedversion = explode('_', $revision);
123
            $yuiversion = $yuipatchedversion[0];
124
 
125
            $yuimodules = array(
126
                'yui',
127
                'oop',
128
                'event-custom-base',
129
                'dom-core',
130
                'dom-base',
131
                'color-base',
132
                'dom-style',
133
                'selector-native',
134
                'selector',
135
                'node-core',
136
                'node-base',
137
                'event-base',
138
                'event-base-ie',
139
                'pluginhost-base',
140
                'pluginhost-config',
141
                'event-delegate',
142
                'node-event-delegate',
143
                'node-pluginhost',
144
                'dom-screen',
145
                'node-screen',
146
                'node-style',
147
                'querystring-stringify-simple',
148
                'io-base',
149
                'json-parse',
150
                'transition',
151
                'selector-css2',
152
                'selector-css3',
153
                'dom-style-ie',
154
 
155
                // Some extras we use everywhere.
156
                'escape',
157
 
158
                'attribute-core',
159
                'event-custom-complex',
160
                'base-core',
161
                'attribute-base',
162
                'attribute-extras',
163
                'attribute-observable',
164
                'base-observable',
165
                'base-base',
166
                'base-pluginhost',
167
                'base-build',
168
                'event-synthetic',
169
 
170
                'attribute-complex',
171
                'event-mouseenter',
172
                'event-key',
173
                'event-outside',
174
                'event-focus',
175
                'classnamemanager',
176
                'widget-base',
177
                'widget-htmlparser',
178
                'widget-skin',
179
                'widget-uievents',
180
                'widget-stdmod',
181
                'widget-position',
182
                'widget-position-align',
183
                'widget-stack',
184
                'widget-position-constrain',
185
                'overlay',
186
 
187
                'widget-autohide',
188
                'button-core',
189
                'button-plugin',
190
                'widget-buttons',
191
                'widget-modality',
192
                'panel',
193
                'yui-throttle',
194
                'dd-ddm-base',
195
                'dd-drag',
196
                'dd-plugin',
197
            );
198
 
199
            // We need to add these new parts to the beginning of the $parts list, not the end.
200
            if ($type === 'js') {
201
                $newparts = array();
202
                foreach ($yuimodules as $module) {
203
                    $newparts[] = $yuiversion . '/' . $module . '/' . $module . $filesuffix;
204
                }
205
                $newparts[] = 'yuiuseall/yuiuseall';
206
                $parts = array_merge($newparts, $parts);
207
            } else {
208
                $newparts = array();
209
                foreach ($yuimodules as $module) {
210
                    $candidate =  $yuiversion . '/' . $module . '/assets/skins/sam/' . $module . '.css';
211
                    if (!file_exists("$CFG->libdir/yuilib/$candidate")) {
212
                        continue;
213
                    }
214
                    $newparts[] = $candidate;
215
                }
216
                if ($newparts) {
217
                    $parts = array_merge($newparts, $parts);
218
                }
219
            }
220
        }
221
 
222
        continue;
223
    }
224
    if ($version === 'm') {
225
        $version = 'moodle';
226
    }
227
    if ($version === 'moodle') {
228
        if (count($bits) <= 3) {
229
            // This is an invalid module load attempt.
230
            $content .= "\n// Incorrect moodle module inclusion. Not enough component information in {$part}.\n";
231
            continue;
232
        }
233
        $revision = (int)array_shift($bits);
234
        if (!min_is_revision_valid_and_current($revision)) {
235
            // A non-current revision means please don't cache the JS
236
            $revision = -1;
237
            $cache = false;
238
        }
239
        $frankenstyle = array_shift($bits);
240
        $filename = array_pop($bits);
241
        $modulename = $bits[0];
242
        $dir = core_component::get_component_directory($frankenstyle);
243
 
244
        // For shifted YUI modules, we need the YUI module name in frankenstyle format.
245
        $frankenstylemodulename = join('-', array($version, $frankenstyle, $modulename));
246
        $frankenstylefilename = preg_replace('/' . $modulename . '/', $frankenstylemodulename, $filename);
247
 
248
        // Submodules are stored in a directory with the full submodule name.
249
        // We need to remove the -debug.js, -min.js, and .js from the file name to calculate that directory name.
250
        $frankenstyledirectoryname = str_replace(array('-min.js', '-debug.js', '.js', '.css'), '', $frankenstylefilename);
251
 
252
        // By default, try and use the /yui/build directory.
253
        $contentfile = $dir . '/yui/build/' . $frankenstyledirectoryname;
254
        if ($mimetype == 'text/css') {
255
            // CSS assets are in a slightly different place to the JS.
256
            $contentfile = $contentfile . '/assets/skins/sam/' . $frankenstylefilename;
257
 
258
            // Add the path to the bits to handle fallback for non-shifted assets.
259
            $bits[] = 'assets';
260
            $bits[] = 'skins';
261
            $bits[] = 'sam';
262
        } else {
263
            $contentfile = $contentfile . '/' . $frankenstylefilename;
264
        }
265
 
266
        // If the shifted versions don't exist, fall back to the non-shifted file.
267
        if (!file_exists($contentfile) or !is_file($contentfile)) {
268
            // We have to revert to the non-minified and non-debug versions.
269
            $filename = preg_replace('/-(min|debug)\./', '.', $filename);
270
            $contentfile = $dir . '/yui/' . join('/', $bits) . '/' . $filename;
271
        }
272
    } else if ($version === '2in3') {
273
        $contentfile = "$CFG->libdir/yuilib/$part";
274
 
275
    } else if ($version == 'gallery') {
276
        if (count($bits) <= 2) {
277
            // This is an invalid module load attempt.
278
            $content .= "\n// Incorrect moodle module inclusion. Not enough component information in {$part}.\n";
279
            continue;
280
        }
281
        $revision = (int)array_shift($bits);
282
        if (!min_is_revision_valid_and_current($revision)) {
283
            // A non-current revision means please don't cache the JS
284
            $revision = -1;
285
            $cache = false;
286
        }
287
        $contentfile = "$CFG->libdir/yuilib/gallery/" . join('/', $bits);
288
 
289
    } else if ($version == 'yuiuseall') {
290
        // Create global Y that is available in global scope,
291
        // this is the trick behind original SimpleYUI.
292
        $filecontent = "var Y = YUI().use('*');";
293
 
294
    } else {
295
        // Allow support for revisions on YUI between official releases.
296
        // We can just discard the subrevision since it is only used to invalidate the browser cache.
297
        $yuipatchedversion = explode('_', $version);
298
        $yuiversion = $yuipatchedversion[0];
299
        if ($yuiversion != $CFG->yui3version) {
300
            $content .= "\n// Wrong yui version $part!\n";
301
            continue;
302
        }
303
        $newpart = explode('/', $part);
304
        $newpart[0] = $yuiversion;
305
        $part = implode('/', $newpart);
306
        $contentfile = "$CFG->libdir/yuilib/$part";
307
    }
308
    if (!file_exists($contentfile) or !is_file($contentfile)) {
309
        $location = '$CFG->dirroot'.preg_replace('/^'.preg_quote($CFG->dirroot, '/').'/', '', $contentfile);
310
        $content .= "\n// Combo resource $part ($location) not found!\n";
311
        continue;
312
    }
313
 
314
    if (empty($filecontent)) {
315
        $filecontent = file_get_contents($contentfile);
316
    }
317
    $fmodified = filemtime($contentfile);
318
    if ($fmodified > $lastmodified) {
319
        $lastmodified = $fmodified;
320
    }
321
 
322
    $relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
323
    $sep = ($slasharguments ? '/' : '?file=');
324
 
325
    if ($mimetype === 'text/css') {
326
        if ($version == 'moodle') {
327
            // Search for all images in the file and replace with an appropriate link to the yui_image.php script
328
            $imagebits = array(
329
                $sep . $version,
330
                $frankenstyle,
331
                $modulename,
332
                array_shift($bits),
333
                '$1.$2'
334
            );
335
 
336
            $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot . '/theme/yui_image.php' . implode('/', $imagebits), $filecontent);
337
        } else if ($version == '2in3') {
338
            // First we need to remove relative paths to images. These are used by YUI modules to make use of global assets.
339
            // I've added this as a separate regex so it can be easily removed once
340
            // YUI standardise there CSS methods
341
            $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent);
342
 
343
            // search for all images in yui2 CSS and serve them through the yui_image.php script
344
            $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$CFG->yui2version.'/$1.$2', $filecontent);
345
 
346
        } else if ($version == 'gallery') {
347
            // Replace any references to the CDN with a relative link.
348
            $filecontent = preg_replace('#(' . preg_quote('http://yui.yahooapis.com/') . '(gallery-[^/]*/))#', '../../../../', $filecontent);
349
 
350
            // Replace all relative image links with the a link to yui_image.php.
351
            $filecontent = preg_replace('#(' . preg_quote('../../../../') . ')(gallery-[^/]*/assets/skins/sam/[a-z0-9_-]+)\.(png|gif)#',
352
                    $relroot . '/theme/yui_image.php' . $sep . '/gallery/' . $revision . '/$2.$3', $filecontent);
353
 
354
        } else {
355
            // First we need to remove relative paths to images. These are used by YUI modules to make use of global assets.
356
            // I've added this as a separate regex so it can be easily removed once
357
            // YUI standardise there CSS methods
358
            $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent);
359
 
360
            // search for all images in yui2 CSS and serve them through the yui_image.php script
361
            $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', $relroot.'/theme/yui_image.php'.$sep.$version.'/$1.$2', $filecontent);
362
        }
363
    }
364
 
365
    $content .= $filecontent;
366
}
367
 
368
if ($lastmodified == 0) {
369
    $lastmodified = time();
370
}
371
 
372
if ($cache) {
373
    combo_send_cached($content, $mimetype, $etag, $lastmodified);
374
} else {
375
    combo_send_uncached($content, $mimetype);
376
}
377
 
378
 
379
/**
380
 * Send the JavaScript cached
381
 * @param string $content
382
 * @param string $mimetype
383
 * @param string $etag
384
 * @param int $lastmodified
385
 */
386
function combo_send_cached($content, $mimetype, $etag, $lastmodified) {
387
    $lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules
388
 
389
    header('Content-Disposition: inline; filename="combo"');
390
    header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
391
    header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
392
    header('Pragma: ');
393
    header('Cache-Control: public, max-age='.$lifetime.', immutable');
394
    header('Accept-Ranges: none');
395
    header('Content-Type: '.$mimetype);
396
    header('Etag: "'.$etag.'"');
397
    if (!min_enable_zlib_compression()) {
398
        header('Content-Length: '.strlen($content));
399
    }
400
 
401
    echo $content;
402
    die;
403
}
404
 
405
/**
406
 * Send the JavaScript uncached
407
 * @param string $content
408
 * @param string $mimetype
409
 */
410
function combo_send_uncached($content, $mimetype) {
411
    header('Content-Disposition: inline; filename="combo"');
412
    header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
413
    header('Expires: '. gmdate('D, d M Y H:i:s', time() + 2) .' GMT');
414
    header('Pragma: ');
415
    header('Accept-Ranges: none');
416
    header('Content-Type: '.$mimetype);
417
    if (!min_enable_zlib_compression()) {
418
        header('Content-Length: '.strlen($content));
419
    }
420
 
421
    echo $content;
422
    die;
423
}
424
 
425
function combo_not_found($message = '') {
426
    header('HTTP/1.0 404 not found');
427
    if ($message) {
428
        echo $message;
429
    } else {
430
        echo 'Combo resource not found, sorry.';
431
    }
432
    die;
433
}
434
 
435
function combo_params() {
436
    if (isset($_SERVER['QUERY_STRING']) and strpos($_SERVER['QUERY_STRING'], 'file=/') === 0) {
437
        // url rewriting
438
        $slashargument = substr($_SERVER['QUERY_STRING'], 6);
439
        return array($slashargument, true);
440
 
441
    } else if (isset($_SERVER['REQUEST_URI']) and strpos($_SERVER['REQUEST_URI'], '?') !== false) {
442
        $parts = explode('?', $_SERVER['REQUEST_URI'], 2);
443
        return array($parts[1], false);
444
 
445
    } else if (isset($_SERVER['QUERY_STRING']) and strpos($_SERVER['QUERY_STRING'], '?') !== false) {
446
        // note: buggy or misconfigured IIS does return the query string in REQUEST_URI
447
        return array($_SERVER['QUERY_STRING'], false);
448
 
449
    } else if ($slashargument = min_get_slash_argument(false)) {
450
        $slashargument = ltrim($slashargument, '/');
451
        return array($slashargument, true);
452
 
453
    } else {
454
        // unsupported server, sorry!
455
        combo_not_found('Unsupported server - query string can not be determined, try disabling YUI combo loading in admin settings.');
456
    }
457
}