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