Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,5 @@ bin
/sormas-e2e-tests/logs

.DS_Store

/.run-configs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ public interface Validations {
String systemConfigurationValueInvalidKey = "systemConfigurationValueInvalidKey";
String systemConfigurationValueInvalidValue = "systemConfigurationValueInvalidValue";
String systemConfigurationValuePatternNotMatched = "systemConfigurationValuePatternNotMatched";
String systemConfigurationValueValidationInvalidBackgroundColor = "systemConfigurationValueValidationInvalidBackgroundColor";
String systemConfigurationValueValidationMenuSubtitle = "systemConfigurationValueValidationMenuSubtitle";
String systemConfigurationValueValidationNotADirectory = "systemConfigurationValueValidationNotADirectory";
String systemConfigurationValueValidationNotAEmail = "systemConfigurationValueValidationNotAEmail";
String systemConfigurationValueValidationNotAFile = "systemConfigurationValueValidationNotAFile";
Expand Down
4 changes: 3 additions & 1 deletion sormas-api/src/main/resources/validations.properties
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,6 @@ systemConfigurationValueValidationNotAEmail = Value is not a valid email address
systemConfigurationValueValidationNotAValidEmailsenderName = Value is not a valid email sender name. Name should contain only alphabets.
systemConfigurationValueValidationNotAValidSmsSenderName = Value is not a valid name. Name should contain only letters and numbers without spaces & special characters. For more info please see https://developer.vonage.com/en/messaging/sms/guides/custom-sender-id .
smsAuthKeyValueValidation = SMS Auth key value is not valid
smsAuthSecretValueValidation = SMS Auth secret value is not valid
smsAuthSecretValueValidation = SMS Auth secret value is not valid
systemConfigurationValueValidationInvalidBackgroundColor = Pre-defined values are: green, red, indigo, gray, default (case-sensitive) otherwise must match hexadecimal format, example: #dd2b0e or #4AA
systemConfigurationValueValidationMenuSubtitle = Can be empty or up to 16 (any) characters. Can be used to define name of the environment: PRODUCTION - TEST etc.
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,8 @@ private void updateDatabase(EntityManager entityManager, String schemaFileName)
databaseVersion = null;
}

logger.info("Current databaseVersion: [{}]", databaseVersion);

