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       return;
 43    }
 44    while (this.size() > 0) {
 45       Choice.remove.call(this.get(0));
 46    }
 47    this.remove();
 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");
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('Results of poll "{0}"', this.question);
208    res.data.body = this.renderSkinAsString("$Poll#results");
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(this.href());
222    //return res.redirect(this._parent.href() + "#" + this._id);
223 }
224 
225 /**
226  * 
227  * @param {Object} param
228  * @param {String} action
229  * @param {String} text
230  * @see HopObject#link_macro
231  */
232 Poll.prototype.link_macro = function(param, action, text) {
233    switch (action) {
234       case ".":
235       case "main":
236       if (this.status === Poll.CLOSED) {
237          return;
238       }
239       break;
240       case "rotate":
241       if (this.status === Poll.OPEN) {
242          text = gettext("Close");
243       } else {
244          text = this.closed ? gettext("Re-open") : gettext("Open");  
245       }
246       break;
247   }
248    return HopObject.prototype.link_macro.call(this, param, action, text);
249 }
250 
251 /**
252  * 
253  * @param {Object} param
254  * @param {String} name
255  * @see HopObject#link_macro
256  */
257 Poll.prototype.input_macro = function(param, name) {
258    switch (name) {
259       case "choices":
260       var index = 0;
261       var add = function(choice) {
262          index += 1;
263          return choice.renderSkin("$Choice#edit", {index: index});
264       };
265       var choices;
266       if (choices = req.postParams.title_array) {
267          while (choices.length < 2) {
268             choices.push(null);
269          }
270          choices.forEach(function(title) {
271             return add(new Choice(title));
272          });
273       } else {
274          this.forEach(function() {
275             return add(this);
276          });
277       }
278       return;
279    }
280    return HopObject.prototype.input_macro.apply(this, arguments);
281 }
282 
283 /**
284  * 
285  */
286 Poll.prototype.votes_macro = function() {
287    return this.votes.size();
288 }
289 
290 /**
291  * 
292  * @param {Object} param
293  * @param {String} format
294  */
295 Poll.prototype.closed_macro = function(param, format) {
296    if (this.status === Poll.CLOSED) {
297       res.write(formatDate(this.closed, param.format || format));
298    }
299    return;
300 }
301