[MAGNOLIA-8931] rendering of RichTextField data fails if it contains a link which doesn't exist Created: 20/Jan/22  Updated: 19/Sep/23  Resolved: 07/Jun/23

Status: Closed
Project: Magnolia
Component/s: None
Affects Version/s: None
Fix Version/s: 6.3.0, 6.2.35

Type: Bug Priority: High
Reporter: Tomáš Gregovský Assignee: Milan Divilek
Resolution: Fixed Votes: 4
Labels: None
Σ Remaining Estimate: Not Specified Remaining Estimate: Not Specified
Σ Time Spent: 2.5h Time Spent: Not Specified
Σ Original Estimate: Not Specified Original Estimate: Not Specified

Attachments: XML File config.modules.multisite.config.sites.test.xml     Text File stack-trace.txt     XML File userroles.deny-test.xml     File website.test.yaml    
Issue Links:
Relates
relates to MAGNOLIA-8936 Link to deleted Page leads to Templat... Closed
relation
Sub-Tasks:
Key
Summary
Type
Status
Assignee
MAGNOLIA-8932 implement Fix Sub-task Completed Milan Divilek  
MAGNOLIA-8933 review Sub-task Completed Robert Šiška  
MAGNOLIA-8934 preintQA Sub-task Completed Robert Šiška  
MAGNOLIA-8935 QA Sub-task Completed Oanh Thai Hoang  
Template:
Acceptance criteria:
Empty
Task DoD:
[X]* Doc/release notes changes? Comment present?
[X]* Downstream builds green?
[X]* Solution information and context easily available?
[X]* Tests
[X]* FixVersion filled and not yet released
[ ]  Architecture Decision Record (ADR)
Bug DoR:
[X]* Steps to reproduce, expected, and actual results filled
[X]* Affected version filled
Date of First Response:
Epic Link: Support
Sprint: DevX 39
Story Points: 2
Team: DeveloperX
Work Started:
Approved:
Yes

 Description   

When editor use CKeditor (richtextfield) and use build-in functionality for internal link - a.k.a. paragraph in richtextfield contains link to another internal page. 

e.g content of the richtextfield (content.desc):

<p>Some text with link <a href="${link:{uuid:{d17e1eba-ea6c-4b65-afd5-43fb28633eb5},repository:{website},path:{/international-programmes}}}">International Busines offering</a></p>

But if this page doest exists:

  1. was deleted
  2. is not published
  3. another editor doesn't have the right to see such page.

Then rendering this data in template using

 

${cmsfn.decode(content).desc) 

Fails!

 

this is possible partially workaround by 

 

