initial commit

This commit is contained in:
Jelle van der Waa 2016-05-11 10:40:44 +02:00
commit ecaa6e3a35
19 changed files with 1047 additions and 0 deletions

198
build.xml Executable file
View File

@ -0,0 +1,198 @@
<project default="all">
<property environment="env"/>
<property name="root-folder" value="${basedir}/../../"/>
<property name="tools-folder" value="${root-folder}/tools/"/>
<property name="target-folder" value="${root-folder}/deploy/plugins"/>
<property name="server-folder" value="${root-folder}/server"/>
<import file="${tools-folder}/antutil.xml"/>
<typedef file="${tools-folder}/antlib.xml">
<classpath>
<pathelement location="${tools-folder}/tools.jar"/>
</classpath>
</typedef>
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="${tools-folder}/lib/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<!-- Determine plugin name -->
<basename file="${basedir}" property="plugin"/>
<!-- The Plugin distribution files -->
<property name="plugin-folder" value="${plugin}"/>
<property name="plugin-debugfile" value="${plugin}-debug.js"/>
<property name="plugin-file" value="${plugin}.js"/>
<!-- The Plugin CSS files -->
<property name="plugin-css-folder" value="resources/css"/>
<property name="plugin-css-file" value="${plugin}.css"/>
<!-- Meta target -->
<target name="all" depends="concat, compress"/>
<!-- Clean -->
<target name="clean">
<delete includeemptydirs="true" failonerror="false">
<!-- Delete the Plugin files -->
<fileset dir="${target-folder}/${plugin-folder}/js">
<include name="${plugin-file}"/>
<include name="${plugin-debugfile}"/>
</fileset>
</delete>
</target>
<!-- Concatenates JavaScript files with automatic dependency generation -->
<target name="concat">
<!-- Concatenate plugin JS file -->
<if>
<available file="js" type="dir" />
<then>
<mkdir dir="${target-folder}/${plugin-folder}/js"/>
<echo message="Concatenating: ${plugin-debugfile}"/>
<zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
<concatfiles>
<fileset dir="js" includes="**/*.js" />
</concatfiles>
</zConcat>
</then>
</if>
<!-- Concatenate plugin CSS files -->
<if>
<available file="${plugin-css-folder}" type="dir" />
<then>
<mkdir dir="${target-folder}/${plugin-folder}/${plugin-css-folder}"/>
<echo message="Concatenating: ${plugin-css-file}"/>
<zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-file}">
<concatfiles>
<fileset dir="${plugin-css-folder}" includes="**/*.css" />
</concatfiles>
</zConcat>
</then>
</if>
</target>
<!-- Preformat the Concatenated Javascript files to improve compilation -->
<target name="preformat" depends="concat">
<if>
<available file="${target-folder}/${plugin-folder}/js/${plugin-debugfile}" type="file" />
<then>
<echo message="Preformatting: ${plugin-debugfile}"/>
<replaceregexp byline="true">
<regexp pattern="(^[ ,\t]*\*[ ,\t]@.*)\{(.*)\[\]\}"/>
<substitution expression="\1{\2\|Array}"/>
<fileset dir="${target-folder}/${plugin-folder}/js" includes="${plugin-debugfile}"/>
</replaceregexp>
</then>
</if>
</target>
<!-- Compress JavaScript -->
<target name="compress" depends="preformat">
<if>
<available file="${target-folder}/${plugin-folder}/js/${plugin-debugfile}" type="file" />
<then>
<echo message="Compiling: ${plugin-debugfile}" />
<zCompile inputFolder="${target-folder}/${plugin-folder}/js" inputFile="${plugin-debugfile}" outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-file}">
<externs>
var Ext = {};
var Zarafa = {};
var container = {};
var _ = function(key, domain) {};
var dgettext = function(domain, msgid) {};
var dngettext = function(domain, msgid, msgid_plural, count) {};
var dnpgettext = function(domain, msgctxt, msgid, msgid_plural, count) {};
var dpgettext = function(domain, msgctxt, msgid) {};
var ngettext = function(msgid, msgid_plural, count) {};
var npgettext = function(msgctxt, msgid, msgid_plural, count) {};
var pgettext = function(msgctxt, msgid) {};
</externs>
</zCompile>
</then>
</if>
</target>
<!-- syntax check all PHP files -->
<target name="validate">
<if>
<available file="php" filepath="${env.PATH}" />
<then>
<if>
<available file="config.php" type="file" />
<then>
<antcall target="syntax-check">
<param name="file" value="config.php"/>
</antcall>
</then>
</if>
<if>
<available file="php" type="dir" />
<then>
<foreach target="syntax-check" param="file">
<path>
<fileset dir=".">
<include name="**/*.php"/>
</fileset>
</path>
</foreach>
</then>
</if>
</then>
<else>
<echo message="WARNING: PHP not available, not performing syntax-check on php files"/>
</else>
</if>
</target>
<target name="syntax-check">
<echo message="validating ${file}"/>
<exec executable="php" failonerror="true">
<arg value="-l"/>
<arg value="${file}"/>
</exec>
</target>
<!-- Install all files into the target folder -->
<target name="deploy" depends="compress, validate">
<mkdir dir="${target-folder}/${plugin-folder}"/>
<!-- Copy (and validate) manifest.xml -->
<if>
<available file="xmllint" filepath="${env.PATH}" />
<then>
<exec executable="xmllint" output="${target-folder}/${plugin-folder}/manifest.xml" failonerror="true" logError="true">
<arg value="--valid"/>
<arg value="--path"/>
<arg value="${server-folder}"/>
<arg value="manifest.xml"/>
</exec>
</then>
<else>
<echo message="WARNING: xmllint not available, not performing syntax-check on manifest.xml"/>
<!-- xmllint is not available, so we must copy the file manually -->
<copy todir="${target-folder}/${plugin-folder}">
<fileset dir=".">
<include name="manifest.xml"/>
</fileset>
</copy>
</else>
</if>
<!-- copy files -->
<copy todir="${target-folder}/${plugin-folder}">
<fileset dir=".">
<include name="resources/**/*.*"/>
<include name="php/**/*.php"/>
<include name="config.php"/>
<!-- exclude the ant script -->
<exclude name="build.xml"/>
<!-- CSS is generated during build -->
<exclude name="resources/css/*.*"/>
</fileset>
</copy>
</target>
</project>

