Merge branch 'MDL-64012' of https://github.com/timhunt/moodle
[moodle.git] / lib / form / filemanager.js
blob7a9a6209c672e8668ee3629668afd3a1b04c5143
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15 /**
16  *
17  * File Manager UI
18  * =====
19  * this.api, stores the URL to make ajax request
20  * this.currentpath
21  * this.filepicker_options
22  * this.movefile_dialog
23  * this.mkdir_dialog
24  * this.rename_dialog
25  * this.client_id
26  * this.filecount, how many files in this filemanager
27  * this.maxfiles
28  * this.maxbytes
29  * this.areamaxbytes, the maximum size of the area
30  * this.filemanager, contains reference to filemanager Node
31  * this.selectnode, contains referenct to select-file Node
32  * this.selectui, M.core.dialogue to select the file
33  * this.viewmode, contains current view mode - icons, tree or details
34  *
35  * FileManager options:
36  * =====
37  * this.options.currentpath
38  * this.options.itemid
39  */
42 M.form_filemanager = {templates:{}};
44 M.form_filemanager.set_templates = function(Y, templates) {
45     M.form_filemanager.templates = templates;
48 /**
49  * This fucntion is called for each file picker on page.
50  */
51 M.form_filemanager.init = function(Y, options) {
52     var FileManagerHelper = function(options) {
53         FileManagerHelper.superclass.constructor.apply(this, arguments);
54     };
55     FileManagerHelper.NAME = "FileManager";
56     FileManagerHelper.ATTRS = {
57         options: {},
58         lang: {}
59     };
61     Y.extend(FileManagerHelper, Y.Base, {
62         api: M.cfg.wwwroot+'/repository/draftfiles_ajax.php',
63         menus: {},
64         initializer: function(options) {
65             this.options = options;
66             if (options.mainfile) {
67                 this.enablemainfile = options.mainfile;
68             }
69             this.client_id = options.client_id;
70             this.currentpath = '/';
71             this.maxfiles = options.maxfiles;
72             this.maxbytes = options.maxbytes;
73             this.areamaxbytes = options.areamaxbytes;
74             this.userprefs = options.userprefs;
75             this.emptycallback = null; // Used by drag and drop upload
77             this.filepicker_options = options.filepicker?options.filepicker:{};
78             this.filepicker_options.client_id = this.client_id;
79             this.filepicker_options.context = options.context;
80             this.filepicker_options.maxfiles = this.maxfiles;
81             this.filepicker_options.maxbytes = this.maxbytes;
82             this.filepicker_options.areamaxbytes = this.areamaxbytes;
83             this.filepicker_options.env = 'filemanager';
84             this.filepicker_options.itemid = options.itemid;
86             if (options.filecount) {
87                 this.filecount = options.filecount;
88             } else {
89                 this.filecount = 0;
90             }
91             // prepare filemanager for drag-and-drop upload
92             this.filemanager = Y.one('#filemanager-'+options.client_id);
93             if (this.filemanager.hasClass('filemanager-container') || !this.filemanager.one('.filemanager-container')) {
94                 this.dndcontainer = this.filemanager;
95             } else  {
96                 this.dndcontainer = this.filemanager.one('.filemanager-container');
97                 if (!this.dndcontainer.get('id')) {
98                     this.dndcontainer.generateID();
99                 }
100             }
101             // save template for one path element and location of path bar
102             if (this.filemanager.one('.fp-path-folder')) {
103                 this.pathnode = this.filemanager.one('.fp-path-folder');
104                 this.pathbar = this.pathnode.get('parentNode');
105                 this.pathbar.removeChild(this.pathnode);
106             }
107             // initialize 'select file' panel
108             this.selectnode = Y.Node.create(M.form_filemanager.templates.fileselectlayout);
109             this.selectnode.setAttribute('aria-live', 'assertive');
110             this.selectnode.setAttribute('role', 'dialog');
111             this.selectnode.generateID();
113             var labelid = 'fm-dialog-label_'+ this.selectnode.get('id');
114             this.selectui = new M.core.dialogue({
115                 draggable    : true,
116                 headerContent: '<h3 id="' + labelid +'">' + M.util.get_string('edit', 'moodle') + '</h3>',
117                 bodyContent  : this.selectnode,
118                 centered     : true,
119                 width        : '480px',
120                 modal        : true,
121                 visible      : false
122             });
123             Y.one('#'+this.selectnode.get('id')).setAttribute('aria-labelledby', labelid);
124             this.selectui.hide();
125             this.setup_select_file();
126             // setup buttons onclick events
127             this.setup_buttons();
128             // set event handler for lazy loading of thumbnails
129             this.filemanager.one('.fp-content').on(['scroll','resize'], this.content_scrolled, this);
130             // display files
131             this.viewmode = this.get_preference("recentviewmode");
132             if (this.viewmode != 2 && this.viewmode != 3) {
133                 this.viewmode = 1;
134             }
135             var viewmodeselectors = {'1': '.fp-vb-icons', '2': '.fp-vb-tree', '3': '.fp-vb-details'};
136             this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').removeClass('checked');
137             this.filemanager.all(viewmodeselectors[this.viewmode]).addClass('checked');
138             this.refresh(this.currentpath); // MDL-31113 get latest list from server
139         },
141         wait: function() {
142            this.filemanager.addClass('fm-updating');
143         },
144         request: function(args, redraw) {
145             var api = this.api + '?action='+args.action;
146             var params = {};
147             var scope = this;
148             if (args['scope']) {
149                 scope = args['scope'];
150             }
151             params['sesskey'] = M.cfg.sesskey;
152             params['client_id'] = this.client_id;
153             params['filepath'] = this.currentpath;
154             params['itemid'] = this.options.itemid?this.options.itemid:0;
155             if (args['params']) {
156                 for (i in args['params']) {
157                     params[i] = args['params'][i];
158                 }
159             }
160             var cfg = {
161                 method: 'POST',
162                 on: {
163                     complete: function(id,o,p) {
164                         if (!o) {
165                             alert('IO FATAL');
166                             return;
167                         }
168                         var data = null;
169                         try {
170                             data = Y.JSON.parse(o.responseText);
171                         } catch(e) {
172                             scope.print_msg(M.util.get_string('invalidjson', 'repository'), 'error');
173                             Y.error(M.util.get_string('invalidjson', 'repository')+":\n"+o.responseText);
174                             return;
175                         }
176                         if (data && data.tree && scope.set_current_tree) {
177                             scope.set_current_tree(data.tree);
178                         }
179                         args.callback(id,data,p);
180                     }
181                 },
182                 arguments: {
183                     scope: scope
184                 },
185                 headers: {
186                     'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
187                 },
188                 data: build_querystring(params)
189             };
190             if (args.form) {
191                 cfg.form = args.form;
192             }
193             Y.io(api, cfg);
194             if (redraw) {
195                 this.wait();
196             }
197         },
198         filepicker_callback: function(obj) {
199             this.filecount++;
200             this.check_buttons();
201             this.refresh(this.currentpath);
202             if (typeof M.core_formchangechecker != 'undefined') {
203                 M.core_formchangechecker.set_form_changed();
204             }
205         },
206         check_buttons: function() {
207             if (this.filecount>0) {
208                 this.filemanager.removeClass('fm-nofiles');
209             } else {
210                 this.filemanager.addClass('fm-nofiles');
211             }
212             if (this.filecount >= this.maxfiles && this.maxfiles!=-1) {
213                 this.filemanager.addClass('fm-maxfiles');
214             }
215             else {
216                 this.filemanager.removeClass('fm-maxfiles');
217             }
218         },
219         refresh: function(filepath) {
220             var scope = this;
221             this.currentpath = filepath;
222             if (!filepath) {
223                 filepath = this.currentpath;
224             } else {
225                 this.currentpath = filepath;
226             }
227             this.request({
228                 action: 'list',
229                 scope: scope,
230                 params: {'filepath':filepath},
231                 callback: function(id, obj, args) {
232                     scope.filecount = obj.filecount;
233                     scope.options = obj;
234                     scope.lazyloading = {};
235                     scope.check_buttons();
236                     scope.render(obj);
237                 }
238             }, true);
239         },
240         /** displays message in a popup */
241         print_msg: function(msg, type) {
242             var header = M.util.get_string('error', 'moodle');
243             if (type != 'error') {
244                 type = 'info'; // one of only two types excepted
245                 header = M.util.get_string('info', 'moodle');
246             }
247             if (!this.msg_dlg) {
248                 this.msg_dlg_node = Y.Node.create(M.form_filemanager.templates.message);
249                 var nodeid = this.msg_dlg_node.generateID();
251                 this.msg_dlg = new M.core.dialogue({
252                     draggable    : true,
253                     bodyContent  : this.msg_dlg_node,
254                     centered     : true,
255                     modal        : true,
256                     visible      : false,
257                 });
258                 this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) {
259                     e.preventDefault();
260                     this.msg_dlg.hide();
261                 }, this);
262             }
264             this.msg_dlg.set('headerContent', header);
265             this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type)
266             this.msg_dlg_node.one('.fp-msg-text').setContent(Y.Escape.html(msg));
267             this.msg_dlg.show();
268         },
269         is_disabled: function() {
270             return this.filemanager.ancestor('.fitem.disabled') != null;
271         },
272         setup_buttons: function() {
273             var button_download = this.filemanager.one('.fp-btn-download');
274             var button_create   = this.filemanager.one('.fp-btn-mkdir');
275             var button_addfile  = this.filemanager.one('.fp-btn-add');
277             // setup 'add file' button
278             button_addfile.on('click', this.show_filepicker, this);
280             var dndarrow = this.filemanager.one('.dndupload-arrow');
281             if (dndarrow) {
282                 dndarrow.on('click', this.show_filepicker, this);
283             }
285             // setup 'make a folder' button
286             if (this.options.subdirs) {
287                 button_create.on('click',function(e) {
288                     e.preventDefault();
289                     if (this.is_disabled()) {
290                         return;
291                     }
292                     var scope = this;
293                     // a function used to perform an ajax request
294                     var perform_action = function(e) {
295                         e.preventDefault();
296                         var foldername = Y.one('#fm-newname-'+scope.client_id).get('value');
297                         if (!foldername) {
298                             scope.mkdir_dialog.hide();
299                             return;
300                         }
301                         scope.request({
302                             action:'mkdir',
303                             params: {filepath:scope.currentpath, newdirname:foldername},
304                             callback: function(id, obj, args) {
305                                 var filepath = obj.filepath;
306                                 scope.mkdir_dialog.hide();
307                                 scope.refresh(filepath);
308                                 Y.one('#fm-newname-'+scope.client_id).set('value', '');
309                                 if (typeof M.core_formchangechecker != 'undefined') {
310                                     M.core_formchangechecker.set_form_changed();
311                                 }
312                             }
313                         });
314                     };
315                     var validate_folder_name = function() {
316                         var valid = false;
317                         var foldername = Y.one('#fm-newname-'+scope.client_id).get('value');
318                         if (foldername.length > 0) {
319                             valid = true;
320                         }
321                         var btn = Y.one('#fm-mkdir-butcreate-'+scope.client_id);
322                         if (btn) {
323                             btn.set('disabled', !valid);
324                         }
325                         return valid;
326                     };
327                     if (!this.mkdir_dialog) {
328                         var node = Y.Node.create(M.form_filemanager.templates.mkdir);
329                         this.mkdir_dialog = new M.core.dialogue({
330                             draggable    : true,
331                             bodyContent  : node,
332                             centered     : true,
333                             modal        : true,
334                             visible      : false,
335                         });
336                         node.one('.fp-dlg-butcreate').set('id', 'fm-mkdir-butcreate-'+this.client_id).on('click',
337                                 perform_action, this);
338                         node.one('input').set('id', 'fm-newname-'+this.client_id).on('keydown', function(e) {
339                             var valid = Y.bind(validate_folder_name, this)();
340                             if (valid && e.keyCode === 13) {
341                                 Y.bind(perform_action, this)(e);
342                             }
343                         }, this);
344                         node.one('#fm-newname-'+this.client_id).on(['keyup', 'change'], function(e) {
345                             Y.bind(validate_folder_name, this)();
346                         }, this);
348                         node.one('label').set('for', 'fm-newname-' + this.client_id);
349                         node.all('.fp-dlg-butcancel').on('click', function(e){e.preventDefault();this.mkdir_dialog.hide();}, this);
350                         node.all('.fp-dlg-curpath').set('id', 'fm-curpath-'+this.client_id);
351                     }
352                     this.mkdir_dialog.show();
354                     // Default folder name:
355                     var foldername = M.util.get_string('newfolder', 'repository');
356                     while (this.has_folder(foldername)) {
357                         foldername = increment_filename(foldername, true);
358                     }
359                     Y.one('#fm-newname-'+scope.client_id).set('value', foldername);
360                     Y.bind(validate_folder_name, this)();
361                     Y.one('#fm-newname-'+scope.client_id).focus().select();
362                     Y.all('#fm-curpath-'+scope.client_id).setContent(this.currentpath);
363                 }, this);
364             } else {
365                 this.filemanager.addClass('fm-nomkdir');
366             }
368             // setup 'download this folder' button
369             button_download.on('click',function(e) {
370                 e.preventDefault();
371                 if (this.is_disabled()) {
372                     return;
373                 }
374                 var scope = this;
376                 var image_downloading = this.filemanager.one('.fp-img-downloading');
377                 if (image_downloading.getStyle('display') == 'inline') {
378                     return;
379                 }
380                 image_downloading.setStyle('display', 'inline');
382                 // perform downloaddir ajax request
383                 this.request({
384                     action: 'downloaddir',
385                     scope: scope,
386                     callback: function(id, obj, args) {
387                         var image_downloading = scope.filemanager.one('.fp-img-downloading');
388                         image_downloading.setStyle('display', 'none');
390                         if (obj) {
391                             scope.refresh(obj.filepath);
392                             node = Y.Node.create('<iframe></iframe>').setStyles({
393                                 visibility : 'hidden',
394                                 width : '1px',
395                                 height : '1px'
396                             });
397                             node.set('src', obj.fileurl);
398                             Y.one('body').appendChild(node);
399                         } else {
400                             scope.print_msg(M.util.get_string('draftareanofiles', 'repository'), 'error');
401                         }
402                     }
403                 });
404             }, this);
406             this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').
407                 on('click', function(e) {
408                     e.preventDefault();
409                     var viewbar = this.filemanager.one('.fp-viewbar')
410                     if (!this.is_disabled() && (!viewbar || !viewbar.hasClass('disabled'))) {
411                         this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').removeClass('checked')
412                         if (e.currentTarget.hasClass('fp-vb-tree')) {
413                             this.viewmode = 2;
414                         } else if (e.currentTarget.hasClass('fp-vb-details')) {
415                             this.viewmode = 3;
416                         } else {
417                             this.viewmode = 1;
418                         }
419                         e.currentTarget.addClass('checked')
420                         this.render();
421                         this.filemanager.one('.fp-content').setAttribute('tabIndex', '0');
422                         this.filemanager.one('.fp-content').focus();
423                         this.set_preference('recentviewmode', this.viewmode);
424                     }
425                 }, this);
426         },
428         show_filepicker: function (e) {
429             // if maxfiles == -1, the no limit
430             e.preventDefault();
431             if (this.is_disabled()) {
432                 return;
433             }
434             var options = this.filepicker_options;
435             options.formcallback = this.filepicker_callback;
436             // XXX: magic here, to let filepicker use filemanager scope
437             options.magicscope = this;
438             options.savepath = this.currentpath;
439             M.core_filepicker.show(Y, options);
440         },
442         print_path: function() {
443             var p = this.options.path;
444             this.pathbar.setContent('').addClass('empty');
445             if (p && p.length!=0 && this.viewmode != 2) {
446                 for(var i = 0; i < p.length; i++) {
447                     var el = this.pathnode.cloneNode(true);
448                     this.pathbar.appendChild(el);
450                     if (i == 0) {
451                         el.addClass('first');
452                     }
453                     if (i == p.length-1) {
454                         el.addClass('last');
455                     }
457                     if (i%2) {
458                         el.addClass('even');
459                     } else {
460                         el.addClass('odd');
461                     }
462                     el.one('.fp-path-folder-name').setContent(Y.Escape.html(p[i].name)).
463                         on('click', function(e, path) {
464                             e.preventDefault();
465                             if (!this.is_disabled()) {
466                                 this.refresh(path);
467                             }
468                         }, this, p[i].path);
469                 }
470                 this.pathbar.removeClass('empty');
471             }
472         },
473         get_filepath: function(obj) {
474             if (obj.path && obj.path.length) {
475                 return obj.path[obj.path.length-1].path;
476             }
477             return '';
478         },
479         treeview_dynload: function(node, cb) {
480             var retrieved_children = {};
481             if (node.children) {
482                 for (var i in node.children) {
483                     retrieved_children[node.children[i].path] = node.children[i];
484                 }
485             }
486             if (!node.path || node.path == '/') {
487                 // this is a root pseudo folder
488                 node.fileinfo.filepath = '/';
489                 node.fileinfo.type = 'folder';
490                 node.fileinfo.fullname = node.fileinfo.title;
491                 node.fileinfo.filename = '.';
492             }
493             this.request({
494                 action:'list',
495                 params: {filepath:node.path?node.path:''},
496                 scope:this,
497                 callback: function(id, obj, args) {
498                     var list = obj.list;
499                     var scope = args.scope;
500                     // check that user did not leave the view mode before recieving this response
501                     if (!(scope.viewmode == 2 && node && node.getChildrenEl())) {
502                         return;
503                     }
504                     if (cb != null) { // (in manual mode do not update current path)
505                         scope.options = obj;
506                         scope.currentpath = node.path?node.path:'/';
507                     }
508                     node.highlight(false);
509                     node.origlist = obj.list ? obj.list : null;
510                     node.origpath = obj.path ? obj.path : null;
511                     node.children = [];
512                     for(k in list) {
513                         if (list[k].type == 'folder' && retrieved_children[list[k].filepath]) {
514                             // if this child is a folder and has already been retrieved
515                             retrieved_children[list[k].filepath].fileinfo = list[k];
516                             node.children[node.children.length] = retrieved_children[list[k].filepath];
517                         } else {
518                             // append new file to the list
519                             scope.view_files([list[k]]);
520                         }
521                     }
522                     if (cb == null) {
523                         node.refresh();
524                     } else {
525                         // invoke callback requested by TreeView component
526                         cb();
527                     }
528                     scope.content_scrolled();
529                 }
530             }, false);
531         },
532         content_scrolled: function(e) {
533             setTimeout(Y.bind(function() {
534                 if (this.processingimages) {return;}
535                 this.processingimages = true;
536                 var scope = this,
537                     fpcontent = this.filemanager.one('.fp-content'),
538                     fpcontenty = fpcontent.getY(),
539                     fpcontentheight = fpcontent.getStylePx('height'),
540                     is_node_visible = function(node) {
541                         var offset = node.getY()-fpcontenty;
542                         if (offset <= fpcontentheight && (offset >=0 || offset+node.getStylePx('height')>=0)) {
543                             return true;
544                         }
545                         return false;
546                     };
547                 // replace src for visible images that need to be lazy-loaded
548                 if (scope.lazyloading) {
549                     fpcontent.all('img').each( function(node) {
550                         if (node.get('id') && scope.lazyloading[node.get('id')] && is_node_visible(node)) {
551                             node.setImgRealSrc(scope.lazyloading);
552                         }
553                     });
554                 }
555                 this.processingimages = false;
556             }, this), 200)
557         },
558         view_files: function(appendfiles) {
559             this.filemanager.removeClass('fm-updating').removeClass('fm-noitems');
560             if ((appendfiles == null) && (!this.options.list || this.options.list.length == 0) && this.viewmode != 2) {
561                 this.filemanager.addClass('fm-noitems');
562                 return;
563             }
564             var list = (appendfiles != null) ? appendfiles : this.options.list;
565             var element_template;
566             if (this.viewmode == 2 || this.viewmode == 3) {
567                 element_template = Y.Node.create(M.form_filemanager.templates.listfilename);
568             } else {
569                 this.viewmode = 1;
570                 element_template = Y.Node.create(M.form_filemanager.templates.iconfilename);
571             }
572             var options = {
573                 viewmode : this.viewmode,
574                 appendonly : appendfiles != null,
575                 filenode : element_template,
576                 callbackcontext : this,
577                 callback : function(e, node) {
578                     if (e.preventDefault) { e.preventDefault(); }
579                     if (node.type == 'folder') {
580                         this.refresh(node.filepath);
581                     } else {
582                         this.select_file(node);
583                     }
584                 },
585                 rightclickcallback : function(e, node) {
586                     if (e.preventDefault) { e.preventDefault(); }
587                     this.select_file(node);
588                 },
589                 classnamecallback : function(node) {
590                     var classname = '';
591                     if (node.type == 'folder' || (!node.type && !node.filename)) {
592                         classname = classname + ' fp-folder';
593                     }
594                     if (node.filename || node.filepath || (node.path && node.path != '/')) {
595                         classname = classname + ' fp-hascontextmenu';
596                     }
597                     if (node.isref) {
598                         classname = classname + ' fp-isreference';
599                     }
600                     if (node.refcount) {
601                         classname = classname + ' fp-hasreferences';
602                     }
603                     if (node.originalmissing) {
604                         classname = classname + ' fp-originalmissing';
605                     }
606                     if (node.sortorder == 1) { classname = classname + ' fp-mainfile';}
607                     return Y.Lang.trim(classname);
608                 }
609             };
610             if (this.viewmode == 2) {
611                 options.dynload = true;
612                 options.filepath = this.options.path;
613                 options.treeview_dynload = this.treeview_dynload;
614                 options.norootrightclick = true;
615                 options.callback = function(e, node) {
616                     // TODO MDL-32736 e is not an event here but an object with properties 'event' and 'node'
617                     if (!node.fullname) {return;}
618                     if (node.type != 'folder') {
619                         if (e.node.parent && e.node.parent.origpath) {
620                             // set the current path
621                             this.options.path = e.node.parent.origpath;
622                             this.options.list = e.node.parent.origlist;
623                             this.print_path();
624                         }
625                         this.currentpath = node.filepath;
626                         this.select_file(node);
627                     } else {
628                         // save current path and filelist (in case we want to jump to other viewmode)
629                         this.options.path = e.node.origpath;
630                         this.options.list = e.node.origlist;
631                         this.currentpath = node.filepath;
632                         this.print_path();
633                         //this.content_scrolled();
634                     }
635                 };
636             }
637             if (!this.lazyloading) {
638                 this.lazyloading={};
639             }
640             this.filemanager.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);
641             this.content_scrolled();
642         },
643         populate_licenses_select: function(node) {
644             if (!node) {
645                 return;
646             }
647             node.setContent('');
648             var licenses = this.options.licenses;
649             for (var i in licenses) {
650                 var option = Y.Node.create('<option/>').
651                     set('value', licenses[i].shortname).
652                     setContent(Y.Escape.html(licenses[i].fullname));
653                 node.appendChild(option)
654             }
655         },
656         set_current_tree: function(tree) {
657             var appendfilepaths = function(list, node) {
658                 if (!node || !node.children || !node.children.length) {return;}
659                 for (var i in node.children) {
660                     list[list.length] = node.children[i].filepath;
661                     appendfilepaths(list, node.children[i]);
662                 }
663             }
664             var list = ['/'];
665             appendfilepaths(list, tree);
666             var selectnode = this.selectnode;
667             node = selectnode.one('.fp-path select');
668             node.setContent('');
669             for (var i in list) {
670                 node.appendChild(Y.Node.create('<option/>').
671                     set('value', list[i]).setContent(Y.Escape.html(list[i])));
672             }
673         },
674         update_file: function(confirmed) {
675             var selectnode = this.selectnode;
676             var fileinfo = this.selectui.fileinfo;
678             var newfilename = Y.Lang.trim(selectnode.one('.fp-saveas input').get('value'));
679             var filenamechanged = (newfilename && newfilename != fileinfo.fullname);
680             var pathselect = selectnode.one('.fp-path select'),
681                     pathindex = pathselect.get('selectedIndex'),
682                     targetpath = pathselect.get("options").item(pathindex).get('value');
683             var filepathchanged = (targetpath != this.get_parent_folder_name(fileinfo));
684             var newauthor = Y.Lang.trim(selectnode.one('.fp-author input').get('value'));
685             var authorchanged = (newauthor != Y.Lang.trim(fileinfo.author));
686             var licenseselect = selectnode.one('.fp-license select'),
687                     licenseindex = licenseselect.get('selectedIndex'),
688                     newlicense = licenseselect.get("options").item(licenseindex).get('value');
689             var licensechanged = (newlicense != fileinfo.license);
691             var params, action;
692             var dialog_options = {callback:this.update_file, callbackargs:[true], scope:this};
693             if (fileinfo.type == 'folder') {
694                 if (!newfilename) {
695                     this.print_msg(M.util.get_string('entername', 'repository'), 'error');
696                     return;
697                 }
698                 if (filenamechanged || filepathchanged) {
699                     if (!confirmed) {
700                         dialog_options.message = M.util.get_string('confirmrenamefolder', 'repository');
701                         this.show_confirm_dialog(dialog_options);
702                         return;
703                     }
704                     params = {filepath:fileinfo.filepath, newdirname:newfilename, newfilepath:targetpath};
705                     action = 'updatedir';
706                 }
707             } else {
708                 if (!newfilename) {
709                     this.print_msg(M.util.get_string('enternewname', 'repository'), 'error');
710                     return;
711                 }
712                 if ((filenamechanged || filepathchanged) && !confirmed && fileinfo.refcount) {
713                     dialog_options.message = M.util.get_string('confirmrenamefile', 'repository', fileinfo.refcount);
714                     this.show_confirm_dialog(dialog_options);
715                     return;
716                 }
717                 if (filenamechanged || filepathchanged || licensechanged || authorchanged) {
718                     params = {filepath:fileinfo.filepath, filename:fileinfo.fullname,
719                         newfilename:newfilename, newfilepath:targetpath,
720                         newlicense:newlicense, newauthor:newauthor};
721                     action = 'updatefile';
722                 }
723             }
724             if (!action) {
725                 // no changes
726                 this.selectui.hide();
727                 return;
728             }
729             selectnode.addClass('loading');
730             this.request({
731                 action: action,
732                 scope: this,
733                 params: params,
734                 callback: function(id, obj, args) {
735                     if (obj.error) {
736                         selectnode.removeClass('loading');
737                         args.scope.print_msg(obj.error, 'error');
738                     } else {
739                         args.scope.selectui.hide();
740                         args.scope.refresh((obj && obj.filepath) ? obj.filepath : '/');
741                         if (typeof M.core_formchangechecker != 'undefined') {
742                             M.core_formchangechecker.set_form_changed();
743                         }
744                     }
745                 }
746             });
747         },
748         /**
749          * Displays a confirmation dialog
750          * Expected attributes in dialog_options: message, callback, callbackargs(optional), scope(optional)
751          */
752         show_confirm_dialog: function(dialog_options) {
753             // instead of M.util.show_confirm_dialog(e, dialog_options);
754             if (!this.confirm_dlg) {
755                 this.confirm_dlg_node = Y.Node.create(M.form_filemanager.templates.confirmdialog);
756                 var node = this.confirm_dlg_node;
757                 node.generateID();
758                 this.confirm_dlg = new M.core.dialogue({
759                     draggable    : true,
760                     bodyContent  : node,
761                     centered     : true,
762                     modal        : true,
763                     visible      : false,
764                     buttons      : {}
765                 });
766                 var handle_confirm = function(ev) {
767                     var dlgopt = this.confirm_dlg.dlgopt;
768                     ev.preventDefault();
769                     this.confirm_dlg.hide();
770                     if (dlgopt.callback) {
771                         if (dlgopt.callbackargs) {
772                             dlgopt.callback.apply(dlgopt.scope || this, dlgopt.callbackargs);
773                         } else {
774                             dlgopt.callback.apply(dlgopt.scope || this);
775                         }
776                     }
777                 }
778                 var handle_cancel = function(ev) {
779                     ev.preventDefault();
780                     this.confirm_dlg.hide();
781                 }
782                 node.one('.fp-dlg-butconfirm').on('click', handle_confirm, this);
783                 node.one('.fp-dlg-butcancel').on('click', handle_cancel, this);
784             }
785             this.confirm_dlg.dlgopt = dialog_options;
786             this.confirm_dlg_node.one('.fp-dlg-text').setContent(dialog_options.message);
787             this.confirm_dlg.show();
788         },
789         setup_select_file: function() {
790             var selectnode = this.selectnode;
791             var scope = this;
792             // bind labels with corresponding inputs
793             selectnode.all('.fp-saveas,.fp-path,.fp-author,.fp-license').each(function (node) {
794                 node.all('label').set('for', node.one('input,select').generateID());
795             });
796             this.populate_licenses_select(selectnode.one('.fp-license select'));
797             // register event on clicking buttons
798             selectnode.one('.fp-file-update').on('click', function(e) {
799                 e.preventDefault();
800                 this.update_file();
801             }, this);
802             selectnode.all('form input').on('key', function(e) {
803                 e.preventDefault();
804                 scope.update_file();
805             }, 'enter');
806             selectnode.one('.fp-file-download').on('click', function(e) {
807                 e.preventDefault();
808                 if (this.selectui.fileinfo.type != 'folder') {
809                     node = Y.Node.create('<iframe></iframe>').setStyles({
810                         visibility : 'hidden',
811                         width : '1px',
812                         height : '1px'
813                     });
814                     node.set('src', this.selectui.fileinfo.url);
815                     Y.one('body').appendChild(node);
816                 }
817             }, this);
818             selectnode.one('.fp-file-delete').on('click', function(e) {
819                 e.preventDefault();
820                 var dialog_options = {};
821                 var params = {};
822                 var fileinfo = this.selectui.fileinfo;
823                 dialog_options.scope = this;
824                 params.filepath = fileinfo.filepath;
825                 if (fileinfo.type == 'folder') {
826                     params.filename = '.';
827                     dialog_options.message = M.util.get_string('confirmdeletefolder', 'repository');
828                 } else {
829                     params.filename = fileinfo.fullname;
830                     if (fileinfo.refcount) {
831                         dialog_options.message = M.util.get_string('confirmdeletefilewithhref', 'repository', fileinfo.refcount);
832                     } else {
833                         dialog_options.message = M.util.get_string('confirmdeletefile', 'repository');
834                     }
835                 }
836                 dialog_options.callbackargs = [params];
837                 dialog_options.callback = function(params) {
838                     //selectnode.addClass('loading');
839                     this.request({
840                         action: 'delete',
841                         scope: this,
842                         params: params,
843                         callback: function(id, obj, args) {
844                             //args.scope.selectui.hide();
845                             args.scope.filecount--;
846                             args.scope.refresh(obj.filepath);
847                             if (typeof M.core_formchangechecker != 'undefined') {
848                                 M.core_formchangechecker.set_form_changed();
849                             }
850                         }
851                     });
852                 };
853                 this.selectui.hide(); // TODO remove this after confirm dialog is replaced with YUI3
854                 this.show_confirm_dialog(dialog_options);
855             }, this);
856             selectnode.one('.fp-file-zip').on('click', function(e) {
857                 e.preventDefault();
858                 var params = {};
859                 var fileinfo = this.selectui.fileinfo;
860                 if (fileinfo.type != 'folder') {
861                     // this button should not even be shown
862                     return;
863                 }
864                 params['filepath']   = fileinfo.filepath;
865                 params['filename']   = '.';
866                 selectnode.addClass('loading');
867                 this.request({
868                     action: 'zip',
869                     scope: this,
870                     params: params,
871                     callback: function(id, obj, args) {
872                         args.scope.selectui.hide();
873                         args.scope.refresh(obj.filepath);
874                     }
875                 });
876             }, this);
877             selectnode.one('.fp-file-unzip').on('click', function(e) {
878                 e.preventDefault();
879                 var params = {};
880                 var fileinfo = this.selectui.fileinfo;
881                 if (fileinfo.type != 'zip') {
882                     // this button should not even be shown
883                     return;
884                 }
885                 params['filepath'] = fileinfo.filepath;
886                 params['filename'] = fileinfo.fullname;
887                 selectnode.addClass('loading');
888                 this.request({
889                     action: 'unzip',
890                     scope: this,
891                     params: params,
892                     callback: function(id, obj, args) {
893                         args.scope.selectui.hide();
894                         args.scope.refresh(obj.filepath);
895                     }
896                 });
897             }, this);
898             selectnode.one('.fp-file-setmain').on('click', function(e) {
899                 e.preventDefault();
900                 var params = {};
901                 var fileinfo = this.selectui.fileinfo;
902                 if (!this.enablemainfile || fileinfo.type == 'folder') {
903                     // this button should not even be shown for folders or when mainfile is disabled
904                     return;
905                 }
906                 params['filepath'] = fileinfo.filepath;
907                 params['filename'] = fileinfo.fullname;
908                 selectnode.addClass('loading');
909                 this.request({
910                     action: 'setmainfile',
911                     scope: this,
912                     params: params,
913                     callback: function(id, obj, args) {
914                         args.scope.selectui.hide();
915                         args.scope.refresh(fileinfo.filepath);
916                     }
917                 });
918             }, this);
919             selectnode.all('.fp-file-cancel').on('click', function(e) {
920                 e.preventDefault();
921                 // TODO if changed asked to confirm, the same with close button
922                 this.selectui.hide();
923             }, this);
924             selectnode.all('.fp-file-update, .fp-file-download, .fp-file-delete, .fp-file-zip, .fp-file-unzip, ' +
925                 '.fp-file-setmain, .fp-file-cancel').on('key', function(e) {
926                     e.preventDefault();
927                     this.simulate('click');
928             }, 'enter');
929         },
930         get_parent_folder_name: function(node) {
931             if (node.type != 'folder' || node.filepath.length < node.fullname.length+1) {
932                 return node.filepath;
933             }
934             var basedir = node.filepath.substr(0, node.filepath.length - node.fullname.length - 1);
935             var lastdir = node.filepath.substr(node.filepath.length - node.fullname.length - 2);
936             if (lastdir == '/' + node.fullname + '/') {
937                 return basedir;
938             }
939             return node.filepath;
940         },
941         select_file: function(node) {
942             if (this.is_disabled()) {
943                 return;
944             }
945             var selectnode = this.selectnode;
946             selectnode.removeClass('loading').removeClass('fp-folder').
947                 removeClass('fp-file').removeClass('fp-zip').removeClass('fp-cansetmain');
948             if (node.type == 'folder' || node.type == 'zip') {
949                 selectnode.addClass('fp-'+node.type);
950             } else {
951                 selectnode.addClass('fp-file');
952             }
953             if (this.enablemainfile && (node.sortorder != 1) && node.type == 'file') {
954                 selectnode.addClass('fp-cansetmain');
955             }
956             this.selectui.fileinfo = node;
957             selectnode.one('.fp-saveas input').set('value', node.fullname);
958             var foldername = this.get_parent_folder_name(node);
959             selectnode.all('.fp-author input').set('value', node.author ? node.author : '');
960             selectnode.all('.fp-license select option[selected]').set('selected', false);
961             selectnode.all('.fp-license select option[value='+node.license+']').set('selected', true);
962             selectnode.all('.fp-path select option[selected]').set('selected', false);
963             selectnode.all('.fp-path select option').each(function(el){
964                 if (el.get('value') == foldername) {
965                     el.set('selected', true);
966                 }
967             });
968             selectnode.all('.fp-author input, .fp-license select').set('disabled',(node.type == 'folder')?'disabled':'');
969             // display static information about a file (when known)
970             var attrs = ['datemodified','datecreated','size','dimensions','original','reflist'];
971             for (var i in attrs) {
972                 if (selectnode.one('.fp-'+attrs[i])) {
973                     var value = (node[attrs[i]+'_f']) ? node[attrs[i]+'_f'] : (node[attrs[i]] ? node[attrs[i]] : '');
974                     // Escape if the attribute being evaluated is not for the list of reference files.
975                     if (attrs[i] !== 'reflist') {
976                         value = Y.Escape.html(value);
977                     }
978                     selectnode.one('.fp-'+attrs[i]).addClassIf('fp-unknown', ''+value == '')
979                         .one('.fp-value').setContent(value);
980                 }
981             }
982             // display thumbnail
983             var imgnode = Y.Node.create('<img/>').
984                 set('src', node.realthumbnail ? node.realthumbnail : node.thumbnail).
985                 setStyle('maxHeight', ''+(node.thumbnail_height ? node.thumbnail_height : 90)+'px').
986                 setStyle('maxWidth', ''+(node.thumbnail_width ? node.thumbnail_width : 90)+'px');
987             selectnode.one('.fp-thumbnail').setContent('').appendChild(imgnode);
988             // load original location if applicable
989             if (node.isref && !node.original) {
990                 selectnode.one('.fp-original').removeClass('fp-unknown').addClass('fp-loading');
991                 this.request({
992                     action: 'getoriginal',
993                     scope: this,
994                     params: {'filepath':node.filepath,'filename':node.fullname},
995                     callback: function(id, obj, args) {
996                         // check if we did not select another file meanwhile
997                         var scope = args.scope;
998                         if (scope.selectui.fileinfo && node &&
999                                 scope.selectui.fileinfo.filepath == node.filepath &&
1000                                 scope.selectui.fileinfo.fullname == node.fullname) {
1001                             selectnode.one('.fp-original').removeClass('fp-loading');
1002                             if (obj.original) {
1003                                 node.original = obj.original;
1004                                 selectnode.one('.fp-original .fp-value').setContent(Y.Escape.html(node.original));
1005                             } else {
1006                                 selectnode.one('.fp-original .fp-value').setContent(M.util.get_string('unknownsource', 'repository'));
1007                             }
1008                         }
1009                     }
1010                 }, false);
1011             }
1012             // load references list if applicable
1013             selectnode.one('.fp-refcount').setContent(node.refcount ? M.util.get_string('referencesexist', 'repository', node.refcount) : '');
1014             if (node.refcount && !node.reflist) {
1015                 selectnode.one('.fp-reflist').removeClass('fp-unknown').addClass('fp-loading');
1016                 this.request({
1017                     action: 'getreferences',
1018                     scope: this,
1019                     params: {'filepath':node.filepath,'filename':node.fullname},
1020                     callback: function(id, obj, args) {
1021                         // check if we did not select another file meanwhile
1022                         var scope = args.scope;
1023                         if (scope.selectui.fileinfo && node &&
1024                                 scope.selectui.fileinfo.filepath == node.filepath &&
1025                                 scope.selectui.fileinfo.fullname == node.fullname) {
1026                             selectnode.one('.fp-reflist').removeClass('fp-loading');
1027                             if (obj.references) {
1028                                 node.reflist = '';
1029                                 for (var i in obj.references) {
1030                                     node.reflist += '<li>'+Y.Escape.html(obj.references[i])+'</li>';
1031                                 }
1032                                 selectnode.one('.fp-reflist .fp-value').setContent(node.reflist);
1033                             } else {
1034                                 selectnode.one('.fp-reflist .fp-value').setContent('');
1035                             }
1036                         }
1037                     }
1038                 }, false);
1039             }
1040             // update dialog header
1041             var nodename = node.fullname;
1042             // Limit the string length so it fits nicely on mobile devices
1043             var namelength = 50;
1044             if (nodename.length > namelength) {
1045                 nodename = nodename.substring(0, namelength) + '...';
1046             }
1047             Y.one('#fm-dialog-label_'+selectnode.get('id')).setContent(Y.Escape.html(M.util.get_string('edit', 'moodle')+' '+nodename));
1048             // show panel
1049             this.selectui.show();
1050             Y.one('#'+selectnode.get('id')).focus();
1051         },
1052         render: function() {
1053             this.print_path();
1054             this.view_files();
1055         },
1056         has_folder: function(foldername) {
1057             var element;
1058             for (var i in this.options.list) {
1059                 element = this.options.list[i];
1060                 if (element.type == 'folder' && element.fullname == foldername) {
1061                     return true;
1062                 }
1063             }
1064             return false;
1065         },
1066         get_preference: function(name) {
1067             if (this.userprefs[name]) {
1068                 return this.userprefs[name];
1069             } else {
1070                 return false;
1071             }
1072         },
1073         set_preference: function(name, value) {
1074             if (this.userprefs[name] != value) {
1075                 M.util.set_user_preference('filemanager_' + name, value);
1076                 this.userprefs[name] = value;
1077             }
1078         },
1079     });
1081     // finally init everything needed
1082     // hide loading picture, display filemanager interface
1083     var filemanager = Y.one('#filemanager-'+options.client_id);
1084     filemanager.removeClass('fm-loading').addClass('fm-loaded');
1086     var manager = new FileManagerHelper(options);
1087     var dndoptions = {
1088         filemanager: manager,
1089         acceptedtypes: options.filepicker.accepted_types,
1090         clientid: options.client_id,
1091         author: options.author,
1092         maxfiles: options.maxfiles,
1093         maxbytes: options.maxbytes,
1094         areamaxbytes: options.areamaxbytes,
1095         itemid: options.itemid,
1096         repositories: manager.filepicker_options.repositories,
1097         containerid: manager.dndcontainer.get('id'),
1098         contextid: options.context.id
1099     };
1100     M.form_dndupload.init(Y, dndoptions);