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