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:3329 $ 20 // $LastChangedBy:piefke3000 $ 21 // $LastChangedDate:2007-09-14 15:18:09 +0200 (Fri, 14 Sep 2007) $ 22 // $URL$ 23 // 24 25 /** 26 * @fileOverview Defines the Members prototype. 27 */ 28 29 markgettext("Members"); 30 markgettext("members"); 31 32 /** 33 * @name Members 34 * @constructor 35 * @property {Membership[]} _children 36 * @property {Membership[]} contributors 37 * @property {Membership[]} managers 38 * @property {Membership[]} owners 39 * @property {Membership[]} subscribers 40 * @extends HopObject 41 */ 42 43 /** 44 * 45 * @param {String} action 46 * @returns {Boolean} 47 */ 48 Members.prototype.getPermission = function(action) { 49 switch (action) { 50 case "login": 51 case "logout": 52 case "register": 53 case "reset": 54 case "salt.js": 55 return true; 56 } 57 58 if (!this._parent.getPermission("main")) { 59 return false; 60 } 61 62 switch (action) { 63 case "edit": 64 case "privileges": 65 case "subscriptions": 66 case "updated": 67 return !!session.user; 68 69 case ".": 70 case "main": 71 case "add": 72 case "owners": 73 case "managers": 74 case "contributors": 75 case "subscribers": 76 return Membership.require(Membership.OWNER) || 77 User.require(User.PRIVILEGED); 78 } 79 return false; 80 } 81 82 Members.prototype.main_action = function() { 83 res.data.title = gettext("Site Members"); 84 res.data.list = renderList(this, "$Membership#member", 85 10, req.queryParams.page); 86 res.data.pager = renderPager(this, this.href(req.action), 87 10, req.queryParams.page); 88 res.data.body = this.renderSkinAsString("$Members#main"); 89 res.handlers.site.renderSkin("Site#page"); 90 return; 91 } 92 93 Members.prototype.register_action = function() { 94 if (req.postParams.register) { 95 if (req.postParams.activation) { 96 app.log("Detected form submit with completed honeypot field: " + req.data); 97 res.redirect(root.href()); 98 } 99 try { 100 var title = res.handlers.site.title; 101 var user = User.register(req.postParams); 102 var membership = new Membership(user, Membership.SUBSCRIBER); 103 this.add(membership); 104 membership.notify(req.action, user.email, 105 gettext('[{0}] Welcome to {1}!', root.title, title)); 106 res.message = gettext('Welcome to “{0}”, {1}. Have fun!', 107 title, user.name); 108 res.redirect(User.getLocation() || this._parent.href()); 109 } catch (ex) { 110 res.message = ex; 111 } 112 } 113 114 session.data.token = User.getSalt(); 115 res.data.action = this.href(req.action); 116 res.data.title = gettext("Register"); 117 res.data.body = this.renderSkinAsString("$Members#register"); 118 this._parent.renderSkin("Site#page"); 119 return; 120 } 121 122 Members.prototype.reset_action = function() { 123 if (req.postParams.reset) { 124 try { 125 if (!req.postParams.name || !req.postParams.email) { 126 throw Error(gettext("Please enter a user name and e-mail address.")); 127 } 128 var user = User.getByName(req.postParams.name); 129 if (!user || user.email !== req.postParams.email) { 130 throw Error(gettext("User name and e-mail address do not match.")) 131 } 132 var token = User.getSalt(); 133 user.metadata.set("resetToken", token); 134 sendMail(user.email, gettext("[{0}] Password reset confirmation", 135 root.title), user.renderSkinAsString("$User#notify_reset", { 136 href: this.href("reset"), 137 token: token 138 })); 139 res.message = gettext("A confirmation mail was sent to your e-mail address."); 140 res.redirect(this._parent.href()); 141 } catch(ex) { 142 res.message = ex; 143 } 144 } else if (req.data.user && req.data.token) { 145 var user = User.getById(req.data.user); 146 if (user) { 147 var token = user.metadata.get("resetToken"); 148 if (token) { 149 session.login(user); 150 if (req.postParams.save) { 151 var password = req.postParams.password; 152 if (!password) { 153 res.message = gettext("Please enter a new password."); 154 } else if (password !== req.postParams.passwordConfirm) { 155 res.message = gettext("The passwords do not match."); 156 } else { 157 user.hash = (password + user.salt).md5(); 158 user.metadata.remove("resetToken"); 159 res.message = gettext("Your password was changed."); 160 res.redirect(this._parent.href()); 161 } 162 } 163 res.data.title = gettext("Reset Password"); 164 res.data.body = this.renderSkinAsString("$Members#password"); 165 this._parent.renderSkin("Site#page"); 166 return; 167 } 168 } 169 res.message = gettext("This URL is not valid for resetting your password."); 170 res.redirect(this.href(req.action)); 171 } 172 res.data.action = this.href(req.action); 173 res.data.title = gettext("Request Password Reset"); 174 res.data.body = this.renderSkinAsString("$Members#reset"); 175 this._parent.renderSkin("Site#page"); 176 return; 177 } 178 179 Members.prototype.login_action = function() { 180 if (req.postParams.login) { 181 if (req.postParams.activation) { 182 app.log("Detected form submit with completed honeypot field: " + req.data); 183 res.redirect(root.href()); 184 } 185 try { 186 var user = User.login(req.postParams); 187 res.message = gettext('Welcome to {0}, {1}. Have fun!', 188 res.handlers.site.getTitle(), user.name); 189 res.redirect(User.getLocation() || this._parent.href()); 190 } catch (ex) { 191 res.message = ex; 192 } 193 } 194 session.data.token = User.getSalt(); 195 res.data.action = this.href(req.action); 196 res.data.title = gettext("Login"); 197 res.data.body = this.renderSkinAsString("$Members#login"); 198 this._parent.renderSkin("Site#page"); 199 return; 200 } 201 202 Members.prototype.logout_action = function() { 203 if (session.user) { 204 res.message = gettext("Good bye, {0}! Looking forward to seeing you again!", 205 session.user.name); 206 User.logout(); 207 } 208 res.redirect(this._parent.href()); 209 return; 210 } 211 212 Members.prototype.edit_action = function() { 213 if (req.postParams.save) { 214 try { 215 session.user.update(req.postParams); 216 res.message = gettext("The changes were saved successfully."); 217 res.redirect(this._parent.href()); 218 } catch (err) { 219 res.message = err.toString(); 220 } 221 } 222 223 session.data.token = User.getSalt(); 224 session.data.salt = session.user.salt; // FIXME 225 res.data.title = gettext("User Profile"); 226 res.data.body = session.user.renderSkinAsString("$User#edit"); 227 this._parent.renderSkin("Site#page"); 228 return; 229 } 230 231 Members.prototype.salt_js_action = function() { 232 res.contentType = "text/javascript"; 233 var user; 234 if (user = User.getByName(req.queryParams.user)) { 235 res.write((user.salt || String.EMPTY).toSource()); 236 } 237 return; 238 } 239 240 Members.prototype.owners_action = function() { 241 res.data.title = gettext("Site Owners"); 242 res.data.list = renderList(this.owners, 243 "$Membership#member", 10, req.queryParams.page); 244 res.data.pager = renderPager(this.owners, 245 this.href(req.action), 10, req.queryParams.page); 246 res.data.body = this.renderSkinAsString("$Members#main"); 247 res.handlers.site.renderSkin("Site#page"); 248 return; 249 } 250 251 Members.prototype.managers_action = function() { 252 res.data.title = gettext("Site Managers"); 253 res.data.list = renderList(this.managers, 254 "$Membership#member", 10, req.queryParams.page); 255 res.data.pager = renderPager(this.managers, 256 this.href(req.action), 10, req.queryParams.page); 257 res.data.body = this.renderSkinAsString("$Members#main"); 258 res.handlers.site.renderSkin("Site#page"); 259 return; 260 } 261 262 Members.prototype.contributors_action = function() { 263 res.data.title = gettext("Site Contributors"); 264 res.data.list = renderList(this.contributors, 265 "$Membership#member", 10, req.queryParams.page); 266 res.data.pager = renderPager(this.contributors, 267 this.href(req.action), 10, req.data.page); 268 res.data.body = this.renderSkinAsString("$Members#main"); 269 res.handlers.site.renderSkin("Site#page"); 270 return; 271 } 272 273 Members.prototype.subscribers_action = function() { 274 res.data.title = gettext("Site Subscribers"); 275 res.data.list = renderList(this.subscribers, 276 "$Membership#member", 10, req.queryParams.page); 277 res.data.pager = renderPager(this.subscribers, 278 this.href(req.action), 10, req.queryParams.page); 279 res.data.body = this.renderSkinAsString("$Members#main"); 280 res.handlers.site.renderSkin("Site#page"); 281 return; 282 } 283 284 Members.prototype.updated_action = function() { 285 res.data.title = gettext("Updates"); 286 res.data.list = session.user.renderSkinAsString("$User#sites"); 287 res.data.body = session.user.renderSkinAsString("$User#subscriptions"); 288 res.handlers.site.renderSkin("Site#page"); 289 return; 290 } 291 292 Members.prototype.privileges_action = function() { 293 var site = res.handlers.site; 294 res.data.title = gettext("Privileges"); 295 res.data.list = renderList(session.user.memberships, function(item) { 296 res.handlers.subscription = item.site; 297 item.renderSkin("$Membership#subscription"); 298 return; 299 }); 300 res.handlers.site = site; 301 res.data.body = session.user.renderSkinAsString("$User#subscriptions"); 302 res.handlers.site.renderSkin("Site#page"); 303 return; 304 } 305 306 Members.prototype.subscriptions_action = function() { 307 var site = res.handlers.site; 308 res.data.title = gettext("Subscriptions"); 309 res.data.list = renderList(session.user.subscriptions, function(item) { 310 res.handlers.subscription = item.site; 311 item.renderSkin("$Membership#subscription"); 312 return; 313 }); 314 res.handlers.site = site; 315 res.data.body = session.user.renderSkinAsString("$User#subscriptions"); 316 res.handlers.site.renderSkin("Site#page"); 317 return; 318 } 319 320 Members.prototype.add_action = function() { 321 if (req.postParams.term) { 322 try { 323 var result = this.search(req.postParams.term); 324 if (result.length < 1) { 325 res.message = gettext("No user found to add as member."); 326 } else { 327 if (result.length >= 100) { 328 res.message = gettext("Too many users found, displaying the first {0} matches only.", 329 result.length); 330 } else { 331 res.message = ngettext("One user found.", "{0} users found.", 332 result.length); 333 } 334 res.data.result = this.renderSkinAsString("$Members#results", result); 335 } 336 } catch (ex) { 337 res.message = ex; 338 app.log(ex); 339 } 340 } else if (req.postParams.add) { 341 try { 342 res.handlers.sender = User.getMembership(); 343 var membership = this.addMembership(req.postParams); 344 membership.notify(req.action, membership.creator.email, 345 gettext('[{0}] Notification of membership change', root.title)); 346 res.message = gettext("Successfully added {0} to the list of members.", 347 req.postParams.name); 348 res.redirect(membership.href("edit")); 349 } catch (ex) { 350 res.message = ex; 351 app.log(ex); 352 } 353 res.redirect(this.href()); 354 } 355 res.data.action = this.href(req.action); 356 res.data.title = gettext('Add Member'); 357 res.data.body = this.renderSkinAsString("$Members#add"); 358 res.handlers.site.renderSkin("Site#page"); 359 return; 360 } 361 362 /** 363 * 364 * @param {String} searchString 365 * @returns {Object} 366 */ 367 Members.prototype.search = function(searchString) { 368 var self = this; 369 var mode = "="; 370 if (searchString.contains("*")) { 371 searchString = searchString.replace(/\*/g, "%"); 372 mode = "like"; 373 } 374 var sql = new Sql; 375 sql.retrieve(Sql.MEMBERSEARCH, mode, searchString, 100); 376 var counter = 0, name; 377 res.push(); 378 sql.traverse(function() { 379 // Check if the user is not already a member 380 if (!self.get(this.name)) { 381 self.renderSkin("$Members#result", {name: this.name}); 382 counter += 1; 383 } 384 }); 385 return { 386 result: res.pop(), 387 length: counter 388 }; 389 } 390 391 /** 392 * 393 * @param {Object} data 394 * @returns {Membership} 395 */ 396 Members.prototype.addMembership = function(data) { 397 var user = root.users.get(data.name); 398 if (!user) { 399 throw Error(gettext("Sorry, your input did not match any registered user.")); 400 } else if (this.get(data.name)) { 401 throw Error(gettext("This user is already a member of this site.")); 402 } 403 var membership = new Membership(user, Membership.SUBSCRIBER); 404 this.add(membership); 405 return membership; 406 } 407 408 Members.prototype.modSorua_action = function() { 409 if (!app.data.modSorua) app.data.modSorua = new Array(); 410 var returnUrl = req.data["sorua-return-url"]; 411 var failUrl = req.data["sorua-fail-url"]; 412 var userID = req.data["sorua-user"]; 413 var action = req.data["sorua-action"]; 414 if (action == "authenticate") { // authenticate-action 415 if (session.user && (userID == null || userID == "" || session.user.name == userID)) { 416 // store returnUrl + timestamp + userID 417 app.data.modSorua[returnUrl] = {time: new Date(), userID: session.user.name}; 418 res.redirect(returnUrl); 419 } else if (failUrl) { 420 res.redirect(failUrl); 421 } else { 422 session.data.modSorua = {returnUrl: returnUrl, 423 userID: userID}; 424 res.redirect(this.href("modSoruaLoginForm")); 425 } 426 427 } else if (action == "verify") { 428 // first remove outdated entries 429 var now = new Date(); 430 var arr = new Array(); 431 for (var i in app.data.modSorua) { 432 if (app.data.modSorua[i] && app.data.modSorua[i].time && 433 now.valueOf() - app.data.modSorua[i].time.valueOf() < 1000 * 60) 434 arr[i] = app.data.modSorua[i]; 435 } 436 app.data.modSorua = arr; 437 // now check whether returnUrl has been used recently 438 if (app.data.modSorua[returnUrl]) { 439 res.status = 200; 440 res.write("user:" + app.data.modSorua[returnUrl].userID); 441 return; 442 } else { 443 res.status = 403; 444 return; 445 } 446 447 } else { // handle wrong call of AuthURI 448 res.redirect(root.href("main")); 449 450 } 451 } 452 453 Members.prototype.modSoruaLoginForm_action = function() { 454 if (!session.data.modSorua || !session.data.modSorua.returnUrl) 455 res.redirect(root.href()); // should not happen anyways 456 if (req.data.login) { 457 try { 458 res.message = this.evalLogin(req.data.name, req.data.password); 459 var returnUrl = session.data.modSorua.returnUrl; 460 app.data.modSorua[returnUrl] = {time: new Date(), userID: req.data.name}; 461 res.redirect(returnUrl); 462 } catch (err) { 463 res.message = err.toString(); 464 } 465 } 466 res.data.action = this.href("modSoruaLoginForm"); 467 this.renderSkin("modSorua"); 468 } 469