Refactor server models into /server/models, remove request and mongoose from test globals

This commit is contained in:
Scott Erickson 2016-04-06 10:56:06 -07:00
parent debc337dd5
commit c3e5416166
144 changed files with 957 additions and 872 deletions

View file

@ -12,7 +12,7 @@ var mongoose = require('mongoose');
database.connect();
var Achievement = require('../server/achievements/Achievement');
var Achievement = require('../server/models/Achievement');
var tierNames = {
"Wood": 1,

View file

@ -18,7 +18,7 @@ do (setupLodash = this) ->
database.connect()
UserHandler = require '../server/users/user_handler'
User = require '../server/users/User'
User = require '../server/models/User'
startDate = new Date 2015, 11, 1

View file

@ -7,9 +7,9 @@ async = require 'async'
serverSetup = require '../server_setup'
sendwithus = require '../server/sendwithus'
User = require '../server/users/User'
Level = require '../server/levels/Level'
LevelSession = require '../server/levels/sessions/LevelSession'
User = require '../server/models/User'
Level = require '../server/models/Level'
LevelSession = require '../server/models/LevelSession'
tournamentResults = require '../app/views/play/ladder/tournament_results'
alreadyEmailed = []

View file

@ -12,8 +12,8 @@ do (setupLodash = this) ->
database.connect()
User = require '../server/users/User'
Payment = require '../server/payments/Payment'
User = require '../server/models/User'
Payment = require '../server/models/Payment'
PaymentHandler = require '../server/payments/payment_handler'
t0 = new Date().getTime()

View file

@ -13,9 +13,9 @@ do (setupLodash = this) ->
database.connect()
LocalMongo = require '../app/lib/LocalMongo'
User = require '../server/users/User'
EarnedAchievement = require '../server/achievements/EarnedAchievement'
Achievement = require '../server/achievements/Achievement'
User = require '../server/models/User'
EarnedAchievement = require '../server/models/EarnedAchievement'
Achievement = require '../server/models/Achievement'
Achievement.loadAchievements (achievementCategories) ->
# Really, it's just the 'users' category, since we don't keep all the LevelSession achievements in memory, rather letting the clients make those.
userAchievements = achievementCategories.users

View file

@ -15,7 +15,7 @@ do (setupLodash = this) ->
database.connect()
UserHandler = require '../server/users/user_handler'
User = require '../server/users/User'
User = require '../server/models/User'
userIDs = [
# Fill in userID strings here

View file

@ -199,8 +199,8 @@ repeatableAchievements =
b: 1
c: 0
Achievement = require '../server/achievements/Achievement'
EarnedAchievement = require '../server/achievements/EarnedAchievement'
Achievement = require '../server/models/Achievement'
EarnedAchievement = require '../server/models/EarnedAchievement'
Achievement.find {}, (err, achievements) ->
achievementIDs = (achievement.get('_id') + '' for achievement in achievements)

View file

@ -6,8 +6,8 @@ GLOBAL.Aether = Aether = require 'aether'
async = require 'async'
serverSetup = require '../server_setup'
Level = require '../server/levels/Level'
LevelSession = require '../server/levels/sessions/LevelSession'
Level = require '../server/models/Level'
LevelSession = require '../server/models/LevelSession'
{createAetherOptions} = require '../app/lib/aether_utils'
i = 0

View file

@ -1,4 +1,4 @@
Achievement = require './Achievement'
Achievement = require './../models/Achievement'
Handler = require '../commons/Handler'
class AchievementHandler extends Handler

View file

@ -1,14 +1,14 @@
log = require 'winston'
mongoose = require 'mongoose'
async = require 'async'
Achievement = require './Achievement'
EarnedAchievement = require './EarnedAchievement'
User = require '../users/User'
Achievement = require './../models/Achievement'
EarnedAchievement = require './../models/EarnedAchievement'
User = require '../models/User'
Handler = require '../commons/Handler'
LocalMongo = require '../../app/lib/LocalMongo'
util = require '../../app/core/utils'
LevelSession = require '../levels/sessions/LevelSession'
UserPollsRecord = require '../polls/UserPollsRecord'
LevelSession = require '../models/LevelSession'
UserPollsRecord = require '../models/UserPollsRecord'
class EarnedAchievementHandler extends Handler
modelClass: EarnedAchievement

View file

@ -1,9 +1,9 @@
log = require 'winston'
mongoose = require 'mongoose'
utils = require '../lib/utils'
AnalyticsLogEvent = require './AnalyticsLogEvent'
Campaign = require '../campaigns/Campaign'
Level = require '../levels/Level'
AnalyticsLogEvent = require './../models/AnalyticsLogEvent'
Campaign = require '../models/Campaign'
Level = require '../models/Level'
Handler = require '../commons/Handler'
class AnalyticsLogEventHandler extends Handler

View file

@ -1,7 +1,7 @@
AnalyticsPerDay = require './AnalyticsPerDay'
AnalyticsString = require './AnalyticsString'
Campaign = require '../campaigns/Campaign'
Level = require '../levels/Level'
AnalyticsPerDay = require './../models/AnalyticsPerDay'
AnalyticsString = require './../models/AnalyticsString'
Campaign = require '../models/Campaign'
Level = require '../models/Level'
Handler = require '../commons/Handler'
log = require 'winston'

View file

@ -1,4 +1,4 @@
AnalyticsString = require './AnalyticsString'
AnalyticsString = require './../models/AnalyticsString'
Handler = require '../commons/Handler'
class AnalyticsStringHandler extends Handler

View file

@ -1,5 +1,5 @@
Handler = require '../commons/Handler'
AnalyticsStripeInvoice = require './AnalyticsStripeInvoice'
AnalyticsStripeInvoice = require './../models/AnalyticsStripeInvoice'
class AnalyticsStripeInvoiceHandler extends Handler
modelClass: AnalyticsStripeInvoice

View file

@ -1,4 +1,4 @@
AnalyticsUsersActive = require './AnalyticsUsersActive'
AnalyticsUsersActive = require './../models/AnalyticsUsersActive'
Handler = require '../commons/Handler'
class AnalyticsUsersActiveHandler extends Handler

View file

@ -1,43 +0,0 @@
mongoose = require 'mongoose'
plugins = require '../plugins/plugins'
config = require '../../server_config'
ArticleSchema = new mongoose.Schema(body: String, {strict: false,read:config.mongo.readpref})
ArticleSchema.index(
{
index: 1
_fts: 'text'
_ftsx: 1
},
{
name: 'search index'
sparse: true
weights: {body: 1, name: 1}
default_language: 'english'
'language_override': 'searchLanguage'
'textIndexVersion': 2
})
ArticleSchema.index(
{
original: 1
'version.major': -1
'version.minor': -1
},
{
name: 'version index'
unique: true
})
ArticleSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true})
ArticleSchema.plugin(plugins.NamedPlugin)
ArticleSchema.plugin(plugins.VersionedPlugin)
ArticleSchema.plugin(plugins.SearchablePlugin, {searchable: ['body', 'name']})
ArticleSchema.plugin(plugins.TranslationCoveragePlugin)
ArticleSchema.plugin(plugins.PatchablePlugin)
ArticleSchema.statics.postEditableProperties = []
ArticleSchema.statics.editableProperties = ['body', 'name', 'i18n', 'i18nCoverage']
ArticleSchema.statics.jsonSchema = require '../../app/schemas/models/article'
module.exports = mongoose.model('article', ArticleSchema)

View file

@ -1,6 +1,6 @@
# TODO: Remove once mapping.coffee is refactored out
Article = require './Article'
Article = require './../models/Article'
Handler = require '../commons/Handler'
ArticleHandler = class ArticleHandler extends Handler

View file

@ -1,41 +0,0 @@
mongoose = require 'mongoose'
plugins = require '../plugins/plugins'
log = require 'winston'
config = require '../../server_config'
jsonSchema = require '../../app/schemas/models/campaign.schema'
CampaignSchema = new mongoose.Schema(body: String, {strict: false,read:config.mongo.readpref})
CampaignSchema.index({i18nCoverage: 1}, {name: 'translation coverage index', sparse: true})
CampaignSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true})
CampaignSchema.index({type: 1}, {name: 'type index', sparse: true})
CampaignSchema.plugin(plugins.NamedPlugin)
CampaignSchema.plugin(plugins.TranslationCoveragePlugin)
CampaignSchema.plugin plugins.PatchablePlugin
CampaignSchema.statics.updateAdjacentCampaigns = (savedCampaign) ->
Campaign = require '../campaigns/Campaign'
query = {}
query["adjacentCampaigns.#{savedCampaign.get '_id'}"] = {$exists: true}
Campaign.find(query).exec (err, campaigns) ->
return log.error "Couldn't search for adjacent campaigns to update because of #{err}" if err
for campaign in campaigns
acs = campaign.get 'adjacentCampaigns'
ac = acs[savedCampaign.get '_id']
# Let's make sure that we're adding translations, otherwise let's not update yet.
# We could possibly remove this; not sure it's worth having.
[oldI18NCount, newI18NCount] = [0, 0]
oldI18NCount += _.size(translations) for lang, translations of ac.i18n ? {}
newI18NCount += _.size(translations) for lang, translations of savedCampaign.get('i18n') ? {}
continue unless newI18NCount > oldI18NCount
ac.i18n = savedCampaign.get('i18n')
# Save without using middleware so that we don't get into a post-save loop.
Campaign.findByIdAndUpdate campaign._id, {$set: {adjacentCampaigns: acs}}, (err, doc) ->
return log.error "Couldn't save updated adjacent campaign because of #{err}" if err
CampaignSchema.post 'save', -> @constructor.updateAdjacentCampaigns @
CampaignSchema.statics.jsonSchema = jsonSchema
module.exports = mongoose.model('campaign', CampaignSchema)

View file

@ -1,6 +1,6 @@
Campaign = require './Campaign'
Level = require '../levels/Level'
Achievement = require '../achievements/Achievement'
Campaign = require './../models/Campaign'
Level = require '../models/Level'
Achievement = require '../models/Achievement'
Handler = require '../commons/Handler'
async = require 'async'
mongoose = require 'mongoose'

View file

