codecombat/app/views/courses/CoursesView.coffee

221 lines
9.6 KiB
CoffeeScript
Raw Normal View History

2015-09-10 13:37:32 -04:00
app = require 'core/application'
RootView = require 'views/core/RootView'
2015-11-03 19:41:06 -05:00
template = require 'templates/courses/courses-view'
AuthModal = require 'views/core/AuthModal'
CreateAccountModal = require 'views/core/CreateAccountModal'
ChangeCourseLanguageModal = require 'views/courses/ChangeCourseLanguageModal'
HeroSelectModal = require 'views/courses/HeroSelectModal'
ChooseLanguageModal = require 'views/courses/ChooseLanguageModal'
Improve student account recovery This adds the ability to verify email addresses of a user, so we know they have access to the email address on their account. Until a user has verified their email address, any teacher of a class they're in can reset their password for them via the Teacher Dashboard. When a user's email address is verified, a teacher may trigger a password recovery email to be sent to the student. Verification links are valid forever, until the user changes the email address they have on file. They are created using a timestamp, with a sha256 of timestamp+salt+userID+email. Currently the hash value is rather long, could be shorter. Squashed commit messages: Add server endpoints for verifying email address Add server endpoints for verifying email address (pt 2) Add Server+Client endpoint for sending verification email Add client view for verification links Add Edit Student Modal for resetting passwords Add specs for EditStudentModal Tweak method name in EditStudentModal Add edit student button to TeacherClassView Fix up frontend for teacher password resetting Add middleware for teacher password resetting Improve button UX in EditStudentModal Add JoinClassModal Add welcome emails, use broad name Use email without domain as fallback instead of full email Fetch user on edit student modal open Don't allow password reset if student email is verified Set role to student on user signup with classCode Tweak interface for joinClassModal Add button to request verification email for yourself Fix verify email template ID Move text to en.coffee Minor tweaks Fix code review comments Fix some tests, disable a broken one Fix misc tests Fix more tests Refactor recovery email sending to auth Fix overbroad sass Add options to refactored recovery email function Rename getByCode to fetchByCode Fix error message Fix up error handling in users middleware Use .get instead of .toObject Use findById Fix more code review comments Disable still-broken test
2016-05-11 17:39:26 -04:00
JoinClassModal = require 'views/courses/JoinClassModal'
CourseInstance = require 'models/CourseInstance'
CocoCollection = require 'collections/CocoCollection'
Course = require 'models/Course'
Classroom = require 'models/Classroom'
Classrooms = require 'collections/Classrooms'
LevelSession = require 'models/LevelSession'
Levels = require 'collections/Levels'
2016-08-17 17:15:36 -04:00
NameLoader = require 'core/NameLoader'
Campaign = require 'models/Campaign'
ThangType = require 'models/ThangType'
utils = require 'core/utils'
# TODO: Test everything
2015-08-29 10:15:35 -04:00
module.exports = class CoursesView extends RootView
id: 'courses-view'
template: template
events:
'click #log-in-btn': 'onClickLogInButton'
'click #start-new-game-btn': 'openSignUpModal'
'click .change-hero-btn': 'onClickChangeHeroButton'
'click #join-class-btn': 'onClickJoinClassButton'
'submit #join-class-form': 'onSubmitJoinClassForm'
'click .play-btn': 'onClickPlay'
'click .view-class-btn': 'onClickViewClass'
'click .view-levels-btn': 'onClickViewLevels'
2015-12-04 15:08:08 -05:00
getTitle: -> return $.i18n.t('teacher.students')
initialize: ->
@classCodeQueryVar = utils.getQueryVariable('_cc', false)
@courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
@courseInstances.comparator = (ci) -> return ci.get('classroomID') + ci.get('courseID')
@listenToOnce @courseInstances, 'sync', @onCourseInstancesLoaded
2016-06-14 20:15:48 -04:00
@supermodel.loadCollection(@courseInstances, { cache: false })
@classrooms = new CocoCollection([], { url: "/db/classroom", model: Classroom})
@classrooms.comparator = (a, b) -> b.id.localeCompare(a.id)
@supermodel.loadCollection(@classrooms, { data: {memberID: me.id}, cache: false })
@ownedClassrooms = new Classrooms()
@ownedClassrooms.fetchMine({data: {project: '_id'}})
@supermodel.trackCollection(@ownedClassrooms)
@courses = new CocoCollection([], { url: "/db/course", model: Course})
@supermodel.loadCollection(@courses)
@originalLevelMap = {}
@urls = require('core/urls')
# TODO: Trim this section for only what's necessary
@hero = new ThangType
defaultHeroOriginal = ThangType.heroes.captain
heroOriginal = me.get('heroConfig')?.thangType or defaultHeroOriginal
@hero.url = "/db/thang.type/#{heroOriginal}/version"
# @hero.setProjection ['name','slug','soundTriggers','featureImages','gems','heroClass','description','components','extendedName','unlockLevelName','i18n']
@supermodel.loadModel(@hero, 'hero')
@listenTo @hero, 'all', ->
@render()
window.tracker?.trackEvent 'Students Loaded', category: 'Students', ['Mixpanel']
2016-08-17 17:15:36 -04:00
2016-06-14 20:15:48 -04:00
afterInsert: ->
super()
unless me.isStudent() or (@classCodeQueryVar and not me.isTeacher())
@onClassLoadError()
onCourseInstancesLoaded: ->
for courseInstance in @courseInstances.models
continue if not courseInstance.get('classroomID')
courseID = courseInstance.get('courseID')
courseInstance.sessions = new CocoCollection([], {
url: courseInstance.url() + '/my-course-level-sessions',
2015-12-04 15:08:08 -05:00
model: LevelSession
})
courseInstance.sessions.comparator = 'changed'
@supermodel.loadCollection(courseInstance.sessions, { data: { project: 'state.complete level.original playtime changed' }})
2015-12-04 15:08:08 -05:00
2016-04-13 12:54:24 -04:00
hocCourseInstance = @courseInstances.findWhere({hourOfCode: true})
if hocCourseInstance
@courseInstances.remove(hocCourseInstance)
2015-12-04 15:08:08 -05:00
onLoaded: ->
2015-11-29 18:20:38 -05:00
super()
if @classCodeQueryVar and not me.isAnonymous()
window.tracker?.trackEvent 'Students Join Class Link', category: 'Students', classCode: @classCodeQueryVar, ['Mixpanel']
@joinClass()
2016-06-30 18:32:58 -04:00
else if @classCodeQueryVar and me.isAnonymous()
@openModalView(new CreateAccountModal())
2016-08-17 17:15:36 -04:00
ownerIDs = _.map(@classrooms.models, (c) -> c.get('ownerID')) ? []
Promise.resolve($.ajax(NameLoader.loadNames(ownerIDs)))
.then(=>
@ownerNameMap = {}
@ownerNameMap[ownerID] = NameLoader.getName(ownerID) for ownerID in ownerIDs
@render?()
)
_.forEach _.unique(_.pluck(@classrooms.models, 'id')), (classroomID) =>
levels = new Levels()
@listenTo levels, 'sync', =>
return if @destroyed
@originalLevelMap[level.get('original')] = level for level in levels.models
@render()
@supermodel.trackRequest(levels.fetchForClassroom(classroomID, { data: { project: 'original,primerLanguage,slug' }}))
onClickLogInButton: ->
modal = new AuthModal()
@openModalView(modal)
window.tracker?.trackEvent 'Students Login Started', category: 'Students', ['Mixpanel']
openSignUpModal: ->
window.tracker?.trackEvent 'Students Signup Started', category: 'Students', ['Mixpanel']
modal = new CreateAccountModal({ initialValues: { classCode: utils.getQueryVariable('_cc', "") } })
@openModalView(modal)
onClickChangeHeroButton: ->
window.tracker?.trackEvent 'Students Change Hero Started', category: 'Students', ['Mixpanel']
modal = new HeroSelectModal({ currentHeroID: @hero.id })
@openModalView(modal)
@listenTo modal, 'hero-select:success', (newHero) =>
# @hero.url = "/db/thang.type/#{me.get('heroConfig').thangType}/version"
# @hero.fetch()
@hero.set(newHero.attributes)
@listenTo modal, 'hide', ->
@stopListening modal
onSubmitJoinClassForm: (e) ->
e.preventDefault()
classCode = @$('#class-code-input').val() or @classCodeQueryVar
window.tracker?.trackEvent 'Students Join Class With Code', category: 'Students', classCode: classCode, ['Mixpanel']
@joinClass()
2015-12-04 15:08:08 -05:00
onClickJoinClassButton: (e) ->
classCode = @$('#class-code-input').val() or @classCodeQueryVar
window.tracker?.trackEvent 'Students Join Class With Code', category: 'Students', classCode: classCode, ['Mixpanel']
@joinClass()
joinClass: ->
return if @state
@state = 'enrolling'
@errorMessage = null
@classCode = @$('#class-code-input').val() or @classCodeQueryVar
if not @classCode
@state = null
@errorMessage = 'Please enter a code.'
@renderSelectors '#join-class-form'
return
@renderSelectors '#join-class-form'
if me.get('emailVerified') or me.isStudent()
Improve student account recovery This adds the ability to verify email addresses of a user, so we know they have access to the email address on their account. Until a user has verified their email address, any teacher of a class they're in can reset their password for them via the Teacher Dashboard. When a user's email address is verified, a teacher may trigger a password recovery email to be sent to the student. Verification links are valid forever, until the user changes the email address they have on file. They are created using a timestamp, with a sha256 of timestamp+salt+userID+email. Currently the hash value is rather long, could be shorter. Squashed commit messages: Add server endpoints for verifying email address Add server endpoints for verifying email address (pt 2) Add Server+Client endpoint for sending verification email Add client view for verification links Add Edit Student Modal for resetting passwords Add specs for EditStudentModal Tweak method name in EditStudentModal Add edit student button to TeacherClassView Fix up frontend for teacher password resetting Add middleware for teacher password resetting Improve button UX in EditStudentModal Add JoinClassModal Add welcome emails, use broad name Use email without domain as fallback instead of full email Fetch user on edit student modal open Don't allow password reset if student email is verified Set role to student on user signup with classCode Tweak interface for joinClassModal Add button to request verification email for yourself Fix verify email template ID Move text to en.coffee Minor tweaks Fix code review comments Fix some tests, disable a broken one Fix misc tests Fix more tests Refactor recovery email sending to auth Fix overbroad sass Add options to refactored recovery email function Rename getByCode to fetchByCode Fix error message Fix up error handling in users middleware Use .get instead of .toObject Use findById Fix more code review comments Disable still-broken test
2016-05-11 17:39:26 -04:00
newClassroom = new Classroom()
jqxhr = newClassroom.joinWithCode(@classCode)
@listenTo newClassroom, 'join:success', -> @onJoinClassroomSuccess(newClassroom)
@listenTo newClassroom, 'join:error', -> @onJoinClassroomError(newClassroom, jqxhr)
else
modal = new JoinClassModal({ @classCode })
@openModalView modal
2016-06-14 20:15:48 -04:00
@listenTo modal, 'error', @onClassLoadError
Improve student account recovery This adds the ability to verify email addresses of a user, so we know they have access to the email address on their account. Until a user has verified their email address, any teacher of a class they're in can reset their password for them via the Teacher Dashboard. When a user's email address is verified, a teacher may trigger a password recovery email to be sent to the student. Verification links are valid forever, until the user changes the email address they have on file. They are created using a timestamp, with a sha256 of timestamp+salt+userID+email. Currently the hash value is rather long, could be shorter. Squashed commit messages: Add server endpoints for verifying email address Add server endpoints for verifying email address (pt 2) Add Server+Client endpoint for sending verification email Add client view for verification links Add Edit Student Modal for resetting passwords Add specs for EditStudentModal Tweak method name in EditStudentModal Add edit student button to TeacherClassView Fix up frontend for teacher password resetting Add middleware for teacher password resetting Improve button UX in EditStudentModal Add JoinClassModal Add welcome emails, use broad name Use email without domain as fallback instead of full email Fetch user on edit student modal open Don't allow password reset if student email is verified Set role to student on user signup with classCode Tweak interface for joinClassModal Add button to request verification email for yourself Fix verify email template ID Move text to en.coffee Minor tweaks Fix code review comments Fix some tests, disable a broken one Fix misc tests Fix more tests Refactor recovery email sending to auth Fix overbroad sass Add options to refactored recovery email function Rename getByCode to fetchByCode Fix error message Fix up error handling in users middleware Use .get instead of .toObject Use findById Fix more code review comments Disable still-broken test
2016-05-11 17:39:26 -04:00
@listenTo modal, 'join:success', @onJoinClassroomSuccess
@listenTo modal, 'join:error', @onJoinClassroomError
2016-06-14 20:15:48 -04:00
@listenToOnce modal, 'hidden', ->
unless me.isStudent()
@onClassLoadError()
@listenTo modal, 'hidden', ->
@state = null
@renderSelectors '#join-class-form'
2016-06-14 20:15:48 -04:00
# Super hacky way to patch users being able to join class while hiding /courses from others
onClassLoadError: ->
_.defer ->
application.router.routeDirectly('courses/RestrictedToStudentsView')
onJoinClassroomError: (classroom, jqxhr, options) ->
@state = null
if jqxhr.status is 422
@errorMessage = 'Please enter a code.'
else if jqxhr.status is 404
2016-07-07 19:00:15 -04:00
@errorMessage = $.t('signup.classroom_not_found')
else
@errorMessage = "#{jqxhr.responseText}"
2015-12-04 15:08:08 -05:00
@renderSelectors '#join-class-form'
Stuff Partially fix ActivateLicensesModal.spec [IN PROGRESS] Don't display deleted users Move userID to classroom.deletedMembers on user delete (not retroactive) Fix PDF links for course guides, remove old PDFs from repo Remove deprecated SalesView Remove underline for not-yet-linked student names Only show class select when there's more than one Ignore case when sorting student names Use student.broadName instead of name for display and sorting Fix initial load not showing progress after joining a course (hacky) Fix text entry for enrollment number input Fix enrollment statistics Fix enrollment stats completely (and add back in per-class unenrolled count) Add deletedMembers to classroom schema More fixes to enrollment stats (don't count nonmember prepaids) Don't use 0 as implicit false for openSpots Update suggested number of credit to buy automatically Fix classroom edit form ignoring cleared values Add alert text when more users selected than enrollments available Alert user when trying to assign course to unenrolled students Alert user when assigning course to nobody Add some tests for TeacherClassView bulk assign alerts Fix TeacherClassView tests failing without demos Use model/collection.fakeRequests :D Remove unused comment Fix handling of improperly sorted deleted users on clientside Add test for moving deleted users to deletedMembers Add script for moving all deleted classroom members to classroom.deletedMembers Completely rewrite tallying up enrollment statistics Fix some tests to not be dependent on logged-in user Address PR comments Fix default number of enrollments to buy Fix i18n for not enough enrollments Use custom error message for classroom name length
2016-04-07 17:55:42 -04:00
onJoinClassroomSuccess: (newClassroom, data, options) ->
Improve student account recovery This adds the ability to verify email addresses of a user, so we know they have access to the email address on their account. Until a user has verified their email address, any teacher of a class they're in can reset their password for them via the Teacher Dashboard. When a user's email address is verified, a teacher may trigger a password recovery email to be sent to the student. Verification links are valid forever, until the user changes the email address they have on file. They are created using a timestamp, with a sha256 of timestamp+salt+userID+email. Currently the hash value is rather long, could be shorter. Squashed commit messages: Add server endpoints for verifying email address Add server endpoints for verifying email address (pt 2) Add Server+Client endpoint for sending verification email Add client view for verification links Add Edit Student Modal for resetting passwords Add specs for EditStudentModal Tweak method name in EditStudentModal Add edit student button to TeacherClassView Fix up frontend for teacher password resetting Add middleware for teacher password resetting Improve button UX in EditStudentModal Add JoinClassModal Add welcome emails, use broad name Use email without domain as fallback instead of full email Fetch user on edit student modal open Don't allow password reset if student email is verified Set role to student on user signup with classCode Tweak interface for joinClassModal Add button to request verification email for yourself Fix verify email template ID Move text to en.coffee Minor tweaks Fix code review comments Fix some tests, disable a broken one Fix misc tests Fix more tests Refactor recovery email sending to auth Fix overbroad sass Add options to refactored recovery email function Rename getByCode to fetchByCode Fix error message Fix up error handling in users middleware Use .get instead of .toObject Use findById Fix more code review comments Disable still-broken test
2016-05-11 17:39:26 -04:00
@state = null
application.tracker?.trackEvent 'Joined classroom', {
2015-12-04 15:08:08 -05:00
category: 'Courses'
classCode: @classCode
classroomID: newClassroom.id
classroomName: newClassroom.get('name')
ownerID: newClassroom.get('ownerID')
}
@classrooms.add(newClassroom)
@render()
@classroomJustAdded = newClassroom.id
2015-12-04 15:08:08 -05:00
classroomCourseInstances = new CocoCollection([], { url: "/db/course_instance", model: CourseInstance })
classroomCourseInstances.fetch({ data: {classroomID: newClassroom.id} })
@listenToOnce classroomCourseInstances, 'sync', ->
# TODO: Smoother system for joining a classroom and course instances, without requiring page reload,
# and showing which class was just joined.
document.location.search = '' # Using document.location.reload() causes an infinite loop of reloading
onClickPlay: (e) ->
levelSlug = $(e.currentTarget).data('level-slug')
window.tracker?.trackEvent $(e.currentTarget).data('event-action'), category: 'Students', levelSlug: levelSlug, ['Mixpanel']
application.router.navigate($(e.currentTarget).data('href'), { trigger: true })
onClickViewClass: (e) ->
classroomID = $(e.target).data('classroom-id')
window.tracker?.trackEvent 'Students View Class', category: 'Students', classroomID: classroomID, ['Mixpanel']
application.router.navigate("/courses/#{classroomID}", { trigger: true })
onClickViewLevels: (e) ->
courseID = $(e.target).data('course-id')
courseInstanceID = $(e.target).data('courseinstance-id')
window.tracker?.trackEvent 'Students View Levels', category: 'Students', courseID: courseID, courseInstanceID: courseInstanceID, ['Mixpanel']
application.router.navigate("/courses/#{courseID}/#{courseInstanceID}", { trigger: true })