[guadec-web-regcfp/develop] Add registration system



commit 65fabb80891a22be277470e5d75eec4c207f3fe8
Author: Patrick Uiterwijk <puiterwijk redhat com>
Date:   Tue Jun 2 13:23:13 2015 +0200

    Add registration system

 app.js                                           |   10 ++
 models/registration.js                           |   46 +++++++
 models/registrationpayment.js                    |   18 +++
 models/user.js                                   |    1 +
 routes/index.js                                  |   21 ++--
 routes/papers.js                                 |    2 +
 routes/registration.js                           |  157 ++++++++++++++++++++++
 utils.js                                         |   38 +++++-
 views/index/index.hbs                            |   26 ++++-
 views/partials/registration/payment.hbs          |   10 ++
 views/registration/list.hbs                      |    6 +
 views/registration/pay.hbs                       |   20 +++
 views/registration/pay_do.hbs                    |    1 +
 views/registration/payment_onsite_registered.hbs |    2 +
 views/registration/register.hbs                  |   53 ++++++++
 views/registration/registration_success.hbs      |    3 +
 views/registration/update_success.hbs            |    2 +
 17 files changed, 395 insertions(+), 21 deletions(-)
---
diff --git a/app.js b/app.js
index 7145bb6..5a0bc7e 100644
--- a/app.js
+++ b/app.js
@@ -39,6 +39,13 @@ var hbs = handlebars.create({
       } else {
         return options.inverse(this);
       }
+    },
+    ifGTE: function(v1, v2, options) {
+      if(v1 >= v2) {
+        return options.fn(this);
+      } else {
+        return options.inverse(this);
+      }
     }
   }
 });
@@ -75,6 +82,9 @@ app.use(bodyParser.urlencoded({ extended: false }));
 app.use(cookieParser());
 app.use(express.static(path.join(__dirname, 'public')));
 
+// Get user if we have any
+app.use(utils.get_user);
+
 // Routing
 app.use('/', routes_index);
 app.use('/auth', routes_auth);
diff --git a/models/registration.js b/models/registration.js
new file mode 100644
index 0000000..d9c1581
--- /dev/null
+++ b/models/registration.js
@@ -0,0 +1,46 @@
+"use strict";
+
+module.exports = function(sequelize, DataTypes) {
+  var Registration = sequelize.define("Registration", {
+    irc: DataTypes.STRING,
+    is_public: DataTypes.BOOLEAN,
+    badge_printed: DataTypes.BOOLEAN,
+    receipt_sent: DataTypes.BOOLEAN,
+    gender: DataTypes.ENUM('male', 'female', 'other'),
+    country: DataTypes.STRING,
+  }, {
+    classMethods: {
+      associate: function(models) {
+        Registration.belongsTo(models.User);
+        Registration.hasMany(models.RegistrationPayment);
+      }
+    },
+    getterMethods: {
+      display_id: function() {
+        return this.id;
+      },
+      paid: function() {
+        var amount = 0;
+        for(var payment in this.RegistrationPayments) {
+          payment = this.RegistrationPayments[payment];
+          if(payment.paid) {
+            amount += payment.amount;
+          }
+        }
+        return amount;
+      },
+      outstanding: function() {
+        var amount = 0;
+        for(var payment in this.RegistrationPayments) {
+          payment = this.RegistrationPayments[payment];
+          if(!payment.paid) {
+            amount += payment.amount;
+          }
+        }
+        return amount;
+      }
+    }
+  });
+
+  return Registration;
+};
diff --git a/models/registrationpayment.js b/models/registrationpayment.js
new file mode 100644
index 0000000..ce2e9a2
--- /dev/null
+++ b/models/registrationpayment.js
@@ -0,0 +1,18 @@
+"use strict";
+
+module.exports = function(sequelize, DataTypes) {
+  var RegistrationPayment = sequelize.define("RegistrationPayment", {
+    amount: DataTypes.FLOAT,
+    paid: DataTypes.BOOLEAN,
+    type: DataTypes.ENUM('paypal', 'onsite'),
+    details: DataTypes.STRING,
+  }, {
+    classMethods: {
+      associate: function(models) {
+        RegistrationPayment.belongsTo(models.Registration);
+      }
+    }
+  });
+
+  return RegistrationPayment;
+};
diff --git a/models/user.js b/models/user.js
index d107e4c..5434fac 100644
--- a/models/user.js
+++ b/models/user.js
@@ -9,6 +9,7 @@ module.exports = function(sequelize, DataTypes) {
       associate: function(models) {
         User.hasMany(models.Paper);
         User.hasMany(models.PaperVote);
+        User.hasOne(models.Registration);
       }
     }
   });
