diff --git a/app/assets/docs/CodeCombatCoursesGettingStartedGuide.pdf b/app/assets/docs/CodeCombatCoursesGettingStartedGuide.pdf
deleted file mode 100644
index ca1436170..000000000
Binary files a/app/assets/docs/CodeCombatCoursesGettingStartedGuide.pdf and /dev/null differ
diff --git a/app/assets/docs/CodeCombatHourofCodeGettingStartedGuide.pdf b/app/assets/docs/CodeCombatHourofCodeGettingStartedGuide.pdf
deleted file mode 100644
index 7dafb31e5..000000000
Binary files a/app/assets/docs/CodeCombatHourofCodeGettingStartedGuide.pdf and /dev/null differ
diff --git a/app/assets/docs/CodeCombatReferencePoster.pdf b/app/assets/docs/CodeCombatReferencePoster.pdf
deleted file mode 100644
index f33090e3e..000000000
Binary files a/app/assets/docs/CodeCombatReferencePoster.pdf and /dev/null differ
diff --git a/app/assets/docs/CodeCombatTeacherGuideCourse1.pdf b/app/assets/docs/CodeCombatTeacherGuideCourse1.pdf
deleted file mode 100644
index 35bfea8cc..000000000
Binary files a/app/assets/docs/CodeCombatTeacherGuideCourse1.pdf and /dev/null differ
diff --git a/app/assets/docs/CodeCombatTeacherGuideCourse2.pdf b/app/assets/docs/CodeCombatTeacherGuideCourse2.pdf
deleted file mode 100644
index 32dbae708..000000000
Binary files a/app/assets/docs/CodeCombatTeacherGuideCourse2.pdf and /dev/null differ
diff --git a/app/collections/Users.coffee b/app/collections/Users.coffee
index da270c61a..4fb75d666 100644
--- a/app/collections/Users.coffee
+++ b/app/collections/Users.coffee
@@ -6,6 +6,9 @@ module.exports = class Users extends CocoCollection
   url: '/db/user'
     
   fetchForClassroom: (classroom, options={}) ->
+    if options.removeDeleted
+      delete options.removeDeleted
+      @listenTo @, 'sync', @removeDeletedUsers
     classroomID = classroom.id or classroom
     limit = 10
     skip = 0
@@ -21,3 +24,8 @@ module.exports = class Users extends CocoCollection
       jqxhrs.push(@fetch(options))
       skip += limit
     return jqxhrs
+
+  removeDeletedUsers: ->
+    @remove @filter (user) ->
+      user.get('deleted')
+    true
diff --git a/app/locale/en.coffee b/app/locale/en.coffee
index c7b513f54..2803f8649 100644
--- a/app/locale/en.coffee
+++ b/app/locale/en.coffee
@@ -1061,7 +1061,6 @@
     already_enrolled: "already enrolled"
     licenses_remaining: "licenses remaining:"
     insufficient_enrollments: "insufficient paid enrollments"
-    enroll_students: "Enroll Students"
     get_enrollments: "Get More Enrollments"
     change_language: "Change Course Language"
     keep_using: "Keep Using"
@@ -1286,10 +1285,14 @@
     assign_to_selected_students: "Assign to Selected Students"
     assigned: "Assigned"
     enroll_selected_students: "Enroll Selected Students"
+    cant_assign_to_unenrolled: "Course cannot be assigned to students who are not enrolled."
+    no_students_selected: "No students were selected."
     guides_coming_soon: "Guides coming soon!" # Courses
     show_students_from: "Show students from" # Enroll students modal
     enroll_the_following_students: "Enroll the following students"
     all_students: "All Students"
+    enroll_students: "Enroll Students"
+    not_enough_enrollments: "Not enough Enrollments available."
     enrollments_blurb_1: "Students taking Computer Science" # Enrollments page
     enrollments_blurb_2: "require enrollments to access the courses."
     credits_available: "Credits Available"
@@ -1305,7 +1308,8 @@
     how_to_enroll_blurb_2: "To bulk-enroll multiple students, select them using the checkboxes on the left side of the classroom page and click the \"Enroll Selected Students\" button."
     how_to_enroll_blurb_3: "Once a student is enrolled, they will have access to all of the course content."
     bulk_pricing_blurb: "Purchasing for more than 15 students? Get in touch with us for bulk pricing quotes."
-
+    total_unenrolled: "Total unenrolled"
+    
   classes:
     archmage_title: "Archmage"
     archmage_title_description: "(Coder)"
diff --git a/app/schemas/models/classroom.schema.coffee b/app/schemas/models/classroom.schema.coffee
index a38db25b8..b5a29560c 100644
--- a/app/schemas/models/classroom.schema.coffee
+++ b/app/schemas/models/classroom.schema.coffee
@@ -4,7 +4,9 @@ ClassroomSchema = c.object {title: 'Classroom', required: ['name']}
 c.extendNamedProperties ClassroomSchema  # name first
 
 _.extend ClassroomSchema.properties,
+  name: { type: 'string', minLength: 1 }
   members: c.array {title: 'Members'}, c.objectId()
+  deletedMembers: c.array {title: 'Deleted Members'}, c.objectId()
   ownerID: c.objectId()
   description: {type: 'string'}
   code: c.shortString(title: "Unique code to redeem")
diff --git a/app/styles/courses/activate-licenses-modal.sass b/app/styles/courses/activate-licenses-modal.sass
index 28f05f85e..625734dfe 100644
--- a/app/styles/courses/activate-licenses-modal.sass
+++ b/app/styles/courses/activate-licenses-modal.sass
@@ -6,3 +6,9 @@
   .well
     max-height: 284px
     overflow: scroll
+
+  .not-enough-enrollments
+    color: red
+    visibility: hidden
+    &.visible
+      visibility: visible
diff --git a/app/styles/courses/teacher-class-view.sass b/app/styles/courses/teacher-class-view.sass
index 932cbc8e8..802207ec5 100644
--- a/app/styles/courses/teacher-class-view.sass
+++ b/app/styles/courses/teacher-class-view.sass
@@ -65,6 +65,7 @@
       border-bottom: none
   
   .bulk-assign-controls
+    position: relative
     float: right
     margin-bottom: -9999px
     margin-top: 20px
@@ -74,7 +75,15 @@
       margin-left: 10px
     .enroll-selected-students
       margin-left: 56px
-    
+    .cant-assign-to-unenrolled, .no-students-selected
+      position: absolute
+      top: -24px
+      color: red
+      font-size: 13px
+      visibility: hidden
+      &.visible
+        visibility: visible
+      
   .students-table
     width: 100%
     .student-info-col
@@ -111,7 +120,7 @@
       display: inline
       .inline-student-name
         white-space: nowrap
-        text-decoration: underline
+        // text-decoration: underline
 
     li:not(:last-child):after
       content: ', '
