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