]> source.dussan.org Git - vaadin-framework.git/commitdiff
Option for rendering Calendar event captions as HTML (#9030)
authorArtur Signell <artur@vaadin.com>
Thu, 18 Dec 2014 23:50:32 +0000 (01:50 +0200)
committerArtur Signell <artur@vaadin.com>
Fri, 2 Jan 2015 14:40:29 +0000 (16:40 +0200)
Change-Id: Ib7f6e67c242449e58a10359c596489fea2f679f6

WebContent/release-notes.html
client/src/com/vaadin/client/ui/VCalendar.java
client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java
client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java
server/src/com/vaadin/ui/Calendar.java
shared/src/com/vaadin/shared/ui/calendar/CalendarState.java
uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEvents.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEventsTest.java [new file with mode: 0644]

index a06037258032bf97df25db09401f0a3c7d690255..7be74ee1ed66b90b30a6b22229d376e86bc7a274 100644 (file)
             <li>Declarative layout support for initializing a component hierarchy from an HTML file.</li>
             <li>Uses GWT 2.7 for improved compilation times when using Super Dev Mode.</li>
             <li>@Viewport annotation for declaratively defining a mobile viewport definition for a UI.</li>
-            <li>Captions can be configured to be displayed as HTML.</li>
+            <li>Component captions, TabSheet/Accordion tab captions and Calendar event captions can be configured to be displayed as HTML.</li>
             <li>Selects use converters when presenting itemids.</li>
             <li>Improved performance when server response contains no visual changing (e.g. empty polling responses).</li>
             <li>Unified JSON library for using the same API in both server-side and client-side code.</li>
                 <p>Raw JSON values passed to AbstractJavaScriptComponent.callFunction and AbstractJavaScriptExtension.callFunction should be changed to use elemental.json types.</p>
             </li>
             <li>The semantics of empty and required for Field classes has been made more consistent. This mainly affects Checkbox which is now considered to be empty when it is not checked.</li>
+            <li>The previously inconsistent behavior in HTML vs plain text rendering of Calendar event captions has been made consistent.</li>
             <li>Support for Opera 12 has been dropped. Newer versions based on the Blink rendering engine are still supported.</li>
         </ul>
         <h3 id="knownissues">Known issues</h3>
index c59a78108c8a54efb197ff3f23ab40e0d774293f..08d4351931fb03774b7da7112c328433783211a1 100644 (file)
@@ -1342,6 +1342,7 @@ public class VCalendar extends Composite implements VHasDropHandler {
     private MouseEventListener mouseEventListener;
     private boolean forwardNavigationEnabled = true;
     private boolean backwardNavigationEnabled = true;
+    private boolean eventCaptionAsHtml = false;
 
     /**
      * Get the listener that listen to mouse events
@@ -1467,4 +1468,33 @@ public class VCalendar extends Composite implements VHasDropHandler {
     public void setDropHandler(CalendarDropHandler dropHandler) {
         this.dropHandler = dropHandler;
     }
+
+    /**
+     * Sets whether the event captions are rendered as HTML.
+     * <p>
+     * If set to true, the captions are rendered in the browser as HTML and the
+     * developer is responsible for ensuring no harmful HTML is used. If set to
+     * false, the caption is rendered in the browser as plain text.
+     * <p>
+     * The default is false, i.e. to render that caption as plain text.
+     * 
+     * @param captionAsHtml
+     *            true if the captions are rendered as HTML, false if rendered
+     *            as plain text
+     */
+    public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) {
+        this.eventCaptionAsHtml = eventCaptionAsHtml;
+    }
+
+    /**
+     * Checks whether event captions are rendered as HTML
+     * <p>
+     * The default is false, i.e. to render that caption as plain text.
+     * 
+     * @return true if the captions are rendered as HTML, false if rendered as
+     *         plain text
+     */
+    public boolean isEventCaptionAsHtml() {
+        return eventCaptionAsHtml;
+    }
 }
index 8f5e9d9a59feb7b622fe483ba41e22fe3decd9e5..8c92ef1233afd32d180fc520ce4738fe36b34305 100644 (file)
@@ -345,6 +345,8 @@ public class CalendarConnector extends AbstractComponentConnector implements
         widget.setEventMoveAllowed(hasEventListener(CalendarEventId.EVENTMOVE));
         widget.setEventResizeAllowed(hasEventListener(CalendarEventId.EVENTRESIZE));
 
