26112e615ab45f592febc1d22d78fa1475f69aae
[moodle.git] / repository / lib.php
1 <?php
2  // $Id$
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 ///////////////////////////////////////////////////////////////////////////
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  */
41 require_once(dirname(dirname(__FILE__)) . '/config.php');
42 require_once(dirname(dirname(__FILE__)) . '/lib/filelib.php');
43 require_once(dirname(dirname(__FILE__)) . '/lib/formslib.php');
44 // File picker javascript code
45 require_once(dirname(dirname(__FILE__)) . '/repository/javascript.php');
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
57  * - when you delete a repository, all instances are deleted, and general
58  *   options are also deleted from database
59  * - When you create a type for a plugin that can't have multiple instances, a
60  *   instance is automatically created.
61  */
62 class repository_type {
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;
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      */
80     private $_options;
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      */
88     private $_visible;
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;
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) {
107         if ($contextlevel == CONTEXT_COURSE) {
108             return $this->_options['enablecourseinstances'];
109         }
111         if ($contextlevel == CONTEXT_USER) {
112             return $this->_options['enableuserinstances'];
113         }
115         //the context is SITE
116         return true;
117     }
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      */
129     public function __construct($typename = '', $typeoptions = array(), $visible = false, $sortorder = 0) {
130         global $CFG;
132         //set type attributs
133         $this->_typename = $typename;
134         $this->_visible = $visible;
135         $this->_sortorder = $sortorder;
137         //set options attribut
138         $this->_options = array();
139         $options = repository::static_function($typename,'get_type_option_names');
140         //check that the type can be setup
141         if (!empty($options)) {
142             //set the type options
143             foreach ($options as $config) {
144                 if (array_key_exists($config,$typeoptions)) {
145                     $this->_options[$config] = $typeoptions[$config];
146                 }
147             }
148         }
150         //retrieve visibility from option
151         if (array_key_exists('enablecourseinstances',$typeoptions)) {
152             $this->_options['enablecourseinstances'] = $typeoptions['enablecourseinstances'];
153         } else {
154              $this->_options['enablecourseinstances'] = 0;
155         }
157         if (array_key_exists('enableuserinstances',$typeoptions)) {
158             $this->_options['enableuserinstances'] = $typeoptions['enableuserinstances'];
159         } else {
160              $this->_options['enableuserinstances'] = 0;
161         }
163     }
165     /**
166      * Get the type name (no whitespace)
167      * For a human readable name, use get_readablename()
168      * @return String the type name
169      */
170     public function get_typename() {
171         return $this->_typename;
172     }
174     /**
175      * Return a human readable and user-friendly type name
176      * @return string user-friendly type name
177      */
178     public function get_readablename() {
179         return get_string('repositoryname','repository_'.$this->_typename);
180     }
182     /**
183      * Return general options
184      * @return array the general options
185      */
186     public function get_options() {
187         return $this->_options;
188     }
190     /**
191      * Return visibility
192      * @return boolean
193      */
194     public function get_visible() {
195         return $this->_visible;
196     }
198     /**
199      * Return order / position of display in the file picker
200      * @return integer
201      */
202     public function get_sortorder() {
203         return $this->_sortorder;
204     }
206     /**
207      * Create a repository type (the type name must not already exist)
208      * @global object $DB
209      */
210     public function create() {
211         global $DB;
213         //check that $type has been set
214         $timmedtype = trim($this->_typename);
215         if (empty($timmedtype)) {
216             throw new repository_exception('emptytype', 'repository');
217         }
219         //set sortorder as the last position in the list
220         if (!isset($this->_sortorder) || $this->_sortorder == 0 ) {
221             $sql = "SELECT MAX(sortorder) FROM {repository}";
222             $this->_sortorder = 1 + $DB->get_field_sql($sql);
223         }
225         //only create a new type if it doesn't already exist
226         $existingtype = $DB->get_record('repository', array('type'=>$this->_typename));
227         if (!$existingtype) {
228             //run init function
229             if (!repository::static_function($this->_typename, 'plugin_init')) {
230                 throw new repository_exception('cannotcreatetype', 'repository');
231             }
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);
240             //save the options in DB
241             $this->update_options();
243             //if the plugin type has no multiple instance (e.g. has no instance option name) so it wont
244             //be possible for the administrator to create a instance
245             //in this case we need to create an instance
246             $instanceoptionnames = repository::static_function($this->_typename, 'get_instance_option_names');
247             if (empty($instanceoptionnames)) {
248                 $instanceoptions = array();
249                 $instanceoptions['name'] = $this->_typename;
250                 repository::static_function($this->_typename, 'create', $this->_typename, 0, get_system_context(), $instanceoptions);
251             }
253         } else {
254             throw new repository_exception('existingrepository', 'repository');
255         }
256     }
259     /**
260      * Update plugin options into the config_plugin table
261      * @param array $options
262      * @return boolean
263      */
264     public function update_options($options = null) {
265         if (!empty($options)) {
266             $this->_options = $options;
267         }
269         foreach ($this->_options as $name => $value) {
270             set_config($name,$value,$this->_typename);
271         }
273         return true;
274     }
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      */
285     private function update_visible($visible = null) {
286         global $DB;
288         if (!empty($visible)) {
289             $this->_visible = $visible;
290         }
291         else if (!isset($this->_visible)) {
292             throw new repository_exception('updateemptyvisible', 'repository');
293         }
295         return $DB->set_field('repository', 'visible', $this->_visible, array('type'=>$this->_typename));
296     }
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      */
307     private function update_sortorder($sortorder = null) {
308         global $DB;
310         if (!empty($sortorder) && $sortorder!=0) {
311             $this->_sortorder = $sortorder;
312         }
313         //if sortorder is not set, we set it as the ;ast position in the list
314         else if (!isset($this->_sortorder) || $this->_sortorder == 0 ) {
315             $sql = "SELECT MAX(sortorder) FROM {repository}";
316             $this->_sortorder = 1 + $DB->get_field_sql($sql);
317         }
319         return $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$this->_typename));
320     }
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;
335         $types = repository::get_types();    // retrieve all types
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) {
341                 $indice = $i;
342             }
343             $i++;
344         }
346     /// retrieve adjacent indice
347         switch ($move) {
348             case "up":
349                 $adjacentindice = $indice - 1;
350             break;
351             case "down":
352                 $adjacentindice = $indice + 1;
353             break;
354             default:
355             throw new repository_exception('movenotdefined', 'repository');
356         }
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.
362         if ($adjacentindice>=0 && !empty($types[$adjacentindice])) {
363             $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$types[$adjacentindice]->get_typename()));
364             $this->update_sortorder($types[$adjacentindice]->get_sortorder());
365         }
366     }
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      */
373     public function switch_and_update_visibility() {
374         $this->_visible = !$this->_visible;
375         return $this->update_visible();
376     }
379     /**
380      * Delete a repository_type (general options are removed from config_plugin
381      * table, and all instances are deleted)
382      * @global object $DB
383      * @return boolean
384      */
385     public function delete() {
386         global $DB;
388         //delete all instances of this type
389         $instances = repository::get_instances(array(),null,false,$this->_typename);
390         foreach ($instances as $instance) {
391             $instance->delete();
392         }
394         //delete all general options
395         foreach ($this->_options as $name => $value) {
396             set_config($name, null, $this->_typename);
397         }
399         return $DB->delete_records('repository', array('type' => $this->_typename));
400     }
403 /**
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
426  */
427 abstract class repository {
428     // $disabled can be set to true to disable a plugin by force
429     // example: self::$disabled = true
430     public $disabled = false;
431     public $id;
432     public $context;
433     public $options;
434     public $readonly;
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;
445         if (!$record = $DB->get_record('repository',array('type' => $typename))) {
446             return false;
447         }
449         return new repository_type($typename, (array)get_config($typename), $record->visible, $record->sortorder);
450     }
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;
461         if (!$record = $DB->get_record('repository',array('id' => $id))) {
462             return false;
463         }
465         return new repository_type($record->type, (array)get_config($record->type), $record->visible, $record->sortorder);
466     }
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;
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         }
489         return $types;
490     }
492     /**
493      * Check context
494      * @param int $ctx_id
495      * @return boolean
496      */
497     public static function check_context($ctx_id) {
498         global $USER;
500         $context = get_context_instance_by_id($ctx_id);
501         $level = $context->contextlevel;
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     }
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) {
531         if (empty($context)) {
532             $context = get_system_context();
533         }
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     }
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      */
560     public static function get_instances($contexts=array(), $userid = null, $onlyvisible = true, $type=null, $accepted_types = '*', $returnvalue = '*') {
561         global $DB, $CFG, $USER;
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 ';
567         if (!empty($userid) && is_numeric($userid)) {
568             $sql .= ' AND (i.userid = 0 or i.userid = ?)';
569             $params[] = $userid;
570         }
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         }
582         if (!empty($firstcontext)) {
583            $sql .=')';
584         }
586         if ($onlyvisible == true) {
587             $sql .= ' AND (r.visible = 1)';
588         }
590         if (isset($type)) {
591             $sql .= ' AND (r.type = ?)';
592             $params[] = $type;
593         }
594         $sql .= ' order by r.sortorder, i.name';
596         if (!$repos = $DB->get_records_sql($sql, $params)) {
597             $repos = array();
598         }
600         $ret = array();
601         $ft = new file_type_to_ext();
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;
608             // tell instance what file types will be accepted by file picker
609             $options['accepted_types'] = $ft->get_file_ext($accepted_types);
610             $classname = 'repository_' . $repo->repositorytype;//
611             $is_supported = true;
613             $repository = new $classname($repo->id, $repo->contextid, $options, $repo->readonly);
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                         }
625                     }
626                 }
627                 if ($returnvalue !== '*' and $repository->supported_return_value() !== '*') {
628                     $tmp = $repository->supported_return_value();
629                     if ($tmp != $returnvalue) {
630                         $is_supported = false;
631                     }
632                 }
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                     }
639                 }
640             }
641         }
642         return $ret;
643     }
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;
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;
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;
672     }
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;
687         //check that the plugin exists
688         $typedirectory = $CFG->dirroot . '/repository/'. $plugin . '/repository.class.php';
689         if (!file_exists($typedirectory)) {
690             //throw new repository_exception('invalidplugin', 'repository');
691             return false;
692         }
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         }
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         }
711         require_once($typedirectory);
712         return call_user_func_array(array('repository_' . $plugin, $function), $args);
713     }
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;
734         //$entry->filepath  = '/'.uniqid().'/';
735         $entry->filepath  = '/';
736         $entry->timecreated  = $now;
737         $entry->timemodified = $now;
738         $entry->userid       = $USER->id;
739         $entry->mimetype     = mimeinfo('type', $path);
740         if(is_numeric($itemid)) {
741             $entry->itemid = $itemid;
742         } else {
743             $entry->itemid = 0;
744         }
745         $fs = get_file_storage();
746         $browser = get_file_browser();
747         if ($existingfile = $fs->get_file($context->id, $filearea, $itemid, $path, $name)) {
748             $existingfile->delete();
749         }
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)) {
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                 );
759             } else {
760                 return null;
761             }
762         } else {
763             return null;
764         }
765     }
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     }
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      */
794     public static function store_to_filepool($elname, $filearea='user_draft', $filepath='/', $itemid='', $filename = '', $override = false) {
795         global $USER;
796         if (!isset($_FILES[$elname])) {
797             return false;
798         }
800         if (!$filename) {
801             $filename = $_FILES[$elname]['name'];
802         }
803         $context = get_context_instance(CONTEXT_USER, $USER->id);
804         if (empty($itemid)) {
805             $itemid = (int)substr(hexdec(uniqid()), 0, 9)+rand(1,100);
806         }
807         $fs = get_file_storage();
808         $browser = get_file_browser();
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         }
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;
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     }
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;
850             if ($fileinfo = $browser->get_file_info(get_system_context(), $filearea, $itemid, $path, $filename)) {
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             }
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             }
875     }
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     }
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;
903         $filecount = 0;
904         $children = $fileinfo->get_children();
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();
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                 }
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                 );
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                 }
945                 //}
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                 }
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         }
973         return $filecount;
974     }
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;
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) {
991             $baseurl = "$CFG->httpswwwroot/$CFG->admin/repositoryinstance.php?sesskey=" . sesskey();
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         }
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();
1011         $table = new StdClass;
1012         $table->head = array($namestr, $pluginstr, $deletestr, $settingsstr);
1013         $table->align = array('left', 'left', 'center','center');
1014         $table->data = array();
1016         $updowncount = 1;
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             }
1026             $type = repository::get_type_by_id($i->typeid);
1027             $table->data[] = array($i->name, $type->get_readablename(), $delete, $settings);
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             }
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;
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>';
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         }
1073         if ($addable) {
1074             $instancehtml .= '</div>';
1075             $output .= $instancehtml;
1076         }
1078         $output .= print_box_end(true);
1080         //print the list + creation links
1081         print($output);
1082     }
1083     /**
1084      * 1. Initialize context and options
1085      * 2. Accept necessary parameters
1086      *
1087      * @param integer $repositoryid
1088      * @param integer $contextid
1089      * @param array $options
1090      */
1091     public function __construct($repositoryid, $contextid = SITEID, $options = array(), $readonly = 0) {
1092         $this->id = $repositoryid;
1093         $this->context = get_context_instance_by_id($contextid);
1094         $this->readonly = $readonly;
1095         $this->options = array();
1096         if (is_array($options)) {
1097             $options = array_merge($this->get_option(), $options);
1098         } else {
1099             $options = $this->get_option();
1100         }
1101         $this->options = array();
1102         foreach ($options as $n => $v) {
1103             $this->options[$n] = $v;
1104         }
1105         $this->name = $this->get_name();
1106         $this->super_called = true;
1107     }
1109     /**
1110      * set options for repository instance
1111      *
1112      * @param string $name
1113      * @param mixed $value
1114      */
1115     public function __set($name, $value) {
1116         $this->options[$name] = $value;
1117     }
1119     /**
1120      * get options for repository instance
1121      *
1122      * @param string $name
1123      * @return mixed
1124      */
1125     public function __get($name) {
1126         if (array_key_exists($name, $this->options)) {
1127             return $this->options[$name];
1128         }
1129         trigger_error('Undefined property: '.$name, E_USER_NOTICE);
1130         return null;
1131     }
1133     /**
1134      * test option name
1135      *
1136      * @param string name
1137      */
1138     public function __isset($name) {
1139         return isset($this->options[$name]);
1140     }
1142     /**
1143      * Return the name of the repository class
1144      * @return <type>
1145      */
1146     public function __toString() {
1147         return 'Repository class: '.__CLASS__;
1148     }
1150     /**
1151      * Download a file, this function can be overridden by
1152      * subclass.
1153      *
1154      * @global object $CFG
1155      * @param string $url the url of file
1156      * @param string $file save location
1157      * @return string the location of the file
1158      * @see curl package
1159      */
1160     public function get_file($url, $file = '') {
1161         global $CFG;
1162         if (!file_exists($CFG->dataroot.'/temp/download')) {
1163             mkdir($CFG->dataroot.'/temp/download/', 0777, true);
1164         }
1165         if (is_dir($CFG->dataroot.'/temp/download')) {
1166             $dir = $CFG->dataroot.'/temp/download/';
1167         }
1168         if (empty($file)) {
1169             $file = uniqid('repo').'_'.time().'.tmp';
1170         }
1171         if (file_exists($dir.$file)) {
1172             $file = uniqid('m').$file;
1173         }
1174         $fp = fopen($dir.$file, 'w');
1175         $c = new curl;
1176         $c->download(array(
1177                     array('url'=>$url, 'file'=>$fp)
1178                     ));
1179         return $dir.$file;
1180     }
1182     /**
1183      * Print a list or return formatted string, can be overridden by subclass
1184      *
1185      * @param string $list
1186      * @param boolean $print false, return html, otherwise, print it directly
1187      * @return <type>
1188      */
1189     public function print_listing($listing = array(), $print=true) {
1190         if (empty($listing)) {
1191             $listing = $this->get_listing();
1192         }
1193         if (empty($listing)) {
1194             $str = '';
1195         } else {
1196             $count = 0;
1197             $str = '<table>';
1198             foreach ($listing as $v) {
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             }
1207             $str .= '</table>';
1208         }
1209         if ($print) {
1210             echo $str;
1211             return null;
1212         } else {
1213             return $str;
1214         }
1215     }
1217     /**
1218      * Return is the instance is visible
1219      * (is the type visible ? is the context enable ?)
1220      * @return boolean
1221      */
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');
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         }
1233         return false;
1234     }
1236     /**
1237      * Return the name of this instance, can be overridden.
1238      * @global <type> $DB
1239      * @return <type>
1240      */
1241     public function get_name() {
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     }
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      */
1263     public function supported_filetypes() {
1264         // return array('text/plain', 'image/gif');
1265         return '*';
1266     }
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';
1275         return 'ref_id';
1276     }
1278     /**
1279      * Provide repository instance information for Ajax
1280      * @global object $CFG
1281      * @return object
1282      */
1283     final public function ajax_info() {
1284         global $CFG;
1285         $ft = new file_type_to_ext;
1286         $repo = new stdclass;
1287         $repo->id   = $this->id;
1288         $repo->name = $this->get_name();
1289         $repo->type = $this->options['type'];
1290         $repo->icon = $CFG->httpswwwroot.'/repository/'.$repo->type.'/icon.png';
1291         $repo->supported_types = $ft->get_file_ext($this->supported_filetypes());
1292         $repo->accepted_types = $this->accepted_types;
1293         return $repo;
1294     }
1296     /**
1297      * Create an instance for this plug-in
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>
1305      */
1306     final public static function create($type, $userid, $context, $params, $readonly=0) {
1307         global $CFG, $DB;
1308         $params = (array)$params;
1309         require_once($CFG->dirroot . '/repository/'. $type . '/repository.class.php');
1310         $classname = 'repository_' . $type;
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;
1318             $record->readonly = $readonly;
1319             $record->userid    = $userid;
1320             $id = $DB->insert_record('repository_instances', $record);
1321             $options = array();
1322             $configs = call_user_func($classname . '::get_instance_option_names');
1323             if (!empty($configs)) {
1324                 foreach ($configs as $config) {
1325                     $options[$config] = $params[$config];
1326                 }
1327             }
1329             if (!empty($id)) {
1330                 unset($options['name']);
1331                 $instance = repository::get_instance($id);
1332                 $instance->set_option($options);
1333                 return $id;
1334             } else {
1335                 return null;
1336             }
1337         } else {
1338             return null;
1339         }
1340     }
1342     /**
1343      * delete a repository instance
1344      * @global object $DB
1345      * @return <type>
1346      */
1347     final public function delete() {
1348         global $DB;
1349         $DB->delete_records('repository_instances', array('id'=>$this->id));
1350         return true;
1351     }
1353     /**
1354      * Hide/Show a repository
1355      * @global object $DB
1356      * @param string $hide
1357      * @return <type>
1358      */
1359     final public function hide($hide = 'toggle') {
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     }
1380     /**
1381      * Cache login details for repositories
1382      * @global object $DB
1383      * @param string $username
1384      * @param string $password
1385      * @param integer $userid The id of specific user
1386      * @return integer Id of the record
1387      */
1388     public function store_login($username = '', $password = '', $userid = 1) {
1389         global $DB;
1391         $repository = new stdclass;
1392         if (!empty($this->id)) {
1393             $repository->id = $this->id;
1394         } else {
1395             $repository->userid         = $userid;
1396             $repository->repositorytype = $this->type;
1397             $repository->contextid      = $this->context->id;
1398         }
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         }
1409     }
1411     /**
1412      * Save settings for repository instance
1413      * $repo->set_option(array('api_key'=>'f2188bde132', 'name'=>'dongsheng'));
1414      * @global object $DB
1415      * @param array $options settings
1416      * @return int Id of the record
1417      */
1418     public function set_option($options = array()) {
1419         global $DB;
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         }
1428         $result = true;
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) {
1432                     $result = $result && $DB->delete_records('repository_instance_config', array('name'=>$name, 'instanceid'=>$this->id));
1433                 } else {
1434                     $result = $result && $DB->set_field('repository_instance_config', 'value', $value, array('id'=>$id));
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;
1444                 $result = $result && $DB->insert_record('repository_instance_config', $config);
1445             }
1446         }
1447         return $result;
1448     }
1450     /**
1451      * Get settings for repository instance
1452      * @global object $DB
1453      * @param <type> $config
1454      * @return array Settings
1455      */
1456     public function get_option($config = '') {
1457         global $DB;
1458         $entries = $DB->get_records('repository_instance_config', array('instanceid'=>$this->id));
1459         $ret = array();
1460         if (empty($entries)) {
1461             return $ret;
1462         }
1463         foreach($entries as $entry) {
1464             $ret[$entry->name] = $entry->value;
1465         }
1466         if (!empty($config)) {
1467             return $ret[$config];
1468         } else {
1469             return $ret;
1470         }
1471     }
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;
1480             if (!empty($value->children)) {
1481                 $value->children = array_filter($value->children, array($this, 'filter'));
1482             }
1483         } else {
1484             if ($accepted_types == '*' or empty($accepted_types)
1485                 or (is_array($accepted_types) and in_array('*', $accepted_types))) {
1486                 $pass = true;
1487             } elseif (is_array($accepted_types)) {
1488                 foreach ($accepted_types as $type) {
1489                     if (preg_match('#'.$type.'$#', $value['title'])) {
1490                         $pass = true;
1491                     }
1492                 }
1493             }
1494         }
1495         return $pass;
1496     }
1498     /**
1499      * Given a path, and perhaps a search, get a list of files.
1500      *
1501      * The format of the returned array must be:
1502      * array(
1503      *   'path' => (string) path for the current folder
1504      *   'dynload' => (bool) use dynamic loading,
1505      *   'manage' => (string) link to file manager,
1506      *   'nologin' => (bool) requires login,
1507      *   'nosearch' => (bool) no search link,
1508      *   'upload' => array( // upload manager
1509      *     'name' => (string) label of the form element,
1510      *     'id' => (string) id of the form element
1511      *   ),
1512      *   'list' => array(
1513      *     array( // file
1514      *       'title' => (string) file name,
1515      *       'date' => (string) file last modification time, usually userdate(...),
1516      *       'size' => (int) file size,
1517      *       'thumbnail' => (string) url to thumbnail for the file,
1518      *       'source' => plugin-dependent unique path to the file (id, url, path, etc.),
1519      *       'url'=> the accessible url of file
1520      *     ),
1521      *     array( // folder - same as file, but no 'source'.
1522      *       'title' => (string) folder name,
1523      *       'path' => (string) path to this folder
1524      *       'date' => (string) folder last modification time, usually userdate(...),
1525      *       'size' => 0,
1526      *       'thumbnail' => (string) url to thumbnail for the folder,
1527      *       'children' => array( // an empty folder needs to have 'children' defined, but empty.
1528      *         // content (files and folders)
1529      *       )
1530      *     ),
1531      *   )
1532      * )
1533      *
1534      * @param string $parent The parent path, this parameter can
1535      * a folder name, or a identification of folder
1536      * @return array the list of files, including meta infomation
1537      */
1538     abstract public function get_listing($parent = '/');
1540     /**
1541      * Search files in repository
1542      * When doing global search, $search_text will be used as
1543      * keyword.
1544      *
1545      * @return mixed, see get_listing()
1546      */
1547     public function search($search_text) {
1548         $list = array();
1549         $list['list'] = array();
1550         return false;
1551     }
1553     /**
1554      * Logout from repository instance
1555      * By default, this function will return a login form
1556      *
1557      * @return string
1558      */
1559     public function logout(){
1560         return $this->print_login();
1561     }
1563     /**
1564      * To check whether the user is logged in.
1565      *
1566      * @return boolean
1567      */
1568     public function check_login(){
1569         return true;
1570     }
1573     /**
1574      * Show the login screen, if required
1575      * This is an abstract function, it must be overriden.
1576      */
1577     public function print_login(){
1578         return $this->get_listing();
1579     }
1581     /**
1582      * Show the search screen, if required
1583      * @return null
1584      */
1585     public function print_search() {
1586         echo '<input type="hidden" name="repo_id" value="'.$this->id.'" />';
1587         echo '<input type="hidden" name="ctx_id" value="'.$this->context->id.'" />';
1588         echo '<input type="hidden" name="seekey" value="'.sesskey().'" />';
1589         echo '<label>'.get_string('keyword', 'repository').': </label><br/><input name="s" value="" /><br/>';
1590         return true;
1591     }
1593     /**
1594      * is it possible to do glboal search?
1595      * @return boolean
1596      */
1597     public function global_search() {
1598         return false;
1599     }
1601     /**
1602      * Defines operations that happen occasionally on cron
1603      * @return <type>
1604      */
1605     public function cron() {
1606         return true;
1607     }
1609     /**
1610      * function which is run when the type is created (moodle administrator add the plugin)
1611      * @return boolean success or fail?
1612      */
1613     public static function plugin_init() {
1614         return true;
1615     }
1617     /**
1618      * Edit/Create Admin Settings Moodle form
1619      * @param object $ Moodle form (passed by reference)
1620      */
1621      public function type_config_form(&$mform) {
1622     }
1624       /**
1625      * Edit/Create Instance Settings Moodle form
1626      * @param object $ Moodle form (passed by reference)
1627      */
1628      public function instance_config_form(&$mform) {
1629     }
1631     /**
1632      * Return names of the general options
1633      * By default: no general option name
1634      * @return array
1635      */
1636     public static function get_type_option_names() {
1637         return array();
1638     }
1640     /**
1641      * Return names of the instance options
1642      * By default: no instance option name
1643      * @return array
1644      */
1645     public static function get_instance_option_names() {
1646         return array();
1647     }
1649     /**
1650      * Override it if you need to implement need mnet function
1651      * @return array
1652      */
1653      public static function mnet_publishes() {
1654         return array();
1655     }
1659 /**
1660  * exception class for repository api
1661  */
1662 class repository_exception extends moodle_exception {
1667 /**
1668  * TODO: write comment
1669  */
1670 final class repository_instance_form extends moodleform {
1671     protected $instance;
1672     protected $plugin;
1674     /**
1675      * TODO: write comment
1676      * @global <type> $CFG
1677      */
1678     public function definition() {
1679         global $CFG;
1680         // type of plugin, string
1681         $this->plugin = $this->_customdata['plugin'];
1682         $this->typeid = $this->_customdata['typeid'];
1683         $this->contextid = $this->_customdata['contextid'];
1684         $this->instance = (isset($this->_customdata['instance'])
1685                 && is_subclass_of($this->_customdata['instance'], 'repository'))
1686             ? $this->_customdata['instance'] : null;
1688         $mform =& $this->_form;
1689         $strrequired = get_string('required');
1691         $mform->addElement('hidden', 'edit',  ($this->instance) ? $this->instance->id : 0);
1692         $mform->addElement('hidden', 'new',   $this->plugin);
1693         $mform->addElement('hidden', 'plugin', $this->plugin);
1694         $mform->addElement('hidden', 'typeid', $this->typeid);
1695         $mform->addElement('hidden', 'contextid', $this->contextid);
1697         $mform->addElement('text', 'name', get_string('name'), 'maxlength="100" size="30"');
1698         $mform->addRule('name', $strrequired, 'required', null, 'client');
1701         //add fields
1702         if (!$this->instance) {
1703             $result = repository::static_function($this->plugin, 'instance_config_form', $mform);
1704         }
1705         else {
1706             $data = array();
1707             $data['name'] = $this->instance->name;
1708             if (!$this->instance->readonly) {
1709                 $result = $this->instance->instance_config_form($mform);
1710                 // and set the data if we have some.
1711                 foreach ($this->instance->get_instance_option_names() as $config) {
1712                     if (!empty($this->instance->$config)) {
1713                         $data[$config] = $this->instance->$config;
1714                      } else {
1715                         $data[$config] = '';
1716                      }
1717                 }
1718             }
1719             $this->set_data($data);
1720         }
1722         $this->add_action_buttons(true, get_string('save','repository'));
1723     }
1725     /**
1726      * TODO: write comment
1727      * @global <type> $DB
1728      * @param <type> $data
1729      * @return <type>
1730      */
1731     public function validation($data) {
1732         global $DB;
1734         $errors = array();
1735         if ($DB->count_records('repository_instances', array('name' => $data['name'], 'typeid' => $data['typeid'])) > 1) {
1736             $errors = array('name' => get_string('err_uniquename', 'repository'));
1737         }
1739         return $errors;
1740     }
1744 /**
1745  * Display a form with the general option fields of a type
1746  */
1747 final class repository_type_form extends moodleform {
1748     protected $instance;
1749     protected $plugin;
1751     /**
1752      * Definition of the moodleform
1753      * @global object $CFG
1754      */
1755     public function definition() {
1756         global $CFG;
1757         // type of plugin, string
1758         $this->plugin = $this->_customdata['plugin'];
1759         $this->instance = (isset($this->_customdata['instance'])
1760                 && is_a($this->_customdata['instance'], 'repository_type'))
1761             ? $this->_customdata['instance'] : null;
1763         $mform =& $this->_form;
1764         $strrequired = get_string('required');
1766         $mform->addElement('hidden', 'edit',  ($this->instance) ? $this->instance->get_typename() : 0);
1767         $mform->addElement('hidden', 'new',   $this->plugin);
1768         $mform->addElement('hidden', 'plugin', $this->plugin);
1770         // let the plugin add its specific fields
1771         if (!$this->instance) {
1772                 $result = repository::static_function($this->plugin, 'type_config_form', $mform);
1773             } else {
1774                 $classname = 'repository_' . $this->instance->get_typename();
1775                 $result = call_user_func(array($classname,'type_config_form'),$mform);
1776         }
1778         //add "enable course/user instances" checkboxes if multiple instances are allowed
1779         $instanceoptionnames = repository::static_function($this->plugin, 'get_instance_option_names');
1780         if (!empty($instanceoptionnames)){
1781             $mform->addElement('checkbox', 'enablecourseinstances', get_string('enablecourseinstances', 'repository'));
1782             $mform->addElement('checkbox', 'enableuserinstances', get_string('enableuserinstances', 'repository'));
1783         }
1785         // set the data if we have some.
1786         if ($this->instance) {
1787             $data = array();
1788             $option_names = call_user_func(array($classname,'get_type_option_names'));
1789             if (!empty($instanceoptionnames)){
1790                 $option_names[] = 'enablecourseinstances';
1791                 $option_names[] = 'enableuserinstances';
1792             }
1794             $instanceoptions = $this->instance->get_options();
1795             foreach ($option_names as $config) {
1796                 if (!empty($instanceoptions[$config])) {
1797                     $data[$config] = $instanceoptions[$config];
1798                 } else {
1799                     $data[$config] = '';
1800                 }
1801             }
1802             $this->set_data($data);
1803         }
1805         $this->add_action_buttons(true, get_string('save','repository'));
1806     }