First round of getting the site to use the new defaults system, in particular the job profile view.

This commit is contained in:
Scott Erickson 2014-08-23 11:07:52 -07:00
parent fe2faefa5d
commit 1c5db3f2b7
21 changed files with 129 additions and 151 deletions

View file

@ -11,6 +11,7 @@ module.exports = class Achievement extends CocoModel
# TODO logic is duplicated in Mongoose Achievement schema
getExpFunction: ->
# TODO DEFAULTS
kind = @get('function')?.kind or jsonschema.properties.function.default.kind
parameters = @get('function')?.parameters or jsonschema.properties.function.default.parameters
return utils.functionCreators[kind](parameters) if kind of utils.functionCreators

View file

@ -9,15 +9,12 @@ class CocoModel extends Backbone.Model
notyErrors: true
@schema: null
getMe: -> @me or @me = require('lib/auth').me
initialize: (attributes, options) ->
super(arguments...)
options ?= {}
@setProjection options.project
if not @constructor.className
console.error("#{@} needs a className set.")
@addSchemaDefaults()
@on 'sync', @onLoaded, @
@on 'error', @onError, @
@on 'add', @onLoaded, @
@ -45,14 +42,31 @@ class CocoModel extends Backbone.Model
@loadFromBackup()
getNormalizedURL: -> "#{@urlRoot}/#{@id}"
attributesWithDefaults: undefined
get: (attribute, withDefault=false) ->
if withDefault
if @attributesWithDefaults is undefined then @buildAttributesWithDefaults()
return @attributesWithDefaults[attribute]
else
super(attribute)
set: ->
delete @attributesWithDefaults
inFlux = @loading or not @loaded
@markToRevert() unless inFlux or @_revertAttributes
res = super(arguments...)
@saveBackup() if @saveBackups and (not inFlux) and @hasLocalChanges()
res
buildAttributesWithDefaults: ->
t0 = new Date()
clone = $.extend true, {}, @attributes
TreemaNode.utils.populateDefaults(clone, @schema())
@attributesWithDefaults = clone
console.debug "Populated defaults for #{@attributes.name or @type()} in #{new Date() - t0}ms"
loadFromBackup: ->
return unless @saveBackups
existing = storage.load @id
@ -161,28 +175,12 @@ class CocoModel extends Backbone.Model
if @isPublished() then throw new Error('Can\'t publish what\'s already-published. Can\'t kill what\'s already dead.')
@set 'permissions', (@get('permissions') or []).concat({access: 'read', target: 'public'})
addSchemaDefaults: ->
return if @addedSchemaDefaults
@addedSchemaDefaults = true
for prop, defaultValue of @constructor.schema.default or {}
continue if @get(prop)?
#console.log 'setting', prop, 'to', defaultValue, 'from attributes.default'
@set prop, defaultValue
for prop, sch of @constructor.schema.properties or {}
continue if @get(prop)?
continue if prop is 'emails' # hack, defaults are handled through User.coffee's email-specific methods.
#console.log 'setting', prop, 'to', sch.default, 'from sch.default' if sch.default?
@set prop, sch.default if sch.default?
if @loaded
@loadFromBackup()
@isObjectID: (s) ->
s.length is 24 and s.match(/[a-f0-9]/gi)?.length is 24
hasReadAccess: (actor) ->
# actor is a User object
actor ?= @getMe()
actor ?= me
return true if actor.isAdmin()
if @get('permissions')?
for permission in @get('permissions')
@ -193,8 +191,7 @@ class CocoModel extends Backbone.Model
hasWriteAccess: (actor) ->
# actor is a User object
actor ?= @getMe()
actor ?= me
return true if actor.isAdmin()
if @get('permissions')?
for permission in @get('permissions')

View file

@ -135,6 +135,7 @@ module.exports = class Level extends CocoModel
visit comp
thang.components = sorted
# TODO DEFAULTS
fillInDefaultComponentConfiguration: (thangs, levelComponents) ->
for thang in thangs
for component in thang.components or []

View file

