2014-04-12 13:51:02 -04:00
|
|
|
schema = require '../../app/schemas/models/user'
|
2014-02-17 18:19:19 -05:00
|
|
|
crypto = require 'crypto'
|
|
|
|
request = require 'request'
|
|
|
|
User = require './User'
|
|
|
|
Handler = require '../commons/Handler'
|
2014-01-03 13:32:13 -05:00
|
|
|
mongoose = require 'mongoose'
|
2014-01-03 17:28:00 -05:00
|
|
|
config = require '../../server_config'
|
2014-02-17 18:19:19 -05:00
|
|
|
errors = require '../commons/errors'
|
|
|
|
async = require 'async'
|
2014-03-31 16:56:13 -04:00
|
|
|
log = require 'winston'
|
2014-03-31 18:48:22 -04:00
|
|
|
LevelSession = require('../levels/sessions/LevelSession')
|
2014-04-09 19:46:44 -04:00
|
|
|
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
2014-05-19 19:25:41 -04:00
|
|
|
EarnedAchievement = require '../achievements/EarnedAchievement'
|
2014-06-17 16:03:08 -04:00
|
|
|
UserRemark = require './remarks/UserRemark'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset']
|
2014-03-23 12:30:01 -04:00
|
|
|
privateProperties = [
|
|
|
|
'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID',
|
2014-04-24 20:36:07 -04:00
|
|
|
'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement'
|
2014-03-23 12:30:01 -04:00
|
|
|
]
|
2014-04-07 18:21:05 -04:00
|
|
|
candidateProperties = [
|
2014-04-07 20:58:02 -04:00
|
|
|
'jobProfile', 'jobProfileApproved', 'jobProfileNotes'
|
2014-03-23 12:30:01 -04:00
|
|
|
]
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
UserHandler = class UserHandler extends Handler
|
|
|
|
modelClass: User
|
2014-06-17 18:17:19 -04:00
|
|
|
jsonSchema: schema
|
2014-01-03 13:32:13 -05:00
|
|
|
editableProperties: [
|
|
|
|
'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
|
2014-04-21 19:15:23 -04:00
|
|
|
'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails',
|
2014-01-12 14:54:50 -05:00
|
|
|
'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage',
|
2014-04-05 20:05:03 -04:00
|
|
|
'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile'
|
2014-01-03 13:32:13 -05:00
|
|
|
]
|
|
|
|
|
2014-04-07 20:58:02 -04:00
|
|
|
getEditableProperties: (req, document) ->
|
|
|
|
props = super req, document
|
2014-06-17 18:17:19 -04:00
|
|
|
props.push 'permissions' unless config.isProduction
|
|
|
|
props.push 'jobProfileApproved', 'jobProfileNotes' if req.user.isAdmin() # Admins naturally edit these
|
|
|
|
props.push privateProperties... if req.user.isAdmin() # Admins are mad with power
|
2014-04-07 20:58:02 -04:00
|
|
|
props
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
formatEntity: (req, document) ->
|
|
|
|
return null unless document?
|
|
|
|
obj = document.toObject()
|
|
|
|
delete obj[prop] for prop in serverProperties
|
2014-04-07 18:21:05 -04:00
|
|
|
includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id))
|
2014-01-03 13:32:13 -05:00
|
|
|
delete obj[prop] for prop in privateProperties unless includePrivates
|
2014-06-10 19:30:07 -04:00
|
|
|
includeCandidate = includePrivates or (obj.jobProfile?.active and req.user and ('employer' in (req.user.get('permissions') ? [])) and @employerCanViewCandidate req.user, obj)
|
2014-04-07 18:21:05 -04:00
|
|
|
delete obj[prop] for prop in candidateProperties unless includeCandidate
|
2014-01-03 13:32:13 -05:00
|
|
|
return obj
|
|
|
|
|
|
|
|
waterfallFunctions: [
|
|
|
|
# FB access token checking
|
|
|
|
# Check the email is the same as FB reports
|
|
|
|
(req, user, callback) ->
|
|
|
|
fbID = req.query.facebookID
|
|
|
|
fbAT = req.query.facebookAccessToken
|
|
|
|
return callback(null, req, user) unless fbID and fbAT
|
|
|
|
url = "https://graph.facebook.com/me?access_token=#{fbAT}"
|
2014-03-31 16:56:13 -04:00
|
|
|
request(url, (err, response, body) ->
|
|
|
|
log.warn "Error grabbing FB token: #{err}" if err
|
2014-01-03 13:32:13 -05:00
|
|
|
body = JSON.parse(body)
|
|
|
|
emailsMatch = req.body.email is body.email
|
|
|
|
return callback(res:'Invalid Facebook Access Token.', code:422) unless emailsMatch
|
|
|
|
callback(null, req, user)
|
|
|
|
)
|
|
|
|
|
|
|
|
# GPlus access token checking
|
|
|
|
(req, user, callback) ->
|
|
|
|
gpID = req.query.gplusID
|
|
|
|
gpAT = req.query.gplusAccessToken
|
|
|
|
return callback(null, req, user) unless gpID and gpAT
|
|
|
|
url = "https://www.googleapis.com/oauth2/v2/userinfo?access_token=#{gpAT}"
|
2014-03-31 16:56:13 -04:00
|
|
|
request(url, (err, response, body) ->
|
|
|
|
log.warn "Error grabbing G+ token: #{err}" if err
|
2014-01-03 13:32:13 -05:00
|
|
|
body = JSON.parse(body)
|
|
|
|
emailsMatch = req.body.email is body.email
|
|
|
|
return callback(res:'Invalid G+ Access Token.', code:422) unless emailsMatch
|
|
|
|
callback(null, req, user)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Email setting
|
|
|
|
(req, user, callback) ->
|
|
|
|
return callback(null, req, user) unless req.body.email?
|
|
|
|
emailLower = req.body.email.toLowerCase()
|
|
|
|
return callback(null, req, user) if emailLower is user.get('emailLower')
|
|
|
|
User.findOne({emailLower:emailLower}).exec (err, otherUser) ->
|
2014-03-31 16:56:13 -04:00
|
|
|
log.error "Database error setting user email: #{err}" if err
|
2014-01-03 13:32:13 -05:00
|
|
|
return callback(res:'Database error.', code:500) if err
|
|
|
|
|
|
|
|
if (req.query.gplusID or req.query.facebookID) and otherUser
|
|
|
|
# special case, log in as that user
|
|
|
|
return req.logIn(otherUser, (err) ->
|
|
|
|
return callback(res:'Facebook user login error.', code:500) if err
|
|
|
|
return callback(null, req, otherUser)
|
|
|
|
)
|
|
|
|
r = {message:'is already used by another account', property:'email'}
|
|
|
|
return callback({res:r, code:409}) if otherUser
|
|
|
|
user.set('email', req.body.email)
|
|
|
|
callback(null, req, user)
|
|
|
|
|
|
|
|
# Name setting
|
|
|
|
(req, user, callback) ->
|
|
|
|
return callback(null, req, user) unless req.body.name
|
|
|
|
nameLower = req.body.name?.toLowerCase()
|
2014-05-07 15:25:05 -04:00
|
|
|
return callback(null, req, user) unless nameLower
|
|
|
|
return callback(null, req, user) if nameLower is user.get('nameLower') and not user.get('anonymous')
|
2014-04-13 08:25:49 -04:00
|
|
|
User.findOne({nameLower:nameLower,anonymous:false}).exec (err, otherUser) ->
|
2014-03-31 16:56:13 -04:00
|
|
|
log.error "Database error setting user name: #{err}" if err
|
2014-01-03 13:32:13 -05:00
|
|
|
return callback(res:'Database error.', code:500) if err
|
|
|
|
r = {message:'is already used by another account', property:'name'}
|
2014-04-13 08:25:49 -04:00
|
|
|
console.log 'Another user exists' if otherUser
|
2014-01-03 13:32:13 -05:00
|
|
|
return callback({res:r, code:409}) if otherUser
|
|
|
|
user.set('name', req.body.name)
|
|
|
|
callback(null, req, user)
|
|
|
|
]
|
|
|
|
|
|
|
|
getById: (req, res, id) ->
|
2014-02-24 23:27:38 -05:00
|
|
|
if req.user?._id.equals(id)
|
2014-04-09 19:46:44 -04:00
|
|
|
return @sendSuccess(res, @formatEntity(req, req.user, 256))
|
2014-01-03 13:32:13 -05:00
|
|
|
super(req, res, id)
|
2014-03-20 18:40:02 -04:00
|
|
|
|
2014-04-18 14:17:13 -04:00
|
|
|
getNamesByIDs: (req, res) ->
|
2014-02-17 18:19:19 -05:00
|
|
|
ids = req.query.ids or req.body.ids
|
2014-03-03 13:21:51 -05:00
|
|
|
returnWizard = req.query.wizard or req.body.wizard
|
2014-04-18 14:17:13 -04:00
|
|
|
properties = if returnWizard then "name wizard" else "name"
|
|
|
|
@getPropertiesFromMultipleDocuments res, User, properties, ids
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-02-27 17:07:11 -05:00
|
|
|
nameToID: (req, res, name) ->
|
2014-06-07 19:33:14 -04:00
|
|
|
User.findOne({nameLower:unescape(name).toLowerCase(),anonymous:false}).exec (err, otherUser) ->
|
2014-02-27 17:42:11 -05:00
|
|
|
res.send(if otherUser then otherUser._id else JSON.stringify(''))
|
2014-02-27 17:07:11 -05:00
|
|
|
res.end()
|
|
|
|
|
2014-04-12 17:13:26 -04:00
|
|
|
getSimulatorLeaderboard: (req, res) ->
|
2014-04-14 17:52:21 -04:00
|
|
|
queryParameters = @getSimulatorLeaderboardQueryParameters(req)
|
|
|
|
leaderboardQuery = User.find(queryParameters.query).select("name simulatedBy simulatedFor").sort({"simulatedBy":queryParameters.sortOrder}).limit(queryParameters.limit)
|
|
|
|
leaderboardQuery.exec (err, otherUsers) ->
|
|
|
|
otherUsers = _.reject otherUsers, _id: req.user._id if req.query.scoreOffset isnt -1
|
|
|
|
otherUsers ?= []
|
|
|
|
res.send(otherUsers)
|
|
|
|
res.end()
|
|
|
|
|
|
|
|
getMySimulatorLeaderboardRank: (req, res) ->
|
|
|
|
req.query.order = 1
|
|
|
|
queryParameters = @getSimulatorLeaderboardQueryParameters(req)
|
|
|
|
User.count queryParameters.query, (err, count) =>
|
|
|
|
return @sendDatabaseError(res, err) if err
|
|
|
|
res.send JSON.stringify(count + 1)
|
|
|
|
|
2014-06-08 12:35:35 -04:00
|
|
|
getSimulatorLeaderboardQueryParameters: (req) ->
|
2014-04-12 17:13:26 -04:00
|
|
|
@validateSimulateLeaderboardRequestParameters(req)
|
2014-04-15 13:45:54 -04:00
|
|
|
|
2014-04-12 17:13:26 -04:00
|
|
|
query = {}
|
2014-04-14 11:46:46 -04:00
|
|
|
sortOrder = -1
|
|
|
|
limit = if req.query.limit > 30 then 30 else req.query.limit
|
2014-04-13 18:28:16 -04:00
|
|
|
if req.query.scoreOffset isnt -1
|
2014-04-12 17:13:26 -04:00
|
|
|
simulatedByQuery = {}
|
2014-04-13 18:28:16 -04:00
|
|
|
simulatedByQuery[if req.query.order is 1 then "$gt" else "$lte"] = req.query.scoreOffset
|
2014-04-12 17:13:26 -04:00
|
|
|
query.simulatedBy = simulatedByQuery
|
2014-04-14 11:46:46 -04:00
|
|
|
sortOrder = 1 if req.query.order is 1
|
2014-04-14 14:39:30 -04:00
|
|
|
else
|
|
|
|
query.simulatedBy = {"$exists": true}
|
2014-04-14 17:52:21 -04:00
|
|
|
{query: query, sortOrder: sortOrder, limit: limit}
|
2014-04-12 17:13:26 -04:00
|
|
|
|
|
|
|
validateSimulateLeaderboardRequestParameters: (req) ->
|
|
|
|
req.query.order = parseInt(req.query.order) ? -1
|
|
|
|
req.query.scoreOffset = parseFloat(req.query.scoreOffset) ? 100000
|
|
|
|
req.query.limit = parseInt(req.query.limit) ? 20
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
post: (req, res) ->
|
|
|
|
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
|
2014-02-24 23:27:38 -05:00
|
|
|
return @sendBadInputError(res, 'Must have an anonymous user to post with.') unless req.user
|
2014-01-03 13:32:13 -05:00
|
|
|
return @sendBadInputError(res, 'Existing users cannot create new ones.') unless req.user.get('anonymous')
|
|
|
|
req.body._id = req.user._id if req.user.get('anonymous')
|
|
|
|
@put(req, res)
|
|
|
|
|
|
|
|
hasAccessToDocument: (req, document) ->
|
|
|
|
if req.route.method in ['put', 'post', 'patch']
|
2014-02-24 23:27:38 -05:00
|
|
|
return true if req.user?.isAdmin()
|
|
|
|
return req.user?._id.equals(document._id)
|
2014-01-03 13:32:13 -05:00
|
|
|
return true
|
|
|
|
|
|
|
|
getByRelationship: (req, res, args...) ->
|
|
|
|
return @agreeToCLA(req, res) if args[1] is 'agreeToCLA'
|
2014-04-24 20:36:07 -04:00
|
|
|
return @agreeToEmployerAgreement(req,res) if args[1] is 'agreeToEmployerAgreement'
|
2014-02-07 19:11:07 -05:00
|
|
|
return @avatar(req, res, args[0]) if args[1] is 'avatar'
|
2014-04-18 14:17:13 -04:00
|
|
|
return @getNamesByIDs(req, res) if args[1] is 'names'
|
2014-02-27 17:07:11 -05:00
|
|
|
return @nameToID(req, res, args[0]) if args[1] is 'nameToID'
|
2014-06-11 22:38:41 -04:00
|
|
|
return @getLevelSessionsForEmployer(req, res, args[0]) if args[1] is 'level.sessions' and args[2] is 'employer'
|
2014-03-31 18:48:22 -04:00
|
|
|
return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions'
|
2014-04-06 20:01:56 -04:00
|
|
|
return @getCandidates(req, res) if args[1] is 'candidates'
|
2014-06-10 19:30:07 -04:00
|
|
|
return @getEmployers(req, res) if args[1] is 'employers'
|
2014-04-12 17:13:26 -04:00
|
|
|
return @getSimulatorLeaderboard(req, res, args[0]) if args[1] is 'simulatorLeaderboard'
|
2014-04-14 17:52:21 -04:00
|
|
|
return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank'
|
2014-05-19 18:24:16 -04:00
|
|
|
return @getEarnedAchievements(req, res, args[0]) if args[1] is 'achievements'
|
2014-06-10 19:30:07 -04:00
|
|
|
return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2]
|
2014-06-17 16:03:08 -04:00
|
|
|
return @getRemark(req, res, args[0]) if args[1] is 'remark'
|
2014-01-03 13:32:13 -05:00
|
|
|
return @sendNotFoundError(res)
|
2014-04-11 13:33:22 -04:00
|
|
|
super(arguments...)
|
2014-02-01 11:22:26 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
agreeToCLA: (req, res) ->
|
2014-02-24 23:27:38 -05:00
|
|
|
return @sendUnauthorizedError(res) unless req.user
|
2014-01-03 13:32:13 -05:00
|
|
|
doc =
|
|
|
|
user: req.user._id+''
|
|
|
|
email: req.user.get 'email'
|
|
|
|
name: req.user.get 'name'
|
|
|
|
githubUsername: req.body.githubUsername
|
2014-01-05 19:02:50 -05:00
|
|
|
created: new Date()+''
|
2014-01-18 13:16:55 -05:00
|
|
|
collection = mongoose.connection.db.collection 'cla.submissions', (err, collection) =>
|
2014-01-03 13:32:13 -05:00
|
|
|
return @sendDatabaseError(res, err) if err
|
2014-01-18 13:16:55 -05:00
|
|
|
collection.insert doc, (err) =>
|
2014-01-03 13:32:13 -05:00
|
|
|
return @sendDatabaseError(res, err) if err
|
|
|
|
req.user.set('signedCLA', doc.created)
|
2014-01-18 13:16:55 -05:00
|
|
|
req.user.save (err) =>
|
2014-01-03 13:32:13 -05:00
|
|
|
return @sendDatabaseError(res, err) if err
|
2014-01-14 17:13:47 -05:00
|
|
|
@sendSuccess(res, {result:'success'})
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-02-07 19:11:07 -05:00
|
|
|
avatar: (req, res, id) ->
|
2014-04-09 19:46:44 -04:00
|
|
|
@modelClass.findById(id).exec (err, document) =>
|
2014-02-07 19:11:07 -05:00
|
|
|
return @sendDatabaseError(res, err) if err
|
2014-04-09 19:46:44 -04:00
|
|
|
photoURL = document?.get('photoURL')
|
2014-04-12 17:53:09 -04:00
|
|
|
if photoURL
|
|
|
|
photoURL = "/file/#{photoURL}"
|
|
|
|
else
|
|
|
|
photoURL = @buildGravatarURL document, req.query.s, req.query.fallback
|
2014-04-09 19:46:44 -04:00
|
|
|
res.redirect photoURL
|
2014-02-07 19:11:07 -05:00
|
|
|
res.end()
|
|
|
|
|
2014-06-11 22:38:41 -04:00
|
|
|
getLevelSessionsForEmployer: (req, res, userID) ->
|
|
|
|
return @sendUnauthorizedError(res) unless req.user._id+'' is userID or req.user.isAdmin() or ('employer' in req.user.get('permissions'))
|
|
|
|
query = creator: userID, levelID: {$in: ['gridmancer', 'greed', 'dungeon-arena', 'brawlwood', 'gold-rush']}
|
2014-06-18 15:05:40 -04:00
|
|
|
projection = 'levelName levelID team playtime codeLanguage submitted code totalScore'
|
2014-06-11 22:38:41 -04:00
|
|
|
LevelSession.find(query).select(projection).exec (err, documents) =>
|
|
|
|
return @sendDatabaseError(res, err) if err
|
|
|
|
documents = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
|
|
|
|
@sendSuccess(res, documents)
|
|
|
|
|
2014-03-31 18:48:22 -04:00
|
|
|
getLevelSessions: (req, res, userID) ->
|
2014-06-11 22:38:41 -04:00
|
|
|
query = creator: userID
|
2014-06-19 11:07:30 -04:00
|
|
|
isAuthorized = req.user._id+'' is userID or req.user.isAdmin()
|
2014-06-19 11:24:07 -04:00
|
|
|
projection = {}
|
2014-03-31 18:48:22 -04:00
|
|
|
if req.query.project
|
2014-06-19 11:07:30 -04:00
|
|
|
projection[field] = 1 for field in req.query.project.split(',') when isAuthorized or not (field in LevelSessionHandler.privateProperties)
|
2014-06-19 11:24:07 -04:00
|
|
|
else unless isAuthorized
|
|
|
|
projection[field] = 0 for field in LevelSessionHandler.privateProperties
|
2014-06-19 11:07:30 -04:00
|
|
|
|
2014-03-31 18:48:22 -04:00
|
|
|
LevelSession.find(query).select(projection).exec (err, documents) =>
|
|
|
|
return @sendDatabaseError(res, err) if err
|
2014-04-09 19:46:44 -04:00
|
|
|
documents = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
|
2014-03-31 18:48:22 -04:00
|
|
|
@sendSuccess(res, documents)
|
2014-04-25 13:46:43 -04:00
|
|
|
|
2014-05-19 18:24:16 -04:00
|
|
|
getEarnedAchievements: (req, res, userID) ->
|
2014-05-24 14:45:53 -04:00
|
|
|
queryObject = {$query: {user: userID}, $orderby: {changed: -1}}
|
2014-05-29 05:37:35 -04:00
|
|
|
queryObject.$query.notified = false if req.query.notified is 'false'
|
2014-05-24 14:45:53 -04:00
|
|
|
query = EarnedAchievement.find(queryObject)
|
2014-05-19 18:24:16 -04:00
|
|
|
query.exec (err, documents) =>
|
|
|
|
return @sendDatabaseError(res, err) if err?
|
2014-05-24 14:45:53 -04:00
|
|
|
cleandocs = (@formatEntity(req, doc) for doc in documents)
|
|
|
|
for doc in documents # Maybe move this logic elsewhere
|
|
|
|
doc.set('notified', true)
|
|
|
|
doc.save()
|
|
|
|
@sendSuccess(res, cleandocs)
|
2014-05-19 18:24:16 -04:00
|
|
|
|
2014-06-10 19:30:07 -04:00
|
|
|
trackActivity: (req, res, userID, activityName, increment=1) ->
|
|
|
|
return @sendMethodNotAllowed res unless req.method is 'POST'
|
|
|
|
isMe = userID is req.user._id + ''
|
|
|
|
isAuthorized = isMe or req.user.isAdmin()
|
2014-06-19 16:42:51 -04:00
|
|
|
isAuthorized ||= ('employer' in req.user.get('permissions')) and (activityName in ['viewed_by_employer', 'contacted_by_employer'])
|
2014-06-10 19:30:07 -04:00
|
|
|
return @sendUnauthorizedError res unless isAuthorized
|
|
|
|
updateUser = (user) =>
|
|
|
|
activity = user.trackActivity activityName, increment
|
|
|
|
user.update {activity: activity}, (err) =>
|
|
|
|
return @sendDatabaseError res, err if err
|
|
|
|
@sendSuccess res, result: 'success'
|
|
|
|
if isMe
|
|
|
|
updateUser(req.user)
|
|
|
|
else
|
|
|
|
@getDocumentForIdOrSlug userID, (err, user) =>
|
|
|
|
return @sendDatabaseError res, err if err
|
|
|
|
return @sendNotFoundError res unless user
|
|
|
|
updateUser user
|
|
|
|
|
2014-04-24 20:36:07 -04:00
|
|
|
agreeToEmployerAgreement: (req, res) ->
|
|
|
|
userIsAnonymous = req.user?.get('anonymous')
|
|
|
|
if userIsAnonymous then return errors.unauthorized(res, "You need to be logged in to agree to the employer agreeement.")
|
|
|
|
profileData = req.body
|
|
|
|
#TODO: refactor this bit to make it more elegant
|
|
|
|
if not profileData.id or not profileData.positions or not profileData.emailAddress or not profileData.firstName or not profileData.lastName
|
|
|
|
return errors.badInput(res, "You need to have a more complete profile to sign up for this service.")
|
|
|
|
@modelClass.findById(req.user.id).exec (err, user) =>
|
|
|
|
if user.get('employerAt') or user.get('signedEmployerAgreement') or "employer" in user.get('permissions')
|
|
|
|
return errors.conflict(res, "You already have signed the agreement!")
|
|
|
|
#TODO: Search for the current position
|
2014-04-25 10:48:59 -04:00
|
|
|
employerAt = _.filter(profileData.positions.values,"isCurrent")[0]?.company.name ? "Not available"
|
2014-04-25 13:46:43 -04:00
|
|
|
signedEmployerAgreement =
|
2014-04-24 20:36:07 -04:00
|
|
|
linkedinID: profileData.id
|
|
|
|
date: new Date()
|
|
|
|
data: profileData
|
2014-04-25 13:46:43 -04:00
|
|
|
updateObject =
|
2014-04-24 20:36:07 -04:00
|
|
|
"employerAt": employerAt
|
|
|
|
"signedEmployerAgreement": signedEmployerAgreement
|
|
|
|
$push: "permissions":'employer'
|
2014-04-25 13:46:43 -04:00
|
|
|
|
2014-04-24 20:36:07 -04:00
|
|
|
User.update {"_id": req.user.id}, updateObject, (err, result) =>
|
|
|
|
if err? then return errors.serverError(res, "There was an issue updating the user object to reflect employer status: #{err}")
|
|
|
|
res.send({"message": "The agreement was successful."})
|
|
|
|
res.end()
|
2014-04-25 13:46:43 -04:00
|
|
|
|
2014-04-06 20:01:56 -04:00
|
|
|
getCandidates: (req, res) ->
|
2014-04-07 18:21:05 -04:00
|
|
|
authorized = req.user.isAdmin() or ('employer' in req.user.get('permissions'))
|
2014-06-24 13:15:38 -04:00
|
|
|
months = if req.user.isAdmin() then 12 else 2
|
|
|
|
since = (new Date((new Date()) - months * 30.4 * 86400 * 1000)).toISOString()
|
2014-04-23 13:21:58 -04:00
|
|
|
query = {'jobProfile.updated': {$gt: since}}
|
|
|
|
query['jobProfile.active'] = true unless req.user.isAdmin()
|
2014-06-10 19:30:07 -04:00
|
|
|
selection = 'jobProfile jobProfileApproved photoURL'
|
2014-06-17 16:03:08 -04:00
|
|
|
selection += ' email name' if authorized
|
2014-04-07 18:21:05 -04:00
|
|
|
User.find(query).select(selection).exec (err, documents) =>
|
2014-04-06 20:01:56 -04:00
|
|
|
return @sendDatabaseError(res, err) if err
|
2014-04-18 15:48:13 -04:00
|
|
|
candidates = (candidate for candidate in documents when @employerCanViewCandidate req.user, candidate.toObject())
|
|
|
|
candidates = (@formatCandidate(authorized, candidate) for candidate in candidates)
|
2014-04-07 18:21:05 -04:00
|
|
|
@sendSuccess(res, candidates)
|
|
|
|
|
|
|
|
formatCandidate: (authorized, document) ->
|
2014-07-03 16:59:10 -04:00
|
|
|
fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['_id','jobProfile', 'jobProfileApproved']
|
2014-04-07 18:21:05 -04:00
|
|
|
obj = _.pick document.toObject(), fields
|
2014-07-03 16:59:10 -04:00
|
|
|
obj.photoURL ||= obj.jobProfile.photoURL #if authorized
|
2014-07-02 18:48:26 -04:00
|
|
|
subfields = ['country', 'city', 'lookingFor', 'jobTitle', 'skills', 'experience', 'updated', 'active', 'shortDescription', 'curated', 'visa']
|
2014-04-07 18:21:05 -04:00
|
|
|
if authorized
|
2014-04-11 15:49:44 -04:00
|
|
|
subfields = subfields.concat ['name']
|
2014-04-07 18:21:05 -04:00
|
|
|
obj.jobProfile = _.pick obj.jobProfile, subfields
|
|
|
|
obj
|
2014-03-31 18:48:22 -04:00
|
|
|
|
2014-04-18 12:53:28 -04:00
|
|
|
employerCanViewCandidate: (employer, candidate) ->
|
|
|
|
return true if employer.isAdmin()
|
|
|
|
for job in candidate.jobProfile?.work ? []
|
|
|
|
# TODO: be smarter about different ways to write same company names to ensure privacy.
|
|
|
|
# We'll have to manually pay attention to how we set employer names for now.
|
2014-04-18 15:48:13 -04:00
|
|
|
if job.employer?.toLowerCase() is employer.get('employerAt')?.toLowerCase()
|
|
|
|
log.info "#{employer.get('name')} at #{employer.get('employerAt')} can't see #{candidate.jobProfile.name} because s/he worked there."
|
2014-04-18 12:53:28 -04:00
|
|
|
return false if job.employer?.toLowerCase() is employer.get('employerAt')?.toLowerCase()
|
|
|
|
true
|
|
|
|
|
2014-06-10 19:30:07 -04:00
|
|
|
getEmployers: (req, res) ->
|
|
|
|
return @sendUnauthorizedError(res) unless req.user.isAdmin()
|
2014-06-17 18:17:19 -04:00
|
|
|
query = {employerAt: {$exists: true, $ne: ''}}
|
2014-06-10 19:30:07 -04:00
|
|
|
selection = 'name firstName lastName email activity signedEmployerAgreement photoURL employerAt'
|
|
|
|
User.find(query).select(selection).lean().exec (err, documents) =>
|
|
|
|
return @sendDatabaseError res, err if err
|
|
|
|
@sendSuccess res, documents
|
|
|
|
|
2014-04-12 17:53:09 -04:00
|
|
|
buildGravatarURL: (user, size, fallback) ->
|
2014-04-09 19:46:44 -04:00
|
|
|
emailHash = @buildEmailHash user
|
2014-04-12 17:53:09 -04:00
|
|
|
fallback ?= "http://codecombat.com/file/db/thang.type/52a00d55cf1818f2be00000b/portrait.png"
|
|
|
|
fallback = "http://codecombat.com#{fallback}" unless /^http/.test fallback
|
|
|
|
"https://www.gravatar.com/avatar/#{emailHash}?s=#{size}&default=#{fallback}"
|
2014-04-09 19:46:44 -04:00
|
|
|
|
|
|
|
buildEmailHash: (user) ->
|
|
|
|
# emailHash is used by gravatar
|
|
|
|
hash = crypto.createHash('md5')
|
|
|
|
if user.get('email')
|
|
|
|
hash.update(_.trim(user.get('email')).toLowerCase())
|
|
|
|
else
|
|
|
|
hash.update(user.get('_id') + '')
|
|
|
|
hash.digest('hex')
|
2014-03-31 18:48:22 -04:00
|
|
|
|
2014-06-17 16:03:08 -04:00
|
|
|
getRemark: (req, res, userID) ->
|
|
|
|
return @sendUnauthorizedError(res) unless req.user.isAdmin()
|
|
|
|
query = user: userID
|
|
|
|
projection = null
|
|
|
|
if req.query.project
|
|
|
|
projection = {}
|
|
|
|
projection[field] = 1 for field in req.query.project.split(',')
|
|
|
|
UserRemark.findOne(query).select(projection).exec (err, remark) =>
|
|
|
|
return @sendDatabaseError res, err if err
|
|
|
|
return @sendNotFoundError res unless remark?
|
|
|
|
@sendSuccess res, remark
|
|
|
|
|
2014-06-24 12:14:26 -04:00
|
|
|
statHandlers:
|
|
|
|
gamesCompleted: (done) ->
|
|
|
|
LevelSession = require '../levels/sessions/LevelSession'
|
|
|
|
|
|
|
|
User.find {}, (err, users) ->
|
|
|
|
async.eachSeries users, ((user, doneWithUser) ->
|
|
|
|
userID = user.get('_id').toHexString()
|
|
|
|
|
2014-06-24 14:27:22 -04:00
|
|
|
LevelSession.count {creator: userID, 'state.completed': true}, (err, count) ->
|
|
|
|
update = if count then {$set: 'stats.gamesCompleted': count} else {$unset: 'stats.gamesCompleted': ''}
|
|
|
|
User.findByIdAndUpdate user.get('_id'), update, (err) ->
|
|
|
|
log.error err if err?
|
|
|
|
doneWithUser()
|
|
|
|
), done
|
|
|
|
|
|
|
|
articleEdits: (done) ->
|
|
|
|
Article = require '../articles/Article'
|
|
|
|
|
|
|
|
User.find {}, (err, users) ->
|
|
|
|
async.eachSeries users, ((user, doneWithUser) ->
|
|
|
|
userID = user.get('_id').toHexString()
|
|
|
|
|
|
|
|
Article.count {creator: userID}, (err, count) ->
|
|
|
|
update = if count then {$set: 'stats.articleEdits': count} else {$unset: 'stats.articleEdits': ''}
|
|
|
|
User.findByIdAndUpdate user.get('_id'), update, (err) ->
|
2014-06-24 12:14:26 -04:00
|
|
|
log.error err if err?
|
|
|
|
doneWithUser()
|
|
|
|
), done
|
|
|
|
|
|
|
|
recalculate: (req, res, statName) ->
|
|
|
|
return @sendForbiddenError(res) unless req.user.isAdmin()
|
|
|
|
|
|
|
|
if statName of @statHandlers
|
2014-06-24 13:59:36 -04:00
|
|
|
@statHandlers[statName]()
|
2014-06-24 12:14:26 -04:00
|
|
|
return @sendAccepted res, {}
|
|
|
|
else return @sendNotFoundError(res)
|
|
|
|
|
2014-06-17 16:03:08 -04:00
|
|
|
|
2014-03-16 11:11:55 -04:00
|
|
|
module.exports = new UserHandler()
|