diff --git a/apps/docs/app/jcr_root/apps/dx-docs/configs/config/com.adobe.dx.style.internal.Border.config b/apps/docs/app/jcr_root/apps/dx-docs/configs/config/com.adobe.dx.style.internal.Border.config new file mode 100644 index 00000000..e69de29b diff --git a/apps/docs/app/jcr_root/apps/dx-docs/configs/config/com.adobe.dx.style.internal.Shadow.config b/apps/docs/app/jcr_root/apps/dx-docs/configs/config/com.adobe.dx.style.internal.Shadow.config new file mode 100644 index 00000000..e69de29b diff --git a/apps/docs/app/jcr_root/apps/dx-docs/configs/config/com.adobe.dx.style.internal.StyleServiceImpl.config b/apps/docs/app/jcr_root/apps/dx-docs/configs/config/com.adobe.dx.style.internal.StyleServiceImpl.config new file mode 100644 index 00000000..e69de29b diff --git a/apps/structure/app/jcr_root/apps/dx/structure/components/flex/.content.xml b/apps/structure/app/jcr_root/apps/dx/structure/components/flex/.content.xml index a0986c7b..ae5c6d20 100644 --- a/apps/structure/app/jcr_root/apps/dx/structure/components/flex/.content.xml +++ b/apps/structure/app/jcr_root/apps/dx/structure/components/flex/.content.xml @@ -2,4 +2,5 @@ \ No newline at end of file + jcr:title="Flex" + styleWorkers="[shadow,border]"/> \ No newline at end of file diff --git a/apps/structure/app/jcr_root/apps/dx/structure/components/flex/flex.html b/apps/structure/app/jcr_root/apps/dx/structure/components/flex/flex.html index ebb0fc20..dce0cd1f 100644 --- a/apps/structure/app/jcr_root/apps/dx/structure/components/flex/flex.html +++ b/apps/structure/app/jcr_root/apps/dx/structure/components/flex/flex.html @@ -36,6 +36,19 @@ + + + + + + + +${model.style @ context='styleString'}
diff --git a/apps/structure/core/src/main/java/com/adobe/dx/structure/flex/FlexModel.java b/apps/structure/core/src/main/java/com/adobe/dx/structure/flex/FlexModel.java index 3f5564f2..d60ae226 100644 --- a/apps/structure/core/src/main/java/com/adobe/dx/structure/flex/FlexModel.java +++ b/apps/structure/core/src/main/java/com/adobe/dx/structure/flex/FlexModel.java @@ -13,11 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - package com.adobe.dx.structure.flex; import com.adobe.dx.domtagging.IDTagger; import com.adobe.dx.responsive.Breakpoint; +import com.adobe.dx.style.StyleService; import com.day.cq.wcm.api.policies.ContentPolicy; import com.day.cq.wcm.api.policies.ContentPolicyManager; @@ -50,6 +50,9 @@ public class FlexModel { @OSGiService IDTagger idTagger; + @OSGiService + StyleService styleService; + @ScriptVariable Breakpoint[] breakpoints; @@ -58,6 +61,8 @@ public class FlexModel { String id; + String style; + @PostConstruct void init() { bpMap = new HashMap<>(); @@ -66,6 +71,9 @@ void init() { bpMap.put(breakpoint.key(), breakpoint); } } + if (styleService != null) { + style = styleService.getLocalStyle(getId(), request); + } } public boolean isStyleNeeded() { @@ -79,6 +87,10 @@ public String getId() { return id; } + public String getStyle() { + return style; + } + private Resource getPolicyResource(String name) { ResourceResolver resolver = request.getResourceResolver(); ContentPolicyManager policyManager = resolver.adaptTo(ContentPolicyManager.class); diff --git a/apps/structure/core/src/main/java/com/adobe/dx/structure/utils/BackgroundGradient.java b/apps/structure/core/src/main/java/com/adobe/dx/structure/utils/BackgroundGradient.java index 2965fef2..7789c2e5 100644 --- a/apps/structure/core/src/main/java/com/adobe/dx/structure/utils/BackgroundGradient.java +++ b/apps/structure/core/src/main/java/com/adobe/dx/structure/utils/BackgroundGradient.java @@ -25,7 +25,7 @@ import org.apache.sling.models.annotations.injectorspecific.SlingObject; import org.apache.sling.models.annotations.DefaultInjectionStrategy; -@Model(adaptables = { SlingHttpServletRequest.class, Resource.class }, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) +@Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class BackgroundGradient { private static final String CQ_STYLEGUIDE_BUCKETNAME = "cq:styleguide"; diff --git a/apps/structure/core/src/test/java/com/adobe/dx/structure/AbstractStructureModelTest.java b/apps/structure/core/src/test/java/com/adobe/dx/structure/AbstractStructureModelTest.java new file mode 100644 index 00000000..c18c136b --- /dev/null +++ b/apps/structure/core/src/test/java/com/adobe/dx/structure/AbstractStructureModelTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * + * ADOBE CONFIDENTIAL + * __________________ + * + * Copyright 2019 Adobe + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Adobe and its suppliers, if any. The intellectual + * and technical concepts contained herein are proprietary to Adobe + * and its suppliers and are protected by all applicable intellectual + * property laws, including trade secret and copyright laws. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Adobe. + ******************************************************************************/ + +package com.adobe.dx.structure; + +import com.adobe.dx.bindings.internal.DxBindingsValueProvider; +import com.adobe.dx.responsive.Breakpoint; +import com.adobe.dx.responsive.ResponsiveConfiguration; +import com.adobe.dx.testing.AbstractRequestModelTest; + +import java.util.Map; + +import javax.script.Bindings; +import javax.script.SimpleBindings; + +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.testing.mock.caconfig.MockContextAwareConfig; +import org.junit.jupiter.api.BeforeEach; + +import io.wcm.testing.mock.aem.junit5.AemContext; + +public class AbstractStructureModelTest extends AbstractRequestModelTest { + + protected SlingBindings getBindings(AemContext context) { + return (SlingBindings) context.request().getAttribute(SlingBindings.class.getName()); + } + + protected static final String MODEL_PATH = CONTENT_ROOT + "/model"; + + @BeforeEach + public void beforeEach() { + context.build().resource(CONF_ROOT + "/sling:configs/" + ResponsiveConfiguration.class.getName() + "/breakpoints") + .siblingsMode() + .resource("1", "propertySuffix", "Mobile", "key", "mobile") + .resource("2", "propertySuffix", "Tablet", "key", "tablet") + .resource("3", "propertySuffix", "Desktop", "key", "desktop"); + MockContextAwareConfig.registerAnnotationClasses(context, ResponsiveConfiguration.class); + MockContextAwareConfig.registerAnnotationClasses(context, Breakpoint.class); + context.create().resource(CONTENT_ROOT, "sling:configRef", CONF_ROOT); + } + + @Override + protected T getModel(Class type) { + DxBindingsValueProvider provider = new DxBindingsValueProvider(); + Bindings bindings = new SimpleBindings(); + for (Map.Entry entry : getBindings(context).entrySet()) { + bindings.put(entry.getKey(), entry.getValue()); + } + bindings.put(SlingBindings.RESOURCE, context.currentResource()); + provider.addBindings(bindings); + SlingBindings slingBindings = getBindings(context); + slingBindings.put(DxBindingsValueProvider.POLICY_KEY, bindings.get(DxBindingsValueProvider.POLICY_KEY)); + slingBindings.put(DxBindingsValueProvider.BP_KEY, bindings.get(DxBindingsValueProvider.BP_KEY)); + slingBindings.put(DxBindingsValueProvider.RESP_PROPS_KEY, bindings.get(DxBindingsValueProvider.RESP_PROPS_KEY)); + return super.getModel(type); + } + + @Override + protected T getModel(Class type, String path) { + context.currentResource(path); + return getModel(type); + } +} diff --git a/apps/structure/core/src/test/java/com/adobe/dx/structure/flex/FlexModelTest.java b/apps/structure/core/src/test/java/com/adobe/dx/structure/flex/FlexModelTest.java index 34134752..24968548 100644 --- a/apps/structure/core/src/test/java/com/adobe/dx/structure/flex/FlexModelTest.java +++ b/apps/structure/core/src/test/java/com/adobe/dx/structure/flex/FlexModelTest.java @@ -21,55 +21,36 @@ import static org.mockito.Mockito.when; import com.adobe.dx.domtagging.IDTagger; -import com.adobe.dx.responsive.Breakpoint; -import com.adobe.dx.responsive.ResponsiveConfiguration; -import com.adobe.dx.testing.AbstractRequestModelTest; +import com.adobe.dx.structure.AbstractStructureModelTest; import com.day.cq.wcm.api.policies.ContentPolicy; import com.day.cq.wcm.api.policies.ContentPolicyManager; import java.util.List; import org.apache.sling.api.resource.ValueMap; -import org.apache.sling.api.wrappers.CompositeValueMap; -import org.apache.sling.caconfig.ConfigurationBuilder; -import org.apache.sling.testing.mock.caconfig.MockContextAwareConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class FlexModelTest extends AbstractRequestModelTest { +class FlexModelTest extends AbstractStructureModelTest { FlexModel model; @BeforeEach public void setup() throws ReflectiveOperationException { - context.build().resource(CONF_ROOT + "/sling:configs/" + ResponsiveConfiguration.class.getName() + "/breakpoints") - .siblingsMode() - .resource("1","propertySuffix", "Mobile", "key", "mobile") - .resource("2", "propertySuffix", "Tablet", "key", "tablet") - .resource("3", "propertySuffix", "Desktop", "key", "desktop"); - MockContextAwareConfig.registerAnnotationClasses(context, ResponsiveConfiguration.class); - MockContextAwareConfig.registerAnnotationClasses(context, Breakpoint.class); - context.create().resource(CONTENT_ROOT, "sling:configRef", CONF_ROOT); - ResponsiveConfiguration configuration = context.resourceResolver() - .getResource(CONTENT_ROOT) - .adaptTo(ConfigurationBuilder.class) - .as(ResponsiveConfiguration.class); - context.build().resource(CONTENT_ROOT, + context.build().resource(MODEL_PATH, "sling:resourceType", "dx/structure/components/flex", "title", "dx flex component") .resource("definitionsMobile") .siblingsMode() .resource("1", "minHeight", "custom") .resource("2", "minHeight", "custom"); - context.currentResource(CONTENT_ROOT); + context.currentResource(MODEL_PATH); context.contentPolicyMapping("dx/structure/components/flex", "blah", "blah"); ContentPolicy policy = context.resourceResolver() .adaptTo(ContentPolicyManager.class).getPolicy(context.currentResource()); context.build().resource(policy.getPath() + "/definitionsTablet/items0", "minHeight", "custom"); - model = getModel(FlexModel.class, CONTENT_ROOT); - model.breakpoints = configuration.breakpoints(); - model.init(); + model = getModel(FlexModel.class, MODEL_PATH); } @Test diff --git a/bundles/core/src/main/java/com/adobe/dx/bindings/internal/DxBindingsValueProvider.java b/bundles/core/src/main/java/com/adobe/dx/bindings/internal/DxBindingsValueProvider.java index b34ab9e0..76c05a03 100644 --- a/bundles/core/src/main/java/com/adobe/dx/bindings/internal/DxBindingsValueProvider.java +++ b/bundles/core/src/main/java/com/adobe/dx/bindings/internal/DxBindingsValueProvider.java @@ -48,11 +48,11 @@ */ public class DxBindingsValueProvider implements BindingsValuesProvider { - private static final String POLICY_KEY = "dxPolicy"; + public static final String POLICY_KEY = "dxPolicy"; - private static final String RESP_PROPS_KEY = "resprops"; + public static final String RESP_PROPS_KEY = "resprops"; - private static final String BP_KEY = "breakpoints"; + public static final String BP_KEY = "breakpoints"; @Override public void addBindings(@NotNull Bindings bindings) { diff --git a/bundles/core/src/main/java/com/adobe/dx/style/Constants.java b/bundles/core/src/main/java/com/adobe/dx/style/Constants.java new file mode 100644 index 00000000..41118eeb --- /dev/null +++ b/bundles/core/src/main/java/com/adobe/dx/style/Constants.java @@ -0,0 +1,28 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style; + +public class Constants { + private Constants() { + } + public static final String DECLARATION_DELIMITER = ";"; + public static final String DECLARATION = ": "; + public static final String SPACE = " "; + public static final String PX = "px"; + public static final String PX_SPACE = PX + SPACE; + public static final String DEL_SPACE = DECLARATION_DELIMITER + SPACE; + +} diff --git a/bundles/core/src/main/java/com/adobe/dx/style/StyleService.java b/bundles/core/src/main/java/com/adobe/dx/style/StyleService.java new file mode 100644 index 00000000..42c35e1a --- /dev/null +++ b/bundles/core/src/main/java/com/adobe/dx/style/StyleService.java @@ -0,0 +1,32 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.jetbrains.annotations.Nullable; + +public interface StyleService { + + /** + * Computes a list of CSS declarations relatives of the given request. If an id is provided, + * encapsulate those declarations in that id rule (for nested usage). + * + * @param id optional ID to encapsulate declarations with + * @param request current request + * @return declaration set, or local rule + */ + String getLocalStyle(@Nullable String id, SlingHttpServletRequest request); +} diff --git a/bundles/core/src/main/java/com/adobe/dx/style/StyleWorker.java b/bundles/core/src/main/java/com/adobe/dx/style/StyleWorker.java new file mode 100644 index 00000000..dd7a6be7 --- /dev/null +++ b/bundles/core/src/main/java/com/adobe/dx/style/StyleWorker.java @@ -0,0 +1,41 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.jetbrains.annotations.Nullable; + +/** + * Single CSS rule generator, for very specific usage in a style tag, modified by the component itself + */ +public interface StyleWorker { + + /** + * @return key with which the worker can be identified + */ + String getKey(); + + /** + * Generates a declaration specific to that generator, for an upper rule + * + * @param resource current component resource, + * @param dxPolicy policy, could be obtained from above resource, but in the signature for practical reason, + * @return single or several declarations split by ';', or null if not necessary or able to generate some + */ + @Nullable String getDeclaration(Resource resource, ValueMap dxPolicy); + +} diff --git a/bundles/core/src/main/java/com/adobe/dx/style/internal/Border.java b/bundles/core/src/main/java/com/adobe/dx/style/internal/Border.java new file mode 100644 index 00000000..05eeaba9 --- /dev/null +++ b/bundles/core/src/main/java/com/adobe/dx/style/internal/Border.java @@ -0,0 +1,163 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style.internal; + +import static com.adobe.dx.style.Constants.DECLARATION; +import static com.adobe.dx.style.Constants.DEL_SPACE; +import static com.adobe.dx.style.Constants.PX; +import static com.adobe.dx.style.Constants.PX_SPACE; +import static com.adobe.dx.style.Constants.SPACE; + +import com.adobe.dx.style.StyleWorker; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.jetbrains.annotations.Nullable; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +@Component(configurationPolicy = ConfigurationPolicy.REQUIRE) +public class Border implements StyleWorker { + private static final String DECL_PREFIX = "border"; + private static final String KEY = DECL_PREFIX; + private static final String PREFIX = KEY; + private static final String COLOR_SUFFIX = "Color"; + private static final String STYLE_SUFFIX = "Style"; + private static final String WIDTH_SUFFIX = "Width"; + private static final String RADIUS = "Radius"; + private static final String TOP = "Top"; + private static final String RIGHT = "Right"; + private static final String LEFT = "Left"; + private static final String BOTTOM = "Bottom"; + private static final String DECL_RADIUS = "border-radius: "; + private static final String DECL_TOP = DECL_PREFIX + "-top"; + private static final String DECL_BOTTOM = DECL_PREFIX + "-bottom"; + private static final String DECL_RIGHT = DECL_PREFIX + "-right"; + private static final String DECL_LEFT = DECL_PREFIX + "-left"; + private static final String ALL = "all"; + private static final String EACH = "each"; + private static final String ALL_CAP = "All"; + private static final String PN_BORDERRADIUS = PREFIX + RADIUS; + private static final String PN_ALLRADIUS = PREFIX + ALL_CAP + RADIUS; + private static final String PN_RADIUS_TOPLEFT = PREFIX + RADIUS + TOP + LEFT; + private static final String PN_RADIUS_TOPRIGHT = PREFIX + RADIUS + TOP + RIGHT; + private static final String PN_RADIUS_BOTTOMLEFT = PREFIX + RADIUS + BOTTOM + LEFT; + private static final String PN_RADIUS_BOTTOMRIGHT = PREFIX + RADIUS + BOTTOM + RIGHT; + private static final String PN_SIDES = PREFIX + "Sides"; + + @Override + public String getKey() { + return KEY; + } + + @Override + public @Nullable String getDeclaration(Resource resource, ValueMap dxPolicy) { + List declarations = null; + String border = buildBorder(dxPolicy); + String radius = buildRadius(dxPolicy); + if (border != null) { + if (radius != null) { + return border + DEL_SPACE + radius; + } else { + return border; + } + } else if (radius != null) { + return radius; + } + return null; + } + + private String buildBorder(ValueMap policy) { + String borderSides = policy.get(PN_SIDES, String.class); + if (StringUtils.equals(ALL, borderSides)) { + return getAllBorders(policy); + } else if (StringUtils.equals(EACH, borderSides)) { + return getEachBorder(policy); + } + return null; + } + + private String getAllBorders(ValueMap policy) { + return getBorderStyle(policy, ALL_CAP, DECL_PREFIX); + } + + private String getEachBorder(ValueMap policy) { + List borders = new ArrayList<>(); + final String topBorder = getBorderStyle(policy, TOP, DECL_TOP); + final String rightBorder = getBorderStyle(policy, RIGHT, DECL_RIGHT); + final String bottomBorder = getBorderStyle(policy, BOTTOM, DECL_BOTTOM); + final String leftBorder = getBorderStyle(policy, LEFT, DECL_LEFT); + if (topBorder != null) { + borders.add(topBorder); + } + if (rightBorder != null) { + borders.add(rightBorder); + } + if (bottomBorder != null) { + borders.add(bottomBorder); + } + if (leftBorder != null) { + borders.add(leftBorder); + } + return borders.isEmpty() ? null : String.join(DEL_SPACE, borders); + } + + private String getBorderStyle(ValueMap policy, String side, String propertyName) { + String borderStyle = policy.get(PREFIX + side + STYLE_SUFFIX, String.class); + long borderThickness = policy.get(PREFIX + side + WIDTH_SUFFIX, 0L); + String borderColor = policy.get(PREFIX + side + COLOR_SUFFIX, String.class); + if (borderStyle != null && borderThickness > 0 && borderColor != null) { + return propertyName + DECLARATION + borderStyle + SPACE + borderThickness + PX_SPACE + borderColor; + } + return null; + } + + private String buildRadius(ValueMap policy) { + String borderRadius = policy.get(PN_BORDERRADIUS, String.class); + if (StringUtils.equals(ALL, borderRadius)) { + return getAllBorderRadius(policy); + } else if (StringUtils.equals(EACH, borderRadius)) { + return getEachBorderRadius(policy); + } + return null; + } + + private String getAllBorderRadius(ValueMap policy) { + long borderRadius = policy.get(PN_ALLRADIUS, 0L); + if (borderRadius > 0) { + return DECL_RADIUS + borderRadius + PX; + } + return null; + } + + private String getEachBorderRadius(ValueMap policy) { + long radiusTopLeft = policy.get(PN_RADIUS_TOPLEFT, 0L); + long radiusTopRight = policy.get(PN_RADIUS_TOPRIGHT, 0L); + long radiusBottomRight = policy.get(PN_RADIUS_BOTTOMRIGHT, 0L); + long radiusBottomLeft = policy.get(PN_RADIUS_BOTTOMLEFT, 0L); + + if (radiusTopLeft > 0 || radiusTopRight > 0 || radiusBottomLeft > 0 || radiusBottomRight > 0) { + return DECL_RADIUS + + radiusTopLeft + PX_SPACE + radiusTopRight + PX_SPACE + + radiusBottomRight + PX_SPACE + radiusBottomLeft + PX; + } + return null; + } +} diff --git a/bundles/core/src/main/java/com/adobe/dx/style/internal/Shadow.java b/bundles/core/src/main/java/com/adobe/dx/style/internal/Shadow.java new file mode 100644 index 00000000..640d584d --- /dev/null +++ b/bundles/core/src/main/java/com/adobe/dx/style/internal/Shadow.java @@ -0,0 +1,65 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style.internal; + +import static com.adobe.dx.style.Constants.PX_SPACE; + +import com.adobe.dx.style.StyleWorker; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.jetbrains.annotations.Nullable; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +@Component(configurationPolicy = ConfigurationPolicy.REQUIRE) +public class Shadow implements StyleWorker { + + private static final String KEY = "shadow"; + private static final String RULE = "box-shadow: "; + private static final String INSET_SUFFIX = " inset"; + private static final String PREFIX = KEY; + private static final String PN_COLOR = PREFIX + "Color"; + private static final String PN_OFFSETX = PREFIX + "OffsetX"; + private static final String PN_OFFSETY = PREFIX + "OffsetY"; + private static final String PN_BLUR = PREFIX + "Blur"; + private static final String PN_SPREAD = PREFIX + "Spread"; + private static final String PN_INSET = PREFIX + "Inset"; + + @Override + public String getKey() { + return KEY; + } + + @Override + public @Nullable String getDeclaration(Resource resource, ValueMap dxPolicy) { + String color = dxPolicy.get(PN_COLOR, String.class); + if (color != null) { + StringBuilder sb = new StringBuilder(); + sb.append(RULE) + .append(dxPolicy.get(PN_OFFSETX, 0L)).append(PX_SPACE) + .append(dxPolicy.get(PN_OFFSETY, 0L)).append(PX_SPACE) + .append(dxPolicy.get(PN_BLUR, 0L)).append(PX_SPACE) + .append(dxPolicy.get(PN_SPREAD, 0L)).append(PX_SPACE) + .append(color); + if (dxPolicy.containsKey(PN_INSET)) { + sb.append(INSET_SUFFIX); + } + return sb.toString(); + } + return null; + } +} diff --git a/bundles/core/src/main/java/com/adobe/dx/style/internal/StyleServiceImpl.java b/bundles/core/src/main/java/com/adobe/dx/style/internal/StyleServiceImpl.java new file mode 100644 index 00000000..16a43869 --- /dev/null +++ b/bundles/core/src/main/java/com/adobe/dx/style/internal/StyleServiceImpl.java @@ -0,0 +1,124 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style.internal; + +import static com.adobe.dx.style.Constants.DECLARATION_DELIMITER; +import static org.apache.commons.lang3.StringUtils.EMPTY; + +import com.adobe.dx.bindings.internal.DxBindingsValueProvider; +import com.adobe.dx.style.StyleWorker; +import com.adobe.dx.style.StyleService; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.scripting.SlingBindings; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(configurationPolicy = ConfigurationPolicy.REQUIRE) +public class StyleServiceImpl implements StyleService { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private static final String FORMAT_ID = "#%s {%s}"; + private static final String SLASH = "/"; + private static final String TYPE_PREFIX = "/apps/"; + private static final String STYLEWORKERS_SUFFIX = "/styleWorkers"; + + @Reference(service= StyleWorker.class, + cardinality= ReferenceCardinality.MULTIPLE, + policy= ReferencePolicy.DYNAMIC, policyOption= ReferencePolicyOption.GREEDY, + bind = "bindWorker", unbind = "unbindWorker") + volatile List workers = new ArrayList<>(); + + Map workerMap = MapUtils.EMPTY_MAP; + + @Override + public String getLocalStyle(String id, SlingHttpServletRequest request) { + List declarations = null; + Resource resource = request.getResource(); + String[] keys = getWorkerKeys(resource); + if (keys != null) { + SlingBindings bindings = (SlingBindings)request.getAttribute(SlingBindings.class.getName()); + ValueMap dxPolicy = bindings != null ? (ValueMap)bindings.get(DxBindingsValueProvider.POLICY_KEY) : ValueMap.EMPTY; + for (String workerKey : keys) { + StyleWorker worker = workerMap.get(workerKey); + if (worker != null) { + String declaration = worker.getDeclaration(resource, dxPolicy); + if (StringUtils.isNotBlank(declaration)) { + logger.debug("generated {} from {}", declaration, request); + if (declarations == null) { + declarations = new ArrayList<>(); + } + declarations.add(declaration); + } + } else { + logger.debug("{} was required resource type {}, but no associated worker is registered", workerKey, + resource.getResourceType()); + } + } + if (declarations != null && !declarations.isEmpty()) { + String concat = String.join(DECLARATION_DELIMITER, declarations); + return StringUtils.isNotBlank(id) ? String.format(FORMAT_ID, id, concat) : concat; + } + } + return EMPTY; + } + + /** + * returns ordered list of workers for that given resource (or null) + */ + String[] getWorkerKeys(Resource resource) { + String type = resource.getResourceType(); + String typePath = (type.startsWith(SLASH) ? type : TYPE_PREFIX + type) + STYLEWORKERS_SUFFIX; + Resource keys = resource.getResourceResolver().getResource(typePath); + if (keys != null) { + return keys.adaptTo(String[].class); + } + return null; + } + + void refreshWorkers() { + Map map = new HashMap<>(); + for (StyleWorker worker : workers) { + map.put(worker.getKey(), worker); + } + workerMap = map; + } + + void bindWorker(StyleWorker worker) { + workers.add(worker); + refreshWorkers(); + } + + void unbindWorker(StyleWorker worker) { + workers.remove(worker); + refreshWorkers(); + } +} diff --git a/bundles/core/src/main/java/com/adobe/dx/style/package-info.java b/bundles/core/src/main/java/com/adobe/dx/style/package-info.java new file mode 100644 index 00000000..0128bc09 --- /dev/null +++ b/bundles/core/src/main/java/com/adobe/dx/style/package-info.java @@ -0,0 +1,19 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2019 Adobe + ~ + ~ Licensed 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. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + @Version("0.0.1") + package com.adobe.dx.style; + + import org.osgi.annotation.versioning.Version; \ No newline at end of file diff --git a/bundles/core/src/test/java/com/adobe/dx/style/internal/BorderTest.java b/bundles/core/src/test/java/com/adobe/dx/style/internal/BorderTest.java new file mode 100644 index 00000000..98144d82 --- /dev/null +++ b/bundles/core/src/test/java/com/adobe/dx/style/internal/BorderTest.java @@ -0,0 +1,85 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.adobe.dx.testing.AbstractTest; + +import org.junit.jupiter.api.Test; + +public class BorderTest extends AbstractTest { + + @Test + void lockKey() { + assertEquals("border", new Border().getKey()); + } + + private void assertBorderEquals(String expected) { + assertEquals(expected, new Border().getDeclaration(context.currentResource(), getVM(CONTENT_ROOT))); + } + + @Test + void getNoBorder() { + context.build().resource(CONTENT_ROOT, "foo", "bar"); + assertNull(new Border().getDeclaration(context.currentResource(), getVM(CONTENT_ROOT))); + } + + @Test + void getBorder() { + context.build().resource(CONTENT_ROOT, "borderSides", "all","borderAllStyle", "dotted", "borderAllWidth", 4, "borderAllColor", "red"); + assertBorderEquals("border: dotted 4px red"); + } + + @Test + void getRadius() { + context.build().resource(CONTENT_ROOT, "borderRadius", "all","borderAllRadius", 3); + assertBorderEquals("border-radius: 3px"); + } + + @Test + void getBorderAndRadius() { + context.build().resource(CONTENT_ROOT, "borderSides", "all","borderAllStyle", "dotted", "borderAllWidth", 4, "borderAllColor", "red", + "borderRadius", "all","borderAllRadius", 3); + assertBorderEquals("border: dotted 4px red; border-radius: 3px"); + } + + @Test + void getSomeBorders() { + context.build().resource(CONTENT_ROOT, "borderSides", "each", + "borderLeftStyle", "dotted", "borderLeftWidth", 4, "borderLeftColor", "red", + "borderRightStyle", "dotted", "borderRightWidth", 4, "borderRightColor", "red"); + assertBorderEquals("border-right: dotted 4px red; border-left: dotted 4px red"); + } + + @Test + void getSomeRadius() { + context.build().resource(CONTENT_ROOT, "borderRadius", "each", + "borderRadiusTopLeft", 4, + "borderRadiusBottomRight", 3); + assertBorderEquals("border-radius: 4px 0px 3px 0px"); + } + + @Test + void getSomeBordersAndRadiuses() { + context.build().resource(CONTENT_ROOT, "borderSides", "each", + "borderTopStyle", "dotted", "borderTopWidth", 4, "borderTopColor", "red", + "borderBottomStyle", "dotted", "borderBottomWidth", 4, "borderBottomColor", "red", + "borderRadius", "each", "borderRadiusBottomLeft", 4, "borderRadiusTopRight", 3); + assertBorderEquals("border-top: dotted 4px red; border-bottom: dotted 4px red; border-radius: 0px 3px 0px 4px"); + } +} diff --git a/bundles/core/src/test/java/com/adobe/dx/style/internal/ShadowTest.java b/bundles/core/src/test/java/com/adobe/dx/style/internal/ShadowTest.java new file mode 100644 index 00000000..5610af5d --- /dev/null +++ b/bundles/core/src/test/java/com/adobe/dx/style/internal/ShadowTest.java @@ -0,0 +1,47 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.adobe.dx.testing.AbstractTest; + +import org.junit.jupiter.api.Test; + +class ShadowTest extends AbstractTest { + + @Test + void lockKey() { + assertEquals("shadow", new Shadow().getKey()); + } + + @Test + void getShadow() { + context.build().resource(CONTENT_ROOT, "shadowColor", "blue", + "shadowOffsetX", 9L, + "shadowOffsetY", 10L, + "shadowBlur", 11L, + "shadowSpread", 12L); + assertEquals("box-shadow: 9px 10px 11px 12px blue", new Shadow().getDeclaration(context.currentResource(), getVM(CONTENT_ROOT))); + } + + @Test + void testInset() { + context.build().resource(CONTENT_ROOT, "shadowColor", "red", + "shadowInset", true); + assertEquals("box-shadow: 0px 0px 0px 0px red inset", new Shadow().getDeclaration(context.currentResource(), getVM(CONTENT_ROOT))); + } +} \ No newline at end of file diff --git a/bundles/core/src/test/java/com/adobe/dx/style/internal/StyleServiceImplTest.java b/bundles/core/src/test/java/com/adobe/dx/style/internal/StyleServiceImplTest.java new file mode 100644 index 00000000..684e79da --- /dev/null +++ b/bundles/core/src/test/java/com/adobe/dx/style/internal/StyleServiceImplTest.java @@ -0,0 +1,86 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2020 Adobe + ~ + ~ Licensed 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 com.adobe.dx.style.internal; + +import static org.junit.jupiter.api.Assertions.*; + +import com.adobe.dx.bindings.internal.DxBindingsValueProvider; +import com.adobe.dx.style.StyleWorker; +import com.adobe.dx.testing.AbstractTest; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.scripting.SlingBindings; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +class StyleServiceImplTest extends AbstractTest { + + @Test + void getLocalStyle() { + final String[] array = new String[] {"worker1", "worker2"}; + context.build().resource("/apps/foo/bar", "styleWorkers", array); + context.build().resource(CONTENT_ROOT, "sling:resourceType", "foo/bar", "color", "blue", "fontsize", "13px"); + context.currentResource(CONTENT_ROOT); + ((SlingBindings)context.request().getAttribute(SlingBindings.class.getName())).put(DxBindingsValueProvider.POLICY_KEY, context.currentResource().getValueMap()); + StyleServiceImpl service = new StyleServiceImpl(); + assertTrue(StringUtils.isBlank(service.getLocalStyle(null, context.request()))); + service.bindWorker(new StyleWorker() { + @Override + public String getKey() { + return "worker1"; + } + + @Override + public @Nullable String getDeclaration(Resource resource, ValueMap dxPolicy) { + return "color: " + dxPolicy.get("color", String.class); + } + }); + StyleWorker worker2 = new StyleWorker() { + @Override + public String getKey() { + return "worker2"; + } + + @Override + public @Nullable String getDeclaration(Resource resource, ValueMap dxPolicy) { + return "font-size: " + dxPolicy.get("fontsize", String.class); + } + }; + service.bindWorker(worker2); + assertEquals("color: blue;font-size: 13px", service.getLocalStyle(null, context.request())); + assertEquals("#this-is-my-block {color: blue;font-size: 13px}", service.getLocalStyle("this-is-my-block", context.request())); + service.unbindWorker(worker2); + assertEquals("color: blue", service.getLocalStyle(null, context.request())); + } + + @Test + void getWorkerKeysFullPath() { + final String[] array = new String[] {"workers1", "workers2"}; + context.build().resource("/apps/foo/bar", "styleWorkers", array); + context.build().resource(CONTENT_ROOT, "sling:resourceType", "foo/bar"); + assertArrayEquals(array, new StyleServiceImpl().getWorkerKeys(context.currentResource(CONTENT_ROOT))); + } + + + @Test + void getWorkerKeysNothing() { + context.build().resource("/apps/foo/bar", "blah", "blah"); + context.build().resource(CONTENT_ROOT, "sling:resourceType", "foo/bar"); + assertNull(new StyleServiceImpl().getWorkerKeys(context.currentResource(CONTENT_ROOT))); + } +} \ No newline at end of file diff --git a/bundles/core/src/test/resources/simplelogger.properties b/bundles/core/src/test/resources/simplelogger.properties new file mode 100644 index 00000000..8cf6602b --- /dev/null +++ b/bundles/core/src/test/resources/simplelogger.properties @@ -0,0 +1,18 @@ +# 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. + +org.slf4j.simpleLogger.defaultLogLevel=error diff --git a/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractRequestModelTest.java b/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractRequestModelTest.java index 8d80cee8..f36d28df 100644 --- a/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractRequestModelTest.java +++ b/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractRequestModelTest.java @@ -16,28 +16,30 @@ package com.adobe.dx.testing; import org.apache.sling.api.resource.Resource; -import org.apache.sling.api.scripting.SlingBindings; import org.apache.sling.models.factory.ModelFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AbstractRequestModelTest extends AbstractTest { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRequestModelTest.class); - protected T getModel(final Class type) throws ReflectiveOperationException { - Resource resource = context.currentResource(); - if (resource != null) { - context.addModelsForClasses(type); - return context.getService(ModelFactory.class).createModel(context.request(), type); + protected T getModel(final Class type) { + try { + Resource resource = context.currentResource(); + if (resource != null) { + context.addModelsForClasses(type); + return context.getService(ModelFactory.class).createModel(context.request(), type); + } + return type.getDeclaredConstructor().newInstance(); + } catch(ReflectiveOperationException e) { + LOGGER.error("unable to fetch model", e); } - return type.getDeclaredConstructor().newInstance(); + return null; } - protected T getModel(final Class type, String path) throws ReflectiveOperationException { + protected T getModel(final Class type, String path) { context.currentResource(path); return getModel(type); } - protected void addBinding(String binding, Object value) { - SlingBindings bindings = (SlingBindings) context.request().getAttribute(SlingBindings.class.getName()); - bindings.put(binding, value); - } - } \ No newline at end of file diff --git a/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractTest.java b/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractTest.java index 2b954160..d761d4f9 100644 --- a/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractTest.java +++ b/bundles/testing/src/main/java/com/adobe/dx/testing/AbstractTest.java @@ -17,7 +17,6 @@ import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ValueMap; -import org.apache.sling.api.scripting.SlingBindings; import org.apache.sling.testing.mock.sling.ResourceResolverType; import org.junit.jupiter.api.extension.ExtendWith; @@ -30,8 +29,8 @@ @ExtendWith(AemContextExtension.class) public class AbstractTest { - protected static final String CONTENT_ROOT = "/content/foo"; - protected static final String CONF_ROOT = "/conf/foo"; + public static final String CONTENT_ROOT = "/content/foo"; + public static final String CONF_ROOT = "/conf/foo"; protected AemContext context = buildContext(getType());