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 plugin is used to access local files
19
 *
20
 * @since Moodle 2.0
21
 * @package    repository_local
22
 * @copyright  2010 Dongsheng Cai {@link http://dongsheng.org}
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
require_once($CFG->dirroot . '/repository/lib.php');
26
 
27
/**
28
 * repository_local class is used to browse moodle files
29
 *
30
 * @since Moodle 2.0
31
 * @package    repository_local
32
 * @copyright  2012 Marina Glancy
33
 * @copyright  2009 Dongsheng Cai {@link http://dongsheng.org}
34
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 */
36
class repository_local extends repository {
37
    /**
38
     * Get file listing
39
     *
40
     * @param string $encodedpath
41
     * @param string $page no paging is used in repository_local
42
     * @return mixed
43
     */
44
    public function get_listing($encodedpath = '', $page = '') {
45
        global $CFG, $USER, $OUTPUT;
46
        $ret = array();
47
        $ret['dynload'] = true;
48
        $ret['nosearch'] = false;
49
        $ret['nologin'] = true;
50
        $ret['list'] = array();
51
 
52
        $itemid   = null;
53
        $filename = null;
54
        $filearea = null;
55
        $filepath = null;
56
        $component = null;
57
 
58
        if (!empty($encodedpath)) {
59
            $params = json_decode(base64_decode($encodedpath), true);
60
            if (is_array($params) && isset($params['contextid'])) {
61
                $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT);
62
                $filearea  = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA);
63
                $itemid    = is_null($params['itemid']) ? NULL : clean_param($params['itemid'], PARAM_INT);
64
                $filepath  = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH);
65
                $filename  = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE);
66
                $context = context::instance_by_id(clean_param($params['contextid'], PARAM_INT));
67
            }
68
        }
69
        if (empty($context) && !empty($this->context)) {
70
            $context = $this->context->get_course_context(false);
71
        }
72
        if (empty($context)) {
73
            $context = context_system::instance();
74
        }
75
 
76
        // prepare list of allowed extensions: $extensions is either string '*'
77
        // or array of lowercase extensions, i.e. array('.gif','.jpg')
78
        $extensions = optional_param_array('accepted_types', '', PARAM_RAW);
79
        if (empty($extensions) || $extensions === '*' || (is_array($extensions) && in_array('*', $extensions))) {
80
            $extensions = '*';
81
        } else {
82
            if (!is_array($extensions)) {
83
                $extensions = array($extensions);
84
            }
85
            $extensions = array_map('core_text::strtolower', $extensions);
86
        }
87
 
88
        // build file tree
89
        $browser = get_file_browser();
90
        if (!($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename))) {
91
            // if file doesn't exist, build path nodes root of current context
92
            $fileinfo = $browser->get_file_info($context, null, null, null, null, null);
93
        }
94
        $ret['list'] = $this->get_non_empty_children($fileinfo, $extensions);
95
 
96
        // build path navigation
97
        $path = array();
98
        for ($level = $fileinfo; $level; $level = $level->get_parent()) {
99
            array_unshift($path, $level);
100
        }
101
        array_unshift($path, null);
102
        $ret['path'] = array();
103
        for ($i=1; $i<count($path); $i++) {
104
            if ($path[$i] == $fileinfo || !$this->can_skip($path[$i], $extensions, $path[$i-1])) {
105
                $ret['path'][] = $this->get_node_path($path[$i]);
106
            }
107
        }
108
        return $ret;
109
    }
110
 
111
    /**
112
     * Tells how the file can be picked from this repository
113
     *
114
     * @return int
115
     */
116
    public function supported_returntypes() {
117
        return FILE_INTERNAL | FILE_REFERENCE;
118
    }
119
 
120
    /**
121
     * Does this repository used to browse moodle files?
122
     *
123
     * @return boolean
124
     */
125
    public function has_moodle_files() {
126
        return true;
127
    }
128
 
129
    /**
130
     * Returns all children elements that have one of the specified extensions
131
     *
132
     * This function may skip subfolders and recursively add their children
133
     * {@link repository_local::can_skip()}
134
     *
135
     * @param file_info|null $fileinfo
136
     * @param string|string[] $extensions for example '*' or ['.gif', '.jpg']
137
     * @return array array of file_info elements
138
     */
