mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-13 21:44:40 -04:00
Merge iDEAL link for nl on home
This commit is contained in:
commit
13928884b0
40 changed files with 697 additions and 728 deletions
app
collections
core
models
schemas/subscriptions
styles
templates
views
server
articles
commons
middleware
models
routes
trial_requests
users
spec/server
test/app
|
@ -7,5 +7,5 @@ module.exports = class TrialRequestCollection extends CocoCollection
|
|||
|
||||
fetchOwn: (options) ->
|
||||
options = _.extend({data: {}}, options)
|
||||
options.url = _.result(@, 'url') + '/-/own'
|
||||
options.data.applicant = me.id
|
||||
@fetch(options)
|
||||
|
|
|
@ -5,7 +5,6 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
initialize: ->
|
||||
# http://nerds.airbnb.com/how-to-add-google-analytics-page-tracking-to-57536
|
||||
@bind 'route', @_trackPageView
|
||||
Backbone.Mediator.subscribe 'auth:gplus-api-loaded', @onGPlusAPILoaded, @
|
||||
Backbone.Mediator.subscribe 'router:navigate', @onNavigate, @
|
||||
@initializeSocialMediaServices = _.once @initializeSocialMediaServices
|
||||
|
||||
|
@ -170,7 +169,6 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
$('#page-container').empty().append view.el
|
||||
window.currentView = view
|
||||
@activateTab()
|
||||
@renderLoginButtons() if view.usesSocialMedia
|
||||
view.afterInsert()
|
||||
view.didReappear()
|
||||
|
||||
|
@ -187,21 +185,19 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
$('body')[0].scrollTop = 0
|
||||
), 10
|
||||
|
||||
onGPlusAPILoaded: =>
|
||||
@renderLoginButtons()
|
||||
|
||||
initializeSocialMediaServices: ->
|
||||
return if application.testing or application.demoing
|
||||
require('core/services/facebook')()
|
||||
application.facebookHandler.loadAPI()
|
||||
application.gplusHandler.loadAPI()
|
||||
require('core/services/twitter')()
|
||||
|
||||
renderLoginButtons: =>
|
||||
renderSocialButtons: =>
|
||||
# TODO: Refactor remaining services to Handlers, use loadAPI success callback
|
||||
@initializeSocialMediaServices()
|
||||
$('.share-buttons, .partner-badges').addClass('fade-in').delay(10000).removeClass('fade-in', 5000)
|
||||
setTimeout(FB.XFBML.parse, 10) if FB?.XFBML?.parse # Handles FB login and Like
|
||||
application.facebookHandler.renderButtons()
|
||||
application.gplusHandler.renderButtons()
|
||||
twttr?.widgets?.load?()
|
||||
application.gplusHandler.renderLoginButtons()
|
||||
|
||||
activateTab: ->
|
||||
base = _.string.words(document.location.pathname[1..], '/')[0]
|
||||
|
|
|
@ -49,6 +49,7 @@ module.exports.loginUser = (userObject, failure=genericFailure, nextURL=null) ->
|
|||
jqxhr.fail(failure)
|
||||
|
||||
module.exports.logoutUser = ->
|
||||
# TODO: Refactor to use User.logout
|
||||
FB?.logout?()
|
||||
callback = ->
|
||||
location = _.result(currentView, 'logoutRedirectURL')
|
||||
|
|
|
@ -21,7 +21,7 @@ module.exports.formToObject = ($el, options) ->
|
|||
obj
|
||||
|
||||
module.exports.objectToForm = ($el, obj, options={}) ->
|
||||
options = _.extend({ overwriteExisting: false })
|
||||
options = _.extend({ overwriteExisting: false }, options)
|
||||
inputs = $('input, textarea, select', $el)
|
||||
for input in inputs
|
||||
input = $(input)
|
||||
|
@ -84,7 +84,8 @@ module.exports.setErrorToProperty = setErrorToProperty = (el, property, message,
|
|||
|
||||
module.exports.scrollToFirstError = ($el=$('body')) ->
|
||||
$first = $el.find('.has-error, .alert-danger, .error-help-block, .has-warning, .alert-warning, .warning-help-block').filter(':visible').first()
|
||||
$('html, body').animate({ scrollTop: $first.offset().top - 20 }, 300)
|
||||
if $first.length
|
||||
$('html, body').animate({ scrollTop: $first.offset().top - 20 }, 300)
|
||||
|
||||
module.exports.clearFormAlerts = (el) ->
|
||||
$('.has-error', el).removeClass('has-error')
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
module.exports = initializeFacebook = ->
|
||||
# Additional JS functions here
|
||||
window.fbAsyncInit = ->
|
||||
FB.init
|
||||
appId: (if document.location.origin is 'http://localhost:3000' then '607435142676437' else '148832601965463') # App ID
|
||||
channelUrl: document.location.origin + '/channel.html' # Channel File
|
||||
status: true # check login status
|
||||
cookie: true # enable cookies to allow the server to access the session
|
||||
xfbml: true # parse XFBML
|
||||
|
||||
Backbone.Mediator.publish 'auth:facebook-api-loaded', {}
|
||||
|
||||
# This is fired for any auth related change, such as login, logout or session refresh.
|
||||
FB.Event.subscribe 'auth.authResponseChange', (response) ->
|
||||
|
||||
# Here we specify what we do with the response anytime this event occurs.
|
||||
if response.status is 'connected'
|
||||
|
||||
# They have logged in to the app.
|
||||
Backbone.Mediator.publish 'auth:logged-in-with-facebook', response: response
|
||||
|
||||
#else if response.status is 'not_authorized'
|
||||
# #
|
||||
#else
|
||||
# #
|
||||
|
||||
# Load the SDK asynchronously
|
||||
((d) ->
|
||||
js = undefined
|
||||
id = 'facebook-jssdk'
|
||||
ref = d.getElementsByTagName('script')[0]
|
||||
return if d.getElementById(id)
|
||||
js = d.createElement('script')
|
||||
js.id = id
|
||||
js.async = true
|
||||
js.src = '//connect.facebook.net/en_US/all.js'
|
||||
|
||||
#js.src = '//connect.facebook.net/en_US/all/debug.js'
|
||||
ref.parentNode.insertBefore js, ref
|
||||
return
|
||||
) document
|
|
@ -13,44 +13,94 @@ userPropsToSave =
|
|||
|
||||
|
||||
module.exports = FacebookHandler = class FacebookHandler extends CocoClass
|
||||
subscriptions:
|
||||
'auth:logged-in-with-facebook': 'onFacebookLoggedIn'
|
||||
|
||||
loggedIn: false
|
||||
|
||||
token: -> @authResponse?.accessToken
|
||||
|
||||
fakeFacebookLogin: ->
|
||||
@onFacebookLoggedIn({
|
||||
response:
|
||||
authResponse: { accessToken: '1234' }
|
||||
})
|
||||
startedLoading: false
|
||||
apiLoaded: false
|
||||
connected: false
|
||||
person: null
|
||||
|
||||
fakeAPI: ->
|
||||
window.FB =
|
||||
login: (cb, options) ->
|
||||
cb({status: 'connected', authResponse: { accessToken: '1234' }})
|
||||
api: (url, options, cb) ->
|
||||
cb({
|
||||
first_name: 'Mr'
|
||||
last_name: 'Bean'
|
||||
id: 'abcd'
|
||||
email: 'some@email.com'
|
||||
})
|
||||
|
||||
onFacebookLoggedIn: (e) ->
|
||||
# user is logged in also when the page first loads, so check to see
|
||||
# if we really need to do the lookup
|
||||
@loggedIn = false
|
||||
@authResponse = e.response.authResponse
|
||||
for fbProp, userProp of userPropsToSave
|
||||
unless me.get(userProp)
|
||||
@loggedIn = true
|
||||
break
|
||||
@startedLoading = true
|
||||
@apiLoaded = true
|
||||
|
||||
@trigger 'logged-into-facebook'
|
||||
|
||||
loginThroughFacebook: ->
|
||||
if @loggedIn
|
||||
return true
|
||||
loadAPI: (options={}) ->
|
||||
options.success ?= _.noop
|
||||
options.context ?= options
|
||||
if @apiLoaded
|
||||
options.success.bind(options.context)()
|
||||
else
|
||||
FB.login ((response) ->
|
||||
console.log 'Received FB login response:', response
|
||||
), scope: 'email'
|
||||
@once 'load-api', options.success, options.context
|
||||
|
||||
if not @startedLoading
|
||||
# Load the SDK asynchronously
|
||||
@startedLoading = true
|
||||
((d) ->
|
||||
js = undefined
|
||||
id = 'facebook-jssdk'
|
||||
ref = d.getElementsByTagName('script')[0]
|
||||
return if d.getElementById(id)
|
||||
js = d.createElement('script')
|
||||
js.id = id
|
||||
js.async = true
|
||||
js.src = '//connect.facebook.net/en_US/all.js'
|
||||
|
||||
#js.src = '//connect.facebook.net/en_US/all/debug.js'
|
||||
ref.parentNode.insertBefore js, ref
|
||||
return
|
||||
)(document)
|
||||
|
||||
loadPerson: ->
|
||||
window.fbAsyncInit = =>
|
||||
FB.init
|
||||
appId: (if document.location.origin is 'http://localhost:3000' then '607435142676437' else '148832601965463') # App ID
|
||||
channelUrl: document.location.origin + '/channel.html' # Channel File
|
||||
cookie: true # enable cookies to allow the server to access the session
|
||||
xfbml: true # parse XFBML
|
||||
|
||||
FB.getLoginStatus (response) =>
|
||||
if response.status is 'connected'
|
||||
@connected = true
|
||||
@authResponse = response.authResponse
|
||||
@trigger 'connect', { response: response }
|
||||
@apiLoaded = true
|
||||
@trigger 'load-api'
|
||||
|
||||
|
||||
connect: (options={}) ->
|
||||
options.success ?= _.noop
|
||||
options.context ?= options
|
||||
FB.login ((response) =>
|
||||
if response.status is 'connected'
|
||||
@connected = true
|
||||
@authResponse = response.authResponse
|
||||
@trigger 'connect', { response: response }
|
||||
options.success.bind(options.context)()
|
||||
), scope: 'email'
|
||||
|
||||
|
||||
loadPerson: (options={}) ->
|
||||
options.success ?= _.noop
|
||||
options.context ?= options
|
||||
FB.api '/me', {fields: 'email,last_name,first_name,gender'}, (person) =>
|
||||
attrs = {}
|
||||
for fbProp, userProp of userPropsToSave
|
||||
value = person[fbProp]
|
||||
if value
|
||||
attrs[userProp] = value
|
||||
@trigger 'person-loaded', attrs
|
||||
@trigger 'load-person', attrs
|
||||
options.success.bind(options.context)(attrs)
|
||||
|
||||
renderButtons: ->
|
||||
setTimeout(FB.XFBML.parse, 10) if FB?.XFBML?.parse # Handles FB login and Like
|
|
@ -20,109 +20,122 @@ scope = 'https://www.googleapis.com/auth/plus.login email'
|
|||
module.exports = GPlusHandler = class GPlusHandler extends CocoClass
|
||||
constructor: ->
|
||||
@accessToken = storage.load GPLUS_TOKEN_KEY, false
|
||||
window.onGPlusLogin = _.bind(@onGPlusLogin, @)
|
||||
super()
|
||||
|
||||
token: -> @accessToken?.access_token
|
||||
|
||||
startedLoading: false
|
||||
apiLoaded: false
|
||||
connected: false
|
||||
person: null
|
||||
|
||||
loadAPI: ->
|
||||
return if @loadedAPI
|
||||
@loadedAPI = true
|
||||
(=>
|
||||
fakeAPI: ->
|
||||
window.gapi =
|
||||
client:
|
||||
load: (api, version, cb) -> cb()
|
||||
plus:
|
||||
people:
|
||||
get: -> {
|
||||
execute: (cb) ->
|
||||
cb({
|
||||
name: {
|
||||
givenName: 'Mr'
|
||||
familyName: 'Bean'
|
||||
}
|
||||
id: 'abcd'
|
||||
emails: [{value: 'some@email.com'}]
|
||||
})
|
||||
}
|
||||
|
||||
auth:
|
||||
authorize: (opts, cb) ->
|
||||
cb({access_token: '1234'})
|
||||
|
||||
@startedLoading = true
|
||||
@apiLoaded = true
|
||||
|
||||
fakeConnect: ->
|
||||
@accessToken = {access_token: '1234'}
|
||||
@trigger 'connect'
|
||||
|
||||
loadAPI: (options={}) ->
|
||||
options.success ?= _.noop
|
||||
options.context ?= options
|
||||
if @apiLoaded
|
||||
options.success.bind(options.context)()
|
||||
else
|
||||
@once 'load-api', options.success, options.context
|
||||
|
||||
if not @startedLoading
|
||||
po = document.createElement('script')
|
||||
po.type = 'text/javascript'
|
||||
po.async = true
|
||||
po.src = 'https://apis.google.com/js/client:platform.js?onload=onGPlusLoaded'
|
||||
s = document.getElementsByTagName('script')[0]
|
||||
s.parentNode.insertBefore po, s
|
||||
window.onGPlusLoaded = _.bind(@onLoadAPI, @)
|
||||
return
|
||||
)()
|
||||
@startedLoading = true
|
||||
window.onGPlusLoaded = =>
|
||||
@apiLoaded = true
|
||||
if @accessToken and me.get('gplusID')
|
||||
# We need to check the current state, given our access token
|
||||
gapi.auth.setToken 'token', @accessToken
|
||||
session_state = @accessToken.session_state
|
||||
gapi.auth.checkSessionState {client_id: clientID, session_state: session_state}, (connected) =>
|
||||
@connected = connected
|
||||
@trigger 'load-api'
|
||||
else
|
||||
@connected = false
|
||||
@trigger 'load-api'
|
||||
|
||||
onLoadAPI: ->
|
||||
Backbone.Mediator.publish 'auth:gplus-api-loaded', {}
|
||||
session_state = null
|
||||
if @accessToken and me.get('gplusID')
|
||||
# We need to check the current state, given our access token
|
||||
gapi.auth.setToken 'token', @accessToken
|
||||
session_state = @accessToken.session_state
|
||||
gapi.auth.checkSessionState({client_id: clientID, session_state: session_state}, @onCheckedSessionState)
|
||||
else
|
||||
# If we ran checkSessionState, it might return true, that the user is logged into Google, but has not authorized us
|
||||
@loggedIn = false
|
||||
func = => @trigger 'checked-state'
|
||||
setTimeout func, 1
|
||||
|
||||
renderLoginButtons: ->
|
||||
return false unless gapi?.plusone?
|
||||
gapi.plusone.go?() # Handles +1 button
|
||||
if not gapi.signin?.render
|
||||
console.warn 'Didn\'t have gapi.signin to render G+ login button. (DoNotTrackMe extension?)'
|
||||
return
|
||||
|
||||
for gplusButton in $('.gplus-login-button')
|
||||
params = {
|
||||
callback: 'onGPlusLogin',
|
||||
clientid: clientID,
|
||||
cookiepolicy: 'single_host_origin',
|
||||
scope: 'https://www.googleapis.com/auth/plus.login email',
|
||||
height: 'short',
|
||||
}
|
||||
if gapi.signin?.render
|
||||
gapi.signin.render(gplusButton, params)
|
||||
|
||||
@trigger 'render-login-buttons'
|
||||
|
||||
onCheckedSessionState: (@loggedIn) =>
|
||||
@trigger 'checked-state'
|
||||
|
||||
reauthorize: ->
|
||||
params =
|
||||
'client_id' : clientID
|
||||
'scope' : scope
|
||||
gapi.auth.authorize params, @onGPlusLogin
|
||||
|
||||
fakeGPlusLogin: ->
|
||||
@onGPlusLogin({
|
||||
access_token: '1234'
|
||||
})
|
||||
|
||||
onGPlusLogin: (e) ->
|
||||
return unless e.access_token
|
||||
@loggedIn = true
|
||||
Backbone.Mediator.publish 'auth:logged-in-with-gplus', e
|
||||
try
|
||||
connect: (options={}) ->
|
||||
options.success ?= _.noop
|
||||
options.context ?= options
|
||||
authOptions = {
|
||||
client_id: clientID
|
||||
scope: 'https://www.googleapis.com/auth/plus.login email'
|
||||
}
|
||||
gapi.auth.authorize authOptions, (e) =>
|
||||
return unless e.access_token
|
||||
@connected = true
|
||||
try
|
||||
# Without removing this, we sometimes get a cross-domain error
|
||||
d = _.omit(e, 'g-oauth-window')
|
||||
storage.save(GPLUS_TOKEN_KEY, d, 0)
|
||||
catch e
|
||||
console.error 'Unable to save G+ token key', e
|
||||
@accessToken = e
|
||||
@trigger 'logged-in'
|
||||
@trigger 'logged-into-google'
|
||||
d = _.omit(e, 'g-oauth-window')
|
||||
storage.save(GPLUS_TOKEN_KEY, d, 0)
|
||||
catch e
|
||||
console.error 'Unable to save G+ token key', e
|
||||
@accessToken = e
|
||||
@trigger 'connect'
|
||||
options.success.bind(options.context)()
|
||||
|
||||
|
||||
loadPerson: (options={}) ->
|
||||
@reloadOnLogin = options.reloadOnLogin
|
||||
options.success ?= _.noop
|
||||
options.context ?= options
|
||||
# email and profile data loaded separately
|
||||
gapi.client.load('plus', 'v1', =>
|
||||
gapi.client.plus.people.get({userId: 'me'}).execute(@onPersonReceived))
|
||||
gapi.client.load 'plus', 'v1', =>
|
||||
gapi.client.plus.people.get({userId: 'me'}).execute (r) =>
|
||||
attrs = {}
|
||||
for gpProp, userProp of userPropsToSave
|
||||
keys = gpProp.split('.')
|
||||
value = r
|
||||
for key in keys
|
||||
value = value[key]
|
||||
if value
|
||||
attrs[userProp] = value
|
||||
|
||||
if r.emails?.length
|
||||
attrs.email = r.emails[0].value
|
||||
@trigger 'load-person', attrs
|
||||
options.success.bind(options.context)(attrs)
|
||||
|
||||
onPersonReceived: (r) =>
|
||||
attrs = {}
|
||||
for gpProp, userProp of userPropsToSave
|
||||
keys = gpProp.split('.')
|
||||
value = r
|
||||
for key in keys
|
||||
value = value[key]
|
||||
if value
|
||||
attrs[userProp] = value
|
||||
|
||||
newEmail = r.emails?.length and r.emails[0] isnt me.get('email')
|
||||
return unless newEmail or me.get('anonymous', true)
|
||||
if r.emails?.length
|
||||
attrs.email = r.emails[0].value
|
||||
@trigger 'person-loaded', attrs
|
||||
|
||||
renderButtons: ->
|
||||
return false unless gapi?.plusone?
|
||||
gapi.plusone.go?() # Handles +1 button
|
||||
|
||||
# Friends logic, not in use
|
||||
|
||||
loadFriends: (friendsCallback) ->
|
||||
return friendsCallback() unless @loggedIn
|
||||
expiresIn = if @accessToken then parseInt(@accessToken.expires_at) - new Date().getTime()/1000 else -1
|
||||
|
@ -133,3 +146,9 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
|
|||
@listenToOnce(@, 'logged-in', onReauthorized)
|
||||
else
|
||||
onReauthorized()
|
||||
|
||||
reauthorize: ->
|
||||
params =
|
||||
'client_id' : clientID
|
||||
'scope' : scope
|
||||
gapi.auth.authorize params, @onGPlusLogin
|
||||
|
|
|
@ -194,6 +194,9 @@ module.exports = class User extends CocoModel
|
|||
isOnPremiumServer: ->
|
||||
me.get('country') in ['china', 'brazil']
|
||||
|
||||
|
||||
# Function meant for "me"
|
||||
|
||||
spy: (user, options={}) ->
|
||||
user = user.id or user # User instance, user ID, email or username
|
||||
options.url = '/auth/spy'
|
||||
|
@ -207,6 +210,18 @@ module.exports = class User extends CocoModel
|
|||
options.type = 'POST'
|
||||
@fetch(options)
|
||||
|
||||
logout: (options={}) ->
|
||||
options.type = 'POST'
|
||||
options.url = '/auth/logout'
|
||||
FB?.logout?()
|
||||
options.success ?= ->
|
||||
location = _.result(currentView, 'logoutRedirectURL')
|
||||
if location
|
||||
window.location = location
|
||||
else
|
||||
window.location.reload()
|
||||
@fetch(options)
|
||||
|
||||
fetchGPlusUser: (gplusID, options={}) ->
|
||||
options.data ?= {}
|
||||
options.data.gplusID = gplusID
|
||||
|
|
|
@ -4,40 +4,10 @@ module.exports =
|
|||
'auth:me-synced': c.object {required: ['me']},
|
||||
me: {type: 'object'}
|
||||
|
||||
'auth:facebook-api-loaded': c.object {}
|
||||
|
||||
'auth:logging-in-with-facebook': c.object {}
|
||||
|
||||
'auth:signed-up': c.object {}
|
||||
|
||||
'auth:logging-out': c.object {}
|
||||
|
||||
'auth:logged-in-with-facebook': c.object {title: 'Facebook logged in', description: 'Published when you successfully logged in with Facebook', required: ['response']},
|
||||
response:
|
||||
type: 'object'
|
||||
properties:
|
||||
status: {type: 'string'}
|
||||
authResponse:
|
||||
type: 'object'
|
||||
properties:
|
||||
accessToken: {type: 'string'}
|
||||
expiresIn: {type: 'number'}
|
||||
signedRequest: {type: 'string'}
|
||||
userID: {type: 'string'}
|
||||
|
||||
'auth:linkedin-api-loaded': c.object {}
|
||||
|
||||
'auth:gplus-api-loaded': c.object {}
|
||||
|
||||
'auth:logging-in-with-gplus': c.object {}
|
||||
|
||||
'auth:logged-in-with-gplus':
|
||||
title: 'G+ logged in'
|
||||
description: 'Published when you successfully logged in with G+'
|
||||
type: 'object'
|
||||
required: ['access_token']
|
||||
properties:
|
||||
access_token: {type: 'string'}
|
||||
# Could be some other stuff
|
||||
|
||||
'auth:log-in-with-github': c.object {}
|
||||
|
|
|
@ -201,6 +201,10 @@ $forest: #20572B
|
|||
border: 1px solid $purple
|
||||
color: $purple
|
||||
|
||||
.btn-burgandy
|
||||
background-color: $burgandy
|
||||
color: white
|
||||
|
||||
.btn-lg
|
||||
font-size: 18px
|
||||
|
||||
|
@ -217,6 +221,43 @@ $forest: #20572B
|
|||
a.btn-primary-alt
|
||||
color: $navy
|
||||
|
||||
// Spacing
|
||||
// Copied spacing classes in bootstrap v4 alpha 2
|
||||
// http://v4-alpha.getbootstrap.com/components/utilities/#spacing
|
||||
|
||||
$spacer: 1rem !default
|
||||
$spacer-x: $spacer !default
|
||||
$spacer-y: $spacer !default
|
||||
$spacers: ( 0: ( x: 0, y: 0 ), 1: ( x: $spacer-x, y: $spacer-y ), 2: ( x: ($spacer-x * 1.5), y: ($spacer-y * 1.5) ), 3: ( x: ($spacer-x * 3), y: ($spacer-y * 3) ) ) !default
|
||||
|
||||
.m-x-auto
|
||||
margin-right: auto !important
|
||||
margin-left: auto !important
|
||||
|
||||
@each $prop, $abbrev in (margin: m, padding: p)
|
||||
@each $size, $lengths in $spacers
|
||||
$length-x: map-get($lengths, x)
|
||||
$length-y: map-get($lengths, y)
|
||||
|
||||
.#{$abbrev}-a-#{$size}
|
||||
#{$prop}: $length-y $length-x !important // a = All sides
|
||||
.#{$abbrev}-t-#{$size}
|
||||
#{$prop}-top: $length-y !important
|
||||
.#{$abbrev}-r-#{$size}
|
||||
#{$prop}-right: $length-x !important
|
||||
.#{$abbrev}-b-#{$size}
|
||||
#{$prop}-bottom: $length-y !important
|
||||
.#{$abbrev}-l-#{$size}
|
||||
#{$prop}-left: $length-x !important
|
||||
|
||||
// Axes
|
||||
.#{$abbrev}-x-#{$size}
|
||||
#{$prop}-right: $length-x !important
|
||||
#{$prop}-left: $length-x !important
|
||||
|
||||
.#{$abbrev}-y-#{$size}
|
||||
#{$prop}-top: $length-y !important
|
||||
#{$prop}-bottom: $length-y !important
|
||||
|
||||
// base-flat.jade
|
||||
|
||||
|
|
|
@ -2,15 +2,9 @@
|
|||
background-color: #eee
|
||||
margin: 0 20px
|
||||
padding: 0
|
||||
|
||||
h2
|
||||
|
||||
#test-h2
|
||||
background: #add8e6
|
||||
font-family: Arial, Geneva, sans-serif
|
||||
padding: 20px
|
||||
font-weight: bold
|
||||
|
||||
#test-wrapper
|
||||
width: 78%
|
||||
|
||||
#test-nav
|
||||
width: 20%
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
p If this is showing, you dun goofed
|
||||
|
||||
block footer
|
||||
#character-lineup.text-center
|
||||
#character-lineup.text-center.m-t-3
|
||||
img(src="/images/pages/home/character_lineup.png")
|
||||
.container-fluid
|
||||
#footer.small
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
#facebook-login-btn.btn.btn-primary.btn-lg.btn-illustrated.network-login
|
||||
img.network-logo(src="/images/pages/community/logo_facebook.png", draggable="false")
|
||||
span.sign-in-blurb(data-i18n="login.sign_in_with_facebook")
|
||||
#gplus-login-btn.btn.btn-danger.btn-lg.btn-illustrated.network-login(disabled=true)
|
||||
#gplus-login-btn.btn.btn-danger.btn-lg.btn-illustrated.network-login
|
||||
img.network-logo(src="/images/pages/community/logo_g+.png", draggable="false")
|
||||
span.sign-in-blurb(data-i18n="login.sign_in_with_gplus")
|
||||
.gplus-login-wrapper
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
div#close-modal
|
||||
span.glyphicon.glyphicon-remove
|
||||
|
||||
.auth-form-content
|
||||
form.auth-form-content
|
||||
if view.options.showRequiredError
|
||||
#required-error-alert.alert.alert-success
|
||||
span(data-i18n="signup.required")
|
||||
|
@ -16,20 +16,19 @@
|
|||
|
||||
#email-password-row.row
|
||||
.col-md-6
|
||||
form
|
||||
.form-group
|
||||
label.control-label(for="email")
|
||||
span(data-i18n="general.email")
|
||||
| :
|
||||
.input-border
|
||||
input#email.input-large.form-control(name="email", type="email", value=view.previousFormInputs.email, tabindex=1)
|
||||
|
||||
.form-group
|
||||
label.control-label(for="password")
|
||||
span(data-i18n="general.password")
|
||||
| :
|
||||
.input-border
|
||||
input#password.input-large.form-control(name="password", type="password", value=view.previousFormInputs.password, tabindex=2)
|
||||
.form-group
|
||||
label.control-label(for="email")
|
||||
span(data-i18n="general.email")
|
||||
| :
|
||||
.input-border
|
||||
input#email.input-large.form-control(name="email", type="email", value=view.previousFormInputs.email, tabindex=1)
|
||||
|
||||
.form-group
|
||||
label.control-label(for="password")
|
||||
span(data-i18n="general.password")
|
||||
| :
|
||||
.input-border
|
||||
input#password.input-large.form-control(name="password", type="password", value=view.previousFormInputs.password, tabindex=2)
|
||||
|
||||
.col-md-6
|
||||
.auth-network-logins.text-center
|
||||
|
@ -37,7 +36,7 @@
|
|||
#facebook-signup-btn.btn.btn-primary.btn-lg.btn-illustrated.network-login
|
||||
img.network-logo(src="/images/pages/community/logo_facebook.png", draggable="false")
|
||||
span.sign-in-blurb(data-i18n="login.sign_in_with_facebook")
|
||||
#gplus-signup-btn.btn.btn-danger.btn-lg.btn-illustrated.network-login(disabled=true)
|
||||
#gplus-signup-btn.btn.btn-danger.btn-lg.btn-illustrated.network-login
|
||||
img.network-logo(src="/images/pages/community/logo_g+.png", draggable="false")
|
||||
span.sign-in-blurb(data-i18n="login.sign_in_with_gplus")
|
||||
.gplus-login-wrapper
|
||||
|
@ -59,52 +58,51 @@
|
|||
h2(data-i18n="signup.facebook_exists")
|
||||
a.btn.btn-primary#facebook-login-btn(data-i18n="login.log_in")
|
||||
|
||||
form
|
||||
.row
|
||||
.col-md-6
|
||||
.form-group
|
||||
label.control-label(for="name")
|
||||
span(data-i18n="general.name") Name
|
||||
| :
|
||||
.input-border
|
||||
if me.get('name')
|
||||
input#name.input-large.form-control(name="name", type="text", value=me.get('name'), tabindex=3)
|
||||
else
|
||||
input#name.input-large.form-control(name="name", type="text", value="", placeholder="e.g. Alex W the Skater", tabindex=3)
|
||||
|
||||
.form-group
|
||||
label.control-label(for="school-input")
|
||||
span.spr(data-i18n="signup.school_name")
|
||||
em.optional-note
|
||||
| (
|
||||
span(data-i18n="signup.optional")
|
||||
| ):
|
||||
.input-border
|
||||
input#school-input.input-large.form-control(name="schoolName", data-i18n="[placeholder]signup.school_name_placeholder", value=view.previousFormInputs.schoolName || '', tabindex=4)
|
||||
|
||||
.form-group
|
||||
label.control-label(for="school-input")
|
||||
span.spr(data-i18n="courses.class_code")
|
||||
em.optional-note
|
||||
| (
|
||||
span(data-i18n="signup.optional")
|
||||
| ):
|
||||
.input-border
|
||||
input#class-code-input.input-large.form-control(name="classCode", value=view.previousFormInputs.classCode || '', tabindex=5)
|
||||
.row
|
||||
.col-md-6
|
||||
.form-group
|
||||
label.control-label(for="name")
|
||||
span(data-i18n="general.name") Name
|
||||
| :
|
||||
.input-border
|
||||
if me.get('name')
|
||||
input#name.input-large.form-control(name="name", type="text", value=me.get('name'), tabindex=3)
|
||||
else
|
||||
input#name.input-large.form-control(name="name", type="text", value="", placeholder="e.g. Alex W the Skater", tabindex=3)
|
||||
|
||||
.col-md-6
|
||||
.form-group.checkbox
|
||||
label.control-label(for="subscribe")
|
||||
.input-border
|
||||
input#subscribe(type="checkbox", checked='checked')
|
||||
span.custom-checkbox
|
||||
.glyphicon.glyphicon-ok
|
||||
span(data-i18n="signup.email_announcements")
|
||||
.form-group
|
||||
label.control-label(for="school-input")
|
||||
span.spr(data-i18n="signup.school_name")
|
||||
em.optional-note
|
||||
| (
|
||||
span(data-i18n="signup.optional")
|
||||
| ):
|
||||
.input-border
|
||||
input#school-input.input-large.form-control(name="schoolName", data-i18n="[placeholder]signup.school_name_placeholder", value=view.previousFormInputs.schoolName || '', tabindex=4)
|
||||
|
||||
.form-group
|
||||
label.control-label(for="school-input")
|
||||
span.spr(data-i18n="courses.class_code")
|
||||
em.optional-note
|
||||
| (
|
||||
span(data-i18n="signup.optional")
|
||||
| ):
|
||||
.input-border
|
||||
input#class-code-input.input-large.form-control(name="classCode", value=view.previousFormInputs.classCode || '', tabindex=5)
|
||||
|
||||
.col-md-6
|
||||
.form-group.checkbox
|
||||
label.control-label(for="subscribe")
|
||||
.input-border
|
||||
input#subscribe(type="checkbox", checked='checked')
|
||||
span.custom-checkbox
|
||||
.glyphicon.glyphicon-ok
|
||||
span(data-i18n="signup.email_announcements")
|
||||
|
||||
.well(data-i18n="signup.hey_students")
|
||||
|
||||
.well(data-i18n="signup.hey_students")
|
||||
|
||||
|
||||
input#signup-button.btn.btn-lg.btn-illustrated.btn-block.btn-success(value=translate("signup.sign_up"), type="submit")
|
||||
input#signup-button.btn.btn-lg.btn-illustrated.btn-block.btn-success(value=translate("signup.sign_up"), type="submit")
|
||||
|
||||
.extra-pane
|
||||
.switch-explanation(data-i18n="signup.login_switch")
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
extends /templates/core/modal-base
|
||||
|
||||
block modal-header-content
|
||||
h3 #{confirmTitle}
|
||||
h3= view.title
|
||||
|
||||
block modal-body-content
|
||||
p #{confirmBody}
|
||||
p= view.body
|
||||
|
||||
block modal-footer-content
|
||||
button.btn.btn-secondary#decline-button(type="button", data-dismiss="modal") #{confirmDecline}
|
||||
button.btn.btn-primary#confirm-button(type="button", data-dismiss=closeOnConfirm === true ? "modal" : undefined) #{confirmConfirm}
|
||||
button.btn.btn-secondary#decline-button(type="button", data-dismiss="modal")= view.decline
|
||||
button.btn.btn-primary#confirm-button(type="button", data-dismiss=closeOnConfirm === true ? "modal" : undefined)= view.confirm
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
h2 Testing Page
|
||||
h2#test-h2 Testing Page
|
||||
|
||||
ol.breadcrumb
|
||||
for path in parentFolders
|
||||
|
@ -6,26 +6,29 @@ ol.breadcrumb
|
|||
a(href=path.url)= path.name
|
||||
li.active= currentFolder
|
||||
|
||||
#test-wrapper.well.pull-left
|
||||
#demo-area
|
||||
#testing-area
|
||||
|
||||
.nav.nav-pills.nav-stacked.pull-right.well#test-nav
|
||||
if view.demosOn
|
||||
button#hide-demos-btn.btn.btn-danger.btn-block Hide Demos
|
||||
else
|
||||
button#show-demos-btn.btn.btn-info.btn-block Show Demos
|
||||
|
||||
hr
|
||||
for child in children
|
||||
li(class=child.type)
|
||||
a(href=child.url).small
|
||||
if child.type == 'folder'
|
||||
span.glyphicon.glyphicon-folder-close
|
||||
else
|
||||
span.glyphicon.glyphicon-file
|
||||
span.spl= child.name
|
||||
if child.type == 'folder'
|
||||
strong (#{child.size})
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-8
|
||||
#test-wrapper.well
|
||||
#demo-area
|
||||
#testing-area
|
||||
|
||||
.clearfix
|
||||
.col-md-4.hidden-sm.hidden-xs
|
||||
.nav.nav-pills.nav-stacked.well#test-nav
|
||||
if view.demosOn
|
||||
button#hide-demos-btn.btn.btn-danger.btn-block Hide Demos
|
||||
else
|
||||
button#show-demos-btn.btn.btn-info.btn-block Show Demos
|
||||
|
||||
hr
|
||||
for child in children
|
||||
li(class=child.type)
|
||||
a(href=child.url).small
|
||||
if child.type == 'folder'
|
||||
span.glyphicon.glyphicon-folder-close
|
||||
else
|
||||
span.glyphicon.glyphicon-file
|
||||
span.spl= child.name
|
||||
if child.type == 'folder'
|
||||
strong (#{child.size})
|
||||
|
|
@ -81,6 +81,7 @@ module.exports = TestView = class TestView extends RootView
|
|||
jasmine.Ajax.requests.reset()
|
||||
Backbone.Mediator.init()
|
||||
Backbone.Mediator.setValidationEnabled false
|
||||
spyOn(application.tracker, 'trackEvent')
|
||||
# TODO Stubbify more things
|
||||
# * document.location
|
||||
# * firebase
|
||||
|
|
|
@ -64,10 +64,10 @@ module.exports = class AccountSettingsView extends CocoView
|
|||
onClickDeleteAccountButton: (e) ->
|
||||
@validateCredentialsForDestruction @$el.find('#delete-account-form'), =>
|
||||
renderData =
|
||||
confirmTitle: 'Are you really sure?'
|
||||
confirmBody: 'This will completely delete your account. This action CANNOT be undone. Are you entirely sure?'
|
||||
confirmDecline: 'Cancel'
|
||||
confirmConfirm: 'DELETE Your Account'
|
||||
title: 'Are you really sure?'
|
||||
body: 'This will completely delete your account. This action CANNOT be undone. Are you entirely sure?'
|
||||
decline: 'Cancel'
|
||||
confirm: 'DELETE Your Account'
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @deleteAccount
|
||||
@openModalView confirmModal
|
||||
|
@ -75,10 +75,10 @@ module.exports = class AccountSettingsView extends CocoView
|
|||
onClickResetProgressButton: ->
|
||||
@validateCredentialsForDestruction @$el.find('#reset-progress-form'), =>
|
||||
renderData =
|
||||
confirmTitle: 'Are you really sure?'
|
||||
confirmBody: 'This will completely erase your progress: code, levels, achievements, earned gems, and course work. This action CANNOT be undone. Are you entirely sure?'
|
||||
confirmDecline: 'Cancel'
|
||||
confirmConfirm: 'Erase ALL Progress'
|
||||
title: 'Are you really sure?'
|
||||
body: 'This will completely erase your progress: code, levels, achievements, earned gems, and course work. This action CANNOT be undone. Are you entirely sure?'
|
||||
decline: 'Cancel'
|
||||
confirm: 'Erase ALL Progress'
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @resetProgress
|
||||
@openModalView confirmModal
|
||||
|
|
|
@ -11,29 +11,19 @@ module.exports = class AuthModal extends ModalView
|
|||
template: template
|
||||
|
||||
events:
|
||||
# login buttons
|
||||
'click #switch-to-signup-btn': 'onSignupInstead'
|
||||
'click #github-login-button': 'onGitHubLoginClicked'
|
||||
'submit form': 'onSubmitForm' # handles both submit buttons
|
||||
'submit form': 'onSubmitForm'
|
||||
'keyup #name': 'onNameChange'
|
||||
'click #gplus-login-btn': 'onClickGPlusLogin'
|
||||
'click #gplus-login-btn': 'onClickGPlusLoginButton'
|
||||
'click #facebook-login-btn': 'onClickFacebookLoginButton'
|
||||
'click #close-modal': 'hide'
|
||||
|
||||
subscriptions:
|
||||
'errors:server-error': 'onServerError'
|
||||
'auth:facebook-api-loaded': 'onFacebookAPILoaded'
|
||||
|
||||
|
||||
# Initialization
|
||||
|
||||
initialize: (options={}) ->
|
||||
@previousFormInputs = options.initialValues or {}
|
||||
@listenTo application.gplusHandler, 'logged-into-google', @onGPlusHandlerLoggedIntoGoogle
|
||||
@listenTo application.gplusHandler, 'person-loaded', @onGPlusPersonLoaded
|
||||
@listenTo application.gplusHandler, 'render-login-buttons', @onGPlusRenderLoginButtons
|
||||
@listenTo application.facebookHandler, 'logged-into-facebook', @onFacebookHandlerLoggedIntoFacebook
|
||||
@listenTo application.facebookHandler, 'person-loaded', @onFacebookPersonLoaded
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
|
@ -47,19 +37,11 @@ module.exports = class AuthModal extends ModalView
|
|||
afterRender: ->
|
||||
super()
|
||||
@playSound 'game-menu-open'
|
||||
@$('#facebook-login-btn').attr('disabled', true) if not window.FB?
|
||||
|
||||
afterInsert: ->
|
||||
super()
|
||||
_.delay (=> application.router.renderLoginButtons()), 500
|
||||
_.delay (=> $('input:visible:first', @$el).focus()), 500
|
||||
|
||||
onGPlusRenderLoginButtons: ->
|
||||
@$('#gplus-login-btn').attr('disabled', false)
|
||||
|
||||
onFacebookAPILoaded: ->
|
||||
@$('#facebook-login-btn').attr('disabled', false)
|
||||
|
||||
onSignupInstead: (e) ->
|
||||
CreateAccountModal = require('./CreateAccountModal')
|
||||
modal = new CreateAccountModal({initialValues: forms.formToObject @$el})
|
||||
|
@ -81,61 +63,71 @@ module.exports = class AuthModal extends ModalView
|
|||
|
||||
# Google Plus
|
||||
|
||||
onClickGPlusLogin: ->
|
||||
@clickedGPlusLogin = true
|
||||
|
||||
onGPlusHandlerLoggedIntoGoogle: ->
|
||||
return unless @clickedGPlusLogin
|
||||
application.gplusHandler.loadPerson()
|
||||
onClickGPlusLoginButton: ->
|
||||
btn = @$('#gplus-login-btn')
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('login.logging_in'))
|
||||
btn.attr('disabled', true)
|
||||
|
||||
onGPlusPersonLoaded: (gplusAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchGPlusUser(gplusAttrs.gplusID, {
|
||||
success: =>
|
||||
me.loginGPlusUser(gplusAttrs.gplusID, {
|
||||
success: -> window.location.reload()
|
||||
error: @onGPlusLoginError
|
||||
application.gplusHandler.loadAPI({
|
||||
context: @
|
||||
success: ->
|
||||
btn.attr('disabled', false)
|
||||
application.gplusHandler.connect({
|
||||
context: @
|
||||
success: ->
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('login.logging_in'))
|
||||
btn.attr('disabled', true)
|
||||
application.gplusHandler.loadPerson({
|
||||
context: @
|
||||
success: (gplusAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchGPlusUser(gplusAttrs.gplusID, {
|
||||
success: =>
|
||||
me.loginGPlusUser(gplusAttrs.gplusID, {
|
||||
success: -> window.location.reload()
|
||||
error: @onGPlusLoginError
|
||||
})
|
||||
error: @onGPlusLoginError
|
||||
})
|
||||
})
|
||||
})
|
||||
error: @onGPlusLoginError
|
||||
})
|
||||
|
||||
|
||||
onGPlusLoginError: =>
|
||||
btn = @$('#gplus-login-btn')
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('login.sign_in_with_gplus'))
|
||||
btn.attr('disabled', false)
|
||||
errors.showNotyNetworkError(arguments...)
|
||||
errors.showNotyNetworkError(arguments...)
|
||||
|
||||
|
||||
# Facebook
|
||||
|
||||
onClickFacebookLoginButton: ->
|
||||
@clickedFacebookLogin = true
|
||||
if application.facebookHandler.loggedIn
|
||||
@onFacebookHandlerLoggedIntoFacebook()
|
||||
else
|
||||
application.facebookHandler.loginThroughFacebook()
|
||||
|
||||
onFacebookHandlerLoggedIntoFacebook: ->
|
||||
return unless @clickedFacebookLogin
|
||||
application.facebookHandler.loadPerson()
|
||||
btn = @$('#facebook-login-btn')
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('login.logging_in'))
|
||||
btn.attr('disabled', true)
|
||||
|
||||
onFacebookPersonLoaded: (facebookAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchFacebookUser(facebookAttrs.facebookID, {
|
||||
success: =>
|
||||
me.loginFacebookUser(facebookAttrs.facebookID, {
|
||||
success: -> window.location.reload()
|
||||
error: @onFacebookLoginError
|
||||
application.facebookHandler.loadAPI({
|
||||
context: @
|
||||
success: ->
|
||||
btn.attr('disabled', false)
|
||||
application.facebookHandler.connect({
|
||||
context: @
|
||||
success: ->
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('login.logging_in'))
|
||||
btn.attr('disabled', true)
|
||||
application.facebookHandler.loadPerson({
|
||||
context: @
|
||||
success: (facebookAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchFacebookUser(facebookAttrs.facebookID, {
|
||||
success: =>
|
||||
me.loginFacebookUser(facebookAttrs.facebookID, {
|
||||
success: -> window.location.reload()
|
||||
error: @onFacebookLoginError
|
||||
})
|
||||
error: @onFacebookLoginError
|
||||
})
|
||||
})
|
||||
})
|
||||
error: @onFacebookLoginError
|
||||
})
|
||||
|
||||
|
||||
onFacebookLoginError: =>
|
||||
btn = @$('#facebook-login-btn')
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('login.sign_in_with_facebook'))
|
||||
|
|
|
@ -444,6 +444,9 @@ module.exports = class CocoView extends Backbone.View
|
|||
scrollToLink: (link, speed=300) ->
|
||||
scrollTo = $(link).offset().top
|
||||
$('html, body').animate({ scrollTop: scrollTo }, speed)
|
||||
|
||||
scrollToTop: (speed=300) ->
|
||||
$('html, body').animate({ scrollTop: 0 }, speed)
|
||||
|
||||
toggleFullscreen: (e) ->
|
||||
# https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode?redirectlocale=en-US&redirectslug=Web/Guide/DOM/Using_full_screen_mode
|
||||
|
|
|
@ -7,8 +7,6 @@ application = require 'core/application'
|
|||
Classroom = require 'models/Classroom'
|
||||
errors = require 'core/errors'
|
||||
|
||||
# TODO: Avoid using G+ render buttons to login, login directly instead.
|
||||
# Form object is split in two in template to avoid having rendered buttons triggered on form submit.
|
||||
|
||||
module.exports = class CreateAccountModal extends ModalView
|
||||
id: 'create-account-modal'
|
||||
|
@ -24,37 +22,21 @@ module.exports = class CreateAccountModal extends ModalView
|
|||
'click #close-modal': 'hide'
|
||||
'click #switch-to-login-btn': 'onClickSwitchToLoginButton'
|
||||
|
||||
subscriptions:
|
||||
'auth:facebook-api-loaded': 'onFacebookAPILoaded'
|
||||
|
||||
|
||||
# Initialization
|
||||
|
||||
initialize: (options={}) ->
|
||||
@onNameChange = _.debounce(_.bind(@checkNameExists, @), 500)
|
||||
@previousFormInputs = options.initialValues or {}
|
||||
@listenTo application.gplusHandler, 'logged-into-google', @onGPlusHandlerLoggedIntoGoogle
|
||||
@listenTo application.gplusHandler, 'person-loaded', @onGPlusPersonLoaded
|
||||
@listenTo application.gplusHandler, 'render-login-buttons', @onGPlusRenderLoginButtons
|
||||
@listenTo application.facebookHandler, 'logged-into-facebook', @onFacebookHandlerLoggedIntoFacebook
|
||||
@listenTo application.facebookHandler, 'person-loaded', @onFacebookPersonLoaded
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@playSound 'game-menu-open'
|
||||
@$('#facebook-signup-btn').attr('disabled', true) if not window.FB?
|
||||
|
||||
afterInsert: ->
|
||||
super()
|
||||
_.delay (-> application.router.renderLoginButtons()), 500
|
||||
_.delay (=> $('input:visible:first', @$el).focus()), 500
|
||||
|
||||
onGPlusRenderLoginButtons: ->
|
||||
@$('#gplus-signup-btn').attr('disabled', false)
|
||||
|
||||
onFacebookAPILoaded: ->
|
||||
@$('#facebook-signup-btn').attr('disabled', false)
|
||||
|
||||
|
||||
# User creation
|
||||
|
||||
|
@ -151,28 +133,35 @@ module.exports = class CreateAccountModal extends ModalView
|
|||
# Google Plus
|
||||
|
||||
onClickGPlusSignupButton: ->
|
||||
@clickedGPlusLogin = true
|
||||
|
||||
onGPlusHandlerLoggedIntoGoogle: ->
|
||||
return unless @clickedGPlusLogin
|
||||
application.gplusHandler.loadPerson()
|
||||
btn = @$('#gplus-signup-btn')
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('signup.creating'))
|
||||
btn.attr('disabled', true)
|
||||
|
||||
onGPlusPersonLoaded: (@gplusAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchGPlusUser(@gplusAttrs.gplusID, {
|
||||
application.gplusHandler.loadAPI({
|
||||
context: @
|
||||
complete: ->
|
||||
@$('#email-password-row').remove()
|
||||
success: =>
|
||||
@$('#gplus-account-exists-row').removeClass('hide')
|
||||
error: (user, jqxhr) =>
|
||||
if jqxhr.status is 404
|
||||
@$('#gplus-logged-in-row').toggleClass('hide')
|
||||
else
|
||||
errors.showNotyNetworkError(jqxhr)
|
||||
success: ->
|
||||
btn.attr('disabled', false)
|
||||
application.gplusHandler.connect({
|
||||
context: @
|
||||
success: ->
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('signup.creating'))
|
||||
btn.attr('disabled', true)
|
||||
application.gplusHandler.loadPerson({
|
||||
context: @
|
||||
success: (@gplusAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchGPlusUser(@gplusAttrs.gplusID, {
|
||||
context: @
|
||||
complete: ->
|
||||
@$('#email-password-row').remove()
|
||||
success: =>
|
||||
@$('#gplus-account-exists-row').removeClass('hide')
|
||||
error: (user, jqxhr) =>
|
||||
if jqxhr.status is 404
|
||||
@$('#gplus-logged-in-row').toggleClass('hide')
|
||||
else
|
||||
errors.showNotyNetworkError(jqxhr)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
onClickGPlusLoginButton: ->
|
||||
|
@ -190,31 +179,35 @@ module.exports = class CreateAccountModal extends ModalView
|
|||
# Facebook
|
||||
|
||||
onClickFacebookSignupButton: ->
|
||||
@clickedFacebookLogin = true
|
||||
if application.facebookHandler.loggedIn
|
||||
@onFacebookHandlerLoggedIntoFacebook()
|
||||
else
|
||||
application.facebookHandler.loginThroughFacebook()
|
||||
|
||||
onFacebookHandlerLoggedIntoFacebook: ->
|
||||
return unless @clickedFacebookLogin
|
||||
application.facebookHandler.loadPerson()
|
||||
btn = @$('#facebook-signup-btn')
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('signup.creating'))
|
||||
btn.attr('disabled', true)
|
||||
|
||||
onFacebookPersonLoaded: (@facebookAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchFacebookUser(@facebookAttrs.facebookID, {
|
||||
success: =>
|
||||
@$('#email-password-row').remove()
|
||||
@$('#facebook-account-exists-row').removeClass('hide')
|
||||
error: (model, jqxhr) =>
|
||||
@$('#email-password-row').remove()
|
||||
if jqxhr.status is 404
|
||||
@$('#facebook-logged-in-row').toggleClass('hide')
|
||||
else
|
||||
errors.showNotyNetworkError(jqxhr)
|
||||
application.facebookHandler.loadAPI({
|
||||
context: @
|
||||
success: ->
|
||||
btn.attr('disabled', false)
|
||||
application.facebookHandler.connect({
|
||||
context: @
|
||||
success: ->
|
||||
btn.find('.sign-in-blurb').text($.i18n.t('signup.creating'))
|
||||
btn.attr('disabled', true)
|
||||
application.facebookHandler.loadPerson({
|
||||
context: @
|
||||
success: (@facebookAttrs) ->
|
||||
existingUser = new User()
|
||||
existingUser.fetchFacebookUser(@facebookAttrs.facebookID, {
|
||||
context: @
|
||||
complete: ->
|
||||
@$('#email-password-row').remove()
|
||||
success: =>
|
||||
@$('#facebook-account-exists-row').removeClass('hide')
|
||||
error: (user, jqxhr) =>
|
||||
if jqxhr.status is 404
|
||||
@$('#facebook-logged-in-row').toggleClass('hide')
|
||||
else
|
||||
errors.showNotyNetworkError(jqxhr)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
onClickFacebookLoginButton: ->
|
||||
|
|
|
@ -99,11 +99,6 @@ module.exports = class RootView extends CocoView
|
|||
#location.hash = hash
|
||||
@renderScrollbar()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.usesSocialMedia = @usesSocialMedia
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
if @$el.find('#site-nav').length # hack...
|
||||
@$el.addClass('site-chrome')
|
||||
|
|
|
@ -85,10 +85,10 @@ module.exports = class AchievementEditView extends RootView
|
|||
|
||||
confirmRecalculation: (e, all=false) ->
|
||||
renderData =
|
||||
'confirmTitle': 'Are you really sure?'
|
||||
'confirmBody': "This will trigger recalculation of #{if all then 'all achievements' else 'the achievement'} for all users. Are you really sure you want to go down this path?"
|
||||
'confirmDecline': 'Not really'
|
||||
'confirmConfirm': 'Definitely'
|
||||
title: 'Are you really sure?'
|
||||
body: "This will trigger recalculation of #{if all then 'all achievements' else 'the achievement'} for all users. Are you really sure you want to go down this path?"
|
||||
decline: 'Not really'
|
||||
confirm: 'Definitely'
|
||||
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @recalculateAchievement
|
||||
|
@ -100,10 +100,10 @@ module.exports = class AchievementEditView extends RootView
|
|||
|
||||
confirmDeletion: ->
|
||||
renderData =
|
||||
'confirmTitle': 'Are you really sure?'
|
||||
'confirmBody': 'This will completely delete the achievement, potentially breaking a lot of stuff you don\'t want breaking. Are you entirely sure?'
|
||||
'confirmDecline': 'Not really'
|
||||
'confirmConfirm': 'Definitely'
|
||||
title: 'Are you really sure?'
|
||||
body: 'This will completely delete the achievement, potentially breaking a lot of stuff you don\'t want breaking. Are you entirely sure?'
|
||||
decline: 'Not really'
|
||||
confirm: 'Definitely'
|
||||
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @deleteAchievement
|
||||
|
|
|
@ -8,19 +8,12 @@ module.exports = class ConfirmModal extends ModalView
|
|||
closeOnConfirm: true
|
||||
|
||||
events:
|
||||
'click #decline-button': 'onDecline'
|
||||
'click #confirm-button': 'onConfirm'
|
||||
'click #decline-button': 'onClickDecline'
|
||||
'click #confirm-button': 'onClickConfirm'
|
||||
|
||||
constructor: (@renderData={}, options={}) ->
|
||||
super(options)
|
||||
initialize: (options) ->
|
||||
_.assign @, _.pick(options, 'title', 'body', 'decline', 'confirm', 'closeOnConfirm', 'closeButton')
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.closeOnConfirm = @closeOnConfirm
|
||||
_.extend context, @renderData
|
||||
onClickDecline: -> @trigger 'decline'
|
||||
|
||||
setRenderData: (@renderData) ->
|
||||
|
||||
onDecline: -> @trigger 'decline'
|
||||
|
||||
onConfirm: -> @trigger 'confirm'
|
||||
onClickConfirm: -> @trigger 'confirm'
|
||||
|
|
|
@ -98,10 +98,10 @@ module.exports = class PollEditView extends RootView
|
|||
|
||||
confirmDeletion: ->
|
||||
renderData =
|
||||
'confirmTitle': 'Are you really sure?'
|
||||
'confirmBody': 'This will completely delete the poll, potentially breaking a lot of stuff you don\'t want breaking. Are you entirely sure?'
|
||||
'confirmDecline': 'Not really'
|
||||
'confirmConfirm': 'Definitely'
|
||||
title: 'Are you really sure?'
|
||||
body: 'This will completely delete the poll, potentially breaking a lot of stuff you don\'t want breaking. Are you entirely sure?'
|
||||
decline: 'Not really'
|
||||
confirm: 'Definitely'
|
||||
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @deletePoll
|
||||
|
|
|
@ -22,11 +22,12 @@ module.exports = class LadderTabView extends CocoView
|
|||
'click .spectate-cell': 'onClickSpectateCell'
|
||||
'click .load-more-ladder-entries': 'onLoadMoreLadderEntries'
|
||||
|
||||
subscriptions:
|
||||
'auth:facebook-api-loaded': 'checkFriends'
|
||||
'auth:gplus-api-loaded': 'checkFriends'
|
||||
'auth:logged-in-with-facebook': 'onConnectedWithFacebook'
|
||||
'auth:logged-in-with-gplus': 'onConnectedWithGPlus'
|
||||
# Refactored, to-reimplement
|
||||
# subscriptions:
|
||||
# 'auth:facebook-api-loaded': 'checkFriends'
|
||||
# 'auth:gplus-api-loaded': 'checkFriends'
|
||||
# 'auth:logged-in-with-facebook': 'onConnectedWithFacebook'
|
||||
# 'auth:logged-in-with-gplus': 'onConnectedWithGPlus'
|
||||
|
||||
constructor: (options, @level, @sessions) ->
|
||||
super(options)
|
||||
|
|
|
@ -36,8 +36,8 @@ ArticleSchema.plugin(plugins.SearchablePlugin, {searchable: ['body', 'name']})
|
|||
ArticleSchema.plugin(plugins.TranslationCoveragePlugin)
|
||||
ArticleSchema.plugin(plugins.PatchablePlugin)
|
||||
|
||||
ArticleSchema.postEditableProperties = []
|
||||
ArticleSchema.editableProperties = ['body', 'name', 'i18n', 'i18nCoverage']
|
||||
ArticleSchema.jsonSchema = require '../../app/schemas/models/article'
|
||||
ArticleSchema.statics.postEditableProperties = []
|
||||
ArticleSchema.statics.editableProperties = ['body', 'name', 'i18n', 'i18nCoverage']
|
||||
ArticleSchema.statics.jsonSchema = require '../../app/schemas/models/article'
|
||||
|
||||
module.exports = mongoose.model('article', ArticleSchema)
|
||||
|
|
|
@ -56,6 +56,7 @@ module.exports =
|
|||
doc.set('original', doc._id)
|
||||
doc.set('creator', req.user._id)
|
||||
|
||||
return doc
|
||||
|
||||
applyCustomSearchToDBQ: (req, dbq) ->
|
||||
specialParameters = ['term', 'project', 'conditions']
|
||||
|
@ -125,11 +126,11 @@ module.exports =
|
|||
assignBody: (req, doc, options={}) ->
|
||||
if _.isEmpty(req.body)
|
||||
throw new errors.UnprocessableEntity('No input')
|
||||
|
||||
props = doc.schema.editableProperties.slice()
|
||||
|
||||
props = doc.schema.statics.editableProperties.slice()
|
||||
|
||||
if doc.isNew
|
||||
props = props.concat doc.schema.postEditableProperties
|
||||
props = props.concat doc.schema.statics.postEditableProperties
|
||||
|
||||
if doc.schema.uses_coco_permissions and req.user
|
||||
isOwner = doc.getAccessForUserObjectId(req.user._id) is 'owner'
|
||||
|
@ -152,7 +153,7 @@ module.exports =
|
|||
# so that validation doesn't get hung up on Date objects in the documents.
|
||||
delete obj.dateCreated
|
||||
tv4 = require('tv4').tv4
|
||||
result = tv4.validateMultiple(obj, doc.schema.jsonSchema)
|
||||
result = tv4.validateMultiple(obj, doc.schema.statics.jsonSchema)
|
||||
if not result.valid
|
||||
throw new errors.UnprocessableEntity('JSON-schema validation failed', { validationErrors: result.errors })
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ 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.handlerUrlOverrides =
|
||||
|
@ -44,7 +43,6 @@ module.exports.handlerUrlOverrides =
|
|||
'user_remark': 'user.remark'
|
||||
'mail_sent': 'mail.sent'
|
||||
'user_polls_record': 'user.polls.record'
|
||||
'trial_request': 'trial.request'
|
||||
'user_code_problem': 'user.code.problem'
|
||||
|
||||
module.exports.routes =
|
||||
|
|
|
@ -4,5 +4,6 @@ module.exports =
|
|||
named: require './named'
|
||||
patchable: require './patchable'
|
||||
rest: require './rest'
|
||||
trialRequests: require './trial-requests'
|
||||
users: require './users'
|
||||
versions: require './versions'
|
44
server/middleware/trial-requests.coffee
Normal file
44
server/middleware/trial-requests.coffee
Normal file
|
@ -0,0 +1,44 @@
|
|||
utils = require '../lib/utils'
|
||||
errors = require '../commons/errors'
|
||||
wrap = require 'co-express'
|
||||
Promise = require 'bluebird'
|
||||
database = require '../commons/database'
|
||||
mongoose = require 'mongoose'
|
||||
TrialRequest = require '../models/TrialRequest'
|
||||
|
||||
module.exports =
|
||||
post: wrap (req, res) ->
|
||||
if req.user.isAnonymous()
|
||||
email = req.body?.properties?.email
|
||||
throw new errors.UnprocessableEntity('Email not provided.') unless email
|
||||
email = email.toLowerCase()
|
||||
user = yield User.findOne({emailLower: email})
|
||||
throw new errors.Conflict('User with this email already exists.') if user
|
||||
|
||||
trialRequest = database.initDoc(req, TrialRequest)
|
||||
trialRequest.set 'applicant', req.user._id
|
||||
trialRequest.set 'created', new Date()
|
||||
trialRequest.set 'status', 'submitted'
|
||||
database.assignBody(req, trialRequest)
|
||||
database.validateDoc(trialRequest)
|
||||
trialRequest = yield trialRequest.save()
|
||||
res.status(201).send(trialRequest.toObject({req: req}))
|
||||
|
||||
put: wrap (req, res) ->
|
||||
trialRequest = yield database.getDocFromHandle(req, TrialRequest)
|
||||
throw new errors.NotFound('Trial Request not found.') if not trialRequest
|
||||
database.assignBody(req, trialRequest)
|
||||
trialRequest.set('reviewDate', new Date())
|
||||
trialRequest.set('reviewer', req.user.get('_id'))
|
||||
database.validateDoc(trialRequest)
|
||||
trialRequest = yield trialRequest.save()
|
||||
res.status(200).send(trialRequest.toObject({req: req}))
|
||||
|
||||
fetchByApplicant: wrap (req, res, next) ->
|
||||
applicantID = req.query.applicant
|
||||
return next() unless applicantID
|
||||
throw new errors.UnprocessableEntity('Bad applicant id') unless utils.isID(applicantID)
|
||||
throw new errors.Forbidden('May not fetch for anyone but yourself') unless req.user.id is applicantID
|
||||
trialRequests = yield TrialRequest.find({applicant: mongoose.Types.ObjectId(applicantID)})
|
||||
trialRequests = (tr.toObject({req: req}) for tr in trialRequests)
|
||||
res.status(200).send(trialRequests)
|
|
@ -81,7 +81,7 @@ module.exports.setup = (app) ->
|
|||
|
||||
app.post('/auth/logout', (req, res) ->
|
||||
req.logout()
|
||||
res.end()
|
||||
res.send({})
|
||||
)
|
||||
|
||||
app.post('/auth/reset', (req, res) ->
|
||||
|
|
|
@ -26,6 +26,12 @@ module.exports.setup = (app) ->
|
|||
app.get('/db/user', mw.users.fetchByGPlusID, mw.users.fetchByFacebookID)
|
||||
|
||||
app.get '/db/products', require('./db/product').get
|
||||
|
||||
TrialRequest = require '../models/TrialRequest'
|
||||
app.get('/db/trial.request', mw.trialRequests.fetchByApplicant, mw.auth.checkHasPermission(['admin']), mw.rest.get(TrialRequest))
|
||||
app.post('/db/trial.request', mw.auth.checkLoggedIn(), mw.trialRequests.post)
|
||||
app.get('/db/trial.request/:handle', mw.auth.checkHasPermission(['admin']), mw.rest.getByHandle(TrialRequest))
|
||||
app.put('/db/trial.request/:handle', mw.auth.checkHasPermission(['admin']), mw.trialRequests.put)
|
||||
|
||||
app.get '/healthcheck', (req, res) ->
|
||||
try
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
async = require 'async'
|
||||
log = require 'winston'
|
||||
mongoose = require 'mongoose'
|
||||
Handler = require '../commons/Handler'
|
||||
TrialRequest = require './TrialRequest'
|
||||
User = require '../users/User'
|
||||
|
||||
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 'created', new Date()
|
||||
instance.set 'status', 'submitted'
|
||||
instance
|
||||
|
||||
post: (req, res) ->
|
||||
return @sendForbiddenError(res) unless req.user?
|
||||
if req.user.isAnonymous()
|
||||
email = req.body?.properties?.email
|
||||
return @sendBadInputError(res, 'email not provided') unless email
|
||||
User.findOne({emailLower: req.body.properties.email}).exec (err, user) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendError(res, 409, 'User with this email already exists.') if user
|
||||
super(req, res)
|
||||
else
|
||||
super(req, res)
|
||||
|
||||
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()
|
|
@ -506,6 +506,8 @@ UserHandler = class UserHandler extends Handler
|
|||
hipchat.sendHipChatMessage "#{req.body.githubUsername or req.user.get('name')} just signed the CLA.", ['main']
|
||||
|
||||
avatar: (req, res, id) ->
|
||||
if not isID(id)
|
||||
return @sendBadInputError(res, 'Invalid avatar id')
|
||||
@modelClass.findById(id).exec (err, document) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res) unless document
|
||||
|
|
|
@ -32,7 +32,7 @@ models_path = [
|
|||
'../../server/achievements/EarnedAchievement'
|
||||
'../../server/payments/Payment'
|
||||
'../../server/prepaids/Prepaid'
|
||||
'../../server/trial_requests/TrialRequest'
|
||||
'../../server/models/TrialRequest'
|
||||
]
|
||||
|
||||
for m in models_path
|
||||
|
|
|
@ -1,172 +1,156 @@
|
|||
require '../common'
|
||||
utils = require '../utils'
|
||||
_ = require 'lodash'
|
||||
Promise = require 'bluebird'
|
||||
|
||||
describe 'Trial Requests', ->
|
||||
fixture = {
|
||||
type: 'subscription'
|
||||
properties:
|
||||
location: 'SF, CA'
|
||||
age: '14-17'
|
||||
numStudents: 14
|
||||
heardAbout: 'magical interwebs'
|
||||
}
|
||||
|
||||
describe 'POST /db/trial.request', ->
|
||||
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()
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModels([User, TrialRequest])
|
||||
@user = yield utils.initUser()
|
||||
yield utils.loginUser(@user)
|
||||
fixture.properties.email = @user.get('email')
|
||||
[res, body] = yield request.postAsync(getURL('/db/trial.request'), { json: fixture })
|
||||
expect(res.statusCode).toBe(201)
|
||||
expect(body._id).toBeDefined()
|
||||
@trialRequest = yield TrialRequest.findById(body._id)
|
||||
done()
|
||||
|
||||
it 'sets type and properties given', ->
|
||||
expect(@trialRequest.get('type')).toBe('subscription')
|
||||
expect(@trialRequest.get('properties').location).toBe('SF, CA')
|
||||
|
||||
it 'sets applicant to the user\'s id', ->
|
||||
expect(@trialRequest.get('applicant').equals(@user._id)).toBe(true)
|
||||
|
||||
describe 'GET /db/trial.request', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModels([User, TrialRequest])
|
||||
@user = yield utils.initUser()
|
||||
yield utils.loginUser(@user)
|
||||
fixture.properties.email = @user.get('email')
|
||||
[res, body] = yield request.postAsync(getURL('/db/trial.request'), { json: fixture })
|
||||
@trialRequest = yield TrialRequest.findById(body._id)
|
||||
done()
|
||||
|
||||
it 'returns 403 to non-admins', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync(getURL('/db/trial.request'))
|
||||
expect(res.statusCode).toEqual(403)
|
||||
done()
|
||||
|
||||
it 'returns trial requests to admins', utils.wrap (done) ->
|
||||
@admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(@admin)
|
||||
[res, body] = yield request.getAsync(getURL('/db/trial.request'), { json: true })
|
||||
expect(res.statusCode).toEqual(200)
|
||||
expect(body.length).toBe(1)
|
||||
done()
|
||||
|
||||
describe 'GET /db/trial.request?applicant=:userID', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModels([User, TrialRequest])
|
||||
@user1 = yield utils.initUser()
|
||||
@user2 = yield utils.initUser()
|
||||
yield utils.loginUser(@user1)
|
||||
@trialRequest1 = new TrialRequest({applicant: @user1._id})
|
||||
yield @trialRequest1.save()
|
||||
@trialRequest2 = yield new TrialRequest({applicant: @user2._id}).save()
|
||||
done()
|
||||
|
||||
it 'returns trial requests for the given applicant', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync(getURL('/db/trial.request?applicant='+@user1.id), { json: true })
|
||||
expect(res.statusCode).toEqual(200)
|
||||
expect(body.length).toBe(1)
|
||||
expect(body[0]._id).toBe(@trialRequest1.id)
|
||||
done()
|
||||
|
||||
it 'returns 403 when non-admins request other people\'s trial requests', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync(getURL('/db/trial.request?applicant='+@user2.id), { json: true })
|
||||
expect(res.statusCode).toEqual(403)
|
||||
done()
|
||||
|
||||
|
||||
describe 'PUT /db/trial.request/:handle', ->
|
||||
putURL = null
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModels([User, TrialRequest])
|
||||
@user = yield utils.initUser()
|
||||
yield utils.loginUser(@user)
|
||||
fixture.properties.email = @user.get('email')
|
||||
[res, body] = yield request.postAsync(getURL('/db/trial.request'), { json: fixture })
|
||||
@trialRequest = yield TrialRequest.findById(body._id)
|
||||
putURL = getURL('/db/trial.request/'+@trialRequest.id)
|
||||
done()
|
||||
|
||||
it 'returns 403 to non-admins', ->
|
||||
[res, body] = yield request.putAsync(getURL("/db/trial.request/#{@trialRequest.id}"))
|
||||
expect(res.statusCode).toEqual(403)
|
||||
done()
|
||||
|
||||
describe 'set status to "approved"', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
@admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(@admin)
|
||||
[res, body] = yield request.putAsync(putURL, { json: { status: 'approved' } })
|
||||
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
|
||||
expect(body.status).toBe('approved')
|
||||
setTimeout done, 10 # let changes propagate
|
||||
|
||||
it 'sets reviewDate and reviewer', utils.wrap (done) ->
|
||||
trialRequest = yield TrialRequest.findById(@trialRequest.id)
|
||||
expect(trialRequest.get('reviewDate')).toBeDefined()
|
||||
expect(trialRequest.get('reviewer').equals(@admin._id))
|
||||
expect(new Date(trialRequest.get('reviewDate'))).toBeLessThan(new Date())
|
||||
done()
|
||||
|
||||
it 'gives the user two enrollments', utils.wrap (done) ->
|
||||
prepaids = yield Prepaid.find({'properties.trialRequestID': @trialRequest._id})
|
||||
expect(prepaids.length).toEqual(1)
|
||||
prepaid = prepaids[0]
|
||||
expect(prepaid.get('type')).toEqual('course')
|
||||
expect(prepaid.get('creator')).toEqual(@user.get('_id'))
|
||||
expect(prepaid.get('maxRedeemers')).toEqual(2)
|
||||
done()
|
||||
|
||||
it 'enables teacherNews for the user', utils.wrap (done) ->
|
||||
user = yield User.findById(@user._id)
|
||||
expect(user.get('emails')?.teacherNews?.enabled).toEqual(true)
|
||||
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()
|
||||
describe 'set status to "denied"', ->
|
||||
|
||||
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()
|
||||
beforeEach utils.wrap (done) ->
|
||||
@admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(@admin)
|
||||
[res, body] = yield request.putAsync(putURL, { json: { status: 'denied' } })
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(body.status).toBe('denied')
|
||||
setTimeout done, 10 # let changes propagate
|
||||
|
||||
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, admin) ->
|
||||
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)
|
||||
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)
|
||||
Prepaid.find {'properties.trialRequestID': doc.get('_id')}, (err, prepaids) ->
|
||||
expect(err).toBeNull()
|
||||
return done(err) if err
|
||||
expect(prepaids.length).toEqual(1)
|
||||
prepaid = prepaids[0]
|
||||
expect(prepaid.get('type')).toEqual('course')
|
||||
expect(prepaid.get('creator')).toEqual(user.get('_id'))
|
||||
expect(prepaid.get('maxRedeemers')).toEqual(2)
|
||||
User.findById user._id, (err, user) =>
|
||||
expect(err).toBeNull()
|
||||
return done(err) if err
|
||||
expect(user.get('emails')?.teacherNews?.enabled).toEqual(true)
|
||||
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()
|
||||
it 'sets reviewDate and reviewer', utils.wrap (done) ->
|
||||
trialRequest = yield TrialRequest.findById(@trialRequest.id)
|
||||
expect(trialRequest.get('reviewDate')).toBeDefined()
|
||||
expect(trialRequest.get('reviewer').equals(@admin._id))
|
||||
expect(new Date(trialRequest.get('reviewDate'))).toBeLessThan(new Date())
|
||||
done()
|
||||
|
||||
it 'does not give the user two enrollments', utils.wrap (done) ->
|
||||
prepaids = yield Prepaid.find({'properties.trialRequestID': @trialRequest._id})
|
||||
expect(prepaids.length).toEqual(0)
|
||||
done()
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
FacebookHandler = require 'core/social-handlers/FacebookHandler'
|
||||
|
||||
mockAuthEvent =
|
||||
response:
|
||||
authResponse:
|
||||
accessToken: 'aksdhjflkqjrj245234b52k345q344le4j4k5l45j45s4dkljvdaskl'
|
||||
userID: '4301938'
|
||||
expiresIn: 5138
|
||||
signedRequest: 'akjsdhfjkhea.3423nkfkdsejnfkd'
|
||||
status: 'connected'
|
||||
|
||||
window.FB ?= {
|
||||
api: ->
|
||||
login: ->
|
||||
}
|
||||
|
||||
describe 'lib/FacebookHandler.coffee', ->
|
||||
it 'on facebook-logged-in, gets data from FB and sends a patch to the server', ->
|
||||
me.clear({silent: true})
|
||||
me.markToRevert()
|
||||
me.set({_id: '12345'})
|
||||
|
||||
facebookHandler = new FacebookHandler()
|
||||
facebookHandler.loginThroughFacebook()
|
||||
Backbone.Mediator.publish 'auth:logged-in-with-facebook', mockAuthEvent
|
||||
|
|
@ -6,16 +6,12 @@ describe 'CreateAccountModal', ->
|
|||
modal = null
|
||||
|
||||
initModal = (options) ->
|
||||
application.facebookHandler.fakeAPI()
|
||||
application.gplusHandler.fakeAPI()
|
||||
modal = new CreateAccountModal(options)
|
||||
modal.render()
|
||||
modal.render = _.noop
|
||||
jasmine.demoModal(modal)
|
||||
window.gapi =
|
||||
client:
|
||||
load: _.noop
|
||||
window.FB =
|
||||
login: _.noop
|
||||
api: _.noop
|
||||
|
||||
afterEach ->
|
||||
modal.stopListening()
|
||||
|
@ -37,21 +33,21 @@ describe 'CreateAccountModal', ->
|
|||
|
||||
it 'fails if nothing is in the form, showing errors for email and password', ->
|
||||
modal.$('form').each (i, el) -> el.reset()
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
expect(jasmine.Ajax.requests.all().length).toBe(0)
|
||||
expect(modal.$('.has-error').length).toBe(2)
|
||||
|
||||
it 'fails if email is missing', ->
|
||||
modal.$('form').each (i, el) -> el.reset()
|
||||
forms.objectToForm(modal.$el, { name: 'Name', password: 'xyzzy' })
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
expect(jasmine.Ajax.requests.all().length).toBe(0)
|
||||
expect(modal.$('.has-error').length).toBeTruthy()
|
||||
|
||||
it 'signs up if only email and password is provided', ->
|
||||
modal.$('form').each (i, el) -> el.reset()
|
||||
forms.objectToForm(modal.$el, { email: 'some@email.com', password: 'xyzzy' })
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
requests = jasmine.Ajax.requests.all()
|
||||
expect(requests.length).toBe(1)
|
||||
expect(modal.$el.has('.has-warning').length).toBeFalsy()
|
||||
|
@ -62,7 +58,7 @@ describe 'CreateAccountModal', ->
|
|||
beforeEach ->
|
||||
modal.$('form').each (i, el) -> el.reset()
|
||||
forms.objectToForm(modal.$el, { email: 'some@email.com', password: 'xyzzy', classCode: 'qwerty' })
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
expect(jasmine.Ajax.requests.all().length).toBe(1)
|
||||
|
||||
it 'checks for Classroom existence if a class code was entered', ->
|
||||
|
@ -84,11 +80,9 @@ describe 'CreateAccountModal', ->
|
|||
describe 'the Classroom does not exist', ->
|
||||
it 'shows an error and clears the field', ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
console.log 'school input?', modal.$('#class-code-input').val()
|
||||
request.respondWith({status: 404, responseText: JSON.stringify({})})
|
||||
expect(jasmine.Ajax.requests.all().length).toBe(1)
|
||||
expect(modal.$el.has('.has-error').length).toBeTruthy()
|
||||
console.log 'school input?', modal.$('#class-code-input').val()
|
||||
expect(modal.$('#class-code-input').val()).toBe('')
|
||||
|
||||
|
||||
|
@ -98,12 +92,9 @@ describe 'CreateAccountModal', ->
|
|||
|
||||
beforeEach ->
|
||||
initModal()
|
||||
application.gplusHandler.trigger 'render-login-buttons'
|
||||
signupButton = modal.$('#gplus-signup-btn')
|
||||
expect(signupButton.attr('disabled')).toBeFalsy()
|
||||
signupButton.click()
|
||||
application.gplusHandler.fakeGPlusLogin()
|
||||
application.gplusHandler.trigger 'person-loaded', { firstName: 'Mr', lastName: 'Bean', gplusID: 'abcd', email: 'some@email.com' }
|
||||
|
||||
it 'checks to see if the user already exists in our system', ->
|
||||
requests = jasmine.Ajax.requests.all()
|
||||
|
@ -131,7 +122,7 @@ describe 'CreateAccountModal', ->
|
|||
describe 'and the user finishes signup anyway with new info', ->
|
||||
beforeEach ->
|
||||
forms.objectToForm(modal.$el, { email: 'some@email.com', schoolName: 'Hogwarts' })
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
|
||||
it 'upserts the values to the new user', ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
|
@ -151,7 +142,7 @@ describe 'CreateAccountModal', ->
|
|||
|
||||
describe 'and the user finishes signup', ->
|
||||
beforeEach ->
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
|
||||
it 'creates the user with the gplus attributes', ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
|
@ -167,12 +158,9 @@ describe 'CreateAccountModal', ->
|
|||
|
||||
beforeEach ->
|
||||
initModal()
|
||||
Backbone.Mediator.publish 'auth:facebook-api-loaded', {}
|
||||
signupButton = modal.$('#facebook-signup-btn')
|
||||
expect(signupButton.attr('disabled')).toBeFalsy()
|
||||
signupButton.click()
|
||||
application.facebookHandler.fakeFacebookLogin()
|
||||
application.facebookHandler.trigger 'person-loaded', { firstName: 'Mr', lastName: 'Bean', facebookID: 'abcd', email: 'some@email.com' }
|
||||
|
||||
it 'checks to see if the user already exists in our system', ->
|
||||
requests = jasmine.Ajax.requests.all()
|
||||
|
@ -200,7 +188,7 @@ describe 'CreateAccountModal', ->
|
|||
describe 'and the user finishes signup anyway with new info', ->
|
||||
beforeEach ->
|
||||
forms.objectToForm(modal.$el, { email: 'some@email.com', schoolName: 'Hogwarts' })
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
|
||||
it 'upserts the values to the new user', ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
|
@ -220,7 +208,7 @@ describe 'CreateAccountModal', ->
|
|||
|
||||
describe 'and the user finishes signup', ->
|
||||
beforeEach ->
|
||||
modal.$('form:first').submit()
|
||||
modal.$('form').submit()
|
||||
|
||||
it 'creates the user with the facebook attributes', ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
|
|
Loading…
Add table
Reference in a new issue