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