From f675468e4489dfa64b50a3d2f19a9f50dad44dc6 Mon Sep 17 00:00:00 2001 From: mharindu Date: Mon, 4 Jul 2016 18:03:20 +0530 Subject: [PATCH 01/71] Added scopes to APIS --- .../service/impl/AndroidSenseService.java | 10 +++---- .../arduino/service/impl/ArduinoService.java | 8 ++--- .../service/impl/RaspberryPiService.java | 6 ++-- .../service/impl/VirtualFireAlarmService.java | 6 ++-- .../pom.xml | 4 +-- .../DeviceManagementAdminService.java | 30 +++++++++++++++++++ .../services/DeviceManagementService.java | 12 ++++++++ .../DeviceTypeConfigurationService.java | 9 ++++++ .../services/EventReceiverService.java | 8 +++++ .../pom.xml | 2 +- .../src/main/resources/p2.inf | 2 +- 11 files changed, 78 insertions(+), 19 deletions(-) diff --git a/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java b/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java index 8ba820d180..323afc19d6 100644 --- a/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java +++ b/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java @@ -45,7 +45,7 @@ public interface AndroidSenseService { @Path("device/{deviceId}/words") @POST @Feature(code = "keywords", name = "Add Keywords", description = "Send keywords to the device") - @Permission(scope = "android_sense_user", permissions = {"/permission/admin/device-mgt/user/operations"}) + @Permission(scope = "android_sense_user", roles = {"/permission/admin/device-mgt/user/operations"}) Response sendKeyWords(@PathParam("deviceId") String deviceId, @QueryParam("keywords") String keywords); /** @@ -57,13 +57,13 @@ public interface AndroidSenseService { @Path("device/{deviceId}/words/threshold") @POST @Feature(code = "threshold", name = "Add a Threshold", description = "Set a threshold for word in the device") - @Permission(scope = "android_sense_user", permissions = {"/permission/admin/device-mgt/user/operations"}) + @Permission(scope = "android_sense_user", roles = {"emm-user"}) Response sendThreshold(@PathParam("deviceId") String deviceId, @QueryParam("threshold") String threshold); @Path("device/{deviceId}/words") @DELETE @Feature(code = "remove", name = "Remove Keywords", description = "Remove the keywords") - @Permission(scope = "android_sense_user", permissions = {"/permission/admin/device-mgt/user/operations"}) + @Permission(scope = "android_sense_user", roles = {"emm-user"}) Response removeKeyWords(@PathParam("deviceId") String deviceId, @QueryParam("words") String words); /** @@ -72,7 +72,7 @@ public interface AndroidSenseService { @Path("stats/{deviceId}/sensors/{sensorName}") @GET @Consumes("application/json") - @Permission(scope = "android_sense_user", permissions = {"/permission/admin/device-mgt/user/stats"}) + @Permission(scope = "android_sense_user", roles = {"emm-user"}) @Produces("application/json") Response getAndroidSenseDeviceStats(@PathParam("deviceId") String deviceId, @PathParam("sensorName") String sensor, @QueryParam("from") long from, @QueryParam("to") long to); @@ -82,7 +82,7 @@ public interface AndroidSenseService { */ @Path("device/{device_id}/register") @POST - @Permission(scope = "android_sense_user", permissions = {"/permission/admin/device-mgt/user/devices"}) + @Permission(scope = "android_sense_user", roles = {"emm-user"}) Response register(@PathParam("device_id") String deviceId, @QueryParam("deviceName") String deviceName); } diff --git a/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java b/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java index afe0c89b1a..6f7480f37c 100644 --- a/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java +++ b/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java @@ -38,12 +38,12 @@ public interface ArduinoService { @Path("device/{deviceId}/bulb") @POST @Feature(code = "bulb", name = "Control Bulb", description = "Control Bulb on Arduino Uno") - @Permission(scope = "arduino_user", permissions = {"/permission/admin/device-mgt/user/operations"}) + @Permission(scope = "arduino_user", roles = {"emm-user"}) Response switchBulb(@PathParam("deviceId") String deviceId, @QueryParam("state") String state); @Path("device/{deviceId}/controls") @GET - @Permission(scope = "arduino_device", permissions = {"/permission/admin/device-mgt/user/operations"}) + @Permission(scope = "arduino_device", roles = {"emm-user"}) Response readControls(@PathParam("deviceId") String deviceId); /** @@ -53,7 +53,7 @@ public interface ArduinoService { @GET @Consumes("application/json") @Produces("application/json") - @Permission(scope = "arduino_user", permissions = {"/permission/admin/device-mgt/user/stats"}) + @Permission(scope = "arduino_user", roles = {"emm-user"}) Response getArduinoTemperatureStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, @QueryParam("to") long to); @@ -63,7 +63,7 @@ public interface ArduinoService { @Path("device/download") @GET @Produces("application/octet-stream") - @Permission(scope = "arduino_user", permissions = {"/permission/admin/device-mgt/user/devices"}) + @Permission(scope = "arduino_user", roles = {"emm-user"}) Response downloadSketch(@QueryParam("deviceName") String customDeviceName); } diff --git a/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java b/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java index 299f7be26f..1fc893ca2d 100644 --- a/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java +++ b/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java @@ -39,7 +39,7 @@ public interface RaspberryPiService { @Path("device/{deviceId}/bulb") @POST @Feature(code = "bulb", name = "Bulb On / Off", description = "Switch on/off Raspberry Pi agent's bulb. (On / Off)") - @Permission(scope = "raspberrypi_user", permissions = {"/permission/admin/device-mgt/user/operations"}) + @Permission(scope = "raspberrypi_user", roles = {"emm-user"}) Response switchBulb(@PathParam("deviceId") String deviceId, @QueryParam("state") String state); /** @@ -49,7 +49,7 @@ public interface RaspberryPiService { @GET @Consumes("application/json") @Produces("application/json") - @Permission(scope = "raspberrypi_user", permissions = {"/permission/admin/device-mgt/user/stats"}) + @Permission(scope = "raspberrypi_user", roles = {"emm-user"}) Response getRaspberryPiTemperatureStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, @QueryParam("to") long to); @@ -59,7 +59,7 @@ public interface RaspberryPiService { @Path("device/download") @GET @Produces(MediaType.APPLICATION_JSON) - @Permission(scope = "raspberrypi_user", permissions = {"/permission/admin/device-mgt/user/devices"}) + @Permission(scope = "raspberrypi_user", roles = {"emm-user"}) Response downloadSketch(@QueryParam("deviceName") String deviceName, @QueryParam("sketch_type") String sketchType); } diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java index 9f380dcdef..1be685927e 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java @@ -48,7 +48,7 @@ public interface VirtualFireAlarmService { */ @POST @Path("device/{deviceId}/buzz") - @Permission(scope = "virtual_firealarm_user", permissions = {"/permission/admin/device-mgt/user/operations"}) + @Permission(scope = "virtual_firealarm_user", roles = {"emm-user"}) @Feature(code = "buzz", name = "Buzzer On / Off", description = "Switch on/off Virtual Fire Alarm Buzzer. (On / Off)") Response switchBuzzer(@PathParam("deviceId") String deviceId, @FormParam("state") String state); @@ -58,7 +58,7 @@ public interface VirtualFireAlarmService { */ @Path("device/stats/{deviceId}") @GET - @Permission(scope = "virtual_firealarm_user", permissions = {"/permission/admin/device-mgt/user/stats"}) + @Permission(scope = "virtual_firealarm_user", roles = {"emm-user"}) @Consumes("application/json") @Produces("application/json") Response getVirtualFirealarmStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, @@ -67,7 +67,7 @@ public interface VirtualFireAlarmService { @Path("device/download") @GET @Produces("application/zip") - @Permission(scope = "virtual_firealarm_user", permissions = {"/permission/admin/device-mgt/user/devices"}) + @Permission(scope = "virtual_firealarm_user", roles = {"emm-user"}) Response downloadSketch(@QueryParam("deviceName") String deviceName, @QueryParam("sketchType") String sketchType); } diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml index 1258094d9b..46a66ae9bc 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml @@ -46,7 +46,7 @@ 2.2 WEB-INF/lib/*cxf*.jar - api#device-mgt#android#v1.0 + api-device-mgt-android-v1.0 @@ -72,7 +72,7 @@ - + diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java index 3e504e52c3..8291a10564 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java @@ -19,6 +19,8 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Permission; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; import org.wso2.carbon.mdm.services.android.bean.wrapper.*; @@ -30,6 +32,10 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.List; +@API(name = "Android Device Management Administrative Service", version = "1.0.0", + context = "api-device-mgt-android-v1.0/admin/devices", + tags = {"devicemgt_android"}) + @Path("/admin/devices") @Api(value = "Android Device Management Administrative Service", description = "Device management related admin APIs.") @Produces(MediaType.APPLICATION_JSON) @@ -85,6 +91,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new lock operation.") }) + @Permission(scope = "device:android:lock", roles = {"admin"}) Response configureDeviceLock( @ApiParam(name = "deviceLockBeanWrapper", value = "Device lock configurations with device IDs") DeviceLockBeanWrapper deviceLockBeanWrapper); @@ -138,6 +145,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new un-lock operation.") }) + @Permission(scope = "device:android:unlock", roles = {"admin"}) Response configureDeviceUnlock( @ApiParam(name = "deviceIDs", value = "DeviceIds to be enable device unlock operation") List deviceIDs); @@ -190,6 +198,7 @@ public interface DeviceManagementAdminService { code = 500, message = "Internal Server Error. \n " + "Server error occurred while adding a new get-location operation.")}) + @Permission(scope = "device:android:location", roles = {"admin"}) Response getDeviceLocation( @ApiParam(name = "deviceIDs", value = "DeviceIDs to be requested to get device location") List deviceIDs); @@ -242,6 +251,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new clear password operation.") }) + @Permission(scope = "device:android:clear-password", roles = {"admin"}) Response removePassword( @ApiParam(name = "deviceIDs", value = "DeviceIds to be requested to remove password") List deviceIDs); @@ -294,6 +304,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new control camera operation.") }) + @Permission(scope = "device:android:camera", roles = {"admin"}) Response configureCamera( @ApiParam(name = "cameraBeanWrapper", value = "Camera enable/disable configurations with device IDs") CameraBeanWrapper cameraBeanWrapper); @@ -349,6 +360,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new device info operation.") }) + @Permission(scope = "device:android:info", roles = {"admin"}) Response getDeviceInformation( @ApiParam(name = "deviceIds", value = "Device IDs to be requested to get device information") List deviceIDs); @@ -402,6 +414,7 @@ public interface DeviceManagementAdminService { code = 500, message = "Internal Server Error. \n " + "Server error occurred while adding a enterprise wipe operation.")}) + @Permission(scope = "device:android:enterprise-wipe", roles = {"admin"}) Response wipeDevice(@ApiParam(name = "deviceIDs", value = "Device IDs to be requested to do enterprise-wipe") List deviceIDs); @@ -453,6 +466,7 @@ public interface DeviceManagementAdminService { code = 500, message = "Internal Server Error. \n " + "Server error occurred while adding a device wipe operation.")}) + @Permission(scope = "device:android:wipe", roles = {"admin"}) Response wipeData( @ApiParam(name = "wipeDataBeanWrapper", value = "Configurations and DeviceIds needed to do wipe-data") WipeDataBeanWrapper wipeDataBeanWrapper); @@ -509,6 +523,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new get-applications operation.") }) + @Permission(scope = "device:android:get-applications", roles = {"admin"}) Response getApplications( @ApiParam(name = "deviceIDs", value = "Device Ids needed to get applications that are already installed") List deviceIDs); @@ -561,6 +576,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new device ring operation.") }) + @Permission(scope = "device:android:ring", roles = {"admin"}) Response ringDevice( @ApiParam(name = "deviceIDs", value = "Device Ids needed for ring") List deviceIDs); @@ -612,6 +628,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new device reboot operation.") }) + @Permission(scope = "device:android:reboot", roles = {"admin"}) Response rebootDevice( @ApiParam(name = "deviceIDs", value = "Device Ids needed for reboot.") List deviceIDs); @@ -663,6 +680,7 @@ public interface DeviceManagementAdminService { "Server error occurred while adding a new device mute operation.") }) @Path("/mute") + @Permission(scope = "device:android:mute", roles = {"admin"}) Response muteDevice( @ApiParam(name = "deviceIDs", value = "DeviceIDs need to be muted") List deviceIDs); @@ -717,6 +735,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new install-application operation.") }) + @Permission(scope = "device:android:install-application", roles = {"admin"}) Response installApplication( @ApiParam(name = "applicationInstallationBeanWrapper", value = "Properties of installed apps and device IDs") ApplicationInstallationBeanWrapper applicationInstallationBeanWrapper); @@ -772,6 +791,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new update-application operation.") }) + @Permission(scope = "device:android:update-application", roles = {"admin"}) Response updateApplication( @ApiParam(name = "applicationUpdateBeanWrapper", value = "Properties of updated apps and device IDs") ApplicationUpdateBeanWrapper applicationUpdateBeanWrapper); @@ -824,6 +844,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new uninstall-application operation.") }) + @Permission(scope = "device:android:uninstall-application", roles = {"admin"}) Response uninstallApplication( @ApiParam(name = "applicationUninstallationBeanWrapper", value = "applicationUninstallationConfigs and Device Ids") @@ -878,6 +899,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new blacklist-applications operation.") }) + @Permission(scope = "device:android:blacklist-application", roles = {"admin"}) Response blacklistApplications( @ApiParam(name = "blacklistApplicationsBeanWrapper", value = "BlacklistApplications " + "Configuration and DeviceIds") @@ -932,6 +954,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new upgrade firmware operation.") }) + @Permission(scope = "device:android:upgrade-firmware", roles = {"admin"}) Response upgradeFirmware( @ApiParam(name = "upgradeFirmwareBeanWrapper", value = "Firmware upgrade configuration and DeviceIds") @@ -986,6 +1009,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new configure VPN operation.") }) + @Permission(scope = "device:android:vpn", roles = {"admin"}) Response configureVPN( @ApiParam(name = "vpnBeanWrapper", value = "VPN configuration and DeviceIds") @@ -1039,6 +1063,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new send notification operation.") }) + @Permission(scope = "device:android:send-notification", roles = {"admin"}) Response sendNotification( @ApiParam(name = "notificationBeanWrapper", value = "Notification Configurations and device Ids") @@ -1092,6 +1117,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new configure wifi operation.") }) + @Permission(scope = "device:android:wifi", roles = {"admin"}) Response configureWifi( @ApiParam(name = "wifiBeanWrapper", value = "WifiConfigurations and Device Ids") WifiBeanWrapper wifiBeanWrapper); @@ -1144,6 +1170,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new encrypt storage operation.") }) + @Permission(scope = "device:android:encrypt", roles = {"admin"}) Response encryptStorage( @ApiParam(name = "encryptionBeanWrapper", value = "Configurations and deviceIds need to be done data encryption") @@ -1197,6 +1224,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new change lock code operation.") }) + @Permission(scope = "device:android:change-lock-code", roles = {"admin"}) Response changeLockCode( @ApiParam(name = "lockCodeBeanWrapper", value = "Configurations and device Ids need to be done change lock code") @@ -1250,6 +1278,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new set password policy operation.") }) + @Permission(scope = "device:android:set-password-policy", roles = {"admin"}) Response setPasswordPolicy( @ApiParam(name = "passwordPolicyBeanWrapper", value = "Password Policy Configurations and Device Ids") @@ -1303,6 +1332,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new set webclip operation.") }) + @Permission(scope = "device:android:webclip", roles = {"admin"}) Response setWebClip( @ApiParam(name = "webClipBeanWrapper", value = "Configurations to need set web clip on device and device Ids") diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java index 4464d6c734..f787cd5b5a 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java @@ -19,6 +19,8 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Permission; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.app.mgt.Application; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; @@ -28,6 +30,10 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.List; +@API(name = "Android Device Management", version = "1.0.0", + context = "api-device-mgt-android-v1.0/devices", + tags = {"devicemgt_android"}) + @Api(value = "Android Device Management", description = "This carries all the resources related to Android device management functionalities") @Path("/devices") @@ -77,6 +83,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while updating the application list.") }) + @Permission(scope = "device:android:enroll", roles = {"admin"}) Response updateApplicationList( @ApiParam( name = "id", @@ -127,6 +134,7 @@ public interface DeviceManagementService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching policies.") }) + @Permission(scope = "device:android:enroll", roles = {"admin"}) Response getPendingOperations( @ApiParam( name = "id", @@ -191,6 +199,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while adding a new policy.") }) + @Permission(scope = "device:android:enroll", roles = {"admin"}) Response enrollDevice(@ApiParam(name = "device", value = "Device Information to be enroll") Device device); @GET @@ -228,6 +237,7 @@ public interface DeviceManagementService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching the enrollment status of the Android device.") }) + @Permission(scope = "device:android:enroll", roles = {"admin"}) Response isEnrolled( @ApiParam( name = "id", @@ -281,6 +291,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while updating the device enrollment.") }) + @Permission(scope = "device:android:enroll", roles = {"admin"}) Response modifyEnrollment( @ApiParam( name = "id", @@ -310,6 +321,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while dis-enrolling the device.") }) + @Permission(scope = "device:android:enroll", roles = {"admin"}) Response disEnrollDevice( @ApiParam( name = "id", diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java index 7d74b700d5..d3b9156d0a 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java @@ -19,6 +19,8 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Permission; import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration; import org.wso2.carbon.mdm.services.android.exception.AndroidAgentException; @@ -26,6 +28,10 @@ import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +@API(name = "Android Configuration Management", version = "1.0.0", + context = "api-device-mgt-android-v1.0/configuration", + tags = {"devicemgt_android"}) + @Api(value = "Android Configuration Management", description = "This API carries all resource associated with " + "manipulating the general configurations of Android platform") @Path("/configuration") @@ -73,6 +79,7 @@ public interface DeviceTypeConfigurationService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching Android platform configuration.") }) + @Permission(scope = "configuration:view", roles = {"admin"}) Response getConfiguration( @ApiParam( name = "If-Modified-Since", @@ -122,6 +129,7 @@ public interface DeviceTypeConfigurationService { message = "Internal Server Error. \n " + "Server error occurred while modifying Android platform configuration.") }) + @Permission(scope = "configuration:modify", roles = {"admin"}) Response updateConfiguration( @ApiParam(name = "configuration", value = "AndroidPlatformConfiguration") @@ -169,6 +177,7 @@ public interface DeviceTypeConfigurationService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching Android license configuration.") }) + @Permission(scope = "device:android:enroll", roles = {"admin"}) Response getLicense( @ApiParam( name = "If-Modified-Since", diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java index c488440906..24c4fbe545 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java @@ -19,6 +19,8 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Permission; import org.wso2.carbon.mdm.services.android.bean.DeviceState; import org.wso2.carbon.mdm.services.android.bean.wrapper.EventBeanWrapper; @@ -26,6 +28,10 @@ import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +@API(name = "Android Event Receiver", version = "1.0.0", + context = "api-device-mgt-android-v1.0/configuration", + tags = {"devicemgt_android"}) + @Api(value = "Event Receiver", description = "Event publishing/retrieving related APIs.To enable Eventing need to" + " configure as ref-https://docs.wso2.com/display/EMM210/Managing+Event+Publishing+with+WSO2+Data+Analytics+Server, " + "https://docs.wso2.com/display/EMM210/Creating+a+New+Event+Stream+and+Receiver") @@ -82,6 +88,7 @@ public interface EventReceiverService { message = "Internal Server Error. \n " + "Server error occurred while publishing events.") }) + @Permission(scope = "device:android:event:publish", roles = {"admin"}) Response publishEvents( @ApiParam( name = "eventBeanWrapper", @@ -130,6 +137,7 @@ public interface EventReceiverService { code = 500, message = "Error occurred while getting published events for specific device.") }) + @Permission(scope = "device:android:event:view", roles = {"admin"}) Response retrieveAlerts( @ApiParam( name = "id", diff --git a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml index 581ca9d104..ce76f7e12f 100644 --- a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml +++ b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml @@ -118,7 +118,7 @@ true ${project.build.directory}/maven-shared-archive-resources/webapps/ - api#device-mgt#android#v1.0.war + api-device-mgt-android-v1.0.war diff --git a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf index 3b9d91aec8..86e299d018 100644 --- a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf +++ b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf @@ -1,5 +1,5 @@ instructions.configure = \ -org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/webapps/api#device-mgt#android#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#device-mgt#android#v1.0.war,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/webapps/api-device-mgt-android-v1.0.war,target:${installFolder}/../../deployment/server/webapps/api-device-mgt-android-v1.0.war,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/jaggeryapps/,target:${installFolder}/../../deployment/server/jaggeryapps/,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/conf/mobile-config.xml,target:${installFolder}/../../conf/mobile-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/dbscripts/plugins/,target:${installFolder}/../../../dbscripts/cdm/plugins/android,overwrite:true);\ From 48613018ab8bf90bf961a65b52c21fa7af950bba Mon Sep 17 00:00:00 2001 From: mharindu Date: Mon, 4 Jul 2016 19:27:46 +0530 Subject: [PATCH 02/71] Fixed issues in scope based autorization implementation --- .../services/android/services/DeviceManagementAdminService.java | 2 +- .../mdm/services/android/services/EventReceiverService.java | 2 +- .../src/main/webapp/WEB-INF/web.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java index 8291a10564..369b87517e 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java @@ -899,7 +899,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new blacklist-applications operation.") }) - @Permission(scope = "device:android:blacklist-application", roles = {"admin"}) + @Permission(scope = "device:android:blacklist-applications", roles = {"admin"}) Response blacklistApplications( @ApiParam(name = "blacklistApplicationsBeanWrapper", value = "BlacklistApplications " + "Configuration and DeviceIds") diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java index 24c4fbe545..a9f0e249e4 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java @@ -29,7 +29,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @API(name = "Android Event Receiver", version = "1.0.0", - context = "api-device-mgt-android-v1.0/configuration", + context = "api-device-mgt-android-v1.0/events", tags = {"devicemgt_android"}) @Api(value = "Event Receiver", description = "Event publishing/retrieving related APIs.To enable Eventing need to" + diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/web.xml b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/web.xml index 48eeb393eb..b39cda6735 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/web.xml +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/web.xml @@ -49,7 +49,7 @@ managed-api-enabled - false + true managed-api-owner From 6b939714d8b842952c5c1a34c968986d3d520bd1 Mon Sep 17 00:00:00 2001 From: mharindu Date: Tue, 12 Jul 2016 01:32:15 +0530 Subject: [PATCH 03/71] Changed context paths --- .../src/main/webapp/WEB-INF/cxf-servlet.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/cxf-servlet.xml b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/cxf-servlet.xml index f6095bd023..5423cba719 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/cxf-servlet.xml +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/WEB-INF/cxf-servlet.xml @@ -51,7 +51,7 @@ - + From 302f71b6d197457906b2f8bd747952c38162e949 Mon Sep 17 00:00:00 2001 From: mharindu Date: Mon, 18 Jul 2016 20:17:31 +0530 Subject: [PATCH 04/71] Fixed webapp name resolving issue and changed API context --- .../org.wso2.carbon.device.mgt.mobile.android.api/pom.xml | 4 ++-- .../android/services/DeviceManagementAdminService.java | 2 +- .../services/android/services/DeviceManagementService.java | 2 +- .../android/services/DeviceTypeConfigurationService.java | 2 +- .../mdm/services/android/services/EventReceiverService.java | 2 +- .../org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml | 2 +- .../src/main/resources/p2.inf | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml index dbd9472121..fa45d4c24c 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/pom.xml @@ -46,7 +46,7 @@ 2.2 WEB-INF/lib/*cxf*.jar - api-device-mgt-android-v1.0 + api#device-mgt#android#v1.0 @@ -72,7 +72,7 @@ - + diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java index e7e8fdc8b1..6f3884a8a0 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java @@ -33,7 +33,7 @@ import javax.ws.rs.core.Response; import java.util.List; @API(name = "Android Device Management Administrative Service", version = "1.0.0", - context = "api-device-mgt-android-v1.0/admin/devices", + context = "api/device-mgt/android/v1.0/admin/devices", tags = {"devicemgt_android"}) @Path("/admin/devices") diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java index f627e5f0f7..2aaaee1c7d 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java @@ -35,7 +35,7 @@ import javax.ws.rs.core.Response; import java.util.List; @API(name = "Android Device Management", version = "1.0.0", - context = "api-device-mgt-android-v1.0/devices", + context = "api/device-mgt/android/v1.0/devices", tags = {"devicemgt_android"}) @Api(value = "Android Device Management", diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java index d5af40fe1f..9a9012bb13 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java @@ -31,7 +31,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @API(name = "Android Configuration Management", version = "1.0.0", - context = "api-device-mgt-android-v1.0/configuration", + context = "api/device-mgt/android/v1.0/configuration", tags = {"devicemgt_android"}) @Api(value = "Android Configuration Management", description = "This API carries all resource associated with " + diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java index fdfa387995..eb6e270edc 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java @@ -32,7 +32,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @API(name = "Android Event Receiver", version = "1.0.0", - context = "api-device-mgt-android-v1.0/events", + context = "api/device-mgt/android/v1.0/events", tags = {"devicemgt_android"}) @Api(value = "Event Receiver", description = "Event publishing/retrieving related APIs.To enable Eventing need to" + diff --git a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml index 6419ef5712..a86fa94aeb 100644 --- a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml +++ b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/pom.xml @@ -118,7 +118,7 @@ true ${project.build.directory}/maven-shared-archive-resources/webapps/ - api-device-mgt-android-v1.0.war + api#device-mgt#android#v1.0.war diff --git a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf index 86e299d018..3b9d91aec8 100644 --- a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf +++ b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/p2.inf @@ -1,5 +1,5 @@ instructions.configure = \ -org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/webapps/api-device-mgt-android-v1.0.war,target:${installFolder}/../../deployment/server/webapps/api-device-mgt-android-v1.0.war,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/webapps/api#device-mgt#android#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#device-mgt#android#v1.0.war,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/jaggeryapps/,target:${installFolder}/../../deployment/server/jaggeryapps/,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/conf/mobile-config.xml,target:${installFolder}/../../conf/mobile-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.android_${feature.version}/dbscripts/plugins/,target:${installFolder}/../../../dbscripts/cdm/plugins/android,overwrite:true);\ From d70c576ff0a1dfdec89c8e1e89beea4374dda57d Mon Sep 17 00:00:00 2001 From: ayyoob Date: Wed, 3 Aug 2016 17:23:09 +0530 Subject: [PATCH 05/71] refactored android configuration --- .../units/iot.unit.policy.wizard/wizard.js | 3 +- .../public/js/platform-configuration.js | 10 - .../src/main/webapp/META-INF/permissions.xml | 2 +- .../configuration.hbs | 92 ++ .../configuration.json | 3 + .../public/js/platform-configuration.js | 250 +++++ .../configuration.hbs | 501 ---------- .../configuration.js | 9 - .../configuration.json | 4 - .../public/js/platform-configuration.js | 878 ------------------ .../units/mdm.unit.policy.wizard/wizard.js | 3 +- .../src/main/webapp/META-INF/permissions.xml | 6 +- .../configuration.hbs | 56 ++ .../configuration.json | 3 + .../public/js/platform-configuration.js | 226 +++++ 15 files changed, 638 insertions(+), 1408 deletions(-) create mode 100644 components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs create mode 100644 components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.json create mode 100644 components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js delete mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.hbs delete mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.js delete mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.json delete mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/public/js/platform-configuration.js create mode 100644 components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs create mode 100644 components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.json create mode 100644 components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.ui/src/main/resources/jaggeryapps/devicemgt/app/units/iot.unit.policy.wizard/wizard.js b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.ui/src/main/resources/jaggeryapps/devicemgt/app/units/iot.unit.policy.wizard/wizard.js index 49c02f84a8..1404144fef 100644 --- a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.ui/src/main/resources/jaggeryapps/devicemgt/app/units/iot.unit.policy.wizard/wizard.js +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.ui/src/main/resources/jaggeryapps/devicemgt/app/units/iot.unit.policy.wizard/wizard.js @@ -23,6 +23,7 @@ function onRequest(context) { var DTYPE_CONF_DEVICE_TYPE_LABEL_KEY = "label"; var userModule = require("/app/modules/user.js")["userModule"]; + var deviceModule = require("/app/modules/device.js").deviceModule; var utility = require('/app/modules/utility.js').utility; var response = userModule.getRoles(); var wizardPage = {}; @@ -30,7 +31,7 @@ function onRequest(context) { wizardPage["roles"] = response["content"]; } var deviceType = context.uriParams.deviceType; - var typesListResponse = userModule.getPlatforms(); + var typesListResponse = deviceModule.getDeviceTypes(); if (typesListResponse["status"] == "success") { for (var type in typesListResponse["content"]) { if (deviceType == typesListResponse["content"][type]["name"]) { diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.virtual_firealarm.platform.configuration/public/js/platform-configuration.js b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.virtual_firealarm.platform.configuration/public/js/platform-configuration.js index 7641365479..57013c1a3e 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.virtual_firealarm.platform.configuration/public/js/platform-configuration.js +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.virtual_firealarm.platform.configuration/public/js/platform-configuration.js @@ -39,16 +39,6 @@ $(document).ready(function () { }); - -// Start of HTML embedded invoke methods -var showAdvanceOperation = function (operation, button) { - $(button).addClass('selected'); - $(button).siblings().removeClass('selected'); - var hiddenOperation = ".wr-hidden-operations-content > div"; - $(hiddenOperation + '[data-operation="' + operation + '"]').show(); - $(hiddenOperation + '[data-operation="' + operation + '"]').siblings().hide(); -}; - // Start of HTML embedded invoke methods var addConfiguration = function () { var errorMsgWrapper = "#virtual_firelarm-config-error-msg"; diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/META-INF/permissions.xml b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/META-INF/permissions.xml index a2e3b1e1dc..61aa4a1ae5 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/META-INF/permissions.xml +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/webapp/META-INF/permissions.xml @@ -43,7 +43,7 @@ Add Tenant configuration /device-mgt/admin/platform-configs/add /configuration - POST + PUT diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs new file mode 100644 index 0000000000..3e598fb0a5 --- /dev/null +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs @@ -0,0 +1,92 @@ +{{! + Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + + WSO2 Inc. licenses this file to you under the Apache License, + Version 2.0 (the "License"); you may not use this file except + in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +}} + +
+
+ +

+ Communication Protocol Configuration +
+

+
+ + +
+ +
+
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+

+ End User License Agreement ( EULA ) +
+

+ +
+ +
+
+ +
+
+
+ +{{#zone "bottomJs"}} + {{js "js/platform-configuration.js"}} +{{/zone}} diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.json b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.json new file mode 100644 index 0000000000..fd25901297 --- /dev/null +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.json @@ -0,0 +1,3 @@ +{ + "version" : "1.0.0" +} \ No newline at end of file diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js new file mode 100644 index 0000000000..01fb2e12c9 --- /dev/null +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Checks if provided input is valid against RegEx input. + * + * @param regExp Regular expression + * @param inputString Input string to check + * @returns {boolean} Returns true if input matches RegEx + */ +function isPositiveInteger(str) { + return /^\+?(0|[1-9]\d*)$/.test(str); +} + +var notifierTypeConstants = { + "LOCAL": "1", + "GCM": "2" +}; +// Constants to define platform types available +var platformTypeConstants = { + "ANDROID": "android", + "IOS": "ios", + "WINDOWS": "windows" +}; + +var responseCodes = { + "CREATED": "Created", + "SUCCESS": "201", + "INTERNAL_SERVER_ERROR": "Internal Server Error" +}; + +var configParams = { + "NOTIFIER_TYPE": "notifierType", + "NOTIFIER_FREQUENCY": "notifierFrequency", + "GCM_API_KEY": "gcmAPIKey", + "GCM_SENDER_ID": "gcmSenderId", + "ANDROID_EULA": "androidEula", + "IOS_EULA": "iosEula", + "CONFIG_COUNTRY": "configCountry", + "CONFIG_STATE": "configState", + "CONFIG_LOCALITY": "configLocality", + "CONFIG_ORGANIZATION": "configOrganization", + "CONFIG_ORGANIZATION_UNIT": "configOrganizationUnit", + "MDM_CERT_PASSWORD": "MDMCertPassword", + "MDM_CERT_TOPIC_ID": "MDMCertTopicID", + "APNS_CERT_PASSWORD": "APNSCertPassword", + "MDM_CERT": "MDMCert", + "MDM_CERT_NAME": "MDMCertName", + "APNS_CERT": "APNSCert", + "APNS_CERT_NAME": "APNSCertName", + "ORG_DISPLAY_NAME": "organizationDisplayName", + "GENERAL_EMAIL_HOST": "emailHost", + "GENERAL_EMAIL_PORT": "emailPort", + "GENERAL_EMAIL_USERNAME": "emailUsername", + "GENERAL_EMAIL_PASSWORD": "emailPassword", + "GENERAL_EMAIL_SENDER_ADDRESS": "emailSender", + "GENERAL_EMAIL_TEMPLATE": "emailTemplate", + "COMMON_NAME": "commonName", + "KEYSTORE_PASSWORD": "keystorePassword", + "PRIVATE_KEY_PASSWORD": "privateKeyPassword", + "BEFORE_EXPIRE": "beforeExpire", + "AFTER_EXPIRE": "afterExpire", + "WINDOWS_EULA": "windowsLicense", + "IOS_CONFIG_MDM_MODE": "iOSConfigMDMMode", + "IOS_CONFIG_APNS_MODE": "iOSConfigAPNSMode" +}; + +$(document).ready(function () { + $("#gcm-inputs").hide(); + tinymce.init({ + selector: "textarea", + height:500, + theme: "modern", + plugins: [ + "autoresize", + "advlist autolink lists link image charmap print preview anchor", + "searchreplace visualblocks code fullscreen", + "insertdatetime image table contextmenu paste" + ], + toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image" + }); + + var androidConfigAPI = "/api/device-mgt/android/v1.0/configuration"; + + /** + * Following requests would execute + * on page load event of platform configuration page in WSO2 EMM Console. + * Upon receiving the response, the parameters will be set to the fields, + * in case those configurations are already set. + */ + + invokerUtil.get( + androidConfigAPI, + function (data) { + data = JSON.parse(data); + if (data != null && data.configuration != null) { + for (var i = 0; i < data.configuration.length; i++) { + var config = data.configuration[i]; + if (config.name == configParams["NOTIFIER_TYPE"]) { + $("#android-config-notifier").val(config.value); + if (config.value != notifierTypeConstants["GCM"]) { + $("#gcm-inputs").hide(); + $("#local-inputs").show(); + } else { + $("#gcm-inputs").show(); + $("#local-inputs").hide(); + } + } else if (config.name == configParams["NOTIFIER_FREQUENCY"]) { + $("input#android-config-notifier-frequency").val(config.value / 1000); + } else if (config.name == configParams["GCM_API_KEY"]) { + $("input#android-config-gcm-api-key").val(config.value); + } else if (config.name == configParams["GCM_SENDER_ID"]) { + $("input#android-config-gcm-sender-id").val(config.value); + } else if (config.name == configParams["ANDROID_EULA"]) { + $("#android-eula").val(config.value); + } + } + } + }, function (data) { + console.log(data); + }); + + $("select.select2[multiple=multiple]").select2({ + tags: true + }); + + $("#android-config-notifier").change(function () { + var notifierType = $("#android-config-notifier").find("option:selected").attr("value"); + if (notifierType != notifierTypeConstants["GCM"]) { + $("#gcm-inputs").hide(); + $("#local-inputs").show(); + } else { + $("#local-inputs").hide(); + $("#gcm-inputs").show(); + } + }); + + /** + * Following click function would execute + * when a user clicks on "Save" button + * on Android platform configuration page in WSO2 EMM Console. + */ + $("button#save-android-btn").click(function () { + var notifierType = $("#android-config-notifier").find("option:selected").attr("value"); + var notifierFrequency = $("input#android-config-notifier-frequency").val(); + var gcmAPIKey = $("input#android-config-gcm-api-key").val(); + var gcmSenderId = $("input#android-config-gcm-sender-id").val(); + var androidLicense = tinyMCE.activeEditor.getContent(); + alert(androidLicense); + var errorMsgWrapper = "#android-config-error-msg"; + var errorMsg = "#android-config-error-msg span"; + if (notifierType == notifierTypeConstants["LOCAL"] && !notifierFrequency) { + $(errorMsg).text("Notifier frequency is a required field. It cannot be empty."); + $(errorMsgWrapper).removeClass("hidden"); + } else if (notifierType == notifierTypeConstants["LOCAL"] && !isPositiveInteger(notifierFrequency)) { + $(errorMsg).text("Provided notifier frequency is invalid. "); + $(errorMsgWrapper).removeClass("hidden"); + } else if (notifierType == notifierTypeConstants["GCM"] && !gcmAPIKey) { + $(errorMsg).text("GCM API Key is a required field. It cannot be empty."); + $(errorMsgWrapper).removeClass("hidden"); + } else if (notifierType == notifierTypeConstants["GCM"] && !gcmSenderId) { + $(errorMsg).text("GCM Sender ID is a required field. It cannot be empty."); + $(errorMsgWrapper).removeClass("hidden"); + } else { + + var addConfigFormData = {}; + var configList = new Array(); + + var type = { + "name": configParams["NOTIFIER_TYPE"], + "value": notifierType, + "contentType": "text" + }; + + var frequency = { + "name": configParams["NOTIFIER_FREQUENCY"], + "value": String(notifierFrequency * 1000), + "contentType": "text" + }; + + var gcmKey = { + "name": configParams["GCM_API_KEY"], + "value": gcmAPIKey, + "contentType": "text" + }; + + var gcmId = { + "name": configParams["GCM_SENDER_ID"], + "value": gcmSenderId, + "contentType": "text" + }; + + var androidEula = { + "name": configParams["ANDROID_EULA"], + "value": androidLicense, + "contentType": "text" + }; + + configList.push(type); + configList.push(frequency); + configList.push(androidEula); + if (notifierType == notifierTypeConstants["GCM"]) { + configList.push(gcmKey); + configList.push(gcmId); + } + + addConfigFormData.type = platformTypeConstants["ANDROID"]; + addConfigFormData.configuration = configList; + + var addConfigAPI = androidConfigAPI; + + invokerUtil.put( + addConfigAPI, + addConfigFormData, + function (data, textStatus, jqXHR) { + data = JSON.parse(data); + if (jqXHR.status == 201) { + $("#config-save-form").addClass("hidden"); + $("#record-created-msg").removeClass("hidden"); + } + + }, function (data) { + if (data.status == 500) { + $(errorMsg).text("Exception occurred at backend."); + } else if (data.status == 403) { + $(errorMsg).text("Action was not permitted."); + } else { + $(errorMsg).text("An unexpected error occurred."); + } + $(errorMsgWrapper).removeClass("hidden"); + } + ); + } + }); +}); \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.hbs deleted file mode 100644 index e8b1fdfbdf..0000000000 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.hbs +++ /dev/null @@ -1,501 +0,0 @@ -{{! - Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - - WSO2 Inc. licenses this file to you under the Apache License, - Version 2.0 (the "License"); you may not use this file except - in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -}} -
-
- -
-
- General and Platform Specific Server Settings for the Tenant -
-
-
-
- - -
- - - -
-
-
- -

- Policy Compliance Monitoring -
-

-
- - -
-
- -
-
- - -
-
- - - -
-
-
- -

- Communication Protocol Configuration -
-

-
- - -
- -
-
- - -
-
- -
-
- - -
- -
- - -
-
-

- End User License Agreement ( EULA ) -
-

- -
- -
-
- -
-
-
-
- - - -
-
-
- -

- iOS SCEP Certificate Configurations -
-

- - -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -

- iOS Profile Configurations -
-

- - -
- - -
- -

- iOS MDM Configurations -
-

- - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -

- iOS APNS Configurations -
-

- - -
-
- - -
- -
- - -
- -
- - -
- -

- End User License Agreement (EULA) -
-

- -
- -
-
- -
-
-
-
- - - -
-
-
- -

- Device Polling Configuration -
-

-
-
- - -
-
-

- End User License Agreement ( EULA ) -
-

-
- -
-
- -
-
-
-
- -
-
-
-
- - - -
-
-{{#zone "bottomJs"}} - {{js "js/platform-configuration.js"}} -{{/zone}} diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.js deleted file mode 100644 index 354702f18f..0000000000 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.js +++ /dev/null @@ -1,9 +0,0 @@ -function onRequest(context) { - // var log = new Log("platform-configuration-unit backend js"); - var userModule = require("/app/modules/user.js")["userModule"]; - var typesListResponse = userModule.getPlatforms(); - if (typesListResponse["status"] == "success") { - context["types"] = typesListResponse["content"]; - } - return context; -} \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.json b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.json deleted file mode 100644 index be0496bf61..0000000000 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/configuration.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version" : "1.0.0", - "extends": "cdmf.unit.platform.configuration" -} \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/public/js/platform-configuration.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/public/js/platform-configuration.js deleted file mode 100644 index 7bbbad0b42..0000000000 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.platform.configuration/public/js/platform-configuration.js +++ /dev/null @@ -1,878 +0,0 @@ -/* - * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * Checks if provided input is valid against RegEx input. - * - * @param regExp Regular expression - * @param inputString Input string to check - * @returns {boolean} Returns true if input matches RegEx - */ -function inputIsValid(regExp, inputString) { - return regExp.test(inputString); -} - -/** - * Checks if provided input is valid against RegEx input. - * - * @param regExp Regular expression - * @param inputString Input string to check - * @returns {boolean} Returns true if input matches RegEx - */ -function isPositiveInteger(str) { - return /^\+?(0|[1-9]\d*)$/.test(str); -} - -/** - * Get valid param. - * - * @param certificate - * @param cached param (in the registry) - * @returns {String} Returns the valid param - */ -function validateCertificateParams(param, cachedParam) { - if (param == '' && cachedParam != null) { - return cachedParam; - } else { - return param; - } -} - -/** - * Checks if an email address has the valid format or not. - * - * @param email Email address - * @returns {boolean} true if email has the valid format, otherwise false. - */ -function emailIsValid(email) { - var regExp = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; - return regExp.test(email); -} - -var iOSMDMCertificateName = null; -var iOSMDMCertificate = null; -var iOSAPNSCertificateName = null; -var iOSAPNSCertificate = null; - -var notifierTypeConstants = { - "LOCAL": "1", - "GCM": "2" -}; -// Constants to define platform types available -var platformTypeConstants = { - "ANDROID": "android", - "IOS": "ios", - "WINDOWS": "windows" -}; - -var responseCodes = { - "CREATED": "Created", - "SUCCESS": "201", - "INTERNAL_SERVER_ERROR": "Internal Server Error" -}; - -var configParams = { - "NOTIFIER_TYPE": "notifierType", - "NOTIFIER_FREQUENCY": "notifierFrequency", - "GCM_API_KEY": "gcmAPIKey", - "GCM_SENDER_ID": "gcmSenderId", - "ANDROID_EULA": "androidEula", - "IOS_EULA": "iosEula", - "CONFIG_COUNTRY": "configCountry", - "CONFIG_STATE": "configState", - "CONFIG_LOCALITY": "configLocality", - "CONFIG_ORGANIZATION": "configOrganization", - "CONFIG_ORGANIZATION_UNIT": "configOrganizationUnit", - "MDM_CERT_PASSWORD": "MDMCertPassword", - "MDM_CERT_TOPIC_ID": "MDMCertTopicID", - "APNS_CERT_PASSWORD": "APNSCertPassword", - "MDM_CERT": "MDMCert", - "MDM_CERT_NAME": "MDMCertName", - "APNS_CERT": "APNSCert", - "APNS_CERT_NAME": "APNSCertName", - "ORG_DISPLAY_NAME": "organizationDisplayName", - "GENERAL_EMAIL_HOST": "emailHost", - "GENERAL_EMAIL_PORT": "emailPort", - "GENERAL_EMAIL_USERNAME": "emailUsername", - "GENERAL_EMAIL_PASSWORD": "emailPassword", - "GENERAL_EMAIL_SENDER_ADDRESS": "emailSender", - "GENERAL_EMAIL_TEMPLATE": "emailTemplate", - "COMMON_NAME": "commonName", - "KEYSTORE_PASSWORD": "keystorePassword", - "PRIVATE_KEY_PASSWORD": "privateKeyPassword", - "BEFORE_EXPIRE": "beforeExpire", - "AFTER_EXPIRE": "afterExpire", - "WINDOWS_EULA": "windowsLicense" -}; - -function promptErrorPolicyPlatform(errorMsg) { - var mainErrorMsgWrapper = "#platform-config-main-error-msg"; - var mainErrorMsg = mainErrorMsgWrapper + " span"; - $(mainErrorMsg).text(errorMsg); - $(mainErrorMsgWrapper).show(); -} - -$(document).ready(function () { - - var platformsSupported = $("#typeDiv").attr("typeData"); - $("#gcm-inputs").hide(); - tinymce.init({ - selector: "textarea", - height: 500, - theme: "modern", - plugins: [ - "autoresize", - "advlist autolink lists link image charmap print preview anchor", - "searchreplace visualblocks code fullscreen", - "insertdatetime image table contextmenu paste" - ], - toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image" - }); - - var getAndroidConfigAPI = "/mdm-android-agent/configuration"; - var getGeneralConfigAPI = "/devicemgt_admin/configuration"; - var getIosConfigAPI = "/ios/configuration"; - var getWindowsConfigAPI = "/mdm-windows-agent/services/configuration"; - - /** - * Following requests would execute - * on page load event of platform configuration page in WSO2 EMM Console. - * Upon receiving the response, the parameters will be set to the fields, - * in case those configurations are already set. - */ - - if (platformsSupported.indexOf('android') != -1) { - invokerUtil.get( - getAndroidConfigAPI, - function (data) { - data = JSON.parse(data); - if (data != null && data.configuration != null) { - for (var i = 0; i < data.configuration.length; i++) { - var config = data.configuration[i]; - if (config.name == configParams["NOTIFIER_TYPE"]) { - $("#android-config-notifier").val(config.value); - if (config.value != notifierTypeConstants["GCM"]) { - $("#gcm-inputs").hide(); - $("#local-inputs").show(); - } else { - $("#gcm-inputs").show(); - $("#local-inputs").hide(); - } - } else if (config.name == configParams["NOTIFIER_FREQUENCY"]) { - $("input#android-config-notifier-frequency").val(config.value / 1000); - } else if (config.name == configParams["GCM_API_KEY"]) { - $("input#android-config-gcm-api-key").val(config.value); - } else if (config.name == configParams["GCM_SENDER_ID"]) { - $("input#android-config-gcm-sender-id").val(config.value); - } else if (config.name == configParams["ANDROID_EULA"]) { - $("#android-eula").val(config.value); - } - } - } - }, function (data) { - console.log(data); - }); - } - - invokerUtil.get( - getGeneralConfigAPI, - function (data) { - data = JSON.parse(data); - if (data && data.configuration) { - for (var i = 0; i < data.configuration.length; i++) { - var config = data.configuration[i]; - if (config.name == configParams["NOTIFIER_FREQUENCY"]) { - $("input#monitoring-config-frequency").val(config.value / 1000); - } - } - } - }, function (data) { - console.log(data); - }); - - if (platformsSupported.indexOf('windows') != -1) { - invokerUtil.get( - getWindowsConfigAPI, - function (data) { - data = JSON.parse(data); - if (data != null && data.configuration != null) { - for (var i = 0; i < data.configuration.length; i++) { - var config = data.configuration[i]; - if (config.name == configParams["NOTIFIER_FREQUENCY"]) { - $("input#windows-config-notifier-frequency").val(config.value / 1000); - } else if (config.name == configParams["WINDOWS_EULA"]) { - $("#windows-eula").val(config.value); - } - } - } - }, function (data) { - console.log(data); - } - ); - } - - if (platformsSupported.indexOf('ios') != -1) { - invokerUtil.get( - getIosConfigAPI, - function (data) { - data = JSON.parse(data); - if (data != null && data.configuration != null) { - for (var i = 0; i < data.configuration.length; i++) { - var config = data.configuration[i]; - if (config.name == configParams["CONFIG_COUNTRY"]) { - $("input#ios-config-country").val(config.value); - } else if (config.name == configParams["CONFIG_STATE"]) { - $("input#ios-config-state").val(config.value); - } else if (config.name == configParams["CONFIG_LOCALITY"]) { - $("input#ios-config-locality").val(config.value); - } else if (config.name == configParams["CONFIG_ORGANIZATION"]) { - $("input#ios-config-organization").val(config.value); - } else if (config.name == configParams["CONFIG_ORGANIZATION_UNIT"]) { - $("input#ios-config-organization-unit").val(config.value); - } else if (config.name == configParams["MDM_CERT_PASSWORD"]) { - $("input#ios-config-mdm-certificate-password").val(config.value); - } else if (config.name == configParams["MDM_CERT_TOPIC_ID"]) { - $("input#ios-config-mdm-certificate-topic-id").val(config.value); - } else if (config.name == configParams["APNS_CERT_PASSWORD"]) { - $("input#ios-config-apns-certificate-password").val(config.value); - } else if (config.name == configParams["MDM_CERT_NAME"]) { - $("#mdm-cert-file-name").html(config.value); - iOSMDMCertificateName = config.value; - } else if (config.name == configParams["MDM_CERT"]) { - iOSMDMCertificate = config.value; - } else if (config.name == configParams["APNS_CERT_NAME"]) { - $("#apns-cert-file-name").html(config.value); - iOSAPNSCertificateName = config.value; - } else if (config.name == configParams["APNS_CERT"]) { - iOSAPNSCertificate = config.value; - } else if (config.name == configParams["ORG_DISPLAY_NAME"]) { - $("input#ios-org-display-name").val(config.value); - } else if (config.name == configParams["IOS_EULA"]) { - $("#ios-eula").val(config.value); - } - } - } - }, function (data) { - console.log(data); - } - ); - } - - $("select.select2[multiple=multiple]").select2({ - tags: true - }); - - $("#android-config-notifier").change(function () { - var notifierType = $("#android-config-notifier").find("option:selected").attr("value"); - if (notifierType != notifierTypeConstants["GCM"]) { - $("#gcm-inputs").hide(); - $("#local-inputs").show(); - } else { - $("#local-inputs").hide(); - $("#gcm-inputs").show(); - } - }); - - /** - * Following click function would execute - * when a user clicks on "Save" button - * on Android platform configuration page in WSO2 EMM Console. - */ - $("button#save-android-btn").click(function () { - var notifierType = $("#android-config-notifier").find("option:selected").attr("value"); - var notifierFrequency = $("input#android-config-notifier-frequency").val(); - var gcmAPIKey = $("input#android-config-gcm-api-key").val(); - var gcmSenderId = $("input#android-config-gcm-sender-id").val(); - var androidLicense = tinymce.get('android-eula').getContent(); - - var errorMsgWrapper = "#android-config-error-msg"; - var errorMsg = "#android-config-error-msg span"; - if (notifierType == notifierTypeConstants["LOCAL"] && !notifierFrequency) { - $(errorMsg).text("Notifier frequency is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (notifierType == notifierTypeConstants["LOCAL"] && !isPositiveInteger(notifierFrequency)) { - $(errorMsg).text("Provided notifier frequency is invalid. "); - $(errorMsgWrapper).removeClass("hidden"); - } else if (notifierType == notifierTypeConstants["GCM"] && !gcmAPIKey) { - $(errorMsg).text("GCM API Key is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (notifierType == notifierTypeConstants["GCM"] && !gcmSenderId) { - $(errorMsg).text("GCM Sender ID is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else { - - var addConfigFormData = {}; - var configList = new Array(); - - var type = { - "name": configParams["NOTIFIER_TYPE"], - "value": notifierType, - "contentType": "text" - }; - - var frequency = { - "name": configParams["NOTIFIER_FREQUENCY"], - "value": String(notifierFrequency * 1000), - "contentType": "text" - }; - - var gcmKey = { - "name": configParams["GCM_API_KEY"], - "value": gcmAPIKey, - "contentType": "text" - }; - - var gcmId = { - "name": configParams["GCM_SENDER_ID"], - "value": gcmSenderId, - "contentType": "text" - }; - - var androidEula = { - "name": configParams["ANDROID_EULA"], - "value": androidLicense, - "contentType": "text" - }; - - configList.push(type); - configList.push(frequency); - configList.push(androidEula); - if (notifierType == notifierTypeConstants["GCM"]) { - configList.push(gcmKey); - configList.push(gcmId); - } - - addConfigFormData.type = platformTypeConstants["ANDROID"]; - addConfigFormData.configuration = configList; - - var addConfigAPI = "/mdm-android-agent/configuration"; - - invokerUtil.post( - addConfigAPI, - addConfigFormData, - function (data) { - data = JSON.parse(data); - if (data.responseCode == responseCodes["CREATED"]) { - $("#config-save-form").addClass("hidden"); - $("#record-created-msg").removeClass("hidden"); - } else if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (data == 403) { - $(errorMsg).text("Action was not permitted."); - $(errorMsgWrapper).removeClass("hidden"); - } else { - $(errorMsg).text("An unexpected error occurred."); - $(errorMsgWrapper).removeClass("hidden"); - } - - - }, function (data) { - data = data.status; - if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - } else if (data == 403) { - $(errorMsg).text("Action was not permitted."); - } else { - $(errorMsg).text("An unexpected error occurred."); - } - $(errorMsgWrapper).removeClass("hidden"); - } - ); - } - }); - - /** - * Following click function would execute - * when a user clicks on "Save" button - * on General platform configuration page in WSO2 EMM Console. - */ - $("button#save-general-btn").click(function () { - var notifierFrequency = $("input#monitoring-config-frequency").val(); - var errorMsgWrapper = "#email-config-error-msg"; - var errorMsg = "#email-config-error-msg span"; - - if (!notifierFrequency) { - $(errorMsg).text("Monitoring frequency is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!isPositiveInteger(notifierFrequency)) { - $(errorMsg).text("Provided monitoring frequency is invalid. "); - $(errorMsgWrapper).removeClass("hidden"); - } else { - var addConfigFormData = {}; - var configList = new Array(); - - var monitorFrequency = { - "name": configParams["NOTIFIER_FREQUENCY"], - "value": String((notifierFrequency * 1000)), - "contentType": "text" - }; - - configList.push(monitorFrequency); - addConfigFormData.configuration = configList; - - var addConfigAPI = "/devicemgt_admin/configuration"; - invokerUtil.post( - addConfigAPI, - addConfigFormData, - function (data) { - data = JSON.parse(data); - if (data.statusCode == responseCodes["SUCCESS"]) { - $("#config-save-form").addClass("hidden"); - $("#record-created-msg").removeClass("hidden"); - } else if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - } else if (data == 403) { - $(errorMsg).text("Action was not permitted."); - } else { - $(errorMsg).text("An unexpected error occurred."); - } - - $(errorMsgWrapper).removeClass("hidden"); - }, function (data) { - data = data.status; - if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - } else if (data == 403) { - $(errorMsg).text("Action was not permitted."); - } else { - $(errorMsg).text("An unexpected error occurred."); - } - $(errorMsgWrapper).removeClass("hidden"); - } - ); - } - }); - - var errorMsgWrapper = "#ios-config-error-msg"; - var errorMsg = "#ios-config-error-msg span"; - var fileTypes = ['pfx']; - var notSupportedError = false; - - var base64MDMCert = ""; - var fileInputMDMCert = $('#ios-config-mdm-certificate'); - var fileNameMDMCert = ""; - var invalidFormatMDMCert = false; - - var base64APNSCert = ""; - var fileInputAPNSCert = $('#ios-config-apns-certificate'); - var fileNameAPNSCert = ""; - var invalidFormatAPNSCert = false; - - $(fileInputMDMCert).change(function () { - - if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { - $(errorMsg).text("The File APIs are not fully supported in this browser."); - $(errorMsgWrapper).removeClass("hidden"); - notSupportedError = true; - return; - } - - var file = fileInputMDMCert[0].files[0]; - fileNameMDMCert = file.name; - var extension = file.name.split('.').pop().toLowerCase(), - isSuccess = fileTypes.indexOf(extension) > -1; - - if (isSuccess) { - var fileReader = new FileReader(); - fileReader.onload = function (event) { - base64MDMCert = event.target.result; - }; - fileReader.readAsDataURL(file); - invalidFormatMDMCert = false; - } else { - base64MDMCert = ""; - invalidFormatMDMCert = true; - } - }); - - $(fileInputAPNSCert).change(function () { - - if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { - $(errorMsg).text("The File APIs are not fully supported in this browser."); - $(errorMsgWrapper).removeClass("hidden"); - notSupportedError = true; - return; - } - - var file = fileInputAPNSCert[0].files[0]; - fileNameAPNSCert = file.name; - var extension = file.name.split('.').pop().toLowerCase(), - isSuccess = fileTypes.indexOf(extension) > -1; - - if (isSuccess) { - var fileReader = new FileReader(); - fileReader.onload = function (event) { - base64APNSCert = event.target.result; - }; - fileReader.readAsDataURL(file); - invalidFormatAPNSCert = false; - } else { - base64MDMCert = ""; - invalidFormatAPNSCert = true; - } - }); - - $("button#save-ios-btn").click(function () { - - var configCountry = $("#ios-config-country").val(); - var configState = $("#ios-config-state").val(); - var configLocality = $("#ios-config-locality").val(); - var configOrganization = $("#ios-config-organization").val(); - var configOrganizationUnit = $("#ios-config-organization-unit").val(); - var MDMCertPassword = $("#ios-config-mdm-certificate-password").val(); - var MDMCertTopicID = $("#ios-config-mdm-certificate-topic-id").val(); - var APNSCertPassword = $("#ios-config-apns-certificate-password").val(); - var configOrgDisplayName = $("#ios-org-display-name").val(); - var iosLicense = tinymce.get('ios-eula').getContent(); - - fileNameMDMCert = validateCertificateParams(fileNameMDMCert, iOSMDMCertificateName); - fileNameAPNSCert = validateCertificateParams(fileNameAPNSCert, iOSAPNSCertificateName); - base64MDMCert = validateCertificateParams(base64MDMCert, iOSMDMCertificate); - base64APNSCert = validateCertificateParams(base64APNSCert, iOSAPNSCertificate); - - if (!configCountry) { - $(errorMsg).text("SCEP country is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!configState) { - $(errorMsg).text("SCEP state is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!configLocality) { - $(errorMsg).text("SCEP locality is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!configOrganization) { - $(errorMsg).text("SCEP organization is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!configOrganizationUnit) { - $(errorMsg).text("SCEP organization unit is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!MDMCertPassword) { - $(errorMsg).text("MDM certificate password is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!MDMCertTopicID) { - $(errorMsg).text("MDM certificate topic ID is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!APNSCertPassword) { - $(errorMsg).text("APNS certificate password is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (notSupportedError) { - $(errorMsg).text("The File APIs are not fully supported in this browser."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (invalidFormatMDMCert) { - $(errorMsg).text("MDM certificate needs to be in pfx format."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (base64MDMCert == '') { - $(errorMsg).text("MDM certificate is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (invalidFormatAPNSCert) { - $(errorMsg).text("APNS certificate needs to be in pfx format."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (base64APNSCert == '') { - $(errorMsg).text("APNS certificate is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else if (!configOrgDisplayName) { - $(errorMsg).text("Organization display name is a required field. It cannot be empty."); - $(errorMsgWrapper).removeClass("hidden"); - } else { - var addConfigFormData = {}; - var configList = new Array(); - - var configCountry = { - "name": configParams["CONFIG_COUNTRY"], - "value": configCountry, - "contentType": "text" - }; - - var configState = { - "name": configParams["CONFIG_STATE"], - "value": configState, - "contentType": "text" - }; - - var configLocality = { - "name": configParams["CONFIG_LOCALITY"], - "value": configLocality, - "contentType": "text" - }; - - var configOrganization = { - "name": configParams["CONFIG_ORGANIZATION"], - "value": configOrganization, - "contentType": "text" - }; - - var configOrganizationUnit = { - "name": configParams["CONFIG_ORGANIZATION_UNIT"], - "value": configOrganizationUnit, - "contentType": "text" - }; - - var MDMCertPassword = { - "name": configParams["MDM_CERT_PASSWORD"], - "value": MDMCertPassword, - "contentType": "text" - }; - - var MDMCertTopicID = { - "name": configParams["MDM_CERT_TOPIC_ID"], - "value": MDMCertTopicID, - "contentType": "text" - }; - - var APNSCertPassword = { - "name": configParams["APNS_CERT_PASSWORD"], - "value": APNSCertPassword, - "contentType": "text" - }; - - var paramBase64MDMCert = { - "name": configParams["MDM_CERT"], - "value": base64MDMCert, - "contentType": "text" - }; - - var MDMCertName = { - "name": configParams["MDM_CERT_NAME"], - "value": fileNameMDMCert, - "contentType": "text" - }; - - var paramBase64APNSCert = { - "name": configParams["APNS_CERT"], - "value": base64APNSCert, - "contentType": "text" - }; - - var APNSCertName = { - "name": configParams["APNS_CERT_NAME"], - "value": fileNameAPNSCert, - "contentType": "text" - }; - - var paramOrganizationDisplayName = { - "name": configParams["ORG_DISPLAY_NAME"], - "value": configOrgDisplayName, - "contentType": "text" - }; - - var iosEula = { - "name": configParams["IOS_EULA"], - "value": iosLicense, - "contentType": "text" - }; - - configList.push(configCountry); - configList.push(configState); - configList.push(configLocality); - configList.push(configOrganization); - configList.push(configOrganizationUnit); - configList.push(MDMCertPassword); - configList.push(MDMCertTopicID); - configList.push(APNSCertPassword); - configList.push(paramBase64MDMCert); - configList.push(MDMCertName); - configList.push(paramBase64APNSCert); - configList.push(APNSCertName); - configList.push(paramOrganizationDisplayName); - configList.push(iosEula); - - addConfigFormData.type = platformTypeConstants["IOS"]; - addConfigFormData.configuration = configList; - - var addConfigAPI = "/ios/configuration"; - - invokerUtil.post( - addConfigAPI, - addConfigFormData, - function (data) { - data = JSON.parse(data); - if (data.responseCode == responseCodes["CREATED"]) { - $("#config-save-form").addClass("hidden"); - $("#record-created-msg").removeClass("hidden"); - } else if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - } else if (data == 400) { - $(errorMsg).text("Configurations cannot be empty."); - } else { - $(errorMsg).text("An unexpected error occurred."); - } - - $(errorMsgWrapper).removeClass("hidden"); - }, function (data) { - data = data.status; - if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - } else if (data == 403) { - $(errorMsg).text("Action was not permitted."); - } else { - $(errorMsg).text("An unexpected error occurred."); - } - $(errorMsgWrapper).removeClass("hidden"); - } - ); - } - - }); - - var errorMsgWrapperWindows = "#windows-config-error-msg"; - var errorMsgWindows = "#windows-config-error-msg span"; - var fileTypesWindows = ['jks']; - var notSupportedError = false; - - var base64WindowsMDMCert = ""; - var fileInputWindowsMDMCert = $('#windows-config-mdm-certificate'); - var fileNameWindowsMDMCert = ""; - var invalidFormatWindowsMDMCert = false; - - $(fileInputWindowsMDMCert).change(function () { - - if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { - $(errorMsgWindows).text("The File APIs are not fully supported in this browser."); - $(errorMsgWrapperWindows).removeClass("hidden"); - notSupportedError = true; - return; - } - - var file = fileInputWindowsMDMCert[0].files[0]; - fileNameWindowsMDMCert = file.name; - var extension = file.name.split('.').pop().toLowerCase(), - isSuccess = fileTypesWindows.indexOf(extension) > -1; - - if (isSuccess) { - var fileReader = new FileReader(); - fileReader.onload = function (event) { - base64WindowsMDMCert = event.target.result; - }; - fileReader.readAsDataURL(file); - invalidFormatWindowsMDMCert = false; - } else { - base64MDMCert = ""; - invalidFormatWindowsMDMCert = true; - } - }); - - $("button#save-windows-btn").click(function () { - - var notifierFrequency = $("#windows-config-notifier-frequency").val(); - var windowsLicense = tinymce.get('windows-eula').getContent(); - - if (!notifierFrequency) { - $(errorMsgWindows).text("Polling Interval is a required field. It cannot be empty."); - $(errorMsgWrapperWindows).removeClass("hidden"); - } else if (!windowsLicense) { - $(errorMsgWindows).text("License is a required field. It cannot be empty."); - $(errorMsgWrapperWindows).removeClass("hidden"); - } else if (!$.isNumeric(notifierFrequency)) { - $(errorMsgWindows).text("Provided Notifier frequency is invalid. It must be a number."); - $(errorMsgWrapperWindows).removeClass("hidden"); - } else { - var addConfigFormData = {}; - var configList = new Array(); - - var paramNotifierFrequency = { - "name": configParams["NOTIFIER_FREQUENCY"], - "value": String(notifierFrequency * 1000), - "contentType": "text" - }; - - var windowsEula = { - "name": configParams["WINDOWS_EULA"], - "value": windowsLicense, - "contentType": "text" - }; - - configList.push(paramNotifierFrequency); - configList.push(windowsEula); - - addConfigFormData.type = platformTypeConstants["WINDOWS"]; - addConfigFormData.configuration = configList; - - var addConfigAPI = "/mdm-windows-agent/services/configuration"; - - invokerUtil.post( - addConfigAPI, - addConfigFormData, - function (data) { - data = JSON.parse(data); - if (data.responseCode == responseCodes["CREATED"]) { - $("#config-save-form").addClass("hidden"); - $("#record-created-msg").removeClass("hidden"); - } else if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - } else if (data == 400) { - $(errorMsg).text("Configurations cannot be empty."); - } else { - $(errorMsg).text("An unexpected error occurred."); - } - - $(errorMsgWrapperWindows).removeClass("hidden"); - }, function (data) { - data = data.status; - if (data == 500) { - $(errorMsg).text("Exception occurred at backend."); - } else if (data == 403) { - $(errorMsg).text("Action was not permitted."); - } else { - $(errorMsg).text("An unexpected error occurred."); - } - $(errorMsgWrapper).removeClass("hidden"); - } - ); - } - - }); -}); - -// Start of HTML embedded invoke methods -var showAdvanceOperation = function (operation, button) { - $(button).addClass('selected'); - $(button).siblings().removeClass('selected'); - var enabledPlatforms = $("#supportedPlatforms"); - var isPluginEnabled = false; - switch (operation) { - case 'ios': - if (enabledPlatforms.data("ios")) { - isPluginEnabled = true; - } - break; - case 'windows': - if (enabledPlatforms.data("windows")) { - isPluginEnabled = true; - } - break; - case 'android': - if (enabledPlatforms.data("android")) { - isPluginEnabled = true; - } - break; - case 'general': - isPluginEnabled = true; - break; - } - if (isPluginEnabled) { - var hiddenOperation = ".wr-hidden-operations-content > div"; - $(hiddenOperation + '[data-operation="' + operation + '"]').show(); - $(hiddenOperation + '[data-operation="' + operation + '"]').siblings().hide(); - } else { - var hiddenOperation = ".wr-hidden-operations-content > div"; - $(hiddenOperation + '[data-operation="error"]').show(); - $(hiddenOperation + '[data-operation="error"]').siblings().hide(); - promptErrorPolicyPlatform("To use " + operation + " related functionalities you need to configure the server " + - "accordingly.Please refer to the user guiled."); - } -}; diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.wizard/wizard.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.wizard/wizard.js index 2a22006402..da27266817 100644 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.wizard/wizard.js +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.wizard/wizard.js @@ -18,6 +18,7 @@ function onRequest(context) { var userModule = require("/app/modules/user.js")["userModule"]; + var deviceModule = require("/app/modules/device.js").deviceModule; var utility = require('/app/modules/utility.js').utility; var response = userModule.getRoles(); var wizardPage = {}; @@ -25,7 +26,7 @@ function onRequest(context) { wizardPage["roles"] = response["content"]; } var deviceType = context.uriParams.deviceType; - var typesListResponse = userModule.getPlatforms(); + var typesListResponse = deviceModule.getDeviceTypes(); if (typesListResponse["status"] == "success") { for (var type in typesListResponse["content"]) { if (deviceType == typesListResponse["content"][type]["name"]) { diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/src/main/webapp/META-INF/permissions.xml b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/src/main/webapp/META-INF/permissions.xml index 1a69d570d4..c3fd4697ba 100644 --- a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/src/main/webapp/META-INF/permissions.xml +++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/src/main/webapp/META-INF/permissions.xml @@ -119,7 +119,7 @@ View Tenant configuration /device-mgt/windows/tenant/configuration - /services/configuration + /configuration GET emm_admin @@ -127,7 +127,7 @@ Add Tenant configuration /device-mgt/windows/tenant/configuration - /services/configuration + /configuration POST emm_admin @@ -135,7 +135,7 @@ Update Tenant configuration /device-mgt/windows/tenant/configuration - /services/configuration + /configuration PUT emm_admin diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs new file mode 100644 index 0000000000..3ca1c1dca5 --- /dev/null +++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs @@ -0,0 +1,56 @@ +{{! + Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + + WSO2 Inc. licenses this file to you under the Apache License, + Version 2.0 (the "License"); you may not use this file except + in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +}} + +
+
+ +

+ Device Polling Configuration +
+

+
+
+ + +
+
+

+ End User License Agreement ( EULA ) +
+

+
+ +
+
+ +
+
+
+ +{{#zone "bottomJs"}} + {{js "js/platform-configuration.js"}} +{{/zone}} diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.json b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.json new file mode 100644 index 0000000000..fd25901297 --- /dev/null +++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.json @@ -0,0 +1,3 @@ +{ + "version" : "1.0.0" +} \ No newline at end of file diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js new file mode 100644 index 0000000000..8da747255d --- /dev/null +++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Constants to define platform types available +var platformTypeConstants = { + "ANDROID": "android", + "IOS": "ios", + "WINDOWS": "windows" +}; + +var responseCodes = { + "CREATED": "Created", + "SUCCESS": "201", + "INTERNAL_SERVER_ERROR": "Internal Server Error" +}; + +var configParams = { + "NOTIFIER_TYPE": "notifierType", + "NOTIFIER_FREQUENCY": "notifierFrequency", + "GCM_API_KEY": "gcmAPIKey", + "GCM_SENDER_ID": "gcmSenderId", + "ANDROID_EULA": "androidEula", + "IOS_EULA": "iosEula", + "CONFIG_COUNTRY": "configCountry", + "CONFIG_STATE": "configState", + "CONFIG_LOCALITY": "configLocality", + "CONFIG_ORGANIZATION": "configOrganization", + "CONFIG_ORGANIZATION_UNIT": "configOrganizationUnit", + "MDM_CERT_PASSWORD": "MDMCertPassword", + "MDM_CERT_TOPIC_ID": "MDMCertTopicID", + "APNS_CERT_PASSWORD": "APNSCertPassword", + "MDM_CERT": "MDMCert", + "MDM_CERT_NAME": "MDMCertName", + "APNS_CERT": "APNSCert", + "APNS_CERT_NAME": "APNSCertName", + "ORG_DISPLAY_NAME": "organizationDisplayName", + "GENERAL_EMAIL_HOST": "emailHost", + "GENERAL_EMAIL_PORT": "emailPort", + "GENERAL_EMAIL_USERNAME": "emailUsername", + "GENERAL_EMAIL_PASSWORD": "emailPassword", + "GENERAL_EMAIL_SENDER_ADDRESS": "emailSender", + "GENERAL_EMAIL_TEMPLATE": "emailTemplate", + "COMMON_NAME": "commonName", + "KEYSTORE_PASSWORD": "keystorePassword", + "PRIVATE_KEY_PASSWORD": "privateKeyPassword", + "BEFORE_EXPIRE": "beforeExpire", + "AFTER_EXPIRE": "afterExpire", + "WINDOWS_EULA": "windowsLicense", + "IOS_CONFIG_MDM_MODE": "iOSConfigMDMMode", + "IOS_CONFIG_APNS_MODE": "iOSConfigAPNSMode" +}; + +function promptErrorPolicyPlatform(errorMsg) { + var mainErrorMsgWrapper = "#platform-config-main-error-msg"; + var mainErrorMsg = mainErrorMsgWrapper + " span"; + $(mainErrorMsg).text(errorMsg); + $(mainErrorMsgWrapper).show(); +} + +$(document).ready(function () { + + var platformsSupported = $("#typeDiv").attr("typeData"); + $("#gcm-inputs").hide(); + tinymce.init({ + selector: "textarea", + height:500, + theme: "modern", + plugins: [ + "autoresize", + "advlist autolink lists link image charmap print preview anchor", + "searchreplace visualblocks code fullscreen", + "insertdatetime image table contextmenu paste" + ], + toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image" + }); + + var windowsConfigAPI = "/api/device-mgt/windows/v1.0/configuration"; + + invokerUtil.get( + windowsConfigAPI, + function (data, textStatus, jqXHR) { + console.log(jqXHR); + console.log(data); + data = JSON.parse(data); + if (data != null && data.configuration != null) { + for (var i = 0; i < data.configuration.length; i++) { + var config = data.configuration[i]; + if (config.name == configParams["NOTIFIER_FREQUENCY"]) { + $("input#windows-config-notifier-frequency").val(config.value / 1000); + } else if (config.name == configParams["WINDOWS_EULA"]) { + $("#windows-eula").val(config.value); + } + } + } + }, function (data) { + console.log(data); + } + ); + + $("select.select2[multiple=multiple]").select2({ + tags: true + }); + + var errorMsgWrapperWindows = "#windows-config-error-msg"; + var errorMsgWindows = "#windows-config-error-msg span"; + var fileTypesWindows = ['jks']; + var notSupportedError = false; + + var base64WindowsMDMCert = ""; + var fileInputWindowsMDMCert = $('#windows-config-mdm-certificate'); + var fileNameWindowsMDMCert = ""; + var invalidFormatWindowsMDMCert = false; + + $(fileInputWindowsMDMCert).change(function () { + + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + $(errorMsgWindows).text("The File APIs are not fully supported in this browser."); + $(errorMsgWrapperWindows).removeClass("hidden"); + notSupportedError = true; + return; + } + + var file = fileInputWindowsMDMCert[0].files[0]; + fileNameWindowsMDMCert = file.name; + var extension = file.name.split('.').pop().toLowerCase(), + isSuccess = fileTypesWindows.indexOf(extension) > -1; + + if (isSuccess) { + var fileReader = new FileReader(); + fileReader.onload = function (event) { + base64WindowsMDMCert = event.target.result; + }; + fileReader.readAsDataURL(file); + invalidFormatWindowsMDMCert = false; + } else { + base64MDMCert = ""; + invalidFormatWindowsMDMCert = true; + } + }); + + $("button#save-windows-btn").click(function () { + + var notifierFrequency = $("#windows-config-notifier-frequency").val(); + var windowsLicense = tinymce.get('windows-eula').getContent(); + + if (!notifierFrequency) { + $(errorMsgWindows).text("Polling Interval is a required field. It cannot be empty."); + $(errorMsgWrapperWindows).removeClass("hidden"); + } else if (!windowsLicense) { + $(errorMsgWindows).text("License is a required field. It cannot be empty."); + $(errorMsgWrapperWindows).removeClass("hidden"); + } else if (!$.isNumeric(notifierFrequency)) { + $(errorMsgWindows).text("Provided Notifier frequency is invalid. It must be a number."); + $(errorMsgWrapperWindows).removeClass("hidden"); + } else { + var addConfigFormData = {}; + var configList = new Array(); + + var paramNotifierFrequency = { + "name": configParams["NOTIFIER_FREQUENCY"], + "value": String(notifierFrequency * 1000), + "contentType": "text" + }; + + var windowsEula = { + "name": configParams["WINDOWS_EULA"], + "value": windowsLicense, + "contentType": "text" + }; + + configList.push(paramNotifierFrequency); + configList.push(windowsEula); + + addConfigFormData.type = platformTypeConstants["WINDOWS"]; + addConfigFormData.configuration = configList; + + var addConfigAPI = windowsConfigAPI; + + invokerUtil.put( + addConfigAPI, + addConfigFormData, + function (data, textStatus, jqXHR) { + data = jqXHR.status; + if (data == 201) { + $("#config-save-form").addClass("hidden"); + $("#record-created-msg").removeClass("hidden"); + } else if (data == 500) { + $(errorMsg).text("Exception occurred at backend."); + } else if (data == 400) { + $(errorMsg).text("Configurations cannot be empty."); + } else { + $(errorMsg).text("An unexpected error occurred."); + } + + $(errorMsgWrapperWindows).removeClass("hidden"); + }, function (data) { + data = data.status; + if (data == 500) { + $(errorMsg).text("Exception occurred at backend."); + } else if (data == 403) { + $(errorMsg).text("Action was not permitted."); + } else { + $(errorMsg).text("An unexpected error occurred."); + } + $(errorMsgWrapper).removeClass("hidden"); + } + ); + } + + }); +}); \ No newline at end of file From 8ddee3b2170be2e23e25f7d474791f029c87f46f Mon Sep 17 00:00:00 2001 From: mharindu Date: Thu, 4 Aug 2016 17:47:26 +0530 Subject: [PATCH 06/71] Refactored scopes --- .../service/impl/AndroidSenseService.java | 22 +++----- .../arduino/service/impl/ArduinoService.java | 19 +++---- .../service/impl/RaspberryPiService.java | 17 +++---- .../service/impl/VirtualFireAlarmService.java | 8 +-- .../DeviceManagementAdminService.java | 50 +++++++++---------- .../services/DeviceManagementService.java | 12 ++--- .../DeviceTypeConfigurationService.java | 8 +-- .../services/EventReceiverService.java | 6 +-- 8 files changed, 63 insertions(+), 79 deletions(-) diff --git a/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java b/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java index 323afc19d6..348a9008d6 100644 --- a/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java +++ b/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.api/src/main/java/org/wso2/carbon/device/mgt/iot/androidsense/service/impl/AndroidSenseService.java @@ -19,17 +19,11 @@ package org.wso2.carbon.device.mgt.iot.androidsense.service.impl; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; + +import javax.ws.rs.*; import javax.ws.rs.core.Response; @DeviceType(value = "android_sense") @@ -45,7 +39,7 @@ public interface AndroidSenseService { @Path("device/{deviceId}/words") @POST @Feature(code = "keywords", name = "Add Keywords", description = "Send keywords to the device") - @Permission(scope = "android_sense_user", roles = {"/permission/admin/device-mgt/user/operations"}) + @Scope(key = "device:android-sense:enroll", name = "", description = "") Response sendKeyWords(@PathParam("deviceId") String deviceId, @QueryParam("keywords") String keywords); /** @@ -57,13 +51,13 @@ public interface AndroidSenseService { @Path("device/{deviceId}/words/threshold") @POST @Feature(code = "threshold", name = "Add a Threshold", description = "Set a threshold for word in the device") - @Permission(scope = "android_sense_user", roles = {"emm-user"}) + @Scope(key = "device:android-sense:enroll", name = "", description = "") Response sendThreshold(@PathParam("deviceId") String deviceId, @QueryParam("threshold") String threshold); @Path("device/{deviceId}/words") @DELETE @Feature(code = "remove", name = "Remove Keywords", description = "Remove the keywords") - @Permission(scope = "android_sense_user", roles = {"emm-user"}) + @Scope(key = "device:android-sense:enroll", name = "", description = "") Response removeKeyWords(@PathParam("deviceId") String deviceId, @QueryParam("words") String words); /** @@ -72,7 +66,7 @@ public interface AndroidSenseService { @Path("stats/{deviceId}/sensors/{sensorName}") @GET @Consumes("application/json") - @Permission(scope = "android_sense_user", roles = {"emm-user"}) + @Scope(key = "device:android-sense:enroll", name = "", description = "") @Produces("application/json") Response getAndroidSenseDeviceStats(@PathParam("deviceId") String deviceId, @PathParam("sensorName") String sensor, @QueryParam("from") long from, @QueryParam("to") long to); @@ -82,7 +76,7 @@ public interface AndroidSenseService { */ @Path("device/{device_id}/register") @POST - @Permission(scope = "android_sense_user", roles = {"emm-user"}) + @Scope(key = "device:android-sense:enroll", name = "", description = "") Response register(@PathParam("device_id") String deviceId, @QueryParam("deviceName") String deviceName); } diff --git a/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java b/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java index 6f7480f37c..9e0c97ec31 100644 --- a/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java +++ b/components/iot-plugins/arduino-plugin/org.wso2.carbon.device.mgt.iot.arduino.api/src/main/java/org/wso2/carbon/device/mgt/iot/arduino/service/impl/ArduinoService.java @@ -19,16 +19,11 @@ package org.wso2.carbon.device.mgt.iot.arduino.service.impl; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; + +import javax.ws.rs.*; import javax.ws.rs.core.Response; @API(name = "arduino", version = "1.0.0", context = "/arduino", tags = {"arduino"}) @@ -38,12 +33,12 @@ public interface ArduinoService { @Path("device/{deviceId}/bulb") @POST @Feature(code = "bulb", name = "Control Bulb", description = "Control Bulb on Arduino Uno") - @Permission(scope = "arduino_user", roles = {"emm-user"}) + @Scope(key = "device:arduino:enroll", name = "", description = "") Response switchBulb(@PathParam("deviceId") String deviceId, @QueryParam("state") String state); @Path("device/{deviceId}/controls") @GET - @Permission(scope = "arduino_device", roles = {"emm-user"}) + @Scope(key = "device:arduino:enroll", name = "", description = "") Response readControls(@PathParam("deviceId") String deviceId); /** @@ -53,7 +48,7 @@ public interface ArduinoService { @GET @Consumes("application/json") @Produces("application/json") - @Permission(scope = "arduino_user", roles = {"emm-user"}) + @Scope(key = "device:arduino:enroll", name = "", description = "") Response getArduinoTemperatureStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, @QueryParam("to") long to); @@ -63,7 +58,7 @@ public interface ArduinoService { @Path("device/download") @GET @Produces("application/octet-stream") - @Permission(scope = "arduino_user", roles = {"emm-user"}) + @Scope(key = "device:arduino:enroll", name = "", description = "") Response downloadSketch(@QueryParam("deviceName") String customDeviceName); } diff --git a/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java b/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java index 1fc893ca2d..00236ddbed 100644 --- a/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java +++ b/components/iot-plugins/raspberrypi-plugin/org.wso2.carbon.device.mgt.iot.raspberrypi.api/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/service/impl/RaspberryPiService.java @@ -19,16 +19,11 @@ package org.wso2.carbon.device.mgt.iot.raspberrypi.service.impl; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; + +import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -39,7 +34,7 @@ public interface RaspberryPiService { @Path("device/{deviceId}/bulb") @POST @Feature(code = "bulb", name = "Bulb On / Off", description = "Switch on/off Raspberry Pi agent's bulb. (On / Off)") - @Permission(scope = "raspberrypi_user", roles = {"emm-user"}) + @Scope(key = "device:raspberrypi:enroll", name = "", description = "") Response switchBulb(@PathParam("deviceId") String deviceId, @QueryParam("state") String state); /** @@ -49,7 +44,7 @@ public interface RaspberryPiService { @GET @Consumes("application/json") @Produces("application/json") - @Permission(scope = "raspberrypi_user", roles = {"emm-user"}) + @Scope(key = "device:raspberrypi:enroll", name = "", description = "") Response getRaspberryPiTemperatureStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, @QueryParam("to") long to); @@ -59,7 +54,7 @@ public interface RaspberryPiService { @Path("device/download") @GET @Produces(MediaType.APPLICATION_JSON) - @Permission(scope = "raspberrypi_user", roles = {"emm-user"}) + @Scope(key = "device:raspberrypi:enroll", name = "", description = "") Response downloadSketch(@QueryParam("deviceName") String deviceName, @QueryParam("sketch_type") String sketchType); } diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java index 1be685927e..9d89050fba 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java @@ -19,7 +19,7 @@ package org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature; @@ -48,7 +48,7 @@ public interface VirtualFireAlarmService { */ @POST @Path("device/{deviceId}/buzz") - @Permission(scope = "virtual_firealarm_user", roles = {"emm-user"}) + @Scope(key = "device:firealarm:enroll", name = "", description = "") @Feature(code = "buzz", name = "Buzzer On / Off", description = "Switch on/off Virtual Fire Alarm Buzzer. (On / Off)") Response switchBuzzer(@PathParam("deviceId") String deviceId, @FormParam("state") String state); @@ -58,7 +58,7 @@ public interface VirtualFireAlarmService { */ @Path("device/stats/{deviceId}") @GET - @Permission(scope = "virtual_firealarm_user", roles = {"emm-user"}) + @Scope(key = "device:firealarm:enroll", name = "", description = "") @Consumes("application/json") @Produces("application/json") Response getVirtualFirealarmStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, @@ -67,7 +67,7 @@ public interface VirtualFireAlarmService { @Path("device/download") @GET @Produces("application/zip") - @Permission(scope = "virtual_firealarm_user", roles = {"emm-user"}) + @Scope(key = "device:firealarm:enroll", name = "", description = "") Response downloadSketch(@QueryParam("deviceName") String deviceName, @QueryParam("sketchType") String sketchType); } diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java index 6f3884a8a0..a23eedb331 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java @@ -20,7 +20,7 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; import org.wso2.carbon.mdm.services.android.bean.wrapper.*; @@ -91,7 +91,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new lock operation.") }) - @Permission(scope = "device:android:lock", roles = {"admin"}) + @Scope(key = "device:android:operation:lock", name = "Lock device", description = "") Response configureDeviceLock( @ApiParam(name = "deviceLockBeanWrapper", value = "Device lock configurations with device IDs") DeviceLockBeanWrapper deviceLockBeanWrapper); @@ -145,7 +145,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new un-lock operation.") }) - @Permission(scope = "device:android:unlock", roles = {"admin"}) + @Scope(key = "device:android:operation:unlock", name = "Unlock device", description = "") Response configureDeviceUnlock( @ApiParam(name = "deviceIDs", value = "DeviceIds to be enable device unlock operation") List deviceIDs); @@ -198,7 +198,7 @@ public interface DeviceManagementAdminService { code = 500, message = "Internal Server Error. \n " + "Server error occurred while adding a new get-location operation.")}) - @Permission(scope = "device:android:location", roles = {"admin"}) + @Scope(key = "device:android:operation:location", name = "Get device location", description = "") Response getDeviceLocation( @ApiParam(name = "deviceIDs", value = "DeviceIDs to be requested to get device location") List deviceIDs); @@ -251,7 +251,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new clear password operation.") }) - @Permission(scope = "device:android:clear-password", roles = {"admin"}) + @Scope(key = "device:android:operation:clear-password", name = "Clear password of device", description = "") Response removePassword( @ApiParam(name = "deviceIDs", value = "DeviceIds to be requested to remove password") List deviceIDs); @@ -304,7 +304,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new control camera operation.") }) - @Permission(scope = "device:android:camera", roles = {"admin"}) + @Scope(key = "device:android:operation:camera", name = "Enable/Disable camera", description = "") Response configureCamera( @ApiParam(name = "cameraBeanWrapper", value = "Camera enable/disable configurations with device IDs") CameraBeanWrapper cameraBeanWrapper); @@ -360,7 +360,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new device info operation.") }) - @Permission(scope = "device:android:info", roles = {"admin"}) + @Scope(key = "device:android:operation:info", name = "Get device information", description = "") Response getDeviceInformation( @ApiParam(name = "deviceIds", value = "Device IDs to be requested to get device information") List deviceIDs); @@ -469,7 +469,7 @@ public interface DeviceManagementAdminService { code = 500, message = "Internal Server Error. \n " + "Server error occurred while adding a enterprise wipe operation.")}) - @Permission(scope = "device:android:enterprise-wipe", roles = {"admin"}) + @Scope(key = "device:android:operation:enterprise-wipe", name = "Enterprise wipe", description = "") Response wipeDevice(@ApiParam(name = "deviceIDs", value = "Device IDs to be requested to do enterprise-wipe") List deviceIDs); @@ -521,7 +521,7 @@ public interface DeviceManagementAdminService { code = 500, message = "Internal Server Error. \n " + "Server error occurred while adding a device wipe operation.")}) - @Permission(scope = "device:android:wipe", roles = {"admin"}) + @Scope(key = "device:android:operation:wipe", name = "Factory reset device", description = "") Response wipeData( @ApiParam(name = "wipeDataBeanWrapper", value = "Configurations and DeviceIds needed to do wipe-data") WipeDataBeanWrapper wipeDataBeanWrapper); @@ -578,7 +578,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new get-applications operation.") }) - @Permission(scope = "device:android:get-applications", roles = {"admin"}) + @Scope(key = "device:android:operation:applications", name = "Get installed applications", description = "") Response getApplications( @ApiParam(name = "deviceIDs", value = "Device Ids needed to get applications that are already installed") List deviceIDs); @@ -631,7 +631,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new device ring operation.") }) - @Permission(scope = "device:android:ring", roles = {"admin"}) + @Scope(key = "device:android:operation:ring", name = "Ring device", description = "") Response ringDevice( @ApiParam(name = "deviceIDs", value = "Device Ids needed for ring") List deviceIDs); @@ -684,7 +684,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new device reboot operation.") }) - @Permission(scope = "device:android:reboot", roles = {"admin"}) + @Scope(key = "device:android:operation:reboot", name = "Reboot device", description = "") Response rebootDevice( @ApiParam(name = "deviceIDs", value = "Device Ids needed for reboot.") List deviceIDs); @@ -737,7 +737,7 @@ public interface DeviceManagementAdminService { "Server error occurred while adding a new device mute operation.") }) @Path("/mute") - @Permission(scope = "device:android:mute", roles = {"admin"}) + @Scope(key = "device:android:operation:mute", name = "Mute device", description = "") Response muteDevice( @ApiParam(name = "deviceIDs", value = "DeviceIDs need to be muted") List deviceIDs); @@ -793,7 +793,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new install-application operation.") }) - @Permission(scope = "device:android:install-application", roles = {"admin"}) + @Scope(key = "device:android:operation:install-app", name = "Install applications", description = "") Response installApplication( @ApiParam(name = "applicationInstallationBeanWrapper", value = "Properties of installed apps and device IDs") ApplicationInstallationBeanWrapper applicationInstallationBeanWrapper); @@ -849,7 +849,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new update-application operation.") }) - @Permission(scope = "device:android:update-application", roles = {"admin"}) + @Scope(key = "device:android:operation:update-app", name = "Update installed applications", description = "") Response updateApplication( @ApiParam(name = "applicationUpdateBeanWrapper", value = "Properties of updated apps and device IDs") ApplicationUpdateBeanWrapper applicationUpdateBeanWrapper); @@ -902,7 +902,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new uninstall-application operation.") }) - @Permission(scope = "device:android:uninstall-application", roles = {"admin"}) + @Scope(key = "device:android:operation:uninstall-app", name = "Uninstall applications", description = "") Response uninstallApplication( @ApiParam(name = "applicationUninstallationBeanWrapper", value = "applicationUninstallationConfigs and Device Ids") @@ -957,7 +957,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new blacklist-applications operation.") }) - @Permission(scope = "device:android:blacklist-applications", roles = {"admin"}) + @Scope(key = "device:android:operation:blacklist-app", name = "Blacklist applications", description = "") Response blacklistApplications( @ApiParam(name = "blacklistApplicationsBeanWrapper", value = "BlacklistApplications " + "Configuration and DeviceIds") @@ -1012,7 +1012,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new upgrade firmware operation.") }) - @Permission(scope = "device:android:upgrade-firmware", roles = {"admin"}) + @Scope(key = "device:android:operation:upgrade", name = "Upgrade firmware", description = "") Response upgradeFirmware( @ApiParam(name = "upgradeFirmwareBeanWrapper", value = "Firmware upgrade configuration and DeviceIds") @@ -1067,7 +1067,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new configure VPN operation.") }) - @Permission(scope = "device:android:vpn", roles = {"admin"}) + @Scope(key = "device:android:operation:vpn", name = "Add VPN profiles", description = "") Response configureVPN( @ApiParam(name = "vpnBeanWrapper", value = "VPN configuration and DeviceIds") @@ -1121,7 +1121,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new send notification operation.") }) - @Permission(scope = "device:android:send-notification", roles = {"admin"}) + @Scope(key = "device:android:operation:notification", name = "Send notifications", description = "") Response sendNotification( @ApiParam(name = "notificationBeanWrapper", value = "Notification Configurations and device Ids") @@ -1175,7 +1175,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new configure wifi operation.") }) - @Permission(scope = "device:android:wifi", roles = {"admin"}) + @Scope(key = "device:android:operation:wifi", name = "Add WiFi configurations", description = "") Response configureWifi( @ApiParam(name = "wifiBeanWrapper", value = "WifiConfigurations and Device Ids") WifiBeanWrapper wifiBeanWrapper); @@ -1228,7 +1228,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new encrypt storage operation.") }) - @Permission(scope = "device:android:encrypt", roles = {"admin"}) + @Scope(key = "device:android:operation:encrypt", name = "Encrypt device", description = "") Response encryptStorage( @ApiParam(name = "encryptionBeanWrapper", value = "Configurations and deviceIds need to be done data encryption") @@ -1282,7 +1282,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new change lock code operation.") }) - @Permission(scope = "device:android:change-lock-code", roles = {"admin"}) + @Scope(key = "device:android:operation:change-lock", name = "Change password of device", description = "") Response changeLockCode( @ApiParam(name = "lockCodeBeanWrapper", value = "Configurations and device Ids need to be done change lock code") @@ -1336,7 +1336,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new set password policy operation.") }) - @Permission(scope = "device:android:set-password-policy", roles = {"admin"}) + @Scope(key = "device:android:operation:password-policy", name = "Set password policy", description = "") Response setPasswordPolicy( @ApiParam(name = "passwordPolicyBeanWrapper", value = "Password Policy Configurations and Device Ids") @@ -1390,7 +1390,7 @@ public interface DeviceManagementAdminService { message = "Internal Server Error. \n " + "Server error occurred while adding a new set webclip operation.") }) - @Permission(scope = "device:android:webclip", roles = {"admin"}) + @Scope(key = "device:android:operation:webclip", name = "Add webclips", description = "") Response setWebClip( @ApiParam(name = "webClipBeanWrapper", value = "Configurations to need set web clip on device and device Ids") diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java index 2aaaee1c7d..b3fd5a35ea 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java @@ -20,7 +20,7 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; import org.wso2.carbon.mdm.services.android.bean.wrapper.AndroidApplication; import org.wso2.carbon.mdm.services.android.bean.wrapper.AndroidDevice; @@ -87,7 +87,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while updating the application list.") }) - @Permission(scope = "device:android:enroll", roles = {"admin"}) + @Scope(key = "device:android:enroll", name = "Enroll Android device", description = "") Response updateApplicationList( @ApiParam( name = "id", @@ -141,7 +141,7 @@ public interface DeviceManagementService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching policies.") }) - @Permission(scope = "device:android:enroll", roles = {"admin"}) + @Scope(key = "device:android:enroll", name = "Enroll Android device", description = "") Response getPendingOperations( @ApiParam( name = "id", @@ -245,7 +245,7 @@ public interface DeviceManagementService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching the enrollment status of the Android device.") }) - @Permission(scope = "device:android:enroll", roles = {"admin"}) + @Scope(key = "device:android:enroll", name = "Enroll Android device", description = "") Response isEnrolled( @ApiParam( name = "id", @@ -299,7 +299,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while updating the device enrollment.") }) - @Permission(scope = "device:android:enroll", roles = {"admin"}) + @Scope(key = "device:android:enroll", name = "Enroll Android device", description = "") Response modifyEnrollment( @ApiParam( name = "id", @@ -329,7 +329,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while dis-enrolling the device.") }) - @Permission(scope = "device:android:enroll", roles = {"admin"}) + @Scope(key = "device:android:enroll", name = "Enroll Android device", description = "") Response disEnrollDevice( @ApiParam( name = "id", diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java index 9a9012bb13..48737c0833 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java @@ -20,7 +20,7 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration; import org.wso2.carbon.mdm.services.android.bean.AndroidPlatformConfiguration; import org.wso2.carbon.mdm.services.android.exception.AndroidAgentException; @@ -81,7 +81,7 @@ public interface DeviceTypeConfigurationService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching Android platform configuration.") }) - @Permission(scope = "configuration:view", roles = {"admin"}) + @Scope(key = "configuration:read", name = "View configurations", description = "") Response getConfiguration( @ApiParam( name = "If-Modified-Since", @@ -131,7 +131,7 @@ public interface DeviceTypeConfigurationService { message = "Internal Server Error. \n " + "Server error occurred while modifying Android platform configuration.") }) - @Permission(scope = "configuration:modify", roles = {"admin"}) + @Scope(key = "configuration:write", name = "Add configurations", description = "") Response updateConfiguration( @ApiParam(name = "configuration", value = "AndroidPlatformConfiguration") @@ -179,7 +179,7 @@ public interface DeviceTypeConfigurationService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching Android license configuration.") }) - @Permission(scope = "device:android:enroll", roles = {"admin"}) + @Scope(key = "device:android:enroll", name = "Enroll Android device", description = "") Response getLicense( @ApiParam( name = "If-Modified-Since", diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java index eb6e270edc..ab9008e735 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/EventReceiverService.java @@ -20,7 +20,7 @@ package org.wso2.carbon.mdm.services.android.services; import io.swagger.annotations.*; import org.wso2.carbon.apimgt.annotations.api.API; -import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.mdm.services.android.bean.DeviceState; import org.wso2.carbon.mdm.services.android.bean.wrapper.EventBeanWrapper; @@ -91,7 +91,7 @@ public interface EventReceiverService { message = "Internal Server Error. \n " + "Server error occurred while publishing events.") }) - @Permission(scope = "device:android:event:publish", roles = {"admin"}) + @Scope(key = "device:android:event:write", name = "Publish events to DAS", description = "") Response publishEvents( @ApiParam( name = "eventBeanWrapper", @@ -141,7 +141,7 @@ public interface EventReceiverService { code = 500, message = "Error occurred while getting published events for specific device.") }) - @Permission(scope = "device:android:event:view", roles = {"admin"}) + @Scope(key = "device:android:event:read", name = "View events", description = "") Response retrieveAlerts( @ApiParam( name = "id", From 6e8f3f5db231c34033fd970f63ef5134e20c2927 Mon Sep 17 00:00:00 2001 From: mharindu Date: Mon, 8 Aug 2016 18:25:00 +0530 Subject: [PATCH 07/71] Modified scopes --- .../services/android/services/DeviceManagementService.java | 2 +- .../android/services/DeviceTypeConfigurationService.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java index b3fd5a35ea..71fcdba84e 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementService.java @@ -329,7 +329,7 @@ public interface DeviceManagementService { message = "Internal Server Error. \n " + "Server error occurred while dis-enrolling the device.") }) - @Scope(key = "device:android:enroll", name = "Enroll Android device", description = "") + @Scope(key = "device:android:disenroll", name = "Enroll Android device", description = "") Response disEnrollDevice( @ApiParam( name = "id", diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java index 48737c0833..44d915028e 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceTypeConfigurationService.java @@ -81,7 +81,7 @@ public interface DeviceTypeConfigurationService { code = 500, message = "Internal Server Error. \n Server error occurred while fetching Android platform configuration.") }) - @Scope(key = "configuration:read", name = "View configurations", description = "") + @Scope(key = "configuration:view", name = "View configurations", description = "") Response getConfiguration( @ApiParam( name = "If-Modified-Since", @@ -131,7 +131,7 @@ public interface DeviceTypeConfigurationService { message = "Internal Server Error. \n " + "Server error occurred while modifying Android platform configuration.") }) - @Scope(key = "configuration:write", name = "Add configurations", description = "") + @Scope(key = "configuration:manage", name = "Add configurations", description = "") Response updateConfiguration( @ApiParam(name = "configuration", value = "AndroidPlatformConfiguration") From a37d5dbfd9fe7cdff1138f3583876f248a330c92 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 9 Aug 2016 07:57:30 +0530 Subject: [PATCH 08/71] Fixing user,device,role listing pages --- .../app/pages/mdm.page.devices/devices.hbs | 123 +++++ .../app/pages/mdm.page.devices/devices.js | 40 ++ .../app/pages/mdm.page.devices/devices.json | 4 + .../public/images/TemperatureController.png | Bin 0 -> 234616 bytes .../public/images/android.png | Bin 0 -> 2948 bytes .../mdm.page.devices/public/images/ios.png | Bin 0 -> 6966 bytes .../public/js/device-listing.js | 520 ++++++++++++++++++ .../public/templates/device-listing.hbs | 42 ++ .../operation-bar.hbs | 8 +- .../public/js/operation-bar.js | 19 +- 10 files changed, 747 insertions(+), 9 deletions(-) create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.hbs create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.js create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.json create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/images/TemperatureController.png create mode 100755 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/images/android.png create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/images/ios.png create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/js/device-listing.js create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/templates/device-listing.hbs diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.hbs new file mode 100644 index 0000000000..2e5c1b3e97 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.hbs @@ -0,0 +1,123 @@ +{{! + Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + + WSO2 Inc. licenses this file to you under the Apache License, + Version 2.0 (the "License"); you may not use this file except + in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +}} +{{unit "cdmf.unit.ui.title" pageTitle="Device Management"}} + +{{unit "cdmf.unit.data-tables-extended"}} + +{{#zone "breadcrumbs"}} +
  • + + + +
  • +
  • + + Devices + +
  • +{{/zone}} + +{{#zone "navbarActions"}} + {{#if permissions.enroll}} +
  • + + + + + + Enroll Device + +
  • + {{/if}} +{{/zone}} + +{{#zone "content"}} + +
    + +     + Loading devices . . . +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    By Device NameBy OwnerBy StatusBy PlatformBy Ownership
    +
    + {{unit "mdm.unit.device.operation-bar"}} +
    +
    +

    Enabling Device Operations

    +

    To enable device operations, select the desired platform from above filter.

    +
    +
    +
    +
    + +{{/zone}} + +{{#zone "bottomJs"}} + + {{js "js/device-listing.js"}} +{{/zone}} diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.js new file mode 100644 index 0000000000..f1339b9e43 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +function onRequest(context){ + var userModule = require("/app/modules/business-controllers/user.js").userModule; + var constants = require("/app/modules/constants.js"); + var viewModel = {}; + var permissions = []; + if(userModule.isAuthorized("/permission/admin/device-mgt/emm-admin/devices/list")){ + permissions.push("LIST_DEVICES"); + if (userModule.isAuthorized("/permission/admin/device-mgt/emm-admin/devices/view")) { + permissions.push("VIEW_DEVICES"); + } + }else if(userModule.isAuthorized("/permission/admin/device-mgt/user/devices/list")){ + permissions.push("LIST_OWN_DEVICES"); + if (userModule.isAuthorized("/permission/admin/device-mgt/user/devices/view")) { + permissions.push("VIEW_OWN_DEVICES"); + } + }else if(userModule.isAuthorized("/permission/admin/device-mgt/emm-admin/policies/list")){ + permissions.push("LIST_POLICIES"); + } + var currentUser = session.get(constants.USER_SESSION_KEY); + viewModel.permissions = stringify(permissions); + viewModel.currentUser = currentUser; + return viewModel; +} \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.json b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.json new file mode 100644 index 0000000000..4886bfc3c4 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/devices.json @@ -0,0 +1,4 @@ +{ + "version": "1.0.0", + "extends": "cdmf.page.devices" +} \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/images/TemperatureController.png b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/images/TemperatureController.png new file mode 100644 index 0000000000000000000000000000000000000000..e16b48d8eefcac064473f94464964b77fa41e23b GIT binary patch literal 234616 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 - - - -
    -
    - -
    +
    - + -
    -
    +
    +
    + + + + +
    +
    + +
    + + + +
    + + +
    + +
    + - -
    - -
    - -
    - - -
    + + +
    + +
    + +
    + + +
    -
    +
    -
    - + +
    + +
    +
    +
    + + + +
    +
    + +
    - - - -
    -
    - -
    +
    - + -
    -
    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
    ' + } + },{ + class: 'fade-edge', + data: 'name', + render: function ( name, type, row, meta ) { + var model = row.model; + var vendor = row.vendor; + var html = '

    Device ' + name + '

    '; + if (model) { + html += '
    (' + vendor + '-' + model + ')
    '; + } + return html; + } + },{ + class: 'fade-edge remove-padding-top', + data: 'user', + render: function ( user, type, row, meta ) { + return '
    ' + user + '
    '; + } + },{ + class: 'fade-edge remove-padding-top', + data: 'status', + render: function ( status, type, row, meta ) { + var html; + switch (status) { + case 'ACTIVE' : + html = ' Active'; + break; + case 'INACTIVE' : + html = ' Inactive'; + break; + case 'BLOCKED' : + html = ' Blocked'; + break; + case 'REMOVED' : + html = ' Removed'; + break; + } + return '
    '+html+'
    '; + } + },{ + className: 'fade-edge remove-padding-top', + data: 'deviceType', + render: function ( deviceType, type, row, meta ) { + return '
    ' + deviceType + '
    '; + } + },{ + className: 'fade-edge remove-padding-top', + data: 'ownership', + render: function ( ownership, type, row, meta ) { + return '
    ' + ownership + '
    '; + } + } + ]; + + + var dataFilter = function(data){ + data = JSON.parse(data); + + var objects = []; + + $(data.devices).each(function( index ) { + objects.push( + { + model: getPropertyValue(data.devices[index].properties, 'DEVICE_MODEL'), + vendor: getPropertyValue(data.devices[index].properties, 'VENDOR'), + owner: data.devices[index].enrolmentInfo.owner, + status: data.devices[index].enrolmentInfo.status, + ownership: data.devices[index].enrolmentInfo.ownership, + deviceType: data.devices[index].type, + deviceIdentifier: data.devices[index].deviceIdentifier, + name : data.devices[index].name + } + ); + }); + + json = { + "recordsTotal": data.count, + "recordsFiltered": data.count, + "data": objects + }; + return JSON.stringify( json ); + }; + + + $('#device-grid').datatables_extended_serverside_paging(null, '/api/device-mgt/v1.0/devices', dataFilter, columns, fnCreatedRow, + function( oSettings ) { + $(".icon .text").res_text(0.2); + $('#device-grid').removeClass('hidden'); + $("#loading-content").remove(); + }, { + "placeholder": "Search By Device Name", + "searchKey" : "name" + }); + + // $('#device-grid').datatables_extended({ + // serverSide: true, + // processing: false, + // searching: true, + // ordering: false, + // filter: false, + // pageLength : 16, + // ajax: { url : '/emm/api/devices', data : {url : serviceURL}, + // dataSrc: function (json) { + // $('#device-grid').removeClass('hidden'); + // $("#loading-content").remove(); + // var $list = $("#device-table :input[type='search']"); + // $list.each(function(){ + // $(this).addClass("hidden"); + // }); + // return json.devices; + // } + // }, + // columnDefs: [ + // { targets: 0, data: 'name', className: 'remove-padding icon-only content-fill viewEnabledIcon' , render: function ( data, type, row, meta ) { + // var deviceType = row.type; + // var deviceIdentifier = row.deviceIdentifier; + // var url = "#"; + // if (status != 'REMOVED') { + // url = "devices/view?type=" + deviceType + "&id=" + deviceIdentifier; + // } + // return '
    '; + // }}, + // { targets: 1, data: 'name', className: 'fade-edge' , render: function ( name, type, row, meta ) { + // var model = getPropertyValue(row.properties, 'DEVICE_MODEL'); + // var vendor = getPropertyValue(row.properties, 'VENDOR'); + // var html = '

    Device ' + name + '

    '; + // if (model) { + // html += '
    (' + vendor + '-' + model + ')
    '; + // } + // return html; + // }}, + // { targets: 2, data: 'enrolmentInfo.owner', className: 'fade-edge remove-padding-top'}, + // { targets: 3, data: 'enrolmentInfo.status', className: 'fade-edge remove-padding-top' , + // render: function ( status, type, row, meta ) { + // var html; + // switch (status) { + // case 'ACTIVE' : + // html = ' Active'; + // break; + // case 'INACTIVE' : + // html = ' Inactive'; + // break; + // case 'BLOCKED' : + // html = ' Blocked'; + // break; + // case 'REMOVED' : + // html = ' Removed'; + // break; + // } + // return html; + // }}, + // { targets: 4, data: 'type' , className: 'fade-edge remove-padding-top' }, + // { targets: 5, data: 'enrolmentInfo.ownership' , className: 'fade-edge remove-padding-top' }, + // { targets: 6, data: 'enrolmentInfo.status' , className: 'text-right content-fill text-left-on-grid-view no-wrap' , + // render: function ( status, type, row, meta ) { + // var deviceType = row.type; + // var deviceIdentifier = row.deviceIdentifier; + // var html = ''; + // return html; + // }} + // ], + // "createdRow": function( row, data, dataIndex ) { + // $(row).attr('data-type', 'selectable'); + // $(row).attr('data-deviceid', data.deviceIdentifier); + // $(row).attr('data-devicetype', data.type); + // var model = getPropertyValue(data.properties, 'DEVICE_MODEL'); + // var vendor = getPropertyValue(data.properties, 'VENDOR'); + // var owner = data.enrolmentInfo.owner; + // var status = data.enrolmentInfo.status; + // var ownership = data.enrolmentInfo.ownership; + // var deviceType = data.type; + // $.each($('td', row), function (colIndex) { + // switch(colIndex) { + // case 1: + // $(this).attr('data-search', model + ',' + vendor); + // $(this).attr('data-display', model); + // break; + // case 2: + // $(this).attr('data-grid-label', "Owner"); + // $(this).attr('data-search', owner); + // $(this).attr('data-display', owner); + // break; + // case 3: + // $(this).attr('data-grid-label', "Status"); + // $(this).attr('data-search', status); + // $(this).attr('data-display', status); + // break; + // case 4: + // $(this).attr('data-grid-label', "Type"); + // $(this).attr('data-search', deviceType); + // $(this).attr('data-display', deviceType); + // break; + // case 5: + // $(this).attr('data-grid-label', "Ownership"); + // $(this).attr('data-search', ownership); + // $(this).attr('data-display', ownership); + // break; + // } + // }); + // }, + // "fnDrawCallback": function( oSettings ) { + // $(".icon .text").res_text(0.2); + // } + // }); + $(deviceCheckbox).click(function () { + addDeviceSelectedClass(this); + }); +} + +/* + * Setting-up global variables. + */ +var deviceCheckbox = "#ast-container .ctrl-wr-asset .itm-select input[type='checkbox']"; +var assetContainer = "#ast-container"; + +function openCollapsedNav(){ + $('.wr-hidden-nav-toggle-btn').addClass('active'); + $('#hiddenNav').slideToggle('slideDown', function(){ + if($(this).css('display') == 'none'){ + $('.wr-hidden-nav-toggle-btn').removeClass('active'); + } + }); +} + +function initPage() { + var currentUser = $("#device-listing").data("currentUser"); + var serviceURL; + if ($.hasPermission("LIST_DEVICES")) { + serviceURL ="/api/device-mgt/v1.0/devices" + } else if ($.hasPermission("LIST_OWN_DEVICES")) { + //Get authenticated users devices + serviceURL = "/api/device-mgt/v1.0/devices?user=" + currentUser; + } + invokerUtil.get( + serviceURL, + function (data) { + if (data) { + data = JSON.parse(data); + if (data.devices.length > 0) { + loadDevices(); + } else { + $("#loading-content").remove(); + $("#device-listing-status-msg").text("No enrolled device is found."); + $("#device-listing-status").removeClass(' hidden'); + } + } + }, function (message) { + initPage(); + } + ); +} + +/* + * DOM ready functions. + */ +$(document).ready(function () { + initPage(); + + /* Adding selected class for selected devices */ + $(deviceCheckbox).each(function () { + addDeviceSelectedClass(this); + }); + + var i; + var permissionList = $("#permission").data("permission"); + for (i = 0; i < permissionList.length; i++) { + $.setPermission(permissionList[i]); + } + + /* for device list sorting drop down */ + $(".ctrl-filter-type-switcher").popover({ + html : true, + content : function () { + return $("#content-filter-types").html(); + } + }); + + $(".ast-container").on("click", ".claim-btn", function(e){ + e.stopPropagation(); + var deviceId = $(this).data("deviceid"); + var deviceListing = $("#device-listing"); + var currentUser = deviceListing.data("current-user"); + var serviceURL = "/temp-controller-agent/enrollment/claim?username=" + currentUser; + var deviceIdentifier = {id: deviceId, type: "TemperatureController"}; + invokerUtil.put(serviceURL, deviceIdentifier, function(message){ + console.log(message); + }, function(message){ + console.log(message.content); + }); + }); + + /* for data tables*/ + $('[data-toggle="tooltip"]').tooltip(); + + $("[data-toggle=popover]").popover(); + + $(".ctrl-filter-type-switcher").popover({ + html : true, + content: function() { + return $('#content-filter-types').html(); + } + }); + + $('#nav').affix({ + offset: { + top: $('header').height() + } + }); + +}); diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/templates/device-listing.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/templates/device-listing.hbs new file mode 100644 index 0000000000..8e5f2d2f88 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/templates/device-listing.hbs @@ -0,0 +1,42 @@ + {{#each devices}} + {{deviceMap this}} + + +
    + +
    + + +

    Device {{name}}

    + {{#if properties.DEVICE_MODEL}} +
    ({{properties.VENDOR}} - {{properties.DEVICE_MODEL}})
    + {{/if}} + + {{enrolmentInfo.owner}} + + {{#equal enrolmentInfo.status "ACTIVE"}} Active{{/equal}} + {{#equal enrolmentInfo.status "INACTIVE"}} Inactive{{/equal}} + {{#equal enrolmentInfo.status "BLOCKED"}} Blocked{{/equal}} + {{#equal enrolmentInfo.status "REMOVED"}} Removed{{/equal}} + + {{type}} + {{enrolmentInfo.ownership}} + + + + + {{/each}} \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/operation-bar.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/operation-bar.hbs index 468bae0c4f..e4a17d8f02 100644 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/operation-bar.hbs +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/operation-bar.hbs @@ -15,10 +15,14 @@ specific language governing permissions and limitations under the License. }} -
    +{{#zone "content"}} +
    + {{unit "mdm.unit.device.operation-mod"}} +
    +{{/zone}} {{#zone "bottomJs"}} - {{js "js/operation-bar.js"}} + {{js "js/operation-bar.js"}} {{/zone}} \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/public/js/operation-bar.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/public/js/operation-bar.js index fe6c85bd0c..1e65ffb33a 100644 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/public/js/operation-bar.js +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.device.operation-bar/public/js/operation-bar.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -103,13 +103,15 @@ function loadOperationBar(deviceType) { var operationBar = $("#operations-bar"); var operationBarSrc = operationBar.attr("src"); var platformType = deviceType; + //var selectedDeviceID = deviceId; $.template("operations-bar", operationBarSrc, function (template) { - var serviceURL = "/devicemgt_admin/features/" + platformType; + //var serviceURL = "/mdm-admin/features/" + platformType; + var serviceURL = "/api/device-mgt/v1.0/devices/"+platformType+"/*/features"; var successCallback = function (data) { var viewModel = {}; data = JSON.parse(data).filter(function (current) { var iconName; - switch (deviceType) { + switch(deviceType) { case platformTypeConstants.ANDROID: iconName = operationModule.getAndroidIconForFeature(current.code); current.type = deviceType; @@ -140,6 +142,7 @@ function loadOperationBar(deviceType) { function runOperation(operationName) { var deviceIdList = getSelectedDeviceIds(); var list = getDevicesByTypes(deviceIdList); + console.log(list); var successCallback = function (data) { if (operationName == "NOTIFICATION") { @@ -157,7 +160,7 @@ function runOperation(operationName) { var payload, serviceEndPoint; if (list[platformTypeConstants.IOS]) { payload = operationModule. - generatePayload(platformTypeConstants.IOS, operationName, list[platformTypeConstants.IOS]); + generatePayload(platformTypeConstants.IOS, operationName, list[platformTypeConstants.IOS]); serviceEndPoint = operationModule.getIOSServiceEndpoint(operationName); } else if (list[platformTypeConstants.ANDROID]) { payload = operationModule @@ -165,14 +168,15 @@ function runOperation(operationName) { serviceEndPoint = operationModule.getAndroidServiceEndpoint(operationName); } else if (list[platformTypeConstants.WINDOWS]) { payload = operationModule. - generatePayload(platformTypeConstants.WINDOWS, operationName, list[platformTypeConstants.WINDOWS]); + generatePayload(platformTypeConstants.WINDOWS, operationName, list[platformTypeConstants.WINDOWS]); serviceEndPoint = operationModule.getWindowsServiceEndpoint(operationName); } if (operationName == "NOTIFICATION") { var errorMsgWrapper = "#notification-error-msg"; var errorMsg = "#notification-error-msg span"; - var message = $("#message").val(); - if (!message) { + var messageTitle = $("#messageTitle").val(); + var messageText = $("#messageText").val(); + if (!(messageTitle && messageText)) { $(errorMsg).text("Enter a message. It cannot be empty."); $(errorMsgWrapper).removeClass("hidden"); } else { @@ -181,6 +185,7 @@ function runOperation(operationName) { hidePopup(); } } else { + console.log(serviceEndPoint); invokerUtil.post(serviceEndPoint, payload, successCallback, errorCallback); $(modalPopupContent).removeData(); hidePopup(); From a62544c68b15249688ff8645916af282416d5eac Mon Sep 17 00:00:00 2001 From: hasuniea Date: Tue, 9 Aug 2016 18:00:49 +0530 Subject: [PATCH 09/71] refactoring windowsapi --- .../org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml | 4 ++-- .../pom.xml | 2 +- .../src/main/resources/p2.inf | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml index 4aedb77bec..98ef253530 100644 --- a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml +++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml @@ -46,7 +46,7 @@ 2.2 - ${project.artifactId} + api#device-mgt#windows#v1.0 @@ -72,7 +72,7 @@ - + diff --git a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml index 633bd9f20b..a2d51efef3 100644 --- a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml +++ b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml @@ -118,7 +118,7 @@ true ${project.build.directory}/maven-shared-archive-resources/webapps/ - mdm-windows-agent.war + api#device-mgt#windows#v1.0.war diff --git a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf index 11320712c2..d70e9fe99c 100644 --- a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf +++ b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf @@ -1,13 +1,13 @@ instructions.configure = \ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/jaggeryapps/,target:${installFolder}/../../deployment/server/jaggeryapps/,overwrite:true);\ -org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/webapps/mdm-windows-agent.war,target:${installFolder}/../../deployment/server/webapps/mdm-windows-agent.war,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/webapps/api#device-mgt#windows#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#device-mgt#windows#v1.0.war,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/dbscripts/plugins/,target:${installFolder}/../../../dbscripts/cdm/plugins/windows,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../database/);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/database/,target:${installFolder}/../../database/,overwrite:true);\ instructions.unconfigure = \ -org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/mdm-windows-agent.war);\ -org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/mdm-windows-agent);\ +org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/api#device-mgt#windows#v1.0.war);\ +org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/api#device-mgt#windows#v1.0);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.device-view);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.policy-edit);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.policy-view);\ From 141a7d09596f6e4670deff686898fb4698aa352a Mon Sep 17 00:00:00 2001 From: mharindu Date: Tue, 9 Aug 2016 19:09:22 +0530 Subject: [PATCH 10/71] Added scopes to the config.json --- .../devicemgt/app/conf/config.json | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json index 65734a16cd..9a667dbf15 100644 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json @@ -70,17 +70,53 @@ "copyrightSuffix" : " All Rights Reserved." }, "scopes" : [ - "license-add", "license-view", "device-view", - "device-info", "device-list", "device-view-own", "device-modify", "device-search", - "operation-install", "operation-view", "operation-modify", "operation-uninstall", - "group-add", "group-share", "group-modify", "group-view", "group-remove", - "certificate-modify", "certificate-view", - "configuration-view", "configuration-modify", - "policy-view", "policy-modify", - "device-notification-view", "device-notification-modify", - "feature-view", - "roles-view", "roles-modify", "roles-remove", "roles-add", - "user-password-reset", "user-password-modify", "user-modify", "user-view", "user-invite", "user-remove", "user-add" + "user:manage", + "user:view", + "device-type:admin:view", + "device:view", + "notification:view", + "device:admin:view", + "application:manage", + "activity:view", + "user:admin:reset-password", + "policy:manage", + "policy:view", + "role:manage", + "role:view", + "configuration:view", + "configuration:modify", + "device:android:operation:reboot", + "device:android:operation:camera", + "device:android:operation:vpn", + "device:android:operation:lock", + "device:android:operation:ring", + "device:android:operation:update-app", + "device:android:operation:wipe", + "device:android:operation:encrypt", + "device:android:operation:blacklist-app", + "device:android:operation:applications", + "device:android:operation:enterprise-wipe", + "device:android:operation:info", + "device:android:operation:wifi", + "device:android:operation:uninstall-app", + "device:android:operation:change-lock", + "device:android:operation:notification", + "device:android:operation:upgrade", + "device:android:operation:unlock", + "device:android:operation:mute", + "device:android:operation:location", + "device:android:operation:webclip", + "device:android:operation:clear-password", + "device:android:operation:password-policy", + "device:android:operation:install-app", + "device:android:event:write", + "device:android:event:read", + "device:android:enroll", + "configuration:manage", + "configuration:view", + "device:android:enroll", + "certificate:view", + "certificate:manage" ], "isOAuthEnabled" : true, "backendRestEndpoints" : { From 4c11157d4e833e699d26970131a2af2691d442e2 Mon Sep 17 00:00:00 2001 From: Rasika Perera Date: Wed, 10 Aug 2016 08:33:11 +0530 Subject: [PATCH 11/71] Enrollment url by default same VM --- .../main/resources/jaggeryapps/devicemgt/app/conf/config.json | 2 +- .../devicemgt/app/pages/mdm.page.dashboard/dashboard.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json index 9a667dbf15..8eb7bfc3cb 100644 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/conf/config.json @@ -61,7 +61,7 @@ "roleNameHelpMsg" : "should be in minimum 3 characters long and do not include any whitespaces." }, "generalConfig" : { - "host" : "https://localhost:9443", + "host" : "%https.ip%", "companyName" : "WSO2 Carbon Device Manager", "browserTitle" : "WSO2 Device Manager", "copyrightPrefix" : "\u00A9 %date-year%, ", diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.dashboard/dashboard.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.dashboard/dashboard.js index 0fb3cf4a94..e9b07a654b 100644 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.dashboard/dashboard.js +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.dashboard/dashboard.js @@ -25,6 +25,6 @@ function onRequest(context) { viewModel.permissions = userModule.getUIPermissions(); new Log().debug("## Permissions : " + stringify(userModule.getUIPermissions())); //TODO: Move enrollment URL into app-conf.json - viewModel.enrollmentURL = mdmProps.enrollmentUrl; + viewModel.enrollmentURL = mdmProps.generalConfig.host + mdmProps.enrollmentDir; return viewModel; } \ No newline at end of file From 5ea262947b953eeb6828d7c6c2e670f076f8e04f Mon Sep 17 00:00:00 2001 From: Rasika Perera Date: Wed, 10 Aug 2016 08:43:00 +0530 Subject: [PATCH 12/71] Added emm specific policy creation unit extending cdmf.unit.policy.create --- .../units/mdm.unit.policy.create/create.hbs | 287 + .../units/mdm.unit.policy.create/create.js | 49 + .../units/mdm.unit.policy.create/create.json | 4 + .../public/js/policy-create.js | 2793 ++++++++++ .../templates/hidden-operations-android.hbs | 1273 +++++ .../templates/hidden-operations-ios.hbs | 4832 +++++++++++++++++ .../templates/hidden-operations-windows.hbs | 566 ++ 7 files changed, 9804 insertions(+) create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.js create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.json create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/js/policy-create.js create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-android.hbs create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-ios.hbs create mode 100644 components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-windows.hbs diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs new file mode 100644 index 0000000000..f101f41a9a --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs @@ -0,0 +1,287 @@ +{{#zone "content"}} +
    +
    + + + + + + + + + + +
    +

    +    +    Loading policy creation wizard . . . +

    +
    + + + +
    +
    +{{/zone}} +{{#zone "bottomJs"}} + + + + {{js "/js/policy-create.js"}} +{{/zone}} + diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.js new file mode 100644 index 0000000000..1b4c879f40 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +function onRequest(context) { + var DTYPE_CONF_DEVICE_TYPE_KEY = "deviceType"; + var DTYPE_CONF_DEVICE_TYPE_LABEL_KEY = "label"; + + var utility = require("/app/modules/utility.js").utility; + var userModule = require("/app/modules/business-controllers/user.js")["userModule"]; + + var types = {}; + types["types"] = []; + var typesListResponse = userModule.getPlatforms(); + var deviceTypes = typesListResponse["content"]["deviceTypes"]; + if (typesListResponse["status"] == "success") { + for (var i = 0; i < deviceTypes.length; i++) { + var content = {}; + var deviceType = deviceTypes[i]; + content["name"] = deviceType; + var configs = utility.getDeviceTypeConfig(deviceType); + var deviceTypeLabel = deviceType; + if (configs && configs[DTYPE_CONF_DEVICE_TYPE_KEY][DTYPE_CONF_DEVICE_TYPE_LABEL_KEY]) { + deviceTypeLabel = configs[DTYPE_CONF_DEVICE_TYPE_KEY][DTYPE_CONF_DEVICE_TYPE_LABEL_KEY]; + } + var policyWizard = new File("/app/units/" + utility.getTenantedDeviceUnitName(deviceType, "policy-wizard")); + if (policyWizard.isExists()) { + content["icon"] = utility.getDeviceThumb(deviceType); + content["label"] = deviceTypeLabel; + types["types"].push(content); + } + } + } + return types; +} diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.json b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.json new file mode 100644 index 0000000000..54fae3dcb5 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.json @@ -0,0 +1,4 @@ +{ + "version" : "1.0.0", + "extends" : "cdmf.unit.policy.create" +} \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/js/policy-create.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/js/policy-create.js new file mode 100644 index 0000000000..2f41084733 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/js/policy-create.js @@ -0,0 +1,2793 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var validateStep = {}; +var stepForwardFrom = {}; +var stepBackFrom = {}; +var policy = {}; +var configuredOperations = []; + +// Constants to define platform types available +var platformTypeConstants = { + "ANDROID": "android", + "IOS": "ios", + "WINDOWS": "windows" +}; + +// Constants to define platform types ids. +var platformIds = { + "ANDROID": 1, + "IOS": 3, + "WINDOWS": 2 +}; + +// Constants to define Android Operation Constants +var androidOperationConstants = { + "PASSCODE_POLICY_OPERATION": "passcode-policy", + "PASSCODE_POLICY_OPERATION_CODE": "PASSCODE_POLICY", + "CAMERA_OPERATION": "camera", + "CAMERA_OPERATION_CODE": "CAMERA", + "ENCRYPT_STORAGE_OPERATION": "encrypt-storage", + "ENCRYPT_STORAGE_OPERATION_CODE": "ENCRYPT_STORAGE", + "WIFI_OPERATION": "wifi", + "WIFI_OPERATION_CODE": "WIFI", + "VPN_OPERATION": "vpn", + "VPN_OPERATION_CODE": "VPN", + "APPLICATION_OPERATION":"app-restriction", + "APPLICATION_OPERATION_CODE":"APP-RESTRICTION" +}; + +// Constants to define iOS Operation Constants +var iosOperationConstants = { + "PASSCODE_POLICY_OPERATION": "passcode-policy", + "PASSCODE_POLICY_OPERATION_CODE": "PASSCODE_POLICY", + "RESTRICTIONS_OPERATION": "restrictions", + "RESTRICTIONS_OPERATION_CODE": "RESTRICTION", + "WIFI_OPERATION": "wifi", + "WIFI_OPERATION_CODE": "WIFI", + "EMAIL_OPERATION": "email", + "EMAIL_OPERATION_CODE": "EMAIL", + "AIRPLAY_OPERATION": "airplay", + "AIRPLAY_OPERATION_CODE": "AIR_PLAY", + "LDAP_OPERATION": "ldap", + "LDAP_OPERATION_CODE": "LDAP", + "CALENDAR_OPERATION": "calendar", + "CALENDAR_OPERATION_CODE": "CALDAV", + "CALENDAR_SUBSCRIPTION_OPERATION": "calendar-subscription", + "CALENDAR_SUBSCRIPTION_OPERATION_CODE": "CALENDAR_SUBSCRIPTION", + "APN_OPERATION": "apn", + "APN_OPERATION_CODE": "APN", + "CELLULAR_OPERATION": "cellular", + "CELLULAR_OPERATION_CODE": "CELLULAR", + "DOMAIN": "DOMAIN", + "VPN_OPERATION_CODE": "VPN", + "VPN_OPERATION": "vpn", + "PER_APP_VPN_OPERATION_CODE": "PER_APP_VPN", + "PER_APP_VPN_OPERATION": "per-app-vpn", + "APP_TO_PER_APP_VPN_MAPPING_OPERATION_CODE": "APP_TO_PER_APP_VPN_MAPPING", + "APP_TO_PER_APP_VPN_MAPPING_OPERATION": "app-to-per-app-vpn-mapping" +}; + +// Constants to define Android Operation Constants +var windowsOperationConstants = { + "PASSCODE_POLICY_OPERATION": "passcode-policy", + "PASSCODE_POLICY_OPERATION_CODE": "PASSCODE_POLICY", + "CAMERA_OPERATION": "camera", + "CAMERA_OPERATION_CODE": "CAMERA", + "ENCRYPT_STORAGE_OPERATION": "encrypt-storage", + "ENCRYPT_STORAGE_OPERATION_CODE": "ENCRYPT_STORAGE" +}; + +/** + * @namespace $ + * The $ is just a function. + * It is actually an alias for the function called jQuery. + * For ex: $(this) means jQuery(this) and S.fn.x means jQuery.fn.x + */ + +/** + * Method to update the visibility of grouped input. + * @param domElement HTML grouped-input element with class name "grouped-input" + */ +var updateGroupedInputVisibility = function (domElement) { + if ($(".parent-input:first", domElement).is(":checked")) { + if ($(".grouped-child-input:first", domElement).hasClass("disabled")) { + $(".grouped-child-input:first", domElement).removeClass("disabled"); + } + $(".child-input", domElement).each(function () { + $(this).prop('disabled', false); + }); + } else { + if (!$(".grouped-child-input:first", domElement).hasClass("disabled")) { + $(".grouped-child-input:first", domElement).addClass("disabled"); + } + $(".child-input", domElement).each(function () { + $(this).prop('disabled', true); + }); + } +}; + +var validateInline = {}; +var clearInline = {}; + +var enableInlineError = function (inputField, errorMsg, errorSign) { + var fieldIdentifier = "#" + inputField; + var errorMsgIdentifier = "#" + inputField + " ." + errorMsg; + var errorSignIdentifier = "#" + inputField + " ." + errorSign; + + if (inputField) { + $(fieldIdentifier).addClass(" has-error has-feedback"); + } + + if (errorMsg) { + $(errorMsgIdentifier).removeClass(" hidden"); + } + + if (errorSign) { + $(errorSignIdentifier).removeClass(" hidden"); + } +}; + +var disableInlineError = function (inputField, errorMsg, errorSign) { + var fieldIdentifier = "#" + inputField; + var errorMsgIdentifier = "#" + inputField + " ." + errorMsg; + var errorSignIdentifier = "#" + inputField + " ." + errorSign; + + if (inputField) { + $(fieldIdentifier).removeClass(" has-error has-feedback"); + } + + if (errorMsg) { + $(errorMsgIdentifier).addClass(" hidden"); + } + + if (errorSign) { + $(errorSignIdentifier).addClass(" hidden"); + } +}; + +/** + *clear inline validation messages. + */ +clearInline["policy-name"] = function () { + disableInlineError("policyNameField", "nameEmpty", "nameError"); +}; + + +/** + * Validate if provided policy name is valid against RegEx configures. + */ +validateInline["policy-name"] = function () { + var policyName = $("input#policy-name-input").val(); + if (policyName && inputIsValidAgainstLength(policyName, 1, 30)) { + disableInlineError("policyNameField", "nameEmpty", "nameError"); + } else { + enableInlineError("policyNameField", "nameEmpty", "nameError"); + } +}; + +$("#policy-name-input").focus(function(){ + clearInline["policy-name"](); +}).blur(function(){ + validateInline["policy-name"](); +}); + +stepForwardFrom["policy-platform"] = function (actionButton) { + policy["platform"] = $(actionButton).data("platform"); + policy["platformId"] = $(actionButton).data("platform-type"); + // updating next-page wizard title with selected platform + $("#policy-profile-page-wizard-title").text("ADD " + policy["platform"] + " POLICY"); + + var deviceType = policy["platform"]; + var hiddenOperationsByDeviceType = $("#hidden-operations-" + deviceType); + var hiddenOperationsByDeviceTypeCacheKey = deviceType + "HiddenOperations"; + var hiddenOperationsByDeviceTypeSrc = hiddenOperationsByDeviceType.attr("src"); + + setTimeout( + function () { + $.template(hiddenOperationsByDeviceTypeCacheKey, hiddenOperationsByDeviceTypeSrc, function (template) { + var content = template(); + $(".wr-advance-operations").html(content); + $(".wr-advance-operations li.grouped-input").each(function () { + updateGroupedInputVisibility(this); + }); + }); + }, + 250 // time delayed for the execution of above function, 250 milliseconds + ); +}; + +/** + * Checks if provided number is valid against a range. + * + * @param numberInput Number Input + * @param min Minimum Limit + * @param max Maximum Limit + * @returns {boolean} Returns true if input is within the specified range + */ +var inputIsValidAgainstRange = function (numberInput, min, max) { + return (numberInput == min || (numberInput > min && numberInput < max) || numberInput == max); +}; + +/** + * Checks if provided input is valid against RegEx input. + * + * @param regExp Regular expression + * @param input Input string to check + * @returns {boolean} Returns true if input matches RegEx + */ +var inputIsValidAgainstRegExp = function (regExp, input) { + return regExp.test(input); +}; + +validateStep["policy-profile"] = function () { + var validationStatusArray = []; + var validationStatus; + var operation; + + // starting validation process and updating validationStatus + if (policy["platform"] == platformTypeConstants["ANDROID"]) { + if (configuredOperations.length == 0) { + // updating validationStatus + validationStatus = { + "error": true, + "mainErrorMsg": "You cannot continue. Zero configured features." + }; + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } else { + // validating each and every configured Operation + // Validating PASSCODE_POLICY + if ($.inArray(androidOperationConstants["PASSCODE_POLICY_OPERATION_CODE"], configuredOperations) != -1) { + // if PASSCODE_POLICY is configured + operation = androidOperationConstants["PASSCODE_POLICY_OPERATION"]; + // initializing continueToCheckNextInputs to true + var continueToCheckNextInputs = true; + + // validating first input: passcodePolicyMaxPasscodeAgeInDays + var passcodePolicyMaxPasscodeAgeInDays = $("input#passcode-policy-max-passcode-age-in-days").val(); + if (passcodePolicyMaxPasscodeAgeInDays) { + if (!$.isNumeric(passcodePolicyMaxPasscodeAgeInDays)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode age is not a number.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(passcodePolicyMaxPasscodeAgeInDays, 1, 730)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode age is not with in the range of 1-to-730.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + // validating second and last input: passcodePolicyPasscodeHistory + if (continueToCheckNextInputs) { + var passcodePolicyPasscodeHistory = $("input#passcode-policy-passcode-history").val(); + if (passcodePolicyPasscodeHistory) { + if (!$.isNumeric(passcodePolicyPasscodeHistory)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode history is not a number.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(passcodePolicyPasscodeHistory, 1, 50)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode history is not with in the range of 1-to-50.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating CAMERA + if ($.inArray(androidOperationConstants["CAMERA_OPERATION_CODE"], configuredOperations) != -1) { + // if CAMERA is configured + operation = androidOperationConstants["CAMERA_OPERATION"]; + // updating validationStatus + validationStatus = { + "error": false, + "okFeature": operation + }; + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating ENCRYPT_STORAGE + if ($.inArray(androidOperationConstants["ENCRYPT_STORAGE_OPERATION_CODE"], configuredOperations) != -1) { + // if ENCRYPT_STORAGE is configured + operation = androidOperationConstants["ENCRYPT_STORAGE_OPERATION"]; + // updating validationStatus + validationStatus = { + "error": false, + "okFeature": operation + }; + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating WIFI + if ($.inArray(androidOperationConstants["WIFI_OPERATION_CODE"], configuredOperations) != -1) { + // if WIFI is configured + operation = androidOperationConstants["WIFI_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var wifiSSID = $("input#wifi-ssid").val(); + if (!wifiSSID) { + validationStatus = { + "error": true, + "subErrorMsg": "WIFI SSID is not given. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + + if ($.inArray(androidOperationConstants["VPN_OPERATION_CODE"], configuredOperations) != -1) { + // if WIFI is configured + operation = androidOperationConstants["VPN_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var serverAddress = $("input#vpn-server-address").val(); + if (!serverAddress) { + validationStatus = { + "error": true, + "subErrorMsg": "Server address is required. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + var serverPort = $("input#vpn-server-port").val(); + if (!serverPort) { + validationStatus = { + "error": true, + "subErrorMsg": "VPN server port is required. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!$.isNumeric(serverPort)) { + validationStatus = { + "error": true, + "subErrorMsg": "VPN server port requires a number input.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(serverPort, 0, 65535)) { + validationStatus = { + "error": true, + "subErrorMsg": "VPN server port is not within the range " + + "of valid port numbers.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + if ($.inArray(androidOperationConstants["APPLICATION_OPERATION_CODE"], configuredOperations) != -1) { + //If application restriction configured + operation = androidOperationConstants["APPLICATION_OPERATION"]; + // Initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var appRestrictionType = $("#app-restriction-type").val(); + + var restrictedApplicationsGridChildInputs = "div#restricted-applications .child-input"; + + if (!appRestrictionType) { + validationStatus = { + "error": true, + "subErrorMsg": "Applications restriction type is not provided.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + if ($(restrictedApplicationsGridChildInputs).length == 0) { + validationStatus = { + "error": true, + "subErrorMsg": "Applications are not provided in application restriction list.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + else { + childInputCount = 0; + childInputArray = []; + emptyChildInputCount = 0; + duplicatesExist = false; + // Looping through each child input + $(restrictedApplicationsGridChildInputs).each(function () { + childInputCount++; + if (childInputCount % 2 == 0) { + // If child input is of second column + childInput = $(this).val(); + childInputArray.push(childInput); + // Updating emptyChildInputCount + if (!childInput) { + // If child input field is empty + emptyChildInputCount++; + } + } + }); + // Checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // Updating validationStatus + if (emptyChildInputCount > 0) { + // If empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more package names of " + + "applications are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // If duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with " + + "for package names.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + } + } + + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // Updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + + } + } + }if (policy["platform"] == platformTypeConstants["WINDOWS"]) { + if (configuredOperations.length == 0) { + // updating validationStatus + validationStatus = { + "error": true, + "mainErrorMsg": "You cannot continue. Zero configured features." + }; + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } else { + // validating each and every configured Operation + // Validating PASSCODE_POLICY + if ($.inArray(windowsOperationConstants["PASSCODE_POLICY_OPERATION_CODE"], configuredOperations) != -1) { + // if PASSCODE_POLICY is configured + operation = windowsOperationConstants["PASSCODE_POLICY_OPERATION"]; + // initializing continueToCheckNextInputs to true + var continueToCheckNextInputs = true; + + // validating first input: passcodePolicyMaxPasscodeAgeInDays + var passcodePolicyMaxPasscodeAgeInDays = $("input#passcode-policy-max-passcode-age-in-days").val(); + if (passcodePolicyMaxPasscodeAgeInDays) { + if (!$.isNumeric(passcodePolicyMaxPasscodeAgeInDays)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode age is not a number.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(passcodePolicyMaxPasscodeAgeInDays, 1, 730)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode age is not with in the range of 1-to-730.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + // validating second and last input: passcodePolicyPasscodeHistory + if (continueToCheckNextInputs) { + var passcodePolicyPasscodeHistory = $("input#passcode-policy-passcode-history").val(); + if (passcodePolicyPasscodeHistory) { + if (!$.isNumeric(passcodePolicyPasscodeHistory)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode history is not a number.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(passcodePolicyPasscodeHistory, 1, 50)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode history is not with in the range of 1-to-50.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating CAMERA + if ($.inArray(windowsOperationConstants["CAMERA_OPERATION_CODE"], configuredOperations) != -1) { + // if CAMERA is configured + operation = windowsOperationConstants["CAMERA_OPERATION"]; + // updating validationStatus + validationStatus = { + "error": false, + "okFeature": operation + }; + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating ENCRYPT_STORAGE + if ($.inArray(windowsOperationConstants["ENCRYPT_STORAGE_OPERATION_CODE"], configuredOperations) != -1) { + // if ENCRYPT_STORAGE is configured + operation = windowsOperationConstants["ENCRYPT_STORAGE_OPERATION"]; + // updating validationStatus + validationStatus = { + "error": false, + "okFeature": operation + }; + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + + } + } else if (policy["platform"] == platformTypeConstants["IOS"]) { + if (configuredOperations.length == 0) { + // updating validationStatus + validationStatus = { + "error": true, + "mainErrorMsg": "You cannot continue. Zero configured features." + }; + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } else { + // validating each and every configured Operation + // Validating PASSCODE_POLICY + if ($.inArray(iosOperationConstants["PASSCODE_POLICY_OPERATION_CODE"], configuredOperations) != -1) { + // if PASSCODE_POLICY is configured + operation = iosOperationConstants["PASSCODE_POLICY_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + // validating first input: passcodePolicyMaxPasscodeAgeInDays + passcodePolicyMaxPasscodeAgeInDays = $("input#passcode-policy-max-passcode-age-in-days").val(); + if (passcodePolicyMaxPasscodeAgeInDays) { + if (!$.isNumeric(passcodePolicyMaxPasscodeAgeInDays)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode age is not a number.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(passcodePolicyMaxPasscodeAgeInDays, 1, 730)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode age is not with in the range of 1-to-730.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + // validating second and last input: passcodePolicyPasscodeHistory + if (continueToCheckNextInputs) { + passcodePolicyPasscodeHistory = $("input#passcode-policy-passcode-history").val(); + if (passcodePolicyPasscodeHistory) { + if (!$.isNumeric(passcodePolicyPasscodeHistory)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode history is not a number.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(passcodePolicyPasscodeHistory, 1, 50)) { + validationStatus = { + "error": true, + "subErrorMsg": "Provided passcode history is not with in the range of 1-to-50.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating RESTRICTIONS + if ($.inArray(iosOperationConstants["RESTRICTIONS_OPERATION_CODE"], configuredOperations) != -1) { + // if RESTRICTION is configured + operation = iosOperationConstants["RESTRICTIONS_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + // getting input values to be validated + var restrictionsAutonomousSingleAppModePermittedAppIDsGridChildInputs = + "div#restrictions-autonomous-single-app-mode-permitted-app-ids .child-input"; + if ($(restrictionsAutonomousSingleAppModePermittedAppIDsGridChildInputs).length > 0) { + var childInput; + var childInputArray = []; + var emptyChildInputCount = 0; + var duplicatesExist = false; + // looping through each child input + $(restrictionsAutonomousSingleAppModePermittedAppIDsGridChildInputs).each(function () { + childInput = $(this).val(); + childInputArray.push(childInput); + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + }); + // checking for duplicates + var initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + var m, poppedChildInput; + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + var n; + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more permitted App ID entries in " + + "Autonomous Single App Mode are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with permitted App ID entries in " + + "Autonomous Single App Mode.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating WIFI + if ($.inArray(iosOperationConstants["WIFI_OPERATION_CODE"], configuredOperations) != -1) { + // if WIFI is configured + operation = iosOperationConstants["WIFI_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + // getting input values to be validated + wifiSSID = $("input#wifi-ssid").val(); + var wifiDomainName = $("input#wifi-domain-name").val(); + if (!wifiSSID && !wifiDomainName) { + validationStatus = { + "error": true, + "subErrorMsg": "Both Wi-Fi SSID and Wi-Fi Domain Name are not given. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + // getting proxy-setup value + var wifiProxyType = $("select#wifi-proxy-type").find("option:selected").attr("value"); + if (wifiProxyType == "Manual") { + // adds up additional fields to be validated + var wifiProxyServer = $("input#wifi-proxy-server").val(); + if (!wifiProxyServer) { + validationStatus = { + "error": true, + "subErrorMsg": "Wi-Fi Proxy Server is required. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + var wifiProxyPort = $("input#wifi-proxy-port").val(); + if (!wifiProxyPort) { + validationStatus = { + "error": true, + "subErrorMsg": "Wi-Fi Proxy Port is required. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!$.isNumeric(wifiProxyPort)) { + validationStatus = { + "error": true, + "subErrorMsg": "Wi-Fi Proxy Port requires a number input.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(wifiProxyPort, 0, 65535)) { + validationStatus = { + "error": true, + "subErrorMsg": "Wi-Fi Proxy Port is not within the range " + + "of valid port numbers.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + } + + if (continueToCheckNextInputs) { + // getting encryption-type value + var wifiEncryptionType = $("select#wifi-encryption-type").find("option:selected").attr("value"); + if (wifiEncryptionType != "None") { + var wifiPayloadCertificateAnchorUUIDsGridChildInputs = + "div#wifi-payload-certificate-anchor-uuids .child-input"; + if ($(wifiPayloadCertificateAnchorUUIDsGridChildInputs).length > 0) { + emptyChildInputCount = 0; + childInputArray = []; + duplicatesExist = false; + // looping through each child input + $(wifiPayloadCertificateAnchorUUIDsGridChildInputs).each(function () { + childInput = $(this).val(); + childInputArray.push(childInput); + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more Payload Certificate " + + "Anchor UUIDs are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist " + + "with Payload Certificate Anchor UUIDs.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + if (continueToCheckNextInputs) { + var wifiTLSTrustedServerNamesGridChildInputs = + "div#wifi-tls-trusted-server-names .child-input"; + if ($(wifiTLSTrustedServerNamesGridChildInputs).length > 0) { + emptyChildInputCount = 0; + childInputArray = []; + duplicatesExist = false; + // looping through each child input + $(wifiTLSTrustedServerNamesGridChildInputs).each(function () { + childInput = $(this).val(); + childInputArray.push(childInput); + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more TLS Trusted Server Names are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist " + + "with TLS Trusted Server Names.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + } + } + + if (continueToCheckNextInputs) { + var wifiRoamingConsortiumOIsGridChildInputs = "div#wifi-roaming-consortium-ois .child-input"; + if ($(wifiRoamingConsortiumOIsGridChildInputs).length > 0) { + emptyChildInputCount = 0; + var outOfAllowedLengthCount = 0; + var invalidAgainstRegExCount = 0; + childInputArray = []; + duplicatesExist = false; + // looping through each child input + $(wifiRoamingConsortiumOIsGridChildInputs).each(function () { + childInput = $(this).val(); + childInputArray.push(childInput); + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } else if (!inputIsValidAgainstLength(childInput, 6, 6) && !inputIsValidAgainstLength(childInput, 10, 10)) { + outOfAllowedLengthCount++; + } else if (!inputIsValidAgainstRegExp(/^[a-fA-F0-9]+$/, childInput)) { + invalidAgainstRegExCount++; + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more Roaming Consortium OIs are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (outOfAllowedLengthCount > 0) { + // if outOfMaxAllowedLength input is present + validationStatus = { + "error": true, + "subErrorMsg": "One or more Roaming Consortium OIs " + + "are out of allowed length.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (invalidAgainstRegExCount > 0) { + // if invalid inputs in terms of hexadecimal format are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more Roaming Consortium OIs " + + "contain non-hexadecimal characters.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with Roaming Consortium OIs.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + if (continueToCheckNextInputs) { + var wifiNAIRealmNamesGridChildInputs = "div#wifi-nai-realm-names .child-input"; + if ($(wifiNAIRealmNamesGridChildInputs).length > 0) { + emptyChildInputCount = 0; + childInputArray = []; + duplicatesExist = false; + // looping through each child input + $(wifiNAIRealmNamesGridChildInputs).each(function () { + childInput = $(this).val(); + childInputArray.push(childInput); + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more NAI Realm Names are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with NAI Realm Names.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + if (continueToCheckNextInputs) { + var wifiMCCAndMNCsGridChildInputs = "div#wifi-mcc-and-mncs .child-input"; + if ($(wifiMCCAndMNCsGridChildInputs).length > 0) { + var childInputCount = 0; + var stringPair; + emptyChildInputCount = 0; + outOfAllowedLengthCount = 0; + var notNumericInputCount = 0; + childInputArray = []; + duplicatesExist = false; + // looping through each child input + $(wifiMCCAndMNCsGridChildInputs).each(function () { + childInput = $(this).val(); + // pushing each string pair to childInputArray + childInputCount++; + if (childInputCount % 2 == 1) { + // initialize stringPair value + stringPair = ""; + // append first part of the string + stringPair += childInput; + } else { + // append second part of the string + stringPair += childInput; + childInputArray.push(stringPair); + } + // updating emptyChildInputCount & outOfAllowedLengthCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } else if (!$.isNumeric(childInput)) { + notNumericInputCount++; + } else if (!inputIsValidAgainstLength(childInput, 3, 3)) { + outOfAllowedLengthCount++; + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more MCC/MNC pairs are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (notNumericInputCount > 0) { + // if notNumeric input is present + validationStatus = { + "error": true, + "subErrorMsg": "One or more MCC/MNC pairs are not numeric.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (outOfAllowedLengthCount > 0) { + // if outOfAllowedLength input is present + validationStatus = { + "error": true, + "subErrorMsg": "One or more MCC/MNC pairs " + + "do not fulfill the accepted length of 6 digits.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with MCC/MNC pairs.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + + if ($.inArray(iosOperationConstants["VPN_OPERATION_CODE"], configuredOperations) != -1) { + // if WIFI is configured + operation = iosOperationConstants["VPN_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var connectionName = $("input#vpn-connection-name").val(); + if (!connectionName) { + validationStatus = { + "error": true, + "subErrorMsg": "Connection Name is required. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + + if ($.inArray(iosOperationConstants["PER_APP_VPN_OPERATION_CODE"], configuredOperations) != -1) { + operation = iosOperationConstants["PER_APP_VPN_OPERATION"]; + continueToCheckNextInputs = true; + + var uuid = $("input#per-app-vpn-uuid").val(); + if (!uuid) { + validationStatus = { + "error": true, + "subErrorMsg": "VPN UUID is required. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + validationStatusArray.push(validationStatus); + } + + if ($.inArray(iosOperationConstants["APP_TO_PER_APP_VPN_MAPPING_OPERATION_CODE"], configuredOperations) != -1) { + operation = iosOperationConstants["APP_TO_PER_APP_VPN_MAPPING_OPERATION"]; + continueToCheckNextInputs = true; + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + validationStatusArray.push(validationStatus); + } + // Validating EMAIL + if ($.inArray(iosOperationConstants["EMAIL_OPERATION_CODE"], configuredOperations) != -1) { + // if EMAIL is configured + operation = iosOperationConstants["EMAIL_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var emailAddress = $("input#email-address").val(); + if (emailAddress && !inputIsValidAgainstRegExp(/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/, emailAddress)) { + validationStatus = { + "error": true, + "subErrorMsg": "Email Address is not valid.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + var emailIncomingMailServerHostname = $("input#email-incoming-mail-server-hostname").val(); + if (!emailIncomingMailServerHostname) { + validationStatus = { + "error": true, + "subErrorMsg": "Incoming Mail Server Hostname is empty. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + if (continueToCheckNextInputs) { + var emailIncomingMailServerPort = $("input#email-incoming-mail-server-port").val(); + if (emailIncomingMailServerPort && emailIncomingMailServerPort != '') { + if (!$.isNumeric(emailIncomingMailServerPort)) { + validationStatus = { + "error": true, + "subErrorMsg": "Incoming Mail Server Port requires a number input.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(emailIncomingMailServerPort, 0, 65535)) { + validationStatus = { + "error": true, + "subErrorMsg": "Incoming Mail Server Port is not within the range " + + "of valid port numbers.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + if (continueToCheckNextInputs) { + var emailOutgoingMailServerHostname = $("input#email-outgoing-mail-server-hostname").val(); + if (!emailOutgoingMailServerHostname) { + validationStatus = { + "error": true, + "subErrorMsg": "Outgoing Mail Server Hostname is empty. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + if (continueToCheckNextInputs) { + var emailOutgoingMailServerPort = $("input#email-outgoing-mail-server-port").val(); + if (emailOutgoingMailServerPort) { + if (!$.isNumeric(emailOutgoingMailServerPort)) { + validationStatus = { + "error": true, + "subErrorMsg": "Outgoing Mail Server Port requires a number input.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(emailOutgoingMailServerPort, 0, 65535)) { + validationStatus = { + "error": true, + "subErrorMsg": "Outgoing Mail Server Port is not within the range " + + "of valid port numbers.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating AIRPLAY + if ($.inArray(iosOperationConstants["AIRPLAY_OPERATION_CODE"], configuredOperations) != -1) { + // if AIRPLAY is configured + operation = iosOperationConstants["AIRPLAY_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var airplayCredentialsGridChildInputs = "div#airplay-credentials .child-input"; + var airplayDestinationsGridChildInputs = "div#airplay-destinations .child-input"; + if ($(airplayCredentialsGridChildInputs).length == 0 && + $(airplayDestinationsGridChildInputs).length == 0) { + validationStatus = { + "error": true, + "subErrorMsg": "AirPlay settings have zero configurations attached.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + if ($(airplayCredentialsGridChildInputs).length > 0) { + childInputCount = 0; + childInputArray = []; + emptyChildInputCount = 0; + duplicatesExist = false; + // looping through each child input + $(airplayCredentialsGridChildInputs).each(function () { + childInputCount++; + if (childInputCount % 2 == 1) { + // if child input is of first column + childInput = $(this).val(); + childInputArray.push(childInput); + // updating emptyChildInputCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more Device Names of " + + "AirPlay Credentials are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // if duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with " + + "Device Names of AirPlay Credentials.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + if (continueToCheckNextInputs) { + if ($(airplayDestinationsGridChildInputs).length > 0) { + childInputArray = []; + emptyChildInputCount = 0; + invalidAgainstRegExCount = 0; + duplicatesExist = false; + // looping through each child input + $(airplayDestinationsGridChildInputs).each(function () { + childInput = $(this).val(); + childInputArray.push(childInput); + // updating emptyChildInputCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } else if (!inputIsValidAgainstRegExp( + /([a-z|A-Z|0-9][a-z|A-Z|0-9][:]){5}([a-z|A-Z|0-9][a-z|A-Z|0-9])$/, childInput)) { + // if child input field is invalid against RegEx + invalidAgainstRegExCount++ + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more AirPlay Destination fields are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (invalidAgainstRegExCount > 0) { + // if invalidAgainstRegEx inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more AirPlay Destination fields " + + "do not fulfill expected format.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // if duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with AirPlay Destinations.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + + // Validating Domains + if ($.inArray(iosOperationConstants["DOMAIN"], configuredOperations) != -1) { + // if DOMAIN is configured + operation = iosOperationConstants["DOMAIN"]; + + continueToCheckNextInputs = true; + + var airplayCredentialsGridChildInputs = "div#unmarked-email-domains .child-input"; + var airplayDestinationsGridChildInputs = "div#safari-web-domains .child-input"; + if ($(airplayCredentialsGridChildInputs).length == 0 && + $(airplayDestinationsGridChildInputs).length == 0) { + validationStatus = { + "error": true, + "subErrorMsg": "Manage Domains have zero configurations attached.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + if ($(airplayCredentialsGridChildInputs).length > 0) { + childInputCount = 0; + childInputArray = []; + emptyChildInputCount = 0; + duplicatesExist = false; + // looping through each child input + $(airplayCredentialsGridChildInputs).each(function () { + childInputCount++; + if (childInputCount % 2 == 1) { + // if child input is of first column + childInput = $(this).val(); + childInputArray.push(childInput); + // updating emptyChildInputCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more email domains of " + + "unmarked email domains are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // if duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with " + + "email domains of unmarked email domains.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + if (continueToCheckNextInputs) { + if ($(airplayDestinationsGridChildInputs).length > 0) { + childInputCount = 0; + childInputArray = []; + emptyChildInputCount = 0; + duplicatesExist = false; + // looping through each child input + $(airplayDestinationsGridChildInputs).each(function () { + childInputCount++; + if (childInputCount % 2 == 1) { + // if child input is of first column + childInput = $(this).val(); + childInputArray.push(childInput); + // updating emptyChildInputCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more managed safari web domains of " + + "unmarked email domains are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // if duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with " + + "managed safari web domains of unmarked email domains.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + validationStatusArray.push(validationStatus); + } + + // Validating LDAP + if ($.inArray(iosOperationConstants["LDAP_OPERATION_CODE"], configuredOperations) != -1) { + // if LDAP is configured + operation = iosOperationConstants["LDAP_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var ldapAccountHostname = $("input#ldap-account-hostname").val(); + if (!ldapAccountHostname) { + validationStatus = { + "error": true, + "subErrorMsg": "LDAP Account Hostname URL is empty. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + var ldapSearchSettingsGridChildInputs = "div#ldap-search-settings .child-input"; + if ($(ldapSearchSettingsGridChildInputs).length > 0) { + childInputCount = 0; + childInputArray = []; + emptyChildInputCount = 0; + duplicatesExist = false; + // looping through each child input + $(ldapSearchSettingsGridChildInputs).each(function () { + childInputCount++; + if (childInputCount % 3 == 2) { + // if child input is of second column + childInput = $(this).find("option:selected").attr("value"); + stringPair = ""; + stringPair += (childInput + " "); + } else if (childInputCount % 3 == 0) { + // if child input is of third column + childInput = $(this).val(); + stringPair += childInput; + childInputArray.push(stringPair); + // updating emptyChildInputCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more Search Setting Scope fields are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // if duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with " + + "Search Setting Search Base and Scope pairs.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating CALENDAR + if ($.inArray(iosOperationConstants["CALENDAR_OPERATION_CODE"], configuredOperations) != -1) { + // if CALENDAR is configured + operation = iosOperationConstants["CALENDAR_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var calendarAccountHostname = $("input#calendar-account-hostname").val(); + if (!calendarAccountHostname) { + validationStatus = { + "error": true, + "subErrorMsg": "Account Hostname URL is empty. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + var calendarAccountPort = $("input#calendar-account-port").val(); + if (!calendarAccountPort) { + validationStatus = { + "error": true, + "subErrorMsg": "Account Port is empty. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!$.isNumeric(calendarAccountPort)) { + validationStatus = { + "error": true, + "subErrorMsg": "Account Port requires a number input.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (!inputIsValidAgainstRange(calendarAccountPort, 0, 65535)) { + validationStatus = { + "error": true, + "subErrorMsg": "Account Port is not within the range " + + "of valid port numbers.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating CALENDAR_SUBSCRIPTION + if ($.inArray(iosOperationConstants["CALENDAR_SUBSCRIPTION_OPERATION_CODE"], configuredOperations) != -1) { + // if CALENDAR_SUBSCRIPTION is configured + operation = iosOperationConstants["CALENDAR_SUBSCRIPTION_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var calendarSubscriptionHostname = $("input#calendar-subscription-hostname").val(); + if (!calendarSubscriptionHostname) { + validationStatus = { + "error": true, + "subErrorMsg": "Account Hostname URL is empty. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating APN + if ($.inArray(iosOperationConstants["APN_OPERATION_CODE"], configuredOperations) != -1) { + // if APN is configured + operation = iosOperationConstants["APN_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var apnConfigurationsGridChildInputs = "div#apn-configurations .child-input"; + if ($(apnConfigurationsGridChildInputs).length == 0) { + validationStatus = { + "error": true, + "subErrorMsg": "APN Settings have zero configurations attached.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if ($(apnConfigurationsGridChildInputs).length > 0) { + childInputCount = 0; + childInputArray = []; + // checking empty APN field count + emptyChildInputCount = 0; + duplicatesExist = false; + // looping through each child input + $(apnConfigurationsGridChildInputs).each(function () { + childInputCount++; + //if (childInputCount % 5 == 1) { + // if child input is of first column + childInput = $(this).val(); + childInputArray.push(childInput); + // updating emptyChildInputCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + //} + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more APN fields of Configurations are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // if duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with " + + "APN fields of Configurations.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + // Validating CELLULAR + if ($.inArray(iosOperationConstants["CELLULAR_OPERATION_CODE"], configuredOperations) != -1) { + // if CELLULAR is configured + operation = iosOperationConstants["CELLULAR_OPERATION"]; + // initializing continueToCheckNextInputs to true + continueToCheckNextInputs = true; + + var cellularAttachAPNName = $("input#cellular-attach-apn-name").val(); + if (!cellularAttachAPNName) { + validationStatus = { + "error": true, + "subErrorMsg": "Cellular Configuration Name is empty. You cannot proceed.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + + if (continueToCheckNextInputs) { + var cellularAPNConfigurationsGridChildInputs = "div#cellular-apn-configurations .child-input"; + if ($(cellularAPNConfigurationsGridChildInputs).length > 0) { + childInputCount = 0; + childInputArray = []; + // checking empty APN field count + emptyChildInputCount = 0; + duplicatesExist = false; + // looping through each child input + $(cellularAPNConfigurationsGridChildInputs).each(function () { + childInputCount++; + if (childInputCount % 6 == 1) { + // if child input is of first column + childInput = $(this).val(); + childInputArray.push(childInput); + // updating emptyChildInputCount + if (!childInput) { + // if child input field is empty + emptyChildInputCount++; + } + } + }); + // checking for duplicates + initialChildInputArrayLength = childInputArray.length; + if (emptyChildInputCount == 0 && initialChildInputArrayLength > 1) { + for (m = 0; m < (initialChildInputArrayLength - 1); m++) { + poppedChildInput = childInputArray.pop(); + for (n = 0; n < childInputArray.length; n++) { + if (poppedChildInput == childInputArray[n]) { + duplicatesExist = true; + break; + } + } + if (duplicatesExist) { + break; + } + } + } + // updating validationStatus + if (emptyChildInputCount > 0) { + // if empty child inputs are present + validationStatus = { + "error": true, + "subErrorMsg": "One or more APN fields of APN Configurations are empty.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } else if (duplicatesExist) { + // if duplicate input is present + validationStatus = { + "error": true, + "subErrorMsg": "Duplicate values exist with " + + "APN fields of APN Configurations.", + "erroneousFeature": operation + }; + continueToCheckNextInputs = false; + } + } + } + + // at-last, if the value of continueToCheckNextInputs is still true + // this means that no error is found + if (continueToCheckNextInputs) { + validationStatus = { + "error": false, + "okFeature": operation + }; + } + + // updating validationStatusArray with validationStatus + validationStatusArray.push(validationStatus); + } + } + } + // ending validation process + + // start taking specific notifying actions upon validation + var wizardIsToBeContinued; + var errorCount = 0; + var mainErrorMsgWrapper, mainErrorMsg, + subErrorMsgWrapper, subErrorMsg, subErrorIcon, subOkIcon, featureConfiguredIcon; + var i; + for (i = 0; i < validationStatusArray.length; i++) { + validationStatus = validationStatusArray[i]; + if (validationStatus["error"]) { + errorCount++; + if (validationStatus["mainErrorMsg"]) { + mainErrorMsgWrapper = "#policy-profile-main-error-msg"; + mainErrorMsg = mainErrorMsgWrapper + " span"; + $(mainErrorMsg).text(validationStatus["mainErrorMsg"]); + $(mainErrorMsgWrapper).removeClass("hidden"); + } else if (validationStatus["subErrorMsg"]) { + subErrorMsgWrapper = "#" + validationStatus["erroneousFeature"] + "-feature-error-msg"; + subErrorMsg = subErrorMsgWrapper + " span"; + subErrorIcon = "#" + validationStatus["erroneousFeature"] + "-error"; + subOkIcon = "#" + validationStatus["erroneousFeature"] + "-ok"; + featureConfiguredIcon = "#" + validationStatus["erroneousFeature"] + "-configured"; + // hiding featureConfiguredState as the first step + if (!$(featureConfiguredIcon).hasClass("hidden")) { + $(featureConfiguredIcon).addClass("hidden"); + } + // updating error state and corresponding messages + $(subErrorMsg).text(validationStatus["subErrorMsg"]); + if ($(subErrorMsgWrapper).hasClass("hidden")) { + $(subErrorMsgWrapper).removeClass("hidden"); + } + if (!$(subOkIcon).hasClass("hidden")) { + $(subOkIcon).addClass("hidden"); + } + if ($(subErrorIcon).hasClass("hidden")) { + $(subErrorIcon).removeClass("hidden"); + } + } + } else { + if (validationStatus["okFeature"]) { + subErrorMsgWrapper = "#" + validationStatus["okFeature"] + "-feature-error-msg"; + subErrorIcon = "#" + validationStatus["okFeature"] + "-error"; + subOkIcon = "#" + validationStatus["okFeature"] + "-ok"; + featureConfiguredIcon = "#" + validationStatus["okFeature"] + "-configured"; + // hiding featureConfiguredState as the first step + if (!$(featureConfiguredIcon).hasClass("hidden")) { + $(featureConfiguredIcon).addClass("hidden"); + } + // updating success state and corresponding messages + if (!$(subErrorMsgWrapper).hasClass("hidden")) { + $(subErrorMsgWrapper).addClass("hidden"); + } + if (!$(subErrorIcon).hasClass("hidden")) { + $(subErrorIcon).addClass("hidden"); + } + if ($(subOkIcon).hasClass("hidden")) { + $(subOkIcon).removeClass("hidden"); + } + } + } + } + + wizardIsToBeContinued = (errorCount == 0); + return wizardIsToBeContinued; +}; + +stepForwardFrom["policy-profile"] = function () { + policy["profile"] = operationModule.generateProfile(policy["platform"], configuredOperations); + // updating next-page wizard title with selected platform + $("#policy-criteria-page-wizard-title").text("ADD " + policy["platform"] + " POLICY"); + // updating ownership type options according to platform + if (policy["platform"] == platformTypeConstants["IOS"] || + policy["platform"] == platformTypeConstants["WINDOWS"]) { + var ownershipTypeSelectOptions = $("#ownership-input"); + ownershipTypeSelectOptions.empty(); + ownershipTypeSelectOptions.append($(""). + attr("value", "BYOD").text("BYOD (Bring Your Own Device)")); + ownershipTypeSelectOptions.attr("disabled", true); + } +}; + +stepBackFrom["policy-profile"] = function () { + // reinitialize configuredOperations + configuredOperations = []; + // clearing already-loaded platform specific hidden-operations html content from the relevant div + // so that, the wrong content would not be shown at the first glance, in case + // the user selects a different platform + $(".wr-advance-operations").html( + "
    " + + "
    " + + "" + + "Loading Platform Features . . ." + + "
    " + + "
    " + + "
    " + ); +}; + +stepForwardFrom["policy-criteria"] = function () { + $("input[type='radio'].select-users-radio").each(function () { + if ($(this).is(':radio')) { + if ($(this).is(":checked")) { + if ($(this).attr("id") == "users-radio-btn") { + policy["selectedUsers"] = $("#users-input").val(); + policy["selectedUserRoles"] = null; + } else if ($(this).attr("id") == "user-roles-radio-btn") { + policy["selectedUsers"] = null; + policy["selectedUserRoles"] = $("#user-roles-input").val(); + } + } + } + }); + policy["selectedNonCompliantAction"] = $("#action-input").find(":selected").data("action"); + policy["selectedOwnership"] = $("#ownership-input").val(); + //updating next-page wizard title with selected platform + $("#policy-naming-page-wizard-title").text("ADD " + policy["platform"] + " POLICY"); +}; + +/** + * Checks if provided input is valid against provided length range. + * + * @param input Alphanumeric or non-alphanumeric input + * @param minLength Minimum Required Length + * @param maxLength Maximum Required Length + * @returns {boolean} Returns true if input matches the provided minimum length and maximum length + */ +var inputIsValidAgainstLength = function (input, minLength, maxLength) { + var length = input.length; + return (length == minLength || (length > minLength && length < maxLength) || length == maxLength); +}; + +validateStep["policy-criteria"] = function () { + var validationStatus = {}; + var selectedAssignees; + var selectedField = "Role(s)"; + + $("input[type='radio'].select-users-radio").each(function () { + if ($(this).is(":checked")) { + if ($(this).attr("id") == "users-radio-btn") { + selectedAssignees = $("#users-input").val(); + selectedField = "User(s)"; + } else if ($(this).attr("id") == "user-roles-radio-btn") { + selectedAssignees = $("#user-roles-input").val(); + } + return false; + } + }); + + if (selectedAssignees) { + validationStatus["error"] = false; + } else { + validationStatus["error"] = true; + validationStatus["mainErrorMsg"] = selectedField + " is a required field. It cannot be empty"; + } + + var wizardIsToBeContinued; + if (validationStatus["error"]) { + wizardIsToBeContinued = false; + var mainErrorMsgWrapper = "#policy-criteria-main-error-msg"; + var mainErrorMsg = mainErrorMsgWrapper + " span"; + $(mainErrorMsg).text(validationStatus["mainErrorMsg"]); + $(mainErrorMsgWrapper).removeClass("hidden"); + } else { + wizardIsToBeContinued = true; + } + + return wizardIsToBeContinued; +}; + +validateStep["policy-naming"] = function () { + var validationStatus = {}; + + // taking values of inputs to be validated + var policyName = $("input#policy-name-input").val(); + // starting validation process and updating validationStatus + if (!policyName) { + validationStatus["error"] = true; + validationStatus["mainErrorMsg"] = "Policy name is empty. You cannot proceed."; + } else if (!inputIsValidAgainstLength(policyName, 1, 30)) { + validationStatus["error"] = true; + validationStatus["mainErrorMsg"] = + "Policy name exceeds maximum allowed length."; + } else { + validationStatus["error"] = false; + } + // ending validation process + + // start taking specific actions upon validation + var wizardIsToBeContinued; + if (validationStatus["error"]) { + wizardIsToBeContinued = false; + var mainErrorMsgWrapper = "#policy-naming-main-error-msg"; + var mainErrorMsg = mainErrorMsgWrapper + " span"; + $(mainErrorMsg).text(validationStatus["mainErrorMsg"]); + $(mainErrorMsgWrapper).removeClass("hidden"); + } else { + wizardIsToBeContinued = true; + } + + return wizardIsToBeContinued; +}; + +validateStep["policy-platform"] = function () { + return false; +}; + +validateStep["policy-naming-publish"] = function () { + var validationStatus = {}; + + // taking values of inputs to be validated + var policyName = $("input#policy-name-input").val(); + // starting validation process and updating validationStatus + if (!policyName) { + validationStatus["error"] = true; + validationStatus["mainErrorMsg"] = "Policy name is empty. You cannot proceed."; + } else if (!inputIsValidAgainstLength(policyName, 1, 30)) { + validationStatus["error"] = true; + validationStatus["mainErrorMsg"] = + "Policy name exceeds maximum allowed length."; + } else { + validationStatus["error"] = false; + } + // ending validation process + + // start taking specific actions upon validation + var wizardIsToBeContinued; + if (validationStatus["error"]) { + wizardIsToBeContinued = false; + var mainErrorMsgWrapper = "#policy-naming-main-error-msg"; + var mainErrorMsg = mainErrorMsgWrapper + " span"; + $(mainErrorMsg).text(validationStatus["mainErrorMsg"]); + $(mainErrorMsgWrapper).removeClass("hidden"); + } else { + wizardIsToBeContinued = true; + } + + return wizardIsToBeContinued; +}; + +stepForwardFrom["policy-naming-publish"] = function () { + policy["policyName"] = $("#policy-name-input").val(); + policy["description"] = $("#policy-description-input").val(); + //All data is collected. Policy can now be updated. + savePolicy(policy, true, "/api/device-mgt/v1.0/policies/"); +}; + +stepForwardFrom["policy-naming"] = function () { + policy["policyName"] = $("#policy-name-input").val(); + policy["description"] = $("#policy-description-input").val(); + //All data is collected. Policy can now be updated. + savePolicy(policy, false, "/api/device-mgt/v1.0/policies/"); +}; + +var savePolicy = function (policy, isActive, serviceURL) { + var profilePayloads = []; + // traverses key by key in policy["profile"] + var key; + for (key in policy["profile"]) { + if (policy["platformId"] == platformIds["WINDOWS"] && + key == windowsOperationConstants["PASSCODE_POLICY_OPERATION_CODE"]) { + policy["profile"][key].enablePassword = true; + } + if (policy["profile"].hasOwnProperty(key)) { + profilePayloads.push({ + "featureCode": key, + "deviceType": policy["platform"], + "content": policy["profile"][key] + }); + } + } + + $.each(profilePayloads, function (i, item) { + $.each(item.content, function (key, value) { + //cannot add a true check since it will catch value = false as well + if (value === null || value === undefined || value === "") { + item.content[key] = null; + } + }); + }); + + var payload = { + "policyName": policy["policyName"], + "description": policy["description"], + "compliance": policy["selectedNonCompliantAction"], + "ownershipType": policy["selectedOwnership"], + "active": isActive, + "profile": { + "profileName": policy["policyName"], + "deviceType": policy["platform"], + "profileFeaturesList": profilePayloads + } + }; + + if (policy["selectedUsers"]) { + payload["users"] = policy["selectedUsers"]; + } else if (policy["selectedUserRoles"]) { + payload["roles"] = policy["selectedUserRoles"]; + } else { + payload["users"] = []; + payload["roles"] = []; + } + + console.log(JSON.stringify(payload)); + + invokerUtil.post( + serviceURL, + payload, + function () { + $(".add-policy").addClass("hidden"); + $(".policy-naming").addClass("hidden"); + $(".policy-message").removeClass("hidden"); + }, + function (data) { + } + ); +}; + +// Start of HTML embedded invoke methods +var showAdvanceOperation = function (operation, button) { + $(button).addClass('selected'); + $(button).siblings().removeClass('selected'); + var hiddenOperation = ".wr-hidden-operations-content > div"; + $(hiddenOperation + '[data-operation="' + operation + '"]').show(); + $(hiddenOperation + '[data-operation="' + operation + '"]').siblings().hide(); +}; + + +/** + * This method will display appropriate fields based on wifi type + * @param select + */ +var changeAndroidWifiPolicy = function (select) { + slideDownPaneAgainstValueSet(select, 'control-wifi-password', ['wep', 'wpa', '802eap']); + slideDownPaneAgainstValueSet(select, 'control-wifi-eap', ['802eap']); + slideDownPaneAgainstValueSet(select, 'control-wifi-phase2', ['802eap']); + slideDownPaneAgainstValueSet(select, 'control-wifi-identity', ['802eap']); + slideDownPaneAgainstValueSet(select, 'control-wifi-anoidentity', ['802eap']); + slideDownPaneAgainstValueSet(select, 'control-wifi-cacert', ['802eap']); +}; + +/** + * This method will display appropriate fields based on wifi EAP type + * @param select + * @param superSelect + */ +var changeAndroidWifiPolicyEAP = function (select, superSelect) { + slideDownPaneAgainstValueSet(select, 'control-wifi-password', ['peap', 'ttls', 'pwd' ,'fast', 'leap']); + slideDownPaneAgainstValueSet(select, 'control-wifi-phase2', ['peap', 'ttls', 'fast']); + slideDownPaneAgainstValueSet(select, 'control-wifi-provisioning', ['fast']); + slideDownPaneAgainstValueSet(select, 'control-wifi-identity', ['peap', 'tls', 'ttls', 'pwd', 'fast', 'leap']); + slideDownPaneAgainstValueSet(select, 'control-wifi-anoidentity', ['peap', 'ttls']); + slideDownPaneAgainstValueSet(select, 'control-wifi-cacert', ['peap', 'tls', 'ttls']); + if (superSelect.value != '802eap') { + changeAndroidWifiPolicy(superSelect); + } +}; + +/** + * This method will encode the file-input and enter the values to given input files + * @param fileInput + * @param fileHiddenInput + * @param fileNameHiddenInput + */ +var base64EncodeFile = function (fileInput, fileHiddenInput, fileNameHiddenInput) { + var file = fileInput.files[0]; + if (file) { + var reader = new FileReader(); + reader.onload = function(readerEvt) { + var binaryString = readerEvt.target.result; + fileHiddenInput.value = (btoa(binaryString)); + fileNameHiddenInput.value = file.name.substr(0,file.name.lastIndexOf(".")); + }; + reader.readAsBinaryString(file); + } +}; + +/** + * Method to slide down a provided pane upon provided value set. + * + * @param selectElement Select HTML Element to consider + * @param paneID HTML ID of div element to slide down + * @param valueSet Applicable Value Set + */ +var slideDownPaneAgainstValueSet = function (selectElement, paneID, valueSet) { + var selectedValueOnChange = $(selectElement).find("option:selected").val(); + if ($(selectElement).is("input:checkbox")) { + selectedValueOnChange = $(selectElement).is(":checked").toString(); + } + + var i, slideDownVotes = 0; + for (i = 0; i < valueSet.length; i++) { + if (selectedValueOnChange == valueSet[i]) { + slideDownVotes++; + } + } + var paneSelector = "#" + paneID; + if (slideDownVotes > 0) { + if (!$(paneSelector).hasClass("expanded")) { + $(paneSelector).addClass("expanded"); + } + $(paneSelector).slideDown(); + } else { + if ($(paneSelector).hasClass("expanded")) { + $(paneSelector).removeClass("expanded"); + } + $(paneSelector).slideUp(); + /** now follows the code to reinitialize all inputs of the slidable pane */ + // reinitializing input fields into the defaults + $(paneSelector + " input").each( + function () { + if ($(this).is("input:text")) { + $(this).val($(this).data("default")); + } else if ($(this).is("input:password")) { + $(this).val(""); + } else if ($(this).is("input:checkbox")) { + $(this).prop("checked", $(this).data("default")); + // if this checkbox is the parent input of a grouped-input + if ($(this).hasClass("parent-input")) { + var groupedInput = $(this).parent().parent().parent(); + updateGroupedInputVisibility(groupedInput); + } + } + } + ); + // reinitializing select fields into the defaults + $(paneSelector + " select").each( + function () { + var defaultOption = $(this).data("default"); + $("option:eq(" + defaultOption + ")", this).prop("selected", "selected"); + } + ); + // collapsing expanded-panes (upon the selection of html-select-options) if any + $(paneSelector + " .expanded").each( + function () { + if ($(this).hasClass("expanded")) { + $(this).removeClass("expanded"); + } + $(this).slideUp(); + } + ); + // removing all entries of grid-input elements if exist + $(paneSelector + " .grouped-array-input").each( + function () { + var gridInputs = $(this).find("[data-add-form-clone]"); + if (gridInputs.length > 0) { + gridInputs.remove(); + } + var helpTexts = $(this).find("[data-help-text=add-form]"); + if (helpTexts.length > 0) { + helpTexts.show(); + } + } + ); + } +}; +// End of HTML embedded invoke methods + + +// Start of functions related to grid-input-view + +/** + * Method to set count id to cloned elements. + * @param {object} addFormContainer + */ +var setId = function (addFormContainer) { + $(addFormContainer).find("[data-add-form-clone]").each(function (i) { + $(this).attr("id", $(this).attr("data-add-form-clone").slice(1) + "-" + (i + 1)); + if ($(this).find(".index").length > 0) { + $(this).find(".index").html(i + 1); + } + }); +}; + +/** + * Method to set count id to cloned elements. + * @param {object} addFormContainer + */ +var showHideHelpText = function (addFormContainer) { + var helpText = "[data-help-text=add-form]"; + if ($(addFormContainer).find("[data-add-form-clone]").length > 0) { + $(addFormContainer).find(helpText).hide(); + } else { + $(addFormContainer).find(helpText).show(); + } +}; + +function formatRepo(user) { + if (user.loading) { + return user.text; + } + if (!user.username) { + return; + } + var markup = '
    ' + + '
    ' + + '
    ' + + '
    ' + user.username + '
    '; + if (user.firstname) { + markup += '
    ' + user.firstname + '
    '; + } + if (user.emailAddress) { + markup += '
    ' + user.emailAddress + '
    '; + } + markup += '
    '; + return markup; +} + +function formatRepoSelection(user) { + return user.username || user.text; +} + +function promptErrorPolicyPlatform(errorMsg) { + var mainErrorMsgWrapper = "#policy-platform-main-error-msg"; + var mainErrorMsg = mainErrorMsgWrapper + " span"; + $(mainErrorMsg).text(errorMsg); + $(mainErrorMsgWrapper).removeClass("hidden"); +} + +// End of functions related to grid-input-view + + +$(document).ready(function () { + $("#users-input").select2({ + multiple: true, + tags: false, + ajax: { + url: "/emm/api/invoker/execute/", + method: "POST", + dataType: 'json', + delay: 250, + id: function (user) { + return user.username; + }, + data: function (params) { + var postData = {}; + postData.actionMethod = "GET"; + postData.actionUrl = "/api/device-mgt/v1.0/users/search/usernames?filter=" + params.term; + postData.actionPayload = null; + return JSON.stringify(postData); + }, + processResults: function (data) { + var newData = []; + $.each(data, function (index, value) { + value.id = value.username; + newData.push(value); + }); + return { + results: newData + }; + }, + cache: true + }, + escapeMarkup: function (markup) { + return markup; + }, // let our custom formatter work + minimumInputLength: 1, + templateResult: formatRepo, // omitted for brevity, see the source of this page + templateSelection: formatRepoSelection // omitted for brevity, see the source of this page + }); + + $("#loading-content").remove(); + $(".policy-platform").removeClass("hidden"); + // Adding initial state of wizard-steps. + $("#policy-platform-wizard-steps").html($(".wr-steps").html()); + + $("select.select2[multiple=multiple]").select2({ + "tags": false + }); + + $("#users-select-field").hide(); + $("#user-roles-select-field").show(); + + $("input[type='radio'].select-users-radio").change(function () { + if ($("#users-radio-btn").is(":checked")) { + $("#user-roles-select-field").hide(); + $("#users-select-field").show(); + } + if ($("#user-roles-radio-btn").is(":checked")) { + $("#users-select-field").hide(); + $("#user-roles-select-field").show(); + } + }); + + // Support for special input type "ANY" on user(s) & user-role(s) selection + $("#user-roles-input").select2({ + "tags": false + }).on("select2:select", function (e) { + if (e.params.data.id == "ANY") { + $(this).val("ANY").trigger("change"); + } else { + $("option[value=ANY]", this).prop("selected", false).parent().trigger("change"); + } + }); + + // Maintains an array of configured features of the profile + var advanceOperations = ".wr-advance-operations"; + $(advanceOperations).on("click", ".wr-input-control.switch", function (event) { + var operationCode = $(this).parents(".operation-data").data("operation-code"); + var operation = $(this).parents(".operation-data").data("operation"); + var operationDataWrapper = $(this).data("target"); + // prevents event bubbling by figuring out what element it's being called from. + if (event.target.tagName == "INPUT") { + var featureConfiguredIcon; + if ($("input[type='checkbox']", this).is(":checked")) { + configuredOperations.push(operationCode); + // when a feature is enabled, if "zero-configured-features" msg is available, hide that. + var zeroConfiguredOperationsErrorMsg = "#policy-profile-main-error-msg"; + if (!$(zeroConfiguredOperationsErrorMsg).hasClass("hidden")) { + $(zeroConfiguredOperationsErrorMsg).addClass("hidden"); + } + // add configured-state-icon to the feature + featureConfiguredIcon = "#" + operation + "-configured"; + if ($(featureConfiguredIcon).hasClass("hidden")) { + $(featureConfiguredIcon).removeClass("hidden"); + } + } else { + //splicing the array if operation is present. + var index = $.inArray(operationCode, configuredOperations); + if (index != -1) { + configuredOperations.splice(index, 1); + } + // when a feature is disabled, clearing all its current configured, error or success states + var subErrorMsgWrapper = "#" + operation + "-feature-error-msg"; + var subErrorIcon = "#" + operation + "-error"; + var subOkIcon = "#" + operation + "-ok"; + featureConfiguredIcon = "#" + operation + "-configured"; + + if (!$(subErrorMsgWrapper).hasClass("hidden")) { + $(subErrorMsgWrapper).addClass("hidden"); + } + if (!$(subErrorIcon).hasClass("hidden")) { + $(subErrorIcon).addClass("hidden"); + } + if (!$(subOkIcon).hasClass("hidden")) { + $(subOkIcon).addClass("hidden"); + } + if (!$(featureConfiguredIcon).hasClass("hidden")) { + $(featureConfiguredIcon).addClass("hidden"); + } + // reinitializing input fields into the defaults + $(operationDataWrapper + " input").each( + function () { + if ($(this).is("input:text")) { + $(this).val($(this).data("default")); + } else if ($(this).is("input:password")) { + $(this).val(""); + } else if ($(this).is("input:checkbox")) { + $(this).prop("checked", $(this).data("default")); + // if this checkbox is the parent input of a grouped-input + if ($(this).hasClass("parent-input")) { + var groupedInput = $(this).parent().parent().parent(); + updateGroupedInputVisibility(groupedInput); + } + } + } + ); + // reinitializing select fields into the defaults + $(operationDataWrapper + " select").each( + function () { + var defaultOption = $(this).data("default"); + $("option:eq(" + defaultOption + ")", this).prop("selected", "selected"); + } + ); + // collapsing expanded-panes (upon the selection of html-select-options) if any + $(operationDataWrapper + " .expanded").each( + function () { + if ($(this).hasClass("expanded")) { + $(this).removeClass("expanded"); + } + $(this).slideUp(); + } + ); + // removing all entries of grid-input elements if exist + $(operationDataWrapper + " .grouped-array-input").each( + function () { + var gridInputs = $(this).find("[data-add-form-clone]"); + if (gridInputs.length > 0) { + gridInputs.remove(); + } + var helpTexts = $(this).find("[data-help-text=add-form]"); + if (helpTexts.length > 0) { + helpTexts.show(); + } + } + ); + } + } + }); + + // adding support for cloning multiple profiles per feature with cloneable class definitions + $(advanceOperations).on("click", ".multi-view.add.enabled", function () { + // get a copy of .cloneable and create new .cloned div element + var cloned = "

    " + $(".cloneable", $(this).parent().parent()).html() + "
    "; + // append newly created .cloned div element to panel-body + $(this).parent().parent().append(cloned); + // enable remove action of newly cloned div element + $(".cloned", $(this).parent().parent()).each( + function () { + if ($(".multi-view.remove", this).hasClass("disabled")) { + $(".multi-view.remove", this).removeClass("disabled"); + } + if (!$(".multi-view.remove", this).hasClass("enabled")) { + $(".multi-view.remove", this).addClass("enabled"); + } + } + ); + }); + + $(advanceOperations).on("click", ".multi-view.remove.enabled", function () { + $(this).parent().remove(); + }); + + // enabling or disabling grouped-input based on the status of a parent check-box + $(advanceOperations).on("click", ".grouped-input", function () { + updateGroupedInputVisibility(this); + }); + + // add form entry click function for grid inputs + $(advanceOperations).on("click", "[data-click-event=add-form]", function () { + var addFormContainer = $("[data-add-form-container=" + $(this).attr("href") + "]"); + var clonedForm = $("[data-add-form=" + $(this).attr("href") + "]").clone(). + find("[data-add-form-element=clone]").attr("data-add-form-clone", $(this).attr("href")); + + // adding class .child-input to capture text-input-array-values + $("input, select", clonedForm).addClass("child-input"); + + $(addFormContainer).append(clonedForm); + setId(addFormContainer); + showHideHelpText(addFormContainer); + }); + + // remove form entry click function for grid inputs + $(advanceOperations).on("click", "[data-click-event=remove-form]", function () { + var addFormContainer = $("[data-add-form-container=" + $(this).attr("href") + "]"); + + $(this).closest("[data-add-form-element=clone]").remove(); + setId(addFormContainer); + showHideHelpText(addFormContainer); + }); + + $(".wizard-stepper").click(function () { + // button clicked here can be either a continue button or a back button. + var currentStep = $(this).data("current"); + var validationIsRequired = $(this).data("validate"); + var wizardIsToBeContinued; + + if (validationIsRequired) { + wizardIsToBeContinued = validateStep[currentStep](); + } else { + wizardIsToBeContinued = true; + } + + if (wizardIsToBeContinued) { + // When moving back and forth, following code segment will + // remove if there are any visible error-messages. + var errorMsgWrappers = ".alert.alert-danger"; + $(errorMsgWrappers).each( + function () { + if (!$(this).hasClass("hidden")) { + $(this).addClass("hidden"); + } + } + ); + + var nextStep = $(this).data("next"); + var isBackBtn = $(this).data("is-back-btn"); + + // if current button is a continuation... + if (!isBackBtn) { + // initiate stepForwardFrom[*] functions to gather form data. + if (stepForwardFrom[currentStep]) { + stepForwardFrom[currentStep](this); + } + } else { + // initiate stepBackFrom[*] functions to rollback. + if (stepBackFrom[currentStep]) { + stepBackFrom[currentStep](); + } + } + + // following step occurs only at the last stage of the wizard. + if (!nextStep) { + window.location.href = $(this).data("direct"); + } + + // updating next wizard step as current. + $(".itm-wiz").each(function () { + var step = $(this).data("step"); + if (step == nextStep) { + $(this).addClass("itm-wiz-current"); + } else { + $(this).removeClass("itm-wiz-current"); + } + }); + + // adding next update of wizard-steps. + $("#" + nextStep + "-wizard-steps").html($(".wr-steps").html()); + + // hiding current section of the wizard and showing next section. + $("." + currentStep).addClass("hidden"); + $("." + nextStep).removeClass("hidden"); + } + }); + +}); diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-android.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-android.hbs new file mode 100644 index 0000000000..9eb9fac829 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-android.hbs @@ -0,0 +1,1273 @@ +
    + + +
    + +
    +
    + +
    + + + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    +
    + + + +
    +
    + +
    + +
    +
    +
    + +
    + +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    + +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    +
    +
    + + + +
    +
    + +
    + + Un-check following checkbox in case you do not need the device to be encrypted. +
    +
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    + +
    + Please note that * sign represents required fields of data. +
    +
    + +
    + + + +
    +
    + + +
    + + + + + + + + + +
    +
    +
    + + +
    +
    + +
    + + + + + + +
    + +
    + + + + + + + + + + + + + + +
    No:Application Name/DescriptionPackage Name
    + No entries added yet . +
    + + + + + + + + + + +
    +
    +
    +
    +
    + + + +
    +
    + +
    + Please note that * sign represents required fields of data. +
    +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + + + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + +
    +
    \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-ios.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-ios.hbs new file mode 100644 index 0000000000..ef6f2d3683 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-ios.hbs @@ -0,0 +1,4832 @@ +
    + + +
    + +
    +
    + +
    + + + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    +
    + + + +
    +
    + +
    +Please note that * sign represents required fields of data. +
    +
    + + +
    + + +
    +
    + +
    +
    + +
    + +
    + +
    + + + + + + + + + + + + + + +
    No:KeyValue
    + No entries added yet . +
    + + + + + + + + + + +
    +
    +
    + + +
    + + + + +
    +
    +
    + + + +
    +
    + +
    + Please note that * sign represents required fields of data. +
    +
    + + +
    + + +
    +
    + +
    +
    + +
    + + + + + + + + + + + + + +
    No:Safari Domain
    + No entries added yet . +
    + + + + + + + + + + +
    +
    +
    +
    +
    + +
    + Please note that * sign represents required fields of data. +
    +
    + + +
    + +
    + + + + + + + + + + + + + + +
    No:App IdentifierVPN UUID
    + No entries added yet . +
    + + + + + + + + + + +
    +
    +
    +
    +
    + + + +
    +
    + +
    + + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + + + +
    + + +
    + + + +
    + + +
    + + + + + + + + + + + + + +
    No:Roaming Consortium OI
    + No entries added yet . +
    + + + + + + + + + +
    +
    + +
    + +
    + + +
    + + + + + + + + + + + + + +
    No:NAI Realm Name
    + No entries added yet . +
    + + + + + + + + + +
    +
    + +
    + +
    + + +
    + + + + + + + + + + + + + + +
    No:Mobile Country Code ( MCC )Mobile Network Code ( MNC )
    + No entries added yet . +
    + + + + + + + + + + +
    +
    +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + +
    + + +
    + +
    + +
    + + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    + +
    + +
    +Incoming Mail Settings : +
    + +
    + + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    +Outgoing Mail Settings : +
    + +
    + + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    + + +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + + + + + + + + + + + + + + +
    No:Device NamePassword
    + No entries added yet . +
    + + + + + + + + + + +
    +
    + +
    + +
    + + +
    + + + + + + + + + + + + + +
    No:Destination
    + No entries added yet . +
    + + + + + + + + + +
    +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + + + + + + + + + + + + + +
    No:Email Domain
    + No entries added yet . +
    + + + + + + + + + +
    +
    + +
    + +
    + + +
    + + + + + + + + + + + + + +
    No:Safari Web Domain
    + No entries added yet . +
    + + + + + + + + + +
    +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + + + + + + + + + + + + + + + +
    No:DescriptionSearch BaseScope
    + No entries added yet . +
    + + + + + + + + + + + +
    +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + + + + + + + + + + + + + + + + + +
    No:APNUsernamePasswordProxyPort
    + No entries added yet . +
    + + + + + + + + + + + + + +
    +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + + + + + + + + + + + + + + + + + + +
    No:APNAuth.TypeUsernamePasswordProxyPort
    + No entries added yet . +
    + + + + + + + + + + + + + + +
    +
    + +
    +
    +
    + + + +
    +
    + +
    + + + +Restrictions on Device Functionality : +
    +
    +
      +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
        +
      • +
        + +
        +
      • +
      • +
        + +
        +
      • +
      • +
        + +
        +
      • +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    +
    +
    +Restrictions on Applications : +
    +
    +
      +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
        +
      • +
        + +
        +
      • +
      • +
        + +
        +
      • +
      • +
        + +
        +
      • +
      • +
        + +
        +
      • +
      • +
        + + +
        +
      • +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    • +
      + +
      +
    • +
    +
    + + + +
    +
    +
    + + +
    +
    + +
    + + + + + +
    + + +
    + + + + + + + + + + + + + + +
    No:Application Name/DescriptionPackage Name
    + No entries added yet . +
    + + + + + + + + + + +
    +
    +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-windows.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-windows.hbs new file mode 100644 index 0000000000..2547a69cec --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/public/templates/hidden-operations-windows.hbs @@ -0,0 +1,566 @@ +
    + + +
    + +
    +
    + +
    + + + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    +
    + + + +
    +
    + +
    + + Un-check following checkbox in case you need to disable camera. +
    +
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    + +
    + + Un-check following checkbox in case you need to disable storage-encryption. +
    +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + +
    No:Application Name/DescriptionPackage Name
    + No entries added yet . +
    + + + + + + + + + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    \ No newline at end of file From 9cde2d815a85cf6b2786486f24c38a75bbd18d81 Mon Sep 17 00:00:00 2001 From: hasuniea Date: Tue, 9 Aug 2016 18:00:49 +0530 Subject: [PATCH 13/71] refactoring windowsapi --- .../org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml | 4 ++-- .../pom.xml | 2 +- .../src/main/resources/p2.inf | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml index 897b7adbe3..c3aee5d026 100644 --- a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml +++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.api/pom.xml @@ -46,7 +46,7 @@ 2.2 - ${project.artifactId} + api#device-mgt#windows#v1.0 @@ -72,7 +72,7 @@ - + diff --git a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml index 633bd9f20b..a2d51efef3 100644 --- a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml +++ b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/pom.xml @@ -118,7 +118,7 @@ true ${project.build.directory}/maven-shared-archive-resources/webapps/ - mdm-windows-agent.war + api#device-mgt#windows#v1.0.war diff --git a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf index 11320712c2..d70e9fe99c 100644 --- a/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf +++ b/features/mobile-plugins-feature/windows-plugin-feature/org.wso2.carbon.device.mgt.mobile.windows.feature/src/main/resources/p2.inf @@ -1,13 +1,13 @@ instructions.configure = \ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/jaggeryapps/,target:${installFolder}/../../deployment/server/jaggeryapps/,overwrite:true);\ -org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/webapps/mdm-windows-agent.war,target:${installFolder}/../../deployment/server/webapps/mdm-windows-agent.war,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/webapps/api#device-mgt#windows#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#device-mgt#windows#v1.0.war,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/dbscripts/plugins/,target:${installFolder}/../../../dbscripts/cdm/plugins/windows,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../database/);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.mobile.windows_${feature.version}/database/,target:${installFolder}/../../database/,overwrite:true);\ instructions.unconfigure = \ -org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/mdm-windows-agent.war);\ -org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/mdm-windows-agent);\ +org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/api#device-mgt#windows#v1.0.war);\ +org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/api#device-mgt#windows#v1.0);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.device-view);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.policy-edit);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.policy-view);\ From d99765401145bbba1ac88ac17b06fa7976afe26a Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 10 Aug 2016 12:47:02 +0530 Subject: [PATCH 14/71] Adding date validations when updating device enrollment --- .../services/impl/DeviceManagementServiceImpl.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java index 69c0c5def5..4dd9248596 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java @@ -260,7 +260,16 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { @Override public Response modifyEnrollment(@PathParam("id") String id, @Valid AndroidDevice androidDevice) { Device device = new Device(); + String msg = ""; device.setType(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_ANDROID); + if(androidDevice.getEnrolmentInfo().getDateOfEnrolment() <= 0){ + msg = "Invalid Enrollment date."; + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } + if(androidDevice.getEnrolmentInfo().getDateOfLastUpdate() <= 0){ + msg = "Invalid Last Updated date."; + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } device.setEnrolmentInfo(androidDevice.getEnrolmentInfo()); device.getEnrolmentInfo().setOwner(AndroidAPIUtils.getAuthenticatedUser()); device.setDeviceInfo(androidDevice.getDeviceInfo()); From c32d9e312539cde926e0af40833abfa8ee44910f Mon Sep 17 00:00:00 2001 From: Ruwan Date: Wed, 10 Aug 2016 12:59:29 +0530 Subject: [PATCH 15/71] Bug fix Adding bug fix --- .../android/services/impl/DeviceManagementServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java index 4dd9248596..dad6393e03 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java @@ -290,7 +290,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { "carries the id '" + id + "' has not been updated").build(); } } catch (DeviceManagementException e) { - String msg = "Error occurred while modifying enrollment of the Android device that carries the id '" + + msg = "Error occurred while modifying enrollment of the Android device that carries the id '" + id + "'"; log.error(msg, e); throw new UnexpectedServerErrorException( From e1e7bccd579113bd2aeec2660fc06ee6f65e9099 Mon Sep 17 00:00:00 2001 From: harshanl Date: Wed, 10 Aug 2016 14:19:37 +0530 Subject: [PATCH 16/71] Temporary removed VirtualFireAlarm module to fix the build issue --- components/iot-plugins/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/iot-plugins/pom.xml b/components/iot-plugins/pom.xml index 283ad011f0..f1b5762d3c 100644 --- a/components/iot-plugins/pom.xml +++ b/components/iot-plugins/pom.xml @@ -36,7 +36,7 @@ androidsense-plugin arduino-plugin raspberrypi-plugin - virtual-fire-alarm-plugin + iot-base-plugin iot-analytics From 0ee28c91d2f7aaf8543b0900aacd4219317938b6 Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 10 Aug 2016 14:26:39 +0530 Subject: [PATCH 17/71] Adding date validations when updating device enrollment --- .../services/impl/DeviceManagementServiceImpl.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java index 69c0c5def5..dad6393e03 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementServiceImpl.java @@ -260,7 +260,16 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { @Override public Response modifyEnrollment(@PathParam("id") String id, @Valid AndroidDevice androidDevice) { Device device = new Device(); + String msg = ""; device.setType(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_ANDROID); + if(androidDevice.getEnrolmentInfo().getDateOfEnrolment() <= 0){ + msg = "Invalid Enrollment date."; + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } + if(androidDevice.getEnrolmentInfo().getDateOfLastUpdate() <= 0){ + msg = "Invalid Last Updated date."; + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } device.setEnrolmentInfo(androidDevice.getEnrolmentInfo()); device.getEnrolmentInfo().setOwner(AndroidAPIUtils.getAuthenticatedUser()); device.setDeviceInfo(androidDevice.getDeviceInfo()); @@ -281,7 +290,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { "carries the id '" + id + "' has not been updated").build(); } } catch (DeviceManagementException e) { - String msg = "Error occurred while modifying enrollment of the Android device that carries the id '" + + msg = "Error occurred while modifying enrollment of the Android device that carries the id '" + id + "'"; log.error(msg, e); throw new UnexpectedServerErrorException( From 891f0964bad180988e37b186bdb81bf80c435fd9 Mon Sep 17 00:00:00 2001 From: harshanl Date: Wed, 10 Aug 2016 15:02:12 +0530 Subject: [PATCH 18/71] Temporary removed VirtualFireAlarm module to fix the build issue --- features/iot-plugins-feature/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/iot-plugins-feature/pom.xml b/features/iot-plugins-feature/pom.xml index 0e4a641bcf..60e45e2cdc 100644 --- a/features/iot-plugins-feature/pom.xml +++ b/features/iot-plugins-feature/pom.xml @@ -38,7 +38,7 @@ androidsense-plugin-feature arduino-plugin-feature raspberrypi-plugin-feature - virtual-fire-alarm-plugin-feature + iot-base-plugin-feature iot-devicetypes-feature From aada09de1085292b31769e9e47b4d2e7be2fc1dd Mon Sep 17 00:00:00 2001 From: dilanua Date: Wed, 10 Aug 2016 18:06:26 +0530 Subject: [PATCH 19/71] Adding policy create UI updates --- .../units/mdm.unit.policy.create/create.hbs | 477 +++++++++--------- .../units/mdm.unit.policy.create/create.js | 32 +- .../public/js/policy-create.js | 166 +++--- 3 files changed, 344 insertions(+), 331 deletions(-) diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs index f101f41a9a..ff6748535d 100644 --- a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/units/mdm.unit.policy.create/create.hbs @@ -1,279 +1,280 @@ {{#zone "content"}}
    -
    +
    -

    4*9t^x*H?1W4_eSBO(j)dZE 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ÐT=za;AQ^X{o=X~1HyA@3L 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 =Np#Sh!kFG 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 Y)v^`Vy{`9j5BTrKHHE7H@a+Eo0I#2R6aWAK literal 0 HcmV?d00001 diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/images/android.png b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/images/android.png new file mode 100755 index 0000000000000000000000000000000000000000..7fee78a646d5c43d1254dbd4c34232fd760deb1d GIT binary patch literal 2948 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 literal 0 HcmV?d00001 diff --git a/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/js/device-listing.js b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/js/device-listing.js new file mode 100644 index 0000000000..8952faac18 --- /dev/null +++ b/components/mobile-plugins/mobile-base-plugin/org.wso2.carbon.device.mgt.mobile.ui/src/main/resources/jaggeryapps/devicemgt/app/pages/mdm.page.devices/public/js/device-listing.js @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Following function would execute + * when a user clicks on the list item + * initial mode and with out select mode. + */ +function InitiateViewOption(url) { + if ($(".select-enable-btn").text() == "Select") { + $(location).attr('href', url); + } +} + +(function () { + var cache = {}; + var permissionSet = {}; + var validateAndReturn = function (value) { + return (value == undefined || value == null) ? "Unspecified" : value; + }; + Handlebars.registerHelper("deviceMap", function (device) { + device.owner = validateAndReturn(device.owner); + device.ownership = validateAndReturn(device.ownership); + var arr = device.properties; + if (arr){ + device.properties = arr.reduce(function (total, current) { + total[current.name] = validateAndReturn(current.value); + return total; + }, {}); + } + }); + + //This method is used to setup permission for device listing + $.setPermission = function (permission) { + permissionSet[permission] = true; + }; + + $.hasPermission = function (permission) { + return permissionSet[permission]; + }; +})(); + +/* + * Setting-up global variables. + */ +var deviceCheckbox = "#ast-container .ctrl-wr-asset .itm-select input[type='checkbox']"; +var assetContainer = "#ast-container"; + +/* + * DOM ready functions. + */ +$(document).ready(function () { + /* Adding selected class for selected devices */ + $(deviceCheckbox).each(function () { + addDeviceSelectedClass(this); + }); + + var i; + var permissionList = $("#permission").data("permission"); + for (i = 0; i < permissionList.length; i++) { + $.setPermission(permissionList[i]); + } + + /* for device list sorting drop down */ + $(".ctrl-filter-type-switcher").popover({ + html : true, + content : function () { + return $("#content-filter-types").html(); + } + }); + + $(".ast-container").on("click", ".claim-btn", function(e){ + e.stopPropagation(); + var deviceId = $(this).data("deviceid"); + var deviceListing = $("#device-listing"); + var currentUser = deviceListing.data("current-user"); + var serviceURL = "/temp-controller-agent/enrollment/claim?username=" + currentUser; + var deviceIdentifier = {id: deviceId, type: "TemperatureController"}; + invokerUtil.put(serviceURL, deviceIdentifier, function(message){ + console.log(message); + }, function(message){ + console.log(message.content); + }); + }); +}); + +/* + * On Select All Device button click function. + * + * @param button: Select All Device button + */ +function selectAllDevices(button) { + if(!$(button).data('select')){ + $(deviceCheckbox).each(function(index){ + $(this).prop('checked', true); + addDeviceSelectedClass(this); + }); + $(button).data('select', true); + $(button).html('Deselect All Devices'); + }else{ + $(deviceCheckbox).each(function(index){ + $(this).prop('checked', false); + addDeviceSelectedClass(this); + }); + $(button).data('select', false); + $(button).html('Select All Devices'); + } +} + +/* + * On listing layout toggle buttons click function. + * + * @param view: Selected view type + * @param selection: Selection button + */ +function changeDeviceView(view, selection) { + $(".view-toggle").each(function() { + $(this).removeClass("selected"); + }); + $(selection).addClass("selected"); + if (view == "list") { + $(assetContainer).addClass("list-view"); + } else { + $(assetContainer).removeClass("list-view"); + } +} + +/* + * Add selected style class to the parent element function. + * + * @param checkbox: Selected checkbox + */ +function addDeviceSelectedClass(checkbox) { + if ($(checkbox).is(":checked")) { + $(checkbox).closest(".ctrl-wr-asset").addClass("selected device-select"); + } else { + $(checkbox).closest(".ctrl-wr-asset").removeClass("selected device-select"); + } +} + +function toTitleCase(str) { + return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); +} + +function loadDevices(searchType, searchParam){ + var deviceListing = $("#device-listing"); + var currentUser = deviceListing.data("currentUser"); + + var serviceURL; + if ($.hasPermission("LIST_DEVICES")) { + //serviceURL = "/mdm-admin/devices"; + serviceURL = "/api/device-mgt/v1.0/devices"; + } else if ($.hasPermission("LIST_OWN_DEVICES")) { + //Get authenticated users devices + serviceURL = "/api/device-mgt/v1.0/devices?user="+currentUser; + //serviceURL = "/mdm-admin/users/devices?username="+currentUser; + } else { + $("#loading-content").remove(); + $('#device-table').addClass('hidden'); + $('#device-listing-status-msg').text('Permission denied.'); + $("#device-listing-status").removeClass(' hidden'); + return; + } + + function getPropertyValue(deviceProperties, propertyName) { + var property; + for (var i =0; i < deviceProperties.length; i++) { + property = deviceProperties[i]; + if (property.name == propertyName) { + return property.value; + } + } + return {}; + } + + var fnCreatedRow = function( nRow, aData, iDataIndex ) { + $(nRow).attr('data-type', 'selectable'); + $(nRow).attr('data-deviceid', aData.deviceIdentifier); + $(nRow).attr('data-devicetype', aData.deviceType); + } + + + var columns = [ + { + class : 'remove-padding icon-only content-fill viewEnabledIcon', + data : 'icon', + render: function (data, type, row, meta) { + var deviceType = row.deviceType; + var deviceIdentifier = row.deviceIdentifier; + var url = "#"; + if (status != 'REMOVED') { + url = "devices/view?type=" + deviceType + "&id=" + deviceIdentifier; + } + return '