From 6b3e94d60a4d201b526610008f7e22b30c3364f4 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 26 May 2016 15:12:10 -0700 Subject: [PATCH 01/13] Remove log --- app/views/courses/TeacherClassView.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/courses/TeacherClassView.coffee b/app/views/courses/TeacherClassView.coffee index 659016298..eecfb3550 100644 --- a/app/views/courses/TeacherClassView.coffee +++ b/app/views/courses/TeacherClassView.coffee @@ -277,7 +277,6 @@ module.exports = class TeacherClassView extends RootView @state.set('searchTerm', $(e.target).val()) onChangeCourseSelect: (e) -> - console.log '??' @trigger 'course-select:change', { selectedCourse: @courses.get($(e.currentTarget).val()) } getSelectedStudentIDs: -> From 77ba873da9aea7d872c3587c53aa436ea4fd2535 Mon Sep 17 00:00:00 2001 From: phoenixeliot Date: Thu, 26 May 2016 15:49:33 -0700 Subject: [PATCH 02/13] Add tests for teacher password reset endpoint --- server/middleware/classrooms.coffee | 3 +- spec/server/functional/classrooms.spec.coffee | 70 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/server/middleware/classrooms.coffee b/server/middleware/classrooms.coffee index c03523a3e..f44198ea7 100644 --- a/server/middleware/classrooms.coffee +++ b/server/middleware/classrooms.coffee @@ -205,7 +205,8 @@ module.exports = ownedStudentIDs = _.flatten ownedClassrooms.map (c) -> c.get('members').map (id) -> id.toString() - return next() unless memberID in ownedStudentIDs + unless memberID in ownedStudentIDs + throw new errors.Forbidden("Can't reset the password of a student that's not in one of your classrooms.") student = yield User.findById(memberID) if student.get('emailVerified') log.debug "classrooms.setStudentPassword: Can't reset password for a student (#{memberID}) that has verified their email address." diff --git a/spec/server/functional/classrooms.spec.coffee b/spec/server/functional/classrooms.spec.coffee index 4d7e814a3..94ad0f9df 100644 --- a/spec/server/functional/classrooms.spec.coffee +++ b/spec/server/functional/classrooms.spec.coffee @@ -447,3 +447,73 @@ describe 'GET /db/classroom/:handle/members', -> expect(user.email).toBeDefined() expect(user.passwordHash).toBeUndefined() done() + +describe 'POST /db/classroom/:classroomID/members/:memberID/reset-password', -> + it 'changes the password', utils.wrap (done) -> + yield utils.clearModels([User, Classroom]) + teacher = yield utils.initUser() + yield utils.loginUser(teacher) + student = yield utils.initUser({ name: "Firstname Lastname" }) + newPassword = "this is a new password" + classroom = yield new Classroom({name: 'Classroom', ownerID: teacher._id, members: [student._id] }).save() + expect(student.get('passwordHash')).not.toEqual(User.hashPassword(newPassword)) + [res, body] = yield request.postAsync({ + uri: getURL("/db/classroom/#{classroom.id}/members/#{student.id}/reset-password") + json: { password: newPassword } + }) + expect(res.statusCode).toBe(200) + changedStudent = yield User.findById(student.id) + expect(changedStudent.get('passwordHash')).toEqual(User.hashPassword(newPassword)) + done() + + it "doesn't change the password if you're not their teacher", utils.wrap (done) -> + yield utils.clearModels([User, Classroom]) + teacher = yield utils.initUser() + yield utils.loginUser(teacher) + student = yield utils.initUser({ name: "Firstname Lastname" }) + student2 = yield utils.initUser({ name: "Firstname Lastname 2" }) + newPassword = "this is a new password" + classroom = yield new Classroom({name: 'Classroom', ownerID: teacher._id, members: [student2._id] }).save() + expect(student.get('passwordHash')).not.toEqual(User.hashPassword(newPassword)) + [res, body] = yield request.postAsync({ + uri: getURL("/db/classroom/#{classroom.id}/members/#{student.id}/reset-password") + json: { password: newPassword } + }) + expect(res.statusCode).toBe(403) + changedStudent = yield User.findById(student.id) + expect(changedStudent.get('passwordHash')).toEqual(student.get('passwordHash')) + done() + + it "doesn't change the password if their email is verified", utils.wrap (done) -> + yield utils.clearModels([User, Classroom]) + teacher = yield utils.initUser() + yield utils.loginUser(teacher) + student = yield utils.initUser({ name: "Firstname Lastname", emailVerified: true }) + newPassword = "this is a new password" + classroom = yield new Classroom({name: 'Classroom', ownerID: teacher._id, members: [student._id] }).save() + expect(student.get('passwordHash')).not.toEqual(User.hashPassword(newPassword)) + [res, body] = yield request.postAsync({ + uri: getURL("/db/classroom/#{classroom.id}/members/#{student.id}/reset-password") + json: { password: newPassword } + }) + expect(res.statusCode).toBe(403) + changedStudent = yield User.findById(student.id) + expect(changedStudent.get('passwordHash')).toEqual(student.get('passwordHash')) + done() + + it "doesn't let you set a 1-character password", utils.wrap (done) -> + yield utils.clearModels([User, Classroom]) + teacher = yield utils.initUser() + yield utils.loginUser(teacher) + student = yield utils.initUser({ name: "Firstname Lastname" }) + newPassword = "e" + classroom = yield new Classroom({name: 'Classroom', ownerID: teacher._id, members: [student._id] }).save() + expect(student.get('passwordHash')).not.toEqual(User.hashPassword(newPassword)) + [res, body] = yield request.postAsync({ + uri: getURL("/db/classroom/#{classroom.id}/members/#{student.id}/reset-password") + json: { password: newPassword } + }) + expect(res.statusCode).toBe(422) + changedStudent = yield User.findById(student.id) + expect(changedStudent.get('passwordHash')).toEqual(student.get('passwordHash')) + done() From c9ece55d499cc8a8fc070e36f4928471ac873e93 Mon Sep 17 00:00:00 2001 From: phoenixeliot Date: Thu, 26 May 2016 16:15:09 -0700 Subject: [PATCH 03/13] Fix console.log to log.debug --- server/middleware/classrooms.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/middleware/classrooms.coffee b/server/middleware/classrooms.coffee index f44198ea7..87c8e992c 100644 --- a/server/middleware/classrooms.coffee +++ b/server/middleware/classrooms.coffee @@ -132,7 +132,7 @@ module.exports = post: wrap (req, res) -> throw new errors.Unauthorized() unless req.user and not req.user.isAnonymous() unless req.user?.isTeacher() - console.log "classrooms.post: Can't create classroom if you (#{req.user?.id}) aren't a teacher." + log.debug "classrooms.post: Can't create classroom if you (#{req.user?.id}) aren't a teacher." throw new errors.Forbidden() classroom = database.initDoc(req, Classroom) classroom.set 'ownerID', req.user._id From 9bc35db7a6c79637a3196e6404534d76c6e89d33 Mon Sep 17 00:00:00 2001 From: phoenixeliot Date: Wed, 25 May 2016 12:53:52 -0700 Subject: [PATCH 04/13] Include teacher name in course invite email data In preparation for changing the sendwithus template. --- server/handlers/classroom_handler.coffee | 2 ++ server/handlers/course_instance_handler.coffee | 1 + 2 files changed, 3 insertions(+) diff --git a/server/handlers/classroom_handler.coffee b/server/handlers/classroom_handler.coffee index a40ca08f1..0fe8b8387 100644 --- a/server/handlers/classroom_handler.coffee +++ b/server/handlers/classroom_handler.coffee @@ -69,6 +69,7 @@ ClassroomHandler = class ClassroomHandler extends Handler return _.omit(doc.toObject(), 'code', 'codeCamel') inviteStudents: (req, res, classroomID) -> + return @sendUnauthorizedError(res) if not req.user? if not req.body.emails return @sendBadInputError(res, 'Emails not included') @@ -86,6 +87,7 @@ ClassroomHandler = class ClassroomHandler extends Handler recipient: address: email email_data: + teacher_name: req.user.broadName() class_name: classroom.get('name') join_link: "https://codecombat.com/courses?_cc=" + joinCode join_code: joinCode diff --git a/server/handlers/course_instance_handler.coffee b/server/handlers/course_instance_handler.coffee index 0aa959124..bf26df540 100644 --- a/server/handlers/course_instance_handler.coffee +++ b/server/handlers/course_instance_handler.coffee @@ -192,6 +192,7 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler address: email subject: course.get('name') email_data: + teacher_name: req.user.broadName() class_name: course.get('name') join_link: "https://codecombat.com/courses/students?_ppc=" + prepaid.get('code') sendwithus.api.send context, _.noop From 4bb3ac1f0aa953235b477b67d2b6f78bf1d6e16a Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Thu, 26 May 2016 16:46:03 -0700 Subject: [PATCH 05/13] Replace Anoner with Anonymous Closes #3686 --- app/models/User.coffee | 2 +- app/schemas/models/user.coffee | 2 +- app/templates/clans/clan-details.jade | 4 ++-- app/templates/clans/clans.jade | 4 ++-- app/templates/editor/level/level-feedback-view.jade | 2 +- app/templates/play/level/duel-stats-view.jade | 2 +- app/templates/user/main-user-view.jade | 2 +- app/views/admin/MainAdminView.coffee | 2 +- app/views/clans/ClanDetailsView.coffee | 8 ++++---- app/views/ladder/LadderPlayModal.coffee | 2 +- app/views/play/SpectateView.coffee | 2 +- app/views/play/level/PlayLevelView.coffee | 2 +- scripts/mail.coffee | 2 +- server/models/User.coffee | 2 +- server/routes/mail.coffee | 8 ++++---- 15 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/models/User.coffee b/app/models/User.coffee index 3f8720f16..495d006fa 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -25,7 +25,7 @@ module.exports = class User extends CocoModel return name if name [emailName, emailDomain] = @get('email')?.split('@') or [] return emailName if emailName - return 'Anoner' + return 'Anonymous' getPhotoURL: (size=80, useJobProfilePhoto=false, useEmployerPageAvatar=false) -> photoURL = if useJobProfilePhoto then @get('jobProfile')?.photoURL else null diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index 0f7936205..3b1799786 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -6,7 +6,7 @@ UserSchema = c.object default: visa: 'Authorized to work in the US' music: true - name: 'Anoner' + name: 'Anonymous' autocastDelay: 5000 emails: {} permissions: [] diff --git a/app/templates/clans/clan-details.jade b/app/templates/clans/clan-details.jade index 387fdba53..b1d16daec 100644 --- a/app/templates/clans/clan-details.jade +++ b/app/templates/clans/clan-details.jade @@ -123,7 +123,7 @@ block content if memberLanguageMap && memberLanguageMap[member.id] span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id]) div - a(href="/user/#{member.id}")= member.get('name') || 'Anoner' + a(href="/user/#{member.id}")= member.get('name') || 'Anonymous' div Level #{member.level()} if isOwner && member.id !== clan.get('ownerID') button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}", data-i18n="clans.rem_hero") Remove Hero @@ -220,7 +220,7 @@ block content if memberLanguageMap && memberLanguageMap[member.id] span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id]) td.name-cell - a(href="/user/#{member.id}")= member.get('name') || 'Anoner' + a(href="/user/#{member.id}")= member.get('name') || 'Anonymous' td.level-cell= member.level() td.achievements-cell if memberAchievementsMap && memberAchievementsMap[member.id] diff --git a/app/templates/clans/clans.jade b/app/templates/clans/clans.jade index 6cccf104d..797fe586a 100644 --- a/app/templates/clans/clans.jade +++ b/app/templates/clans/clans.jade @@ -45,7 +45,7 @@ block content if view.idNameMap && view.idNameMap[clan.get('ownerID')] a(href="/user/#{clan.get('ownerID')}")= view.idNameMap[clan.get('ownerID')] else - a(href="/user/#{clan.get('ownerID')}") Anoner + a(href="/user/#{clan.get('ownerID')}") Anonymous td if view.myClanIDs.indexOf(clan.id) < 0 button.btn.btn-success.join-clan-btn(data-id="#{clan.id}", data-i18n="clans.join_clan") Join Clan @@ -75,7 +75,7 @@ block content if view.idNameMap && view.idNameMap[clan.get('ownerID')] a(href="/user/#{clan.get('ownerID')}")= view.idNameMap[clan.get('ownerID')] else - a(href="/user/#{clan.get('ownerID')}") Anoner + a(href="/user/#{clan.get('ownerID')}") Anonymous td= clan.get('type') td if clan.get('ownerID') !== me.id diff --git a/app/templates/editor/level/level-feedback-view.jade b/app/templates/editor/level/level-feedback-view.jade index be266b474..97170f3a6 100644 --- a/app/templates/editor/level/level-feedback-view.jade +++ b/app/templates/editor/level/level-feedback-view.jade @@ -11,7 +11,7 @@ ul.user-feedback-list.list-group em= moment(new Date(feedback.created)).fromNow() span.spl.spr - a(href="/user/#{feedback.creator}") - strong= feedback.creatorName || 'Anoner' + strong= feedback.creatorName || 'Anonymous' if feedback.review span.spr : span= feedback.review diff --git a/app/templates/play/level/duel-stats-view.jade b/app/templates/play/level/duel-stats-view.jade index c37aa0eb9..9dbe6d07d 100644 --- a/app/templates/play/level/duel-stats-view.jade +++ b/app/templates/play/level/duel-stats-view.jade @@ -12,7 +12,7 @@ for player in view.players .player-gold .gold-icon .gold-value - .player-name= player.name || 'Anoner' + .player-name= player.name || 'Anonymous' .player-health .health-icon .health-bar-container diff --git a/app/templates/user/main-user-view.jade b/app/templates/user/main-user-view.jade index e08acb0eb..494a23c73 100644 --- a/app/templates/user/main-user-view.jade +++ b/app/templates/user/main-user-view.jade @@ -81,7 +81,7 @@ block append content if idNameMap && idNameMap[clan.get('ownerID')] a(href="/user/#{clan.get('ownerID')}")= idNameMap[clan.get('ownerID')] else - a(href="/user/#{clan.get('ownerID')}") Anoner + a(href="/user/#{clan.get('ownerID')}") Anonymous td= clan.get('members').length else .panel-body diff --git a/app/views/admin/MainAdminView.coffee b/app/views/admin/MainAdminView.coffee index 6a79f155c..cd720d5b0 100644 --- a/app/views/admin/MainAdminView.coffee +++ b/app/views/admin/MainAdminView.coffee @@ -65,7 +65,7 @@ module.exports = class MainAdminView extends RootView forms.enableSubmit(@$('#user-search-button')) result = '' if users.length - result = ("#{user._id}#{_.escape(user.name or 'Anoner')}#{_.escape(user.email)}" for user in users) + result = ("#{user._id}#{_.escape(user.name or 'Anonymous')}#{_.escape(user.email)}" for user in users) result = "#{result.join('\n')}
" @$el.find('#user-search-result').html(result) diff --git a/app/views/clans/ClanDetailsView.coffee b/app/views/clans/ClanDetailsView.coffee index 2348843d8..8e8d7bc12 100644 --- a/app/views/clans/ClanDetailsView.coffee +++ b/app/views/clans/ClanDetailsView.coffee @@ -138,7 +138,7 @@ module.exports = class ClanDetailsView extends RootView return unless @members? and @memberSort? switch @memberSort when "nameDesc" - @members.comparator = (a, b) -> return (b.get('name') or 'Anoner').localeCompare(a.get('name') or 'Anoner') + @members.comparator = (a, b) -> return (b.get('name') or 'Anonymous').localeCompare(a.get('name') or 'Anonymous') when "progressAsc" @members.comparator = (a, b) -> aComplete = (concept for concept, state of userConceptsMap[a.id] when state is 'complete') @@ -151,7 +151,7 @@ module.exports = class ClanDetailsView extends RootView else if aStarted > bStarted then return 1 if highestUserLevelCountMap[a.id] < highestUserLevelCountMap[b.id] then return -1 else if highestUserLevelCountMap[a.id] > highestUserLevelCountMap[b.id] then return 1 - (a.get('name') or 'Anoner').localeCompare(b.get('name') or 'Anoner') + (a.get('name') or 'Anonymous').localeCompare(b.get('name') or 'Anonymous') when "progressDesc" @members.comparator = (a, b) -> aComplete = (concept for concept, state of userConceptsMap[a.id] when state is 'complete') @@ -164,9 +164,9 @@ module.exports = class ClanDetailsView extends RootView else if aStarted < bStarted then return 1 if highestUserLevelCountMap[a.id] > highestUserLevelCountMap[b.id] then return -1 else if highestUserLevelCountMap[a.id] < highestUserLevelCountMap[b.id] then return 1 - (b.get('name') or 'Anoner').localeCompare(a.get('name') or 'Anoner') + (b.get('name') or 'Anonymous').localeCompare(a.get('name') or 'Anonymous') else - @members.comparator = (a, b) -> return (a.get('name') or 'Anoner').localeCompare(b.get('name') or 'Anoner') + @members.comparator = (a, b) -> return (a.get('name') or 'Anonymous').localeCompare(b.get('name') or 'Anonymous') @members.sort() updateHeroIcons: -> diff --git a/app/views/ladder/LadderPlayModal.coffee b/app/views/ladder/LadderPlayModal.coffee index aba8cff87..ee3e79c20 100644 --- a/app/views/ladder/LadderPlayModal.coffee +++ b/app/views/ladder/LadderPlayModal.coffee @@ -61,7 +61,7 @@ module.exports = class LadderPlayModal extends ModalView success = (@nameMap) => for challenger in _.values(@challengers) - challenger.opponentName = @nameMap[challenger.opponentID]?.name or 'Anoner' + challenger.opponentName = @nameMap[challenger.opponentID]?.name or 'Anonymous' challenger.opponentWizard = @nameMap[challenger.opponentID]?.wizard or {} @checkWizardLoaded() diff --git a/app/views/play/SpectateView.coffee b/app/views/play/SpectateView.coffee index 00d391098..769f75324 100644 --- a/app/views/play/SpectateView.coffee +++ b/app/views/play/SpectateView.coffee @@ -207,7 +207,7 @@ module.exports = class SpectateLevelView extends RootView findPlayerNames: -> playerNames = {} for session in [@session, @otherSession] when session?.get('team') - playerNames[session.get('team')] = session.get('creatorName') or 'Anoner' + playerNames[session.get('team')] = session.get('creatorName') or 'Anonymous' playerNames initGoalManager: -> diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index a75ac807c..9be13aa1f 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -353,7 +353,7 @@ module.exports = class PlayLevelView extends RootView return {} unless @level.get('type') in ['ladder', 'hero-ladder', 'course-ladder'] playerNames = {} for session in [@session, @otherSession] when session?.get('team') - playerNames[session.get('team')] = session.get('creatorName') or 'Anoner' + playerNames[session.get('team')] = session.get('creatorName') or 'Anonymous' playerNames # Once Surface is Loaded #################################################### diff --git a/scripts/mail.coffee b/scripts/mail.coffee index 952d4b2a1..c73d8ee41 100644 --- a/scripts/mail.coffee +++ b/scripts/mail.coffee @@ -87,7 +87,7 @@ emailUserInitialRecruiting = (user, callback) -> return callback null, false if DEBUGGING and (totalEmailsSent > 1 or Math.random() > 0.05) ++totalEmailsSent name = if user.firstName and user.lastName then "#{user.firstName}" else user.name - name = 'Wizard' if not name or name is 'Anoner' + name = 'Wizard' if not name or name in ['Anoner', 'Anonymous'] team = user.session.levelInfo.team team = team.substr(0, team.length - 1) context = diff --git a/server/models/User.coffee b/server/models/User.coffee index 26c429517..d35a65b12 100644 --- a/server/models/User.coffee +++ b/server/models/User.coffee @@ -49,7 +49,7 @@ UserSchema.methods.broadName = -> return name if name [emailName, emailDomain] = @get('email').split('@') return emailName if emailName - return 'Anoner' + return 'Anonymous' UserSchema.methods.isInGodMode = -> p = @get('permissions') diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 20abb99b9..b888f261e 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -578,7 +578,7 @@ sendLadderUpdateEmail = (session, now, daysAgo) -> #log.info "Not sending email to #{user.get('email')} #{user.get('name')} because the session had levelName #{session.levelName} or team #{session.team} in it." return name = if user.get('firstName') and user.get('lastName') then "#{user.get('firstName')}" else user.get('name') - name = 'Wizard' if not name or name is 'Anoner' + name = 'Wizard' if not name or name is 'Anonymous' # Fetch the most recent defeat and victory, if there are any. # (We could look at strongest/weakest, but we'd have to fetch everyone, or denormalize more.) @@ -622,13 +622,13 @@ sendLadderUpdateEmail = (session, now, daysAgo) -> if err log.error "Couldn't find defeateded opponent: #{err}" defeatedOpponent = null - victoryContext = {opponent_name: defeatedOpponent?.name ? 'Anoner', url: urlForMatch(victory)} if victory + victoryContext = {opponent_name: defeatedOpponent?.name ? 'Anonymous', url: urlForMatch(victory)} if victory onFetchedVictoriousOpponent = (err, victoriousOpponent) -> if err log.error "Couldn't find victorious opponent: #{err}" victoriousOpponent = null - defeatContext = {opponent_name: victoriousOpponent?.name ? 'Anoner', url: urlForMatch(defeat)} if defeat + defeatContext = {opponent_name: victoriousOpponent?.name ? 'Anonymous', url: urlForMatch(defeat)} if defeat Level.find({original: session.level.original, created: {$gt: session.submitDate}}).select('created commitMessage version').sort('-created').lean().exec (err, levelVersions) -> sendEmail defeatContext, victoryContext, (if levelVersions.length then levelVersions else null) @@ -706,7 +706,7 @@ sendNextStepsEmail = (user, now, daysAgo) -> do (err, nextLevel) -> return log.error "Couldn't find next level for #{user.get('email')}: #{err}" if err name = if user.get('firstName') and user.get('lastName') then "#{user.get('firstName')}" else user.get('name') - name = 'hero' if not name or name is 'Anoner' + name = 'Hero' if not name or name in ['Anoner', 'Anonymous'] #secretLevel = switch user.get('testGroupNumber') % 8 # when 0, 1, 2, 3 then name: 'Forgetful Gemsmith', slug: 'forgetful-gemsmith' # when 4, 5, 6, 7 then name: 'Signs and Portents', slug: 'signs-and-portents' From 72b8674237f44bdf46550f136e00177d60081d7a Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Thu, 26 May 2016 17:02:58 -0700 Subject: [PATCH 06/13] Add error message for closeIO.getSalesContactEmail --- server/lib/closeIO.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/server/lib/closeIO.coffee b/server/lib/closeIO.coffee index 23fbb3306..be254091f 100644 --- a/server/lib/closeIO.coffee +++ b/server/lib/closeIO.coffee @@ -68,4 +68,5 @@ module.exports = return done(null, activity.sender) if /@codecombat\.com/ig.test(activity.sender) return done(null, config.mail.supportSchools) catch error + log.error("closeIO.getSalesContactEmail Error for #{email}: #{JSON.stringify(error)}") return done(error, config.mail.supportSchools) From d0d3d838edec56494edba42c3d6ca413ab78da47 Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Fri, 27 May 2016 09:40:46 -0700 Subject: [PATCH 07/13] Better page titles Also ditching non-production constructor page titles --- app/views/AboutView.coffee | 2 ++ app/views/admin/MainAdminView.coffee | 4 +++- app/views/core/RootView.coffee | 6 ++---- app/views/courses/CoursesView.coffee | 2 ++ app/views/courses/EnrollmentsView.coffee | 2 ++ app/views/courses/TeacherClassView.coffee | 4 +++- app/views/courses/TeacherClassesView.coffee | 2 ++ app/views/courses/TeacherCoursesView.coffee | 2 ++ 8 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/views/AboutView.coffee b/app/views/AboutView.coffee index f0705979e..7de91ecae 100644 --- a/app/views/AboutView.coffee +++ b/app/views/AboutView.coffee @@ -23,6 +23,8 @@ module.exports = class AboutView extends RootView 'left': 'onLeftPressed' 'esc': 'onEscapePressed' + getTitle: -> return $.i18n.t('nav.about') + afterRender: -> super(arguments...) @$('#fixed-nav').affix({ diff --git a/app/views/admin/MainAdminView.coffee b/app/views/admin/MainAdminView.coffee index cd720d5b0..550fa4f13 100644 --- a/app/views/admin/MainAdminView.coffee +++ b/app/views/admin/MainAdminView.coffee @@ -19,7 +19,9 @@ module.exports = class MainAdminView extends RootView 'click #user-search-result': 'onClickUserSearchResult' 'click #create-free-sub-btn': 'onClickFreeSubLink' 'click #terminal-create': 'onClickTerminalSubLink' - + + getTitle: -> return $.i18n.t('account_settings.admin') + initialize: -> if window.amActually @amActually = new User({_id: window.amActually}) diff --git a/app/views/core/RootView.coffee b/app/views/core/RootView.coffee index 83cc25bcb..75ed205a3 100644 --- a/app/views/core/RootView.coffee +++ b/app/views/core/RootView.coffee @@ -110,10 +110,8 @@ module.exports = class RootView extends CocoView @buildLanguages() $('body').removeClass('is-playing') - if application.isProduction() - title = 'CodeCombat - ' + (@getTitle() or 'Learn how to code by playing a game') - else - title = @getTitle() or @constructor.name + if title = @getTitle() then title += ' | CodeCombat' + else title = 'CodeCombat - Learn how to code by playing a game' $('title').text(title) diff --git a/app/views/courses/CoursesView.coffee b/app/views/courses/CoursesView.coffee index 749318cf7..d255beb9d 100644 --- a/app/views/courses/CoursesView.coffee +++ b/app/views/courses/CoursesView.coffee @@ -28,6 +28,8 @@ module.exports = class CoursesView extends RootView 'submit #join-class-form': 'onSubmitJoinClassForm' 'click #change-language-link': 'onClickChangeLanguageLink' + getTitle: -> return $.i18n.t('teacher.students') + initialize: -> @courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance}) @courseInstances.comparator = (ci) -> return ci.get('classroomID') + ci.get('courseID') diff --git a/app/views/courses/EnrollmentsView.coffee b/app/views/courses/EnrollmentsView.coffee index b79d51886..db77c870c 100644 --- a/app/views/courses/EnrollmentsView.coffee +++ b/app/views/courses/EnrollmentsView.coffee @@ -19,6 +19,8 @@ module.exports = class EnrollmentsView extends RootView 'click #how-to-enroll-link': 'onClickHowToEnrollLink' 'click #contact-us-btn': 'onClickContactUsButton' + getTitle: -> return $.i18n.t('teacher.enrollments') + initialize: -> @state = new State({ totalEnrolled: 0 diff --git a/app/views/courses/TeacherClassView.coffee b/app/views/courses/TeacherClassView.coffee index eecfb3550..bfb907bbb 100644 --- a/app/views/courses/TeacherClassView.coffee +++ b/app/views/courses/TeacherClassView.coffee @@ -63,6 +63,8 @@ module.exports = class TeacherClassView extends RootView enrolledUsers: "" } + getTitle: -> return @classroom?.get('name') + initialize: (options, classroomID) -> super(options) @singleStudentCourseProgressDotTemplate = require 'templates/teachers/hovers/progress-dot-single-student-course' @@ -116,7 +118,7 @@ module.exports = class TeacherClassView extends RootView @supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts'}}) @attachMediatorEvents() - + attachMediatorEvents: () -> @listenTo @state, 'sync change', -> if _.isEmpty(_.omit(@state.changed, 'searchTerm')) diff --git a/app/views/courses/TeacherClassesView.coffee b/app/views/courses/TeacherClassesView.coffee index 5a5f4e904..63f196e14 100644 --- a/app/views/courses/TeacherClassesView.coffee +++ b/app/views/courses/TeacherClassesView.coffee @@ -25,6 +25,8 @@ module.exports = class TeacherClassesView extends RootView 'click .add-students-btn': 'onClickAddStudentsButton' 'click .create-classroom-btn': 'onClickCreateClassroomButton' + getTitle: -> return $.i18n.t('teacher.my_classes') + initialize: (options) -> super(options) @classrooms = new Classrooms() diff --git a/app/views/courses/TeacherCoursesView.coffee b/app/views/courses/TeacherCoursesView.coffee index 482f58ec6..14ca396fe 100644 --- a/app/views/courses/TeacherCoursesView.coffee +++ b/app/views/courses/TeacherCoursesView.coffee @@ -39,6 +39,8 @@ module.exports = class TeacherCoursesView extends RootView "569ed916efa72b0ced971447": null } + getTitle: -> return $.i18n.t('teacher.courses') + constructor: (options) -> super(options) @ownedClassrooms = new Classrooms() From 9ce4ac51f079455e95c037d26a356aaee7ffc9ac Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Thu, 26 May 2016 14:25:34 -0700 Subject: [PATCH 08/13] Role-based hard feature blocks --- app/core/Router.coffee | 18 ++++++++-------- app/locale/en.coffee | 3 ++- .../courses/restricted-to-students-view.jade | 21 +++++++++++++------ 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/app/core/Router.coffee b/app/core/Router.coffee index a6c42752b..984619ee6 100644 --- a/app/core/Router.coffee +++ b/app/core/Router.coffee @@ -69,15 +69,15 @@ module.exports = class CocoRouter extends Backbone.Router 'contribute/diplomat': go('contribute/DiplomatView') 'contribute/scribe': go('contribute/ScribeView') - 'courses': go('courses/CoursesView') # , { studentsOnly: true }) # TODO: Enforce after session-less play for teachers - 'Courses': go('courses/CoursesView') # , { studentsOnly: true }) + 'courses': go('courses/CoursesView', { studentsOnly: true }) + 'Courses': go('courses/CoursesView', { studentsOnly: true }) 'courses/students': redirect('/courses') 'courses/teachers': redirect('/teachers/classes') 'courses/purchase': redirect('/teachers/licenses') 'courses/enroll(/:courseID)': redirect('/teachers/licenses') 'courses/update-account': go('courses/CoursesUpdateAccountView') - 'courses/:classroomID': go('courses/ClassroomView') #, { studentsOnly: true }) - 'courses/:courseID/:courseInstanceID': go('courses/CourseDetailsView') + 'courses/:classroomID': go('courses/ClassroomView', { studentsOnly: true }) + 'courses/:courseID/:courseInstanceID': go('courses/CourseDetailsView', { studentsOnly: true }) 'db/*path': 'routeToServer' 'demo(/*subpath)': go('DemoView') @@ -142,14 +142,14 @@ module.exports = class CocoRouter extends Backbone.Router 'SEEN': go('NewHomeView') 'teachers': redirect('/teachers/classes') - 'teachers/classes': go('courses/TeacherClassesView') #, { teachersOnly: true }) - 'teachers/classes/:classroomID': go('courses/TeacherClassView') #, { teachersOnly: true }) + 'teachers/classes': go('courses/TeacherClassesView', { teachersOnly: true }) + 'teachers/classes/:classroomID': go('courses/TeacherClassView', { teachersOnly: true }) 'teachers/courses': go('courses/TeacherCoursesView') 'teachers/demo': go('teachers/RequestQuoteView') 'teachers/enrollments': redirect('/teachers/licenses') - 'teachers/licenses': go('courses/EnrollmentsView') #, { teachersOnly: true }) + 'teachers/licenses': go('courses/EnrollmentsView', { teachersOnly: true }) 'teachers/freetrial': go('teachers/RequestQuoteView') - 'teachers/quote': go('teachers/RequestQuoteView') + 'teachers/quote': redirect('/teachers/demo') 'teachers/signup': -> return @routeDirectly('teachers/CreateTeacherAccountView', []) if me.isAnonymous() @navigate('/teachers/update-account', {trigger: true, replace: true}) @@ -174,7 +174,7 @@ module.exports = class CocoRouter extends Backbone.Router routeDirectly: (path, args=[], options={}) -> if options.teachersOnly and not me.isTeacher() return @routeDirectly('teachers/RestrictedToTeachersView') - if options.studentsOnly and me.isTeacher() + if options.studentsOnly and not me.isStudent() return @routeDirectly('courses/RestrictedToStudentsView') leavingMessage = _.result(window.currentView, 'onLeaveMessage') if leavingMessage diff --git a/app/locale/en.coffee b/app/locale/en.coffee index d546acda9..f7ebe2c6e 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -1279,7 +1279,8 @@ student_age_range_to: "to" create_class: "Create Class" class_name: "Class Name" - teacher_account_restricted: "Your account is a teacher account, and so cannot access student content." + teacher_account_restricted: "Your account is a teacher account and cannot access student content." # {change} + account_restricted: "A student account is required to access this page." update_account_login_title: "Log in to update your account" update_account_title: "Your account needs attention!" update_account_blurb: "Before you can access your classes, choose how you want to use this account." diff --git a/app/templates/courses/restricted-to-students-view.jade b/app/templates/courses/restricted-to-students-view.jade index d31f421c8..ebea62f7c 100644 --- a/app/templates/courses/restricted-to-students-view.jade +++ b/app/templates/courses/restricted-to-students-view.jade @@ -3,10 +3,19 @@ extends /templates/base-flat block content .access-restricted.container.text-center.m-y-3 h5(data-i18n='teacher.access_restricted') - p(data-i18n='courses.teacher_account_restricted') - a.btn.btn-lg.btn-primary(href="/teachers/classes" data-i18n="new_home.goto_classes") - button#logout-button.btn.btn-lg.btn-primary-alt(data-i18n="login.log_out") + if me.isTeacher() + p(data-i18n='courses.teacher_account_restricted') + a.btn.btn-lg.btn-primary(href="/teachers/classes" data-i18n="new_home.goto_classes") + button#logout-button.btn.btn-lg.btn-primary-alt(data-i18n="login.log_out") + else + p(data-i18n='courses.account_restricted') + if me.isAnonymous() + .login-button.btn.btn-lg.btn-primary(data-i18n='login.log_in') + else + a.btn.btn-lg.btn-primary(href="/courses/update-account" data-i18n="courses.update_account_update_student") + button#logout-button.btn.btn-lg.btn-primary-alt(data-i18n="login.log_out") - .teacher-account-blurb.text-center.col-xs-6.col-xs-offset-3.m-y-3 - h5(data-i18n='teacher.what_is_a_teacher_account') - p(data-i18n='teacher.teacher_account_explanation') \ No newline at end of file + if me.isTeacher() + .teacher-account-blurb.text-center.col-xs-6.col-xs-offset-3.m-y-3 + h5(data-i18n='teacher.what_is_a_teacher_account') + p(data-i18n='teacher.teacher_account_explanation') \ No newline at end of file From e3c2947e2d7c8ad65aefcb3a375d062527583566 Mon Sep 17 00:00:00 2001 From: duybkict Date: Thu, 19 May 2016 17:05:18 +0700 Subject: [PATCH 09/13] upgrade brunch related packages to >=2.0.0 --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index fc326df34..cd1c9d497 100644 --- a/package.json +++ b/package.json @@ -93,21 +93,21 @@ "devDependencies": { "after-brunch": "0.0.5", "assetsmanager-brunch": "^1.8.1", - "auto-reload-brunch": "^1.8.1", + "auto-reload-brunch": ">=2.0.0", "bower": "~1.6.4", - "brunch": "^1.8.5", - "coffee-script-brunch": "^1.8.3", - "coffeelint-brunch": "^1.7.1", + "brunch": ">=2.0.0", + "coffee-script-brunch": ">=2.0.0", + "coffeelint-brunch": ">=2.0.0", "commonjs-require-definition": "0.2.0", "compressible": "~1.0.1", "country-data": "0.0.24", "country-list": "0.0.3", - "css-brunch": "^1.7.0", + "css-brunch": ">=2.0.0", "fs-extra": "^0.26.2", "http-proxy": "^1.13.2", "jade-brunch": "1.7.5", "jasmine": "^2.4.1", - "javascript-brunch": "> 1.0 < 1.8", + "javascript-brunch": ">=2.0.0", "karma": "~0.13", "karma-chrome-launcher": "~0.1.2", "karma-coffee-preprocessor": "~0.1.2", @@ -123,7 +123,7 @@ "nodemon": "1.6.1", "parse-domain": "^0.2.1", "requirejs": "~2.1.10", - "sass-brunch": "https://github.com/basicer/sass-brunch-bleeding/archive/1.9.1-bleeding.tar.gz", + "sass-brunch": ">=2.0.0", "telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master", "uglify-js": "^2.5.0" }, From 877bea35e7e1533092199ce2434600e89dec0735 Mon Sep 17 00:00:00 2001 From: duybkict Date: Thu, 26 May 2016 15:44:35 +0700 Subject: [PATCH 10/13] add new vagrant box "brunchv2", revert npm package upgrades in package.json --- Vagrantfile | 4 ++++ package.json | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 22228c451..c4ac08792 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -19,6 +19,10 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.define "default" do |default| default.vm.provision "shell", path: "scripts/vagrant/core/provision.sh", privileged: false +<<<<<<< HEAD +======= + +>>>>>>> f148776... add new vagrant box "brunchv2", revert npm package upgrades in package.json end config.vm.define "brunchv2", autostart: false do |brunchv2| diff --git a/package.json b/package.json index cd1c9d497..fa69ffaee 100644 --- a/package.json +++ b/package.json @@ -93,11 +93,11 @@ "devDependencies": { "after-brunch": "0.0.5", "assetsmanager-brunch": "^1.8.1", - "auto-reload-brunch": ">=2.0.0", + "auto-reload-brunch": "^1.8.1", "bower": "~1.6.4", - "brunch": ">=2.0.0", - "coffee-script-brunch": ">=2.0.0", - "coffeelint-brunch": ">=2.0.0", + "brunch": "^1.8.5", + "coffee-script-brunch": "^1.8.3", + "coffeelint-brunch": "^1.7.1", "commonjs-require-definition": "0.2.0", "compressible": "~1.0.1", "country-data": "0.0.24", @@ -107,7 +107,7 @@ "http-proxy": "^1.13.2", "jade-brunch": "1.7.5", "jasmine": "^2.4.1", - "javascript-brunch": ">=2.0.0", + "javascript-brunch": "> 1.0 < 1.8", "karma": "~0.13", "karma-chrome-launcher": "~0.1.2", "karma-coffee-preprocessor": "~0.1.2", @@ -123,7 +123,7 @@ "nodemon": "1.6.1", "parse-domain": "^0.2.1", "requirejs": "~2.1.10", - "sass-brunch": ">=2.0.0", + "sass-brunch": "https://github.com/basicer/sass-brunch-bleeding/archive/1.9.1-bleeding.tar.gz", "telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master", "uglify-js": "^2.5.0" }, From ebab0dedec20420bdd2631d72eb9a28fdf509835 Mon Sep 17 00:00:00 2001 From: duybkict Date: Thu, 26 May 2016 16:52:57 +0700 Subject: [PATCH 11/13] minor fixes --- Vagrantfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index c4ac08792..22228c451 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -19,10 +19,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.define "default" do |default| default.vm.provision "shell", path: "scripts/vagrant/core/provision.sh", privileged: false -<<<<<<< HEAD -======= - ->>>>>>> f148776... add new vagrant box "brunchv2", revert npm package upgrades in package.json end config.vm.define "brunchv2", autostart: false do |brunchv2| From 5fcc3669af85c7ea007e66de386e4c23e904a998 Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Fri, 27 May 2016 11:19:50 -0700 Subject: [PATCH 12/13] :bug:Revert to css-brunch ^1.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa69ffaee..fc326df34 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "compressible": "~1.0.1", "country-data": "0.0.24", "country-list": "0.0.3", - "css-brunch": ">=2.0.0", + "css-brunch": "^1.7.0", "fs-extra": "^0.26.2", "http-proxy": "^1.13.2", "jade-brunch": "1.7.5", From 438e8e426ce1329ccf0130a61fb5a3a756203b12 Mon Sep 17 00:00:00 2001 From: phoenixeliot Date: Fri, 27 May 2016 11:22:33 -0700 Subject: [PATCH 13/13] Fix potential undefined error in logging --- server/handlers/classroom_handler.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/handlers/classroom_handler.coffee b/server/handlers/classroom_handler.coffee index 0fe8b8387..40b8055f6 100644 --- a/server/handlers/classroom_handler.coffee +++ b/server/handlers/classroom_handler.coffee @@ -105,7 +105,7 @@ ClassroomHandler = class ClassroomHandler extends Handler return @sendSuccess(res, (@formatEntity(req, classroom) for classroom in classrooms)) else if memberID = req.query.memberID unless req.user and (req.user.isAdmin() or memberID is req.user.id) - log.debug "classroom_handler.get: memberID (#{memberID}) must be yourself (#{req.user.id})" + log.debug "classroom_handler.get: memberID (#{memberID}) must be yourself (#{req.user?.id})" return @sendForbiddenError(res) return @sendBadInputError(res, 'Bad memberID') unless utils.isID memberID Classroom.find {members: mongoose.Types.ObjectId(memberID)}, (err, classrooms) =>