diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java index bf99446804..62f07e362c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java @@ -140,6 +140,93 @@ public interface ActivityInfoProviderService { @HeaderParam("If-Modified-Since") String ifModifiedSince); + @GET + @Path("/{id}/{devicetype}/{deviceid}") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Getting Details of an Activity for a specific device", + notes = "Retrieve the details of a specific activity/operation, such as the meta information of " + + "an operation, including the responses from a given device", + tags = "Activity Info Provider", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:get-activity") + }) + } + ) + @ApiResponses(value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the activity details.", + response = Activity.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. \n Empty body because the client already has the latest version of " + + "the requested resource."), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 401, + message = "Unauthorized. \n Unauthorized request."), + @ApiResponse( + code = 404, + message = "Not Found. \n No activity found with the given ID.", + response = ErrorResponse.class), + @ApiResponse( + code = 406, + message = "Not Acceptable.\n The requested media type is not supported"), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Server error occurred while fetching the activity data.", + response = ErrorResponse.class) + }) + Response getActivityByDevice( + @ApiParam( + name = "id", + value = "Activity id of the operation/activity.", + required = true, + defaultValue = "ACTIVITY_1") + @PathParam("id") + @Size(max = 45) + String id, + @ApiParam( + name = "devicetype", + value = "The device type name, such as ios, android, windows or fire-alarm.", + required = true) + @PathParam("devicetype") + @Size(max = 45) + String type, + @ApiParam( + name = "deviceid", + value = "The device identifier of the device you want ot get details.", + required = true) + @PathParam("deviceid") + @Size(max = 45) + String deviceid, + @ApiParam( + name = "If-Modified-Since", + value = "Checks if the requested variant was modified, since the specified date-time\n." + + "Provide the value in the Java Date Format: EEE, d MMM yyyy HH:mm:ss Z\n." + + "Example: Mon, 05 Jan 2014 15:10:00 +0200", + required = false) + @HeaderParam("If-Modified-Since") String ifModifiedSince); + @GET @ApiOperation( produces = MediaType.APPLICATION_JSON, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java index d06b8a8180..2b39044d17 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java @@ -20,6 +20,7 @@ package org.wso2.carbon.device.mgt.jaxrs.service.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; @@ -72,6 +73,43 @@ public class ActivityProviderServiceImpl implements ActivityInfoProviderService } } + + @GET + @Override + @Path("/{id}/{devicetype}/{deviceid}") + public Response getActivityByDevice(@PathParam("id") + @Size(max = 45) String id, + @PathParam("devicetype") + @Size(max = 45) String devicetype, + @PathParam("deviceid") + @Size(max = 45) String deviceid, + @HeaderParam("If-Modified-Since") String ifModifiedSince) { + Activity activity; + DeviceManagementProviderService dmService; + try { + RequestValidationUtil.validateActivityId(id); + + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(deviceid); + deviceIdentifier.setType(devicetype); + + dmService = DeviceMgtAPIUtils.getDeviceManagementService(); + activity = dmService.getOperationByActivityIdAndDevice(id, deviceIdentifier); + if (activity == null) { + return Response.status(404).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage("No activity can be " + + "found upon the provided activity id '" + id + "'").build()).build(); + } + return Response.status(Response.Status.OK).entity(activity).build(); + } catch (OperationManagementException e) { + String msg = "ErrorResponse occurred while fetching the activity for the supplied id."; + log.error(msg, e); + return Response.serverError().entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } + } + + @GET @Override public Response getActivities(@QueryParam("since") String since, @QueryParam("offset") int offset, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java index 45db0d3eff..8de9f3199e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java @@ -93,6 +93,8 @@ public interface OperationManager { Activity getOperationByActivityId(String activity) throws OperationManagementException; + Activity getOperationByActivityIdAndDevice(String activity, DeviceIdentifier deviceId) throws OperationManagementException; + List getOperationUpdatedAfter(long timestamp) throws OperationManagementException; List getActivitiesUpdatedAfter(long timestamp) throws OperationManagementException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java index dc096eb130..8a5d9aa6ce 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java @@ -812,6 +812,28 @@ public class OperationManagerImpl implements OperationManager { } } + public Activity getOperationByActivityIdAndDevice(String activity, DeviceIdentifier deviceId) throws OperationManagementException { + // This parses the operation id from activity id (ex : ACTIVITY_23) and converts to the integer. + int operationId = Integer.parseInt( + activity.replace(DeviceManagementConstants.OperationAttributes.ACTIVITY, "")); + if (operationId == 0) { + throw new IllegalArgumentException("Operation ID cannot be null or zero (0)."); + } + + Device device = this.getDevice(deviceId); + try { + OperationManagementDAOFactory.openConnection(); + return operationDAO.getActivityByDevice(operationId, device.getId()); + } catch (SQLException e) { + throw new OperationManagementException("Error occurred while opening a connection to the data source.", e); + } catch (OperationManagementDAOException e) { + throw new OperationManagementException("Error occurred while retrieving the operation with activity Id '" + + activity + " and device Id: " + deviceId.getId(), e); + } finally { + OperationManagementDAOFactory.closeConnection(); + } + } + @Override public List getOperationUpdatedAfter(long timestamp) throws OperationManagementException { return null; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java index 5e3848cf9b..dc9b6dfb9a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java @@ -70,6 +70,8 @@ public interface OperationDAO { Activity getActivity(int operationId) throws OperationManagementDAOException; + Activity getActivityByDevice(int operationId, int deviceId) throws OperationManagementDAOException; + int getEnrolmentIdFromMappingId(int enrollmentOpMappingId) throws OperationManagementDAOException; List getOperationsUpdatedAfter(long timestamp) throws OperationManagementDAOException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java index f581f76085..bd6b996e0b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java @@ -384,6 +384,83 @@ public class GenericOperationDAOImpl implements OperationDAO { return activity; } + public Activity getActivityByDevice(int operationId, int deviceId) throws OperationManagementDAOException { + + PreparedStatement stmt = null; + ResultSet rs = null; + Activity activity = null; + List activityStatusList = new ArrayList<>(); + try { + Connection conn = OperationManagementDAOFactory.getConnection(); + String sql = "SELECT eom.ENROLMENT_ID, eom.OPERATION_ID, eom.ID AS EOM_MAPPING_ID, dor.ID AS OP_RES_ID,\n" + + "de.DEVICE_ID, d.DEVICE_IDENTIFICATION, \n" + + "d.DEVICE_TYPE_ID, dt.NAME AS DEVICE_TYPE_NAME, eom.STATUS, eom.CREATED_TIMESTAMP, \n" + + "eom.UPDATED_TIMESTAMP, op.OPERATION_CODE, op.TYPE AS OPERATION_TYPE, dor.OPERATION_RESPONSE, \n" + + "dor.RECEIVED_TIMESTAMP FROM DM_ENROLMENT_OP_MAPPING AS eom \n" + + "INNER JOIN DM_OPERATION AS op ON op.ID=eom.OPERATION_ID\n" + + "INNER JOIN DM_ENROLMENT AS de ON de.ID=eom.ENROLMENT_ID\n" + + "INNER JOIN DM_DEVICE AS d ON d.ID=de.DEVICE_ID \n" + + "INNER JOIN DM_DEVICE_TYPE AS dt ON dt.ID=d.DEVICE_TYPE_ID\n" + + "LEFT JOIN DM_DEVICE_OPERATION_RESPONSE AS dor ON dor.ENROLMENT_ID=de.id \n" + + "AND dor.OPERATION_ID = eom.OPERATION_ID\n" + + "WHERE eom.OPERATION_ID = ? AND de.device_id = ? AND de.TENANT_ID = ?"; + + stmt = conn.prepareStatement(sql); + stmt.setInt(1, operationId); + stmt.setInt(2, deviceId); + stmt.setInt(3, PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId()); + rs = stmt.executeQuery(); + + int enrolmentId = 0; + ActivityStatus activityStatus = null; + + while (rs.next()) { + if (enrolmentId == 0) { + activity = new Activity(); + activity.setType(Activity.Type.valueOf(rs.getString("OPERATION_TYPE"))); + activity.setCreatedTimeStamp(new java.util.Date(rs.getLong(("CREATED_TIMESTAMP")) * 1000).toString()); + activity.setCode(rs.getString("OPERATION_CODE")); + } + if (enrolmentId != rs.getInt("ENROLMENT_ID")) { + activityStatus = new ActivityStatus(); + + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(rs.getString("DEVICE_IDENTIFICATION")); + deviceIdentifier.setType(rs.getString("DEVICE_TYPE_NAME")); + activityStatus.setDeviceIdentifier(deviceIdentifier); + + activityStatus.setStatus(ActivityStatus.Status.valueOf(rs.getString("STATUS"))); + + List operationResponses = new ArrayList<>(); + if (rs.getInt("UPDATED_TIMESTAMP") != 0) { + activityStatus.setUpdatedTimestamp(new java.util.Date(rs.getLong(("UPDATED_TIMESTAMP")) * 1000).toString()); + operationResponses.add(OperationDAOUtil.getOperationResponse(rs)); + } + activityStatus.setResponses(operationResponses); + + activityStatusList.add(activityStatus); + + enrolmentId = rs.getInt("ENROLMENT_ID"); + activity.setActivityStatus(activityStatusList); + } else { + if (rs.getInt("UPDATED_TIMESTAMP") != 0) { + activityStatus.getResponses().add(OperationDAOUtil.getOperationResponse(rs)); + } + } + } + } catch (SQLException e) { + throw new OperationManagementDAOException("Error occurred while getting the operation details from " + + "the database.", e); + } catch (ClassNotFoundException e) { + throw new OperationManagementDAOException("Error occurred while converting the operation response to string.", e); + } catch (IOException e) { + throw new OperationManagementDAOException("IO exception occurred while converting the operations responses.", e); + } finally { + OperationManagementDAOUtil.cleanupResources(stmt, rs); + } + return activity; + } + @Override public List getActivitiesUpdatedAfter(long timestamp) throws OperationManagementDAOException { return this.getActivitiesUpdatedAfter(timestamp, 0, 0); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/push/notification/mgt/PushNotificationBasedOperationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/push/notification/mgt/PushNotificationBasedOperationManager.java index 0d92577135..79ffacb370 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/push/notification/mgt/PushNotificationBasedOperationManager.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/push/notification/mgt/PushNotificationBasedOperationManager.java @@ -116,6 +116,11 @@ public class PushNotificationBasedOperationManager implements OperationManager { return this.operationManager.getOperationByActivityId(activity); } + @Override + public Activity getOperationByActivityIdAndDevice(String activity, DeviceIdentifier deviceId) throws OperationManagementException { + return this.operationManager.getOperationByActivityIdAndDevice(activity, deviceId); + } + @Override public List getOperationUpdatedAfter(long timestamp) throws OperationManagementException { return this.operationManager.getOperationUpdatedAfter(timestamp); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java index d1d3f5f03d..09911f3cd6 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java @@ -532,6 +532,8 @@ public interface DeviceManagementProviderService { Activity getOperationByActivityId(String activity) throws OperationManagementException; + Activity getOperationByActivityIdAndDevice(String activity, DeviceIdentifier deviceId) throws OperationManagementException; + List getActivitiesUpdatedAfter(long timestamp) throws OperationManagementException; List getActivitiesUpdatedAfter(long timestamp, int limit, int offset) throws OperationManagementException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java index 6543f4bda9..77934202b2 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java @@ -1008,6 +1008,10 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv return DeviceManagementDataHolder.getInstance().getOperationManager().getOperationByActivityId(activity); } + public Activity getOperationByActivityIdAndDevice(String activity, DeviceIdentifier deviceId) throws OperationManagementException { + return DeviceManagementDataHolder.getInstance().getOperationManager().getOperationByActivityIdAndDevice(activity, deviceId); + } + @Override public List getActivitiesUpdatedAfter(long timestamp) throws OperationManagementException { return DeviceManagementDataHolder.getInstance().getOperationManager().getActivitiesUpdatedAfter(timestamp); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/public/css/main.css b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/public/css/main.css new file mode 100644 index 0000000000..e4891d63b7 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/public/css/main.css @@ -0,0 +1,345 @@ +.operation-icon{ + width: 88px; + height: 100px; + background-color: #ebebeb; + float: left; + margin: 0px 10px 10px 0px; + cursor: pointer; + position: relative; +} + +.operation-icon i{ + padding: 15px 0px 0px 0px; + display: block; + min-height: 65px; + font-size: 3em !important; + margin: 0px !important; +} + +.operation-icon span{ + height: 35px; + line-height: 15px; + vertical-align: middle; + display: table-cell; + width: inherit; +} + +.application{ + width: 250px; + height: 100px; + background-color: #ebebeb; + float: left; + margin: 0px 10px 10px 0px; +} + +.application img{ + height: inherit; + padding: 10px; + float: left; +} + +.app-info h4{ + margin-bottom: 0px; +} + +.application i{ + float: right; + margin: 0px 10px; + position: relative; + bottom: -20px; + cursor: pointer; +} + +.nav-tabs>li>a{ + color: #333; +} +.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover{ + border-bottom: 2px solid #37474f; + border-right: none; + border-top: none; + border-left: none; + font-weight: 400; +} +.nav-tabs-selector{ + margin: 0px; + border: 1px solid #37474f; +} + +.tab-pane{ + padding: 20px 0px; +} + +#location{ + height: calc(100vh - 400px); +} + +.page-loader{ + position: absolute; + left: 0px; + top: 0px; + width: 100%; + height: calc(100vh - 160px); + z-index: 9999; + background: url(images/page-loader.gif) 50% 50% no-repeat rgb(249,249,249); +} + +.page-loader .loader{ + left: 43%; + position: absolute; + bottom: 50%; + width: 13%; +} + +.page-loader .loader span{ + padding: 9px; + position: absolute; + font-size: 18px; +} + +.device-info-container{ + margin-bottom: 40px; +} +.device-type{ + font-size: 9em; +} +.device-info h1, .device-info h4:not(:last-child){ + margin: 0px 0px 9px 0px; + position: relative; +} +.device-id a{ + font-size: 0.4em; + position: absolute; + margin: 0px 5px; +} +.device-info h4:last-child{ + margin: 0px; +} +.tab-actions{ + position: relative; +} +.tab-actions .action-btn{ + padding: 10px 15px; + background-color: #ebebeb; + float: right; + cursor: pointer; + position: relative; + margin-left: 10px; + ma +} +.tab-actions .action{ + float: right; + margin-left: 10px; +} +.tab-actions .action-btn.show a{ + margin: 0 0 10px; +} +.tab-actions .action-btn a{ + margin: 0px; +} +.tab-actions { + margin: 0px; +} +.tab-actions .action-prop{ + padding: 10px; + background: #ebebeb; + width: 100%; + margin-bottom: 10px; +} +.tab-action-active{ + padding-bottom: 15px !important; +} +.action-btn-container:after{ + content: ''; + display: block; + clear:both; +} +.input-group .fw{ + position: absolute; + top: 10px; + right: 10px; + z-index: 100; +} + +.vital-strip{ + display: flex; + flex-direction: row; + background-color: #ebebeb; + padding: 10px; + margin-bottom: 40px; +} +.vital-strip p{ + flex-grow: 1; + margin: 0px; + display: inline-flex; +} +.vital-strip p i{ + margin: 0px 10px; +} +.vital-strip p span{ + padding: 5px 0px; +} +.memory-amt{ + font-size: 0.8em; + position: relative; + bottom: -3px; + padding-left: 2px !important; +} + +.policy-item{ + display: flex; + flex-direction: row; + padding: 10px; + background-color: #ebebeb; +} +.policy-item .policy-status, .policy-item p{ + flex-grow: 1; +} +.policy-item .policy-status{ + flex-grow: 1; + flex-basis: 0; + margin-right: 10px; + align-self: center; + color: #5cb85c; +} +.policy-name{ + font-size: 1.5em; + display: flex; +} +.policy-platform{ + display: flex; +} +.policy-item p:nth-child(2){ + margin: 0px; +} +.policy-item p:nth-child(3){ + flex-grow: 7; + align-self: center; + margin: 0px; +} +.policy-item p:nth-child(3) span{ + display: flex; +} +.policy-item .actions{ + flex-grow: 1; + display: flex; +} +.policy-item .action-btn{ + flex-grow: 1; + margin: 3px 5px; + background-color: #ccc; + display: flex; + cursor: pointer; + position: relative; +} +.policy-item .action-btn p{ + align-self: center; + margin: 0px; + display: flex; + justify-content: center; +} +.policy-item .action-btn p i{ + padding: 0px 5px; +} +.policy-item .action-btn span{ + align-self: center; +} + + +#operation-log tr td{ + border-bottom: 15px solid #fff !important; +} +#operation-log tr.shown td{ + border-bottom: none !important; +} +.log-data-row td{ + padding: 0px !important; + border-bottom: 15px solid #fff !important; +} +.log-data{ + background-color: #f6f6f6; + padding: 0px 20px; +} +.log-data:after{ + content: ''; + width: 0; + height: 100%; + position: absolute; + border: 1px solid #ced8db; + top: 0; + left: 50px; +} +.log-record-status{ + background-color: #4c4c4c !important; + color: #fff; + cursor: pointer; + user-select: none; +} +.log-record-status i:first-child{ + float: left; + line-height: 19px; + padding: 0px 10px 0px 0px; +} +.log-record-status span{ + float: left; + line-height: 17px; +} +.log-status{ + padding-left: 14px; + z-index: 10; + position: relative; +} +.log-status i{ + margin: 0px 10px; + background-color: #f6f6f6; + border-radius: 20px; +} +.log-entry{ + padding: 15px 0px; +} +.log-entry:not(:first-child){ + padding-top: 0px; +} + + +/* + * custom css fixes + */ + +.page-content-wrapper, .page-content-wrapper[data-container-behaviour=static]{ + min-height: calc(100vh - 123px); +} + +.content{ + /*opacity: 0;*/ +} + +.content-wrapper{ + position: relative; +} + +.dataTablesTop{ + display: none; +} + +.table.list-table:not(.grid-view)>tbody>tr>td, .table.list-table:not(.grid-view)>tbody>tr>th{ + background-color: #eaeaea; +} +.table.table-hover>tbody>tr:hover td:not(.dataTables_empty){ + background-color: inherit !important; +} +.table-hover>tbody>tr:hover,{ + background-color: +} +.table.list-table>tbody>tr:nth-of-type(even), .table.list-table>tbody>tr:nth-of-type(odd){ + background-color: #eaeaea; +} +.table.table-hover>tbody>tr:hover td:not(.dataTables_empty){ + background-color: inherit !important; +} +.table.table-hover>tbody>tr:hover td.log-record-status{ + background-color: #4c4c4c !important; +} + +.tab-content{ + border:none; +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/public/js/device-view.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/public/js/device-view.js index b0fb423aa2..cf6b1dda7c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/public/js/device-view.js +++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/public/js/device-view.js @@ -16,195 +16,322 @@ * under the License. */ - var deviceId = $(".device-id"); - var deviceIdentifier = deviceId.data("deviceid"); - var deviceType = deviceId.data("type"); - var deviceOwner = deviceId.data("owner"); +var deviceId = $(".device-id"); +var deviceIdentifier = deviceId.data("deviceid"); +var deviceType = deviceId.data("type"); +var deviceOwner = deviceId.data("owner"); - $(document).ready(function () { - $(".panel-body").removeClass("hidden"); - $("#loading-content").remove(); +$(document).ready(function() { + $(".panel-body").removeClass("hidden"); + $("#loading-content").remove(); - if ($('#event_log').length) { - loadOperationsLog(); - } - - if ($('#policy_compliance').length) { - loadPolicyCompliance(); - } + if ($('#event_log').length) { + loadOperationsLog(); + } + if ($('#policy_compliance').length) { + loadPolicyCompliance(); + } - $("#refresh-policy").click(function () { - $('#policy-spinner').removeClass('hidden'); - loadPolicyCompliance(); - }); - $("#refresh-operations").click(function () { - $('#operations-spinner').removeClass('hidden'); - loadOperationsLog(true); - }); + $("#refresh-policy").click(function() { + $('#policy-spinner').removeClass('hidden'); + loadPolicyCompliance(); + }); + $("#refresh-operations").click(function() { + $('#operations-spinner').removeClass('hidden'); + loadOperationsLog(true); }); - function loadOperationsLog(update) { - var operationsLogTable = "#operations-log-table"; +}); - if (update) { - operationTable = $(operationsLogTable).DataTable(); - $("#operations-spinner").removeClass("hidden"); - operationTable.ajax.reload(function ( json ) { +function loadOperationsLog() { + var table = $('#operation-log').DataTable({ + serverSide: true, + processing: false, + searching: false, + ordering: false, + pageLength: 10, + order: [], + autoWidth: false, + ajax: { + url: "/devicemgt/api/operation/paginate", + data: { + deviceId: deviceIdentifier, + deviceType: deviceType, + owner: deviceOwner + }, + dataSrc: function(json) { $("#operations-spinner").addClass("hidden"); - }, false); - return; - } - operationTable = $(operationsLogTable).datatables_extended({ - serverSide: true, - processing: false, - searching: false, - ordering: false, - pageLength : 10, - order: [], - ajax: { - - url: "/devicemgt/api/operation/paginate", - data: {deviceId : deviceIdentifier, deviceType: deviceType, owner: deviceOwner}, - dataSrc: function (json) { - $("#operations-spinner").addClass("hidden"); - $("#operations-log-container").empty(); - return json.data; + $("#operations-log-container").empty(); + return json.data; + } + }, + columnDefs: [{ + targets: 0, + data: "code", + class: "icon-only content-fill" + }, + { + targets: 1, + data: "createdTimeStamp", + class: "text-right", + render: function(date) { + var value = String(date); + return value.slice(0, 16); } }, - columnDefs: [ - {targets: 0, data: "code" }, - {targets: 1, data: "status", render: - function (status) { - var html; - switch (status) { - case "COMPLETED" : - html = " Completed"; - break; - case "PENDING" : - html = " Pending"; - break; - case "ERROR" : - html = " Error"; - break; - case "IN_PROGRESS" : - html = " In Progress"; - break; - case "REPEATED" : - html = " Repeated"; - break; - } - return html; - } + { + targets: 2, + data: "status", + class: "text-right extended-log-data log-record-status", + render: function(data, type, full, meta) { + return ' ' + data + ' '; }, - {targets: 2, data: "createdTimeStamp", render: - function (date) { - var value = String(date); - return value.slice(0, 16); + width: "100%" + } + ], + fnCreatedRow: function(nRow, aData, iDataIndex) { + $('td:eq(0)', nRow) + .attr('data-search', aData.Device_Type) + .attr('data-display', aData.Device_Type) + .addClass(' icon-only content-fill'); + + $('td:eq(1), td:eq(2)', nRow).addClass('text-right'); + $('td:eq(2)', nRow).addClass('log-record-status') + + } + }); + + $('#operation-log tbody').on('click', 'td.extended-log-data', function() { + var tr = $(this).closest('tr'); + var row = table.row(tr); + var rowData = row.data() + var deviceid = $('.device-id').data('deviceid'); + var deviceType = $('.device-id').data('type'); + var uri = "/api/device-mgt/v1.0/activities/" + rowData.activityId + "/" + deviceType + "/" + deviceid; + var contentType = "application/json"; + + if (row.child.isShown()) { + row.child.hide(); + $(row.child()).removeClass('log-data-row'); + tr.removeClass('shown'); + } else { + invokerUtil.get(uri,(payload) => { + row.child(renderLogDetails(row.data(),payload)).show(); + $(row.child()).addClass('log-data-row'); + tr.addClass('shown'); + },(error) => { + + },contentType); + } + + }); + + function renderLogDetails(obj,data) { + var payload = JSON.parse(data); + var logStream = '
'; + + Object.entries(payload.activityStatus).forEach( + ([key, entry]) => { + logStream += '
' + + '
' + + '
' + + '' + entry.status + '
' + + '
' + + '
' + + '
' + entry.updatedTimestamp + '
' + + '
' + + '
'; + } + ); + logStream += '
'; + return logStream; + + function getLogStatusIcon(entry) { + switch (entry) { + case 'COMPLETED': + return 'fw-success' + break; + case 'PENDING': + return 'fw-pending' + break; + default: + return 'fw-info' + } + } + } +} + +function loadOperationsLog2(update) { + var operationsLogTable = "#operations-log-table"; + + if (update) { + operationTable = $(operationsLogTable).DataTable(); + $("#operations-spinner").removeClass("hidden"); + operationTable.ajax.reload(function(json) { + $("#operations-spinner").addClass("hidden"); + }, false); + return; + } + operationTable = $(operationsLogTable).datatables_extended({ + serverSide: true, + processing: false, + searching: false, + ordering: false, + pageLength: 10, + order: [], + ajax: { + url: "/devicemgt/api/operation/paginate", + data: { + deviceId: deviceIdentifier, + deviceType: deviceType, + owner: deviceOwner + }, + dataSrc: function(json) { + $("#operations-spinner").addClass("hidden"); + $("#operations-log-container").empty(); + return json.data; + } + }, + columnDefs: [{ + targets: 0, + data: "code" + }, + { + targets: 1, + data: "status", + render: function(status) { + var html; + switch (status) { + case "COMPLETED": + html = " Completed"; + break; + case "PENDING": + html = " Pending"; + break; + case "ERROR": + html = " Error"; + break; + case "IN_PROGRESS": + html = " In Progress"; + break; + case "REPEATED": + html = " Repeated"; + break; } + return html; + } + }, + { + targets: 2, + data: "createdTimeStamp", + render: function(date) { + var value = String(date); + return value.slice(0, 16); } - ], - "createdRow": function(row, data) { - - $(row).attr("data-type", "selectable"); - $(row).attr("data-id", data["id"]); - $.each($("td", row), - function(colIndex) { - switch(colIndex) { - case 1: - $(this).attr("data-grid-label", "Code"); - $(this).attr("data-display", data["code"]); - break; - case 2: - $(this).attr("data-grid-label", "Status"); - $(this).attr("data-display", data["status"]); - break; - case 3: - $(this).attr("data-grid-label", "Created Timestamp"); - $(this).attr("data-display", data["createdTimeStamp"]); - break; - } - } - ); } - }); - } + ], + "createdRow": function(row, data) { + + $(row).attr("data-type", "selectable"); + $(row).attr("data-id", data["id"]); + $.each($("td", row), + function(colIndex) { + switch (colIndex) { + case 1: + $(this).attr("data-grid-label", "Code"); + $(this).attr("data-display", data["code"]); + break; + case 2: + $(this).attr("data-grid-label", "Status"); + $(this).attr("data-display", data["status"]); + break; + case 3: + $(this).attr("data-grid-label", "Created Timestamp"); + $(this).attr("data-display", data["createdTimeStamp"]); + break; + } + } + ); + } + }); +} + +function loadPolicyCompliance() { + var policyCompliance = $("#policy-view"); + var policyComplianceTemplate = policyCompliance.attr("src"); + var deviceId = policyCompliance.data("device-id"); + var deviceType = policyCompliance.data("device-type"); + var activePolicy = null; - function loadPolicyCompliance() { - var policyCompliance = $("#policy-view"); - var policyComplianceTemplate = policyCompliance.attr("src"); - var deviceId = policyCompliance.data("device-id"); - var deviceType = policyCompliance.data("device-type"); - var activePolicy = null; - - $.template( - "policy-view", - policyComplianceTemplate, - function (template) { - var getEffectivePolicyURL = "/api/device-mgt/v1.0/devices/" + deviceType + "/" + deviceId + "/effective-policy"; - var getDeviceComplianceURL = "/api/device-mgt/v1.0/devices/" + deviceType + "/" + deviceId + "/compliance-data"; - invokerUtil.get( - getEffectivePolicyURL, - // success-callback - function (data, textStatus, jqXHR) { - if (jqXHR.status == 200) { - $("#policy-spinner").addClass("hidden"); - if(data){ - data = JSON.parse(data); - if (data["active"] == true) { - activePolicy = data; - invokerUtil.get( - getDeviceComplianceURL, - // success-callback - function (data, textStatus, jqXHR) { - if (jqXHR.status == 200 && data) { - var viewModel = {}; - viewModel["policy"] = activePolicy; - viewModel["deviceType"] = deviceType; - viewModel["deviceId"] = deviceId; - viewModel["appContext"] = context; - data = JSON.parse(data); - var content; - if (data["complianceData"]) { - if (data["complianceData"]["complianceFeatures"] && - data["complianceData"]["complianceFeatures"].length > 0) { - viewModel["compliance"] = "NON-COMPLIANT"; - viewModel["complianceFeatures"] = data["complianceData"]["complianceFeatures"]; - content = template(viewModel); - $("#policy-list-container").html(content); - } else { - viewModel["compliance"] = "COMPLIANT"; - content = template(viewModel); - $("#policy-list-container").html(content); - $("#policy-compliance-table").addClass("hidden"); - } + $.template( + "policy-view", + policyComplianceTemplate, + function(template) { + var getEffectivePolicyURL = "/api/device-mgt/v1.0/devices/" + deviceType + "/" + deviceId + "/effective-policy"; + var getDeviceComplianceURL = "/api/device-mgt/v1.0/devices/" + deviceType + "/" + deviceId + "/compliance-data"; + invokerUtil.get( + getEffectivePolicyURL, + // success-callback + function(data, textStatus, jqXHR) { + if (jqXHR.status == 200) { + $("#policy-spinner").addClass("hidden"); + if (data) { + data = JSON.parse(data); + if (data["active"] == true) { + activePolicy = data; + invokerUtil.get( + getDeviceComplianceURL, + // success-callback + function(data, textStatus, jqXHR) { + if (jqXHR.status == 200 && data) { + var viewModel = {}; + viewModel["policy"] = activePolicy; + viewModel["deviceType"] = deviceType; + viewModel["deviceId"] = deviceId; + viewModel["appContext"] = context; + data = JSON.parse(data); + var content; + if (data["complianceData"]) { + if (data["complianceData"]["complianceFeatures"] && + data["complianceData"]["complianceFeatures"].length > 0) { + viewModel["compliance"] = "NON-COMPLIANT"; + viewModel["complianceFeatures"] = data["complianceData"]["complianceFeatures"]; + content = template(viewModel); + $("#policy-list-container").html(content); } else { - $("#policy-list-container"). - html("

