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