From e7f5bd0a98e8b7ad0d22c736c704d689a8a0345c Mon Sep 17 00:00:00 2001 From: quasi Date: Wed, 28 Dec 2011 08:59:05 +0000 Subject: [PATCH] Forum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Erweitert, so daß per Konfiguration eingestellt werden kann, ob ein Autor einen eigenen Beitrag löschen darf, falls es der letzte Eintrag im Thread ist * Dateien werden nun als Download Verknüpft, nicht mehr als direkte Ansicht git-svn-id: https://svn.libreccm.org/ccm/trunk@1407 8810af33-2d31-482b-a856-94f89814c4df --- ccm-forum/src/com/arsdigita/forum/Forum.java | 12 + .../src/com/arsdigita/forum/ForumConfig.java | 12 +- .../forum/ForumConfig_parameter.properties | 7 +- .../src/com/arsdigita/forum/ForumContext.java | 10 + ccm-forum/src/com/arsdigita/forum/Post.java | 535 +++++++++--------- .../forum/PostFileAttachmentURLFinder.java | 69 ++- .../arsdigita/forum/ui/AttachedFilesStep.java | 2 +- .../src/com/arsdigita/forum/ui/Constants.java | 4 +- .../forum/ui/DiscussionPostsList.java | 229 ++++---- 9 files changed, 458 insertions(+), 422 deletions(-) diff --git a/ccm-forum/src/com/arsdigita/forum/Forum.java b/ccm-forum/src/com/arsdigita/forum/Forum.java index 96bbbb43d..1c5787fc0 100755 --- a/ccm-forum/src/com/arsdigita/forum/Forum.java +++ b/ccm-forum/src/com/arsdigita/forum/Forum.java @@ -731,6 +731,18 @@ public class Forum extends Application { party))); } + /** + * checks if the user can delete posts in this forum + */ + public boolean canDelete(Party party) { + return ((getConfig().canAdminEditPosts() + || getConfig().canAuthorDeletePosts()) + && PermissionService.checkPermission( + new PermissionDescriptor(PrivilegeDescriptor.DELETE, + this, + party))); + } + public boolean canAdminister(Party party) { return PermissionService.checkPermission( new PermissionDescriptor(PrivilegeDescriptor.ADMIN, diff --git a/ccm-forum/src/com/arsdigita/forum/ForumConfig.java b/ccm-forum/src/com/arsdigita/forum/ForumConfig.java index 5720039ef..53f365f6f 100755 --- a/ccm-forum/src/com/arsdigita/forum/ForumConfig.java +++ b/ccm-forum/src/com/arsdigita/forum/ForumConfig.java @@ -49,6 +49,7 @@ public class ForumConfig extends AbstractConfig { private Parameter m_adminEditPosts; private Parameter m_authorEditPosts; + private Parameter m_authorDeletePosts; private Parameter m_digestUserEmail; private Parameter m_replyHostName; private Parameter m_disablePageCaching; @@ -74,6 +75,10 @@ public class ForumConfig extends AbstractConfig { "com.arsdigita.forum.author_can_edit_posts", Parameter.REQUIRED, Boolean.TRUE); + m_authorDeletePosts = new BooleanParameter( + "com.arsdigita.forum.author_can_delete_posts", + Parameter.REQUIRED, + Boolean.TRUE); m_replyHostName = new StringParameter( "com.arsdigita.forum.reply_host_name", Parameter.OPTIONAL, @@ -134,6 +139,7 @@ public class ForumConfig extends AbstractConfig { register(m_digestUserEmail); register(m_adminEditPosts); register(m_authorEditPosts); + register(m_authorDeletePosts); register(m_replyHostName); register(m_adapters); register(m_disablePageCaching); @@ -162,6 +168,10 @@ public class ForumConfig extends AbstractConfig { return ((Boolean)get(m_authorEditPosts)).booleanValue(); } + boolean canAuthorDeletePosts() { + return ((Boolean)get(m_authorDeletePosts)).booleanValue(); + } + public String getDigestUserEmail() { String email = (String)get(m_digestUserEmail); if (email == null) { @@ -295,5 +305,5 @@ public class ForumConfig extends AbstractConfig { public boolean deleteNotifications () { return ((Boolean)get(m_deleteSentSubscriptionNotifications)).booleanValue(); } - + } diff --git a/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties b/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties index 9628cd504..3c8820862 100755 --- a/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties +++ b/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties @@ -24,10 +24,15 @@ com.arsdigita.forum.admin_only_to_create_topics.format=[boolean] com.arsdigita.forum.admin_only_to_create_topics.example=true|false com.arsdigita.forum.author_can_edit_posts.title=Authors can edit posts -com.arsdigita.forum.author_can_edit_posts.purpose=Whether authors can edijt their posts +com.arsdigita.forum.author_can_edit_posts.purpose=Whether authors can edit their posts com.arsdigita.forum.author_can_edit_posts.format=[boolean] com.arsdigita.forum.author_can_edit_posts.example=true|false +com.arsdigita.forum.author_can_delete_posts.title=Authors can delete posts +com.arsdigita.forum.author_can_delete_posts.purpose=Whether authors can delete their posts as long it is the last in the thread +com.arsdigita.forum.author_can_delete_posts.format=[boolean] +com.arsdigita.forum.author_can_delete_posts.example=true|false + com.arsdigita.forum.disable_page_caching.title=Disable client & middleware page caching com.arsdigita.forum.disable_page_caching.purpose=Disable caching, particularly for situations where users share PCs com.arsdigita.forum.disable_page_caching.format=[boolean] diff --git a/ccm-forum/src/com/arsdigita/forum/ForumContext.java b/ccm-forum/src/com/arsdigita/forum/ForumContext.java index fe7e295a8..0d47216b9 100755 --- a/ccm-forum/src/com/arsdigita/forum/ForumContext.java +++ b/ccm-forum/src/com/arsdigita/forum/ForumContext.java @@ -157,6 +157,16 @@ public final class ForumContext { return m_canAdminister; } + /** + * + * @return + */ + public boolean canDelete(Post post) { + Party party = Kernel.getContext().getParty(); + + return post.canDelete(party); + } + /** * * @return diff --git a/ccm-forum/src/com/arsdigita/forum/Post.java b/ccm-forum/src/com/arsdigita/forum/Post.java index fe36f9df4..8cf2a60fb 100755 --- a/ccm-forum/src/com/arsdigita/forum/Post.java +++ b/ccm-forum/src/com/arsdigita/forum/Post.java @@ -114,39 +114,31 @@ import com.arsdigita.util.Assert; * @author Nobuko Asakai (nasakai@redhat.com) */ public class Post extends ThreadedMessage { - private static final Logger s_log = Logger.getLogger(Post.class); + private static final Logger s_log = Logger.getLogger(Post.class); /** PDL property for marking the approval state of a message, one * of 'approved', 'rejected', 'reapprove', 'supressed' */ public static final String STATUS = "status"; - /** ID of the administrator who last changed the status of a * message */ public static final String MODERATOR = "moderator"; - /** * 0..n association with PostImageAttachments */ public static final String IMAGE_ATTACHMENTS = "images"; - /** * 0..n association with PostFileAttachments */ public static final String FILE_ATTACHMENTS = "files"; - /** The status strings */ public static final String PENDING = "pending"; public static final String APPROVED = "approved"; public static final String REJECTED = "rejected"; public static final String REAPPROVE = "reapprove"; public static final String SUPPRESSED = "suppressed"; - - public static final String POST_STATUS_SUBQUERY = - "com.arsdigita.forum.threadModerationStatus"; - + "com.arsdigita.forum.threadModerationStatus"; private Party m_moderator; - // referred to afterSave method private boolean m_wasNew; @@ -155,13 +147,13 @@ public class Post extends ThreadedMessage { * other words, all bboard messages are ThreadedMessages. */ public static final String BASE_DATA_OBJECT_TYPE = - "com.arsdigita.forum.Post"; + "com.arsdigita.forum.Post"; private Post() { this(BASE_DATA_OBJECT_TYPE); } - public Post(String typeName) { + public Post(String typeName) { super(typeName); } @@ -169,9 +161,9 @@ public class Post extends ThreadedMessage { super(oid); } - public Post(BigDecimal id) { - this(new OID(BASE_DATA_OBJECT_TYPE, id)); - } + public Post(BigDecimal id) { + this(new OID(BASE_DATA_OBJECT_TYPE, id)); + } public Post(DataObject obj) { super(obj); @@ -217,10 +209,10 @@ public class Post extends ThreadedMessage { BigDecimal id = getID(); // XXX this isn't really the host we want - setRFCMessageID(id + ".bboard@" + - Forum.getConfig().getReplyHostName()); - setReplyTo(getRefersTo() + ".bboard@" + - Forum.getConfig().getReplyHostName()); + setRFCMessageID(id + ".bboard@" + + Forum.getConfig().getReplyHostName()); + setReplyTo(getRefersTo() + ".bboard@" + + Forum.getConfig().getReplyHostName()); super.beforeSave(); @@ -239,8 +231,8 @@ public class Post extends ThreadedMessage { s_log.info("Setting context for " + getOID() + " to " + root.getOID()); PermissionService.setContext(this, root); - s_log.info( "Setting context for " + root.getOID() + " to " + - forum.getOID()); + s_log.info("Setting context for " + root.getOID() + " to " + + forum.getOID()); PermissionService.setContext(root, forum); // originally this was created in beforeSave, but this was when only // noticeboard (reply disabled) forums could have a lifecycle. Now that @@ -251,7 +243,7 @@ public class Post extends ThreadedMessage { if (m_wasNew) { if (getRoot() == null && forum.getExpireAfter() > 0) { s_log.info("Creating expiration lifecycle for " + getOID()); - setLifecycle(forum.getLifecycleDefinition()); + setLifecycle(forum.getLifecycleDefinition()); } } m_wasNew = false; @@ -263,7 +255,7 @@ public class Post extends ThreadedMessage { while (files.next()) { PostFileAttachment file = (PostFileAttachment) DomainObjectFactory.newInstance( - files.getDataObject()); + files.getDataObject()); if (getStatus().equals(APPROVED)) { file.setLive(); } else { @@ -277,15 +269,16 @@ public class Post extends ThreadedMessage { /** * Sends out the notifications for any subscriptions to the forum * or thread to which this message belongs. Only sends - * notifications if the post is approved. + * notifications if the post is approved. */ - public void sendNotifications(final String context) { + public void sendNotifications(final String context) { KernelExcursion ex = new KernelExcursion() { - protected void excurse() { - setEffectiveParty(Kernel.getSystemParty()); - doSendNotifications(context); - } - }; + + protected void excurse() { + setEffectiveParty(Kernel.getSystemParty()); + doSendNotifications(context); + } + }; ex.run(); } @@ -295,59 +288,58 @@ public class Post extends ThreadedMessage { */ public void sendModeratorAlerts() { - if (!getStatus().equals(APPROVED)) { - // don't send if pre-approved (ie posted by a moderator) - KernelExcursion ex = new KernelExcursion() { + if (!getStatus().equals(APPROVED)) { + // don't send if pre-approved (ie posted by a moderator) + KernelExcursion ex = new KernelExcursion() { + protected void excurse() { setEffectiveParty(Kernel.getSystemParty()); doSendModeratorAlerts(); } }; - ex.run(); - } else { - s_log.debug("not sending moderator alerts because the post " + - "was pre-approved (created by an approver)"); - } + ex.run(); + } else { + s_log.debug("not sending moderator alerts because the post " + + "was pre-approved (created by an approver)"); + } } - private void doSendNotifications(String context) { - s_log.debug("sending user notifications"); + private void doSendNotifications(String context) { + s_log.debug("sending user notifications"); Forum forum = getForum(); if (getStatus().equals(APPROVED)) { - s_log.debug("Sending forum level subsriptions"); + s_log.debug("Sending forum level subsriptions"); DataCollection subscriptions = forum.getSubscriptions(); while (subscriptions.next()) { - ForumSubscription subscription = (ForumSubscription) - DomainObjectFactory.newInstance( + ForumSubscription subscription = (ForumSubscription) DomainObjectFactory.newInstance( subscriptions.getDataObject()); s_log.debug("notification to " + subscription.getOID()); - subscription.sendNotification(Post.this, Forum.getConfig() - .deleteNotifications()); + subscription.sendNotification(Post.this, Forum.getConfig().deleteNotifications()); } - s_log.debug("Sending thread level subsriptions"); - if (context == null || !context.equals(PostForm.NEW_CONTEXT)) { + s_log.debug("Sending thread level subsriptions"); + if (context == null || !context.equals(PostForm.NEW_CONTEXT)) { - ThreadSubscription sub = - ThreadSubscription.getThreadSubscription(getThread()); - if (sub == null) { - s_log.error( - "Got a null ThreadSubscription from " - + "Post # " - + getID()); - } else { - sub.sendNotification(this, Forum.getConfig().deleteNotifications()); - } + ThreadSubscription sub = + ThreadSubscription.getThreadSubscription(getThread()); + if (sub == null) { + s_log.error( + "Got a null ThreadSubscription from " + + "Post # " + + getID()); + } else { + sub.sendNotification(this, Forum.getConfig().deleteNotifications()); + } } } else { - s_log.debug("Not sending notifications because the " + - "message is not approved"); - } + s_log.debug("Not sending notifications because the " + + "message is not approved"); } + } private void doSendModeratorAlerts() { if (s_log.isDebugEnabled()) { @@ -361,18 +353,16 @@ public class Post extends ThreadedMessage { DataCollection alerts = forum.getModerationAlerts(); while (alerts.next()) { - ModerationAlert alert - = (ModerationAlert) - DomainObjectFactory.newInstance( - alerts.getDataObject()); - s_log.debug("Processing moderation alert " + alert.getOID()); - alert.sendNotification(this, Forum.getConfig().deleteNotifications()); + ModerationAlert alert = (ModerationAlert) DomainObjectFactory.newInstance( + alerts.getDataObject()); + s_log.debug("Processing moderation alert " + alert.getOID()); + alert.sendNotification(this, Forum.getConfig().deleteNotifications()); } } else { - s_log.debug("Not sending moderator alerts because the " + - "forum is not moderated"); - } + s_log.debug("Not sending moderator alerts because the " + + "forum is not moderated"); } + } /** * Set the Forum that contains this post. Just a wrapper for the @@ -381,7 +371,6 @@ public class Post extends ThreadedMessage { * * @param forum the Forum that contains this post. */ - public void setForum(Forum forum) { setRefersTo(forum); } @@ -399,12 +388,10 @@ public class Post extends ThreadedMessage { * * @param category the Category for this post. */ - public void mapCategory(Category category) - throws PersistenceException { + throws PersistenceException { if (isNew()) { - throw new PersistenceException - ("Post must be persistent to map categories"); + throw new PersistenceException("Post must be persistent to map categories"); } category.addChild(this); category.save(); @@ -413,34 +400,33 @@ public class Post extends ThreadedMessage { /** * Clears categories for this post. Used when editing a post */ - public void clearCategories() { - DataCollection categories = - SessionManager.getSession().retrieve( - Category.BASE_DATA_OBJECT_TYPE); - categories.addEqualsFilter( - Category.CHILD_OBJECTS + "." + ACSObject.ID, - getID()); - while (categories.next()) { - Category cat = - (Category) DomainObjectFactory.newInstance( - categories.getDataObject()); - cat.removeChild(this); - } + DataCollection categories = + SessionManager.getSession().retrieve( + Category.BASE_DATA_OBJECT_TYPE); + categories.addEqualsFilter( + Category.CHILD_OBJECTS + "." + ACSObject.ID, + getID()); + while (categories.next()) { + Category cat = + (Category) DomainObjectFactory.newInstance( + categories.getDataObject()); + cat.removeChild(this); + } - // above is slower than data operation implementation below, - // but data op caused problems in persistence. If edited post - // had topic unchanged, then attempt was made to assign topic - // category before data op had cleared existing. Hence exception - // - attempt to map object to same cat twice + // above is slower than data operation implementation below, + // but data op caused problems in persistence. If edited post + // had topic unchanged, then attempt was made to assign topic + // category before data op had cleared existing. Hence exception + // - attempt to map object to same cat twice - /* - DataOperation clearCategories = - SessionManager.getSession().retrieveDataOperation( - "com.arsdigita.forum.clearCategories"); + /* + DataOperation clearCategories = + SessionManager.getSession().retrieveDataOperation( + "com.arsdigita.forum.clearCategories"); clearCategories.setParameter("postID", this.getID()); clearCategories.execute(); - return;*/ + return;*/ } /** @@ -451,41 +437,41 @@ public class Post extends ThreadedMessage { } /** - * creates a ThreadSubscription, and returns it but only if this is a root, - * else return null + * creates a ThreadSubscription, and returns it but only if this is a root, + * else return null * Note, you must save() the Post before calling this method. */ - public ThreadSubscription createThreadSubscription() { - ThreadSubscription sub = null; + public ThreadSubscription createThreadSubscription() { + ThreadSubscription sub = null; if (getRoot() == null) { - sub = new ThreadSubscription(); + sub = new ThreadSubscription(); sub.setThread(getThread()); sub.save(); } - return sub; - } + return sub; + } - public ThreadSubscription getSubscription() { - MessageThread thread; - if (getRoot() != null) { - thread = getRootMsg().getThread(); - } else { - thread = getThread(); - } - DataCollection subscriptions = - SessionManager.getSession().retrieve( - ThreadSubscription.BASE_DATA_OBJECT_TYPE); - subscriptions.addEqualsFilter( - ThreadSubscription.THREAD, - thread.getID()); - ThreadSubscription subscription = null; - while (subscriptions.next()) { - subscription = - (ThreadSubscription) DomainObjectFactory.newInstance( - subscriptions.getDataObject()); + public ThreadSubscription getSubscription() { + MessageThread thread; + if (getRoot() != null) { + thread = getRootMsg().getThread(); + } else { + thread = getThread(); + } + DataCollection subscriptions = + SessionManager.getSession().retrieve( + ThreadSubscription.BASE_DATA_OBJECT_TYPE); + subscriptions.addEqualsFilter( + ThreadSubscription.THREAD, + thread.getID()); + ThreadSubscription subscription = null; + while (subscriptions.next()) { + subscription = + (ThreadSubscription) DomainObjectFactory.newInstance( + subscriptions.getDataObject()); - } - return subscription; + } + return subscription; } /** @@ -498,64 +484,78 @@ public class Post extends ThreadedMessage { Party author = getFrom(); //determin sender / author of message // cg added - for anonymous posts, don't allow editing, else everyone // could edit everyone else's posts - return ( !author.equals(Kernel.getPublicUser()) - && Forum.getConfig().canAuthorEditPosts() - && author.equals(party) ) - || getForum().canEdit(party); + return (!author.equals(Kernel.getPublicUser()) + && Forum.getConfig().canAuthorEditPosts() + && author.equals(party)) + || getForum().canEdit(party); + } + + /** + * Determines if the User has permission to delete this Post. + * Note that you probably don't want to use this over and + * over for a list of messages because the permission check + * on the forum is not cached. + */ + public boolean canDelete(Party party) { + Party author = getFrom(); //determin sender / author of message + // cg added - for anonymous posts, don't allow editing, else everyone + // could edit everyone else's posts + return (!author.equals(Kernel.getPublicUser()) + && Forum.getConfig().canAuthorDeletePosts() + && author.equals(party)) + || getForum().canDelete(party); } public void setStatus(String status) { Assert.isTrue( - (status.equals(APPROVED) - || status.equals(REJECTED) - || status.equals(REAPPROVE) - || status.equals(SUPPRESSED) - || status.equals(PENDING) - ), - "The status must be one of " + APPROVED - + ", " + REJECTED - + ", " + REAPPROVE - + ", "+ SUPPRESSED - + ", the input was " + status - ); + (status.equals(APPROVED) + || status.equals(REJECTED) + || status.equals(REAPPROVE) + || status.equals(SUPPRESSED) + || status.equals(PENDING)), + "The status must be one of " + APPROVED + + ", " + REJECTED + + ", " + REAPPROVE + + ", " + SUPPRESSED + + ", the input was " + status); set(STATUS, status); } - /** - * set the status of a new post according to the priviliges of - * the current user - used by UI when creating new post or reply - * @param state - */ - public void setStatus(PageState state) { - setStatus(state, null); - } + /** + * set the status of a new post according to the priviliges of + * the current user - used by UI when creating new post or reply + * @param state + */ + public void setStatus(PageState state) { + setStatus(state, null); + } - /** - * set the status of an edited post according to the privileges - * of the current user and the status of the post that is being - * edited - used by the edit post UI - * @param state - * @param previousStatus - */ - public void setStatus(PageState state, String previousStatus) { - ForumContext ctx = ForumContext.getContext(state); - Forum forum = ctx.getForum(); - // set status of edited post - if (forum.isModerated() && !ctx.canModerate()) { - if (Post.APPROVED.equals(previousStatus)) { - setStatus(Post.REAPPROVE); - } else { - setStatus(Post.PENDING); - } - } else { - setStatus(Post.APPROVED); - } + /** + * set the status of an edited post according to the privileges + * of the current user and the status of the post that is being + * edited - used by the edit post UI + * @param state + * @param previousStatus + */ + public void setStatus(PageState state, String previousStatus) { + ForumContext ctx = ForumContext.getContext(state); + Forum forum = ctx.getForum(); + // set status of edited post + if (forum.isModerated() && !ctx.canModerate()) { + if (Post.APPROVED.equals(previousStatus)) { + setStatus(Post.REAPPROVE); + } else { + setStatus(Post.PENDING); + } + } else { + setStatus(Post.APPROVED); + } - } + } public String getStatus() { - return (String)get(STATUS); + return (String) get(STATUS); } public void setModerator(Party moderator) { @@ -566,26 +566,24 @@ public class Post extends ThreadedMessage { if (m_moderator == null) { DataObject moderatorData = (DataObject) get(MODERATOR); if (moderatorData != null) { - m_moderator = (Party) DomainObjectFactory.newInstance - (moderatorData); + m_moderator = (Party) DomainObjectFactory.newInstance(moderatorData); } } return m_moderator; } - // note that the replies to this post are deleted in beforeDelete() of - // ThreadedMessage (and hence beforeDelete is called recursively on their replies) - + // note that the replies to this post are deleted in beforeDelete() of + // ThreadedMessage (and hence beforeDelete is called recursively on their replies) protected void beforeDelete() { - s_log.debug("Post - before delete " + getID()); + s_log.debug("Post - before delete " + getID()); - // threaded message recursively deletes children - super.beforeDelete(); - // remove any nt_requests - DataCollection requests = - SessionManager.getSession().retrieve( - Notification.BASE_DATA_OBJECT_TYPE); - requests.addEqualsFilter(Notification.MESSAGE_ID, this.getID()); + // threaded message recursively deletes children + super.beforeDelete(); + // remove any nt_requests + DataCollection requests = + SessionManager.getSession().retrieve( + Notification.BASE_DATA_OBJECT_TYPE); + requests.addEqualsFilter(Notification.MESSAGE_ID, this.getID()); while (requests.next()) { Notification no = new Notification(requests.getDataObject().getOID()); no.setMessageDelete(Boolean.FALSE); @@ -593,23 +591,23 @@ public class Post extends ThreadedMessage { } if (getRoot() == null) { - s_log.debug( - "Root post - get rid of thread subscription and thread"); - // This posting is the root of the thread. Remove the thread subscription and thread - MessageThread thread = getThread(); - ThreadSubscription sub = - ThreadSubscription.getThreadSubscription(thread); - if (sub != null) { - // if unconfirmed post, then threadsubscription has not been created - sub.delete(); + s_log.debug( + "Root post - get rid of thread subscription and thread"); + // This posting is the root of the thread. Remove the thread subscription and thread + MessageThread thread = getThread(); + ThreadSubscription sub = + ThreadSubscription.getThreadSubscription(thread); + if (sub != null) { + // if unconfirmed post, then threadsubscription has not been created + sub.delete(); } - if (thread != null) { - thread.delete(); + if (thread != null) { + thread.delete(); + } } - } - } + } // package access only void setLifecycle(LifecycleDefinition life) { @@ -619,91 +617,86 @@ public class Post extends ThreadedMessage { cycle.save(); } - public void addImage(PostImageAttachment image) { - DataAssociation images = (DataAssociation) get(Post.IMAGE_ATTACHMENTS); - image.addToAssociation(images); - long currentImageCount = images.getDataAssociationCursor().size(); - image.setImageOrder((int) currentImageCount); - } + public void addImage(PostImageAttachment image) { + DataAssociation images = (DataAssociation) get(Post.IMAGE_ATTACHMENTS); + image.addToAssociation(images); + long currentImageCount = images.getDataAssociationCursor().size(); + image.setImageOrder((int) currentImageCount); + } - public void removeImage(PostImageAttachment image) { - DataAssociation images = (DataAssociation) get(Post.IMAGE_ATTACHMENTS); - image.removeFromAssociation(images); - renumberImages(); - } + public void removeImage(PostImageAttachment image) { + DataAssociation images = (DataAssociation) get(Post.IMAGE_ATTACHMENTS); + image.removeFromAssociation(images); + renumberImages(); + } - // image order for a new image is based on the count of existing - // images, hence necessary to fill in any gaps when images are deleted - private void renumberImages() { - int count = 1; - DataAssociationCursor images = getImages(); - while (images.next()) { - PostImageAttachment image = - (PostImageAttachment) DomainObjectFactory.newInstance( - images.getDataObject()); - image.setImageOrder(count); - count++; - } - } + // image order for a new image is based on the count of existing + // images, hence necessary to fill in any gaps when images are deleted + private void renumberImages() { + int count = 1; + DataAssociationCursor images = getImages(); + while (images.next()) { + PostImageAttachment image = + (PostImageAttachment) DomainObjectFactory.newInstance( + images.getDataObject()); + image.setImageOrder(count); + count++; + } + } - public DataAssociationCursor getImages() { - DataAssociationCursor images = - ((DataAssociation) get(Post.IMAGE_ATTACHMENTS)) - .getDataAssociationCursor(); - images.addOrder(PostImageAttachment.IMAGE_ORDER); - return images; - } + public DataAssociationCursor getImages() { + DataAssociationCursor images = + ((DataAssociation) get(Post.IMAGE_ATTACHMENTS)).getDataAssociationCursor(); + images.addOrder(PostImageAttachment.IMAGE_ORDER); + return images; + } - public void addFile(PostFileAttachment file) { - DataAssociation files = (DataAssociation) get(Post.FILE_ATTACHMENTS); - file.addToAssociation(files); - PermissionService.setContext(file, this); - long currentFileCount = files.getDataAssociationCursor().size(); - file.setFileOrder((int) currentFileCount); - } + public void addFile(PostFileAttachment file) { + DataAssociation files = (DataAssociation) get(Post.FILE_ATTACHMENTS); + file.addToAssociation(files); + PermissionService.setContext(file, this); + long currentFileCount = files.getDataAssociationCursor().size(); + file.setFileOrder((int) currentFileCount); + } - public void removeFile(PostFileAttachment file) { - DataAssociation files = (DataAssociation) get(Post.FILE_ATTACHMENTS); - file.removeFromAssociation(files); - renumberFiles(); + public void removeFile(PostFileAttachment file) { + DataAssociation files = (DataAssociation) get(Post.FILE_ATTACHMENTS); + file.removeFromAssociation(files); + renumberFiles(); - } + } - // file order for a new file is based on the count of existing - // files, hence necessary to fill in any gaps when images are deleted + // file order for a new file is based on the count of existing + // files, hence necessary to fill in any gaps when images are deleted + private void renumberFiles() { + int count = 1; + DataAssociationCursor files = getFiles(); + while (files.next()) { + PostFileAttachment file = + (PostFileAttachment) DomainObjectFactory.newInstance( + files.getDataObject()); + file.setFileOrder(count); + count++; + } - private void renumberFiles() { - int count = 1; - DataAssociationCursor files = getFiles(); - while (files.next()) { - PostFileAttachment file = - (PostFileAttachment) DomainObjectFactory.newInstance( - files.getDataObject()); - file.setFileOrder(count); - count++; - } + } - } + public DataAssociationCursor getFiles() { + DataAssociationCursor files = + ((DataAssociation) get(Post.FILE_ATTACHMENTS)).getDataAssociationCursor(); + files.addOrder(PostFileAttachment.FILE_ORDER); + return files; - public DataAssociationCursor getFiles() { - DataAssociationCursor files = - ((DataAssociation) get(Post.FILE_ATTACHMENTS)) - .getDataAssociationCursor(); - files.addOrder(PostFileAttachment.FILE_ORDER); - return files; + } - } - - - /** - * used by thread to prevent counting unapproved posts in the - * reply count. - * - */ - // should really be static - revisit this - refer to MessageThread for use - protected void addReplyFilter(DataCollection replies) { - replies.addEqualsFilter(STATUS, APPROVED); - - } + /** + * used by thread to prevent counting unapproved posts in the + * reply count. + * + */ + // should really be static - revisit this - refer to MessageThread for use + protected void addReplyFilter(DataCollection replies) { + replies.addEqualsFilter(STATUS, APPROVED); + } } diff --git a/ccm-forum/src/com/arsdigita/forum/PostFileAttachmentURLFinder.java b/ccm-forum/src/com/arsdigita/forum/PostFileAttachmentURLFinder.java index ca82640a9..283bec681 100644 --- a/ccm-forum/src/com/arsdigita/forum/PostFileAttachmentURLFinder.java +++ b/ccm-forum/src/com/arsdigita/forum/PostFileAttachmentURLFinder.java @@ -18,16 +18,14 @@ */ package com.arsdigita.forum; -import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.Asset; import com.arsdigita.cms.dispatcher.AssetURLFinder; import com.arsdigita.kernel.NoValidURLException; import com.arsdigita.kernel.URLFinder; -import com.arsdigita.kernel.URLService; -import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; -import com.arsdigita.persistence.SessionManager; -import com.arsdigita.util.Assert; +import com.arsdigita.web.Web; +import com.arsdigita.web.WebConfig; /** * @author chris.gilbert@westsussex.gov.uk @@ -36,32 +34,41 @@ import com.arsdigita.util.Assert; */ public class PostFileAttachmentURLFinder implements URLFinder { - private static final AssetURLFinder s_assetFinder = new AssetURLFinder(); + private static final AssetURLFinder s_assetFinder = new AssetURLFinder(); - /** - * - * find URL for a file attachment by finding its post - * - * @param oid the OID of the file attachment - * @param content the context of the search (ie draft/live) - */ - public String find(OID oid, String context) throws NoValidURLException { - // a draft attachment is one where the post hasn't been saved yet - // the behaviour is the same as far as finding the url goes - return find(oid); - - - } + /** + * + * find URL for a file attachment by finding its post + * + * @param oid the OID of the file attachment + * @param content the context of the search (ie draft/live) + */ + public String find(OID oid, String context) throws NoValidURLException { + // a draft attachment is one where the post hasn't been saved yet + // the behaviour is the same as far as finding the url goes + return find(oid); - /** - * - * find URL for the context of a file attachment. Delegates to - * AssetURLFinder. - * - * @param oid the OID of the file attachment - * - */ - public String find(OID oid) throws NoValidURLException { - return s_assetFinder.find(oid); - } + + } + + /** + * + * find URL for the context of a file attachment. Delegates to + * AssetURLFinder. + * + * @param oid the OID of the file attachment + * + */ + public String find(OID oid) throws NoValidURLException { + WebConfig config = Web.getConfig(); + + StringBuilder url = new StringBuilder(); + url.append(config.getDispatcherServletPath()); + url.append(config.getDispatcherContextPath()); + url.append("/cms-service/download/asset/?asset_id="); + url.append(oid.get(Asset.ID).toString()); + + return url.toString(); +// return s_assetFinder.find(oid); + } } diff --git a/ccm-forum/src/com/arsdigita/forum/ui/AttachedFilesStep.java b/ccm-forum/src/com/arsdigita/forum/ui/AttachedFilesStep.java index d8d15f130..a4dc24d64 100644 --- a/ccm-forum/src/com/arsdigita/forum/ui/AttachedFilesStep.java +++ b/ccm-forum/src/com/arsdigita/forum/ui/AttachedFilesStep.java @@ -356,7 +356,7 @@ public class AttachedFilesStep while(files.next()) { PostFileAttachment file = (PostFileAttachment) DomainObjectFactory.newInstance( files.getDataObject()); - DomainObjectXMLRenderer xr = new DomainObjectXMLRenderer(p.newChildElement(FORUM_XML_PREFIX + "files")); + DomainObjectXMLRenderer xr = new DomainObjectXMLRenderer(p.newChildElement(FORUM_XML_PREFIX + ":files", FORUM_XML_NS)); xr.setWrapRoot(false); xr.setWrapAttributes(true); xr.setWrapObjects(false); diff --git a/ccm-forum/src/com/arsdigita/forum/ui/Constants.java b/ccm-forum/src/com/arsdigita/forum/ui/Constants.java index 2ecb0a038..4d6854837 100755 --- a/ccm-forum/src/com/arsdigita/forum/ui/Constants.java +++ b/ccm-forum/src/com/arsdigita/forum/ui/Constants.java @@ -25,13 +25,13 @@ import java.math.BigDecimal; * XML namespaces, URLs, URL variable names, etc. * * @author Tracy Adams - * @version $Revision: 1.3 $ $Date: 2006/03/08 15:38:33 $ + * @version $Revision: 1.3 $ $Date: 2006/03/08 15:38:33 $ * @since ACS 4.7 */ public interface Constants { - static final String FORUM_XML_PREFIX = "forum"; + static final String FORUM_XML_PREFIX = "forum"; static final String FORUM_XML_NS = "http://www.arsdigita.com/forum/1.0"; static final String FORUM_MODE_VIEW = "view"; diff --git a/ccm-forum/src/com/arsdigita/forum/ui/DiscussionPostsList.java b/ccm-forum/src/com/arsdigita/forum/ui/DiscussionPostsList.java index f7a748d8f..a4b1be73d 100755 --- a/ccm-forum/src/com/arsdigita/forum/ui/DiscussionPostsList.java +++ b/ccm-forum/src/com/arsdigita/forum/ui/DiscussionPostsList.java @@ -68,18 +68,15 @@ import com.arsdigita.xml.XML; public class DiscussionPostsList extends SimpleComponent implements Constants { private static final Logger s_log = - Logger.getLogger(DiscussionPostsList.class); - + Logger.getLogger(DiscussionPostsList.class); private IntegerParameter m_pageNumber = - new IntegerParameter(PAGINATOR_PARAM); - private int m_pageSize = Forum.getConfig().getThreadPageSize(); - + new IntegerParameter(PAGINATOR_PARAM); + private int m_pageSize = Forum.getConfig().getThreadPageSize(); private static final String ACTION_EDIT = "edit"; private static final String ACTION_DELETE = "delete"; private static final String ACTION_REPLY = "reply"; private static final String ACTION_APPROVE = "approve"; private static final String ACTION_REJECT = "reject"; - private DiscussionThreadSimpleView m_threadMessagesPanel; private ACSObjectSelectionModel m_post; @@ -90,12 +87,11 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { * @param threadMessagesPanel */ public DiscussionPostsList(ACSObjectSelectionModel post, - DiscussionThreadSimpleView threadMessagesPanel) { + DiscussionThreadSimpleView threadMessagesPanel) { m_threadMessagesPanel = threadMessagesPanel; m_post = post; } - /** * * @param p @@ -112,56 +108,66 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { * @throws ServletException */ public void respond(PageState state) - throws ServletException { + throws ServletException { super.respond(state); String key = state.getControlEventName(); String value = state.getControlEventValue(); OID oid = new OID(Post.BASE_DATA_OBJECT_TYPE, - new BigDecimal(value)); + new BigDecimal(value)); ForumContext ctx = ForumContext.getContext(state); - Post post = (Post)DomainObjectFactory.newInstance(oid); + Post post = (Post) DomainObjectFactory.newInstance(oid); if (ACTION_EDIT.equals(key)) { m_post.setSelectedObject(state, post); m_threadMessagesPanel.makeEditFormVisible(state); } else if (ACTION_DELETE.equals(key)) { - Assert.isTrue(ctx.canAdminister(), "can administer forums"); +// Assert.isTrue(ctx.canDelete(post), "can administer forums"); MessageThread thread = ctx.getMessageThread(); ThreadedMessage root = thread.getRootMessage(); - if ( s_log.isDebugEnabled() ) { - s_log.debug("message: " + post.getOID() + - " root: " + root.getOID() + - " thread: " + thread.getOID()); + if (s_log.isDebugEnabled()) { + s_log.debug("message: " + post.getOID() + + " root: " + root.getOID() + + " thread: " + thread.getOID()); } - if ( ctx.getForum().isModerated() ) { - if ( !ctx.canModerate() ) { + if (ctx.getForum().isModerated()) { + if (!ctx.canModerate()) { + Assert.isTrue(ctx.canAdminister(), "can administer forums"); + post.setStatus(Post.SUPPRESSED); post.save(); } else if (post.equals(root)) { + Assert.isTrue(ctx.canDelete(post), "can delete posts"); + s_log.debug("Deleting entire thread"); post.delete(); Forum forum = ctx.getForum(); - URL url = URL.there(state.getRequest(), forum, null ); - throw new RedirectSignal( url, true ); + URL url = URL.there(state.getRequest(), forum, null); + throw new RedirectSignal(url, true); } else { + Assert.isTrue(ctx.canDelete(post), "can delete posts"); + s_log.debug("Deleting message"); post.delete(); } } else if (post.equals(root)) { + Assert.isTrue(ctx.canDelete(post), "can delete posts"); + s_log.debug("Deleting entire thread"); post.delete(); Forum forum = ctx.getForum(); - URL url = URL.there(state.getRequest(), forum, null ); - throw new RedirectSignal( url, true ); + URL url = URL.there(state.getRequest(), forum, null); + throw new RedirectSignal(url, true); } else { + Assert.isTrue(ctx.canDelete(post), "can delete posts"); + s_log.debug("Deleting message"); post.delete(); } @@ -179,9 +185,9 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { state.clearControlEvent(); try { - throw new RedirectSignal( state.stateAsURL(), true ); - } catch( IOException ex ) { - throw new UncheckedWrapperException( ex ); + throw new RedirectSignal(state.stateAsURL(), true); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); } } @@ -197,10 +203,9 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { Forum forum = context.getForum(); BigDecimal rootID = context.getMessageThread(). - getRootMessage().getID(); + getRootMessage().getID(); - DataCollection messages = SessionManager.getSession().retrieve - (Post.BASE_DATA_OBJECT_TYPE); + DataCollection messages = SessionManager.getSession().retrieve(Post.BASE_DATA_OBJECT_TYPE); // Hide replies if we're in noticeboard mode if (forum.isNoticeboard()) { @@ -210,21 +215,14 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { FilterFactory ff = messages.getFilterFactory(); messages.addFilter( - ff.or() - .addFilter(ff.and() - .addFilter(ff.equals("root", null)) - .addFilter(ff.equals("id", rootID))) - .addFilter(ff.equals("root", rootID))); + ff.or().addFilter(ff.and().addFilter(ff.equals("root", null)).addFilter(ff.equals("id", rootID))).addFilter(ff.equals("root", rootID))); messages.addOrderWithNull("sortKey", "---", true); // Add a filter to only show approved messages if (forum.isModerated() && !forum.canModerate(party)) { - messages.addFilter(ff.or() - .addFilter(ff.equals("status", Post.APPROVED)) - .addFilter(ff.equals("sender.id", party == null ? - null : party.getID())) - ); + messages.addFilter(ff.or().addFilter(ff.equals("status", Post.APPROVED)).addFilter(ff.equals("sender.id", party == null + ? null : party.getID()))); } return new DomainCollection(messages); @@ -237,22 +235,21 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { */ @Override public void generateXML(PageState state, - Element parent) { - Element content = parent.newChildElement(FORUM_XML_PREFIX + - ":threadDisplay", - FORUM_XML_NS); + Element parent) { + Element content = parent.newChildElement(FORUM_XML_PREFIX + + ":threadDisplay", + FORUM_XML_NS); exportAttributes(content); Forum forum = ForumContext.getContext(state).getForum(); content.addAttribute("forumTitle", forum.getTitle()); - content.addAttribute("noticeboard", (new Boolean(forum.isNoticeboard())). - toString()); + content.addAttribute("noticeboard", (new Boolean(forum.isNoticeboard())).toString()); DomainCollection messages = getMessages(state); - Integer page = (Integer)state.getValue(m_pageNumber); + Integer page = (Integer) state.getValue(m_pageNumber); int pageNumber = (page == null ? 1 : page.intValue()); long objectCount = messages.size(); - int pageCount = (int)Math.ceil((double)objectCount / (double)m_pageSize); + int pageCount = (int) Math.ceil((double) objectCount / (double) m_pageSize); if (pageNumber < 1) { pageNumber = 1; @@ -262,29 +259,30 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { pageNumber = (pageCount == 0 ? 1 : pageCount); } - long begin = ((pageNumber-1) * m_pageSize); - int count = (int)Math.min(m_pageSize, (objectCount - begin)); + long begin = ((pageNumber - 1) * m_pageSize); + int count = (int) Math.min(m_pageSize, (objectCount - begin)); long end = begin + count; generatePaginatorXML(content, - pageNumber, - pageCount, - m_pageSize, - begin, - end, - objectCount); + pageNumber, + pageCount, + m_pageSize, + begin, + end, + objectCount); if (begin != 0 || end != 0) { - messages.setRange(new Integer((int)begin+1), - new Integer((int)end+1)); + messages.setRange(new Integer((int) begin + 1), + new Integer((int) end + 1)); } while (messages.next()) { - Post message = (Post)messages.getDomainObject(); + Post message = (Post) messages.getDomainObject(); Element messageEl = content.newChildElement(FORUM_XML_PREFIX + ":message", - FORUM_XML_NS); + FORUM_XML_NS); - generateActionXML(state, messageEl, message); + generateActionXML(state, messageEl, message, + (messages.getPosition() == messages.size()) ? true : false ); DomainObjectXMLRenderer xr = new DomainObjectXMLRenderer(messageEl); xr.setWrapRoot(false); @@ -299,60 +297,62 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { /** * * @param state - * @param parent - * @param post + * @param parent Parent XML element to add any additional elements + * @param post Current post to generate action links for + * @param isLast Post is last in list */ protected void generateActionXML(PageState state, - Element parent, - Post post) { + Element parent, + Post post, + boolean isLast) { ForumContext ctx = ForumContext.getContext(state); - String status = post.getStatus(); - if (ctx.canModerate()) { - if (!status.equals(Post.REJECTED) && - !status.equals(Post.SUPPRESSED) ) { - parent.addAttribute("rejectURL", - makeURL(state, ACTION_REJECT, post)); - } - } - - if (ctx.canModerate() && - !post.getStatus().equals(post.APPROVED)) { - parent.addAttribute("approveURL", - makeURL(state, ACTION_APPROVE, post)); - } - - if (ctx.canAdminister()) { - parent.addAttribute("deleteURL", - makeURL(state, ACTION_DELETE, post)); - } - - Party party = Kernel.getContext().getParty(); if (party == null) { - party = Kernel.getPublicUser(); - } - if (post.canEdit(party)) { - parent.addAttribute("editURL", - makeURL(state, ACTION_EDIT, post)); + party = Kernel.getPublicUser(); } - PermissionDescriptor canRespond = new PermissionDescriptor( - PrivilegeDescriptor.get(Forum.RESPOND_TO_THREAD_PRIVILEGE), - Kernel.getContext().getResource(), party); - - if (!ctx.getForum().isNoticeboard() && PermissionService. - checkPermission(canRespond)) { + String status = post.getStatus(); + if (ctx.canModerate()) { + if (!status.equals(Post.REJECTED) + && !status.equals(Post.SUPPRESSED)) { + parent.addAttribute("rejectURL", + makeURL(state, ACTION_REJECT, post)); + } + } + + if (ctx.canModerate() + && !post.getStatus().equals(post.APPROVED)) { + parent.addAttribute("approveURL", + makeURL(state, ACTION_APPROVE, post)); + } + + if (ctx.canAdminister() || (post.canDelete(party) && isLast)) { + parent.addAttribute("deleteURL", + makeURL(state, ACTION_DELETE, post)); + } + + + if (post.canEdit(party)) { + parent.addAttribute("editURL", + makeURL(state, ACTION_EDIT, post)); + } + + PermissionDescriptor canRespond = new PermissionDescriptor( + PrivilegeDescriptor.get(Forum.RESPOND_TO_THREAD_PRIVILEGE), + Kernel.getContext().getResource(), party); + + if (!ctx.getForum().isNoticeboard() && PermissionService.checkPermission(canRespond)) { parent.addAttribute("replyURL", - makeURL(state, ACTION_REPLY, post)); + makeURL(state, ACTION_REPLY, post)); } } protected String makeURL(PageState state, - String action, - Post post) { - state.setControlEvent(this, action,post.getID().toString()); + String action, + Post post) { + state.setControlEvent(this, action, post.getID().toString()); String url = null; try { @@ -365,35 +365,34 @@ public class DiscussionPostsList extends SimpleComponent implements Constants { } protected void generatePaginatorXML(Element parent, - int pageNumber, - int pageCount, - int pageSize, - long begin, - long end, - long objectCount) { - Element paginator = parent.newChildElement(FORUM_XML_PREFIX + - ":paginator", FORUM_XML_NS); + int pageNumber, + int pageCount, + int pageSize, + long begin, + long end, + long objectCount) { + Element paginator = parent.newChildElement(FORUM_XML_PREFIX + + ":paginator", FORUM_XML_NS); URL here = Web.getContext().getRequestURL(); ParameterMap params = new ParameterMap(here.getParameterMap()); params.clearParameter(PAGINATOR_PARAM); URL url = new URL(here.getScheme(), - here.getServerName(), - here.getServerPort(), - here.getContextPath(), - here.getServletPath(), - here.getPathInfo(), - params); + here.getServerName(), + here.getServerPort(), + here.getContextPath(), + here.getServletPath(), + here.getPathInfo(), + params); paginator.addAttribute("param", PAGINATOR_PARAM); paginator.addAttribute("baseURL", XML.format(url)); paginator.addAttribute("pageNumber", XML.format(new Integer(pageNumber))); paginator.addAttribute("pageCount", XML.format(new Integer(pageCount))); paginator.addAttribute("pageSize", XML.format(new Integer(pageSize))); - paginator.addAttribute("objectBegin", XML.format(new Long(begin+1))); + paginator.addAttribute("objectBegin", XML.format(new Long(begin + 1))); paginator.addAttribute("objectEnd", XML.format(new Long(end))); paginator.addAttribute("objectCount", XML.format(new Long(objectCount))); } - }