Merge branch 'master' into production

This commit is contained in:
Matt Lott 2016-08-11 15:38:34 -07:00
commit be5bfc6881
12 changed files with 366 additions and 159 deletions

View file

@ -149,6 +149,7 @@ module.exports = class CocoRouter extends Backbone.Router
'teachers/classes': go('courses/TeacherClassesView', { teachersOnly: true }) 'teachers/classes': go('courses/TeacherClassesView', { teachersOnly: true })
'teachers/classes/:classroomID': go('courses/TeacherClassView', { teachersOnly: true }) 'teachers/classes/:classroomID': go('courses/TeacherClassView', { teachersOnly: true })
'teachers/courses': go('courses/TeacherCoursesView') 'teachers/courses': go('courses/TeacherCoursesView')
'teachers/course-solution/:courseID/:language': go('teachers/TeacherCourseSolutionView')
'teachers/demo': go('teachers/RequestQuoteView') 'teachers/demo': go('teachers/RequestQuoteView')
'teachers/enrollments': redirect('/teachers/licenses') 'teachers/enrollments': redirect('/teachers/licenses')
'teachers/licenses': go('courses/EnrollmentsView', { teachersOnly: true }) 'teachers/licenses': go('courses/EnrollmentsView', { teachersOnly: true })

View file

