Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

Expand All @@ -30,6 +32,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;

import org.apache.commons.lang3.StringUtils;

import org.exoplatform.portal.config.model.Application;
import org.exoplatform.portal.config.model.ApplicationBackgroundStyle;
import org.exoplatform.portal.config.model.ApplicationState;
Expand Down Expand Up @@ -132,6 +136,14 @@ public class LayoutModel {

private String appBackgroundAttachment;

private Integer appMarginTop;

private Integer appMarginRight;

private Integer appMarginBottom;

private Integer appMarginLeft;

private String textTitleColor;

private String textTitleFontSize;
Expand Down Expand Up @@ -272,6 +284,42 @@ public LayoutModel(ModelObject model, PortletInstanceService portletInstanceServ
init(model, portletInstanceService, portletInstanceContext);
}

/**
* Application margins are custom properties on the platform scale where 20
* means "no extra margin". An application whose cssClass still carries
* Vuetify spacing tokens in their cssClass, written either by
* the editors from a 0-neutral attribute or by ModelStyle at import from a
* 20-neutral page-XML attribute: the two attributes disagree, the tokens do
* not (mt-n1 rendered -4px in both). The token is therefore the source of a
* legacy margin: value = N x 4 + 20, and the tokens are stripped from the
* class exposed to every Vue consumer (view-time layout and editors).
*/
private static final Pattern SPACING_TOKEN_PATTERN = Pattern.compile("(^| )(mt|mr|mb|ml|ms|me)-((md|lg|xl)-)?(n?)(\\d{1,2})(?= |$)");

private static final int MARGIN_NEUTRAL = 20;

private void convertLegacyMarginTokens() {
if (StringUtils.isBlank(this.cssClass)) {
return;
}
Matcher matcher = SPACING_TOKEN_PATTERN.matcher(this.cssClass);
boolean legacy = false;
while (matcher.find()) {
legacy = true;
int value = Integer.parseInt(matcher.group(6)) * 4 * ("n".equals(matcher.group(5)) ? -1 : 1) + MARGIN_NEUTRAL;
switch (matcher.group(2)) {
case "mt" -> this.marginTop = value;
case "mb" -> this.marginBottom = value;
case "me", "mr" -> this.marginRight = value;
default -> this.marginLeft = value;
}
}
if (legacy) {
String stripped = SPACING_TOKEN_PATTERN.matcher(this.cssClass).replaceAll(" ").replaceAll(" {2,}", " ").trim();
this.cssClass = StringUtils.isBlank(stripped) ? null : stripped;
}
}

private void init(ModelObject model, PortletInstanceService portletInstanceService, PortletInstanceContext portletInstanceContext) { // NOSONAR
ModelStyle cssStyle = model.getCssStyle();
if (cssStyle != null) {
Expand Down Expand Up @@ -379,6 +427,10 @@ private void init(ModelObject model, PortletInstanceService portletInstanceServi
this.appBackgroundSize = appCssStyle.getBackgroundSize();
this.appBackgroundRepeat = appCssStyle.getBackgroundRepeat();
this.appBackgroundAttachment = appCssStyle.getBackgroundAttachment();
this.appMarginTop = appCssStyle.getMarginTop();
this.appMarginRight = appCssStyle.getMarginRight();
this.appMarginBottom = appCssStyle.getMarginBottom();
this.appMarginLeft = appCssStyle.getMarginLeft();
}
if (model instanceof Page page) {
this.editPermission = page.getEditPermission();
Expand All @@ -400,6 +452,7 @@ private void init(ModelObject model, PortletInstanceService portletInstanceServi
this.width = application.getWidth();
this.height = application.getHeight();
this.cssClass = application.getCssClass();
convertLegacyMarginTokens();
this.showInfoBar = application.getShowInfoBar();
this.showApplicationState = application.getShowApplicationState();
this.showApplicationMode = application.getShowApplicationMode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,11 @@ private static ModelStyle mapToStyle(LayoutModel layoutModel) {
private static ApplicationBackgroundStyle mapToAppStyle(LayoutModel layoutModel) {
ApplicationBackgroundStyle cssStyle = null;
if (StringUtils.isNotBlank(layoutModel.getAppBackgroundColor())
|| StringUtils.isNotBlank(layoutModel.getAppBackgroundImage())) {
|| StringUtils.isNotBlank(layoutModel.getAppBackgroundImage())
|| layoutModel.getAppMarginTop() != null
|| layoutModel.getAppMarginRight() != null
|| layoutModel.getAppMarginBottom() != null
|| layoutModel.getAppMarginLeft() != null) {
cssStyle = new ApplicationBackgroundStyle();
cssStyle.setBackgroundColor(layoutModel.getAppBackgroundColor());
cssStyle.setBackgroundImage(layoutModel.getAppBackgroundImage());
Expand All @@ -260,6 +264,10 @@ private static ApplicationBackgroundStyle mapToAppStyle(LayoutModel layoutModel)
cssStyle.setBackgroundSize(layoutModel.getAppBackgroundSize());
cssStyle.setBackgroundRepeat(layoutModel.getAppBackgroundRepeat());
cssStyle.setBackgroundAttachment(layoutModel.getAppBackgroundAttachment());
cssStyle.setMarginTop(layoutModel.getAppMarginTop());
cssStyle.setMarginRight(layoutModel.getAppMarginRight());
cssStyle.setMarginBottom(layoutModel.getAppMarginBottom());
cssStyle.setMarginLeft(layoutModel.getAppMarginLeft());
}
return cssStyle;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2020 - 2026 Meeds Association contact@meeds.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package io.meeds.layout.model;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import org.junit.jupiter.api.Test;

import org.exoplatform.portal.config.model.Application;
import org.exoplatform.portal.config.model.ModelStyle;
import org.exoplatform.portal.config.model.TransientApplicationState;

/**
* Legacy application margins are read once, server-side, from
* the Vuetify spacing tokens still stored in the cssClass (value = N x 4 + 20
* on the platform scale), whatever scale the stored attribute used, and the
* tokens are stripped from the class exposed to the Vue consumers.
*/
public class LayoutModelTest {

Check warning on line 36 in layout-service/src/test/java/io/meeds/layout/model/LayoutModelTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove redundant visibility modifiers from the methods of this test class.

See more on https://sonarcloud.io/project/issues?id=io.meeds.layout&issues=AaD7Yz_AwE1quNk4WEtj&open=AaD7Yz_AwE1quNk4WEtj&pullRequest=575

@Test
public void shouldReadLegacyMarginsFromXmlImportedApplicationTokens() {
// page XML: 20-neutral attribute next to the tokens ModelStyle generated at import (mt-n1 = -4px)
LayoutModel model = new LayoutModel(application(16, 8, 4, 0, "mt-n1 mb-n3 me-n4 ms-n5 TEST-class"));
assertEquals(16, model.getMarginTop());
assertEquals(8, model.getMarginBottom());
assertEquals(4, model.getMarginRight());
assertEquals(0, model.getMarginLeft());
assertEquals("TEST-class", model.getCssClass());
}

@Test
public void shouldReadLegacyMarginsFromEditorSavedApplicationTokens() {
// editor before 7.3.0.30: 0-neutral attribute next to the tokens the editor wrote for the same -4px
LayoutModel model = new LayoutModel(application(-4, -12, -16, -20, "mt-n1 mb-n3 me-n4 ms-n5"));
assertEquals(16, model.getMarginTop());
assertEquals(8, model.getMarginBottom());
assertEquals(4, model.getMarginRight());
assertEquals(0, model.getMarginLeft());
assertNull(model.getCssClass());
}

@Test
public void shouldKeepPlatformScaleMarginsWithoutTokens() {
LayoutModel model = new LayoutModel(application(32, 20, null, null, "brtr-2 TEST-class"));
assertEquals(32, model.getMarginTop());
assertEquals(20, model.getMarginBottom());
assertNull(model.getMarginRight());
// Application.getCssClass() prefixes the style tokens with a space (pre-existing): only the content matters here
assertEquals("brtr-2 TEST-class", model.getCssClass().trim());
}

@Test
public void shouldReadPositiveAndBreakpointTokens() {
LayoutModel model = new LayoutModel(application(null, null, null, null, "mt-3 mb-0 mr-md-2 ml-1"));
assertEquals(32, model.getMarginTop());
assertEquals(20, model.getMarginBottom());
assertEquals(28, model.getMarginRight());
assertEquals(24, model.getMarginLeft());
assertNull(model.getCssClass());
}

private Application application(Integer top, Integer bottom, Integer right, Integer left, String cssClass) {
Application application = new Application("storageId");
application.setState(new TransientApplicationState("layout/Test"));
ModelStyle style = new ModelStyle();
style.setMarginTop(top);
style.setMarginBottom(bottom);
style.setMarginRight(right);
style.setMarginLeft(left);
application.setCssStyle(style);
application.setCssClass(cssClass);
return application;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,7 @@ layout.pageDraftSavedSuccessfully=Page draft saved successfully
layout.pageOutdatedError=The current page version seems to be outdated. Please try again by refreshing the page or check if another session is ongoing (another browser tab) or contact the support services.
layout.switchToDesktop=Switch to Desktop View
layout.switchToMobile=Switch to Mobile View
layout.margins=Margins
layout.top=Top
layout.right=Right
layout.bottom=Bottom
layout.left=Left
layout.border=Border
layout.borderColor=Color
layout.borderSize=Size
layout.boxShadow=Shadow
layout.borderRadius=Radius
layout.ok=Ok
layout.view=View
layout.sameForAllSides=Same for all sides
layout.differentForEachSide=Different for each side
layout.sameForAllCorners=Same for all corners
layout.differentForEachCorner=Different for each corner
layout.topRight=Top-Right
layout.topLeft=Top-Left
layout.bottomRight=Bottom-Right
layout.bottomLeft=Bottom-Left
layout.chooseATemplate=Choose a template
layout.fixedSectionTypeChoice=Fixed Section
layout.dynamicSectionTypeChoice=Dynamic Section
Expand Down Expand Up @@ -397,42 +378,13 @@ portletInstance.label.close=Close


layout.editPortletInstance=Edit portlet instance {0}
layout.uploadBackgroundImageTitle=Upload a background image
layout.deleteBackgroundImageTitle=Delete the background image
layout.background=Background
layout.backgroundColor=Background color
layout.backgroundImage=Background image
layout.image=Image
layout.imageSizeTitle=Size
layout.imageSizeCover=Fill, cropped
layout.imageSizeContain=Fit, whole image
layout.imageRepeatTitle=Repeat
layout.imageRepeat=Repeat
layout.imageNoRepeat=No repeat
layout.imageScrollingTitle=Scrolling
layout.imageScrollingFixed=Stays fixed
layout.imageScrollingScroll=Scrolls with page
layout.editPageProperties=Edit page design
layout.fullWindow=Full window
layout.imagePositionTitle=Position
layout.imagePositionCenter=Center
layout.imagePositionTopLeft=Top left
layout.imagePositionTopRight=Top right
layout.imagePositionBottomLeft=Bottom left
layout.imagePositionBottomRight=Bottom right
layout.gradient=Gradient
layout.gradientFrom=From
layout.gradientTo=To
layout.linearGradient=Linear Gradient
layout.radialGradient=Radial Gradient
layout.angularGradient=Angular Gradient
layout.gradientDirectionTopToBottom=Top to Bottom
layout.gradientDirectionLeftToRight=Left to Right
layout.gradientCornerTopLeft=Top Left
layout.gradientCornerTopRight=Top Right
layout.gradientCornerBottomRight=Bottom Right
layout.gradientCornerBottomLeft=Bottom Left
layout.gradientRatio=Ratio
layout.saveDraft=Save
layout.saveDraftTooltip=Save page as draft
layout.saveSiteDraftTooltip=Save site as draft
Expand All @@ -449,13 +401,6 @@ layout.draftConfirmQuestion=Do you want to resume the draft?
layout.draftConfirmOkLabel=Resume Draft
layout.draftConfirmCancelLabel=Create New
layout.pageBeingEditedByYouInOther=This version of the page is outdated and has been replaced by another one.
layout.textStyle=Text
layout.textColorTitle=Title
layout.textColorHeader=Header
layout.textColorBody=Body
layout.textColorSubtitle=Subtitle
layout.textBackground=Text Background
layout.color=Solid Color
layout.applicationStyling=Application Styling
layout.globalPageDesign=Global Page Design
layout.globalPageBackground=Page Background
Expand Down Expand Up @@ -665,7 +610,5 @@ layout.adjustPortalPageWidth=Adjust Page width
layout.editSiteLayout=Edit Site Layout
layout.editSiteNavigation=Edit Site Navigation
layout.pageMargins=Page Margins
layout.scrollTopColor=Top
layout.scrollMiddleColor=On scroll
layout.siteDesign=Site Design
layout.siteBackground=Site Background
Loading
Loading