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