From e582c3b84c6090b08f33d599c354968fcd868d14 Mon Sep 17 00:00:00 2001 From: Dennis Eriksen Date: Sat, 25 May 2013 11:17:42 +0200 Subject: moved and renamed files to match the filesetup we want. havebeen using a dev-setup till now. --- app/controllers/system.js | 329 +++++++++++++++++++++++++++++++++++++ app/controllers/users.js | 282 +++++++++++++++++++++++++++++++ app/models/Access.js | 129 +++++++++++++++ app/models/Project.js | 75 +++++++++ app/models/User.js | 182 ++++++++++++++++++++ app/models/pPost.js | 78 +++++++++ app/views/contact.ejs | 17 ++ app/views/dashboard.ejs | 154 +++++++++++++++++ app/views/error.ejs | 9 + app/views/faq.ejs | 115 +++++++++++++ app/views/home.ejs | 164 ++++++++++++++++++ app/views/index.ejs | 131 +++++++++++++++ app/views/project/newProject.ejs | 79 +++++++++ app/views/project/participants.ejs | 55 +++++++ app/views/project/post.ejs | 103 ++++++++++++ app/views/project/project.ejs | 220 +++++++++++++++++++++++++ app/views/template.ejs | 18 ++ app/views/templates/footer.ejs | 20 +++ app/views/templates/header.ejs | 16 ++ app/views/templates/navbar.ejs | 73 ++++++++ app/views/test.ejs | 46 ++++++ app/views/users/registerEmail.ejs | 43 +++++ app/views/users/signup.ejs | 86 ++++++++++ 23 files changed, 2424 insertions(+) create mode 100644 app/controllers/system.js create mode 100644 app/controllers/users.js create mode 100644 app/models/Access.js create mode 100644 app/models/Project.js create mode 100644 app/models/User.js create mode 100644 app/models/pPost.js create mode 100644 app/views/contact.ejs create mode 100644 app/views/dashboard.ejs create mode 100644 app/views/error.ejs create mode 100644 app/views/faq.ejs create mode 100644 app/views/home.ejs create mode 100644 app/views/index.ejs create mode 100644 app/views/project/newProject.ejs create mode 100644 app/views/project/participants.ejs create mode 100644 app/views/project/post.ejs create mode 100644 app/views/project/project.ejs create mode 100644 app/views/template.ejs create mode 100644 app/views/templates/footer.ejs create mode 100644 app/views/templates/header.ejs create mode 100644 app/views/templates/navbar.ejs create mode 100644 app/views/test.ejs create mode 100644 app/views/users/registerEmail.ejs create mode 100644 app/views/users/signup.ejs (limited to 'app') diff --git a/app/controllers/system.js b/app/controllers/system.js new file mode 100644 index 0000000..0e63083 --- /dev/null +++ b/app/controllers/system.js @@ -0,0 +1,329 @@ + +/** + * Module dependencies + */ +var mongoose = require('mongoose') + , env = process.env.NODE_ENV || 'development' + , config = require('../../config/config.js')[env] + , Project = mongoose.model('Project') + , Access = mongoose.model('Access') + , User = mongoose.model('User') + , pPost = mongoose.model('pPost') + , Validator = require('validator').Validator + , v = new Validator() + , sanitize = require('validator').sanitize; + + +// validation error handling. This collects all errors before pushing them out in getErrors() +Validator.prototype.error = function(msg) { + this._errors.push(msg); + return this; +} +Validator.prototype.getErrors = function() { + var returnThis = this._errors; + this._errors = ''; // need to reset errors between sessions because of object model + return returnThis; +} + +/** + * Before the user log in + * =============================================================== +*/ + +exports.index = function(req, res) { + if (req.user !== undefined) { return res.redirect('/dashboard'); } + res.render('index', { title: 'DERS', user: req.user }); + }; + + +exports.faq = function(req, res) { + res.render('faq', { + title: 'faq', + user: req.user + }); +} + + +exports.contact = function(req, res) { + res.render('contact', { + title: 'contact', + user: req.user + }); +} + + +/** + * After the user has logged in + * =============================================================== +*/ + + + +exports.dashboard = function(req, res) { + +/* + Access.find({ user: req.user._id }, function(err, accesses) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + console.log('accesses ' + accesses); + accesses.forEach(function(access) { + Project.load(access.project, function(err, project) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + projectList.push(project); + console.log(project.user.username); + }); + }); + }); +*/ + if (req.user.status < 3) { + if (req.header('Referer') === undefined) { return res.status(403).render('error', { title: 403, text: 'Du har ikke tilgang til denne siden. Du må registrere deg først. Sjekk mailen din for å se invitekode.' }); } + else { return res.redirect('back'); } + } + + Access.loadUser(req.user._id, function(err, projects) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + var projectIDs = []; + var pro = { project: [] }; + projects.forEach(function(project) { + projectIDs.push(project.project._id); + pro.project[project.project._id] = { + total: 0 // total for project + , user: 0 // what req-user has spent on project + , users: 0 // number of users on project + }; + }); + Access.loadProjects(projectIDs, function(err, participants) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + participants.forEach(function(p) { + pro.project[p.project].users++; + }); + pPost.loadByProjects(projectIDs, function(err, posts) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + Access.loadProjects(projectIDs, function(err, participants) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + + // FUN FUN FUN CALCULATIONS + + posts.forEach(function(p) { + if (String(p.user._id) === String(req.user._id)) pro.project[p.project._id].user += p.value; + pro.project[p.project._id].total += p.value; + }); + + res.render('dashboard', { + title: 'Dashboard' + , user: req.user + , projects: projects + , posts: posts + , participants: participants + , pro: pro + }); + }); + }); + /* res.render('dashboard', { + title: 'Dashboard', + user: req.user, + projects: projects + }); +*/ + }); + }); + +/* + Project.find(function(err, projects) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + res.render('dashboard', { + title: 'Dashboad', + user: req.user, + projects: projects + }); + });*/ +} + + + +exports.project = function(req, res) { + Project.loadShort(req.params.short, function(err, project) { + if (err || !project) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err }); + Access.loadProject(project._id, function(err, access) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + pPost.loadProject(project._id, function(err, posts) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + + // ALRIGHT! This is where the FUN starts! + + // first we create an object that will hold all the calculational data + var pro = { + users: 0 // number of users + , user: [] // this array will contain every user. Every user will then have it's own object inside this. + , total: 0 // the overall total. + , each: 0 // what each person has to pay + , otot: 0 // how much is owned in total! + }; + + // then we calculate how many users we have, and initiate objects foreach user + access.forEach(function(a) { + if (String(a.user._id) === String(req.user._id)) req.user.permissions = a.permissions; //sets YOUR permissions in this project + + pro.users++; + pro.user[a.user._id] = { + total: 0 + , diff: 0 + , coeff: 0 // the coefficient of hom much you are owned + , name: a.user.name + }; + }); + + // now we must collect all the money! + posts.forEach(function(p) { + pro.total += parseFloat(p.value); + pro.user[p.user._id].total += parseFloat(p.value); + }); + + // then calculate how much each user must pay in total + pro.each = pro.total / pro.users; + + // then calculate how much each person owe and is owned + for(var i in pro.user) { + pro.user[i].diff = parseFloat(pro.user[i].total - pro.each).toFixed(2); + if (pro.user[i].diff > 0) pro.otot += parseFloat(pro.user[i].diff); + } + for (var i in pro.user) { + if (pro.user[i].diff > 0) pro.user[i].coeff = pro.user[i].diff / pro.otot; + } + console.log(pro); + res.render('project/project', { + title: project.name + , user: req.user + , req: req + , project: project + , access: access + , posts: posts + , pro: pro + }); + }); + }); + + }); +} + + +exports.projectParticipants = function(req, res) { + if (req.user.status < 3) { + if (req.header('Referer') === undefined) { return res.status(403).render('error', { title: 403, text: 'Du har ikke tilgang til denne siden. Du må registrere deg først. Sjekk mailen din for å se invitekode.' }); } + else { return res.redirect('back'); } + } + res.render('project/participants', { title: 'Prosjektdeltakere', user: req.user }); + +} + + +exports.projectPost = function(req, res) { + + /** ################################### + * Need to check if user has access to this project!! + */ + Project.loadShort(req.params.short, function(err, project) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + req.project = project; + res.render('project/post', { title: 'Legg til utgift', user: req.user, req: req, project: project }); + }); + + + +} + +exports.postProjectPost = function(req, res) { + + // Validation + v.check(req.body.project, 'The project was lost').notEmpty(); + v.check(req.body.what, 'You need to fill in the what-field').notEmpty(); + v.check(req.body.value, 'The value must be a positive number').notEmpty().isInt().min(0); + + // error when validation fails + var errors = v.getErrors(); + if (errors.length !== 0) return res.status(500).render('error', { title: '500', text: 'Det oppstod en valideringsfeil ' + errors, error: errors }); + + Project.loadShort(req.params.short, function(err, project) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + + // check if access + Access.checkAccess(req.user._id, project._id, 0, function(err, access) { + if (err || !access) return res.status(403).render('error', { title: '403', text: 'no sir.' }); + + // Time to fill in the model! + var ppost = new pPost(); + + ppost.user = req.user._id; + ppost.for = req.user._id; + ppost.project = project._id; + ppost.what = sanitize(req.body.what).escape(); + ppost.comment = sanitize(req.body.comment).xss(); // xss will remove cross-site-scripting in the textfield. + ppost.participants = sanitize(req.body.participants).escape(); + ppost.value = sanitize(req.body.value).toInt(); // this will remove leading zeroes. '0123' => '123' + ppost.when = new Date(sanitize(req.body.date).escape() + ' ' + sanitize(req.body.time).escape() + ':00'); + ppost.save(function(err) { + if (err) return res.render('project/post', { title: 'Legg til utgift - en feil oppstod', user: req.user, req: req, project: project }); + return res.redirect('/project/' + project.shortURL); + }); + }); + }); +} +exports.newProject = function(req, res) { + if (req.user.status < 3) { + if (req.header('Referer') === undefined) { return res.status(403).render('error', { title: 403, text: 'Du har ikke tilgang til denne siden. Du må registrere deg først. Sjekk mailen din for å se invitekode.' }); } + else { return res.redirect('back'); } + } + + res.render('project/newProject', { title: 'Nytt prosjekt', user: req.user }); +} + +exports.postNewProject = function(req, res) { + if (req.user.status < 3) { + if (req.header('Referer') === undefined) { return res.status(403).render('error', { title: 403, text: 'Du har ikke tilgang til denne siden. Du må registrere deg først. Sjekk mailen din for å se invitekode.' }); } + else { return res.redirect('back'); } + } + + var project = new Project(req.body); + project.user = req.user._id; + project.save(function(err) { + if (err) { + console.log(err.errors); + return res.render('project/newProject', { title: 'Nytt prosjekt - en feil oppstod', user: req.user, errors: err.errors, project: project }); + } + var access = new Access(); + access.user = req.user._id; + access.creator = req.user._id; + access.project = project._id; + access.permissions = 9; + access.save(function(err) { + if (err) { + console.log(err.errors); + return res.render('project/newProject', { title: 'Nytt prosjekt - en feil oppstod', user: req.user }); + } + return res.redirect('/dashboard'); + }); + }); + +} + + +exports.deleteProjectPost = function(req, res) { + Project.findOne({ shortURL: req.params.short }).select('_id').exec(function(err, project) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + Access.findOne({project: project._id, user: req.user._id}, function(err, access) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + if (!access) return res.status(403).render('error', { title: '403', text: 'Du har ikke tilgang til å gjøre dette' }); + pPost.load(req.params.post, function(err, post) { + if (err || !post) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err }); + if (post.user._id === req.user._id || access.permissions >= 6) { + pPost.remove({ _id: post._id }, function(err) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + console.log('deleted post ' + post._id); + return res.redirect('back'); + }) + } else { return res.status(403).render('error', { title: '403', text: 'Du har ikke tilgang til å gjøre dette' }); } + }) + }) + }); +} + + diff --git a/app/controllers/users.js b/app/controllers/users.js new file mode 100644 index 0000000..725fa3e --- /dev/null +++ b/app/controllers/users.js @@ -0,0 +1,282 @@ + +/** + * Module dependencies + */ + +var mongoose = require('mongoose') + , User = mongoose.model('User') + , Project = mongoose.model('Project') + , Access = mongoose.model('Access') + , env = process.env.NODE_ENV || 'development' + , config = require('../../config/config')[env] + , Validator = require('validator').Validator + , v = new Validator() + , sanitize = require('validator').sanitize; + +// validation error handling. This collects all errors before pushing them out in getErrors() +Validator.prototype.error = function(msg) { + this._errors.push(msg); + return this; +} +Validator.prototype.getErrors = function() { + var returnThis = this._errors; + this._errors = ''; // need to reset errors between sessions because of object model + return returnThis; +} + + +/** + * Logout + */ +exports.logout = function(req, res) { + req.logout(); + res.redirect('/'); +} + + +/** + * Signin + * This is triggered when the user post to /login + */ +exports.signin = function(req, res) { + res.redirect('/dashboard'); +} + +exports.randomLogin = function(req, res) { + Access.findOne({ randomToken: req.params.hash }).populate('project', 'shortURL').exec(function(err, access) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + return res.redirect('/project/' + access.project.shortURL); + + }); +} + + +/** + * Signup + */ +exports.signup = function(req, res) { + res.render('users/signup', { title: 'Registrer deg', invite: false }); +} + +/** + * Create users + */ +exports.create = function(req, res) { + var user = new User(req.body); + user.provider = 'local'; + user.save(function(err) { + if (err) return res.render('users/signup', { errors: err.errors, user: user }); + req.logIn(user, function(err) { + if (err) return next(err); + return res.redirect('/dashboard'); + }); + }); +} + + +/** + * AuthCallback + * This is what happends when a user has signed in using facebook/twitter + */ + +exports.authCallback = function(req, res, next) { + // if the user hasn't registered an email, we need to do so + if (!req.user.email || req.user.email === undefined) return res.redirect('/registerEmail'); + + res.redirect('/dashboard'); +} + + +/** + * registerEmail + * Will register the users email if they don't have already + */ + +exports.registerEmail = function(req, res) { + // in case some user who has alreadu registered an email gets on this page + if (req.user.email !== undefined) return res.redirect('/dashboard'); + res.render('users/registerEmail', { title: 'Registrer din e-post' }); +} + + +/** + * postRegisterEmail + */ + +exports.postRegisterEmail = function(req, res) { + + v.check(req.body.email, 'You need to supply a proper email').isEmail(); + var errors = v.getErrors(); + if (errors.length !== 0) return res.status(500).render('error', { title: '500', text: 'Det oppstod en valideringsfeil
' + errors, error: errors }); + + // first we need to check if the email is already in use + User.findOne({ email: req.body.email }, function(err, user) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + + // if mail is in use.. + if (user) return res.render('users/registerEmail', { title: 'Den e-posten er allerede i bruk. Vennligs registrer en annen.' }); + + User.update({ _id: req.user._id }, { email: req.body.email, status: 3 }, function(err) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + return res.redirect('/dashboard'); + }); + }); +} + + +/** + * postProjectParticipants + * This callback is in this file because it treats users. + */ +exports.postProjectParticipants = function(req, res) { + Project.loadShort(req.params.short, function(err, project) { + if (err || !project) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + + Access.checkAccess(req.user._id, project._id, 3, function(err, access) { + if (err || !access) return res.status(403).render('error', { title: '403', text: 'No sir! NO ACCESS FOR YOU', error: err }); + + // validate + var emails = sanitize(req.body.emails).xss(); + v.check(emails, 'You need to enter some emails to invite someone').notEmpty(); + //var emails = sanitize(req.body.emails).xss(); + emails = emails.split('\r\n'); + emails.forEach(function(m) { // m = each mailaddress + if (m) { + v.check(m, m + ' is not a valid email').isEmail(); + } + }); + + // error when validation fails + var errors = v.getErrors(); + if (errors.length !== 0) return res.status(500).render('error', { title: '500', text: 'Det oppstod en valideringsfeil
' + errors, error: errors }); + + // EMAIL + // Require dependencies. We require them here so that they're not fetched until they're actually needed. + var email = require('emailjs') + , server = email.server.connect(config.email) + , message = { + subject: 'You were invited to use Divid', + text: 'VIL DU BRUK DIVID?', + from: 'Divid ', + } + emails.forEach(function(mailAddress) { // loops through all the emails and sets up each user + User.loadUser(mailAddress, function(err, user) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + if (!user) { //if the user doesn't exist, create one + console.log('fant ingen brukere med den eposten. må invitere og stasj'); + var newUser = new User(); + newUser.email = mailAddress; + newUser.username = mailAddress; + newUser.name = mailAddress + ' (ikke registrert)'; + newUser.status = 1; + newUser.password = newUser.generateRandomToken(32); + newUser.randomToken = newUser.generateRandomToken(10, true); + newUser.save(function(err) { + if (err) return res.render('project/participants', { title: 'Nytt prosjekt - en feil oppstod', loggedin: true }); + console.log('made new user ' + newUser._id); + var access = new Access(); + access.user = newUser._id; + access.creator = req.user._id; + access.project = project._id; + access.randomToken = access.generateRandomToken(15); + access.save(function(err) { + if (err) { + console.log(err.errors); + return res.render('project/participants', { title: 'Nytt prosjekt - en feil oppstod', loggedin: true }); + } + console.log('made new access for user ' + newUser._id); + message.to = newUser.email; + message.text = 'Hei! Du har blitt invitert til å delta i et Divid-prosjekt! https://divid.no/invite/' + newUser.randomToken + '\n Du kan også gå direkte til prosjektet her: https://divid.no/login/' + access.randomToken; + server.send(message, function(err, message) { console.log(err || message);}); + }); + }); + + } else { // if the user exists, add him to the project + Access.checkAccess(user._id, project._id, 0, function(err, acc) { + if (err) return res.render('project/participants', { title: 'Nytt prosjekt - en feil oppstod', loggedin: true }); + if (acc) { // if the user already has access to the project.. do nothing + console.log('user ' + user.email + ' already has access to project ' + project.name); + } else { + console.log('fant en bruker. må lage ny access til han og si i fra.'); + var access = new Access(); + access.user = user._id; + access.creator = req.user._id; + access.project = project._id; + message.text = 'Du ble lagt til projektet "' + project.name + '"'; + if (Number(user.status) < 3) { + access.randomToken = access.generateRandomToken(15); + message.text += '.\nDu kan få direkte tilgang til dette prosjektet her: https://divid.no/login/' + access.randomToken + ' \nDu kan bruke denne linken for å registrere deg, for å få tilgang til flere funksjoner: https://divid.no/invite/' + user.randomToken; + } + access.save(function(err) { + if (err) { + console.log(err.errors); + return res.render('project/participants', { title: 'Nytt prosjekt - en feil oppstod', loggedin: true }); + } + console.log('made new access for user ' + user.username); + message.to = user.email; + server.send(message, function(err, message) { console.log(err || message);}); + }); + } + }); + } + }); + }); + + res.redirect('back'); + }); + }); +} + + +/** + * claimInvite + * So users can use their inviteEmail + */ + +exports.claimInvite = function(req, res) { + + // first we need to check if the invite is valid! + User.findOne({ randomToken: sanitize(req.params.randomToken).escape(), status: 1 }, function(err, user) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + if (!user) return res.render('error', { title: 'This invite does not exist', text: 'Invitasjonen din er ugyldig' }); + + res.render('users/signup', { + invite: true, + title: 'Registrer deg!', + email: user.email } + ); + }); + + +} + + +exports.postClaimInvite = function(req, res) { + + User.findOne({ randomToken: sanitize(req.params.randomToken).escape(), status: 1 }, function(err, user) { + if (err) return res.status(500).render('error', { title: '500', text: 'En serverfeil oppstod', error: err.stack }); + if (!user) return res.render('error', { title: 'This invite does not exist', text: 'Invitasjonen din er ugyldig' }); + + v.check(req.body.password).notEmpty(); + v.check(req.body.name).notEmpty(); + v.check(req.body.username).notEmpty(); + + errors = v.getErrors(); + if (errors.length !== 0) return res.status(500).render('error', { title: '500', text: 'Det oppstod en valideringsfeil
' + errors, error: errors }); + + user.name = sanitize(req.body.name).escape(); + user.username = sanitize(req.body.username).escape(); + user.password = req.body.password; + user.provider = 'local'; + user.status = 3; + user.randomToken = ''; + user.save(function(err) { + if (err) return res.render('signup', { errors: err.errors, user: user }); + req.logIn(user, function(err) { + if (err) return next(err); + return res.redirect('/dashboard'); + }); + }); + }); +} + diff --git a/app/models/Access.js b/app/models/Access.js new file mode 100644 index 0000000..b9570e6 --- /dev/null +++ b/app/models/Access.js @@ -0,0 +1,129 @@ + +/** + * Module dependencies + */ + +var mongoose = require('mongoose') + , Schema = mongoose.Schema; + + +/** + * Schema + * + * Permissions: + * 3 = normal + * 6 = admin + * 9 = owner + * These permissions are set in steps of three, in case + * we need to add more permissions later. + */ + +var AccessSchema = new Schema({ + user: { type: Schema.ObjectId, ref: 'User' }, + creator: { type: Schema.ObjectId, ref: 'User' }, + project: { type: Schema.ObjectId, ref: 'Project' }, + permissions: { type: Number, default: '3' }, + randomToken: { type: String }, + created: { type: Date, default: Date.now }, + updated: { type: Date, default: Date.now } +}); + + +// the four validations below only apply if you are signing up traditionally + +AccessSchema.methods = { + + /** + * Generate random access token for Remember Me function + * + * @param {Number} length + * @return {String} + * @api public + */ + + generateRandomToken: function(length) { + if (typeof(length) === undefined) length = 16; // default length of token + var chars = '_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' + , token = ''; + for (var i = 0; i < length; i++) { + var x = Math.floor(Math.random() * chars.length); + token += chars.charAt(x); + } + console.log('token ' + token); + return token; + } + +} + +AccessSchema.statics = { + + /** + * Load ALL accesses for a single user + * + * @param {ObjectId} id + * @param {Function} callback + * @api private + */ + + loadUser: function(id, callback) { + this.find({ user: id }) + .populate('project') + .sort({ 'created': -1 }) // sort by date + .exec(callback); + }, + + + /** + * Load all users associated with a project + * + * @param {ObjectId} project + * @param {Function} callback + * @api private + */ + + loadProject: function(project, callback) { + this.find({ project: project }) + .populate({path: 'user', select: '_id name'}) + .sort({ 'created': 1 }) // sort by date + .exec(callback); + }, + + + /** + * Load all users associated with several projects + * + * @param {Arrau[ObjectId]} projects + * @param {Function} callback + * @api private + */ + + loadProjects: function(projects, callback) { + this.find({ project: { $in: projects } }) + .populate({ path: 'user', select: '_id name' }) + .sort({ 'created': -1 }) + .exec(callback); + }, + + + /** + * Check to see if user has access to a particular project + * + * @param {ObjectId} user + * @param {ObjectId} project + * @param {Number} permissisons + * @param {Function} callback + * @api private + */ + + checkAccess: function(user, project, permissions, callback) { + if (typeof(permissions) === 'undefined') permissions = 0; + console.log('inni checkPermissions!') + this.findOne({ user: user }) + .where('project').equals(project) + .where('permissions').gte(permissions) + .exec(callback); + } + +} + +mongoose.model('Access', AccessSchema); diff --git a/app/models/Project.js b/app/models/Project.js new file mode 100644 index 0000000..ba0e8e7 --- /dev/null +++ b/app/models/Project.js @@ -0,0 +1,75 @@ + +/** + * Module dependencies + */ + +var mongoose = require('mongoose') + , Schema = mongoose.Schema; + +var ProjectSchema = new Schema({ + user: { type: Schema.ObjectId, ref: 'User' }, + name: { type: String, default: '', trim: true }, + description: {type: String, default: '', trim: true }, + currency: { type: String, default: 'kr', trim: true }, + public: { type: String, default: 'invite-only' }, + created: { type: Date, default: Date.now }, + updated: { type: Date, default: Date.now }, + shortURL: { type: String, unique: true } +}); + +// the four validations below only apply if you are signing up traditionally + +ProjectSchema.path('name').validate(function(name) { + // if you're authenticated by any of the oauth strategies (facebook, twitter), don't validate + return name.length; +}, 'Project name cannot be blank'); + + +ProjectSchema.pre('save', function(next) { + if (this.shortURL !== undefined) return next(); + var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + this.shortURL = ''; + for (var i = 0; i < 6; i++) { + var x = Math.floor(Math.random() * chars.length); + this.shortURL += chars.charAt(x); + } + console.log('SHORT: ' + this.shortURL); + next(); +}); + + + +ProjectSchema.statics = { + + /** + * Find project by id + * + * @param {ObjectId} id + * @param {Function} callback + * @api private + */ + + load: function(id, callback) { + this.findOne({ _id: id }) + .populate('user') + .exec(callback); + }, + + + /** + * Find project my shortURL + * + * @param {shortURL} shortURL + * @param {Function} callback + * @api private + */ + + loadShort : function(shortURL, callback) { + this.findOne({ shortURL: shortURL }) + .populate('user') + .exec(callback); + } + +} + +mongoose.model('Project', ProjectSchema); diff --git a/app/models/User.js b/app/models/User.js new file mode 100644 index 0000000..0dc38f3 --- /dev/null +++ b/app/models/User.js @@ -0,0 +1,182 @@ + +/** + * Module dependencies + */ + +var mongoose = require('mongoose') + , Schema = mongoose.Schema + , crypto = require('crypto') + , authTypes = ['facebook', 'twitter']; + + +/** + * User schema + * + * statuscodes: + * 1: invited + * 2: unconfirmed + * 3: active + * 4: paying user + */ + +var UserSchema = new Schema({ + name: String, + email: { type: String, unique: true }, + username: String, + provider: String, + hashed_password: String, + salt: String, + accessToken: String, + facebook: {}, + twitter: {}, + status: { type: Number, default: 2 }, + randomToken: String, + created: { type: Date, default: Date.now }, + updated: { type: Date, default: Date.now } +}); + + +/** + * Virtuals + */ + +UserSchema + .virtual('password') + .set(function(password) { + this._password = password + this.salt = this.makeSalt() + this.hashed_password = this.encryptPassword(password) + }).get(function() { return this._password }); + + +/** + * Validations + */ + +var validatePrecenceOf = function(value) { + return value && value.length; +} + +// the four validations below only apply if you are signing up traditionally + +UserSchema.path('name').validate(function(name) { + // if you're authenticated by any of the oauth strategies (facebook, twitter), don't validate + if(authTypes.indexOf(this.provider) !== -1 || this.status === 1) return true; + return name.length; +}, 'Name cannot be blank'); + +UserSchema.path('email').validate(function(email) { + if(authTypes.indexOf(this.provider) !== -1) return true; + return email.length; +}, 'Email cannot be blank'); + +UserSchema.path('username').validate(function(username) { + if(authTypes.indexOf(this.provider) !== -1 || this.status === 1) return true; + return username.length; +}, 'Username cannot be blank'); + +UserSchema.path('hashed_password').validate(function(hashed_password) { + if(authTypes.indexOf(this.provider) !== -1) return true; + return hashed_password.length; +}, 'Password cannot be blank'); + + +/** + * Pre-save hook + */ + +UserSchema.pre('save', function(next) { + if (!this.isNew || this.status === 1) return next(); + + this.updated = Date.now(); + next(); + +}); + + +/** + * Methods + */ + +UserSchema.methods = { + + /** + * Authenticate - check if passwords are the same + * + * @param {String} plainText + * @return {Bolean} + * @api public + */ + + authenticate: function(plainText) { + return this.encryptPassword(plainText) === this.hashed_password; + }, + + + /** + * Make salt + * + * @return {String} + * @api public + */ + + makeSalt: function() { + return Math.round((new Date().valueOf() * Math.random())) + ''; + }, + + + /** + * Encrypt password + * + * @param {String} password + * @return {String} + * @api public + */ + + encryptPassword: function(password) { + if (!password) return ''; + return crypto.createHmac('sha1', this.salt).update(password).digest('hex'); + }, + + + /** + * Generate random access token for Remember Me function + * + * @param {Number} length + * @param {Boolean} noDate + * @return {String} + * @api public + */ + + generateRandomToken: function(length, noDate) { + if (typeof(length) === undefined) length = 16; // default length of token + var chars = '_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' + , token = noDate ? '' : new Date().getTime() + '_'; + for (var i = 0; i < length; i++) { + var x = Math.floor(Math.random() * chars.length); + token += chars.charAt(x); + } + return token; + } +} + +UserSchema.statics = { + + /** + * Load user from their email address + * + * @param {String} email + * @param {Function} callback + * @api private + */ + + loadUser: function(email, callback) { + this.findOne({ email: email }) + .exec(callback); + } + +} + +mongoose.model('User', UserSchema); + + diff --git a/app/models/pPost.js b/app/models/pPost.js new file mode 100644 index 0000000..1f53984 --- /dev/null +++ b/app/models/pPost.js @@ -0,0 +1,78 @@ + +/** + * Module dependencies + */ + +var mongoose = require('mongoose') + , Schema = mongoose.Schema; + +var pPostSchema = new Schema({ + user: { type: Schema.ObjectId, ref: 'User' }, + for: { type: Schema.ObjectId, ref: 'User' }, + project: { type: Schema.ObjectId, ref: 'Project' }, + what: { type: String, default: '', trim: true }, + comment: { type: String, default: '', trim: true }, + participants: [], + value: { type: Number, defailt: 0 }, + file: { type: String, default: '', trim: true }, + currency: { type: String, default: 'kr', trim: true }, + created: { type: Date, default: Date.now }, + updated: { type: Date, default: Date.now }, + when: { type: Date, default: Date.now } +}); + + + + +pPostSchema.statics = { + + /** + * Find post by id + * + * @param {ObjectId} id + * @param {Function} callback + * @api private + */ + + load: function(id, callback) { + this.findOne({ _id: id }) + .populate({ path: 'user', select: '_id, name'}) + .exec(callback); + }, + + + /** + * Find all posts that belong to a project, by project id + * + * @param {ObjectId} project + * @param {Function} callback + * @api private + */ + + loadProject: function(project, callback) { + this.find({ project: project }) + .populate('user') + .sort({ 'when': -1, 'created': -1 }) + .exec(callback); + }, + + + /** + * Find last ten posts belonging projects a user is part of, by project ids + * + * @param {Array[ObjectId]} projects + * @param {Function} callback + * @api private + */ + + loadByProjects: function(projects, callback) { + this.find({ project: { $in: projects } }) + .populate({ path: 'user', select: '_id name' }) + .populate({ path: 'project', select: 'name shortURL' }) + .sort({ 'when': -1, 'created': -1 }) + .exec(callback); + } + +} + +mongoose.model('pPost', pPostSchema); diff --git a/app/views/contact.ejs b/app/views/contact.ejs new file mode 100644 index 0000000..72268cb --- /dev/null +++ b/app/views/contact.ejs @@ -0,0 +1,17 @@ +<% include templates/header %> + + + + <% include templates/navbar %> + + +
+

Kontakt oss

+

Support

+

Se gjerne igjennom vår faq - ofte stilte spørsmål - før dere sender mail på support(at)divid.no.

+

Business

+

Ønsker du å kontakte oss for kjøp av annonseplass, forretninger og lignende ta kontakt på hallo(at)divid.no.

+<% include templates/footer %> + + diff --git a/app/views/dashboard.ejs b/app/views/dashboard.ejs new file mode 100644 index 0000000..f2979b3 --- /dev/null +++ b/app/views/dashboard.ejs @@ -0,0 +1,154 @@ +<% include templates/header %> + + + + <% include templates/navbar %> + + +
+
+ +
+

Prosjekter

+
+ <% if (projects.length === 0) { %> +

Du har ingen aktive prosjekt. Du kan starte et prosjekt ved å klikke her.

+ <% } else { projects.forEach(function(projects) { %> +
+
+
+

<%= projects.project.name %>

+
+
+ + <% var ppl = 'Meg'; + participants.forEach(function(participant) { + if (String(participant.project) === String(projects.project._id) && String(participant.user._id) !== String(user._id)) { + ppl += ', ' + participant.user.name; + } + }); %> + <%- ppl %> + +
+
+ <% s = (pro.project[projects.project._id].user - (pro.project[projects.project._id].total / pro.project[projects.project._id].users)).toFixed(2); %> + Oppgjør: <%- s >= 0 ? '' + s : '' + s %> <%= projects.project.currency %> +
+
+
+ +
+
+ <% }); } %> + + +
+
+ + + + + + + + +
+
+

Status total

+ +
+
+

Siste aktivitet

+
+ <% posts.forEach(function(post) { %> +
+
+
+ <% + var d = new Date(post.when); + var month = ['JAN', 'FEB', 'MAR', 'APR', 'MAI', 'JUN', 'JUL', 'AUG', 'SEP', 'OKT', 'NOV', 'DES']; + %> +
<%= d.getDate() %>
+
<%= month[d.getMonth()] %>
+ +
+
+
+
prosjekt
+ +
+
+
av
+
<%= post.user.name === undefined ? post.user.email + ' (ikke registrert)' : post.user.name %>
+
+
+
hva
+
<%= post.what %>
+
+
+
+
+ sum + <%= post.value %> <%= post.currency %> +
+
+ +
+
+
+
+
+
+
kommentar
+
+ <%= post.comment %> +
+
+
+
+
+
+
+
+
+ + <% }); %> +
+
+
+ + +
+
+ + +<% include templates/footer %> + + + + diff --git a/app/views/error.ejs b/app/views/error.ejs new file mode 100644 index 0000000..47cc546 --- /dev/null +++ b/app/views/error.ejs @@ -0,0 +1,9 @@ +<% include header %> + + + +

<%= title %>

+

<%= text %>

+ + +<% include footer %> diff --git a/app/views/faq.ejs b/app/views/faq.ejs new file mode 100644 index 0000000..d3473eb --- /dev/null +++ b/app/views/faq.ejs @@ -0,0 +1,115 @@ +<% include templates/header %> + + + + <% include templates/navbar %> + + +
+

Ofte stilte spørsmål

+ + + +

+ Hvor kan jeg få tak i appen?
+ Applikasjonen er tilgjengelig gjennom App Store. Den blir tilgjengelig gjennom Google Play og Windows Mobile Store etterhvert. +

+ +

+ Hvilke mobiltelefoner støtter Divid?
+ Divid støtter Iphone, Android, Windows mobile og Blackberry +

+ + +

+ Hvem eier dataene min?
+ Brukerenes data er brukerenes eie og de skal misbrukes. Personvernet skal opprettholdes i forhold til Norsk lov. +

+ +

+ Hvor sikkert er Divid? Kan uvedkommede få tak i dataene mine?
+ Veldig. +

+ +

+ Kan jeg invitere folk som ikke er registrert?
+ Ja, man kan invitere folk når prosjekt opprettes ved å fylle inn mailadressen deres. De får da en mailinvitasjon. +

+ +

+ Savner muligheten til å eksportere prosjektene til excel, pdf eller csv.
+ Denne funksjonaliteten kommer og mer! Følg oss på Facebook for å få oppdateringer. +

+ +

+ Deltakerene vil ikke betale tilbake det de skylder!
+ Man velger sine venner selv. Dette er en webtjeneste. Innkreving er et individuelt ansvar og her ligger tjenestens begrensning. +

+ +

+ Jeg delegerte ut administratorroller til andre deltakere, men det er full uenighet og prosjektet blir redigert - ingen vet hvem som skylder hvem hva
+ Du er prosjektets eier og kan frata de administratorrollene i prosjektets innstillinger. +

+ +

+ Jeg får ikke varslinger fra Divid
+ Du må sjekke prosjektets innstillinger om den funksjonaliteten er skrudd på. Er ikke det tilfelle, ta kontakt med oss gjennom Facebook likepagen vår eller på mail +

+ +

+ Jeg har lastet ned gratisversjonen, men vil ikke ha reklame - på app eller nettside?! Hva kan jeg gjøre?
+ Kjøp premium versjonen av Divid. Da slipper du reklame for alltid og blir prioritert i servicesystemet vårt. Det er bare 35 kroner. +

+ +

+ Jeg har alt kjøpt premiumversjonen, men slettet applikasjonen. Må jeg betale på nytt?
+ Nei, premium versjonen er til odel og eie. Den følger mailadressen din så det er bare å laste den ned igjen på nytt og logge inn med samme mailadresse. +

+ +

+ Jeg har andre spørsmål enn det som står her. Hvor henvender jeg meg?
+ Du kan nå oss gjennom Facebook siden vår, forumet eller kontaktsiden. Disse lenkene finner du på toppen av siden. +

+ +

+ Jeg representerer interesser som ønsker å investere eller kjøpe tjenesten Divid. Hvor henvender jeg meg?
+ Via kontaktsiden på toppen av siden her +

+ +

+ jeg vil ikke at mine data skal være lagret på serveren lengre. Kan jeg slette selv eller gjør dere det?
+ Du har muligheten til å slette selv. +

+ +

+ Jeg vil ha tilbake pengene mine
+ Det må du ta opp med butikken hvor du kjøpte applikasjonen. +

+ + +

Skulle du ha flere spørsmål sjekk forumet, se forum, Facebook like page eller kontakt oss på support(a)divid.no

+ + +
+ + + <% include templates/footer %> + + diff --git a/app/views/home.ejs b/app/views/home.ejs new file mode 100644 index 0000000..70dbb0f --- /dev/null +++ b/app/views/home.ejs @@ -0,0 +1,164 @@ +<% include header %> + + + + + + <% include navbar %> + + +
+
+ +
+
+ + +
+
+
+

Samarbeid om utgifter

+

Divid er en delingstjeneste som regner ut hva folk skylder hverandre

+
    +

    Registrer deg gratis

    +

    Opprett prosjekt

    +

    Inviter deltakere

    +

    Før inn utgiftene

    +

    Ta et oppgjør

    +
+
+
+

Perfekt for venner, familie og bekjente

+ Registrer » +
+
+
+ +
+
+
+
+
+
+ +
+
+ +

Bruk mobil eller nettleser

+

Divid er en webapplikasjon og kan brukes på pc, nettbrett og mobil. Uansett hva du bruker er Divid tilgjengelig.

+
+
+ +

Inviter de du vil

+

Inviter deltakere til prosjekt ved å legge til epost-adressen deres eller send dem en link!

+
+
+ +

Få oversikt

+

Ikke la delte utgifter gå i glemmeboken. Send påminnelser og få varsel når deltakere betaler.

+
+
+
+
+
+

Hva kan Divid brukes til

+
+
+ +

Studentkollektiv

+

Fellesutgifter, fester, bøter, bursdager, øl, mat, strøm

+
+
+ +

Singelliv

+

Ute på farten, stelle i stand aktiviter med andre, sydentur osv

+
+
+ +

Parforhold

+

I etableringsfasen som ønsker å ha ordnet økonomi fra starten av

+
+
+ +

Delt omsorg

+

Klær, mat, medisiner og aktiviteter. Hvem betaler hva for barnet?

+ +
+
+
+
+
+
+
+

Hvordan bruke Divid

+
+
+

Tilgjengelig via:

+
+
+
+ +
+
+
+ + + +
+
+
+
+
+ +
+
+

+

Registrering

+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+

+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +
+
+
+
+
+
+
+ +<% include footer %> + + diff --git a/app/views/index.ejs b/app/views/index.ejs new file mode 100644 index 0000000..0768773 --- /dev/null +++ b/app/views/index.ejs @@ -0,0 +1,131 @@ +<% include templates/header %> + + + + + <% include templates/navbar %> +
+
+
+
+

<%= title %>

+

Velkommen til <%= title %>

+

Her kommer det kanskje mer senere.

+
    +

    Registrer deg gratis

    +

    Opprett prosjekt

    +

    Inviter deltakere

    +

    Før inn utgiftene

    +

    Ta et oppgjør

    +
+

Perfekt for venner, familie og bekjente!

+ Registrer deg! +
+
+ +
+
+
+
+ +

Bruk mobil eller nettleser

+

Divid er en webapplikasjon og kan brukes på pc, nettbrett og mobil. Uansett hva du bruker er Divid tilgjengelig.

+
+
+ +

Inviter de du vil

+

Inviter deltakere til prosjekt ved å legge til epost-adressen deres eller send dem en link!

+
+
+ +

Få oversikt

+

Ikke la delte utgifter gå i glemmeboken. Send påminnelser og få varsel når deltakere betaler.

+
+
+
+
+ +
+
+

Hva kan Divid brukes til

+
+
+ +

Studentkollektiv

+

Fellesutgifter, fester, bøter, bursdager, øl, mat, strøm

+
+
+ +

Singelliv

+

Ute på farten, stelle i stand aktiviter med andre, sydentur osv

+
+
+ +

Parforhold

+

I etableringsfasen som ønsker å ha ordnet økonomi fra starten av

+
+
+ +

Delt omsorg

+

Klær, mat, medisiner og aktiviteter. Hvem betaler hva for barnet?

+ +
+
+
+
+ + +
+
+

Hvordan bruke Divid

+ + + + +
+
+ + +
+
+

Registrer deg i dag!

+

Du kan enten bruke Facebook eller Twitter, eller registrere deg med epost under!

+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+ +
+
+ + + + +<% include templates/footer %> + + diff --git a/app/views/project/newProject.ejs b/app/views/project/newProject.ejs new file mode 100644 index 0000000..2101138 --- /dev/null +++ b/app/views/project/newProject.ejs @@ -0,0 +1,79 @@ +<% include ../templates/header %> + + + + <% include ../templates/navbar %> + + +
+
+ + +
+
+ +

<%= title %>

+ +
+ +
+ +

Navnet på prosjektet du vil lage

+
+
+ +
+ +
+ +
+
+ +
+ +
+ kr + +
+
+ + + + +
+ +
+ + +
+
+ +
+
+ + + + + + + +
+
+ + +<% include ../templates/footer %> + + diff --git a/app/views/project/participants.ejs b/app/views/project/participants.ejs new file mode 100644 index 0000000..4341a4f --- /dev/null +++ b/app/views/project/participants.ejs @@ -0,0 +1,55 @@ +<% include ../templates/header %> + + + + <% include ../templates/navbar %> + + +
+
+ + +
+
+ +

<%= title %>

+

Her kan du invitere venner til å delta i prosjektet ditt. Skriv inn eposadressene deres, en på hver linje, i tekstboksen under.

+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + + + + +
+
+ + +<% include ../templates/footer %> + + diff --git a/app/views/project/post.ejs b/app/views/project/post.ejs new file mode 100644 index 0000000..5e8f283 --- /dev/null +++ b/app/views/project/post.ejs @@ -0,0 +1,103 @@ +<% include ../templates/header %> + + + + <% include ../templates/navbar %> + + +
+
+ + +
+ +
+ +

<%= title %>

+
+ +
+ <%= req.user.username %> +
+
+
+ +
+ +
+
+ <% + // we need to populate the date- and timefield with the current time and date + var d = new Date(); + var n = d.getFullYear() + '-' + ('0' + (d.getMonth()+1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2); + var t = d.toLocaleTimeString().slice(0, 5); + %> + +
+ +
+ + +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ + +
+
+ +
+
+ + + + + + + +
+
+ + +<% include ../templates/footer %> + + + + diff --git a/app/views/project/project.ejs b/app/views/project/project.ejs new file mode 100644 index 0000000..7e06dad --- /dev/null +++ b/app/views/project/project.ejs @@ -0,0 +1,220 @@ +<% include ../templates/header %> + + + + <% include ../templates/navbar %> + + +
+
+
+
+
+ prosjekt +
+
+

<%= project.name %>

+
+
+
+
+ opprettet +
+
+

+ <% var d = new Date(project.created); %> + <%= d.getFullYear() + '-' + ('0' + (d.getMonth()+1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2) + ' ' + d.toLocaleTimeString().slice(0, 5) %> +

+
+
+
+
+ deltakere +
+
+ <% access.forEach(function(participants) { %> + <%- participants.user.name %><% if (participants.permissions >= 6) { %> (<%= participants.permissions === 9 ? 'eier' : 'admin' %>)<% } %>
+ <% }); %> + Legg til flere deltakere +
+
+
+ +
+
+ + + + + + + + +
+
+
+
+

Status total

+
+
+
+ + + + + + +
<%= pro.user[req.user._id].total %><%= pro.total %><%= pro.user[req.user._id].diff %>
+
+
+
+
+

Det er til sammen brukt <%= (pro.total).toFixed(2) %> <%= project.currency %>. Deltakerne skal altså betale <%= (pro.each).toFixed(2) %> <%= project.currency %> hver.

+ <% for (var i in pro.user) { %> + <%- pro.user[i].name %> har betalt <%= pro.user[i].total %> <%= project.currency %>, og <%- pro.user[i].diff >= 0 ? 'skyldes ' + pro.user[i].diff : 'skylder ' + Math.abs(pro.user[i].diff) %> <%= project.currency %>
+ <% } %> +

Eksempel på hvordan oppgjøret kan gjøres

+
+
+ + <% + // FUGLY LAST-MINUTE CODE + var i = 1; + for (var u in pro.user) { + if (pro.user[u].coeff === 0) { + if (i === 1) {%> + + + <% for (var p in pro.user) { %> + <%- pro.user[p].coeff > 0 ? '' : '' %> + <% } %> + + <% } %> + + + <% for (var p in pro.user) { %> + <%- pro.user[p].coeff > 0 ? '' : '' %> + <% } %> + +<% i++; + }} %> +
' + pro.user[p].name + '
<%- pro.user[u].name %>' + (Math.abs(pro.user[u].diff) * pro.user[p].coeff).toFixed(2) + '
+ +
+
+ +
+
+
+
+ + + + + + + + +
+
+
+

Siste aktivitet

+
+ <% posts.forEach(function(post) { %> +
+
+
+ <% + var d = new Date(post.when); + var month = ['JAN', 'FEB', 'MAR', 'APR', 'MAI', 'JUN', 'JUL', 'AUG', 'SEP', 'OKT', 'NOV', 'DES']; + %> +
<%= d.getDate() %>
+
<%= month[d.getMonth()] %>
+ +
+
+
+
av
+
<%- post.user.name %>
+
+
+
hva
+
<%= post.what %>
+
+
+
+
+   +
+
+
+
+ sum + <%= post.value %> <%= post.currency %> +
+
+ +
+
+
+
+
+
+
kommentar
+
+ <%= post.comment %> +
+
+
+ <% if (String(post.user._id) === String(req.user._id) || req.user.permissions >= 6) { %> +
+ [edit] +
+
+ [delete] +
+ <% } %> +
+
+ + <% }); %> +
+
+
+ + +
+
+ + +<% include ../templates/footer %> + + + + diff --git a/app/views/template.ejs b/app/views/template.ejs new file mode 100644 index 0000000..6803c37 --- /dev/null +++ b/app/views/template.ejs @@ -0,0 +1,18 @@ +<% include templates/header %> + + + + <% include templates/navbar %> + + +
+
+ +
+
+ + +<% include templates/footer %> + + diff --git a/app/views/templates/footer.ejs b/app/views/templates/footer.ejs new file mode 100644 index 0000000..2421d82 --- /dev/null +++ b/app/views/templates/footer.ejs @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/app/views/templates/header.ejs b/app/views/templates/header.ejs new file mode 100644 index 0000000..815be5c --- /dev/null +++ b/app/views/templates/header.ejs @@ -0,0 +1,16 @@ + + + + + + + <%= title %> + + + + + + + + + diff --git a/app/views/templates/navbar.ejs b/app/views/templates/navbar.ejs new file mode 100644 index 0000000..f72947d --- /dev/null +++ b/app/views/templates/navbar.ejs @@ -0,0 +1,73 @@ + + + + + diff --git a/app/views/test.ejs b/app/views/test.ejs new file mode 100644 index 0000000..03a8aee --- /dev/null +++ b/app/views/test.ejs @@ -0,0 +1,46 @@ +<% include templates/header %> + + + + <% include templates/navbar %> + + +
+
+ + + + + +
+
+

<%= title %>

+

Velkommen til <%= title %>

+

Her kommer det kanskje mer senere.

+
+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vestibulum, mauris molestie interdum venenatis, sapien diam vestibulum arcu, sit amet ultrices urna sem et ipsum. Pellentesque dapibus tellus tristique augue sollicitudin mollis. Donec consectetur scelerisque lacus sit amet ullamcorper. Maecenas lectus ante, iaculis vitae consequat quis, viverra non lacus. Vestibulum vestibulum lacinia ullamcorper. Nam facilisis blandit turpis sit amet ultricies. Nulla facilisi. Donec non quam risus. Suspendisse venenatis nisl ut dolor imperdiet in eleifend nulla sagittis. Cras facilisis tempus sapien. In dapibus sodales aliquet. Nullam blandit, elit non convallis congue, velit augue facilisis odio, vel blandit turpis purus nec eros. Donec vel ipsum tellus, id lobortis justo. Vivamus ac porta nisi. Suspendisse malesuada convallis velit, non eleifend metus vehicula vitae. Ut pretium, diam nec iaculis gravida, nunc lacus accumsan arcu, et laoreet elit justo nec libero.

+

Integer sit amet lacus lectus, quis laoreet nisl. Pellentesque ut augue odio, eget placerat mauris. Phasellus tortor libero, placerat at interdum id, luctus scelerisque ipsum. Maecenas auctor feugiat aliquam. Nullam eu lorem quis est euismod elementum. Integer sit amet nulla at dui semper pellentesque eget id sem. Sed bibendum nisl id eros ullamcorper ut condimentum justo tempus. Nam eget neque arcu, ac pulvinar justo. Nunc ac justo vel risus ultrices aliquam vel vehicula velit. Nullam faucibus, sapien eget volutpat commodo, erat ipsum congue ante, non facilisis lacus erat non eros. Pellentesque facilisis semper nibh ac tincidunt. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis vitae magna massa, eu laoreet libero.

+
+
+

Would you?

+ +

Cras ac erat arcu, condimentum posuere lectus. Suspendisse mollis volutpat ipsum, ac posuere quam rutrum vitae. Nullam dignissim, diam eu posuere blandit, diam nibh congue eros, a posuere magna diam ut metus. Sed vehicula commodo lobortis. Aliquam erat volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam a purus lorem, non laoreet libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras bibendum, ligula non tristique consequat, erat lacus porttitor orci, eu viverra ipsum nunc nec risus.

+

Donec vitae felis et quam tempor vestibulum vel at diam. Donec felis mauris, euismod vel interdum ac, commodo quis nunc. Nulla nec libero diam, in tempus arcu. Cras justo neque, sodales vel rhoncus nec, placerat ut nunc. Mauris convallis urna non erat varius malesuada. Curabitur eu mauris purus, ut suscipit lorem. Ut convallis varius lacus nec sollicitudin. Fusce egestas ultricies justo, et gravida neque mollis vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;

+
+
+
+
+ +<% include templates/footer %> + + diff --git a/app/views/users/registerEmail.ejs b/app/views/users/registerEmail.ejs new file mode 100644 index 0000000..bdf5b65 --- /dev/null +++ b/app/views/users/registerEmail.ejs @@ -0,0 +1,43 @@ +<% include ../templates/header %> + + + + + +
+
+ +
+
+ +

<%= title %>

+

Vær vennlig å registrer eposten din

+ +
+ +
+
+ + +
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +<% include ../templates/footer %> + + diff --git a/app/views/users/signup.ejs b/app/views/users/signup.ejs new file mode 100644 index 0000000..611c052 --- /dev/null +++ b/app/views/users/signup.ejs @@ -0,0 +1,86 @@ +<% include ../templates/header %> + + + + + +
+
+

+

Please tell us a little about yourself.

+
+
+
+ +
+ +
+
+
+ +
+ <% if (invite) { %> + <%= email %> + <% } else { %> + + <% } %> +
+
+
+

+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + +<% include ../templates/footer %> -- cgit v1.2.3