      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 Poll prototype
     28  */
     29
     30 markgettext("Poll");
     31 markgettext("poll");
     32
     33 /**
     34  * @function
     35  * @param {String} ctor
     36  * @returns {String[]}
     37  * @see defineConstants
     38  */
     39 Poll.getStatus = defineConstants(Poll, markgettext("closed"), markgettext("open"));
     40
     41 /**
     42  *
     43  */
     44 Poll.remove = function() {
     45    if (this.constructor === Poll) {
     46       HopObject.remove.call(this);
     47       this.remove();
     48    }
     49    return;
     50 }
     51
     52 /**
     53  * @name Poll
     54  * @constructor
     55  * @param {String} question
     56  * @property {Choice[]} _children
     57  * @property {String} closed
     58  * @property {Date} created
     59  * @property {User} creator
     60  * @property {Date} modified
     61  * @property {User} modifier
     62  * @property {String} question
     63  * @property {Site} site
     64  * @property {String} status
     65  * @property {Vote[]} votes
     66  * @extends HopObject
     67  */
     68 Poll.prototype.constructor = function(question) {
     69    this.question = question;
     70    this.creator = this.modifier = session.user;
     71    this.created = this.modified = new Date;
     72    return this;
     73 }
     74
     75 /**
     76  *
     77  * @param {String} action
     78  * @returns {Boolean}
     79  */
     80 Poll.prototype.getPermission = function(action) {
     81    if (!this.site.getPermission("main")) {
     82       return false;
     83    }
     84    switch (action) {
     85       case ".":
     86       case "main":
     87       return !!session.user;
     88       case "result":
     89       return true;
     90       case "edit":
     91       return Membership.require(Membership.OWNER) ||
     92             User.require(User.PRIVILEGED);
     93       case "rotate":
     94       case "delete":
     95       return this.creator === session.user ||
     96             Membership.require(Membership.MANAGER) ||
     97             User.require(User.PRIVILEGED);
     98    }
     99    return false;
    100 }
    101
    102 /**
    103  *
    104  * @param {String} name
    105  * @returns {Object}
    106  */
    107 Poll.prototype.getFormOptions = function(name) {
    108    switch (name) {
    109       case "status":
    110       return Poll.getStatus();
    111    }
    112    return;
    113 }
    114
    115 Poll.prototype.main_action = function() {
    116    if (this.status !== Poll.OPEN) {
    117       res.redirect(this.href("result"));
    118       return;
    119    }
    120    if (req.postParams.vote) {
    121       try {
    122          this.vote(req.postParams);
    123          res.message = gettext("Thanks, your vote was registered. You can change your mind until the poll is closed.");
    124          res.redirect(this.href("result"));
    125       } catch (ex) {
    126          res.message = ex;
    127          app.log(ex);
    128       }
    129    }
    130    res.data.action = this.href();
    131    res.data.title = gettext("Poll: {0}", this.question);
    132    res.data.body = this.renderSkinAsString("$Poll#main", {header: true});
    133    this.site.renderSkin("Site#page");
    134    return;
    135 }
    136
    137 /**
    138  *
    139  * @param {Object} data
    140  */
    141 Poll.prototype.vote = function(data) {
    142 	if (!data.choice) {
    143 	   throw Error(gettext("You did not vote, yet. You can vote until the poll is closed."));
    144 	}
    145 	var choice = this.get(data.choice);
    146 	var vote = session.user && this.votes.get(session.user.name);
    147 	if (vote) {
    148 		vote.choice = choice;
    149 		vote.modified = new Date;
    150 	} else {
    151 		this.votes.add(new Vote(choice));
    152 	}
    153 	return;
    154 }
    155
    156 Poll.prototype.edit_action = function() {
    157    if (req.postParams.save) {
    158       try {
    159          this.update(req.postParams);
    160          res.message = gettext("The poll was updated successfully.");
    161          res.redirect(this.href());
    162       } catch (ex) {
    163          res.message = ex;
    164          app.log(ex);
    165       }
    166    }
    167    res.data.action = this.href(req.action);
    168    res.data.title = gettext("Edit Poll: {0}", this.question);
    169    res.data.body = this.renderSkinAsString("$Poll#edit");
    170    this.site.renderSkin("Site#page");
    171    return;
    172 }
    173
    174 /**
    175  *
    176  * @param {Object} data
    177  */
    178 Poll.prototype.update = function(data) {
    179    var choices = [];
    180    for each (var title in data.title_array) {
    181       if (title = title.trim()) {
    182          choices.push(title);
    183       }
    184    }
    185    if (choices.length < 2 || !data.question) {
    186       throw Error(gettext("Please fill out the whole form to create a valid poll."));
    187    }
    188    var size = this.size();
    189    // Update or remove choices
    190    for (var i=size-1; i>-1; i-=1) {
    191       var choice = this.get(i);
    192       var title = choices[i];
    193       if (title) {
    194          choice.title = title;
    195          choice.touch();
    196       } else {
    197          Choice.remove.call(choice);
    198       }
    199    }
    200    // Add new choices
    201    for (var i=size; i<choices.length; i+=1) {
    202       this.add(new Choice(choices[i]));
    203    }
    204    if (data.save !== Poll.CLOSED) {
    205       delete this.closed;
    206    } else if (this.status === Poll.OPEN) {
    207       this.closed = new Date;
    208    }
    209    this.status = data.save;
    210    this.question = data.question;
    211    this.touch();
    212    return;
    213 }
    214
    215 Poll.prototype.result_action = function() {
    216    res.data.title = gettext('Poll Results: {0}', this.question);
    217    res.data.body = this.renderSkinAsString("$Poll#results", {header: true});
    218    this.site.renderSkin("Site#page");
    219    return;
    220 }
    221
    222 Poll.prototype.rotate_action = function() {
    223    if (this.status === Poll.CLOSED) {
    224       this.status = Poll.OPEN;
    225    } else if (this.status === Poll.OPEN) {
    226       this.status = Poll.CLOSED;
    227       this.closed = new Date;
    228    }
    229    this.touch();
    230    return res.redirect(this.href());
    231 }
    232
    233 /**
    234  * @returns {String}
    235  */
    236 Poll.prototype.getConfirmText = function() {
    237    return gettext("You are about to delete a poll by user {0}.",
    238          this.creator.name);
    239 }
    240
    241 /**
    242  *
    243  * @param {Object} param
    244  * @param {String} action
    245  * @param {String} text
    246  * @see HopObject#link_macro
    247  */
    248 Poll.prototype.link_macro = function(param, action, text) {
    249    switch (action) {
    250       case ".":
    251       case "main":
    252       if (this.status === Poll.CLOSED) {
    253          return;
    254       }
    255       break;
    256       case "rotate":
    257       if (this.status === Poll.OPEN) {
    258          text = gettext("Stop");
    259       } else {
    260          text = this.closed ? gettext("Re-run") : gettext("Run");
    261       }
    262       break;
    263   }
    264    return HopObject.prototype.link_macro.call(this, param, action, text);
    265 }
    266
    267 /**
    268  *
    269  * @param {Object} param
    270  * @param {String} name
    271  * @see HopObject#link_macro
    272  */
    273 Poll.prototype.input_macro = function(param, name) {
    274    switch (name) {
    275       case "choices":
    276       var index = 0;
    277       var add = function(choice) {
    278          index += 1;
    279          return choice.renderSkin("$Choice#edit", {index: index});
    280       };
    281       var choices;
    282       if (choices = req.postParams.title_array) {
    283          while (choices.length < 2) {
    284             choices.push(null);
    285          }
    286          choices.forEach(function(title) {
    287             return add(new Choice(title));
    288          });
    289       } else {
    290          this.forEach(function() {
    291             return add(this);
    292          });
    293       }
    294       return;
    295    }
    296    return HopObject.prototype.input_macro.apply(this, arguments);
    297 }
    298
    299 /**
    300  *
    301  */
    302 Poll.prototype.votes_macro = function() {
    303    return this.votes.size();
    304 }
    305
    306 /**
    307  *
    308  * @param {Object} param
    309  * @param {String} format
    310  */
    311 Poll.prototype.closed_macro = function(param, format) {
    312    if (this.status === Poll.CLOSED) {
    313       res.write(formatDate(this.closed, param.format || format));
    314    }
    315    return;
    316 }
    317