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