139
    private function get_non_empty_children(?file_info $fileinfo, $extensions): array {
140
        if ($fileinfo === null) {
141
            return [];
142
        }
143
        $nonemptychildren = $fileinfo->get_non_empty_children($extensions);
144
        $list = array();
145
        foreach ($nonemptychildren as $child) {
146
            if ($this->can_skip($child, $extensions, $fileinfo)) {
147
                $list = array_merge($list, $this->get_non_empty_children($child, $extensions));
148
            } else {
149
                $list[] = $this->get_node($child);
150
            }
151
        }
152
        return $list;
153
    }
154
 
155
    /**
156
     * Whether this folder may be skipped in folder hierarchy
157
     *
158
     * 1. Skip the name of a single filearea in a module
159
     * 2. Skip course categories for non-admins who do not have navshowmycoursecategories setting
160
     *
161
     * @param file_info $fileinfo
162
     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
163
     * @param file_info|int $parent specify parent here if we know it to avoid creating extra objects
164
     * @return bool
165
     */
166
    private function can_skip(file_info $fileinfo, $extensions, $parent = -1) {
167
        global $CFG;
168
        if (!$fileinfo->is_directory()) {
169
            // do not skip files
170
            return false;
171
        }
172
        if ($fileinfo instanceof file_info_context_course ||
173
            $fileinfo instanceof file_info_context_user ||
174
            $fileinfo instanceof file_info_area_course_legacy ||
175
            $fileinfo instanceof file_info_context_module ||
176
            $fileinfo instanceof file_info_context_system) {
177
            // These instances can never be filearea inside an activity, they will never be skipped.
178
            return false;
179
        } else if ($fileinfo instanceof file_info_context_coursecat) {
180
            // This is a course category. For non-admins we do not display categories
181
            return empty($CFG->navshowmycoursecategories) &&
182
                            !has_capability('moodle/course:update', context_system::instance());
183
        } else {
184
            $params = $fileinfo->get_params();
185
            if (strlen($params['filearea']) &&
186
                    ($params['filepath'] === '/' || empty($params['filepath'])) &&
187
                    ($params['filename'] === '.' || empty($params['filename'])) &&
188
                    context::instance_by_id($params['contextid'])->contextlevel == CONTEXT_MODULE) {
189
                if ($parent === -1) {
190
                    $parent = $fileinfo->get_parent();
191
                }
192
                // This is a filearea inside an activity, it can be skipped if it has no non-empty siblings
193
                if ($parent && ($parent instanceof file_info_context_module)) {
194
                    if ($parent->count_non_empty_children($extensions, 2) <= 1) {
195
                        return true;
196
                    }
197
                }
198
            }
199
        }
200
        return false;
201
    }
202
 
203
    /**
204
     * Converts file_info object to element of repository return list
205
     *
206
     * @param file_info $fileinfo
207
     * @return array
208
     */
209
    private function get_node(file_info $fileinfo) {
210
        global $OUTPUT;
211
        $encodedpath = base64_encode(json_encode($fileinfo->get_params()));
212
        $node = array(
213
            'title' => $fileinfo->get_visible_name(),
214
            'datemodified' => $fileinfo->get_timemodified(),
215
            'datecreated' => $fileinfo->get_timecreated()
216
        );
217
        if ($fileinfo->is_directory()) {
218
            $node['path'] = $encodedpath;
219
            $node['thumbnail'] = $OUTPUT->image_url(file_folder_icon())->out(false);
220
            $node['children'] = array();
221
        } else {
222
            $node['size'] = $fileinfo->get_filesize();
223
            $node['author'] = $fileinfo->get_author();
224
            $node['license'] = $fileinfo->get_license();
225
            $node['isref'] = $fileinfo->is_external_file();
226
            if ($fileinfo->get_status() == 666) {
227
                $node['originalmissing'] = true;
228
            }
229
            $node['source'] = $encodedpath;
230
            $node['thumbnail'] = $OUTPUT->image_url(file_file_icon($fileinfo))->out(false);
231
            $node['icon'] = $OUTPUT->image_url(file_file_icon($fileinfo))->out(false);
232
            if ($imageinfo = $fileinfo->get_imageinfo()) {
233
                // what a beautiful picture, isn't it
234
                $fileurl = new moodle_url($fileinfo->get_url());
235
                $node['realthumbnail'] = $fileurl->out(false, array('preview' => 'thumb', 'oid' => $fileinfo->get_timemodified()));
236
                $node['realicon'] = $fileurl->out(false, array('preview' => 'tinyicon', 'oid' => $fileinfo->get_timemodified()));
237
                $node['image_width'] = $imageinfo['width'];
238
                $node['image_height'] = $imageinfo['height'];
239
            }
240
        }
241
        return $node;
242
    }
