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