/* * File : $Source: /alkacon/cvs/alkacon/com.alkacon.opencms.calendar/src/com/alkacon/opencms/calendar/CmsCalendarMonthBean.java,v $ * Date : $Date: 2009/02/05 09:49:31 $ * Version: $Revision: 1.2 $ * * This file is part of the Alkacon OpenCms Add-On Module Package * * Copyright (c) 2008 Alkacon Software GmbH (http://www.alkacon.com) * * The Alkacon OpenCms Add-On Module Package is free software: * you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Alkacon OpenCms Add-On Module Package is distributed * in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the Alkacon OpenCms Add-On Module Package. * If not, see http://www.gnu.org/licenses/. * * For further information about Alkacon Software GmbH, please see the * company website: http://www.alkacon.com. * * For further information about OpenCms, please see the * project website: http://www.opencms.org. */ package com.alkacon.opencms.calendar; import org.opencms.jsp.CmsJspActionElement; import org.opencms.main.CmsLog; import org.opencms.util.CmsStringUtil; import java.text.DateFormat; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import org.apache.commons.logging.Log; /** * Provides help methods to display monthly views of calendar entries.<p> * * This includes methods to build the complete HTML output for a single month and CSS style settings that are used by * the build methods.<p> * * @author Andreas Zahner * * @version $Revision: 1.2 $ * * @since 6.0.1 */ public class CmsCalendarMonthBean extends CmsCalendarDisplay { /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsCalendarMonthBean.class); /** The URI to the view which is displayed when clicking on a day. */ private String m_viewUri; /** * Empty constructor.<p> * * Be sure to call the {@link #init(CmsJspActionElement)} method with an JSP action element.<p> */ public CmsCalendarMonthBean() { super(); } /** * Constructor with an initialized calendar object and JSP action element.<p> * * @param jsp the JSP action element to use */ public CmsCalendarMonthBean(CmsJspActionElement jsp) { super(jsp); } /** * Builds the HTML output to create a basic calendar overview of the current month or the month based on request parameters * if present including a month navigation.<p> * * The calendar Locale to use is determined from the current request context.<p> * * @return the HTML output to create a basic calendar overview of the current month */ public String buildCalendarMonth() { return buildCalendarMonth(getJsp().getRequestContext().getLocale()); } /** * Builds the HTML output to create a basic calendar month overview with month navigation.<p> * * This method serves as a simple example to create a basic html calendar monthly view.<p> * * @param year the year of the month to display * @param month the month to display * @param calendarLocale the Locale for the calendar to determine the start day of the weeks * @return the HTML output to create a basic calendar month overview */ public String buildCalendarMonth(int year, int month, Locale calendarLocale) { return buildCalendarMonth(year, month, calendarLocale, true); } /** * Builds the HTML output to create a basic calendar month overview.<p> * * This method serves as a simple example to create a basic html calendar monthly view.<p> * * @param year the year of the month to display * @param month the month to display * @param calendarLocale the Locale for the calendar to determine the start day of the weeks * @param showNavigation if true, navigation links to switch the month are created, otherwise not * @return the HTML output to create a basic calendar month overview */ public String buildCalendarMonth(int year, int month, Locale calendarLocale, boolean showNavigation) { StringBuffer result = new StringBuffer(1024); Map dates = getMonthDaysMatrix(year, month, calendarLocale); Map monthEntries = getEntriesForMonth(year, month); // calculate the start day of the week Calendar calendar = new GregorianCalendar(calendarLocale); int weekStart = calendar.getFirstDayOfWeek(); // store current calendar date Calendar currentCalendar = (Calendar)calendar.clone(); // init the date format symbols DateFormatSymbols calendarSymbols = new DateFormatSymbols(calendarLocale); // open the table result.append("<table class=\""); result.append(getStyle().getStyleTable()); result.append("\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n"); // create the calendar navigation row result.append(buildMonthNavigation(year, month, currentCalendar, calendarLocale, showNavigation)); // create the week day row result.append("<tr>\n"); int currWeekDay = weekStart; for (int i = 1; i <= 7; i++) { result.append("\t<td class=\""); result.append(getStyle().getStyleWeekdays()); result.append("\">"); result.append(calendarSymbols.getShortWeekdays()[currWeekDay]); result.append("</td>\n"); // check if we are at end of week if (currWeekDay == Calendar.SATURDAY) { currWeekDay = 0; } currWeekDay++; } result.append("</tr>\n"); // now create the entry rows result.append("<tr>\n"); // iterate the index entries of the matrix Iterator i = dates.keySet().iterator(); while (i.hasNext()) { Integer index = (Integer)i.next(); result.append("\t<td class=\""); Calendar currDay = (Calendar)dates.get(index); if (currDay != null) { // current index represents a day, create day output String styleDayCell = getStyle().getStyleDay(); if (isCurrentDay(currentCalendar, currDay)) { // for the current day, use another cell style styleDayCell = getStyle().getStyleDayCurrent(); } // get entries for the day List dayEntries = (List)monthEntries.get(currDay.getTime()); if (dayEntries.size() > 0) { // current day has calendar entries int weekdayStatus = 0; int holidayEntries = 0; int commonEntries = dayEntries.size(); StringBuffer dayText = new StringBuffer(128); // check all entries for special weekday status entries for (int k = 0; k < commonEntries; k++) { CmsCalendarEntry entry = (CmsCalendarEntry)dayEntries.get(k); int entryWeekdayStatus = entry.getEntryData().getWeekdayStatus(); if (entryWeekdayStatus > 0) { // entry is a special weekday holidayEntries++; // append special day info to title info dayText.append(entry.getEntryData().getTitle()); dayText.append(" - "); if (entryWeekdayStatus > weekdayStatus) { // increase the status of the weekday weekdayStatus = entryWeekdayStatus; } } } // calculate the count of common calendar entries commonEntries = commonEntries - holidayEntries; // determine the CSS class to use String dayStyle = getWeekdayStyle(currDay.get(Calendar.DAY_OF_WEEK), weekdayStatus); result.append(styleDayCell); result.append("\" title=\""); result.append(dayText); // check the number of common entries and generate output of entry count if (commonEntries <= 0) { // no entry found result.append(getMessages().key("calendar.entries.count.none")); } else if (commonEntries == 1) { // one entry found result.append(getMessages().key("calendar.entries.count.one")); } else { // more than one entry found result.append(getMessages().key( "calendar.entries.count.more", new String[] {String.valueOf(commonEntries)})); } result.append("\">"); if (commonEntries > 0) { // common entries present, create link to the overview page result.append("<a href=\""); result.append(createLink(currDay, m_viewUri, true, -1)); result.append("\" class=\""); result.append(getStyle().getStyleDayEntryLink()); result.append("\">"); } result.append("<span class=\""); result.append(dayStyle); result.append("\">"); result.append(currDay.get(Calendar.DAY_OF_MONTH)); result.append("</span>"); if (commonEntries > 0) { // common entries present, close link result.append("</a>"); } } else { // current day has no entries result.append(styleDayCell); result.append("\" title=\""); result.append(getMessages().key("calendar.entries.count.none")); result.append("\">"); result.append("<span class=\""); result.append(getWeekdayStyle( currDay.get(Calendar.DAY_OF_WEEK), I_CmsCalendarEntryData.WEEKDAYSTATUS_WORKDAY)); result.append("\">"); result.append(currDay.get(Calendar.DAY_OF_MONTH)); result.append("</span>"); } } else { // this is an empty cell result.append(getStyle().getStyleDayEmpty()); result.append("\">"); } result.append("</td>\n"); if ((index.intValue() % 7) == 0) { // append closing row tag result.append("</tr>\n"); if (i.hasNext()) { // open next row if more elements are present result.append("<tr>\n"); } } } // close the table result.append("</table>"); return result.toString(); } /** * Builds the HTML output to create a basic calendar overview of the current month or the month based on request parameters * if present, including a month navigation row.<p> * * @param calendarLocale the Locale for the calendar to determine the start day of the weeks * @return the HTML output to create a basic calendar overview of the current month */ public String buildCalendarMonth(Locale calendarLocale) { Calendar calendar = new GregorianCalendar(calendarLocale); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); // get date parameters from request String yearParam = getJsp().getRequest().getParameter(PARAM_YEAR); String monthParam = getJsp().getRequest().getParameter(PARAM_MONTH); if (CmsStringUtil.isNotEmpty(yearParam) && CmsStringUtil.isNotEmpty(monthParam)) { // build calendar of month specified by given request parameters try { year = Integer.parseInt(yearParam); month = Integer.parseInt(monthParam); } catch (NumberFormatException e) { // wrong parameters given, log error if (LOG.isErrorEnabled()) { LOG.error(Messages.get().getBundle().key( Messages.LOG_CALENDAR_REQUESTPARAMS_1, getJsp().getRequestContext().getUri())); } } } return buildCalendarMonth(year, month, calendarLocale, true); } /** * Returns the days of a month to display in a matrix, depending on the start day of the week.<p> * * The month matrix starts with index "1" and uses 7 columns per row to display one week in a row. * The value returns null if no date should be shown at the current index position.<p> * * @param year the year of the month to display * @param month the month to display * @param calendarLocale the Locale for the calendar to determine the start day of the week * @return the days of a month to display in a matrix, depending on the start day of the week */ public Map getMonthDaysMatrix(int year, int month, Locale calendarLocale) { Map monthDays = new TreeMap(); Calendar startDay = new GregorianCalendar(year, month, 1); Calendar runDay = startDay; int index = 1; // calculate the start day of the week Calendar calendar = new GregorianCalendar(calendarLocale); int weekStart = calendar.getFirstDayOfWeek(); // create empty indexes before the first day of the month while (runDay.get(Calendar.DAY_OF_WEEK) != weekStart) { monthDays.put(new Integer(index), null); index++; if (weekStart == Calendar.SATURDAY) { weekStart = Calendar.SUNDAY; } else { weekStart++; } } // create the indexes for the month dates while (true) { monthDays.put(new Integer(index), runDay.clone()); // increase day to next day runDay.roll(Calendar.DAY_OF_MONTH, true); index++; if (runDay.get(Calendar.DAY_OF_MONTH) == 1) { // runDay has switched to the next month, stop loop break; } } // create empty indexes after the last day of the month int rest = (index - 1) % 7; if (rest > 0) { rest = 7 - rest; } for (int i = 0; i < rest; i++) { monthDays.put(new Integer(index), null); index++; } return monthDays; } /** * Sets the view URI and the default view period.<p> * * @see com.alkacon.opencms.calendar.CmsCalendarDisplay#init(org.opencms.jsp.CmsJspActionElement) */ public void init(CmsJspActionElement jsp) { // call super initialisation super.init(jsp); setViewPeriod(CmsCalendarDisplay.PERIOD_DAY); setViewUri(jsp.property(CmsCalendarDisplay.PROPERTY_CALENDAR_URI, "search", "")); } /** * Sets the URI to the view which is displayed when clicking on a day.<p> * * @param viewUri the URI to the view which is displayed when clicking on a day */ public void setViewUri(String viewUri) { m_viewUri = viewUri; } /** * Builds the HTML for the calendar month navigation row.<p> * * @param year the year of the month to display * @param month the month to display * @param currentCalendar the current calendar date * @param calendarLocale the Locale to use to display the calendar information * @param showNavigation if true, navigation links to switch the month are created, otherwise not * @return the HTML for the calendar month navigation row */ private String buildMonthNavigation( int year, int month, Calendar currentCalendar, Locale calendarLocale, boolean showNavigation) { StringBuffer result = new StringBuffer(256); StringBuffer navLink = new StringBuffer(64); Calendar calendar; int monthSpan = 7; result.append("<tr>\n"); if (showNavigation) { // create the navigation to the previous month monthSpan -= 2; result.append("\t<td class=\""); result.append(getStyle().getStyleNavigation()); result.append("\" title=\""); result.append(getMessages().key("calendar.navigation.month.previous")); result.append("\"><a class=\""); result.append(getStyle().getStyleNavigation()); result.append("\" href=\""); if (isUseAjaxLinks()) { result.append("javascript:void(0);\" onclick=\"calendarSidePagination('prev');"); } else { calendar = getPreviousPeriod(new GregorianCalendar(year, month, 1), CmsCalendarDisplay.PERIOD_MONTH); navLink.append(getJsp().getRequestContext().getUri()); navLink.append("?").append(PARAM_YEAR).append("=").append(calendar.get(Calendar.YEAR)); navLink.append("&").append(PARAM_MONTH).append("=").append(calendar.get(Calendar.MONTH)); result.append(getJsp().link(navLink.toString())); } result.append("\">«</a></td>\n"); } // create the navigation to the current month result.append("\t<td class=\""); result.append(getStyle().getStyleNavigation()); result.append("\" title=\""); result.append(getMessages().key("calendar.navigation.month.current")); result.append("\" colspan=\""); result.append(monthSpan); result.append("\">"); DateFormat df = new SimpleDateFormat(getMessages().key("calendar.format.headline.month"), calendarLocale); calendar = new GregorianCalendar(year, month, 1); if (showNavigation) { // create the link to the current month result.append("<a class=\""); result.append(getStyle().getStyleNavigation()); result.append("\" href=\""); if (isUseAjaxLinks()) { result.append("javascript:void(0);\" onclick=\"calendarSidePagination('current');"); } else { navLink = new StringBuffer(64); navLink.append(getJsp().getRequestContext().getUri()); navLink.append("?").append(PARAM_YEAR).append("=").append(currentCalendar.get(Calendar.YEAR)); navLink.append("&").append(PARAM_MONTH).append("=").append(currentCalendar.get(Calendar.MONTH)); result.append(getJsp().link(navLink.toString())); } result.append("\">"); result.append(df.format(calendar.getTime())); result.append("</a>"); } else { // create only the viewed months String result.append(df.format(calendar.getTime())); } result.append("</td>\n"); if (showNavigation) { // create the navigation to the previous month result.append("\t<td class=\""); result.append(getStyle().getStyleNavigation()); result.append("\" title=\""); result.append(getMessages().key("calendar.navigation.month.next")); result.append("\"><a class=\""); result.append(getStyle().getStyleNavigation()); result.append("\" href=\""); calendar = getNextPeriod(new GregorianCalendar(year, month, 1), CmsCalendarDisplay.PERIOD_MONTH); if (isUseAjaxLinks()) { result.append("javascript:void(0);\" onclick=\"calendarSidePagination('next');"); } else { navLink = new StringBuffer(64); navLink.append(getJsp().getRequestContext().getUri()); navLink.append("?").append(PARAM_YEAR).append("=").append(calendar.get(Calendar.YEAR)); navLink.append("&").append(PARAM_MONTH).append("=").append(calendar.get(Calendar.MONTH)); result.append(getJsp().link(navLink.toString())); } result.append("\">»</a></td>\n"); } result.append("</tr>\n"); return result.toString(); } /** * Returns the CSS class name to use to format the weekday depending on the status and localized settings.<p> * * @param currentWeekday the current weekday * @param currentWeekdayStatus the weekday status of the current weekday * @return the CSS class name to use to format the weekday depending on the status and localized settings */ private String getWeekdayStyle(int currentWeekday, int currentWeekdayStatus) { if (currentWeekdayStatus < I_CmsCalendarEntryData.WEEKDAYSTATUS_MAYBEHOLIDAY) { // check the localized week days to mark specially as maybe holiday days if (currentWeekday == getWeekdayMaybeHoliday()) { currentWeekdayStatus = I_CmsCalendarEntryData.WEEKDAYSTATUS_MAYBEHOLIDAY; } } if (currentWeekdayStatus < I_CmsCalendarEntryData.WEEKDAYSTATUS_HOLIDAY) { // check the localized week days to mark specially as holiday days if (currentWeekday == getWeekdayHoliday()) { currentWeekdayStatus = I_CmsCalendarEntryData.WEEKDAYSTATUS_HOLIDAY; } } String dayStyle; switch (currentWeekdayStatus) { case I_CmsCalendarEntryData.WEEKDAYSTATUS_HOLIDAY: dayStyle = getStyle().getStyleDayHoliday(); break; case I_CmsCalendarEntryData.WEEKDAYSTATUS_MAYBEHOLIDAY: dayStyle = getStyle().getStyleDayMaybeHoliday(); break; case I_CmsCalendarEntryData.WEEKDAYSTATUS_WORKDAY: default: dayStyle = getStyle().getStyleDay(); } return dayStyle; } }