001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.widgets; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.i18n.CmsMessages; 033import org.opencms.main.CmsLog; 034import org.opencms.util.CmsMacroResolver; 035import org.opencms.util.CmsStringUtil; 036import org.opencms.workplace.CmsWorkplace; 037import org.opencms.xml.content.I_CmsXmlContentHandler.DisplayType; 038import org.opencms.xml.types.A_CmsXmlContentValue; 039 040import java.text.DateFormat; 041import java.text.ParseException; 042import java.text.SimpleDateFormat; 043import java.util.GregorianCalendar; 044import java.util.List; 045import java.util.Locale; 046import java.util.Map; 047import java.util.TimeZone; 048 049import org.apache.commons.logging.Log; 050 051/** 052 * Provides a DHTML calendar widget, for use on a widget dialog.<p> 053 * 054 * @since 6.0.0 055 */ 056public class CmsCalendarWidget extends A_CmsWidget implements I_CmsADEWidget { 057 058 /** The log object for this class. */ 059 private static final Log LOG = CmsLog.getLog(CmsCalendarWidget.class); 060 061 /** 062 * Creates a new calendar widget.<p> 063 */ 064 public CmsCalendarWidget() { 065 066 // empty constructor is required for class registration 067 this(""); 068 } 069 070 /** 071 * Creates a new calendar widget with the given configuration.<p> 072 * 073 * @param configuration the configuration to use 074 */ 075 public CmsCalendarWidget(String configuration) { 076 077 super(configuration); 078 } 079 080 /** 081 * Creates the HTML JavaScript and stylesheet includes required by the calendar for the head of the page.<p> 082 * 083 * The default <code>"opencms"</code> style is used.<p> 084 * 085 * @param locale the locale to use for the calendar 086 * 087 * @return the necessary HTML code for the js and stylesheet includes 088 * 089 * @see #calendarIncludes(Locale, String) 090 */ 091 public static String calendarIncludes(Locale locale) { 092 093 return calendarIncludes(locale, "opencms"); 094 } 095 096 /** 097 * Creates the HTML JavaScript and stylesheet includes required by the calendar for the head of the page.<p> 098 * 099 * @param locale the locale to use for the calendar 100 * @param style the name of the used calendar style, e.g. "system", "blue" 101 * 102 * @return the necessary HTML code for the js and stylesheet includes 103 */ 104 public static String calendarIncludes(Locale locale, String style) { 105 106 StringBuffer result = new StringBuffer(512); 107 String calendarPath = CmsWorkplace.getSkinUri() + "components/js_calendar/"; 108 if (CmsStringUtil.isEmpty(style)) { 109 style = "system"; 110 } 111 result.append("<link rel=\"stylesheet\" type=\"text/css\" href=\""); 112 result.append(calendarPath); 113 result.append("calendar-"); 114 result.append(style); 115 result.append(".css\">\n"); 116 result.append("<script type=\"text/javascript\" src=\""); 117 result.append(calendarPath); 118 result.append("calendar.js\"></script>\n"); 119 result.append("<script type=\"text/javascript\" src=\""); 120 result.append(calendarPath); 121 result.append("lang/calendar-"); 122 result.append(getLanguageSuffix(locale.getLanguage())); 123 result.append(".js\"></script>\n"); 124 result.append("<script type=\"text/javascript\" src=\""); 125 result.append(calendarPath); 126 result.append("calendar-setup.js\"></script>\n"); 127 return result.toString(); 128 } 129 130 /** 131 * Generates the HTML to initialize the JavaScript calendar element on the end of a page.<p> 132 * 133 * This method must be called at the end of a HTML page, e.g. before the closing <body> tag.<p> 134 * 135 * @param messages the messages to use (for date and time formats) 136 * @param inputFieldId the ID of the input field where the date is pasted to 137 * @param triggerButtonId the ID of the button which triggers the calendar 138 * @param align initial position of the calendar popup element 139 * @param singleClick if true, a single click selects a date and closes the calendar, otherwise calendar is closed by doubleclick 140 * @param weekNumbers show the week numbers in the calendar or not 141 * @param mondayFirst show monday as first day of week 142 * @param dateStatusFunc name of the function which determines if/how a date should be disabled 143 * @param showTime true if the time selector should be shown, otherwise false 144 * @return the HTML code to initialize a calendar poup element 145 */ 146 public static String calendarInit( 147 CmsMessages messages, 148 String inputFieldId, 149 String triggerButtonId, 150 String align, 151 boolean singleClick, 152 boolean weekNumbers, 153 boolean mondayFirst, 154 String dateStatusFunc, 155 boolean showTime) { 156 157 StringBuffer result = new StringBuffer(512); 158 if (CmsStringUtil.isEmpty(align)) { 159 align = "Bc"; 160 } 161 result.append("<script type=\"text/javascript\">\n"); 162 result.append("<!--\n"); 163 result.append("\tCalendar.setup({\n"); 164 result.append("\t\tinputField : \""); 165 result.append(inputFieldId); 166 result.append("\",\n"); 167 result.append("\t\tifFormat : \""); 168 result.append(messages.key(org.opencms.workplace.Messages.GUI_CALENDAR_DATE_FORMAT_0)); 169 if (showTime) { 170 result.append(" "); 171 result.append(messages.key(org.opencms.workplace.Messages.GUI_CALENDAR_TIME_FORMAT_0)); 172 } 173 result.append("\",\n"); 174 result.append("\t\tbutton : \""); 175 result.append(triggerButtonId); 176 result.append("\",\n"); 177 result.append("\t\talign : \""); 178 result.append(align); 179 result.append("\",\n"); 180 result.append("\t\tsingleClick : "); 181 result.append(singleClick); 182 result.append(",\n"); 183 result.append("\t\tweekNumbers : "); 184 result.append(weekNumbers); 185 result.append(",\n"); 186 result.append("\t\tmondayFirst : "); 187 result.append(mondayFirst); 188 result.append(",\n"); 189 result.append("\t\tshowsTime : " + showTime); 190 if (showTime 191 && (messages.key(org.opencms.workplace.Messages.GUI_CALENDAR_TIMEFORMAT_0).toLowerCase().indexOf( 192 "p") != -1)) { 193 result.append(",\n\t\ttimeFormat : \"12\""); 194 } 195 if (CmsStringUtil.isNotEmpty(dateStatusFunc)) { 196 result.append(",\n\t\tdateStatusFunc : "); 197 result.append(dateStatusFunc); 198 } 199 result.append("\n\t});\n"); 200 201 result.append("//-->\n"); 202 result.append("</script>\n"); 203 return result.toString(); 204 } 205 206 /** 207 * Creates the time in milliseconds from the given parameter.<p> 208 * 209 * @param messages the messages that contain the time format definitions 210 * @param dateString the String representation of the date 211 * @param useTime true if the time should be parsed, too, otherwise false 212 * 213 * @return the time in milliseconds 214 * 215 * @throws ParseException if something goes wrong 216 */ 217 public static long getCalendarDate(CmsMessages messages, String dateString, boolean useTime) throws ParseException { 218 219 long dateLong = 0; 220 221 // substitute some chars because calendar syntax != DateFormat syntax 222 String dateFormat = messages.key(org.opencms.workplace.Messages.GUI_CALENDAR_DATE_FORMAT_0); 223 if (useTime) { 224 dateFormat += " " + messages.key(org.opencms.workplace.Messages.GUI_CALENDAR_TIME_FORMAT_0); 225 } 226 dateFormat = CmsCalendarWidget.getCalendarJavaDateFormat(dateFormat); 227 228 SimpleDateFormat df = new SimpleDateFormat(dateFormat); 229 dateLong = df.parse(dateString).getTime(); 230 return dateLong; 231 } 232 233 /** 234 * Parses the JavaScript calendar date format to the java patterns of SimpleDateFormat.<p> 235 * 236 * @param dateFormat the dateformat String of the JS calendar 237 * @return the parsed SimpleDateFormat pattern String 238 */ 239 public static String getCalendarJavaDateFormat(String dateFormat) { 240 241 dateFormat = CmsStringUtil.substitute(dateFormat, "%", ""); // remove all "%" 242 dateFormat = CmsStringUtil.substitute(dateFormat, "m", "${month}"); 243 dateFormat = CmsStringUtil.substitute(dateFormat, "H", "${hour}"); 244 dateFormat = CmsStringUtil.substitute(dateFormat, "Y", "${4anno}"); 245 dateFormat = dateFormat.toLowerCase(); 246 dateFormat = CmsStringUtil.substitute(dateFormat, "${month}", "M"); 247 dateFormat = CmsStringUtil.substitute(dateFormat, "${hour}", "H"); 248 dateFormat = CmsStringUtil.substitute(dateFormat, "y", "yy"); 249 dateFormat = CmsStringUtil.substitute(dateFormat, "${4anno}", "yyyy"); 250 dateFormat = CmsStringUtil.substitute(dateFormat, "m", "mm"); // minutes with two digits 251 dateFormat = dateFormat.replace('e', 'd'); // day of month 252 dateFormat = dateFormat.replace('i', 'h'); // 12 hour format 253 dateFormat = dateFormat.replace('p', 'a'); // pm/am String 254 return dateFormat; 255 } 256 257 /** 258 * Returns the given timestamp as String formatted in a localized pattern.<p> 259 * 260 * @param locale the locale for the time format 261 * @param messages the messages that contain the time format definitions 262 * @param timestamp the time to format 263 * 264 * @return the given timestamp as String formatted in a localized pattern 265 */ 266 public static String getCalendarLocalizedTime(Locale locale, CmsMessages messages, long timestamp) { 267 268 // get the current date & time 269 TimeZone zone = TimeZone.getDefault(); 270 GregorianCalendar cal = new GregorianCalendar(zone, locale); 271 cal.setTimeInMillis(timestamp); 272 // format it nicely according to the localized pattern 273 DateFormat df = new SimpleDateFormat( 274 CmsCalendarWidget.getCalendarJavaDateFormat( 275 messages.key(org.opencms.workplace.Messages.GUI_CALENDAR_DATE_FORMAT_0) 276 + " " 277 + messages.key(org.opencms.workplace.Messages.GUI_CALENDAR_TIME_FORMAT_0))); 278 return df.format(cal.getTime()); 279 } 280 281 /** 282 * Returns the language suffix for the calendar-*.js localizations.<p> 283 * 284 * @param language the language from the locale 285 * 286 * @return the suffix to use for the calendar-*js localication file 287 */ 288 private static String getLanguageSuffix(String language) { 289 290 if (language.equals(Locale.JAPANESE.getLanguage())) { 291 return "jp"; 292 } else { 293 return language; 294 } 295 } 296 297 /** 298 * @see org.opencms.widgets.I_CmsADEWidget#getConfiguration(org.opencms.file.CmsObject, org.opencms.xml.types.A_CmsXmlContentValue, org.opencms.i18n.CmsMessages, org.opencms.file.CmsResource, java.util.Locale) 299 */ 300 public String getConfiguration( 301 CmsObject cms, 302 A_CmsXmlContentValue schemaType, 303 CmsMessages messages, 304 CmsResource resource, 305 Locale contentLocale) { 306 307 return getConfiguration(); 308 } 309 310 /** 311 * @see org.opencms.widgets.I_CmsADEWidget#getCssResourceLinks(org.opencms.file.CmsObject) 312 */ 313 public List<String> getCssResourceLinks(CmsObject cms) { 314 315 return null; 316 } 317 318 /** 319 * @see org.opencms.widgets.I_CmsADEWidget#getDefaultDisplayType() 320 */ 321 public DisplayType getDefaultDisplayType() { 322 323 return DisplayType.singleline; 324 } 325 326 /** 327 * @see org.opencms.widgets.I_CmsWidget#getDialogIncludes(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog) 328 */ 329 @Override 330 public String getDialogIncludes(CmsObject cms, I_CmsWidgetDialog widgetDialog) { 331 332 return calendarIncludes(widgetDialog.getLocale()); 333 } 334 335 /** 336 * @see org.opencms.widgets.I_CmsWidget#getDialogWidget(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter) 337 */ 338 public String getDialogWidget(CmsObject cms, I_CmsWidgetDialog widgetDialog, I_CmsWidgetParameter param) { 339 340 StringBuffer result = new StringBuffer(16); 341 result.append("<td class=\"xmlTd\">"); 342 result.append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td>"); 343 result.append("<input class=\"xmlInputSmall"); 344 if (param.hasError()) { 345 result.append(" xmlInputError"); 346 } 347 result.append("\" value=\""); 348 String dateTimeValue = getWidgetStringValue(cms, widgetDialog, param); 349 result.append(dateTimeValue); 350 String id = param.getId(); 351 result.append("\" name=\""); 352 result.append(id); 353 result.append("\" id=\""); 354 result.append(id); 355 result.append("\"></td>"); 356 result.append(widgetDialog.dialogHorizontalSpacer(10)); 357 result.append("<td>"); 358 result.append("<table class=\"editorbuttonbackground\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" id=\""); 359 result.append(id); 360 result.append(".calendar\"><tr>"); 361 result.append( 362 widgetDialog.button( 363 "#", 364 null, 365 "calendar", 366 org.opencms.workplace.Messages.GUI_CALENDAR_CHOOSE_DATE_0, 367 widgetDialog.getButtonStyle())); 368 result.append("</tr></table>"); 369 result.append("</td></tr></table>"); 370 371 result.append( 372 calendarInit(widgetDialog.getMessages(), id, id + ".calendar", "cR", false, false, true, null, true)); 373 374 result.append("</td>"); 375 376 return result.toString(); 377 } 378 379 /** 380 * @see org.opencms.widgets.I_CmsADEWidget#getInitCall() 381 */ 382 public String getInitCall() { 383 384 return null; 385 } 386 387 /** 388 * @see org.opencms.widgets.I_CmsADEWidget#getJavaScriptResourceLinks(org.opencms.file.CmsObject) 389 */ 390 public List<String> getJavaScriptResourceLinks(CmsObject cms) { 391 392 return null; 393 } 394 395 /** 396 * @see org.opencms.widgets.I_CmsADEWidget#getWidgetName() 397 */ 398 public String getWidgetName() { 399 400 return CmsCalendarWidget.class.getName(); 401 } 402 403 /** 404 * @see org.opencms.widgets.A_CmsWidget#getWidgetStringValue(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter) 405 */ 406 @Override 407 public String getWidgetStringValue(CmsObject cms, I_CmsWidgetDialog widgetDialog, I_CmsWidgetParameter param) { 408 409 String result = param.getStringValue(cms); 410 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(result) && !"0".equals(result)) { 411 try { 412 result = getCalendarLocalizedTime( 413 widgetDialog.getLocale(), 414 widgetDialog.getMessages(), 415 Long.parseLong(result)); 416 } catch (NumberFormatException e) { 417 if (!CmsMacroResolver.isMacro(result, CmsMacroResolver.KEY_CURRENT_TIME)) { 418 // neither long nor macro, show empty value 419 result = ""; 420 } 421 } 422 } else { 423 result = ""; 424 } 425 return result; 426 } 427 428 /** 429 * @see org.opencms.widgets.I_CmsADEWidget#isInternal() 430 */ 431 public boolean isInternal() { 432 433 return true; 434 } 435 436 /** 437 * @see org.opencms.widgets.I_CmsWidget#newInstance() 438 */ 439 public I_CmsWidget newInstance() { 440 441 return new CmsCalendarWidget(getConfiguration()); 442 } 443 444 /** 445 * @see org.opencms.widgets.I_CmsWidget#setEditorValue(org.opencms.file.CmsObject, java.util.Map, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter) 446 */ 447 @Override 448 public void setEditorValue( 449 CmsObject cms, 450 Map<String, String[]> formParameters, 451 I_CmsWidgetDialog widgetDialog, 452 I_CmsWidgetParameter param) { 453 454 String[] values = formParameters.get(param.getId()); 455 if ((values != null) && (values.length > 0)) { 456 String dateTimeValue = values[0].trim(); 457 if (CmsMacroResolver.isMacro(dateTimeValue, CmsMacroResolver.KEY_CURRENT_TIME)) { 458 // a macro is used, redisplay it 459 param.setStringValue(cms, dateTimeValue); 460 } else { 461 // a date value should be used 462 long dateTime; 463 try { 464 dateTime = Long.valueOf(param.getStringValue(cms)).longValue(); 465 } catch (NumberFormatException e) { 466 dateTime = 0; 467 } 468 if (CmsStringUtil.isNotEmpty(dateTimeValue)) { 469 try { 470 dateTime = getCalendarDate(widgetDialog.getMessages(), dateTimeValue, true); 471 } catch (ParseException e) { 472 // TODO: Better exception handling 473 if (LOG.isWarnEnabled()) { 474 LOG.warn(Messages.get().getBundle().key(Messages.ERR_PARSE_DATETIME_1, dateTimeValue), e); 475 } 476 } 477 } else { 478 dateTime = 0; 479 } 480 param.setStringValue(cms, String.valueOf(dateTime)); 481 } 482 } 483 } 484}