mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Teacher trial subscription form
Add a teacher survey form for applying for a free trial subscription for evaluation purposes. Add an admin trial requests review page, where admins can approve/deny requests.
This commit is contained in:
parent
d0f9456024
commit
d7cddcb136
22 changed files with 620 additions and 22 deletions
|
@ -38,6 +38,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
'admin/level-sessions': go('admin/LevelSessionsView')
|
||||
'admin/users': go('admin/UsersView')
|
||||
'admin/base': go('admin/BaseView')
|
||||
'admin/trial-requests': go('admin/TrialRequestsView')
|
||||
'admin/user-code-problems': go('admin/UserCodeProblemsView')
|
||||
'admin/pending-patches': go('admin/PendingPatchesView')
|
||||
|
||||
|
@ -109,6 +110,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
'preview': go('HomeView')
|
||||
|
||||
'teachers': go('TeachersView')
|
||||
'teachers/freetrial': go('TeachersFreeTrialView')
|
||||
|
||||
'test(/*subpath)': go('TestView')
|
||||
|
||||
|
|
|
@ -600,8 +600,9 @@
|
|||
free_1: "There are 80+ FREE levels which cover every concept." # {change}
|
||||
free_2: "A monthly subscription provides access to video tutorials and extra practice levels."
|
||||
teacher_subs_title: "Teachers get free subscriptions!"
|
||||
teacher_subs_1: "Please contact"
|
||||
teacher_subs_2: "to set up a free monthly subscription."
|
||||
teacher_subs_1: "Please fill out our" # {change}
|
||||
teacher_subs_2: "Teacher Survey" # {change}
|
||||
teacher_subs_3: "to set up your subscription."
|
||||
sub_includes_title: "What is included in the subscription?"
|
||||
sub_includes_1: "In addition to the 80+ basic levels, students with a monthly subscription get access to these additional features:"
|
||||
sub_includes_2: "60+ practice levels"
|
||||
|
|
7
app/models/TrialRequest.coffee
Normal file
7
app/models/TrialRequest.coffee
Normal file
|
@ -0,0 +1,7 @@
|
|||
CocoModel = require './CocoModel'
|
||||
schema = require 'schemas/models/trial_request.schema'
|
||||
|
||||
module.exports = class TrialRequest extends CocoModel
|
||||
@className: 'TrialRequest'
|
||||
@schema: schema
|
||||
urlRoot: '/db/trial.request'
|
18
app/schemas/models/trial_request.schema.coffee
Normal file
18
app/schemas/models/trial_request.schema.coffee
Normal file
|
@ -0,0 +1,18 @@
|
|||
c = require './../schemas'
|
||||
|
||||
TrialRequestSchema = c.object {
|
||||
title: 'Trial request',
|
||||
required: ['type']
|
||||
}
|
||||
|
||||
_.extend TrialRequestSchema.properties,
|
||||
applicant: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}])
|
||||
prepaidCode: c.objectId()
|
||||
reviewDate: c.date({readOnly: true})
|
||||
reviewer: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}])
|
||||
properties: {type: 'object', description: 'Data specific to this request.'}
|
||||
status: {type: 'string', 'enum': ['submitted', 'approved', 'denied']}
|
||||
type: {type: 'string', 'enum': ['subscription']}
|
||||
|
||||
c.extendBasicProperties TrialRequestSchema, 'TrialRequest'
|
||||
module.exports = TrialRequestSchema
|
16
app/styles/admin/trial-requests.sass
Normal file
16
app/styles/admin/trial-requests.sass
Normal file
|
@ -0,0 +1,16 @@
|
|||
#admin-trial-requests-view
|
||||
|
||||
#site-content-area
|
||||
width: 100%
|
||||
|
||||
.btn-deny
|
||||
float: right
|
||||
|
||||
.status-cell
|
||||
width: 120px
|
||||
|
||||
td.created
|
||||
min-width: 90px
|
||||
|
||||
td.reviewed
|
||||
min-width: 90px
|
17
app/styles/teachers-free-trial.sass
Normal file
17
app/styles/teachers-free-trial.sass
Normal file
|
@ -0,0 +1,17 @@
|
|||
#teachers-free-trial-view
|
||||
|
||||
.input-email-address
|
||||
width: 40%
|
||||
|
||||
.input-location
|
||||
width: 40%
|
||||
|
||||
.input-heard-about
|
||||
width: 100%
|
||||
|
||||
.thanks-submit
|
||||
display: none
|
||||
|
||||
.error-message
|
||||
display: none
|
||||
color: red
|
|
@ -25,16 +25,18 @@ block content
|
|||
h4(data-i18n="admin.av_entities_sub_title") Entities
|
||||
|
||||
ul
|
||||
li
|
||||
a(href="/admin/users", data-i18n="admin.av_entities_users_url") Users
|
||||
li
|
||||
a(href="/admin/level-sessions", data-i18n="admin.av_entities_active_instances_url") Active Instances
|
||||
li
|
||||
a(href="/admin/employers", data-i18n="admin.av_entities_employer_list_url") Employer List
|
||||
li
|
||||
a(href="/admin/candidates", data-i18n="admin.av_entities_candidates_list_url") Candidate List
|
||||
li
|
||||
a(href="/admin/employers", data-i18n="admin.av_entities_employer_list_url") Employer List
|
||||
li
|
||||
a(href="/admin/trial-requests") Trial Requests
|
||||
li
|
||||
a(href="/admin/user-code-problems", data-i18n="admin.av_entities_user_code_problems_list_url") User Code Problems List
|
||||
li
|
||||
a(href="/admin/users", data-i18n="admin.av_entities_users_url") Users
|
||||
|
||||
h4(data-i18n="admin.av_other_sub_title") Other
|
||||
|
||||
|
|
52
app/templates/admin/trial-requests.jade
Normal file
52
app/templates/admin/trial-requests.jade
Normal file
|
@ -0,0 +1,52 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
if !me.isAdmin()
|
||||
div You must be logged in as an admin to view this page.
|
||||
|
||||
else
|
||||
h2 Trial Requests
|
||||
if !trialRequests || trialRequests.length < 1
|
||||
h4 Fetching trial requests...
|
||||
else
|
||||
table.table.table-condensed
|
||||
thead
|
||||
tr
|
||||
th Created
|
||||
th Reviewed
|
||||
th Applicant
|
||||
th Location
|
||||
th Age
|
||||
th Students
|
||||
th How Found
|
||||
th Status
|
||||
tbody
|
||||
- var numReviewed = 0
|
||||
- var maxReviewedShown = 100
|
||||
each trialRequest in trialRequests
|
||||
if trialRequest.get('status') !== 'submitted'
|
||||
- numReviewed++
|
||||
if numReviewed > maxReviewedShown
|
||||
- break
|
||||
tr
|
||||
td.created= new Date(parseInt(trialRequest.get('_id').substring(0, 8), 16) * 1000).toISOString().substring(0, 10)
|
||||
td.reviewed
|
||||
if trialRequest.get('reviewDate')
|
||||
span= trialRequest.get('reviewDate').substring(0, 10)
|
||||
td
|
||||
a(href="/user/#{trialRequest.get('applicant')}")= trialRequest.get('properties').email
|
||||
td= trialRequest.get('properties').location
|
||||
td= trialRequest.get('properties').age
|
||||
td= trialRequest.get('properties').numStudents
|
||||
td= trialRequest.get('properties').heardAbout
|
||||
td.status-cell
|
||||
if trialRequest.get('status') === 'submitted'
|
||||
button.btn.btn-xs.btn-success.btn-approve(data-trial-request=trialRequest) Approve
|
||||
button.btn.btn-xs.btn-danger.btn-deny(data-trial-request=trialRequest) Deny
|
||||
else if trialRequest.get('prepaidCode')
|
||||
span= trialRequest.get('prepaidCode')
|
||||
else
|
||||
span= trialRequest.get('status')
|
||||
|
||||
div *Currently assumes all trial requests of type 'subscription'
|
71
app/templates/teachers-free-trial.jade
Normal file
71
app/templates/teachers-free-trial.jade
Normal file
|
@ -0,0 +1,71 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
h2 Teacher Survey
|
||||
|
||||
if me.isAnonymous()
|
||||
p You must be logged in to apply. Please create an account or log in from the menu above.
|
||||
else if fetchingData
|
||||
h4 Retrieving information...
|
||||
else if existingRequests.length > 0
|
||||
if existingRequests[0].get('status') === 'submitted'
|
||||
p
|
||||
span.spr Your application for a free trial subscription is being
|
||||
strong reviewed
|
||||
else if existingRequests[0].get('status') === 'approved'
|
||||
p
|
||||
span.spr Your application for a free trial subscription was
|
||||
strong.spr approved
|
||||
span.spr . Further instructions have been sent to
|
||||
strong= existingRequests[0].get('properties').email
|
||||
else
|
||||
p
|
||||
span.spr Your application for a free trial subscription has been
|
||||
strong.spr denied
|
||||
p
|
||||
span.spr Please contact
|
||||
span.spr
|
||||
a(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
span if you have further questions.
|
||||
else
|
||||
p
|
||||
span.spr We offer free subscriptions to teachers for evaluation purposes. You can find more information on our
|
||||
a.spr(href='/teachers') teachers
|
||||
span page.
|
||||
p Please fill out this quick survey and we’ll email you setup instructions.
|
||||
p.container-email-address
|
||||
label.control-label Email Address
|
||||
br
|
||||
input.control-label.input-email-address(type='text', value=email)
|
||||
p.container-location
|
||||
label.control-label Name of School, City
|
||||
br
|
||||
input.control-label.input-location(type='text')
|
||||
p.container-age
|
||||
label.control-label How old are your students?
|
||||
div
|
||||
input(type="radio", name="age", value="Under 14")
|
||||
span.spl Under 14
|
||||
div
|
||||
input(type="radio", name="age", value="14-17")
|
||||
span.spl 14-17
|
||||
div
|
||||
input(type="radio", name="age", value="18+")
|
||||
span.spl 18+
|
||||
div
|
||||
input(type="radio", name="age", value='other')
|
||||
span.spl.spr Other:
|
||||
input.spr.input-age-other(type='text')
|
||||
p.container-num-students
|
||||
label.control-label How many students do you teach?
|
||||
br
|
||||
input.control-label.input-num-students(type='text')
|
||||
p.container-heard-about
|
||||
label.control-label How did you hear about CodeCombat?
|
||||
br
|
||||
textarea.control-label.input-heard-about(rows=4)
|
||||
p.error-message Please fill out all fields.
|
||||
p
|
||||
button.btn.btn-default.submit-button Submit
|
||||
p.thanks-submit Thanks! We'll send you setup instructions shortly.
|
|
@ -19,10 +19,9 @@ block content
|
|||
|
||||
h3.teachers-title(data-i18n="teachers.teacher_subs_title")
|
||||
p
|
||||
span(data-i18n="teachers.teacher_subs_1")
|
||||
span.spr.spl
|
||||
a(href='mailto:team@codecombat.com?subject=Free%20Teacher%20Subscription') team@codecombat.com
|
||||
span.spr.spl(data-i18n="teachers.teacher_subs_2")
|
||||
span.spr(data-i18n="teachers.teacher_subs_1")
|
||||
a.spr(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
|
||||
span.spr(data-i18n="teachers.teacher_subs_3")
|
||||
|
||||
h3(data-i18n="teachers.sub_includes_title")
|
||||
p(data-i18n="teachers.sub_includes_1")
|
||||
|
|
85
app/views/TeachersFreeTrialView.coffee
Normal file
85
app/views/TeachersFreeTrialView.coffee
Normal file
|
@ -0,0 +1,85 @@
|
|||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/teachers-free-trial'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
TrialRequest = require 'models/TrialRequest'
|
||||
|
||||
# TODO: distinguish between this type of existing trial requests and others
|
||||
|
||||
module.exports = class TeachersFreeTrialView extends RootView
|
||||
id: 'teachers-free-trial-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click .submit-button': 'onClickSubmit'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
@email = me.get('email')
|
||||
@refreshData()
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.email = @email
|
||||
context.existingRequests = @existingRequests.models
|
||||
context.fetchingData = @fetchingData
|
||||
context
|
||||
|
||||
refreshData: ->
|
||||
@fetchingData = true
|
||||
@existingRequests = new CocoCollection([], { url: '/db/trial.request/-/own', model: TrialRequest, comparator: '_id' })
|
||||
@listenToOnce @existingRequests, 'sync', =>
|
||||
@fetchingData = false
|
||||
@render?()
|
||||
@supermodel.loadCollection(@existingRequests, 'own_trial_requests', {cache: false})
|
||||
|
||||
onClickSubmit: (e) ->
|
||||
email = $('.input-email-address').val()
|
||||
location = $('.input-location').val()
|
||||
age = $('input[name=age]:checked').val()
|
||||
age = $('.input-age-other').val() if age is 'other'
|
||||
numStudents = $('.input-num-students').val()
|
||||
heardAbout = $('.input-heard-about').val()
|
||||
|
||||
# Validate input
|
||||
$('.container-email-address').removeClass('has-error')
|
||||
$('.container-location').removeClass('has-error')
|
||||
$('.container-age').removeClass('has-error')
|
||||
$('.container-num-students').removeClass('has-error')
|
||||
$('.container-heard-about').removeClass('has-error')
|
||||
$('.error-message').hide()
|
||||
emailPattern = /^([\w.-]+)@([\w.-]+)\.([a-zA-Z.]{2,6})$/i
|
||||
unless email?.match(emailPattern)
|
||||
$('.container-email-address').addClass('has-error')
|
||||
$('.error-message').show()
|
||||
return
|
||||
unless location
|
||||
$('.container-location').addClass('has-error')
|
||||
$('.error-message').show()
|
||||
return
|
||||
unless age
|
||||
$('.container-age').addClass('has-error')
|
||||
$('.error-message').show()
|
||||
return
|
||||
unless numStudents
|
||||
$('.container-num-students').addClass('has-error')
|
||||
$('.error-message').show()
|
||||
return
|
||||
unless heardAbout
|
||||
$('.container-heard-about').addClass('has-error')
|
||||
$('.error-message').show()
|
||||
return
|
||||
|
||||
# Save trial request
|
||||
trialRequest = new TrialRequest
|
||||
type: 'subscription'
|
||||
properties:
|
||||
email: email
|
||||
location: location
|
||||
age: age
|
||||
numStudents: numStudents
|
||||
heardAbout: heardAbout
|
||||
trialRequest.save {},
|
||||
error: (model, response, options) =>
|
||||
console.error 'Error saving trial request', response
|
||||
success: (model, response, options) =>
|
||||
@refreshData()
|
66
app/views/admin/TrialRequestsView.coffee
Normal file
66
app/views/admin/TrialRequestsView.coffee
Normal file
|
@ -0,0 +1,66 @@
|
|||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/admin/trial-requests'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
TrialRequest = require 'models/TrialRequest'
|
||||
|
||||
module.exports = class TrialRequestsView extends RootView
|
||||
id: 'admin-trial-requests-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click .btn-approve': 'onClickApprove'
|
||||
'click .btn-deny': 'onClickDeny'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
if me.isAdmin()
|
||||
sortRequests = (a, b) ->
|
||||
statusA = a.get('status')
|
||||
statusB = b.get('status')
|
||||
if statusA is 'submitted' and statusB is 'submitted'
|
||||
if a.get('_id') < b.get('_id')
|
||||
-1
|
||||
else
|
||||
1
|
||||
else if statusA is 'submitted'
|
||||
-1
|
||||
else if statusB is 'submitted'
|
||||
1
|
||||
else if not b.get('reviewDate') or a.get('reviewDate') > b.get('reviewDate')
|
||||
-1
|
||||
else
|
||||
1
|
||||
@trialRequests = new CocoCollection([], { url: '/db/trial.request', model: TrialRequest, comparator: sortRequests })
|
||||
@supermodel.loadCollection(@trialRequests, 'trial-requests', {cache: false})
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.trialRequests = @trialRequests?.models ? []
|
||||
context
|
||||
|
||||
onClickApprove: (e) ->
|
||||
trialRequestData = $(e.target).data('trial-request')
|
||||
trialRequest = _.find @trialRequests.models, (a) -> a.get('_id') is trialRequestData._id
|
||||
unless trialRequest
|
||||
console.error 'Could not find trial request model for', trialRequestData
|
||||
return
|
||||
trialRequest.set('status', 'approved')
|
||||
trialRequest.patch
|
||||
error: (model, response, options) =>
|
||||
console.error 'Error patching trial request', response
|
||||
success: (model, response, options) =>
|
||||
@render?()
|
||||
|
||||
onClickDeny: (e) ->
|
||||
trialRequestData = $(e.target).data('trial-request')
|
||||
trialRequest = _.find @trialRequests.models, (a) -> a.get('_id') is trialRequestData._id
|
||||
unless trialRequest
|
||||
console.error 'Could not find trial request model for', trialRequestData
|
||||
return
|
||||
return unless window.confirm("Deny #{trialRequest.get('properties').email}?")
|
||||
trialRequest.set('status', 'denied')
|
||||
trialRequest.patch
|
||||
error: (model, response, options) =>
|
||||
console.error 'Error patching trial request', response
|
||||
success: (model, response, options) =>
|
||||
@render?()
|
|
@ -28,7 +28,6 @@ ClanHandler = class ClanHandler extends Handler
|
|||
false
|
||||
|
||||
makeNewInstance: (req) ->
|
||||
userName = req.user.get('name') ? 'Anoner'
|
||||
instance = super(req)
|
||||
instance.set 'ownerID', req.user._id
|
||||
instance.set 'members', [req.user._id]
|
||||
|
|
|
@ -25,6 +25,7 @@ module.exports.handlers =
|
|||
'poll': 'polls/poll_handler'
|
||||
'prepaid': 'prepaids/prepaid_handler'
|
||||
'subscription': 'payments/subscription_handler'
|
||||
'trial_request': 'trial_requests/trial_request_handler'
|
||||
'user_polls_record': 'polls/user_polls_record_handler'
|
||||
|
||||
module.exports.routes =
|
||||
|
|
|
@ -2,4 +2,13 @@ mongoose = require 'mongoose'
|
|||
config = require '../../server_config'
|
||||
PrepaidSchema = new mongoose.Schema {}, {strict: false, minimize: false,read:config.mongo.readpref}
|
||||
|
||||
PrepaidSchema.statics.generateNewCode = (done) ->
|
||||
tryCode = ->
|
||||
code = _.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8).join('')
|
||||
Prepaid.findOne code: code, (err, prepaid) ->
|
||||
return done() if err
|
||||
return done(code) unless prepaid
|
||||
tryCode()
|
||||
tryCode()
|
||||
|
||||
module.exports = Prepaid = mongoose.model('prepaid', PrepaidSchema)
|
||||
|
|
|
@ -20,7 +20,7 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
createPrepaid: (req, res) ->
|
||||
return @sendForbiddenError(res) unless @hasAccess(req)
|
||||
return @sendForbiddenError(res) unless req.body.type is 'subscription'
|
||||
@generateNewCode (code) =>
|
||||
Prepaid.generateNewCode (code) =>
|
||||
return @sendDatabaseError(res, 'Database error.') unless code
|
||||
prepaid = new Prepaid
|
||||
creator: req.user.id
|
||||
|
@ -33,13 +33,4 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, prepaid.toObject())
|
||||
|
||||
generateNewCode: (done) ->
|
||||
tryCode = ->
|
||||
code = _.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8).join('')
|
||||
Prepaid.findOne code: code, (err, prepaid) ->
|
||||
return if err
|
||||
return done(code) unless prepaid
|
||||
tryCode()
|
||||
tryCode()
|
||||
|
||||
module.exports = new PrepaidHandler()
|
||||
|
|
|
@ -11,6 +11,7 @@ if config.unittest
|
|||
module.exports.api.send = ->
|
||||
module.exports.templates =
|
||||
parent_subscribe_email: 'tem_2APERafogvwKhmcnouigud'
|
||||
setup_free_sub_email: 'tem_sqdvLCZRwoDQc6jAf5RrQE'
|
||||
share_progress_email: 'tem_VHE3ihhGmVa3727qds9zY8'
|
||||
welcome_email: 'utnGaBHuSU4Hmsi7qrAypU'
|
||||
ladder_update_email: 'JzaZxf39A4cKMxpPZUfWy4'
|
||||
|
|
59
server/trial_requests/TrialRequest.coffee
Normal file
59
server/trial_requests/TrialRequest.coffee
Normal file
|
@ -0,0 +1,59 @@
|
|||
log = require 'winston'
|
||||
mongoose = require 'mongoose'
|
||||
config = require '../../server_config'
|
||||
hipchat = require '../hipchat'
|
||||
sendwithus = require '../sendwithus'
|
||||
Prepaid = require '../prepaids/Prepaid'
|
||||
jsonSchema = require '../../app/schemas/models/trial_request.schema'
|
||||
|
||||
TrialRequestSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
|
||||
|
||||
TrialRequestSchema.pre 'save', (next) ->
|
||||
return next() unless @get('status') is 'approved'
|
||||
Prepaid.generateNewCode (code) =>
|
||||
unless code
|
||||
log.error "Trial request pre save prepaid gen new code failure"
|
||||
return next()
|
||||
prepaid = new Prepaid
|
||||
creator: @get('reviewer')
|
||||
type: 'subscription'
|
||||
status: 'active'
|
||||
code: code
|
||||
properties:
|
||||
couponID: 'free'
|
||||
prepaid.save (err) =>
|
||||
if err
|
||||
log.error "Trial request prepaid creation error: #{err}"
|
||||
@set('prepaidCode', code)
|
||||
next()
|
||||
|
||||
TrialRequestSchema.post 'save', (doc) ->
|
||||
if doc.get('status') is 'submitted'
|
||||
msg = "http://codecombat.com/admin/trial-requests #{doc.get('type')} request submitted by #{doc.get('properties').email}"
|
||||
hipchat.sendHipChatMessage msg, ['tower']
|
||||
else if doc.get('status') is 'approved'
|
||||
ppc = doc.get('prepaidCode')
|
||||
unless ppc
|
||||
log.error 'Trial request post save no ppc'
|
||||
return
|
||||
emailParams =
|
||||
recipient:
|
||||
address: doc.get('properties')?.email
|
||||
email_id: sendwithus.templates.setup_free_sub_email
|
||||
email_data:
|
||||
url: "https://codecombat.com/account/subscription?_ppc=#{ppc}";
|
||||
sendwithus.api.send emailParams, (err, result) =>
|
||||
log.error "sendwithus trial request approved error: #{err}, result: #{result}" if err
|
||||
|
||||
TrialRequestSchema.statics.privateProperties = []
|
||||
TrialRequestSchema.statics.editableProperties = [
|
||||
'prepaidCode'
|
||||
'properties'
|
||||
'reviewDate'
|
||||
'reviewer'
|
||||
'status'
|
||||
'type'
|
||||
]
|
||||
|
||||
TrialRequestSchema.statics.jsonSchema = jsonSchema
|
||||
module.exports = TrialRequest = mongoose.model 'trial.request', TrialRequestSchema, 'trial.requests'
|
40
server/trial_requests/trial_request_handler.coffee
Normal file
40
server/trial_requests/trial_request_handler.coffee
Normal file
|
@ -0,0 +1,40 @@
|
|||
async = require 'async'
|
||||
log = require 'winston'
|
||||
mongoose = require 'mongoose'
|
||||
Handler = require '../commons/Handler'
|
||||
TrialRequest = require './TrialRequest'
|
||||
|
||||
TrialRequestHandler = class TrialRequestHandler extends Handler
|
||||
modelClass: TrialRequest
|
||||
jsonSchema: require '../../app/schemas/models/trial_request.schema'
|
||||
|
||||
hasAccess: (req) ->
|
||||
req.method in ['POST'] or req.user?.isAdmin()
|
||||
|
||||
hasAccessToDocument: (req, document, method=null) ->
|
||||
return false unless document?
|
||||
return true if req.user?.isAdmin()
|
||||
false
|
||||
|
||||
makeNewInstance: (req) ->
|
||||
instance = super(req)
|
||||
instance.set 'applicant', req.user._id
|
||||
instance.set 'status', 'submitted'
|
||||
instance
|
||||
|
||||
put: (req, res, id) ->
|
||||
req.body.reviewDate = new Date()
|
||||
req.body.reviewer = req.user.get('_id')
|
||||
super(req, res, id)
|
||||
|
||||
getByRelationship: (req, res, args...) ->
|
||||
return @getOwn(req, res) if args[1] is 'own'
|
||||
super(arguments...)
|
||||
|
||||
getOwn: (req, res) ->
|
||||
return @sendForbiddenError(res) unless req.user?
|
||||
TrialRequest.find {applicant: req.user.get('_id')}, (err, documents) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, documents)
|
||||
|
||||
module.exports = new TrialRequestHandler()
|
|
@ -41,6 +41,7 @@ models_path = [
|
|||
'../../server/achievements/EarnedAchievement'
|
||||
'../../server/payments/Payment'
|
||||
'../../server/prepaids/Prepaid'
|
||||
'../../server/trial_requests/TrialRequest'
|
||||
]
|
||||
|
||||
for m in models_path
|
||||
|
|
|
@ -4,7 +4,6 @@ utils = require '../../../app/core/utils' # Must come after require /common
|
|||
mongoose = require 'mongoose'
|
||||
|
||||
describe 'Clans', ->
|
||||
stripe = require('stripe')(config.stripe.secretKey)
|
||||
clanURL = getURL('/db/clan')
|
||||
userURL = getURL('/db/user')
|
||||
|
||||
|
|
162
test/server/functional/trial_request.spec.coffee
Normal file
162
test/server/functional/trial_request.spec.coffee
Normal file
|
@ -0,0 +1,162 @@
|
|||
require '../common'
|
||||
|
||||
describe 'Trial Requests', ->
|
||||
URL = getURL('/db/trial.request')
|
||||
ownURL = getURL('/db/trial.request/-/own')
|
||||
|
||||
createTrialRequest = (user, type, properties, done) ->
|
||||
requestBody =
|
||||
type: type
|
||||
properties: properties
|
||||
request.post {uri: URL, json: requestBody }, (err, res, body) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(body.type).toEqual(type)
|
||||
expect(body.properties).toEqual(properties)
|
||||
expect(body.applicant).toEqual(user.id)
|
||||
expect(body.status).toEqual('submitted')
|
||||
TrialRequest.findById body._id, (err, doc) ->
|
||||
expect(err).toBeNull()
|
||||
expect(doc.get('type')).toEqual(type)
|
||||
expect(doc.get('properties')).toEqual(properties)
|
||||
expect(doc.get('applicant')).toEqual(user._id)
|
||||
expect(doc.get('status')).toEqual('submitted')
|
||||
done(doc)
|
||||
|
||||
it 'Clear database', (done) ->
|
||||
clearModels [User, TrialRequest], (err) ->
|
||||
throw err if err
|
||||
done()
|
||||
|
||||
it 'Create trial request', (done) ->
|
||||
loginNewUser (user) ->
|
||||
properties =
|
||||
email: user.get('email')
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
createTrialRequest user, 'subscription', properties, (trialRequest) ->
|
||||
done()
|
||||
|
||||
it 'Get trial requests, non-admin', (done) ->
|
||||
loginNewUser (user) ->
|
||||
properties =
|
||||
email: user.get('email')
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
createTrialRequest user, 'subscription', properties, (trialRequest) ->
|
||||
request.get URL, (err, res, body) ->
|
||||
expect(res.statusCode).toEqual(403)
|
||||
done()
|
||||
|
||||
it 'Get trial requests, admin', (done) ->
|
||||
loginNewUser (user) ->
|
||||
properties =
|
||||
email: user.get('email')
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
createTrialRequest user, 'subscription', properties, (trialRequest) ->
|
||||
loginNewUser (admin) ->
|
||||
admin.set('permissions', ['admin'])
|
||||
admin.save (err, user) ->
|
||||
request.get URL, (err, res, body) ->
|
||||
expect(res.statusCode).toEqual(200)
|
||||
expect(body.length).toBeGreaterThan(0)
|
||||
done()
|
||||
|
||||
it 'Get own trial requests', (done) ->
|
||||
loginNewUser (user) ->
|
||||
properties =
|
||||
email: user.get('email')
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
createTrialRequest user, 'subscription', properties, (trialRequest) ->
|
||||
request.get ownURL, (err, res, body) ->
|
||||
expect(res.statusCode).toEqual(200)
|
||||
ownRequests = JSON.parse(body)
|
||||
expect(ownRequests.length).toEqual(1)
|
||||
expect(ownRequests[0]._id).toEqual(trialRequest.id)
|
||||
done()
|
||||
|
||||
it 'Get non-owned trial request, non-admin', (done) ->
|
||||
loginNewUser (user) ->
|
||||
properties =
|
||||
email: user.get('email')
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
createTrialRequest user, 'subscription', properties, (trialRequest) ->
|
||||
loginNewUser (user2) ->
|
||||
request.get URL + "/#{trialRequest.id}", (err, res, body) ->
|
||||
expect(res.statusCode).toEqual(403)
|
||||
done()
|
||||
|
||||
it 'Approve trial request', (done) ->
|
||||
loginNewUser (user) ->
|
||||
properties =
|
||||
email: user.get('email')
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
createTrialRequest user, 'subscription', properties, (trialRequest) ->
|
||||
loginNewUser (admin) ->
|
||||
admin.set('permissions', ['admin'])
|
||||
admin.save (err, user) ->
|
||||
requestBody = trialRequest.toObject()
|
||||
requestBody.status = 'approved'
|
||||
request.put {uri: URL, json: requestBody }, (err, res, body) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(body.status).toEqual('approved')
|
||||
expect(body.reviewDate).toBeDefined()
|
||||
expect(new Date(body.reviewDate)).toBeLessThan(new Date())
|
||||
expect(body.reviewer).toEqual(admin.id)
|
||||
expect(body.prepaidCode).toBeDefined()
|
||||
TrialRequest.findById body._id, (err, doc) ->
|
||||
expect(err).toBeNull()
|
||||
expect(doc.get('status')).toEqual('approved')
|
||||
expect(doc.get('reviewDate')).toBeDefined()
|
||||
expect(new Date(doc.get('reviewDate'))).toBeLessThan(new Date())
|
||||
expect(doc.get('reviewer')).toEqual(admin._id)
|
||||
expect(doc.get('prepaidCode')).toBeDefined()
|
||||
done()
|
||||
|
||||
it 'Deny trial request', (done) ->
|
||||
loginNewUser (user) ->
|
||||
properties =
|
||||
email: user.get('email')
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
createTrialRequest user, 'subscription', properties, (trialRequest) ->
|
||||
loginNewUser (admin) ->
|
||||
admin.set('permissions', ['admin'])
|
||||
admin.save (err, user) ->
|
||||
requestBody = trialRequest.toObject()
|
||||
requestBody.status = 'denied'
|
||||
request.put {uri: URL, json: requestBody }, (err, res, body) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(body.status).toEqual('denied')
|
||||
expect(body.reviewDate).toBeDefined()
|
||||
expect(new Date(body.reviewDate)).toBeLessThan(new Date())
|
||||
expect(body.reviewer).toEqual(admin.id)
|
||||
expect(body.prepaidCode).not.toBeDefined()
|
||||
TrialRequest.findById body._id, (err, doc) ->
|
||||
expect(err).toBeNull()
|
||||
expect(doc.get('status')).toEqual('denied')
|
||||
expect(doc.get('reviewDate')).toBeDefined()
|
||||
expect(new Date(doc.get('reviewDate'))).toBeLessThan(new Date())
|
||||
expect(doc.get('reviewer')).toEqual(admin._id)
|
||||
expect(doc.get('prepaidCode')).not.toBeDefined()
|
||||
done()
|
Loading…
Reference in a new issue