Commit b3782d97 authored by Jeroen Hoffman's avatar Jeroen Hoffman

CMS-11068 Reintegrate branch 'bugfix/CMS-11068' into release/4.2

parents 99f58813 5de4fb33
/*
* Copyright 2018 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.frontend.plugins.standards.datetime;
import java.time.format.FormatStyle;
import java.util.Date;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.IModel;
import org.hippoecm.frontend.model.ReadOnlyModel;
public class DateLabel extends Label {
public DateLabel(final String id, final IModel<Date> model) {
super(id, ReadOnlyModel.of(() -> DatePrinter.of(model.getObject()).print()));
}
public DateLabel(final String id, final IModel<Date> model, final FormatStyle style) {
super(id, ReadOnlyModel.of(() -> DatePrinter.of(model.getObject()).print(style)));
}
public DateLabel(final String id, final IModel<Date> model, final String pattern) {
super(id, ReadOnlyModel.of(() -> DatePrinter.of(model.getObject()).print(pattern)));
}
}
/*
* Copyright 2018 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.frontend.plugins.standards.datetime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.util.io.IClusterable;
import org.hippoecm.frontend.plugins.standards.ClassResourceModel;
import org.hippoecm.frontend.session.UserSession;
/**
* Utility for printing java date objects using the java.time classes. Takes care of setting the correct locale and
* timezone. Prints the date but excludes the time.
*/
public interface DatePrinter extends IClusterable {
/**
* Print with default style (medium-short).
*
* @return the date as a string formatted in default style
*/
String print();
/**
* Print with specified pattern. See <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns">patterns</a>
* for all pattern options.
*
* @param pattern the pattern to use, not null
* @return the date as a string based on the pattern
*/
String print(final String pattern);
/**
* Print with specified FormatStyle. Check <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html">here</a>
* for all possible styles.
*
* @param style the formatter style to obtain, not null
* @return the date as a string based on the style
*/
String print(final FormatStyle style);
/**
* Append an explanatory string to the printed date if it is in Daylight Saving Time.
* Java shifts the time zone +1 if a date is in DST (e.g. CET becomes CEST), so to avoid confusion we add
* a description after the time zone (e.g. " (DST)" in English).
*
* @return the DatePrinter instance
*/
DatePrinter appendDST();
static DatePrinter of(final Date date) {
return date != null ? of(date.toInstant()) : EmptyDatePrinter.INSTANCE;
}
static DatePrinter of(final Calendar calendar) {
return calendar != null ? of(calendar.toInstant()) : EmptyDatePrinter.INSTANCE;
}
static DatePrinter of(final Instant instant) {
return instant != null ? new JavaDatePrinter(instant) : EmptyDatePrinter.INSTANCE;
}
class JavaDatePrinter implements DatePrinter {
private final Instant instant;
private final Locale locale;
private final ZoneId zoneId;
private boolean appendDST;
JavaDatePrinter(final Instant instant) {
this.instant = instant;
final UserSession session = UserSession.get();
if (session == null) {
throw new NullPointerException("Unable to retrieve user session");
}
locale = session.getLocale();
zoneId = toZoneId(session.getTimeZone());
}
private static ZoneId toZoneId(final TimeZone timeZone) {
return ZoneId.of(timeZone.getID(), ZoneId.SHORT_IDS);
}
@Override
public DatePrinter appendDST() {
appendDST = true;
return this;
}
@Override
public String print() {
return print(FormatStyle.MEDIUM);
}
@Override
public String print(final FormatStyle style) {
return print(DateTimeFormatter.ofLocalizedDate(style));
}
@Override
public String print(final String pattern) {
return print(DateTimeFormatter.ofPattern(pattern));
}
String print(DateTimeFormatter formatter) {
final ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, zoneId);
formatter = formatter.withLocale(locale);
return dateTime.format(formatter) + getSuffix();
}
private String getSuffix() {
final String dst = new ClassResourceModel("dst", JavaDatePrinter.class, locale, null).getObject();
return appendDST && isDST() ? " (" + dst + ")" : StringUtils.EMPTY;
}
private boolean isDST() {
return zoneId.getRules().isDaylightSavings(instant);
}
}
class EmptyDatePrinter implements DatePrinter {
private final static DatePrinter INSTANCE = new EmptyDatePrinter();
@Override
public String print() {
return StringUtils.EMPTY;
}
@Override
public String print(final String pattern) {
return print();
}
@Override
public String print(final FormatStyle style) {
return print();
}
@Override
public DatePrinter appendDST() {
return this;
}
}
}
/*
* Copyright 2016 Hippo B.V. (http://www.onehippo.com)
* Copyright 2016-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -15,50 +15,28 @@
*/
package org.hippoecm.frontend.plugins.standards.datetime;
import java.io.Serializable;
import java.time.format.FormatStyle;
import java.util.Date;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.hippoecm.frontend.model.ReadOnlyModel;
public class DateTimeLabel extends Label {
public DateTimeLabel(final String id, final IModel<Date> model) {
super(id, new DateTimePrinterModel(model, DateTimePrinter::print));
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print()));
}
public DateTimeLabel(final String id, final IModel<Date> model, final FormatStyle style) {
super(id, new DateTimePrinterModel(model, printer -> printer.print(style)));
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print(style)));
}
public DateTimeLabel(final String id, final IModel<Date> model, final FormatStyle dateStyle, final FormatStyle timeStyle) {
super(id, new DateTimePrinterModel(model, printer -> printer.print(dateStyle, timeStyle)));
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print(dateStyle, timeStyle)));
}
public DateTimeLabel(final String id, final IModel<Date> model, final String pattern) {
super(id, new DateTimePrinterModel(model, printer -> printer.print(pattern)));
}
private interface Printer extends Serializable {
String print(DateTimePrinter printer);
}
private static class DateTimePrinterModel extends AbstractReadOnlyModel<String> {
private final IModel<Date> dateModel;
private final Printer printer;
private DateTimePrinterModel(final IModel<Date> dateModel, final Printer printer) {
this.dateModel = dateModel;
this.printer = printer;
}
@Override
public String getObject() {
final Date date = dateModel.getObject();
return printer.print(DateTimePrinter.of(date));
}
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print(pattern)));
}
}
/*
* Copyright 2016 Hippo B.V. (http://www.onehippo.com)
* Copyright 2016-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -16,51 +16,21 @@
package org.hippoecm.frontend.plugins.standards.datetime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.util.io.IClusterable;
import org.hippoecm.frontend.plugins.standards.ClassResourceModel;
import org.hippoecm.frontend.session.UserSession;
/**
* Utility for printing java date objects using the java.time classes. Takes care of setting the correct locale and
* timezone.
* timezone and prints the date and the time.
*/
public interface DateTimePrinter extends IClusterable {
/**
* Print with default style (medium-short).
* @return the date as a string formatted in default style
*/
String print();
/**
* Print with specified pattern. See <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns">patterns</a>
* for all pattern options.
* @param pattern the pattern to use, not null
* @return the date as a string based on the pattern
*/
String print(final String pattern);
/**
* Print with specified FormatStyle. Check <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html">here</a>
* for all possible styles. The specified style will be used for both the date and the time part.
* @param style the formatter style to obtain, not null
* @return the date as a string based on the style
*/
String print(final FormatStyle style);
public interface DateTimePrinter extends DatePrinter {
/**
* Print with specified FormatStyles. Check <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html">here</a>
* for all possible styles. The dateStyle will be used for the date part, the timeStyle for the time part.
*
* @param dateStyle the formatter style to use for the date part, not null
* @param timeStyle the formatter style to use for the time part, not null
* @return the date as a string based the both styles
......@@ -68,11 +38,12 @@ public interface DateTimePrinter extends IClusterable {
String print(final FormatStyle dateStyle, final FormatStyle timeStyle);
/**
* Append an explanatory string to the printed date if it is in Daylight Saving Time.
* Java shifts the time zone +1 if a date is in DST (e.g. CET becomes CEST), so to avoid confusion we add
* a description after the time zone (e.g. " (DST)" in English).
* Override appendDST to return a more specific implementation DateTimePrinter and allow proper method chaining.
*
* @see DatePrinter#appendDST()
* @return the DateTimePrinter instance
*/
@Override
DateTimePrinter appendDST();
static DateTimePrinter of(final Date date) {
......@@ -84,42 +55,13 @@ public interface DateTimePrinter extends IClusterable {
}
static DateTimePrinter of(final Instant instant) {
if (instant == null) {
return EmptyDateTimePrinter.INSTANCE;
}
final UserSession session = UserSession.get();
if (session == null) {
throw new NullPointerException("Unable to retrieve user session");
}
final Locale locale = session.getLocale();
final ZoneId zoneId = toZoneId(session.getTimeZone());
return new JavaDateTimePrinter(instant, locale, zoneId);
}
static ZoneId toZoneId(final TimeZone timeZone) {
return ZoneId.of(timeZone.getID(), ZoneId.SHORT_IDS);
return instant != null ? new JavaDateTimePrinter(instant) : EmptyDateTimePrinter.INSTANCE;
}
class JavaDateTimePrinter implements DateTimePrinter {
private final Instant instant;
private final Locale locale;
private final ZoneId zoneId;
private boolean appendDST;
class JavaDateTimePrinter extends JavaDatePrinter implements DateTimePrinter {
private JavaDateTimePrinter(final Instant instant, final Locale locale, final ZoneId zoneId) {
this.instant = instant;
this.locale = locale;
this.zoneId = zoneId;
}
@Override
public DateTimePrinter appendDST() {
appendDST = true;
return this;
JavaDateTimePrinter(final Instant instant) {
super(instant);
}
@Override
......@@ -138,42 +80,16 @@ public interface DateTimePrinter extends IClusterable {
}
@Override
public String print(final String pattern) {
return print(DateTimeFormatter.ofPattern(pattern));
}
private String print(DateTimeFormatter formatter) {
final ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, zoneId);
formatter = formatter.withLocale(locale);
final String dst = new ClassResourceModel("dst", JavaDateTimePrinter.class, locale, null).getObject();
final String suffix = appendDST && isDST() ? " (" + dst + ")" : StringUtils.EMPTY;
return dateTime.format(formatter) + suffix;
}
private boolean isDST() {
return zoneId.getRules().isDaylightSavings(instant);
public DateTimePrinter appendDST() {
super.appendDST();
return this;
}
}
class EmptyDateTimePrinter implements DateTimePrinter {
class EmptyDateTimePrinter extends EmptyDatePrinter implements DateTimePrinter {
private final static DateTimePrinter INSTANCE = new EmptyDateTimePrinter();
@Override
public String print() {
return StringUtils.EMPTY;
}
@Override
public String print(final String pattern) {
return print();
}
@Override
public String print(final FormatStyle style) {
return print();
}
@Override
public String print(final FormatStyle dateStyle, final FormatStyle timeStyle) {
return print();
......
/*
* Copyright 2016 Hippo B.V. (http://www.onehippo.com)
* Copyright 2016-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -27,7 +27,11 @@ import org.apache.wicket.model.IModel;
/**
* Label component to render a date (year-month-day) of the {@link Date} object in GMT timezone.
*
* @deprecated no longer used because it uses a hardcoded GMT timezone, while user session time zone is to be used.
* Use DateTimeLabel instead.
*/
@Deprecated
public class GMTDateLabel extends ZonedDateLabel {
private static class GMTZonedDateTimeModel extends AbstractReadOnlyModel<ZonedDateTime> {
......
/*
* Copyright 2016 Hippo B.V. (http://www.onehippo.com)
* Copyright 2016-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -30,7 +30,10 @@ import org.apache.wicket.model.IModel;
/**
* Label component that represents only the date (year-month-day) of the
* {@link ZonedDateTime} model object in current session locale format.
*
* @deprecated no longer used because it was only used by deprecated GMTDateLabel.
*/
@Deprecated
public class ZonedDateLabel extends Label {
public ZonedDateLabel(final String id, final IModel<ZonedDateTime> model, final FormatStyle dateStyle) {
......
/*
* Copyright 2008-2016 Hippo B.V. (http://www.onehippo.com)
* Copyright 2008-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -17,19 +17,21 @@ package org.hippoecm.frontend.plugins.yui.datetime;
import java.util.Date;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.IModel;
import org.hippoecm.frontend.plugin.IPluginContext;
import org.hippoecm.frontend.plugin.config.IPluginConfig;
import org.hippoecm.frontend.util.MappingException;
import org.hippoecm.frontend.util.PluginConfigMapper;
public class DateFieldWidget extends Panel {
public class DateFieldWidget extends GenericPanel<Date> {
public static final String CONFIG_HIDE_TIME = "time.hide";
public static final String CONFIG_SHOW_TODAY_BUTTON = "show.today.button";
private final IPluginConfig config;
public DateFieldWidget(String id, IModel<Date> model, IPluginContext context, IPluginConfig config) {
public DateFieldWidget(final String id, final IModel<Date> model, final IPluginContext context, final IPluginConfig config) {
super(id, model);
this.config = config;
}
......@@ -37,25 +39,34 @@ public class DateFieldWidget extends Panel {
@Override
protected void onInitialize() {
super.onInitialize();
final IModel<Date> model = getModel();
final YuiDatePickerSettings settings = getSettings();
final YuiDateTimeField dateTimeField = newYuiDateTimeField("widget", model, settings);
final boolean currentDateLinkVisible = config.getAsBoolean(CONFIG_SHOW_TODAY_BUTTON, true);
dateTimeField.setCurrentDateLinkVisible(currentDateLinkVisible);
add(dateTimeField);
}
private YuiDatePickerSettings getSettings() {
final YuiDatePickerSettings settings = new YuiDatePickerSettings();
settings.setLanguage(getLocale().getLanguage());
if (config.containsKey("datepicker")) {
try {
PluginConfigMapper.populate(settings, config.getPluginConfig("datepicker"));
} catch (MappingException e) {
} catch (final MappingException e) {
throw new RuntimeException(e);
}
}
final IModel<Date> model = (IModel<Date>) getDefaultModel();
final YuiDateTimeField dateTimeField = newYuiDateTimeField("widget", model, settings);
boolean todayLinkVisible = config.getAsBoolean("show.today.button", true);
dateTimeField.setTodayLinkVisible(todayLinkVisible);
add(dateTimeField);
return settings;
}
protected YuiDateTimeField newYuiDateTimeField(String id, final IModel<Date> model, final YuiDatePickerSettings settings) {
final boolean isHideTime = config.getAsBoolean(CONFIG_HIDE_TIME, false);
return isHideTime ? new YuiGMTDateField(id, model, settings) : new YuiDateTimeField(id, model, settings);
protected YuiDateTimeField newYuiDateTimeField(final String id, final IModel<Date> model, final YuiDatePickerSettings settings) {
final boolean hideTime = config.getAsBoolean(CONFIG_HIDE_TIME, false);
return hideTime
? new YuiDateField(id, model, settings)
: new YuiDateTimeField(id, model, settings);
}
}
/*
* Copyright 2018 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.frontend.plugins.yui.datetime;
import java.util.Date;
import org.apache.wicket.model.IModel;
import org.joda.time.DateTimeFieldType;
import org.joda.time.MutableDateTime;
public class YuiDateField extends YuiDateTimeField {
private static final String CURRENT_DATE_LABEL = "set-to-current-date-only";
private static final String CURRENT_DATE_TOOLTIP = "set-to-current-date-only-tooltip";
public YuiDateField(final String id, final IModel<Date> model) {
this(id, model, null);
}
public YuiDateField(final String id, final IModel<Date> model, final YuiDatePickerSettings settings) {
super(id, model, settings);
// hiding the "hours" component hides the entire "hours" wicket:enclosure
get(HOURS).setVisibilityAllowed(false);
// hide the minutes field to prevent wicket.ajax javascript errors
get(MINUTES).setVisibilityAllowed(false);
}
@Override
String getTodayLinkLabel() {
return getString(CURRENT_DATE_LABEL);
}
@Override
String getTodayLinkTooltip() {
return getString(CURRENT_DATE_TOOLTIP);
}
/**
* Set the hour and minutes to 00:00. This 'removes' the time factor from affecting sort order when handling
* multiple documents with a date-only field.
*
* @param dateTime
*/
@Override
void setHourAndMinutes(final MutableDateTime dateTime) {
dateTime.set(DateTimeFieldType.hourOfDay(), 0);
dateTime.setMinuteOfHour(0);
}
}
<!--
* Copyright 2007-2016 Hippo B.V. (http://www.onehippo.com)
* Copyright 2007-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -34,9 +34,9 @@
</div>
</wicket:enclosure>
<div class="hippo-global-clearer"/>
<a wicket:id="today" class=" hippo-datepicker-reset btn btn-default btn-sm" wicket:message="title:set-to-current-date-tooltip">
<a wicket:id="current-date" class=" hippo-datepicker-reset btn btn-default btn-sm">
<span wicket:id="current-date-icon"/>
<span class="button-label"><wicket:message key="set-to-current-date"></wicket:message></span>
<span class="button-label" wicket:id="current-date-label"></span>
</a>
<div class="hippo-global-clearer"/>
</div>
......
/*
* Copyright 2008-2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2008-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -28,11 +28,13 @@ import org.apache.wicket.datetime.markup.html.form.DateTextField;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.extensions.yui.calendar.DatePicker;
import org.apache.wicket.extensions.yui.calendar.DateTimeField;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.hippoecm.frontend.plugins.standards.icon.HippoIcon;
import org.hippoecm.frontend.plugins.standards.list.resolvers.TitleAttribute;
import org.hippoecm.frontend.skin.Icon;
import org.hippoecm.frontend.widgets.UpdateFeedbackInfo;
import org.joda.time.DateTimeFieldType;
......@@ -44,33 +46,36 @@ import org.joda.time.format.DateTimeFormatter;
/**
* Semi-fork of YUI DateTimeField from Wicket extensions. Replaces Wicket extensions YUI behaviors with a {@link YuiDatePicker}
* so it fit's in the Hippo ECM YUI framework.
*
* <p>
* DatePicker can be configured using a frontend:pluginconfig node with name <code>datepicker</code>.
*
* @see YuiDatePickerSettings for all configuration options
*/
public class YuiDateTimeField extends DateTimeField {
private static final String CURRENT_DATE_TIME_TOOLTIP = "set-to-current-date-tooltip";
private static final String CURRENT_DATE_TIME_LABEL = "set-to-current-date";
public static final String DATE_LABEL = "date-label";
public static final String HOURS_LABEL = "hours-label";
public static final String MINUTES_LABEL = "minutes-label";