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 markgettext("Poll"); 30 markgettext("poll"); 31 32 /** 33 * @function 34 * @param {String} ctor 35 * @returns {String[]} 36 * @see defineConstants 37 */ 38 Poll.getStatus = defineConstants(Poll, markgettext("closed"), markgettext("open")); 39 40 /** 41 * 42 */ 43 Poll.remove = function() { 44 if (this.constructor === Poll) { 45 HopObject.remove.call(this); 46 this.remove(); 47 } 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", {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 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('Poll Results: {0}', this.question); 208 res.data.body = this.renderSkinAsString("$Poll#results", {header: true}); 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(req.data.http_referer); 222 } 223 224 /** 225 * @returns {String} 226 */ 227 Poll.prototype.getConfirmText = function() { 228 return gettext("You are about to delete a poll by user {0}.", 229 this.creator.name); 230 } 231 232 /** 233 * 234 * @param {Object} param 235 * @param {String} action 236 * @param {String} text 237 * @see HopObject#link_macro 238 */ 239 Poll.prototype.link_macro = function(param, action, text) { 240 switch (action) { 241 case ".": 242 case "main": 243 if (this.status === Poll.CLOSED) { 244 return; 245 } 246 break; 247 case "rotate": 248 if (this.status === Poll.OPEN) { 249 text = gettext("Stop"); 250 } else { 251 text = this.closed ? gettext("Re-run") : gettext("Run"); 252 } 253 break; 254 } 255 return HopObject.prototype.link_macro.call(this, param, action, text); 256 } 257 258 /** 259 * 260 * @param {Object} param 261 * @param {String} name 262 * @see HopObject#link_macro 263 */ 264 Poll.prototype.input_macro = function(param, name) { 265 switch (name) { 266 case "choices": 267 var index = 0; 268 var add = function(choice) { 269 index += 1; 270 return choice.renderSkin("$Choice#edit", {index: index}); 271 }; 272 var choices; 273 if (choices = req.postParams.title_array) { 274 while (choices.length < 2) { 275 choices.push(null); 276 } 277 choices.forEach(function(title) { 278 return add(new Choice(title)); 279 }); 280 } else { 281 this.forEach(function() { 282 return add(this); 283 }); 284 } 285 return; 286 } 287 return HopObject.prototype.input_macro.apply(this, arguments); 288 } 289 290 /** 291 * 292 */ 293 Poll.prototype.votes_macro = function() { 294 return this.votes.size(); 295 } 296 297 /** 298 * 299 * @param {Object} param 300 * @param {String} format 301 */ 302 Poll.prototype.closed_macro = function(param, format) { 303 if (this.status === Poll.CLOSED) { 304 res.write(formatDate(this.closed, param.format || format)); 305 } 306 return; 307 } 308