001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.tags; 020 021import org.apache.logging.log4j.LogManager; 022import org.apache.logging.log4j.Logger; 023import org.apache.wiki.api.core.ContextEnum; 024import org.apache.wiki.api.core.Engine; 025import org.apache.wiki.api.core.Page; 026import org.apache.wiki.pages.PageManager; 027import org.apache.wiki.util.HttpUtil; 028import org.apache.wiki.util.TextUtil; 029 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.jsp.JspWriter; 032import java.io.IOException; 033import java.text.ParseException; 034import java.text.SimpleDateFormat; 035import java.util.Calendar; 036import java.util.Date; 037 038 039/** 040 * Provides a nice calendar. Responds to the following HTTP parameters: 041 * <ul> 042 * <li>calendar.date - If this parameter exists, then the calendar 043 * date is taken from the month and year. The date must be in ddMMyy format. 044 * <li>weblog.startDate - If calendar.date parameter does not exist, 045 * we then check this date. 046 * </ul> 047 * 048 * If neither calendar.date nor weblog.startDate parameters exist, 049 * then the calendar will default to the current month. 050 * 051 * @since 2.0 052 */ 053 054// FIXME: This class is extraordinarily lacking. 055public class CalendarTag extends WikiTagBase { 056 057 private static final long serialVersionUID = 0L; 058 private static final Logger LOG = LogManager.getLogger( CalendarTag.class ); 059 private static final int NUM_PAGES_TO_CHECK = 3; 060 061 private SimpleDateFormat m_pageFormat; 062 private SimpleDateFormat m_urlFormat; 063 private SimpleDateFormat m_monthUrlFormat; 064 private boolean m_addIndex; 065 private SimpleDateFormat m_dateFormat = new SimpleDateFormat( "ddMMyy" ); 066 067 /** 068 * {@inheritDoc} 069 */ 070 @Override 071 public void initTag() 072 { 073 super.initTag(); 074 m_pageFormat = m_urlFormat = m_monthUrlFormat = null; 075 m_dateFormat = new SimpleDateFormat( "ddMMyy" ); 076 } 077 078 /* 079 public void setYear( String year ) 080 { 081 m_year = year; 082 } 083 084 public void setMonth( String month ) 085 { 086 m_month = month; 087 } 088 */ 089 090 /** 091 * Sets the page format. If a page corresponding to the format is found when 092 * the calendar is being rendered, a link to that page is created. E.g. if the 093 * format is set to <tt>'Main_blogentry_'ddMMyy</tt>, it works nicely in 094 * conjunction to the WeblogPlugin. 095 * 096 * @param format The format in the SimpleDateFormat fashion. 097 * 098 * @see SimpleDateFormat 099 * @see org.apache.wiki.plugin.WeblogPlugin 100 */ 101 public void setPageformat( final String format ) 102 { 103 m_pageFormat = new SimpleDateFormat( format ); 104 } 105 106 /** 107 * Set the URL format. If the pageformat is not set, all dates are 108 * links to pages according to this format. The pageformat 109 * takes precedence. 110 * 111 * @param format The URL format in the SimpleDateFormat fashion. 112 * @see SimpleDateFormat 113 */ 114 public void setUrlformat( final String format ) 115 { 116 m_urlFormat = new SimpleDateFormat( format ); 117 } 118 119 /** 120 * Set the format to be used for links for the months. 121 * 122 * @param format The format to set in the SimpleDateFormat fashion. 123 * 124 * @see SimpleDateFormat 125 */ 126 public void setMonthurlformat( final String format ) 127 { 128 m_monthUrlFormat = new SimpleDateFormat( format ); 129 } 130 131 /** 132 * Sets whether or not the pageFormat contains a page index at the end. 133 * This is the case for the WeblogPlugin. 134 * 135 * @param addindex Whether a page index should be appended to the pageFormat 136 * 137 * @see org.apache.wiki.plugin.WeblogPlugin 138 */ 139 public void setAddindex( final boolean addIndex ) 140 { 141 m_addIndex = addIndex; 142 } 143 144 private String format( final String txt ) { 145 final Page p = m_wikiContext.getPage(); 146 if( p != null ) { 147 return TextUtil.replaceString( txt, "%p", p.getName() ); 148 } 149 150 return txt; 151 } 152 153 /** 154 * Returns a link to the given day. 155 */ 156 private String getDayLink( final Calendar day ) { 157 final Engine engine = m_wikiContext.getEngine(); 158 final String result; 159 160 if( m_pageFormat != null ) { 161 final String pagename = m_pageFormat.format( day.getTime() ); 162 163 var somePageExistsOnThisDay = false; 164 if (m_addIndex) { 165 // Look at up to 3 pages for whether the page exists. This avoids an issue 166 // with the WeblogPlugin when the first blog post(s) of a day gets deleted. 167 for (int pageIdx = 1; pageIdx <= NUM_PAGES_TO_CHECK; pageIdx++) { 168 if( engine.getManager( PageManager.class ).wikiPageExists( pagename+pageIdx ) ) { 169 somePageExistsOnThisDay = true; 170 break; 171 } 172 } 173 } else { 174 somePageExistsOnThisDay = engine.getManager( PageManager.class ).wikiPageExists( pagename ); 175 } 176 177 if( somePageExistsOnThisDay ) { 178 if( m_urlFormat != null ) { 179 final String url = m_urlFormat.format( day.getTime() ); 180 result = "<td class=\"link\"><a href=\""+url+"\">"+day.get( Calendar.DATE )+"</a></td>"; 181 } else { 182 result = "<td class=\"link\"><a href=\""+m_wikiContext.getViewURL( pagename )+"\">"+ 183 day.get( Calendar.DATE )+"</a></td>"; 184 } 185 } else { 186 result = "<td class=\"days\">"+day.get(Calendar.DATE)+"</td>"; 187 } 188 } else if( m_urlFormat != null ) { 189 final String url = m_urlFormat.format( day.getTime() ); 190 result = "<td><a href=\""+url+"\">"+day.get( Calendar.DATE )+"</a></td>"; 191 } else { 192 result = "<td class=\"days\">"+day.get(Calendar.DATE)+"</td>"; 193 } 194 195 return format( result ); 196 } 197 198 private String getMonthLink( final Calendar day ) { 199 final SimpleDateFormat monthfmt = new SimpleDateFormat( "MMMM yyyy" ); 200 final String result; 201 202 if( m_monthUrlFormat == null ) { 203 result = monthfmt.format( day.getTime() ); 204 } else { 205 final Calendar cal = (Calendar)day.clone(); 206 final int firstDay = cal.getActualMinimum( Calendar.DATE ); 207 final int lastDay = cal.getActualMaximum( Calendar.DATE ); 208 209 cal.set( Calendar.DATE, lastDay ); 210 String url = m_monthUrlFormat.format( cal.getTime() ); 211 212 url = TextUtil.replaceString( url, "%d", Integer.toString( lastDay - firstDay + 1 ) ); 213 214 result = "<a href=\""+url+"\">"+monthfmt.format(cal.getTime())+"</a>"; 215 } 216 217 return format( result ); 218 } 219 220 private String getMonthNaviLink( final Calendar day, final String txt, String queryString ) { 221 final String result; 222 queryString = TextUtil.replaceEntities( queryString ); 223 final Calendar nextMonth = Calendar.getInstance(); 224 nextMonth.set( Calendar.DATE, 1 ); 225 nextMonth.add( Calendar.DATE, -1); 226 nextMonth.add( Calendar.MONTH, 1 ); // Now move to 1st day of next month 227 228 if ( day.before( nextMonth ) ) { 229 final Page thePage = m_wikiContext.getPage(); 230 final String pageName = thePage.getName(); 231 232 final String calendarDate = m_dateFormat.format( day.getTime() ); 233 String url = m_wikiContext.getURL( ContextEnum.PAGE_VIEW.getRequestContext(), pageName,"calendar.date="+calendarDate ); 234 final int queryStringLlength = queryString.length(); 235 if(queryStringLlength > 0) { 236 // 237 // Ensure that the 'calendar.date=ddMMyy' has been removed from the queryString 238 // 239 240 // FIXME: Might be useful to have an entire library of 241 // routines for this. Will fail if it's not calendar.date 242 // but something else. 243 244 final int pos1 = queryString.indexOf("calendar.date="); 245 if( pos1 >= 0 ) { 246 String tmp = queryString.substring( 0, pos1 ); 247 // FIXME: Will this fail when we use & instead of &? 248 // FIXME: should use some parsing routine 249 final int pos2 = queryString.indexOf("&", pos1 ) + 1; 250 if ( ( pos2 > 0 ) && ( pos2 < queryStringLlength ) ) { 251 tmp = tmp + queryString.substring(pos2); 252 } 253 queryString = tmp; 254 } 255 256 if( queryStringLlength > 0 ) { 257 url = url + "&" + queryString; 258 } 259 } 260 result = "<td><a href=\""+url+"\">"+txt+"</a></td>"; 261 } else { 262 result="<td> </td>"; 263 } 264 265 return format( result ); 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override 272 public final int doWikiStartTag() throws IOException { 273 final Engine engine = m_wikiContext.getEngine(); 274 final JspWriter out = pageContext.getOut(); 275 final Calendar cal = Calendar.getInstance(); 276 final Calendar prevCal = Calendar.getInstance(); 277 final Calendar nextCal = Calendar.getInstance(); 278 279 // 280 // Check if there is a parameter in the request to set the date. 281 // 282 String calendarDate = pageContext.getRequest().getParameter( "calendar.date" ); 283 if( calendarDate == null ) { 284 calendarDate = pageContext.getRequest().getParameter( "weblog.startDate" ); 285 } 286 287 if( calendarDate != null ) { 288 try { 289 final Date d = m_dateFormat.parse( calendarDate ); 290 cal.setTime( d ); 291 prevCal.setTime( d ); 292 nextCal.setTime( d ); 293 } catch( final ParseException e ) { 294 LOG.warn( "date format wrong: " + calendarDate ); 295 } 296 } 297 298 cal.set( Calendar.DATE, 1 ); // First, set to first day of month 299 prevCal.set( Calendar.DATE, 1 ); 300 nextCal.set( Calendar.DATE, 1 ); 301 302 prevCal.add(Calendar.MONTH, -1); // Now move to first day of previous month 303 nextCal.add(Calendar.MONTH, 1); // Now move to first day of next month 304 305 out.write( "<table class=\"calendar\">\n" ); 306 307 final HttpServletRequest httpServletRequest = m_wikiContext.getHttpRequest(); 308 final String queryString = HttpUtil.safeGetQueryString( httpServletRequest, engine.getContentEncoding() ); 309 out.write( "<tr>"+ 310 getMonthNaviLink(prevCal,"<<", queryString)+ 311 "<td colspan=5 class=\"month\">"+ 312 getMonthLink( cal )+ 313 "</td>"+ 314 getMonthNaviLink(nextCal,">>", queryString)+ 315 "</tr>\n" 316 ); 317 318 final int month = cal.get( Calendar.MONTH ); 319 cal.set( Calendar.DAY_OF_WEEK, Calendar.MONDAY ); // Then, find the first day of the week. 320 321 out.write( "<tr><td class=\"weekdays\">Mon</td>"+ 322 "<td class=\"weekdays\">Tue</td>"+ 323 "<td class=\"weekdays\">Wed</td>"+ 324 "<td class=\"weekdays\">Thu</td>"+ 325 "<td class=\"weekdays\">Fri</td>"+ 326 "<td class=\"weekdays\">Sat</td>"+ 327 "<td class=\"weekdays\">Sun</td></tr>\n" ); 328 329 boolean noMoreDates = false; 330 while( !noMoreDates ) { 331 out.write( "<tr>" ); 332 333 for( int i = 0; i < 7; i++ ) { 334 final int mth = cal.get( Calendar.MONTH ); 335 336 if( mth != month ) { 337 out.write("<td class=\"othermonth\">"+cal.get(Calendar.DATE)+"</td>"); 338 } else { 339 out.write( getDayLink(cal) ); 340 } 341 342 cal.add( Calendar.DATE, 1 ); 343 } 344 345 if( cal.get( Calendar.MONTH ) != month ) { 346 noMoreDates = true; 347 } 348 349 out.write( "</tr>\n" ); 350 } 351 352 out.write( "</table>\n" ); 353 354 return EVAL_BODY_INCLUDE; 355 } 356 357}