Merge branch 'application-mgt-new' of https://gitlab.com/entgra/carbon-device-mgt into application-mgt-new

This commit is contained in:
nipunnadeen 2019-08-23 15:53:18 +05:30
commit cda071643d
96 changed files with 10144 additions and 222 deletions

4
.gitignore vendored
View File

@ -35,4 +35,8 @@ components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react
components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/dist/
components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/package-lock.json
components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/tmp/
components/device-mgt/io.entgra.device.mgt.ui/react-app/node_modules/
components/device-mgt/io.entgra.device.mgt.ui/react-app/dist/
components/device-mgt/io.entgra.device.mgt.ui/react-app/package-lock.json
components/device-mgt/io.entgra.device.mgt.ui/react-app/tmp/

View File

@ -55,7 +55,7 @@ public class AuthenticationHandler extends AbstractHandler {
private static final String X_JWT_ASSERTION = "X-JWT-Assertion";
private static final String JWTTOKEN = "JWTToken";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER = "Bearer ";
private static final String BEARER = "Basic ";
private static final String CONTENT_TYPE = "Content-Type";
private IOTServerConfiguration iotServerConfiguration;
@ -95,7 +95,7 @@ public class AuthenticationHandler extends AbstractHandler {
log.debug("Verify Cert:\n" + mdmSignature);
}
URI certVerifyUrl = new URI(iotServerConfiguration.getVerificationEndpoint() + "ios");
Map<String, String> certVerifyHeaders = this.setHeaders(this.restInvoker);
Map<String, String> certVerifyHeaders = this.setHeaders();
Certificate certificate = new Certificate();
certificate.setPem(mdmSignature);
@ -127,7 +127,7 @@ public class AuthenticationHandler extends AbstractHandler {
String deviceType = this.getDeviceType(messageContext.getTo().getAddress().trim());
URI certVerifyUrl = new URI(iotServerConfiguration.getVerificationEndpoint() + deviceType);
Map<String, String> certVerifyHeaders = this.setHeaders(this.restInvoker);
Map<String, String> certVerifyHeaders = this.setHeaders();
Certificate certificate = new Certificate();
certificate.setPem(subjectDN);
certificate.setTenantId(tenantId);
@ -157,7 +157,7 @@ public class AuthenticationHandler extends AbstractHandler {
}
String deviceType = this.getDeviceType(messageContext.getTo().getAddress().trim());
URI certVerifyUrl = new URI(iotServerConfiguration.getVerificationEndpoint() + deviceType);
Map<String, String> certVerifyHeaders = this.setHeaders(this.restInvoker);
Map<String, String> certVerifyHeaders = this.setHeaders();
Certificate certificate = new Certificate();
certificate.setPem(encodedPem);
@ -184,9 +184,6 @@ public class AuthenticationHandler extends AbstractHandler {
} catch (URISyntaxException e) {
log.error("Error while processing certificate.", e);
return false;
} catch (APIMCertificateMGTException e) {
log.error("Error while processing certificate.", e);
return false;
} catch (CertificateException e) {
log.error("Certificate issue occurred when generating converting PEM to x509Certificate", e);
return false;
@ -212,9 +209,9 @@ public class AuthenticationHandler extends AbstractHandler {
return null;
}
private Map<String, String> setHeaders(RESTInvoker restInvoker) throws APIMCertificateMGTException {
private Map<String, String> setHeaders() {
Map<String, String> map = new HashMap<>();
String accessToken = Utils.getAccessToken(iotServerConfiguration, restInvoker);
String accessToken = Utils.getBase64EncodedToken(iotServerConfiguration);
map.put(AUTHORIZATION, BEARER + accessToken);
map.put(CONTENT_TYPE, "application/json");
return map;

View File

@ -135,38 +135,14 @@ public class Utils {
}
/**
* This class get the access token from the key manager.
* This method is used to get the base64 encoded token.
*
* @param iotServerConfiguration Instance of the IoTsererConfiguration.
* @return Access token will be returned.
* @throws APIMCertificateMGTException
*/
public static String getAccessToken(IOTServerConfiguration iotServerConfiguration, RESTInvoker restInvoker)
throws APIMCertificateMGTException {
try {
if (clientId == null || clientSecret == null) {
getClientSecretes(iotServerConfiguration, restInvoker);
}
URI tokenUrl = new URI(iotServerConfiguration.getOauthTokenEndpoint());
String tokenContent = "grant_type=password&username=" + iotServerConfiguration.getUsername() + "&password=" +
iotServerConfiguration.getPassword() + "&scope=activity-view";
String tokenBasicAuth = "Basic " + Base64.encode((clientId + ":" + clientSecret).getBytes());
Map<String, String> tokenHeaders = new HashMap<>();
tokenHeaders.put("Authorization", tokenBasicAuth);
tokenHeaders.put("Content-Type", "application/x-www-form-urlencoded");
RESTResponse response = restInvoker.invokePOST(tokenUrl, tokenHeaders, tokenContent);
if (log.isDebugEnabled()) {
log.debug("Token response:" + response.getContent());
}
JSONObject jsonResponse = new JSONObject(response.getContent());
return jsonResponse.getString("access_token");
} catch (URISyntaxException | IOException e) {
throw new APIMCertificateMGTException("Error occurred while trying to call oauth token endpoint", e);
} catch (JSONException e) {
throw new APIMCertificateMGTException("Error occurred while converting the json to object", e);
}
public static String getBase64EncodedToken(IOTServerConfiguration iotServerConfiguration) {
return Base64.encode((iotServerConfiguration.getUsername() + ":" + iotServerConfiguration.getPassword()).
getBytes());
}
/**

View File

@ -92,8 +92,6 @@ public class AuthenticationHandlerTest extends BaseAPIHandlerTest {
HashMap<String, String> transportHeaders = new HashMap<>();
transportHeaders.put(AuthConstants.MDM_SIGNATURE, "some cert");
setMockClient();
this.mockClient.setResponse(getDCRResponse());
this.mockClient.setResponse(getAccessTokenReponse());
this.mockClient.setResponse(getValidationResponse());
boolean response = this.handler.handleRequest(createSynapseMessageContext("<empty/>", this.synapseConfiguration,
transportHeaders, "https://test.com/testservice/device-mgt/testdevice"));
@ -107,7 +105,6 @@ public class AuthenticationHandlerTest extends BaseAPIHandlerTest {
HashMap<String, String> transportHeaders = new HashMap<>();
transportHeaders.put(AuthConstants.PROXY_MUTUAL_AUTH_HEADER, "Test Header");
setMockClient();
this.mockClient.setResponse(getAccessTokenReponse());
this.mockClient.setResponse(getValidationResponse());
boolean response = this.handler.handleRequest(createSynapseMessageContext("<empty/>", this.synapseConfiguration,
transportHeaders, "https://test.com/testservice/device-mgt/testdevice"));
@ -121,7 +118,6 @@ public class AuthenticationHandlerTest extends BaseAPIHandlerTest {
HashMap<String, String> transportHeaders = new HashMap<>();
transportHeaders.put(AuthConstants.MUTUAL_AUTH_HEADER, "Test Header");
setMockClient();
this.mockClient.setResponse(getAccessTokenReponse());
this.mockClient.setResponse(getValidationResponse());
MessageContext messageContext = createSynapseMessageContext("<empty/>", this.synapseConfiguration,
transportHeaders, "https://test.com/testservice/device-mgt/testdevice");
@ -141,7 +137,6 @@ public class AuthenticationHandlerTest extends BaseAPIHandlerTest {
HashMap<String, String> transportHeaders = new HashMap<>();
transportHeaders.put(AuthConstants.ENCODED_PEM, "encoded pem");
setMockClient();
this.mockClient.setResponse(getAccessTokenReponse());
this.mockClient.setResponse(getValidationResponse());
MessageContext messageContext = createSynapseMessageContext("<empty/>", this.synapseConfiguration,
transportHeaders, "https://test.com/testservice/device-mgt/testdevice");
@ -156,7 +151,6 @@ public class AuthenticationHandlerTest extends BaseAPIHandlerTest {
HashMap<String, String> transportHeaders = new HashMap<>();
transportHeaders.put(AuthConstants.ENCODED_PEM, "encoded pem");
setMockClient();
this.mockClient.setResponse(getAccessTokenReponse());
this.mockClient.setResponse(getInvalidResponse());
MessageContext messageContext = createSynapseMessageContext("<empty/>", this.synapseConfiguration,
transportHeaders, "https://test.com/testservice/device-mgt/testdevice");
@ -185,7 +179,6 @@ public class AuthenticationHandlerTest extends BaseAPIHandlerTest {
HashMap<String, String> transportHeaders = new HashMap<>();
transportHeaders.put(AuthConstants.ENCODED_PEM, "encoded pem");
setMockClient();
this.mockClient.setResponse(getAccessTokenReponse());
this.mockClient.setResponse(null);
MessageContext messageContext = createSynapseMessageContext("<empty/>", this.synapseConfiguration,
transportHeaders, "https://test.com/testservice/device-mgt/testdevice");

View File

@ -172,6 +172,10 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
sql += " AND AP_APP.DEVICE_TYPE_ID = ?";
}
if (filter.getLimit() == -1) {
sql = sql.replace("LIMIT ? OFFSET ?", "");
}
String sortingOrder = "ASC";
if (!StringUtils.isEmpty(filter.getSortBy() )) {
sortingOrder = filter.getSortBy();
@ -182,12 +186,14 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql);
){
if (filter.getLimit() != -1) {
if (filter.getLimit() == 0) {
stmt.setInt(paramIndex++, 100);
} else {
stmt.setInt(paramIndex++, filter.getLimit());
}
stmt.setInt(paramIndex++, filter.getOffset());
}
stmt.setInt(paramIndex++, tenantId);
if (filter.getAppType() != null && !filter.getAppType().isEmpty()) {

View File

@ -147,9 +147,8 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when obtaining database connection for updating the device subscriptions of "
+ "application. Updated by: " + updateBy + " and updating action triggered from "
+ actionTriggeredFrom;
String msg = "Error occurred while executing SQL to update the device subscriptions of application. "
+ "Updated by: " + updateBy + " and updating action triggered from " + actionTriggeredFrom;
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
}
@ -356,7 +355,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while getting device subscription data for application ID: " + appReleaseId;
String msg = "Error occurred while while running SQL to get device subscription data for application ID: " + appReleaseId;
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
}
@ -618,12 +617,12 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
stmt.executeBatch();
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to update the user subscriptions of "
+ "application.";
String msg = "Error occurred while obtaining the DB connection to update the user/role/group subscriptions "
+ "of application.";
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when obtaining database connection for updating the user subscriptions of "
String msg = "Error occurred while processing SQL to update the user/role/group subscriptions of "
+ "application.";
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
@ -691,8 +690,8 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when obtaining database connection for updating the subscription status of the "
+ "device subscription.";
String msg = "Error occurred when processing SQL to update the subscription status of the device "
+ "subscription.";
log.error(msg);
throw new ApplicationManagementDAOException(msg, e);
}

View File

@ -37,7 +37,7 @@
<Extension name="ApplicationStorageManager">
<ClassName>org.wso2.carbon.device.application.mgt.core.impl.ApplicationStorageManagerImpl</ClassName>
<Parameters>
<Parameter name="StoragePath">repository/resources/apps/</Parameter>
<Parameter name="StoragePath">/tmp/apps/</Parameter>
<Parameter name="MaxScreenShotCount">6</Parameter>
</Parameters>
</Extension>

View File

@ -30,14 +30,8 @@ import org.wso2.carbon.apimgt.annotations.api.Scope;
import org.wso2.carbon.apimgt.annotations.api.Scopes;
import org.wso2.carbon.device.application.mgt.common.ErrorResponse;
import org.wso2.carbon.device.application.mgt.common.PaginationResult;
import org.wso2.carbon.device.application.mgt.common.response.Review;
import org.wso2.carbon.device.application.mgt.common.wrapper.ReviewWrapper;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@ -69,13 +63,13 @@ tags = {
scopes = {
@Scope(
name = "Update a Review",
description = "Update a Review of applications.",
description = "Update a Review of application.",
key = "perm:admin:app:review:update",
permissions = {"/app-mgt/publisher/admin/review/update"}
),
@Scope(
name = "Get Review Details",
description = "Get review details of applications.",
description = "Get review details of application.",
key = "perm:admin:app:review:view",
permissions = {"/app-mgt/publisher/admin/review/view"}
)

View File

@ -49,11 +49,11 @@ import javax.ws.rs.core.Response;
@SwaggerDefinition(
info = @Info(
version = "1.0.0",
title = "ApplicationDTO Storage Management Service",
title = "Application Storage Management Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = "name", value = "ApplicationStorageManagementService"),
@ExtensionProperty(name = "context", value = "/api/application-mgt-store/v1.0/store-applications"),
@ExtensionProperty(name = "context", value = "/api/application-mgt-store/v1.0/applications"),
})
}
),
@ -65,7 +65,7 @@ import javax.ws.rs.core.Response;
@Scopes(
scopes = {
@Scope(
name = "Get ApplicationDTO Details",
name = "Get Application Details",
description = "Get application details",
key = "perm:app:store:view",
permissions = {"/app-mgt/store/application/view"}
@ -73,8 +73,8 @@ import javax.ws.rs.core.Response;
}
)
@Path("/applications")
@Api(value = "ApplicationDTO Management", description = "This API carries all app store management related operations " +
"such as get all the applications etc.")
@Api(value = "Application Management", description = "This API carries all app store management related operations such"
+ " as get all the applications etc.")
@Produces(MediaType.APPLICATION_JSON)
public interface ApplicationManagementAPI {
@ -89,7 +89,7 @@ public interface ApplicationManagementAPI {
httpMethod = "GET",
value = "get all applications",
notes = "This will get all applications",
tags = "ApplicationDTO Management",
tags = "Application Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:app:store:view")
@ -129,7 +129,7 @@ public interface ApplicationManagementAPI {
httpMethod = "GET",
value = "get the application of requesting application type",
notes = "This will get the application identified by the application type and name, if exists",
tags = "ApplicationDTO Management",
tags = "Application Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:app:store:view")
@ -144,7 +144,7 @@ public interface ApplicationManagementAPI {
response = ApplicationDTO.class),
@ApiResponse(
code = 404,
message = "ApplicationDTO not found"),
message = "Application not found"),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while getting relevant application.",

View File

@ -37,6 +37,10 @@
<param-name>doAuthentication</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>basicAuth</param-name>
<param-value>true</param-value>
</context-param>
<!--publish to apim-->
<context-param>

View File

@ -324,9 +324,14 @@ public class CertificateGenerator {
KeyStoreReader keyStoreReader = new KeyStoreReader();
if (distinguishedName != null && !distinguishedName.isEmpty()) {
if (distinguishedName.contains("/CN=")) {
String[] dnSplits = distinguishedName.split("/CN=");
String commonNameExtracted = dnSplits[dnSplits.length - 1];
String[] dnSplits = distinguishedName.split("/");
for (String dnPart : dnSplits) {
if (dnPart.contains("CN=")) {
String commonNameExtracted = dnPart.replace("CN=", "");
lookUpCertificate = keyStoreReader.getCertificateBySerial(commonNameExtracted);
break;
}
}
} else {
LdapName ldapName;
try {

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2017, 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>3.2.9-SNAPSHOT</version>
</parent>
<artifactId>io.entgra.device.mgt.ui</artifactId>
<version>3.2.9-SNAPSHOT</version>
<packaging>war</packaging>
<name>WSO2 Carbon - Device Management UI Component</name>
<url>http://wso2.org</url>
<description>This Component contains Device Management UI</description>
<dependencies>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes>WEB-INF/lib/*cxf*.jar</packagingExcludes>
<warName>entgra</warName>
<webResources>
<resource>
<directory>${npm.output.directory}/dist</directory>
</resource>
<resource>
<directory>${npm.output.directory}/public</directory>
<targetPath>public</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>${frontend.mave.version}</version>
<configuration>
<workingDirectory>${npm.working.dir}</workingDirectory>
<!-- where to install npm -->
<installDirectory>${npm.install.dir}</installDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<nodeVersion>${node.version}</nodeVersion>
<npmVersion>${npm.version}</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<!-- Optional configuration which provides for running any npm command -->
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>prod</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run-script ${npm.build.command}</arguments>
</configuration>
<phase>generate-resources</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>platform-windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<!-- Override the executable names for Windows -->
<npm.executable>npm.cmd</npm.executable>
</properties>
</profile>
</profiles>
<properties>
<maven.test.skip>false</maven.test.skip>
<npm.executable>npm</npm.executable>
<npm.build.command>build_prod</npm.build.command>
<npm.working.dir>./react-app</npm.working.dir>
<npm.install.dir>./react-app/tmp</npm.install.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<npm.output.directory>react-app</npm.output.directory>
</properties>
</project>

View File

@ -0,0 +1,11 @@
module.exports = function (api) {
api.cache(true);
const presets = [ "@babel/preset-env",
"@babel/preset-react" ];
const plugins = ["@babel/plugin-proposal-class-properties"];
return {
presets,
plugins
};
};

View File

@ -0,0 +1,78 @@
{
"name": "entgra",
"version": "1.0.0",
"description": "Entgra device management",
"main": "App.js",
"license": "Apache License 2.0",
"dependencies": {
"acorn": "^6.2.0",
"antd": "^3.22.0",
"axios": "^0.18.1",
"javascript-time-ago": "^2.0.1",
"keymirror": "^0.1.1",
"lodash.debounce": "^4.0.8",
"rc-viewer": "0.0.9",
"react-highlight-words": "^0.16.0",
"react-image-viewer-zoom": "^1.0.36",
"react-infinite-scroller": "^1.2.4",
"react-router": "^5.0.1",
"react-router-config": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-scripts": "2.1.8",
"react-star-ratings": "^2.3.0",
"react-twemoji": "^0.2.3",
"react-virtualized": "^9.21.1",
"reqwest": "^2.0.5",
"storm-react-diagrams": "^5.2.1"
},
"devDependencies": {
"@babel/core": "^7.5.4",
"@babel/plugin-proposal-class-properties": "^7.5.0",
"@babel/preset-env": "^7.5.4",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.4.4",
"babel-loader": "^8.0.6",
"body-parser": "^1.19.0",
"chai": "^4.1.2",
"css-loader": "^0.28.11",
"express": "^4.17.1",
"express-pino-logger": "^4.0.0",
"file-loader": "^2.0.0",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"img-loader": "^3.0.1",
"json-loader": "^0.5.7",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"mini-css-extract-plugin": "^0.5.0",
"mocha": "^5.2.0",
"mock-local-storage": "^1.0.5",
"node-env-run": "^3.0.2",
"node-sass": "^4.12.0",
"nodemon": "^1.19.1",
"npm-run-all": "^4.1.5",
"pino-colada": "^1.4.5",
"postcss-loader": "^3.0.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-intl": "^2.9.0",
"sass-loader": "^6.0.7",
"style-loader": "^0.18.2",
"url-loader": "^1.1.2",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2"
},
"scripts": {
"start": "webpack-dev-server --mode development --open",
"dev": "webpack --mode development",
"build": "webpack --mode production",
"watch": "webpack --watch --mode development",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"build_prod": "NODE_ENV=production NODE_OPTIONS=--max_old_space_size=4096 webpack -p --display errors-only --hide-modules",
"build_dev": "NODE_ENV=development webpack -d --watch ",
"server": "node-env-run server --exec nodemon | pino-colada",
"dev2": "run-p server start"
}
}

View File

@ -0,0 +1,43 @@
{
"theme": {
"type": "default",
"value": "lightBaseTheme",
"logo" : "https://entgra.io/assets/images/svg/logo.svg",
"primaryColor": "rgb(24, 144, 255)"
},
"serverConfig": {
"invokerUri": "/ui-request-handler/invoke/application-mgt-entgra/v1.0",
"invoker": {
"uri": "/entgra-ui-request-handler/invoke",
"publisher": "/application-mgt-publisher/v1.0",
"entgra": "/application-mgt-entgra/v1.0",
"admin" : "",
"deviceMgt" : "/device-mgt/v1.0"
},
"loginUri": "/entgra-ui-request-handler/login",
"logoutUri": "/entgra-ui-request-handler/logout",
"platform": "entgra"
},
"defaultPlatformIcons": {
"default": {
"icon": "hdd",
"color": "#535c68",
"theme": "outlined"
},
"android": {
"icon": "android",
"color": "#7db343",
"theme": "filled"
},
"ios": {
"icon": "apple",
"color": "#535c68",
"theme": "filled"
},
"windows": {
"icon": "windows",
"color": "#008cc4",
"theme": "filled"
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
{
"short_name": "App Store",
"name": "WSO2 IoT App Store",
"icons": [
{
"src": "images/favicon.png",
"sizes": "16x16",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,798 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 232 81" style="enable-background:new 0 0 232 81;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{clip-path:url(#SVGID_2_);fill:#308BD6;}
.st2{clip-path:url(#SVGID_2_);fill:#318CD6;}
.st3{clip-path:url(#SVGID_2_);fill:#328DD7;}
.st4{clip-path:url(#SVGID_2_);fill:#338ED7;}
.st5{clip-path:url(#SVGID_2_);fill:#348FD8;}
.st6{clip-path:url(#SVGID_2_);fill:#3590D8;}
.st7{clip-path:url(#SVGID_2_);fill:#3691D9;}
.st8{clip-path:url(#SVGID_2_);fill:#3792D9;}
.st9{clip-path:url(#SVGID_2_);fill:#3893DA;}
.st10{clip-path:url(#SVGID_2_);fill:#3994DA;}
.st11{clip-path:url(#SVGID_2_);fill:#3A95DA;}
.st12{clip-path:url(#SVGID_2_);fill:#3B96DB;}
.st13{clip-path:url(#SVGID_2_);fill:#3C97DB;}
.st14{clip-path:url(#SVGID_2_);fill:#3D98DC;}
.st15{clip-path:url(#SVGID_2_);fill:#3E99DC;}
.st16{clip-path:url(#SVGID_2_);fill:#3F9ADD;}
.st17{clip-path:url(#SVGID_2_);fill:#409BDD;}
.st18{clip-path:url(#SVGID_2_);fill:#419CDE;}
.st19{clip-path:url(#SVGID_2_);fill:#429DDE;}
.st20{clip-path:url(#SVGID_2_);fill:#439EDE;}
.st21{clip-path:url(#SVGID_2_);fill:#449FDF;}
.st22{clip-path:url(#SVGID_2_);fill:#45A0DF;}
.st23{clip-path:url(#SVGID_2_);fill:#46A1E0;}
.st24{clip-path:url(#SVGID_2_);fill:#47A2E0;}
.st25{clip-path:url(#SVGID_2_);fill:#48A4E1;}
.st26{clip-path:url(#SVGID_2_);fill:#49A5E1;}
.st27{clip-path:url(#SVGID_2_);fill:#4AA6E2;}
.st28{clip-path:url(#SVGID_2_);fill:#4BA7E2;}
.st29{clip-path:url(#SVGID_2_);fill:#4CA8E3;}
.st30{clip-path:url(#SVGID_2_);fill:#4DA9E3;}
.st31{clip-path:url(#SVGID_2_);fill:#4EAAE3;}
.st32{clip-path:url(#SVGID_2_);fill:#4FABE4;}
.st33{clip-path:url(#SVGID_2_);fill:#50ACE4;}
.st34{clip-path:url(#SVGID_2_);fill:#51ADE5;}
.st35{clip-path:url(#SVGID_2_);fill:#52AEE5;}
.st36{clip-path:url(#SVGID_2_);fill:#53AFE6;}
.st37{clip-path:url(#SVGID_2_);fill:#54B0E6;}
.st38{clip-path:url(#SVGID_2_);fill:#55B1E7;}
.st39{clip-path:url(#SVGID_2_);fill:#56B2E7;}
.st40{clip-path:url(#SVGID_2_);fill:#57B3E7;}
.st41{clip-path:url(#SVGID_2_);fill:#58B4E8;}
.st42{clip-path:url(#SVGID_2_);fill:#59B5E8;}
.st43{clip-path:url(#SVGID_2_);fill:#5AB6E9;}
.st44{clip-path:url(#SVGID_2_);fill:#5BB7E9;}
.st45{clip-path:url(#SVGID_2_);fill:#5CB8EA;}
.st46{clip-path:url(#SVGID_2_);fill:#5DB9EA;}
.st47{clip-path:url(#SVGID_2_);fill:#5EBAEB;}
.st48{clip-path:url(#SVGID_2_);fill:#5FBBEB;}
.st49{clip-path:url(#SVGID_4_);fill:#2E5E87;}
.st50{clip-path:url(#SVGID_4_);fill:#2E5F88;}
.st51{clip-path:url(#SVGID_4_);fill:#2F5F89;}
.st52{clip-path:url(#SVGID_4_);fill:#2F608A;}
.st53{clip-path:url(#SVGID_4_);fill:#30618B;}
.st54{clip-path:url(#SVGID_4_);fill:#30628C;}
.st55{clip-path:url(#SVGID_4_);fill:#31628D;}
.st56{clip-path:url(#SVGID_4_);fill:#31638E;}
.st57{clip-path:url(#SVGID_4_);fill:#31648F;}
.st58{clip-path:url(#SVGID_4_);fill:#326590;}
.st59{clip-path:url(#SVGID_4_);fill:#326591;}
.st60{clip-path:url(#SVGID_4_);fill:#336692;}
.st61{clip-path:url(#SVGID_4_);fill:#336793;}
.st62{clip-path:url(#SVGID_4_);fill:#346894;}
.st63{clip-path:url(#SVGID_4_);fill:#346895;}
.st64{clip-path:url(#SVGID_4_);fill:#356996;}
.st65{clip-path:url(#SVGID_4_);fill:#356A97;}
.st66{clip-path:url(#SVGID_4_);fill:#356B98;}
.st67{clip-path:url(#SVGID_4_);fill:#366B99;}
.st68{clip-path:url(#SVGID_4_);fill:#366C9A;}
.st69{clip-path:url(#SVGID_4_);fill:#376D9B;}
.st70{clip-path:url(#SVGID_4_);fill:#376E9C;}
.st71{clip-path:url(#SVGID_4_);fill:#386E9D;}
.st72{clip-path:url(#SVGID_4_);fill:#386F9E;}
.st73{clip-path:url(#SVGID_4_);fill:#38709F;}
.st74{clip-path:url(#SVGID_4_);fill:#3971A0;}
.st75{clip-path:url(#SVGID_4_);fill:#3971A1;}
.st76{clip-path:url(#SVGID_4_);fill:#3A72A2;}
.st77{clip-path:url(#SVGID_4_);fill:#3A73A3;}
.st78{clip-path:url(#SVGID_4_);fill:#3B74A4;}
.st79{clip-path:url(#SVGID_4_);fill:#3B74A5;}
.st80{clip-path:url(#SVGID_4_);fill:#3C75A7;}
.st81{clip-path:url(#SVGID_4_);fill:#3C76A8;}
.st82{clip-path:url(#SVGID_4_);fill:#3C76A9;}
.st83{clip-path:url(#SVGID_4_);fill:#3D77AA;}
.st84{clip-path:url(#SVGID_4_);fill:#3D78AB;}
.st85{clip-path:url(#SVGID_4_);fill:#3E79AC;}
.st86{clip-path:url(#SVGID_4_);fill:#3E79AD;}
.st87{clip-path:url(#SVGID_4_);fill:#3F7AAE;}
.st88{clip-path:url(#SVGID_4_);fill:#3F7BAF;}
.st89{clip-path:url(#SVGID_4_);fill:#3F7CB0;}
.st90{clip-path:url(#SVGID_4_);fill:#407CB1;}
.st91{clip-path:url(#SVGID_4_);fill:#407DB2;}
.st92{clip-path:url(#SVGID_4_);fill:#417EB3;}
.st93{clip-path:url(#SVGID_4_);fill:#417FB4;}
.st94{clip-path:url(#SVGID_4_);fill:#427FB5;}
.st95{clip-path:url(#SVGID_4_);fill:#4280B6;}
.st96{clip-path:url(#SVGID_4_);fill:#4281B7;}
.st97{clip-path:url(#SVGID_4_);fill:#4382B8;}
.st98{clip-path:url(#SVGID_4_);fill:#4382B9;}
.st99{clip-path:url(#SVGID_4_);fill:#4483BA;}
.st100{clip-path:url(#SVGID_4_);fill:#4484BB;}
.st101{clip-path:url(#SVGID_4_);fill:#4585BC;}
.st102{clip-path:url(#SVGID_4_);fill:#4585BD;}
.st103{clip-path:url(#SVGID_4_);fill:#4686BE;}
.st104{clip-path:url(#SVGID_4_);fill:#4687BF;}
.st105{clip-path:url(#SVGID_4_);fill:#4688C0;}
.st106{clip-path:url(#SVGID_4_);fill:#4788C1;}
.st107{clip-path:url(#SVGID_4_);fill:#4789C2;}
.st108{clip-path:url(#SVGID_4_);fill:#488AC3;}
.st109{clip-path:url(#SVGID_4_);fill:#488BC4;}
.st110{clip-path:url(#SVGID_4_);fill:#498BC5;}
.st111{clip-path:url(#SVGID_4_);fill:#498CC6;}
.st112{clip-path:url(#SVGID_6_);fill:#4CE8C6;}
.st113{clip-path:url(#SVGID_6_);fill:#4BE8C7;}
.st114{clip-path:url(#SVGID_6_);fill:#4AE7C7;}
.st115{clip-path:url(#SVGID_6_);fill:#49E7C8;}
.st116{clip-path:url(#SVGID_6_);fill:#48E6C8;}
.st117{clip-path:url(#SVGID_6_);fill:#47E6C9;}
.st118{clip-path:url(#SVGID_6_);fill:#46E6C9;}
.st119{clip-path:url(#SVGID_6_);fill:#45E5CA;}
.st120{clip-path:url(#SVGID_6_);fill:#44E5CB;}
.st121{clip-path:url(#SVGID_6_);fill:#43E4CB;}
.st122{clip-path:url(#SVGID_6_);fill:#42E4CC;}
.st123{clip-path:url(#SVGID_6_);fill:#41E4CC;}
.st124{clip-path:url(#SVGID_6_);fill:#40E3CD;}
.st125{clip-path:url(#SVGID_6_);fill:#3FE3CE;}
.st126{clip-path:url(#SVGID_6_);fill:#3EE2CE;}
.st127{clip-path:url(#SVGID_6_);fill:#3DE2CF;}
.st128{clip-path:url(#SVGID_6_);fill:#3CE2CF;}
.st129{clip-path:url(#SVGID_6_);fill:#3BE1D0;}
.st130{clip-path:url(#SVGID_6_);fill:#3AE1D0;}
.st131{clip-path:url(#SVGID_6_);fill:#39E0D1;}
.st132{clip-path:url(#SVGID_6_);fill:#38E0D2;}
.st133{clip-path:url(#SVGID_6_);fill:#37E0D2;}
.st134{clip-path:url(#SVGID_6_);fill:#36DFD3;}
.st135{clip-path:url(#SVGID_6_);fill:#35DFD3;}
.st136{clip-path:url(#SVGID_6_);fill:#34DED4;}
.st137{clip-path:url(#SVGID_6_);fill:#33DED5;}
.st138{clip-path:url(#SVGID_6_);fill:#31DED5;}
.st139{clip-path:url(#SVGID_6_);fill:#30DDD6;}
.st140{clip-path:url(#SVGID_6_);fill:#2FDDD6;}
.st141{clip-path:url(#SVGID_6_);fill:#2EDCD7;}
.st142{clip-path:url(#SVGID_6_);fill:#2DDCD7;}
.st143{clip-path:url(#SVGID_6_);fill:#2CDCD8;}
.st144{clip-path:url(#SVGID_6_);fill:#2BDBD9;}
.st145{clip-path:url(#SVGID_6_);fill:#2ADBD9;}
.st146{clip-path:url(#SVGID_6_);fill:#29DADA;}
.st147{clip-path:url(#SVGID_6_);fill:#28DADA;}
.st148{clip-path:url(#SVGID_6_);fill:#27DADB;}
.st149{clip-path:url(#SVGID_6_);fill:#26D9DB;}
.st150{clip-path:url(#SVGID_6_);fill:#25D9DC;}
.st151{clip-path:url(#SVGID_6_);fill:#24D8DD;}
.st152{clip-path:url(#SVGID_6_);fill:#23D8DD;}
.st153{clip-path:url(#SVGID_6_);fill:#22D8DE;}
.st154{clip-path:url(#SVGID_6_);fill:#21D7DE;}
.st155{clip-path:url(#SVGID_6_);fill:#20D7DF;}
.st156{clip-path:url(#SVGID_6_);fill:#1FD6E0;}
.st157{clip-path:url(#SVGID_6_);fill:#1ED6E0;}
.st158{clip-path:url(#SVGID_6_);fill:#1DD6E1;}
.st159{clip-path:url(#SVGID_6_);fill:#1CD5E1;}
.st160{clip-path:url(#SVGID_6_);fill:#1BD5E2;}
.st161{clip-path:url(#SVGID_6_);fill:#1AD4E2;}
.st162{clip-path:url(#SVGID_6_);fill:#19D4E3;}
.st163{opacity:0.4;}
.st164{clip-path:url(#SVGID_8_);fill:#4CE8C6;}
.st165{clip-path:url(#SVGID_8_);fill:#4BE8C7;}
.st166{clip-path:url(#SVGID_8_);fill:#4AE7C7;}
.st167{clip-path:url(#SVGID_8_);fill:#49E7C8;}
.st168{clip-path:url(#SVGID_8_);fill:#48E6C8;}
.st169{clip-path:url(#SVGID_8_);fill:#47E6C9;}
.st170{clip-path:url(#SVGID_8_);fill:#46E6C9;}
.st171{clip-path:url(#SVGID_8_);fill:#45E5CA;}
.st172{clip-path:url(#SVGID_8_);fill:#44E5CB;}
.st173{clip-path:url(#SVGID_8_);fill:#43E4CB;}
.st174{clip-path:url(#SVGID_8_);fill:#42E4CC;}
.st175{clip-path:url(#SVGID_8_);fill:#41E4CC;}
.st176{clip-path:url(#SVGID_8_);fill:#40E3CD;}
.st177{clip-path:url(#SVGID_8_);fill:#3FE3CE;}
.st178{clip-path:url(#SVGID_8_);fill:#3EE2CE;}
.st179{clip-path:url(#SVGID_8_);fill:#3DE2CF;}
.st180{clip-path:url(#SVGID_8_);fill:#3CE2CF;}
.st181{clip-path:url(#SVGID_8_);fill:#3BE1D0;}
.st182{clip-path:url(#SVGID_8_);fill:#3AE1D0;}
.st183{clip-path:url(#SVGID_8_);fill:#39E0D1;}
.st184{clip-path:url(#SVGID_8_);fill:#38E0D2;}
.st185{clip-path:url(#SVGID_8_);fill:#37E0D2;}
.st186{clip-path:url(#SVGID_8_);fill:#36DFD3;}
.st187{clip-path:url(#SVGID_8_);fill:#35DFD3;}
.st188{clip-path:url(#SVGID_8_);fill:#34DED4;}
.st189{clip-path:url(#SVGID_8_);fill:#33DED5;}
.st190{clip-path:url(#SVGID_8_);fill:#31DED5;}
.st191{clip-path:url(#SVGID_8_);fill:#30DDD6;}
.st192{clip-path:url(#SVGID_8_);fill:#2FDDD6;}
.st193{clip-path:url(#SVGID_8_);fill:#2EDCD7;}
.st194{clip-path:url(#SVGID_8_);fill:#2DDCD7;}
.st195{clip-path:url(#SVGID_8_);fill:#2CDCD8;}
.st196{clip-path:url(#SVGID_8_);fill:#2BDBD9;}
.st197{clip-path:url(#SVGID_8_);fill:#2ADBD9;}
.st198{clip-path:url(#SVGID_8_);fill:#29DADA;}
.st199{clip-path:url(#SVGID_8_);fill:#28DADA;}
.st200{clip-path:url(#SVGID_8_);fill:#27DADB;}
.st201{clip-path:url(#SVGID_8_);fill:#26D9DB;}
.st202{clip-path:url(#SVGID_8_);fill:#25D9DC;}
.st203{clip-path:url(#SVGID_8_);fill:#24D8DD;}
.st204{clip-path:url(#SVGID_8_);fill:#23D8DD;}
.st205{clip-path:url(#SVGID_8_);fill:#22D8DE;}
.st206{clip-path:url(#SVGID_8_);fill:#21D7DE;}
.st207{clip-path:url(#SVGID_8_);fill:#20D7DF;}
.st208{clip-path:url(#SVGID_8_);fill:#1FD6E0;}
.st209{clip-path:url(#SVGID_8_);fill:#1ED6E0;}
.st210{clip-path:url(#SVGID_8_);fill:#1DD6E1;}
.st211{clip-path:url(#SVGID_8_);fill:#1CD5E1;}
.st212{clip-path:url(#SVGID_8_);fill:#1BD5E2;}
.st213{clip-path:url(#SVGID_8_);fill:#1AD4E2;}
.st214{clip-path:url(#SVGID_8_);fill:#19D4E3;}
.st215{opacity:0.5;}
.st216{clip-path:url(#SVGID_10_);fill:#316490;}
.st217{clip-path:url(#SVGID_10_);fill:#316591;}
.st218{clip-path:url(#SVGID_10_);fill:#326692;}
.st219{clip-path:url(#SVGID_10_);fill:#326693;}
.st220{clip-path:url(#SVGID_10_);fill:#336794;}
.st221{clip-path:url(#SVGID_10_);fill:#336895;}
.st222{clip-path:url(#SVGID_10_);fill:#346996;}
.st223{clip-path:url(#SVGID_10_);fill:#346997;}
.st224{clip-path:url(#SVGID_10_);fill:#356A98;}
.st225{clip-path:url(#SVGID_10_);fill:#356B99;}
.st226{clip-path:url(#SVGID_10_);fill:#366C9A;}
.st227{clip-path:url(#SVGID_10_);fill:#366C9B;}
.st228{clip-path:url(#SVGID_10_);fill:#366D9C;}
.st229{clip-path:url(#SVGID_10_);fill:#376E9D;}
.st230{clip-path:url(#SVGID_10_);fill:#376F9E;}
.st231{clip-path:url(#SVGID_10_);fill:#386F9F;}
.st232{clip-path:url(#SVGID_10_);fill:#3870A0;}
.st233{clip-path:url(#SVGID_10_);fill:#3971A1;}
.st234{clip-path:url(#SVGID_10_);fill:#3972A2;}
.st235{clip-path:url(#SVGID_10_);fill:#3A72A3;}
.st236{clip-path:url(#SVGID_10_);fill:#3A73A4;}
.st237{clip-path:url(#SVGID_10_);fill:#3B74A5;}
.st238{clip-path:url(#SVGID_10_);fill:#3B75A6;}
.st239{clip-path:url(#SVGID_10_);fill:#3B75A7;}
.st240{clip-path:url(#SVGID_10_);fill:#3C76A8;}
.st241{clip-path:url(#SVGID_10_);fill:#3C77A9;}
.st242{clip-path:url(#SVGID_10_);fill:#3D78AA;}
.st243{clip-path:url(#SVGID_10_);fill:#3D78AC;}
.st244{clip-path:url(#SVGID_10_);fill:#3E79AD;}
.st245{clip-path:url(#SVGID_10_);fill:#3E7AAE;}
.st246{clip-path:url(#SVGID_10_);fill:#3F7BAF;}
.st247{clip-path:url(#SVGID_10_);fill:#3F7BB0;}
.st248{clip-path:url(#SVGID_10_);fill:#3F7CB1;}
.st249{clip-path:url(#SVGID_10_);fill:#407DB2;}
.st250{clip-path:url(#SVGID_10_);fill:#407EB3;}
.st251{clip-path:url(#SVGID_10_);fill:#417EB4;}
.st252{clip-path:url(#SVGID_10_);fill:#417FB5;}
.st253{clip-path:url(#SVGID_10_);fill:#4280B6;}
.st254{clip-path:url(#SVGID_10_);fill:#4281B7;}
.st255{clip-path:url(#SVGID_10_);fill:#4381B8;}
.st256{clip-path:url(#SVGID_10_);fill:#4382B9;}
.st257{clip-path:url(#SVGID_10_);fill:#4483BA;}
.st258{clip-path:url(#SVGID_10_);fill:#4484BB;}
.st259{clip-path:url(#SVGID_10_);fill:#4484BC;}
.st260{clip-path:url(#SVGID_10_);fill:#4585BD;}
.st261{clip-path:url(#SVGID_10_);fill:#4586BE;}
.st262{clip-path:url(#SVGID_10_);fill:#4687BF;}
.st263{clip-path:url(#SVGID_10_);fill:#4687C0;}
.st264{clip-path:url(#SVGID_10_);fill:#4788C1;}
.st265{clip-path:url(#SVGID_10_);fill:#4789C2;}
.st266{clip-path:url(#SVGID_10_);fill:#488AC3;}
.st267{clip-path:url(#SVGID_10_);fill:#488AC4;}
.st268{clip-path:url(#SVGID_10_);fill:#498BC5;}
.st269{clip-path:url(#SVGID_10_);fill:#498CC6;}
.st270{clip-path:url(#SVGID_12_);fill:#5FBBEB;}
.st271{clip-path:url(#SVGID_12_);fill:#5EBAEB;}
.st272{clip-path:url(#SVGID_12_);fill:#5DB9EA;}
.st273{clip-path:url(#SVGID_12_);fill:#5CB8EA;}
.st274{clip-path:url(#SVGID_12_);fill:#5BB7E9;}
.st275{clip-path:url(#SVGID_12_);fill:#5AB6E9;}
.st276{clip-path:url(#SVGID_12_);fill:#59B5E8;}
.st277{clip-path:url(#SVGID_12_);fill:#58B4E8;}
.st278{clip-path:url(#SVGID_12_);fill:#57B3E7;}
.st279{clip-path:url(#SVGID_12_);fill:#56B2E7;}
.st280{clip-path:url(#SVGID_12_);fill:#55B1E7;}
.st281{clip-path:url(#SVGID_12_);fill:#54B0E6;}
.st282{clip-path:url(#SVGID_12_);fill:#53AFE6;}
.st283{clip-path:url(#SVGID_12_);fill:#52AEE5;}
.st284{clip-path:url(#SVGID_12_);fill:#51ADE5;}
.st285{clip-path:url(#SVGID_12_);fill:#50ACE4;}
.st286{clip-path:url(#SVGID_12_);fill:#4FABE4;}
.st287{clip-path:url(#SVGID_12_);fill:#4EAAE3;}
.st288{clip-path:url(#SVGID_12_);fill:#4DA9E3;}
.st289{clip-path:url(#SVGID_12_);fill:#4CA8E3;}
.st290{clip-path:url(#SVGID_12_);fill:#4BA7E2;}
.st291{clip-path:url(#SVGID_12_);fill:#4AA6E2;}
.st292{clip-path:url(#SVGID_12_);fill:#49A5E1;}
.st293{clip-path:url(#SVGID_12_);fill:#48A4E1;}
.st294{clip-path:url(#SVGID_12_);fill:#47A2E0;}
.st295{clip-path:url(#SVGID_12_);fill:#46A1E0;}
.st296{clip-path:url(#SVGID_12_);fill:#45A0DF;}
.st297{clip-path:url(#SVGID_12_);fill:#449FDF;}
.st298{clip-path:url(#SVGID_12_);fill:#439EDE;}
.st299{clip-path:url(#SVGID_12_);fill:#429DDE;}
.st300{clip-path:url(#SVGID_12_);fill:#419CDE;}
.st301{clip-path:url(#SVGID_12_);fill:#409BDD;}
.st302{clip-path:url(#SVGID_12_);fill:#3F9ADD;}
.st303{clip-path:url(#SVGID_12_);fill:#3E99DC;}
.st304{clip-path:url(#SVGID_12_);fill:#3D98DC;}
.st305{clip-path:url(#SVGID_12_);fill:#3C97DB;}
.st306{clip-path:url(#SVGID_12_);fill:#3B96DB;}
.st307{clip-path:url(#SVGID_12_);fill:#3A95DA;}
.st308{clip-path:url(#SVGID_12_);fill:#3994DA;}
.st309{clip-path:url(#SVGID_12_);fill:#3893DA;}
.st310{clip-path:url(#SVGID_12_);fill:#3792D9;}
.st311{clip-path:url(#SVGID_12_);fill:#3691D9;}
.st312{clip-path:url(#SVGID_12_);fill:#3590D8;}
.st313{clip-path:url(#SVGID_12_);fill:#348FD8;}
.st314{clip-path:url(#SVGID_12_);fill:#338ED7;}
.st315{clip-path:url(#SVGID_12_);fill:#328DD7;}
.st316{clip-path:url(#SVGID_12_);fill:#318CD6;}
.st317{clip-path:url(#SVGID_12_);fill:#308BD6;}
.st318{fill:#316490;}
</style>
<path class="st0" d="M224,81H8c-4.4,0-8-3.6-8-8V8c0-4.4,3.6-8,8-8h216c4.4,0,8,3.6,8,8v65C232,77.4,228.4,81,224,81z"/>
<g>
<g>
<g>
<g>
<g>
<defs>
<polygon id="SVGID_1_" points="15.2,15 15.2,62.9 59.2,56 59.2,8.1 "/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<polygon class="st1" points="56.7,62.9 59.2,56 59.2,62.9 "/>
<polygon class="st1" points="55.7,62.9 59.2,53.1 59.2,56 56.7,62.9 "/>
<polygon class="st2" points="54.6,62.9 59.2,50.3 59.2,53.1 55.7,62.9 "/>
<polygon class="st3" points="53.6,62.9 59.2,47.4 59.2,50.3 54.6,62.9 "/>
<polygon class="st4" points="52.6,62.9 59.2,44.6 59.2,47.4 53.6,62.9 "/>
<polygon class="st5" points="51.5,62.9 59.2,41.7 59.2,44.6 52.6,62.9 "/>
<polygon class="st6" points="50.5,62.9 59.2,38.9 59.2,41.7 51.5,62.9 "/>
<polygon class="st7" points="49.4,62.9 59.2,36 59.2,38.9 50.5,62.9 "/>
<polygon class="st8" points="48.4,62.9 59.2,33.1 59.2,36 49.4,62.9 "/>
<polygon class="st9" points="47.4,62.9 59.2,30.3 59.2,33.1 48.4,62.9 "/>
<polygon class="st10" points="46.3,62.9 59.2,27.4 59.2,30.3 47.4,62.9 "/>
<polygon class="st11" points="45.3,62.9 59.2,24.6 59.2,27.4 46.3,62.9 "/>
<polygon class="st12" points="44.2,62.9 59.2,21.7 59.2,24.6 45.3,62.9 "/>
<polygon class="st13" points="43.2,62.9 59.2,18.9 59.2,21.7 44.2,62.9 "/>
<polygon class="st14" points="42.2,62.9 59.2,16 59.2,18.9 43.2,62.9 "/>
<polygon class="st15" points="41.1,62.9 59.2,13.2 59.2,16 42.2,62.9 "/>
<polygon class="st16" points="40.1,62.9 59.2,10.3 59.2,13.2 41.1,62.9 "/>
<polygon class="st17" points="39,62.9 59,8.1 59.2,8.1 59.2,10.3 40.1,62.9 "/>
<polygon class="st18" points="38,62.9 57.9,8.1 59,8.1 39,62.9 "/>
<polygon class="st19" points="37,62.9 56.9,8.1 57.9,8.1 38,62.9 "/>
<polygon class="st20" points="35.9,62.9 55.9,8.1 56.9,8.1 37,62.9 "/>
<polygon class="st21" points="34.9,62.9 54.8,8.1 55.9,8.1 35.9,62.9 "/>
<polygon class="st22" points="33.8,62.9 53.8,8.1 54.8,8.1 34.9,62.9 "/>
<polygon class="st23" points="32.8,62.9 52.7,8.1 53.8,8.1 33.8,62.9 "/>
<polygon class="st24" points="31.8,62.9 51.7,8.1 52.7,8.1 32.8,62.9 "/>
<polygon class="st25" points="30.7,62.9 50.7,8.1 51.7,8.1 31.8,62.9 "/>
<polygon class="st26" points="29.7,62.9 49.6,8.1 50.7,8.1 30.7,62.9 "/>
<polygon class="st27" points="28.7,62.9 48.6,8.1 49.6,8.1 29.7,62.9 "/>
<polygon class="st28" points="27.6,62.9 47.5,8.1 48.6,8.1 28.7,62.9 "/>
<polygon class="st29" points="26.6,62.9 46.5,8.1 47.5,8.1 27.6,62.9 "/>
<polygon class="st30" points="25.5,62.9 45.5,8.1 46.5,8.1 26.6,62.9 "/>
<polygon class="st31" points="24.5,62.9 44.4,8.1 45.5,8.1 25.5,62.9 "/>
<polygon class="st32" points="23.5,62.9 43.4,8.1 44.4,8.1 24.5,62.9 "/>
<polygon class="st33" points="22.4,62.9 42.3,8.1 43.4,8.1 23.5,62.9 "/>
<polygon class="st34" points="21.4,62.9 41.3,8.1 42.3,8.1 22.4,62.9 "/>
<polygon class="st35" points="20.3,62.9 40.3,8.1 41.3,8.1 21.4,62.9 "/>
<polygon class="st36" points="19.3,62.9 39.2,8.1 40.3,8.1 20.3,62.9 "/>
<polygon class="st37" points="18.3,62.9 38.2,8.1 39.2,8.1 19.3,62.9 "/>
<polygon class="st38" points="17.2,62.9 37.1,8.1 38.2,8.1 18.3,62.9 "/>
<polygon class="st39" points="16.2,62.9 36.1,8.1 37.1,8.1 17.2,62.9 "/>
<polygon class="st40" points="15.2,62.7 35.1,8.1 36.1,8.1 16.2,62.9 15.2,62.9 "/>
<polygon class="st41" points="15.2,59.9 34,8.1 35.1,8.1 15.2,62.7 "/>
<polygon class="st42" points="15.2,57 33,8.1 34,8.1 15.2,59.9 "/>
<polygon class="st43" points="15.2,54.2 32,8.1 33,8.1 15.2,57 "/>
<polygon class="st44" points="15.2,51.3 30.9,8.1 32,8.1 15.2,54.2 "/>
<polygon class="st45" points="15.2,48.5 29.9,8.1 30.9,8.1 15.2,51.3 "/>
<polygon class="st46" points="15.2,45.6 28.8,8.1 29.9,8.1 15.2,48.5 "/>
<polygon class="st47" points="15.2,42.7 27.8,8.1 28.8,8.1 15.2,45.6 "/>
<polygon class="st48" points="15.2,39.9 26.8,8.1 27.8,8.1 15.2,42.7 "/>
<polygon class="st48" points="26.8,8.1 15.2,39.9 15.2,8.1 "/>
</g>
</g>
</g>
<g>
<g>
<g>
<defs>
<polygon id="SVGID_3_" points="83,68.7 83,20.8 59.2,8.1 59.2,56 "/>
</defs>
<clipPath id="SVGID_4_">
<use xlink:href="#SVGID_3_" style="overflow:visible;"/>
</clipPath>
<polygon class="st49" points="83,20.8 78.4,8.1 83,8.1 "/>
<polygon class="st49" points="83,22 77.9,8.1 78.4,8.1 83,20.8 "/>
<polygon class="st50" points="83,23.1 77.5,8.1 77.9,8.1 83,22 "/>
<polygon class="st51" points="83,24.3 77.1,8.1 77.5,8.1 83,23.1 "/>
<polygon class="st52" points="83,25.4 76.7,8.1 77.1,8.1 83,24.3 "/>
<polygon class="st53" points="83,26.6 76.3,8.1 76.7,8.1 83,25.4 "/>
<polygon class="st54" points="83,27.7 75.9,8.1 76.3,8.1 83,26.6 "/>
<polygon class="st55" points="83,28.8 75.4,8.1 75.9,8.1 83,27.7 "/>
<polygon class="st56" points="83,30 75,8.1 75.4,8.1 83,28.8 "/>
<polygon class="st57" points="83,31.1 74.6,8.1 75,8.1 83,30 "/>
<polygon class="st58" points="83,32.3 74.2,8.1 74.6,8.1 83,31.1 "/>
<polygon class="st59" points="83,33.4 73.8,8.1 74.2,8.1 83,32.3 "/>
<polygon class="st60" points="83,34.6 73.4,8.1 73.8,8.1 83,33.4 "/>
<polygon class="st61" points="83,35.7 72.9,8.1 73.4,8.1 83,34.6 "/>
<polygon class="st62" points="83,36.9 72.5,8.1 72.9,8.1 83,35.7 "/>
<polygon class="st63" points="83,38 72.1,8.1 72.5,8.1 83,36.9 "/>
<polygon class="st64" points="83,39.1 71.7,8.1 72.1,8.1 83,38 "/>
<polygon class="st65" points="83,40.3 71.3,8.1 71.7,8.1 83,39.1 "/>
<polygon class="st66" points="83,41.4 70.9,8.1 71.3,8.1 83,40.3 "/>
<polygon class="st67" points="83,42.6 70.4,8.1 70.9,8.1 83,41.4 "/>
<polygon class="st68" points="83,43.7 70,8.1 70.4,8.1 83,42.6 "/>
<polygon class="st69" points="83,44.9 69.6,8.1 70,8.1 83,43.7 "/>
<polygon class="st70" points="83,46 69.2,8.1 69.6,8.1 83,44.9 "/>
<polygon class="st71" points="83,47.2 68.8,8.1 69.2,8.1 83,46 "/>
<polygon class="st72" points="83,48.3 68.4,8.1 68.8,8.1 83,47.2 "/>
<polygon class="st73" points="83,49.5 67.9,8.1 68.4,8.1 83,48.3 "/>
<polygon class="st74" points="83,50.6 67.5,8.1 67.9,8.1 83,49.5 "/>
<polygon class="st75" points="83,51.7 67.1,8.1 67.5,8.1 83,50.6 "/>
<polygon class="st76" points="83,52.9 66.7,8.1 67.1,8.1 83,51.7 "/>
<polygon class="st77" points="83,54 66.3,8.1 66.7,8.1 83,52.9 "/>
<polygon class="st78" points="83,55.2 65.9,8.1 66.3,8.1 83,54 "/>
<polygon class="st79" points="83,56.3 65.4,8.1 65.9,8.1 83,55.2 "/>
<polygon class="st80" points="83,57.5 65,8.1 65.4,8.1 83,56.3 "/>
<polygon class="st81" points="83,58.6 64.6,8.1 65,8.1 83,57.5 "/>
<polygon class="st82" points="83,59.8 64.2,8.1 64.6,8.1 83,58.6 "/>
<polygon class="st83" points="83,60.9 63.8,8.1 64.2,8.1 83,59.8 "/>
<polygon class="st84" points="83,62 63.3,8.1 63.8,8.1 83,60.9 "/>
<polygon class="st85" points="83,63.2 62.9,8.1 63.3,8.1 83,62 "/>
<polygon class="st86" points="83,64.3 62.5,8.1 62.9,8.1 83,63.2 "/>
<polygon class="st87" points="83,65.5 62.1,8.1 62.5,8.1 83,64.3 "/>
<polygon class="st88" points="83,66.6 61.7,8.1 62.1,8.1 83,65.5 "/>
<polygon class="st89" points="83,67.8 61.3,8.1 61.7,8.1 83,66.6 "/>
<polygon class="st90" points="82.9,68.7 60.8,8.1 61.3,8.1 83,67.8 83,68.7 "/>
<polygon class="st91" points="82.5,68.7 60.4,8.1 60.8,8.1 82.9,68.7 "/>
<polygon class="st92" points="82.1,68.7 60,8.1 60.4,8.1 82.5,68.7 "/>
<polygon class="st93" points="81.6,68.7 59.6,8.1 60,8.1 82.1,68.7 "/>
<polygon class="st94" points="81.2,68.7 59.2,8.1 59.2,8.1 59.6,8.1 81.6,68.7 "/>
<polygon class="st95" points="80.8,68.7 59.2,9.3 59.2,8.1 81.2,68.7 "/>
<polygon class="st96" points="80.4,68.7 59.2,10.4 59.2,9.3 80.8,68.7 "/>
<polygon class="st97" points="80,68.7 59.2,11.6 59.2,10.4 80.4,68.7 "/>
<polygon class="st98" points="79.6,68.7 59.2,12.7 59.2,11.6 80,68.7 "/>
<polygon class="st99" points="79.1,68.7 59.2,13.8 59.2,12.7 79.6,68.7 "/>
<polygon class="st100" points="78.7,68.7 59.2,15 59.2,13.8 79.1,68.7 "/>
<polygon class="st101" points="78.3,68.7 59.2,16.1 59.2,15 78.7,68.7 "/>
<polygon class="st102" points="77.9,68.7 59.2,17.3 59.2,16.1 78.3,68.7 "/>
<polygon class="st103" points="77.5,68.7 59.2,18.4 59.2,17.3 77.9,68.7 "/>
<polygon class="st104" points="77.1,68.7 59.2,19.6 59.2,18.4 77.5,68.7 "/>
<polygon class="st105" points="76.6,68.7 59.2,20.7 59.2,19.6 77.1,68.7 "/>
<polygon class="st106" points="76.2,68.7 59.2,21.9 59.2,20.7 76.6,68.7 "/>
<polygon class="st107" points="75.8,68.7 59.2,23 59.2,21.9 76.2,68.7 "/>
<polygon class="st108" points="75.4,68.7 59.2,24.1 59.2,23 75.8,68.7 "/>
<polygon class="st109" points="75,68.7 59.2,25.3 59.2,24.1 75.4,68.7 "/>
<polygon class="st110" points="74.6,68.7 59.2,26.4 59.2,25.3 75,68.7 "/>
<polygon class="st111" points="74.1,68.7 59.2,27.6 59.2,26.4 74.6,68.7 "/>
<polygon class="st111" points="59.2,27.6 74.1,68.7 59.2,68.7 "/>
</g>
</g>
</g>
<g>
<g>
<g>
<defs>
<polygon id="SVGID_5_" points="39,75.5 83,68.7 59.2,56 15.2,62.9 "/>
</defs>
<clipPath id="SVGID_6_">
<use xlink:href="#SVGID_5_" style="overflow:visible;"/>
</clipPath>
<path class="st112" d="M83,75.5v-6.9V75.5z"/>
<rect x="82.1" y="56" class="st112" width="0.9" height="19.5"/>
<rect x="81.2" y="56" class="st113" width="0.9" height="19.5"/>
<rect x="80.3" y="56" class="st114" width="0.9" height="19.5"/>
<rect x="79.4" y="56" class="st115" width="0.9" height="19.5"/>
<rect x="78.5" y="56" class="st116" width="0.9" height="19.5"/>
<rect x="77.6" y="56" class="st117" width="0.9" height="19.5"/>
<rect x="76.7" y="56" class="st118" width="0.9" height="19.5"/>
<rect x="75.8" y="56" class="st119" width="0.9" height="19.5"/>
<rect x="74.9" y="56" class="st120" width="0.9" height="19.5"/>
<rect x="74" y="56" class="st121" width="0.9" height="19.5"/>
<rect x="73.1" y="56" class="st122" width="0.9" height="19.5"/>
<rect x="72.2" y="56" class="st123" width="0.9" height="19.5"/>
<rect x="71.3" y="56" class="st124" width="0.9" height="19.5"/>
<rect x="70.4" y="56" class="st125" width="0.9" height="19.5"/>
<rect x="69.5" y="56" class="st126" width="0.9" height="19.5"/>
<rect x="68.6" y="56" class="st127" width="0.9" height="19.5"/>
<rect x="67.7" y="56" class="st128" width="0.9" height="19.5"/>
<rect x="66.8" y="56" class="st129" width="0.9" height="19.5"/>
<rect x="65.9" y="56" class="st130" width="0.9" height="19.5"/>
<rect x="65" y="56" class="st131" width="0.9" height="19.5"/>
<rect x="64.1" y="56" class="st132" width="0.9" height="19.5"/>
<rect x="63.2" y="56" class="st133" width="0.9" height="19.5"/>
<rect x="62.3" y="56" class="st134" width="0.9" height="19.5"/>
<rect x="61.4" y="56" class="st135" width="0.9" height="19.5"/>
<rect x="60.5" y="56" class="st136" width="0.9" height="19.5"/>
<rect x="59.6" y="56" class="st137" width="0.9" height="19.5"/>
<rect x="58.7" y="56" class="st138" width="0.9" height="19.5"/>
<rect x="57.8" y="56" class="st139" width="0.9" height="19.5"/>
<rect x="56.9" y="56" class="st140" width="0.9" height="19.5"/>
<rect x="56" y="56" class="st141" width="0.9" height="19.5"/>
<rect x="55.1" y="56" class="st142" width="0.9" height="19.5"/>
<rect x="54.2" y="56" class="st143" width="0.9" height="19.5"/>
<rect x="53.3" y="56" class="st144" width="0.9" height="19.5"/>
<rect x="52.4" y="56" class="st145" width="0.9" height="19.5"/>
<rect x="51.5" y="56" class="st146" width="0.9" height="19.5"/>
<rect x="50.6" y="56" class="st147" width="0.9" height="19.5"/>
<rect x="49.7" y="56" class="st148" width="0.9" height="19.5"/>
<rect x="48.8" y="56" class="st149" width="0.9" height="19.5"/>
<rect x="47.9" y="56" class="st150" width="0.9" height="19.5"/>
<rect x="47" y="56" class="st151" width="0.9" height="19.5"/>
<rect x="46.1" y="56" class="st152" width="0.9" height="19.5"/>
<rect x="45.2" y="56" class="st153" width="0.9" height="19.5"/>
<rect x="44.3" y="56" class="st154" width="0.9" height="19.5"/>
<rect x="43.4" y="56" class="st155" width="0.9" height="19.5"/>
<rect x="42.5" y="56" class="st156" width="0.9" height="19.5"/>
<rect x="41.6" y="56" class="st157" width="0.9" height="19.5"/>
<rect x="40.7" y="56" class="st158" width="0.9" height="19.5"/>
<rect x="39.8" y="56" class="st159" width="0.9" height="19.5"/>
<rect x="38.9" y="56" class="st160" width="0.9" height="19.5"/>
<rect x="38" y="56" class="st161" width="0.9" height="19.5"/>
<rect x="37.1" y="56" class="st162" width="0.9" height="19.5"/>
<rect x="15.2" y="56" class="st162" width="21.9" height="19.5"/>
</g>
</g>
</g>
</g>
<g class="st163">
<g>
<g>
<defs>
<polygon id="SVGID_7_" points="39,27.6 15.2,15 15.2,62.9 39,75.5 "/>
</defs>
<clipPath id="SVGID_8_">
<use xlink:href="#SVGID_7_" style="overflow:visible;"/>
</clipPath>
<polygon class="st164" points="37.7,75.5 39,73.7 39,75.5 "/>
<polygon class="st165" points="36.4,75.5 39,71.8 39,73.7 37.7,75.5 "/>
<polygon class="st166" points="35.1,75.5 39,70 39,71.8 36.4,75.5 "/>
<polygon class="st167" points="33.8,75.5 39,68.1 39,70 35.1,75.5 "/>
<polygon class="st168" points="32.5,75.5 39,66.2 39,68.1 33.8,75.5 "/>
<polygon class="st169" points="31.9,75.5 31.4,75.2 39,64.4 39,66.2 32.5,75.5 "/>
<polygon class="st170" points="31.4,75.2 30.5,74.6 39,62.5 39,64.4 "/>
<polygon class="st171" points="30.5,74.6 29.7,74 39,60.7 39,62.5 "/>
<polygon class="st172" points="29.7,74 28.8,73.4 39,58.8 39,60.7 "/>
<polygon class="st173" points="28.8,73.4 27.9,72.8 39,57 39,58.8 "/>
<polygon class="st174" points="27.9,72.8 27.1,72.1 39,55.1 39,57 "/>
<polygon class="st175" points="27.1,72.1 26.2,71.5 39,53.3 39,55.1 "/>
<polygon class="st176" points="26.2,71.5 25.3,70.9 39,51.4 39,53.3 "/>
<polygon class="st177" points="25.3,70.9 24.4,70.3 39,49.6 39,51.4 "/>
<polygon class="st178" points="24.4,70.3 23.6,69.7 39,47.7 39,49.6 "/>
<polygon class="st179" points="23.6,69.7 22.7,69.1 39,45.9 39,47.7 "/>
<polygon class="st180" points="22.7,69.1 21.8,68.5 39,44 39,45.9 "/>
<polygon class="st181" points="21.8,68.5 21,67.9 39,42.2 39,44 "/>
<polygon class="st182" points="21,67.9 20.1,67.3 39,40.3 39,42.2 "/>
<polygon class="st183" points="20.1,67.3 19.2,66.7 39,38.4 39,40.3 "/>
<polygon class="st184" points="19.2,66.7 18.4,66 39,36.6 39,38.4 "/>
<polygon class="st185" points="18.4,66 17.5,65.4 39,34.7 39,36.6 "/>
<polygon class="st186" points="17.5,65.4 16.6,64.8 39,32.9 39,34.7 "/>
<polygon class="st187" points="16.6,64.8 15.7,64.2 39,31 39,32.9 "/>
<polygon class="st188" points="15.7,64.2 15.2,63.8 15.2,63.2 39,29.2 39,31 "/>
<polygon class="st189" points="15.2,61.3 39,27.3 39,29.2 15.2,63.2 "/>
<polygon class="st190" points="15.2,59.5 38.4,26.3 39,26.7 39,27.3 15.2,61.3 "/>
<polygon class="st191" points="15.2,57.6 37.5,25.7 38.4,26.3 15.2,59.5 "/>
<polygon class="st192" points="15.2,55.7 36.7,25 37.5,25.7 15.2,57.6 "/>
<polygon class="st193" points="15.2,53.9 35.8,24.4 36.7,25 15.2,55.7 "/>
<polygon class="st194" points="15.2,52 34.9,23.8 35.8,24.4 15.2,53.9 "/>
<polygon class="st195" points="15.2,50.2 34.1,23.2 34.9,23.8 15.2,52 "/>
<polygon class="st196" points="15.2,48.3 33.2,22.6 34.1,23.2 15.2,50.2 "/>
<polygon class="st197" points="15.2,46.5 32.3,22 33.2,22.6 15.2,48.3 "/>
<polygon class="st198" points="15.2,44.6 31.5,21.4 32.3,22 15.2,46.5 "/>
<polygon class="st199" points="15.2,42.8 30.6,20.8 31.5,21.4 15.2,44.6 "/>
<polygon class="st200" points="15.2,40.9 29.7,20.2 30.6,20.8 15.2,42.8 "/>
<polygon class="st201" points="15.2,39.1 28.8,19.6 29.7,20.2 15.2,40.9 "/>
<polygon class="st202" points="15.2,37.2 28,18.9 28.8,19.6 15.2,39.1 "/>
<polygon class="st203" points="15.2,35.4 27.1,18.3 28,18.9 15.2,37.2 "/>
<polygon class="st204" points="15.2,33.5 26.2,17.7 27.1,18.3 15.2,35.4 "/>
<polygon class="st205" points="15.2,31.6 25.4,17.1 26.2,17.7 15.2,33.5 "/>
<polygon class="st206" points="15.2,29.8 24.5,16.5 25.4,17.1 15.2,31.6 "/>
<polygon class="st207" points="15.2,27.9 23.6,15.9 24.5,16.5 15.2,29.8 "/>
<polygon class="st208" points="15.2,26.1 22.7,15.3 23.6,15.9 15.2,27.9 "/>
<polygon class="st209" points="15.2,24.2 21.7,15 22.3,15 22.7,15.3 15.2,26.1 "/>
<polygon class="st210" points="15.2,22.4 20.4,15 21.7,15 15.2,24.2 "/>
<polygon class="st211" points="15.2,20.5 19.1,15 20.4,15 15.2,22.4 "/>
<polygon class="st212" points="15.2,18.7 17.8,15 19.1,15 15.2,20.5 "/>
<polygon class="st213" points="15.2,16.8 16.5,15 17.8,15 15.2,18.7 "/>
<polygon class="st214" points="15.2,15 15.2,15 16.5,15 15.2,16.8 "/>
<polygon class="st214" points="15.2,15 15.2,15 15.2,15 "/>
</g>
</g>
</g>
<g class="st215">
<g>
<g>
<defs>
<polygon id="SVGID_9_" points="83,20.8 59.2,8.1 15.2,15 39,27.6 "/>
</defs>
<clipPath id="SVGID_10_">
<use xlink:href="#SVGID_9_" style="overflow:visible;"/>
</clipPath>
<path class="st216" d="M83,27.6v-6.9V27.6z"/>
<rect x="81.7" y="8.1" class="st216" width="1.3" height="19.5"/>
<rect x="80.4" y="8.1" class="st217" width="1.3" height="19.5"/>
<rect x="79.2" y="8.1" class="st218" width="1.3" height="19.5"/>
<rect x="77.9" y="8.1" class="st219" width="1.3" height="19.5"/>
<rect x="76.7" y="8.1" class="st220" width="1.3" height="19.5"/>
<rect x="75.4" y="8.1" class="st221" width="1.3" height="19.5"/>
<rect x="74.2" y="8.1" class="st222" width="1.3" height="19.5"/>
<rect x="72.9" y="8.1" class="st223" width="1.3" height="19.5"/>
<rect x="71.7" y="8.1" class="st224" width="1.3" height="19.5"/>
<rect x="70.4" y="8.1" class="st225" width="1.3" height="19.5"/>
<rect x="69.2" y="8.1" class="st226" width="1.3" height="19.5"/>
<rect x="67.9" y="8.1" class="st227" width="1.3" height="19.5"/>
<rect x="66.6" y="8.1" class="st228" width="1.3" height="19.5"/>
<rect x="65.4" y="8.1" class="st229" width="1.3" height="19.5"/>
<rect x="64.1" y="8.1" class="st230" width="1.3" height="19.5"/>
<rect x="62.9" y="8.1" class="st231" width="1.3" height="19.5"/>
<rect x="61.6" y="8.1" class="st232" width="1.3" height="19.5"/>
<rect x="60.4" y="8.1" class="st233" width="1.3" height="19.5"/>
<rect x="59.1" y="8.1" class="st234" width="1.3" height="19.5"/>
<rect x="57.9" y="8.1" class="st235" width="1.3" height="19.5"/>
<rect x="56.6" y="8.1" class="st236" width="1.3" height="19.5"/>
<rect x="55.3" y="8.1" class="st237" width="1.3" height="19.5"/>
<rect x="54.1" y="8.1" class="st238" width="1.3" height="19.5"/>
<rect x="52.8" y="8.1" class="st239" width="1.3" height="19.5"/>
<rect x="51.6" y="8.1" class="st240" width="1.3" height="19.5"/>
<rect x="50.3" y="8.1" class="st241" width="1.3" height="19.5"/>
<rect x="49.1" y="8.1" class="st242" width="1.3" height="19.5"/>
<rect x="47.8" y="8.1" class="st243" width="1.3" height="19.5"/>
<rect x="46.6" y="8.1" class="st244" width="1.3" height="19.5"/>
<rect x="45.3" y="8.1" class="st245" width="1.3" height="19.5"/>
<rect x="44.1" y="8.1" class="st246" width="1.3" height="19.5"/>
<rect x="42.8" y="8.1" class="st247" width="1.3" height="19.5"/>
<rect x="41.5" y="8.1" class="st248" width="1.3" height="19.5"/>
<rect x="40.3" y="8.1" class="st249" width="1.3" height="19.5"/>
<rect x="39" y="8.1" class="st250" width="1.3" height="19.5"/>
<rect x="37.8" y="8.1" class="st251" width="1.3" height="19.5"/>
<rect x="36.5" y="8.1" class="st252" width="1.3" height="19.5"/>
<rect x="35.3" y="8.1" class="st253" width="1.3" height="19.5"/>
<rect x="34" y="8.1" class="st254" width="1.3" height="19.5"/>
<rect x="32.8" y="8.1" class="st255" width="1.3" height="19.5"/>
<rect x="31.5" y="8.1" class="st256" width="1.3" height="19.5"/>
<rect x="30.2" y="8.1" class="st257" width="1.3" height="19.5"/>
<rect x="29" y="8.1" class="st258" width="1.3" height="19.5"/>
<rect x="27.7" y="8.1" class="st259" width="1.3" height="19.5"/>
<rect x="26.5" y="8.1" class="st260" width="1.3" height="19.5"/>
<rect x="25.2" y="8.1" class="st261" width="1.3" height="19.5"/>
<rect x="24" y="8.1" class="st262" width="1.3" height="19.5"/>
<rect x="22.7" y="8.1" class="st263" width="1.3" height="19.5"/>
<rect x="21.5" y="8.1" class="st264" width="1.3" height="19.5"/>
<rect x="20.2" y="8.1" class="st265" width="1.3" height="19.5"/>
<rect x="18.9" y="8.1" class="st266" width="1.3" height="19.5"/>
<rect x="17.7" y="8.1" class="st267" width="1.3" height="19.5"/>
<rect x="16.4" y="8.1" class="st268" width="1.3" height="19.5"/>
<polygon class="st269" points="15.2,15 15.2,8.1 16.4,8.1 16.4,27.6 15.2,27.6 "/>
<path class="st269" d="M15.2,8.1V15V8.1z"/>
</g>
</g>
</g>
<g class="st215">
<g>
<g>
<defs>
<polygon id="SVGID_11_" points="39,27.6 39,75.5 83,68.7 83,20.8 "/>
</defs>
<clipPath id="SVGID_12_">
<use xlink:href="#SVGID_11_" style="overflow:visible;"/>
</clipPath>
<polygon class="st270" points="77.2,75.5 83,68.7 83,75.5 "/>
<polygon class="st270" points="76.2,75.5 83,67.5 83,68.7 77.2,75.5 "/>
<polygon class="st271" points="75.3,75.5 83,66.3 83,67.5 76.2,75.5 "/>
<polygon class="st272" points="74.3,75.5 83,65.1 83,66.3 75.3,75.5 "/>
<polygon class="st273" points="73.3,75.5 83,64 83,65.1 74.3,75.5 "/>
<polygon class="st274" points="72.3,75.5 83,62.8 83,64 73.3,75.5 "/>
<polygon class="st275" points="71.3,75.5 83,61.6 83,62.8 72.3,75.5 "/>
<polygon class="st276" points="70.3,75.5 83,60.4 83,61.6 71.3,75.5 "/>
<polygon class="st277" points="69.3,75.5 83,59.3 83,60.4 70.3,75.5 "/>
<polygon class="st278" points="68.4,75.5 83,58.1 83,59.3 69.3,75.5 "/>
<polygon class="st279" points="67.4,75.5 83,56.9 83,58.1 68.4,75.5 "/>
<polygon class="st280" points="66.4,75.5 83,55.7 83,56.9 67.4,75.5 "/>
<polygon class="st281" points="65.4,75.5 83,54.6 83,55.7 66.4,75.5 "/>
<polygon class="st282" points="64.4,75.5 83,53.4 83,54.6 65.4,75.5 "/>
<polygon class="st283" points="63.4,75.5 83,52.2 83,53.4 64.4,75.5 "/>
<polygon class="st284" points="62.4,75.5 83,51 83,52.2 63.4,75.5 "/>
<polygon class="st285" points="61.5,75.5 83,49.9 83,51 62.4,75.5 "/>
<polygon class="st286" points="60.5,75.5 83,48.7 83,49.9 61.5,75.5 "/>
<polygon class="st287" points="59.5,75.5 83,47.5 83,48.7 60.5,75.5 "/>
<polygon class="st288" points="58.5,75.5 83,46.3 83,47.5 59.5,75.5 "/>
<polygon class="st289" points="57.5,75.5 83,45.2 83,46.3 58.5,75.5 "/>
<polygon class="st290" points="56.5,75.5 83,44 83,45.2 57.5,75.5 "/>
<polygon class="st291" points="55.6,75.5 83,42.8 83,44 56.5,75.5 "/>
<polygon class="st292" points="54.6,75.5 83,41.7 83,42.8 55.6,75.5 "/>
<polygon class="st293" points="53.6,75.5 83,40.5 83,41.7 54.6,75.5 "/>
<polygon class="st294" points="52.6,75.5 83,39.3 83,40.5 53.6,75.5 "/>
<polygon class="st295" points="51.6,75.5 83,38.1 83,39.3 52.6,75.5 "/>
<polygon class="st296" points="50.6,75.5 83,37 83,38.1 51.6,75.5 "/>
<polygon class="st297" points="49.6,75.5 83,35.8 83,37 50.6,75.5 "/>
<polygon class="st298" points="48.7,75.5 83,34.6 83,35.8 49.6,75.5 "/>
<polygon class="st299" points="47.7,75.5 83,33.4 83,34.6 48.7,75.5 "/>
<polygon class="st300" points="46.7,75.5 83,32.3 83,33.4 47.7,75.5 "/>
<polygon class="st301" points="45.7,75.5 83,31.1 83,32.3 46.7,75.5 "/>
<polygon class="st302" points="44.7,75.5 83,29.9 83,31.1 45.7,75.5 "/>
<polygon class="st303" points="43.7,75.5 83,28.7 83,29.9 44.7,75.5 "/>
<polygon class="st304" points="42.8,75.5 83,27.6 83,28.7 43.7,75.5 "/>
<polygon class="st305" points="41.8,75.5 83,26.4 83,27.6 42.8,75.5 "/>
<polygon class="st306" points="40.8,75.5 83,25.2 83,26.4 41.8,75.5 "/>
<polygon class="st307" points="39.8,75.5 83,24 83,25.2 40.8,75.5 "/>
<polygon class="st308" points="39,75.4 83,22.9 83,24 39.8,75.5 39,75.5 "/>
<polygon class="st309" points="39,74.2 83,21.7 83,22.9 39,75.4 "/>
<polygon class="st310" points="39,73 82.8,20.8 83,20.8 83,21.7 39,74.2 "/>
<polygon class="st311" points="39,71.8 81.8,20.8 82.8,20.8 39,73 "/>
<polygon class="st312" points="39,70.7 80.8,20.8 81.8,20.8 39,71.8 "/>
<polygon class="st313" points="39,69.5 79.8,20.8 80.8,20.8 39,70.7 "/>
<polygon class="st314" points="39,68.3 78.9,20.8 79.8,20.8 39,69.5 "/>
<polygon class="st315" points="39,67.1 77.9,20.8 78.9,20.8 39,68.3 "/>
<polygon class="st316" points="39,66 76.9,20.8 77.9,20.8 39,67.1 "/>
<polygon class="st317" points="39,64.8 75.9,20.8 76.9,20.8 39,66 "/>
<polygon class="st317" points="75.9,20.8 39,64.8 39,20.8 "/>
</g>
</g>
</g>
<g>
<g>
<g>
<path class="st0" d="M36.6,28.1c0-1.4,1.1-2.7,2.5-2.9l22.5-3.6v31.9c0,2.6-1.9,4.7-4.4,5.1L36.5,62L36.6,28.1z M56.7,27.3
l-15.3,2.4l0,26.6l15.4-2.5V27.3z"/>
</g>
</g>
</g>
<g>
<g>
<rect x="36.6" y="48.3" class="st0" width="4.8" height="28.2"/>
</g>
</g>
<g>
<g>
<rect x="59.2" y="3.9" class="st0" width="2.4" height="36.7"/>
</g>
</g>
<g>
<g>
<path class="st0" d="M83,23.4v42.4c0,1.5-1.1,2.9-2.6,3.1l-35.3,5.7c-1.9,0.3-3.6-1.2-3.6-3.1v-7.6c0-1.5,1.1-2.9,2.6-3.1l14-2.2
c2-0.3,3.5-2.1,3.5-4.1l0.2-39.7c0-2.4,2.5-3.9,4.6-2.8l14.3,7.7C82.1,20.4,83,21.8,83,23.4z M86.5,16L62.3,3
c-0.8-0.6-1.9-0.1-1.9,0.9l-1.1,51c0,0.6-0.4,1.1-1,1.2L40,59c-0.6,0.1-1,0.6-1,1.2l0,18.7c0,0.7,0.7,1.3,1.4,1.2L86,71.8
c0.6-0.1,1-0.6,1-1.2V17C87,16.6,86.8,16.3,86.5,16z"/>
</g>
</g>
<g>
<g>
<path class="st0" d="M59.2,11.7l0,7.5c0,1.6-1.2,3-2.7,3.2l-17.1,2.8c-1.6,0.3-2.7,1.6-2.7,3.2l0,40.3c0,2.5-2.6,4-4.8,2.9
l-14.8-7.9c-1.1-0.6-1.7-1.7-1.7-2.9V17.6c0-1.6,1.2-3,2.8-3.2l37.5-5.8C57.4,8.2,59.2,9.7,59.2,11.7z M58.8,2.6l-45.5,7.9
c-0.8,0.1-1.4,0.8-1.4,1.6l-0.2,49.6c-0.1,0.7,0.3,1.3,0.9,1.6l25.2,15.4c1.2,0.6,2.6-0.2,2.5-1.6L39,29.3c0-0.9,0.6-1.7,1.5-1.8
l18.5-2.1c0.9-0.1,1.5-0.8,1.5-1.7l0.2-19.4C60.8,3.3,59.8,2.4,58.8,2.6z"/>
</g>
</g>
<g>
<path class="st0" d="M47.5,43.8c-0.6,0-1.1-0.4-1.2-1c-0.1-0.7,0.3-1.3,1-1.4l10.1-1.6c0.7-0.1,1.3,0.3,1.4,1
c0.1,0.7-0.3,1.3-1,1.4l-10.1,1.6C47.6,43.8,47.6,43.8,47.5,43.8z"/>
</g>
<g>
<g>
<path class="st0" d="M55.2,42.6L55.2,42.6c0.8-0.1,1.6,0.5,1.6,1.3v8.7l4.5,0V30.1l-4.5,0.2V38c0,1.1-0.8,2-1.8,2.2l-1.7,0.3
L55.2,42.6z"/>
</g>
</g>
</g>
<g>
<g>
<polygon class="st318" points="112.5,28.8 112.5,37.1 125.6,37.1 125.6,41.2 112.5,41.2 112.5,49.5 125.6,49.5 125.6,53.6
108,53.6 108,24.7 125.6,24.7 125.6,28.8 "/>
</g>
<g>
<path class="st318" d="M147.2,38.3v14.2l-3.8,1.8h-0.6v-16c0-1.7-0.8-1.9-1.5-1.9h-4.3c-0.7,0-1.5,0.2-1.5,1.9v14.2l-3.8,1.8H131
v-16c0-4,2-6,6-6h4.3C145.2,32.3,147.2,34.3,147.2,38.3z"/>
</g>
<g>
<polygon class="st318" points="161.5,32.3 161.5,32.9 159.8,36.4 156.7,36.4 156.7,52.5 152.8,54.3 152.2,54.3 152.2,27.7
156,25.9 156.7,25.9 156.7,32.3 "/>
</g>
<g>
<path class="st318" d="M174.6,32.3h-4.3c-4,0-6,2-6,6v9.3c0,4,2,6,6,6h4.3c0.6,0,1.1-0.1,1.5-0.3v2c0,1.7-0.8,1.9-1.5,1.9h-10.3
v4.1h10.3c4,0,6-2,6-6v-17C180.5,34.3,178.5,32.3,174.6,32.3z M176.1,47.6c0,1.7-0.8,1.9-1.5,1.9h-4.3c-0.7,0-1.5-0.2-1.5-1.9
v-9.3c0-1.7,0.8-1.9,1.5-1.9h4.3c0.7,0,1.5,0.2,1.5,1.9V47.6z"/>
<path class="st318" d="M197,32.3v0.6l-1.6,3.5H192c-0.7,0-1.5,0.2-1.5,1.9v14.2l-3.8,1.8H186v-16c0-4,2-6,6-6H197z"/>
</g>
<g>
<path class="st318" d="M208,32.3h-7v4.1h7c0.7,0,1.5,0.2,1.5,1.9v1.1c-0.5-0.2-1-0.3-1.5-0.3h-3.9c-4,0-6,2-6,6v2.6c0,4,2,6,6,6
h3.9c0.6,0,1.1-0.1,1.5-0.3v1.4h0.6l3.8-1.8V38.3C214,34.3,212,32.3,208,32.3z M208,49.5h-3.9c-0.7,0-1.5-0.2-1.5-1.9V45
c0-1.7,0.8-1.9,1.5-1.9h3.9c0.7,0,1.5,0.2,1.5,1.9v2.6C209.5,49.3,208.7,49.5,208,49.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,67 @@
{
"Title" : "Title",
"Description" : "Description",
"ShortDescription" : "Short Description",
"Category" : "Category",
"Visibility" : "Visibility",
"Devices" : "Devices",
"Roles" : "Roles",
"Groups" : "Groups",
"Tags" : "Tags",
"Platform" : "Platform",
"Platforms" : "Platforms",
"Applications": "Applications",
"No.Platform" : "No Platforms",
"Screenshots" : "Screenshots",
"Icon" : "Icon",
"Info" : "Info",
"Banner" : "Banner",
"Create.Application" : "Create Application",
"Back" : "Back",
"Cancel" : "Cancel",
"Finish" : "Finish",
"Continue" : "Continue",
"Name" : "Name",
"Application.Name" : "Application Name",
"General" : "General",
"App.Releases" : "Application Releases",
"Package.Manager" : "Package Manager",
"Save" : "Save",
"Create.Release" : "Create Release",
"Release.Channel" : "Release Channel",
"Release" : "Release",
"New.Release.For" : "New Release for",
"Upload.Package.File" : "Upload Package File",
"Upload" : "Upload",
"Select.from.package.library" : "Select from package library",
"Release.Name" : "Release Name",
"Release.Notes" : "Release Notes",
"Send.for.Review" : "Send for Review",
"Production.Releases" : "Production Releases",
"Beta.Releases" : "Beta Releases",
"Alpha.Releases" : "Alpha Releases",
"Version" : "Version",
"Status" : "Status",
"App.Publisher" : "Application Publisher",
"Search.Apps" : "Search for Applications",
"View.In.Store" : "View in Store",
"Last.Updated" : "Last updated on",
"Installs" : "Installs",
"General.Info" : "General Info",
"Select.Platform": "Select Platform",
"Add.Release" : "Add Release to Application",
"Share.With.Tenants" : "Share with Tenants",
"Disable" : "Disable",
"File.Based" : "File Based",
"Activate" : "Activate",
"Yes" : "Yes",
"No" : "No",
"No.Platform.Tags" : "No Platform Tags",
"Create.Platform" : "Create Platform",
"Optional": "Optional",
"Identifier": "Identifier",
"Next": "Next",
"Platform.Enable": "Enable Platform",
"Share.with.Tenants": "Share between all tenants",
"Platform.Properties": "Platform Properties"
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,903 @@
/*
* Copyright (c) 2017, 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.
*/
@font-face {
font-family: "Roboto-Medium";
src: url('../../fonts/Roboto-Medium.woff');
src: local("Roboto-Medium"), url("../../fonts/Roboto-Medium.ttf") format("ttf");
src: local("Roboto-Medium"), url("../../fonts/Roboto-Medium.woff") format("woff");
src: local("Roboto-Medium"), url("../../fonts/Roboto-Medium.woff2") format("woff2");
}
@font-face {
font-family: "Roboto-Regular";
src: url("../../fonts/Roboto-Regular.woff");
src: local("Roboto-Regular"), url("../../fonts/Roboto-Regular.ttf") format("ttf");
src: local("Roboto-Regular"), url("../../fonts/Roboto-Regular.woff") format("woff");
src: local("Roboto-Regular"), url("../../fonts/Roboto-Regular.woff2") format("woff2");
}
/*Colors*/
.primary {
color: white;
background-color: #2196f3 !important;
}
.primary-flat {
color: #2196F3 !important;
}
.danger {
color: white;
background-color: #e91e63 !important;
}
.danger-flat {
color: #e91e63 !important;
}
.grey {
color: #b3b3b3 !important;
}
/* ==================================================================== */
/* Custom button styles based on material design specs. */
.custom-raised {
font-family: Roboto-Medium;
text-transform: uppercase !important;
font-size: 14px !important;
padding-left: 16px !important;
border-radius: 2px !important;
padding-right: 16px !important;
height: 36px !important;
border: none !important;
}
.custom-raised:hover {
cursor: pointer;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
-webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
background-color: #1976D2 !important;
}
.custom-raised:focus {
box-shadow: none !important;
-webkit-box-shadow: none !important;
background-color: #1976D2 !important;
}
.custom-flat {
font-family: Roboto-Medium;
height: 36px !important;
border-radius: 2px !important;
margin-left: 8px !important;
margin-right: 8px !important;
padding-left: 8px !important;
padding-right: 8px !important;
background-color: transparent !important;
text-transform: uppercase;
outline: none !important;
border: none !important;
}
.custom-flat:hover {
cursor: pointer;
background-color: rgba(0, 0, 0, 0.12) !important;
}
.custom-flat:focus {
outline: none !important;
border: none !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
background-color: rgba(0, 0, 0, 0.40) !important;
}
.circle-button {
border-radius: 100% !important;
height: 36px !important;
width: 36px;
}
/* ==================================================================== */
/* Body Styling */
body {
width: 100%;
font-family: "Roboto-Regular" !important;
font-size: 14px !important;
background-color: #e8e8e8 !important;
}
.app-manager-title {
font-family: "Roboto-Medium";
font-size: 20px;
}
.app-manager-sub-title {
font-family: "Roboto-Regular";
font-size: 18px;
}
#app-mgt-footer {
clear: both;
position: relative;
height: 50px;
width: 100%;
color: white;
background-color: #334d88;
}
/* Login page styles*/
#userName {
border-radius: 0;
}
#password {
border-radius: 0;
}
.login-btn {
float: right;
}
.login-header {
background-color: #3f50b5;
color: white;
height: 128px;
width: 100%;
margin: 0 !important;
padding: 20px;
box-shadow: -2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
#login-card {
width: 25%;
height: 50%;
margin: 10% auto;
font-family: Roboto-Regular;
font-size: 14px;
border-radius: 0;
background-color: #ffffff;
box-shadow: -2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.login-header-title {
font-family: Roboto-Medium;
font-size: 20px;
font-weight: 500;
}
.login-header-logo {
height: 70px;
width: 150px;
}
.login-form {
margin: 0 !important;
padding: 40px;
}
/* Base layout container */
/* Base layout header content*/
.header-content {
height: 128px !important;
width: 100% !important;
margin: 0 10px 0 0;
background-color: #3f50b5 !important;
position: fixed; /* Set the navbar to fixed position */
top: 0; /* Position the navbar at the top of the page */
z-index: 2;
box-shadow: -2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
/* Contains the header styles.*/
.header {
padding: 24px 24px 10px 24px;
/*margin: 16px 16px 20px 16px;*/
position: relative;
}
#header-text {
color: #ffffff;
font-size: 20px;
font-family: Roboto-Medium;
top: 10px;
margin-left: 10px;
}
/* The buttons in the header (User and Notification)*/
.header-button-container {
display: flex;
justify-content: flex-end;
}
.header-user-name {
font-family: Roboto-Medium;
font-size: 14px;
padding-top: 15px;
color: white;
}
.header-image {
height: 43px;
width: 100px;
margin-right: 24px;
}
#header-button {
border-radius: 50%;
background-color: transparent;
border: none;
height: 50px;
width: 50px;
margin-right: 10px;
position: relative;
outline: none;
}
#header-button:hover {
background-color: #4353bd;
cursor: pointer;
}
#header-button i {
position: absolute;
bottom: 19px;
left: 17px;
}
.btn-header {
margin-top: 15px;
margin-right: 20px;
color: white;
}
#sub-title {
font-family: Roboto-Regular;
font-size: 18px;
font-weight: 600;
padding-top: 5px;
padding-left: 18px;
color: RGBA(0, 0, 0, 1);
}
/* Search box styles */
.search-box {
display: flex;
float: right;
}
.search-box i {
position: absolute;
top: 5px;
color: #BaBaBa;
}
#search {
position: relative;
color: white;
background-color: transparent;
left: 15px;
top: 0px;
height: 25px;
outline: none;
border: none;
border-radius: 0%;
}
/* Application Add button */
#add-btn-container {
position: absolute;
top: 98px;
}
.add-btn {
background-color: #ff5722;
}
.add-btn:hover {
background-color: #E64A19;
}
#sub-title-container {
height: 100px;
padding: 50px 0 20px 0;
}
.application-container {
padding: 0 !important;
min-height: 100% !important;
margin-top: 128px !important;
}
/* Holds the app pages. */
.store-card {
height: auto;
background-color: white;
box-shadow: 2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
padding: 24px;
}
.platform-link-placeholder {
color: #888888;
float: right;
padding-bottom: 10px;
}
.platform-link-placeholder i {
margin-right: 4px;
}
.application-list {
transition: margin-right .5s;
}
#batch-content {
display: flex;
margin-top: 5px;
}
.app-list-icon {
border-radius: 50%;
height: 50px;
width: 50px
}
.app-table-row {
height: 62px;
cursor: pointer;
padding-top: 6px;
font-family: "Roboto-Regular";
font-size: medium;
}
.app-table-row:hover {
color: white;
background-color: #3f50b5;
}
.app-list-table-header {
margin-top: 30px;
margin-bottom: 10px;
font-family: "Roboto-Medium";
font-size: 15px;
}
.app-view-image {
height: 100px;
width: 100px;
border-radius: 50%;
}
#app-visibility-default {
display: none;
}
#app-image-screenshot {
width: 300px;
height: 300px;
}
#app-image-icon {
width: 300px;
height: 300px;
}
#app-image-banner {
width: 400px;
height: 300px;
}
#form-error {
color: red;
}
.application-create-banner-dropzone {
width: 300px;
height: 150px;
border-radius: 5%;
position: relative;
border: dashed #888888 2px;
}
.application-create-banner-dropzone i {
position: absolute;
top: 65px;
left: 145px;
}
.application-create-screenshot-dropzone {
width: 150px;
height: 150px;
margin: 0 5px 0 5px;
border-radius: 10%;
position: relative;
border: dashed #888888 2px;
}
.application-create-screenshot-dropzone i {
position: absolute;
top: 65px;
left: 65px;
}
.application-create-icon-dropzone {
width: 150px;
height: 150px;
border-radius: 10%;
position: relative;
border: dashed #888888 2px;
}
.application-create-icon-dropzone i {
position: absolute;
top: 65px;
left: 65px;
}
#screenshot-container {
max-width: 600px;
display: flex;
overflow-x: auto;
height: 200px;
}
#app-icon-container {
height: 300px;
overflow-x: auto;
}
#modal-body-content {
max-height: 700px;
padding-left: 24px;
overflow-y: auto;
}
.custom-footer {
justify-content: inherit !important;
margin: 0 !important;
}
.footer-main-btn {
display: flex;
justify-content: flex-end;
}
#img-btn-screenshot {
margin: 0 5px 0 5px;
}
#app-create-modal {
max-width: 850px;
border-radius: 0% !important;
}
.app-create-modal-header {
background-color: #4353bd;
color: white;
padding: 24px !important;
}
.app-create-modal-content {
padding: 0 !important;
}
#store {
border: none;
border-bottom: solid #BDBDBD 1px;
border-radius: 0px;
width: 200px;
}
#version {
border: none;
border-bottom: solid #BDBDBD 1px;
border-radius: 0px;
width: 200px;
}
#app-release-switch-content {
display: flex;
}
#app-release-switch-label {
position: absolute;
float: left;
}
#app-release-switch {
position: absolute;
right: 10px;
}
.image-sub-title {
font-style: italic;
font-size: 12px;
color: #818181;
}
/* Application View */
#application-view-content {
width: 100%;
}
#application-view-row {
margin: 10px 10px 0 20px;
}
#app-icon {
height: 100px;
width: 100px;
border: solid 1px black;
border-radius: 50%;
}
.app-updated-date {
color: #888888;
}
.app-install-count {
font-style: italic;
}
.app-details-tbl {
outline: none;
border-color: #2196F3;
}
.app-details-tbl tr {
margin: 20px 0 0 0;
}
.app-details-tbl td {
margin-left: 10px;
max-width: 400px;
}
/* Application Edit Base Layout */
#application-edit-header {
height: 40px;
width: 100%;
margin-top: 20px;
margin-bottom: 20px;
font-size: 25px;
}
.application-header-text {
margin: 10px 0px 0px 10px;
}
#save-btn-content {
float: right;
}
#app-save-btn {
border-radius: 0%;
}
.save-btn {
margin: 5px 5px 5px 0px;
height: 70%;
width: 50%;
float: right;
}
.save-btn:hover {
cursor: pointer;
}
/*Tab styling*/
div.tab {
float: left;
border-right: 1px solid #d8d8d8;
height: 100%;
}
/* Style the tab buttons */
div.tab button {
display: block;
background-color: inherit;
color: black;
padding: 15px 16px;
width: 100%;
border: none;
outline: none;
text-align: left;
cursor: pointer;
transition: 0.3s;
}
/* Change background color of buttons on hover */
div.tab button:hover {
background-color: #ddd6d7;
cursor: pointer;
}
/* Create an active/current "tab button" class */
div.tab button.active {
background-color: #1b3bcc;
color: white;
}
#application-edit-main-container {
display: flex;
}
#application-edit-outer-content {
height: auto;
}
#app-edit-content {
height: 100%;
position: relative;
}
.back-to-app {
position: absolute;
height: 50px;
width: 50px;
border-radius: 50%;
}
.back-to-app i {
padding: 12px 10px 10px 12px;
}
.back-to-app:hover {
cursor: pointer;
background-color: #dedede;
transition: .5s;
}
/* Create Release and Release management */
.release-header {
margin-top: 20px;
margin-bottom: 20px;
}
.release-create {
height: 150px;
margin-bottom: 20px;
}
.release-detail-content {
width: 100%;
margin-top: 20%;
height: 300px;
}
.form-btn {
float: right;
margin-bottom: 10px;
}
.release-content {
height: 180px;
width: 95%;
border: dashed 1px #626262;
border-radius: 2%;
position: relative;
background-color: #e8e8e8;
}
.release-content:after {
content: "";
letter-spacing: 4px;
}
.release {
margin: 30px 10px 20px 30px;
}
.no-release-content {
position: absolute;
margin-top: 10px;
left: 40%;
}
.button-add:hover {
cursor: pointer;
}
.release-inner {
margin-top: 5%;
}
/* Application Edit General Info */
.app-edit-general-info {
margin-top: 20px;
max-width: 100%;
}
.save-info {
float: right;
margin-bottom: 10px;
}
.app-view-field {
font-family: Roboto-Medium;
font-size: 14px;
}
.app-view-text {
font-family: Roboto-Regular;
font-size: 14px;
}
/* Platform Specific Styles. */
#platform-listing {
margin: 10px;
}
.create-platform i {
margin-right: 10px;
}
#platform-list {
margin-top: 20px;
display: flex;
flex-flow: wrap;
}
.platform-content {
margin: 10px;
padding-top: 16px;
box-shadow: 2px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.platform-content .row {
margin: 0;
}
.platform-content .col {
padding: 0;
}
.platform-content-basic {
padding: 0 16px 0 16px;
display: flex;
}
.platform-content-more-outer {
}
.platform-content-more {
padding: 16px 16px 24px 16px;
}
.platform-content-footer {
display: flex;
padding: 8px 8px 8px 8px;
}
.platform-text-container {
padding: 8px 16px 0 16px;
}
.circle-button {
float: right;
}
.platform-icon-letter {
text-align: center;
text-transform: uppercase;
font-family: Roboto-Medium;
font-size: 70px;
color: white;
padding-top: 15px;
}
.platform-icon-container {
height: 120px;
width: 120px;
background-color: #01579B;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
-webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08) !important;
}
.platform-property-container {
padding-top: 20px;
font-family: Roboto-Regular;
font-size: 14px;
}
.platform-property-row {
align-items: center;
}
.circle-btn-clear {
background-color: white !important;
color: rgba(0, 0, 0, 0.50) !important;
}
.circle-btn-clear:hover {
background-color: white !important;
color: rgba(0, 0, 0, 0.38) !important;
}
.circle-btn-clear:focus {
background-color: white !important;
color: rgba(0, 0, 0, 0.60) !important;
}
.data-table-row-cell {
padding-top: 14px;
}
.error-code {
text-align: center;
font-family: Roboto-Medium;
font-weight: 800;
font-size: 15em;
color: #BaBaBa;
}
.error-code p {
}
.error-text {
text-align: center;
font-family: Roboto-Regular;
font-size: 14px;
font-weight: 500;
color: #9e9e9e;
}
.circle-btn-add {
background-color: #bababa !important;
border-radius: 50% !important;
height: 30px !important;
width: 30px;
text-align: -webkit-center;
font-size: 18px;
padding: 6px !important;
}
.circle-btn-add:hover {
background-color: #828282 !important;
}
/**
If you need to change the color of active steps in stepper,
uncomment the following and set the background color and font color as needed.
*/
/*
.stepper-active-index {
background-color: #0a6eff !important;
color: white !important;
}
.stepper-passed-index {
background-color: #0a6eff !important;
color: green !important;
}
*/

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
.ant-upload.ant-upload-drag {
height: 170px;
}
.release .release-icon{
margin-right: 15px;
}
.release .release-icon img{
width: 100%;
border-radius: 28%;
}
.release .release-title{
margin-left: 15px;
}
.release .release-screenshot img{
width: 100%;
border-radius: 15px;
padding: 5px;
}
.logo-image {
/*width: 120px;*/
height: 31px;
margin: 0 5px 16px 24px;
float: left;
}
.logo-image img{
height: 35px;
}
.main-container{
background: #f0f2f5;
min-height: 780px
}
.profile{
float:right;
margin-right: 2%;
}
@media only screen and (min-width: 768px) {
.main-container{
padding: 24px;
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import "antd/dist/antd.less";
import RouteWithSubRoutes from "./components/RouteWithSubRoutes";
import {
BrowserRouter as Router,
Redirect, Switch,
} from 'react-router-dom';
import axios from "axios";
import {Layout, Spin, Result} from "antd";
import ConfigContext from "./context/ConfigContext";
const {Content} = Layout;
const loadingView = (
<Layout>
<Content style={{
padding: '0 0',
paddingTop: 300,
backgroundColor: '#fff',
textAlign: 'center'
}}>
<Spin tip="Loading..."/>
</Content>
</Layout>
);
const errorView = (
<Result
style={{
paddingTop: 200
}}
status="500"
title="Error occurred while loading the configuration"
subTitle="Please refresh your browser window"
/>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
error: false,
config: {}
}
}
componentDidMount() {
axios.get(
window.location.origin + "/entgra/public/conf/config.json",
).then(res => {
console.log(res);
this.setState({
loading: false,
config: res.data
})
}).catch((error) => {
this.setState({
loading: false,
error: true
})
});
}
render() {
const {loading, error} = this.state;
const applicationView = (
<Router>
<ConfigContext.Provider value={this.state.config}>
<div>
<Switch>
<Redirect exact from="/entgra" to="/entgra/devices"/>
{this.props.routes.map((route) => (
<RouteWithSubRoutes key={route.path} {...route} />
))}
</Switch>
</div>
</ConfigContext.Provider>
</Router>
);
return (
<div>
{loading && loadingView}
{!loading && !error && applicationView}
{error && errorView}
</div>
);
}
}
export default App;

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

