      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 = Membership.add(user, Membership.SUBSCRIBER, this._parent);
    101          membership.notify(req.action, user.email,
    102                gettext('[{0}] Welcome to {1}!', root.title, 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.setMetadata("resetToken", token);
    131          sendMail(user.email, gettext("[{0}] Password reset confirmation", root.title),
    132                user.renderSkinAsString("$User#notify_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.getMetadata("resetToken");
    145          if (token && token === req.data.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.setMetadata("resetToken", null);
    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       try {
    179          var user = User.login(req.postParams);
    180          res.message = gettext('Welcome to {0}, {1}. Have fun!',
    181                res.handlers.site.getTitle(), user.name);
    182          res.redirect(User.getLocation() || this._parent.href());
    183       } catch (ex) {
    184          res.message = ex;
    185       }
    186    }
    187    session.data.token = User.getSalt();
    188    res.data.action = this.href(req.action);
    189    res.data.title = gettext("Login");
    190    res.data.body = this.renderSkinAsString("$Members#login");
    191    this._parent.renderSkin("Site#page");
    192    return;
    193 }
    194
    195 Members.prototype.logout_action = function() {
    196    if (session.user) {
    197       res.message = gettext("Good bye, {0}! Looking forward to seeing you again!",
    198             session.user.name);
    199       User.logout();
    200    }
    201    res.redirect(this._parent.href());
    202    return;
    203 }
    204
    205 Members.prototype.edit_action = function() {
    206    if (req.postParams.save) {
    207       try {
    208          session.user.update(req.postParams);
    209          res.message = gettext("The changes were saved successfully.");
    210          res.redirect(this._parent.href());
    211       } catch (err) {
    212          res.message = err.toString();
    213       }
    214    }
    215
    216    session.data.token = User.getSalt();
    217    session.data.salt = session.user.salt; // FIXME
    218    res.data.title = gettext("User Profile");
    219    res.data.body = session.user.renderSkinAsString("$User#edit");
    220    this._parent.renderSkin("Site#page");
    221    return;
    222 }
    223
    224 Members.prototype.salt_txt_action = function() {
    225    res.contentType = "text/plain";
    226    var user;
    227    if (user = User.getByName(req.queryParams.user)) {
    228       res.write(user.salt || String.EMPTY);
    229    }
    230    return;
    231 }
    232
    233 Members.prototype.owners_action = function() {
    234    res.data.title = gettext("Site Owners");
    235    res.data.list = renderList(this.owners,
    236          "$Membership#member", 10, req.queryParams.page);
    237    res.data.pager = renderPager(this.owners,
    238          this.href(req.action), 10, req.queryParams.page);
    239    res.data.body = this.renderSkinAsString("$Members#main");
    240    res.handlers.site.renderSkin("Site#page");
    241    return;
    242 }
    243
    244 Members.prototype.managers_action = function() {
    245    res.data.title = gettext("Site Managers");
    246    res.data.list = renderList(this.managers,
    247          "$Membership#member", 10, req.queryParams.page);
    248    res.data.pager = renderPager(this.managers,
    249          this.href(req.action), 10, req.queryParams.page);
    250    res.data.body = this.renderSkinAsString("$Members#main");
    251    res.handlers.site.renderSkin("Site#page");
    252    return;
    253 }
    254
    255 Members.prototype.contributors_action = function() {
    256    res.data.title = gettext("Site Contributors");
    257    res.data.list = renderList(this.contributors,
    258          "$Membership#member", 10, req.queryParams.page);
    259    res.data.pager = renderPager(this.contributors,
    260          this.href(req.action), 10, req.data.page);
    261    res.data.body = this.renderSkinAsString("$Members#main");
    262    res.handlers.site.renderSkin("Site#page");
    263    return;
    264 }
    265
    266 Members.prototype.subscribers_action = function() {
    267    res.data.title = gettext("Site Subscribers");
    268    res.data.list = renderList(this.subscribers,
    269          "$Membership#member", 10, req.queryParams.page);
    270    res.data.pager = renderPager(this.subscribers,
    271          this.href(req.action), 10, req.queryParams.page);
    272    res.data.body = this.renderSkinAsString("$Members#main");
    273    res.handlers.site.renderSkin("Site#page");
    274    return;
    275 }
    276
    277 Members.prototype.updated_action = function() {
    278    res.data.title = gettext("Updates");
    279    res.data.list = session.user.renderSkinAsString("$User#sites");
    280    res.data.body = session.user.renderSkinAsString("$User#subscriptions");
    281    res.handlers.site.renderSkin("Site#page");
    282    return;
    283 }
    284
    285 Members.prototype.privileges_action = function() {
    286    var site = res.handlers.site;
    287    res.data.title = gettext("Privileges");
    288    res.data.list = renderList(session.user.memberships, function(item) {
    289       res.handlers.subscription = item.site;
    290       item.renderSkin("$Membership#subscription");
    291       return;
    292    });
    293    res.handlers.site = site;
    294    res.data.body = session.user.renderSkinAsString("$User#subscriptions");
    295    res.handlers.site.renderSkin("Site#page");
    296    return;
    297 }
    298
    299 Members.prototype.subscriptions_action = function() {
    300    var site = res.handlers.site;
    301    res.data.title = gettext("Subscriptions");
    302    res.data.list = renderList(session.user.subscriptions, function(item) {
    303       res.handlers.subscription = item.site;
    304       item.renderSkin("$Membership#subscription");
    305       return;
    306    });
    307    res.handlers.site = site;
    308    res.data.body = session.user.renderSkinAsString("$User#subscriptions");
    309    res.handlers.site.renderSkin("Site#page");
    310    return;
    311 }
    312
    313 Members.prototype.add_action = function() {
    314    if (req.postParams.term) {
    315       try {
    316          var result = this.search(req.postParams.term);
    317          if (result.length < 1) {
    318             res.message = gettext("No user found to add as member.");
    319          } else {
    320             if (result.length >= 100) {
    321                res.message = gettext("Too many users found, displaying the first {0} matches only.",
    322                      result.length);
    323             } else {
    324                res.message = ngettext("One user found.", "{0} users found.",
    325                       result.length);
    326             }
    327             res.data.result = this.renderSkinAsString("$Members#results", result);
    328          }
    329       } catch (ex) {
    330          res.message = ex;
    331          app.log(ex);
    332       }
    333    } else if (req.postParams.add) {
    334       try {
    335          var membership = this.addMembership(req.postParams);
    336          membership.notify(req.action, membership.creator.email,
    337                gettext('[{0}] Notification of membership change', root.title));
    338          res.message = gettext("Successfully added {0} to the list of members.",
    339                req.postParams.name);
    340          res.redirect(membership.href("edit"));
    341       } catch (ex) {
    342          res.message = ex;
    343          app.log(ex);
    344       }
    345       res.redirect(this.href());
    346    }
    347    res.data.action = this.href(req.action);
    348    res.data.title = gettext('Add Member');
    349    res.data.body = this.renderSkinAsString("$Members#add");
    350    res.handlers.site.renderSkin("Site#page");
    351    return;
    352 }
    353
    354 /**
    355  *
    356  * @param {String} searchString
    357  * @returns {Object}
    358  */
    359 Members.prototype.search = function(searchString) {
    360    var self = this;
    361    var mode = "=";
    362    if (searchString.contains("*")) {
    363       searchString = searchString.replace(/\*/g, "%");
    364       mode = "like";
    365    }
    366    var sql = new Sql;
    367    sql.retrieve(Sql.MEMBERSEARCH, mode, searchString, 100);
    368    var counter = 0, name;
    369    res.push();
    370    sql.traverse(function() {
    371       // Check if the user is not already a member
    372       if (!self.get(this.name)) {
    373          self.renderSkin("$Members#result", {name: this.name});
    374          counter += 1;
    375       }
    376    });
    377    return {
    378       result: res.pop(),
    379       length: counter
    380    };
    381 }
    382
    383 /**
    384  *
    385  * @param {Object} data
    386  * @returns {Membership}
    387  */
    388 Members.prototype.addMembership = function(data) {
    389    var user = root.users.get(data.name);
    390    if (!user) {
    391       throw Error(gettext("Sorry, your input did not match any registered user."));
    392    } else if (this.get(data.name)) {
    393       throw Error(gettext("This user is already a member of this site."));
    394    }
    395    var membership = Membership.add(user, Membership.SUBSCRIBER, this._parent);
    396    return membership;
    397 }
    398