diff --git a/js/data/MDMDeviceFolderStore.js b/js/data/MDMDeviceFolderStore.js index 3981838..1f89c4c 100644 --- a/js/data/MDMDeviceFolderStore.js +++ b/js/data/MDMDeviceFolderStore.js @@ -1,33 +1,62 @@ -Ext.namespace('Zarafa.plugins.mdm.data'); - -/** - * @class Zarafa.plugins.mdm.data.MDMDeviceFolderStore - * @extends Zarafa.core.data.MAPISubStore - * @xtype mdm.devicefolderstore - * Store specific for MDM Plugin which creates {@link Zarafa.plugins.mdm.MDMDeviceFolderStore record}. - */ -Zarafa.plugins.mdm.data.MDMDeviceFolderStore = Ext.extend(Zarafa.core.data.MAPISubStore, { - /** - * @constructor - * @param config Configuration object - */ - constructor: function (config) - { - config = config || {}; - - Ext.applyIf(config, { - autoLoad: true, - remoteSort: false, - reader: new Zarafa.plugins.mdm.data.JsonDeviceFolderReader(), - writer: new Zarafa.core.data.JsonWriter(), - proxy: new Zarafa.core.data.IPMProxy({ - listModuleName: 'pluginmdmmodule', - itemModuleName: 'pluginmdmmodule' - }) - }); - - Zarafa.plugins.mdm.data.MDMDeviceFolderStore.superclass.constructor.call(this, config); - } -}); - +Ext.namespace('Zarafa.plugins.mdm.data'); + +/** + * @class Zarafa.plugins.mdm.data.MDMDeviceFolderStore + * @extends Zarafa.core.data.MAPISubStore + * @xtype mdm.devicefolderstore + * Store specific for MDM Plugin which creates {@link Zarafa.plugins.mdm.MDMDeviceFolderStore record}. + */ +Zarafa.plugins.mdm.data.MDMDeviceFolderStore = Ext.extend(Zarafa.core.data.MAPISubStore, { + /** + * @constructor + * @param config Configuration object + */ + constructor: function (config) + { + config = config || {}; + + Ext.applyIf(config, { + autoLoad: true, + remoteSort: false, + reader: new Zarafa.plugins.mdm.data.JsonDeviceFolderReader(), + writer: new Zarafa.plugins.mdm.data.MDMDeviceFolderWriter(), + proxy: new Zarafa.core.data.IPMProxy({ + listModuleName: 'pluginmdmmodule', + itemModuleName: 'pluginmdmmodule' + }) + }); + + Zarafa.plugins.mdm.data.MDMDeviceFolderStore.superclass.constructor.call(this, config); + }, + + /** + * Function which is use to add {@link Zarafa.plugins.mdm.data.MDMDeviceFolderRecord folder} into + * {@link Zarafa.plugins.mdm.MDMDeviceFolderStore store} which will share with respective device. + * @param {Zarafa.hierarchy.data.IPFRecord} folder folder which is will add into {@link Zarafa.plugins.mdm.MDMDeviceFolderStore store} + */ + addFolder : function (folder) + { + var record = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.MDM_Device_Folder, { + "entryid": folder.get("entryid") + }); + this.add(record); + }, + + /** + * Function which is use to remove {@link Zarafa.plugins.mdm.data.MDMDeviceFolderRecord folder} from + * {@link Zarafa.plugins.mdm.MDMDeviceFolderStore store}. + * @param {Zarafa.hierarchy.data.IPFRecord} folder folder which is will remove from {@link Zarafa.plugins.mdm.MDMDeviceFolderStore store} + */ + removeFolder : function (folder) + { + var found = this.findBy(function (record) { + return Zarafa.core.EntryId.compareEntryIds(record.get("entryid"), folder.get("entryid")); + }); + + if (found >= 0) { + this.removeAt(found); + } + } +}); + Ext.reg('mdm.devicefolderstore', Zarafa.plugins.mdm.data.MDMDeviceFolderStore); \ No newline at end of file diff --git a/js/data/MDMDeviceFolderWriter.js b/js/data/MDMDeviceFolderWriter.js new file mode 100644 index 0000000..5999891 --- /dev/null +++ b/js/data/MDMDeviceFolderWriter.js @@ -0,0 +1,52 @@ +Ext.namespace('Zarafa.plugins.mdm.data'); + +/** + * @class Zarafa.plugins.mdm.data.MDMDeviceFolderWriter + * @extends Zarafa.core.data.JsonWriter + * + * This extension of the {@link Zarafa.core.data.JsonWriter} for writing + * {@link Zarafa.plugins.mdm.data.MDMDeviceFolderRecord records} in preparation for executing CRUD action on + * {@link Zarafa.plugins.mdm.data.MDMDeviceFolderStore stores} + */ +Zarafa.plugins.mdm.data.MDMDeviceFolderWriter = Ext.extend(Zarafa.core.data.JsonWriter, { + /** + * Similar to {@link Ext.data.JsonWriter#toHash} + * + * Convert sharedFolder into a hash. {@link Zarafa.plugins.mdm.data.MDMDeviceFolderRecord folder} exists + * within a {@link Zarafa.plugins.mdm.data.MDMDeviceRecord IPMRecord} and thus must be serialized + * seperately into the hash object. + * + * @param {Ext.data.Record} record The record to hash + * @return {Object} The hashed object + * @override + * @private + */ + toPropHash : function(record) + { + var sharedFolderStore = record.getSubStore('sharedfolders'); + var hash = {}; + + if (!Ext.isDefined(sharedFolderStore)) { + return hash; + } + + // Get list of modified (modified and newly added) records + var modifiedRecords = sharedFolderStore.getModifiedRecords(); + // Get list of removed records + var deletedRecords = sharedFolderStore.getRemovedRecords(); + + // Adding the modified folder to the add or modified part of the sharedFolder bit + if (modifiedRecords.length) { + hash.sharedfolders = {}; + hash.sharedfolders.add = modifiedRecords.map(function(r){return r.data;}); + } + + // Adding the removed folders to the remove part of the sharedFolder bit + if (deletedRecords.length) { + hash.sharedfolders = hash.sharedfolders || {}; + hash.sharedfolders.remove = deletedRecords.map(function(r){return r.data;}); + } + + return hash; + } +}); diff --git a/js/data/MDMHierarchyTreeLoader.js b/js/data/MDMHierarchyTreeLoader.js index 8eb159c..78e7c98 100644 --- a/js/data/MDMHierarchyTreeLoader.js +++ b/js/data/MDMHierarchyTreeLoader.js @@ -1,57 +1,49 @@ -Ext.namespace('Zarafa.hierarchy.data'); - -/** - * @class Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader - * @extends Zarafa.hierarchy.data.HierarchyTreeLoader - * - * A Special treeloader to be used by the {@link Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader MDMHierarchyTree}. - * This wil dynamically load the child nodes for a given node by obtaining the subfolders of - * the folder related to the given node. - */ -Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader = Ext.extend(Zarafa.hierarchy.data.HierarchyTreeLoader, { - - /** - * @constructor - * @param {Object} config Configuration object - */ - constructor : function(config) - { - Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader.superclass.constructor.call(this, config); - }, - - /** - * Add extra attributes for a new {@link Zarafa.hierarchy.ui.FolderNode folderNode} which is about - * to be created. This will check the {@link Zarafa.hierarchy.ui.FolderNode#folder folder} to - * see what properties must be set. - * - * Override to provide (@link Zarafa.plugins.mdm.ui.MDMFolderNodeUI MDMFolderNodeUI} to ui provider - * @param {Object} attr The attributes which will be used to create the node - * @return {Zarafa.hierarchy.ui.FolderNode} The created node - */ - createNode : function(attr) - { - var folder = attr.folder; - - if (folder) { - if (attr.nodeType === 'rootfolder') { - attr.extendedDisplayName = this.tree.hasFilter(); - } - - // To uniquely identify the favorites tree nodes we append the "favorites-" key word with node id - // when the node is created. - attr.id = folder.isFavoritesFolder() ? "favorites-" + folder.get('entryid') : folder.get('entryid'); - if (folder.isFavoritesRootFolder()) { - attr.leaf = folder.get('assoc_content_count') === 0; - } else { - attr.leaf = !folder.get('has_subfolder'); - } - - attr.uiProvider = Zarafa.plugins.mdm.ui.MDMFolderNodeUI; - attr.expanded = this.tree.isFolderOpened(folder); - attr.allowDrag = !folder.isDefaultFolder(); - } - - // call parent of parent because of parent class will change ui provider - return Zarafa.hierarchy.data.HierarchyTreeLoader.superclass.createNode.apply(this, arguments); - } -}); +Ext.namespace('Zarafa.hierarchy.data'); + +/** + * @class Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader + * @extends Zarafa.hierarchy.data.HierarchyTreeLoader + * + * A Special treeloader to be used by the {@link Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader MDMHierarchyTree}. + * This wil dynamically load the child nodes for a given node by obtaining the subfolders of + * the folder related to the given node. + */ +Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader = Ext.extend(Zarafa.hierarchy.data.HierarchyTreeLoader, { + + /** + * @constructor + * @param {Object} config Configuration object + */ + constructor : function(config) + { + Zarafa.plugins.mdm.data.MDMHierarchyTreeLoader.superclass.constructor.call(this, config); + }, + + /** + * Add extra attributes for a new {@link Zarafa.hierarchy.ui.FolderNode folderNode} which is about + * to be created. This will check the {@link Zarafa.hierarchy.ui.FolderNode#folder folder} to + * see what properties must be set. + * + * Override to provide (@link Zarafa.plugins.mdm.ui.MDMFolderNodeUI MDMFolderNodeUI} to ui provider + * @param {Object} attr The attributes which will be used to create the node + * @return {Zarafa.hierarchy.ui.FolderNode} The created node + */ + createNode : function(attr) + { + var folder = attr.folder; + + if (folder) { + if (attr.nodeType === 'rootfolder') { + attr.extendedDisplayName = this.tree.hasFilter(); + } + + attr.leaf = !folder.get('has_subfolder'); + attr.uiProvider = Zarafa.plugins.mdm.ui.MDMFolderNodeUI; + attr.expanded = this.tree.isFolderOpened(folder); + attr.allowDrag = !folder.isDefaultFolder(); + } + + // call parent of parent because of parent class will change ui provider + return Zarafa.hierarchy.data.HierarchyTreeLoader.superclass.createNode.apply(this, arguments); + } +}); diff --git a/js/dialogs/MDMDeviceContentPanel.js b/js/dialogs/MDMDeviceContentPanel.js index 497ac7e..b93a1d5 100644 --- a/js/dialogs/MDMDeviceContentPanel.js +++ b/js/dialogs/MDMDeviceContentPanel.js @@ -27,6 +27,7 @@ Zarafa.plugins.mdm.dialogs.MDMDeviceContentPanel = Ext.extend(Zarafa.core.ui.Rec }), layout: 'fit', stateful: false, + showInfoMask : false, showLoadMask: false, width: isKOE ? 440 : 405, height: isKOE ? 450 : 420, diff --git a/js/dialogs/MDMDeviceDetailsTab.js b/js/dialogs/MDMDeviceDetailsTab.js index 5b8105a..9ea9b91 100644 --- a/js/dialogs/MDMDeviceDetailsTab.js +++ b/js/dialogs/MDMDeviceDetailsTab.js @@ -32,15 +32,11 @@ Zarafa.plugins.mdm.dialogs.MDMDeviceDetailsTab = Ext.extend(Ext.form.FormPanel, layout: 'form', labelWidth: 150 }, - + plugins : ['zarafa.recordcomponentupdaterplugin'], items: [ this.createDeviceInfoPanel(config.isKoe), this.createVersionInfoPanel(config.isKoe) - ], - listeners: { - afterlayout: this.onAfterLayout, - scope: this - } + ] }); // KOE information @@ -156,12 +152,14 @@ Zarafa.plugins.mdm.dialogs.MDMDeviceDetailsTab = Ext.extend(Ext.form.FormPanel, }, /** - * Function which handles the afterlayout event - * Which is use to set record values into form fields. + * Updates the panel by loading data from the record. + * + * @param {Zarafa.core.data.IPMRecord} record The record update the panel with. + * @param {Boolean} contentReset force the component to perform a full update of the data. */ - onAfterLayout: function () + update : function(record, contentReset) { - this.getForm().loadRecord(this.record); + this.getForm().loadRecord(record); } }); diff --git a/js/dialogs/MDMDeviceGeneralTab.js b/js/dialogs/MDMDeviceGeneralTab.js index e46b0c6..2730456 100644 --- a/js/dialogs/MDMDeviceGeneralTab.js +++ b/js/dialogs/MDMDeviceGeneralTab.js @@ -33,14 +33,11 @@ Zarafa.plugins.mdm.dialogs.MDMDeviceGeneralTab = Ext.extend(Ext.form.FormPanel, labelWidth: 150, cls: 'mdm-device-panel' }, + plugins : ['zarafa.recordcomponentupdaterplugin'], items: [ this.createDeviceInfoPanel(config), this.createFolderInfoPanel() - ], - listeners: { - afterlayout: this.onAfterLayout, - scope: this - } + ] }); Zarafa.plugins.mdm.dialogs.MDMDeviceGeneralTab.superclass.constructor.call(this, config); @@ -173,12 +170,15 @@ Zarafa.plugins.mdm.dialogs.MDMDeviceGeneralTab = Ext.extend(Ext.form.FormPanel, }, /** - * Function which handles the after layoutevent. - * Which is use to set record values into form fields. + * Updates the panel by loading data from the record into the header template, and + * loading the body html into the embedded iframe. + * + * @param {Zarafa.core.data.IPMRecord} record The record update the panel with. + * @param {Boolean} contentReset force the component to perform a full update of the data. */ - onAfterLayout: function () + update : function(record, contentReset) { - this.getForm().loadRecord(this.record); + this.getForm().loadRecord(record); }, /** @@ -198,6 +198,7 @@ Zarafa.plugins.mdm.dialogs.MDMDeviceGeneralTab = Ext.extend(Ext.form.FormPanel, */ onClickManageSharedFolder: function () { + this.dialog.record.opened = false; Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['mdm.dialog.mdmmanagesharedfoldercontentpanel'], undefined, { manager: Ext.WindowMgr, record: this.dialog.record diff --git a/js/dialogs/MDMManageSharedFolderContentPanel.js b/js/dialogs/MDMManageSharedFolderContentPanel.js index b77d673..47e9b50 100644 --- a/js/dialogs/MDMManageSharedFolderContentPanel.js +++ b/js/dialogs/MDMManageSharedFolderContentPanel.js @@ -2,13 +2,13 @@ Ext.namespace('Zarafa.plugins.mdm.dialogs'); /** * @class Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderContentPanel - * @extends Zarafa.core.ui.ContentPanel + * @extends Zarafa.core.ui.RecordContentPanel * @xtype zarafa.managesharedfoldercontentpanel * * This will display a {@link Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel contentpanel} * to show {@link Zarafa.core.data.IPFRecord folders} which are shared with device. */ -Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, { +Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderContentPanel = Ext.extend(Zarafa.core.ui.RecordContentPanel, { /** * @constructor @@ -22,6 +22,11 @@ Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderContentPanel = Ext.extend(Zarafa xtype: 'mdm.managesharedfoldercontentpanel', layout: 'fit', title: dgettext('plugin_mdm','Manage Shared Folder'), + modal: true, + stateful: false, + showInfoMask : false, + showLoadMask: false, + closeOnSave: true, width: 300, height: 350, items: [{ diff --git a/js/dialogs/MDMManageSharedFolderPanel.js b/js/dialogs/MDMManageSharedFolderPanel.js index b7dd667..486c845 100644 --- a/js/dialogs/MDMManageSharedFolderPanel.js +++ b/js/dialogs/MDMManageSharedFolderPanel.js @@ -1,132 +1,217 @@ -Ext.namespace('Zarafa.plugins.mdm.dialogs'); - -/** - * @class Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel - * @extends Ext.Panel - * @xtype mdm.managesharedfolderpanel - * - * Panel for users to show the {@link Zarafa.core.data.IPFRecord folders} which are shared with device - */ -Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel = Ext.extend(Ext.Panel, { - - /** - * @constructor - * @param {Object} config Configuration structure - */ - constructor: function (config) - { - config = config || {}; - - Ext.applyIf(config, { - xtype: 'mdm.managesharedfolderpanel', - layout: { - type: 'fit', - align: 'stretch' - }, - border: false, - header: false, - items: [ - this.createTreePanel() - ], - buttonAlign: 'right', - buttons: [{ - text: _('Apply'), - ref: '../okButton', - cls: 'zarafa-action', - scope: this - }, { - text: _('Cancel'), - ref: '../cancelButton', - handler: this.onCancel, - scope: this - }] - }); - - Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel.superclass.constructor.call(this, config); - }, - - /** - * Creates a {@link Zarafa.hierarchy.ui.Tree treepanel} - * which contains all the {@link Zarafa.hierarchy.data.MAPIFolderRecord folders} - * on which search get perform. - * @return {Object} Configuration object for the tree panel. - * @private - */ - createTreePanel: function () - { - return { - xtype: 'panel', - layout : 'form', - defaults: { - cls : 'mdm-create-tree-panel-item' - }, - border: false, - flex: 1, - items: [{ - xtype: 'displayfield', - hideLabel : true, - value: dgettext('plugin_mdm','Select folders to sync on your device') - }, { - xtype: 'mdm.hierarchytree', - autoScroll : true, - nodeConfig : { - checked : false - }, - multiSelect: true, - hideShowAllFolders: true, - border: true, - treeSorter: true, - bbarConfig: { - hidden: true - }, - enableDD: false, - anchor: '100% 90%', - ref: '../hierarchyTree' - }] - }; - }, - - /** - * Initialize the events - * @private - */ - initEvents: function () - { - Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel.superclass.initEvents.apply(this, arguments); - this.mon(this.hierarchyTree, 'load', this.onTreeNodeLoad, this); - }, - - /** - * Fired when the {@link Zarafa.hierarchy.ui.Tree Tree} fires the {@link Zarafa.hierarchy.ui.Tree#load load} - * event. This function will try to select those {@link Ext.tree.TreeNode TreeNode} in - * {@link Zarafa.hierarchy.ui.Tree Tree} which was shared with respective device. When the given node is not loaded yet, it will try again - * later when the event is fired again. - * - * @private - */ - onTreeNodeLoad: function () - { - var subStore = this.dialog.record.getSubStore('sharedfolders'); - var folders = subStore.getRange(); - folders.forEach(function (folder) { - var node = this.hierarchyTree.getNodeById(folder.get('entryid')); - if (Ext.isDefined(node)) { - if(node.hasChildNodes()){ - node.expand(); - } - node.getUI().toggleCheck(true) - } - }, this); - }, - - /** - * Action handler when the user presses the "Cancel" button. - * This will close the panel. - */ - onCancel: function () - { - this.dialog.close(); - } -}); - +Ext.namespace('Zarafa.plugins.mdm.dialogs'); + +/** + * @class Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel + * @extends Ext.Panel + * @xtype mdm.managesharedfolderpanel + * + * Panel for users to show the {@link Zarafa.core.data.IPFRecord folders} which are shared with device + */ +Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel = Ext.extend(Ext.Panel, { + + /** + * @cfg {Zarafa.plugins.mdm.data.MDMDeviceFolderStore} store contains {@link Zarafa.plugins.mdm.data.MDMDeviceFolderRecord folders} which + * is going to shared with device. + */ + sharedFoldersStore : undefined, + + /** + * @cfg {Zarafa.core.data.IPMRecord} record The mail which + * is being update by this panel. + */ + record: null, + + /** + * @constructor + * @param {Object} config Configuration structure + */ + constructor: function (config) + { + config = config || {}; + + Ext.applyIf(config, { + xtype: 'mdm.managesharedfolderpanel', + layout: { + type: 'fit', + align: 'stretch' + }, + border: false, + header: false, + items: [ + this.createTreePanel() + ], + buttonAlign: 'right', + plugins : ['zarafa.recordcomponentupdaterplugin'], + buttons: [{ + text: _('Apply'), + handler: this.onApply, + cls: 'zarafa-action', + scope: this + }, { + text: _('Cancel'), + handler: this.onCancel, + scope: this + }] + }); + + Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel.superclass.constructor.call(this, config); + }, + + /** + * Creates a {@link Zarafa.hierarchy.ui.Tree treepanel} + * which contains all the {@link Zarafa.hierarchy.data.MAPIFolderRecord folders} + * on which search get perform. + * @return {Object} Configuration object for the tree panel. + * @private + */ + createTreePanel: function () + { + return { + xtype: 'panel', + layout : 'form', + defaults: { + cls : 'mdm-create-tree-panel-item' + }, + border: false, + flex: 1, + items: [{ + xtype: 'displayfield', + hideLabel : true, + value: dgettext('plugin_mdm','Select folders to sync on your device') + }, { + xtype: 'mdm.hierarchytree', + autoScroll : true, + hideOwnTree : true, + nodeConfig : { + checked : false + }, + multiSelect: true, + hideShowAllFolders: true, + border: true, + treeSorter: true, + bbarConfig: { + hidden: true + }, + enableDD: false, + anchor: '100% 90%', + ref: '../hierarchyTree' + }] + }; + }, + + /** + * Initialize the events + * @private + */ + initEvents: function () + { + Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel.superclass.initEvents.apply(this, arguments); + this.mon(this.hierarchyTree, { + expandnode: this.onTreeNodeExpand, + checkchange: this.onTreeNodeCheckChange, + click: this.onTreeNodeClick, + scope: this + }); + }, + + /** + * Fired when the {@link Zarafa.hierarchy.ui.Tree Tree} fires the {@link Zarafa.hierarchy.ui.Tree#nodeexpand nodeexpand} + * event. + * It will update the hierarchy by selecting child node if it will shared with device. + * @private + */ + onTreeNodeExpand: function () + { + if (!this.record.isOpened()) { + return false; + } + this.updateHierarchy(); + }, + + /** + * Updates the panel by loading data from the record. + * + * @param {Zarafa.core.data.IPMRecord} 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; + this.sharedFoldersStore = record.getSubStore('sharedfolders'); + this.updateHierarchy(); + }, + + /** + * Function will try to select those {@link Ext.tree.TreeNode TreeNode} in + * {@link Zarafa.hierarchy.ui.Tree Tree} which was shared with respective device. + */ + updateHierarchy : function () + { + var folders = this.sharedFoldersStore.getRange(); + folders.forEach(function (folder) { + var node = this.hierarchyTree.getNodeById(folder.get('entryid')); + if (Ext.isDefined(node)) { + if (node.hasChildNodes()) { + node.expand(); + } + node.isNodeSelected = true; + node.getUI().toggleCheck(true); + } + }, this); + }, + + /** + * Called when a treeNode is click in tree. The corresponding folder is added to, + * or removed from the active folder list depending on the state of the check box. + * @param {Ext.tree.TreeNode} treeNode tree node. + * @private + */ + onTreeNodeClick : function(treeNode) + { + var treeNodeui = treeNode.getUI(); + if (treeNodeui.checkbox.checked && treeNode.isNodeSelected) { + treeNodeui.toggleCheck(false); + return false; + } + treeNode.isNodeSelected = true; + this.sharedFoldersStore.addFolder(treeNode.getFolder()); + treeNodeui.toggleCheck(true); + }, + + /** + * Called when a check box in the calendar tree is toggled. The corresponding folder is added to, + * or removed from the active folder list depending on the state of the check box. + * @param {Ext.tree.TreeNode} node tree node. + * @param {Boolean} checked indicates whether the box is checked. + * @private + */ + onTreeNodeCheckChange : function(node, checked) + { + if (!checked) { + node.isNodeSelected = false; + this.sharedFoldersStore.removeFolder(node.getFolder()); + } else if (checked && !node.isNodeSelected) { + this.onTreeNodeClick(node); + } + }, + + /** + * Action handler when the user presses the "Apply" button. + * This save the record and close the panel. + */ + onApply : function () + { + this.record.save(); + }, + + /** + * Action handler when the user presses the "Cancel" button. + * This will close the panel. + */ + onCancel: function () + { + this.dialog.close(); + } +}); + Ext.reg('mdm.managesharedfolderpanel', Zarafa.plugins.mdm.dialogs.MDMManageSharedFolderPanel); \ No newline at end of file diff --git a/js/ui/MDMHierarchyTreePanel.js b/js/ui/MDMHierarchyTreePanel.js index f0aa48a..05f9fa5 100644 --- a/js/ui/MDMHierarchyTreePanel.js +++ b/js/ui/MDMHierarchyTreePanel.js @@ -38,6 +38,25 @@ Zarafa.plugins.mdm.ui.MDMHierarchyTreePanel = Ext.extend(Zarafa.hierarchy.ui.Tre // call parent Zarafa.plugins.mdm.ui.MDMHierarchyTreePanel.superclass.initComponent.apply(this, arguments); + }, + + /** + * The filter which is applied for filtering nodes from the + * {@link Zarafa.hierarchy.ui.Tree HierarchyTree}. + * It will hide own user store. + * + * @param {Object} folder the folder to filter + * @return {Boolean} true to accept the folder + */ + nodeFilter: function (folder) + { + var hide = Zarafa.plugins.mdm.ui.MDMHierarchyTreePanel.superclass.nodeFilter.apply(this, arguments); + + if(hide && this.hideOwnTree) { + hide = !folder.getMAPIStore().isDefaultStore(); + } + + return hide; } }); diff --git a/manifest.xml b/manifest.xml index fa102d2..a002150 100755 --- a/manifest.xml +++ b/manifest.xml @@ -32,6 +32,7 @@ js/data/MDMResponseHandler.js js/data/MDMDeviceRecord.js js/data/JsonDeviceReader.js + js/data/MDMDeviceFolderWriter.js js/data/MDMDeviceStore.js js/data/ProvisioningStatus.js js/data/MDMDeviceProxy.js diff --git a/php/class.pluginmdmmodule.php b/php/class.pluginmdmmodule.php index 22ce717..f57f87b 100755 --- a/php/class.pluginmdmmodule.php +++ b/php/class.pluginmdmmodule.php @@ -121,6 +121,22 @@ class PluginMDMModule extends Module return $client->RemoveDevice($deviceid); } + + /** + * Function witch is use get details of given device. + * @param string $deviceid id of device. + * @return array contains device props. + */ + function getDeviceDetails($deviceid) + { + $client = $this->getSoapClient(); + $deviceRawData = $client->GetDeviceDetails($deviceid); + $device = array(); + $device['props'] = $this->getDeviceProps($deviceRawData->data); + $device["sharedfolders"] = array("item" => $this->getAdditionalFolderList($deviceid)); + return $device; + } + /** * Executes all the actions in the $data variable. * @return boolean true on success of false on fialure. @@ -173,18 +189,18 @@ class PluginMDMModule extends Module break; case 'open': - $data = array(); - $rawData = $this->getDevices(); - foreach($rawData as $device){ - if($device->data['deviceid'] === $actionData['entryid']) { - $data['props'] = $this->getDeviceProps($device->data); - $data["sharedfolders"] = array("item" => $this->getAdditionalFolderList($actionData['entryid'])); - } - } - $item = array("item" => $data); + $device = $this->getDeviceDetails($actionData["entryid"]); + $item = array("item" => $device); $this->addActionData('item', $item); $GLOBALS['bus']->addData($this->getResponseData()); break; + case 'save': + $this ->saveDevice($actionData); + $device = $this->getDeviceDetails($actionData["entryid"]); + $item = array("item" => $device); + $this->addActionData('update', $item); + $GLOBALS['bus']->addData($this->getResponseData()); + break; default: $this->handleUnknownActionType($actionType); } @@ -201,8 +217,21 @@ class PluginMDMModule extends Module $display_message = dgettext('plugin_mdm', 'Unable to connect to Z-Push Server. Not found.'); } } else if ($fault->faultcode === "ERROR") { - $display_message = dgettext('plugin_mdm', 'Device ID could not be found'); - } + $errors = (explode(": ", $fault->getMessage())); + switch ($errors[0]) { + case "ASDevice->AddAdditionalFolder()": + case "ZPushAdmin::AdditionalFolderAdd()": + $display_message = dgettext('plugin_mdm', "Folder can not be added because there is already an additional folder with the same name"); + break; + + case "ASDevice->RemoveAdditionalFolder()": + case "ZPushAdmin::AdditionalFolderRemove()": + $display_message = dgettext('plugin_mdm', "Folder can not be removed because there is no folder known with given folder id"); + break; + default: + $display_message = dgettext('plugin_mdm', "Device ID could not be found"); + } + } $this->sendFeedback(false, array("type" => ERROR_GENERAL, "info" => array('display_message' => $display_message))); } catch (Exception $e) { @@ -268,7 +297,11 @@ class PluginMDMModule extends Module $folderType = $this->getSyncFolderType($type, $name); if (isset($contentData[$folderid][self::FOLDERUUID])) { - $synchedFolderTypes[$folderType]++; + if(isset($synchedFolderTypes[$folderType])){ + $synchedFolderTypes[$folderType]++; + } else { + $synchedFolderTypes[$folderType] = 1; + } } } } @@ -295,7 +328,6 @@ class PluginMDMModule extends Module */ function getSyncFolderType($type, $name) { - $folderType = ''; switch ($type) { case SYNC_FOLDER_TYPE_APPOINTMENT: case SYNC_FOLDER_TYPE_USER_APPOINTMENT: @@ -353,5 +385,193 @@ class PluginMDMModule extends Module } return $data; } + + /** + * Function which is use to remove additional folder which was shared with given device. + * @param string $entryId id of device. + * @param string $folderid id of folder which will remove from device. + */ + function additionalFolderRemove($entryId, $folderid) + { + $client = $this->getSoapClient(); + $client->AdditionalFolderRemove($entryId, $folderid); + } + + /** + * Function which is use to add additional folder which will share with given device. + * @param string $entryId id of device. + * @param array $folder folder which will share with device. + */ + function additionalFolderAdd($entryId, $folder) + { + $client = $this->getSoapClient(); + $containerClass = isset($folder[PR_CONTAINER_CLASS]) ? $folder[PR_CONTAINER_CLASS] : "IPF.Note"; + $folderId = bin2hex($folder[PR_SOURCE_KEY]); + $userName = $folder["user"]; + $folderName = $userName === "SYSTEM" ? $folder[PR_DISPLAY_NAME] : $folder[PR_DISPLAY_NAME]." - ".$userName; + $folderType = $this->getFolderTypeFromContainerClass($containerClass); + $client->AdditionalFolderAdd($entryId, $userName, $folderId, $folderName, $folderType, FLD_FLAGS_REPLYASUSER); + } + + /** + * Function which use to save the device. + * It will use to add or remove folders in the device. + * @param array $data array of added and removed folders. + */ + function saveDevice($data) + { + $entryid = $data["entryid"]; + if (isset($data['sharedfolders'])) { + if (isset($data['sharedfolders']['remove'])) { + $deletedFolders = $data['sharedfolders']['remove']; + foreach ($deletedFolders as $folder) { + $this->additionalFolderRemove($entryid, $folder["folderid"]); + } + } + if (isset($data['sharedfolders']['add'])) { + $addFolders = $data['sharedfolders']['add']; + $hierarchyFolders = $this->getHierarchyList(); + foreach ($addFolders as $folder) { + foreach ($hierarchyFolders as $hierarchyFolder) { + $folderEntryid = bin2hex($hierarchyFolder[PR_ENTRYID]); + if ($folderEntryid === $folder["entryid"]) { + $this->additionalFolderAdd($entryid, $hierarchyFolder); + continue 2; + } + } + } + } + } + } + + /** + * Gets the hierarchy list of all required stores. + * Function which is use to get the hierarchy list with PR_SOURCE_KEY. + * @return array the array of all hierarchy folders. + */ + function getHierarchyList() + { + $storeList = $GLOBALS["mapisession"]->getAllMessageStores(); + $properties = $GLOBALS["properties"]->getFolderListProperties(); + $properties["source_key"] = PR_SOURCE_KEY; + $storeData = array(); + foreach ($storeList as $store) { + $msgstore_props = mapi_getprops($store, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_MDB_PROVIDER, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_OBJECT_TYPE, PR_STORE_SUPPORT_MASK, PR_MAILBOX_OWNER_ENTRYID, PR_MAILBOX_OWNER_NAME, PR_USER_ENTRYID, PR_USER_NAME, PR_QUOTA_WARNING_THRESHOLD, PR_QUOTA_SEND_THRESHOLD, PR_QUOTA_RECEIVE_THRESHOLD, PR_MESSAGE_SIZE_EXTENDED, PR_MAPPING_SIGNATURE, PR_COMMON_VIEWS_ENTRYID, PR_FINDER_ENTRYID)); + $storeType = $msgstore_props[PR_MDB_PROVIDER]; + + if ($storeType == ZARAFA_STORE_DELEGATE_GUID) { + $storeUserName = $GLOBALS["mapisession"]->getUserNameOfStore($msgstore_props[PR_ENTRYID]); + } else if( $storeType == ZARAFA_STORE_PUBLIC_GUID){ + $storeUserName = "SYSTEM"; + } else { + $storeUserName = $msgstore_props[PR_USER_NAME]; + } + if (isset($msgstore_props[PR_IPM_SUBTREE_ENTRYID])) { + $subtreeFolderEntryID = $msgstore_props[PR_IPM_SUBTREE_ENTRYID]; + try { + $subtreeFolder = mapi_msgstore_openentry($store, $subtreeFolderEntryID); + } catch (MAPIException $e) { + // We've handled the event + $e->setHandled(); + } + try { + + // remove hidden folders, folders with PR_ATTR_HIDDEN property set + // should not be shown to the client + $restriction = Array(RES_OR, Array( + Array(RES_PROPERTY, + Array( + RELOP => RELOP_EQ, + ULPROPTAG => PR_ATTR_HIDDEN, + VALUE => Array(PR_ATTR_HIDDEN => false) + ) + ), + Array(RES_NOT, + Array( + Array(RES_EXIST, + Array( + ULPROPTAG => PR_ATTR_HIDDEN + ) + ) + ) + ) + )); + + $expand = Array( + Array( + 'folder' => $subtreeFolder, + 'props' => mapi_getprops($subtreeFolder, Array(PR_ENTRYID, PR_SUBFOLDERS)) + ) + ); + + // Start looping through the $expand array, during each loop we grab the first item in + // the array and obtain the hierarchy table for that particular folder. If one of those + // subfolders has subfolders of its own, it will be appended to $expand again to ensure + // it will be expanded later. + while (!empty($expand)) { + $item = array_shift($expand); + + $hierarchyTable = mapi_folder_gethierarchytable($item['folder'], MAPI_DEFERRED_ERRORS); + mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH); + + mapi_table_setcolumns($hierarchyTable, $properties); + + // Load the hierarchy in small batches + $batchcount = 100; + do { + $rows = mapi_table_queryrows($hierarchyTable, null, 0, $batchcount); + + foreach ($rows as $subfolder) { + + // If the subfolders has subfolders of its own, append the folder + // to the $expand array, so it can be expanded in the next loop. + if ($subfolder[PR_SUBFOLDERS]) { + $folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]); + array_push($expand, array('folder' => $folderObject, 'props' => $subfolder)); + } + $subfolder["user"] = $storeUserName; + // Add the folder to the return list. + array_push($storeData, $subfolder); + } + + // When the server returned a different number of rows then was requested, + // we have reached the end of the table and we should exit the loop. + } while (count($rows) === $batchcount); + } + + } catch (MAPIException $e) { + + // We've handled the event + $e->setHandled(); + } + } + } + return $storeData; + } + + /** + * Function which is use get folder types from the container class + * @param string $containerClass container class of folder + * @return int folder type + */ + function getFolderTypeFromContainerClass($containerClass) + { + switch ($containerClass) { + case "IPF.Note": + return SYNC_FOLDER_TYPE_USER_MAIL; + case "IPF.Appointment": + return SYNC_FOLDER_TYPE_USER_APPOINTMENT; + case "IPF.Contact": + return SYNC_FOLDER_TYPE_USER_CONTACT; + case "IPF.StickyNote": + return SYNC_FOLDER_TYPE_USER_NOTE; + case "IPF.Task": + return SYNC_FOLDER_TYPE_USER_TASK; + case "IPF.Journal": + return SYNC_FOLDER_TYPE_USER_JOURNAL; + default: + return SYNC_FOLDER_TYPE_UNKNOWN; + } + } }; ?> diff --git a/php/zpushprops.php b/php/zpushprops.php index 70d31cb..22cf4ae 100644 --- a/php/zpushprops.php +++ b/php/zpushprops.php @@ -20,6 +20,7 @@ define("SYNC_FOLDER_TYPE_USER_NOTE", 17); define("SYNC_FOLDER_TYPE_UNKNOWN", 18); define("SYNC_FOLDER_TYPE_RECIPIENT_CACHE", 19); define("SYNC_FOLDER_TYPE_DUMMY", 999999); +define("FLD_FLAGS_REPLYASUSER", 1); // Other constant define('KOE_GAB_NAME', 'Z-Push-KOE-GAB');