@ -1,13 +1,13 @@
async = require 'async'
mongoose = require 'mongoose'
Handler = require '../commons/Handler'
AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent'
Clan = require './Clan'
EarnedAchievement = require '../achievements/EarnedAchievement'
AnalyticsLogEvent = require '../models/AnalyticsLogEvent'
Clan = require './../models/Clan'
EarnedAchievement = require '../models/EarnedAchievement'
EarnedAchievementHandler = require '../achievements/earned_achievement_handler'
LevelSession = require '../levels/sessions/LevelSession'
LevelSession = require '../models/LevelSession'
LevelSessionHandler = require '../levels/sessions/level_session_handler'
User = require '../users/User'
User = require '../models/User'
UserHandler = require '../users/user_handler'
memberLimit = 200

View file

@ -1,63 +0,0 @@
mongoose = require 'mongoose'
log = require 'winston'
config = require '../../server_config'
plugins = require '../plugins/plugins'
User = require '../users/User'
jsonSchema = require '../../app/schemas/models/classroom.schema'
utils = require '../lib/utils'
ClassroomSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
ClassroomSchema.index({ownerID: 1}, {name: 'ownerID index'})
ClassroomSchema.index({members: 1}, {name: 'members index'})
ClassroomSchema.index({code: 1}, {name: 'code index', unique: true})
ClassroomSchema.statics.privateProperties = []
ClassroomSchema.statics.editableProperties = [
'description'
'name'
'aceConfig'
'averageStudentExp'
'ageRangeMin'
'ageRangeMax'
'archived'
]
ClassroomSchema.statics.generateNewCode = (done) ->
tryCode = ->
# Use 4 code words once we get past 10M classrooms
codeCamel = utils.getCodeCamel(3)
code = codeCamel.toLowerCase()
Classroom.findOne code: code, (err, classroom) ->
return done() if err
return done(code, codeCamel) unless classroom
tryCode()
tryCode()
ClassroomSchema.pre('save', (next) ->
return next() if @get('code')
Classroom.generateNewCode (code, codeCamel) =>
@set 'code', code
@set 'codeCamel', codeCamel
next()
)
ClassroomSchema.methods.isOwner = (userID) ->
return userID.equals(@get('ownerID'))
ClassroomSchema.methods.isMember = (userID) ->
return _.any @get('members') or [], (memberID) -> userID.equals(memberID)
ClassroomSchema.statics.jsonSchema = jsonSchema
ClassroomSchema.set('toObject', {
transform: (doc, ret, options) ->
return ret unless options.req
user = options.req.user
unless user?.isAdmin() or user?.get('_id').equals(doc.get('ownerID'))
delete ret.code
delete ret.codeCamel
return ret
})
module.exports = Classroom = mongoose.model 'classroom', ClassroomSchema, 'classrooms'

View file

@ -1,8 +1,8 @@
async = require 'async'
mongoose = require 'mongoose'
Handler = require '../commons/Handler'
Classroom = require './Classroom'
User = require '../users/User'
Classroom = require './../models/Classroom'
User = require '../models/User'
sendwithus = require '../sendwithus'
utils = require '../lib/utils'
UserHandler = require '../users/user_handler'

View file

@ -3,8 +3,8 @@ mongoose = require 'mongoose'
Grid = require 'gridfs-stream'
errors = require './errors'
log = require 'winston'
Patch = require '../patches/Patch'
User = require '../users/User'
Patch = require '../models/Patch'
User = require '../models/User'
sendwithus = require '../sendwithus'
slack = require '../slack'
deltasLib = require '../../app/core/deltas'

View file

@ -19,7 +19,7 @@ module.exports =
# Hack around Mongoose not exporting Aggregate so that we can patch its exec, too
# https://github.com/LearnBoost/mongoose/issues/1910
Level = require '../levels/Level'
Level = require '../models/Level'
Aggregate = Level.aggregate().constructor
maxAge = (Math.random() * 10 + 10) * 60 * 1000 # Randomize so that each server doesn't refresh cache from db at same times
mongooseCache.install(mongoose, {max: 1000, maxAge: maxAge, debug: false}, Aggregate)

View file

@ -1,32 +0,0 @@
mongoose = require 'mongoose'
config = require '../../server_config'
plugins = require '../plugins/plugins'
jsonSchema = require '../../app/schemas/models/course_instance.schema'
CourseInstanceSchema = new mongoose.Schema {
ownerID: mongoose.Schema.Types.ObjectId
courseID: mongoose.Schema.Types.ObjectId
classroomID: mongoose.Schema.Types.ObjectId
prepaidID: mongoose.Schema.Types.ObjectId
members: [mongoose.Schema.Types.ObjectId]
}, {strict: false, minimize: false, read:config.mongo.readpref}
CourseInstanceSchema.index({ownerID: 1}, {name: 'ownerID index'})
CourseInstanceSchema.index({members: 1}, {name: 'members index'})
CourseInstanceSchema.index({classroomID: 1}, {name: 'classroomID index', sparse: true})
CourseInstanceSchema.index({prepaidID: 1}, {name: 'prepaidID index', sparse: true}) # Deprecated? Can we get rid of this?
CourseInstanceSchema.statics.privateProperties = []
CourseInstanceSchema.statics.editableProperties = [
'description'
'name'
'aceConfig'
]
CourseInstanceSchema.statics.postEditableProperties = [
'courseID'
'classroomID'
]
CourseInstanceSchema.statics.jsonSchema = jsonSchema
module.exports = CourseInstance = mongoose.model 'course.instance', CourseInstanceSchema, 'course.instances'

View file

@ -1,14 +1,14 @@
async = require 'async'
Handler = require '../commons/Handler'
Campaign = require '../campaigns/Campaign'
Classroom = require '../classrooms/Classroom'
Campaign = require '../models/Campaign'
Classroom = require '../models/Classroom'
Course = require '../models/Course'
CourseInstance = require './CourseInstance'
LevelSession = require '../levels/sessions/LevelSession'
CourseInstance = require './../models/CourseInstance'
LevelSession = require '../models/LevelSession'
LevelSessionHandler = require '../levels/sessions/level_session_handler'
Prepaid = require '../prepaids/Prepaid'
Prepaid = require '../models/Prepaid'
PrepaidHandler = require '../prepaids/prepaid_handler'
User = require '../users/User'
User = require '../models/User'
UserHandler = require '../users/user_handler'
utils = require '../../app/core/utils'
{objectIdFromTimestamp} = require '../lib/utils'

View file

@ -1,4 +1,4 @@
LevelComponent = require './LevelComponent'
LevelComponent = require './../../models/LevelComponent'
Handler = require '../../commons/Handler'
mongoose = require 'mongoose'

View file

@ -1,4 +1,4 @@
LevelFeedback = require './LevelFeedback'
LevelFeedback = require './../../models/LevelFeedback'
Handler = require '../../commons/Handler'
class LevelFeedbackHandler extends Handler

View file

@ -1,17 +1,17 @@
Level = require './Level'
Session = require './sessions/LevelSession'
User = require '../users/User'
Level = require './../models/Level'
Session = require './../models/LevelSession'
User = require '../models/User'
SessionHandler = require './sessions/level_session_handler'
Feedback = require './feedbacks/LevelFeedback'
Feedback = require './../models/LevelFeedback'
Handler = require '../commons/Handler'
mongoose = require 'mongoose'
async = require 'async'
utils = require '../lib/utils'
log = require 'winston'
Campaign = require '../campaigns/Campaign'
Campaign = require '../models/Campaign'
Course = require '../models/Course'
CourseInstance = require '../courses/CourseInstance'
Classroom = require '../classrooms/Classroom'
CourseInstance = require '../models/CourseInstance'
Classroom = require '../models/Classroom'
LevelHandler = class LevelHandler extends Handler
modelClass: Level

View file

@ -1,4 +1,4 @@
LevelSession = require './LevelSession'
LevelSession = require './../../models/LevelSession'
Handler = require '../../commons/Handler'
log = require 'winston'

View file

@ -1,4 +1,4 @@
LevelSystem = require './LevelSystem'
LevelSystem = require './../../models/LevelSystem'
Handler = require '../../commons/Handler'
LevelSystemHandler = class LevelSystemHandler extends Handler

View file

@ -1,4 +1,4 @@
ThangType = require './ThangType'
ThangType = require './../../models/ThangType'
Handler = require '../../commons/Handler'
ThangTypeHandler = class ThangTypeHandler extends Handler

View file

@ -1,6 +1,6 @@
config = require '../../server_config'
request = require 'request'
User = require '../users/User'
User = require '../models/User'
http = require 'http'
authstr = new Buffer("#{config.picoCTF_auth.username}:#{config.picoCTF_auth.password}").toString 'base64'

View file

@ -1,5 +1,5 @@
log = require 'winston'
Payment = require '../payments/Payment'
Payment = require '../models/Payment'
PaymentHandler = require '../payments/payment_handler'
module.exports =

View file

@ -1,4 +1,4 @@
AnalyticsString = require '../analytics/AnalyticsString'
AnalyticsString = require '../models/AnalyticsString'
log = require 'winston'
mongoose = require 'mongoose'
config = require '../../server_config'

View file

@ -1,4 +1,4 @@
MailSent = require './MailSent'
MailSent = require './../../models/MailSent'
Handler = require '../../commons/Handler'
class MailSentHandler extends Handler

View file

@ -5,7 +5,7 @@ wrap = require 'co-express'
Promise = require 'bluebird'
parse = require '../commons/parse'
request = require 'request'
User = require '../users/User'
User = require '../models/User'
utils = require '../lib/utils'
mongoose = require 'mongoose'

View file

@ -4,9 +4,9 @@ wrap = require 'co-express'
Promise = require 'bluebird'
database = require '../commons/database'
mongoose = require 'mongoose'
Campaign = require '../campaigns/Campaign'
Campaign = require '../models/Campaign'
parse = require '../commons/parse'
LevelSession = require '../levels/sessions/LevelSession'
LevelSession = require '../models/LevelSession'
module.exports =
fetchByType: wrap (req, res, next) ->

View file

