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