From 3806315b39047e427d5bc7a94526a4341083f97c Mon Sep 17 00:00:00 2001
From: Jakob Vogel <jvo@scireum.de>
Date: Thu, 5 Sep 2024 00:31:58 +0200
Subject: [PATCH] =?UTF-8?q?Enables=20rewriting=20of=20plain=20URLs=20?=
 =?UTF-8?q?=F0=9F=94=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

…into PDF-compatible specialised URLs with custom schemes. That way we can support URLs such as `https://<server>/assets/…` directly by converting them silently into PDF-compatible form `resource://assets/…`, for example.

OX-10783
---
 .../pdf/ImageReplacedElementFactory.java      | 14 ++++++++++++-
 .../pdf/handlers/PdfReplaceHandler.java       | 11 ++++++++++
 .../handlers/ResourcePdfReplaceHandler.java   | 20 +++++++++++++++++++
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/src/main/java/sirius/web/templates/pdf/ImageReplacedElementFactory.java b/src/main/java/sirius/web/templates/pdf/ImageReplacedElementFactory.java
index 3b9210400..5d3a34545 100644
--- a/src/main/java/sirius/web/templates/pdf/ImageReplacedElementFactory.java
+++ b/src/main/java/sirius/web/templates/pdf/ImageReplacedElementFactory.java
@@ -21,6 +21,7 @@
 import sirius.web.templates.pdf.handlers.PdfReplaceHandler;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Used by the XHTMLRenderer (creating PDFs) to replace img elements by their referenced image.
@@ -61,7 +62,7 @@ public ReplacedElement createReplacedElement(LayoutContext layoutContext,
             return super.createReplacedElement(layoutContext, box, userAgentCallback, cssWidth, cssHeight);
         }
 
-        String source = element.getAttribute(ATTR_SRC);
+        String source = rewriteLegacyUrl(element.getAttribute(ATTR_SRC));
         if (Strings.isEmpty(source)) {
             return super.createReplacedElement(layoutContext, box, userAgentCallback, cssWidth, cssHeight);
         }
@@ -85,4 +86,15 @@ private PdfReplaceHandler findHandler(String protocol) {
                                "No handler for protocol '%s' could be found",
                                protocol)));
     }
+
+    private String rewriteLegacyUrl(String url) {
+        for (PdfReplaceHandler handler : handlers) {
+            Optional<String> rewrittenUrl = handler.tryRewritePlainUrl(url);
+            if (rewrittenUrl.isPresent()) {
+                return rewrittenUrl.get();
+            }
+        }
+
+        return url;
+    }
 }
diff --git a/src/main/java/sirius/web/templates/pdf/handlers/PdfReplaceHandler.java b/src/main/java/sirius/web/templates/pdf/handlers/PdfReplaceHandler.java
index 378359251..cff99a21f 100644
--- a/src/main/java/sirius/web/templates/pdf/handlers/PdfReplaceHandler.java
+++ b/src/main/java/sirius/web/templates/pdf/handlers/PdfReplaceHandler.java
@@ -21,6 +21,7 @@
 import javax.annotation.Nullable;
 import java.io.IOException;
 import java.net.URL;
+import java.util.Optional;
 
 /**
  * Represents a image replace handler that is used by {@link sirius.web.templates.pdf.ImageReplacedElementFactory} to
@@ -34,6 +35,16 @@ public int getPriority() {
         return Priorized.DEFAULT_PRIORITY;
     }
 
+    /**
+     * Attempts to rewrite a plain URL to a PDF-compatible one.
+     *
+     * @param url the plain URL to rewrite
+     * @return an optional containing the new URL if the plain URL could be rewritten, an empty optional otherwise
+     */
+    public Optional<String> tryRewritePlainUrl(String url) {
+        return Optional.empty();
+    }
+
     /**
      * Determines if this handler can resolve a URI with the given protocol.
      *
diff --git a/src/main/java/sirius/web/templates/pdf/handlers/ResourcePdfReplaceHandler.java b/src/main/java/sirius/web/templates/pdf/handlers/ResourcePdfReplaceHandler.java
index ddaea7ec8..9ee24ed98 100644
--- a/src/main/java/sirius/web/templates/pdf/handlers/ResourcePdfReplaceHandler.java
+++ b/src/main/java/sirius/web/templates/pdf/handlers/ResourcePdfReplaceHandler.java
@@ -11,12 +11,14 @@
 import org.xhtmlrenderer.extend.FSImage;
 import org.xhtmlrenderer.extend.UserAgentCallback;
 import sirius.kernel.commons.Strings;
+import sirius.kernel.di.std.ConfigValue;
 import sirius.kernel.di.std.Part;
 import sirius.kernel.di.std.Register;
 import sirius.web.resources.Resource;
 import sirius.web.resources.Resources;
 
 import javax.annotation.Nullable;
+import java.util.Optional;
 
 /**
  * Resolves resource:// URIs that are referencing {@link Resources} to resized images while maintaining the image
@@ -25,6 +27,9 @@
 @Register
 public class ResourcePdfReplaceHandler extends PdfReplaceHandler {
 
+    @ConfigValue("product.baseUrl")
+    private String productBaseUrl;
+
     @Part
     private Resources resources;
 
@@ -46,4 +51,19 @@ public FSImage resolveUri(String uri, UserAgentCallback userAgentCallback, int c
 
         return null;
     }
+
+    @Override
+    public Optional<String> tryRewritePlainUrl(String url) {
+        if (!url.startsWith(productBaseUrl)) {
+            return Optional.empty();
+        }
+
+        // remap plain asset URLs to the resource:// scheme
+        int indexOfAssets = url.indexOf("/assets/");
+        if (indexOfAssets >= 0) {
+            return Optional.of("resource:/" + url.substring(indexOfAssets));
+        }
+
+        return Optional.empty();
+    }
 }