"MDL-13766, add extra yui images for yui treeview, this will display folder-style...
[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($CFG->libdir . '/filelib.php');
43 require_once($CFG->libdir . '/formslib.php');
44 // File picker javascript code
46 /**
47  * A repository_type is a repository plug-in. It can be Box.net, Flick-r, ...
48  * A repository type can be edited, sorted and hidden. It is mandatory for an
49  * administrator to create a repository type in order to be able to create
50  * some instances of this type.
51  *
52  * Coding note:
53  * - a repository_type object is mapped to the "repository" database table
54  * - "typename" attibut maps the "type" database field. It is unique.
55  * - general "options" for a repository type are saved in the config_plugin table
56  * - when you delete a repository, all instances are deleted, and general
57  *   options are also deleted from database
58  * - When you create a type for a plugin that can't have multiple instances, a
59  *   instance is automatically created.
60  */
61 class repository_type {
64     /**
65      * Type name (no whitespace) - A type name is unique
66      * Note: for a user-friendly type name see get_readablename()
67      * @var String
68      */
69     private $_typename;
72     /**
73      * Options of this type
74      * They are general options that any instance of this type would share
75      * e.g. API key
76      * These options are saved in config_plugin table
77      * @var array
78      */
79     private $_options;
82     /**
83      * Is the repository type visible or hidden
84      * If false (hidden): no instances can be created, edited, deleted, showned , used...
85      * @var boolean
86      */
87     private $_visible;
90     /**
91      * 0 => not ordered, 1 => first position, 2 => second position...
92      * A not order type would appear in first position (should never happened)
93      * @var integer
94      */
95     private $_sortorder;
97      /**
98      * Return if the instance is visible in a context
99      * TODO: check if the context visibility has been overwritten by the plugin creator
100      *       (need to create special functions to be overvwritten in repository class)
101      * @param objet $contextlevel - context level
102      * @return boolean
103      */
104     public function get_contextvisibility($contextlevel) {
106         if ($contextlevel == CONTEXT_COURSE) {
107             return $this->_options['enablecourseinstances'];
108         }
110         if ($contextlevel == CONTEXT_USER) {
111             return $this->_options['enableuserinstances'];
112         }
114         //the context is SITE
115         return true;
116     }
120     /**
121      * repository_type constructor
122      * @global object $CFG
123      * @param integer $typename
124      * @param array $typeoptions
125      * @param boolean $visible
126      * @param integer $sortorder (don't really need set, it will be during create() call)
127      */
128     public function __construct($typename = '', $typeoptions = array(), $visible = false, $sortorder = 0) {
129         global $CFG;
131         //set type attributs
132         $this->_typename = $typename;
133         $this->_visible = $visible;
134         $this->_sortorder = $sortorder;
136         //set options attribut
137         $this->_options = array();
138         $options = repository::static_function($typename,'get_type_option_names');
139         //check that the type can be setup
140         if (!empty($options)) {
141             //set the type options
142             foreach ($options as $config) {
143                 if (array_key_exists($config,$typeoptions)) {
144                     $this->_options[$config] = $typeoptions[$config];
145                 }
146             }
147         }
149         //retrieve visibility from option
150         if (array_key_exists('enablecourseinstances',$typeoptions)) {
151             $this->_options['enablecourseinstances'] = $typeoptions['enablecourseinstances'];
152         } else {
153              $this->_options['enablecourseinstances'] = 0;
154         }
156         if (array_key_exists('enableuserinstances',$typeoptions)) {
157             $this->_options['enableuserinstances'] = $typeoptions['enableuserinstances'];
158         } else {
159              $this->_options['enableuserinstances'] = 0;
160         }
162     }
164     /**
165      * Get the type name (no whitespace)
166      * For a human readable name, use get_readablename()
167      * @return String the type name
168      */
169     public function get_typename() {
170         return $this->_typename;
171     }
173     /**
174      * Return a human readable and user-friendly type name
175      * @return string user-friendly type name
176      */
177     public function get_readablename() {
178         return get_string('repositoryname','repository_'.$this->_typename);
179     }
181     /**
182      * Return general options
183      * @return array the general options
184      */
185     public function get_options() {
186         return $this->_options;
187     }
189     /**
190      * Return visibility
191      * @return boolean
192      */
193     public function get_visible() {
194         return $this->_visible;
195     }
197     /**
198      * Return order / position of display in the file picker
199      * @return integer
200      */
201     public function get_sortorder() {
202         return $this->_sortorder;
203     }
205     /**
206      * Create a repository type (the type name must not already exist)
207      * @param boolean throw exception?
208      * @return mixed return int if create successfully, return false if
209      *         any errors
210      * @global object $DB
211      */
212     public function create($silent = false) {
213         global $DB;
215         //check that $type has been set
216         $timmedtype = trim($this->_typename);
217         if (empty($timmedtype)) {
218             throw new repository_exception('emptytype', 'repository');
219         }
221         //set sortorder as the last position in the list
222         if (!isset($this->_sortorder) || $this->_sortorder == 0 ) {
223             $sql = "SELECT MAX(sortorder) FROM {repository}";
224             $this->_sortorder = 1 + $DB->get_field_sql($sql);
225         }
227         //only create a new type if it doesn't already exist
228         $existingtype = $DB->get_record('repository', array('type'=>$this->_typename));
229         if (!$existingtype) {
230             //create the type
231             $newtype = new stdclass;
232             $newtype->type = $this->_typename;
233             $newtype->visible = $this->_visible;
234             $newtype->sortorder = $this->_sortorder;
235             $plugin_id = $DB->insert_record('repository', $newtype);
236             //save the options in DB
237             $this->update_options();
239             //if the plugin type has no multiple instance (e.g. has no instance option name) so it wont
240             //be possible for the administrator to create a instance
241             //in this case we need to create an instance
242             $instanceoptionnames = repository::static_function($this->_typename, 'get_instance_option_names');
243             if (empty($instanceoptionnames)) {
244                 $instanceoptions = array();
245                 $instanceoptions['name'] = $this->_typename;
246                 repository::static_function($this->_typename, 'create', $this->_typename, 0, get_system_context(), $instanceoptions);
247             }
248             //run plugin_init function
249             if (!repository::static_function($this->_typename, 'plugin_init')) {
250                 if (!$silent) {
251                     throw new repository_exception('cannotinitplugin', 'repository');
252                 }
253             }
255             if(!empty($plugin_id)) {
256                 // return plugin_id if create successfully
257                 return $plugin_id;
258             } else {
259                 return false;
260             }
262         } else {
263             if (!$silent) {
264                 throw new repository_exception('existingrepository', 'repository');
265             }
266             // If plugin existed, return false, tell caller no new plugins were created.
267             return false;
268         }
269     }
272     /**
273      * Update plugin options into the config_plugin table
274      * @param array $options
275      * @return boolean
276      */
277     public function update_options($options = null) {
278         if (!empty($options)) {
279             $this->_options = $options;
280         }
282         foreach ($this->_options as $name => $value) {
283             set_config($name,$value,$this->_typename);
284         }
286         return true;
287     }
289     /**
290      * Update visible database field with the value given as parameter
291      * or with the visible value of this object
292      * This function is private.
293      * For public access, have a look to switch_and_update_visibility()
294      * @global object $DB
295      * @param boolean $visible
296      * @return boolean
297      */
298     private function update_visible($visible = null) {
299         global $DB;
301         if (!empty($visible)) {
302             $this->_visible = $visible;
303         }
304         else if (!isset($this->_visible)) {
305             throw new repository_exception('updateemptyvisible', 'repository');
306         }
308         return $DB->set_field('repository', 'visible', $this->_visible, array('type'=>$this->_typename));
309     }
311     /**
312      * Update database sortorder field with the value given as parameter
313      * or with the sortorder value of this object
314      * This function is private.
315      * For public access, have a look to move_order()
316      * @global object $DB
317      * @param integer $sortorder
318      * @return boolean
319      */
320     private function update_sortorder($sortorder = null) {
321         global $DB;
323         if (!empty($sortorder) && $sortorder!=0) {
324             $this->_sortorder = $sortorder;
325         }
326         //if sortorder is not set, we set it as the ;ast position in the list
327         else if (!isset($this->_sortorder) || $this->_sortorder == 0 ) {
328             $sql = "SELECT MAX(sortorder) FROM {repository}";
329             $this->_sortorder = 1 + $DB->get_field_sql($sql);
330         }
332         return $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$this->_typename));
333     }
335     /**
336      * Change order of the type with its adjacent upper or downer type
337      * (database fields are updated)
338      * Algorithm details:
339      * 1. retrieve all types in an array. This array is sorted by sortorder,
340      * and the array keys start from 0 to X (incremented by 1)
341      * 2. switch sortorder values of this type and its adjacent type
342      * @global object $DB
343      * @param string $move "up" or "down"
344      */
345     public function move_order($move) {
346         global $DB;
348         $types = repository::get_types();    // retrieve all types
350     /// retrieve this type into the returned array
351         $i = 0;
352         while (!isset($indice) && $i<count($types)) {
353             if ($types[$i]->get_typename() == $this->_typename) {
354                 $indice = $i;
355             }
356             $i++;
357         }
359     /// retrieve adjacent indice
360         switch ($move) {
361             case "up":
362                 $adjacentindice = $indice - 1;
363             break;
364             case "down":
365                 $adjacentindice = $indice + 1;
366             break;
367             default:
368             throw new repository_exception('movenotdefined', 'repository');
369         }
371         //switch sortorder of this type and the adjacent type
372         //TODO: we could reset sortorder for all types. This is not as good in performance term, but
373         //that prevent from wrong behaviour on a screwed database. As performance are not important in this particular case
374         //it worth to change the algo.
375         if ($adjacentindice>=0 && !empty($types[$adjacentindice])) {
376             $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$types[$adjacentindice]->get_typename()));
377             $this->update_sortorder($types[$adjacentindice]->get_sortorder());
378         }
379     }
381     /**
382      * 1. Switch the visibility OFF if it's ON, and ON if it's OFF.
383      * 2. Update the type
384      * @return boolean
385      */
386     public function switch_and_update_visibility() {
387         $this->_visible = !$this->_visible;
388         return $this->update_visible();
389     }
392     /**
393      * Delete a repository_type (general options are removed from config_plugin
394      * table, and all instances are deleted)
395      * @global object $DB
396      * @return boolean
397      */
398     public function delete() {
399         global $DB;
401         //delete all instances of this type
402         $instances = repository::get_instances(array(),null,false,$this->_typename);
403         foreach ($instances as $instance) {
404             $instance->delete();
405         }
407         //delete all general options
408         foreach ($this->_options as $name => $value) {
409             set_config($name, null, $this->_typename);
410         }
412         return $DB->delete_records('repository', array('type' => $this->_typename));
413     }
416 /**
417  * This is the base class of the repository class
418  *
419  * To use repository plugin, see:
420  * http://docs.moodle.org/en/Development:Repository_How_to_Create_Plugin
421  *
422  * class repository is an abstract class, some functions must be implemented in subclass.
423  *
424  * See an example: repository/boxnet/repository.class.php
425  *
426  * A few notes:
427  *   // for ajax file picker, this will print a json string to tell file picker
428  *   // how to build a login form
429  *   $repo->print_login();
430  *   // for ajax file picker, this will return a files list.
431  *   $repo->get_listing();
432  *   // this function will be used for non-javascript version.
433  *   $repo->print_listing();
434  *   // print a search box
435  *   $repo->print_search();
436  *
437  * @package repository
438  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
439  */
440 abstract class repository {
441     // $disabled can be set to true to disable a plugin by force
442     // example: self::$disabled = true
443     public $disabled = false;
444     public $id;
445     public $context;
446     public $options;
447     public $readonly;
449     /**
450      * Return a type for a given type name.
451      * @global object $DB
452      * @param string $typename the type name
453      * @return integer
454      */
455     public static function get_type_by_typename($typename) {
456         global $DB;
458         if (!$record = $DB->get_record('repository',array('type' => $typename))) {
459             return false;
460         }
462         return new repository_type($typename, (array)get_config($typename), $record->visible, $record->sortorder);
463     }
465     /**
466      * Return a type for a given type id.
467      * @global object $DB
468      * @param string $typename the type name
469      * @return integer
470      */
471     public static function get_type_by_id($id) {
472         global $DB;
474         if (!$record = $DB->get_record('repository',array('id' => $id))) {
475             return false;
476         }
478         return new repository_type($record->type, (array)get_config($record->type), $record->visible, $record->sortorder);
479     }
481     /**
482      * Return all repository types ordered by sortorder
483      * first type in returnedarray[0], second type in returnedarray[1], ...
484      * @global object $DB
485      * @param boolean $visible can return types by visiblity, return all types if null
486      * @return array Repository types
487      */
488     public static function get_types($visible=null) {
489         global $DB;
491         $types = array();
492         $params = null;
493         if (!empty($visible)) {
494             $params = array('visible' => $visible);
495         }
496         if ($records = $DB->get_records('repository',$params,'sortorder')) {
497             foreach($records as $type) {
498                 $types[] = new repository_type($type->type, (array)get_config($type->type), $type->visible, $type->sortorder);
499             }
500         }
502         return $types;
503     }
505     /**
506      * Check context
507      * @param int $ctx_id
508      * @return boolean
509      */
510     public static function check_context($ctx_id) {
511         global $USER;
513         $context = get_context_instance_by_id($ctx_id);
514         $level = $context->contextlevel;
516         if ($level == CONTEXT_COURSE) {
517             if (!has_capability('moodle/course:view', $context)) {
518                 return false;
519             } else {
520                 return true;
521             }
522         } else if ($level == CONTEXT_USER) {
523             $c = get_context_instance(CONTEXT_USER, $USER->id);
524             if ($c->id == $ctx_id) {
525                 return true;
526             } else {
527                 return false;
528             }
529         } else if ($level == CONTEXT_SYSTEM) {
530             // it is always ok in system level
531             return true;
532         }
533         return false;
534     }
536     /**
537      * Return all types that you a user can create/edit and which are also visible
538      * Note: Mostly used in order to know if at least one editable type can be set
539      * @param object $context the context for which we want the editable types
540      * @return array types
541      */
542     public static function get_editable_types($context = null) {
544         if (empty($context)) {
545             $context = get_system_context();
546         }
548         $types= repository::get_types(true);
549         $editabletypes = array();
550         foreach ($types as $type) {
551             $instanceoptionnames = repository::static_function($type->get_typename(), 'get_instance_option_names');
552             if (!empty($instanceoptionnames)) {
553                 if ($type->get_contextvisibility($context->contextlevel)) {
554                     $editabletypes[]=$type;
555                 }
556              }
557         }
558         return $editabletypes;
559     }
561     /**
562      * Return repository instances
563      * @global object $DB
564      * @global object $CFG
565      * @global object $USER
566      * @param object $contexts contexts for which the instances are set
567      * @param integer $userid
568      * @param boolean $onlyvisible if visible == true, return visible instances only,
569      *                otherwise, return all instances
570      * @param string $type a type name to retrieve
571      * @return array repository instances
572      */
573     public static function get_instances($contexts=array(), $userid = null, $onlyvisible = true, $type=null, $accepted_types = '*', $returnvalue = '*') {
574         global $DB, $CFG, $USER;
576         $params = array();
577         $sql = 'SELECT i.*, r.type AS repositorytype, r.sortorder, r.visible FROM {repository} r, {repository_instances} i WHERE ';
578         $sql .= 'i.typeid = r.id ';
580         if (!empty($userid) && is_numeric($userid)) {
581             $sql .= ' AND (i.userid = 0 or i.userid = ?)';
582             $params[] = $userid;
583         }
585         foreach ($contexts as $context) {
586             if (empty($firstcontext)) {
587                 $firstcontext = true;
588                 $sql .= ' AND ((i.contextid = ?)';
589             } else {
590                 $sql .= ' OR (i.contextid = ?)';
591             }
592             $params[] = $context->id;
593         }
595         if (!empty($firstcontext)) {
596            $sql .=')';
597         }
599         if ($onlyvisible == true) {
600             $sql .= ' AND (r.visible = 1)';
601         }
603         if (isset($type)) {
604             $sql .= ' AND (r.type = ?)';
605             $params[] = $type;
606         }
607         $sql .= ' order by r.sortorder, i.name';
609         if (!$repos = $DB->get_records_sql($sql, $params)) {
610             $repos = array();
611         }
613         $ret = array();
614         $ft = new file_type_to_ext();
615         foreach ($repos as $repo) {
616             require_once($CFG->dirroot . '/repository/'. $repo->repositorytype.'/repository.class.php');
617             $options['visible'] = $repo->visible;
618             $options['name']    = $repo->name;
619             $options['type']    = $repo->repositorytype;
620             $options['typeid']  = $repo->typeid;
621             // tell instance what file types will be accepted by file picker
622             $options['accepted_types'] = $ft->get_file_ext($accepted_types);
623             $classname = 'repository_' . $repo->repositorytype;//
624             $is_supported = true;
626             $repository = new $classname($repo->id, $repo->contextid, $options, $repo->readonly);
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                     if ($is_supported) {
650                         $ret[] = $repository;
651                     }
652                 }
653             }
654         }
655         return $ret;
656     }
658     /**
659      * Get single repository instance
660      * @global object $DB
661      * @global object $CFG
662      * @param integer $id repository id
663      * @return object repository instance
664      */
665     public static function get_instance($id) {
666         global $DB, $CFG;
667         $sql = 'SELECT i.*, r.type AS repositorytype, r.visible FROM {repository} r, {repository_instances} i WHERE ';
668         $sql .= 'i.typeid = r.id AND ';
669         $sql .= 'i.id = '.$id;
671         if(!$instance = $DB->get_record_sql($sql)) {
672             return false;
673         }
674         require_once($CFG->dirroot . '/repository/'. $instance->repositorytype
675                 . '/repository.class.php');
676         $classname = 'repository_' . $instance->repositorytype;
677         $options['typeid'] = $instance->typeid;
678         $options['type']   = $instance->repositorytype;
679         $options['name']   = $instance->name;
680         $obj = new $classname($instance->id, $instance->contextid, $options, $instance->readonly);
681         if (empty($obj->super_called)) {
682             debugging('parent::__construct must be called by '.$classname.' plugin.');
683         }
684         return $obj;
685     }
687     /**
688      * call a static function
689      * @global object $CFG
690      * @param string $plugin
691      * @param string $function
692      * @param type $nocallablereturnvalue default value if function not found
693      *             it's mostly used when you don't want to display an error but
694      *             return a boolean
695      * @return mixed
696      */
697     public static function static_function($plugin, $function) {
698         global $CFG;
700         //check that the plugin exists
701         $typedirectory = $CFG->dirroot . '/repository/'. $plugin . '/repository.class.php';
702         if (!file_exists($typedirectory)) {
703             //throw new repository_exception('invalidplugin', 'repository');
704             return false;
705         }
707         $pname = null;
708         if (is_object($plugin) || is_array($plugin)) {
709             $plugin = (object)$plugin;
710             $pname = $plugin->name;
711         } else {
712             $pname = $plugin;
713         }
715         $args = func_get_args();
716         if (count($args) <= 2) {
717             $args = array();
718         }
719         else {
720             array_shift($args);
721             array_shift($args);
722         }
724         require_once($typedirectory);
725         return call_user_func_array(array('repository_' . $plugin, $function), $args);
726     }
728     /**
729      * Move file from download folder to file pool using FILE API
730      * @global object $DB
731      * @global object $CFG
732      * @global object $USER
733      * @param string $path file path in download folder
734      * @param string $name file name
735      * @param integer $itemid item id to identify a file in filepool
736      * @param string $filearea file area
737      * @return array information of file in file pool
738      */
739     public static function move_to_filepool($path, $name, $itemid, $filearea = 'user_draft') {
740         global $DB, $CFG, $USER;
741         $context = get_context_instance(CONTEXT_USER, $USER->id);
742         $now = time();
743         $entry = new object();
744         $entry->filearea  = $filearea;
745         $entry->contextid = $context->id;
746         $entry->filename  = $name;
747         //$entry->filepath  = '/'.uniqid().'/';
748         $entry->filepath  = '/';
749         $entry->timecreated  = $now;
750         $entry->timemodified = $now;
751         $entry->userid       = $USER->id;
752         $entry->mimetype     = mimeinfo('type', $path);
753         if(is_numeric($itemid)) {
754             $entry->itemid = $itemid;
755         } else {
756             $entry->itemid = 0;
757         }
758         $fs = get_file_storage();
759         $browser = get_file_browser();
760         if ($existingfile = $fs->get_file($context->id, $filearea, $itemid, $path, $name)) {
761             $existingfile->delete();
762         }
763         if ($file = $fs->create_file_from_pathname($entry, $path)) {
764             if (empty($CFG->repository_no_delete)) {
765                 $delete = unlink($path);
766                 unset($CFG->repository_no_delete);
767             }
768             $ret = $browser->get_file_info($context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
769             if(!empty($ret)) {
770                 return array('url'=>$ret->get_url(),
771                     'id'=>$file->get_itemid(),
772                     'file'=>$file->get_filename(),
773                     'icon'=>$CFG->pixpath.'/f/'.mimeinfo('icon32', $path)
774                 );
775             } else {
776                 return null;
777             }
778         } else {
779             return null;
780         }
781     }
783     /**
784      * Save file to local filesystem pool
785      * @param string $elname name of element
786      * @param string $filearea
787      * @param string $filepath
788      * @param string $filename - use specified filename, if not specified name of uploaded file used
789      * @param bool $override override file if exists
790      * @return mixed stored_file object or false if error; may throw exception if duplicate found
791      */
792     public static function store_to_filepool($elname, $filearea='user_draft', $filepath='/', $itemid='', $filename = '', $override = false) {
793         global $USER;
794         if (!isset($_FILES[$elname])) {
795             return false;
796         }
798         if (!$filename) {
799             $filename = $_FILES[$elname]['name'];
800         }
801         $context = get_context_instance(CONTEXT_USER, $USER->id);
802         if (empty($itemid)) {
803             $itemid = (int)substr(hexdec(uniqid()), 0, 9)+rand(1,100);
804         }
805         $fs = get_file_storage();
806         $browser = get_file_browser();
808         if ($file = $fs->get_file($context->id, $filearea, $itemid, $filepath, $filename)) {
809             if ($override) {
810                 $file->delete();
811             } else {
812                 return false;
813             }
814         }
816         $file_record = new object();
817         $file_record->contextid = $context->id;
818         $file_record->filearea  = $filearea;
819         $file_record->itemid    = $itemid;
820         $file_record->filepath  = $filepath;
821         $file_record->filename  = $filename;
822         $file_record->userid    = $USER->id;
824         $file = $fs->create_file_from_pathname($file_record, $_FILES[$elname]['tmp_name']);
825         $info = $browser->get_file_info($context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
826         $ret = array('url'=>$info->get_url(),'id'=>$itemid, 'file'=>$file->get_filename());
827         return $ret;
828     }
830     /**
831      * Return the user files tree in a format to be returned by the function get_listing
832      * @global object $CFG
833      * @param string $search
834      * @return array
835      */
836     public static function get_user_file_tree($search = ""){
837         global $CFG;
838         $ret = array();
839         $ret['nologin'] = true;
840         $ret['manage'] = $CFG->wwwroot .'/files/index.php'; // temporary
841         $browser = get_file_browser();
842         $itemid = null;
843         $filename = null;
844         $filearea = null;
845         $path = '/';
846         $ret['dynload'] = false;
848         if ($fileinfo = $browser->get_file_info(get_system_context(), $filearea, $itemid, $path, $filename)) {
850             $ret['path'] = array();
851             $params = $fileinfo->get_params();
852             $filearea = $params['filearea'];
853             $ret['path'][] = repository::encode_path($filearea, $path, $fileinfo->get_visible_name());
854             if ($fileinfo->is_directory()) {
855                 $level = $fileinfo->get_parent();
856                 while ($level) {
857                     $params = $level->get_params();
858                     $ret['path'][] = repository::encode_path($params['filearea'], $params['filepath'], $level->get_visible_name());
859                     $level = $level->get_parent();
860                 }
861             }
862             $filecount = repository::build_tree($fileinfo, $search, $ret['dynload'], $ret['list']);
863             $ret['path'] = array_reverse($ret['path']);
864         }
866         if (empty($ret['list'])) {
867             //exit(mnet_server_fault(9016, get_string('emptyfilelist', 'repository_local')));
868             throw new Exception('emptyfilelist');
869         } else {
870             return $ret;
871         }
873     }
875     /**
876      *
877      * @param string $filearea
878      * @param string $path
879      * @param string $visiblename
880      * @return array
881      */
882     public static function encode_path($filearea, $path, $visiblename) {
883         return array('path'=>serialize(array($filearea, $path)), 'name'=>$visiblename);
884     }
886     /**
887      * Builds a tree of files This function is
888      * then called recursively.
889      *
890      * @param $fileinfo an object returned by file_browser::get_file_info()
891      * @param $search searched string
892      * @param $dynamicmode bool no recursive call is done when in dynamic mode
893      * @param $list - the array containing the files under the passed $fileinfo
894      * @returns int the number of files found
895      *
896      * todo: take $search into account, and respect a threshold for dynamic loading
897      */
898     public static function build_tree($fileinfo, $search, $dynamicmode, &$list) {
899         global $CFG;
901         $filecount = 0;
902         $children = $fileinfo->get_children();
904         foreach ($children as $child) {
905             $filename = $child->get_visible_name();
906             $filesize = $child->get_filesize();
907             $filesize = $filesize ? display_size($filesize) : '';
908             $filedate = $child->get_timemodified();
909             $filedate = $filedate ? userdate($filedate) : '';
910             $filetype = $child->get_mimetype();
912             if ($child->is_directory()) {
913                 $path = array();
914                 $level = $child->get_parent();
915                 while ($level) {
916                     $params = $level->get_params();
917                     $path[] = repository::encode_path($params['filearea'], $params['filepath'], $level->get_visible_name());
918                     $level = $level->get_parent();
919                 }
921                 $tmp = array(
922                     'title' => $child->get_visible_name(),
923                     'size' => 0,
924                     'date' => $filedate,
925                     'path' => array_reverse($path),
926                     'thumbnail' => $CFG->pixpath .'/f/folder-32.png'
927                 );
929                 //if ($dynamicmode && $child->is_writable()) {
930                 //    $tmp['children'] = array();
931                 //} else {
932                     // if folder name matches search, we send back all files contained.
933                 $_search = $search;
934                 if ($search && stristr($tmp['title'], $search) !== false) {
935                     $_search = false;
936                 }
937                 $tmp['children'] = array();
938                 $_filecount = repository::build_tree($child, $_search, $dynamicmode, $tmp['children']);
939                 if ($search && $_filecount) {
940                     $tmp['expanded'] = 1;
941                 }
943                 //}
945                 //Uncomment this following line if you wanna display all directory ()even empty
946                 if (!$search || $_filecount || (stristr($tmp['title'], $search) !== false)) {
947                     $filecount += $_filecount;
948                     $list[] = $tmp;
949                 }
951             } else { // not a directory
952                 // skip the file, if we're in search mode and it's not a match
953                 if ($search && (stristr($filename, $search) === false)) {
954                     continue;
955                 }
956                 $params = $child->get_params();
957                 $source = serialize(array($params['contextid'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']));
958                 $list[] = array(
959                     'title' => $filename,
960                     'size' => $filesize,
961                     'date' => $filedate,
962                     //'source' => $child->get_url(),
963                     'source' => base64_encode($source),
964                     'thumbnail' => $CFG->pixpath .'/f/'. mimeinfo('icon32', $filename)
965                 );
966                 $filecount++;
967             }
968         }
970         return $filecount;
971     }
974     /**
975      * Display a repository instance list (with edit/delete/create links)
976      * @global object $CFG
977      * @global object $USER
978      * @param object $context the context for which we display the instance
979      * @param string $typename if set, we display only one type of instance
980      */
981     public static function display_instances_list($context, $typename = null) {
982         global $CFG, $USER;
984         $output = print_box_start('generalbox','',true);
985         //if the context is SYSTEM, so we call it from administration page
986         $admin = ($context->id == SYSCONTEXTID) ? true : false;
987         if ($admin) {
988             $baseurl = "$CFG->httpswwwroot/$CFG->admin/repositoryinstance.php?sesskey=" . sesskey();
989             $output .= "<div ><h2 style='text-align: center'>" . get_string('siteinstances', 'repository') . " ";
990             $output .= "</h2></div>";
991         } else {
992             $baseurl = $CFG->httpswwwroot . '/repository/manage_instances.php?contextid=' . $context->id . '&amp;sesskey=' . sesskey();
993         }
995         $namestr = get_string('name');
996         $pluginstr = get_string('plugin', 'repository');
997         $settingsstr = get_string('settings');
998         $deletestr = get_string('delete');
999         $updown = get_string('updown', 'repository');
1000         //retrieve list of instances. In administration context we want to display all
1001         //instances of a type, even if this type is not visible. In course/user context we
1002         //want to display only visible instances, but for every type types. The repository_get_instances()
1003         //third parameter displays only visible type.
1004         $instances = repository::get_instances(array($context),null,!$admin,$typename);
1005         $instancesnumber = count($instances);
1006         $alreadyplugins = array();
1008         $table = new StdClass;
1009         $table->head = array($namestr, $pluginstr, $deletestr, $settingsstr);
1010         $table->align = array('left', 'left', 'center','center');
1011         $table->data = array();
1013         $updowncount = 1;
1015         foreach ($instances as $i) {
1016             $settings = '';
1017             $delete = '';
1018             $settings .= '<a href="' . $baseurl . '&amp;type='.$typename.'&amp;edit=' . $i->id . '">' . $settingsstr . '</a>' . "\n";
1019             if (!$i->readonly) {
1020                 $delete .= '<a href="' . $baseurl . '&amp;type='.$typename.'&amp;delete=' .  $i->id . '">' . $deletestr . '</a>' . "\n";
1021             }
1023             $type = repository::get_type_by_id($i->typeid);
1024             $table->data[] = array($i->name, $type->get_readablename(), $delete, $settings);
1026             //display a grey row if the type is defined as not visible
1027             if (isset($type) && !$type->get_visible()) {
1028                 $table->rowclass[] = 'dimmed_text';
1029             } else {
1030                 $table->rowclass[] = '';
1031             }
1033             if (!in_array($i->name, $alreadyplugins)) {
1034                 $alreadyplugins[] = $i->name;
1035             }
1036         }
1037         $output .= print_table($table, true);
1038         $instancehtml = '<div>';
1039         $addable = 0;
1041         //if no type is set, we can create all type of instance
1042         if (!$typename) {
1043             $instancehtml .= '<h3>';
1044             $instancehtml .= get_string('createrepository', 'repository');
1045             $instancehtml .= '</h3><ul>';
1046             $types = repository::get_editable_types($context);
1047             foreach ($types as $type) {
1048                 if (!empty($type) && $type->get_visible()) {
1049                     $instanceoptionnames = repository::static_function($type->get_typename(), 'get_instance_option_names');
1050                     if (!empty($instanceoptionnames)) {
1051                         $instancehtml .= '<li><a href="'.$baseurl.'&amp;new='.$type->get_typename().'">'.get_string('createxxinstance', 'repository', get_string('repositoryname', 'repository_'.$type->get_typename()))
1052                             .'</a></li>';
1053                         $addable++;
1054                     }
1055                 }
1056             }
1057             $instancehtml .= '</ul>';
1059         } else {
1060             $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
1061             if (!empty($instanceoptionnames)) {   //create a unique type of instance
1062                 $addable = 1;
1063                 $instancehtml .= "<form action='".$baseurl."&amp;new=".$typename."' method='post'>
1064                     <p style='text-align:center'><input type='submit' value='".get_string('createinstance', 'repository')."'/></p>
1065                     </form>";
1066             }
1067         }
1069         if ($addable) {
1070             $instancehtml .= '</div>';
1071             $output .= $instancehtml;
1072         }
1074         $output .= print_box_end(true);
1076         //print the list + creation links
1077         print($output);
1078     }
1079     /**
1080      * 1. Initialize context and options
1081      * 2. Accept necessary parameters
1082      *
1083      * @param integer $repositoryid
1084      * @param integer $contextid
1085      * @param array $options
1086      */
1087     public function __construct($repositoryid, $contextid = SITEID, $options = array(), $readonly = 0) {
1088         $this->id = $repositoryid;
1089         $this->context = get_context_instance_by_id($contextid);
1090         $this->readonly = $readonly;
1091         $this->options = array();
1092         if (is_array($options)) {
1093             $options = array_merge($this->get_option(), $options);
1094         } else {
1095             $options = $this->get_option();
1096         }
1097         $this->options = array();
1098         foreach ($options as $n => $v) {
1099             $this->options[$n] = $v;
1100         }
1101         $this->name = $this->get_name();
1102         $this->super_called = true;
1103     }
1105     /**
1106      * set options for repository instance
1107      *
1108      * @param string $name
1109      * @param mixed $value
1110      */
1111     public function __set($name, $value) {
1112         $this->options[$name] = $value;
1113     }
1115     /**
1116      * get options for repository instance
1117      *
1118      * @param string $name
1119      * @return mixed
1120      */
1121     public function __get($name) {
1122         if (array_key_exists($name, $this->options)) {
1123             return $this->options[$name];
1124         }
1125         trigger_error('Undefined property: '.$name, E_USER_NOTICE);
1126         return null;
1127     }
1129     /**
1130      * test option name
1131      *
1132      * @param string name
1133      */
1134     public function __isset($name) {
1135         return isset($this->options[$name]);
1136     }
1138     /**
1139      * Return the name of the repository class
1140      * @return string
1141      */
1142     public function __toString() {
1143         return 'Repository class: '.__CLASS__;
1144     }
1146     /**
1147      * Decide where to save the file, can be
1148      * reused by sub class
1149      * @param string filename
1150      */
1151     public function prepare_file($file) {
1152         global $CFG;
1153         if (!file_exists($CFG->dataroot.'/temp/download')) {
1154             mkdir($CFG->dataroot.'/temp/download/', 0777, true);
1155         }
1156         if (is_dir($CFG->dataroot.'/temp/download')) {
1157             $dir = $CFG->dataroot.'/temp/download/';
1158         }
1159         if (empty($file)) {
1160             $file = uniqid('repo').'_'.time().'.tmp';
1161         }
1162         if (file_exists($dir.$file)) {
1163             $file = uniqid('m').$file;
1164         }
1165         return $dir.$file;
1166     }
1168     /**
1169      * Download a file, this function can be overridden by
1170      * subclass.
1171      *
1172      * @global object $CFG
1173      * @param string $url the url of file
1174      * @param string $file save location
1175      * @return string the location of the file
1176      * @see curl package
1177      */
1178     public function get_file($url, $file = '') {
1179         global $CFG;
1181         $path = $this->prepare_file($file);
1182         $fp = fopen($path, 'w');
1183         $c = new curl;
1184         $c->download(array(
1185                     array('url'=>$url, 'file'=>$fp)
1186                     ));
1187         return $path;
1188     }
1190     /**
1191      * Return is the instance is visible
1192      * (is the type visible ? is the context enable ?)
1193      * @return boolean
1194      */
1195     public function is_visible() {
1196         $type = repository::get_type_by_id($this->typeid);
1197         $instanceoptions = repository::static_function($type->get_typename(), 'get_instance_option_names');
1199         if ($type->get_visible()) {
1200             //if the instance is unique so it's visible, otherwise check if the instance has a enabled context
1201             if (empty($instanceoptions) || $type->get_contextvisibility($this->context->contextlevel)) {
1202                 return true;
1203             }
1204         }
1206         return false;
1207     }
1209     /**
1210      * Return the name of this instance, can be overridden.
1211      * @global object $DB
1212      * @return string
1213      */
1214     public function get_name() {
1215         global $DB;
1216         // We always verify instance id from database,
1217         // so we always know repository name before init
1218         // a repository, so we don't enquery repository
1219         // name from database again here.
1220         if (isset($this->options['name'])) {
1221             return $this->options['name'];
1222         } else {
1223             if ( $repo = $DB->get_record('repository_instances', array('id'=>$this->id)) ) {
1224                 return $repo->name;
1225             } else {
1226                 return '';
1227             }
1228         }
1229     }
1231     /**
1232      * what kind of files will be in this repository?
1233      * @return array return '*' means this repository support any files, otherwise
1234      *               return mimetypes of files, it can be an array
1235      */
1236     public function supported_filetypes() {
1237         // return array('text/plain', 'image/gif');
1238         return '*';
1239     }
1241     /**
1242      * does it return a file url or a item_id
1243      * @return string
1244      */
1245     public function supported_return_value() {
1246         // return 'link';
1247         // return 'ref_id';
1248         return 'ref_id';
1249     }
1251     /**
1252      * Provide repository instance information for Ajax
1253      * @global object $CFG
1254      * @return object
1255      */
1256     final public function ajax_info() {
1257         global $CFG;
1258         $ft = new file_type_to_ext;
1259         $repo = new stdclass;
1260         $repo->id   = $this->id;
1261         $repo->name = $this->get_name();
1262         $repo->type = $this->options['type'];
1263         $repo->icon = $CFG->httpswwwroot.'/repository/'.$repo->type.'/icon.png';
1264         $repo->supported_types = $ft->get_file_ext($this->supported_filetypes());
1265         $repo->accepted_types = $this->accepted_types;
1266         return $repo;
1267     }
1269     /**
1270      * Create an instance for this plug-in
1271      * @global object $CFG
1272      * @global object $DB
1273      * @param string $type the type of the repository
1274      * @param integer $userid the user id
1275      * @param object $context the context
1276      * @param array $params the options for this instance
1277      * @return mixed
1278      */
1279     final public static function create($type, $userid, $context, $params, $readonly=0) {
1280         global $CFG, $DB;
1281         $params = (array)$params;
1282         require_once($CFG->dirroot . '/repository/'. $type . '/repository.class.php');
1283         $classname = 'repository_' . $type;
1284         if ($repo = $DB->get_record('repository', array('type'=>$type))) {
1285             $record = new stdclass;
1286             $record->name = $params['name'];
1287             $record->typeid = $repo->id;
1288             $record->timecreated  = time();
1289             $record->timemodified = time();
1290             $record->contextid = $context->id;
1291             $record->readonly = $readonly;
1292             $record->userid    = $userid;
1293             $id = $DB->insert_record('repository_instances', $record);
1294             $options = array();
1295             $configs = call_user_func($classname . '::get_instance_option_names');
1296             if (!empty($configs)) {
1297                 foreach ($configs as $config) {
1298                     $options[$config] = $params[$config];
1299                 }
1300             }
1302             if (!empty($id)) {
1303                 unset($options['name']);
1304                 $instance = repository::get_instance($id);
1305                 $instance->set_option($options);
1306                 return $id;
1307             } else {
1308                 return null;
1309             }
1310         } else {
1311             return null;
1312         }
1313     }
1315     /**
1316      * delete a repository instance
1317      * @global object $DB
1318      * @return mixed
1319      */
1320     final public function delete() {
1321         global $DB;
1322         $DB->delete_records('repository_instances', array('id'=>$this->id));
1323         $DB->delete_records('repository_instance_config', array('instanceid'=>$this->id));
1324         return true;
1325     }
1327     /**
1328      * Hide/Show a repository
1329      * @global object $DB
1330      * @param string $hide
1331      * @return boolean
1332      */
1333     final public function hide($hide = 'toggle') {
1334         global $DB;
1335         if ($entry = $DB->get_record('repository', array('id'=>$this->id))) {
1336             if ($hide === 'toggle' ) {
1337                 if (!empty($entry->visible)) {
1338                     $entry->visible = 0;
1339                 } else {
1340                     $entry->visible = 1;
1341                 }
1342             } else {
1343                 if (!empty($hide)) {
1344                     $entry->visible = 0;
1345                 } else {
1346                     $entry->visible = 1;
1347                 }
1348             }
1349             return $DB->update_record('repository', $entry);
1350         }
1351         return false;
1352     }
1354     /**
1355      * Cache login details for repositories
1356      * @global object $DB
1357      * @param string $username
1358      * @param string $password
1359      * @param integer $userid The id of specific user
1360      * @return integer Id of the record
1361      */
1362     public function store_login($username = '', $password = '', $userid = 1) {
1363         global $DB;
1365         $repository = new stdclass;
1366         if (!empty($this->id)) {
1367             $repository->id = $this->id;
1368         } else {
1369             $repository->userid         = $userid;
1370             $repository->repositorytype = $this->type;
1371             $repository->contextid      = $this->context->id;
1372         }
1373         if ($entry = $DB->get_record('repository', $repository)) {
1374             $repository->id = $entry->id;
1375             $repository->username = $username;
1376             $repository->password = $password;
1377             return $DB->update_record('repository', $repository);
1378         } else {
1379             $repository->username = $username;
1380             $repository->password = $password;
1381             return $DB->insert_record('repository', $repository);
1382         }
1383     }
1385     /**
1386      * Save settings for repository instance
1387      * $repo->set_option(array('api_key'=>'f2188bde132', 'name'=>'dongsheng'));
1388      * @global object $DB
1389      * @param array $options settings
1390      * @return int Id of the record
1391      */
1392     public function set_option($options = array()) {
1393         global $DB;
1395         if (!empty($options['name'])) {
1396             $r = new object();
1397             $r->id   = $this->id;
1398             $r->name = $options['name'];
1399             $DB->update_record('repository_instances', $r);
1400             unset($options['name']);
1401         }
1402         $result = true;
1403         foreach ($options as $name=>$value) {
1404             if ($id = $DB->get_field('repository_instance_config', 'id', array('name'=>$name, 'instanceid'=>$this->id))) {
1405                 if ($value===null) {
1406                     $result = $result && $DB->delete_records('repository_instance_config', array('name'=>$name, 'instanceid'=>$this->id));
1407                 } else {
1408                     $result = $result && $DB->set_field('repository_instance_config', 'value', $value, array('id'=>$id));
1409                 }
1410             } else {
1411                 if ($value===null) {
1412                     return true;
1413                 }
1414                 $config = new object();
1415                 $config->instanceid = $this->id;
1416                 $config->name   = $name;
1417                 $config->value  = $value;
1418                 $result = $result && $DB->insert_record('repository_instance_config', $config);
1419             }
1420         }
1421         return $result;
1422     }
1424     /**
1425      * Get settings for repository instance
1426      * @global object $DB
1427      * @param string $config
1428      * @return array Settings
1429      */
1430     public function get_option($config = '') {
1431         global $DB;
1432         $entries = $DB->get_records('repository_instance_config', array('instanceid'=>$this->id));
1433         $ret = array();
1434         if (empty($entries)) {
1435             return $ret;
1436         }
1437         foreach($entries as $entry) {
1438             $ret[$entry->name] = $entry->value;
1439         }
1440         if (!empty($config)) {
1441             return $ret[$config];
1442         } else {
1443             return $ret;
1444         }
1445     }
1447     public function filter(&$value) {
1448         $pass = false;
1449         $accepted_types = optional_param('accepted_types', '', PARAM_RAW);
1450         $ft = new file_type_to_ext;
1451         $ext = $ft->get_file_ext($this->supported_filetypes());
1452         if (isset($value['children'])) {
1453             $pass = true;
1454             if (!empty($value['children'])) {
1455                 $value['children'] = array_filter($value['children'], array($this, 'filter'));
1456             }
1457         } else {
1458             if ($accepted_types == '*' or empty($accepted_types)
1459                 or (is_array($accepted_types) and in_array('*', $accepted_types))) {
1460                 $pass = true;
1461             } elseif (is_array($accepted_types)) {
1462                 foreach ($accepted_types as $type) {
1463                     if (preg_match('#'.$type.'$#', $value['title'])) {
1464                         $pass = true;
1465                     }
1466                 }
1467             }
1468         }
1469         return $pass;
1470     }
1472     /**
1473      * Given a path, and perhaps a search, get a list of files.
1474      *
1475      * See details on http://docs.moodle.org/en/Development:Repository_plugins
1476      *
1477      * @param string $parent The parent path, this parameter can
1478      * a folder name, or a identification of folder
1479      * @return array the list of files, including meta infomation
1480      */
1481     public function get_listing($path = '', $page = '') {
1482     }
1484     /**
1485      * Search files in repository
1486      * When doing global search, $search_text will be used as
1487      * keyword.
1488      *
1489      * @return mixed, see get_listing()
1490      */
1491     public function search($search_text) {
1492         $list = array();
1493         $list['list'] = array();
1494         return false;
1495     }
1497     /**
1498      * Logout from repository instance
1499      * By default, this function will return a login form
1500      *
1501      * @return string
1502      */
1503     public function logout(){
1504         return $this->print_login();
1505     }
1507     /**
1508      * To check whether the user is logged in.
1509      *
1510      * @return boolean
1511      */
1512     public function check_login(){
1513         return true;
1514     }
1517     /**
1518      * Show the login screen, if required
1519      */
1520     public function print_login(){
1521         return $this->get_listing();
1522     }
1524     /**
1525      * Show the search screen, if required
1526      * @return null
1527      */
1528     public function print_search() {
1529         $str = '';
1530         $str .= '<input type="hidden" name="repo_id" value="'.$this->id.'" />';
1531         $str .= '<input type="hidden" name="ctx_id" value="'.$this->context->id.'" />';
1532         $str .= '<input type="hidden" name="seekey" value="'.sesskey().'" />';
1533         $str .= '<label>'.get_string('keyword', 'repository').': </label><br/><input name="s" value="" /><br/>';
1534         return $str;
1535     }
1537     /**
1538      * is it possible to do glboal search?
1539      * @return boolean
1540      */
1541     public function global_search() {
1542         return false;
1543     }
1545     /**
1546      * Defines operations that happen occasionally on cron
1547      * @return boolean
1548      */
1549     public function cron() {
1550         return true;
1551     }
1553     /**
1554      * function which is run when the type is created (moodle administrator add the plugin)
1555      * @return boolean success or fail?
1556      */
1557     public static function plugin_init() {
1558         return true;
1559     }
1561     /**
1562      * Edit/Create Admin Settings Moodle form
1563      * @param object $ Moodle form (passed by reference)
1564      */
1565      public function type_config_form(&$mform) {
1566     }
1568       /**
1569      * Edit/Create Instance Settings Moodle form
1570      * @param object $ Moodle form (passed by reference)
1571      */
1572      public function instance_config_form(&$mform) {
1573     }
1575     /**
1576      * Return names of the general options
1577      * By default: no general option name
1578      * @return array
1579      */
1580     public static function get_type_option_names() {
1581         return array();
1582     }
1584     /**
1585      * Return names of the instance options
1586      * By default: no instance option name
1587      * @return array
1588      */
1589     public static function get_instance_option_names() {
1590         return array();
1591     }
1593     /**
1594      * Override it if you need to implement need mnet function
1595      * @return array
1596      */
1597      public static function mnet_publishes() {
1598         return array();
1599     }
1603 /**
1604  * exception class for repository api
1605  */
1606 class repository_exception extends moodle_exception {
1611 /**
1612  * TODO: write comment
1613  */
1614 final class repository_instance_form extends moodleform {
1615     protected $instance;
1616     protected $plugin;
1618     /**
1619      * TODO: write comment
1620      * @global object $CFG
1621      */
1622     public function definition() {
1623         global $CFG;
1624         // type of plugin, string
1625         $this->plugin = $this->_customdata['plugin'];
1626         $this->typeid = $this->_customdata['typeid'];
1627         $this->contextid = $this->_customdata['contextid'];
1628         $this->instance = (isset($this->_customdata['instance'])
1629                 && is_subclass_of($this->_customdata['instance'], 'repository'))
1630             ? $this->_customdata['instance'] : null;
1632         $mform =& $this->_form;
1633         $strrequired = get_string('required');
1635         $mform->addElement('hidden', 'edit',  ($this->instance) ? $this->instance->id : 0);
1636         $mform->addElement('hidden', 'new',   $this->plugin);
1637         $mform->addElement('hidden', 'plugin', $this->plugin);
1638         $mform->addElement('hidden', 'typeid', $this->typeid);
1639         $mform->addElement('hidden', 'contextid', $this->contextid);
1641         $mform->addElement('text', 'name', get_string('name'), 'maxlength="100" size="30"');
1642         $mform->addRule('name', $strrequired, 'required', null, 'client');
1645         //add fields
1646         if (!$this->instance) {
1647             $result = repository::static_function($this->plugin, 'instance_config_form', $mform);
1648         }
1649         else {
1650             $data = array();
1651             $data['name'] = $this->instance->name;
1652             if (!$this->instance->readonly) {
1653                 $result = $this->instance->instance_config_form($mform);
1654                 // and set the data if we have some.
1655                 foreach ($this->instance->get_instance_option_names() as $config) {
1656                     if (!empty($this->instance->$config)) {
1657                         $data[$config] = $this->instance->$config;
1658                      } else {
1659                         $data[$config] = '';
1660                      }
1661                 }
1662             }
1663             $this->set_data($data);
1664         }
1666         $this->add_action_buttons(true, get_string('save','repository'));
1667     }
1669     /**
1670      * TODO: write comment
1671      * @global object $DB
1672      * @param mixed $data
1673      * @return mixed
1674      */
1675     public function validation($data) {
1676         global $DB;
1678         $errors = array();
1679         $sql = "SELECT count('x') FROM {repository_instances} i, {repository} r WHERE r.type=:plugin AND r.id=i.typeid AND i.name=:name"; 
1680         if ($DB->count_records_sql($sql, array('name' => $data['name'], 'plugin' => $data['plugin'])) > 1) {
1681             $errors = array('name' => get_string('err_uniquename', 'repository'));
1682         }
1684         return $errors;
1685     }
1689 /**
1690  * Display a form with the general option fields of a type
1691  */
1692 final class repository_type_form extends moodleform {
1693     protected $instance;
1694     protected $plugin;
1696     /**
1697      * Definition of the moodleform
1698      * @global object $CFG
1699      */
1700     public function definition() {
1701         global $CFG;
1702         // type of plugin, string
1703         $this->plugin = $this->_customdata['plugin'];
1704         $this->instance = (isset($this->_customdata['instance'])
1705                 && is_a($this->_customdata['instance'], 'repository_type'))
1706             ? $this->_customdata['instance'] : null;
1708         $mform =& $this->_form;
1709         $strrequired = get_string('required');
1711         $mform->addElement('hidden', 'edit',  ($this->instance) ? $this->instance->get_typename() : 0);
1712         $mform->addElement('hidden', 'new',   $this->plugin);
1713         $mform->addElement('hidden', 'plugin', $this->plugin);
1715         // let the plugin add its specific fields
1716         if (!$this->instance) {
1717             $result = repository::static_function($this->plugin, 'type_config_form', $mform);
1718         } else {
1719             $classname = 'repository_' . $this->instance->get_typename();
1720             $result = call_user_func(array($classname, 'type_config_form'), $mform);
1721         }
1723         //add "enable course/user instances" checkboxes if multiple instances are allowed
1724         $instanceoptionnames = repository::static_function($this->plugin, 'get_instance_option_names');
1725         if (!empty($instanceoptionnames)){
1726             $mform->addElement('checkbox', 'enablecourseinstances', get_string('enablecourseinstances', 'repository'));
1727             $mform->addElement('checkbox', 'enableuserinstances', get_string('enableuserinstances', 'repository'));
1728         }
1730         // set the data if we have some.
1731         if ($this->instance) {
1732             $data = array();
1733             $option_names = call_user_func(array($classname,'get_type_option_names'));
1734             if (!empty($instanceoptionnames)){
1735                 $option_names[] = 'enablecourseinstances';
1736                 $option_names[] = 'enableuserinstances';
1737             }
1739             $instanceoptions = $this->instance->get_options();
1740             foreach ($option_names as $config) {
1741                 if (!empty($instanceoptions[$config])) {
1742                     $data[$config] = $instanceoptions[$config];
1743                 } else {
1744                     $data[$config] = '';
1745                 }
1746             }
1747             $this->set_data($data);
1748         }
1750         $this->add_action_buttons(true, get_string('save','repository'));
1751     }
1754 function repository_setup_default_plugins() {
1755     //if the plugin type has no multiple instance (e.g. has no instance option name)
1756     //repository_type::create will create an instance automatically
1757     $local_plugin = new repository_type('local', array(), true);
1758     $local_plugin_id = $local_plugin->create(true);
1759     $upload_plugin = new repository_type('upload', array(), true);
1760     $upload_plugin_id = $upload_plugin->create(true);
1761     if (is_int($local_plugin_id) or is_int($upload_plugin_id)) {
1762         print_box(get_string('setupdefaultplugins', 'repository'));
1763     }
1764     return true;
1766 /**
1767  * Return javascript to create file picker to browse repositories
1768  * @global object $CFG
1769  * @global object $USER
1770  * @param object $context the context
1771  * @param string $id unique id for every file picker
1772  * @param string $accepted_filetypes
1773  * @param string $returnvalue the return value of file picker
1774  * @return array
1775  */
1776 function repository_get_client($context, $id = '',  $accepted_filetypes = '*', $returnvalue = '*') {
1777     global $CFG, $USER;
1779     $ft = new file_type_to_ext();
1780     $image_file_ext = json_encode($ft->get_file_ext(array('image')));
1781     $video_file_ext = json_encode($ft->get_file_ext(array('video')));
1782     $accepted_file_ext = json_encode($ft->get_file_ext($accepted_filetypes));
1784     $css = '';
1785     $js  = '';
1786     if (!isset($CFG->filepickerjsloaded)) {
1787         $css .= <<<EOD
1788 <style type="text/css">
1789 @import "$CFG->httpswwwroot/lib/yui/resize/assets/skins/sam/resize.css";
1790 @import "$CFG->httpswwwroot/lib/yui/container/assets/skins/sam/container.css";
1791 @import "$CFG->httpswwwroot/lib/yui/layout/assets/skins/sam/layout.css";
1792 @import "$CFG->httpswwwroot/lib/yui/button/assets/skins/sam/button.css";
1793 @import "$CFG->httpswwwroot/lib/yui/assets/skins/sam/treeview.css";
1794 </style>
1795 <style type="text/css">
1796 /* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
1797 /* copy from yui/examples/treeview/assets/css/folders/tree.css */
1798 /* first or middle sibling, no children */
1799 .ygtvtn { background: url($CFG->pixpath/y/tn.gif) 0 0 no-repeat; width:17px; height:22px; }
1800 /* first or middle sibling, collapsable */
1801 .ygtvtm { background: url($CFG->pixpath/y/tm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1802 /* first or middle sibling, collapsable, hover */
1803 .ygtvtmh { background: url($CFG->pixpath/y/tmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1804 /* first or middle sibling, expandable */
1805 .ygtvtp { background: url($CFG->pixpath/y/tp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1806 /* first or middle sibling, expandable, hover */
1807 .ygtvtph { background: url($CFG->pixpath/y/tph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1808 /* last sibling, no children */
1809 .ygtvln { background: url($CFG->pixpath/y/ln.gif) 0 0 no-repeat; width:17px; height:22px; }
1810 /* Last sibling, collapsable */
1811 .ygtvlm { background: url($CFG->pixpath/y/lm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1812 /* Last sibling, collapsable, hover */
1813 .ygtvlmh { background: url($CFG->pixpath/y/lmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1814 /* Last sibling, expandable */
1815 .ygtvlp { background: url($CFG->pixpath/y/lp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1816 /* Last sibling, expandable, hover */
1817 .ygtvlph { background: url($CFG->pixpath/y/lph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
1818 /* Loading icon */
1819 .ygtvloading { background: url($CFG->pixpath/y/loading.gif) 0 0 no-repeat; width:16px; height:22px; }
1820 /* the style for the empty cells that are used for rendering the depth 
1821  * of the node */
1822 .ygtvdepthcell { background: url($CFG->pixpath/y/vline.gif) 0 0 no-repeat; width:17px; height:22px; }
1823 .ygtvblankdepthcell { width:17px; height:22px; }
1824 /* the style of the div around each node */
1825 .ygtvitem { }  
1826 .ygtvitem  table{ margin-bottom:0; }
1827 .ygtvitem  td { border:none;padding:0; } 
1828 /* the style of the div around each node's collection of children */
1829 .ygtvchildren { }  
1830 * html .ygtvchildren { height:1%; }  
1831 /* the style of the text label in ygTextNode */
1832 .ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { margin-left:2px; text-decoration: none; }
1835 .file-picker{font-size:12px;}
1836 .file-picker strong{background:#FFFFCC}
1837 .file-picker a{color: #336699}
1838 .file-picker a:hover{background:#003366;color:white}
1839 .fp-panel{padding:0;margin:0; text-align:left;}
1840 .fp-login-form{text-align:center}
1841 .fp-searchbar{float:right}
1842 .fp-viewbar{width:300px;float:left}
1843 .fp-toolbar{padding: .8em;background: #FFFFCC;color:white;text-align:center;margin: 3px}
1844 .fp-toolbar a{padding: 0 .5em}
1845 .fp-list{list-style-type:none;padding:0;float:left;width:100%;margin:0;}
1846 .fp-list li{border-bottom:1px dotted gray;margin-bottom: 1em;}
1847 .fp-repo-name{display:block;padding: .5em;margin-bottom: .5em}
1848 .fp-pathbar{margin: .4em;border-bottom: 1px dotted gray;}
1849 .fp-pathbar a{padding: .4em;}
1850 .fp-rename-form{text-align:center}
1851 .fp-rename-form p{margin: 1em;}
1852 .fp-upload-form{margin: 2em 0;text-align:center}
1853 .fp-upload-btn a{cursor: default;background: white;border:1px solid gray;color:black;padding: .5em}
1854 .fp-upload-btn a:hover {background: grey;color:white}
1855 .fp-paging{margin:1em .5em; clear:both;text-align:center;line-height: 2.5em;}
1856 .fp-paging a{padding: .5em;border: 1px solid #CCC}
1857 .fp-paging a.cur_page{border: 1px solid blue}
1858 .fp-popup{text-align:center}
1859 .fp-grid{float:left;text-align:center;}
1860 .fp-grid div{overflow: hidden}
1861 .fp-grid p{margin:0;padding:0;background: #FFFFCC}
1862 .fp-grid .label{height:48px;text-align:center}
1863 .fp-grid span{color:gray}
1864 </style>
1866 <!--[if IE 6]>
1867 <style type="text/css">
1868 /* Fix for IE6 */
1869 .yui-skin-sam .yui-panel .hd{}
1870 </style>
1871 <![endif]-->
1872 EOD;
1874         require_js(array(
1875             'yui_yahoo',
1876             'yui_dom',
1877             'yui_event',
1878             'yui_element',
1879             'yui_treeview',
1880             'yui_dragdrop',
1881             'yui_container',
1882             'yui_resize',
1883             'yui_layout',
1884             'yui_connection',
1885             'yui_json',
1886             'yui_button',
1887             'yui_selector',
1888             'repository/repository.src.js'
1889         ));
1890         $lang = array();
1891         $lang['title'] = get_string('title', 'repository');
1892         $lang['preview'] = get_string('preview', 'repository');
1893         $lang['add']     = get_string('add', 'repository');
1894         $lang['back']      = get_string('back', 'repository');
1895         $lang['cancel']    = get_string('cancel');
1896         $lang['close']     = get_string('close', 'repository');
1897         $lang['ccache']    = get_string('cleancache', 'repository');
1898         $lang['copying']   = get_string('copying', 'repository');
1899         $lang['downbtn']   = get_string('getfile', 'repository');
1900         $lang['download']  = get_string('downloadsucc', 'repository');
1901         $lang['date']      = get_string('date', 'repository').': ';
1902         $lang['error']     = get_string('error', 'repository');
1903         $lang['filenotnull'] = get_string('filenotnull', 'repository');
1904         $lang['federatedsearch'] = get_string('federatedsearch', 'repository');
1905         $lang['help']      = get_string('help');
1906         $lang['refresh']   = get_string('refresh', 'repository');
1907         $lang['invalidjson'] = get_string('invalidjson', 'repository');
1908         $lang['listview']  = get_string('listview', 'repository');
1909         $lang['login']     = get_string('login', 'repository');
1910         $lang['logout']    = get_string('logout', 'repository');
1911         $lang['loading']   = get_string('loading', 'repository');
1912         $lang['thumbview'] = get_string('thumbview', 'repository');
1913         $lang['title']     = get_string('title', 'repository');
1914         $lang['noresult']  = get_string('noresult', 'repository');
1915         $lang['mgr']       = get_string('manageurl', 'repository');
1916         $lang['noenter']   = get_string('noenter', 'repository');
1917         $lang['save']      = get_string('save', 'repository');
1918         $lang['saveas']    = get_string('saveas', 'repository').': ';
1919         $lang['saved']     = get_string('saved', 'repository');
1920         $lang['saving']    = get_string('saving', 'repository');
1921         $lang['size']      = get_string('size', 'repository').': ';
1922         $lang['sync']      = get_string('sync', 'repository');
1923         $lang['search']    = get_string('search', 'repository');
1924         $lang['searching'] = get_string('searching', 'repository');
1925         $lang['submit']    = get_string('submit', 'repository');
1926         $lang['preview']   = get_string('preview', 'repository');
1927         $lang['popup']     = get_string('popup', 'repository');
1928         $lang['upload']    = get_string('upload', 'repository').'...';
1929         $lang['uploading'] = get_string('uploading', 'repository');
1930         // fp_lang includes language strings
1931         $js .= print_js_config($lang, 'fp_lang', true);
1933         $options = array();
1934         $context = get_system_context();
1935         $options['contextid'] = $context->id;
1936         // fp_config includes filepicker options
1937         $js .= print_js_config($options, 'fp_config', true);
1939         $accepted_file_ext = json_encode($ft->get_file_ext($accepted_filetypes));
1940         $js .= <<<EOD
1941 <script>
1942 file_extensions.image = $image_file_ext;
1943 file_extensions.media = $video_file_ext;
1944 </script>
1945 EOD;
1947         $CFG->filepickerjsloaded = true;
1948     } else {
1949         // if yui and repository javascript libs are loaded
1950         $js = '';
1951     }
1953     // print instances listing
1954     $user_context = get_context_instance(CONTEXT_USER, $USER->id);
1955     if (is_array($accepted_filetypes) && in_array('*', $accepted_filetypes)) {
1956         $accepted_filetypes = '*';
1957     }
1958     $repos = repository::get_instances(array($user_context, $context, get_system_context()), null, true, null, $accepted_filetypes, $returnvalue);
1960     // print repository instances listing
1961     $js .= <<<EOD
1962 <script>
1963 repository_listing['$id'] = {};
1964 EOD;
1965     foreach ($repos as $repo) {
1966         $info = $repo->ajax_info();
1967         $js .= "\r\n";
1968         $js .= 'repository_listing[\''.$id.'\'][\''.$info->id.'\']='.json_encode($repo->ajax_info()).';';
1969         $js .= "\n";
1970     }
1971     $js .= "\r\n";
1972     $js .= "</script>";
1974     return array('css'=>$css, 'js'=>$js);