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