mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-02 03:47:09 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
52d08d7d05
14 changed files with 190 additions and 10 deletions
|
@ -282,6 +282,7 @@
|
||||||
education_description: "Description"
|
education_description: "Description"
|
||||||
education_description_help: "Highlight anything about this educational experience. (140 chars; optional)"
|
education_description_help: "Highlight anything about this educational experience. (140 chars; optional)"
|
||||||
our_notes: "Our Notes"
|
our_notes: "Our Notes"
|
||||||
|
remarks: "Remarks"
|
||||||
projects: "Projects"
|
projects: "Projects"
|
||||||
projects_header: "Add 3 projects"
|
projects_header: "Add 3 projects"
|
||||||
projects_header_2: "Projects (Top 3)"
|
projects_header_2: "Projects (Top 3)"
|
||||||
|
@ -320,6 +321,7 @@
|
||||||
candidate_top_skills: "Top Skills"
|
candidate_top_skills: "Top Skills"
|
||||||
candidate_years_experience: "Yrs Exp"
|
candidate_years_experience: "Yrs Exp"
|
||||||
candidate_last_updated: "Last Updated"
|
candidate_last_updated: "Last Updated"
|
||||||
|
candidate_who: "Who"
|
||||||
featured_developers: "Featured Developers"
|
featured_developers: "Featured Developers"
|
||||||
other_developers: "Other Developers"
|
other_developers: "Other Developers"
|
||||||
inactive_developers: "Inactive Developers"
|
inactive_developers: "Inactive Developers"
|
||||||
|
@ -882,6 +884,7 @@
|
||||||
document: "Document"
|
document: "Document"
|
||||||
sprite_sheet: "Sprite Sheet"
|
sprite_sheet: "Sprite Sheet"
|
||||||
candidate_sessions: "Candidate Sessions"
|
candidate_sessions: "Candidate Sessions"
|
||||||
|
user_remark: "User Remark"
|
||||||
|
|
||||||
delta:
|
delta:
|
||||||
added: "Added"
|
added: "Added"
|
||||||
|
|
6
app/models/UserRemark.coffee
Normal file
6
app/models/UserRemark.coffee
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
CocoModel = require('./CocoModel')
|
||||||
|
|
||||||
|
module.exports = class UserRemark extends CocoModel
|
||||||
|
@className: "UserRemark"
|
||||||
|
@schema: require 'schemas/models/user_remark'
|
||||||
|
urlRoot: "/db/user.remark"
|
24
app/schemas/models/user_remark.coffee
Normal file
24
app/schemas/models/user_remark.coffee
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
c = require './../schemas'
|
||||||
|
|
||||||
|
UserRemarkSchema = c.object {
|
||||||
|
title: "Remark"
|
||||||
|
description: "Remarks on a user, point of contact, tasks."
|
||||||
|
}
|
||||||
|
|
||||||
|
_.extend UserRemarkSchema.properties,
|
||||||
|
user: c.objectId links: [{rel: 'extra', href: "/db/user/{($)}"}]
|
||||||
|
contact: c.objectId links: [{rel: 'extra', href: "/db/user/{($)}"}]
|
||||||
|
created: c.date title: 'Created', readOnly: true
|
||||||
|
history: c.array {title: 'History', description: 'Records of our interactions with the user.'},
|
||||||
|
c.object {title: 'Record'}, {date: c.date(title: 'Date'), content: {title: 'Content', type: 'string', format: 'markdown'}}
|
||||||
|
tasks: c.array {title: 'Tasks', description: 'Task entries: when to email the contact about something.'},
|
||||||
|
c.object {title: 'Task'}, {date: c.date(title: 'Date'), action: {title: 'Action', type: 'string'}}
|
||||||
|
|
||||||
|
# denormalization
|
||||||
|
userName: { title: "Player Name", type: 'string' }
|
||||||
|
contactName: { title: "Contact Name", type: 'string' } # Not actually our usernames
|
||||||
|
|
||||||
|
|
||||||
|
c.extendBasicProperties UserRemarkSchema, 'user.remark'
|
||||||
|
|
||||||
|
module.exports = UserRemarkSchema
|
|
@ -193,6 +193,11 @@
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 100px
|
height: 100px
|
||||||
|
|
||||||
|
#remark-treema
|
||||||
|
background-color: white
|
||||||
|
border: 0
|
||||||
|
padding-top: 0
|
||||||
|
|
||||||
.right-column
|
.right-column
|
||||||
width: $side-width
|
width: $side-width
|
||||||
background-color: $sideBackground
|
background-color: $sideBackground
|
||||||
|
|
|
@ -169,6 +169,10 @@ block content
|
||||||
button#contact-candidate.btn.btn-large.btn-inverse.flat-button
|
button#contact-candidate.btn.btn-large.btn-inverse.flat-button
|
||||||
span(data-i18n="account_profile.contact") Contact
|
span(data-i18n="account_profile.contact") Contact
|
||||||
| #{profile.name.split(' ')[0]}
|
| #{profile.name.split(' ')[0]}
|
||||||
|
if me.isAdmin()
|
||||||
|
select#admin-contact.form-control
|
||||||
|
for contact in adminContacts
|
||||||
|
option(value=contact.id, selected=remark && remark.get('contact') == contact.id)= contact.name
|
||||||
|
|
||||||
if !editing && sessions.length
|
if !editing && sessions.length
|
||||||
h3(data-i18n="account_profile.player_code") Player Code
|
h3(data-i18n="account_profile.player_code") Player Code
|
||||||
|
@ -191,9 +195,12 @@ block content
|
||||||
if editing && !profile.name
|
if editing && !profile.name
|
||||||
h3.edit-label(data-i18n="account_profile.name_header") Fill in your name
|
h3.edit-label(data-i18n="account_profile.name_header") Fill in your name
|
||||||
else if profile.name
|
else if profile.name
|
||||||
h3= profile.name
|
h3= profile.name + (me.isAdmin() ? ' (' + user.get('name') + ')' : '')
|
||||||
else
|
else
|
||||||
h3(data-i18n="account_profile.name_anonymous") Anonymous Developer
|
h3
|
||||||
|
span(data-i18n="account_profile.name_anonymous") Anonymous Developer
|
||||||
|
if me.isAdmin()
|
||||||
|
span (#{user.get('name')})
|
||||||
|
|
||||||
form.editable-form
|
form.editable-form
|
||||||
.editable-icon.glyphicon.glyphicon-remove
|
.editable-icon.glyphicon.glyphicon-remove
|
||||||
|
@ -396,6 +403,10 @@ block content
|
||||||
else
|
else
|
||||||
div!= marked(notes)
|
div!= marked(notes)
|
||||||
|
|
||||||
|
if me.isAdmin()
|
||||||
|
h3(data-i18n="account_profile.remarks") Remarks
|
||||||
|
#remark-treema
|
||||||
|
|
||||||
.right-column.full-height-column
|
.right-column.full-height-column
|
||||||
.sub-column
|
.sub-column
|
||||||
#projects-container.editable-section
|
#projects-container.editable-section
|
||||||
|
|
|
@ -84,6 +84,8 @@ block content
|
||||||
th(data-i18n="employers.candidate_top_skills") Top Skills
|
th(data-i18n="employers.candidate_top_skills") Top Skills
|
||||||
th(data-i18n="employers.candidate_years_experience") Yrs Exp
|
th(data-i18n="employers.candidate_years_experience") Yrs Exp
|
||||||
th(data-i18n="employers.candidate_last_updated") Last Updated
|
th(data-i18n="employers.candidate_last_updated") Last Updated
|
||||||
|
if me.isAdmin()
|
||||||
|
th(data-i18n="employers.candidate_who") Who
|
||||||
if me.isAdmin() && area.id == 'inactive-candidates'
|
if me.isAdmin() && area.id == 'inactive-candidates'
|
||||||
th ✓?
|
th ✓?
|
||||||
|
|
||||||
|
@ -95,7 +97,10 @@ block content
|
||||||
td
|
td
|
||||||
if authorized
|
if authorized
|
||||||
img(src=candidate.getPhotoURL(50), alt=profile.name, title=profile.name, height=50)
|
img(src=candidate.getPhotoURL(50), alt=profile.name, title=profile.name, height=50)
|
||||||
p= profile.name
|
if profile.name
|
||||||
|
p= profile.name
|
||||||
|
else if me.isAdmin()
|
||||||
|
p (#{candidate.get('name')})
|
||||||
else
|
else
|
||||||
img(src="/images/pages/contribute/archmage.png", alt="", title="Sign up as an employer to see our candidates", width=50)
|
img(src="/images/pages/contribute/archmage.png", alt="", title="Sign up as an employer to see our candidates", width=50)
|
||||||
p Developer ##{index + 1 + (area.id == 'featured-candidates' ? 0 : featuredCandidates.length)}
|
p Developer ##{index + 1 + (area.id == 'featured-candidates' ? 0 : featuredCandidates.length)}
|
||||||
|
@ -111,6 +116,8 @@ block content
|
||||||
span
|
span
|
||||||
td= profile.experience
|
td= profile.experience
|
||||||
td(data-profile-age=(new Date() - new Date(profile.updated)) / 86400 / 1000)= moment(profile.updated).fromNow()
|
td(data-profile-age=(new Date() - new Date(profile.updated)) / 86400 / 1000)= moment(profile.updated).fromNow()
|
||||||
|
if me.isAdmin()
|
||||||
|
td= remarks[candidate.id] ? remarks[candidate.id].get('contactName') : ''
|
||||||
if me.isAdmin() && area.id == 'inactive-candidates'
|
if me.isAdmin() && area.id == 'inactive-candidates'
|
||||||
if candidate.get('jobProfileApproved')
|
if candidate.get('jobProfileApproved')
|
||||||
td ✓
|
td ✓
|
||||||
|
|
|
@ -6,6 +6,8 @@ locale = require 'locale/locale'
|
||||||
class DateTimeTreema extends TreemaNode.nodeMap.string
|
class DateTimeTreema extends TreemaNode.nodeMap.string
|
||||||
valueClass: 'treema-date-time'
|
valueClass: 'treema-date-time'
|
||||||
buildValueForDisplay: (el) -> el.text(moment(@data).format('llll'))
|
buildValueForDisplay: (el) -> el.text(moment(@data).format('llll'))
|
||||||
|
buildValueForEditing: (valEl) ->
|
||||||
|
@buildValueForEditingSimply valEl, null, 'date'
|
||||||
|
|
||||||
class VersionTreema extends TreemaNode
|
class VersionTreema extends TreemaNode
|
||||||
valueClass: 'treema-version'
|
valueClass: 'treema-version'
|
||||||
|
|
|
@ -19,10 +19,10 @@ module.exports = class JobProfileView extends CocoView
|
||||||
|
|
||||||
buildJobProfileTreema: ->
|
buildJobProfileTreema: ->
|
||||||
visibleSettings = @editableSettings.concat @readOnlySettings
|
visibleSettings = @editableSettings.concat @readOnlySettings
|
||||||
data = _.pick (me.get('jobProfile') ? {}), (value, key) => key in visibleSettings
|
data = _.pick (me.get('jobProfile') ? {}), (value, key) -> key in visibleSettings
|
||||||
data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName')
|
data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName')
|
||||||
schema = _.cloneDeep me.schema().properties.jobProfile
|
schema = _.cloneDeep me.schema().properties.jobProfile
|
||||||
schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings
|
schema.properties = _.pick schema.properties, (value, key) -> key in visibleSettings
|
||||||
schema.required = _.intersection schema.required, visibleSettings
|
schema.required = _.intersection schema.required, visibleSettings
|
||||||
for prop in @readOnlySettings
|
for prop in @readOnlySettings
|
||||||
schema.properties[prop].readOnly = true
|
schema.properties[prop].readOnly = true
|
||||||
|
|
|
@ -6,6 +6,7 @@ CocoCollection = require 'collections/CocoCollection'
|
||||||
{me} = require 'lib/auth'
|
{me} = require 'lib/auth'
|
||||||
JobProfileContactView = require 'views/modal/job_profile_contact_modal'
|
JobProfileContactView = require 'views/modal/job_profile_contact_modal'
|
||||||
JobProfileView = require 'views/account/job_profile_view'
|
JobProfileView = require 'views/account/job_profile_view'
|
||||||
|
UserRemark = require 'models/UserRemark'
|
||||||
forms = require 'lib/forms'
|
forms = require 'lib/forms'
|
||||||
|
|
||||||
class LevelSessionsCollection extends CocoCollection
|
class LevelSessionsCollection extends CocoCollection
|
||||||
|
@ -14,6 +15,14 @@ class LevelSessionsCollection extends CocoCollection
|
||||||
constructor: (@userID) ->
|
constructor: (@userID) ->
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
adminContacts = [
|
||||||
|
{id: "", name: "Assign a Contact"}
|
||||||
|
{id: "512ef4805a67a8c507000001", name: "Nick"}
|
||||||
|
{id: "5162fab9c92b4c751e000274", name: "Scott"}
|
||||||
|
{id: "51eb2714fa058cb20d0006ef", name: "Michael"}
|
||||||
|
{id: "51538fdb812dd9af02000001", name: "George"}
|
||||||
|
]
|
||||||
|
|
||||||
module.exports = class ProfileView extends View
|
module.exports = class ProfileView extends View
|
||||||
id: "profile-view"
|
id: "profile-view"
|
||||||
template: template
|
template: template
|
||||||
|
@ -36,12 +45,14 @@ module.exports = class ProfileView extends View
|
||||||
'change .editable-profile .editable-array input': 'onEditArray'
|
'change .editable-profile .editable-array input': 'onEditArray'
|
||||||
'keyup .editable-profile .editable-array input': 'onEditArray'
|
'keyup .editable-profile .editable-array input': 'onEditArray'
|
||||||
'click .editable-profile a': 'onClickLinkWhileEditing'
|
'click .editable-profile a': 'onClickLinkWhileEditing'
|
||||||
|
'change #admin-contact': 'onAdminContactChanged'
|
||||||
|
|
||||||
constructor: (options, @userID) ->
|
constructor: (options, @userID) ->
|
||||||
@userID ?= me.id
|
@userID ?= me.id
|
||||||
@onJobProfileNotesChanged = _.debounce @onJobProfileNotesChanged, 1000
|
@onJobProfileNotesChanged = _.debounce @onJobProfileNotesChanged, 1000
|
||||||
|
@onRemarkChanged = _.debounce @onRemarkChanged, 1000
|
||||||
@authorizedWithLinkedIn = IN?.User?.isAuthorized()
|
@authorizedWithLinkedIn = IN?.User?.isAuthorized()
|
||||||
@linkedInLoaded = Boolean(IN.parse)
|
@linkedInLoaded = Boolean(IN?.parse)
|
||||||
@waitingForLinkedIn = false
|
@waitingForLinkedIn = false
|
||||||
window.contractCallback = =>
|
window.contractCallback = =>
|
||||||
@authorizedWithLinkedIn = IN?.User?.isAuthorized()
|
@authorizedWithLinkedIn = IN?.User?.isAuthorized()
|
||||||
|
@ -70,6 +81,22 @@ module.exports = class ProfileView extends View
|
||||||
else
|
else
|
||||||
@user = User.getByID(@userID)
|
@user = User.getByID(@userID)
|
||||||
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(@userID), 'candidate_sessions').model
|
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(@userID), 'candidate_sessions').model
|
||||||
|
if me.isAdmin()
|
||||||
|
# Mimicking how the VictoryModal fetches LevelFeedback
|
||||||
|
@remark = new UserRemark()
|
||||||
|
@remark.setURL "/db/user/#{@userID}/remark"
|
||||||
|
@remark.fetch()
|
||||||
|
@listenToOnce @remark, 'sync', @onRemarkLoaded
|
||||||
|
@listenToOnce @remark, 'error', @onRemarkNotFound
|
||||||
|
|
||||||
|
onRemarkLoaded: ->
|
||||||
|
@remark.setURL "/db/user.remark/#{@remark.id}"
|
||||||
|
@render()
|
||||||
|
|
||||||
|
onRemarkNotFound: ->
|
||||||
|
@remark = new UserRemark() # hmm, why do we create a new one here?
|
||||||
|
@remark.set 'user', @userID
|
||||||
|
@remark.set 'userName', name if name = @user.get('name')
|
||||||
|
|
||||||
onLinkedInLoaded: =>
|
onLinkedInLoaded: =>
|
||||||
@linkedinLoaded = true
|
@linkedinLoaded = true
|
||||||
|
@ -229,6 +256,8 @@ module.exports = class ProfileView extends View
|
||||||
context.sessions.sort (a, b) -> (b.playtime ? 0) - (a.playtime ? 0)
|
context.sessions.sort (a, b) -> (b.playtime ? 0) - (a.playtime ? 0)
|
||||||
else
|
else
|
||||||
context.sessions = []
|
context.sessions = []
|
||||||
|
context.adminContacts = adminContacts
|
||||||
|
context.remark = @remark
|
||||||
context
|
context
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
|
@ -249,6 +278,31 @@ module.exports = class ProfileView extends View
|
||||||
_.delay ->
|
_.delay ->
|
||||||
justSavedSection.removeClass "just-saved", duration: 1500, easing: 'easeOutQuad'
|
justSavedSection.removeClass "just-saved", duration: 1500, easing: 'easeOutQuad'
|
||||||
, 500
|
, 500
|
||||||
|
if me.isAdmin()
|
||||||
|
visibleSettings = ['history', 'tasks']
|
||||||
|
data = _.pick (@remark.attributes), (value, key) -> key in visibleSettings
|
||||||
|
data.history ?= []
|
||||||
|
data.tasks ?= []
|
||||||
|
schema = _.cloneDeep @remark.schema()
|
||||||
|
schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings
|
||||||
|
schema.required = _.intersection (schema.required ? []), visibleSettings
|
||||||
|
treemaOptions =
|
||||||
|
filePath: "db/user/#{@userID}"
|
||||||
|
schema: schema
|
||||||
|
data: data
|
||||||
|
aceUseWrapMode: true
|
||||||
|
callbacks: {change: @onRemarkChanged}
|
||||||
|
@remarkTreema = @$el.find('#remark-treema').treema treemaOptions
|
||||||
|
@remarkTreema.build()
|
||||||
|
@remarkTreema.open(3)
|
||||||
|
|
||||||
|
onRemarkChanged: (e) =>
|
||||||
|
return unless @remarkTreema.isValid()
|
||||||
|
for key in ['history', 'tasks']
|
||||||
|
val = _.filter(@remarkTreema.get(key), (entry) -> entry?.content or entry?.action)
|
||||||
|
entry.date ?= (new Date()).toISOString() for entry in val if key is 'history'
|
||||||
|
@remark.set key, val
|
||||||
|
@saveRemark()
|
||||||
|
|
||||||
initializeAutocomplete: (container) ->
|
initializeAutocomplete: (container) ->
|
||||||
(container ? @$el).find('input[data-autocomplete]').each ->
|
(container ? @$el).find('input[data-autocomplete]').each ->
|
||||||
|
@ -455,6 +509,26 @@ module.exports = class ProfileView extends View
|
||||||
onClickLinkWhileEditing: (e) ->
|
onClickLinkWhileEditing: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
|
onAdminContactChanged: (e) ->
|
||||||
|
newContact = @$el.find('#admin-contact').val()
|
||||||
|
newContactName = if newContact then _.find(adminContacts, id: newContact).name else ''
|
||||||
|
@remark.set 'contact', newContact
|
||||||
|
@remark.set 'contactName', newContactName
|
||||||
|
@saveRemark()
|
||||||
|
|
||||||
|
saveRemark: ->
|
||||||
|
@remark.set 'user', @user.id
|
||||||
|
@remark.set 'userName', @user.get('name')
|
||||||
|
if errors = @remark.validate()
|
||||||
|
return console.error "UserRemark", @remark, "failed validation with errors:", errors
|
||||||
|
res = @remark.save()
|
||||||
|
res.error =>
|
||||||
|
return if @destroyed
|
||||||
|
console.error "UserRemark", @remark, "failed to save with error:", res.responseText
|
||||||
|
res.success (model, response, options) =>
|
||||||
|
return if @destroyed
|
||||||
|
console.log "Saved UserRemark", @remark, "with response", response
|
||||||
|
|
||||||
updateProgress: (highlightNext) ->
|
updateProgress: (highlightNext) ->
|
||||||
return unless @user
|
return unless @user
|
||||||
completed = 0
|
completed = 0
|
||||||
|
|
|
@ -2,6 +2,7 @@ View = require 'views/kinds/RootView'
|
||||||
template = require 'templates/employers'
|
template = require 'templates/employers'
|
||||||
app = require 'application'
|
app = require 'application'
|
||||||
User = require 'models/User'
|
User = require 'models/User'
|
||||||
|
UserRemark = require 'models/UserRemark'
|
||||||
{me} = require 'lib/auth'
|
{me} = require 'lib/auth'
|
||||||
CocoCollection = require 'collections/CocoCollection'
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
EmployerSignupView = require 'views/modal/employer_signup_modal'
|
EmployerSignupView = require 'views/modal/employer_signup_modal'
|
||||||
|
@ -10,6 +11,10 @@ class CandidatesCollection extends CocoCollection
|
||||||
url: '/db/user/x/candidates'
|
url: '/db/user/x/candidates'
|
||||||
model: User
|
model: User
|
||||||
|
|
||||||
|
class UserRemarksCollection extends CocoCollection
|
||||||
|
url: '/db/user.remark?project=contact,contactName,user'
|
||||||
|
model: UserRemark
|
||||||
|
|
||||||
module.exports = class EmployersView extends View
|
module.exports = class EmployersView extends View
|
||||||
id: "employers-view"
|
id: "employers-view"
|
||||||
template: template
|
template: template
|
||||||
|
@ -37,6 +42,8 @@ module.exports = class EmployersView extends View
|
||||||
ctx.inactiveCandidates = _.reject ctx.candidates, (c) -> c.get('jobProfile').active
|
ctx.inactiveCandidates = _.reject ctx.candidates, (c) -> c.get('jobProfile').active
|
||||||
ctx.featuredCandidates = _.filter ctx.activeCandidates, (c) -> c.get('jobProfileApproved')
|
ctx.featuredCandidates = _.filter ctx.activeCandidates, (c) -> c.get('jobProfileApproved')
|
||||||
ctx.otherCandidates = _.reject ctx.activeCandidates, (c) -> c.get('jobProfileApproved')
|
ctx.otherCandidates = _.reject ctx.activeCandidates, (c) -> c.get('jobProfileApproved')
|
||||||
|
ctx.remarks = {}
|
||||||
|
ctx.remarks[remark.get('user')] = remark for remark in @remarks.models
|
||||||
ctx.moment = moment
|
ctx.moment = moment
|
||||||
ctx._ = _
|
ctx._ = _
|
||||||
ctx
|
ctx
|
||||||
|
@ -48,11 +55,13 @@ module.exports = class EmployersView extends View
|
||||||
getCandidates: ->
|
getCandidates: ->
|
||||||
@candidates = new CandidatesCollection()
|
@candidates = new CandidatesCollection()
|
||||||
@candidates.fetch()
|
@candidates.fetch()
|
||||||
|
@remarks = new UserRemarksCollection()
|
||||||
|
@remarks.fetch()
|
||||||
# Re-render when we have fetched them, but don't wait and show a progress bar while loading.
|
# Re-render when we have fetched them, but don't wait and show a progress bar while loading.
|
||||||
@listenToOnce @candidates, 'all', @renderCandidatesAndSetupScrolling
|
@listenToOnce @candidates, 'all', @renderCandidatesAndSetupScrolling
|
||||||
|
@listenToOnce @remarks, 'all', @renderCandidatesAndSetupScrolling
|
||||||
|
|
||||||
renderCandidatesAndSetupScrolling: =>
|
renderCandidatesAndSetupScrolling: =>
|
||||||
|
|
||||||
@render()
|
@render()
|
||||||
$(".nano").nanoScroller()
|
$(".nano").nanoScroller()
|
||||||
if window.history?.state?.lastViewedCandidateID
|
if window.history?.state?.lastViewedCandidateID
|
||||||
|
@ -179,7 +188,7 @@ module.exports = class EmployersView extends View
|
||||||
"Last 4 weeks": (e, n, f, i, $r) ->
|
"Last 4 weeks": (e, n, f, i, $r) ->
|
||||||
days = parseFloat $($r.find('td')[i]).data('profile-age')
|
days = parseFloat $($r.find('td')[i]).data('profile-age')
|
||||||
days <= 28
|
days <= 28
|
||||||
7:
|
8:
|
||||||
"✓": filterSelectExactMatch
|
"✓": filterSelectExactMatch
|
||||||
"✗": filterSelectExactMatch
|
"✗": filterSelectExactMatch
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ module.exports.handlers =
|
||||||
'patch': 'patches/patch_handler'
|
'patch': 'patches/patch_handler'
|
||||||
'thang_type': 'levels/thangs/thang_type_handler'
|
'thang_type': 'levels/thangs/thang_type_handler'
|
||||||
'user': 'users/user_handler'
|
'user': 'users/user_handler'
|
||||||
|
'user_remark': 'users/remarks/user_remark_handler'
|
||||||
'achievement': 'achievements/achievement_handler'
|
'achievement': 'achievements/achievement_handler'
|
||||||
'earned_achievement': 'achievements/earned_achievement_handler'
|
'earned_achievement': 'achievements/earned_achievement_handler'
|
||||||
|
|
||||||
|
|
11
server/users/remarks/UserRemark.coffee
Normal file
11
server/users/remarks/UserRemark.coffee
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
mongoose = require('mongoose')
|
||||||
|
plugins = require('../../plugins/plugins')
|
||||||
|
jsonschema = require('../../../app/schemas/models/user_remark')
|
||||||
|
|
||||||
|
UserRemarkSchema = new mongoose.Schema({
|
||||||
|
created:
|
||||||
|
type: Date
|
||||||
|
'default': Date.now
|
||||||
|
}, {strict: false})
|
||||||
|
|
||||||
|
module.exports = UserRemark = mongoose.model('user.remark', UserRemarkSchema)
|
12
server/users/remarks/user_remark_handler.coffee
Normal file
12
server/users/remarks/user_remark_handler.coffee
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
UserRemark = require('./UserRemark')
|
||||||
|
Handler = require('../../commons/Handler')
|
||||||
|
|
||||||
|
class UserRemarkHandler extends Handler
|
||||||
|
modelClass: UserRemark
|
||||||
|
editableProperties: ['user', 'contact', 'history', 'tasks', 'userName', 'contactName']
|
||||||
|
jsonSchema: require '../../../app/schemas/models/user_remark'
|
||||||
|
|
||||||
|
hasAccess: (req) ->
|
||||||
|
req.user?.isAdmin()
|
||||||
|
|
||||||
|
module.exports = new UserRemarkHandler()
|
|
@ -11,6 +11,7 @@ log = require 'winston'
|
||||||
LevelSession = require('../levels/sessions/LevelSession')
|
LevelSession = require('../levels/sessions/LevelSession')
|
||||||
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
||||||
EarnedAchievement = require '../achievements/EarnedAchievement'
|
EarnedAchievement = require '../achievements/EarnedAchievement'
|
||||||
|
UserRemark = require './remarks/UserRemark'
|
||||||
|
|
||||||
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset']
|
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset']
|
||||||
privateProperties = [
|
privateProperties = [
|
||||||
|
@ -197,6 +198,7 @@ UserHandler = class UserHandler extends Handler
|
||||||
return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank'
|
return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank'
|
||||||
return @getEarnedAchievements(req, res, args[0]) if args[1] is 'achievements'
|
return @getEarnedAchievements(req, res, args[0]) if args[1] is 'achievements'
|
||||||
return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2]
|
return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2]
|
||||||
|
return @getRemark(req, res, args[0]) if args[1] is 'remark'
|
||||||
return @sendNotFoundError(res)
|
return @sendNotFoundError(res)
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
|
|
||||||
|
@ -313,7 +315,7 @@ UserHandler = class UserHandler extends Handler
|
||||||
#query.jobProfileApproved = true unless req.user.isAdmin() # We split into featured and other now.
|
#query.jobProfileApproved = true unless req.user.isAdmin() # We split into featured and other now.
|
||||||
query['jobProfile.active'] = true unless req.user.isAdmin()
|
query['jobProfile.active'] = true unless req.user.isAdmin()
|
||||||
selection = 'jobProfile jobProfileApproved photoURL'
|
selection = 'jobProfile jobProfileApproved photoURL'
|
||||||
selection += ' email' if authorized
|
selection += ' email name' if authorized
|
||||||
User.find(query).select(selection).exec (err, documents) =>
|
User.find(query).select(selection).exec (err, documents) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
candidates = (candidate for candidate in documents when @employerCanViewCandidate req.user, candidate.toObject())
|
candidates = (candidate for candidate in documents when @employerCanViewCandidate req.user, candidate.toObject())
|
||||||
|
@ -321,7 +323,7 @@ UserHandler = class UserHandler extends Handler
|
||||||
@sendSuccess(res, candidates)
|
@sendSuccess(res, candidates)
|
||||||
|
|
||||||
formatCandidate: (authorized, document) ->
|
formatCandidate: (authorized, document) ->
|
||||||
fields = if authorized then ['jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['jobProfile', 'jobProfileApproved']
|
fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['jobProfile', 'jobProfileApproved']
|
||||||
obj = _.pick document.toObject(), fields
|
obj = _.pick document.toObject(), fields
|
||||||
obj.photoURL ||= obj.jobProfile.photoURL if authorized
|
obj.photoURL ||= obj.jobProfile.photoURL if authorized
|
||||||
subfields = ['country', 'city', 'lookingFor', 'jobTitle', 'skills', 'experience', 'updated', 'active']
|
subfields = ['country', 'city', 'lookingFor', 'jobTitle', 'skills', 'experience', 'updated', 'active']
|
||||||
|
@ -363,4 +365,17 @@ UserHandler = class UserHandler extends Handler
|
||||||
hash.update(user.get('_id') + '')
|
hash.update(user.get('_id') + '')
|
||||||
hash.digest('hex')
|
hash.digest('hex')
|
||||||
|
|
||||||
|
getRemark: (req, res, userID) ->
|
||||||
|
return @sendUnauthorizedError(res) unless req.user.isAdmin()
|
||||||
|
query = user: userID
|
||||||
|
projection = null
|
||||||
|
if req.query.project
|
||||||
|
projection = {}
|
||||||
|
projection[field] = 1 for field in req.query.project.split(',')
|
||||||
|
UserRemark.findOne(query).select(projection).exec (err, remark) =>
|
||||||
|
return @sendDatabaseError res, err if err
|
||||||
|
return @sendNotFoundError res unless remark?
|
||||||
|
@sendSuccess res, remark
|
||||||
|
|
||||||
|
|
||||||
module.exports = new UserHandler()
|
module.exports = new UserHandler()
|
||||||
|
|
Loading…
Reference in a new issue