001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018: package org.apache.roller.ui.authoring.struts.actions;
019:
020: import java.io.IOException;
021: import java.net.MalformedURLException;
022: import java.util.ArrayList;
023: import java.util.Arrays;
024: import java.util.Date;
025: import java.util.Iterator;
026: import java.util.List;
027: import javax.servlet.ServletException;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.apache.struts.action.ActionErrors;
033: import org.apache.struts.action.ActionForm;
034: import org.apache.struts.action.ActionForward;
035: import org.apache.struts.action.ActionMapping;
036: import org.apache.struts.action.ActionMessage;
037: import org.apache.struts.action.ActionMessages;
038: import org.apache.struts.actions.DispatchAction;
039: import org.apache.struts.util.RequestUtils;
040: import org.apache.roller.RollerException;
041: import org.apache.roller.config.RollerRuntimeConfig;
042: import org.apache.roller.business.Roller;
043: import org.apache.roller.business.RollerFactory;
044: import org.apache.roller.business.WeblogManager;
045: import org.apache.roller.pojos.CommentData;
046: import org.apache.roller.pojos.WeblogEntryData;
047: import org.apache.roller.ui.core.BasePageModel;
048: import org.apache.roller.ui.core.RollerContext;
049: import org.apache.roller.ui.core.RollerRequest;
050: import org.apache.roller.ui.core.RollerSession;
051: import org.apache.roller.util.cache.CacheManager;
052: import org.apache.roller.ui.rendering.servlets.CommentServlet;
053: import org.apache.roller.ui.authoring.struts.formbeans.CommentManagementForm;
054: import org.apache.roller.util.Utilities;
055:
056: /**
057: * Action for quering, approving, marking as spam and deleting comments.
058: *
059: * @struts.action path="/roller-ui/authoring/commentManagement" name="commentManagementForm"
060: * scope="request" parameter="method"
061: *
062: * @struts.action path="/roller-ui/authoring/commentQuery" name="commentQueryForm"
063: * scope="request" parameter="method"
064: *
065: * @struts.action path="/roller-ui/admin/commentManagement" name="commentManagementForm"
066: * scope="request" parameter="method"
067: *
068: * @struts.action path="/roller-ui/admin/commentQuery" name="commentQueryForm"
069: * scope="request" parameter="method"
070: *
071: * @struts.action-forward name="commentManagement.page" path=".CommentManagement"
072: * @struts.action-forward name="commentManagementGlobal.page" path=".CommentManagementGlobal"
073: */
074: public final class CommentManagementAction extends DispatchAction {
075:
076: private static Log logger = LogFactory.getFactory().getInstance(
077: CommentManagementAction.class);
078:
079: public ActionForward query(ActionMapping mapping,
080: ActionForm actionForm, HttpServletRequest request,
081: HttpServletResponse response) throws IOException,
082: ServletException, RollerException {
083:
084: CommentManagementForm queryForm = (CommentManagementForm) actionForm;
085: RollerRequest rreq = RollerRequest.getRollerRequest(request);
086: RollerSession rses = RollerSession.getRollerSession(request);
087:
088: ActionForward fwd = null;
089: // Ensure user is authorized to view comments in weblog
090: if (rreq.getWebsite() != null
091: && rses.isUserAuthorized(rreq.getWebsite())) {
092: fwd = mapping.findForward("commentManagement.page");
093: }
094: // Ensure only global admins can see all comments
095: else if (rses.isGlobalAdminUser()) {
096: fwd = mapping.findForward("commentManagementGlobal.page");
097: } else {
098: // And everybody else gets...
099: return mapping.findForward("access-denied");
100: }
101:
102: if (rreq.getWeblogEntry() != null) {
103: queryForm.setEntryid(rreq.getWeblogEntry().getId());
104: queryForm.setWeblog(rreq.getWeblogEntry().getWebsite()
105: .getHandle());
106: } else if (rreq.getWebsite() != null) {
107: queryForm.setWeblog(rreq.getWebsite().getHandle());
108: }
109: request.setAttribute("model", new CommentManagementPageModel(
110: "commentManagement.title", request, response, mapping,
111: queryForm));
112: if (request.getAttribute("commentManagementForm") == null) {
113: request.setAttribute("commentManagementForm", actionForm);
114: }
115: return fwd;
116: }
117:
118: public ActionForward bulkDelete(ActionMapping mapping,
119: ActionForm actionForm, HttpServletRequest request,
120: HttpServletResponse response) throws Exception {
121: if ("POST".equals(request.getMethod())) {
122: RollerRequest rreq = RollerRequest
123: .getRollerRequest(request);
124: RollerSession rses = RollerSession
125: .getRollerSession(request);
126: if (rreq.getWebsite() != null
127: && rses.isUserAuthorized(rreq.getWebsite())
128: || rses.isGlobalAdminUser()) {
129: WeblogManager wmgr = RollerFactory.getRoller()
130: .getWeblogManager();
131: CommentManagementForm queryForm = (CommentManagementForm) actionForm;
132: wmgr.removeMatchingComments(rreq.getWebsite(), rreq
133: .getWeblogEntry(), queryForm.getSearchString(),
134: queryForm.getStartDate(request.getLocale()),
135: queryForm.getEndDate(request.getLocale()),
136: queryForm.getPending(),
137: queryForm.getApproved(), queryForm.getSpam());
138: }
139: CommentManagementForm queryForm = (CommentManagementForm) actionForm;
140: }
141: return query(mapping, actionForm, request, response);
142: }
143:
144: public ActionForward update(ActionMapping mapping,
145: ActionForm actionForm, HttpServletRequest request,
146: HttpServletResponse response) throws IOException,
147: ServletException, RollerException {
148:
149: CommentManagementForm queryForm = (CommentManagementForm) actionForm;
150: RollerRequest rreq = RollerRequest.getRollerRequest(request);
151: if (rreq.getWeblogEntry() != null) {
152: queryForm.setEntryid(rreq.getWeblogEntry().getId());
153: queryForm.setWeblog(rreq.getWeblogEntry().getWebsite()
154: .getHandle());
155: } else if (rreq.getWebsite() != null) {
156: queryForm.setWeblog(rreq.getWebsite().getHandle());
157: } else {
158: // user needs Global Admin rights to access site-wide comments
159: RollerSession rses = RollerSession
160: .getRollerSession(request);
161: if (!rses.isGlobalAdminUser()) {
162: return mapping.findForward("access-denied");
163: }
164: }
165: RollerSession rses = RollerSession.getRollerSession(request);
166: try {
167: if (rses.isGlobalAdminUser()
168: || (rreq.getWebsite() != null && rses
169: .isUserAuthorizedToAuthor(rreq.getWebsite()))) {
170: WeblogManager mgr = RollerFactory.getRoller()
171: .getWeblogManager();
172:
173: // delete all comments with delete box checked
174: CommentData deleteComment = null;
175: String[] deleteIds = queryForm.getDeleteComments();
176: List deletedList = Arrays.asList(deleteIds);
177: if (deleteIds != null && deleteIds.length > 0) {
178: for (int j = 0; j < deleteIds.length; j++) {
179: deleteComment = mgr.getComment(deleteIds[j]);
180:
181: mgr.removeComment(deleteComment);
182: }
183: }
184:
185: // Collect comments approved for first time, so we can send
186: // out comment approved notifications later
187: List approvedComments = new ArrayList();
188:
189: // loop through IDs of all comments displayed on page
190: String[] ids = Utilities.stringToStringArray(queryForm
191: .getIds(), ",");
192: List flushList = new ArrayList();
193: for (int i = 0; i < ids.length; i++) {
194: if (deletedList.contains(ids[i]))
195: continue;
196: CommentData comment = mgr.getComment(ids[i]);
197:
198: // apply spam checkbox
199: List spamIds = Arrays.asList(queryForm
200: .getSpamComments());
201: if (spamIds.contains(ids[i])) {
202: comment.setSpam(Boolean.TRUE);
203: } else {
204: comment.setSpam(Boolean.FALSE);
205: }
206:
207: // Only participate in comment review workflow if we're
208: // working within one specfic weblog. Global admins should
209: // be able to mark-as-spam and delete comments without
210: // interfering with moderation by bloggers.
211: if (rreq.getWebsite() != null) {
212:
213: // all comments reviewed, so they're no longer pending
214: if (comment.getPending() != null
215: && comment.getPending().booleanValue()) {
216: comment.setPending(Boolean.FALSE);
217: approvedComments.add(comment);
218: }
219:
220: // apply pending checkbox
221: List approvedIds = Arrays.asList(queryForm
222: .getApprovedComments());
223: if (approvedIds.contains(ids[i])) {
224: comment.setApproved(Boolean.TRUE);
225:
226: } else {
227: comment.setApproved(Boolean.FALSE);
228: }
229: }
230: mgr.saveComment(comment);
231: flushList.add(comment);
232: }
233:
234: RollerFactory.getRoller().flush();
235: for (Iterator comments = flushList.iterator(); comments
236: .hasNext();) {
237: CacheManager.invalidate((CommentData) comments
238: .next());
239: }
240:
241: sendCommentNotifications(request, approvedComments);
242:
243: ActionMessages msgs = new ActionMessages();
244: msgs.add(ActionMessages.GLOBAL_MESSAGE,
245: new ActionMessage(
246: "commentManagement.updateSuccess"));
247: saveMessages(request, msgs);
248: }
249: } catch (Exception e) {
250: ActionMessages errors = new ActionMessages();
251: errors.add(ActionErrors.GLOBAL_MESSAGE, new ActionMessage(
252: "commentManagement.updateError", e.toString()));
253: saveErrors(request, errors);
254: logger.error("ERROR updating comments", e);
255: }
256: CommentManagementPageModel model = new CommentManagementPageModel(
257: "commentManagement.title", request, response, mapping,
258: queryForm);
259: request.setAttribute("model", model);
260: if (request.getAttribute("commentManagementForm") == null) {
261: request.setAttribute("commentManagementForm", actionForm);
262: }
263:
264: if (rreq.getWebsite() != null) {
265: return mapping.findForward("commentManagement.page");
266: }
267: return mapping.findForward("commentManagementGlobal.page");
268: }
269:
270: private void sendCommentNotifications(HttpServletRequest req,
271: List comments) throws RollerException {
272:
273: RollerContext rc = RollerContext.getRollerContext();
274: String rootURL = RollerRuntimeConfig.getAbsoluteContextURL();
275: try {
276: if (rootURL == null || rootURL.trim().length() == 0) {
277: rootURL = RequestUtils.serverURL(req)
278: + req.getContextPath();
279: }
280: } catch (MalformedURLException e) {
281: logger.error("ERROR: determining URL of site");
282: return;
283: }
284:
285: Iterator iter = comments.iterator();
286: while (iter.hasNext()) {
287: CommentData comment = (CommentData) iter.next();
288:
289: // Send email notifications because a new comment has been approved
290: CommentServlet.sendEmailNotification(comment, rootURL);
291:
292: // Send approval notification to author of approved comment
293: CommentServlet.sendEmailApprovalNotification(comment,
294: rootURL);
295: }
296: }
297:
298: public class CommentManagementPageModel extends BasePageModel {
299: private List comments = new ArrayList();
300: private WeblogEntryData weblogEntry = null;
301: private CommentManagementForm queryForm = null;
302: private boolean more = false;
303: private int totalMatchingCommentCount = 0;
304: private boolean showBulkDeleteLink = false;
305:
306: public CommentManagementPageModel(String titleKey,
307: HttpServletRequest request,
308: HttpServletResponse response, ActionMapping mapping,
309: CommentManagementForm queryForm) throws RollerException {
310:
311: super (titleKey, request, response, mapping);
312: this .queryForm = queryForm;
313:
314: Roller roller = RollerFactory.getRoller();
315: RollerRequest rreq = RollerRequest
316: .getRollerRequest(request);
317: if (rreq.getWeblogEntry() != null) {
318: website = rreq.getWeblogEntry().getWebsite();
319: weblogEntry = rreq.getWeblogEntry();
320: } else if (rreq.getWebsite() != null) {
321: website = rreq.getWebsite();
322: }
323: WeblogManager blogmgr = roller.getWeblogManager();
324:
325: int offset = queryForm.getOffset();
326: comments = blogmgr.getComments(website, weblogEntry,
327: queryForm.getSearchString(), queryForm
328: .getStartDate(request.getLocale()),
329: queryForm.getEndDate(request.getLocale()),
330: queryForm.getPending(), queryForm.getApproved(),
331: queryForm.getSpam(), true, // reverse chrono order
332: queryForm.getOffset(), queryForm.getCount() + 1);
333: if (comments.size() > queryForm.getCount()) {
334: more = true;
335: comments.remove(comments.size() - 1);
336: }
337: this .queryForm.loadCheckboxes(comments);
338:
339: // If we have a query POST, then we know we're responding to a query so
340: // we need to decide whether or not to show the bulk comment prompt.
341: if ("POST".equals(request.getMethod())
342: && "query".equals(request.getParameter("method"))) {
343:
344: // So we run the query again, except this time with no limit.
345: List allMatchingComments = blogmgr.getComments(website,
346: weblogEntry, queryForm.getSearchString(),
347: queryForm.getStartDate(request.getLocale()),
348: queryForm.getEndDate(request.getLocale()),
349: queryForm.getPending(),
350: queryForm.getApproved(), queryForm.getSpam(),
351: true, 0, -1);
352: totalMatchingCommentCount = allMatchingComments.size();
353:
354: // If there are more comments than can be shown on one
355: // page, then present the bulk-comment delete prompt.
356: showBulkDeleteLink = totalMatchingCommentCount > queryForm
357: .getCount();
358: }
359: }
360:
361: public List getComments() {
362: return comments;
363: }
364:
365: public int getCommentCount() {
366: int ret = comments.size();
367: return ret > queryForm.getCount() ? queryForm.getCount()
368: : ret;
369: }
370:
371: /**
372: * Number of pending entries on current page of results.
373: * Returns zero when managing comments across entire site, because
374: * we don't want global admins to change pending status of posts.
375: */
376: public int getPendingCommentCount() {
377: int count = 0;
378: if (getWebsite() != null) {
379: for (Iterator iter = comments.iterator(); iter
380: .hasNext();) {
381: CommentData cd = (CommentData) iter.next();
382: if (cd.getPending().booleanValue())
383: count++;
384: }
385: }
386: return count;
387: }
388:
389: public Date getEarliestDate() {
390: Date date = null;
391: if (comments.size() > 0) {
392: CommentData earliest = (CommentData) comments
393: .get(comments.size() - 1);
394: date = earliest.getPostTime();
395: }
396: return date;
397: }
398:
399: public Date getLatestDate() {
400: Date date = null;
401: if (comments.size() > 0) {
402: CommentData latest = (CommentData) comments.get(0);
403: date = latest.getPostTime();
404: }
405: return date;
406: }
407:
408: public WeblogEntryData getWeblogEntry() {
409: return weblogEntry;
410: }
411:
412: public String getLink() {
413: return getQueryLink() + "&offset=" + queryForm.getOffset();
414: }
415:
416: public String getNextLink() {
417: if (more) {
418: int offset = queryForm.getOffset()
419: + queryForm.getCount();
420: offset = (offset < 0) ? 0 : offset;
421: return getQueryLink() + "&offset=" + offset;
422: } else {
423: return null;
424: }
425: }
426:
427: public String getPrevLink() {
428: if (queryForm.getOffset() > 0) {
429: int offset = queryForm.getOffset()
430: - queryForm.getCount();
431: offset = (offset < 0) ? 0 : offset;
432: return getQueryLink() + "&offset=" + offset;
433: } else {
434: return null;
435: }
436: }
437:
438: private String getQueryLink() {
439: StringBuffer sb = new StringBuffer();
440: sb.append(request.getContextPath());
441: if (getWebsite() != null) {
442: sb.append("/roller-ui/authoring/commentManagement.do"); // TODO: get path from Struts
443: sb.append("?method=query");
444: sb.append("&weblog=");
445: sb.append(getWebsite().getHandle());
446: } else {
447: sb.append("/roller-ui/admin/commentManagement.do"); // TODO: get path from Struts
448: sb.append("?method=query");
449: }
450: sb.append("&count=");
451: sb.append(queryForm.getCount());
452: return sb.toString();
453: }
454:
455: public int getTotalMatchingCommentCount() {
456: return totalMatchingCommentCount;
457: }
458:
459: public boolean isShowBulkDeleteLink() {
460: return showBulkDeleteLink;
461: }
462: }
463: }
|