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 java.io.IOException;
022import java.util.Calendar;
023import java.util.Date;
024import java.text.SimpleDateFormat;
025import java.text.ParseException;
026
027import javax.servlet.jsp.JspWriter;
028import javax.servlet.http.HttpServletRequest;
029
030import org.apache.log4j.Logger;
031import org.apache.wiki.WikiEngine;
032import org.apache.wiki.WikiPage;
033import org.apache.wiki.WikiContext;
034import org.apache.wiki.api.exceptions.ProviderException;
035import org.apache.wiki.util.TextUtil;
036
037
038/**
039 *  Provides a nice calendar.  Responds to the following HTTP parameters:
040 *  <ul>
041 *  <li>calendar.date - If this parameter exists, then the calendar
042 *  date is taken from the month and year.  The date must be in ddMMyy
043 *  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 *
052 *  @since 2.0
053 */
054
055// FIXME: This class is extraordinarily lacking.
056public class CalendarTag extends WikiTagBase {
057
058    private static final long serialVersionUID = 0L;
059    private static final Logger log = Logger.getLogger( CalendarTag.class );
060    
061    private SimpleDateFormat m_pageFormat = null;
062    private SimpleDateFormat m_urlFormat = null;
063    private SimpleDateFormat m_monthUrlFormat = null;
064    private SimpleDateFormat m_dateFormat = new SimpleDateFormat( "ddMMyy" );
065
066    /**
067     *  {@inheritDoc}
068     */
069    @Override
070    public void initTag()
071    {
072        super.initTag();
073        m_pageFormat = m_urlFormat = m_monthUrlFormat = null;
074        m_dateFormat = new SimpleDateFormat( "ddMMyy" );
075    }
076
077    /*
078    public void setYear( String year )
079    {
080        m_year = year;
081    }
082
083    public void setMonth( String month )
084    {
085        m_month = month;
086    }
087    */
088
089    /**
090     *  Sets the page format.  If a page corresponding to the format is found when
091     *  the calendar is being rendered, a link to that page is created.  E.g. if the
092     *  format is set to <tt>'Main_blogentry_'ddMMyy</tt>, it works nicely in
093     *  conjuction to the WeblogPlugin.
094     *  
095     *  @param format The format in the SimpleDateFormat fashion.
096     *  
097     *  @see SimpleDateFormat
098     *  @see org.apache.wiki.plugin.WeblogPlugin
099     */
100    public void setPageformat( String format )
101    {
102        m_pageFormat = new SimpleDateFormat(format);
103    }
104
105    /**
106     *  Set the URL format.  If the pageformat is not set, all dates are
107     *  links to pages according to this format.  The pageformat
108     *  takes precedence.
109     *  
110     *  @param format The URL format in the SimpleDateFormat fashion.
111     *  @see SimpleDateFormat
112     */
113    public void setUrlformat( String format )
114    {
115        m_urlFormat = new SimpleDateFormat(format);
116    }
117
118    /**
119     *  Set the format to be used for links for the months.
120     *  
121     *  @param format The format to set in the SimpleDateFormat fashion.
122     *  
123     *  @see SimpleDateFormat
124     */
125    public void setMonthurlformat( String format )
126    {
127        m_monthUrlFormat = new SimpleDateFormat( format );
128    }
129
130    private String format( String txt )
131    {
132        WikiPage p = m_wikiContext.getPage();
133
134        if( p != null )
135        {
136            return TextUtil.replaceString( txt, "%p", p.getName() );
137        }
138
139        return txt;
140    }
141
142    /**
143     *  Returns a link to the given day.
144     */
145    private String getDayLink( Calendar day )
146    {
147        WikiEngine engine = m_wikiContext.getEngine();
148        String result = "";
149
150        if( m_pageFormat != null )
151        {
152            String pagename = m_pageFormat.format( day.getTime() );
153            
154            if( engine.pageExists( pagename ) )
155            {
156                if( m_urlFormat != null )
157                {
158                    String url = m_urlFormat.format( day.getTime() );
159
160                    result = "<td class=\"link\"><a href=\""+url+"\">"+day.get( Calendar.DATE )+"</a></td>";
161                }
162                else
163                {
164                    result = "<td class=\"link\"><a href=\""+m_wikiContext.getViewURL( pagename )+"\">"+
165                             day.get( Calendar.DATE )+"</a></td>";
166                }
167            }
168            else
169            {
170                result = "<td class=\"days\">"+day.get(Calendar.DATE)+"</td>";
171            }
172        }
173        else if( m_urlFormat != null )
174        {
175            String url = m_urlFormat.format( day.getTime() );
176
177            result = "<td><a href=\""+url+"\">"+day.get( Calendar.DATE )+"</a></td>";
178        }
179        else
180        {
181            result = "<td class=\"days\">"+day.get(Calendar.DATE)+"</td>";
182        }
183
184        return format(result);
185    }
186
187    private String getMonthLink( Calendar day )
188    {
189        SimpleDateFormat monthfmt = new SimpleDateFormat( "MMMM yyyy" );
190        String result;
191
192        if( m_monthUrlFormat == null )
193        {
194            result = monthfmt.format( day.getTime() );
195        }
196        else
197        {
198            Calendar cal = (Calendar)day.clone();
199            int firstDay = cal.getActualMinimum( Calendar.DATE );
200            int lastDay  = cal.getActualMaximum( Calendar.DATE );
201
202            cal.set( Calendar.DATE, lastDay );
203            String url = m_monthUrlFormat.format( cal.getTime() );
204
205            url = TextUtil.replaceString( url, "%d", Integer.toString( lastDay-firstDay+1 ) );
206
207            result = "<a href=\""+url+"\">"+monthfmt.format(cal.getTime())+"</a>";
208        }
209
210        return format(result);
211        
212    }
213    private String getMonthNaviLink( Calendar day, String txt, String queryString )
214    {
215        String result = "";
216        queryString = TextUtil.replaceEntities( queryString );
217        Calendar nextMonth = Calendar.getInstance();
218        nextMonth.set( Calendar.DATE, 1 );  
219        nextMonth.add( Calendar.DATE, -1);
220        nextMonth.add( Calendar.MONTH, 1 ); // Now move to 1st day of next month
221
222        if ( day.before(nextMonth) )
223        {
224            WikiPage thePage = m_wikiContext.getPage();
225            String pageName = thePage.getName();
226
227            String calendarDate = m_dateFormat.format(day.getTime());
228            String url = m_wikiContext.getURL( WikiContext.VIEW, pageName, 
229                                               "calendar.date="+calendarDate );
230
231            if ( (queryString != null) && (queryString.length() > 0) )
232            {
233                //
234                // Ensure that the 'calendar.date=ddMMyy' has been removed 
235                // from the queryString
236                //
237
238                // FIXME: Might be useful to have an entire library of 
239                //        routines for this.  Will fail if it's not calendar.date 
240                //        but something else.
241
242                int pos1 = queryString.indexOf("calendar.date=");
243                if (pos1 >= 0)
244                {
245                    String tmp = queryString.substring(0,pos1);
246                    // FIXME: Will this fail when we use & instead of &amp?
247                    // FIXME: should use some parsing routine
248                    int pos2 = queryString.indexOf("&",pos1) + 1;
249                    if ( (pos2 > 0) && (pos2 < queryString.length()) )
250                    {
251                        tmp = tmp + queryString.substring(pos2);
252                    }
253                    queryString = tmp;
254                }
255
256                if( queryString != null && queryString.length() > 0 )
257                {
258                    url = url + "&amp;"+queryString;
259                }
260            }
261            result = "<td><a href=\""+url+"\">"+txt+"</a></td>";
262        }
263        else
264        {
265            result="<td> </td>";
266        }    
267
268        return format(result);
269    }
270
271    /**
272     *  {@inheritDoc}
273     */
274    @Override
275    public final int doWikiStartTag()
276        throws IOException,
277               ProviderException
278    {
279        WikiEngine       engine   = m_wikiContext.getEngine();
280        JspWriter        out      = pageContext.getOut();
281        Calendar         cal      = Calendar.getInstance();
282        Calendar         prevCal  = Calendar.getInstance();
283        Calendar         nextCal  = Calendar.getInstance();
284
285        //
286        //  Check if there is a parameter in the request to set the date.
287        //
288        String calendarDate = pageContext.getRequest().getParameter( "calendar.date" );
289        if( calendarDate == null )
290        {
291            calendarDate = pageContext.getRequest().getParameter( "weblog.startDate" );
292        }
293        
294        if( calendarDate != null )
295        {
296            try
297            {
298                Date d = m_dateFormat.parse( calendarDate );
299                cal.setTime( d );
300                prevCal.setTime( d );
301                nextCal.setTime( d );
302            }
303            catch( ParseException e )
304            {
305                log.warn( "date format wrong: "+calendarDate );
306            }
307        }
308
309        cal.set( Calendar.DATE, 1 );     // First, set to first day of month
310        prevCal.set( Calendar.DATE, 1 );
311        nextCal.set( Calendar.DATE, 1 );
312
313        prevCal.add(Calendar.MONTH, -1); // Now move to first day of previous month
314        nextCal.add(Calendar.MONTH, 1);  // Now move to first day of next month
315
316        out.write( "<table class=\"calendar\">\n" );
317
318        HttpServletRequest httpServletRequest = m_wikiContext.getHttpRequest();
319        String queryString = engine.safeGetQueryString( httpServletRequest );
320        out.write( "<tr>"+
321                   getMonthNaviLink(prevCal,"&lt;&lt;", queryString)+
322                   "<td colspan=5 class=\"month\">"+
323                   getMonthLink( cal )+
324                   "</td>"+
325                   getMonthNaviLink(nextCal,"&gt;&gt;", queryString)+ 
326                   "</tr>\n"
327                 );
328
329        int month = cal.get( Calendar.MONTH );
330        cal.set( Calendar.DAY_OF_WEEK, Calendar.MONDAY ); // Then, find the first day of the week.
331
332        out.write( "<tr><td class=\"weekdays\">Mon</td>"+
333                   "<td class=\"weekdays\">Tue</td>"+
334                   "<td class=\"weekdays\">Wed</td>"+
335                   "<td class=\"weekdays\">Thu</td>"+
336                   "<td class=\"weekdays\">Fri</td>"+
337                   "<td class=\"weekdays\">Sat</td>"+
338                   "<td class=\"weekdays\">Sun</td></tr>\n" );
339
340        boolean noMoreDates = false;
341        while( !noMoreDates )
342        {
343            out.write( "<tr>" );
344            
345            for( int i = 0; i < 7; i++ )
346            {
347                int mth = cal.get( Calendar.MONTH );
348
349                if( mth != month )
350                {
351                    out.write("<td class=\"othermonth\">"+cal.get(Calendar.DATE)+"</td>");
352                }
353                else
354                {
355                    out.write( getDayLink(cal) );
356                }
357
358                cal.add( Calendar.DATE, 1 );
359            }
360
361            if( cal.get( Calendar.MONTH ) != month )
362            {
363                noMoreDates = true;
364            }
365
366            out.write( "</tr>\n" );
367        }
368
369        out.write( "</table>\n" );
370
371        return EVAL_BODY_INCLUDE;
372    }
373
374}