@ -9,13 +9,6 @@ module.exports = class User extends CocoModel
urlRoot: '/db/user'
notyErrors: false
defaults:
points: 0
initialize: ->
super()
@migrateEmails()
onLoaded: ->
CocoModel.pollAchievements() # Check for achievements on login
super arguments...
@ -24,14 +17,9 @@ module.exports = class User extends CocoModel
permissions = @attributes['permissions'] or []
return 'admin' in permissions
isAnonymous: ->
@get 'anonymous'
displayName: ->
@get('name') or 'Anoner'
lang: ->
@get('preferredLanguage') or 'en-US'
isAnonymous: -> @get('anonymous', true)
displayName: -> @get('name', true)
lang: -> @get('preferredLanguage', true)
getPhotoURL: (size=80, useJobProfilePhoto=false, useEmployerPageAvatar=false) ->
photoURL = if useJobProfilePhoto then @get('jobProfile')?.photoURL else null
@ -57,33 +45,13 @@ module.exports = class User extends CocoModel
done response.name
getEnabledEmails: ->
@migrateEmails()
emails = _.clone(@get('emails')) or {}
emails = _.defaults emails, @schema().properties.emails.default
(emailName for emailName, emailDoc of emails when emailDoc.enabled)
(emailName for emailName, emailDoc of @get('emails', true) when emailDoc.enabled)
setEmailSubscription: (name, enabled) ->
newSubs = _.clone(@get('emails')) or {}
(newSubs[name] ?= {}).enabled = enabled
@set 'emails', newSubs
emailMap:
announcement: 'generalNews'
developer: 'archmageNews'
tester: 'adventurerNews'
level_creator: 'artisanNews'
article_editor: 'scribeNews'
translator: 'diplomatNews'
support: 'ambassadorNews'
notification: 'anyNotes'
migrateEmails: ->
return if @attributes.emails or not @attributes.emailSubscriptions
oldSubs = @get('emailSubscriptions') or []
newSubs = {}
newSubs[newSubName] = {enabled: oldSubName in oldSubs} for oldSubName, newSubName of @emailMap
@set('emails', newSubs)
isEmailSubscriptionEnabled: (name) -> (@get('emails') or {})[name]?.enabled
a = 5

View file