View File

@ -0,0 +1,247 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd";
import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext";
const {Text} = Typography;
let config = null;
const columns = [
{
title: 'Device',
dataIndex: 'name',
width: 100,
},
{
title: 'Type',
dataIndex: 'type',
key: 'type',
render: type => {
const defaultPlatformIcons = config.defaultPlatformIcons;
let icon = defaultPlatformIcons.default.icon;
let color = defaultPlatformIcons.default.color;
let theme = defaultPlatformIcons.default.theme;
if (defaultPlatformIcons.hasOwnProperty(type)) {
icon = defaultPlatformIcons[type].icon;
color = defaultPlatformIcons[type].color;
theme = defaultPlatformIcons[type].theme;
}
return (
<span style={{fontSize: 20, color: color, textAlign: "center"}}>
<Icon type={icon} theme={theme}/>
</span>
);
}
// todo add filtering options
},
{
title: 'Owner',
dataIndex: 'enrolmentInfo',
key: 'owner',
render: enrolmentInfo => enrolmentInfo.owner
// todo add filtering options
},
{
title: 'Ownership',
dataIndex: 'enrolmentInfo',
key: 'ownership',
render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
},
{
title: 'Status',
dataIndex: 'enrolmentInfo',
key: 'status',
render: (enrolmentInfo) => {
const status = enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "removed":
color = "#ff7979";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
}
return <Tag color={color}>{status}</Tag>;
}
// todo add filtering options
},
{
title: 'Last Updated',
dataIndex: 'enrolmentInfo',
key: 'dateOfLastUpdate',
render: (data) => {
const {dateOfLastUpdate} = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return <Tooltip title={new Date(dateOfLastUpdate).toString()}>{timeAgoString}</Tooltip>;
}
// todo add filtering options
},
{
title: 'Action',
key: 'action',
render: () => (
<span>
<a><Icon type="edit" /></a>
<Divider type="vertical" />
<a><Text type="danger"><Icon type="delete" /></Text></a>
</span>
),
},
];
const getTimeAgo = (time) => {
const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time);
};
class DeviceTable extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
TimeAgo.addLocale(en);
this.state = {
data: [],
pagination: {},
loading: false,
selectedRows: []
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
this.setState({
selectedRows: selectedRows
})
}
};
componentDidMount() {
this.fetch();
}
//fetch data from api
fetch = (params = {}) => {
const config = this.props.context;
this.setState({loading: true});
// get current page
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), //calculate the offset
limit: 10,
requireDeviceInfo: true,
};
const encodedExtraParams = Object.keys(extraParams).map(key => key + '=' + extraParams[key]).join('&');
//send request to the invoker
axios.get(
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt +
"/devices?" + encodedExtraParams,
).then(res => {
if (res.status === 200) {
const pagination = {...this.state.pagination};
this.setState({
loading: false,
data: res.data.data.devices,
pagination,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load devices.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
return (
<div>
<Table
columns={columns}
rowKey={record => record.deviceIdentifier}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
scroll={{x: 1000}}
/>
</div>
);
}
}
export default withConfigContext(DeviceTable);

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from 'react';
import {Route} from 'react-router-dom';
class RouteWithSubRoutes extends React.Component{
props;
constructor(props){
super(props);
this.props = props;
}
render() {
return(
<Route path={this.props.path} exact={this.props.exact} render={(props) => (
<this.props.component {...props} {...this.props} routes={this.props.routes}/>
)}/>
);
}
}
export default RouteWithSubRoutes;

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
const ConfigContext = React.createContext();
export const withConfigContext = Component => {
return props => (
<ConfigContext.Consumer>
{context => {
return <Component {...props} context={context}/>;
}}
</ConfigContext.Consumer>
);
};
export default ConfigContext;

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
.App {
padding: 20px;
}
.ant-layout-header{
padding: 0;
height: auto;
box-shadow: 0 2px 8px #f0f1f2;
}
.steps-content {
margin-top: 16px;
border: 1px dashed #e9e9e9;
border-radius: 6px;
background-color: #fafafa;
min-height: 200px;
text-align: center;
padding-top: 80px;
}
.steps-action {
margin-top: 24px;
}
.ant-input-affix-wrapper .ant-input{
min-height: 0;
}

View File

@ -0,0 +1,26 @@
<!--
~ Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
~
~ Entgra (pvt) Ltd. 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.
-->
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/>
<title>Entgra Device Management</title>
</head>
<div id="root"></div>
</html>

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import App from "./App";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard/Dashboard";
import './index.css';
import Devices from "./pages/Dashboard/Devices/Devices";
import Reports from "./pages/Dashboard/Reports/Reports";
import Geo from "./pages/Dashboard/Geo/Geo";
const routes = [
{
path: '/entgra/login',
exact: true,
component: Login
},
{
path: '/entgra',
exact: false,
component: Dashboard,
routes: [
{
path: '/entgra/devices',
component: Devices,
exact: true
},
{
path: '/entgra/geo',
component: Geo,
exact: true
},
{
path: '/entgra/reports',
component: Reports,
exact: true
}
]
}
];
ReactDOM.render(
<App routes={routes}/>,
document.getElementById('root'));
// If you want your app e and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import {Layout, Menu, Icon} from 'antd';
import {Switch, Link} from "react-router-dom";
import RouteWithSubRoutes from "../../components/RouteWithSubRoutes"
import {Redirect} from 'react-router'
import "../../App.css";
import {withConfigContext} from "../../context/ConfigContext";
import Logout from "./Logout/Logout";
const {Header, Content, Footer} = Layout;
const {SubMenu} = Menu;
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
routes: props.routes,
selectedKeys: [],
deviceTypes: []
};
this.logo = this.props.context.theme.logo;
}
render() {
return (
<div>
<Layout className="layout">
<Header style={{paddingLeft: 0, paddingRight: 0}}>
<div className="logo-image">
<img alt="logo" src={this.logo}/>
</div>
<Menu
theme="light"
mode="horizontal"
defaultSelectedKeys={['1']}
style={{lineHeight: '64px'}}
>
<Menu.Item key="devices"><Link to="/entgra/devices"><Icon type="appstore"/>Devices</Link></Menu.Item>
<Menu.Item key="geo"><Link to="/entgra/geo"><Icon type="environment"/>Geo</Link></Menu.Item>
<Menu.Item key="reports"><Link to="/entgra/reports"><Icon type="bar-chart"/>Reports</Link></Menu.Item>
<SubMenu className="profile"
title={
<span className="submenu-title-wrapper">
<Icon type="user"/>
Profile
</span>
}
>
<Logout/>
</SubMenu>
</Menu>
</Header>
</Layout>
<Layout>
<Content style={{marginTop: 2}}>
<Switch>
<Redirect exact from="/entgra" to="/entgra/devices"/>
{this.state.routes.map((route) => (
<RouteWithSubRoutes key={route.path} {...route} />
))}
</Switch>
</Content>
<Footer style={{textAlign: 'center'}}>
©2019 entgra.io
</Footer>
</Layout>
</div>
);
}
}
export default withConfigContext(Dashboard);

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import {
PageHeader,
Typography,
Breadcrumb,
Icon,
Card
} from "antd";
import {Link} from "react-router-dom";
import DeviceTable from "../../../components/Devices/DevicesTable";
const {Paragraph} = Typography;
class Devices extends React.Component {
routes;
constructor(props) {
super(props);
this.routes = props.routes;
}
render() {
return (
<div>
<PageHeader style={{paddingTop: 0}}>
<Breadcrumb style={{paddingBottom: 16}}>
<Breadcrumb.Item>
<Link to="/entgra/devices"><Icon type="home"/> Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Devices</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap">
<h3>Devices</h3>
<Paragraph>Lorem ipsum dolor sit amet, est similique constituto at, quot inermis id mel, an
illud incorrupte nam.</Paragraph>
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}>
<DeviceTable/>
</div>
</div>
</div>
);
}
}
export default Devices;

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import {
PageHeader,
Typography,
Breadcrumb,
Icon,
Card
} from "antd";
import {Link} from "react-router-dom";
import DeviceTable from "../../../components/Devices/DevicesTable";
const {Paragraph} = Typography;
class Geo extends React.Component {
routes;
constructor(props) {
super(props);
this.routes = props.routes;
}
render() {
return (
<div>
<PageHeader style={{paddingTop: 0}}>
<Breadcrumb style={{paddingBottom: 16}}>
<Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Geo</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap">
<h3>Geo</h3>
<Paragraph>Lorem ipsum dolor sit amet, est similique constituto at, quot inermis id mel, an
illud incorrupte nam.</Paragraph>
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
</div>
</div>
);
}
}
export default Geo;

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import {notification, Menu, Icon} from 'antd';
import axios from 'axios';
import {withConfigContext} from "../../../context/ConfigContext";
/*
This class for call the logout api by sending request
*/
class Logout extends React.Component {
constructor(props) {
super(props);
this.state = {
inValid: false,
loading: false
};
}
/*
This function call the logout api when the request is success
*/
handleSubmit = () => {
const thisForm = this;
const config = this.props.context;
thisForm.setState({
inValid: false
});
axios.post(window.location.origin + config.serverConfig.logoutUri
).then(res => {
//if the api call status is correct then user will logout and then it goes to login page
if (res.status === 200) {
window.location = window.location.origin + "/entgra/login";
}
}).catch(function (error) {
if (error.hasOwnProperty("response") && error.response.status === 400) {
thisForm.setState({
inValid: true
});
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to logout.",
});
}
});
};
render() {
return (
<Menu>
<Menu.Item key="1" onClick={this.handleSubmit}><Icon type="logout"/>Logout</Menu.Item>
</Menu>
);
}
}
export default withConfigContext(Logout);

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import {
PageHeader,
Typography,
Breadcrumb,
Icon,
Card
} from "antd";
import {Link} from "react-router-dom";
import DeviceTable from "../../../components/Devices/DevicesTable";
const {Paragraph} = Typography;
class Reports extends React.Component {
routes;
constructor(props) {
super(props);
this.routes = props.routes;
}
render() {
return (
<div>
<PageHeader style={{paddingTop: 0}}>
<Breadcrumb style={{paddingBottom: 16}}>
<Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Reports</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap">
<h3>Reports</h3>
<Paragraph>Lorem ipsum dolor sit amet, est similique constituto at, quot inermis id mel, an
illud incorrupte nam.</Paragraph>
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
</div>
</div>
);
}
}
export default Reports;

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
@-moz-keyframes spin {
0% {
-moz-transform: rotate(0deg) scale(1.0);
}
100% {
-moz-transform: rotate(360deg) scale(0.1);
}
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg) scale(1.0);
}
100% {
-webkit-transform: rotate(360deg) scale(0.1);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg) scale(1.0);
}
100% {
-webkit-transform: rotate(360deg) scale(0.1);
transform: rotate(360deg) scale(0.1);
}
}
.background {
position: absolute;
height: 100%;
width: 100%;
z-index: 0;
background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
background-repeat: no-repeat;
background-position: center 110px;
background-size: 100%;
animation: spin 200s infinite linear;
}
.content {
position: relative;
z-index: 1;
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import {Typography, Row, Col, Form, Icon, Input, Button, Checkbox} from 'antd';
import './Login.css';
import axios from 'axios';
import {withConfigContext} from "../context/ConfigContext";
const {Title} = Typography;
const {Text} = Typography;
class Login extends React.Component {
render() {
const config = this.props.context;
return (
<div>
<div className="background">
</div>
<div className="content">
<Row>
<Col xs={3} sm={3} md={10}>
</Col>
<Col xs={18} sm={18} md={4}>
<Row style={{marginBottom: 20}}>
<Col style={{textAlign: "center"}}>
<img style={
{
marginTop: 36,
height: 60
}
}
src={config.theme.logo}/>
</Col>
</Row>
<Title level={2}>Login</Title>
<WrappedNormalLoginForm/>
</Col>
</Row>
<Row>
<Col span={4} offset={10}>
</Col>
</Row>
</div>
</div>
);
}
}
class NormalLoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
inValid: false,
loading: false
};
}
handleSubmit = (e) => {
const thisForm = this;
const config = this.props.context;
console.log(config);
e.preventDefault();
this.props.form.validateFields((err, values) => {
thisForm.setState({
inValid: false
});
if (!err) {
thisForm.setState({
loading: true
});
const parameters = {
username: values.username,
password: values.password,
platform: "entgra"
};
const request = Object.keys(parameters).map(key => key + '=' + parameters[key]).join('&');
axios.post(window.location.origin+ config.serverConfig.loginUri, request
).then(res => {
if (res.status === 200) {
window.location = window.location.origin+ "/entgra";
}
}).catch(function (error) {
if (error.response.status === 400) {
thisForm.setState({
inValid: true,
loading: false
});
}
});
}
});
};
render() {
const {getFieldDecorator} = this.props.form;
let errorMsg = "";
if (this.state.inValid) {
errorMsg = <Text type="danger">Invalid Login Details</Text>;
}
let loading = "";
if (this.state.loading) {
loading = <Text type="secondary">Loading..</Text>;
}
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
{getFieldDecorator('username', {
rules: [{required: true, message: 'Please input your username!'}],
})(
<Input name="username" style={{height: 32}}
prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
placeholder="Username"/>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{required: true, message: 'Please input your Password!'}],
})(
<Input name="password" style={{height: 32}}
prefix={<Icon type="lock" style={{color: 'rgba(0,0,0,.25)'}}/>} type="password"
placeholder="Password"/>
)}
</Form.Item>
{loading}
{errorMsg}
<Form.Item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(
<Checkbox>Remember me</Checkbox>
)}
<br/>
<a className="login-form-forgot" href="">Forgot password</a>
<Button block type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
</Form.Item>
</Form>
);
}
}
const WrappedNormalLoginForm = withConfigContext(Form.create({name: 'normal_login'})(NormalLoginForm));
export default withConfigContext(Login);

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
.logo {
width: 120px;
height: 31px;
margin: 16px 0 16px 20px;
float: left;
img{
height: 35px;
}
}
input{
min-height: 0;
}