diff --git a/app/styles/sales-view.sass b/app/styles/sales-view.sass
deleted file mode 100644
index 33dc210c0..000000000
--- a/app/styles/sales-view.sass
+++ /dev/null
@@ -1,198 +0,0 @@
-#sales-view
-
-  background-color: #F5F5F5
-  font-family: Helvetica, sans-serif
-  font-size: 15px
-  line-height: 20px
-
-  // TODO: better way to remove content styling?
-  #site-content-area
-    width: 100%
-    background-color: #F5F5F5
-    border: none
-    margin: auto
-    padding: 0px
-    min-height: inherit
-
-  .btn-contact-us
-    background-color: #3878DE
-    border: none
-    color: #FFFFFF
-    font-size: 18px
-    margin-top: 20px
-    padding: 20px
-    width: 330px
-    text-decoration: none
-    text-transform: uppercase
-
-    div
-      text-align: left
-
-    img
-      float: left
-      margin-right: 10px
-
-
-  .btn-create-account, .btn-enter-courses
-    background-color: #09AC48
-    color: #FFFFFF
-    display: inline-block
-    font-size: 18px
-    margin: 10px
-    padding: 24px
-    width: 400px
-    text-decoration: none
-    text-transform: uppercase
-
-  .btn-login-account
-    color: #FFFFFF
-    text-decoration: underline
-
-  .btn-setup-class
-    margin-top: 20px
-    text-transform: uppercase
-    width: 300px
-
-  .section-header
-    font-family: Merriweather
-    font-size: 23px
-    line-height: 29px
-    padding: 0px 24px 0px 24px
-    border-bottom: 1px solid lightgray
-
-  .section-subheader
-    font-family: Helvetica, sans-serif
-    font-size: 13px
-    color: #727272
-    line-height: 15px
-
-  .text-right
-    text-align: right
-
-  #top-page-content
-    background-image: url('/images/pages/sales/hero_background.png')
-    background-size: cover
-    color: #FFFFFF
-
-    td
-      vertical-align: top
-
-    .big-quote-mark
-      font-family: Merriweather
-      font-size: 130px
-      font-weight: 700
-      line-height: 130px
-      margin-right: 0px
-      opacity: 0.5
-
-    .hero-quote-container
-      margin: 20px 20px 20px 0px
-      width: 60%
-
-    .hero-quote
-      color: #FFFFFF
-      font-family: Merriweather
-      font-size: 38px
-      font-weight: 700
-      line-height: 48px
-
-    .hero-quote-attribution
-      font-family: Helvetica, sans-serif
-      font-style: italic
-      font-size: 15px
-      color: #FFFFFF
-      line-height: 20px
-      margin-right: 100px
-      text-align: right
-
-    #down-arrow
-      padding: 20px
-
-  #main-content
-    text-align: left
-    display: inline-block
-    width: 850px
-
-    #blurb1
-      padding: 0px 20px 0px 20px
-
-    .blurb-subtitle
-      font-size: 17px
-      font-weight: bold
-
-    #course-comparisons
-      font-size: 12px
-      margin: 20px
-      width: 90%
-
-      img
-        width: 100%
-
-    .img-caption
-      font-family: Helvetica, sans-serif
-      font-style: italic
-      font-size: 11px
-      color: #727272
-      line-height: 13px
-
-    .img-face
-      background-color: #f0e5c7
-      border-radius: 50%
-      height: 100px
-      width: 100px
-
-    .img-game
-      width: 100%
-
-    .teacher-quote
-      font-family: Merriweather
-      font-weight: 300
-      font-size: 15px
-      line-height: 20px
-      padding-top: 5px
-
-    .teacher-name
-      font-family: Helvetica, sans-serif
-      font-weight: 700
-      font-size: 16px
-      line-height: 19px
-
-    .teacher-location
-      font-family: Helvetica, sans-serif
-      font-style: italic
-      font-size: 12px
-      line-height: 14px
-
-    #quote1-container
-      background-image: url('/images/pages/sales/quote1.png')
-      background-repeat: no-repeat
-      background-size: 100% auto
-      height: 265px
-      padding: 12px
-      margin-right: 10px
-
-      .hero-quote-attribution
-        margin-top: 60px
-
-    #quote2-container
-      background-image: url('/images/pages/sales/quote2.png')
-      background-repeat: no-repeat
-      background-size: 100% auto
-      height: 265px
-      padding: 20px 12px 12px 12px
-      margin-left: 10px
-
-      .teacher-quote
-        margin-top: 60px
-
-      .hero-quote-attribution
-        margin-top: 20px
-
-    .twitter-attribution
-      font-family: Helvetica
-      font-weight: 700
-      font-size: 11px
-      line-height: 14px
-      color: black
-      display: inline-block
-      text-decoration: none
diff --git a/app/templates/courses/activate-licenses-modal.jade b/app/templates/courses/activate-licenses-modal.jade
index 2a46095c9..591ef66f9 100644
--- a/app/templates/courses/activate-licenses-modal.jade
+++ b/app/templates/courses/activate-licenses-modal.jade
@@ -3,20 +3,21 @@ extends /templates/core/modal-base-flat
 block modal-header-content
   .clearfix
   .text-center
-    h1(data-i18n="courses.enroll_students")
+    h1(data-i18n="teacher.enroll_students")
     h2(data-i18n="courses.grants_lifetime_access")
     if view.classroom
       p= view.classroom.get('name')
 
 block modal-body-content
-  .text-center
-    span(data-i18n='teacher.show_students_from')
-    span.spr :
-    select
-      each classroom in view.classrooms.models
-        option(selected=(classroom.id === view.classroom.id), value=classroom.id)
-          = classroom.get('name')
-      //- option(selected=!view.classroom, value='all-classrooms' data-i18n='teacher.all_students')
+  if view.classrooms.length > 1
+    .text-center
+      span(data-i18n='teacher.show_students_from')
+      span.spr :
+      select
+        each classroom in view.classrooms.models
+          option(selected=(classroom.id === view.classroom.id), value=classroom.id)
+            = classroom.get('name')
+        //- option(selected=!view.classroom, value='all-classrooms' data-i18n='teacher.all_students')
   form.form
     span(data-i18n="teacher.enroll_the_following_students")
     span :
@@ -40,6 +41,8 @@ block modal-body-content
         span.spr(data-i18n="courses.enrollment_credits_available")
         span#total-available= view.prepaids.totalAvailable()
 
+      p.small-details.not-enough-enrollments
+        span(data-i18n='teacher.not_enough_enrollments')
       p
         button#activate-licenses-btn.btn.btn-lg.btn-primary(type="submit")
           span.spr(data-i18n="courses.enroll")
diff --git a/app/templates/courses/enrollments-view.jade b/app/templates/courses/enrollments-view.jade
index 29a848c9d..47779b36d 100644
--- a/app/templates/courses/enrollments-view.jade
+++ b/app/templates/courses/enrollments-view.jade
@@ -51,27 +51,37 @@ mixin enrollmentStats
     span(data-i18n='teacher.credits_available')
     span.spr :
     = view.prepaids.totalAvailable()
-  //- .small-details
-  //-   span(data-i18n='teacher.total_unique_students')
-  //-   span.spr :
-  //-   = view.members.length
-  //- .small-details
-  //-   span(data-i18n='teacher.total_enrolled_students')
-  //-   span.spr :
-  //-   = view.prepaids.totalRedeemers()
-  //- .small-details
-  //-   span(data-i18n='teacher.unenrolled_students')
-  //-   span.spr :
-  //-   = (view.members.length - view.prepaids.totalRedeemers())
+  .small-details
+    span(data-i18n='teacher.total_unique_students')
+    span.spr :
+    = view.totalEnrolled + view.totalNotEnrolled
+  .small-details
+    span(data-i18n='teacher.total_enrolled_students')
+    span.spr :
+    = view.totalEnrolled
+    
+  h5.small-details.m-t-3
+    span(data-i18n='teacher.unenrolled_students')
+  each classroom in view.classrooms.models
+    if classroom.get('members').length > 0 && view.classroomNotEnrolledMap && view.classroomNotEnrolledMap[classroom.id] > 0
+      .small-details
+        span= classroom.get('name')
+        span.spr : 
+        span= view.classroomNotEnrolledMap[classroom.id]
+
+  .small-details
+    span(data-i18n='teacher.total_unenrolled')
+    span.spr :
+    = view.totalNotEnrolled
+  
   //- .enroll-students.btn.btn-lg.btn-navy
-  //-   | Enroll Students
+  //-   span(data-i18n='teacher.enroll_students')
 
 mixin addCredits
   .text-center
     h5(data-i18n='teacher.add_enrollment_credits')
     div.m-t-1
-      //- input#students-input.text-center.enrollment-count(value=view.numberOfStudents type='number')
-      input#students-input.text-center.enrollment-count(value=15 type='number')
+      input#students-input.text-center.enrollment-count(value=view.numberOfStudents type='number')
     div.m-t-1
       if view.state === 'purchasing'
         .purchase-now.btn.btn-lg.btn-forest.disabled
diff --git a/app/templates/courses/teacher-class-view.jade b/app/templates/courses/teacher-class-view.jade
index 52746391a..79467dc24 100644
--- a/app/templates/courses/teacher-class-view.jade
+++ b/app/templates/courses/teacher-class-view.jade
@@ -134,9 +134,9 @@ mixin inlineUserList(users)
       each student in users
         li
           //- a(href='TODO')
-          //-   = student.get('name')
+          //-   = student.broadName()
           span.inline-student-name
-            = student.get('name')
+            = student.broadName()
 
 mixin addStudentsButton
   .add-students.text-center
@@ -176,7 +176,7 @@ mixin studentRow(student)
       .student-info
         if student.get('deleted')
           em (deleted)
-        div.student-name= student.get('name')
+        div.student-name= student.broadName()
         div.student-email.small-details= student.get('email')
     td.hidden
       a.edit-student-button(data-student-id=student.id)
@@ -244,7 +244,7 @@ mixin courseOverview
 mixin studentLevelsRow(student)
   .student-levels-row.alternating-background
     div.student-info
-      div.student-name= student.get('name')
+      div.student-name= student.broadName()
       div.student-email.small-details= student.get('email')
     div.student-levels-progress
       - var course = view.selectedCourse
@@ -284,6 +284,10 @@ mixin copyCodes
 
 mixin bulkAssignControls
   .bulk-assign-controls.form-inline