@ -3,6 +3,20 @@ emailSubscriptions = ['announcement', 'tester', 'level_creator', 'developer', 'a
UserSchema = c.object
title: 'User'
default:
visa: 'Authorized to work in the US'
music: true
name: 'Anoner'
autocastDelay: 5000
emails: {}
permissions: []
anonymous: true
points: 0
preferredLanguage: 'en-US'
aceConfig: {}
simulatedBy: 0
simulatedFor: 0
jobProfile: {}
c.extendNamedProperties UserSchema # let's have the name be the first property
@ -31,7 +45,6 @@ visa = c.shortString
title: 'US Work Status'
description: 'Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)'
enum: ['Authorized to work in the US', 'Need visa sponsorship']
default: 'Authorized to work in the US'
_.extend UserSchema.properties,
email: c.shortString({title: 'Email', format: 'email'})
@ -47,12 +60,12 @@ _.extend UserSchema.properties,
wizardColor1: c.pct({title: 'Wizard Clothes Color'})
volume: c.pct({title: 'Volume'})
music: {type: 'boolean', default: true}
autocastDelay: {type: 'integer', 'default': 5000}
lastLevel: {type: 'string'}
music: { type: 'boolean' }
autocastDelay: { type: 'integer' }
lastLevel: { type: 'string' }
emailSubscriptions: c.array {uniqueItems: true}, {'enum': emailSubscriptions}
emails: c.object {title: 'Email Settings', default: {generalNews: {enabled: true}, anyNotes: {enabled: true}, recruitNotes: {enabled: true}}},
emails: c.object {title: 'Email Settings', default: generalNews: {enabled: true}, anyNotes: {enabled: true}, recruitNotes: {enabled: true} },
# newsletters
generalNews: {$ref: '#/definitions/emailSubscription'}
adventurerNews: {$ref: '#/definitions/emailSubscription'}
@ -68,9 +81,9 @@ _.extend UserSchema.properties,
employerNotes: {$ref: '#/definitions/emailSubscription'}
# server controlled
permissions: c.array {'default': []}, c.shortString()
permissions: c.array {}, c.shortString()
dateCreated: c.date({title: 'Date Joined'})
anonymous: {type: 'boolean', 'default': true}
anonymous: {type: 'boolean' }
testGroupNumber: {type: 'integer', minimum: 0, maximum: 256, exclusiveMaximum: true}
mailChimp: {type: 'object'}
hourOfCode: {type: 'boolean'}
@ -84,36 +97,36 @@ _.extend UserSchema.properties,
emailHash: {type: 'string'}
#Internationalization stuff
preferredLanguage: {type: 'string', default: 'en', 'enum': c.getLanguageCodeArray()}
preferredLanguage: {type: 'string', 'enum': c.getLanguageCodeArray()}
signedCLA: c.date({title: 'Date Signed the CLA'})
wizard: c.object {},
colorConfig: c.object {additionalProperties: c.colorConfig()}
aceConfig: c.object {},
language: {type: 'string', 'default': 'javascript', 'enum': ['javascript', 'coffeescript', 'python', 'clojure', 'lua', 'io']}
keyBindings: {type: 'string', 'default': 'default', 'enum': ['default', 'vim', 'emacs']}
invisibles: {type: 'boolean', 'default': false}
indentGuides: {type: 'boolean', 'default': false}
behaviors: {type: 'boolean', 'default': false}
liveCompletion: {type: 'boolean', 'default': true}
aceConfig: c.object { default: { language: 'javascript', keyBindings: 'default', invisibles: false, indentGuides: false, behaviors: false, liveCompletion: true }},
language: {type: 'string', 'enum': ['javascript', 'coffeescript', 'python', 'clojure', 'lua', 'io']}
keyBindings: {type: 'string', 'enum': ['default', 'vim', 'emacs']}
invisibles: {type: 'boolean' }
indentGuides: {type: 'boolean' }
behaviors: {type: 'boolean' }
liveCompletion: {type: 'boolean' }
simulatedBy: {type: 'integer', minimum: 0, default: 0}
simulatedFor: {type: 'integer', minimum: 0, default: 0}
jobProfile: c.object {title: 'Job Profile', required: ['lookingFor', 'jobTitle', 'active', 'name', 'city', 'country', 'skills', 'experience', 'shortDescription', 'longDescription', 'visa', 'work', 'education', 'projects', 'links']},
lookingFor: {title: 'Looking For', type: 'string', enum: ['Full-time', 'Part-time', 'Remote', 'Contracting', 'Internship'], default: 'Full-time', description: 'What kind of developer position do you want?'}
jobTitle: {type: 'string', maxLength: 50, title: 'Desired Job Title', description: 'What role are you looking for? Ex.: "Full Stack Engineer", "Front-End Developer", "iOS Developer"', default: 'Software Developer'}
simulatedBy: {type: 'integer', minimum: 0 }
simulatedFor: {type: 'integer', minimum: 0 }
jobProfile: c.object {title: 'Job Profile', default: { active: false, lookingFor: 'Full-time', jobTitle: 'Software Developer', city: 'Defaultsville, CA', country: 'USA', skills: ['javascript'], shortDescription: 'Programmer seeking to build great software.', longDescription: '* I write great code.\n* You need great code?\n* Great!' }},
lookingFor: {title: 'Looking For', type: 'string', enum: ['Full-time', 'Part-time', 'Remote', 'Contracting', 'Internship'], description: 'What kind of developer position do you want?'}
jobTitle: {type: 'string', maxLength: 50, title: 'Desired Job Title', description: 'What role are you looking for? Ex.: "Full Stack Engineer", "Front-End Developer", "iOS Developer"' }
active: {title: 'Open to Offers', type: 'boolean', description: 'Want interview offers right now?'}
updated: c.date {title: 'Last Updated', description: 'How fresh your profile appears to employers. Profiles go inactive after 4 weeks.'}
name: c.shortString {title: 'Name', description: 'Name you want employers to see, like "Nick Winter".'}
city: c.shortString {title: 'City', description: 'City you want to work in (or live in now), like "San Francisco" or "Lubbock, TX".', default: 'Defaultsville, CA', format: 'city'}
country: c.shortString {title: 'Country', description: 'Country you want to work in (or live in now), like "USA" or "France".', default: 'USA', format: 'country'}
skills: c.array {title: 'Skills', description: 'Tag relevant developer skills in order of proficiency.', default: ['javascript'], minItems: 1, maxItems: 30, uniqueItems: true},
city: c.shortString {title: 'City', description: 'City you want to work in (or live in now), like "San Francisco" or "Lubbock, TX".', format: 'city'}
country: c.shortString {title: 'Country', description: 'Country you want to work in (or live in now), like "USA" or "France".', format: 'country'}
skills: c.array {title: 'Skills', description: 'Tag relevant developer skills in order of proficiency', maxItems: 30, uniqueItems: true},
{type: 'string', minLength: 1, maxLength: 50, description: 'Ex.: "objective-c", "mongodb", "rails", "android", "javascript"', format: 'skill'}
experience: {type: 'integer', title: 'Years of Experience', minimum: 0, description: 'How many years of professional experience (getting paid) developing software do you have?'}
shortDescription: {type: 'string', maxLength: 140, title: 'Short Description', description: 'Who are you, and what are you looking for? 140 characters max.', default: 'Programmer seeking to build great software.'}
longDescription: {type: 'string', maxLength: 600, title: 'Description', description: 'Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max.', format: 'markdown', default: '* I write great code.\n* You need great code?\n* Great!'}
shortDescription: {type: 'string', maxLength: 140, title: 'Short Description', description: 'Who are you, and what are you looking for? 140 characters max.' }
longDescription: {type: 'string', maxLength: 600, title: 'Description', description: 'Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max.', format: 'markdown' }
visa: visa
work: c.array {title: 'Work Experience', description: 'List your relevant work experience, most recent first.'},
c.object {title: 'Job', description: 'Some work experience you had.', required: ['employer', 'role', 'duration']},
@ -129,14 +142,14 @@ _.extend UserSchema.properties,
description: {type: 'string', title: 'Description', description: 'Highlight anything about this educational experience. (140 chars; optional)', maxLength: 140}
projects: c.array {title: 'Projects (Top 3)', description: 'Highlight your projects to amaze employers.', maxItems: 3},
c.object {title: 'Project', description: 'A project you created.', required: ['name', 'description', 'picture'], default: {name: 'My Project', description: 'A project I worked on.', link: 'http://example.com', picture: ''}},
name: c.shortString {title: 'Project Name', description: 'What was the project called?', default: 'My Project'}
description: {type: 'string', title: 'Description', description: 'Briefly describe the project.', maxLength: 400, default: 'A project I worked on.', format: 'markdown'}
name: c.shortString {title: 'Project Name', description: 'What was the project called?' }
description: {type: 'string', title: 'Description', description: 'Briefly describe the project.', maxLength: 400, format: 'markdown'}
picture: {type: 'string', title: 'Picture', format: 'image-file', description: 'Upload a 230x115px or larger image showing off the project.'}
link: c.url {title: 'Link', description: 'Link to the project.', default: 'http://example.com'}
link: c.url {title: 'Link', description: 'Link to the project.'}
links: c.array {title: 'Personal and Social Links', description: 'Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog.'},
c.object {title: 'Link', description: 'A link to another site you want to highlight, like your GitHub, your LinkedIn, or your blog.', required: ['name', 'link']},
c.object {title: 'Link', description: 'A link to another site you want to highlight, like your GitHub, your LinkedIn, or your blog.', required: ['name', 'link'], default: {link: 'http://example.com'}},
name: {type: 'string', maxLength: 30, title: 'Link Name', description: 'What are you linking to? Ex: "Personal Website", "GitHub"', format: 'link-name'}
link: c.url {title: 'Link', description: 'The URL.', default: 'http://example.com'}
link: c.url {title: 'Link', description: 'The URL.' }
photoURL: {type: 'string', format: 'image-file', title: 'Profile Picture', description: 'Upload a 256x256px or larger image if you want to show a different profile picture to employers than your normal avatar.'}
curated: c.object {title: 'Curated', required: ['shortDescription', 'mainTag', 'location', 'education', 'workHistory', 'phoneScreenFilter', 'schoolFilter', 'locationFilter', 'roleFilter', 'seniorityFilter']},
shortDescription:
@ -169,7 +182,7 @@ _.extend UserSchema.properties,
description: 'Should this candidate be prominently featured on the site?'
jobProfileApproved: {title: 'Job Profile Approved', type: 'boolean', description: 'Whether your profile has been approved by CodeCombat.'}
jobProfileApprovedDate: c.date {title: 'Approved date', description: 'The date that the candidate was approved'}
jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: 'CodeCombat\'s notes on the candidate.', format: 'markdown', default: ''}
jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: 'CodeCombat\'s notes on the candidate.', format: 'markdown' }
employerAt: c.shortString {description: 'If given employer permissions to view job candidates, for which employer?'}
signedEmployerAgreement: c.object {},
linkedinID: c.shortString {title: 'LinkedInID', description: 'The user\'s LinkedIn ID when they signed the contract.'}
@ -252,10 +265,11 @@ _.extend UserSchema.properties,
c.extendBasicProperties UserSchema, 'user'
c.definitions =
emailSubscription =
UserSchema.definitions =
emailSubscription: c.object { default: { enabled: true, count: 0 } }, {
enabled: {type: 'boolean'}
lastSent: c.date()
count: {type: 'integer'}
}
module.exports = UserSchema

