summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2014-12-19 01:50:32 +0200
committerArtur Signell <artur@vaadin.com>2015-01-02 16:40:29 +0200
commit26832b6947266ce5cffd92558c23e6556278038d (patch)
tree6e550fb5dc1510c22b5d76e097970a0f97e4c31d
parentfea60eaea2c791766be9f17ff2900739b32bf576 (diff)
downloadvaadin-framework-26832b6947266ce5cffd92558c23e6556278038d.tar.gz
vaadin-framework-26832b6947266ce5cffd92558c23e6556278038d.zip
Option for rendering Calendar event captions as HTML (#9030)
Change-Id: Ib7f6e67c242449e58a10359c596489fea2f679f6
-rw-r--r--WebContent/release-notes.html3
-rw-r--r--client/src/com/vaadin/client/ui/VCalendar.java30
-rw-r--r--client/src/com/vaadin/client/ui/calendar/CalendarConnector.java2
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java14
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java15
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java6
-rw-r--r--server/src/com/vaadin/ui/Calendar.java37
-rw-r--r--shared/src/com/vaadin/shared/ui/calendar/CalendarState.java1
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEvents.java101
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEventsTest.java103
10 files changed, 302 insertions, 10 deletions
diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html
index a060372580..7be74ee1ed 100644
--- a/WebContent/release-notes.html
+++ b/WebContent/release-notes.html
@@ -112,7 +112,7 @@
<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>
@@ -134,6 +134,7 @@
<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>
diff --git a/client/src/com/vaadin/client/ui/VCalendar.java b/client/src/com/vaadin/client/ui/VCalendar.java
index c59a78108c..08d4351931 100644
--- a/client/src/com/vaadin/client/ui/VCalendar.java
+++ b/client/src/com/vaadin/client/ui/VCalendar.java
@@ -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;
+ }
}
diff --git a/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
index 8f5e9d9a59..8c92ef1233 100644
--- a/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
+++ b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
@@ -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;
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
index 3b168b636c..8b08e9bc7a 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
@@ -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("");
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java b/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java
index 6fc2e430cd..31e600c8f9 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java
@@ -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));
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java
index bd833e06a0..9488c8835a 100644
--- a/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java
@@ -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;
}
}
diff --git a/server/src/com/vaadin/ui/Calendar.java b/server/src/com/vaadin/ui/Calendar.java
index 5b5c390fa1..206cc01d1a 100644
--- a/server/src/com/vaadin/ui/Calendar.java
+++ b/server/src/com/vaadin/ui/Calendar.java
@@ -297,6 +297,11 @@ public class Calendar extends AbstractComponent implements
}
@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;
+ }
+
}
diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java
index 93bd05bc1e..c26c4ead16 100644
--- a/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java
+++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java
@@ -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
index 0000000000..15cde71838
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEvents.java
@@ -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
index 0000000000..31e3f754e3
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarHtmlInEventsTest.java
@@ -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);
+ }
+}