@ -5,10 +5,10 @@ wrap = require 'co-express'
Promise = require 'bluebird'
database = require '../commons/database'
mongoose = require 'mongoose'
Classroom = require '../classrooms/Classroom'
Classroom = require '../models/Classroom'
parse = require '../commons/parse'
LevelSession = require '../levels/sessions/LevelSession'
User = require '../users/User'
LevelSession = require '../models/LevelSession'
User = require '../models/User'
module.exports =
getByOwner: wrap (req, res, next) ->

View file

@ -3,7 +3,7 @@ wrap = require 'co-express'
Promise = require 'bluebird'
parse = require '../commons/parse'
request = require 'request'
User = require '../users/User'
User = require '../models/User'
module.exports =

View file

@ -1,6 +1,6 @@
utils = require '../lib/utils'
errors = require '../commons/errors'
User = require '../users/User'
User = require '../models/User'
sendwithus = require '../sendwithus'
slack = require '../slack'
_ = require 'lodash'

View file

@ -55,7 +55,7 @@ AchievementSchema.statics.achievementCollections = {}
# TODO might want to tweak this to only load new achievements
AchievementSchema.statics.loadAchievements = (done) ->
AchievementSchema.statics.resetAchievements()
Achievement = require('../achievements/Achievement')
Achievement = require('./Achievement')
query = Achievement.find({collection: {$ne: 'level.sessions'}})
query.exec (err, docs) ->
_.each docs, (achievement) ->

View file

@ -1,3 +1,43 @@
# TODO: Migrate Article to here
mongoose = require 'mongoose'
plugins = require '../plugins/plugins'
config = require '../../server_config'
module.exports = require '../articles/Article'
ArticleSchema = new mongoose.Schema(body: String, {strict: false,read:config.mongo.readpref})
ArticleSchema.index(
{
index: 1
_fts: 'text'
_ftsx: 1
},
{
name: 'search index'
sparse: true
weights: {body: 1, name: 1}
default_language: 'english'
'language_override': 'searchLanguage'
'textIndexVersion': 2
})
ArticleSchema.index(
{
original: 1
'version.major': -1
'version.minor': -1
},
{
name: 'version index'
unique: true
})
ArticleSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true})
ArticleSchema.plugin(plugins.NamedPlugin)
ArticleSchema.plugin(plugins.VersionedPlugin)
ArticleSchema.plugin(plugins.SearchablePlugin, {searchable: ['body', 'name']})
ArticleSchema.plugin(plugins.TranslationCoveragePlugin)
ArticleSchema.plugin(plugins.PatchablePlugin)
ArticleSchema.statics.postEditableProperties = []
ArticleSchema.statics.editableProperties = ['body', 'name', 'i18n', 'i18nCoverage']
ArticleSchema.statics.jsonSchema = require '../../app/schemas/models/article'
module.exports = mongoose.model('article', ArticleSchema)

View file

@ -1,3 +1,41 @@
# TODO: Migrate Campaign to here
mongoose = require 'mongoose'
plugins = require '../plugins/plugins'
log = require 'winston'
config = require '../../server_config'
jsonSchema = require '../../app/schemas/models/campaign.schema.coffee'
module.exports = require '../campaigns/Campaign'
CampaignSchema = new mongoose.Schema(body: String, {strict: false,read:config.mongo.readpref})
CampaignSchema.index({i18nCoverage: 1}, {name: 'translation coverage index', sparse: true})
CampaignSchema.index({slug: 1}, {name: 'slug index', sparse: true, unique: true})
CampaignSchema.index({type: 1}, {name: 'type index', sparse: true})
CampaignSchema.plugin(plugins.NamedPlugin)
CampaignSchema.plugin(plugins.TranslationCoveragePlugin)
CampaignSchema.plugin plugins.PatchablePlugin
CampaignSchema.statics.updateAdjacentCampaigns = (savedCampaign) ->
Campaign = require './Campaign'
query = {}
query["adjacentCampaigns.#{savedCampaign.get '_id'}"] = {$exists: true}
Campaign.find(query).exec (err, campaigns) ->
return log.error "Couldn't search for adjacent campaigns to update because of #{err}" if err
for campaign in campaigns
acs = campaign.get 'adjacentCampaigns'
ac = acs[savedCampaign.get '_id']
# Let's make sure that we're adding translations, otherwise let's not update yet.
# We could possibly remove this; not sure it's worth having.
[oldI18NCount, newI18NCount] = [0, 0]
oldI18NCount += _.size(translations) for lang, translations of ac.i18n ? {}
newI18NCount += _.size(translations) for lang, translations of savedCampaign.get('i18n') ? {}
continue unless newI18NCount > oldI18NCount
ac.i18n = savedCampaign.get('i18n')
# Save without using middleware so that we don't get into a post-save loop.
Campaign.findByIdAndUpdate campaign._id, {$set: {adjacentCampaigns: acs}}, (err, doc) ->
return log.error "Couldn't save updated adjacent campaign because of #{err}" if err
CampaignSchema.post 'save', -> @constructor.updateAdjacentCampaigns @
CampaignSchema.statics.jsonSchema = jsonSchema
module.exports = mongoose.model('campaign', CampaignSchema)

View file

@ -2,8 +2,8 @@ mongoose = require 'mongoose'
log = require 'winston'
config = require '../../server_config'
plugins = require '../plugins/plugins'
User = require '../users/User'
jsonSchema = require '../../app/schemas/models/clan.schema'
User = require './User'
jsonSchema = require '../../app/schemas/models/clan.schema.coffee'
ClanSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}

View file

@ -1,3 +1,63 @@
# TODO: Migrate Classroom to here
mongoose = require 'mongoose'
log = require 'winston'
config = require '../../server_config'
plugins = require '../plugins/plugins'
User = require './User'
jsonSchema = require '../../app/schemas/models/classroom.schema.coffee'
utils = require '../lib/utils'
module.exports = require '../classrooms/Classroom'
ClassroomSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
ClassroomSchema.index({ownerID: 1}, {name: 'ownerID index'})
ClassroomSchema.index({members: 1}, {name: 'members index'})
ClassroomSchema.index({code: 1}, {name: 'code index', unique: true})
ClassroomSchema.statics.privateProperties = []
ClassroomSchema.statics.editableProperties = [
'description'
'name'
'aceConfig'
'averageStudentExp'
'ageRangeMin'
'ageRangeMax'
'archived'
]
ClassroomSchema.statics.generateNewCode = (done) ->
tryCode = ->
# Use 4 code words once we get past 10M classrooms
codeCamel = utils.getCodeCamel(3)
code = codeCamel.toLowerCase()
Classroom.findOne code: code, (err, classroom) ->
return done() if err
return done(code, codeCamel) unless classroom
tryCode()
tryCode()
ClassroomSchema.pre('save', (next) ->
return next() if @get('code')
Classroom.generateNewCode (code, codeCamel) =>
@set 'code', code
@set 'codeCamel', codeCamel
next()
)
ClassroomSchema.methods.isOwner = (userID) ->
return userID.equals(@get('ownerID'))
ClassroomSchema.methods.isMember = (userID) ->
return _.any @get('members') or [], (memberID) -> userID.equals(memberID)
ClassroomSchema.statics.jsonSchema = jsonSchema
ClassroomSchema.set('toObject', {
transform: (doc, ret, options) ->
return ret unless options.req
user = options.req.user
unless user?.isAdmin() or user?.get('_id').equals(doc.get('ownerID'))
delete ret.code
delete ret.codeCamel
return ret
})
module.exports = Classroom = mongoose.model 'classroom', ClassroomSchema, 'classrooms'

View file

@ -1,3 +1,32 @@
# TODO: Migrate CourseInstance to here
mongoose = require 'mongoose'
config = require '../../server_config'
plugins = require '../plugins/plugins'
jsonSchema = require '../../app/schemas/models/course_instance.schema.coffee'
module.exports = require '../courses/CourseInstance'
CourseInstanceSchema = new mongoose.Schema {
ownerID: mongoose.Schema.Types.ObjectId
courseID: mongoose.Schema.Types.ObjectId
classroomID: mongoose.Schema.Types.ObjectId
prepaidID: mongoose.Schema.Types.ObjectId
members: [mongoose.Schema.Types.ObjectId]
}, {strict: false, minimize: false, read:config.mongo.readpref}
CourseInstanceSchema.index({ownerID: 1}, {name: 'ownerID index'})
CourseInstanceSchema.index({members: 1}, {name: 'members index'})
CourseInstanceSchema.index({classroomID: 1}, {name: 'classroomID index', sparse: true})
CourseInstanceSchema.index({prepaidID: 1}, {name: 'prepaidID index', sparse: true}) # Deprecated? Can we get rid of this?
CourseInstanceSchema.statics.privateProperties = []
CourseInstanceSchema.statics.editableProperties = [
'description'
'name'
'aceConfig'
]
CourseInstanceSchema.statics.postEditableProperties = [
'courseID'
'classroomID'
]
CourseInstanceSchema.statics.jsonSchema = jsonSchema
module.exports = CourseInstance = mongoose.model 'course.instance', CourseInstanceSchema, 'course.instances'

View file

