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