You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/06/23 07:33:32 UTC

[isis] branch master updated: ISIS-3077: escape Strings for rendering dynamically based on what the output format dictates

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new d9e7457223 ISIS-3077: escape Strings for rendering dynamically based on what the output format dictates
d9e7457223 is described below

commit d9e74572236081541f8659d488d8ad3b009abfd6
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Jun 23 09:33:24 2022 +0200

    ISIS-3077: escape Strings for rendering dynamically based on what the
    output format dictates
---
 .../viewer/common/model/StringForRendering.java    | 49 ++++++++++++++++++++++
 .../components/scalars/ScalarPanelAbstract2.java   | 33 +++++++++------
 .../scalars/ScalarPanelTextFieldAbstract.java      |  3 +-
 .../blobclob/IsisBlobOrClobPanelAbstract.java      | 10 +++--
 .../ui/components/scalars/bool/BooleanPanel.java   |  3 +-
 .../scalars/reference/ReferencePanel.java          |  5 ++-
 .../scalars/string/ScalarTitleBadgePanel.java      |  2 +-
 .../valuechoices/ValueChoicesSelect2Panel.java     |  5 ++-
 .../org/apache/isis/viewer/wicket/ui/util/Wkt.java | 18 ++++++++
 9 files changed, 105 insertions(+), 23 deletions(-)

diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/StringForRendering.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/StringForRendering.java
new file mode 100644
index 0000000000..dac9a1ea1e
--- /dev/null
+++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/StringForRendering.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.common.model;
+
+import java.io.Serializable;
+
+@lombok.Value(staticConstructor = "of")
+public class StringForRendering implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Typically to be rendered with a label component, that supports escaping.
+     * <p>
+     * In other words, given {@code text} must not to be interpreted by the browser, that renders it.
+     */
+    public static StringForRendering text(final String text) {
+        return StringForRendering.of(text, false);
+    }
+
+    /**
+     * Typically to be rendered with a markup component, to be rendered as is.
+     * <p>
+     * In other words, given {@code html} must be interpreted by the browser, that renders it.
+     */
+    public static StringForRendering markup(final String html) {
+        return StringForRendering.of(html, true);
+    }
+
+    private String string;
+    private boolean markup;
+
+}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
index ecc7b4c50b..84f7687405 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract2.java
@@ -29,7 +29,7 @@ import org.apache.wicket.markup.repeater.RepeatingView;
 import org.springframework.lang.Nullable;
 
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract.PlaceholderLiteral;
-import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.viewer.common.model.StringForRendering;
 import org.apache.isis.viewer.wicket.model.models.InlinePromptContext;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.CompactFragment;