+        widget.setEventCaptionAsHtml(state.eventCaptionAsHtml);
+
         List<CalendarState.Day> days = state.days;
         List<CalendarState.Event> events = state.events;
 
index 3b168b636c586f009fa71dfd0432d41ec7246dbc..8b08e9bc7a0b527db2317e079b42a8cdf2613e43 100644 (file)
@@ -184,14 +184,20 @@ public class DateCellDayEvent extends FocusableHTML implements
      */
     private void updateCaptions(boolean bigMode) {
         String innerHtml;
-        String escapedCaption = Util.escapeHTML(calendarEvent.getCaption());
         String timeAsText = calendarEvent.getTimeAsText();
+        String htmlOrText;
+
+        if (dateCell.weekgrid.getCalendar().isEventCaptionAsHtml()) {
+            htmlOrText = calendarEvent.getCaption();
+        } else {
+            htmlOrText = Util.escapeHTML(calendarEvent.getCaption());
+        }
+
         if (bigMode) {
-            innerHtml = "<span>" + timeAsText + "</span><br />"
-                    + escapedCaption;
+            innerHtml = "<span>" + timeAsText + "</span><br />" + htmlOrText;
         } else {
             innerHtml = "<span>" + timeAsText + "<span>:</span></span> "
-                    + escapedCaption;
+                    + htmlOrText;
         }
         caption.setInnerHTML(innerHtml);
         eventContent.setInnerHTML("");
index 6fc2e430cd1aeceda591b1b504a5a1cbf0652ba3..31e600c8f977ea9b413f4661ae63b47fe4e7991b 100644 (file)
@@ -20,6 +20,7 @@ import java.util.Date;
 import com.google.gwt.event.dom.client.ContextMenuEvent;
 import com.google.gwt.event.dom.client.ContextMenuHandler;
 import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.client.Util;
 import com.vaadin.client.ui.VCalendar;
 
 /**
@@ -75,7 +76,8 @@ public class MonthEventLabel extends HTML implements HasTooltipKey {
      * Set the caption of the event label
      * 
      * @param caption
-     *            The caption string, can be HTML
+     *            The caption string, can be HTML if
+     *            {@link VCalendar#isEventCaptionAsHtml()} is true
      */
     public void setCaption(String caption) {
         this.caption = caption;
@@ -87,13 +89,20 @@ public class MonthEventLabel extends HTML implements HasTooltipKey {
      */
     private void renderCaption() {
         StringBuilder html = new StringBuilder();
+        String textOrHtml;
+        if (calendar.isEventCaptionAsHtml()) {
+            textOrHtml = caption;
+        } else {
+            textOrHtml = Util.escapeHTML(caption);
+        }
+
         if (caption != null && time != null) {
             html.append("<span class=\"" + STYLENAME + "-time\">");
             html.append(calendar.getTimeFormat().format(time));
             html.append("</span> ");
-            html.append(caption);
+            html.append(textOrHtml);
         } else if (caption != null) {
-            html.append(caption);
+            html.append(textOrHtml);
         } else if (time != null) {
             html.append("<span class=\"" + STYLENAME + "-time\">");
             html.append(calendar.getTimeFormat().format(time));
index bd833e06a00e5cb30ad7e5700a89d3facb237637..9488c8835af529984af6b5112acae2c7104cbc30 100644 (file)
@@ -102,7 +102,11 @@ public class WeeklyLongEvents extends HorizontalPanel implements HasTooltipKey {
                     eventLabel.addStyleDependentName(extraStyle + "-all-day");
                 }
                 if (!started) {
-                    eventLabel.setText(calendarEvent.getCaption());
+                    if (calendar.isEventCaptionAsHtml()) {
+                        eventLabel.setHTML(calendarEvent.getCaption());
+                    } else {
+                        eventLabel.setText(calendarEvent.getCaption());
+                    }
                     started = true;
                 }
             }
index 5b5c390fa1a887c2dff423741c62006cf33c685b..206cc01d1a46d9b615fabf2100aadc3a256de4e3 100644 (file)
@@ -296,6 +296,11 @@ public class Calendar extends AbstractComponent implements
         return (CalendarState) super.getState();
     }
 
+    @Override
+    protected CalendarState getState(boolean markAsDirty) {
+        return (CalendarState) super.getState(markAsDirty);
+    }
+
     @Override
     public void beforeClientResponse(boolean initial) {
         super.beforeClientResponse(initial);
@@ -1667,7 +1672,7 @@ public class Calendar extends AbstractComponent implements
      *         weekly mode
      */
     public boolean isMonthlyMode() {
-        CalendarState state = (CalendarState) getState(false);
+        CalendarState state = getState(false);
         if (state.days != null) {
             return state.days.size() > 7;
         } else {
@@ -1895,4 +1900,34 @@ public class Calendar extends AbstractComponent implements
             dropHandler.getAcceptCriterion().paint(target);
         }
     }
+
+    /**
+     * Sets whether the event captions are rendered as HTML.
+     * <p>
+     * If set to true, the captions are rendered in the browser as HTML and the
+     * developer is responsible for ensuring no harmful HTML is used. If set to
+     * false, the caption is rendered in the browser as plain text.
+     * <p>
+     * The default is false, i.e. to render that caption as plain text.
+     * 
+     * @param captionAsHtml
+     *            true if the captions are rendered as HTML, false if rendered
+     *            as plain text
+     */
+    public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) {
+        getState().eventCaptionAsHtml = eventCaptionAsHtml;
+    }
+
+    /**
+     * Checks whether event captions are rendered as HTML
+     * <p>
+     * The default is false, i.e. to render that caption as plain text.
+     * 
+     * @return true if the captions are rendered as HTML, false if rendered as
+     *         plain text
+     */
+    public boolean isEventCaptionAsHtml() {
+        return getState(false).eventCaptionAsHtml;
+    }
+
 }
index 93bd05bc1ec6bdfef7be9999454f74335a277cdd..c26c4ead16cdb14a93ddf7bab30079f87118a645 100644 (file)
@@ -38,6 +38,7 @@ public class CalendarState extends AbstractComponentState {
     public List<CalendarState.Day> days;
     public List<CalendarState.Event> events;
     public List<CalendarState.Action> actions;
+    public boolean eventCaptionAsHtml;
 
     public static class Day implements java.io.Serializable {
         public String date;
diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEvents.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEvents.java
new file mode 100644 (file)
index 0000000..15cde71
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed 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 com.vaadin.tests.components.calendar;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.util.MethodProperty;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider;
+
+public class CalendarHtmlInEvents extends AbstractTestUIWithLog {
+
+    private Calendar calendar = new Calendar();
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        final NativeSelect ns = new NativeSelect("Period");
+        ns.addItems("Day", "Week", "Month");
+        ns.addValueChangeListener(new ValueChangeListener() {
+            @Override
+            public void valueChange(ValueChangeEvent event) {
+                if ("Day".equals(ns.getValue())) {
+                    calendar.setStartDate(new Date(2014 - 1900, 1 - 1, 1));
+                    calendar.setEndDate(new Date(2014 - 1900, 1 - 1, 1));
+                } else if ("Week".equals(ns.getValue())) {
+                    calendar.setStartDate(new Date(2014 - 1900, 1 - 1, 1));
+                    calendar.setEndDate(new Date(2014 - 1900, 1 - 1, 7));
+                } else if ("Month".equals(ns.getValue())) {
+                    calendar.setStartDate(new Date(2014 - 1900, 1 - 1, 1));
+                    calendar.setEndDate(new Date(2014 - 1900, 2 - 1, 1));
+                }
+            }
+        });
+        ns.setValue("Month");
+        final CheckBox allowHtml = new CheckBox("Allow HTML in event caption",
+                new MethodProperty<Boolean>(calendar, "eventCaptionAsHtml"));
+        allowHtml.addValueChangeListener(new ValueChangeListener() {
+            @Override
+            public void valueChange(ValueChangeEvent event) {
+                log("HTML in event caption: " + allowHtml.getValue());
+            }
+        });
+        HorizontalLayout hl = new HorizontalLayout();
+        hl.setDefaultComponentAlignment(Alignment.BOTTOM_LEFT);
+        hl.addComponents(ns, allowHtml);
+        hl.setSpacing(true);
+        hl.setMargin(true);
+        calendar.setEventProvider(new CalendarEventProvider() {
+
+            @Override
+            public List<CalendarEvent> getEvents(Date startDate, Date endDate) {
+                Date d = startDate;
+                ArrayList<CalendarEvent> events = new ArrayList<CalendarEvent>();
+                while (d.before(endDate)) {
+                    BasicEvent ce = new BasicEvent();
+                    ce.setAllDay(false);
+                    ce.setCaption("<b>Hello</b> <u>world</u>!");
+                    ce.setDescription("Nothing really important");
+                    Date start = new Date(d.getTime());
+                    start.setHours(d.getDay());
+                    Date end = new Date(d.getTime());
+                    end.setHours(d.getDay() + 3);
+                    ce.setStart(start);
+                    ce.setEnd(end);
+                    events.add(ce);
+                    d.setTime(d.getTime() + 1000 * 60 * 60 * 24);
+                }
+
+                return events;
+            }
+
+        });
+        addComponent(hl);
+        addComponent(calendar);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEventsTest.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEventsTest.java
new file mode 100644 (file)
index 0000000..31e3f75
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed 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 com.vaadin.tests.components.calendar;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.elements.CalendarElement;
+import com.vaadin.testbench.elements.CheckBoxElement;
+import com.vaadin.testbench.elements.NativeSelectElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class CalendarHtmlInEventsTest extends SingleBrowserTest {
+
+    private NativeSelectElement periodSelect;
+    private CheckBoxElement htmlAllowed;
+    private CalendarElement calendar;
+
+    @Override
+    public void setup() throws Exception {
+        super.setup();
+        openTestURL();
+        periodSelect = $(NativeSelectElement.class).first();
+        htmlAllowed = $(CheckBoxElement.class).first();
+        calendar = $(CalendarElement.class).first();
+    }
+
+    @Test
+    public void monthViewEventCaptions() {
+        Assert.assertEquals(getMonthEvent(0).getText(),
+                "12:00 AM <b>Hello</b> <u>world</u>!");
+
+        // Switch to HTML mode
+        click(htmlAllowed);
+        Assert.assertEquals("1. HTML in event caption: true", getLogRow(0));
+
+        Assert.assertEquals(getMonthEvent(0).getText(), "12:00 AM Hello world!");
+    }
+
+    @Test
+    public void weekViewEventCaptions() {
+        periodSelect.selectByText("Week");
+        Assert.assertEquals("4:00 AM\n<b>Hello</b> <u>world</u>!",
+                getWeekEvent(1).getText());
+
+        // Switch to HTML mode
+        click(htmlAllowed);
+        Assert.assertEquals("1. HTML in event caption: true", getLogRow(0));
+
+        Assert.assertEquals("4:00 AM\nHello world!", getWeekEvent(1).getText());
+    }
+
+    @Test
+    public void dayViewEventCaptions() {
+        periodSelect.selectByText("Day");
+        Assert.assertEquals("3:00 AM\n<b>Hello</b> <u>world</u>!",
+                getWeekEvent(0).getText());
+
+        // Switch to HTML mode
+        click(htmlAllowed);
+        Assert.assertEquals("1. HTML in event caption: true", getLogRow(0));
+        Assert.assertEquals("3:00 AM\nHello world!", getWeekEvent(0).getText());
+    }
+
+    private WebElement getMonthEvent(int dayInCalendar) {
+        return getMonthDay(dayInCalendar).findElement(
+                By.className("v-calendar-event"));
+    }
+
+    private WebElement getWeekEvent(int dayInCalendar) {
+        return getWeekDay(dayInCalendar).findElement(
+                By.className("v-calendar-event"));
+    }
+
+    private void click(CheckBoxElement htmlAllowed2) {
+        htmlAllowed2.findElement(By.xpath("input")).click();
+    }
+
+    private WebElement getMonthDay(int i) {
+        return calendar.findElements(By.className("v-calendar-month-day")).get(
+                i);
+    }
+
+    private WebElement getWeekDay(int i) {
+        return calendar.findElements(By.className("v-calendar-day-times")).get(
+                i);
+    }
+}