@ -17,7 +17,7 @@ EarnedAchievementSchema.index({user: 1, achievement: 1}, {unique: true, name: 'e
EarnedAchievementSchema.index({user: 1, changed: -1}, {name: 'latest '})
EarnedAchievementSchema.statics.createForAchievement = (achievement, doc, originalDocObj=null, previouslyEarnedAchievement=null, done) ->
User = require '../users/User'
User = require './User'
userObjectID = doc.get(achievement.get('userField'))
userID = if _.isObject userObjectID then userObjectID.toHexString() else userObjectID # Standardize! Use strings, not ObjectId's

View file

@ -1,7 +1,7 @@
mongoose = require 'mongoose'
plugins = require '../../plugins/plugins'
jsonschema = require '../../../app/schemas/models/level_component'
config = require '../../../server_config'
plugins = require '../plugins/plugins'
jsonschema = require '../../app/schemas/models/level_component'
config = require '../../server_config'
LevelComponentSchema = new mongoose.Schema {
description: String

View file

@ -1,9 +1,9 @@
# TODO: not updated since rename from level_instance, or since we redid how all models are done; probably busted
mongoose = require 'mongoose'
plugins = require '../../plugins/plugins'
jsonschema = require '../../../app/schemas/models/level_feedback'
config = require '../../../server_config'
plugins = require '../plugins/plugins'
jsonschema = require '../../app/schemas/models/level_feedback'
config = require '../../server_config'
LevelFeedbackSchema = new mongoose.Schema({
created:

View file

@ -1,9 +1,9 @@
mongoose = require 'mongoose'
plugins = require '../../plugins/plugins'
AchievablePlugin = require '../../plugins/achievements'
jsonschema = require '../../../app/schemas/models/level_session'
plugins = require '../plugins/plugins'
AchievablePlugin = require '../plugins/achievements'
jsonschema = require '../../app/schemas/models/level_session'
log = require 'winston'
config = require '../../../server_config'
config = require '../../server_config'
LevelSessionSchema = new mongoose.Schema({
created:
@ -43,8 +43,8 @@ LevelSessionSchema.post 'init', (doc) ->
playtime: doc.get 'playtime'
LevelSessionSchema.pre 'save', (next) ->
User = require '../../users/User' # Avoid mutual inclusion cycles
Level = require '../Level'
User = require './User' # Avoid mutual inclusion cycles
Level = require './Level'
@set('changed', new Date())
id = @get('id')

View file

@ -1,7 +1,7 @@
mongoose = require 'mongoose'
plugins = require '../../plugins/plugins'
jsonschema = require '../../../app/schemas/models/level_system'
config = require '../../../server_config'
plugins = require '../plugins/plugins'
jsonschema = require '../../app/schemas/models/level_system'
config = require '../../server_config'
LevelSystemSchema = new mongoose.Schema {
description: String

View file

@ -1,5 +1,5 @@
mongoose = require 'mongoose'
plugins = require '../../plugins/plugins'
plugins = require '../plugins/plugins'
LevelComponentSchema = new mongoose.Schema(
original: {type: mongoose.Schema.ObjectId, ref: 'level.session'}

View file

@ -1,6 +1,6 @@
mongoose = require 'mongoose'
plugins = require '../../plugins/plugins'
jsonschema = require '../../../app/schemas/models/mail_sent'
plugins = require '../plugins/plugins'
jsonschema = require '../../app/schemas/models/mail_sent'
MailSent = new mongoose.Schema({
sent:

View file

@ -1,3 +1,79 @@
# TODO: Migrate Patch to here
mongoose = require('mongoose')
deltas = require '../../app/core/deltas'
log = require 'winston'
{handlers} = require '../commons/mapping'
config = require '../../server_config'
module.exports = require '../patches/Patch'
PatchSchema = new mongoose.Schema({status: String}, {strict: false,read:config.mongo.readpref})
PatchSchema.pre 'save', (next) ->
return next() unless @isNew # patch can't be altered after creation, so only need to check data once
target = @get('target')
targetID = target.id
Handler = require '../commons/Handler'
if not Handler.isID(targetID)
err = new Error('Invalid input.')
err.response = {message: 'isn\'t a MongoDB id.', property: 'target.id'}
err.code = 422
return next(err)
collection = target.collection
handler = require('../' + handlers[collection])
handler.getDocumentForIdOrSlug targetID, (err, document) =>
if err
err = new Error('Server error.')
err.response = {message: '', property: 'target.id'}
err.code = 500
return next(err)
if not document
err = new Error('Target of patch not found.')
err.response = {message: 'was not found.', property: 'target.id'}
err.code = 404
return next(err)
target.id = document.get('_id')
if handler.modelClass.schema.uses_coco_versions
target.original = document.get('original')
version = document.get('version')
target.version = _.pick document.get('version'), 'major', 'minor'
@set('target', target)
else
target.original = targetID
patches = document.get('patches') or []
patches = _.clone patches
patches.push @_id
document.set 'patches', patches, {strict: false}
@targetLoaded = document
document.save (err) -> next(err)
PatchSchema.methods.isTranslationPatch = -> # Don't ever fat arrow bind this one
expanded = deltas.flattenDelta @get('delta')
_.some expanded, (delta) -> 'i18n' in delta.dataPath
PatchSchema.methods.isMiscPatch = ->
expanded = deltas.flattenDelta @get('delta')
_.some expanded, (delta) -> 'i18n' not in delta.dataPath
# Keep track of when a patch is pending and newly approved.
PatchSchema.path('status').set (newVal) ->
@set 'wasPending', @status is 'pending' and newVal isnt 'pending'
@set 'newlyAccepted', newVal is 'accepted' and not @get('newlyAccepted') # Only true on the first accept
newVal
PatchSchema.methods.isNewlyAccepted = -> @get('newlyAccepted')
PatchSchema.methods.wasPending = -> @get 'wasPending'
PatchSchema.pre 'save', (next) ->
User = require './User'
userID = @get('creator').toHexString()
if @get('status') is 'accepted'
User.incrementStat userID, 'stats.patchesContributed' # accepted patches
else if @get('status') is 'pending'
User.incrementStat userID, 'stats.patchesSubmitted' # submitted patches
next()
module.exports = mongoose.model('patch', PatchSchema)

View file

@ -1,6 +1,6 @@
mongoose = require 'mongoose'
plugins = require '../plugins/plugins'
jsonSchema = require '../../app/schemas/models/poll.schema'
jsonSchema = require '../../app/schemas/models/poll.schema.coffee'
log = require 'winston'
config = require '../../server_config'
PollSchema = new mongoose.Schema {

View file

@ -1,6 +1,6 @@
mongoose = require 'mongoose'
plugins = require '../../plugins/plugins'
config = require '../../../server_config'
plugins = require '../plugins/plugins'
config = require '../../server_config'
ThangTypeSchema = new mongoose.Schema({
body: String,

View file

@ -3,10 +3,10 @@ log = require 'winston'
mongoose = require 'mongoose'
config = require '../../server_config'
sendwithus = require '../sendwithus'
Prepaid = require '../prepaids/Prepaid'
Prepaid = require './Prepaid'
jsonSchema = require '../../app/schemas/models/trial_request.schema'
Classroom = require '../classrooms/Classroom'
User = require '../users/User'
Classroom = require './Classroom'
User = require './User'
TrialRequestSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}

View file

@ -1,3 +1,382 @@
# TODO: Migrate User to here
mongoose = require 'mongoose'
jsonschema = require '../../app/schemas/models/user'
crypto = require 'crypto'
{salt, isProduction} = require '../../server_config'
mail = require '../commons/mail'
log = require 'winston'
plugins = require '../plugins/plugins'
AnalyticsUsersActive = require './AnalyticsUsersActive'
module.exports = require '../users/User'
config = require '../../server_config'
stripe = require('stripe')(config.stripe.secretKey)
sendwithus = require '../sendwithus'
delighted = require '../delighted'
UserSchema = new mongoose.Schema({
dateCreated:
type: Date
'default': Date.now
}, {strict: false})
UserSchema.index({'dateCreated': 1})
UserSchema.index({'emailLower': 1}, {unique: true, sparse: true, name: 'emailLower_1'})
UserSchema.index({'facebookID': 1}, {sparse: true})
UserSchema.index({'gplusID': 1}, {sparse: true})
UserSchema.index({'iosIdentifierForVendor': 1}, {name: 'iOS identifier for vendor', sparse: true, unique: true})
UserSchema.index({'mailChimp.leid': 1}, {sparse: true})
UserSchema.index({'nameLower': 1}, {sparse: true, name: 'nameLower_1'})
UserSchema.index({'simulatedBy': 1})
UserSchema.index({'slug': 1}, {name: 'slug index', sparse: true, unique: true})
UserSchema.index({'stripe.subscriptionID': 1}, {unique: true, sparse: true})
UserSchema.index({'siteref': 1}, {name: 'siteref index', sparse: true})
UserSchema.index({'schoolName': 1}, {name: 'schoolName index', sparse: true})
UserSchema.index({'country': 1}, {name: 'country index', sparse: true})
UserSchema.index({'role': 1}, {name: 'role index', sparse: true})
UserSchema.post('init', ->
@set('anonymous', false) if @get('email')
)
UserSchema.methods.isInGodMode = ->
p = @get('permissions')
return p and 'godmode' in p
UserSchema.methods.isAdmin = ->
p = @get('permissions')
return p and 'admin' in p
UserSchema.methods.hasPermission = (neededPermissions) ->
permissions = @get('permissions') or []
if _.contains(permissions, 'admin')
return true
if _.isString(neededPermissions)
neededPermissions = [neededPermissions]
return _.size(_.intersection(permissions, neededPermissions))
UserSchema.methods.isArtisan = ->
p = @get('permissions')
return p and 'artisan' in p
UserSchema.methods.isAnonymous = ->
@get 'anonymous'
UserSchema.statics.teacherRoles = ['teacher', 'technology coordinator', 'advisor', 'principal', 'superintendent', 'parent']
UserSchema.methods.isTeacher = ->
return @get('role') in User.teacherRoles
UserSchema.methods.getUserInfo = ->
id: @get('_id')
email: if @get('anonymous') then 'Unregistered User' else @get('email')
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
emailNameMap =
generalNews: 'announcement'
adventurerNews: 'tester'
artisanNews: 'level_creator'
archmageNews: 'developer'
scribeNews: 'article_editor'
diplomatNews: 'translator'
ambassadorNews: 'support'
anyNotes: 'notification'
teacherNews: 'teacher'
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)
newSubs = _.clone(@get('emails') or _.cloneDeep(jsonschema.properties.emails.default))
newSubs[newName] ?= {}
newSubs[newName].enabled = enabled
@set('emails', newSubs)
@newsSubsChanged = true if newName in mail.NEWS_GROUPS
UserSchema.methods.gems = ->
gemsEarned = @get('earned')?.gems ? 0
gemsEarned = gemsEarned + 100000 if @isInGodMode()
gemsPurchased = @get('purchased')?.gems ? 0
gemsSpent = @get('spent') ? 0
gemsEarned + gemsPurchased - gemsSpent
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 ?= {}
_.defaults emails, _.cloneDeep(jsonschema.properties.emails.default)
return emails[newName]?.enabled
UserSchema.statics.updateServiceSettings = (doc, callback) ->
return callback?() unless isProduction or GLOBAL.testing
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')
if emailChanged and customerID = doc.get('stripe')?.customerID
unless stripe?.customers
console.error('Oh my god, Stripe is not imported correctly-how could we have done this (again)?')
stripe?.customers?.update customerID, {email:doc.get('email')}, (err, customer) ->
console.error('Error updating stripe customer...', err) if err
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)
if (not existingProps) and newGroups.length is 0
return callback?() # don't add totally unsubscribed people to the list
params = {}
params.id = mail.MAILCHIMP_LIST_ID
params.email = if existingProps then {leid: existingProps.leid} else {email: doc.get('email')}
params.merge_vars = {
groupings: [{id: mail.MAILCHIMP_GROUP_ID, groups: newGroups}]
'new-email': doc.get('email')
}
params.update_existing = true
onSuccess = (data) ->
data.email = doc.get('email') # Make sure that we don't spam opt-in emails even if MailChimp doesn't update the email it gets in this object until they have confirmed.
doc.set('mailChimp', data)
doc.updatedMailChimp = true
doc.save()
callback?()
onFailure = (error) ->
log.error 'failed to subscribe', error, callback?
doc.updatedMailChimp = true
callback?()
mc?.lists.subscribe params, onSuccess, onFailure
UserSchema.statics.statsMapping =
edits:
article: 'stats.articleEdits'
level: 'stats.levelEdits'
'level.component': 'stats.levelComponentEdits'
'level.system': 'stats.levelSystemEdits'
'thang.type': 'stats.thangTypeEdits'
'Achievement': 'stats.achievementEdits'
'campaign': 'stats.campaignEdits'
'poll': 'stats.pollEdits'
translations:
article: 'stats.articleTranslationPatches'
level: 'stats.levelTranslationPatches'
'level.component': 'stats.levelComponentTranslationPatches'
'level.system': 'stats.levelSystemTranslationPatches'
'thang.type': 'stats.thangTypeTranslationPatches'
'Achievement': 'stats.achievementTranslationPatches'
'campaign': 'stats.campaignTranslationPatches'
'poll': 'stats.pollTranslationPatches'
misc:
article: 'stats.articleMiscPatches'
level: 'stats.levelMiscPatches'
'level.component': 'stats.levelComponentMiscPatches'
'level.system': 'stats.levelSystemMiscPatches'
'thang.type': 'stats.thangTypeMiscPatches'
'Achievement': 'stats.achievementMiscPatches'
'campaign': 'stats.campaignMiscPatches'
'poll': 'stats.pollMiscPatches'
UserSchema.statics.incrementStat = (id, statName, done, inc=1) ->
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() if err?
user.incrementStat statName, done, inc
UserSchema.methods.incrementStat = (statName, done, inc=1) ->
if /^concepts\./.test statName
# Concept stats are nested a level deeper.
concepts = @get('concepts') or {}
concept = statName.split('.')[1]
concepts[concept] = (concepts[concept] or 0) + inc
@set 'concepts', concepts
else
@set statName, (@get(statName) or 0) + inc
@save (err) -> done?(err)
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)
if (name = @get 'name')? and name isnt ''
unconflictName name, (err, uniqueName) =>
return done err if err
@set 'name', uniqueName
done()
else done()
if @isEmailSubscriptionEnabled 'generalNews'
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
delighted.addDelightedUser @
@saveActiveUser 'register'
UserSchema.methods.hasSubscription = ->
return false unless stripeObject = @get('stripe')
return true if stripeObject.sponsorID
return true if stripeObject.subscriptionID
return true if stripeObject.free is true
return true if _.isString(stripeObject.free) and new Date() < new Date(stripeObject.free)
UserSchema.methods.isPremium = ->
return true if @isInGodMode()
return true if @isAdmin()
return true if @hasSubscription()
return false
UserSchema.methods.formatEntity = (req, publicOnly=false) ->
obj = @toObject()
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP']
delete obj[prop] for prop in serverProperties
candidateProperties = ['jobProfile', 'jobProfileApproved', 'jobProfileNotes']
delete obj[prop] for prop in candidateProperties
includePrivates = not publicOnly and (req.user and (req.user.isAdmin() or req.user._id.equals(@_id)))
delete obj[prop] for prop in User.privateProperties unless includePrivates
return obj
UserSchema.methods.isOnPremiumServer = ->
@get('country') in ['china', 'brazil']
UserSchema.methods.level = ->
xp = @get('points') or 0
a = 5
b = c = 100
if xp > 0 then Math.floor(a * Math.log((1 / b) * (xp + c))) + 1 else 1
UserSchema.statics.saveActiveUser = (id, event, done=null) ->
# TODO: Disabling this until we know why our app servers CPU grows out of control.
return done?()
id = mongoose.Types.ObjectId id if _.isString id
@findById id, (err, user) ->
if err?
log.error err
else
user?.saveActiveUser event
done?()
UserSchema.methods.saveActiveUser = (event, done=null) ->
# TODO: Disabling this until we know why our app servers CPU grows out of control.
return done?()
try
return done?() if @isAdmin()
userID = @get('_id')
# Create if no active user entry for today
today = new Date()
minDate = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()))
AnalyticsUsersActive.findOne({created: {$gte: minDate}, creator: mongoose.Types.ObjectId(userID)}).exec (err, activeUser) ->
if err?
log.error "saveActiveUser error retrieving active users: #{err}"
else if not activeUser
newActiveUser = new AnalyticsUsersActive()
newActiveUser.set 'creator', userID
newActiveUser.set 'event', event
newActiveUser.save (err) ->
log.error "Level session saveActiveUser error saving active user: #{err}" if err?
done?()
catch err
log.error err
done?()
UserSchema.pre('save', (next) ->
Classroom = require './Classroom'
if @isTeacher() and not @wasTeacher
Classroom.update({members: @_id}, {$pull: {members: @_id}}, {multi: true}).exec (err, res) ->
console.log 'removed self from all classrooms as a member', err, res
if email = @get('email')
@set('emailLower', email.toLowerCase())
if name = @get('name')
@set('nameLower', name.toLowerCase())
pwd = @get('password')
if @get('password')
@set('passwordHash', User.hashPassword(pwd))
@set('password', undefined)
if @get('email') and @get('anonymous') # a user registers
@register next
else
next()
)
UserSchema.post 'save', (doc) ->
doc.newsSubsChanged = not _.isEqual(_.pick(doc.get('emails'), mail.NEWS_GROUPS), _.pick(doc.startingEmails, mail.NEWS_GROUPS))
UserSchema.statics.updateServiceSettings(doc)
UserSchema.post 'init', (doc) ->
doc.wasTeacher = doc.isTeacher()
doc.startingEmails = _.cloneDeep(doc.get('emails'))
UserSchema.statics.hashPassword = (password) ->
password = password.toLowerCase()
shasum = crypto.createHash('sha512')
shasum.update(salt + password)
shasum.digest('hex')
UserSchema.statics.privateProperties = [
'permissions', 'email', 'mailChimp', 'firstName', 'lastName', 'gender', 'facebookID',
'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement',
'emailSubscriptions', 'emails', 'activity', 'stripe', 'stripeCustomerID', 'chinaVersion', 'country',
'schoolName', 'ageRange', 'role'
]
UserSchema.statics.jsonSchema = jsonschema
UserSchema.statics.editableProperties = [
'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
'firstName', 'lastName', 'gender', 'ageRange', 'facebookID', 'gplusID', 'emails',
'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage',
'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile', 'savedEmployerFilterAlerts',
'heroConfig', 'iosIdentifierForVendor', 'siteref', 'referrer', 'schoolName', 'role'
]
UserSchema.statics.serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP']
UserSchema.statics.candidateProperties = [ 'jobProfile', 'jobProfileApproved', 'jobProfileNotes']
UserSchema.set('toObject', {
transform: (doc, ret, options) ->
req = options.req
return ret unless req # TODO: Make deleting properties the default, but the consequences are far reaching
publicOnly = options.publicOnly
delete ret[prop] for prop in User.serverProperties
includePrivates = not publicOnly and (req.user and (req.user.isAdmin() or req.user._id.equals(doc._id) or req.session.amActually is doc.id))
if options.includedPrivates
excludedPrivates = _.reject User.privateProperties, (prop) ->
prop in options.includedPrivates
else
excludedPrivates = User.privateProperties
delete ret[prop] for prop in excludedPrivates unless includePrivates
delete ret[prop] for prop in User.candidateProperties
return ret
})
UserSchema.plugin plugins.NamedPlugin
module.exports = User = mongoose.model('User', UserSchema)
AchievablePlugin = require '../plugins/achievements'
UserSchema.plugin(AchievablePlugin)

View file

@ -1,9 +1,9 @@
mongoose = require 'mongoose'
plugins = require '../plugins/plugins'
jsonSchema = require '../../app/schemas/models/user-polls-record.schema'
jsonSchema = require '../../app/schemas/models/user-polls-record.schema.coffee'
log = require 'winston'
Poll = require './Poll'
User = require '../users/User'
User = require './User'
UserPollsRecordSchema = new mongoose.Schema {}, {strict: false, minimize: false}

View file

@ -1,79 +0,0 @@
mongoose = require('mongoose')
deltas = require '../../app/core/deltas'
log = require 'winston'
{handlers} = require '../commons/mapping'
config = require '../../server_config'
PatchSchema = new mongoose.Schema({status: String}, {strict: false,read:config.mongo.readpref})
PatchSchema.pre 'save', (next) ->
return next() unless @isNew # patch can't be altered after creation, so only need to check data once
target = @get('target')
targetID = target.id
Handler = require '../commons/Handler'
if not Handler.isID(targetID)
err = new Error('Invalid input.')
err.response = {message: 'isn\'t a MongoDB id.', property: 'target.id'}
err.code = 422
return next(err)
collection = target.collection
handler = require('../' + handlers[collection])
handler.getDocumentForIdOrSlug targetID, (err, document) =>
if err
err = new Error('Server error.')
err.response = {message: '', property: 'target.id'}
err.code = 500
return next(err)
if not document
err = new Error('Target of patch not found.')
err.response = {message: 'was not found.', property: 'target.id'}
err.code = 404
return next(err)
target.id = document.get('_id')
if handler.modelClass.schema.uses_coco_versions
target.original = document.get('original')
version = document.get('version')
target.version = _.pick document.get('version'), 'major', 'minor'
@set('target', target)
else
target.original = targetID
patches = document.get('patches') or []
patches = _.clone patches
patches.push @_id
document.set 'patches', patches, {strict: false}
@targetLoaded = document
document.save (err) -> next(err)
PatchSchema.methods.isTranslationPatch = -> # Don't ever fat arrow bind this one
expanded = deltas.flattenDelta @get('delta')
_.some expanded, (delta) -> 'i18n' in delta.dataPath
PatchSchema.methods.isMiscPatch = ->
expanded = deltas.flattenDelta @get('delta')
_.some expanded, (delta) -> 'i18n' not in delta.dataPath
# Keep track of when a patch is pending and newly approved.
PatchSchema.path('status').set (newVal) ->
@set 'wasPending', @status is 'pending' and newVal isnt 'pending'
@set 'newlyAccepted', newVal is 'accepted' and not @get('newlyAccepted') # Only true on the first accept
newVal
PatchSchema.methods.isNewlyAccepted = -> @get('newlyAccepted')
PatchSchema.methods.wasPending = -> @get 'wasPending'
PatchSchema.pre 'save', (next) ->
User = require '../users/User'
userID = @get('creator').toHexString()
if @get('status') is 'accepted'
User.incrementStat userID, 'stats.patchesContributed' # accepted patches
else if @get('status') is 'pending'
User.incrementStat userID, 'stats.patchesSubmitted' # submitted patches
next()
module.exports = mongoose.model('patch', PatchSchema)

View file

@ -1,5 +1,5 @@
Patch = require './Patch'
User = require '../users/User'
Patch = require './../models/Patch'
User = require '../models/User'
Handler = require '../commons/Handler'
schema = require '../../app/schemas/models/patch'
{handlers} = require '../commons/mapping'

View file

@ -1,7 +1,7 @@
Payment = require './Payment'
Prepaid = require '../prepaids/Prepaid'
Payment = require './../models/Payment'
Prepaid = require '../models/Prepaid'
Product = require '../models/Product'
User = require '../users/User'
User = require '../models/User'
Handler = require '../commons/Handler'
{handlers} = require '../commons/mapping'
mongoose = require 'mongoose'

View file

@ -9,8 +9,8 @@ config = require '../../server_config'
Handler = require '../commons/Handler'
slack = require '../slack'
discountHandler = require './discount_handler'
Prepaid = require '../prepaids/Prepaid'
User = require '../users/User'
Prepaid = require '../models/Prepaid'
User = require '../models/User'
{findStripeSubscription} = require '../lib/utils'
{getSponsoredSubsAmount} = require '../../app/core/utils'
StripeUtils = require '../lib/stripe_utils'

View file

@ -1,5 +1,5 @@
mongoose = require 'mongoose'
EarnedAchievement = require '../achievements/EarnedAchievement'
EarnedAchievement = require '../models/EarnedAchievement'
LocalMongo = require '../../app/lib/LocalMongo'
util = require '../../app/core/utils'
log = require 'winston'
@ -9,8 +9,8 @@ log = require 'winston'
# TODO if this is still a common scenario I could implement a database hit after all, but only
# on the condition that it's necessary and still not too frequent in occurrence
AchievablePlugin = (schema, options) ->
User = require '../users/User' # Avoid mutual inclusion cycles
Achievement = require '../achievements/Achievement'
User = require '../models/User' # Avoid mutual inclusion cycles
Achievement = require '../models/Achievement'
# Keep track the document before it's saved
schema.post 'init', (doc) ->

View file

@ -267,7 +267,7 @@ module.exports.VersionedPlugin = (schema) ->
# Assume every save is a new version, hence an edit
schema.pre 'save', (next) ->
User = require '../users/User' # Avoid mutual inclusion cycles
User = require '../models/User' # Avoid mutual inclusion cycles
userID = @get('creator')?.toHexString()
return next() unless userID?

View file

@ -1,5 +1,5 @@
Poll = require './Poll'
UserPollsRecord = require './UserPollsRecord'
Poll = require './../models/Poll'
UserPollsRecord = require './../models/UserPollsRecord'
Handler = require '../commons/Handler'
async = require 'async'
mongoose = require 'mongoose'

View file

@ -1,4 +1,4 @@
UserPollsRecord = require './UserPollsRecord'
UserPollsRecord = require './../models/UserPollsRecord'
Handler = require '../commons/Handler'
async = require 'async'
mongoose = require 'mongoose'

View file

@ -1,8 +1,8 @@
Course = require '../models/Course'
Handler = require '../commons/Handler'
slack = require '../slack'
Prepaid = require './Prepaid'
User = require '../users/User'
Prepaid = require './../models/Prepaid'
User = require '../models/User'
StripeUtils = require '../lib/stripe_utils'
utils = require '../../app/core/utils'
mongoose = require 'mongoose'

View file

@ -1,5 +1,5 @@
Purchase = require './Purchase'
User = require '../users/User'
Purchase = require './../models/Purchase'
User = require '../models/User'
Handler = require '../commons/Handler'
{handlers} = require '../commons/mapping'
mongoose = require 'mongoose'

View file

@ -6,10 +6,10 @@ errors = require '../commons/errors'
aws = require 'aws-sdk'
db = require './../routes/db'
queues = require '../commons/queue'
LevelSession = require '../levels/sessions/LevelSession'
Level = require '../levels/Level'
User = require '../users/User'
TaskLog = require './scoring/ScoringTask'
LevelSession = require '../models/LevelSession'
Level = require '../models/Level'
User = require '../models/User'
TaskLog = require './../models/ScoringTask'
scoringUtils = require './scoring/scoringUtils'
getTwoGames = require './scoring/getTwoGames'
recordTwoGames = require './scoring/recordTwoGames'

View file

@ -2,8 +2,8 @@ log = require 'winston'
async = require 'async'
errors = require '../../commons/errors'
scoringUtils = require './scoringUtils'
LevelSession = require '../../levels/sessions/LevelSession'
Level = require '../../levels/Level'
LevelSession = require '../../models/LevelSession'
Level = require '../../models/Level'
module.exports = createNewTask = (req, res) ->
requestSessionID = req.body.session

View file

@ -2,8 +2,8 @@ log = require 'winston'
async = require 'async'
errors = require '../../commons/errors'
scoringUtils = require './scoringUtils'
LevelSession = require '../../levels/sessions/LevelSession'
TaskLog = require './ScoringTask'
LevelSession = require '../../models/LevelSession'
TaskLog = require './../../models/ScoringTask'
module.exports = dispatchTaskToConsumer = (req, res) ->
yetiGuru = {}

View file

@ -2,7 +2,7 @@ log = require 'winston'
async = require 'async'
errors = require '../../commons/errors'
scoringUtils = require './scoringUtils'
LevelSession = require '../../levels/sessions/LevelSession'
LevelSession = require '../../models/LevelSession'
Mandate = require '../../models/Mandate'
module.exports = getTwoGames = (req, res) ->

View file

@ -2,8 +2,8 @@ log = require 'winston'
async = require 'async'
errors = require '../../commons/errors'
scoringUtils = require './scoringUtils'
LevelSession = require '../../levels/sessions/LevelSession'
TaskLog = require './ScoringTask'
LevelSession = require '../../models/LevelSession'
TaskLog = require './../../models/ScoringTask'
module.exports = processTaskResult = (req, res) ->
return if scoringUtils.simulatorIsTooOld req, res

View file

@ -2,7 +2,7 @@ log = require 'winston'
async = require 'async'
errors = require '../../commons/errors'
scoringUtils = require './scoringUtils'
LevelSession = require '../../levels/sessions/LevelSession'
LevelSession = require '../../models/LevelSession'
module.exports = recordTwoGames = (req, res) ->
sessions = req.body.sessions

View file

@ -1,8 +1,8 @@
log = require 'winston'
async = require 'async'
bayes = new (require 'bayesian-battle')()
LevelSession = require '../../levels/sessions/LevelSession'
User = require '../../users/User'
LevelSession = require '../../models/LevelSession'
User = require '../../models/User'
perfmon = require '../../commons/perfmon'
SIMULATOR_VERSION = 3

View file

@ -1,8 +1,8 @@
authentication = require 'passport'
LocalStrategy = require('passport-local').Strategy
User = require '../users/User'
User = require '../models/User'
UserHandler = require '../users/user_handler'
LevelSession = require '../levels/sessions/LevelSession'
LevelSession = require '../models/LevelSession'
config = require '../../server_config'
errors = require '../commons/errors'
languages = require '../routes/languages'

View file

@ -1,9 +1,9 @@
config = require '../../server_config'
log = require 'winston'
User = require '../users/User'
User = require '../models/User'
sendwithus = require '../sendwithus'
async = require 'async'
LevelSession = require '../levels/sessions/LevelSession'
LevelSession = require '../models/LevelSession'
moment = require 'moment'
module.exports.setup = (app) ->

View file

@ -3,7 +3,7 @@ errors = require '../commons/errors'
mongoose = require 'mongoose'
config = require('../../server_config')
request = require 'request'
User = require '../users/User'
User = require '../models/User'
module.exports.setup = (app) ->
app.get '/github/auth_callback', (req, res) ->

View file

@ -49,7 +49,7 @@ module.exports.setup = (app) ->
app.get '/healthcheck', (req, res) ->
try
async = require 'async'
User = require '../users/User'
User = require '../models/User'
async.waterfall [
(callback) ->
User.find({}).limit(1).exec(callback)

View file

@ -1,12 +1,12 @@
mail = require '../commons/mail'
MailSent = require '../mail/sent/MailSent'
MailSent = require '../models/MailSent'
UserRemark = require '../users/remarks/UserRemark'
User = require '../users/User'
User = require '../models/User'
async = require 'async'
errors = require '../commons/errors'
config = require '../../server_config'
LevelSession = require '../levels/sessions/LevelSession'
Level = require '../levels/Level'
LevelSession = require '../models/LevelSession'
Level = require '../models/Level'
log = require 'winston'
sendwithus = require '../sendwithus'
if config.isProduction and config.redis.host isnt 'localhost'

View file

@ -1,8 +1,8 @@
async = require 'async'
config = require '../../server_config'
stripe = require('stripe')(config.stripe.secretKey)
User = require '../users/User'
Payment = require '../payments/Payment'
User = require '../models/User'
Payment = require '../models/Payment'
errors = require '../commons/errors'
mongoose = require 'mongoose'
utils = require '../../app/core/utils'

View file

@ -1,4 +1,4 @@
UserCodeProblem = require './UserCodeProblem'
UserCodeProblem = require './../models/UserCodeProblem'
Handler = require '../commons/Handler'
utils = require '../lib/utils'

View file

@ -1,382 +0,0 @@
mongoose = require 'mongoose'
jsonschema = require '../../app/schemas/models/user'
crypto = require 'crypto'
{salt, isProduction} = require '../../server_config'
mail = require '../commons/mail'
log = require 'winston'
plugins = require '../plugins/plugins'
AnalyticsUsersActive = require '../analytics/AnalyticsUsersActive'
config = require '../../server_config'
stripe = require('stripe')(config.stripe.secretKey)
sendwithus = require '../sendwithus'
delighted = require '../delighted'
UserSchema = new mongoose.Schema({
dateCreated:
type: Date
'default': Date.now
}, {strict: false})
UserSchema.index({'dateCreated': 1})
UserSchema.index({'emailLower': 1}, {unique: true, sparse: true, name: 'emailLower_1'})
UserSchema.index({'facebookID': 1}, {sparse: true})
UserSchema.index({'gplusID': 1}, {sparse: true})
UserSchema.index({'iosIdentifierForVendor': 1}, {name: 'iOS identifier for vendor', sparse: true, unique: true})
UserSchema.index({'mailChimp.leid': 1}, {sparse: true})
UserSchema.index({'nameLower': 1}, {sparse: true, name: 'nameLower_1'})
UserSchema.index({'simulatedBy': 1})
UserSchema.index({'slug': 1}, {name: 'slug index', sparse: true, unique: true})
UserSchema.index({'stripe.subscriptionID': 1}, {unique: true, sparse: true})
UserSchema.index({'siteref': 1}, {name: 'siteref index', sparse: true})
UserSchema.index({'schoolName': 1}, {name: 'schoolName index', sparse: true})
UserSchema.index({'country': 1}, {name: 'country index', sparse: true})
UserSchema.index({'role': 1}, {name: 'role index', sparse: true})
UserSchema.post('init', ->
@set('anonymous', false) if @get('email')
)
UserSchema.methods.isInGodMode = ->
p = @get('permissions')
return p and 'godmode' in p
UserSchema.methods.isAdmin = ->
p = @get('permissions')
return p and 'admin' in p
UserSchema.methods.hasPermission = (neededPermissions) ->
permissions = @get('permissions') or []
if _.contains(permissions, 'admin')
return true
if _.isString(neededPermissions)
neededPermissions = [neededPermissions]
return _.size(_.intersection(permissions, neededPermissions))
UserSchema.methods.isArtisan = ->
p = @get('permissions')
return p and 'artisan' in p
UserSchema.methods.isAnonymous = ->
@get 'anonymous'
UserSchema.statics.teacherRoles = ['teacher', 'technology coordinator', 'advisor', 'principal', 'superintendent', 'parent']
UserSchema.methods.isTeacher = ->
return @get('role') in User.teacherRoles
UserSchema.methods.getUserInfo = ->
id: @get('_id')
email: if @get('anonymous') then 'Unregistered User' else @get('email')
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
emailNameMap =
generalNews: 'announcement'
adventurerNews: 'tester'
artisanNews: 'level_creator'
archmageNews: 'developer'
scribeNews: 'article_editor'
diplomatNews: 'translator'
ambassadorNews: 'support'
anyNotes: 'notification'
teacherNews: 'teacher'
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)
newSubs = _.clone(@get('emails') or _.cloneDeep(jsonschema.properties.emails.default))
newSubs[newName] ?= {}
newSubs[newName].enabled = enabled
@set('emails', newSubs)
@newsSubsChanged = true if newName in mail.NEWS_GROUPS
UserSchema.methods.gems = ->
gemsEarned = @get('earned')?.gems ? 0
gemsEarned = gemsEarned + 100000 if @isInGodMode()
gemsPurchased = @get('purchased')?.gems ? 0
gemsSpent = @get('spent') ? 0
gemsEarned + gemsPurchased - gemsSpent
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 ?= {}
_.defaults emails, _.cloneDeep(jsonschema.properties.emails.default)
return emails[newName]?.enabled
UserSchema.statics.updateServiceSettings = (doc, callback) ->
return callback?() unless isProduction or GLOBAL.testing
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')
if emailChanged and customerID = doc.get('stripe')?.customerID
unless stripe?.customers
console.error('Oh my god, Stripe is not imported correctly-how could we have done this (again)?')
stripe?.customers?.update customerID, {email:doc.get('email')}, (err, customer) ->
console.error('Error updating stripe customer...', err) if err
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)
if (not existingProps) and newGroups.length is 0
return callback?() # don't add totally unsubscribed people to the list
params = {}
params.id = mail.MAILCHIMP_LIST_ID
params.email = if existingProps then {leid: existingProps.leid} else {email: doc.get('email')}
params.merge_vars = {
groupings: [{id: mail.MAILCHIMP_GROUP_ID, groups: newGroups}]
'new-email': doc.get('email')
}
params.update_existing = true
onSuccess = (data) ->
data.email = doc.get('email') # Make sure that we don't spam opt-in emails even if MailChimp doesn't update the email it gets in this object until they have confirmed.
doc.set('mailChimp', data)
doc.updatedMailChimp = true
doc.save()
callback?()
onFailure = (error) ->
log.error 'failed to subscribe', error, callback?
doc.updatedMailChimp = true
callback?()
mc?.lists.subscribe params, onSuccess, onFailure
UserSchema.statics.statsMapping =
edits:
article: 'stats.articleEdits'
level: 'stats.levelEdits'
'level.component': 'stats.levelComponentEdits'
'level.system': 'stats.levelSystemEdits'
'thang.type': 'stats.thangTypeEdits'
'Achievement': 'stats.achievementEdits'
'campaign': 'stats.campaignEdits'
'poll': 'stats.pollEdits'
translations:
article: 'stats.articleTranslationPatches'
level: 'stats.levelTranslationPatches'
'level.component': 'stats.levelComponentTranslationPatches'
'level.system': 'stats.levelSystemTranslationPatches'
'thang.type': 'stats.thangTypeTranslationPatches'
'Achievement': 'stats.achievementTranslationPatches'
'campaign': 'stats.campaignTranslationPatches'
'poll': 'stats.pollTranslationPatches'
misc:
article: 'stats.articleMiscPatches'
level: 'stats.levelMiscPatches'
'level.component': 'stats.levelComponentMiscPatches'
'level.system': 'stats.levelSystemMiscPatches'
'thang.type': 'stats.thangTypeMiscPatches'
'Achievement': 'stats.achievementMiscPatches'
'campaign': 'stats.campaignMiscPatches'
'poll': 'stats.pollMiscPatches'
UserSchema.statics.incrementStat = (id, statName, done, inc=1) ->
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() if err?
user.incrementStat statName, done, inc
UserSchema.methods.incrementStat = (statName, done, inc=1) ->
if /^concepts\./.test statName
# Concept stats are nested a level deeper.
concepts = @get('concepts') or {}
concept = statName.split('.')[1]
concepts[concept] = (concepts[concept] or 0) + inc
@set 'concepts', concepts
else
@set statName, (@get(statName) or 0) + inc
@save (err) -> done?(err)
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)
if (name = @get 'name')? and name isnt ''
unconflictName name, (err, uniqueName) =>
return done err if err
@set 'name', uniqueName
done()
else done()
if @isEmailSubscriptionEnabled 'generalNews'
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
delighted.addDelightedUser @
@saveActiveUser 'register'
UserSchema.methods.hasSubscription = ->
return false unless stripeObject = @get('stripe')
return true if stripeObject.sponsorID
return true if stripeObject.subscriptionID
return true if stripeObject.free is true
return true if _.isString(stripeObject.free) and new Date() < new Date(stripeObject.free)
UserSchema.methods.isPremium = ->
return true if @isInGodMode()
return true if @isAdmin()
return true if @hasSubscription()
return false
UserSchema.methods.formatEntity = (req, publicOnly=false) ->
obj = @toObject()
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP']
delete obj[prop] for prop in serverProperties
candidateProperties = ['jobProfile', 'jobProfileApproved', 'jobProfileNotes']
delete obj[prop] for prop in candidateProperties
includePrivates = not publicOnly and (req.user and (req.user.isAdmin() or req.user._id.equals(@_id)))
delete obj[prop] for prop in User.privateProperties unless includePrivates
return obj
UserSchema.methods.isOnPremiumServer = ->
@get('country') in ['china', 'brazil']
UserSchema.methods.level = ->
xp = @get('points') or 0
a = 5
b = c = 100
if xp > 0 then Math.floor(a * Math.log((1 / b) * (xp + c))) + 1 else 1
UserSchema.statics.saveActiveUser = (id, event, done=null) ->
# TODO: Disabling this until we know why our app servers CPU grows out of control.
return done?()
id = mongoose.Types.ObjectId id if _.isString id
@findById id, (err, user) ->
if err?
log.error err
else
user?.saveActiveUser event
done?()
UserSchema.methods.saveActiveUser = (event, done=null) ->
# TODO: Disabling this until we know why our app servers CPU grows out of control.
return done?()
try
return done?() if @isAdmin()
userID = @get('_id')
# Create if no active user entry for today
today = new Date()
minDate = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()))
AnalyticsUsersActive.findOne({created: {$gte: minDate}, creator: mongoose.Types.ObjectId(userID)}).exec (err, activeUser) ->
if err?
log.error "saveActiveUser error retrieving active users: #{err}"
else if not activeUser
newActiveUser = new AnalyticsUsersActive()
newActiveUser.set 'creator', userID
newActiveUser.set 'event', event
newActiveUser.save (err) ->
log.error "Level session saveActiveUser error saving active user: #{err}" if err?
done?()
catch err
log.error err
done?()
UserSchema.pre('save', (next) ->
Classroom = require '../classrooms/Classroom'
if @isTeacher() and not @wasTeacher
Classroom.update({members: @_id}, {$pull: {members: @_id}}, {multi: true}).exec (err, res) ->
console.log 'removed self from all classrooms as a member', err, res
if email = @get('email')
@set('emailLower', email.toLowerCase())
if name = @get('name')
@set('nameLower', name.toLowerCase())
pwd = @get('password')
if @get('password')
@set('passwordHash', User.hashPassword(pwd))
@set('password', undefined)
if @get('email') and @get('anonymous') # a user registers
@register next
else
next()
)
UserSchema.post 'save', (doc) ->
doc.newsSubsChanged = not _.isEqual(_.pick(doc.get('emails'), mail.NEWS_GROUPS), _.pick(doc.startingEmails, mail.NEWS_GROUPS))
UserSchema.statics.updateServiceSettings(doc)
UserSchema.post 'init', (doc) ->
doc.wasTeacher = doc.isTeacher()
doc.startingEmails = _.cloneDeep(doc.get('emails'))
UserSchema.statics.hashPassword = (password) ->
password = password.toLowerCase()
shasum = crypto.createHash('sha512')
shasum.update(salt + password)
shasum.digest('hex')
UserSchema.statics.privateProperties = [
'permissions', 'email', 'mailChimp', 'firstName', 'lastName', 'gender', 'facebookID',
'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement',
'emailSubscriptions', 'emails', 'activity', 'stripe', 'stripeCustomerID', 'chinaVersion', 'country',
'schoolName', 'ageRange', 'role'
]
UserSchema.statics.jsonSchema = jsonschema
UserSchema.statics.editableProperties = [
'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
'firstName', 'lastName', 'gender', 'ageRange', 'facebookID', 'gplusID', 'emails',
'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage',
'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile', 'savedEmployerFilterAlerts',
'heroConfig', 'iosIdentifierForVendor', 'siteref', 'referrer', 'schoolName', 'role'
]
UserSchema.statics.serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP']
UserSchema.statics.candidateProperties = [ 'jobProfile', 'jobProfileApproved', 'jobProfileNotes']
UserSchema.set('toObject', {
transform: (doc, ret, options) ->
req = options.req
return ret unless req # TODO: Make deleting properties the default, but the consequences are far reaching
publicOnly = options.publicOnly
delete ret[prop] for prop in User.serverProperties
includePrivates = not publicOnly and (req.user and (req.user.isAdmin() or req.user._id.equals(doc._id) or req.session.amActually is doc.id))
if options.includedPrivates
excludedPrivates = _.reject User.privateProperties, (prop) ->
prop in options.includedPrivates
else
excludedPrivates = User.privateProperties
delete ret[prop] for prop in excludedPrivates unless includePrivates
delete ret[prop] for prop in User.candidateProperties
return ret
})
UserSchema.plugin plugins.NamedPlugin
module.exports = User = mongoose.model('User', UserSchema)
AchievablePlugin = require '../plugins/achievements'
UserSchema.plugin(AchievablePlugin)

View file

@ -1,7 +1,7 @@
schema = require '../../app/schemas/models/user'
crypto = require 'crypto'
request = require 'request'
User = require './User'
User = require './../models/User'
Handler = require '../commons/Handler'
mongoose = require 'mongoose'
config = require '../../server_config'
@ -9,23 +9,23 @@ errors = require '../commons/errors'
async = require 'async'
log = require 'winston'
moment = require 'moment'
AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent'
Clan = require '../clans/Clan'
CourseInstance = require '../courses/CourseInstance'
LevelSession = require '../levels/sessions/LevelSession'
AnalyticsLogEvent = require '../models/AnalyticsLogEvent'
Clan = require '../models/Clan'
CourseInstance = require '../models/CourseInstance'
LevelSession = require '../models/LevelSession'
LevelSessionHandler = require '../levels/sessions/level_session_handler'
Payment = require '../payments/Payment'
Payment = require '../models/Payment'
SubscriptionHandler = require '../payments/subscription_handler'
DiscountHandler = require '../payments/discount_handler'
EarnedAchievement = require '../achievements/EarnedAchievement'
EarnedAchievement = require '../models/EarnedAchievement'
UserRemark = require './remarks/UserRemark'
{findStripeSubscription} = require '../lib/utils'
{isID} = require '../lib/utils'
slack = require '../slack'
sendwithus = require '../sendwithus'
Prepaid = require '../prepaids/Prepaid'
UserPollsRecord = require '../polls/UserPollsRecord'
EarnedAchievement = require '../achievements/EarnedAchievement'
Prepaid = require '../models/Prepaid'
UserPollsRecord = require '../models/UserPollsRecord'
EarnedAchievement = require '../models/EarnedAchievement'
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP']
candidateProperties = [
@ -792,7 +792,7 @@ UserHandler = class UserHandler extends Handler
expanded = deltas.flattenDelta obj.get 'delta'
_.some expanded, (delta) -> 'i18n' in delta.dataPath
Patch = require '../patches/Patch'
Patch = require '../models/Patch'
# filter is passed a mongoose document and should return a boolean,
# determining whether the patch should be counted
countPatchesByUsersInMemory = (query, filter, statName, done) ->
@ -836,7 +836,7 @@ UserHandler = class UserHandler extends Handler
updateUser user, count, doneWithUser
countPatchesByUsers = (query, statName, done) ->
Patch = require '../patches/Patch'
Patch = require '../models/Patch'
userStream = User.find({anonymous: false}).sort('_id').stream()
streamFinished = false
@ -870,7 +870,7 @@ UserHandler = class UserHandler extends Handler
statRecalculators:
gamesCompleted: (done) ->
LevelSession = require '../levels/sessions/LevelSession'
LevelSession = require '../models/LevelSession'
userStream = User.find({anonymous: false}).sort('_id').stream()
streamFinished = false
@ -899,23 +899,23 @@ UserHandler = class UserHandler extends Handler
User.findByIdAndUpdate user.get('_id'), update, doneWithUser
articleEdits: (done) ->
Article = require '../articles/Article'
Article = require '../models/Article'
countEdits Article, done
levelEdits: (done) ->
Level = require '../levels/Level'
Level = require '../models/Level'
countEdits Level, done
levelComponentEdits: (done) ->
LevelComponent = require '../levels/components/LevelComponent'
LevelComponent = require '../models/LevelComponent'
countEdits LevelComponent, done
levelSystemEdits: (done) ->
LevelSystem = require '../levels/systems/LevelSystem'
LevelSystem = require '../models/LevelSystem'
countEdits LevelSystem, done
thangTypeEdits: (done) ->
ThangType = require '../levels/thangs/ThangType'
ThangType = require '../models/ThangType'
countEdits ThangType, done
patchesContributed: (done) ->

View file

@ -37,6 +37,7 @@ if (database.generateMongoConnectionString() !== dbString) {
}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 120; // for long Stripe tests
require('../server/common'); // Make sure global testing functions are set up
var initialized = false;
beforeEach(function(done) {
@ -53,7 +54,7 @@ beforeEach(function(done) {
},
function(cb) {
// 5. Check actual database
var User = require('../../server/users/User');
var User = require('../../server/models/User');
User.find({}).count(function(err, count) {
// For this to serve as a line of defense against testing with the
// production DB, tests must be run with
@ -77,6 +78,7 @@ beforeEach(function(done) {
},
function(cb) {
// Initialize products
request = require('../server/request');
request.get(getURL('/db/products'), function(err, res, body) {
expect(err).toBe(null);
expect(res.statusCode).toBe(200);

View file

@ -5,40 +5,13 @@ console.log 'IT BEGINS'
if process.env.COCO_MONGO_HOST
throw Error('Tests may not run with production environment')
GLOBAL._ = require 'lodash'
_.str = require 'underscore.string'
_.mixin(_.str.exports())
GLOBAL.mongoose = require 'mongoose' # TODO: Remove, otherwise it hides when the server is missing a mongoose require
require '../../server' # make lodash globally available
mongoose = require 'mongoose'
path = require 'path'
GLOBAL.testing = true
GLOBAL.tv4 = require 'tv4' # required for TreemaUtils to work
models_path = [
'../../server/analytics/AnalyticsUsersActive'
'../../server/articles/Article'
'../../server/campaigns/Campaign'
'../../server/clans/Clan'
'../../server/classrooms/Classroom'
'../../server/models/Course'
'../../server/courses/CourseInstance'
'../../server/levels/Level'
'../../server/levels/components/LevelComponent'
'../../server/levels/systems/LevelSystem'
'../../server/levels/sessions/LevelSession'
'../../server/levels/thangs/LevelThangType'
'../../server/users/User'
'../../server/patches/Patch'
'../../server/achievements/Achievement'
'../../server/achievements/EarnedAchievement'
'../../server/payments/Payment'
'../../server/prepaids/Prepaid'
'../../server/models/TrialRequest'
]
for m in models_path
model = path.basename(m)
#console.log('model=' + model)
GLOBAL[model] = require m
User = require '../../server/models/User'
async = require 'async'
@ -70,9 +43,6 @@ GLOBAL.saveModels = (models, done) ->
GLOBAL.simplePermissions = [target: 'public', access: 'owner']
GLOBAL.ObjectId = mongoose.Types.ObjectId
GLOBAL.request = require('request').defaults({jar: true})
Promise = require 'bluebird'
Promise.promisifyAll(request, {multiArgs: true})
GLOBAL.unittest = {}
unittest.users = unittest.users or {}

View file

@ -38,6 +38,12 @@ diminishing =
url = getURL('/db/achievement')
Achievement = require '../../../server/models/Achievement'
EarnedAchievement = require '../../../server/models/EarnedAchievement'
LevelSession = require '../../../server/models/LevelSession'
User = require '../../../server/models/User'
request = require '../request'
describe 'Achievement', ->
allowHeader = 'GET, POST, PUT, PATCH, DELETE'

Some files were not shown because too many files have changed in this diff Show more