This device " + - "has no policy applied.

"); + viewModel["compliance"] = "COMPLIANT"; + content = template(viewModel); + $("#policy-list-container").html(content); + $("#policy-compliance-table").addClass("hidden"); } + } else { + $("#policy-list-container"). + html("

This device " + + "has no policy applied.

"); } - }, - // error-callback - function () { - $("#policy-list-container"). - html("

Loading policy compliance related data " + - "was not successful. please try refreshing data in a while.

"); } - ); - } + }, + // error-callback + function() { + $("#policy-list-container"). + html("

Loading policy compliance related data " + + "was not successful. please try refreshing data in a while.

"); + } + ); } } - }, - // error-callback - function () { - $("#policy-list-container"). - html("

Loading policy compliance related data " + - "was not successful. please try refreshing data in a while.

"); } - ); - } - ); - } + }, + // error-callback + function() { + $("#policy-list-container"). + html("

Loading policy compliance related data " + + "was not successful. please try refreshing data in a while.

"); + } + ); + } + ); +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/view.hbs b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/view.hbs index 8a58f285ab..e9b704cd7a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/view.hbs +++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.view/view.hbs @@ -15,156 +15,114 @@ specific language governing permissions and limitations under the License. }} +{{#zone "topCss"}} + {{css "css/main.css"}} +{{/zone}} {{unit "cdmf.unit.lib.editable"}} {{#zone "content"}} {{#if deviceFound}} {{#if isAuthorized}} - - {{#defineZone "device-details-header"}} -

