1 // The Antville Project
  2 // http://code.google.com/p/antville
  3 //
  4 // Copyright 2007-2011 by Tobi Schäfer.
  5 //
  6 // Copyright 2001–2007 Robert Gaggl, Hannes Wallnöfer, Tobi Schäfer,
  7 // Matthias & Michael Platzer, Christoph Lincke.
  8 //
  9 // Licensed under the Apache License, Version 2.0 (the ``License'');
 10 // you may not use this file except in compliance with the License.
 11 // You may obtain a copy of the License at
 12 //
 13 //    http://www.apache.org/licenses/LICENSE-2.0
 14 //
 15 // Unless required by applicable law or agreed to in writing, software
 16 // distributed under the License is distributed on an ``AS IS'' BASIS,
 17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 18 // See the License for the specific language governing permissions and
 19 // limitations under the License.
 20 //
 21 // $Revision$
 22 // $Author$
 23 // $Date$
 24 // $URL$
 25 
 26 /**
 27  * @fileOverview Defines the Members prototype.
 28  */
 29 
 30 markgettext("Members");
 31 markgettext("members");
 32 
 33 /**
 34  * @name Members
 35  * @constructor
 36  * @property {Membership[]} _children
 37  * @property {Membership[]} contributors
 38  * @property {Membership[]} managers
 39  * @property {Membership[]} owners
 40  * @property {Membership[]} subscribers
 41  * @extends HopObject
 42  */
 43 
 44 /**
 45  * 
 46  * @param {String} action
 47  * @returns {Boolean}
 48  */
 49 Members.prototype.getPermission = function(action) {
 50    switch (action) {
 51       case "login":
 52       case "logout":
 53       case "register":
 54       case "reset":
 55       case "salt.txt":
 56       return true;
 57    }
 58       
 59    if (!this._parent.getPermission("main")) {
 60       return false;
 61    }
 62 
 63    switch (action) {
 64       case "edit":
 65       case "privileges":
 66       case "subscriptions":
 67       case "updated":
 68       return !!session.user;
 69       
 70       case ".":
 71       case "main":
 72       case "add":
 73       case "owners":
 74       case "managers":
 75       case "contributors":
 76       case "subscribers":
 77       return Membership.require(Membership.OWNER) ||
 78             User.require(User.PRIVILEGED);
 79    }
 80 
 81    return Feature.getPermission.apply(this, arguments);
 82 }
 83 
 84 Members.prototype.main_action = function() {
 85    res.data.title = gettext("Site Members");
 86    res.data.list = renderList(this, "$Membership#member", 
 87          10, req.queryParams.page);
 88    res.data.pager = renderPager(this, this.href(req.action), 
 89          10, req.queryParams.page);
 90    res.data.body = this.renderSkinAsString("$Members#main");
 91    res.handlers.site.renderSkin("Site#page");
 92    return;
 93 }
 94 
 95 Members.prototype.register_action = function() {
 96    if (req.postParams.register) {
 97       try {
 98          var title = res.handlers.site.title;
 99          var user = User.register(req.postParams);
100          var membership = new Membership(user, Membership.SUBSCRIBER);
101          this.add(membership);
102          membership.notify(req.action, user.email, 
103                gettext('[{0}] Welcome to {1}!', root.title, title));
104          res.message = gettext('Welcome to “{0}”, {1}. Have fun!',
105                title, user.name);
106          res.redirect(User.getLocation() || this._parent.href());
107       } catch (ex) {
108          res.message = ex;
109       }
110    }
111 
112    session.data.token = User.getSalt();
113    res.data.action = this.href(req.action);
114    res.data.title = gettext("Register");
115    res.data.body = this.renderSkinAsString("$Members#register");
116    this._parent.renderSkin("Site#page");
117    return;
118 }
119 
120 Members.prototype.reset_action = function() {
121    if (req.postParams.reset) {
122       try {
123          if (!req.postParams.name || !req.postParams.email) {
124             throw Error(gettext("Please enter a user name and e-mail address."));
125          }
126          var user = User.getByName(req.postParams.name);
127          if (!user || user.email !== req.postParams.email) {
128             throw Error(gettext("User name and e-mail address do not match."))
129          }
130          var token = User.getSalt();
131          user.setMetadata("resetToken", token);
132          sendMail(user.email, gettext("[{0}] Password reset confirmation", root.title),
133                user.renderSkinAsString("$User#notify_reset", {
134                   href: this.href("reset"),
135                   token: token
136                }));
137          res.message = gettext("A confirmation mail was sent to your e-mail address.");
138          res.redirect(this._parent.href());
139       } catch(ex) {
140          res.message = ex;
141       }
142    } else if (req.data.user && req.data.token) {
143       var user = User.getById(req.data.user);
144       if (user) {
145          var token = user.getMetadata("resetToken");
146          if (token && token === req.data.token) {
147             session.login(user);
148             if (req.postParams.save) {
149                var password = req.postParams.password;
150                if (!password) {
151                   res.message = gettext("Please enter a new password.");
152                } else if (password !== req.postParams.passwordConfirm) {
153                   res.message = gettext("The passwords do not match.");
154                } else {
155                   user.hash = (password + user.salt).md5();
156                   user.setMetadata("resetToken", null);
157                   res.message = gettext("Your password was changed.");
158                   res.redirect(this._parent.href());
159                }
160             }
161             res.data.title = gettext("Reset Password");
162             res.data.body = this.renderSkinAsString("$Members#password");
163             this._parent.renderSkin("Site#page");
164             return;
165          }
166       }
167       res.message = gettext("This URL is not valid for resetting your password.");
168       res.redirect(this.href(req.action));
169    }
170    res.data.action = this.href(req.action);
171    res.data.title = gettext("Request Password Reset");
172    res.data.body = this.renderSkinAsString("$Members#reset");
173    this._parent.renderSkin("Site#page");
174    return;
175 }
176 
177 Members.prototype.login_action = function() {
178    if (req.postParams.login) {
179       try {
180          var user = User.login(req.postParams);
181          res.message = gettext('Welcome to {0}, {1}. Have fun!',
182                res.handlers.site.getTitle(), user.name);
183          res.redirect(User.getLocation() || this._parent.href());
184       } catch (ex) {
185          res.message = ex;
186       }
187    }
188    session.data.token = User.getSalt();
189    res.data.action = this.href(req.action);
190    res.data.title = gettext("Login");
191    res.data.body = this.renderSkinAsString("$Members#login");
192    this._parent.renderSkin("Site#page");
193    return;
194 }
195 
196 Members.prototype.logout_action = function() {
197    if (session.user) {
198       res.message = gettext("Good bye, {0}! Looking forward to seeing you again!", 
199             session.user.name);
200       User.logout();
201    }
202    res.redirect(this._parent.href());
203    return;
204 }
205 
206 Members.prototype.edit_action = function() {
207    if (req.postParams.save) {
208       try {
209          session.user.update(req.postParams);
210          res.message = gettext("The changes were saved successfully.");
211          res.redirect(this._parent.href());
212       } catch (err) {
213          res.message = err.toString();
214       }
215    }
216 
217    session.data.token = User.getSalt();
218    session.data.salt = session.user.salt; // FIXME
219    res.data.title = gettext("User Profile");
220    res.data.body = session.user.renderSkinAsString("$User#edit");
221    this._parent.renderSkin("Site#page");
222    return;
223 }
224 
225 Members.prototype.salt_txt_action = function() {
226    res.contentType = "text/plain";
227    var user;
228    if (user = User.getByName(req.queryParams.user)) {
229       res.write(user.salt || String.EMPTY);
230    }
231    return;
232 }
233 
234 Members.prototype.owners_action = function() {
235    res.data.title = gettext("Site Owners");
236    res.data.list = renderList(this.owners, 
237          "$Membership#member", 10, req.queryParams.page);
238    res.data.pager = renderPager(this.owners, 
239          this.href(req.action), 10, req.queryParams.page);
240    res.data.body = this.renderSkinAsString("$Members#main");
241    res.handlers.site.renderSkin("Site#page");
242    return;
243 }
244 
245 Members.prototype.managers_action = function() {
246    res.data.title = gettext("Site Managers");
247    res.data.list = renderList(this.managers, 
248          "$Membership#member", 10, req.queryParams.page); 
249    res.data.pager = renderPager(this.managers, 
250          this.href(req.action), 10, req.queryParams.page);
251    res.data.body = this.renderSkinAsString("$Members#main");
252    res.handlers.site.renderSkin("Site#page");
253    return;
254 }
255 
256 Members.prototype.contributors_action = function() {
257    res.data.title = gettext("Site Contributors");
258    res.data.list = renderList(this.contributors, 
259          "$Membership#member", 10, req.queryParams.page);
260    res.data.pager = renderPager(this.contributors, 
261          this.href(req.action), 10, req.data.page);
262    res.data.body = this.renderSkinAsString("$Members#main");
263    res.handlers.site.renderSkin("Site#page");
264    return;
265 }
266 
267 Members.prototype.subscribers_action = function() {
268    res.data.title = gettext("Site Subscribers");
269    res.data.list = renderList(this.subscribers, 
270          "$Membership#member", 10, req.queryParams.page);
271    res.data.pager = renderPager(this.subscribers, 
272          this.href(req.action), 10, req.queryParams.page);
273    res.data.body = this.renderSkinAsString("$Members#main");
274    res.handlers.site.renderSkin("Site#page");
275    return;
276 }
277 
278 Members.prototype.updated_action = function() {
279    res.data.title = gettext("Updates");
280    res.data.list = session.user.renderSkinAsString("$User#sites");
281    res.data.body = session.user.renderSkinAsString("$User#subscriptions");
282    res.handlers.site.renderSkin("Site#page");
283    return;
284 }
285 
286 Members.prototype.privileges_action = function() {
287    var site = res.handlers.site;
288    res.data.title = gettext("Privileges");
289    res.data.list = renderList(session.user.memberships, function(item) {
290       res.handlers.subscription = item.site;
291       item.renderSkin("$Membership#subscription");
292       return;
293    });
294    res.handlers.site = site;
295    res.data.body = session.user.renderSkinAsString("$User#subscriptions");
296    res.handlers.site.renderSkin("Site#page");
297    return;
298 }
299 
300 Members.prototype.subscriptions_action = function() {
301    var site = res.handlers.site;
302    res.data.title = gettext("Subscriptions");
303    res.data.list = renderList(session.user.subscriptions, function(item) {
304       res.handlers.subscription = item.site;
305       item.renderSkin("$Membership#subscription");
306       return;
307    });
308    res.handlers.site = site;
309    res.data.body = session.user.renderSkinAsString("$User#subscriptions");
310    res.handlers.site.renderSkin("Site#page");
311    return;
312 }
313 
314 Members.prototype.add_action = function() {
315    if (req.postParams.term) {
316       try {
317          var result = this.search(req.postParams.term);
318          if (result.length < 1) {
319             res.message = gettext("No user found to add as member.");
320          } else {
321             if (result.length >= 100) {
322                res.message = gettext("Too many users found, displaying the first {0} matches only.", 
323                      result.length);
324             } else {
325                res.message = ngettext("One user found.", "{0} users found.", 
326                       result.length);
327             }
328             res.data.result = this.renderSkinAsString("$Members#results", result);
329          }
330       } catch (ex) {
331          res.message = ex;
332          app.log(ex);
333       }
334    } else if (req.postParams.add) {
335       try {
336          var membership = this.addMembership(req.postParams);
337          membership.notify(req.action, membership.creator.email,  
338                gettext('[{0}] Notification of membership change', root.title));
339          res.message = gettext("Successfully added {0} to the list of members.", 
340                req.postParams.name);
341          res.redirect(membership.href("edit"));
342       } catch (ex) {
343          res.message = ex;
344          app.log(ex);
345       }
346       res.redirect(this.href());
347    }
348    res.data.action = this.href(req.action);
349    res.data.title = gettext('Add Member');
350    res.data.body = this.renderSkinAsString("$Members#add");
351    res.handlers.site.renderSkin("Site#page");
352    return;
353 }
354 
355 /**
356  * 
357  * @param {String} searchString
358  * @returns {Object}
359  */
360 Members.prototype.search = function(searchString) {
361    var self = this;
362    var mode = "=";
363    if (searchString.contains("*")) {
364       searchString = searchString.replace(/\*/g, "%");
365       mode = "like";
366    }
367    var sql = new Sql;
368    sql.retrieve(Sql.MEMBERSEARCH, mode, searchString, 100);
369    var counter = 0, name;
370    res.push();
371    sql.traverse(function() {
372       // Check if the user is not already a member
373       if (!self.get(this.name)) {
374          self.renderSkin("$Members#result", {name: this.name});
375          counter += 1;
376       }
377    });
378    return {
379       result: res.pop(),
380       length: counter
381    };
382 }
383 
384 /**
385  * 
386  * @param {Object} data
387  * @returns {Membership}
388  */
389 Members.prototype.addMembership = function(data) {
390    var user = root.users.get(data.name);
391    if (!user) {
392       throw Error(gettext("Sorry, your input did not match any registered user."));
393    } else if (this.get(data.name)) {
394       throw Error(gettext("This user is already a member of this site."));
395    }
396    var membership = new Membership(user, Membership.SUBSCRIBER);
397    this.add(membership);
398    return membership;
399 }
400