5
config.php Executable file
View File

@ -0,0 +1,5 @@
<?php
define('PLUGIN_MDM_USER_DEFAULT_ENABLE_MDM', false);
define('PLUGIN_MDM_SERVER', 'localhost');
define('PLUGIN_MDM_SERVER_SSL', false);
?>

0
js/ABOUT.js Executable file
View File

50
js/MDM.js Executable file
View File

@ -0,0 +1,50 @@
Ext.namespace('Zarafa.plugins.mdm');
/**
* @class Zarafa.plugins.mdm.MDM
* @extends Zarafa.core.Plugin
*
* Plugin which lists all devices connected to a Kopano account with Z-Push.
* The user can wipe, resync, remove a device using buttons in the WebApp.
*/
Zarafa.plugins.mdm.MDM = Ext.extend(Zarafa.core.Plugin, {
/**
* Constructor
* @param {Object} config
* @protected
*/
constructor : function(config) {
config = config || {};
Zarafa.plugins.mdm.MDM.superclass.constructor.call(this, config);
},
/**
* Called after constructor.
* Registers insertion points.
* @protected
*/
initPlugin : function()
{
this.registerInsertionPoint('context.settings.categories', this.createSettingCategory, this);
Zarafa.plugins.mdm.MDM.superclass.initPlugin.apply(this, arguments);
},
/**
* Creates a category in settings for Z-Push
* @return {mdmsettingscategory}
*/
createSettingCategory: function() {
return [{
xtype : 'Zarafa.plugins.mdm.mdmsettingscategory'
}];
}
});
Zarafa.onReady(function() {
container.registerPlugin(new Zarafa.core.PluginMetaData({
name : 'mdm',
displayName : _('Mobile device management'),
pluginConstructor : Zarafa.plugins.mdm.MDM
}));
});

View File

