Merge pull request #963 from menakaj/application-mgt

Application Management Publisher UI
merge-requests/7/head
sinthuja 7 years ago committed by GitHub
commit 034fe9313e

@ -0,0 +1,192 @@
<?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.0.46-SNAPSHOT</version>
</parent>
<artifactId>org.wso2.carbon.device.application.mgt.authhandler</artifactId>
<version>3.0.46-SNAPSHOT</version>
<packaging>war</packaging>
<name>WSO2 Carbon - Application Management Authentication Handler API</name>
<description>Proxy Service for Authentication Handling in WSO2 App Manager.</description>
<url>http://wso2.org</url>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes>WEB-INF/lib/*cxf*.jar</packagingExcludes>
<warName>auth#application-mgt#v1.0</warName>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>deploy</id>
<build>
<defaultGoal>compile</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy todir="${basedir}/../../../repository/deployment/server/webapps" overwrite="true">
<fileset dir="${basedir}/target">
<include name="auth#application-mgt#v1.0.war" />
</fileset>
</copy>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>client</id>
<build>
<defaultGoal>test</defaultGoal>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.utils</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.json.wso2</groupId>
<artifactId>json</artifactId>
</dependency>
<dependency>
<groupId>commons-codec.wso2</groupId>
<artifactId>commons-codec</artifactId>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-core -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>9.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-jackson -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>9.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-jackson -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs</artifactId>
<version>9.5.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.orbit.com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,52 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.service;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/auth")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface AuthHandlerService {
@POST
@Path("/login")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
Response login(@QueryParam("userName") String userName, @QueryParam("password") String password);
@POST
@Path("/refresh")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
Response refresh(@QueryParam("refreshToken") String refreshToken, @QueryParam("clientId") String clientId,
@QueryParam("clientSecret") String clientSecret);
@POST
@Path("/logout")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
Response logout(@QueryParam("token") String token, @QueryParam("clientId") String clientId,
@QueryParam("clientSecret") String clientSecret);
}

@ -0,0 +1,164 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.service.impl;
import feign.Client;
import feign.Feign;
import feign.auth.BasicAuthRequestInterceptor;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.jaxrs.JAXRSContract;
import org.json.JSONObject;
import org.wso2.carbon.device.application.mgt.auth.handler.service.AuthHandlerService;
import org.wso2.carbon.device.application.mgt.auth.handler.util.Constants;
import org.wso2.carbon.device.application.mgt.auth.handler.util.dto.AccessTokenInfo;
import org.wso2.carbon.device.application.mgt.auth.handler.util.dto.ApiApplicationKey;
import org.wso2.carbon.device.application.mgt.auth.handler.util.dto.ApiApplicationRegistrationService;
import org.wso2.carbon.device.application.mgt.auth.handler.util.dto.ApiRegistrationProfile;
import org.wso2.carbon.device.application.mgt.auth.handler.util.dto.TokenIssuerService;
import org.wso2.carbon.device.application.mgt.auth.handler.util.dto.TokenRevokeService;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
@Path("/auth")
public class AuthHandlerServiceImpl implements AuthHandlerService {
private TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
private Client disableHostnameVerification = new Client.Default(getTrustedSSLSocketFactory(),
new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}
);
@POST
@Path("/login")
@Produces(MediaType.APPLICATION_JSON)
@Override
public Response login(@QueryParam("userName") String userName, @QueryParam("password") String password) {
try {
ApiApplicationRegistrationService apiApplicationRegistrationService = Feign.builder()
.client(disableHostnameVerification)
.requestInterceptor(new BasicAuthRequestInterceptor(userName, password))
.contract(new JAXRSContract()).encoder(new JacksonEncoder()).decoder(new JacksonDecoder())
.target(ApiApplicationRegistrationService.class, Constants.API_APPLICATION_ENDPOINT);
ApiRegistrationProfile apiRegistrationProfile = new ApiRegistrationProfile();
apiRegistrationProfile.setApplicationName(Constants.APPLICATION_NAME);
apiRegistrationProfile.setIsAllowedToAllDomains(false);
apiRegistrationProfile.setIsMappingAnExistingOAuthApp(false);
apiRegistrationProfile.setTags(Constants.TAGS);
ApiApplicationKey apiApplicationKey = apiApplicationRegistrationService.register(apiRegistrationProfile);
//PasswordGrantType
TokenIssuerService tokenIssuerService = Feign.builder().client(disableHostnameVerification)
.requestInterceptor(new BasicAuthRequestInterceptor(apiApplicationKey.getConsumerKey(),
apiApplicationKey.getConsumerSecret()))
.contract(new JAXRSContract()).encoder(new JacksonEncoder()).decoder(new JacksonDecoder())
.target(TokenIssuerService.class, Constants.TOKEN_ENDPOINT);
AccessTokenInfo accessTokenInfo = tokenIssuerService.getToken(Constants.PASSWORD_GRANT_TYPE,
userName, password, Constants.SCOPES);
JSONObject loginInfo = new JSONObject(accessTokenInfo);
loginInfo.append(Constants.USER_NAME, userName);
loginInfo.append(Constants.APPLICATION_INFO, new JSONObject(apiApplicationKey));
System.out.println(loginInfo);
return Response.status(200).entity(loginInfo.toString()).build();
} catch (Exception e) {
//return Response.status(500).build();
}
return Response.status(200).build();
}
@POST
@Path("/refresh")
@Produces(MediaType.APPLICATION_JSON)
@Override
public Response refresh(@QueryParam("refreshToken") String refreshToken, @QueryParam("clientId") String clientId,
@QueryParam("clientSecret") String clientSecret) {
try {
TokenIssuerService tokenIssuerService = Feign.builder().client(disableHostnameVerification)
.requestInterceptor(new BasicAuthRequestInterceptor(clientId, clientSecret))
.contract(new JAXRSContract()).encoder(new JacksonEncoder()).decoder(new JacksonDecoder())
.target(TokenIssuerService.class, Constants.TOKEN_ENDPOINT);
AccessTokenInfo accessTokenInfo = tokenIssuerService.getRefreshToken(Constants.REFRESH_GRANT_TYPE,
refreshToken);
return Response.status(200).entity(new JSONObject(accessTokenInfo)).build();
} catch (Exception e) {
return Response.status(500).build();
}
}
@POST
@Path("/logout")
@Override
public Response logout(@QueryParam("token") String token, @QueryParam("clientId") String clientId,
@QueryParam("clientSecret") String clientSecret) {
try {
TokenRevokeService tokenRevokeService = Feign.builder().client(disableHostnameVerification)
.requestInterceptor(new BasicAuthRequestInterceptor(clientId, clientSecret))
.contract(new JAXRSContract()).encoder(new JacksonEncoder()).decoder(new JacksonDecoder())
.target(TokenRevokeService.class, Constants.TOKEN_ENDPOINT);
tokenRevokeService.revoke(token);
return Response.status(200).build();
} catch (Exception e) {
return Response.status(500).build();
}
}
private SSLSocketFactory getTrustedSSLSocketFactory() {
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
return sc.getSocketFactory();
} catch (KeyManagementException | NoSuchAlgorithmException e) {
return null;
}
}
}

@ -0,0 +1,35 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util;
//TODO: Remove hardcoded localhost and ports
public class Constants {
public static String SCOPES = "perm:application:get perm:application:create perm:application:update " +
"perm:application-mgt:login perm:application:delete perm:platform:add perm:platform:remove " +
"perm:roles:view perm:devices:view perm:platform:get";
public static String[] TAGS = {"device_management"};
public static String USER_NAME = "userName";
public static String APPLICATION_NAME = "applicationmgt_publisher";
public static String TOKEN_ENDPOINT = "https://localhost:8243";
public static String PASSWORD_GRANT_TYPE = "password";
public static String REFRESH_GRANT_TYPE = "refresh_token";
public static String API_APPLICATION_ENDPOINT = "https://localhost:9443/api-application-registration/";
public static String APPLICATION_INFO = "application_info";
}

@ -0,0 +1,83 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* This hold access token info that returned from the api call
*/
@XmlRootElement(name = "AccessTokenInfo")
@JsonIgnoreProperties(ignoreUnknown = true)
public class AccessTokenInfo {
@XmlElement(required = true, name = "tokenType")
private String tokenType;
@XmlElement(required = true, name = "expiresIn")
private String expiresIn;
@XmlElement(required = true, name = "refreshToken")
private String refreshToken;
@XmlElement(required = true, name = "accessToken")
private String accessToken;
public AccessTokenInfo() {}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
@Override
public String toString() {
return accessToken + " " + tokenType + " " + refreshToken + " ";
}
}

@ -0,0 +1,49 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* This holds api application consumer key and secret.
*/
@XmlRootElement
public class ApiApplicationKey {
@XmlElement
private String clientId;
@XmlElement
private String clientSecret;
public String getConsumerKey() {
return this.clientId;
}
public void setClientId(String consumerKey) {
this.clientId = consumerKey;
}
public String getConsumerSecret() {
return this.clientSecret;
}
public void setClientSecret(String consumerSecret) {
this.clientSecret = consumerSecret;
}
}

