mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-12 08:41:46 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
b837de2394
14 changed files with 172 additions and 6 deletions
|
@ -55,7 +55,7 @@ module.exports = FacebookHandler = class FacebookHandler extends CocoClass
|
||||||
js = d.createElement('script')
|
js = d.createElement('script')
|
||||||
js.id = id
|
js.id = id
|
||||||
js.async = true
|
js.async = true
|
||||||
js.src = '//connect.facebook.net/en_US/all.js'
|
js.src = '//connect.facebook.net/en_US/sdk.js'
|
||||||
|
|
||||||
#js.src = '//connect.facebook.net/en_US/all/debug.js'
|
#js.src = '//connect.facebook.net/en_US/all/debug.js'
|
||||||
ref.parentNode.insertBefore js, ref
|
ref.parentNode.insertBefore js, ref
|
||||||
|
@ -63,12 +63,13 @@ module.exports = FacebookHandler = class FacebookHandler extends CocoClass
|
||||||
)(document)
|
)(document)
|
||||||
|
|
||||||
window.fbAsyncInit = =>
|
window.fbAsyncInit = =>
|
||||||
FB.init
|
FB.init({
|
||||||
appId: (if document.location.origin is 'http://localhost:3000' then '607435142676437' else '148832601965463') # App ID
|
appId: (if document.location.origin is 'http://localhost:3000' then '607435142676437' else '148832601965463') # App ID
|
||||||
channelUrl: document.location.origin + '/channel.html' # Channel File
|
channelUrl: document.location.origin + '/channel.html' # Channel File
|
||||||
cookie: true # enable cookies to allow the server to access the session
|
cookie: true # enable cookies to allow the server to access the session
|
||||||
xfbml: true # parse XFBML
|
xfbml: true # parse XFBML
|
||||||
|
version: 'v2.7'
|
||||||
|
})
|
||||||
FB.getLoginStatus (response) =>
|
FB.getLoginStatus (response) =>
|
||||||
if response.status is 'connected'
|
if response.status is 'connected'
|
||||||
@connected = true
|
@connected = true
|
||||||
|
|
|
@ -287,6 +287,7 @@
|
||||||
email_good: "Email looks good!"
|
email_good: "Email looks good!"
|
||||||
name_taken: "Username already taken! Try {{suggestedName}}?"
|
name_taken: "Username already taken! Try {{suggestedName}}?"
|
||||||
name_available: "Username available!"
|
name_available: "Username available!"
|
||||||
|
name_is_email: "Username may not be an email"
|
||||||
choose_type: "Choose your account type:"
|
choose_type: "Choose your account type:"
|
||||||
teacher_type_1: "Teach programming using CodeCombat!"
|
teacher_type_1: "Teach programming using CodeCombat!"
|
||||||
teacher_type_2: "Set up your class"
|
teacher_type_2: "Set up your class"
|
||||||
|
|
|
@ -354,6 +354,16 @@ module.exports = class User extends CocoModel
|
||||||
options.type = 'PUT'
|
options.type = 'PUT'
|
||||||
@fetch(options)
|
@fetch(options)
|
||||||
|
|
||||||
|
destudent: (options={}) ->
|
||||||
|
options.url = _.result(@, 'url') + '/destudent'
|
||||||
|
options.type = 'POST'
|
||||||
|
@fetch(options)
|
||||||
|
|
||||||
|
deteacher: (options={}) ->
|
||||||
|
options.url = _.result(@, 'url') + '/deteacher'
|
||||||
|
options.type = 'POST'
|
||||||
|
@fetch(options)
|
||||||
|
|
||||||
tiersByLevel = [-1, 0, 0.05, 0.14, 0.18, 0.32, 0.41, 0.5, 0.64, 0.82, 0.91, 1.04, 1.22, 1.35, 1.48, 1.65, 1.78, 1.96, 2.1, 2.24, 2.38, 2.55, 2.69, 2.86, 3.03, 3.16, 3.29, 3.42, 3.58, 3.74, 3.89, 4.04, 4.19, 4.32, 4.47, 4.64, 4.79, 4.96,
|
tiersByLevel = [-1, 0, 0.05, 0.14, 0.18, 0.32, 0.41, 0.5, 0.64, 0.82, 0.91, 1.04, 1.22, 1.35, 1.48, 1.65, 1.78, 1.96, 2.1, 2.24, 2.38, 2.55, 2.69, 2.86, 3.03, 3.16, 3.29, 3.42, 3.58, 3.74, 3.89, 4.04, 4.19, 4.32, 4.47, 4.64, 4.79, 4.96,
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 13.5, 14, 14.5, 15
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 13.5, 14, 14.5, 15
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,6 +3,12 @@ extends /templates/core/modal-base-flat
|
||||||
// DNT
|
// DNT
|
||||||
|
|
||||||
block modal-header-content
|
block modal-header-content
|
||||||
|
.pull-right
|
||||||
|
if view.user.isStudent()
|
||||||
|
button#destudent-btn.btn.btn-burgandy Destudent
|
||||||
|
if view.user.isTeacher()
|
||||||
|
button#deteacher-btn.btn.btn-burgandy Deteacher
|
||||||
|
|
||||||
h3 Administer User
|
h3 Administer User
|
||||||
h4 #{view.user.get('name') || 'Unnamed'} / #{view.user.get('email')}
|
h4 #{view.user.get('name') || 'Unnamed'} / #{view.user.get('email')}
|
||||||
span= view.user.id
|
span= view.user.id
|
||||||
|
|
|
@ -231,7 +231,7 @@ module.exports = class AccountSettingsView extends CocoView
|
||||||
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
|
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
|
||||||
else
|
else
|
||||||
noty
|
noty
|
||||||
text: res.responseText
|
text: res.responseJSON?.message or res.responseText
|
||||||
type: 'error'
|
type: 'error'
|
||||||
layout: 'topCenter'
|
layout: 'topCenter'
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
|
|
|
@ -13,6 +13,8 @@ module.exports = class AdministerUserModal extends ModalView
|
||||||
events:
|
events:
|
||||||
'click #save-changes': 'onClickSaveChanges'
|
'click #save-changes': 'onClickSaveChanges'
|
||||||
'click #add-seats-btn': 'onClickAddSeatsButton'
|
'click #add-seats-btn': 'onClickAddSeatsButton'
|
||||||
|
'click #destudent-btn': 'onClickDestudentButton'
|
||||||
|
'click #deteacher-btn': 'onClickDeteacherButton'
|
||||||
|
|
||||||
initialize: (options, @userHandle) ->
|
initialize: (options, @userHandle) ->
|
||||||
@user = new User({_id:@userHandle})
|
@user = new User({_id:@userHandle})
|
||||||
|
@ -71,3 +73,33 @@ module.exports = class AdministerUserModal extends ModalView
|
||||||
@listenTo prepaid, 'sync', ->
|
@listenTo prepaid, 'sync', ->
|
||||||
@state = 'made-prepaid'
|
@state = 'made-prepaid'
|
||||||
@renderSelectors('#prepaid-form')
|
@renderSelectors('#prepaid-form')
|
||||||
|
|
||||||
|
onClickDestudentButton: (e) ->
|
||||||
|
button = $(e.currentTarget)
|
||||||
|
button.attr('disabled', true).text('...')
|
||||||
|
Promise.resolve(@user.destudent())
|
||||||
|
.then =>
|
||||||
|
button.remove()
|
||||||
|
.catch (e) =>
|
||||||
|
button.attr('disabled', false).text('Destudent')
|
||||||
|
noty {
|
||||||
|
text: e.message or e.responseJSON?.message or e.responseText or 'Unknown Error'
|
||||||
|
type: 'error'
|
||||||
|
}
|
||||||
|
if e.stack
|
||||||
|
throw e
|
||||||
|
|
||||||
|
onClickDeteacherButton: (e) ->
|
||||||
|
button = $(e.currentTarget)
|
||||||
|
button.attr('disabled', true).text('...')
|
||||||
|
Promise.resolve(@user.deteacher())
|
||||||
|
.then =>
|
||||||
|
button.remove()
|
||||||
|
.catch (e) =>
|
||||||
|
button.attr('disabled', false).text('Destudent')
|
||||||
|
noty {
|
||||||
|
text: e.message or e.responseJSON?.message or e.responseText or 'Unknown Error'
|
||||||
|
type: 'error'
|
||||||
|
}
|
||||||
|
if e.stack
|
||||||
|
throw e
|
||||||
|
|
|
@ -146,6 +146,11 @@ module.exports = class BasicInfoView extends CocoView
|
||||||
})
|
})
|
||||||
|
|
||||||
forms.clearFormAlerts(@$el)
|
forms.clearFormAlerts(@$el)
|
||||||
|
|
||||||
|
if data.name and forms.validateEmail(data.name)
|
||||||
|
forms.setErrorToProperty(@$el, 'name', $.i18n.t('signup.name_is_email'))
|
||||||
|
return false
|
||||||
|
|
||||||
res = tv4.validateMultiple data, @formSchema()
|
res = tv4.validateMultiple data, @formSchema()
|
||||||
forms.applyErrorsToForm(@$('form'), res.errors) unless res.valid
|
forms.applyErrorsToForm(@$('form'), res.errors) unless res.valid
|
||||||
return res.valid
|
return res.valid
|
||||||
|
|
|
@ -143,7 +143,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
|
|
||||||
onLevelLoaded: (e) ->
|
onLevelLoaded: (e) ->
|
||||||
@god = new God({@gameUIState}) unless e.level.isType('web-dev')
|
@god = new God({@gameUIState}) unless e.level.isType('web-dev')
|
||||||
@setUpGod() if @waitingToSetUpGod
|
@setupGod() if @waitingToSetUpGod
|
||||||
|
|
||||||
trackLevelLoadEnd: ->
|
trackLevelLoadEnd: ->
|
||||||
return if @isEditorPreview
|
return if @isEditorPreview
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// Unset someone with a student role. Remove from classrooms, unset role.
|
// Unset someone with a student role. Remove from classrooms, unset role.
|
||||||
|
|
||||||
// Usage
|
// Usage
|
||||||
|
|
|
@ -66,4 +66,5 @@ module.exports.modules = modules = # by collection name
|
||||||
'users': 'User'
|
'users': 'User'
|
||||||
|
|
||||||
mongoose.modelNameByCollection = (collection) ->
|
mongoose.modelNameByCollection = (collection) ->
|
||||||
|
return require('../models/LevelSession') if collection is 'level.sessions'
|
||||||
mongoose.model modules[collection] if collection of modules
|
mongoose.model modules[collection] if collection of modules
|
||||||
|
|
|
@ -8,9 +8,11 @@ Promise = require 'bluebird'
|
||||||
parse = require '../commons/parse'
|
parse = require '../commons/parse'
|
||||||
request = require 'request'
|
request = require 'request'
|
||||||
mongoose = require 'mongoose'
|
mongoose = require 'mongoose'
|
||||||
|
database = require '../commons/database'
|
||||||
sendwithus = require '../sendwithus'
|
sendwithus = require '../sendwithus'
|
||||||
User = require '../models/User'
|
User = require '../models/User'
|
||||||
Classroom = require '../models/Classroom'
|
Classroom = require '../models/Classroom'
|
||||||
|
CourseInstance = require '../models/CourseInstance'
|
||||||
facebook = require '../lib/facebook'
|
facebook = require '../lib/facebook'
|
||||||
gplus = require '../lib/gplus'
|
gplus = require '../lib/gplus'
|
||||||
TrialRequest = require '../models/TrialRequest'
|
TrialRequest = require '../models/TrialRequest'
|
||||||
|
@ -211,3 +213,44 @@ module.exports =
|
||||||
yield trialRequest.update({$unset: {applicant: ''}})
|
yield trialRequest.update({$unset: {applicant: ''}})
|
||||||
|
|
||||||
res.status(200).send(req.user.toObject({req: req}))
|
res.status(200).send(req.user.toObject({req: req}))
|
||||||
|
|
||||||
|
destudent: wrap (req, res) ->
|
||||||
|
user = yield database.getDocFromHandle(req, User)
|
||||||
|
if not user
|
||||||
|
throw new errors.NotFound('User not found.')
|
||||||
|
|
||||||
|
if not user.isStudent()
|
||||||
|
return res.status(200).send(user.toObject({req: req}))
|
||||||
|
|
||||||
|
yield Classroom.update(
|
||||||
|
{ members: user._id },
|
||||||
|
{ $pull: {members: user._id} },
|
||||||
|
{ multi: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
yield CourseInstance.update(
|
||||||
|
{ members: user._id },
|
||||||
|
{ $pull: {members: user._id} },
|
||||||
|
{ multi: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
yield user.update({ $unset: {role: ''}})
|
||||||
|
user.set('role', undefined)
|
||||||
|
return res.status(200).send(user.toObject({req: req}))
|
||||||
|
|
||||||
|
|
||||||
|
deteacher: wrap (req, res) ->
|
||||||
|
user = yield database.getDocFromHandle(req, User)
|
||||||
|
if not user
|
||||||
|
throw new errors.NotFound('User not found.')
|
||||||
|
|
||||||
|
if not user.isTeacher()
|
||||||
|
return res.status(200).send(user.toObject({req: req}))
|
||||||
|
|
||||||
|
yield TrialRequest.remove(
|
||||||
|
{ applicant: user._id },
|
||||||
|
)
|
||||||
|
|
||||||
|
yield user.update({ $unset: {role: ''}})
|
||||||
|
user.set('role', undefined)
|
||||||
|
return res.status(200).send(user.toObject({req: req}))
|
||||||
|
|
|
@ -361,6 +361,10 @@ UserSchema.pre('save', (next) ->
|
||||||
@set('email', undefined)
|
@set('email', undefined)
|
||||||
@set('emailLower', undefined)
|
@set('emailLower', undefined)
|
||||||
if name = @get('name')
|
if name = @get('name')
|
||||||
|
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i # https://news.ycombinator.com/item?id=5763990
|
||||||
|
if filter.test(name)
|
||||||
|
return next(new errors.UnprocessableEntity('Name may not be an email'))
|
||||||
|
|
||||||
@set('nameLower', name.toLowerCase())
|
@set('nameLower', name.toLowerCase())
|
||||||
else
|
else
|
||||||
@set('name', undefined)
|
@set('name', undefined)
|
||||||
|
|
|
@ -104,6 +104,8 @@ module.exports.setup = (app) ->
|
||||||
app.post('/db/user/:handle/signup-with-facebook', mw.users.signupWithFacebook)
|
app.post('/db/user/:handle/signup-with-facebook', mw.users.signupWithFacebook)
|
||||||
app.post('/db/user/:handle/signup-with-gplus', mw.users.signupWithGPlus)
|
app.post('/db/user/:handle/signup-with-gplus', mw.users.signupWithGPlus)
|
||||||
app.post('/db/user/:handle/signup-with-password', mw.users.signupWithPassword)
|
app.post('/db/user/:handle/signup-with-password', mw.users.signupWithPassword)
|
||||||
|
app.post('/db/user/:handle/destudent', mw.auth.checkHasPermission(['admin']), mw.users.destudent)
|
||||||
|
app.post('/db/user/:handle/deteacher', mw.auth.checkHasPermission(['admin']), mw.users.deteacher)
|
||||||
|
|
||||||
app.get('/db/prepaid', mw.auth.checkLoggedIn(), mw.prepaids.fetchByCreator)
|
app.get('/db/prepaid', mw.auth.checkLoggedIn(), mw.prepaids.fetchByCreator)
|
||||||
app.get('/db/prepaid/-/active-schools', mw.auth.checkHasPermission(['admin']), mw.prepaids.fetchActiveSchools)
|
app.get('/db/prepaid/-/active-schools', mw.auth.checkHasPermission(['admin']), mw.prepaids.fetchActiveSchools)
|
||||||
|
|
|
@ -3,6 +3,9 @@ utils = require '../utils'
|
||||||
urlUser = '/db/user'
|
urlUser = '/db/user'
|
||||||
User = require '../../../server/models/User'
|
User = require '../../../server/models/User'
|
||||||
Classroom = require '../../../server/models/Classroom'
|
Classroom = require '../../../server/models/Classroom'
|
||||||
|
CourseInstance = require '../../../server/models/CourseInstance'
|
||||||
|
Course = require '../../../server/models/Course'
|
||||||
|
Campaign = require '../../../server/models/Campaign'
|
||||||
TrialRequest = require '../../../server/models/TrialRequest'
|
TrialRequest = require '../../../server/models/TrialRequest'
|
||||||
Prepaid = require '../../../server/models/Prepaid'
|
Prepaid = require '../../../server/models/Prepaid'
|
||||||
request = require '../request'
|
request = require '../request'
|
||||||
|
@ -970,3 +973,62 @@ describe 'POST /db/user/:handle/signup-with-gplus', ->
|
||||||
expect(res.statusCode).toBe(409)
|
expect(res.statusCode).toBe(409)
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
describe 'POST /db/user/:handle/destudent', ->
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
|
yield utils.clearModels([User, Classroom, CourseInstance, Course, Campaign])
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'removes a student user from all classrooms and unsets their role property', utils.wrap (done) ->
|
||||||
|
student1 = yield utils.initUser({role: 'student'})
|
||||||
|
student2 = yield utils.initUser({role: 'student'})
|
||||||
|
members = [student1._id, student2._id]
|
||||||
|
|
||||||
|
classroom = new Classroom({members})
|
||||||
|
yield classroom.save()
|
||||||
|
courseInstance = new CourseInstance({members})
|
||||||
|
yield courseInstance.save()
|
||||||
|
|
||||||
|
admin = yield utils.initAdmin()
|
||||||
|
yield utils.loginUser(admin)
|
||||||
|
|
||||||
|
url = getURL("/db/user/#{student1.id}/destudent")
|
||||||
|
[res, body] = yield request.postAsync({url, json:true})
|
||||||
|
|
||||||
|
student1 = yield User.findById(student1.id)
|
||||||
|
student2 = yield User.findById(student2.id)
|
||||||
|
classroom = yield Classroom.findById(classroom.id)
|
||||||
|
courseInstance = yield CourseInstance.findById(courseInstance.id)
|
||||||
|
|
||||||
|
expect(student1.get('role')).toBeUndefined()
|
||||||
|
expect(student2.get('role')).toBe('student')
|
||||||
|
expect(classroom.get('members').length).toBe(1)
|
||||||
|
expect(classroom.get('members')[0].toString()).toBe(student2.id)
|
||||||
|
expect(courseInstance.get('members').length).toBe(1)
|
||||||
|
expect(courseInstance.get('members')[0].toString()).toBe(student2.id)
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe 'POST /db/user/:handle/deteacher', ->
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
|
yield utils.clearModels([User, TrialRequest])
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'removes a student user from all classrooms and unsets their role property', utils.wrap (done) ->
|
||||||
|
teacher = yield utils.initUser({role: 'teacher'})
|
||||||
|
yield utils.loginUser(teacher)
|
||||||
|
trialRequest = yield utils.makeTrialRequest(teacher)
|
||||||
|
|
||||||
|
admin = yield utils.initAdmin()
|
||||||
|
yield utils.loginUser(admin)
|
||||||
|
|
||||||
|
trialRequest = yield TrialRequest.findById(trialRequest.id)
|
||||||
|
expect(trialRequest).toBeDefined()
|
||||||
|
expect(teacher.get('role')).toBe('teacher')
|
||||||
|
|
||||||
|
url = getURL("/db/user/#{teacher.id}/deteacher")
|
||||||
|
[res, body] = yield request.postAsync({url, json:true})
|
||||||
|
|
||||||
|
trialRequest = yield TrialRequest.findById(trialRequest.id)
|
||||||
|
expect(trialRequest).toBeNull()
|
||||||
|
teacher = yield User.findById(teacher.id)
|
||||||
|
expect(teacher.get('role')).toBeUndefined()
|
||||||
|
done()
|
||||||
|
|
Loading…
Reference in a new issue