diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 000000000..ba9bf6ad6 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,30 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Build + +on: + push: + branches: [ release/1.8 ] + pull_request: + branches: [ release/1.8 ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: gradle erapper update-index + run: git update-index --chmod=+x gradlew + - name: chmod gradle wrapper + run: chmod +x gradlew + - name: list of permissions + run: git ls-tree HEAD + - name: Build with gradle + run: ./gradlew build diff --git a/README.md b/README.md index 2ed3f0343..cc692ad62 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,93 @@ -dxa-web-application-java -=== SDL Digital Experience Accelerator Java Spring MVC web application +=== About ----- -The SDL Digital Experience Accelerator (DXA) is a reference implementation of SDL Web 8 and SDL Tridion 2013 SP1(*) intended to help you create, design and publish an SDL Web/Tridion-based website quickly. +The SDL Digital Experience Accelerator (DXA) is a reference implementation of SDL Tridion Sites 9 and SDL Web 8 intended to help you create, design and publish an SDL Tridion/Web-based website quickly. -It is available for .NET and Java Web Applications and has a modular architecture consisting of a Framework and example web application providing core functionality and separate Modules for additional, optional functionality. +DXA is available for both .NET and Java web applications. Its modular architecture consists of a framework and example web application, which includes all core SDL Tridion/Web functionality as well as separate Modules for additional, optional functionality. -This repository contains the source code of the DXA Framework, example web application and Maven archetype for Java. -The full DXA distribution (including CM-side items and installation support) is downloadable from the [SDL Community site](https://community.sdl.com/developers/tridion_developer/m/mediagallery/1241) (latest version) -or the [Releases in GitHub](https://github.com/sdl/dxa-web-application-java/releases) (all versions) +This repository contains the source code of the DXA Framework, an example Java web application, and a Maven archetype for Java. +The full DXA distribution (including Content Manager-side items and installation support) is downloadable from the [SDL AppStore](https://appstore.sdl.com/list/?search=dxa) +or the [Releases in GitHub](https://github.com/sdl/dxa-web-application-java/releases) Furthermore, the compiled DXA artifacts are available on [Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cdxa). -To facilitate upgrades, it is highly recommended to use official, compiled DXA artifacts from Maven Central instead of a custom build. -If you really have to modify the DXA Framework, we kindly request you to submit your changes as a Contribution; see below. -(*) SDL Tridion 2013 SP1 is only supported up to DXA version 1.6. +To facilitate upgrades, we strongly recommend that you use official, compiled DXA artifacts from Maven Central instead of a custom build. +If you really must modify the DXA framework, we kindly request you to submit your changes as a Contribution (see the Branches and Contributions section below). + Support --------------- -At SDL we take your investment in Digital Experience very seriously, and will do our best to support you throughout this journey. -If you encounter any issues with the Digital Experience Accelerator, please reach out to us via one of the following channels: +At SDL we take your investment in Digital Experience very seriously, if you encounter any issues with the Digital Experience Accelerator, please use one of the following channels: - Report issues directly in [this repository](https://github.com/sdl/dxa-web-application-java/issues) -- Ask questions 24/7 on the SDL Web Community at https://tridion.stackexchange.com -- Contact Technical Support through the Customer Support Web Portal at https://www.sdl.com/support +- Ask questions 24/7 on the SDL Tridion Community at https://tridion.stackexchange.com +- Contact SDL Professional Services for DXA release management support packages to accelerate your support requirements Documentation ------------- -Documentation can be found online in the SDL documentation portal: http://docs.sdl.com/sdldxa +Documentation can be found online in the SDL documentation portal: https://docs.sdl.com/sdldxa Repositories ------------ The following repositories with source code are available: - - https://github.com/sdl/dxa-content-management - Core Template Building Blocks + - https://github.com/sdl/dxa-content-management - CM-side framework (.NET Template Building Blocks) - https://github.com/sdl/dxa-html-design - Whitelabel HTML Design + - https://github.com/sdl/dxa-model-service - Model Service (Java) - https://github.com/sdl/dxa-modules - Modules (.NET and Java) - - https://github.com/sdl/dxa-web-application-dotnet - ASP.NET MVC web application (incl. framework) - - https://github.com/sdl/dxa-web-application-java - Java Spring MVC web application (incl. framework) + - https://github.com/sdl/dxa-web-application-dotnet - ASP.NET MVC web application (including framework) + - https://github.com/sdl/dxa-web-application-java - Java Spring MVC web application (including framework) Branches and Contributions -------------------------- We are using the following branching strategy: - - `master` - Represents the latest stable version. This may be a pre-release version (tagged as `DXA x.y Sprint z`). Updated each development Sprint (approx. bi-weekly). + - `master` - Represents the latest stable version. This may be a pre-release version (tagged as `DXA x.y Sprint z`). Updated each development Sprint (approximately bi-weekly). - `develop` - Represents the latest development version. Updated very frequently (typically nightly). - - `release/x.y` - Represents the x.y Release. If hotfixes are applicable, they will be applied to the appropriate release branch, so that the release branch actually represent the initial release plus hotfixes. + - `release/x.y` - Represents the x.y Release. If hotfixes are applicable, they will be applied to the appropriate release branch so that the branch actually represents the initial release plus hotfixes. All releases (including pre-releases and hotfix releases) are tagged. Note that development sources (on `develop` branch) have dependencies on SNAPSHOT versions of the DXA artifacts, which are available here: https://oss.sonatype.org/content/repositories/snapshots/com/sdl/dxa/ -If you wish to submit a Pull Request, it should normally be submitted on the `develop` branch, so it can be incorporated in the upcoming release. +If you wish to submit a Pull Request, it should normally be submitted on the `develop` branch so that it can be incorporated in the upcoming release. -Fixes for really severe/urgent issues (which qualify as hotfixes) should be submitted as Pull Request on the appropriate release branch. +Fixes for severe/urgent issues (that qualify as hotfixes) should be submitted as Pull Requests on the appropriate release branch. -Please always submit an Issue for the problem and indicate whether you think it qualifies as a hotfix; Pull Requests on release branches will only be accepted after agreement on the severity of the issue. +Always submit an Issue for the problem, and indicate whether you think it qualifies as a hotfix. Pull Requests on release branches will only be accepted after agreement on the severity of the issue. Furthermore, Pull Requests on release branches are expected to be extensively tested by the submitter. -Of course, it's also possible (and appreciated) to report an Issue without associated Pull Requests. +Of course, it is also possible (and appreciated) to report an issue without associated Pull Requests. DXA Builder ----------- -Current DXA Builder is available in Maven Central, latest DXA Builder is also available as a public snapshot. +The current DXA Builder is available in Maven Central, and the latest DXA Builder is also available as a public snapshot. + +If you have not configured a snapshot repository and don't want to, you may need to install the DXA Builder locally in order to run the SNAPSHOT. + +To install it, run the wrapper script of the `dxa-builder` project: `gradlew(.bat) publishLocal` +On Windows, you can also just run `get-started.cmd` script at first run. + + +DD4T support +--- +DD4T 2.1 for Java is incorporated into the DXA codebase in the `dxa-compatible` artifact. As a result, when migrating from DD4T to DXA, you do not need separate dependencies on DD4T. -You may need DXA Builder to be installed locally to run the SNAPSHOT build if you haven't configured snapshot repository and you don't want to. -To install it run the wrapper script of `dxa-builder` project: `gradlew(.bat) publishLocal` -On Windows you can also just run `get-started.cmd` script at first run. Snapshots --------- -DXA publishes SNAPSHOT versions to Sonatype. If you want to use them, you have to configure `https://oss.sonatype.org/content/repositories/snapshots` as a repository in your Maven settings. Read [this](https://maven.apache.org/settings.html#Repositories) for instructions. +DXA publishes SNAPSHOT versions to Sonatype. To use them, configure `https://oss.sonatype.org/content/repositories/snapshots` as a repository in your Maven settings. Read [this](https://maven.apache.org/settings.html#Repositories) for instructions. + License ------- -Copyright (c) 2014-2016 SDL Group. +Copyright (c) 2014-2020 SDL Group. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/build.gradle b/build.gradle index 73df81aa0..62b4ee609 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,9 @@ task prepareArchetype(dependsOn: buildFramework) << { XmlUtil.serialize rootTo, to.newPrintWriter() println 'POMs for Archetype have been changed (parent tag moved from archetype itself to its pom)' + + def iml = new File("${project.projectDir}/dxa-webapp/target/generated-sources/archetype/src/main/resources/archetype-resources/__artifactId__.iml") + iml.delete() } task buildArchetype(type: MavenBuildTask, dependsOn: prepareArchetype) { diff --git a/dxa-bom-modules/pom.xml b/dxa-bom-modules/pom.xml index 596042921..2b4b11358 100644 --- a/dxa-bom-modules/pom.xml +++ b/dxa-bom-modules/pom.xml @@ -12,7 +12,7 @@ com.sdl.dxa.modules dxa-bom-modules - 1.8.0-SNAPSHOT + 1.8.0 pom DXA Modules - Bill of Materials @@ -45,7 +45,7 @@ - 1.8.0-SNAPSHOT + 1.8.0 ${dxa-modules.version} ${dxa-modules.version} ${dxa-modules.version} diff --git a/dxa-bom/pom.xml b/dxa-bom/pom.xml index 5384b9f39..cc0b7225b 100644 --- a/dxa-bom/pom.xml +++ b/dxa-bom/pom.xml @@ -12,7 +12,7 @@ com.sdl.dxa dxa-bom - 1.8.0-SNAPSHOT + 1.8.0 pom DXA - Bill of Materials @@ -45,8 +45,8 @@ - 2.1.4-DXA17 - 8.5.0-1008 + 2.0.15.3-DXA18 + 10.1.0-1012 ${project.version} @@ -749,6 +749,11 @@ annotations 13.0 + + org.hibernate + hibernate-validator + 5.3.4.Final + diff --git a/dxa-builder/build.gradle b/dxa-builder/build.gradle index 5748ae69b..1f93ee8e3 100644 --- a/dxa-builder/build.gradle +++ b/dxa-builder/build.gradle @@ -1,5 +1,5 @@ group 'com.sdl.dxa' -version '1.8.0-SNAPSHOT' +version '1.8.0' apply plugin: 'groovy' apply plugin: 'maven-publish' diff --git a/dxa-framework/dxa-common-api/pom.xml b/dxa-framework/dxa-common-api/pom.xml index fc23639bb..788e63fc0 100644 --- a/dxa-framework/dxa-common-api/pom.xml +++ b/dxa-framework/dxa-common-api/pom.xml @@ -6,7 +6,7 @@ com.sdl.dxa dxa-framework - 1.8.0-SNAPSHOT + 1.8.0 dxa-common-api @@ -117,6 +117,5 @@ org.apache.httpcomponents httpclient - diff --git a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/WebRequestContext.java b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/WebRequestContext.java index 3fd986599..4bac7f75c 100644 --- a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/WebRequestContext.java +++ b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/WebRequestContext.java @@ -1,12 +1,9 @@ package com.sdl.webapp.common.api; -import com.sdl.webapp.common.api.contextengine.ContextEngine; import com.sdl.webapp.common.api.localization.Localization; import com.sdl.webapp.common.api.model.PageModel; import com.sdl.webapp.common.api.model.RegionModel; -import javax.servlet.ServletRequest; - /** * Provides information relevant for the current request. */ @@ -14,54 +11,28 @@ public interface WebRequestContext { /** * Gets the base URL for the current request. The base URL consists of the protocol, server name, and port number. - * It does not include the context path of the web application. + * It does not include the context path of the web application. Does not end with a slash. * * @return The base URL for the current request. */ String getBaseUrl(); /** - * Sets the base URL for the current request. - * - * @param baseUrl The base URL for the current request. - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setBaseUrl(String baseUrl); - - /** - * Gets the context path of the web application. + * Gets the context path of the web application in a servlet container. For root applications return an empty string. + * Does not end with a slash. * * @return The context path of the web application. */ String getContextPath(); - /** - * Sets the context path of the web application. - * - * @param contextPath The context path of the web application. - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setContextPath(String contextPath); - /** * Gets the request path of the current request. This path is relative to the context path of the web application. - * If the current request is an include, this returns the path of the original request. + * If the current request is an include, this returns the path of the original request. Does not end with a slash. * * @return The request path of the current request. */ String getRequestPath(); - /** - * Sets the request path of the current request. - * - * @param requestPath The request path of the current request. - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setRequestPath(String requestPath); - /** * Gets the full URL of the current request, consisting of the base URL, context path and request path. * @@ -71,12 +42,6 @@ public interface WebRequestContext { boolean isContextCookiePresent(); - /** - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setContextCookiePresent(boolean present); - /** * Gets the localization of the current request. * @@ -84,15 +49,6 @@ public interface WebRequestContext { */ Localization getLocalization(); - /** - * Sets the localization of the current request. - * - * @param localization a {@link com.sdl.webapp.common.api.localization.Localization} object. - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setLocalization(Localization localization); - /** * Checks if the web application is in preview mode (when XPM is enabled). * @@ -142,39 +98,6 @@ public interface WebRequestContext { */ void popContainerSize(); - /** - * Gets the contextengine of the current request. - * - * @return The contextengine of the current request. - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - ContextEngine getContextEngine(); - - /** - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - boolean isNoLocalization(); - - /** - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setNoLocalization(boolean value); - - /** - * @deprecated since 1.6, use {@link #getPage()} - */ - @Deprecated - String getPageId(); - - /** - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setPageId(String value); - /** * Return the current page model. * @@ -187,32 +110,12 @@ public interface WebRequestContext { boolean isDeveloperMode(); - /** - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setDeveloperMode(boolean value); - boolean isInclude(); - /** - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - void setInclude(boolean value); - RegionModel getParentRegion(); void pushParentRegion(RegionModel value); void popParentRegion(); - /** - *

Current servlet request.

- * - * @return a {@link javax.servlet.ServletRequest} object. - * @deprecated since 1.5, will be removed in 2.0 - */ - @Deprecated - ServletRequest getServletRequest(); } diff --git a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/localization/Localization.java b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/localization/Localization.java index 76f071ea5..cad012826 100644 --- a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/localization/Localization.java +++ b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/api/localization/Localization.java @@ -32,6 +32,16 @@ public interface Localization { */ boolean isStaticContent(String url); + /** + * Determines if the specified URL refers to static content within the webapp + * + * @return {@code true} if the specified URL refers to static content within the webapp, {@code false} otherwise. + * @param url a {@link java.lang.String} object. + */ + default boolean isNonPublishedAsset(String url) { + return false; + } + /** *

isDefault.

* @@ -120,5 +130,13 @@ public interface Localization { */ List getDataFormats(); + /** + * Gets URI scheme used inside CM-URIs + * + * @return The URI scheme + */ + default String getCmUriScheme() { + return "tcm"; + } } diff --git a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/PageController.java b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/PageController.java index 074697e38..ee08247e8 100644 --- a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/PageController.java +++ b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/PageController.java @@ -139,7 +139,7 @@ public String handleGetPage(HttpServletRequest request, HttpServletResponse resp request.setAttribute(MEDIAHELPER, mediaHelper); request.setAttribute(SCREEN_WIDTH, mediaHelper.getScreenWidth()); request.setAttribute(SOCIALSHARE_URL, webRequestContext.getFullUrl()); - request.setAttribute(CONTEXTENGINE, webRequestContext.getContextEngine()); + request.setAttribute(CONTEXTENGINE, context); final MvcData mvcData = page.getMvcData(); log.trace("Page MvcData: {}", mvcData); @@ -249,7 +249,7 @@ public String handleNotFoundException(HttpServletRequest request, HttpServletRes request.setAttribute(MEDIAHELPER, mediaHelper); request.setAttribute(SCREEN_WIDTH, mediaHelper.getScreenWidth()); request.setAttribute(SOCIALSHARE_URL, webRequestContext.getFullUrl()); - request.setAttribute(CONTEXTENGINE, webRequestContext.getContextEngine()); + request.setAttribute(CONTEXTENGINE, context); response.setStatus(SC_NOT_FOUND); return this.viewNameResolver.resolveView(pageModel.getMvcData(), "Page"); diff --git a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/SiteMapXmlController.java b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/SiteMapXmlController.java index ca0db6f2c..88fbcd66f 100644 --- a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/SiteMapXmlController.java +++ b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/controller/SiteMapXmlController.java @@ -42,17 +42,17 @@ public SiteMapXmlController(WebRequestContext webRequestContext, NavigationProvi this.markup = markup; } - private static void writeSitemapItemsXml(Collection items, StringBuilder builder) { + private static void writeSitemapItemsXml(Collection items, StringBuilder builder, String baseUrl) { for (SitemapItem item : items) { if ("Page".equals(item.getType()) && item.getUrl().startsWith("/")) { builder.append(""); - builder.append("").append(item.getUrl()).append(""); + builder.append("").append(baseUrl).append(item.getUrl()).append(""); if (item.getPublishedDate() != null) { builder.append("").append(item.getPublishedDate()).append(""); } builder.append(""); } else { - writeSitemapItemsXml(item.getItems(), builder); + writeSitemapItemsXml(item.getItems(), builder, baseUrl); } } } @@ -62,7 +62,7 @@ private static void writeSitemapItemsXml(Collection items, StringBu * * @throws NavigationProviderException If an error occurs so that the navigation data cannot be retrieved. */ - @RequestMapping(value = "/sitemap.xml", produces = MediaType.APPLICATION_XML_VALUE) + @RequestMapping(value = {"/sitemap.xml", "/{path}/sitemap.xml"}, produces = MediaType.APPLICATION_XML_VALUE) @ResponseBody public String handleGetSiteMapXml(HttpServletResponse response) throws NavigationProviderException { LOG.trace("handleGetSiteMapXml"); @@ -72,7 +72,7 @@ public String handleGetSiteMapXml(HttpServletResponse response) throws Navigatio StringBuilder builder = new StringBuilder(); builder.append(""); builder.append(""); - writeSitemapItemsXml(navigationModel.getItems(), builder); + writeSitemapItemsXml(navigationModel.getItems(), builder, webRequestContext.getBaseUrl()); builder.append(""); return builder.toString(); diff --git a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/MimeUtils.java b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/MimeUtils.java index 9edabb4fc..a6098a0f0 100644 --- a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/MimeUtils.java +++ b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/MimeUtils.java @@ -9,7 +9,9 @@ /** *

MimeUtils class.

+ * @deprecated since 1.8, to remove in DXA 2.0 */ +@Deprecated public final class MimeUtils { static HashMap MIME_TYPES = new HashMap<>(); @@ -27,6 +29,7 @@ public final class MimeUtils { MIME_TYPES.put("au", "audio/basic"); MIME_TYPES.put("avi", "video/x-msvideo"); MIME_TYPES.put("bin", "application/octet-stream"); + MIME_TYPES.put("bmp", "image/bmp"); MIME_TYPES.put("cab", "application/x-cabinet"); MIME_TYPES.put("cdf", "application/x-netcdf"); MIME_TYPES.put("class", "application/java-vm"); diff --git a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/TcmUtils.java b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/TcmUtils.java index d45b19d2d..e60f92bc3 100644 --- a/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/TcmUtils.java +++ b/dxa-framework/dxa-common-api/src/main/java/com/sdl/webapp/common/util/TcmUtils.java @@ -1,11 +1,18 @@ package com.sdl.webapp.common.util; import com.sdl.webapp.common.api.model.entity.SitemapItem; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.sdl.webapp.common.util.TcmUtils.Namespace.TCM; + /** * Simple utility functions to process TCM-URIs. */ @@ -18,11 +25,11 @@ public final class TcmUtils { private static final int PAGE_ITEM_TYPE = 64; - private static final String TCM_S_S = "tcm:%s-%s"; + private static final String S_S_S = "%s:%s-%s"; - private static final String TCM_S_S_S = "tcm:%s-%s-%s"; + private static final String S_S_S_S = "%s:%s-%s-%s"; - private static final Pattern PATTERN = Pattern.compile("tcm:(\\d+)-(\\d+)(-(\\d+))?"); + private static final Pattern PATTERN = Pattern.compile("(\\w+):(\\d+)-(\\d+)(-(\\d+))?"); private TcmUtils() { } @@ -34,7 +41,22 @@ private TcmUtils() { * @return TCM URI for publication */ public static String buildPublicationTcmUri(int publicationId) { - return buildPublicationTcmUriInternal(String.valueOf(publicationId)); + return buildPublicationCmUri(TCM, publicationId); + } + + /** + * Build a publication CM URI looking like namespace:0-ID-1. + * + * @param namespace CM URI namespace + * @param publicationId publication ID + * @return TCM URI for publication + */ + public static String buildPublicationCmUri(Namespace namespace, int publicationId) { + return buildPublicationCmUriInternal(namespace, String.valueOf(publicationId)); + } + + private static String buildPublicationCmUriInternal(Namespace namespace, String publicationId) { + return String.format(S_S_S_S, namespace.getValue(), 0, publicationId, PUBLICATION_ITEM_TYPE); } /** @@ -44,11 +66,18 @@ public static String buildPublicationTcmUri(int publicationId) { * @return TCM URI for publication */ public static String buildPublicationTcmUri(String publicationId) { - return buildPublicationTcmUriInternal(publicationId); + return buildPublicationCmUri(TCM, publicationId); } - private static String buildPublicationTcmUriInternal(String publicationId) { - return String.format(TCM_S_S_S, 0, publicationId, PUBLICATION_ITEM_TYPE); + /** + * Build a publication CM URI looking like namespace:0-ID-1. + * + * @param namespace CM URI namespace + * @param publicationId publication ID + * @return TCM URI for publication + */ + public static String buildPublicationCmUri(Namespace namespace, String publicationId) { + return buildPublicationCmUriInternal(namespace, publicationId); } /** @@ -59,7 +88,19 @@ private static String buildPublicationTcmUriInternal(String publicationId) { * @return TCM URI for template */ public static String buildTemplateTcmUri(String publicationId, String itemId) { - return String.format(TCM_S_S_S, publicationId, itemId, TEMPLATE_ITEM_TYPE); + return buildTemplateCmUri(TCM, publicationId, itemId); + } + + /** + * Build a template CM URI looking like namespace:PUB_ID-ITEM_ID-32. + * + * @param namespace CM URI namespace + * @param publicationId publication ID + * @param itemId item ID + * @return TCM URI for template + */ + public static String buildTemplateCmUri(Namespace namespace, String publicationId, String itemId) { + return String.format(S_S_S_S, namespace.getValue(), publicationId, itemId, TEMPLATE_ITEM_TYPE); } /** @@ -70,7 +111,19 @@ public static String buildTemplateTcmUri(String publicationId, String itemId) { * @return TCM URI for page */ public static String buildPageTcmUri(String publicationId, String itemId) { - return String.format(TCM_S_S_S, publicationId, itemId, PAGE_ITEM_TYPE); + return buildPageCmUri(TCM, publicationId, itemId); + } + + /** + * Build a template CM URI looking like namespace:PUB_ID-ITEM_ID-32. + * + * @param namespace CM URI namespace + * @param publicationId publication ID + * @param itemId item ID + * @return TCM URI for page + */ + public static String buildPageCmUri(Namespace namespace, String publicationId, String itemId) { + return String.format(S_S_S_S, namespace.getValue(), publicationId, itemId, PAGE_ITEM_TYPE); } /** @@ -81,7 +134,23 @@ public static String buildPageTcmUri(String publicationId, String itemId) { * @return short TCM URI */ public static String buildTcmUri(String publicationId, String itemId) { - return buildTcmUriInternal(publicationId, itemId); + return buildCmUri(TCM, publicationId, itemId); + } + + /** + * Build a short CM URI looking like namespace:PUB_ID-ITEM_ID. + * + * @param namespace CM URI namespace + * @param publicationId publication ID + * @param itemId item ID + * @return short CM URI + */ + public static String buildCmUri(Namespace namespace, String publicationId, String itemId) { + return buildCmUriInternal(namespace, publicationId, itemId); + } + + private static String buildCmUriInternal(Namespace namespace, String publicationId, String itemId) { + return String.format(S_S_S, namespace.getValue(), publicationId, itemId); } /** @@ -92,11 +161,19 @@ public static String buildTcmUri(String publicationId, String itemId) { * @return short TCM URI */ public static String buildTcmUri(int publicationId, int itemId) { - return buildTcmUriInternal(String.valueOf(publicationId), String.valueOf(itemId)); + return buildCmUri(TCM, publicationId, itemId); } - private static String buildTcmUriInternal(String publicationId, String itemId) { - return String.format(TCM_S_S, publicationId, itemId); + /** + * Build a short CM URI looking like namespace:PUB_ID-ITEM_ID. + * + * @param namespace CM URI namespace + * @param publicationId publication ID + * @param itemId item ID + * @return short CM URI + */ + public static String buildCmUri(Namespace namespace, int publicationId, int itemId) { + return buildCmUriInternal(namespace, String.valueOf(publicationId), String.valueOf(itemId)); } /** @@ -108,7 +185,24 @@ private static String buildTcmUriInternal(String publicationId, String itemId) { * @return a TCM URI */ public static String buildTcmUri(String publicationId, String itemId, String itemType) { - return buildTcmUriInternal(publicationId, itemId, itemType); + return buildCmUri(TCM, publicationId, itemId, itemType); + } + + /** + * Build a CM URI looking like namespace:PUB_ID-ITEM_ID-ITEM_TYPE. + * + * @param namespace CM URI namespace + * @param publicationId publication ID + * @param itemId item ID + * @param itemType item type + * @return a CM URI + */ + public static String buildCmUri(Namespace namespace, String publicationId, String itemId, String itemType) { + return buildCmUriInternal(namespace, publicationId, itemId, itemType); + } + + public static String buildCmUriInternal(Namespace namespace, String publicationId, String itemId, String itemType) { + return String.format(S_S_S_S, namespace.getValue(), publicationId, itemId, itemType); } /** @@ -120,40 +214,39 @@ public static String buildTcmUri(String publicationId, String itemId, String ite * @return a TCM URI */ public static String buildTcmUri(int publicationId, int itemId, int itemType) { - return buildTcmUriInternal(String.valueOf(publicationId), String.valueOf(itemId), String.valueOf(itemType)); - } - - public static String buildTcmUriInternal(String publicationId, String itemId, String itemType) { - return String.format(TCM_S_S_S, publicationId, itemId, itemType); + return buildCmUri(TCM, publicationId, itemId, itemType); } /** - * Extracts item ID from a valid TCM URI. + * Build a CM URI looking like namespace:PUB_ID-ITEM_ID-ITEM_TYPE. * - * @param tcmUri tcm uri to process - * @return item ID or -1 if URI is not valid or null + * @param namespace CM URI namespace + * @param publicationId publication ID + * @param itemId item ID + * @param itemType item type + * @return a CM URI */ - public static int getItemId(String tcmUri) { - return extractGroupFromTcm(tcmUri, 2); + public static String buildCmUri(Namespace namespace, int publicationId, int itemId, int itemType) { + return buildCmUriInternal(namespace, String.valueOf(publicationId), String.valueOf(itemId), String.valueOf(itemType)); } /** - * Extracts publication ID from a valid TCM URI. + * Extracts publication ID from a valid CM URI. * - * @param tcmUri tcm uri to process + * @param cmUri cm uri to process * @return publication ID or -1 if URI is not valid or null */ - public static int getPublicationId(String tcmUri) { - return extractGroupFromTcm(tcmUri, 1); + public static int getPublicationId(String cmUri) { + return extractGroupFromCm(cmUri, 2); } - private static int extractGroupFromTcm(String tcmUri, int group) { + private static int extractGroupFromCm(String cmUri, int group) { int failed = -1; - if (tcmUri == null) { + if (cmUri == null) { return failed; } - Matcher matcher = PATTERN.matcher(tcmUri); + Matcher matcher = PATTERN.matcher(cmUri); return matcher.matches() ? Integer.parseInt(matcher.group(group)) : failed; } @@ -166,8 +259,52 @@ private static int extractGroupFromTcm(String tcmUri, int group) { * @return localized TCM URI of an item */ public static String localizeTcmUri(String tcmUri, String publicationTcmUri) { - int publicationId = getItemId(publicationTcmUri); - return localizeTcmUri(tcmUri, publicationId); + return localizeCmUri(TCM, tcmUri, publicationTcmUri); + } + + /** + * Localizes given CM URI to current publication. + *

E.g. namespace:1-2-3 with publication URI namespace:0-8-1 will look like namespace:8-2-3.

+ * + * @param namespace CM URI namespace + * @param cmUri cm uri of item to localize + * @param publicationCmUri CM URI of publication + * @return localized CM URI of an item + */ + public static String localizeCmUri(Namespace namespace, String cmUri, String publicationCmUri) { + int publicationId = getItemId(publicationCmUri); + return localizeCmUri(namespace, cmUri, publicationId); + } + + /** + * Extracts item ID from a valid CM URI. + * + * @param cmUri cm uri to process + * @return item ID or -1 if URI is not valid or null + */ + public static int getItemId(String cmUri) { + return extractGroupFromCm(cmUri, 3); + } + + /** + * Localizes given CM URI to current publication. + *

E.g. namespace:1-2-3 with publication ID 8 will look like namespace:8-2-3.

+ * + * @param namespace CM URI namespace + * @param cmUri cm uri of item to localize + * @param publicationId publication ID + * @return localized CM URI of an item + */ + public static String localizeCmUri(Namespace namespace, String cmUri, int publicationId) { + Matcher matcher = PATTERN.matcher(cmUri); + if (!matcher.matches()) { + log.warn("CM URI {} is not valid", cmUri); + throw new IllegalArgumentException("CM URI is not valid: " + cmUri); + } + String itemType = matcher.group(5); + return itemType == null ? buildCmUri(namespace, publicationId, getItemId(cmUri)) : + buildCmUri(namespace, publicationId, getItemId(cmUri), Integer.parseInt(itemType)); + } /** @@ -179,15 +316,30 @@ public static String localizeTcmUri(String tcmUri, String publicationTcmUri) { * @return localized TCM URI of an item */ public static String localizeTcmUri(String tcmUri, int publicationId) { - Matcher matcher = PATTERN.matcher(tcmUri); - if (!matcher.matches()) { - log.warn("TCM URI {} is not valid", tcmUri); - throw new IllegalArgumentException("TCM URI is not valid: " + tcmUri); + return localizeCmUri(TCM, tcmUri, publicationId); + } + + @AllArgsConstructor + public enum Namespace { + TCM("tcm"), + ISH("ish"); + + private static final Map namespaces = Collections.unmodifiableMap(initialize()); + + @Getter + private String value; + + private static Map initialize() { + Map result = new HashMap<>(); + for (Namespace n : Namespace.values()) { + result.put(n.value, n); + } + return result; } - String itemType = matcher.group(4); - return itemType == null ? buildTcmUri(publicationId, getItemId(tcmUri)) : - buildTcmUri(publicationId, getItemId(tcmUri), Integer.parseInt(itemType)); + public static Namespace getNamespaceFor(String value) { + return namespaces.get(value); + } } /** diff --git a/dxa-framework/dxa-common-impl/pom.xml b/dxa-framework/dxa-common-impl/pom.xml index c0921947c..2d14b9fac 100644 --- a/dxa-framework/dxa-common-impl/pom.xml +++ b/dxa-framework/dxa-common-impl/pom.xml @@ -7,7 +7,7 @@ dxa-framework com.sdl.dxa - 1.8.0-SNAPSHOT + 1.8.0 dxa-common-impl diff --git a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/WebRequestContextImpl.java b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/WebRequestContextImpl.java index ccd6f4aae..1363e4cb7 100644 --- a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/WebRequestContextImpl.java +++ b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/WebRequestContextImpl.java @@ -30,7 +30,7 @@ import java.util.Stack; /** - *

This implementation gets information about the display width etc. from the Ambient Data Framework.

+ * This implementation gets information about the display width etc. from the Ambient Data Framework. */ @Slf4j @Primary @@ -40,14 +40,33 @@ public class WebRequestContextImpl implements WebRequestContext { private final UrlPathHelper urlPathHelper = new UrlPathHelper(); + @Getter(lazy = true) + private final Boolean noLocalization = noLocalization(); + @Autowired private MediaHelper mediaHelper; - @Getter @Autowired private HttpServletRequest servletRequest; - @Getter + @Getter(lazy = true) + private final boolean include = include(); + + @Getter(lazy = true) + private final String contextPath = contextPath(); + + @Getter(lazy = true) + private final String requestPath = requestPath(); + + @Getter(lazy = true) + private final String baseUrl = baseUrl(); + + @Getter(lazy = true) + private final boolean developerMode = developerMode(); + + @Getter(lazy = true) + private final boolean contextCookiePresent = contextCookiePresent(); + @Autowired private ContextEngine contextEngine; @@ -63,25 +82,8 @@ public class WebRequestContextImpl implements WebRequestContext { @Autowired(required = false) private UnknownLocalizationHandler unknownLocalizationHandler; - private SettableField include = new SettableField<>(); - - private SettableField contextPath = new SettableField<>(); - - private SettableField requestPath = new SettableField<>(); - - private SettableField baseUrl = new SettableField<>(); - - private SettableField developerMode = new SettableField<>(); - - private SettableField contextCookiePresent = new SettableField<>(); - - private SettableField localization = new SettableField<>(); - - private SettableField noLocalization = new SettableField<>(); - - @Getter - @Setter - private String pageId; + @Getter(lazy = true) + private final Localization localization = localization(); @Getter @Setter @@ -91,97 +93,11 @@ public class WebRequestContextImpl implements WebRequestContext { private Stack containerSizeStack = new Stack<>(); - @Override - public String getBaseUrl() { - if (this.baseUrl.isSet) { - return this.baseUrl.value; - } - String newValue = baseUrl(); - setBaseUrl(newValue); - return newValue; - } - - @Override - public void setBaseUrl(String baseUrl) { - this.baseUrl.value = baseUrl; - this.baseUrl.isSet = true; - } - - @Override - public String getContextPath() { - if (contextPath.isSet) { - return contextPath.value; - } - String newValue = contextPath(); - setContextPath(newValue); - return newValue; - } - - @Override - public void setContextPath(String contextPath) { - this.contextPath.value = contextPath; - this.contextPath.isSet = true; - } - - @Override - public String getRequestPath() { - if (requestPath.isSet) { - return requestPath.value; - } - String newValue = requestPath(); - setRequestPath(newValue); - return newValue; - } - - @Override - public void setRequestPath(String requestPath) { - this.requestPath.value = requestPath; - this.requestPath.isSet = true; - } - - /** - * {@inheritDoc} - */ @Override public String getFullUrl() { return getBaseUrl() + getContextPath() + getRequestPath(); } - @Override - public boolean isContextCookiePresent() { - if (this.contextCookiePresent.isSet) { - return this.contextCookiePresent.value; - } - boolean value = contextCookiePresent(); - setContextCookiePresent(value); - return value; - } - - @Override - public void setContextCookiePresent(boolean present) { - this.contextCookiePresent.value = present; - this.contextCookiePresent.isSet = true; - } - - @Override - public Localization getLocalization() { - if (localization.isSet) { - return localization.value; - } - Localization value = localization(); - setLocalization(value); - return value; - } - - @Override - public void setLocalization(Localization localization) { - this.localization.value = localization; - this.localization.isSet = true; - } - - /** - * {@inheritDoc} - */ @Override public boolean isPreview() { // Should return true if the request is from XPM (NOTE currently always true for staging as we cannot reliably @@ -191,122 +107,68 @@ public boolean isPreview() { @Override public ScreenWidth getScreenWidth() { - return screenWidth(); + int width = isContextCookiePresent() ? getDisplayWidth() : Width.MAX_WIDTH; + + if (width < mediaHelper.getSmallScreenBreakpoint()) { + return ScreenWidth.EXTRA_SMALL; + } + + if (width < mediaHelper.getMediumScreenBreakpoint()) { + return ScreenWidth.SMALL; + } + + if (width < mediaHelper.getLargeScreenBreakpoint()) { + return ScreenWidth.MEDIUM; + } + + return ScreenWidth.LARGE; } @Override public int getMaxMediaWidth() { - return maxMediaWidth(); + return (int) Math.max(1.0, getPixelRatio()) * Math.min(getDisplayWidth(), Width.MAX_WIDTH); } - /** - * {@inheritDoc} - */ @Override public int getContainerSize() { return containerSizeStack.peek(); } - /** - * {@inheritDoc} - */ @Override public void pushContainerSize(int containerSize) { + int size = containerSize; if (containerSize == 0) { - containerSize = mediaHelper.getGridSize(); + size = mediaHelper.getGridSize(); } if (!containerSizeStack.isEmpty()) { int parentContainerSize = containerSizeStack.peek(); - containerSize = containerSize * parentContainerSize / mediaHelper.getGridSize(); + size = containerSize * parentContainerSize / mediaHelper.getGridSize(); } - containerSizeStack.push(containerSize); + containerSizeStack.push(size); } - /** - * {@inheritDoc} - */ @Override public void popContainerSize() { containerSizeStack.pop(); } - @Override - public boolean isNoLocalization() { - if (noLocalization.isSet) { - return noLocalization.value; - } - return getLocalization() == null; - } - - @Override - public void setNoLocalization(boolean value) { - this.noLocalization.value = value; - this.noLocalization.isSet = true; - } - - @Override - public boolean isDeveloperMode() { - if (developerMode.isSet) { - return developerMode.value; - } - boolean value = developerMode(); - setDeveloperMode(value); - return value; - } - - @Override - public void setDeveloperMode(boolean value) { - this.developerMode.value = value; - this.developerMode.isSet = true; - } - - @Override - public boolean isInclude() { - if (include.isSet) { - return include.value; - } - - boolean newValue = include(); - setInclude(newValue); - return newValue; - } - - @Override - public void setInclude(boolean value) { - include.value = value; - include.isSet = true; - } - - /** - * {@inheritDoc} - */ @Override public RegionModel getParentRegion() { return parentRegionStack.isEmpty() ? null : parentRegionStack.peek(); } - /** - * {@inheritDoc} - */ @Override public void pushParentRegion(RegionModel parentRegion) { parentRegionStack.push(parentRegion); } - /** - * {@inheritDoc} - */ @Override public void popParentRegion() { parentRegionStack.pop(); } private String baseUrl() { - String baseUrl = servletRequest.getRequestURL().toString(); - if (baseUrl.endsWith("/")) { - baseUrl = baseUrl.substring(0, baseUrl.length() - 1); - } - return baseUrl; + return servletRequest.getRequestURL().toString().replaceFirst(servletRequest.getRequestURI() + "$", ""); } private String contextPath() { @@ -314,7 +176,7 @@ private String contextPath() { } private String requestPath() { - return urlPathHelper.getOriginatingRequestUri(servletRequest); + return urlPathHelper.getPathWithinApplication(servletRequest); } private boolean developerMode() { @@ -333,32 +195,35 @@ private boolean contextCookiePresent() { return false; } + private boolean noLocalization() { + return getLocalization() == null; + } + private Localization localization() { - Localization localization = null; + Localization resolveLocalization = null; try { - localization = localizationResolver.getLocalization(getFullUrl()); + resolveLocalization = localizationResolver.getLocalization(getFullUrl()); } catch (LocalizationResolverException e) { if (unknownLocalizationHandler != null) { log.info("Localization is not resolved for {}, Localization handler is set, trying to resolve using it", getFullUrl()); - localization = unknownLocalizationHandler.handleUnknown(e, servletRequest); - if (localization == null) { + resolveLocalization = unknownLocalizationHandler.handleUnknown(e, servletRequest); + if (resolveLocalization == null) { log.info("Unknown Localization handler is set but localization wasn't resolved with it, fallback"); - LocalizationNotResolvedException fallbackException = - unknownLocalizationHandler.getFallbackException(e, servletRequest); + LocalizationNotResolvedException fallbackException = unknownLocalizationHandler.getFallbackException(e, servletRequest); if (fallbackException != null) { throw fallbackException; } log.info("Fallback exception from Unknown Localization Handler is null, fallback to default handling"); } } - if (localization == null) { + if (resolveLocalization == null) { throw new LocalizationNotFoundException("Localization not found", e); } } if (log.isTraceEnabled()) { - log.trace("Localization for {} is: [{}] {}", getFullUrl(), localization.getId(), localization.getPath()); + log.trace("Localization for {} is: [{}] {}", getFullUrl(), resolveLocalization.getId(), resolveLocalization.getPath()); } - return localization; + return resolveLocalization; } public boolean include() { @@ -366,62 +231,43 @@ public boolean include() { } private int displayWidth() { - Integer displayWidth = contextEngine.getClaims(BrowserClaims.class).getDisplayWidth(); - if (displayWidth == null) { - return Width.defaultWidth; - } - - // NOTE: The context engine uses a default browser width of 800, which we override to 1024 - if (displayWidth == 800 && contextCookiePresent()) { - return Width.defaultWidth; + BrowserClaims claims = contextEngine.getClaims(BrowserClaims.class); + if (claims == null) { + return Width.DEFAULT_WIDTH; } - return displayWidth; - } - - private ScreenWidth screenWidth() { - int width = isContextCookiePresent() ? getDisplayWidth() : Width.maxWidth; - - if (width < mediaHelper.getSmallScreenBreakpoint()) { - return ScreenWidth.EXTRA_SMALL; + Integer resolveDisplayWidth = claims.getDisplayWidth(); + if (resolveDisplayWidth == null) { + return Width.DEFAULT_WIDTH; } - if (width < mediaHelper.getMediumScreenBreakpoint()) { - return ScreenWidth.SMALL; - } - - if (width < mediaHelper.getLargeScreenBreakpoint()) { - return ScreenWidth.MEDIUM; + // NOTE: The context engine uses a default browser width of 800, which we override to 1024 + if (resolveDisplayWidth == 800 && contextCookiePresent()) { + return Width.DEFAULT_WIDTH; } - return ScreenWidth.LARGE; - } - - private int maxMediaWidth() { - return (int) Math.max(1.0, getPixelRatio()) * Math.min(getDisplayWidth(), Width.maxWidth); + return resolveDisplayWidth; } private double pixelRatio() { - Double pixelRatio = contextEngine.getClaims(DeviceClaims.class).getPixelRatio(); - if (pixelRatio == null) { - pixelRatio = 1.0; - log.debug("Pixel ratio ADF claim not available - using default value: {}", pixelRatio); - } - return pixelRatio; - } - - private interface Width { + DeviceClaims claims = contextEngine.getClaims(DeviceClaims.class); - int defaultWidth = 1024; + Double resolvePixelRatio; + if (claims == null || (resolvePixelRatio = claims.getPixelRatio()) == null) { + resolvePixelRatio = 1.0; + log.debug("Pixel ratio ADF claim not available - using default value: {}", resolvePixelRatio); + } - int maxWidth = 1024; + return resolvePixelRatio; } - //todo dxa2 replace with @Getter(lazy=true) - private static class SettableField { + private class Width { + + static final int DEFAULT_WIDTH = 1024; - T value; + static final int MAX_WIDTH = 1024; - boolean isSet; + private Width() { + } } } diff --git a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/interceptor/StaticContentInterceptor.java b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/interceptor/StaticContentInterceptor.java index bbfc114f7..bdeaf5d0e 100644 --- a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/interceptor/StaticContentInterceptor.java +++ b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/interceptor/StaticContentInterceptor.java @@ -8,6 +8,7 @@ import com.sdl.webapp.common.api.localization.Localization; import com.sdl.webapp.common.util.MimeUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.joda.time.Hours; import org.joda.time.Weeks; import org.slf4j.Logger; @@ -27,6 +28,7 @@ import java.io.OutputStream; import java.lang.management.ManagementFactory; import java.net.URL; +import java.util.regex.Pattern; /** * Static content interceptor. This interceptor checks if the request is for static content, and if it is, it sends @@ -38,6 +40,7 @@ public class StaticContentInterceptor extends HandlerInterceptorAdapter { private static final Logger LOG = LoggerFactory.getLogger(StaticContentInterceptor.class); private static final String CACHE_CONTROL_WEEK = "public, max-age=" + Weeks.ONE.toStandardSeconds().getSeconds(); private static final String CACHE_CONTROL_HOUR = "public, max-age=" + Hours.ONE.toStandardSeconds().getSeconds(); + private static final Pattern SYSTEM_VERSION_PATTERN = Pattern.compile("/system/v\\d+\\.\\d+/"); @Autowired private ContentProvider contentProvider; @@ -89,9 +92,10 @@ private static void fallbackForContentProvider(ServletServerHttpRequest req, Str /** * {@inheritDoc} + * @throws IOException */ @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException { + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException { final String requestPath = webRequestContext.getRequestPath(); LOG.trace("preHandle: {}", requestPath); @@ -103,12 +107,16 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons final ServletServerHttpResponse res = new ServletServerHttpResponse(response); try { + if (localization.isNonPublishedAsset(requestPath)) + { + fallbackForContentProvider(req, removeVersionNumber(requestPath), res); + } StaticContentItem staticContentItem = null; try { staticContentItem = contentProvider.getStaticContent(requestPath, localization.getId(), localization.getPath()); } catch (StaticContentNotFoundException e) { - fallbackForContentProvider(req, requestPath, res); + fallbackForContentProvider(req, removeVersionNumber(requestPath), res); } if (staticContentItem != null) { @@ -131,7 +139,10 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons res.close(); return false; } - return true; } + + protected static String removeVersionNumber(String path) { + return SYSTEM_VERSION_PATTERN.matcher(path).replaceFirst("/system/"); + } } diff --git a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationFactoryImpl.java b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationFactoryImpl.java index d4d7635f9..2c587bc2f 100644 --- a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationFactoryImpl.java +++ b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationFactoryImpl.java @@ -125,14 +125,16 @@ private boolean loadVersionFromProperties(String id, String path, LocalizationIm return false; } builder.setVersion(assetsVersion); + builder.setHtmlDesignPublished(false); return true; -} + } private boolean loadVersionFromBroker(String id, String path, LocalizationImpl.Builder builder) throws LocalizationFactoryException { try { StaticContentItem item = contentProvider.getStaticContent(VERSION_PATH, id, path); try (final InputStream in = item.getContent()) { builder.setVersion(objectMapper.readTree(in).get("version").asText()); + builder.setHtmlDesignPublished(true); return true; } } catch (StaticContentNotFoundException e) { @@ -153,6 +155,7 @@ private boolean loadVersionFromWebapp(String id, String path, LocalizationImpl.B try (final InputStream in = new FileInputStream(file)) { builder.setVersion(objectMapper.readTree(in).get("version").asText()); + builder.setHtmlDesignPublished(false); return true; } catch (IOException e) { throw new LocalizationFactoryException("Exception while reading configuration of localization: [" + id + diff --git a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationImpl.java b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationImpl.java index 8e8f83716..bd1072f8e 100644 --- a/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationImpl.java +++ b/dxa-framework/dxa-common-impl/src/main/java/com/sdl/webapp/common/impl/localization/LocalizationImpl.java @@ -24,6 +24,7 @@ public class LocalizationImpl implements Localization { private static final String FAVICON_PATH = "/favicon.ico"; private static final Pattern SYSTEM_ASSETS_PATTERN = Pattern.compile("/system(/v\\d+\\.\\d+)?/assets/.*"); + private static final Pattern SYSTEM_RESOURCES_PATTERN = Pattern.compile("/system(/v\\d+\\.\\d+)?/(resources|config)/.*"); @Getter private final String id; @@ -41,6 +42,9 @@ public class LocalizationImpl implements Localization { @Getter private final String version; + @Getter + private final boolean isHtmlDesignPublished; + @Getter private final List siteLocalizations; @@ -67,7 +71,7 @@ private LocalizationImpl(Builder builder) { this.default_ = builder.default_; this.staging = builder.staging; this.version = builder.version; - + this.isHtmlDesignPublished = builder.htmlDesignPublished; this.siteLocalizations = builder.siteLocalizationsBuilder.build(); this.configuration = builder.configurationBuilder.build(); this.resources = builder.resourcesBuilder.build(); @@ -96,11 +100,21 @@ public boolean isStaticContent(String url) { if (url.startsWith(mediaRoot)) { return true; } - final String p = path.equals("/") ? url : url.substring(path.length()); + if (SYSTEM_RESOURCES_PATTERN.matcher(p).matches()) { + return true; + } return p.equals(FAVICON_PATH) || SYSTEM_ASSETS_PATTERN.matcher(p).matches(); } + /** + * {@inheritDoc} + */ + @Override + public boolean isNonPublishedAsset(String url) { + return !this.isHtmlDesignPublished && SYSTEM_ASSETS_PATTERN.matcher(url).matches(); + } + /** * {@inheritDoc} */ @@ -173,6 +187,7 @@ public static final class Builder { private boolean default_; private boolean staging; private String version; + private boolean htmlDesignPublished; private Builder() { } @@ -207,6 +222,11 @@ public Builder setVersion(String version) { return this; } + public Builder setHtmlDesignPublished(boolean htmlDesignPublished) { + this.htmlDesignPublished = htmlDesignPublished; + return this; + } + public Builder addSiteLocalizations(Iterable siteLocalizations) { this.siteLocalizationsBuilder.addAll(siteLocalizations); return this; diff --git a/dxa-framework/dxa-common-impl/src/test/java/com/sdl/webapp/common/impl/WebRequestContextImplTest.java b/dxa-framework/dxa-common-impl/src/test/java/com/sdl/webapp/common/impl/WebRequestContextImplTest.java index 646f3fb66..b23ec701d 100644 --- a/dxa-framework/dxa-common-impl/src/test/java/com/sdl/webapp/common/impl/WebRequestContextImplTest.java +++ b/dxa-framework/dxa-common-impl/src/test/java/com/sdl/webapp/common/impl/WebRequestContextImplTest.java @@ -17,13 +17,13 @@ import org.springframework.test.util.ReflectionTestUtils; import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -38,11 +38,17 @@ public class WebRequestContextImplTest { @Mock private LocalizationResolver localizationResolver; + @Mock + private HttpServletRequest servletRequest; + @Before public void init() { - doReturn("Hello").when(webRequestContext).getFullUrl(); + when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("http://localhost:8080/context/publication/request")); + when(servletRequest.getRequestURI()).thenReturn("/context/publication/request"); + when(servletRequest.getContextPath()).thenReturn("/context"); } + @Test(expected = LocalizationNotFoundException.class) public void shouldThrowExceptionIfNoLocalizationFoundWithSpecialHandler() throws Exception { //given @@ -139,4 +145,53 @@ public void shouldThrowExceptionIfNoLocalizationFoundWithoutSpecialHandler() thr //then // exception } + + @Test + public void shouldReturnContextPath() { + //when + String contextPath = webRequestContext.getContextPath(); + + //then + assertEquals("/context", contextPath); + } + + @Test + public void shouldReturnRequestPath_RelativeToContext() { + //when + String requestPath = webRequestContext.getRequestPath(); + + //then + assertEquals("/publication/request", requestPath); + } + + @Test + public void shouldReturnBaseUrl() { + //when + String baseUrl = webRequestContext.getBaseUrl(); + + //then + assertEquals("http://localhost:8080", baseUrl); + } + + @Test + public void shouldReturnFullUrl() { + //when + String fullUrl = webRequestContext.getFullUrl(); + + //then + assertEquals("http://localhost:8080/context/publication/request", fullUrl); + } + + @Test + public void shouldBeAwareOfRootRequests() { + //given + when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("http://localhost:8080/")); + when(servletRequest.getRequestURI()).thenReturn("/"); + + //when + String baseUrl = webRequestContext.getBaseUrl(); + + //then + assertEquals("http://localhost:8080", baseUrl); + } } \ No newline at end of file diff --git a/dxa-framework/dxa-tridion-provider/pom.xml b/dxa-framework/dxa-tridion-provider/pom.xml index 75034a1c8..df5718752 100644 --- a/dxa-framework/dxa-tridion-provider/pom.xml +++ b/dxa-framework/dxa-tridion-provider/pom.xml @@ -6,7 +6,7 @@ dxa-framework com.sdl.dxa - 1.8.0-SNAPSHOT + 1.8.0 dxa-tridion-provider diff --git a/dxa-framework/dxa-tridion-provider/src/main/java/com/sdl/webapp/tridion/linking/AbstractTridionLinkResolver.java b/dxa-framework/dxa-tridion-provider/src/main/java/com/sdl/webapp/tridion/linking/AbstractTridionLinkResolver.java index ecdecebd2..23122d887 100644 --- a/dxa-framework/dxa-tridion-provider/src/main/java/com/sdl/webapp/tridion/linking/AbstractTridionLinkResolver.java +++ b/dxa-framework/dxa-tridion-provider/src/main/java/com/sdl/webapp/tridion/linking/AbstractTridionLinkResolver.java @@ -63,7 +63,7 @@ public String resolveLink(String uri, int publicationId, boolean isBinary) { } final int itemId = Integer.parseInt(parts[1]); - final int itemType = parts.length > 2 ? Integer.parseInt(parts[2]) : 16; + final int itemType = parts.length > 2 && !parts[2].startsWith("v") ? Integer.parseInt(parts[2]) : 16; switch (itemType) { case 16: diff --git a/dxa-framework/dxa-web8-provider/pom.xml b/dxa-framework/dxa-web8-provider/pom.xml index 3725e41d6..edbdd2181 100644 --- a/dxa-framework/dxa-web8-provider/pom.xml +++ b/dxa-framework/dxa-web8-provider/pom.xml @@ -4,7 +4,7 @@ dxa-framework com.sdl.dxa - 1.8.0-SNAPSHOT + 1.8.0 ../../dxa-framework 4.0.0 @@ -54,6 +54,10 @@ cil pom + + org.hibernate + hibernate-validator + xalan diff --git a/dxa-framework/dxa-web8-provider/src/main/java/com/sdl/webapp/tridion/BrokerDynamicComponentPresentationProvider.java b/dxa-framework/dxa-web8-provider/src/main/java/com/sdl/webapp/tridion/BrokerDynamicComponentPresentationProvider.java new file mode 100644 index 000000000..6f31f11f1 --- /dev/null +++ b/dxa-framework/dxa-web8-provider/src/main/java/com/sdl/webapp/tridion/BrokerDynamicComponentPresentationProvider.java @@ -0,0 +1,52 @@ +package com.sdl.webapp.tridion; + +import com.sdl.web.api.broker.WebComponentPresentationFactoryImpl; +import com.sdl.web.api.dynamic.ComponentPresentationAssemblerImpl; +import com.sdl.web.api.dynamic.WebComponentPresentationFactory; +import com.sdl.web.api.dynamic.WebComponentPresentationAssembler; +import lombok.extern.slf4j.Slf4j; +import org.dd4t.contentmodel.ComponentPresentation; +import org.dd4t.core.exceptions.ItemNotFoundException; +import org.dd4t.core.exceptions.SerializationException; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Component +@Slf4j +public class BrokerDynamicComponentPresentationProvider extends AbstractBrokerComponentPresentationProvider { + + private static final Map FACTORY_CACHE = new ConcurrentHashMap<>(); + private static final String ERROR_MESSAGE + = "Component Presentation not found for componentId: %d, templateId: %d and publicationId: %d"; + + /** + * {@inheritDoc} + */ + @Override + protected ComponentPresentation getDynamicComponentPresentationInternal(int componentId, int templateId, + int publicationId) + throws ItemNotFoundException, SerializationException { + WebComponentPresentationFactory factory = FACTORY_CACHE.get(publicationId); + if (factory == null) { + factory = new WebComponentPresentationFactoryImpl(publicationId); + FACTORY_CACHE.put(publicationId, factory); + } + + com.tridion.dcp.ComponentPresentation result = templateId != 0 ? + factory.getComponentPresentation(componentId, templateId) : + factory.getComponentPresentationWithHighestPriority(componentId); + + if (result == null) { + log.info(String.format(ERROR_MESSAGE, componentId, templateId, publicationId)); + throw new ItemNotFoundException(String.format(ERROR_MESSAGE, componentId, templateId, publicationId)); + } + + WebComponentPresentationAssembler assembler = new ComponentPresentationAssemblerImpl(publicationId); + String assembledContent = assembler.getContent(result.getComponentId(), result.getComponentTemplateId()); + + return getComponentPresentation(assembledContent); + } + +} \ No newline at end of file diff --git a/dxa-framework/pom.xml b/dxa-framework/pom.xml index 7b1e41278..a15531896 100644 --- a/dxa-framework/pom.xml +++ b/dxa-framework/pom.xml @@ -11,7 +11,7 @@ com.sdl.dxa - 1.8.0-SNAPSHOT + 1.8.0 dxa-framework pom @@ -61,7 +61,7 @@ UTF-8 UTF-8 - 1.8.0-SNAPSHOT + 1.8.0 1.8 diff --git a/dxa-webapp/pom.xml b/dxa-webapp/pom.xml index 958bbd510..6b3a1f7bf 100644 --- a/dxa-webapp/pom.xml +++ b/dxa-webapp/pom.xml @@ -10,7 +10,7 @@ 1.0.3 - 1.8.0-SNAPSHOT + 1.8.0 com.sdl.dxa dxa-webapp war @@ -19,8 +19,8 @@ http://www.sdl.com/cxc/digital-experience/web-experience-management/digital-experience-accelerator.html - 1.8.0-SNAPSHOT - release/1.7 + 1.8.0 + release/1.8 1.8 diff --git a/gradle.properties b/gradle.properties index bfd07ae2b..4bc7430e5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=1.8.0-SNAPSHOT \ No newline at end of file +version=1.8.0 \ No newline at end of file diff --git a/local-project-repo/org/dd4t/dd4t-api/2.1.4-DXA17/dd4t-api-2.1.4-DXA17.jar b/local-project-repo/org/dd4t/dd4t-api/2.0.15.3-DXA18/dd4t-api-2.0.15.3-DXA18.jar similarity index 62% rename from local-project-repo/org/dd4t/dd4t-api/2.1.4-DXA17/dd4t-api-2.1.4-DXA17.jar rename to local-project-repo/org/dd4t/dd4t-api/2.0.15.3-DXA18/dd4t-api-2.0.15.3-DXA18.jar index 37020dbc2..1f86c5509 100644 Binary files a/local-project-repo/org/dd4t/dd4t-api/2.1.4-DXA17/dd4t-api-2.1.4-DXA17.jar and b/local-project-repo/org/dd4t/dd4t-api/2.0.15.3-DXA18/dd4t-api-2.0.15.3-DXA18.jar differ diff --git a/local-project-repo/org/dd4t/dd4t-api/2.1.4-DXA17/dd4t-api-2.1.4-DXA17.pom b/local-project-repo/org/dd4t/dd4t-api/2.0.15.3-DXA18/dd4t-api-2.0.15.3-DXA18.pom similarity index 96% rename from local-project-repo/org/dd4t/dd4t-api/2.1.4-DXA17/dd4t-api-2.1.4-DXA17.pom rename to local-project-repo/org/dd4t/dd4t-api/2.0.15.3-DXA18/dd4t-api-2.0.15.3-DXA18.pom index 7ab113b72..1a778bed7 100644 --- a/local-project-repo/org/dd4t/dd4t-api/2.1.4-DXA17/dd4t-api-2.1.4-DXA17.pom +++ b/local-project-repo/org/dd4t/dd4t-api/2.0.15.3-DXA18/dd4t-api-2.0.15.3-DXA18.pom @@ -5,13 +5,13 @@ dd4t-parent org.dd4t - 2.1.4-DXA17 + 2.0.15.3-DXA18 4.0.0 jar dd4t-api - 2.1.4-DXA17 + 2.0.15.3-DXA18 ${project.groupId}:${project.artifactId} Contains the dd4t interfaces and content model. diff --git a/local-project-repo/org/dd4t/dd4t-core/2.1.4-DXA17/dd4t-core-2.1.4-DXA17.jar b/local-project-repo/org/dd4t/dd4t-core/2.0.15.3-DXA18/dd4t-core-2.0.15.3-DXA18.jar similarity index 86% rename from local-project-repo/org/dd4t/dd4t-core/2.1.4-DXA17/dd4t-core-2.1.4-DXA17.jar rename to local-project-repo/org/dd4t/dd4t-core/2.0.15.3-DXA18/dd4t-core-2.0.15.3-DXA18.jar index 8c981ef99..3a0f8c2ec 100644 Binary files a/local-project-repo/org/dd4t/dd4t-core/2.1.4-DXA17/dd4t-core-2.1.4-DXA17.jar and b/local-project-repo/org/dd4t/dd4t-core/2.0.15.3-DXA18/dd4t-core-2.0.15.3-DXA18.jar differ diff --git a/local-project-repo/org/dd4t/dd4t-core/2.1.4-DXA17/dd4t-core-2.1.4-DXA17.pom b/local-project-repo/org/dd4t/dd4t-core/2.0.15.3-DXA18/dd4t-core-2.0.15.3-DXA18.pom similarity index 99% rename from local-project-repo/org/dd4t/dd4t-core/2.1.4-DXA17/dd4t-core-2.1.4-DXA17.pom rename to local-project-repo/org/dd4t/dd4t-core/2.0.15.3-DXA18/dd4t-core-2.0.15.3-DXA18.pom index 8d38955d5..219385b0e 100644 --- a/local-project-repo/org/dd4t/dd4t-core/2.1.4-DXA17/dd4t-core-2.1.4-DXA17.pom +++ b/local-project-repo/org/dd4t/dd4t-core/2.0.15.3-DXA18/dd4t-core-2.0.15.3-DXA18.pom @@ -5,7 +5,7 @@ dd4t-parent org.dd4t - 2.1.4-DXA17 + 2.0.15.3-DXA18 4.0.0 diff --git a/local-project-repo/org/dd4t/dd4t-databind/2.1.4-DXA17/dd4t-databind-2.1.4-DXA17.jar b/local-project-repo/org/dd4t/dd4t-databind/2.0.15.3-DXA18/dd4t-databind-2.0.15.3-DXA18.jar similarity index 84% rename from local-project-repo/org/dd4t/dd4t-databind/2.1.4-DXA17/dd4t-databind-2.1.4-DXA17.jar rename to local-project-repo/org/dd4t/dd4t-databind/2.0.15.3-DXA18/dd4t-databind-2.0.15.3-DXA18.jar index 28f008219..c46bbc621 100644 Binary files a/local-project-repo/org/dd4t/dd4t-databind/2.1.4-DXA17/dd4t-databind-2.1.4-DXA17.jar and b/local-project-repo/org/dd4t/dd4t-databind/2.0.15.3-DXA18/dd4t-databind-2.0.15.3-DXA18.jar differ diff --git a/local-project-repo/org/dd4t/dd4t-databind/2.1.4-DXA17/dd4t-databind-2.1.4-DXA17.pom b/local-project-repo/org/dd4t/dd4t-databind/2.0.15.3-DXA18/dd4t-databind-2.0.15.3-DXA18.pom similarity index 98% rename from local-project-repo/org/dd4t/dd4t-databind/2.1.4-DXA17/dd4t-databind-2.1.4-DXA17.pom rename to local-project-repo/org/dd4t/dd4t-databind/2.0.15.3-DXA18/dd4t-databind-2.0.15.3-DXA18.pom index bd0f4c7cc..7e7cad4fe 100644 --- a/local-project-repo/org/dd4t/dd4t-databind/2.1.4-DXA17/dd4t-databind-2.1.4-DXA17.pom +++ b/local-project-repo/org/dd4t/dd4t-databind/2.0.15.3-DXA18/dd4t-databind-2.0.15.3-DXA18.pom @@ -7,11 +7,11 @@ dd4t-parent org.dd4t - 2.1.4-DXA17 + 2.0.15.3-DXA18 dd4t-databind - 2.1.4-DXA17 + 2.0.15.3-DXA18 ${project.groupId}:${project.artifactId} Contains deserialization functionality of datasources. https://github.com/dd4t diff --git a/local-project-repo/org/dd4t/dd4t-parent/2.1.4-DXA17/dd4t-parent-2.1.4-DXA17.pom b/local-project-repo/org/dd4t/dd4t-parent/2.0.15.3-DXA18/dd4t-parent-2.0.15.3-DXA18.pom similarity index 98% rename from local-project-repo/org/dd4t/dd4t-parent/2.1.4-DXA17/dd4t-parent-2.1.4-DXA17.pom rename to local-project-repo/org/dd4t/dd4t-parent/2.0.15.3-DXA18/dd4t-parent-2.0.15.3-DXA18.pom index c3b1dab26..6ef042c50 100644 --- a/local-project-repo/org/dd4t/dd4t-parent/2.1.4-DXA17/dd4t-parent-2.1.4-DXA17.pom +++ b/local-project-repo/org/dd4t/dd4t-parent/2.0.15.3-DXA18/dd4t-parent-2.0.15.3-DXA18.pom @@ -7,7 +7,7 @@ org.dd4t dd4t-parent pom - 2.1.4-DXA17 + 2.0.15.3-DXA18 dd4t-api dd4t-core @@ -66,15 +66,14 @@ 1.8 3.3.2 - 3.2.0.RELEASE + 4.3.5.RELEASE 1.0.13 1.6.4 2.2 17.0 3.0.1 - 3.2.0.RELEASE 4.0.1.Final - 2.8.3 + 2.8.5 4.4.1 1.1-rev-1 diff --git a/local-project-repo/org/dd4t/dd4t-providers/2.1.4-DXA17/dd4t-providers-2.1.4-DXA17.jar b/local-project-repo/org/dd4t/dd4t-providers/2.0.15.3-DXA18/dd4t-providers-2.0.15.3-DXA18.jar similarity index 83% rename from local-project-repo/org/dd4t/dd4t-providers/2.1.4-DXA17/dd4t-providers-2.1.4-DXA17.jar rename to local-project-repo/org/dd4t/dd4t-providers/2.0.15.3-DXA18/dd4t-providers-2.0.15.3-DXA18.jar index 77ba4af71..c7f9fb828 100644 Binary files a/local-project-repo/org/dd4t/dd4t-providers/2.1.4-DXA17/dd4t-providers-2.1.4-DXA17.jar and b/local-project-repo/org/dd4t/dd4t-providers/2.0.15.3-DXA18/dd4t-providers-2.0.15.3-DXA18.jar differ diff --git a/local-project-repo/org/dd4t/dd4t-providers/2.1.4-DXA17/dd4t-providers-2.1.4-DXA17.pom b/local-project-repo/org/dd4t/dd4t-providers/2.0.15.3-DXA18/dd4t-providers-2.0.15.3-DXA18.pom similarity index 95% rename from local-project-repo/org/dd4t/dd4t-providers/2.1.4-DXA17/dd4t-providers-2.1.4-DXA17.pom rename to local-project-repo/org/dd4t/dd4t-providers/2.0.15.3-DXA18/dd4t-providers-2.0.15.3-DXA18.pom index 066d26b47..a1f9f463d 100644 --- a/local-project-repo/org/dd4t/dd4t-providers/2.1.4-DXA17/dd4t-providers-2.1.4-DXA17.pom +++ b/local-project-repo/org/dd4t/dd4t-providers/2.0.15.3-DXA18/dd4t-providers-2.0.15.3-DXA18.pom @@ -5,7 +5,7 @@ dd4t-parent org.dd4t - 2.1.4-DXA17 + 2.0.15.3-DXA18 4.0.0 ${project.groupId}:${project.artifactId} @@ -43,7 +43,7 @@ jar - 8.5.0-1008 + 10.1.0-1012 1.8