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