1 // The Antville Project 2 // http://code.google.com/p/antville 3 // 4 // Copyright 2007-2011 by Tobi Schäfer. 5 // 6 // Copyright 2001–2007 Robert Gaggl, Hannes Wallnöfer, Tobi Schäfer, 7 // Matthias & Michael Platzer, Christoph Lincke. 8 // 9 // Licensed under the Apache License, Version 2.0 (the ``License''); 10 // you may not use this file except in compliance with the License. 11 // You may obtain a copy of the License at 12 // 13 // http://www.apache.org/licenses/LICENSE-2.0 14 // 15 // Unless required by applicable law or agreed to in writing, software 16 // distributed under the License is distributed on an ``AS IS'' BASIS, 17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 // See the License for the specific language governing permissions and 19 // limitations under the License. 20 // 21 // $Revision$ 22 // $LastChangedBy$ 23 // $LastChangedDate$ 24 // $URL$ 25 26 /** 27 * @fileOverview Defines the Poll prototype 28 */ 29 30 markgettext("Poll"); 31 markgettext("poll"); 32 33 /** 34 * @function 35 * @param {String} ctor 36 * @returns {String[]} 37 * @see defineConstants 38 */ 39 Poll.getStatus = defineConstants(Poll, markgettext("closed"), markgettext("open")); 40 41 /** 42 * @param {String} question 43 */ 44 Poll.add = function(data, site) { 45 HopObject.confirmConstructor(Poll); 46 var poll = new Poll; 47 poll.creator = session.user; 48 poll.created = new Date; 49 poll.update(data); 50 site.polls.add(poll); 51 return poll; 52 } 53 54 /** 55 * 56 */ 57 Poll.remove = function() { 58 if (this.constructor === Poll) { 59 HopObject.remove.call(this); 60 this.remove(); 61 } 62 return; 63 } 64 65 /** 66 * @name Poll 67 * @constructor 68 * @param {String} question 69 * @property {Choice[]} _children 70 * @property {String} closed 71 * @property {Date} created 72 * @property {User} creator 73 * @property {Date} modified 74 * @property {User} modifier 75 * @property {String} question 76 * @property {Site} site 77 * @property {String} status 78 * @property {Vote[]} votes 79 * @extends HopObject 80 */ 81 Poll.prototype.constructor = function() { 82 HopObject.confirmConstructor(this); 83 return this; 84 } 85 86 /** 87 * 88 * @param {String} action 89 * @returns {Boolean} 90 */ 91 Poll.prototype.getPermission = function(action) { 92 if (!this.site.getPermission("main")) { 93 return false; 94 } 95 switch (action) { 96 case ".": 97 case "main": 98 return !!session.user; 99 case "result": 100 return true; 101 case "edit": 102 return Membership.require(Membership.OWNER) || 103 User.require(User.PRIVILEGED); 104 case "rotate": 105 case "delete": 106 return this.creator === session.user || 107 Membership.require(Membership.MANAGER) || 108 User.require(User.PRIVILEGED); 109 } 110 return false; 111 } 112 113 /** 114 * 115 * @param {String} name 116 * @returns {Object} 117 */ 118 Poll.prototype.getFormOptions = function(name) { 119 switch (name) { 120 case "status": 121 return Poll.getStatus(); 122 } 123 return; 124 } 125 126 Poll.prototype.main_action = function() { 127 if (this.status !== Poll.OPEN) { 128 res.redirect(this.href("result")); 129 return; 130 } 131 if (req.postParams.vote) { 132 try { 133 this.vote(req.postParams); 134 res.message = gettext("Thanks, your vote was registered. You can change your mind until the poll is closed."); 135 res.redirect(this.href("result")); 136 } catch (ex) { 137 res.message = ex; 138 app.log(ex); 139 } 140 } 141 res.data.action = this.href(); 142 res.data.title = gettext("Poll: {0}", this.question); 143 res.data.body = this.renderSkinAsString("$Poll#main", {header: true}); 144 this.site.renderSkin("Site#page"); 145 return; 146 } 147 148 /** 149 * 150 * @param {Object} data 151 */ 152 Poll.prototype.vote = function(data) { 153 if (!data.choice) { 154 throw Error(gettext("You did not vote, yet. You can vote until the poll is closed.")); 155 } 156 var choice = this.get(data.choice); 157 var vote = session.user && this.votes.get(session.user.name); 158 if (vote) { 159 vote.choice = choice; 160 vote.modified = new Date; 161 } else { 162 vote = Vote.add(choice, this); 163 } 164 return vote; 165 } 166 167 Poll.prototype.edit_action = function() { 168 if (req.postParams.save) { 169 try { 170 this.update(req.postParams); 171 res.message = gettext("The poll was updated successfully."); 172 res.redirect(this.href()); 173 } catch (ex) { 174 res.message = ex; 175 app.log(ex); 176 } 177 } 178 res.data.action = this.href(req.action); 179 res.data.title = gettext("Edit Poll: {0}", this.question); 180 res.data.body = this.renderSkinAsString("$Poll#edit"); 181 this.site.renderSkin("Site#page"); 182 return; 183 } 184 185 /** 186 * 187 * @param {Object} data 188 */ 189 Poll.prototype.update = function(data) { 190 var choices = []; 191 for each (var title in data.title_array) { 192 if (title = title.trim()) { 193 choices.push(title); 194 } 195 } 196 if (choices.length < 2 || !data.question) { 197 throw Error(gettext("Please fill out the whole form to create a valid poll.")); 198 } 199 var size = this.size(); 200 // Update or remove choices 201 for (var i=size-1; i>-1; i-=1) { 202 var choice = this.get(i); 203 var title = choices[i]; 204 if (title) { 205 choice.title = title; 206 choice.touch(); 207 } else { 208 Choice.remove.call(choice); 209 } 210 } 211 // Add new choices 212 for (var i=size; i<choices.length; i+=1) { 213 Choice.add(choices[i], this); 214 } 215 if (data.save !== Poll.CLOSED) { 216 delete this.closed; 217 } else if (this.status === Poll.OPEN) { 218 this.closed = new Date; 219 } 220 this.status = data.save; 221 this.question = data.question; 222 this.touch(); 223 return; 224 } 225 226 Poll.prototype.result_action = function() { 227 res.data.title = gettext('Poll Results: {0}', this.question); 228 res.data.body = this.renderSkinAsString("$Poll#results", {header: true}); 229 this.site.renderSkin("Site#page"); 230 return; 231 } 232 233 Poll.prototype.rotate_action = function() { 234 if (this.status === Poll.CLOSED) { 235 this.status = Poll.OPEN; 236 } else if (this.status === Poll.OPEN) { 237 this.status = Poll.CLOSED; 238 this.closed = new Date; 239 } 240 this.touch(); 241 return res.redirect(this.href()); 242 } 243 244 /** 245 * @returns {String} 246 */ 247 Poll.prototype.getConfirmText = function() { 248 return gettext("You are about to delete a poll by user {0}.", 249 this.creator.name); 250 } 251 252 /** 253 * 254 * @param {Object} param 255 * @param {String} action 256 * @param {String} text 257 * @see HopObject#link_macro 258 */ 259 Poll.prototype.link_macro = function(param, action, text) { 260 switch (action) { 261 case ".": 262 case "main": 263 if (this.status === Poll.CLOSED) { 264 return; 265 } 266 break; 267 case "rotate": 268 if (this.status === Poll.OPEN) { 269 text = gettext("Stop"); 270 } else { 271 text = this.closed ? gettext("Re-run") : gettext("Run"); 272 } 273 break; 274 } 275 return HopObject.prototype.link_macro.call(this, param, action, text); 276 } 277 278 /** 279 * 280 * @param {Object} param 281 * @param {String} name 282 * @see HopObject#link_macro 283 */ 284 Poll.prototype.input_macro = function(param, name) { 285 switch (name) { 286 case "choices": 287 var index = 0; 288 var add = function(choice) { 289 index += 1; 290 return choice.renderSkin("$Choice#edit", {index: index}); 291 }; 292 var choices; 293 if (choices = req.postParams.title_array) { 294 HopObject.confirmConstructor(Choice); 295 choices.forEach(function(title) { 296 return add(new Choice(title)); 297 }); 298 } else { 299 this.forEach(function() { 300 return add(this); 301 }); 302 } 303 return; 304 } 305 return HopObject.prototype.input_macro.apply(this, arguments); 306 } 307 308 /** 309 * 310 */ 311 Poll.prototype.votes_macro = function() { 312 return this.votes.size(); 313 } 314 315 /** 316 * 317 * @param {Object} param 318 * @param {String} format 319 */ 320 Poll.prototype.closed_macro = function(param, format) { 321 if (this.status === Poll.CLOSED) { 322 res.write(formatDate(this.closed, param.format || format)); 323 } 324 return; 325 } 326