4 * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
5 * @author Philipp Schüle <p.schuele@metaways.de>
6 * @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
9 Ext.ns('Tine.widgets', 'Tine.widgets.tree');
12 * returns generic tree context menu with
18 * ctxNode class var is required in calling class
20 Tine.widgets.tree.ContextMenu = {
23 * create new Ext.menu.Menu with actions
25 * @param {} config has the node name, actions, etc.
28 getMenu: function(config) {
32 /****************** create ITEMS array ****************/
34 this.action_add = new Ext.Action({
35 text: String.format(_('Add {0}'), this.config.nodeName),
36 iconCls: 'action_add',
37 handler: this.addNode,
38 requiredGrant: 'addGrant',
42 this.action_rename = new Ext.Action({
43 text: String.format(_('Rename {0}'), this.config.nodeName),
44 iconCls: 'action_rename',
45 handler: this.renameNode,
47 requiredGrant: 'editGrant',
51 this.action_delete = new Ext.Action({
52 text: String.format(Tine.Tinebase.translation.ngettext('Delete {0}', 'Delete {0}', 1), this.config.nodeName),
53 iconCls: 'action_delete',
54 handler: this.deleteNode,
56 requiredGrant: 'deleteGrant',
60 this.action_grants = new Ext.Action({
61 text: String.format(_('Manage {0} Permissions'), this.config.nodeName),
62 iconCls: 'action_managePermissions',
63 handler: this.managePermissions,
64 requiredGrant: 'editGrant',
68 this.action_properties = new Ext.Action({
69 text: String.format(_('{0} Properties'), this.config.nodeName),
70 iconCls: 'action_manageProperties',
71 handler: this.manageProperties,
72 requiredGrant: 'readGrant',
76 // TODO is edit grant required?
77 this.action_changecolor = new Ext.Action({
78 text: String.format(_('Set {0} color'), this.config.nodeName),
79 iconCls: 'action_changecolor',
80 // requiredGrant: 'editGrant',
82 menu: new Ext.menu.ColorMenu({
85 select: this.changeNodeColor,
91 this.action_reload = new Ext.Action({
92 text: String.format(_('Reload {0}'), this.config.nodeName),
93 iconCls: 'x-tbar-loading',
94 handler: this.reloadNode,
98 // TODO move the next 5 to Filemanager!
99 this.action_resume = new Ext.Action({
100 text: String.format(_('Resume upload'), config.nodeName),
101 iconCls: 'action_resume',
102 handler: this.onResume,
104 actionUpdater: this.isResumeEnabled
106 this.action_editFile = new Ext.Action({
107 requiredGrant: 'editGrant',
108 allowMultiple: false,
109 text: _('Edit Properties'),
110 handler: this.onEditFile,
111 iconCls: 'action_edit_file',
115 this.action_pause = new Ext.Action({
116 text: String.format(_('Pause upload'), config.nodeName),
117 iconCls: 'action_pause',
118 handler: this.onPause,
119 actionUpdater: this.isPauseEnabled,
123 this.action_download = new Ext.Action({
124 text: String.format(_('Download'), config.nodeName),
125 iconCls: 'action_filemanager_save_all',
126 handler: this.downloadFile,
127 actionUpdater: this.isDownloadEnabled,
131 this.action_publish = new Ext.Action({
132 text: String.format(_('Publish'), config.nodeName),
133 iconCls: 'action_publish',
134 handler: this.publishFile,
140 for (var i=0; i < config.actions.length; i++) {
141 switch(config.actions[i]) {
143 items.push(this.action_add);
146 items.push(this.action_delete);
149 items.push(this.action_rename);
152 items.push(this.action_properties);
155 items.push(this.action_changecolor);
158 items.push(this.action_grants);
161 items.push(this.action_reload);
164 items.push(this.action_resume);
167 items.push(this.action_pause);
170 items.push(this.action_download);
173 items.push(this.action_editFile);
176 items.push(this.action_publish);
179 // add custom actions
180 items.push(new Ext.Action(config.actions[i]));
185 /******************* return menu **********************/
187 return new Ext.menu.Menu({
195 addNode: function() {
196 Ext.MessageBox.prompt(String.format(_('New {0}'), this.nodeName), String.format(_('Please enter the name of the new {0}:'), this.nodeName), function(_btn, _text) {
197 if( this.scope.ctxNode && _btn == 'ok') {
199 Ext.Msg.alert(String.format(_('No {0} added'), this.nodeName), String.format(_('You have to supply a {0} name!'), this.nodeName));
202 Ext.MessageBox.wait(_('Please wait'), String.format(_('Creating {0}...' ), this.nodeName));
203 var parentNode = this.scope.ctxNode;
206 method: this.backend + '.add' + this.backendModel,
210 // TODO try to generalize this and move app specific stuff to app
212 if (this.backendModel == 'Node') {
213 params.application = this.scope.app.appName || this.scope.appName;
214 var filename = parentNode.attributes.nodeRecord.data.path + '/' + _text;
215 params.filename = filename;
216 params.type = 'folder';
217 params.method = this.backend + ".createNode";
219 else if (this.backendModel == 'Container') {
220 params.application = this.scope.app.appName || this.scope.appName;
221 params.containerType = Tine.Tinebase.container.path2type(parentNode.attributes.path);
222 params.modelName = this.scope.app.getMainScreen().getActiveContentType();
224 else if (this.backendModel == 'Folder') {
225 var parentFolder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(parentNode.attributes.folder_id);
226 params.parent = parentFolder.get('globalname');
227 params.accountId = parentFolder.get('account_id');
233 success: function(result, request){
234 var nodeData = Ext.util.JSON.decode(result.responseText);
236 if (nodeData.length == 0) {
237 Tine.log.err('Server returned empty node data!');
238 Ext.MessageBox.hide();
242 // TODO add + icon if it wasn't expandable before
243 if(nodeData.type == 'folder') {
244 var nodeData = Ext.util.JSON.decode(result.responseText);
246 var app = Tine.Tinebase.appMgr.get(this.scope.app.appName);
247 var newNode = app.getMainScreen().getWestPanel().getContainerTreePanel().createTreeNode(nodeData, parentNode);
248 parentNode.appendChild(newNode);
251 var newNode = this.scope.loader.createNode(nodeData);
252 parentNode.appendChild(newNode);
256 this.scope.fireEvent('containeradd', nodeData);
258 Ext.MessageBox.hide();
260 failure: function(result, request) {
261 var nodeData = Ext.util.JSON.decode(result.responseText);
263 var appContext = Tine[this.scope.app.appName];
264 if(appContext && appContext.handleRequestException) {
265 appContext.handleRequestException(nodeData.data);
277 renameNode: function() {
278 if (this.scope.ctxNode) {
280 var node = this.scope.ctxNode;
281 Ext.MessageBox.show({
282 title: String.format(_('Rename {0}'), this.nodeName),
283 msg: String.format(_('Please enter the new name of the {0}:'), this.nodeName),
284 buttons: Ext.MessageBox.OKCANCEL,
285 value: node.attributes.longName || node.text,
286 fn: function(_btn, _text){
289 Ext.Msg.alert(String.format(_('Not renamed {0}'), this.nodeName), String.format(_('You have to supply a {0} name!'), this.nodeName));
294 method: this.backend + '.rename' + this.backendModel,
298 params.application = this.scope.app.appName || this.scope.appName;
300 if (this.backendModel == 'Node') {
301 var filename = node.attributes.path;
302 params.sourceFilenames = [filename];
304 var targetFilename = "/";
305 var sourceSplitArray = filename.split("/");
306 for (var i=1; i<sourceSplitArray.length-1; i++) {
307 targetFilename += sourceSplitArray[i] + '/';
310 params.destinationFilenames = [targetFilename + _text];
311 params.method = this.backend + '.moveNodes';
314 // TODO try to generalize this
315 if (this.backendModel == 'Container') {
316 params.containerId = node.attributes.container.id;
317 } else if (this.backendModel == 'Folder') {
318 var folder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(node.attributes.folder_id);
319 params.oldGlobalName = folder.get('globalname');
320 params.accountId = folder.get('account_id');
326 success: function(_result, _request){
328 var nodeData = Ext.util.JSON.decode(_result.responseText);
331 this.scope.fireEvent('containerrename', nodeData, node, _text);
334 failure: function(result, request) {
335 var nodeData = Ext.util.JSON.decode(result.responseText);
337 var appContext = Tine[this.scope.app.appName];
338 if(appContext && appContext.handleRequestException) {
339 appContext.handleRequestException(nodeData.data);
347 icon: Ext.MessageBox.QUESTION
355 deleteNode: function() {
357 if (this.scope.ctxNode) {
358 var node = this.scope.ctxNode;
360 Tine.log.debug('Tine.widgets.tree.ContextMenu::deleteNode()');
361 Tine.log.debug(node);
363 Ext.MessageBox.confirm(_('Confirm'), String.format(_('Do you really want to delete the {0} "{1}"?'), this.nodeName, node.text), function(_btn){
364 if ( _btn == 'yes') {
367 method: this.backend + '.delete' + this.backendModel
370 if (this.backendModel == 'Node') {
372 var filenames = [node.attributes.path];
373 params.application = this.scope.app.appName || this.scope.appName;
374 params.filenames = filenames;
375 params.method = this.backend + ".deleteNodes";
377 } else if (this.backendModel == 'Container') {
378 params.containerId = node.attributes.container.id;
379 } else if (this.backendModel == 'Folder') {
380 var folder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(node.attributes.folder_id);
381 params.folder = folder.get('globalname');
382 params.accountId = folder.get('account_id');
384 // use default json api style
385 params.ids = [node.id];
386 params.method = params.method + 's';
392 success: function(_result, _request){
395 if(node.isSelected()) {
396 this.scope.getSelectionModel().select(node.parentNode);
397 this.scope.fireEvent('click', node.parentNode, Ext.EventObject.setEvent());
401 if (this.backendModel == 'Container') {
402 this.scope.fireEvent('containerdelete', node.attributes.container);
403 } else if (this.backendModel == 'Node') {
404 this.scope.fireEvent('containerdelete', node);
406 this.scope.fireEvent('containerdelete', node.attributes);
411 failure: function(result, request) {
412 var nodeData = Ext.util.JSON.decode(result.responseText);
413 var appContext = Tine[this.scope.app.appName];
414 if(appContext && appContext.handleRequestException) {
415 appContext.handleRequestException(nodeData.data);
425 * change tree node color
427 changeNodeColor: function(cp, color) {
428 if (this.scope.ctxNode) {
429 var node = this.scope.ctxNode;
430 node.getUI().addClass("x-tree-node-loading");
433 method: this.backend + '.set' + this.backendModel + 'Color',
434 containerId: node.attributes.container.id,
438 success: function(_result, _request){
439 var nodeData = Ext.util.JSON.decode(_result.responseText);
440 node.getUI().colorNode.setStyle({color: nodeData.color});
441 node.attributes.color = nodeData.color;
442 this.scope.fireEvent('containercolorset', nodeData);
443 node.getUI().removeClass("x-tree-node-loading");
445 failure: function(result, request) {
446 var nodeData = Ext.util.JSON.decode(result.responseText);
448 var appContext = Tine[this.scope.app.appName];
449 if(appContext && appContext.handleRequestException) {
450 appContext.handleRequestException(nodeData.data);
462 managePermissions: function() {
464 if (this.scope.ctxNode) {
465 var node = this.scope.ctxNode,
467 if(node.attributes.nodeRecord && node.attributes.nodeRecord.data.name) {
468 grantsContainer = node.attributes.nodeRecord.data.name;
469 } else if(node.attributes.container) {
470 grantsContainer = node.attributes.container;
473 var window = Tine.widgets.container.GrantsDialog.openWindow({
474 title: String.format(_('Manage Permissions for {0} "{1}"'), this.nodeName, Ext.util.Format.htmlEncode(grantsContainer.name)),
475 containerName: this.nodeName,
476 grantContainer: grantsContainer,
477 app: this.scope.app.appName
487 manageProperties: function() {
488 if (this.scope.ctxNode) {
489 var node = this.scope.ctxNode,
492 if (node.attributes.nodeRecord && node.attributes.nodeRecord.data.name) {
493 grantsContainer = node.attributes.nodeRecord.data.name;
494 } else if(node.attributes.container) {
495 grantsContainer = node.attributes.container;
498 ctxNodeName = grantsContainer.name ? grantsContainer.name : grantsContainer;
500 var window = Tine.widgets.container.PropertiesDialog.openWindow({
501 title: String.format(_('Properties for {0} "{1}"'), this.nodeName, Ext.util.Format.htmlEncode(ctxNodeName)),
502 containerName: this.nodeName,
503 // TODO fix this in Filemanager! sub-nodes do not have a container...
504 grantContainer: grantsContainer,
505 app: this.scope.app.appName
513 reloadNode: function() {
514 if (this.scope.ctxNode) {
515 var tree = this.scope;
516 this.scope.ctxNode.reload(function(node) {
520 tree.filterPlugin.onFilterChange();