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 * 43 */ 44 Poll.remove = function() { 45 if (this.constructor === Poll) { 46 HopObject.remove.call(this); 47 this.remove(); 48 } 49 return; 50 } 51 52 /** 53 * @name Poll 54 * @constructor 55 * @param {String} question 56 * @property {Choice[]} _children 57 * @property {String} closed 58 * @property {Date} created 59 * @property {User} creator 60 * @property {Date} modified 61 * @property {User} modifier 62 * @property {String} question 63 * @property {Site} site 64 * @property {String} status 65 * @property {Vote[]} votes 66 * @extends HopObject 67 */ 68 Poll.prototype.constructor = function(question) { 69 this.question = question; 70 this.creator = this.modifier = session.user; 71 this.created = this.modified = new Date; 72 return this; 73 } 74 75 /** 76 * 77 * @param {String} action 78 * @returns {Boolean} 79 */ 80 Poll.prototype.getPermission = function(action) { 81 if (!this.site.getPermission("main")) { 82 return false; 83 } 84 switch (action) { 85 case ".": 86 case "main": 87 return !!session.user; 88 case "result": 89 return true; 90 case "edit": 91 return 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", {header: true}); 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 if (choices.length < 2 || !data.question) { 186 throw Error(gettext("Please fill out the whole form to create a valid poll.")); 187 } 188 var size = this.size(); 189 // Update or remove choices 190 for (var i=size-1; i>-1; i-=1) { 191 var choice = this.get(i); 192 var title = choices[i]; 193 if (title) { 194 choice.title = title; 195 choice.touch(); 196 } else { 197 Choice.remove.call(choice); 198 } 199 } 200 // Add new choices 201 for (var i=size; i<choices.length; i+=1) { 202 this.add(new Choice(choices[i])); 203 } 204 if (data.save !== Poll.CLOSED) { 205 delete this.closed; 206 } else if (this.status === Poll.OPEN) { 207 this.closed = new Date; 208 } 209 this.status = data.save; 210 this.question = data.question; 211 this.touch(); 212 return; 213 } 214 215 Poll.prototype.result_action = function() { 216 res.data.title = gettext('Poll Results: {0}', this.question); 217 res.data.body = this.renderSkinAsString("$Poll#results", {header: true}); 218 this.site.renderSkin("Site#page"); 219 return; 220 } 221 222 Poll.prototype.rotate_action = function() { 223 if (this.status === Poll.CLOSED) { 224 this.status = Poll.OPEN; 225 } else if (this.status === Poll.OPEN) { 226 this.status = Poll.CLOSED; 227 this.closed = new Date; 228 } 229 this.touch(); 230 return res.redirect(this.href()); 231 } 232 233 /** 234 * @returns {String} 235 */ 236 Poll.prototype.getConfirmText = function() { 237 return gettext("You are about to delete a poll by user {0}.", 238 this.creator.name); 239 } 240 241 /** 242 * 243 * @param {Object} param 244 * @param {String} action 245 * @param {String} text 246 * @see HopObject#link_macro 247 */ 248 Poll.prototype.link_macro = function(param, action, text) { 249 switch (action) { 250 case ".": 251 case "main": 252 if (this.status === Poll.CLOSED) { 253 return; 254 } 255 break; 256 case "rotate": 257 if (this.status === Poll.OPEN) { 258 text = gettext("Stop"); 259 } else { 260 text = this.closed ? gettext("Re-run") : gettext("Run"); 261 } 262 break; 263 } 264 return HopObject.prototype.link_macro.call(this, param, action, text); 265 } 266 267 /** 268 * 269 * @param {Object} param 270 * @param {String} name 271 * @see HopObject#link_macro 272 */ 273 Poll.prototype.input_macro = function(param, name) { 274 switch (name) { 275 case "choices": 276 var index = 0; 277 var add = function(choice) { 278 index += 1; 279 return choice.renderSkin("$Choice#edit", {index: index}); 280 }; 281 var choices; 282 if (choices = req.postParams.title_array) { 283 while (choices.length < 2) { 284 choices.push(null); 285 } 286 choices.forEach(function(title) { 287 return add(new Choice(title)); 288 }); 289 } else { 290 this.forEach(function() { 291 return add(this); 292 }); 293 } 294 return; 295 } 296 return HopObject.prototype.input_macro.apply(this, arguments); 297 } 298 299 /** 300 * 301 */ 302 Poll.prototype.votes_macro = function() { 303 return this.votes.size(); 304 } 305 306 /** 307 * 308 * @param {Object} param 309 * @param {String} format 310 */ 311 Poll.prototype.closed_macro = function(param, format) { 312 if (this.status === Poll.CLOSED) { 313 res.write(formatDate(this.closed, param.format || format)); 314 } 315 return; 316 } 317