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