View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2017, 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 path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const configurations = require("./public/conf/config.json");
const config = {
devtool: "source-map",
output: {
publicPath: '/entgra/'
},
watch: false,
resolve: {
alias: {
AppData: path.resolve(__dirname, 'source/src/app/common/'),
AppComponents: path.resolve(__dirname, 'source/src/app/components/')
},
extensions: ['.jsx', '.js', '.ttf', '.woff', '.woff2', '.svg']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader'
}
]
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: { minimize: true }
}
]
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader"
]
},
{
test: /\.scss$/,
use: [ 'style-loader', 'scss-loader' ]
},
{
test: /\.less$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
},
{
loader: "less-loader",
options: {
modifyVars: {
'primary-color': configurations.theme.primaryColor,
'link-color': configurations.theme.primaryColor,
},
javascriptEnabled: true,
},
}
]
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000',
},
{
test: /\.(png|jpe?g)/i,
use: [
{
loader: "url-loader",
options: {
name: "./img/[name].[ext]",
limit: 10000
}
},
{
loader: "img-loader"
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
externals: {
'Config': JSON.stringify(require('./public/conf/config.json'))
}
};
if (process.env.NODE_ENV === "development") {
config.watch = true;
}
module.exports = config;

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>Entgra-Webapp</display-name>
<error-page>
<error-code>404</error-code>
<location>/index.html</location>
</error-page>
</web-app>

View File

@ -901,12 +901,7 @@ public interface DeviceManagementService {
required = true)
@PathParam("device-id")
@Size(max = 45)
String deviceId,
@ApiParam(
name = "permanentDelete",
value = "Boolean flag indicating whether to permanently delete the device.",
required = false)
@QueryParam("permanentDelete") boolean permanentDelete);
String deviceId);
@GET
@Path("/{type}/{id}/features")

