1 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
19 * this.api, stores the URL to make ajax request
21 * this.filepicker_options
22 * this.movefile_dialog
26 * this.filecount, how many files in this filemanager
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 * this.viewmode, contains current view mode - icons, tree or details
35 * FileManager options:
37 * this.options.currentpath
42 M.form_filemanager = {templates:{}};
44 M.form_filemanager.set_templates = function(Y, templates) {
45 M.form_filemanager.templates = templates;
49 * This fucntion is called for each file picker on page.
51 M.form_filemanager.init = function(Y, options) {
52 var FileManagerHelper = function(options) {
53 FileManagerHelper.superclass.constructor.apply(this, arguments);
55 FileManagerHelper.NAME = "FileManager";
56 FileManagerHelper.ATTRS = {
61 Y.extend(FileManagerHelper, Y.Base, {
62 api: M.cfg.wwwroot+'/repository/draftfiles_ajax.php',
64 initializer: function(options) {
65 this.options = options;
66 if (options.mainfile) {
67 this.enablemainfile = options.mainfile;
69 this.client_id = options.client_id;
70 this.currentpath = '/';
71 this.maxfiles = options.maxfiles;
72 this.maxbytes = options.maxbytes;
73 this.areamaxbytes = options.areamaxbytes;
74 this.userprefs = options.userprefs;
75 this.emptycallback = null; // Used by drag and drop upload
77 this.filepicker_options = options.filepicker?options.filepicker:{};
78 this.filepicker_options.client_id = this.client_id;
79 this.filepicker_options.context = options.context;
80 this.filepicker_options.maxfiles = this.maxfiles;
81 this.filepicker_options.maxbytes = this.maxbytes;
82 this.filepicker_options.areamaxbytes = this.areamaxbytes;
83 this.filepicker_options.env = 'filemanager';
84 this.filepicker_options.itemid = options.itemid;
86 if (options.filecount) {
87 this.filecount = options.filecount;
91 // prepare filemanager for drag-and-drop upload
92 this.filemanager = Y.one('#filemanager-'+options.client_id);
93 if (this.filemanager.hasClass('filemanager-container') || !this.filemanager.one('.filemanager-container')) {
94 this.dndcontainer = this.filemanager;
96 this.dndcontainer = this.filemanager.one('.filemanager-container');
97 if (!this.dndcontainer.get('id')) {
98 this.dndcontainer.generateID();
101 // save template for one path element and location of path bar
102 if (this.filemanager.one('.fp-path-folder')) {
103 this.pathnode = this.filemanager.one('.fp-path-folder');
104 this.pathbar = this.pathnode.get('parentNode');
105 this.pathbar.removeChild(this.pathnode);
107 // initialize 'select file' panel
108 this.selectnode = Y.Node.create(M.form_filemanager.templates.fileselectlayout);
109 this.selectnode.setAttribute('aria-live', 'assertive');
110 this.selectnode.setAttribute('role', 'dialog');
111 this.selectnode.generateID();
113 var labelid = 'fm-dialog-label_'+ this.selectnode.get('id');
114 this.selectui = new M.core.dialogue({
116 headerContent: '<h3 id="' + labelid +'">' + M.util.get_string('edit', 'moodle') + '</h3>',
117 bodyContent : this.selectnode,
123 Y.one('#'+this.selectnode.get('id')).setAttribute('aria-labelledby', labelid);
124 this.selectui.hide();
125 this.setup_select_file();
126 // setup buttons onclick events
127 this.setup_buttons();
128 // set event handler for lazy loading of thumbnails
129 this.filemanager.one('.fp-content').on(['scroll','resize'], this.content_scrolled, this);
131 this.viewmode = this.get_preference("recentviewmode");
132 if (this.viewmode != 2 && this.viewmode != 3) {
135 var viewmodeselectors = {'1': '.fp-vb-icons', '2': '.fp-vb-tree', '3': '.fp-vb-details'};
136 this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').removeClass('checked');
137 this.filemanager.all(viewmodeselectors[this.viewmode]).addClass('checked');
138 this.refresh(this.currentpath); // MDL-31113 get latest list from server
142 this.filemanager.addClass('fm-updating');
144 request: function(args, redraw) {
145 var api = this.api + '?action='+args.action;
149 scope = args['scope'];
151 params['sesskey'] = M.cfg.sesskey;
152 params['client_id'] = this.client_id;
153 params['filepath'] = this.currentpath;
154 params['itemid'] = this.options.itemid?this.options.itemid:0;
155 if (args['params']) {
156 for (i in args['params']) {
157 params[i] = args['params'][i];
163 complete: function(id,o,p) {
170 data = Y.JSON.parse(o.responseText);
172 scope.print_msg(M.util.get_string('invalidjson', 'repository'), 'error');
173 Y.error(M.util.get_string('invalidjson', 'repository')+":\n"+o.responseText);
176 if (data && data.tree && scope.set_current_tree) {
177 scope.set_current_tree(data.tree);
179 args.callback(id,data,p);
186 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
188 data: build_querystring(params)
191 cfg.form = args.form;
198 filepicker_callback: function(obj) {
200 this.check_buttons();
201 this.refresh(this.currentpath);
202 if (typeof M.core_formchangechecker != 'undefined') {
203 M.core_formchangechecker.set_form_changed();
206 check_buttons: function() {
207 if (this.filecount>0) {
208 this.filemanager.removeClass('fm-nofiles');
210 this.filemanager.addClass('fm-nofiles');
212 if (this.filecount >= this.maxfiles && this.maxfiles!=-1) {
213 this.filemanager.addClass('fm-maxfiles');
216 this.filemanager.removeClass('fm-maxfiles');
219 refresh: function(filepath) {
221 this.currentpath = filepath;
223 filepath = this.currentpath;
225 this.currentpath = filepath;
230 params: {'filepath':filepath},
231 callback: function(id, obj, args) {
232 scope.filecount = obj.filecount;
234 scope.lazyloading = {};
235 scope.check_buttons();
240 /** displays message in a popup */
241 print_msg: function(msg, type) {
242 var header = M.util.get_string('error', 'moodle');
243 if (type != 'error') {
244 type = 'info'; // one of only two types excepted
245 header = M.util.get_string('info', 'moodle');
248 this.msg_dlg_node = Y.Node.create(M.form_filemanager.templates.message);
249 var nodeid = this.msg_dlg_node.generateID();
251 this.msg_dlg = new M.core.dialogue({
253 bodyContent : this.msg_dlg_node,
258 this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) {
264 this.msg_dlg.set('headerContent', header);
265 this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type)
266 this.msg_dlg_node.one('.fp-msg-text').setContent(Y.Escape.html(msg));
269 is_disabled: function() {
270 return this.filemanager.ancestor('.fitem.disabled') != null;
272 setup_buttons: function() {
273 var button_download = this.filemanager.one('.fp-btn-download');
274 var button_create = this.filemanager.one('.fp-btn-mkdir');
275 var button_addfile = this.filemanager.one('.fp-btn-add');
277 // setup 'add file' button
278 button_addfile.on('click', this.show_filepicker, this);
280 var dndarrow = this.filemanager.one('.dndupload-arrow');
282 dndarrow.on('click', this.show_filepicker, this);
285 // setup 'make a folder' button
286 if (this.options.subdirs) {
287 button_create.on('click',function(e) {
289 if (this.is_disabled()) {
293 // a function used to perform an ajax request
294 var perform_action = function(e) {
296 var foldername = Y.one('#fm-newname-'+scope.client_id).get('value');
298 scope.mkdir_dialog.hide();
303 params: {filepath:scope.currentpath, newdirname:foldername},
304 callback: function(id, obj, args) {
305 var filepath = obj.filepath;
306 scope.mkdir_dialog.hide();
307 scope.refresh(filepath);
308 Y.one('#fm-newname-'+scope.client_id).set('value', '');
309 if (typeof M.core_formchangechecker != 'undefined') {
310 M.core_formchangechecker.set_form_changed();
315 var validate_folder_name = function() {
317 var foldername = Y.one('#fm-newname-'+scope.client_id).get('value');
318 if (foldername.length > 0) {
321 var btn = Y.one('#fm-mkdir-butcreate-'+scope.client_id);
323 btn.set('disabled', !valid);
327 if (!this.mkdir_dialog) {
328 var node = Y.Node.create(M.form_filemanager.templates.mkdir);
329 this.mkdir_dialog = new M.core.dialogue({
336 node.one('.fp-dlg-butcreate').set('id', 'fm-mkdir-butcreate-'+this.client_id).on('click',
337 perform_action, this);
338 node.one('input').set('id', 'fm-newname-'+this.client_id).on('keydown', function(e) {
339 var valid = Y.bind(validate_folder_name, this)();
340 if (valid && e.keyCode === 13) {
341 Y.bind(perform_action, this)(e);
344 node.one('#fm-newname-'+this.client_id).on(['keyup', 'change'], function(e) {
345 Y.bind(validate_folder_name, this)();
348 node.one('label').set('for', 'fm-newname-' + this.client_id);
349 node.all('.fp-dlg-butcancel').on('click', function(e){e.preventDefault();this.mkdir_dialog.hide();}, this);
350 node.all('.fp-dlg-curpath').set('id', 'fm-curpath-'+this.client_id);
352 this.mkdir_dialog.show();
354 // Default folder name:
355 var foldername = M.util.get_string('newfolder', 'repository');
356 while (this.has_folder(foldername)) {
357 foldername = increment_filename(foldername, true);
359 Y.one('#fm-newname-'+scope.client_id).set('value', foldername);
360 Y.bind(validate_folder_name, this)();
361 Y.one('#fm-newname-'+scope.client_id).focus().select();
362 Y.all('#fm-curpath-'+scope.client_id).setContent(this.currentpath);
365 this.filemanager.addClass('fm-nomkdir');
368 // setup 'download this folder' button
369 button_download.on('click',function(e) {
371 if (this.is_disabled()) {
376 var image_downloading = this.filemanager.one('.fp-img-downloading');
377 if (image_downloading.getStyle('display') == 'inline') {
380 image_downloading.setStyle('display', 'inline');
382 // perform downloaddir ajax request
384 action: 'downloaddir',
386 callback: function(id, obj, args) {
387 var image_downloading = scope.filemanager.one('.fp-img-downloading');
388 image_downloading.setStyle('display', 'none');
391 scope.refresh(obj.filepath);
392 node = Y.Node.create('<iframe></iframe>').setStyles({
393 visibility : 'hidden',
397 node.set('src', obj.fileurl);
398 Y.one('body').appendChild(node);
400 scope.print_msg(M.util.get_string('draftareanofiles', 'repository'), 'error');
406 this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').
407 on('click', function(e) {
409 var viewbar = this.filemanager.one('.fp-viewbar')
410 if (!this.is_disabled() && (!viewbar || !viewbar.hasClass('disabled'))) {
411 this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').removeClass('checked')
412 if (e.currentTarget.hasClass('fp-vb-tree')) {
414 } else if (e.currentTarget.hasClass('fp-vb-details')) {
419 e.currentTarget.addClass('checked')
421 this.filemanager.one('.fp-content').setAttribute('tabIndex', '0');
422 this.filemanager.one('.fp-content').focus();
423 this.set_preference('recentviewmode', this.viewmode);
428 show_filepicker: function (e) {
429 // if maxfiles == -1, the no limit
431 if (this.is_disabled()) {
434 var options = this.filepicker_options;
435 options.formcallback = this.filepicker_callback;
436 // XXX: magic here, to let filepicker use filemanager scope
437 options.magicscope = this;
438 options.savepath = this.currentpath;
439 M.core_filepicker.show(Y, options);
442 print_path: function() {
443 var p = this.options.path;
444 this.pathbar.setContent('').addClass('empty');
445 if (p && p.length!=0 && this.viewmode != 2) {
446 for(var i = 0; i < p.length; i++) {
447 var el = this.pathnode.cloneNode(true);
448 this.pathbar.appendChild(el);
451 el.addClass('first');
453 if (i == p.length-1) {
462 el.one('.fp-path-folder-name').setContent(Y.Escape.html(p[i].name)).
463 on('click', function(e, path) {
465 if (!this.is_disabled()) {
470 this.pathbar.removeClass('empty');
473 get_filepath: function(obj) {
474 if (obj.path && obj.path.length) {
475 return obj.path[obj.path.length-1].path;
479 treeview_dynload: function(node, cb) {
480 var retrieved_children = {};
482 for (var i in node.children) {
483 retrieved_children[node.children[i].path] = node.children[i];
486 if (!node.path || node.path == '/') {
487 // this is a root pseudo folder
488 node.fileinfo.filepath = '/';
489 node.fileinfo.type = 'folder';
490 node.fileinfo.fullname = node.fileinfo.title;
491 node.fileinfo.filename = '.';
495 params: {filepath:node.path?node.path:''},
497 callback: function(id, obj, args) {
499 var scope = args.scope;
500 // check that user did not leave the view mode before recieving this response
501 if (!(scope.viewmode == 2 && node && node.getChildrenEl())) {
504 if (cb != null) { // (in manual mode do not update current path)
506 scope.currentpath = node.path?node.path:'/';
508 node.highlight(false);
509 node.origlist = obj.list ? obj.list : null;
510 node.origpath = obj.path ? obj.path : null;
513 if (list[k].type == 'folder' && retrieved_children[list[k].filepath]) {
514 // if this child is a folder and has already been retrieved
515 retrieved_children[list[k].filepath].fileinfo = list[k];
516 node.children[node.children.length] = retrieved_children[list[k].filepath];
518 // append new file to the list
519 scope.view_files([list[k]]);
525 // invoke callback requested by TreeView component
528 scope.content_scrolled();
532 content_scrolled: function(e) {
533 setTimeout(Y.bind(function() {
534 if (this.processingimages) {return;}
535 this.processingimages = true;
537 fpcontent = this.filemanager.one('.fp-content'),
538 fpcontenty = fpcontent.getY(),
539 fpcontentheight = fpcontent.getStylePx('height'),
540 is_node_visible = function(node) {
541 var offset = node.getY()-fpcontenty;
542 if (offset <= fpcontentheight && (offset >=0 || offset+node.getStylePx('height')>=0)) {
547 // replace src for visible images that need to be lazy-loaded
548 if (scope.lazyloading) {
549 fpcontent.all('img').each( function(node) {
550 if (node.get('id') && scope.lazyloading[node.get('id')] && is_node_visible(node)) {
551 node.setImgRealSrc(scope.lazyloading);
555 this.processingimages = false;
558 view_files: function(appendfiles) {
559 this.filemanager.removeClass('fm-updating').removeClass('fm-noitems');
560 if ((appendfiles == null) && (!this.options.list || this.options.list.length == 0) && this.viewmode != 2) {
561 this.filemanager.addClass('fm-noitems');
564 var list = (appendfiles != null) ? appendfiles : this.options.list;
565 var element_template;
566 if (this.viewmode == 2 || this.viewmode == 3) {
567 element_template = Y.Node.create(M.form_filemanager.templates.listfilename);
570 element_template = Y.Node.create(M.form_filemanager.templates.iconfilename);
573 viewmode : this.viewmode,
574 appendonly : appendfiles != null,
575 filenode : element_template,
576 callbackcontext : this,
577 callback : function(e, node) {
578 if (e.preventDefault) { e.preventDefault(); }
579 if (node.type == 'folder') {
580 this.refresh(node.filepath);
582 this.select_file(node);
585 rightclickcallback : function(e, node) {
586 if (e.preventDefault) { e.preventDefault(); }
587 this.select_file(node);
589 classnamecallback : function(node) {
591 if (node.type == 'folder' || (!node.type && !node.filename)) {
592 classname = classname + ' fp-folder';
594 if (node.filename || node.filepath || (node.path && node.path != '/')) {
595 classname = classname + ' fp-hascontextmenu';
598 classname = classname + ' fp-isreference';
601 classname = classname + ' fp-hasreferences';
603 if (node.originalmissing) {
604 classname = classname + ' fp-originalmissing';
606 if (node.sortorder == 1) { classname = classname + ' fp-mainfile';}
607 return Y.Lang.trim(classname);
610 if (this.viewmode == 2) {
611 options.dynload = true;
612 options.filepath = this.options.path;
613 options.treeview_dynload = this.treeview_dynload;
614 options.norootrightclick = true;
615 options.callback = function(e, node) {
616 // TODO MDL-32736 e is not an event here but an object with properties 'event' and 'node'
617 if (!node.fullname) {return;}
618 if (node.type != 'folder') {
619 if (e.node.parent && e.node.parent.origpath) {
620 // set the current path
621 this.options.path = e.node.parent.origpath;
622 this.options.list = e.node.parent.origlist;
625 this.currentpath = node.filepath;
626 this.select_file(node);
628 // save current path and filelist (in case we want to jump to other viewmode)
629 this.options.path = e.node.origpath;
630 this.options.list = e.node.origlist;
631 this.currentpath = node.filepath;
633 //this.content_scrolled();
637 if (!this.lazyloading) {
640 this.filemanager.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);
641 this.content_scrolled();
643 populate_licenses_select: function(node) {
648 var licenses = this.options.licenses;
649 for (var i in licenses) {
650 var option = Y.Node.create('<option/>').
651 set('value', licenses[i].shortname).
652 setContent(Y.Escape.html(licenses[i].fullname));
653 node.appendChild(option)
656 set_current_tree: function(tree) {
657 var appendfilepaths = function(list, node) {
658 if (!node || !node.children || !node.children.length) {return;}
659 for (var i in node.children) {
660 list[list.length] = node.children[i].filepath;
661 appendfilepaths(list, node.children[i]);
665 appendfilepaths(list, tree);
666 var selectnode = this.selectnode;
667 node = selectnode.one('.fp-path select');
669 for (var i in list) {
670 node.appendChild(Y.Node.create('<option/>').
671 set('value', list[i]).setContent(Y.Escape.html(list[i])));
674 update_file: function(confirmed) {
675 var selectnode = this.selectnode;
676 var fileinfo = this.selectui.fileinfo;
678 var newfilename = Y.Lang.trim(selectnode.one('.fp-saveas input').get('value'));
679 var filenamechanged = (newfilename && newfilename != fileinfo.fullname);
680 var pathselect = selectnode.one('.fp-path select'),
681 pathindex = pathselect.get('selectedIndex'),
682 targetpath = pathselect.get("options").item(pathindex).get('value');
683 var filepathchanged = (targetpath != this.get_parent_folder_name(fileinfo));
684 var newauthor = Y.Lang.trim(selectnode.one('.fp-author input').get('value'));
685 var authorchanged = (newauthor != Y.Lang.trim(fileinfo.author));
686 var licenseselect = selectnode.one('.fp-license select'),
687 licenseindex = licenseselect.get('selectedIndex'),
688 newlicense = licenseselect.get("options").item(licenseindex).get('value');
689 var licensechanged = (newlicense != fileinfo.license);
692 var dialog_options = {callback:this.update_file, callbackargs:[true], scope:this};
693 if (fileinfo.type == 'folder') {
695 this.print_msg(M.util.get_string('entername', 'repository'), 'error');
698 if (filenamechanged || filepathchanged) {
700 dialog_options.message = M.util.get_string('confirmrenamefolder', 'repository');
701 this.show_confirm_dialog(dialog_options);
704 params = {filepath:fileinfo.filepath, newdirname:newfilename, newfilepath:targetpath};
705 action = 'updatedir';
709 this.print_msg(M.util.get_string('enternewname', 'repository'), 'error');
712 if ((filenamechanged || filepathchanged) && !confirmed && fileinfo.refcount) {
713 dialog_options.message = M.util.get_string('confirmrenamefile', 'repository', fileinfo.refcount);
714 this.show_confirm_dialog(dialog_options);
717 if (filenamechanged || filepathchanged || licensechanged || authorchanged) {
718 params = {filepath:fileinfo.filepath, filename:fileinfo.fullname,
719 newfilename:newfilename, newfilepath:targetpath,
720 newlicense:newlicense, newauthor:newauthor};
721 action = 'updatefile';
726 this.selectui.hide();
729 selectnode.addClass('loading');
734 callback: function(id, obj, args) {
736 selectnode.removeClass('loading');
737 args.scope.print_msg(obj.error, 'error');
739 args.scope.selectui.hide();
740 args.scope.refresh((obj && obj.filepath) ? obj.filepath : '/');
741 if (typeof M.core_formchangechecker != 'undefined') {
742 M.core_formchangechecker.set_form_changed();
749 * Displays a confirmation dialog
750 * Expected attributes in dialog_options: message, callback, callbackargs(optional), scope(optional)
752 show_confirm_dialog: function(dialog_options) {
753 // instead of M.util.show_confirm_dialog(e, dialog_options);
754 if (!this.confirm_dlg) {
755 this.confirm_dlg_node = Y.Node.create(M.form_filemanager.templates.confirmdialog);
756 var node = this.confirm_dlg_node;
758 this.confirm_dlg = new M.core.dialogue({
766 var handle_confirm = function(ev) {
767 var dlgopt = this.confirm_dlg.dlgopt;
769 this.confirm_dlg.hide();
770 if (dlgopt.callback) {
771 if (dlgopt.callbackargs) {
772 dlgopt.callback.apply(dlgopt.scope || this, dlgopt.callbackargs);
774 dlgopt.callback.apply(dlgopt.scope || this);
778 var handle_cancel = function(ev) {
780 this.confirm_dlg.hide();
782 node.one('.fp-dlg-butconfirm').on('click', handle_confirm, this);
783 node.one('.fp-dlg-butcancel').on('click', handle_cancel, this);
785 this.confirm_dlg.dlgopt = dialog_options;
786 this.confirm_dlg_node.one('.fp-dlg-text').setContent(dialog_options.message);
787 this.confirm_dlg.show();
789 setup_select_file: function() {
790 var selectnode = this.selectnode;
792 // bind labels with corresponding inputs
793 selectnode.all('.fp-saveas,.fp-path,.fp-author,.fp-license').each(function (node) {
794 node.all('label').set('for', node.one('input,select').generateID());
796 this.populate_licenses_select(selectnode.one('.fp-license select'));
797 // register event on clicking buttons
798 selectnode.one('.fp-file-update').on('click', function(e) {
802 selectnode.all('form input').on('key', function(e) {
806 selectnode.one('.fp-file-download').on('click', function(e) {
808 if (this.selectui.fileinfo.type != 'folder') {
809 node = Y.Node.create('<iframe></iframe>').setStyles({
810 visibility : 'hidden',
814 node.set('src', this.selectui.fileinfo.url);
815 Y.one('body').appendChild(node);
818 selectnode.one('.fp-file-delete').on('click', function(e) {
820 var dialog_options = {};
822 var fileinfo = this.selectui.fileinfo;
823 dialog_options.scope = this;
824 params.filepath = fileinfo.filepath;
825 if (fileinfo.type == 'folder') {
826 params.filename = '.';
827 dialog_options.message = M.util.get_string('confirmdeletefolder', 'repository');
829 params.filename = fileinfo.fullname;
830 if (fileinfo.refcount) {
831 dialog_options.message = M.util.get_string('confirmdeletefilewithhref', 'repository', fileinfo.refcount);
833 dialog_options.message = M.util.get_string('confirmdeletefile', 'repository');
836 dialog_options.callbackargs = [params];
837 dialog_options.callback = function(params) {
838 //selectnode.addClass('loading');
843 callback: function(id, obj, args) {
844 //args.scope.selectui.hide();
845 args.scope.filecount--;
846 args.scope.refresh(obj.filepath);
847 if (typeof M.core_formchangechecker != 'undefined') {
848 M.core_formchangechecker.set_form_changed();
853 this.selectui.hide(); // TODO remove this after confirm dialog is replaced with YUI3
854 this.show_confirm_dialog(dialog_options);
856 selectnode.one('.fp-file-zip').on('click', function(e) {
859 var fileinfo = this.selectui.fileinfo;
860 if (fileinfo.type != 'folder') {
861 // this button should not even be shown
864 params['filepath'] = fileinfo.filepath;
865 params['filename'] = '.';
866 selectnode.addClass('loading');
871 callback: function(id, obj, args) {
872 args.scope.selectui.hide();
873 args.scope.refresh(obj.filepath);
877 selectnode.one('.fp-file-unzip').on('click', function(e) {
880 var fileinfo = this.selectui.fileinfo;
881 if (fileinfo.type != 'zip') {
882 // this button should not even be shown
885 params['filepath'] = fileinfo.filepath;
886 params['filename'] = fileinfo.fullname;
887 selectnode.addClass('loading');
892 callback: function(id, obj, args) {
893 args.scope.selectui.hide();
894 args.scope.refresh(obj.filepath);
898 selectnode.one('.fp-file-setmain').on('click', function(e) {
901 var fileinfo = this.selectui.fileinfo;
902 if (!this.enablemainfile || fileinfo.type == 'folder') {
903 // this button should not even be shown for folders or when mainfile is disabled
906 params['filepath'] = fileinfo.filepath;
907 params['filename'] = fileinfo.fullname;
908 selectnode.addClass('loading');
910 action: 'setmainfile',
913 callback: function(id, obj, args) {
914 args.scope.selectui.hide();
915 args.scope.refresh(fileinfo.filepath);
919 selectnode.all('.fp-file-cancel').on('click', function(e) {
921 // TODO if changed asked to confirm, the same with close button
922 this.selectui.hide();
924 selectnode.all('.fp-file-update, .fp-file-download, .fp-file-delete, .fp-file-zip, .fp-file-unzip, ' +
925 '.fp-file-setmain, .fp-file-cancel').on('key', function(e) {
927 this.simulate('click');
930 get_parent_folder_name: function(node) {
931 if (node.type != 'folder' || node.filepath.length < node.fullname.length+1) {
932 return node.filepath;
934 var basedir = node.filepath.substr(0, node.filepath.length - node.fullname.length - 1);
935 var lastdir = node.filepath.substr(node.filepath.length - node.fullname.length - 2);
936 if (lastdir == '/' + node.fullname + '/') {
939 return node.filepath;
941 select_file: function(node) {
942 if (this.is_disabled()) {
945 var selectnode = this.selectnode;
946 selectnode.removeClass('loading').removeClass('fp-folder').
947 removeClass('fp-file').removeClass('fp-zip').removeClass('fp-cansetmain');
948 if (node.type == 'folder' || node.type == 'zip') {
949 selectnode.addClass('fp-'+node.type);
951 selectnode.addClass('fp-file');
953 if (this.enablemainfile && (node.sortorder != 1) && node.type == 'file') {
954 selectnode.addClass('fp-cansetmain');
956 this.selectui.fileinfo = node;
957 selectnode.one('.fp-saveas input').set('value', node.fullname);
958 var foldername = this.get_parent_folder_name(node);
959 selectnode.all('.fp-author input').set('value', node.author ? node.author : '');
960 selectnode.all('.fp-license select option[selected]').set('selected', false);
961 selectnode.all('.fp-license select option[value='+node.license+']').set('selected', true);
962 selectnode.all('.fp-path select option[selected]').set('selected', false);
963 selectnode.all('.fp-path select option').each(function(el){
964 if (el.get('value') == foldername) {
965 el.set('selected', true);
968 selectnode.all('.fp-author input, .fp-license select').set('disabled',(node.type == 'folder')?'disabled':'');
969 // display static information about a file (when known)
970 var attrs = ['datemodified','datecreated','size','dimensions','original','reflist'];
971 for (var i in attrs) {
972 if (selectnode.one('.fp-'+attrs[i])) {
973 var value = (node[attrs[i]+'_f']) ? node[attrs[i]+'_f'] : (node[attrs[i]] ? node[attrs[i]] : '');
974 // Escape if the attribute being evaluated is not for the list of reference files.
975 if (attrs[i] !== 'reflist') {
976 value = Y.Escape.html(value);
978 selectnode.one('.fp-'+attrs[i]).addClassIf('fp-unknown', ''+value == '')
979 .one('.fp-value').setContent(value);
983 var imgnode = Y.Node.create('<img/>').
984 set('src', node.realthumbnail ? node.realthumbnail : node.thumbnail).
985 setStyle('maxHeight', ''+(node.thumbnail_height ? node.thumbnail_height : 90)+'px').
986 setStyle('maxWidth', ''+(node.thumbnail_width ? node.thumbnail_width : 90)+'px');
987 selectnode.one('.fp-thumbnail').setContent('').appendChild(imgnode);
988 // load original location if applicable
989 if (node.isref && !node.original) {
990 selectnode.one('.fp-original').removeClass('fp-unknown').addClass('fp-loading');
992 action: 'getoriginal',
994 params: {'filepath':node.filepath,'filename':node.fullname},
995 callback: function(id, obj, args) {
996 // check if we did not select another file meanwhile
997 var scope = args.scope;
998 if (scope.selectui.fileinfo && node &&
999 scope.selectui.fileinfo.filepath == node.filepath &&
1000 scope.selectui.fileinfo.fullname == node.fullname) {
1001 selectnode.one('.fp-original').removeClass('fp-loading');
1003 node.original = obj.original;
1004 selectnode.one('.fp-original .fp-value').setContent(Y.Escape.html(node.original));
1006 selectnode.one('.fp-original .fp-value').setContent(M.util.get_string('unknownsource', 'repository'));
1012 // load references list if applicable
1013 selectnode.one('.fp-refcount').setContent(node.refcount ? M.util.get_string('referencesexist', 'repository', node.refcount) : '');
1014 if (node.refcount && !node.reflist) {
1015 selectnode.one('.fp-reflist').removeClass('fp-unknown').addClass('fp-loading');
1017 action: 'getreferences',
1019 params: {'filepath':node.filepath,'filename':node.fullname},
1020 callback: function(id, obj, args) {
1021 // check if we did not select another file meanwhile
1022 var scope = args.scope;
1023 if (scope.selectui.fileinfo && node &&
1024 scope.selectui.fileinfo.filepath == node.filepath &&
1025 scope.selectui.fileinfo.fullname == node.fullname) {
1026 selectnode.one('.fp-reflist').removeClass('fp-loading');
1027 if (obj.references) {
1029 for (var i in obj.references) {
1030 node.reflist += '<li>'+Y.Escape.html(obj.references[i])+'</li>';
1032 selectnode.one('.fp-reflist .fp-value').setContent(node.reflist);
1034 selectnode.one('.fp-reflist .fp-value').setContent('');
1040 // update dialog header
1041 var nodename = node.fullname;
1042 // Limit the string length so it fits nicely on mobile devices
1043 var namelength = 50;
1044 if (nodename.length > namelength) {
1045 nodename = nodename.substring(0, namelength) + '...';
1047 Y.one('#fm-dialog-label_'+selectnode.get('id')).setContent(Y.Escape.html(M.util.get_string('edit', 'moodle')+' '+nodename));
1049 this.selectui.show();
1050 Y.one('#'+selectnode.get('id')).focus();
1052 render: function() {
1056 has_folder: function(foldername) {
1058 for (var i in this.options.list) {
1059 element = this.options.list[i];
1060 if (element.type == 'folder' && element.fullname == foldername) {
1066 get_preference: function(name) {
1067 if (this.userprefs[name]) {
1068 return this.userprefs[name];
1073 set_preference: function(name, value) {
1074 if (this.userprefs[name] != value) {
1075 M.util.set_user_preference('filemanager_' + name, value);
1076 this.userprefs[name] = value;
1081 // finally init everything needed
1082 // hide loading picture, display filemanager interface
1083 var filemanager = Y.one('#filemanager-'+options.client_id);
1084 filemanager.removeClass('fm-loading').addClass('fm-loaded');
1086 var manager = new FileManagerHelper(options);
1088 filemanager: manager,
1089 acceptedtypes: options.filepicker.accepted_types,
1090 clientid: options.client_id,
1091 author: options.author,
1092 maxfiles: options.maxfiles,
1093 maxbytes: options.maxbytes,
1094 areamaxbytes: options.areamaxbytes,
1095 itemid: options.itemid,
1096 repositories: manager.filepicker_options.repositories,
1097 containerid: manager.dndcontainer.get('id'),
1098 contextid: options.context.id
1100 M.form_dndupload.init(Y, dndoptions);