diff --git a/dotCMS/src/main/java/com/dotmarketing/cms/urlmap/URLMapAPIImpl.java b/dotCMS/src/main/java/com/dotmarketing/cms/urlmap/URLMapAPIImpl.java index d172f5654e71..b6cb962a8bae 100644 --- a/dotCMS/src/main/java/com/dotmarketing/cms/urlmap/URLMapAPIImpl.java +++ b/dotCMS/src/main/java/com/dotmarketing/cms/urlmap/URLMapAPIImpl.java @@ -1,5 +1,6 @@ package com.dotmarketing.cms.urlmap; +import com.dotcms.api.web.HttpServletRequestThreadLocal; import com.dotcms.content.elasticsearch.constants.ESMappingConstants; import com.dotcms.content.elasticsearch.util.ESUtils; import com.dotcms.contenttype.business.ContentTypeAPI; @@ -28,6 +29,7 @@ import io.vavr.control.Try; import org.jetbrains.annotations.NotNull; +import javax.servlet.http.HttpServletRequest; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.PathMatcher; @@ -43,6 +45,12 @@ public class URLMapAPIImpl implements URLMapAPI { private final PermissionAPI permissionAPI = APILocator.getPermissionAPI(); private final IdentifierAPI identifierAPI = APILocator.getIdentifierAPI(); private final ContentTypeAPI typeAPI = APILocator.getContentTypeAPI(APILocator.systemUser()); + + /** Request-attribute key prefix for caching the resolved contentlet within a single request. */ + private static final String REQUEST_CACHE_KEY = URLMapAPIImpl.class.getName() + ".contentlet:"; + /** Sentinel stored in the request cache to represent a "not found" result without using null. */ + private static final Contentlet CONTENTLET_NOT_FOUND = new Contentlet(); + private static final Lazy ignorePaths = Lazy.of(() -> { String[] patterns = Config.getStringArrayProperty("urlmap.ignore.glob.patterns", new String[]{"/application/**", "/api/**", "/dA/**", "/dotAdmin/**", "/html/**"}); PathMatcher[] paths = new PathMatcher[patterns.length]; @@ -87,6 +95,23 @@ public Optional processURLMap(final UrlMapContext context) */ private Contentlet getContentlet(final UrlMapContext urlMapContext) throws DotSecurityException { + // isUrlPattern() and processURLMap() are both called on the same HTTP request. + // Cache the resolved Contentlet in request scope so the second call reuses the first result + // (each call issues up to 2 ES queries with the cross-site fallback in place). + final String cacheKey = REQUEST_CACHE_KEY + + urlMapContext.getUri() + "|" + + urlMapContext.getHost().getIdentifier() + "|" + + urlMapContext.getLanguageId() + "|" + + urlMapContext.getMode().name(); + + final HttpServletRequest request = HttpServletRequestThreadLocal.INSTANCE.getRequest(); + if (request != null) { + final Object cached = request.getAttribute(cacheKey); + if (cached != null) { + return cached == CONTENTLET_NOT_FOUND ? null : (Contentlet) cached; + } + } + Contentlet matchingContentlet = null; try { @@ -112,6 +137,10 @@ private Contentlet getContentlet(final UrlMapContext urlMapContext) throws DotSe return null; } + if (request != null) { + request.setAttribute(cacheKey, matchingContentlet != null ? matchingContentlet : CONTENTLET_NOT_FOUND); + } + return matchingContentlet; } @@ -138,10 +167,13 @@ private Optional getDetailPageUri(final ContentType contentType, Hos // look for it on the current host final Identifier myHostIdentifier = this.identifierAPI.find(currentHost, identifier.getPath()); if (myHostIdentifier == null || !UtilMethods.isSet(myHostIdentifier.getId())) { + // No page at the same path on the current site — fall back to the configured + // detail page identifier (e.g. a shared page on a global host). Logger.info(this.getClass(), - "No valid detail page for Content Type '" + contentType.name() - + "'. Looking for a detail page=" + identifier.getPath() + " on Site " + currentHost.getHostname()); - return Optional.empty(); + "No detail page found at path '" + identifier.getPath() + "' on Site '" + + currentHost.getHostname() + "'. Falling back to configured detail page for Content Type '" + + contentType.name() + "'."); + return Optional.of(identifier); } return Optional.of(myHostIdentifier); @@ -266,9 +298,20 @@ private Contentlet getContentlet( Contentlet contentlet = null; - final String query = this.buildContentQuery(matches, contentType, context); - final List contentletSearches = - ContentUtils.pull(query, 0, 2, "score", this.wuserAPI.getSystemUser(), true); + // First search restricted to current host (and SYSTEM_HOST). If the content lives on a + // different site but is referenced from this site's pages (cross-site URL map scenario), + // the host-restricted query returns nothing. In that case, fall back to a site-agnostic + // query so the content can still be found and rendered against the current site's detail page. + List contentletSearches = + ContentUtils.pull(this.buildContentQuery(matches, contentType, context, true), 0, 2, "score", this.wuserAPI.getSystemUser(), true); + + if (contentletSearches.isEmpty()) { + Logger.debug(this.getClass(), String.format( + "No URL-mapped contentlet found on current site '%s'. Retrying without host restriction.", + context.getHost().getHostname().replaceAll("[\\r\\n\\t]", "_"))); + contentletSearches = + ContentUtils.pull(this.buildContentQuery(matches, contentType, context, false), 0, 2, "score", this.wuserAPI.getSystemUser(), true); + } if (!contentletSearches.isEmpty()) { @@ -332,15 +375,18 @@ private void checkContentPermission(final UrlMapContext context, final Contentle * Builds the Lucene query used to find the specific {@link Contentlet} that matches a given URL Map for a * Content Type. * - * @param matches The set of URL Maps that match a specific Content Type. - * @param contentType The Content Type that matches the URL Map. - * @param context The instance of the URL Map Context. + * @param matches The set of URL Maps that match a specific Content Type. + * @param contentType The Content Type that matches the URL Map. + * @param context The instance of the URL Map Context. + * @param restrictToHost When {@code true}, limits results to the current site and SYSTEM_HOST. + * Pass {@code false} for a cross-site fallback that searches all sites. * @return The Lucene query that will return a potential match for the URL Map. */ private String buildContentQuery( final Matches matches, final ContentType contentType, - final UrlMapContext context) { + final UrlMapContext context, + final boolean restrictToHost) { final StringBuilder query = new StringBuilder(); @@ -348,12 +394,15 @@ private String buildContentQuery( .append(contentType.variable()) .append(" +" + ESMappingConstants.VARIANT + ":") .append(VariantAPI.DEFAULT_VARIANT.name()) - .append(" +deleted:false ") - .append(" +(conhost:") - .append(context.getHost().getIdentifier()) - .append(" OR conhost:") - .append(Host.SYSTEM_HOST) - .append(")"); + .append(" +deleted:false "); + + if (restrictToHost) { + query.append(" +(conhost:") + .append(context.getHost().getIdentifier()) + .append(" OR conhost:") + .append(Host.SYSTEM_HOST) + .append(")"); + } if (context.getMode().showLive) { query.append(" +live:true "); } else { diff --git a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PageDetailCollectorTest.java b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PageDetailCollectorTest.java index b3753662608f..66417abdc38b 100644 --- a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PageDetailCollectorTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PageDetailCollectorTest.java @@ -47,8 +47,6 @@ public class PageDetailCollectorTest extends IntegrationTestBase { private static final String PARENT_FOLDER_1_NAME = "news"; private static final String TEST_URL_MAP_PAGE_NAME = "news-detail"; - private static final String TEST_PATTERN = "/testpattern/"; - private static final String TEST_URL_MAP_DETAIL_PAGE_URL = TEST_PATTERN + "mynews"; private static Host testSite = null; @@ -74,17 +72,21 @@ public static void prepare() throws Exception { */ @Test public void testPageDetailCollector() throws DotDataException, UnknownHostException, DotSecurityException { + final String uniqueSuffix = String.valueOf(System.nanoTime()); + final String urlTitle = "mynews-" + uniqueSuffix; + final String testPattern = "/page-detail-collector-" + uniqueSuffix + "/"; + final String testUrlMapDetailPageUrl = testPattern + urlTitle; + final HttpServletResponse response = mock(HttpServletResponse.class); final String requestId = UUIDUtil.uuid(); final HttpServletRequest request = Util.mockHttpRequestObj(response, - TEST_URL_MAP_DETAIL_PAGE_URL, requestId, + testUrlMapDetailPageUrl, requestId, APILocator.getUserAPI().getAnonymousUser()); final HTMLPageAsset testDetailPage = Util.createTestHTMLPage(testSite, TEST_URL_MAP_PAGE_NAME, PARENT_FOLDER_1_NAME); - final String urlTitle = "mynews"; - final String urlMapPatternToUse = TEST_PATTERN + "{urlTitle}"; + final String urlMapPatternToUse = testPattern + "{urlTitle}"; final Language language = APILocator.getLanguageAPI().getDefaultLanguage(); final long langId = language.getId(); @@ -107,11 +109,11 @@ public void testPageDetailCollector() throws DotDataException, UnknownHostExcept Collector.SITE_NAME, testSite.getHostname(), Collector.SITE_ID, testSite.getIdentifier(), Collector.LANGUAGE, language.getIsoCode(), - Collector.URL, TEST_URL_MAP_DETAIL_PAGE_URL, + Collector.URL, testUrlMapDetailPageUrl, Collector.OBJECT, Map.of( Collector.ID, testDetailPage.getIdentifier(), Collector.TITLE, testDetailPage.getTitle(), - Collector.URL, TEST_URL_MAP_DETAIL_PAGE_URL, + Collector.URL, testUrlMapDetailPageUrl, Collector.CONTENT_TYPE_ID, testDetailPage.getContentTypeId(), Collector.CONTENT_TYPE_NAME, testDetailPage.getContentType().name(), Collector.CONTENT_TYPE_VAR_NAME, testDetailPage.getContentType().variable(), diff --git a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PagesCollectorTest.java b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PagesCollectorTest.java index fffb530b698f..0425fe76147d 100644 --- a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PagesCollectorTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/PagesCollectorTest.java @@ -49,8 +49,6 @@ public class PagesCollectorTest extends IntegrationTestBase { private static final String PARENT_FOLDER_1_NAME = "news"; private static final String TEST_URL_MAP_PAGE_NAME = "news-detail"; - private static final String URL_MAP_PATTERN = "/testpattern/"; - private static final String TEST_URL_MAP_DETAIL_PAGE_URL = URL_MAP_PATTERN + "mynews"; private static Host testSite = null; @@ -125,17 +123,21 @@ public void collectPageData() throws Exception { */ @Test public void collectUrlMapPageData() throws Exception { + final String uniqueSuffix = String.valueOf(System.nanoTime()); + final String urlTitle = "mynews-" + uniqueSuffix; + final String urlMapPatternPrefix = "/pages-collector-" + uniqueSuffix + "/"; + final String testUrlMapDetailPageUrl = urlMapPatternPrefix + urlTitle; + final HttpServletResponse response = mock(HttpServletResponse.class); final String requestId = UUIDUtil.uuid(); final HttpServletRequest request = Util.mockHttpRequestObj(response, - TEST_URL_MAP_DETAIL_PAGE_URL, requestId, + testUrlMapDetailPageUrl, requestId, APILocator.getUserAPI().getAnonymousUser()); final HTMLPageAsset testDetailPage = Util.createTestHTMLPage(testSite, TEST_URL_MAP_PAGE_NAME, PARENT_FOLDER_1_NAME); - final String urlTitle = "mynews"; - final String urlMapPatternToUse = URL_MAP_PATTERN + "{urlTitle}"; + final String urlMapPatternToUse = urlMapPatternPrefix + "{urlTitle}"; final long langId = APILocator.getLanguageAPI().getDefaultLanguage().getId(); final ContentType urlMappedContentType = Util.getUrlMapLikeContentType( @@ -159,11 +161,11 @@ public void collectUrlMapPageData() throws Exception { Collector.EVENT_TYPE, EventType.URL_MAP.getType(), Collector.SITE_NAME, testSite.getHostname(), Collector.LANGUAGE, APILocator.getLanguageAPI().getDefaultLanguage().getIsoCode(), - Collector.URL, TEST_URL_MAP_DETAIL_PAGE_URL, + Collector.URL, testUrlMapDetailPageUrl, Collector.OBJECT, Map.of( Collector.ID, newsTestContent.getIdentifier(), - Collector.TITLE, urlTitle, - Collector.URL, TEST_URL_MAP_DETAIL_PAGE_URL, + Collector.TITLE, newsTestContent.getTitle(), + Collector.URL, testUrlMapDetailPageUrl, Collector.CONTENT_TYPE_ID, urlMappedContentType.id(), Collector.CONTENT_TYPE_NAME, urlMappedContentType.name(), Collector.CONTENT_TYPE_VAR_NAME, urlMappedContentType.variable(), diff --git a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceImplTest.java b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceImplTest.java index 2b702d2b9547..282c641315f0 100644 --- a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceImplTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceImplTest.java @@ -67,8 +67,6 @@ public class WebEventsCollectorServiceImplTest extends IntegrationTestBase { private static final String TEST_PAGE_NAME = "index"; private static final String TEST_PAGE_URL = "/" + TEST_PAGE_NAME; private static final String TEST_URL_MAP_PAGE_NAME = "news-detail"; - private static final String TEST_PATTERN = "/testpattern/"; - private static final String TEST_URL_MAP_DETAIL_PAGE_URL = TEST_PATTERN + "mynews"; private static final String URI = "/my-test/vanity-url"; private static final String CLIENT_ID = "analytics-customer-customer1"; @@ -232,10 +230,14 @@ public void testPagesCollector() throws DotDataException, IOException, DotSecuri */ @Test public void testPageDetailCollector() throws Exception { + final String uniqueSuffix = String.valueOf(System.nanoTime()); + final String urlTitle = "mynews-" + uniqueSuffix; + final String testPattern = "/web-events-page-detail-" + uniqueSuffix + "/"; + final String testUrlMapDetailPageUrl = testPattern + urlTitle; + testDetailPage = null != testDetailPage ? testDetailPage : Util.createTestHTMLPage(testSite, TEST_URL_MAP_PAGE_NAME, PARENT_FOLDER_1_NAME); - final String urlTitle = "mynews"; - final String urlMapPatternToUse = TEST_PATTERN + "{urlTitle}"; + final String urlMapPatternToUse = testPattern + "{urlTitle}"; final Language language = APILocator.getLanguageAPI().getDefaultLanguage(); final long langId = language.getId(); @@ -257,11 +259,11 @@ public void testPageDetailCollector() throws Exception { Collector.EVENT_TYPE, EventType.PAGE_REQUEST.getType(), Collector.SITE_NAME, testSite.getHostname(), Collector.LANGUAGE, language.getIsoCode(), - Collector.URL, TEST_URL_MAP_DETAIL_PAGE_URL, + Collector.URL, testUrlMapDetailPageUrl, Collector.OBJECT, Map.of( Collector.ID, testDetailPage.getIdentifier(), Collector.TITLE, testDetailPage.getTitle(), - Collector.URL, TEST_URL_MAP_DETAIL_PAGE_URL, + Collector.URL, testUrlMapDetailPageUrl, Collector.CONTENT_TYPE_ID, testDetailPage.getContentTypeId(), Collector.CONTENT_TYPE_NAME, testDetailPage.getContentType().name(), Collector.CONTENT_TYPE_VAR_NAME, testDetailPage.getContentType().variable(), @@ -284,7 +286,7 @@ public void testPageDetailCollector() throws Exception { final Map requestParams = Map.of( "host_id", testSite.getIdentifier() ); - final HttpServletRequest request = Util.mockHttpRequestObj(response, TEST_URL_MAP_DETAIL_PAGE_URL, + final HttpServletRequest request = Util.mockHttpRequestObj(response, testUrlMapDetailPageUrl, UUIDUtil.uuid(), APILocator.getUserAPI().getAnonymousUser(), null, requestParams); final RequestMatcher requestMatcher = new PagesAndUrlMapsRequestMatcher(); diff --git a/dotcms-integration/src/test/java/com/dotmarketing/cms/urlmap/URLMapAPIImplTest.java b/dotcms-integration/src/test/java/com/dotmarketing/cms/urlmap/URLMapAPIImplTest.java index 535ce5f9a97b..d99b0efb333e 100644 --- a/dotcms-integration/src/test/java/com/dotmarketing/cms/urlmap/URLMapAPIImplTest.java +++ b/dotcms-integration/src/test/java/com/dotmarketing/cms/urlmap/URLMapAPIImplTest.java @@ -12,8 +12,10 @@ import com.dotcms.contenttype.business.ContentTypeAPI; import com.dotcms.contenttype.model.field.DataTypes; import com.dotcms.contenttype.model.field.Field; +import com.dotcms.contenttype.model.type.BaseContentType; import com.dotcms.contenttype.model.type.ContentType; import com.dotcms.datagen.*; +import com.dotmarketing.portlets.htmlpageasset.business.HTMLPageAssetAPI; import com.dotcms.util.IntegrationTestInitService; import com.dotmarketing.beans.Host; import com.dotmarketing.business.APILocator; @@ -684,7 +686,7 @@ private static Contentlet createURLMapperContentType(final String newsPatternPre private UrlMapContext getUrlMapContext(final User systemUser, final Host host, final String uri, final PageMode pageMode) { return UrlMapContextBuilder.builder() .setHost(host) - .setLanguageId(1L) + .setLanguageId(APILocator.getLanguageAPI().getDefaultLanguage().getId()) .setMode(pageMode) .setUri(uri) .setUser(systemUser) @@ -695,6 +697,136 @@ private UrlMapContext getUrlMapContext(final User systemUser, final Host host, f return getUrlMapContext(systemUser, host, uri, PageMode.PREVIEW_MODE); } + /** + * methodToTest {@link URLMapAPIImpl#processURLMap(UrlMapContext)} + * Given Scenario: The Content Type's detail page is configured on a different (global) host + * using a custom Page content type. The current request host does NOT have any page at the + * same path. This reproduces the runtime 404 from issue #35268: URL mapping failed silently + * when the configured detail page lived on a different host. + * ExpectedResult: processURLMap must fall back to the configured detail-page identifier + * (from the other host) rather than returning empty, so the URL map resolves successfully. + */ + @Test + public void processURLMap_detailPageOnDifferentHost_shouldFallBackToConfiguredIdentifier() + throws DotDataException, DotSecurityException { + + // A separate "global" host that owns the shared detail page + final Host globalHost = new SiteDataGen().nextPersisted(); + final Folder globalFolder = new FolderDataGen() + .name("global-detail-pages-" + System.currentTimeMillis()) + .site(globalHost) + .nextPersisted(); + + // Custom Page content type on the global host (simulates "Landing Page") + final ContentType customPageType = new ContentTypeDataGen() + .baseContentType(BaseContentType.HTMLPAGE) + .host(globalHost) + .name("LandingPage" + System.currentTimeMillis()) + .nextPersisted(); + + final Template globalTemplate = new TemplateDataGen().site(globalHost).nextPersisted(); + + // Detail page using the custom Page content type — lives ONLY on globalHost + final Contentlet detailPageContentlet = new ContentletDataGen(customPageType) + .host(globalHost) + .folder(globalFolder) + .setProperty(HTMLPageAssetAPI.URL_FIELD, "global-detail-" + System.currentTimeMillis()) + .setProperty(HTMLPageAssetAPI.TITLE_FIELD, "Global Detail Page") + .setProperty(HTMLPageAssetAPI.TEMPLATE_FIELD, globalTemplate.getIdentifier()) + .setProperty(HTMLPageAssetAPI.FRIENDLY_NAME_FIELD, "Global Detail Page") + .setProperty(HTMLPageAssetAPI.CACHE_TTL_FIELD, "0") + .nextPersisted(); + + final String detailPageIdentifierId = detailPageContentlet.getIdentifier(); + + // Content Type on the regular host, pointing to the global host's custom-type detail page + final String urlPattern = "/global-test-" + System.currentTimeMillis() + "/{urlTitle}"; + final ContentType urlMappedType = getNewsLikeContentType( + "GlobalNews" + System.currentTimeMillis(), + host, + detailPageIdentifierId, + urlPattern); + + // Content item on the regular host + final Contentlet newsContent = TestDataUtils.getNewsContent( + true, + APILocator.getLanguageAPI().getDefaultLanguage().getId(), + urlMappedType.id(), + host, + null, + null); + + // Request the URL-mapped URL from the regular host + // (the regular host has no page at the same path as globalHost's detail page) + final UrlMapContext context = getUrlMapContext(systemUser, host, + urlPattern.replace("{urlTitle}", newsContent.getStringProperty("urlTitle"))); + + final Optional urlMapInfoOptional = urlMapAPI.processURLMap(context); + + assertTrue("processURLMap should fall back to the configured detail page on globalHost", + urlMapInfoOptional.isPresent()); + assertEquals("URLMapInfo identifier should be the configured detail page on globalHost", + detailPageIdentifierId, + urlMapInfoOptional.get().getIdentifier().getId()); + } + + /** + * methodToTest {@link URLMapAPIImpl#processURLMap(UrlMapContext)} + * Given Scenario: Content Type with URL pattern is registered on SYSTEM_HOST (global), but + * its configured detail page lives on siteA. A content item matching the URL pattern was + * created on siteB (a different site). The request comes in on siteA. + * This is the cross-site URL-map scenario from issue #35268: a list page on siteA shows + * content from multiple sites, and clicking a siteB item navigates to siteA's detail page. + * ExpectedResult: processURLMap must resolve the siteB content and return the siteA detail page. + * The initial ES query (restricted to siteA + SYSTEM_HOST) finds nothing because the content + * is on siteB; the fallback site-agnostic query locates it and the siteA detail page is used. + */ + @Test + public void processURLMap_contentOnDifferentSite_shouldResolveViaFallback() + throws DotDataException, DotSecurityException { + + // siteB: the site where the content lives (different from the request site / host) + final Host siteB = new SiteDataGen().nextPersisted(); + + // Content type on SYSTEM_HOST so content can be created on any site. + // The detail page is on siteA (host), which is where the request originates. + // Path must not begin with a prefix that matches BACKEND_FILTERED_COLLECTION using + // startsWith (e.g. "/c" matches "/cross-site/..."), or findMatch() skips URL-map logic entirely. + final String urlPattern = "/urlmap-cross-site-" + System.currentTimeMillis() + "/{urlTitle}"; + final ContentType urlMappedType = getNewsLikeContentType( + "CrossSiteNews" + System.currentTimeMillis(), + APILocator.systemHost(), + detailPage1.getIdentifier(), + urlPattern); + + // Content item lives on siteB — NOT on siteA (host) or SYSTEM_HOST, + // so the host-restricted query will miss it and the fallback is required. + // Publish (uses IndexPolicy.WAIT_FOR) so ES commits the document before we query. + final Contentlet contentOnSiteB = ContentletDataGen.publish( + TestDataUtils.getNewsContent( + true, + APILocator.getLanguageAPI().getDefaultLanguage().getId(), + urlMappedType.id(), + siteB, + null, + null)); + + // Request the URL-mapped URL from siteA (LIVE mode to match published content). + final UrlMapContext context = getUrlMapContext(systemUser, host, + urlPattern.replace("{urlTitle}", contentOnSiteB.getStringProperty("urlTitle")), + PageMode.LIVE); + + final Optional result = urlMapAPI.processURLMap(context); + + assertTrue("processURLMap should find cross-site content via fallback query", result.isPresent()); + assertEquals("Resolved contentlet should be the one from siteB", + contentOnSiteB.getIdentifier(), + result.get().getContentlet().getIdentifier()); + assertEquals("Detail page should be siteA's configured detail page", + "/news-events/news/news-detail", + result.get().getIdentifier().getURI()); + } + /** * methodToTest {@link URLMapAPIImpl#processURLMap(UrlMapContext)} * Given Scenario: Process a URL Map url when both the Content Type and Content exists but the field to Map is a @@ -718,7 +850,7 @@ public void shouldReturnContentletWhenTheContentExistsAndUseAIntegerFIeld() final Contentlet newsTestContent = new ContentletDataGen(contentType.id()) .setProperty(field.variable(), 2) - .languageId(1) + .languageId(APILocator.getLanguageAPI().getDefaultLanguage().getId()) .host(host) .nextPersisted(); @@ -756,7 +888,7 @@ public void shouldReturnContentletWhenTheContentExistsAndUseAFloatField() .nextPersisted(); final Contentlet newsTestContent = new ContentletDataGen(contentType.id()) .setProperty(field.variable(), 2f) - .languageId(1) + .languageId(APILocator.getLanguageAPI().getDefaultLanguage().getId()) .host(host) .nextPersisted();