@ -76,23 +76,23 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
boast: "Bietet Rätsel die komplex genug sind um Spieler und Programmiere zu faszinieren." boast: "Bietet Rätsel die komplex genug sind um Spieler und Programmiere zu faszinieren."
winning: "Eine gewinnbringende Kombination aus RPG-Gameplay und Programmierhausaufgaben die aufgeht und kindgerechte Bildung berechtigterweise unterhaltsam macht." winning: "Eine gewinnbringende Kombination aus RPG-Gameplay und Programmierhausaufgaben die aufgeht und kindgerechte Bildung berechtigterweise unterhaltsam macht."
run_class: "Alles was man braucht um einen Informatikkurs in der Schule zu geben, ohne dass man Hintergrundwissen braucht." run_class: "Alles was man braucht um einen Informatikkurs in der Schule zu geben, ohne dass man Hintergrundwissen braucht."
# teachers: "Teachers!" teachers: "Lehrer!"
# teachers_and_educators: "Teachers & Educators" teachers_and_educators: "Lehrkräfte"
# class_in_box: "Learn how our classroom-in-a-box platform fits into your curriculum." # class_in_box: "Learn how our classroom-in-a-box platform fits into your curriculum."
# get_started: "Get Started" get_started: "Beginnen"
# students: "Students:" students: "Schüler:"
# join_class: "Join Class" # join_class: "Join Class"
# role: "Your role:" role: "Deine Rolle:"
# student_count: "Number of students:" student_count: "Anzahl der Schüler:"
# start_playing_for_free: "Start Playing for Free!" start_playing_for_free: "Spiele jetzt kostenlos!"
# students_and_players: "Students & Players" students_and_players: "Schüler und Spieler"
# goto_classes: "Go to My Classes" goto_classes: "Gehe zu meinen Klassen"
# view_profile: "View My Profile" view_profile: "Zeige mein Profil"
# view_progress: "View Progress" view_progress: "Fortschritt ansehen"
# check_out_wiki: "Check out our new Educator Wiki" check_out_wiki: "Entdecken Sie unser neues Lehrkraft-Wiki"
# want_coco: "Want CodeCombat at your school?" want_coco: "Wollen Sie CodeCombat an ihrer Schule?"
# form_select_role: "Select primary role" form_select_role: "Hauptrolle auswählen"
# form_select_range: "Select class size" form_select_range: "Klassenstärke auswählen"
nav: nav:
play: "Spielen" # The top nav bar entry where players choose which levels to play play: "Spielen" # The top nav bar entry where players choose which levels to play
@ -102,7 +102,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
blog: "Blog" blog: "Blog"
forum: "Forum" forum: "Forum"
account: "Account" account: "Account"
# my_account: "My Account" my_account: "Mein Account"
profile: "Profil" profile: "Profil"
stats: "Statistiken" stats: "Statistiken"
code: "Code" code: "Code"
@ -112,7 +112,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
about: "Über" about: "Über"
contact: "Kontakt" contact: "Kontakt"
twitter_follow: "Folge uns auf Twitter" twitter_follow: "Folge uns auf Twitter"
# students: "Students" students: "Schüler"
teachers: "Lehrer" teachers: "Lehrer"
careers: "Karriere mit CodeCombat!" careers: "Karriere mit CodeCombat!"
facebook: "Facebook" facebook: "Facebook"
@ -121,18 +121,18 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
other: "Andere" other: "Andere"
learn_to_code: "Lerne zu coden!" learn_to_code: "Lerne zu coden!"
# toggle_nav: "Toggle navigation" # toggle_nav: "Toggle navigation"
# jobs: "Jobs" jobs: "Jobs"
# schools: "Schools" schools: "Schulen"
# educator_wiki: "Educator Wiki" educator_wiki: "Lehrkraft-Wiki"
# get_involved: "Get Involved" get_involved: "Mitmachen"
# open_source: "Open source (GitHub)" open_source: "Open source (GitHub)"
# support: "Support" support: "Support"
# faqs: "FAQs" faqs: "FAQs"
# help_pref: "Need help? Email" help_pref: "Noch Fragen? Sende eine Email an"
# help_suff: "and we'll get in touch!" help_suff: "und wir melden uns!"
modal: modal:
# cancel: "Cancel" cancel: "Abbrechen"
close: "Schließen" close: "Schließen"
okay: "Okay" okay: "Okay"
@ -183,55 +183,55 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
campaign_old_multiplayer: "Ältere Mehrspieler-Abenteuer" campaign_old_multiplayer: "Ältere Mehrspieler-Abenteuer"
campaign_old_multiplayer_description: "Relikte einer eher zivilisierten Zeit. Keine Simulationen laufen hier ab für diese älteren Multiplayerarenas ohne Helden." campaign_old_multiplayer_description: "Relikte einer eher zivilisierten Zeit. Keine Simulationen laufen hier ab für diese älteren Multiplayerarenas ohne Helden."
# code: code:
# if: "if" # Keywords--these translations show up on hover, so please translate them all, even if it's kind of long. (In the code editor, they will still be in English.) if: "wenn" # Keywords--these translations show up on hover, so please translate them all, even if it's kind of long. (In the code editor, they will still be in English.)
# else: "else" else: "sonst"
# elif: "else if" elif: "sonst wenn"
# while: "while" while: "solange"
# loop: "loop" loop: "Schleife"
# for: "for" for: "r"
# break: "break" break: "Abbruch"
# continue: "continue" continue: "weiter"
# pass: "pass" pass: "passen"
# return: "return" return: "zurückgeben"
# then: "then" then: "dann"
# do: "do" do: "tu"
# end: "end" end: "Ende"
# function: "function" function: "Function"
# def: "define" def: "definiere"
# var: "variable" var: "Variable"
# self: "self" self: "selbst"
# hero: "hero" hero: "Held"
# this: "this" this: "dies"
# or: "or" or: "oder"
# "||": "or" "||": "oder"
# and: "and" and: "und"
# "&&": "and" "&&": "und"
# not: "not" not: "nicht"
# "!": "not" "!": "nicht"
# "=": "assign" "=": "zuordnen"
# "==": "equals" "==": "ist gleich"
# "===": "strictly equals" "===": "ist genau gleich"
# "!=": "does not equal" "!=": "ist nicht gleich"
# "!==": "does not strictly equal" "!==": "ist nicht genau gleich"
# ">": "is greater than" ">": "ist größer als"
# ">=": "is greater than or equal" ">=": "ist größer als oder gleich"
# "<": "is less than" "<": "ist kleiner als"
# "<=": "is less than or equal" "<=": "ist kleiner als oder gleich"
# "*": "multiplied by" "*": "multipliziert mit"
# "/": "divided by" "/": "geteilt durch"
# "+": "plus" "+": "plus"
# "-": "minus" "-": "minus"
# "+=": "add and assign" "+=": "addieren und zuweisen"
# "-=": "subtract and assign" "-=": "subtrahieren und zuweisen"
# True: "True" True: "Wahr"
# true: "true" true: "wahr"
# False: "False" False: "Falsch"
# false: "false" false: "falsch"
# undefined: "undefined" undefined: "undefiniert"
# null: "null" null: "null"
# nil: "nil" nil: "nil"
# None: "None" None: "Nicht"
share_progress_modal: share_progress_modal:
blurb: "Du machst dich gut! Sag deinen Eltern wie viel du mit CodeCombat gelernt hast." # {change} blurb: "Du machst dich gut! Sag deinen Eltern wie viel du mit CodeCombat gelernt hast." # {change}
@ -243,7 +243,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
login: login:
sign_up: "Registrieren" sign_up: "Registrieren"
# email_or_username: "Email or username" email_or_username: "Email oder Username"
log_in: "Einloggen" log_in: "Einloggen"
logging_in: "Logge dich ein" logging_in: "Logge dich ein"
log_out: "Ausloggen" log_out: "Ausloggen"
@ -259,10 +259,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
# create_student_header: "Create Student Account" # create_student_header: "Create Student Account"
# create_teacher_header: "Create Teacher Account" # create_teacher_header: "Create Teacher Account"
# create_individual_header: "Create Individual Account" # create_individual_header: "Create Individual Account"
# create_header: "Create Account" create_header: "Account erstellen"
email_announcements: "Erhalte Benachrichtigungen per Email" # {change} email_announcements: "Erhalte Benachrichtigungen per Email" # {change}
creating: "Erzeuge Account..." creating: "Erzeuge Account..."
# create_account: "Create Account" create_account: "Account erstellen"
sign_up: "Neuen Account anlegen" sign_up: "Neuen Account anlegen"
log_in: "mit Passwort einloggen" log_in: "mit Passwort einloggen"
required: "Du musst dich vorher einloggen diesen Dienst zu nutzen" required: "Du musst dich vorher einloggen diesen Dienst zu nutzen"
@ -270,7 +270,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
school_name: "Schulname und Stadt" school_name: "Schulname und Stadt"
optional: "optional" optional: "optional"
school_name_placeholder: "Beispiel Gymnasium, Musterdorf, DE" school_name_placeholder: "Beispiel Gymnasium, Musterdorf, DE"
# connect_with: "Connect with:" connect_with: "Verbinden mit:"
connected_gplus_header: "Du wurdest erfolgreich mit Google+ verknüpft!" connected_gplus_header: "Du wurdest erfolgreich mit Google+ verknüpft!"
connected_gplus_p: "Schließe das Registrieren ab, so dass du dich mit deinem Google+ Account einloggen." connected_gplus_p: "Schließe das Registrieren ab, so dass du dich mit deinem Google+ Account einloggen."
gplus_exists: "Du hast bereits einen Account mit Google+ verknüpft!" gplus_exists: "Du hast bereits einen Account mit Google+ verknüpft!"
@ -278,7 +278,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
connected_facebook_p: "Schließe das Registrieren ab, so dass du dich mit deinem Facebook Account einloggen." connected_facebook_p: "Schließe das Registrieren ab, so dass du dich mit deinem Facebook Account einloggen."
facebook_exists: "Du hast bereits einen Account mit Facebook verknüpft!" facebook_exists: "Du hast bereits einen Account mit Facebook verknüpft!"
hey_students: "Schüler, gib den Klassencode von deinem Lehrer ein." hey_students: "Schüler, gib den Klassencode von deinem Lehrer ein."
# birthday: "Birthday" birthday: "Geburtstag"
# parent_email_blurb: "We know you can't wait to learn programming &mdash; we're excited too! Your parents will receive an email with further instructions on how to create an account for you. Email {{email_link}} if you have any questions." # parent_email_blurb: "We know you can't wait to learn programming &mdash; we're excited too! Your parents will receive an email with further instructions on how to create an account for you. Email {{email_link}} if you have any questions."
# classroom_not_found: "No classes exist with this Class Code. Check your spelling or ask your teacher for help." # classroom_not_found: "No classes exist with this Class Code. Check your spelling or ask your teacher for help."
# checking: "Checking..." # checking: "Checking..."
@ -340,8 +340,8 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
saving: "Speichere..." saving: "Speichere..."
sending: "Sende..." sending: "Sende..."
send: "Senden" send: "Senden"
# sent: "Sent" sent: "Gesendet"
# type: "Type" type: "Typ"
cancel: "Abbrechen" cancel: "Abbrechen"
save: "Speichern" save: "Speichern"
publish: "Veröffentlichen" publish: "Veröffentlichen"
@ -357,7 +357,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
submit_patch: "Patch einreichen" submit_patch: "Patch einreichen"
submit_changes: "Änderungen einreichen" submit_changes: "Änderungen einreichen"
save_changes: "Änderungen speichern" save_changes: "Änderungen speichern"
# required_field: "Required field" required_field: "Benötigte Felder"
general: general:
and: "und" and: "und"
@ -390,7 +390,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
subject: "Betreff" subject: "Betreff"
email: "Email" email: "Email"
password: "Passwort" password: "Passwort"
# confirm_password: "Confirm Password" confirm_password: "Passwort bestätigen"
message: "Nachricht" message: "Nachricht"
code: "Code" code: "Code"
ladder: "Rangliste" ladder: "Rangliste"
@ -409,10 +409,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
warrior: "Krieger" warrior: "Krieger"
ranger: "Waldläufer" ranger: "Waldläufer"
wizard: "Magier" wizard: "Magier"
# first_name: "First Name" first_name: "Vorname"
# last_name: "Last Name" last_name: "Nachname"
# last_initial: "Last Initial" # last_initial: "Last Initial"
# username: "Username" username: "Username"
units: units:
second: "Sekunde" second: "Sekunde"
@ -431,13 +431,13 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
years: "Jahre" years: "Jahre"
play_level: play_level:
# level_complete: "Level Complete" level_complete: "Level abgeschlossen"
completed_level: "Abgeschlossene Level:" completed_level: "Abgeschlossene Level:"
course: "Kurse:" course: "Kurse:"
done: "Fertig" done: "Fertig"
next_level: "Nächster Level" next_level: "Nächster Level"
next_game: "Nächstes Spiel" next_game: "Nächstes Spiel"
# programming_language: "Programming language" programming_language: "Programmiersprache"
show_menu: "Menü anzeigen" show_menu: "Menü anzeigen"
home: "Startseite" # Not used any more, will be removed soon. home: "Startseite" # Not used any more, will be removed soon.
level: "Level" # Like "Level: Dungeons of Kithgard" level: "Level" # Like "Level: Dungeons of Kithgard"
@ -484,8 +484,8 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
tome_available_spells: "Verfügbare Zauber" tome_available_spells: "Verfügbare Zauber"
tome_your_skills: "Deine Fähigkeiten" tome_your_skills: "Deine Fähigkeiten"
tome_current_method: "Aktuelle Methode" tome_current_method: "Aktuelle Methode"
# hints: "Hints" hints: "Tipps"
# hints_title: "Hint {{number}}" hints_title: "Tipp {{number}}"
code_saved: "Code gespeichert" code_saved: "Code gespeichert"
skip_tutorial: "Überspringen (Esc)" skip_tutorial: "Überspringen (Esc)"
keyboard_shortcuts: "Tastenkürzel" keyboard_shortcuts: "Tastenkürzel"
@ -633,7 +633,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
feature5: "Videoanleitungen" feature5: "Videoanleitungen"
feature6: "Premium Emailsupport" feature6: "Premium Emailsupport"
feature7: "Private <strong>Klans</strong>" feature7: "Private <strong>Klans</strong>"
# feature8: "<strong>No ads!</strong>" feature8: "<strong>Keine Werbung!</strong>"
free: "Kostenlos" free: "Kostenlos"
month: "Monat" month: "Monat"
must_be_logged: "Du musst eingeloggt sein. Bitte kreiere einen Account oder logge dich oben im Menü ein." must_be_logged: "Du musst eingeloggt sein. Bitte kreiere einen Account oder logge dich oben im Menü ein."
@ -723,12 +723,12 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
skill_docs: skill_docs:
# function: "function" # skill types # function: "function" # skill types
# method: "method" method: "Methode"
# snippet: "snippet" # snippet: "snippet"
# number: "number" # number: "number"
# array: "array" # array: "array"
# object: "object" # object: "object"
# string: "string" string: "String"
writable: "beschreibbar" # Hover over "attack" in Your Skills while playing a level to see most of this writable: "beschreibbar" # Hover over "attack" in Your Skills while playing a level to see most of this
read_only: "schreibgeschützt" read_only: "schreibgeschützt"
action: "Aktion" action: "Aktion"
@ -911,7 +911,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
# create_account: "Create a Teacher Account" # create_account: "Create a Teacher Account"
# create_account_subtitle: "Get access to teacher-only tools for using CodeCombat in the classroom. <strong>Set up a class</strong>, add your students, and <strong>monitor their progress</strong>!" # create_account_subtitle: "Get access to teacher-only tools for using CodeCombat in the classroom. <strong>Set up a class</strong>, add your students, and <strong>monitor their progress</strong>!"
# convert_account_title: "Update to Teacher Account" # convert_account_title: "Update to Teacher Account"
# not: "Not" not: "Nicht"
setup_a_class: "Eine Klasse erstellen" setup_a_class: "Eine Klasse erstellen"
versions: versions:
@ -1180,7 +1180,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
remove_student: "Schüler entfernen" remove_student: "Schüler entfernen"
assign: "Zuordnen" assign: "Zuordnen"
to_assign: "um bezahlte Kurse zuzuordnen." to_assign: "um bezahlte Kurse zuzuordnen."
# student: "Student" student: "Schüler"
teacher: "Lehrer" teacher: "Lehrer"
complete: "Abgeschlossen" complete: "Abgeschlossen"
none: "Keine" none: "Keine"
@ -1842,10 +1842,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
you_can2: "einen Prepaidcode kaufen" you_can2: "einen Prepaidcode kaufen"
you_can3: "der ihrem Account hinzugefügt werden kann oder den Sie verschenken können." you_can3: "der ihrem Account hinzugefügt werden kann oder den Sie verschenken können."
# coppa_deny: coppa_deny:
# text1: "Cant wait to learn programming?" # text1: "Cant wait to learn programming?"
# text2: "Your parents will need to create an account for you to use! Email team@codecombat.com if you have any questions." # text2: "Your parents will need to create an account for you to use! Email team@codecombat.com if you have any questions."
# close: "Close Window" close: "Fenster schließen"
loading_error: loading_error:
could_not_load: "Fehler beim Laden vom Server" could_not_load: "Fehler beim Laden vom Server"
@ -2001,19 +2001,19 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
license: "Lizenz" license: "Lizenz"
oreilly: "Ebook deiner Wahl" oreilly: "Ebook deiner Wahl"
# calendar: calendar:
# year: "Year" year: "Jahr"
# day: "Day" day: "Tag"
# month: "Month" month: "Monat"
# january: "January" january: "Januar"
# february: "February" february: "Februar"
# march: "March" march: "März"
# april: "April" april: "April"
# may: "May" may: "Mai"
# june: "June" june: "Juni"
# july: "July" july: "July"
# august: "August" august: "August"
# september: "September" september: "September"
# october: "October" october: "Oktober"
# november: "November" november: "November"
# december: "December" december: "Dezember"