- Device {{device.name}} - {{#if device.viewModel.model}} - - ( {{device.viewModel.vendor}} {{device.viewModel.model}} ) - - {{/if}} -

- {{/defineZone}} -
-
-
-
-
- {{#defineZone "device-thumbnail"}} - - {{/defineZone}} -
-
- -
- {{#defineZone "overview-section"}} -
- Device Overview - {{label}}
- {{unit "cdmf.unit.device.overview-section" device=device}} - {{/defineZone}} - {{#defineZone "operation-status"}}{{/defineZone}} - {{#defineZone "device-opetations"}} -
- Operations -
-
- {{unit "cdmf.unit.device.operation-bar" device=device}} -
- {{/defineZone}} -
-
-
- -
- -
-
- - - {{#defineZone "device-view-tab-contents"}} - {{#defineZone "device-details-tab-contents"}} -
-

- - No Device details avaialbe yet. +

+
+
+ {{#defineZone "device-details-header"}} +

+ {{#if device.viewModel.model}} +

{{device.viewModel.vendor}} {{device.viewModel.model}}

+ {{/if}} +

Ownership - {{device.viewModel.ownership}}

+

Device is + + {{#equal device.status "ACTIVE"}}Active{{/equal}} + {{#equal device.status "INACTIVE"}}Inactive{{/equal}} + {{#equal device.status "BLOCKED"}}Blocked{{/equal}} + {{#equal device.status "REMOVED"}}Removed{{/equal}} + {{#equal device.status "UNREACHABLE"}}Unreachable{{/equal}} +

-
- {{/defineZone}} - - {{#defineZone "device-view-tab-injected-conents"}} - {{/defineZone}} - - {{#defineZone "device-view-tab-operations-log-conents"}} -
- - -
-
- - - - - - -
-
-

- - There are no operations, performed yet on this device. -

-
-
- - - - - - - - - - -
Operation CodeStatusRequest created at
-
-
-
{{/defineZone}} - {{/defineZone}} - +
+
+
+
+
+ {{#defineZone "device-details"}} + {{/defineZone}} +
+ {{#defineZone "device-opetations"}} +
+
+

Device Operations

+
+ {{unit "cdmf.unit.device.operation-bar" device=device}} +
+ {{/defineZone}} +
+
+ +
+ +
+ {{#defineZone "device-view-tab-contents"}} +
+
+
+ + + + + + + + + + + + +
NamePositionOffice
+ +
-
-
+ {{#defineZone "device-view-tab-injected-conents"}} + {{/defineZone}} + {{/defineZone}} + + + + {{else}}

Permission Denied @@ -192,4 +150,4 @@ -{{/zone}} \ No newline at end of file +{{/zone}} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.ui.theme/public/css/custom-desktop.css b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.ui.theme/public/css/custom-desktop.css index 4d04a5bdf2..4b3cb525a9 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.ui.theme/public/css/custom-desktop.css +++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.ui.theme/public/css/custom-desktop.css @@ -2859,7 +2859,8 @@ a.ast-type-item:hover { font-size: 12px; text-decoration: none; margin-right: 10px; - color: #526A84; + /*color: #526A84;*/ + color: #333; min-width: 70px; background: #fafafa; padding: 2px 10px 10px 10px;