View file

@ -117,7 +117,7 @@ block content
button.btn.btn-success.btn-block.save-section(data-i18n="common.save") Save
.editable-section#basic-info-container
- var editableDefaults = editing && profile.city == jobProfileSchema.properties.city.default
- var editableDefaults = editing && !rawProfile.city
div(class="editable-display" + (editableDefaults ? " edit-example-text" : ""), title="Click to edit your basic info")
.editable-icon.glyphicon.glyphicon-pencil
if editableDefaults
@ -224,7 +224,7 @@ block content
#short-description-container.editable-section
.editable-display(title="Click to write your tagline")
.editable-icon.glyphicon.glyphicon-pencil
if editing && (!profile.shortDescription || profile.shortDescription == jobProfileSchema.properties.shortDescription.default)
if editing && !rawProfile.shortDescription
h3.edit-label(data-i18n="account_profile.short_description_header") Write a short description of yourself
p.edit-example-text(data-i18n="account_profile.short_description_blurb") Add a tagline to help an employer quickly learn more about you.
@ -269,7 +269,7 @@ block content
#long-description-container.editable-section
.editable-display(title="Click to start writing your longer description")
.editable-icon.glyphicon.glyphicon-pencil
- var modified = profile.longDescription && profile.longDescription != jobProfileSchema.properties.longDescription.default
- var modified = rawProfile.longDescription
if editing && !modified
h3.edit-label(data-i18n="account_profile.long_description_header") Describe your desired position
p.edit-example-text(data-i18n="account_profile.long_description_blurb") Tell employers how awesome you are and what role you want.

