001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.plugin;
021:
022: import com.ecyrd.jspwiki.*;
023: import com.ecyrd.jspwiki.providers.ProviderException;
024: import org.apache.log4j.Logger;
025:
026: import java.text.SimpleDateFormat;
027: import java.text.ParseException;
028: import java.util.*;
029:
030: /**
031: * Builds a simple weblog.
032: * <P>
033: * The pageformat can use the following params:<br>
034: * %p - Page name<br>
035: *
036: * <B>Parameters</B>
037: * <UL>
038: * <LI>page - which page is used to do the blog; default is the current page.
039: * <LI>days - how many days the weblog aggregator should show. If set to "all", shows all pages.
040: * <LI>pageformat - What the entry pages should look like.
041: * <LI>startDate - Date when to start. Format is "ddMMyy";
042: * <li>maxEntries - How many entries to show at most.
043: * </UL>
044: *
045: * The "days" and "startDate" can also be sent in HTTP parameters,
046: * and the names are "weblog.days" and "weblog.startDate", respectively.
047: * <p>
048: * The weblog plugin also adds an attribute to each page it is on: "weblogplugin.isweblog" is set to "true". This can be used to quickly peruse pages which have weblogs.
049: * @since 1.9.21
050: */
051:
052: // FIXME: Add "entries" param as an alternative to "days".
053: // FIXME: Entries arrive in wrong order.
054: public class WeblogPlugin implements WikiPlugin, InitializablePlugin {
055: private static Logger log = Logger.getLogger(WeblogPlugin.class);
056:
057: public static final int DEFAULT_DAYS = 7;
058: public static final String DEFAULT_PAGEFORMAT = "%p_blogentry_";
059:
060: public static final String DEFAULT_DATEFORMAT = "ddMMyy";
061:
062: public static final String PARAM_STARTDATE = "startDate";
063: public static final String PARAM_DAYS = "days";
064: public static final String PARAM_ALLOWCOMMENTS = "allowComments";
065: public static final String PARAM_MAXENTRIES = "maxEntries";
066: public static final String PARAM_PAGE = "page";
067:
068: public static final String ATTR_ISWEBLOG = "weblogplugin.isweblog";
069:
070: public static String makeEntryPage(String pageName, String date,
071: String entryNum) {
072: return TextUtil.replaceString(DEFAULT_PAGEFORMAT, "%p",
073: pageName)
074: + date + "_" + entryNum;
075: }
076:
077: public static String makeEntryPage(String pageName) {
078: return TextUtil.replaceString(DEFAULT_PAGEFORMAT, "%p",
079: pageName);
080: }
081:
082: public static String makeEntryPage(String pageName, String date) {
083: return TextUtil.replaceString(DEFAULT_PAGEFORMAT, "%p",
084: pageName)
085: + date;
086: }
087:
088: /**
089: * Just sets the "I am a weblog" mark.
090: */
091: public void initialize(WikiContext context, Map params) {
092: context.getPage().setAttribute(ATTR_ISWEBLOG, "true");
093: }
094:
095: public String execute(WikiContext context, Map params)
096: throws PluginException {
097: Calendar startTime;
098: Calendar stopTime;
099: int numDays;
100: WikiEngine engine = context.getEngine();
101:
102: //
103: // Parse parameters.
104: //
105: String days;
106: String startDay = null;
107: boolean hasComments = false;
108: int maxEntries;
109: String weblogName;
110:
111: if ((weblogName = (String) params.get(PARAM_PAGE)) == null) {
112: weblogName = context.getPage().getName();
113: }
114:
115: if ((days = context.getHttpParameter("weblog." + PARAM_DAYS)) == null) {
116: days = (String) params.get(PARAM_DAYS);
117: }
118:
119: if (days != null && days.equalsIgnoreCase("all")) {
120: numDays = Integer.MAX_VALUE;
121: } else {
122: numDays = TextUtil.parseIntParameter(days, DEFAULT_DAYS);
123: }
124:
125: if ((startDay = (String) params.get(PARAM_STARTDATE)) == null) {
126: startDay = context.getHttpParameter("weblog."
127: + PARAM_STARTDATE);
128: }
129:
130: if (TextUtil.isPositive((String) params
131: .get(PARAM_ALLOWCOMMENTS))) {
132: hasComments = true;
133: }
134:
135: maxEntries = TextUtil.parseIntParameter((String) params
136: .get(PARAM_MAXENTRIES), Integer.MAX_VALUE);
137:
138: //
139: // Determine the date range which to include.
140: //
141:
142: startTime = Calendar.getInstance();
143: stopTime = Calendar.getInstance();
144:
145: if (startDay != null) {
146: SimpleDateFormat fmt = new SimpleDateFormat(
147: DEFAULT_DATEFORMAT);
148: try {
149: Date d = fmt.parse(startDay);
150: startTime.setTime(d);
151: stopTime.setTime(d);
152: } catch (ParseException e) {
153: return "Illegal time format: " + startDay;
154: }
155: }
156:
157: //
158: // Mark this to be a weblog
159: //
160:
161: context.getPage().setAttribute(ATTR_ISWEBLOG, "true");
162:
163: //
164: // We make a wild guess here that nobody can do millisecond
165: // accuracy here.
166: //
167: startTime.add(Calendar.DAY_OF_MONTH, -numDays);
168: startTime.set(Calendar.HOUR, 0);
169: startTime.set(Calendar.MINUTE, 0);
170: startTime.set(Calendar.SECOND, 0);
171: stopTime.set(Calendar.HOUR, 23);
172: stopTime.set(Calendar.MINUTE, 59);
173: stopTime.set(Calendar.SECOND, 59);
174:
175: StringBuffer sb = new StringBuffer();
176:
177: try {
178: List blogEntries = findBlogEntries(engine.getPageManager(),
179: weblogName, startTime.getTime(), stopTime.getTime());
180:
181: Collections.sort(blogEntries, new PageDateComparator());
182:
183: SimpleDateFormat entryDateFmt = new SimpleDateFormat(
184: "dd-MMM-yyyy HH:mm");
185:
186: sb.append("<div class=\"weblog\">\n");
187: for (Iterator i = blogEntries.iterator(); i.hasNext()
188: && maxEntries-- > 0;) {
189: WikiPage p = (WikiPage) i.next();
190:
191: sb.append("<div class=\"weblogentry\">\n");
192:
193: //
194: // Heading
195: //
196: sb.append("<div class=\"weblogentryheading\">\n");
197:
198: Date entryDate = p.getLastModified();
199: sb.append(entryDateFmt.format(entryDate));
200:
201: sb.append("</div>\n");
202:
203: //
204: // Append the text of the latest version. Reset the
205: // context to that page.
206: //
207:
208: sb.append("<div class=\"weblogentrybody\">\n");
209:
210: WikiContext entryCtx = (WikiContext) context.clone();
211: entryCtx.setPage(p);
212:
213: sb.append(engine.getHTML(entryCtx, engine.getPage(p
214: .getName())));
215:
216: sb.append("</div>\n");
217:
218: //
219: // Append footer
220: //
221: sb.append("<div class=\"weblogentryfooter\">\n");
222:
223: String author = p.getAuthor();
224: String displayAuthor = author;
225:
226: if (author != null) {
227: displayAuthor = context.getUserCommonName(author);
228: if (engine.pageExists(author)) {
229: displayAuthor = "<a href=\""
230: + entryCtx.getURL(WikiContext.VIEW,
231: author) + "\">"
232: + engine.beautifyTitle(displayAuthor)
233: + "</a>";
234: }
235: } else {
236: displayAuthor = "AnonymousCoward";
237: }
238: sb.append("By " + displayAuthor + " ");
239: sb.append("<a href=\""
240: + entryCtx
241: .getURL(WikiContext.VIEW, p.getName())
242: + "\">Permalink</a>");
243: String commentPageName = TextUtil.replaceString(p
244: .getName(), "blogentry", "comments");
245:
246: if (hasComments) {
247: int numComments = guessNumberOfComments(engine,
248: commentPageName);
249:
250: //
251: // We add the number of comments to the URL so that
252: // the user's browsers would realize that the page
253: // has changed.
254: //
255: sb.append(" ");
256: sb.append("<a target=\"_blank\" href=\""
257: + entryCtx.getURL(WikiContext.COMMENT,
258: commentPageName, "nc="
259: + numComments)
260: + "\">Comments? (" + numComments + ")</a>");
261: }
262:
263: sb.append("</div>\n");
264:
265: //
266: // Done, close
267: //
268: sb.append("</div>\n");
269: }
270:
271: sb.append("</div>\n");
272: } catch (ProviderException e) {
273: log.error("Could not locate blog entries", e);
274: throw new PluginException("Could not locate blog entries: "
275: + e.getMessage());
276: }
277:
278: return sb.toString();
279: }
280:
281: private int guessNumberOfComments(WikiEngine engine,
282: String commentpage) throws ProviderException {
283: String pagedata = engine.getPureText(commentpage,
284: WikiProvider.LATEST_VERSION);
285:
286: return TextUtil.countSections(pagedata);
287: }
288:
289: /**
290: * Attempts to locate all pages that correspond to the
291: * blog entry pattern. Will only consider the days on the dates; not the hours and minutes.
292: *
293: * Returns a list of pages with their FIRST revisions.
294: */
295: public List findBlogEntries(PageManager mgr, String baseName,
296: Date start, Date end) throws ProviderException {
297: Collection everyone = mgr.getAllPages();
298: ArrayList result = new ArrayList();
299:
300: baseName = makeEntryPage(baseName);
301: SimpleDateFormat fmt = new SimpleDateFormat(DEFAULT_DATEFORMAT);
302:
303: for (Iterator i = everyone.iterator(); i.hasNext();) {
304: WikiPage p = (WikiPage) i.next();
305:
306: String pageName = p.getName();
307:
308: if (pageName.startsWith(baseName)) {
309: //
310: // Check the creation date from the page name.
311: // We do this because RCSFileProvider is very slow at getting a
312: // specific page version.
313: //
314: try {
315: //log.debug("Checking: "+pageName);
316: int firstScore = pageName.indexOf('_', baseName
317: .length() - 1);
318: if (firstScore != -1
319: && firstScore + 1 < pageName.length()) {
320: int secondScore = pageName.indexOf('_',
321: firstScore + 1);
322:
323: if (secondScore != -1) {
324: String creationDate = pageName.substring(
325: firstScore + 1, secondScore);
326:
327: //log.debug(" Creation date: "+creationDate);
328:
329: Date pageDay = fmt.parse(creationDate);
330:
331: //
332: // Add the first version of the page into the list. This way
333: // the page modified date becomes the page creation date.
334: //
335: if (pageDay != null && pageDay.after(start)
336: && pageDay.before(end)) {
337: WikiPage firstVersion = mgr
338: .getPageInfo(pageName, 1);
339: result.add(firstVersion);
340: }
341: }
342: }
343: } catch (Exception e) {
344: log
345: .debug(
346: "Page name :"
347: + pageName
348: + " was suspected as a blog entry but it isn't because of parsing errors",
349: e);
350: }
351: }
352: }
353:
354: return result;
355: }
356:
357: /**
358: * Reverse comparison.
359: */
360: private class PageDateComparator implements Comparator {
361: public int compare(Object o1, Object o2) {
362: if (o1 == null || o2 == null) {
363: return 0;
364: }
365:
366: WikiPage page1 = (WikiPage) o1;
367: WikiPage page2 = (WikiPage) o2;
368:
369: return page2.getLastModified().compareTo(
370: page1.getLastModified());
371: }
372: }
373: }
|