From 2d4ed7d888727e1b973c2581b694d900e30c2ebd Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 7 Jan 2015 13:53:27 -0500 Subject: plan/subscription schemas --- server/lib/schemas/Plan.js | 36 +++++++++++++++++++++++++++ server/lib/schemas/Subscription.js | 25 +++++++++++++++++++ server/lib/views/index.js | 8 +----- server/lib/views/subscription.js | 51 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 server/lib/schemas/Plan.js create mode 100644 server/lib/schemas/Subscription.js create mode 100644 server/lib/views/subscription.js diff --git a/server/lib/schemas/Plan.js b/server/lib/schemas/Plan.js new file mode 100644 index 0000000..3e74997 --- /dev/null +++ b/server/lib/schemas/Plan.js @@ -0,0 +1,36 @@ +/* jshint node: true */ + +var mongoose = require('mongoose'), + _ = require('lodash'), + crypto = require('crypto'), + config = require('../../../config.json'), + util = require('../util'); + +var PlanSchema = new mongoose.Schema({ + name: { type: String }, + slug: { type: String }, + + monthly_price: { type: Number }, + yearly_price: { type: Number }, + + basic_layout_limit: { type: Number }, + pro_layout_limit: { type: Number }, + + stock_layout_project_limit: { type: Number }, + basic_layout_project_limit: { type: Number }, + pro_layout_project_limit: { type: Number }, + + permissions: { + basic_editor: { type: Boolean, default: false }, + pro_editor: { type: Boolean, default: false }, + solids: { type: Boolean, default: false }, + collaborators: { type: Boolean, default: false }, + no_logo: { type: Boolean, default: false }, + }, + + created_at: { type: Date, default: Date.now }, + updated_at: { type: Date, default: Date.now }, +}) + +module.exports = exports = mongoose.model('plan', PlanSchema); +exports.schema = PlanSchema; diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js new file mode 100644 index 0000000..8d0b10e --- /dev/null +++ b/server/lib/schemas/Subscription.js @@ -0,0 +1,25 @@ +/* jshint node: true */ + +var mongoose = require('mongoose'), + _ = require('lodash'), + crypto = require('crypto'), + config = require('../../../config.json'), + util = require('../util'); + +var SubscriptionSchema = new mongoose.Schema({ + user_id: { type: mongoose.Schema.ObjectId, index: true }, + + monthly_total: { type: Number }, + yearly_total: { type: Number }, + + plans: [{ + tier: { type: String }, + monthly: { type: Boolean }, + }] + + created_at: { type: Date, default: Date.now }, + updated_at: { type: Date, default: Date.now }, +}) + +module.exports = exports = mongoose.model('subscription', SubscriptionSchema); +exports.schema = SubscriptionSchema; diff --git a/server/lib/views/index.js b/server/lib/views/index.js index 8c3e63d..3326499 100644 --- a/server/lib/views/index.js +++ b/server/lib/views/index.js @@ -21,6 +21,7 @@ marked.setOptions({ var views = module.exports = { staff: require('./staff'), + subscription: require('./subscription'), editor_new: function (req, res) { if (! req.user) { @@ -88,13 +89,6 @@ var views = module.exports = { }, home: function (req, res) { - // while in development, blank homepage if not logged in -/* - if (! req.user) { - res.send("") - return - } -*/ views_middleware.fetchProjects({ featured: true }, null, null, function(err, projects){ res.render('home', { projects: projects || [] diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js new file mode 100644 index 0000000..77db1a0 --- /dev/null +++ b/server/lib/views/subscription.js @@ -0,0 +1,51 @@ +/* jshint node: true */ + +var User = require('../schemas/User'), + Subscription = require('../schemas/Subscription'), + config = require('../../../config'), + middleware = require('../middleware'), + util = require('../util'), + _ = require('lodash'), + moment = require('moment'); + +var subscription = 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", + }, + + defaults: { + user: { + _id: "", username: "", displayName: "", + created_at: "", updated_at: "", created_ip: "", last_ip: "", + }, + }, + + middleware: { + }, + + helpers: { + 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 + }, + }, + + route: function(app){ + app.get('/staff', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensureRecentUsers, + staff.middleware.ensureUsersCount, + staff.middleware.ensureProjectsCount, + staff.middleware.ensureMediaCount, + + staff.index + ); + }, + +} -- cgit v1.2.3-70-g09d2 From 9c6f8f8568d20d75eb22955dbf2752ea777e59f8 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 7 Jan 2015 14:34:41 -0500 Subject: stub in brochure page --- public/assets/stylesheets/app.css | 14 +++++++ server/lib/schemas/Plan.js | 6 +++ server/lib/schemas/Subscription.js | 2 +- server/lib/views/index.js | 5 +++ server/lib/views/subscription.js | 1 - views/about/_blank.ejs | 2 +- views/about/about.ejs | 16 +------- views/about/brochure.ejs | 79 ++++++++++++++++++++++++++++++++++++++ views/about/howto.ejs | 2 +- views/builder.ejs | 2 +- views/docs.ejs | 2 +- views/editor.ejs | 2 +- views/home.ejs | 2 +- views/modal.ejs | 2 +- views/profile.ejs | 2 +- views/reader.ejs | 2 +- views/staff/_header.ejs | 2 +- 17 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 views/about/brochure.ejs diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index aecd6be..0463e26 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -673,7 +673,21 @@ iframe.embed { font-weight: 300; } +.aboutintro { + text-align: center; + line-height: 43px; + font-size: 24px; + padding: 5% 0; + font-weight: 300; +} +.aboutintro .inner { + max-width: 800px; + margin: 0 auto; + text-align: center; +} + /* PROFILE PAGE */ + .profilePic { background-size: cover; background-position: center; diff --git a/server/lib/schemas/Plan.js b/server/lib/schemas/Plan.js index 3e74997..1057bb2 100644 --- a/server/lib/schemas/Plan.js +++ b/server/lib/schemas/Plan.js @@ -13,6 +13,12 @@ var PlanSchema = new mongoose.Schema({ monthly_price: { type: Number }, yearly_price: { type: Number }, + basic_layout_monthly_price: { type: Number }, + basic_layout_yearly_price: { type: Number }, + + pro_layout_monthly_price: { type: Number }, + pro_layout_yearly_price: { type: Number }, + basic_layout_limit: { type: Number }, pro_layout_limit: { type: Number }, diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js index 8d0b10e..8315009 100644 --- a/server/lib/schemas/Subscription.js +++ b/server/lib/schemas/Subscription.js @@ -15,7 +15,7 @@ var SubscriptionSchema = new mongoose.Schema({ plans: [{ tier: { type: String }, monthly: { type: Boolean }, - }] + }], created_at: { type: Date, default: Date.now }, updated_at: { type: Date, default: Date.now }, diff --git a/server/lib/views/index.js b/server/lib/views/index.js index 3326499..5f9088b 100644 --- a/server/lib/views/index.js +++ b/server/lib/views/index.js @@ -111,6 +111,11 @@ var views = module.exports = { res.render('about/' + name) return } + if (name == "brochure") { + // TODO: fetch plans + res.render('about/' + name) + return + } if (name == "about" || name == "index") { res.render('about/' + name) return diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js index 77db1a0..ba54bb4 100644 --- a/server/lib/views/subscription.js +++ b/server/lib/views/subscription.js @@ -12,7 +12,6 @@ var subscription = 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", }, defaults: { diff --git a/views/about/_blank.ejs b/views/about/_blank.ejs index 0e9ea7e..3c23fa7 100644 --- a/views/about/_blank.ejs +++ b/views/about/_blank.ejs @@ -1,7 +1,7 @@ - vvalls + VValls [[ include ../partials/meta ]] diff --git a/views/about/about.ejs b/views/about/about.ejs index dd536be..2aec982 100644 --- a/views/about/about.ejs +++ b/views/about/about.ejs @@ -1,7 +1,7 @@ - vvalls + About VValls [[ include ../partials/meta ]] @@ -50,17 +50,3 @@ [[ include ../partials/scripts ]] - \ No newline at end of file diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs new file mode 100644 index 0000000..00083cf --- /dev/null +++ b/views/about/brochure.ejs @@ -0,0 +1,79 @@ + + + + VValls Subscriptions + [[ include ../partials/meta ]] + + +
+ [[ include ../partials/header ]] + +

Subscriptions

+ +
+
+ Want to get more out of VValls? Consider becoming a subscription user. +
+
+ +
+

[[- plans.free.name ]]

+
    +
  • One exhibition with pre-designed template floor plan +
+
+ +
+

[[- plans.premium.name ]]

+
    +
  • $[[- plans.premium.monthly_price ]]/mo or $[[- plans.premium.yearly_price ]]/year +
  • [[- plans.premium.stock_layout_project_limit ]] exhibitions included with pre-designed template floor plans +
  • Each new basic floor plan costs $[[- plans.premium.basic_layout_monthly_price ]]/mo + or $[[- plans.premium.basic_layout_yearly_price ]]/year, minimum 3 months +
  • Each new basic floor plan can have up to [[- plans.premium.basic_layout_project_limit ]] exhibitions +
  • VValls logo appears when embedding an exhibition on a web page +
+
+ +
+

[[- plans.pro.name ]]

+
    +
  • $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year +
  • Comes with [[- plans.premium.pro_layout_limit ]] pro floor plan and [[- plans.premium.pro_layout_project_limit ]] exhibitions +
  • Each new pro floor plan costs $[[- plans.pro.pro_layout_monthly_price ]]/mo + or $[[- plans.pro.pro_layout_yearly_price ]]/year, minimum 3 months +
  • Each new pro floor plan can have up to [[- plans.pro.pro_layout_project_limit ]] exhibitions +
  • Includes planning for 3D objects in the room +
  • No VValls logo on embed +
+
+ +
+ Buying any extra floor plan unlocks collaboration. Invite an artist or curator to work on the exhibition with you. +
+ +
+ Basic Floor plan: Rectangle-based design of any dimension. +
+ +
+ Pro Floor plan: Trace an arbitrary floorplan. +
+ + +
+

Custom

+ We offer many types of customizations and white-label options for business and educational uses. + Contact us for more information +
+ + + [[ include ../partials/confirm-modal ]] + [[ include ../projects/layouts-modal ]] + [[ include ../partials/sign-in ]] + [[ include ../partials/footer ]] + +
+ +[[ include ../partials/scripts ]] + diff --git a/views/about/howto.ejs b/views/about/howto.ejs index 5278a40..914c3b3 100644 --- a/views/about/howto.ejs +++ b/views/about/howto.ejs @@ -1,7 +1,7 @@ - vvalls + How to Use VValls [[ include ../partials/meta ]] diff --git a/views/builder.ejs b/views/builder.ejs index afb8c66..0ba4238 100644 --- a/views/builder.ejs +++ b/views/builder.ejs @@ -1,7 +1,7 @@ - vvalls + VValls [[ include partials/meta ]] diff --git a/views/docs.ejs b/views/docs.ejs index b3ead82..a1f081f 100644 --- a/views/docs.ejs +++ b/views/docs.ejs @@ -1,7 +1,7 @@ - vvalls + VValls [[ include partials/meta ]] diff --git a/views/editor.ejs b/views/editor.ejs index 656615c..74e4d6d 100755 --- a/views/editor.ejs +++ b/views/editor.ejs @@ -1,7 +1,7 @@ - vvalls + VValls [[ include partials/meta ]] diff --git a/views/home.ejs b/views/home.ejs index 36fc2fc..ffb0976 100755 --- a/views/home.ejs +++ b/views/home.ejs @@ -1,7 +1,7 @@ - vvalls + VValls [[ include partials/meta ]] diff --git a/views/modal.ejs b/views/modal.ejs index 7ca869c..732953d 100644 --- a/views/modal.ejs +++ b/views/modal.ejs @@ -1,7 +1,7 @@ - vvalls + VValls [[ include partials/meta ]] diff --git a/views/profile.ejs b/views/profile.ejs index a62652c..88af6b0 100644 --- a/views/profile.ejs +++ b/views/profile.ejs @@ -1,7 +1,7 @@ - vvalls + VValls | [[- profile.displayName ]] [[ include partials/meta ]] diff --git a/views/reader.ejs b/views/reader.ejs index 6c9856a..7035356 100644 --- a/views/reader.ejs +++ b/views/reader.ejs @@ -1,7 +1,7 @@ - vvalls + VValls [[ include partials/meta ]] diff --git a/views/staff/_header.ejs b/views/staff/_header.ejs index 3bbf4f1..a73c12e 100644 --- a/views/staff/_header.ejs +++ b/views/staff/_header.ejs @@ -1,7 +1,7 @@ - vvalls | staff + VValls | staff [[ include ../partials/meta ]] -- cgit v1.2.3-70-g09d2 From 5dd9742da846e8db863a951f1502d0edf5a3f90b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 7 Jan 2015 18:02:13 -0500 Subject: forms for editing plans --- server/lib/schemas/Plan.js | 6 +-- server/lib/views/staff.js | 22 ++++++++ views/about/brochure.ejs | 4 +- views/staff/_nav.ejs | 6 +++ views/staff/media/index.ejs | 7 +-- views/staff/media/show.ejs | 7 +-- views/staff/media/show_404.ejs | 9 +--- views/staff/plans/_form.ejs | 109 ++++++++++++++++++++++++++++++++++++++ views/staff/plans/edit.ejs | 13 +++++ views/staff/plans/index.ejs | 54 +++++++++++++++++++ views/staff/plans/new.ejs | 15 ++++++ views/staff/projects/index.ejs | 7 +-- views/staff/projects/show.ejs | 9 +--- views/staff/projects/show_404.ejs | 7 +-- views/staff/users/index.ejs | 7 +-- views/staff/users/media.ejs | 13 ++--- views/staff/users/show.ejs | 7 +-- views/staff/users/show_404.ejs | 16 +++--- 18 files changed, 244 insertions(+), 74 deletions(-) create mode 100644 views/staff/_nav.ejs create mode 100644 views/staff/plans/_form.ejs create mode 100644 views/staff/plans/edit.ejs create mode 100644 views/staff/plans/index.ejs create mode 100644 views/staff/plans/new.ejs diff --git a/server/lib/schemas/Plan.js b/server/lib/schemas/Plan.js index 1057bb2..1208672 100644 --- a/server/lib/schemas/Plan.js +++ b/server/lib/schemas/Plan.js @@ -22,9 +22,9 @@ var PlanSchema = new mongoose.Schema({ basic_layout_limit: { type: Number }, pro_layout_limit: { type: Number }, - stock_layout_project_limit: { type: Number }, - basic_layout_project_limit: { type: Number }, - pro_layout_project_limit: { type: Number }, + stock_project_limit: { type: Number }, + basic_project_limit: { type: Number }, + pro_project_limit: { type: Number }, permissions: { basic_editor: { type: Boolean, default: false }, diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index 49f492b..c3739e9 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -4,6 +4,8 @@ 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'), @@ -540,6 +542,26 @@ var staff = module.exports = { res.render('staff/media/show_404') } }, + }, + + plans: { + index: function(req, res){ + res.locals.fields = ( + "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" + ).split(" ") + + res.locals.permissions = "basic_editor pro_editor solids collaborators no_logo".split(" ") + + res.render('staff/plans/index') + }, + new: function(req, res){ + res.render('staff/plans/new') + }, + edit: function(req, res){ + res.render('staff/plans/edit') + }, } } diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs index 00083cf..d816dc4 100644 --- a/views/about/brochure.ejs +++ b/views/about/brochure.ejs @@ -35,7 +35,7 @@ -
+

[[- plans.pro.name ]]

  • $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year @@ -57,7 +57,7 @@
- Pro Floor plan: Trace an arbitrary floorplan. + Pro Floor plan: Trace an arbitrary floor plan.
diff --git a/views/staff/_nav.ejs b/views/staff/_nav.ejs new file mode 100644 index 0000000..2115e9f --- /dev/null +++ b/views/staff/_nav.ejs @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/views/staff/media/index.ejs b/views/staff/media/index.ejs index 516af2d..3805c8e 100644 --- a/views/staff/media/index.ejs +++ b/views/staff/media/index.ejs @@ -2,12 +2,7 @@

Media

- +[[ include ../_nav ]]
diff --git a/views/staff/media/show.ejs b/views/staff/media/show.ejs index 76dcd32..9d05cb9 100644 --- a/views/staff/media/show.ejs +++ b/views/staff/media/show.ejs @@ -2,12 +2,7 @@

Media: [[- media.type ]]

- +[[ include ../_nav ]]
diff --git a/views/staff/media/show_404.ejs b/views/staff/media/show_404.ejs index f07cef2..c6bac6f 100644 --- a/views/staff/media/show_404.ejs +++ b/views/staff/media/show_404.ejs @@ -2,13 +2,8 @@

Media not found

- - +[[ include ../_nav ]] +
[[ include ../_footer ]] diff --git a/views/staff/plans/_form.ejs b/views/staff/plans/_form.ejs new file mode 100644 index 0000000..b97716f --- /dev/null +++ b/views/staff/plans/_form.ejs @@ -0,0 +1,109 @@ + +
+ + +
+ +
+ + +
+ + + +
+ + +
+ +
+ + +
+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+ + +
+ +
+ + +
+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ + + + diff --git a/views/staff/plans/edit.ejs b/views/staff/plans/edit.ejs new file mode 100644 index 0000000..503c97d --- /dev/null +++ b/views/staff/plans/edit.ejs @@ -0,0 +1,13 @@ +[[ include ../_header ]] + +

Edit Plan

+ +[[ include ../_nav ]] + +
+ +
+[[- include form ]] +
+ +[[ include ../_footer ]] diff --git a/views/staff/plans/index.ejs b/views/staff/plans/index.ejs new file mode 100644 index 0000000..aa6c35a --- /dev/null +++ b/views/staff/plans/index.ejs @@ -0,0 +1,54 @@ +[[ include ../_header ]] + +

Plans

+ +[[ include ../_nav ]] + +
+ + + + + [[ plans.forEach(function(plan){ ]] + + [[ }) ]] + + + + + [[ plans.forEach(function(plan){ ]] + + [[ }) ]] + + + [[ fields.forEach(function(field){ ]] + + + [[ plans.forEach(function(plan){ ]] + + [[ }) ]] + + [[ }) ]] + + [[ permissions.forEach(function(permission){ ]] + + + [[ plans.forEach(function(plan){ ]] + + [[ }) ]] + + [[ }) ]] + +
+ [[- plan.name ]] +
+ edit +
[[- field.replace(/_/," ") ]] + [[- plan[field] ]] +
[[- permission.replace(/_/," ") ]] + [[- plan.permissions[permission] ? "x" : " " ]] +
+ +
+ +[[ include ../_footer ]] diff --git a/views/staff/plans/new.ejs b/views/staff/plans/new.ejs new file mode 100644 index 0000000..d56a1c3 --- /dev/null +++ b/views/staff/plans/new.ejs @@ -0,0 +1,15 @@ +[[ include ../_header ]] + +

New Plan

+ +[[ include ../_nav ]] + +
+ +
+[[- include form ]] +
+ +[[ include ../_footer ]] + + diff --git a/views/staff/projects/index.ejs b/views/staff/projects/index.ejs index 482ea25..e4ba469 100644 --- a/views/staff/projects/index.ejs +++ b/views/staff/projects/index.ejs @@ -2,12 +2,7 @@

Projects

- +[[ include ../_nav ]]
diff --git a/views/staff/projects/show.ejs b/views/staff/projects/show.ejs index 1034b31..b090a41 100644 --- a/views/staff/projects/show.ejs +++ b/views/staff/projects/show.ejs @@ -2,13 +2,8 @@

[[- project.name ]]

- - +[[ include ../_nav ]] +
diff --git a/views/staff/projects/show_404.ejs b/views/staff/projects/show_404.ejs index 70320c0..f5e1658 100644 --- a/views/staff/projects/show_404.ejs +++ b/views/staff/projects/show_404.ejs @@ -2,12 +2,7 @@

Project not found

- +[[ include ../_nav ]]
diff --git a/views/staff/users/index.ejs b/views/staff/users/index.ejs index f14d666..1795dde 100644 --- a/views/staff/users/index.ejs +++ b/views/staff/users/index.ejs @@ -2,12 +2,7 @@

Users

- +[[ include ../_nav ]]
diff --git a/views/staff/users/media.ejs b/views/staff/users/media.ejs index c1097dd..8927c00 100644 --- a/views/staff/users/media.ejs +++ b/views/staff/users/media.ejs @@ -1,15 +1,10 @@ [[ include ../_header ]] -

User Media: [[- profile.username ]]

+

User Media: [[- profile.username ]]

- - -
+[[ include ../_nav ]] + +
[[ include ../_pagination ]] [[ include ../_gallery ]] diff --git a/views/staff/users/show.ejs b/views/staff/users/show.ejs index 8e9b447..4ce1d9a 100644 --- a/views/staff/users/show.ejs +++ b/views/staff/users/show.ejs @@ -1,12 +1,7 @@ [[ include ../_header ]]

User: [[- profile.username ]]

- +[[ include ../_nav ]]
diff --git a/views/staff/users/show_404.ejs b/views/staff/users/show_404.ejs index bcd0271..11663fa 100644 --- a/views/staff/users/show_404.ejs +++ b/views/staff/users/show_404.ejs @@ -1,13 +1,9 @@ [[ include ../_header ]] -

User not found

- - - -
+ +

User not found

+ +[[ include ../_nav ]] + +
[[ include ../_footer ]] -- cgit v1.2.3-70-g09d2 From 190a145cf1433fde570df1ac9784d4c5cea77d3d Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 7 Jan 2015 18:28:46 -0500 Subject: everything but saving --- server/lib/middleware.js | 2 +- server/lib/views/staff.js | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/server/lib/middleware.js b/server/lib/middleware.js index 4848ab0..797d677 100644 --- a/server/lib/middleware.js +++ b/server/lib/middleware.js @@ -75,7 +75,7 @@ var middleware = { } next() }) - } + } else { req.project = null next() diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index c3739e9..ce676ed 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -120,6 +120,31 @@ var staff = module.exports = { next() }) }, + + ensurePlans: function(req, res, next){ + Plan.exec(function (err, plans) { + res.locals.plans = plans.map(staff.helpers.plan) + 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) + req.plan = null + } + else { + req.plan = plan + } + next() + }) + } + else { + req.plan = null + next() + } + }, ensureRecentProjects: function(req, res, next){ var dreq = { params: { sort: 'created_at', limit: 20, offset: 0 } } @@ -424,7 +449,43 @@ var staff = module.exports = { 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.plans.update + ); }, paginate: function(req, res){ @@ -557,11 +618,18 @@ var staff = module.exports = { res.render('staff/plans/index') }, new: function(req, res){ + res.locals.plan = new Plan () res.render('staff/plans/new') }, edit: function(req, res){ res.render('staff/plans/edit') }, + create: function(req, res){ + res.redirect("/staff/plans/") + }, + update: function(req, res){ + res.redirect("/staff/plans/") + }, } } -- cgit v1.2.3-70-g09d2 From 664099f91ae3ed2d667d331b19c2e41dec60124c Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Fri, 9 Jan 2015 06:43:31 -0500 Subject: fix up plans form --- public/assets/stylesheets/app.css | 6 +- public/assets/stylesheets/staff.css | 16 ++++ server/lib/views/staff.js | 70 ++++++++++++---- server/repl.js | 2 + views/staff/_nav.ejs | 1 + views/staff/index.ejs | 6 +- views/staff/plans/_form.ejs | 160 ++++++++++++++++++++++-------------- views/staff/plans/edit.ejs | 2 +- views/staff/plans/index.ejs | 11 ++- views/staff/plans/new.ejs | 4 +- 10 files changed, 186 insertions(+), 92 deletions(-) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 0463e26..9e86ac3 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -2478,7 +2478,7 @@ form li { form label { float:left; } -form input[type="text"],form input[type="password"] { +form input[type="text"],form input[type="password"],form input[type="number"] { border: 1px solid; font-size: 20px; padding: 5px; @@ -3048,8 +3048,8 @@ a[data-role="forgot-password"] { form li { font-size: 16px; } - form input[type="text"], form input[type="password"] { - font-size: 15px; + form input[type="text"],form input[type="password"],form input[type="number"] { + font-size: 15px; } .page h1 { font-size: 26px; diff --git a/public/assets/stylesheets/staff.css b/public/assets/stylesheets/staff.css index de31571..e5cafa2 100644 --- a/public/assets/stylesheets/staff.css +++ b/public/assets/stylesheets/staff.css @@ -71,6 +71,22 @@ hr { color: #00f; border-bottom: 1px solid; } +.staff form { + max-width: none; + width: 600px; + padding: 20px; +} +.staff form label { + float: none; +} +.staff form p { + width: 350px; + margin: 5px 0; + color: #444; +} +.staff form div li { + width: 180px; +} #iframe-embed, #iframe-embed tr, #iframe-embed td { width: 79vw; } diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index ce676ed..6639137 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -18,6 +18,10 @@ 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: { @@ -122,8 +126,8 @@ var staff = module.exports = { }, ensurePlans: function(req, res, next){ - Plan.exec(function (err, plans) { - res.locals.plans = plans.map(staff.helpers.plan) + Plan.find(function (err, plans) { + res.locals.plans = (plans || []).map(staff.helpers.plan) next() }) }, @@ -132,17 +136,16 @@ var staff = module.exports = { Plan.findOne({ slug: req.params.slug }, function(err, plan){ if (err || ! plan) { console.error(err) - req.plan = null + res.redirect("/staff/plans/") } else { req.plan = plan + next() } - next() }) } else { - req.plan = null - next() + res.redirect("/staff/plans/") } }, @@ -326,7 +329,14 @@ var staff = module.exports = { 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 + }, }, route: function(app){ @@ -452,7 +462,8 @@ var staff = module.exports = { // // plans - app.get('/staff/plans/', + + app.get('/staff/plans', middleware.ensureAuthenticated, middleware.ensureIsStaff, @@ -484,6 +495,8 @@ var staff = module.exports = { middleware.ensureAuthenticated, middleware.ensureIsStaff, + staff.middleware.ensurePlan, + staff.plans.update ); }, @@ -607,14 +620,8 @@ var staff = module.exports = { plans: { index: function(req, res){ - res.locals.fields = ( - "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" - ).split(" ") - - res.locals.permissions = "basic_editor pro_editor solids collaborators no_logo".split(" ") - + res.locals.fields = staff.fields.plans.split(" ") + res.locals.permissions = staff.fields.plans_permissions.split(" ") res.render('staff/plans/index') }, new: function(req, res){ @@ -625,10 +632,37 @@ var staff = module.exports = { res.render('staff/plans/edit') }, create: function(req, res){ - res.redirect("/staff/plans/") + 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()) + + permissions.forEach(function(field){ + data[field] = data["permissions_" + field] + }) + + new Plan (data).save(function(err, doc){ + if (err || ! doc) { return res.json({ error: err }) } + res.redirect("/staff/plans/") + }) }, update: function(req, res){ - res.redirect("/staff/plans/") + 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[field] = data["permissions_" + field] + }) + + req.plan.save(function(err, doc){ + if (err || ! doc) { return res.json({ error: err }) } + res.redirect("/staff/plans/") + }) }, } diff --git a/server/repl.js b/server/repl.js index ba94d45..353d8c5 100644 --- a/server/repl.js +++ b/server/repl.js @@ -9,6 +9,8 @@ mongoose.connect('mongodb://' + DB_HOST + '/vvalls', {}, function(){ "./lib/schemas/Layout", "./lib/schemas/Media", "./lib/schemas/Project", + "./lib/schemas/Plan", + "./lib/schemas/Subscription", ].forEach(function(modName){ // console.log(name, modName) var namez = modName.split("/"), name = namez[namez.length-1]; diff --git a/views/staff/_nav.ejs b/views/staff/_nav.ejs index 2115e9f..db7bedb 100644 --- a/views/staff/_nav.ejs +++ b/views/staff/_nav.ejs @@ -3,4 +3,5 @@ users projects media + plans \ No newline at end of file diff --git a/views/staff/index.ejs b/views/staff/index.ejs index 5ca7269..1b73641 100644 --- a/views/staff/index.ejs +++ b/views/staff/index.ejs @@ -2,11 +2,7 @@

Staff Area

- + [[ include _nav ]]
diff --git a/views/staff/plans/_form.ejs b/views/staff/plans/_form.ejs index b97716f..fc86516 100644 --- a/views/staff/plans/_form.ejs +++ b/views/staff/plans/_form.ejs @@ -1,109 +1,149 @@ + -
+
    + +
  • +

    New Plan

    +
  • + +
  • - -
+
+ -
+
  • - -
  • +
    + -
    +
  • +

    Plan Pricing

    +
  • + +
  • - -
  • +
    + -
    +
  • - -
  • +
    + + +

    + Note: Pricing should be in cents, i.e. a price of $10.00 should be entered as 1000. +

    -
    - - -
    +
  • +

    Additional Template Pricing

    +
  • -
    - - -
    +
  • + +
    +
  • -
    - - -
    +
  • + +
    +
  • -
    - - -
    +
  • + +
    +
  • + +
  • + +
    +
  • -
    - - -
    +
  • +

    Per-Plan Template Limits

    +
  • -
    - - -
    +
  • + +
    +
  • + +
  • + +
    +
  • -
    +
  • +

    Per-Plan Project Limits

    +
  • + +
  • - -
  • +
    + -
    +
  • - -
  • +
    + -
    +
  • - -
  • +
    + +
  • +

    Permissions

    +
  • +
    - +
  • -
  • + + -
    - +
  • -
  • + + -
    - +
  • -
  • + + -
    - +
  • -
  • + + -
    - +
  • -
  • + + + +

    + These permissions should harmonize with the restrictions on layouts set above. +

    + - +
  • + +
  • + diff --git a/views/staff/plans/edit.ejs b/views/staff/plans/edit.ejs index 503c97d..9848873 100644 --- a/views/staff/plans/edit.ejs +++ b/views/staff/plans/edit.ejs @@ -7,7 +7,7 @@
    -[[- include form ]] +[[- include _form ]] [[ include ../_footer ]] diff --git a/views/staff/plans/index.ejs b/views/staff/plans/index.ejs index aa6c35a..121a2fc 100644 --- a/views/staff/plans/index.ejs +++ b/views/staff/plans/index.ejs @@ -6,6 +6,7 @@
    +[[ if (plans.length) { ]]
    @@ -30,7 +31,11 @@ [[ plans.forEach(function(plan){ ]] [[ }) ]] @@ -46,8 +51,10 @@ [[ }) ]] [[ }) ]] -
    [[- field.replace(/_/," ") ]] - [[- plan[field] ]] + [[ if (field.indexOf("_price") != -1) { ]] + [[- plan[field] == 0 ? "" : "$" + (plan[field]/100).toFixed(2) ]] + [[ } else { ]] + [[- plan[field] ]] + [[ } ]]
    +
    +[[ } ]] + New Plan
    diff --git a/views/staff/plans/new.ejs b/views/staff/plans/new.ejs index d56a1c3..297d3d6 100644 --- a/views/staff/plans/new.ejs +++ b/views/staff/plans/new.ejs @@ -7,9 +7,7 @@
    -[[- include form ]] +[[- include _form ]]
    [[ include ../_footer ]] - - -- cgit v1.2.3-70-g09d2 From b386c3b88034e4e372f147ffd368c2de1d23a865 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Fri, 9 Jan 2015 07:28:43 -0500 Subject: saving plans --- public/assets/stylesheets/staff.css | 2 +- server/lib/views/staff.js | 6 +++++- views/staff/plans/_form.ejs | 10 +++++----- views/staff/plans/index.ejs | 34 +++++++++++++++++++--------------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/public/assets/stylesheets/staff.css b/public/assets/stylesheets/staff.css index e5cafa2..fffadbf 100644 --- a/public/assets/stylesheets/staff.css +++ b/public/assets/stylesheets/staff.css @@ -67,7 +67,7 @@ hr { .staff .body a { border-bottom: 1px dotted; } -.staff .editLinks a { +.staff .editLinks a, .staff a.bluelink { color: #00f; border-bottom: 1px solid; } diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index 6639137..2fdc2c3 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -629,6 +629,7 @@ var staff = module.exports = { res.render('staff/plans/new') }, edit: function(req, res){ + res.locals.plan = req.plan res.render('staff/plans/edit') }, create: function(req, res){ @@ -650,13 +651,16 @@ var staff = module.exports = { }) }, 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[field] = data["permissions_" + field] + req.plan.permissions[field] = data["permissions_" + field].length == 2 }) req.plan.save(function(err, doc){ diff --git a/views/staff/plans/_form.ejs b/views/staff/plans/_form.ejs index fc86516..ae5ca5a 100644 --- a/views/staff/plans/_form.ejs +++ b/views/staff/plans/_form.ejs @@ -107,31 +107,31 @@
  • - +
  • - +
  • - +
  • - +
  • - +
  • diff --git a/views/staff/plans/index.ejs b/views/staff/plans/index.ejs index 121a2fc..16fcf14 100644 --- a/views/staff/plans/index.ejs +++ b/views/staff/plans/index.ejs @@ -6,31 +6,33 @@
    -[[ if (plans.length) { ]] - - [[ plans.forEach(function(plan){ ]] + [[ plans.forEach(function(plan){ ]] + [[ }) ]] - - - [[ plans.forEach(function(plan){ ]] + + [[ plans.forEach(function(plan){ ]] + [[ }) ]] [[ fields.forEach(function(field){ ]] - + [[ plans.forEach(function(plan){ ]] - [[ }) ]] + + + + [[ permissions.forEach(function(permission){ ]] - + [[ plans.forEach(function(plan){ ]] - [[ }) ]] [[ }) ]]
    - [[- plan.name ]] + + [[- plan.name ]]
    [[- field.replace(/_/," ") ]][[- field.replace(/_/g," ") ]] + [[ if (field.indexOf("_price") != -1) { ]] [[- plan[field] == 0 ? "" : "$" + (plan[field]/100).toFixed(2) ]] [[ } else { ]] @@ -41,20 +43,22 @@
    [[- permission.replace(/_/," ") ]][[- permission.replace(/_/g," ") ]] - [[- plan.permissions[permission] ? "x" : " " ]] + + [[- plan.permissions[permission] ? "x" : " " ]]

    -[[ } ]] - New Plan
    -- cgit v1.2.3-70-g09d2 From 372b8eb7f2a34902c72f5a831404070b5bd761c1 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Fri, 9 Jan 2015 07:53:23 -0500 Subject: edit brochure --- server/lib/views/index.js | 20 ++++++++++++++++++-- views/about/brochure.ejs | 12 ++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/server/lib/views/index.js b/server/lib/views/index.js index 5f9088b..2a8f921 100644 --- a/server/lib/views/index.js +++ b/server/lib/views/index.js @@ -4,6 +4,7 @@ var User = require('../schemas/User'), Project = require('../schemas/Project'), Documentation = require('../schemas/Documentation'), Collaborator = require('../schemas/Collaborator'), + Plan = require('../schemas/Plan'), config = require('../../../config'), marked = require('marked'), util = require('../util'), @@ -111,9 +112,11 @@ var views = module.exports = { res.render('about/' + name) return } - if (name == "brochure") { + if (name == "brochure" || name == "plans") { // TODO: fetch plans - res.render('about/' + name) + views_middleware.ensurePlans(req, res, function(){ + res.render('about/' + name) + }) return } if (name == "about" || name == "index") { @@ -224,6 +227,19 @@ var views = module.exports = { } var views_middleware = { + ensurePlans: function(req, res, next){ + Plan.find(function (err, plans) { + res.locals.plans = {} + plans.forEach(function(plan){ + res.locals.plans[ plan.slug ] = plan + "monthly_price yearly_price basic_layout_monthly_price basic_layout_yearly_price pro_layout_monthly_price pro_layout_yearly_price".split(" ").forEach(function(key){ + plan[key] = (plan[key]/100).toFixed(2)+"" + }) + }) + next() + }) + }, + fetchProjects: function (criteria, limit, offset, next) { limit = limit || 7 offset = offset || 0 diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs index d816dc4..4ce558a 100644 --- a/views/about/brochure.ejs +++ b/views/about/brochure.ejs @@ -19,7 +19,7 @@

    [[- plans.free.name ]]

      -
    • One exhibition with pre-designed template floor plan +
    • [[- plans.free.stock_project_limit ]] exhibition with pre-designed template floor plan
    @@ -27,10 +27,10 @@

    [[- plans.premium.name ]]

    • $[[- plans.premium.monthly_price ]]/mo or $[[- plans.premium.yearly_price ]]/year -
    • [[- plans.premium.stock_layout_project_limit ]] exhibitions included with pre-designed template floor plans +
    • [[- plans.premium.stock_project_limit ]] exhibitions included with pre-designed template floor plans
    • Each new basic floor plan costs $[[- plans.premium.basic_layout_monthly_price ]]/mo or $[[- plans.premium.basic_layout_yearly_price ]]/year, minimum 3 months -
    • Each new basic floor plan can have up to [[- plans.premium.basic_layout_project_limit ]] exhibitions +
    • Each new basic floor plan can have up to [[- plans.premium.basic_project_limit ]] exhibitions
    • VValls logo appears when embedding an exhibition on a web page
    @@ -39,10 +39,10 @@

    [[- plans.pro.name ]]

    • $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year -
    • Comes with [[- plans.premium.pro_layout_limit ]] pro floor plan and [[- plans.premium.pro_layout_project_limit ]] exhibitions +
    • Comes with [[- plans.premium.pro_layout_limit ]] pro floor plan and [[- plans.premium.pro_project_limit ]] exhibitions
    • Each new pro floor plan costs $[[- plans.pro.pro_layout_monthly_price ]]/mo or $[[- plans.pro.pro_layout_yearly_price ]]/year, minimum 3 months -
    • Each new pro floor plan can have up to [[- plans.pro.pro_layout_project_limit ]] exhibitions +
    • Each new pro floor plan can have up to [[- plans.pro.pro_project_limit ]] exhibitions
    • Includes planning for 3D objects in the room
    • No VValls logo on embed
    @@ -57,7 +57,7 @@
    - Pro Floor plan: Trace an arbitrary floor plan. + Pro Floor plan: Trace an arbitrary floor plan from image.
    -- cgit v1.2.3-70-g09d2 From 63d8dd85a47c20d3c9bad963d7845d4d5b1c2e2e Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Fri, 9 Jan 2015 08:23:35 -0500 Subject: very light styling on brochure page --- server/lib/views/staff.js | 1 + views/about/brochure.ejs | 122 +++++++++++++++++++++++++++++----------------- 2 files changed, 77 insertions(+), 46 deletions(-) diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index 2fdc2c3..97ecde6 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -128,6 +128,7 @@ var staff = module.exports = { ensurePlans: function(req, res, next){ Plan.find(function (err, plans) { res.locals.plans = (plans || []).map(staff.helpers.plan) + res.locals.plans.sort(function(a,b){ return a.monthly_price }) next() }) }, diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs index 4ce558a..75e7b60 100644 --- a/views/about/brochure.ejs +++ b/views/about/brochure.ejs @@ -13,58 +13,54 @@
    Want to get more out of VValls? Consider becoming a subscription user. +

    -
    -
    -

    [[- plans.free.name ]]

    -
      -
    • [[- plans.free.stock_project_limit ]] exhibition with pre-designed template floor plan -
    -
    +
    +

    [[- plans.free.name ]]

    +
      +
    • [[- plans.free.stock_project_limit ]] exhibition with pre-designed template floor plan +
    +
    -
    -

    [[- plans.premium.name ]]

    -
      -
    • $[[- plans.premium.monthly_price ]]/mo or $[[- plans.premium.yearly_price ]]/year -
    • [[- plans.premium.stock_project_limit ]] exhibitions included with pre-designed template floor plans -
    • Each new basic floor plan costs $[[- plans.premium.basic_layout_monthly_price ]]/mo - or $[[- plans.premium.basic_layout_yearly_price ]]/year, minimum 3 months -
    • Each new basic floor plan can have up to [[- plans.premium.basic_project_limit ]] exhibitions -
    • VValls logo appears when embedding an exhibition on a web page -
    -
    +
    +

    [[- plans.basic.name ]]

    +
      +
    • $[[- plans.basic.monthly_price ]]/mo or $[[- plans.basic.yearly_price ]]/year +
    • [[- plans.basic.stock_project_limit ]] exhibitions included with pre-designed template floor plans +
    • Each new basic floor plan costs $[[- plans.basic.basic_layout_monthly_price ]]/mo + or $[[- plans.basic.basic_layout_yearly_price ]]/year, minimum 3 months +
    • Each new basic floor plan can have up to [[- plans.basic.basic_project_limit ]] exhibitions +
    • VValls logo appears when embedding an exhibition on a web page +
    +
    -
    -

    [[- plans.pro.name ]]

    -
      -
    • $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year -
    • Comes with [[- plans.premium.pro_layout_limit ]] pro floor plan and [[- plans.premium.pro_project_limit ]] exhibitions -
    • Each new pro floor plan costs $[[- plans.pro.pro_layout_monthly_price ]]/mo - or $[[- plans.pro.pro_layout_yearly_price ]]/year, minimum 3 months -
    • Each new pro floor plan can have up to [[- plans.pro.pro_project_limit ]] exhibitions -
    • Includes planning for 3D objects in the room -
    • No VValls logo on embed -
    -
    +
    +

    [[- plans.pro.name ]]

    +
      +
    • $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year +
    • Comes with [[- plans.pro.pro_layout_limit ]] pro floor plan and [[- plans.pro.pro_project_limit ]] exhibitions +
    • Each new pro floor plan costs $[[- plans.pro.pro_layout_monthly_price ]]/mo + or $[[- plans.pro.pro_layout_yearly_price ]]/year, minimum 3 months +
    • Each new pro floor plan can have up to [[- plans.pro.pro_project_limit ]] exhibitions +
    • Includes planning for 3D objects in the room +
    • No VValls logo on embed +
    +
    -
    - Buying any extra floor plan unlocks collaboration. Invite an artist or curator to work on the exhibition with you. -
    - -
    - Basic Floor plan: Rectangle-based design of any dimension. -
    - -
    - Pro Floor plan: Trace an arbitrary floor plan from image. -
    - +
    +
      +
    • Buying any extra floor plan unlocks collaboration. Invite an artist or curator to work on the exhibition with you. +
    • Basic Floor plan: Rectangle-based design of any dimension. +
    • Pro Floor plan: Trace an arbitrary floor plan from image. +
    +
    -
    -

    Custom

    - We offer many types of customizations and white-label options for business and educational uses. - Contact us for more information +
    +

    Custom

    +
  • We offer customized white-label options for business and educational uses. +
  • Contact us for more information. +
  • @@ -77,3 +73,37 @@ [[ include ../partials/scripts ]] + \ No newline at end of file -- cgit v1.2.3-70-g09d2 From c5ab88deba82c4b4a149b3df594162351157483a Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Fri, 9 Jan 2015 10:51:24 -0500 Subject: buy buttons --- public/assets/stylesheets/app.css | 4 +++- views/about/_blank.ejs | 2 +- views/about/about.ejs | 2 +- views/about/brochure.ejs | 45 +++++++++++++++++++++++++++------------ 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 9e86ac3..a149166 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -604,7 +604,9 @@ iframe.embed { .page h1:nth-child(2) { margin-top: 40px; } - +.page h1.leader { + margin-top: 60px; +} .page p { margin: 20px; } diff --git a/views/about/_blank.ejs b/views/about/_blank.ejs index 3c23fa7..732ea3f 100644 --- a/views/about/_blank.ejs +++ b/views/about/_blank.ejs @@ -8,7 +8,7 @@
    [[ include ../partials/header ]] - +

    Hallo

    diff --git a/views/about/about.ejs b/views/about/about.ejs index 2aec982..6d5f011 100644 --- a/views/about/about.ejs +++ b/views/about/about.ejs @@ -8,7 +8,7 @@
    [[ include ../partials/header ]] -

    About VValls

    +

    About VValls

    diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs index 75e7b60..e75f8c6 100644 --- a/views/about/brochure.ejs +++ b/views/about/brochure.ejs @@ -8,7 +8,7 @@
    [[ include ../partials/header ]] -

    Subscriptions

    +

    Subscriptions

    @@ -27,11 +27,14 @@

    [[- plans.basic.name ]]

    • $[[- plans.basic.monthly_price ]]/mo or $[[- plans.basic.yearly_price ]]/year -
    • [[- plans.basic.stock_project_limit ]] exhibitions included with pre-designed template floor plans +
    • Comes with [[- plans.basic.basic_layout_limit ]] basic floor plan and [[- plans.basic.basic_project_limit ]] exhibitions
    • Each new basic floor plan costs $[[- plans.basic.basic_layout_monthly_price ]]/mo or $[[- plans.basic.basic_layout_yearly_price ]]/year, minimum 3 months -
    • Each new basic floor plan can have up to [[- plans.basic.basic_project_limit ]] exhibitions +
    • Each new floor plan can have up to [[- plans.basic.basic_project_limit ]] exhibitions
    • VValls logo appears when embedding an exhibition on a web page +
    • + +
    @@ -45,10 +48,13 @@
  • Each new pro floor plan can have up to [[- plans.pro.pro_project_limit ]] exhibitions
  • Includes planning for 3D objects in the room
  • No VValls logo on embed +
  • + +
  • -
    +
    • Buying any extra floor plan unlocks collaboration. Invite an artist or curator to work on the exhibition with you.
    • Basic Floor plan: Rectangle-based design of any dimension. @@ -56,10 +62,10 @@
    -
    -

    Custom

    +
    +

    Want Something Custom?

  • We offer customized white-label options for business and educational uses. -
  • Contact us for more information. +
  • Contact us for more information.
  • @@ -74,13 +80,23 @@ [[ include ../partials/scripts ]] \ No newline at end of file -- cgit v1.2.3-70-g09d2 From afce400c65f362f7dd6307a5670dc3873d74ab79 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Sun, 11 Jan 2015 21:42:45 -0500 Subject: stub in subscriptions admin pages --- server/lib/views/staff.js | 34 +++++++++++++++++++++++++++++++++- views/staff/_nav.ejs | 1 + views/staff/subscriptions/index.ejs | 36 ++++++++++++++++++++++++++++++++++++ views/staff/subscriptions/show.ejs | 12 ++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 views/staff/subscriptions/index.ejs create mode 100644 views/staff/subscriptions/show.ejs diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index 97ecde6..4351ef0 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -500,6 +500,27 @@ var staff = module.exports = { 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.edit + ); }, paginate: function(req, res){ @@ -669,6 +690,17 @@ var staff = module.exports = { res.redirect("/staff/plans/") }) }, - } + }, + + subscriptions: { + index: function(req, res){ + res.locals.subscriptions = req.subscriptions + res.render('staff/plans/index') + }, + show: function(req, res){ + res.locals.subscription = req.subscription + res.render('staff/plans/show') + }, + }, } diff --git a/views/staff/_nav.ejs b/views/staff/_nav.ejs index db7bedb..e79ff69 100644 --- a/views/staff/_nav.ejs +++ b/views/staff/_nav.ejs @@ -4,4 +4,5 @@ projects media plans + subscriptions \ No newline at end of file diff --git a/views/staff/subscriptions/index.ejs b/views/staff/subscriptions/index.ejs new file mode 100644 index 0000000..d1c0588 --- /dev/null +++ b/views/staff/subscriptions/index.ejs @@ -0,0 +1,36 @@ +[[ include ../_header ]] + +

    Users

    + +[[ include ../_nav ]] + +
    + +[[ include ../_pagination ]] + + +[[ subscriptions.forEach(function(subscription){ ]] + + + + + + + +[[ }) ]] +
    +
    +
    + [[- subscription.user.username ]] + + [[- subscription.user.displayName ]] + + [[- subscription.user.last_seen ]] +
    + + +[[ include ../_pagination ]] + +[[ include ../_footer ]] diff --git a/views/staff/subscriptions/show.ejs b/views/staff/subscriptions/show.ejs new file mode 100644 index 0000000..e2839a6 --- /dev/null +++ b/views/staff/subscriptions/show.ejs @@ -0,0 +1,12 @@ +[[ include ../_header ]] +

    User: [[- subscription.user.username ]]

    + +[[ include ../_nav ]] + +
    + +
    +info to show..
    +- link to recurly profile
    +- link to vvalls profile
    +- subscription tier + add-ons
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From a1c9bdecff30d72eeaef54c66c7211966a97d4e4 Mon Sep 17 00:00:00 2001
    From: Julie Lala 
    Date: Sun, 11 Jan 2015 21:57:17 -0500
    Subject: crud
    
    ---
     server/lib/views/staff.js | 69 ++++++++++++++++++++++++++++++++++++++++++++---
     1 file changed, 66 insertions(+), 3 deletions(-)
    
    diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js
    index 4351ef0..39a8dca 100644
    --- a/server/lib/views/staff.js
    +++ b/server/lib/views/staff.js
    @@ -124,7 +124,35 @@ var staff = module.exports = {
             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(function (err, plans) {
             res.locals.plans = (plans || []).map(staff.helpers.plan)
    @@ -150,6 +178,24 @@ var staff = module.exports = {
           }
         },
     
    +    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)
    @@ -160,11 +206,23 @@ var staff = module.exports = {
           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(){
    @@ -338,6 +396,13 @@ var staff = module.exports = {
           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){
    @@ -694,11 +759,9 @@ var staff = module.exports = {
       
       subscriptions: {
         index: function(req, res){
    -      res.locals.subscriptions = req.subscriptions
           res.render('staff/plans/index')
         },
         show: function(req, res){
    -      res.locals.subscription = req.subscription
           res.render('staff/plans/show')
         },
       },
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From ffdee0615c775466053daff3ce4afd1d11c83e33 Mon Sep 17 00:00:00 2001
    From: Julie Lala 
    Date: Sun, 11 Jan 2015 22:37:07 -0500
    Subject: pagination
    
    ---
     server/lib/views/staff.js | 7 +++++--
     1 file changed, 5 insertions(+), 2 deletions(-)
    
    diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js
    index 39a8dca..64cfe77 100644
    --- a/server/lib/views/staff.js
    +++ b/server/lib/views/staff.js
    @@ -759,10 +759,13 @@ var staff = module.exports = {
       
       subscriptions: {
         index: function(req, res){
    -      res.render('staff/plans/index')
    +      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/plans/show')
    +      res.render('staff/subscriptions/show')
         },
       },
     
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From 77c4c8d066611ad651af8df7290fb3e54d074081 Mon Sep 17 00:00:00 2001
    From: Julie Lala 
    Date: Sun, 11 Jan 2015 23:37:49 -0500
    Subject: fix
    
    ---
     server/lib/views/staff.js | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js
    index 64cfe77..d4abf1f 100644
    --- a/server/lib/views/staff.js
    +++ b/server/lib/views/staff.js
    @@ -584,7 +584,7 @@ var staff = module.exports = {
           staff.middleware.ensureSubscription,
           staff.middleware.ensureSubscriptionUser,
     
    -      staff.subscriptions.edit
    +      staff.subscriptions.show
         );
       },
       
    @@ -765,7 +765,7 @@ var staff = module.exports = {
           res.render('staff/subscriptions/index')
         },
         show: function(req, res){
    -      res.render('staff/subscriptions/show')
    +      res.render('staff/subscriptions /show')
         },
       },
     
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From dcfbf734436fad76f5ca2e1cecadf4051118d56f Mon Sep 17 00:00:00 2001
    From: Julie Lala 
    Date: Mon, 12 Jan 2015 12:24:57 -0500
    Subject: stubbing webhook
    
    ---
     server/index.js                  |  2 ++
     server/lib/views/staff.js        |  2 +-
     server/lib/views/subscription.js | 21 ++++++---------------
     3 files changed, 9 insertions(+), 16 deletions(-)
    
    diff --git a/server/index.js b/server/index.js
    index 9a9323c..a14eaab 100644
    --- a/server/index.js
    +++ b/server/index.js
    @@ -70,6 +70,8 @@ site.setup = function(){
     	app.all('*', middleware.ensureLocals);
     	app.all('*', middleware.ensureIP);
     
    +  app.get('/subscribe/webhook', views.subscription.webhook);
    +
     	server = http.createServer(app)
     	server.listen(app.get('port'), function () {
     		console.log('Express server listening on port ' + app.get('port'));
    diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js
    index d4abf1f..74dd7cd 100644
    --- a/server/lib/views/staff.js
    +++ b/server/lib/views/staff.js
    @@ -765,7 +765,7 @@ var staff = module.exports = {
           res.render('staff/subscriptions/index')
         },
         show: function(req, res){
    -      res.render('staff/subscriptions /show')
    +      res.render('staff/subscriptions/show')
         },
       },
     
    diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js
    index ba54bb4..e29e40d 100644
    --- a/server/lib/views/subscription.js
    +++ b/server/lib/views/subscription.js
    @@ -32,19 +32,10 @@ var subscription = module.exports = {
           return project
     	  },
     	},
    -
    -	route: function(app){
    -    app.get('/staff',
    -      middleware.ensureAuthenticated,
    -      middleware.ensureIsStaff,
    -      
    -      staff.middleware.ensureRecentUsers,
    -      staff.middleware.ensureUsersCount,
    -      staff.middleware.ensureProjectsCount,
    -      staff.middleware.ensureMediaCount,
    -
    -      staff.index
    -    );
    -  },
    -
    +	
    +  // need a route for the webhook,
    +  // then calls to get appropriate info from the recurly api
    +	webhook: function(req, res){
    +    res.status(200).end()
    +	},
     }
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From 034f8343f2d194c2b1e3dbb20cfb8658e2795ce0 Mon Sep 17 00:00:00 2001
    From: Jules Laplace 
    Date: Mon, 12 Jan 2015 18:38:42 -0500
    Subject: parsing some xml for webhooks
    
    ---
     package.json                     | 37 +++++++++---------
     server/index.js                  |  1 +
     server/lib/views/subscription.js | 81 +++++++++++++++++++++++++++++++++++-----
     3 files changed, 92 insertions(+), 27 deletions(-)
    
    diff --git a/package.json b/package.json
    index adefb82..34e0c4c 100644
    --- a/package.json
    +++ b/package.json
    @@ -6,32 +6,33 @@
         "url": "git://github.com/okfocus/vvalls.git"
       },
       "dependencies": {
    -    "express": "~3.4.8",
    -    "monk": "~0.7.1",
    -    "socket.io": "~0.9.16",
    +    "body-parser": "1.3.0",
         "connect-mongo": "~0.4.1",
    -    "passport": "~0.2.0",
    -    "passport-local": "~1.0.0",
    -    "passport-twitter": "~1.0.2",
    -    "passport-facebook": "~1.0.3",
    -    "passport.socketio": "~3.0.1",
    -    "node-restful": "~0.1.14",
         "ejs": "^0.8.8",
    -    "useful-string": "0.0.1",
    +    "emailjs": "~0.3.6",
    +    "express": "~3.4.8",
         "express-subdomain-handler": "~0.1.0",
         "express-subdomains": "0.0.5",
    +    "html-entities": "~1.0.10",
    +    "intro.js": "^0.9.0",
    +    "knox": "~0.8.10",
         "lodash": "~2.4.1",
    +    "marked": "~0.3.2",
    +    "moment": "~2.6.0",
         "mongoose": "~3.8.8",
    -    "mongoose-unique-validator": "~0.3.0",
         "mongoose-lifecycle": "~1.0.0",
    -    "knox": "~0.8.10",
    -    "moment": "~2.6.0",
    -    "html-entities": "~1.0.10",
    +    "mongoose-unique-validator": "~0.3.0",
    +    "monk": "~0.7.1",
         "multer": "~0.1.0",
    -    "body-parser": "1.3.0",
    -    "marked": "~0.3.2",
    -    "emailjs": "~0.3.6",
    -    "intro.js": "^0.9.0"
    +    "node-restful": "~0.1.14",
    +    "passport": "~0.2.0",
    +    "passport-facebook": "~1.0.3",
    +    "passport-local": "~1.0.0",
    +    "passport-twitter": "~1.0.2",
    +    "passport.socketio": "~3.0.1",
    +    "socket.io": "~0.9.16",
    +    "useful-string": "0.0.1",
    +    "xml2js": "^0.4.4"
       },
       "devDependencies": {
         "grunt": "~0.4.1",
    diff --git a/server/index.js b/server/index.js
    index a14eaab..0f4941a 100644
    --- a/server/index.js
    +++ b/server/index.js
    @@ -70,6 +70,7 @@ site.setup = function(){
     	app.all('*', middleware.ensureLocals);
     	app.all('*', middleware.ensureIP);
     
    +	// where should this live?
       app.get('/subscribe/webhook', views.subscription.webhook);
     
     	server = http.createServer(app)
    diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js
    index e29e40d..251e217 100644
    --- a/server/lib/views/subscription.js
    +++ b/server/lib/views/subscription.js
    @@ -6,7 +6,15 @@ var User = require('../schemas/User'),
     	middleware = require('../middleware'),
     	util = require('../util'),
     	_ = require('lodash'),
    -	moment = require('moment');
    +	moment = require('moment'),
    +	xml2js = require('xml2js');
    +
    +var parser = new xml2js.Parser();
    +// fs.readFile('./foo.xml', function(err, data) {
    +// 	parser.parseString(data, function (err, result) {
    +// 		console.log(inspect(result, { colors: true, depth: Infinity }));
    +// 	});
    +// });
     
     var subscription = module.exports = {
     	
    @@ -24,18 +32,73 @@ var subscription = module.exports = {
     	middleware: {
       },
     
    -	helpers: {
    -	  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
    -	  },
    -	},
    +	fields: [
    +		"new_account_notification",
    +		"canceled_account_notification",
    +		"billing_info_updated_notification",
    +		"reactivated_account_notification",
    +		"new_invoice_notification",
    +		"closed_invoice_notification",
    +		"past_due_invoice_notification",
    +		"new_subscription_notification",
    +		"updated_subscription_notification",
    +		"canceled_subscription_notification",
    +		"expired_subscription_notification",
    +		"renewed_subscription_notification",
    +		"successful_payment_notification",
    +		"failed_payment_notification",
    +		"successful_refund_notification",
    +		"void_payment_notification",
    +	],  
    +
    +  callbacks: {
    +		// accounts
    +		new_account_notification: function(data){
    +		},
    +		canceled_account_notification: function(data){
    +		},
    +		billing_info_updated_notification: function(data){
    +		},
    +		reactivated_account_notification: function(data){
    +		},
    +		
    +		// invoices
    +		new_invoice_notification: function(data){
    +		},
    +		closed_invoice_notification: function(data){
    +		},
    +		past_due_invoice_notification: function(data){
    +		},
    +		
    +		// subscriptions
    +		new_subscription_notification: function(data){
    +		},
    +		updated_subscription_notification: function(data){
    +		},
    +		canceled_subscription_notification: function(data){
    +		},
    +		expired_subscription_notification: function(data){
    +		},
    +		renewed_subscription_notification: function(data){
    +		},
    +		
    +		// payments
    +		successful_payment_notification: function(data){
    +		},
    +		failed_payment_notification: function(data){
    +		},
    +		successful_refund_notification: function(data){
    +		},
    +		void_payment_notification: function(data){
    +		},
    +  },
     	
       // need a route for the webhook,
       // then calls to get appropriate info from the recurly api
     	webhook: function(req, res){
         res.status(200).end()
    +		parser.parseString(data, function (err, result) {
    +			console.log(inspect(result, { colors: true, depth: Infinity }));
    +		});
     	},
     }
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From ffa627b1032f9244df8c685c86fd24f3e7c2881a Mon Sep 17 00:00:00 2001
    From: Julie Lala 
    Date: Mon, 12 Jan 2015 23:35:57 -0500
    Subject: etc
    
    ---
     server/lib/views/subscription.js | 29 +++++++++++++++--------------
     1 file changed, 15 insertions(+), 14 deletions(-)
    
    diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js
    index 251e217..b9c79cb 100644
    --- a/server/lib/views/subscription.js
    +++ b/server/lib/views/subscription.js
    @@ -18,39 +18,35 @@ var parser = new xml2js.Parser();
     
     var subscription = module.exports = {
     	
    -	fields: {
    -	  user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip",
    -	},
    -	
    -	defaults: {
    -	  user: {
    -	    _id: "", username: "", displayName: "",
    -      created_at: "", updated_at: "", created_ip: "", last_ip: "",
    -	  },
    -	},
    -
    -	middleware: {
    -  },
    -
     	fields: [
    +	  // accounts
     		"new_account_notification",
     		"canceled_account_notification",
     		"billing_info_updated_notification",
     		"reactivated_account_notification",
    +
    +		// invoices
     		"new_invoice_notification",
     		"closed_invoice_notification",
     		"past_due_invoice_notification",
    +		
    +		// subscriptions
     		"new_subscription_notification",
     		"updated_subscription_notification",
     		"canceled_subscription_notification",
     		"expired_subscription_notification",
     		"renewed_subscription_notification",
    +		
    +		// payments
     		"successful_payment_notification",
     		"failed_payment_notification",
     		"successful_refund_notification",
     		"void_payment_notification",
     	],  
     
    +	middleware: {
    +  },
    +
       callbacks: {
     		// accounts
     		new_account_notification: function(data){
    @@ -99,6 +95,11 @@ var subscription = module.exports = {
         res.status(200).end()
     		parser.parseString(data, function (err, result) {
     			console.log(inspect(result, { colors: true, depth: Infinity }));
    +			for (var i in data) {
    +			  if (subscription.callbacks[i]) {
    +			    subscription.callbacks[i](data)
    +			  }
    +			}
     		});
     	},
     }
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From 904a18b763889c9f85d874388fdff9daaefb89cd Mon Sep 17 00:00:00 2001
    From: Jules Laplace 
    Date: Wed, 14 Jan 2015 15:44:34 -0500
    Subject: roll back devicePixelRatio fix on desktop
    
    ---
     public/assets/javascripts/app.js   |  4 +++-
     public/assets/javascripts/mx/mx.js | 14 +++++++-------
     public/assets/stylesheets/app.css  |  4 ++++
     3 files changed, 14 insertions(+), 8 deletions(-)
    
    diff --git a/public/assets/javascripts/app.js b/public/assets/javascripts/app.js
    index 41edafe..a146325 100644
    --- a/public/assets/javascripts/app.js
    +++ b/public/assets/javascripts/app.js
    @@ -3,7 +3,7 @@ if (is_mobile) {
     	$("html").addClass("mobile")
     }
     else {
    - $("html").addClass("desktop")
    +  $("html").addClass("desktop")
     }
     
     
    @@ -23,6 +23,8 @@ app.launch = function () {
       
     	var movements
     
    +	app.devicePixelRatio = is_mobile ? devicePixelRatio : 1
    +
     	scene = new MX.Scene().addTo('#scene')
     	scene.width = window.innerWidth
     	scene.height = window.innerHeight
    diff --git a/public/assets/javascripts/mx/mx.js b/public/assets/javascripts/mx/mx.js
    index d59a551..ab9a9a0 100644
    --- a/public/assets/javascripts/mx/mx.js
    +++ b/public/assets/javascripts/mx/mx.js
    @@ -162,24 +162,24 @@ var MX = MX || (function (undefined) {
             Object.defineProperty(this, 'width', {
                 get: function () {
                     return width
    -                    || parseInt(self.el.style.width*devicePixelRatio, 10)
    +                    || parseInt(self.el.style.width, 10) * app.devicePixelRatio
                         || 0
                 },
                 set: function (val) {
                     width = val
    -                this.el.style.width = (width/devicePixelRatio) + 'px'
    +                this.el.style.width = (width/app.devicePixelRatio) + 'px'
                 }
             })
     
             Object.defineProperty(this, 'height', {
                 get: function () {
                     return height
    -                    || parseInt(self.el.style.height*devicePixelRatio, 10)
    +                    || parseInt(self.el.style.height, 10) * app.devicePixelRatio
                         || 0
                 },
                 set: function (val) {
                     height = val
    -                this.el.style.height = (height/devicePixelRatio) + 'px'
    +                this.el.style.height = (height/app.devicePixelRatio) + 'px'
                 }
             })
         }
    @@ -302,9 +302,9 @@ var MX = MX || (function (undefined) {
                             + (-this.y).toFixed(floatPrecision) + 'px,'
                             + (-this.z).toFixed(floatPrecision) + 'px) '
                         + 'scale3d('
    -                        + (devicePixelRatio * this.scaleX).toFixed(floatPrecision) + ','
    -                        + (devicePixelRatio * this.scaleY).toFixed(floatPrecision) + ','
    -                        + (devicePixelRatio * this.scaleZ).toFixed(floatPrecision) + ') '
    +                        + (app.devicePixelRatio * this.scaleX).toFixed(floatPrecision) + ','
    +                        + (app.devicePixelRatio * this.scaleY).toFixed(floatPrecision) + ','
    +                        + (app.devicePixelRatio * this.scaleZ).toFixed(floatPrecision) + ') '
     
                     if (rotationTranslation) {
                         transformString += rotationTranslation.before
    diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css
    index a149166..0ce2c5e 100755
    --- a/public/assets/stylesheets/app.css
    +++ b/public/assets/stylesheets/app.css
    @@ -929,6 +929,9 @@ border-left: 1px solid black;
     .no-templates {
     	display: none;
     }
    +.no-templates a {
    +	border-bottom: 1px solid;
    +}
     
     .templates span {
     	display: block;
    @@ -2373,6 +2376,7 @@ button {
       font-weight: 500;
       width: 100%;
     	font-size:14px;
    +	font-family:'Lato', sans-serif;
     }
     
     #builder-units {
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From 717e87b7422db8e1eda655fbf04e45fe5f877c9b Mon Sep 17 00:00:00 2001
    From: Jules Laplace 
    Date: Tue, 20 Jan 2015 00:10:23 -0500
    Subject: combining webhook stuff
    
    ---
     package.json                       |   1 +
     server/index.js                    |   3 --
     server/lib/api/profile.js          |   1 +
     server/lib/schemas/Subscription.js |   1 +
     server/lib/schemas/User.js         |   4 +-
     server/lib/views/index.js          |   1 -
     server/lib/views/subscription.js   | 105 -------------------------------------
     server/lib/webhook/config.js       |   6 +++
     server/lib/webhook/index.js        |  88 +++++++++++++++++++++++++++++++
     9 files changed, 100 insertions(+), 110 deletions(-)
     delete mode 100644 server/lib/views/subscription.js
     create mode 100644 server/lib/webhook/config.js
     create mode 100644 server/lib/webhook/index.js
    
    diff --git a/package.json b/package.json
    index 34e0c4c..8afb96e 100644
    --- a/package.json
    +++ b/package.json
    @@ -24,6 +24,7 @@
         "mongoose-unique-validator": "~0.3.0",
         "monk": "~0.7.1",
         "multer": "~0.1.0",
    +    "node-recurly": "^2.1.0",
         "node-restful": "~0.1.14",
         "passport": "~0.2.0",
         "passport-facebook": "~1.0.3",
    diff --git a/server/index.js b/server/index.js
    index 0f4941a..9a9323c 100644
    --- a/server/index.js
    +++ b/server/index.js
    @@ -70,9 +70,6 @@ site.setup = function(){
     	app.all('*', middleware.ensureLocals);
     	app.all('*', middleware.ensureIP);
     
    -	// where should this live?
    -  app.get('/subscribe/webhook', views.subscription.webhook);
    -
     	server = http.createServer(app)
     	server.listen(app.get('port'), function () {
     		console.log('Express server listening on port ' + app.get('port'));
    diff --git a/server/lib/api/profile.js b/server/lib/api/profile.js
    index 996505f..d72a2c3 100644
    --- a/server/lib/api/profile.js
    +++ b/server/lib/api/profile.js
    @@ -32,6 +32,7 @@ var profile = {
     		delete data.old_password
     		delete data.new_password
     		delete data.isStaff
    +		delete data.plan_level
     		data.updated_at = new Date ()
     		
     		if (req.files.avatar) {
    diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js
    index 8315009..7f2579b 100644
    --- a/server/lib/schemas/Subscription.js
    +++ b/server/lib/schemas/Subscription.js
    @@ -15,6 +15,7 @@ var SubscriptionSchema = new mongoose.Schema({
     	plans: [{
         tier: { type: String },
         monthly: { type: Boolean },
    +    projects: [{ type: mongoose.Schema.ObjectId }],
     	}],
     	
     	created_at: { type: Date, default: Date.now },
    diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js
    index 180a140..ae1d912 100644
    --- a/server/lib/schemas/User.js
    +++ b/server/lib/schemas/User.js
    @@ -54,7 +54,9 @@ var UserSchema = new mongoose.Schema({
     		type: String,
     		default: "",
     	},
    -
    +	
    +	plan_level: { type: Number, default: 0 },
    +	
     	location: { type: String, default: "" },
     	photo: { type: String, default: "" },
     	bio: { type: String, default: "" },
    diff --git a/server/lib/views/index.js b/server/lib/views/index.js
    index 2a8f921..0ce0357 100644
    --- a/server/lib/views/index.js
    +++ b/server/lib/views/index.js
    @@ -22,7 +22,6 @@ marked.setOptions({
     var views = module.exports = {
     
     	staff: require('./staff'),
    -	subscription: require('./subscription'),
     
     	editor_new: function (req, res) {
     		if (! req.user) {
    diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js
    deleted file mode 100644
    index b9c79cb..0000000
    --- a/server/lib/views/subscription.js
    +++ /dev/null
    @@ -1,105 +0,0 @@
    -/* jshint node: true */
    -
    -var User = require('../schemas/User'),
    -	Subscription = require('../schemas/Subscription'),
    -	config = require('../../../config'),
    -	middleware = require('../middleware'),
    -	util = require('../util'),
    -	_ = require('lodash'),
    -	moment = require('moment'),
    -	xml2js = require('xml2js');
    -
    -var parser = new xml2js.Parser();
    -// fs.readFile('./foo.xml', function(err, data) {
    -// 	parser.parseString(data, function (err, result) {
    -// 		console.log(inspect(result, { colors: true, depth: Infinity }));
    -// 	});
    -// });
    -
    -var subscription = module.exports = {
    -	
    -	fields: [
    -	  // accounts
    -		"new_account_notification",
    -		"canceled_account_notification",
    -		"billing_info_updated_notification",
    -		"reactivated_account_notification",
    -
    -		// invoices
    -		"new_invoice_notification",
    -		"closed_invoice_notification",
    -		"past_due_invoice_notification",
    -		
    -		// subscriptions
    -		"new_subscription_notification",
    -		"updated_subscription_notification",
    -		"canceled_subscription_notification",
    -		"expired_subscription_notification",
    -		"renewed_subscription_notification",
    -		
    -		// payments
    -		"successful_payment_notification",
    -		"failed_payment_notification",
    -		"successful_refund_notification",
    -		"void_payment_notification",
    -	],  
    -
    -	middleware: {
    -  },
    -
    -  callbacks: {
    -		// accounts
    -		new_account_notification: function(data){
    -		},
    -		canceled_account_notification: function(data){
    -		},
    -		billing_info_updated_notification: function(data){
    -		},
    -		reactivated_account_notification: function(data){
    -		},
    -		
    -		// invoices
    -		new_invoice_notification: function(data){
    -		},
    -		closed_invoice_notification: function(data){
    -		},
    -		past_due_invoice_notification: function(data){
    -		},
    -		
    -		// subscriptions
    -		new_subscription_notification: function(data){
    -		},
    -		updated_subscription_notification: function(data){
    -		},
    -		canceled_subscription_notification: function(data){
    -		},
    -		expired_subscription_notification: function(data){
    -		},
    -		renewed_subscription_notification: function(data){
    -		},
    -		
    -		// payments
    -		successful_payment_notification: function(data){
    -		},
    -		failed_payment_notification: function(data){
    -		},
    -		successful_refund_notification: function(data){
    -		},
    -		void_payment_notification: function(data){
    -		},
    -  },
    -	
    -  // need a route for the webhook,
    -  // then calls to get appropriate info from the recurly api
    -	webhook: function(req, res){
    -    res.status(200).end()
    -		parser.parseString(data, function (err, result) {
    -			console.log(inspect(result, { colors: true, depth: Infinity }));
    -			for (var i in data) {
    -			  if (subscription.callbacks[i]) {
    -			    subscription.callbacks[i](data)
    -			  }
    -			}
    -		});
    -	},
    -}
    diff --git a/server/lib/webhook/config.js b/server/lib/webhook/config.js
    new file mode 100644
    index 0000000..ecafeb3
    --- /dev/null
    +++ b/server/lib/webhook/config.js
    @@ -0,0 +1,6 @@
    +module.exports = {
    +	API_KEY: require('process').env['VVALLS_RECURLY_SECRET'],
    +	SUBDOMAIN: 'vvalls',
    +	ENVIRONMENT: 'sandbox',
    +	DEBUG: true,
    +};
    diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js
    new file mode 100644
    index 0000000..c4b4b76
    --- /dev/null
    +++ b/server/lib/webhook/index.js
    @@ -0,0 +1,88 @@
    +// // where should this live?
    +// app.get('/subscribe/webhook', views.subscription.webhook);
    +
    +/* jshint node: true */
    +
    +var User = require('../schemas/User'),
    +	Subscription = require('../schemas/Subscription'),
    +	config = require('../../../config'),
    +	middleware = require('../middleware'),
    +	util = require('../util'),
    +	_ = require('lodash'),
    +	moment = require('moment'),
    +	xml2js = require('xml2js'),
    +	Recurly = require('node-recurly'),
    +	recurly = new Recurly(require('./config'));
    +
    +var parser = new xml2js.Parser();
    +// fs.readFile('./foo.xml', function(err, data) {
    +// 	parser.parseString(data, function (err, result) {
    +// 		console.log(inspect(result, { colors: true, depth: Infinity }));
    +// 	});
    +// });
    +
    +/*
    +app.use(express.basicAuth(function(user, pass, callback) {
    + var result = (user === 'testUser' && pass === 'testPass');
    + callback(null, result);
    +}));
    +*/
    +
    +var subscription = module.exports = {
    +	
    +  callbacks: {
    +		// accounts
    +		new_account_notification: function(data){
    +		},
    +		canceled_account_notification: function(data){
    +		},
    +		billing_info_updated_notification: function(data){
    +		},
    +		reactivated_account_notification: function(data){
    +		},
    +		
    +		// invoices
    +		new_invoice_notification: function(data){
    +		},
    +		closed_invoice_notification: function(data){
    +		},
    +		past_due_invoice_notification: function(data){
    +		},
    +		
    +		// subscriptions
    +		new_subscription_notification: function(data){
    +		},
    +		updated_subscription_notification: function(data){
    +		},
    +		canceled_subscription_notification: function(data){
    +		},
    +		expired_subscription_notification: function(data){
    +		},
    +		renewed_subscription_notification: function(data){
    +		},
    +		
    +		// payments
    +		successful_payment_notification: function(data){
    +		},
    +		failed_payment_notification: function(data){
    +		},
    +		successful_refund_notification: function(data){
    +		},
    +		void_payment_notification: function(data){
    +		},
    +  },
    +	
    +  // need a route for the webhook,
    +  // then calls to get appropriate info from the recurly api
    +	webhook: function(req, res){
    +    res.status(200).end()
    +		parser.parseString(data, function (err, result) {
    +			console.log(inspect(result, { colors: true, depth: Infinity }));
    +			for (var i in data) {
    +			  if (subscription.callbacks[i]) {
    +			    subscription.callbacks[i](data[i])
    +			  }
    +			}
    +		});
    +	},
    +}
    -- 
    cgit v1.2.3-70-g09d2
    
    
    From 3eee6a15ee44a75f6deedd073f60b88d342d56ef Mon Sep 17 00:00:00 2001
    From: Jules Laplace 
    Date: Thu, 22 Jan 2015 16:54:08 -0500
    Subject: recurly links on brochure
    
    ---
     server/lib/schemas/Plan.js  |  2 ++
     server/lib/schemas/User.js  |  1 +
     server/lib/webhook/index.js |  1 +
     views/about/brochure.ejs    | 19 +++++++++++++++----
     4 files changed, 19 insertions(+), 4 deletions(-)
    
    diff --git a/server/lib/schemas/Plan.js b/server/lib/schemas/Plan.js
    index 1208672..8a19b99 100644
    --- a/server/lib/schemas/Plan.js
    +++ b/server/lib/schemas/Plan.js
    @@ -10,6 +10,8 @@ var PlanSchema = new mongoose.Schema({
       name: { type: String },
       slug: { type: String },
     
    +  level: { type: Number },
    +
       monthly_price: { type: Number },
       yearly_price: { type: Number },
     
    diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js
    index ae1d912..867939e 100644
    --- a/server/lib/schemas/User.js
    +++ b/server/lib/schemas/User.js
    @@ -56,6 +56,7 @@ var UserSchema = new mongoose.Schema({
     	},
     	
     	plan_level: { type: Number, default: 0 },
    +	subscription_id: { type: mongoose.Schema.ObjectId },
     	
     	location: { type: String, default: "" },
     	photo: { type: String, default: "" },
    diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js
    index c4b4b76..ebbd01a 100644
    --- a/server/lib/webhook/index.js
    +++ b/server/lib/webhook/index.js
    @@ -33,6 +33,7 @@ var subscription = module.exports = {
       callbacks: {
     		// accounts
     		new_account_notification: function(data){
    +			// 
     		},
     		canceled_account_notification: function(data){
     		},
    diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs
    index e75f8c6..cffa51f 100644
    --- a/views/about/brochure.ejs
    +++ b/views/about/brochure.ejs
    @@ -33,8 +33,14 @@
             
  • Each new floor plan can have up to [[- plans.basic.basic_project_limit ]] exhibitions
  • VValls logo appears when embedding an exhibition on a web page
  • - - + [[ if (! logged_in) { ]] + + [[ } else if (! profile.plan_level || profile.plan_level < plan.level) { ]] + + [[ } else if (profile.plan_level == plan.level) { ]] + Current Level + [[ } else { ]] + [[ } ]]
  • @@ -49,8 +55,13 @@
  • Includes planning for 3D objects in the room
  • No VValls logo on embed
  • - - + [[ if (! logged_in) { ]] + + [[ } else if (! profile.plan_level || profile.plan_level < plan.level) { ]] + + [[ } else if (profile.plan_level == plan.level) { ]] + Current Level + [[ } ]]
  • -- cgit v1.2.3-70-g09d2 From e0debd761bc24cd42f4140d2b6cc32a4271fe975 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Fri, 23 Jan 2015 02:35:46 -0500 Subject: getting webhooks in order.. didnt need as many as i thought --- Procfile | 3 +- config.json.example | 1 + server/index.js | 2 +- server/lib/schemas/User.js | 2 + server/lib/webhook/config.js | 2 +- server/lib/webhook/index.js | 119 +++++++++++++----------------------------- server/lib/webhook/webhook.js | 118 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 161 insertions(+), 86 deletions(-) create mode 100644 server/lib/webhook/webhook.js diff --git a/Procfile b/Procfile index 983db91..bab08fb 100644 --- a/Procfile +++ b/Procfile @@ -1 +1,2 @@ -web: node server \ No newline at end of file +web: node server +webhook: node server/lib/webhook \ No newline at end of file diff --git a/config.json.example b/config.json.example index 6028021..b8c310c 100644 --- a/config.json.example +++ b/config.json.example @@ -3,6 +3,7 @@ "hostName": "lvh.me", "port": 3000, "socketPort": 1337, + "webhookPort": 5000, "databaseHost": "lvh.me", "pageSize": 10, "env": { "development": 1 } diff --git a/server/index.js b/server/index.js index 9a9323c..02fea3c 100644 --- a/server/index.js +++ b/server/index.js @@ -72,7 +72,7 @@ site.setup = function(){ server = http.createServer(app) server.listen(app.get('port'), function () { - console.log('Express server listening on port ' + app.get('port')); + console.log('Vvalls server listening on port ' + app.get('port')); }); // var io = websocket.listen(server) diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js index 867939e..77de2d4 100644 --- a/server/lib/schemas/User.js +++ b/server/lib/schemas/User.js @@ -56,6 +56,8 @@ var UserSchema = new mongoose.Schema({ }, plan_level: { type: Number, default: 0 }, + plan_type: { type: String, default: "free" }, + last_charged: { type: Date, default: null }, subscription_id: { type: mongoose.Schema.ObjectId }, location: { type: String, default: "" }, diff --git a/server/lib/webhook/config.js b/server/lib/webhook/config.js index ecafeb3..3d7e1c5 100644 --- a/server/lib/webhook/config.js +++ b/server/lib/webhook/config.js @@ -1,5 +1,5 @@ module.exports = { - API_KEY: require('process').env['VVALLS_RECURLY_SECRET'], + API_KEY: process.env['VVALLS_RECURLY_SECRET'], SUBDOMAIN: 'vvalls', ENVIRONMENT: 'sandbox', DEBUG: true, diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js index ebbd01a..7dd68e6 100644 --- a/server/lib/webhook/index.js +++ b/server/lib/webhook/index.js @@ -1,89 +1,42 @@ -// // where should this live? -// app.get('/subscribe/webhook', views.subscription.webhook); +var config = require('../../../config.json'), + http = require('http'), + express = require('express'), + bodyParser = require('body-parser'), + mongoose = require('mongoose'); -/* jshint node: true */ +var http = require('http'), + express = require('express'), + bodyParser = require('body-parser'), + multer = require('multer'), + MongoStore = require('connect-mongo')(express), + passport = require('passport'), + path = require('path'), + mongoose = require('mongoose'); -var User = require('../schemas/User'), - Subscription = require('../schemas/Subscription'), - config = require('../../../config'), - middleware = require('../middleware'), - util = require('../util'), - _ = require('lodash'), - moment = require('moment'), - xml2js = require('xml2js'), - Recurly = require('node-recurly'), - recurly = new Recurly(require('./config')); +var app = express() +var server +var DATABASE_URI = process.env.MONGOLAB_URI || ('mongodb://' + config.databaseHost + '/vvalls') -var parser = new xml2js.Parser(); -// fs.readFile('./foo.xml', function(err, data) { -// parser.parseString(data, function (err, result) { -// console.log(inspect(result, { colors: true, depth: Infinity })); -// }); -// }); +var site = {} -/* -app.use(express.basicAuth(function(user, pass, callback) { - var result = (user === 'testUser' && pass === 'testPass'); - callback(null, result); -})); -*/ +site.init = function(){ + mongoose.connect(DATABASE_URI, {}, site.ready); +} + +site.ready = function(){ + app.set('port', config.webhookPort); + app.use(bodyParser()); + app.use(express.query()); + app.set('env', config.env.production ? "production" : "development") + app.get('env') === 'development' && app.use(express.errorHandler()); -var subscription = module.exports = { - - callbacks: { - // accounts - new_account_notification: function(data){ - // - }, - canceled_account_notification: function(data){ - }, - billing_info_updated_notification: function(data){ - }, - reactivated_account_notification: function(data){ - }, - - // invoices - new_invoice_notification: function(data){ - }, - closed_invoice_notification: function(data){ - }, - past_due_invoice_notification: function(data){ - }, - - // subscriptions - new_subscription_notification: function(data){ - }, - updated_subscription_notification: function(data){ - }, - canceled_subscription_notification: function(data){ - }, - expired_subscription_notification: function(data){ - }, - renewed_subscription_notification: function(data){ - }, - - // payments - successful_payment_notification: function(data){ - }, - failed_payment_notification: function(data){ - }, - successful_refund_notification: function(data){ - }, - void_payment_notification: function(data){ - }, - }, - - // need a route for the webhook, - // then calls to get appropriate info from the recurly api - webhook: function(req, res){ - res.status(200).end() - parser.parseString(data, function (err, result) { - console.log(inspect(result, { colors: true, depth: Infinity })); - for (var i in data) { - if (subscription.callbacks[i]) { - subscription.callbacks[i](data[i]) - } - } - }); - }, + server = http.createServer(app) + server.listen(app.get('port'), function () { + console.log('Webhook server listening on port ' + app.get('port')); + }); + + app.get('/', function(req,res){ res.send('HI THERE') }) + app.get('/subscribe/webhook', require('./webhook').webhook); } + +site.init() diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js new file mode 100644 index 0000000..067af30 --- /dev/null +++ b/server/lib/webhook/webhook.js @@ -0,0 +1,118 @@ +// // where should this live? +// app.get('/subscribe/webhook', views.subscription.webhook); + +/* +app.use(express.basicAuth(function(user, pass, callback) { + var result = (user === 'testUser' && pass === 'testPass'); + callback(null, result); +})); +*/ + + +/* jshint node: true */ + +var User = require('../schemas/User'), + Subscription = require('../schemas/Subscription'), + config = require('../../../config'), + middleware = require('../middleware'), + util = require('../util'), + _ = require('lodash'), + moment = require('moment'), + xml2js = require('xml2js'), + Recurly = require('node-recurly'), + recurly = new Recurly(require('./config')); + +var parser = new xml2js.Parser(); + +var subscription = module.exports = { + plan_level: { + free: 0, + basic: 1, + pro: 2, + }, + + callbacks: { + // accounts + new_account_notification: function(data){ + // fires on successful signup + // use the account_code to find the user, + }, + canceled_account_notification: function(data){ + }, + billing_info_updated_notification: function(data){ + }, + reactivated_account_notification: function(data){ + }, + + // invoices + new_invoice_notification: function(data){ + }, + closed_invoice_notification: function(data){ + }, + past_due_invoice_notification: function(data){ + }, + + // subscriptions + new_subscription_notification: function(data){ + var account = data.account + User.findOne({ _id: account.account_code }, function(err, user){ + if (err) return; + var subscription = data.subscription + + var plan = subscription.plan.plan_code.split("_") + var plan_type = plan[0] + var plan_period = plan[1] + + user.plan_type = plan_type + user.plan_level = subscription.plan_level[plan_type] + + // subscription.uuid + // subscription.subscription_add_ons[] + // subscription.subscription_add_ons[0].quantity + user.save(function(){ + }) + + }) + }, + updated_subscription_notification: function(data){ + }, + canceled_subscription_notification: function(data){ + }, + expired_subscription_notification: function(data){ + }, + renewed_subscription_notification: function(data){ + }, + + // payments + successful_payment_notification: function(data){ + var account = data.account + User.findOne({ _id: account.account_code }, function(err, user){ + if (err) return; + user.last_charged = new Date(data.transaction.date) + user.save(function(){ + }) + }) + }, + failed_payment_notification: function(data){ + }, + successful_refund_notification: function(data){ + }, + void_payment_notification: function(data){ + }, + + }, + + // need a route for the webhook, + // then calls to get appropriate info from the recurly api + webhook: function(req, res){ + res.status(200).end() + parser.parseString(data, function (err, result) { + console.log(inspect(result, { colors: true, depth: Infinity })); + for (var i in data) { + if (subscription.callbacks[i]) { + subscription.callbacks[i](data[i]) + } + } + }); + }, +} -- cgit v1.2.3-70-g09d2 From 74fb7a313b4d9ad3517e97133febff9cada96fe0 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 23 Jan 2015 14:46:44 -0500 Subject: handle subscribe and payment events with webhook --- server/lib/schemas/Subscription.js | 17 +++--- server/lib/schemas/User.js | 1 - server/lib/webhook/index.js | 4 +- server/lib/webhook/webhook.js | 108 ++++++++++++++++++++++--------------- 4 files changed, 76 insertions(+), 54 deletions(-) diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js index 7f2579b..99e4ebf 100644 --- a/server/lib/schemas/Subscription.js +++ b/server/lib/schemas/Subscription.js @@ -8,14 +8,15 @@ var mongoose = require('mongoose'), var SubscriptionSchema = new mongoose.Schema({ user_id: { type: mongoose.Schema.ObjectId, index: true }, - - monthly_total: { type: Number }, - yearly_total: { type: Number }, - - plans: [{ - tier: { type: String }, - monthly: { type: Boolean }, - projects: [{ type: mongoose.Schema.ObjectId }], + + plan_level: { type: Number, default: 0 }, + plan_type: { type: String, default: "free" }, + last_charged: { type: Date, default: null }, + + subscription_uuid: { type: String }, + subscription_add_ons: [{ + name: { type: String }, + quantity: { type: Number }, }], created_at: { type: Date, default: Date.now }, diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js index 77de2d4..06ad33d 100644 --- a/server/lib/schemas/User.js +++ b/server/lib/schemas/User.js @@ -58,7 +58,6 @@ var UserSchema = new mongoose.Schema({ plan_level: { type: Number, default: 0 }, plan_type: { type: String, default: "free" }, last_charged: { type: Date, default: null }, - subscription_id: { type: mongoose.Schema.ObjectId }, location: { type: String, default: "" }, photo: { type: String, default: "" }, diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js index 7dd68e6..11419c2 100644 --- a/server/lib/webhook/index.js +++ b/server/lib/webhook/index.js @@ -13,6 +13,8 @@ var http = require('http'), path = require('path'), mongoose = require('mongoose'); +var webhook = require('./webhook'); + var app = express() var server var DATABASE_URI = process.env.MONGOLAB_URI || ('mongodb://' + config.databaseHost + '/vvalls') @@ -36,7 +38,7 @@ site.ready = function(){ }); app.get('/', function(req,res){ res.send('HI THERE') }) - app.get('/subscribe/webhook', require('./webhook').webhook); + webhook.route(app) } site.init() diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 067af30..2e5e627 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -24,7 +24,7 @@ var User = require('../schemas/User'), var parser = new xml2js.Parser(); -var subscription = module.exports = { +var subscribe = module.exports = { plan_level: { free: 0, basic: 1, @@ -32,87 +32,107 @@ var subscription = module.exports = { }, callbacks: { +/* // accounts - new_account_notification: function(data){ + new_account_notification: function(data, user){ // fires on successful signup - // use the account_code to find the user, }, - canceled_account_notification: function(data){ + canceled_account_notification: function(data, user){ }, - billing_info_updated_notification: function(data){ + billing_info_updated_notification: function(data, user){ }, - reactivated_account_notification: function(data){ + reactivated_account_notification: function(data, user){ }, // invoices - new_invoice_notification: function(data){ + new_invoice_notification: function(data, user){ }, - closed_invoice_notification: function(data){ + closed_invoice_notification: function(data, user){ }, - past_due_invoice_notification: function(data){ + past_due_invoice_notification: function(data, user){ }, - +*/ + // subscriptions - new_subscription_notification: function(data){ + new_subscription_notification: function(data, user){ var account = data.account - User.findOne({ _id: account.account_code }, function(err, user){ - if (err) return; - var subscription = data.subscription - - var plan = subscription.plan.plan_code.split("_") + Subscription.findOne({ "uuid": data.subscription.uuid }, function(err, subscription){ + if (err || subscription) return; + + var plan = data.subscription.plan.plan_code.split("-") var plan_type = plan[0] var plan_period = plan[1] user.plan_type = plan_type - user.plan_level = subscription.plan_level[plan_type] - - // subscription.uuid - // subscription.subscription_add_ons[] - // subscription.subscription_add_ons[0].quantity - user.save(function(){ - }) + user.plan_period = plan_period + user.plan_level = subscribe.plan_level[plan_type] + var subscriber = new Subscription () + subscriber.user_id = user._id + subscriber.uuid = data.subscription.uuid + subscriber.add_ons = subscription.add_ons.map(function(add_on){ + return { + name: add_on.plan, + quantity: add_on.quantity, + } + }) + subscriber.save(function(err, data){ + if (err) return; + user.save(function(err){ + // saved! + }) + }) }) }, - updated_subscription_notification: function(data){ + +/* + updated_subscription_notification: function(data, user){ }, - canceled_subscription_notification: function(data){ + canceled_subscription_notification: function(data, user){ }, - expired_subscription_notification: function(data){ + expired_subscription_notification: function(data, user){ }, - renewed_subscription_notification: function(data){ + renewed_subscription_notification: function(data, user){ }, - +*/ // payments - successful_payment_notification: function(data){ + successful_payment_notification: function(data, user){ var account = data.account - User.findOne({ _id: account.account_code }, function(err, user){ - if (err) return; - user.last_charged = new Date(data.transaction.date) - user.save(function(){ - }) - }) + user.last_charged = new Date(data.transaction.date) + user.save(function(){ + }) }, - failed_payment_notification: function(data){ +/* + failed_payment_notification: function(data, user){ }, - successful_refund_notification: function(data){ + successful_refund_notification: function(data, user){ }, - void_payment_notification: function(data){ + void_payment_notification: function(data, user){ }, - +*/ }, - // need a route for the webhook, + execute: function(action, data){ + User.findOne({ _id: data.account.account_code }, function(err, user){ + if (err) { return } + subscribe.callbacks[action](data, user) + }) + }, + // then calls to get appropriate info from the recurly api - webhook: function(req, res){ + handle: function(req, res){ res.status(200).end() parser.parseString(data, function (err, result) { console.log(inspect(result, { colors: true, depth: Infinity })); - for (var i in data) { - if (subscription.callbacks[i]) { - subscription.callbacks[i](data[i]) + for (var action in result) { + if (subscribe.callbacks[action]) { + subscribe.execute(action, result[action]); } } }); }, + + route: function(app){ + app.post('/subscribe/webhook', subscribe.handle); + }, } -- cgit v1.2.3-70-g09d2 From 5efb0ed941ed80136e63014c4f615574b2b613d7 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 23 Jan 2015 17:58:41 -0500 Subject: edit subscription partial stub --- server/lib/api/index.js | 1 + server/lib/api/subscription.js | 22 ++++++++++ server/lib/schemas/Subscription.js | 3 +- server/lib/webhook/config.js | 6 --- server/lib/webhook/recurly-config.js | 6 +++ server/lib/webhook/webhook.js | 8 ++-- views/about/brochure.ejs | 12 +++--- views/partials/edit-subscription.ejs | 33 +++++++++++++++ views/profile.ejs | 82 ++++++++++++++++++++---------------- 9 files changed, 119 insertions(+), 54 deletions(-) create mode 100644 server/lib/api/subscription.js delete mode 100644 server/lib/webhook/config.js create mode 100644 server/lib/webhook/recurly-config.js create mode 100644 views/partials/edit-subscription.ejs diff --git a/server/lib/api/index.js b/server/lib/api/index.js index 11e13fc..9478d9b 100644 --- a/server/lib/api/index.js +++ b/server/lib/api/index.js @@ -8,6 +8,7 @@ var api = { projects: require('./projects'), rooms: require('./rooms'), collaborator: require('./collaborator'), + subscription: require('./subscription'), } module.exports = api diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js new file mode 100644 index 0000000..6fe8c61 --- /dev/null +++ b/server/lib/api/subscription.js @@ -0,0 +1,22 @@ +/* jshint node: true */ + +var _ = require('lodash'), + util = require('../util'), + upload = require('../upload'), + config = require('../../../config.json'), + User = require('../schemas/User'), + Project = require('../schemas/Project'), + Layout = require('../schemas/Layout'), + Subscription = require('../schemas/Subscription'); + +var subscription = module.exports = { + +/* + index: function(req, res){ + Project.find({ user_id: req.user._id }, function(err, docs){ + res.json(docs) + }) + }, +*/ + +}; \ No newline at end of file diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js index 99e4ebf..2f49ea1 100644 --- a/server/lib/schemas/Subscription.js +++ b/server/lib/schemas/Subscription.js @@ -9,9 +9,8 @@ var mongoose = require('mongoose'), var SubscriptionSchema = new mongoose.Schema({ user_id: { type: mongoose.Schema.ObjectId, index: true }, - plan_level: { type: Number, default: 0 }, plan_type: { type: String, default: "free" }, - last_charged: { type: Date, default: null }, + plan_period: { type: String, default: "monthly" }, subscription_uuid: { type: String }, subscription_add_ons: [{ diff --git a/server/lib/webhook/config.js b/server/lib/webhook/config.js deleted file mode 100644 index 3d7e1c5..0000000 --- a/server/lib/webhook/config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - API_KEY: process.env['VVALLS_RECURLY_SECRET'], - SUBDOMAIN: 'vvalls', - ENVIRONMENT: 'sandbox', - DEBUG: true, -}; diff --git a/server/lib/webhook/recurly-config.js b/server/lib/webhook/recurly-config.js new file mode 100644 index 0000000..3d7e1c5 --- /dev/null +++ b/server/lib/webhook/recurly-config.js @@ -0,0 +1,6 @@ +module.exports = { + API_KEY: process.env['VVALLS_RECURLY_SECRET'], + SUBDOMAIN: 'vvalls', + ENVIRONMENT: 'sandbox', + DEBUG: true, +}; diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 2e5e627..e9a7925 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -20,7 +20,7 @@ var User = require('../schemas/User'), moment = require('moment'), xml2js = require('xml2js'), Recurly = require('node-recurly'), - recurly = new Recurly(require('./config')); + recurly = new Recurly(require('./recurly-config')); var parser = new xml2js.Parser(); @@ -64,12 +64,14 @@ var subscribe = module.exports = { var plan_period = plan[1] user.plan_type = plan_type - user.plan_period = plan_period user.plan_level = subscribe.plan_level[plan_type] var subscriber = new Subscription () - subscriber.user_id = user._id subscriber.uuid = data.subscription.uuid + subscriber.user_id = user._id + subscriber.plan_type = plan_type + subscriber.plan_period = plan_period + subscriber.plan_level = subscribe.plan_level[plan_type] subscriber.add_ons = subscription.add_ons.map(function(add_on){ return { name: add_on.plan, diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs index cffa51f..1c808f8 100644 --- a/views/about/brochure.ejs +++ b/views/about/brochure.ejs @@ -35,9 +35,9 @@
  • [[ if (! logged_in) { ]] - [[ } else if (! profile.plan_level || profile.plan_level < plan.level) { ]] - - [[ } else if (profile.plan_level == plan.level) { ]] + [[ } else if (! user.plan_level || user.plan_level < plan.level) { ]] + + [[ } else if (user.plan_level == plan.level) { ]] Current Level [[ } else { ]] [[ } ]] @@ -57,9 +57,9 @@
  • [[ if (! logged_in) { ]] - [[ } else if (! profile.plan_level || profile.plan_level < plan.level) { ]] - - [[ } else if (profile.plan_level == plan.level) { ]] + [[ } else if (! user.plan_level || user.plan_level < plan.level) { ]] + + [[ } else if (user.plan_level == plan.level) { ]] Current Level [[ } ]] diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs new file mode 100644 index 0000000..0aa5281 --- /dev/null +++ b/views/partials/edit-subscription.ejs @@ -0,0 +1,33 @@ +
    + X +
    +
    + +
      +
    • +

      Edit Subscription

      +
    • +
    • + [[ if (! user.plan_level) { ]] + You are currently using the free plan. For access to all of Vvalls features, + consider upgrading to a paid plan. +

      + View the Plans + [[ } else { ]] + Your current plan level is XXX + You have been a member since XXX + $cost/month OR $cost/year + + You are using N basic layouts + Buy more + + You are using N pro layouts + Buy more / Upgrade your account + + Cancel your subscription + [[ } ]] +

    • +
    +
    +
    +
    diff --git a/views/profile.ejs b/views/profile.ejs index 88af6b0..e149847 100644 --- a/views/profile.ejs +++ b/views/profile.ejs @@ -9,48 +9,53 @@ [[- include partials/header ]]
    - [[ if (profile.photo && profile.photo.length) { ]] -
    -
    - [[ } else { ]] -
    - - - [[ if (isOwnProfile) { ]] -
    click to add profile pic
    - - [[ } ]] -
    -
    + [[ if (profile.photo && profile.photo.length) { ]] +
    +
    + [[ } else { ]] +
    + + + [[ if (isOwnProfile) { ]] +
    click to add profile pic
    + [[ } ]] -
    -
    -

    [[- profile.displayName ]]

    - [[ if (profile.location) { ]] - - [[- profile.location ]] - - [[ } ]] - [[ if (profile.website && profile.website.length) { ]] - - [[- profile.website ]] - - [[ } ]] - [[ if (profile.twitterName && profile.twitterName.length) { ]] - - @[[- profile.twitterName ]] - - [[ } ]] -
    -
    - +
    +
    + [[ } ]] +
    +
    +

    [[- profile.displayName ]]

    + [[ if (profile.location) { ]] + + [[- profile.location ]] + + [[ } ]] + [[ if (profile.website && profile.website.length) { ]] + + [[- profile.website ]] + + [[ } ]] + [[ if (profile.twitterName && profile.twitterName.length) { ]] + + @[[- profile.twitterName ]] + + [[ } ]] + [[ if (profile.plan_level == 1) { ]] + PREMIUM + [[ } else if (profile.plan_level == 2) { ]] + PRO + [[ } ]] +
    +
    [[ if (projects.length) { ]] +

    [[- profile.username ]] has [[- projectCount ]] project[[- projectCount != 1 ? "s" : "" ]]

    - [[ include projects/list-projects ]] + [[ } else { ]] - +

    Welcome to VVALLS

    @@ -69,8 +74,11 @@

    This person has no projects.

    [[ } ]]
    + [[ } ]] -
    + +
  • + [[ include partials/edit-subscription ]] [[ include partials/edit-profile ]] [[ include projects/layouts-modal ]] [[ include projects/edit-project ]] -- cgit v1.2.3-70-g09d2 From 29e0f23c5ade5072851aaa74314166b0c7281272 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 23 Jan 2015 19:28:53 -0500 Subject: stub subscription modal js --- .../javascripts/ui/site/EditSubscriptionModal.js | 21 ++++++++++ views/partials/edit-subscription.ejs | 45 +++++++++++++++------- 2 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 public/assets/javascripts/ui/site/EditSubscriptionModal.js diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js new file mode 100644 index 0000000..1b3b859 --- /dev/null +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -0,0 +1,21 @@ + +var EditSubscriptionModal = ModalFormView.extend({ + el: ".mediaDrawer.editSubscription", + action: "/api/subscription", + method: "put", + + fixedClose: true, + + events: { + "click [data-role='changePasswordToggle']": 'togglePasswordFields' + }, + + load: function(){ + this.reset() + $.get("/api/subscription", function(data){ + + this.show() + }.bind(this)) + }, + +}) diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 0aa5281..1f8db62 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -7,24 +7,43 @@
  • Edit Subscription

  • -
  • - [[ if (! user.plan_level) { ]] +
    You are currently using the free plan. For access to all of Vvalls features, consider upgrading to a paid plan.

    View the Plans - [[ } else { ]] - Your current plan level is XXX - You have been a member since XXX - $cost/month OR $cost/year +

    +
    + Your current plan level is + + + + + + + + + + + + + + + + + + + + + + + + +
    Basic plan@ $/
    Additional basic layouts@ $/Buy more
    Additional PRO layouts$/Buy more
    Total$/
    + + - You are using N basic layouts - Buy more - - You are using N pro layouts - Buy more / Upgrade your account - - Cancel your subscription + [[ } ]]
  • -- cgit v1.2.3-70-g09d2 From 0a8849bc0abea10fa0d53207159c09f433c25555 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Mon, 26 Jan 2015 20:37:06 -0500 Subject: methodz --- server/lib/api/subscription.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 6fe8c61..bd19127 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -18,5 +18,17 @@ var subscription = module.exports = { }) }, */ + show: function(req,res){ + // fetch from recurly + }, + + update: function(req,res){ + // update plan_type on recurly + // update add_ons on recurly + }, + + destroy: function(req,res){ + // destroy on recurly + }, }; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 3059c3203d2cec4e2e745be8c21c6d3fbddb0c14 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Wed, 28 Jan 2015 01:39:28 -0500 Subject: rigging EditSubscriptionModal --- Gruntfile.js | 1 + config.json.example | 1 - public/assets/javascripts/ui/_router.js | 10 ++++++++++ server/index.js | 1 + server/lib/api/subscription.js | 19 ++++++++++++++++--- server/lib/webhook/webhook.js | 14 ++++++++++++++ views/about/brochure.ejs | 3 +++ views/partials/edit-subscription.ejs | 1 - views/partials/scripts.ejs | 1 + 9 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f62cc82..ed236c5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -99,6 +99,7 @@ module.exports = function(grunt) { "public/assets/javascripts/ui/site/LayoutsModal.js", "public/assets/javascripts/ui/site/EditProjectModal.js", "public/assets/javascripts/ui/site/EditProfileModal.js", + "public/assets/javascripts/ui/site/EditSubscriptionModal.js", "public/assets/javascripts/ui/site/DocumentModal.js", "public/assets/javascripts/ui/site/HomeView.js", diff --git a/config.json.example b/config.json.example index b8c310c..dc79edd 100644 --- a/config.json.example +++ b/config.json.example @@ -5,6 +5,5 @@ "socketPort": 1337, "webhookPort": 5000, "databaseHost": "lvh.me", - "pageSize": 10, "env": { "development": 1 } } diff --git a/public/assets/javascripts/ui/_router.js b/public/assets/javascripts/ui/_router.js index 3532428..9e7ce75 100644 --- a/public/assets/javascripts/ui/_router.js +++ b/public/assets/javascripts/ui/_router.js @@ -9,6 +9,7 @@ var SiteRouter = Router.extend({ "click [data-role='new-project-modal']": 'newProject', "click [data-role='edit-project-modal']": 'editProject', "click [data-role='edit-profile-modal']": 'editProfile', + "click [data-role='edit-subscription-modal']": 'editSubscription', "click [data-role='new-document-modal']": 'newDocument', "click [data-role='edit-document-modal']": 'editDocument', "click [data-role='destroy-document-modal']": 'destroyDocument', @@ -29,6 +30,7 @@ var SiteRouter = Router.extend({ "/profile": 'profile', "/profile/edit": 'editProfile', + "/profile/billing": 'editSubscription', "/profile/:name": 'profile', "/about/:name/edit": 'editDocument', "/about/new": 'newDocument', @@ -56,6 +58,7 @@ var SiteRouter = Router.extend({ "/profile": 'profile', "/profile/edit": 'editProfile', + "/profile/billing": 'editSubscription', "/profile/:name": 'profile', "/project/:name": 'projectViewer', @@ -69,6 +72,7 @@ var SiteRouter = Router.extend({ this.newProjectModal = new NewProjectModal() this.editProjectModal = new EditProjectModal() this.editProfileModal = new EditProfileModal() + this.editSubscriptionModal = new EditSubscriptionModal() this.passwordForgotModal = new PasswordForgot() this.documentModal = new DocumentModal() this.profileView = new ProfileView() @@ -195,6 +199,12 @@ var SiteRouter = Router.extend({ this.editProfileModal.load() }, + editSubscription: function(e){ + e && e.preventDefault() + window.history.pushState(null, document.title, "/profile/billing") + + this.editSubscriptionModal.load() + }, newDocument: function(e){ diff --git a/server/index.js b/server/index.js index 02fea3c..475054d 100644 --- a/server/index.js +++ b/server/index.js @@ -102,6 +102,7 @@ site.route = function () { app.get('/profile', views.profile) app.get('/profile/edit', views.profile) + app.get('/profile/billing', views.profile) app.get('/profile/:username', views.profile) app.get('/about', views.docs); diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index bd19127..83644cf 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -18,16 +18,29 @@ var subscription = module.exports = { }) }, */ - show: function(req,res){ + middleware: { + fetchAccount: function(req, res, next){ + recurly.subscriptions.listByAccount(req.user._id, function(data){ + }) + }, + }, + + // synchronise an account with recurly.. + // useful when testing locally (if webhooks do not fire) + sync: function(req, res){ + // fetch req.user._id + }, + + show: function(req, res){ // fetch from recurly }, - update: function(req,res){ + update: function(req, res){ // update plan_type on recurly // update add_ons on recurly }, - destroy: function(req,res){ + destroy: function(req, res){ // destroy on recurly }, diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index e9a7925..4f23d0b 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -134,7 +134,21 @@ var subscribe = module.exports = { }); }, + list: function(req, res){ + recurly.subscriptions.listByAccount(req.params.id, function(data){ + if (data.data != 404) { + res.json(data) + return + } + else { + res.json(data) + return + } + }) + }, + route: function(app){ app.post('/subscribe/webhook', subscribe.handle); + app.get('/subscribe/list/:id', subscribe.list); }, } diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs index 1c808f8..49b03db 100644 --- a/views/about/brochure.ejs +++ b/views/about/brochure.ejs @@ -123,6 +123,9 @@ text-align: center; margin-bottom: 10px; } +.about_plan ul { + margin-bottom: 60px; +} .planbox li { list-style-type: none; margin-bottom: 5px; diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 1f8db62..adc3f71 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -44,7 +44,6 @@ - [[ } ]] diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index fc94992..04bd945 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -95,6 +95,7 @@ + -- cgit v1.2.3-70-g09d2 From 79fee7f24d43873fc35295eab1d2a089d373e133 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 28 Jan 2015 11:49:08 -0500 Subject: merge --- server/lib/schemas/Subscription.js | 6 +++++ server/lib/schemas/User.js | 1 + server/lib/views/subscription.js | 50 -------------------------------------- 3 files changed, 7 insertions(+), 50 deletions(-) delete mode 100644 server/lib/views/subscription.js diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js index 8315009..8ec557d 100644 --- a/server/lib/schemas/Subscription.js +++ b/server/lib/schemas/Subscription.js @@ -17,6 +17,12 @@ var SubscriptionSchema = new mongoose.Schema({ monthly: { type: Boolean }, }], + history: [{ + action: { type: String }, + plan_id: { type: String }, + created_at: { type: Date, default: Date.now }, + }], + created_at: { type: Date, default: Date.now }, updated_at: { type: Date, default: Date.now }, }) diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js index 180a140..19b5ede 100644 --- a/server/lib/schemas/User.js +++ b/server/lib/schemas/User.js @@ -62,6 +62,7 @@ var UserSchema = new mongoose.Schema({ twitterName: { type: String, default: "" }, facebookUrl: { type: String, default: "" }, isStaff: { type: Boolean, default: false }, + subscription_id: { type: mongoose.Schema.ObjectId }, created_at: { type: Date }, updated_at: { type: Date }, last_seen: { type: Date }, diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js deleted file mode 100644 index ba54bb4..0000000 --- a/server/lib/views/subscription.js +++ /dev/null @@ -1,50 +0,0 @@ -/* jshint node: true */ - -var User = require('../schemas/User'), - Subscription = require('../schemas/Subscription'), - config = require('../../../config'), - middleware = require('../middleware'), - util = require('../util'), - _ = require('lodash'), - moment = require('moment'); - -var subscription = module.exports = { - - fields: { - user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip", - }, - - defaults: { - user: { - _id: "", username: "", displayName: "", - created_at: "", updated_at: "", created_ip: "", last_ip: "", - }, - }, - - middleware: { - }, - - helpers: { - 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 - }, - }, - - route: function(app){ - app.get('/staff', - middleware.ensureAuthenticated, - middleware.ensureIsStaff, - - staff.middleware.ensureRecentUsers, - staff.middleware.ensureUsersCount, - staff.middleware.ensureProjectsCount, - staff.middleware.ensureMediaCount, - - staff.index - ); - }, - -} -- cgit v1.2.3-70-g09d2 From 5adac681bdb43b8b709795fa501689fb9ae8a4e1 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 28 Jan 2015 19:18:27 -0500 Subject: many methods --- package.json | 1 + server/lib/api/subscription.js | 124 ++++++++++++++++++++++++++++++++---- server/lib/middleware.js | 2 +- server/lib/schemas/Subscription.js | 6 +- server/lib/views/staff.js | 2 +- server/lib/webhook/webhook.js | 2 +- views/about/brochure.ejs | 15 +++-- views/partials/header.ejs | 1 + views/staff/_users.ejs | 1 + views/staff/plans/_form.ejs | 5 ++ views/staff/subscriptions/index.ejs | 2 +- views/staff/users/show.ejs | 1 + 12 files changed, 135 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 8afb96e..e89fcd9 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "express-subdomains": "0.0.5", "html-entities": "~1.0.10", "intro.js": "^0.9.0", + "js2xml": "^1.0.0", "knox": "~0.8.10", "lodash": "~2.4.1", "marked": "~0.3.2", diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 83644cf..40aa715 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -9,7 +9,22 @@ var _ = require('lodash'), Layout = require('../schemas/Layout'), Subscription = require('../schemas/Subscription'); +var plan_levels = { + free: 0, + basic: 1, + pro: 2, +} + var subscription = module.exports = { + middleware: { + ensureSubscription: function(req, res, next){ + Subscription.findOne({ user_id: req.user._id }, function(err, data){ + if (err) { return next() } + req.subscription = data + next() + }) + }, + } /* index: function(req, res){ @@ -18,30 +33,115 @@ var subscription = module.exports = { }) }, */ - middleware: { - fetchAccount: function(req, res, next){ - recurly.subscriptions.listByAccount(req.user._id, function(data){ - }) - }, - }, // synchronise an account with recurly.. - // useful when testing locally (if webhooks do not fire) + // useful when testing locally (where webhooks cannot be received) sync: function(req, res){ - // fetch req.user._id + var subscriber = req.subscription || new Subscription () + var user = req.user + recurly.subscriptions.listByAccount(req.user._id, function(data){ + if (data.description !== 200) { + res.json({ error: "no account" }) + return + } + if (! data.subscriptions.subscription) { + res.json({ error: "account error" }) + return + } + + var plan = data.subscriptions.subscription.plan.plan_code.split("-") + var plan_type = plan[0] + var plan_period = plan[1] + + user.plan_type = plan_type + user.plan_level = plan_levels[plan_type] + + subscriber.uuid = data.subscriptions.subscription.uuid + subscriber.user_id = user._id + subscriber.plan_type = plan_type + subscriber.plan_period = plan_period + subscriber.plan_level = plan_levels[plan_type] + + var add_ons = data.subscriptions.subscription.subscription_add_ons.subscription_add_on + if (add_ons) { + if (add_ons.add_on_code) { + add_ons = [ add_ons ] + } + // TODO: handle multiple add-ons.. presumably this will work + add_ons.forEach(function(add_on){ + var add_on_type = add_on.add_on_code.split("-")[1] + if (add_on_type == "basic") { + subscriber.basic_layouts = parseInt( add_on.quantity._ ) + } + if (add_on_type == "pro") { + subscriber.pro_layouts = parseInt( add_on.quantity._ ) + } + }) + } + else { + subscriber.basic_layouts = 0 + subscriber.pro_layouts = 0 + } + + subscriber.save(function(){ + user.save(function(){ + res.render(subscriber) + }) + }) + }) }, show: function(req, res){ - // fetch from recurly + res.json(req.subscription || { error: "no subscription" }) }, update: function(req, res){ - // update plan_type on recurly - // update add_ons on recurly + if (! req.subscription ) { + return res.json({ error: "no subscription" }) + } + if (! (req.body.plan_type in plan_levels)) { + return res.json({ error: "bad input" }) + } + var subscriber = req.subscription + + // change.. + // data.plan_code + // data.subscription_add_ons = [] + // add_on.add_on_code + // add_on.quantity + var basic_layouts = max(0, parseInt(req.body.basic_layouts)) + var pro_layouts = max(0, parseInt(req.body.pro_layouts)) + + var data = {} + data.plan_code = req.body.plan_type + "_monthly" + data.subscription_add_ons = [] + + if (plan_levels[req.body.plan_type]) { + data.subscription_add_ons.push({ add_on_code: "extra-basic-layout", quantity: basic_layouts }) + } + + if (req.body.plan_type == "pro") { + data.subscription_add_ons.push({ add_on_code: "extra-pro-layout", quantity: pro_layouts }) + } + + + recurly.subscriptions.update(subscriber.uuid, data, function(){ + return res.json(subscriber) + }) }, destroy: function(req, res){ - // destroy on recurly + if (! req.subscription ) { + return res.json({ error: "no subscription" }) + } + var subscriber = req.subscription + + recurly.subscriptions.cancel(subscriber.uuid, function(){ + subscriber.remove(function(){ + req.user.plan_code = 0 + req.user.plan_type = "free" + }) + }) }, }; \ No newline at end of file diff --git a/server/lib/middleware.js b/server/lib/middleware.js index 797d677..7dfe821 100644 --- a/server/lib/middleware.js +++ b/server/lib/middleware.js @@ -59,7 +59,7 @@ var middleware = { res.locals.opt = {} next() }, - + ensureProject: function (req, res, next) { if (req.params.slug) { Project.findOne({ slug: req.params.slug }, function(err, project){ diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js index b766555..24c5096 100644 --- a/server/lib/schemas/Subscription.js +++ b/server/lib/schemas/Subscription.js @@ -13,10 +13,8 @@ var SubscriptionSchema = new mongoose.Schema({ plan_period: { type: String, default: "monthly" }, subscription_uuid: { type: String }, - subscription_add_ons: [{ - name: { type: String }, - quantity: { type: Number }, - }], + basic_layouts: { type: Number, default: 0 }, + pro_layouts: { type: Number, default: 0 }, history: [{ action: { type: String }, diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index 74dd7cd..67193fe 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -154,7 +154,7 @@ var staff = module.exports = { }, ensurePlans: function(req, res, next){ - Plan.find(function (err, plans) { + 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() diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 4f23d0b..a871a3a 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -149,6 +149,6 @@ var subscribe = module.exports = { route: function(app){ app.post('/subscribe/webhook', subscribe.handle); - app.get('/subscribe/list/:id', subscribe.list); + app.get('/subscribe/list/:id', subscribe.middleware.ensureSubscription, subscribe.list); }, } diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs index 49b03db..1dad763 100644 --- a/views/about/brochure.ejs +++ b/views/about/brochure.ejs @@ -34,12 +34,11 @@
  • VValls logo appears when embedding an exhibition on a web page
  • [[ if (! logged_in) { ]] - - [[ } else if (! user.plan_level || user.plan_level < plan.level) { ]] - + + [[ } else if (! user.plan_level) { ]] + [[ } else if (user.plan_level == plan.level) { ]] Current Level - [[ } else { ]] [[ } ]]
  • @@ -56,11 +55,13 @@
  • No VValls logo on embed
  • [[ if (! logged_in) { ]] - - [[ } else if (! user.plan_level || user.plan_level < plan.level) { ]] - + + [[ } else if (! user.plan_level) { ]] + [[ } else if (user.plan_level == plan.level) { ]] Current Level + [[ } else if (user.plan_level < plan.level) { ]] + [[ } ]]
  • diff --git a/views/partials/header.ejs b/views/partials/header.ejs index ce5bab9..2acf2bc 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -64,6 +64,7 @@ [[ if (profile && String(user._id) == String(profile._id)) { ]] Settings + Subscription [[ } else if (! profile) { ]] Profile [[ } ]] diff --git a/views/staff/_users.ejs b/views/staff/_users.ejs index d46058f..053e289 100644 --- a/views/staff/_users.ejs +++ b/views/staff/_users.ejs @@ -12,6 +12,7 @@ [view profile] + [recurly] [[- user.last_seen ]] diff --git a/views/staff/plans/_form.ejs b/views/staff/plans/_form.ejs index ae5ca5a..b55c5cd 100644 --- a/views/staff/plans/_form.ejs +++ b/views/staff/plans/_form.ejs @@ -16,6 +16,11 @@
    +
  • + +
    +
  • +
  • diff --git a/views/staff/subscriptions/index.ejs b/views/staff/subscriptions/index.ejs index d1c0588..3efffb5 100644 --- a/views/staff/subscriptions/index.ejs +++ b/views/staff/subscriptions/index.ejs @@ -1,6 +1,6 @@ [[ include ../_header ]] -

    Users

    +

    Subscriptions

    [[ include ../_nav ]] diff --git a/views/staff/users/show.ejs b/views/staff/users/show.ejs index 4ce1d9a..d6a21d5 100644 --- a/views/staff/users/show.ejs +++ b/views/staff/users/show.ejs @@ -19,6 +19,7 @@ [view profile] [view media] + [view on recurly] -- cgit v1.2.3-70-g09d2 From ccfb71101d8f87e4c1dce9eb477d164707ee02ff Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 28 Jan 2015 20:22:13 -0500 Subject: comma --- server/lib/api/subscription.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 40aa715..140bd12 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -24,7 +24,7 @@ var subscription = module.exports = { next() }) }, - } + }, /* index: function(req, res){ @@ -124,7 +124,6 @@ var subscription = module.exports = { data.subscription_add_ons.push({ add_on_code: "extra-pro-layout", quantity: pro_layouts }) } - recurly.subscriptions.update(subscriber.uuid, data, function(){ return res.json(subscriber) }) -- cgit v1.2.3-70-g09d2 From fdcc335d7e95139b71083928602713b82f29aba1 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 28 Jan 2015 20:22:53 -0500 Subject: lol --- server/lib/webhook/webhook.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index a871a3a..4f23d0b 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -149,6 +149,6 @@ var subscribe = module.exports = { route: function(app){ app.post('/subscribe/webhook', subscribe.handle); - app.get('/subscribe/list/:id', subscribe.middleware.ensureSubscription, subscribe.list); + app.get('/subscribe/list/:id', subscribe.list); }, } -- cgit v1.2.3-70-g09d2 From f55d634b7b24b6af0fb7dd017544a79672bb1bc7 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Wed, 28 Jan 2015 22:23:03 -0500 Subject: routez --- server/index.js | 5 +++++ server/lib/api/subscription.js | 10 +--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/server/index.js b/server/index.js index 475054d..53da448 100644 --- a/server/index.js +++ b/server/index.js @@ -151,6 +151,11 @@ site.route = function () { app.post('/api/media/upload', middleware.ensureAuthenticated, api.media.upload) app.delete('/api/media/destroy', middleware.ensureAuthenticated, api.media.destroy) + app.get('/api/subscription/sync', middleware.ensureAuthenticated, api.subscription.sync) + app.get('/api/subscription', middleware.ensureAuthenticated, api.subscription.show) + app.put('/api/subscription', middleware.ensureAuthenticated, api.subscription.update) + app.delete('/api/subscription', middleware.ensureAuthenticated, api.subscription.destroy) + app.get('/test/*', middleware.ensureAuthenticated, middleware.ensureIsStaff, views.modal) views.staff.route(app) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 140bd12..e7cd8f4 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -26,14 +26,6 @@ var subscription = module.exports = { }, }, -/* - index: function(req, res){ - Project.find({ user_id: req.user._id }, function(err, docs){ - res.json(docs) - }) - }, -*/ - // synchronise an account with recurly.. // useful when testing locally (where webhooks cannot be received) sync: function(req, res){ @@ -128,7 +120,7 @@ var subscription = module.exports = { return res.json(subscriber) }) }, - + destroy: function(req, res){ if (! req.subscription ) { return res.json({ error: "no subscription" }) -- cgit v1.2.3-70-g09d2 From 1d88890a988b255c915d0472297b18a2635f6433 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 30 Jan 2015 01:26:46 -0500 Subject: ui boilerplate --- .../javascripts/ui/site/EditSubscriptionModal.js | 40 ++++++++++++++++++++-- views/partials/edit-subscription.ejs | 28 +++++++-------- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 1b3b859..342e8be 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -7,15 +7,51 @@ var EditSubscriptionModal = ModalFormView.extend({ fixedClose: true, events: { - "click [data-role='changePasswordToggle']": 'togglePasswordFields' + "click [data-role='']": 'togglePasswordFields' + }, + + initialize: function(){ + this.$freePlan = this.$("#free_plan") + this.$paidPlan = this.$("#paid_plan") + this.$proLayoutRow = this.$("#proLayoutRow") + + this.$billingInterval = this.$("[data-role=billingInterval]") + + this.$planType = this.$("[data-role=planType]") + this.$planName = this.$("[data-role=planName]") + this.$planCost = this.$("[data-role=planCost]") + + this.$basicLayoutCost = this.$("[data-role=basicLayoutCost]") + this.$basicLayoutQuantity = this.$("[data-role=basicLayoutQuantity]") + + this.$proLayoutCost = this.$("[data-role=proLayoutCost]") + this.$proLayoutQuantity = this.$("[data-role=proLayoutQuantity]") + + this.$upgradeSubscription = this.$("[data-role=upgradeSubscription]") + this.$cancelSubscription = this.$("[data-role=cancelSubscription]") }, load: function(){ this.reset() $.get("/api/subscription", function(data){ - + if (data.error) { + this.$freePlan.show() + this.$paidPlan.hide() + this.show() + return + } + this.$freePlan.show() + this.$paidPlan.hide() + + var plan = data.plans[ data.subscription.plan_name ] + this.show() + + // layouts }.bind(this)) }, + + updateTotals: function(){ + }, }) diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index adc3f71..f49d791 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -13,37 +13,37 @@

    View the Plans

  • -
    - Your current plan level is + -
    -

    [[- plans.free.name ]]

    -
      -
    • [[- plans.free.stock_project_limit ]] exhibition with pre-designed template floor plan -
    -
    - -
    -

    [[- plans.basic.name ]]

    -
      -
    • $[[- plans.basic.monthly_price ]]/mo or $[[- plans.basic.yearly_price ]]/year -
    • Comes with [[- plans.basic.basic_layout_limit ]] basic floor plan and [[- plans.basic.basic_project_limit ]] exhibitions -
    • Each new basic floor plan costs $[[- plans.basic.basic_layout_monthly_price ]]/mo - or $[[- plans.basic.basic_layout_yearly_price ]]/year, minimum 3 months -
    • Each new floor plan can have up to [[- plans.basic.basic_project_limit ]] exhibitions -
    • VValls logo appears when embedding an exhibition on a web page -
    • - [[ if (! logged_in) { ]] - - [[ } else if (! user.plan_level) { ]] - - [[ } else if (user.plan_level == plan.level) { ]] - Current Level - [[ } ]] -
    -
    - -
    -

    [[- plans.pro.name ]]

    -
      -
    • $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year -
    • Comes with [[- plans.pro.pro_layout_limit ]] pro floor plan and [[- plans.pro.pro_project_limit ]] exhibitions -
    • Each new pro floor plan costs $[[- plans.pro.pro_layout_monthly_price ]]/mo - or $[[- plans.pro.pro_layout_yearly_price ]]/year, minimum 3 months -
    • Each new pro floor plan can have up to [[- plans.pro.pro_project_limit ]] exhibitions -
    • Includes planning for 3D objects in the room -
    • No VValls logo on embed -
    • - [[ if (! logged_in) { ]] - - [[ } else if (! user.plan_level) { ]] - - [[ } else if (user.plan_level == plan.level) { ]] - Current Level - [[ } else if (user.plan_level < plan.level) { ]] - - [[ } ]] -
    -
    - -
    -
      -
    • Buying any extra floor plan unlocks collaboration. Invite an artist or curator to work on the exhibition with you. -
    • Basic Floor plan: Rectangle-based design of any dimension. -
    • Pro Floor plan: Trace an arbitrary floor plan from image. -
    -
    - -
    -

    Want Something Custom?

    -
  • We offer customized white-label options for business and educational uses. -
  • Contact us for more information. -
  • + [[ include _plans ]]
    - [[ include ../partials/confirm-modal ]] [[ include ../projects/layouts-modal ]] [[ include ../partials/sign-in ]] @@ -91,51 +28,3 @@ [[ include ../partials/scripts ]] - \ No newline at end of file diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index bb7cc27..2f6e4c1 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -7,15 +7,14 @@
  • Edit Subscription

  • -
    - You are currently using the free plan. For access to all of Vvalls features, +
  • + You are currently using the free version of Vvalls. For access to all of Vvalls features, consider upgrading to a paid plan. -

    - View the Plans -

  • - + +
    +

    [[- plans.basic.name ]]

    +
      +
    • $[[- plans.basic.monthly_price ]]/mo or $[[- plans.basic.yearly_price ]]/year +
    • Comes with [[- plans.basic.basic_layout_limit ]] basic floor plan and [[- plans.basic.basic_project_limit ]] exhibitions +
    • Each new basic floor plan costs $[[- plans.basic.basic_layout_monthly_price ]]/mo + or $[[- plans.basic.basic_layout_yearly_price ]]/year, minimum 3 months +
    • Each new floor plan can have up to [[- plans.basic.basic_project_limit ]] exhibitions +
    • VValls logo appears when embedding an exhibition on a web page +
    • + [[ if (! logged_in) { ]] + + [[ } else if (! user.plan_level) { ]] + + [[ } else if (user.plan_level == plan.level) { ]] + Current Level + [[ } ]] +
    +
    + +
    +

    [[- plans.pro.name ]]

    +
      +
    • $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year +
    • Comes with [[- plans.pro.pro_layout_limit ]] pro floor plan and [[- plans.pro.pro_project_limit ]] exhibitions +
    • Each new pro floor plan costs $[[- plans.pro.pro_layout_monthly_price ]]/mo + or $[[- plans.pro.pro_layout_yearly_price ]]/year, minimum 3 months +
    • Each new pro floor plan can have up to [[- plans.pro.pro_project_limit ]] exhibitions +
    • Includes planning for 3D objects in the room +
    • No VValls logo on embed +
    • + [[ if (! logged_in) { ]] + + [[ } else if (! user.plan_level) { ]] + + [[ } else if (user.plan_level == plan.level) { ]] + Current Level + [[ } else if (user.plan_level < plan.level) { ]] + + [[ } ]] +
    +
    + +
    +
      +
    • Buying any extra floor plan unlocks collaboration.
      Invite an artist or curator to work on the exhibition with you. +
    • Basic Floor plan: Rectangle-based design of any dimension. +
    • Pro Floor plan: Trace an arbitrary floor plan from image. +
    +
    + +
    +

    Want Something Custom?

    +
  • We offer customized white-label options for business and educational uses. +
  • Contact us for more information. +
  • diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 2f6e4c1..cc296f6 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -1,19 +1,19 @@
    X
    -
    +
    • Edit Subscription

    • -
    • +
    • You are currently using the free version of Vvalls. For access to all of Vvalls features, consider upgrading to a paid plan.

      - +
    • -
    • Your current plan level is diff --git a/views/staff/_users.ejs b/views/staff/_users.ejs index 053e289..46811b6 100644 --- a/views/staff/_users.ejs +++ b/views/staff/_users.ejs @@ -12,7 +12,8 @@ - - @@ -44,7 +42,8 @@
      [[- user.last_seen ]] -- cgit v1.2.3-70-g09d2 From a3f3f4d370da5d38c039f6cdb36223b396eeeb77 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 30 Jan 2015 17:59:29 -0500 Subject: sync gear for test purposes --- .../javascripts/ui/site/EditSubscriptionModal.js | 20 +++++++++++++++----- public/assets/stylesheets/app.css | 14 ++++++++++++++ server/index.js | 2 +- server/lib/api/subscription.js | 4 +++- views/partials/edit-subscription.ejs | 2 ++ 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index e7e1fa5..1637e18 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -2,6 +2,7 @@ var EditSubscriptionModal = ModalFormView.extend({ el: ".mediaDrawer.editSubscription", action: "/api/subscription", + syncAction: "/api/subscription/sync", updateAction: "/api/subscription", destroyAction: "/api/subscription/destroy", @@ -11,6 +12,7 @@ var EditSubscriptionModal = ModalFormView.extend({ events: { "click [data-role='upgradeSubscription']": 'upgradeSubscription', "click [data-role='cancelSubscription']": 'cancelSubscription', + "click .gear": 'sync', }, initialize: function(){ @@ -41,6 +43,7 @@ var EditSubscriptionModal = ModalFormView.extend({ this.$upgradeSubscription = this.$("[data-role=upgradeSubscription]") this.$cancelSubscription = this.$("[data-role=cancelSubscription]") + this.$gear = this.$(".gear") }, plan_levels: { @@ -48,10 +51,6 @@ var EditSubscriptionModal = ModalFormView.extend({ basic: 1, pro: 2, }, - - sync: function(){ - $.put(this.syncAction, this.didLoad.bind(this)) - }, loaded: false, load: function(){ @@ -74,6 +73,7 @@ var EditSubscriptionModal = ModalFormView.extend({ show: function(){ + this.$gear.removeClass("turning") if (! this.subscriber) { this.$freePlan.show() this.$paidPlan.hide() @@ -88,7 +88,7 @@ var EditSubscriptionModal = ModalFormView.extend({ this.$paidPlan.show() this.updateTotals() - + this.__super__.show.call(this) }, @@ -127,6 +127,16 @@ var EditSubscriptionModal = ModalFormView.extend({ this.$planTotal.toDollars ( totals.plan_total ) }, + sync: function(){ + this.$gear.addClass("turning") + $.ajax({ + url: this.syncAction, + type: "put", + data: { _csrf: $("[name=_csrf]").val() }, + success: this.didLoad.bind(this) + }) + }, + update: function(){ $.ajax({ url: this.updateAction, diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 8184032..981869b 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -979,6 +979,20 @@ iframe.embed { .about_custom a { border-bottom: 1px solid; } +.editSubscription .gear { + position: absolute; + top: 10px; + left: 10px; + opacity: 0.1; + cursor: pointer; + transition: all 0.3s; +} +.editSubscription .gear.turning { + opacity: 0.3; + -webkit-transform: rotate(360deg); + transform: rotate(360deg); +} + /* LAYOUTS MODAL */ diff --git a/server/index.js b/server/index.js index fa7044b..5c14d9d 100644 --- a/server/index.js +++ b/server/index.js @@ -151,9 +151,9 @@ site.route = function () { app.post('/api/media/upload', middleware.ensureAuthenticated, api.media.upload) app.delete('/api/media/destroy', middleware.ensureAuthenticated, api.media.destroy) - app.get('/api/subscription/sync', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.sync) app.get('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensurePlans, api.subscription.middleware.ensureSubscription, api.subscription.show) app.put('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.update) + app.put('/api/subscription/sync', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.sync) app.delete('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.destroy) app.get('/partials/plans', views.partials.plans) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 9c77dfc..4ec7709 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -8,7 +8,9 @@ var _ = require('lodash'), Project = require('../schemas/Project'), Layout = require('../schemas/Layout'), Plan = require('../schemas/Plan'); - Subscription = require('../schemas/Subscription'); + Subscription = require('../schemas/Subscription'), + Recurly = require('node-recurly'), + recurly = new Recurly(require('../webhook/recurly-config')); var plan_levels = { free: 0, diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index cc296f6..cfd5728 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -12,6 +12,7 @@ consider upgrading to a paid plan.

      +


    • Your current plan level is @@ -43,6 +44,7 @@
    • +
      -- cgit v1.2.3-70-g09d2 From 682b96ebb7210858e26157d1367b75efde9119e2 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 30 Jan 2015 18:11:55 -0500 Subject: follow links in profile brochure --- public/assets/javascripts/ui/site/EditSubscriptionModal.js | 9 ++++++--- public/assets/stylesheets/app.css | 12 ++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 1637e18..b38894d 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -1,5 +1,5 @@ -var EditSubscriptionModal = ModalFormView.extend({ +var EditSubscriptionModal = ModalView.extend({ el: ".mediaDrawer.editSubscription", action: "/api/subscription", syncAction: "/api/subscription/sync", @@ -13,6 +13,7 @@ var EditSubscriptionModal = ModalFormView.extend({ "click [data-role='upgradeSubscription']": 'upgradeSubscription', "click [data-role='cancelSubscription']": 'cancelSubscription', "click .gear": 'sync', + "click .planList button": 'followLink', }, initialize: function(){ @@ -54,7 +55,6 @@ var EditSubscriptionModal = ModalFormView.extend({ loaded: false, load: function(){ - this.reset() if (this.loaded) { return this.show() } $.get(this.action, this.didLoad.bind(this)) }, @@ -70,7 +70,10 @@ var EditSubscriptionModal = ModalFormView.extend({ } return this.show() }, - + followLink: function(e){ + e.preventDefault(); + window.location.href = $(e.target).closest("a").attr("href") + }, show: function(){ this.$gear.removeClass("turning") diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 981869b..4826aef 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -989,8 +989,16 @@ iframe.embed { } .editSubscription .gear.turning { opacity: 0.3; - -webkit-transform: rotate(360deg); - transform: rotate(360deg); + -webkit-animation: gear 1s; + animation: gear 1s; +} +@-webkit-keyframes gear { + from { transform: rotate(0deg); } + to { transform: rotate(720deg); } +} +@keyframes gear { + from { transform: rotate(0deg); } + to { transform: rotate(720deg); } } -- cgit v1.2.3-70-g09d2 From 9e8364dd574c02c8a5605c3b09c0230a5476f9b1 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 30 Jan 2015 18:30:37 -0500 Subject: sync working --- public/assets/javascripts/ui/site/EditSubscriptionModal.js | 1 + server/index.js | 2 +- server/lib/api/subscription.js | 9 +++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index b38894d..28d699c 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -60,6 +60,7 @@ var EditSubscriptionModal = ModalView.extend({ }, didLoad: function(data){ this.loaded = true + console.log("didLoad", data) this.plans = data.plans if (data.subscription) { this.subscriber = data.subscription diff --git a/server/index.js b/server/index.js index 5c14d9d..9f28f59 100644 --- a/server/index.js +++ b/server/index.js @@ -153,7 +153,7 @@ site.route = function () { app.get('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensurePlans, api.subscription.middleware.ensureSubscription, api.subscription.show) app.put('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.update) - app.put('/api/subscription/sync', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.sync) + app.put('/api/subscription/sync', middleware.ensureAuthenticated, api.subscription.middleware.ensurePlans, api.subscription.middleware.ensureSubscription, api.subscription.sync) app.delete('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.destroy) app.get('/partials/plans', views.partials.plans) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 4ec7709..0801204 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -41,8 +41,9 @@ var subscription = module.exports = { sync: function(req, res){ var subscriber = req.subscription || new Subscription () var user = req.user - recurly.subscriptions.listByAccount(req.user._id, function(data){ - if (data.description !== 200) { + recurly.subscriptions.listByAccount(req.user._id, function(recurlyRes){ + var data = recurlyRes.data + if (recurlyRes.description !== 200) { res.json({ error: "no account" }) return } @@ -87,8 +88,8 @@ var subscription = module.exports = { subscriber.save(function(){ user.save(function(){ - res.render({ - subscription: req.subscription, + res.json({ + subscription: subscriber, plans: res.locals.plans }) }) -- cgit v1.2.3-70-g09d2 From 18c81b8a5c8ac4b6d9cbc16e370fcc37e9138cd6 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 30 Jan 2015 19:04:31 -0500 Subject: styling form --- .../javascripts/ui/site/EditSubscriptionModal.js | 4 ++-- public/assets/stylesheets/app.css | 27 ++++++++++++++++++++++ views/partials/edit-subscription.ejs | 27 ++++++++++++---------- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 28d699c..0d54d8f 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -30,7 +30,7 @@ var EditSubscriptionModal = ModalView.extend({ this.$billingInterval = this.$("[data-role=billingInterval]") - this.$planType = this.$("[data-role=planType]") + this.$planName = this.$("[data-role=planName]") this.$planCost = this.$("[data-role=planCost]") this.$planTotal = this.$("[data-role=planTotal]") @@ -114,7 +114,7 @@ var EditSubscriptionModal = ModalView.extend({ var plan = this.plans[ this.plan_levels[ subscriber.plan_type ] ] var totals = this.calculateTotals(subscriber, plan) - this.$planType.html ( plan.name ) + this.$planName.html ( plan.name ) this.$planCost.toDollars ( totals.plan_price ) this.$billingInterval.html ( totals.is_monthly ? "mo." : "yr." ) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 4826aef..bedf2c4 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1000,6 +1000,33 @@ iframe.embed { from { transform: rotate(0deg); } to { transform: rotate(720deg); } } +.paidPlan { + text-align: left; + font-size: 14px; +} +.planInfo { + margin: 10px 0; + width: 100%; + padding: 0; +} +.planInfo td, .planInfo th { + text-align: left; + padding: 10px 3px; + margin: 0; +} +.planInfo td:nth-child(2), +.planInfo th:nth-child(2), +.planInfo td:nth-child(3), +.planInfo td:nth-child(4), +.planInfo th:nth-child(3) { + text-align: right; +} +.planInfo [data-role="billingInterval"] { + font-size: 10px; +} +.totalRow td:nth-child(4) { + border-top: 1px solid; +} /* LAYOUTS MODAL */ diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index cfd5728..266d43b 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -16,29 +16,32 @@
    • Your current plan level is - +
      - + - + + - + - - + + - + - - + + + - - - + + + +
      @ $/$/
      Additional Basic layoutsAdditional Basic layouts @ $/$each $/$/ Buy more
      Additional Pro layoutsAdditional Pro layouts @ $/$@ $ ...$/Buy more
      Total$Total$/
      -- cgit v1.2.3-70-g09d2 From 6a9418a4256c04c327262947f0fee3b615c93029 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 30 Jan 2015 20:27:42 -0500 Subject: buttonz --- .../javascripts/ui/site/EditSubscriptionModal.js | 7 ++++--- public/assets/stylesheets/app.css | 23 ++++++++++++++++++++-- views/partials/edit-subscription.ejs | 5 ++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 0d54d8f..6823740 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -10,7 +10,8 @@ var EditSubscriptionModal = ModalView.extend({ subscriber: null, events: { - "click [data-role='upgradeSubscription']": 'upgradeSubscription', + "click [data-role='addLayouts']": 'addLayouts', + "click [data-role='changePlan']": 'changePlan', "click [data-role='cancelSubscription']": 'cancelSubscription', "click .gear": 'sync', "click .planList button": 'followLink', @@ -141,11 +142,11 @@ var EditSubscriptionModal = ModalView.extend({ }) }, - update: function(){ + update: function(data){ $.ajax({ url: this.updateAction, type: "put", - data: { _csrf: this.$csrf.val() }, + data: { _csrf: $("[name=_csrf]").val() }, success: function(data){ } }) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index bedf2c4..2570bb2 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -985,12 +985,15 @@ iframe.embed { left: 10px; opacity: 0.1; cursor: pointer; + -webkit-transition: all 0.3s; + -webkit-transform-origin: 49% 53%; transition: all 0.3s; + transform-origin: 49% 53%; } .editSubscription .gear.turning { opacity: 0.3; - -webkit-animation: gear 1s; - animation: gear 1s; + -webkit-animation: gear 1s infinite linear; + animation: gear 1s infinite linear; } @-webkit-keyframes gear { from { transform: rotate(0deg); } @@ -1027,6 +1030,22 @@ iframe.embed { .totalRow td:nth-child(4) { border-top: 1px solid; } +.paidPlan button { + width: 200px; + float: none; + margin: 5px 7px; +} +.paidPlan [data-role="cancelSubscription"] { + color: red; + width: 200px; + float: none; +} +.paidPlan [data-role="cancelSubscription"]:hover { + color: white; + background: red; + border-color: red; +} + /* LAYOUTS MODAL */ diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 266d43b..8599fb5 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -28,14 +28,12 @@
    • each $/ $/Buy more
      Additional Pro layouts @ $ ... $/Buy more
      $/
      - + +
    • -- cgit v1.2.3-70-g09d2 From d08c86d68ceb22665b735054fc3bc316f85f84c1 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 31 Jan 2015 08:51:55 -0500 Subject: billing link in footer --- public/assets/javascripts/ui/site/EditSubscriptionModal.js | 12 ++++++++++-- server/lib/auth/index.js | 4 ++-- views/partials/footer.ejs | 6 ++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 6823740..82f6903 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -12,7 +12,7 @@ var EditSubscriptionModal = ModalView.extend({ events: { "click [data-role='addLayouts']": 'addLayouts', "click [data-role='changePlan']": 'changePlan', - "click [data-role='cancelSubscription']": 'cancelSubscription', + "click [data-role='cancelSubscription']": 'destroy', "click .gear": 'sync', "click .planList button": 'followLink', }, @@ -132,6 +132,13 @@ var EditSubscriptionModal = ModalView.extend({ this.$planTotal.toDollars ( totals.plan_total ) }, + addLayouts: function(e){ + e.preventDefault() + }, + changePlan: function(e){ + e.preventDefault() + }, + sync: function(){ this.$gear.addClass("turning") $.ajax({ @@ -152,7 +159,8 @@ var EditSubscriptionModal = ModalView.extend({ }) }, - destroy: function(){ + destroy: function(e){ + e.preventDefault() var msg = "Are you sure you want to cancel your subscription?" ConfirmModal.confirm(msg, function(){ $.ajax({ diff --git a/server/lib/auth/index.js b/server/lib/auth/index.js index a9a2400..199377d 100644 --- a/server/lib/auth/index.js +++ b/server/lib/auth/index.js @@ -135,12 +135,12 @@ var auth = { deserializeUser: function (id, done) { try { var _id = mongoose.Types.ObjectId(id) - User.findOne({ _id: _id }, "_id displayName username photo isStaff", function (err, user) { + User.findOne({ _id: _id }, "_id displayName username photo isStaff plan_level", function (err, user) { done(err, user); }); } catch (e) { - User.findOne({ twitter_id: id }, "_id displayName username photo isStaff", function (err, user) { + User.findOne({ twitter_id: id }, "_id displayName username photo isStaff plan_level", function (err, user) { done(err, user); }); } diff --git a/views/partials/footer.ejs b/views/partials/footer.ejs index e3c572f..8a95fc4 100644 --- a/views/partials/footer.ejs +++ b/views/partials/footer.ejs @@ -15,6 +15,12 @@ About How To + + [[ if (logged_in && user.plan_level > 0) { ]] + Billing + [[ } else { ]] + Subscribe + [[ } ]] Terms Privacy Contact -- cgit v1.2.3-70-g09d2 From af230e59ec431c9b617b0caf94dc7c37bb5e81af Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 31 Jan 2015 09:16:27 -0500 Subject: handle cancelled subscription --- .../javascripts/ui/site/EditSubscriptionModal.js | 5 +- server/index.js | 2 +- server/lib/api/subscription.js | 78 +++++++++++++--------- server/lib/schemas/Subscription.js | 2 +- 4 files changed, 51 insertions(+), 36 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 82f6903..7dd527a 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -61,7 +61,6 @@ var EditSubscriptionModal = ModalView.extend({ }, didLoad: function(data){ this.loaded = true - console.log("didLoad", data) this.plans = data.plans if (data.subscription) { this.subscriber = data.subscription @@ -163,11 +162,13 @@ var EditSubscriptionModal = ModalView.extend({ e.preventDefault() var msg = "Are you sure you want to cancel your subscription?" ConfirmModal.confirm(msg, function(){ + this.__super__.show.call(this) $.ajax({ url: this.destroyAction, type: "delete", - data: { _csrf: this.$csrf.val() }, + data: { _csrf: $("[name=_csrf]").val() }, success: function(data){ + window.location.href = "/" } }) }.bind(this)) diff --git a/server/index.js b/server/index.js index 9f28f59..3572675 100644 --- a/server/index.js +++ b/server/index.js @@ -154,7 +154,7 @@ site.route = function () { app.get('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensurePlans, api.subscription.middleware.ensureSubscription, api.subscription.show) app.put('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.update) app.put('/api/subscription/sync', middleware.ensureAuthenticated, api.subscription.middleware.ensurePlans, api.subscription.middleware.ensureSubscription, api.subscription.sync) - app.delete('/api/subscription', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.destroy) + app.delete('/api/subscription/destroy', middleware.ensureAuthenticated, api.subscription.middleware.ensureSubscription, api.subscription.destroy) app.get('/partials/plans', views.partials.plans) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 0801204..285ce8b 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -51,40 +51,51 @@ var subscription = module.exports = { res.json({ error: "account error" }) return } + if (subscriber.state != "active") { + user.plan_type = "free" + user.plan_level = plan_levels["free"] - var plan = data.subscriptions.subscription.plan.plan_code.split("-") - var plan_type = plan[0] - var plan_period = plan[1] - - user.plan_type = plan_type - user.plan_level = plan_levels[plan_type] + return subscriber.remove(function(){ + user.save(function(){ + res.json({ error: "account cancelled" }) + }) + }) + } + else { + var plan = data.subscriptions.subscription.plan.plan_code.split("-") + var plan_type = plan[0] + var plan_period = plan[1] + + user.plan_type = plan_type + user.plan_level = plan_levels[plan_type] - subscriber.uuid = data.subscriptions.subscription.uuid - subscriber.user_id = user._id - subscriber.plan_type = plan_type - subscriber.plan_period = plan_period - subscriber.plan_level = plan_levels[plan_type] - - var add_ons = data.subscriptions.subscription.subscription_add_ons.subscription_add_on - if (add_ons) { - if (add_ons.add_on_code) { - add_ons = [ add_ons ] - } - // TODO: handle multiple add-ons.. presumably this will work - add_ons.forEach(function(add_on){ - var add_on_type = add_on.add_on_code.split("-")[1] - if (add_on_type == "basic") { - subscriber.basic_layouts = parseInt( add_on.quantity._ ) - } - if (add_on_type == "pro") { - subscriber.pro_layouts = parseInt( add_on.quantity._ ) - } - }) - } - else { - subscriber.basic_layouts = 0 - subscriber.pro_layouts = 0 - } + subscriber.uuid = data.subscriptions.subscription.uuid + subscriber.user_id = user._id + subscriber.plan_type = plan_type + subscriber.plan_period = plan_period + subscriber.plan_level = plan_levels[plan_type] + + var add_ons = data.subscriptions.subscription.subscription_add_ons.subscription_add_on + if (add_ons) { + if (add_ons.add_on_code) { + add_ons = [ add_ons ] + } + // TODO: handle multiple add-ons.. presumably this will work + add_ons.forEach(function(add_on){ + var add_on_type = add_on.add_on_code.split("-")[1] + if (add_on_type == "basic") { + subscriber.basic_layouts = parseInt( add_on.quantity._ ) + } + if (add_on_type == "pro") { + subscriber.pro_layouts = parseInt( add_on.quantity._ ) + } + }) + } + else { + subscriber.basic_layouts = 0 + subscriber.pro_layouts = 0 + } + } subscriber.save(function(){ user.save(function(){ @@ -156,6 +167,9 @@ var subscription = module.exports = { subscriber.remove(function(){ req.user.plan_code = 0 req.user.plan_type = "free" + req.user.save(function(){ + res.json({ status: "OK" }) + }) }) }) }, diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js index 44455a9..355bbe2 100644 --- a/server/lib/schemas/Subscription.js +++ b/server/lib/schemas/Subscription.js @@ -12,7 +12,7 @@ var SubscriptionSchema = new mongoose.Schema({ plan_type: { type: String, default: "free" }, plan_period: { type: String, default: "monthly" }, - subscription_uuid: { type: String }, + uuid: { type: String }, basic_layouts: { type: Number, default: 0 }, pro_layouts: { type: Number, default: 0 }, -- cgit v1.2.3-70-g09d2 From 8a2125b84b04335920b91eed03ecdb38a9f9e3c0 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 31 Jan 2015 09:48:35 -0500 Subject: should terminate to get rid of the old subscription --- server/lib/api/subscription.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 285ce8b..a3baf34 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -163,7 +163,7 @@ var subscription = module.exports = { } var subscriber = req.subscription - recurly.subscriptions.cancel(subscriber.uuid, function(){ + recurly.subscriptions.terminate(subscriber.uuid, "partial", function(){ subscriber.remove(function(){ req.user.plan_code = 0 req.user.plan_type = "free" -- cgit v1.2.3-70-g09d2 From 6113063fa350114af8f39b87e96cc2c321bc2629 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 31 Jan 2015 10:10:27 -0500 Subject: handle syncing when an account has been cancelled and recreated --- server/lib/api/subscription.js | 109 +++++++++++++++++++---------------- views/partials/edit-subscription.ejs | 2 +- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index a3baf34..b8853e6 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -47,65 +47,72 @@ var subscription = module.exports = { res.json({ error: "no account" }) return } - if (! data.subscriptions.subscription) { - res.json({ error: "account error" }) - return - } - if (subscriber.state != "active") { - user.plan_type = "free" - user.plan_level = plan_levels["free"] + var subscriptions = data.subscriptions.subscription.length ? data.subscriptions.subscription : [data.subscriptions.subscription] + var is_active = subscriptions.some(function(subscription_data){ + if (subscription_data.state == "active") { + set_active(subscription_data) + return true + } + return false + }) + console.log(data.subscriptions) + if (! is_active) { + set_cancelled() + } + }) + function set_cancelled(){ + user.plan_type = "free" + user.plan_level = plan_levels["free"] - return subscriber.remove(function(){ - user.save(function(){ - res.json({ error: "account cancelled" }) - }) + return subscriber.remove(function(){ + user.save(function(){ + res.json({ error: "account cancelled" }) }) - } - else { - var plan = data.subscriptions.subscription.plan.plan_code.split("-") - var plan_type = plan[0] - var plan_period = plan[1] - - user.plan_type = plan_type - user.plan_level = plan_levels[plan_type] + }) + } + function set_active(data){ + var plan = data.plan.plan_code.split("-") + var plan_type = plan[0] + var plan_period = plan[1] + + user.plan_type = plan_type + user.plan_level = plan_levels[plan_type] - subscriber.uuid = data.subscriptions.subscription.uuid - subscriber.user_id = user._id - subscriber.plan_type = plan_type - subscriber.plan_period = plan_period - subscriber.plan_level = plan_levels[plan_type] - - var add_ons = data.subscriptions.subscription.subscription_add_ons.subscription_add_on - if (add_ons) { - if (add_ons.add_on_code) { - add_ons = [ add_ons ] - } - // TODO: handle multiple add-ons.. presumably this will work - add_ons.forEach(function(add_on){ - var add_on_type = add_on.add_on_code.split("-")[1] - if (add_on_type == "basic") { - subscriber.basic_layouts = parseInt( add_on.quantity._ ) - } - if (add_on_type == "pro") { - subscriber.pro_layouts = parseInt( add_on.quantity._ ) - } - }) - } - else { - subscriber.basic_layouts = 0 - subscriber.pro_layouts = 0 + subscriber.uuid = data.uuid + subscriber.user_id = user._id + subscriber.plan_type = plan_type + subscriber.plan_period = plan_period + subscriber.plan_level = plan_levels[plan_type] + + var add_ons = data.subscription_add_ons.subscription_add_on + if (add_ons) { + if (add_ons.add_on_code) { + add_ons = [ add_ons ] } + // TODO: handle multiple add-ons.. presumably this will work + add_ons.forEach(function(add_on){ + var add_on_type = add_on.add_on_code.split("-")[1] + if (add_on_type == "basic") { + subscriber.basic_layouts = parseInt( add_on.quantity._ ) + } + if (add_on_type == "pro") { + subscriber.pro_layouts = parseInt( add_on.quantity._ ) + } + }) + } + else { + subscriber.basic_layouts = 0 + subscriber.pro_layouts = 0 } - - subscriber.save(function(){ - user.save(function(){ - res.json({ + subscriber.save(function(){ + user.save(function(){ + res.json({ subscription: subscriber, plans: res.locals.plans }) - }) - }) - }) + }) + }) + } }, show: function(req, res){ diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 8599fb5..69ec88d 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -42,7 +42,7 @@ $/ - + -- cgit v1.2.3-70-g09d2 From 85728db4898e88592d9e89510bf121de91e59a6f Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 31 Jan 2015 10:17:12 -0500 Subject: .. --- public/assets/javascripts/ui/site/EditSubscriptionModal.js | 5 ++--- server/lib/api/subscription.js | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 7dd527a..0f72995 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -162,14 +162,13 @@ var EditSubscriptionModal = ModalView.extend({ e.preventDefault() var msg = "Are you sure you want to cancel your subscription?" ConfirmModal.confirm(msg, function(){ - this.__super__.show.call(this) $.ajax({ url: this.destroyAction, type: "delete", data: { _csrf: $("[name=_csrf]").val() }, success: function(data){ - window.location.href = "/" - } + this.didLoad(data) + }.bind(this) }) }.bind(this)) }, diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index b8853e6..3a8403f 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -55,7 +55,6 @@ var subscription = module.exports = { } return false }) - console.log(data.subscriptions) if (! is_active) { set_cancelled() } -- cgit v1.2.3-70-g09d2 From 4d0a9b9402b0ba1b5d50ba27106541e4e4dbe19f Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 31 Jan 2015 15:29:37 -0500 Subject: css --- public/assets/stylesheets/app.css | 56 ++++++++++++++++++++++----------------- views/about/_plans.ejs | 2 +- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 2570bb2..082a2d4 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -904,30 +904,6 @@ iframe.embed { /* PLANS BROCHURE */ /* nb these styles should be fixed for narrower screens/mobile layout */ -.planList { - position: absolute; - left: 50%; - transform: translateX(-48%); -} -.planList .about_custom { - width: 56vw; - margin: 1vw 4vw; -} -.planList .planbox { - padding: 0; -} -.mediaDrawer .planList h3 { - margin-top: 3px; - font-size: 21px; - font-weight: bold -} -.freePlan { - text-align: left; -} -.planList .planbox li:first-child { - font-size: 1.9vw; - padding: 0; -} .about_plan { width: 28vw; margin: 2vw; @@ -979,6 +955,38 @@ iframe.embed { .about_custom a { border-bottom: 1px solid; } + +/* Billing / Subscription Settings */ +.planList { + position: absolute; + left: 50%; + transform: translateX(-48%); +} +.planList .about_custom { + width: 56vw; + margin: 1vw 4vw; +} +.planList .planbox { + padding: 0; +} +.planList .custombox { + margin: 2vw 4vw 4vw 4vw; +} +.planList .miscbox { + line-height: 2.7vw; +} +.mediaDrawer .planList h3 { + margin-top: 3px; + font-size: 21px; + font-weight: bold +} +.freePlan { + text-align: left; +} +.planList .planbox li:first-child { + font-size: 1.9vw; + padding: 0; +} .editSubscription .gear { position: absolute; top: 10px; diff --git a/views/about/_plans.ejs b/views/about/_plans.ejs index 1536ac3..abd6aaa 100644 --- a/views/about/_plans.ejs +++ b/views/about/_plans.ejs @@ -56,7 +56,7 @@
    -
    +

    Want Something Custom?

  • We offer customized white-label options for business and educational uses.
  • Contact us for more information. -- cgit v1.2.3-70-g09d2 From ef52758ef0c27760a93ca861f1d17259dbc40fc4 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 31 Jan 2015 19:10:24 -0500 Subject: adding radio buttons etc --- .../javascripts/ui/site/EditSubscriptionModal.js | 31 +++++++++++++++++-- public/assets/stylesheets/app.css | 11 +++++-- server/index.js | 2 +- server/lib/api/subscription.js | 2 +- views/partials/edit-subscription.ejs | 35 +++++++++++++++++----- 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 0f72995..619bff8 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -35,6 +35,11 @@ var EditSubscriptionModal = ModalView.extend({ this.$planCost = this.$("[data-role=planCost]") this.$planTotal = this.$("[data-role=planTotal]") + this.$basicPlanName = this.$("[data-role=basicPlanName]") + this.$proPlanName = this.$("[data-role=proPlanName]") + this.$basicPlanCost = this.$("[data-role=basicPlanCost]") + this.$proPlanCost = this.$("[data-role=proPlanCost]") + this.$basicLayoutCost = this.$("[data-role=basicLayoutCost]") this.$basicLayoutQuantity = this.$("[data-role=basicLayoutQuantity]") this.$basicLayoutTotal = this.$("[data-role=basicLayoutTotal]") @@ -43,8 +48,24 @@ var EditSubscriptionModal = ModalView.extend({ this.$proLayoutQuantity = this.$("[data-role=proLayoutQuantity]") this.$proLayoutTotal = this.$("[data-role=proLayoutTotal]") + this.$showLayoutsMenu = this.$("[data-role=showLayoutsMenu]") + this.$showPlanMenu = this.$("[data-role=showPlanMenu]") + + this.$layoutsMenu = this.$("[data-role=layoutsMenu]") + this.$planMenu = this.$("[data-role=planMenu]") + this.$closeMenu = this.$("[data-role=closeMenu]") + + this.$buyLayouts = this.$("[data-role=buyLayouts]") + + this.$changePlan = this.$("[data-role=changePlan]") + + this.$basicLayoutInput = this.$("[data-role=basicLayoutInput]") + this.$proLayoutInput = this.$("[data-role=proLayoutInput]") + this.$planRadio = this.$("[name=planRadio]") + this.$upgradeSubscription = this.$("[data-role=upgradeSubscription]") this.$cancelSubscription = this.$("[data-role=cancelSubscription]") + this.$gear = this.$(".gear") }, @@ -100,7 +121,7 @@ var EditSubscriptionModal = ModalView.extend({ var t = {} t.is_pro = plan.name == "pro" t.is_monthly = subscriber.plan_period == "monthly" - t.plan_price = t.is_monthly ? plan.monthly_price : plan.yearly_price + t.plan_price = t.is_monthly ? plan.basic_layout_monthly_price : plan.yearly_price t.basic_layout_price = t.is_monthly ? plan.basic_layout_monthly_price : plan.basic_layout_yearly_price t.basic_layout_total = subscriber.basic_layouts * t.basic_layout_price t.pro_layout_price = t.is_monthly ? plan.pro_layout_monthly_price : plan.pro_layout_yearly_price @@ -113,12 +134,18 @@ var EditSubscriptionModal = ModalView.extend({ var subscriber = this.subscriber var plan = this.plans[ this.plan_levels[ subscriber.plan_type ] ] var totals = this.calculateTotals(subscriber, plan) + + this.$basicPlanName.html ( this.plans[1].name ) + this.$proPlanName.html ( this.plans[2].name ) + this.$basicPlanCost.toDollars ( totals.is_monthly ? this.plans[1].monthly_price : this.plans[2].yearly_price) + this.$proPlanCost.toDollars ( totals.is_monthly ? this.plans[2].monthly_price : this.plans[2].yearly_price) this.$planName.html ( plan.name ) this.$planCost.toDollars ( totals.plan_price ) this.$billingInterval.html ( totals.is_monthly ? "mo." : "yr." ) - this.$proLayoutRow.toggle ( totals.is_pro ) + this.$basicLayoutRow.toggle ( subscriber.basic_layouts > 0 ) + this.$proLayoutRow.toggle ( totals.is_pro && subscriber.pro_layouts > 0) this.$basicLayoutCost.toDollars ( totals.basic_layout_price ) this.$basicLayoutQuantity.html ( subscriber.basic_layouts ) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 082a2d4..4144b8b 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -987,6 +987,8 @@ iframe.embed { font-size: 1.9vw; padding: 0; } + +/* debug sync button */ .editSubscription .gear { position: absolute; top: 10px; @@ -1053,8 +1055,13 @@ iframe.embed { background: red; border-color: red; } - - +/* purchase mode */ +.paidPlan input[type=number] { display: none } +.paidPlan.editing input[type=number] { display: inline-block } +.paidPlan.editing input[type=number]+span { display: none } +form .paidPlan div { float: none; } +form .paidPlan label { float: none; font-size: 2vw; margin: 0 10px; } +.paidPlan .upgradeMenu div { padding: 5px 10px; } /* LAYOUTS MODAL */ diff --git a/server/index.js b/server/index.js index 3572675..8c8212f 100644 --- a/server/index.js +++ b/server/index.js @@ -72,7 +72,7 @@ site.setup = function(){ server = http.createServer(app) server.listen(app.get('port'), function () { - console.log('Vvalls server listening on port ' + app.get('port')); + console.log('VValls server listening on port ' + app.get('port')); }); // var io = websocket.listen(server) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 3a8403f..6f08e66 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -28,7 +28,7 @@ var subscription = module.exports = { }) }, ensurePlans: function(req, res, next){ - Plan.find({}).sort({ 'level': -1 }).exec(function (err, plans) { + Plan.find({}).sort({ 'level': 1 }).exec(function (err, plans) { res.locals.plans = (plans || []) next() }) diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 69ec88d..fcb4b94 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -8,13 +8,13 @@

    Edit Subscription

  • - You are currently using the free version of Vvalls. For access to all of Vvalls features, + You are currently using the free version of VValls. For access to all of VValls features, consider upgrading to a paid plan.




  • -
  • +
  • Your current plan level is @@ -25,13 +25,13 @@ - + - + @@ -42,9 +42,30 @@
    Additional Basic layouts each $/ $/
    Additional Pro layouts @ $ ... $/
    $/
    - - - +
    + + + +
    +
    + + +
    +
    +

    Select desired plan:

    +
    + + + $/ +
    +
    + + + $/ +
    + + +
  • -- cgit v1.2.3-70-g09d2 From a7be7295ae568bee76f18e3cf1228168b265db76 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 14:32:25 -0500 Subject: setting up events --- .../javascripts/ui/site/EditSubscriptionModal.js | 44 ++++++++++++++++------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 619bff8..af3b99e 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -15,14 +15,30 @@ var EditSubscriptionModal = ModalView.extend({ "click [data-role='cancelSubscription']": 'destroy', "click .gear": 'sync', "click .planList button": 'followLink', + + "click [data-role=showLayoutsMenu]": "layoutsMode", + "click [data-role=showPlanMenu]": "planMode", + + "click [data-role=closeMenu]": "reset", + + "input [data-role=basicLayoutInput]": "updateQuantity", + "input [data-role=proLayoutInput]": "updateQuantity", + "click [data-role=buyLayouts]": "saveLayouts", + + "input [name=planRadio]": "updatePlan", + "click [data-role=changePlan]": "savePlan", }, initialize: function(){ // this.parent = opt.parent this.__super__.initialize.call(this) + // two sections this.$freePlan = this.$(".freePlan") this.$paidPlan = this.$(".paidPlan") + + // subscription table + this.$planInfo = this.$(".planInfo") this.$planRow = this.$(".planRow") this.$basicLayoutRow = this.$(".basicLayoutRow") this.$proLayoutRow = this.$(".proLayoutRow") @@ -31,15 +47,18 @@ var EditSubscriptionModal = ModalView.extend({ this.$billingInterval = this.$("[data-role=billingInterval]") + // plan stuff this.$planName = this.$("[data-role=planName]") this.$planCost = this.$("[data-role=planCost]") this.$planTotal = this.$("[data-role=planTotal]") this.$basicPlanName = this.$("[data-role=basicPlanName]") - this.$proPlanName = this.$("[data-role=proPlanName]") this.$basicPlanCost = this.$("[data-role=basicPlanCost]") + + this.$proPlanName = this.$("[data-role=proPlanName]") this.$proPlanCost = this.$("[data-role=proPlanCost]") + // basic + pro layout stuff this.$basicLayoutCost = this.$("[data-role=basicLayoutCost]") this.$basicLayoutQuantity = this.$("[data-role=basicLayoutQuantity]") this.$basicLayoutTotal = this.$("[data-role=basicLayoutTotal]") @@ -48,24 +67,24 @@ var EditSubscriptionModal = ModalView.extend({ this.$proLayoutQuantity = this.$("[data-role=proLayoutQuantity]") this.$proLayoutTotal = this.$("[data-role=proLayoutTotal]") + // menus.. main menu this.$showLayoutsMenu = this.$("[data-role=showLayoutsMenu]") this.$showPlanMenu = this.$("[data-role=showPlanMenu]") + this.$cancelSubscription = this.$("[data-role=cancelSubscription]") + // three submenus this.$layoutsMenu = this.$("[data-role=layoutsMenu]") this.$planMenu = this.$("[data-role=planMenu]") - this.$closeMenu = this.$("[data-role=closeMenu]") this.$buyLayouts = this.$("[data-role=buyLayouts]") - + this.$closeMenu = this.$("[data-role=closeMenu]") this.$changePlan = this.$("[data-role=changePlan]") + // input fields this.$basicLayoutInput = this.$("[data-role=basicLayoutInput]") this.$proLayoutInput = this.$("[data-role=proLayoutInput]") this.$planRadio = this.$("[name=planRadio]") - this.$upgradeSubscription = this.$("[data-role=upgradeSubscription]") - this.$cancelSubscription = this.$("[data-role=cancelSubscription]") - this.$gear = this.$(".gear") }, @@ -112,11 +131,15 @@ var EditSubscriptionModal = ModalView.extend({ this.$freePlan.hide() this.$paidPlan.show() - this.updateTotals() + this.reset() this.__super__.show.call(this) }, - + reset: function(){ + var subscriber = this.subscriber + var plan = this.plans[ this.plan_levels[ subscriber.plan_type ] ] + this.displayTotals(subscriber) + }, calculateTotals: function(subscriber, plan){ var t = {} t.is_pro = plan.name == "pro" @@ -129,10 +152,7 @@ var EditSubscriptionModal = ModalView.extend({ t.plan_total = t.plan_price + t.basic_layout_total + t.pro_layout_total return t }, - - updateTotals: function(){ - var subscriber = this.subscriber - var plan = this.plans[ this.plan_levels[ subscriber.plan_type ] ] + displayTotals: function(subscriber, plan){ var totals = this.calculateTotals(subscriber, plan) this.$basicPlanName.html ( this.plans[1].name ) -- cgit v1.2.3-70-g09d2 From 97e7312439b24bfdd255f6868e8b42c469b0b38e Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 14:49:10 -0500 Subject: basic ui --- .../javascripts/ui/site/EditSubscriptionModal.js | 60 ++++++++++++++++++++-- public/assets/stylesheets/app.css | 10 +++- views/partials/edit-subscription.ejs | 4 +- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index af3b99e..539a8ae 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -8,6 +8,7 @@ var EditSubscriptionModal = ModalView.extend({ fixedClose: true, subscriber: null, + tempSubscriber: null, events: { "click [data-role='addLayouts']": 'addLayouts', @@ -19,16 +20,62 @@ var EditSubscriptionModal = ModalView.extend({ "click [data-role=showLayoutsMenu]": "layoutsMode", "click [data-role=showPlanMenu]": "planMode", - "click [data-role=closeMenu]": "reset", + "click [data-role=closeMenu]": "resetMode", "input [data-role=basicLayoutInput]": "updateQuantity", "input [data-role=proLayoutInput]": "updateQuantity", - "click [data-role=buyLayouts]": "saveLayouts", + "click [data-role=saveQuantity]": "saveQuantity", "input [name=planRadio]": "updatePlan", "click [data-role=changePlan]": "savePlan", }, - + + resetMode: function(e){ + e.preventDefault() + this.$paidPlan.removeClass("editLayouts editPlan") + this.reset() + }, + + layoutsMode: function(e){ + e.preventDefault() + this.$paidPlan.addClass("editLayouts") + this.tempSubscriber = defaults({}, this.subscriber) + this.$basicLayoutInput.val( this.subscriber.basic_layouts ) + this.$proLayoutInput.val( this.subscriber.pro_layouts ) + }, + updateQuantity: function(e){ + e.preventDefault() + this.tempSubscriber.basic_layouts = this.$proLayoutInput.int() + this.tempSubscriber.pro_layouts = this.$basicLayoutInput.int() + + var plan = this.getPlan( this.tempSubscriber.plan_type ) + this.displayTotals(this.tempSubscriber, plan) + }, + saveQuantity: function(e){ + e.preventDefault() + // blabla.. save + this.subscriber = this.tempSubscriber + this.resetMode() + }, + + planMode: function(e){ + e.preventDefault() + this.$paidPlan.addClass("editPlan") + this.$planRadio.val( this.subscriber.plan_type ) + this.tempSubscriber = defaults({}, this.subscriber) + }, + updatePlan: function(e){ + e.preventDefault() + var plan = this.getPlan( this.$("[name=planRadio]").val() ) + this.displayTotals(this.tempSubscriber, plan) + }, + savePlan: function(e){ + e.preventDefault() + this.subscriber.plan_type = this.$("[name=planRadio]").val() + this.subscriber = this.tempSubscriber + this.resetMode() + }, + initialize: function(){ // this.parent = opt.parent this.__super__.initialize.call(this) @@ -137,8 +184,11 @@ var EditSubscriptionModal = ModalView.extend({ }, reset: function(){ var subscriber = this.subscriber - var plan = this.plans[ this.plan_levels[ subscriber.plan_type ] ] - this.displayTotals(subscriber) + var plan = this.getPlan(subscriber.plan_type) + this.displayTotals(subscriber, plan) + }, + getPlan: function(plan_type){ + return this.plans[ this.plan_levels[ plan_type ] ] }, calculateTotals: function(subscriber, plan){ var t = {} diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 4144b8b..b3d380c 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1057,11 +1057,17 @@ iframe.embed { } /* purchase mode */ .paidPlan input[type=number] { display: none } -.paidPlan.editing input[type=number] { display: inline-block } -.paidPlan.editing input[type=number]+span { display: none } form .paidPlan div { float: none; } form .paidPlan label { float: none; font-size: 2vw; margin: 0 10px; } .paidPlan .upgradeMenu div { padding: 5px 10px; } +.paidPlan .layoutsMenu { display: none } +.paidPlan .planMenu { display: none } +.paidPlan.editLayouts .layoutsMenu { display: block } +.paidPlan.editLayouts .billingMenu { display: none } +.paidPlan.editLayouts input[type=number] { display: inline-block } +.paidPlan.editLayouts input[type=number]+span { display: none } +.paidPlan.editPlan .planMenu { display: block } +.paidPlan.editPlan .billingMenu { display: none } /* LAYOUTS MODAL */ diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index fcb4b94..086f513 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -48,7 +48,7 @@
    - +
    @@ -63,7 +63,7 @@ $/
    - +
    -- cgit v1.2.3-70-g09d2 From bc843c0c65d9ff98dac35a72821f7e312dc7e62b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 15:31:44 -0500 Subject: let confirmModal take two callbacks --- public/assets/javascripts/ui/lib/ConfirmModal.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/public/assets/javascripts/ui/lib/ConfirmModal.js b/public/assets/javascripts/ui/lib/ConfirmModal.js index a72b31e..7d9da67 100644 --- a/public/assets/javascripts/ui/lib/ConfirmModal.js +++ b/public/assets/javascripts/ui/lib/ConfirmModal.js @@ -4,21 +4,31 @@ var ConfirmModal = new( ModalFormView.extend({ el: ".mediaDrawer.confirm", events: { - "click .yes": "advance", - "click .no": "hide", + "click .yes": "agree", + "click .no": "cancel", }, - confirm: function(question, callback){ + confirm: function(question, agreeCallback, cancelCallback){ this.$(".question").empty().append(question) - this.callback = callback + this.agreeCallback = agreeCallback + this.cancelCallback = cancelCallback this.show() }, - advance: function(e){ + agree: function(e){ e && e.preventDefault() this.hide() - this.callback && this.callback() - this.callback = null + this.agreeCallback && this.agreeCallback() + this.agreeCallback = null + this.cancelCallback = null + }, + + cancel: function(e){ + e && e.preventDefault() + this.hide() + this.cancelCallback && this.cancelCallback() + this.agreeCallback = null + this.cancelCallback = null } }) ) \ No newline at end of file -- cgit v1.2.3-70-g09d2 From fbf637f2210d281c8c6f41b72cf50746939a8ece Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 15:32:01 -0500 Subject: edit layout counts --- .../javascripts/ui/site/EditSubscriptionModal.js | 24 +++++++++++++--------- public/assets/stylesheets/app.css | 13 ++++++++++-- views/partials/edit-subscription.ejs | 4 ++-- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 539a8ae..fe93499 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -31,46 +31,50 @@ var EditSubscriptionModal = ModalView.extend({ }, resetMode: function(e){ - e.preventDefault() + e && e.preventDefault() this.$paidPlan.removeClass("editLayouts editPlan") this.reset() }, layoutsMode: function(e){ - e.preventDefault() + e && e.preventDefault() this.$paidPlan.addClass("editLayouts") this.tempSubscriber = defaults({}, this.subscriber) this.$basicLayoutInput.val( this.subscriber.basic_layouts ) this.$proLayoutInput.val( this.subscriber.pro_layouts ) + this.$basicLayoutRow.show() + this.$proLayoutRow.toggle(this.subscriber.plan_name == "pro") }, updateQuantity: function(e){ - e.preventDefault() - this.tempSubscriber.basic_layouts = this.$proLayoutInput.int() - this.tempSubscriber.pro_layouts = this.$basicLayoutInput.int() + e && e.preventDefault() + this.tempSubscriber.basic_layouts = this.$basicLayoutInput.int() + this.tempSubscriber.pro_layouts = this.$proLayoutInput.int() var plan = this.getPlan( this.tempSubscriber.plan_type ) this.displayTotals(this.tempSubscriber, plan) + this.$basicLayoutRow.show() + this.$proLayoutRow.toggle(plan.name == "pro") }, saveQuantity: function(e){ - e.preventDefault() + e && e.preventDefault() // blabla.. save this.subscriber = this.tempSubscriber this.resetMode() }, planMode: function(e){ - e.preventDefault() + e && e.preventDefault() this.$paidPlan.addClass("editPlan") this.$planRadio.val( this.subscriber.plan_type ) this.tempSubscriber = defaults({}, this.subscriber) }, updatePlan: function(e){ - e.preventDefault() + e && e.preventDefault() var plan = this.getPlan( this.$("[name=planRadio]").val() ) this.displayTotals(this.tempSubscriber, plan) }, savePlan: function(e){ - e.preventDefault() + e && e.preventDefault() this.subscriber.plan_type = this.$("[name=planRadio]").val() this.subscriber = this.tempSubscriber this.resetMode() @@ -178,7 +182,7 @@ var EditSubscriptionModal = ModalView.extend({ this.$freePlan.hide() this.$paidPlan.show() - this.reset() + this.resetMode() this.__super__.show.call(this) }, diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index b3d380c..b58bb01 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1027,6 +1027,10 @@ iframe.embed { padding: 10px 3px; margin: 0; } +.planInfo td { + height: 60px; + vertical-align: middle; +} .planInfo td:nth-child(2), .planInfo th:nth-child(2), .planInfo td:nth-child(3), @@ -1034,6 +1038,10 @@ iframe.embed { .planInfo th:nth-child(3) { text-align: right; } +.planInfo td:nth-child(4) { + width: 90px; +} + .planInfo [data-role="billingInterval"] { font-size: 10px; } @@ -1046,7 +1054,8 @@ iframe.embed { margin: 5px 7px; } .paidPlan [data-role="cancelSubscription"] { - color: red; + color: #800; + border-color: black; width: 200px; float: none; } @@ -1056,7 +1065,7 @@ iframe.embed { border-color: red; } /* purchase mode */ -.paidPlan input[type=number] { display: none } +.paidPlan input[type=number] { display: none; float: right; } form .paidPlan div { float: none; } form .paidPlan label { float: none; font-size: 2vw; margin: 0 10px; } .paidPlan .upgradeMenu div { padding: 5px 10px; } diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 086f513..19f6851 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -43,8 +43,8 @@
    - - +
    +
    -- cgit v1.2.3-70-g09d2 From 60aa806d65a90907c6a76cb017c167c8453260b3 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 16:58:58 -0500 Subject: plan val from radio button --- .../javascripts/ui/site/EditSubscriptionModal.js | 110 ++++++++++----------- views/partials/edit-subscription.ejs | 4 +- 2 files changed, 56 insertions(+), 58 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index fe93499..b9abd75 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -26,60 +26,10 @@ var EditSubscriptionModal = ModalView.extend({ "input [data-role=proLayoutInput]": "updateQuantity", "click [data-role=saveQuantity]": "saveQuantity", - "input [name=planRadio]": "updatePlan", + "change [name=planRadio]": "updatePlan", "click [data-role=changePlan]": "savePlan", }, - resetMode: function(e){ - e && e.preventDefault() - this.$paidPlan.removeClass("editLayouts editPlan") - this.reset() - }, - - layoutsMode: function(e){ - e && e.preventDefault() - this.$paidPlan.addClass("editLayouts") - this.tempSubscriber = defaults({}, this.subscriber) - this.$basicLayoutInput.val( this.subscriber.basic_layouts ) - this.$proLayoutInput.val( this.subscriber.pro_layouts ) - this.$basicLayoutRow.show() - this.$proLayoutRow.toggle(this.subscriber.plan_name == "pro") - }, - updateQuantity: function(e){ - e && e.preventDefault() - this.tempSubscriber.basic_layouts = this.$basicLayoutInput.int() - this.tempSubscriber.pro_layouts = this.$proLayoutInput.int() - - var plan = this.getPlan( this.tempSubscriber.plan_type ) - this.displayTotals(this.tempSubscriber, plan) - this.$basicLayoutRow.show() - this.$proLayoutRow.toggle(plan.name == "pro") - }, - saveQuantity: function(e){ - e && e.preventDefault() - // blabla.. save - this.subscriber = this.tempSubscriber - this.resetMode() - }, - - planMode: function(e){ - e && e.preventDefault() - this.$paidPlan.addClass("editPlan") - this.$planRadio.val( this.subscriber.plan_type ) - this.tempSubscriber = defaults({}, this.subscriber) - }, - updatePlan: function(e){ - e && e.preventDefault() - var plan = this.getPlan( this.$("[name=planRadio]").val() ) - this.displayTotals(this.tempSubscriber, plan) - }, - savePlan: function(e){ - e && e.preventDefault() - this.subscriber.plan_type = this.$("[name=planRadio]").val() - this.subscriber = this.tempSubscriber - this.resetMode() - }, - initialize: function(){ // this.parent = opt.parent this.__super__.initialize.call(this) @@ -135,6 +85,8 @@ var EditSubscriptionModal = ModalView.extend({ this.$basicLayoutInput = this.$("[data-role=basicLayoutInput]") this.$proLayoutInput = this.$("[data-role=proLayoutInput]") this.$planRadio = this.$("[name=planRadio]") + this.$basicPlanInput = this.$("[data-role=basicPlanInput]") + this.$proPlanInput = this.$("[data-role=proPlanInput]") this.$gear = this.$(".gear") }, @@ -198,7 +150,7 @@ var EditSubscriptionModal = ModalView.extend({ var t = {} t.is_pro = plan.name == "pro" t.is_monthly = subscriber.plan_period == "monthly" - t.plan_price = t.is_monthly ? plan.basic_layout_monthly_price : plan.yearly_price + t.plan_price = t.is_monthly ? plan.monthly_price : plan.yearly_price t.basic_layout_price = t.is_monthly ? plan.basic_layout_monthly_price : plan.basic_layout_yearly_price t.basic_layout_total = subscriber.basic_layouts * t.basic_layout_price t.pro_layout_price = t.is_monthly ? plan.pro_layout_monthly_price : plan.pro_layout_yearly_price @@ -232,13 +184,59 @@ var EditSubscriptionModal = ModalView.extend({ this.$planTotal.toDollars ( totals.plan_total ) }, - addLayouts: function(e){ - e.preventDefault() + resetMode: function(e){ + e && e.preventDefault() + this.$paidPlan.removeClass("editLayouts editPlan") + this.reset() }, - changePlan: function(e){ - e.preventDefault() + + layoutsMode: function(e){ + e && e.preventDefault() + this.$paidPlan.addClass("editLayouts") + this.tempSubscriber = defaults({}, this.subscriber) + this.$basicLayoutInput.val( this.subscriber.basic_layouts ) + this.$proLayoutInput.val( this.subscriber.pro_layouts ) + this.$basicLayoutRow.show() + this.$proLayoutRow.toggle(this.subscriber.plan_name == "pro") + }, + updateQuantity: function(e){ + e && e.preventDefault() + var plan = this.getPlan( this.tempSubscriber.plan_type ) + this.tempSubscriber.basic_layouts = this.$basicLayoutInput.int() + this.tempSubscriber.pro_layouts = this.$proLayoutInput.int() + this.displayTotals(this.tempSubscriber, plan) + this.$basicLayoutRow.show() + this.$proLayoutRow.toggle(plan.name == "pro") + }, + saveQuantity: function(e){ + e && e.preventDefault() + this.subscriber = this.tempSubscriber + this.resetMode() + // blabla.. save }, + planMode: function(e){ + e && e.preventDefault() + this.$paidPlan.addClass("editPlan") + switch (this.subscriber.plan_type) { + case 'basic': this.$basicPlanInput.prop('checked', true); break; + case 'pro': this.$proPlanInput.prop('checked', true); break; + } + this.tempSubscriber = defaults({}, this.subscriber) + }, + updatePlan: function(e){ + e && e.preventDefault() + var plan_type = this.$("[name=planRadio]:checked").val() + var plan = this.getPlan( plan_type ) + this.displayTotals(this.tempSubscriber, plan) + }, + savePlan: function(e){ + e && e.preventDefault() + this.subscriber.plan_type = this.$("[name=planRadio]:checked").val() + this.subscriber = this.tempSubscriber + this.resetMode() + // blabla.. save + }, sync: function(){ this.$gear.addClass("turning") $.ajax({ diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 19f6851..e651187 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -54,12 +54,12 @@

    Select desired plan:

    - + $/
    - + $/
    -- cgit v1.2.3-70-g09d2 From 003f59d687d8d3d5e4e9c4a2eeb1bb167753d0c9 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 18:22:28 -0500 Subject: check form input --- .../javascripts/ui/site/EditSubscriptionModal.js | 18 +++++++++++------- views/partials/edit-subscription.ejs | 6 +++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index b9abd75..384460d 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -27,7 +27,9 @@ var EditSubscriptionModal = ModalView.extend({ "click [data-role=saveQuantity]": "saveQuantity", "change [name=planRadio]": "updatePlan", - "click [data-role=changePlan]": "savePlan", + "click [data-role=savePlan]": "savePlan", + + "submit form": "preventDefault", }, initialize: function(){ @@ -148,7 +150,7 @@ var EditSubscriptionModal = ModalView.extend({ }, calculateTotals: function(subscriber, plan){ var t = {} - t.is_pro = plan.name == "pro" + t.is_pro = subscriber.plan_type == "pro" t.is_monthly = subscriber.plan_period == "monthly" t.plan_price = t.is_monthly ? plan.monthly_price : plan.yearly_price t.basic_layout_price = t.is_monthly ? plan.basic_layout_monthly_price : plan.basic_layout_yearly_price @@ -197,16 +199,19 @@ var EditSubscriptionModal = ModalView.extend({ this.$basicLayoutInput.val( this.subscriber.basic_layouts ) this.$proLayoutInput.val( this.subscriber.pro_layouts ) this.$basicLayoutRow.show() - this.$proLayoutRow.toggle(this.subscriber.plan_name == "pro") + this.$proLayoutRow.toggle(this.subscriber.plan_type == "pro") }, updateQuantity: function(e){ e && e.preventDefault() var plan = this.getPlan( this.tempSubscriber.plan_type ) - this.tempSubscriber.basic_layouts = this.$basicLayoutInput.int() - this.tempSubscriber.pro_layouts = this.$proLayoutInput.int() + this.tempSubscriber.basic_layouts = clamp( this.$basicLayoutInput.int() || 0, 0, 100) + this.tempSubscriber.pro_layouts = clamp( this.$proLayoutInput.int() || 0, 0, 100) + + this.$basicLayoutInput.val(this.tempSubscriber.basic_layouts) + this.$proLayoutInput.val(this.tempSubscriber.pro_layouts) this.displayTotals(this.tempSubscriber, plan) this.$basicLayoutRow.show() - this.$proLayoutRow.toggle(plan.name == "pro") + this.$proLayoutRow.toggle(this.tempSubscriber.plan_type == "pro") }, saveQuantity: function(e){ e && e.preventDefault() @@ -233,7 +238,6 @@ var EditSubscriptionModal = ModalView.extend({ savePlan: function(e){ e && e.preventDefault() this.subscriber.plan_type = this.$("[name=planRadio]:checked").val() - this.subscriber = this.tempSubscriber this.resetMode() // blabla.. save }, diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index e651187..1b0fc41 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -32,7 +32,7 @@ Additional Pro layouts - @ $ ... + each $/ $/ @@ -43,12 +43,12 @@
    -
    +

    - +
    -- cgit v1.2.3-70-g09d2 From 3f0ef2173b6d00ae8130b3125b63f3d9ad9e98de Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 18:48:08 -0500 Subject: make it 1 action --- .../javascripts/ui/site/EditSubscriptionModal.js | 57 ++++++++++------------ public/assets/stylesheets/app.css | 14 +++--- views/partials/edit-subscription.ejs | 43 ++++++++-------- 3 files changed, 56 insertions(+), 58 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 384460d..6e5dc9e 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -7,6 +7,7 @@ var EditSubscriptionModal = ModalView.extend({ destroyAction: "/api/subscription/destroy", fixedClose: true, + editing: false, subscriber: null, tempSubscriber: null, @@ -17,14 +18,13 @@ var EditSubscriptionModal = ModalView.extend({ "click .gear": 'sync', "click .planList button": 'followLink', - "click [data-role=showLayoutsMenu]": "layoutsMode", - "click [data-role=showPlanMenu]": "planMode", + "click [data-role=showEditMenu]": "editMode", "click [data-role=closeMenu]": "resetMode", "input [data-role=basicLayoutInput]": "updateQuantity", "input [data-role=proLayoutInput]": "updateQuantity", - "click [data-role=saveQuantity]": "saveQuantity", + "click [data-role=saveChanges]": "saveChanges", "change [name=planRadio]": "updatePlan", "click [data-role=savePlan]": "savePlan", @@ -186,21 +186,28 @@ var EditSubscriptionModal = ModalView.extend({ this.$planTotal.toDollars ( totals.plan_total ) }, - resetMode: function(e){ - e && e.preventDefault() - this.$paidPlan.removeClass("editLayouts editPlan") - this.reset() - }, - - layoutsMode: function(e){ + editMode: function(e){ e && e.preventDefault() - this.$paidPlan.addClass("editLayouts") + + this.editing = true + this.$el.addClass("editing") this.tempSubscriber = defaults({}, this.subscriber) this.$basicLayoutInput.val( this.subscriber.basic_layouts ) this.$proLayoutInput.val( this.subscriber.pro_layouts ) this.$basicLayoutRow.show() this.$proLayoutRow.toggle(this.subscriber.plan_type == "pro") + switch (this.subscriber.plan_type) { + case 'basic': this.$basicPlanInput.prop('checked', true); break; + case 'pro': this.$proPlanInput.prop('checked', true); break; + } }, + resetMode: function(e){ + e && e.preventDefault() + this.editing = false + this.$el.removeClass("editing") + this.reset() + }, + updateQuantity: function(e){ e && e.preventDefault() var plan = this.getPlan( this.tempSubscriber.plan_type ) @@ -213,34 +220,24 @@ var EditSubscriptionModal = ModalView.extend({ this.$basicLayoutRow.show() this.$proLayoutRow.toggle(this.tempSubscriber.plan_type == "pro") }, - saveQuantity: function(e){ + saveChanges: function(e){ e && e.preventDefault() + if (this.tempSubscriber.plan_type != this.subscriber.plan_type + || this.tempSubscriber.basic_layouts != this.subscriber.basic_layouts + || this.tempSubscriber.pro_layouts != this.subscriber.pro_layouts) { + // update plan_type + } this.subscriber = this.tempSubscriber this.resetMode() // blabla.. save }, - planMode: function(e){ - e && e.preventDefault() - this.$paidPlan.addClass("editPlan") - switch (this.subscriber.plan_type) { - case 'basic': this.$basicPlanInput.prop('checked', true); break; - case 'pro': this.$proPlanInput.prop('checked', true); break; - } - this.tempSubscriber = defaults({}, this.subscriber) - }, updatePlan: function(e){ e && e.preventDefault() - var plan_type = this.$("[name=planRadio]:checked").val() - var plan = this.getPlan( plan_type ) - this.displayTotals(this.tempSubscriber, plan) - }, - savePlan: function(e){ - e && e.preventDefault() - this.subscriber.plan_type = this.$("[name=planRadio]:checked").val() - this.resetMode() - // blabla.. save + this.tempSubscriber.plan_type = this.$("[name=planRadio]:checked").val() + this.updateQuantity() }, + sync: function(){ this.$gear.addClass("turning") $.ajax({ diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index b58bb01..ffb133d 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1071,12 +1071,14 @@ form .paidPlan label { float: none; font-size: 2vw; margin: 0 10px; } .paidPlan .upgradeMenu div { padding: 5px 10px; } .paidPlan .layoutsMenu { display: none } .paidPlan .planMenu { display: none } -.paidPlan.editLayouts .layoutsMenu { display: block } -.paidPlan.editLayouts .billingMenu { display: none } -.paidPlan.editLayouts input[type=number] { display: inline-block } -.paidPlan.editLayouts input[type=number]+span { display: none } -.paidPlan.editPlan .planMenu { display: block } -.paidPlan.editPlan .billingMenu { display: none } + +.editing .section_break { display: none } +.editing .paidPlan .currentPlanLevel { display: none } +.editing .paidPlan .billingMenu { display: none } +.editing .paidPlan .layoutsMenu { display: block } +.editing .paidPlan .planMenu { display: block } +.editing .paidPlan input[type=number] { display: inline-block } +.editing .paidPlan input[type=number]+span { display: none } /* LAYOUTS MODAL */ diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 1b0fc41..38c6dfc 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -14,8 +14,23 @@



    -
  • - Your current plan level is +
  • +
    + Your current plan level is +
    +
    +

    Select desired plan:

    +
    + + + $/ +
    +
    + + + $/ +
    +
    @@ -24,13 +39,13 @@ - + - + @@ -43,27 +58,11 @@
    $/
    Additional Basic layoutsBasic layouts each $/ $/
    Additional Pro layoutsPro layouts each $/ $/
    -
    -
    +
    - - -
    -
    -

    Select desired plan:

    -
    - - - $/ -
    -
    - - - $/ -
    - +
  • -- cgit v1.2.3-70-g09d2 From d45c7f3c6e5a05a263f4a58dea83f2d578013f12 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 18:49:29 -0500 Subject: save changes on profile --- views/partials/edit-profile.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/partials/edit-profile.ejs b/views/partials/edit-profile.ejs index 5775ca5..5720bc8 100644 --- a/views/partials/edit-profile.ejs +++ b/views/partials/edit-profile.ejs @@ -83,7 +83,7 @@
  • - +
  • There was a problem with your submission:
    -- cgit v1.2.3-70-g09d2 From 9e40939c50cf098e88f7d66f85bd925fcc9d01e4 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 18:52:32 -0500 Subject: space for blabitybla text --- public/assets/javascripts/ui/site/EditSubscriptionModal.js | 8 +++++--- public/assets/stylesheets/app.css | 5 +++-- views/partials/edit-subscription.ejs | 7 ++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 6e5dc9e..cd3d1bb 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -71,12 +71,11 @@ var EditSubscriptionModal = ModalView.extend({ this.$proLayoutTotal = this.$("[data-role=proLayoutTotal]") // menus.. main menu - this.$showLayoutsMenu = this.$("[data-role=showLayoutsMenu]") - this.$showPlanMenu = this.$("[data-role=showPlanMenu]") + this.$showEditMenu = this.$("[data-role=showEditMenu]") this.$cancelSubscription = this.$("[data-role=cancelSubscription]") // three submenus - this.$layoutsMenu = this.$("[data-role=layoutsMenu]") + this.$editMenu = this.$("[data-role=editMenu]") this.$planMenu = this.$("[data-role=planMenu]") this.$buyLayouts = this.$("[data-role=buyLayouts]") @@ -270,6 +269,9 @@ var EditSubscriptionModal = ModalView.extend({ this.didLoad(data) }.bind(this) }) + }.bind(this), + function(){ + this.show() }.bind(this)) }, diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index ffb133d..7c49ed1 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1069,17 +1069,18 @@ iframe.embed { form .paidPlan div { float: none; } form .paidPlan label { float: none; font-size: 2vw; margin: 0 10px; } .paidPlan .upgradeMenu div { padding: 5px 10px; } -.paidPlan .layoutsMenu { display: none } +.paidPlan .editMenu { display: none } .paidPlan .planMenu { display: none } .editing .section_break { display: none } .editing .paidPlan .currentPlanLevel { display: none } .editing .paidPlan .billingMenu { display: none } -.editing .paidPlan .layoutsMenu { display: block } +.editing .paidPlan .editMenu { display: block } .editing .paidPlan .planMenu { display: block } .editing .paidPlan input[type=number] { display: inline-block } .editing .paidPlan input[type=number]+span { display: none } +.editMenu .fineprint { font-size: 12px; width: auto; text-align: justify; } /* LAYOUTS MODAL */ .templates { diff --git a/views/partials/edit-subscription.ejs b/views/partials/edit-subscription.ejs index 38c6dfc..f3c3589 100644 --- a/views/partials/edit-subscription.ejs +++ b/views/partials/edit-subscription.ejs @@ -61,7 +61,12 @@
    -
    +
    +
    + Your credit card will be charged the difference of XXXX for this month. + Your subscription will renew at XXXX at the start of your next billing cycle. + We thank you for your support! +
    -- cgit v1.2.3-70-g09d2 From 7a23f57b1ef3e9ceca414d909aa15a7d5ea6d536 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 20:46:42 -0500 Subject: hitting update api --- .../javascripts/ui/site/EditSubscriptionModal.js | 25 ++++++++++++++++------ server/lib/api/subscription.js | 20 ++++++++--------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index cd3d1bb..3bc1a24 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -221,14 +221,21 @@ var EditSubscriptionModal = ModalView.extend({ }, saveChanges: function(e){ e && e.preventDefault() - if (this.tempSubscriber.plan_type != this.subscriber.plan_type - || this.tempSubscriber.basic_layouts != this.subscriber.basic_layouts - || this.tempSubscriber.pro_layouts != this.subscriber.pro_layouts) { - // update plan_type + var is_changed = false + var diff = {} + "plan_type basic_layouts pro_layouts".split(" ").forEach(function(field){ + if (this.tempSubscriber[field] != this.subscriber[field]) { + diff[field] = this.tempSubscriber[field] + is_changed = true + } + }.bind(this)) + + if (is_changed) { + diff.plan_type = this.tempSubscriber.plan_type + this.update(diff) } this.subscriber = this.tempSubscriber this.resetMode() - // blabla.. save }, updatePlan: function(e){ @@ -248,12 +255,16 @@ var EditSubscriptionModal = ModalView.extend({ }, update: function(data){ + data['_csrf'] = $("[name=_csrf]").val() + this.$gear.addClass("turning") $.ajax({ url: this.updateAction, type: "put", - data: { _csrf: $("[name=_csrf]").val() }, + data: data, success: function(data){ - } + console.log("SUCCESS") + this.$gear.removeClass("turning") + }.bind(this) }) }, diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 6f08e66..362d633 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -138,26 +138,26 @@ var subscription = module.exports = { } var subscriber = req.subscription - // change.. - // data.plan_code - // data.subscription_add_ons = [] - // add_on.add_on_code - // add_on.quantity - var basic_layouts = max(0, parseInt(req.body.basic_layouts)) - var pro_layouts = max(0, parseInt(req.body.pro_layouts)) + var plan_type = req.body.plan_type + var basic_layouts = Math.max(0, parseInt(req.body.basic_layouts, 10)) + var pro_layouts = Math.max(0, parseInt(req.body.pro_layouts, 10)) var data = {} - data.plan_code = req.body.plan_type + "_monthly" + data.plan_code = plan_type + "_monthly" data.subscription_add_ons = [] - if (plan_levels[req.body.plan_type]) { + if (plan_levels[plan_type] > 0) { data.subscription_add_ons.push({ add_on_code: "extra-basic-layout", quantity: basic_layouts }) } - if (req.body.plan_type == "pro") { + if (plan_type == "pro") { data.subscription_add_ons.push({ add_on_code: "extra-pro-layout", quantity: pro_layouts }) } + // data.plan_code + // data.subscription_add_ons = [] + // add_on.add_on_code + // add_on.quantity recurly.subscriptions.update(subscriber.uuid, data, function(){ return res.json(subscriber) }) -- cgit v1.2.3-70-g09d2 From a5c5fa85c85fc31bebae1917ceeac4f8e1e383d5 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 1 Feb 2015 22:02:52 -0500 Subject: talking to recurly --- package.json | 2 +- .../javascripts/ui/site/EditSubscriptionModal.js | 1 - server/lib/api/subscription.js | 32 ++++++++++++++++++---- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index e89fcd9..56ffcdf 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "mongoose-unique-validator": "~0.3.0", "monk": "~0.7.1", "multer": "~0.1.0", - "node-recurly": "^2.1.0", + "node-recurly": "julescarbon/node-recurly", "node-restful": "~0.1.14", "passport": "~0.2.0", "passport-facebook": "~1.0.3", diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 3bc1a24..55ff3b5 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -262,7 +262,6 @@ var EditSubscriptionModal = ModalView.extend({ type: "put", data: data, success: function(data){ - console.log("SUCCESS") this.$gear.removeClass("turning") }.bind(this) }) diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 362d633..7e9221a 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -137,13 +137,20 @@ var subscription = module.exports = { return res.json({ error: "bad input" }) } var subscriber = req.subscription + var user = req.user var plan_type = req.body.plan_type - var basic_layouts = Math.max(0, parseInt(req.body.basic_layouts, 10)) - var pro_layouts = Math.max(0, parseInt(req.body.pro_layouts, 10)) + var basic_layouts = Math.max(0, parseInt(req.body.basic_layouts || 0, 10)) + var pro_layouts = Math.max(0, parseInt(req.body.pro_layouts || 0, 10)) + + if (plan_type == subscription.plan_type + && basic_layouts == subscriber.basic_layouts + && pro_layouts == subscriber.pro_layouts) { + return res.json(subscriber) + } var data = {} - data.plan_code = plan_type + "_monthly" + data.plan_code = plan_type + "-monthly" data.subscription_add_ons = [] if (plan_levels[plan_type] > 0) { @@ -158,8 +165,21 @@ var subscription = module.exports = { // data.subscription_add_ons = [] // add_on.add_on_code // add_on.quantity - recurly.subscriptions.update(subscriber.uuid, data, function(){ - return res.json(subscriber) + recurly.subscriptions.update(subscriber.uuid, data, function(err, data){ + if (err) { + console.log("error updating recurly subscription", err) + return res.json({ error: err }) + } + subscriber.plan_type = plan_type + subscriber.basic_layouts = basic_layouts + subscriber.pro_layouts = pro_layouts + subscriber.save(function(){ + user.plan_level = plan_levels[plan_type] + user.plan_type = plan_type + user.save(function(){ + return res.json(subscriber) + }) + }) }) }, @@ -169,7 +189,7 @@ var subscription = module.exports = { } var subscriber = req.subscription - recurly.subscriptions.terminate(subscriber.uuid, "partial", function(){ + recurly.subscriptions.terminate(subscriber.uuid, "partial", function(err, data){ subscriber.remove(function(){ req.user.plan_code = 0 req.user.plan_type = "free" -- cgit v1.2.3-70-g09d2 From e7ecd5b141945a9c9ca7a57df643eaa3f3fdc3d6 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 09:43:06 -0500 Subject: double checking all plan changes.. need to pass full plan info to update --- public/assets/javascripts/ui/site/EditSubscriptionModal.js | 3 +-- server/lib/api/subscription.js | 10 ++++++---- views/staff/_nav.ejs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 55ff3b5..c276354 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -224,14 +224,13 @@ var EditSubscriptionModal = ModalView.extend({ var is_changed = false var diff = {} "plan_type basic_layouts pro_layouts".split(" ").forEach(function(field){ + diff[field] = this.tempSubscriber[field] if (this.tempSubscriber[field] != this.subscriber[field]) { - diff[field] = this.tempSubscriber[field] is_changed = true } }.bind(this)) if (is_changed) { - diff.plan_type = this.tempSubscriber.plan_type this.update(diff) } this.subscriber = this.tempSubscriber diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 7e9221a..9c2d6ef 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -142,6 +142,7 @@ var subscription = module.exports = { var plan_type = req.body.plan_type var basic_layouts = Math.max(0, parseInt(req.body.basic_layouts || 0, 10)) var pro_layouts = Math.max(0, parseInt(req.body.pro_layouts || 0, 10)) + if (plan_type != "pro") { pro_layouts = 0 } if (plan_type == subscription.plan_type && basic_layouts == subscriber.basic_layouts @@ -149,15 +150,14 @@ var subscription = module.exports = { return res.json(subscriber) } - var data = {} + var data = { subscription_add_ons: [] } data.plan_code = plan_type + "-monthly" - data.subscription_add_ons = [] - if (plan_levels[plan_type] > 0) { + if (plan_levels[plan_type] > 0 && basic_layouts > 0) { data.subscription_add_ons.push({ add_on_code: "extra-basic-layout", quantity: basic_layouts }) } - if (plan_type == "pro") { + if (plan_type == "pro" && pro_layouts > 0) { data.subscription_add_ons.push({ add_on_code: "extra-pro-layout", quantity: pro_layouts }) } @@ -165,7 +165,9 @@ var subscription = module.exports = { // data.subscription_add_ons = [] // add_on.add_on_code // add_on.quantity + console.log(data) recurly.subscriptions.update(subscriber.uuid, data, function(err, data){ + console.log("got response from RECURLY ...") if (err) { console.log("error updating recurly subscription", err) return res.json({ error: err }) diff --git a/views/staff/_nav.ejs b/views/staff/_nav.ejs index e79ff69..a607638 100644 --- a/views/staff/_nav.ejs +++ b/views/staff/_nav.ejs @@ -4,5 +4,5 @@ projects media plans - subscriptions + \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 95b7043c7bf259da3135652461f80162be432691 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 10:43:46 -0500 Subject: spacing --- config.json.example | 12 ++++++------ public/assets/stylesheets/app.css | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config.json.example b/config.json.example index dc79edd..7072d48 100644 --- a/config.json.example +++ b/config.json.example @@ -1,9 +1,9 @@ { - "host": "lvh.me:3000", - "hostName": "lvh.me", - "port": 3000, - "socketPort": 1337, - "webhookPort": 5000, + "host": "lvh.me:3000", + "hostName": "lvh.me", + "port": 3000, + "socketPort": 1337, + "webhookPort": 5000, "databaseHost": "lvh.me", - "env": { "development": 1 } + "env": { "development": 1 } } diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 7c49ed1..0a7e24a 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1067,7 +1067,7 @@ iframe.embed { /* purchase mode */ .paidPlan input[type=number] { display: none; float: right; } form .paidPlan div { float: none; } -form .paidPlan label { float: none; font-size: 2vw; margin: 0 10px; } +form .paidPlan label { float: none; font-size: 16px; margin: 0 10px; } .paidPlan .upgradeMenu div { padding: 5px 10px; } .paidPlan .editMenu { display: none } .paidPlan .planMenu { display: none } -- cgit v1.2.3-70-g09d2 From 0cb3f8b480e46a3376b5accba18cde0c3cbdd13d Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 10:53:28 -0500 Subject: bower moved lodash --- Gruntfile.js | 2 +- views/partials/scripts.ejs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index ed236c5..f7af106 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,7 +9,7 @@ module.exports = function(grunt) { js: { src: [ "public/assets/javascripts/vendor/bower_components/jquery/dist/jquery.min.js", - "public/assets/javascripts/vendor/bower_components/lodash/dist/lodash.min.js", + "public/assets/javascripts/vendor/bower_components/lodash/lodash.min.js", "public/assets/javascripts/vendor/bower_components/momentjs/min/moment.min.js", "public/assets/javascripts/vendor/bower_components/fiber/src/fiber.min.js", "public/assets/javascripts/vendor/bower_components/marked/lib/marked.js", diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index 04bd945..a58eca1 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -2,7 +2,7 @@ [[ } else { ]] - + -- cgit v1.2.3-70-g09d2 From b9ad0704417aaa8cf4da7a1ec2109959622bd454 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 11:31:31 -0500 Subject: setting up staging env --- server/lib/views/staff.js | 3 ++- views/staff/plans/_form.ejs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index 67193fe..c3ecc97 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -728,8 +728,9 @@ var staff = module.exports = { data.name = util.sanitize(data.name) data.slug = util.sanitize(data.slug.toLowerCase()) + data.permissions = {} permissions.forEach(function(field){ - data[field] = data["permissions_" + field] + data.permissions[field] = data["permissions_" + field].length == 2 }) new Plan (data).save(function(err, doc){ diff --git a/views/staff/plans/_form.ejs b/views/staff/plans/_form.ejs index 0240e56..61be7e8 100644 --- a/views/staff/plans/_form.ejs +++ b/views/staff/plans/_form.ejs @@ -100,7 +100,7 @@
  • - +
  • -- cgit v1.2.3-70-g09d2 From 03842d65cc018f9a718b2408d19e978f3d08e042 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 13:23:51 -0500 Subject: sanity --- server/lib/views/index.js | 2 +- server/lib/webhook/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/lib/views/index.js b/server/lib/views/index.js index 9f244c8..be46cc6 100644 --- a/server/lib/views/index.js +++ b/server/lib/views/index.js @@ -107,7 +107,7 @@ var views = module.exports = { partials: { plans: function (req, res){ views_middleware.ensurePlans(req, res, function(){ - res.render('about/_plans') + res.render('about/_plans', { logged_in: res.locals.logged_in || false }) }) }, }, diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js index 11419c2..798e4be 100644 --- a/server/lib/webhook/index.js +++ b/server/lib/webhook/index.js @@ -37,7 +37,7 @@ site.ready = function(){ console.log('Webhook server listening on port ' + app.get('port')); }); - app.get('/', function(req,res){ res.send('HI THERE') }) + app.get('/', function(req,res){ res.send('hello@vvalls.com') }) webhook.route(app) } -- cgit v1.2.3-70-g09d2 From 466e56bbdcf1b713a91e77081237b3adf0b86ba0 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 13:24:03 -0500 Subject: using xml middleware parser for webhook --- package.json | 1 + server/lib/webhook/index.js | 2 +- server/lib/webhook/webhook.js | 191 +++++++++++++++++++++--------------------- 3 files changed, 99 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index 56ffcdf..7a504af 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "express": "~3.4.8", "express-subdomain-handler": "~0.1.0", "express-subdomains": "0.0.5", + "express-xml-bodyparser": "0.0.6", "html-entities": "~1.0.10", "intro.js": "^0.9.0", "js2xml": "^1.0.0", diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js index 11419c2..eb6a6b3 100644 --- a/server/lib/webhook/index.js +++ b/server/lib/webhook/index.js @@ -27,7 +27,7 @@ site.init = function(){ site.ready = function(){ app.set('port', config.webhookPort); - app.use(bodyParser()); + // app.use(bodyParser()); app.use(express.query()); app.set('env', config.env.production ? "production" : "development") app.get('env') === 'development' && app.use(express.errorHandler()); diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 4f23d0b..21ff23d 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -12,15 +12,17 @@ app.use(express.basicAuth(function(user, pass, callback) { /* jshint node: true */ var User = require('../schemas/User'), - Subscription = require('../schemas/Subscription'), - config = require('../../../config'), - middleware = require('../middleware'), - util = require('../util'), - _ = require('lodash'), - moment = require('moment'), - xml2js = require('xml2js'), - Recurly = require('node-recurly'), - recurly = new Recurly(require('./recurly-config')); + Subscription = require('../schemas/Subscription'), + config = require('../../../config'), + middleware = require('../middleware'), + util = require('../util'), + _ = require('lodash'), + moment = require('moment'), + xml2js = require('xml2js'), + Recurly = require('node-recurly'), + recurly = new Recurly(require('./recurly-config')); + +var xml_bodyparser = require('express-xml-bodyparser'); var parser = new xml2js.Parser(); @@ -30,34 +32,34 @@ var subscribe = module.exports = { basic: 1, pro: 2, }, - + callbacks: { /* - // accounts - new_account_notification: function(data, user){ - // fires on successful signup - }, - canceled_account_notification: function(data, user){ - }, - billing_info_updated_notification: function(data, user){ - }, - reactivated_account_notification: function(data, user){ - }, - - // invoices - new_invoice_notification: function(data, user){ - }, - closed_invoice_notification: function(data, user){ - }, - past_due_invoice_notification: function(data, user){ - }, + // accounts + new_account_notification: function(data, user){ + // fires on successful signup + }, + canceled_account_notification: function(data, user){ + }, + billing_info_updated_notification: function(data, user){ + }, + reactivated_account_notification: function(data, user){ + }, + + // invoices + new_invoice_notification: function(data, user){ + }, + closed_invoice_notification: function(data, user){ + }, + past_due_invoice_notification: function(data, user){ + }, */ - // subscriptions - new_subscription_notification: function(data, user){ - var account = data.account - Subscription.findOne({ "uuid": data.subscription.uuid }, function(err, subscription){ - if (err || subscription) return; + // subscriptions + new_subscription_notification: function(data, user){ + var account = data.account + Subscription.findOne({ "uuid": data.subscription.uuid }, function(err, subscription){ + if (err || subscription) return; var plan = data.subscription.plan.plan_code.split("-") var plan_type = plan[0] @@ -66,75 +68,76 @@ var subscribe = module.exports = { user.plan_type = plan_type user.plan_level = subscribe.plan_level[plan_type] - var subscriber = new Subscription () + var subscriber = new Subscription () subscriber.uuid = data.subscription.uuid - subscriber.user_id = user._id + subscriber.user_id = user._id subscriber.plan_type = plan_type subscriber.plan_period = plan_period subscriber.plan_level = subscribe.plan_level[plan_type] subscriber.add_ons = subscription.add_ons.map(function(add_on){ - return { - name: add_on.plan, - quantity: add_on.quantity, - } + return { + name: add_on.plan, + quantity: add_on.quantity, + } }) subscriber.save(function(err, data){ - if (err) return; - user.save(function(err){ - // saved! - }) + if (err) return; + user.save(function(err){ + // saved! + }) }) - }) - }, + }) + }, /* - updated_subscription_notification: function(data, user){ - }, - canceled_subscription_notification: function(data, user){ - }, - expired_subscription_notification: function(data, user){ - }, - renewed_subscription_notification: function(data, user){ - }, -*/ - // payments - successful_payment_notification: function(data, user){ - var account = data.account - user.last_charged = new Date(data.transaction.date) - user.save(function(){ - }) - }, + updated_subscription_notification: function(data, user){ + }, + canceled_subscription_notification: function(data, user){ + }, + expired_subscription_notification: function(data, user){ + }, + renewed_subscription_notification: function(data, user){ + }, +*/ + // payments + successful_payment_notification: function(data, user){ + var account = data.account + user.last_charged = new Date(data.transaction.date) + user.save(function(){ + }) + }, /* - failed_payment_notification: function(data, user){ - }, - successful_refund_notification: function(data, user){ - }, - void_payment_notification: function(data, user){ - }, + failed_payment_notification: function(data, user){ + }, + successful_refund_notification: function(data, user){ + }, + void_payment_notification: function(data, user){ + }, */ }, - - execute: function(action, data){ - User.findOne({ _id: data.account.account_code }, function(err, user){ - if (err) { return } - subscribe.callbacks[action](data, user) - }) - }, - + + execute: function(action, data){ + User.findOne({ _id: data.account.account_code }, function(err, user){ + if (err) { return } + subscribe.callbacks[action](data, user) + }) + }, + // then calls to get appropriate info from the recurly api - handle: function(req, res){ - res.status(200).end() - parser.parseString(data, function (err, result) { - console.log(inspect(result, { colors: true, depth: Infinity })); - for (var action in result) { - if (subscribe.callbacks[action]) { - subscribe.execute(action, result[action]); - } - } - }); - }, - - list: function(req, res){ + handle: function(req, res){ + console.log(req.body) + // parser.parseString(req.body, function (err, result) { + var result = req.body + console.log(inspect(result, { colors: true, depth: Infinity })); + for (var action in result) { + if (subscribe.callbacks[action]) { + subscribe.execute(action, result[action]); + return res.status(200).end() + } + } + }, + + list: function(req, res){ recurly.subscriptions.listByAccount(req.params.id, function(data){ if (data.data != 404) { res.json(data) @@ -145,10 +148,10 @@ var subscribe = module.exports = { return } }) - }, - - route: function(app){ - app.post('/subscribe/webhook', subscribe.handle); - app.get('/subscribe/list/:id', subscribe.list); - }, + }, + + route: function(app){ + app.post('/subscribe/webhook', xml_bodyparser(), subscribe.handle); + app.get('/subscribe/list/:id', subscribe.list); + }, } -- cgit v1.2.3-70-g09d2 From cd6b76e4665f37566f08075479d501628f30eb0b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 13:25:16 -0500 Subject: fix template --- views/about/_plans.ejs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views/about/_plans.ejs b/views/about/_plans.ejs index abd6aaa..16fb9c6 100644 --- a/views/about/_plans.ejs +++ b/views/about/_plans.ejs @@ -19,7 +19,7 @@ [[ } else if (! user.plan_level) { ]] - [[ } else if (user.plan_level == plan.level) { ]] + [[ } else if (user.plan_level == plans.basic.level) { ]] Current Level [[ } ]] @@ -40,9 +40,9 @@ [[ } else if (! user.plan_level) { ]] - [[ } else if (user.plan_level == plan.level) { ]] + [[ } else if (user.plan_level == plans.pro.level) { ]] Current Level - [[ } else if (user.plan_level < plan.level) { ]] + [[ } else if (user.plan_level < plans.pro.level) { ]] [[ } ]] -- cgit v1.2.3-70-g09d2 From 19b74bf938aef3b0c3a35c185d9d1563e773a972 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 13:50:09 -0500 Subject: webhook firing --- package.json | 1 + server/lib/webhook/webhook.js | 35 ++++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 7a504af..69766ce 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "express-subdomains": "0.0.5", "express-xml-bodyparser": "0.0.6", "html-entities": "~1.0.10", + "inspect": "0.0.2", "intro.js": "^0.9.0", "js2xml": "^1.0.0", "knox": "~0.8.10", diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 21ff23d..25e3fb8 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -57,11 +57,13 @@ var subscribe = module.exports = { // subscriptions new_subscription_notification: function(data, user){ - var account = data.account - Subscription.findOne({ "uuid": data.subscription.uuid }, function(err, subscription){ + var account = data.account[0].account_code[0] + var subscrip = data.subscription[0] + var uuid = subscrip.uuid[0] + Subscription.findOne({ "uuid": uuid }, function(err, subscription){ if (err || subscription) return; - var plan = data.subscription.plan.plan_code.split("-") + var plan = subscrip.plan[0].plan_code[0].split("-") var plan_type = plan[0] var plan_period = plan[1] @@ -69,17 +71,25 @@ var subscribe = module.exports = { user.plan_level = subscribe.plan_level[plan_type] var subscriber = new Subscription () - subscriber.uuid = data.subscription.uuid + subscriber.uuid = uuid subscriber.user_id = user._id subscriber.plan_type = plan_type subscriber.plan_period = plan_period subscriber.plan_level = subscribe.plan_level[plan_type] - subscriber.add_ons = subscription.add_ons.map(function(add_on){ - return { - name: add_on.plan, - quantity: add_on.quantity, - } - }) + subscriber.add_ons = [] + var add_ons = subscrip.subscription_add_ons[0].subscription_add_on + if (add_ons) { + add_ons.forEach(function(add_on){ + switch (add_on.add_on_code[0]) { + case 'extra-basic-layout': + subscriber.basic_layouts = parseInt(add_on.quantity[0]._, 10) || 0 + break + case 'extra-pro-layout': + subscriber.pro_layouts = parseInt(add_on.quantity[0]._, 10) || 0 + break + } + }) + } subscriber.save(function(err, data){ if (err) return; user.save(function(err){ @@ -117,7 +127,7 @@ var subscribe = module.exports = { }, execute: function(action, data){ - User.findOne({ _id: data.account.account_code }, function(err, user){ + User.findOne({ _id: data.account[0].account_code[0] }, function(err, user){ if (err) { return } subscribe.callbacks[action](data, user) }) @@ -128,13 +138,12 @@ var subscribe = module.exports = { console.log(req.body) // parser.parseString(req.body, function (err, result) { var result = req.body - console.log(inspect(result, { colors: true, depth: Infinity })); for (var action in result) { if (subscribe.callbacks[action]) { subscribe.execute(action, result[action]); - return res.status(200).end() } } + return res.status(200).end() }, list: function(req, res){ -- cgit v1.2.3-70-g09d2 From 4b6ba728390f3c16c48392beef5cb0b58cbf1763 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 13:57:13 -0500 Subject: null sub --- public/assets/javascripts/ui/site/EditSubscriptionModal.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index c276354..3a20234 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -275,6 +275,7 @@ var EditSubscriptionModal = ModalView.extend({ type: "delete", data: { _csrf: $("[name=_csrf]").val() }, success: function(data){ + this.subscriber = null this.didLoad(data) }.bind(this) }) -- cgit v1.2.3-70-g09d2 From 07f648798676d565914dd8e7b1cd78ebfc3b7309 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 13:57:49 -0500 Subject: dont make a million subscriber --- server/lib/webhook/webhook.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 25e3fb8..9e3a4d3 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -60,8 +60,8 @@ var subscribe = module.exports = { var account = data.account[0].account_code[0] var subscrip = data.subscription[0] var uuid = subscrip.uuid[0] - Subscription.findOne({ "uuid": uuid }, function(err, subscription){ - if (err || subscription) return; + Subscription.findOne({ "uuid": uuid }, function(err, old_subscriber){ + // if (err) return; var plan = subscrip.plan[0].plan_code[0].split("-") var plan_type = plan[0] @@ -70,7 +70,7 @@ var subscribe = module.exports = { user.plan_type = plan_type user.plan_level = subscribe.plan_level[plan_type] - var subscriber = new Subscription () + var subscriber = old_subscriber || new Subscription () subscriber.uuid = uuid subscriber.user_id = user._id subscriber.plan_type = plan_type -- cgit v1.2.3-70-g09d2 From 6a4e4d30d5428fd0e629081d6a214e9a618ab52f Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 14:05:24 -0500 Subject: css a bit --- public/assets/stylesheets/app.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 0a7e24a..b23b50b 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -942,6 +942,9 @@ iframe.embed { list-style-type: none; margin-bottom: 0; font-size: 1.3vw; + padding: 0 1vw; +} +.paidPlan .planbox li { padding: 0 5.6vw; } .about_custom.planbox li { -- cgit v1.2.3-70-g09d2 From 72754ed88f7dfed9b80f5a46cd11550aac11cf1e Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 15:28:48 -0500 Subject: fix successful_payment_notification --- server/lib/webhook/webhook.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 9e3a4d3..58a13ca 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -1,5 +1,4 @@ // // where should this live? -// app.get('/subscribe/webhook', views.subscription.webhook); /* app.use(express.basicAuth(function(user, pass, callback) { @@ -111,8 +110,8 @@ var subscribe = module.exports = { */ // payments successful_payment_notification: function(data, user){ - var account = data.account - user.last_charged = new Date(data.transaction.date) + var account = data.account[0] + user.last_charged = new Date(data.transaction[0].date[0]._) user.save(function(){ }) }, -- cgit v1.2.3-70-g09d2 From ca838d172cbd7fca1f2cba3bb1f095821710920a Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 17:53:16 -0500 Subject: userlist sort --- public/assets/stylesheets/staff.css | 14 ++++++++++++++ server/lib/views/staff.js | 4 +++- views/staff/_users.ejs | 13 +++++++++++++ views/staff/users/show.ejs | 31 ++++++++++++++++++++++++++++--- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/public/assets/stylesheets/staff.css b/public/assets/stylesheets/staff.css index fffadbf..d93e46e 100644 --- a/public/assets/stylesheets/staff.css +++ b/public/assets/stylesheets/staff.css @@ -30,6 +30,15 @@ nav { nav a { margin-left: 10px; } +nav.subnav { + background: white; + padding: 10px; + font-weight: 200; + font-size: 12px; +} +.alphabet a { + margin-left: 5px; +} hr { border: 1px solid #bbb; margin: 10px auto 10px; @@ -62,8 +71,13 @@ hr { user-select: none; } .staff { + background: white; font-size: 15px; } +.staff hr { + border: 1px solid black; + border-top: 0; +} .staff .body a { border-bottom: 1px dotted; } diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index c3ecc97..b772859 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -368,8 +368,10 @@ var staff = module.exports = { helpers: { user: function(user){ + var last_seen = moment( user.last_seen || user.updated_at || user.created_at ) user = user.toObject() - user.last_seen = moment( user.last_seen || user.updated_at || user.created_at ).fromNow() + 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 diff --git a/views/staff/_users.ejs b/views/staff/_users.ejs index 46811b6..1af47aa 100644 --- a/views/staff/_users.ejs +++ b/views/staff/_users.ejs @@ -1,9 +1,22 @@ + + [[ users.forEach(function(user){ ]] + diff --git a/views/staff/users/show.ejs b/views/staff/users/show.ejs index d6a21d5..e441109 100644 --- a/views/staff/users/show.ejs +++ b/views/staff/users/show.ejs @@ -17,13 +17,38 @@ [[- profile.displayName ]]
    + [[- user.plan_type ]] + [[- user.last_charged ]] + [[- user.username ]]
    + + [[ if (profile.subscription) { ]] +

    Subscription

    + + + + + + + + + +
    + plan + + [[- profile.plan_type ]] +
    + last charged + + [[- profile.last_charged ]] +
    + [[ } ]] +

    Profile

    -- cgit v1.2.3-70-g09d2 From 1169b347c1f339b64f30466004a3f9a06ef7e117 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 2 Feb 2015 18:16:09 -0500 Subject: find users by initial --- public/assets/stylesheets/staff.css | 8 ++++++++ server/lib/views/staff.js | 12 ++++++++++++ views/staff/_users.ejs | 10 ++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/public/assets/stylesheets/staff.css b/public/assets/stylesheets/staff.css index d93e46e..ebc240a 100644 --- a/public/assets/stylesheets/staff.css +++ b/public/assets/stylesheets/staff.css @@ -39,6 +39,14 @@ nav.subnav { .alphabet a { margin-left: 5px; } +.body .error { + color: #f00; + border: 1px solid #f00; + margin: 20px 5px 200px 5px; + padding: 10px; + width: 400px; + display: inline-block; +} hr { border: 1px solid #bbb; margin: 10px auto 10px; diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index b772859..07050f1 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -38,6 +38,7 @@ var staff = module.exports = { 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"] @@ -54,6 +55,9 @@ var staff = module.exports = { paginationInfo.sort = "username" break } + if (initial) { + criteria.username = new RegExp('^' + initial, "i") + } User.find(criteria) .select(staff.fields.user) .sort(sort) @@ -61,6 +65,14 @@ var staff = module.exports = { .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() }) }, diff --git a/views/staff/_users.ejs b/views/staff/_users.ejs index 1af47aa..9caf893 100644 --- a/views/staff/_users.ejs +++ b/views/staff/_users.ejs @@ -1,12 +1,18 @@ +[[ if (! users.length || opt.error) { ]] +
    + [[- opt.error ]] +
    +[[ } ]] +
    [[ users.forEach(function(user){ ]] -- cgit v1.2.3-70-g09d2 From 78620c1451f78bdb470ed89c8a1e5c7a6bb10990 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 3 Feb 2015 13:07:42 -0500 Subject: support basicAuth --- config.json.example | 3 ++- server/index.js | 1 + server/lib/auth/index.js | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/config.json.example b/config.json.example index 7072d48..7221aa0 100644 --- a/config.json.example +++ b/config.json.example @@ -5,5 +5,6 @@ "socketPort": 1337, "webhookPort": 5000, "databaseHost": "lvh.me", - "env": { "development": 1 } + "env": { "development": 1 }, + "basicAuth": { "user": "foo", "pass": "bar" } } diff --git a/server/index.js b/server/index.js index 8c8212f..7bc3f7f 100644 --- a/server/index.js +++ b/server/index.js @@ -39,6 +39,7 @@ site.ready = function(){ site.setup = function(){ var SessionStore = new MongoStore({ mongoose_connection: mongoose.connection }) + auth.initBasicAuth(app) app.set('port', config.port); app.set('views', path.join(__dirname, '../views')); app.set('view engine', 'ejs'); diff --git a/server/lib/auth/index.js b/server/lib/auth/index.js index 199377d..8e20bcc 100644 --- a/server/lib/auth/index.js +++ b/server/lib/auth/index.js @@ -39,7 +39,14 @@ var auth = { auth.mail.init() }, - + initBasicAuth: function(app){ + if (config.basicAuth) { + app.use(express.basicAuth(function(user, pass) { + return user === config.basicAuth.user && + pass === config.basicAuth.pass + })) + } + }, initSockets: function (io, SessionStore) { io.set('authorization', passportSocketIo.authorize({ cookieParser: express.cookieParser, -- cgit v1.2.3-70-g09d2 From f51b018fd760d8550a06cb900a1311164ea881ad Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 3 Feb 2015 13:15:58 -0500 Subject: emails --- server/lib/auth/mail.js | 2 +- views/home.ejs | 2 +- views/staff/_users.ejs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/lib/auth/mail.js b/server/lib/auth/mail.js index eac4007..990b53f 100644 --- a/server/lib/auth/mail.js +++ b/server/lib/auth/mail.js @@ -6,7 +6,7 @@ var email = require("emailjs"), var mail = { - from: 'VValls ', + from: 'VValls ', templates: {}, init: function(){ diff --git a/views/home.ejs b/views/home.ejs index ffb0976..20723a5 100755 --- a/views/home.ejs +++ b/views/home.ejs @@ -68,7 +68,7 @@

    Ready To Go Pro?

    Use VValls as part of your product, service, or marketing campaign. We offer many types of customizations, including automation of layouts, more elaborate floor plans, enhanced video, interactivity, and other features. - Contact + Contact [[ include partials/confirm-modal ]] diff --git a/views/staff/_users.ejs b/views/staff/_users.ejs index 9caf893..ee988ab 100644 --- a/views/staff/_users.ejs +++ b/views/staff/_users.ejs @@ -1,5 +1,5 @@
    -[[ layouts.forEach(function(project){ ]] +[[ layouts.forEach(function(layout){ ]]
    [[- layout.name ]] diff --git a/views/staff/layouts/show.ejs b/views/staff/layouts/show.ejs index 0a2014b..b66449f 100644 --- a/views/staff/layouts/show.ejs +++ b/views/staff/layouts/show.ejs @@ -45,14 +45,14 @@ featured? - [[- layout.plan_type == 0 ? "yes" : "no" ]] + [[- layout.is_stock ? "yes" : "no" ]]


    - +

    -- cgit v1.2.3-70-g09d2 From e61e94f5d2f570f0cba3a3f7d91a18d8db524d79 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 10 Aug 2015 20:42:19 -0400 Subject: blueprint upload stuff --- .../javascripts/rectangles/engine/map/_map.js | 2 +- .../javascripts/ui/builder/BlueprintUpload.js | 120 +++++++++++++++++++++ public/assets/stylesheets/app.css | 3 +- public/assets/test/ortho4.html | 43 +++++++- 4 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 public/assets/javascripts/ui/builder/BlueprintUpload.js diff --git a/public/assets/javascripts/rectangles/engine/map/_map.js b/public/assets/javascripts/rectangles/engine/map/_map.js index bf646bb..e3d7621 100644 --- a/public/assets/javascripts/rectangles/engine/map/_map.js +++ b/public/assets/javascripts/rectangles/engine/map/_map.js @@ -44,7 +44,7 @@ var Map = function(opt){ var canvas = base.canvas = document.createElement("canvas") canvas.width = base.dimensions.a canvas.height = base.dimensions.b - + base.el.appendChild(canvas) switch (opt.type) { diff --git a/public/assets/javascripts/ui/builder/BlueprintUpload.js b/public/assets/javascripts/ui/builder/BlueprintUpload.js new file mode 100644 index 0000000..dbc6f12 --- /dev/null +++ b/public/assets/javascripts/ui/builder/BlueprintUpload.js @@ -0,0 +1,120 @@ + +var BlueprintUpload = UploadView.extend({ + el: ".blueprintUpload", + + mediaTag: "blueprint", + createAction: "/api/media/new", + uploadAction: "/api/media/upload", + listAction: "/api/media/user", + destroyAction: "/api/media/destroy", + + events: { + "mousedown": 'stopPropagation', + "change .url": "enterUrl", + "keydown .url": "enterSetUrl", + + "click .blueprint": "choose", + "change [name=blueprint-dimensions]": "changeDimensions", + "change [name=blueprint-units]": "changeUnits", + "click #saveBlueprint": "save", + }, + + initialize: function(opt){ + this.parent = opt.parent + this.__super__.initialize.call(this) + + this.$url = this.$(".url") + + this.$blueprintMap = this.$("#blueprintMap") + this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") + this.$dimensions = this.$("[name=blueprint-dimensions]") + this.$units = this.$("[name=blueprint-units]") + this.$save = this.$("#saveBlueprint") + + this.map = new Map ({ type: ortho }) + + this.load() + }, + + loaded: false, + load: function(){ + $.get(this.listAction, { tag: this.mediaTag }, this.populate.bind(this)) + }, + + populate: function(data){ + this.loaded = true + if (data && data.length) { + data.forEach(this.append.bind(this)) + this.$(".txt").hide() + } + else { + this.$(".txt").show() + } + }, + + append: function(media){ + var $el = $("
    ") + $el.data("id", media._id) + $el.addClass("blueprint") + this.$blueprints.append($el) + }, + + pick: function(e){ + var $el = $(e.currentTarget) + // load map with it + }, + + destroy: function(_id, cb){ + $.ajax({ + type: "delete", + url: this.destroyAction, + data: { _id: _id, _csrf: $("[name=_csrf]").val() } + }).complete(cb || function(){}) + }, + + show: function(){ + this.toggle(true) + }, + hide: function(){ + this.toggle(false) + }, + toggle: function (state) { + this.$el.toggleClass("active", state) + }, + + addUrl: function (url){ + Parser.loadImage(url, function(media){ + if (! media) return + media._csrf = $("[name=_csrf]").val() + media.tag = this.mediaTag + + var request = $.ajax({ + type: "post", + url: this.createAction, + data: media, + }) + request.done(this.add.bind(this)) + + }.bind(this)) + }, + enterUrl: function(){ + var url = this.$url.sanitize() + this.addUrl(url) + this.$url.val("") + }, + enterSetUrl: function (e) { + e.stopPropagation() + if (e.keyCode == 13) { + setTimeout(this.enterUrl.bind(this), 100) + } + }, + + add: function(media){ + this.append(media) + }, + changeDimensions: function(){ + }, + changeUnits: function(){ + }, + +}) diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 56c65fe..bb32500 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1597,7 +1597,8 @@ form .paidPlan label { float: none; font-size: 16px; margin: 0 10px; } .mediaDrawer.signin, .mediaDrawer.signup, .mediaDrawer.alert, .mediaDrawer.confirm, .mediaDrawer.passwordForgot, .mediaDrawer.passwordReset, .mediaDrawer.usernameTaken, -.mediaDrawer.layouts, .mediaDrawer.projects, .mediaDrawer.newProject { +.mediaDrawer.layouts, .mediaDrawer.projects, .mediaDrawer.newProject, +.mediaDrawer.blueprintUpload { display:table; } .confirm button { diff --git a/public/assets/test/ortho4.html b/public/assets/test/ortho4.html index b434efb..12b6ab2 100644 --- a/public/assets/test/ortho4.html +++ b/public/assets/test/ortho4.html @@ -1,15 +1,19 @@ +
    @@ -47,11 +55,38 @@ body {
    -
    +
    X

    Upload A Room Image

    + +
    +
    +
    + + + +
    +
    + +
    + + Please tell us the scale of your map. + Click both corners of a wall, and then enter how long the wall is. + +
    +
    + +
    + + + + +
    -- cgit v1.2.3-70-g09d2 From 8d749201d661f62766b4e3a84735c3307ff7ab5e Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 11 Aug 2015 13:22:02 -0400 Subject: refactor environment loading --- public/assets/javascripts/app.js | 30 +----- public/assets/javascripts/rectangles/_env.js | 34 +++++- public/assets/javascripts/ui/_router.js | 11 ++ .../javascripts/ui/builder/BlueprintUpload.js | 120 --------------------- public/assets/test/ortho4.html | 9 +- views/modal.ejs | 3 +- views/partials/scripts.ejs | 17 ++- 7 files changed, 68 insertions(+), 156 deletions(-) delete mode 100644 public/assets/javascripts/ui/builder/BlueprintUpload.js diff --git a/public/assets/javascripts/app.js b/public/assets/javascripts/app.js index 6ebcda5..3cafeca 100644 --- a/public/assets/javascripts/app.js +++ b/public/assets/javascripts/app.js @@ -21,40 +21,12 @@ app.init = function () { app.launch = function () { if ($.browser.msie || ! has3d()) { return app.fallback() } - scene = new MX.Scene().addTo('#scene') - scene.width = window.innerWidth - scene.height = window.innerHeight - scene.perspective = window.innerHeight - - window.onresize = function () { - scene.width = window.innerWidth - scene.height = window.innerHeight - scene.perspective = window.innerHeight - scene.update() - } - - cam = scene.camera - cam.y = viewHeight - - if (MX.Map) map = app.map = new MX.Map() - - if (is_mobile) { - app.movements = new MX.MobileMovements(cam, viewHeight) - } - else { - app.movements = new MX.Movements(cam, viewHeight) - } - app.movements.init() - var last_t = 0 function animate (t) { var dt = t - last_t last_t = t requestAnimationFrame(animate) - environment.update(t) - window.path && path.update(t) - app.movements.update(dt || 0) - scene.update() + environment.update(t, dt) } var loader = new Loader(function(){ diff --git a/public/assets/javascripts/rectangles/_env.js b/public/assets/javascripts/rectangles/_env.js index 14f73e3..b3c7d66 100644 --- a/public/assets/javascripts/rectangles/_env.js +++ b/public/assets/javascripts/rectangles/_env.js @@ -1,6 +1,22 @@ var environment = new function(){} environment.init = function(){ + scene = new MX.Scene().addTo('#scene') + scene.width = window.innerWidth + scene.height = window.innerHeight + scene.perspective = window.innerHeight + + cam = scene.camera + cam.y = viewHeight + + if (is_mobile) { + app.movements = new MX.MobileMovements(cam, viewHeight) + } + else { + app.movements = new MX.Movements(cam, viewHeight) + } + app.movements.init() + map = new Map () if (window.scene) { @@ -16,7 +32,14 @@ environment.init = function(){ scene.camera.radius = 20 } - + + window.onresize = function () { + scene.width = window.innerWidth + scene.height = window.innerHeight + scene.perspective = window.innerHeight + scene.update() + } + Rooms.init() Walls.init() Scenery.init() @@ -52,8 +75,13 @@ environment.init = function(){ } }) } -environment.update = function(t){ +environment.minimal = function(){ + environment.update = function(t){} +} +environment.update = function(t, dt){ + app.movements.update(dt || 0) + scene.update() map.update() - window.minimap && window.minimap.update && minimap.update() + window.minimap && minimap.update && minimap.update() z = false } diff --git a/public/assets/javascripts/ui/_router.js b/public/assets/javascripts/ui/_router.js index 1bdce19..857377c 100644 --- a/public/assets/javascripts/ui/_router.js +++ b/public/assets/javascripts/ui/_router.js @@ -44,6 +44,8 @@ var SiteRouter = Router.extend({ "/project/:name": 'projectViewer', "/project/:name/edit": 'projectEditor', "/project/:name/view": 'projectViewer', + + "/test/blueprint": 'blueprintEditor', }, mobileRoutes: { @@ -157,6 +159,15 @@ var SiteRouter = Router.extend({ this.readerView = app.controller = new ReaderView() this.readerView.load(name) }, + + blueprintEditor: function(e){ + environment.init = environment.minimal + app.launch() + if (app.unsupported) return + + this.blueprintView = app.controller = new BlueprintView () + this.blueprintView.load() + }, signup: function(e){ e && e.preventDefault() diff --git a/public/assets/javascripts/ui/builder/BlueprintUpload.js b/public/assets/javascripts/ui/builder/BlueprintUpload.js deleted file mode 100644 index dbc6f12..0000000 --- a/public/assets/javascripts/ui/builder/BlueprintUpload.js +++ /dev/null @@ -1,120 +0,0 @@ - -var BlueprintUpload = UploadView.extend({ - el: ".blueprintUpload", - - mediaTag: "blueprint", - createAction: "/api/media/new", - uploadAction: "/api/media/upload", - listAction: "/api/media/user", - destroyAction: "/api/media/destroy", - - events: { - "mousedown": 'stopPropagation', - "change .url": "enterUrl", - "keydown .url": "enterSetUrl", - - "click .blueprint": "choose", - "change [name=blueprint-dimensions]": "changeDimensions", - "change [name=blueprint-units]": "changeUnits", - "click #saveBlueprint": "save", - }, - - initialize: function(opt){ - this.parent = opt.parent - this.__super__.initialize.call(this) - - this.$url = this.$(".url") - - this.$blueprintMap = this.$("#blueprintMap") - this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") - this.$dimensions = this.$("[name=blueprint-dimensions]") - this.$units = this.$("[name=blueprint-units]") - this.$save = this.$("#saveBlueprint") - - this.map = new Map ({ type: ortho }) - - this.load() - }, - - loaded: false, - load: function(){ - $.get(this.listAction, { tag: this.mediaTag }, this.populate.bind(this)) - }, - - populate: function(data){ - this.loaded = true - if (data && data.length) { - data.forEach(this.append.bind(this)) - this.$(".txt").hide() - } - else { - this.$(".txt").show() - } - }, - - append: function(media){ - var $el = $("
    ") - $el.data("id", media._id) - $el.addClass("blueprint") - this.$blueprints.append($el) - }, - - pick: function(e){ - var $el = $(e.currentTarget) - // load map with it - }, - - destroy: function(_id, cb){ - $.ajax({ - type: "delete", - url: this.destroyAction, - data: { _id: _id, _csrf: $("[name=_csrf]").val() } - }).complete(cb || function(){}) - }, - - show: function(){ - this.toggle(true) - }, - hide: function(){ - this.toggle(false) - }, - toggle: function (state) { - this.$el.toggleClass("active", state) - }, - - addUrl: function (url){ - Parser.loadImage(url, function(media){ - if (! media) return - media._csrf = $("[name=_csrf]").val() - media.tag = this.mediaTag - - var request = $.ajax({ - type: "post", - url: this.createAction, - data: media, - }) - request.done(this.add.bind(this)) - - }.bind(this)) - }, - enterUrl: function(){ - var url = this.$url.sanitize() - this.addUrl(url) - this.$url.val("") - }, - enterSetUrl: function (e) { - e.stopPropagation() - if (e.keyCode == 13) { - setTimeout(this.enterUrl.bind(this), 100) - } - }, - - add: function(media){ - this.append(media) - }, - changeDimensions: function(){ - }, - changeUnits: function(){ - }, - -}) diff --git a/public/assets/test/ortho4.html b/public/assets/test/ortho4.html index 12b6ab2..1c1adef 100644 --- a/public/assets/test/ortho4.html +++ b/public/assets/test/ortho4.html @@ -91,14 +91,16 @@ body {
    - - + + + + @@ -126,8 +128,10 @@ body { + + + + @@ -79,10 +81,20 @@ + + + + - + + + + + + + @@ -115,6 +127,9 @@ + + + -- cgit v1.2.3-70-g09d2 From b5d2503ec4e5844ab3793c9e48020bcbaef3112b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 11 Aug 2015 13:22:31 -0400 Subject: blueprint integration into views --- .../javascripts/ui/blueprint/BlueprintScale.js | 0 .../javascripts/ui/blueprint/BlueprintUpload.js | 122 +++++++++++++++++++++ .../javascripts/ui/blueprint/BlueprintView.js | 39 +++++++ views/controls/builder/blueprint.ejs | 92 ++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 public/assets/javascripts/ui/blueprint/BlueprintScale.js create mode 100644 public/assets/javascripts/ui/blueprint/BlueprintUpload.js create mode 100644 public/assets/javascripts/ui/blueprint/BlueprintView.js create mode 100644 views/controls/builder/blueprint.ejs diff --git a/public/assets/javascripts/ui/blueprint/BlueprintScale.js b/public/assets/javascripts/ui/blueprint/BlueprintScale.js new file mode 100644 index 0000000..e69de29 diff --git a/public/assets/javascripts/ui/blueprint/BlueprintUpload.js b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js new file mode 100644 index 0000000..fd3a659 --- /dev/null +++ b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js @@ -0,0 +1,122 @@ + +var BlueprintUploadView = UploadView.extend({ + el: ".blueprintUpload", + + mediaTag: "blueprint", + createAction: "/api/media/new", + uploadAction: "/api/media/upload", + listAction: "/api/media/user", + destroyAction: "/api/media/destroy", + + events: { + "mousedown": 'stopPropagation', + "change .url": "enterUrl", + "keydown .url": "enterSetUrl", + + "click .blueprint": "choose", + "change [name=blueprint-dimensions]": "changeDimensions", + "change [name=blueprint-units]": "changeUnits", + "click #saveBlueprint": "save", + }, + + initialize: function(opt){ + this.parent = opt.parent + this.__super__.initialize.call(this) + + this.$url = this.$(".url") + + this.$blueprintMap = this.$("#blueprintMap") + this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") + this.$dimensions = this.$("[name=blueprint-dimensions]") + this.$units = this.$("[name=blueprint-units]") + this.$save = this.$("#saveBlueprint") + + this.map = new Map ({ type: 'ortho' }) + + this.load() + }, + + loaded: false, + load: function(){ + $.get(this.listAction, { tag: this.mediaTag }, this.populate.bind(this)) + }, + + populate: function(data){ + this.loaded = true + if (data && data.length) { + data.forEach(this.append.bind(this)) + this.$(".txt").hide() + } + else { + this.$(".txt").show() + } + }, + + append: function(media){ + var $el = $("
    ") + $el.data("id", media._id) + $el.addClass("blueprint") + this.$blueprints.append($el) + }, + + pick: function(e){ + var $el = $(e.currentTarget) + // load map with it + }, + + destroy: function(_id, cb){ + $.ajax({ + type: "delete", + url: this.destroyAction, + data: { _id: _id, _csrf: $("[name=_csrf]").val() } + }).complete(cb || function(){}) + }, + + show: function(){ + this.toggle(true) + }, + hide: function(){ + this.toggle(false) + }, + toggle: function (state) { + this.$el.toggleClass("active", state) + }, + + addUrl: function (url){ + Parser.loadImage(url, function(media){ + if (! media) return + media._csrf = $("[name=_csrf]").val() + media.tag = this.mediaTag + + var request = $.ajax({ + type: "post", + url: this.createAction, + data: media, + }) + request.done(this.add.bind(this)) + + }.bind(this)) + }, + enterUrl: function(){ + var url = this.$url.sanitize() + this.addUrl(url) + this.$url.val("") + }, + enterSetUrl: function (e) { + e.stopPropagation() + if (e.keyCode == 13) { + setTimeout(this.enterUrl.bind(this), 100) + } + }, + + add: function(media){ + this.append(media) + }, + changeDimensions: function(){ + }, + changeUnits: function(){ + }, + save: function(){ + }, + +}) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintView.js b/public/assets/javascripts/ui/blueprint/BlueprintView.js new file mode 100644 index 0000000..f7ee13e --- /dev/null +++ b/public/assets/javascripts/ui/blueprint/BlueprintView.js @@ -0,0 +1,39 @@ + +var BlueprintView = View.extend({ + el: "#blueprintView", + + events: { + }, + + initialize: function(){ +// this.info = new BuilderInfo ({ parent: this }) +// this.toolbar = new BuilderToolbar ({ parent: this }) +// this.settings = new BuilderSettings ({ parent: this }) +// this.colorControl = new ColorControl ({ parent: this }) +// this.cursor = new HelpCursor({ parent: this }) + this.blueprintUpload = new BlueprintUploadView ({ parent: this }) + }, + + load: function(name){ +// if (! name || name == "new") { +// this.ready({ isNew: true, _id: "new", name: "" }) +// return +// } +// +// name = sanitize(name) +// +// $.get(this.action + name, this.ready.bind(this)) + }, + + ready: function(data){ +// this.settings.load(data) +// this.info.load(data) + }, + + hideExtras: function(){ + }, + + pickWall: function(wall, pos){ + }, + +}) diff --git a/views/controls/builder/blueprint.ejs b/views/controls/builder/blueprint.ejs new file mode 100644 index 0000000..e5f3257 --- /dev/null +++ b/views/controls/builder/blueprint.ejs @@ -0,0 +1,92 @@ + + +
    +
    +
    + +
    + + + +
    + +
    + + + + +
    + +
    + X +
    + +

    Upload A Room Image

    + +
    +
    +
    + + + +
    +
    + +
    + + Please tell us the scale of your map. + Click both corners of a wall, and then enter how long the wall is. + +
    +
    + +
    + + + + +
    + +
    +
    +
    \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 4024085b09261c23cd89227e340037e0238072c7 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 11 Aug 2015 14:15:31 -0400 Subject: uploader functioning --- .../javascripts/ui/blueprint/BlueprintEditor.js | 30 ++++++ .../javascripts/ui/blueprint/BlueprintScale.js | 0 .../javascripts/ui/blueprint/BlueprintUpload.js | 35 ++----- .../javascripts/ui/blueprint/BlueprintView.js | 3 +- public/assets/stylesheets/app.css | 2 +- views/controls/blueprint/editor.ejs | 104 +++++++++++++++++++++ views/controls/builder/blueprint.ejs | 92 ------------------ views/modal.ejs | 2 +- views/partials/scripts.ejs | 1 + 9 files changed, 149 insertions(+), 120 deletions(-) create mode 100644 public/assets/javascripts/ui/blueprint/BlueprintEditor.js delete mode 100644 public/assets/javascripts/ui/blueprint/BlueprintScale.js create mode 100644 views/controls/blueprint/editor.ejs delete mode 100644 views/controls/builder/blueprint.ejs diff --git a/public/assets/javascripts/ui/blueprint/BlueprintEditor.js b/public/assets/javascripts/ui/blueprint/BlueprintEditor.js new file mode 100644 index 0000000..227c1c8 --- /dev/null +++ b/public/assets/javascripts/ui/blueprint/BlueprintEditor.js @@ -0,0 +1,30 @@ + +var BlueprintEditor = ModalView.extend({ + el: ".blueprintEditor", + + events: { + "change [name=blueprint-dimensions]": "changeDimensions", + "change [name=blueprint-units]": "changeUnits", + "click #saveBlueprint": "save", + }, + + initialize: function(){ + this.$blueprintMap = this.$("#blueprintMap") + this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") + this.$dimensions = this.$("[name=blueprint-dimensions]") + this.$units = this.$("[name=blueprint-units]") + this.$save = this.$("#saveBlueprint") + + this.map = new Map ({ type: 'ortho' }) + + this.$blueprintMap.append(this.map.el) + }, + + changeDimensions: function(){ + }, + changeUnits: function(){ + }, + save: function(){ + }, + +}) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintScale.js b/public/assets/javascripts/ui/blueprint/BlueprintScale.js deleted file mode 100644 index e69de29..0000000 diff --git a/public/assets/javascripts/ui/blueprint/BlueprintUpload.js b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js index fd3a659..9467715 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintUpload.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js @@ -1,5 +1,5 @@ -var BlueprintUploadView = UploadView.extend({ +var BlueprintUpload = UploadView.extend({ el: ".blueprintUpload", mediaTag: "blueprint", @@ -14,9 +14,6 @@ var BlueprintUploadView = UploadView.extend({ "keydown .url": "enterSetUrl", "click .blueprint": "choose", - "change [name=blueprint-dimensions]": "changeDimensions", - "change [name=blueprint-units]": "changeUnits", - "click #saveBlueprint": "save", }, initialize: function(opt){ @@ -24,15 +21,8 @@ var BlueprintUploadView = UploadView.extend({ this.__super__.initialize.call(this) this.$url = this.$(".url") + this.$blueprints = this.$(".blueprints") - this.$blueprintMap = this.$("#blueprintMap") - this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") - this.$dimensions = this.$("[name=blueprint-dimensions]") - this.$units = this.$("[name=blueprint-units]") - this.$save = this.$("#saveBlueprint") - - this.map = new Map ({ type: 'ortho' }) - this.load() }, @@ -52,13 +42,6 @@ var BlueprintUploadView = UploadView.extend({ } }, - append: function(media){ - var $el = $("
    ") - $el.data("id", media._id) - $el.addClass("blueprint") - this.$blueprints.append($el) - }, - pick: function(e){ var $el = $(e.currentTarget) // load map with it @@ -112,11 +95,13 @@ var BlueprintUploadView = UploadView.extend({ add: function(media){ this.append(media) }, - changeDimensions: function(){ - }, - changeUnits: function(){ - }, - save: function(){ - }, + append: function(media){ + var $el = $("") + $el.attr("src", media.url) + $el.data("id", media._id) + $el.addClass("blueprint") + this.$blueprints.append($el) + }, + }) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintView.js b/public/assets/javascripts/ui/blueprint/BlueprintView.js index f7ee13e..40c61d7 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintView.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintView.js @@ -11,7 +11,8 @@ var BlueprintView = View.extend({ // this.settings = new BuilderSettings ({ parent: this }) // this.colorControl = new ColorControl ({ parent: this }) // this.cursor = new HelpCursor({ parent: this }) - this.blueprintUpload = new BlueprintUploadView ({ parent: this }) + this.blueprintUpload = new BlueprintUpload ({ parent: this }) + this.blueprintEditor = new BlueprintEditor ({ parent: this }) }, load: function(name){ diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index bb32500..0f89242 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -1598,7 +1598,7 @@ form .paidPlan label { float: none; font-size: 16px; margin: 0 10px; } .mediaDrawer.alert, .mediaDrawer.confirm, .mediaDrawer.passwordForgot, .mediaDrawer.passwordReset, .mediaDrawer.usernameTaken, .mediaDrawer.layouts, .mediaDrawer.projects, .mediaDrawer.newProject, -.mediaDrawer.blueprintUpload { +.mediaDrawer.blueprintEditor { display:table; } .confirm button { diff --git a/views/controls/blueprint/editor.ejs b/views/controls/blueprint/editor.ejs new file mode 100644 index 0000000..6b80287 --- /dev/null +++ b/views/controls/blueprint/editor.ejs @@ -0,0 +1,104 @@ + + +
    +
    +
    + +
    + + + +
    + +
    + + + + +
    +
    + +
    + X +
    + +

    Upload your Blueprint

    + +
    + +
    +
    + + + +
    +
    + +
    +
    Your uploaded blueprints
    +
    +
    + + Please tell us the scale of your blueprint. + Click both corners of a wall, and then enter how long the wall is. + https://s3.amazonaws.com/luckyplop/fbf4295da80f1f66c5e4a248f2ea3e1ce7a22c3d.jpg + +
    +
    + +
    + + + + +
    + +
    +
    diff --git a/views/controls/builder/blueprint.ejs b/views/controls/builder/blueprint.ejs deleted file mode 100644 index e5f3257..0000000 --- a/views/controls/builder/blueprint.ejs +++ /dev/null @@ -1,92 +0,0 @@ - - -
    -
    -
    - -
    - - - -
    - -
    - - - - -
    - -
    - X -
    - -

    Upload A Room Image

    - -
    -
    -
    - - - -
    -
    - -
    - - Please tell us the scale of your map. - Click both corners of a wall, and then enter how long the wall is. - -
    -
    - -
    - - - - -
    - -
    -
    -
    \ No newline at end of file diff --git a/views/modal.ejs b/views/modal.ejs index 433861e..775ba88 100644 --- a/views/modal.ejs +++ b/views/modal.ejs @@ -10,7 +10,7 @@
    - [[ include controls/builder/blueprint ]] + [[ include controls/blueprint/editor ]] [[ include partials/confirm-modal ]] [[ include partials/sign-in ]] [[ include projects/layouts-modal ]] diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index 64ac135..2a93e9b 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -128,6 +128,7 @@ + -- cgit v1.2.3-70-g09d2 From d84c89a8dd770ea174a7d2ac90927046a5f4b5f6 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 11 Aug 2015 16:50:31 -0400 Subject: blueprint upload styling --- .../javascripts/rectangles/engine/map/ui/ortho.js | 4 +- .../javascripts/ui/blueprint/BlueprintEditor.js | 30 ----- .../javascripts/ui/blueprint/BlueprintScaler.js | 43 ++++++++ .../javascripts/ui/blueprint/BlueprintUpload.js | 36 ++++-- .../javascripts/ui/blueprint/BlueprintView.js | 2 +- views/controls/blueprint/editor.ejs | 122 +++++++++++++++++---- views/partials/scripts.ejs | 2 +- 7 files changed, 178 insertions(+), 61 deletions(-) delete mode 100644 public/assets/javascripts/ui/blueprint/BlueprintEditor.js create mode 100644 public/assets/javascripts/ui/blueprint/BlueprintScaler.js diff --git a/public/assets/javascripts/rectangles/engine/map/ui/ortho.js b/public/assets/javascripts/rectangles/engine/map/ui/ortho.js index 52f7339..5be7446 100644 --- a/public/assets/javascripts/rectangles/engine/map/ui/ortho.js +++ b/public/assets/javascripts/rectangles/engine/map/ui/ortho.js @@ -81,7 +81,9 @@ Map.UI.Ortho = function(map){ } base.set_tool = function(s){ console.log("set tool to", s) - base.tools[currentTool].cancel() + if (base.tools[currentTool]) { + base.tools[currentTool].cancel() + } currentTool = s tool = base.tools[currentTool] } diff --git a/public/assets/javascripts/ui/blueprint/BlueprintEditor.js b/public/assets/javascripts/ui/blueprint/BlueprintEditor.js deleted file mode 100644 index 227c1c8..0000000 --- a/public/assets/javascripts/ui/blueprint/BlueprintEditor.js +++ /dev/null @@ -1,30 +0,0 @@ - -var BlueprintEditor = ModalView.extend({ - el: ".blueprintEditor", - - events: { - "change [name=blueprint-dimensions]": "changeDimensions", - "change [name=blueprint-units]": "changeUnits", - "click #saveBlueprint": "save", - }, - - initialize: function(){ - this.$blueprintMap = this.$("#blueprintMap") - this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") - this.$dimensions = this.$("[name=blueprint-dimensions]") - this.$units = this.$("[name=blueprint-units]") - this.$save = this.$("#saveBlueprint") - - this.map = new Map ({ type: 'ortho' }) - - this.$blueprintMap.append(this.map.el) - }, - - changeDimensions: function(){ - }, - changeUnits: function(){ - }, - save: function(){ - }, - -}) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js new file mode 100644 index 0000000..e11b61e --- /dev/null +++ b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js @@ -0,0 +1,43 @@ + +var BlueprintScaler = ModalView.extend({ + el: ".blueprintScaler", + + events: { + "change [name=blueprint-dimensions]": "changeDimensions", + "change [name=blueprint-units]": "changeUnits", + "click #saveBlueprint": "save", + }, + + initialize: function(){ + this.$blueprintMap = this.$("#blueprintMap") + this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") + this.$dimensions = this.$("[name=blueprint-dimensions]") + this.$units = this.$("[name=blueprint-units]") + this.$save = this.$("#saveBlueprint") + + this.map = map = new Map ({ + type: "ortho", + el: this.$blueprintMap.get(0), + width: window.innerWidth/2, + height: window.innerHeight, + zoom: -2, + zoom_min: -6.2, + zoom_max: 1, + }) + map.ui.add_tool("arrow", new ArrowTool) + map.ui.add_tool("position", new PositionTool) + map.ui.set_tool("position") + }, + + pick: function(){ + + }, + + changeDimensions: function(){ + }, + changeUnits: function(){ + }, + save: function(){ + }, + +}) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintUpload.js b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js index 9467715..deb1075 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintUpload.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js @@ -13,7 +13,8 @@ var BlueprintUpload = UploadView.extend({ "change .url": "enterUrl", "keydown .url": "enterSetUrl", - "click .blueprint": "choose", + "click .blueprint": "pick", + "click .remove": "destroy", }, initialize: function(opt){ @@ -34,25 +35,33 @@ var BlueprintUpload = UploadView.extend({ populate: function(data){ this.loaded = true if (data && data.length) { + this.$blueprints.show() data.forEach(this.append.bind(this)) - this.$(".txt").hide() + this.show() } else { - this.$(".txt").show() + this.show() } }, pick: function(e){ var $el = $(e.currentTarget) - // load map with it + var media = $el.data("media") + this.hide() + this.parent.blueprintScaler.pick(media) }, - destroy: function(_id, cb){ + destroy: function(e){ + e.stopPropagation() + var $el = $(e.currentTarget) + var _id = $el.closest(".blueprint").data("id") + $el.remove() $.ajax({ type: "delete", url: this.destroyAction, data: { _id: _id, _csrf: $("[name=_csrf]").val() } - }).complete(cb || function(){}) + }).complete(function(){ + }) }, show: function(){ @@ -93,13 +102,24 @@ var BlueprintUpload = UploadView.extend({ }, add: function(media){ + this.$blueprints.show() this.append(media) + this.hide() + this.parent.blueprintScaler.pick(media) }, append: function(media){ - var $el = $("") - $el.attr("src", media.url) + var $el = $("") + var img = new Image () + img.src = media.url + var remove = document.createElement("span") + remove.className = "remove" + remove.innerHTML = "x" + $el.data("id", media._id) + $el.data("media", media) + $el.append(img) + $el.append(remove) $el.addClass("blueprint") this.$blueprints.append($el) }, diff --git a/public/assets/javascripts/ui/blueprint/BlueprintView.js b/public/assets/javascripts/ui/blueprint/BlueprintView.js index 40c61d7..a803f12 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintView.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintView.js @@ -12,7 +12,7 @@ var BlueprintView = View.extend({ // this.colorControl = new ColorControl ({ parent: this }) // this.cursor = new HelpCursor({ parent: this }) this.blueprintUpload = new BlueprintUpload ({ parent: this }) - this.blueprintEditor = new BlueprintEditor ({ parent: this }) + this.blueprintScaler = new BlueprintScaler ({ parent: this }) }, load: function(name){ diff --git a/views/controls/blueprint/editor.ejs b/views/controls/blueprint/editor.ejs index 6b80287..aec5e25 100644 --- a/views/controls/blueprint/editor.ejs +++ b/views/controls/blueprint/editor.ejs @@ -33,13 +33,88 @@ body { .hud span.active { color: #000; } .blueprintUpload { - width: 240px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + width: 340px; + position: absolute; + top: 50%; left: 50%; + background: white; + padding: 10px; + border: 1px solid black; + box-shadow: -3px 3px #000; + -webkit-transform: translate3D(0%,-200%,0); + transform: translate3D(0%,-200%,0); + margin-left: -175px; + margin-top: -200px; + opacity: 0; +} +.blueprintUpload.active { + -webkit-transform: translate3D(0,0,0); + transform: translate3D(0,0,0); + opacity: 1; +} +.blueprintUpload .toolButton { + float: none; + width: 108px; + display: inline-block; +} +.blueprintUpload .url { + font-size: 15px; + border: 1px solid #888; + padding: 2px; + font-weight: 300; + position: relative; + top: 3px; + margin-right: 10px; + width: 190px; +} +.blueprintUpload p { + font-weight: 300; + font-size: 13px; +} +.blueprintUpload .blueprints { + display: none; +} +.blueprintUpload .blueprints h5 { + width: 250px; + margin: 10px auto; + padding-top: 9px; } .blueprints .blueprint { border: 1px solid black; - padding: 5px; + background: white; + padding: 0px; + position: relative; + display: inline-block; +} +.blueprints .blueprint img { height: 100px; max-width: 200px; + display: block; + cursor: pointer; +} +.blueprints .blueprint .remove { + box-shadow: 0 1px 2px #888; + cursor: pointer; + position: absolute; + color: red; + top: 7px; + right: 7px; + width: 20px; height: 20px; + text-align: center; + background: #fff; + border-radius: 50%; +} +.blueprints .blueprint .remove span { + position: relative; + top: -2px; +} +.blueprintUpload .wallpaperUpload .upload-icon { + margin: 0 4px; +} +.uploadNewBlueprint { + color: #333; + border-bottom: 1px solid; cursor: pointer; } @@ -63,42 +138,49 @@ body {
    - X
    -

    Upload your Blueprint

    -
    +

    Upload your Blueprint

    +

    + Upload an image which you will trace to make a floor plan. + Images should be at least 1000x1000. +

    +
    - +
    -
    Your uploaded blueprints
    - Please tell us the scale of your blueprint. - Click both corners of a wall, and then enter how long the wall is. - https://s3.amazonaws.com/luckyplop/fbf4295da80f1f66c5e4a248f2ea3e1ce7a22c3d.jpg +
    +
    + Please tell us the scale of your blueprint. + Click both corners of a wall, and then enter how long the wall is. +

    + Do you want to upload a blueprint? +
    -
    -
    +
    +
    -
    - - +
    + + - + +
    - +
    diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index 2a93e9b..136bd4d 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -128,7 +128,7 @@ - + -- cgit v1.2.3-70-g09d2 From 554463ca8e8492bfd5f0f496e3a2291bfb495f80 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 11 Aug 2015 18:49:29 -0400 Subject: blueprint scaler stuff --- .../assets/javascripts/mx/primitives/mx.image.js | 17 ++- .../javascripts/ui/blueprint/BlueprintScaler.js | 34 +++++- .../javascripts/ui/blueprint/BlueprintUpload.js | 5 +- public/assets/stylesheets/app.css | 89 +++++++++++++++ public/assets/test/ortho4.html | 2 + views/controls/blueprint/editor.ejs | 120 +++++---------------- 6 files changed, 163 insertions(+), 104 deletions(-) diff --git a/public/assets/javascripts/mx/primitives/mx.image.js b/public/assets/javascripts/mx/primitives/mx.image.js index f9de141..33b1373 100644 --- a/public/assets/javascripts/mx/primitives/mx.image.js +++ b/public/assets/javascripts/mx/primitives/mx.image.js @@ -1,5 +1,7 @@ MX.Image = MX.Object3D.extend({ init: function (ops) { + ops = ops || {} + this.type = "Image" this.media = ops.media this.width = 0 @@ -41,15 +43,20 @@ MX.Image = MX.Object3D.extend({ layer.dirty = true layer.update() layer.ops.onload + + if (ops.keepImage) { + layer.image = image + } } - image.src = ops.src; - - if (ops.keepImage) { - this.image = image - } + + if (ops.src) image.src = ops.src + else if (ops.media) image.src = ops.media.url + else if (ops.url) image.src = ops.url }, draw: function(ctx, recenter){ + if (! this.image) { return } + if (recenter) { ctx.save() ctx.scale(-1, 1) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js index e11b61e..a81c89b 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js @@ -18,7 +18,7 @@ var BlueprintScaler = ModalView.extend({ this.map = map = new Map ({ type: "ortho", el: this.$blueprintMap.get(0), - width: window.innerWidth/2, + width: window.innerWidth, height: window.innerHeight, zoom: -2, zoom_min: -6.2, @@ -27,10 +27,38 @@ var BlueprintScaler = ModalView.extend({ map.ui.add_tool("arrow", new ArrowTool) map.ui.add_tool("position", new PositionTool) map.ui.set_tool("position") + + scene = scene || { camera: { x: 0, y: 0, z: 0 } } + + this.floorplan = new MX.Image () + + this.animate() }, - pick: function(){ - + pick: function(media){ + this.floorplan.load({ media: media, keepImage: true }) + }, + + animate: function(t){ + requestAnimationFrame(this.animate.bind(this)) + + var dt = t - this.last_t + this.last_t = t + + if (! t) return + + this.map.update(t) + + this.map.draw.ctx.save() + this.map.draw.translate() + + this.floorplan.draw(this.map.draw.ctx, true) + + this.map.draw.coords() + + this.map.draw.mouse(this.map.ui.mouse.cursor) + + this.map.draw.ctx.restore() }, changeDimensions: function(){ diff --git a/public/assets/javascripts/ui/blueprint/BlueprintUpload.js b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js index deb1075..498575a 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintUpload.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintUpload.js @@ -36,8 +36,9 @@ var BlueprintUpload = UploadView.extend({ this.loaded = true if (data && data.length) { this.$blueprints.show() - data.forEach(this.append.bind(this)) - this.show() + data.forEach(this.append.bind(this)) + this.hide() + this.parent.blueprintScaler.pick(data[0]) } else { this.show() diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 0f89242..6507cc1 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -3253,6 +3253,95 @@ a[data-role="forgot-password"] { } +/* blueprint upload box */ + +.blueprintUpload { + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + width: 340px; + position: absolute; + top: 50%; left: 50%; + background: white; + padding: 10px; + border: 1px solid black; + box-shadow: -3px 3px #000; + -webkit-transform: translate3D(0%,-200%,0); + transform: translate3D(0%,-200%,0); + margin-left: -175px; + margin-top: -200px; + opacity: 0; +} +.blueprintUpload.active { + -webkit-transform: translate3D(0,0,0); + transform: translate3D(0,0,0); + opacity: 1; +} +.blueprintUpload .toolButton { + float: none; + width: 108px; + display: inline-block; +} +.blueprintUpload .url { + font-size: 15px; + border: 1px solid #888; + padding: 2px; + font-weight: 300; + position: relative; + top: 3px; + margin-right: 10px; + width: 190px; +} +.blueprintUpload p { + font-weight: 300; + font-size: 13px; +} +.blueprintUpload .blueprints { + display: none; +} +.blueprintUpload .blueprints h5 { + width: 250px; + margin: 10px auto; + padding-top: 9px; +} +.blueprints .blueprint { + border: 2px solid black; + background: white; + padding: 0px; + position: relative; + display: inline-block; +} +.blueprints .blueprint img { + height: 100px; + max-width: 200px; + display: block; + cursor: pointer; +} +.blueprints .blueprint .remove { + box-shadow: -2px 2px #000; + cursor: pointer; + position: absolute; + color: red; + top: 7px; + right: 7px; + width: 20px; height: 20px; + text-align: center; + background: #fff; + border: 1px solid black; +} +.blueprints .blueprint .remove span { + position: relative; + top: -2px; +} +.blueprintUpload .wallpaperUpload .upload-icon { + margin: 0 4px; +} +.uploadNewBlueprint { + color: #333; + border-bottom: 1px solid; + cursor: pointer; +} + + /* KEYBOARD SHORTCUTS */ .keyboard { float: left; width: 50%; margin-top: 50px; } diff --git a/public/assets/test/ortho4.html b/public/assets/test/ortho4.html index 1c1adef..d704e0e 100644 --- a/public/assets/test/ortho4.html +++ b/public/assets/test/ortho4.html @@ -160,7 +160,9 @@ map.ui.placing = false $(window).resize(function(){ scene.width = window.innerWidth/2 + scene.height = window.innerHeight map.canvas.width = map.dimensions.a = window.innerWidth/2 + map.canvas.height = map.dimensions.b = window.innerHeight/2 }) var wall_height = 180 diff --git a/views/controls/blueprint/editor.ejs b/views/controls/blueprint/editor.ejs index aec5e25..308b4c8 100644 --- a/views/controls/blueprint/editor.ejs +++ b/views/controls/blueprint/editor.ejs @@ -32,90 +32,20 @@ body { .hud span { color: #888; cursor: pointer; } .hud span.active { color: #000; } -.blueprintUpload { - -webkit-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; - width: 340px; - position: absolute; - top: 50%; left: 50%; - background: white; - padding: 10px; - border: 1px solid black; - box-shadow: -3px 3px #000; - -webkit-transform: translate3D(0%,-200%,0); - transform: translate3D(0%,-200%,0); - margin-left: -175px; - margin-top: -200px; - opacity: 0; -} -.blueprintUpload.active { - -webkit-transform: translate3D(0,0,0); - transform: translate3D(0,0,0); - opacity: 1; -} -.blueprintUpload .toolButton { - float: none; - width: 108px; - display: inline-block; -} -.blueprintUpload .url { - font-size: 15px; - border: 1px solid #888; - padding: 2px; - font-weight: 300; - position: relative; - top: 3px; - margin-right: 10px; - width: 190px; -} -.blueprintUpload p { - font-weight: 300; +.blueprintInfo { + bottom: 14px; + left: 10px; + width: 270px; font-size: 13px; + font-weight: 300; + padding: 10px; } -.blueprintUpload .blueprints { - display: none; -} -.blueprintUpload .blueprints h5 { - width: 250px; - margin: 10px auto; - padding-top: 9px; -} -.blueprints .blueprint { - border: 1px solid black; - background: white; - padding: 0px; - position: relative; - display: inline-block; -} -.blueprints .blueprint img { - height: 100px; - max-width: 200px; - display: block; - cursor: pointer; -} -.blueprints .blueprint .remove { - box-shadow: 0 1px 2px #888; - cursor: pointer; - position: absolute; - color: red; - top: 7px; - right: 7px; - width: 20px; height: 20px; - text-align: center; - background: #fff; - border-radius: 50%; -} -.blueprints .blueprint .remove span { - position: relative; - top: -2px; -} -.blueprintUpload .wallpaperUpload .upload-icon { - margin: 0 4px; +.blueprintInfo .setting { + margin-bottom: 20px; } -.uploadNewBlueprint { - color: #333; - border-bottom: 1px solid; - cursor: pointer; +.blueprintInfo .setting.number input[type=text] { + width: 100px; + font-size: 16px; } @@ -139,7 +69,7 @@ body {
    - +

    Upload your Blueprint

    @@ -161,25 +91,27 @@ body {

    -
    +
    Please tell us the scale of your blueprint. - Click both corners of a wall, and then enter how long the wall is. + Click two corners of a wall, and then enter how long the wall is.

    - Do you want to upload a blueprint? + You can also upload another blueprint. +

    + +
    + + + +
    +
    -
    - - - - -
    -- cgit v1.2.3-70-g09d2 From d9050d0faacb0434a94e4bce2acc8f99e189db4f Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 12 Aug 2015 12:30:44 -0400 Subject: refactor staff area --- server/lib/views/staff.js | 918 ----------------------------------- server/lib/views/staff/defaults.js | 6 + server/lib/views/staff/fields.js | 9 + server/lib/views/staff/helpers.js | 52 ++ server/lib/views/staff/index.js | 452 +++++++++++++++++ server/lib/views/staff/middleware.js | 421 ++++++++++++++++ views/staff/projects/index.ejs | 2 +- 7 files changed, 941 insertions(+), 919 deletions(-) delete mode 100644 server/lib/views/staff.js create mode 100644 server/lib/views/staff/defaults.js create mode 100644 server/lib/views/staff/fields.js create mode 100644 server/lib/views/staff/helpers.js create mode 100644 server/lib/views/staff/index.js create mode 100644 server/lib/views/staff/middleware.js diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js deleted file mode 100644 index 43330e2..0000000 --- a/server/lib/views/staff.js +++ /dev/null @@ -1,918 +0,0 @@ -/* 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'), - Layout = require('../schemas/Layout'), - 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 layout_type created_at updated_at", - layout: "_id name slug user_id layout_type 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 sculpture 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() - }) - }, - - ensureLayouts: 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 - } - Layout.find(criteria) - .select(staff.fields.layout) - .sort(sort) - .skip(offset) - .limit(limit) - .exec(function (err, layouts) { - res.locals.layouts = layouts.map(staff.helpers.layout) - 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) - }, - - ensureLayoutsUsers: function(req, res, next){ - if (! res.locals.layouts || ! res.locals.layouts.length) { return next() } - staff.middleware.ensureObjectsUsers(res.locals.layouts, 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() - }) - }, - - ensureLayoutsCount: function(req, res, next){ - Layout.count({}, function(err, count){ - res.locals.layoutCount = 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() - }) - }, - - ensureLayout: function(req, res, next){ - res.locals.layout = req.layout - next() - }, - ensureLayoutUser: function(req, res, next){ - if (! res.locals.layout) { return next() } - User.findOne({ _id: res.locals.layout.user_id }, staff.fields.user, function(err, user){ - res.locals.layoutUser = staff.helpers.user(user) || staff.defaults.user - 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 - }, - - layout: function(layout){ - layout = layout.toObject() - layout.date = moment( layout.updated_at || layout.created_at ).format("M/DD/YYYY hh:mm a") - layout.user = {} - return layout - }, - - 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 - ); - - // - // layouts - - app.get('/staff/layouts', - middleware.ensureAuthenticated, - middleware.ensureIsStaff, - - staff.middleware.ensureLayoutsCount, - - staff.middleware.ensureLayouts, - staff.middleware.ensureLayoutsUsers, - - staff.layouts.index - ); - app.get('/staff/layouts/:slug', - middleware.ensureAuthenticated, - middleware.ensureIsStaff, - - middleware.ensureLayout, - staff.middleware.ensureLayout, - staff.middleware.ensureLayoutUser, - - staff.layouts.show - ); - app.put('/staff/layouts/:slug/stock', - middleware.ensureAuthenticated, - middleware.ensureIsStaff, - - middleware.ensureLayout, - staff.middleware.ensureLayout, - - staff.layouts.make_stock - ); - - // - // 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 }) - }) - }, - }, - - // /staff/layouts/ - // /staff/layouts/:name - layouts: { - index: function(req, res){ - res.locals.pagination.count = res.locals.layouts.length - res.locals.pagination.max = res.locals.layoutCount - staff.paginate(req, res) - res.render('staff/layouts/index') - }, - show: function(req, res){ - if (res.locals.layout) { - res.render('staff/layouts/show', { - }) - } - else { - res.render('staff/layouts/show_404') - } - }, - make_stock: function(req, res){ - res.locals.layout.is_stock = req.body.state == "true" - res.locals.layout.save(function(err, layout){ - res.json({ state: layout.is_stock }) - }) - }, - }, - - - 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') - }, - }, - -} diff --git a/server/lib/views/staff/defaults.js b/server/lib/views/staff/defaults.js new file mode 100644 index 0000000..4d86c41 --- /dev/null +++ b/server/lib/views/staff/defaults.js @@ -0,0 +1,6 @@ +module.exports = { + user: { + _id: "", username: "", displayName: "", + created_at: "", updated_at: "", created_ip: "", last_ip: "", + }, +} diff --git a/server/lib/views/staff/fields.js b/server/lib/views/staff/fields.js new file mode 100644 index 0000000..57eea7e --- /dev/null +++ b/server/lib/views/staff/fields.js @@ -0,0 +1,9 @@ +module.exports = { + user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip", + project: "_id name slug user_id privacy layout_type created_at updated_at", + layout: "_id name slug user_id layout_type 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 sculpture collaborators no_logo", +} diff --git a/server/lib/views/staff/helpers.js b/server/lib/views/staff/helpers.js new file mode 100644 index 0000000..57383f4 --- /dev/null +++ b/server/lib/views/staff/helpers.js @@ -0,0 +1,52 @@ + +var util = require('../../util'), + _ = require('lodash'), + moment = require('moment'); + +module.exports = { + 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 + }, + + layout: function(layout){ + layout = layout.toObject() + layout.date = moment( layout.updated_at || layout.created_at ).format("M/DD/YYYY hh:mm a") + layout.user = {} + return layout + }, + + 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 + }, +} diff --git a/server/lib/views/staff/index.js b/server/lib/views/staff/index.js new file mode 100644 index 0000000..6a56238 --- /dev/null +++ b/server/lib/views/staff/index.js @@ -0,0 +1,452 @@ +/* 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'), + Layout = require('../../schemas/Layout'), + config = require('../../../../config'), + middleware = require('../../middleware'), + util = require('../../util'), + _ = require('lodash'), + moment = require('moment'); + + +var staff = module.exports = { + + fields: require('./fields'), + defaults: require('./defaults'), + middleware: require('./middleware'), + helpers: require('./helpers'), + + 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 + ); + + // + // layouts + + app.get('/staff/layouts', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensureLayoutsCount, + + staff.middleware.ensureLayouts, + staff.middleware.ensureLayoutsUsers, + + staff.layouts.index + ); + app.get('/staff/layouts/:slug', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + middleware.ensureLayout, + staff.middleware.ensureLayout, + staff.middleware.ensureLayoutUser, + + staff.layouts.show + ); + app.put('/staff/layouts/:slug/stock', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + middleware.ensureLayout, + staff.middleware.ensureLayout, + + staff.layouts.make_stock + ); + + // + // 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 }) + }) + }, + }, + + // /staff/layouts/ + // /staff/layouts/:name + layouts: { + index: function(req, res){ + res.locals.pagination.count = res.locals.layouts.length + res.locals.pagination.max = res.locals.layoutCount + staff.paginate(req, res) + res.render('staff/layouts/index') + }, + show: function(req, res){ + if (res.locals.layout) { + res.render('staff/layouts/show', { + }) + } + else { + res.render('staff/layouts/show_404') + } + }, + make_stock: function(req, res){ + res.locals.layout.is_stock = req.body.state == "true" + res.locals.layout.save(function(err, layout){ + res.json({ state: layout.is_stock }) + }) + }, + }, + + + 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') + }, + }, + +} diff --git a/server/lib/views/staff/middleware.js b/server/lib/views/staff/middleware.js new file mode 100644 index 0000000..5c74d0b --- /dev/null +++ b/server/lib/views/staff/middleware.js @@ -0,0 +1,421 @@ + +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'), + Layout = require('../../schemas/Layout'), + util = require('../../util'), + fields = require('./fields'), + helpers = require('./helpers'), + defaults = require('./defaults'), + _ = require('lodash'), + moment = require('moment'); + + +var middleware = module.exports = { + + 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(fields.user) + .sort(sort) + .skip(offset) + .limit(limit) + .exec(function (err, users) { + res.locals.users = users.map(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 } } + 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(fields.project) + .sort(sort) + .skip(offset) + .limit(limit) + .exec(function (err, projects) { + res.locals.projects = projects.map(helpers.project) + next() + }) + }, + + ensureLayouts: 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 + } + Layout.find(criteria) + .select(fields.layout) + .sort(sort) + .skip(offset) + .limit(limit) + .exec(function (err, layouts) { + res.locals.layouts = layouts.map(helpers.layout) + 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(fields.media) + .sort(sort) + .skip(offset) + .limit(limit) + .exec(function (err, media) { + res.locals.media = media.map(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(fields.project) + .sort(sort) + .skip(offset) + .limit(limit) + .exec(function (err, subscriptions) { + res.locals.subscriptions = subscriptions.map(helpers.subscription) + next() + }) + }, + + ensurePlans: function(req, res, next){ + Plan.find({}).sort({ 'level': -1 }).exec(function (err, plans) { + res.locals.plans = (plans || []).map(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 } } + middleware.ensureProjects(dreq, res, next) + }, + + ensureProjectsUsers: function(req, res, next){ + if (! res.locals.projects || ! res.locals.projects.length) { return next() } + middleware.ensureObjectsUsers(res.locals.projects, next) + }, + + ensureLayoutsUsers: function(req, res, next){ + if (! res.locals.layouts || ! res.locals.layouts.length) { return next() } + middleware.ensureObjectsUsers(res.locals.layouts, next) + }, + + ensureSubscriptionsUsers: function(req, res, next){ + if (! res.locals.subscriptions || ! res.locals.subscriptions.length) { return next() } + middleware.ensureObjectsUsers(res.locals.subscriptions, next) + }, + + ensureMediaUsers: function(req, res, next){ + if (! res.locals.media || ! res.locals.media.length) { return next() } + middleware.ensureObjectsUsers(res.locals.media, next) + }, + + ensureSubscriptionUser: function(req, res, next){ + if (! res.locals.subscription) { return next() } + middleware.ensureObjectsUsers([ res.locals.subscription ], function(){ + next() + }) + }, + + ensureMediaUser: function(req, res, next){ + if (! res.locals.media) { return next() } + 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(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" ? 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" ? 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() + }) + }, + + ensureLayoutsCount: function(req, res, next){ + Layout.count({}, function(err, count){ + res.locals.layoutCount = 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 }, fields.project, function(err, projects){ + res.locals.projects = projects.map(helpers.project) + next() + }) + }, + + ensureProfileMedia: function(req, res, next){ + if (! res.locals.profile) { return next() } + req.criteria = { user_id: res.locals.profile._id } + 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 }, fields.user, function(err, user){ + res.locals.projectUser = helpers.user(user) || 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() + }) + }, + + ensureLayout: function(req, res, next){ + res.locals.layout = req.layout + next() + }, + ensureLayoutUser: function(req, res, next){ + if (! res.locals.layout) { return next() } + User.findOne({ _id: res.locals.layout.user_id }, fields.user, function(err, user){ + res.locals.layoutUser = helpers.user(user) || defaults.user + next() + }) + }, + +} \ No newline at end of file diff --git a/views/staff/projects/index.ejs b/views/staff/projects/index.ejs index 1d309ce..e4ba469 100644 --- a/views/staff/projects/index.ejs +++ b/views/staff/projects/index.ejs @@ -7,7 +7,7 @@
    [[ include ../_pagination ]] -[[ include ../_layouts ]] +[[ include ../_projects ]] [[ include ../_pagination ]] [[ include ../_footer ]] -- cgit v1.2.3-70-g09d2 From dd4f0178c7bcb5d14e1308e3877c5ab02eddf000 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 12 Aug 2015 12:37:49 -0400 Subject: modify webhook to support custom plans --- .../javascripts/ui/site/EditSubscriptionModal.js | 1 + server/lib/api/subscription.js | 1 + server/lib/schemas/Subscription.js | 1 + server/lib/schemas/User.js | 1 + server/lib/views/staff/middleware.js | 2 +- server/lib/webhook/webhook.js | 24 ++++++++++++++++------ views/staff/subscriptions/index.ejs | 3 +++ 7 files changed, 26 insertions(+), 7 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 3a20234..1033114 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -96,6 +96,7 @@ var EditSubscriptionModal = ModalView.extend({ free: 0, basic: 1, pro: 2, + custom: 3, }, loaded: false, diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js index 9c2d6ef..c2fff4c 100644 --- a/server/lib/api/subscription.js +++ b/server/lib/api/subscription.js @@ -16,6 +16,7 @@ var plan_levels = { free: 0, basic: 1, pro: 2, + custom: 3, } var subscription = module.exports = { diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js index 355bbe2..bf43e8b 100644 --- a/server/lib/schemas/Subscription.js +++ b/server/lib/schemas/Subscription.js @@ -9,6 +9,7 @@ var mongoose = require('mongoose'), var SubscriptionSchema = new mongoose.Schema({ user_id: { type: mongoose.Schema.ObjectId, index: true }, + plan_code: { type: String, default: "" }, plan_type: { type: String, default: "free" }, plan_period: { type: String, default: "monthly" }, diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js index 4b6ff39..e6e7f03 100644 --- a/server/lib/schemas/User.js +++ b/server/lib/schemas/User.js @@ -55,6 +55,7 @@ var UserSchema = new mongoose.Schema({ default: "", }, + plan_code: { type: String, default: "" }, plan_level: { type: Number, default: 0 }, plan_type: { type: String, default: "free" }, last_charged: { type: Date, default: null }, diff --git a/server/lib/views/staff/middleware.js b/server/lib/views/staff/middleware.js index 5c74d0b..1ea98e9 100644 --- a/server/lib/views/staff/middleware.js +++ b/server/lib/views/staff/middleware.js @@ -40,7 +40,7 @@ var middleware = module.exports = { } if (initial) { if (initial == "?") { - criteria.username = new RegExp('^[$a-zA-Z]', "i") + criteria.username = new RegExp('^[^a-zA-Z]', "i") } else { criteria.username = new RegExp('^' + initial, "i") diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index 58a13ca..bd51dac 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -26,10 +26,11 @@ var xml_bodyparser = require('express-xml-bodyparser'); var parser = new xml2js.Parser(); var subscribe = module.exports = { - plan_level: { + plan_levels: { free: 0, basic: 1, pro: 2, + custom: 3, }, callbacks: { @@ -62,19 +63,30 @@ var subscribe = module.exports = { Subscription.findOne({ "uuid": uuid }, function(err, old_subscriber){ // if (err) return; - var plan = subscrip.plan[0].plan_code[0].split("-") - var plan_type = plan[0] - var plan_period = plan[1] + var plan, plan_type, plan_code + var plan_code = subscrip.plan[0].plan_code[0] + if (plan_code.indexOf("-") !== -1) { + plan = plan_code.split("-") + plan_type = plan[0] + plan_period = plan[1] + } + else { + plan_type = "custom" + plan_period = "monthly" + } + + user.plan_code = plan_code user.plan_type = plan_type - user.plan_level = subscribe.plan_level[plan_type] + user.plan_level = subscribe.plan_levels[plan_type] var subscriber = old_subscriber || new Subscription () subscriber.uuid = uuid subscriber.user_id = user._id + subscriber.plan_code = plan_code subscriber.plan_type = plan_type subscriber.plan_period = plan_period - subscriber.plan_level = subscribe.plan_level[plan_type] + subscriber.plan_level = subscribe.plan_levels[plan_type] subscriber.add_ons = [] var add_ons = subscrip.subscription_add_ons[0].subscription_add_on if (add_ons) { diff --git a/views/staff/subscriptions/index.ejs b/views/staff/subscriptions/index.ejs index 3efffb5..adf148c 100644 --- a/views/staff/subscriptions/index.ejs +++ b/views/staff/subscriptions/index.ejs @@ -26,6 +26,9 @@ [[- subscription.user.last_seen ]] + + [[- subscription.plan_code ]] + [[ }) ]] -- cgit v1.2.3-70-g09d2 From cc3d0bf036dc934494bf517ebae88bd8544b9b06 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 12 Aug 2015 13:27:07 -0400 Subject: add artist privilege --- .../javascripts/ui/site/EditSubscriptionModal.js | 1 + public/assets/javascripts/ui/site/StaffView.js | 23 +++++++++++++++++++++- server/lib/schemas/User.js | 1 + server/lib/views/staff/index.js | 16 ++++++++++++++- server/lib/webhook/webhook.js | 1 + views/staff/users/show.ejs | 11 ++++++++++- 6 files changed, 50 insertions(+), 3 deletions(-) diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js index 1033114..c1dc9f8 100644 --- a/public/assets/javascripts/ui/site/EditSubscriptionModal.js +++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js @@ -97,6 +97,7 @@ var EditSubscriptionModal = ModalView.extend({ basic: 1, pro: 2, custom: 3, + artist: 4, }, loaded: false, diff --git a/public/assets/javascripts/ui/site/StaffView.js b/public/assets/javascripts/ui/site/StaffView.js index 59649e3..97f86c2 100644 --- a/public/assets/javascripts/ui/site/StaffView.js +++ b/public/assets/javascripts/ui/site/StaffView.js @@ -5,12 +5,14 @@ var StaffView = View.extend({ "click #toggle-staff": "toggleStaff", "click #toggle-featured": "toggleFeatured", "click #toggle-stock": "toggleStock", + "click #toggle-artist": "toggleArtist", }, initialize: function() { this.$toggleStaff = $("#toggle-staff") this.$toggleFeatured = $("#toggle-featured") this.$toggleStock = $("#toggle-stock") + this.$toggleArtist = $("#toggle-artist") this.$mediaEmbed = $("#media-embed") if (this.$toggleStaff.length && this.$toggleStaff.data().isstaff) { this.$toggleStaff.html("Is Staff") @@ -21,6 +23,9 @@ var StaffView = View.extend({ if (this.$toggleStock.length && this.$toggleStock.data().stock) { this.$toggleStock.html("Layout is Stock") } + if (this.$toggleArtist.length && this.$toggleArtist.data().isartist) { + this.$toggleArtist.html("Is Artist") + } if (this.$mediaEmbed.length) { var media = this.$mediaEmbed.data() this.$mediaEmbed.html( Parser.tag( media ) ) @@ -75,7 +80,6 @@ var StaffView = View.extend({ }, toggleStock: function(){ - console.log("stock") var state = ! this.$toggleStock.data().stock $.ajax({ type: "put", @@ -93,4 +97,21 @@ var StaffView = View.extend({ }) }, + toggleArtist: function(){ + var state = ! this.$toggleArtist.data().isartist + $.ajax({ + type: "put", + dataType: "json", + url: window.location.href + "/artist", + data: { + state: state, + _csrf: $("#_csrf").val(), + }, + success: function(data){ + this.$toggleArtist.data("stock", data.state) + this.$toggleArtist.html(data.state ? "Is Artist" : "Make Artist") + $("#isArtist").html(data.state ? "yes" : "no") + }.bind(this) + }) + }, }) diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js index e6e7f03..829b360 100644 --- a/server/lib/schemas/User.js +++ b/server/lib/schemas/User.js @@ -67,6 +67,7 @@ var UserSchema = new mongoose.Schema({ twitterName: { type: String, default: "" }, facebookUrl: { type: String, default: "" }, isStaff: { type: Boolean, default: false }, + isArtist: { type: Boolean, default: false }, subscription_id: { type: mongoose.Schema.ObjectId }, created_at: { type: Date }, updated_at: { type: Date }, diff --git a/server/lib/views/staff/index.js b/server/lib/views/staff/index.js index 6a56238..033fc88 100644 --- a/server/lib/views/staff/index.js +++ b/server/lib/views/staff/index.js @@ -75,7 +75,15 @@ var staff = module.exports = { staff.users.bless ); - + app.put('/staff/users/:username/artist', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensureProfile, + + staff.users.make_artist + ); + if (app.get('env') === 'development') { app.get('/staff/authorize', middleware.ensureAuthenticated, @@ -304,6 +312,12 @@ var staff = module.exports = { res.json({ state: user.isStaff }) }) }, + make_artist: function(req, res){ + res.locals.profile.isArtist = req.body.state == "true" + res.locals.profile.save(function(err, user){ + res.json({ state: user.isArtist }) + }) + }, }, // /staff/projects/ diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js index bd51dac..896d836 100644 --- a/server/lib/webhook/webhook.js +++ b/server/lib/webhook/webhook.js @@ -31,6 +31,7 @@ var subscribe = module.exports = { basic: 1, pro: 2, custom: 3, + artist: 4, }, callbacks: { diff --git a/views/staff/users/show.ejs b/views/staff/users/show.ejs index e441109..a434b57 100644 --- a/views/staff/users/show.ejs +++ b/views/staff/users/show.ejs @@ -35,7 +35,7 @@ plan - [[- profile.plan_type ]] + [[- profile.plan_code ]] @@ -101,12 +101,21 @@ [[- profile.isStaff ? "yes" : "no" ]] + + + is artist? + + + [[- profile.isArtist ? "yes" : "no" ]] + +

    [[ if (String(user._id) != String(profile._id)) { ]] + [[ } ]]
    -- cgit v1.2.3-70-g09d2 From 9ba29a587bf0722db82e5caf1b1cf4e5596003b6 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 12 Aug 2015 18:38:12 -0400 Subject: functioning blueprint scaler --- .../javascripts/rectangles/engine/map/draw.js | 2 +- .../rectangles/engine/map/tools/line.js | 49 ++++++++++++ .../assets/javascripts/rectangles/models/rect.js | 6 ++ .../javascripts/ui/blueprint/BlueprintScaler.js | 91 ++++++++++++++++++++-- .../javascripts/ui/blueprint/BlueprintView.js | 3 + server/lib/schemas/Layout.js | 1 + views/controls/blueprint/editor.ejs | 4 +- views/partials/scripts.ejs | 1 + 8 files changed, 148 insertions(+), 9 deletions(-) create mode 100644 public/assets/javascripts/rectangles/engine/map/tools/line.js diff --git a/public/assets/javascripts/rectangles/engine/map/draw.js b/public/assets/javascripts/rectangles/engine/map/draw.js index 11ba3f8..cc2f4d8 100644 --- a/public/assets/javascripts/rectangles/engine/map/draw.js +++ b/public/assets/javascripts/rectangles/engine/map/draw.js @@ -198,7 +198,7 @@ Map.Draw = function(map, opt){ // - function line (x,y,a,b,translation){ + var line = draw.line = function (x,y,a,b,translation){ if (translation) { x += translation.a a += translation.a diff --git a/public/assets/javascripts/rectangles/engine/map/tools/line.js b/public/assets/javascripts/rectangles/engine/map/tools/line.js new file mode 100644 index 0000000..8f409a8 --- /dev/null +++ b/public/assets/javascripts/rectangles/engine/map/tools/line.js @@ -0,0 +1,49 @@ +var LineTool = MapTool.extend(function(base){ + var exports = {} + + var selected_point = null + + var line = exports.line = [] + + var can_drag, dragging + + exports.down = function(e, cursor){ + this.cursor = cursor + switch (line.length) { + case 0: + line[0] = cursor.x_component() + can_drag = true + break + case 1: + line[1] = cursor.x_component() + can_drag = false + break + case 2: + line[0] = cursor.x_component() + line.pop() + can_drag = true + break + } + } + + exports.move = function(e, cursor){ + this.cursor = cursor + } + + exports.drag = function(e, cursor){ + if (dragging) { + line[1].a = cursor.x.b + line[1].b = cursor.y.b + } + else if (can_drag && cursor.magnitude() > 10/map.zoom) { + line[1] = cursor.y_component() + dragging = true + } + } + + exports.up = function(e, cursor){ + can_drag = dragging = false + } + + return exports +}) \ No newline at end of file diff --git a/public/assets/javascripts/rectangles/models/rect.js b/public/assets/javascripts/rectangles/models/rect.js index c667cf5..92c8c9e 100644 --- a/public/assets/javascripts/rectangles/models/rect.js +++ b/public/assets/javascripts/rectangles/models/rect.js @@ -39,6 +39,12 @@ Rect.prototype.clone = function(){ return new Rect( this.x.clone(), this.y.clone() ) } + Rect.prototype.x_component = function(){ + return new vec2( this.x.a, this.y.a ) + } + Rect.prototype.y_component = function(){ + return new vec2( this.x.b, this.y.b ) + } Rect.prototype.assign = function(r) { this.x.assign(r.x) this.y.assign(r.y) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js index a81c89b..addf9a9 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js @@ -1,10 +1,13 @@ -var BlueprintScaler = ModalView.extend({ +var BlueprintScaler = ModalFormView.extend({ el: ".blueprintScaler", + method: "/api/media/scale", + events: { "change [name=blueprint-dimensions]": "changeDimensions", "change [name=blueprint-units]": "changeUnits", + "click .uploadNewBlueprint": "showBlueprintUpload", "click #saveBlueprint": "save", }, @@ -12,6 +15,7 @@ var BlueprintScaler = ModalView.extend({ this.$blueprintMap = this.$("#blueprintMap") this.$blueprintDimensionsRapper = this.$("#blueprintDimensions") this.$dimensions = this.$("[name=blueprint-dimensions]") + this.$pixels = this.$("[name=blueprint-pixels]") this.$units = this.$("[name=blueprint-units]") this.$save = this.$("#saveBlueprint") @@ -24,23 +28,40 @@ var BlueprintScaler = ModalView.extend({ zoom_min: -6.2, zoom_max: 1, }) - map.ui.add_tool("arrow", new ArrowTool) - map.ui.add_tool("position", new PositionTool) - map.ui.set_tool("position") + this.lineTool = new LineTool + map.ui.add_tool("line", this.lineTool) + map.ui.set_tool("line") scene = scene || { camera: { x: 0, y: 0, z: 0 } } this.floorplan = new MX.Image () - this.animate() + this.animating = false + }, + + showBlueprintUpload: function(){ + this.parent.blueprintUpload.show() }, pick: function(media){ + this.media = media + + if (!! media.scale) { + this.parent.useFloorplan(media) + } + this.floorplan.load({ media: media, keepImage: true }) + + if (! this.animating) { + this.animating = true + this.animate() + } }, animate: function(t){ requestAnimationFrame(this.animate.bind(this)) + + if (! this.animating) return var dt = t - this.last_t this.last_t = t @@ -53,6 +74,27 @@ var BlueprintScaler = ModalView.extend({ this.map.draw.translate() this.floorplan.draw(this.map.draw.ctx, true) + + this.map.draw.ctx.strokeStyle = "#f00" + this.map.draw.ctx.lineWidth = 2/map.zoom + switch (this.lineTool.line.length) { + case 1: + this.map.draw.line( + this.lineTool.line[0].a, + this.lineTool.line[0].b, + this.lineTool.cursor.x.a, + this.lineTool.cursor.y.a + ) + break + case 2: + this.map.draw.line( + this.lineTool.line[0].a, + this.lineTool.line[0].b, + this.lineTool.line[1].a, + this.lineTool.line[1].b + ) + break + } this.map.draw.coords() @@ -62,10 +104,47 @@ var BlueprintScaler = ModalView.extend({ }, changeDimensions: function(){ + app.units = this.$units.val() + this.$dimensions.unitVal() }, changeUnits: function(){ + app.units = this.$units.val() + this.$dimensions.resetUnitVal() + }, + lineLength: function(){ + if (this.lineTool.line.length !== 2) return 0 + var line = this.lineTool.line + return dist( line[0].a, line[0].b, line[1].a, line[1].b ) + } + + validate: function(){ + var val = this.$dimensions.unitVal() + var errors = [] + if (! this.lineLength()) { + errors.push("no line") + alert("Please click two corners of a wall and then specify how long it is in feet or meters.") + } + else if (val == 0) { + errors.push("no measurement") + alert("Please tell us how long the wall is in feet or meters.") + } + return errors }, - save: function(){ + + showErrors: function(){}, + + serialize: function(){ + var fd = new FormData() + fd.append( "_id", this.media._id) + fd.append( "units", this.$units.val() ) + fd.append( "scale", this.$dimensions.unitVal() / this.lineLength() ) + fd.append( "_csrf", $("[name=_csrf]").val()) + return fd + }, + + success: function(){ + this.animating = false + this.parent.useFloorplan(media) }, }) diff --git a/public/assets/javascripts/ui/blueprint/BlueprintView.js b/public/assets/javascripts/ui/blueprint/BlueprintView.js index a803f12..6b204e5 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintView.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintView.js @@ -33,6 +33,9 @@ var BlueprintView = View.extend({ hideExtras: function(){ }, + + useFloorplan: function(media){ + }, pickWall: function(wall, pos){ }, diff --git a/server/lib/schemas/Layout.js b/server/lib/schemas/Layout.js index cff1d78..e15e188 100644 --- a/server/lib/schemas/Layout.js +++ b/server/lib/schemas/Layout.js @@ -23,6 +23,7 @@ var LayoutSchema = new mongoose.Schema({ photo: { type: String, }, + media: mongoose.Schema.Types.Mixed, rooms: [mongoose.Schema.Types.Mixed], startPosition: mongoose.Schema.Types.Mixed, viewHeight: { type: Number }, diff --git a/views/controls/blueprint/editor.ejs b/views/controls/blueprint/editor.ejs index 308b4c8..3e0c097 100644 --- a/views/controls/blueprint/editor.ejs +++ b/views/controls/blueprint/editor.ejs @@ -99,8 +99,8 @@ body {

    - - + + - -
    - +
    +
    + + + +
    + +
    -- cgit v1.2.3-70-g09d2 From 6b3d07293bf674703b286cb396049fb8e83b86b7 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 14 Aug 2015 16:37:39 -0400 Subject: AnimatedView --- .../javascripts/ui/blueprint/BlueprintScaler.js | 29 ++++----------- public/assets/javascripts/ui/lib/AnimatedView.js | 31 ++++++++++++++++ views/blueprint.ejs | 33 +++++++++++++++++ views/builder.ejs | 16 -------- views/controls/blueprint/editor.ejs | 43 ++++++++++------------ views/modal.ejs | 1 - views/partials/scripts.ejs | 1 + 7 files changed, 93 insertions(+), 61 deletions(-) create mode 100644 public/assets/javascripts/ui/lib/AnimatedView.js create mode 100644 views/blueprint.ejs diff --git a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js index c393cfb..741f4fb 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js @@ -1,5 +1,5 @@ -var BlueprintScaler = ModalFormView.extend({ +var BlueprintScaler = ModalFormView.extend(AnimatedView.prototype).extend({ el: ".blueprintScaler", action: "/api/media/scale", @@ -20,7 +20,7 @@ var BlueprintScaler = ModalFormView.extend({ this.$units = this.$("[name=blueprint-units]") this.$save = this.$("#saveBlueprint") - this.map = map = new Map ({ + this.map = new Map ({ type: "ortho", el: this.$blueprintMap.get(0), width: window.innerWidth, @@ -30,14 +30,12 @@ var BlueprintScaler = ModalFormView.extend({ zoom_max: 1, }) this.lineTool = new LineTool - map.ui.add_tool("line", this.lineTool) - map.ui.set_tool("line") + this.map.ui.add_tool("line", this.lineTool) + this.map.ui.set_tool("line") scene = scene || { camera: { x: 0, y: 0, z: 0 } } this.floorplan = new MX.Image () - - this.animating = false }, showBlueprintUpload: function(){ @@ -50,27 +48,16 @@ var BlueprintScaler = ModalFormView.extend({ if (!! media.units) { this.parent.useFloorplan(media) this.hide() + this.stopAnimating() return } this.floorplan.load({ media: media, keepImage: true }) - if (! this.animating) { - this.animating = true - this.animate() - } + this.startAnimating() }, - animate: function(t){ - requestAnimationFrame(this.animate.bind(this)) - - if (! this.animating) return - - var dt = t - this.last_t - this.last_t = t - - if (! t) return - + animate: function(t, dt){ this.map.update(t) this.map.draw.ctx.save() @@ -147,7 +134,7 @@ var BlueprintScaler = ModalFormView.extend({ }, success: function(){ - this.animating = false + this.stopAnimating() this.parent.useFloorplan(this.media) this.hide() }, diff --git a/public/assets/javascripts/ui/lib/AnimatedView.js b/public/assets/javascripts/ui/lib/AnimatedView.js new file mode 100644 index 0000000..3c50b0a --- /dev/null +++ b/public/assets/javascripts/ui/lib/AnimatedView.js @@ -0,0 +1,31 @@ +var AnimatedView = View.extend({ + + _animating: false, + last_t: 0, + + startAnimating: function(){ + if (this._animating) return + this._animating = true + this._animate() + }, + + stopAnimating: function(){ + this._animating = false + }, + + _animate: function(t){ + if (! this._animating) return + + requestAnimationFrame(this._animate.bind(this)) + + var dt = t - this.last_t + this.last_t = t + + if (! t) return + + this.animate(t, dt) + }, + + animate: function(t, dt){}, + +}) \ No newline at end of file diff --git a/views/blueprint.ejs b/views/blueprint.ejs new file mode 100644 index 0000000..371d66f --- /dev/null +++ b/views/blueprint.ejs @@ -0,0 +1,33 @@ + + + + VValls + [[ include partials/meta ]] + + + +
    + +
    + [[ include partials/header ]] + +
    + [[ include controls/builder/info ]] + [[ include controls/builder/toolbar ]] + [[ include controls/builder/settings ]] + [[ include controls/blueprint/editor ]] +
    + +
    +
    +
    +
    +
    + + [[ include partials/confirm-modal ]] + [[ include projects/layouts-modal ]] + [[ include partials/sign-in ]] + + +[[ include partials/scripts ]] + diff --git a/views/builder.ejs b/views/builder.ejs index 0ba4238..6a31e22 100644 --- a/views/builder.ejs +++ b/views/builder.ejs @@ -17,22 +17,6 @@ [[ include controls/builder/settings ]]
    - -
    diff --git a/views/controls/blueprint/editor.ejs b/views/controls/blueprint/editor.ejs index 77c958a..e18f501 100644 --- a/views/controls/blueprint/editor.ejs +++ b/views/controls/blueprint/editor.ejs @@ -67,7 +67,7 @@ body {
    -
    +
    @@ -90,30 +90,27 @@ body {
    -
    -
    - Please tell us the scale of your blueprint. - Click two corners of a wall, and then enter how long the wall is. -

    - You can also upload another blueprint. -

    +
    + Please tell us the scale of your blueprint. + Click two corners of a wall, and then enter how long the wall is. +

    + You can also upload another blueprint. +

    -
    -
    - - - -
    - -
    -
    +
    +
    + + + +
    + +
    +
    -
    -
    - +
    diff --git a/views/modal.ejs b/views/modal.ejs index 775ba88..dfc5573 100644 --- a/views/modal.ejs +++ b/views/modal.ejs @@ -10,7 +10,6 @@
    - [[ include controls/blueprint/editor ]] [[ include partials/confirm-modal ]] [[ include partials/sign-in ]] [[ include projects/layouts-modal ]] diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index a0222b5..e0024a4 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -102,6 +102,7 @@ + -- cgit v1.2.3-70-g09d2 From 599b43df07f092b35d25e7adac11db3c3b3d9c76 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 17 Aug 2015 12:23:39 -0400 Subject: BlueprintEditor --- public/assets/javascripts/ui/_router.js | 7 +- .../javascripts/ui/blueprint/BlueprintEditor.js | 155 +++++++++++++++++++++ .../javascripts/ui/blueprint/BlueprintView.js | 18 +-- server/index.js | 3 + server/lib/views/index.js | 4 + views/blueprint.ejs | 1 + views/controls/blueprint/editor.ejs | 53 ------- views/controls/blueprint/scaler.ejs | 48 +++++++ views/partials/scripts.ejs | 1 + 9 files changed, 227 insertions(+), 63 deletions(-) create mode 100644 public/assets/javascripts/ui/blueprint/BlueprintEditor.js create mode 100644 views/controls/blueprint/scaler.ejs diff --git a/public/assets/javascripts/ui/_router.js b/public/assets/javascripts/ui/_router.js index 857377c..177e86f 100644 --- a/public/assets/javascripts/ui/_router.js +++ b/public/assets/javascripts/ui/_router.js @@ -38,6 +38,9 @@ var SiteRouter = Router.extend({ "/layout": 'layoutPicker', "/layout/:name": 'layoutEditor', + "/blueprint": 'blueprintEditor', + "/blueprint/:name": 'blueprintEditor', + "/project": 'projectPicker', "/project/new": 'newProject', "/project/new/:layout": 'projectNewWithLayout', @@ -160,13 +163,13 @@ var SiteRouter = Router.extend({ this.readerView.load(name) }, - blueprintEditor: function(e){ + blueprintEditor: function(e, name){ environment.init = environment.minimal app.launch() if (app.unsupported) return this.blueprintView = app.controller = new BlueprintView () - this.blueprintView.load() + this.blueprintView.load(name) }, signup: function(e){ diff --git a/public/assets/javascripts/ui/blueprint/BlueprintEditor.js b/public/assets/javascripts/ui/blueprint/BlueprintEditor.js new file mode 100644 index 0000000..c781495 --- /dev/null +++ b/public/assets/javascripts/ui/blueprint/BlueprintEditor.js @@ -0,0 +1,155 @@ + +var wall_height = 180 +var shapes = new ShapeList +var last_point = new vec2 (0,0) + +var BlueprintEditor = View.extend(AnimatedView.prototype).extend({ + + initialize: function(opt){ + this.parent = opt.parent + + map = new Map ({ + type: "ortho", + el: document.querySelector("#orthographic"), + width: window.innerWidth/2, + height: window.innerHeight, + zoom: -2, + zoom_min: -6.2, + zoom_max: 1, + }) + map.ui.add_tool("arrow", new ArrowTool) + map.ui.add_tool("polyline", new PolylineTool) + map.ui.add_tool("ortho-polyline", new OrthoPolylineTool) + map.ui.add_tool("eraser", new EraserTool) + map.ui.add_tool("position", new PositionTool) + map.ui.placing = false + +/* + $(window).resize(function(){ + scene.width = window.innerWidth/2 + scene.height = window.innerHeight + map.canvas.width = map.dimensions.a = window.innerWidth/2 + map.canvas.height = map.dimensions.b = window.innerHeight/2 + }) +*/ + + var PerspectiveToolbar = new Toolbar (".persp-hud") + PerspectiveToolbar.add("orbit-mode", function(){ + controls.toggle(true) + movements.lock() + }) + PerspectiveToolbar.add("keyboard-mode", function(){ + controls.toggle(false) + movements.unlock() + movements.gravity(true) + cam.rotationX = 0 + cam.rotationY = -cam.rotationY + cam.x = 0 + cam.y = viewHeight + 100 + cam.z = 0 + }) + + var OrthographicToolbar = new Toolbar (".ortho-hud") + OrthographicToolbar.add("arrow-mode", function(){ + map.ui.set_tool("arrow") + }) + OrthographicToolbar.add("polyline-mode", function(){ + map.ui.set_tool("polyline") + }) + OrthographicToolbar.add("ortho-polyline-mode", function(){ + map.ui.set_tool("ortho-polyline") + }) + OrthographicToolbar.add("eraser-mode", function(){ + map.ui.set_tool("eraser") + }) + OrthographicToolbar.pick("ortho-polyline-mode") + }, + + animate: function(t, dt){ + map.update(t) + + movements.update(dt) + controls.update() + scene.update() + + map.draw.ctx.save() + map.draw.translate() + + floorplan.draw(map.draw.ctx, true) + + map.draw.coords() + + if (shapes.workline) { + shapes.workline.draw(map.draw.ctx) + if (map.ui.placing && last_point) { + shapes.workline.draw_line( map.draw.ctx, last_point ) + } + } + + shapes.forEach(function(shape){ + shape.draw(map.draw.ctx) + }) + + map.draw.ctx.strokeStyle = "#f00"; + map.draw.x_at(0,0) + map.draw.mouse(map.ui.mouse.cursor) + map.draw.camera(scene.camera) + + map.draw.ctx.restore() + }, + +}) + +function build () { + scene = new MX.Scene().addTo("#perspective") + scene.camera.radius = 20 + + viewHeight = 100 + + scene.width = window.innerWidth/2 + scene.height = window.innerHeight + scene.perspective = window.innerHeight + + cam = scene.camera + movements = new MX.Movements(cam, viewHeight) + movements.init() + movements.lock() + movements.velocity(8) + app.on("move", function(pos){ + cam.x = pos.x + cam.y = pos.y + cam.z = pos.z + }) + + floorplan = new MX.Image({ + src: "https://s3.amazonaws.com/luckyplop/fbf4295da80f1f66c5e4a248f2ea3e1ce7a22c3d.jpg", + keepImage: true, + rotationX: -PI/2, + rotationY: PI, + }) + scene.add(floorplan) + + // recenter perspective view by rightclicking map + floorplan.el.addEventListener("contextmenu", function(e){ + e.preventDefault() + var offset = offsetFromPoint(e, this) + var x = (offset.left - 0.5) * floorplan.width * floorplan.scale + var z = (offset.top - 0.5) * floorplan.height * floorplan.scale + controls.opt.center.x = -x + controls.opt.center.y = 0 + controls.opt.center.z = -z + }, true) + + scene.update() + + controls = new MX.OrbitCamera({ + el: scene.el, + radius: 3000, + radiusRange: [ 10, 10000 ], + rotationX: PI/4, + rotationY: PI/2, + }) + controls.init() + + animate(0) +} diff --git a/public/assets/javascripts/ui/blueprint/BlueprintView.js b/public/assets/javascripts/ui/blueprint/BlueprintView.js index 6b204e5..0a06fda 100644 --- a/public/assets/javascripts/ui/blueprint/BlueprintView.js +++ b/public/assets/javascripts/ui/blueprint/BlueprintView.js @@ -1,7 +1,9 @@ var BlueprintView = View.extend({ el: "#blueprintView", - + + action: "/api/layout/", + events: { }, @@ -16,14 +18,14 @@ var BlueprintView = View.extend({ }, load: function(name){ -// if (! name || name == "new") { + if (! name || name == "new") { // this.ready({ isNew: true, _id: "new", name: "" }) -// return -// } -// -// name = sanitize(name) -// -// $.get(this.action + name, this.ready.bind(this)) + return + } + + name = sanitize(name) + + $.get(this.action + name, this.ready.bind(this)) }, ready: function(data){ diff --git a/server/index.js b/server/index.js index 0028888..078db8e 100644 --- a/server/index.js +++ b/server/index.js @@ -127,6 +127,9 @@ site.route = function () { app.get('/layout', middleware.ensureAuthenticated, middleware.ensureIsStaff, views.modal) app.get('/layout/:name', middleware.ensureAuthenticated, middleware.ensureIsStaff, views.builder) + app.get('/blueprint', middleware.ensureAuthenticated, middleware.ensureIsStaff, views.blueprint) + app.get('/blueprint/:name', middleware.ensureAuthenticated, middleware.ensureIsStaff, views.blueprint) + app.get('/join/:nonce', middleware.ensureAuthenticated, api.collaborator.join) app.get('/api/collaborator/:slug/index', middleware.ensureAuthenticated, middleware.ensureProject, api.collaborator.index) app.post('/api/collaborator/:slug/create', middleware.ensureAuthenticated, middleware.ensureProject, api.collaborator.create) diff --git a/server/lib/views/index.js b/server/lib/views/index.js index 5241ddb..523f628 100644 --- a/server/lib/views/index.js +++ b/server/lib/views/index.js @@ -84,6 +84,10 @@ var views = module.exports = { res.render('builder') }, + blueprint: function (req, res) { + res.render('blueprint') + }, + modal: function (req, res) { res.render('modal'); }, diff --git a/views/blueprint.ejs b/views/blueprint.ejs index 371d66f..7e13318 100644 --- a/views/blueprint.ejs +++ b/views/blueprint.ejs @@ -16,6 +16,7 @@ [[ include controls/builder/toolbar ]] [[ include controls/builder/settings ]] [[ include controls/blueprint/editor ]] + [[ include controls/blueprint/scaler ]]
    diff --git a/views/controls/blueprint/editor.ejs b/views/controls/blueprint/editor.ejs index e18f501..5334f85 100644 --- a/views/controls/blueprint/editor.ejs +++ b/views/controls/blueprint/editor.ejs @@ -1,8 +1,4 @@