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