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