View file

@ -3,7 +3,7 @@
.editable-icon.glyphicon.glyphicon-pencil
img.profile-photo(src=me.getPhotoURL(230))
.form-group
input#player-name.profile-caption(name="playerName", type="text", value=me.get('name') || 'Anoner')
input#player-name.profile-caption(name="playerName", type="text", value=me.get('name', true))
.form
h3(data-i18n="options.general_options") General Options

View file

@ -39,7 +39,7 @@
strong.tip.rare(data-i18n='play_level.tip_first_language') The most disastrous thing that you can ever learn is your first programming language. - Alan Kay
strong.tip.rare
span(data-i18n='play_level.tip_harry') Yer a Wizard,
span= me.get('name') || 'Anoner'
span= me.get('name', true)
.errors

View file

@ -61,6 +61,7 @@ module.exports = class AddLevelSystemModal extends ModalView
levelSystem =
original: s.get('original') ? id
majorVersion: s.get('version').major ? 0
# TODO DEFAULTS
config: $.extend(true, {}, s.get('configSchema').default ? {})
@extantSystems.push levelSystem
Backbone.Mediator.publish 'level-system-added', system: levelSystem

View file

@ -72,6 +72,14 @@ module.exports = class JobProfileView extends UserView
finishInit: ->
return unless @userID
@uploadFilePath = "db/user/#{@userID}"
if @user?.get('firstName')
jobProfile = @user.get('jobProfile')
jobProfile ?= {}
if not jobProfile.name
jobProfile.name = (@user.get('firstName') + ' ' + @user.get('lastName')).trim()
@user.set('jobProfile', jobProfile)
@highlightedContainers = []
if me.isAdmin() or 'employer' in me.get('permissions')
$.post "/db/user/#{me.id}/track/view_candidate"
@ -223,16 +231,8 @@ module.exports = class JobProfileView extends UserView
context = super()
context.userID = @userID
context.linkedInAuthorized = @authorizedWithLinkedIn
context.jobProfileSchema = me.schema().properties.jobProfile
if @user and not jobProfile = @user.get 'jobProfile'
jobProfile = {}
for prop, schema of context.jobProfileSchema.properties
jobProfile[prop] = _.clone schema.default if schema.default?
for prop in context.jobProfileSchema.required
jobProfile[prop] ?= {string: '', boolean: false, number: 0, integer: 0, array: []}[context.jobProfileSchema.properties[prop].type]
@user.set 'jobProfile', jobProfile
jobProfile.name ?= (@user.get('firstName') + ' ' + @user.get('lastName')).trim() if @user?.get('firstName')
context.profile = jobProfile
context.profile = @user.get('jobProfile', true)
context.rawProfile = @user.get('jobProfile') or {}
context.user = @user
context.myProfile = @isMe()
context.allowedToViewJobProfile = @user and (me.isAdmin() or 'employer' in me.get('permissions') or (context.myProfile && !me.get('anonymous')))
@ -244,7 +244,7 @@ module.exports = class JobProfileView extends UserView
context.marked = marked
context.moment = moment
context.iconForLink = @iconForLink
if links = jobProfile?.links
if links = context.profile.links
links = ($.extend(true, {}, link) for link in links)
link.icon = @iconForLink link for link in links
context.profileLinks = _.sortBy links, (link) -> not link.icon # icons first
@ -290,8 +290,8 @@ module.exports = class JobProfileView extends UserView
aceUseWrapMode: true
callbacks: {change: @onRemarkChanged}
@remarkTreema = @$el.find('#remark-treema').treema treemaOptions
@remarkTreema.build()
@remarkTreema.open(3)
@remarkTreema?.build()
@remarkTreema?.open(3)
onRemarkChanged: (e) =>
return unless @remarkTreema.isValid()
@ -440,7 +440,8 @@ module.exports = class JobProfileView extends UserView
$(@).remove() if isEmpty @
resetOnce = false # We have to clear out arrays if we're going to redo them
serialized = form.serializeArray()
jobProfile = @user.get 'jobProfile'
jobProfile = @user.get('jobProfile') or {}
jobProfile[prop] ?= [] for prop in ['links', 'skills', 'work', 'education', 'projects']
rootPropertiesSeen = {}
for field in serialized
keyChain = @extractFieldKeyChain field.name
@ -449,6 +450,7 @@ module.exports = class JobProfileView extends UserView
for key, i in keyChain
rootPropertiesSeen[key] = true unless i
break if i is keyChain.length - 1
parent[key] ?= {}
child = parent[key]
if _.isArray(child) and not resetOnce
child = parent[key] = []
@ -467,6 +469,7 @@ module.exports = class JobProfileView extends UserView
if section.hasClass('projects-container') and not section.find('.array-item').length
jobProfile.projects = []
section.addClass 'saving'
@user.set('jobProfile', jobProfile)
@saveEdits true
extractFieldKeyChain: (key) ->