View file

@ -337,8 +337,13 @@
common: common:
back: "Back" # When used as an action verb, like "Navigate backward" back: "Back" # When used as an action verb, like "Navigate backward"
coming_soon: "Coming soon!"
continue: "Continue" # When used as an action verb, like "Continue forward" continue: "Continue" # When used as an action verb, like "Continue forward"
default_code: "Default Code"
loading: "Loading..." loading: "Loading..."
overview: "Overview"
solution: "Solution"
intro: "Intro"
saving: "Saving..." saving: "Saving..."
sending: "Sending..." sending: "Sending..."
send: "Send" send: "Send"
@ -1316,15 +1321,14 @@
students_assigned: "students assigned" students_assigned: "students assigned"
length: "Length:" length: "Length:"
title: "Courses" # Flat style redesign title: "Courses" # Flat style redesign
subtitle: "Review course guidelines, solutions, and levels" subtitle: "Review course overviews and levels" # {change}
changelog: "View latest changes to course levels." changelog: "View latest changes to course levels."
select_language: "Select language" select_language: "Select language"
select_level: "Select level" select_level: "Select level"
play_level: "Play Level" play_level: "Play Level"
concepts_covered: "Concepts covered" concepts_covered: "Concepts covered"
print_guide: "Print Guide (PDF)" print_guide: "Print Guide (PDF)"
view_guide_online: "View Guide Online (PDF)" view_guide_online: "Level Overviews and Solutions" # {change}
last_updated: "Last updated:"
grants_lifetime_access: "Grants access to all Courses." grants_lifetime_access: "Grants access to all Courses."
enrollment_credits_available: "Licenses Available:" enrollment_credits_available: "Licenses Available:"
description: "Description" # ClassroomSettingsModal description: "Description" # ClassroomSettingsModal
@ -1391,6 +1395,8 @@
select_this_hero: "Select this Hero" select_this_hero: "Select this Hero"
teacher: teacher:
course_solution: "Course Solution"
level_overview_solutions: "Level Overview and Solutions"
teacher_dashboard: "Teacher Dashboard" # Navbar teacher_dashboard: "Teacher Dashboard" # Navbar
my_classes: "My Classes" my_classes: "My Classes"
courses: "Course Guides" courses: "Course Guides"

