      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