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
 * @package    backup-convert
19
 * @subpackage cc-library
20
 * @copyright  2011 Darko Miletic <dmiletic@moodlerooms.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
require_once('xmlbase.php');
25
 
26
/**
27
 *
28
 * Various helper utils
29
 * @author Darko Miletic dmiletic@moodlerooms.com
30
 *
31
 */
32
abstract class cc_helpers {
33
 
34
    /**
35
     * Checks extension of the supplied filename
36
     *
37
     * @param string $filename
38
     */
39
    public static function is_html($filename) {
40
        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
41
        return in_array($extension, array('htm', 'html'));
42
    }
43
 
44
    /**
45
     * Generates unique identifier
46
     * @param string $prefix
47
     * @param string $suffix
48
     * @return string
49
     */
50
    public static function uuidgen($prefix = '', $suffix = '', $uppercase = true) {
51
        $uuid = trim(sprintf('%s%04x%04x%s', $prefix, mt_rand(0, 65535), mt_rand(0, 65535), $suffix));
52
        $result = $uppercase ? strtoupper($uuid) : strtolower($uuid);
53
        return $result;
54
    }
55
 
56
    /**
57
     * Creates new folder with random name
58
     * @param string $where
59
     * @param string $prefix
60
     * @param string $suffix
61
     * @return mixed - directory short name or false in case of failure
62
     */
63
    public static function randomdir($where, $prefix = '', $suffix = '') {
64
        global $CFG;
65
 
66
        $dirname    = false;
67
        $randomname = self::uuidgen($prefix, $suffix, false);
68
        $newdirname = $where.DIRECTORY_SEPARATOR.$randomname;
69
        if (mkdir($newdirname)) {
70
            chmod($newdirname, $CFG->directorypermissions);
71
            $dirname = $randomname;
72
        }
73
        return $dirname;
74
    }
75
 
76
    public static function build_query($attributes, $search) {
77
        $result = '';
78
        foreach ($attributes as $attribute) {
79
            if ($result != '') {
80
                $result .= ' | ';
81
            }
82
            $result .= "//*[starts-with(@{$attribute},'{$search}')]/@{$attribute}";
83
        }
84
        return $result;
85
    }
86
 
87
    public static function process_embedded_files(&$doc, $attributes, $search, $customslash = null) {
88
        $result = array();
89
        $query = self::build_query($attributes, $search);
90
        $list = $doc->nodeList($query);
91
        foreach ($list as $filelink) {
92
            $rvalue = str_replace($search, '', $filelink->nodeValue);
93
            if (!empty($customslash)) {
94
                $rvalue = str_replace($customslash, '/', $rvalue);
95
            }
96
            $result[] = rawurldecode($rvalue);
97
        }
98
        return $result;
99
    }
100
 
101
    /**
102
     *
103
     * Get list of embedded files
104
     * @param string $html
105
     * @return multitype:mixed
106
     */
107
    public static function embedded_files($html) {
108
        $result = array();
109
        $doc = new XMLGenericDocument();
110
        $doc->doc->validateOnParse = false;
111
        $doc->doc->strictErrorChecking = false;
112
        if (!empty($html) && $doc->loadHTML($html)) {
113
            $attributes = array('src', 'href');
114
            $result1 = self::process_embedded_files($doc, $attributes, '@@PLUGINFILE@@');
115
            $result2 = self::process_embedded_files($doc, $attributes, '$@FILEPHP@$', '$@SLASH@$');
116
            $result = array_merge($result1, $result2);
117
        }
118
        return $result;
119
    }
120
 
121
    public static function embedded_mapping($packageroot, $contextid = null) {
122
        $main_file = $packageroot . DIRECTORY_SEPARATOR . 'files.xml';
123
        $mfile = new XMLGenericDocument();
124
        if (!$mfile->load($main_file)) {
125
            return false;
126
        }
127
        $query = "/files/file[filename!='.']";
128
        if (!empty($contextid)) {
129
            $query .= "[contextid='{$contextid}']";
130
        }
131
        $files = $mfile->nodeList($query);
132
        $depfiles = array();
133
        foreach ($files as $node) {
134
            $mainfile   = intval($mfile->nodeValue('sortorder', $node));
135
            $filename   = $mfile->nodeValue('filename', $node);
136
            $filepath   = $mfile->nodeValue('filepath', $node);
137
            $source     = $mfile->nodeValue('source', $node);
138
            $author     = $mfile->nodeValue('author', $node);
139
            $license    = $mfile->nodeValue('license', $node);
140
            $hashedname = $mfile->nodeValue('contenthash', $node);
141
            $hashpart   = substr($hashedname, 0, 2);
142
            $location   = 'files'.DIRECTORY_SEPARATOR.$hashpart.DIRECTORY_SEPARATOR.$hashedname;
143
            $type       = $mfile->nodeValue('mimetype', $node);
144
            $depfiles[$filepath.$filename] = array( $location,
145
                                                    ($mainfile == 1),
146
                                                    strtolower(str_replace(' ', '_', $filename)),
147
                                                    $type,
148
                                                    $source,
149
                                                    $author,
150
                                                    $license,
151
                                                    strtolower(str_replace(' ', '_', $filepath)));
152
        }
153
 
154
        return $depfiles;
155
    }
156
 
157
    public static function add_files(cc_i_manifest &$manifest, $packageroot, $outdir, $allinone = true) {
158
        global $CFG;
159
 
160
        if (pkg_static_resources::instance()->finished) {
161
            return;
162
        }
163
        $files = cc_helpers::embedded_mapping($packageroot);
164
        $rdir = $allinone ? new cc_resource_location($outdir) : null;
165
        foreach ($files as $virtual => $values) {
166
            $clean_filename = $values[2];
167
            if (!$allinone) {
168
                $rdir = new cc_resource_location($outdir);
169
            }
170
            $rtp = $rdir->fullpath().$values[7].$clean_filename;
171
            //Are there any relative virtual directories?
172
            //let us try to recreate them
173
            $justdir = $rdir->fullpath(false).$values[7];
174
            if (!file_exists($justdir)) {
175
                if (!mkdir($justdir, $CFG->directorypermissions, true)) {
176
                    throw new RuntimeException('Unable to create directories!');
177
                }
178
            }
179
 
180
            $source = $packageroot.DIRECTORY_SEPARATOR.$values[0];
181
            if (!copy($source, $rtp)) {
182
                throw new RuntimeException('Unable to copy files!');
183
            }
184
            $resource = new cc_resource($rdir->rootdir(),
185
                                        $values[7].$clean_filename,
186
                                        $rdir->dirname(false));
187
            $res = $manifest->add_resource($resource, null, cc_version11::webcontent);
188
            pkg_static_resources::instance()->add($virtual,
189
                                                  $res[0],
190
                                                  $rdir->dirname(false).$values[7].$clean_filename,
191
                                                  $values[1],
192
                                                  $resource);
193
        }
194
 
195
        pkg_static_resources::instance()->finished = true;
196
    }
197
 
198
    /**
199
     *
200
     * Excerpt from IMS CC 1.1 overview :
201
     * No spaces in filenames, directory and file references should
202
     * employ all lowercase or all uppercase - no mixed case
203
     *
204
     * @param cc_i_manifest $manifest
205
     * @param string $packageroot
206
     * @param integer $contextid
207
     * @param string $outdir
208
     * @param boolean $allinone
209
     * @throws RuntimeException
210
     */
211
    public static function handle_static_content(cc_i_manifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true) {
212
        self::add_files($manifest, $packageroot, $outdir, $allinone);
213
        return pkg_static_resources::instance()->get_values();
214
    }
215
 
216
    public static function handle_resource_content(cc_i_manifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true) {
217
        $result = array();
218
        self::add_files($manifest, $packageroot, $outdir, $allinone);
219
        $files = self::embedded_mapping($packageroot, $contextid);
220
        $rootnode = null;
221
        $rootvals = null;
222
        $depfiles = array();
223
        $depres = array();
224
        $flocation = null;
225
        foreach ($files as $virtual => $values) {
226
            $vals = pkg_static_resources::instance()->get_identifier($virtual);
227
            $resource = $vals[3];
228
            $identifier = $resource->identifier;
229
            $flocation = $vals[1];
230
            if ($values[1]) {
231
                $rootnode = $resource;
232
                $rootvals = $flocation;
233
                continue;
234
            }
235
 
236
            $depres[] = $identifier;
237
            $depfiles[] = $vals[1];
238
            $result[$virtual] = array($identifier, $flocation, false);
239
        }
240
 
241
        if (!empty($rootnode)) {
242
            $rootnode->files = array_merge($rootnode->files, $depfiles);
243
            $result[$virtual] = array($rootnode->identifier, $rootvals, true);
244
        }
245
 
246
        return $result;
247
    }
248
 
249
    public static function process_linked_files($content, cc_i_manifest &$manifest, $packageroot,
250
                                                $contextid, $outdir, $webcontent = false) {
251
        // Detect all embedded files
252
        // locate their physical counterparts in moodle 2 backup
253
        // copy all files in the cc package stripping any spaces and using only lowercase letters
254
        // add those files as resources of the type webcontent to the manifest
255
        // replace the links to the resource using $IMS-CC-FILEBASE$ and their new locations
256
        // cc_resource has array of files and array of dependencies
257
        // most likely we would need to add all files as independent resources and than
258
        // attach them all as dependencies to the forum tag.
259
        $lfiles = self::embedded_files($content);
260
        $text = $content;
261
        $deps = array();
262
        if (!empty($lfiles)) {
263
            $files = self::handle_static_content($manifest,
264
                                                 $packageroot,
265
                                                 $contextid,
266
                                                 $outdir);
267
            $replaceprefix = $webcontent ? '' : '$IMS-CC-FILEBASE$';
268
            foreach ($lfiles as $lfile) {
269
                if (isset($files[$lfile])) {
270
                    $filename = str_replace('%2F', '/', rawurlencode($lfile));
271
                    $content = str_replace('@@PLUGINFILE@@'.$filename,
272
                                           $replaceprefix.'../'.$files[$lfile][1],
273
                                           $content);
274
                    // For the legacy stuff.
275
                    $content = str_replace('$@FILEPHP@$'.str_replace('/', '$@SLASH@$', $filename),
276
                                           $replaceprefix.'../'.$files[$lfile][1],
277
                                           $content);
278
                    $deps[] = $files[$lfile][0];
279
                }
280
            }
281
            $text = $content;
282
        }
283
        return array($text, $deps);
284
    }
285
 
286
    public static function relative_location($originpath, $linkingpath) {
287
        return false;
288
    }
289
 
290
}
291
 
