MDL-17457 migrated all modules to new db/install.php; added upgrade.txt file for...
[moodle.git] / repository / lib.php
CommitLineData
12c79bfd 1<?php
2 // $Id$
1cf56396 3
4///////////////////////////////////////////////////////////////////////////
5// //
6// NOTICE OF COPYRIGHT //
7// //
8// Moodle - Modular Object-Oriented Dynamic Learning Environment //
9// http://moodle.com //
10// //
11// Copyright (C) 2008 onwards Moodle Pty Ltd http://moodle.com //
12// //
13// This program is free software; you can redistribute it and/or modify //
14// it under the terms of the GNU General Public License as published by //
15// the Free Software Foundation; either version 2 of the License, or //
16// (at your option) any later version. //
17// //
18// This program is distributed in the hope that it will be useful, //
19// but WITHOUT ANY WARRANTY; without even the implied warranty of //
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
21// GNU General Public License for more details: //
22// //
23// http://www.gnu.org/copyleft/gpl.html //
24// //
25///////////////////////////////////////////////////////////////////////////
26
aca64b79 27/**
28 * About repository/lib.php:
29 * two main classes:
30 * 1. repository_type => a repository plugin, You can activate a plugin into
31 * Moodle. You also can set some general settings/options for this type of repository.
32 * All instances would share the same options (for example: a API key for the connection
33 * to the repository)
34 * 2. repository => an instance of a plugin. You can also call it an access or
35 * an account. An instance has specific settings (for example: a public url) and a specific
36 * name. That's this name which is displayed in the file picker.
37 */
38
39
40
19add4c0 41require_once(dirname(dirname(__FILE__)) . '/config.php');
9bdd9eee 42require_once(dirname(dirname(__FILE__)) . '/lib/filelib.php');
43require_once(dirname(dirname(__FILE__)) . '/lib/formslib.php');
44// File picker javascript code
45require_once(dirname(dirname(__FILE__)) . '/repository/javascript.php');
1cf56396 46
a6600395 47/**
48 * A repository_type is a repository plug-in. It can be Box.net, Flick-r, ...
49 * A repository type can be edited, sorted and hidden. It is mandatory for an
50 * administrator to create a repository type in order to be able to create
51 * some instances of this type.
52 *
53 * Coding note:
54 * - a repository_type object is mapped to the "repository" database table
55 * - "typename" attibut maps the "type" database field. It is unique.
56 * - general "options" for a repository type are saved in the config_plugin table
eb239694 57 * - when you delete a repository, all instances are deleted, and general
58 * options are also deleted from database
a6600395 59 * - When you create a type for a plugin that can't have multiple instances, a
60 * instance is automatically created.
61 */
62class repository_type {
63
64
65 /**
66 * Type name (no whitespace) - A type name is unique
67 * Note: for a user-friendly type name see get_readablename()
68 * @var String
69 */
70 private $_typename;
71
72
73 /**
74 * Options of this type
75 * They are general options that any instance of this type would share
76 * e.g. API key
77 * These options are saved in config_plugin table
78 * @var array
79 */
7a3b93c1 80 private $_options;
a6600395 81
82
83 /**
84 * Is the repository type visible or hidden
85 * If false (hidden): no instances can be created, edited, deleted, showned , used...
86 * @var boolean
87 */
7a3b93c1 88 private $_visible;
a6600395 89
90
91 /**
92 * 0 => not ordered, 1 => first position, 2 => second position...
93 * A not order type would appear in first position (should never happened)
94 * @var integer
95 */
96 private $_sortorder;
97
f48fb4d6 98 /**
99 * Return if the instance is visible in a context
100 * TODO: check if the context visibility has been overwritten by the plugin creator
101 * (need to create special functions to be overvwritten in repository class)
102 * @param objet $contextlevel - context level
103 * @return boolean
104 */
105 public function get_contextvisibility($contextlevel) {
dbc01944 106
f48fb4d6 107 if ($contextlevel == CONTEXT_COURSE) {
108 return $this->_options['enablecourseinstances'];
109 }
110
111 if ($contextlevel == CONTEXT_USER) {
112 return $this->_options['enableuserinstances'];
113 }
114
115 //the context is SITE
116 return true;
117 }
dbc01944 118
f48fb4d6 119
120
a6600395 121 /**
122 * repository_type constructor
123 * @global <type> $CFG
124 * @param integer $typename
125 * @param array $typeoptions
126 * @param boolean $visible
127 * @param integer $sortorder (don't really need set, it will be during create() call)
128 */
7a3b93c1 129 public function __construct($typename = '', $typeoptions = array(), $visible = false, $sortorder = 0) {
a6600395 130 global $CFG;
131
132 //set type attributs
133 $this->_typename = $typename;
134 $this->_visible = $visible;
135 $this->_sortorder = $sortorder;
46dd6bb0 136
a6600395 137 //set options attribut
138 $this->_options = array();
dbc01944 139 $options = repository::static_function($typename,'get_type_option_names');
a6600395 140 //check that the type can be setup
06e65e1e 141 if (!empty($options)) {
a6600395 142 //set the type options
143 foreach ($options as $config) {
7a3b93c1 144 if (array_key_exists($config,$typeoptions)) {
145 $this->_options[$config] = $typeoptions[$config];
a6600395 146 }
147 }
148 }
f48fb4d6 149
150 //retrieve visibility from option
151 if (array_key_exists('enablecourseinstances',$typeoptions)) {
152 $this->_options['enablecourseinstances'] = $typeoptions['enablecourseinstances'];
9f7c761a 153 } else {
154 $this->_options['enablecourseinstances'] = 0;
f48fb4d6 155 }
dbc01944 156
f48fb4d6 157 if (array_key_exists('enableuserinstances',$typeoptions)) {
158 $this->_options['enableuserinstances'] = $typeoptions['enableuserinstances'];
9f7c761a 159 } else {
160 $this->_options['enableuserinstances'] = 0;
f48fb4d6 161 }
dbc01944 162
a6600395 163 }
164
165 /**
166 * Get the type name (no whitespace)
167 * For a human readable name, use get_readablename()
168 * @return String the type name
169 */
7a3b93c1 170 public function get_typename() {
a6600395 171 return $this->_typename;
172 }
173
174 /**
175 * Return a human readable and user-friendly type name
176 * @return string user-friendly type name
177 */
7a3b93c1 178 public function get_readablename() {
a6600395 179 return get_string('repositoryname','repository_'.$this->_typename);
180 }
181
182 /**
183 * Return general options
184 * @return array the general options
185 */
7a3b93c1 186 public function get_options() {
a6600395 187 return $this->_options;
188 }
189
190 /**
191 * Return visibility
192 * @return boolean
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
200 * @return integer
201 */
7a3b93c1 202 public function get_sortorder() {
a6600395 203 return $this->_sortorder;
204 }
205
206 /**
207 * Create a repository type (the type name must not already exist)
208 * @global object $DB
209 */
7a3b93c1 210 public function create() {
a6600395 211 global $DB;
212
213 //check that $type has been set
214 $timmedtype = trim($this->_typename);
215 if (empty($timmedtype)) {
7a3b93c1 216 throw new repository_exception('emptytype', 'repository');
a6600395 217 }
218
219 //set sortorder as the last position in the list
7a3b93c1 220 if (!isset($this->_sortorder) || $this->_sortorder == 0 ) {
a6600395 221 $sql = "SELECT MAX(sortorder) FROM {repository}";
222 $this->_sortorder = 1 + $DB->get_field_sql($sql);
223 }
224
225 //only create a new type if it doesn't already exist
226 $existingtype = $DB->get_record('repository', array('type'=>$this->_typename));
7a3b93c1 227 if (!$existingtype) {
f1cfe56e 228 //run init function
229 if (!repository::static_function($this->_typename, 'plugin_init')) {
230 throw new repository_exception('cannotcreatetype', 'repository');
231 }
232
7a3b93c1 233 //create the type
234 $newtype = new stdclass;
235 $newtype->type = $this->_typename;
236 $newtype->visible = $this->_visible;
237 $newtype->sortorder = $this->_sortorder;
238 $DB->insert_record('repository', $newtype);
239
240 //save the options in DB
241 $this->update_options();
242
edb50637 243 //if the plugin type has no multiple instance (e.g. has no instance option name) so it wont
7a3b93c1 244 //be possible for the administrator to create a instance
245 //in this case we need to create an instance
dbc01944 246 $instanceoptionnames = repository::static_function($this->_typename, 'get_instance_option_names');
edb50637 247 if (empty($instanceoptionnames)) {
7a3b93c1 248 $instanceoptions = array();
249 $instanceoptions['name'] = $this->_typename;
dbc01944 250 repository::static_function($this->_typename, 'create', $this->_typename, 0, get_system_context(), $instanceoptions);
7a3b93c1 251 }
948c2860 252
7a3b93c1 253 } else {
a6600395 254 throw new repository_exception('existingrepository', 'repository');
255 }
256 }
257
258
259 /**
260 * Update plugin options into the config_plugin table
261 * @param array $options
262 * @return boolean
263 */
7a3b93c1 264 public function update_options($options = null) {
265 if (!empty($options)) {
a6600395 266 $this->_options = $options;
267 }
268
269 foreach ($this->_options as $name => $value) {
270 set_config($name,$value,$this->_typename);
271 }
272
273 return true;
274 }
275
276 /**
277 * Update visible database field with the value given as parameter
278 * or with the visible value of this object
279 * This function is private.
280 * For public access, have a look to switch_and_update_visibility()
281 * @global object $DB
282 * @param boolean $visible
283 * @return boolean
284 */
7a3b93c1 285 private function update_visible($visible = null) {
a6600395 286 global $DB;
287
7a3b93c1 288 if (!empty($visible)) {
a6600395 289 $this->_visible = $visible;
290 }
7a3b93c1 291 else if (!isset($this->_visible)) {
a6600395 292 throw new repository_exception('updateemptyvisible', 'repository');
293 }
294
295 return $DB->set_field('repository', 'visible', $this->_visible, array('type'=>$this->_typename));
296 }
297
298 /**
299 * Update database sortorder field with the value given as parameter
300 * or with the sortorder value of this object
301 * This function is private.
302 * For public access, have a look to move_order()
303 * @global object $DB
304 * @param integer $sortorder
305 * @return boolean
306 */
7a3b93c1 307 private function update_sortorder($sortorder = null) {
a6600395 308 global $DB;
309
7a3b93c1 310 if (!empty($sortorder) && $sortorder!=0) {
a6600395 311 $this->_sortorder = $sortorder;
312 }
313 //if sortorder is not set, we set it as the ;ast position in the list
7a3b93c1 314 else if (!isset($this->_sortorder) || $this->_sortorder == 0 ) {
a6600395 315 $sql = "SELECT MAX(sortorder) FROM {repository}";
316 $this->_sortorder = 1 + $DB->get_field_sql($sql);
317 }
318
319 return $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$this->_typename));
320 }
321
322 /**
323 * Change order of the type with its adjacent upper or downer type
324 * (database fields are updated)
325 * Algorithm details:
326 * 1. retrieve all types in an array. This array is sorted by sortorder,
327 * and the array keys start from 0 to X (incremented by 1)
328 * 2. switch sortorder values of this type and its adjacent type
329 * @global object $DB
330 * @param string $move "up" or "down"
331 */
332 public function move_order($move) {
333 global $DB;
a6600395 334
dbc01944 335 $types = repository::get_types(); // retrieve all types
7a3b93c1 336
337 /// retrieve this type into the returned array
338 $i = 0;
339 while (!isset($indice) && $i<count($types)) {
340 if ($types[$i]->get_typename() == $this->_typename) {
a6600395 341 $indice = $i;
342 }
343 $i++;
344 }
345
7a3b93c1 346 /// retrieve adjacent indice
a6600395 347 switch ($move) {
348 case "up":
349 $adjacentindice = $indice - 1;
7a3b93c1 350 break;
a6600395 351 case "down":
352 $adjacentindice = $indice + 1;
7a3b93c1 353 break;
a6600395 354 default:
7a3b93c1 355 throw new repository_exception('movenotdefined', 'repository');
a6600395 356 }
357
358 //switch sortorder of this type and the adjacent type
359 //TODO: we could reset sortorder for all types. This is not as good in performance term, but
360 //that prevent from wrong behaviour on a screwed database. As performance are not important in this particular case
361 //it worth to change the algo.
7a3b93c1 362 if ($adjacentindice>=0 && !empty($types[$adjacentindice])) {
a6600395 363 $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$types[$adjacentindice]->get_typename()));
364 $this->update_sortorder($types[$adjacentindice]->get_sortorder());
365 }
366 }
367
368 /**
369 * 1. Switch the visibility OFF if it's ON, and ON if it's OFF.
370 * 2. Update the type
371 * @return <type>
372 */
7a3b93c1 373 public function switch_and_update_visibility() {
a6600395 374 $this->_visible = !$this->_visible;
375 return $this->update_visible();
376 }
377
378
379 /**
eb239694 380 * Delete a repository_type (general options are removed from config_plugin
381 * table, and all instances are deleted)
a6600395 382 * @global object $DB
383 * @return boolean
384 */
7a3b93c1 385 public function delete() {
a6600395 386 global $DB;
46dd6bb0 387
388 //delete all instances of this type
dbc01944 389 $instances = repository::get_instances(array(),null,false,$this->_typename);
7a3b93c1 390 foreach ($instances as $instance) {
46dd6bb0 391 $instance->delete();
392 }
393
eb239694 394 //delete all general options
7a3b93c1 395 foreach ($this->_options as $name => $value) {
eb239694 396 set_config($name, null, $this->_typename);
397 }
398
a6600395 399 return $DB->delete_records('repository', array('type' => $this->_typename));
400 }
401}
402
a6600395 403/**
8d419e59 404 * This is the base class of the repository class
405 *
406 * To use repository plugin, see:
407 * http://docs.moodle.org/en/Development:Repository_How_to_Create_Plugin
408 *
409 * class repository is an abstract class, some functions must be implemented in subclass.
410 *
411 * See an example: repository/boxnet/repository.class.php
412 *
413 * A few notes:
414 * // for ajax file picker, this will print a json string to tell file picker
415 * // how to build a login form
416 * $repo->print_login();
417 * // for ajax file picker, this will return a files list.
418 * $repo->get_listing();
419 * // this function will be used for non-javascript version.
420 * $repo->print_listing();
421 * // print a search box
422 * $repo->print_search();
423 *
424 * @package repository
425 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
a6600395 426 */
fca079c5 427abstract class repository {
efe018b4 428 // $disabled can be set to true to disable a plugin by force
429 // example: self::$disabled = true
430 public $disabled = false;
4a65c39a 431 public $id;
432 public $context;
433 public $options;
948c2860 434 public $readonly;
1cf56396 435
dbc01944 436 /**
437 * Return a type for a given type name.
438 * @global object $DB
439 * @param string $typename the type name
440 * @return integer
441 */
442 public static function get_type_by_typename($typename) {
443 global $DB;
444
445 if (!$record = $DB->get_record('repository',array('type' => $typename))) {
446 return false;
447 }
448
449 return new repository_type($typename, (array)get_config($typename), $record->visible, $record->sortorder);
450 }
451
452 /**
453 * Return a type for a given type id.
454 * @global object $DB
455 * @param string $typename the type name
456 * @return integer
457 */
458 public static function get_type_by_id($id) {
459 global $DB;
460
461 if (!$record = $DB->get_record('repository',array('id' => $id))) {
462 return false;
463 }
464
465 return new repository_type($record->type, (array)get_config($record->type), $record->visible, $record->sortorder);
466 }
467
468 /**
469 * Return all repository types ordered by sortorder
470 * first type in returnedarray[0], second type in returnedarray[1], ...
471 * @global object $DB
472 * @param boolean $visible can return types by visiblity, return all types if null
473 * @return array Repository types
474 */
475 public static function get_types($visible=null) {
476 global $DB;
477
478 $types = array();
479 $params = null;
480 if (!empty($visible)) {
481 $params = array('visible' => $visible);
482 }
483 if ($records = $DB->get_records('repository',$params,'sortorder')) {
484 foreach($records as $type) {
485 $types[] = new repository_type($type->type, (array)get_config($type->type), $type->visible, $type->sortorder);
486 }
487 }
488
489 return $types;
490 }
491
492 /**
493 * Check context
494 * @param int $ctx_id
495 * @return boolean
496 */
497 public static function check_context($ctx_id) {
498 global $USER;
499
500 $context = get_context_instance_by_id($ctx_id);
501 $level = $context->contextlevel;
502
503 if ($level == CONTEXT_COURSE) {
504 if (!has_capability('moodle/course:view', $context)) {
505 return false;
506 } else {
507 return true;
508 }
509 } else if ($level == CONTEXT_USER) {
510 $c = get_context_instance(CONTEXT_USER, $USER->id);
511 if ($c->id == $ctx_id) {
512 return true;
513 } else {
514 return false;
515 }
516 } else if ($level == CONTEXT_SYSTEM) {
517 // it is always ok in system level
518 return true;
519 }
520 return false;
521 }
522
523 /**
524 * Return all types that you a user can create/edit and which are also visible
525 * Note: Mostly used in order to know if at least one editable type can be set
526 * @param object $context the context for which we want the editable types
527 * @return array types
528 */
529 public static function get_editable_types($context = null) {
530
531 if (empty($context)) {
532 $context = get_system_context();
533 }
534
535 $types= repository::get_types(true);
536 $editabletypes = array();
537 foreach ($types as $type) {
538 $instanceoptionnames = repository::static_function($type->get_typename(), 'get_instance_option_names');
539 if (!empty($instanceoptionnames)) {
540 if ($type->get_contextvisibility($context->contextlevel)) {
541 $editabletypes[]=$type;
542 }
543 }
544 }
545 return $editabletypes;
546 }
547
548 /**
549 * Return repository instances
550 * @global object $DB
551 * @global object $CFG
552 * @global object $USER
553 * @param object $contexts contexts for which the instances are set
554 * @param integer $userid
555 * @param boolean $onlyvisible if visible == true, return visible instances only,
556 * otherwise, return all instances
557 * @param string $type a type name to retrieve
558 * @return array repository instances
559 */
c167aa26 560 public static function get_instances($contexts=array(), $userid = null, $onlyvisible = true, $type=null, $accepted_types = '*', $returnvalue = '*') {
dbc01944 561 global $DB, $CFG, $USER;
562
563 $params = array();
564 $sql = 'SELECT i.*, r.type AS repositorytype, r.sortorder, r.visible FROM {repository} r, {repository_instances} i WHERE ';
565 $sql .= 'i.typeid = r.id ';
566
567 if (!empty($userid) && is_numeric($userid)) {
568 $sql .= ' AND (i.userid = 0 or i.userid = ?)';
569 $params[] = $userid;
570 }
571
572 foreach ($contexts as $context) {
573 if (empty($firstcontext)) {
574 $firstcontext = true;
575 $sql .= ' AND ((i.contextid = ?)';
576 } else {
577 $sql .= ' OR (i.contextid = ?)';
578 }
579 $params[] = $context->id;
580 }
581
582 if (!empty($firstcontext)) {
583 $sql .=')';
584 }
585
586 if ($onlyvisible == true) {
587 $sql .= ' AND (r.visible = 1)';
588 }
589
590 if (isset($type)) {
591 $sql .= ' AND (r.type = ?)';
592 $params[] = $type;
593 }
594 $sql .= ' order by r.sortorder, i.name';
595
596 if (!$repos = $DB->get_records_sql($sql, $params)) {
597 $repos = array();
598 }
599
600 $ret = array();
c167aa26 601 $ft = new file_type_to_ext();
dbc01944 602 foreach ($repos as $repo) {
603 require_once($CFG->dirroot . '/repository/'. $repo->repositorytype.'/repository.class.php');
604 $options['visible'] = $repo->visible;
605 $options['name'] = $repo->name;
606 $options['type'] = $repo->repositorytype;
607 $options['typeid'] = $repo->typeid;
c167aa26 608 // tell instance what file types will be accepted by file picker
609 $options['accepted_types'] = $ft->get_file_ext($accepted_types);
dbc01944 610 $classname = 'repository_' . $repo->repositorytype;//
014c1ca0 611 $is_supported = true;
dbc01944 612
613 $repository = new $classname($repo->id, $repo->contextid, $options, $repo->readonly);
271bd2e0 614 if (empty($repository->super_called)) {
615 debugging('parent::__construct must be called by '.$repo->repositorytype.' plugin.');
616 } else {
617 if ($accepted_types !== '*' and $repository->supported_filetypes() !== '*') {
618 $accepted_types = $ft->get_file_ext($accepted_types);
619 $supported_filetypes = $ft->get_file_ext($repository->supported_filetypes());
620 $is_supported = false;
621 foreach ($supported_filetypes as $type) {
622 if (in_array($type, $accepted_types)) {
623 $is_supported = true;
624 }
014c1ca0 625 }
626 }
271bd2e0 627 if ($returnvalue !== '*' and $repository->supported_return_value() !== '*') {
628 $tmp = $repository->supported_return_value();
629 if ($tmp != $returnvalue) {
630 $is_supported = false;
631 }
014c1ca0 632 }
271bd2e0 633 if (!$onlyvisible || ($repository->is_visible() && !$repository->disabled)) {
634 // super_called will make sure the parent construct function is called
635 // by repository construct function
636 if ($is_supported) {
637 $ret[] = $repository;
638 }
014c1ca0 639 }
dbc01944 640 }
641 }
642 return $ret;
643 }
644
645 /**
646 * Get single repository instance
647 * @global object $DB
648 * @global object $CFG
649 * @param integer $id repository id
650 * @return object repository instance
651 */
652 public static function get_instance($id) {
653 global $DB, $CFG;
654 $sql = 'SELECT i.*, r.type AS repositorytype, r.visible FROM {repository} r, {repository_instances} i WHERE ';
655 $sql .= 'i.typeid = r.id AND ';
656 $sql .= 'i.id = '.$id;
657
658 if(!$instance = $DB->get_record_sql($sql)) {
659 return false;
660 }
661 require_once($CFG->dirroot . '/repository/'. $instance->repositorytype
662 . '/repository.class.php');
663 $classname = 'repository_' . $instance->repositorytype;
664 $options['typeid'] = $instance->typeid;
665 $options['type'] = $instance->repositorytype;
666 $options['name'] = $instance->name;
271bd2e0 667 $obj = new $classname($instance->id, $instance->contextid, $options, $instance->readonly);
668 if (empty($obj->super_called)) {
669 debugging('parent::__construct must be called by '.$classname.' plugin.');
670 }
671 return $obj;
dbc01944 672 }
673
674 /**
675 * call a static function
676 * @global <type> $CFG
677 * @param <type> $plugin
678 * @param <type> $function
679 * @param type $nocallablereturnvalue default value if function not found
680 * it's mostly used when you don't want to display an error but
681 * return a boolean
682 * @return <type>
683 */
684 public static function static_function($plugin, $function) {
685 global $CFG;
686
687 //check that the plugin exists
688 $typedirectory = $CFG->dirroot . '/repository/'. $plugin . '/repository.class.php';
689 if (!file_exists($typedirectory)) {
f1cfe56e 690 //throw new repository_exception('invalidplugin', 'repository');
691 return false;
dbc01944 692 }
693
694 $pname = null;
695 if (is_object($plugin) || is_array($plugin)) {
696 $plugin = (object)$plugin;
697 $pname = $plugin->name;
698 } else {
699 $pname = $plugin;
700 }
701
702 $args = func_get_args();
703 if (count($args) <= 2) {
704 $args = array();
705 }
706 else {
707 array_shift($args);
708 array_shift($args);
709 }
710
711 require_once($typedirectory);
712 return call_user_func_array(array('repository_' . $plugin, $function), $args);
713 }
714
715 /**
716 * Move file from download folder to file pool using FILE API
717 * @global object $DB
718 * @global object $CFG
719 * @global object $USER
720 * @param string $path file path in download folder
721 * @param string $name file name
722 * @param integer $itemid item id to identify a file in filepool
723 * @param string $filearea file area
724 * @return array information of file in file pool
725 */
726 public static function move_to_filepool($path, $name, $itemid, $filearea = 'user_draft') {
727 global $DB, $CFG, $USER;
728 $context = get_context_instance(CONTEXT_USER, $USER->id);
729 $now = time();
730 $entry = new object();
731 $entry->filearea = $filearea;
732 $entry->contextid = $context->id;
733 $entry->filename = $name;
b423b4af 734 //$entry->filepath = '/'.uniqid().'/';
735 $entry->filepath = '/';
dbc01944 736 $entry->timecreated = $now;
737 $entry->timemodified = $now;
b423b4af 738 $entry->userid = $USER->id;
739 $entry->mimetype = mimeinfo('type', $path);
dbc01944 740 if(is_numeric($itemid)) {
741 $entry->itemid = $itemid;
742 } else {
743 $entry->itemid = 0;
744 }
dbc01944 745 $fs = get_file_storage();
746 $browser = get_file_browser();
b423b4af 747 if ($existingfile = $fs->get_file($context->id, $filearea, $itemid, $path, $name)) {
748 $existingfile->delete();
749 }
dbc01944 750 if ($file = $fs->create_file_from_pathname($entry, $path)) {
751 $delete = unlink($path);
752 $ret = $browser->get_file_info($context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
753 if(!empty($ret)) {
b423b4af 754 return array('url'=>$ret->get_url(),
755 'id'=>$file->get_itemid(),
756 'file'=>$file->get_filename(),
757 'icon'=>$CFG->pixpath.'/f/'.mimeinfo('icon', $path)
758 );
dbc01944 759 } else {
760 return null;
761 }
762 } else {
763 return null;
764 }
765 }
766
767 public static function download_btn($repo_id, $ctx_id, $sesskey, $title, $src, $returnurl = '') {
768 global $CFG;
769 if (empty($returnurl)) {
770 $returnurl = get_referer();
771 }
772 $str = '<form action="'.$CFG->httpswwwroot.'/repository/ws.php">';
773 $str .= ' <input type="hidden" name="sesskey" value="'.$sesskey.'" />';
774 $str .= ' <input type="hidden" name="ctx_id" value="'.$ctx_id.'" />';
775 $str .= ' <input type="hidden" name="repo_id" value="'.$repo_id.'" />';
776 $str .= ' <input type="hidden" name="file" value="'.$src.'" />';
777 $str .= ' <input type="hidden" name="action" value="download" />';
778 $str .= ' <input type="hidden" name="returnurl" value="'.$returnurl.'" />';
779 $str .= ' <input type="text" name="title" value="'.$title.'" />';
780 $str .= ' <input type="submit" value="Select it!" />';
781 $str .= '</form>';
782 return $str;
783 }
784
785 /**
786 * Save file to local filesystem pool
787 * @param string $elname name of element
788 * @param string $filearea
789 * @param string $filepath
790 * @param string $filename - use specified filename, if not specified name of uploaded file used
791 * @param bool $override override file if exists
792 * @return mixed stored_file object or false if error; may throw exception if duplicate found
793 */
b423b4af 794 public static function store_to_filepool($elname, $filearea='user_draft', $filepath='/', $itemid='', $filename = '', $override = false) {
dbc01944 795 global $USER;
796 if (!isset($_FILES[$elname])) {
797 return false;
798 }
799
800 if (!$filename) {
801 $filename = $_FILES[$elname]['name'];
802 }
803 $context = get_context_instance(CONTEXT_USER, $USER->id);
b423b4af 804 if (empty($itemid)) {
805 $itemid = (int)substr(hexdec(uniqid()), 0, 9)+rand(1,100);
806 }
dbc01944 807 $fs = get_file_storage();
808 $browser = get_file_browser();
809
810 if ($file = $fs->get_file($context->id, $filearea, $itemid, $filepath, $filename)) {
811 if ($override) {
812 $file->delete();
813 } else {
814 return false;
815 }
816 }
817
818 $file_record = new object();
819 $file_record->contextid = $context->id;
820 $file_record->filearea = $filearea;
821 $file_record->itemid = $itemid;
822 $file_record->filepath = $filepath;
823 $file_record->filename = $filename;
824 $file_record->userid = $USER->id;
825
826 $file = $fs->create_file_from_pathname($file_record, $_FILES[$elname]['tmp_name']);
827 $info = $browser->get_file_info($context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
828 $ret = array('url'=>$info->get_url(),'id'=>$itemid, 'file'=>$file->get_filename());
829 return $ret;
830 }
831
832 /**
833 * Return the user arborescence in a format to be returned by the function get_listing
834 * @global <type> $CFG
835 * @param <type> $search
836 * @return <type>
837 */
838 public static function get_user_file_tree($search = ""){
839 global $CFG;
840 $ret = array();
841 $ret['nologin'] = true;
842 $ret['manage'] = $CFG->wwwroot .'/files/index.php'; // temporary
843 $browser = get_file_browser();
844 $itemid = null;
845 $filename = null;
846 $filearea = null;
847 $path = '/';
848 $ret['dynload'] = false;
849
850 if ($fileinfo = $browser->get_file_info(get_system_context(), $filearea, $itemid, $path, $filename)) {
851
852 $ret['path'] = array();
853 $params = $fileinfo->get_params();
854 $filearea = $params['filearea'];
855 $ret['path'][] = repository::encode_path($filearea, $path, $fileinfo->get_visible_name());
856 if ($fileinfo->is_directory()) {
857 $level = $fileinfo->get_parent();
858 while ($level) {
859 $params = $level->get_params();
860 $ret['path'][] = repository::encode_path($params['filearea'], $params['filepath'], $level->get_visible_name());
861 $level = $level->get_parent();
862 }
863 }
864 $filecount = repository::build_tree($fileinfo, $search, $ret['dynload'], $ret['list']);
865 $ret['path'] = array_reverse($ret['path']);
866 }
867
868 if (empty($ret['list'])) {
869 //exit(mnet_server_fault(9016, get_string('emptyfilelist', 'repository_local')));
870 throw new Exception('emptyfilelist');
871 } else {
872 return $ret;
873 }
874
875 }
876
877 /**
878 *
879 * @param <type> $filearea
880 * @param <type> $path
881 * @param <type> $visiblename
882 * @return <type>
883 */
884 public static function encode_path($filearea, $path, $visiblename) {
885 return array('path'=>serialize(array($filearea, $path)), 'name'=>$visiblename);
886 }
887
888 /**
889 * Builds a tree of files This function is
890 * then called recursively.
891 *
892 * @param $fileinfo an object returned by file_browser::get_file_info()
893 * @param $search searched string
894 * @param $dynamicmode bool no recursive call is done when in dynamic mode
895 * @param $list - the array containing the files under the passed $fileinfo
896 * @returns int the number of files found
897 *
898 * todo: take $search into account, and respect a threshold for dynamic loading
899 */
900 public static function build_tree($fileinfo, $search, $dynamicmode, &$list) {
901 global $CFG;
902
903 $filecount = 0;
904 $children = $fileinfo->get_children();
905
906 foreach ($children as $child) {
907 $filename = $child->get_visible_name();
908 $filesize = $child->get_filesize();
909 $filesize = $filesize ? display_size($filesize) : '';
910 $filedate = $child->get_timemodified();
911 $filedate = $filedate ? userdate($filedate) : '';
912 $filetype = $child->get_mimetype();
913
914 if ($child->is_directory()) {
915 $path = array();
916 $level = $child->get_parent();
917 while ($level) {
918 $params = $level->get_params();
919 $path[] = repository::encode_path($params['filearea'], $params['filepath'], $level->get_visible_name());
920 $level = $level->get_parent();
921 }
922
923 $tmp = array(
924 'title' => $child->get_visible_name(),
925 'size' => 0,
926 'date' => $filedate,
927 'path' => array_reverse($path),
928 'thumbnail' => $CFG->pixpath .'/f/folder.gif'
929 );
930
931 //if ($dynamicmode && $child->is_writable()) {
932 // $tmp['children'] = array();
933 //} else {
934 // if folder name matches search, we send back all files contained.
935 $_search = $search;
936 if ($search && stristr($tmp['title'], $search) !== false) {
937 $_search = false;
938 }
939 $tmp['children'] = array();
940 $_filecount = repository::build_tree($child, $_search, $dynamicmode, $tmp['children']);
941 if ($search && $_filecount) {
942 $tmp['expanded'] = 1;
943 }
944
945 //}
946
947 //Uncomment this following line if you wanna display all directory ()even empty
948 //if (!$search || $_filecount || (stristr($tmp['title'], $search) !== false)) {
949 if ($_filecount) {
950 $filecount += $_filecount;
951 $list[] = $tmp;
952 }
953
954 } else { // not a directory
955 // skip the file, if we're in search mode and it's not a match
956 if ($search && (stristr($filename, $search) === false)) {
957 continue;
958 }
959 $params = $child->get_params();
960 $source = serialize(array($params['contextid'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']));
961 $list[] = array(
962 'title' => $filename,
963 'size' => $filesize,
964 'date' => $filedate,
965 //'source' => $child->get_url(),
966 'source' => base64_encode($source),
967 'thumbnail' => $CFG->pixpath .'/f/'. mimeinfo_from_type("icon", $filetype)
968 );
969 $filecount++;
970 }
971 }
972
973 return $filecount;
974 }
975
976
977 /**
978 * Display a repository instance list (with edit/delete/create links)
979 * @global object $CFG
980 * @global object $USER
981 * @param object $context the context for which we display the instance
982 * @param string $typename if set, we display only one type of instance
983 */
984 public static function display_instances_list($context, $typename = null) {
985 global $CFG, $USER;
986
987 $output = print_box_start('generalbox','',true);
988 //if the context is SYSTEM, so we call it from administration page
989 $admin = ($context->id == SYSCONTEXTID) ? true : false;
990 if ($admin) {
d1aa1e48 991 $baseurl = "$CFG->httpswwwroot/$CFG->admin/repositoryinstance.php?sesskey=" . sesskey();
dbc01944 992 $output .= "<div ><h2 style='text-align: center'>" . get_string('siteinstances', 'repository') . " ";
993 $output .= "</h2></div>";
994 } else {
995 $baseurl = $CFG->httpswwwroot . '/repository/manage_instances.php?contextid=' . $context->id . '&amp;sesskey=' . sesskey();
996 }
997
998 $namestr = get_string('name');
999 $pluginstr = get_string('plugin', 'repository');
1000 $settingsstr = get_string('settings');
1001 $deletestr = get_string('delete');
1002 $updown = get_string('updown', 'repository');
1003 //retrieve list of instances. In administration context we want to display all
1004 //instances of a type, even if this type is not visible. In course/user context we
1005 //want to display only visible instances, but for every type types. The repository_get_instances()
1006 //third parameter displays only visible type.
1007 $instances = repository::get_instances(array($context),null,!$admin,$typename);
1008 $instancesnumber = count($instances);
1009 $alreadyplugins = array();
1010
1011 $table = new StdClass;
1012 $table->head = array($namestr, $pluginstr, $deletestr, $settingsstr);
1013 $table->align = array('left', 'left', 'center','center');
1014 $table->data = array();
1015
1016 $updowncount = 1;
1017
1018 foreach ($instances as $i) {
1019 $settings = '';
1020 $delete = '';
1021 $settings .= '<a href="' . $baseurl . '&amp;type='.$typename.'&amp;edit=' . $i->id . '">' . $settingsstr . '</a>' . "\n";
1022 if (!$i->readonly) {
1023 $delete .= '<a href="' . $baseurl . '&amp;type='.$typename.'&amp;delete=' . $i->id . '">' . $deletestr . '</a>' . "\n";
1024 }
1025
1026 $type = repository::get_type_by_id($i->typeid);
1027 $table->data[] = array($i->name, $type->get_readablename(), $delete, $settings);
1028
1029 //display a grey row if the type is defined as not visible
1030 if (isset($type) && !$type->get_visible()) {
1031 $table->rowclass[] = 'dimmed_text';
1032 } else {
1033 $table->rowclass[] = '';
1034 }
1035
1036 if (!in_array($i->name, $alreadyplugins)) {
1037 $alreadyplugins[] = $i->name;
1038 }
1039 }
1040 $output .= print_table($table, true);
1041 $instancehtml = '<div>';
1042 $addable = 0;
1043
1044 //if no type is set, we can create all type of instance
1045 if (!$typename) {
1046 $instancehtml .= '<h3>';
1047 $instancehtml .= get_string('createrepository', 'repository');
1048 $instancehtml .= '</h3><ul>';
1049 $types = repository::get_editable_types($context);
1050 foreach ($types as $type) {
1051 if (!empty($type) && $type->get_visible()) {
1052 $instanceoptionnames = repository::static_function($type->get_typename(), 'get_instance_option_names');
1053 if (!empty($instanceoptionnames)) {
1054 $instancehtml .= '<li><a href="'.$baseurl.'&amp;new='.$type->get_typename().'">'.get_string('create', 'repository')
1055 .' "'.get_string('repositoryname', 'repository_'.$type->get_typename()).'" '
1056 .get_string('instance', 'repository').'</a></li>';
1057 $addable++;
1058 }
1059 }
1060 }
1061 $instancehtml .= '</ul>';
1062
1063 } else {
1064 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
1065 if (!empty($instanceoptionnames)) { //create a unique type of instance
1066 $addable = 1;
1067 $instancehtml .= "<form action='".$baseurl."&amp;new=".$typename."' method='post'>
1068 <p style='text-align:center'><input type='submit' value='".get_string('createinstance', 'repository')."'/></p>
1069 </form>";
1070 }
1071 }
1072
1073 if ($addable) {
1074 $instancehtml .= '</div>';
1075 $output .= $instancehtml;
1076 }
1077
1078 $output .= print_box_end(true);
1079
1080 //print the list + creation links
1081 print($output);
1082 }
1cf56396 1083 /**
3f24ea1e 1084 * 1. Initialize context and options
1085 * 2. Accept necessary parameters
1086 *
8dcd5deb 1087 * @param integer $repositoryid
1088 * @param integer $contextid
1089 * @param array $options
1cf56396 1090 */
948c2860 1091 public function __construct($repositoryid, $contextid = SITEID, $options = array(), $readonly = 0) {
4a65c39a 1092 $this->id = $repositoryid;
1093 $this->context = get_context_instance_by_id($contextid);
948c2860 1094 $this->readonly = $readonly;
4a65c39a 1095 $this->options = array();
fca079c5 1096 if (is_array($options)) {
4a65c39a 1097 $options = array_merge($this->get_option(), $options);
1098 } else {
1099 $options = $this->get_option();
1100 }
b1339e98 1101 $this->options = array();
4a65c39a 1102 foreach ($options as $n => $v) {
1103 $this->options[$n] = $v;
82669dc4 1104 }
d31af46a 1105 $this->name = $this->get_name();
271bd2e0 1106 $this->super_called = true;
1cf56396 1107 }
1108
8dcd5deb 1109 /**
3f24ea1e 1110 * set options for repository instance
1111 *
8dcd5deb 1112 * @param string $name
3f24ea1e 1113 * @param mixed $value
8dcd5deb 1114 */
fca079c5 1115 public function __set($name, $value) {
82669dc4 1116 $this->options[$name] = $value;
1117 }
1cf56396 1118
8dcd5deb 1119 /**
3f24ea1e 1120 * get options for repository instance
1121 *
1122 * @param string $name
1123 * @return mixed
8dcd5deb 1124 */
fca079c5 1125 public function __get($name) {
7a3b93c1 1126 if (array_key_exists($name, $this->options)) {
82669dc4 1127 return $this->options[$name];
1128 }
1129 trigger_error('Undefined property: '.$name, E_USER_NOTICE);
1130 return null;
1131 }
1cf56396 1132
8dcd5deb 1133 /**
3f24ea1e 1134 * test option name
1135 *
1136 * @param string name
8dcd5deb 1137 */
fca079c5 1138 public function __isset($name) {
82669dc4 1139 return isset($this->options[$name]);
1cf56396 1140 }
1141
8dcd5deb 1142 /**
3f24ea1e 1143 * Return the name of the repository class
8dcd5deb 1144 * @return <type>
1145 */
fca079c5 1146 public function __toString() {
82669dc4 1147 return 'Repository class: '.__CLASS__;
1148 }
4a65c39a 1149
c425472d 1150 /**
66dc47bc 1151 * Download a file, this function can be overridden by
1152 * subclass.
3f24ea1e 1153 *
8dcd5deb 1154 * @global object $CFG
c425472d 1155 * @param string $url the url of file
1156 * @param string $file save location
3f24ea1e 1157 * @return string the location of the file
b1339e98 1158 * @see curl package
c425472d 1159 */
bb2c046d 1160 public function get_file($url, $file = '') {
c425472d 1161 global $CFG;
c9260130 1162 if (!file_exists($CFG->dataroot.'/temp/download')) {
1163 mkdir($CFG->dataroot.'/temp/download/', 0777, true);
1e28c767 1164 }
7a3b93c1 1165 if (is_dir($CFG->dataroot.'/temp/download')) {
c9260130 1166 $dir = $CFG->dataroot.'/temp/download/';
1e28c767 1167 }
7a3b93c1 1168 if (empty($file)) {
84df43de 1169 $file = uniqid('repo').'_'.time().'.tmp';
1170 }
7a3b93c1 1171 if (file_exists($dir.$file)) {
84df43de 1172 $file = uniqid('m').$file;
c425472d 1173 }
84df43de 1174 $fp = fopen($dir.$file, 'w');
1175 $c = new curl;
1176 $c->download(array(
7a3b93c1 1177 array('url'=>$url, 'file'=>$fp)
1178 ));
84df43de 1179 return $dir.$file;
82669dc4 1180 }
1cf56396 1181
1cf56396 1182 /**
66dc47bc 1183 * Print a list or return formatted string, can be overridden by subclass
82669dc4 1184 *
1185 * @param string $list
3f24ea1e 1186 * @param boolean $print false, return html, otherwise, print it directly
8dcd5deb 1187 * @return <type>
1188 */
fca079c5 1189 public function print_listing($listing = array(), $print=true) {
7a3b93c1 1190 if (empty($listing)) {
0f59046f 1191 $listing = $this->get_listing();
1192 }
fca079c5 1193 if (empty($listing)) {
1194 $str = '';
1195 } else {
1196 $count = 0;
1197 $str = '<table>';
7a3b93c1 1198 foreach ($listing as $v) {
fca079c5 1199 $str .= '<tr id="entry_'.$count.'">';
1200 $str .= '<td><input type="checkbox" /></td>';
1201 $str .= '<td>'.$v['name'].'</td>';
1202 $str .= '<td>'.$v['size'].'</td>';
1203 $str .= '<td>'.$v['date'].'</td>';
1204 $str .= '</tr>';
1205 $count++;
1206 }
d1fe3452 1207 $str .= '</table>';
82669dc4 1208 }
7a3b93c1 1209 if ($print) {
82669dc4 1210 echo $str;
1211 return null;
1212 } else {
1213 return $str;
1214 }
1cf56396 1215 }
eb239694 1216
f48fb4d6 1217 /**
1218 * Return is the instance is visible
1219 * (is the type visible ? is the context enable ?)
1220 * @return boolean
1221 */
dbc01944 1222 public function is_visible() {
1223 $type = repository::get_type_by_id($this->typeid);
1224 $instanceoptions = repository::static_function($type->get_typename(), 'get_instance_option_names');
f48fb4d6 1225
1226 if ($type->get_visible()) {
1227 //if the instance is unique so it's visible, otherwise check if the instance has a enabled context
1228 if (empty($instanceoptions) || $type->get_contextvisibility($this->context->contextlevel)) {
1229 return true;
1230 }
1231 }
1232
1233 return false;
1234 }
1235
eb239694 1236 /**
66dc47bc 1237 * Return the name of this instance, can be overridden.
eb239694 1238 * @global <type> $DB
1239 * @return <type>
1240 */
7a3b93c1 1241 public function get_name() {
d31af46a 1242 global $DB;
1243 // We always verify instance id from database,
1244 // so we always know repository name before init
1245 // a repository, so we don't enquery repository
1246 // name from database again here.
1247 if (isset($this->options['name'])) {
1248 return $this->options['name'];
1249 } else {
1250 if ( $repo = $DB->get_record('repository_instances', array('id'=>$this->id)) ) {
1251 return $repo->name;
1252 } else {
1253 return '';
1254 }
1255 }
1256 }
aa754fe3 1257
b318bb6d 1258 /**
1259 * what kind of files will be in this repository?
1260 * @return array return '*' means this repository support any files, otherwise
1261 * return mimetypes of files, it can be an array
1262 */
014c1ca0 1263 public function supported_filetypes() {
b318bb6d 1264 // return array('text/plain', 'image/gif');
1265 return '*';
1266 }
1267
1268 /**
1269 * does it return a file url or a item_id
1270 * @return string
1271 */
1272 public function supported_return_value() {
1273 // return 'link';
1274 // return 'ref_id';
014c1ca0 1275 return 'ref_id';
b318bb6d 1276 }
1277
82669dc4 1278 /**
3f24ea1e 1279 * Provide repository instance information for Ajax
8dcd5deb 1280 * @global object $CFG
4a65c39a 1281 * @return object
82669dc4 1282 */
b1339e98 1283 final public function ajax_info() {
4a65c39a 1284 global $CFG;
fc11edbf 1285 $ft = new file_type_to_ext;
4a65c39a 1286 $repo = new stdclass;
27051e43 1287 $repo->id = $this->id;
d31af46a 1288 $repo->name = $this->get_name();
4a65c39a 1289 $repo->type = $this->options['type'];
6fe8b022 1290 $repo->icon = $CFG->httpswwwroot.'/repository/'.$repo->type.'/icon.png';
c167aa26 1291 $repo->supported_types = $ft->get_file_ext($this->supported_filetypes());
1292 $repo->accepted_types = $this->accepted_types;
4a65c39a 1293 return $repo;
1294 }
1cf56396 1295
b1339e98 1296 /**
1297 * Create an instance for this plug-in
8dcd5deb 1298 * @global object $CFG
1299 * @global object $DB
1300 * @param string $type the type of the repository
1301 * @param integer $userid the user id
1302 * @param object $context the context
1303 * @param array $params the options for this instance
1304 * @return <type>
b1339e98 1305 */
948c2860 1306 final public static function create($type, $userid, $context, $params, $readonly=0) {
b1339e98 1307 global $CFG, $DB;
1308 $params = (array)$params;
1309 require_once($CFG->dirroot . '/repository/'. $type . '/repository.class.php');
1310 $classname = 'repository_' . $type;
3023078f 1311 if ($repo = $DB->get_record('repository', array('type'=>$type))) {
1312 $record = new stdclass;
1313 $record->name = $params['name'];
1314 $record->typeid = $repo->id;
1315 $record->timecreated = time();
1316 $record->timemodified = time();
1317 $record->contextid = $context->id;
948c2860 1318 $record->readonly = $readonly;
3023078f 1319 $record->userid = $userid;
1320 $id = $DB->insert_record('repository_instances', $record);
0a6221f9 1321 $options = array();
edb50637 1322 $configs = call_user_func($classname . '::get_instance_option_names');
1e08b5cf 1323 if (!empty($configs)) {
1324 foreach ($configs as $config) {
1325 $options[$config] = $params[$config];
1326 }
3023078f 1327 }
3a01a46a 1328
3023078f 1329 if (!empty($id)) {
1330 unset($options['name']);
dbc01944 1331 $instance = repository::get_instance($id);
3023078f 1332 $instance->set_option($options);
1333 return $id;
1334 } else {
1335 return null;
b1339e98 1336 }
b1339e98 1337 } else {
1338 return null;
1339 }
1340 }
8dcd5deb 1341
82669dc4 1342 /**
4a65c39a 1343 * delete a repository instance
8dcd5deb 1344 * @global object $DB
1345 * @return <type>
82669dc4 1346 */
7a3b93c1 1347 final public function delete() {
4a65c39a 1348 global $DB;
122defc5 1349 $DB->delete_records('repository_instances', array('id'=>$this->id));
4a65c39a 1350 return true;
1351 }
8dcd5deb 1352
4a65c39a 1353 /**
1354 * Hide/Show a repository
8dcd5deb 1355 * @global object $DB
1356 * @param string $hide
1357 * @return <type>
4a65c39a 1358 */
7a3b93c1 1359 final public function hide($hide = 'toggle') {
4a65c39a 1360 global $DB;
1361 if ($entry = $DB->get_record('repository', array('id'=>$this->id))) {
1362 if ($hide === 'toggle' ) {
1363 if (!empty($entry->visible)) {
1364 $entry->visible = 0;
1365 } else {
1366 $entry->visible = 1;
1367 }
1368 } else {
1369 if (!empty($hide)) {
1370 $entry->visible = 0;
1371 } else {
1372 $entry->visible = 1;
1373 }
1374 }
1375 return $DB->update_record('repository', $entry);
1376 }
1377 return false;
1378 }
1cf56396 1379
1380 /**
82669dc4 1381 * Cache login details for repositories
8dcd5deb 1382 * @global object $DB
82669dc4 1383 * @param string $username
1384 * @param string $password
8dcd5deb 1385 * @param integer $userid The id of specific user
1386 * @return integer Id of the record
1cf56396 1387 */
b6558c3b 1388 public function store_login($username = '', $password = '', $userid = 1) {
fca079c5 1389 global $DB;
1390
1391 $repository = new stdclass;
4a65c39a 1392 if (!empty($this->id)) {
1393 $repository->id = $this->id;
19add4c0 1394 } else {
1395 $repository->userid = $userid;
1396 $repository->repositorytype = $this->type;
b6558c3b 1397 $repository->contextid = $this->context->id;
19add4c0 1398 }
fca079c5 1399 if ($entry = $DB->get_record('repository', $repository)) {
1400 $repository->id = $entry->id;
1401 $repository->username = $username;
1402 $repository->password = $password;
1403 return $DB->update_record('repository', $repository);
1404 } else {
1405 $repository->username = $username;
1406 $repository->password = $password;
1407 return $DB->insert_record('repository', $repository);
1408 }
1cf56396 1409 }
1410
1cf56396 1411 /**
4a65c39a 1412 * Save settings for repository instance
8dcd5deb 1413 * $repo->set_option(array('api_key'=>'f2188bde132', 'name'=>'dongsheng'));
1414 * @global object $DB
1415 * @param array $options settings
4a65c39a 1416 * @return int Id of the record
1cf56396 1417 */
7a3b93c1 1418 public function set_option($options = array()) {
4a65c39a 1419 global $DB;
7a3b93c1 1420
122defc5 1421 if (!empty($options['name'])) {
1422 $r = new object();
1423 $r->id = $this->id;
1424 $r->name = $options['name'];
1425 $DB->update_record('repository_instances', $r);
1426 unset($options['name']);
1427 }
e53a97d1 1428 $result = true;
27051e43 1429 foreach ($options as $name=>$value) {
1430 if ($id = $DB->get_field('repository_instance_config', 'id', array('name'=>$name, 'instanceid'=>$this->id))) {
1431 if ($value===null) {
e53a97d1 1432 $result = $result && $DB->delete_records('repository_instance_config', array('name'=>$name, 'instanceid'=>$this->id));
27051e43 1433 } else {
e53a97d1 1434 $result = $result && $DB->set_field('repository_instance_config', 'value', $value, array('id'=>$id));
27051e43 1435 }
1436 } else {
1437 if ($value===null) {
1438 return true;
1439 }
1440 $config = new object();
1441 $config->instanceid = $this->id;
1442 $config->name = $name;
1443 $config->value = $value;
e53a97d1 1444 $result = $result && $DB->insert_record('repository_instance_config', $config);
27051e43 1445 }
4a65c39a 1446 }
e53a97d1 1447 return $result;
1cf56396 1448 }
1cf56396 1449
4a65c39a 1450 /**
1451 * Get settings for repository instance
8dcd5deb 1452 * @global object $DB
1453 * @param <type> $config
4a65c39a 1454 * @return array Settings
1455 */
7a3b93c1 1456 public function get_option($config = '') {
4a65c39a 1457 global $DB;
27051e43 1458 $entries = $DB->get_records('repository_instance_config', array('instanceid'=>$this->id));
1459 $ret = array();
1460 if (empty($entries)) {
1461 return $ret;
4a65c39a 1462 }
7a3b93c1 1463 foreach($entries as $entry) {
27051e43 1464 $ret[$entry->name] = $entry->value;
4a65c39a 1465 }
1466 if (!empty($config)) {
1467 return $ret[$config];
1468 } else {
1469 return $ret;
1470 }
1471 }
bf1fccf0 1472
c167aa26 1473 public function filter(&$value) {
1474 $pass = false;
1475 $accepted_types = optional_param('accepted_types', '', PARAM_RAW);
1476 $ft = new file_type_to_ext;
1477 $ext = $ft->get_file_ext($this->supported_filetypes());
1478 if (isset($value->children)) {
1479 $pass = true;
537d44e8 1480 if (!empty($value->children)) {
1481 $value->children = array_filter($value->children, array($this, 'filter'));
1482 }
c167aa26 1483 } else {
0c6e9212 1484 if ($accepted_types == '*' || in_array('*', $accepted_types)) {
c167aa26 1485 $pass = true;
1486 } elseif (is_array($accepted_types)) {
1487 foreach ($accepted_types as $type) {
1488 if (preg_match('#'.$type.'$#', $value['title'])) {
1489 $pass = true;
1490 }
1491 }
1492 }
1493 }
1494 return $pass;
1495 }
1496
4a65c39a 1497 /**
1498 * Given a path, and perhaps a search, get a list of files.
1499 *
e6be3a69 1500 * The format of the returned array must be:
f7639c37 1501 * array(
f6812a21 1502 * 'path' => (string) path for the current folder
f7639c37 1503 * 'dynload' => (bool) use dynamic loading,
f6812a21 1504 * 'manage' => (string) link to file manager,
f7639c37 1505 * 'nologin' => (bool) requires login,
d31af46a 1506 * 'nosearch' => (bool) no search link,
f7639c37 1507 * 'upload' => array( // upload manager
1508 * 'name' => (string) label of the form element,
1509 * 'id' => (string) id of the form element
1510 * ),
1511 * 'list' => array(
1512 * array( // file
1513 * 'title' => (string) file name,
3c9e53c0 1514 * 'date' => (string) file last modification time, usually userdate(...),
f6812a21 1515 * 'size' => (int) file size,
3c9e53c0 1516 * 'thumbnail' => (string) url to thumbnail for the file,
336bb44b 1517 * 'source' => plugin-dependent unique path to the file (id, url, path, etc.),
1518 * 'url'=> the accessible url of file
f7639c37 1519 * ),
3c9e53c0 1520 * array( // folder - same as file, but no 'source'.
f7639c37 1521 * 'title' => (string) folder name,
f6812a21 1522 * 'path' => (string) path to this folder
3c9e53c0 1523 * 'date' => (string) folder last modification time, usually userdate(...),
f6812a21 1524 * 'size' => 0,
3c9e53c0 1525 * 'thumbnail' => (string) url to thumbnail for the folder,
1526 * 'children' => array( // an empty folder needs to have 'children' defined, but empty.
1527 * // content (files and folders)
f7639c37 1528 * )
e6be3a69 1529 * ),
f7639c37 1530 * )
1531 * )
1532 *
4a65c39a 1533 * @param string $parent The parent path, this parameter can
1534 * a folder name, or a identification of folder
4a65c39a 1535 * @return array the list of files, including meta infomation
1536 */
353d5cf3 1537 abstract public function get_listing($parent = '/');
1cf56396 1538
fbd508b4 1539 /**
353d5cf3 1540 * Search files in repository
1541 * When doing global search, $search_text will be used as
dbc01944 1542 * keyword.
353d5cf3 1543 *
fbd508b4 1544 * @return mixed, see get_listing()
1545 */
353d5cf3 1546 public function search($search_text) {
1547 $list = array();
1548 $list['list'] = array();
1549 return false;
fbd508b4 1550 }
1551
d68c527f 1552 /**
1553 * Logout from repository instance
1554 * By default, this function will return a login form
1555 *
1556 * @return string
1557 */
1558 public function logout(){
1559 return $this->print_login();
1560 }
1561
1562 /**
1563 * To check whether the user is logged in.
1564 *
1565 * @return boolean
1566 */
1567 public function check_login(){
1568 return true;
1569 }
1570
1cf56396 1571
4a65c39a 1572 /**
1573 * Show the login screen, if required
1574 * This is an abstract function, it must be overriden.
4a65c39a 1575 */
1d66f2b2 1576 public function print_login(){
1577 return $this->get_listing();
1578 }
1cf56396 1579
4a65c39a 1580 /**
1581 * Show the search screen, if required
4a65c39a 1582 * @return null
1583 */
2b9feb5f 1584 public function print_search() {
1585 echo '<input type="hidden" name="repo_id" value="'.$this->id.'" />';
1586 echo '<input type="hidden" name="ctx_id" value="'.$this->context->id.'" />';
1587 echo '<input type="hidden" name="seekey" value="'.sesskey().'" />';
8d419e59 1588 echo '<label>'.get_string('keyword', 'repository').': </label><br/><input name="s" value="" /><br/>';
2b9feb5f 1589 return true;
1590 }
4a65c39a 1591
455860ce 1592 /**
1593 * is it possible to do glboal search?
1594 * @return boolean
1595 */
7a3b93c1 1596 public function global_search() {
455860ce 1597 return false;
1598 }
1599
8dcd5deb 1600 /**
a6600395 1601 * Defines operations that happen occasionally on cron
8dcd5deb 1602 * @return <type>
1603 */
a6600395 1604 public function cron() {
1605 return true;
1606 }
1607
7892948d 1608 /**
83a018ed 1609 * function which is run when the type is created (moodle administrator add the plugin)
3e0794ed 1610 * @return boolean success or fail?
7892948d 1611 */
f1cfe56e 1612 public static function plugin_init() {
3e0794ed 1613 return true;
7892948d 1614 }
1615
a6600395 1616 /**
06e65e1e 1617 * Edit/Create Admin Settings Moodle form
1618 * @param object $ Moodle form (passed by reference)
a6600395 1619 */
daff8f50 1620 public function type_config_form(&$mform) {
4a65c39a 1621 }
dbc01944 1622
06e65e1e 1623 /**
1624 * Edit/Create Instance Settings Moodle form
1625 * @param object $ Moodle form (passed by reference)
a6600395 1626 */
06e65e1e 1627 public function instance_config_form(&$mform) {
a6600395 1628 }
4a65c39a 1629
a6600395 1630 /**
1631 * Return names of the general options
1632 * By default: no general option name
1633 * @return array
1634 */
1b79955a 1635 public static function get_type_option_names() {
a6600395 1636 return array();
1637 }
1638
1639 /**
1640 * Return names of the instance options
1641 * By default: no instance option name
1642 * @return array
1643 */
7a3b93c1 1644 public static function get_instance_option_names() {
a6600395 1645 return array();
8b65d45c 1646 }
119a4ae1 1647
ce5e846c 1648 /**
1649 * Override it if you need to implement need mnet function
1650 * @return array
1651 */
119a4ae1 1652 public static function mnet_publishes() {
1653 return array();
1654 }
1655
8b65d45c 1656}
837ebb78 1657
1658/**
4a65c39a 1659 * exception class for repository api
837ebb78 1660 */
4a65c39a 1661class repository_exception extends moodle_exception {
8b65d45c 1662}
4ed43890 1663
e3ca0f3a 1664
1665
8dcd5deb 1666/**
1667 * TODO: write comment
1668 */
a6600395 1669final class repository_instance_form extends moodleform {
4a65c39a 1670 protected $instance;
1671 protected $plugin;
1672
8dcd5deb 1673 /**
1674 * TODO: write comment
1675 * @global <type> $CFG
1676 */
4a65c39a 1677 public function definition() {
1678 global $CFG;
1679 // type of plugin, string
1680 $this->plugin = $this->_customdata['plugin'];
27051e43 1681 $this->typeid = $this->_customdata['typeid'];
faaa613d 1682 $this->contextid = $this->_customdata['contextid'];
4a65c39a 1683 $this->instance = (isset($this->_customdata['instance'])
1684 && is_subclass_of($this->_customdata['instance'], 'repository'))
1685 ? $this->_customdata['instance'] : null;
1686
1687 $mform =& $this->_form;
1688 $strrequired = get_string('required');
1689
1690 $mform->addElement('hidden', 'edit', ($this->instance) ? $this->instance->id : 0);
1691 $mform->addElement('hidden', 'new', $this->plugin);
1692 $mform->addElement('hidden', 'plugin', $this->plugin);
27051e43 1693 $mform->addElement('hidden', 'typeid', $this->typeid);
faaa613d 1694 $mform->addElement('hidden', 'contextid', $this->contextid);
4a65c39a 1695
1696 $mform->addElement('text', 'name', get_string('name'), 'maxlength="100" size="30"');
1697 $mform->addRule('name', $strrequired, 'required', null, 'client');
1698
1e97f196 1699
f48fb4d6 1700 //add fields
1701 if (!$this->instance) {
dbc01944 1702 $result = repository::static_function($this->plugin, 'instance_config_form', $mform);
f48fb4d6 1703 }
1704 else {
1705 $data = array();
1706 $data['name'] = $this->instance->name;
1707 if (!$this->instance->readonly) {
1708 $result = $this->instance->instance_config_form($mform);
1709 // and set the data if we have some.
1e97f196 1710 foreach ($this->instance->get_instance_option_names() as $config) {
1711 if (!empty($this->instance->$config)) {
1712 $data[$config] = $this->instance->$config;
1713 } else {
1714 $data[$config] = '';
1715 }
27051e43 1716 }
4a65c39a 1717 }
f48fb4d6 1718 $this->set_data($data);
1e97f196 1719 }
1720
46dd6bb0 1721 $this->add_action_buttons(true, get_string('save','repository'));
4a65c39a 1722 }
1723
8dcd5deb 1724 /**
1725 * TODO: write comment
1726 * @global <type> $DB
1727 * @param <type> $data
1728 * @return <type>
1729 */
4a65c39a 1730 public function validation($data) {
1731 global $DB;
1732
1733 $errors = array();
27051e43 1734 if ($DB->count_records('repository_instances', array('name' => $data['name'], 'typeid' => $data['typeid'])) > 1) {
4a65c39a 1735 $errors = array('name' => get_string('err_uniquename', 'repository'));
1736 }
1737
4a65c39a 1738 return $errors;
1739 }
1740}
a6600395 1741
1742
1743/**
1744 * Display a form with the general option fields of a type
1745 */
c295f9ff 1746final class repository_type_form extends moodleform {
a6600395 1747 protected $instance;
1748 protected $plugin;
1749
1750 /**
1751 * Definition of the moodleform
1752 * @global object $CFG
1753 */
1754 public function definition() {
1755 global $CFG;
1756 // type of plugin, string
1757 $this->plugin = $this->_customdata['plugin'];
1758 $this->instance = (isset($this->_customdata['instance'])
1759 && is_a($this->_customdata['instance'], 'repository_type'))
1760 ? $this->_customdata['instance'] : null;
1761
1762 $mform =& $this->_form;
1763 $strrequired = get_string('required');
4d5948f1 1764
a6600395 1765 $mform->addElement('hidden', 'edit', ($this->instance) ? $this->instance->get_typename() : 0);
1766 $mform->addElement('hidden', 'new', $this->plugin);
1767 $mform->addElement('hidden', 'plugin', $this->plugin);
8f943eba 1768
06e65e1e 1769 // let the plugin add its specific fields
1770 if (!$this->instance) {
dbc01944 1771 $result = repository::static_function($this->plugin, 'type_config_form', $mform);
a6600395 1772 } else {
7a3b93c1 1773 $classname = 'repository_' . $this->instance->get_typename();
daff8f50 1774 $result = call_user_func(array($classname,'type_config_form'),$mform);
a6600395 1775 }
1776
f48fb4d6 1777 //add "enable course/user instances" checkboxes if multiple instances are allowed
dbc01944 1778 $instanceoptionnames = repository::static_function($this->plugin, 'get_instance_option_names');
f48fb4d6 1779 if (!empty($instanceoptionnames)){
1780 $mform->addElement('checkbox', 'enablecourseinstances', get_string('enablecourseinstances', 'repository'));
1781 $mform->addElement('checkbox', 'enableuserinstances', get_string('enableuserinstances', 'repository'));
1782 }
1783
1784 // set the data if we have some.
a6600395 1785 if ($this->instance) {
1786 $data = array();
1b79955a 1787 $option_names = call_user_func(array($classname,'get_type_option_names'));
f48fb4d6 1788 if (!empty($instanceoptionnames)){
1789 $option_names[] = 'enablecourseinstances';
1790 $option_names[] = 'enableuserinstances';
1791 }
1792
a6600395 1793 $instanceoptions = $this->instance->get_options();
1794 foreach ($option_names as $config) {
1795 if (!empty($instanceoptions[$config])) {
1796 $data[$config] = $instanceoptions[$config];
1797 } else {
1798 $data[$config] = '';
1799 }
1800 }
1801 $this->set_data($data);
1802 }
dbc01944 1803
46dd6bb0 1804 $this->add_action_buttons(true, get_string('save','repository'));
a6600395 1805 }
a6600395 1806}
1807
1808