Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

DateField improvements:

- Remove the lat/long to time zone conversion code (requesting user's geolocation too intrusive)
- Allow time zone tracking cookie to be secure
- Add min and max parameters to DateField component
  • Loading branch information...
commit 9eca44493baaf82cf29242efb8304718318b40e2 1 parent 0f582ab
@hlship authored
Showing with 277 additions and 365 deletions.
  1. +1 −0  tapx-datefield/build.gradle
  2. +5 −4 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/DateFieldSymbols.java
  3. +94 −56 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/components/DateField.java
  4. +25 −24 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/components/TimeZoneIdentifier.java
  5. +18 −13 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/services/ClientTimeZoneData.java
  6. +10 −28 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/services/DateFieldModule.java
  7. +0 −20 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/services/LatLongToTimeZoneResolver.java
  8. +5 −6 ...datefield/src/main/java/com/howardlewisship/tapx/internal/datefield/services/BestGuessTimeZoneAnalyzer.java
  9. +37 −6 ...datefield/src/main/java/com/howardlewisship/tapx/internal/datefield/services/ClientTimeZoneTrackerImpl.java
  10. +0 −113 tapx-datefield/src/main/java/com/howardlewisship/tapx/internal/datefield/services/GeonameTimeZoneResolver.java
  11. +0 −38 tapx-datefield/src/main/java/com/howardlewisship/tapx/internal/datefield/services/LatLongTimeZoneAnalyzer.java
  12. +7 −3 tapx-datefield/src/main/java/com/howardlewisship/tapx/internal/datefield/services/SystemTimeZoneAnalyzer.java
  13. +14 −32 tapx-datefield/src/main/resources/com/howardlewisship/tapx/datefield/components/time-zone-identifier.js
  14. +28 −9 tapx-datefield/src/main/resources/com/howardlewisship/tapx/datefield/tapx-datefield.js
  15. +4 −1 tapx-datefield/src/main/resources/com/howardlewisship/tapx/datefield/tapx-datefield.properties
  16. +27 −10 tapx-datefield/src/test/java/demo/pages/DateFieldDemo.java
  17. +2 −2 tapx-datefield/src/test/resources/demo/pages/DateFieldDemo.tml
View
1  tapx-datefield/build.gradle
@@ -2,6 +2,7 @@ description = "Improved JavaScript date/time selection control"
dependencies {
compile project(':tapx-core')
+ provided "javax.servlet:servlet-api:2.5"
}
jar {
View
9 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/DateFieldSymbols.java
@@ -1,4 +1,4 @@
-// Copyright 2009 Howard M. Lewis Ship
+// Copyright 2009, 2012 Howard M. Lewis Ship
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -32,10 +32,11 @@
public static final String THEME = "tapx.datefield.theme";
/**
- * Symbol that defines the base URL for accessing the GeoNames database. This can be overridden just for
- * testing purposes.
+ * If true, then the cookie used to track the user's time zone (once identified) is secure.
+ * Default is false.
*
* @since 1.2
*/
- public static final String GEONAMES_URL = "tapx.geonames.url";
+ public static final String SECURE_TIME_ZONE_COOKIE = "tapx.datefield.secure-timezone-cookie";
+
}
View
150 tapx-datefield/src/main/java/com/howardlewisship/tapx/datefield/components/DateField.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010, 2011 Howard M. Lewis Ship
+// Copyright 2009, 2010, 2011, 2012 Howard M. Lewis Ship
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,25 +14,11 @@
package com.howardlewisship.tapx.datefield.components;
-import java.lang.annotation.Annotation;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import org.apache.tapestry5.Asset;
-import org.apache.tapestry5.Binding;
-import org.apache.tapestry5.BindingConstants;
-import org.apache.tapestry5.ComponentResources;
-import org.apache.tapestry5.FieldValidationSupport;
-import org.apache.tapestry5.FieldValidator;
-import org.apache.tapestry5.MarkupWriter;
-import org.apache.tapestry5.ValidationException;
-import org.apache.tapestry5.ValidationTracker;
+import com.howardlewisship.tapx.datefield.TimeSignificant;
+import com.howardlewisship.tapx.datefield.TimeZoneVisibility;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
+import com.howardlewisship.tapx.datefield.services.DateFieldFormatConverter;
+import org.apache.tapestry5.*;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.Parameter;
@@ -50,17 +36,18 @@
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
-import com.howardlewisship.tapx.datefield.TimeSignificant;
-import com.howardlewisship.tapx.datefield.TimeZoneVisibility;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
-import com.howardlewisship.tapx.datefield.services.DateFieldFormatConverter;
+import java.lang.annotation.Annotation;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
/**
* A replacement for Tapestry's built-in DateField, built around the <a
* href="http://www.dynarch.com/projects/calendar/old/">Dynarch
* JSCalendar Widget</a>. This is a highly functional calendar, but is distributed as LGPL and so
* can't be built directly into Tapestry.
- * <p>
+ * <p/>
* Starting with version 1.1, there is enhanced control over the time zone displayed to the user (including the optional
* ability to edit the time zone). You will likely want to include a {@link TimeZoneIdentifier} component in your
* application's Layout to ensure that the correct time zone for the client is used.
@@ -93,7 +80,7 @@
/**
* If true, then the calendar will include time selection as well as date selection. This is
* normally false, unless the property has the {@link TimeSignificant} annotation.
- *
+ *
* @since 1.1
*/
@Parameter
@@ -101,7 +88,7 @@
/**
* Controls how the time zone is presented to the user; the default (for compatibility) is NONE.
- *
+ *
* @since 1.1
*/
@Parameter(value = "none", defaultPrefix = BindingConstants.LITERAL)
@@ -118,20 +105,31 @@
* Object that will provide access to annotations (such as {@link TimeSignificant}). This is
* only used when configuring DateField to work within the {@link BeanEditor}. Normally,
* annotations come from the property bound the value parameter.
- *
+ *
* @since 1.1
*/
@Parameter
private AnnotationProvider annotationProvider;
/**
+ * Sets optional maximum and minimum dates for the calendar. This is enforced on the client-side
+ * by making dates outside the range non-selectable, and is enforced on the server as a validation error.
+ *
+ * @since 1.2
+ */
+ @Parameter
+ private Date max, min;
+
+ /**
* The object that will perform input validation (which occurs after translation). The translate
* binding prefix is generally used to provide this object in a declarative fashion.
*/
@Parameter(defaultPrefix = BindingConstants.VALIDATE)
private FieldValidator<Object> validate;
- /** The icon to use for the button that raises the calendar popup. */
+ /**
+ * The icon to use for the button that raises the calendar popup.
+ */
@Parameter(defaultPrefix = BindingConstants.ASSET, value = "datefield.gif")
private Asset icon;
@@ -232,23 +230,27 @@ public void beginRender(MarkupWriter writer)
String value = tracker.getInput(this);
if (value == null)
+ {
value = formatCurrentValue();
+ }
String clientId = getClientId();
String triggerId = clientId + "-trigger";
writer.element("input",
- "type", hideTextField ? "hidden" : "text",
+ "type", hideTextField ? "hidden" : "text",
- "name", getControlName(),
+ "name", getControlName(),
- "id", clientId,
+ "id", clientId,
- "value", value);
+ "value", value);
if (isDisabled())
+ {
writer.attributes("disabled", "disabled");
+ }
validate.render(writer);
@@ -262,19 +264,31 @@ public void beginRender(MarkupWriter writer)
writer.element("img",
- "id", triggerId,
+ "id", triggerId,
- "class", "t-calendar-trigger",
+ "class", "t-calendar-trigger",
- "src", icon.toClientURL(),
+ "src", icon.toClientURL(),
- "alt", "[Show]");
+ "alt", "[Show]");
writer.end(); // img
writeTimeZone(writer);
- JSONObject spec = new JSONObject("clientId", clientId, "clientDateFormat",
- formatConverter.convertToClient(format)).put("time", time).put("singleClick", singleClick);
+ JSONObject spec = new JSONObject("clientId", clientId,
+ "clientDateFormat", formatConverter.convertToClient(format))
+ .put("time", time)
+ .put("singleClick", singleClick);
+
+ if (max != null)
+ {
+ spec.put("max", convertDateToClientTimeZone(max).getTime());
+ }
+
+ if (min != null)
+ {
+ spec.put("min", convertDateToClientTimeZone(min).getTime());
+ }
javascriptSupport.addInitializerCall("tapxDateField", spec);
}
@@ -346,7 +360,9 @@ private void writeTimeZone(MarkupWriter writer)
private String formatCurrentValue()
{
if (value == null)
+ {
return "";
+ }
format.setTimeZone(timeZoneTracker.getClientTimeZone());
@@ -373,20 +389,9 @@ protected void processSubmission(String elementName)
Date inDefaultTimeZone = format.parse(value);
- Calendar c = Calendar.getInstance(locale);
- c.setTime(inDefaultTimeZone);
-
- TimeZone clientZone = timeZoneTracker.getClientTimeZone();
- TimeZone defaultZone = TimeZone.getDefault();
- long now = System.currentTimeMillis();
- int offset = defaultZone.getOffset(now) - clientZone.getOffset(now);
-
- c.add(Calendar.MILLISECOND, offset);
-
- parsedValue = c.getTime();
+ parsedValue = convertDateToClientTimeZone(inDefaultTimeZone);
}
- }
- catch (ParseException ex)
+ } catch (ParseException ex)
{
tracker.recordError(this, messages.format("tapx-date-value-not-parseable", value));
return;
@@ -396,12 +401,46 @@ protected void processSubmission(String elementName)
{
fieldValidationSupport.validate(parsedValue, resources, validate);
- this.value = parsedValue;
- }
- catch (ValidationException ex)
+ } catch (ValidationException ex)
{
tracker.recordError(this, ex.getMessage());
+
+ return;
+ }
+
+ if (min != null && parsedValue.before(min)) {
+ tracker.recordError(this, messages.get("tapx-date-value-to-early"));
+ return;
+ }
+
+ if (max != null && parsedValue.after(max)) {
+ tracker.recordError(this, messages.get("tapx-date-value-too-late"));
+ return;
}
+
+ this.value = parsedValue;
+ }
+
+ /**
+ * Converts a Date in default time zone to client's time zone.
+ *
+ * @param inputDate
+ * a date in the default time zone
+ * @return a date in the client time zone
+ */
+ private Date convertDateToClientTimeZone(Date inputDate)
+ {
+ Calendar c = Calendar.getInstance(locale);
+ c.setTime(inputDate);
+
+ TimeZone clientZone = timeZoneTracker.getClientTimeZone();
+ TimeZone defaultZone = TimeZone.getDefault();
+ long now = System.currentTimeMillis();
+ int offset = defaultZone.getOffset(now) - clientZone.getOffset(now);
+
+ c.add(Calendar.MILLISECOND, offset);
+
+ return c.getTime();
}
private void updateClientTimeZone(String elementName)
@@ -414,6 +453,5 @@ private void updateClientTimeZone(String elementName)
TimeZone tz = TimeZone.getTimeZone(id);
timeZoneTracker.setClientTimeZone(tz);
-
}
}
View
49 ...field/src/main/java/com/howardlewisship/tapx/datefield/components/TimeZoneIdentifier.java
@@ -1,41 +1,49 @@
-package com.howardlewisship.tapx.datefield.components;
+// Copyright 2011, 2012 Howard M. Lewis Ship
+//
+// 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.
-import java.util.TimeZone;
+package com.howardlewisship.tapx.datefield.components;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneAnalyzer;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneData;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.RequestParameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Primary;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneAnalyzer;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneData;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
+import java.util.TimeZone;
/**
* Determines if {@linkplain ClientTimeZoneTracker#isClientTimeZoneIdentified() the client has
* identified the time zone} and, if not, adds JavaScript to the page to send the time zone
- * information to the server. The JavaScript will ask for access to geolocation data available on
- * the
- * client (this works in Firefox and Chrome) and will report the client's latitude and longitude.
- * If geolocation data is not available, other date information is used to determine the best
- * matching TimeZone.
- * <p>
+ * information to the server.
+ * <p/>
* Typically, this component is placed into the application's <em>Layout</em> component (a common
* component that defines the global layout of the application).
- * <p>
+ * <p/>
* After the time zone is identified, an event, "tapx:time-zone-identified" is triggered on the
* document object. The memo of the event is a JSON object with key "timeZoneId".
- * <p>
+ * <p/>
* TODO: Seems like collecting this information is just part of a larger cycle of determining
* exactly what's running on the client ... imagine if we knew exactly what browser was out there,
* and what features it supported, and could customize to that!
- *
+ *
* @see ClientTimeZoneTracker
* @see ClientTimeZoneAnalyzer
*/
@@ -75,12 +83,12 @@ void beginRender()
}
Object onIdentifyTimeZone(@RequestParameter("offsetMinutes")
- int offsetMinutes, @RequestParameter("dateString")
+ int offsetMinutes, @RequestParameter("dateString")
String dateString, @RequestParameter("epochMillis")
long epochMillis)
{
TimeZone timeZone = timeZoneAnalyzer.extractTimeZone(new ClientTimeZoneData(dateString,
- offsetMinutes, epochMillis, getDouble("latitude"), getDouble("longitude")));
+ offsetMinutes, epochMillis));
tracker.setClientTimeZone(timeZone);
@@ -88,11 +96,4 @@ Object onIdentifyTimeZone(@RequestParameter("offsetMinutes")
return response;
}
-
- private Double getDouble(String name)
- {
- String value = request.getParameter(name);
-
- return InternalUtils.isBlank(value) ? null : new Double(value);
- }
}
View
31 ...tefield/src/main/java/com/howardlewisship/tapx/datefield/services/ClientTimeZoneData.java
@@ -1,3 +1,17 @@
+// Copyright 2012 Howard M. Lewis Ship
+//
+// 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.howardlewisship.tapx.datefield.services;
/**
@@ -11,28 +25,19 @@
public final long epochMillis;
- public final Double latitude, longitude;
-
/**
* @param date
- * the client-side date as a string, formatted by the client using toString()
+ * the client-side date as a string, formatted by the client using toString()
* @param offsetMinutes
- * offset, in minutes, from GMT
+ * offset, in minutes, from GMT
* @param epochMillis
- * client reported time in milliseconds sinch the epoch (Jan 1 1970)
- * @param latitude
- * reported latitude value from client (via geolocation), may be null
- * @param longitude
- * reported longitude value form client (via geolocation), may be null
+ * client reported time in milliseconds sinch the epoch (Jan 1 1970)
*/
- public ClientTimeZoneData(String date, int offsetMinutes, long epochMillis, Double latitude,
- Double longitude)
+ public ClientTimeZoneData(String date, int offsetMinutes, long epochMillis)
{
this.date = date;
this.offsetMinutes = offsetMinutes;
this.epochMillis = epochMillis;
- this.latitude = latitude;
- this.longitude = longitude;
}
}
View
38 ...-datefield/src/main/java/com/howardlewisship/tapx/datefield/services/DateFieldModule.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010 Howard M. Lewis Ship
+// Copyright 2009, 2010, 2012 Howard M. Lewis Ship
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,20 +15,20 @@
package com.howardlewisship.tapx.datefield.services;
import com.howardlewisship.tapx.datefield.DateFieldSymbols;
-import com.howardlewisship.tapx.datefield.TimeZoneVisibility;
-import com.howardlewisship.tapx.internal.datefield.services.*;
+import com.howardlewisship.tapx.internal.datefield.services.BestGuessTimeZoneAnalyzer;
+import com.howardlewisship.tapx.internal.datefield.services.ClientTimeZoneTrackerImpl;
+import com.howardlewisship.tapx.internal.datefield.services.DateFieldFormatConverterImpl;
+import com.howardlewisship.tapx.internal.datefield.services.SystemTimeZoneAnalyzer;
import org.apache.tapestry5.ioc.*;
import org.apache.tapestry5.ioc.annotations.Contribute;
import org.apache.tapestry5.ioc.annotations.Marker;
import org.apache.tapestry5.ioc.annotations.Primary;
import org.apache.tapestry5.ioc.annotations.Value;
import org.apache.tapestry5.ioc.services.ChainBuilder;
-import org.apache.tapestry5.ioc.services.CoercionTuple;
import org.apache.tapestry5.services.BeanBlockContribution;
import org.apache.tapestry5.services.EditBlockContribution;
import org.apache.tapestry5.services.LibraryMapping;
import org.apache.tapestry5.services.javascript.JavaScriptStack;
-import org.apache.tapestry5.util.StringToEnumCoercion;
import java.util.List;
@@ -38,16 +38,15 @@ public static void bind(ServiceBinder binder)
{
binder.bind(DateFieldFormatConverter.class, DateFieldFormatConverterImpl.class);
binder.bind(ClientTimeZoneTracker.class, ClientTimeZoneTrackerImpl.class);
- binder.bind(LatLongToTimeZoneResolver.class, GeonameTimeZoneResolver.class);
}
- public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
+ public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration)
{
configuration.add(DateFieldSymbols.JSCALENDAR_PATH,
"classpath:/com/howardlewisship/tapx/datefield/jscalendar-1.0");
configuration.add(DateFieldSymbols.SKIN, "aqua");
configuration.add(DateFieldSymbols.THEME, "");
- configuration.add(DateFieldSymbols.GEONAMES_URL, "http://ws.geonames.org/timezoneJSON");
+ configuration.add(DateFieldSymbols.SECURE_TIME_ZONE_COOKIE, false);
}
public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration)
@@ -77,33 +76,17 @@ public static void contributeComponentMessagesSource(
@Marker(Primary.class)
public static ClientTimeZoneAnalyzer build(List<ClientTimeZoneAnalyzer> configuration,
- ChainBuilder builder)
+ ChainBuilder builder)
{
return builder.build(ClientTimeZoneAnalyzer.class, configuration);
}
/**
- * Contributes conversions:
- * <ul>
- * <li>String --&gt; {@link TimeZoneVisibility}</li>
- * </ul>
- */
- @SuppressWarnings("rawtypes")
- public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration)
- {
- configuration.add(CoercionTuple.create(String.class, TimeZoneVisibility.class,
- StringToEnumCoercion.create(TimeZoneVisibility.class)));
-
- }
-
- /**
* Provides the default set of {@link ClientTimeZoneAnalyzer}s:
* <dl>
- * <dt>LatLong</dt>
- * <dd>Uses {@link LatLongToTimeZoneResolver}, if latitude and longitude are available
* <dt>BestGuess</dt>
* <dd>Finds <em>some</em> TimeZone that has the correct offset (remember that multiple
- * TimeZones often share an offset); ordered after LatLong
+ * TimeZones often share an offset)
* <dt>System</dt>
* <dd>Fallback, ordered last, the returns the System's default time zone
* </dl>
@@ -113,8 +96,7 @@ public static void contributeTypeCoercer(Configuration<CoercionTuple> configurat
public static void setupDefaultAnalyzers(
OrderedConfiguration<ClientTimeZoneAnalyzer> configuration)
{
- configuration.addInstance("LatLong", LatLongTimeZoneAnalyzer.class);
- configuration.add("BestGuess", new BestGuessTimeZoneAnalyzer(), "after:LatLong");
+ configuration.add("BestGuess", new BestGuessTimeZoneAnalyzer());
configuration.add("System", new SystemTimeZoneAnalyzer(), "after:*");
}
}
View
20 .../src/main/java/com/howardlewisship/tapx/datefield/services/LatLongToTimeZoneResolver.java
@@ -1,20 +0,0 @@
-package com.howardlewisship.tapx.datefield.services;
-
-import java.util.TimeZone;
-
-/**
- * Given a latitude and longitude (as reported by the client-side geolocation API), resolve that
- * location to a particular time zone. The default implementation uses a web service provided by
- * http://www.geonames.org.
- */
-public interface LatLongToTimeZoneResolver
-{
- /**
- * Identifies the TimeZone via its location.
- *
- * @param latitude
- * @param longitude
- * @return TimeZone if identified, or null
- */
- TimeZone resolveTimeZone(double latitude, double longitude);
-}
View
11 .../java/com/howardlewisship/tapx/internal/datefield/services/BestGuessTimeZoneAnalyzer.java
@@ -1,4 +1,4 @@
-// Copyright 2010, 2011 Howard M. Lewis Ship
+// Copyright 2010, 2011, 2012 Howard M. Lewis Ship
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,15 +14,14 @@
package com.howardlewisship.tapx.internal.datefield.services;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneAnalyzer;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneData;
+
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneAnalyzer;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneData;
-
-/**
- * Used in the absence of latitude/longitude information to perform a best guess based purely on
+/** Performs a best guess based purely on
* the remaining data from the client, possibly including a time zone id extracted from the Date
* string.
*/
View
43 .../java/com/howardlewisship/tapx/internal/datefield/services/ClientTimeZoneTrackerImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2011 Howard M. Lewis Ship
+// Copyright 2011, 2012 Howard M. Lewis Ship
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,15 +14,18 @@
package com.howardlewisship.tapx.internal.datefield.services;
-import java.util.TimeZone;
-
+import com.howardlewisship.tapx.datefield.DateFieldSymbols;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
+import org.apache.tapestry5.internal.services.CookieSink;
import org.apache.tapestry5.ioc.ScopeConstants;
import org.apache.tapestry5.ioc.annotations.Scope;
+import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.services.Cookies;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Session;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
+import javax.servlet.http.Cookie;
+import java.util.TimeZone;
@Scope(ScopeConstants.PERTHREAD)
public class ClientTimeZoneTrackerImpl implements ClientTimeZoneTracker
@@ -31,18 +34,28 @@
private static final String ATTRIBUTE_NAME = "tapx-datefield.timezone-id";
+ private static final int THIRTY_DAYS_IN_SECONDS = 30 * 24 * 60 * 60;
+
private final Cookies cookies;
+ private final CookieSink cookieSink;
+
private final Request request;
+ private final boolean secure;
+
private TimeZone timeZone;
private boolean identified;
- public ClientTimeZoneTrackerImpl(Cookies cookies, Request request)
+ public ClientTimeZoneTrackerImpl(Cookies cookies, CookieSink cookieSink, Request request,
+ @Symbol(DateFieldSymbols.SECURE_TIME_ZONE_COOKIE)
+ boolean secure)
{
this.cookies = cookies;
+ this.cookieSink = cookieSink;
this.request = request;
+ this.secure = secure;
setupTimeZone();
}
@@ -52,12 +65,18 @@ private void setupTimeZone()
timeZone = readTimeZoneFromSession();
if (timeZone == null)
+ {
timeZone = readTimeZoneFromCookie();
+ }
if (timeZone == null)
+ {
timeZone = TimeZone.getDefault();
+ }
else
+ {
identified = true;
+ }
}
private TimeZone readTimeZoneFromSession()
@@ -69,7 +88,9 @@ private TimeZone readTimeZoneFromSession()
String id = (String) session.getAttribute(ATTRIBUTE_NAME);
if (id != null)
+ {
return TimeZone.getTimeZone(id);
+ }
}
return null;
@@ -99,18 +120,28 @@ public void setClientTimeZone(TimeZone timeZone)
identified = true;
if (timeZone == this.timeZone)
+ {
return;
+ }
this.timeZone = timeZone;
- cookies.writeCookieValue(COOKIE_NAME, timeZone.getID());
+ Cookie cookie = new Cookie(COOKIE_NAME, timeZone.getID());
+ cookie.setPath(request.getContextPath() + "/");
+ cookie.setMaxAge(THIRTY_DAYS_IN_SECONDS);
+ cookie.setSecure(secure);
+
+ cookieSink.addCookie(cookie);
+
// Write to the Session, if it exists, in case the client doesn't support cookies.
Session session = request.getSession(false);
if (session != null)
+ {
session.setAttribute(ATTRIBUTE_NAME, timeZone.getID());
+ }
// Worst case: no session yet AND client doesn't support cookies. That means we'll likely
// keep tracking the time zone (on the client) and updating (here on the server) until
View
113 ...in/java/com/howardlewisship/tapx/internal/datefield/services/GeonameTimeZoneResolver.java
@@ -1,113 +0,0 @@
-// Copyright 2011 Howard M. Lewis Ship
-//
-// 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.howardlewisship.tapx.internal.datefield.services;
-
-import com.howardlewisship.tapx.datefield.DateFieldSymbols;
-import com.howardlewisship.tapx.datefield.services.LatLongToTimeZoneResolver;
-import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
-import org.apache.tapestry5.json.JSONObject;
-import org.slf4j.Logger;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.TimeZone;
-
-/**
- * Makes use of the web service at http://ws.geonames.org/timezoneJSON to convert a latitude and
- * longitude into TimeZone.
- */
-public class GeonameTimeZoneResolver implements LatLongToTimeZoneResolver {
- private final Logger logger;
-
- private final String geonamesURL;
-
- public GeonameTimeZoneResolver(Logger logger, @Symbol(DateFieldSymbols.GEONAMES_URL) String geonamesURL) {
- this.logger = logger;
- this.geonamesURL = geonamesURL;
- }
-
- public TimeZone resolveTimeZone(double latitude, double longitude) {
- try {
- String content = readContent(latitude, longitude);
-
- if (content == null) {
- return null;
- }
-
- JSONObject response = new JSONObject(content);
-
- String timeZoneId = response.getString("timezoneId");
-
- return TimeZone.getTimeZone(timeZoneId);
- } catch (Exception ex) {
- logger.error(String.format("Unable to use %s to resolve time zone for %f, %f: %s", geonamesURL, latitude,
- longitude, InternalUtils.toMessage(ex)), ex);
-
- return null;
- }
-
- }
-
- private String readContent(double latitude, double longitude) throws IOException {
- URL url = new URL(String.format("%s?lat=%f&lng=%f", geonamesURL, latitude, longitude));
-
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-
- connection.connect();
-
- if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
- logger.info(String.format("Attempt to resolve location to time zone got %d result from %s",
- connection.getResponseCode(), url));
-
- connection.disconnect();
-
- return null;
- }
-
- String content = readContentFromConnection(connection);
-
- connection.disconnect();
-
- return content;
-
- }
-
- private String readContentFromConnection(HttpURLConnection connection) throws IOException {
- StringBuilder result = new StringBuilder(200);
- BufferedReader reader = null;
-
- try {
- reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- String line = null;
-
- while (true) {
- line = reader.readLine();
-
- if (line == null)
- break;
-
- result.append(line).append('\n');
- }
- } finally {
- InternalUtils.close(reader);
- }
-
- return result.toString();
- }
-}
View
38 ...in/java/com/howardlewisship/tapx/internal/datefield/services/LatLongTimeZoneAnalyzer.java
@@ -1,38 +0,0 @@
-// Copyright 2011 Howard M. Lewis Ship
-//
-// 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.howardlewisship.tapx.internal.datefield.services;
-
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneAnalyzer;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneData;
-import com.howardlewisship.tapx.datefield.services.LatLongToTimeZoneResolver;
-import org.apache.tapestry5.ioc.annotations.Inject;
-
-import java.util.TimeZone;
-
-public class LatLongTimeZoneAnalyzer implements ClientTimeZoneAnalyzer
-{
- @Inject
- private LatLongToTimeZoneResolver resolver;
-
- public TimeZone extractTimeZone(ClientTimeZoneData data)
- {
- if (data.latitude != null && data.longitude != null) {
- return resolver.resolveTimeZone(data.latitude, data.longitude);
- }
-
- return null;
- }
-
-}
View
10 ...ain/java/com/howardlewisship/tapx/internal/datefield/services/SystemTimeZoneAnalyzer.java
@@ -1,4 +1,4 @@
-// Copyright 2011 Howard M. Lewis Ship
+// Copyright 2011, 2012 Howard M. Lewis Ship
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,11 +14,15 @@
package com.howardlewisship.tapx.internal.datefield.services;
-import java.util.TimeZone;
-
import com.howardlewisship.tapx.datefield.services.ClientTimeZoneAnalyzer;
import com.howardlewisship.tapx.datefield.services.ClientTimeZoneData;
+import java.util.TimeZone;
+
+/**
+ * The default implementation, used when information from the client can't be converted into a time zone;
+ * instead uses the server's default time zone.
+ */
public class SystemTimeZoneAnalyzer implements ClientTimeZoneAnalyzer
{
public TimeZone extractTimeZone(ClientTimeZoneData data)
View
46 .../src/main/resources/com/howardlewisship/tapx/datefield/components/time-zone-identifier.js
@@ -1,37 +1,19 @@
-Tapestry.Initializer.identifyClientTimeZone = function(url) {
+Tapestry.Initializer.identifyClientTimeZone = function (url) {
- function sendData(extra) {
- var date = new Date();
+ var date = new Date();
- var params = Object.extend( {
- offsetMinutes : date.getTimezoneOffset(),
- dateString : date.toString(),
- epochMillis : date.getTime()
- }, extra || {});
+ var params = {
+ offsetMinutes: date.getTimezoneOffset(),
+ dateString: date.toString(),
+ epochMillis: date.getTime()
+ };
- Tapestry.ajaxRequest(url, {
- parameters : params,
- onSuccess : function(reply) {
- var response = reply.responseJSON;
+ Tapestry.ajaxRequest(url, {
+ parameters: params,
+ onSuccess: function (reply) {
+ var response = reply.responseJSON;
- document.fire("tapx:time-zone-identified", response);
- }
- });
- }
-
- if (!navigator.geolocation) {
- sendData();
- return;
- }
-
- navigator.geolocation.getCurrentPosition(function(position) {
-
- sendData( {
- latitude : position.coords.latitude,
- longitude : position.coords.longitude
- });
-
- }, function(positionError) {
- sendData();
- });
+ document.fire("tapx:time-zone-identified", response);
+ }
+ });
};
View
37 tapx-datefield/src/main/resources/com/howardlewisship/tapx/datefield/tapx-datefield.js
@@ -1,13 +1,32 @@
-Tapestry.Initializer.tapxDateField = function(spec)
+Tapestry.Initializer.tapxDateField = function (spec)
{
+ /**
+ * Checks if the date is outside the range
+ * @param date a date to display
+ * @return {Boolean} true to disable the date (if outside the range), false if date is valid
+ */
+ function dateStatusHandler(date)
+ {
+ if (spec.min && date.getTime() < spec.min) {
+ return true;
+ }
+
+ if (spec.max && date.getTime() > spec.max) {
+ return true;
+ }
+
+ return false;
+ }
+
$T(spec.clientId).calendar = Calendar.setup({
- inputField: spec.clientId,
- button: spec.clientId + "-trigger",
- weekNumbers : false,
- showsTime : spec.time,
- ifFormat : spec.clientDateFormat,
- timeFormat : spec.time && spec.clientDateFormat.match("%p") != null ? "12" : "24",
- cache : true,
- singleClick : spec.singleClick
+ inputField:spec.clientId,
+ button:spec.clientId + "-trigger",
+ weekNumbers:false,
+ showsTime:spec.time,
+ ifFormat:spec.clientDateFormat,
+ timeFormat:spec.time && spec.clientDateFormat.match("%p") != null ? "12" : "24",
+ cache:true,
+ singleClick:spec.singleClick,
+ dateStatusFunc:dateStatusHandler
});
}
View
5 ...datefield/src/main/resources/com/howardlewisship/tapx/datefield/tapx-datefield.properties
@@ -1,4 +1,4 @@
-# Copyright 2009, 2010 Howard M. Lewis Ship
+# Copyright 2009, 2010 2012 Howard M. Lewis Ship
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,3 +13,6 @@
# limitations under the License.
tapx-date-value-not-parseable=Date value '%s' is not parseable.
+tapx-date-value-too-early=Date provided is earlier than minimum value.
+tapx-date-value-too-late=Date provide is later than maximum value.
+
View
37 tapx-datefield/src/test/java/demo/pages/DateFieldDemo.java
@@ -1,10 +1,7 @@
package demo.pages;
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Locale;
-
+import com.howardlewisship.tapx.datefield.TimeZoneVisibility;
+import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
@@ -13,8 +10,10 @@
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.services.PersistentLocale;
-import com.howardlewisship.tapx.datefield.TimeZoneVisibility;
-import com.howardlewisship.tapx.datefield.services.ClientTimeZoneTracker;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
public class DateFieldDemo
{
@@ -58,9 +57,9 @@ public DateFormat getDateFormat()
{
return time
- ? DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, locale)
+ ? DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, locale)
- : DateFormat.getDateInstance(DateFormat.LONG, locale);
+ : DateFormat.getDateInstance(DateFormat.LONG, locale);
}
public String getLocaleName()
@@ -75,7 +74,7 @@ public void setLocaleName(String localeName)
}
@SuppressWarnings(
- { "deprecation" })
+ {"deprecation"})
void onActionFromSetup()
{
setLocaleName("en");
@@ -83,4 +82,22 @@ void onActionFromSetup()
time = false;
}
+ public Date getMinDate()
+ {
+ return nowOffset(Calendar.DAY_OF_YEAR, -30);
+ }
+
+ public Date getMaxDate()
+ {
+ return nowOffset(Calendar.DAY_OF_YEAR, 30);
+ }
+
+ private Date nowOffset(int units, int offset)
+ {
+ Calendar calendar = Calendar.getInstance();
+
+ calendar.add(units, offset);
+
+ return calendar.getTime();
+ }
}
View
4 tapx-datefield/src/test/resources/demo/pages/DateFieldDemo.tml
@@ -1,10 +1,10 @@
<t:layout xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
- xmlns:tx="tapestry-library:tapx" xmlns:p="tapestry:parameter">
+ xmlns:tx="tapestry-library:tapx">
<tx:timezoneidentifier/>
<t:form>
- <tx:datefield t:id="date" time="time"
+ <tx:datefield t:id="date" time="time" min="minDate" max="maxDate"
timezone="prop:timezonevisibility"/>
<br/>
Please sign in to comment.
Something went wrong with that request. Please try again.