View file

@ -2001,19 +2001,19 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
# license: "license" # license: "license"
# oreilly: "ebook of your choice" # oreilly: "ebook of your choice"
# calendar: calendar:
# year: "Year" year: "Yıl"
# day: "Day" day: "Gün"
# month: "Month" month: "Ay"
# january: "January" january: "Ocak"
# february: "February" february: "Şubat"
# march: "March" march: "Mart"
# april: "April" april: "Nisan"
# may: "May" may: "Mayıs"
# june: "June" june: "Haziran"
# july: "July" july: "Temmuz"
# august: "August" august: "Ağustos"
# september: "September" september: "Eylül"
# october: "October" october: "Ekim"
# november: "November" november: "Kasım"
# december: "December" december: "Aralık"

View file

@ -0,0 +1,31 @@
#teacher-course-solution-view
background-color: white
color: black
font-family: sans-serif
margin: 0px
padding: 24px
hr
display: block
border-style: inset
border-width: 1px
h1, h2, h3, h4, h5
color: black
font-family: sans-serif
font-variant: normal
margin: 20px 0px
h4
color: gray
img
display: block
margin: auto
p
margin: 16px 0px
.page-break-before
page-break-before: always

View file

@ -17,8 +17,8 @@ block content
.container .container
h1(data-i18n="courses.title") h1(data-i18n="courses.title")
h2(data-i18n="courses.subtitle") h2(data-i18n="courses.subtitle")
p //- p
a(href="https://discourse.codecombat.com/t/course-level-changelog/7352" data-i18n="courses.changelog") //- a(href="https://discourse.codecombat.com/t/course-level-changelog/7352" data-i18n="courses.changelog")
.courses.container .courses.container
- var courses = view.courses.models; - var courses = view.courses.models;
@ -29,7 +29,7 @@ block content
.course.row .course.row
.col-sm-9 .col-sm-9
+course-info(course) +course-info(course)
if me.isTeacher() if me.isTeacher() || me.isAdmin()
.col-sm-3 .col-sm-3
.play-level-form(data-course-id=course.id) .play-level-form(data-course-id=course.id)
.form-group .form-group
@ -85,22 +85,12 @@ mixin course-info(course)
if course.get('concepts').indexOf(concept) !== course.get('concepts').length - 1 if course.get('concepts').indexOf(concept) !== course.get('concepts').length - 1
span.spr , span.spr ,
if me.isTeacher() || view.ownedClassrooms.size() if me.isTeacher() || view.ownedClassrooms.size() || me.isAdmin()
if view.guideLinks[course.id] p
//- a.btn.btn-primary(href=view.guideLinks[course.id] class=(me.isTeacher() ? '': 'disabled')) a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/python") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide Python" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
//- span(data-i18n="courses.print_guide")
a.guide-btn.btn.btn-primary(href=view.guideLinks[course.id].python data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide Python" class=(me.isTeacher() ? '': 'disabled'))
span(data-i18n="courses.view_guide_online") span(data-i18n="courses.view_guide_online")
| &mdash; Python | &mdash; Python
a.guide-btn.btn.btn-primary(href=view.guideLinks[course.id].javascript data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() ? '': 'disabled')) p
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
span(data-i18n="courses.view_guide_online") span(data-i18n="courses.view_guide_online")
| &mdash; JavaScript | &mdash; JavaScript
else
i.small
| (
span(data-i18n='teacher.guides_coming_soon')
| )
if campaign && campaign.get('levelsUpdated')
p.small.m-t-2
span.spr(data-i18n="courses.last_updated")
span= moment(campaign.get('levelsUpdated')).format('LL')

