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 <ahu...@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;