292
 
293
final class cc_resource_location {
294
    /**
295
     *
296
     * Root directory
297
     * @var string
298
     */
299
    private $rootdir = null;
300
    /**
301
     *
302
     * new directory
303
     * @var string
304
     */
305
    private $dir = null;
306
    /**
307
     *
308
     * Full precalculated path
309
     * @var string
310
     */
311
    private $fullpath = null;
312
 
313
    /**
314
     *
315
     * ctor
316
     * @param string $rootdir - path to the containing directory
317
     * @throws InvalidArgumentException
318
     * @throws RuntimeException
319
     */
320
    public function __construct($rootdir) {
321
        $rdir = realpath($rootdir);
322
        if (empty($rdir)) {
323
            throw new InvalidArgumentException('Invalid path!');
324
        }
325
        $dir = cc_helpers::randomdir($rdir, 'i_');
326
        if ($dir === false) {
327
            throw new RuntimeException('Unable to create directory!');
328
        }
329
        $this->rootdir  = $rdir;
330
        $this->dir      = $dir;
331
        $this->fullpath = $rdir.DIRECTORY_SEPARATOR.$dir;
332
    }
333
 
334
    /**
335
     *
336
     * Newly created directory
337
     * @return string
338
     */
339
    public function dirname($endseparator=false) {
340
        return $this->dir.($endseparator ? '/' : '');
341
    }
342
 
343
    /**
344
     *
345
     * Full path to the new directory
346
     * @return string
347
     */
348
    public function fullpath($endseparator=false) {
349
        return $this->fullpath.($endseparator ? DIRECTORY_SEPARATOR : '');
350
    }
351
 
352
    /**
353
     * Returns containing dir
354
     * @return string
355
     */
356
    public function rootdir($endseparator=false) {
357
        return $this->rootdir.($endseparator ? DIRECTORY_SEPARATOR : '');
358
    }
359
}
360
 