@ -0,0 +1,32 @@
Ext.namespace('Zarafa.plugins.mdm.data');
/**
* @class Zarafa.plugins.mdm.data.JsonCertificateReader
* @extends Zarafa.core.data.JsonReader
*/
Zarafa.plugins.mdm.data.JsonCertificateReader = Ext.extend(Zarafa.core.data.JsonReader, {
/**
* @cfg {Zarafa.core.data.RecordCustomObjectType} customObjectType The custom object type
* which represents the {@link Ext.data.Record records} which should be created using
* {@link Zarafa.core.data.RecordFactory#createRecordObjectByCustomType}.
*/
customObjectType : Zarafa.core.data.RecordCustomObjectType.ZARAFA_MDM,
/**
* @constructor
* @param {Object} meta Metadata configuration options.
* @param {Object} recordType (optional) Optional Record type matches the type
* which must be read from response. If no type is given, it will use the
* record type for the {@link Zarafa.core.data.RecordCustomObjectType#ZARAFA_MDM}.
*/
constructor : function(meta, recordType)
{
meta = Ext.applyIf(meta || {}, {
dynamicRecord : false
});
recordType = Zarafa.core.data.RecordFactory.getRecordClassByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_MDM);
Zarafa.plugins.mdm.data.JsonCertificateReader.superclass.constructor.call(this, meta, recordType);
}
});

View File

@ -0,0 +1,33 @@
Ext.namespace('Zarafa.plugins.mdm');
Zarafa.plugins.mdm.data.MDMDeviceRecordFields = [
{name: 'entryid', type: 'string'},
{name: 'changed', type: 'boolean'},
{name: 'deviceos', type: 'string'},
{name: 'devicefriendlyname', type: 'string'},
{name: 'deviceinfo', type: 'string'},
{name: 'devicetype', type: 'string'},
{name: 'devicemodel', type: 'string'},
{name: 'domain', type: 'string'},
{name: 'hierarchyuuid', type: 'string'},
// ignoredmessages
{name: 'firstsynctime', type: 'date', dateFormat: 'timestamp'},
{name: 'lastupdatetime', type: 'date', dateFormat: 'timestamp'},
{name: 'wipestatus', type: 'string'},
{name: 'useragent', type: 'string'}
];
/**
*
*/
Zarafa.plugins.mdm.data.MDMDeviceRecord = Ext.extend(Zarafa.core.data.IPMRecord, {});
Zarafa.core.data.RecordCustomObjectType.addProperty('ZARAFA_MDM');
Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_MDM, Zarafa.plugins.mdm.data.MDMDeviceRecordFields);
Zarafa.core.data.RecordFactory.addListenerToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_MDM, 'createphantom', function(record)
{
// Phantom records must always be marked as opened (they contain the full set of data)
record.afterOpen();
});
Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_MDM, Zarafa.plugins.mdm.data.MDMDeviceRecord);

33
js/data/MDMDeviceStore.js Normal file
View File

@ -0,0 +1,33 @@
Ext.namespace('Zarafa.plugins.mdm.data');
/**
* @class Zarafa.plugins.mdm.data.MDMDeviceStore
* @extends Zarafa.core.data.ListModuleStore
* @xtype mdm.devicestore
* Store specific for MDM Plugin which creates {@link Zarafa.plugins.mdm.MDMDeviceRecord record}.
*/
Zarafa.plugins.mdm.data.MDMDeviceStore = Ext.extend(Zarafa.core.data.ListModuleStore, {
/**
* @constructor
* @param config Configuration object
*/
constructor : function(config)
{
config = config || {};
Ext.applyIf(config, {
autoLoad : true,
remoteSort: false,
reader : new Zarafa.plugins.mdm.data.JsonCertificateReader(),
writer : new Zarafa.core.data.JsonWriter(),
proxy : new Zarafa.core.data.IPMProxy({
listModuleName: 'pluginmdmmodule',
itemModuleName: 'pluginmdmmodule'
})
});
Zarafa.plugins.mdm.data.MDMDeviceStore.superclass.constructor.call(this, config);
}
});
Ext.reg('mdm.devicestore', Zarafa.plugins.mdm.data.MDMDeviceStore);

0
js/data/MDMRecord.js Normal file
View File

View File

