Ext.namespace('Zarafa.common.rules.dialogs'); /** * @class Zarafa.common.rules.dialogs.RulesActionsContainer * @extends Ext.Container * @xtype zarafa.rulesactionscontainer * * The container in which all actions can be edited. This container * can be expanded to include multiple actions, and is able to parse * the rules_actions property of a {@link Zarafa.common.rules.data.RulesRecord rule}. */ Zarafa.common.rules.dialogs.RulesActionsContainer = Ext.extend(Ext.Container, { /** * The current number of action boxes which are present in the container. * This number is changed by {@link #addActionBox} and {@link #removeActionBox}. * @property * @type Number * @private */ actionCount : 0, /** * @constructor * @param {Object} config Configuration object */ constructor : function(config) { config = config || {}; config.plugins = Ext.value(config.plugins, []); config.plugins.push('zarafa.recordcomponentupdaterplugin'); Ext.applyIf(config, { layout : 'form', autoHeight: true, items : [{ xtype : 'zarafa.compositefield', hideLabel : true, items : [{ xtype : 'button', ref : '../addActionBtn', text : _('Add action'), handler : this.addActionBox, scope : this },{ xtype : 'button', ref : '../removeActionBtn', text : _('Remove action'), handler : this.removeActionBox, scope : this }] }] }); Zarafa.common.rules.dialogs.RulesActionsContainer.superclass.constructor.call(this, config); }, /** * Generic function to create containers in which an action is represented. This consists of * 2 components, the first one is the combobox in which the action type is selected, and the * second in which special option for the given action can be configured. * @param {Number} The index of the action which is created * @return {Object} config object to create a {@link Ext.Container}. * @private */ createActionBox : function(index) { var id = 'rule-action-' + String(index); var profileStore = { xtype : 'jsonstore', fields : [ { name : 'name' }, { name : 'value', type : 'int' } ], data : Zarafa.common.rules.data.ActionProfiles }; return { xtype : 'container', id : id, flex : 1, height : 25, layout : { type : 'hbox', align : 'stretch', defaultMargins : '0 5 0 0' }, items : [{ xtype : 'combo', width : 300, store : profileStore, mode : 'local', triggerAction : 'all', displayField : 'name', valueField : 'value', lazyInit : false, forceSelection : true, editable : false, value : _('Select one...'), listeners : { 'select' : this.onActionComboSelect, 'scope' : this } }, { xtype : 'container', flex : 1, layout : 'card', activeItem : 0, items : this.createActionContentPanels(id) }] }; }, /** * Create a set of ContentPanels which are used to configure the various action type. * The array which is returned contains should be applied on a {@link Ext.Container} with * a {@link Ext.layout.CardLayout CardLayout} to ensure only one container is visible * at a time. * In each container the user is able to set various configuration options for the * action type as selected in the combobox. * @param {String} baseId The baseId which is used to create the id for the individual containers. * @return {Array} Array of config objects to create a {@link Ext.Container}. * @private */ createActionContentPanels : function(baseId) { return [{ xtype : 'container', id : baseId + '-empty' },{ xtype : 'zarafa.folderselectionlink', id : baseId + '-folder' },{ xtype : 'zarafa.deletelink', id : baseId + '-delete', storeEntryId: this.storeEntryId },{ xtype : 'zarafa.userselectionlink', id : baseId + '-to' }]; }, /** * Function that can be used to add more actions in a rule. * @return {Ext.Container} The Action Box which was inserted * @private */ addActionBox : function() { this.actionCount++; var container = this.createActionBox(this.actionCount); container = this.insert(this.items.getCount() - 1, container); // Toggle the removeActionBtn this.removeActionBtn.setDisabled(this.actionCount <= 1); this.doLayout(); return container; }, /** * Function that can be used to remove an action from a rule. * This will always remove the last action. * @private */ removeActionBox : function() { if (this.actionCount > 1) { // Don't remove the last item, as that is the container // to add and remove actions. this.remove(this.get(this.items.getCount() - 2)); this.actionCount--; // Toggle the removeActionBtn this.removeActionBtn.setDisabled(this.actionCount <= 1); this.doLayout(); } }, /** * {@link #addActionBox add} or {@link #removeActionBox remove} * Action Boxes for a rule, until the {@link #actionCount} reaches * the given count. * @param {Number} count The desired number of action boxes * @private */ setActionBoxCount : function(count) { while (count < this.actionCount) { this.removeActionBox(); } while (count > this.actionCount) { this.addActionBox(); } }, /** * Updates the panel by loading data from the record into the form panel. * @param {Zarafa.common.rules.data.RulesRecord} record The record update the panel with. * @param {Boolean} contentReset force the component to perform a full update of the data. */ update : function(record, contentReset) { this.record = record; if (contentReset || record.isModifiedSinceLastUpdate('rule_actions')) { var actions = record.get('rule_actions'); if (Ext.isEmpty(actions)) { this.setActionBoxCount(1); return; } // Force actions to be an array actions = [].concat(actions); // We have to ensure that there are sufficient action fields // present in the container. When the rule doesn't have any // actions specified, we will create an empty action var count = Math.max(1, actions.length); this.setActionBoxCount(count); for (var i = 0, len = actions.length; i < len; i++) { // Apply the action to the corresponding container if (actions[i]) { this.applyAction(this.get(i), actions[i]); } } } }, /** * Update the given {@link Zarafa.core.data.IPMRecord record} with * the values from this {@link Ext.Panel panel}. * @param {Zarafa.core.data.IPMRecord} record The record to update */ updateRecord : function(record) { var actions = []; var actionsValid = true; for (var i = 0; i < this.actionCount; i++) { var panel = this.get(i); var activeItem = panel.get(1).layout.activeItem; var action = null; if (Ext.isFunction(activeItem.getAction)) { action = activeItem.getAction(); } // If no valid action was found, then // we have a problem and we can't save // the action. Break out of the loop // and invalidate the rule_actions property. if (!action) { panel.get(0).markInvalid(); actionsValid = false; } actions.push(action); } record.set('rule_actions', actions); record.setActionsValid(actionsValid); }, /** * Load an Action from a {@Link Zarafa.common.rules.data.RulesRecord} and apply it * onto the {@link Ext.Container} which was created by {@link #addActionBox}. * @param {Ext.Container} panel The container on which the action will be loaded * @param {Object} action The action which should be loaded * @private */ applyAction : function(panel, action) { var actionFlag = this.getActionFlagFromAction(action); var combo = panel.get(0); var content = panel.get(1); var store = combo.store; // Apply the correct value to the combobox. var index = store.findExact(combo.valueField, actionFlag); if (index >= 0) { var record = store.getAt(index); combo.setValue(actionFlag); this.onActionComboSelect(combo, record, index); } else { actionFlag = Zarafa.common.rules.data.ActionFlags.UNKNOWN; combo.setValue(_('Unknown action')); combo.markInvalid(_('This action for the current rule is unknown')); } // Fill the content with the data from the action var layout = content.getLayout(); switch (actionFlag) { case Zarafa.common.rules.data.ActionFlags.UNKNOWN: /* falls through */ default: break; case Zarafa.common.rules.data.ActionFlags.MOVE: case Zarafa.common.rules.data.ActionFlags.COPY: case Zarafa.common.rules.data.ActionFlags.DELETE: case Zarafa.common.rules.data.ActionFlags.REDIRECT: case Zarafa.common.rules.data.ActionFlags.FORWARD: case Zarafa.common.rules.data.ActionFlags.FORWARD_ATTACH: layout.activeItem.setAction(actionFlag, action); break; } }, /** * Read a Action object as located in the {@link Zarafa.common.rules.data.RulesRecord Rule} * and convert it to the corresponding ActionFlag which properly represents the action * to be taken. * @param {Object} action The action which should be converted to a Action Flag * @return {Zarafa.common.rules.data.ActionFlags} The Action Flag * @private */ getActionFlagFromAction : function(action) { switch (action.action) { case Zarafa.core.mapi.RuleActions.OP_MOVE: // The MOVE action can be used for either // DELETE or MOVE action. The decision for this // depends on if the selected folder is set the // the "Deleted Items" folder, and if the rule_state // property has the ST_EXIT_LEVEL flag. var RulesStates = Zarafa.core.mapi.RuleStates; if (this.record.get('rule_state') & RulesStates.ST_EXIT_LEVEL === RulesStates.ST_EXIT_LEVEL) { var deletedItems = container.getHierarchyStore().getDefaultFolder('wastebasket'); if (deletedItems && Zarafa.core.EntryId.compareEntryIds(deletedItems.get('entryid'), action.folderentryid)) { return Zarafa.common.rules.data.ActionFlags.DELETE; } } return Zarafa.common.rules.data.ActionFlags.MOVE; case Zarafa.core.mapi.RuleActions.OP_COPY: // Normal copy, nothing fancy. return Zarafa.common.rules.data.ActionFlags.COPY; case Zarafa.core.mapi.RuleActions.OP_FORWARD: // The exact forward action depends on the 'flavor' property. var FlavorFlags = Zarafa.core.mapi.FlavorFlags; switch (action.flavor) { case 0: // Forward return Zarafa.common.rules.data.ActionFlags.FORWARD; case FlavorFlags.FWD_PRESERVE_SENDER | FlavorFlags.FWD_DO_NOT_MUNGE_MSG: return Zarafa.common.rules.data.ActionFlags.REDIRECT; case FlavorFlags.FWD_AS_ATTACHMENT: return Zarafa.common.rules.data.ActionFlags.FORWARD_ATTACH; default: return Zarafa.common.rules.data.ActionFlags.UNKNOWN; } /* falls through */ default: // Any other RuleAction is not supported return Zarafa.common.rules.data.ActionFlags.UNKNOWN; } }, /** * The event handler for the {@link Ext.form.ComboBox#select} event for the combobox for * a particular action. This will update the corresponding content panel to show the correct * content type. * @param {Ext.form.ComboBox} combo The combobox which fired the event * @param {Ext.data.Record} record The record which was selected from the combobox * @param {Number} index The selected index from the combobox list * @private */ onActionComboSelect : function(combo, record, index) { var panel = combo.ownerCt; var content = panel.get(1); var layout = content.getLayout(); var value = record.get(combo.valueField); switch (value) { case Zarafa.common.rules.data.ActionFlags.UNKNOWN: /* falls through */ default: layout.setActiveItem(panel.id + '-empty'); break; case Zarafa.common.rules.data.ActionFlags.MOVE: case Zarafa.common.rules.data.ActionFlags.COPY: layout.setActiveItem(panel.id + '-folder'); layout.activeItem.setAction(value); break; case Zarafa.common.rules.data.ActionFlags.DELETE: layout.setActiveItem(panel.id + '-delete'); layout.activeItem.setAction(value); // For the DELETE action, the rule_state // must contain the ST_EXIT_LEVEL flag. this.record.set('rule_state', this.record.get('rule_state') | Zarafa.core.mapi.RuleStates.ST_EXIT_LEVEL); break; case Zarafa.common.rules.data.ActionFlags.REDIRECT: case Zarafa.common.rules.data.ActionFlags.FORWARD: case Zarafa.common.rules.data.ActionFlags.FORWARD_ATTACH: layout.setActiveItem(panel.id + '-to'); layout.activeItem.setAction(value); break; } } }); Ext.reg('zarafa.rulesactionscontainer', Zarafa.common.rules.dialogs.RulesActionsContainer);