diff --git a/routes/index.js b/routes/index.js
index 7fb3897..90fa2b7 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -3,6 +3,8 @@ var router = express.Router();
 
 var models = require('../models');
 var User = models.User;
+var Registration = models.Registration;
+var RegistrationPayment = models.RegistrationPayment;
 
 var env = process.env.NODE_ENV || "development";
 var config = require(__dirname + '/../config/config.json')[env];
@@ -12,19 +14,14 @@ var config = require(__dirname + '/../config/config.json')[env];
 router.get('/', function(req, res, next) {
   if(req.session.currentUser)
   {
-    User
-      .find({
-        where: {
-          email: req.session.currentUser
-        }
-      })
-      .complete(function(err, user) {
-        if(user) {
-          res.render('index/index', { 'name': user.name });
-        } else {
-          res.render('index/index', { 'name': req.session.currentUser });
-        }
+    if(req.user) {
+      req.user.getRegistration({include: [RegistrationPayment]})
+      .complete(function(err, reg) {
+        res.render('index/index', { name: req.user.name, registration: reg });
       });
+    } else {
+      res.render('index/index', { name: req.session.currentUser });
+    }
   } else {
     res.render('index/index', { });
   }
diff --git a/routes/papers.js b/routes/papers.js
index d2027be..9ca8195 100644
--- a/routes/papers.js
+++ b/routes/papers.js
@@ -8,6 +8,8 @@ var User = models.User;
 var Paper = models.Paper;
 var PaperVote = models.PaperVote;
 
+router.all('/', utils.require_feature('papers'));
+
 router.all('/submit', utils.require_user);
 router.all('/submit', utils.require_permission('papers/submit'));
 router.get('/submit', function(req, res, next) {
diff --git a/routes/registration.js b/routes/registration.js
index 6309524..d637a97 100644
--- a/routes/registration.js
+++ b/routes/registration.js
@@ -5,7 +5,164 @@ var utils = require('../utils');
 
 var models = require('../models');
 var User = models.User;
+var Registration = models.Registration;
+var RegistrationPayment = models.RegistrationPayment;
 
+var all_genders = ['male', 'female', 'other'];
 
+router.all('/', utils.require_feature("registration"));
+
+router.all('/list', utils.require_permission('registration/view_public'));
+router.get('/list', function(req, res, next) {
+  Registration
+    .findAll({
+      where: {
+        is_public: true
+      }
+    })
+    .complete(function(err, registrations) {
+      res.render('registration/list', { registrations: registrations });
+    });
+});
+
+router.all('/pay', utils.require_user);
+router.all('/pay', utils.require_permission('registration/pay_extra'));
+router.get('/pay', function(req, res, next) {
+  res.render('registration/pay');
+});
+
+router.post('/pay', function(req, res, next) {
+  var regfee = req.body.regfee;
+  if(regfee == 'custom')
+  {
+    regfee = req.body.regfee_custom.trim();
+  }
+  
+  if(regfee == null) {
+    res.render('registration/pay');
+  } else {
+    res.render('registration/pay_do', {regfee: regfee});
+  }
+});
+
+router.all('/pay/do', utils.require_user);
+router.all('/pay/do', utils.require_permission('registration/pay_extra'));
+router.post('/pay/do', function(req, res, next) {
+  var method = req.body.method;
+  if(method == 'onsite') {
+    var info = {
+      amount: req.body.regfee,
+      paid: false,
+      type: 'onsite',
+    };
+    RegistrationPayment
+      .create(info)
+      .complete(function(err, payment) {
+        if(!!err) {
+          console.log('Error saving payment: ' + err);
+          res.status(500).send('ERROR saving payment');
+        } else {
+          req.user.getRegistration()
+            .complete(function(err, reg) {
+              reg.addRegistrationPayment(payment)
+                .complete(function(err) {
+                  if(!!err) {
+                    console.log('Error attaching payment to reg: ' + err);
+                    res.status(500).send('Error attaching payment');
+                  } else {
+                    res.render('registration/payment_onsite_registered', {amount: info.amount});
+                  }
+                });
+            });
+        }
+      });
+  } else {
+    res.status(402).send('Invalid payment method selected');
+  }
+});
+
+router.all('/register', utils.require_user);
+router.all('/register', utils.require_permission('registration/register'));
+router.get('/register', function(req, res, next) {
+  req.user.getRegistration()
+  .complete(function(err, reg) {
+    res.render('registration/register', { registration: reg, genders: all_genders });
+  });
+});
+
+router.post('/register', function(req, res, next) {
+  req.user.getRegistration({include: [RegistrationPayment]})
+  .complete(function(err, reg) {
+    console.log('Body: ' + JSON.stringify(req.body));
+    var reg_info = {
+      irc: req.body.irc.trim(),
+      is_public: req.body.is_public.indexOf('true') != -1,
+      gender: req.body.gender,
+      country: req.body.country.trim(),
+      badge_printed: false,
+      receipt_sent: false,
+      UserId: req.user.Id,
+      is_not_saved: true
+    };
+
+    console.log("Reg info: " + JSON.stringify(reg_info));
+
+    var regfee = req.body.regfee;
+    if(regfee == 'custom')
+    {
+      regfee = req.body.regfee_custom.trim();
+    }
+
+    if((all_genders.indexOf(reg_info.gender) == -1) || (reg == null && regfee == null)) {
+      res.render('registration/register', { registration: reg_info, genders: all_genders,
+                                            submission_error: true});
+    } else {
+      // Form OK
+      if(reg == null) {
+        console.log("CREATING");
+        // Create new registration
+        Registration.create(reg_info)
+          .complete(function(err, reg) {
+            if(!!err) {
+              console.log('Error saving reg: ' + err);
+              res.status(500).send('Error saving registration');
+            } else {
+              req.user.setRegistration(reg)
+                .complete(function(err) {
+                  if(!!err) {
+                    console.log('Error adding reg to user: ' + err);
+                    res.status(500).send('Error attaching registration to your user');
+                  } else {
+                    res.render('registration/registration_success', {regfee: regfee});
+                  }
+              });
+            }
+        });
+      } else {
+        // Update
+        console.log("UPDATING");
+        reg.irc = reg_info.irc;
+        reg.is_public = reg_info.is_public;
+        reg.gender = reg_info.gender;
+        reg.country = reg_info.country;
+        reg.save().complete(function (err, reg){
+          if(!!err) {
+            res.render('registration/register', { registration: reg_info, genders: all_genders,
+                                                  save_error: true });
+          } else {
+            res.render('registration/update_success');
+          }
+        });
+      }
+    }
+  });
+});
+
+
+router.all('/admin/list', utils.require_user);
+router.all('/admin/list', utils.require_permission('registration/view_all'));
+router.get('/admin/list', function(req, res, next) {
+  next();
+});
 
 module.exports = router;
diff --git a/utils.js b/utils.js
index 977da57..466206e 100644
--- a/utils.js
+++ b/utils.js
@@ -69,12 +69,23 @@ utils.require_login = function(req, res, next) {
   }
 };
 
-utils.require_user = function(req, res, next) {
+utils.require_feature = function(feature) {
+  if(config[feature]['enabled']) {
+    return function(req, res, next) {
+      next();
+    }
+  } else {
+    return function(req, res, next) {
+      res.render('auth/no_permission', { required_permission: 'Feature disabled' });
+    }
+  }
+};
+
+utils.get_user = function(req, res, next) {
   if(!req.session.currentUser) {
-    res.render('auth/no_permission', { required_permission: 'Login' });
+    next();
     return;
   }
-
   User.find({
     where: {
       email: req.session.currentUser
@@ -83,15 +94,28 @@ utils.require_user = function(req, res, next) {
   .complete(function(err, user) {
     if(!!err) {
       res.status(500).send('Error retrieving user object');
-    } else if(!user) {
-      // Redirect to register
-      res.redirect(302, '/auth/register?origin=' + req.originalUrl);
-    } else {
+    } else if(user) {
       req.user = user;
       res.locals.user = user;
       next();
+    } else {
+      next();
     }
   });
+}
+
+utils.require_user = function(req, res, next) {
+  if(!req.session.currentUser) {
+    res.render('auth/no_permission', { required_permission: 'Login' });
+    return;
+  }
+
+  if(!req.user) {
+    // Redirect to register
+    res.redirect(302, '/auth/register?origin=' + req.originalUrl);
+  } else {
+      next();
+  }
 };
 
 module.exports = utils;
diff --git a/views/index/index.hbs b/views/index/index.hbs
index e29a222..c0a1a1e 100644
--- a/views/index/index.hbs
+++ b/views/index/index.hbs
@@ -28,8 +28,27 @@ Hello, please login, or choose an option below.
 {{#if config.registration.enabled}}
 
 <h2>Registration</h2>
-.....
-
+{{#has_permission "registration/view_public"}}
+<div class="button"><a href="/registration/list">Current registrations</a></div>
+{{/has_permission}}
+<div class="buttons">
+{{#has_permission "registration/register" }}
+{{#if registration}}
+<h3>Registration ID: {{registration.display_id}}</h3>
+<div class="button"><a href="/registration/register">Update registration</a></div>
+{{#ifGTE registration.paid config.registration.min_amount_for_receipt}}
+<div class="button"><a href="/registration/receipt">Get receipt</a></div>
+{{else}}
+Not enough paid for receipt (Paid {{config.registration.currency_symbol}}{{registration.paid}}, outstanding 
for onsite payment: {{config.registration.currency_symbol}}{{registration.outstanding}})
+{{/ifGTE}}
+{{#has_permission "registration/pay_extra"}}
+<div class="button"><a href="/registration/pay">Pay more</a></div>
+{{/has_permission}}
+{{else}}
+<div class="button"><a href="/registration/register">Register</a></div>
+{{/if}}
+{{/has_permission}}
+</div>
 {{/if}}
 
 
@@ -51,6 +70,9 @@ Hello, please login, or choose an option below.
 
 <h3>Registration</h3>
 <div class="buttons">
+{{#has_permission "registration/view_all" }}
+<div class="button"><a href="/registration/admin/list">List all registrations</a></div>
+{{/has_permission}}
 </div>
 
 {{/has_permission}}
diff --git a/views/partials/registration/payment.hbs b/views/partials/registration/payment.hbs
new file mode 100644
index 0000000..2b6c9c1
--- /dev/null
+++ b/views/partials/registration/payment.hbs
@@ -0,0 +1,10 @@
+Your selected registration fee: {{config.registration.currency_symbol}}{{regfee}}.
+<br />
+Please select how you wish to pay:<br />
+Paypal: $INSERT_PAYPAL_BUTTON_HERE<br />
+On-site: 
+<form action="/registration/pay/do" method="POST">
+<input type="hidden" name="method" value="onsite">
+<input type="hidden" name="regfee" value="{{regfee}}">
+<input type="submit" value="Pay on-site">
+</form><br />
diff --git a/views/registration/list.hbs b/views/registration/list.hbs
new file mode 100644
index 0000000..31ceb33
--- /dev/null
+++ b/views/registration/list.hbs
@@ -0,0 +1,6 @@
+<table>
+<tr><th>IRC Nick</th></tr>
+{{#each registrations}}
+<tr><td>{{this.irc}}</td></tr>
+{{/each}}
+</table>
diff --git a/views/registration/pay.hbs b/views/registration/pay.hbs
new file mode 100644
index 0000000..8105a2a
--- /dev/null
+++ b/views/registration/pay.hbs
@@ -0,0 +1,20 @@
+<form action="/registration/pay" method="post">
+<table class="submission-form">
+<tr><td>Your name:</td><td><input type="text" name="submitter_name" value="{{user.name}}" disabled 
/></td></tr>
+<tr><td>Amount:</td><td>
+{{#each config.registration.fee_options}}
+<input type="radio" name="regfee" value="{{this}}" id="regfee_{{this}}">
+<label for="regfee_{{this}}">{{../config.registration.currency_symbol}}{{this}}</label>
+<br />
+{{/each}}
+<input type="radio" name="regfee" value="custom">
+<input type="text" name="regfee_custom">
+</td>
+<td>NOTE: Only registrations with a fee of at least 
{{config.registration.currency_symbol}}{{config.registration.min_amount_for_receipt}} can get a receipt! (you 
can always pay more)
+</td></tr>
+
+<tr><td>&nbsp;</td>
+<td><input class="submit-talk" type="submit" value="Submit" /></td>
+</tr>
+</table>
+</form>
diff --git a/views/registration/pay_do.hbs b/views/registration/pay_do.hbs
new file mode 100644
index 0000000..f506fa1
--- /dev/null
+++ b/views/registration/pay_do.hbs
@@ -0,0 +1 @@
+{{>registration/payment}}
diff --git a/views/registration/payment_onsite_registered.hbs 
b/views/registration/payment_onsite_registered.hbs
new file mode 100644
index 0000000..1c7bebb
--- /dev/null
+++ b/views/registration/payment_onsite_registered.hbs
@@ -0,0 +1,2 @@
+Your payment has been marked as to be completed onsite, thank you!<br />
+<a href="/">Return</a>
diff --git a/views/registration/register.hbs b/views/registration/register.hbs
new file mode 100644
index 0000000..c5cb5e9
--- /dev/null
+++ b/views/registration/register.hbs
@@ -0,0 +1,53 @@
+{{#if submission_error}}
+<font color="red"><b>Something went wrong submitting. Please check you filled in all fields!</b></font>
+{{/if}}
+
+<form action="/registration/register" method="post">
+<table class="submission-form">
+<tr><td>Your name:</td><td><input type="text" name="submitter_name" value="{{user.name}}" disabled 
/></td></tr>
+<tr><td>IRC nickname:</td><td><input type="text" name="irc" value="{{registration.irc}}"></td></tr>
+<input type="hidden" name="is_public" value="false">
+<tr><td>Registration public:</td><td><input type="checkbox" value="true" name="is_public" 
+{{#if registration.is_public}}
+checked
+{{/if}}
+></td></tr>
+
+<tr><td>Registration fee:</td><td>
+{{#unless registration.is_not_saved}}
+Registered
+{{else}}
+{{#each config.registration.fee_options}}
+<input type="radio" name="regfee" value="{{this}}" id="regfee_{{this}}">
+<label for="regfee_{{this}}">{{../config.registration.currency_symbol}}{{this}}</label>
+<br />
+{{/each}}
+<input type="radio" name="regfee" value="custom">
+<input type="text" name="regfee_custom">
+</td>
+<td>NOTE: Only registrations with a fee of at least 
{{config.registration.currency_symbol}}{{config.registration.min_amount_for_receipt}} can get a receipt! (you 
can always pay more)
+{{/unless}}
+</td></tr>
+
+<tr><td colspan="2">Always private info for stats:</td></tr>
+<tr><td>Gender:</td><td>
+{{#each genders}}
+<input type="radio" name="gender" value="{{this}}" id="gender_{{this}}"
+{{#ifEqual ../registration/gender this}}
+checked
+{{/ifEqual}}
+>
+<label for="gender_{{this}}">{{this}}</label>
+{{/each}}
+</td></tr>
+
+<tr><td>Country:</td><td><input type="text" name="country" value="{{registration.country}}"></td></tr>
+
+
+
+<tr><td>&nbsp;</td>
+<td><input class="submit-talk" type="submit" value="Submit" /></td>
+</tr>
+
+</table>
+</form>
diff --git a/views/registration/registration_success.hbs b/views/registration/registration_success.hbs
new file mode 100644
index 0000000..6ea630b
--- /dev/null
+++ b/views/registration/registration_success.hbs
@@ -0,0 +1,3 @@
+Your registration was recorded, thank you!<br />
+
+{{>registration/payment}}
diff --git a/views/registration/update_success.hbs b/views/registration/update_success.hbs
new file mode 100644
index 0000000..513cdfa
--- /dev/null
+++ b/views/registration/update_success.hbs
@@ -0,0 +1,2 @@
+Your registration was updated, thank you!<br />
+<a href="/">Return</a>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]