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