@ -0,0 +1,74 @@
Ext.namespace('Zarafa.plugins.mdm.data');
/**
* @class Zarafa.plugins.mdm.data.MDMResponseHandler
* @extends Zarafa.core.data.AbstractResponseHandler
*
* MDM specific response handler.
*/
Zarafa.plugins.mdm.data.MDMResponseHandler = Ext.extend(Zarafa.core.data.AbstractResponseHandler, {
/**
* @cfg {Function} successCallback The function which
* will be called after success request.
*/
successCallback : Ext.emptyFn,
/**
* @cfg {Function} failureCallback The function which
* will be called after a failed request.
* This callback is optional and currently unused.
*/
failureCallback : Ext.emptyFn,
/**
* Device information from Z-Push's soap call,
* @param {Object} response Object contained the response data.
*/
doInfo : function(response) {
this.successCallback(response);
/*
if(response.status != true && this.failureCallback != null) {
this.failureCallback(response);
} else {
this.successCallback(response);
}*/
},
/**
* Call the successCallback callback function if device was successfully removed from
* z-push server.
* @param {Object} response Object contained the response data.
*/
doRemove : function(response)
{
if(response.remove){
this.successCallback();
}
},
/**
* If wipe request response was successful, show informative message.
* @param {Object} response Object contained the response data.
*/
doWipe : function(response) {
if (response.wipe === true) {
container.getNotifier().notify('info.mdm', _('Mobile Device Manager'), _('Wiping device'));
} else {
container.getNotifier().notify('info.mdm', _('Mobile Device Manager'), _('Password incorrect'));
}
},
/**
* If resync request response was successful, show informative message.
* @param {Object} response Object contained the response data.
*/
doResync : function(response) {
if (response.resync === true) {
container.getNotifier().notify('info.mdm', _('Mobile Device Manager'), _('Full resync in progress'));
}
}
});
Ext.reg('mdm.responsehandler', Zarafa.plugins.mdm.data.MDMResponseHandler);

View File

@ -0,0 +1,67 @@
Ext.namespace('Zarafa.plugins.mdm.data');
/**
* @class Zarafa.plugins.mdm.data.ProvisioningStatus
* @extends Zarafa.core.Enum
*
* @singleton
*/
Zarafa.plugins.mdm.data.ProvisioningStatus = Zarafa.core.Enum.create({
/**
* Denotes that the wipe is not applicable.
* @property
* @type Number
*/
'NOT_APPLICABLE' : 0,
/**
* Denotes that the wipe is ok.
* @property
* @type Number
*/
'OK' : 1,
/**
* Denotes that the wipe is pending.
* @property
* @type Number
*/
'WIPE_PENDING' : 2,
/**
* Denotes that the Wipe is requested.
* @property
* @type Number
*/
'WIPE_REQUESTED' : 4,
/**
* Denotes that the Wipe is executed.
* @property
* @type Number
*/
'WIPE_EXECUTED' : 8,
/**
* Return the display name for the given provisioning Status
* @param {Zarafa.plugins.mdm.js.data.ProvisioningStatus} provisioningStatus The given provisioning status
* @return {String} The display name for the provisioning status
*/
getDisplayName : function(provisioningStatus)
{
switch (provisioningStatus) {
case Zarafa.plugins.mdm.data.ProvisioningStatus.NOT_APPLICABLE:
return _('Not Applicable');
case Zarafa.plugins.mdm.data.ProvisioningStatus.OK:
return _('Ok');
case Zarafa.plugins.mdm.data.ProvisioningStatus.WIPE_PENDING:
return _('Wipe Pending');
case Zarafa.plugins.mdm.data.ProvisioningStatus.WIPE_REQUESTED:
return _('Wipe Requested');
case Zarafa.plugins.mdm.data.ProvisioningStatus.WIPE_EXECUTED:
return _('Wipe Executed');
}
return '';
}
});

View File

