1 //
  2 // The Antville Project
  3 // http://code.google.com/p/antville
  4 //
  5 // Copyright 2001-2007 by The Antville People
  6 //
  7 // Licensed under the Apache License, Version 2.0 (the ``License'');
  8 // you may not use this file except in compliance with the License.
  9 // You may obtain a copy of the License at
 10 //
 11 //    http://www.apache.org/licenses/LICENSE-2.0
 12 //
 13 // Unless required by applicable law or agreed to in writing, software
 14 // distributed under the License is distributed on an ``AS IS'' BASIS,
 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16 // See the License for the specific language governing permissions and
 17 // limitations under the License.
 18 //
 19 // $Revision$
 20 // $LastChangedBy$
 21 // $LastChangedDate$
 22 // $URL$
 23 //
 24 
 25 /**
 26  * @fileOverview Defines the Poll prototype
 27  */
 28 
 29 markgettext("Poll");
 30 markgettext("poll");
 31 
 32 /**
 33  * @function
 34  * @param {String} ctor
 35  * @returns {String[]}
 36  * @see defineConstants
 37  */
 38 Poll.getStatus = defineConstants(Poll, markgettext("closed"), markgettext("open"));
 39 
 40 /**
 41  * 
 42  */
 43 Poll.remove = function() {
 44    if (this.constructor === Poll) {
 45       HopObject.remove.call(this);
 46       this.remove();
 47    }
 48    return;
 49 }
 50 
 51 /**
 52  * @name Poll
 53  * @constructor
 54  * @param {String} question
 55  * @property {Choice[]} _children
 56  * @property {String} closed
 57  * @property {Date} created
 58  * @property {User} creator
 59  * @property {Date} modified
 60  * @property {User} modifier
 61  * @property {String} question
 62  * @property {Site} site
 63  * @property {String} status
 64  * @property {Vote[]} votes
 65  * @extends HopObject
 66  */
 67 Poll.prototype.constructor = function(question) {
 68    this.question = question;
 69    this.creator = this.modifier = session.user;
 70    this.created = this.modified = new Date;
 71    return this;
 72 }
 73 
 74 /**
 75  * 
 76  * @param {String} action
 77  * @returns {Boolean}
 78  */
 79 Poll.prototype.getPermission = function(action) {
 80    if (!this.site.getPermission("main")) {
 81       return false;
 82    }
 83    switch (action) {
 84       case ".":
 85       case "main":
 86       return !!session.user;
 87       case "result":
 88       return true;
 89       case "edit":
 90       return this.status === Poll.CLOSED ||
 91             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    data.title_array = choices;
186    if (choices.length < 2 || !data.question) {
187       throw Error(gettext("Please fill out the whole form to create a valid poll."));
188    }
189    while (this.size() > 0) {
190       Choice.remove.call(this.get(0));
191    }
192    for (var i=0; i<choices.length; i+=1) {
193       this.add(new Choice(choices[i]));
194    }
195    if (data.save !== Poll.CLOSED) {
196       delete this.closed;
197    } else if (this.status) {
198       this.closed = new Date;
199    }
200    this.status = data.save;
201    this.question = data.question;
202    this.touch();
203    return;
204 }
205 
206 Poll.prototype.result_action = function() {
207    res.data.title = gettext('Poll Results: {0}', this.question);
208    res.data.body = this.renderSkinAsString("$Poll#results", {header: true});
209    this.site.renderSkin("Site#page");
210    return;
211 }
212 
213 Poll.prototype.rotate_action = function() {
214    if (this.status === Poll.CLOSED) {
215       this.status = Poll.OPEN;
216    } else if (this.status === Poll.OPEN) {
217       this.status = Poll.CLOSED;
218       this.closed = new Date;
219    }
220    this.touch();
221    return res.redirect(req.data.http_referer);
222 }
223 
224 /**
225  * @returns {String}
226  */
227 Poll.prototype.getConfirmText = function() {
228    return gettext("You are about to delete a poll by user {0}.", 
229          this.creator.name);
230 }
231 
232 /**
233  * 
234  * @param {Object} param
235  * @param {String} action
236  * @param {String} text
237  * @see HopObject#link_macro
238  */
239 Poll.prototype.link_macro = function(param, action, text) {
240    switch (action) {
241       case ".":
242       case "main":
243       if (this.status === Poll.CLOSED) {
244          return;
245       }
246       break;
247       case "rotate":
248       if (this.status === Poll.OPEN) {
249          text = gettext("Stop");
250       } else {
251          text = this.closed ? gettext("Re-run") : gettext("Run");  
252       }
253       break;
254   }
255    return HopObject.prototype.link_macro.call(this, param, action, text);
256 }
257 
258 /**
259  * 
260  * @param {Object} param
261  * @param {String} name
262  * @see HopObject#link_macro
263  */
264 Poll.prototype.input_macro = function(param, name) {
265    switch (name) {
266       case "choices":
267       var index = 0;
268       var add = function(choice) {
269          index += 1;
270          return choice.renderSkin("$Choice#edit", {index: index});
271       };
272       var choices;
273       if (choices = req.postParams.title_array) {
274          while (choices.length < 2) {
275             choices.push(null);
276          }
277          choices.forEach(function(title) {
278             return add(new Choice(title));
279          });
280       } else {
281          this.forEach(function() {
282             return add(this);
283          });
284       }
285       return;
286    }
287    return HopObject.prototype.input_macro.apply(this, arguments);
288 }
289 
290 /**
291  * 
292  */
293 Poll.prototype.votes_macro = function() {
294    return this.votes.size();
295 }
296 
297 /**
298  * 
299  * @param {Object} param
300  * @param {String} format
301  */
302 Poll.prototype.closed_macro = function(param, format) {
303    if (this.status === Poll.CLOSED) {
304       res.write(formatDate(this.closed, param.format || format));
305    }
306    return;
307 }
308