forked from community/device-mgt-core
parent
4e4b2fb5c8
commit
5cb706f24b
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.wso2.carbon.device.application.mgt.common.dto;
|
||||
|
||||
public class ApiRegistrationProfile {
|
||||
|
||||
private String applicationName;
|
||||
private String tags[];
|
||||
private boolean isAllowedToAllDomains;
|
||||
private 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 setAllowedToAllDomains(boolean allowedToAllDomains) {
|
||||
isAllowedToAllDomains = allowedToAllDomains;
|
||||
}
|
||||
|
||||
public boolean isMappingAnExistingOAuthApp() {
|
||||
return isMappingAnExistingOAuthApp;
|
||||
}
|
||||
|
||||
public void setMappingAnExistingOAuthApp(boolean mappingAnExistingOAuthApp) {
|
||||
isMappingAnExistingOAuthApp = mappingAnExistingOAuthApp;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package org.wso2.carbon.device.application.mgt.common.dto;
|
||||
|
||||
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ApplicationPolicyDTO {
|
||||
ApplicationDTO applicationDTO;
|
||||
String policy;
|
||||
List<DeviceIdentifier> deviceIdentifierList;
|
||||
String action;
|
||||
|
||||
public List<DeviceIdentifier> getDeviceIdentifierList() {
|
||||
return deviceIdentifierList;
|
||||
}
|
||||
|
||||
public void setDeviceIdentifierList(List<DeviceIdentifier> deviceIdentifierList) {
|
||||
this.deviceIdentifierList = deviceIdentifierList;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public ApplicationDTO getApplicationDTO() {
|
||||
return applicationDTO;
|
||||
}
|
||||
|
||||
public void setApplicationDTO(ApplicationDTO applicationDTO) {
|
||||
this.applicationDTO = applicationDTO;
|
||||
}
|
||||
|
||||
public String getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
public void setPolicy(String policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.wso2.carbon.device.application.mgt.core.util;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.apimgt.application.extension.APIManagementProviderService;
|
||||
import org.wso2.carbon.apimgt.application.extension.dto.ApiApplicationKey;
|
||||
import org.wso2.carbon.apimgt.application.extension.exception.APIManagerException;
|
||||
import org.wso2.carbon.context.PrivilegedCarbonContext;
|
||||
import org.wso2.carbon.device.application.mgt.common.dto.ApiRegistrationProfile;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.JWTClient;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.exception.JWTClientException;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService;
|
||||
import org.wso2.carbon.user.api.UserStoreException;
|
||||
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
|
||||
|
||||
public class OAuthUtils {
|
||||
|
||||
private static final Log log = LogFactory.getLog(OAuthUtils.class);
|
||||
|
||||
public static ApiApplicationKey getClientCredentials(String tenantDomain)
|
||||
throws UserStoreException, APIManagerException {
|
||||
ApiRegistrationProfile registrationProfile = new ApiRegistrationProfile();
|
||||
registrationProfile.setApplicationName(Constants.ApplicationInstall.APPLICATION_NAME);
|
||||
registrationProfile.setTags(new String[]{Constants.ApplicationInstall.DEVICE_TYPE_ANDROID});
|
||||
registrationProfile.setAllowedToAllDomains(false);
|
||||
registrationProfile.setMappingAnExistingOAuthApp(false);
|
||||
return getCredentials(registrationProfile, tenantDomain);
|
||||
}
|
||||
|
||||
public static ApiApplicationKey getCredentials(ApiRegistrationProfile registrationProfile, String tenantDomain)
|
||||
throws UserStoreException, APIManagerException {
|
||||
ApiApplicationKey apiApplicationKeyInfo;
|
||||
if (tenantDomain == null || tenantDomain.isEmpty()) {
|
||||
tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
|
||||
}
|
||||
try {
|
||||
PrivilegedCarbonContext.startTenantFlow();
|
||||
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
|
||||
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(PrivilegedCarbonContext.
|
||||
getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration().getAdminUserName());
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
APIManagementProviderService apiManagementProviderService = (APIManagementProviderService) ctx.
|
||||
getOSGiService(APIManagementProviderService.class, null);
|
||||
apiApplicationKeyInfo = apiManagementProviderService.
|
||||
generateAndRetrieveApplicationKeys(registrationProfile.getApplicationName(),
|
||||
registrationProfile.getTags(), Constants.ApplicationInstall.DEFAULT_TOKEN_TYPE,
|
||||
registrationProfile.getApplicationName(), registrationProfile.isAllowedToAllDomains(),
|
||||
Constants.ApplicationInstall.DEFAULT_VALIDITY_PERIOD);
|
||||
} finally {
|
||||
PrivilegedCarbonContext.endTenantFlow();
|
||||
}
|
||||
return apiApplicationKeyInfo;
|
||||
}
|
||||
|
||||
public static AccessTokenInfo getOAuthCredentials(ApiApplicationKey apiApplicationKey, String username)
|
||||
throws APIManagerException {
|
||||
try {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
JWTClientManagerService jwtClientManagerService = (JWTClientManagerService) ctx.
|
||||
getOSGiService(JWTClientManagerService.class, null);
|
||||
JWTClient jwtClient = jwtClientManagerService.getJWTClient();
|
||||
return jwtClient.getAccessToken(apiApplicationKey.getConsumerKey(), apiApplicationKey.getConsumerSecret(),
|
||||
username, Constants.ApplicationInstall.SUBSCRIPTION_SCOPE);
|
||||
} catch (JWTClientException e) {
|
||||
String errorMsg = "Error while generating an OAuth token for user " + username;
|
||||
log.error(errorMsg, e);
|
||||
throw new APIManagerException(errorMsg, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, Divider, Form, Input, message, Modal, notification, Spin} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import {withRouter} from "react-router";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
|
||||
class AddNewPage extends React.Component {
|
||||
|
||||
state = {
|
||||
visible: false,
|
||||
pageName: ''
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
loading: false
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
handleCancel = e => {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handlePageName = (e) => {
|
||||
this.setState({
|
||||
pageName: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
createNewPage = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page",
|
||||
{
|
||||
"locale": "en",
|
||||
"pageName": this.state.pageName
|
||||
}
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
|
||||
const {pageId, pageName} = res.data.data;
|
||||
|
||||
notification["success"]({
|
||||
message: 'Saved!',
|
||||
description: 'Page created successfully!'
|
||||
});
|
||||
|
||||
this.setState({loading: false});
|
||||
|
||||
this.props.history.push(`/publisher/manage/android-enterprise/pages/${pageName}/${pageId}`);
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{marginTop: 24, marginBottom: 24}}>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={this.showModal}>
|
||||
Add new page
|
||||
</Button>
|
||||
<Modal
|
||||
title="Add new page"
|
||||
visible={this.state.visible}
|
||||
onOk={this.createNewPage}
|
||||
onCancel={this.handleCancel}
|
||||
okText="Create Page"
|
||||
footer={null}
|
||||
>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<p>Choose a name for the page</p>
|
||||
<Input onChange={this.handlePageName}/>
|
||||
<Divider/>
|
||||
<div>
|
||||
<Button
|
||||
onClick={this.handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Button
|
||||
onClick={this.createNewPage}
|
||||
htmlType="button" type="primary"
|
||||
disabled={this.state.pageName.length === 0}>
|
||||
Create Page
|
||||
</Button>
|
||||
</div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(withRouter(AddNewPage));
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Modal, Button} from "antd";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
|
||||
class GooglePlayIframe extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.config = this.props.context;
|
||||
|
||||
this.state = {
|
||||
visible: false
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = e => {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{display: "inline-block", padding: 4}}>
|
||||
<Button type="primary" onClick={this.showModal}>
|
||||
Approve Applications
|
||||
</Button>
|
||||
<Modal
|
||||
title={null}
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
width = {740}
|
||||
footer={null}>
|
||||
<iframe
|
||||
style={{
|
||||
height: 720,
|
||||
border: 0,
|
||||
width: "100%"
|
||||
}}
|
||||
src={"https://play.google.com/work/embedded/search?token=" + this.config.androidEnterpriseToken +
|
||||
"&mode=APPROVE&showsearchbox=TRUE"}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(GooglePlayIframe);
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, message, Modal, notification, Spin} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../../js/Utils";
|
||||
|
||||
// import gapi from 'gapi-client';
|
||||
|
||||
class ManagedConfigurationsIframe extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.config = this.props.context;
|
||||
this.state = {
|
||||
visible: false,
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.getMcm();
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = e => {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
getMcm = () => {
|
||||
const {packageName} = this.props;
|
||||
this.setState({loading: true});
|
||||
|
||||
//send request to the invoker
|
||||
axios.get(
|
||||
window.location.origin + this.config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/managed-configs/package/" + packageName,
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
let mcmId = null;
|
||||
if (res.data.hasOwnProperty("data")) {
|
||||
mcmId = res.data.data.mcmId;
|
||||
}
|
||||
this.loadIframe(mcmId);
|
||||
this.setState({loading: false});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to load configurations.",
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false, visible: false});
|
||||
});
|
||||
};
|
||||
|
||||
loadIframe = (mcmId) => {
|
||||
const {packageName} = this.props;
|
||||
let method = "post";
|
||||
gapi.load('gapi.iframes', () => {
|
||||
const parameters = {
|
||||
token: this.config.androidEnterpriseToken,
|
||||
packageName: packageName
|
||||
};
|
||||
if (mcmId != null) {
|
||||
parameters.mcmId = mcmId;
|
||||
parameters.canDelete = true;
|
||||
method = "put";
|
||||
}
|
||||
|
||||
const queryString = Object.keys(parameters).map(key => key + '=' + parameters[key]).join('&');
|
||||
|
||||
var options = {
|
||||
'url': "https://play.google.com/managed/mcm?" + queryString,
|
||||
'where': document.getElementById('manage-config-iframe-container'),
|
||||
'attributes': {style: 'height:720px', scrolling: 'yes'}
|
||||
};
|
||||
|
||||
var iframe = gapi.iframes.getContext().openChild(options);
|
||||
iframe.register('onconfigupdated', (event) => {
|
||||
this.updateConfig(method, event);
|
||||
}, gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER);
|
||||
|
||||
iframe.register('onconfigdeleted', (event) => {
|
||||
this.deleteConfig(event);
|
||||
}, gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER);
|
||||
});
|
||||
};
|
||||
|
||||
updateConfig = (method, event) => {
|
||||
const {packageName} = this.props;
|
||||
this.setState({loading: true});
|
||||
console.log(event);
|
||||
|
||||
const data = {
|
||||
mcmId: event.mcmId,
|
||||
profileName: event.name,
|
||||
packageName
|
||||
};
|
||||
|
||||
//send request to the invoker
|
||||
axios({
|
||||
method,
|
||||
url: window.location.origin + this.config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/managed-configs",
|
||||
data
|
||||
}).then(res => {
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
notification["success"]({
|
||||
message: 'Saved!',
|
||||
description: 'Configuration Profile updated Successfully',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to update configurations.",
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
});
|
||||
};
|
||||
|
||||
deleteConfig = (event) => {
|
||||
const {packageName} = this.props;
|
||||
this.setState({loading: true});
|
||||
console.log(event);
|
||||
|
||||
//send request to the invoker
|
||||
axios.delete(
|
||||
window.location.origin + this.config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/managed-configs/mcm/" + event.mcmId
|
||||
).then(res => {
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
notification["success"]({
|
||||
message: 'Saved!',
|
||||
description: 'Configuration Profile removed Successfully',
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to remove configurations.");
|
||||
this.setState({loading: false});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="setting"
|
||||
onClick={this.showModal}>
|
||||
Manage
|
||||
</Button>
|
||||
<Modal
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={null}>
|
||||
<Spin spinning={this.state.loading}>
|
||||
<div id="manage-config-iframe-container">
|
||||
</div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(ManagedConfigurationsIframe);
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Modal, Icon, Table, Avatar} from 'antd';
|
||||
import "../Cluster.css";
|
||||
import {withConfigContext} from "../../../../../../context/ConfigContext";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'iconUrl',
|
||||
key: 'iconUrl',
|
||||
render: (iconUrl) => (<Avatar shape="square" src={iconUrl}/>)
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'Page',
|
||||
dataIndex: 'packageId',
|
||||
key: 'packageId'
|
||||
}
|
||||
];
|
||||
|
||||
class AddAppsToClusterModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visible: false,
|
||||
loading: false,
|
||||
selectedProducts: [],
|
||||
homePageId: null
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = () => {
|
||||
this.props.addSelectedProducts(this.state.selectedProducts);
|
||||
this.handleCancel();
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
rowSelection = {
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
this.setState({
|
||||
selectedProducts: selectedRows
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
const {pagination, loading} = this.state;
|
||||
return (
|
||||
<div>
|
||||
<div className="btn-add-new-wrapper">
|
||||
<div className="btn-add-new">
|
||||
<button className="btn"
|
||||
onClick={this.showModal}>
|
||||
<Icon style={{position: "relative"}} type="plus"/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="title">
|
||||
Add app
|
||||
</div>
|
||||
</div>
|
||||
<Modal
|
||||
title="Select Apps"
|
||||
width={640}
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}>
|
||||
<Table
|
||||
columns={columns}
|
||||
rowKey={record => record.packageId}
|
||||
dataSource={this.props.unselectedProducts}
|
||||
scroll={{ x: 300 }}
|
||||
pagination={{
|
||||
...pagination,
|
||||
size: "small",
|
||||
// position: "top",
|
||||
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} pages`,
|
||||
showQuickJumper: true
|
||||
}}
|
||||
loading={loading}
|
||||
onChange={this.handleTableChange}
|
||||
rowSelection={this.rowSelection}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(AddAppsToClusterModal);
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.cluster{
|
||||
border-radius: 5px;
|
||||
background-color: white;
|
||||
padding: 24px;
|
||||
margin: 14px 0 14px 0;
|
||||
}
|
||||
|
||||
.cluster .products-row{
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.cluster .product{
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
padding: 20px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cluster .product .product-icon{
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
height: 180px;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.cluster .product .title, .cluster .btn-add-new-wrapper .title {
|
||||
color: #202124;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.cluster .product .product-icon img{
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
height: 90px;
|
||||
/*padding-bottom: 16px;*/
|
||||
width: 90px;
|
||||
border-radius: 28%;
|
||||
}
|
||||
|
||||
.cluster .product .arrow {
|
||||
color: #80868b;
|
||||
font-size: 20px;
|
||||
position: relative;
|
||||
top: 20px;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.cluster .product .arrow .btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 12px;
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
fill: currentColor;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.cluster .product .arrow .btn-right {
|
||||
left: -12px;
|
||||
}
|
||||
|
||||
.cluster .product .delete-btn {
|
||||
color: #80868b;
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
.cluster .product .delete-btn .btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 16px 28px 0 0;
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
fill: currentColor;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new-wrapper{
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
height: 180px;
|
||||
width: 90px;
|
||||
margin: 0 33px;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new{
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
height: 90px;
|
||||
padding-bottom: 16px;
|
||||
width: 90px;
|
||||
border-radius: 28%;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new .btn{
|
||||
height: 36px;
|
||||
padding: 0 23px 0 23px;
|
||||
border-width: 1px;
|
||||
min-height: 90px;
|
||||
width: 90px;
|
||||
background-color: transparent;
|
||||
border-radius: 28%;
|
||||
}
|
||||
|
||||
.cluster .btn-add-new :hover{
|
||||
background-color: rgba(250, 159, 0, 0.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cluster .ant-typography-edit-content{
|
||||
width: 200px;
|
||||
}
|
@ -0,0 +1,410 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, Col, Divider, Icon, message, notification, Popconfirm, Row, Spin, Tooltip, Typography} from "antd";
|
||||
|
||||
import "./Cluster.css";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../../context/ConfigContext";
|
||||
import AddAppsToClusterModal from "./AddAppsToClusterModal/AddAppsToClusterModal";
|
||||
import {handleApiError} from "../../../../../js/Utils";
|
||||
|
||||
const {Title} = Typography;
|
||||
|
||||
class Cluster extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const {cluster, pageId} = this.props;
|
||||
this.originalCluster = Object.assign({}, cluster);
|
||||
const {name, products, clusterId} = cluster;
|
||||
this.clusterId = clusterId;
|
||||
this.pageId = pageId;
|
||||
this.state = {
|
||||
name,
|
||||
products,
|
||||
isSaveable: false,
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
|
||||
handleNameChange = (name) => {
|
||||
this.setState({
|
||||
name
|
||||
});
|
||||
if (name !== this.originalCluster.name) {
|
||||
this.setState({
|
||||
isSaveable: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
isProductsChanged = (currentProducts) => {
|
||||
let isChanged = false;
|
||||
const originalProducts = this.originalCluster.products;
|
||||
if (currentProducts.length === originalProducts.length) {
|
||||
for (let i = 0; i < currentProducts.length; i++) {
|
||||
if (currentProducts[i].packageId !== originalProducts[i].packageId) {
|
||||
isChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isChanged = true;
|
||||
}
|
||||
return isChanged;
|
||||
};
|
||||
|
||||
swapProduct = (index, swapIndex) => {
|
||||
const products = [...this.state.products];
|
||||
if (swapIndex !== -1 && index < products.length) {
|
||||
// swap elements
|
||||
[products[index], products[swapIndex]] = [products[swapIndex], products[index]];
|
||||
|
||||
this.setState({
|
||||
products,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
isSaveable: this.isProductsChanged(products)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
removeProduct = (index) => {
|
||||
const products = [...this.state.products];
|
||||
products.splice(index, 1);
|
||||
this.setState({
|
||||
products,
|
||||
isSaveable: true
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
getCurrentCluster = () => {
|
||||
const {products, name} = this.state;
|
||||
return {
|
||||
pageId: this.pageId,
|
||||
clusterId: this.clusterId,
|
||||
name: name,
|
||||
products: products,
|
||||
orderInPage: this.props.orderInPage
|
||||
};
|
||||
};
|
||||
|
||||
resetChanges = () => {
|
||||
const cluster = this.originalCluster;
|
||||
const {name, products} = cluster;
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
name,
|
||||
products,
|
||||
isSaveable: false
|
||||
});
|
||||
};
|
||||
|
||||
updateCluster = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
const cluster = this.getCurrentCluster();
|
||||
this.setState({loading: true});
|
||||
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/cluster",
|
||||
cluster
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: 'Saved!',
|
||||
description: 'Cluster updated successfully!'
|
||||
});
|
||||
const cluster = res.data.data;
|
||||
const {name, products} = cluster;
|
||||
|
||||
this.originalCluster = Object.assign({}, cluster);
|
||||
|
||||
this.resetChanges();
|
||||
if (this.props.toggleAddNewClusterVisibility !== undefined) {
|
||||
this.props.toggleAddNewClusterVisibility(false);
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
deleteCluster = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
|
||||
axios.delete(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
`/device-mgt/android/v1.0/enterprise/store-layout/cluster/${this.clusterId}/page/${this.pageId}`
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: 'Done!',
|
||||
description: 'Cluster deleted successfully!'
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
|
||||
this.props.removeLoadedCluster(this.clusterId);
|
||||
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
getUnselectedProducts = () => {
|
||||
const {applications} = this.props;
|
||||
const selectedProducts = this.state.products;
|
||||
|
||||
// get a copy from all products
|
||||
const unSelectedProducts = [...applications];
|
||||
|
||||
// remove selected products from unselected products
|
||||
selectedProducts.forEach((selectedProduct) => {
|
||||
for (let i = 0; i < unSelectedProducts.length; i++) {
|
||||
if (selectedProduct.packageId === unSelectedProducts[i].packageId) {
|
||||
// remove item from array
|
||||
unSelectedProducts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return unSelectedProducts;
|
||||
};
|
||||
|
||||
addSelectedProducts = (products) => {
|
||||
this.setState({
|
||||
products: [...this.state.products, ...products],
|
||||
isSaveable: products.length > 0
|
||||
});
|
||||
};
|
||||
|
||||
cancelAddingNewCluster = () => {
|
||||
this.resetChanges();
|
||||
this.props.toggleAddNewClusterVisibility(false);
|
||||
};
|
||||
|
||||
saveNewCluster = () => {
|
||||
const config = this.props.context;
|
||||
|
||||
const cluster = this.getCurrentCluster();
|
||||
this.setState({loading: true});
|
||||
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/cluster",
|
||||
cluster
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: 'Saved!',
|
||||
description: 'Cluster updated successfully!'
|
||||
});
|
||||
|
||||
const cluster = res.data.data;
|
||||
|
||||
this.resetChanges();
|
||||
this.props.addSavedClusterToThePage(cluster);
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to update the cluster.",
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {name, products, loading} = this.state;
|
||||
const unselectedProducts = this.getUnselectedProducts();
|
||||
const {isTemporary, index} = this.props;
|
||||
const Product = ({product, index}) => {
|
||||
const {packageId} = product;
|
||||
let imageSrc = "";
|
||||
const iconUrl = product.iconUrl;
|
||||
// check if the icon url is an url or google image id
|
||||
if (iconUrl.startsWith("http")) {
|
||||
imageSrc = iconUrl;
|
||||
} else {
|
||||
imageSrc = `https://lh3.googleusercontent.com/${iconUrl}=s240-rw`;
|
||||
}
|
||||
return (
|
||||
<div className="product">
|
||||
<div className="arrow">
|
||||
<button disabled={index === 0} className="btn"
|
||||
onClick={() => {
|
||||
this.swapProduct(index, index - 1);
|
||||
}}>
|
||||
<Icon type="caret-left" theme="filled"/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="product-icon">
|
||||
<img src={imageSrc}/>
|
||||
<Tooltip title={packageId}>
|
||||
<div className="title">
|
||||
{packageId}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="arrow">
|
||||
<button
|
||||
disabled={index === products.length - 1}
|
||||
onClick={() => {
|
||||
this.swapProduct(index, index + 1);
|
||||
}} className="btn btn-right"><Icon type="caret-right" theme="filled"/></button>
|
||||
</div>
|
||||
<div className="delete-btn">
|
||||
<button className="btn"
|
||||
onClick={() => {
|
||||
this.removeProduct(index)
|
||||
}}>
|
||||
<Icon type="close-circle" theme="filled"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="cluster" id={this.props.orderInPage}>
|
||||
<Spin spinning={loading}>
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<Title editable={{onChange: this.handleNameChange}} level={4}>{name}</Title>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
{!isTemporary && (
|
||||
<div style={{float: "right"}}>
|
||||
<Tooltip title="Move Up">
|
||||
<Button
|
||||
type="link"
|
||||
icon="caret-up"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
this.props.swapClusters(index, index - 1)
|
||||
}} htmlType="button"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Move Down">
|
||||
<Button
|
||||
type="link"
|
||||
icon="caret-down"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
this.props.swapClusters(index, index + 1)
|
||||
}} htmlType="button"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete Cluster">
|
||||
<Popconfirm
|
||||
title="Are you sure?"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onConfirm={this.deleteCluster}>
|
||||
<Button
|
||||
type="danger"
|
||||
icon="delete"
|
||||
shape="circle"
|
||||
htmlType="button"/>
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="products-row">
|
||||
<AddAppsToClusterModal
|
||||
addSelectedProducts={this.addSelectedProducts}
|
||||
unselectedProducts={unselectedProducts}/>
|
||||
{
|
||||
products.map((product, index) => {
|
||||
return (
|
||||
<Product
|
||||
key={product.packageId}
|
||||
product={product}
|
||||
index={index}/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<Row>
|
||||
<Col>
|
||||
{isTemporary && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={this.cancelAddingNewCluster}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Tooltip
|
||||
title={(products.length === 0) ? "You must add applications to the cluster before saving" : ""}>
|
||||
<Button
|
||||
disabled={products.length === 0}
|
||||
onClick={this.saveNewCluster}
|
||||
htmlType="button" type="primary">
|
||||
Save
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
{!isTemporary && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={this.resetChanges}
|
||||
disabled={!this.state.isSaveable}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Divider type="vertical"/>
|
||||
<Button
|
||||
onClick={this.updateCluster}
|
||||
htmlType="button" type="primary"
|
||||
disabled={!this.state.isSaveable}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(Cluster);
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, message, Modal, notification, Select, Spin} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../../../js/Utils";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
class EditLinks extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.selectedLinks = [];
|
||||
this.state = {
|
||||
visible: false
|
||||
};
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
loading: false
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
updateLinks = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page-link",
|
||||
{
|
||||
pageId: this.props.pageId,
|
||||
links: this.selectedLinks
|
||||
}
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
|
||||
notification["success"]({
|
||||
message: 'Saved!',
|
||||
description: 'Links updated successfully!'
|
||||
});
|
||||
|
||||
this.props.updateLinks(this.selectedLinks);
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while trying to update the cluster.");
|
||||
this.setState({loading: false});
|
||||
});
|
||||
};
|
||||
|
||||
handleChange= (selectedLinks) =>{
|
||||
this.selectedLinks = selectedLinks;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={this.showModal} type="link">[add / remove links]</Button>
|
||||
<Modal
|
||||
title="Add / Remove Links"
|
||||
visible={this.state.visible}
|
||||
onOk={this.updateLinks}
|
||||
onCancel={this.handleCancel}
|
||||
okText="Update">
|
||||
<Spin spinning={this.state.loading}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{width: '100%'}}
|
||||
placeholder="Please select links"
|
||||
defaultValue={this.props.selectedLinks}
|
||||
onChange={this.handleChange}>
|
||||
{
|
||||
this.props.pages.map((page) => (
|
||||
<Option disabled={page.id===this.props.pageId} key={page.id}>
|
||||
{page.name[0]["text"]}
|
||||
</Option>))
|
||||
}
|
||||
</Select>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(EditLinks);
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.layout-pages .action {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layout-pages .edit {
|
||||
color: #008dff;
|
||||
}
|
||||
|
||||
.layout-pages .btn-warning {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.layout-pages .btn-warning:hover {
|
||||
color: #fa8905;
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {Button, notification} from "antd";
|
||||
import axios from "axios";
|
||||
import {withConfigContext} from "../../../context/ConfigContext";
|
||||
import {handleApiError} from "../../../js/Utils";
|
||||
|
||||
class SyncAndroidApps extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false
|
||||
}
|
||||
}
|
||||
|
||||
syncApps = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri + "/device-mgt/android/v1.0/enterprise/products/sync",
|
||||
).then(res => {
|
||||
|
||||
notification["success"]({
|
||||
message: "Done!",
|
||||
description:
|
||||
"Apps synced successfully!",
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
}).catch((error) => {
|
||||
handleApiError(error, "Error occurred while syncing the apps.");
|
||||
this.setState({
|
||||
loading: false
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {loading} = this.state;
|
||||
return (
|
||||
<div style={{display: "inline-block", padding: 4}}>
|
||||
<Button
|
||||
onClick={this.syncApps}
|
||||
loading={loading}
|
||||
style={{marginTop: 16}}
|
||||
type="primary"
|
||||
icon="sync"
|
||||
>
|
||||
Sync{loading && "ing..."}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(SyncAndroidApps);
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import {message, notification} from "antd";
|
||||
|
||||
export const handleApiError = (error, message) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description: message,
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {PageHeader, Typography, Breadcrumb, Divider, Button, Icon} from "antd";
|
||||
import {Link} from "react-router-dom";
|
||||
import SyncAndroidApps from "../../../../components/manage/android-enterprise/SyncAndroidApps";
|
||||
import {withConfigContext} from "../../../../context/ConfigContext";
|
||||
import GooglePlayIframe from "../../../../components/manage/android-enterprise/GooglePlayIframe";
|
||||
import Pages from "../../../../components/manage/android-enterprise/Pages/Pages";
|
||||
|
||||
const {Paragraph} = Typography;
|
||||
|
||||
class ManageAndroidEnterprise extends React.Component {
|
||||
routes;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.routes = props.routes;
|
||||
this.config = this.props.context;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop: 0}}>
|
||||
<Breadcrumb style={{paddingBottom: 16}}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
Manage
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Android Enterprise</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Manage Android Enterprise</h3>
|
||||
{/*<Paragraph>Lorem ipsum</Paragraph>*/}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<SyncAndroidApps/>
|
||||
<GooglePlayIframe/>
|
||||
<Divider/>
|
||||
<Pages/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(ManageAndroidEnterprise);
|
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
PageHeader,
|
||||
Typography,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Icon,
|
||||
Col,
|
||||
Row,
|
||||
notification,
|
||||
message,
|
||||
Spin,
|
||||
Select,
|
||||
Tag,
|
||||
Divider
|
||||
} from "antd";
|
||||
import {Link, withRouter} from "react-router-dom";
|
||||
import {withConfigContext} from "../../../../../context/ConfigContext";
|
||||
import axios from "axios";
|
||||
import Cluster from "../../../../../components/manage/android-enterprise/Pages/Cluster/Cluster";
|
||||
import EditLinks from "../../../../../components/manage/android-enterprise/Pages/EditLinks/EditLinks";
|
||||
|
||||
const {Option} = Select;
|
||||
const {Title, Text} = Typography;
|
||||
|
||||
class Page extends React.Component {
|
||||
routes;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const {pageName, pageId} = this.props.match.params;
|
||||
this.pageId = pageId;
|
||||
this.routes = props.routes;
|
||||
this.config = this.props.context;
|
||||
this.pages = [];
|
||||
this.pageNames = {};
|
||||
this.state = {
|
||||
pageName,
|
||||
clusters: [],
|
||||
loading: false,
|
||||
applications: [],
|
||||
isAddNewClusterVisible: false,
|
||||
links: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchClusters();
|
||||
this.fetchApplications();
|
||||
this.fetchPages();
|
||||
}
|
||||
|
||||
removeLoadedCluster = (clusterId) => {
|
||||
const clusters = [...this.state.clusters];
|
||||
let index = -1;
|
||||
for (let i = 0; i < clusters.length; i++) {
|
||||
if (clusters[i].clusterId === clusterId) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
clusters.splice(index, 1);
|
||||
this.setState({
|
||||
clusters
|
||||
});
|
||||
};
|
||||
|
||||
updatePageName = pageName => {
|
||||
const config = this.props.context;
|
||||
if (pageName !== this.state.pageName && pageName !== "") {
|
||||
const data = {
|
||||
locale: "en",
|
||||
pageName: pageName,
|
||||
pageId: this.pageId
|
||||
};
|
||||
axios.put(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page",
|
||||
data
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
notification["success"]({
|
||||
message: 'Saved!',
|
||||
description: 'Page name updated successfully!'
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
pageName: res.data.data.pageName,
|
||||
});
|
||||
|
||||
this.props.history.push(`/publisher/manage/android-enterprise/pages/${pageName}/${this.pageId}`);
|
||||
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to save the page name.",
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
swapClusters = (index, swapIndex) => {
|
||||
const clusters = [...this.state.clusters];
|
||||
|
||||
if (swapIndex !== -1 && index < clusters.length) {
|
||||
// swap elements
|
||||
[clusters[index], clusters[swapIndex]] = [clusters[swapIndex], clusters[index]];
|
||||
|
||||
this.setState({
|
||||
clusters,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
fetchPages = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
|
||||
//send request to the invoker
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
"/device-mgt/android/v1.0/enterprise/store-layout/page",
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
this.pages = res.data.data.page;
|
||||
|
||||
let links = [];
|
||||
|
||||
this.pages.forEach((page) => {
|
||||
this.pageNames[page.id.toString()] = page.name[0]["text"];
|
||||
if (page.id === this.pageId && page.hasOwnProperty("link")) {
|
||||
links = page["link"];
|
||||
}
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
links
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to load pages.",
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
});
|
||||
};
|
||||
|
||||
fetchClusters = () => {
|
||||
const config = this.props.context;
|
||||
axios.get(
|
||||
window.location.origin + config.serverConfig.invoker.uri +
|
||||
`/device-mgt/android/v1.0/enterprise/store-layout/page/${this.pageId}/clusters`
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
let clusters = JSON.parse(res.data.data);
|
||||
|
||||
// sort according to the orderInPage value
|
||||
clusters.sort((a, b) => (a.orderInPage > b.orderInPage) ? 1 : -1);
|
||||
|
||||
this.setState({
|
||||
clusters,
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else if (!(error.hasOwnProperty("response") && error.response.status === 404)) {
|
||||
// API sends 404 when no apps
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to load clusters.",
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//fetch applications
|
||||
fetchApplications = () => {
|
||||
const config = this.props.context;
|
||||
this.setState({loading: true});
|
||||
|
||||
const filters = {
|
||||
appType: "PUBLIC",
|
||||
deviceType: "android"
|
||||
};
|
||||
|
||||
//send request to the invoker
|
||||
axios.post(
|
||||
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications",
|
||||
filters
|
||||
).then(res => {
|
||||
if (res.status === 200) {
|
||||
const applications = res.data.data.applications.map(application => {
|
||||
const release = application.applicationReleases[0];
|
||||
return {
|
||||
packageId: `app:${application.packageName}`,
|
||||
iconUrl: release.iconPath,
|
||||
name: application.name
|
||||
}
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
applications,
|
||||
});
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
if (error.hasOwnProperty("response") && error.response.status === 401) {
|
||||
message.error('You are not logged in');
|
||||
window.location.href = window.location.origin + '/publisher/login';
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: "There was a problem",
|
||||
duration: 0,
|
||||
description:
|
||||
"Error occurred while trying to load pages.",
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({loading: false});
|
||||
});
|
||||
};
|
||||
|
||||
toggleAddNewClusterVisibility = (isAddNewClusterVisible) => {
|
||||
this.setState({
|
||||
isAddNewClusterVisible
|
||||
});
|
||||
};
|
||||
|
||||
addSavedClusterToThePage = (cluster) => {
|
||||
this.setState({
|
||||
clusters: [...this.state.clusters, cluster],
|
||||
isAddNewClusterVisible: false
|
||||
});
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
};
|
||||
|
||||
updateLinks = (links) =>{
|
||||
this.setState({
|
||||
links
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {pageName, loading, clusters, applications, isAddNewClusterVisible, links} = this.state;
|
||||
return (
|
||||
<div>
|
||||
<PageHeader style={{paddingTop: 0}}>
|
||||
<Breadcrumb style={{paddingBottom: 16}}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/apps"><Icon type="home"/> Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
Manage
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/publisher/manage/android-enterprise">Android Enterprise</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Manage Page</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="wrap">
|
||||
<h3>Manage Android Enterprise</h3>
|
||||
{/*<Paragraph>Lorem ipsum</Paragraph>*/}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<Spin spinning={loading}>
|
||||
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
|
||||
<Row>
|
||||
<Col md={8} sm={18} xs={24}>
|
||||
<Title editable={{onChange: this.updatePageName}} level={2}>{pageName}</Title>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<Title level={4}>Links</Title>
|
||||
{
|
||||
links.map(link => {
|
||||
if (this.pageNames.hasOwnProperty(link.toString())) {
|
||||
return <Tag key={link}
|
||||
color="#87d068">{this.pageNames[link.toString()]}</Tag>
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
}
|
||||
<EditLinks
|
||||
updateLinks={this.updateLinks}
|
||||
pageId={this.pageId}
|
||||
selectedLinks={links}
|
||||
pages={this.pages}/>
|
||||
</Col>
|
||||
{/*<Col>*/}
|
||||
|
||||
{/*</Col>*/}
|
||||
</Row>
|
||||
|
||||
<Divider dashed={true}/>
|
||||
<Title level={4}>Clusters</Title>
|
||||
|
||||
<div hidden={isAddNewClusterVisible} style={{textAlign: "center"}}>
|
||||
<Button
|
||||
type="dashed"
|
||||
shape="round"
|
||||
icon="plus"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
this.toggleAddNewClusterVisibility(true);
|
||||
}}
|
||||
>Add new cluster</Button>
|
||||
</div>
|
||||
<div hidden={!isAddNewClusterVisible}>
|
||||
<Cluster
|
||||
cluster={{
|
||||
clusterId: 0,
|
||||
name: "New Cluster",
|
||||
products: []
|
||||
}}
|
||||
orderInPage={clusters.length}
|
||||
isTemporary={true}
|
||||
pageId={this.pageId}
|
||||
applications={applications}
|
||||
addSavedClusterToThePage={this.addSavedClusterToThePage}
|
||||
toggleAddNewClusterVisibility={this.toggleAddNewClusterVisibility}/>
|
||||
</div>
|
||||
|
||||
{
|
||||
clusters.map((cluster, index) => {
|
||||
return (
|
||||
<Cluster
|
||||
key={cluster.clusterId}
|
||||
index={index}
|
||||
orderInPage={cluster.orderInPage}
|
||||
isTemporary={false}
|
||||
cluster={cluster}
|
||||
pageId={this.pageId}
|
||||
applications={applications}
|
||||
swapClusters={this.swapClusters}
|
||||
removeLoadedCluster={this.removeLoadedCluster}/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withConfigContext(withRouter(Page));
|
@ -0,0 +1,132 @@
|
||||
<%
|
||||
var log = new Log("api/enterprise.jag");
|
||||
|
||||
var uri = request.getRequestURI();
|
||||
var uriMatcher = new URIMatcher(String(uri));
|
||||
|
||||
var constants = require("/app/modules/constants.js");
|
||||
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
|
||||
var userModule = require("/app/modules/business-controllers/user.js")["userModule"];
|
||||
var restAPIRequestDetails = request.getContent();
|
||||
var result;
|
||||
var user = session.get(constants.USER_SESSION_KEY);
|
||||
|
||||
// This checks if the session is valid
|
||||
getAccessToken = function() {
|
||||
if (session) {
|
||||
var tokenPair = session.get(constants["TOKEN_PAIR"]);
|
||||
if (tokenPair) {
|
||||
return parse(tokenPair)["accessToken"];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
callBackend = function(url, token, method, payload) {
|
||||
var xmlHttpRequest = new XMLHttpRequest();
|
||||
xmlHttpRequest.open(method, url);
|
||||
xmlHttpRequest.setRequestHeader(constants["AUTHORIZATION_HEADER"], constants["BEARER_PREFIX"] + token);
|
||||
xmlHttpRequest.setRequestHeader(constants["CONTENT_TYPE_IDENTIFIER"], constants["APPLICATION_JSON"]);
|
||||
xmlHttpRequest.setRequestHeader(constants["ACCEPT_IDENTIFIER"], constants["APPLICATION_JSON"]);
|
||||
if (payload) {
|
||||
xmlHttpRequest.send(payload);
|
||||
} else {
|
||||
xmlHttpRequest.send();
|
||||
}
|
||||
response["status"] = xmlHttpRequest["status"];
|
||||
if (xmlHttpRequest["responseText"]) {
|
||||
result = xmlHttpRequest["responseText"];
|
||||
response["content"] = xmlHttpRequest["responseText"];
|
||||
}
|
||||
};
|
||||
|
||||
var accessToken = getAccessToken();
|
||||
if (!user || accessToken == null) {
|
||||
response.sendRedirect("/devicemgt/login?#login-required");
|
||||
exit();
|
||||
} else {
|
||||
response.contentType = 'application/json';
|
||||
if (uriMatcher.match("/{context}/api/enterprise/token")) {
|
||||
session.put("externalEndpoint", restAPIRequestDetails["endpoint"]);
|
||||
session.put("externalToken", restAPIRequestDetails["externalToken"]);
|
||||
log.info("Calling get token");
|
||||
callBackend(restAPIRequestDetails["endpoint"], session.get("externalToken"), "POST", restAPIRequestDetails);
|
||||
if (response["status"] && response["status"] == 200) {
|
||||
var completionToken = parse(result)["completionToken"];
|
||||
if (completionToken) {
|
||||
log.info("Token received");
|
||||
session.put("completionToken", completionToken)
|
||||
}
|
||||
}
|
||||
} else if (uriMatcher.match("/{context}/api/enterprise/enroll-complete")) {
|
||||
var tokenEndpoint = session.get("externalEndpoint")
|
||||
var enterpriseEndpoint = tokenEndpoint.replace("signup-url", "complete-signup");
|
||||
|
||||
var completionToken = session.get("completionToken");
|
||||
var enterpriseToken = request.getParameter("enterpriseToken");
|
||||
|
||||
log.debug("completionToken" + completionToken + ", enterpriseEndpoint" + enterpriseEndpoint +
|
||||
", enterpriseToken" + enterpriseToken);
|
||||
|
||||
var requestPayload = {}
|
||||
requestPayload.completionToken = completionToken;
|
||||
requestPayload.enterpriseToken = enterpriseToken;
|
||||
log.info("Calling complete-signup");
|
||||
callBackend(enterpriseEndpoint, session.get("externalToken"), "POST", requestPayload);
|
||||
|
||||
var enterpriseId = parse(result)["id"];
|
||||
if (enterpriseId) {
|
||||
log.info("Calling complete-signup success");
|
||||
var serviceAccountRequest = {};
|
||||
serviceAccountRequest.enterpriseId = enterpriseId;
|
||||
serviceAccountRequest.keyType = "googleCredentials"
|
||||
|
||||
var enterpriseEndpoint = tokenEndpoint.replace("signup-url", "create-esa");
|
||||
|
||||
log.info("Calling create-esa");
|
||||
callBackend(enterpriseEndpoint, session.get("externalToken"), "POST", serviceAccountRequest);
|
||||
var data = parse(result)["data"];
|
||||
log.info("Calling create-esa success" + data);
|
||||
|
||||
var androidConfigAPI = devicemgtProps["httpsURL"] + "/api/device-mgt/android/v1.0/configuration";
|
||||
log.info("fetching platform configs");
|
||||
callBackend(androidConfigAPI, accessToken, "GET");
|
||||
|
||||
var configurationsList = parse(result);
|
||||
|
||||
for (var x = 0; x < configurationsList.configuration.length; x++) {
|
||||
if (configurationsList.configuration[x].name == "esa" || configurationsList.configuration[x].name == "enterpriseId") {
|
||||
configurationsList.configuration.splice(x, 1);
|
||||
}
|
||||
}
|
||||
log.info("fetching platform configs success");
|
||||
var payloadToAdd = {};
|
||||
payloadToAdd.contentType = "text";
|
||||
payloadToAdd.name = "esa";
|
||||
payloadToAdd.value = data;
|
||||
|
||||
var enterpriseIdPayload = {};
|
||||
enterpriseIdPayload.contentType = "text";
|
||||
enterpriseIdPayload.name = "enterpriseId";
|
||||
enterpriseIdPayload.value = enterpriseId;
|
||||
|
||||
configurationsList.configuration[configurationsList.configuration.length] = payloadToAdd;
|
||||
configurationsList.configuration[configurationsList.configuration.length] = enterpriseIdPayload;
|
||||
|
||||
log.info("saving platform configs");
|
||||
callBackend(androidConfigAPI, accessToken, "PUT", configurationsList);
|
||||
log.info("saving platform configs success");
|
||||
if (response["status"] == 200) {
|
||||
log.info("Process successful!! Redirecting...");
|
||||
response.sendRedirect("/devicemgt/platform-configuration?enterprise-success=true");
|
||||
}
|
||||
}
|
||||
} else if (uriMatcher.match("/{context}/api/enterprise/asdsad/unenroll")) {
|
||||
session.put("externalEndpoint", restAPIRequestDetails["endpoint"]);
|
||||
session.put("externalToken", restAPIRequestDetails["externalToken"]);
|
||||
callBackend(restAPIRequestDetails["endpoint"], session.get("externalToken"), "POST", restAPIRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%>
|
Loading…
Reference in new issue