View File

@ -15,6 +15,22 @@
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2019, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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.
*/
package org.wso2.carbon.device.mgt.jaxrs.service.api.admin;
@ -76,6 +92,12 @@ import java.util.List;
description = "Update the ownership of the device",
key = "perm:admin:devices:update-enrollment",
permissions = {"/device-mgt/admin/devices/update-enrollment"}
),
@Scope(
name = "Permanently Delete the device specified by device id",
description = "Permanently Delete the device specified by device id",
key = "perm:devices:permanent-delete",
permissions = {"/device-mgt/admin/devices/permanent-delete"}
)
}
)
@ -225,4 +247,72 @@ public interface DeviceManagementAdminService {
value = "List of device identifiers.",
required = true)
List<String> deviceIdentifiers);
@DELETE
@Path("/type/{device-type}/id/{device-id}")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON,
httpMethod = "DELETE",
value = "Permanently remove the Device Specified by the Device ID",
notes = "Returns the status of the permanently deleted device operation and the details of the deleted device.",
tags = "Device Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:permanent-delete")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully deleted the device permanently.",
response = Device.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 has been modified the last time.\n" +
"Used by caches, or in conditional requests."),
}),
@ApiResponse(
code = 304,
message = "Not Modified. 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 = 404,
message = "Not Found. \n No device is found under the provided type and id.",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n " +
"Server error occurred while retrieving information requested device.",
response = ErrorResponse.class)
})
Response deleteDevicePermanently(
@ApiParam(
name = "device-type",
value = "The device type, such as ios, android, or windows.",
required = true)
@PathParam("device-type")
@Size(max = 45)
String deviceType,
@ApiParam(
name = "device-id",
value = "The device identifier of the device.",
required = true)
@PathParam("device-id")
@Size(max = 45)
String deviceId);
}

