2014-06-30 22:16:26 -04:00
|
|
|
mongoose = require 'mongoose'
|
|
|
|
jsonschema = require '../../app/schemas/models/user'
|
|
|
|
crypto = require 'crypto'
|
|
|
|
{salt, isProduction} = require '../../server_config'
|
2014-01-24 14:47:14 -05:00
|
|
|
mail = require '../commons/mail'
|
2014-03-11 22:17:58 -04:00
|
|
|
log = require 'winston'
|
2014-07-09 14:23:05 -04:00
|
|
|
plugins = require '../plugins/plugins'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-01-17 12:53:17 -05:00
|
|
|
sendwithus = require '../sendwithus'
|
2014-08-10 02:11:26 -04:00
|
|
|
delighted = require '../delighted'
|
2014-01-17 12:53:17 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
UserSchema = new mongoose.Schema({
|
|
|
|
dateCreated:
|
|
|
|
type: Date
|
|
|
|
'default': Date.now
|
|
|
|
}, {strict: false})
|
|
|
|
|
|
|
|
UserSchema.pre('init', (next) ->
|
|
|
|
return next() unless jsonschema.properties?
|
|
|
|
for prop, sch of jsonschema.properties
|
2014-04-21 19:15:23 -04:00
|
|
|
continue if prop is 'emails' # defaults may change, so don't carry them over just yet
|
2014-01-03 13:32:13 -05:00
|
|
|
@set(prop, sch.default) if sch.default?
|
|
|
|
next()
|
|
|
|
)
|
|
|
|
|
|
|
|
UserSchema.post('init', ->
|
|
|
|
@set('anonymous', false) if @get('email')
|
|
|
|
)
|
|
|
|
|
|
|
|
UserSchema.methods.isAdmin = ->
|
|
|
|
p = @get('permissions')
|
|
|
|
return p and 'admin' in p
|
2014-04-25 14:12:52 -04:00
|
|
|
|
2014-07-10 12:00:32 -04:00
|
|
|
UserSchema.methods.isAnonymous = ->
|
|
|
|
@get 'anonymous'
|
|
|
|
|
2014-06-10 19:30:07 -04:00
|
|
|
UserSchema.methods.trackActivity = (activityName, increment) ->
|
|
|
|
now = new Date()
|
|
|
|
increment ?= parseInt increment or 1
|
|
|
|
increment = Math.max increment, 0
|
|
|
|
activity = @get('activity') ? {}
|
|
|
|
activity[activityName] ?= {first: now, count: 0}
|
|
|
|
activity[activityName].count += increment
|
|
|
|
activity[activityName].last = now
|
|
|
|
@set 'activity', activity
|
|
|
|
activity
|
|
|
|
|
2014-04-21 19:15:23 -04:00
|
|
|
emailNameMap =
|
|
|
|
generalNews: 'announcement'
|
|
|
|
adventurerNews: 'tester'
|
|
|
|
artisanNews: 'level_creator'
|
|
|
|
archmageNews: 'developer'
|
|
|
|
scribeNews: 'article_editor'
|
|
|
|
diplomatNews: 'translator'
|
|
|
|
ambassadorNews: 'support'
|
|
|
|
anyNotes: 'notification'
|
2014-04-25 14:12:52 -04:00
|
|
|
|
2014-04-21 19:15:23 -04:00
|
|
|
UserSchema.methods.setEmailSubscription = (newName, enabled) ->
|
|
|
|
oldSubs = _.clone @get('emailSubscriptions')
|
|
|
|
if oldSubs and oldName = emailNameMap[newName]
|
|
|
|
oldSubs = (s for s in oldSubs when s isnt oldName)
|
|
|
|
oldSubs.push(oldName) if enabled
|
|
|
|
@set('emailSubscriptions', oldSubs)
|
2014-04-25 14:12:52 -04:00
|
|
|
|
2014-04-23 12:19:07 -04:00
|
|
|
newSubs = _.clone(@get('emails') or _.cloneDeep(jsonschema.properties.emails.default))
|
2014-04-21 19:15:23 -04:00
|
|
|
newSubs[newName] ?= {}
|
|
|
|
newSubs[newName].enabled = enabled
|
|
|
|
@set('emails', newSubs)
|
|
|
|
@newsSubsChanged = true if newName in mail.NEWS_GROUPS
|
2014-04-25 14:12:52 -04:00
|
|
|
|
2014-04-21 19:15:23 -04:00
|
|
|
UserSchema.methods.isEmailSubscriptionEnabled = (newName) ->
|
|
|
|
emails = @get 'emails'
|
|
|
|
if not emails
|
|
|
|
oldSubs = @get('emailSubscriptions')
|
|
|
|
oldName = emailNameMap[newName]
|
|
|
|
return oldName and oldName in oldSubs if oldSubs
|
|
|
|
emails ?= {}
|
2014-04-23 12:19:07 -04:00
|
|
|
_.defaults emails, _.cloneDeep(jsonschema.properties.emails.default)
|
2014-04-21 19:15:23 -04:00
|
|
|
return emails[newName]?.enabled
|
2014-03-11 22:17:58 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
UserSchema.statics.updateMailChimp = (doc, callback) ->
|
2014-04-21 19:15:23 -04:00
|
|
|
return callback?() unless isProduction or GLOBAL.testing
|
2014-01-03 13:32:13 -05:00
|
|
|
return callback?() if doc.updatedMailChimp
|
|
|
|
return callback?() unless doc.get('email')
|
|
|
|
existingProps = doc.get('mailChimp')
|
|
|
|
emailChanged = (not existingProps) or existingProps?.email isnt doc.get('email')
|
2014-04-21 19:15:23 -04:00
|
|
|
return callback?() unless emailChanged or doc.newsSubsChanged
|
|
|
|
|
|
|
|
newGroups = []
|
|
|
|
for [mailchimpEmailGroup, emailGroup] in _.zip(mail.MAILCHIMP_GROUPS, mail.NEWS_GROUPS)
|
|
|
|
newGroups.push(mailchimpEmailGroup) if doc.isEmailSubscriptionEnabled(emailGroup)
|
2014-04-25 14:12:52 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
if (not existingProps) and newGroups.length is 0
|
|
|
|
return callback?() # don't add totally unsubscribed people to the list
|
2014-04-25 14:12:52 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
params = {}
|
2014-01-24 14:47:14 -05:00
|
|
|
params.id = mail.MAILCHIMP_LIST_ID
|
2014-06-30 22:16:26 -04:00
|
|
|
params.email = if existingProps then {leid: existingProps.leid} else {email: doc.get('email')}
|
|
|
|
params.merge_vars = {groupings: [{id: mail.MAILCHIMP_GROUP_ID, groups: newGroups}]}
|
2014-01-03 13:32:13 -05:00
|
|
|
params.update_existing = true
|
2014-01-23 12:28:21 -05:00
|
|
|
params.double_optin = false
|
2014-03-11 22:17:58 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
onSuccess = (data) ->
|
|
|
|
doc.set('mailChimp', data)
|
|
|
|
doc.updatedMailChimp = true
|
|
|
|
doc.save()
|
|
|
|
callback?()
|
2014-03-11 22:17:58 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
onFailure = (error) ->
|
2014-03-11 22:17:58 -04:00
|
|
|
log.error 'failed to subscribe', error, callback?
|
2014-01-03 13:32:13 -05:00
|
|
|
doc.updatedMailChimp = true
|
|
|
|
callback?()
|
2014-03-11 22:17:58 -04:00
|
|
|
|
2014-04-15 12:01:51 -04:00
|
|
|
mc?.lists.subscribe params, onSuccess, onFailure
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-06-27 15:30:31 -04:00
|
|
|
UserSchema.statics.statsMapping =
|
|
|
|
edits:
|
|
|
|
article: 'stats.articleEdits'
|
|
|
|
level: 'stats.levelEdits'
|
|
|
|
'level.component': 'stats.levelComponentEdits'
|
|
|
|
'level.system': 'stats.levelSystemEdits'
|
|
|
|
'thang.type': 'stats.thangTypeEdits'
|
2014-06-28 11:44:07 -04:00
|
|
|
translations:
|
|
|
|
article: 'stats.articleTranslationPatches'
|
|
|
|
level: 'stats.levelTranslationPatches'
|
|
|
|
'level.component': 'stats.levelComponentTranslationPatches'
|
|
|
|
'level.system': 'stats.levelSystemTranslationPatches'
|
|
|
|
'thang.type': 'stats.thangTypeTranslationPatches'
|
|
|
|
misc:
|
|
|
|
article: 'stats.articleMiscPatches'
|
|
|
|
level: 'stats.levelMiscPatches'
|
|
|
|
'level.component': 'stats.levelComponentMiscPatches'
|
|
|
|
'level.system': 'stats.levelSystemMiscPatches'
|
|
|
|
'thang.type': 'stats.thangTypeMiscPatches'
|
|
|
|
|
2014-06-27 15:30:31 -04:00
|
|
|
UserSchema.statics.incrementStat = (id, statName, done, inc=1) ->
|
2014-07-24 08:41:06 -04:00
|
|
|
id = mongoose.Types.ObjectId id if _.isString id
|
|
|
|
@findById id, (err, user) ->
|
|
|
|
log.error err if err?
|
|
|
|
err = new Error "Could't find user with id '#{id}'" unless user or err
|
|
|
|
return done err if err?
|
|
|
|
user.incrementStat statName, done, inc=1
|
2014-07-05 11:02:48 -04:00
|
|
|
|
|
|
|
UserSchema.methods.incrementStat = (statName, done, inc=1) ->
|
|
|
|
@set statName, (@get(statName) or 0) + inc
|
2014-08-07 16:03:00 -04:00
|
|
|
@save (err) -> done?(err)
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-07-10 12:00:32 -04:00
|
|
|
UserSchema.statics.unconflictName = unconflictName = (name, done) ->
|
|
|
|
User.findOne {slug: _.str.slugify(name)}, (err, otherUser) ->
|
|
|
|
return done err if err?
|
|
|
|
return done null, name unless otherUser
|
|
|
|
suffix = _.random(0, 9) + ''
|
|
|
|
unconflictName name + suffix, done
|
|
|
|
|
|
|
|
UserSchema.methods.register = (done) ->
|
|
|
|
@set('anonymous', false)
|
|
|
|
@set('permissions', ['admin']) if not isProduction
|
|
|
|
if (name = @get 'name')? and name isnt ''
|
|
|
|
unconflictName name, (err, uniqueName) =>
|
|
|
|
return done err if err
|
|
|
|
@set 'name', uniqueName
|
|
|
|
done()
|
|
|
|
else done()
|
|
|
|
data =
|
|
|
|
email_id: sendwithus.templates.welcome_email
|
|
|
|
recipient:
|
|
|
|
address: @get 'email'
|
|
|
|
sendwithus.api.send data, (err, result) ->
|
|
|
|
log.error "sendwithus post-save error: #{err}, result: #{result}" if err
|
2014-08-10 02:11:26 -04:00
|
|
|
delighted.addDelightedUser @
|
2014-07-10 12:00:32 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
UserSchema.pre('save', (next) ->
|
|
|
|
@set('emailLower', @get('email')?.toLowerCase())
|
|
|
|
@set('nameLower', @get('name')?.toLowerCase())
|
|
|
|
pwd = @get('password')
|
|
|
|
if @get('password')
|
|
|
|
@set('passwordHash', User.hashPassword(pwd))
|
|
|
|
@set('password', undefined)
|
2014-07-10 12:00:32 -04:00
|
|
|
if @get('email') and @get('anonymous') # a user registers
|
|
|
|
@register next
|
|
|
|
else
|
|
|
|
next()
|
2014-01-03 13:32:13 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
UserSchema.post 'save', (doc) ->
|
|
|
|
UserSchema.statics.updateMailChimp(doc)
|
|
|
|
|
|
|
|
UserSchema.statics.hashPassword = (password) ->
|
|
|
|
password = password.toLowerCase()
|
|
|
|
shasum = crypto.createHash('sha512')
|
|
|
|
shasum.update(salt + password)
|
|
|
|
shasum.digest('hex')
|
|
|
|
|
2014-07-22 14:07:00 -04:00
|
|
|
UserSchema.statics.privateProperties = [
|
|
|
|
'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID',
|
|
|
|
'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement'
|
|
|
|
]
|
|
|
|
UserSchema.statics.jsonSchema = jsonschema
|
|
|
|
UserSchema.statics.editableProperties = [
|
|
|
|
'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
|
|
|
|
'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails',
|
|
|
|
'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage',
|
2014-08-04 15:48:41 -04:00
|
|
|
'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile', 'savedEmployerFilterAlerts'
|
2014-07-22 14:07:00 -04:00
|
|
|
]
|
|
|
|
|
2014-07-09 14:23:05 -04:00
|
|
|
UserSchema.plugin plugins.NamedPlugin
|
|
|
|
|
2014-03-11 22:17:58 -04:00
|
|
|
module.exports = User = mongoose.model('User', UserSchema)
|
2014-05-31 17:19:55 -04:00
|
|
|
|
|
|
|
AchievablePlugin = require '../plugins/achievements'
|
|
|
|
UserSchema.plugin(AchievablePlugin)
|