From 7727b28980b58139be226af765356739bd423686 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 16 Apr 2024 09:04:02 -0300 Subject: [PATCH 01/22] Add first version --- api/pom.xml | 9 + .../main/java/com/cloud/event/EventTypes.java | 10 + .../apache/cloudstack/api/ApiConstants.java | 14 + .../cloudstack/api/ResponseGenerator.java | 4 + .../user/gui/themes/CreateGuiThemeCmd.java | 125 ++++++ .../user/gui/themes/ListGuiThemesCmd.java | 109 +++++ .../user/gui/themes/RemoveGuiThemeCmd.java | 61 +++ .../user/gui/themes/UpdateGuiThemeCmd.java | 129 ++++++ .../api/response/GuiThemeResponse.java | 167 ++++++++ .../gui/themes/GuiThemeService.java | 19 + .../cloudstack/gui/themes/GuiThemeVO.java | 198 +++++++++ .../cloudstack/gui/theme/dao/GuiThemeDao.java | 16 + .../gui/theme/dao/GuiThemeDaoImpl.java | 110 +++++ ...spring-engine-schema-core-daos-context.xml | 1 + .../META-INF/db/schema-41900to42000.sql | 98 +++++ .../java/com/cloud/api/ApiResponseHelper.java | 22 + .../main/java/com/cloud/api/ApiServer.java | 4 + .../main/java/com/cloud/api/ApiServlet.java | 17 + .../cloud/server/ManagementServerImpl.java | 8 + .../gui/theme/GuiThemeServiceImpl.java | 405 ++++++++++++++++++ .../spring-server-core-managers-context.xml | 1 + .../gui/theme/GuiThemeServiceImplTest.java | 225 ++++++++++ tools/apidoc/gen_toc.py | 6 +- ui/public/config.json | 1 + ui/src/api/index.js | 11 +- ui/src/components/header/UserMenu.vue | 6 +- ui/src/main.js | 19 +- ui/src/store/modules/user.js | 10 +- ui/src/utils/guiTheme.js | 100 +++++ 29 files changed, 1897 insertions(+), 8 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java create mode 100644 api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java create mode 100644 api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java create mode 100644 engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql create mode 100644 server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java create mode 100644 server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java create mode 100644 ui/src/utils/guiTheme.js diff --git a/api/pom.xml b/api/pom.xml index ec68e24c7e59..fdaa9f185852 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -71,6 +71,15 @@ cloud-framework-direct-download ${project.version} + + org.eclipse.persistence + javax.persistence + + + org.apache.cloudstack + cloud-framework-db + ${project.version} + diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 88050349bc99..fa92c2ccd55b 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -806,6 +806,11 @@ public class EventTypes { public static final String VM_LEASE_CANCELLED = "VM.LEASE.CANCELLED"; public static final String VM_LEASE_EXPIRING = "VM.LEASE.EXPIRING"; + // GUI Theme + public static final String EVENT_GUI_THEME_CREATE = "GUI.THEME.CREATE"; + public static final String EVENT_GUI_THEME_REMOVE = "GUI.THEME.REMOVE"; + public static final String EVENT_GUI_THEME_UPDATE = "GUI.THEME.UPDATE"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking @@ -1306,6 +1311,11 @@ public class EventTypes { entityEventDetails.put(VM_LEASE_EXPIRING, VirtualMachine.class); entityEventDetails.put(VM_LEASE_DISABLED, VirtualMachine.class); entityEventDetails.put(VM_LEASE_CANCELLED, VirtualMachine.class); + + // GUI theme + entityEventDetails.put(EVENT_GUI_THEME_CREATE, "GuiTheme"); + entityEventDetails.put(EVENT_GUI_THEME_REMOVE, "GuiTheme"); + entityEventDetails.put(EVENT_GUI_THEME_UPDATE, "GuiTheme"); } public static boolean isNetworkEvent(String eventType) { diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 8fe01fd794c8..4c30569b2f46 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1255,6 +1255,20 @@ public class ApiConstants { public static final String VMWARE_DC = "vmwaredc"; + public static final String CSS = "css"; + + public static final String JSON_CONFIGURATION = "jsonconfiguration"; + + public static final String COMMON_NAMES = "commonnames"; + + public static final String COMMON_NAME = "commonname"; + + public static final String DOMAIN_IDS = "domainids"; + + public static final String SHOW_PUBLIC = "showpublic"; + + public static final String LIST_ONLY_DEFAULT_THEME = "listonlydefaulttheme"; + /** * This enum specifies IO Drivers, each option controls specific policies on I/O. * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 3288eb58d755..5424e9acf720 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -64,6 +64,7 @@ import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; import org.apache.cloudstack.api.response.GuestVlanResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; @@ -150,6 +151,7 @@ import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; +import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; @@ -579,4 +581,6 @@ List createTemplateResponses(ResponseView view, VirtualMachine SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS sharedFS); void updateTemplateIsoResponsesForIcons(List responses, ResourceTag.ResourceObjectType type); + + GuiThemeResponse createGuiThemeResponse(GuiThemeVO guiThemeVO); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java new file mode 100644 index 000000000000..9183c6b0b8f7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java @@ -0,0 +1,125 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.api.command.user.gui.themes; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "createGuiTheme", description = "Creates a customized GUI theme for a set of Common Names (fixed or wildcard), a set of domain UUIDs, and/or a set of " + + "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.18.0.4-scclouds", authorized = {RoleType.Admin}) +public class CreateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.NAME, required = true, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public void setIsPublic(Boolean isPublic) { + this.isPublic = isPublic; + } + + @Override + public void execute() { + CallContext.current().setEventDetails(String.format("Name: %s, AccountIDs: %s, DomainIDs: %s, CommonNames: %s", getName(), getAccountIds(), getDomainIds(), getCommonNames())); + GuiThemeVO guiThemeVO = guiThemeService.createGuiTheme(this); + + if (guiThemeVO == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeVO); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java new file mode 100644 index 000000000000..9f2abea586ac --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java @@ -0,0 +1,109 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.api.command.user.gui.themes; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, + since = "4.18.0.4-scclouds", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, RoleType.ResourceAdmin}) +public class ListGuiThemesCmd extends BaseListCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "The theme ID.", entityType = GuiThemeResponse.class) + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the theme.") + private String name; + + @Parameter(name = ApiConstants.COMMON_NAME, type = CommandType.STRING, description = "The internet Common Name (CN) to be filtered.") + private String commonName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The ID of the domain to be filtered.") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "The ID of the account to be filtered.") + private Long accountId; + + @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "Whether to list all themes.") + private boolean listAll = false; + + @Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "Whether to list removed themes.") + private boolean showRemoved = false; + + @Parameter(name = ApiConstants.SHOW_PUBLIC, type = CommandType.BOOLEAN, description = "Whether to list public themes.") + private Boolean showPublic; + + @Parameter(name = ApiConstants.LIST_ONLY_DEFAULT_THEME, type = CommandType.BOOLEAN, description = "Whether to only list the default theme.") + private boolean listOnlyDefaultTheme = false; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getCommonName() { + return commonName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getAccountId() { + return accountId; + } + + public boolean getListAll() { + return listAll; + } + + public boolean getShowRemoved() { + return showRemoved; + } + + public Boolean getShowPublic() { + return showPublic; + } + + public boolean getListOnlyDefaultTheme() { + return listOnlyDefaultTheme; + } + + @Override + public void execute() { + ListResponse response = guiThemeService.listGuiThemes(this); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java new file mode 100644 index 000000000000..8c8f46c7bf75 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.api.command.user.gui.themes; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, + since = "4.18.0.4-scclouds", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class RemoveGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuiThemeResponse.class, required = true, + description = "The unique identifier of the GUI theme to be removed.") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() { + CallContext.current().setEventDetails(String.format("ID: %s", getId())); + guiThemeService.removeGuiTheme(this); + final SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + response.setSuccess(true); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java new file mode 100644 index 000000000000..651c8d5c04b7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.api.command.user.gui.themes; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeService; + +import javax.inject.Inject; + + +@APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, + since = "4.18.0.4-scclouds", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class UpdateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, required = true, type = CommandType.UUID, entityType = GuiThemeResponse.class, description = "The ID of the theme to be updated.") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getIsPublic() { + return isPublic; + } + + @Override + public void execute() { + CallContext.current().setEventDetails(String.format("ID: %s, Name: %s, AccountIDs: %s, DomainIDs: %s, CommonNames: %s", getId(), getName(), getAccountIds(), + getDomainIds(), getCommonNames())); + GuiThemeVO guiThemeVO = guiThemeService.updateGuiTheme(this); + + if (guiThemeVO == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeVO); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java new file mode 100644 index 000000000000..726520b72202 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -0,0 +1,167 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF 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.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gui.themes.GuiThemeVO; + +import java.util.Date; + +@EntityReference(value = {GuiThemeVO.class}) +public class GuiThemeResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the custom GUI theme.") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the GUI theme.") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the GUI theme.") + private String description; + + @SerializedName(ApiConstants.CSS) + @Param(description = "The CSS to be retrieved and imported into the GUI when matching the theme access configurations.") + private String css; + + @SerializedName(ApiConstants.JSON_CONFIGURATION) + @Param(description = "The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @SerializedName(ApiConstants.COMMON_NAMES) + @Param(description = "A set of Common Names (CN) (fixed or wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @SerializedName(ApiConstants.DOMAIN_IDS) + @Param(description = "A set of domain UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @SerializedName(ApiConstants.ACCOUNT_IDS) + @Param(description = "A set of account UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @SerializedName(ApiConstants.IS_PUBLIC) + @Param(description = "Defines whether a theme can be retrieved by anyone when only the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is " + + "considered as `false`.") + private Boolean isPublic; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "When the GUI theme was created.") + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "When the GUI theme was removed.") + private Date removed; + + public GuiThemeResponse() { + super("guiThemes"); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCss() { + return css; + } + + public void setCss(String css) { + this.css = css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public void setJsonConfiguration(String jsonConfiguration) { + this.jsonConfiguration = jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public void setCommonNames(String commonNames) { + this.commonNames = commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public void setDomainIds(String domainIds) { + this.domainIds = domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public void setAccountIds(String accountIds) { + this.accountIds = accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public void setPublic(Boolean aPublic) { + isPublic = aPublic; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java new file mode 100644 index 000000000000..9622a15eca9a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java @@ -0,0 +1,19 @@ +package org.apache.cloudstack.gui.themes; + +import org.apache.cloudstack.api.command.user.gui.themes.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.themes.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.themes.UpdateGuiThemeCmd; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.ListResponse; + +public interface GuiThemeService { + + ListResponse listGuiThemes(ListGuiThemesCmd cmd); + + GuiThemeVO createGuiTheme(CreateGuiThemeCmd cmd); + + GuiThemeVO updateGuiTheme(UpdateGuiThemeCmd cmd); + + void removeGuiTheme(RemoveGuiThemeCmd cmd); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java new file mode 100644 index 000000000000..86170e721a63 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java @@ -0,0 +1,198 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.themes; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "gui_themes") +public class GuiThemeVO implements InternalIdentity, Identity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid = UUID.randomUUID().toString(); + + @Column(name = "name", nullable = false, length = 2048) + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "css", nullable = false, length = 65535) + private String css; + + @Column(name = "json_configuration", nullable = false, length = 65535) + private String jsonConfiguration; + + @Column(name = "common_names", length = 65535) + private String commonNames; + + @Column(name = "domain_uuids", length = 65535) + private String domainUuids; + + @Column(name = "account_uuids", length = 65535) + private String accountUuids; + + @Column(name = "is_public") + private boolean isPublic; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + public GuiThemeVO() { + + } + + public GuiThemeVO(String name, String description, String css, String jsonConfiguration, String commonNames, String domainUuids, String accountUuids, + boolean isPublic, Date created, Date removed) { + this.name = name; + this.description = description; + this.css = css; + this.jsonConfiguration = jsonConfiguration; + this.commonNames = commonNames; + this.domainUuids = domainUuids; + this.accountUuids = accountUuids; + this.isPublic = isPublic; + this.created = created; + this.removed = removed; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainUuids() { + return domainUuids; + } + + public String getAccountUuids() { + return accountUuids; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + public boolean getIsPublic() { + return isPublic; + } + + public void setId(Long id) { + this.id = id; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setName(String name) { + this.name = name; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setCss(String css) { + this.css = css; + } + + public void setJsonConfiguration(String jsonConfiguration) { + this.jsonConfiguration = jsonConfiguration; + } + + public void setCommonNames(String commonNames) { + this.commonNames = commonNames; + } + + public void setDomainUuids(String domainUuids) { + this.domainUuids = domainUuids; + } + + public void setAccountUuids(String accountUuids) { + this.accountUuids = accountUuids; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public void setIsPublic(boolean isPublic) { + this.isPublic = isPublic; + } + + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "description", "isPublic"); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java new file mode 100644 index 000000000000..6df15749fcd4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java @@ -0,0 +1,16 @@ +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.themes.GuiThemeVO; + +import java.util.List; + +public interface GuiThemeDao extends GenericDao { + + GuiThemeVO findDefaultTheme(); + + Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainId, String accountId, boolean listAll, boolean showRemoved, Boolean showPublic); + + Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java new file mode 100644 index 000000000000..ebba45aab299 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +public class GuiThemeDaoImpl extends GenericDaoBase implements GuiThemeDao { + + @Override + public GuiThemeVO findDefaultTheme() { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("commonNames", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.NULL); + searchBuilder.and("domainUuids", searchBuilder.entity().getDomainUuids(), SearchCriteria.Op.NULL); + searchBuilder.and("accountUuids", searchBuilder.entity().getAccountUuids(), SearchCriteria.Op.NULL); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + + return findOneBy(searchCriteria); + } + + @Override + public Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, + Boolean showPublic) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(id, name, commonName, domainUuid, accountUuid, showPublic, listAll); + + if (listAll) { + showRemoved = false; + } + + return searchAndCount(searchCriteria, null, showRemoved); + } + + private SearchCriteria createGuiThemeSearchCriteria(Long id, String name, String commonName, String domainUuid, String accountUuid, Boolean showPublic, boolean listAll) { + SearchCriteria searchCriteria = createGuiThemeSearchBuilder(accountUuid, domainUuid, commonName, listAll, showPublic).create(); + + searchCriteria.setParametersIfNotNull("id", id); + searchCriteria.setParametersIfNotNull("name", name); + searchCriteria.setParametersIfNotNull("isPublic", showPublic); + + if (StringUtils.isNotBlank(commonName)) { + searchCriteria.setParameters("commonName", "%" + commonName + "%"); + } + + if (StringUtils.isNotBlank(domainUuid)) { + searchCriteria.setParameters("domainId", "%" + domainUuid + "%"); + } + + if (StringUtils.isNotBlank(accountUuid)) { + searchCriteria.setParameters("accountId", "%" + accountUuid + "%"); + } + + return searchCriteria; + } + + private SearchBuilder createGuiThemeSearchBuilder(String accountUuid, String domainUuid, String commonName, boolean listAll, Boolean showPublic) { + SearchBuilder searchBuilder = createSearchBuilder(); + + searchBuilder.and("id", searchBuilder.entity().getId(), SearchCriteria.Op.EQ); + searchBuilder.and("name", searchBuilder.entity().getName(), SearchCriteria.Op.EQ); + + if (accountUuid != null) { + searchBuilder.and("accountId", searchBuilder.entity().getAccountUuids(), SearchCriteria.Op.LIKE); + } + + if (domainUuid != null) { + searchBuilder.and("domainId", searchBuilder.entity().getDomainUuids(), SearchCriteria.Op.LIKE); + } + + if (commonName != null) { + searchBuilder.and("commonName", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.LIKE); + } + + if (!listAll && showPublic != null) { + searchBuilder.and("isPublic", searchBuilder.entity().getIsPublic(), SearchCriteria.Op.EQ); + } + + searchBuilder.done(); + return searchBuilder; + } + + @Override + public Pair, Integer> listGuiThemesWithNoAuthentication(String commonName) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(null, null, commonName, null, null, null, false); + Filter filter = new Filter(GuiThemeVO.class, "created", false); + + return searchAndCount(searchCriteria, filter, false); + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index d05635f4614c..724283b0544a 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -301,4 +301,5 @@ + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql new file mode 100644 index 000000000000..fc3f8f32b541 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql @@ -0,0 +1,98 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF 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. + +--; +-- Schema upgrade from 4.19.0.0 to 4.20.0.0 +--; + +-- Add tag column to tables +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_limit', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the limit" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_count', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the resource count" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the resource reservation" '); +ALTER TABLE `resource_count` +DROP INDEX `i_resource_count__type_accountId`, +DROP INDEX `i_resource_count__type_domaintId`, +ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`), +ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`); + + +ALTER TABLE `cloud`.`resource_reservation` + ADD COLUMN `resource_id` bigint unsigned NULL; + +ALTER TABLE `cloud`.`resource_reservation` + MODIFY COLUMN `amount` bigint NOT NULL; + + +-- Update Default System offering for Router to 512MiB +UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local", + "Cloud.Com-InternalLBVm", "Cloud.Com-InternalLBVm-Local", + "Cloud.Com-ElasticLBVm", "Cloud.Com-ElasticLBVm-Local") + AND system_use = 1 AND ram_size < 512; + +-- NSX Plugin -- +CREATE TABLE `cloud`.`nsx_providers` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40), + `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', + `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', + `provider_name` varchar(40), + `hostname` varchar(255) NOT NULL, + `port` varchar(255), + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `tier0_gateway` varchar(255), + `edge_cluster` varchar(255), + `transport_zone` varchar(255), + `created` datetime NOT NULL COMMENT 'date created', + `removed` datetime COMMENT 'date removed if not null', + PRIMARY KEY (`id`), + CONSTRAINT `fk_nsx_providers__zone_id` FOREIGN KEY `fk_nsx_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + INDEX `i_nsx_providers__zone_id`(`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- NSX Plugin -- +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); + + +-- Create table to persist quota email template configurations +CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`( + `account_id` int(11) NOT NULL, + `email_template_id` bigint(20) NOT NULL, + `enabled` int(1) UNSIGNED NOT NULL, + PRIMARY KEY (`account_id`, `email_template_id`), + CONSTRAINT `FK_quota_email_configuration_account_id` FOREIGN KEY (`account_id`) REFERENCES `cloud_usage`.`quota_account`(`account_id`), + CONSTRAINT `FK_quota_email_configuration_email_template_id` FOREIGN KEY (`email_template_id`) REFERENCES `cloud_usage`.`quota_email_templates`(`id`)); + +-- Whitelabel GUI +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `uuid` varchar(255) UNIQUE, + `name` varchar(2048) NOT NULL COMMENT 'A name to identify the theme.', + `description` varchar(4096) DEFAULT NULL COMMENT 'A description for the theme.', + `css` text DEFAULT NULL COMMENT 'The CSS to be retrieved and imported into the GUI when matching the theme access configurations.', + `json_configuration` text DEFAULT NULL COMMENT 'The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.', + `common_names` text DEFAULT NULL COMMENT 'A set of internet Common Names (CN), fixed of wildcard, separated by comma that can use the theme; e.g.: *acme.com,acme2.com', + `domain_uuids` text DEFAULT NULL COMMENT 'A set of domain IDs separated by comma that can use the theme.', + `account_uuids` text DEFAULT NULL COMMENT 'A set of account IDs separated by comma that can use the theme.', + `is_public` tinyint(1) default 1 COMMENT 'Defines whether a theme can be retrieved by anyone when only the `internet_domains_names` is informed. If the `domain_uuids` or `account_uuids` is informed, it is considered as `false`.', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +); diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index ef65c4c3120e..ef4ead1582ca 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -97,6 +97,7 @@ import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; import org.apache.cloudstack.api.response.GuestVlanResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; @@ -206,6 +207,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.BgpPeerVO; import org.apache.cloudstack.network.RoutedIpv4Manager; @@ -5550,4 +5552,24 @@ public void updateTemplateIsoResponsesForIcons(List responses, response.setResourceIconResponse(iconResponse); } } + + @Override + public GuiThemeResponse createGuiThemeResponse(GuiThemeVO guiThemeVO) { + GuiThemeResponse guiThemeResponse = new GuiThemeResponse(); + + guiThemeResponse.setId(guiThemeVO.getUuid()); + guiThemeResponse.setName(guiThemeVO.getName()); + guiThemeResponse.setDescription(guiThemeVO.getDescription()); + guiThemeResponse.setCss(guiThemeVO.getCss()); + guiThemeResponse.setJsonConfiguration(guiThemeVO.getJsonConfiguration()); + guiThemeResponse.setCommonNames(guiThemeVO.getCommonNames()); + guiThemeResponse.setDomainIds(guiThemeVO.getDomainUuids()); + guiThemeResponse.setAccountIds(guiThemeVO.getAccountUuids()); + guiThemeResponse.setPublic(guiThemeVO.getIsPublic()); + guiThemeResponse.setCreated(guiThemeVO.getCreated()); + guiThemeResponse.setRemoved(guiThemeVO.getRemoved()); + guiThemeResponse.setResponseName("guithemes"); + + return guiThemeResponse; + } } diff --git a/server/src/main/java/com/cloud/api/ApiServer.java b/server/src/main/java/com/cloud/api/ApiServer.java index b8227ef9d589..5d26065e1c5a 100644 --- a/server/src/main/java/com/cloud/api/ApiServer.java +++ b/server/src/main/java/com/cloud/api/ApiServer.java @@ -87,6 +87,7 @@ import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; +import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; import org.apache.cloudstack.api.command.user.offering.ListDiskOfferingsCmd; import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; import org.apache.cloudstack.api.command.user.project.ListProjectInvitationsCmd; @@ -953,6 +954,9 @@ public boolean verifyRequest(final Map requestParameters, fina final User user = ApiDBUtils.findUserById(userId); return commandAvailable(remoteAddress, commandName, user); } else { + if (commandName.equalsIgnoreCase(ListGuiThemesCmd.class.getAnnotation(APICommand.class).name())) { + return true; + } // check against every available command to see if the command exists or not if (!s_apiNameCmdClassMap.containsKey(commandName) && !commandName.equals("login") && !commandName.equals("logout")) { final String errorMessage = "The given command " + commandName + " either does not exist, is not available" + diff --git a/server/src/main/java/com/cloud/api/ApiServlet.java b/server/src/main/java/com/cloud/api/ApiServlet.java index 4994c42bb4dc..7d76e6dc3e7b 100644 --- a/server/src/main/java/com/cloud/api/ApiServlet.java +++ b/server/src/main/java/com/cloud/api/ApiServlet.java @@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ApiServerService; @@ -43,6 +44,7 @@ import org.apache.cloudstack.api.auth.APIAuthenticationType; import org.apache.cloudstack.api.auth.APIAuthenticator; import org.apache.cloudstack.api.command.user.consoleproxy.CreateConsoleEndpointCmd; +import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.managed.context.ManagedContext; import org.apache.cloudstack.utils.consoleproxy.ConsoleAccessUtils; @@ -336,6 +338,8 @@ void processRequestInContext(final HttpServletRequest req, final HttpServletResp CallContext.register(accountMgr.getSystemUser(), accountMgr.getSystemAccount()); } setProjectContext(params); + setGuiThemeParameterIfApiCallIsUnauthenticated(userId, command, req, params); + if (LOGGER.isTraceEnabled()) { LOGGER.trace(String.format("verifying request for user %s from %s with %d parameters", userId, remoteAddress.getHostAddress(), params.size())); @@ -396,6 +400,19 @@ public static String getApiSessionKeySameSite() { } } + private void setGuiThemeParameterIfApiCallIsUnauthenticated(Long userId, String command, HttpServletRequest req, Map params) { + String listGuiThemesApiName = ListGuiThemesCmd.class.getAnnotation(APICommand.class).name(); + + if (userId != null || !listGuiThemesApiName.equalsIgnoreCase(command)) { + return; + } + + String serverName = req.getServerName(); + LOGGER.info("Unauthenticated call to {} API, thus, the `commonName` parameter will be inferred as {}.", listGuiThemesApiName, serverName); + params.put(ApiConstants.COMMON_NAME, new String[]{serverName}); + } + + private boolean checkIfAuthenticatorIsOf2FA(String command) { boolean verify2FA = false; APIAuthenticator apiAuthenticator = authManager.getAPIAuthenticator(command); diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 3bb6f02a58e3..5345da6d4d35 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -400,6 +400,10 @@ import org.apache.cloudstack.api.command.user.firewall.UpdatePortForwardingRuleCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCategoriesCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCmd; +import org.apache.cloudstack.api.command.user.gui.themes.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.themes.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.themes.UpdateGuiThemeCmd; import org.apache.cloudstack.api.command.user.iso.AttachIsoCmd; import org.apache.cloudstack.api.command.user.iso.CopyIsoCmd; import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; @@ -4177,6 +4181,10 @@ public List> getCommands() { cmdList.add(UpdateSecondaryStorageSelectorCmd.class); cmdList.add(RemoveSecondaryStorageSelectorCmd.class); cmdList.add(ListAffectedVmsForStorageScopeChangeCmd.class); + cmdList.add(ListGuiThemesCmd.class); + cmdList.add(UpdateGuiThemeCmd.class); + cmdList.add(CreateGuiThemeCmd.class); + cmdList.add(RemoveGuiThemeCmd.class); // Out-of-band management APIs for admins cmdList.add(EnableOutOfBandManagementForHostCmd.class); diff --git a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java new file mode 100644 index 000000000000..ece209da3758 --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java @@ -0,0 +1,405 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme; + +import com.cloud.domain.Domain; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.command.user.gui.themes.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.themes.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.themes.UpdateGuiThemeCmd; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDao; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeService; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class GuiThemeServiceImpl implements GuiThemeService { + + protected Logger logger = Logger.getLogger(GuiThemeServiceImpl.class); + + private static final List ALLOWED_PRIMITIVE_PROPERTIES = List.of("appTitle", "footer", "loginFooter", "logo", "minilogo", "banner"); + + private static final List ALLOWED_ERROR_PROPERTIES = List.of("403", "404", "500"); + + private static final List ALLOWED_PLUGIN_PROPERTIES = List.of("name", "path", "icon", "isExternalLink"); + + private static final String ERROR = "error"; + + private static final String PLUGINS = "plugins"; + + @Inject + GuiThemeDao guiThemeDao; + + @Inject + ResponseGenerator responseGenerator; + + @Inject + EntityManager entityManager; + + @Inject + AccountDao accountDao; + + @Inject + DomainDao domainDao; + + @Override + public ListResponse listGuiThemes(ListGuiThemesCmd cmd) { + ListResponse response = new ListResponse<>(); + Pair, Integer> result; + boolean listOnlyDefaultTheme = cmd.getListOnlyDefaultTheme(); + + if (listOnlyDefaultTheme) { + result = retrieveDefaultTheme(); + } else if (CallContext.current().getCallingAccountId() == Account.ACCOUNT_ID_SYSTEM) { + logger.info("Unauthenticated call to `listGuiThemes` API, ignoring all parameters, except `commonName`."); + result = listGuiThemesWithNoAuthentication(cmd); + } else { + result = listGuiThemesInternal(cmd); + } + List guiThemeResponses = new ArrayList<>(); + + for (GuiThemeVO guiThemeVO : result.first()) { + GuiThemeResponse guiThemeResponse = responseGenerator.createGuiThemeResponse(guiThemeVO); + guiThemeResponses.add(guiThemeResponse); + } + + response.setResponses(guiThemeResponses); + return response; + } + + private Pair, Integer> retrieveDefaultTheme() { + GuiThemeVO defaultTheme = guiThemeDao.findDefaultTheme(); + List list = new ArrayList<>(); + + if (defaultTheme != null) { + list.add(defaultTheme); + } + + return new Pair<>(list, list.size()); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_CREATE, eventDescription = "Creating GUI theme") + public GuiThemeVO createGuiTheme(CreateGuiThemeCmd cmd) { + String name = cmd.getName(); + String description = cmd.getDescription(); + String css = cmd.getCss(); + String jsonConfiguration = cmd.getJsonConfiguration(); + String commonNames = cmd.getCommonNames(); + String providedDomainIds = cmd.getDomainIds(); + String providedAccountIds = cmd.getAccountIds(); + boolean isPublic = cmd.getPublic(); + + if (StringUtils.isAllBlank(css, jsonConfiguration)) { + throw new CloudRuntimeException("Either the `css` or `jsonConfiguration` parameter must be informed."); + } + + validateParameters(jsonConfiguration, providedDomainIds, providedAccountIds, commonNames, null); + + if (shouldSetGuiThemeToPrivate(providedDomainIds, providedAccountIds)) { + isPublic = false; + } + + GuiThemeVO guiThemeVO = new GuiThemeVO(name, description, css, jsonConfiguration, commonNames, providedDomainIds, providedAccountIds, isPublic, new Date(), null); + return guiThemeDao.persist(guiThemeVO); + } + + protected boolean shouldSetGuiThemeToPrivate(String providedDomainIds, String providedAccountIds) { + if (StringUtils.isNotBlank(providedAccountIds)) { + logger.info("Parameter `accountIds` was informed during GUI theme creation, therefore, `isPublic` will be set to `false`."); + return true; + } + + if (StringUtils.isNotBlank(providedDomainIds)) { + logger.info("Parameter `domainIds` was informed during GUI theme creation, therefore, `isPublic` will be set to `false`."); + return true; + } + return false; + } + + /** + * A GUI theme is only considered the default one if the parameters `commonNames`, `domainIds` and `accountIds` are all blank. + * @return true if all parameters are blank, false otherwise. + */ + protected boolean isConsideredDefaultTheme(String commonNames, String providedDomainIds, String providedAccountIds) { + return StringUtils.isAllBlank(commonNames, providedDomainIds, providedAccountIds); + } + + /** + * There can only be one default theme registered, therefore, a {@link CloudRuntimeException} will be thrown if: + *
    + *
  • There is already a default theme registered when creating a new GUI theme.
  • + *
  • Or, the GUI theme to be updated is not the default theme already registered.
  • + *
+ */ + protected void checkIfDefaultThemeIsAllowed(String commonNames, String providedDomainIds, String providedAccountIds, Long idOfThemeToBeUpdated) { + if (!isConsideredDefaultTheme(commonNames, providedDomainIds, providedAccountIds)) { + logger.info("The GUI theme will not be considered as the default one, as the `commonNames`, `domainIds` and `accountIds` are not all blank."); + return; + } + + GuiThemeVO defaultTheme = guiThemeDao.findDefaultTheme(); + + if (defaultTheme != null && (idOfThemeToBeUpdated == null || defaultTheme.getId() != idOfThemeToBeUpdated.longValue())) { + throw new CloudRuntimeException(String.format("Only one default GUI theme is allowed. Remove the current default theme %s and try again.", defaultTheme)); + } + + logger.info("The parameters `commonNames`, `domainIds` and `accountIds` were not informed. The created theme will be considered as the default theme."); + } + + protected Pair, Integer> listGuiThemesWithNoAuthentication(ListGuiThemesCmd cmd) { + return guiThemeDao.listGuiThemesWithNoAuthentication(cmd.getCommonName()); + } + + + protected Pair, Integer> listGuiThemesInternal(ListGuiThemesCmd cmd) { + Long id = cmd.getId(); + String name = cmd.getName(); + String commonName = cmd.getCommonName(); + String domainUuid = cmd.getDomainId() == null ? null : domainDao.findById(cmd.getDomainId()).getUuid(); + String accountUuid = cmd.getAccountId() == null ? null : accountDao.findById(cmd.getAccountId()).getUuid(); + boolean listAll = cmd.getListAll(); + boolean showRemoved = cmd.getShowRemoved(); + Boolean showPublic = cmd.getShowPublic(); + + return guiThemeDao.listGuiThemes(id, name, commonName, domainUuid, accountUuid, listAll, showRemoved, showPublic); + } + + protected void validateParameters(String jsonConfig, String domainIds, String accountIds, String commonNames, Long idOfThemeToBeUpdated) { + if (isConsideredDefaultTheme(commonNames, domainIds, accountIds)) { + checkIfDefaultThemeIsAllowed(commonNames, domainIds, accountIds, idOfThemeToBeUpdated); + } + + validateObjectUuids(accountIds, Account.class); + validateObjectUuids(domainIds, Domain.class); + validateJsonConfiguration(jsonConfig); + } + + protected void validateJsonConfiguration(String jsonConfig) { + if (jsonConfig == null) { + return; + } + + JsonObject jsonObject = new JsonObject(); + + try { + JsonElement jsonElement = new JsonParser().parse(jsonConfig); + Set> entries = jsonElement.getAsJsonObject().entrySet(); + entries.stream().forEach(entry -> validateJsonAttributes(entry, jsonObject)); + } catch (JsonSyntaxException exception) { + logger.error(String.format("The following exception was thrown while parsing the JSON object: [%s].", exception.getMessage())); + throw new CloudRuntimeException("Specified JSON configuration is not a valid JSON object."); + } + } + + /** + * Validates the informed JSON attributes considering the allowed properties by the API, any invalid option is ignored. + * All valid options are added to a {@link JsonObject} that will be considered as the final JSON configuration used by the GUI theme. + */ + private void validateJsonAttributes(Map.Entry entry, JsonObject jsonObject) { + JsonElement entryValue = entry.getValue(); + String entryKey = entry.getKey(); + + if (entryValue.isJsonPrimitive() && ALLOWED_PRIMITIVE_PROPERTIES.contains(entryKey)) { + logger.trace("The JSON attribute [%s] is a valid option."); + jsonObject.add(entryKey, entryValue); + } else if (entryValue.isJsonObject() && ERROR.equals(entryKey)) { + validateErrorAttribute(entry, jsonObject); + } else if (entryValue.isJsonArray() && PLUGINS.equals(entryKey)) { + validatePluginsAttribute(entry, jsonObject); + } else { + warnOfInvalidJsonAttribute(entryKey); + } + } + + /** + * Creates a {@link JsonObject} with only the valid options for the Plugins' properties specified in the {@link #ALLOWED_PLUGIN_PROPERTIES}. + */ + protected void validatePluginsAttribute(Map.Entry entry, JsonObject jsonObject) { + Set> entries = entry.getValue().getAsJsonArray().get(0).getAsJsonObject().entrySet(); + JsonObject objectToBeAdded = createJsonObject(entries, ALLOWED_PLUGIN_PROPERTIES); + JsonArray jsonArray = new JsonArray(); + + if (objectToBeAdded.entrySet().isEmpty()) { + return; + } + + jsonArray.add(objectToBeAdded); + jsonObject.add(entry.getKey(), jsonArray); + } + + /** + * Creates a {@link JsonObject} with only the valid options for the Error's properties specified in the {@link #ALLOWED_ERROR_PROPERTIES}. + */ + protected void validateErrorAttribute(Map.Entry entry, JsonObject jsonObject) { + Set> entries = entry.getValue().getAsJsonObject().entrySet(); + JsonObject objectToBeAdded = createJsonObject(entries, ALLOWED_ERROR_PROPERTIES); + + if (objectToBeAdded.entrySet().isEmpty()) { + return; + } + + jsonObject.add(entry.getKey(), objectToBeAdded); + } + + protected JsonObject createJsonObject(Set> entries, List allowedProperties) { + JsonObject objectToBeAdded = new JsonObject(); + + for (Map.Entry recursiveEntry : entries) { + String entryKey = recursiveEntry.getKey(); + + if (!allowedProperties.contains(entryKey)) { + warnOfInvalidJsonAttribute(entryKey); + continue; + } + objectToBeAdded.add(entryKey, recursiveEntry.getValue()); + } + + return objectToBeAdded; + } + + protected void warnOfInvalidJsonAttribute(String entryKey) { + logger.warn(String.format("The JSON attribute [%s] is not a valid option, therefore, it will be ignored.", entryKey)); + } + + /** + * Validate if the comma separated list of UUIDs of the fields {@link GuiThemeVO#accountUuids} and {@link GuiThemeVO#domainUuids} are valid. + * @param providedIds a comma separated list of UUIDs of {@link Account} or {@link Domain} + * @param clazz the class to infer the DAO object. Valid options are: {@link Account} and {@link Domain} + */ + protected void validateObjectUuids(String providedIds, Class clazz) { + if (StringUtils.isBlank(providedIds)) { + return; + } + + List commaSeparatedIds = new ArrayList<>(Arrays.asList(providedIds.split("\\s*,\\s*"))); + for (String id : commaSeparatedIds) { + Object objectVO = entityManager.findByUuid(clazz, id); + + if (objectVO == null) { + throw new CloudRuntimeException(String.format("The %s ID %s does not exist. Verify the informed IDs and try again.", clazz.getSimpleName(), id)); + } + } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_UPDATE, eventDescription = "Updating GUI theme") + public GuiThemeVO updateGuiTheme(UpdateGuiThemeCmd cmd) { + Long guiThemeId = cmd.getId(); + GuiThemeVO guiThemeVO = guiThemeDao.findById(guiThemeId); + + String name = cmd.getName(); + String description = cmd.getDescription(); + String css = cmd.getCss(); + String jsonConfiguration = cmd.getJsonConfiguration(); + String commonNames = cmd.getCommonNames() == null ? guiThemeVO.getCommonNames() : cmd.getCommonNames(); + String providedDomainIds = cmd.getDomainIds() == null ? guiThemeVO.getDomainUuids() : cmd.getDomainIds(); + String providedAccountIds = cmd.getAccountIds() == null ? guiThemeVO.getAccountUuids() : cmd.getAccountIds(); + Boolean isPublic = cmd.getIsPublic(); + + validateParameters(jsonConfiguration, providedDomainIds, providedAccountIds, commonNames, guiThemeId); + + if (shouldSetGuiThemeToPrivate(providedDomainIds, providedAccountIds)) { + isPublic = false; + } + + return persistGuiTheme(name, description, css, jsonConfiguration, commonNames, providedDomainIds, providedAccountIds, isPublic, guiThemeVO); + + } + + protected GuiThemeVO persistGuiTheme(String name, String description, String css, String jsonConfiguration, String commonNames, String providedDomainIds, + String providedAccountIds, Boolean isPublic, GuiThemeVO guiThemeVO){ + if (name != null) { + guiThemeVO.setName(ifBlankReturnNull(name)); + } + + if (description != null) { + guiThemeVO.setDescription(ifBlankReturnNull(description)); + } + + if (css != null) { + guiThemeVO.setCss(css); + } + + if (jsonConfiguration != null) { + guiThemeVO.setJsonConfiguration(jsonConfiguration); + } + + if (commonNames != null) { + guiThemeVO.setCommonNames(ifBlankReturnNull(commonNames)); + } + + if (providedAccountIds != null) { + guiThemeVO.setAccountUuids(ifBlankReturnNull(providedAccountIds)); + } + + if (providedDomainIds != null) { + guiThemeVO.setDomainUuids(ifBlankReturnNull(providedDomainIds)); + } + + if (isPublic != null) { + guiThemeVO.setIsPublic(isPublic); + } + + return guiThemeDao.persist(guiThemeVO); + } + + protected String ifBlankReturnNull(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + return value; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_REMOVE, eventDescription = "Removing GUI theme") + public void removeGuiTheme(RemoveGuiThemeCmd cmd) { + Long guiThemeId = cmd.getId(); + GuiThemeVO guiThemeVO = guiThemeDao.findById(guiThemeId); + + if (guiThemeVO != null) { + guiThemeDao.remove(guiThemeId); + } else { + throw new CloudRuntimeException(String.format("Unable to find a GUI theme with the specified UUID.")); + } + } +} diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index dfb38cd7d8ba..d82e20dc344f 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -390,4 +390,5 @@
+ diff --git a/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java new file mode 100644 index 000000000000..5eeaf7bcfb54 --- /dev/null +++ b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java @@ -0,0 +1,225 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme; + +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDao; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.ArrayList; +import java.util.List; + + +@RunWith(PowerMockRunner.class) +@PrepareForTest(CallContext.class) +public class GuiThemeServiceImplTest { + + @Mock + GuiThemeDao guiThemeDaoMock; + + @Mock + GuiThemeVO guiThemeVOMock; + + @Mock + EntityManager entityManagerMock; + + @Mock + Object objectMock; + + @Mock + CallContext callContextMock; + + @Mock + ListGuiThemesCmd listGuiThemesCmdMock; + + @Spy + @InjectMocks + GuiThemeServiceImpl guiThemeServiceSpy = new GuiThemeServiceImpl(); + + private static final String COMMON_NAME = "*acme.com,acm2.com"; + + private static final String DOMAIN_IDS = "1,2,3"; + + private static final String ACCOUNT_IDS = "4,5,6"; + + private static final String BLANK_STRING = ""; + + @Test + public void listGuiThemesTestShouldIgnoreParametersWhenItIsCalledUnauthenticated() { + Pair, Integer> emptyPair = new Pair<>(new ArrayList<>(), 0); + PowerMockito.mockStatic(CallContext.class); + PowerMockito.when(CallContext.current()).thenReturn(callContextMock); + + Long accountId = Account.ACCOUNT_ID_SYSTEM; + + Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); + Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); + Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(false); + guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); + Mockito.verify(guiThemeServiceSpy, Mockito.times(1)).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); + } + + @Test + public void listGuiThemesTestShouldCallNormalFlowWhenAuthenticated() { + Pair, Integer> emptyPair = new Pair<>(new ArrayList<>(), 0); + PowerMockito.mockStatic(CallContext.class); + PowerMockito.when(CallContext.current()).thenReturn(callContextMock); + + Long accountId = 3L; + + Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); + Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesInternal(Mockito.nullable(ListGuiThemesCmd.class)); + Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(false); + guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); + Mockito.verify(guiThemeServiceSpy, Mockito.times(1)).listGuiThemesInternal(Mockito.nullable(ListGuiThemesCmd.class)); + } + + @Test + public void listGuiThemesTestListOnlyDefaultThemesShouldCallFindDefaultTheme() { + Pair, Integer> emptyPair = new Pair<>(new ArrayList<>(), 0); + PowerMockito.mockStatic(CallContext.class); + PowerMockito.when(CallContext.current()).thenReturn(callContextMock); + + Long accountId = Account.ACCOUNT_ID_SYSTEM; + + Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); + Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); + Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(true); + guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); + Mockito.verify(guiThemeDaoMock, Mockito.times(1)).findDefaultTheme(); + } + + @Test + public void shouldSetGuiThemeToPrivateTestDomainIdsAndAccountIdsAreBlankShouldReturnFalse() { + boolean result = guiThemeServiceSpy.shouldSetGuiThemeToPrivate(BLANK_STRING, BLANK_STRING); + + Assert.assertFalse(result); + } + + @Test + public void shouldSetGuiThemeToPrivateTestDomainIdsNotBlankAndAccountIdsIsBlankShouldReturnTrue() { + boolean result = guiThemeServiceSpy.shouldSetGuiThemeToPrivate(DOMAIN_IDS, BLANK_STRING); + + Assert.assertTrue(result); + } + + @Test + public void shouldSetGuiThemeToPrivateTestDomainIdsIsBlankAndAccountIdsIsNotBlankShouldReturnTrue() { + boolean result = guiThemeServiceSpy.shouldSetGuiThemeToPrivate(BLANK_STRING, ACCOUNT_IDS); + + Assert.assertTrue(result); + } + + @Test + public void shouldSetGuiThemeToPrivateTestDomainIdsAndAccountIdsAreNotBlankShouldReturnTrue() { + boolean result = guiThemeServiceSpy.shouldSetGuiThemeToPrivate(DOMAIN_IDS, ACCOUNT_IDS); + + Assert.assertTrue(result); + } + + @Test + public void validateObjectUuidsTestProvidedUuidsIsNullShouldNotThrowCloudRuntimeException() { + guiThemeServiceSpy.validateObjectUuids(null, null); + } + + @Test + public void validateObjectUuidsTestProvidedUuidsIsBlankShouldNotThrowCloudRuntimeException() { + guiThemeServiceSpy.validateObjectUuids(BLANK_STRING, null); + } + + @Test + public void validateObjectUuidsTestProvidedUuidsIsBlankAndCommaSeparatedShouldNotThrowCloudRuntimeException() { + String blankCommaSeparatedString = ",,,"; + guiThemeServiceSpy.validateObjectUuids(blankCommaSeparatedString, null); + } + + @Test + public void validateObjectUuidsTestProvidedUuidIsValidShouldNotThrowCloudRuntimeException() { + String validUuid = "4"; + + Mockito.when(entityManagerMock.findByUuid(Mockito.any(Class.class), Mockito.anyString())).thenReturn(objectMock); + guiThemeServiceSpy.validateObjectUuids(validUuid, Account.class); + } + + @Test + public void validateObjectUuidsTestProvidedUuidsAreValidShouldNotThrowCloudRuntimeException() { + Mockito.when(entityManagerMock.findByUuid(Mockito.any(Class.class), Mockito.anyString())).thenReturn(objectMock); + guiThemeServiceSpy.validateObjectUuids(ACCOUNT_IDS, Account.class); + } + + @Test(expected = CloudRuntimeException.class) + public void validateObjectUuidsTestProvidedUuidsAreNotValidShouldThrowCloudRuntimeException() { + Mockito.when(entityManagerMock.findByUuid(Mockito.any(Class.class), Mockito.anyString())).thenReturn(null); + guiThemeServiceSpy.validateObjectUuids(ACCOUNT_IDS, Account.class); + } + + @Test + public void checkIfDefaultThemeIsAllowedTestThemeIsNotConsideredDefault() { + Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(false); + + guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(COMMON_NAME, BLANK_STRING, BLANK_STRING, null); + } + + @Test + public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndThereIsNotADefaultThemeRegistered() { + Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); + Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(null); + + guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, null); + } + + @Test(expected = CloudRuntimeException.class) + public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndThereIsADefaultThemeRegisteredShouldThrowCloudRuntimeException() { + Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); + Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(guiThemeVOMock); + + guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, null); + } + + @Test(expected = CloudRuntimeException.class) + public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndWillBeUpdatedAndIsDifferentIdShouldThrowCloudRuntimeException() { + Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); + Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(guiThemeVOMock); + Mockito.when(guiThemeVOMock.getId()).thenReturn(1L); + + guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, 2L); + } + + @Test + public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndWillBeUpdatedAndIsTheSameShouldAllowUpdate() { + Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); + Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(guiThemeVOMock); + Mockito.when(guiThemeVOMock.getId()).thenReturn(1L); + + guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, 1L); + } +} diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 8fafd841be49..8d507670ba82 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -260,7 +260,11 @@ 'addNodesToKubernetesCluster': 'Kubernetes Service', 'removeNodesFromKubernetesCluster': 'Kubernetes Service', 'configureStorageAccess': 'Storage Access Groups', - 'listStorageAccessGroups': 'Storage Access Groups' + 'listStorageAccessGroups': 'Storage Access Groups', + 'listGuiThemes': 'GUI Theme', + 'createGuiTheme': 'GUI Theme', + 'updateGuiTheme': 'GUI Theme', + 'removeGuiTheme': 'GUI Theme' } diff --git a/ui/public/config.json b/ui/public/config.json index 446256b5b189..0b949d19770d 100644 --- a/ui/public/config.json +++ b/ui/public/config.json @@ -17,6 +17,7 @@ "logo": "assets/logo.svg", "minilogo": "assets/mini-logo.svg", "banner": "assets/banner.svg", + "favicon": "cloud.ico", "error": { "403": "assets/403.png", "404": "assets/404.png", diff --git a/ui/src/api/index.js b/ui/src/api/index.js index 7ab87780a9d4..6c2b9d914cca 100644 --- a/ui/src/api/index.js +++ b/ui/src/api/index.js @@ -74,10 +74,13 @@ export function login (arg) { }) } -export function logout () { - message.destroy() - notification.destroy() - return api('logout') +export async function logout () { + const result = await api('logout').finally(() => { + sourceToken.cancel() + message.destroy() + notification.destroy() + }) + return result } export function oauthlogin (arg) { diff --git a/ui/src/components/header/UserMenu.vue b/ui/src/components/header/UserMenu.vue index 32597dffdfbd..0d6660f9b3c5 100644 --- a/ui/src/components/header/UserMenu.vue +++ b/ui/src/components/header/UserMenu.vue @@ -80,6 +80,8 @@ import { mapActions, mapGetters } from 'vuex' import ResourceIcon from '@/components/view/ResourceIcon' import eventBus from '@/config/eventBus' import { SERVER_MANAGER } from '@/store/mutation-types' +import { sourceToken } from '@/utils/request' +import { applyCustomGuiTheme } from '@/utils/guiTheme' export default { name: 'UserMenu', @@ -178,7 +180,9 @@ export default { } }, handleLogout () { - return this.Logout({}).then(() => { + this.Logout({}).finally(async () => { + sourceToken.init() + await applyCustomGuiTheme(null, null) this.$router.push('/user/login') }).catch(err => { this.$message.error({ diff --git a/ui/src/main.js b/ui/src/main.js index 3fee7428210b..7ccd9d8a9ee3 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -42,6 +42,9 @@ import { } from './utils/plugins' import { VueAxios } from './utils/request' import directives from './utils/directives' +import Cookies from 'js-cookie' +import { api } from '@/api' +import { applyCustomGuiTheme } from './utils/guiTheme' vueApp.use(VueAxios, router) vueApp.use(pollJobPlugin) @@ -60,7 +63,8 @@ vueApp.use(imagesUtilPlugin) vueApp.use(extensions) vueApp.use(directives) -fetch('config.json?ts=' + Date.now()).then(response => response.json()).then(config => { + +fetch('config.json?ts=' + Date.now()).then(response => response.json()).then(async config => { vueProps.$config = config let basUrl = config.apiBase if (config.multipleServer) { @@ -69,6 +73,19 @@ fetch('config.json?ts=' + Date.now()).then(response => response.json()).then(con vueProps.axios.defaults.baseURL = basUrl + const userid = Cookies.get('userid') + let accountid = null + let domainid = null + + if (userid !== undefined) { + await api('listUsers', { userid: userid }).then(response => { + accountid = response.listusersresponse.user[0].accountid + domainid = response.listusersresponse.user[0].domainid + }) + } + + await applyCustomGuiTheme(accountid, domainid) + loadLanguageAsync().then(() => { vueApp.use(store) .use(router) diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index 6fa3ce1b10aa..6998abea290d 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -47,6 +47,10 @@ import { LATEST_CS_VERSION } from '@/store/mutation-types' +import { + applyCustomGuiTheme +} from '@/utils/guiTheme' + const user = { state: { token: '', @@ -201,7 +205,7 @@ const user = { }, Login ({ commit }, userInfo) { return new Promise((resolve, reject) => { - login(userInfo).then(response => { + login(userInfo).then(async response => { const result = response.loginresponse || {} Cookies.set('account', result.account, { expires: 1 }) Cookies.set('domainid', result.domainid, { expires: 1 }) @@ -244,6 +248,10 @@ const user = { commit('SET_LATEST_VERSION', latestVersion) notification.destroy() + await api('listUsers', { userid: result.userid }).then(async response => { + await applyCustomGuiTheme(response.listusersresponse.user[0].accountid, result.domainid) + }) + resolve() }).catch(error => { reject(error) diff --git a/ui/src/utils/guiTheme.js b/ui/src/utils/guiTheme.js new file mode 100644 index 000000000000..7cc3dc0cc034 --- /dev/null +++ b/ui/src/utils/guiTheme.js @@ -0,0 +1,100 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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 { vueProps } from '@/vue-app' +import { api } from '@/api' + +export async function applyCustomGuiTheme (accountid, domainid) { + await fetch('config.json').then(response => response.json()).then(config => { + vueProps.$config = config + }) + + let guiTheme + + if (accountid != null) { + guiTheme = await fetchGuiTheme({ accountid: accountid }) + } + + if (guiTheme === undefined && domainid != null) { + guiTheme = await fetchGuiTheme({ domainid: domainid }) + } + + if (guiTheme === undefined) { + guiTheme = await fetchGuiTheme({ commonname: window.location.hostname }) + } + + if (guiTheme === undefined) { + guiTheme = await fetchGuiTheme({ listonlydefaulttheme: true }) + } + + await applyDynamicCustomization(guiTheme) +} + +async function fetchGuiTheme (params) { + return await api('listGuiThemes', params).then(response => { + if (response.listguithemesresponse.guiThemes) { + return response.listguithemesresponse.guiThemes[0] + } + }) +} + +async function applyDynamicCustomization (response) { + let jsonConfig + + if (response?.jsonconfiguration) { + jsonConfig = JSON.parse(response?.jsonconfiguration) + } + + // Sets custom GUI fields only if is not nullish. + vueProps.$config.appTitle = jsonConfig?.appTitle ?? vueProps.$config.appTitle + vueProps.$config.footer = jsonConfig?.footer ?? vueProps.$config.footer + vueProps.$config.loginFooter = jsonConfig?.loginFooter ?? vueProps.$config.loginFooter + vueProps.$config.logo = jsonConfig?.logo ?? vueProps.$config.logo + vueProps.$config.minilogo = jsonConfig?.minilogo ?? vueProps.$config.minilogo + vueProps.$config.banner = jsonConfig?.banner ?? vueProps.$config.banner + + if (jsonConfig?.error) { + vueProps.$config.error[403] = jsonConfig?.error[403] ?? vueProps.$config.error[403] + vueProps.$config.error[404] = jsonConfig?.error[404] ?? vueProps.$config.error[404] + vueProps.$config.error[500] = jsonConfig?.error[500] ?? vueProps.$config.error[500] + } + + if (jsonConfig?.plugins) { + jsonConfig.plugins.forEach(plugin => { + vueProps.$config.plugins.push(plugin) + }) + } + + vueProps.$config.favicon = jsonConfig?.favicon ?? vueProps.$config.favicon + vueProps.$config.css = response?.css ?? null + + await applyStaticCustomization(vueProps.$config.favicon, vueProps.$config.css) +} + +async function applyStaticCustomization (favicon, css) { + document.getElementById('favicon').href = favicon + + let style = document.getElementById('guiThemeCSS') + if (style != null) { + style.innerHTML = css + } else { + style = document.createElement('style') + style.setAttribute('id', 'guiThemeCSS') + style.innerHTML = css + document.body.appendChild(style) + } +} From 65e83c24ec976b2aa1a6e2d7c96923c0998b0b18 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 16 Apr 2024 10:02:17 -0300 Subject: [PATCH 02/22] Add guithemedetails join --- .../apache/cloudstack/api/ApiConstants.java | 2 + .../cloudstack/api/ResponseGenerator.java | 4 +- .../user/gui/themes/CreateGuiThemeCmd.java | 18 ++- .../user/gui/themes/UpdateGuiThemeCmd.java | 19 ++- .../api/response/GuiThemeResponse.java | 16 +- .../gui/themes/GuiThemeDetailsVO.java | 86 ++++++++++ .../cloudstack/gui/themes/GuiThemeJoinVO.java | 150 ++++++++++++++++++ .../gui/themes/GuiThemeService.java | 4 +- .../cloudstack/gui/themes/GuiThemeVO.java | 54 ++----- .../cloudstack/gui/theme/dao/GuiThemeDao.java | 9 -- .../gui/theme/dao/GuiThemeDaoImpl.java | 89 +---------- .../gui/theme/dao/GuiThemeDetailsDao.java | 30 ++++ .../gui/theme/dao/GuiThemeDetailsDaoImpl.java | 129 +++++++++++++++ .../gui/theme/dao/GuiThemeJoinDao.java | 31 ++++ .../gui/theme/dao/GuiThemeJoinDaoImpl.java | 142 +++++++++++++++++ ...spring-engine-schema-core-daos-context.xml | 2 + .../META-INF/db/schema-41900to42000.sql | 33 +++- .../java/com/cloud/utils/db/SearchBase.java | 8 + .../com/cloud/utils/db/SearchCriteria.java | 6 +- .../java/com/cloud/api/ApiResponseHelper.java | 27 ++-- .../gui/theme/GuiThemeServiceImpl.java | 140 +++++++++------- .../gui/theme/GuiThemeServiceImplTest.java | 74 ++++----- 22 files changed, 809 insertions(+), 264 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java create mode 100644 api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 4c30569b2f46..b1612e01a489 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1269,6 +1269,8 @@ public class ApiConstants { public static final String LIST_ONLY_DEFAULT_THEME = "listonlydefaulttheme"; + public static final String RECURSIVE_DOMAINS = "recursivedomains"; + /** * This enum specifies IO Drivers, each option controls specific policies on I/O. * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 5424e9acf720..6bce5e893105 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -151,7 +151,7 @@ import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; @@ -582,5 +582,5 @@ List createTemplateResponses(ResponseView view, VirtualMachine void updateTemplateIsoResponsesForIcons(List responses, ResourceTag.ResourceObjectType type); - GuiThemeResponse createGuiThemeResponse(GuiThemeVO guiThemeVO); + GuiThemeResponse createGuiThemeResponse(GuiThemeJoinVO guiThemeJoinVO); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java index 9183c6b0b8f7..9990a551d151 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java @@ -25,6 +25,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.apache.cloudstack.gui.themes.GuiThemeService; @@ -68,6 +69,10 @@ public class CreateGuiThemeCmd extends BaseCmd { "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") private Boolean isPublic = true; + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + public String getName() { return name; } @@ -100,20 +105,21 @@ public Boolean getPublic() { return isPublic; } - public void setIsPublic(Boolean isPublic) { - this.isPublic = isPublic; + public Boolean getRecursiveDomains() { + return recursiveDomains; } @Override public void execute() { - CallContext.current().setEventDetails(String.format("Name: %s, AccountIDs: %s, DomainIDs: %s, CommonNames: %s", getName(), getAccountIds(), getDomainIds(), getCommonNames())); - GuiThemeVO guiThemeVO = guiThemeService.createGuiTheme(this); + CallContext.current().setEventDetails(String.format("Name: %s, AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", getName(), getAccountIds(), + getDomainIds(), getRecursiveDomains(), getCommonNames())); + GuiThemeJoinVO guiTheme = guiThemeService.createGuiTheme(this); - if (guiThemeVO == null) { + if (guiTheme == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create the GUI theme."); } - GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeVO); + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiTheme); response.setResponseName(getCommandName()); this.setResponseObject(response); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java index 651c8d5c04b7..2009c659650e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java @@ -25,6 +25,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.apache.cloudstack.gui.themes.GuiThemeService; @@ -63,6 +64,10 @@ public class UpdateGuiThemeCmd extends BaseCmd { "the end-user) separated by comma that can retrieve the theme.") private String domainIds; + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + " the end-user) separated by comma that can retrieve the theme.") private String accountIds; @@ -103,21 +108,25 @@ public String getAccountIds() { return accountIds; } + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + public Boolean getIsPublic() { return isPublic; } @Override public void execute() { - CallContext.current().setEventDetails(String.format("ID: %s, Name: %s, AccountIDs: %s, DomainIDs: %s, CommonNames: %s", getId(), getName(), getAccountIds(), - getDomainIds(), getCommonNames())); - GuiThemeVO guiThemeVO = guiThemeService.updateGuiTheme(this); + CallContext.current().setEventDetails(String.format("ID: %s, Name: %s, AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", getId(), getName(), + getAccountIds(), getDomainIds(), getRecursiveDomains(), getCommonNames())); + GuiThemeJoinVO guiThemeJoinVO = guiThemeService.updateGuiTheme(this); - if (guiThemeVO == null) { + if (guiThemeJoinVO == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the GUI theme."); } - GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeVO); + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoinVO); response.setResponseName(getCommandName()); this.setResponseObject(response); } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java index 726520b72202..31390623fd97 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -21,11 +21,11 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; import java.util.Date; -@EntityReference(value = {GuiThemeVO.class}) +@EntityReference(value = {GuiThemeJoinVO.class}) public class GuiThemeResponse extends BaseResponse { @SerializedName(ApiConstants.ID) @@ -56,6 +56,10 @@ public class GuiThemeResponse extends BaseResponse { @Param(description = "A set of domain UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") private String domainIds; + @SerializedName(ApiConstants.RECURSIVE_DOMAINS) + @Param(description = "Whether to consider the subdomains of the informed domain IDs.") + private Boolean recursiveDomains; + @SerializedName(ApiConstants.ACCOUNT_IDS) @Param(description = "A set of account UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") private String accountIds; @@ -161,6 +165,14 @@ public Date getRemoved() { return removed; } + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + public void setRecursiveDomains(Boolean recursiveDomains) { + this.recursiveDomains = recursiveDomains; + } + public void setRemoved(Date removed) { this.removed = removed; } diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java new file mode 100644 index 000000000000..daa6a0715221 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java @@ -0,0 +1,86 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.themes; + +import org.apache.cloudstack.api.InternalIdentity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "gui_themes_details") +public class GuiThemeDetailsVO implements InternalIdentity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "gui_theme_id", nullable = false) + private Long guiThemeId; + + @Column(name = "type", nullable = false, length = 100) + private String type; + + @Column(name = "value", nullable = false, length = 65535) + private String value; + + public GuiThemeDetailsVO() { + } + + public GuiThemeDetailsVO(Long guiThemeId, String type, String value) { + this.guiThemeId = guiThemeId; + this.type = type; + this.value = value; + } + + @Override + public long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getGuiThemeId() { + return guiThemeId; + } + + public void setGuiThemeId(Long guiThemeId) { + this.guiThemeId = guiThemeId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java new file mode 100644 index 000000000000..85681380c87f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java @@ -0,0 +1,150 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.themes; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.InternalIdentity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; + +@Entity +@Table(name = "gui_themes_view") +public class GuiThemeJoinVO implements InternalIdentity { + @Id + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid; + + @Column(name = "name", nullable = false, length = 2048) + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "css", nullable = false, length = 65535) + private String css; + + @Column(name = "json_configuration", nullable = false, length = 65535) + private String jsonConfiguration; + + @Column(name = "common_names", length = 65535) + private String commonNames; + + @Column(name = "domains", length = 65535) + private String domains; + + @Column(name = "accounts", length = 65535) + private String accounts; + + @Column(name = "recursive_domains") + private boolean recursiveDomains; + + @Column(name = "is_public") + private boolean isPublic; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + public GuiThemeJoinVO() { + } + + public GuiThemeJoinVO(Long id, String uuid, String name, String description, String css, String jsonConfiguration, String commonNames, String domains, + String accounts, boolean recursiveDomains, boolean isPublic, Date created, Date removed) { + this.id = id; + this.uuid = uuid; + this.name = name; + this.description = description; + this.css = css; + this.jsonConfiguration = jsonConfiguration; + this.commonNames = commonNames; + this.domains = domains; + this.accounts = accounts; + this.recursiveDomains = recursiveDomains; + this.isPublic = isPublic; + this.created = created; + this.removed = removed; + } + + public long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomains() { + return domains; + } + + public String getAccounts() { + return accounts; + } + + public boolean isRecursiveDomains() { + return recursiveDomains; + } + + public boolean getIsPublic() { + return isPublic; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java index 9622a15eca9a..20bec4d037dc 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java @@ -11,9 +11,9 @@ public interface GuiThemeService { ListResponse listGuiThemes(ListGuiThemesCmd cmd); - GuiThemeVO createGuiTheme(CreateGuiThemeCmd cmd); + GuiThemeJoinVO createGuiTheme(CreateGuiThemeCmd cmd); - GuiThemeVO updateGuiTheme(UpdateGuiThemeCmd cmd); + GuiThemeJoinVO updateGuiTheme(UpdateGuiThemeCmd cmd); void removeGuiTheme(RemoveGuiThemeCmd cmd); } diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java index 86170e721a63..063469630acc 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java @@ -55,18 +55,12 @@ public class GuiThemeVO implements InternalIdentity, Identity { @Column(name = "json_configuration", nullable = false, length = 65535) private String jsonConfiguration; - @Column(name = "common_names", length = 65535) - private String commonNames; - - @Column(name = "domain_uuids", length = 65535) - private String domainUuids; - - @Column(name = "account_uuids", length = 65535) - private String accountUuids; - @Column(name = "is_public") private boolean isPublic; + @Column(name = "recursive_domains") + private boolean recursiveDomains = false; + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) @Temporal(value = TemporalType.TIMESTAMP) private Date created; @@ -79,15 +73,12 @@ public GuiThemeVO() { } - public GuiThemeVO(String name, String description, String css, String jsonConfiguration, String commonNames, String domainUuids, String accountUuids, - boolean isPublic, Date created, Date removed) { + public GuiThemeVO(String name, String description, String css, String jsonConfiguration, boolean recursiveDomains, boolean isPublic, Date created, Date removed) { this.name = name; this.description = description; this.css = css; this.jsonConfiguration = jsonConfiguration; - this.commonNames = commonNames; - this.domainUuids = domainUuids; - this.accountUuids = accountUuids; + this.recursiveDomains = recursiveDomains; this.isPublic = isPublic; this.created = created; this.removed = removed; @@ -119,18 +110,6 @@ public String getJsonConfiguration() { return jsonConfiguration; } - public String getCommonNames() { - return commonNames; - } - - public String getDomainUuids() { - return domainUuids; - } - - public String getAccountUuids() { - return accountUuids; - } - public Date getCreated() { return created; } @@ -167,18 +146,6 @@ public void setJsonConfiguration(String jsonConfiguration) { this.jsonConfiguration = jsonConfiguration; } - public void setCommonNames(String commonNames) { - this.commonNames = commonNames; - } - - public void setDomainUuids(String domainUuids) { - this.domainUuids = domainUuids; - } - - public void setAccountUuids(String accountUuids) { - this.accountUuids = accountUuids; - } - public void setCreated(Date created) { this.created = created; } @@ -191,8 +158,17 @@ public void setIsPublic(boolean isPublic) { this.isPublic = isPublic; } + public boolean isRecursiveDomains() { + return recursiveDomains; + } + + public void setRecursiveDomains(boolean recursiveDomains) { + this.recursiveDomains = recursiveDomains; + } + + @Override public String toString() { - return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "description", "isPublic"); + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "description", "isPublic", "recursiveDomains"); } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java index 6df15749fcd4..7049fd0da44e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java @@ -1,16 +1,7 @@ package org.apache.cloudstack.gui.theme.dao; -import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.gui.themes.GuiThemeVO; -import java.util.List; - public interface GuiThemeDao extends GenericDao { - - GuiThemeVO findDefaultTheme(); - - Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainId, String accountId, boolean listAll, boolean showRemoved, Boolean showPublic); - - Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java index ebba45aab299..f87ffd95fe98 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java @@ -16,95 +16,10 @@ // under the License. package org.apache.cloudstack.gui.theme.dao; -import com.cloud.utils.Pair; -import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; -import com.cloud.utils.db.SearchBuilder; -import com.cloud.utils.db.SearchCriteria; import org.apache.cloudstack.gui.themes.GuiThemeVO; -import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; -import java.util.List; +@Component public class GuiThemeDaoImpl extends GenericDaoBase implements GuiThemeDao { - - @Override - public GuiThemeVO findDefaultTheme() { - SearchBuilder searchBuilder = createSearchBuilder(); - searchBuilder.and("commonNames", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.NULL); - searchBuilder.and("domainUuids", searchBuilder.entity().getDomainUuids(), SearchCriteria.Op.NULL); - searchBuilder.and("accountUuids", searchBuilder.entity().getAccountUuids(), SearchCriteria.Op.NULL); - searchBuilder.done(); - - SearchCriteria searchCriteria = searchBuilder.create(); - - return findOneBy(searchCriteria); - } - - @Override - public Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, - Boolean showPublic) { - SearchCriteria searchCriteria = createGuiThemeSearchCriteria(id, name, commonName, domainUuid, accountUuid, showPublic, listAll); - - if (listAll) { - showRemoved = false; - } - - return searchAndCount(searchCriteria, null, showRemoved); - } - - private SearchCriteria createGuiThemeSearchCriteria(Long id, String name, String commonName, String domainUuid, String accountUuid, Boolean showPublic, boolean listAll) { - SearchCriteria searchCriteria = createGuiThemeSearchBuilder(accountUuid, domainUuid, commonName, listAll, showPublic).create(); - - searchCriteria.setParametersIfNotNull("id", id); - searchCriteria.setParametersIfNotNull("name", name); - searchCriteria.setParametersIfNotNull("isPublic", showPublic); - - if (StringUtils.isNotBlank(commonName)) { - searchCriteria.setParameters("commonName", "%" + commonName + "%"); - } - - if (StringUtils.isNotBlank(domainUuid)) { - searchCriteria.setParameters("domainId", "%" + domainUuid + "%"); - } - - if (StringUtils.isNotBlank(accountUuid)) { - searchCriteria.setParameters("accountId", "%" + accountUuid + "%"); - } - - return searchCriteria; - } - - private SearchBuilder createGuiThemeSearchBuilder(String accountUuid, String domainUuid, String commonName, boolean listAll, Boolean showPublic) { - SearchBuilder searchBuilder = createSearchBuilder(); - - searchBuilder.and("id", searchBuilder.entity().getId(), SearchCriteria.Op.EQ); - searchBuilder.and("name", searchBuilder.entity().getName(), SearchCriteria.Op.EQ); - - if (accountUuid != null) { - searchBuilder.and("accountId", searchBuilder.entity().getAccountUuids(), SearchCriteria.Op.LIKE); - } - - if (domainUuid != null) { - searchBuilder.and("domainId", searchBuilder.entity().getDomainUuids(), SearchCriteria.Op.LIKE); - } - - if (commonName != null) { - searchBuilder.and("commonName", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.LIKE); - } - - if (!listAll && showPublic != null) { - searchBuilder.and("isPublic", searchBuilder.entity().getIsPublic(), SearchCriteria.Op.EQ); - } - - searchBuilder.done(); - return searchBuilder; - } - - @Override - public Pair, Integer> listGuiThemesWithNoAuthentication(String commonName) { - SearchCriteria searchCriteria = createGuiThemeSearchCriteria(null, null, commonName, null, null, null, false); - Filter filter = new Filter(GuiThemeVO.class, "created", false); - - return searchAndCount(searchCriteria, filter, false); - } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java new file mode 100644 index 000000000000..b27e6678a29e --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.themes.GuiThemeDetailsVO; + +import java.util.List; + +public interface GuiThemeDetailsDao extends GenericDao { + List listGuiThemeIdsByCommonName(String commonName); + + List listGuiThemeIdsByDomainUuids(String domainUuid); + + void expungeByGuiThemeId(long guiThemeId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java new file mode 100644 index 000000000000..0b3dcabc3ab4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gui.themes.GuiThemeDetailsVO; +import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GuiThemeDetailsDao { + + @Inject + DomainDao domainDao; + + @Inject + GuiThemeDao guiThemeDao; + + @Override + public List listGuiThemeIdsByCommonName(String commonName) { + GenericSearchBuilder detailsDaoSearchBuilder = createSearchBuilder(Long.class); + detailsDaoSearchBuilder.selectFields(detailsDaoSearchBuilder.entity().getGuiThemeId()); + detailsDaoSearchBuilder.and("commonNameType", detailsDaoSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + detailsDaoSearchBuilder.and().op("firstReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE); + detailsDaoSearchBuilder.or("secondReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE).cp(); + detailsDaoSearchBuilder.done(); + + SearchCriteria searchCriteria = detailsDaoSearchBuilder.create(); + searchCriteria.setParameters("commonNameType", "commonName"); + searchCriteria.setParameters("firstReplace", commonName, "*", "%"); + searchCriteria.setParameters("secondReplace", commonName, "*.", "%"); + + return customSearch(searchCriteria, null); + } + + @Override + public List listGuiThemeIdsByDomainUuids(String domainUuid) { + List guiThemeIds = new ArrayList<>(); + String requestedDomainPath = domainDao.findByUuid(domainUuid).getPath(); + + SearchBuilder domainSearchBuilderPathLike = domainDao.createSearchBuilder(); + domainSearchBuilderPathLike.and("pathLike", domainSearchBuilderPathLike.entity().getPath(), SearchCriteria.Op.LIKE_CONCAT); + + SearchBuilder domainSearchBuilderPathEq = domainDao.createSearchBuilder(); + domainSearchBuilderPathEq.and("pathEq", domainSearchBuilderPathEq.entity().getPath(), SearchCriteria.Op.EQ); + + GenericSearchBuilder detailsSearchBuilderPathLike = createDetailsSearchBuilder(domainSearchBuilderPathLike); + SearchCriteria searchCriteriaDomainPathLike = setParametersDomainPathLike(detailsSearchBuilderPathLike, requestedDomainPath); + + GenericSearchBuilder detailsSearchBuilderPathEq = createDetailsSearchBuilder(domainSearchBuilderPathEq); + SearchCriteria searchCriteriaDomainPathEq = setParametersDomainPathEq(detailsSearchBuilderPathEq, requestedDomainPath); + + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathLike, null)); + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathEq, null)); + return guiThemeIds; + } + + private SearchCriteria setParametersDomainPathLike(GenericSearchBuilder detailsSearchBuilderPathLike, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathLike.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathLike", requestedDomainPath, "%"); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", true); + + return searchCriteria; + } + + private SearchCriteria setParametersDomainPathEq(GenericSearchBuilder detailsSearchBuilderPathEq, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathEq.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathEq", requestedDomainPath); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", false); + + return searchCriteria; + } + + private GenericSearchBuilder createDetailsSearchBuilder(SearchBuilder domainSearchBuilder) { + SearchBuilder guiThemeDaoSearchBuilder = guiThemeDao.createSearchBuilder(); + guiThemeDaoSearchBuilder.and("recursiveDomains", guiThemeDaoSearchBuilder.entity().isRecursiveDomains(), SearchCriteria.Op.EQ); + + GenericSearchBuilder guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder = createSearchBuilder(Long.class); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.selectFields(guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId()); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.and("domainUuidType", guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("domainJoin", domainSearchBuilder, domainSearchBuilder.entity().getUuid(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getValue(), JoinBuilder.JoinType.INNER); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("guiThemesJoin", guiThemeDaoSearchBuilder, guiThemeDaoSearchBuilder.entity().getId(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId(), JoinBuilder.JoinType.INNER); + + domainSearchBuilder.done(); + guiThemeDaoSearchBuilder.done(); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.done(); + + return guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder; + } + + @Override + public void expungeByGuiThemeId(long guiThemeId) { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("guiThemeId", searchBuilder.entity().getGuiThemeId(), SearchCriteria.Op.EQ); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + searchCriteria.setParameters("guiThemeId", guiThemeId); + expunge(searchCriteria); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java new file mode 100644 index 000000000000..abd8f59d5216 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; + +import java.util.List; + +public interface GuiThemeJoinDao extends GenericDao { + GuiThemeJoinVO findDefaultTheme(); + + Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); + + Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, Boolean showPublic); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java new file mode 100644 index 000000000000..5ca5cafceb12 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java @@ -0,0 +1,142 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GuiThemeJoinDao { + @Inject + GuiThemeDetailsDao guiThemeDetailsDao; + + public static final Long INVALID_ID = -1L; + + @Override + public GuiThemeJoinVO findDefaultTheme() { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("commonNames", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.NULL); + searchBuilder.and("domainUuids", searchBuilder.entity().getDomains(), SearchCriteria.Op.NULL); + searchBuilder.and("accountUuids", searchBuilder.entity().getAccounts(), SearchCriteria.Op.NULL); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + + return findOneBy(searchCriteria); + } + + @Override + public Pair, Integer> listGuiThemesWithNoAuthentication(String commonName) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(null, null, commonName, null, null, null, false); + return searchOrderByCreatedDate(searchCriteria, false); + } + + @Override + public Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, + boolean showRemoved, Boolean showPublic) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(id, name, commonName, domainUuid, accountUuid, showPublic, listAll); + + if (listAll) { + showRemoved = false; + } + + return searchOrderByCreatedDate(searchCriteria, showRemoved); + } + + private Pair, Integer> searchOrderByCreatedDate(SearchCriteria searchCriteria, boolean showRemoved) { + Filter filter = new Filter(GuiThemeJoinVO.class, "created", false); + return searchAndCount(searchCriteria, filter, showRemoved); + } + + private SearchCriteria createGuiThemeSearchCriteria(Long id, String name, String commonName, String domainUuid, String accountUuid, Boolean showPublic, boolean listAll) { + SearchCriteria searchCriteria = createGuiThemeJoinSearchBuilder(listAll, showPublic).create(); + List idList = new ArrayList<>(); + + if (id != null) { + idList.add(id); + } + + searchCriteria.setParametersIfNotNull("name", name); + searchCriteria.setParametersIfNotNull("isPublic", showPublic); + + if (StringUtils.isNotBlank(accountUuid)) { + searchCriteria.setParameters("accountUuid", "%" + accountUuid + "%"); + } + + if (StringUtils.isNotBlank(commonName)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.COMMON_NAME, commonName); + } + + if (StringUtils.isNotBlank(domainUuid)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.DOMAIN, domainUuid); + } + + searchCriteria.setParametersIfNotNull("idIn", idList.toArray()); + + return searchCriteria; + } + + /** + * Sets the `id IN ( )` clause of the query. If the informed value of common name or domain ID does not retrieve any GUI theme ID; then, an invalid ID (-1) is passed to the + * list, as not a single entity has this ID. This is necessary as to set the parameter even if it did not find any GUI theme ID; otherwise, the query would not filter the + * common name or domain ID passed. + */ + public void setGuiThemeIdsFilteredByType(List idList, String type, String value) { + List guiThemeIdsFilteredByType = new ArrayList<>(); + + switch (type) { + case ApiConstants.COMMON_NAME: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByCommonName(value); + break; + case ApiConstants.DOMAIN: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByDomainUuids(value); + break; + } + + if (CollectionUtils.isNotEmpty(guiThemeIdsFilteredByType)) { + idList.addAll(guiThemeIdsFilteredByType); + return; + } + logger.trace(String.format("No GUI theme with the specified [%s] with UUID [%s] was found, adding an invalid ID for filtering.", type, value)); + idList.add(INVALID_ID); + } + + private SearchBuilder createGuiThemeJoinSearchBuilder(boolean listAll, Boolean showPublic) { + SearchBuilder guiThemeJoinSearchBuilder = createSearchBuilder(); + guiThemeJoinSearchBuilder.and("idIn", guiThemeJoinSearchBuilder.entity().getId(), SearchCriteria.Op.IN); + guiThemeJoinSearchBuilder.and("name", guiThemeJoinSearchBuilder.entity().getName(), SearchCriteria.Op.EQ); + guiThemeJoinSearchBuilder.and("accountUuid", guiThemeJoinSearchBuilder.entity().getAccounts(), SearchCriteria.Op.LIKE); + + if (!listAll && showPublic != null) { + guiThemeJoinSearchBuilder.and("isPublic", guiThemeJoinSearchBuilder.entity().getIsPublic(), SearchCriteria.Op.EQ); + } + + return guiThemeJoinSearchBuilder; + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 724283b0544a..474569f49243 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -302,4 +302,6 @@ + + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql index fc3f8f32b541..e25ebb691189 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql @@ -88,11 +88,38 @@ CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( `description` varchar(4096) DEFAULT NULL COMMENT 'A description for the theme.', `css` text DEFAULT NULL COMMENT 'The CSS to be retrieved and imported into the GUI when matching the theme access configurations.', `json_configuration` text DEFAULT NULL COMMENT 'The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.', - `common_names` text DEFAULT NULL COMMENT 'A set of internet Common Names (CN), fixed of wildcard, separated by comma that can use the theme; e.g.: *acme.com,acme2.com', - `domain_uuids` text DEFAULT NULL COMMENT 'A set of domain IDs separated by comma that can use the theme.', - `account_uuids` text DEFAULT NULL COMMENT 'A set of account IDs separated by comma that can use the theme.', + `recursive_domains` tinyint(1) DEFAULT 0 COMMENT 'Defines whether the subdomains of the informed domains are considered. Default value is false.', `is_public` tinyint(1) default 1 COMMENT 'Defines whether a theme can be retrieved by anyone when only the `internet_domains_names` is informed. If the `domain_uuids` or `account_uuids` is informed, it is considered as `false`.', `created` datetime NOT NULL, `removed` datetime DEFAULT NULL, PRIMARY KEY (`id`) ); + +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `gui_theme_id` bigint(20) unsigned NOT NULL COMMENT 'Foreign key referencing the GUI theme on `gui_themes` table.', + `type` varchar(100) DEFAULT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', + `value` text COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', + PRIMARY KEY (`id`), + CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) +); + +CREATE OR REPLACE + VIEW `cloud`.`gui_themes_view` AS +SELECT + `cloud`.`gui_themes`.`id` AS `id`, + `cloud`.`gui_themes`.`uuid` AS `uuid`, + `cloud`.`gui_themes`.`name` AS `name`, + `cloud`.`gui_themes`.`description` AS `description`, + `cloud`.`gui_themes`.`css` AS `css`, + `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'commonName' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) common_names, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'domain' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) domains, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'account' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) accounts, + `cloud`.`gui_themes`.`recursive_domains` AS `recursive_domains`, + `cloud`.`gui_themes`.`is_public` AS `is_public`, + `cloud`.`gui_themes`.`created` AS `created`, + `cloud`.`gui_themes`.`removed` AS `removed` +FROM `cloud`.`gui_themes` LEFT JOIN `cloud`.`gui_themes_details` ON `cloud`.`gui_themes_details`.`gui_theme_id` = `cloud`.`gui_themes`.`id` +GROUP BY `cloud`.`gui_themes`.`id`; + diff --git a/framework/db/src/main/java/com/cloud/utils/db/SearchBase.java b/framework/db/src/main/java/com/cloud/utils/db/SearchBase.java index b4c27746799b..512941f4ee3c 100644 --- a/framework/db/src/main/java/com/cloud/utils/db/SearchBase.java +++ b/framework/db/src/main/java/com/cloud/utils/db/SearchBase.java @@ -479,6 +479,14 @@ public void toSql(final StringBuilder sql, String tableAlias, final Object[] par sql.append(" FIND_IN_SET(?, "); } + if (op == Op.LIKE_REPLACE) { + sql.append(" ? LIKE REPLACE ("); + } + + if (op == Op.LIKE_CONCAT) { + sql.append(" ? LIKE CONCAT ("); + } + if (tableAlias == null) { if (joinName != null) { tableAlias = joinName; diff --git a/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java b/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java index 4a5349b31f40..15807eb26d42 100644 --- a/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java +++ b/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java @@ -39,7 +39,8 @@ public enum Op { " NOT BETWEEN ? AND ? ", 2), IN(" IN () ", -1), NOTIN(" NOT IN () ", -1), LIKE(" LIKE ? ", 1), NLIKE(" NOT LIKE ? ", 1), NIN(" NOT IN () ", -1), NULL(" IS NULL ", 0), NNULL( " IS NOT NULL ", - 0), SC(" () ", 1), TEXT(" () ", 1), RP("", 0), AND(" AND ", 0), OR(" OR ", 0), NOT(" NOT ", 0), FIND_IN_SET(" ) ", 1), BINARY_OR(" & ?) > 0", 1); + 0), SC(" () ", 1), TEXT(" () ", 1), RP("", 0), AND(" AND ", 0), OR(" OR ", 0), NOT(" NOT ", 0), FIND_IN_SET(" ) ", 1), BINARY_OR(" & ?) > 0", 1), + LIKE_REPLACE(", ?, ?)", 3), LIKE_CONCAT(", ?)", 2); private final String op; int params; @@ -60,7 +61,8 @@ public int getParams() { } public enum Func { - NATIVE("@", 1), MAX("MAX(@)", 1), MIN("MIN(@)", 1), FIRST("FIRST(@)", 1), LAST("LAST(@)", 1), SUM("SUM(@)", 1), COUNT("COUNT(@)", 1), DISTINCT("DISTINCT(@)", 1), DISTINCT_PAIR("DISTINCT @, @", 2); + NATIVE("@", 1), MAX("MAX(@)", 1), MIN("MIN(@)", 1), FIRST("FIRST(@)", 1), LAST("LAST(@)", 1), SUM("SUM(@)", 1), COUNT("COUNT(@)", 1), DISTINCT("DISTINCT(@)", 1), DISTINCT_PAIR("DISTINCT @, @", 2), + CONCAT("CONCAT(@,@)", 2); private String func; private int count; diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index ef4ead1582ca..0a2697c6d496 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -207,7 +207,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.BgpPeerVO; import org.apache.cloudstack.network.RoutedIpv4Manager; @@ -5554,20 +5554,21 @@ public void updateTemplateIsoResponsesForIcons(List responses, } @Override - public GuiThemeResponse createGuiThemeResponse(GuiThemeVO guiThemeVO) { + public GuiThemeResponse createGuiThemeResponse(GuiThemeJoinVO guiThemeJoinVO) { GuiThemeResponse guiThemeResponse = new GuiThemeResponse(); - guiThemeResponse.setId(guiThemeVO.getUuid()); - guiThemeResponse.setName(guiThemeVO.getName()); - guiThemeResponse.setDescription(guiThemeVO.getDescription()); - guiThemeResponse.setCss(guiThemeVO.getCss()); - guiThemeResponse.setJsonConfiguration(guiThemeVO.getJsonConfiguration()); - guiThemeResponse.setCommonNames(guiThemeVO.getCommonNames()); - guiThemeResponse.setDomainIds(guiThemeVO.getDomainUuids()); - guiThemeResponse.setAccountIds(guiThemeVO.getAccountUuids()); - guiThemeResponse.setPublic(guiThemeVO.getIsPublic()); - guiThemeResponse.setCreated(guiThemeVO.getCreated()); - guiThemeResponse.setRemoved(guiThemeVO.getRemoved()); + guiThemeResponse.setId(guiThemeJoinVO.getUuid()); + guiThemeResponse.setName(guiThemeJoinVO.getName()); + guiThemeResponse.setDescription(guiThemeJoinVO.getDescription()); + guiThemeResponse.setCss(guiThemeJoinVO.getCss()); + guiThemeResponse.setJsonConfiguration(guiThemeJoinVO.getJsonConfiguration()); + guiThemeResponse.setCommonNames(guiThemeJoinVO.getCommonNames()); + guiThemeResponse.setDomainIds(guiThemeJoinVO.getDomains()); + guiThemeResponse.setRecursiveDomains(guiThemeJoinVO.isRecursiveDomains()); + guiThemeResponse.setAccountIds(guiThemeJoinVO.getAccounts()); + guiThemeResponse.setPublic(guiThemeJoinVO.getIsPublic()); + guiThemeResponse.setCreated(guiThemeJoinVO.getCreated()); + guiThemeResponse.setRemoved(guiThemeJoinVO.getRemoved()); guiThemeResponse.setResponseName("guithemes"); return guiThemeResponse; diff --git a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java index ece209da3758..d7c1fd5f88af 100644 --- a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java @@ -24,6 +24,8 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; import com.cloud.utils.db.EntityManager; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.exception.CloudRuntimeException; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -39,6 +41,10 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.gui.theme.dao.GuiThemeDao; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDao; +import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao; +import org.apache.cloudstack.gui.themes.GuiThemeDetailsVO; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.apache.cloudstack.gui.themes.GuiThemeService; import org.apache.commons.lang3.StringUtils; @@ -46,7 +52,6 @@ import javax.inject.Inject; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; @@ -69,6 +74,12 @@ public class GuiThemeServiceImpl implements GuiThemeService { @Inject GuiThemeDao guiThemeDao; + @Inject + GuiThemeDetailsDao guiThemeDetailsDao; + + @Inject + GuiThemeJoinDao guiThemeJoinDao; + @Inject ResponseGenerator responseGenerator; @@ -84,7 +95,7 @@ public class GuiThemeServiceImpl implements GuiThemeService { @Override public ListResponse listGuiThemes(ListGuiThemesCmd cmd) { ListResponse response = new ListResponse<>(); - Pair, Integer> result; + Pair, Integer> result; boolean listOnlyDefaultTheme = cmd.getListOnlyDefaultTheme(); if (listOnlyDefaultTheme) { @@ -97,8 +108,8 @@ public ListResponse listGuiThemes(ListGuiThemesCmd cmd) { } List guiThemeResponses = new ArrayList<>(); - for (GuiThemeVO guiThemeVO : result.first()) { - GuiThemeResponse guiThemeResponse = responseGenerator.createGuiThemeResponse(guiThemeVO); + for (GuiThemeJoinVO guiThemeJoinVO : result.first()) { + GuiThemeResponse guiThemeResponse = responseGenerator.createGuiThemeResponse(guiThemeJoinVO); guiThemeResponses.add(guiThemeResponse); } @@ -106,9 +117,9 @@ public ListResponse listGuiThemes(ListGuiThemesCmd cmd) { return response; } - private Pair, Integer> retrieveDefaultTheme() { - GuiThemeVO defaultTheme = guiThemeDao.findDefaultTheme(); - List list = new ArrayList<>(); + private Pair, Integer> retrieveDefaultTheme() { + GuiThemeJoinVO defaultTheme = guiThemeJoinDao.findDefaultTheme(); + List list = new ArrayList<>(); if (defaultTheme != null) { list.add(defaultTheme); @@ -119,7 +130,7 @@ private Pair, Integer> retrieveDefaultTheme() { @Override @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_CREATE, eventDescription = "Creating GUI theme") - public GuiThemeVO createGuiTheme(CreateGuiThemeCmd cmd) { + public GuiThemeJoinVO createGuiTheme(CreateGuiThemeCmd cmd) { String name = cmd.getName(); String description = cmd.getDescription(); String css = cmd.getCss(); @@ -128,6 +139,7 @@ public GuiThemeVO createGuiTheme(CreateGuiThemeCmd cmd) { String providedDomainIds = cmd.getDomainIds(); String providedAccountIds = cmd.getAccountIds(); boolean isPublic = cmd.getPublic(); + Boolean recursiveDomains = cmd.getRecursiveDomains(); if (StringUtils.isAllBlank(css, jsonConfiguration)) { throw new CloudRuntimeException("Either the `css` or `jsonConfiguration` parameter must be informed."); @@ -139,8 +151,27 @@ public GuiThemeVO createGuiTheme(CreateGuiThemeCmd cmd) { isPublic = false; } - GuiThemeVO guiThemeVO = new GuiThemeVO(name, description, css, jsonConfiguration, commonNames, providedDomainIds, providedAccountIds, isPublic, new Date(), null); - return guiThemeDao.persist(guiThemeVO); + GuiThemeVO guiThemeVO = new GuiThemeVO(name, description, css, jsonConfiguration, recursiveDomains, isPublic, new Date(), null); + guiThemeDao.persist(guiThemeVO); + persistGuiThemeDetails(guiThemeVO.getId(), commonNames, providedDomainIds, providedAccountIds); + return guiThemeJoinDao.findById(guiThemeVO.getId()); + } + + protected void persistGuiThemeDetails(long guiThemeId, String commonNames, String providedDomainIds, String providedAccountIds) { + persistDetailValueIfNotNull(guiThemeId, commonNames, "commonName"); + persistDetailValueIfNotNull(guiThemeId, providedDomainIds, "domain"); + persistDetailValueIfNotNull(guiThemeId, providedAccountIds, "account"); + } + + protected void persistDetailValueIfNotNull(long guiThemeId, String providedParameter, String type) { + if (providedParameter == null) { + logger.trace(String.format("GUI theme provided parameter `%s` is null; therefore, it will be ignored.", type)); + return; + } + for (String splitParameter : StringUtils.deleteWhitespace(providedParameter).split(",")) { + guiThemeDetailsDao.persist(new GuiThemeDetailsVO(guiThemeId, type, splitParameter)); + } + } protected boolean shouldSetGuiThemeToPrivate(String providedDomainIds, String providedAccountIds) { @@ -177,21 +208,21 @@ protected void checkIfDefaultThemeIsAllowed(String commonNames, String providedD return; } - GuiThemeVO defaultTheme = guiThemeDao.findDefaultTheme(); + GuiThemeJoinVO defaultTheme = guiThemeJoinDao.findDefaultTheme(); - if (defaultTheme != null && (idOfThemeToBeUpdated == null || defaultTheme.getId() != idOfThemeToBeUpdated.longValue())) { + if (defaultTheme != null && (idOfThemeToBeUpdated == null || defaultTheme.getId() != idOfThemeToBeUpdated)) { throw new CloudRuntimeException(String.format("Only one default GUI theme is allowed. Remove the current default theme %s and try again.", defaultTheme)); } logger.info("The parameters `commonNames`, `domainIds` and `accountIds` were not informed. The created theme will be considered as the default theme."); } - protected Pair, Integer> listGuiThemesWithNoAuthentication(ListGuiThemesCmd cmd) { - return guiThemeDao.listGuiThemesWithNoAuthentication(cmd.getCommonName()); + protected Pair, Integer> listGuiThemesWithNoAuthentication(ListGuiThemesCmd cmd) { + return guiThemeJoinDao.listGuiThemesWithNoAuthentication(cmd.getCommonName()); } - protected Pair, Integer> listGuiThemesInternal(ListGuiThemesCmd cmd) { + protected Pair, Integer> listGuiThemesInternal(ListGuiThemesCmd cmd) { Long id = cmd.getId(); String name = cmd.getName(); String commonName = cmd.getCommonName(); @@ -201,7 +232,7 @@ protected Pair, Integer> listGuiThemesInternal(ListGuiThemesCmd boolean showRemoved = cmd.getShowRemoved(); Boolean showPublic = cmd.getShowPublic(); - return guiThemeDao.listGuiThemes(id, name, commonName, domainUuid, accountUuid, listAll, showRemoved, showPublic); + return guiThemeJoinDao.listGuiThemes(id, name, commonName, domainUuid, accountUuid, listAll, showRemoved, showPublic); } protected void validateParameters(String jsonConfig, String domainIds, String accountIds, String commonNames, Long idOfThemeToBeUpdated) { @@ -302,7 +333,7 @@ protected void warnOfInvalidJsonAttribute(String entryKey) { } /** - * Validate if the comma separated list of UUIDs of the fields {@link GuiThemeVO#accountUuids} and {@link GuiThemeVO#domainUuids} are valid. + * Validate if the comma separated list of UUIDs of the fields {@link GuiThemeJoinVO#getAccounts()} and {@link GuiThemeJoinVO#getDomains()} are valid. * @param providedIds a comma separated list of UUIDs of {@link Account} or {@link Domain} * @param clazz the class to infer the DAO object. Valid options are: {@link Account} and {@link Domain} */ @@ -311,7 +342,7 @@ protected void validateObjectUuids(String providedIds, Class clazz) { return; } - List commaSeparatedIds = new ArrayList<>(Arrays.asList(providedIds.split("\\s*,\\s*"))); + String[] commaSeparatedIds = providedIds.split("\\s*,\\s*"); for (String id : commaSeparatedIds) { Object objectVO = entityManager.findByUuid(clazz, id); @@ -323,18 +354,19 @@ protected void validateObjectUuids(String providedIds, Class clazz) { @Override @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_UPDATE, eventDescription = "Updating GUI theme") - public GuiThemeVO updateGuiTheme(UpdateGuiThemeCmd cmd) { + public GuiThemeJoinVO updateGuiTheme(UpdateGuiThemeCmd cmd) { Long guiThemeId = cmd.getId(); - GuiThemeVO guiThemeVO = guiThemeDao.findById(guiThemeId); + GuiThemeJoinVO guiThemeJoinVO = guiThemeJoinDao.findById(guiThemeId); String name = cmd.getName(); String description = cmd.getDescription(); String css = cmd.getCss(); String jsonConfiguration = cmd.getJsonConfiguration(); - String commonNames = cmd.getCommonNames() == null ? guiThemeVO.getCommonNames() : cmd.getCommonNames(); - String providedDomainIds = cmd.getDomainIds() == null ? guiThemeVO.getDomainUuids() : cmd.getDomainIds(); - String providedAccountIds = cmd.getAccountIds() == null ? guiThemeVO.getAccountUuids() : cmd.getAccountIds(); + String commonNames = cmd.getCommonNames() == null ? guiThemeJoinVO.getCommonNames() : cmd.getCommonNames(); + String providedDomainIds = cmd.getDomainIds() == null ? guiThemeJoinVO.getDomains() : cmd.getDomainIds(); + String providedAccountIds = cmd.getAccountIds() == null ? guiThemeJoinVO.getAccounts() : cmd.getAccountIds(); Boolean isPublic = cmd.getIsPublic(); + Boolean recursiveDomains = cmd.getRecursiveDomains(); validateParameters(jsonConfiguration, providedDomainIds, providedAccountIds, commonNames, guiThemeId); @@ -342,45 +374,46 @@ public GuiThemeVO updateGuiTheme(UpdateGuiThemeCmd cmd) { isPublic = false; } - return persistGuiTheme(name, description, css, jsonConfiguration, commonNames, providedDomainIds, providedAccountIds, isPublic, guiThemeVO); - + return persistGuiTheme(guiThemeId, name, description, css, jsonConfiguration, commonNames, providedDomainIds, providedAccountIds, isPublic, recursiveDomains); } - protected GuiThemeVO persistGuiTheme(String name, String description, String css, String jsonConfiguration, String commonNames, String providedDomainIds, - String providedAccountIds, Boolean isPublic, GuiThemeVO guiThemeVO){ - if (name != null) { - guiThemeVO.setName(ifBlankReturnNull(name)); - } + protected GuiThemeJoinVO persistGuiTheme(Long guiThemeId, String name, String description, String css, String jsonConfiguration, String commonNames, String providedDomainIds, + String providedAccountIds, Boolean isPublic, Boolean recursiveDomains){ + return Transaction.execute((TransactionCallback) status -> { + GuiThemeVO guiThemeVO = guiThemeDao.findById(guiThemeId); - if (description != null) { - guiThemeVO.setDescription(ifBlankReturnNull(description)); - } + if (name != null) { + guiThemeVO.setName(ifBlankReturnNull(name)); + } - if (css != null) { - guiThemeVO.setCss(css); - } + if (description != null) { + guiThemeVO.setDescription(ifBlankReturnNull(description)); + } - if (jsonConfiguration != null) { - guiThemeVO.setJsonConfiguration(jsonConfiguration); - } + if (css != null) { + guiThemeVO.setCss(css); + } - if (commonNames != null) { - guiThemeVO.setCommonNames(ifBlankReturnNull(commonNames)); - } + if (jsonConfiguration != null) { + guiThemeVO.setJsonConfiguration(jsonConfiguration); + } - if (providedAccountIds != null) { - guiThemeVO.setAccountUuids(ifBlankReturnNull(providedAccountIds)); - } + if (isPublic != null) { + guiThemeVO.setIsPublic(isPublic); + } - if (providedDomainIds != null) { - guiThemeVO.setDomainUuids(ifBlankReturnNull(providedDomainIds)); - } + if (recursiveDomains != null) { + guiThemeVO.setRecursiveDomains(recursiveDomains); + } - if (isPublic != null) { - guiThemeVO.setIsPublic(isPublic); - } + logger.trace(String.format("Persisting GUI theme [%s] with CSS [%s] and JSON configuration [%s].", guiThemeVO, guiThemeVO.getCss(), guiThemeVO.getJsonConfiguration())); + + guiThemeDao.persist(guiThemeVO); + guiThemeDetailsDao.expungeByGuiThemeId(guiThemeId); + persistGuiThemeDetails(guiThemeId, commonNames, providedDomainIds, providedAccountIds); - return guiThemeDao.persist(guiThemeVO); + return guiThemeJoinDao.findById(guiThemeId); + }); } protected String ifBlankReturnNull(String value) { @@ -399,7 +432,8 @@ public void removeGuiTheme(RemoveGuiThemeCmd cmd) { if (guiThemeVO != null) { guiThemeDao.remove(guiThemeId); } else { - throw new CloudRuntimeException(String.format("Unable to find a GUI theme with the specified UUID.")); + logger.error(String.format("Unable to find a GUI theme with the specified ID [%s].", guiThemeId)); + throw new CloudRuntimeException("Unable to find a GUI theme with the specified ID."); } } } diff --git a/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java index 5eeaf7bcfb54..4deec868a805 100644 --- a/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java @@ -22,32 +22,31 @@ import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.theme.dao.GuiThemeDao; +import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao; +import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.Spy; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; -@RunWith(PowerMockRunner.class) -@PrepareForTest(CallContext.class) +@RunWith(MockitoJUnitRunner.class) public class GuiThemeServiceImplTest { @Mock - GuiThemeDao guiThemeDaoMock; + GuiThemeJoinDao guiThemeJoinDaoMock; @Mock - GuiThemeVO guiThemeVOMock; + GuiThemeJoinVO guiThemeJoinVOMock; @Mock EntityManager entityManagerMock; @@ -55,9 +54,6 @@ public class GuiThemeServiceImplTest { @Mock Object objectMock; - @Mock - CallContext callContextMock; - @Mock ListGuiThemesCmd listGuiThemesCmdMock; @@ -75,47 +71,43 @@ public class GuiThemeServiceImplTest { @Test public void listGuiThemesTestShouldIgnoreParametersWhenItIsCalledUnauthenticated() { - Pair, Integer> emptyPair = new Pair<>(new ArrayList<>(), 0); - PowerMockito.mockStatic(CallContext.class); - PowerMockito.when(CallContext.current()).thenReturn(callContextMock); + Pair, Integer> emptyPair = new Pair<>(new ArrayList<>(), 0); Long accountId = Account.ACCOUNT_ID_SYSTEM; - Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); - Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); - Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(false); - guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); - Mockito.verify(guiThemeServiceSpy, Mockito.times(1)).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); + try (MockedStatic callContextMocked = Mockito.mockStatic(CallContext.class)) { + CallContext callContextMock = Mockito.mock(CallContext.class); + callContextMocked.when(CallContext::current).thenReturn(callContextMock); + Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); + Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); + Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(false); + guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); + Mockito.verify(guiThemeServiceSpy, Mockito.times(1)).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); + } } @Test public void listGuiThemesTestShouldCallNormalFlowWhenAuthenticated() { Pair, Integer> emptyPair = new Pair<>(new ArrayList<>(), 0); - PowerMockito.mockStatic(CallContext.class); - PowerMockito.when(CallContext.current()).thenReturn(callContextMock); Long accountId = 3L; - Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); - Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesInternal(Mockito.nullable(ListGuiThemesCmd.class)); - Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(false); - guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); - Mockito.verify(guiThemeServiceSpy, Mockito.times(1)).listGuiThemesInternal(Mockito.nullable(ListGuiThemesCmd.class)); + try (MockedStatic callContextMocked = Mockito.mockStatic(CallContext.class)) { + CallContext callContextMock = Mockito.mock(CallContext.class); + callContextMocked.when(CallContext::current).thenReturn(callContextMock); + Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); + Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesInternal(Mockito.nullable(ListGuiThemesCmd.class)); + Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(false); + guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); + Mockito.verify(guiThemeServiceSpy, Mockito.times(1)).listGuiThemesInternal(Mockito.nullable(ListGuiThemesCmd.class)); + } } @Test public void listGuiThemesTestListOnlyDefaultThemesShouldCallFindDefaultTheme() { - Pair, Integer> emptyPair = new Pair<>(new ArrayList<>(), 0); - PowerMockito.mockStatic(CallContext.class); - PowerMockito.when(CallContext.current()).thenReturn(callContextMock); - - Long accountId = Account.ACCOUNT_ID_SYSTEM; - - Mockito.doReturn(accountId).when(callContextMock).getCallingAccountId(); - Mockito.doReturn(emptyPair).when(guiThemeServiceSpy).listGuiThemesWithNoAuthentication(Mockito.nullable(ListGuiThemesCmd.class)); Mockito.when(listGuiThemesCmdMock.getListOnlyDefaultTheme()).thenReturn(true); guiThemeServiceSpy.listGuiThemes(listGuiThemesCmdMock); - Mockito.verify(guiThemeDaoMock, Mockito.times(1)).findDefaultTheme(); + Mockito.verify(guiThemeJoinDaoMock, Mockito.times(1)).findDefaultTheme(); } @Test @@ -192,7 +184,7 @@ public void checkIfDefaultThemeIsAllowedTestThemeIsNotConsideredDefault() { @Test public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndThereIsNotADefaultThemeRegistered() { Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); - Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(null); + Mockito.when(guiThemeJoinDaoMock.findDefaultTheme()).thenReturn(null); guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, null); } @@ -200,7 +192,7 @@ public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndThereIsNo @Test(expected = CloudRuntimeException.class) public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndThereIsADefaultThemeRegisteredShouldThrowCloudRuntimeException() { Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); - Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(guiThemeVOMock); + Mockito.when(guiThemeJoinDaoMock.findDefaultTheme()).thenReturn(guiThemeJoinVOMock); guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, null); } @@ -208,8 +200,8 @@ public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndThereIsAD @Test(expected = CloudRuntimeException.class) public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndWillBeUpdatedAndIsDifferentIdShouldThrowCloudRuntimeException() { Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); - Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(guiThemeVOMock); - Mockito.when(guiThemeVOMock.getId()).thenReturn(1L); + Mockito.when(guiThemeJoinDaoMock.findDefaultTheme()).thenReturn(guiThemeJoinVOMock); + Mockito.when(guiThemeJoinVOMock.getId()).thenReturn(1L); guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, 2L); } @@ -217,8 +209,8 @@ public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndWillBeUpd @Test public void checkIfDefaultThemeIsAllowedTestThemeIsConsideredDefaultAndWillBeUpdatedAndIsTheSameShouldAllowUpdate() { Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true); - Mockito.when(guiThemeDaoMock.findDefaultTheme()).thenReturn(guiThemeVOMock); - Mockito.when(guiThemeVOMock.getId()).thenReturn(1L); + Mockito.when(guiThemeJoinDaoMock.findDefaultTheme()).thenReturn(guiThemeJoinVOMock); + Mockito.when(guiThemeJoinVOMock.getId()).thenReturn(1L); guiThemeServiceSpy.checkIfDefaultThemeIsAllowed(BLANK_STRING, BLANK_STRING, BLANK_STRING, 1L); } From 69ce3dbae32573ee166ff7f314476b9bd25139ee Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 16 Apr 2024 14:13:30 -0300 Subject: [PATCH 03/22] Update since and remove extra line --- .../api/command/user/gui/themes/CreateGuiThemeCmd.java | 2 +- .../api/command/user/gui/themes/ListGuiThemesCmd.java | 3 ++- .../api/command/user/gui/themes/RemoveGuiThemeCmd.java | 2 +- .../api/command/user/gui/themes/UpdateGuiThemeCmd.java | 2 +- ui/src/main.js | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java index 9990a551d151..578d0b8c8322 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java @@ -33,7 +33,7 @@ @APICommand(name = "createGuiTheme", description = "Creates a customized GUI theme for a set of Common Names (fixed or wildcard), a set of domain UUIDs, and/or a set of " + "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, - since = "4.18.0.4-scclouds", authorized = {RoleType.Admin}) + since = "4.20.0.0", authorized = {RoleType.Admin}) public class CreateGuiThemeCmd extends BaseCmd { @Inject diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java index 9f2abea586ac..bc9c145f138f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java @@ -31,7 +31,8 @@ import javax.inject.Inject; @APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, - since = "4.18.0.4-scclouds", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, RoleType.ResourceAdmin}) + since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, + RoleType.ResourceAdmin}) public class ListGuiThemesCmd extends BaseListCmd { @Inject diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java index 8c8f46c7bf75..9bc718c893f8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java @@ -30,7 +30,7 @@ import javax.inject.Inject; @APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, - since = "4.18.0.4-scclouds", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) + since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) public class RemoveGuiThemeCmd extends BaseCmd { @Inject diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java index 2009c659650e..c6529208a3e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java @@ -33,7 +33,7 @@ @APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, - since = "4.18.0.4-scclouds", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) + since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) public class UpdateGuiThemeCmd extends BaseCmd { @Inject diff --git a/ui/src/main.js b/ui/src/main.js index 7ccd9d8a9ee3..6917fa986e04 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -63,7 +63,6 @@ vueApp.use(imagesUtilPlugin) vueApp.use(extensions) vueApp.use(directives) - fetch('config.json?ts=' + Date.now()).then(response => response.json()).then(async config => { vueProps.$config = config let basUrl = config.apiBase From cdb6e893fa219b9a77b9e04086f9aaef75eb186b Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 16 Apr 2024 16:41:27 -0300 Subject: [PATCH 04/22] Limit information on API response for non admin users --- .../java/com/cloud/api/ApiResponseHelper.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 0a2697c6d496..984e4490fbaa 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -5557,18 +5557,22 @@ public void updateTemplateIsoResponsesForIcons(List responses, public GuiThemeResponse createGuiThemeResponse(GuiThemeJoinVO guiThemeJoinVO) { GuiThemeResponse guiThemeResponse = new GuiThemeResponse(); - guiThemeResponse.setId(guiThemeJoinVO.getUuid()); - guiThemeResponse.setName(guiThemeJoinVO.getName()); - guiThemeResponse.setDescription(guiThemeJoinVO.getDescription()); - guiThemeResponse.setCss(guiThemeJoinVO.getCss()); + Long callerId = CallContext.current().getCallingAccount().getAccountId(); + if (callerId != Account.ACCOUNT_ID_SYSTEM && _accountMgr.isRootAdmin(callerId)) { + guiThemeResponse.setId(guiThemeJoinVO.getUuid()); + guiThemeResponse.setName(guiThemeJoinVO.getName()); + guiThemeResponse.setDescription(guiThemeJoinVO.getDescription()); + guiThemeResponse.setCommonNames(guiThemeJoinVO.getCommonNames()); + guiThemeResponse.setDomainIds(guiThemeJoinVO.getDomains()); + guiThemeResponse.setRecursiveDomains(guiThemeJoinVO.isRecursiveDomains()); + guiThemeResponse.setAccountIds(guiThemeJoinVO.getAccounts()); + guiThemeResponse.setPublic(guiThemeJoinVO.getIsPublic()); + guiThemeResponse.setCreated(guiThemeJoinVO.getCreated()); + guiThemeResponse.setRemoved(guiThemeJoinVO.getRemoved()); + } + guiThemeResponse.setJsonConfiguration(guiThemeJoinVO.getJsonConfiguration()); - guiThemeResponse.setCommonNames(guiThemeJoinVO.getCommonNames()); - guiThemeResponse.setDomainIds(guiThemeJoinVO.getDomains()); - guiThemeResponse.setRecursiveDomains(guiThemeJoinVO.isRecursiveDomains()); - guiThemeResponse.setAccountIds(guiThemeJoinVO.getAccounts()); - guiThemeResponse.setPublic(guiThemeJoinVO.getIsPublic()); - guiThemeResponse.setCreated(guiThemeJoinVO.getCreated()); - guiThemeResponse.setRemoved(guiThemeJoinVO.getRemoved()); + guiThemeResponse.setCss(guiThemeJoinVO.getCss()); guiThemeResponse.setResponseName("guithemes"); return guiThemeResponse; From 1af256b0a058fa57e705c80ef700ab9020b786cd Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Thu, 18 Apr 2024 11:58:32 -0300 Subject: [PATCH 05/22] Add base files for preset themes --- ui/public/assets/asf-logo.svg | 116 +++ ui/public/assets/feather.svg | 89 ++ ui/public/css/apache-theme.css | 1151 +++++++++++++++++++++++ ui/public/css/dark-theme.css | 1574 ++++++++++++++++++++++++++++++++ ui/public/index.html | 8 - ui/src/main.js | 2 +- 6 files changed, 2931 insertions(+), 9 deletions(-) create mode 100644 ui/public/assets/asf-logo.svg create mode 100644 ui/public/assets/feather.svg create mode 100644 ui/public/css/apache-theme.css create mode 100644 ui/public/css/dark-theme.css diff --git a/ui/public/assets/asf-logo.svg b/ui/public/assets/asf-logo.svg new file mode 100644 index 000000000000..ce1f394599ad --- /dev/null +++ b/ui/public/assets/asf-logo.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/public/assets/feather.svg b/ui/public/assets/feather.svg new file mode 100644 index 000000000000..7c6a07b885db --- /dev/null +++ b/ui/public/assets/feather.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/public/css/apache-theme.css b/ui/public/css/apache-theme.css new file mode 100644 index 000000000000..e00f91bba343 --- /dev/null +++ b/ui/public/css/apache-theme.css @@ -0,0 +1,1151 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); + +:root { + --main-verde: #3E7C59; + --main-vermelho: #ae0000; + --main-normal:#327355; + --main-exception:#ae0000; + --main-primary-claro: #db4e2d22; + --main-primary-escuro: #db4e2d; + --main-secondary-claro: #E9782622; + --main-secondary-escuro: #E97826; + --main-grey-claro: #eee; + --main-grey-escuro: #6D6E71; + --main-image-menu: url("../assets/asf-logo.svg"); + --main-image-login: url("../assets/asf-logo.svg"); + --main-image-icon: url("../assets/feather.svg"); + --main-linear-cora: #ddd; + --main-linear-corb: #ddd; +} + +#app > div > form > img.user-layout-logo{ + display:none !important; +} + +#app > div > form{ + background-image:var(--main-image-login); + background-repeat:no-repeat; + background-size:250px; + background-position:top center; + padding-top:120px; +} + +.userLayout .user-layout-header{ + min-height:100px; + background-image:var(--main-image-login); + background-repeat:no-repeat; + background-size:250px; + background-position:top center; +} + +.ant-layout-sider.light.ant-fixed-sidemenu > div > div{ + height: 100px !important; + background-image:var(--main-image-menu); + background-repeat:no-repeat; + background-size:200px; + background-position:25px 25px; + margin-bottom:20px; +} + +.ant-layout-sider.light.ant-fixed-sidemenu.ant-layout-sider-collapsed > div > div{ + height: 70px !important; + background-image:var(--main-image-icon); + background-repeat:no-repeat; + background-size:30px; + background-position:25px 20px; +} + +.sider.light .ant-menu-light a, +.sider.light .ant-menu-submenu > .ant-menu-submenu-title { + color:#000 !important; +} + +.sider.light .ant-menu-light a path{ + +} + +.ant-menu:not(.ant-menu-horizontal) span.ant-menu-title-content .custom-icon path{ + transition:fill 1s easyin; +} + +.ant-menu:not(.ant-menu-horizontal) span.ant-menu-title-content:hover .custom-icon path{ + fill:#fff !important; +} + +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-active:hover a, +.ant-menu:not(.ant-menu-horizontal) .ant-menu-submenu .ant-menu-submenu-title:hover{ + color:#fff !important; +} + +.ant-menu-submenu ul a, +.ant-menu-submenu ul .ant-menu-item-active.ant-menu-item-selected a span{ + color:#fff !important; +} + +.ant-menu-submenu ul .ant-menu-item-selected a span{ + color:#000 !important; +} + +*{ + font-family: 'Poppins' , Courier !important; +} + +aside, +.ant-menu-submenu ul{ + background: linear-gradient(var(--main-linear-cora), var(--main-linear-corb)) !important; +} + +html{ + background-color: var(--main-secondary-escuro); +} + +/* HEADER */ +.layout.ant-layout .header{ + background: linear-gradient(var(--main-linear-cora), var(--main-linear-corb)) !important; + box-shadow: 0px 0px 10px #00000055; +} + +.layout.ant-layout .header .user-menu > span.ant-dropdown-open{ + background-color:var(--main-primary-claro) !important; + border-bottom:5px solid var(--main-primary-escuro) !important; +} + +.layout.ant-layout .header .anticon-menu-unfold:hover, +.layout.ant-layout .header .anticon-menu-fold:hover, +.layout.ant-layout .header .user-menu > span:hover{ + background-color:var(--main-primary-escuro) !important; + color:#fff !important; +} + +.layout.ant-layout .header .user-menu > span:hover *{ + color:#fff !important; +} +.layout.ant-layout .header .user-menu > span *{ + color:#000 !important; +} + +.layout.ant-layout .header .anticon-menu-unfold, +.layout.ant-layout .header .anticon-menu-fold{ + color:#000; +} + +.header-notice-opener{ + padding-left:15px; +} + + +.ant-table-tbody > .ant-table-row:hover td{ + background-color:var(--main-secondary-claro) !important; +} + +/* BARRA LATERAL */ + +.vm-info-card .ant-card-body .ant-card-bordered{ + border-top:2px solid var(--main-primary-escuro) !important; + margin-bottom:50px; +} + +.vm-info-card .ant-card-body .ant-card-bordered .ant-card-head, +.vm-info-card .ant-card-body .ant-card-bordered .ant-card-grid{ + padding:10px 0px !important; +} + +.vm-info-card .ant-card-body .ant-card-bordered .resource-detail-item{ + margin-bottom:20px; +} + +.vm-info-card .ant-card-body .ant-card-bordered .resource-detail-item .resource-detail-item__label{ + font-weight:900; +} + +/* TELA USER LOGIN */ + +.ant-tabs-tab, +.user-layout{ + background-color:var(--main-grey-claro) !important; +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + background-color:var(--main-grey-claro) !important; + color: var(--main-primary-escuro); + border-bottom:0px solid var(--main-primary-escuro); +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab:hover{ + background-color:var(--main-secondary-escuro) !important; + color: #fff; + border-bottom:3px solid var(--main-primary-escuro); +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active{ + background-color:var(--main-grey-claro) !important; + color: var(--main-primary-escuro); + border-bottom:3px solid var(--main-primary-escuro); +} + +.user-layout .ant-tabs-nav{ + width:100%; +} + +.user-layout .ant-tabs-nav > div > div{ + width:50%; +} + +/* IMAGES LOGO */ +.userLayout .user-layout-logo{ + display:none; +} + +.ant-layout-sider.light.ant-fixed-sidemenu > div > div > img{ + display:none !important; +} + +/* PROGRESS */ +.ant-progress.ant-progress-status-active .ant-progress-inner, +.ant-progress.ant-progress-status-normal .ant-progress-inner{ + background-color:var(--main-grey-claro) !important; +} + +.ant-progress.ant-progress-status-active .ant-progress-inner .ant-progress-bg, +.ant-progress.ant-progress-status-normal .ant-progress-inner .ant-progress-bg{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-progress .ant-progress-text{ + color:var(--main-grey-escuro) !important; + font-weight:900; +} + +.ant-progress.ant-progress-status-exception .ant-progress-inner .ant-progress-bg{ + background-color:var(--main-exception) !important; +} + +/* FORMS */ +.ant-select-selector, +.ant-input{ + background-color:white !important; +} + +.ant-select.ant-select-focused .ant-select-arrow .anticon{ + color:var(--main-grey-claro); +} + +.ant-select-selector .ant-select-selection-placeholder, +.ant-select, +.ant-input::placeholder{ + color:var(--main-grey-escuro) !important; +} + +.ant-select-selector:hover, .ant-select-selector:focus, +.ant-input:hover, .ant-input:focus{ + box-shadow: 0px 0px 3px var(--main-primary-escuro); + border-color:var(--main-primary-escuro) !important; +} + +.ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) .ant-select-selector { + border-color: var(--main-primary-escuro); + border-right-width: 1px !important; + box-shadow: 0 0 0 2px var(--main-primary-claro); +} + +.ant-select-dropdown .ant-select-item.ant-select-item-option-selected, +.ant-select-dropdown .ant-select-item:hover{ + background-color:var(--main-primary-claro) !important; +} + +/* PAGINACAO */ +.ant-pagination .ant-pagination-item.ant-pagination-item-active{ + background-color:var(--main-primary-escuro) !important; + border-color:#fff; +} + +.ant-pagination .ant-pagination-item.ant-pagination-item-active a{ + color:#fff !important; + font-weight:bold; +} + +.ant-pagination .ant-pagination-item:not(.ant-pagination-item-active){ + background-color:#fff !important; +} + +.ant-pagination .ant-pagination-item:not(.ant-pagination-item-active):hover{ + background-color:var(--main-secondary-escuro) !important; + border-color:#fff; +} + +.ant-pagination .ant-pagination-item:not(.ant-pagination-item-active):hover a{ + color:#fff !important; + font-weight:bold; +} + +.ant-pagination .ant-pagination-next span, +.ant-pagination .ant-pagination-prev span{ + color:var(--main-grey-escuro) !important; +} + +.ant-pagination .ant-pagination-next span:hover, +.ant-pagination .ant-pagination-prev span:hover{ + color:var(--main-secondary-escuro) !important; +} + + +/* RADIO GROUP */ +.ant-radio-group label.ant-radio-button-wrapper-checked{ + background-color:var(--main-primary-escuro) !important; + border-color:var(--main-primary-escuro) !important; +} + +.ant-radio-group label:not(.ant-radio-button-wrapper-checked):hover{ + color:#000 !important; + background-color:var(--main-primary-claro) !important; +} + +.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)::before { + background-color:var(--main-primary-escuro) !important; +} + +/* TABLES */ +.ant-table{ + font-size:16px !important; +} + +.ant-table th{ + border-bottom:5px solid var(--main-primary-claro) !important; +} + +.ant-table th.ant-table-column-has-sorters:hover{ + border-bottom:5px solid var(--main-primary-escuro) !important; +} + +.ant-table thead.ant-table-thead{ + background-color:Red !important; + border-bottom:5px solid var(--main-primary-escuro) !important; +} + +.anticon.ant-table-column-sorter-up.on *, +.anticon.ant-table-column-sorter-down.on *{ + color: var(--main-primary-escuro) !important; +} + +/* PAGE TABS LEFT */ +.ant-tabs-left-bar .ant-tabs-nav-scroll { + border-right:3px solid var(--main-primary-escuro); +} + +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + margin:0px; + padding:10px 15px; + border-right:3px solid transparent; + transform: all 1s easyin !important; + background-color:#fff !important; +} + +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active, +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-tab:hover{ + background-color:var(--main-grey-claro) !important; + color: var(--main-primary-escuro); + border-right:3px solid var(--main-primary-escuro); +} + +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-ink-bar{ + display:none !important; + width:0px !important; + background-color:var(--main-primary-escuro) !important; +} + +/* PAGE TABS TOP */ +.ant-tabs-tab-next, +.ant-tabs-tab-prev{ + color:var(--main-primary-escuro) !important; +} + +.ant-tabs-tab-next svg, +.ant-tabs-tab-prev svg{ + transform: scale(1.5); +} + +.ant-tabs-tab-next:not(.ant-tabs-tab-btn-disabled):hover, +.ant-tabs-tab-prev:not(.ant-tabs-tab-btn-disabled):hover{ + background-color:var(--main-primary-escuro) !important; + color:#fff !important; +} + +.ant-tabs-tab-next.ant-tabs-tab-btn-disabled, +.ant-tabs-tab-prev.ant-tabs-tab-btn-disabled{ + color:var(--main-primary-claro) !important; +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll { + border-bottom:3px solid var(--main-primary-escuro); +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + margin:0px; + padding:14px 30px; + border-bottom:3px solid transparent; + transform: all 1s easyin !important; + background-color:#fff !important; +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active, +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab:hover{ + background-color:var(--main-grey-claro) !important; + color: var(--main-primary-escuro); + border-bottom:3px solid var(--main-primary-escuro); +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-ink-bar{ + height:0px !important; + background-color:var(--main-primary-escuro) !important; +} + +/* LINKS */ +a{ + color: var(--main-primary-escuro); +} + +a:hover{ + color: var(--main-secondary-escuro); +} + +.ant-breadcrumb-link a:hover{ + color: var(--main-primary-escuro) !important;; +} + +.ant-breadcrumb-link .anticon{ + margin-top:5px !important; +} + +.ant-breadcrumb-link .ant-select-arrow .anticon{ + margin-top:0px !important; +} + +/* USER MENU */ +.user-menu > span{ + border-bottom:5px solid transparent; +} + +.user-menu > span:hover{ + border-bottom:5px solid var(--main-primary-escuro); + background-color:var(--main-grey-claro) !important; +} + +.ant-dropdown .ant-dropdown-menu-item:hover{ + background-color:var(--main-primary-claro) !important; +} + +.ant-dropdown .ant-dropdown-menu-item:hover *{ + color:#000; +} + +.ant-dropdown .ant-dropdown-menu-item.ant-dropdown-menu-item-selected, +.ant-dropdown .ant-dropdown-menu-item.ant-dropdown-menu-item-selected:hover *{ + background-color:var(--main-primary-escuro) !important; + color:#fff !important; +} + +/* CHECKBOXES */ +.ant-checkbox-checked .ant-checkbox-inner { + background-color: var(--main-primary-escuro) !important; + border-color: var(--main-primary-escuro) !important; +} + +.ant-checkbox .ant-checkbox-inner { + border-color: var(--main-primary-escuro) !important; +} + +.ant-checkbox-indeterminate .ant-checkbox-inner::after{ + background-color: var(--main-primary-escuro) !important; +} + +/* SEARCH */ +/* +.ant-input-search .ant-input-wrapper.ant-input-group{ + display:flex; + justify-content: flex-end; + padding-right:2%; + +} + +.ant-input-search.ant-input-affix-wrapper{ + width:300px !important; + transform: width 1s easyin; + border:0px; +} + +.ant-input-search.ant-input-affix-wrapper-focused{ + width:98% !important; + border:0px !important; + border-color:transparent !important; +} +*/ + +.breadcrumb-card > .ant-card-body >.ant-row{ + display:flex; + align-items: end; + height:90px; +} + +.ant-breadcrumb > span:last-child label, +.ant-breadcrumb-link .router-link-exact-active{ + font-size:35px; + position:absolute; + left:10px; + top:-65px; + /*! width:100vh !important; */ +} + +.ant-card-head .ant-card-head-title{ + font-size:35px; +} + +.breadcrumb-card > .ant-card-body >.ant-row .ant-col:last-child{ + padding-bottom:7px; +} + +.ant-input-search .ant-input-group-addon{ + padding:0px !important; + width:30px !important; +} + +.ant-input-search .ant-input-group-addon button{ + height:30px !important; + width:30px; + border-radius:7px 0px 0px 7px; + border:2px solid var(--main-primary-claro) !important; +} + +.ant-select.project-select > .ant-select-selector, +.ant-input-affix-wrapper, +.ant-input-affix-wrapper input, +.ant-input-search .ant-input-search.input-search, +.ant-input-search .ant-input-search.input-search input{ + background-color:white !important; +} + +.ant-select.project-select.ant-select-focused > .ant-select-selector, +.ant-input-affix-wrapper.ant-input-affix-wrapper-focused, +.ant-input-affix-wrapper.ant-input-affix-wrapper-focused input, +.ant-input-search .ant-input-search.input-search.ant-input-affix-wrapper-focused, +.ant-input-search .ant-input-search.input-search.ant-input-affix-wrapper-focused input{ + background-color: var(--main-grey-escuro) !important; + color:#fff !important; +} + +.ant-input-search .ant-input-search.input-search.ant-input-affix-wrapper-focused .anticon{ + background-color:red !important; +} + +.user-layout .ant-input-affix-wrapper-focused .ant-input-prefix, +.user-layout .ant-input-affix-wrapper-focused .ant-input-suffix{ + color:#fff !important; +} + +/* +.ant-select.project-select.ant-select-focused .anticon path{ + color:#fff !important; + stroke:#fff !important; +} +*/ + +.ant-input-search .ant-input-search.input-search input::placeholder { + color: #666; +} + +.ant-input-affix-wrapper.ant-input-affix-wrapper-focused input::placeholder, +.ant-input-search .ant-input-search.input-search.ant-input-affix-wrapper-focused input::placeholder{ + color:#ffffffDD !important; +} + +.ant-input-search .ant-input-search.input-search{ + border-radius:0px 7px 7px 0px; +} + +.anticon-search.ant-input-search-icon{ + background-color:var(--main-primary-escuro) !important; + position:absolute; + height:30px; + width:30px; + border-radius:0px 7px 7px 0px; + display:flex; + justify-content: center; + align-items: center; + color:#fff; + right:0; +} + +.anticon-search.ant-input-search-icon:hover{ + background-color:var(--main-secondary-escuro) !important; +} + +/* SWITCH E BOTAO */ +button.ant-btn:not(.ant-btn-icon-only){ + background-color:var(--main-primary-escuro) !important; + color:#fff; + border:0px; +} + +button.ant-btn:not(.ant-btn-icon-only):hover{ + background-color:var(--main-secondary-escuro) !important; + color:#fff; +} + +.ant-radio .ant-radio-inner{ + border-color:var(--main-primary-escuro) !important; +} + +.ant-radio .ant-radio-inner:after{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-switch{ + background-color:var(--main-primary-claro); + border-color:var(--main-primary-escuro) !important; + height:24px; + /*! position:absolute; */ +} + +.ant-switch-loading-icon, .ant-switch::after{ + height:20px; +} + +.ant-switch-checked{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-switch span{ + color:#444; +} + +.ant-switch-checked span{ + color:#fff; +} + +.ant-slider-handle, +.ant-slider-handle .ant-tooltip-open{ + background-color: #fff; + border: solid 2px var(--main-primary-escuro); + border-radius: 50%; + box-shadow: 0; +} + +.ant-slider-handle.ant-tooltip-open{ + box-shadow: 0px 0px 5px var(--main-grey-claro) !important; + border: solid 2px var(--main-primary-escuro); +} + +.ant-slider:hover .ant-slider-handle, +.ant-slider-handle:hover, +.ant-slider-handle:focus{ + background-color: var(--main-primary-escuro); + border: solid 2px var(--main-primary-escuro) !important; + border-radius: 50%; + box-shadow: 0; + box-shadow: 0px 0px 5px var(--main-grey-claro) !important; +} + +.ant-slider-track{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-slider-rail{ + background-color:var(--main-primary-claro) !important; +} + + +/* HEADER */ +.layout.ant-layout .header{ + background-color:#fff !important; +} + +/* MENU ACTIVE */ +.ant-menu-vertical .ant-menu-item::after, .ant-menu-vertical-left .ant-menu-item::after, .ant-menu-vertical-right .ant-menu-item::after, .ant-menu-inline .ant-menu-item::after { + border-color:var(--main-primary-escuro) !important; + /*! color: red !important; */ +} + +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item a, +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected a{ + color:#000 !important; +} + +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item.ant-menu-item-selected { + background-color: var(--main-primary-claro) !important; +} + +/* MENU LIGHT */ +.sider.light .ant-menu-light, +.sider.light .ant-menu-submenu > .ant-menu{ + background-color:var(--main-secondary-escuro); + background-color:transparent; +} + + +.sider.light .ant-menu-dark{ + background-color:green; +} + +.ant-menu-submenu-inline > .ant-menu-submenu-title .ant-menu-submenu-arrow::before, +.ant-menu-submenu-inline > .ant-menu-submenu-title .ant-menu-submenu-arrow::after{ + background-color: #000 !important; +} + +.ant-menu-submenu-inline:hover .ant-menu-submenu-title .ant-menu-submenu-arrow::before, +.ant-menu-submenu-inline:hover .ant-menu-submenu-title .ant-menu-submenu-arrow::after{ + background-color: #fff !important; +} + +/* MENU ITEM */ +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item:hover, +.ant-menu:not(.ant-menu-horizontal) .ant-menu-submenu div:hover{ + background-color: var(--main-primary-escuro) !important; +} + +.ant-menu-inline-collapsed .ant-menu-submenu-title{ + margin:0px !important; +} + + +/* CARDS DASHBOARD */ +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(2) .ant-card.usage-dashboard-chart-card{ + background-color:var(--main-vermelho) !important; +} + +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(1) .ant-card.usage-dashboard-chart-card{ + background-color:var(--main-verde) !important; +} + +.usage-dashboard-chart-card-inner h2, +.usage-dashboard-chart-card-inner h3{ + color:#fff !important; +} + +/* GRAFICOS */ + +.ant-progress .ant-progress-status-normal path{ + stroke:var(--main-exception) !important; +} + +.ant-progress-circle.ant-progress-status-normal .ant-progress-text{ + color:var(--main-normal) !important; + font-weight: bold +} + +.ant-progress.ant-progress-status-normal path.ant-progress-circle-path{ + stroke:var(--main-normal) !important; +} + +.ant-progress.ant-progress-status-exception path.ant-progress-circle-path, +.ant-progress.ant-progress-status-active path.ant-progress-circle-path{ + stroke:var(--main-exception) !important; +} + +.ant-progress path.ant-progress-circle-trail{ + stroke:var(--main-grey-claro) !important; +} + +.ant-progress-circle.ant-progress-status-exception .ant-progress-text, +.ant-progress-circle.ant-progress-status-active .ant-progress-text{ + color:var(--main-exception) !important; + font-weight: bold +} + +/* lISTAS TABELADAS */ +.ant-list .ant-list-item{ + border-left:5px solid transparent !important; + padding-left:10px; +} + +.ant-list .ant-list-item:hover{ + background-color:var(--main-grey-claro) !important; + border-left:5px solid var(--main-primary-escuro) !important; +} + +.ant-list .ant-list-item .ant-btn{ + border-color:var(--main-grey-escuro) !important; + background-color:var(--main-grey-claro) !important; + color:var(--main-grey-escuro) !important; + +} + +.ant-list .ant-list-item:hover .ant-btn{ + border-color:red!important; + background-color:var(--main-primary-escuro) !important; + color:#fff !important; +} + +.ant-list .ant-list-item:hover .ant-btn:hover{ + border-color:var(--main-secondary-escuro) !important; + background-color:var(--main-secondary-escuro) !important; +} + +/* OUTROS */ + +.ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected{ + background-color:var(--main-primary-claro) +} + +/* MODAL */ +.ant-modal .ant-modal-header{ + background-color:var(--main-secondary-escuro); +} + +.ant-modal .ant-modal-header *{ + color:#fff; +} + +.ant-modal .ant-row.ant-form-item{ + margin-bottom:20px !important; +} + +.ant-modal .ant-row.ant-form-item .ant-form-item-label{ + padding-bottom:0px !important; +} + +.ant-row.ant-form-item{ + margin-bottom:20px !important; +} + +.ant-steps-item .ant-form.ant-form-vertical{ + margin-top:20px !important; +} + +.ant-row.ant-form-item .ant-form-item-label{ + padding-bottom:0px !important; +} + +.ant-modal .ant-modal-title{ + font-size:20px +} + +.ant-modal .ant-modal-title .anticon{ + font-size:15px +} + +.ant-modal .ant-modal-body .anticon:not(.anticon-cloud-upload){ + position:absolute; + transform: scale(0.9); + margin-left:5px; +} + +.ant-select-selector .anticon{ + position: relative !important; + margin-left:0px !important; +} + +.ant-modal .anticon:hover{ + color:var(--main-primary-escuro); +} + +.ant-modal .ant-modal-close{ + color:#fff; + height:54px; + transition:all 1s easyin; + background-color:transparent; +} + +.ant-modal .ant-modal-close:hover{ + background-color:var(--main-primary-escuro); +} + +.ant-modal .ant-modal-close-x:hover svg{ + color:#fff; +} + +.ant-modal .ant-modal-close-x svg{ + transform: scale(1.3); +} + +/* CADASTROS FORMS E ADDS */ +.ant-steps-item-tail{ + margin-top:15px; +} + +.ant-steps-item-tail:after{ + background-color:var(--main-grey-claro) !important; +} + +.ant-steps-item-icon{ + background-color:var(--main-primary-escuro) !important; + border-color:var(--main-primary-escuro) !important; + margin-top:10px +} + +.ant-steps-item .ant-card-head{ + padding:0px; +} + +.ant-steps-item .ant-radio-group .ant-card-grid{ + box-shadow: ;none; + border:2px solid var(--main-grey-claro) +} + +.ant-steps-item .ant-card-meta-description{ + color:var(--main-grey-escuro) !important; +} + +.ant-steps-item .ant-card-body .ant-tabs-nav > div .ant-tabs-tab, +.ant-steps-item .ant-card-contain-tabs .ant-card-body{ + background-color:var(--main-grey-claro) !important; +} + +.ant-steps-item .ant-card-grid .ant-radio-wrapper{ + width:100%; + border-radius:7px; + padding:10px; +} + +.ant-steps-item .ant-radio-wrapper:hover{ + background-color:green !important; +} + +.ant-steps-item .ant-card-grid .ant-radio-wrapper:hover{ + background-color: blue !important; +} + +.ant-steps-item .ant-radio-group label:not(.ant-radio-button-wrapper-checked):hover{ + background-color:transparent !important; +} + +.ant-steps-item #zoneid .ant-card-grid .ant-radio-wrapper{ + background-color: var(--main-grey-claro) !important; +} + +.ant-steps-item #zoneid .ant-card-grid .ant-radio-wrapper:hover{ + background-color: var(--main-primary-claro) !important; +} + +.ant-steps-item .ant-card-grid .ant-radio-wrapper .anticon{ + margin-left:0px !important; + width:100%; + margin-top:0px !important; +} + +.ant-steps-item .ant-list .ant-list-item:hover{ + background-color:var(--main-primary-claro) !important; + border-left:5px solid transparent !important; +} + +.ant-steps-item{ + margin-bottom:30px; + margin-top:0px; +} + +.ant-table-body, +.ant-list-vertical.form-item-scroll .ant-spin-container{ + max-height:none !important;; +} + +.ant-steps-item-title{ + width:100%; + height:40px; + display: flex; + align-items: center; + border-top:2px solid var(--main-primary-claro) !important; + font-weight:00 !important; + font-size:22px !important; +} + + +.ant-menu-submenu-arrow:after{ + /*! color:red !important; */ +} + +button.ant-btn.ant-btn-dangerous:not(.ant-btn-icon-only) +{ + background-color:var(--main-vermelho) !important; +} + +.resource-detail-item{ + border-bottom: 1px solid #ddd; + padding-bottom: 20px; +} + +.account-center-tags .ant-divider-horizontal{ + display:none +} + +.ant-table tr.ant-table-row-selected td{ + background-color:var(--main-secondary-claro); +} + +/* MODAL Steps */ + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item-title{ + font-size:14px !important; + border-top:0px !important; + justify-content: center; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-wait .ant-steps-item-icon{ + background-color:#ddd !important; + border-color:#999 !important; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-wait .ant-steps-item-icon span{ + color:#999; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-finish .ant-steps-item-icon{ + background-color:#555 !important; + border-color:#555 !important; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-finish .ant-steps-item-icon span{ + color:white !important; + right:-3px; + top:9px; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item-container .ant-steps-item-icon{ + background-color:var(--main-primary-claro) !important; + margin-top:0px; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-active .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-wait .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + background-color:var(--main-primary-claro) !important; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-finish .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + background-color:var(--main-secondary-escuro) !important; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-finish .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + color:white !important; + left:0px !important; + top:0px; +} + +.ant-modal-body .ant-table-body .ant-tag span{ + position:relative !important; +} + +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle{ + padding-top:15px !important; +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous{ + background-color:var( --main-vermelho); + border-color:var( --main-vermelho); +} + +.ant-modal-body .ant-form-item-control-input .anticon.anticon-close-circle{ + top:0px !important; + bottom:0px !important; + left:-10px !important; +} + +.ant-modal-body .ant-form-item-control-input .ant-select-arrow{ + right:15px +} + +.ant-form-item-has-feedback .ant-form-item-control-input :not(.ant-input-group-addon) > .ant-select .ant-select-arrow{ + right:40px +} + +.ant-modal-body .ant-form-item-control-input .ant-select-clear{ + background-color:transparent !important; + top:13px; + right:-20px !important; +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous .anticon{ + /*! left:2px; */ + /*! top:7px; */ +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous:hover, +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle:hover{ + background-color:var(--main-secondary-escuro); + border-color:var(--main-secondary-escuro); +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous .anticon, +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle:hover svg{ + color:#fff; +} + +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle > span{ + top:4px; + left:-1px; +} + +.ant-modal-body .traffic-select-item .ant-select-arrow .anticon.anticon-down{ + margin-left:0px !important; +} + +.ant-modal-body .ant-form-item-control-input .anticon.anticon-close-circle, +.ant-modal-body .ant-form-item-control-input .anticon.anticon-check-circle{ + left:5px; + top:4px !important; +} + +.ant-modal-body .card-waiting-launch .anticon-check-circle{ + font-size:40px !important; + left:60px; + top:40px +} + +.ant-modal-body .anticon-rocket{ + left:1px; + bottom:7px; +} + +.ant-modal-body .anticon-rocket:hover{ + color:inherit !important; +} + +.anticon-loading{ + top:0px; +} + +.card-launch-content .ant-steps-item-finish .anticon.anticon-check.ant-steps-finish-icon{ + right:-6px; + color:#fff; + top:19px +} + +.card-launch-content .ant-steps-item .ant-steps-item-icon .ant-steps-icon .anticon{ + left:-10px +} + +.card-launch-content .ant-steps-item .ant-steps-item-icon{ + min-width:23px !important; +} + +.card-launch-content .anticon.anticon-close-circle{ + left:-15px !important; +} + +.card-launch-content .anticon.anticon-loading{ + top:1px +} + + +.ant-modal-body .ant-table-bordered .ant-table-body .ant-table-row-cell-break-word .ant-btn-circle.ant-btn-dangerous{ + background-color:var(--main-vermelho) !important; + + &:hover{ + background-color:var(--main-secondary-escuro) !important; + } + +} + +.ant-modal-body .ant-table-bordered .ant-table-body .ant-table-row-cell-break-word .ant-btn-circle.ant-btn-dangerous span{ + background-color: transparent; + margin-right:5px !important; +} + +.ant-alert:not(.ant-alert-no-icon) .ant-alert-message{ + margin-left:40px; +} + +.ant-btn-dangerous.ant-btn-primary.ant-btn-dangerous{ + background:var(--main-vermelho); + border-color:var(--main-vermelho); +} + diff --git a/ui/public/css/dark-theme.css b/ui/public/css/dark-theme.css new file mode 100644 index 000000000000..155ef9d94f62 --- /dev/null +++ b/ui/public/css/dark-theme.css @@ -0,0 +1,1574 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); + +:root { + --main-verde: #3E7C59; + --main-vermelho: #ae0000; + --main-normal:#b2e061; + --main-exception:#fd7f6f; + --main-primary-claro: #0094F055; + --main-primary-text: #A1E4F0; + --main-primary-escuro: #B08A2A; + --main-secondary-claro: #B08A2A22; + --main-secondary-escuro: #7eb0d5; + --main-grey-claro: #ccc; + --main-grey-escuro: #444; + --main-image-menu: url("../assets/logo.svg"); + --main-image-login: url("../assets/banner.svg"); + --main-image-icon: url("../assets/mini-logo.svg"); + --main-linear-cora: #333; + --main-linear-corb: #333; +} + +#app{ + background-color: var(--main-linear-cora); +} + +#app > div > form.ant-form.ant-form-horizontal h1, +#app > div > form.ant-form.ant-form-horizontal p{ + color:#fff !important; +} + +#app > div > form > img.user-layout-logo{ + display:none !important; +} + +#app > div > form{ + background-image:var(--main-image-login); + background-repeat:no-repeat; + background-size:250px; + background-position:top center; + padding-top:120px; +} + +.userLayout .user-layout-header{ + min-height:130px; + background-image:var(--main-image-login); + background-repeat:no-repeat; + background-size:400px; + background-position:top center; + margin-bottom:20px; +} + +.ant-layout-sider.light.ant-fixed-sidemenu > div > div{ + height: 90px; + background-image:var(--main-image-menu); + background-repeat:no-repeat; + background-size:150px; + background-position:25px 25px; + margin-bottom:20px; +} + +.ant-layout-sider.light.ant-fixed-sidemenu.ant-layout-sider-collapsed > div > div{ + height: 80px; + background-image:var(--main-image-icon); + background-repeat:no-repeat; + background-size:60px; + background-position:10px 20px; +} + +.sider.light .ant-menu-light a, +.sider.light .ant-menu-submenu > .ant-menu-submenu-title { + color:#fff !important; +} + +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-active:hover a, +.ant-menu:not(.ant-menu-horizontal) .ant-menu-submenu span.ant-menu-title-content:hover{ + color:#fff !important; +} + +.ant-menu-submenu ul a, +.ant-menu-submenu ul .ant-menu-item-selected a span{ + color:#000 !important; +} + +/* USER MMENU DARK */ +*{ + font-family: 'Poppins' , Courier !important; +} + +aside, +.ant-menu-submenu ul{ + background: linear-gradient(var(--main-linear-cora), var(--main-linear-corb)) !important; +} + +.ant-menu-submenu ul a, +.ant-menu-submenu ul .ant-menu-item-selected a span{ + color:#fff !important; +} + +html{ + background-color: var(--main-secondary-escuro); +} + +/* HEADER */ +.layout.ant-layout .header{ + background: linear-gradient(var(--main-linear-cora), var(--main-linear-corb)) !important; + box-shadow: 0px 0px 10px #00000055; +} + +.layout.ant-layout .header .user-menu > span.ant-dropdown-open{ + background-color:var(--main-primary-claro) !important; + border-bottom:5px solid var(--main-primary-escuro) !important; +} + +.layout.ant-layout .header .anticon-menu-unfold:hover, +.layout.ant-layout .header .anticon-menu-fold:hover, +.layout.ant-layout .header .user-menu > span:hover{ + background-color:var(--main-primary-escuro) !important; + color:#fff !important; +} + +.layout.ant-layout .header .user-menu > span:hover *, +.layout.ant-layout .header .user-menu > span *{ + color:#fff !important; +} + +.layout.ant-layout .header .anticon-menu-unfold, +.layout.ant-layout .header .anticon-menu-fold{ + color:#fff; +} + +.header-notice-opener{ + padding-left:15px; +} + + +.ant-table-tbody > .ant-table-row:hover td{ + background-color:var(--main-secondary-claro) !important; +} + +/* BARRA LATERAL */ +.ant-card { + background-color: var(--main-linear-cora) !important; +} + +.vm-info-card .ant-card-body .ant-card-bordered{ + border-top:2px solid var(--main-primary-escuro) !important; + margin-bottom:50px; +} + +.vm-info-card .ant-card-body .ant-card-bordered .ant-card-head, +.vm-info-card .ant-card-body .ant-card-bordered .ant-card-grid{ + padding:10px 0px !important; +} + +.vm-info-card .ant-card-body .ant-card-bordered .resource-detail-item{ + margin-bottom:20px; +} + +.vm-info-card .ant-card-body .ant-card-bordered .resource-detail-item .resource-detail-item__label{ + font-weight:900; +} + +/* TELA USER LOGIN */ + +.ant-tabs-tab, +.user-layout{ + background-color:var(--main-grey-claro) !important; +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + background-color:var(--main-grey-claro) !important; + color: var(--main-primary-escuro); + border-bottom:0px solid var(--main-primary-escuro); +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab:hover{ + background-color:var(--main-secondary-escuro) !important; + color: #fff; + border-bottom:3px solid var(--main-primary-escuro); +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active{ + background-color:var(--main-grey-claro) !important; + color: var(--main-primary-escuro); + border-bottom:3px solid var(--main-primary-escuro); +} + +.user-layout .ant-tabs-nav{ + width:100%; +} + +.user-layout .ant-tabs-nav > div > div{ + width:50%; +} + +/* IMAGES LOGO */ +.userLayout .user-layout-logo{ + display:none; +} + +.ant-layout-sider.light.ant-fixed-sidemenu > div > div > img{ + display:none !important; +} + +/* PROGRESS */ +.ant-progress.ant-progress-status-active .ant-progress-inner, +.ant-progress.ant-progress-status-normal .ant-progress-inner{ + background-color:var(--main-grey-claro) !important; +} + +.ant-progress.ant-progress-status-active .ant-progress-inner .ant-progress-bg, +.ant-progress.ant-progress-status-normal .ant-progress-inner .ant-progress-bg{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-progress .ant-progress-text{ + color:var(--main-grey-claro) !important; + font-weight:700; +} + +.ant-progress.ant-progress-status-exception .ant-progress-inner .ant-progress-bg{ + background-color:var(--main-exception) !important; +} + +/* FORMS */ +.ant-input-number-input{ + color:#000; +} + +.ant-input-number-disabled .ant-input-number-input{ + color:#666; +} + +.ant-select-selector, +.ant-input{ + background-color:white !important; + color:var(--main-grey-escuro) !important; +} + +.ant-select.ant-select-focused .ant-select-arrow .anticon{ + color:var(--main-grey-claro); +} + +.ant-select-selector .ant-select-selection-placeholder, +.ant-select, +.ant-input::placeholder{ + color:var(--main-grey-escuro) !important; +} + + +.ant-select-selector:hover, .ant-select-selector:focus, +.ant-input:hover, .ant-input:focus{ + box-shadow: 0px 0px 3px var(--main-primary-escuro); + border-color:var(--main-primary-escuro) !important; +} + +.ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) .ant-select-selector { + border-color: var(--main-primary-escuro); + border-right-width: 1px !important; + box-shadow: 0 0 0 2px var(--main-primary-claro); +} + +.ant-select-dropdown .ant-select-item.ant-select-item-option-selected, +.ant-select-dropdown .ant-select-item:hover{ + background-color:var(--main-primary-claro) !important; +} + +/* PAGINACAO */ +.ant-pagination .ant-pagination-item.ant-pagination-item-active{ + background-color:var(--main-primary-escuro) !important; + border-color:#fff; +} + +.ant-pagination .ant-pagination-item.ant-pagination-item-active a{ + color:#fff !important; + font-weight:bold; +} + +.ant-pagination .ant-pagination-item:not(.ant-pagination-item-active){ + background-color:#fff !important; +} + +.ant-pagination .ant-pagination-item:not(.ant-pagination-item-active):hover{ + background-color:var(--main-secondary-escuro) !important; + border-color:#666; +} + +.ant-pagination .ant-pagination-item:not(.ant-pagination-item-active):hover a{ + color:#666 !important; + font-weight:bold; +} + +.ant-pagination .ant-pagination-next span, +.ant-pagination .ant-pagination-prev span{ + color:var(--main-grey-escuro) !important; +} + +.ant-pagination .ant-pagination-next span:hover, +.ant-pagination .ant-pagination-prev span:hover{ + color:var(--main-secondary-escuro) !important; +} + + +/* RADIO GROUP */ +.ant-radio-group label.ant-radio-button-wrapper-checked{ + background-color:var(--main-primary-escuro) !important; + border-color:var(--main-primary-escuro) !important; +} + +.ant-radio-group label:not(.ant-radio-button-wrapper-checked):hover{ + color:#000 !important; + background-color:var(--main-primary-claro) !important; +} + +.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)::before { + background-color:var(--main-primary-escuro) !important; +} + +/* TABLES */ +.ant-table{ + font-size:16px !important; + background:var(--main-linear-cora) !important; +} + +.ant-table th{ + border-bottom:5px solid #ffffff55!important; +} + +.ant-table th.ant-table-column-has-sorters:hover{ + border-bottom:5px solid var(--main-primary-escuro) !important; + background-color:#ffffff22 !important; +} + +.ant-table thead.ant-table-thead{ + border-bottom:5px solid var(--main-primary-escuro) !important; +} + +.anticon.ant-table-column-sorter-up.on *, +.anticon.ant-table-column-sorter-down.on *{ + color: var(--main-primary-escuro) !important; +} + +.anticon.anticon-camera.upload-icon{ + background:#ffffff33 !important; + border:0px; +} + +.anticon.anticon-camera.upload-icon:hover{ + background:var(--main-secondary-escuro) !important; +} + +.anticon.anticon-camera.upload-icon{ + background: !important; +} + +/* PAGE TABS LEFT */ +.ant-tabs-left-bar .ant-tabs-nav-scroll { + border-right:3px solid var(--main-primary-escuro); +} + +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + margin:0px; + padding:10px 15px; + border-right:3px solid transparent; + transition: all 1s ease !important; + background-color:var(--main-linear-cora) !important; +} + +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active, +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-tab:hover{ + background-color:#ffffff33 !important; + color: var(--main-primary-escuro); + border-right:3px solid var(--main-primary-escuro); +} + +.ant-tabs-left-bar .ant-tabs-nav-scroll .ant-tabs-ink-bar{ + display:none !important; + width:0px !important; + background-color:var(--main-primary-escuro) !important; +} + +/* PAGE TABS TOP */ +.ant-tabs-tab-next, +.ant-tabs-tab-prev{ + color:var(--main-primary-escuro) !important; +} + +.ant-tabs-tab-next svg, +.ant-tabs-tab-prev svg{ + transform: scale(1.5); +} + +.ant-tabs-tab-next:not(.ant-tabs-tab-btn-disabled):hover, +.ant-tabs-tab-prev:not(.ant-tabs-tab-btn-disabled):hover{ + background-color:var(--main-primary-escuro) !important; + color:#fff !important; +} + +.ant-tabs-tab-next.ant-tabs-tab-btn-disabled, +.ant-tabs-tab-prev.ant-tabs-tab-btn-disabled{ + color:var(--main-primary-claro) !important; +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll { + border-bottom:3px solid var(--main-primary-escuro); +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + margin:0px; + padding:14px 30px; + border-bottom:3px solid transparent; + transform: all 1s easyin !important; + background-color:#fff !important; +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active, +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab:hover{ + background-color:var(--main-grey-claro) !important; + color: var(--main-primary-escuro); + border-bottom:3px solid var(--main-primary-escuro); +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-ink-bar{ + height:0px !important; + background-color:var(--main-primary-escuro) !important; +} + +/* LINKS */ +a{ + color: var(--main-primary-escuro); +} + +a:hover{ + color: var(--main-secondary-escuro); +} + +.ant-breadcrumb-link a:hover{ + color: var(--main-primary-escuro) !important;; +} + +.ant-breadcrumb-link .anticon{ + margin-top:5px !important; +} + +.ant-breadcrumb-link .ant-select-arrow .anticon{ + margin-top:0px !important; +} + + +/* USER MENU */ +.user-menu > span{ + border-bottom:5px solid transparent; +} + +.user-menu > span:hover{ + border-bottom:5px solid var(--main-primary-escuro); + background-color:var(--main-grey-claro) !important; +} + +.ant-dropdown .ant-dropdown-menu-item:hover{ + background-color:var(--main-primary-claro) !important; +} + +.ant-dropdown .ant-dropdown-menu-item:hover *{ + color:#000; +} + +.ant-dropdown .ant-dropdown-menu-item.ant-dropdown-menu-item-selected, +.ant-dropdown .ant-dropdown-menu-item.ant-dropdown-menu-item-selected:hover *{ + background-color:var(--main-primary-escuro) !important; + color:#fff !important; +} + +/* CHECKBOXES */ +.ant-checkbox-checked .ant-checkbox-inner { + background-color: var(--main-primary-escuro) !important; + border-color: var(--main-primary-escuro) !important; +} + +.ant-checkbox .ant-checkbox-inner { + border-color: var(--main-primary-escuro) !important; +} + +.ant-checkbox-indeterminate .ant-checkbox-inner::after{ + background-color: var(--main-primary-escuro) !important; +} + +/* SEARCH */ +/* +.ant-input-search .ant-input-wrapper.ant-input-group{ + display:flex; + justify-content: flex-end; + padding-right:2%; + +} + +.ant-input-search.ant-input-affix-wrapper{ + width:300px !important; + transform: width 1s easyin; + border:0px; +} + +.ant-input-search.ant-input-affix-wrapper-focused{ + width:98% !important; + border:0px !important; + border-color:transparent !important; +} +*/ + +.breadcrumb-card > .ant-card-body >.ant-row{ + display:flex; + align-items: end; + height:90px; +} + +.ant-breadcrumb > span:last-child label, +.ant-breadcrumb-link .router-link-exact-active{ + font-size:35px; + position:absolute; + left:10px; + top:-65px; + /*! width:100vh !important; */ +} + +.ant-card-head{ + background-color:var(--main-linear-cora); + color:#fff; +} + +.ant-card-head .ant-card-head-title{ + font-size:35px; +} + +.breadcrumb-card > .ant-card-body >.ant-row .ant-col:last-child{ + padding-bottom:7px; +} + +.ant-input-search .ant-input-group-addon{ + padding:0px !important; + width:30px !important; +} + +.ant-input-search .ant-input-group-addon button{ + height:30px !important; + width:30px; + border-radius:7px 0px 0px 7px; + border:2px solid var(--main-primary-escuro) !important; +} + +.ant-select.project-select > .ant-select-selector, +.ant-input-affix-wrapper, +.ant-input-affix-wrapper input, +.ant-input-search .ant-input-search.input-search, +.ant-input-search .ant-input-search.input-search input{ + background-color:white !important; +} + +.ant-input-affix-wrapper{ + border-radius: 7px; +} + +.ant-select.project-select.ant-select-focused > .ant-select-selector, +.ant-input-affix-wrapper.ant-input-affix-wrapper-focused, +.ant-input-affix-wrapper.ant-input-affix-wrapper-focused input, +.ant-input-search .ant-input-search.input-search.ant-input-affix-wrapper-focused, +.ant-input-search .ant-input-search.input-search.ant-input-affix-wrapper-focused input{ + background-color: var(--main-grey-claro) !important; + color: var(--main-grey-escuro) !important; +} + +.user-layout .ant-input-affix-wrapper-focused .ant-input-prefix, +.user-layout .ant-input-affix-wrapper-focused .ant-input-suffix{ + color:#333 !important; +} + +/* +.ant-select.project-select.ant-select-focused .anticon path{ + color:#fff !important; + stroke:#fff !important; +} +*/ + +.ant-input-search .ant-input-search.input-search input::placeholder { + color: #666; +} + +.ant-input-affix-wrapper.ant-input-affix-wrapper-focused input::placeholder, +.ant-input-search .ant-input-search.input-search.ant-input-affix-wrapper-focused input::placeholder{ + color:var(--main-grey-escuro) !important; +} + +.ant-input-search .ant-input-search.input-search{ + border-radius:0px 7px 7px 0px; +} + +.anticon-search.ant-input-search-icon{ + background-color:var(--main-primary-escuro) !important; + position:absolute; + height:30px; + width:30px; + border-radius:0px 7px 7px 0px; + display:flex; + justify-content: center; + align-items: center; + color:#fff; + right:0; +} + +.anticon-search.ant-input-search-icon:hover{ + background-color:var(--main-secondary-escuro) !important; +} + +/* SWITCH E BOTAO */ +button.ant-btn{ + background-color:var(--main-primary-escuro) !important; + color:#fff; + border:0px; +} + +button.ant-btn:hover{ + background-color:var(--main-secondary-escuro) !important; + color:#fff; +} + +.ant-radio .ant-radio-inner{ + border-color:var(--main-primary-escuro) !important; +} + +.ant-radio .ant-radio-inner:after{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-tabs-tabpane .ant-radio-button-wrapper{ + background-color:var(--main-linear-cora); +} + +.ant-switch{ + background-color:var(--main-primary-claro); + border-color:var(--main-primary-escuro) !important; + height:24px; + /*! position:absolute; */ +} + +.ant-switch-loading-icon, .ant-switch::after{ + height:20px; +} + +.ant-switch-disabled, +.ant-switch-checked{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-switch span{ + color:#444; +} + +.ant-switch-checked span{ + color:#fff; +} + +.ant-slider-handle, +.ant-slider-handle .ant-tooltip-open{ + background-color: #fff; + border: solid 2px var(--main-primary-escuro); + border-radius: 50%; + box-shadow: 0; +} + +.ant-slider-handle.ant-tooltip-open{ + box-shadow: 0px 0px 5px var(--main-grey-claro) !important; + border: solid 2px var(--main-primary-escuro); +} + +.ant-slider:hover .ant-slider-handle, +.ant-slider-handle:hover, +.ant-slider-handle:focus{ + background-color: var(--main-primary-escuro); + border: solid 2px var(--main-primary-escuro) !important; + border-radius: 50%; + box-shadow: 0; + box-shadow: 0px 0px 5px var(--main-grey-claro) !important; +} + +.ant-slider-track{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-slider-rail{ + background-color:var(--main-primary-claro) !important; +} + +.ant-avatar-icon{ + background-color:transparent; +} + + +/* HEADER */ +.layout.ant-layout .header{ + background-color:#fff !important; +} + +/* MENU ACTIVE */ +.ant-menu-vertical .ant-menu-item::after, .ant-menu-vertical-left .ant-menu-item::after, .ant-menu-vertical-right .ant-menu-item::after, .ant-menu-inline .ant-menu-item::after { + border-color:var(--main-primary-escuro) !important; +} + +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected a{ + color:#000 !important; +} + +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item.ant-menu-item-selected { + background-color: var(--main-primary-claro) !important; +} + +/* MENU LIGHT */ +.sider.light .ant-menu-light, +.sider.light .ant-menu-submenu > .ant-menu{ + background-color:transparent; +} + +.ant-menu-submenu-inline > .ant-menu-submenu-title .ant-menu-submenu-arrow::before, +.ant-menu-submenu-inline > .ant-menu-submenu-title .ant-menu-submenu-arrow::after{ + background-color: #fff !important; +} + +/* MENU ITEM */ +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item:hover, +.ant-menu:not(.ant-menu-horizontal) .ant-menu-submenu div:hover{ + background-color: var(--main-primary-escuro) !important; +} + +.ant-menu-inline-collapsed .ant-menu-submenu-title{ + margin:0px !important; +} + + +/* CARDS DASHBOARD */ +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(2) .ant-card.usage-dashboard-chart-card{ + background-color:var(--main-vermelho) !important; +} + +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(1) .ant-card.usage-dashboard-chart-card{ + background-color:var(--main-verde) !important; +} + +.usage-dashboard-chart-card-inner h2, +.usage-dashboard-chart-card-inner h3{ + color:#fff !important; +} + +/* GRAFICOS */ + +.ant-progress .ant-progress-status-normal path{ + stroke:var(--main-exception) !important; +} + +.ant-progress-circle.ant-progress-status-normal .ant-progress-text{ + color:var(--main-normal) !important; + font-weight: bold +} + +.ant-progress.ant-progress-status-normal path.ant-progress-circle-path{ + stroke:var(--main-normal) !important; +} + +.ant-progress.ant-progress-status-exception path.ant-progress-circle-path, +.ant-progress.ant-progress-status-active path.ant-progress-circle-path{ + stroke:var(--main-exception) !important; +} + +.ant-progress path.ant-progress-circle-trail{ + stroke:var(--main-grey-claro) !important; +} + +.ant-progress-circle.ant-progress-status-exception .ant-progress-text, +.ant-progress-circle.ant-progress-status-active .ant-progress-text{ + color:var(--main-exception) !important; + font-weight: bold +} + +/* lISTAS TABELADAS */ +.ant-list .ant-list-item{ + border-left:5px solid transparent !important; + padding-left:10px; +} + +.ant-list .ant-list-item:hover{ + background-color:#ffffff33 !important; + border-left:5px solid var(--main-primary-escuro) !important; +} + +.ant-list.ant-list-split .ant-list-item:hover{ + background-color:transparent !important; + +} + +.ant-list .ant-list-item .ant-btn{ + opacity:0; +} + +.ant-list .ant-list-item:hover .ant-btn{ + background-color:var(--main-primary-escuro) !important; + color:#fff !important; + opacity:1; +} + +.ant-list .ant-list-item-action-split{ + opacity:0; +} + +.ant-list .ant-list-item:hover .ant-btn:hover{ + border-color:var(--main-secondary-escuro) !important; + background-color:var(--main-secondary-escuro) !important; +} + +/* OUTROS */ + +.ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected{ + background-color:var(--main-primary-claro) +} + +/* MODAL */ +.ant-modal .ant-modal-header{ + background-color:var(--main-primary-claro); +} + +.ant-modal .ant-modal-header *{ + color:#fff; +} + +.ant-modal .ant-row.ant-form-item{ + margin-bottom:20px !important; +} + +.ant-modal .ant-row.ant-form-item .ant-form-item-label{ + padding-bottom:0px !important; +} + +.ant-row.ant-form-item{ + margin-bottom:20px !important; +} + +.ant-steps-item .ant-form.ant-form-vertical{ + margin-top:20px !important; +} + +.ant-row.ant-form-item .ant-form-item-label{ + padding-bottom:0px !important; +} + +.ant-modal .ant-modal-title{ + font-size:20px +} + +.ant-modal .ant-modal-title .anticon{ + font-size:15px +} + +.ant-modal .ant-modal-body .anticon{ + /* position:absolute; */ + transform: scale(0.9); + margin-left:5px; +} + +.ant-select-selector .anticon{ + position: relative !important; + margin-left:0px !important; +} + +.ant-modal .anticon:hover{ + color:var(--main-primary-escuro); +} + +.ant-modal .ant-modal-close{ + color:#fff; + height:54px; + transition:all 1s easyin; + background-color:transparent; +} + +.ant-modal .ant-modal-close:hover{ + background-color:var(--main-primary-escuro); +} + +.ant-modal .ant-modal-close-x:hover svg{ + color:#fff; +} + +.ant-modal .ant-modal-close-x svg{ + transform: scale(1.3); +} + +/* CADASTROS FORMS E ADDS */ +.ant-steps-item-tail{ + margin-top:15px; +} + +.ant-steps-item-tail:after{ + background-color:var(--main-grey-claro) !important; +} + +.ant-steps-item-icon{ + background-color:var(--main-primary-escuro) !important; + border-color:var(--main-primary-escuro) !important; + margin-top:10px +} + +.ant-steps-item .ant-card-head{ + padding:0px; +} + +.ant-steps-item .ant-radio-group .ant-card-grid{ + box-shadow: ;none; + border:2px solid var(--main-grey-claro) +} + +.ant-steps-item .ant-card-meta-description{ + color:var(--main-grey-claro) !important; +} + +.ant-steps-item .ant-card-body .ant-tabs-nav > div .ant-tabs-tab, +.ant-steps-item .ant-card-contain-tabs .ant-card-body{ + background-color:var(--main-linear-cora) !important; +} + +.ant-steps-item .ant-card-grid .ant-radio-wrapper{ + width:100%; + border-radius:7px; + padding:10px; +} + +.ant-steps-item .ant-radio-wrapper:hover{ + background-color:green !important; +} + + +.ant-steps-item .ant-card-grid .ant-radio-wrapper:hover{ + background-color: blue !important; +} + +.ant-steps-item .ant-radio-group label:not(.ant-radio-button-wrapper-checked):hover{ + background-color:transparent !important; +} + +.ant-steps-item #zoneid .ant-card-grid .ant-radio-wrapper{ + background-color: var(--main-grey-escuro) !important; +} + +.ant-steps-item #zoneid .ant-card-grid .ant-radio-wrapper:hover{ + background-color: var(--main-primary-claro) !important; +} + +.ant-steps-item .ant-card-grid .ant-radio-wrapper .anticon{ + margin-left:0px !important; + width:100%; + margin-top:0px !important; +} + +.ant-steps-item .ant-list .ant-list-item:hover{ + background-color:var(--main-primary-claro) !important; + border-left:5px solid transparent !important; +} + +.ant-steps-item{ + margin-bottom:30px; + margin-top:0px; +} + +.ant-table-body, +.ant-list-vertical.form-item-scroll .ant-spin-container{ + max-height:none !important;; +} + +.ant-steps-item-title{ + width:100%; + height:40px; + display: flex; + align-items: center; + border-top:0px solid #ffffff55 !important; + font-weight:00 !important; + font-size:22px !important; +} + +/* DARK HOME */ +body #userLayout{ + background-color:var(--main-linear-cora) !important; +} + +.userLayout .user-layout-header{ + margin-bottom:20px; +} + +.userLayout .anticon-translation{ + color:#FFF; +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active{ + background-color:var(--main-primary-escuro) !important; + color: #fff; + border-bottom:3px solid var(--main-primary-escuro); +} + +.user-layout .ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + background-color:var(--main-linear-cora) !important; + color: #fff; + border-bottom:3px solid var(--main-primary-escuro); +} + +.ant-layout-footer *{ + color:#fff !important; +} + +.ant-card{ + border:0px !important; +} + +.ant-card-body *:not(a):not(.ant-tag):not(.ant-tag *):not(input):not(.ant-select-selection-item span):not(div.ant-select *), +.ant-table *:not(a):not(.ant-tag):not(.ant-tag *):not(input):not(.ant-select-selection-item span){ + color:#fff; +} + +.ant-input-suffix .anticon:not(.anticon-search ) *{ + color:#000 !important; +} + +.ant-alert-info *{ + color:#000 !important; +} + +.anticon.anticon-delete{ + background-color:var(--main-vermelho); + padding:0px; + border-radius:50px; +} + +.ant-popover-inner-content .anticon.anticon-delete{ + padding:0px; +} + +.anticon.anticon-delete svg path:first-child{ + fill:transparent; +} + +.anticon.anticon-delete svg path:last-child{ + fill:#fff; +} + +.ant-pagination .ant-select-item-option-content span{ + color:#000 !important; +} + +.ant-select-selection-item{ + color:#000 !important; +} + +.ant-table a{ + color:var(--main-primary-escuro) !important; +} + + +.ant-table tr, +.ant-table th, +.ant-table-footer, +.ant-card-body, +.ant-layout, +.ant-layout-footer{ + background-color:var(--main-linear-cora) !important; +} + +.ant-table tr.ant-table-row-selected td{ + background-color:var(--main-secondary-claro); +} + +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(1) .ant-card.usage-dashboard-chart-card .ant-card-body{ + background-color:var(--main-verde) !important; +} + +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(2) .ant-card.usage-dashboard-chart-card .ant-card-body{ + background-color:var(--main-vermelho) !important; +} + +.usage-dashboard .ant-card-body .ant-row .ant-col .ant-card.usage-dashboard-chart-card .ant-card-body{ + background-color:#fff !important; +} + +.usage-dashboard-chart-card-inner h2, +.usage-dashboard-chart-card-inner h3, +.usage-dashboard-chart-card-inner svg{ + color:#000 !important; + fill:#000; +} + +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(2) .ant-card.usage-dashboard-chart-card .ant-card-body .usage-dashboard-chart-card-inner svg, +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(1) .ant-card.usage-dashboard-chart-card .ant-card-body .usage-dashboard-chart-card-inner svg, +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(2) .ant-card.usage-dashboard-chart-card .ant-card-body .usage-dashboard-chart-card-inner h2, +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(1) .ant-card.usage-dashboard-chart-card .ant-card-body .usage-dashboard-chart-card-inner h2, +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(2) .ant-card.usage-dashboard-chart-card .ant-card-body .usage-dashboard-chart-card-inner h3, +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(1) .ant-card.usage-dashboard-chart-card .ant-card-body .usage-dashboard-chart-card-inner h3{ + color:#fff !important; + fill:#fff; +} + +.usage-dashboard .ant-card-body .ant-row .ant-col:nth-child(2) .ant-card.usage-dashboard-chart-card .ant-card-body{ + background-color:var(--main-vermelho) !important; +} + +.ant-table tr a, +.ant-table th a{ + color:var(--main-primary-text) !important; +} + +.ant-pagination{ + color:#fff; +} + +.ant-tabs-tabpane .ant-table tr, +.ant-tabs-tabpane .ant-table-footer, +.ant-tabs-tabpane .ant-table th{ + background-color:var(--main-linear-cora) !important; +} + +.usage-dashboard .ant-tabs-tabpane .ant-table table, +.usage-dashboard .ant-tabs-tabpane .ant-table tr, +.usage-dashboard .ant-tabs-tabpane .ant-table-footer, +.usage-dashboard .ant-tabs-tabpane .ant-table th{ + background-color:var(--main-linear-cora) !important; +} + +.ant-tabs-tabpane .ant-table tr, +.ant-tabs-tabpane .ant-table th{ + background-color: var(--main-linear-cora) !important; +} + +.ant-tabs-tabpane .ant-pagination{ + color:#000; +} + +.ant-tabs .ant-tabs-left-content{ + border-left: 0px !important; +} + +.ant-tabs .ant-tabs-bar{ + border-color:transparent; +} + +.ant-layout-content .page-header-wrapper-grid-content-main .ant-row .ant-col-lg-16 .ant-card-body{ + background-color:var(--main-linear-cora) !important; +} + +.ant-select-selection-item, +.ant-select-selector, +.ant-select-selector *{ + color:#666 !important; +} + +.ant-layout-content .page-header-wrapper-grid-content-main .ant-row .ant-col-lg-16 .ant-card-body *:not(a):not(input):not(.ant-radio-button-wrapper-checked span):not(button *):not(.ant-select-selector *):not(.ant-select-item-option-content){ + color:#fff; +} + +.ant-layout-content .page-header-wrapper-grid-content-main .ant-row .ant-col-lg-16 .ant-card-body p.ant-empty-description:not(.ant-radio-button-wrapper-checked span):not(button *){ + color:#fff !important; +} + +.input-search .ant-input-group-addon{ + border:0px; +} + +.ant-select-arrow path{ + color:#666 !important; +} + +.ant-input-search, +.ant-input-search .ant-input-search-icon{ + border:0px !important; +} + +.ant-progress.ant-progress-status-active .ant-progress-inner, +.ant-progress.ant-progress-status-normal .ant-progress-inner{ + background-color:#ffffff33 !important; +} + +.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected a{ + color:#fff !important; +} + +.ant-menu span.ant-menu-title-content .custom-icon path, +.ant-menu span.ant-menu-title-content:hover .custom-icon path{ + fill:#fff !important; +} + +.ant-menu-submenu-popup.ant-menu-light, .ant-menu-light > .ant-menu, +.ant-menu-submenu ul, +.ant-menu-submenu ul .ant-menu-item { + background:var(--main-grey-escuro) !important; +} + +.ant-pagination .ant-pagination-item.ant-pagination-item-active{ + border:0px; +} + +.ant-pagination .ant-pagination-item{ + border:0px; + margin-left:2px !important; +} + +.ant-table-placeholder{ + background:var(--main-linear-cora) +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab{ + margin:0px; + padding:14px 30px; + border-bottom:3px solid transparent; + transform: all 1s easyin !important; + background-color:var(--main-linear-cora) !important; + font-weight: 600; +} + +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab.ant-tabs-tab-active, +.ant-tabs-top-bar .ant-tabs-nav-scroll .ant-tabs-tab:hover{ + background-color:var(--main-primary-escuro) !important; + color: #fff; + border-bottom:3px solid var(--main-primary-escuro); +} + +.ant-table .ant-table-body .ant-btn{ + background:var(--main-primary-escuro) !important; +} + +.ant-table .ant-table-body .ant-btn:hover{ + background:var(--main-primary-claro) !important; +} + +.ant-input-group-addon{ + background-color:transparent; +} + +/* FIM DARKMODE */ + +button.ant-btn.ant-btn-dangerous:not(.ant-btn-icon-only) +{ + background-color:var(--main-vermelho) !important; +} + +/* TREE FILES */ +.ant-tree-icon__customize, +.ant-tree.ant-tree-show-line li span.ant-tree-switcher{ + background-color:transparent; +} + +.ant-tree .ant-tree-node-content-wrapper:hover{ + background-color:inherit !important; +} + +.ant-tree .ant-tree-node-content-wrapper:hover span{ + color:var(--main-primary-escuro) !important; +} + + + + + + +.ant-dropdown-placement-bottomRight ul{ + background-color:#fff;; +} + + +.ant-dropdown-placement-bottomRight li *{ + color:#000 !important; +} + +.ant-table .ant-table-row-expand-icon{ + border-radius:50px; +} + +.ant-table .ant-table-expanded-row.ant-table-expanded-row-level-1:hover td, +.ant-table .ant-table-expanded-row.ant-table-expanded-row-level-1 td:hover{ + background-color:transparent !important; +} + +.ant-table .ant-table-row-expand-icon:after{ + color:#000; + display:flex; + justify-content: center; + align-items: center; + height:100%; + width:100%; +} + +.vm-info-card .resource-detail-item__details a, .vm-info-card .resource-detail-item a { + color: var(--main-primary-escuro); + cursor: default; + pointer-events: none; +} + +/* Modal */ +.ant-modal-content{ + background-color:var(--main-linear-cora); + border:1px solid #fff; +} + +.ant-modal-content .ant-radio-group.ant-radio-group-solid span{ + color:#000 !important; +} + +.ant-modal-content .ant-radio-group.ant-radio-group-solid label{ + background-color:#fff; +} + +.ant-modal-content .ant-radio-group.ant-radio-group-solid .ant-radio-button-wrapper-checked span, +.ant-modal-content .ant-radio-group.ant-radio-group-solid label:hover span{ + color:#fff !important; +} + +.ant-modal-content label{ + color:#fff; +} + +.ant-modal-content .ant-tabs-nav .ant-tabs-tab{ + color:#fff; +} + +.resource-detail-item{ + border-bottom: 1px solid #777; + padding-bottom: 20px; +} + +.account-center-tags .ant-divider-horizontal{ + display:none +} + + +/* MODAL Steps */ + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item-title{ + font-size:14px !important; + border-top:0px !important; + justify-content: center; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-wait .ant-steps-item-icon{ + background-color:#ddd !important; + border-color:#999 !important; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-wait .ant-steps-item-icon span{ + color:#999; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-finish .ant-steps-item-icon{ + background-color:#555 !important; + border-color:#555 !important; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-finish .ant-steps-item-icon span{ + color:white !important; + right:-3px; + top:9px; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item-container .ant-steps-item-icon{ + background-color:var(--main-primary-claro) !important; + margin-top:0px; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-active .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + background-color:var(--main-primary-escuro) !important; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-wait .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + background-color:var(--main-primary-claro) !important; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-finish .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + background-color:var(--main-secondary-escuro) !important; +} + +.ant-modal-body .ant-steps.ant-steps-dot .ant-steps-item.ant-steps-item-finish .ant-steps-item-container .ant-steps-item-icon .ant-steps-icon-dot{ + color:white !important; + left:0px !important; + top:0px; +} + +.ant-modal-body .ant-table-body .ant-tag span{ + position:relative !important; +} + +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle{ + padding-top:15px !important; +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous{ + background-color:var( --main-vermelho); + border-color:var( --main-vermelho); +} + +.ant-modal-body .ant-form-item-control-input .anticon.anticon-close-circle{ + top:0px !important; + bottom:0px !important; + left:-10px !important; +} + +.ant-modal-body .ant-form-item-control-input .ant-select-arrow{ + right:15px +} + +.ant-form-item-has-feedback .ant-form-item-control-input :not(.ant-input-group-addon) > .ant-select .ant-select-arrow{ + right:40px +} + +.ant-modal-body .ant-form-item-control-input .ant-select-clear{ + background-color:transparent !important; + top:13px; + right:-20px !important; +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous .anticon{ + /* left:2px; + top:7px; */ +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous:hover, +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle:hover{ + background-color:var(--main-secondary-escuro); + border-color:var(--main-secondary-escuro); +} + +.ant-modal-body .ant-table-body button.ant-btn-circle.ant-btn-dangerous .anticon, +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle:hover svg{ + color:#fff; +} + +.ant-modal-body .ant-table-body .traffic-select-item > span button.ant-btn-circle > span{ + top:4px; + left:-1px; +} + +.ant-modal-body .traffic-select-item .ant-select-arrow .anticon.anticon-down{ + margin-left:0px !important; +} + +.ant-modal-body .ant-form-item-control-input .anticon.anticon-close-circle, +.ant-modal-body .ant-form-item-control-input .anticon.anticon-check-circle{ + left:5px; + top:4px !important; +} + +.ant-modal-body .card-waiting-launch .anticon-check-circle{ + position: absolute; + font-size:40px !important; + left:60px; + top:40px +} + +.ant-modal-body .anticon-rocket{ + left:1px; + bottom:7px; +} + +.ant-modal-body .anticon-rocket:hover{ + color:inherit !important; +} + +.card-launch-content .ant-steps-item-finish .anticon.anticon-check.ant-steps-finish-icon{ + right:-6px; + color:#fff; + top:19px +} + +.card-launch-content .ant-steps-item .ant-steps-item-icon .ant-steps-icon .anticon{ + left:-10px +} + +.card-launch-content .ant-steps-item .ant-steps-item-icon .ant-steps-icon .anticon.ant-steps-finish-icon{ + position: absolute; +} + +.card-launch-content .ant-steps-item .ant-steps-item-icon{ + min-width:23px !important; +} + +.card-launch-content .anticon.anticon-close-circle{ + left:-15px !important; +} + +.card-launch-content .anticon.anticon-loading{ + top:1px +} + +/* NOVAS ENTRADAS */ +.ant-spin-nested-loading .ant-table-bordered .ant-table-body .ant-table-row-cell-break-word .ant-btn-circle{ + +} + +.ant-modal-body .ant-table-bordered .ant-table-body .ant-table-row-cell-break-word .ant-btn-circle.ant-btn-dangerous{ + background-color:var(--main-vermelho) !important; + + &:hover{ + background-color:var(--main-secondary-escuro) !important; + } + +} + +.ant-alert .ant-alert-message{ + margin-left:40px; +} + +.ant-alert:not(.ant-alert-no-icon) .ant-alert-message{ + margin-left:40px; +} + +.ant-btn-dangerous.ant-btn-primary.ant-btn-dangerous{ + background:var(--main-vermelho); + border-color:var(--main-vermelho); +} + + +/* SOMENTE DARK */ +.ant-modal-body .ant-table-bordered .ant-table-body .ant-table-row-cell-break-word .ant-btn-circle.ant-btn-dangerous span{ + background-color: transparent; + margin-right:5px !important; +} + +.ant-steps-item-content .ant-card.ant-card-bordered .ant-card-body{ + border:1px solid var(--main-primary-escuro) !important; + border-radius: 0px; +} + +.ant-modal-body .ant-form.ant-form-horizontal.form-content{ + background-color:transparent !important; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item-title{ + color:#fff; +} +.ant-modal-body .zone-support{ + background:transparent !important; +} + +.ant-card-bordered:not(:last-child){ + border-bottom:1px solid rgba(255,255,255,0.5) !important; +} + +.ant-steps-item-description .ant-card-bordered:not(:last-child){ + border-bottom:0px !important; +} + +.ant-card-bordered.ant-form-text{ + background-color:var(--main-grey-escuro); + border-bottom:0px !important; + color:#fff; +} + +.ant-modal-body .ant-steps.ant-steps-horizontal .ant-steps-item.ant-steps-item-finish .ant-steps-item-icon span{ + color:white !important; + right:3px; + top:0px; +} + +.ant-steps-item-icon{ + background-color:var(--main-primary-escuro) !important; + border-color:var(--main-primary-escuro) !important; + margin-top:10px +} + +.card-launch-content .ant-steps-item .ant-steps-item-icon{ + background-color:transparent !important; +} + +form.ant-form .ant-form-item-explain.ant-form-item-explain-error{ + color:pink !important; +} + diff --git a/ui/public/index.html b/ui/public/index.html index b681ad6a9028..7397f27d545e 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -54,12 +54,4 @@
- diff --git a/ui/src/main.js b/ui/src/main.js index 6917fa986e04..392dfdf8c8eb 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -76,7 +76,7 @@ fetch('config.json?ts=' + Date.now()).then(response => response.json()).then(asy let accountid = null let domainid = null - if (userid !== undefined) { + if (userid !== undefined && Cookies.get('sessionkey')) { await api('listUsers', { userid: userid }).then(response => { accountid = response.listusersresponse.user[0].accountid domainid = response.listusersresponse.user[0].domainid From ccfa62235c31aa7c7743e2817da925d69b837c1b Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Thu, 18 Apr 2024 13:08:16 -0300 Subject: [PATCH 06/22] Add miising license --- .../cloudstack/gui/themes/GuiThemeService.java | 16 ++++++++++++++++ .../cloudstack/gui/theme/dao/GuiThemeDao.java | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java index 20bec4d037dc..ed6109519161 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java @@ -1,3 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.themes; import org.apache.cloudstack.api.command.user.gui.themes.CreateGuiThemeCmd; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java index 7049fd0da44e..5583b4768659 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java @@ -1,3 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; import com.cloud.utils.db.GenericDao; From 90c966b227a3bfb434f384e4801eacf0c51a3273 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Thu, 18 Apr 2024 13:37:43 -0300 Subject: [PATCH 07/22] Revert cookie check --- ui/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/main.js b/ui/src/main.js index 392dfdf8c8eb..6917fa986e04 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -76,7 +76,7 @@ fetch('config.json?ts=' + Date.now()).then(response => response.json()).then(asy let accountid = null let domainid = null - if (userid !== undefined && Cookies.get('sessionkey')) { + if (userid !== undefined) { await api('listUsers', { userid: userid }).then(response => { accountid = response.listusersresponse.user[0].accountid domainid = response.listusersresponse.user[0].domainid From 40af0442a0426a2ae55074f3522d010d61592d38 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Mon, 22 Apr 2024 13:47:23 -0300 Subject: [PATCH 08/22] Fix imports --- .../apache/cloudstack/gui/theme/GuiThemeServiceImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java index d7c1fd5f88af..d13f8b7300a8 100644 --- a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java @@ -45,10 +45,11 @@ import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao; import org.apache.cloudstack.gui.themes.GuiThemeDetailsVO; import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; -import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.apache.cloudstack.gui.themes.GuiThemeService; +import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import javax.inject.Inject; import java.util.ArrayList; @@ -59,7 +60,7 @@ public class GuiThemeServiceImpl implements GuiThemeService { - protected Logger logger = Logger.getLogger(GuiThemeServiceImpl.class); + protected Logger logger = LogManager.getLogger(getClass()); private static final List ALLOWED_PRIMITIVE_PROPERTIES = List.of("appTitle", "footer", "loginFooter", "logo", "minilogo", "banner"); From 0d3bee5ca38eab7a9663bc2b201b04fa1676e232 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Mon, 22 Apr 2024 14:54:38 -0300 Subject: [PATCH 09/22] Fix pre-commit --- .../src/main/resources/META-INF/db/schema-41900to42000.sql | 1 - ui/public/assets/asf-logo.svg | 2 +- ui/public/assets/feather.svg | 2 +- ui/public/css/apache-theme.css | 1 - ui/public/css/dark-theme.css | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql index e25ebb691189..5e620a66ca68 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql @@ -122,4 +122,3 @@ SELECT `cloud`.`gui_themes`.`removed` AS `removed` FROM `cloud`.`gui_themes` LEFT JOIN `cloud`.`gui_themes_details` ON `cloud`.`gui_themes_details`.`gui_theme_id` = `cloud`.`gui_themes`.`id` GROUP BY `cloud`.`gui_themes`.`id`; - diff --git a/ui/public/assets/asf-logo.svg b/ui/public/assets/asf-logo.svg index ce1f394599ad..d69e8d7ebb89 100644 --- a/ui/public/assets/asf-logo.svg +++ b/ui/public/assets/asf-logo.svg @@ -113,4 +113,4 @@ - \ No newline at end of file + diff --git a/ui/public/assets/feather.svg b/ui/public/assets/feather.svg index 7c6a07b885db..95cc5b2c07ac 100644 --- a/ui/public/assets/feather.svg +++ b/ui/public/assets/feather.svg @@ -86,4 +86,4 @@ - \ No newline at end of file + diff --git a/ui/public/css/apache-theme.css b/ui/public/css/apache-theme.css index e00f91bba343..832d2bd22b3e 100644 --- a/ui/public/css/apache-theme.css +++ b/ui/public/css/apache-theme.css @@ -1148,4 +1148,3 @@ button.ant-btn.ant-btn-dangerous:not(.ant-btn-icon-only) background:var(--main-vermelho); border-color:var(--main-vermelho); } - diff --git a/ui/public/css/dark-theme.css b/ui/public/css/dark-theme.css index 155ef9d94f62..7bfe1e89f68b 100644 --- a/ui/public/css/dark-theme.css +++ b/ui/public/css/dark-theme.css @@ -1571,4 +1571,3 @@ button.ant-btn.ant-btn-dangerous:not(.ant-btn-icon-only) form.ant-form .ant-form-item-explain.ant-form-item-explain-error{ color:pink !important; } - From 4b45e094318844d6487b8f7935b457e71095ce2c Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Wed, 12 Jun 2024 16:39:13 -0300 Subject: [PATCH 10/22] Address log4j2 string to format review and add license to css files --- .../gui/theme/GuiThemeServiceImpl.java | 12 ++++++------ ui/public/css/apache-theme.css | 19 +++++++++++++++++++ ui/public/css/dark-theme.css | 19 +++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java index d13f8b7300a8..f056037b6a34 100644 --- a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java @@ -166,7 +166,7 @@ protected void persistGuiThemeDetails(long guiThemeId, String commonNames, Strin protected void persistDetailValueIfNotNull(long guiThemeId, String providedParameter, String type) { if (providedParameter == null) { - logger.trace(String.format("GUI theme provided parameter `%s` is null; therefore, it will be ignored.", type)); + logger.trace("GUI theme provided parameter `{}` is null; therefore, it will be ignored.", type); return; } for (String splitParameter : StringUtils.deleteWhitespace(providedParameter).split(",")) { @@ -258,7 +258,7 @@ protected void validateJsonConfiguration(String jsonConfig) { Set> entries = jsonElement.getAsJsonObject().entrySet(); entries.stream().forEach(entry -> validateJsonAttributes(entry, jsonObject)); } catch (JsonSyntaxException exception) { - logger.error(String.format("The following exception was thrown while parsing the JSON object: [%s].", exception.getMessage())); + logger.error("The following exception was thrown while parsing the JSON object: [{}].", exception.getMessage()); throw new CloudRuntimeException("Specified JSON configuration is not a valid JSON object."); } } @@ -272,7 +272,7 @@ private void validateJsonAttributes(Map.Entry entry, JsonOb String entryKey = entry.getKey(); if (entryValue.isJsonPrimitive() && ALLOWED_PRIMITIVE_PROPERTIES.contains(entryKey)) { - logger.trace("The JSON attribute [%s] is a valid option."); + logger.trace("The JSON attribute [{}] is a valid option.", entryKey); jsonObject.add(entryKey, entryValue); } else if (entryValue.isJsonObject() && ERROR.equals(entryKey)) { validateErrorAttribute(entry, jsonObject); @@ -330,7 +330,7 @@ protected JsonObject createJsonObject(Set> entrie } protected void warnOfInvalidJsonAttribute(String entryKey) { - logger.warn(String.format("The JSON attribute [%s] is not a valid option, therefore, it will be ignored.", entryKey)); + logger.warn("The JSON attribute [{}] is not a valid option, therefore, it will be ignored.", entryKey); } /** @@ -407,7 +407,7 @@ protected GuiThemeJoinVO persistGuiTheme(Long guiThemeId, String name, String de guiThemeVO.setRecursiveDomains(recursiveDomains); } - logger.trace(String.format("Persisting GUI theme [%s] with CSS [%s] and JSON configuration [%s].", guiThemeVO, guiThemeVO.getCss(), guiThemeVO.getJsonConfiguration())); + logger.trace("Persisting GUI theme [{}] with CSS [{}] and JSON configuration [{}].", guiThemeVO, guiThemeVO.getCss(), guiThemeVO.getJsonConfiguration()); guiThemeDao.persist(guiThemeVO); guiThemeDetailsDao.expungeByGuiThemeId(guiThemeId); @@ -433,7 +433,7 @@ public void removeGuiTheme(RemoveGuiThemeCmd cmd) { if (guiThemeVO != null) { guiThemeDao.remove(guiThemeId); } else { - logger.error(String.format("Unable to find a GUI theme with the specified ID [%s].", guiThemeId)); + logger.error("Unable to find a GUI theme with the specified ID [{}].", guiThemeId); throw new CloudRuntimeException("Unable to find a GUI theme with the specified ID."); } } diff --git a/ui/public/css/apache-theme.css b/ui/public/css/apache-theme.css index 832d2bd22b3e..ad4e079db057 100644 --- a/ui/public/css/apache-theme.css +++ b/ui/public/css/apache-theme.css @@ -1,3 +1,22 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF 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 url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); :root { diff --git a/ui/public/css/dark-theme.css b/ui/public/css/dark-theme.css index 7bfe1e89f68b..b548722533ee 100644 --- a/ui/public/css/dark-theme.css +++ b/ui/public/css/dark-theme.css @@ -1,3 +1,22 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF 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 url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); :root { From 25c555115ae0542de323317f6f809ee808eb858d Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Mon, 1 Jul 2024 10:08:31 -0300 Subject: [PATCH 11/22] Fix infinite loading --- ui/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/main.js b/ui/src/main.js index 6917fa986e04..392dfdf8c8eb 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -76,7 +76,7 @@ fetch('config.json?ts=' + Date.now()).then(response => response.json()).then(asy let accountid = null let domainid = null - if (userid !== undefined) { + if (userid !== undefined && Cookies.get('sessionkey')) { await api('listUsers', { userid: userid }).then(response => { accountid = response.listusersresponse.user[0].accountid domainid = response.listusersresponse.user[0].domainid From 412c1466288111d7e451e886348445c8fd5199cf Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Wed, 24 Jul 2024 10:36:32 -0300 Subject: [PATCH 12/22] Move event details to service implementation --- .../api/command/user/gui/themes/CreateGuiThemeCmd.java | 2 -- .../api/command/user/gui/themes/RemoveGuiThemeCmd.java | 1 - .../api/command/user/gui/themes/UpdateGuiThemeCmd.java | 2 -- .../apache/cloudstack/gui/theme/GuiThemeServiceImpl.java | 7 +++++++ 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java index 578d0b8c8322..285cc066fdff 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java @@ -111,8 +111,6 @@ public Boolean getRecursiveDomains() { @Override public void execute() { - CallContext.current().setEventDetails(String.format("Name: %s, AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", getName(), getAccountIds(), - getDomainIds(), getRecursiveDomains(), getCommonNames())); GuiThemeJoinVO guiTheme = guiThemeService.createGuiTheme(this); if (guiTheme == null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java index 9bc718c893f8..3910b07ad499 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java @@ -46,7 +46,6 @@ public Long getId() { @Override public void execute() { - CallContext.current().setEventDetails(String.format("ID: %s", getId())); guiThemeService.removeGuiTheme(this); final SuccessResponse response = new SuccessResponse(); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java index c6529208a3e5..86126187fd68 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java @@ -118,8 +118,6 @@ public Boolean getIsPublic() { @Override public void execute() { - CallContext.current().setEventDetails(String.format("ID: %s, Name: %s, AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", getId(), getName(), - getAccountIds(), getDomainIds(), getRecursiveDomains(), getCommonNames())); GuiThemeJoinVO guiThemeJoinVO = guiThemeService.updateGuiTheme(this); if (guiThemeJoinVO == null) { diff --git a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java index f056037b6a34..42aab9170e70 100644 --- a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java @@ -142,6 +142,9 @@ public GuiThemeJoinVO createGuiTheme(CreateGuiThemeCmd cmd) { boolean isPublic = cmd.getPublic(); Boolean recursiveDomains = cmd.getRecursiveDomains(); + CallContext.current().setEventDetails(String.format("Name: %s, AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", name, providedAccountIds, + providedDomainIds, recursiveDomains, commonNames)); + if (StringUtils.isAllBlank(css, jsonConfiguration)) { throw new CloudRuntimeException("Either the `css` or `jsonConfiguration` parameter must be informed."); } @@ -369,6 +372,9 @@ public GuiThemeJoinVO updateGuiTheme(UpdateGuiThemeCmd cmd) { Boolean isPublic = cmd.getIsPublic(); Boolean recursiveDomains = cmd.getRecursiveDomains(); + CallContext.current().setEventDetails(String.format("ID: %s, Name: %s, AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", guiThemeId, name, + providedAccountIds, providedDomainIds, recursiveDomains, commonNames)); + validateParameters(jsonConfiguration, providedDomainIds, providedAccountIds, commonNames, guiThemeId); if (shouldSetGuiThemeToPrivate(providedDomainIds, providedAccountIds)) { @@ -429,6 +435,7 @@ protected String ifBlankReturnNull(String value) { public void removeGuiTheme(RemoveGuiThemeCmd cmd) { Long guiThemeId = cmd.getId(); GuiThemeVO guiThemeVO = guiThemeDao.findById(guiThemeId); + CallContext.current().setEventDetails(String.format("ID: %s", guiThemeId)); if (guiThemeVO != null) { guiThemeDao.remove(guiThemeId); From 10ab939916b43b8fa26f7d4874ccee9613617b8b Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Wed, 24 Jul 2024 10:37:33 -0300 Subject: [PATCH 13/22] Move view to a specific view file --- .../META-INF/db/schema-41900to42000.sql | 19 ---------- .../db/views/cloud.gui_themes_view.sql | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql index 5e620a66ca68..95adcfd74ae3 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql @@ -103,22 +103,3 @@ CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( PRIMARY KEY (`id`), CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) ); - -CREATE OR REPLACE - VIEW `cloud`.`gui_themes_view` AS -SELECT - `cloud`.`gui_themes`.`id` AS `id`, - `cloud`.`gui_themes`.`uuid` AS `uuid`, - `cloud`.`gui_themes`.`name` AS `name`, - `cloud`.`gui_themes`.`description` AS `description`, - `cloud`.`gui_themes`.`css` AS `css`, - `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`, - (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'commonName' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) common_names, - (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'domain' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) domains, - (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'account' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) accounts, - `cloud`.`gui_themes`.`recursive_domains` AS `recursive_domains`, - `cloud`.`gui_themes`.`is_public` AS `is_public`, - `cloud`.`gui_themes`.`created` AS `created`, - `cloud`.`gui_themes`.`removed` AS `removed` -FROM `cloud`.`gui_themes` LEFT JOIN `cloud`.`gui_themes_details` ON `cloud`.`gui_themes_details`.`gui_theme_id` = `cloud`.`gui_themes`.`id` -GROUP BY `cloud`.`gui_themes`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql new file mode 100644 index 000000000000..3173274623ed --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql @@ -0,0 +1,38 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF 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. + +-- VIEW `cloud`.`gui_themes_view`; + +DROP VIEW IF EXISTS `cloud`.`gui_themes_view`; + +CREATE VIEW `cloud`.`gui_themes_view` AS +SELECT + `cloud`.`gui_themes`.`id` AS `id`, + `cloud`.`gui_themes`.`uuid` AS `uuid`, + `cloud`.`gui_themes`.`name` AS `name`, + `cloud`.`gui_themes`.`description` AS `description`, + `cloud`.`gui_themes`.`css` AS `css`, + `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'commonName' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) common_names, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'domain' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) domains, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'account' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) accounts, + `cloud`.`gui_themes`.`recursive_domains` AS `recursive_domains`, + `cloud`.`gui_themes`.`is_public` AS `is_public`, + `cloud`.`gui_themes`.`created` AS `created`, + `cloud`.`gui_themes`.`removed` AS `removed` +FROM `cloud`.`gui_themes` LEFT JOIN `cloud`.`gui_themes_details` ON `cloud`.`gui_themes_details`.`gui_theme_id` = `cloud`.`gui_themes`.`id` +GROUP BY `cloud`.`gui_themes`.`id`; From 6a3f8ea72e0841374653d282223e6f5ba16c21d0 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Mon, 5 Aug 2024 17:25:31 -0300 Subject: [PATCH 14/22] Refactoring gui theme classes --- api/pom.xml | 9 -- .../cloudstack/api/ResponseGenerator.java | 4 +- .../user/gui/themes/CreateGuiThemeCmd.java | 12 +- .../user/gui/themes/ListGuiThemesCmd.java | 4 +- .../user/gui/themes/RemoveGuiThemeCmd.java | 4 +- .../user/gui/themes/UpdateGuiThemeCmd.java | 12 +- .../api/response/GuiThemeResponse.java | 4 +- .../cloudstack/gui/themes/GuiTheme.java | 61 ++++++++++ .../gui/themes/GuiThemeDetails.java | 21 +++- .../cloudstack/gui/themes/GuiThemeJoin.java | 33 ++++-- .../gui/themes/GuiThemeService.java | 4 +- .../gui/theme/dao/GuiThemeJoinDao.java | 31 ------ .../gui/themes/GuiThemeDetailsVO.java | 11 +- .../cloudstack/gui/themes/GuiThemeJoinVO.java | 37 +++--- .../cloudstack/gui/themes/GuiThemeVO.java | 23 +++- .../dao/GuiThemeDaoImpl.java | 5 +- .../dao/GuiThemeDetailsDaoImpl.java | 10 +- .../dao/GuiThemeJoinDaoImpl.java | 10 +- .../META-INF/db/schema-41900to42000.sql | 105 ------------------ .../META-INF/db/schema-42010to42100.sql | 24 ++++ .../java/com/cloud/api/ApiResponseHelper.java | 32 +++--- .../GuiThemeServiceImpl.java | 26 ++--- .../GuiThemeServiceImplTest.java | 8 +- 23 files changed, 231 insertions(+), 259 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/gui/themes/GuiTheme.java rename engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java => api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetails.java (69%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java => api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoin.java (60%) delete mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java rename {api => engine/schema}/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java (93%) rename {api => engine/schema}/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java (78%) rename {api => engine/schema}/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java (93%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{theme => themes}/dao/GuiThemeDaoImpl.java (88%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{theme => themes}/dao/GuiThemeDetailsDaoImpl.java (97%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{theme => themes}/dao/GuiThemeJoinDaoImpl.java (97%) delete mode 100644 engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql rename server/src/main/java/org/apache/cloudstack/gui/{theme => themes}/GuiThemeServiceImpl.java (96%) rename server/src/test/java/org/apache/cloudstack/gui/{theme => themes}/GuiThemeServiceImplTest.java (97%) diff --git a/api/pom.xml b/api/pom.xml index fdaa9f185852..ec68e24c7e59 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -71,15 +71,6 @@ cloud-framework-direct-download ${project.version} - - org.eclipse.persistence - javax.persistence - - - org.apache.cloudstack - cloud-framework-db - ${project.version} - diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 6bce5e893105..f092f5c1aa28 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -151,7 +151,7 @@ import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; +import org.apache.cloudstack.gui.themes.GuiThemeJoin; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; @@ -582,5 +582,5 @@ List createTemplateResponses(ResponseView view, VirtualMachine void updateTemplateIsoResponsesForIcons(List responses, ResourceTag.ResourceObjectType type); - GuiThemeResponse createGuiThemeResponse(GuiThemeJoinVO guiThemeJoinVO); + GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java index 285cc066fdff..0f0c14e4c424 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java @@ -25,14 +25,14 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiTheme; +import org.apache.cloudstack.gui.themes.GuiThemeJoin; import org.apache.cloudstack.gui.themes.GuiThemeService; import javax.inject.Inject; @APICommand(name = "createGuiTheme", description = "Creates a customized GUI theme for a set of Common Names (fixed or wildcard), a set of domain UUIDs, and/or a set of " + - "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.20.0.0", authorized = {RoleType.Admin}) public class CreateGuiThemeCmd extends BaseCmd { @@ -111,13 +111,13 @@ public Boolean getRecursiveDomains() { @Override public void execute() { - GuiThemeJoinVO guiTheme = guiThemeService.createGuiTheme(this); + GuiThemeJoin guiThemeJoin = guiThemeService.createGuiTheme(this); - if (guiTheme == null) { + if (guiThemeJoin == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create the GUI theme."); } - GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiTheme); + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); response.setResponseName(getCommandName()); this.setResponseObject(response); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java index bc9c145f138f..66024535350e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java @@ -25,12 +25,12 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiTheme; import org.apache.cloudstack.gui.themes.GuiThemeService; import javax.inject.Inject; -@APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, +@APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, RoleType.ResourceAdmin}) public class ListGuiThemesCmd extends BaseListCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java index 3910b07ad499..18713327cb51 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java @@ -24,12 +24,12 @@ import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiTheme; import org.apache.cloudstack.gui.themes.GuiThemeService; import javax.inject.Inject; -@APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, +@APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) public class RemoveGuiThemeCmd extends BaseCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java index 86126187fd68..ec638a4da2b2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java @@ -25,14 +25,14 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.GuiTheme; +import org.apache.cloudstack.gui.themes.GuiThemeJoin; import org.apache.cloudstack.gui.themes.GuiThemeService; import javax.inject.Inject; -@APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiThemeVO.class}, +@APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) public class UpdateGuiThemeCmd extends BaseCmd { @@ -118,13 +118,13 @@ public Boolean getIsPublic() { @Override public void execute() { - GuiThemeJoinVO guiThemeJoinVO = guiThemeService.updateGuiTheme(this); + GuiThemeJoin guiThemeJoin = guiThemeService.updateGuiTheme(this); - if (guiThemeJoinVO == null) { + if (guiThemeJoin == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the GUI theme."); } - GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoinVO); + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); response.setResponseName(getCommandName()); this.setResponseObject(response); } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java index 31390623fd97..771becdeb3d4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -21,11 +21,11 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; +import org.apache.cloudstack.gui.themes.GuiThemeJoin; import java.util.Date; -@EntityReference(value = {GuiThemeJoinVO.class}) +@EntityReference(value = {GuiThemeJoin.class}) public class GuiThemeResponse extends BaseResponse { @SerializedName(ApiConstants.ID) diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiTheme.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiTheme.java new file mode 100644 index 000000000000..4ecb79433be5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiTheme.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.themes; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface GuiTheme extends InternalIdentity, Identity { + + String getName(); + + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + Date getCreated(); + + Date getRemoved(); + + boolean getIsPublic(); + + void setId(Long id); + + void setUuid(String uuid); + + void setName(String name); + + void setDescription(String description); + + void setCss(String css); + + void setJsonConfiguration(String jsonConfiguration); + + void setCreated(Date created); + + void setRemoved(Date removed); + + void setIsPublic(boolean isPublic); + + boolean isRecursiveDomains(); + + void setRecursiveDomains(boolean recursiveDomains); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetails.java similarity index 69% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java rename to api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetails.java index 5583b4768659..ef9213c878f9 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetails.java @@ -14,10 +14,23 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.theme.dao; +package org.apache.cloudstack.gui.themes; -import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.api.InternalIdentity; -public interface GuiThemeDao extends GenericDao { +public interface GuiThemeDetails extends InternalIdentity { + + void setId(Long id); + + Long getGuiThemeId(); + + void setGuiThemeId(Long guiThemeId); + + String getType(); + + void setType(String type); + + String getValue(); + + void setValue(String value); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoin.java similarity index 60% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java rename to api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoin.java index b27e6678a29e..e65491349df3 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoin.java @@ -14,17 +14,34 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.theme.dao; +package org.apache.cloudstack.gui.themes; -import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.gui.themes.GuiThemeDetailsVO; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; -import java.util.List; +import java.util.Date; -public interface GuiThemeDetailsDao extends GenericDao { - List listGuiThemeIdsByCommonName(String commonName); +public interface GuiThemeJoin extends InternalIdentity, Identity { - List listGuiThemeIdsByDomainUuids(String domainUuid); + String getName(); - void expungeByGuiThemeId(long guiThemeId); + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + String getCommonNames(); + + String getDomains(); + + String getAccounts(); + + boolean isRecursiveDomains(); + + boolean getIsPublic(); + + Date getCreated(); + + Date getRemoved(); } diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java index ed6109519161..ce3b5bcf9ee4 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java +++ b/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java @@ -27,9 +27,9 @@ public interface GuiThemeService { ListResponse listGuiThemes(ListGuiThemesCmd cmd); - GuiThemeJoinVO createGuiTheme(CreateGuiThemeCmd cmd); + GuiThemeJoin createGuiTheme(CreateGuiThemeCmd cmd); - GuiThemeJoinVO updateGuiTheme(UpdateGuiThemeCmd cmd); + GuiThemeJoin updateGuiTheme(UpdateGuiThemeCmd cmd); void removeGuiTheme(RemoveGuiThemeCmd cmd); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java deleted file mode 100644 index abd8f59d5216..000000000000 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; - -import com.cloud.utils.Pair; -import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; - -import java.util.List; - -public interface GuiThemeJoinDao extends GenericDao { - GuiThemeJoinVO findDefaultTheme(); - - Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); - - Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, Boolean showPublic); -} diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java similarity index 93% rename from api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java index daa6a0715221..00d13da3ce03 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.gui.themes; -import org.apache.cloudstack.api.InternalIdentity; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -27,7 +25,7 @@ @Entity @Table(name = "gui_themes_details") -public class GuiThemeDetailsVO implements InternalIdentity { +public class GuiThemeDetailsVO implements GuiThemeDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) @@ -56,30 +54,37 @@ public long getId() { return id; } + @Override public void setId(Long id) { this.id = id; } + @Override public Long getGuiThemeId() { return guiThemeId; } + @Override public void setGuiThemeId(Long guiThemeId) { this.guiThemeId = guiThemeId; } + @Override public String getType() { return type; } + @Override public void setType(String type) { this.type = type; } + @Override public String getValue() { return value; } + @Override public void setValue(String value) { this.value = value; } diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java similarity index 78% rename from api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java index 85681380c87f..46bebde31599 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.gui.themes; import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.api.InternalIdentity; import javax.persistence.Column; import javax.persistence.Entity; @@ -29,7 +28,7 @@ @Entity @Table(name = "gui_themes_view") -public class GuiThemeJoinVO implements InternalIdentity { +public class GuiThemeJoinVO implements GuiThemeJoin { @Id @Column(name = "id", nullable = false) private Long id; @@ -75,75 +74,67 @@ public class GuiThemeJoinVO implements InternalIdentity { public GuiThemeJoinVO() { } - public GuiThemeJoinVO(Long id, String uuid, String name, String description, String css, String jsonConfiguration, String commonNames, String domains, - String accounts, boolean recursiveDomains, boolean isPublic, Date created, Date removed) { - this.id = id; - this.uuid = uuid; - this.name = name; - this.description = description; - this.css = css; - this.jsonConfiguration = jsonConfiguration; - this.commonNames = commonNames; - this.domains = domains; - this.accounts = accounts; - this.recursiveDomains = recursiveDomains; - this.isPublic = isPublic; - this.created = created; - this.removed = removed; - } - + @Override public long getId() { return id; } - public void setId(Long id) { - this.id = id; - } - + @Override public String getUuid() { return uuid; } + @Override public String getName() { return name; } + @Override public String getDescription() { return description; } + @Override public String getCss() { return css; } + @Override public String getJsonConfiguration() { return jsonConfiguration; } + @Override public String getCommonNames() { return commonNames; } + @Override public String getDomains() { return domains; } + @Override public String getAccounts() { return accounts; } + @Override public boolean isRecursiveDomains() { return recursiveDomains; } + @Override public boolean getIsPublic() { return isPublic; } + @Override public Date getCreated() { return created; } + @Override public Date getRemoved() { return removed; } diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java similarity index 93% rename from api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java index 063469630acc..45b0c3d5894a 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java @@ -17,8 +17,6 @@ package org.apache.cloudstack.gui.themes; import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.api.Identity; -import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; @@ -34,7 +32,7 @@ @Entity @Table(name = "gui_themes") -public class GuiThemeVO implements InternalIdentity, Identity { +public class GuiThemeVO implements GuiTheme { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) @@ -94,79 +92,96 @@ public String getUuid() { return uuid; } + @Override public String getName() { return name; } + @Override public String getDescription() { return description; } + @Override public String getCss() { return css; } + @Override public String getJsonConfiguration() { return jsonConfiguration; } + @Override public Date getCreated() { return created; } + @Override public Date getRemoved() { return removed; } + @Override public boolean getIsPublic() { return isPublic; } + @Override public void setId(Long id) { this.id = id; } + @Override public void setUuid(String uuid) { this.uuid = uuid; } + @Override public void setName(String name) { this.name = name; } + @Override public void setDescription(String description) { this.description = description; } + @Override public void setCss(String css) { this.css = css; } + @Override public void setJsonConfiguration(String jsonConfiguration) { this.jsonConfiguration = jsonConfiguration; } + @Override public void setCreated(Date created) { this.created = created; } + @Override public void setRemoved(Date removed) { this.removed = removed; } + @Override public void setIsPublic(boolean isPublic) { this.isPublic = isPublic; } + @Override public boolean isRecursiveDomains() { return recursiveDomains; } + @Override public void setRecursiveDomains(boolean recursiveDomains) { this.recursiveDomains = recursiveDomains; } - @Override public String toString() { return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "description", "isPublic", "recursiveDomains"); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDaoImpl.java similarity index 88% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDaoImpl.java index f87ffd95fe98..0af5d0750df7 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDaoImpl.java @@ -14,12 +14,13 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.theme.dao; +package org.apache.cloudstack.gui.themes.dao; +import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; import org.apache.cloudstack.gui.themes.GuiThemeVO; import org.springframework.stereotype.Component; @Component -public class GuiThemeDaoImpl extends GenericDaoBase implements GuiThemeDao { +public class GuiThemeDaoImpl extends GenericDaoBase implements GenericDao { } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDetailsDaoImpl.java similarity index 97% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDetailsDaoImpl.java index 0b3dcabc3ab4..2349d94235a2 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDetailsDaoImpl.java @@ -14,10 +14,11 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.theme.dao; +package org.apache.cloudstack.gui.themes.dao; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; +import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -32,15 +33,14 @@ import java.util.List; @Component -public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GuiThemeDetailsDao { +public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GenericDao { @Inject DomainDao domainDao; @Inject - GuiThemeDao guiThemeDao; + GuiThemeDaoImpl guiThemeDao; - @Override public List listGuiThemeIdsByCommonName(String commonName) { GenericSearchBuilder detailsDaoSearchBuilder = createSearchBuilder(Long.class); detailsDaoSearchBuilder.selectFields(detailsDaoSearchBuilder.entity().getGuiThemeId()); @@ -57,7 +57,6 @@ public List listGuiThemeIdsByCommonName(String commonName) { return customSearch(searchCriteria, null); } - @Override public List listGuiThemeIdsByDomainUuids(String domainUuid) { List guiThemeIds = new ArrayList<>(); String requestedDomainPath = domainDao.findByUuid(domainUuid).getPath(); @@ -116,7 +115,6 @@ private GenericSearchBuilder createDetailsSearchBuilder return guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder; } - @Override public void expungeByGuiThemeId(long guiThemeId) { SearchBuilder searchBuilder = createSearchBuilder(); searchBuilder.and("guiThemeId", searchBuilder.entity().getGuiThemeId(), SearchCriteria.Op.EQ); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeJoinDaoImpl.java similarity index 97% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeJoinDaoImpl.java index 5ca5cafceb12..caf8cdd08126 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeJoinDaoImpl.java @@ -14,10 +14,11 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.theme.dao; +package org.apache.cloudstack.gui.themes.dao; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -32,13 +33,12 @@ import java.util.List; @Component -public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GuiThemeJoinDao { +public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GenericDao { @Inject - GuiThemeDetailsDao guiThemeDetailsDao; + GuiThemeDetailsDaoImpl guiThemeDetailsDao; public static final Long INVALID_ID = -1L; - @Override public GuiThemeJoinVO findDefaultTheme() { SearchBuilder searchBuilder = createSearchBuilder(); searchBuilder.and("commonNames", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.NULL); @@ -51,13 +51,11 @@ public GuiThemeJoinVO findDefaultTheme() { return findOneBy(searchCriteria); } - @Override public Pair, Integer> listGuiThemesWithNoAuthentication(String commonName) { SearchCriteria searchCriteria = createGuiThemeSearchCriteria(null, null, commonName, null, null, null, false); return searchOrderByCreatedDate(searchCriteria, false); } - @Override public Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, Boolean showPublic) { SearchCriteria searchCriteria = createGuiThemeSearchCriteria(id, name, commonName, domainUuid, accountUuid, showPublic, listAll); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql deleted file mode 100644 index 95adcfd74ae3..000000000000 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql +++ /dev/null @@ -1,105 +0,0 @@ --- Licensed to the Apache Software Foundation (ASF) under one --- or more contributor license agreements. See the NOTICE file --- distributed with this work for additional information --- regarding copyright ownership. The ASF 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. - ---; --- Schema upgrade from 4.19.0.0 to 4.20.0.0 ---; - --- Add tag column to tables -CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_limit', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the limit" '); -CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_count', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the resource count" '); -CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the resource reservation" '); -ALTER TABLE `resource_count` -DROP INDEX `i_resource_count__type_accountId`, -DROP INDEX `i_resource_count__type_domaintId`, -ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`), -ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`); - - -ALTER TABLE `cloud`.`resource_reservation` - ADD COLUMN `resource_id` bigint unsigned NULL; - -ALTER TABLE `cloud`.`resource_reservation` - MODIFY COLUMN `amount` bigint NOT NULL; - - --- Update Default System offering for Router to 512MiB -UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local", - "Cloud.Com-InternalLBVm", "Cloud.Com-InternalLBVm-Local", - "Cloud.Com-ElasticLBVm", "Cloud.Com-ElasticLBVm-Local") - AND system_use = 1 AND ram_size < 512; - --- NSX Plugin -- -CREATE TABLE `cloud`.`nsx_providers` ( - `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', - `uuid` varchar(40), - `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', - `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', - `provider_name` varchar(40), - `hostname` varchar(255) NOT NULL, - `port` varchar(255), - `username` varchar(255) NOT NULL, - `password` varchar(255) NOT NULL, - `tier0_gateway` varchar(255), - `edge_cluster` varchar(255), - `transport_zone` varchar(255), - `created` datetime NOT NULL COMMENT 'date created', - `removed` datetime COMMENT 'date removed if not null', - PRIMARY KEY (`id`), - CONSTRAINT `fk_nsx_providers__zone_id` FOREIGN KEY `fk_nsx_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, - INDEX `i_nsx_providers__zone_id`(`zone_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- NSX Plugin -- -CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); -CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); -CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); -CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); - - --- Create table to persist quota email template configurations -CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`( - `account_id` int(11) NOT NULL, - `email_template_id` bigint(20) NOT NULL, - `enabled` int(1) UNSIGNED NOT NULL, - PRIMARY KEY (`account_id`, `email_template_id`), - CONSTRAINT `FK_quota_email_configuration_account_id` FOREIGN KEY (`account_id`) REFERENCES `cloud_usage`.`quota_account`(`account_id`), - CONSTRAINT `FK_quota_email_configuration_email_template_id` FOREIGN KEY (`email_template_id`) REFERENCES `cloud_usage`.`quota_email_templates`(`id`)); - --- Whitelabel GUI -CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( - `id` bigint(20) unsigned NOT NULL auto_increment, - `uuid` varchar(255) UNIQUE, - `name` varchar(2048) NOT NULL COMMENT 'A name to identify the theme.', - `description` varchar(4096) DEFAULT NULL COMMENT 'A description for the theme.', - `css` text DEFAULT NULL COMMENT 'The CSS to be retrieved and imported into the GUI when matching the theme access configurations.', - `json_configuration` text DEFAULT NULL COMMENT 'The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.', - `recursive_domains` tinyint(1) DEFAULT 0 COMMENT 'Defines whether the subdomains of the informed domains are considered. Default value is false.', - `is_public` tinyint(1) default 1 COMMENT 'Defines whether a theme can be retrieved by anyone when only the `internet_domains_names` is informed. If the `domain_uuids` or `account_uuids` is informed, it is considered as `false`.', - `created` datetime NOT NULL, - `removed` datetime DEFAULT NULL, - PRIMARY KEY (`id`) -); - -CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( - `id` bigint(20) unsigned NOT NULL auto_increment, - `gui_theme_id` bigint(20) unsigned NOT NULL COMMENT 'Foreign key referencing the GUI theme on `gui_themes` table.', - `type` varchar(100) DEFAULT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', - `value` text COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', - PRIMARY KEY (`id`), - CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) -); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql index 5a50b96d8f2a..f5bf04eab17e 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -203,3 +203,27 @@ SET `sort_key` = CASE ELSE `sort_key` END; -- End: Changes for Guest OS category cleanup + +-- Whitelabel GUI +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `uuid` varchar(255) UNIQUE, + `name` varchar(2048) NOT NULL COMMENT 'A name to identify the theme.', + `description` varchar(4096) DEFAULT NULL COMMENT 'A description for the theme.', + `css` text DEFAULT NULL COMMENT 'The CSS to be retrieved and imported into the GUI when matching the theme access configurations.', + `json_configuration` text DEFAULT NULL COMMENT 'The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.', + `recursive_domains` tinyint(1) DEFAULT 0 COMMENT 'Defines whether the subdomains of the informed domains are considered. Default value is false.', + `is_public` tinyint(1) default 1 COMMENT 'Defines whether a theme can be retrieved by anyone when only the `internet_domains_names` is informed. If the `domain_uuids` or `account_uuids` is informed, it is considered as `false`.', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `gui_theme_id` bigint(20) unsigned NOT NULL COMMENT 'Foreign key referencing the GUI theme on `gui_themes` table.', + `type` varchar(100) DEFAULT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', + `value` text COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', + PRIMARY KEY (`id`), + CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) +); diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 984e4490fbaa..1534b7f0cd91 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -207,7 +207,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; +import org.apache.cloudstack.gui.themes.GuiThemeJoin; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.BgpPeerVO; import org.apache.cloudstack.network.RoutedIpv4Manager; @@ -5554,25 +5554,25 @@ public void updateTemplateIsoResponsesForIcons(List responses, } @Override - public GuiThemeResponse createGuiThemeResponse(GuiThemeJoinVO guiThemeJoinVO) { + public GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin) { GuiThemeResponse guiThemeResponse = new GuiThemeResponse(); Long callerId = CallContext.current().getCallingAccount().getAccountId(); if (callerId != Account.ACCOUNT_ID_SYSTEM && _accountMgr.isRootAdmin(callerId)) { - guiThemeResponse.setId(guiThemeJoinVO.getUuid()); - guiThemeResponse.setName(guiThemeJoinVO.getName()); - guiThemeResponse.setDescription(guiThemeJoinVO.getDescription()); - guiThemeResponse.setCommonNames(guiThemeJoinVO.getCommonNames()); - guiThemeResponse.setDomainIds(guiThemeJoinVO.getDomains()); - guiThemeResponse.setRecursiveDomains(guiThemeJoinVO.isRecursiveDomains()); - guiThemeResponse.setAccountIds(guiThemeJoinVO.getAccounts()); - guiThemeResponse.setPublic(guiThemeJoinVO.getIsPublic()); - guiThemeResponse.setCreated(guiThemeJoinVO.getCreated()); - guiThemeResponse.setRemoved(guiThemeJoinVO.getRemoved()); - } - - guiThemeResponse.setJsonConfiguration(guiThemeJoinVO.getJsonConfiguration()); - guiThemeResponse.setCss(guiThemeJoinVO.getCss()); + guiThemeResponse.setId(guiThemeJoin.getUuid()); + guiThemeResponse.setName(guiThemeJoin.getName()); + guiThemeResponse.setDescription(guiThemeJoin.getDescription()); + guiThemeResponse.setCommonNames(guiThemeJoin.getCommonNames()); + guiThemeResponse.setDomainIds(guiThemeJoin.getDomains()); + guiThemeResponse.setRecursiveDomains(guiThemeJoin.isRecursiveDomains()); + guiThemeResponse.setAccountIds(guiThemeJoin.getAccounts()); + guiThemeResponse.setPublic(guiThemeJoin.getIsPublic()); + guiThemeResponse.setCreated(guiThemeJoin.getCreated()); + guiThemeResponse.setRemoved(guiThemeJoin.getRemoved()); + } + + guiThemeResponse.setJsonConfiguration(guiThemeJoin.getJsonConfiguration()); + guiThemeResponse.setCss(guiThemeJoin.getCss()); guiThemeResponse.setResponseName("guithemes"); return guiThemeResponse; diff --git a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImpl.java similarity index 96% rename from server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java rename to server/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImpl.java index 42aab9170e70..71c8b3c1a1e4 100644 --- a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.theme; +package org.apache.cloudstack.gui.themes; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; @@ -40,13 +40,9 @@ import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.theme.dao.GuiThemeDao; -import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDao; -import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao; -import org.apache.cloudstack.gui.themes.GuiThemeDetailsVO; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; -import org.apache.cloudstack.gui.themes.GuiThemeService; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.dao.GuiThemeDaoImpl; +import org.apache.cloudstack.gui.themes.dao.GuiThemeDetailsDaoImpl; +import org.apache.cloudstack.gui.themes.dao.GuiThemeJoinDaoImpl; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -73,13 +69,13 @@ public class GuiThemeServiceImpl implements GuiThemeService { private static final String PLUGINS = "plugins"; @Inject - GuiThemeDao guiThemeDao; + GuiThemeDaoImpl guiThemeDao; @Inject - GuiThemeDetailsDao guiThemeDetailsDao; + GuiThemeDetailsDaoImpl guiThemeDetailsDao; @Inject - GuiThemeJoinDao guiThemeJoinDao; + GuiThemeJoinDaoImpl guiThemeJoinDao; @Inject ResponseGenerator responseGenerator; @@ -109,8 +105,8 @@ public ListResponse listGuiThemes(ListGuiThemesCmd cmd) { } List guiThemeResponses = new ArrayList<>(); - for (GuiThemeJoinVO guiThemeJoinVO : result.first()) { - GuiThemeResponse guiThemeResponse = responseGenerator.createGuiThemeResponse(guiThemeJoinVO); + for (GuiThemeJoin guiThemeJoin : result.first()) { + GuiThemeResponse guiThemeResponse = responseGenerator.createGuiThemeResponse(guiThemeJoin); guiThemeResponses.add(guiThemeResponse); } @@ -131,7 +127,7 @@ private Pair, Integer> retrieveDefaultTheme() { @Override @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_CREATE, eventDescription = "Creating GUI theme") - public GuiThemeJoinVO createGuiTheme(CreateGuiThemeCmd cmd) { + public GuiThemeJoin createGuiTheme(CreateGuiThemeCmd cmd) { String name = cmd.getName(); String description = cmd.getDescription(); String css = cmd.getCss(); @@ -358,7 +354,7 @@ protected void validateObjectUuids(String providedIds, Class clazz) { @Override @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_UPDATE, eventDescription = "Updating GUI theme") - public GuiThemeJoinVO updateGuiTheme(UpdateGuiThemeCmd cmd) { + public GuiThemeJoin updateGuiTheme(UpdateGuiThemeCmd cmd) { Long guiThemeId = cmd.getId(); GuiThemeJoinVO guiThemeJoinVO = guiThemeJoinDao.findById(guiThemeId); diff --git a/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImplTest.java similarity index 97% rename from server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java rename to server/src/test/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImplTest.java index 4deec868a805..18aaa57bae62 100644 --- a/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImplTest.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.theme; +package org.apache.cloudstack.gui.themes; import com.cloud.user.Account; import com.cloud.utils.Pair; @@ -22,9 +22,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.themes.dao.GuiThemeJoinDaoImpl; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,7 +41,7 @@ public class GuiThemeServiceImplTest { @Mock - GuiThemeJoinDao guiThemeJoinDaoMock; + GuiThemeJoinDaoImpl guiThemeJoinDaoMock; @Mock GuiThemeJoinVO guiThemeJoinVOMock; From 6915fd63bef1e1fa0ed76a4d1b5b0bf09a2add39 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Mon, 5 Aug 2024 17:27:53 -0300 Subject: [PATCH 15/22] Normalize package name --- .../apache/cloudstack/api/ResponseGenerator.java | 2 +- .../gui/{themes => theme}/CreateGuiThemeCmd.java | 8 ++++---- .../gui/{themes => theme}/ListGuiThemesCmd.java | 6 +++--- .../gui/{themes => theme}/RemoveGuiThemeCmd.java | 6 +++--- .../gui/{themes => theme}/UpdateGuiThemeCmd.java | 8 ++++---- .../api/response/GuiThemeResponse.java | 2 +- .../gui/{themes => theme}/GuiTheme.java | 2 +- .../gui/{themes => theme}/GuiThemeDetails.java | 2 +- .../gui/{themes => theme}/GuiThemeJoin.java | 2 +- .../gui/{themes => theme}/GuiThemeService.java | 10 +++++----- .../gui/{themes => theme}/GuiThemeDetailsVO.java | 2 +- .../gui/{themes => theme}/GuiThemeJoinVO.java | 2 +- .../gui/{themes => theme}/GuiThemeVO.java | 2 +- .../{themes => theme}/dao/GuiThemeDaoImpl.java | 4 ++-- .../dao/GuiThemeDetailsDaoImpl.java | 6 +++--- .../dao/GuiThemeJoinDaoImpl.java | 4 ++-- .../java/com/cloud/api/ApiResponseHelper.java | 2 +- .../src/main/java/com/cloud/api/ApiServer.java | 2 +- .../src/main/java/com/cloud/api/ApiServlet.java | 2 +- .../com/cloud/server/ManagementServerImpl.java | 8 ++++---- .../{themes => theme}/GuiThemeServiceImpl.java | 16 ++++++++-------- .../GuiThemeServiceImplTest.java | 6 +++--- 22 files changed, 52 insertions(+), 52 deletions(-) rename api/src/main/java/org/apache/cloudstack/api/command/user/gui/{themes => theme}/CreateGuiThemeCmd.java (95%) rename api/src/main/java/org/apache/cloudstack/api/command/user/gui/{themes => theme}/ListGuiThemesCmd.java (96%) rename api/src/main/java/org/apache/cloudstack/api/command/user/gui/{themes => theme}/RemoveGuiThemeCmd.java (93%) rename api/src/main/java/org/apache/cloudstack/api/command/user/gui/{themes => theme}/UpdateGuiThemeCmd.java (96%) rename api/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiTheme.java (97%) rename api/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeDetails.java (96%) rename api/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeJoin.java (96%) rename api/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeService.java (77%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeDetailsVO.java (98%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeJoinVO.java (98%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeVO.java (99%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{themes => theme}/dao/GuiThemeDaoImpl.java (91%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{themes => theme}/dao/GuiThemeDetailsDaoImpl.java (97%) rename engine/schema/src/main/java/org/apache/cloudstack/gui/{themes => theme}/dao/GuiThemeJoinDaoImpl.java (98%) rename server/src/main/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeServiceImpl.java (97%) rename server/src/test/java/org/apache/cloudstack/gui/{themes => theme}/GuiThemeServiceImplTest.java (98%) diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index f092f5c1aa28..d0683299e73f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -151,7 +151,7 @@ import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; -import org.apache.cloudstack.gui.themes.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java similarity index 95% rename from api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java index 0f0c14e4c424..c34bde3a9d28 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/CreateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.api.command.user.gui.themes; +package org.apache.cloudstack.api.command.user.gui.theme; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -25,9 +25,9 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.GuiTheme; -import org.apache.cloudstack.gui.themes.GuiThemeJoin; -import org.apache.cloudstack.gui.themes.GuiThemeService; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; import javax.inject.Inject; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java similarity index 96% rename from api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java index 66024535350e..8fa549080bf1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/ListGuiThemesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.api.command.user.gui.themes; +package org.apache.cloudstack.api.command.user.gui.theme; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -25,8 +25,8 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.gui.themes.GuiTheme; -import org.apache.cloudstack.gui.themes.GuiThemeService; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; import javax.inject.Inject; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java similarity index 93% rename from api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java index 18713327cb51..dd2216865223 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/RemoveGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.api.command.user.gui.themes; +package org.apache.cloudstack.api.command.user.gui.theme; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -24,8 +24,8 @@ import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.GuiTheme; -import org.apache.cloudstack.gui.themes.GuiThemeService; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; import javax.inject.Inject; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java similarity index 96% rename from api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java index ec638a4da2b2..15e8c4d50ad5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/themes/UpdateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.api.command.user.gui.themes; +package org.apache.cloudstack.api.command.user.gui.theme; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -25,9 +25,9 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.GuiTheme; -import org.apache.cloudstack.gui.themes.GuiThemeJoin; -import org.apache.cloudstack.gui.themes.GuiThemeService; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; import javax.inject.Inject; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java index 771becdeb3d4..fe8a85b4176e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import org.apache.cloudstack.gui.themes.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; import java.util.Date; diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiTheme.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java similarity index 97% rename from api/src/main/java/org/apache/cloudstack/gui/themes/GuiTheme.java rename to api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java index 4ecb79433be5..0cca8bc2d7db 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiTheme.java +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetails.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java similarity index 96% rename from api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetails.java rename to api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java index ef9213c878f9..164535033d98 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetails.java +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import org.apache.cloudstack.api.InternalIdentity; diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoin.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java similarity index 96% rename from api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoin.java rename to api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java index e65491349df3..e54d53138ef6 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoin.java +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; diff --git a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java similarity index 77% rename from api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java rename to api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java index ce3b5bcf9ee4..8a066d3ffeca 100644 --- a/api/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeService.java +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java @@ -14,12 +14,12 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; -import org.apache.cloudstack.api.command.user.gui.themes.CreateGuiThemeCmd; -import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; -import org.apache.cloudstack.api.command.user.gui.themes.RemoveGuiThemeCmd; -import org.apache.cloudstack.api.command.user.gui.themes.UpdateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.UpdateGuiThemeCmd; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.ListResponse; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java similarity index 98% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java index 00d13da3ce03..046a19c59fa0 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeDetailsVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import javax.persistence.Column; import javax.persistence.Entity; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java similarity index 98% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java index 46bebde31599..2df23b3d1064 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeJoinVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import com.cloud.utils.db.GenericDao; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java similarity index 99% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java index 45b0c3d5894a..30d201f18b0b 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java similarity index 91% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDaoImpl.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java index 0af5d0750df7..4278c335d751 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java @@ -14,11 +14,11 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes.dao; +package org.apache.cloudstack.gui.theme.dao; import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.theme.GuiThemeVO; import org.springframework.stereotype.Component; @Component diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java similarity index 97% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDetailsDaoImpl.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java index 2349d94235a2..4c85c8c55863 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeDetailsDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes.dao; +package org.apache.cloudstack.gui.theme.dao; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -24,8 +24,8 @@ import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import org.apache.cloudstack.gui.themes.GuiThemeDetailsVO; -import org.apache.cloudstack.gui.themes.GuiThemeVO; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; +import org.apache.cloudstack.gui.theme.GuiThemeVO; import org.springframework.stereotype.Component; import javax.inject.Inject; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java similarity index 98% rename from engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeJoinDaoImpl.java rename to engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java index caf8cdd08126..6bbf256dc65f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/themes/dao/GuiThemeJoinDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes.dao; +package org.apache.cloudstack.gui.theme.dao; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; @@ -23,7 +23,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.gui.themes.GuiThemeJoinVO; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 1534b7f0cd91..7af412b79daf 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -207,7 +207,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.cloudstack.gui.themes.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.BgpPeerVO; import org.apache.cloudstack.network.RoutedIpv4Manager; diff --git a/server/src/main/java/com/cloud/api/ApiServer.java b/server/src/main/java/com/cloud/api/ApiServer.java index 5d26065e1c5a..f842b94c7851 100644 --- a/server/src/main/java/com/cloud/api/ApiServer.java +++ b/server/src/main/java/com/cloud/api/ApiServer.java @@ -87,7 +87,7 @@ import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; -import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; import org.apache.cloudstack.api.command.user.offering.ListDiskOfferingsCmd; import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; import org.apache.cloudstack.api.command.user.project.ListProjectInvitationsCmd; diff --git a/server/src/main/java/com/cloud/api/ApiServlet.java b/server/src/main/java/com/cloud/api/ApiServlet.java index 7d76e6dc3e7b..7359d1eeb542 100644 --- a/server/src/main/java/com/cloud/api/ApiServlet.java +++ b/server/src/main/java/com/cloud/api/ApiServlet.java @@ -44,7 +44,7 @@ import org.apache.cloudstack.api.auth.APIAuthenticationType; import org.apache.cloudstack.api.auth.APIAuthenticator; import org.apache.cloudstack.api.command.user.consoleproxy.CreateConsoleEndpointCmd; -import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.managed.context.ManagedContext; import org.apache.cloudstack.utils.consoleproxy.ConsoleAccessUtils; diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 5345da6d4d35..45001c896f69 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -400,10 +400,10 @@ import org.apache.cloudstack.api.command.user.firewall.UpdatePortForwardingRuleCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCategoriesCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCmd; -import org.apache.cloudstack.api.command.user.gui.themes.CreateGuiThemeCmd; -import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; -import org.apache.cloudstack.api.command.user.gui.themes.RemoveGuiThemeCmd; -import org.apache.cloudstack.api.command.user.gui.themes.UpdateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.UpdateGuiThemeCmd; import org.apache.cloudstack.api.command.user.iso.AttachIsoCmd; import org.apache.cloudstack.api.command.user.iso.CopyIsoCmd; import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; diff --git a/server/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java similarity index 97% rename from server/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImpl.java rename to server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java index 71c8b3c1a1e4..4ce45b2a0977 100644 --- a/server/src/main/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; @@ -33,16 +33,16 @@ import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import org.apache.cloudstack.api.ResponseGenerator; -import org.apache.cloudstack.api.command.user.gui.themes.CreateGuiThemeCmd; -import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; -import org.apache.cloudstack.api.command.user.gui.themes.RemoveGuiThemeCmd; -import org.apache.cloudstack.api.command.user.gui.themes.UpdateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.UpdateGuiThemeCmd; import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.dao.GuiThemeDaoImpl; -import org.apache.cloudstack.gui.themes.dao.GuiThemeDetailsDaoImpl; -import org.apache.cloudstack.gui.themes.dao.GuiThemeJoinDaoImpl; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDaoImpl; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDaoImpl; +import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDaoImpl; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/server/src/test/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java similarity index 98% rename from server/src/test/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImplTest.java rename to server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java index 18aaa57bae62..c47fdadd32c7 100644 --- a/server/src/test/java/org/apache/cloudstack/gui/themes/GuiThemeServiceImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java @@ -14,15 +14,15 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.gui.themes; +package org.apache.cloudstack.gui.theme; import com.cloud.user.Account; import com.cloud.utils.Pair; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; -import org.apache.cloudstack.api.command.user.gui.themes.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.themes.dao.GuiThemeJoinDaoImpl; +import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDaoImpl; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; From a79a546b1b7fe756dcd2fd87d984c7001f073fbc Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Mon, 5 Aug 2024 17:40:30 -0300 Subject: [PATCH 16/22] Address Henrique review --- .../cloudstack/gui/theme/GuiThemeVO.java | 4 ++-- .../META-INF/db/schema-42010to42100.sql | 4 ++-- ui/public/css/apache-theme.css | 14 +++++++------- ui/public/css/dark-theme.css | 18 +++++++++--------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java index 30d201f18b0b..887e3886f6c6 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java @@ -47,10 +47,10 @@ public class GuiThemeVO implements GuiTheme { @Column(name = "description", length = 4096) private String description; - @Column(name = "css", nullable = false, length = 65535) + @Column(name = "css", length = 65535) private String css; - @Column(name = "json_configuration", nullable = false, length = 65535) + @Column(name = "json_configuration", length = 65535) private String jsonConfiguration; @Column(name = "is_public") diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql index f5bf04eab17e..817a58a271ca 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -222,8 +222,8 @@ CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( `id` bigint(20) unsigned NOT NULL auto_increment, `gui_theme_id` bigint(20) unsigned NOT NULL COMMENT 'Foreign key referencing the GUI theme on `gui_themes` table.', - `type` varchar(100) DEFAULT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', - `value` text COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', + `type` varchar(100) DEFAULT NOT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', + `value` text NOT NULL COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', PRIMARY KEY (`id`), CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) ); diff --git a/ui/public/css/apache-theme.css b/ui/public/css/apache-theme.css index ad4e079db057..573c062c5138 100644 --- a/ui/public/css/apache-theme.css +++ b/ui/public/css/apache-theme.css @@ -157,7 +157,7 @@ html{ background-color:var(--main-secondary-claro) !important; } -/* BARRA LATERAL */ +/* SIDE BAR */ .vm-info-card .ant-card-body .ant-card-bordered{ border-top:2px solid var(--main-primary-escuro) !important; @@ -177,7 +177,7 @@ html{ font-weight:900; } -/* TELA USER LOGIN */ +/* USER LOGIN SCREEN */ .ant-tabs-tab, .user-layout{ @@ -272,7 +272,7 @@ html{ background-color:var(--main-primary-claro) !important; } -/* PAGINACAO */ +/* PAGINATION */ .ant-pagination .ant-pagination-item.ant-pagination-item-active{ background-color:var(--main-primary-escuro) !important; border-color:#fff; @@ -739,7 +739,7 @@ button.ant-btn:not(.ant-btn-icon-only):hover{ color:#fff !important; } -/* GRAFICOS */ +/* CHARTS */ .ant-progress .ant-progress-status-normal path{ stroke:var(--main-exception) !important; @@ -769,7 +769,7 @@ button.ant-btn:not(.ant-btn-icon-only):hover{ font-weight: bold } -/* lISTAS TABELADAS */ +/* LIST TABLES */ .ant-list .ant-list-item{ border-left:5px solid transparent !important; padding-left:10px; @@ -798,7 +798,7 @@ button.ant-btn:not(.ant-btn-icon-only):hover{ background-color:var(--main-secondary-escuro) !important; } -/* OUTROS */ +/* OTHERS */ .ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected{ background-color:var(--main-primary-claro) @@ -875,7 +875,7 @@ button.ant-btn:not(.ant-btn-icon-only):hover{ transform: scale(1.3); } -/* CADASTROS FORMS E ADDS */ +/* REGISTRATIONS FORMS AND ADDS */ .ant-steps-item-tail{ margin-top:15px; } diff --git a/ui/public/css/dark-theme.css b/ui/public/css/dark-theme.css index b548722533ee..3af275b966f0 100644 --- a/ui/public/css/dark-theme.css +++ b/ui/public/css/dark-theme.css @@ -100,7 +100,7 @@ color:#000 !important; } -/* USER MMENU DARK */ +/* USER MENU DARK */ *{ font-family: 'Poppins' , Courier !important; } @@ -156,7 +156,7 @@ html{ background-color:var(--main-secondary-claro) !important; } -/* BARRA LATERAL */ +/* SIDE BAR */ .ant-card { background-color: var(--main-linear-cora) !important; } @@ -179,7 +179,7 @@ html{ font-weight:900; } -/* TELA USER LOGIN */ +/* USER LOGIN SCREEN */ .ant-tabs-tab, .user-layout{ @@ -284,7 +284,7 @@ html{ background-color:var(--main-primary-claro) !important; } -/* PAGINACAO */ +/* PAGINATION */ .ant-pagination .ant-pagination-item.ant-pagination-item-active{ background-color:var(--main-primary-escuro) !important; border-color:#fff; @@ -767,7 +767,7 @@ button.ant-btn:hover{ color:#fff !important; } -/* GRAFICOS */ +/* CHARTS */ .ant-progress .ant-progress-status-normal path{ stroke:var(--main-exception) !important; @@ -797,7 +797,7 @@ button.ant-btn:hover{ font-weight: bold } -/* lISTAS TABELADAS */ +/* LIST TABLES */ .ant-list .ant-list-item{ border-left:5px solid transparent !important; padding-left:10px; @@ -832,7 +832,7 @@ button.ant-btn:hover{ background-color:var(--main-secondary-escuro) !important; } -/* OUTROS */ +/* OTHERS */ .ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected{ background-color:var(--main-primary-claro) @@ -909,7 +909,7 @@ button.ant-btn:hover{ transform: scale(1.3); } -/* CADASTROS FORMS E ADDS */ +/* REGISTRATIONS FORMS AND ADDS */ .ant-steps-item-tail{ margin-top:15px; } @@ -1535,7 +1535,7 @@ button.ant-btn.ant-btn-dangerous:not(.ant-btn-icon-only) } -/* SOMENTE DARK */ +/* DARK ONLY */ .ant-modal-body .ant-table-bordered .ant-table-body .ant-table-row-cell-break-word .ant-btn-circle.ant-btn-dangerous span{ background-color: transparent; margin-right:5px !important; From 1a547d739dfd244221ae44ee365a3e77a528ec7f Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Fri, 23 Aug 2024 16:47:27 -0300 Subject: [PATCH 17/22] Fix create table SQL --- .../src/main/resources/META-INF/db/schema-42010to42100.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql index 817a58a271ca..53c2bbf60cce 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -222,7 +222,7 @@ CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( `id` bigint(20) unsigned NOT NULL auto_increment, `gui_theme_id` bigint(20) unsigned NOT NULL COMMENT 'Foreign key referencing the GUI theme on `gui_themes` table.', - `type` varchar(100) DEFAULT NOT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', + `type` varchar(100) NOT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', `value` text NOT NULL COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', PRIMARY KEY (`id`), CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) From dd8233ff7f60efc5ea5e072bdaa8181650d1c6bf Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Fri, 6 Sep 2024 14:18:30 -0300 Subject: [PATCH 18/22] Add interface for Dao classes --- .../cloudstack/gui/theme/dao/GuiThemeDao.java | 24 ++++++++++++++ .../gui/theme/dao/GuiThemeDaoImpl.java | 3 +- .../gui/theme/dao/GuiThemeDetailsDao.java | 30 ++++++++++++++++++ .../gui/theme/dao/GuiThemeDetailsDaoImpl.java | 5 ++- .../gui/theme/dao/GuiThemeJoinDao.java | 31 +++++++++++++++++++ .../gui/theme/dao/GuiThemeJoinDaoImpl.java | 5 ++- .../gui/theme/GuiThemeServiceImpl.java | 14 +++++---- 7 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java new file mode 100644 index 000000000000..c00aedcc7866 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeVO; + +public interface GuiThemeDao extends GenericDao { + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java index 4278c335d751..bc58c5f80f30 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java @@ -16,11 +16,10 @@ // under the License. package org.apache.cloudstack.gui.theme.dao; -import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; import org.apache.cloudstack.gui.theme.GuiThemeVO; import org.springframework.stereotype.Component; @Component -public class GuiThemeDaoImpl extends GenericDaoBase implements GenericDao { +public class GuiThemeDaoImpl extends GenericDaoBase implements GuiThemeDao { } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java new file mode 100644 index 000000000000..af243b1ffa46 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; + +import java.util.List; + +public interface GuiThemeDetailsDao extends GenericDao { + List listGuiThemeIdsByCommonName(String commonName); + + List listGuiThemeIdsByDomainUuids(String domainUuid); + + void expungeByGuiThemeId(long guiThemeId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java index 4c85c8c55863..b0969833eb01 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java @@ -18,7 +18,6 @@ import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; -import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -33,13 +32,13 @@ import java.util.List; @Component -public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GenericDao { +public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GuiThemeDetailsDao { @Inject DomainDao domainDao; @Inject - GuiThemeDaoImpl guiThemeDao; + GuiThemeDao guiThemeDao; public List listGuiThemeIdsByCommonName(String commonName) { GenericSearchBuilder detailsDaoSearchBuilder = createSearchBuilder(Long.class); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java new file mode 100644 index 000000000000..740199cfca73 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF 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.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; + +import java.util.List; + +public interface GuiThemeJoinDao extends GenericDao { + GuiThemeJoinVO findDefaultTheme(); + + Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); + + Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, Boolean showPublic); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java index 6bbf256dc65f..ce6f70558128 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java @@ -18,7 +18,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; -import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -33,9 +32,9 @@ import java.util.List; @Component -public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GenericDao { +public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GuiThemeJoinDao { @Inject - GuiThemeDetailsDaoImpl guiThemeDetailsDao; + GuiThemeDetailsDao guiThemeDetailsDao; public static final Long INVALID_ID = -1L; diff --git a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java index 4ce45b2a0977..78bcb6bbc037 100644 --- a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java @@ -40,12 +40,13 @@ import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.gui.theme.dao.GuiThemeDaoImpl; -import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDaoImpl; -import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDaoImpl; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDao; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDao; +import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.springframework.stereotype.Component; import javax.inject.Inject; import java.util.ArrayList; @@ -54,6 +55,7 @@ import java.util.Map; import java.util.Set; +@Component public class GuiThemeServiceImpl implements GuiThemeService { protected Logger logger = LogManager.getLogger(getClass()); @@ -69,13 +71,13 @@ public class GuiThemeServiceImpl implements GuiThemeService { private static final String PLUGINS = "plugins"; @Inject - GuiThemeDaoImpl guiThemeDao; + GuiThemeDao guiThemeDao; @Inject - GuiThemeDetailsDaoImpl guiThemeDetailsDao; + GuiThemeDetailsDao guiThemeDetailsDao; @Inject - GuiThemeJoinDaoImpl guiThemeJoinDao; + GuiThemeJoinDao guiThemeJoinDao; @Inject ResponseGenerator responseGenerator; From 17fade48bc7bdec345e2956245b84df6200bd814 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 10 Sep 2024 09:52:46 -0300 Subject: [PATCH 19/22] Remove extra tabs --- server/src/main/java/com/cloud/api/ApiResponseHelper.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 7af412b79daf..15d8c7309d4a 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -5517,8 +5517,7 @@ public SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS share SharedFSJoinVO sharedFSView = ApiDBUtils.newSharedFSView(sharedFS); return ApiDBUtils.newSharedFSResponse(view, sharedFSView); } - - protected Map getResourceIconsUsingOsCategory(List responses) { +protected Map getResourceIconsUsingOsCategory(List responses) { Set guestOsCategoryIds = responses.stream().map(TemplateResponse::getOsTypeCategoryId).collect(Collectors.toSet()); Map guestOsCategoryIcons = resourceIconManager.getByResourceTypeAndIds(ResourceTag.ResourceObjectType.GuestOsCategory, @@ -5552,7 +5551,6 @@ public void updateTemplateIsoResponsesForIcons(List responses, response.setResourceIconResponse(iconResponse); } } - @Override public GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin) { GuiThemeResponse guiThemeResponse = new GuiThemeResponse(); From b26c8d54a2c0406c4a1b4fc567ec57a9ab60ae41 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Wed, 26 Feb 2025 22:52:21 +0100 Subject: [PATCH 20/22] Address unauthorized call when 2FA is enabled --- ui/src/store/modules/user.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index 6998abea290d..5ff8766fa9e2 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -205,7 +205,7 @@ const user = { }, Login ({ commit }, userInfo) { return new Promise((resolve, reject) => { - login(userInfo).then(async response => { + login(userInfo).then(response => { const result = response.loginresponse || {} Cookies.set('account', result.account, { expires: 1 }) Cookies.set('domainid', result.domainid, { expires: 1 }) @@ -247,11 +247,6 @@ const user = { const latestVersion = vueProps.$localStorage.get(LATEST_CS_VERSION, { version: '', fetchedTs: 0 }) commit('SET_LATEST_VERSION', latestVersion) notification.destroy() - - await api('listUsers', { userid: result.userid }).then(async response => { - await applyCustomGuiTheme(response.listusersresponse.user[0].accountid, result.domainid) - }) - resolve() }).catch(error => { reject(error) @@ -414,6 +409,7 @@ const user = { api('listUsers', { id: Cookies.get('userid'), showicon: true }).then(response => { const result = response.listusersresponse.user[0] + applyCustomGuiTheme(result.accountid, result.domainid) commit('SET_INFO', result) commit('SET_NAME', result.firstname + ' ' + result.lastname) commit('SET_AVATAR', result.icon?.base64image || '') From 6374971c6776a69a1c7d470624f98086f32a7aa7 Mon Sep 17 00:00:00 2001 From: Henrique Sato Date: Mon, 7 Jul 2025 13:27:17 -0300 Subject: [PATCH 21/22] Remove trailing whitespaces --- ui/src/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/main.js b/ui/src/main.js index cfce3f0de77e..c25ab1066d4e 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -104,14 +104,14 @@ fetch('config.json?ts=' + Date.now()) const userid = Cookies.get('userid') let accountid = null let domainid = null - + if (userid !== undefined && Cookies.get('sessionkey')) { await api('listUsers', { userid: userid }).then(response => { accountid = response.listusersresponse.user[0].accountid domainid = response.listusersresponse.user[0].domainid }) } - + await applyCustomGuiTheme(accountid, domainid) loadLanguageAsync().then(() => { From c5f3efd56c38406f4c3e2b70d279dcb4e974b5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Mon, 14 Jul 2025 09:46:51 -0300 Subject: [PATCH 22/22] Apply suggestions from code review Co-authored-by: Suresh Kumar Anaparti --- .../api/command/user/gui/theme/CreateGuiThemeCmd.java | 2 +- .../cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java | 2 +- .../api/command/user/gui/theme/RemoveGuiThemeCmd.java | 2 +- .../api/command/user/gui/theme/UpdateGuiThemeCmd.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java index c34bde3a9d28..8566b413cc12 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java @@ -33,7 +33,7 @@ @APICommand(name = "createGuiTheme", description = "Creates a customized GUI theme for a set of Common Names (fixed or wildcard), a set of domain UUIDs, and/or a set of " + "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, - since = "4.20.0.0", authorized = {RoleType.Admin}) + since = "4.21.0.0", authorized = {RoleType.Admin}) public class CreateGuiThemeCmd extends BaseCmd { @Inject diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java index 8fa549080bf1..35a0a749aa94 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java @@ -31,7 +31,7 @@ import javax.inject.Inject; @APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, - since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, RoleType.ResourceAdmin}) public class ListGuiThemesCmd extends BaseListCmd { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java index dd2216865223..64164838eba4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java @@ -30,7 +30,7 @@ import javax.inject.Inject; @APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, - since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) public class RemoveGuiThemeCmd extends BaseCmd { @Inject diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java index 15e8c4d50ad5..daef2235ce89 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java @@ -33,7 +33,7 @@ @APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, - since = "4.20.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) public class UpdateGuiThemeCmd extends BaseCmd { @Inject