Deleted schemas from /server and modified files to point to /app/schemas

This commit is contained in:
Aditya Raisinghani 2014-04-12 14:16:41 +05:30
parent 6fb5b59a01
commit b932bf1e7c
22 changed files with 38 additions and 584 deletions

View file

@ -2,15 +2,6 @@ storage = require 'lib/storage'
deltasLib = require 'lib/deltas'
auth = require 'lib/auth'
class CocoSchema extends Backbone.Model
constructor: (path, args...) ->
super(args...)
# @urlRoot = path + '/schema'
@schemaName = path[4..].replace '.', '_'
@schema = require 'schemas/' + @schemaName + '_schema'
# window.CocoSchema = CocoSchema.schema
class CocoModel extends Backbone.Model
idAttribute: "_id"
loaded: false
@ -69,7 +60,6 @@ class CocoModel extends Backbone.Model
return if @constructor.schema.loading
@constructor.schema = require 'schemas/' + @constructor.schema + '_schema' unless @constructor.schema.loaded
@onConstructorSync()
# @listenToOnce(@constructor.schema, 'sync', @onConstructorSync)
onConstructorSync: ->
@constructor.schema.loaded = true

View file

@ -6,8 +6,9 @@ c.extendNamedProperties ArticleSchema # name first
ArticleSchema.properties.body = { type: 'string', title: 'Content', format: 'markdown' }
ArticleSchema.properties.i18n = { type: 'object', title: 'i18n', format: 'i18n', props: ['name', 'body'] }
c.extendBasicProperties(ArticleSchema, 'article')
c.extendSearchableProperties(ArticleSchema)
c.extendVersionedProperties(ArticleSchema, 'article')
c.extendBasicProperties ArticleSchema, 'article'
c.extendSearchableProperties ArticleSchema
c.extendVersionedProperties ArticleSchema, 'article'
c.extendPatchableProperties ArticleSchema
module.exports = ArticleSchema

View file

@ -1,20 +1,5 @@
# errors = require '../commons/errors'
# log = require 'winston'
locale = require '../locale/locale' # requiring from app; will break if we stop serving from where app lives
# module.exports.setup = (app) ->
# app.all '/languages/add/:lang/:namespace', (req, res) ->
# # Should probably store these somewhere
# log.info "#{req.params.lang}.#{req.params.namespace} missing an i18n key:", req.body
# res.send('')
# res.end()
# app.all '/languages', (req, res) ->
# # Now that these are in the client, not sure when we would use this, but hey
# return errors.badMethod(res) if req.route.method isnt 'get'
# res.send(languages)
# return res.end()
languages = []
for code, localeInfo of locale
languages.push code: code, nativeDescription: localeInfo.nativeDescription, englishDescription: localeInfo.englishDescription

View file

@ -115,5 +115,6 @@ c.extendBasicProperties LevelComponentSchema, 'level.component'
c.extendSearchableProperties LevelComponentSchema
c.extendVersionedProperties LevelComponentSchema, 'level.component'
c.extendPermissionsProperties LevelComponentSchema, 'level.component'
c.extendPatchableProperties LevelComponentSchema
module.exports = LevelComponentSchema

View file

