1 //
  2 // The Antville Project
  3 // http://code.google.com/p/antville
  4 //
  5 // Copyright 2001-2007 by The Antville People
  6 //
  7 // Licensed under the Apache License, Version 2.0 (the ``License'');
  8 // you may not use this file except in compliance with the License.
  9 // You may obtain a copy of the License at
 10 //
 11 //    http://www.apache.org/licenses/LICENSE-2.0
 12 //
 13 // Unless required by applicable law or agreed to in writing, software
 14 // distributed under the License is distributed on an ``AS IS'' BASIS,
 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16 // See the License for the specific language governing permissions and
 17 // limitations under the License.
 18 //
 19 // $Revision$
 20 // $LastChangedBy$
 21 // $LastChangedDate$
 22 // $URL$
 23 //
 24 
 25 /**
 26  * @fileOverview Defines the Comment prototype.
 27  */
 28 
 29 markgettext("Comment");
 30 markgettext("comment");
 31 
 32 /**
 33  * @see defineConstants
 34  */
 35 Comment.getStatus = defineConstants(Comment, markgettext("deleted"), 
 36       markgettext("pending"), markgettext("readonly"), markgettext("public"));
 37 
 38 /**
 39  * @returns {String}
 40  */
 41 Comment.remove = function(options) {
 42    if (this.constructor !== Comment) {
 43       return;
 44    }
 45    if (options && options.mode === "user" && options.confirm === "1") {
 46       var membership = Membership.getByName(this.creator.name, this.site);
 47       HopObject.remove.call(membership.comments);
 48    } else {
 49       while (this.size() > 0) {
 50          Comment.remove.call(this.get(0));
 51       }
 52       // Explicitely remove comment from aggressively cached collections:
 53       (this.parent || this).removeChild(this);
 54       this.story.comments.removeChild(this);
 55       this.remove();
 56    }
 57    return this.parent.href();
 58 }
 59 
 60 /**
 61  * @name Comment
 62  * @constructor
 63  * @param {Object} parent
 64  * @property {Comment[]} _children
 65  * @property {String} name
 66  * @property {Story|Comment} parent
 67  * @property {Story} story
 68  * @extends Story
 69  */
 70 Comment.prototype.constructor = function(parent) {
 71    this.name = String.EMPTY;
 72    this.site = parent.site;
 73    this.story = parent.story || parent;
 74    this.parent = parent;
 75    // FIXME: Correct parent_type (Helma bug?)
 76    this.parent_type = parent._prototype;
 77    this.status = Story.PUBLIC;
 78    this.creator = this.modifier = session.user;
 79    this.created = this.modified = new Date;
 80    return this;
 81 }
 82 
 83 /**
 84  * 
 85  * @param {Object} action
 86  * @returns {Boolean}
 87  */
 88 Comment.prototype.getPermission = function(action) {
 89    switch (action) {
 90       case ".":
 91       case "main":
 92       case "comment":
 93       return this.site.commentMode === Site.ENABLED &&
 94             this.story.getPermission(action) && 
 95             this.status !== Comment.PENDING;
 96       case "delete":
 97       return this.story.getPermission.call(this, "delete");
 98       case "edit":
 99       case "rotate":
100       if (this.status === Comment.DELETED) {
101          if (this.creator !== this.modifier) {
102             return User.require(User.PRIVILEGED);
103          } else {
104             return session.user === this.creator;
105          }
106       }
107       return this.story.getPermission.call(this, "delete");
108    }
109    return false;
110 }
111 
112 /**
113  * 
114  * @param {Object} action
115  * @returns {String}
116  */
117 Comment.prototype.href = function(action) {
118    var buffer = [];
119    switch (action) {
120       case null:
121       case undefined:
122       case "":
123       case ".":
124       case "main":
125       buffer.push(this.story.href(), "#", this._id);
126       break;
127       default:
128       buffer.push(this.story.comments.href(), this._id, "/", action);
129    }
130    return buffer.join(String.EMPTY);
131 }
132 
133 Comment.prototype.edit_action = function() {
134    if (req.postParams.save) {
135       try {
136          this.update(req.postParams);
137          delete session.data.backup;
138          res.message = gettext("The comment was successfully updated.");;
139          res.redirect(this.story.href() + "#" + this._id);
140       } catch (ex) {
141          res.message = ex;
142          app.log(ex);
143       }
144    }
145    
146    res.handlers.parent = this.parent;
147    res.data.action = this.href(req.action);
148    res.data.title = gettext("Edit Comment");
149    res.data.body = this.renderSkinAsString("Comment#edit");
150    this.site.renderSkin("Site#page");
151    return;
152 }
153 
154 /**
155  * 
156  * @param {Object} data
157  */
158 Comment.prototype.update = function(data) {
159    if (!data.title && !data.text) {
160       throw Error(gettext("Please enter at least something into the “title” or “text” field."));
161    }
162    // Get difference to current content before applying changes
163    var delta = this.getDelta(data);
164    this.title = data.title;
165    this.text = data.text;
166    this.setMetadata(data);
167 
168    if (this.story.commentMode === Story.MODERATED) {
169       this.status = Comment.PENDING;
170    } else if (delta > 50) {
171       this.modified = new Date;
172       if (this.story.status !== Story.CLOSED) { 
173          this.site.modified = this.modified;
174       }
175       // We need persistence for adding the callback
176       this.isTransient() && this.persist();
177       res.handlers.site.callback(this);
178       // Notification is sent in Story.comment_action()
179    }
180    this.clearCache();
181    this.modifier = session.user;
182    return;
183 }
184 
185 /**
186  * @returns {String}
187  */
188 Comment.prototype.getConfirmText = function() {
189    return gettext("You are about to delete a comment by user {0}.", 
190          this.creator.name);
191 }
192 
193 /**
194  * 
195  * @param {String} name
196  * @returns {HopObject} 
197  */
198 Comment.prototype.getMacroHandler = function(name) {
199    if (name === "related") {
200       var membership = Membership.getByName(this.creator.name, this.site);
201       if (!membership) {
202          return {}; // Work-around for issue 88
203       }
204       return membership.comments;
205    }
206    return null;
207 }
208 
209 /**
210  * 
211  */
212 Comment.prototype.text_macro = function() {
213    if (this.status === Comment.DELETED) {
214       res.write("<em>");
215       res.write(this.modifier === this.creator ? 
216             gettext("This comment was removed by the author.") : 
217             gettext("This comment was removed."));
218       res.writeln("</em>");
219    } else {
220       res.write(this.text);
221    }
222    return;
223 }
224 
225 /**
226  * 
227  */
228 Comment.prototype.title_macro = function() {
229    if (this.status !== Comment.DELETED) {
230       res.write(this.title);
231    }
232    return;
233 }
234 
235 /**
236  * 
237  * @param {Object} param
238  * @param {Object} action
239  * @param {Object} text
240  */
241 Comment.prototype.link_macro = function(param, action, text) {
242    switch (action) {
243       case "rotate":
244       if (this.status === Comment.DELETED) {
245          text = gettext("Show");
246       } else {
247          text = gettext("Hide");
248       }
249    }
250    return HopObject.prototype.link_macro.call(this, param, action, text);
251 }
252 
253 Comment.prototype.rotate_action = function() {
254    this.status = this.status === Comment.DELETED ? 
255          Comment.PUBLIC : Comment.DELETED;
256    this.touch();
257    return res.redirect(this.href());
258 }
259