/* jshint node: true */ var User = require('../schemas/User'), Project = require('../schemas/Project'), Media = require('../schemas/Media'), Collaborator = require('../schemas/Collaborator'), Plan = require('../schemas/Plan'), Subscription = require('../schemas/Subscription'), config = require('../../../config'), middleware = require('../middleware'), util = require('../util'), _ = require('lodash'), moment = require('moment'); var staff = module.exports = { fields: { user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip", project: "_id name slug user_id privacy created_at updated_at", plans: "monthly_price yearly_price basic_layout_monthly_price basic_layout_yearly_price " + "pro_layout_monthly_price pro_layout_yearly_price " + "basic_layout_limit pro_layout_limit stock_project_limit basic_project_limit pro_project_limit", plans_permissions: "basic_editor pro_editor solids collaborators no_logo", }, defaults: { user: { _id: "", username: "", displayName: "", created_at: "", updated_at: "", created_ip: "", last_ip: "", }, }, middleware: { ensureUsers: function(req, res, next){ var paginationInfo = res.locals.pagination = {} var criteria = req.criteria || {} var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 ) var offset = paginationInfo.offset = Number(req.query.offset) || 0 var initial = util.sanitize(req.query.initial) var sort paginationInfo.sort = req.query.sort paginationInfo.sortOptions = ["date", "last_seen", "username"] switch (req.query.sort) { case 'date': sort = {'created_at': -1} break case 'last_seen': sort = {'last_seen': -1} break case 'username': default: sort = {'username': 1} paginationInfo.sort = "username" break } if (initial) { if (initial == "?") { criteria.username = new RegExp('^[$a-zA-Z]', "i") } else { criteria.username = new RegExp('^' + initial, "i") } } User.find(criteria) .select(staff.fields.user) .sort(sort) .skip(offset) .limit(limit) .exec(function (err, users) { res.locals.users = users.map(staff.helpers.user) if (! res.locals.users.length) { if (initial) { res.locals.opt.error = "No users found starting with " + initial.toUpperCase() + "" } else { res.locals.opt.error = "No users found" } } next() }) }, ensureRecentUsers: function(req, res, next){ var dreq = { query: { sort: 'last_seen', limit: 20, offset: 0 } } staff.middleware.ensureUsers(dreq, res, next) }, ensureProjects: function(req, res, next){ var paginationInfo = res.locals.pagination = {} var criteria = req.criteria || {} var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 ) var offset = paginationInfo.offset = Number(req.query.offset) || 0 var sort paginationInfo.sort = req.query.sort paginationInfo.sortOptions = ["date", "name"] switch (req.query.sort) { default: case 'date': sort = {'updated_at': -1} break case 'name': paginationInfo.sort = "name" sort = {'slug': 1} break } Project.find(criteria) .select(staff.fields.project) .sort(sort) .skip(offset) .limit(limit) .exec(function (err, projects) { res.locals.projects = projects.map(staff.helpers.project) next() }) }, ensureMedia: function(req, res, next){ var paginationInfo = res.locals.pagination = {} var criteria = req.criteria || {} var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 ) var offset = paginationInfo.offset = Number(req.query.offset) || 0 var sort paginationInfo.sort = req.query.sort paginationInfo.sortOptions = ["date"] switch (req.query.sort) { default: case 'date': paginationInfo.sort = "date" sort = {'created_at': -1} break } Media.find(criteria) // .select(staff.fields.media) .sort(sort) .skip(offset) .limit(limit) .exec(function (err, media) { res.locals.media = media.map(staff.helpers.media) next() }) }, ensureSubscriptions: function(req, res, next){ var paginationInfo = res.locals.pagination = {} var criteria = req.criteria || {} var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 ) var offset = paginationInfo.offset = Number(req.query.offset) || 0 var sort paginationInfo.sort = req.query.sort paginationInfo.sortOptions = ["date", "name"] switch (req.query.sort) { case 'created': sort = {'created_at': -1} break default: case 'date': sort = {'updated_at': -1} break } Subscription.find(criteria) .select(staff.fields.project) .sort(sort) .skip(offset) .limit(limit) .exec(function (err, subscriptions) { res.locals.subscriptions = subscriptions.map(staff.helpers.subscription) next() }) }, ensurePlans: function(req, res, next){ Plan.find({}).sort({ 'level': -1 }).exec(function (err, plans) { res.locals.plans = (plans || []).map(staff.helpers.plan) res.locals.plans.sort(function(a,b){ return a.monthly_price }) next() }) }, ensurePlan: function (req, res, next) { if (req.params.slug) { Plan.findOne({ slug: req.params.slug }, function(err, plan){ if (err || ! plan) { console.error(err) res.redirect("/staff/plans/") } else { req.plan = plan next() } }) } else { res.redirect("/staff/plans/") } }, ensureSubscription: function (req, res, next) { if (req.params.id) { Subscription.findOne({ _id: req.params.id }, function(err, subscription){ if (err || ! subscription) { console.error(err) res.redirect("/staff/subscriptions/") } else { req.subscription = subscription next() } }) } else { res.redirect("/staff/subscriptions/") } }, ensureRecentProjects: function(req, res, next){ var dreq = { params: { sort: 'created_at', limit: 20, offset: 0 } } staff.middleware.ensureProjects(dreq, res, next) }, ensureProjectsUsers: function(req, res, next){ if (! res.locals.projects || ! res.locals.projects.length) { return next() } staff.middleware.ensureObjectsUsers(res.locals.projects, next) }, ensureSubscriptionsUsers: function(req, res, next){ if (! res.locals.subscriptions || ! res.locals.subscriptions.length) { return next() } staff.middleware.ensureObjectsUsers(res.locals.subscriptions, next) }, ensureMediaUsers: function(req, res, next){ if (! res.locals.media || ! res.locals.media.length) { return next() } staff.middleware.ensureObjectsUsers(res.locals.media, next) }, ensureSubscriptionUser: function(req, res, next){ if (! res.locals.subscription) { return next() } staff.middleware.ensureObjectsUsers([ res.locals.subscription ], function(){ next() }) }, ensureMediaUser: function(req, res, next){ if (! res.locals.media) { return next() } staff.middleware.ensureObjectsUsers([ res.locals.media ], function(){ res.locals.mediaUser = res.locals.media.User next() }) }, ensureObjectsUsers: function(objects, next){ if (! objects) { return next () } var dedupe = {}, user_ids objects.forEach(function(obj){ dedupe[ obj.user_id ] = dedupe[ obj.user_id ] || [] dedupe[ obj.user_id ].push(obj) }) user_ids = _.keys(dedupe) User.find({ _id: { $in: user_ids } }) .select(staff.fields.user) .exec(function (err, users) { if (! users) { return next () } users.forEach(function(user){ dedupe[user._id].forEach(function(obj){ obj.user = user }) }) next() }) }, ensureProfile: function(req, res, next){ var username = req.params.username if (username) { User.findOne({ username: username }, function (err, user) { if (user) { res.locals.profile = req.method == "GET" ? staff.helpers.user(user) : user } else { res.locals.profile = null } next() }) } else { res.locals.profile = null next() } }, ensureSingleMedia: function(req, res, next){ var id = req.params.id if (id) { Media.findOne({ _id: id }, function (err, media) { if (media) { res.locals.media = req.method == "GET" ? staff.helpers.media(media) : media } else { res.locals.media = null } next() }) } else { res.locals.media = null next() } }, ensureUsersCount: function(req, res, next){ User.count({}, function(err, count){ res.locals.userCount = count || 0 next() }) }, ensureProjectsCount: function(req, res, next){ Project.count({}, function(err, count){ res.locals.projectCount = count || 0 next() }) }, ensureMediaCount: function(req, res, next){ Media.count({}, function(err, count){ res.locals.mediaCount = count || 0 next() }) }, ensureProfileProjectCount: function(req, res, next){ if (! res.locals.profile) { return next() } Project.count({ user_id: res.locals.profile._id}, function(err, count){ res.locals.profile.projectCount = count || 0 next() }) }, ensureProfileMediaCount: function(req, res, next){ if (! res.locals.profile) { return next() } Media.count({ user_id: res.locals.profile._id}, function(err, count){ res.locals.profile.mediaCount = count || 0 next() }) }, ensureProfileProjects: function(req, res, next){ if (! res.locals.profile) { return next() } Project.find({ user_id: res.locals.profile._id }, staff.fields.project, function(err, projects){ res.locals.projects = projects.map(staff.helpers.project) next() }) }, ensureProfileMedia: function(req, res, next){ if (! res.locals.profile) { return next() } req.criteria = { user_id: res.locals.profile._id } staff.middleware.ensureMedia(req, res, next) }, ensureProject: function(req, res, next){ res.locals.project = req.project next() }, ensureProjectUser: function(req, res, next){ if (! res.locals.project) { return next() } User.findOne({ _id: res.locals.project.user_id }, staff.fields.user, function(err, user){ res.locals.projectUser = staff.helpers.user(user) || staff.defaults.user next() }) }, ensureProjectCollaborators: function(req, res, next){ if (! res.locals.project) { res.locals.collaborators = [] return next() } Collaborator.find({ project_id: res.locals.project._id}, function(err, collaborators){ res.locals.collaborators = collaborators || [] next() }) }, }, helpers: { user: function(user){ var last_seen = moment( user.last_seen || user.updated_at || user.created_at ) user = user.toObject() user.last_seen = last_seen.format("YYYY/MM/DD HH:MM") + " " + last_seen.fromNow() user.last_charged = user.last_charged && moment( user.last_charged ).format("YYYY/MM/DD HH:MM") user.created_ip = util.num2ip( user.created_ip ) user.last_ip = util.num2ip( user.last_ip ) return user }, project: function(project){ project = project.toObject() project.date = moment( project.updated_at || project.created_at ).format("M/DD/YYYY hh:mm a") project.user = {} return project }, media: function(media){ media = media.toObject() media.date = moment( media.updated_at || media.created_at ).format("M/DD/YYYY hh:mm a") media.user = {} media.shortUrl = media.url.replace(/^http.:\/\//,"") return media }, plan: function(plan){ plan = plan.toObject() plan.date = moment( plan.updated_at || plan.created_at ).format("M/DD/YYYY hh:mm a") plan.user = {} return plan }, subscription: function(subscription){ subscription = subscription.toObject() subscription.date = moment( subscription.updated_at || subscription.created_at ).format("M/DD/YYYY hh:mm a") subscription.user = {} return subscription }, }, route: function(app){ app.get('/staff', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureRecentUsers, staff.middleware.ensureUsersCount, staff.middleware.ensureProjectsCount, staff.middleware.ensureMediaCount, staff.index ); // // users app.get('/staff/users', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureUsersCount, staff.middleware.ensureUsers, staff.users.index ); app.get('/staff/users/:username', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureProfile, staff.middleware.ensureProfileProjectCount, staff.middleware.ensureProfileMediaCount, staff.middleware.ensureProfileProjects, staff.users.show ); app.get('/staff/users/:username/media', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureProfile, staff.middleware.ensureProfileMedia, staff.middleware.ensureProfileMediaCount, staff.users.media ); app.put('/staff/users/:username/bless', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureProfile, staff.users.bless ); if (app.get('env') === 'development') { app.get('/staff/authorize', middleware.ensureAuthenticated, staff.users.blessSelf ); } // // projects app.get('/staff/projects', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureProjectsCount, staff.middleware.ensureProjects, staff.middleware.ensureProjectsUsers, staff.projects.index ); app.get('/staff/projects/:slug', middleware.ensureAuthenticated, middleware.ensureIsStaff, middleware.ensureProject, staff.middleware.ensureProject, staff.middleware.ensureProjectUser, staff.middleware.ensureProjectCollaborators, staff.projects.show ); app.put('/staff/projects/:slug/feature', middleware.ensureAuthenticated, middleware.ensureIsStaff, middleware.ensureProject, staff.middleware.ensureProject, staff.projects.feature ); // // media app.get('/staff/media', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureMediaCount, staff.middleware.ensureMedia, staff.middleware.ensureMediaUsers, staff.media.index ); app.get('/staff/media/:id', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureSingleMedia, staff.middleware.ensureMediaUser, staff.media.show ); // // plans app.get('/staff/plans', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensurePlans, staff.plans.index ); app.get('/staff/plans/new', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.plans.new ); app.post('/staff/plans/new', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.plans.create ); app.get('/staff/plans/:slug', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensurePlan, staff.plans.edit ); app.post('/staff/plans/:slug', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensurePlan, staff.plans.update ); // // subscriptions app.get('/staff/subscriptions', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureSubscriptions, staff.middleware.ensureSubscriptionsUsers, staff.subscriptions.index ); app.get('/staff/subscriptions/:id', middleware.ensureAuthenticated, middleware.ensureIsStaff, staff.middleware.ensureSubscription, staff.middleware.ensureSubscriptionUser, staff.subscriptions.show ); }, paginate: function(req, res){ var info = res.locals.pagination info.query = "sort=" + info.sort + "&limit=" + info.limit info.first_page = 0 info.last_page = Math.max(0, info.max - info.limit) info.sortOptions = info.sortOptions if (info.offset > 0) { info.prev_page = Math.max(0, info.offset - info.limit) } else { info.prev_page = -1 } if (info.count == info.limit && info.offset + info.limit < info.max) { info.next_page = info.offset + info.limit } else { info.next_page = -1 } }, index: function(req, res){ res.render('staff/index') }, // /staff/users/ // /staff/users/:username users: { index: function(req, res){ res.locals.pagination.count = res.locals.users.length res.locals.pagination.max = res.locals.userCount staff.paginate(req, res) res.render('staff/users/index') }, show: function(req, res){ if (res.locals.profile) { res.render('staff/users/show', { profileJSON: util.escape( JSON.stringify( res.locals.profile ) ) }) } else { res.render('staff/users/show_404') } }, media: function(req, res){ if (res.locals.profile) { res.locals.pagination.count = res.locals.media.length res.locals.pagination.max = res.locals.profile.mediaCount staff.paginate(req, res) res.render('staff/users/media') } else { res.render('staff/users/show_404') } }, blessSelf: function(req, res){ req.user.isStaff = true req.user.save(function(err, user){ res.json({ state: user.isStaff }) }) }, bless: function(req, res){ res.locals.profile.isStaff = req.body.state == "true" res.locals.profile.save(function(err, user){ res.json({ state: user.isStaff }) }) }, }, // /staff/projects/ // /staff/projects/:name projects: { index: function(req, res){ res.locals.pagination.count = res.locals.projects.length res.locals.pagination.max = res.locals.projectCount staff.paginate(req, res) res.render('staff/projects/index') }, show: function(req, res){ if (res.locals.project) { res.render('staff/projects/show', { projectJSON: util.escape( JSON.stringify( res.locals.project ) ), projectUserJSON: util.escape( JSON.stringify( res.locals.projectUser ) ), collaboratorsJSON: util.escape( JSON.stringify( res.locals.collaborators ) ), }) } else { res.render('staff/projects/show_404') } }, feature: function(req, res){ res.locals.project.featured = req.body.state == "true" res.locals.project.save(function(err, project){ res.json({ state: project.featured }) }) }, }, media: { index: function(req, res){ res.locals.pagination.count = res.locals.media.length res.locals.pagination.max = res.locals.mediaCount staff.paginate(req, res) res.render('staff/media/index') }, show: function(req, res){ if (res.locals.media) { res.render('staff/media/show', { mediaJSON: util.escape( JSON.stringify( res.locals.media ) ), mediaUserJSON: util.escape( JSON.stringify( res.locals.mediaUser ) ), }) } else { res.render('staff/media/show_404') } }, }, plans: { index: function(req, res){ res.locals.fields = staff.fields.plans.split(" ") res.locals.permissions = staff.fields.plans_permissions.split(" ") res.render('staff/plans/index') }, new: function(req, res){ res.locals.plan = new Plan () res.render('staff/plans/new') }, edit: function(req, res){ res.locals.plan = req.plan res.render('staff/plans/edit') }, create: function(req, res){ var plan = new Plan () var fields = staff.fields.plans.split(" ") var permissions = staff.fields.plans_permissions.split(" ") var data = util.cleanQuery(req.body) data.name = util.sanitize(data.name) data.slug = util.sanitize(data.slug.toLowerCase()) data.permissions = {} permissions.forEach(function(field){ data.permissions[field] = data["permissions_" + field].length == 2 }) new Plan (data).save(function(err, doc){ if (err || ! doc) { return res.json({ error: err }) } middleware.updatePlans() res.redirect("/staff/plans/") }) }, update: function(req, res){ var fields = staff.fields.plans.split(" ") var permissions = staff.fields.plans_permissions.split(" ") var data = util.cleanQuery(req.body) data.name = util.sanitize(data.name) data.slug = util.sanitize(data.slug.toLowerCase()) _.extend(req.plan, data) permissions.forEach(function(field){ req.plan.permissions[field] = data["permissions_" + field].length == 2 }) req.plan.save(function(err, doc){ if (err || ! doc) { return res.json({ error: err }) } middleware.updatePlans() res.redirect("/staff/plans/") }) }, }, subscriptions: { index: function(req, res){ res.locals.pagination.count = res.locals.subscriptions.length res.locals.pagination.max = res.locals.subscriptionCount staff.paginate(req, res) res.render('staff/subscriptions/index') }, show: function(req, res){ res.render('staff/subscriptions/show') }, }, }