Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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
 * Private imscp module utility functions
19
 *
20
 * @package mod_imscp
21
 * @copyright  2009 Petr Skoda  {@link http://skodak.org}
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
defined('MOODLE_INTERNAL') || die();
26
 
27
require_once("$CFG->dirroot/mod/imscp/lib.php");
28
require_once("$CFG->libdir/filelib.php");
29
require_once("$CFG->libdir/resourcelib.php");
30
 
31
/**
32
 * Print IMSCP content to page.
33
 *
34
 * @param stdClass $imscp module instance.
35
 * @param stdClass $cm course module.
36
 * @param stdClass $course record.
37
 */
38
function imscp_print_content($imscp, $cm, $course) {
39
    global $PAGE, $CFG;
40
 
41
    $items = array_filter((array) unserialize_array($imscp->structure));
42
 
43
    echo '<div id="imscp_layout">';
44
    echo '<div id="imscp_toc">';
45
    echo '<div id="imscp_tree"><ul>';
46
    foreach ($items as $item) {
47
        echo imscp_htmllize_item($item, $imscp, $cm);
48
    }
49
    echo '</ul></div>';
50
    echo '<div id="imscp_nav" style="display:none">';
51
    echo '<button id="nav_skipprev">&lt;&lt;</button><button id="nav_prev">&lt;</button><button id="nav_up">^</button>';
52
    echo '<button id="nav_next">&gt;</button><button id="nav_skipnext">&gt;&gt;</button>';
53
    echo '</div>';
54
    echo '</div>';
55
    echo '</div>';
56
 
57
    $PAGE->requires->js_init_call('M.mod_imscp.init');
58
}
59
 
60
/**
61
 * Internal function - creates htmls structure suitable for YUI tree.
62
 */
63
function imscp_htmllize_item($item, $imscp, $cm) {
64
    global $CFG;
65
 
66
    if ($item['href']) {
67
        if (preg_match('|^https?://|', $item['href'])) {
68
            $url = $item['href'];
69
        } else {
70
            $context = context_module::instance($cm->id);
71
            $urlbase = "$CFG->wwwroot/pluginfile.php";
72
            $path = '/'.$context->id.'/mod_imscp/content/'.$imscp->revision.'/'.$item['href'];
73
            $url = file_encode_url($urlbase, $path, false);
74
        }
75
        $result = "<li><a href=\"$url\">".$item['title'].'</a>';
76
    } else {
77
        $result = '<li>'.$item['title'];
78
    }
79
    if ($item['subitems']) {
80
        $result .= '<ul>';
81
        foreach ($item['subitems'] as $subitem) {
82
            $result .= imscp_htmllize_item($subitem, $imscp, $cm);
83
        }
84
        $result .= '</ul>';
85
    }
86
    $result .= '</li>';
87
 
88
    return $result;
89
}
90
 
91
/**
92
 * Parse an IMS content package's manifest file to determine its structure
93
 * @param object $imscp
94
 * @param object $context
95
 * @return array
96
 */
97
function imscp_parse_structure($imscp, $context) {
98
    $fs = get_file_storage();
99
 
100
    if (!$manifestfile = $fs->get_file($context->id, 'mod_imscp', 'content', $imscp->revision, '/', 'imsmanifest.xml')) {
101
        return null;
102
    }
103
 
104
    return imscp_parse_manifestfile($manifestfile->get_content(), $imscp, $context);
105
}
106
 
107
/**
108
 * Parse the contents of a IMS package's manifest file.
109
 * @param string $manifestfilecontents the contents of the manifest file
110
 * @return array
111
 */
112
function imscp_parse_manifestfile($manifestfilecontents, $imscp, $context) {
113
    $doc = new DOMDocument();
114
    if (!$doc->loadXML($manifestfilecontents, LIBXML_NONET)) {
115
        return null;
116
    }
117
 
118
    // We put this fake URL as base in order to detect path changes caused by xml:base attributes.
119
    $doc->documentURI = 'http://grrr/';
120
 
121
    $xmlorganizations = $doc->getElementsByTagName('organizations');
122
    if (empty($xmlorganizations->length)) {
123
        return null;
124
    }
125
    $default = null;
126
    if ($xmlorganizations->item(0)->attributes->getNamedItem('default')) {
127
        $default = $xmlorganizations->item(0)->attributes->getNamedItem('default')->nodeValue;
128
    }
129
    $xmlorganization = $doc->getElementsByTagName('organization');
130
    if (empty($xmlorganization->length)) {
131
        return null;
132
    }
133
    $organization = null;
134
    foreach ($xmlorganization as $org) {
135
        if (is_null($organization)) {
136
            // Use first if default nor found.
137
            $organization = $org;
138
        }
139
        if (!$org->attributes->getNamedItem('identifier')) {
140
            continue;
141
        }
142
        if ($default === $org->attributes->getNamedItem('identifier')->nodeValue) {
143
            // Found default - use it.
144
            $organization = $org;
145
            break;
146
        }
147
    }
148
 
149
    // Load all resources.
150
    $resources = array();
151
 
152
    $xmlresources = $doc->getElementsByTagName('resource');
153
    foreach ($xmlresources as $res) {
154
        if (!$identifier = $res->attributes->getNamedItem('identifier')) {
155
            continue;
156
        }
157
        $identifier = $identifier->nodeValue;
158
        if ($xmlbase = $res->baseURI) {
159
            // Undo the fake URL, we are interested in relative links only.
160
            $xmlbase = str_replace('http://grrr/', '/', $xmlbase);
161
            $xmlbase = rtrim($xmlbase, '/').'/';
162
        } else {
163
            $xmlbase = '';
164
        }
165
        if (!$href = $res->attributes->getNamedItem('href')) {
166
            // If href not found look for <file href="help.htm"/>.
167
            $fileresources = $res->getElementsByTagName('file');
168
            foreach ($fileresources as $file) {
169
                $href = $file->getAttribute('href');
170
            }
171
            if (pathinfo($href, PATHINFO_EXTENSION) == 'xml') {
172
                $href = imscp_recursive_href($href, $imscp, $context);
173
            }
174
            if (empty($href)) {
175
                continue;
176
            }
177
        } else {
178
            $href = $href->nodeValue;
179
        }
180
        if (strpos($href, 'http://') !== 0) {
181
            $href = $xmlbase.$href;
182
        }
183
        // Item href cleanup - Some packages are poorly done and use \ in urls.
184
        $href = ltrim(strtr($href, "\\", '/'), '/');
185
        $resources[$identifier] = $href;
186
    }
187
 
188
    $items = array();
189
    foreach ($organization->childNodes as $child) {
190
        if ($child->nodeName === 'item') {
191
            if (!$item = imscp_recursive_item($child, 0, $resources)) {
192
                continue;
193
            }
194
            $items[] = $item;
195
        }
196
    }
197
 
198
    return $items;
199
}
200
 
201
function imscp_recursive_href($manifestfilename, $imscp, $context) {
202
    $fs = get_file_storage();
203
 
204
    $dirname = dirname($manifestfilename);
205
    $filename = basename($manifestfilename);
206
 
207
    if ($dirname !== '/') {
208
        $dirname = "/$dirname/";
209
    }
210
 
211
    if (!$manifestfile = $fs->get_file($context->id, 'mod_imscp', 'content', $imscp->revision, $dirname, $filename)) {
212
        return null;
213
    }
214
 
215
    $doc = new DOMDocument();
216
    if (!$doc->loadXML($manifestfile->get_content(), LIBXML_NONET)) {
217
        return null;
218
    }
219
 
220
    $xmlresources = $doc->getElementsByTagName('resource');
221
    foreach ($xmlresources as $res) {
222
        if (!$href = $res->attributes->getNamedItem('href')) {
223
            $fileresources = $res->getElementsByTagName('file');
224
            foreach ($fileresources as $file) {
225
                $href = $file->getAttribute('href');
226
                if (pathinfo($href, PATHINFO_EXTENSION) == 'xml') {
227
                    $href = imscp_recursive_href($href, $imscp, $context);
228
                }
229
 
230
                if (pathinfo($href, PATHINFO_EXTENSION) == 'htm' || pathinfo($href, PATHINFO_EXTENSION) == 'html') {
231
                    return $href;
232
                }
233
            }
234
        }
235
    }
236
 
237
    return $manifestfilename;
238
}
239
 
240
function imscp_recursive_item($xmlitem, $level, $resources) {
241
    $identifierref = '';
242
    if ($identifierref = $xmlitem->attributes->getNamedItem('identifierref')) {
243
        $identifierref = $identifierref->nodeValue;
244
    }
245
 
246
    $title = '?';
247
    $subitems = array();
248
 
249
    foreach ($xmlitem->childNodes as $child) {
250
        if ($child->nodeName === 'title') {
251
            $title = $child->textContent;
252
 
253
        } else if ($child->nodeName === 'item') {
254
            if ($subitem = imscp_recursive_item($child, $level + 1, $resources)) {
255
                $subitems[] = $subitem;
256
            }
257
        }
258
    }
259
 
260
    return array('href'     => isset($resources[$identifierref]) ? $resources[$identifierref] : '',
261
                 'title'    => $title,
262
                 'level'    => $level,
263
                 'subitems' => $subitems,
264
                );
265
}
266
 
267
/**
268
 * File browsing support class
269
 *
270
 * @copyright  2009 Petr Skoda  {@link http://skodak.org}
271
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
272
 */
273
class imscp_file_info extends file_info {
274
    protected $course;
275
    protected $cm;
276
    protected $areas;
277
    protected $filearea;
278
 
279
    public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
280
        parent::__construct($browser, $context);
281
        $this->course   = $course;
282
        $this->cm       = $cm;
283
        $this->areas    = $areas;
284
        $this->filearea = $filearea;
285
    }
286
 
287
    /**
288
     * Returns list of standard virtual file/directory identification.
289
     * The difference from stored_file parameters is that null values
290
     * are allowed in all fields
291
     * @return array with keys contextid, filearea, itemid, filepath and filename
292
     */
293
    public function get_params() {
294
        return array('contextid' => $this->context->id,
295
                     'component' => 'mod_imscp',
296
                     'filearea'  => $this->filearea,
297
                     'itemid'    => null,
298
                     'filepath'  => null,
299
                     'filename'  => null);
300
    }
301
 
302
    /**
303
     * Returns localised visible name.
304
     * @return string
305
     */
306
    public function get_visible_name() {
307
        return $this->areas[$this->filearea];
308
    }
309
 
310
    /**
311
     * Can I add new files or directories?
312
     * @return bool
313
     */
314
    public function is_writable() {
315
        return false;
316
    }
317
 
318
    /**
319
     * Is directory?
320
     * @return bool
321
     */
322
    public function is_directory() {
323
        return true;
324
    }
325
 
326
    /**
327
     * Returns list of children.
328
     * @return array of file_info instances
329
     */
330
    public function get_children() {
331
        return $this->get_filtered_children('*', false, true);
332
    }
333
 
334
    /**
335
     * Help function to return files matching extensions or their count
336
     *
337
     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
338
     * @param bool|int $countonly if false returns the children, if an int returns just the
339
     *    count of children but stops counting when $countonly number of children is reached
340
     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
341
     * @return array|int array of file_info instances or the count
342
     */
343
    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
344
        global $DB;
345
        $params = array('contextid' => $this->context->id,
346
            'component' => 'mod_imscp',
347
            'filearea' => $this->filearea);
348
        $sql = 'SELECT DISTINCT itemid
349
                    FROM {files}
350
                    WHERE contextid = :contextid
351
                    AND component = :component
352
                    AND filearea = :filearea';
353
        if (!$returnemptyfolders) {
354
            $sql .= ' AND filename <> :emptyfilename';
355
            $params['emptyfilename'] = '.';
356
        }
357
        list($sql2, $params2) = $this->build_search_files_sql($extensions);
358
        $sql .= ' '.$sql2;
359
        $params = array_merge($params, $params2);
360
        if ($countonly !== false) {
361
            $sql .= ' ORDER BY itemid';
362
        }
363
 
364
        $rs = $DB->get_recordset_sql($sql, $params);
365
        $children = array();
366
        foreach ($rs as $record) {
367
            if ($child = $this->browser->get_file_info($this->context, 'mod_imscp', $this->filearea, $record->itemid)) {
368
                $children[] = $child;
369
                if ($countonly !== false && count($children) >= $countonly) {
370
                    break;
371
                }
372
            }
373
        }
374
        $rs->close();
375
        if ($countonly !== false) {
376
            return count($children);
377
        }
378
        return $children;
379
    }
380
 
381
    /**
382
     * Returns list of children which are either files matching the specified extensions
383
     * or folders that contain at least one such file.
384
     *
385
     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
386
     * @return array of file_info instances
387
     */
388
    public function get_non_empty_children($extensions = '*') {
389
        return $this->get_filtered_children($extensions, false);
390
    }
391
 
392
    /**
393
     * Returns the number of children which are either files matching the specified extensions
394
     * or folders containing at least one such file.
395
     *
396
     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
397
     * @param int $limit stop counting after at least $limit non-empty children are found
398
     * @return int
399
     */
400
    public function count_non_empty_children($extensions = '*', $limit = 1) {
401
        return $this->get_filtered_children($extensions, $limit);
402
    }
403
 
404
    /**
405
     * Returns parent file_info instance
406
     * @return file_info or null for root
407
     */
408
    public function get_parent() {
409
        return $this->browser->get_file_info($this->context);
410
    }
411
}