    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 jakarta.servlet.http.HttpServletRequest;
    031import jakarta.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 &amp?
    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 + "&amp;" + 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,"&lt;&lt;", queryString)+
    311                   "<td colspan=5 class=\"month\">"+
    312                   getMonthLink( cal )+
    313                   "</td>"+
    314                   getMonthNaviLink(nextCal,"&gt;&gt;", 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}