View file

@ -0,0 +1,52 @@
block content
if !me.isTeacher() && !me.isAdmin()
a(href="/")
img#nav-logo(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat")
h2.text-center(data-i18n="teacher.teacher_account_required")
else
.text-center
img(src="http://direct.codecombat.com/images/pages/base/logo.png")
if view.course.loaded
h1 #{view.course.get('name')}
h3 #{view.prettyLanguage}
i= view.course.get('description')
br
br
if view.levels
each level, index in view.levels.models
h2.page-break-before ##{index + 1}. #{level.get('name')}
h3(data-i18n="teacher.level_overview_solutions")
i #{level.get('description')}
div
h4.text-center(data-i18n="common.intro")
if level.get('intro')
p!= level.get('intro')
else
.text-center
i(data-i18n="common.coming_soon")
h4.text-center(data-i18n="common.default_code")
if level.get('begin')
pre!= level.get('begin')
else
.text-center
i(data-i18n="common.coming_soon")
div.overview
br
h4.text-center(data-i18n="common.overview")
if level.get('guide')
p!= level.get('guide')
else
.text-center
i(data-i18n="common.coming_soon")
h4.text-center
span= level.get('name')
span.spl(data-i18n="common.solution")
if level.get('solution')
pre!= level.get('solution')
else
.text-center
i(data-i18n="common.coming_soon")
hr
br

