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     */
019    package org.apache.wiki.tags;
020    
021    import java.io.IOException;
022    import java.util.Calendar;
023    import java.util.Date;
024    import java.text.SimpleDateFormat;
025    import java.text.ParseException;
026    
027    import javax.servlet.jsp.JspWriter;
028    import javax.servlet.http.HttpServletRequest;
029    
030    import org.apache.log4j.Logger;
031    import org.apache.wiki.WikiEngine;
032    import org.apache.wiki.WikiPage;
033    import org.apache.wiki.WikiContext;
034    import org.apache.wiki.api.exceptions.ProviderException;
035    import 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.
056    public 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    }