View file

@ -40,7 +40,7 @@
"jsondiffpatch": "~0.1.5",
"nanoscroller": "~0.8.0",
"jquery.tablesorter": "~2.15.13",
"treema": "~0.1.2",
"treema": "https://github.com/codecombat/treema.git#release/0.1.0",
"bootstrap": "~3.1.1",
"validated-backbone-mediator": "~0.1.3",
"jquery.browser": "~0.0.6",

View file

@ -25,6 +25,7 @@ AchievementSchema.methods.stringifyQuery = ->
@set('query', JSON.stringify(@get('query'))) if typeof @get('query') != 'string'
AchievementSchema.methods.getExpFunction = ->
# TODO DEFAULTS
kind = @get('function')?.kind or jsonschema.properties.function.default.kind
parameters = @get('function')?.parameters or jsonschema.properties.function.default.parameters
return utils.functionCreators[kind](parameters) if kind of utils.functionCreators

View file

@ -12,12 +12,6 @@ LevelSchema.plugin(plugins.VersionedPlugin)
LevelSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description']})
LevelSchema.plugin(plugins.PatchablePlugin)
LevelSchema.pre 'init', (next) ->
return next() unless jsonschema.properties?
for prop, sch of jsonschema.properties
@set(prop, _.cloneDeep(sch.default)) if sch.default?
next()
LevelSchema.post 'init', (doc) ->
if _.isString(doc.get('nextLevel'))
doc.set('nextLevel', undefined)

View file

@ -13,10 +13,4 @@ LevelComponentSchema.plugin plugins.VersionedPlugin
LevelComponentSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'description', 'system']}
LevelComponentSchema.plugin plugins.PatchablePlugin
LevelComponentSchema.pre 'init', (next) ->
return next() unless jsonschema.properties?
for prop, sch of jsonschema.properties
@set(prop, _.cloneDeep sch.default) if sch.default?
next()
module.exports = LevelComponent = mongoose.model('level.component', LevelComponentSchema)