+    .no-students-selected.small-details(class=view.assigningToNobody ? 'visible' : '')
+      span(data-i18n='teacher.no_students_selected')
+    .cant-assign-to-unenrolled.small-details(class=view.assigningToUnenrolled ? 'visible' : '')
+      span(data-i18n='teacher.cant_assign_to_unenrolled')
     span.small
       span(data-i18n='teacher.bulk_assign')
       span :
diff --git a/app/templates/courses/teacher-courses-view.jade b/app/templates/courses/teacher-courses-view.jade
index 7d33eb6bd..853b99681 100644
--- a/app/templates/courses/teacher-courses-view.jade
+++ b/app/templates/courses/teacher-courses-view.jade
@@ -82,8 +82,12 @@ mixin course-info(course)
       if view.guideLinks[course.id]
         //- a.btn.btn-primary(href=view.guideLinks[course.id] class=(me.isAnonymous() ? 'disabled' : ''))
         //-   span(data-i18n="courses.print_guide")
-        a.btn.btn-primary(href=view.guideLinks[course.id] class=(me.isAnonymous() ? 'disabled' : ''))
+        a.btn.btn-primary(href=view.guideLinks[course.id].python class=(me.isAnonymous() ? 'disabled' : ''))
           span(data-i18n="courses.view_guide_online")
+          |  — Python
+        a.btn.btn-primary(href=view.guideLinks[course.id].javascript class=(me.isAnonymous() ? 'disabled' : ''))
+          span(data-i18n="courses.view_guide_online")
+          |  — JavaScript
       else
         i.small
           | (
diff --git a/app/templates/sales-view.jade b/app/templates/sales-view.jade
deleted file mode 100644
index 12715e85f..000000000
--- a/app/templates/sales-view.jade
+++ /dev/null
@@ -1,220 +0,0 @@
-extends /templates/base
-
-//- Do NOT localize / i18n
-
-block content
-  #top-page-content
-    br
-    br
-    .text-right
-      button.btn-contact-us(href='/teachers/quote')
-        img(src='/images/pages/sales/chat_icon.png')
-        div contact us for a quote
-
-    table
-      tr
-        td
-          .big-quote-mark “
-        td
-          .hero-quote-container
-            .hero-quote CodeCombat is without question the most engaging platform for learning programming languages.”
-            .hero-quote-attribution - Jonathan P., Elementary Computers Teacher
-
-    if me.isAnonymous()
-      .text-center
-        a.btn-create-account set up a free class
-      .text-center
-        span.spr Already have an account?
-        a.btn-login-account Log in here.
-    else
-      .text-center
-        a.btn-enter-courses(href="/teachers/classes") set up a free class
-
-    p.text-center
-      a(href='#getting-started')
-        img#down-arrow(src='/images/pages/sales/down_arrow.png')
-    a(name="getting-started")
-
-  .text-center
-    #main-content
-      br
-      p.text-center
-        span.section-header What are CodeCombat Courses?
-      p.text-center.section-subheader An entire semesters' worth of computer science curriculum for teachers who want to bring programming into their 4th-12th grade classes.
-      br
-
-      .row
-        .col-md-5
-          img#img-game(src='/images/pages/sales/screen1.png')
-          .text-center
-            i
-              small Students learn by writing code to complete levels in the game.
-        .col-md-7
-          #blurb1
-            //- TODO: why don't jade inline tags work here?
-            //- http://stackoverflow.com/questions/10953326/what-is-a-concise-way-to-create-inline-elements-in-jade
-            p
-              strong.spr CodeCombat
-              span.spr is a platform for students to learn programming all while playing a game with their classmates.  We believe in encouraging
-              strong.spr real, typed code
-              span to support healthy learning curve with programming.
-            p
-              span.spr CodeCombat's Courses consist of levels that have been specifically playtested to work best in a classroom setting, designed to be used by teachers with
-              strong.spr no prior coding experience necessary.
-              span Current Courses are available in JavaScript and Python, with solution guides provided for both.
-      br
-      br
-
-      .row
-        .col-md-7
-          #blurb2
-            p.text-center.blurb-subtitle Designed with Teachers in Mind
-            ul
-              li Intuitive dashboard to track student progress
-              li Course-specific teacher guides with full solutions to each levels
-              li Teachers' forum and community-generated lesson plans.
-              li Optional leaderboards to encourage friendly competitions
-              li
-                span.spr Auto-generate student progress reports
-                i (coming soon!)
-              li
-                span.spr Multiple administrative accounts
-                i (coming soon!)
-        .col-md-5
-          img.img-game(src='/images/pages/sales/screen2.png')
-          .text-center
-            i
-              small Teachers can monitor progress, manage classrooms and more.
-
-      if me.isAnonymous()
-        br
-        .text-center
-          a.btn-create-account set up a free class
-        br
-        br
-
-      p.text-center
-        span.section-header Students (and parents) love us!
-      br
-
-      table
-        tr
-          td
-            #quote1-container
-              .teacher-quote
-                span.spr “My class had been struggling with the basics of Python all year. Today they were having fun and getting into it
-                strong - I think they forgot that they were actually learning something.”
-              .row
-                .col-md-4
-                .col-md-8
-                  .hero-quote-attribution
-                    .teacher-name Tim M.
-                    a(href='https://twitter.com/timmaki',target='_blank') @timmaki
-                    .teacher-location Director of Technology, Tilton School
-          td
-            #quote2-container
-              .row
-                .col-md-8
-                  .hero-quote-attribution
-                    .teacher-name.text-right Darlease M.
-                    .teacher-location.text-right Technology Coordinator,
-                    .teacher-location.text-right Global Learning Charter Public School
-                .col-md-4
-              .teacher-quote
-                span.spr “My girls, who were apprehensive about taking a coding class, are some of my top students.
-                strong They work together and explain the code to each other to make sure each understands.”
-      br
-
-      .text-center.section-subheader Students play CodeCombat during #HourOfCode 2015
-      table(cellpadding=4)
-        tr
-          td
-            img(src='/images/pages/sales/classroom3.png')
-            .text-right
-              a(href='https://twitter.com/flinng/status/674238468747354112')
-                img(src='/images/twitter_icon.png')
-                span.twitter-attribution @flinng
-          td
-            img(src='/images/pages/sales/classroom4.png')
-            .text-right
-              a(href='https://twitter.com/HikariKishi/status/674359511566577664')
-                img(src='/images/twitter_icon.png')
-                span.twitter-attribution @HikariKishi
-          td
-            img(src='/images/pages/sales/classroom2.png')
-            .text-right
-              a(href='https://twitter.com/Coderdojovno/status/675743290461941760')
-                img(src='/images/twitter_icon.png')
-                span.twitter-attribution @Coderdojovno
-      table(cellpadding=4)
-        tr
-          td
-            img(src='/images/pages/sales/classroom6.png')
-            .text-right
-              a(href='https://twitter.com/teachercoulter/status/674734565487828992')
-                img(src='/images/twitter_icon.png')
-                span.twitter-attribution @teachercoulter
-          td
-            img(src='/images/pages/sales/classroom1.png')
-            .text-right
-              a(href='https://twitter.com/CentreHigh/status/674643613360324608')
-                img(src='/images/twitter_icon.png')
-                span.twitter-attribution @CentreHigh
-          td
-            img(src='/images/pages/sales/classroom5.png')
-            .text-right
-              a(href='https://twitter.com/MrsYassen_GRE/status/674747949902090244')
-                img(src='/images/twitter_icon.png')
-                span.twitter-attribution @MrsYassen_GRE
-      br
-
-      p.blurb-subtitle Facts about CodeCombat:
-      .row
-        .col-md-6
-          ul
-            li Students collaborate and help each other solve levels.
-            li Appeals to all genders and a wide range of age groups.
-        .col-md-6
-          ul
-            li Typed code gives students an advantage over block-based programs.
-            li Success with both high-performing and low-performing students, as well as ESL.
-      br
-
-      .row
-        .col-md-8
-          p.text-center
-            span.section-header What are paid courses?
-          p The one-hour long "Introduction to Computer Science" course will always be free for an unlimited number of students. Paid enrollments allow each student access to Computer Science 2, 3, and 4, which is an additional 15 hours of content on top of the free content.  Paid enrollments never expire.
-
-          #course-comparisons
-            img(src='/images/pages/sales/content_table.png')
-          br
-
-          p Per-student pricing allows us to better support flexibility for both large and small schools. Contact us if you are interested in purchasing paid course enrollments for your classroom or school.  Free trials are also available upon request.
-
-        .col-md-4
-          .well
-            p.text-center.blurb-subtitle Resources for Teachers
-            p
-              a(href='http://codecombat.com/docs/CodeCombatCoursesGettingStartedGuide.pdf')
-                img(src='/images/Adobe_PDF_file_icon_32x32.png')
-                span Getting Started Guide
-            p
-              a(href='http://codecombat.com/docs/CodeCombatTeacherGuideCourse1.pdf')
-                img(style='float:left;', src='/images/Adobe_PDF_file_icon_32x32.png')
-                span Introduction to Computer Science Teacher's Guide
-            br
-            p
-              i
-                small Solution guides and additional lesson plans available for paid courses.
-            p
-              i
-                small.spr Contact
-                a(href='mailto:team@codecombat.com') team@codecombat.com
-                small.spl with additional requests.
-      br
-      br
-
-      p.text-center
-        button.btn-contact-us contact us for a quote
-      br
diff --git a/app/templates/teachers/request-quote-view.jade b/app/templates/teachers/request-quote-view.jade
index 645771c77..b70177758 100644
--- a/app/templates/teachers/request-quote-view.jade
+++ b/app/templates/teachers/request-quote-view.jade
@@ -194,7 +194,6 @@ block content
         p(data-i18n="teachers_quote.finish_signup_p")
 
         #social-network-signups
-          span(data-i18n="teachers_quote.signup_with")
           button#facebook-signup-btn.btn.btn-facebook.btn-lg.m-x-1
             span.spr(data-i18n="teachers_quote.signup_with")
             | Facebook
diff --git a/app/views/SalesView.coffee b/app/views/SalesView.coffee
deleted file mode 100644
index be5e5ab86..000000000
--- a/app/views/SalesView.coffee
+++ /dev/null
@@ -1,37 +0,0 @@
-app = require 'core/application'
-AuthModal = require 'views/core/AuthModal'
-RootView = require 'views/core/RootView'
-template = require 'templates/sales-view'
-CreateAccountModal = require 'views/core/CreateAccountModal'
-
-module.exports = class SalesView extends RootView
-  id: 'sales-view'
-  template: template
-
-  events:
-    'click .btn-contact-us': 'onClickContactUs'
-    'click .btn-create-account': 'onClickSignup'
-    'click .btn-login-account': 'onClickLogin'
-    'click #down-arrow': 'onClickDownArrow'
-
-  getTitle: ->
-    'CodeCombat'
-
-  onClickContactUs: (e) ->
-    app.router.navigate '/teachers/quote', trigger: true
-
-  onClickLogin: (e) ->
-    @openModalView new AuthModal() if me.get('anonymous')
-    window.tracker?.trackEvent 'Started Login', category: 'Sales', label: 'Sales Login', ['Mixpanel']
-
-  onClickSignup: (e) ->
-    @openModalView new CreateAccountModal() if me.get('anonymous')
-    window.tracker?.trackEvent 'Started Signup', category: 'Sales', label: 'Sales Create', ['Mixpanel']
-
-  logoutRedirectURL: false
-
-  onClickDownArrow: (e) ->
-    $('#page-container').animate({
-      scrollTop: $('[name="' + $(e.target).closest('a').attr('href').substr(1) + '"]').offset().top
-    }, 300)
-    false
diff --git a/app/views/courses/ActivateLicensesModal.coffee b/app/views/courses/ActivateLicensesModal.coffee
index 6e2539245..d847c4a32 100644
--- a/app/views/courses/ActivateLicensesModal.coffee
+++ b/app/views/courses/ActivateLicensesModal.coffee
@@ -29,7 +29,7 @@ module.exports = class ActivateLicensesModal extends ModalView
       success: =>
         @classrooms.each (classroom) =>
           classroom.users = new Users()
-          jqxhrs = classroom.users.fetchForClassroom(classroom)
+          jqxhrs = classroom.users.fetchForClassroom(classroom, { removeDeleted: true })
           @supermodel.trackRequests(jqxhrs)
       })
     @supermodel.trackCollection(@classrooms)