243
 
244
    /**
245
     * Converts file_info object to element of repository return path
246
     *
247
     * @param file_info $fileinfo
248
     * @return array
249
     */
250
    private function get_node_path(file_info $fileinfo) {
251
        $encodedpath = base64_encode(json_encode($fileinfo->get_params()));
252
        return array(
253
            'path' => $encodedpath,
254
            'name' => $fileinfo->get_visible_name()
255
        );
256
    }
257
 
258
    /**
259
     * Search through all the files.
260
     *
261
     * This method will do a raw search through the database, then will try
262
     * to match with files that a user can access. A maximum of 50 files will be
263
     * returned at a time, excluding possible duplicates found along the way.
264
     *
265
     * Queries are done in chunk of 100 files to prevent too many records to be fetched
266
     * at once. When too many files are not included, or a maximum of 10 queries are
267
     * performed we consider that this was the last page.
268
     *
269
     * @param  String  $q    The query string.
270
     * @param  integer $page The page number.
271
     * @return array of results.
272
     */
273
    public function search($q, $page = 1) {
274
        global $DB, $SESSION;
275
 
276
        // Because the repository API is weird, the first page is 0, but it should be 1.
277
        if (!$page) {
278
            $page = 1;
279
        }
280
 
281
        if (!isset($SESSION->repository_local_search)) {
282
            $SESSION->repository_local_search = array();
283
        }
284
 
285
        $fs = get_file_storage();
286
        $fb = get_file_browser();
287
 
288
        $max = 50;
289
        $limit = 100;
290
        if ($page <= 1) {
291
            $SESSION->repository_local_search['query'] = $q;
292
            $SESSION->repository_local_search['from'] = 0;
293
            $from = 0;
294
        } else {
295
            // Yes, the repository does not send the query again...
296
            $q = $SESSION->repository_local_search['query'];
297
            $from = (int) $SESSION->repository_local_search['from'];
298
        }
299
 
300
        $count = $fs->search_server_files('%' . $DB->sql_like_escape($q) . '%', null, null, true);
301
        $remaining = $count - $from;
302
        $maxloops = 3000;
303
        $loops = 0;
304
 
305
        $results = array();
306
        while (count($results) < $max && $maxloops > 0 && $remaining > 0) {
307
            if (empty($files)) {
308
                $files = $fs->search_server_files('%' . $DB->sql_like_escape($q) . '%', $from, $limit);
309
                $from += $limit;
310
            };
311
 
312
            $remaining--;
313
            $maxloops--;
314
            $loops++;
315
 
316
            $file = array_shift($files);
317
            if (!$file) {
318
                // This should not happen.
319
                throw new coding_exception('Unexpected end of files list.');
320
            }
321
 
322
            $key = $file->get_contenthash() . ':' . $file->get_filename();
323
            if (isset($results[$key])) {
324
                // We found the file with same content and same name, let's skip it.
325
                continue;
326
            }
327
 
328
            $ctx = context::instance_by_id($file->get_contextid());
329
            $fileinfo = $fb->get_file_info($ctx, $file->get_component(), $file->get_filearea(), $file->get_itemid(),
330
                $file->get_filepath(), $file->get_filename());
331
            if ($fileinfo) {
332
                $results[$key] = $this->get_node($fileinfo);
333
            }
334
 
335
        }
336
 
337
        // Save the position for the paging to work.
338
        if ($maxloops > 0 && $remaining > 0) {
339
            $SESSION->repository_local_search['from'] += $loops;
340
            $pages = -1;
341
        } else {
342
            $SESSION->repository_local_search['from'] = 0;
343
            $pages = 0;
344
        }
345
 
346
        $return = array(
347
            'list' => array_values($results),
348
            'dynload' => true,
349
            'pages' => $pages,
350
            'page' => $page
351
        );
352
 
353
        return $return;
354
    }
355
 
356
    /**
357
     * Is this repository accessing private data?
358
     *
359
     * @return bool
360
     */
361
    public function contains_private_data() {
362
        return false;
363
    }
364
}