View File

@ -105,7 +105,6 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Path("/devices")
@Produces(MediaType.APPLICATION_JSON)
@ -326,8 +325,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
@Override
@Path("/type/{device-type}/id/{device-id}")
public Response deleteDevice(@PathParam("device-type") String deviceType,
@PathParam("device-id") String deviceId,
@QueryParam("permanentDelete") boolean permanentDelete) {
@PathParam("device-id") String deviceId) {
DeviceManagementProviderService deviceManagementProviderService =
DeviceMgtAPIUtils.getDeviceManagementService();
try {
@ -336,16 +334,8 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
if (persistedDevice == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
boolean response;
if (permanentDelete) {
response = deviceManagementProviderService.deleteDevice(deviceIdentifier);
} else {
response = deviceManagementProviderService.disenrollDevice(deviceIdentifier);
}
boolean response = deviceManagementProviderService.disenrollDevice(deviceIdentifier);
return Response.status(Response.Status.OK).entity(response).build();
} catch (DeviceManagementException e) {
String msg = "Error encountered while deleting device of type : " + deviceType + " and " +
"ID : " + deviceId;

View File

@ -15,6 +15,22 @@
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2019, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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.
*/
package org.wso2.carbon.device.mgt.jaxrs.service.impl.admin;
@ -24,10 +40,12 @@ import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.DeviceManagementAdminService;
@ -96,25 +114,53 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
@QueryParam("owner") String owner,
List<String> deviceIdentifiers){
try {
if (DeviceMgtAPIUtils.getDeviceManagementService().updateEnrollment(owner, deviceIdentifiers)){
if (DeviceMgtAPIUtils.getDeviceManagementService().updateEnrollment(owner, deviceIdentifiers)) {
String msg = "Device owner is updated successfully.";
return Response.status(Response.Status.OK).entity(msg).build();
}
String msg = "Device owner updating is failed.";
log.error(msg);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} catch(InvalidDeviceException e){
} catch (InvalidDeviceException e) {
String msg = "Invalid device identifiers are found with the request.";
log.error(msg);
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
}catch (DeviceManagementException e) {
} catch (DeviceManagementException e) {
String msg = "Error occurred when updating device owners.";
log.error(msg);
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} catch (UserNotFoundException e) {
String msg = "Couldn't found the owner in user store to update the owner of devices.";
log.error(msg);
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
}
}
@DELETE
@Override
@Path("/type/{device-type}/id/{device-id}")
public Response deleteDevicePermanently(@PathParam("device-type") String deviceType,
@PathParam("device-id") String deviceId) {
DeviceManagementProviderService deviceManagementProviderService =
DeviceMgtAPIUtils.getDeviceManagementService();
try {
DeviceIdentifier deviceIdentifier = new DeviceIdentifier(deviceId, deviceType);
Device persistedDevice = deviceManagementProviderService.getDevice(deviceIdentifier, true);
if (persistedDevice == null) {
String msg = "No device found with the device type: " + deviceType +
" having the device ID: " + deviceId + " to permanently delete.";
log.error(msg);
return Response.status(Response.Status.NOT_FOUND).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
boolean response = deviceManagementProviderService.deleteDevice(deviceIdentifier);
return Response.status(Response.Status.OK).entity(response).build();
} catch (DeviceManagementException e) {
String msg = "Error encountered while permanently deleting device of type : " + deviceType + " and " +
"ID : " + deviceId;
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
}
}

View File

@ -140,7 +140,8 @@ public class DeviceTypeManagementAdminServiceImpl implements DeviceTypeManagemen
@Override
@PUT
public Response updateDeviceType(String type, DeviceType deviceType) {
@Path("/{type}")
public Response updateDeviceType(@PathParam("type") String type, DeviceType deviceType) {
if (deviceType != null && deviceType.getDeviceTypeMetaDefinition() != null) {
if (deviceType.getName() == null || !deviceType.getName().equals(type)) {
return Response.status(Response.Status.BAD_REQUEST).entity("Type name mismatch. Expected: '" + type +
@ -166,7 +167,10 @@ public class DeviceTypeManagementAdminServiceImpl implements DeviceTypeManagemen
}
@Override
public Response addDeviceTypePlatformConfig(String type, PlatformConfiguration platformConfiguration) {
@POST
@Path("/{type}/configs")
public Response addDeviceTypePlatformConfig(@PathParam("type") String type,
PlatformConfiguration platformConfiguration) {
boolean isSaved;
if (platformConfiguration.getType() == null || !platformConfiguration.getType().equals(type)) {
return Response.status(Response.Status.BAD_REQUEST).entity("Type name mismatch. Expected: '" + type +

View File

@ -481,8 +481,7 @@ public class DeviceManagementServiceImplTest {
public void testDeleteDevice() {
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getDeviceManagementService"))
.toReturn(this.deviceManagementProviderService);
Response response = this.deviceManagementService.deleteDevice(TEST_DEVICE_TYPE, UUID.randomUUID().toString()
, false);
Response response = this.deviceManagementService.deleteDevice(TEST_DEVICE_TYPE, UUID.randomUUID().toString());
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
}
@ -492,8 +491,7 @@ public class DeviceManagementServiceImplTest {
.toReturn(this.deviceManagementProviderService);
Mockito.when(this.deviceManagementProviderService
.getDevice(Mockito.any(DeviceIdentifier.class), Mockito.anyBoolean())).thenReturn(null);
Response response = this.deviceManagementService.deleteDevice(TEST_DEVICE_TYPE, UUID.randomUUID().toString()
, false);
Response response = this.deviceManagementService.deleteDevice(TEST_DEVICE_TYPE, UUID.randomUUID().toString());
Assert.assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
Mockito.reset(this.deviceManagementProviderService);
}
@ -504,8 +502,7 @@ public class DeviceManagementServiceImplTest {
.toReturn(this.deviceManagementProviderService);
Mockito.when(this.deviceManagementProviderService.disenrollDevice(Mockito.any(DeviceIdentifier.class)))
.thenThrow(new DeviceManagementException());
Response response = this.deviceManagementService.deleteDevice(TEST_DEVICE_TYPE, UUID.randomUUID().toString()
, false);
Response response = this.deviceManagementService.deleteDevice(TEST_DEVICE_TYPE, UUID.randomUUID().toString());
Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
Mockito.reset(this.deviceManagementProviderService);
}

View File

@ -277,6 +277,17 @@ public interface DeviceDAO {
*/
List<Device> getDevices(PaginationRequest request, int tenantId) throws DeviceManagementDAOException;
/**
* This method is used to search for devices within a specific group.
*
* @param request PaginationRequest object holding the data for pagination
* @param tenantId tenant id.
* @return returns paginated list of devices.
* @throws DeviceManagementDAOException
*/
List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId) throws DeviceManagementDAOException;
/**
* This method is used to retrieve all the devices of a given tenant and device type.
*
@ -498,7 +509,8 @@ public interface DeviceDAO {
* @throws DeviceManagementDAOException throws {@link DeviceManagementDAOException} if connections establishment
* fails.
*/
List<Device> getDevicesByIdentifiers(List<String> deviceIdentifiers, int tenantId) throws DeviceManagementDAOException;
List<Device> getDevicesByIdentifiers(List<String> deviceIdentifiers, int tenantId)
throws DeviceManagementDAOException;
/**
* This method is used to permanently delete the device and its related details
@ -508,4 +520,3 @@ public interface DeviceDAO {
*/
void deleteDevice(DeviceIdentifier deviceIdentifier, int tenantId) throws DeviceManagementDAOException;
}

View File

@ -1507,8 +1507,8 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
}
return devices;
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while obtaining the DB connection when adding tags",
e);
throw new DeviceManagementDAOException("Error occurred while obtaining the DB connection to get devices for"
+ " given device identifiers.", e);
}
}

View File

@ -369,7 +369,11 @@ public class EnrollmentDAOImpl implements EnrollmentDAO {
try {
Connection conn = this.getConnection();
boolean updateStatus = true;
String sql = "UPDATE DM_ENROLMENT SET OWNER = ? WHERE ID = ? AND TENANT_ID = ?";
String sql = "UPDATE "
+ "DM_ENROLMENT "
+ "SET OWNER = ? "
+ "WHERE ID = ? AND "
+ "TENANT_ID = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
if (conn.getMetaData().supportsBatchUpdates()) {
for (Device device : devices) {
@ -381,6 +385,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO {
for (int i : ps.executeBatch()) {
if (i == 0 || i == Statement.SUCCESS_NO_INFO || i == Statement.EXECUTE_FAILED) {
updateStatus = false;
break;
}
}
} else {
@ -390,14 +395,15 @@ public class EnrollmentDAOImpl implements EnrollmentDAO {
ps.setInt(3, tenantId);
if (ps.executeUpdate() == 0) {
updateStatus = false;
break;
}
}
}
}
return updateStatus;
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while obtaining the DB connection when adding tags",
e);
throw new DeviceManagementDAOException("Error occurred while obtaining the DB connection to update the "
+ "owner of the device enrollment.", e);
}
}

View File

@ -153,6 +153,137 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
return devices;
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
List<Device> devices = null;
int groupId = request.getGroupId();
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
conn = this.getConnection();
String sql = "SELECT d1.DEVICE_ID, d1.DESCRIPTION, d1.NAME AS DEVICE_NAME, d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, e.OWNER, e.OWNERSHIP, e.STATUS, e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, e.ID AS ENROLMENT_ID FROM DM_ENROLMENT e, " +
"(SELECT gd.DEVICE_ID, gd.DESCRIPTION, gd.NAME, gd.DEVICE_IDENTIFICATION, t.NAME AS DEVICE_TYPE " +
"FROM (SELECT d.ID AS DEVICE_ID, d.DESCRIPTION, d.NAME, d.DEVICE_IDENTIFICATION, d.DEVICE_TYPE_ID " +
"FROM DM_DEVICE d, (SELECT dgm.DEVICE_ID FROM DM_DEVICE_GROUP_MAP dgm WHERE dgm.GROUP_ID = ?) dgm1 WHERE" +
" d.ID = dgm1.DEVICE_ID AND d.TENANT_ID = ?";
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") gd, DM_DEVICE_TYPE t";
if (since != null) {
sql = sql + ", DM_DEVICE_DETAIL dt";
isSinceProvided = true;
}
sql = sql + " WHERE gd.DEVICE_TYPE_ID = t.ID";
//Add query for last updated timestamp
if (isSinceProvided) {
sql = sql + " AND dt.DEVICE_ID = gd.DEVICE_ID AND dt.UPDATE_TIMESTAMP > ?";
}
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
sql = sql + " ) d1 WHERE d1.DEVICE_ID = e.DEVICE_ID AND TENANT_ID = ? ";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
//Add the query for status
if (status != null && !status.isEmpty()) {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
sql = sql + " LIMIT ?,?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
int paramIdx = 3;
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
if (isSinceProvided) {
stmt.setLong(paramIdx++, since.getTime());
}
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
rs = stmt.executeQuery();
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while retrieving information of" +
" devices belonging to group : " + groupId, e);
} finally {
DeviceManagementDAOUtil.cleanupResources(stmt, rs);
}
return devices;
}
@Override
public List<Device> getDevicesOfUser(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

View File

@ -159,6 +159,137 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
return devices;
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
List<Device> devices = null;
int groupId = request.getGroupId();
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
conn = this.getConnection();
String sql = "SELECT d1.DEVICE_ID, d1.DESCRIPTION, d1.NAME AS DEVICE_NAME, d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, e.OWNER, e.OWNERSHIP, e.STATUS, e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, e.ID AS ENROLMENT_ID FROM DM_ENROLMENT e, " +
"(SELECT gd.DEVICE_ID, gd.DESCRIPTION, gd.NAME, gd.DEVICE_IDENTIFICATION, t.NAME AS DEVICE_TYPE " +
"FROM (SELECT d.ID AS DEVICE_ID, d.DESCRIPTION, d.NAME, d.DEVICE_IDENTIFICATION, d.DEVICE_TYPE_ID " +
"FROM DM_DEVICE d, (SELECT dgm.DEVICE_ID FROM DM_DEVICE_GROUP_MAP dgm WHERE dgm.GROUP_ID = ?) dgm1 WHERE" +
" d.ID = dgm1.DEVICE_ID AND d.TENANT_ID = ?";
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") gd, DM_DEVICE_TYPE t";
if (since != null) {
sql = sql + ", DM_DEVICE_DETAIL dt";
isSinceProvided = true;
}
sql = sql + " WHERE gd.DEVICE_TYPE_ID = t.ID";
//Add query for last updated timestamp
if (isSinceProvided) {
sql = sql + " AND dt.DEVICE_ID = gd.DEVICE_ID AND dt.UPDATE_TIMESTAMP > ?";
}
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
sql = sql + " ) d1 WHERE d1.DEVICE_ID = e.DEVICE_ID AND TENANT_ID = ? ";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
//Add the query for status
if (status != null && !status.isEmpty()) {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
int paramIdx = 3;
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
if (isSinceProvided) {
stmt.setLong(paramIdx++, since.getTime());
}
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
rs = stmt.executeQuery();
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while retrieving information of" +
" devices belonging to group : " + groupId, e);
} finally {
DeviceManagementDAOUtil.cleanupResources(stmt, rs);
}
return devices;
}
@Override
public List<Device> getDevicesOfUser(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

View File

@ -140,6 +140,136 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
return devices;
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
List<Device> devices = null;
int groupId = request.getGroupId();
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
conn = this.getConnection();
String sql = "SELECT d1.DEVICE_ID, d1.DESCRIPTION, d1.NAME AS DEVICE_NAME, d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, e.OWNER, e.OWNERSHIP, e.STATUS, e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, e.ID AS ENROLMENT_ID FROM DM_ENROLMENT e, " +
"(SELECT gd.DEVICE_ID, gd.DESCRIPTION, gd.NAME, gd.DEVICE_IDENTIFICATION, t.NAME AS DEVICE_TYPE " +
"FROM (SELECT d.ID AS DEVICE_ID, d.DESCRIPTION, d.NAME, d.DEVICE_IDENTIFICATION, d.DEVICE_TYPE_ID " +
"FROM DM_DEVICE d, (SELECT dgm.DEVICE_ID FROM DM_DEVICE_GROUP_MAP dgm WHERE dgm.GROUP_ID = ?) dgm1 WHERE" +
" d.ID = dgm1.DEVICE_ID AND d.TENANT_ID = ?";
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") gd, DM_DEVICE_TYPE t";
if (since != null) {
sql = sql + ", DM_DEVICE_DETAIL dt";
isSinceProvided = true;
}
sql = sql + " WHERE gd.DEVICE_TYPE_ID = t.ID";
//Add query for last updated timestamp
if (isSinceProvided) {
sql = sql + " AND dt.DEVICE_ID = gd.DEVICE_ID AND dt.UPDATE_TIMESTAMP > ?";
}
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
sql = sql + " ) d1 WHERE d1.DEVICE_ID = e.DEVICE_ID AND TENANT_ID = ? ";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
//Add the query for status
if (status != null && !status.isEmpty()) {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
sql = sql + " LIMIT ? OFFSET ?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
int paramIdx = 3;
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
if (isSinceProvided) {
stmt.setLong(paramIdx++, since.getTime());
}
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
stmt.setInt(paramIdx, request.getRowCount());
stmt.setInt(paramIdx++, request.getStartIndex());
rs = stmt.executeQuery();
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while retrieving information of" +
" devices belonging to group : " + groupId, e);
} finally {
DeviceManagementDAOUtil.cleanupResources(stmt, rs);
}
return devices;
}
@Override
public List<Device> getDevicesOfUser(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

View File

@ -156,6 +156,136 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
return devices;
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
List<Device> devices = null;
int groupId = request.getGroupId();
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
conn = this.getConnection();
String sql = "SELECT d1.DEVICE_ID, d1.DESCRIPTION, d1.NAME AS DEVICE_NAME, d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, e.OWNER, e.OWNERSHIP, e.STATUS, e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, e.ID AS ENROLMENT_ID FROM DM_ENROLMENT e, " +
"(SELECT gd.DEVICE_ID, gd.DESCRIPTION, gd.NAME, gd.DEVICE_IDENTIFICATION, t.NAME AS DEVICE_TYPE " +
"FROM (SELECT d.ID AS DEVICE_ID, d.DESCRIPTION, d.NAME, d.DEVICE_IDENTIFICATION, d.DEVICE_TYPE_ID " +
"FROM DM_DEVICE d, (SELECT dgm.DEVICE_ID FROM DM_DEVICE_GROUP_MAP dgm WHERE dgm.GROUP_ID = ?) dgm1 WHERE" +
" d.ID = dgm1.DEVICE_ID AND d.TENANT_ID = ?";
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") gd, DM_DEVICE_TYPE t";
if (since != null) {
sql = sql + ", DM_DEVICE_DETAIL dt";
isSinceProvided = true;
}
sql = sql + " WHERE gd.DEVICE_TYPE_ID = t.ID";
//Add query for last updated timestamp
if (isSinceProvided) {
sql = sql + " AND dt.DEVICE_ID = gd.DEVICE_ID AND dt.UPDATE_TIMESTAMP > ?";
}
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
sql = sql + " ) d1 WHERE d1.DEVICE_ID = e.DEVICE_ID AND TENANT_ID = ? ";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
//Add the query for status
if (status != null && !status.isEmpty()) {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
int paramIdx = 3;
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
if (isSinceProvided) {
stmt.setLong(paramIdx++, since.getTime());
}
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
rs = stmt.executeQuery();
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while retrieving information of" +
" devices belonging to group : " + groupId, e);
} finally {
DeviceManagementDAOUtil.cleanupResources(stmt, rs);
}
return devices;
}
@Override
public List<Device> getDevicesOfUser(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

View File

@ -827,7 +827,11 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
} else {
try {
DeviceManagementDAOFactory.openConnection();
if(request.getGroupId()!=0){
allDevices = deviceDAO.searchDevicesInGroup(request, tenantId);
} else{
allDevices = deviceDAO.getDevices(request, tenantId);
}
count = deviceDAO.getDeviceCount(request, tenantId);
} catch (DeviceManagementDAOException e) {
String msg = "Error occurred while retrieving device list pertaining to the current tenant";
@ -3090,12 +3094,12 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
DeviceManagementDAOFactory.rollbackTransaction();
return false;
} catch (TransactionManagementException e) {
String msg = "Error occurred while initiating transaction";
String msg = "Error occurred while initiating the transaction.";
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} catch (DeviceManagementDAOException e) {
String msg = "Error occurred either verifying existence of device ids or updating owner of the device.";
log.error(msg);
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} finally {
DeviceManagementDAOFactory.closeConnection();
@ -3118,7 +3122,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
return owner;
} catch (UserStoreException e) {
String msg = "Error occurred when checking whether owner is exist or not. Owner: " + owner;
log.error(msg);
log.error(msg, e);
throw new DeviceManagementException(msg, e);
}
}

View File

@ -34,6 +34,7 @@
*/
package org.wso2.carbon.device.mgt.extensions.device.type.template;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
@ -58,10 +59,12 @@ import org.wso2.carbon.device.mgt.extensions.device.type.template.dao.DeviceDAOD
import org.wso2.carbon.device.mgt.extensions.device.type.template.dao.DeviceTypePluginDAOManager;
import org.wso2.carbon.device.mgt.extensions.device.type.template.exception.DeviceTypeDeployerPayloadException;
import org.wso2.carbon.device.mgt.extensions.device.type.template.exception.DeviceTypeMgtPluginException;
import org.wso2.carbon.device.mgt.extensions.device.type.template.exception.DeviceTypePluginExtensionException;
import org.wso2.carbon.device.mgt.extensions.device.type.template.feature.ConfigurationBasedFeatureManager;
import org.wso2.carbon.device.mgt.extensions.device.type.template.util.DeviceTypePluginConstants;
import org.wso2.carbon.device.mgt.extensions.device.type.template.util.DeviceTypeUtils;
import org.wso2.carbon.device.mgt.extensions.license.mgt.registry.RegistryBasedLicenseManager;
import org.wso2.carbon.device.mgt.extensions.spi.DeviceTypePluginExtensionService;
import org.wso2.carbon.registry.api.RegistryException;
import org.wso2.carbon.registry.api.Resource;
import org.wso2.carbon.utils.CarbonUtils;
@ -213,6 +216,35 @@ public class DeviceTypeManager implements DeviceManager {
}
}
}
setDeviceTypePluginManager();
}
/**
* Set device type plugin DAO manager of each device type in a HashMap which can then be used via individual
* device type plugin in working with its DAO components
*/
private void setDeviceTypePluginManager() {
if (StringUtils.isNotEmpty(deviceType)) {
if (deviceTypePluginDAOManager != null) {
DeviceTypePluginExtensionService deviceTypeManagerExtensionService =
new DeviceTypePluginExtensionServiceImpl();
try {
deviceTypeManagerExtensionService.addPluginDAOManager(deviceType, deviceTypePluginDAOManager);
} catch (DeviceTypePluginExtensionException e) {
String msg = "Error occurred while saving DeviceTypePluginDAOManager for device type: "
+ deviceType;
log.error(msg);
throw new DeviceTypeDeployerPayloadException(msg);
}
} else {
log.warn("Could not save DeviceTypePluginDAOManager for device type: " + deviceType +
" since DeviceTypePluginDAOManager is null.");
}
} else {
String msg = "Could not save DeviceTypePluginDAOManager since device type is null or empty.";
log.error(msg);
throw new DeviceTypeDeployerPayloadException(msg);
}
}
@Override
@ -307,15 +339,11 @@ public class DeviceTypeManager implements DeviceManager {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().commitTransaction();
}
} catch (DeviceTypeMgtPluginException e) {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().rollbackTransaction();
} catch (DeviceTypeMgtPluginException ex) {
String msg = "Error occurred while roll back the device enrol transaction :" +
device.toString();
log.warn(msg, ex);
}
String msg = "Error while enrolling the " + deviceType + " device : " + device.getDeviceIdentifier();
throw new DeviceManagementException(msg, e);
} finally {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
}
return status;
}
@ -334,16 +362,12 @@ public class DeviceTypeManager implements DeviceManager {
status = deviceTypePluginDAOManager.getDeviceDAO().updateDevice(device);
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().commitTransaction();
} catch (DeviceTypeMgtPluginException e) {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().rollbackTransaction();
} catch (DeviceTypeMgtPluginException mobileDAOEx) {
String msg = "Error occurred while roll back the update device transaction :" +
device.toString();
log.warn(msg, mobileDAOEx);
}
String msg = "Error while updating the enrollment of the " + deviceType + " device : " +
device.getDeviceIdentifier();
throw new DeviceManagementException(msg, e);
} finally {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
}
return status;
}
@ -378,13 +402,7 @@ public class DeviceTypeManager implements DeviceManager {
deviceId.getId();
throw new DeviceManagementException(msg, e);
} finally {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
} catch (DeviceTypeMgtPluginException e) {
String msg = "Error occurred while closing the transaction to check device " +
deviceId.getId() + " is enrolled.";
log.warn(msg, e);
}
}
return isEnrolled;
}
@ -419,12 +437,7 @@ public class DeviceTypeManager implements DeviceManager {
throw new DeviceManagementException(
"Error occurred while fetching the " + deviceType + " device: '" + deviceId.getId() + "'", e);
} finally {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
} catch (DeviceTypeMgtPluginException e) {
String msg = "Error occurred while closing the transaction to get device " + deviceId.getId();
log.warn(msg, e);
}
}
return device;
}
@ -447,14 +460,11 @@ public class DeviceTypeManager implements DeviceManager {
status = deviceTypePluginDAOManager.getDeviceDAO().updateDevice(updatedDevice);
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().commitTransaction();
} catch (DeviceTypeMgtPluginException e) {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().rollbackTransaction();
} catch (DeviceTypeMgtPluginException transactionException) {
String msg = "Error occurred while rolling back transaction for device: " + deviceId.getId();
log.warn(msg, transactionException);
}
throw new DeviceManagementException(
"Error occurred while fetching the " + deviceType + " device: '" + deviceId.getId() + "'", e);
} finally {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
}
}
return status;
@ -544,15 +554,12 @@ public class DeviceTypeManager implements DeviceManager {
status = deviceTypePluginDAOManager.getDeviceDAO().updateDevice(existingDevice);
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().commitTransaction();
} catch (DeviceTypeMgtPluginException e) {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().rollbackTransaction();
} catch (DeviceTypeMgtPluginException e1) {
log.warn("Error occurred while roll back the update device info transaction : '" +
device.toString() + "'", e1);
}
throw new DeviceManagementException(
"Error occurred while updating the " + deviceType + " device: '" +
device.getDeviceIdentifier() + "'", e);
} finally {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
}
return status;
}
@ -572,12 +579,7 @@ public class DeviceTypeManager implements DeviceManager {
} catch (DeviceTypeMgtPluginException e) {
throw new DeviceManagementException("Error occurred while fetching all " + deviceType + " devices", e);
} finally {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
} catch (DeviceTypeMgtPluginException e) {
String msg = "Error occurred while closing the transaction to get all devices.";
log.warn(msg, e);
}
}
return devices;
}
@ -600,15 +602,12 @@ public class DeviceTypeManager implements DeviceManager {
status = deviceTypePluginDAOManager.getDeviceDAO().deleteDevice(existingDevice);
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().commitTransaction();
} catch (DeviceTypeMgtPluginException e) {
try {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().rollbackTransaction();
} catch (DeviceTypeMgtPluginException e1) {
log.warn("Error occurred while roll back the delete device info transaction : '" +
device.toString() + "'", e1);
}
throw new DeviceManagementException(
"Error occurred while deleting the " + deviceType + " device: '" +
device.getDeviceIdentifier() + "'", e);
} finally {
deviceTypePluginDAOManager.getDeviceTypeDAOHandler().closeConnection();
}
return status;
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2019, Entgra (Pvt) Ltd. (https://entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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.
*/
package org.wso2.carbon.device.mgt.extensions.device.type.template;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.extensions.device.type.template.dao.DeviceTypePluginDAOManager;
import org.wso2.carbon.device.mgt.extensions.device.type.template.exception.DeviceTypePluginExtensionException;
import org.wso2.carbon.device.mgt.extensions.spi.DeviceTypePluginExtensionService;
import java.util.HashMap;
import java.util.Map;
public class DeviceTypePluginExtensionServiceImpl implements DeviceTypePluginExtensionService {
private static final Log log = LogFactory.getLog(DeviceTypePluginExtensionServiceImpl.class);
private static volatile Map<String, DeviceTypePluginDAOManager> pluginDAOManagers = new HashMap<>();
@Override
public void addPluginDAOManager(String deviceType, DeviceTypePluginDAOManager pluginDAOManager)
throws DeviceTypePluginExtensionException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
if (pluginDAOManager == null) {
String msg = "Cannot save DeviceTypePluginDAOManager against tenant id " + tenantId
+ " and device type: " + deviceType + " since DeviceTypePluginDAOManager is null";
log.error(msg);
throw new DeviceTypePluginExtensionException(msg);
}
if (!pluginDAOManagers.containsKey(tenantId + deviceType)) {
if (log.isDebugEnabled()) {
log.debug("Saving DeviceTypePluginDAOManager against tenant id " + tenantId +
" and device type: " + deviceType);
}
pluginDAOManagers.put(tenantId + deviceType, pluginDAOManager);
}
}
@Override
public DeviceTypePluginDAOManager getPluginDAOManager(String deviceType) throws DeviceTypePluginExtensionException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
if (pluginDAOManagers.containsKey(tenantId + deviceType)) {
if (log.isDebugEnabled()) {
log.debug("Retrieving DeviceTypePluginDAOManager against tenant id " + tenantId +
" and device type: " + deviceType);
}
return pluginDAOManagers.get(tenantId + deviceType);
} else {
String msg = "DeviceTypePluginDAOManager could not be found against tenant id " + tenantId +
" and device type: " + deviceType;
log.error(msg);
throw new DeviceTypePluginExtensionException(msg);
}
}
}

