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