@ -1,5 +1,5 @@
c = require './schemas'
ThangComponentSchema = require './thang_component_schema'
ThangComponentSchema = require './thangs/thang_component_schema'
SpecificArticleSchema = c.object()
c.extendNamedProperties SpecificArticleSchema # name first
@ -108,9 +108,9 @@ NoteGroupSchema = c.object {title: "Note Group", description: "A group of notes
lock: {title: "Lock", description: "Whether the interface should be locked so that the player's focus is on the script, or specific areas to lock.", type: ['boolean', 'array'], items: {type: 'string', enum: ['surface', 'editor', 'palette', 'hud', 'playback', 'playback-hover', 'level', ]}}
letterbox: {type: 'boolean', title: 'Letterbox', description:'Turn letterbox mode on or off. Disables surface and playback controls.'}
goals: c.object {title: "Goals", description: "Add or remove goals for the player to complete in the level."},
add: c.array {title: "Add", description: "Add these goals."}, GoalSchema
remove: c.array {title: "Remove", description: "Remove these goals."}, GoalSchema
goals: c.object {title: "Goals (Old)", description: "Deprecated. Goals added here have no effect. Add goals in the level settings instead."},
add: c.array {title: "Add", description: "Deprecated. Goals added here have no effect. Add goals in the level settings instead."}, GoalSchema
remove: c.array {title: "Remove", description: "Deprecated. Goals removed here have no effect. Adjust goals in the level settings instead."}, GoalSchema
playback: c.object {title: "Playback", description: "Control the playback of the level."},
playing: {type: 'boolean', title: "Set Playing", description: "Set whether playback is playing or paused."}
@ -243,6 +243,7 @@ c.extendBasicProperties LevelSchema, 'level'
c.extendSearchableProperties LevelSchema
c.extendVersionedProperties LevelSchema, 'level'
c.extendPermissionsProperties LevelSchema, 'level'
c.extendPatchableProperties LevelSchema
module.exports = LevelSchema

View file

@ -101,6 +101,7 @@ _.extend LevelSystemSchema.properties,
c.extendBasicProperties LevelSystemSchema, 'level.system'
c.extendSearchableProperties LevelSystemSchema
c.extendVersionedProperties LevelSystemSchema, 'level.system'
c.extendPermissionsProperties LevelSystemSchema, 'level.system'
c.extendPermissionsProperties LevelSystemSchema
c.extendPatchableProperties LevelSystemSchema
module.exports = LevelSystemSchema

View file

@ -15,7 +15,7 @@ me.object = (ext, props) -> combine {type: 'object', additionalProperties: false
me.array = (ext, items) -> combine {type: 'array', items: items or {}}, ext
me.shortString = (ext) -> combine({type: 'string', maxLength: 100}, ext)
me.pct = (ext) -> combine({type: 'number', maximum: 1.0, minimum: 0.0}, ext)
me.date = (ext) -> combine({type: 'string', format: 'date-time'}, ext)
me.date = (ext) -> combine({type: ['object', 'string'], format: 'date-time'}, ext)
# should just be string (Mongo ID), but sometimes mongoose turns them into objects representing those, so we are lenient
me.objectId = (ext) -> schema = combine({type: ['object', 'string'] }, ext)
me.url = (ext) -> combine({type: 'string', format: 'url', pattern: urlPattern}, ext)
@ -54,7 +54,21 @@ basicProps = (linkFragment) ->
me.extendBasicProperties = (schema, linkFragment) ->
schema.properties = {} unless schema.properties?
_.extend(schema.properties, basicProps(linkFragment))
# PATCHABLE
patchableProps = ->
patches: me.array({title:'Patches'}, {
_id: me.objectId(links: [{rel: "db", href: "/db/patch/{($)}"}], title: "Patch ID", description: "A reference to the patch.")
status: { enum: ['pending', 'accepted', 'rejected', 'cancelled']}
})
allowPatches: { type: 'boolean' }
listeners: me.array({title:'Listeners'},
me.objectId(links: [{rel: 'extra', href: "/db/user/{($)}"}]))
me.extendPatchableProperties = (schema) ->
schema.properties = {} unless schema.properties?
_.extend(schema.properties, patchableProps())
# NAMED

View file

@ -146,8 +146,9 @@ ThangTypeSchema.definitions =
action: ActionSchema
sound: SoundSchema
c.extendBasicProperties(ThangTypeSchema, 'thang.type')
c.extendSearchableProperties(ThangTypeSchema)
c.extendVersionedProperties(ThangTypeSchema, 'thang.type')
c.extendBasicProperties ThangTypeSchema, 'thang.type'
c.extendSearchableProperties ThangTypeSchema
c.extendVersionedProperties ThangTypeSchema, 'thang.type'
c.extendPatchableProperties ThangTypeSchema
module.exports = ThangTypeSchema

View file

@ -62,7 +62,6 @@ module.exports = class ThangTypeEditView extends View
@thangType.fetch()
@thangType.loadSchema()
# @listenToOnce(@thangType.schema(), 'sync', @onThangTypeSync)
@listenToOnce(@thangType, 'sync', @onThangTypeSync)
@refreshAnimation = _.debounce @refreshAnimation, 500

View file

@ -1,48 +0,0 @@
#this file will hold the experimental JSON schema for i18n
c = require './schemas'
languageCodeArrayRegex = c.generateLanguageCodeArrayRegex()
ExampleSchema = {
title: "Example Schema",
description:"An example schema",
type: "object",
properties: {
text: {
title: "Text",
description: "A short message to display in the dialogue area. Markdown okay.",
type: "string",
maxLength: 400
},
i18n: {"$ref": "#/definitions/i18n"}
},
definitions: {
i18n: {
title: "i18n",
description: "The internationalization object",
type: "object",
patternProperties: {
languageCodeArrayRegex: {
additionalProperties: false,
properties: {
#put the translatable properties here
#if it is possible to not include i18n with a reference
# to #/properties, you could just do
properties: {"$ref":"#/properties"}
# text: {"$ref": "#/properties/text"}
}
default: {
title: "LanguageCode",
description: "LanguageDescription"
}
}
}
}
},
}
#define a i18n object type for each schema, then have the i18n have it's oneOf check against
#translatable schemas of that object

View file

@ -1,132 +0,0 @@
# The JSON Schema Core/Validation Meta-Schema, but with titles and descriptions added to make it easier to edit in Treema, and in CoffeeScript
module.exports =
id: "metaschema"
displayProperty: "title"
$schema: "http://json-schema.org/draft-04/schema#"
title: "Schema"
description: "Core schema meta-schema"
definitions:
schemaArray:
type: "array"
minItems: 1
items: { $ref: "#" }
title: "Array of Schemas"
"default": [{}]
positiveInteger:
type: "integer"
minimum: 0
title: "Positive Integer"
positiveIntegerDefault0:
allOf: [ { $ref: "#/definitions/positiveInteger" }, { "default": 0 } ]
simpleTypes:
title: "Single Type"
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
stringArray:
type: "array"
items: { type: "string" }
minItems: 1
uniqueItems: true
title: "String Array"
"default": ['']
type: "object"
properties:
id:
type: "string"
format: "uri"
$schema:
type: "string"
format: "uri"
"default": "http://json-schema.org/draft-04/schema#"
title:
type: "string"
description:
type: "string"
"default": {}
multipleOf:
type: "number"
minimum: 0
exclusiveMinimum: true
maximum:
type: "number"
exclusiveMaximum:
type: "boolean"
"default": false
minimum:
type: "number"
exclusiveMinimum:
type: "boolean"
"default": false
maxLength: { $ref: "#/definitions/positiveInteger" }
minLength: { $ref: "#/definitions/positiveIntegerDefault0" }
pattern:
type: "string"
format: "regex"
additionalItems:
anyOf: [
{ type: "boolean", "default": false }
{ $ref: "#" }
]
items:
anyOf: [
{ $ref: "#" }
{ $ref: "#/definitions/schemaArray" }
]
"default": {}
maxItems: { $ref: "#/definitions/positiveInteger" }
minItems: { $ref: "#/definitions/positiveIntegerDefault0" }
uniqueItems:
type: "boolean"
"default": false
maxProperties: { $ref: "#/definitions/positiveInteger" }
minProperties: { $ref: "#/definitions/positiveIntegerDefault0" }
required: { $ref: "#/definitions/stringArray" }
additionalProperties:
anyOf: [
{ type: "boolean", "default": true }
{ $ref: "#" }
]
"default": {}
definitions:
type: "object"
additionalProperties: { $ref: "#" }
"default": {}
properties:
type: "object"
additionalProperties: { $ref: "#" }
"default": {}
patternProperties:
type: "object"
additionalProperties: { $ref: "#" }
"default": {}
dependencies:
type: "object"
additionalProperties:
anyOf: [
{ $ref: "#" }
{ $ref: "#/definitions/stringArray" }
]
"enum":
type: "array"
minItems: 1
uniqueItems: true
"default": ['']
type:
anyOf: [
{ $ref: "#/definitions/simpleTypes" }
{
type: "array"
items: { $ref: "#/definitions/simpleTypes" }
minItems: 1
uniqueItems: true
title: "Array of Types"
"default": ['string']
}]
allOf: { $ref: "#/definitions/schemaArray" }
anyOf: { $ref: "#/definitions/schemaArray" }
oneOf: { $ref: "#/definitions/schemaArray" }
not: { $ref: "#" }
dependencies:
exclusiveMaximum: [ "maximum" ]
exclusiveMinimum: [ "minimum" ]
"default": {}

View file

@ -1,6 +1,6 @@
mongoose = require('mongoose')
plugins = require('../plugins/plugins')
jsonschema = require('./level_schema')
jsonschema = require('../../app/schemas/level_schema')
LevelSchema = new mongoose.Schema({
description: String

View file

@ -1,6 +1,6 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
jsonschema = require('./level_component_schema')
jsonschema = require('../../../app/schemas/level_component_schema')
LevelComponentSchema = new mongoose.Schema {
description: String

View file

@ -2,7 +2,7 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
jsonschema = require('./level_feedback_schema')
jsonschema = require('../../../app/schemas/level_feedback_schema')
LevelFeedbackSchema = new mongoose.Schema({
created:

View file

@ -1,27 +0,0 @@
c = require '../../commons/schemas'
LevelFeedbackLevelSchema = c.object {required: ['original', 'majorVersion']}, {
original: c.objectId({})
majorVersion: {type: 'integer', minimum: 0, default: 0}}
LevelFeedbackSchema = c.object {
title: "Feedback"
description: "Feedback on a level."
}
_.extend LevelFeedbackSchema.properties,
# denormalization
creatorName: { type: 'string' }
levelName: { type: 'string' }
levelID: { type: 'string' }
creator: c.objectId(links: [{rel: 'extra', href: "/db/user/{($)}"}])
created: c.date( { title: 'Created', readOnly: true })
level: LevelFeedbackLevelSchema
rating: { type: 'number', minimum: 1, maximum: 5 }
review: { type: 'string' }
c.extendBasicProperties LevelFeedbackSchema, 'level.feedback'
module.exports = LevelFeedbackSchema

View file

@ -2,7 +2,7 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
jsonschema = require('./level_session_schema')
jsonschema = require('../../../app/schemas/level_session_schema')
LevelSessionSchema = new mongoose.Schema({
created:

View file

@ -1,213 +0,0 @@
c = require '../../commons/schemas'
LevelSessionPlayerSchema = c.object
id: c.objectId
links: [
{
rel: 'extra'
href: "/db/user/{($)}"
}
]
time:
type: 'Number'
changes:
type: 'Number'
LevelSessionLevelSchema = c.object {required: ['original', 'majorVersion']},
original: c.objectId({})
majorVersion:
type: 'integer'
minimum: 0
default: 0
LevelSessionSchema = c.object
title: "Session"
description: "A single session for a given level."
_.extend LevelSessionSchema.properties,
# denormalization
creatorName:
type: 'string'
levelName:
type: 'string'
levelID:
type: 'string'
multiplayer:
type: 'boolean'
creator: c.objectId
links:
[
{
rel: 'extra'
href: "/db/user/{($)}"
}
]
created: c.date
title: 'Created'
readOnly: true
changed: c.date
title: 'Changed'
readOnly: true
team: c.shortString()
level: LevelSessionLevelSchema
screenshot:
type: 'string'
state: c.object {},
complete:
type: 'boolean'
scripts: c.object {},
ended:
type: 'object'
additionalProperties:
type: 'number'
currentScript:
type: [
'null'
'string'
]
currentScriptOffset:
type: 'number'
selected:
type: [
'null'
'string'
]
playing:
type: 'boolean'
frame:
type: 'number'
thangs:
type: 'object'
additionalProperties:
title: 'Thang'
type: 'object'
properties:
methods:
type: 'object'
additionalProperties:
title: 'Thang Method'
type: 'object'
properties:
metrics:
type: 'object'
source:
type: 'string'
# TODO: specify this more
code:
type: 'object'
teamSpells:
type: 'object'
additionalProperties:
type: 'array'
players:
type: 'object'
chat:
type: 'array'
meanStrength:
type: 'number'
standardDeviation:
type:'number'
minimum: 0
totalScore:
type: 'number'
submitted:
type: 'boolean'
submitDate: c.date
title: 'Submitted'
submittedCode:
type: 'object'
isRanking:
type: 'boolean'
description: 'Whether this session is still in the first ranking chain after being submitted.'
unsubscribed:
type: 'boolean'
description: 'Whether the player has opted out of receiving email updates about ladder rankings for this session.'
numberOfWinsAndTies:
type: 'number'
numberOfLosses:
type: 'number'
scoreHistory:
type: 'array'
title: 'Score History'
description: 'A list of objects representing the score history of a session'
items:
title: 'Score History Point'
description: 'An array with the format [unix timestamp, totalScore]'
type: 'array'
items:
type: 'number'
matches:
type: 'array'
title: 'Matches'
description: 'All of the matches a submitted session has played in its current state.'
items:
type: 'object'
properties:
date: c.date
title: 'Date computed'
description: 'The date a match was computed.'
metrics:
type: 'object'
title: 'Metrics'
description: 'Various information about the outcome of a match.'
properties:
rank:
title: 'Rank'
description: 'A 0-indexed ranking representing the player\'s standing in the outcome of a match'
type: 'number'
opponents:
type: 'array'
title: 'Opponents'
description: 'An array containing information about the opponents\' sessions in a given match.'
items:
type: 'object'
properties:
sessionID:
title: 'Opponent Session ID'
description: 'The session ID of an opponent.'
type: ['object', 'string']
userID:
title: 'Opponent User ID'
description: 'The user ID of an opponent'
type: ['object','string']
metrics:
type: 'object'
properties:
rank:
title: 'Opponent Rank'
description: 'The opponent\'s ranking in a given match'
type: 'number'
c.extendBasicProperties LevelSessionSchema, 'level.session'
c.extendPermissionsProperties LevelSessionSchema, 'level.session'
module.exports = LevelSessionSchema

View file

@ -1,6 +1,6 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
jsonschema = require('./level_system_schema')
jsonschema = require('../../../app/schemas/level_system_schema')
LevelSystemSchema = new mongoose.Schema {
description: String

View file

@ -1,21 +0,0 @@
c = require '../../commons/schemas'
module.exports = ThangComponentSchema = c.object {
title: "Component"
description: "Configuration for a Component that this Thang uses."
format: 'thang-component'
required: ['original', 'majorVersion']
'default':
majorVersion: 0
config: {}
links: [{rel: "db", href: "/db/level.component/{(original)}/version/{(majorVersion)}"}]
},
original: c.objectId(title: "Original", description: "A reference to the original Component being configured.", format: "hidden")
config: c.object {title: "Configuration", description: "Component-specific configuration properties.", additionalProperties: true, format: 'thang-component-configuration'}
majorVersion:
title: "Major Version"
description: "Which major version of the Component is being used."
type: 'integer'
minimum: 0
default: 0
format: "hidden"

View file

@ -1,5 +1,5 @@
mongoose = require('mongoose')
jsonschema = require('./user_schema')
jsonschema = require('../../app/schemas/user_schema')
crypto = require('crypto')
{salt, isProduction} = require('../../server_config')
mail = require '../commons/mail'

View file

@ -1,4 +1,4 @@
schema = require './user_schema'
schema = require '../../app/schemas/user_schema'
crypto = require 'crypto'
request = require 'request'
User = require './User'

View file

@ -1,98 +0,0 @@
c = require '../commons/schemas'
emailSubscriptions = ['announcement', 'tester', 'level_creator', 'developer', 'article_editor', 'translator', 'support', 'notification']
UserSchema = c.object {},
name: c.shortString({title: 'Display Name', default:''})
email: c.shortString({title: 'Email', format: 'email'})
firstName: c.shortString({title: 'First Name'})
lastName: c.shortString({title: 'Last Name'})
gender: {type: 'string', 'enum': ['male', 'female']}
password: {type: 'string', maxLength: 256, minLength: 2, title:'Password'}
passwordReset: {type: 'string'}
photoURL: {type: 'string', format: 'image-file', title: 'Profile Picture', description: 'Upload a 256x256px or larger image to serve as your profile picture.'}
facebookID: c.shortString({title: 'Facebook ID'})
gplusID: c.shortString({title: 'G+ ID'})
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' }
emailSubscriptions: c.array {uniqueItems: true, 'default': ['announcement', 'notification']}, {'enum': emailSubscriptions}
# server controlled
permissions: c.array {'default': []}, c.shortString()
dateCreated: c.date({title: 'Date Joined'})
anonymous: {type: 'boolean', 'default': true}
testGroupNumber: {type: 'integer', minimum: 0, maximum: 256, exclusiveMaximum: true}
mailChimp: {type: 'object'}
hourOfCode: {type: 'boolean'}
hourOfCodeComplete: {type: 'boolean'}
emailLower: c.shortString()
nameLower: c.shortString()
passwordHash: {type: 'string', maxLength: 256}
# client side
emailHash: {type: 'string'}
#Internationalization stuff
preferredLanguage: {type: 'string', default: 'en', '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']}
keyBindings: {type: 'string', 'default': 'default', 'enum': ['default', 'vim', 'emacs']}
invisibles: {type: 'boolean', 'default': false}
indentGuides: {type: 'boolean', 'default': false}
behaviors: {type: 'boolean', 'default': false}
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'}
active: {title: 'Active', type: 'boolean', description: 'Want interview offers right now?'}
updated: c.date {title: 'Last Updated', description: 'How fresh your profile appears to employers. The fresher, the better. Profiles go inactive after 30 days.'}
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. Employers will see the first five at a glance.', default: ['javascript'], minItems: 1, maxItems: 30, uniqueItems: true},
{type: 'string', minLength: 1, maxLength: 20, 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!'}
visa: c.shortString {title: 'US Work Status', description: 'Are you authorized to work in the US, or do you need visa sponsorship?', enum: ['Authorized to work in the US', 'Need visa sponsorship'], default: 'Authorized to work in the US'}
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']},
employer: c.shortString {title: 'Employer', description: 'Name of your employer.'}
role: c.shortString {title: 'Job Title', description: 'What was your job title or role?'}
duration: c.shortString {title: 'Duration', description: 'When did you hold this gig? Ex.: "Feb 2013 - present".'}
education: c.array {title: 'Education', description: 'List your academic ordeals.'},
c.object {title: 'Ordeal', description: 'Some education that befell you.', required: ['school', 'degree', 'duration']},
school: c.shortString {title: 'School', description: 'Name of your school.'}
degree: c.shortString {title: 'Degree', description: 'What was your degree and field of study? Ex. Ph.D. Human-Computer Interaction (incomplete)'}
duration: c.shortString {title: 'Dates', description: 'When? Ex.: "Aug 2004 - May 2008".'}
projects: c.array {title: 'Projects', description: 'Highlight your projects to amaze employers.'},
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'}
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'}
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']},
name: {type: 'string', maxLength: 30, title: 'Link Name', description: 'What are you linking to? Ex: "Personal Website", "Twitter"', format: 'link-name'}
link: c.url {title: 'Link', description: 'The URL.', default: 'http://example.com'}
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.'}
jobProfileApproved: {title: 'Job Profile Approved', type: 'boolean', description: 'Whether your profile has been approved by CodeCombat.'}
jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: "CodeCombat's notes on the candidate.", format: 'markdown', default: ''}
c.extendBasicProperties UserSchema, 'user'
module.exports = UserSchema