361
class pkg_static_resources {
362
 
363
    /**
364
     * @var array
365
     */
366
    private $values = array();
367
 
368
    /**
369
     * @var boolean
370
     */
371
    public $finished = false;
372
 
373
    /**
374
     * @var pkg_static_resources
375
     */
376
    private static $instance = null;
377
 
378
    private function __clone() {
379
    }
380
 
381
    private function __construct() {
382
    }
383
 
384
    /**
385
     * @return pkg_static_resources
386
     */
387
    public static function instance() {
388
        if (empty(self::$instance)) {
389
            $c = __CLASS__;
390
            self::$instance = new $c();
391
        }
392
        return self::$instance;
393
    }
394
 
395
    /**
396
     *
397
     * add new element
398
     * @param string $identifier
399
     * @param string $file
400
     * @param boolean $main
401
     */
402
    public function add($key, $identifier, $file, $main, $node = null) {
403
        $this->values[$key] = array($identifier, $file, $main, $node);
404
    }
405
 
406
    /**
407
     * @return array
408
     */
409
    public function get_values() {
410
        return $this->values;
411
    }
412
 
413
    public function get_identifier($location) {
414
        return isset($this->values[$location]) ? $this->values[$location] : false;
415
    }
416
 
417
    public function reset() {
418
        $this->values   = array();
419
        $this->finished = false;
420
    }
421
}
422
 
423
 
424
class pkg_resource_dependencies {
425
    /**
426
     * @var array
427
     */
428
    private $values = array();
429
 
430
    /**
431
     * @var pkg_resource_dependencies
432
     */
433
    private static $instance = null;
434
 
435
    private function __clone() {
436
    }
437
    private function __construct() {
438
    }
439
 
440
    /**
441
     * @return pkg_resource_dependencies
442
     */
443
    public static function instance() {
444
        if (empty(self::$instance)) {
445
            $c = __CLASS__;
446
            self::$instance = new $c();
447
        }
448
        return self::$instance;
449
    }
450
 
451
    /**
452
     * @param array $deps
453
     */
454
    public function add(array $deps) {
455
        $this->values = array_merge($this->values, $deps);
456
    }
457
 
458
    public function reset() {
459
        $this->values = array();
460
    }
461
 
462
    /**
463
     * @return array
464
     */
465
    public function get_deps() {
466
        return $this->values;
467
    }
468
}