mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-14 01:31:15 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
9ed201c910
26 changed files with 106 additions and 521 deletions
|
@ -1037,6 +1037,7 @@
|
|||
ambassador_title: "Ambassador"
|
||||
ambassador_title_description: "(Support)"
|
||||
ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world."
|
||||
teacher_title: "Teacher"
|
||||
|
||||
editor:
|
||||
main_title: "CodeCombat Editors"
|
||||
|
@ -1193,6 +1194,7 @@
|
|||
ambassador_join_note_strong: "Note"
|
||||
ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!"
|
||||
ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments."
|
||||
teacher_subscribe_desc: "Get emails on updates and announcements for teachers."
|
||||
changes_auto_save: "Changes are saved automatically when you toggle checkboxes."
|
||||
diligent_scribes: "Our Diligent Scribes:"
|
||||
powerful_archmages: "Our Powerful Archmages:"
|
||||
|
|
|
@ -79,6 +79,7 @@ _.extend UserSchema.properties,
|
|||
archmageNews: {$ref: '#/definitions/emailSubscription'}
|
||||
artisanNews: {$ref: '#/definitions/emailSubscription'}
|
||||
diplomatNews: {$ref: '#/definitions/emailSubscription'}
|
||||
teacherNews: {$ref: '#/definitions/emailSubscription'}
|
||||
scribeNews: {$ref: '#/definitions/emailSubscription'}
|
||||
|
||||
# notifications
|
||||
|
|
|
@ -1,344 +0,0 @@
|
|||
@import "app/styles/bootstrap/variables"
|
||||
|
||||
#profile-view
|
||||
$sideBackground: rgb(220, 220, 220)
|
||||
#login-message
|
||||
h1, h2, h3, h4
|
||||
font-family: Arial, Helvetica, sans-serif
|
||||
color: #333333
|
||||
width: 100%
|
||||
text-align: center
|
||||
margin-top: 200px
|
||||
.profile-control-bar
|
||||
background-color: $sideBackground
|
||||
width: 100%
|
||||
text-align: center
|
||||
|
||||
.profile-completion-progress
|
||||
width: 100%
|
||||
height: 33px
|
||||
margin-bottom: 0
|
||||
border-radius: 0
|
||||
background-color: darken($sideBackground, 15%)
|
||||
|
||||
.progress-bar
|
||||
line-height: 33px
|
||||
font-size: 16px
|
||||
|
||||
.progress-text
|
||||
position: absolute
|
||||
width: 100%
|
||||
text-align: center
|
||||
line-height: 33px
|
||||
font-size: 16px
|
||||
color: white
|
||||
text-shadow: 0px 1px 0px black
|
||||
|
||||
button, a.btn
|
||||
margin: 10px 2px 10px 2px
|
||||
|
||||
&:disabled
|
||||
border-radius: 0
|
||||
opacity: 1
|
||||
|
||||
i
|
||||
margin-right: 5px
|
||||
|
||||
.sample-profile
|
||||
position: absolute
|
||||
right: 5px
|
||||
|
||||
.main-content-area
|
||||
padding: 0
|
||||
background-color: white
|
||||
|
||||
.flat-button
|
||||
width: 100%
|
||||
margin-bottom: 10px
|
||||
background: rgb(78, 78, 78)
|
||||
border: 0
|
||||
border-radius: 0
|
||||
padding: 10px
|
||||
|
||||
.public-profile-container
|
||||
padding: 20px
|
||||
|
||||
img.profile-photo
|
||||
width: 256px
|
||||
border-radius: 6px
|
||||
|
||||
.job-profile-container
|
||||
width: 100%
|
||||
height: 100%
|
||||
min-height: 600px
|
||||
padding: 0
|
||||
display: table
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||
color: #555
|
||||
|
||||
ul.links, ul.projects, ul.sessions
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
li
|
||||
list-style: none
|
||||
|
||||
.job-profile-row
|
||||
height: 100%
|
||||
display: table-row
|
||||
$side-width: 250px
|
||||
$side-padding: 5px
|
||||
$middle-width: 524px
|
||||
$middle-padding: 20px
|
||||
|
||||
.full-height-column
|
||||
height: 100%
|
||||
padding: $side-padding
|
||||
display: table-cell
|
||||
vertical-align: top
|
||||
|
||||
h3:first-child
|
||||
margin: 5px 0 5px 0
|
||||
|
||||
.left-column
|
||||
width: $side-width - 2 * $side-padding
|
||||
padding: $side-padding
|
||||
background-color: $sideBackground
|
||||
|
||||
.sub-column
|
||||
width: $side-width - 2 * $side-padding
|
||||
overflow-wrap: break-word
|
||||
|
||||
#profile-photo-container
|
||||
position: relative
|
||||
margin-bottom: 10px
|
||||
|
||||
img.profile-photo
|
||||
width: $side-width - 2 * $side-padding
|
||||
border-radius: 6px
|
||||
|
||||
.profile-caption
|
||||
background-color: rgba(0, 0, 0, 0.5)
|
||||
color: white
|
||||
border-bottom-right-radius: 6px
|
||||
border-bottom-left-radius: 6px
|
||||
position: absolute
|
||||
width: 100%
|
||||
bottom: 0px
|
||||
text-align: center
|
||||
|
||||
ul.links
|
||||
text-align: center
|
||||
li.has-icon
|
||||
display: inline-block
|
||||
img
|
||||
margin: 0 0 10px 0
|
||||
li.has-icon:not(:nth-child(5))
|
||||
img
|
||||
margin: 0 5px 10px 5px
|
||||
|
||||
#contact-candidate
|
||||
margin-top: 20px
|
||||
background-color: rgb(177, 55, 25)
|
||||
padding: 15px
|
||||
font-size: 20px
|
||||
|
||||
.middle-column
|
||||
width: $middle-width - 2 * $middle-padding
|
||||
padding-left: $middle-padding
|
||||
padding-right: $middle-padding
|
||||
background-color: white
|
||||
|
||||
.sub-column
|
||||
width: $middle-width - 2 * $middle-padding
|
||||
overflow-wrap: break-word
|
||||
|
||||
&.double-column
|
||||
width: $middle-width + $side-width + 2 * $side-padding - 2 * $middle-padding
|
||||
$middle-padding-double: 30px
|
||||
padding-left: $middle-padding-double
|
||||
padding-right: $middle-padding-double
|
||||
|
||||
.sub-column
|
||||
width: $middle-width + $side-width + 2 * $side-padding - 2 * $middle-padding - 2 * $middle-padding-double
|
||||
overflow-wrap: break-word
|
||||
|
||||
code
|
||||
background-color: $sideBackground
|
||||
color: #555
|
||||
margin: 2px 0
|
||||
display: inline-block
|
||||
text-transform: lowercase
|
||||
|
||||
.long-description
|
||||
margin-top: 10px
|
||||
img
|
||||
max-width: 524px - 60px
|
||||
max-height: 200px
|
||||
|
||||
.experience-header
|
||||
margin-top: 25px
|
||||
|
||||
.header-icon
|
||||
margin-right: 10px
|
||||
width: 32px
|
||||
height: 32px
|
||||
|
||||
.experience-entry
|
||||
margin-bottom: 15px
|
||||
|
||||
.duration
|
||||
margin-left: 10px
|
||||
margin-bottom: 10px
|
||||
|
||||
#job-profile-notes
|
||||
width: 100%
|
||||
height: 100px
|
||||
|
||||
#remark-treema
|
||||
background-color: white
|
||||
border: 0
|
||||
padding-top: 0
|
||||
|
||||
.right-column
|
||||
width: $side-width
|
||||
background-color: $sideBackground
|
||||
|
||||
.sub-column
|
||||
width: $side-width - 2 * $side-padding
|
||||
overflow-wrap: break-word
|
||||
|
||||
> h3:first-child
|
||||
background-color: white
|
||||
padding: 5px 5px
|
||||
margin: 5px 2px 5px 2px
|
||||
|
||||
ul.projects
|
||||
li
|
||||
margin-bottom: 10px
|
||||
padding: 5px 3px
|
||||
border: 2px solid $sideBackground
|
||||
transition: .5s ease-in-out
|
||||
position: relative
|
||||
background-color: white
|
||||
|
||||
&:hover
|
||||
border-color: rgb(100, 130, 255)
|
||||
|
||||
a
|
||||
position: relative
|
||||
z-index: 2
|
||||
|
||||
> a
|
||||
position: absolute
|
||||
width: 100%
|
||||
height: 100%
|
||||
top: 0
|
||||
left: 0
|
||||
z-index: 1
|
||||
|
||||
.project-image
|
||||
width: 230px
|
||||
height: 115px
|
||||
background-size: cover
|
||||
background-repeat: no-repeat
|
||||
background-position: center
|
||||
|
||||
-webkit-filter: grayscale(100%)
|
||||
-webkit-transition: .5s ease-in-out
|
||||
-moz-filter: grayscale(100%)
|
||||
-moz-transition: .5s ease-in-out
|
||||
-o-filter: grayscale(100%)
|
||||
-o-transition: .5s ease-in-out
|
||||
filter: grayscale(100%)
|
||||
transition: .5s ease-in-out
|
||||
|
||||
ul.projects li:hover .project-image, .project-image:hover
|
||||
-webkit-filter: grayscale(0%)
|
||||
-moz-filter: grayscale(0%)
|
||||
-o-filter: grayscale(0%)
|
||||
filter: grayscale(0%)
|
||||
|
||||
.main-content-area
|
||||
|
||||
.job-profile-container
|
||||
.editable-section
|
||||
position: relative
|
||||
transition: box-shadow 0.5s easeInOutQuad
|
||||
min-height: 30px
|
||||
|
||||
&.just-saved
|
||||
box-shadow: 0px 0px 20px 0px #080
|
||||
z-index: 1
|
||||
|
||||
.editable-form
|
||||
display: none
|
||||
background-color: white
|
||||
padding: 5px 5px 5px 5px
|
||||
|
||||
.skill-array-item
|
||||
display: inline-block
|
||||
|
||||
input
|
||||
width: 120px
|
||||
margin: 5px
|
||||
|
||||
.project-image
|
||||
width: 210px
|
||||
height: 105px
|
||||
cursor: pointer
|
||||
|
||||
.editable-icon
|
||||
display: none
|
||||
|
||||
.job-profile-container.editable-profile
|
||||
|
||||
.full-height-column.deemphasized
|
||||
background-color: $sideBackground
|
||||
|
||||
.saving
|
||||
opacity: 0.75
|
||||
|
||||
.editable-thinner
|
||||
padding-right: 30px
|
||||
|
||||
.editable-icon
|
||||
display: block
|
||||
position: absolute
|
||||
right: 5px
|
||||
top: 5px
|
||||
font-size: 20px
|
||||
color: $blue
|
||||
opacity: 0.5
|
||||
|
||||
.edit-label
|
||||
color: $blue
|
||||
font-weight: 300
|
||||
|
||||
.edit-example-button
|
||||
background-color: transparentize($blue, 0.25)
|
||||
|
||||
.edit-example-text
|
||||
color: $blue
|
||||
|
||||
code.edit-example-tag
|
||||
color: $blue
|
||||
|
||||
.emphasized
|
||||
outline: 1px solid $green
|
||||
|
||||
.editable-section.deemphasized:not(.just-saved), .our-notes-section.deemphasized
|
||||
opacity: 0.5
|
||||
|
||||
.editable-section:hover
|
||||
cursor: pointer
|
||||
outline: 1px solid $blue
|
||||
|
||||
.editable-icon
|
||||
opacity: 1.0
|
||||
cursor: pointer
|
||||
|
||||
.editable-form
|
||||
cursor: default
|
|
@ -1,74 +0,0 @@
|
|||
#admin-candidates-view
|
||||
|
||||
h1, h2, h3
|
||||
font: Arial
|
||||
|
||||
.see-candidates-header
|
||||
margin: 30px
|
||||
text-align: center
|
||||
|
||||
#see-candidates
|
||||
cursor: pointer
|
||||
|
||||
.employer_icon
|
||||
width: 125px
|
||||
float: left
|
||||
margin: 0px 15px 15px 0px
|
||||
|
||||
.information_row
|
||||
height: 150px
|
||||
padding-right: 15px
|
||||
|
||||
#leftside
|
||||
width: 500px
|
||||
float: left
|
||||
|
||||
#rightside
|
||||
width: 500px
|
||||
float: left
|
||||
|
||||
.tablesorter
|
||||
//img
|
||||
// display: none
|
||||
|
||||
.tablesorter-header
|
||||
cursor: pointer
|
||||
&:hover
|
||||
color: black
|
||||
|
||||
&:first-child
|
||||
// Make sure that "Developer #56" doesn't wrap onto second row
|
||||
min-width: 110px
|
||||
|
||||
.tablesorter-headerAsc
|
||||
background-color: #cfc
|
||||
|
||||
.tablesorter-headerDesc
|
||||
background-color: #ccf
|
||||
|
||||
tr
|
||||
cursor: pointer
|
||||
|
||||
tr.expired
|
||||
opacity: 0.5
|
||||
|
||||
code
|
||||
background-color: rgb(220, 220, 220)
|
||||
color: #555
|
||||
margin: 2px 0
|
||||
display: inline-block
|
||||
text-transform: lowercase
|
||||
|
||||
td:nth-child(3) select
|
||||
min-width: 100px
|
||||
td:nth-child(6) select
|
||||
min-width: 50px
|
||||
td:nth-child(7) select
|
||||
min-width: 100px
|
||||
|
||||
#employers-view, #profile-view.viewed-by-employer
|
||||
#outer-content-wrapper, #intermediate-content-wrapper, #inner-content-wrapper
|
||||
background: #949494
|
||||
|
||||
.main-content-area
|
||||
background-color: #EAEAEA
|
|
@ -18,9 +18,20 @@
|
|||
.name-row
|
||||
@extend .body-row
|
||||
max-width: 300px
|
||||
.description-row
|
||||
@extend .body-row
|
||||
max-width: 520px
|
||||
.small-name-row
|
||||
@extend .body-row
|
||||
max-width: 200px
|
||||
.watch-row
|
||||
@extend .body-row
|
||||
max-width: 80px
|
||||
text-align: center
|
||||
&.watching
|
||||
opacity: 1.0
|
||||
&.not-watching
|
||||
opacity: 0.5
|
||||
|
||||
tr.mine
|
||||
background-color: #f8ecaa
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@import "app/styles/mixins"
|
||||
@import "app/styles/bootstrap/variables"
|
||||
|
||||
#ladder-home-view
|
||||
#main-ladder-view
|
||||
.level
|
||||
width: 100%
|
||||
position: relative
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
@import "app/styles/mixins"
|
||||
@import "app/styles/bootstrap/variables"
|
||||
|
||||
#employers-wrapper
|
||||
background-color: #B4B4B4
|
||||
height: 100%
|
||||
#outer-content-wrapper, #intermediate-content-wrapper, #inner-content-wrapper
|
||||
background: #B4B4B4
|
||||
|
||||
.navbar, #top-nav, .content.clearfix
|
||||
background-color: #B4B4B4
|
||||
|
||||
.footer
|
||||
border-top: none
|
||||
background-color: #B4B4B4
|
||||
padding-bottom: 50px
|
||||
|
||||
|
||||
#employer-content-area
|
||||
margin: auto
|
||||
|
||||
.employer-modal-background-wrapper
|
||||
background-color: white
|
||||
border: 2px #333333 solid
|
||||
border-radius: 4px
|
||||
h1, h2, h3, h4, h5
|
||||
color: black
|
||||
font-family: Arial, Helvetica, sans-serif
|
||||
.input-large
|
||||
font-size: 28px
|
||||
height: 60px
|
||||
border: 2px rgb(231,231,231) solid
|
||||
box-shadow: none
|
||||
width: 70%
|
||||
margin-left: 15%
|
||||
#create-account-button, #contract-agreement-button, #login-button
|
||||
background: #fce232 /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #fce232 0%, #ea8e2b 100%)
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fce232), color-stop(100%,#ea8e2b))
|
||||
background: -webkit-linear-gradient(top, #fce232 0%,#ea8e2b 100%)
|
||||
background: -o-linear-gradient(top, #fce232 0%,#ea8e2b 100%)
|
||||
background: -ms-linear-gradient(top, #fce232 0%,#ea8e2b 100%)
|
||||
background: linear-gradient(to bottom, #fce232 0%,#ea8e2b 100%)
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fce232', endColorstr='#ea8e2b',GradientType=0 )
|
||||
height: 60px
|
||||
font-size: 24px
|
||||
color: black
|
||||
.login-link
|
||||
text-decoration: underline
|
||||
#login-button
|
||||
margin-left: 40%
|
||||
width: 20%
|
|
@ -159,7 +159,7 @@ else
|
|||
| (Translator)
|
||||
input#email_diplomatNews(name="email_diplomatNews", type="checkbox", checked=subs.diplomatNews)
|
||||
span(data-i18n="contribute.diplomat_subscribe_desc").help-block Get emails about i18n developments and, eventually, levels to translate.
|
||||
|
||||
|
||||
.form-group.checkbox
|
||||
label.control-label(for="email_ambassadorNews")
|
||||
span(data-i18n="classes.ambassador_title")
|
||||
|
@ -169,7 +169,13 @@ else
|
|||
| (Support)
|
||||
input#email_ambassadorNews(name="email_ambassadorNews", type="checkbox", checked=subs.ambassadorNews)
|
||||
span(data-i18n="contribute.ambassador_subscribe_desc").help-block Get emails on support updates and multiplayer developments.
|
||||
|
||||
|
||||
.form-group.checkbox
|
||||
label.control-label(for="email_teacherNews")
|
||||
span(data-i18n="classes.teacher_title")
|
||||
input#email_teacherNews(name="email_teacherNews", type="checkbox", checked=subs.teacherNews)
|
||||
span(data-i18n="contribute.teacher_subscribe_desc").help-block
|
||||
|
||||
button#toggle-all-btn.btn.btn-primary.form-control(data-i18n="account_settings.email_toggle") Toggle All
|
||||
|
||||
.panel.panel-default
|
||||
|
|
|
@ -1 +1,35 @@
|
|||
extends /templates/common/table
|
||||
|
||||
block tableResultsHeader
|
||||
tr
|
||||
th(colspan=4)
|
||||
span(data-i18n="general.results")
|
||||
| Results
|
||||
span
|
||||
|: #{documents.length}
|
||||
|
||||
block tableHeader
|
||||
tr
|
||||
th(data-i18n="general.name") Name
|
||||
th(data-i18n="general.description") Description
|
||||
th(data-i18n="general.version") Version
|
||||
th(data-i18n="common.watch") Watch
|
||||
|
||||
block tableBody
|
||||
for document in documents
|
||||
- var data = document.attributes;
|
||||
tr(class=document.get('creator') == me.id ? 'mine' : '')
|
||||
td(title=data.name).name-row
|
||||
a(href="/editor/#{page}/#{data.slug || data._id}")
|
||||
| #{data.name}
|
||||
td(title=data.description).description-row
|
||||
| #{data.description}
|
||||
td #{data.version.major}.#{data.version.minor}
|
||||
if document.watching()
|
||||
td.watch-row.watching
|
||||
span(aria-hidden="true").glyphicon.glyphicon-eye-open
|
||||
span(data-i18n="common.watch").sr-only Watch
|
||||
else
|
||||
td.watch-row.not-watching
|
||||
span(aria-hidden="true").glyphicon.glyphicon-eye-close
|
||||
span(data-i18n="common.unwatch").sr-only Unwatch
|
|
@ -1,21 +0,0 @@
|
|||
body
|
||||
#fb-root
|
||||
#employers-wrapper
|
||||
block header
|
||||
.nav
|
||||
.content.clearfix
|
||||
.navbar-header
|
||||
a.navbar-brand(href='/')
|
||||
img(src="/images/pages/base/recruitment_logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat")
|
||||
block outer_content
|
||||
#outer-content-wrapper
|
||||
|
||||
#intermediate-content-wrapper
|
||||
|
||||
#inner-content-wrapper
|
||||
.main-content-area#employer-content-area
|
||||
block content
|
||||
p If this is showing, you dun goofed
|
||||
|
||||
block footer
|
||||
.footer
|
|
@ -7,7 +7,7 @@ class PendingPatchesCollection extends CocoCollection
|
|||
url: '/db/patch?view=pending'
|
||||
model: Patch
|
||||
|
||||
module.exports = class PatchesView extends RootView
|
||||
module.exports = class PendingPatchesView extends RootView
|
||||
id: 'pending-patches-view'
|
||||
template: template
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ Classroom = require 'models/Classroom'
|
|||
ModalView = require 'views/core/ModalView'
|
||||
template = require 'templates/courses/classroom-settings-modal'
|
||||
|
||||
module.exports = class AddLevelSystemModal extends ModalView
|
||||
module.exports = class ClassroomSettingsModal extends ModalView
|
||||
id: 'classroom-settings-modal'
|
||||
template: template
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ auth = require 'core/auth'
|
|||
forms = require 'core/forms'
|
||||
User = require 'models/User'
|
||||
|
||||
module.exports = class StudentSignInModal extends ModalView
|
||||
module.exports = class StudentLogInModal extends ModalView
|
||||
id: 'student-log-in-modal'
|
||||
template: template
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ module.exports = class LevelSearchView extends SearchView
|
|||
model: require 'models/Level'
|
||||
modelURL: '/db/level'
|
||||
tableTemplate: require 'templates/editor/level/table'
|
||||
projection: ['slug', 'name', 'description', 'version', 'watchers', 'creator']
|
||||
page: 'level'
|
||||
|
||||
getRenderData: ->
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
I18NEditModelView = require './I18NEditModelView'
|
||||
ThangType = require 'models/ThangType'
|
||||
|
||||
module.exports = class ThangTypeI18NView extends I18NEditModelView
|
||||
id: 'thang-type-i18n-view'
|
||||
module.exports = class I18NEditThangTypeView extends I18NEditModelView
|
||||
id: 'i18n-thang-type-view'
|
||||
modelClass: ThangType
|
||||
|
||||
buildTranslationList: ->
|
||||
|
|
|
@ -11,8 +11,8 @@ class LevelSessionsCollection extends CocoCollection
|
|||
super()
|
||||
@url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID"
|
||||
|
||||
module.exports = class LadderHomeView extends RootView
|
||||
id: 'ladder-home-view'
|
||||
module.exports = class MainLadderView extends RootView
|
||||
id: 'main-ladder-view'
|
||||
template: template
|
||||
|
||||
constructor: (options) ->
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
AuthModal = require 'views/core/AuthModal'
|
||||
|
||||
module.exports = class SignupModalView extends AuthModal
|
||||
mode: 'signup'
|
|
@ -2,7 +2,7 @@ ModalView = require 'views/core/ModalView'
|
|||
template = require 'templates/play/modal/share-progress-modal'
|
||||
storage = require 'core/storage'
|
||||
|
||||
module.exports = class SubscribeModal extends ModalView
|
||||
module.exports = class ShareProgressModal extends ModalView
|
||||
id: 'share-progress-modal'
|
||||
template: template
|
||||
plain: true
|
||||
|
|
|
@ -4,6 +4,7 @@ config = require '../../server_config'
|
|||
plugins = require '../plugins/plugins'
|
||||
User = require '../users/User'
|
||||
jsonSchema = require '../../app/schemas/models/classroom.schema'
|
||||
utils = require '../lib/utils'
|
||||
|
||||
ClassroomSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
|
||||
|
||||
|
@ -18,12 +19,10 @@ ClassroomSchema.statics.editableProperties = [
|
|||
'aceConfig'
|
||||
]
|
||||
|
||||
# 250 words; will want to use 4 code words once we get past 10M classrooms.
|
||||
words = 'angry apple arm army art baby back bad bag ball bath bean bear bed bell best big bird bite blue boat book box boy bread burn bus cake car cat chair city class clock cloud coat coin cold cook cool corn crash cup dark day deep desk dish dog door down draw dream drink drop dry duck dust east eat egg enemy eye face false farm fast fear fight find fire flag floor fly food foot fork fox free fruit full fun funny game gate gift glass goat gold good green hair half hand happy heart heavy help hide hill home horse house ice idea iron jelly job jump key king lamp large last late lazy leaf left leg life light lion lock long luck map mean milk mix moon more most mouth music name neck net new next nice night north nose old only open page paint pan paper park party path pig pin pink place plane plant plate play point pool power pull push queen rain ready red rest rice ride right ring road rock room run sad safe salt same sand sell shake shape share sharp sheep shelf ship shirt shoe shop short show sick side silly sing sink sit size sky sleep slow small snow sock soft soup south space speed spell spoon star start step stone stop sweet swim sword table team thick thin thing think today tooth top town tree true turn type under want warm watch water west wide win word yes zoo'.split(' ')
|
||||
|
||||
ClassroomSchema.statics.generateNewCode = (done) ->
|
||||
tryCode = ->
|
||||
codeCamel = _.map(_.sample(words, 3), (s) -> s[0].toUpperCase() + s.slice(1)).join('')
|
||||
# Use 4 code words once we get past 10M classrooms
|
||||
codeCamel = utils.getCodeCamel(3)
|
||||
code = codeCamel.toLowerCase()
|
||||
Classroom.findOne code: code, (err, classroom) ->
|
||||
return done() if err
|
||||
|
@ -31,8 +30,6 @@ ClassroomSchema.statics.generateNewCode = (done) ->
|
|||
tryCode()
|
||||
tryCode()
|
||||
|
||||
#ClassroomSchema.plugin plugins.NamedPlugin
|
||||
|
||||
ClassroomSchema.pre('save', (next) ->
|
||||
return next() if @get('code')
|
||||
Classroom.generateNewCode (code, codeCamel) =>
|
||||
|
|
|
@ -3,6 +3,6 @@ config = require '../../server_config'
|
|||
module.exports.MAILCHIMP_LIST_ID = 'e9851239eb'
|
||||
module.exports.MAILCHIMP_GROUP_ID = '4529'
|
||||
|
||||
# these two need to be parallel
|
||||
module.exports.MAILCHIMP_GROUPS = ['Announcements', 'Adventurers', 'Artisans', 'Archmages', 'Scribes', 'Diplomats', 'Ambassadors']
|
||||
module.exports.NEWS_GROUPS = ['generalNews', 'adventurerNews', 'artisanNews', 'archmageNews', 'scribeNews', 'diplomatNews', 'ambassadorNews']
|
||||
# These two need to be parallel
|
||||
module.exports.MAILCHIMP_GROUPS = ['Announcements', 'Adventurers', 'Artisans', 'Archmages', 'Scribes', 'Diplomats', 'Ambassadors', 'Teachers']
|
||||
module.exports.NEWS_GROUPS = ['generalNews', 'adventurerNews', 'artisanNews', 'archmageNews', 'scribeNews', 'diplomatNews', 'ambassadorNews', 'teacherNews']
|
||||
|
|
|
@ -6,6 +6,11 @@ config = require '../../server_config'
|
|||
module.exports =
|
||||
isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24
|
||||
|
||||
getCodeCamel: (numWords=3) ->
|
||||
# 250 words
|
||||
words = 'angry apple arm army art baby back bad bag ball bath bean bear bed bell best big bird bite blue boat book box boy bread burn bus cake car cat chair city class clock cloud coat coin cold cook cool corn crash cup dark day deep desk dish dog door down draw dream drink drop dry duck dust east eat egg enemy eye face false farm fast fear fight find fire flag floor fly food foot fork fox free fruit full fun funny game gate gift glass goat gold good green hair half hand happy heart heavy help hide hill home horse house ice idea iron jelly job jump key king lamp large last late lazy leaf left leg life light lion lock long luck map mean milk mix moon more most mouth music name neck net new next nice night north nose old only open page paint pan paper park party path pig pin pink place plane plant plate play point pool power pull push queen rain ready red rest rice ride right ring road rock room run sad safe salt same sand sell shake shape share sharp sheep shelf ship shirt shoe shop short show sick side silly sing sink sit size sky sleep slow small snow sock soft soup south space speed spell spoon star start step stone stop sweet swim sword table team thick thin thing think today tooth top town tree true turn type under want warm watch water west wide win word yes zoo'.split(' ')
|
||||
_.map(_.sample(words, numWords), (s) -> s[0].toUpperCase() + s.slice(1)).join('')
|
||||
|
||||
objectIdFromTimestamp: (timestamp) ->
|
||||
# mongoDB ObjectId contains creation date in first 4 bytes
|
||||
# So, it can be used instead of a redundant created field
|
||||
|
|
|
@ -41,7 +41,7 @@ module.exports.formatSessionInformation = (session) ->
|
|||
shouldUpdateLastOpponentSubmitDateForLeague: session.shouldUpdateLastOpponentSubmitDateForLeague
|
||||
|
||||
module.exports.calculateSessionScores = (callback) ->
|
||||
sessionIDs = _.pluck @clientResponseObject.sessions, 'sessionID'
|
||||
sessionIDs = _.map @clientResponseObject.sessions, 'sessionID'
|
||||
async.map sessionIDs, retrieveOldSessionData.bind(@), (err, oldScores) =>
|
||||
if err? then return callback err, {error: 'There was an error retrieving the old scores'}
|
||||
try
|
||||
|
@ -151,7 +151,7 @@ module.exports.addMatchToSessionsAndUpdate = (newScoreObject, callback) ->
|
|||
#log.info "Match object computed, result: #{JSON.stringify(matchObject, null, 2)}"
|
||||
#log.info 'Writing match object to database...'
|
||||
#use bind with async to do the writes
|
||||
sessionIDs = _.pluck @clientResponseObject.sessions, 'sessionID'
|
||||
sessionIDs = _.map @clientResponseObject.sessions, 'sessionID'
|
||||
async.each sessionIDs, updateMatchesInSession.bind(@, matchObject), (err) ->
|
||||
callback err
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ errors = require '../commons/errors'
|
|||
languages = require '../routes/languages'
|
||||
sendwithus = require '../sendwithus'
|
||||
log = require 'winston'
|
||||
utils = require '../lib/utils'
|
||||
|
||||
module.exports.setup = (app) ->
|
||||
authentication.serializeUser((user, done) -> done(null, user._id))
|
||||
|
@ -101,7 +102,10 @@ module.exports.setup = (app) ->
|
|||
if not user
|
||||
return errors.notFound(res, [{message: 'not found', property: 'email'}])
|
||||
|
||||
user.set('passwordReset', Math.random().toString(36).slice(2, 7).toUpperCase())
|
||||
user.set('passwordReset', utils.getCodeCamel())
|
||||
emailContent = "<h3>Your temporary password: <b>#{user.get('passwordReset')}</b></h3>"
|
||||
emailContent += "<p>Reset your password at <a href=\"http://codecombat.com/account/settings\">http://codecombat.com/account/settings</a></p>"
|
||||
emailContent += "<p>Your old password cannot be retrieved.</p>"
|
||||
user.save (err) =>
|
||||
return errors.serverError(res) if err
|
||||
unless config.unittest
|
||||
|
@ -111,8 +115,8 @@ module.exports.setup = (app) ->
|
|||
address: req.body.email
|
||||
email_data:
|
||||
subject: 'CodeCombat Recovery Password'
|
||||
title: 'Recovery Password'
|
||||
content: "<p>Your CodeCombat recovery password for email #{req.body.email} is: #{user.get('passwordReset')}</p><p>Log in at <a href=\"http://codecombat.com/account/settings\">http://codecombat.com/account/settings</a> and change it.</p><p>Hope this helps!</p>"
|
||||
title: ''
|
||||
content: emailContent
|
||||
sendwithus.api.send context, (err, result) ->
|
||||
if err
|
||||
console.error "Error sending password reset email: #{err.message or err}"
|
||||
|
|
|
@ -5,6 +5,7 @@ hipchat = require '../hipchat'
|
|||
sendwithus = require '../sendwithus'
|
||||
Prepaid = require '../prepaids/Prepaid'
|
||||
jsonSchema = require '../../app/schemas/models/trial_request.schema'
|
||||
User = require '../users/User'
|
||||
|
||||
TrialRequestSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
|
||||
|
||||
|
@ -32,6 +33,19 @@ TrialRequestSchema.post 'save', (doc) ->
|
|||
sendwithus.api.send emailParams, (err, result) =>
|
||||
log.error "sendwithus trial request approved error: #{err}, result: #{result}" if err
|
||||
|
||||
# Subscribe to teacher news group
|
||||
User.findById doc.get('applicant'), (err, user) =>
|
||||
if err
|
||||
log.error "Trial request user find error: #{err}"
|
||||
return
|
||||
emails = _.cloneDeep(user.get('emails') ? {})
|
||||
emails.teacherNews ?= {}
|
||||
emails.teacherNews.enabled = true
|
||||
user.update {$set: {emails: emails}}, {}, (err) =>
|
||||
if err
|
||||
log.error "Trial request user update error: #{err}"
|
||||
return
|
||||
|
||||
TrialRequestSchema.statics.privateProperties = []
|
||||
TrialRequestSchema.statics.editableProperties = [
|
||||
'created'
|
||||
|
|
|
@ -76,6 +76,7 @@ emailNameMap =
|
|||
diplomatNews: 'translator'
|
||||
ambassadorNews: 'support'
|
||||
anyNotes: 'notification'
|
||||
teacherNews: 'teacher'
|
||||
|
||||
UserSchema.methods.setEmailSubscription = (newName, enabled) ->
|
||||
oldSubs = _.clone @get('emailSubscriptions')
|
||||
|
|
|
@ -134,7 +134,11 @@ describe 'Trial Requests', ->
|
|||
expect(prepaid.get('type')).toEqual('course')
|
||||
expect(prepaid.get('creator')).toEqual(user.get('_id'))
|
||||
expect(prepaid.get('maxRedeemers')).toEqual(2)
|
||||
done()
|
||||
User.findById user._id, (err, user) =>
|
||||
expect(err).toBeNull()
|
||||
return done(err) if err
|
||||
expect(user.get('emails')?.teacherNews?.enabled).toEqual(true)
|
||||
done()
|
||||
|
||||
it 'Deny trial request', (done) ->
|
||||
loginNewUser (user) ->
|
||||
|
|
Loading…
Reference in a new issue