@ -0,0 +1,35 @@
Ext.namespace('Zarafa.plugins.mdm.settings');
/*
* Settings category entry for MDM
* @extends
*/
/**
* @class Zarafa.plugins.mdm.settings.MDMSettingsCategory
* @extends Zarafa.settings.ui.SettingsCategory
* @xtype Zarafa.plugins.mdmsettingscategory
*
* The mdm settings category entry.
*/
Zarafa.plugins.mdm.settings.MDMSettingsCategory = Ext.extend(Zarafa.settings.ui.SettingsCategory, {
/**
* @constructor
* @param {Object} config Configuration object
*/
constructor : function(config) {
config = config || {};
Ext.applyIf(config, {
title : _('MDM'),
iconCls : 'icon_mdm_settings',
items : [{
xtype : 'Zarafa.plugins.mdm.mdmsettingswidget'
}]
});
Zarafa.plugins.mdm.settings.MDMSettingsCategory.superclass.constructor.call(this, config);
}
});
Ext.reg('Zarafa.plugins.mdm.mdmsettingscategory', Zarafa.plugins.mdm.settings.MDMSettingsCategory);

View File

@ -0,0 +1,198 @@
Ext.namespace('Zarafa.plugins.mdm.settings');
/**
* @class Zarafa.plugins.mdm.settings.MDMSettingsWidget
* @extends Zarafa.settings.ui.SettingsWidget
*/
Zarafa.plugins.mdm.settings.MDMSettingsWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {
/**
* @constructor
* @param {Object} config Configuration object
*/
constructor : function(config)
{
config = config || {};
var store = new Zarafa.plugins.mdm.data.MDMDeviceStore();
Ext.applyIf(config, {
title : _('Mobile Devices'),
items : [{
xtype : 'container',
layout : 'column',
items : [{
xtype : 'grid',
name : _('Devices'),
ref : '../deviceGrid',
height : 400,
store : store,
viewConfig : {
forceFit : true,
deferEmptyText: false,
emptyText: '<div class="emptytext">' + _('No devices connected to your account') + '</div>'
},
columns : [{
dataIndex : 'devicetype',
header : _('Device'),
renderer : Ext.util.Format.htmlEncode
},{
dataIndex : 'useragent',
header : _('User Agent'),
renderer : Ext.util.Format.htmlEncode
},{
dataIndex : 'wipestatus',
header : _('Provisioning Status'),
renderer : Zarafa.plugins.mdm.ui.Renderers.provisioningStatus
},{
dataIndex : 'lastupdatetime',
header : _('Last Update'),
renderer : Ext.util.Format.htmlEncode
},{
dataIndex : 'entryid',
header : _('Device ID'),
renderer : Ext.util.Format.htmlEncode
},{
dataIndex : 'deviceos',
header : _('Device OS'),
hidden : true,
renderer : Ext.util.Format.htmlEncode
},{
dataIndex : 'devicefriendlyname',
header : _('Device Info'),
hidden : true,
renderer : Ext.util.Format.htmlEncode
},{
dataIndex : 'firstsynctime',
header : _('First Sync time'),
hidden : true,
renderer : Ext.util.Format.htmlEncode
}],
buttons : [{
text : _('Wipe Device'),
ref : '../../../wipeBtn',
handler : this.onWipeBtn,
scope : this
},{
text : _('Full resync'),
ref : '../../../resyncBtn',
handler : this.onFullResync,
scope : this
},{
text : _('Remove device'),
ref : '../../../removeBtn',
handler : this.onRemoveDevice,
scope : this
},{
text : _('Refresh'),
ref : '../../../refresh',
handler : this.onRefresh,
scope : this
}]
}]
}]
});
Zarafa.plugins.mdm.settings.MDMSettingsWidget.superclass.constructor.call(this, config);
},
/**
* Function which handles the click event on the "Wipe Device" button, displays
* a MessageBox for the user to confirm the wipe action. The wipe action is
* handled by the onWipeDevice function.
*/
onWipeBtn : function()
{
var msgbox = Ext.MessageBox.show({
title: _('Kopano WebApp'),
msg: _('Do you really want to wipe your device?\n Enter your password to confirm.'),
inputType :'password',
icon: Ext.MessageBox.WARNING,
buttons: Ext.MessageBox.YESNO,
fn: this.onWipeDevice,
prompt: true,
scope: this.deviceGrid
});
// ExtJS does not support a password field.
msgbox.getDialog().body.child('input').dom.type = 'password';
},
/**
* Function which handles the confirmation button in the "wipe device" messagebox.
* Sends an wipe request to PHP for the selected device in the grid.
*
* @param {Ext.Button} button button from the messagebox
* @param {String} password user password
*/
onWipeDevice : function(button, password)
{
if (button === 'yes') {
var selectionModel = this.getSelectionModel();
var record = selectionModel.getSelected();
if (record) {
container.getRequest().singleRequest(
'pluginmdmmodule',
'wipe',
{ 'deviceid' : record.get('entryid'), 'password': password },
new Zarafa.plugins.mdm.data.MDMResponseHandler()
);
}
}
},
/**
* Function which handles the click event on the "Full resync" button, sends a
* full resync request to PHP.
*/
onFullResync : function()
{
var selectionModel = this.deviceGrid.getSelectionModel();
var record = selectionModel.getSelected();
if(record) {
container.getRequest().singleRequest(
'pluginmdmmodule',
'resync',
{ 'deviceid' : record.get('entryid') },
new Zarafa.plugins.mdm.data.MDMResponseHandler()
);
}
},
/**
* Remove all state data for a device, essentially resyncing it.
*/
onRemoveDevice : function()
{
var selectionModel = this.deviceGrid.getSelectionModel();
var record = selectionModel.getSelected();
if(record) {
container.getRequest().singleRequest(
'pluginmdmmodule',
'remove',
{ 'deviceid' : record.get('entryid') },
new Zarafa.plugins.mdm.data.MDMResponseHandler({
successCallback : this.removeDone.createDelegate(this, [record], true)
})
);
}
},
/**
* Callback function triggers when device was successfully removed from the z-push server.
* we have to remove that device from {@link Zarafa.plugins.mdm.data.MDMDeviceStore store}.
* @param {Zarafa.plugins.mdm.data.MDMDeviceRecord} record {@link Zarafa.core.data.IPMRecord record} object
*/
removeDone : function(record)
{
record.getStore().remove(record);
},
/**
* Function which refreshes the store records from the server.
*/
onRefresh : function()
{
this.deviceGrid.getStore().load();
}
});
Ext.reg('Zarafa.plugins.mdm.mdmsettingswidget', Zarafa.plugins.mdm.settings.MDMSettingsWidget);