@@ -125,7 +125,7 @@ extends ScalarPanelAbstract {
         if(isUsingTextarea()) {
             return PromptFragment.TEXTAREA
                     .createFragment(id, this, scalarValueId->{
-                        val textArea = Wkt.textAreaNoTab(scalarValueId, this::obtainOutputFormat);
+                        val textArea = Wkt.textAreaNoTab(scalarValueId, this::outputFormatAsString);
                         val scalarModel = scalarModel();
                         Wkt.setFormComponentAttributes(textArea,
                                 scalarModel::multilineNumberOfLines,
@@ -137,8 +137,8 @@ extends ScalarPanelAbstract {
         return CompactFragment.LABEL
                     .createFragment(id, this, scalarValueId->
                         getFormatModifiers().contains(FormatModifier.NO_OUTPUT_ESCAPE)
-                            ? Wkt.markup(scalarValueId, this::obtainOutputFormat)
-                            : Wkt.label(scalarValueId, this::obtainOutputFormat));
+                            ? Wkt.markup(scalarValueId, this::outputFormatAsString)
+                            : Wkt.labelWithDynamicEscaping(scalarValueId, this::obtainOutputFormat));
     }
 
     private boolean isUsingTextarea() {
@@ -151,19 +151,28 @@ extends ScalarPanelAbstract {
         return !scalarModel().isEmpty();
     }
 
+    /**
+     * @see #obtainOutputFormat()
+     */
+    protected final String outputFormatAsString() {
+        return obtainOutputFormat().getString();
+    }
+
     /**
      * Output format (usually HTML) as String, for any non editing scenario.
      * <p>
      * Usually HTML, except for (non-empty) text-areas or badges (that are already modeled in HTML).
      */
-    protected String obtainOutputFormat() {
-        return _Strings.nonEmpty(
-                    isUsingTextarea()
-                    || getFormatModifiers().contains(FormatModifier.BADGE)
-                        ? scalarModel().proposedValue().getValueAsTitle().getValue()
-                        : scalarModel().proposedValue().getValueAsHtml().getValue())
-                .orElseGet(()->
-                    PlaceholderLiteral.NULL_REPRESENTATION.asHtml(this::translate));
+    protected StringForRendering obtainOutputFormat() {
+        val proposedValue = scalarModel().proposedValue();
+        if(!proposedValue.isPresent()) {
+            return StringForRendering.markup(PlaceholderLiteral.NULL_REPRESENTATION.asHtml(this::translate));
+        }
+        val useText = isUsingTextarea()
+                || getFormatModifiers().contains(FormatModifier.BADGE);
+        return useText
+                        ? StringForRendering.text(proposedValue.getValueAsTitle().getValue())
+                        : StringForRendering.markup(proposedValue.getValueAsHtml().getValue());
     }
 
     /**
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
index e0e23548e7..ab03f754e1 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
@@ -28,6 +28,7 @@ import org.apache.wicket.util.convert.IConverter;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
 import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
+import org.apache.isis.viewer.common.model.StringForRendering;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.InputFragment;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
@@ -129,7 +130,7 @@ extends ScalarPanelFormFieldAbstract<T> {
     // -- CONVERSION
 
     @Override
-    protected final String obtainOutputFormat() {
+    protected final StringForRendering obtainOutputFormat() {
         // conversion does not affect the output format (usually HTML)
         return super.obtainOutputFormat();
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java
index 4b4f1338f1..f846b3a78b 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java
@@ -33,6 +33,7 @@ import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.applib.value.NamedWithMimeType;
 import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract.PlaceholderLiteral;
+import org.apache.isis.viewer.common.model.StringForRendering;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.CompactFragment;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.InputFragment;
@@ -75,7 +76,7 @@ extends ScalarPanelFormFieldAbstract<T> {
     @SuppressWarnings({ "unchecked", "rawtypes" })
     @Override
     protected FormComponent createFormComponent(final String id, final ScalarModel scalarModel) {
-        val initialCaption = obtainOutputFormat();
+        val initialCaption = outputFormatAsString();
         val fileUploadField = Wkt.fileUploadField(id, initialCaption, fileUploadModel());
         addAcceptFilterTo(fileUploadField);
         return fileUploadField;
@@ -84,18 +85,19 @@ extends ScalarPanelFormFieldAbstract<T> {
     // -- OUTPUT FORMAT
 
     @Override
-    protected String obtainOutputFormat() {
-        return getBlobOrClobFromModel()
+    protected StringForRendering obtainOutputFormat() {
+        val caption = getBlobOrClobFromModel()
                 .map(NamedWithMimeType::getName)
                 .orElseGet(()->
                     PlaceholderLiteral.NULL_REPRESENTATION.asText(this::translate));
+        return StringForRendering.text(caption);
     }
 
     @Override
     protected Component createComponentForOutput(final String id) {
         val link = CompactFragment.LINK
                 .createFragment(id, this, scalarValueId->
-                    createDownloadLink(scalarValueId, this::obtainOutputFormat));
+                    createDownloadLink(scalarValueId, this::outputFormatAsString));
         return link;
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bool/BooleanPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bool/BooleanPanel.java
index e3765a2708..e47ee3cb79 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bool/BooleanPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bool/BooleanPanel.java
@@ -26,6 +26,7 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.form.FormComponent;
 
 import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.viewer.common.model.StringForRendering;
 import org.apache.isis.viewer.wicket.model.models.BooleanModel;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.CompactFragment;
@@ -86,7 +87,7 @@ extends ScalarPanelFormFieldAbstract<Boolean> {
     }
 
     @Override
-    protected String obtainOutputFormat() {
+    protected StringForRendering obtainOutputFormat() {
         throw _Exceptions.unexpectedCodeReach(); // not used in createComponentForOutput(...)
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
index 28f51aeb99..dd37d98ea8 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
@@ -35,6 +35,7 @@ import org.apache.isis.applib.value.semantics.ValueSemanticsAbstract.Placeholder
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMemento;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.util.Facets;
+import org.apache.isis.viewer.common.model.StringForRendering;
 import org.apache.isis.viewer.common.model.components.ComponentType;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.CompactFragment;
@@ -74,8 +75,8 @@ public class ReferencePanel extends ScalarPanelSelectAbstract {
     }
 
     @Override
-    protected String obtainOutputFormat() {
-        return select2.obtainOutputFormatModel().getObject();
+    protected StringForRendering obtainOutputFormat() {
+        return StringForRendering.text(select2.obtainOutputFormatModel().getObject());
     }
 
     @Override
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/string/ScalarTitleBadgePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/string/ScalarTitleBadgePanel.java
index 225b026b20..edabbb3bbf 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/string/ScalarTitleBadgePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/string/ScalarTitleBadgePanel.java
@@ -48,7 +48,7 @@ public class ScalarTitleBadgePanel<T> extends ScalarPanelTextFieldWithValueSeman
     @Override
     protected Component createComponentForOutput(final String id) {
         return CompactFragment.BADGE.createFragment(id, this, scalarValueId->
-            Wkt.label(scalarValueId, this::obtainOutputFormat));
+            Wkt.label(scalarValueId, this::outputFormatAsString));
     }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
index 6ba5656d23..6bd1cf81a0 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
@@ -28,6 +28,7 @@ import org.wicketstuff.select2.ChoiceProvider;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMemento;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.viewer.common.model.StringForRendering;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.InputFragment;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelSelectAbstract;
@@ -75,8 +76,8 @@ extends ScalarPanelSelectAbstract {
     // --
 
     @Override
-    protected String obtainOutputFormat() {
-        return select2.obtainOutputFormatModel().getObject();
+    protected StringForRendering obtainOutputFormat() {
+        return StringForRendering.text(select2.obtainOutputFormatModel().getObject());
     }
 
     // --
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
index 880d7aa3d9..e37c7a2d56 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
@@ -83,7 +83,9 @@ import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.debug._Probe;
 import org.apache.isis.commons.internal.debug._Probe.EntryPoint;
 import org.apache.isis.commons.internal.functions._Functions.SerializableFunction;
+import org.apache.isis.commons.internal.functions._Functions.SerializableSupplier;
 import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataTableModel;
+import org.apache.isis.viewer.common.model.StringForRendering;
 import org.apache.isis.viewer.wicket.model.hints.IsisActionCompletedEvent;
 import org.apache.isis.viewer.wicket.model.hints.IsisEnvelopeEvent;
 import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings;
@@ -676,6 +678,22 @@ public class Wkt {
         return new Label(id, labelModel);
     }
 
+    /**
+     *  Whether to escape the underlying String for rendering
+     *  is dynamically based on the provided {@link StringForRendering}.
+     */
+    public Label labelWithDynamicEscaping(final String id, final SerializableSupplier<StringForRendering> labelModel) {
+        val label = new Label(id, ()->labelModel.get().getString()) {
+            private static final long serialVersionUID = 1L;
+            // we are using this method as a hook to update the ESCAPE flag before rendering
+            @Override public <C> IConverter<C> getConverter(final Class<C> type) {
+                setEscapeModelStrings(!labelModel.get().isMarkup());
+                return super.getConverter(type);
+            }
+        };
+        return label;
+    }
+
     public Label labelNoTab(final String id, final IModel<String> labelModel) {
         return new Label(id, labelModel) {
             private static final long serialVersionUID = 1L;