Commit | Line | Data |
---|---|---|
12c79bfd | 1 | <?php |
6f2cd52a DC |
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 | ||
aca64b79 | 17 | /** |
6f2cd52a | 18 | * This file contains classes used to manage the repository plugins in Moodle |
6f2cd52a DC |
19 | * |
20 | * @since 2.0 | |
72a56555 | 21 | * @package core_repository |
67233725 DC |
22 | * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org} |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
aca64b79 | 24 | */ |
25 | ||
e56de3a4 | 26 | defined('MOODLE_INTERNAL') || die(); |
c19f8e7d | 27 | require_once($CFG->libdir . '/filelib.php'); |
28 | require_once($CFG->libdir . '/formslib.php'); | |
41076c58 | 29 | |
67233725 DC |
30 | define('FILE_EXTERNAL', 1); |
31 | define('FILE_INTERNAL', 2); | |
32 | define('FILE_REFERENCE', 4); | |
f392caba | 33 | define('RENAME_SUFFIX', '_2'); |
41076c58 | 34 | |
a6600395 | 35 | /** |
6f2cd52a DC |
36 | * This class is used to manage repository plugins |
37 | * | |
a6600395 | 38 | * A repository_type is a repository plug-in. It can be Box.net, Flick-r, ... |
39 | * A repository type can be edited, sorted and hidden. It is mandatory for an | |
40 | * administrator to create a repository type in order to be able to create | |
41 | * some instances of this type. | |
a6600395 | 42 | * Coding note: |
43 | * - a repository_type object is mapped to the "repository" database table | |
44 | * - "typename" attibut maps the "type" database field. It is unique. | |
45 | * - general "options" for a repository type are saved in the config_plugin table | |
eb239694 | 46 | * - when you delete a repository, all instances are deleted, and general |
47 | * options are also deleted from database | |
a6600395 | 48 | * - When you create a type for a plugin that can't have multiple instances, a |
49 | * instance is automatically created. | |
6f2cd52a | 50 | * |
72a56555 | 51 | * @package core_repository |
6f2cd52a DC |
52 | * @copyright 2009 Jerome Mouneyrac |
53 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
a6600395 | 54 | */ |
55 | class repository_type { | |
56 | ||
57 | ||
58 | /** | |
59 | * Type name (no whitespace) - A type name is unique | |
60 | * Note: for a user-friendly type name see get_readablename() | |
61 | * @var String | |
62 | */ | |
63 | private $_typename; | |
64 | ||
65 | ||
66 | /** | |
67 | * Options of this type | |
68 | * They are general options that any instance of this type would share | |
69 | * e.g. API key | |
70 | * These options are saved in config_plugin table | |
71 | * @var array | |
72 | */ | |
7a3b93c1 | 73 | private $_options; |
a6600395 | 74 | |
75 | ||
76 | /** | |
77 | * Is the repository type visible or hidden | |
78 | * If false (hidden): no instances can be created, edited, deleted, showned , used... | |
79 | * @var boolean | |
80 | */ | |
7a3b93c1 | 81 | private $_visible; |
a6600395 | 82 | |
83 | ||
84 | /** | |
85 | * 0 => not ordered, 1 => first position, 2 => second position... | |
86 | * A not order type would appear in first position (should never happened) | |
87 | * @var integer | |
88 | */ | |
89 | private $_sortorder; | |
90 | ||
67233725 | 91 | /** |
f48fb4d6 | 92 | * Return if the instance is visible in a context |
67233725 DC |
93 | * |
94 | * @todo check if the context visibility has been overwritten by the plugin creator | |
f48fb4d6 | 95 | * (need to create special functions to be overvwritten in repository class) |
67233725 DC |
96 | * @param stdClass $context context |
97 | * @return bool | |
f48fb4d6 | 98 | */ |
5363905a DC |
99 | public function get_contextvisibility($context) { |
100 | global $USER; | |
dbc01944 | 101 | |
5363905a | 102 | if ($context->contextlevel == CONTEXT_COURSE) { |
f48fb4d6 | 103 | return $this->_options['enablecourseinstances']; |
104 | } | |
105 | ||
5363905a | 106 | if ($context->contextlevel == CONTEXT_USER) { |
f48fb4d6 | 107 | return $this->_options['enableuserinstances']; |
108 | } | |
109 | ||
110 | //the context is SITE | |
111 | return true; | |
112 | } | |
dbc01944 | 113 | |
f48fb4d6 | 114 | |
115 | ||
a6600395 | 116 | /** |
117 | * repository_type constructor | |
67233725 DC |
118 | * |
119 | * @param int $typename | |
a6600395 | 120 | * @param array $typeoptions |
67233725 DC |
121 | * @param bool $visible |
122 | * @param int $sortorder (don't really need set, it will be during create() call) | |
a6600395 | 123 | */ |
a642bf6f | 124 | public function __construct($typename = '', $typeoptions = array(), $visible = true, $sortorder = 0) { |
a6600395 | 125 | global $CFG; |
126 | ||
127 | //set type attributs | |
128 | $this->_typename = $typename; | |
129 | $this->_visible = $visible; | |
130 | $this->_sortorder = $sortorder; | |
46dd6bb0 | 131 | |
a6600395 | 132 | //set options attribut |
133 | $this->_options = array(); | |
a5adfa26 | 134 | $options = repository::static_function($typename, 'get_type_option_names'); |
a6600395 | 135 | //check that the type can be setup |
06e65e1e | 136 | if (!empty($options)) { |
a6600395 | 137 | //set the type options |
138 | foreach ($options as $config) { | |
a5adfa26 | 139 | if (array_key_exists($config, $typeoptions)) { |
7a3b93c1 | 140 | $this->_options[$config] = $typeoptions[$config]; |
a6600395 | 141 | } |
142 | } | |
143 | } | |
f48fb4d6 | 144 | |
145 | //retrieve visibility from option | |
146 | if (array_key_exists('enablecourseinstances',$typeoptions)) { | |
147 | $this->_options['enablecourseinstances'] = $typeoptions['enablecourseinstances']; | |
9f7c761a | 148 | } else { |
149 | $this->_options['enablecourseinstances'] = 0; | |
f48fb4d6 | 150 | } |
dbc01944 | 151 | |
f48fb4d6 | 152 | if (array_key_exists('enableuserinstances',$typeoptions)) { |
153 | $this->_options['enableuserinstances'] = $typeoptions['enableuserinstances']; | |
9f7c761a | 154 | } else { |
155 | $this->_options['enableuserinstances'] = 0; | |
f48fb4d6 | 156 | } |
dbc01944 | 157 | |
a6600395 | 158 | } |
159 | ||
160 | /** | |
161 | * Get the type name (no whitespace) | |
162 | * For a human readable name, use get_readablename() | |
67233725 DC |
163 | * |
164 | * @return string the type name | |
a6600395 | 165 | */ |
7a3b93c1 | 166 | public function get_typename() { |
a6600395 | 167 | return $this->_typename; |
168 | } | |
169 | ||
170 | /** | |
171 | * Return a human readable and user-friendly type name | |
67233725 | 172 | * |
a6600395 | 173 | * @return string user-friendly type name |
174 | */ | |
7a3b93c1 | 175 | public function get_readablename() { |
614d18d2 | 176 | return get_string('pluginname','repository_'.$this->_typename); |
a6600395 | 177 | } |
178 | ||
179 | /** | |
180 | * Return general options | |
67233725 | 181 | * |
a6600395 | 182 | * @return array the general options |
183 | */ | |
7a3b93c1 | 184 | public function get_options() { |
a6600395 | 185 | return $this->_options; |
186 | } | |
187 | ||
188 | /** | |
189 | * Return visibility | |
67233725 DC |
190 | * |
191 | * @return bool | |
a6600395 | 192 | */ |
7a3b93c1 | 193 | public function get_visible() { |
a6600395 | 194 | return $this->_visible; |
195 | } | |
196 | ||
197 | /** | |
198 | * Return order / position of display in the file picker | |
67233725 DC |
199 | * |
200 | * @return int | |
a6600395 | 201 | */ |
7a3b93c1 | 202 | public function get_sortorder() { |
a6600395 | 203 | return $this->_sortorder; |
204 | } | |
205 | ||
206 | /** | |
207 | * Create a repository type (the type name must not already exist) | |
67233725 | 208 | * @param bool $silent throw exception? |
a75c78d3 | 209 | * @return mixed return int if create successfully, return false if |
a6600395 | 210 | */ |
a75c78d3 | 211 | public function create($silent = false) { |
a6600395 | 212 | global $DB; |
213 | ||
214 | //check that $type has been set | |
215 | $timmedtype = trim($this->_typename); | |
216 | if (empty($timmedtype)) { | |
7a3b93c1 | 217 | throw new repository_exception('emptytype', 'repository'); |
a6600395 | 218 | } |
219 | ||
220 | //set sortorder as the last position in the list | |
7a3b93c1 | 221 | if (!isset($this->_sortorder) || $this->_sortorder == 0 ) { |
a6600395 | 222 | $sql = "SELECT MAX(sortorder) FROM {repository}"; |
223 | $this->_sortorder = 1 + $DB->get_field_sql($sql); | |
224 | } | |
225 | ||
226 | //only create a new type if it doesn't already exist | |
227 | $existingtype = $DB->get_record('repository', array('type'=>$this->_typename)); | |
7a3b93c1 | 228 | if (!$existingtype) { |
229 | //create the type | |
6bdfef5d | 230 | $newtype = new stdClass(); |
7a3b93c1 | 231 | $newtype->type = $this->_typename; |
232 | $newtype->visible = $this->_visible; | |
233 | $newtype->sortorder = $this->_sortorder; | |
a75c78d3 | 234 | $plugin_id = $DB->insert_record('repository', $newtype); |
7a3b93c1 | 235 | //save the options in DB |
236 | $this->update_options(); | |
237 | ||
a5adfa26 DC |
238 | $instanceoptionnames = repository::static_function($this->_typename, 'get_instance_option_names'); |
239 | ||
edb50637 | 240 | //if the plugin type has no multiple instance (e.g. has no instance option name) so it wont |
7a3b93c1 | 241 | //be possible for the administrator to create a instance |
242 | //in this case we need to create an instance | |
edb50637 | 243 | if (empty($instanceoptionnames)) { |
7a3b93c1 | 244 | $instanceoptions = array(); |
a5adfa26 DC |
245 | if (empty($this->_options['pluginname'])) { |
246 | // when moodle trying to install some repo plugin automatically | |
247 | // this option will be empty, get it from language string when display | |
248 | $instanceoptions['name'] = ''; | |
249 | } else { | |
250 | // when admin trying to add a plugin manually, he will type a name | |
251 | // for it | |
252 | $instanceoptions['name'] = $this->_options['pluginname']; | |
253 | } | |
dbc01944 | 254 | repository::static_function($this->_typename, 'create', $this->_typename, 0, get_system_context(), $instanceoptions); |
7a3b93c1 | 255 | } |
60c366e8 | 256 | //run plugin_init function |
257 | if (!repository::static_function($this->_typename, 'plugin_init')) { | |
258 | if (!$silent) { | |
259 | throw new repository_exception('cannotinitplugin', 'repository'); | |
260 | } | |
261 | } | |
262 | ||
a75c78d3 | 263 | if(!empty($plugin_id)) { |
264 | // return plugin_id if create successfully | |
265 | return $plugin_id; | |
266 | } else { | |
267 | return false; | |
268 | } | |
948c2860 | 269 | |
7a3b93c1 | 270 | } else { |
a75c78d3 | 271 | if (!$silent) { |
272 | throw new repository_exception('existingrepository', 'repository'); | |
273 | } | |
274 | // If plugin existed, return false, tell caller no new plugins were created. | |
275 | return false; | |
a6600395 | 276 | } |
277 | } | |
278 | ||
279 | ||
280 | /** | |
281 | * Update plugin options into the config_plugin table | |
67233725 | 282 | * |
a6600395 | 283 | * @param array $options |
67233725 | 284 | * @return bool |
a6600395 | 285 | */ |
7a3b93c1 | 286 | public function update_options($options = null) { |
a5adfa26 DC |
287 | global $DB; |
288 | $classname = 'repository_' . $this->_typename; | |
289 | $instanceoptions = repository::static_function($this->_typename, 'get_instance_option_names'); | |
290 | if (empty($instanceoptions)) { | |
291 | // update repository instance name if this plugin type doesn't have muliti instances | |
292 | $params = array(); | |
293 | $params['type'] = $this->_typename; | |
294 | $instances = repository::get_instances($params); | |
295 | $instance = array_pop($instances); | |
296 | if ($instance) { | |
297 | $DB->set_field('repository_instances', 'name', $options['pluginname'], array('id'=>$instance->id)); | |
298 | } | |
299 | unset($options['pluginname']); | |
300 | } | |
301 | ||
7a3b93c1 | 302 | if (!empty($options)) { |
a6600395 | 303 | $this->_options = $options; |
304 | } | |
305 | ||
306 | foreach ($this->_options as $name => $value) { | |
a5adfa26 | 307 | set_config($name, $value, $this->_typename); |
a6600395 | 308 | } |
309 | ||
310 | return true; | |
311 | } | |
312 | ||
313 | /** | |
314 | * Update visible database field with the value given as parameter | |
315 | * or with the visible value of this object | |
316 | * This function is private. | |
317 | * For public access, have a look to switch_and_update_visibility() | |
67233725 DC |
318 | * |
319 | * @param bool $visible | |
320 | * @return bool | |
a6600395 | 321 | */ |
7a3b93c1 | 322 | private function update_visible($visible = null) { |
a6600395 | 323 | global $DB; |
324 | ||
7a3b93c1 | 325 | if (!empty($visible)) { |
a6600395 | 326 | $this->_visible = $visible; |
327 | } | |
7a3b93c1 | 328 | else if (!isset($this->_visible)) { |
a6600395 | 329 | throw new repository_exception('updateemptyvisible', 'repository'); |
330 | } | |
331 | ||
332 | return $DB->set_field('repository', 'visible', $this->_visible, array('type'=>$this->_typename)); | |
333 | } | |
334 | ||
335 | /** | |
336 | * Update database sortorder field with the value given as parameter | |
337 | * or with the sortorder value of this object | |
338 | * This function is private. | |
339 | * For public access, have a look to move_order() | |
67233725 DC |
340 | * |
341 | * @param int $sortorder | |
342 | * @return bool | |
a6600395 | 343 | */ |
7a3b93c1 | 344 | private function update_sortorder($sortorder = null) { |
a6600395 | 345 | global $DB; |
346 | ||
7a3b93c1 | 347 | if (!empty($sortorder) && $sortorder!=0) { |
a6600395 | 348 | $this->_sortorder = $sortorder; |
349 | } | |
350 | //if sortorder is not set, we set it as the ;ast position in the list | |
7a3b93c1 | 351 | else if (!isset($this->_sortorder) || $this->_sortorder == 0 ) { |
a6600395 | 352 | $sql = "SELECT MAX(sortorder) FROM {repository}"; |
353 | $this->_sortorder = 1 + $DB->get_field_sql($sql); | |
354 | } | |
355 | ||
356 | return $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$this->_typename)); | |
357 | } | |
358 | ||
359 | /** | |
360 | * Change order of the type with its adjacent upper or downer type | |
361 | * (database fields are updated) | |
362 | * Algorithm details: | |
363 | * 1. retrieve all types in an array. This array is sorted by sortorder, | |
364 | * and the array keys start from 0 to X (incremented by 1) | |
365 | * 2. switch sortorder values of this type and its adjacent type | |
67233725 | 366 | * |
a6600395 | 367 | * @param string $move "up" or "down" |
368 | */ | |
369 | public function move_order($move) { | |
370 | global $DB; | |
a6600395 | 371 | |
dbc01944 | 372 | $types = repository::get_types(); // retrieve all types |
7a3b93c1 | 373 | |
67233725 | 374 | // retrieve this type into the returned array |
7a3b93c1 | 375 | $i = 0; |
376 | while (!isset($indice) && $i<count($types)) { | |
377 | if ($types[$i]->get_typename() == $this->_typename) { | |
a6600395 | 378 | $indice = $i; |
379 | } | |
380 | $i++; | |
381 | } | |
382 | ||
67233725 | 383 | // retrieve adjacent indice |
a6600395 | 384 | switch ($move) { |
385 | case "up": | |
386 | $adjacentindice = $indice - 1; | |
7a3b93c1 | 387 | break; |
a6600395 | 388 | case "down": |
389 | $adjacentindice = $indice + 1; | |
7a3b93c1 | 390 | break; |
a6600395 | 391 | default: |
7a3b93c1 | 392 | throw new repository_exception('movenotdefined', 'repository'); |
a6600395 | 393 | } |
394 | ||
395 | //switch sortorder of this type and the adjacent type | |
396 | //TODO: we could reset sortorder for all types. This is not as good in performance term, but | |
397 | //that prevent from wrong behaviour on a screwed database. As performance are not important in this particular case | |
398 | //it worth to change the algo. | |
7a3b93c1 | 399 | if ($adjacentindice>=0 && !empty($types[$adjacentindice])) { |
a6600395 | 400 | $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$types[$adjacentindice]->get_typename())); |
401 | $this->update_sortorder($types[$adjacentindice]->get_sortorder()); | |
402 | } | |
403 | } | |
404 | ||
405 | /** | |
79698344 | 406 | * 1. Change visibility to the value chosen |
a6600395 | 407 | * 2. Update the type |
67233725 DC |
408 | * |
409 | * @param bool $visible | |
410 | * @return bool | |
a6600395 | 411 | */ |
79698344 MD |
412 | public function update_visibility($visible = null) { |
413 | if (is_bool($visible)) { | |
414 | $this->_visible = $visible; | |
415 | } else { | |
416 | $this->_visible = !$this->_visible; | |
417 | } | |
a6600395 | 418 | return $this->update_visible(); |
419 | } | |
420 | ||
421 | ||
422 | /** | |
eb239694 | 423 | * Delete a repository_type (general options are removed from config_plugin |
424 | * table, and all instances are deleted) | |
67233725 DC |
425 | * |
426 | * @param bool $downloadcontents download external contents if exist | |
427 | * @return bool | |
a6600395 | 428 | */ |
67233725 | 429 | public function delete($downloadcontents = false) { |
a6600395 | 430 | global $DB; |
46dd6bb0 | 431 | |
432 | //delete all instances of this type | |
6f2cd52a DC |
433 | $params = array(); |
434 | $params['context'] = array(); | |
435 | $params['onlyvisible'] = false; | |
436 | $params['type'] = $this->_typename; | |
437 | $instances = repository::get_instances($params); | |
7a3b93c1 | 438 | foreach ($instances as $instance) { |
67233725 | 439 | $instance->delete($downloadcontents); |
46dd6bb0 | 440 | } |
441 | ||
eb239694 | 442 | //delete all general options |
7a3b93c1 | 443 | foreach ($this->_options as $name => $value) { |
eb239694 | 444 | set_config($name, null, $this->_typename); |
445 | } | |
446 | ||
67233725 DC |
447 | try { |
448 | $DB->delete_records('repository', array('type' => $this->_typename)); | |
449 | } catch (dml_exception $ex) { | |
450 | return false; | |
451 | } | |
452 | return true; | |
a6600395 | 453 | } |
454 | } | |
455 | ||
a6600395 | 456 | /** |
67233725 | 457 | * This is the base class of the repository class. |
8d419e59 | 458 | * |
67233725 DC |
459 | * To create repository plugin, see: {@link http://docs.moodle.org/dev/Repository_plugins} |
460 | * See an example: {@link repository_boxnet} | |
8d419e59 | 461 | * |
72a56555 | 462 | * @package core_repository |
67233725 | 463 | * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org} |
6f2cd52a | 464 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
a6600395 | 465 | */ |
fca079c5 | 466 | abstract class repository { |
72a56555 MG |
467 | /** Timeout in seconds for downloading the external file into moodle */ |
468 | const GETFILE_TIMEOUT = 30; | |
469 | /** Timeout in seconds for syncronising the external file size */ | |
470 | const SYNCFILE_TIMEOUT = 1; | |
471 | /** Timeout in seconds for downloading an image file from external repository during syncronisation */ | |
472 | const SYNCIMAGE_TIMEOUT = 3; | |
473 | ||
efe018b4 | 474 | // $disabled can be set to true to disable a plugin by force |
475 | // example: self::$disabled = true | |
67233725 | 476 | /** @var bool force disable repository instance */ |
efe018b4 | 477 | public $disabled = false; |
67233725 | 478 | /** @var int repository instance id */ |
4a65c39a | 479 | public $id; |
67233725 | 480 | /** @var stdClass current context */ |
4a65c39a | 481 | public $context; |
67233725 | 482 | /** @var array repository options */ |
4a65c39a | 483 | public $options; |
67233725 | 484 | /** @var bool Whether or not the repository instance is editable */ |
948c2860 | 485 | public $readonly; |
67233725 | 486 | /** @var int return types */ |
41076c58 | 487 | public $returntypes; |
67233725 | 488 | /** @var stdClass repository instance database record */ |
a5adfa26 | 489 | public $instance; |
31581ae6 FM |
490 | /** @var string Type of repository (webdav, google_docs, dropbox, ...). Read from $this->get_typename(). */ |
491 | protected $typename; | |
492 | ||
a0ff4137 | 493 | /** |
67233725 | 494 | * Constructor |
a0ff4137 | 495 | * |
67233725 DC |
496 | * @param int $repositoryid repository instance id |
497 | * @param int|stdClass $context a context id or context object | |
e35194be | 498 | * @param array $options repository options |
67233725 | 499 | * @param int $readonly indicate this repo is readonly or not |
a0ff4137 | 500 | */ |
e35194be | 501 | public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array(), $readonly = 0) { |
a5adfa26 | 502 | global $DB; |
a0ff4137 | 503 | $this->id = $repositoryid; |
be85f7ab | 504 | if (is_object($context)) { |
e35194be DC |
505 | $this->context = $context; |
506 | } else { | |
d197ea43 | 507 | $this->context = context::instance_by_id($context); |
e35194be | 508 | } |
a5adfa26 | 509 | $this->instance = $DB->get_record('repository_instances', array('id'=>$this->id)); |
a0ff4137 DC |
510 | $this->readonly = $readonly; |
511 | $this->options = array(); | |
e35194be | 512 | |
a0ff4137 | 513 | if (is_array($options)) { |
67233725 | 514 | // The get_option() method will get stored options in database. |
a0ff4137 DC |
515 | $options = array_merge($this->get_option(), $options); |
516 | } else { | |
517 | $options = $this->get_option(); | |
518 | } | |
519 | foreach ($options as $n => $v) { | |
520 | $this->options[$n] = $v; | |
521 | } | |
522 | $this->name = $this->get_name(); | |
523 | $this->returntypes = $this->supported_returntypes(); | |
524 | $this->super_called = true; | |
525 | } | |
1cf56396 | 526 | |
67233725 DC |
527 | /** |
528 | * Get repository instance using repository id | |
529 | * | |
cfb71351 MG |
530 | * Note that this function does not check permission to access repository contents |
531 | * | |
532 | * @throws repository_exception | |
533 | * | |
534 | * @param int $repositoryid repository instance ID | |
535 | * @param context|int $context context instance or context ID where this repository will be used | |
6dc82385 | 536 | * @param array $options additional repository options |
67233725 DC |
537 | * @return repository |
538 | */ | |
6dc82385 | 539 | public static function get_repository_by_id($repositoryid, $context, $options = array()) { |
67233725 | 540 | global $CFG, $DB; |
cfb71351 MG |
541 | if (!is_object($context)) { |
542 | $context = context::instance_by_id($context); | |
543 | } | |
67233725 | 544 | |
cfb71351 MG |
545 | $sql = "SELECT i.id, i.name, i.typeid, i.readonly, r.type AS repositorytype, r.visible |
546 | FROM {repository_instances} i | |
547 | JOIN {repository} r ON r.id = i.typeid | |
548 | WHERE i.id = ?"; | |
67233725 DC |
549 | |
550 | if (!$record = $DB->get_record_sql($sql, array($repositoryid))) { | |
551 | throw new repository_exception('invalidrepositoryid', 'repository'); | |
552 | } else { | |
cfb71351 | 553 | $type = $record->repositorytype; |
67233725 DC |
554 | if (file_exists($CFG->dirroot . "/repository/$type/lib.php")) { |
555 | require_once($CFG->dirroot . "/repository/$type/lib.php"); | |
556 | $classname = 'repository_' . $type; | |
6dc82385 MG |
557 | $options['type'] = $type; |
558 | $options['typeid'] = $record->typeid; | |
cfb71351 | 559 | $options['visible'] = $record->visible; |
6dc82385 MG |
560 | if (empty($options['name'])) { |
561 | $options['name'] = $record->name; | |
562 | } | |
cfb71351 | 563 | $repository = new $classname($repositoryid, $context, $options, $record->readonly); |
67233725 DC |
564 | return $repository; |
565 | } else { | |
6dc82385 | 566 | throw new repository_exception('invalidplugin', 'repository'); |
67233725 DC |
567 | } |
568 | } | |
569 | } | |
570 | ||
31581ae6 FM |
571 | /** |
572 | * Returns the type name of the repository. | |
573 | * | |
574 | * @return string type name of the repository. | |
575 | * @since 2.5 | |
576 | */ | |
577 | public function get_typename() { | |
578 | if (empty($this->typename)) { | |
579 | $matches = array(); | |
580 | if (!preg_match("/^repository_(.*)$/", get_class($this), $matches)) { | |
581 | throw new coding_exception('The class name of a repository should be repository_<typeofrepository>, '. | |
582 | 'e.g. repository_dropbox'); | |
583 | } | |
584 | $this->typename = $matches[1]; | |
585 | } | |
586 | return $this->typename; | |
587 | } | |
588 | ||
dbc01944 | 589 | /** |
e35194be | 590 | * Get a repository type object by a given type name. |
67233725 DC |
591 | * |
592 | * @static | |
e35194be DC |
593 | * @param string $typename the repository type name |
594 | * @return repository_type|bool | |
dbc01944 | 595 | */ |
596 | public static function get_type_by_typename($typename) { | |
597 | global $DB; | |
598 | ||
599 | if (!$record = $DB->get_record('repository',array('type' => $typename))) { | |
600 | return false; | |
601 | } | |
602 | ||
603 | return new repository_type($typename, (array)get_config($typename), $record->visible, $record->sortorder); | |
604 | } | |
605 | ||
606 | /** | |
e35194be | 607 | * Get the repository type by a given repository type id. |
67233725 DC |
608 | * |
609 | * @static | |
8e0946bf DC |
610 | * @param int $id the type id |
611 | * @return object | |
dbc01944 | 612 | */ |
613 | public static function get_type_by_id($id) { | |
614 | global $DB; | |
615 | ||
616 | if (!$record = $DB->get_record('repository',array('id' => $id))) { | |
617 | return false; | |
618 | } | |
619 | ||
620 | return new repository_type($record->type, (array)get_config($record->type), $record->visible, $record->sortorder); | |
621 | } | |
622 | ||
623 | /** | |
e35194be DC |
624 | * Return all repository types ordered by sortorder field |
625 | * first repository type in returnedarray[0], second repository type in returnedarray[1], ... | |
67233725 DC |
626 | * |
627 | * @static | |
628 | * @param bool $visible can return types by visiblity, return all types if null | |
dbc01944 | 629 | * @return array Repository types |
630 | */ | |
631 | public static function get_types($visible=null) { | |
489d8a75 | 632 | global $DB, $CFG; |
dbc01944 | 633 | |
634 | $types = array(); | |
635 | $params = null; | |
636 | if (!empty($visible)) { | |
637 | $params = array('visible' => $visible); | |
638 | } | |
639 | if ($records = $DB->get_records('repository',$params,'sortorder')) { | |
640 | foreach($records as $type) { | |
e35194be | 641 | if (file_exists($CFG->dirroot . '/repository/'. $type->type .'/lib.php')) { |
489d8a75 DC |
642 | $types[] = new repository_type($type->type, (array)get_config($type->type), $type->visible, $type->sortorder); |
643 | } | |
dbc01944 | 644 | } |
645 | } | |
646 | ||
647 | return $types; | |
648 | } | |
649 | ||
650 | /** | |
31581ae6 | 651 | * Checks if user has a capability to view the current repository. |
67233725 | 652 | * |
31581ae6 FM |
653 | * @return bool true when the user can, otherwise throws an exception. |
654 | * @throws repository_exception when the user does not meet the requirements. | |
dbc01944 | 655 | */ |
6dc82385 | 656 | public final function check_capability() { |
31581ae6 FM |
657 | global $USER; |
658 | ||
e42861d4 FM |
659 | // The context we are on. |
660 | $currentcontext = $this->context; | |
661 | ||
31581ae6 | 662 | // Ensure that the user can view the repository in the current context. |
e42861d4 | 663 | $can = has_capability('repository/'.$this->get_typename().':view', $currentcontext); |
31581ae6 FM |
664 | |
665 | // Context in which the repository has been created. | |
666 | $repocontext = context::instance_by_id($this->instance->contextid); | |
667 | ||
668 | // Prevent access to private repositories when logged in as. | |
669 | if ($can && session_is_loggedinas()) { | |
670 | if ($this->contains_private_data() || $repocontext->contextlevel == CONTEXT_USER) { | |
671 | $can = false; | |
672 | } | |
6dc82385 | 673 | } |
31581ae6 | 674 | |
e42861d4 FM |
675 | // We are going to ensure that the current context was legit, and reliable to check |
676 | // the capability against. (No need to do that if we already cannot). | |
31581ae6 | 677 | if ($can) { |
e42861d4 FM |
678 | if ($repocontext->contextlevel == CONTEXT_USER) { |
679 | // The repository is a user instance, ensure we're the right user to access it! | |
680 | if ($repocontext->instanceid != $USER->id) { | |
681 | $can = false; | |
682 | } | |
683 | } else if ($repocontext->contextlevel == CONTEXT_COURSE) { | |
684 | // The repository is a course one. Let's check that we are on the right course. | |
685 | if (in_array($currentcontext->contextlevel, array(CONTEXT_COURSE, CONTEXT_MODULE, CONTEXT_BLOCK))) { | |
686 | $coursecontext = $currentcontext->get_course_context(); | |
687 | if ($coursecontext->instanceid != $repocontext->instanceid) { | |
688 | $can = false; | |
689 | } | |
690 | } else { | |
691 | // We are on a parent context, therefore it's legit to check the permissions | |
692 | // in the current context. | |
693 | } | |
31581ae6 | 694 | } else { |
e42861d4 FM |
695 | // Nothing to check here, system instances can have different permissions on different |
696 | // levels. We do not want to prevent URL hack here, because it does not make sense to | |
697 | // prevent a user to access a repository in a context if it's accessible in another one. | |
31581ae6 FM |
698 | } |
699 | } | |
700 | ||
701 | if ($can) { | |
702 | return true; | |
dbc01944 | 703 | } |
31581ae6 FM |
704 | |
705 | throw new repository_exception('nopermissiontoaccess', 'repository'); | |
dbc01944 | 706 | } |
707 | ||
f392caba | 708 | /** |
d7d69396 | 709 | * Check if file already exists in draft area. |
f392caba | 710 | * |
67233725 | 711 | * @static |
d7d69396 FM |
712 | * @param int $itemid of the draft area. |
713 | * @param string $filepath path to the file. | |
714 | * @param string $filename file name. | |
67233725 | 715 | * @return bool |
f392caba DC |
716 | */ |
717 | public static function draftfile_exists($itemid, $filepath, $filename) { | |
718 | global $USER; | |
719 | $fs = get_file_storage(); | |
21c08c63 | 720 | $usercontext = context_user::instance($USER->id); |
d7d69396 | 721 | return $fs->file_exists($usercontext->id, 'user', 'draft', $itemid, $filepath, $filename); |
f392caba DC |
722 | } |
723 | ||
c0e8f211 MG |
724 | /** |
725 | * Parses the 'source' returned by moodle repositories and returns an instance of stored_file | |
726 | * | |
727 | * @param string $source | |
728 | * @return stored_file|null | |
729 | */ | |
730 | public static function get_moodle_file($source) { | |
1bd01747 MG |
731 | $params = file_storage::unpack_reference($source, true); |
732 | $fs = get_file_storage(); | |
733 | return $fs->get_file($params['contextid'], $params['component'], $params['filearea'], | |
734 | $params['itemid'], $params['filepath'], $params['filename']); | |
735 | } | |
736 | ||
737 | /** | |
738 | * Repository method to make sure that user can access particular file. | |
739 | * | |
740 | * This is checked when user tries to pick the file from repository to deal with | |
741 | * potential parameter substitutions is request | |
742 | * | |
743 | * @param string $source | |
744 | * @return bool whether the file is accessible by current user | |
745 | */ | |
746 | public function file_is_accessible($source) { | |
747 | if ($this->has_moodle_files()) { | |
748 | try { | |
749 | $params = file_storage::unpack_reference($source, true); | |
750 | } catch (file_reference_exception $e) { | |
751 | return false; | |
c0e8f211 | 752 | } |
1bd01747 | 753 | $browser = get_file_browser(); |
6dc82385 | 754 | $context = context::instance_by_id($params['contextid']); |
1bd01747 MG |
755 | $file_info = $browser->get_file_info($context, $params['component'], $params['filearea'], |
756 | $params['itemid'], $params['filepath'], $params['filename']); | |
757 | return !empty($file_info); | |
c0e8f211 | 758 | } |
1bd01747 | 759 | return true; |
c0e8f211 MG |
760 | } |
761 | ||
f392caba | 762 | /** |
1bd01747 MG |
763 | * This function is used to copy a moodle file to draft area. |
764 | * | |
765 | * It DOES NOT check if the user is allowed to access this file because the actual file | |
766 | * can be located in the area where user does not have access to but there is an alias | |
767 | * to this file in the area where user CAN access it. | |
768 | * {@link file_is_accessible} should be called for alias location before calling this function. | |
f392caba | 769 | * |
1bd01747 | 770 | * @param string $source The metainfo of file, it is base64 encoded php serialized data |
2dfa16ef MG |
771 | * @param stdClass|array $filerecord contains itemid, filepath, filename and optionally other |
772 | * attributes of the new file | |
773 | * @param int $maxbytes maximum allowed size of file, -1 if unlimited. If size of file exceeds | |
774 | * the limit, the file_exception is thrown. | |
68acd115 FM |
775 | * @param int $areamaxbytes the maximum size of the area. A file_exception is thrown if the |
776 | * new file will reach the limit. | |
1bd01747 | 777 | * @return array The information about the created file |
f392caba | 778 | */ |
68acd115 | 779 | public function copy_to_area($source, $filerecord, $maxbytes = -1, $areamaxbytes = FILE_AREA_MAX_BYTES_UNLIMITED) { |
2dfa16ef | 780 | global $USER; |
67233725 | 781 | $fs = get_file_storage(); |
f392caba DC |
782 | |
783 | if ($this->has_moodle_files() == false) { | |
67233725 | 784 | throw new coding_exception('Only repository used to browse moodle files can use repository::copy_to_area()'); |
f392caba DC |
785 | } |
786 | ||
2dfa16ef MG |
787 | $user_context = context_user::instance($USER->id); |
788 | ||
789 | $filerecord = (array)$filerecord; | |
790 | // make sure the new file will be created in user draft area | |
791 | $filerecord['component'] = 'user'; | |
792 | $filerecord['filearea'] = 'draft'; | |
793 | $filerecord['contextid'] = $user_context->id; | |
794 | $draftitemid = $filerecord['itemid']; | |
795 | $new_filepath = $filerecord['filepath']; | |
796 | $new_filename = $filerecord['filename']; | |
f392caba | 797 | |
f392caba | 798 | // the file needs to copied to draft area |
1bd01747 MG |
799 | $stored_file = self::get_moodle_file($source); |
800 | if ($maxbytes != -1 && $stored_file->get_filesize() > $maxbytes) { | |
2dfa16ef MG |
801 | throw new file_exception('maxbytes'); |
802 | } | |
68acd115 FM |
803 | // Validate the size of the draft area. |
804 | if (file_is_draft_area_limit_reached($draftitemid, $areamaxbytes, $stored_file->get_filesize())) { | |
805 | throw new file_exception('maxareabytes'); | |
806 | } | |
f392caba DC |
807 | |
808 | if (repository::draftfile_exists($draftitemid, $new_filepath, $new_filename)) { | |
809 | // create new file | |
810 | $unused_filename = repository::get_unused_filename($draftitemid, $new_filepath, $new_filename); | |
2dfa16ef | 811 | $filerecord['filename'] = $unused_filename; |
1bd01747 | 812 | $fs->create_file_from_storedfile($filerecord, $stored_file); |
f392caba DC |
813 | $event = array(); |
814 | $event['event'] = 'fileexists'; | |
815 | $event['newfile'] = new stdClass; | |
816 | $event['newfile']->filepath = $new_filepath; | |
817 | $event['newfile']->filename = $unused_filename; | |
818 | $event['newfile']->url = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $unused_filename)->out(); | |
819 | $event['existingfile'] = new stdClass; | |
820 | $event['existingfile']->filepath = $new_filepath; | |
821 | $event['existingfile']->filename = $new_filename; | |
1bd01747 | 822 | $event['existingfile']->url = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out(); |
f392caba DC |
823 | return $event; |
824 | } else { | |
1bd01747 | 825 | $fs->create_file_from_storedfile($filerecord, $stored_file); |
f392caba DC |
826 | $info = array(); |
827 | $info['itemid'] = $draftitemid; | |
1bd01747 MG |
828 | $info['file'] = $new_filename; |
829 | $info['title'] = $new_filename; | |
f392caba | 830 | $info['contextid'] = $user_context->id; |
1bd01747 MG |
831 | $info['url'] = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out(); |
832 | $info['filesize'] = $stored_file->get_filesize(); | |
f392caba DC |
833 | return $info; |
834 | } | |
835 | } | |
836 | ||
837 | /** | |
d7d69396 FM |
838 | * Get an unused filename from the current draft area. |
839 | * | |
840 | * Will check if the file ends with ([0-9]) and increase the number. | |
f392caba | 841 | * |
67233725 | 842 | * @static |
d7d69396 FM |
843 | * @param int $itemid draft item ID. |
844 | * @param string $filepath path to the file. | |
845 | * @param string $filename name of the file. | |
846 | * @return string an unused file name. | |
f392caba DC |
847 | */ |
848 | public static function get_unused_filename($itemid, $filepath, $filename) { | |
849 | global $USER; | |
d7d69396 | 850 | $contextid = context_user::instance($USER->id)->id; |
f392caba | 851 | $fs = get_file_storage(); |
d7d69396 | 852 | return $fs->get_unused_filename($contextid, 'user', 'draft', $itemid, $filepath, $filename); |
f392caba DC |
853 | } |
854 | ||
855 | /** | |
d7d69396 | 856 | * Append a suffix to filename. |
f392caba | 857 | * |
67233725 | 858 | * @static |
f392caba DC |
859 | * @param string $filename |
860 | * @return string | |
d7d69396 | 861 | * @deprecated since 2.5 |
f392caba | 862 | */ |
061eeed5 | 863 | public static function append_suffix($filename) { |
d7d69396 FM |
864 | debugging('The function repository::append_suffix() has been deprecated. Use repository::get_unused_filename() instead.', |
865 | DEBUG_DEVELOPER); | |
f392caba DC |
866 | $pathinfo = pathinfo($filename); |
867 | if (empty($pathinfo['extension'])) { | |
868 | return $filename . RENAME_SUFFIX; | |
869 | } else { | |
870 | return $pathinfo['filename'] . RENAME_SUFFIX . '.' . $pathinfo['extension']; | |
871 | } | |
872 | } | |
873 | ||
dbc01944 | 874 | /** |
875 | * Return all types that you a user can create/edit and which are also visible | |
876 | * Note: Mostly used in order to know if at least one editable type can be set | |
67233725 DC |
877 | * |
878 | * @static | |
879 | * @param stdClass $context the context for which we want the editable types | |
dbc01944 | 880 | * @return array types |
881 | */ | |
882 | public static function get_editable_types($context = null) { | |
883 | ||
884 | if (empty($context)) { | |
885 | $context = get_system_context(); | |
886 | } | |
887 | ||
888 | $types= repository::get_types(true); | |
889 | $editabletypes = array(); | |
890 | foreach ($types as $type) { | |
891 | $instanceoptionnames = repository::static_function($type->get_typename(), 'get_instance_option_names'); | |
892 | if (!empty($instanceoptionnames)) { | |
5363905a | 893 | if ($type->get_contextvisibility($context)) { |
dbc01944 | 894 | $editabletypes[]=$type; |
895 | } | |
896 | } | |
897 | } | |
898 | return $editabletypes; | |
899 | } | |
900 | ||
901 | /** | |
902 | * Return repository instances | |
92a89068 | 903 | * |
67233725 | 904 | * @static |
92a89068 PL |
905 | * @param array $args Array containing the following keys: |
906 | * currentcontext | |
907 | * context | |
908 | * onlyvisible | |
909 | * type | |
910 | * accepted_types | |
8e0946bf | 911 | * return_types |
92a89068 PL |
912 | * userid |
913 | * | |
dbc01944 | 914 | * @return array repository instances |
915 | */ | |
6f2cd52a | 916 | public static function get_instances($args = array()) { |
dbc01944 | 917 | global $DB, $CFG, $USER; |
918 | ||
6f2cd52a DC |
919 | if (isset($args['currentcontext'])) { |
920 | $current_context = $args['currentcontext']; | |
921 | } else { | |
cfb71351 | 922 | $current_context = get_system_context(); |
6f2cd52a DC |
923 | } |
924 | ||
925 | if (!empty($args['context'])) { | |
926 | $contexts = $args['context']; | |
927 | } else { | |
928 | $contexts = array(); | |
929 | } | |
930 | ||
931 | $onlyvisible = isset($args['onlyvisible']) ? $args['onlyvisible'] : true; | |
e35194be | 932 | $returntypes = isset($args['return_types']) ? $args['return_types'] : 3; |
6f2cd52a | 933 | $type = isset($args['type']) ? $args['type'] : null; |
6f2cd52a | 934 | |
dbc01944 | 935 | $params = array(); |
a090554a PS |
936 | $sql = "SELECT i.*, r.type AS repositorytype, r.sortorder, r.visible |
937 | FROM {repository} r, {repository_instances} i | |
938 | WHERE i.typeid = r.id "; | |
dbc01944 | 939 | |
227dfa43 | 940 | if (!empty($args['disable_types']) && is_array($args['disable_types'])) { |
cf717dc2 | 941 | list($types, $p) = $DB->get_in_or_equal($args['disable_types'], SQL_PARAMS_QM, 'param', false); |
a090554a | 942 | $sql .= " AND r.type $types"; |
227dfa43 DC |
943 | $params = array_merge($params, $p); |
944 | } | |
945 | ||
6f2cd52a | 946 | if (!empty($args['userid']) && is_numeric($args['userid'])) { |
a090554a | 947 | $sql .= " AND (i.userid = 0 or i.userid = ?)"; |
6f2cd52a | 948 | $params[] = $args['userid']; |
dbc01944 | 949 | } |
227dfa43 | 950 | |
dbc01944 | 951 | foreach ($contexts as $context) { |
952 | if (empty($firstcontext)) { | |
953 | $firstcontext = true; | |
a090554a | 954 | $sql .= " AND ((i.contextid = ?)"; |
dbc01944 | 955 | } else { |
a090554a | 956 | $sql .= " OR (i.contextid = ?)"; |
dbc01944 | 957 | } |
958 | $params[] = $context->id; | |
959 | } | |
960 | ||
961 | if (!empty($firstcontext)) { | |
962 | $sql .=')'; | |
963 | } | |
964 | ||
965 | if ($onlyvisible == true) { | |
a090554a | 966 | $sql .= " AND (r.visible = 1)"; |
dbc01944 | 967 | } |
968 | ||
969 | if (isset($type)) { | |
a090554a | 970 | $sql .= " AND (r.type = ?)"; |
dbc01944 | 971 | $params[] = $type; |
972 | } | |
a090554a | 973 | $sql .= " ORDER BY r.sortorder, i.name"; |
50a5cf09 | 974 | |
6f2cd52a DC |
975 | if (!$records = $DB->get_records_sql($sql, $params)) { |
976 | $records = array(); | |
dbc01944 | 977 | } |
978 | ||
6f2cd52a | 979 | $repositories = array(); |
1e0b0415 DC |
980 | if (isset($args['accepted_types'])) { |
981 | $accepted_types = $args['accepted_types']; | |
bb9ea890 FM |
982 | if (is_array($accepted_types) && in_array('*', $accepted_types)) { |
983 | $accepted_types = '*'; | |
984 | } | |
1e0b0415 DC |
985 | } else { |
986 | $accepted_types = '*'; | |
987 | } | |
fdb0322d DS |
988 | // Sortorder should be unique, which is not true if we use $record->sortorder |
989 | // and there are multiple instances of any repository type | |
990 | $sortorder = 1; | |
6f2cd52a | 991 | foreach ($records as $record) { |
e35194be | 992 | if (!file_exists($CFG->dirroot . '/repository/'. $record->repositorytype.'/lib.php')) { |
062d21cd DC |
993 | continue; |
994 | } | |
e35194be | 995 | require_once($CFG->dirroot . '/repository/'. $record->repositorytype.'/lib.php'); |
6f2cd52a | 996 | $options['visible'] = $record->visible; |
6f2cd52a DC |
997 | $options['type'] = $record->repositorytype; |
998 | $options['typeid'] = $record->typeid; | |
c167aa26 | 999 | // tell instance what file types will be accepted by file picker |
6f2cd52a DC |
1000 | $classname = 'repository_' . $record->repositorytype; |
1001 | ||
cfb71351 MG |
1002 | $repository = new $classname($record->id, $current_context, $options, $record->readonly); |
1003 | $repository->options['sortorder'] = $sortorder++; | |
6f2cd52a | 1004 | |
014c1ca0 | 1005 | $is_supported = true; |
dbc01944 | 1006 | |
271bd2e0 | 1007 | if (empty($repository->super_called)) { |
6f2cd52a DC |
1008 | // to make sure the super construct is called |
1009 | debugging('parent::__construct must be called by '.$record->repositorytype.' plugin.'); | |
271bd2e0 | 1010 | } else { |
6f2cd52a | 1011 | // check mimetypes |
99eaca9d | 1012 | if ($accepted_types !== '*' and $repository->supported_filetypes() !== '*') { |
559276b1 MG |
1013 | $accepted_ext = file_get_typegroup('extension', $accepted_types); |
1014 | $supported_ext = file_get_typegroup('extension', $repository->supported_filetypes()); | |
1015 | $valid_ext = array_intersect($accepted_ext, $supported_ext); | |
1016 | $is_supported = !empty($valid_ext); | |
014c1ca0 | 1017 | } |
6f2cd52a | 1018 | // check return values |
41076c58 DC |
1019 | if ($returntypes !== 3 and $repository->supported_returntypes() !== 3) { |
1020 | $type = $repository->supported_returntypes(); | |
1021 | if ($type & $returntypes) { | |
1022 | // | |
1023 | } else { | |
1024 | $is_supported = false; | |
271bd2e0 | 1025 | } |
014c1ca0 | 1026 | } |
d3959c26 | 1027 | |
271bd2e0 | 1028 | if (!$onlyvisible || ($repository->is_visible() && !$repository->disabled)) { |
6f2cd52a | 1029 | // check capability in current context |
cfb71351 | 1030 | $capability = has_capability('repository/'.$record->repositorytype.':view', $current_context); |
d3959c26 DC |
1031 | if ($record->repositorytype == 'coursefiles') { |
1032 | // coursefiles plugin needs managefiles permission | |
cfb71351 | 1033 | $capability = $capability && has_capability('moodle/course:managefiles', $current_context); |
d3959c26 | 1034 | } |
c86dde2e | 1035 | if ($is_supported && $capability) { |
99eaca9d | 1036 | $repositories[$repository->id] = $repository; |
271bd2e0 | 1037 | } |
014c1ca0 | 1038 | } |
dbc01944 | 1039 | } |
1040 | } | |
6f2cd52a | 1041 | return $repositories; |
dbc01944 | 1042 | } |
1043 | ||
1044 | /** | |
cfb71351 MG |
1045 | * Get single repository instance for administrative actions |
1046 | * | |
1047 | * Do not use this function to access repository contents, because it | |
1048 | * does not set the current context | |
1049 | * | |
1050 | * @see rpository::get_repository_by_id() | |
67233725 DC |
1051 | * |
1052 | * @static | |
cfb71351 MG |
1053 | * @param integer $id repository instance id |
1054 | * @return repository | |
dbc01944 | 1055 | */ |
1056 | public static function get_instance($id) { | |
cfb71351 | 1057 | return self::get_repository_by_id($id, context_system::instance()); |
dbc01944 | 1058 | } |
1059 | ||
1060 | /** | |
e35194be | 1061 | * Call a static function. Any additional arguments than plugin and function will be passed through. |
67233725 DC |
1062 | * |
1063 | * @static | |
1064 | * @param string $plugin repository plugin name | |
1065 | * @param string $function funciton name | |
2b8beee3 | 1066 | * @return mixed |
dbc01944 | 1067 | */ |
1068 | public static function static_function($plugin, $function) { | |
1069 | global $CFG; | |
1070 | ||
1071 | //check that the plugin exists | |
e35194be | 1072 | $typedirectory = $CFG->dirroot . '/repository/'. $plugin . '/lib.php'; |
dbc01944 | 1073 | if (!file_exists($typedirectory)) { |
f1cfe56e | 1074 | //throw new repository_exception('invalidplugin', 'repository'); |
1075 | return false; | |
dbc01944 | 1076 | } |
1077 | ||
1078 | $pname = null; | |
1079 | if (is_object($plugin) || is_array($plugin)) { | |
1080 | $plugin = (object)$plugin; | |
1081 | $pname = $plugin->name; | |
1082 | } else { | |
1083 | $pname = $plugin; | |
1084 | } | |
1085 | ||
1086 | $args = func_get_args(); | |
1087 | if (count($args) <= 2) { | |
1088 | $args = array(); | |
e35194be | 1089 | } else { |
dbc01944 | 1090 | array_shift($args); |
1091 | array_shift($args); | |
1092 | } | |
1093 | ||
1094 | require_once($typedirectory); | |
81bdfdb9 | 1095 | return call_user_func_array(array('repository_' . $plugin, $function), $args); |
dbc01944 | 1096 | } |
1097 | ||
23bfe0a4 PS |
1098 | /** |
1099 | * Scan file, throws exception in case of infected file. | |
1100 | * | |
1101 | * Please note that the scanning engine must be able to access the file, | |
1102 | * permissions of the file are not modified here! | |
1103 | * | |
1104 | * @static | |
1105 | * @param string $thefile | |
1106 | * @param string $filename name of the file | |
1107 | * @param bool $deleteinfected | |
23bfe0a4 PS |
1108 | */ |
1109 | public static function antivir_scan_file($thefile, $filename, $deleteinfected) { | |
1110 | global $CFG; | |
1111 | ||
1112 | if (!is_readable($thefile)) { | |
1113 | // this should not happen | |
1114 | return; | |
1115 | } | |
1116 | ||
1117 | if (empty($CFG->runclamonupload) or empty($CFG->pathtoclam)) { | |
1118 | // clam not enabled | |
1119 | return; | |
1120 | } | |
1121 | ||
1122 | $CFG->pathtoclam = trim($CFG->pathtoclam); | |
1123 | ||
1124 | if (!file_exists($CFG->pathtoclam) or !is_executable($CFG->pathtoclam)) { | |
1125 | // misconfigured clam - use the old notification for now | |
1126 | require("$CFG->libdir/uploadlib.php"); | |
1127 | $notice = get_string('clamlost', 'moodle', $CFG->pathtoclam); | |
1128 | clam_message_admins($notice); | |
1129 | return; | |
1130 | } | |
1131 | ||
1132 | // do NOT mess with permissions here, the calling party is responsible for making | |
1133 | // sure the scanner engine can access the files! | |
1134 | ||
1135 | // execute test | |
1136 | $cmd = escapeshellcmd($CFG->pathtoclam).' --stdout '.escapeshellarg($thefile); | |
1137 | exec($cmd, $output, $return); | |
1138 | ||
1139 | if ($return == 0) { | |
1140 | // perfect, no problem found | |
1141 | return; | |
1142 | ||
1143 | } else if ($return == 1) { | |
1144 | // infection found | |
1145 | if ($deleteinfected) { | |
1146 | unlink($thefile); | |
1147 | } | |
1148 | throw new moodle_exception('virusfounduser', 'moodle', '', array('filename'=>$filename)); | |
1149 | ||
1150 | } else { | |
1151 | //unknown problem | |
1152 | require("$CFG->libdir/uploadlib.php"); | |
1153 | $notice = get_string('clamfailed', 'moodle', get_clam_error_code($return)); | |
1154 | $notice .= "\n\n". implode("\n", $output); | |
1155 | clam_message_admins($notice); | |
1156 | if ($CFG->clamfailureonupload === 'actlikevirus') { | |
1157 | if ($deleteinfected) { | |
1158 | unlink($thefile); | |
1159 | } | |
1160 | throw new moodle_exception('virusfounduser', 'moodle', '', array('filename'=>$filename)); | |
1161 | } else { | |
1162 | return; | |
1163 | } | |
1164 | } | |
1165 | } | |
1166 | ||
67233725 | 1167 | /** |
0b2bfbd1 | 1168 | * Repository method to serve the referenced file |
67233725 | 1169 | * |
0b2bfbd1 MG |
1170 | * @see send_stored_file |
1171 | * | |
1172 | * @param stored_file $storedfile the file that contains the reference | |
67233725 DC |
1173 | * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) |
1174 | * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only | |
1175 | * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin | |
1176 | * @param array $options additional options affecting the file serving | |
1177 | */ | |
1178 | public function send_file($storedfile, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) { | |
0b2bfbd1 MG |
1179 | if ($this->has_moodle_files()) { |
1180 | $fs = get_file_storage(); | |
1181 | $params = file_storage::unpack_reference($storedfile->get_reference(), true); | |
1182 | $srcfile = null; | |
1183 | if (is_array($params)) { | |
1184 | $srcfile = $fs->get_file($params['contextid'], $params['component'], $params['filearea'], | |
1185 | $params['itemid'], $params['filepath'], $params['filename']); | |
1186 | } | |
1187 | if (empty($options)) { | |
1188 | $options = array(); | |
1189 | } | |
1190 | if (!isset($options['filename'])) { | |
1191 | $options['filename'] = $storedfile->get_filename(); | |
1192 | } | |
1193 | if (!$srcfile) { | |
1194 | send_file_not_found(); | |
1195 | } else { | |
1196 | send_stored_file($srcfile, $lifetime, $filter, $forcedownload, $options); | |
1197 | } | |
1198 | } else { | |
1199 | throw new coding_exception("Repository plugin must implement send_file() method."); | |
1200 | } | |
67233725 DC |
1201 | } |
1202 | ||
1203 | /** | |
1204 | * Return reference file life time | |
1205 | * | |
1206 | * @param string $ref | |
1207 | * @return int | |
1208 | */ | |
1209 | public function get_reference_file_lifetime($ref) { | |
1210 | // One day | |
1211 | return 60 * 60 * 24; | |
1212 | } | |
1213 | ||
1214 | /** | |
1215 | * Decide whether or not the file should be synced | |
1216 | * | |
1217 | * @param stored_file $storedfile | |
1218 | * @return bool | |
1219 | */ | |
1220 | public function sync_individual_file(stored_file $storedfile) { | |
1221 | return true; | |
1222 | } | |
1223 | ||
1224 | /** | |
1225 | * Return human readable reference information | |
67233725 | 1226 | * |
59cb7598 | 1227 | * @param string $reference value of DB field files_reference.reference |
0b2bfbd1 MG |
1228 | * @param int $filestatus status of the file, 0 - ok, 666 - source missing |
1229 | * @return string | |
67233725 | 1230 | */ |
0b2bfbd1 MG |
1231 | public function get_reference_details($reference, $filestatus = 0) { |
1232 | if ($this->has_moodle_files()) { | |
1233 | $fileinfo = null; | |
1234 | $params = file_storage::unpack_reference($reference, true); | |
1235 | if (is_array($params)) { | |
5fbe2118 | 1236 | $context = context::instance_by_id($params['contextid'], IGNORE_MISSING); |
0b2bfbd1 MG |
1237 | if ($context) { |
1238 | $browser = get_file_browser(); | |
1239 | $fileinfo = $browser->get_file_info($context, $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']); | |
1240 | } | |
1241 | } | |
1242 | if (empty($fileinfo)) { | |
1243 | if ($filestatus == 666) { | |
1244 | if (is_siteadmin() || ($context && has_capability('moodle/course:managefiles', $context))) { | |
1245 | return get_string('lostsource', 'repository', | |
1246 | $params['contextid']. '/'. $params['component']. '/'. $params['filearea']. '/'. $params['itemid']. $params['filepath']. $params['filename']); | |
1247 | } else { | |
1248 | return get_string('lostsource', 'repository', ''); | |
1249 | } | |
1250 | } | |
1251 | return get_string('undisclosedsource', 'repository'); | |
1252 | } else { | |
1253 | return $fileinfo->get_readable_fullname(); | |
1254 | } | |
1255 | } | |
1256 | return ''; | |
67233725 DC |
1257 | } |
1258 | ||
1259 | /** | |
1260 | * Cache file from external repository by reference | |
1261 | * {@link repository::get_file_reference()} | |
1262 | * {@link repository::get_file()} | |
1263 | * Invoked at MOODLE/repository/repository_ajax.php | |
1264 | * | |
1265 | * @param string $reference this reference is generated by | |
1266 | * repository::get_file_reference() | |
1267 | * @param stored_file $storedfile created file reference | |
1268 | */ | |
6dd299be | 1269 | public function cache_file_by_reference($reference, $storedfile) { |
67233725 DC |
1270 | } |
1271 | ||
1272 | /** | |
0b2bfbd1 | 1273 | * Returns information about file in this repository by reference |
67233725 | 1274 | * |
2d222a32 MG |
1275 | * This function must be implemented for repositories supporting FILE_REFERENCE, it is called |
1276 | * for existing aliases when the lifetime of the previous syncronisation has expired. | |
0b2bfbd1 | 1277 | * |
2d222a32 MG |
1278 | * Returns null if file not found or is not readable or timeout occured during request. |
1279 | * Note that this function may be run for EACH file that needs to be synchronised at the | |
1280 | * moment. If anything is being downloaded or requested from external sources there | |
1281 | * should be a small timeout. The synchronisation is performed to update the size of | |
1282 | * the file and/or to update image and re-generated image preview. There is nothing | |
1283 | * fatal if syncronisation fails but it is fatal if syncronisation takes too long | |
1284 | * and hangs the script generating a page. | |
0b2bfbd1 | 1285 | * |
2d222a32 MG |
1286 | * If get_file_by_reference() returns filesize just the record in {files} table is being updated. |
1287 | * If filepath, handle or content are returned - the file is also stored in moodle filepool | |
1288 | * (recommended for images to generate the thumbnails). For non-image files it is not | |
1289 | * recommended to download them to moodle during syncronisation since it may take | |
1290 | * unnecessary long time. | |
1291 | * | |
1292 | * @param stdClass $reference record from DB table {files_reference} | |
0b2bfbd1 | 1293 | * @return stdClass|null contains one of the following: |
2d222a32 | 1294 | * - 'filesize' and optionally 'contenthash' |
0b2bfbd1 MG |
1295 | * - 'filepath' |
1296 | * - 'handle' | |
1297 | * - 'content' | |
67233725 DC |
1298 | */ |
1299 | public function get_file_by_reference($reference) { | |
0b2bfbd1 MG |
1300 | if ($this->has_moodle_files() && isset($reference->reference)) { |
1301 | $fs = get_file_storage(); | |
1302 | $params = file_storage::unpack_reference($reference->reference, true); | |
1303 | if (!is_array($params) || !($storedfile = $fs->get_file($params['contextid'], | |
1304 | $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], | |
1305 | $params['filename']))) { | |
1306 | return null; | |
1307 | } | |
1308 | return (object)array( | |
1309 | 'contenthash' => $storedfile->get_contenthash(), | |
1310 | 'filesize' => $storedfile->get_filesize() | |
1311 | ); | |
1312 | } | |
67233725 DC |
1313 | return null; |
1314 | } | |
1315 | ||
d6453211 DC |
1316 | /** |
1317 | * Return the source information | |
1318 | * | |
59cb7598 MG |
1319 | * The result of the function is stored in files.source field. It may be analysed |
1320 | * when the source file is lost or repository may use it to display human-readable | |
1321 | * location of reference original. | |
1322 | * | |
1323 | * This method is called when file is picked for the first time only. When file | |
1324 | * (either copy or a reference) is already in moodle and it is being picked | |
1325 | * again to another file area (also as a copy or as a reference), the value of | |
1326 | * files.source is copied. | |
1327 | * | |
1328 | * @param string $source the value that repository returned in listing as 'source' | |
d6453211 DC |
1329 | * @return string|null |
1330 | */ | |
59cb7598 | 1331 | public function get_file_source_info($source) { |
83d2700e | 1332 | if ($this->has_moodle_files()) { |
59cb7598 | 1333 | return $this->get_reference_details($source, 0); |
83d2700e | 1334 | } |
59cb7598 | 1335 | return $source; |
d6453211 DC |
1336 | } |
1337 | ||
dbc01944 | 1338 | /** |
1339 | * Move file from download folder to file pool using FILE API | |
67233725 DC |
1340 | * |
1341 | * @todo MDL-28637 | |
1342 | * @static | |
8e0946bf | 1343 | * @param string $thefile file path in download folder |
67233725 | 1344 | * @param stdClass $record |
1a6195b4 DC |
1345 | * @return array containing the following keys: |
1346 | * icon | |
1347 | * file | |
1348 | * id | |
1349 | * url | |
dbc01944 | 1350 | */ |
14469892 | 1351 | public static function move_to_filepool($thefile, $record) { |
390baf46 | 1352 | global $DB, $CFG, $USER, $OUTPUT; |
23bfe0a4 PS |
1353 | |
1354 | // scan for viruses if possible, throws exception if problem found | |
1355 | self::antivir_scan_file($thefile, $record->filename, empty($CFG->repository_no_delete)); //TODO: MDL-28637 this repository_no_delete is a bloody hack! | |
1356 | ||
dbc01944 | 1357 | $fs = get_file_storage(); |
67233725 DC |
1358 | // If file name being used. |
1359 | if (repository::draftfile_exists($record->itemid, $record->filepath, $record->filename)) { | |
f392caba DC |
1360 | $draftitemid = $record->itemid; |
1361 | $new_filename = repository::get_unused_filename($draftitemid, $record->filepath, $record->filename); | |
1362 | $old_filename = $record->filename; | |
67233725 | 1363 | // Create a tmp file. |
f392caba DC |
1364 | $record->filename = $new_filename; |
1365 | $newfile = $fs->create_file_from_pathname($record, $thefile); | |
1366 | $event = array(); | |
1367 | $event['event'] = 'fileexists'; | |
1368 | $event['newfile'] = new stdClass; | |
1369 | $event['newfile']->filepath = $record->filepath; | |
1370 | $event['newfile']->filename = $new_filename; | |
1371 | $event['newfile']->url = moodle_url::make_draftfile_url($draftitemid, $record->filepath, $new_filename)->out(); | |
1372 | ||
1373 | $event['existingfile'] = new stdClass; | |
1374 | $event['existingfile']->filepath = $record->filepath; | |
1375 | $event['existingfile']->filename = $old_filename; | |
0e35ba6f | 1376 | $event['existingfile']->url = moodle_url::make_draftfile_url($draftitemid, $record->filepath, $old_filename)->out(); |
f392caba | 1377 | return $event; |
b423b4af | 1378 | } |
14469892 | 1379 | if ($file = $fs->create_file_from_pathname($record, $thefile)) { |
88b126a5 | 1380 | if (empty($CFG->repository_no_delete)) { |
99eaca9d | 1381 | $delete = unlink($thefile); |
88b126a5 | 1382 | unset($CFG->repository_no_delete); |
1383 | } | |
64f93798 | 1384 | return array( |
50a8bd6c | 1385 | 'url'=>moodle_url::make_draftfile_url($file->get_itemid(), $file->get_filepath(), $file->get_filename())->out(), |
64f93798 PS |
1386 | 'id'=>$file->get_itemid(), |
1387 | 'file'=>$file->get_filename(), | |
1388 | 'icon' => $OUTPUT->pix_url(file_extension_icon($thefile, 32))->out(), | |
1389 | ); | |
dbc01944 | 1390 | } else { |
1391 | return null; | |
1392 | } | |
1393 | } | |
1394 | ||
dbc01944 | 1395 | /** |
67233725 | 1396 | * Builds a tree of files This function is then called recursively. |
dbc01944 | 1397 | * |
67233725 DC |
1398 | * @static |
1399 | * @todo take $search into account, and respect a threshold for dynamic loading | |
1400 | * @param file_info $fileinfo an object returned by file_browser::get_file_info() | |
1401 | * @param string $search searched string | |
1402 | * @param bool $dynamicmode no recursive call is done when in dynamic mode | |
1403 | * @param array $list the array containing the files under the passed $fileinfo | |
72a56555 | 1404 | * @return int the number of files found |
dbc01944 | 1405 | */ |
1406 | public static function build_tree($fileinfo, $search, $dynamicmode, &$list) { | |
390baf46 | 1407 | global $CFG, $OUTPUT; |
dbc01944 | 1408 | |
1409 | $filecount = 0; | |
1410 | $children = $fileinfo->get_children(); | |
1411 | ||
1412 | foreach ($children as $child) { | |
1413 | $filename = $child->get_visible_name(); | |
1414 | $filesize = $child->get_filesize(); | |
1415 | $filesize = $filesize ? display_size($filesize) : ''; | |
1416 | $filedate = $child->get_timemodified(); | |
1417 | $filedate = $filedate ? userdate($filedate) : ''; | |
1418 | $filetype = $child->get_mimetype(); | |
1419 | ||
1420 | if ($child->is_directory()) { | |
1421 | $path = array(); | |
1422 | $level = $child->get_parent(); | |
1423 | while ($level) { | |
1424 | $params = $level->get_params(); | |
64f93798 | 1425 | $path[] = array($params['filepath'], $level->get_visible_name()); |
dbc01944 | 1426 | $level = $level->get_parent(); |
1427 | } | |
1428 | ||
1429 | $tmp = array( | |
1430 | 'title' => $child->get_visible_name(), | |
1431 | 'size' => 0, | |
1432 | 'date' => $filedate, | |
1433 | 'path' => array_reverse($path), | |
559276b1 | 1434 | 'thumbnail' => $OUTPUT->pix_url(file_folder_icon(90))->out(false) |
dbc01944 | 1435 | ); |
1436 | ||
1437 | //if ($dynamicmode && $child->is_writable()) { | |
1438 | // $tmp['children'] = array(); | |
1439 | //} else { | |
1440 | // if folder name matches search, we send back all files contained. | |
1441 | $_search = $search; | |
1442 | if ($search && stristr($tmp['title'], $search) !== false) { | |
1443 | $_search = false; | |
1444 | } | |
1445 | $tmp['children'] = array(); | |
1446 | $_filecount = repository::build_tree($child, $_search, $dynamicmode, $tmp['children']); | |
1447 | if ($search && $_filecount) { | |
1448 | $tmp['expanded'] = 1; | |
1449 | } | |
1450 | ||
1451 | //} | |
1452 | ||
fc3ec2ca | 1453 | if (!$search || $_filecount || (stristr($tmp['title'], $search) !== false)) { |
dbc01944 | 1454 | $filecount += $_filecount; |
1455 | $list[] = $tmp; | |
1456 | } | |
1457 | ||
1458 | } else { // not a directory | |
1459 | // skip the file, if we're in search mode and it's not a match | |
1460 | if ($search && (stristr($filename, $search) === false)) { | |
1461 | continue; | |
1462 | } | |
1463 | $params = $child->get_params(); | |
64f93798 | 1464 | $source = serialize(array($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename'])); |
dbc01944 | 1465 | $list[] = array( |
1466 | 'title' => $filename, | |
1467 | 'size' => $filesize, | |
1468 | 'date' => $filedate, | |
1469 | //'source' => $child->get_url(), | |
1470 | 'source' => base64_encode($source), | |
559276b1 MG |
1471 | 'icon'=>$OUTPUT->pix_url(file_file_icon($child, 24))->out(false), |
1472 | 'thumbnail'=>$OUTPUT->pix_url(file_file_icon($child, 90))->out(false), | |
dbc01944 | 1473 | ); |
1474 | $filecount++; | |
1475 | } | |
1476 | } | |
1477 | ||
1478 | return $filecount; | |
1479 | } | |
1480 | ||
dbc01944 | 1481 | /** |
1482 | * Display a repository instance list (with edit/delete/create links) | |
67233725 DC |
1483 | * |
1484 | * @static | |
1485 | * @param stdClass $context the context for which we display the instance | |
dbc01944 | 1486 | * @param string $typename if set, we display only one type of instance |
1487 | */ | |
1488 | public static function display_instances_list($context, $typename = null) { | |
50a5cf09 | 1489 | global $CFG, $USER, $OUTPUT; |
dbc01944 | 1490 | |
50a5cf09 | 1491 | $output = $OUTPUT->box_start('generalbox'); |
dbc01944 | 1492 | //if the context is SYSTEM, so we call it from administration page |
1493 | $admin = ($context->id == SYSCONTEXTID) ? true : false; | |
1494 | if ($admin) { | |
e35194be DC |
1495 | $baseurl = new moodle_url('/'.$CFG->admin.'/repositoryinstance.php', array('sesskey'=>sesskey())); |
1496 | $output .= $OUTPUT->heading(get_string('siteinstances', 'repository')); | |
dbc01944 | 1497 | } else { |
e35194be | 1498 | $baseurl = new moodle_url('/repository/manage_instances.php', array('contextid'=>$context->id, 'sesskey'=>sesskey())); |
dbc01944 | 1499 | } |
1500 | ||
1501 | $namestr = get_string('name'); | |
1502 | $pluginstr = get_string('plugin', 'repository'); | |
1503 | $settingsstr = get_string('settings'); | |
1504 | $deletestr = get_string('delete'); | |
89a9d21a FM |
1505 | // Retrieve list of instances. In administration context we want to display all |
1506 | // instances of a type, even if this type is not visible. In course/user context we | |
1507 | // want to display only visible instances, but for every type types. The repository::get_instances() | |
1508 | // third parameter displays only visible type. | |
6f2cd52a | 1509 | $params = array(); |
89a9d21a | 1510 | $params['context'] = array($context); |
6f2cd52a DC |
1511 | $params['currentcontext'] = $context; |
1512 | $params['onlyvisible'] = !$admin; | |
1513 | $params['type'] = $typename; | |
1514 | $instances = repository::get_instances($params); | |
dbc01944 | 1515 | $instancesnumber = count($instances); |
1516 | $alreadyplugins = array(); | |
1517 | ||
55400db0 | 1518 | $table = new html_table(); |
79698344 | 1519 | $table->head = array($namestr, $pluginstr, $settingsstr, $deletestr); |
dbc01944 | 1520 | $table->align = array('left', 'left', 'center','center'); |
1521 | $table->data = array(); | |
1522 | ||
1523 | $updowncount = 1; | |
1524 | ||
1525 | foreach ($instances as $i) { | |
1526 | $settings = ''; | |
1527 | $delete = ''; | |
5363905a DC |
1528 | |
1529 | $type = repository::get_type_by_id($i->options['typeid']); | |
1530 | ||
1531 | if ($type->get_contextvisibility($context)) { | |
1532 | if (!$i->readonly) { | |
1533 | ||
67233725 DC |
1534 | $settingurl = new moodle_url($baseurl); |
1535 | $settingurl->param('type', $i->options['type']); | |
1536 | $settingurl->param('edit', $i->id); | |
1537 | $settings .= html_writer::link($settingurl, $settingsstr); | |
5363905a | 1538 | |
67233725 DC |
1539 | $deleteurl = new moodle_url($baseurl); |
1540 | $deleteurl->param('delete', $i->id); | |
1541 | $deleteurl->param('type', $i->options['type']); | |
1542 | $delete .= html_writer::link($deleteurl, $deletestr); | |
5363905a | 1543 | } |
dbc01944 | 1544 | } |
1545 | ||
a3d8df25 | 1546 | $type = repository::get_type_by_id($i->options['typeid']); |
8655fb96 | 1547 | $table->data[] = array(format_string($i->name), $type->get_readablename(), $settings, $delete); |
dbc01944 | 1548 | |
1549 | //display a grey row if the type is defined as not visible | |
1550 | if (isset($type) && !$type->get_visible()) { | |
704ac344 | 1551 | $table->rowclasses[] = 'dimmed_text'; |
dbc01944 | 1552 | } else { |
704ac344 | 1553 | $table->rowclasses[] = ''; |
dbc01944 | 1554 | } |
1555 | ||
1556 | if (!in_array($i->name, $alreadyplugins)) { | |
1557 | $alreadyplugins[] = $i->name; | |
1558 | } | |
1559 | } | |
16be8974 | 1560 | $output .= html_writer::table($table); |
dbc01944 | 1561 | $instancehtml = '<div>'; |
1562 | $addable = 0; | |
1563 | ||
1564 | //if no type is set, we can create all type of instance | |
1565 | if (!$typename) { | |
1566 | $instancehtml .= '<h3>'; | |
1567 | $instancehtml .= get_string('createrepository', 'repository'); | |
1568 | $instancehtml .= '</h3><ul>'; | |
1569 | $types = repository::get_editable_types($context); | |
1570 | foreach ($types as $type) { | |
1571 | if (!empty($type) && $type->get_visible()) { | |
2c869550 FM |
1572 | // If the user does not have the permission to view the repository, it won't be displayed in |
1573 | // the list of instances. Hiding the link to create new instances will prevent the | |
1574 | // user from creating them without being able to find them afterwards, which looks like a bug. | |
1575 | if (!has_capability('repository/'.$type->get_typename().':view', $context)) { | |
1576 | continue; | |
1577 | } | |
dbc01944 | 1578 | $instanceoptionnames = repository::static_function($type->get_typename(), 'get_instance_option_names'); |
1579 | if (!empty($instanceoptionnames)) { | |
e35194be DC |
1580 | $baseurl->param('new', $type->get_typename()); |
1581 | $instancehtml .= '<li><a href="'.$baseurl->out().'">'.get_string('createxxinstance', 'repository', get_string('pluginname', 'repository_'.$type->get_typename())). '</a></li>'; | |
1582 | $baseurl->remove_params('new'); | |
dbc01944 | 1583 | $addable++; |
1584 | } | |
1585 | } | |
1586 | } | |
1587 | $instancehtml .= '</ul>'; | |
1588 | ||
1589 | } else { | |
1590 | $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names'); | |
1591 | if (!empty($instanceoptionnames)) { //create a unique type of instance | |
1592 | $addable = 1; | |
e35194be | 1593 | $baseurl->param('new', $typename); |
67233725 | 1594 | $output .= $OUTPUT->single_button($baseurl, get_string('createinstance', 'repository'), 'get'); |
e35194be | 1595 | $baseurl->remove_params('new'); |
dbc01944 | 1596 | } |
1597 | } | |
1598 | ||
1599 | if ($addable) { | |
1600 | $instancehtml .= '</div>'; | |
1601 | $output .= $instancehtml; | |
1602 | } | |
1603 | ||
50a5cf09 | 1604 | $output .= $OUTPUT->box_end(); |
dbc01944 | 1605 | |
1606 | //print the list + creation links | |
1607 | print($output); | |
1608 | } | |
1cf56396 | 1609 | |
67233725 DC |
1610 | /** |
1611 | * Prepare file reference information | |
1612 | * | |
1613 | * @param string $source | |
1614 | * @return string file referece | |
1615 | */ | |
1616 | public function get_file_reference($source) { | |
0b2bfbd1 MG |
1617 | if ($this->has_moodle_files() && ($this->supported_returntypes() & FILE_REFERENCE)) { |
1618 | $params = file_storage::unpack_reference($source); | |
1619 | if (!is_array($params)) { | |
1620 | throw new repository_exception('invalidparams', 'repository'); | |
1621 | } | |
1622 | return file_storage::pack_reference($params); | |
1623 | } | |
67233725 DC |
1624 | return $source; |
1625 | } | |
72a56555 | 1626 | |
c425472d | 1627 | /** |
e35194be | 1628 | * Decide where to save the file, can be overwriten by subclass |
67233725 DC |
1629 | * |
1630 | * @param string $filename file name | |
1631 | * @return file path | |
c425472d | 1632 | */ |
a560785f | 1633 | public function prepare_file($filename) { |
c425472d | 1634 | global $CFG; |
72a56555 MG |
1635 | $dir = make_temp_directory('download/'.get_class($this).'/'); |
1636 | while (empty($filename) || file_exists($dir.$filename)) { | |
1637 | $filename = uniqid('', true).'_'.time().'.tmp'; | |
c425472d | 1638 | } |
a560785f | 1639 | return $dir.$filename; |
a53d4f45 | 1640 | } |
1641 | ||
67233725 DC |
1642 | /** |
1643 | * Does this repository used to browse moodle files? | |
1644 | * | |
1645 | * @return bool | |
1646 | */ | |
1647 | public function has_moodle_files() { | |
1648 | return false; | |
1649 | } | |
1650 | ||
99d52655 DC |
1651 | /** |
1652 | * Return file URL, for most plugins, the parameter is the original | |
1653 | * url, but some plugins use a file id, so we need this function to | |
1654 | * convert file id to original url. | |
1655 | * | |
1656 | * @param string $url the url of file | |
8e0946bf | 1657 | * @return string |
99d52655 DC |
1658 | */ |
1659 | public function get_link($url) { | |
1660 | return $url; | |
1661 | } | |
1662 | ||
a53d4f45 | 1663 | /** |
72a56555 | 1664 | * Downloads a file from external repository and saves it in temp dir |
a53d4f45 | 1665 | * |
72a56555 MG |
1666 | * Function get_file() must be implemented by repositories that support returntypes |
1667 | * FILE_INTERNAL or FILE_REFERENCE. It is invoked to pick up the file and copy it | |
1668 | * to moodle. This function is not called for moodle repositories, the function | |
1669 | * {@link repository::copy_to_area()} is used instead. | |
1670 | * | |
1671 | * This function can be overridden by subclass if the files.reference field contains | |
1672 | * not just URL or if request should be done differently. | |
1673 | * | |
1674 | * @see curl | |
1675 | * @throws file_exception when error occured | |
1676 | * | |
1677 | * @param string $url the content of files.reference field, in this implementaion | |
1678 | * it is asssumed that it contains the string with URL of the file | |
1679 | * @param string $filename filename (without path) to save the downloaded file in the | |
1680 | * temporary directory, if omitted or file already exists the new filename will be generated | |
0b2bfbd1 MG |
1681 | * @return array with elements: |
1682 | * path: internal location of the file | |
1683 | * url: URL to the source (from parameters) | |
a53d4f45 | 1684 | */ |
a560785f | 1685 | public function get_file($url, $filename = '') { |
41076c58 | 1686 | $path = $this->prepare_file($filename); |
41076c58 | 1687 | $c = new curl; |
72a56555 MG |
1688 | $result = $c->download_one($url, null, array('filepath' => $path, 'timeout' => self::GETFILE_TIMEOUT)); |
1689 | if ($result !== true) { | |
1690 | throw new moodle_exception('errorwhiledownload', 'repository', '', $result); | |
0b2bfbd1 | 1691 | } |
1dce6261 | 1692 | return array('path'=>$path, 'url'=>$url); |
82669dc4 | 1693 | } |
1cf56396 | 1694 | |
bc6f241c MG |
1695 | /** |
1696 | * Downloads the file from external repository and saves it in moodle filepool. | |
1697 | * This function is different from {@link repository::sync_external_file()} because it has | |
1698 | * bigger request timeout and always downloads the content. | |
1699 | * | |
1700 | * This function is invoked when we try to unlink the file from the source and convert | |
1701 | * a reference into a true copy. | |
1702 | * | |
1703 | * @throws exception when file could not be imported | |
1704 | * | |
1705 | * @param stored_file $file | |
1706 | * @param int $maxbytes throw an exception if file size is bigger than $maxbytes (0 means no limit) | |
1707 | */ | |
1708 | public function import_external_file_contents(stored_file $file, $maxbytes = 0) { | |
1709 | if (!$file->is_external_file()) { | |
1710 | // nothing to import if the file is not a reference | |
1711 | return; | |
1712 | } else if ($file->get_repository_id() != $this->id) { | |
1713 | // error | |
1714 | debugging('Repository instance id does not match'); | |
1715 | return; | |
1716 | } else if ($this->has_moodle_files()) { | |
1717 | // files that are references to local files are already in moodle filepool | |
1718 | // just validate the size | |
1719 | if ($maxbytes > 0 && $file->get_filesize() > $maxbytes) { | |
1720 | throw new file_exception('maxbytes'); | |
1721 | } | |
1722 | return; | |
1723 | } else { | |
1724 | if ($maxbytes > 0 && $file->get_filesize() > $maxbytes) { | |
1725 | // note that stored_file::get_filesize() also calls synchronisation | |
1726 | throw new file_exception('maxbytes'); | |
1727 | } | |
1728 | $fs = get_file_storage(); | |
1729 | $contentexists = $fs->content_exists($file->get_contenthash()); | |
1730 | if ($contentexists && $file->get_filesize() && $file->get_contenthash() === sha1('')) { | |
1731 | // even when 'file_storage::content_exists()' returns true this may be an empty | |
1732 | // content for the file that was not actually downloaded | |
1733 | $contentexists = false; | |
1734 | } | |
1735 | $now = time(); | |
1736 | if ($file->get_referencelastsync() + $file->get_referencelifetime() >= $now && | |
1737 | !$file->get_status() && | |
1738 | $contentexists) { | |
1739 | // we already have the content in moodle filepool and it was synchronised recently. | |
1740 | // Repositories may overwrite it if they want to force synchronisation anyway! | |
1741 | return; | |
1742 | } else { | |
1743 | // attempt to get a file | |
1744 | try { | |
1745 | $fileinfo = $this->get_file($file->get_reference()); | |
1746 | if (isset($fileinfo['path'])) { | |
1747 | list($contenthash, $filesize, $newfile) = $fs->add_file_to_pool($fileinfo['path']); | |
1748 | // set this file and other similar aliases synchronised | |
1749 | $lifetime = $this->get_reference_file_lifetime($file->get_reference()); | |
1750 | $file->set_synchronized($contenthash, $filesize, 0, $lifetime); | |
1751 | } else { | |
1752 | throw new moodle_exception('errorwhiledownload', 'repository', '', ''); | |
1753 | } | |
1754 | } catch (Exception $e) { | |
1755 | if ($contentexists) { | |
1756 | // better something than nothing. We have a copy of file. It's sync time | |
1757 | // has expired but it is still very likely that it is the last version | |
1758 | } else { | |
1759 | throw($e); | |
1760 | } | |
1761 | } | |
1762 | } | |
1763 | } | |
1764 | } | |
1765 | ||
c0d60b3a AKA |
1766 | /** |
1767 | * Return size of a file in bytes. | |
1768 | * | |
1769 | * @param string $source encoded and serialized data of file | |
67233725 | 1770 | * @return int file size in bytes |
c0d60b3a AKA |
1771 | */ |
1772 | public function get_file_size($source) { | |
2dfa16ef | 1773 | // TODO MDL-33297 remove this function completely? |
c0d60b3a AKA |
1774 | $browser = get_file_browser(); |
1775 | $params = unserialize(base64_decode($source)); | |
1776 | $contextid = clean_param($params['contextid'], PARAM_INT); | |
1777 | $fileitemid = clean_param($params['itemid'], PARAM_INT); | |
1778 | $filename = clean_param($params['filename'], PARAM_FILE); | |
1779 | $filepath = clean_param($params['filepath'], PARAM_PATH); | |
aff24313 PS |
1780 | $filearea = clean_param($params['filearea'], PARAM_AREA); |
1781 | $component = clean_param($params['component'], PARAM_COMPONENT); | |
d197ea43 | 1782 | $context = context::instance_by_id($contextid); |
c0d60b3a AKA |
1783 | $file_info = $browser->get_file_info($context, $component, $filearea, $fileitemid, $filepath, $filename); |
1784 | if (!empty($file_info)) { | |
1785 | $filesize = $file_info->get_filesize(); | |
1786 | } else { | |
1787 | $filesize = null; | |
1788 | } | |
1789 | return $filesize; | |
1790 | } | |
1791 | ||
f48fb4d6 | 1792 | /** |
1793 | * Return is the instance is visible | |
1794 | * (is the type visible ? is the context enable ?) | |
67233725 DC |
1795 | * |
1796 | * @return bool | |
f48fb4d6 | 1797 | */ |
dbc01944 | 1798 | public function is_visible() { |
7d554b64 | 1799 | $type = repository::get_type_by_id($this->options['typeid']); |
dbc01944 | 1800 | $instanceoptions = repository::static_function($type->get_typename(), 'get_instance_option_names'); |
f48fb4d6 | 1801 | |
1802 | if ($type->get_visible()) { | |
1803 | //if the instance is unique so it's visible, otherwise check if the instance has a enabled context | |
5363905a | 1804 | if (empty($instanceoptions) || $type->get_contextvisibility($this->context)) { |
f48fb4d6 | 1805 | return true; |
1806 | } | |
1807 | } | |
1808 | ||
1809 | return false; | |
1810 | } | |
1811 | ||
89a9d21a FM |
1812 | /** |
1813 | * Can the instance be edited by the current user? | |
1814 | * | |
1815 | * The property $readonly must not be used within this method because | |
1816 | * it only controls if the options from self::get_instance_option_names() | |
1817 | * can be edited. | |
1818 | * | |
1819 | * @return bool true if the user can edit the instance. | |
1820 | * @since 2.5 | |
1821 | */ | |
1822 | public final function can_be_edited_by_user() { | |
1823 | global $USER; | |
1824 | ||
1825 | // We need to be able to explore the repository. | |
1826 | try { | |
1827 | $this->check_capability(); | |
1828 | } catch (repository_exception $e) { | |
1829 | return false; | |
1830 | } | |
1831 | ||
1832 | $repocontext = context::instance_by_id($this->instance->contextid); | |
1833 | if ($repocontext->contextlevel == CONTEXT_USER && $repocontext->instanceid != $USER->id) { | |
1834 | // If the context of this instance is a user context, we need to be this user. | |
1835 | return false; | |
1836 | } else if ($repocontext->contextlevel == CONTEXT_MODULE && !has_capability('moodle/course:update', $repocontext)) { | |
1837 | // We need to have permissions on the course to edit the instance. | |
1838 | return false; | |
1839 | } else if ($repocontext->contextlevel == CONTEXT_SYSTEM && !has_capability('moodle/site:config', $repocontext)) { | |
1840 | // Do not meet the requirements for the context system. | |
1841 | return false; | |
1842 | } | |
1843 | ||
1844 | return true; | |
1845 | } | |
1846 | ||
eb239694 | 1847 | /** |
66dc47bc | 1848 | * Return the name of this instance, can be overridden. |
67233725 | 1849 | * |
2b8beee3 | 1850 | * @return string |
eb239694 | 1851 | */ |
7a3b93c1 | 1852 | public function get_name() { |
d31af46a | 1853 | global $DB; |
31581ae6 | 1854 | if ($name = $this->instance->name) { |
a5adfa26 | 1855 | return $name; |
d31af46a | 1856 | } else { |
31581ae6 | 1857 | return get_string('pluginname', 'repository_' . $this->get_typename()); |
d31af46a | 1858 | } |
1859 | } | |
aa754fe3 | 1860 | |
31581ae6 FM |
1861 | /** |
1862 | * Is this repository accessing private data? | |
1863 | * | |
1864 | * This function should return true for the repositories which access external private | |
1865 | * data from a user. This is the case for repositories such as Dropbox, Google Docs or Box.net | |
1866 | * which authenticate the user and then store the auth token. | |
1867 | * | |
1868 | * Of course, many repositories store 'private data', but we only want to set | |
1869 | * contains_private_data() to repositories which are external to Moodle and shouldn't be accessed | |
1870 | * to by the users having the capability to 'login as' someone else. For instance, the repository | |
1871 | * 'Private files' is not considered as private because it's part of Moodle. | |
1872 | * | |
1873 | * You should not set contains_private_data() to true on repositories which allow different types | |
1874 | * of instances as the levels other than 'user' are, by definition, not private. Also | |
1875 | * the user instances will be protected when they need to. | |
1876 | * | |
1877 | * @return boolean True when the repository accesses private external data. | |
1878 | * @since 2.5 | |
1879 | */ | |
1880 | public function contains_private_data() { | |
1881 | return true; | |
1882 | } | |
1883 | ||
b318bb6d | 1884 | /** |
67233725 DC |
1885 | * What kind of files will be in this repository? |
1886 | * | |
b318bb6d | 1887 | * @return array return '*' means this repository support any files, otherwise |
1888 | * return mimetypes of files, it can be an array | |
1889 | */ | |
014c1ca0 | 1890 | public function supported_filetypes() { |
b318bb6d | 1891 | // return array('text/plain', 'image/gif'); |
1892 | return '*'; | |
1893 | } | |
1894 | ||
1895 | /** | |
c8161eec | 1896 | * Tells how the file can be picked from this repository |
67233725 | 1897 | * |
c8161eec MG |
1898 | * Maximum value is FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE |
1899 | * | |
1900 | * @return int | |
b318bb6d | 1901 | */ |
41076c58 | 1902 | public function supported_returntypes() { |
c8161eec | 1903 | return (FILE_INTERNAL | FILE_EXTERNAL); |
92eaeca5 | 1904 | } |
b318bb6d | 1905 | |
82669dc4 | 1906 | /** |
3f24ea1e | 1907 | * Provide repository instance information for Ajax |
67233725 DC |
1908 | * |
1909 | * @return stdClass | |
82669dc4 | 1910 | */ |
7d554b64 | 1911 | final public function get_meta() { |
c82d7320 | 1912 | global $CFG, $OUTPUT; |
6bdfef5d | 1913 | $meta = new stdClass(); |
7d554b64 | 1914 | $meta->id = $this->id; |
102f9286 | 1915 | $meta->name = format_string($this->get_name()); |
31581ae6 | 1916 | $meta->type = $this->get_typename(); |
c82d7320 | 1917 | $meta->icon = $OUTPUT->pix_url('icon', 'repository_'.$meta->type)->out(false); |
559276b1 | 1918 | $meta->supported_types = file_get_typegroup('extension', $this->supported_filetypes()); |
6f2cd52a | 1919 | $meta->return_types = $this->supported_returntypes(); |
36ef6643 | 1920 | $meta->sortorder = $this->options['sortorder']; |
7d554b64 | 1921 | return $meta; |
4a65c39a | 1922 | } |
1cf56396 | 1923 | |
b1339e98 | 1924 | /** |
1925 | * Create an instance for this plug-in | |
67233725 DC |
1926 | * |
1927 | * @static | |
8dcd5deb | 1928 | * @param string $type the type of the repository |
67233725 DC |
1929 | * @param int $userid the user id |
1930 | * @param stdClass $context the context | |
8dcd5deb | 1931 | * @param array $params the options for this instance |
67233725 | 1932 | * @param int $readonly whether to create it readonly or not (defaults to not) |
2b8beee3 | 1933 | * @return mixed |
b1339e98 | 1934 | */ |
49d20def | 1935 | public static function create($type, $userid, $context, $params, $readonly=0) { |
b1339e98 | 1936 | global $CFG, $DB; |
1937 | $params = (array)$params; | |
e35194be | 1938 | require_once($CFG->dirroot . '/repository/'. $type . '/lib.php'); |
b1339e98 | 1939 | $classname = 'repository_' . $type; |
3023078f | 1940 | if ($repo = $DB->get_record('repository', array('type'=>$type))) { |
6bdfef5d | 1941 | $record = new stdClass(); |
3023078f | 1942 | $record->name = $params['name']; |
1943 | $record->typeid = $repo->id; | |
1944 | $record->timecreated = time(); | |
1945 | $record->timemodified = time(); | |
1946 | $record->contextid = $context->id; | |
948c2860 | 1947 | $record->readonly = $readonly; |
3023078f | 1948 | $record->userid = $userid; |
1949 | $id = $DB->insert_record('repository_instances', $record); | |
0a6221f9 | 1950 | $options = array(); |
edb50637 | 1951 | $configs = call_user_func($classname . '::get_instance_option_names'); |
1e08b5cf | 1952 | if (!empty($configs)) { |
1953 | foreach ($configs as $config) { | |
14b2d8d8 DC |
1954 | if (isset($params[$config])) { |
1955 | $options[$config] = $params[$config]; | |
1956 | } else { | |
1957 | $options[$config] = null; | |
1958 | } | |
1e08b5cf | 1959 | } |
3023078f | 1960 | } |
3a01a46a | 1961 | |
3023078f | 1962 | if (!empty($id)) { |
1963 | unset($options['name']); | |
dbc01944 | 1964 | $instance = repository::get_instance($id); |
3023078f | 1965 | $instance->set_option($options); |
1966 | return $id; | |
1967 | } else { | |
1968 | return null; | |
b1339e98 | 1969 | } |
b1339e98 | 1970 | } else { |
1971 | return null; | |
1972 | } | |
1973 | } | |
8dcd5deb | 1974 | |
82669dc4 | 1975 | /** |
4a65c39a | 1976 | * delete a repository instance |
67233725 DC |
1977 | * |
1978 | * @param bool $downloadcontents | |
1979 | * @return bool | |
82669dc4 | 1980 | */ |
67233725 | 1981 | final public function delete($downloadcontents = false) { |
4a65c39a | 1982 | global $DB; |
67233725 DC |
1983 | if ($downloadcontents) { |
1984 | $this->convert_references_to_local(); | |
1985 | } | |
1986 | try { | |
1987 | $DB->delete_records('repository_instances', array('id'=>$this->id)); | |
1988 | $DB->delete_records('repository_instance_config', array('instanceid'=>$this->id)); | |
1989 | } catch (dml_exception $ex) { | |
1990 | return false; | |
1991 | } | |
4a65c39a | 1992 | return true; |
1993 | } | |
8dcd5deb | 1994 | |
4a65c39a | 1995 | /** |
1996 | * Hide/Show a repository | |
67233725 | 1997 | * |
8dcd5deb | 1998 | * @param string $hide |
67233725 | 1999 | * @return bool |
4a65c39a | 2000 | */ |
7a3b93c1 | 2001 | final public function hide($hide = 'toggle') { |
4a65c39a | 2002 | global $DB; |
2003 | if ($entry = $DB->get_record('repository', array('id'=>$this->id))) { | |
2004 | if ($hide === 'toggle' ) { | |
2005 | if (!empty($entry->visible)) { | |
2006 | $entry->visible = 0; | |
2007 | } else { | |
2008 | $entry->visible = 1; | |
2009 | } | |
2010 | } else { | |
2011 | if (!empty($hide)) { | |
2012 | $entry->visible = 0; | |
2013 | } else { | |
2014 | $entry->visible = 1; | |
2015 | } | |
2016 | } | |
2017 | return $DB->update_record('repository', $entry); | |
2018 | } | |
2019 | return false; | |
2020 | } | |
1cf56396 | 2021 | |
1cf56396 | 2022 | /** |
4a65c39a | 2023 | * Save settings for repository instance |
8dcd5deb | 2024 | * $repo->set_option(array('api_key'=>'f2188bde132', 'name'=>'dongsheng')); |
67233725 | 2025 | * |
8dcd5deb | 2026 | * @param array $options settings |
67233725 | 2027 | * @return bool |
1cf56396 | 2028 | */ |
7a3b93c1 | 2029 | public function set_option($options = array()) { |
4a65c39a | 2030 | global $DB; |
7a3b93c1 | 2031 | |
122defc5 | 2032 | if (!empty($options['name'])) { |
a27ab6fd | 2033 | $r = new stdClass(); |
122defc5 | 2034 | $r->id = $this->id; |
2035 | $r->name = $options['name']; | |
2036 | $DB->update_record('repository_instances', $r); | |
2037 | unset($options['name']); | |
2038 | } | |
27051e43 | 2039 | foreach ($options as $name=>$value) { |
2040 | if ($id = $DB->get_field('repository_instance_config', 'id', array('name'=>$name, 'instanceid'=>$this->id))) { | |
f685e830 | 2041 | $DB->set_field('repository_instance_config', 'value', $value, array('id'=>$id)); |
27051e43 | 2042 | } else { |
a27ab6fd | 2043 | $config = new stdClass(); |
27051e43 | 2044 | $config->instanceid = $this->id; |
2045 | $config->name = $name; | |
2046 | $config->value = $value; | |
f685e830 | 2047 | $DB->insert_record('repository_instance_config', $config); |
27051e43 | 2048 | } |
4a65c39a | 2049 | } |
f685e830 | 2050 | return true; |
1cf56396 | 2051 | } |
1cf56396 | 2052 | |
4a65c39a | 2053 | /** |
2054 | * Get settings for repository instance | |
67233725 | 2055 | * |
2b8beee3 | 2056 | * @param string $config |
4a65c39a | 2057 | * @return array Settings |
2058 | */ | |
7a3b93c1 | 2059 | public function get_option($config = '') { |
4a65c39a | 2060 | global $DB; |
27051e43 | 2061 | $entries = $DB->get_records('repository_instance_config', array('instanceid'=>$this->id)); |
2062 | $ret = array(); | |
2063 | if (empty($entries)) { | |
2064 | return $ret; | |
4a65c39a | 2065 | } |
7a3b93c1 | 2066 | foreach($entries as $entry) { |
27051e43 | 2067 | $ret[$entry->name] = $entry->value; |
4a65c39a | 2068 | } |
2069 | if (!empty($config)) { | |
9e6aa5ec | 2070 | if (isset($ret[$config])) { |
75003899 | 2071 | return $ret[$config]; |
9e6aa5ec DC |
2072 | } else { |
2073 | return null; | |
2074 | } | |
4a65c39a | 2075 | } else { |
2076 | return $ret; | |
2077 | } | |
2078 | } | |
bf1fccf0 | 2079 | |
67233725 DC |
2080 | /** |
2081 | * Filter file listing to display specific types | |
2082 | * | |
2083 | * @param array $value | |
2084 | * @return bool | |
2085 | */ | |
c167aa26 | 2086 | public function filter(&$value) { |
18bd7573 | 2087 | $accepted_types = optional_param_array('accepted_types', '', PARAM_RAW); |
b81ebefd | 2088 | if (isset($value['children'])) { |
b81ebefd | 2089 | if (!empty($value['children'])) { |
b81ebefd | 2090 | $value['children'] = array_filter($value['children'], array($this, 'filter')); |
537d44e8 | 2091 | } |
559276b1 | 2092 | return true; // always return directories |
c167aa26 | 2093 | } else { |
46ee50dc | 2094 | if ($accepted_types == '*' or empty($accepted_types) |
2095 | or (is_array($accepted_types) and in_array('*', $accepted_types))) { | |
559276b1 MG |
2096 | return true; |
2097 | } else { | |
e5635827 MG |
2098 | foreach ($accepted_types as $ext) { |
2099 | if (preg_match('#'.$ext.'$#i', $value['title'])) { | |
559276b1 | 2100 | return true; |
c167aa26 | 2101 | } |
2102 | } | |
2103 | } | |
2104 | } | |
559276b1 | 2105 | return false; |
c167aa26 | 2106 | } |
2107 | ||
4a65c39a | 2108 | /** |
2109 | * Given a path, and perhaps a search, get a list of files. | |
2110 | * | |
67233725 | 2111 | * See details on {@link http://docs.moodle.org/dev/Repository_plugins} |
f7639c37 | 2112 | * |
67233725 DC |
2113 | * @param string $path this parameter can a folder name, or a identification of folder |
2114 | * @param string $page the page number of file list | |
1a6195b4 DC |
2115 | * @return array the list of files, including meta infomation, containing the following keys |
2116 | * manage, url to manage url | |
2117 | * client_id | |
2118 | * login, login form | |
2119 | * repo_id, active repository id | |
2120 | * login_btn_action, the login button action | |
2121 | * login_btn_label, the login button label | |
2122 | * total, number of results | |
2123 | * perpage, items per page | |
2124 | * page | |
2125 | * pages, total pages | |
29199e56 | 2126 | * issearchresult, is it a search result? |
1a6195b4 DC |
2127 | * list, file list |
2128 | * path, current path and parent path | |
4a65c39a | 2129 | */ |
9d6aa286 | 2130 | public function get_listing($path = '', $page = '') { |
2131 | } | |
1cf56396 | 2132 | |
8136753a | 2133 | |
fbd508b4 | 2134 | /** |
8136753a | 2135 | * Prepare the breadcrumb. |
5bdf63cc | 2136 | * |
8136753a FM |
2137 | * @param array $breadcrumb contains each element of the breadcrumb. |
2138 | * @return array of breadcrumb elements. | |
2139 | * @since 2.3.3 | |
5bdf63cc | 2140 | */ |
8136753a | 2141 | protected static function prepare_breadcrumb($breadcrumb) { |
5bdf63cc | 2142 | global $OUTPUT; |
8136753a FM |
2143 | $foldericon = $OUTPUT->pix_url(file_folder_icon(24))->out(false); |
2144 | $len = count($breadcrumb); | |
2145 | for ($i = 0; $i < $len; $i++) { | |
2146 | if (is_array($breadcrumb[$i]) && !isset($breadcrumb[$i]['icon'])) { | |
2147 | $breadcrumb[$i]['icon'] = $foldericon; | |
2148 | } else if (is_object($breadcrumb[$i]) && !isset($breadcrumb[$i]->icon)) { | |
2149 | $breadcrumb[$i]->icon = $foldericon; | |
9213f547 MG |
2150 | } |
2151 | } | |
8136753a FM |
2152 | return $breadcrumb; |
2153 | } | |
9213f547 | 2154 | |
8136753a FM |
2155 | /** |
2156 | * Prepare the file/folder listing. | |
2157 | * | |
2158 | * @param array $list of files and folders. | |
2159 | * @return array of files and folders. | |
2160 | * @since 2.3.3 | |
2161 | */ | |
2162 | protected static function prepare_list($list) { | |
2163 | global $OUTPUT; | |
2164 | $foldericon = $OUTPUT->pix_url(file_folder_icon(24))->out(false); | |
2165 | ||
2166 | // Reset the array keys because non-numeric keys will create an object when converted to JSON. | |
2167 | $list = array_values($list); | |
2168 | ||
2169 | $len = count($list); | |
2170 | for ($i = 0; $i < $len; $i++) { | |
2171 | if (is_object($list[$i])) { | |
2172 | $file = (array)$list[$i]; | |
e709ddd2 MG |
2173 | $converttoobject = true; |
2174 | } else { | |
8136753a | 2175 | $file =& $list[$i]; |
e709ddd2 MG |
2176 | $converttoobject = false; |
2177 | } | |
b92241f2 MG |
2178 | if (isset($file['size'])) { |
2179 | $file['size'] = (int)$file['size']; | |
2180 | $file['size_f'] = display_size($file['size']); | |
2181 | } | |
8136753a | 2182 | if (isset($file['license']) && get_string_manager()->string_exists($file['license'], 'license')) { |
b92241f2 MG |
2183 | $file['license_f'] = get_string($file['license'], 'license'); |
2184 | } | |
2185 | if (isset($file['image_width']) && isset($file['image_height'])) { | |
dd1f051e MG |
2186 | $a = array('width' => $file['image_width'], 'height' => $file['image_height']); |
2187 | $file['dimensions'] = get_string('imagesize', 'repository', (object)$a); | |
5bdf63cc MG |
2188 | } |
2189 | foreach (array('date', 'datemodified', 'datecreated') as $key) { | |
b92241f2 MG |
2190 | if (!isset($file[$key]) && isset($file['date'])) { |
2191 | $file[$key] = $file['date']; | |
5bdf63cc | 2192 | } |
b92241f2 | 2193 | if (isset($file[$key])) { |
5bdf63cc | 2194 | // must be UNIX timestamp |
b92241f2 MG |
2195 | $file[$key] = (int)$file[$key]; |
2196 | if (!$file[$key]) { | |
2197 | unset($file[$key]); | |
5bdf63cc | 2198 | } else { |
b92241f2 MG |
2199 | $file[$key.'_f'] = userdate($file[$key], get_string('strftimedatetime', 'langconfig')); |
2200 | $file[$key.'_f_s'] = userdate($file[$key], get_string('strftimedatetimeshort', 'langconfig')); | |
5bdf63cc MG |
2201 | } |
2202 | } | |
2203 | } | |
e709ddd2 MG |
2204 | $isfolder = (array_key_exists('children', $file) || (isset($file['type']) && $file['type'] == 'folder')); |
2205 | $filename = null; | |
2206 | if (isset($file['title'])) { | |
2207 | $filename = $file['title']; | |
2208 | } | |
2209 | else if (isset($file['fullname'])) { | |
2210 | $filename = $file['fullname']; | |
2211 | } | |
2212 | if (!isset($file['mimetype']) && !$isfolder && $filename) { | |
559276b1 | 2213 | $file['mimetype'] = get_mimetype_description(array('filename' => $filename)); |
5bdf63cc | 2214 | } |
e709ddd2 MG |
2215 | if (!isset($file['icon'])) { |
2216 | if ($isfolder) { | |
8136753a | 2217 | $file['icon'] = $foldericon; |
e709ddd2 | 2218 | } else if ($filename) { |
559276b1 | 2219 | $file['icon'] = $OUTPUT->pix_url(file_extension_icon($filename, 24))->out(false); |
5bdf63cc MG |
2220 | } |
2221 | } | |
8136753a FM |
2222 | |
2223 | // Recursively loop over children. | |
2224 | if (isset($file['children'])) { | |
2225 | $file['children'] = self::prepare_list($file['children']); | |
2226 | } | |
2227 | ||
2228 | // Convert the array back to an object. | |
e709ddd2 | 2229 | if ($converttoobject) { |
8136753a | 2230 | $list[$i] = (object)$file; |
e709ddd2 | 2231 | } |
5bdf63cc | 2232 | } |
8136753a FM |
2233 | return $list; |
2234 | } | |
2235 | ||
2236 | /** | |
2237 | * Prepares list of files before passing it to AJAX, makes sure data is in the correct | |
2238 | * format and stores formatted values. | |
2239 | * | |
2240 | * @param array|stdClass $listing result of get_listing() or search() or file_get_drafarea_files() | |
2241 | * @return array | |
2242 | */ | |
2243 | public static function prepare_listing($listing) { | |
2244 | $wasobject = false; | |
2245 | if (is_object($listing)) { | |
2246 | $listing = (array) $listing; | |
2247 | $wasobject = true; | |
2248 | } | |
2249 | ||
2250 | // Prepare the breadcrumb, passed as 'path'. | |
2251 | if (isset($listing['path']) && is_array($listing['path'])) { | |
2252 | $listing['path'] = self::prepare_breadcrumb($listing['path']); | |
2253 | } | |
2254 | ||
2255 | // Prepare the listing of objects. | |
2256 | if (isset($listing['list']) && is_array($listing['list'])) { | |
2257 | $listing['list'] = self::prepare_list($listing['list']); | |
2258 | } | |
2259 | ||
2260 | // Convert back to an object. | |
2261 | if ($wasobject) { | |
2262 | $listing = (object) $listing; | |
2263 | } | |
5bdf63cc MG |
2264 | return $listing; |
2265 | } | |
2266 | ||
2267 | /** | |
2268 | * Search files in repository | |
2269 | * When doing global search, $search_text will be used as | |
2270 | * keyword. | |
353d5cf3 | 2271 | * |
67233725 DC |
2272 | * @param string $search_text search key word |
2273 | * @param int $page page | |
72a56555 | 2274 | * @return mixed see {@link repository::get_listing()} |
fbd508b4 | 2275 | */ |
68a7c9a6 | 2276 | public function search($search_text, $page = 0) { |
353d5cf3 | 2277 | $list = array(); |
2278 | $list['list'] = array(); | |
2279 | return false; | |
fbd508b4 | 2280 | } |
2281 | ||
d68c527f | 2282 | /** |
2283 | * Logout from repository instance | |
2284 | * By default, this function will return a login form | |
2285 | * | |
2286 | * @return string | |
2287 | */ | |
2288 | public function logout(){ | |
2289 | return $this->print_login(); | |
2290 | } | |
2291 | ||
2292 | /** | |
2293 | * To check whether the user is logged in. | |
2294 | * | |
67233725 | 2295 | * @return bool |
d68c527f | 2296 | */ |
2297 | public function check_login(){ | |
2298 | return true; | |
2299 | } | |
2300 | ||
1cf56396 | 2301 | |
4a65c39a | 2302 | /** |
2303 | * Show the login screen, if required | |
67233725 DC |
2304 | * |
2305 | * @return string | |
4a65c39a | 2306 | */ |
1d66f2b2 | 2307 | public function print_login(){ |
2308 | return $this->get_listing(); | |
2309 | } | |
1cf56396 | 2310 | |
4a65c39a | 2311 | /** |
2312 | * Show the search screen, if required | |
67233725 DC |
2313 | * |
2314 | * @return string | |
4a65c39a | 2315 | */ |
a560785f | 2316 | public function print_search() { |
b5e7b638 | 2317 | global $PAGE; |
d1d18691 | 2318 | $renderer = $PAGE->get_renderer('core', 'files'); |
b5e7b638 | 2319 | return $renderer->repository_default_searchform(); |
2b9feb5f | 2320 | } |
4a65c39a | 2321 | |
3e123368 DC |
2322 | /** |
2323 | * For oauth like external authentication, when external repository direct user back to moodle, | |
2324 | * this funciton will be called to set up token and token_secret | |
2325 | */ | |
2326 | public function callback() { | |
2327 | } | |
2328 | ||
455860ce | 2329 | /** |
2330 | * is it possible to do glboal search? | |
67233725 DC |
2331 | * |
2332 | * @return bool | |
455860ce | 2333 | */ |
7a3b93c1 | 2334 | public function global_search() { |
455860ce | 2335 | return false; |
2336 | } | |
2337 | ||
8dcd5deb | 2338 | /** |
a6600395 | 2339 | * Defines operations that happen occasionally on cron |
67233725 DC |
2340 | * |
2341 | * @return bool | |
8dcd5deb | 2342 | */ |
a6600395 | 2343 | public function cron() { |
2344 | return true; | |
2345 | } | |
2346 | ||
7892948d | 2347 | /** |
83a018ed | 2348 | * function which is run when the type is created (moodle administrator add the plugin) |
67233725 DC |
2349 | * |
2350 | * @return bool success or fail? | |
7892948d | 2351 | */ |
f1cfe56e | 2352 | public static function plugin_init() { |
3e0794ed | 2353 | return true; |
7892948d | 2354 | } |
2355 | ||
a6600395 | 2356 | /** |
06e65e1e | 2357 | * Edit/Create Admin Settings Moodle form |
67233725 DC |
2358 | * |
2359 | * @param moodleform $mform Moodle form (passed by reference) | |
8e5af6cf | 2360 | * @param string $classname repository class name |
a6600395 | 2361 | */ |
68a7c9a6 | 2362 | public static function type_config_form($mform, $classname = 'repository') { |
8e5af6cf | 2363 | $instnaceoptions = call_user_func(array($classname, 'get_instance_option_names'), $mform, $classname); |
a5adfa26 DC |
2364 | if (empty($instnaceoptions)) { |
2365 | // this plugin has only one instance | |
2366 | // so we need to give it a name | |
2367 | // it can be empty, then moodle will look for instance name from language string | |
2368 | $mform->addElement('text', 'pluginname', get_string('pluginname', 'repository'), array('size' => '40')); | |
2369 | $mform->addElement('static', 'pluginnamehelp', '', get_string('pluginnamehelp', 'repository')); | |
102f9286 | 2370 | $mform->setType('pluginname', PARAM_TEXT); |
a5adfa26 | 2371 | } |
4a65c39a | 2372 | } |
dbc01944 | 2373 | |
84ee73ee RK |
2374 | /** |
2375 | * Validate Admin Settings Moodle form | |
67233725 DC |
2376 | * |
2377 | * @static | |
2378 | * @param moodleform $mform Moodle form (passed by reference) | |
2379 | * @param array $data array of ("fieldname"=>value) of submitted data | |
2380 | * @param array $errors array of ("fieldname"=>errormessage) of errors | |
84ee73ee RK |
2381 | * @return array array of errors |
2382 | */ | |
2383 | public static function type_form_validation($mform, $data, $errors) { | |
2384 | return $errors; | |
2385 | } | |
2386 | ||
2387 | ||
6f2cd52a | 2388 | /** |
06e65e1e | 2389 | * Edit/Create Instance Settings Moodle form |
67233725 DC |
2390 | * |
2391 | * @param moodleform $mform Moodle form (passed by reference) | |
a6600395 | 2392 | */ |
4a126f17 | 2393 | public static function instance_config_form($mform) { |
a6600395 | 2394 | } |
4a65c39a | 2395 | |
a6600395 | 2396 | /** |
67233725 | 2397 | * Return names of the general options. |
a6600395 | 2398 | * By default: no general option name |
67233725 | 2399 | * |
a6600395 | 2400 | * @return array |
2401 | */ | |
1b79955a | 2402 | public static function get_type_option_names() { |
a5adfa26 | 2403 | return array('pluginname'); |
a6600395 | 2404 | } |
2405 | ||
2406 | /** | |
67233725 | 2407 | * Return names of the instance options. |
a6600395 | 2408 | * By default: no instance option name |
67233725 | 2409 | * |
a6600395 | 2410 | * @return array |
2411 | */ | |
7a3b93c1 | 2412 | public static function get_instance_option_names() { |
a6600395 | 2413 | return array(); |
8b65d45c | 2414 | } |
e35194be | 2415 | |
67233725 DC |
2416 | /** |
2417 | * Validate repository plugin instance form | |
2418 | * | |
2419 | * @param moodleform $mform moodle form | |
2420 | * @param array $data form data | |
2421 | * @param array $errors errors | |
2422 | * @return array errors | |
2423 | */ | |
6b172cdc DC |
2424 | public static function instance_form_validation($mform, $data, $errors) { |
2425 | return $errors; | |
2426 | } | |
2427 | ||
67233725 DC |
2428 | /** |
2429 | * Create a shorten filename | |
2430 | * | |
2431 | * @param string $str filename | |
2432 | * @param int $maxlength max file name length | |
2433 | * @return string short filename | |
2434 | */ | |
f00340e2 | 2435 | public function get_short_filename($str, $maxlength) { |
138c7678 PS |
2436 | if (textlib::strlen($str) >= $maxlength) { |
2437 | return trim(textlib::substr($str, 0, $maxlength)).'...'; | |
f00340e2 DC |
2438 | } else { |
2439 | return $str; | |
2440 | } | |
2441 | } | |
f392caba DC |
2442 | |
2443 | /** | |
2444 | * Overwrite an existing file | |
2445 | * | |
2446 | * @param int $itemid | |
2447 | * @param string $filepath | |
2448 | * @param string $filename | |
2449 | * @param string $newfilepath | |
2450 | * @param string $newfilename | |
67233725 | 2451 | * @return bool |
f392caba | 2452 | */ |
061eeed5 | 2453 | public static function overwrite_existing_draftfile($itemid, $filepath, $filename, $newfilepath, $newfilename) { |
f392caba DC |
2454 | global $USER; |
2455 | $fs = get_file_storage(); | |
21c08c63 | 2456 | $user_context = context_user::instance($USER->id); |
f392caba DC |
2457 | if ($file = $fs->get_file($user_context->id, 'user', 'draft', $itemid, $filepath, $filename)) { |
2458 | if ($tempfile = $fs->get_file($user_context->id, 'user', 'draft', $itemid, $newfilepath, $newfilename)) { | |
2459 | // delete existing file to release filename | |
2460 | $file->delete(); | |
2461 | // create new file | |
2462 | $newfile = $fs->create_file_from_storedfile(array('filepath'=>$filepath, 'filename'=>$filename), $tempfile); | |
2463 | // remove temp file | |
2464 | $tempfile->delete(); | |
2465 | return true; | |
2466 | } | |
2467 | } | |
2468 | return false; | |
2469 | } | |
2470 | ||
2471 | /** | |
2472 | * Delete a temp file from draft area | |
2473 | * | |
2474 | * @param int $draftitemid | |
2475 | * @param string $filepath | |
2476 | * @param string $filename | |
67233725 | 2477 | * @return bool |
f392caba | 2478 | */ |
061eeed5 | 2479 | public static function delete_tempfile_from_draft($draftitemid, $filepath, $filename) { |
f392caba DC |
2480 | global $USER; |
2481 | $fs = get_file_storage(); | |
21c08c63 | 2482 | $user_context = context_user::instance($USER->id); |
f392caba DC |
2483 | if ($file = $fs->get_file($user_context->id, 'user', 'draft', $draftitemid, $filepath, $filename)) { |
2484 | $file->delete(); | |
2485 | return true; | |
2486 | } else { | |
2487 | return false; | |
2488 | } | |
2489 | } | |
67233725 DC |
2490 | |
2491 | /** | |
2492 | * Find all external files in this repo and import them | |
2493 | */ | |
2494 | public function convert_references_to_local() { | |
2495 | $fs = get_file_storage(); | |
2496 | $files = $fs->get_external_files($this->id); | |
2497 | foreach ($files as $storedfile) { | |
2498 | $fs->import_external_file($storedfile); | |
2499 | } | |
2500 | } | |
2501 | ||
6fd1cf05 MG |
2502 | /** |
2503 | * Called from phpunit between tests, resets whatever was cached | |
2504 | */ | |
2505 | public static function reset_caches() { | |
2506 | self::sync_external_file(null, true); | |
2507 | } | |
67233725 DC |
2508 | |
2509 | /** | |
2d222a32 | 2510 | * Performs synchronisation of reference to an external file if the previous one has expired. |
67233725 DC |
2511 | * |
2512 | * @param stored_file $file | |
6fd1cf05 | 2513 | * @param bool $resetsynchistory whether to reset all history of sync (used by phpunit) |
67233725 DC |
2514 | * @return bool success |
2515 | */ | |
6fd1cf05 | 2516 | public static function sync_external_file($file, $resetsynchistory = false) { |
67233725 | 2517 | global $DB; |
6fd1cf05 | 2518 | // TODO MDL-25290 static should be replaced with MUC code. |
0b2bfbd1 | 2519 | static $synchronized = array(); |
6fd1cf05 MG |
2520 | if ($resetsynchistory) { |
2521 | $synchronized = array(); | |
2522 | } | |
67233725 DC |
2523 | |
2524 | $fs = get_file_storage(); | |
2525 | ||
6fd1cf05 | 2526 | if (!$file || !$file->get_referencefileid()) { |
0b2bfbd1 MG |
2527 | return false; |
2528 | } | |
2529 | if (array_key_exists($file->get_id(), $synchronized)) { | |
2530 | return $synchronized[$file->get_id()]; | |
2531 | } | |
2532 | ||
2533 | // remember that we already cached in current request to prevent from querying again | |
2534 | $synchronized[$file->get_id()] = false; | |
2535 | ||
67233725 DC |
2536 | if (!$reference = $DB->get_record('files_reference', array('id'=>$file->get_referencefileid()))) { |
2537 | return false; | |
2538 | } | |
2539 | ||
2540 | if (!empty($reference->lastsync) and ($reference->lastsync + $reference->lifetime > time())) { | |
0b2bfbd1 MG |
2541 | $synchronized[$file->get_id()] = true; |
2542 | return true; | |
67233725 DC |
2543 | } |
2544 | ||
2545 | if (!$repository = self::get_repository_by_id($reference->repositoryid, SYSCONTEXTID)) { | |
2546 | return false; | |
2547 | } | |
2548 | ||
2549 | if (!$repository->sync_individual_file($file)) { | |
2550 | return false; | |
2551 | } | |
2552 | ||
2d222a32 | 2553 | $lifetime = $repository->get_reference_file_lifetime($reference); |
67233725 DC |
2554 | $fileinfo = $repository->get_file_by_reference($reference); |
2555 | if ($fileinfo === null) { | |
2556 | // does not exist any more - set status to missing | |
2d222a32 | 2557 | $file->set_missingsource($lifetime); |
0b2bfbd1 | 2558 | $synchronized[$file->get_id()] = true; |
67233725 | 2559 | return true; |
67233725 DC |
2560 | } |
2561 | ||
2562 | $contenthash = null; | |
2563 | $filesize = null; | |
2d222a32 MG |
2564 | if (!empty($fileinfo->filesize)) { |
2565 | // filesize returned | |
2566 | if (!empty($fileinfo->contenthash) && $fs->content_exists($fileinfo->contenthash)) { | |
2567 | // contenthash is specified and valid | |
2568 | $contenthash = $fileinfo->contenthash; | |
2569 | } else if ($fileinfo->filesize == $file->get_filesize()) { | |
2570 | // we don't know the new contenthash but the filesize did not change, | |
2571 | // assume the contenthash did not change either | |
2572 | $contenthash = $file->get_contenthash(); | |
2573 | } else { | |
2574 | // we can't save empty contenthash so generate contenthash from empty string | |
2575 | $fs->add_string_to_pool(''); | |
2576 | $contenthash = sha1(''); | |
2577 | } | |
67233725 DC |
2578 | $filesize = $fileinfo->filesize; |
2579 | } else if (!empty($fileinfo->filepath)) { | |
2580 | // File path returned | |
2581 | list($contenthash, $filesize, $newfile) = $fs->add_file_to_pool($fileinfo->filepath); | |
2582 | } else if (!empty($fileinfo->handle) && is_resource($fileinfo->handle)) { | |
2583 | // File handle returned | |
2584 | $contents = ''; | |
2585 | while (!feof($fileinfo->handle)) { | |
2586 | $contents .= fread($handle, 8192); | |
2587 | } | |
2588 | fclose($fileinfo->handle); | |
2589 | list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($content); | |
2590 | } else if (isset($fileinfo->content)) { | |
2591 | // File content returned | |
2592 | list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($fileinfo->content); | |
2593 | } | |
2594 | ||
2595 | if (!isset($contenthash) or !isset($filesize)) { | |
2596 | return false; | |
2597 | } | |
2598 | ||
67233725 | 2599 | // update files table |
2d222a32 | 2600 | $file->set_synchronized($contenthash, $filesize, 0, $lifetime); |
0b2bfbd1 | 2601 | $synchronized[$file->get_id()] = true; |
67233725 DC |
2602 | return true; |
2603 | } | |
d6453211 DC |
2604 | |
2605 | /** | |
2606 | * Build draft file's source field | |
2607 | * | |
2608 | * {@link file_restore_source_field_from_draft_file()} | |
2609 | * XXX: This is a hack for file manager (MDL-28666) | |
2610 | * For newly created draft files we have to construct | |
2611 | * source filed in php serialized data format. | |
2612 | * File manager needs to know the original file information before copying | |
2613 | * to draft area, so we append these information in mdl_files.source field | |
2614 | * | |
2615 | * @param string $source | |
2616 | * @return string serialised source field | |
2617 | */ | |
2618 | public static function build_source_field($source) { | |
2619 | $sourcefield = new stdClass; | |
2620 | $sourcefield->source = $source; | |
2621 | return serialize($sourcefield); | |
2622 | } | |
8b65d45c | 2623 | } |
837ebb78 | 2624 | |
2625 | /** | |
6f2cd52a DC |
2626 | * Exception class for repository api |
2627 | * | |
2628 | * @since 2.0 | |
72a56555 | 2629 | * @package core_repository |
67233725 | 2630 | * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org} |
6f2cd52a | 2631 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
837ebb78 | 2632 | */ |
4a65c39a | 2633 | class repository_exception extends moodle_exception { |
8b65d45c | 2634 | } |
4ed43890 | 2635 | |
8dcd5deb | 2636 | /** |
6f2cd52a DC |
2637 | * This is a class used to define a repository instance form |
2638 | * | |
2639 | * @since 2.0 | |
72a56555 | 2640 | * @package core_repository |
67233725 | 2641 | * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org} |
6f2cd52a | 2642 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
8dcd5deb | 2643 | */ |
a6600395 | 2644 | final class repository_instance_form extends moodleform { |
67233725 | 2645 | /** @var stdClass repository instance */ |
4a65c39a | 2646 | protected $instance; |
67233725 | 2647 | /** @var string repository plugin type */ |
4a65c39a | 2648 | protected $plugin; |
67233725 DC |
2649 | |
2650 | /** | |
2651 | * Added defaults to moodle form | |
2652 | */ | |
49d20def | 2653 | protected function add_defaults() { |
4a65c39a | 2654 | $mform =& $this->_form; |
2655 | $strrequired = get_string('required'); | |
2656 | ||
2657 | $mform->addElement('hidden', 'edit', ($this->instance) ? $this->instance->id : 0); | |
d18e0fe6 | 2658 | $mform->setType('edit', PARAM_INT); |
4a65c39a | 2659 | $mform->addElement('hidden', 'new', $this->plugin); |
405aca35 | 2660 | $mform->setType('new', PARAM_ALPHANUMEXT); |
4a65c39a | 2661 | $mform->addElement('hidden', 'plugin', $this->plugin); |
aff24313 | 2662 | $mform->setType('plugin', PARAM_PLUGIN); |
27051e43 | 2663 | $mform->addElement('hidden', 'typeid', $this->typeid); |
d18e0fe6 | 2664 | $mform->setType('typeid', PARAM_INT); |
faaa613d | 2665 | $mform->addElement('hidden', 'contextid', $this->contextid); |
d18e0fe6 | 2666 | $mform->setType('contextid', PARAM_INT); |
4a65c39a | 2667 | |
2668 | $mform->addElement('text', 'name', get_string('name'), 'maxlength="100" size="30"'); | |
2669 | $mform->addRule('name', $strrequired, 'required', null, 'client'); | |
102f9286 | 2670 | $mform->setType('name', PARAM_TEXT); |
49d20def | 2671 | } |
4a65c39a | 2672 | |
67233725 DC |
2673 | /** |
2674 | * Define moodle form elements | |
2675 | */ | |
49d20def DC |
2676 | public function definition() { |
2677 | global $CFG; | |
2678 | // type of plugin, string | |
2679 | $this->plugin = $this->_customdata['plugin']; | |
2680 | $this->typeid = $this->_customdata['typeid']; | |
2681 | $this->contextid = $this->_customdata['contextid']; | |
2682 | $this->instance = (isset($this->_customdata['instance']) | |
2683 | && is_subclass_of($this->_customdata['instance'], 'repository')) | |
2684 | ? $this->_customdata['instance'] : null; | |
2685 | ||
2686 | $mform =& $this->_form; | |
1e97f196 | 2687 | |
49d20def | 2688 | $this->add_defaults(); |
4a126f17 DP |
2689 | |
2690 | // Add instance config options. | |
2691 | $result = repository::static_function($this->plugin, 'instance_config_form', $mform); | |
2692 | if ($result === false) { | |
2693 | // Remove the name element if no other config options. | |
2694 | $mform->removeElement('name'); | |
2695 | } | |
2696 | if ($this->instance) { | |
f48fb4d6 | 2697 | $data = array(); |
2698 | $data['name'] = $this->instance->name; | |
2699 | if (!$this->instance->readonly) { | |
f48fb4d6 | 2700 | // and set the data if we have some. |
1e97f196 | 2701 | foreach ($this->instance->get_instance_option_names() as $config) { |
a3d8df25 | 2702 | if (!empty($this->instance->options[$config])) { |
2703 | $data[$config] = $this->instance->options[$config]; | |
1e97f196 | 2704 | } else { |
2705 | $data[$config] = ''; | |
2706 | } | |
27051e43 | 2707 | } |
4a65c39a | 2708 | } |
f48fb4d6 | 2709 | $this->set_data($data); |
1e97f196 | 2710 | } |
2711 | ||
49d20def DC |
2712 | if ($result === false) { |
2713 | $mform->addElement('cancel'); | |
2714 | } else { | |
2715 | $this->add_action_buttons(true, get_string('save','repository')); | |
2716 | } | |
4a65c39a | 2717 | } |
2718 | ||
67233725 DC |
2719 | /** |
2720 | * Validate moodle form data | |
2721 | * | |
2722 | * @param array $data form data | |
2723 | * @param array $files files in form | |
2724 | * @return array errors | |
2725 | */ | |
58eb9b9f | 2726 | public function validation($data, $files) { |
4a65c39a | 2727 | global $DB; |
4a65c39a | 2728 | $errors = array(); |
6b172cdc DC |
2729 | $plugin = $this->_customdata['plugin']; |
2730 | $instance = (isset($this->_customdata['instance']) | |
2731 | && is_subclass_of($this->_customdata['instance'], 'repository')) | |
2732 | ? $this->_customdata['instance'] : null; | |
2733 | if (!$instance) { | |
2734 | $errors = repository::static_function($plugin, 'instance_form_validation', $this, $data, $errors); | |
2735 | } else { | |
2736 | $errors = $instance->instance_form_validation($this, $data, $errors); | |
2737 | } | |
2738 | ||
a090554a PS |
2739 | $sql = "SELECT count('x') |
2740 | FROM {repository_instances} i, {repository} r | |
2741 | WHERE r.type=:plugin AND r.id=i.typeid AND i.name=:name"; | |
09bff8e8 | 2742 | if ($DB->count_records_sql($sql, array('name' => $data['name'], 'plugin' => $data['plugin'])) > 1) { |
6b172cdc | 2743 | $errors['name'] = get_string('erroruniquename', 'repository'); |
4a65c39a | 2744 | } |
2745 | ||
4a65c39a | 2746 | return $errors; |
2747 | } | |
2748 | } | |
a6600395 | 2749 | |
a6600395 | 2750 | /** |
6f2cd52a DC |
2751 | * This is a class used to define a repository type setting form |
2752 | * | |
2753 | * @since 2.0 | |
72a56555 | 2754 | * @package core_repository |
67233725 | 2755 | * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org} |
6f2cd52a | 2756 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
a6600395 | 2757 | */ |
c295f9ff | 2758 | final class repository_type_form extends moodleform { |
67233725 | 2759 | /** @var stdClass repository instance */ |
a6600395 | 2760 | protected $instance; |
67233725 | 2761 | /** @var string repository plugin name */ |
a6600395 | 2762 | protected $plugin; |
67233725 | 2763 | /** @var string action */ |
79698344 | 2764 | protected $action; |
a6600395 | 2765 | |
2766 | /** | |
2767 | * Definition of the moodleform | |
a6600395 | 2768 | */ |
2769 | public function definition() { | |
2770 | global $CFG; | |
2771 | // type of plugin, string | |
2772 | $this->plugin = $this->_customdata['plugin']; | |
2773 | $this->instance = (isset($this->_customdata['instance']) | |
2774 | && is_a($this->_customdata['instance'], 'repository_type')) | |
2775 | ? $this->_customdata['instance'] : null; | |
2776 | ||
79698344 | 2777 | $this->action = $this->_customdata['action']; |
a5adfa26 | 2778 | $this->pluginname = $this->_customdata['pluginname']; |
a6600395 | 2779 | $mform =& $this->_form; |
2780 | $strrequired = get_string('required'); | |
4d5948f1 | 2781 | |
79698344 MD |
2782 | $mform->addElement('hidden', 'action', $this->action); |
2783 | $mform->setType('action', PARAM_TEXT); | |
2784 | $mform->addElement('hidden', 'repos', $this->plugin); | |
aff24313 | 2785 | $mform->setType('repos', PARAM_PLUGIN); |
8f943eba | 2786 | |
06e65e1e | 2787 | // let the plugin add its specific fields |
a5adfa26 DC |
2788 | $classname = 'repository_' . $this->plugin; |
2789 | require_once($CFG->dirroot . '/repository/' . $this->plugin . '/lib.php'); | |
f48fb4d6 | 2790 | //add "enable course/user instances" checkboxes if multiple instances are allowed |
dbc01944 | 2791 | $instanceoptionnames = repository::static_function($this->plugin, 'get_instance_option_names'); |
8e5af6cf DC |
2792 | |
2793 | $result = call_user_func(array($classname, 'type_config_form'), $mform, $classname); | |
2794 | ||
a5adfa26 | 2795 | if (!empty($instanceoptionnames)) { |
8e5af6cf DC |
2796 | $sm = get_string_manager(); |
2797 | $component = 'repository'; | |
2798 | if ($sm->string_exists('enablecourseinstances', 'repository_' . $this->plugin)) { | |
2799 | $component .= ('_' . $this->plugin); | |
2800 | } | |
2801 | $mform->addElement('checkbox', 'enablecourseinstances', get_string('enablecourseinstances', $component)); | |
2802 | ||
2803 | $component = 'repository'; | |
2804 | if ($sm->string_exists('enableuserinstances', 'repository_' . $this->plugin)) { | |
2805 | $component .= ('_' . $this->plugin); | |
2806 | } | |
2807 | $mform->addElement('checkbox', 'enableuserinstances', get_string('enableuserinstances', $component)); | |
f48fb4d6 | 2808 | } |
2809 | ||
2810 | // set the data if we have some. | |
a6600395 | 2811 | if ($this->instance) { |
2812 | $data = array(); | |
1b79955a | 2813 | $option_names = call_user_func(array($classname,'get_type_option_names')); |
f48fb4d6 | 2814 | if (!empty($instanceoptionnames)){ |
2815 | $option_names[] = 'enablecourseinstances'; | |
2816 | $option_names[] = 'enableuserinstances'; | |
2817 | } | |
2818 | ||
a6600395 | 2819 | $instanceoptions = $this->instance->get_options(); |
2820 | foreach ($option_names as $config) { | |
2821 | if (!empty($instanceoptions[$config])) { | |
2822 | $data[$config] = $instanceoptions[$config]; | |
2823 | } else { | |
2824 | $data[$config] = ''; | |
2825 | } | |
2826 | } | |
a5adfa26 DC |
2827 | // XXX: set plugin name for plugins which doesn't have muliti instances |
2828 | if (empty($instanceoptionnames)){ | |
2829 | $data['pluginname'] = $this->pluginname; | |
2830 | } | |
a6600395 | 2831 | $this->set_data($data); |
2832 | } | |
dbc01944 | 2833 | |
46dd6bb0 | 2834 | $this->add_action_buttons(true, get_string('save','repository')); |
a6600395 | 2835 | } |
84ee73ee | 2836 | |
67233725 DC |
2837 | /** |
2838 | * Validate moodle form data | |
2839 | * | |
2840 | * @param array $data moodle form data | |
2841 | * @param array $files | |
2842 | * @return array errors | |
2843 | */ | |
58eb9b9f | 2844 | public function validation($data, $files) { |
84ee73ee RK |
2845 | $errors = array(); |
2846 | $plugin = $this->_customdata['plugin']; | |
2847 | $instance = (isset($this->_customdata['instance']) | |
2848 | && is_subclass_of($this->_customdata['instance'], 'repository')) | |
2849 | ? $this->_customdata['instance'] : null; | |
2850 | if (!$instance) { | |
2851 | $errors = repository::static_function($plugin, 'type_form_validation', $this, $data, $errors); | |
2852 | } else { | |
2853 | $errors = $instance->type_form_validation($this, $data, $errors); | |
2854 | } | |
2855 | ||
2856 | return $errors; | |
2857 | } | |
a6600395 | 2858 | } |
2859 | ||
8e0946bf DC |
2860 | /** |
2861 | * Generate all options needed by filepicker | |
2862 | * | |
67233725 | 2863 | * @param array $args including following keys |
8e0946bf DC |
2864 | * context |
2865 | * accepted_types | |
2866 | * return_types | |
2867 | * | |
2868 | * @return array the list of repository instances, including meta infomation, containing the following keys | |
2869 | * externallink | |
2870 | * repositories | |
2871 | * accepted_types | |
2872 | */ | |
99eaca9d | 2873 | function initialise_filepicker($args) { |
a28c9253 | 2874 | global $CFG, $USER, $PAGE, $OUTPUT; |
ec3eaa2c | 2875 | static $templatesinitialized = array(); |
1dce6261 | 2876 | require_once($CFG->libdir . '/licenselib.php'); |
4f0c2d00 | 2877 | |
6bdfef5d | 2878 | $return = new stdClass(); |
d1d4813f | 2879 | $licenses = array(); |
4f0c2d00 PS |
2880 | if (!empty($CFG->licenses)) { |
2881 | $array = explode(',', $CFG->licenses); | |
2882 | foreach ($array as $license) { | |
6bdfef5d | 2883 | $l = new stdClass(); |
4f0c2d00 PS |
2884 | $l->shortname = $license; |
2885 | $l->fullname = get_string($license, 'license'); | |
2886 | $licenses[] = $l; | |
2887 | } | |
d1d4813f | 2888 | } |
308d948b DC |
2889 | if (!empty($CFG->sitedefaultlicense)) { |
2890 | $return->defaultlicense = $CFG->sitedefaultlicense; | |
2891 | } | |
1dce6261 | 2892 | |
1dce6261 DC |
2893 | $return->licenses = $licenses; |
2894 | ||
2895 | $return->author = fullname($USER); | |
2896 | ||
99eaca9d DC |
2897 | if (empty($args->context)) { |
2898 | $context = $PAGE->context; | |
e189ec00 | 2899 | } else { |
99eaca9d DC |
2900 | $context = $args->context; |
2901 | } | |
6bf197b3 DC |
2902 | $disable_types = array(); |
2903 | if (!empty($args->disable_types)) { | |
2904 | $disable_types = $args->disable_types; | |
2905 | } | |
99eaca9d | 2906 | |
21c08c63 | 2907 | $user_context = context_user::instance($USER->id); |
99eaca9d | 2908 | |
bf413f6f DC |
2909 | list($context, $course, $cm) = get_context_info_array($context->id); |
2910 | $contexts = array($user_context, get_system_context()); | |
2911 | if (!empty($course)) { | |
2912 | // adding course context | |
21c08c63 | 2913 | $contexts[] = context_course::instance($course->id); |
bf413f6f | 2914 | } |
99eaca9d DC |
2915 | $externallink = (int)get_config(null, 'repositoryallowexternallinks'); |
2916 | $repositories = repository::get_instances(array( | |
bf413f6f | 2917 | 'context'=>$contexts, |
99eaca9d | 2918 | 'currentcontext'=> $context, |
ea1780ad | 2919 | 'accepted_types'=>$args->accepted_types, |
6bf197b3 DC |
2920 | 'return_types'=>$args->return_types, |
2921 | 'disable_types'=>$disable_types | |
99eaca9d DC |
2922 | )); |
2923 | ||
2924 | $return->repositories = array(); | |
2925 | ||
2926 | if (empty($externallink)) { | |
2927 | $return->externallink = false; | |
2928 | } else { | |
2929 | $return->externallink = true; | |
2930 | } | |
2931 | ||
837eef2e DP |
2932 | $return->userprefs = array(); |
2933 | $return->userprefs['recentrepository'] = get_user_preferences('filepicker_recentrepository', ''); | |
2934 | $return->userprefs['recentlicense'] = get_user_preferences('filepicker_recentlicense', ''); | |
2935 | $return->userprefs['recentviewmode'] = get_user_preferences('filepicker_recentviewmode', ''); | |
2936 | ||
2937 | user_preference_allow_ajax_update('filepicker_recentrepository', PARAM_INT); | |
2938 | user_preference_allow_ajax_update('filepicker_recentlicense', PARAM_SAFEDIR); | |
2939 | user_preference_allow_ajax_update('filepicker_recentviewmode', PARAM_INT); | |
2940 | ||
2941 | ||
99eaca9d | 2942 | // provided by form element |
559276b1 | 2943 | $return->accepted_types = file_get_typegroup('extension', $args->accepted_types); |
766514a0 | 2944 | $return->return_types = $args->return_types; |
013cf287 | 2945 | $templates = array(); |
99eaca9d DC |
2946 | foreach ($repositories as $repository) { |
2947 | $meta = $repository->get_meta(); | |
6b37c2b0 SH |
2948 | // Please note that the array keys for repositories are used within |
2949 | // JavaScript a lot, the key NEEDS to be the repository id. | |
b0985814 | 2950 | $return->repositories[$repository->id] = $meta; |
013cf287 | 2951 | // Register custom repository template if it has one |
ec3eaa2c PN |
2952 | if(method_exists($repository, 'get_upload_template') && !array_key_exists('uploadform_' . $meta->type, $templatesinitialized)) { |
2953 | $templates['uploadform_' . $meta->type] = $repository->get_upload_template(); | |
2954 | $templatesinitialized['uploadform_' . $meta->type] = true; | |
013cf287 | 2955 | } |
99eaca9d | 2956 | } |
ec3eaa2c PN |
2957 | if (!array_key_exists('core', $templatesinitialized)) { |
2958 | // we need to send each filepicker template to the browser just once | |
d1d18691 | 2959 | $fprenderer = $PAGE->get_renderer('core', 'files'); |
013cf287 | 2960 | $templates = array_merge($templates, $fprenderer->filepicker_js_templates()); |
ec3eaa2c PN |
2961 | $templatesinitialized['core'] = true; |
2962 | } | |
2963 | if (sizeof($templates)) { | |
d1d18691 | 2964 | $PAGE->requires->js_init_call('M.core_filepicker.set_templates', array($templates), true); |
d1d18691 | 2965 | } |
99eaca9d | 2966 | return $return; |
e189ec00 | 2967 | } |
72a56555 | 2968 | |
f392caba DC |
2969 | /** |
2970 | * Small function to walk an array to attach repository ID | |
67233725 | 2971 | * |
f392caba DC |
2972 | * @param array $value |
2973 | * @param string $key | |
2974 | * @param int $id | |
2975 | */ | |
2976 | function repository_attach_id(&$value, $key, $id){ | |
2977 | $value['repo_id'] = $id; | |
2978 | } |