23
js/ui/Renderers.js Normal file
View File

@ -0,0 +1,23 @@
Ext.namespace('Zarafa.plugins.mdm.ui');
/**
* @class Zarafa.plugins.mdm.ui.Renderers
* Methods of this object can be used as renderers for grid panels, to render
* cells in custom format according to data type
* @singleton
*/
Zarafa.plugins.mdm.ui.Renderers = {
/**
* Render the Provisioning Status.
*
* @param {Object} value The data value for the cell.
* @param {Object} p An object with metadata
* @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
* @return {String} The formatted string
*/
provisioningStatus : function(value, p, record)
{
return Zarafa.plugins.mdm.data.ProvisioningStatus.getDisplayName(parseInt(value));
}
}

41
manifest.xml Executable file
View File

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<!DOCTYPE plugin SYSTEM "manifest.dtd">
<plugin version="2">
<info>
<version>0.1</version>
<name>mdm</name>
<title>Mobile Device Management</title>
<author>Kopano</author>
<authorURL>http://www.kopano.com</authorURL>
<description>Mobile Device Management plugin</description>
</info>
<config>
<configfile>config.php</configfile>
</config>
<components>
<component>
<files>
<server>
<serverfile>php/plugin.mdm.php</serverfile>
<serverfile type="module" module="pluginmdmmodule">php/class.pluginmdmmodule.php</serverfile>
</server>
<client>
<clientfile load="release">js/mdm.js</clientfile>
<clientfile load="debug">js/mdm-debug.js</clientfile>
<clientfile load="source">js/MDM.js</clientfile>
<clientfile load="source">js/data/MDMResponseHandler.js</clientfile>
<clientfile load="source">js/data/MDMDeviceRecord.js</clientfile>
<clientfile load="source">js/data/JsonDeviceReader.js</clientfile>
<clientfile load="source">js/data/MDMDeviceStore.js</clientfile>
<clientfile load="source">js/data/ProvisioningStatus.js</clientfile>
<clientfile load="source">js/settings/MDMSettingsWidget.js</clientfile>
<clientfile load="source">js/settings/MDMSettingsCategory.js</clientfile>
<clientfile load="source">js/ui/Renderers.js</clientfile>
</client>
<resources>
<resourcefile load="release">resources/css/mdm.css</resourcefile>
</resources>
</files>
</component>
</components>
</plugin>