@ -0,0 +1,43 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* This is the application registration service that exposed for apimApplicationRegistration
*/
@Path("/register")
public interface ApiApplicationRegistrationService {
/**
* This method is used to register api application
*
* @param registrationProfile contains the necessary attributes that are needed in order to register an app.
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
ApiApplicationKey register(ApiRegistrationProfile registrationProfile);
}

@ -0,0 +1,82 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
/**
* This class represents the data that are required to register
* the oauth application.
*/
public class ApiRegistrationProfile {
public String applicationName;
public String tags[];
public boolean isAllowedToAllDomains;
public String consumerKey;
public String consumerSecret;
public boolean isMappingAnExistingOAuthApp;
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public String[] getTags() {
return tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
public boolean isAllowedToAllDomains() {
return isAllowedToAllDomains;
}
public void setIsAllowedToAllDomains(boolean isAllowedToAllDomains) {
this.isAllowedToAllDomains = isAllowedToAllDomains;
}
public boolean isMappingAnExistingOAuthApp() {
return isMappingAnExistingOAuthApp;
}
public void setIsMappingAnExistingOAuthApp(boolean isMappingAnExistingOAuthApp) {
this.isMappingAnExistingOAuthApp = isMappingAnExistingOAuthApp;
}
public String getConsumerKey() {
return consumerKey;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public String getConsumerSecret() {
return consumerSecret;
}
public void setConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
}
}

@ -0,0 +1,47 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import static feign.Util.checkNotNull;
/**
* This is a request interceptor to add oauth token header.
*/
public class OAuthRequestInterceptor implements RequestInterceptor {
private final String headerValue;
/**
* Creates an interceptor that authenticates all requests with the specified OAUTH token
*
* @param token the access token to use for authentication
*/
public OAuthRequestInterceptor(String token) {
checkNotNull(token, "access_token");
headerValue = "Bearer " + token;
}
@Override
public void apply(RequestTemplate template) {
template.header("Authorization", headerValue);
}
}

@ -0,0 +1,83 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
/**
* This class represents the data that are required to register
* the oauth application.
*/
public class RegistrationProfile {
public String callbackUrl;
public String clientName;
public String tokenScope;
public String owner;
public String grantType;
public String applicationType;
private static final String TAG = RegistrationProfile.class.getSimpleName();
public String getCallbackUrl() {
return callbackUrl;
}
public void setCallbackUrl(String callBackUrl) {
this.callbackUrl = callBackUrl;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getTokenScope() {
return tokenScope;
}
public void setTokenScope(String tokenScope) {
this.tokenScope = tokenScope;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getApplicationType() {
return applicationType;
}
public void setApplicationType(String applicationType) {
this.applicationType = applicationType;
}
}

@ -0,0 +1,42 @@
/*
* 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.
*/
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
/**
* This hold the api definition that is used as a contract with netflix feign.
*/
@Path("/token")
public interface TokenIssuerService {
@POST
@Produces(MediaType.APPLICATION_JSON)
AccessTokenInfo getToken(@QueryParam("grant_type") String grant, @QueryParam("username") String username,
@QueryParam("password") String password, @QueryParam("scope") String scope);
@POST
@Produces(MediaType.APPLICATION_JSON)
AccessTokenInfo getRefreshToken(@QueryParam("grant_type") String grantType,
@QueryParam("refreshToken") String refreshToken);
}

@ -16,6 +16,20 @@
* under the License. * under the License.
*/ */
package org.wso2.carbon.device.application.mgt.auth.handler.util.dto;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
/** /**
* Defines the list of App Manager APIs. * Api definition for token revoke that will be used as Feign contract.
* */ * */
@Path("/revoke")
public interface TokenRevokeService {
@POST
Response revoke(@QueryParam("token")String accessToken);
}

@ -0,0 +1,35 @@
<?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.
-->
<!-- This file contains the list of permissions that are associated with URL end points
of the web app. Each permission should contain the name, permission path ,API path
(URL) , HTTP method and OAUTH2 authorization scope (not-required).
When defining dynamic paths for APIs, path variables are denoted by '*' notation.
For ex:
Actual API endpoint: devicemgt_admin/1.0.0/devices/{device-id}
URL to be represented here: /devices/*
NOTE: All the endpoints of the web app should be available in this file. Otherwise
it will result 403 error at the runtime.
-->
<PermissionConfiguration>
<APIVersion></APIVersion>
<!-- Application related permissions -->
</PermissionConfiguration>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
~ Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
~
~ WSO2 Inc. licenses this file to you under the Apache License,
~ Version 2.0 (the "License"); you may not use this file except
~ in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
-->
<!--
This file defines class loading policy of the whole container. But this behaviour can be overridden by individual webapps by putting this file into the META-INF/ directory.
-->
<Classloading xmlns="http://wso2.org/projects/as/classloading">
<!-- Parent-first or child-first. Default behaviour is child-first.-->
<ParentFirst>false</ParentFirst>
<!--
Default environments that contains provides to all the webapps. This can be overridden by individual webapps by specifing required environments
Tomcat environment is the default and every webapps gets it even if they didn't specify it.
e.g. If a webapps requires CXF, they will get both Tomcat and CXF.
-->
<Environments>CXF,Carbon</Environments>
</Classloading>

@ -0,0 +1,38 @@
<?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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
<jaxrs:server id="applicationMgtAuthService" address="/">
<jaxrs:serviceBeans>
<ref bean="applicationMgtAuthServiceBean"/>
</jaxrs:serviceBeans>
</jaxrs:server>
<bean id="applicationMgtAuthServiceBean"
class="org.wso2.carbon.device.application.mgt.auth.handler.service.impl.AuthHandlerServiceImpl"/>
</beans>

@ -0,0 +1,115 @@
<?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>Application Management Auth Webapp</display-name>
<servlet>
<description>JAX-WS/JAX-RS Application Management Endpoint</description>
<display-name>JAX-WS/JAX-RS Servlet</display-name>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
<context-param>
<param-name>doAuthentication</param-name>
<param-value>false</param-value>
</context-param>
<!--publish to apim-->
<context-param>
<param-name>managed-api-enabled</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>managed-api-owner</param-name>
<param-value>admin</param-value>
</context-param>
<context-param>
<param-name>isSharedWithAllTenants</param-name>
<param-value>true</param-value>
</context-param>
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,DELETE,PUT</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Content-Type</param-value>
</init-param>
</filter>
<filter>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter>
<filter-name>ContentTypeBasedCachePreventionFilter</filter-name>
<filter-class>org.wso2.carbon.ui.filters.cache.ContentTypeBasedCachePreventionFilter</filter-class>
<init-param>
<param-name>patterns</param-name>
<param-value>text/html" ,application/json" ,text/plain</param-value>
</init-param>
<init-param>
<param-name>filterAction</param-name>
<param-value>enforce</param-value>
</init-param>
<init-param>
<param-name>httpHeaders</param-name>
<param-value>Cache-Control: no-store, no-cache, must-revalidate, private</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ContentTypeBasedCachePreventionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

@ -19,15 +19,19 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> 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> <parent>
<groupId>org.wso2.carbon.devicemgt</groupId> <groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId> <artifactId>application-mgt</artifactId>
<version>3.0.46-SNAPSHOT</version> <version>3.0.46-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.publisher.ui</artifactId> <artifactId>org.wso2.carbon.device.application.mgt.publisher.ui</artifactId>
<version>3.0.46-SNAPSHOT</version> <version>3.0.46-SNAPSHOT</version>
<packaging>war</packaging>
<name>WSO2 Carbon - Application Management Publisher UI</name>
<description>WSO2 Carbon - Application Management Publisher UI React Application</description>
<url>http://wso2.org</url>
<build> <build>
<plugins> <plugins>

@ -11,38 +11,40 @@
"dependencies": { "dependencies": {
"axios": "^0.16.2", "axios": "^0.16.2",
"flux": "^3.1.3", "flux": "^3.1.3",
"history": "^4.6.3", "history": "^4.7.2",
"latest-version": "^3.1.0", "latest-version": "^3.1.0",
"material-ui": "^0.19.0", "material-ui": "^0.19.1",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qs": "^6.5.0", "qs": "^6.5.0",
"react": "^15.6.1", "react": "^15.6.1",
"react-dom": "^15.6.1", "react-dom": "^15.6.1",
"react-dropzone": "^4.1.0", "react-dropzone": "^4.1.2",
"react-images-uploader": "^1.1.0", "react-images-uploader": "^1.1.0",
"react-material-ui-form-validator": "^0.5.0", "react-material-ui-form-validator": "^0.5.1",
"react-modal": "^2.2.2", "react-modal": "^2.3.2",
"react-router": "^4.1.2", "react-router": "^4.2.0",
"react-router-dom": "^4.1.2", "react-router-dom": "^4.2.2",
"react-scripts": "1.0.10", "react-scripts": "1.0.10",
"react-sliding-pane": "^1.2.3", "react-sliding-pane": "^1.2.3",
"react-tap-event-plugin": "^2.0.1" "react-tap-event-plugin": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.24.1", "babel-core": "^6.26.0",
"babel-loader": "^7.0.0", "babel-loader": "^7.1.2",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"chai": "^4.0.2",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babel-register": "^6.24.1", "babel-register": "^6.26.0",
"css-loader": "^0.28.2", "chai": "^4.1.2",
"css-loader": "^0.28.7",
"less": "^2.7.2", "less": "^2.7.2",
"less-loader": "^4.0.4", "less-loader": "^4.0.4",
"mocha": "^3.4.1", "mocha": "^3.4.1",
"mock-local-storage": "^1.0.2", "mock-local-storage": "^1.0.2",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.1", "style-loader": "^0.18.1",
"webpack": "^2.5.0" "webpack": "^2.7.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

@ -66,3 +66,8 @@
.createplatformdropzonep { .createplatformdropzonep {
margin: 70px 40px 70px 70px margin: 70px 40px 70px 70px
} }
.createPlatformTagWrapper {
display: flex;
flex-wrap: wrap;
}

@ -16,27 +16,27 @@
* under the License. * under the License.
*/ */
import './App.css'; import './App.scss';
import Theme from './theme';
import React, {Component} from 'react'; import React, {Component} from 'react';
import AuthHandler from './api/authHandler';
import createHistory from 'history/createBrowserHistory'; import createHistory from 'history/createBrowserHistory';
import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom' import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme'; import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { import {
ApplicationCreate, ApplicationCreate,
ApplicationListing, ApplicationListing,
BaseLayout, BaseLayout,
Login, Login,
NotFound, NotFound,
PlatformCreate, PlatformCreate,
PlatformListing PlatformListing
} from './components'; } from './components';
import Theme from './theme';
const history = createHistory({basename: '/publisher'}); const history = createHistory({basename: '/publisher'});
/** /**
* This component defines the layout and the routes for the app. * This component defines the layout and the routes for the app.
* All the content will be loaded inside the Base component. * All the content will be loaded inside the Base component.
@ -54,23 +54,40 @@ class Base extends Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
user: "admin" user: null
}
}
componentWillMount() {
let user = AuthHandler.getUser();
if (user) {
if (!AuthHandler.isTokenExpired()) {
this.setState({user: user});
} else {
console.log("expired!");
this.setState({user: null});
}
} }
} }
componentDidMount() {
}
render() { render() {
if (this.state.user) { if (this.state.user !== null) {
console.log("Have User.");
return ( return (
<div className="container"> <div className="container">
<BaseLayout> <BaseLayout user={this.state.user}>
<Switch> <Switch>
<Redirect exact path={"/"} to={"/assets/apps"}/> <Redirect exact path={"/"} to={"/assets/apps"}/>
<Route exact path={"/assets/apps"} component={ApplicationListing}/> <Route exact path={"/assets/apps"} component={ApplicationListing}/>
<Route exact path={"/assets/apps/create"} component={ApplicationCreate}/> <Route exact path={"/assets/apps/create"} component={ApplicationCreate}/>
<Route exact path={"/assets/platforms"} component={PlatformListing}/> <Route exact path={"/assets/platforms"} component={PlatformListing}/>
<Route exact path={"/assets/platforms/create"} component={PlatformCreate}/> <Route exact path={"/assets/platforms/create"} component={PlatformCreate}/>
<Route exact path={"/assets/apps/:app"} /> <Route exact path={"/assets/apps/:app"}/>
<Route exact path={"/assets/apps/:app/edit"} /> <Route exact path={"/assets/apps/:app/edit"}/>
<Route exact path={"/assets/platforms/:platform"}/> <Route exact path={"/assets/platforms/:platform"}/>
<Route exact path={"/assets/platforms/:platform/edit"}/> <Route exact path={"/assets/platforms/:platform/edit"}/>
<Route exact path={"/assets/reviews"}/> <Route exact path={"/assets/reviews"}/>
@ -80,9 +97,12 @@ class Base extends Component {
</BaseLayout> </BaseLayout>
</div> </div>
) )
} } else {
console.log("No user");
return (<Redirect to={"/login"}/>) return (<Redirect to={"/login"}/>)
} }
}
} }
/** /**

@ -1,17 +0,0 @@
/*
* 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.
*/

@ -0,0 +1,138 @@
/*
* 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.
*/
'use strict';
import Axios from 'axios';
import AuthHandler from './authHandler';
import Constants from '../common/constants';
import Helper from './helpers/appMgtApiHelpers';
/**
* Api definitions related to application management.
* TODO: Work to be done on Application release.
* */
export default class ApplicationMgtApi {
/**
* Api for create an application.
* @param: applicationData: The application data object. This contains an object array of each step data from
* application creation wizard.
*
* From applicationData, the proper application object will be created and send it to the api.
* */
static createApplication(applicationData) {
let {application, images} = Helper.buildApplication(applicationData);
const headers = AuthHandler.createAuthenticationHeaders("application/json");
console.log(application);
console.log(images);
Axios.post(Constants.appManagerEndpoints.CREATE_APP, application, {headers: headers});
}
/**
* Upload the image artifacts (banner, icon, screenshots) related to the application.
* @param appId: The application uuid of the application which the images should be uploaded to.
* @param images: The images object. This contains icon, banner and screenshots.
* */
static uploadImageArtifacts(appId, images) {
let formData = new FormData();
formData.append('icon', images.icon);
formData.append('banner', images.banner);
formData.append('screenshot', images.screenshots);
console.log("Image", formData);
const headers = AuthHandler.createAuthenticationHeaders("multipart/form-data");
return Axios.post(Constants.appManagerEndpoints.UPLOAD_IMAGE_ARTIFACTS + appId, formData, {headers: headers});
}
/**
* Method to handle application release process.
* */
static releaseApplication(appId) {
}
/**
* Promote the current life cycle state of the application.
* @param appId: The uuid of the application which the state should be updated.
* @param nextState: The next lifecycle state that the application can be updated to.
*
* URL Pattern : /application/1.0/
* */
static updateLifeCycleState(appId, nextState) {
}
/**
* Get the next possible state, which the application can be promoted to.
* @param appId: The application uuid.
*/
static getNextLifeCycleState(appId) {
}
/**
* Edit created application.
* @param applicationData: The modified application data.
* */
static editApplication(applicationData) {
let app = Helper.buildApplication(applicationData).application;
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.put(Constants.appManagerEndpoints.CREATE_APP, app, {headers: headers});
}
static getApplicationArtifacts(appId, artifactName) {
const headers = AuthHandler.createAuthenticationHeaders("image/png");
return Axios.get(Constants.appManagerEndpoints.GET_IMAGE_ARTIFACTS + appId + "?name=" + artifactName,
{headers: headers});
}
static editApplicationArtifacts(appId, images) {
let formData = new FormData();
formData.append('icon', images.icon);
formData.append('banner', images.banner);
formData.append('screenshot', images.screenshots);
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.put(Constants.appManagerEndpoints.UPLOAD_IMAGE_ARTIFACTS + appId, formData, {headers: headers});
}
/**
* Get all the created applications for the user.
* @return Object: The response object from the axios post.
* */
static getApplications() {
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.get(Constants.appManagerEndpoints.GET_ALL_APPS, {headers: headers});
}
/**
* Get specific application.
* @param appId: The application Id.
* */
static getApplication(appId) {
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.get(Constants.appManagerEndpoints.GET_ALL_APPS + appId, {headers: headers});
}
/**
* Delete specified application.
* @param appId: The id of the application which is to be deleted.
* */
static deleteApplication(appId) {
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.delete(Constants.appManagerEndpoints.GET_ALL_APPS + appId, {headers: headers});
}
}

@ -0,0 +1,148 @@
/*
* 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.
*/
'use strict';
import Axios from 'axios';
import User from './data/user';
import Utils from './data/utils';
import Constants from "../common/constants";
/**
* Handles all tasks related to Authentication and Authorization.
* Generate access tokens, verify the user has necessary permissions etc.
* */
class AuthHandler {
/**
* Sends a request to the auth handler endpoint (auth/application-mgt/v1.0/auth/login) and generate token pair.
* @param userName: The user name of the user.
* @param password: The user password.
* @return Object: The response object from the axios post.
* */
static login(userName, password) {
const headers = {"Content-type": "application/json"};
let login_promise =
Axios.post(Constants.userConstants.LOGIN_URL+"?userName=" + userName+ "&password=" + password,
null, {headers: headers});
login_promise.then(response => {
console.log(response);
const userName = response.data.userName;
const validityPeriod = response.data.expiresIn; // In seconds
const WSO2_IOT_TOKEN = response.data.accessToken;
const refreshToken = response.data.refreshToken;
const clientId = response.data.application_info[0].consumerKey;
const clientSecret = response.data.application_info[0].consumerSecret;
const user = new User(userName, clientId, clientSecret, validityPeriod);
console.log(user);
user.setAuthToken(WSO2_IOT_TOKEN, validityPeriod);
let expiresIn = Date.now() + (validityPeriod * 1000);
localStorage.setItem("expiresIn", expiresIn);
AuthHandler.setUser(user);
}
);
return login_promise;
};
/**
* Persists the user object in browser's local storage.
* @param user: The user object.
* */
static setUser(user) {
if (!user instanceof User) {
throw "Invalid user object";
}
user.created = Date.now();
localStorage.setItem(Constants.userConstants.WSO2_USER, JSON.stringify(user.toJson()));
/* TODO: IMHO it's better to get this key (`wso2_user`) from configs */
}
static unauthorizedErrorHandler(error_response) {
if (error_response.status !== 401) { /* Skip unrelated response code to handle in unauthorizedErrorHandler*/
throw error_response;
/* re throwing the error since we don't handle it here and propagate to downstream error handlers in catch chain*/
}
let message = "The session has expired" + ".<br/> You will be redirect to the login page ...";
if (true) {
alert(message);
} else {
throw error_response;
}
}
/**
* Get the logged in user.
* @return User: The logged in user object.
* */
static getUser() {
const userData = localStorage.getItem(Constants.userConstants.WSO2_USER);
const partialToken = Utils.getCookie(Constants.userConstants.PARTIAL_TOKEN);
if (!(userData && partialToken)) {
return null;
}
return User.fromJson(JSON.parse(userData));
}
isLoggedIn() {
}
static logout() {
const user = AuthHandler.getUser();
const clientId = user.getClientId();
const clientSecret = user.getClientSecret();
const token = user.getAuthToken();
const headers = {"Content-type": "application/json"};
let login_promise = Axios.post(Constants.userConstants.LOGOUT_URL+"?token=" + token + "&clientId=" + clientId
+ "&clientSecret=" + clientSecret,
null, {headers: headers});
login_promise.then(
(response) => {
Utils.delete_cookie(Constants.userConstants.PARTIAL_TOKEN);
localStorage.removeItem(Constants.userConstants.WSO2_USER);
window.location = "/";
}
).catch(
(err) => {
AuthHandler.unauthorizedErrorHandler(err);
}
)
}
/**
* Checks whether the access token is expired.
* @return boolean: True if expired. False otherwise.
* */
static isTokenExpired() {
const expiresIn = localStorage.getItem("expiresIn");
return (expiresIn < Date.now());
}
static createAuthenticationHeaders(contentType) {
return {
"Authorization": "Bearer " + AuthHandler.getUser().getAuthToken(),
"Content-Type": contentType,
};
};
}
export default AuthHandler;

@ -0,0 +1,118 @@
/*
* 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.
*/
"use strict";
import Utils from './utils'
import Constants from '../../common/constants';
/**
* Represent an user logged in to the application, There will be allays one user per session and
* this user details will be persist in browser localstorage.
*/
export default class User {
constructor(name, clientId, clientSecret, validityPeriod) {
if (User._instance) {
return User._instance;
}
this._userName = name;
this._clientId = clientId;
this._clientSecret = clientSecret;
this._expires = validityPeriod;
User._instance = this;
}
/**
* OAuth scopes which are available for use by this user
* @returns {Array} : An array of scopes
*/
get scopes() {
return this._scopes;
}
/**
* Set OAuth scopes available to be used by this user
* @param {Array} newScopes : An array of scopes
*/
set scopes(newScopes) {
Object.assign(this.scopes, newScopes);
}
/**
* Get the JS accessible access token fragment from cookie storage.
* @returns {String|null}
*/
getAuthToken() {
return Utils.getCookie(Constants.userConstants.PARTIAL_TOKEN);
}
getClientId() {
return this._clientId;
}
getClientSecret() {
return this._clientSecret;
}
/**
* Store the JavaScript accessible access token segment in cookie storage
* @param {String} newToken : Part of the access token which needs when accessing REST API
* @param {Number} validityPeriod : Validity period of the cookie in seconds
*/
setAuthToken(newToken, validityPeriod) {
Utils.delete_cookie(Constants.userConstants.PARTIAL_TOKEN);
Utils.setCookie(Constants.userConstants.PARTIAL_TOKEN, newToken, validityPeriod);
}
/**
* Get the user name of logged in user.
* @return String: User name
* */
getUserName() {
return this._userName;
}
/**
* Provide user data in JSON structure.
* @returns {JSON} : JSON representation of the user object
*/
toJson() {
return {
name: this._userName,
clientId: this._clientId,
clientSecret: this._clientSecret,
expires: this._expires
};
}
/**
* User utility method to create an user from JSON object.
* @param {JSON} userJson : Need to provide user information in JSON structure to create an user object
* @returns {User} : An instance of User(this) class.
*/
static fromJson(userJson) {
const _user = new User(userJson.name);
_user._clientId = userJson.clientId;
_user._clientSecret = userJson.clientSecret;
_user._expires = userJson.expires;
console.log(_user);
return _user;
}
}
User._instance = null; // A private class variable to preserve the single instance of a swaggerClient

@ -0,0 +1,92 @@
/*
* 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.
*/
/**
* Utility class for Publisher application
*/
class PublisherUtils {
/**
* TODO: Remove this method one the initial phase is done, This is used to continue the API class until the login page is create
* @returns {promise}
*/
// static autoLogin() {
// let auth = new AuthManager();
// return auth.authenticateUser('admin', 'admin');
// }
/**
* Get JavaScript accessible cookies saved in browser, by giving the cooke name.
* @param {String} name : Name of the cookie which need to be retrived
* @returns {String|null} : If found a cookie with given name , return its value,Else null value is returned
*/
static getCookie(name) {
let pairs = document.cookie.split(";");
let cookie = null;
for (let pair of pairs) {
pair = pair.split("=");
let cookie_name = pair[0].trim();
let value = encodeURIComponent(pair[1]);
if (cookie_name === name) {
cookie = value;
break;
}
}
return cookie;
}
/**
* Delete a browser cookie given its name
* @param {String} name : Name of the cookie which need to be deleted
*/
static delete_cookie(name) {
document.cookie = name + '=; Path=' + "/" + '; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
}
/**
* Set a cookie with given name and value assigned to it. Cookies can be only set to the same origin,
* which the script is running
* @param {String} name : Name of the cookie which need to be set
* @param {String} value : Value of the cookie, expect it to be URLEncoded
* @param {number} validityPeriod : (Optional) Validity period of the cookie in seconds
* @param {String} path : Path which needs to set the given cookie
* @param {boolean} secured : secured parameter is set
*/
static setCookie(name, value, validityPeriod, path = "/", secured = true) {
let expires = "";
const securedDirective = secured ? "; Secure" : "";
if (validityPeriod) {
const date = new Date();
date.setTime(date.getTime() + validityPeriod * 1000);
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + value + expires + "; path=" + path + securedDirective + validityPeriod
}
/**
* Given an object returns whether the object is empty or not
* @param {Object} object : Any JSON object
* @returns {boolean}
*/
static isEmptyObject(object) {
return Object.keys(object).length === 0 && object.constructor === Object
}
}
export default PublisherUtils;

@ -0,0 +1,61 @@
/*
* 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.
*/
'use strict';
/**
* Helper methods for app publisher.
* */
export default class Helper {
/**
* Generate application object from form data passed.
* @param appData: Application data from the application creation form.
* @return {Object, Object}: The application object and the set of images related to the application.
* */
static buildApplication(appData) {
let application = {};
let images = {};
for (let step in appData) {
let tmpData = appData[step].data.step;
for (let prop in tmpData) {
if (prop === 'banner' || prop === 'screenshots' || prop === 'icon') {
images[prop] = tmpData[prop];
} else if(prop === 'tags') {
application[prop] = Helper.stringifyTags(tmpData[prop]);
} else {
application[prop] = tmpData[prop];
}
}
}
return {application, images};
}
static stringifyTags(tags) {
let tmpTags = [];
for (let tag in tags) {
console.log(tag);
tmpTags.push(tags[tag].value);
}
return tmpTags;
}
}

@ -0,0 +1,68 @@
/*
* 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.
*/
'use strict';
import Axios from 'axios';
import AuthHandler from './authHandler';
import Constants from '../common/constants';
/**
* Api definitions for Platform management.
* */
export default class PlatformMgtApi{
/**
* Create a new Platform
* @param platformData: The platform data object.
* */
static createPlatform(platformData) {
const headers = AuthHandler.createAuthenticationHeaders("application/json");
Axios.post(Constants.platformManagerEndpoints.CREATE_PLATFORM, platformData, {headers: headers}).then(
function (response) {
console.log(response);
}
).catch(function (err) {
console.log(err);
});
}
/**
* Get available platforms
* */
static getPlatforms() {
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.get(Constants.platformManagerEndpoints.GET_ENABLED_PLATFORMS, {headers: headers});
}
/**
* Get the user specified platform
* @param platformId: The identifier of the platform
* */
static getPlatform(platformId) {
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.get(Constants.platformManagerEndpoints.GET_PLATFORM + platformId, {headers: headers});
}
/**
* Delete specified platform
* @param platformId: The id of the platform which is to be deleted.
* */
static deletePlatform(platformId) {
const headers = AuthHandler.createAuthenticationHeaders("application/json");
return Axios.delete(Constants.platformManagerEndpoints.GET_PLATFORM + platformId, {headers: headers});
}
}

@ -15,3 +15,33 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
'use strict';
//TODO: Replace the server address with response from auth endpoint and remove hardcoded ids etc.
export default class Constants {
static scopes = 'perm:application:get perm:application:create perm:application:update perm:application-mgt:login' +
' perm:application:delete perm:platform:add perm:platform:remove perm:roles:view perm:devices:view';
static appManagerEndpoints = {
GET_ALL_APPS: 'https://localhost:8243/api/application-mgt/v1.0/applications/1.0.0/',
CREATE_APP: 'https://localhost:8243/api/application-mgt/v1.0/applications/1.0.0/',
UPLOAD_IMAGE_ARTIFACTS: 'https://localhost:8243/api/application-mgt/v1.0/applications/1.0.0/upload-image-artifacts/', //+appId
GET_IMAGE_ARTIFACTS: "https://localhost:8243/api/application-mgt/v1.0/applications/1.0.0/image-artifacts/"
};
static platformManagerEndpoints = {
CREATE_PLATFORM: 'https://localhost:8243/api/application-mgt/v1.0/platforms/1.0.0',
GET_ENABLED_PLATFORMS: 'https://localhost:8243/api/application-mgt/v1.0/platforms/1.0.0?status=ENABLED',
GET_PLATFORM: 'https://localhost:8243/api/application-mgt/v1.0/platforms/1.0.0/'
};
static userConstants = {
LOGIN_URL:"https://localhost:9443/auth/application-mgt/v1.0/auth/login",
LOGOUT_URL: "https://localhost:9443/auth/application-mgt/v1.0/auth/logout",
REFRESH_TOKEN_URL: "",
WSO2_USER: 'wso2_user',
PARTIAL_TOKEN: 'WSO2_IOT_TOKEN'
}
}

@ -16,15 +16,16 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import React, {Component} from 'react'; import React, {Component} from 'react';
import Dialog from 'material-ui/Dialog'; import Dialog from 'material-ui/Dialog';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import FlatButton from 'material-ui/FlatButton'; import FlatButton from 'material-ui/FlatButton';
import {Step1, Step2, Step3} from './CreateSteps'; import {Step1, Step2, Step3} from './CreateSteps';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import ApplicationMgtApi from '../../api/applicationMgtApi';
import {Card, CardActions, CardTitle} from 'material-ui/Card'; import {Card, CardActions, CardTitle} from 'material-ui/Card';
import {Step, StepLabel, Stepper,} from 'material-ui/Stepper'; import {Step, StepLabel, Stepper,} from 'material-ui/Stepper';
import Theme from '../../theme';
/** /**
* The App Create Component. * The App Create Component.
@ -38,12 +39,14 @@ class ApplicationCreate extends Component {
constructor() { constructor() {
super(); super();
this.scriptId = "application-create"; this.scriptId = "application-create";
this.setStepData.bind(this); this.setStepData = this.setStepData.bind(this);
this.removeStepData.bind(this); this.removeStepData = this.removeStepData.bind(this);
this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
this.handleCancel.bind(this); this.handleCancel = this.handleCancel.bind(this);
this.handleYes.bind(this); this.handleYes = this.handleYes.bind(this);
this.handleNo.bind(this); this.handleNo = this.handleNo.bind(this);
this.handlePrev = this.handlePrev.bind(this);
this.handleNext = this.handleNext.bind(this);
this.state = { this.state = {
finished: false, finished: false,
stepIndex: 0, stepIndex: 0,
@ -66,7 +69,7 @@ class ApplicationCreate extends Component {
/** /**
* Handles next button click event. * Handles next button click event.
* */ * */
handleNext = () => { handleNext() {
console.log("Handle Next"); console.log("Handle Next");
const {stepIndex} = this.state; const {stepIndex} = this.state;
this.setState({ this.setState({
@ -78,15 +81,24 @@ class ApplicationCreate extends Component {
/** /**
* Handles form submit. * Handles form submit.
* */ * */
handleSubmit = () => { handleSubmit() {
console.log(this.state.stepData); let stepData = this.state.stepData;
let applicationCreationPromise = ApplicationMgtApi.createApplication(stepData);
applicationCreationPromise.then( response => {
this.handleYes();
}
).catch(
function (err) {
console.log(err);
}
);
}; };
/** /**
* Handles cancel button click event. * Handles cancel button click event.
* This will show a confirmation dialog to cancel the application creation process. * This will show a confirmation dialog to cancel the application creation process.
* */ * */
handleCancel = () => { handleCancel() {
this.setState({isDialogOpen: true}); this.setState({isDialogOpen: true});
}; };
@ -94,7 +106,7 @@ class ApplicationCreate extends Component {
* Handled [ < Prev ] button click. * Handled [ < Prev ] button click.
* This clears the data in the current step and returns to the previous step. * This clears the data in the current step and returns to the previous step.
* */ * */
handlePrev = () => { handlePrev() {
const {stepIndex} = this.state; const {stepIndex} = this.state;
if (stepIndex > 0) { if (stepIndex > 0) {
this.removeStepData(); this.removeStepData();
@ -104,8 +116,10 @@ class ApplicationCreate extends Component {
/** /**
* Saves form data in each step in to the state. * Saves form data in each step in to the state.
* @param step: The step number of the step data.
* @param data: The form data of the step.
* */ * */
setStepData = (step, data) => { setStepData(step, data) {
console.log(step, data, this.state.stepData); console.log(step, data, this.state.stepData);
let tmpStepData = this.state.stepData; let tmpStepData = this.state.stepData;
tmpStepData.push({step: step, data: data}); tmpStepData.push({step: step, data: data});
@ -116,7 +130,7 @@ class ApplicationCreate extends Component {
/** /**
* Remove the last data point * Remove the last data point
* */ * */
removeStepData = () => { removeStepData() {
let tempData = this.state.stepData; let tempData = this.state.stepData;
tempData.pop(); tempData.pop();
this.setState({stepData: tempData}); this.setState({stepData: tempData});
@ -126,7 +140,7 @@ class ApplicationCreate extends Component {
* Handles the Yes button in app creation cancellation dialog. * Handles the Yes button in app creation cancellation dialog.
* Clears all the form data and reset the wizard. * Clears all the form data and reset the wizard.
* */ * */
handleYes = () => { handleYes() {
this.setState({finished: false, stepIndex: 0, stepData: [], isDialogOpen: false}); this.setState({finished: false, stepIndex: 0, stepData: [], isDialogOpen: false});
}; };
@ -134,7 +148,7 @@ class ApplicationCreate extends Component {
* Handles No button in app creation cancellation dialog. * Handles No button in app creation cancellation dialog.
* Returns to the same step. * Returns to the same step.
* */ * */
handleNo = () => { handleNo() {
this.setState({isDialogOpen: false}); this.setState({isDialogOpen: false});
}; };
@ -150,28 +164,38 @@ class ApplicationCreate extends Component {
getStepContent(stepIndex) { getStepContent(stepIndex) {
switch (stepIndex) { switch (stepIndex) {
case 0: case 0:
return <Step1 handleNext={this.handleNext} return (
<Step1
handleNext={this.handleNext}
setData={this.setStepData} setData={this.setStepData}
removeData={this.removeStepData}/>; removeData={this.removeStepData}
/>
);
case 1: case 1:
return <Step2 handleNext={this.handleNext} return (
<Step2
handleNext={this.handleNext}
handlePrev={this.handlePrev} handlePrev={this.handlePrev}
setData={this.setStepData} setData={this.setStepData}
removeData={this.removeStepData}/>; removeData={this.removeStepData}
/>
);
case 2: case 2:
return <Step3 handleFinish={this.handleNext} return (
<Step3
handleFinish={this.handleNext}
handlePrev={this.handlePrev} handlePrev={this.handlePrev}
setData={this.setStepData} setData={this.setStepData}
removeData={this.removeStepData}/>; removeData={this.removeStepData}
/>
);
default: default:
return 'You\'re a long way from home sonny jim!'; return <div/>;
} }
} }
render() { render() {
const {finished, stepIndex} = this.state; const {finished, stepIndex} = this.state;
const contentStyle = {margin: '0 16px'};
/** /**
* Defines the dialog box actions. [Yes][No] * Defines the dialog box actions. [Yes][No]
@ -189,7 +213,6 @@ class ApplicationCreate extends Component {
/>, />,
]; ];
return ( return (
<div className="middle createapplicationmiddle"> <div className="middle createapplicationmiddle">
<Card className="creataapplicationcard"> <Card className="creataapplicationcard">

@ -16,12 +16,14 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import React, {Component} from 'react'; import React, {Component} from 'react';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import TextField from 'material-ui/TextField'; import TextField from 'material-ui/TextField';
import AuthHandler from "../../api/authHandler";
import DataTable from '../UIComponents/DataTable'; import DataTable from '../UIComponents/DataTable';
import ApplicationMgtApi from '../../api/applicationMgtApi';
import {Card, CardActions, CardTitle} from 'material-ui/Card'; import {Card, CardActions, CardTitle} from 'material-ui/Card';
import Theme from '../../theme';
/** /**
* The App Create Component. * The App Create Component.
@ -34,69 +36,39 @@ import Theme from '../../theme';
class ApplicationListing extends Component { class ApplicationListing extends Component {
constructor() { constructor() {
super(); super();
this.searchApplications = this.searchApplications.bind(this);
this.onRowClick = this.onRowClick.bind(this);
this.setData = this.setData.bind(this);
this.sortData = this.sortData.bind(this);
this.compare = this.compare.bind(this);
this.state = { this.state = {
data: [], searchedApplications: [],
applications: [],
asc: true asc: true
}; };
this.scriptId = "application-listing"; this.scriptId = "application-listing";
} }
data = [
{
id: Math.random(),
applicationName:"Cne",
platform:'Android',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"Gone",
platform:'IOS',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"Ane",
platform:'Android',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"one",
platform:'Android',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"one",
platform:'Android',
category:"Public",
status: "Created"
},
];
headers = [ headers = [
{ {
data_id: "image", data_id: "image",
data_type: "image", data_type: "image",
sortable: false, sortable: false,
label: ""}, label: ""
},
{ {
data_id: "applicationName", data_id: "applicationName",
data_type: "string", data_type: "string",
sortable: true, sortable: true,
label: "Application Name", label: "Application Name",
sort: this._sortData.bind(this) sort: this.sortData
}, },
{ {
data_id: "platform", data_id: "platform",
data_type: "image_array", data_type: "image_array",
sortable: false, sortable: false,
label: "Platform"}, label: "Platform"
},
{ {
data_id: "category", data_id: "category",
data_type: "string", data_type: "string",
@ -112,8 +84,6 @@ class ApplicationListing extends Component {
]; ];
componentWillMount() { componentWillMount() {
//Fetch all the applications from backend and create application objects.
this.setState({data: this.data});
/** /**
*Loading the theme files based on the the user-preference. *Loading the theme files based on the the user-preference.
@ -123,38 +93,70 @@ class ApplicationListing extends Component {
componentWillUnmount() { componentWillUnmount() {
Theme.removeThemingScripts(this.scriptId); Theme.removeThemingScripts(this.scriptId);
// this.setState({data: this.data});
}
componentDidMount() {
let getApps = ApplicationMgtApi.getApplications();
getApps.then(response => {
let apps = this.setData(response.data.applications);
console.log(apps);
this.setState({searchedApplications: apps});
// console.log(this.setState({data: response.data}), console.log(this.state));
}).catch(err => {
AuthHandler.unauthorizedErrorHandler(err);
});
}
/**
* Extract application from application list and update the state.
* */
setData(applications) {
let apps = [];
for (let app in applications) {
let application = {};
application.id = applications[app].uuid;
application.applicationName = applications[app].name;
application.platform = applications[app].platform.name;
application.category = applications[app].category.id;
application.status = applications[app].currentLifecycle.lifecycleState.name;
apps.push(application);
} }
this.setState({searchedApplications: apps});
}
/** /**
* Handles the search action. * Handles the search action.
* When typing in the search bar, this method will be invoked. * When typing in the search bar, this method will be invoked.
* @param event: The event triggered from typing in the search box.
* @param searchText: The text that typed in the search box.
* */ * */
_searchApplications(event, word) { searchApplications(event, searchText) {
let searchedData; let searchedData;
if (word){ if (searchText) {
searchedData = this.data.filter((dataItem) => { searchedData = this.state.applications.filter((dataItem) => {
return dataItem.applicationName.includes(word); return dataItem.applicationName.includes(searchText);
}); });
} else { } else {
searchedData = this.data; searchedData = this.state.applications;
} }
this.setState({data: searchedData}, console.log("Searched data ", this.state.data)); this.setState({searchedApplications: searchedData}, console.log("Searched data ", this.state.searchedApplications));
} }
/** /**
* Handles sort data function and toggles the asc state. * Handles sort data function and toggles the asc state.
* asc: true : sort in ascending order. * asc: true : sort in ascending order.
* */ * */
_sortData() { sortData() {
console.log(this.state);
let isAsc = this.state.asc; let isAsc = this.state.asc;
let datas = isAsc?this.data.sort(this._compare):this.data.reverse(); let sortedData = isAsc ? this.state.searchedApplications.sort(this.compare) : this.data.reverse();
this.setState({data: datas, asc: !isAsc}); this.setState({searchedApplications: sortedData, asc: !isAsc});
} }
_compare(a, b) { compare(a, b) {
if (a.applicationName < b.applicationName) if (a.applicationName < b.applicationName)
return -1; return -1;
if (a.applicationName > b.applicationName) if (a.applicationName > b.applicationName)
@ -162,27 +164,33 @@ class ApplicationListing extends Component {
return 0; return 0;
} }
_onRowClick(id) { onRowClick(id) {
this.props.history.push("apps/"+id); ApplicationMgtApi.getApplication(id).then(response => {
console.log(response);
}).catch(err => {
console.log(err)
});
// this.props.history.push("apps/" + id);
} }
render() { render() {
return ( return (
<div className="middle applicationListingMiddle"> <div className="middle applicationListingMiddle">
<Card className="applicationListingCard"> <Card className="applicationListingCard">
<TextField hintText="Search" className="applicationListingSearch" <TextField
onChange={this._searchApplications.bind(this)}/> hintText="Search"
className="applicationListingSearch"
onChange={this.searchApplications}/>
<CardTitle title="Applications" className="applicationListTitle"/> <CardTitle title="Applications" className="applicationListTitle"/>
<CardActions> <DataTable
headers={this.headers}
</CardActions> data={this.state.searchedApplications}
<DataTable headers={this.headers} handleRowClick={this.onRowClick}
data={this.state.data} noDataMessage={{type: 'button', text: 'Create Application'}}
handleRowClick={this._onRowClick.bind(this)} />
noDataMessage={{type: 'button', text: 'Create Application'}}/>
</Card> </Card>
</div>
</div>); );
} }
} }

@ -16,12 +16,14 @@
* under the License. * under the License.
*/ */
import Theme from '../../../theme';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import MenuItem from 'material-ui/MenuItem'; import MenuItem from 'material-ui/MenuItem';
import SelectField from 'material-ui/SelectField'; import SelectField from 'material-ui/SelectField';
import AuthHandler from "../../../api/authHandler";
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import Theme from '../../../theme'; import PlatformMgtApi from "../../../api/platformMgtApi";
/** /**
* The first step of the application creation wizard. * The first step of the application creation wizard.
@ -39,11 +41,15 @@ import Theme from '../../../theme';
class Step1 extends Component { class Step1 extends Component {
constructor() { constructor() {
super(); super();
this.setPlatforms = this.setPlatforms.bind(this);
this.platforms = [];
this.state = { this.state = {
finished: false, finished: false,
stepIndex: 0, stepIndex: 0,
store: 1, store: 1,
platform: 1, platformSelectedIndex: 0,
platform: "",
platforms: [],
stepData: [], stepData: [],
title: "", title: "",
titleError: "" titleError: ""
@ -61,22 +67,40 @@ class Step1 extends Component {
componentWillUnmount() { componentWillUnmount() {
Theme.removeThemingScripts(this.scriptId); Theme.removeThemingScripts(this.scriptId);
} }
componentDidMount() {
//Get the list of available platforms and set to the state.
PlatformMgtApi.getPlatforms().then(response => {
console.log(response);
this.setPlatforms(response.data);
}).catch(err => {
AuthHandler.unauthorizedErrorHandler(err);
})
}
/** /**
* Invokes the handleNext function in Create component. * Extract the platforms from the response data and populate the state.
* @param platforms: The array returned as the response.
* */ * */
_handleNext = () => { setPlatforms(platforms) {
this.props.handleNext(); let tmpPlatforms = [];
}; for (let index in platforms) {
let platform = {};
platform = platforms[index];
tmpPlatforms.push(platform);
}
this.setState({platforms: tmpPlatforms, platformSelectedIndex: 0, platform: tmpPlatforms[0].identifier})
}
/** /**
* Persist the current form data to the state. * Persist the current form data to the state.
* */ * */
_setStepData() { setStepData() {
var step = { console.log("Platforms",this.state.platforms);
let step = {
store: this.state.store, store: this.state.store,
platform: this.state.platform platform: this.state.platforms[this.state.platformSelectedIndex]
}; };
console.log(step);
this.props.setData("step1", {step: step}); this.props.setData("step1", {step: step});
} }
@ -86,32 +110,25 @@ class Step1 extends Component {
* Sets the data to the state. * Sets the data to the state.
* Invokes the handleNext method of Create component. * Invokes the handleNext method of Create component.
* */ * */
_handleClick() { handleClick() {
this._setStepData(); this.setStepData();
} }
/** /**
* Triggers when changing the Platform selection. * Triggers when changing the Platform selection.
* */ * */
_onChangePlatform = (event, index, value) => { onChangePlatform(event, index, value) {
console.log(value); console.log(this.state.platforms[index]);
this.setState({platform: value}); this.setState({platform: this.state.platforms[index].identifier, platformSelectedIndex: index});
}; };
/** /**
* Triggers when changing the Store selection. * Triggers when changing the Store selection.
* */ * */
_onChangeStore = (event, index, value) => { onChangeStore(event, index, value) {
this.setState({store: value}); this.setState({store: value});
}; };
/**
* Triggers when user types on Title text field.
* */
_onChangeTitle = (event, value) => {
this.setState({title: value});
};
render() { render() {
return ( return (
<div> <div>
@ -122,30 +139,38 @@ class Step1 extends Component {
floatingLabelText="Store Type*" floatingLabelText="Store Type*"
value={this.state.store} value={this.state.store}
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this._onChangeStore.bind(this)} onChange={this.onChangeStore.bind(this)}
> >
<MenuItem value={1} primaryText="Enterprise"/> <MenuItem value={0} primaryText="Enterprise"/>
<MenuItem value={2} primaryText="Public"/> <MenuItem value={1} primaryText="Public"/>
</SelectField> <br/> </SelectField>
<br/>
<SelectField <SelectField
floatingLabelText="Platform*" floatingLabelText="Platform*"
value={this.state.platform} value={this.state.platform}
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this._onChangePlatform.bind(this)} onChange={this.onChangePlatform.bind(this)}
> >
<MenuItem value={1} primaryText="Android"/> {this.state.platforms.length > 0 ? this.state.platforms.map(platform => {
<MenuItem value={2} primaryText="iOS"/> return (
<MenuItem value={{name: "Web", id:3}} primaryText="Web"/> <MenuItem
key={Math.random()}
value={platform.identifier}
primaryText={platform.name}
/>
)
}) : <div/>}
</SelectField> </SelectField>
</div> </div>
<br/> <br/>
<br/> <br/>
<div className="nextButton"> <div className="nextButton">
<RaisedButton <RaisedButton
label="Next >" label="Next >"
primary={true} primary={true}
onClick={this._handleClick.bind(this)} onClick={this.handleClick.bind(this)}
/> />
</div> </div>
</div> </div>

@ -17,6 +17,7 @@
*/ */
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Theme from '../../../theme';
import Chip from 'material-ui/Chip'; import Chip from 'material-ui/Chip';
import Dropzone from 'react-dropzone'; import Dropzone from 'react-dropzone';
import React, {Component} from 'react'; import React, {Component} from 'react';
@ -28,7 +29,6 @@ import SelectField from 'material-ui/SelectField';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import Clear from 'material-ui/svg-icons/content/clear'; import Clear from 'material-ui/svg-icons/content/clear';
import {GridList, GridTile} from 'material-ui/GridList'; import {GridList, GridTile} from 'material-ui/GridList';
import Theme from '../../../theme';
/** /**
* The Second step of application create wizard. * The Second step of application create wizard.
@ -56,16 +56,17 @@ class Step2 extends Component {
super(); super();
this.state = { this.state = {
tags: [], tags: [],
icon: [],
title: "",
errors: {},
banner: [],
defValue: "", defValue: "",
category: 0, category: 0,
visibility: 0, visibility: 0,
errors: {},
title: "",
shortDescription: "",
description: "", description: "",
banner: [],
screenshots: [], screenshots: [],
icon: [] identifier: "",
shortDescription: ""
}; };
this.scriptId = "application-create-step2"; this.scriptId = "application-create-step2";
} }
@ -86,19 +87,19 @@ class Step2 extends Component {
* Clears the tags text field. * Clears the tags text field.
* Chip gets two parameters: Key and value. * Chip gets two parameters: Key and value.
* */ * */
_addTags(event) { addTags(event) {
let tags = this.state.tags; let tags = this.state.tags;
if (event.charCode === 13) { if (event.charCode === 13) {
event.preventDefault(); event.preventDefault();
tags.push({key: Math.floor(Math.random() * 1000), value: event.target.value}); tags.push({key: Math.floor(Math.random() * 1000), value: event.target.value});
this.setState({tags, defValue: ""}); this.setState({tags, defValue: ""}, console.log(tags));
} }
} }
/** /**
* Set the value for tag. * Set the value for tag.
* */ * */
_handleTagChange(event) { handleTagChange(event) {
let defaultValue = this.state.defValue; let defaultValue = this.state.defValue;
defaultValue = event.target.value; defaultValue = event.target.value;
this.setState({defValue: defaultValue}) this.setState({defValue: defaultValue})
@ -107,21 +108,21 @@ class Step2 extends Component {
/** /**
* Invokes the handleNext function in Create component. * Invokes the handleNext function in Create component.
* */ * */
_handleNext() { handleNext() {
let fields = [{name: "Title", value: this.state.title}, let fields = [{name: "Title", value: this.state.title},
{name: "Short Description", value: this.state.shortDescription}, {name: "Short Description", value: this.state.shortDescription},
{name: "Description", value: this.state.description}, {name: "Description", value: this.state.description},
{name: "Banner", value: this.state.banner}, {name: "Banner", value: this.state.banner},
{name: "Screenshots", value: this.state.screenshots}, {name: "Screenshots", value: this.state.screenshots},
{name: "Identifier", value: this.state.identifier},
{name: "Icon", value: this.state.icon}]; {name: "Icon", value: this.state.icon}];
this._validate(fields); this.validate(fields);
// this.props.handleNext();
} }
/** /**
* Invokes the handlePrev function in Create component. * Invokes the handlePrev function in Create component.
* */ * */
_handlePrev() { handlePrev() {
this.props.handlePrev(); this.props.handlePrev();
} }
@ -129,7 +130,7 @@ class Step2 extends Component {
* Handles Chip delete function. * Handles Chip delete function.
* Removes the tag from state.tags * Removes the tag from state.tags
* */ * */
_handleRequestDelete = (key) => { handleRequestDelete(key) {
this.chipData = this.state.tags; this.chipData = this.state.tags;
const chipToDelete = this.chipData.map((chip) => chip.key).indexOf(key); const chipToDelete = this.chipData.map((chip) => chip.key).indexOf(key);
this.chipData.splice(chipToDelete, 1); this.chipData.splice(chipToDelete, 1);
@ -139,18 +140,18 @@ class Step2 extends Component {
/** /**
* Creates Chip array from state.tags. * Creates Chip array from state.tags.
* */ * */
_renderChip(data) { renderChip(data) {
return ( return (
<Chip <Chip
key={data.key} key={data.key}
onRequestDelete={() => this._handleRequestDelete(data.key)} onRequestDelete={() => this.handleRequestDelete(data.key)}
className="applicationCreateChip"> className="applicationCreateChip">
{data.value} {data.value}
</Chip> </Chip>
); );
} }
_onVisibilitySelect = (event, index, value) => { onVisibilitySelect(event, index, value) {
console.log(value); console.log(value);
let comp = <SelectField> <MenuItem value={0} primaryText="Public"/> let comp = <SelectField> <MenuItem value={0} primaryText="Public"/>
<MenuItem value={1} primaryText="Roles"/> <MenuItem value={1} primaryText="Roles"/>
@ -167,7 +168,7 @@ class Step2 extends Component {
/** /**
* Validate the form. * Validate the form.
* */ * */
_validate(fields) { validate(fields) {
let errors = {}; let errors = {};
let errorsPresent = false; let errorsPresent = false;
fields.forEach(function (field) { fields.forEach(function (field) {
@ -181,6 +182,15 @@ class Step2 extends Component {
} }
break; break;
} }
case 'Identifier': {
if (field.value === "") {
errors[field.name] = field.name + " is required!";
errorsPresent = true;
} else {
errorsPresent = false;
}
break;
}
case 'Short Description': { case 'Short Description': {
if (field.value === "") { if (field.value === "") {
errors[field.name] = field.name + " is required!"; errors[field.name] = field.name + " is required!";
@ -219,7 +229,7 @@ class Step2 extends Component {
} }
case 'Screenshots': { case 'Screenshots': {
if (field.value.length < 3) { if (field.value.length < 3) {
errors[field.name] = "3 " +field.name + " are required!"; errors[field.name] = "3 " + field.name + " are required!";
errorsPresent = true; errorsPresent = true;
} else { } else {
errorsPresent = false; errorsPresent = false;
@ -229,40 +239,40 @@ class Step2 extends Component {
} }
}); });
console.log(errorsPresent);
if (!errorsPresent) { if (!errorsPresent) {
this._setStepData(); this.setStepData();
} else { } else {
this.setState({errors: errors}, console.log(errors)); this.setState({errors: errors}, console.log(errors));
} }
} }
/** /**
* Creates an object with the current step data and persist in the parent. * Creates an object with the current step data and persist in the parent.
* */ * */
_setStepData() { setStepData() {
let stepData = { let stepData = {
title: this.state.title, icon: this.state.icon,
description: this.state.description, name: this.state.name,
shortDescription: this.state.shortDescription,
tags: this.state.tags, tags: this.state.tags,
banner: this.state.banner, banner: this.state.banner,
category: this.categories[this.state.category],
identifier: this.state.identifier,
screenshots: this.state.screenshots, screenshots: this.state.screenshots,
icon: this.state.icon description: this.state.description,
shortDescription: this.state.shortDescription
}; };
this.props.setData("step2", {step: stepData}); this.props.setData("step2", {step: stepData});
} };
/** /**
* Set text field values to state. * Set text field values to state.
* */ * */
_onTextFieldChange(event, value) { onTextFieldChange(event, value) {
let field = event.target.id; let field = event.target.id;
switch (field) { switch (field) {
case "title": { case "name": {
this.setState({title: value}); this.setState({name: value});
break; break;
} }
case "shortDescription": { case "shortDescription": {
@ -273,30 +283,34 @@ class Step2 extends Component {
this.setState({description: value}); this.setState({description: value});
break; break;
} }
case "identifier": {
this.setState({identifier: value});
break;
} }
} }
};
/** /**
* Removed user uploaded banner. * Removed user uploaded banner.
* */ * */
_removeBanner(event, d) { removeBanner(event, d) {
console.log(event, d); console.log(event, d);
this.setState({banner: []}); this.setState({banner: []});
} };
/** /**
* Removes uploaded icon. * Removes uploaded icon.
* */ * */
_removeIcon(event) { removeIcon(event) {
this.setState({icon: []}); this.setState({icon: []});
} };
/** /**
* Removes selected screenshot. * Removes selected screenshot.
* */ * */
_removeScreenshot(event) { removeScreenshot(event) {
console.log(event.target) console.log(event.target)
} };
render() { render() {
console.log(this.state.visibilityComponent); console.log(this.state.visibilityComponent);
@ -305,13 +319,23 @@ class Step2 extends Component {
<div> <div>
<div> <div>
<TextField <TextField
id="title" id="name"
hintText="Enter a title for your application." hintText="Enter a name for your application."
errorText={this.state.errors["Title"]} errorText={this.state.errors["Title"]}
floatingLabelText="Title*" floatingLabelText="Name*"
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this._onTextFieldChange.bind(this)} onChange={this.onTextFieldChange.bind(this)}
/><br/> />
<br/>
<TextField
id="identifier"
hintText="Unique Identifier for Application."
errorText={this.state.errors["Identifier"]}
floatingLabelText="Identifier*"
floatingLabelFixed={true}
onChange={this.onTextFieldChange.bind(this)}
/>
<br/>
<TextField <TextField
id="shortDescription" id="shortDescription"
hintText="Enter a short description for your application." hintText="Enter a short description for your application."
@ -320,9 +344,9 @@ class Step2 extends Component {
floatingLabelFixed={true} floatingLabelFixed={true}
multiLine={true} multiLine={true}
rows={2} rows={2}
onChange={this._onTextFieldChange.bind(this)} onChange={this.onTextFieldChange.bind(this)}
/>
/><br/> <br/>
<TextField <TextField
id="description" id="description"
errorText={this.state.errors["Description"]} errorText={this.state.errors["Description"]}
@ -331,30 +355,33 @@ class Step2 extends Component {
floatingLabelFixed={true} floatingLabelFixed={true}
multiLine={true} multiLine={true}
rows={4} rows={4}
onChange={this._onTextFieldChange.bind(this)} onChange={this.onTextFieldChange.bind(this)}
/><br/> />
<br/>
<SelectField <SelectField
floatingLabelText="Visibility*" floatingLabelText="Visibility*"
value={this.state.visibility} value={this.state.visibility}
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this._onVisibilitySelect.bind(this)} onChange={this.onVisibilitySelect.bind(this)}
> >
<MenuItem value={0} primaryText="Public"/> <MenuItem value={0} primaryText="Public"/>
<MenuItem value={1} primaryText="Roles"/> <MenuItem value={1} primaryText="Roles"/>
<MenuItem value={2} primaryText="Devices"/> <MenuItem value={2} primaryText="Devices"/>
</SelectField><br/> </SelectField>
<br/>
<TextField <TextField
id="tags" id="tags"
errorText={this.state.errors["tags"]} errorText={this.state.errors["tags"]}
hintText="Enter application tags.." hintText="Enter Application tags.."
floatingLabelText="Tags*" floatingLabelText="Tags*"
floatingLabelFixed={true} floatingLabelFixed={true}
value={this.state.defValue} value={this.state.defValue}
onChange={this._handleTagChange.bind(this)} onChange={this.handleTagChange.bind(this)}
onKeyPress={this._addTags.bind(this)} onKeyPress={this.addTags.bind(this)}
/><br/> />
<br/>
<div className="applicationCreateWrapper"> <div className="applicationCreateWrapper">
{this.state.tags.map(this._renderChip, this)} {this.state.tags.map(this.renderChip, this)}
</div> </div>
<br/> <br/>
<SelectField <SelectField
@ -363,7 +390,8 @@ class Step2 extends Component {
floatingLabelFixed={true} floatingLabelFixed={true}
> >
<MenuItem value={0} primaryText="Business"/> <MenuItem value={0} primaryText="Business"/>
</SelectField> <br/> </SelectField>
<br/>
{/*Platform Specific Properties.*/} {/*Platform Specific Properties.*/}
<div className="platformSpecificPropertyDiv"> <div className="platformSpecificPropertyDiv">
<p className="platformSpecificPropertyP">Platform Specific Properties</p> <p className="platformSpecificPropertyP">Platform Specific Properties</p>
@ -374,50 +402,58 @@ class Step2 extends Component {
<p className="applicationCreateBannerTitle">Banner*:</p> <p className="applicationCreateBannerTitle">Banner*:</p>
<GridList className="applicationCreateGrid" cols={1.1}> <GridList className="applicationCreateGrid" cols={1.1}>
{this.state.banner.map((tile) => ( {this.state.banner.map((tile) => (
<GridTile key={Math.floor(Math.random() * 1000)} <GridTile
key={Math.floor(Math.random() * 1000)}
title={tile.name} title={tile.name}
actionIcon={ actionIcon={
<IconButton onClick={this._removeBanner.bind(this)}> <IconButton onClick={this.removeBanner.bind(this)}>
<Clear /> <Clear/>
</IconButton>}> </IconButton>}>
<img src={tile.preview}/></GridTile> <img src={tile.preview}/>
</GridTile>
))} ))}
{this.state.banner.length === 0 ? {this.state.banner.length === 0 ?
<Dropzone className="applicationCreateBannerDropZone" accept="image/jpeg, image/png" <Dropzone
className="applicationCreateBannerDropZone"
accept="image/jpeg, image/png"
onDrop={(banner, rejected) => { onDrop={(banner, rejected) => {
this.setState({banner, rejected}); this.setState({banner, rejected});
}}> }}
>
<p className="applicationCreateBannerp">+</p> <p className="applicationCreateBannerp">+</p>
</Dropzone> : <div />} </Dropzone> : <div/>
}
</GridList> </GridList>
</div> </div>
<br/> <br/>
<div> <div>
<p className="applicationCreateScreenshotError">{this.state.errors["Screenshots"]}</p> <p className="applicationCreateScreenshotError">{this.state.errors["Screenshots"]}</p>
<p className="applicationCreateScreenshotTitle">Screenshots*:</p> <p className="applicationCreateScreenshotTitle">Screenshots*:</p>
<GridList className = "applicationCreateScreenshotGrid" cols={1.1}> <GridList className="applicationCreateScreenshotGrid" cols={1.1}>
{this.state.screenshots.map((file) => ( {this.state.screenshots.map((file) => (
<GridTile key={Math.floor(Math.random() * 1000)} <GridTile
key={Math.floor(Math.random() * 1000)}
title={file[0].name} title={file[0].name}
actionIcon={ actionIcon={
<IconButton onClick={this._removeScreenshot.bind(this)}> <IconButton onClick={this.removeScreenshot.bind(this)}>
<Clear/> <Clear/>
</IconButton>}> </IconButton>}>
<img src={file[0].preview}/></GridTile> <img src={file[0].preview}/></GridTile>
))} ))}
{this.state.screenshots.length < 3 ? {this.state.screenshots.length < 3 ?
<Dropzone className="applicationCreateScreenshotDropZone" <Dropzone
className="applicationCreateScreenshotDropZone"
accept="image/jpeg, image/png" accept="image/jpeg, image/png"
onDrop={(screenshots, rejected) => { onDrop={(screenshots, rejected) => {
let tmpScreenshots = this.state.screenshots; let tmpScreenshots = this.state.screenshots;
tmpScreenshots.push(screenshots); tmpScreenshots.push(screenshots);
this.setState({ this.setState({
screenshots: tmpScreenshots}); screenshots: tmpScreenshots
}}> });
}}
>
<p className="applicationCreateScreenshotp">+</p> <p className="applicationCreateScreenshotp">+</p>
</Dropzone> : <div />} </Dropzone> : <div/>}
</GridList> </GridList>
</div> </div>
<br/> <br/>
@ -426,38 +462,43 @@ class Step2 extends Component {
<p className="applicationCreateIconTitle">Icon*:</p> <p className="applicationCreateIconTitle">Icon*:</p>
<GridList className="applicationCreateIconGrid" cols={1.1}> <GridList className="applicationCreateIconGrid" cols={1.1}>
{this.state.icon.map((tile) => ( {this.state.icon.map((tile) => (
<GridTile key={Math.floor(Math.random() * 1000)} <GridTile
key={Math.floor(Math.random() * 1000)}
title={tile.name} title={tile.name}
actionIcon={ actionIcon={
<IconButton onClick={this._removeIcon.bind(this)}> <IconButton onClick={this.removeIcon.bind(this)}>
<Clear /> <Clear/>
</IconButton>}> </IconButton>}>
<img src={tile.preview}/></GridTile> <img src={tile.preview}/>
</GridTile>
))} ))}
{this.state.icon.length === 0 ? {this.state.icon.length === 0 ?
<Dropzone className="applicationCreateIconDropZone" <Dropzone
className="applicationCreateIconDropZone"
accept="image/jpeg, image/png" accept="image/jpeg, image/png"
onDrop={(icon, rejected) => {this.setState({icon, rejected});}}> onDrop={(icon, rejected) => {
this.setState({icon, rejected});
}}
>
<p className="applicationCreateIconp">+</p> <p className="applicationCreateIconp">+</p>
</Dropzone> : <div />} </Dropzone> : <div/>}
</GridList> </GridList>
</div> </div>
<br/> <br/>
</div> </div>
<br/> <br/>
<br/> <br/>
<div className="applicationCreateBackAndNext"> <div className="applicationCreateBackAndNext">
<FlatButton <FlatButton
label="< Back" label="< Back"
disabled={false} disabled={false}
onClick={this._handlePrev.bind(this)} onClick={this.handlePrev.bind(this)}
className="applicationCreateBack" style={{marginRight: 12}}
/> />
<RaisedButton <RaisedButton
label="Next >" label="Next >"
primary={true} primary={true}
onClick={this._handleNext.bind(this)} onClick={this.handleNext.bind(this)}
/> />
</div> </div>
</div> </div>

@ -51,6 +51,10 @@ import Theme from '../../../theme';
class Step3 extends Component { class Step3 extends Component {
constructor() { constructor() {
super(); super();
this.handleToggle = this.handleToggle.bind(this);
this.handlePrev = this.handlePrev.bind(this);
this.handleToggle = this.handleToggle.bind(this);
this.handleFinish = this.handleFinish.bind(this);
this.state = { this.state = {
showForm: false, showForm: false,
releaseChannel: 1, releaseChannel: 1,
@ -74,21 +78,21 @@ class Step3 extends Component {
* Handles finish button click. * Handles finish button click.
* This invokes handleNext function in parent component. * This invokes handleNext function in parent component.
* */ * */
_handleFinish() { handleFinish() {
this.props.handleFinish(); this.props.handleFinish();
} }
/** /**
* Invokes Prev button click. * Invokes Prev button click.
* */ * */
_handlePrev() { handlePrev() {
this.props.handlePrev(); this.props.handlePrev();
} }
/** /**
* Handles release application selection. * Handles release application selection.
* */ * */
_handleToggle() { handleToggle() {
let hide = this.state.showForm; let hide = this.state.showForm;
this.setState({showForm: !hide}); this.setState({showForm: !hide});
} }
@ -100,11 +104,12 @@ class Step3 extends Component {
<Toggle <Toggle
label="Release the Application" label="Release the Application"
labelPosition="right" labelPosition="right"
onToggle={this._handleToggle.bind(this)} onToggle={this.handleToggle}
defaultToggled={this.state.showForm} defaultToggled={this.state.showForm}
/> />
{/*If toggle is true, the release form will be shown.*/} {/*If toggle is true, the release form will be shown.*/}
{!this.state.showForm ? <div/> : <div> {!this.state.showForm ? <div/> :
<div>
<SelectField <SelectField
floatingLabelText="Select Release Channel*" floatingLabelText="Select Release Channel*"
value={this.state.releaseChannel} value={this.state.releaseChannel}
@ -113,7 +118,8 @@ class Step3 extends Component {
<MenuItem value={1} primaryText="Alpha"/> <MenuItem value={1} primaryText="Alpha"/>
<MenuItem value={2} primaryText="Beta"/> <MenuItem value={2} primaryText="Beta"/>
<MenuItem value={3} primaryText="GA"/> <MenuItem value={3} primaryText="GA"/>
</SelectField> <br/> </SelectField>
<br/>
<TextField <TextField
hintText="1.0.0" hintText="1.0.0"
floatingLabelText="Version*" floatingLabelText="Version*"
@ -121,15 +127,19 @@ class Step3 extends Component {
floatingLabelFixed={true} floatingLabelFixed={true}
/><br/> /><br/>
</div>} </div>}
<div className="applicationCreateBackAndFinish"> <div className="applicationCreateBackAndFinish">
<FlatButton label="< Back" disabled={false} onClick={this._handlePrev.bind(this)} <FlatButton
className="applicationCreateFinish"/> label="< Back"
disabled={false}
onClick={this.handlePrev}
className="applicationCreateFinish"
/>
<RaisedButton <RaisedButton
label="Finish" label="Finish"
primary={true} primary={true}
onClick={this._handleFinish.bind(this)} onClick={this.handleFinish}
/> />
</div> </div>
</div> </div>
</div> </div>

@ -16,12 +16,17 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Badge from 'material-ui/Badge'; import Badge from 'material-ui/Badge';
import React, {Component} from 'react'; import React, {Component} from 'react';
import AppBar from 'material-ui/AppBar'; import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer'; import Drawer from 'material-ui/Drawer';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import AuthHandler from "../../api/authHandler";
import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton'; import IconButton from 'material-ui/IconButton';
import {List, ListItem} from 'material-ui/List'; import {List, ListItem} from 'material-ui/List';
import Apps from 'material-ui/svg-icons/navigation/apps'; import Apps from 'material-ui/svg-icons/navigation/apps';
@ -30,7 +35,6 @@ import Feedback from 'material-ui/svg-icons/action/feedback';
import DevicesOther from 'material-ui/svg-icons/hardware/devices-other'; import DevicesOther from 'material-ui/svg-icons/hardware/devices-other';
import NotificationsIcon from 'material-ui/svg-icons/social/notifications'; import NotificationsIcon from 'material-ui/svg-icons/social/notifications';
import ActionAccountCircle from 'material-ui/svg-icons/action/account-circle'; import ActionAccountCircle from 'material-ui/svg-icons/action/account-circle';
import Theme from '../../theme';
/** /**
@ -48,6 +52,7 @@ class BaseLayout extends Component {
user: 'Admin' user: 'Admin'
}; };
this.scriptId = "basic-layout"; this.scriptId = "basic-layout";
this.logout = this.logout.bind(this);
} }
componentWillMount() { componentWillMount() {
@ -93,11 +98,16 @@ class BaseLayout extends Component {
this.props.history.push(to); this.props.history.push(to);
} }
logout(event, index, value) {
AuthHandler.logout();
}
render() { render() {
return ( return (
<div> <div>
<AppBar title="App Publisher" <AppBar
title="App Publisher"
iconElementRight={ iconElementRight={
<div> <div>
<Badge <Badge
@ -109,18 +119,30 @@ class BaseLayout extends Component {
<NotificationsIcon/> <NotificationsIcon/>
</IconButton> </IconButton>
</Badge> </Badge>
<IconButton onClick={() => { <IconMenu
console.log("Clicked") iconButtonElement={<FlatButton
}}> icon={<ActionAccountCircle/>}
<ActionAccountCircle/> label="sdfdsf"
</IconButton> />}
anchorOrigin={{horizontal: 'left', vertical: 'top'}}
targetOrigin={{horizontal: 'left', vertical: 'top'}}
onChange={this.logout}
>
<MenuItem value={0} primaryText="Logout" />
</IconMenu>
{/*<FlatButton*/}
{/*icon={<ActionAccountCircle/>}*/}
{/*onClick={() => {console.log("Clicked")}}*/}
{/*label={this.props.user.getUserName()}*/}
{/*/>*/}
</div> </div>
} }
/> />
<div> <div>
<Drawer containerStyle={{height: 'calc(100% - 64px)', width: '15%', top: '10%'}} open={true}> <Drawer containerStyle={{height: 'calc(100% - 64px)', width: '15%', top: '10%'}} open={true}>
<List> <List>
<ListItem primaryText="Applications" <ListItem
primaryText="Applications"
leftIcon={<Apps/>} leftIcon={<Apps/>}
initiallyOpen={false} initiallyOpen={false}
primaryTogglesNestedList={true} primaryTogglesNestedList={true}
@ -131,9 +153,11 @@ class BaseLayout extends Component {
primaryText="Create" primaryText="Create"
onClick={this.handleApplicationCreateClick.bind(this)} onClick={this.handleApplicationCreateClick.bind(this)}
leftIcon={<Add/>} leftIcon={<Add/>}
/>]}
/> />
<ListItem primaryText="Platforms" ]}
/>
<ListItem
primaryText="Platforms"
leftIcon={<DevicesOther/>} leftIcon={<DevicesOther/>}
initiallyOpen={false} initiallyOpen={false}
primaryTogglesNestedList={true} primaryTogglesNestedList={true}
@ -144,20 +168,23 @@ class BaseLayout extends Component {
primaryText="Create" primaryText="Create"
onClick={this.handlePlatformCreateClick.bind(this)} onClick={this.handlePlatformCreateClick.bind(this)}
leftIcon={<Add/>} leftIcon={<Add/>}
/>]}
/> />
<ListItem primaryText="Reviews" ]}
/>
<ListItem
primaryText="Reviews"
onClick={this.handleReviewClick.bind(this)} onClick={this.handleReviewClick.bind(this)}
leftIcon={<Feedback/>}/> leftIcon={<Feedback/>}
/>
</List> </List>
</Drawer> </Drawer>
</div> </div>
<div className="basicLayoutDiv"> <div className="basicLayoutDiv">
{this.props.children} {this.props.children}
</div> </div>
</div>); </div>
);
} }
} }
BaseLayout.propTypes = { BaseLayout.propTypes = {

@ -1,49 +0,0 @@
/*
* 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.
*/
import React, {Component} from 'react';
import DataTable from '../UIComponents/DataTable';
/**
*
* ***NEW***
* The Publisher overview component.
* This component could be used to view app analytics.
* i.e number of overall downloads, ratings ect.
* */
class PublisherOverview extends Component {
constructor() {
super();
}
componentWillMount() {
}
render() {
return (
<div>
Overview
</div>
);
}
}
export default PublisherOverview;

@ -16,7 +16,9 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Chip from 'material-ui/Chip';
import Dropzone from 'react-dropzone'; import Dropzone from 'react-dropzone';
import React, {Component} from 'react'; import React, {Component} from 'react';
import Toggle from 'material-ui/Toggle'; import Toggle from 'material-ui/Toggle';
@ -26,12 +28,12 @@ import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton'; import IconButton from 'material-ui/IconButton';
import SelectField from 'material-ui/SelectField'; import SelectField from 'material-ui/SelectField';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import PlatformMgtApi from '../../api/platformMgtApi';
import Clear from 'material-ui/svg-icons/content/clear'; import Clear from 'material-ui/svg-icons/content/clear';
import {GridList, GridTile} from 'material-ui/GridList'; import {GridList, GridTile} from 'material-ui/GridList';
import Close from 'material-ui/svg-icons/navigation/close'; import Close from 'material-ui/svg-icons/navigation/close';
import {Card, CardActions, CardTitle} from 'material-ui/Card'; import {Card, CardActions, CardTitle} from 'material-ui/Card';
import AddCircleOutline from 'material-ui/svg-icons/content/add-circle-outline'; import AddCircleOutline from 'material-ui/svg-icons/content/add-circle-outline';
import Theme from '../../theme';
/** /**
* Platform Create component. * Platform Create component.
@ -47,7 +49,20 @@ class PlatformCreate extends Component {
constructor() { constructor() {
super(); super();
this.onCreatePlatform = this.onCreatePlatform.bind(this);
this.handleToggle = this.handleToggle.bind(this);
this.addProperty = this.addProperty.bind(this);
this.addTags = this.addTags.bind(this);
this.clearForm = this.clearForm.bind(this);
this.onPropertySelect = this.onPropertySelect.bind(this);
this.handleTagChange = this.handleTagChange.bind(this);
this.removeIcon = this.removeIcon.bind(this);
this.onTextChange = this.onTextChange.bind(this);
this.renderChip = this.renderChip.bind(this);
this.removeProperty = this.removeProperty.bind(this);
this.state = { this.state = {
tags: [],
defValue: "",
enabled: true, enabled: true,
allTenants: false, allTenants: false,
files: [], files: [],
@ -57,6 +72,7 @@ class PlatformCreate extends Component {
description: "", description: "",
property: "", property: "",
icon: [], icon: [],
identifier: "",
propertyTypes: [ propertyTypes: [
{key: 0, value: 'String'}, {key: 0, value: 'String'},
{key: 1, value: 'Number'}, {key: 1, value: 'Number'},
@ -81,7 +97,7 @@ class PlatformCreate extends Component {
* Handles toggle button actions. * Handles toggle button actions.
* One method is used for all the toggle buttons and, each toggle is identified by the id. * One method is used for all the toggle buttons and, each toggle is identified by the id.
* */ * */
_handleToggle(event) { handleToggle(event) {
switch (event.target.id) { switch (event.target.id) {
case "enabled" : { case "enabled" : {
let enabled = this.state.enabled; let enabled = this.state.enabled;
@ -99,15 +115,64 @@ class PlatformCreate extends Component {
/** /**
* Triggers the onChange action on property type selection. * Triggers the onChange action on property type selection.
* */ * */
_onPropertySelect = (event, index, value) => { onPropertySelect(event, index, value) {
console.log(this.state.propertyTypes[value]); console.log(this.state.propertyTypes[value]);
this.setState({selectedProperty: value}); this.setState({selectedProperty: value});
}; }
/**
* Handles Chip delete function.
* Removes the tag from state.tags
* */
handleTagDelete(key) {
this.chipData = this.state.tags;
const chipToDelete = this.chipData.map((chip) => chip.key).indexOf(key);
this.chipData.splice(chipToDelete, 1);
this.setState({tags: this.chipData});
}
/**
* Create a tag on Enter key press and set it to the state.
* Clears the tags text field.
* Chip gets two parameters: Key and value.
* */
addTags(event) {
let tags = this.state.tags;
if (event.charCode === 13) {
event.preventDefault();
tags.push({key: Math.floor(Math.random() * 1000), value: event.target.value});
this.setState({tags, defValue: ""});
}
}
/**
* Creates Chip array from state.tags.
* */
renderChip(data) {
return (
<Chip
key={data.key}
onRequestDelete={() => this.handleTagDelete(data.key)}
style={this.styles.chip}
>
{data.value}
</Chip>
);
}
/**
* Set the value for tag.
* */
handleTagChange(event) {
let defaultValue = this.state.defValue;
defaultValue = event.target.value;
this.setState({defValue: defaultValue})
}
/** /**
* Remove the selected property from the property list. * Remove the selected property from the property list.
* */ * */
_removeProperty(property) { removeProperty(property) {
let properties = this.state.platformProperties; let properties = this.state.platformProperties;
properties.splice(properties.indexOf(property), 1); properties.splice(properties.indexOf(property), 1);
this.setState({platformProperties: properties}); this.setState({platformProperties: properties});
@ -116,28 +181,31 @@ class PlatformCreate extends Component {
/** /**
* Add a new platform property. * Add a new platform property.
* */ * */
_addProperty() { addProperty() {
let property = this.state.property; let property = this.state.property;
let selected = this.state.selectedProperty; let selected = this.state.selectedProperty;
this.setState({platformProperties: this.setState({
platformProperties:
this.state.platformProperties.concat([ this.state.platformProperties.concat([
{ {
key: property, key: property,
value: this.state.propertyTypes[selected].value value: this.state.propertyTypes[selected].value
}]), }]),
property: "", property: "",
selectedProperty: 0}); selectedProperty: 0
});
} }
/** /**
* Triggers in onChange event of text fields. * Triggers in onChange event of text fields.
* Text fields are identified by their ids and the value will be persisted in the component state. * Text fields are identified by their ids and the value will be persisted in the component state.
* */ * */
_onTextChange = (event, value) => { onTextChange(event, value) {
let property = this.state.property; let property = this.state.property;
let name = this.state.name; let name = this.state.name;
let description = this.state.description; let description = this.state.description;
let identifier = this.state.identifier;
switch (event.target.id) { switch (event.target.id) {
case "name": { case "name": {
@ -157,32 +225,57 @@ class PlatformCreate extends Component {
this.setState({property: property}); this.setState({property: property});
break; break;
} }
case "identifier": {
identifier = value;
this.setState({identifier: identifier});
}
} }
}; };
_onCreatePlatform() { /**
* Create platform object and call the create platform api.
* */
onCreatePlatform(event) {
//Call the platform create api.
event.preventDefault();
let platform = {};
platform.identifier = this.state.identifier;
platform.name = this.state.name;
platform.description = this.state.description;
platform.tags = this.state.tags;
platform.properties = this.state.platformProperties;
platform.icon = this.state.icon;
platform.enabled = this.state.enabled;
platform.allTenants = this.state.allTenants;
platform.defaultTenantMapping = true;
PlatformMgtApi.createPlatform(platform);
} }
/** /**
* Remove the uploaded icon. * Remove the uploaded icon.
* */ * */
_removeIcon(event) { removeIcon(event) {
event.preventDefault();
this.setState({icon: []}); this.setState({icon: []});
} }
/** /**
* Clears the user entered values in the form. * Clears the user entered values in the form.
* */ * */
_clearForm() { clearForm(event) {
this.setState({enabled: true, event.preventDefault();
this.setState({
enabled: true,
allTenants: false, allTenants: false,
files: [], files: [],
platformProperties: [], platformProperties: [],
selectedProperty: 0, selectedProperty: 0,
name: "", name: "",
description: "", description: "",
property: "",}) property: "",
})
} }
render() { render() {
@ -193,25 +286,38 @@ class PlatformCreate extends Component {
selectedProperty, selectedProperty,
propertyTypes, propertyTypes,
name, name,
tags,
defValue,
description, description,
property} = this.state; identifier,
property
} = this.state;
return ( return (
<div className="middle createplatformmiddle"> <div className="middle createplatformmiddle">
<Card> <Card>
<CardTitle title="Create Platform"/> <CardTitle title="Create Platform"/>
<CardActions> <CardActions>
<div className="createplatformcardaction"> <div className="createplatformcardaction">
<form> <form>
<TextField
hintText="Unique Identifier for Platform."
id="identifier"
floatingLabelText="Identifier*"
floatingLabelFixed={true}
value={identifier}
onChange={this.onTextChange}
/>
<br/>
<TextField <TextField
hintText="Enter the Platform Name." hintText="Enter the Platform Name."
id="name" id="name"
floatingLabelText="Name*" floatingLabelText="Name*"
floatingLabelFixed={true} floatingLabelFixed={true}
value={name} value={name}
onChange={this._onTextChange.bind(this)} onChange={this.onTextChange}
/><br/> />
<br/>
<TextField <TextField
id="description" id="description"
hintText="Enter the Platform Description." hintText="Enter the Platform Description."
@ -220,28 +326,46 @@ class PlatformCreate extends Component {
multiLine={true} multiLine={true}
rows={2} rows={2}
value={description} value={description}
onChange={this._onTextChange.bind(this)} onChange={this.onTextChange}
/><br/><br/> />
<br/>
<br/>
<Toggle <Toggle
id="tenant" id="tenant"
label="Shared with all Tenants" label="Shared with all Tenants"
labelPosition="right" labelPosition="right"
onToggle={this._handleToggle.bind(this)} onToggle={this.handleToggle}
toggled={allTenants} toggled={allTenants}
/> <br/> />
<br/>
<Toggle <Toggle
id="enabled" id="enabled"
label="Enabled" label="Enabled"
labelPosition="right" labelPosition="right"
onToggle={this._handleToggle.bind(this)} onToggle={this.handleToggle}
toggled={enabled} toggled={enabled}
/> <br/> />
<br/>
<TextField
id="tags"
hintText="Enter Platform tags.."
floatingLabelText="Tags*"
floatingLabelFixed={true}
value={defValue}
onChange={this.handleTagChange}
onKeyPress={this.addTags}
/>
<br/>
<div className="createPlatformTagWrapper">
{tags.map(this.renderChip, this)}
</div>
<br/>
<div> <div>
<p className="createplatformproperties">Platform Properties</p> <p className="createplatformproperties">Platform Properties</p>
<div id="property-container"> <div id="property-container">
{platformProperties.map((p) => { {platformProperties.map((p) => {
return <div key={p.key}>{p.key} : {p.value} return <div key={p.key}>{p.key} : {p.value}
<IconButton onClick={this._removeProperty.bind(this, p)}> <IconButton onClick={this.removeProperty.bind(this, p)}>
<Close className="createplatformpropertyclose"/> <Close className="createplatformpropertyclose"/>
</IconButton> </IconButton>
</div> </div>
@ -254,21 +378,21 @@ class PlatformCreate extends Component {
floatingLabelText="Platform Property*" floatingLabelText="Platform Property*"
floatingLabelFixed={true} floatingLabelFixed={true}
value={this.state.property} value={this.state.property}
onChange={this._onTextChange.bind(this)} onChange={this.onTextChange}
/> <em/> /> <em/>
<SelectField <SelectField
className="createplatformpropertyselect" className="createplatformpropertyselect"
floatingLabelText="Property Type" floatingLabelText="Property Type"
value={selectedProperty} value={selectedProperty}
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this._onPropertySelect.bind(this)}> onChange={this.onPropertySelect}>
{propertyTypes.map((type) => { {propertyTypes.map((type) => {
return <MenuItem key={type.key} return <MenuItem key={type.key}
value={type.key} value={type.key}
primaryText={type.value}/> primaryText={type.value}/>
})} })}
</SelectField> </SelectField>
<IconButton onClick={this._addProperty.bind(this)}> <IconButton onClick={this.addProperty}>
<AddCircleOutline/> <AddCircleOutline/>
</IconButton> </IconButton>
<br/> <br/>
@ -278,27 +402,33 @@ class PlatformCreate extends Component {
<p className="createplatformiconp">Platform Icon*:</p> <p className="createplatformiconp">Platform Icon*:</p>
<GridList className="createplatformicon" cols={1.1}> <GridList className="createplatformicon" cols={1.1}>
{this.state.icon.map((tile) => ( {this.state.icon.map((tile) => (
<GridTile key={Math.floor(Math.random() * 1000)} <GridTile
key={Math.floor(Math.random() * 1000)}
title={tile.name} title={tile.name}
actionIcon={ actionIcon={
<IconButton onClick={this._removeIcon.bind(this)}> <IconButton onClick={this.removeIcon}>
<Clear /> <Clear/>
</IconButton>}> </IconButton>}>
<img src={tile.preview}/> <img src={tile.preview}/>
</GridTile> </GridTile>
))} ))}
{this.state.icon.length === 0 ? {this.state.icon.length === 0 ?
<Dropzone className="createplatformdropzone" <Dropzone
className="createplatformdropzone"
accept="image/jpeg, image/png" accept="image/jpeg, image/png"
onDrop={(icon, rejected) => {this.setState({icon, rejected})}}> onDrop={(icon, rejected) => {
this.setState({icon, rejected})
}}
>
<p className="createplatformdropzonep">+</p> <p className="createplatformdropzonep">+</p>
</Dropzone> : <div />} </Dropzone> : <div/>}
</GridList> </GridList>
</div> </div>
<br/> <br/>
<RaisedButton primary={true} label="Create" <RaisedButton
onClick={this._onCreatePlatform.bind(this)}/> primary={true} label="Create"
<FlatButton label="Cancel" onClick={this._clearForm.bind(this)}/> onClick={this.onCreatePlatform}/>
<FlatButton label="Cancel" onClick={this.clearForm}/>
</form> </form>
</div> </div>
</CardActions> </CardActions>
@ -308,7 +438,6 @@ class PlatformCreate extends Component {
} }
} }
PlatformCreate.prototypes = { PlatformCreate.prototypes = {};
};
export default PlatformCreate; export default PlatformCreate;

@ -16,12 +16,12 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import React, {Component} from 'react'; import React, {Component} from 'react';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import TextField from 'material-ui/TextField'; import TextField from 'material-ui/TextField';
import DataTable from '../UIComponents/DataTable'; import DataTable from '../UIComponents/DataTable';
import {Card, CardActions, CardTitle} from 'material-ui/Card'; import {Card, CardActions, CardTitle} from 'material-ui/Card';
import Theme from '../../theme';
/** /**
* The App Create Component. * The App Create Component.
@ -56,7 +56,7 @@ class PlatformListing extends Component {
* Handles the search action. * Handles the search action.
* When typing in the search bar, this method will be invoked. * When typing in the search bar, this method will be invoked.
* */ * */
_searchApplications(word) { searchApplications(word) {
let searchedData = []; let searchedData = [];
} }
@ -64,13 +64,13 @@ class PlatformListing extends Component {
* Handles sort data function and toggles the asc state. * Handles sort data function and toggles the asc state.
* asc: true : sort in ascending order. * asc: true : sort in ascending order.
* */ * */
_sortData() { sortData() {
let isAsc = this.state.asc; let isAsc = this.state.asc;
let datas = isAsc?this.data.sort(this._compare):this.data.reverse(); let datas = isAsc ? this.data.sort(this.compare) : this.data.reverse();
this.setState({data: datas, asc: !isAsc}); this.setState({data: datas, asc: !isAsc});
} }
_compare(a, b) { compare(a, b) {
if (a.applicationName < b.applicationName) if (a.applicationName < b.applicationName)
return -1; return -1;
if (a.applicationName > b.applicationName) if (a.applicationName > b.applicationName)
@ -78,27 +78,28 @@ class PlatformListing extends Component {
return 0; return 0;
} }
_onRowClick(id) { onRowClick(id) {
console.log(id) console.log(id)
} }
render() { render() {
return ( return (
<div className= 'middle listingplatformmiddle'> <div className='middle listingplatformmiddle'>
<Card className='listingplatformcard'> <Card className='listingplatformcard'>
<TextField hintText="Search" onChange={this._searchApplications.bind(this)} <TextField hintText="Search" onChange={this.searchApplications.bind(this)}
className='listingplatformsearch'/> className='listingplatformsearch'/>
<CardTitle title="Platforms" className='listingplatformTitle'/> <CardTitle title="Platforms" className='listingplatformTitle'/>
<CardActions> <CardActions>
</CardActions> </CardActions>
<DataTable headers={this.headers} <DataTable
headers={this.headers}
data={this.data} data={this.data}
handleRowClick={this._onRowClick.bind(this)} handleRowClick={this.onRowClick.bind(this)}
noDataMessage={{type: 'button', text: 'Create Platform'}}/> noDataMessage={{type: 'button', text: 'Create Platform'}}/>
</Card> </Card>
</div>
</div>); );
} }
} }

@ -16,13 +16,13 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import DataTableRow from './DataTableRow'; import DataTableRow from './DataTableRow';
import DataTableHeader from './DataTableHeader'; import DataTableHeader from './DataTableHeader';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import {Table, TableBody, TableHeader, TableRow} from 'material-ui/Table'; import {Table, TableBody, TableHeader, TableRow} from 'material-ui/Table';
import Theme from '../../theme';
/** /**
* The Custom Table Component. * The Custom Table Component.
@ -53,6 +53,7 @@ class DataTable extends Component {
constructor() { constructor() {
super(); super();
this.handleRowClick = this.handleRowClick.bind(this);
this.state = { this.state = {
data: [], data: [],
headers: [], headers: [],
@ -83,7 +84,7 @@ class DataTable extends Component {
* Triggers when user click on table row. * Triggers when user click on table row.
* This method invokes the parent method handleRowClick, which is passed via props. * This method invokes the parent method handleRowClick, which is passed via props.
* */ * */
_handleRowClick(id) { handleRowClick(id) {
this.props.handleRowClick(id); this.props.handleRowClick(id);
} }
@ -100,22 +101,29 @@ class DataTable extends Component {
if (data) { if (data) {
return (<Table return (<Table
selectable={ false }> selectable={false}>
<TableHeader displaySelectAll={ false } <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
adjustForCheckbox={ false }>
<TableRow> <TableRow>
{headers.map((header) => { {headers.map((header) => {
return (<DataTableHeader key={header.data_id} className="datatableRowColumn" return (
header={header}/>) <DataTableHeader
} key={header.data_id}
className="datatableRowColumn"
header={header}
/>
)}
)} )}
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{data.map((dataItem) =>{ {data.map((dataItem) => {
return (<DataTableRow key={dataItem.id} return (
<DataTableRow
key={dataItem.id}
dataItem={dataItem} dataItem={dataItem}
handleClick={this._handleRowClick.bind(this)}/>) handleClick={this.handleRowClick}
/>
)
})} })}
</TableBody> </TableBody>
</Table>) </Table>)

@ -16,11 +16,11 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import FlatButton from 'material-ui/FlatButton'; import FlatButton from 'material-ui/FlatButton';
import {TableHeaderColumn} from 'material-ui/Table'; import {TableHeaderColumn} from 'material-ui/Table';
import Theme from '../../theme';
/** /**
* Data Table header component. * Data Table header component.
@ -30,6 +30,7 @@ class DataTableHeader extends Component {
constructor() { constructor() {
super(); super();
this.tableHeaderClick = this.tableHeaderClick.bind(this);
this.scriptId = "data-table"; this.scriptId = "data-table";
} }
@ -42,13 +43,14 @@ class DataTableHeader extends Component {
componentWillUnmount() { componentWillUnmount() {
Theme.removeThemingScripts(this.scriptId); Theme.removeThemingScripts(this.scriptId);
} }
/** /**
* The onClick function of the table header. * The onClick function of the table header.
* Invokes the function passed in the header object. * Invokes the function passed in the header object.
* */ * */
_tableHeaderClick() { tableHeaderClick() {
this.props.header.sort(); this.props.header.sort();
} }
@ -60,14 +62,18 @@ class DataTableHeader extends Component {
* else create a span element with label as the table header. * else create a span element with label as the table header.
* */ * */
if (this.props.header.sortable) { if (this.props.header.sortable) {
headerCell = <FlatButton label={this.props.header.label} headerCell =
onClick={this._tableHeaderClick.bind(this)} className="sortableHeaderCell"/>; <FlatButton
label={this.props.header.label}
onClick={this.tableHeaderClick}
className="sortableHeaderCell"
/>
} else { } else {
headerCell = <span className="notsortableHeaderCell">{this.props.header.label}</span>; headerCell = <span className="notsortableHeaderCell">{this.props.header.label}</span>;
} }
return ( return (
<TableHeaderColumn key={this.props.header.id} className="datatableHeaderColumn" > <TableHeaderColumn key={this.props.header.id} className="datatableHeaderColumn">
{headerCell} {headerCell}
</TableHeaderColumn> </TableHeaderColumn>
); );

@ -16,10 +16,10 @@
* under the License. * under the License.
*/ */
import Theme from '../../theme';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import {TableRow, TableRowColumn} from 'material-ui/Table'; import {TableRow, TableRowColumn} from 'material-ui/Table';
import Theme from '../../theme';
/** /**
* Data table row component. * Data table row component.
@ -51,23 +51,31 @@ class DataTableRow extends Component {
/** /**
* Triggers the click event on the data table row. * Triggers the click event on the data table row.
* */ * */
_handleClick() { handleClick() {
this.props.handleClick(this.state.dataItem.id); this.props.handleClick(this.state.dataItem.id);
} }
render() { render() {
const {dataItem} = this.state; const {dataItem} = this.state;
return ( return (
<TableRow key={this.props.key} onClick={this._handleClick.bind(this)} > <TableRow
key={this.props.key}
onClick={this.handleClick.bind(this)}
>
{Object.keys(dataItem).map((key) => { {Object.keys(dataItem).map((key) => {
if (key !== 'id') { if (key !== 'id') {
return <TableRowColumn className = "datatableRowColumn" return (
key={key}>{dataItem[key]}</TableRowColumn> <TableRowColumn
className="datatableRowColumn"
key={key}
>
{dataItem[key]}
</TableRowColumn>)
} else { } else {
return <TableRowColumn key={key}/> return <TableRowColumn key={key}/>
} }
} )} })}
</TableRow> </TableRow>
); );
} }

@ -20,13 +20,11 @@ import qs from 'qs';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import Checkbox from 'material-ui/Checkbox'; import Checkbox from 'material-ui/Checkbox';
import TextField from 'material-ui/TextField';
import {Redirect, Switch} from 'react-router-dom'; import {Redirect, Switch} from 'react-router-dom';
import AuthHandler from '../../../api/authHandler';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import {Card, CardActions, CardTitle} from 'material-ui/Card'; import {Card, CardActions, CardTitle} from 'material-ui/Card';
import {TextValidator, ValidatorForm} from 'react-material-ui-form-validator';
//todo: remove the {TextValidator, ValidatorForm} and implement it manually.
/** /**
* The Login Component. * The Login Component.
@ -40,36 +38,43 @@ class Login extends Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
isLoggedIn: true, isLoggedIn: false,
referrer: "/", referrer: "/",
userName: "", userName: "",
password: "", password: "",
rememberMe: true rememberMe: true,
errors: {}
} }
} }
componentDidMount() { componentWillMount() {
let queryString = this.props.location.search; console.log("IN Login")
console.log(queryString);
queryString = queryString.replace(/^\?/, '');
/* With QS version up we can directly use {ignoreQueryPrefix: true} option */
let params = qs.parse(queryString);
if (params.referrer) {
this.setState({referrer: params.referrer});
} }
componentDidMount() {
console.log("in Login")
// let queryString = this.props.location.search;
// console.log(queryString);
// queryString = queryString.replace(/^\?/, '');
// /* With QS version up we can directly use {ignoreQueryPrefix: true} option */
// let params = qs.parse(queryString);
// if (params.referrer) {
// this.setState({referrer: params.referrer});
// }
} }
handleLogin(event) { handleLogin(event) {
event.preventDefault(); event.preventDefault();
this.validateForm();
} }
/** /**
* Handles the username field change event. * Handles the username field change event.
* */ * */
onUserNameChange(event) { onUserNameChange(event, value) {
this.setState( this.setState(
{ {
userName: event.target.value userName: value
} }
); );
} }
@ -77,10 +82,10 @@ class Login extends Component {
/** /**
* Handles the password field change event. * Handles the password field change event.
* */ * */
onPasswordChange(event) { onPasswordChange(event, value) {
this.setState( this.setState(
{ {
password: event.target.value password: value
} }
); );
} }
@ -96,48 +101,77 @@ class Login extends Component {
); );
} }
/**
* Validate the login form.
* */
validateForm() {
let errors = {};
let validationFailed = true;
if (!this.state.password) {
errors["passwordError"] = "Password is Required";
validationFailed = true;
} else {
validationFailed = false;
}
if (!this.state.userName) {
errors["userNameError"] = "User Name is Required";
validationFailed = true;
} else {
validationFailed = false;
}
if (validationFailed) {
this.setState({errors: errors}, console.log(errors));
} else {
let loginPromis = AuthHandler.login(this.state.userName, this.state.password);
loginPromis.then(response => {
console.log(AuthHandler.getUser());
this.setState({isLoggedIn: AuthHandler.getUser()});
})
}
}
render() { render() {
if (!this.state.isLoggedIn) { if (!this.state.isLoggedIn) {
return ( return (
<div> <div>
{/*TODO: Style the components.*/} {/*TODO: Style the components.*/}
<Card> <Card>
<CardTitle title="WSO2 IoT App Publisher"/> <CardTitle title="WSO2 IoT App Publisher"/>
<CardActions> <CardActions>
<ValidatorForm <form onSubmit={this.handleLogin.bind(this)}>
ref="form" <TextField
onSubmit={this.handleLogin.bind(this)} hintText="Enter the User Name."
onError={errors => console.log(errors)}> id="username"
<TextValidator errorText={this.state.errors["userNameError"]}
floatingLabelText="User Name" floatingLabelText="User Name*"
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this.onUserNameChange.bind(this)}
name="userName"
validators={['required']}
errorMessages={['User Name is required']}
value={this.state.userName} value={this.state.userName}
onChange={this.onUserNameChange.bind(this)}
/> />
<br/> <br/>
<TextValidator <TextField
floatingLabelText="Password" hintText="Enter the Password."
floatingLabelFixed={true} id="password"
onChange={this.onPasswordChange.bind(this)}
name="password"
type="password" type="password"
errorText={this.state.errors["passwordError"]}
floatingLabelText="Password*"
floatingLabelFixed={true}
value={this.state.password} value={this.state.password}
validators={['required']} onChange={this.onPasswordChange.bind(this)}
errorMessages={['Password is required']}
/> />
<br/> <br/>
<Checkbox label="Remember me." <Checkbox
label="Remember me."
onCheck={this.handleRememberMe.bind(this)} onCheck={this.handleRememberMe.bind(this)}
checked={this.state.rememberMe}/> checked={this.state.rememberMe}
/>
<br/> <br/>
<RaisedButton type="submit" label="Login"/> <RaisedButton type="submit" label="Login"/>
</ValidatorForm> </form>
</CardActions> </CardActions>
</Card> </Card>
</div>); </div>);

@ -21,7 +21,6 @@ import NotFound from './Error/NotFound';
import BaseLayout from './Base/BaseLayout'; import BaseLayout from './Base/BaseLayout';
import PlatformCreate from './Platform/PlatformCreate'; import PlatformCreate from './Platform/PlatformCreate';
import PlatformListing from './Platform/PlatformListing'; import PlatformListing from './Platform/PlatformListing';
import PublisherOverview from './Overview/PublisherOverview';
import ApplicationCreate from './Application/ApplicationCreate'; import ApplicationCreate from './Application/ApplicationCreate';
import ApplicationListing from './Application/ApplicationListing'; import ApplicationListing from './Application/ApplicationListing';
@ -29,5 +28,4 @@ import ApplicationListing from './Application/ApplicationListing';
* Contains all UI components related to Application, Login and Platform * Contains all UI components related to Application, Login and Platform
*/ */
export {Login, BaseLayout, ApplicationCreate, ApplicationListing, PlatformListing, NotFound, PublisherOverview, export {Login, BaseLayout, ApplicationCreate, ApplicationListing, PlatformListing, NotFound, PlatformCreate};
PlatformCreate};

@ -35,10 +35,10 @@ class Theme {
//TODO Need to get the app context properly when the server is ready //TODO Need to get the app context properly when the server is ready
this.baseURL = window.location.origin; this.baseURL = window.location.origin;
this.appContext = window.location.pathname.split("/")[1]; this.appContext = window.location.pathname.split("/")[1];
this.loadThemeConfigs.bind(this); this.loadThemeConfigs = this.loadThemeConfigs.bind(this);
this.loadThemeFiles.bind(this); this.loadThemeFiles = this.loadThemeFiles.bind(this);
this.insertThemingScripts.bind(this); this.insertThemingScripts = this.insertThemingScripts.bind(this);
this.removeThemingScripts.bind(this); this.removeThemingScripts = this.removeThemingScripts.bind(this);
} }
/** /**

@ -38,6 +38,7 @@
<module>org.wso2.carbon.device.application.mgt.common</module> <module>org.wso2.carbon.device.application.mgt.common</module>
<module>org.wso2.carbon.device.application.mgt.api</module> <module>org.wso2.carbon.device.application.mgt.api</module>
<module>org.wso2.carbon.device.application.mgt.publisher.ui</module> <module>org.wso2.carbon.device.application.mgt.publisher.ui</module>
<module>org.wso2.carbon.device.application.mgt.authhandler</module>
</modules> </modules>
<build> <build>

@ -0,0 +1,120 @@
<?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">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt-feature</artifactId>
<version>3.0.46-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.auth.handler.feature</artifactId>
<version>3.0.46-SNAPSHOT</version>
<packaging>pom</packaging>
<name>WSO2 Carbon - Application Management Authentication Handler Feature</name>
<description>This feature contains the Authentication Handler implementation for Publisher and Store.</description>
<url>http://wso2.org</url>
<build>
<plugins>
<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>org.wso2.carbon.device.application.mgt.authhandler
</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<outputDirectory>
${project.build.directory}/maven-shared-archive-resources/webapps
</outputDirectory>
<destFileName>auth#application-mgt#v1.0.war</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>src/main/resources</outputDirectory>
<resources>
<resource>
<directory>resources</directory>
<includes>
<include>build.properties</include>
<include>p2.inf</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.wso2.maven</groupId>
<artifactId>carbon-p2-plugin</artifactId>
<executions>
<execution>
<id>p2-feature-generation</id>
<phase>package</phase>
<goals>
<goal>p2-feature-gen</goal>
</goals>
<configuration>
<id>org.wso2.carbon.device.application.mgt.auth.handler</id>
<propertiesFile>../../../features/etc/feature.properties
</propertiesFile>
<adviceFile>
<properties>
<propertyDef>org.wso2.carbon.p2.category.type:server
</propertyDef>
<propertyDef>org.eclipse.equinox.p2.type.group:false
</propertyDef>
</properties>
</adviceFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,3 @@
instructions.configure = \
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.application.mgt.auth.handler_${feature.version}/webapps/auth#application-mgt#v1.0.war,target:${installFolder}/../../deployment/server/webapps/auth#application-mgt#v1.0.war,overwrite:true);\

@ -42,7 +42,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.wso2.carbon.devicemgt</groupId> <groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.application.mgt.ui.feature</artifactId> <artifactId>org.wso2.carbon.device.application.mgt.publisher.ui.feature</artifactId>
<type>zip</type> <type>zip</type>
</dependency> </dependency>
<dependency> <dependency>

@ -35,6 +35,7 @@
<modules> <modules>
<module>org.wso2.carbon.device.application.mgt.api.feature</module> <module>org.wso2.carbon.device.application.mgt.api.feature</module>
<module>org.wso2.carbon.device.application.mgt.auth.handler.feature</module>
<!--<module>org.wso2.carbon.device.application.mgt.ui.feature</module>--> <!--<module>org.wso2.carbon.device.application.mgt.ui.feature</module>-->
<module>org.wso2.carbon.device.application.mgt.feature</module> <module>org.wso2.carbon.device.application.mgt.feature</module>
<!--<module>org.wso2.carbon.device.application.mgt.extensions.feature</module>--> <!--<module>org.wso2.carbon.device.application.mgt.extensions.feature</module>-->

Loading…
Cancel
Save