MDL-21503 fixed regression
[moodle.git] / lib / form / filemanager.js
CommitLineData
494bf5c8
DC
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/>.
494bf5c8 15/**
494bf5c8 16 *
840912d5
DC
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
554cd8fc
DC
26 * this.filecount, how many files in this filemanager
27 * this.maxfiles
28 * this.maxbytes
840912d5
DC
29 *
30 * FileManager options:
31 * =====
32 * this.options.currentpath
33 * this.options.itemid
494bf5c8 34 */
554cd8fc
DC
35M.filemanagers = M.filemanagers||{};
36M.fm_filepickers = M.fm_filepickers || {};
56a7bf68 37
6c7c9d39 38YUI.add('core_filemanager', function(Y) {
840912d5
DC
39 function core_filemanager (args) {
40 core_filemanager.superclass.constructor.apply(this, arguments);
56a7bf68 41 }
840912d5
DC
42 core_filemanager.NAME = "FileManager";
43 core_filemanager.ATTRS = {
44 options: {},
45 lang: {}
46 };
47 Y.extend(core_filemanager, Y.Base, {
48 api: M.cfg.wwwroot+'/files/files_ajax.php',
49 menus: {},
50 initializer: function(args) {
51 this.options = args;
52 this.currentpath = '/';
53 this.filepicker_options = args.filepicker;
54 this.client_id = '';
554cd8fc
DC
55 this.maxfiles = args.maxfiles;
56 this.maxbytes = args.maxbytes;
840912d5
DC
57 if (args.client_id) {
58 this.client_id = args.client_id;
4bb7f787 59 }
554cd8fc
DC
60 if (args.filecount) {
61 this.filecount = args.filecount;
62 } else {
63 this.filecount = 0;
64 }
840912d5
DC
65 this.setup_buttons();
66 this.render();
67 },
68 request: function(args) {
69 var api = this.api + '?action='+args.action;
70 var params = {};
71 var scope = this;
72 if (args['scope']) {
73 scope = args['scope'];
74 }
75 params['sesskey']=M.cfg.sesskey;
76 params['client_id'] = this.client_id;
77 params['filepath'] = this.currentpath;
78 params['itemid'] = this.options.itemid?this.options.itemid:0;
79 if (args['params']) {
80 for (i in args['params']) {
81 params[i] = args['params'][i];
56a7bf68 82 }
56a7bf68 83 }
840912d5
DC
84 var cfg = {
85 method: 'POST',
86 on: {
87 complete: function(id,o,p) {
88 if (!o) {
89 alert('IO FATAL');
90 return;
91 }
384ab39a 92 var data = Y.JSON.parse(o.responseText);
840912d5
DC
93 args.callback(id,data,p);
94 }
95 },
96 arguments: {
97 scope: scope
98 },
99 headers: {
100 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
101 'User-Agent': 'MoodleFileManager/3.0'
102 },
103 data: build_querystring(params)
104 };
105 if (args.form) {
106 cfg.form = args.form;
56a7bf68 107 }
840912d5
DC
108 Y.io(api, cfg);
109 },
110 filepicker_callback: function(obj) {
554cd8fc
DC
111 var button_addfile = Y.one("#btnadd-"+this.client_id);
112 this.filecount++;
113 if (this.filecount >= this.maxfiles && this.maxfiles!=-1) {
114 button_addfile.setStyle('display', 'none');
115 }
840912d5
DC
116 this.refresh(this.currentpath);
117 },
118 refresh: function(filepath) {
119 var scope = this;
120 if (!filepath) {
121 filepath = this.currentpath;
122 } else {
123 this.currentpath = filepath;
56a7bf68 124 }
840912d5
DC
125 this.request({
126 action: 'list',
127 scope: scope,
128 params: {'filepath':filepath},
129 callback: function(id, obj, args) {
130 scope.options = obj;
131 scope.render(obj);
56a7bf68 132 }
840912d5
DC
133 });
134 },
135 setup_buttons: function() {
136 var client_id = this.client_id;
137 var button_download = Y.one("#btndwn-"+client_id);
138 var button_create = Y.one("#btncrt-"+client_id);
139 var button_addfile = Y.one("#btnadd-"+client_id);
840912d5
DC
140
141 // setup 'add file' button
142 // if maxfiles == -1, the no limit
554cd8fc
DC
143 if (this.filecount >= this.maxfiles
144 && this.maxfiles!=-1) {
145 button_addfile.setStyle('display', 'none');
146 } else {
147 button_addfile.on('click', function(e) {
148 var options = this.filepicker_options;
149 var scope = this;
150 var client_id = this.client_id;
151 Y.use('core_filepicker', function (Y) {
152 options.formcallback = scope.filepicker_callback;
153 // XXX: magic here, to let filepicker use filemanager scope
154 options.magicscope = scope;
155 options.savepath = scope.currentpath;
156 if (!M.fm_filepickers[client_id]) {
157 M.fm_filepickers[client_id] = new M.core_filepicker(options);
158 }
159 M.fm_filepickers[client_id].show();
160 });
161 }, this);
162 }
840912d5
DC
163
164 // setup 'make a folder' button
165 if (this.options.subdirs) {
166 button_create.on('click',function(e) {
167 var scope = this;
168 // a function used to perform an ajax request
169 function perform_action(e) {
170 var foldername = Y.one('#fm-newname').get('value');
171 if (!foldername) {
172 return;
173 }
174 scope.request({
175 action:'mkdir',
176 params: {filepath:scope.currentpath, newdirname:foldername},
177 callback: function(id, obj, args) {
178 var filepath = obj.filepath;
179 scope.mkdir_dialog.hide();
180 scope.refresh(filepath);
181 Y.one('#fm-newname').set('value', '');
182 }
183 });
184 }
185 if (!Y.one('#fm-mkdir-dlg')) {
186 var dialog = Y.Node.create('<div id="fm-mkdir-dlg"><div class="hd">'+mstr.repository.entername+'</div><div class="bd"><input type="text" id="fm-newname" /></div></div>');
187 Y.one(document.body).appendChild(dialog);
188 this.mkdir_dialog = new YAHOO.widget.Dialog("fm-mkdir-dlg", {
189 width: "300px",
190 visible: true,
191 x:e.pageX,
192 y:e.pageY,
193 constraintoviewport : true
194 });
56a7bf68 195
840912d5
DC
196 }
197 var buttons = [ { text:mstr.moodle.ok, handler:perform_action, isDefault:true },
198 { text:mstr.moodle.cancel, handler:function(){this.cancel();}}];
56a7bf68 199
840912d5
DC
200 this.mkdir_dialog.cfg.queueProperty("buttons", buttons);
201 this.mkdir_dialog.render();
202 this.mkdir_dialog.show();
203 }, this);
56a7bf68 204 } else {
840912d5 205 button_create.setStyle('display', 'none');
56a7bf68 206 }
840912d5
DC
207
208 // setup 'download this folder' button
209 // NOTE: popup window must be enabled to perform download process
210 button_download.on('click',function() {
211 var scope = this;
212 // perform downloaddir ajax request
213 this.request({
214 action: 'downloaddir',
215 scope: scope,
216 callback: function(id, obj, args) {
217 scope.refresh(obj.filepath);
218 var win = window.open(obj.fileurl, 'fm-download-folder');
219 if (!win) {
220 alert(mstr.repository.popupblockeddownload);
221 }
222 }
223 });
224 }, this);
225 },
226 render: function() {
227 var client_id = this.client_id;
228 var options = this.options;
229 var path = this.options.path;
230 var list = this.options.list;
231 var breadcrumb = Y.one('#fm-path-'+client_id);
232 // build breadcrumb
233 if (path) {
234 // empty breadcrumb
235 breadcrumb.set('innerHTML', '');
236 var count = 0;
237 for(var p in path) {
238 var arrow = '';
239 if (count==0) {
240 arrow = Y.Node.create('<span>'+mstr.moodle.path + ': </span>');
241 } else {
242 arrow = Y.Node.create('<span> ▶ </span>');
494bf5c8 243 }
840912d5
DC
244 count++;
245
246 var pathid = 'fm-path-node-'+client_id;
247 pathid += ('-'+count);
248
249 var crumb = Y.Node.create('<a href="###" id="'+pathid+'">'+path[p].name+'</a>');
250 breadcrumb.appendChild(arrow);
251 breadcrumb.appendChild(crumb);
252
253 var args = {};
254 args.requestpath = path[p].path;
255 args.client_id = client_id;
256 Y.one('#'+pathid).on('click', function(e, args) {
257 var scope = this;
258 var params = {};
259 params['filepath'] = args.requestpath;
260 this.request({
261 action: 'list',
262 scope: scope,
263 params: params,
264 callback: function(id, obj, args) {
265 scope.options = obj;
266 scope.render(obj);
267 }
268 });
269 }, this, args);
56a7bf68 270 }
56a7bf68 271 }
840912d5
DC
272 var template = Y.one('#fm-template');
273 var container = Y.one('#filemanager-' + client_id);
274 var listhtml = '';
275
276 // folder list items
277 var folder_ids = [];
278 var folder_data = {};
279
280 // normal file list items
281 var file_ids = [];
282 var file_data = {};
283
284 // archives list items
285 var zip_ids = [];
286 var zip_data = {};
287
288 var html_ids = [];
289 var html_data = {};
290
291 file_data.itemid = folder_data.itemid = zip_data.itemid = options.itemid;
292 file_data.client_id = folder_data.client_id = zip_data.client_id = this.client_id;
293
294 var foldername_ids = [];
295 if (!list || list.length == 0) {
296 // hide file browser and breadcrumb
297 container.setStyle('display', 'none');
298 if (!path || path.length <= 1) {
299 breadcrumb.setStyle('display', 'none');
56a7bf68 300 }
56a7bf68 301 return;
56a7bf68 302 } else {
840912d5
DC
303 container.setStyle('display', 'block');
304 breadcrumb.setStyle('display', 'block');
56a7bf68 305 }
56a7bf68 306
840912d5
DC
307 var count = 0;
308 for(var i in list) {
309 count++;
310 // the li html element
311 var htmlid = 'fileitem-'+client_id+'-'+count;
312 // link to file
313 var fileid = 'filename-'+client_id+'-'+count;
314 // file menu
315 var action = 'action-' +client_id+'-'+count;
316
317 var html = template.get('innerHTML');
318
319 html_ids.push('#'+htmlid);
320 html_data[htmlid] = action;
321
322 list[i].htmlid = htmlid;
323 list[i].fileid = fileid;
324 list[i].action = action;
325
326 var url = "###";
327 // check main file
328 //var ismainfile = false;
329 //if (fm_cfg[client_id].mainfilename && (fm_cfg[client_id].mainfilename.toLowerCase() == list[i].fullname.toLowerCase())) {
330 //ismainfile = true;
331 //}
332
333 switch (list[i].type) {
334 case 'folder':
335 // click folder name
336 foldername_ids.push('#'+fileid);
337 // click folder menu
338 folder_ids.push('#'+action);
339 folder_data[action] = list[i];
340 folder_data[fileid] = list[i];
341 break;
342 case 'file':
343 file_ids.push('#'+action);
344 file_data[action] = list[i];
345 if (list[i].url) {
346 url = list[i].url;
347 }
348 break;
349 case 'zip':
350 zip_ids.push('#'+action);
351 zip_data[action] = list[i];
352 if (list[i].url) {
353 url = list[i].url;
354 }
355 break;
356 }
357 var fullname = list[i].fullname;
56a7bf68 358
840912d5
DC
359 // add green tick to main file
360 //if (ismainfile) {
361 //fullname = "<strong>"+list[i].fullname+"</strong> <img src='"+M.cfg.wwwroot+"/pix/i/tick_green_small.gif"+"' />";
362 //}
56a7bf68 363
840912d5
DC
364 html = html.replace('___fullname___', '<a href="'+url+'" id="'+fileid+'"><img src="'+list[i].icon+'" /> ' + fullname + '</a>');
365 html = html.replace('___action___', '<a style="display:none" href="###" id="'+action+'"><img alt="▶" src="'+M.cfg.wwwroot+'/pix/i/settings.gif'+'" /></a>');
366 html = '<li id="'+htmlid+'">'+html+'</li>';
367 listhtml += html;
368 }
369 if (!Y.one('#draftfiles-'+client_id)) {
370 var filelist = Y.Node.create('<ul id="draftfiles-'+client_id+'"></ul>');
371 container.appendChild(filelist);
56a7bf68 372 }
840912d5
DC
373 Y.one('#draftfiles-'+client_id).set('innerHTML', listhtml);
374
375 // click normal file menu
376 Y.on('click', this.create_filemenu, file_ids, this, file_data);
377 // click folder menu
378 Y.on('click', this.create_foldermenu, folder_ids, this, folder_data);
379 // click archievs menu
380 Y.on('click', this.create_zipmenu, zip_ids, this, zip_data);
381 // when mouse moveover every menu
382 function mouseover_menu(e, obj) {
383 var node = e.currentTarget;
384 node.setStyle('backgroundColor', '#0066EE');
385 var menu = Y.one('#'+obj[node.get('id')]);
386 menu.setStyle('display', 'inline');
387 }
388 function mouseout_menu(e, obj) {
389 var node = e.currentTarget;
390 node.setStyle('backgroundColor', 'transparent');
391 var menu = Y.one('#'+obj[node.get('id')]);
392 menu.setStyle('display', 'none');
393 }
394 Y.on('mouseover', mouseover_menu, html_ids, this, html_data);
395 Y.on('mouseout', mouseout_menu, html_ids, this, html_data);
396 // click folder name
397 Y.on('click', this.enter_folder, foldername_ids, this, folder_data);
398 },
399 enter_folder: function(e, data) {
400 var node = e.currentTarget;
401 var file = data[node.get('id')];
402 this.refresh(file.filepath);
403 },
404 create_filemenu: function(e, data) {
405 var node = e.currentTarget;
406 var file = data[node.get('id')];
407
408 var menuitems = [
409 {text: mstr.moodle.download, url:file.url}
410 ];
411 this.create_menu(e, 'filemenu', menuitems, file, data);
412 },
413 create_foldermenu: function(e, data) {
414 var scope = this;
415 var node = e.currentTarget;
416 var fileinfo = data[node.get('id')];
417 // an extra menu item for folder to zip it
418 function archive_folder(type,ev,obj) {
419 var params = {};
420 params['filepath'] = fileinfo.filepath;
421 params['filename'] = '.';
422 this.request({
423 action: 'zip',
424 scope: scope,
425 params: params,
426 callback: function(id, obj, args) {
427 scope.refresh(obj.filepath);
56a7bf68 428 }
840912d5 429 });
56a7bf68 430 }
840912d5
DC
431 var menuitems = [
432 {text: mstr.editor.zip, onclick: {fn: archive_folder, obj: data, scope: this}},
433 ];
434 this.create_menu(e, 'foldermenu', menuitems, fileinfo, data);
435 },
436 create_zipmenu: function(e, data) {
437 var scope = this;
438 var node = e.currentTarget;
439 var fileinfo = data[node.get('id')];
440
441 function unzip(type, ev, obj) {
442 var params = {};
443 params['filepath'] = fileinfo.filepath;
444 params['filename'] = fileinfo.fullname;
445 this.request({
446 action: 'unzip',
447 scope: scope,
448 params: params,
449 callback: function(id, obj, args) {
450 scope.refresh(obj.filepath);
451 }
452 });
56a7bf68 453 }
840912d5
DC
454 var menuitems = [
455 {text: mstr.moodle.download, url:fileinfo.url},
456 {text: mstr.moodle.unzip, onclick: {fn: unzip, obj: data, scope: this}}
457 ];
458 this.create_menu(e, 'zipmenu', menuitems, fileinfo, data);
459 },
460 create_menu: function(ev, menuid, menuitems, fileinfo, options) {
461 var position = [ev.pageX, ev.pageY];
462 var scope = this;
463 function remove(type, ev, obj) {
464 var dialog_options = {};
465 var params = {};
466 dialog_options.message = mstr.repository.confirmdeletefile;
467 dialog_options.scope = this;
468 var filename = '';
469 var filepath = '';
470 if (fileinfo.type == 'folder') {
471 params.filename = '.';
472 params.filepath = fileinfo.fullname;
473 } else {
474 params.filename = fileinfo.fullname;
475 }
476 dialog_options.callback = function() {
477 this.request({
478 action: 'delete',
479 scope: scope,
480 params: params,
481 callback: function(id, obj, args) {
554cd8fc 482 scope.filecount--;
840912d5 483 scope.refresh(obj.filepath);
554cd8fc
DC
484 if (scope.filecount < scope.maxfiles && scope.maxfiles!=-1) {
485 var button_addfile = Y.one("#btnadd-"+scope.client_id);
486 button_addfile.setStyle('display', 'inline');
487 }
56a7bf68 488 }
840912d5 489 });
56a7bf68 490 }
840912d5 491 var dlg = confirm_dialog(ev, dialog_options);
56a7bf68 492 }
840912d5
DC
493 function rename (type, ev, obj) {
494 var scope = this;
495 var perform = function(e) {
496 var newfilename = Y.one('#fm-rename-input').get('value');
497 if (!newfilename) {
498 return;
499 }
56a7bf68 500
840912d5
DC
501 var action = '';
502 var params = {};
503 if (fileinfo.type == 'folder') {
504 params['filepath'] = fileinfo.filepath;
505 params['filename'] = '.';
506 action = 'renamedir';
507 } else {
508 params['filepath'] = fileinfo.filepath;
509 params['filename'] = fileinfo.fullname;
510 action = 'rename';
511 }
512 params['newfilename'] = newfilename;
513 scope.request({
514 action: action,
515 scope: scope,
516 params: params,
517 callback: function(id, obj, args) {
518 scope.refresh(obj.filepath);
519 Y.one('#fm-rename-input').set('value', '');
520 scope.rename_dialog.hide();
521 }
522 });
523 }
56a7bf68 524
840912d5
DC
525 var dialog = Y.one('#fm-rename-dlg');
526 if (!dialog) {
527 dialog = Y.Node.create('<div id="fm-rename-dlg"><div class="hd">'+mstr.repository.enternewname+'</div><div class="bd"><input type="text" id="fm-rename-input" /></div></div>');
528 Y.one(document.body).appendChild(dialog);
529 this.rename_dialog = new YAHOO.widget.Dialog("fm-rename-dlg", {
530 width: "300px",
531 fixedcenter: true,
532 visible: true,
533 constraintoviewport : true
534 });
56a7bf68 535
840912d5
DC
536 }
537 var buttons = [ { text:mstr.moodle.rename, handler:perform, isDefault:true},
538 { text:mstr.moodle.cancel, handler:function(){this.cancel();}}];
539
540 this.rename_dialog.cfg.queueProperty('buttons', buttons);
541 this.rename_dialog.render();
542 this.rename_dialog.show();
543 //var k1 = new YAHOO.util.KeyListener(scope, {keys:13}, {fn:function(){perform();}, correctScope: true});
544 //k1.enable();
545 Y.one('#fm-rename-input').set('value', fileinfo.fullname);
56a7bf68 546 }
840912d5
DC
547 function move(type, ev, obj) {
548 var scope = this;
549 var itemid = this.options.itemid;
550 // setup move file dialog
551 var dialog = null;
552 if (!Y.one('#fm-move-dlg')) {
553 dialog = Y.Node.create('<div id="fm-move-dlg"></div>');
554 Y.one(document.body).appendChild(dialog);
555 } else {
556 dialog = Y.one('#fm-move-dlg');
557 }
56a7bf68 558
840912d5 559 dialog.set('innerHTML', '<div class="hd">Moving</div><div class="bd"><div id="fm-move-div">'+mstr.repository.nopathselected+'</div><div id="fm-tree"></div></div>');
56a7bf68 560
840912d5
DC
561 this.movefile_dialog = new YAHOO.widget.Dialog("fm-move-dlg", {
562 width : "600px",
563 fixedcenter : true,
564 visible : false,
565 constraintoviewport : true
566 });
494bf5c8 567
840912d5 568 var treeview = new YAHOO.widget.TreeView("fm-tree");
494bf5c8 569
840912d5
DC
570 function _move(e) {
571 if (!treeview.targetpath) {
572 return;
573 }
574 var params = {};
575 if (fileinfo.type == 'folder') {
576 alert('Moving folder is not supported yet');
577 action = 'movedir';
578 return;
579 } else {
580 action = 'movefile';
581 }
582 params['filepath'] = fileinfo.filepath;
583 params['filename'] = fileinfo.fullname;
584 params['newfilepath'] = treeview.targetpath;
585 scope.request({
586 action: action,
587 scope: scope,
588 params: params,
589 callback: function(id, obj, args) {
590 var p = '/';
591 if (obj) {
592 p = result.filepath;
593 }
594 this.movefile_dialog.cancel();
595 }
596 });
56a7bf68 597 }
56a7bf68 598
840912d5
DC
599 var buttons = [ { text:mstr.moodle.move, handler:_move, isDefault:true },
600 { text:mstr.moodle.cancel, handler:function(){this.cancel();}}];
601
602 this.movefile_dialog.cfg.queueProperty("buttons", buttons);
603 this.movefile_dialog.render();
604
605 treeview.subscribe("dblClickEvent", function(e) {
606 // update destidatoin folder
607 this.targetpath = e.node.data.path;
608 var title = Y.one('#fm-move-div');
609 title.set('innerHTML', '<strong>"' + this.targetpath + '"</strong> has been selected.');
610 });
611
612 function loadDataForNode(node, onCompleteCallback) {
613 var params = {};
614 params['filepath'] = node.data.path;
615 var obj = {
616 action: 'dir',
617 scope: scope,
618 params: params,
619 callback: function(id, obj, args) {
620 data = obj.children;
621 if (data.length == 0) {
622 // so it is empty
623 } else {
624 for (var i in data) {
625 var textnode = {label: data[i].fullname, path: data[i].filepath, itemid: this.itemid};
626 var tmpNode = new YAHOO.widget.TextNode(textnode, node, false);
627 }
628 }
629 this.oncomplete();
630 }
631 };
632 obj.oncomplete = onCompleteCallback;
633 scope.request(obj);
634 }
56a7bf68 635
840912d5
DC
636 this.movefile_dialog.subscribe('show', function(){
637 var rootNode = treeview.getRoot();
638 treeview.setDynamicLoad(loadDataForNode);
639 treeview.removeChildren(rootNode);
640 var textnode = {label: "Files", path: '/'};
641 var tmpNode = new YAHOO.widget.TextNode(textnode, rootNode, true);
642 treeview.draw();
643 }, this, true);
56a7bf68 644
840912d5
DC
645 this.movefile_dialog.show();
646 }
647 var shared_items = [
648 {text: mstr.moodle.rename+'...', onclick: {fn: rename, obj: options, scope: this}},
649 {text: mstr.moodle.move+'...', onclick: {fn: move, obj: options, scope: this}},
650 // delete is reserve word in Javascript
651 {text: mstr.moodle['delete']+'...', onclick: {fn: remove, obj: options, scope: this}}
652 ];
653 var menu = new YAHOO.widget.Menu(menuid, {xy:position, clicktohide:true});
654 menu.clearContent();
655 menu.addItems(menuitems);
656 menu.addItems(shared_items);
657 menu.render(document.body);
658 menu.subscribe('hide', function(){
659 this.fireEvent('destroy');
660 });
661 menu.show();
662 }
663 });
6c7c9d39
DC
664 M.core_filemanager = core_filemanager;
665}, '3.0.1', {requires: ['base', 'io', 'node', 'json']});