207
php/class.pluginmdmmodule.php Executable file
View File

@ -0,0 +1,207 @@
<?php
// For backward compatibility we must check if the file exists
if ( file_exists(BASE_PATH . 'server/includes/core/class.encryptionstore.php') ) {
require_once(BASE_PATH . 'server/includes/core/class.encryptionstore.php');
}
/**
* PluginMDMModule Module
*/
class PluginMDMModule extends Module
{
private $server = '';
private $username = '';
private $password = '';
/**
* Constructor
* @param int $id unique id.
* @param array $data list of all actions.
*/
function PluginMDMModule($id, $data)
{
parent::Module($id, $data);
$this->server = (PLUGIN_MDM_SERVER_SSL ? 'https://' : 'http://') . PLUGIN_MDM_SERVER;
// For backward compatibility we will check if the Encryption store exists. If not,
// we will fall back to the old way of retrieving the password from the session.
if ( class_exists('EncryptionStore') ) {
// Get the username and password from the Encryption store
$encryptionStore = EncryptionStore::getInstance();
$this->username = $encryptionStore->get('username');
$this->password = $encryptionStore->get('password');
} else {
$this->username = $GLOBALS['mapisession']->getUserName();
$this->password = $_SESSION['password'];
if(function_exists('openssl_decrypt')) {
// In PHP 5.3.3 the iv parameter was added
if(version_compare(phpversion(), "5.3.3", "<")) {
$this->password = openssl_decrypt($this->password, "des-ede3-cbc", PASSWORD_KEY, 0);
} else {
$this->password = openssl_decrypt($this->password, "des-ede3-cbc", PASSWORD_KEY, 0, PASSWORD_IV);
}
}
}
$this->url = $this->server .'/Microsoft-Server-ActiveSync?Cmd=WebserviceDevice&DeviceId=webservice&DeviceType=webservice&User=' . $this->username;
}
/**
* Helper to setup a client.
*/
function getSoapClient()
{
return new SoapClient(null, array(
'location' => $this->url,
'uri' => $this->server,
'trace' => 1,
'login' => $this->username,
'password' => $this->password
));
}
/**
* Function which calls the soap call to do a full resync
* @param int $deviceid of phone which has to be resynced
* @return json $response object contains the response of the soap request from Z-Push
*/
function resyncDevice($deviceid)
{
$client = $this->getSoapClient();
return $client->ResyncDevice($deviceid);
}
/**
* Function which calls the wipeDevice soap call
* @param int $deviceid of phone which has to be wiped
* @param string $password user password
* @return json $response object contains the response of the soap request from Z-Push
*/
function wipeDevice($deviceid, $password)
{
if ($password == $this->password) {
$client = $this->getSoapClient();
return $client->WipeDevice($deviceid);
} else {
return false;
}
}
/**
* function which calls the ListDeviceDetails soap call
* @return array $response array contains a list of devices connected to the users account
*/
function getDevices()
{
$client = $this->getSoapClient();
return $client->ListDevicesDetails();
}
/**
function which calls the wipeDevice soap call
* @param int $deviceid of phone which has to be wiped
* @return json $response object contains the response of the soap request from Z-Push
*/
function removeDevice($deviceid)
{
$client = $this->getSoapClient();
return $client->RemoveDevice($deviceid);
}
/**
* Executes all the actions in the $data variable.
* @return boolean true on success of false on fialure.
*/
function execute()
{
foreach($this->data as $actionType => $actionData)
{
if(isset($actionType)) {
try {
switch($actionType)
{
case 'wipe':
$this->wipeDevice($actionData['deviceid'], $actionData['password']);
$this->addActionData('wipe', array(
'type' => 3,
'wipe' => $this->wipeDevice($actionData['deviceid'], $actionData['password'])
));
$GLOBALS['bus']->addData($this->getResponseData());
break;
case 'resync':
$this->addActionData('resync', array(
'type' => 3,
'resync' => $this->resyncDevice($actionData['deviceid'])
));
$GLOBALS['bus']->addData($this->getResponseData());
break;
case 'remove':
$this->addActionData('remove', array(
'type' => 3,
'remove' => $this->removeDevice($actionData['deviceid'])
));
$GLOBALS['bus']->addData($this->getResponseData());
break;
case 'list':
$items = array();
$date['page'] = array();
$rawData = $this->getDevices();
foreach($rawData as $device){
$device = $device->data;
$item = array();
$item['entryid'] = $device['deviceid'];
$item['changed'] = $device['changed'];
$item['deviceos'] = $device['deviceos'];
$item['devicefriendlyname'] = $device['devicefriendlyname'];
$item['devicetype'] = $device['devicetype'];
$item['devicemodel'] = $device['devicemodel'];
$item['hierarchyuuid'] = $device['hierarchyuuid'];
$item['firstsynctime'] = $device['firstsynctime'];
$item['lastupdatetime'] = $device['lastupdatetime'];
$item['wipestatus'] = $device['wipestatus'];
$item['useragent'] = $device['useragent'];
$item['domain'] = $device['domain'];
array_push($items, array('props' => $item));
}
$data['page']['start'] = 0;
$data['page']['rowcount'] = count($rawData);
$data['page']['totalrowcount'] = $data['page']['rowcount'];
$data = array_merge($data, array('item' => $items));
$this->addActionData('list', $data);
$GLOBALS['bus']->addData($this->getResponseData());
break;
default:
$this->handleUnknownActionType($actionType);
}
} catch (SoapFault $fault) {
$display_message = _('Something went wrong.');
if ($fault->faultcode === 'HTTP') {
if ($fault->getMessage() === "Unauthorized") {
$display_message = _('Unable to connect to Z-Push Server. Unauthorized.');
}
if ($fault->getMessage() === "Could not connect to host") {
$display_message = _('Unable to connect to Z-Push Server. Could not connect to host.');
}
if ($fault->getMessage() === "Not Found") {
$display_message = _('Unable to connect to Z-Push Server. Not found.');
}
} else if ($fault->faultcode === "ERROR") {
$display_message = _('Device ID could not be found');
}
$this->sendFeedback(false, array("type" => ERROR_GENERAL, "info" => array('display_message' => $display_message)));
}
catch (Exception $e) {
$this->sendFeedback(true, array("type" => ERROR_GENERAL, "info" => array('display_message' => _('Something went wrong'))));
}
}
}
}
};
?>