try (InputStream schemaStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(schemaFileName);
Scanner scanner = new Scanner(schemaStream, StandardCharsets.UTF_8.name())) {
StringBuilder nextUpdateBuilder = new StringBuilder();
Expand Down
56 changes: 55 additions & 1 deletion sormas-backend/src/main/resources/sql/sormas_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -15062,4 +15062,58 @@ ALTER TABLE testreport_history ADD COLUMN serotype character varying(255);
ALTER TABLE testreport_history ADD COLUMN straincallstatus character varying(255);

INSERT INTO schema_version (version_number, comment) VALUES (602, 'External message additional fields');
-- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. ***

ALTER TABLE systemconfigurationvalue ADD CONSTRAINT uk_systemconfigurationvalue_config_key UNIQUE (config_key);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check the exact line and surrounding context around line 15066
sed -n '15050,15100p' sormas-backend/src/main/resources/sql/sormas_schema.sql

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 2625


🏁 Script executed:

# Search for schema version 603 and the complete migration block
rg -n "603|systemconfigurationvalue" sormas-backend/src/main/resources/sql/sormas_schema.sql -A 5 -B 5 | tail -150

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 8165


🏁 Script executed:

# Check if there are any precondition checks or duplicate handling before this constraint
rg -n "SELECT.*config_key|DELETE.*systemconfigurationvalue|TRUNCATE.*systemconfigurationvalue" sormas-backend/src/main/resources/sql/sormas_schema.sql

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 126


🏁 Script executed:

# Look at the complete migration block for schema version 603
rg -n "-- schema version 603|603" sormas-backend/src/main/resources/sql/sormas_schema.sql -A 50 | head -100

Repository: SORMAS-Foundation/SORMAS-Project

Length of output: 503


Add cleanup before the unique constraint to prevent migration failures.

The ALTER TABLE ADD CONSTRAINT on line 15066 will fail if duplicate config_key values exist in production. Prior schema migrations (version 569) demonstrate the correct pattern: DELETE duplicate rows before adding the constraint. Add DELETE FROM systemconfigurationvalue WHERE config_key IN ('MENU_BACKGROUND_COLOR', 'MENU_SUBTITLE'); before the constraint addition, consistent with the established migration pattern for configuration updates.

🤖 Prompt for AI Agents
In `@sormas-backend/src/main/resources/sql/sormas_schema.sql` at line 15066,
Before adding the unique constraint uk_systemconfigurationvalue_config_key on
systemconfigurationvalue(config_key), add a cleanup step that deletes any
existing duplicate rows for the specific keys 'MENU_BACKGROUND_COLOR' and
'MENU_SUBTITLE' (i.e., remove rows where config_key is in that set) so the ALTER
TABLE ADD CONSTRAINT will not fail; place this DELETE immediately before the
ALTER TABLE statement in the migration.


DO
$$
DECLARE
general_configuration_id bigint;

BEGIN
-- Check if the category 'GENERAL_CATEGORY' already exists
SELECT id
INTO general_configuration_id
FROM systemconfigurationcategory
WHERE name = 'GENERAL_CATEGORY';

-- Insert the general category if it doesn't exist
IF
general_configuration_id IS NULL THEN
INSERT INTO systemconfigurationcategory(id, uuid, changedate, creationdate, name, caption, description)
VALUES (nextval('entity_seq'), generate_base32_uuid(), now(), now(), 'GENERAL_CATEGORY', 'i18n/General/categoryGeneral',
'i18n/General/categoryGeneral')
RETURNING id INTO general_configuration_id;
END IF;

-- Insert new configurations if missing
-- Background color
INSERT INTO systemconfigurationvalue(config_key, config_value, category_id, value_optional, value_pattern,
value_encrypt, data_provider, validation_message, changedate, creationdate,
id,
uuid)
VALUES ('MENU_BACKGROUND_COLOR', 'default', general_configuration_id, true,
'^(default|red|green|indigo|gray)|(#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}))$', false,
null,
'i18n/systemConfigurationValueValidationInvalidBackgroundColor', now(), now(), nextval('entity_seq'),
generate_base32_uuid())
ON CONFLICT (config_key) DO NOTHING;

-- Menu Subtitle
INSERT INTO systemconfigurationvalue(config_key, config_value, category_id, value_optional, value_pattern,
value_encrypt, data_provider, validation_message, changedate, creationdate,
id,
uuid)
VALUES ('MENU_SUBTITLE', '', general_configuration_id, true,
'^\w{0,16}$', false,
null,
'i18n/systemConfigurationValueValidationMenuSubtitle', now(), now(), nextval('entity_seq'),
generate_base32_uuid())
Comment on lines +15107 to +15111
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

MENU_SUBTITLE pattern may be too restrictive for display text.

The pattern ^\w{0,16}$ only allows alphanumeric characters and underscores (no spaces, hyphens, or other punctuation). For a subtitle displayed in the UI banner, this prevents values like "Test System", "DEV-01", or "Stage 1".

If this is intentional (e.g., for a short identifier), please disregard. Otherwise, consider a more permissive pattern:

Suggested pattern allowing spaces and common punctuation
-        VALUES ('MENU_SUBTITLE', '', general_configuration_id, true,
-                '^\w{0,16}$', false,
+        VALUES ('MENU_SUBTITLE', '', general_configuration_id, true,
+                '^[\w\s\-\.]{0,16}$', false,

This allows word characters, spaces, hyphens, and periods up to 16 characters.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VALUES ('MENU_SUBTITLE', '', general_configuration_id, true,
'^\w{0,16}$', false,
null,
'i18n/systemConfigurationValueValidationMenuSubtitle', now(), now(), nextval('entity_seq'),
generate_base32_uuid())
VALUES ('MENU_SUBTITLE', '', general_configuration_id, true,
'^[\w\s\-\.]{0,16}$', false,
null,
'i18n/systemConfigurationValueValidationMenuSubtitle', now(), now(), nextval('entity_seq'),
generate_base32_uuid())
🤖 Prompt for AI Agents
In `@sormas-backend/src/main/resources/sql/sormas_schema.sql` around lines 15107 -
15111, The MENU_SUBTITLE validation regex in the VALUES tuple currently only
allows word characters and is too restrictive for UI subtitles; update the regex
used there (replace the existing ^\w{0,16}$) with a permissive pattern that
permits word characters plus spaces, hyphens and periods up to 16 characters
(i.e., use a character class containing \w, space, hyphen and dot and the same
length limit) so values like "Test System", "DEV-01" or "Stage 1" are accepted.

ON CONFLICT (config_key) DO NOTHING;

END
$$
LANGUAGE plpgsql;

INSERT INTO schema_version (version_number, comment) VALUES (603, 'System configuration: to visually distinguish different system types');
-- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. ***
110 changes: 96 additions & 14 deletions sormas-ui/src/main/java/de/symeda/sormas/ui/Menu.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/*******************************************************************************
* SORMAS® - Surveillance Outbreak Response Management & Analysis System
* Copyright © 2016-2018 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
*
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
Expand All @@ -22,12 +22,15 @@
import java.util.HashMap;
import java.util.Map;

import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.StringUtils;

import com.vaadin.icons.VaadinIcons;
import com.vaadin.navigator.Navigator;
import com.vaadin.navigator.View;
import com.vaadin.server.FileResource;
import com.vaadin.server.Page;
import com.vaadin.server.Resource;
import com.vaadin.server.ThemeResource;
import com.vaadin.ui.Alignment;
Expand All @@ -39,6 +42,7 @@
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.Command;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.ValoTheme;

Expand All @@ -57,30 +61,47 @@
/**
* Responsive navigation menu presenting a list of available views to the user.
*/
@SuppressWarnings("serial")
public class Menu extends CssLayout {

private static final String VALO_MENUITEMS = "valo-menuitems";
private static final String VALO_MENU_TOGGLE = "valo-menu-toggle";
private static final String VALO_MENU_VISIBLE = "valo-menu-visible";
private Navigator navigator;
private Map<String, Button> viewButtons = new HashMap<String, Button>();

private CssLayout menuItemsLayout;
private CssLayout menuPart;
private static final String BACKGROUND_COLOR_CONFIG_KEY = "MENU_BACKGROUND_COLOR";
private static final String MENU_SUBTITLE_CONFIG_KEY = "MENU_SUBTITLE";

private static final String DEFAULT_BACKGROUND_COLOR_NAME = "default";

private static final String COLOR_BACKGROUND_STYLE_NAME = "color-background";
private static final String MENU_SUBTITLE_STYLE_NAME = "menu-subtitle";
private static final String TOP_MENU_CONTAINER_STYLE_NAME = "top-menu-container";

private final Navigator navigator;
private final Map<String, Button> viewButtons = new HashMap<>();

private final CssLayout menuItemsLayout;
private final CssLayout menuPart;

public Menu(Navigator navigator) {

this.navigator = navigator;
setPrimaryStyleName(ValoTheme.MENU_ROOT);

defineCustomColorBackgroundIfSystemConfigured();

menuPart = new CssLayout();
menuPart.addStyleName(ValoTheme.MENU_PART);

// header of the menu
final HorizontalLayout top = new HorizontalLayout();
VerticalLayout topContainer = new VerticalLayout();
topContainer.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
CssStyles.style(topContainer, ValoTheme.MENU_TITLE, TOP_MENU_CONTAINER_STYLE_NAME);
topContainer.setSpacing(false);

HorizontalLayout top = new HorizontalLayout();
top.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
top.addStyleName(ValoTheme.MENU_TITLE);
top.setSpacing(true);

Label title = new Label(FacadeProvider.getConfigFacade().getSormasInstanceName());
title.setSizeUndefined();

Expand All @@ -95,8 +116,13 @@ public Menu(Navigator navigator) {
CssStyles.style(image, ValoTheme.MENU_LOGO, ValoTheme.BUTTON_LINK);
top.addComponent(image);
top.addComponent(title);
top.addLayoutClickListener(listener -> SormasUI.get().getNavigator().navigateTo(SurveillanceDashboardView.VIEW_NAME));
menuPart.addComponent(top);

topContainer.addLayoutClickListener(listener -> SormasUI.get().getNavigator().navigateTo(SurveillanceDashboardView.VIEW_NAME));
topContainer.addComponent(top);

defineMenuSubtitleIfSystemConfigured(topContainer);

menuPart.addComponent(topContainer);

// button for toggling the visibility of the menu when on a small screen
final Button showMenu = ButtonHelper.createIconButton(Captions.menu, VaadinIcons.MENU, event -> {
Expand Down Expand Up @@ -136,14 +162,70 @@ public Menu(Navigator navigator) {
addComponent(menuPart);
}

private void defineCustomColorBackgroundIfSystemConfigured() {
String backgroundColor = FacadeProvider.getSystemConfigurationValueFacade().getValue(BACKGROUND_COLOR_CONFIG_KEY);

if (StringUtils.isBlank(backgroundColor) || DEFAULT_BACKGROUND_COLOR_NAME.equals(backgroundColor)) {
// no need to configure anything as we can keep the default color.
return;
}

addStyleName(COLOR_BACKGROUND_STYLE_NAME);

String actualColorBackgroundColor = determineActualColor(backgroundColor);

Page.Styles styles = Page.getCurrent().getStyles();
// specifying !important as inline CSS has lower precedence
styles.add(
".valo-menu-color-background {\n" + " background-color: " + actualColorBackgroundColor + " !important;\n"
+ " background-image: -webkit-linear-gradient(right, " + actualColorBackgroundColor + " 0%, #EEFA5F" + actualColorBackgroundColor
+ " 8px) !important;\n" + " background-image: linear-gradient(to left, " + actualColorBackgroundColor + " 0%, "
+ actualColorBackgroundColor + " 8px) !important;" + " }");
}

private static void defineMenuSubtitleIfSystemConfigured(VerticalLayout topContainer) {
String menuSubtitle = FacadeProvider.getSystemConfigurationValueFacade().getValue(MENU_SUBTITLE_CONFIG_KEY);

if (StringUtils.isBlank(menuSubtitle)) {
// no need to configure anything as no subtitled must be added
return;
}

Label systemConfigurationLabel = new Label(menuSubtitle);
systemConfigurationLabel.setSizeUndefined();
CssStyles.style(systemConfigurationLabel, CssStyles.LABEL_UNDERLINE, MENU_SUBTITLE_STYLE_NAME);

topContainer.addComponent(systemConfigurationLabel);
}

private static @NotNull String determineActualColor(String backgroundColor) {
String actualColorBackgroundColor;
switch (backgroundColor) {
case "green":
actualColorBackgroundColor = "#108548";
break;
case "red":
actualColorBackgroundColor = "#dd2b0e";
break;
case "indigo":
actualColorBackgroundColor = "#7b58cf";
break;
case "gray":
actualColorBackgroundColor = "#737278";
break;
default:
actualColorBackgroundColor = backgroundColor;
}
return actualColorBackgroundColor;
}

private void showSettingsPopup() {

Window window = VaadinUiUtil.createPopupWindow();
window.setCaption(I18nProperties.getString(Strings.headingUserSettings));
window.setModal(true);

CommitDiscardWrapperComponent<UserSettingsForm> component =
ControllerProvider.getUserController().getUserSettingsComponent(() -> window.close());
CommitDiscardWrapperComponent<UserSettingsForm> component = ControllerProvider.getUserController().getUserSettingsComponent(window::close);

window.setContent(component);
UI.getCurrent().addWindow(window);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ private CssStyles() {
public static final String LABEL_BOLD = "bold";
public static final String LABEL_UPPERCASE = "uppercase";
public static final String LABEL_ITALIC = "italic";
public static final String LABEL_UNDERLINE = "underline";

// Label styles
public static final String LABEL_BOTTOM_LINE = "bottom-line";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ $valo-menu-background-color: $s-default-color !default;
&::before {
position: absolute;
top: -12px;
right: 0px;
right: 0;
width: 4px;
height: 74px;
background-color: $v-selection-color;
background-color: $v-selection-color;
content: "";
}
}
}

.v-menubar-user-menu {
margin: 8px 0px;
margin: 8px 0;
text-align: left;
}
}
Expand Down Expand Up @@ -124,5 +124,13 @@ $valo-menu-background-color: $s-default-color !default;
.valo-menu .v-menubar-user-menu .v-menubar-menuitem-caption {
width: auto;
}
}
}

.top-menu-container {
max-height: 115px;
}

.menu-subtitle {
font-size: 24px;
}
}
4 changes: 4 additions & 0 deletions sormas-ui/src/main/webapp/VAADIN/themes/sormas/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -530,4 +530,8 @@
background-color: #FFFFFF !important;
border-radius: 24px;
}

.underline {
text-decoration: underline;
}
}
Loading