MDL-13766
[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 /**
42  * This is the base class of the repository class
43  *
44  * To use repository plugin, you need to create a new folder under repository/, named as the remote
45  * repository, the subclass must be defined in  the name
47  *
48  * class repository is an abstract class, some functions must be implemented in subclass.
49  *
50  * See an example of use of this library in repository/boxnet/repository.class.php
51  *
52  * A few notes:
53  *   // print login page or a link to redirect to another page
54  *   $repo->print_login();
55  *   // call get_listing, and print result
56  *   $repo->print_listing();
57  *   // print a search box
58  *   $repo->print_search();
59  *
60  * @version 1.0 dev
61  * @package repository
62  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
63  */
64 require_once(dirname(dirname(__FILE__)) . '/config.php');
65 require_once(dirname(dirname(__FILE__)).'/lib/filelib.php');
66 require_once(dirname(dirname(__FILE__)).'/lib/formslib.php');
68 /**
69  * A repository_type is a repository plug-in. It can be Box.net, Flick-r, ...
70  * A repository type can be edited, sorted and hidden. It is mandatory for an
71  * administrator to create a repository type in order to be able to create
72  * some instances of this type.
73  *
74  * Coding note:
75  * - a repository_type object is mapped to the "repository" database table
76  * - "typename" attibut maps the "type" database field. It is unique.
77  * - general "options" for a repository type are saved in the config_plugin table
78  * - when you delete a repository, all instances are deleted, and general
79  *   options are also deleted from database
80  * - When you create a type for a plugin that can't have multiple instances, a
81  *   instance is automatically created.
82  */
83 class repository_type {
86     /**
87      * Type name (no whitespace) - A type name is unique
88      * Note: for a user-friendly type name see get_readablename()
89      * @var String
90      */
91     private $_typename;
94     /**
95      * Options of this type
96      * They are general options that any instance of this type would share
97      * e.g. API key
98      * These options are saved in config_plugin table
99      * @var array
100      */
101    private $_options;
104     /**
105      * Is the repository type visible or hidden
106      * If false (hidden): no instances can be created, edited, deleted, showned , used...
107      * @var boolean
108      */
109    private $_visible;
112     /**
113      * 0 => not ordered, 1 => first position, 2 => second position...
114      * A not order type would appear in first position (should never happened)
115      * @var integer
116      */
117     private $_sortorder;
119     /**
120      * repository_type constructor
121      * @global <type> $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         //check that the type can be setup
138         if (repository_static_function($typename,"has_admin_config")){
139             $options = repository_static_function($typename,'get_admin_option_names');
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         }
147     }
149     /**
150      * Get the type name (no whitespace)
151      * For a human readable name, use get_readablename()
152      * @return String the type name
153      */
154     public function get_typename(){
155         return $this->_typename;
156     }
158     /**
159      * Return a human readable and user-friendly type name
160      * @return string user-friendly type name
161      */
162     public function get_readablename(){
163         return get_string('repositoryname','repository_'.$this->_typename);
164     }
166     /**
167      * Return general options
168      * @return array the general options
169      */
170     public function get_options(){
171         return $this->_options;
172     }
174     /**
175      * Return visibility
176      * @return boolean
177      */
178     public function get_visible(){
179         return $this->_visible;
180     }
182     /**
183      * Return order / position of display in the file picker
184      * @return integer
185      */
186     public function get_sortorder(){
187         return $this->_sortorder;
188     }
190     /**
191      * Create a repository type (the type name must not already exist)
192      * @global object $DB
193      */
194     public function create(){
195         global $DB;
197         //check that $type has been set
198         $timmedtype = trim($this->_typename);
199         if (empty($timmedtype)) {
200              throw new repository_exception('emptytype', 'repository');
201         }
203         //set sortorder as the last position in the list
204         if (!isset($this->_sortorder) || $this->_sortorder == 0 ){
205             $sql = "SELECT MAX(sortorder) FROM {repository}";
206             $this->_sortorder = 1 + $DB->get_field_sql($sql);
207         }
209         //only create a new type if it doesn't already exist
210         $existingtype = $DB->get_record('repository', array('type'=>$this->_typename));
211         if(!$existingtype){
212            //create the type
213            $newtype = new stdclass;
214            $newtype->type = $this->_typename;
215            $newtype->visible = $this->_visible;
216            $newtype->sortorder = $this->_sortorder;
217            $DB->insert_record('repository', $newtype);
219            //save the options in DB
220            $this->update_options();
222            //if the plugin type has no multiple and no instance config so it wont
223            //be possible for the administrator to create a instance
224            //in this case we need to create an instance
225            if (!repository_static_function($this->_typename,"has_instance_config")
226            && !repository_static_function($this->_typename,"has_multiple_instances")){
227               $instanceoptions = array();
228               $instanceoptions['name'] = $this->_typename;
229               repository_static_function($this->_typename, 'create', $this->_typename, 0, get_system_context(), $instanceoptions);
230            }
231         }
232         else {
233             throw new repository_exception('existingrepository', 'repository');
234         }
235     }
238     /**
239      * Update plugin options into the config_plugin table
240      * @param array $options
241      * @return boolean
242      */
243     public function update_options($options = null){
244         if (!empty($options)){
245             $this->_options = $options;
246         }
248         foreach ($this->_options as $name => $value) {
249             set_config($name,$value,$this->_typename);
250         }
252         return true;
253     }
255     /**
256      * Update visible database field with the value given as parameter
257      * or with the visible value of this object
258      * This function is private.
259      * For public access, have a look to switch_and_update_visibility()
260      * @global object $DB
261      * @param boolean $visible
262      * @return boolean
263      */
264     private function update_visible($visible = null){
265         global $DB;
267         if (!empty($visible)){
268             $this->_visible = $visible;
269         }
270         else if (!isset($this->_visible)){
271             throw new repository_exception('updateemptyvisible', 'repository');
272         }
274         return $DB->set_field('repository', 'visible', $this->_visible, array('type'=>$this->_typename));
275     }
277     /**
278      * Update database sortorder field with the value given as parameter
279      * or with the sortorder value of this object
280      * This function is private.
281      * For public access, have a look to move_order()
282      * @global object $DB
283      * @param integer $sortorder
284      * @return boolean
285      */
286     private function update_sortorder($sortorder = null){
287         global $DB;
289         if (!empty($sortorder) && $sortorder!=0){
290             $this->_sortorder = $sortorder;
291         }
292         //if sortorder is not set, we set it as the ;ast position in the list
293         else if (!isset($this->_sortorder) || $this->_sortorder == 0 ){
294             $sql = "SELECT MAX(sortorder) FROM {repository}";
295             $this->_sortorder = 1 + $DB->get_field_sql($sql);
296         }
298         return $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$this->_typename));
299     }
301     /**
302      * Change order of the type with its adjacent upper or downer type
303      * (database fields are updated)
304      * Algorithm details:
305      * 1. retrieve all types in an array. This array is sorted by sortorder,
306      * and the array keys start from 0 to X (incremented by 1)
307      * 2. switch sortorder values of this type and its adjacent type
308      * @global object $DB
309      * @param string $move "up" or "down"
310      */
311     public function move_order($move) {
312         global $DB;
313         //retrieve all types
314         $types = repository_get_types();
316         //retrieve this type into the returned array
317          $i = 0;
318         while (!isset($indice) && $i<count($types)){
319             if ($types[$i]->get_typename() == $this->_typename){
320                 $indice = $i;
321             }
322             $i++;
323         }
325         //retrieve adjacent indice
326         switch ($move) {
327             case "up":
328                 $adjacentindice = $indice - 1;
329                 break;
330             case "down":
331                 $adjacentindice = $indice + 1;
332                 break;
333             default:
334                 throw new repository_exception('movenotdefined', 'repository');
335         }
337         //switch sortorder of this type and the adjacent type
338         //TODO: we could reset sortorder for all types. This is not as good in performance term, but
339         //that prevent from wrong behaviour on a screwed database. As performance are not important in this particular case
340         //it worth to change the algo.
341         if ($adjacentindice>=0 && !empty($types[$adjacentindice])){
342             $DB->set_field('repository', 'sortorder', $this->_sortorder, array('type'=>$types[$adjacentindice]->get_typename()));
343             $this->update_sortorder($types[$adjacentindice]->get_sortorder());
344         }
345     }
347     /**
348      * 1. Switch the visibility OFF if it's ON, and ON if it's OFF.
349      * 2. Update the type
350      * @return <type>
351      */
352     public function switch_and_update_visibility(){
353         $this->_visible = !$this->_visible;
354         return $this->update_visible();
355     }
358     /**
359      * Delete a repository_type (general options are removed from config_plugin
360      * table, and all instances are deleted)
361      * @global object $DB
362      * @return boolean
363      */
364     public function delete(){
365         global $DB;
367         //delete all instances of this type
368         $instances = repository_get_instances(array(),null,false,$this->_typename);
369         foreach($instances as $instance){
370             $instance->delete();
371         }
373         //delete all general options
374         foreach ($this->_options as $name => $value){
375             set_config($name, null, $this->_typename);
376         }
378         return $DB->delete_records('repository', array('type' => $this->_typename));
379     }
382 /**
383  * Return a type for a given type name.
384  * @global object $DB
385  * @param string $typename the type name
386  * @return integer
387  */
388 function repository_get_type_by_typename($typename){
389     global $DB;
391     if(!$record = $DB->get_record('repository',array('type' => $typename))) {
392         return false;
393     }
395     return new repository_type($typename, (array)get_config($typename), $record->visible, $record->sortorder);
398 /**
399  * Return a type for a given type id.
400  * @global object $DB
401  * @param string $typename the type name
402  * @return integer
403  */
404 function repository_get_type_by_id($id){
405     global $DB;
407     if(!$record = $DB->get_record('repository',array('id' => $id))) {
408         return false;
409     }
411     return new repository_type($record->type, (array)get_config($record->type), $record->visible, $record->sortorder);
414 /**
415  * Return all repository types ordered by sortorder
416  * first type in returnedarray[0], second type in returnedarray[1], ...
417  * @global object $DB
418  * @return array Repository types
419  */
420 function repository_get_types(){
421     global $DB;
423     $types = array();
425     if($records = $DB->get_records('repository',null,'sortorder')) {
426         foreach($records as $type) {
427             $types[] = new repository_type($type->type, (array)get_config($type->type), $type->visible, $type->sortorder);
428         }
429     }
431     return $types;
434 /**
435  *TODO: write comment
436  */
438 abstract class repository {
439     public $id;
440     public $context;
441     public $options;
443     /**
444      * 1. Initialize context and options
445      * 2. Accept necessary parameters
446      *
447      * @param integer $repositoryid
448      * @param integer $contextid
449      * @param array $options
450      */
451     public function __construct($repositoryid, $contextid = SITEID, $options = array()){
452         $this->id = $repositoryid;
453         $this->context = get_context_instance_by_id($contextid);
454         $this->options = array();
455         if (is_array($options)) {
456             $options = array_merge($this->get_option(), $options);
457         } else {
458             $options = $this->get_option();
459         }
460         $this->options = array();
461         foreach ($options as $n => $v) {
462             $this->options[$n] = $v;
463         }
464         $this->name = $this->get_name();
465     }
467     /**
468      * set options for repository instance
469      *
470      * @param string $name
471      * @param mixed $value
472      */
473     public function __set($name, $value) {
474         $this->options[$name] = $value;
475     }
477     /**
478      * get options for repository instance
479      *
480      * @param string $name
481      * @return mixed
482      */
483     public function __get($name) {
484         if (array_key_exists($name, $this->options)){
485             return $this->options[$name];
486         }
487         trigger_error('Undefined property: '.$name, E_USER_NOTICE);
488         return null;
489     }
491     /**
492      * test option name
493      *
494      * @param string name
495      */
496     public function __isset($name) {
497         return isset($this->options[$name]);
498     }
500     /**
501      * Return the name of the repository class
502      * @return <type>
503      */
504     public function __toString() {
505         return 'Repository class: '.__CLASS__;
506     }
508     /**
509      * Download a file from a given url
510      *
511      * @global object $CFG
512      * @param string $url the url of file
513      * @param string $file save location
514      * @return string the location of the file
515      * @see curl package
516      */
517     public function get_file($url, $file = '') {
518         global $CFG;
519         if (!file_exists($CFG->dataroot.'/temp/download')) {
520             mkdir($CFG->dataroot.'/temp/download/', 0777, true);
521         }
522         if(is_dir($CFG->dataroot.'/temp/download')) {
523             $dir = $CFG->dataroot.'/temp/download/';
524         }
525         if(empty($file)) {
526             $file = uniqid('repo').'_'.time().'.tmp';
527         }
528         if(file_exists($dir.$file)){
529             $file = uniqid('m').$file;
530         }
531         $fp = fopen($dir.$file, 'w');
532         $c = new curl;
533         $c->download(array(
534             array('url'=>$url, 'file'=>$fp)
535         ));
536         return $dir.$file;
537     }
539     /**
540      * Print a list or return string
541      *
542      * @param string $list
543      * @param boolean $print false, return html, otherwise, print it directly
544      * @return <type>
545      */
546     public function print_listing($listing = array(), $print=true) {
547         if(empty($listing)){
548             $listing = $this->get_listing();
549         }
550         if (empty($listing)) {
551             $str = '';
552         } else {
553             $count = 0;
554             $str = '<table>';
555             foreach ($listing as $v){
556                 $str .= '<tr id="entry_'.$count.'">';
557                 $str .= '<td><input type="checkbox" /></td>';
558                 $str .= '<td>'.$v['name'].'</td>';
559                 $str .= '<td>'.$v['size'].'</td>';
560                 $str .= '<td>'.$v['date'].'</td>';
561                 $str .= '</tr>';
562                 $count++;
563             }
564             $str .= '</table>';
565         }
566         if ($print){
567             echo $str;
568             return null;
569         } else {
570             return $str;
571         }
572     }
574     /**
575      * TODO: write documentation here
576      * @global <type> $DB
577      * @return <type>
578      */
579     public function get_name(){
580         global $DB;
581         // We always verify instance id from database,
582         // so we always know repository name before init
583         // a repository, so we don't enquery repository
584         // name from database again here.
585         if (isset($this->options['name'])) {
586             return $this->options['name'];
587         } else {
588             if ( $repo = $DB->get_record('repository_instances', array('id'=>$this->id)) ) {
589                 return $repo->name;
590             } else {
591                 return '';
592             }
593         }
594     }
596     /**
597      * Provide repository instance information for Ajax
598      * @global object $CFG
599      * @return object
600      */
601     final public function ajax_info() {
602         global $CFG;
603         $repo = new stdclass;
604         $repo->id   = $this->id;
605         $repo->name = $this->get_name();
606         $repo->type = $this->options['type'];
607         $repo->icon = $CFG->httpswwwroot.'/repository/'.$repo->type.'/icon.png';
608         return $repo;
609     }
611     /**
612      * Create an instance for this plug-in
613      * @global object $CFG
614      * @global object $DB
615      * @param string $type the type of the repository
616      * @param integer $userid the user id
617      * @param object $context the context
618      * @param array $params the options for this instance
619      * @return <type>
620      */
621     final public static function create($type, $userid, $context, $params) {
622         global $CFG, $DB;
623         $params = (array)$params;
624         require_once($CFG->dirroot . '/repository/'. $type . '/repository.class.php');
625         $classname = 'repository_' . $type;
626         if ($repo = $DB->get_record('repository', array('type'=>$type))) {
627             $record = new stdclass;
628             $record->name = $params['name'];
629             $record->typeid = $repo->id;
630             $record->timecreated  = time();
631             $record->timemodified = time();
632             $record->contextid = $context->id;
633             $record->userid    = $userid;
634             $id = $DB->insert_record('repository_instances', $record);
635             $options = array();
636             if (call_user_func($classname . '::has_instance_config')) {
637                 $configs = call_user_func($classname . '::get_instance_option_names');
638                 foreach ($configs as $config) {
639                     $options[$config] = $params[$config];
640                 }
641             }
642             if (!empty($id)) {
643                 unset($options['name']);
644                 $instance = repository_get_instance($id);
645                 $instance->set_option($options);
646                 return $id;
647             } else {
648                 return null;
649             }
650         } else {
651             return null;
652         }
653     }
655     /**
656      * delete a repository instance
657      * @global object $DB
658      * @return <type>
659      */
660     final public function delete(){
661         global $DB;
662         $DB->delete_records('repository_instances', array('id'=>$this->id));
663         return true;
664     }
666     /**
667      * Hide/Show a repository
668      * @global object $DB
669      * @param string $hide
670      * @return <type>
671      */
672     final public function hide($hide = 'toggle'){
673         global $DB;
674         if ($entry = $DB->get_record('repository', array('id'=>$this->id))) {
675             if ($hide === 'toggle' ) {
676                 if (!empty($entry->visible)) {
677                     $entry->visible = 0;
678                 } else {
679                     $entry->visible = 1;
680                 }
681             } else {
682                 if (!empty($hide)) {
683                     $entry->visible = 0;
684                 } else {
685                     $entry->visible = 1;
686                 }
687             }
688             return $DB->update_record('repository', $entry);
689         }
690         return false;
691     }
693     /**
694      * Cache login details for repositories
695      * @global object $DB
696      * @param string $username
697      * @param string $password
698      * @param integer $userid The id of specific user
699      * @return integer Id of the record
700      */
701     public function store_login($username = '', $password = '', $userid = 1) {
702         global $DB;
704         $repository = new stdclass;
705         if (!empty($this->id)) {
706             $repository->id = $this->id;
707         } else {
708             $repository->userid         = $userid;
709             $repository->repositorytype = $this->type;
710             $repository->contextid      = $this->context->id;
711         }
712         if ($entry = $DB->get_record('repository', $repository)) {
713             $repository->id = $entry->id;
714             $repository->username = $username;
715             $repository->password = $password;
716             return $DB->update_record('repository', $repository);
717         } else {
718             $repository->username = $username;
719             $repository->password = $password;
720             return $DB->insert_record('repository', $repository);
721         }
722     }
724     /**
725      * Save settings for repository instance
726      * $repo->set_option(array('api_key'=>'f2188bde132', 'name'=>'dongsheng'));
727      * @global object $DB
728      * @param array $options settings
729      * @return int Id of the record
730      */
731     public function set_option($options = array()){
732         global $DB;
733         if (!empty($options['name'])) {
734             $r = new object();
735             $r->id   = $this->id;
736             $r->name = $options['name'];
737             $DB->update_record('repository_instances', $r);
738             unset($options['name']);
739         }
740         foreach ($options as $name=>$value) {
741             if ($id = $DB->get_field('repository_instance_config', 'id', array('name'=>$name, 'instanceid'=>$this->id))) {
742                 if ($value===null) {
743                     return $DB->delete_records('repository_instance_config', array('name'=>$name, 'instanceid'=>$this->id));
744                 } else {
745                     return $DB->set_field('repository_instance_config', 'value', $value, array('id'=>$id));
746                 }
747             } else {
748                 if ($value===null) {
749                     return true;
750                 }
751                 $config = new object();
752                 $config->instanceid = $this->id;
753                 $config->name   = $name;
754                 $config->value  = $value;
755                 return $DB->insert_record('repository_instance_config', $config);
756             }
757         }
758         return true;
759     }
761     /**
762      * Get settings for repository instance
763      * @global object $DB
764      * @param <type> $config
765      * @return array Settings
766      */
767     public function get_option($config = ''){
768         global $DB;
769         $entries = $DB->get_records('repository_instance_config', array('instanceid'=>$this->id));
770         $ret = array();
771         if (empty($entries)) {
772             return $ret;
773         }
774         foreach($entries as $entry){
775             $ret[$entry->name] = $entry->value;
776         }
777         if (!empty($config)) {
778             return $ret[$config];
779         } else {
780             return $ret;
781         }
782     }
784     /**
785      * Given a path, and perhaps a search, get a list of files.
786      *
787      * The format of the returned array must be:
788      * array(
789      *   'path' => (string) path for the current folder
790      *   'dynload' => (bool) use dynamic loading,
791      *   'manage' => (string) link to file manager,
792      *   'nologin' => (bool) requires login,
793      *   'nosearch' => (bool) no search link,
794      *   'upload' => array( // upload manager
795      *     'name' => (string) label of the form element,
796      *     'id' => (string) id of the form element
797      *   ),
798      *   'list' => array(
799      *     array( // file
800      *       'title' => (string) file name,
801      *       'date' => (string) file last modification time, usually userdate(...),
802      *       'size' => (int) file size,
803      *       'thumbnail' => (string) url to thumbnail for the file,
804      *       'source' => plugin-dependent unique path to the file (id, url, path, etc.),
805      *       'url'=> the accessible url of file
806      *     ),
807      *     array( // folder - same as file, but no 'source'.
808      *       'title' => (string) folder name,
809      *       'path' => (string) path to this folder
810      *       'date' => (string) folder last modification time, usually userdate(...),
811      *       'size' => 0,
812      *       'thumbnail' => (string) url to thumbnail for the folder,
813      *       'children' => array( // an empty folder needs to have 'children' defined, but empty.
814      *         // content (files and folders)
815      *       )
816      *     ),
817      *   )
818      * )
819      *
820      * @param string $parent The parent path, this parameter can
821      * a folder name, or a identification of folder
822      * @param string $search The text will be searched.
823      * @return array the list of files, including meta infomation
824      */
825     abstract public function get_listing($parent = '/', $search = '');
828     /**
829      * Show the login screen, if required
830      * This is an abstract function, it must be overriden.
831      */
832     abstract public function print_login();
834     /**
835      * Show the search screen, if required
836      * @return null
837      */
838     abstract public function print_search();
840     /**
841      * is it possible to do glboal search?
842      * @return boolean
843      */
844     public function global_search(){
845         return false;
846     }
848     /**
849      * Defines operations that happen occasionally on cron
850      * @return <type>
851      */
852     public function cron() {
853         return true;
854     }
856     /**
857      * Return true if the plugin type has at least one general option field
858      * By default: false
859      * @return boolean
860      */
861     public static function has_admin_config() {
862         return false;
863     }
865     /**
866      * Return true if a plugin instance has at least one config field
867      * By default: false
868      * @return boolean
869      */
870     public static function has_instance_config() {
871         return false;
872     }
874     /**
875      * Return true if the plugin can have multiple instances
876      * By default: false
877      * @return boolean
878      */
879     public static function has_multiple_instances(){
880         return false;
881     }
883     /**
884      * Return names of the general options
885      * By default: no general option name
886      * @return array
887      */
888     public static function get_admin_option_names(){
889         return array();
890     }
892     /**
893      * Return names of the instance options
894      * By default: no instance option name
895      * @return array
896      */
897     public static function get_instance_option_names(){
898         return array();
899     }
902 /**
903  * exception class for repository api
904  */
905 class repository_exception extends moodle_exception {
908 /**
909  * Check context
910  * @param int $ctx_id
911  * @return boolean
912  */
913 function repository_check_context($ctx_id){
914     global $USER;
915     $context = get_context_instance_by_id($ctx_id);
916     $level = $context->contextlevel;
917     if ($level == CONTEXT_COURSE) {
918         if (!has_capability('moodle/course:view', $context)) {
919             return false;
920         } else {
921             return true;
922         }
923     } elseif ($level == CONTEXT_USER) {
924         $c = get_context_instance(CONTEXT_USER, $USER->id);
925         if ($c->id == $ctx_id) {
926             return true;
927         } else {
928             return false;
929         }
930     } elseif ($level == CONTEXT_SYSTEM) {
931         // it is always ok in system level
932         return true;
933     }
934     return false;
937 /**
938  * Return repository instances
939  * @global object $DB
940  * @global object $CFG
941  * @global object $USER
942  * @param object $contexts contexts for which the instances are set
943  * @param integer $userid
944  * @param boolean $onlyvisible if visible == true, return visible instances only,
945  *                otherwise, return all instances
946  * @param string $type a type name to retrieve
947  * @return array repository instances
948  */
949 function repository_get_instances($contexts=array(), $userid = null, $onlyvisible = true, $type=null){
950     global $DB, $CFG, $USER;
951     $params = array();
952     $sql = 'SELECT i.*, r.type AS repositorytype, r.sortorder, r.visible FROM {repository} r, {repository_instances} i WHERE ';
953     $sql .= 'i.typeid = r.id ';
954     if (!empty($userid) && is_numeric($userid)) {
955         $sql .= ' AND (i.userid = 0 or i.userid = ?)';
956         $params[] = $userid;
957     }
958     foreach ($contexts as $context) {
959         if (empty($firstcontext)){
960             $firstcontext = true;
961             $sql .= ' AND ((i.contextid = ?)';
962         } else {
963             $sql .= ' OR (i.contextid = ?)';
964         }
965         $params[] = $context->id;
966     }
967     if ($firstcontext) {
968        $sql .=')';
969     }
971     if($onlyvisible == true) {
972         $sql .= ' AND (r.visible = 1)';
973     }
974     if(isset($type)) {
975         $sql .= ' AND (r.type = ?)';
976         $params[] = $type;
977     }
978     $sql .= ' order by r.sortorder, i.name';
979     if(!$repos = $DB->get_records_sql($sql, $params)) {
980         $repos = array();
981     }
982     $ret = array();
983     foreach($repos as $repo) {
984         require_once($CFG->dirroot . '/repository/'. $repo->repositorytype
985             . '/repository.class.php');
986         $options['visible'] = $repo->visible;
987         $options['name']    = $repo->name;
988         $options['type']    = $repo->repositorytype;
989         $options['typeid']  = $repo->typeid;
990         $classname = 'repository_' . $repo->repositorytype;
991         $ret[] = new $classname($repo->id, $repo->contextid, $options);
992     }
993     return $ret;
996 /**
997  * Get single repository instance
998  * @global object $DB
999  * @global object $CFG
1000  * @param integer $id repository id
1001  * @return object repository instance
1002  */
1003 function repository_get_instance($id){
1004     global $DB, $CFG;
1005     $sql = 'SELECT i.*, r.type AS repositorytype, r.visible FROM {repository} r, {repository_instances} i WHERE ';
1006     $sql .= 'i.typeid = r.id AND ';
1007     $sql .= 'i.id = '.$id;
1009     if(!$instance = $DB->get_record_sql($sql)) {
1010         return false;
1011     }
1012     require_once($CFG->dirroot . '/repository/'. $instance->repositorytype
1013         . '/repository.class.php');
1014     $classname = 'repository_' . $instance->repositorytype;
1015     $options['typeid'] = $instance->typeid;
1016     $options['type']   = $instance->repositorytype;
1017     $options['name']   = $instance->name;
1018     return new $classname($instance->id, $instance->contextid, $options);
1021 /**
1022  * TODO: write documentation
1023  * @global <type> $CFG
1024  * @param <type> $plugin
1025  * @param <type> $function
1026  * @param type $nocallablereturnvalue default value if function not found
1027  *             it's mostly used when you don't want to display an error but
1028  *             return a boolean
1029  * @return <type>
1030  */
1031 function repository_static_function($plugin, $function) {
1032     global $CFG;
1034     //check that the plugin exists
1035     $typedirectory = $CFG->dirroot . '/repository/'. $plugin . '/repository.class.php';
1036         if (!file_exists($typedirectory)) {
1037             throw new repository_exception('invalidplugin', 'repository');
1038     }
1040     $pname = null;
1041     if (is_object($plugin) || is_array($plugin)) {
1042         $plugin = (object)$plugin;
1043         $pname = $plugin->name;
1044     } else {
1045         $pname = $plugin;
1046     }
1048     $args = func_get_args();
1049     if (count($args) <= 2) {
1050         $args = array();
1051     }
1052     else {
1053         array_shift($args);
1054         array_shift($args);
1055     }
1057     require_once($typedirectory);
1058     return call_user_func_array(array('repository_' . $plugin, $function), $args);
1061 /**
1062  * Move file from download folder to file pool using FILE API
1063  * @TODO Need review
1064  * @global object $DB
1065  * @global object $CFG
1066  * @global object $USER
1067  * @param string $path file path in download folder
1068  * @param string $name file name
1069  * @param integer $itemid item id to identify a file in filepool
1070  * @param string $filearea file area
1071  * @return array information of file in file pool
1072  */
1073 function repository_move_to_filepool($path, $name, $itemid, $filearea = 'user_draft') {
1074     global $DB, $CFG, $USER;
1075     $context = get_context_instance(CONTEXT_USER, $USER->id);
1076     $now = time();
1077     $entry = new object();
1078     $entry->filearea  = $filearea;
1079     $entry->contextid = $context->id;
1080     $entry->filename  = $name;
1081     $entry->filepath  = '/'.uniqid().'/';
1082     $entry->timecreated  = $now;
1083     $entry->timemodified = $now;
1084     if(is_numeric($itemid)) {
1085         $entry->itemid = $itemid;
1086     } else {
1087         $entry->itemid = 0;
1088     }
1089     $entry->mimetype     = mimeinfo('type', $path);
1090     $entry->userid       = $USER->id;
1091     $fs = get_file_storage();
1092     $browser = get_file_browser();
1093     if ($file = $fs->create_file_from_pathname($entry, $path)) {
1094         $delete = unlink($path);
1095         $ret = $browser->get_file_info($context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
1096         if(!empty($ret)){
1097             return array('url'=>$ret->get_url(),'id'=>$file->get_itemid(), 'file'=>$file->get_filename());
1098         } else {
1099             return null;
1100         }
1101     } else {
1102         return null;
1103     }
1106 /**
1107  * Save file to local filesystem pool
1108  * @param string $elname name of element
1109  * @param string $filearea
1110  * @param string $filepath
1111  * @param string $filename - use specified filename, if not specified name of uploaded file used
1112  * @param bool $override override file if exists
1113  * @return mixed stored_file object or false if error; may throw exception if duplicate found
1114  */
1115 function repository_store_to_filepool($elname, $filearea='user_draft', $filepath='/', $filename = '', $override = false) {
1116     global $USER;
1117     if (!isset($_FILES[$elname])) {
1118         return false;
1119     }
1121     if (!$filename) {
1122         $filename = $_FILES[$elname]['name'];
1123     }
1124     $context = get_context_instance(CONTEXT_USER, $USER->id);
1125     $itemid = (int)substr(hexdec(uniqid()), 0, 9)+rand(1,100);
1126     $fs = get_file_storage();
1127     $browser = get_file_browser();
1129     if ($file = $fs->get_file($context->id, $filearea, $itemid, $filepath, $filename)) {
1130         if ($override) {
1131             $file->delete();
1132         } else {
1133             return false;
1134         }
1135     }
1137     $file_record = new object();
1138     $file_record->contextid = $context->id;
1139     $file_record->filearea  = $filearea;
1140     $file_record->itemid    = $itemid;
1141     $file_record->filepath  = $filepath;
1142     $file_record->filename  = $filename;
1143     $file_record->userid    = $USER->id;
1145     $file = $fs->create_file_from_pathname($file_record, $_FILES[$elname]['tmp_name']);
1146     $info = $browser->get_file_info($context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
1147     $ret = array('url'=>$info->get_url(),'id'=>$itemid, 'file'=>$file->get_filename());
1148     return $ret;
1151 /**
1152  * Return javascript to create file picker to browse repositories
1153  * @global object $CFG
1154  * @global object $USER
1155  * @param object $context the context
1156  * @return array
1157  */
1158 function repository_get_client($context){
1159     global $CFG, $USER;
1160     $suffix = uniqid();
1161     $sesskey = sesskey();
1162     $strsaveas    = get_string('saveas', 'repository').': ';
1163     $stradd  = get_string('add', 'repository');
1164     $strback      = get_string('back', 'repository');
1165     $strcancel    = get_string('cancel');
1166     $strclose     = get_string('close', 'repository');
1167     $strccache    = get_string('cleancache', 'repository');
1168     $strcopying   = get_string('copying', 'repository');
1169     $strdownbtn   = get_string('getfile', 'repository');
1170     $strdownload  = get_string('downloadsucc', 'repository');
1171     $strdate      = get_string('date', 'repository').': ';
1172     $strerror     = get_string('error', 'repository');
1173     $strfilenotnull = get_string('filenotnull', 'repository');
1174     $strrefresh   = get_string('refresh', 'repository');
1175     $strinvalidjson = get_string('invalidjson', 'repository');
1176     $strlistview  = get_string('listview', 'repository');
1177     $strlogout    = get_string('logout', 'repository');
1178     $strloading   = get_string('loading', 'repository');
1179     $strthumbview = get_string('thumbview', 'repository');
1180     $strtitle     = get_string('title', 'repository');
1181     $strmgr       = get_string('manageurl', 'repository');
1182     $strnoenter   = get_string('noenter', 'repository');
1183     $strsave      = get_string('save', 'repository');
1184     $strsaved     = get_string('saved', 'repository');
1185     $strsaving    = get_string('saving', 'repository');
1186     $strsize      = get_string('size', 'repository').': ';
1187     $strsync      = get_string('sync', 'repository');
1188     $strsearch    = get_string('search', 'repository');
1189     $strsearching = get_string('searching', 'repository');
1190     $strsubmit    = get_string('submit', 'repository');
1191     $strpreview   = get_string('preview', 'repository');
1192     $strupload    = get_string('upload', 'repository');
1193     $struploading = get_string('uploading', 'repository');
1194     $css = <<<EOD
1195 <style type="text/css">
1196 .repo-list{list-style-type:none;padding:0}
1197 .repo-list li{border-bottom:1px dotted gray;margin-bottom: 1em;}
1198 .repo-name{display:block;padding: 3px;margin-bottom: 5px}
1199 .paging{margin:10px 5px; clear:both;}
1200 .paging a{padding: 4px;border: 1px solid #CCC}
1201 .repo-path{margin: 4px;border-bottom: 1px dotted gray;}
1202 .repo-path a{padding: 4px;}
1203 .rename-form{text-align:center}
1204 .rename-form p{margin: 1em;}
1205 .upload-form{margin: 2em 0;text-align:center}
1206 p.upload a{font-size: 14px;background: #ccc;color:white;padding: 5px}
1207 p.upload a:hover {background: grey;color:white}
1208 .file_name{color:green;}
1209 .file_date{color:blue}
1210 .file_size{color:gray}
1211 .grid{width:80px; float:left;text-align:center;}
1212 .grid div{width: 80px; overflow: hidden}
1213 .grid p{margin:0;padding:0;background: #FFFFCC}
1214 .grid .label{height:48px}
1215 .grid span{background: #EEF9EB;color:gray}
1216 #panel-$suffix{padding:0;margin:0; text-align:left;}
1217 #file-picker-$suffix{font-size:12px;}
1218 #file-picker-$suffix strong{background:#FFFFCC}
1219 #file-picker-$suffix a{color: #336699}
1220 #file-picker-$suffix a:hover{background:#003366;color:white}
1221 #repo-viewbar-$suffix{width:300px;float:left}
1222 #search-div-$suffix{float:right}
1223 #repo-tb-$suffix{padding: .8em;background: #FFFFCC;color:white;text-align:center}
1224 </style>
1225 <style type="text/css">
1226 @import "$CFG->httpswwwroot/lib/yui/resize/assets/skins/sam/resize.css";
1227 @import "$CFG->httpswwwroot/lib/yui/container/assets/skins/sam/container.css";
1228 @import "$CFG->httpswwwroot/lib/yui/layout/assets/skins/sam/layout.css";
1229 @import "$CFG->httpswwwroot/lib/yui/button/assets/skins/sam/button.css";
1230 @import "$CFG->httpswwwroot/lib/yui/assets/skins/sam/treeview.css";
1231 </style>
1232 EOD;
1234     $js = <<<EOD
1235 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/yahoo-dom-event/yahoo-dom-event.js"></script>
1236 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/element/element-beta-min.js"></script>
1237 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/treeview/treeview-min.js"></script>
1238 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/dragdrop/dragdrop-min.js"></script>
1239 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/container/container-min.js"></script>
1240 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/resize/resize-beta-min.js"></script>
1241 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/layout/layout-beta-min.js"></script>
1242 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/connection/connection-min.js"></script>
1243 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/json/json-min.js"></script>
1244 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/button/button-min.js"></script>
1245 <script type="text/javascript" src="$CFG->httpswwwroot/lib/yui/selector/selector-beta-min.js"></script>
1246 <script type="text/javascript">
1247 //<![CDATA[
1248 var active_instance = null;
1249 function repository_callback(id){
1250     active_instance.req(id, '', 0);
1252 var repository_client_$suffix = (function() {
1253 // private static field
1254 var dver = '1.0';
1255 // private static methods
1256 function alert_version(){
1257     alert(dver);
1259 function _client(){
1260     // public varible
1261     this.name = 'repository_client_$suffix';
1262     // private varible
1263     var Dom = YAHOO.util.Dom, Event = YAHOO.util.Event, layout = null, resize = null;
1264     var IE_QUIRKS = (YAHOO.env.ua.ie && document.compatMode == "BackCompat");
1265     var IE_SYNC = (YAHOO.env.ua.ie == 6 || (YAHOO.env.ua.ie == 7 && IE_QUIRKS));
1266     var PANEL_BODY_PADDING = (10*2);
1267     var btn_list = {label: '$strlistview', value: 'l', checked: true, onclick: {fn: _client.viewlist}};
1268     var btn_thumb = {label: '$strthumbview', value: 't', onclick: {fn: _client.viewthumb}};
1269     var repo_list = null;
1270     var resize = null;
1271     var panel = new YAHOO.widget.Panel('file-picker-$suffix', {
1272         draggable: true,
1273         close: true,
1274         modal: true,
1275         underlay: 'none',
1276         zindex: 666666,
1277         xy: [50, Dom.getDocumentScrollTop()+20]
1278     });
1279     // construct code section
1280     {
1281         panel.setHeader('$strtitle');
1282         panel.setBody('<div id="layout-$suffix"></div>');
1283         panel.beforeRenderEvent.subscribe(function() {
1284             Event.onAvailable('layout-$suffix', function() {
1285                 layout = new YAHOO.widget.Layout('layout-$suffix', {
1286                     height: 480, width: 630,
1287                     units: [
1288                         {position: 'top', height: 32, resize: false,
1289                         body:'<div class="yui-buttongroup" id="repo-viewbar-$suffix"></div><div id="search-div-$suffix"></div>', gutter: '2'},
1290                         {position: 'left', width: 200, resize: true,
1291                         body:'<ul class="repo-list" id="repo-list-$suffix"></ul>', gutter: '0 5 0 2', minWidth: 150, maxWidth: 300 },
1292                         {position: 'center', body: '<div id="panel-$suffix"></div>',
1293                         scroll: true, gutter: '0 2 0 0' }
1294                     ]
1295                 });
1296                 layout.render();
1297             });
1298         });
1299         resize = new YAHOO.util.Resize('file-picker-$suffix', {
1300             handles: ['br'],
1301             autoRatio: true,
1302             status: true,
1303             minWidth: 380,
1304             minHeight: 400
1305         });
1306         resize.on('resize', function(args) {
1307             var panelHeight = args.height;
1308             var headerHeight = this.header.offsetHeight; // Content + Padding + Border
1309             var bodyHeight = (panelHeight - headerHeight);
1310             var bodyContentHeight = (IE_QUIRKS) ? bodyHeight : bodyHeight - PANEL_BODY_PADDING;
1311             Dom.setStyle(this.body, 'height', bodyContentHeight + 'px');
1312             if (IE_SYNC) {
1313                 this.sizeUnderlay();
1314                 this.syncIframe();
1315             }
1316             layout.set('height', bodyContentHeight);
1317             layout.set('width', (args.width - PANEL_BODY_PADDING));
1318             layout.resize();
1320         }, panel, true);
1321         _client.viewbar = new YAHOO.widget.ButtonGroup({
1322             id: 'btngroup-$suffix',
1323             name: 'buttons',
1324             disabled: true,
1325             container: 'repo-viewbar-$suffix'
1326             });
1327     }
1328     // public method
1329     this.show = function(){
1330         panel.show();
1331     }
1332     this.hide = function(){
1333         panel.hide();
1334     }
1335     this.create_picker = function(){
1336         // display UI
1337         panel.render();
1338         _client.viewbar.addButtons([btn_list, btn_thumb]);
1339         // init repository list
1340         repo_list = new YAHOO.util.Element('repo-list-$suffix');
1341         repo_list.on('contentReady', function(e){
1342             var searchbar = new YAHOO.util.Element('search-div-$suffix');
1343             searchbar.get('element').innerHTML = '<input id="search-input-$suffix" /><button id="search-btn-$suffix">$strsearch</button>';
1344             var searchbtn = new YAHOO.util.Element('search-btn-$suffix');
1345             searchbtn.callback = {
1346                 success: function(o) {
1347                     var panel = new YAHOO.util.Element('panel-$suffix');
1348                     try {
1349                         if(!o.responseText){
1350                             var panel = new YAHOO.util.Element('panel-$suffix');
1351                             panel.get('element').innerHTML = 'no';
1352                             return;
1353                         }
1354                         var json = YAHOO.lang.JSON.parse(o.responseText);
1355                     } catch(e) {
1356                         alert('$strinvalidjson - '+o.responseText);
1357                     }
1358                     _client.ds = {};
1359                     if(!json.list || json.list.length<1){
1360                         var panel = new YAHOO.util.Element('panel-$suffix');
1361                         panel.get('element').innerHTML = 'no';
1362                         return;
1363                     }
1364                     _client.ds.list = json.list;
1365                     if(_client.ds.list) {
1366                         if(_client.viewmode) {
1367                             _client.viewthumb();
1368                         } else {
1369                             _client.viewlist();
1370                         }
1371                         var input_ctl = new YAHOO.util.Element('search-input-$suffix');
1372                         input_ctl.get('element').value='';
1373                     }
1374                 }
1375             }
1376             searchbtn.input_ctl = new YAHOO.util.Element('search-input-$suffix');
1377             searchbtn.on('click', function(e){
1378                 var keyword = this.input_ctl.get('value');
1379                 var params = [];
1380                 params['s'] = keyword;
1381                 params['env']=_client.env;
1382                 params['action']='gsearch';
1383                 params['sesskey']='$sesskey';
1384                 params['ctx_id']=$context->id;
1385                 _client.loading('load');
1386                 var trans = YAHOO.util.Connect.asyncRequest('POST',
1387                     '$CFG->httpswwwroot/repository/ws.php?action=gsearch', this.callback, _client.postdata(params));
1388             });
1389             for(var i=0; i<_client.repos.length; i++) {
1390                 var repo = _client.repos[i];
1391                 var li = document.createElement('li');
1392                 li.id = 'repo-$suffix-'+repo.id;
1393                 var icon = document.createElement('img');
1394                 icon.src = repo.icon;
1395                 icon.width = '16';
1396                 icon.height = '16';
1397                 var link = document.createElement('a');
1398                 link.href = '###';
1399                 link.id = 'repo-call-$suffix-'+repo.id;
1400                 link.appendChild(icon);
1401                 link.className = 'repo-name';
1402                 link.onclick = function(){
1403                     var re = /repo-call-$suffix-(\d+)/i;
1404                     var id = this.id.match(re);
1405                     repository_client_$suffix.req(id[1], '', 0);
1406                 }
1407                 link.innerHTML += ' '+repo.name;
1408                 li.appendChild(link);
1409                 this.appendChild(li);
1410                 repo = null;
1411             }
1412             });
1413     }
1416 // public static varible
1417 _client.repos = [];
1418 _client.repositoryid = 0;
1419 // _client.ds save all data received from server side
1420 _client.ds = null;
1421 _client.viewmode = 0;
1422 _client.viewbar =null;
1424 // public static mehtod
1425 _client.postdata = function(obj) {
1426     var str = '';
1427     for(k in obj) {
1428         if(obj[k] instanceof Array) {
1429             for(i in obj[k]) {
1430                 str += (encodeURIComponent(k) +'[]='+encodeURIComponent(obj[k][i]));
1431                 str += '&';
1432             }
1433         } else {
1434             str += encodeURIComponent(k) +'='+encodeURIComponent(obj[k]);
1435             str += '&';
1436         }
1437     }
1438     return str;
1440 _client.loading = function(type, name){
1441     var panel = new YAHOO.util.Element('panel-$suffix');
1442     panel.get('element').innerHTML = '';
1443     var content = document.createElement('div');
1444     content.style.textAlign='center';
1445     var para = document.createElement('P');
1446     var img = document.createElement('IMG');
1447     if(type=='load'){
1448     img.src = '$CFG->pixpath/i/loading.gif';
1449     para.innerHTML = '$strloading';
1450     }else{
1451     img.src = '$CFG->pixpath/i/progressbar.gif';
1452     para.innerHTML = '$strcopying <strong>'+name+'</strong>';
1453     }
1454     content.appendChild(para);
1455     content.appendChild(img);
1456     //content.innerHTML = '';
1457     panel.get('element').appendChild(content);
1459 _client.rename = function(oldname, url, icon, repo_id){
1460     var panel = new YAHOO.util.Element('panel-$suffix');
1461     var html = '<div class="rename-form">';
1462     _client.repositoryid=repo_id;
1463     html += '<p><img src="'+icon+'" /></p>';
1464     html += '<p><label for="newname-$suffix">$strsaveas</label>';
1465     html += '<input type="text" id="newname-$suffix" value="'+oldname+'" /></p>';
1466     /**
1467     html += '<p><label for="syncfile-$suffix">$strsync</label> ';
1468     html += '<input type="checkbox" id="syncfile-$suffix" /></p>';
1469     */
1470     html += '<p><input type="hidden" id="fileurl-$suffix" value="'+url+'" />';
1471     html += '<a href="###" onclick="repository_client_$suffix.viewfiles()">$strback</a> ';
1472     html += '<input type="button" onclick="repository_client_$suffix.download()" value="$strdownbtn" />';
1473     html += '<input type="button" onclick="repository_client_$suffix.hide()" value="$strcancel" /></p>';
1474     html += '</div>';
1475     panel.get('element').innerHTML = html;
1477 _client.popup = function(url){
1478     active_instance = repository_client_$suffix;
1479     _client.win = window.open(url,'repo_auth', 'location=0,status=0,scrollbars=0,width=500,height=300');
1480     return false;
1482 _client.print_login = function(){
1483     var panel = new YAHOO.util.Element('panel-$suffix');
1484     var data = _client.ds.login;
1485     var str = '';
1486     for(var k in data){
1487         str += '<p>';
1488         if(data[k].type=='popup'){
1489             str += '<a href="###" onclick="repository_client_$suffix.popup(\''+data[k].url+'\')">test</a>';
1490         }else{
1491             var lable_id = '';
1492             var field_id = '';
1493             var field_value = '';
1494             if(data[k].id){
1495                 lable_id = ' for="'+data[k].id+'"';
1496                 field_id = ' id="'+data[k].id+'"';
1497             }
1498             if (data[k].label) {
1499                 str += '<label'+lable_id+'>'+data[k].label+'</label><br/>';
1500             }
1501             if(data[k].value){
1502                 field_value = ' value="'+data[k].value+'"';
1503             }
1504             str += '<input type="'+data[k].type+'"'+' name="'+data[k].name+'"'+field_id+field_value+' />';
1505         }
1506         str += '</p>';
1507     }
1508     str += '<p><input type="button" onclick="repository_client_$suffix.login()" value="$strsubmit" /></p>';
1509     panel.get('element').innerHTML = str;
1512 _client.viewfiles = function(){
1513     if(_client.viewmode) {
1514         _client.viewthumb();
1515     } else {
1516         _client.viewlist();
1517     }
1519 _client.print_header = function(){
1520     var panel = new YAHOO.util.Element('panel-$suffix');
1521     var str = '';
1522     str += '<div id="repo-tb-$suffix"></div>';
1523     str += _client.uploadcontrol();
1524     panel.set('innerHTML', str);
1525     _client.makepath();
1527 _client.print_footer = function(){
1528     var panel = new YAHOO.util.Element('panel-$suffix');
1529     panel.get('element').innerHTML += _client.makepage();
1530     panel.get('element').innerHTML += _client.uploadcontrol();
1531     var oDiv = document.getElementById('repo-tb-$suffix');
1532     if(!_client.ds.nosearch){
1533         var search = document.createElement('A');
1534         search.href = '###';
1535         search.innerHTML = '<img src="$CFG->pixpath/a/search.png" /> $strsearch';
1536         oDiv.appendChild(search);
1537         search.onclick = function() {
1538             repository_client_$suffix.search(repository_client_$suffix.repositoryid);
1539         }
1540     }
1541     // weather we use cache for this instance, this button will reload listing anyway
1542     var ccache = document.createElement('A');
1543     ccache.href = '###';
1544     ccache.innerHTML = '<img src="$CFG->pixpath/a/refresh.png" /> $strrefresh';
1545     oDiv.appendChild(ccache);
1546     ccache.onclick = function() {
1547         var params = [];
1548         params['env']=_client.env;
1549         params['sesskey']='$sesskey';
1550         params['ctx_id']=$context->id;
1551         params['repo_id']=repository_client_$suffix.repositoryid;
1552         _client.loading('load');
1553         var trans = YAHOO.util.Connect.asyncRequest('POST',
1554             '$CFG->httpswwwroot/repository/ws.php?action=ccache', repository_client_$suffix.req_cb, _client.postdata(params));
1555     }
1556     if(_client.ds.manage){
1557         var mgr = document.createElement('A');
1558         mgr.innerHTML = '<img src="$CFG->pixpath/a/setting.png" /> $strmgr';
1559         mgr.href = _client.ds.manage;
1560         mgr.target = "_blank";
1561         oDiv.appendChild(mgr);
1562     }
1563     if(!_client.ds.nologin){
1564         var logout = document.createElement('A');
1565         logout.href = '###';
1566         logout.innerHTML = '<img src="$CFG->pixpath/a/logout.png" /> $strlogout';
1567         oDiv.appendChild(logout);
1568         logout.onclick = function() {
1569             repository_client_$suffix.req(repository_client_$suffix.repositoryid, 1, 1);
1570         }
1571     }
1573 _client.viewthumb = function(ds){
1574     _client.viewmode = 1;
1575     var panel = new YAHOO.util.Element('panel-$suffix');
1576     _client.viewbar.check(1);
1577     var list = null;
1578     var args = arguments.length;
1579     if(args == 1){
1580         list = ds;
1581     } else {
1582         // from button
1583         list = _client.ds.list;
1584     }
1585     _client.print_header();
1586     var count = 0;
1587     for(k in list){
1588         var el = document.createElement('div');
1589         el.className='grid';
1590         var frame = document.createElement('DIV');
1591         frame.style.textAlign='center';
1592         var img = document.createElement('img');
1593         img.src = list[k].thumbnail;
1594         var link = document.createElement('A');
1595         link.href='###';
1596         link.id = 'img-id-'+String(count);
1597         link.appendChild(img);
1598         frame.appendChild(link);
1599         var title = document.createElement('div');
1600         if(list[k].children){
1601             title.innerHTML = '<i><u>'+list[k].title+'</i></u>';
1602         } else {
1603             if(list[k].url)
1604                 title.innerHTML = '<p><a target="_blank" href="'+list[k].url+'">$strpreview</a></p>';
1605             title.innerHTML += '<span>'+list[k].title+"</span>";
1606         }
1607         title.className = 'label';
1608         el.appendChild(frame);
1609         el.appendChild(title);
1610         panel.get('element').appendChild(el);
1611         if(list[k].children){
1612             var folder = new YAHOO.util.Element(link.id);
1613             folder.ds = list[k].children;
1614             folder.on('contentReady', function(){
1615                 this.on('click', function(){
1616                     if(_client.ds.dynload){
1617                         // TODO: get file list dymanically
1618                     }else{
1619                         _client.viewthumb(this.ds);
1620                     }
1621                 });
1622             });
1623         } else {
1624             var file = new YAHOO.util.Element(link.id);
1625             file.title = list[k].title;
1626             file.value = list[k].source;
1627             file.icon  = list[k].thumbnail;
1628             if(list[k].repo_id){
1629                 file.repo_id = list[k].repo_id;
1630             }else{
1631                 file.repo_id = _client.repositoryid;
1632             }
1633             file.on('contentReady', function(){
1634                 this.on('click', function(){
1635                     repository_client_$suffix.rename(this.title, this.value, this.icon, this.repo_id);
1636                 });
1637             });
1638         }
1639         count++;
1640     }
1641     _client.print_footer();
1643 _client.buildtree = function(node, level){
1644     if(node.children){
1645         node.title = '<i><u>'+node.title+'</u></i>';
1646     }
1647     var info = {label:node.title, title:"$strdate"+node.date+' '+'$strsize'+node.size};
1648     var tmpNode = new YAHOO.widget.TextNode(info, level, false);
1649     var tooltip = new YAHOO.widget.Tooltip(tmpNode.labelElId, {
1650         context:tmpNode.labelElId, text:info.title});
1651     if(node.repo_id){
1652         tmpNode.repo_id=node.repo_id;
1653     }else{
1654         tmpNode.repo_id=_client.repositoryid;
1655     }
1656     tmpNode.filename = node.title;
1657     tmpNode.value  = node.source;
1658     tmpNode.icon = node.thumbnail;
1659     if(node.children){
1660         tmpNode.isLeaf = false;
1661         if (node.path) {
1662             tmpNode.path = node.path;
1663         } else {
1664             tmpNode.path = '';
1665         }
1666         for(var c in node.children){
1667             _client.buildtree(node.children[c], tmpNode);
1668         }
1669     } else {
1670         tmpNode.isLeaf = true;
1671         tmpNode.onLabelClick = function() {
1672             repository_client_$suffix.rename(this.filename, this.value, this.icon, this.repo_id);
1673         }
1674     }
1676 _client.dynload = function (node, fnLoadComplete){
1677     var callback = {
1678         success: function(o) {
1679             try {
1680                 var json = YAHOO.lang.JSON.parse(o.responseText);
1681             } catch(e) {
1682                 alert('$strinvalidjson - '+o.responseText);
1683             }
1684             for(k in json.list){
1685                 _client.buildtree(json.list[k], node);
1686             }
1687             o.argument.fnLoadComplete();
1688         },
1689         failure:function(oResponse){
1690             alert('$strerror');
1691             oResponse.argument.fnLoadComplete();
1692         },
1693         argument:{"node":node, "fnLoadComplete": fnLoadComplete},
1694         timeout:600
1695     }
1696     var params = [];
1697     params['p']=node.path;
1698     params['env']=_client.env;
1699     params['sesskey']='$sesskey';
1700     params['ctx_id']=$context->id;
1701     params['repo_id']=_client.repositoryid;
1702     var trans = YAHOO.util.Connect.asyncRequest('POST',
1703         '$CFG->httpswwwroot/repository/ws.php?action=list', callback, _client.postdata(params));
1705 _client.viewlist = function(){
1706     _client.viewmode = 0;
1707     var panel = new YAHOO.util.Element('panel-$suffix');
1708     _client.viewbar.check(0);
1709     list = _client.ds.list;
1710     _client.print_header();
1711     panel.get('element').innerHTML += '<div id="treediv-$suffix"></div>';
1712     var tree = new YAHOO.widget.TreeView('treediv-$suffix');
1713     if(_client.ds.dynload) {
1714         tree.setDynamicLoad(_client.dynload, 1);
1715     } else {
1716     }
1717     for(k in list){
1718         _client.buildtree(list[k], tree.getRoot());
1719     }
1720     tree.draw();
1721     _client.print_footer();
1723 _client.upload = function(){
1724     var u = _client.ds.upload;
1725     var aform = document.getElementById(u.id);
1726     var parent = document.getElementById(u.id+'_div');
1727     var d = document.getElementById(_client.ds.upload.id+'-file');
1728     if(d.value!='' && d.value!=null){
1729         var container = document.createElement('DIV');
1730         container.id = u.id+'_loading';
1731         container.style.textAlign='center';
1732         var img = document.createElement('IMG');
1733         img.src = '$CFG->pixpath/i/progressbar.gif';
1734         var para = document.createElement('p');
1735         para.innerHTML = '$struploading';
1736         container.appendChild(para);
1737         container.appendChild(img);
1738         parent.appendChild(container);
1739         YAHOO.util.Connect.setForm(aform, true, true);
1740         var trans = YAHOO.util.Connect.asyncRequest('POST',
1741             '$CFG->httpswwwroot/repository/ws.php?action=upload&sesskey=$sesskey&ctx_id=$context->id&repo_id='
1742                 +_client.repositoryid,
1743             _client.upload_cb);
1744     }else{
1745         alert('$strfilenotnull');
1746     }
1748 _client.upload_cb = {
1749     upload: function(o){
1750         try {
1751             var ret = YAHOO.lang.JSON.parse(o.responseText);
1752         } catch(e) {
1753             alert('$strinvalidjson - '+o.responseText);
1754         }
1755         if(ret && ret.e){
1756             var panel = new YAHOO.util.Element('panel-$suffix');
1757             panel.get('element').innerHTML = ret.e;
1758             return;
1759         }
1760         if(ret){
1761             alert('$strsaved');
1762             repository_client_$suffix.end(ret);
1763         }else{
1764             alert('$strinvalidjson');
1765         }
1766     }
1768 _client.uploadcontrol = function() {
1769     var str = '';
1770     if(_client.ds.upload){
1771         str += '<div id="'+_client.ds.upload.id+'_div" class="upload-form">';
1772         str += '<form id="'+_client.ds.upload.id+'" onsubmit="return false">';
1773         str += '<label for="'+_client.ds.upload.id+'-file">'+_client.ds.upload.label+'</label>';
1774         str += '<input type="file" id="'+_client.ds.upload.id+'-file" name="repo_upload_file" />';
1775         str += '<p class="upload"><a href="###" onclick="return repository_client_$suffix.upload();">$strupload</a></p>';
1776         str += '</form>';
1777         str += '</div>';
1778     }
1779     return str;
1781 _client.makepage = function(){
1782     var str = '';
1783     if(_client.ds.pages){
1784         str += '<div class="paging" id="paging-$suffix">';
1785         for(var i = 1; i <= _client.ds.pages; i++) {
1786             str += '<a onclick="repository_client_$suffix.req('+_client.repositoryid+', '+i+', 0)" href="###">';
1787             str += String(i);
1788             str += '</a> ';
1789         }
1790         str += '</div>';
1791     }
1792     return str;
1794 _client.makepath = function(){
1795     if(_client.viewmode == 0) {
1796         return;
1797     }
1798     var panel = new YAHOO.util.Element('panel-$suffix');
1799     var p = _client.ds.path;
1800     if(p && p.length!=0){
1801         var oDiv = document.createElement('DIV');
1802         oDiv.id = "path-$suffix";
1803         oDiv.className = "repo-path";
1804         panel.get('element').appendChild(oDiv);
1805         for(var i = 0; i < _client.ds.path.length; i++) {
1806             var link = document.createElement('A');
1807             link.href = "###";
1808             link.innerHTML = _client.ds.path[i].name;
1809             link.id = 'path-'+i+'-el';
1810             var sep = document.createElement('SPAN');
1811             sep.innerHTML = '/';
1812             oDiv.appendChild(link);
1813             oDiv.appendChild(sep);
1814             var el = new YAHOO.util.Element(link.id);
1815             el.id = _client.repositoryid;
1816             el.path = _client.ds.path[i].path;
1817             el.on('click', function(){
1818                 repository_client_$suffix.req(this.id, this.path, 0);
1819             });
1820         }
1821     }
1823 // send download request
1824 _client.download = function(){
1825     var title = document.getElementById('newname-$suffix').value;
1826     var file = document.getElementById('fileurl-$suffix').value;
1827     _client.loading('download', title);
1828     var params = [];
1829     params['env']=_client.env;
1830     params['file']=file;
1831     params['title']=title;
1832     params['sesskey']='$sesskey';
1833     params['ctx_id']=$context->id;
1834     params['repo_id']=_client.repositoryid;
1835     var trans = YAHOO.util.Connect.asyncRequest('POST',
1836         '$CFG->httpswwwroot/repository/ws.php?action=download', _client.download_cb, _client.postdata(params));
1838 // send login request
1839 _client.login = function(){
1840     var params = [];
1841     var data = _client.ds.login;
1842     for (var k in data) {
1843         if(data[k].type!='popup'){
1844             var el = document.getElementsByName(data[k].name)[0];
1845             params[data[k].name] = '';
1846             if(el.type == 'checkbox') {
1847                 params[data[k].name] = el.checked;
1848             } else {
1849                 params[data[k].name] = el.value;
1850             }
1851         }
1852     }
1853     params['env'] = _client.env;
1854     params['ctx_id'] = $context->id;
1855     params['sesskey']= '$sesskey';
1856     _client.loading('load');
1857     var trans = YAHOO.util.Connect.asyncRequest('POST',
1858         '$CFG->httpswwwroot/repository/ws.php?action=sign', _client.req_cb, _client.postdata(params));
1860 _client.end = function(str){
1861     if(_client.env=='form'){
1862         _client.target.value = str['id'];
1863     }else{
1864         _client.target.value = str['url'];
1865         _client.target.onchange();
1866     }
1867     _client.formcallback(str['file']);
1868     _client.instance.hide();
1869     _client.viewfiles();
1871 _client.hide = function(){
1872     _client.instance.hide();
1873     _client.viewfiles();
1875 // request file list or login
1876 _client.req = function(id, path, reset) {
1877     _client.viewbar.set('disabled', false);
1878     _client.loading('load');
1879     _client.repositoryid = id;
1880     if (reset == 1) {
1881         action = 'logout';
1882     } else {
1883         action = 'list';
1884     }
1885     var params = [];
1886     params['p'] = path;
1887     params['reset']=reset;
1888     params['env']=_client.env;
1889     params['action']=action;
1890     params['sesskey']='$sesskey';
1891     params['ctx_id']=$context->id;
1892     params['repo_id']=id;
1893     var trans = YAHOO.util.Connect.asyncRequest('POST', '$CFG->httpswwwroot/repository/ws.php?action='+action, _client.req_cb, _client.postdata(params));
1895 _client.search = function(id){
1896     var data = window.prompt("$strsearching");
1897     if(data == '') {
1898         alert('$strnoenter');
1899         return;
1900     }else if(data == null){
1901         return;
1902     }
1903     _client.viewbar.set('disabled', false);
1904     _client.loading('load');
1905     var params = [];
1906     params['s']=data;
1907     params['env']=_client.env;
1908     params['sesskey']='$sesskey';
1909     params['ctx_id']=$context->id;
1910     params['repo_id']=id;
1911     var trans = YAHOO.util.Connect.asyncRequest('POST', '$CFG->httpswwwroot/repository/ws.php?action=search', _client.req_cb, _client.postdata(params));
1913 _client.req_cb = {
1914     success: function(o) {
1915         var panel = new YAHOO.util.Element('panel-$suffix');
1916         try {
1917             var ret = YAHOO.lang.JSON.parse(o.responseText);
1918         } catch(e) {
1919             alert('$strinvalidjson - '+o.responseText);
1920         };
1921         if(ret && ret.e){
1922             panel.get('element').innerHTML = ret.e;
1923             return;
1924         }
1925         _client.ds = ret;
1926         if(!_client.ds){
1927             return;
1928         }else if(_client.ds && _client.ds.login){
1929             _client.print_login();
1930         } else if(_client.ds.list) {
1931             if(_client.viewmode) {
1932                 _client.viewthumb();
1933             } else {
1934                 _client.viewlist();
1935             }
1936         }
1937     }
1939 _client.download_cb = {
1940     success: function(o) {
1941         var panel = new YAHOO.util.Element('panel-$suffix');
1942         try {
1943             var ret = YAHOO.lang.JSON.parse(o.responseText);
1944         } catch(e) {
1945             alert('$strinvalidjson - '+o.responseText);
1946         }
1947         if(ret && ret.e){
1948             panel.get('element').innerHTML = ret.e;
1949             return;
1950         }
1951         if(ret){
1952             repository_client_$suffix.end(ret);
1953         }else{
1954             alert('$strinvalidjson');
1955         }
1956     }
1959 return _client;
1960 })();
1961 EOD;
1963     $repos = repository_get_instances(array($context,get_system_context()));
1964     foreach($repos as $repo) {
1965         $js .= "\r\n";
1966         $js .= 'repository_client_'.$suffix.'.repos.push('.json_encode($repo->ajax_info()).');'."\n";
1967     }
1968     $js .= "\r\n";
1970     $js .= <<<EOD
1971 function openpicker_$suffix(params) {
1972     if(!repository_client_$suffix.instance) {
1973         repository_client_$suffix.env = params.env;
1974         repository_client_$suffix.target = params.target;
1975         if(params.type){
1976             repository_client_$suffix.filetype = params.filetype;
1977         } else {
1978             repository_client_$suffix.filetype = 'all';
1979         }
1980         repository_client_$suffix.instance = new repository_client_$suffix();
1981         repository_client_$suffix.instance.create_picker();
1982         if(params.callback){
1983             repository_client_$suffix.formcallback = params.callback;
1984         } else {
1985             repository_client_$suffix.formcallback = function(){};
1986         }
1987     } else {
1988         repository_client_$suffix.instance.show();
1989     }
1991 //]]>
1992 </script>
1993 EOD;
1994     return array('css'=>$css, 'js'=>$js, 'suffix'=>$suffix);
1997 /**
1998  * TODO: write comment
1999  */
2000 final class repository_instance_form extends moodleform {
2001     protected $instance;
2002     protected $plugin;
2004     /**
2005      * TODO: write comment
2006      * @global <type> $CFG
2007      */
2008     public function definition() {
2009         global $CFG;
2010         // type of plugin, string
2011         $this->plugin = $this->_customdata['plugin'];
2012         $this->typeid = $this->_customdata['typeid'];
2013         $this->contextid = $this->_customdata['contextid'];
2014         $this->instance = (isset($this->_customdata['instance'])
2015                 && is_subclass_of($this->_customdata['instance'], 'repository'))
2016             ? $this->_customdata['instance'] : null;
2018         $mform =& $this->_form;
2019         $strrequired = get_string('required');
2021         $mform->addElement('hidden', 'edit',  ($this->instance) ? $this->instance->id : 0);
2022         $mform->addElement('hidden', 'new',   $this->plugin);
2023         $mform->addElement('hidden', 'plugin', $this->plugin);
2024         $mform->addElement('hidden', 'typeid', $this->typeid);
2025         $mform->addElement('hidden', 'contextid', $this->contextid);
2027         $mform->addElement('text', 'name', get_string('name'), 'maxlength="100" size="30"');
2028         $mform->addRule('name', $strrequired, 'required', null, 'client');
2030         // let the plugin add the fields they want (either statically or not)
2031         if (repository_static_function($this->plugin, 'has_instance_config')) {
2032             if (!$this->instance) {
2033                 $result = repository_static_function($this->plugin, 'instance_config_form', $mform);
2034             } else {
2035                 $result = $this->instance->instance_config_form($mform);
2036             }
2037         }
2039         // and set the data if we have some.
2040         if ($this->instance) {
2041             $data = array();
2042             $data['name'] = $this->instance->name;
2043             foreach ($this->instance->get_instance_option_names() as $config) {
2044                 if (!empty($this->instance->$config)) {
2045                     $data[$config] = $this->instance->$config;
2046                 } else {
2047                     $data[$config] = '';
2048                 }
2049             }
2050             $this->set_data($data);
2051         }
2052         $this->add_action_buttons(true, get_string('save','repository'));
2053     }
2055     /**
2056      * TODO: write comment
2057      * @global <type> $DB
2058      * @param <type> $data
2059      * @return <type>
2060      */
2061     public function validation($data) {
2062         global $DB;
2064         $errors = array();
2065         if ($DB->count_records('repository_instances', array('name' => $data['name'], 'typeid' => $data['typeid'])) > 1) {
2066             $errors = array('name' => get_string('err_uniquename', 'repository'));
2067         }
2069         $pluginerrors = array();
2070         if ($this->instance) {
2071             //$pluginerrors = $this->instance->admin_config_validation($data);
2072         } else {
2073             //$pluginerrors = repository_static_function($this->plugin, 'admin_config_validation', $data);
2074         }
2075         if (is_array($pluginerrors)) {
2076             $errors = array_merge($errors, $pluginerrors);
2077         }
2078         return $errors;
2079     }
2083 /**
2084  * Display a form with the general option fields of a type
2085  */
2086 final class repository_admin_form extends moodleform {
2087     protected $instance;
2088     protected $plugin;
2090     /**
2091      * Definition of the moodleform
2092      * @global object $CFG
2093      */
2094     public function definition() {
2095         global $CFG;
2096         // type of plugin, string
2097         $this->plugin = $this->_customdata['plugin'];
2098         $this->instance = (isset($this->_customdata['instance'])
2099                 && is_a($this->_customdata['instance'], 'repository_type'))
2100             ? $this->_customdata['instance'] : null;
2102         $mform =& $this->_form;
2103         $strrequired = get_string('required');
2105         $mform->addElement('hidden', 'edit',  ($this->instance) ? $this->instance->get_typename() : 0);
2106         $mform->addElement('hidden', 'new',   $this->plugin);
2107         $mform->addElement('hidden', 'plugin', $this->plugin);
2108         // let the plugin add the fields they want (either statically or not)
2109         if (repository_static_function($this->plugin, 'has_admin_config')) {
2110             if (!$this->instance) {
2111                 $result = repository_static_function($this->plugin, 'admin_config_form', $mform);
2112             } else {
2113                   $classname = 'repository_' . $this->instance->get_typename();
2114                   $result = call_user_func(array($classname,'admin_config_form'),$mform);
2115             }
2116         }
2118         // and set the data if we have some.
2119         if ($this->instance) {
2120             $data = array();
2121             $option_names = call_user_func(array($classname,'get_admin_option_names'));
2122             $instanceoptions = $this->instance->get_options();
2123             foreach ($option_names as $config) {
2124                 if (!empty($instanceoptions[$config])) {
2125                     $data[$config] = $instanceoptions[$config];
2126                 } else {
2127                     $data[$config] = '';
2128                 }
2129             }
2130             $this->set_data($data);
2131         }
2132         $this->add_action_buttons(true, get_string('save','repository'));
2133     }
2138 /**
2139  * Display a repository instance list (with edit/delete/create links)
2140  * @global object $CFG
2141  * @global object $USER
2142  * @param object $context the context for which we display the instance
2143  * @param string $typename if set, we display only one type of instance
2144  */
2145 function repository_display_instances_list($context, $typename = null){
2146        global $CFG, $USER;
2148         $output = print_box_start('generalbox','',true);
2149         //if the context is SYSTEM, so we call it from administration page
2150         $admin = ($context->id == SYSCONTEXTID) ? true : false;
2151         if($admin) {
2152             $baseurl = $CFG->httpswwwroot . '/admin/repositoryinstance.php?sesskey=' . sesskey();
2153              $output .= "<div ><h2 style='text-align: center'>" . get_string('siteinstances', 'repository') . " ";
2154              $output .= "</h2></div>";
2155         } else {
2156           $baseurl = $CFG->httpswwwroot . '/repository/manage_instances.php?contextid=' . $context->id . '&amp;sesskey=' . sesskey();
2158         }
2160         $namestr = get_string('name');
2161         $pluginstr = get_string('plugin', 'repository');
2162         $settingsstr = get_string('settings');
2163         $deletestr = get_string('delete');
2164         $updown = get_string('updown', 'repository');
2165         $plugins = get_list_of_plugins('repository');
2166         //retrieve list of instances. In administration context we want to display all
2167         //instances of a type, even if this type is not visible. In course/user context we
2168         //want to display only visible instances, but for every type types. The repository_get_instances()
2169         //third parameter displays only visible type.
2170         $instances = repository_get_instances(array($context),null,!$admin,$typename);
2171         $instancesnumber = count($instances);
2172         $alreadyplugins = array();
2173         $table = new StdClass;
2174         $table->head = array($namestr, $pluginstr, $deletestr, $settingsstr);
2175         $table->align = array('left', 'left', 'center','center');
2176         $table->data = array();
2177         $updowncount=1;
2178         foreach ($instances as $i) {
2179             $settings = '';
2180             $settings .= '<a href="' . $baseurl . '&amp;type='.$typename.'&amp;edit=' . $i->id . '">' . $settingsstr . '</a>' . "\n";
2181             $delete = '<a href="' . $baseurl . '&amp;type='.$typename.'&amp;delete=' .  $i->id . '">' . $deletestr . '</a>' . "\n";
2183             $type = repository_get_type_by_id($i->typeid);
2184             $table->data[] = array($i->name, $type->get_readablename(), $delete, $settings);
2186             //display a grey row if the type is defined as not visible
2187             if (isset($type) && !$type->get_visible()){
2188                 $table->rowclass[] = 'dimmed_text';
2189             } else{
2190                 $table->rowclass[] = '';
2191             }
2193             if (!in_array($i->name, $alreadyplugins)) {
2194                 $alreadyplugins[] = $i->name;
2195             }
2196         }
2197         $output .= print_table($table, true);
2198         $instancehtml = '<div>';
2199         $addable = 0;
2201         //if no type is set, we can create all type of instance
2202         if (!$typename) {
2203             $instancehtml .= '<h3>';
2204             $instancehtml .= get_string('createrepository', 'repository');
2205             $instancehtml .= '</h3><ul>';
2206             foreach ($plugins as $p) {
2207                    $type = repository_get_type_by_typename($p);
2208                    if (!empty($type) && $type->get_visible()) {
2209                        if (repository_static_function($p, 'has_multiple_instances')){
2210                             $instancehtml .= '<li><a href="'.$baseurl.'&amp;new='.$p.'">'.get_string('create', 'repository')
2211                                 .' "'.get_string('repositoryname', 'repository_'.$p).'" '
2212                                 .get_string('instance', 'repository').'</a></li>';
2213                             $addable++;
2214                         }
2215                    }
2216             }
2217              $instancehtml .= '</ul>';
2218         }
2219         //create a unique type of instance
2220         else {
2221             if (repository_static_function($typename, 'has_multiple_instances')){
2222                 $addable = 1;
2223                 $instancehtml .= "<form action='".$baseurl."&amp;new=".$typename."' method='post'>
2224                                   <p style='text-align:center'><input type='submit' value='".get_string('createinstance', 'repository')."'/></p>
2225                                   </form>";
2226             }
2227         }
2229         if ($addable) {
2230             $instancehtml .= '</div>';
2231             $output .= $instancehtml;
2232         }
2234         $output .= print_box_end(true);
2236         //print the list + creation links
2237         print($output);