From 8238f7dd22c6866778fd68a108086690c67c9898 Mon Sep 17 00:00:00 2001 From: Farheen Boosary Date: Wed, 18 Nov 2020 19:26:15 +0000 Subject: [PATCH] Add Windows enterprise app installation support for publisher and store --- .../application/mgt/common/DeviceTypes.java | 2 +- .../mgt/common/response/Application.java | 8 + .../common/response/ApplicationRelease.java | 8 + .../common/services/ApplicationManager.java | 10 + .../common/wrapper/EntAppReleaseWrapper.java | 24 ++ .../mgt/core/impl/ApplicationManagerImpl.java | 78 +++- .../core/impl/SubscriptionManagerImpl.java | 13 + .../application/mgt/core/util/APIUtil.java | 6 + .../application/mgt/core/util/Constants.java | 4 + ...ApplicationManagementPublisherAPIImpl.java | 4 + .../react-app/public/conf/config.json | 8 +- .../components/NewAppDetailsForm/index.js | 48 +++ .../components/NewAppUploadForm/index.js | 388 +++++++++++++++--- .../components/AddNewAppForm/index.js | 23 +- .../components/AddNewReleaseForm/index.js | 6 + .../ApssTable/AppDetailsDrawer/index.js | 1 + .../components/EditRelease/index.js | 211 ++++++---- .../Release/components/ReleaseView/index.js | 40 +- .../react-app/public/conf/config.json | 3 + .../components/ReleaseView/index.js | 41 +- .../device/mgt/common/MDMAppConstants.java | 25 +- .../carbon/device/mgt/common/app/mgt/App.java | 8 + .../mgt/windows/EnterpriseApplication.java | 54 +++ .../mgt/windows/HostedAppxApplication.java | 71 ++++ .../app/mgt/windows/HostedMSIApplication.java | 52 +++ .../core/util/MDMWindowsOperationUtil.java | 146 +++++++ 26 files changed, 1118 insertions(+), 164 deletions(-) create mode 100644 components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/windows/EnterpriseApplication.java create mode 100644 components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/windows/HostedAppxApplication.java create mode 100644 components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/windows/HostedMSIApplication.java create mode 100644 components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMWindowsOperationUtil.java diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java index 465b67cafd..7fa8a02062 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java @@ -17,5 +17,5 @@ package org.wso2.carbon.device.application.mgt.common; public enum DeviceTypes { - ANDROID, IOS + ANDROID, IOS, WINDOWS } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java index 778f7fc08d..0cd8a72783 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java @@ -31,6 +31,10 @@ public class Application { required = true) private String name; + @ApiModelProperty(name = "installerName", + value = "Application Installer Name") + private String installerName; + @ApiModelProperty(name = "description", value = "Description of the application", required = true) @@ -173,4 +177,8 @@ public class Application { public boolean isAndroidEnterpriseApp() { return isAndroidEnterpriseApp; } public void setAndroidEnterpriseApp(boolean androidEnterpriseApp) { isAndroidEnterpriseApp = androidEnterpriseApp; } + + public String getInstallerName() { return installerName; } + + public void setInstallerName(String installerName) { this.installerName = installerName; } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java index 36ed3e8648..25009bae1f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java @@ -87,6 +87,10 @@ public class ApplicationRelease { value = "Application Rating") private double rating; + @ApiModelProperty(name = "packageName", + value = "package name of the application") + private String packageName; + public String getReleaseType() { return releaseType; } @@ -162,4 +166,8 @@ public class ApplicationRelease { public List getScreenshots() { return screenshots; } public void setScreenshots(List screenshots) { this.screenshots = screenshots; } + + public String getPackageName() { return packageName; } + + public void setPackageName(String packageName) { this.packageName = packageName; } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java index ab031305f8..73f78478e7 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java @@ -281,6 +281,16 @@ public interface ApplicationManager { String getInstallableLifecycleState() throws ApplicationManagementException; + /** + * Check if there are subscription devices for operations + * + * @param operationId Id of the operation + * @param deviceId deviceId of the relevant device + * @return boolean value either true or false according to the situation + * @throws ApplicationManagementException + */ + boolean checkSubDeviceIdsForOperations(int operationId, int deviceId) throws ApplicationManagementException; + void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java index ad79d9e7f3..c3c7ddb84d 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java @@ -60,6 +60,14 @@ public class EntAppReleaseWrapper { @NotNull private String supportedOsVersions; + @ApiModelProperty(name = "version", + value = "Version number of the applications installer specifically for windows") + private String version; + + @ApiModelProperty(name = "packageName", + value = "PackageName of the application installer specifically for windows") + private String packageName; + public String getReleaseType() { return releaseType; } @@ -99,4 +107,20 @@ public class EntAppReleaseWrapper { public String getSupportedOsVersions() { return supportedOsVersions; } public void setSupportedOsVersions(String supportedOsVersions) { this.supportedOsVersions = supportedOsVersions; } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java index 1404c0c912..3e4942158f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java @@ -141,6 +141,7 @@ public class ApplicationManagerImpl implements ApplicationManager { } int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); ApplicationDTO applicationDTO = APIUtil.convertToAppDTO(applicationWrapper); + //uploading application artifacts ApplicationReleaseDTO applicationReleaseDTO = uploadEntAppReleaseArtifacts( applicationDTO.getApplicationReleaseDTOs().get(0), applicationArtifact, @@ -338,9 +339,25 @@ public class ApplicationManagerImpl implements ApplicationManager { byte[] content = IOUtils.toByteArray(applicationArtifact.getInstallerStream()); applicationReleaseDTO.setInstallerName(applicationArtifact.getInstallerName()); try (ByteArrayInputStream binary = new ByteArrayInputStream(content)) { - ApplicationInstaller applicationInstaller = applicationStorageManager - .getAppInstallerData(binary, deviceType); - String packagename = applicationInstaller.getPackageName(); + ApplicationInstaller applicationInstaller = null; + String packagename; + if (!DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceType)) { + applicationInstaller = applicationStorageManager.getAppInstallerData(binary, deviceType); + packagename = applicationInstaller.getPackageName(); + applicationReleaseDTO.setVersion(applicationInstaller.getVersion()); + applicationReleaseDTO.setPackageName(packagename); + } else { + String windowsInstallerName = applicationArtifact.getInstallerName(); + String extension = windowsInstallerName.substring(windowsInstallerName.lastIndexOf(".") + 1); + if (!extension.equalsIgnoreCase(Constants.MSI) && + !extension.equalsIgnoreCase(Constants.APPX)) { + String msg = "Application Type doesn't match with supporting application types of " + + deviceType + "platform which are APPX and MSI"; + log.error(msg); + throw new BadRequestException(msg); + } + packagename = applicationReleaseDTO.getPackageName(); + } try { ConnectionManagerUtil.openDBConnection(); @@ -354,8 +371,6 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg); throw new ApplicationManagementException(msg); } - applicationReleaseDTO.setVersion(applicationInstaller.getVersion()); - applicationReleaseDTO.setPackageName(packagename); String md5OfApp = StorageManagementUtil.getMD5(new ByteArrayInputStream(content)); if (md5OfApp == null) { String msg = "Error occurred while md5sum value retrieving process: application UUID " @@ -1012,6 +1027,7 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg); throw new BadRequestException(msg); } + ApplicationReleaseDTO applicationReleaseDTO = uploadEntAppReleaseArtifacts( APIUtil.releaseWrapperToReleaseDTO(entAppReleaseWrapper), applicationArtifact, deviceType.getName(), tenantId, true); @@ -2680,7 +2696,12 @@ public class ApplicationManagerImpl implements ApplicationManager { if (!StringUtils.isEmpty(entAppReleaseWrapper.getMetaData())) { applicationReleaseDTO.get().setMetaData(entAppReleaseWrapper.getMetaData()); } - + if (!StringUtils.isEmpty(entAppReleaseWrapper.getVersion())) { // Updating version + applicationReleaseDTO.get().setVersion(entAppReleaseWrapper.getVersion()); + } + if (!StringUtils.isEmpty(entAppReleaseWrapper.getPackageName())) { // Updating packageName + applicationReleaseDTO.get().setPackageName(entAppReleaseWrapper.getPackageName()); + } if (!StringUtils.isEmpty(applicationArtifact.getInstallerName()) && applicationArtifact.getInstallerStream() != null) { DeviceType deviceTypeObj = APIUtil.getDeviceTypeData(applicationDTO.getDeviceTypeId()); @@ -3114,6 +3135,17 @@ public class ApplicationManagerImpl implements ApplicationManager { throw new BadRequestException(msg); } unrestrictedRoles = applicationWrapper.getUnrestrictedRoles(); + + //Validating the version number and the packageName of the Windows applications + if (DeviceTypes.WINDOWS.toString().equalsIgnoreCase(applicationWrapper.getDeviceType())) { + if (applicationWrapper.getEntAppReleaseWrappers().get(0).getVersion() == null || + applicationWrapper.getEntAppReleaseWrappers().get(0).getPackageName() == null) { + String msg = "Application Version number or/and PackageName both are required only when the app type is " + + applicationWrapper.getDeviceType() + " platform type"; + log.error(msg); + throw new BadRequestException(msg); + } + } } else if (param instanceof WebAppWrapper) { WebAppWrapper webAppWrapper = (WebAppWrapper) param; appName = webAppWrapper.getName(); @@ -3326,6 +3358,15 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg); throw new BadRequestException(msg); } + //Validating the version number and the packageName of the Windows new applications releases + if (DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceType)) { + if (entAppReleaseWrapper.get().getVersion() == null || entAppReleaseWrapper.get().getPackageName() == null) { + String msg = "Application Version number or/and PackageName..both are required only when the app type is " + + deviceType + " platform type"; + log.error(msg); + throw new BadRequestException(msg); + } + } } else if (param instanceof WebAppReleaseWrapper) { WebAppReleaseWrapper webAppReleaseWrapper = (WebAppReleaseWrapper) param; UrlValidator urlValidator = new UrlValidator(); @@ -3414,18 +3455,31 @@ public class ApplicationManagerImpl implements ApplicationManager { } } + @Override + public boolean checkSubDeviceIdsForOperations(int operationId, int deviceId) throws ApplicationManagementException { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + try { + ConnectionManagerUtil.openDBConnection(); + List deviceSubIds = subscriptionDAO.getDeviceSubIdsForOperation(operationId, deviceId, tenantId); + if (deviceSubIds.isEmpty() || deviceSubIds == null) { + return false; + } + } catch (ApplicationManagementDAOException e) { + String msg = "Error occurred while getting the device sub ids for the operations"; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + return true; + } + @Override public void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException { int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); try { ConnectionManagerUtil.beginDBTransaction(); List deviceSubIds = subscriptionDAO.getDeviceSubIdsForOperation(operationId, deviceId, tenantId); - if (deviceSubIds.isEmpty()){ - ConnectionManagerUtil.rollbackDBTransaction(); - String msg = "Couldn't find device subscription for operation id " + operationId; - log.error(msg); - throw new ApplicationManagementException(msg); - } if (!subscriptionDAO.updateDeviceSubStatus(deviceId, deviceSubIds, status, tenantId)){ ConnectionManagerUtil.rollbackDBTransaction(); String msg = "Didn't update an any app subscription of device for operation Id: " + operationId; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java index c844ddaf08..248b0d272c 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java @@ -82,6 +82,7 @@ import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService; import org.wso2.carbon.device.mgt.core.util.MDMAndroidOperationUtil; import org.wso2.carbon.device.mgt.core.util.MDMIOSOperationUtil; +import org.wso2.carbon.device.mgt.core.util.MDMWindowsOperationUtil; import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.device.mgt.common.PaginationResult; @@ -1022,6 +1023,18 @@ public class SubscriptionManagerImpl implements SubscriptionManager { log.error(msg); throw new ApplicationManagementException(msg); } + } else if (DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceType)) { + app.setType(mobileAppType); + app.setIdentifier(application.getPackageName()); + app.setMetaData(application.getApplicationReleases().get(0).getMetaData()); + app.setName(application.getInstallerName()); + if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { + return MDMWindowsOperationUtil.createInstallAppOperation(app); + } else { + String msg = "Invalid Action is found. Action: " + action; + log.error(msg); + throw new ApplicationManagementException(msg); + } } else { String msg = "Invalid device type is found. Device Type: " + deviceType; log.error(msg); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java index 12d28191b7..a2375f1d55 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java @@ -304,6 +304,10 @@ public class APIUtil { applicationReleaseDTO.setIsSharedWithAllTenants(entAppReleaseWrapper.getIsSharedWithAllTenants()); applicationReleaseDTO.setMetaData(entAppReleaseWrapper.getMetaData()); applicationReleaseDTO.setSupportedOsVersions(entAppReleaseWrapper.getSupportedOsVersions()); + //Setting version number value specifically for windows type and in an instance of android and ios it will be null + applicationReleaseDTO.setVersion(entAppReleaseWrapper.getVersion()); + //Setting package name value specifically for windows type and in an instance of android and ios it will be null + applicationReleaseDTO.setPackageName(entAppReleaseWrapper.getPackageName()); } else if (param instanceof WebAppReleaseWrapper){ WebAppReleaseWrapper webAppReleaseWrapper = (WebAppReleaseWrapper) param; applicationReleaseDTO.setDescription(webAppReleaseWrapper.getDescription()); @@ -358,6 +362,7 @@ public class APIUtil { application.setTags(applicationDTO.getTags()); application.setUnrestrictedRoles(applicationDTO.getUnrestrictedRoles()); application.setRating(applicationDTO.getAppRating()); + application.setInstallerName(applicationDTO.getApplicationReleaseDTOs().get(0).getInstallerName()); List applicationReleases = new ArrayList<>(); if (ApplicationType.PUBLIC.toString().equals(applicationDTO.getType()) && application.getCategories() .contains("GooglePlaySyncedApp")) { @@ -384,6 +389,7 @@ public class APIUtil { applicationRelease.setDescription(applicationReleaseDTO.getDescription()); applicationRelease.setVersion(applicationReleaseDTO.getVersion()); + applicationRelease.setPackageName(applicationReleaseDTO.getPackageName()); applicationRelease.setUuid(applicationReleaseDTO.getUuid()); applicationRelease.setReleaseType(applicationReleaseDTO.getReleaseType()); applicationRelease.setPrice(applicationReleaseDTO.getPrice()); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java index 11ca234e1a..c18224f417 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java @@ -65,6 +65,10 @@ public class Constants { public static final String SUBSCRIBED = "SUBSCRIBED"; public static final String UNSUBSCRIBED = "UNSUBSCRIBED"; + //App type constants related to window device type + public static final String MSI = "MSI"; + public static final String APPX = "APPX"; + private static final Map AGENT_DATA = new HashMap<>(); static { AGENT_DATA.put("android", "android-agent.apk"); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java index 2b53e65da7..74f4b4ee75 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java @@ -373,6 +373,10 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem log.error("ApplicationDTO Creation Failed"); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } + } catch (BadRequestException e) { + String msg = "Found incompatible payload with enterprise app release creating request."; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } catch (ApplicationManagementException e) { String msg = "Error occurred while creating the application"; log.error(msg, e); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json index de1726ce39..9264aac6d7 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json @@ -85,6 +85,12 @@ } }, "deviceTypes": { - "mobileTypes": ["android", "ios"] + "mobileTypes": ["android", "ios", "windows"] + }, + "windowsDeviceType": { + "appType": ["msi", "appx"] + }, + "windowsAppxMsiKeyValueForMetaData": { + "metaKeyArray": ["Package_Url", "Dependency_Package_Url", "Certificate_Hash", "Encoded_Cert_Content", "Package_Family_Name", "Product_Id", "Content_Uri", "File_Hash"] } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js index 4219e5fcd6..c1bfc1f5c0 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js @@ -46,6 +46,7 @@ class NewAppDetailsForm extends React.Component { categories: [], tags: [], deviceTypes: [], + selectedValue: null, fetching: false, roleSearchValue: [], unrestrictedRoles: [], @@ -312,12 +313,33 @@ class NewAppDetailsForm extends React.Component { }); }; + // Event handler for selecting the device type + handleSelect = event => { + this.setState({ + selectedValue: event, + }); + if (this.props.selectedValueHandler) { + this.props.selectedValueHandler(event); + } + }; + + // Event handler for selecting the windows app type + handleSelectForAppType = event => { + if (this.props.selectedAppTypeHandler) { + this.props.selectedAppTypeHandler(event); + } + }; + render() { + const config = this.props.context; + // Windows installation app types + const appTypes = config.windowsDeviceType.appType; const { formConfig } = this.props; const { categories, tags, deviceTypes, + selectedValue, fetching, unrestrictedRoles, } = this.state; @@ -358,6 +380,7 @@ class NewAppDetailsForm extends React.Component { )} + {/* App Type only shown for windows device types for enterprise apps */} + {selectedValue === 'windows' && + this.props.formConfig.installationType === 'ENTERPRISE' && ( + + {getFieldDecorator('appType', { + rules: [ + { + required: true, + message: 'Please select app type', + }, + ], + })( + , + )} + + )} + {/* description*/} {getFieldDecorator('description', { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js index 32368cb26a..eb918dc423 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js @@ -33,6 +33,7 @@ import { } from 'antd'; import '@babel/polyfill'; import Authorized from '../../../../../../../../components/Authorized/Authorized'; +import { withConfigContext } from '../../../../../../../../components/ConfigContext'; const { Text } = Typography; @@ -59,6 +60,12 @@ function getBase64(file) { }); } +// function for access the full name of the binary file using the installation path +function extractBinaryFileName(installationPath) { + let UploadedBinaryName = installationPath.split('/'); + return UploadedBinaryName[UploadedBinaryName.length - 1]; +} + class NewAppUploadForm extends React.Component { constructor(props) { super(props); @@ -77,6 +84,7 @@ class NewAppUploadForm extends React.Component { osVersionsHelperText: '', osVersionsValidateStatus: 'validating', metaData: [], + appType: null, }; this.lowerOsVersion = null; this.upperOsVersion = null; @@ -93,6 +101,8 @@ class NewAppUploadForm extends React.Component { e.preventDefault(); const { formConfig } = this.props; const { specificElements } = formConfig; + let windowsAppTypeMetaArray = []; + let metaValue = []; this.props.form.validateFields((err, values) => { if (!err) { @@ -107,6 +117,19 @@ class NewAppUploadForm extends React.Component { releaseType, } = values; + /** + * To save the metaData value that receive from + * metaData UI In an windows app type creation + */ + if ( + ((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows') && + this.state.metaData.length !== 0 + ) { + metaValue = [...this.state.metaData]; + } + // add release data const release = { description: releaseDescription, @@ -116,6 +139,90 @@ class NewAppUploadForm extends React.Component { releaseType: releaseType, }; + const data = new FormData(); + const config = this.props.context; + // Accessing the Meta Key value for windows device type from the config.json file + const metaKeyValues = + config.windowsAppxMsiKeyValueForMetaData.metaKeyArray; + + /* + Setting up the app type specific values to the + metaData state field and Setting up the version + and the packageName for windows type + */ + if ( + (this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows' + ) { + // Setting up the version and packageName + release.version = values.version; + release.packageName = values.packageName; + // setting the metaData value for appx type + if ( + this.props.selectedAppType === 'appx' || + this.state.appType === 'appx' + ) { + windowsAppTypeMetaArray = [ + { + key: metaKeyValues[0], + value: values.packageUrl, + }, + { + key: metaKeyValues[1], + value: values.dependencyPackageUrl, + }, + { + key: metaKeyValues[2], + value: values.certificateHash, + }, + { + key: metaKeyValues[3], + value: values.encodedCertContent, + }, + { + key: metaKeyValues[4], + value: values.packageName, + }, + ]; + windowsAppTypeMetaArray = [ + ...windowsAppTypeMetaArray, + ...metaValue, + ]; + } else if ( + this.props.selectedAppType === 'msi' || + this.state.appType === 'msi' + ) { + windowsAppTypeMetaArray = [ + { + key: metaKeyValues[5], + value: values.productId, + }, + { + key: metaKeyValues[6], + value: values.contentUri, + }, + { + key: metaKeyValues[7], + value: values.fileHash, + }, + ]; + windowsAppTypeMetaArray = [ + ...windowsAppTypeMetaArray, + ...metaValue, + ]; + } + this.setState( + { + metaData: windowsAppTypeMetaArray, + }, + () => { + release.metaData = JSON.stringify(this.state.metaData); + this.props.onSuccessReleaseData({ data, release }); + }, + ); + } + if (specificElements.hasOwnProperty('version')) { release.version = values.version; } @@ -126,7 +233,6 @@ class NewAppUploadForm extends React.Component { release.packageName = values.packageName; } - const data = new FormData(); let isFormValid = true; // flag to check if this form is valid if ( @@ -187,7 +293,16 @@ class NewAppUploadForm extends React.Component { if (specificElements.hasOwnProperty('binaryFile')) { data.append('binaryFile', binaryFile[0].originFileObj); } - this.props.onSuccessReleaseData({ data, release }); + // Condition to check is it not an Enterprise windows app creation or release + if ( + !( + this.props.selectedValue === 'windows' && + this.props.formConfig.installationType === 'ENTERPRISE' + ) && + this.props.deviceType !== 'windows' + ) { + this.props.onSuccessReleaseData({ data, release }); + } } } }); @@ -203,13 +318,44 @@ class NewAppUploadForm extends React.Component { icons: fileList, }); }; + handleBinaryFileChange = ({ fileList }) => { + let validity = true; + // To set the app type of windows by using the binary file in an new app release + if (this.props.formConfig.isNewRelease && fileList.length !== 0) { + let firstUploadedBinaryFileName = extractBinaryFileName( + this.props.uploadedInstalltionAppType, + ); + let FirstFileExtension = firstUploadedBinaryFileName.substr( + firstUploadedBinaryFileName.lastIndexOf('.') + 1, + ); + let LastFileExtension = fileList[0].name.substr( + fileList[0].name.lastIndexOf('.') + 1, + ); + if (FirstFileExtension !== LastFileExtension) { + validity = false; + } else if (LastFileExtension === 'msi' || LastFileExtension === 'appx') { + this.setState({ + appType: LastFileExtension, + }); + } + } + if (fileList.length === 1) { this.setState({ binaryFileHelperText: '', }); } - this.setState({ binaryFiles: fileList }); + + if (validity) { + this.setState({ + binaryFiles: fileList, + }); + } else { + this.setState({ + binaryFileHelperText: 'Upload Correct Binary File extension', + }); + } }; handleScreenshotChange = ({ fileList }) => { @@ -266,6 +412,7 @@ class NewAppUploadForm extends React.Component { render() { const { formConfig, supportedOsVersions } = this.props; const { getFieldDecorator } = this.props.form; + const config = this.props.context; const { icons, screenshots, @@ -399,7 +546,12 @@ class NewAppUploadForm extends React.Component { - {formConfig.specificElements.hasOwnProperty('packageName') && ( + + {/* Package Name field for windows device type and other specific scene using it */} + {(formConfig.specificElements.hasOwnProperty('packageName') || + (this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows') && ( {getFieldDecorator('packageName', { rules: [ @@ -425,7 +577,11 @@ class NewAppUploadForm extends React.Component { )} - {formConfig.specificElements.hasOwnProperty('version') && ( + {/* Version field for windows device type and other specific scene using it */} + {(formConfig.specificElements.hasOwnProperty('version') || + (this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows') && ( {getFieldDecorator('version', { rules: [ @@ -438,6 +594,127 @@ class NewAppUploadForm extends React.Component { )} + {/* Windows Appx App Type Fields */} + {/* For Windows appx app type only -> Package Url */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('packageUrl', { + rules: [ + { + required: true, + message: 'Please input the package url', + }, + ], + })()} + + )} + + {/* For Windows appx app type only -> Dependency Package Url */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('dependencyPackageUrl', {})( + , + )} + + )} + + {/* For Windows appx app type only -> Certificate Hash */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('certificateHash', { + rules: [ + { + required: true, + message: 'Please input the certificate hash', + }, + ], + })()} + + )} + + {/* For Windows appx app type only -> Encoded Certificate Content */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('encodedCertContent', { + rules: [ + { + required: true, + message: 'Give the encoded cert content', + }, + ], + })( +