[#attempt]
    ${cmsfn.decode(content).desc}
[#recover]
    // print something else
[/#attempt] 

But this is still not ideal, because it will not render whole paragraph (whole content of richtextfiled)

Expected: Paragraph will be rendered and only link would not have a link <a href>

 

But there is second part in case we need to check if content exist first, code like this:

 

[#if content.title?has_content || content.desc?has_content]

    <section class="ArticleSection">
        [#if content.title?has_content]
            <h2 class="ArticleSection--title">${content.title!}</h2>
        [/#if]

        [#attempt]
            <div class="richtext">
                [#if content.desc?has_content]
                    ${cmsfn.decode(content).desc}
                [/#if]
            </div>
        [#recover]             
            // print something else         
        [/#attempt]
    </section>
[/#if] 

This will already fail on line #1 :

 

 

Error while rendering [/...] with template [...:components/basicContent2] for URI [/...]: RenderException: freemarker.core._MiscTemplateException: Expression has thrown an unchecked exception; see the cause exception. The blamed expression: ==> content.desc [in template ".../templates/components/basicContent.ftl" at line 37, column 35] ---- FTL stack trace ("~" means nesting-related): - Failed at: #if content.title?has_content || cont... [in template ".../templates/components/basicContent.ftl" at line 37, column 1] ---- info.magnolia.rendering.engine.RenderException: freemarker.core._MiscTemplateException: Expression has thrown an unchecked exception; see the cause exception. The blamed expression: ==> content.desc [in template "..../templates/components/basicContent.ftl" at line 37, column 35] 

Expected: content.desc?has_content will return true or fals no matter if it contains link which doesn't exists or not.

Thank you

Developer notes:

see attached stack-trace.txt:

Caused by: java.lang.NullPointerException
        at info.magnolia.multisite.CrossSiteAbsolutePathTransformer.prefixLink(CrossSiteAbsolutePathTransformer.java:67) ~[magnolia-module-multisite-2.1.2.jar:?] 


 Comments   
Comment by Roman Kovařík [ 04/Feb/22 ]

Hey ,

Could you explain what the relation between

 

<p>Some text with link <a href="${link:{uuid:{d17e1eba-ea6c-4b65-afd5-43fb28633eb5},repository:{website},path:{/international-programmes}}}">International Busines offering</a></p>

and

${cmsfn.decode(content).desc) 

? (Seems like two complately unrelated things). Could you elaborate (the later is not a rich text field content).

 

Thanks in advance.

Roman

 

Comment by Tomáš Gregovský [ 09/Feb/22 ]

hi rkovarik , first is what is saved in jcr if you use richtext, enter the text "Some text with link International Busines offering" and select few words to create an internal link ...

the second is freemarker code to render such content (name of the field/property is 'desc' in this case)

Comment by Tomáš Gregovský [ 11/May/23 ]

issue still happening on Magnolia 6.2.28, pls see my recording https://drive.google.com/file/d/1-qKHNcUXYJjIPfM39FMgTnJUz_yKUf1b/view?usp=sharing 

Comment by Milan Divilek [ 26/May/23 ]

To reproduce the issue, you need to remove i18n from the site definition of the page and you need to visit the site from different domain or subdomain. This then fails when it tries to resolve link in CrossSiteAbsolutePathTransformer on getting raw url from i18n which is not configured for site.

Reproduce:

  1. Import this page structure to website - website.test.yaml
  2. Import site definition for test page - config.modules.multisite.config.sites.test.xml
  3. Create role where you deny access to "/test/test-sub" in "website" workspace - userroles.deny-test.xml
  4. Assign this role to "eric"
  5. Login as eric
  6. Visit the page http://localhost:8080/magnoliaAuthor/test.html
  7. NPE occures

Investigation:

When creating this link from the link pattern

${link:{uuid:{5d6384f7-f044-4194-a120-703aedf7c054},repository:{website},path:{/test/test-sub}}}

info.magnolia.link.LinkUtil#createLinkInstance tries to resolve node but it fails because it has no permission for it. This then leads to creation of info.magnolia.link.Link without jcrNode, workspace and path set.

Then in info.magnolia.multisite.DomainNamePathTransformer#transform when resolving best URI2RepositoryMapping no mapping match because there's no workspace set in the link (if it's set then site mapping is resolved). This then leads to different behaviour when handlePrefix is not removed from the linkStr. So when you have permissions link is generated without site prefix, but when you don't have permission link is generated with site prefix.

CrossSiteAbsolutePathTransformer#65-69 tries to remove site prefix from the link when it was already added by i18n to avoid doubling site prefix in url. If link contains site prefix it tries to get rawUri from i18n link and remove site prefix, but when i18n is not set this fails on NPE.

Discovered problem with different link resolved when accessing page from configured domain for site

In our example above we use sportstation.com domain for our test site.

Visit page http://sportstation.com:8080/magnoliaAuthor/test.html :
1. As superuser you will see url resolved for subpage - http://sportstation.com:8080/magnoliaAuthor/test-sub.html
2. As eric (user without permission to subpage) - http://sportstation.com:8080/magnoliaAuthor/test/test-sub
You can see site prefix isn't remove in the second case from the URL.

This is cause also by missing workspace in Link instance, then it doesn't remove ssite prefix from url because again no mapping matched in DomainNamePathTransformer#transform.

Solution

When we creating the link from link pattern we should set the workspace to Link instance even when node wasn't resolved

diff --git a/magnolia-core/src/main/java/info/magnolia/link/LinkUtil.java b/magnolia-core/src/main/java/info/magnolia/link/LinkUtil.java
index c06d59bbbc..76173ebb47 100644
--- a/magnolia-core/src/main/java/info/magnolia/link/LinkUtil.java
+++ b/magnolia-core/src/main/java/info/magnolia/link/LinkUtil.java
@@ -548,10 +548,12 @@ public class LinkUtil {
                 log.warn("Can't find node with uuid {} or handle {} in repository {}", uuid, fallbackHandle, defaultRepository);
                 link = new Link();
                 link.setUUID(uuid);
+                link.setWorkspace(workspaceName);
             } catch (RepositoryException re) {
                 log.warn("Can't find node with uuid {} or handle {} in repository {}", uuid, fallbackHandle, defaultRepository);
                 link = new Link();
                 link.setUUID(uuid);
+                link.setWorkspace(workspaceName);
             }
         }
         link.setFallbackPath(fallbackHandle);

This will fix both issues because site mapping will be correctly applied to linkStr and remove the handlePrefix from it.
It resolves correct link when domain match without site prefix for both user (superuser with permissions and eric without permissions).
It also doesn't get into the code where we try to remove site prefix when i18n is not set, because it couldn't add the site prefix when it's not configured.

To be on safe side we can still check for site.getI18n() != null in CrossSiteAbsolutePathTransformer, but actually when i18n is not set it shouldn't get into this part of the code.

Comment by Pierre Sandrin [ 19/Sep/23 ]

To whom it may help: We are using the URL-Translation Module and needed to update to 6.2.4 to fix this.

Generated at Mon Feb 12 04:37:05 CET 2024 using Jira 9.4.2#940002-sha1:46d1a51de284217efdcb32434eab47a99af2938b.