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