View file

@ -0,0 +1,47 @@
RootView = require 'views/core/RootView'
CocoCollection = require 'collections/CocoCollection'
Course = require 'models/Course'
Level = require 'models/Level'
module.exports = class TeacherCourseSolutionView extends RootView
id: 'teacher-course-solution-view'
template: require 'templates/teachers/teacher-course-solution-view'
getTitle: -> $.i18n.t('teacher.course_solution')
initialize: (options, @courseID, @language) ->
if me.isTeacher() or me.isAdmin()
@prettyLanguage = @camelCaseLanguage(@language)
@course = new Course(_id: @courseID)
@supermodel.trackRequest(@course.fetch())
@levels = new CocoCollection([], { url: "/db/course/#{@courseID}/level-solutions", model: Level})
@supermodel.loadCollection(@levels, 'levels', {cache: false})
super(options)
camelCaseLanguage: (language) ->
return language if _.isEmpty(language)
return 'JavaScript' if language is 'javascript'
language.charAt(0).toUpperCase() + language.slice(1)
hideWrongLanguage: (s) ->
return '' unless s
s.replace /```([a-z]+)[^`]+```/gm, (a, l) =>
return '' if l isnt @language
a
onLoaded: ->
for level in @levels?.models
articles = level.get('documentation').specificArticles
if articles
guide = articles.filter((x) => x.name == "Overview").pop()
level.set 'guide', marked(@hideWrongLanguage(guide.body)) if guide
intro = articles.filter((x) => x.name == "Intro").pop()
level.set 'intro', marked(@hideWrongLanguage(intro.body)) if intro
heroPlaceholder = level.get('thangs').filter((x) => x.id == 'Hero Placeholder').pop()
comp = heroPlaceholder?.components.filter((x) => x.original.toString() == '524b7b5a7fc0f6d51900000e' ).pop()
programmableMethod = comp?.config.programmableMethods.plan
if programmableMethod
level.set 'begin', _.template(programmableMethod.languages[@language] or programmableMethod.source)(programmableMethod.context)
solution = programmableMethod.solutions?.find (x) => x.language is @language
level.set 'solution', _.template(solution?.source)(programmableMethod.context)
@render?()

View file

@ -1,4 +1,5 @@
errors = require '../commons/errors' errors = require '../commons/errors'
log = require 'winston'
wrap = require 'co-express' wrap = require 'co-express'
database = require '../commons/database' database = require '../commons/database'
mongoose = require 'mongoose' mongoose = require 'mongoose'
@ -11,6 +12,24 @@ Level = require '../models/Level'
parse = require '../commons/parse' parse = require '../commons/parse'
module.exports = module.exports =
fetchLevelSolutions: wrap (req, res) ->
unless req.user?.isTeacher() or req.user?.isAdmin()
log.debug "courses.fetchLevelSolutions: level solutions only for teachers, (#{req.user?.id})"
throw new errors.Forbidden()
course = yield database.getDocFromHandle(req, Course)
throw new errors.NotFound('Course not found.') unless course
campaign = yield Campaign.findById course.get('campaignID')
throw new errors.NotFound('Campaign not found.') unless campaign
levelOriginals = (mongoose.Types.ObjectId(levelID) for levelID of campaign.get('levels'))
query = { original: { $in: levelOriginals }, slug: { $exists: true }}
select = {documentation: 1, intro: 1, name: 1, slug: 1, thangs: 1}
levels = yield Level.find(query).select(select).lean()
res.status(200).send(levels)
fetchNextLevel: wrap (req, res) -> fetchNextLevel: wrap (req, res) ->
levelOriginal = req.params.levelOriginal levelOriginal = req.params.levelOriginal
if not database.isID(levelOriginal) if not database.isID(levelOriginal)
@ -18,7 +37,7 @@ module.exports =
course = yield database.getDocFromHandle(req, Course) course = yield database.getDocFromHandle(req, Course)
if not course if not course
throw new errors.NotFound('Course Instance not found.') throw new errors.NotFound('Course not found.')
campaign = yield Campaign.findById course.get('campaignID') campaign = yield Campaign.findById course.get('campaignID')
if not campaign if not campaign
@ -43,7 +62,6 @@ module.exports =
dbq = Level.findOne({original: mongoose.Types.ObjectId(nextLevelOriginal)}) dbq = Level.findOne({original: mongoose.Types.ObjectId(nextLevelOriginal)})
dbq.sort({ 'version.major': -1, 'version.minor': -1 }) dbq.sort({ 'version.major': -1, 'version.minor': -1 })
dbq.select(parse.getProjectFromReq(req)) dbq.select(parse.getProjectFromReq(req))
level = yield dbq level = yield dbq

View file

@ -82,6 +82,7 @@ module.exports.setup = (app) ->
Course = require '../models/Course' Course = require '../models/Course'
app.get('/db/course', mw.courses.get(Course)) app.get('/db/course', mw.courses.get(Course))
app.get('/db/course/:handle', mw.rest.getByHandle(Course)) app.get('/db/course/:handle', mw.rest.getByHandle(Course))
app.get('/db/course/:handle/level-solutions', mw.courses.fetchLevelSolutions)
app.get('/db/course/:handle/levels/:levelOriginal/next', mw.courses.fetchNextLevel) app.get('/db/course/:handle/levels/:levelOriginal/next', mw.courses.fetchNextLevel)
app.get('/db/course_instance/-/non-hoc', mw.auth.checkHasPermission(['admin']), mw.courseInstances.fetchNonHoc) app.get('/db/course_instance/-/non-hoc', mw.auth.checkHasPermission(['admin']), mw.courseInstances.fetchNonHoc)

View file

@ -130,3 +130,64 @@ describe 'GET /db/course/:handle/levels/:levelOriginal/next', ->
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course/#{@courseB.id}/levels/#{@levelA.id}/next"), json: true } [res, body] = yield request.getAsync { uri: utils.getURL("/db/course/#{@courseB.id}/levels/#{@levelA.id}/next"), json: true }
expect(res.statusCode).toBe(404) expect(res.statusCode).toBe(404)
done() done()
describe 'GET /db/course/:handle/level-solutions', ->
beforeEach utils.wrap (done) ->
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
admin = yield utils.initAdmin()
yield utils.loginUser(admin)
levelJSON = { name: 'A', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
expect(res.statusCode).toBe(200)
@levelA = yield Level.findById(res.body._id)
paredLevelA = _.pick(res.body, 'name', 'original', 'type')
levelJSON = { name: 'B', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
expect(res.statusCode).toBe(200)
@levelB = yield Level.findById(res.body._id)
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
campaignJSONA = { name: 'Campaign A', levels: {} }
campaignJSONA.levels[paredLevelA.original] = paredLevelA
campaignJSONA.levels[paredLevelB.original] = paredLevelB
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
@campaignA = yield Campaign.findById(res.body._id)
@courseA = Course({name: 'Course A', campaignID: @campaignA._id, releasePhase: 'released'})
yield @courseA.save()
done()
describe 'when admin', ->
it 'returns level solutions', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course/#{@courseA.id}/level-solutions"), json: true }
expect(res.statusCode).toBe(200)
expect(body.length).toEqual(2)
expect(body[0].slug).toEqual('a')
done()
describe 'when teacher', ->
beforeEach utils.wrap (done) ->
teacher = yield utils.initUser({role: 'teacher'})
yield utils.loginUser(teacher)
done()
it 'returns level solutions', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course/#{@courseA.id}/level-solutions"), json: true }
expect(res.statusCode).toBe(200)
expect(body.length).toEqual(2)
expect(body[1].slug).toEqual('b')
done()
describe 'when anonymous', ->
beforeEach utils.wrap (done) ->
yield utils.logout()
done()
it 'returns 403', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course/#{@courseA.id}/level-solutions"), json: true }
expect(res.statusCode).toBe(403)
done()

View file

@ -58,7 +58,7 @@ describe 'SuperModel', ->
request = jasmine.Ajax.requests.mostRecent() request = jasmine.Ajax.requests.mostRecent()
expect(request).toBeDefined() expect(request).toBeDefined()
describe 'timeout handling', -> xdescribe 'timeout handling', ->
beforeEach -> beforeEach ->
jasmine.clock().install() jasmine.clock().install()
afterEach -> afterEach ->