Properly render the news preview in both dark and lightmode.
authorDave Page <dpage@pgadmin.org>
Fri, 28 Nov 2025 08:52:04 +0000 (08:52 +0000)
committerDave Page <dpage@pgadmin.org>
Fri, 28 Nov 2025 08:52:04 +0000 (08:52 +0000)
This requires us to render the email template in an iframe to prevent it's styles leaking out into the main page.

media/css/main.css
media/js/moderation_preview.js [new file with mode: 0644]
templates/account/submit_preview.html

index 3be226d3a70f680458e3a54a974969dfe3e9f178..28e01b8dccb9ab9cf9f65158a62ac60c6b02c68e 100644 (file)
@@ -1851,6 +1851,14 @@ details.release-notes-list {
     max-width: 650px;
 }
 
+.moderation-preview-iframe {
+    width: 100%;
+    min-height: 400px;
+    border: 1px solid var(--button-input-bdr-color);
+    border-radius: 5px;
+    background-color: #fff;
+}
+
 /* Buttons that are images */
 button.imagebutton {
     border: 0;
diff --git a/media/js/moderation_preview.js b/media/js/moderation_preview.js
new file mode 100644 (file)
index 0000000..cff19ff
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Moderation preview handler
+ * Renders HTML content in iframes to isolate styles from the main page
+ */
+document.addEventListener('DOMContentLoaded', function() {
+    /* Find preview data textareas and render them in iframes */
+    var dataAreas = document.getElementsByClassName('moderation-preview-data');
+    for (var i = 0; i < dataAreas.length; i++) {
+        var dataArea = dataAreas[i];
+        var container = dataArea.parentElement;
+        var content = dataArea.value;
+
+        /* Create an iframe to isolate the HTML content styles */
+        var iframe = document.createElement('iframe');
+        iframe.className = 'moderation-preview-iframe';
+        iframe.sandbox = 'allow-same-origin';
+        iframe.srcdoc = content;
+
+        /* Resize iframe to fit content after it loads */
+        iframe.onload = function() {
+            try {
+                var contentHeight = this.contentDocument.body.scrollHeight;
+                if (contentHeight > 0) {
+                    this.style.height = (contentHeight + 20) + 'px';
+                }
+            } catch (e) {
+                /* Only ignore SecurityError/cross-origin errors, rethrow others */
+                if (e.name !== 'SecurityError') {
+                    throw e;
+                }
+            }
+        };
+
+        container.appendChild(iframe);
+    }
+});
index 054e03bf44358bcdef315476d22d99c1820e7b42..c7ac5a6e8cbceb5185ad9290a45e4266c4bd3ba9 100644 (file)
 {%for fld, title, contents, mdcontents, note in preview %}
 <div class="row">
   <div class="col-sm-2 col-form-label"><strong>{{title}}</strong></div>
-  <div class="col-sm-10">{%if mdcontents%}<div class="moderation-mdpreview-wrap">{{mdcontents|safe}}</div>{%else%}{{contents}}{%endif%}</div>
+  <div class="col-sm-10">
+    {%if mdcontents%}
+      <div class="moderation-mdpreview-wrap moderation-preview-container">
+        {# Use hidden textarea to store raw HTML without browser parsing #}
+        <textarea class="moderation-preview-data" style="display:none;">{{mdcontents}}</textarea>
+      </div>
+    {%else%}
+      {{contents}}
+    {%endif%}
+  </div>
 </div>
 {%endfor%}
 
@@ -25,3 +34,7 @@
 {%include "base/form_contents.html" with savebutton="Submit for moderation"%}
 
 {%endblock%}
+
+{%block extrascript%}
+<script src="/media/js/moderation_preview.js?{{gitrev}}"></script>
+{%endblock%}