@@ -46,10 +46,8 @@ module.exports = class ActivateLicensesModal extends ModalView
       numToActivate = @$('input[name="user"]:checked:not(:disabled)').length
     @$('#total-selected-span').text(numToActivate)
     remaining = @prepaids.totalMaxRedeemers() - @prepaids.totalRedeemers() - numToActivate
-    @$('#licenses-remaining-span').text(remaining)
     depleted = remaining < 0
-    @$('#not-depleted-span').toggleClass('hide', depleted)
-    @$('#depleted-span').toggleClass('hide', !depleted)
+    @$('.not-enough-enrollments').toggleClass('visible', depleted)
     @$('#activate-licenses-btn').toggleClass('disabled', depleted).toggleClass('btn-success', not depleted).toggleClass('btn-default', depleted)
     
   replaceStudentList: (e) ->
@@ -97,8 +95,8 @@ module.exports = class ActivateLicensesModal extends ModalView
       return
 
     user = @usersToRedeem.first()
-    prepaid = @prepaids.find((prepaid) -> prepaid.get('properties')?.endDate? and prepaid.openSpots())
-    prepaid = @prepaids.find((prepaid) -> prepaid.openSpots()) unless prepaid
+    prepaid = @prepaids.find((prepaid) -> prepaid.get('properties')?.endDate? and prepaid.openSpots() > 0)
+    prepaid = @prepaids.find((prepaid) -> prepaid.openSpots() > 0) unless prepaid
     $.ajax({
       method: 'POST'
       url: _.result(prepaid, 'url') + '/redeemers'
diff --git a/app/views/courses/ClassroomSettingsModal.coffee b/app/views/courses/ClassroomSettingsModal.coffee
index 3abcff452..d42a20bd6 100644
--- a/app/views/courses/ClassroomSettingsModal.coffee
+++ b/app/views/courses/ClassroomSettingsModal.coffee
@@ -28,7 +28,7 @@ module.exports = class ClassroomSettingsModal extends ModalView
     e.preventDefault()
     form = @$('form')
     forms.clearFormAlerts(form)
-    attrs = forms.formToObject(form)
+    attrs = forms.formToObject(form, ignoreEmptyString: false)
     if attrs.language
       attrs.aceConfig = { language: attrs.language }
       delete attrs.language
@@ -38,6 +38,9 @@ module.exports = class ClassroomSettingsModal extends ModalView
     @classroom.set(attrs)
     schemaErrors = @classroom.getValidationErrors()
     if schemaErrors
+      for error in schemaErrors
+        if error.schemaPath is "/properties/name/minLength"
+          error.message = 'Please enter a class name.'
       forms.applyErrorsToForm(form, schemaErrors)
       return
 
@@ -49,4 +52,4 @@ module.exports = class ClassroomSettingsModal extends ModalView
       @stopListening @classroom, 'sync', @hide
       button.text(@oldButtonText).attr('disabled', false)
       errors.showNotyNetworkError(jqxhr)
-    @listenToOnce @classroom, 'sync', @hide
\ No newline at end of file
+    @listenToOnce @classroom, 'sync', @hide
diff --git a/app/views/courses/ClassroomView.coffee b/app/views/courses/ClassroomView.coffee
index ec3316df3..45d19cdcf 100644
--- a/app/views/courses/ClassroomView.coffee
+++ b/app/views/courses/ClassroomView.coffee
@@ -116,8 +116,8 @@ module.exports = class ClassroomView extends RootView
     userID = $(e.target).closest('.btn').data('user-id')
     if @prepaids.totalMaxRedeemers() - @prepaids.totalRedeemers() > 0
       # Have an unused enrollment, enroll student immediately instead of opening the enroll modal
-      prepaid = @prepaids.find((prepaid) -> prepaid.get('properties')?.endDate? and prepaid.openSpots())
-      prepaid = @prepaids.find((prepaid) -> prepaid.openSpots()) unless prepaid
+      prepaid = @prepaids.find((prepaid) -> prepaid.get('properties')?.endDate? and prepaid.openSpots() > 0)
+      prepaid = @prepaids.find((prepaid) -> prepaid.openSpots() > 0) unless prepaid
       $.ajax({
         method: 'POST'
         url: _.result(prepaid, 'url') + '/redeemers'
diff --git a/app/views/courses/CoursesView.coffee b/app/views/courses/CoursesView.coffee
index f62945e1f..68a55f59a 100644
--- a/app/views/courses/CoursesView.coffee
+++ b/app/views/courses/CoursesView.coffee
@@ -134,7 +134,7 @@ module.exports = class CoursesView extends RootView
       @errorMessage = "#{jqxhr.responseText}"
     @renderSelectors '#join-class-form'
 
-  onJoinClassroomSuccess: (newClassroom, jqxhr, options) ->
+  onJoinClassroomSuccess: (newClassroom, data, options) ->
     application.tracker?.trackEvent 'Joined classroom', {
       category: 'Courses'
       classCode: @classCode
@@ -158,13 +158,17 @@ module.exports = class CoursesView extends RootView
           courseInstance.sessions = new Backbone.Collection()
           @courseInstances.add(courseInstance)
       $.when(jqxhrs...).done =>
-        @state = null
-        @render()
-        location.hash = ''
-        f = -> location.hash = '#just-added-text'
-        # quick and dirty scroll to just-added classroom
-        setTimeout(f, 10)
-
+        # This is a hack to work around previous hacks
+        # TODO: Do joinWithCode properly (before page load)
+        # TODO: Do data flow properly (so going to the class URL works and we don't need to just refresh)
+        location.search = ""
+        # @state = null
+        # @render()
+        # location.hash = ''
+        # f = -> location.hash = '#just-added-text'
+        # # quick and dirty scroll to just-added classroom
+        # setTimeout(f, 10)
+    
   onClickChangeLanguageLink: ->
     application.tracker?.trackEvent 'Student clicked change language', category: 'Courses'
     modal = new ChangeCourseLanguageModal()
diff --git a/app/views/courses/EnrollmentsView.coffee b/app/views/courses/EnrollmentsView.coffee
index a63fc8695..4f1ee3bfd 100644
--- a/app/views/courses/EnrollmentsView.coffee
+++ b/app/views/courses/EnrollmentsView.coffee
@@ -9,6 +9,7 @@ RootView = require 'views/core/RootView'
 stripeHandler = require 'core/services/stripe'
 template = require 'templates/courses/enrollments-view'
 User = require 'models/User'
+Users = require 'collections/Users'
 utils = require 'core/utils'
 Products = require 'collections/Products'
 
@@ -24,8 +25,8 @@ module.exports = class EnrollmentsView extends RootView
     @supermodel.trackCollection(@ownedClassrooms)
     @listenTo stripeHandler, 'received-token', @onStripeReceivedToken
     @fromClassroom = utils.getQueryVariable('from-classroom')
-    @members = new CocoCollection([], { model: User })
-    @listenTo @members, 'sync', @membersSync
+    @members = new Users()
+    # @listenTo @members, 'sync add remove', @calculateEnrollmentStats
     @classrooms = new CocoCollection([], { url: "/db/classroom", model: Classroom })
     @classrooms.comparator = '_id'
     @listenToOnce @classrooms, 'sync', @onceClassroomsSync
@@ -44,6 +45,7 @@ module.exports = class EnrollmentsView extends RootView
     # 'click .enroll-students': 'onClickEnrollStudents'
 
   onLoaded: ->
+    @calculateEnrollmentStats()
     @pricePerStudent = @products.findWhere({name: 'course'}).get('amount')
     me.setRole 'teacher'
     super()
@@ -53,28 +55,54 @@ module.exports = class EnrollmentsView extends RootView
 
   onceClassroomsSync: ->
     for classroom in @classrooms.models
-      @members.fetch({
-        remove: false
-        url: "/db/classroom/#{classroom.id}/members"
-      })
+      @supermodel.trackRequests @members.fetchForClassroom(classroom, {remove: false, removeDeleted: true})
 
-  membersSync: ->
+  calculateEnrollmentStats: ->
+    @removeDeletedStudents()
     @memberEnrolledMap = {}
     for user in @members.models
       @memberEnrolledMap[user.id] = user.get('coursePrepaidID')?
-    @classroomNotEnrolledMap = {}
-    @totalNotEnrolled = 0
+      
+    @totalEnrolled = _.reduce @members.models, ((sum, user) ->
+      sum + (if user.get('coursePrepaidID') then 1 else 0)
+    ), 0
+    
+    @numberOfStudents = @totalNotEnrolled = _.reduce @members.models, ((sum, user) ->
+      sum + (if not user.get('coursePrepaidID') then 1 else 0)
+    ), 0
+    
+    @classroomEnrolledMap = _.reduce @classrooms.models, ((map, classroom) =>
+      enrolled = _.reduce classroom.get('members'), ((sum, userID) =>
+        sum + (if @members.get(userID).get('coursePrepaidID') then 1 else 0)
+      ), 0
+      map[classroom.id] = enrolled
+      map
+    ), {}
+    
+    @classroomNotEnrolledMap = _.reduce @classrooms.models, ((map, classroom) =>
+      enrolled = _.reduce classroom.get('members'), ((sum, userID) =>
+        sum + (if not @members.get(userID).get('coursePrepaidID') then 1 else 0)
+      ), 0
+      map[classroom.id] = enrolled
+      map
+    ), {}
+    
+    true
+    
+  removeDeletedStudents: (e) ->
     for classroom in @classrooms.models
-      @classroomNotEnrolledMap[classroom.id] = 0
-      for memberID in classroom.get('members')
-        @classroomNotEnrolledMap[classroom.id]++ unless @memberEnrolledMap[memberID]
-      @totalNotEnrolled += @classroomNotEnrolledMap[classroom.id]
-    @numberOfStudents = @totalNotEnrolled
-    @render?()
+      _.remove(classroom.get('members'), (memberID) =>
+        not @members.get(memberID) or @members.get(memberID)?.get('deleted')
+      )
+    true
 
   onInputStudentsInput: ->
-    @numberOfStudents = Math.max(parseInt(@$('#students-input').val()) or 0, 0)
-    @updatePrice()
+    input = @$('#students-input').val()
+    if input isnt "" and (parseFloat(input) isnt parseInt(input) or _.isNaN parseInt(input))
+      @$('#students-input').val(@numberOfStudents)
+    else
+      @numberOfStudents = Math.max(parseInt(@$('#students-input').val()) or 0, 0)
+      @updatePrice()
 
   updatePrice: ->
     @renderSelectors '#price-form-group'
diff --git a/app/views/courses/TeacherClassView.coffee b/app/views/courses/TeacherClassView.coffee
index 066cd575c..fe6fe93a3 100644
--- a/app/views/courses/TeacherClassView.coffee
+++ b/app/views/courses/TeacherClassView.coffee
@@ -48,7 +48,7 @@ module.exports = class TeacherClassView extends RootView
     
     @listenTo @classroom, 'sync', ->
       @students = new Users()
-      jqxhrs = @students.fetchForClassroom(@classroom)
+      jqxhrs = @students.fetchForClassroom(@classroom, removeDeleted: true)
       if jqxhrs.length > 0
         @supermodel.trackCollection(@students)
       @listenTo @students, 'sync', @sortByName
@@ -71,6 +71,7 @@ module.exports = class TeacherClassView extends RootView
     @supermodel.trackCollection(@courseInstances)
 
   onLoaded: ->
+    @removeDeletedStudents()
     
     @classCode = @classroom.get('codeCamel') or @classroom.get('code')
     @joinURL = document.location.origin + "/courses?_cc=" + @classCode
@@ -130,6 +131,12 @@ module.exports = class TeacherClassView extends RootView
     modal = new InviteToClassroomModal({ classroom: @classroom })
     @openModalView(modal)
     @listenToOnce modal, 'hide', @render
+  
+  removeDeletedStudents: () ->
+    _.remove(@classroom.get('members'), (memberID) =>
+      not @students.get(memberID) or @students.get(memberID)?.get('deleted')
+    )
+    true
     
   sortByName: (e) ->
     if @sortValue is 'name'
@@ -140,7 +147,7 @@ module.exports = class TeacherClassView extends RootView
       
     dir = @sortDirection
     @students.comparator = (student1, student2) ->
-      return (if student1.get('name') < student2.get('name') then -dir else dir)
+      return (if student1.broadName().toLowerCase() < student2.broadName().toLowerCase() then -dir else dir)
     @students.sort()
     
   sortByProgress: (e) ->
@@ -162,7 +169,7 @@ module.exports = class TeacherClassView extends RootView
     @students.sort()
   
   getSelectedStudentIDs: ->
-    $('.student-row .checkbox-flat input:checked').map (index, checkbox) ->
+    @$('.student-row .checkbox-flat input:checked').map (index, checkbox) ->
       $(checkbox).data('student-id')
     
   ensureInstance: (courseID) ->
@@ -177,7 +184,7 @@ module.exports = class TeacherClassView extends RootView
     application.tracker?.trackEvent 'Classroom started enroll students', category: 'Courses'
   
   onClickBulkEnroll: ->
-    courseID = $('.bulk-course-select').val()
+    courseID = @$('.bulk-course-select').val()
     courseInstance = @courseInstances.findWhere({ courseID, classroomID: @classroom.id })
     userIDs = @getSelectedStudentIDs().toArray()
     selectedUsers = new Users(@students.get(userID) for userID in userIDs)
@@ -187,12 +194,21 @@ module.exports = class TeacherClassView extends RootView
     application.tracker?.trackEvent 'Classroom started enroll students', category: 'Courses'
     
   onClickBulkAssign: ->
-    courseID = $('.bulk-course-select').val()
+    courseID = @$('.bulk-course-select').val()
     courseInstance = @courseInstances.findWhere({ courseID, classroomID: @classroom.id })
-    members = @getSelectedStudentIDs().filter((index, userID) =>
+    selectedIDs = @getSelectedStudentIDs()
+    members = selectedIDs.filter((index, userID) =>
       user = @students.get(userID)
       user.isEnrolled()
     ).toArray()
+    
+    @assigningToUnenrolled = _.any selectedIDs, (userID) =>
+      not @students.get(userID).isEnrolled()
+    
+    @$('.cant-assign-to-unenrolled').toggleClass('visible', @assigningToUnenrolled)
+    
+    @assigningToNobody = selectedIDs.length is 0
+    @$('.no-students-selected').toggleClass('visible', @assigningToNobody)
 
     if courseInstance
       courseInstance.addMembers members, {
@@ -220,12 +236,12 @@ module.exports = class TeacherClassView extends RootView
     
   onClickSelectAll: (e) ->
     e.preventDefault()
-    checkboxes = $('.student-checkbox input')
+    checkboxes = @$('.student-checkbox input')
     if _.all(checkboxes, 'checked')
-      $('.select-all input').prop('checked', false)
+      @$('.select-all input').prop('checked', false)
       checkboxes.prop('checked', false)
     else
-      $('.select-all input').prop('checked', true)
+      @$('.select-all input').prop('checked', true)
       checkboxes.prop('checked', true)
     null
     
@@ -235,8 +251,8 @@ module.exports = class TeacherClassView extends RootView
     checkbox = $(e.currentTarget).find('input')
     checkbox.prop('checked', not checkbox.prop('checked'))
     # checkboxes.prop('checked', false)
-    checkboxes = $('.student-checkbox input')
-    $('.select-all input').prop('checked', _.all(checkboxes, 'checked'))
+    checkboxes = @$('.student-checkbox input')
+    @$('.select-all input').prop('checked', _.all(checkboxes, 'checked'))
   
   onChangeCourseSelect: (e) ->
     @selectedCourse = @courses.get($(e.currentTarget).val())
diff --git a/app/views/courses/TeacherCoursesView.coffee b/app/views/courses/TeacherCoursesView.coffee
index 4e0517687..3ba95d3e9 100644
--- a/app/views/courses/TeacherCoursesView.coffee
+++ b/app/views/courses/TeacherCoursesView.coffee
@@ -27,9 +27,15 @@ module.exports = class TeacherCoursesView extends RootView
     
   guideLinks:
     {
-      "560f1a9f22961295f9427742": 'http://codecombat.com/docs/CodeCombatTeacherGuideCourse1.pdf'
-      "5632661322961295f9428638": 'https://docs.google.com/a/codecombat.com/viewer?a=v&pid=sites&srcid=Y29kZWNvbWJhdC5jb218dGVhY2hlci1ndWlkZXN8Z3g6NGEzMDFhZTZmMTg4YmRmZQ'
-      "56462f935afde0c6fd30fc8c": 'https://docs.google.com/a/codecombat.com/viewer?a=v&pid=sites&srcid=Y29kZWNvbWJhdC5jb218dGVhY2hlci1ndWlkZXN8Z3g6NzY0Nzc1NWRjMTk4MGRiMQ'
+      "560f1a9f22961295f9427742":
+        python: 'http://files.codecombat.com/teacherguides/CodeCombat_TeacherGuide_intro_python.pdf'
+        javascript: 'http://files.codecombat.com/teacherguides/CodeCombat_TeacherGuide_intro_javascript.pdf'
+      "5632661322961295f9428638":
+        python: 'http://files.codecombat.com/teacherguides/CodeCombat_TeacherGuide_course-2_python.pdf'
+        javascript: 'http://files.codecombat.com/teacherguides/CodeCombat_TeacherGuide_course-2_javascript.pdf'
+      "56462f935afde0c6fd30fc8c":
+        python: 'http://files.codecombat.com/teacherguides/CodeCombat_TeacherGuide_course-3_python.pdf'
+        javascript: 'http://files.codecombat.com/teacherguides/CodeCombat_TeacherGuide_course-3_javascript.pdf'
       "56462f935afde0c6fd30fc8d": null
       "569ed916efa72b0ced971447": null
     }
diff --git a/scripts/mongodb/migrations/2016-04-15-move-deleted-classroom-members.js b/scripts/mongodb/migrations/2016-04-15-move-deleted-classroom-members.js
new file mode 100644
index 000000000..67504e72f
--- /dev/null
+++ b/scripts/mongodb/migrations/2016-04-15-move-deleted-classroom-members.js
@@ -0,0 +1,16 @@
+var classrooms = db.classrooms.find();
+classrooms.forEach(function (classroom) {
+  printjson(classroom.members);
+  classroom.members.forEach(function (userID) {
+    var user = db.users.findOne({ _id: userID }, { deleted: true });
+    if (user.deleted) {
+      db.classrooms.update(
+        { _id: classroom._id },
+        {
+          $addToSet: { deletedMembers: userID },
+          $pull: { members: userID },
+        },
+      );
+    }
+  });
+});
diff --git a/server/middleware/classrooms.coffee b/server/middleware/classrooms.coffee
index 13b5e357f..ef6ad0ec6 100644
--- a/server/middleware/classrooms.coffee
+++ b/server/middleware/classrooms.coffee
@@ -59,6 +59,7 @@ module.exports =
     memberIDs = memberIDs.slice(memberSkip, memberSkip + memberLimit)
     
     members = yield User.find({ _id: { $in: memberIDs }}).select(parse.getProjectFromReq(req))
+    # members = yield User.find({ _id: { $in: memberIDs }, deleted: { $ne: true }}).select(parse.getProjectFromReq(req))
     memberObjects = (member.toObject({ req: req, includedPrivates: ["name", "email"] }) for member in members)
     
     res.status(200).send(memberObjects)
diff --git a/server/middleware/users.coffee b/server/middleware/users.coffee
index 0744df455..c1add7f00 100644
--- a/server/middleware/users.coffee
+++ b/server/middleware/users.coffee
@@ -1,9 +1,13 @@
+_ = require 'lodash'
+co = require 'co'
 errors = require '../commons/errors'
 wrap = require 'co-express'
 Promise = require 'bluebird'
 parse = require '../commons/parse'
 request = require 'request'
+mongoose = require 'mongoose'
 User = require '../models/User'
+Classroom = require '../models/Classroom'
 
 
 module.exports =
@@ -36,3 +40,15 @@ module.exports =
     user = yield User.findOne({facebookID: fbID})
     throw new errors.NotFound('No user with that Facebook ID') unless user
     res.status(200).send(user.toObject({req: req}))
+
+  removeFromClassrooms: wrap (req, res, next) ->
+    userID = mongoose.Types.ObjectId(req.user.id)
+    yield Classroom.update(
+      { members: userID }
+      {
+        $addToSet: { deletedMembers: userID }
+        $pull: { members: userID }
+      }
+      { multi: true }
+    )
+    next()
diff --git a/server/routes/index.coffee b/server/routes/index.coffee
index abea5d999..df98d9c19 100644
--- a/server/routes/index.coffee
+++ b/server/routes/index.coffee
@@ -56,6 +56,7 @@ module.exports.setup = (app) ->
   
   app.post('/db/course_instance/:handle/members', mw.auth.checkLoggedIn(), mw.courseInstances.addMembers)
   
+  app.delete('/db/user/:handle', mw.users.removeFromClassrooms)
   app.get('/db/user', mw.users.fetchByGPlusID, mw.users.fetchByFacebookID)
 
   app.get '/db/products', require('./db/product').get
diff --git a/spec/server/functional/user.spec.coffee b/spec/server/functional/user.spec.coffee
index 2b5e8fb54..c4ca07c49 100644
--- a/spec/server/functional/user.spec.coffee
+++ b/spec/server/functional/user.spec.coffee
@@ -310,22 +310,35 @@ describe 'GET /db/user', ->
   xit 'can fetch another user with restricted fields'
 
 describe 'DELETE /db/user', ->
-  it 'can delete a user', (done) ->
-    loginNewUser (user1) ->
-      beforeDeleted = new Date()
-      request.del {uri: "#{getURL(urlUser)}/#{user1.id}"}, (err, res) ->
-        expect(err).toBeNull()
-        return done() if err
-        User.findById user1.id, (err, user1) ->
-          expect(err).toBeNull()
-          return done() if err
-          expect(user1.get('deleted')).toBe(true)
-          expect(user1.get('dateDeleted')).toBeGreaterThan(beforeDeleted)
-          expect(user1.get('dateDeleted')).toBeLessThan(new Date())
-          for key, value of user1.toObject()
-            continue if key in ['_id', 'deleted', 'dateDeleted']
-            expect(_.isEmpty(value)).toEqual(true)
-          done()
+  it 'can delete a user', utils.wrap (done) ->
+    user = yield utils.initUser()
+    yield utils.loginUser(user)
+    beforeDeleted = new Date()
+    [res, body] = yield request.delAsync {uri: "#{getURL(urlUser)}/#{user.id}"}
+    user = yield User.findById user.id
+    expect(user.get('deleted')).toBe(true)
+    expect(user.get('dateDeleted')).toBeGreaterThan(beforeDeleted)
+    expect(user.get('dateDeleted')).toBeLessThan(new Date())
+    for key, value of user.toObject()
+      continue if key in ['_id', 'deleted', 'dateDeleted']
+      expect(_.isEmpty(value)).toEqual(true)
+    done()
+    
+  it 'moves user to classroom.deletedMembers', utils.wrap (done) ->
+    user = yield utils.initUser()
+    user2 = yield utils.initUser()
+    yield utils.loginUser(user)
+    classroom = new Classroom({
+      members: [user._id, user2._id]
+    })
+    yield classroom.save()
+    [res, body] = yield request.delAsync {uri: "#{getURL(urlUser)}/#{user.id}"}
+    classroom = yield Classroom.findById(classroom.id)
+    expect(classroom.get('members').length).toBe(1)
+    expect(classroom.get('deletedMembers').length).toBe(1)
+    expect(classroom.get('members')[0].toString()).toEqual(user2.id)
+    expect(classroom.get('deletedMembers')[0].toString()).toEqual(user.id)
+    done()
 
 describe 'Statistics', ->
   LevelSession = require '../../../server/models/LevelSession'
diff --git a/test/app/fixtures/campaigns.coffee b/test/app/fixtures/campaigns.coffee
index 101494934..eaada39b0 100644
--- a/test/app/fixtures/campaigns.coffee
+++ b/test/app/fixtures/campaigns.coffee
@@ -1,5 +1,5 @@
-Campaign = require 'models/Campaign';
-Campaigns = require 'collections/Campaigns';
+Campaign = require 'models/Campaign'
+Campaigns = require 'collections/Campaigns'
 
 module.exports = new Campaigns([
   new Campaign({
diff --git a/test/app/fixtures/classrooms.coffee b/test/app/fixtures/classrooms.coffee
deleted file mode 100644
index 4229443e6..000000000
--- a/test/app/fixtures/classrooms.coffee
+++ /dev/null
@@ -1,40 +0,0 @@
-Classroom = require 'models/Classroom';
-Classrooms = require 'collections/Classrooms';
-
-module.exports = new Classrooms([
-  {
-    _id: "classroom0",
-    name: "Teacher Zero's Other Classroom"
-    ownerID: "teacher0",
-    aceConfig:
-      language: 'python'
-    members: []
-  }
-
-  {
-    _id: "classroom1",
-    name: "Teacher Zero's Classroomiest Classroom"
-    members: [
-      "student0",
-      "student1",
-      "student2",
-      "student3",
-    ],
-    ownerID: "teacher0",
-    aceConfig:
-      language: 'python'
-  }
-  
-  {
-    _id: "classroom_archived",
-    name: "Teacher Zero's Archived Classroom"
-    members: [
-      "student0",
-      "student4",
-    ],
-    ownerID: "teacher0",
-    aceConfig:
-      language: 'python'
-    archived: true
-  }
-])
diff --git a/test/app/fixtures/classrooms/active-classroom.coffee b/test/app/fixtures/classrooms/active-classroom.coffee
new file mode 100644
index 000000000..326391c32
--- /dev/null
+++ b/test/app/fixtures/classrooms/active-classroom.coffee
@@ -0,0 +1,17 @@
+Classroom = require 'models/Classroom'
+
+module.exports = new Classroom(
+  {
+    _id: "active-classroom",
+    name: "Teacher Zero's Classroomiest Classroom"
+    members: [
+      "student0",
+      "student1",
+      "student2",
+      "student3",
+    ],
+    ownerID: "teacher0",
+    aceConfig:
+      language: 'python'
+  }
+)
diff --git a/test/app/fixtures/classrooms/archived-classroom.coffee b/test/app/fixtures/classrooms/archived-classroom.coffee
new file mode 100644
index 000000000..eaa0d05d6
--- /dev/null
+++ b/test/app/fixtures/classrooms/archived-classroom.coffee
@@ -0,0 +1,16 @@
+Classroom = require 'models/Classroom'
+
+module.exports = new Classroom(
+  {
+    _id: "classroom_archived",
+    name: "Teacher Zero's Archived Classroom"
+    members: [
+      "student0",
+      "student3",
+    ],
+    ownerID: "teacher0",
+    aceConfig:
+      language: 'python'
+    archived: true
+  }
+)
diff --git a/test/app/fixtures/classrooms/classrooms.coffee b/test/app/fixtures/classrooms/classrooms.coffee
new file mode 100644
index 000000000..720f7901b
--- /dev/null
+++ b/test/app/fixtures/classrooms/classrooms.coffee
@@ -0,0 +1,8 @@
+Classroom = require 'models/Classroom'
+Classrooms = require 'collections/Classrooms'
+
+module.exports = new Classrooms([
+  require './active-classroom'
+  require './empty-classroom'
+  require './archived-classroom'
+])
diff --git a/test/app/fixtures/classrooms/empty-classroom.coffee b/test/app/fixtures/classrooms/empty-classroom.coffee
new file mode 100644
index 000000000..f806d6eea
--- /dev/null
+++ b/test/app/fixtures/classrooms/empty-classroom.coffee
@@ -0,0 +1,12 @@
+Classroom = require 'models/Classroom'
+
+module.exports = new Classroom(
+  {
+    _id: "classroom0",
+    name: "Teacher Zero's Other Classroom"
+    ownerID: "teacher0",
+    aceConfig:
+      language: 'python'
+    members: []
+  }
+)
diff --git a/test/app/fixtures/classrooms/unarchived-classrooms.coffee b/test/app/fixtures/classrooms/unarchived-classrooms.coffee
new file mode 100644
index 000000000..49101bfeb
--- /dev/null
+++ b/test/app/fixtures/classrooms/unarchived-classrooms.coffee
@@ -0,0 +1,7 @@
+Classroom = require 'models/Classroom'
+Classrooms = require 'collections/Classrooms'
+
+module.exports = new Classrooms([
+  require './active-classroom'
+  require './empty-classroom'
+])
diff --git a/test/app/fixtures/course-instances.coffee b/test/app/fixtures/course-instances.coffee
index 9dc33727d..3b0414468 100644
--- a/test/app/fixtures/course-instances.coffee
+++ b/test/app/fixtures/course-instances.coffee
@@ -1,9 +1,11 @@
-CourseInstances = require 'collections/CourseInstances';
+CourseInstances = require 'collections/CourseInstances'
 
 module .exports = new CourseInstances([
   {
     _id: "instance0"
-    courseID: "course0", 
-    classroomID: "classroom0"
+    courseID: "course0",
+    classroomID: "active-classroom"
+    ownerID: "teacher1"
+    members: (require 'test/app/fixtures/students').map('id')
   },
 ])
diff --git a/test/app/fixtures/courses.coffee b/test/app/fixtures/courses.coffee
index b9a24892f..2ad8e3b07 100644
--- a/test/app/fixtures/courses.coffee
+++ b/test/app/fixtures/courses.coffee
@@ -1,4 +1,4 @@
-Courses = require 'collections/Courses';
+Courses = require 'collections/Courses'
 
 module.exports = new Courses(
   [
diff --git a/test/app/fixtures/prepaids.coffee b/test/app/fixtures/prepaids.coffee
index c1e6f4697..82b7c41a1 100644
--- a/test/app/fixtures/prepaids.coffee
+++ b/test/app/fixtures/prepaids.coffee
@@ -1,4 +1,4 @@
-Prepaids = require 'collections/Prepaids';
+Prepaids = require 'collections/Prepaids'
 
 module.exports = new Prepaids([
   {
diff --git a/test/app/fixtures/students.coffee b/test/app/fixtures/students.coffee
index fafa4ca83..e631b657a 100644
--- a/test/app/fixtures/students.coffee
+++ b/test/app/fixtures/students.coffee
@@ -1,4 +1,4 @@
-Users = require 'collections/Users';
+Users = require 'collections/Users'
 
 module.exports = new Users(
   [
@@ -21,20 +21,5 @@ module.exports = new Users(
       _id: "student3"
       name: "Student Three"
     }
-    
-    {
-      _id: "student4"
-      name: "Student Four"
-    }
-    
-    {
-      _id: "student5"
-      name: "Student Five"
-    }
-    
-    {
-      _id: "student6"
-      name: "Student Six"
-    }
   ]
 )
diff --git a/test/app/fixtures/teacher.coffee b/test/app/fixtures/teacher.coffee
index b02837813..2e852763e 100644
--- a/test/app/fixtures/teacher.coffee
+++ b/test/app/fixtures/teacher.coffee
@@ -1,4 +1,4 @@
-User = require 'models/User';
+User = require 'models/User'
 
 module.exports = new User(
   {
diff --git a/test/app/lib/CoursesHelper.spec.coffee b/test/app/lib/CoursesHelper.spec.coffee
index 250be7110..ece911977 100644
--- a/test/app/lib/CoursesHelper.spec.coffee
+++ b/test/app/lib/CoursesHelper.spec.coffee
@@ -6,14 +6,14 @@ CourseInstances = require 'collections/CourseInstances'
 Classrooms = require 'collections/Classrooms'
 
 # These got broken by changes to fixtures :(
-xdescribe 'CoursesHelper', ->
+describe 'CoursesHelper', ->
 
   describe 'calculateAllProgress', ->
 
     beforeEach ->
       # classrooms, courses, campaigns, courseInstances, students
-      @classrooms = require 'test/app/fixtures/classrooms'
-      @classroom = @classrooms.models[0]
+      @classroom = require 'test/app/fixtures/classrooms/active-classroom'
+      @classrooms = new Classrooms([ @classroom ])
       @courses = require 'test/app/fixtures/courses'
       @course = @courses.models[0]
       @campaigns = require 'test/app/fixtures/campaigns'
diff --git a/test/app/views/play/level/modal/CourseVictoryModal.spec.coffee b/test/app/views/play/level/modal/CourseVictoryModal.spec.coffee
index 75ae097be..3e0d30696 100644
--- a/test/app/views/play/level/modal/CourseVictoryModal.spec.coffee
+++ b/test/app/views/play/level/modal/CourseVictoryModal.spec.coffee
@@ -9,6 +9,8 @@ NewItemView = require 'views/play/level/modal/NewItemView'
 ProgressView = require 'views/play/level/modal/ProgressView'
 
 describe 'CourseVictoryModal', ->
+  beforeEach ->
+    me.clear()
 
   it 'will eventually be the only victory modal'
   
diff --git a/test/app/views/teachers/ActivateLicensesModal.spec.coffee b/test/app/views/teachers/ActivateLicensesModal.spec.coffee
index 741e5a1d9..518f32786 100644
--- a/test/app/views/teachers/ActivateLicensesModal.spec.coffee
+++ b/test/app/views/teachers/ActivateLicensesModal.spec.coffee
@@ -9,12 +9,12 @@ xdescribe 'ActivateLicensesModal', ->
   
   me = require 'test/app/fixtures/teacher'
   prepaids = require 'test/app/fixtures/prepaids'
-  classrooms = require 'test/app/fixtures/classrooms' # TODO: Don't use archived ones
+  classrooms = require 'test/app/fixtures/classrooms/unarchived-classrooms'
   users = require 'test/app/fixtures/students'
   responses = {
     '/db/prepaid': prepaids.toJSON()
     '/db/classroom': classrooms.toJSON()
-    '/db/users': users.toJSON() # TODO: Respond with different ones for different classrooms
+    # '/members': users.toJSON() # TODO: Respond with different ones for different classrooms
   }
   
   makeModal = (options) ->
@@ -24,11 +24,17 @@ xdescribe 'ActivateLicensesModal', ->
         @classroom, @users, @selectedUsers
       })
       jasmine.Ajax.requests.sendResponses(responses)
+      _.filter(jasmine.Ajax.requests.all().slice(), (request) ->
+        /\/db\/classroom\/.*\/members/.test(request.url) and request.readyState < 4
+      ).forEach (request) ->
+        request.respondWith(users.toJSON)
+      # debugger
+        
       jasmine.demoModal(@modal)
       _.defer done
     
   beforeEach ->
-    @classroom = classrooms.get('classroom1')
+    @classroom = classrooms.get('active-classroom')
     @users = require 'test/app/fixtures/students'
         
   afterEach ->
@@ -84,26 +90,26 @@ xdescribe 'ActivateLicensesModal', ->
       
     
   
-  # 
+  #
   # describe 'enroll button', ->
   #   beforeEach (done) ->
     #   makeModal.bind(this)(done)
-  # 
+  #
   #   it 'should display the correct total number of credits', ->
   #     expect(@modal.$('#total-available').html()).toBe('2')
-  #     
+  #
   #   it 'should be disabled when teacher doesn\'t have enough enrollments', ->
   #     expect(@modal.$('#total-available').html()).toBe('2')
-  #     
-  #     
-  # 
+  #
+  #
+  #
   # describe 'when enrolling only a single student', ->
   #   describe 'the list of students', ->
   #     it 'should only have the one student selected'
-  #     
+  #
   # describe 'when bulk-enrolling students', ->
   #   describe 'the list of students', ->
   #     it 'should have the right students selected'
-  # 
+  #
   # describe 'selecting more students', ->
   #   it 'should increase the student counter'
diff --git a/test/app/views/teachers/TeacherClassView.spec.coffee b/test/app/views/teachers/TeacherClassView.spec.coffee
new file mode 100644
index 000000000..e20b749ac
--- /dev/null
+++ b/test/app/views/teachers/TeacherClassView.spec.coffee
@@ -0,0 +1,74 @@
+TeacherClassView = require 'views/courses/TeacherClassView'
+storage = require 'core/storage'
+forms = require 'core/forms'
+
+describe '/teachers/classes/:handle', ->
+  
+describe 'TeacherClassView', ->
+  
+  # describe 'when logged out', ->
+  #   it 'responds with 401 error'
+  #   it 'shows Log In and Create Account buttons'
+  
+  @view = null
+    
+  # describe "when you don't own the class", ->
+  #   it 'responds with 403 error'
+  #   it 'shows Log Out button'
+    
+  describe 'when logged in', ->
+    beforeEach (done) ->
+      me = require 'test/app/fixtures/teacher'
+      @classroom = require 'test/app/fixtures/classrooms/active-classroom'
+      @students = require 'test/app/fixtures/students'
+      @courses = require 'test/app/fixtures/courses'
+      @campaigns = require 'test/app/fixtures/campaigns'
+      @courseInstances = require 'test/app/fixtures/course-instances'
+      @levelSessions = require 'test/app/fixtures/level-sessions-partially-completed'
+      
+      @view = new TeacherClassView()
+      @view.classroom.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@classroom) })
+      @view.courses.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@courses) })
+      @view.campaigns.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@campaigns) })
+      @view.courseInstances.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@courseInstances) })
+      @view.students.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@students) })
+      @view.classroom.sessions.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@levelSessions) })
+      
+      jasmine.demoEl(@view.$el)
+      _.defer done
+    
+    it 'has contents', ->
+      expect(@view.$el.children().length).toBeGreaterThan(0)
+
+      
+    # it "shows the classroom's name and description"
+    # it "shows the classroom's join code"
+    
+    describe 'the Students tab', ->
+      # it 'shows all of the students'
+      # it 'sorts correctly by Name'
+      # it 'sorts correctly by Progress'
+      
+      describe 'bulk-assign controls', ->
+        it 'shows alert when assigning course 2 to unenrolled students', ->
+          expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(false)
+          @view.$('.student-row .checkbox-flat').click()
+          @view.$('.assign-to-selected-students').click()
+          expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(true)
+          
+        it 'shows alert when assigning but no students are selected', ->
+          expect(@view.$('.no-students-selected').hasClass('visible')).toBe(false)
+          @view.$('.assign-to-selected-students').click()
+          expect(@view.$('.no-students-selected').hasClass('visible')).toBe(true)
+    
+    # describe 'the Course Progress tab', ->
+    #   it 'shows the correct Course Overview progress'
+    #
+    #   describe 'when viewing another course'
+    #     it 'still shows the correct Course Overview progress'
+    #
+    
+      
+    
+    
+