View File

@ -1,7 +1,25 @@
/*
* Copyright (c) 2019, Entgra (Pvt) Ltd. (https://entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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.
*/
package org.wso2.carbon.device.mgt.extensions.device.type.template.dao;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException;
import org.wso2.carbon.device.mgt.extensions.device.type.template.exception.DeviceTypeDeployerPayloadException;
import org.wso2.carbon.device.mgt.extensions.device.type.template.exception.DeviceTypeMgtPluginException;
@ -31,7 +49,26 @@ public class DeviceTypeDAOHandler {
Context ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup(datasourceName);
} catch (NamingException e) {
throw new DeviceTypeDeployerPayloadException("Error while looking up the data source: " + datasourceName, e);
String msg = "Error while looking up the data source: " + datasourceName;
log.error(msg, e);
throw new DeviceTypeDeployerPayloadException(msg, e);
}
}
public void openConnection() throws DeviceTypeMgtPluginException {
try {
Connection conn = currentConnection.get();
if (conn != null) {
String msg = "Database connection has already been obtained.";
log.error(msg);
throw new IllegalTransactionStateException(msg);
}
conn = dataSource.getConnection();
currentConnection.set(conn);
} catch (SQLException e) {
String msg = "Failed to get a database connection.";
log.error(msg, e);
throw new DeviceTypeMgtPluginException(msg, e);
}
}
@ -41,7 +78,9 @@ public class DeviceTypeDAOHandler {
conn.setAutoCommit(false);
currentConnection.set(conn);
} catch (SQLException e) {
throw new DeviceTypeMgtPluginException("Error occurred while retrieving datasource connection", e);
String msg = "Error occurred while retrieving datasource connection";
log.error(msg, e);
throw new DeviceTypeMgtPluginException(msg, e);
}
}
@ -50,58 +89,58 @@ public class DeviceTypeDAOHandler {
try {
currentConnection.set(dataSource.getConnection());
} catch (SQLException e) {
throw new DeviceTypeMgtPluginException("Error occurred while retrieving data source connection", e);
String msg = "Error occurred while retrieving data source connection";
log.error(msg, e);
throw new DeviceTypeMgtPluginException(msg, e);
}
}
return currentConnection.get();
}
public void commitTransaction() throws DeviceTypeMgtPluginException {
try {
public void commitTransaction() {
Connection conn = currentConnection.get();
if (conn != null) {
if (conn == null) {
String msg = "No connection is associated with the current transaction. This might have ideally been " +
"caused by not properly initiating the transaction via " +
"'beginTransaction'/'openConnection' methods";
log.error(msg);
throw new IllegalStateException(msg);
}
try {
conn.commit();
} else {
if (log.isDebugEnabled()) {
log.debug("Datasource connection associated with the current thread is null, hence commit "
+ "has not been attempted");
}
}
} catch (SQLException e) {
throw new DeviceTypeMgtPluginException("Error occurred while committing the transaction", e);
} finally {
closeConnection();
String msg = "Error occurred while committing the transaction.";
log.error(msg, e);
}
}
public void closeConnection() throws DeviceTypeMgtPluginException {
public void closeConnection() {
Connection con = currentConnection.get();
if (con != null) {
try {
con.close();
} catch (SQLException e) {
log.error("Error occurred while close the connection");
String msg = "Error occurred while close the connection";
log.error(msg, e);
}
}
currentConnection.remove();
}
public void rollbackTransaction() throws DeviceTypeMgtPluginException {
try {
public void rollbackTransaction() {
Connection conn = currentConnection.get();
if (conn != null) {
if (conn == null) {
String msg = "No connection is associated with the current transaction. This might have ideally been " +
"caused by not properly initiating the transaction via " +
"'beginTransaction'/'openConnection' methods";
log.error(msg);
throw new IllegalStateException(msg);
}
try {
conn.rollback();
} else {
if (log.isDebugEnabled()) {
log.debug("Datasource connection associated with the current thread is null, hence rollback "
+ "has not been attempted");
}
}
} catch (SQLException e) {
throw new DeviceTypeMgtPluginException("Error occurred while rollback the transaction", e);
} finally {
closeConnection();
String msg = "Error occurred while roll-backing the transaction.";
log.error(msg, e);
}
}
}

View File

@ -0,0 +1,12 @@
package org.wso2.carbon.device.mgt.extensions.device.type.template.exception;
public class DeviceTypePluginExtensionException extends Exception {
public DeviceTypePluginExtensionException(String msg) {
super(msg);
}
public DeviceTypePluginExtensionException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2019, Entgra (Pvt) Ltd. (https://entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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.
*/
package org.wso2.carbon.device.mgt.extensions.internal;
@ -23,6 +40,8 @@ import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.device.mgt.common.spi.DeviceTypeGeneratorService;
import org.wso2.carbon.device.mgt.extensions.device.type.template.DeviceTypeGeneratorServiceImpl;
import org.wso2.carbon.device.mgt.extensions.device.type.template.DeviceTypePluginExtensionServiceImpl;
import org.wso2.carbon.device.mgt.extensions.spi.DeviceTypePluginExtensionService;
import org.wso2.carbon.ndatasource.core.DataSourceService;
import org.wso2.carbon.registry.core.service.RegistryService;
@ -50,6 +69,8 @@ public class DeviceTypeExtensionServiceComponent {
}
ctx.getBundleContext()
.registerService(DeviceTypeGeneratorService.class, new DeviceTypeGeneratorServiceImpl(), null);
ctx.getBundleContext().registerService(DeviceTypePluginExtensionService.class,
new DeviceTypePluginExtensionServiceImpl(), null);
if (log.isDebugEnabled()) {
log.debug("Device Type Extension Service Component successfully activated");
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019, Entgra (Pvt) Ltd. (https://entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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.
*/
package org.wso2.carbon.device.mgt.extensions.spi;
import org.wso2.carbon.device.mgt.extensions.device.type.template.dao.DeviceTypePluginDAOManager;
import org.wso2.carbon.device.mgt.extensions.device.type.template.exception.DeviceTypePluginExtensionException;
/**
* This represents the device type plugin extension service which can be used by any device type plugin implementation
* intended to use the same plugin DAO instances to be used with its plugin level DAO components
*/
public interface DeviceTypePluginExtensionService {
/**
* Save device type specific DeviceTypePluginDAOManager in a HashMap againast tenant ID and device type
* @param deviceType - Type of the device (i.e; android, ios, windows)
* @param pluginDAOManager - Device type plugin DAO manager instance to be saved against device type
* @throws DeviceTypePluginExtensionException when pluginDAOManager is null
*/
void addPluginDAOManager(String deviceType, DeviceTypePluginDAOManager pluginDAOManager)
throws DeviceTypePluginExtensionException;
/**
* Retrieve the DeviceTypePluginDAOManager instance against tenant ID and given device type
* @param deviceType - Type of the device (i.e; android, ios, windows)
* @return an Instance of {@link DeviceTypePluginDAOManager}
* @throws DeviceTypePluginExtensionException when pluginDAOManager cannot be found
*/
DeviceTypePluginDAOManager getPluginDAOManager(String deviceType) throws DeviceTypePluginExtensionException;
}

View File

@ -327,15 +327,6 @@ deviceModule = function () {
return response;
};
publicMethods.getDeviceTypesConfig = function () {
var url = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"] + "/device-types/config";
var response = privateMethods.callBackend(url, constants["HTTP_GET"]);
if (response.status == "success") {
response.content = parse(response.content);
}
return response;
};
/*
@Updated
*/

View File

@ -506,7 +506,7 @@ function loadDevices(searchType, searchParam) {
$('#device-grid').datatables_extended_serverside_paging(
null,
serviceURL,
"/api/device-mgt/v1.0/devices/",
dataFilter,
columns,
fnCreatedRow,
@ -525,7 +525,8 @@ function loadDevices(searchType, searchParam) {
}, {
"placeholder": "Top-Device-Name-Search",
"searchKey": "namePattern"
"searchKey": "namePattern",
"groupId": groupId
}
);

View File

@ -35,7 +35,7 @@ function onRequest(context) {
var deviceType = request.getParameter("type");
var serviceInvokers = require("/app/modules/oauth/token-protected-service-invokers.js")["invokers"];
var restAPIEndpoint = deviceMgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"]
+ "/device-types/config/" + deviceType;
+ "/device-types/" + deviceType;
displayData.name = deviceType;
serviceInvokers.XMLHttp.get(
restAPIEndpoint,

View File

@ -210,7 +210,7 @@ $(document).ready(function () {
});
deviceType.deviceTypeMetaDefinition.features = features;
var addRoleAPI = apiBasePath + "/admin/device-types";
var addRoleAPI = apiBasePath + "/admin/device-types/" + deviceType.name;
invokerUtil.put(
addRoleAPI,

View File

@ -76,6 +76,9 @@ $.fn.datatables_extended_serverside_paging = function (settings, url, dataFilter
searchParams[params.columns[i].data] = encodeURIComponent(params.columns[i].search.value);
}
if (options) {
if (options.groupId){
searchParams["groupId"] = options.groupId;
}
searchParams[options.searchKey] = encodeURIComponent(params.search.value);
}
params.filter = JSON.stringify(searchParams);

View File

@ -42,7 +42,7 @@ function onRequest(context) {
var displayData = {};
var restAPIEndpoint = devicemgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"]
+ "/device-types/config/" + deviceType;
+ "/device-types/" + deviceType;
displayData.deviceType = deviceType;
displayData.tenantDomain = tenantDomain;
serviceInvokers.XMLHttp.get(

View File

@ -32,7 +32,7 @@ function onRequest(context) {
return opts.inverse(this);
});
var restAPIEndpoint = deviceMgtProps["httpsURL"] + devicemgtProps["backendRestEndpoints"]["deviceMgt"]
+ "/device-types/config/" + deviceType;
+ "/device-types/" + deviceType;
displayData.deviceType = deviceType;
displayData.tenantDomain = tenantDomain;
serviceInvokers.XMLHttp.get(

View File

@ -41,6 +41,7 @@
<module>org.wso2.carbon.device.mgt.analytics.data.publisher</module>
<module>org.wso2.carbon.device.mgt.url.printer</module>
<module>org.wso2.carbon.device.mgt.analytics.wsproxy</module>
<module>io.entgra.device.mgt.ui</module>
</modules>
</project>

View File

@ -27,6 +27,10 @@
<param-name>doAuthentication</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>basicAuth</param-name>
<param-value>true</param-value>
</context-param>
<!--publish to apim-->
<context-param>

View File

@ -115,7 +115,35 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.device.mgt.ui</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<outputDirectory>
${project.build.directory}/maven-shared-archive-resources/webapps
</outputDirectory>
<destFileName>entgra.war</destFileName>
<includes>**/*</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -4,3 +4,4 @@ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../featur
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/jaggeryapps/uuf-template-app);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.ui_${feature.version}/jaggeryapps/uuf-template-app,target:${installFolder}/../../deployment/server/jaggeryapps/uuf-template-app,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.ui_${feature.version}/jaggery-modules/utils/,target:${installFolder}/../../modules/utils,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.ui_${feature.version}/webapps/entgra.war,target:${installFolder}/../../deployment/server/webapps/entgra.war,overwrite:true);\

View File

@ -2,3 +2,4 @@ instructions.configure = \
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.ui.request.interceptor_${feature.version}/webapps/ui-request-handler.war,target:${installFolder}/../../deployment/server/webapps/publisher-ui-request-handler.war,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.ui.request.interceptor_${feature.version}/webapps/ui-request-handler.war,target:${installFolder}/../../deployment/server/webapps/store-ui-request-handler.war,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.ui.request.interceptor_${feature.version}/webapps/ui-request-handler.war,target:${installFolder}/../../deployment/server/webapps/entgra-ui-request-handler.war,overwrite:true);\