48
php/plugin.mdm.php Executable file
View File

@ -0,0 +1,48 @@
<?php
/**
* Handles plugin registration.
*/
class PluginMDM extends Plugin {
// Constructor
function PluginMDM(){}
/**
* Called to initialize the plugin and register for hooks.
*/
function init(){
$this->registerHook('server.core.settings.init.before');
}
/**
* Function is executed when a hook is triggered by the PluginManager
* @param String $eventID Identifier of the hook
* @param Array $data Reference to the data of the triggered hook
*/
function execute($eventID, &$data){
switch($eventID){
case 'server.core.settings.init.before':
$this->onBeforeSettingsInit($data);
break;
}
}
/**
* Called when the core Settings class is initialized and ready to accept sysadmin default
* settings. Registers the sysadmin defaults for the StatsLogging plugin.
* @param Array $data Reference to the data of the triggered hook
*/
function onBeforeSettingsInit(&$data){
$data['settingsObj']->addSysAdminDefaults(Array(
'zarafa' => Array(
'v1' => Array(
'plugins' => Array(
'mdm' => Array(
'enable' => PLUGIN_MDM_USER_DEFAULT_ENABLE_MDM,
)
)
)
)
));
}
}
?>

3
resources/css/mdm.css Executable file
View File

@ -0,0 +1,3 @@
.icon_mdm_settings {
background-image:url(../icons/mdm_icon.png) !important;
}

BIN
resources/icons/mdm_icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

BIN
resources/icons/mdm_icon.xcf Executable file

Binary file not shown.