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