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