View file

@ -14,13 +14,6 @@ LevelSessionSchema = new mongoose.Schema({
LevelSessionSchema.plugin(plugins.PermissionsPlugin)
LevelSessionSchema.plugin(AchievablePlugin)
LevelSessionSchema.pre 'init', (next) ->
# TODO: refactor this into a set of common plugins for all models?
return next() unless jsonschema.properties?
for prop, sch of jsonschema.properties
@set(prop, _.cloneDeep(sch.default)) if sch.default?
next()
previous = {}
LevelSessionSchema.post 'init', (doc) ->

View file

@ -12,10 +12,4 @@ LevelSystemSchema.plugin(plugins.VersionedPlugin)
LevelSystemSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description']})
LevelSystemSchema.plugin(plugins.PatchablePlugin)
LevelSystemSchema.pre 'init', (next) ->
return next() unless jsonschema.properties?
for prop, sch of jsonschema.properties
@set(prop, _.cloneDeep sch.default) if sch.default?
next()
module.exports = LevelSystem = mongoose.model('level.system', LevelSystemSchema)

View file

@ -15,14 +15,6 @@ UserSchema = new mongoose.Schema({
'default': Date.now
}, {strict: false})
UserSchema.pre('init', (next) ->
return next() unless jsonschema.properties?
for prop, sch of jsonschema.properties
continue if prop is 'emails' # defaults may change, so don't carry them over just yet
@set(prop, sch.default) if sch.default?
next()
)
UserSchema.post('init', ->
@set('anonymous', false) if @get('email')
)

View file

@ -161,7 +161,7 @@ UserHandler = class UserHandler extends Handler
post: (req, res) ->
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
return @sendBadInputError(res, 'Must have an anonymous user to post with.') unless req.user
return @sendBadInputError(res, 'Existing users cannot create new ones.') unless req.user.get('anonymous')
return @sendBadInputError(res, 'Existing users cannot create new ones.') if req.user.get('anonymous') is false
req.body._id = req.user._id if req.user.get('anonymous')
@put(req, res)

View file

@ -14,3 +14,28 @@ describe 'UserModel', ->
me.set 'points', 50
expect(me.level()).toBe User.levelFromExp 50
describe 'user emails', ->
it 'has anyNotes, generalNews and recruitNotes enabled by default', ->
u = new User()
expect(u.get('emails')).toBeUndefined()
defaultEmails = u.get('emails', true)
expect(defaultEmails.anyNotes.enabled).toBe(true)
expect(defaultEmails.generalNews.enabled).toBe(true)
expect(defaultEmails.recruitNotes.enabled).toBe(true)
it 'maintains defaults of other emails when one is explicitly set', ->
u = new User()
u.setEmailSubscription('recruitNotes', false)
defaultEmails = u.get('emails', true)
expect(defaultEmails.anyNotes?.enabled).toBe(true)
expect(defaultEmails.generalNews?.enabled).toBe(true)
expect(defaultEmails.recruitNotes.enabled).toBe(false)
it 'does not populate raw data for other emails when one is explicitly set', ->
u = new User()
u.setEmailSubscription('recruitNotes', false)
u.buildAttributesWithDefaults()
emails = u.get('emails')
expect(emails.anyNotes).toBeUndefined()
expect(emails.generalNews).toBeUndefined()

View file

@ -6,14 +6,14 @@ responses =
'/db/level.component/B/version/0': {
system: 'System'
original: 'B'
majorVersion: 0
version: {major: 0, minor:0}
name: 'B (depends on A)'
dependencies: [{original:'A', majorVersion: 0}]
}
'/db/level.component/A/version/0': {
system: 'System'
original: 'A'
majorVersion: 0
version: {major: 0, minor:0}
name: 'A'
configSchema: { type: 'object', properties: { propA: { type: 'number' }, propB: { type: 'string' }} }
}
@ -21,7 +21,7 @@ responses =
componentC = new LevelComponent({
system: 'System'
original: 'C'
majorVersion: 0
version: {major: 0, minor:0}
name: 'C (depends on B)'
dependencies: [{original:'B', majorVersion: 0}]
})

View file

@ -1,4 +1,4 @@
JobProfileView = require 'views/account/JobProfileView'
JobProfileView = require 'views/user/JobProfileView'
responses =
'/db/user/joe/nameToID':'512ef4805a67a8c507000001'