diff --git a/app/assets/images/pages/account/profile/sample_profile.png b/app/assets/images/pages/account/profile/sample_profile.png new file mode 100644 index 000000000..b61294854 Binary files /dev/null and b/app/assets/images/pages/account/profile/sample_profile.png differ diff --git a/app/assets/images/pages/account/profile/sample_profile_thumb.png b/app/assets/images/pages/account/profile/sample_profile_thumb.png new file mode 100644 index 000000000..bc1e63e13 Binary files /dev/null and b/app/assets/images/pages/account/profile/sample_profile_thumb.png differ diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 1a5da3221..41f9f15b6 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -36,6 +36,7 @@ editor: "Editor" blog: "Blog" forum: "Forum" + account: "Account" admin: "Admin" home: "Home" contribute: "Contribute" @@ -170,6 +171,8 @@ job_profile: "Job Profile" job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." + sample_profile: "See a sample profile" + view_profile: "View Your Profile" account_profile: edit_settings: "Edit Settings" @@ -306,6 +309,13 @@ lg_title: "Latest Games" clas: "CLAs" + community: + level_editor: "Level Editor" + main_title: "CodeCombat Community" + facebook: "Facebook" + twitter: "Twitter" + gplus: "Google+" + editor: main_title: "CodeCombat Editors" main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!" @@ -315,8 +325,8 @@ thang_description: "Build units, defining their default logic, graphics and audio. Currently only supports importing Flash exported vector graphics." level_title: "Level Editor" level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!" - security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, " - contact_us: "contact us!" + got_questions: "Questions about using the CodeCombat editors?" + contact_us: "Contact us!" hipchat_prefix: "You can also find us in our" hipchat_url: "HipChat room." back: "Back" diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index 28ab6d5c4..b91c4571b 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -58,7 +58,7 @@ UserSchema = c.object {}, 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?'} + 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'} @@ -74,13 +74,14 @@ UserSchema = c.object {}, 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".'} - description: {type: 'string', title: 'Description', description: 'What did you do there? (140 chars)', maxLength: 140} + description: {type: 'string', title: 'Description', description: 'What did you do there? (140 chars; optional)', maxLength: 140} 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.', maxItems: 3}, + 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'} @@ -94,6 +95,8 @@ UserSchema = c.object {}, 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: ''} + employerAt: c.shortString {description: "If given employer permissions to view job candidates, for which employer?"} + c.extendBasicProperties UserSchema, 'user' module.exports = UserSchema diff --git a/app/styles/account/settings.sass b/app/styles/account/settings.sass index b768d69a2..ae607c086 100644 --- a/app/styles/account/settings.sass +++ b/app/styles/account/settings.sass @@ -44,6 +44,26 @@ .form max-width: 600px +#job-profile-view + .profile-preview-button + &.bottom-preview + margin: 15px 0 0 0 + + .sample-profile-thumbnail + margin-top: -60px + + .profile-completion-progress + width: 100% + display: inline-block + height: 33px + + .progress-bar + line-height: 33px + + .progress-next-item + margin-top: -20px + margin-bottom: 15px + #job-profile-treema background-color: white @@ -54,3 +74,16 @@ font-size: 14px line-height: 22px opacity: 1 + + .treema-row + padding-top: 6px + + .treema-image-file + .btn:after + content: "Upload Picture" + margin-left: 20px + + img + display: block + clear: both + max-width: 300px diff --git a/app/styles/common/top_nav.sass b/app/styles/common/top_nav.sass index f5c61685e..3619a3f9b 100644 --- a/app/styles/common/top_nav.sass +++ b/app/styles/common/top_nav.sass @@ -10,16 +10,19 @@ font-weight: 400 letter-spacing: 1px - .navbuttontext-user-name - max-width: 110px - overflow: hidden - text-overflow: ellipsis - white-space: nowrap + .navbuttontext-account display: inline-block padding: 0 5px 0 0 - margin: 0 + margin: 0 5px 0 0 height: 18px + .account-settings-image + width: 18px + height: 18px + + .glyphicon-user + font-size: 16px + .nav.navbar-link-text, .nav.navbar-link-text > li > a font-weight: normal font-size: 25px diff --git a/app/templates/account/job_profile.jade b/app/templates/account/job_profile.jade index 491ea8b9c..6ec836343 100644 --- a/app/templates/account/job_profile.jade +++ b/app/templates/account/job_profile.jade @@ -1,8 +1,28 @@ h3(data-i18n="account_settings.job_profile") Job Profile -if me.get('jobProfileApproved') - p.lead(data-i18n="account_settings.job_profile_approved") Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks. -else - p.lead(data-i18n="account_settings.job_profile_explanation") Hi! Fill this out, and we will get in touch about finding you a software developer job. +.row + .col-md-9 + if me.get('jobProfileApproved') + p.lead(data-i18n="account_settings.job_profile_approved") Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks. + else + p.lead(data-i18n="account_settings.job_profile_explanation") Hi! Fill this out, and we will get in touch about finding you a software developer job. -#job-profile-treema \ No newline at end of file + .row + .col-md-9 + .progress.profile-completion-progress + .progress-bar.progress-bar-success + .progress-next-item + + .col-md-3 + a.btn.btn-large.btn-primary.profile-preview-button.top-preview(href="/account/profile/#{me.id}", target="job_profile", data-i18n="account_settings.view_profile") View Your Profile + + .col-md-3 + .thumbnail.sample-profile-thumbnail + a(href="http://codecombat.com/images/pages/account/profile/sample_profile.png", target="_blank") + img(src="/images/pages/account/profile/sample_profile_thumb.png" alt="Sample Profile Thumbnail") + .caption + span(data-i18n="account_settings.sample_profile") See a sample profile + +#job-profile-treema + +a.btn.btn-large.btn-primary.profile-preview-button.bottom-preview(href="/account/profile/#{me.id}", target="job_profile", data-i18n="account_settings.view_profile") View Your Profile diff --git a/app/templates/account/profile.jade b/app/templates/account/profile.jade index 2e0921f7f..2726f456a 100644 --- a/app/templates/account/profile.jade +++ b/app/templates/account/profile.jade @@ -5,7 +5,7 @@ block content if myProfile || (me.isAdmin() && user.get('jobProfile')) .profile-control-bar if myProfile - a(href="/account/settings") + a(href=user.get('jobProfile') ? "/account/settings#job-profile" : "/account/settings") button.btn.edit-settings-button i.icon-cog span(data-i18n="account_profile.edit_settings") Edit Settings @@ -28,12 +28,13 @@ block content if profileLinks.length ul.links each link in profileLinks - li(title=profile.name + " on " + link.name, class=link.icon ? "has-icon" : "") - a(href=link.link) - if link.icon - img(src=link.icon.url, alt=link.icon.name) - else - button.btn.btn-large.btn-inverse.flat-button= link.name + if link.link && link.name + li(title=profile.name + " on " + link.name, class=link.icon ? "has-icon" : "") + a(href=link.link) + if link.icon + img(src=link.icon.url, alt=link.icon.name) + else + button.btn.btn-large.btn-inverse.flat-button= link.name div= profile.city + ', ' + profile.country div= profile.visa @@ -50,40 +51,46 @@ block content .middle-column.full-height-column .sub-column - h3= profile.name - p= profile.shortDescription + h3= profile.name || "Anonymous Developer" + if profile.shortDescription + p= profile.shortDescription each skill in profile.skills code= skill span - div.long-description!= marked(profile.longDescription) + if profile.longDescription + div.long-description!= marked(profile.longDescription) if profile.work.length h3.experience-header img.header-icon(src="/images/pages/account/profile/work.png", alt="") span(data-i18n="account_profile.work_experience") Work Experience each job in profile.work - div.experience-entry - div.duration.pull-right= job.duration - | #{job.role} at #{job.employer} - .clearfix - if job.description - div!= marked(job.description) + if job.role && job.employer + div.experience-entry + div.duration.pull-right= job.duration + | #{job.role} at #{job.employer} + .clearfix + if job.description + div!= marked(job.description) if profile.education.length h3.experience-header img.header-icon(src="/images/pages/account/profile/education.png", alt="") span(data-i18n="account_profile.education") Education each school in profile.education - div.experience-entry - div.duration.pull-right= school.duration - | #{school.degree} at #{school.school} - .clearfix + if school.degree && school.school + div.experience-entry + div.duration.pull-right= school.duration + | #{school.degree} at #{school.school} + .clearfix + if school.description + div!= marked(school.description) if user.get('jobProfileNotes') || me.isAdmin() h3.experience-header(data-i18n="account_profile.our_notes") Our Notes - var notes = user.get('jobProfileNotes') || ''; - if me.isAdmin() + if !me.isAdmin() textarea#job-profile-notes!= notes else div!= marked(notes) @@ -94,12 +101,13 @@ block content h3(data-i18n="account_profile.projects") Projects ul.projects each project in profile.projects - li - a(href=project.link) - if project.picture - .project-image(style="background-image: url(/file/" + project.picture + ")") - p= project.name - div!= marked(project.description) + if project.name + li + a(href=project.link) + if project.picture + .project-image(style="background-image: url(/file/" + project.picture + ")") + p= project.name + div!= marked(project.description) else .public-profile-container diff --git a/app/templates/account/settings.jade b/app/templates/account/settings.jade index a1e829b7e..96179d5ba 100644 --- a/app/templates/account/settings.jade +++ b/app/templates/account/settings.jade @@ -39,7 +39,7 @@ block content if !isProduction .form-group.checkbox label(for="email", data-i18n="account_settings.admin") Admin - input#admin(name="admin", type="checkbox", checked=me.get('permissions').indexOf('admin')>-1)) + input#admin(name="admin", type="checkbox", checked=me.get('permissions').indexOf('admin') != -1) #picture-pane.tab-pane diff --git a/app/templates/base.jade b/app/templates/base.jade index 96d184876..6cb4a34f2 100644 --- a/app/templates/base.jade +++ b/app/templates/base.jade @@ -16,12 +16,8 @@ body ul.nav.navbar-nav li.play a.header-font(href='/play', data-i18n="nav.play") Levels - li.editor - a.header-font(href='/editor', data-i18n="nav.editor") Editor - li.blog - a.header-font(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog - li.forum - a.header-font(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum + li + a.header-font(href='/community', data-i18n="nav.community") Community .nav.navbar.navbar-fixed-top#top-nav .content.clearfix @@ -34,9 +30,11 @@ body if me.get('anonymous') === false button.btn.btn-primary.navbuttontext.header-font#logout-button(data-i18n="login.log_out") Log Out a.btn.btn-primary.navbuttontext.header-font(href=me.get('jobProfile') ? "/account/profile/#{me.id}" : "/account/settings") - div.navbuttontext-user-name - | #{me.displayName()} - i.icon-cog.icon-white.big + div.navbuttontext-account(data-i18n="nav.account") Account + if me.get('photoURL') + img.account-settings-image(src=me.getPhotoURL(18), alt="") + else + span.glyphicon.glyphicon-user else button.btn.btn-primary.navbuttontext.header-font(data-toggle="coco-modal", data-target="modal/signup", data-i18n="login.sign_up") Create Account @@ -45,13 +43,8 @@ body ul(class='navbar-link-text').nav.navbar-nav.pull-right li.play a.header-font(href='/play', data-i18n="nav.play") Levels - li.editor - a.header-font(href='/editor', data-i18n="nav.editor") Editor - li.blog - a.header-font(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog - li.forum - a.header-font(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum - + li + a.header-font(href='/community', data-i18n="nav.community") Community block outer_content #outer-content-wrapper @@ -73,6 +66,9 @@ body a(href='/legal', title='Legal', tabindex=-1, data-i18n="nav.legal") Legal a(href='/about', title='About', tabindex=-1, data-i18n="nav.about") About a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact + a(href='/editor', data-i18n="nav.editor") Editor + a(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog + a(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum if me.isAdmin() a(href='/admin', data-i18n="nav.admin") Admin diff --git a/app/templates/community.jade b/app/templates/community.jade new file mode 100644 index 000000000..b2d7aabc0 --- /dev/null +++ b/app/templates/community.jade @@ -0,0 +1,35 @@ +extends /templates/base + +block content + + h1(data-i18n="community.main_title") CodeCombat Community + + h2 + a(href="/editor", data-i18n="community.level_editor") Level Editor + + h2 + a(href="http://blog.codecombat.com", data-i18n="nav.blog") Blog + + h2 + a(href="http://discourse.codecombat.com", data-i18n="nav.forum") Forum + + h2 + a(href="/contribute", data-i18n="nav.contribute") Contribute + + h2 + a(href="https://www.facebook.com/codecombat", data-i18n="community.facebook") Facebook + + h2 + a(href="https://twitter.com/CodeCombat", data-i18n="community.twitter") Twitter + + h2 + a(href="https://plus.google.com/115285980638641924488/posts", data-i18n="community.gplus") Google+ + + h2 + a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="community.contact_us") Contact us! + + p + span(data-i18n="editor.hipchat_prefix") You can also find us in our + | + strong + a(href="http://www.hipchat.com/g3plnOKqa", data-i18n="editor.hipchat_url") HipChat room. \ No newline at end of file diff --git a/app/templates/editor.jade b/app/templates/editor.jade index c92140c29..82101e03f 100644 --- a/app/templates/editor.jade +++ b/app/templates/editor.jade @@ -34,11 +34,9 @@ block content hr p - span(data-i18n="editor.security_notice") - | Many major features in these editors are not currently enabled by default. - | As we improve the security of these systems, they will be made generally available. - | If you'd like to use these features sooner, - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="editor.contact_us") email us! + span(data-i18n="editor.got_questions") Questions about using the CodeCombat editors? + | + a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="editor.contact_us") Contact us! | span(data-i18n="editor.hipchat_prefix") You can also find us in our | diff --git a/app/views/account/job_profile_view.coffee b/app/views/account/job_profile_view.coffee index 8a9fdc799..0d32cd799 100644 --- a/app/views/account/job_profile_view.coffee +++ b/app/views/account/job_profile_view.coffee @@ -21,7 +21,6 @@ module.exports = class JobProfileView extends CocoView visibleSettings = @editableSettings.concat @readOnlySettings data = _.pick (me.get('jobProfile') ? {}), (value, key) => key in visibleSettings data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName') - console.log 'schema?', me.schema() schema = _.cloneDeep me.schema().properties.jobProfile schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings schema.required = _.intersection schema.required, visibleSettings @@ -42,10 +41,56 @@ module.exports = class JobProfileView extends CocoView @jobProfileTreema = @$el.find('#job-profile-treema').treema treemaOptions @jobProfileTreema.build() @jobProfileTreema.open() + @updateProgress() onJobProfileChanged: (e) => @hasEditedProfile = true @trigger 'change' + @updateProgress() + + updateProgress: -> + completed = 0 + totalWeight = 0 + next = null + for metric in metrics = @getProgressMetrics() + done = metric.fn() + completed += metric.weight ? 1 if done + totalWeight += metric.weight + next = metric.name unless next or done + progress = Math.round 100 * completed / totalWeight + bar = @$el.find('.profile-completion-progress .progress-bar') + bar.css 'width', "#{progress}%" + text = "" + if progress > 19 + text = "#{progress}% complete" + else if progress > 5 + text = "#{progress}%" + bar.text text + bar.parent().toggle Boolean progress + @$el.find('.progress-next-item').text(next).toggle Boolean next + + getProgressMetrics: -> + return @progressMetrics if @progressMetrics + schema = me.schema().properties.jobProfile + jobProfile = @jobProfileTreema.data + exists = (field) -> -> jobProfile[field] + modified = (field) -> -> jobProfile[field] and jobProfile[field] isnt schema.properties[field].default + listStarted = (field, subfields) -> -> jobProfile[field]?.length and _.every subfields, (subfield) -> jobProfile[field][0][subfield] + @progressMetrics = [ + {name: "Mark yourself open to offers to show up in searches.", weight: 1, fn: modified 'active'} + {name: "Specify your desired job title.", weight: 0, fn: exists 'jobTitle'} + {name: "Provide your name.", weight: 1, fn: modified 'name'} + {name: "Choose your city.", weight: 1, fn: modified 'city'} + {name: "Pick your country.", weight: 0, fn: exists 'country'} + {name: "List at least five skills.", weight: 2, fn: -> jobProfile.skills.length >= 5} + {name: "Write a short description to summarize yourself at a glance.", weight: 2, fn: modified 'shortDescription'} + {name: "Fill in your main description to sell yourself and describe the work you're looking for.", weight: 3, fn: modified 'longDescription'} + {name: "List your work experience.", weight: 3, fn: listStarted 'work', ['role', 'employer']} + {name: "Recount your educational ordeals.", weight: 3, fn: listStarted 'education', ['degree', 'school']} + {name: "Show off up to three projects you've worked on.", weight: 3, fn: listStarted 'projects', ['name']} + {name: "Add any personal or social links.", weight: 2, fn: listStarted 'links', ['link', 'name']} + {name: "Add an optional professional photo.", weight: 2, fn: modified 'photoURL'} + ] getData: -> return {} unless me.get('jobProfile') or @hasEditedProfile diff --git a/app/views/account/settings_view.coffee b/app/views/account/settings_view.coffee index a9674c919..16ea6b059 100644 --- a/app/views/account/settings_view.coffee +++ b/app/views/account/settings_view.coffee @@ -3,6 +3,7 @@ template = require 'templates/account/settings' {me} = require('lib/auth') forms = require('lib/forms') User = require('models/User') +LoginModalView = require 'views/modal/login_modal' WizardSettingsView = require './wizard_settings_view' JobProfileView = require './job_profile_view' @@ -45,6 +46,11 @@ module.exports = class SettingsView extends View @insertSubView @jobProfileView _.defer => @buildPictureTreema() # Not sure why, but the Treemas don't fully build without this if you reload the page. + afterInsert: -> + super() + if me.get('anonymous') + @openModalView new LoginModalView() + chooseTab: (category) -> id = "##{category}-pane" pane = $(id, @$el) diff --git a/app/views/community_view.coffee b/app/views/community_view.coffee new file mode 100644 index 000000000..8c9ae43df --- /dev/null +++ b/app/views/community_view.coffee @@ -0,0 +1,6 @@ +View = require 'views/kinds/RootView' +template = require 'templates/community' + +module.exports = class CommunityView extends View + id: "communit-view" + template: template diff --git a/app/views/modal/model_modal.coffee b/app/views/modal/model_modal.coffee index 4f56855e4..fc7922320 100644 --- a/app/views/modal/model_modal.coffee +++ b/app/views/modal/model_modal.coffee @@ -26,7 +26,7 @@ module.exports = class ModelModal extends View treemaOptions = schema: schema data: data - readOnly: true + readOnly: false modelTreema = @$el.find(".model-treema[data-model-id='#{model.id}']").treema treemaOptions modelTreema?.build() modelTreema?.open() diff --git a/app/views/play/ladder/ladder_tab.coffee b/app/views/play/ladder/ladder_tab.coffee index 1cbefb4e0..cc88ed474 100644 --- a/app/views/play/ladder/ladder_tab.coffee +++ b/app/views/play/ladder/ladder_tab.coffee @@ -46,7 +46,7 @@ module.exports = class LadderTabView extends CocoView return if @checked or (not window.FB) or (not window.gapi) @checked = true - @addSomethingToLoad("facebook_status") + @addSomethingToLoad("facebook_status", 0) # This might not load ever, so we can't wait for it FB.getLoginStatus (response) => @facebookStatus = response.status @loadFacebookFriends() if @facebookStatus is 'connected' @@ -105,7 +105,7 @@ module.exports = class LadderTabView extends CocoView gplusSessionStateLoaded: -> if application.gplusHandler.loggedIn - @addSomethingToLoad("gplus_friends", 0) # this might not load ever, so we can't wait for it + @addSomethingToLoad("gplus_friends", 0) # This might not load ever, so we can't wait for it application.gplusHandler.loadFriends @gplusFriendsLoaded gplusFriendsLoaded: (friends) => diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index 8a3da57e2..f13c11c8c 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -14,7 +14,7 @@ LevelSessionHandler = require '../levels/sessions/level_session_handler' serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset'] privateProperties = [ 'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID', - 'gplusID', 'music', 'volume', 'aceConfig' + 'gplusID', 'music', 'volume', 'aceConfig', 'employerAt' ] candidateProperties = [ 'jobProfile', 'jobProfileApproved', 'jobProfileNotes' @@ -47,7 +47,7 @@ UserHandler = class UserHandler extends Handler delete obj[prop] for prop in serverProperties includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id)) delete obj[prop] for prop in privateProperties unless includePrivates - includeCandidate = includePrivates or (obj.jobProfileApproved and req.user and ('employer' in (req.user.permissions ? []))) + includeCandidate = includePrivates or (obj.jobProfileApproved and req.user and ('employer' in (req.user.permissions ? [])) and @employerCanViewCandidate req.user, obj) delete obj[prop] for prop in candidateProperties unless includeCandidate return obj @@ -259,6 +259,7 @@ UserHandler = class UserHandler extends Handler authorized = req.user.isAdmin() or ('employer' in req.user.get('permissions')) since = (new Date((new Date()) - 2 * 30.4 * 86400 * 1000)).toISOString() query = {'jobProfile.active': true, 'jobProfile.updated': {$gt: since}} + #query = {'jobProfile.updated': {$gt: since}} query.jobProfileApproved = true unless req.user.isAdmin() selection = 'jobProfile' selection += ' email' if authorized @@ -266,6 +267,7 @@ UserHandler = class UserHandler extends Handler User.find(query).select(selection).exec (err, documents) => return @sendDatabaseError(res, err) if err candidates = (@formatCandidate(authorized, doc) for doc in documents) + candidates = (candidate for candidate in candidates when @employerCanViewCandidate req.user, candidate) @sendSuccess(res, candidates) formatCandidate: (authorized, document) -> @@ -278,6 +280,14 @@ UserHandler = class UserHandler extends Handler obj.jobProfile = _.pick obj.jobProfile, subfields obj + employerCanViewCandidate: (employer, candidate) -> + return true if employer.isAdmin() + for job in candidate.jobProfile?.work ? [] + # TODO: be smarter about different ways to write same company names to ensure privacy. + # We'll have to manually pay attention to how we set employer names for now. + return false if job.employer?.toLowerCase() is employer.get('employerAt')?.toLowerCase() + true + buildGravatarURL: (user, size, fallback) -> emailHash = @buildEmailHash user fallback ?= "http://codecombat.com/file/db/thang.type/52a00d55cf1818f2be00000b/portrait.png"