mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 00:40:56 -05:00
First round of getting the site to use the new defaults system, in particular the job profile view.
This commit is contained in:
parent
fe2faefa5d
commit
1c5db3f2b7
21 changed files with 129 additions and 151 deletions
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) ->
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) ->
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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}]
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
JobProfileView = require 'views/account/JobProfileView'
|
||||
JobProfileView = require 'views/user/JobProfileView'
|
||||
|
||||
responses =
|
||||
'/db/user/joe/nameToID':'512ef4805a67a8c507000001'
|
||||
|
|
Loading…
Reference in a new issue