mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-27 06:23:41 -04:00
Merge branch 'master' into courses-vhoc
This commit is contained in:
commit
9dbd4d296a
23 changed files with 239 additions and 80 deletions
app
lib
locale
schemas/models
styles/play/ladder
templates/play/ladder
views
scripts/mongodb/migrations
server
campaigns
prepaids
trial_requests
test/server/functional
|
@ -10,6 +10,7 @@ CocoClass = require 'core/CocoClass'
|
|||
AudioPlayer = require 'lib/AudioPlayer'
|
||||
app = require 'core/application'
|
||||
World = require 'lib/world/world'
|
||||
utils = require 'core/utils'
|
||||
|
||||
# This is an initial stab at unifying loading and setup into a single place which can
|
||||
# monitor everything and keep a LoadingScreen visible overall progress.
|
||||
|
@ -105,6 +106,8 @@ module.exports = class LevelLoader extends CocoClass
|
|||
loadDependenciesForSession: (session) ->
|
||||
if me.id isnt session.get 'creator'
|
||||
session.patch = session.save = -> console.error "Not saving session, since we didn't create it."
|
||||
else if codeLanguage = utils.getQueryVariable 'codeLanguage'
|
||||
session.set 'codeLanguage', codeLanguage
|
||||
@loadCodeLanguagesForSession session
|
||||
if session is @session
|
||||
@addSessionBrowserInfo session
|
||||
|
|
|
@ -247,7 +247,7 @@
|
|||
victory_title_suffix: " Complete"
|
||||
victory_sign_up: "Sign Up to Save Progress"
|
||||
victory_sign_up_poke: "Want to save your code? Create a free account!"
|
||||
victory_rate_the_level: "Rate the level: " # Only in old-style levels.
|
||||
victory_rate_the_level: "How fun was this level?" # {change}
|
||||
victory_return_to_ladder: "Return to Ladder"
|
||||
victory_play_continue: "Continue"
|
||||
victory_saving_progress: "Saving Progress"
|
||||
|
|
|
@ -80,7 +80,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
adjust_volume: "Regola il volume"
|
||||
campaign_multiplayer: "Arene multigiocatore"
|
||||
campaign_multiplayer_description: "... nelle quali programmi faccia a faccia contro altri giocatori."
|
||||
# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas"
|
||||
campaign_old_multiplayer: "(Deprecato) Vecchia Arena multiplayer"
|
||||
campaign_old_multiplayer_description: "Reliquie di un'epoca più civilizzata. Nessuna simulazione viene eseguita per queste arene multi-giocatore più vecchie e senza eroi" #"Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas."
|
||||
|
||||
share_progress_modal:
|
||||
|
@ -218,7 +218,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
play_level:
|
||||
done: "Fatto"
|
||||
next_game: "Prossimo gioco" #"Next game"
|
||||
# show_menu: "Show game menu"
|
||||
show_menu: "Visualizza menu gioco"
|
||||
home: "Pagina iniziale" # Not used any more, will be removed soon.
|
||||
level: "Livello" # Like "Level: Dungeons of Kithgard"
|
||||
skip: "Salta"
|
||||
|
@ -306,8 +306,8 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
tip_baby_coders: "Nel futuro, persino i neonati saranno Arcimaghi."
|
||||
tip_morale_improves: "Il caricamento continuerà fino a che il morale migliora." #"Loading will continue until morale improves."
|
||||
tip_all_species: "Crediamo che chiunque debba avere le stesse opportunità di imparare a programmare."
|
||||
# tip_reticulating: "Reticulating spines."
|
||||
# tip_harry: "Yer a Wizard, "
|
||||
tip_reticulating: "Reticolazione spine"
|
||||
tip_harry: "Yer il mago, "
|
||||
tip_great_responsibility: "Da grandi abilità di programmazione derivano grandi responsabilità."
|
||||
tip_munchkin: "Se non mangi la tua verdura, un munchkin verrà a cercarti mentre dormi."
|
||||
tip_binary: "Ci sono solo 10 tipi di persone al mondo: quelli che capiscono il binario, e quelli che non lo capiscono." #"There are only 10 types of people in the world: those who understand binary, and those who don't."
|
||||
|
@ -335,7 +335,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
tip_move_forward: "Qualsiasi cosa tu faccia, vai sempre avanti. - Martin Luther King Jr."
|
||||
tip_google: "Hai un problema che non riesci a risolvere? Cerca su Google!"
|
||||
tip_adding_evil: "Aggiungendo un pizzico di malvagità."
|
||||
# tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven"
|
||||
tip_hate_computers: "A proposito delle persone che pensano di odiare i computer. Cio' che odiano realmente sono i programmatori scarsi - Larry Niven"
|
||||
tip_open_source_contribute: "Puoi aiutare CodeCombat a migliorare!"
|
||||
tip_recurse: "Iterare e umano, usare la ricorsione è divino. - L. Peter Deutsch"
|
||||
tip_free_your_mind: "Devi liberarti di tutto questo, Neo. Paura, dubbio, sfiducia. Libera la tua mente. - Morpheus"
|
||||
|
@ -431,60 +431,60 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
parents: "Per i genitori"
|
||||
parents_title: "Caro Genitore: Tuo figlio/a sta imparando a programmare. Vuoi continuare ad aiutarlo/a ? "
|
||||
parents_blurb1: "Tuo figlio/a ha giocato a _nLevels__ livelli ed ha imparato le basi della programmazione. Aiutalo/a a coltivare i suoi interessi ed acquistagli un’abbonamento così potrà continuare a giocare."
|
||||
# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?"
|
||||
# parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers."
|
||||
# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe."
|
||||
parents_blurb1a: "La programmazione e' una capacita' che indubbiamente sara' utile a tuo figlio da adulto. Nel 2020 una capacita' basilare di programmazione sara' richiesta dal 77% dei lavori e la richiesta a livello mondiale di ingegneri del software e' in continua crescita. Lo sai che la laurea in Informatica e' la piu' pagata sul mercato?"
|
||||
parents_blurb2: "Per $9.99 dollari/mese, tuo figlio ricevera' settimanalmente nuove avventure e un supporto personale via email fornito d aprogrammatori professionisti."
|
||||
parents_blurb3: "Nessun rischio: 100% restituzione dei soldi con un semplice processo di deregistrazione"
|
||||
payment_methods: "Metodi di Pagamento"
|
||||
payment_methods_title: "Metodi di Pagamento Accetati"
|
||||
payment_methods_blurb1: "Attualmente accettiamo come metodi di pagamento la carta di credito e Alipay."
|
||||
payment_methods_blurb2: "Se necessiti di un forma di pagamento diverso.Per favore contattaci"
|
||||
sale_already_subscribed: "Sei già abbonato!" #"You're already subscribed!"
|
||||
sale_blurb1: "Risparmia il 35%"
|
||||
# sale_blurb2: "off regular subscription price of $120 for a whole year!"
|
||||
sale_blurb2: "rispetto all'abbinamento ordinario di 120$ per l'intero anno!"
|
||||
sale_button: "Saldi!" #"Sale!"
|
||||
sale_button_title: "Risparmi il 35% quando compri l'abbonamento per 1 anno"
|
||||
sale_click_here: "Clicca qui"
|
||||
# sale_ends: "Ends"
|
||||
# sale_extended: "*Existing subscriptions will be extended by 1 year."
|
||||
# sale_feature_here: "Here's what you'll get:"
|
||||
# sale_feature2: "Access to 9 powerful <strong>new heroes</strong> with unique skills!"
|
||||
# sale_feature4: "<strong>42,000 bonus gems</strong> awarded immediately!"
|
||||
# sale_continue: "Ready to continue adventuring?"
|
||||
# sale_limited_time: "Limited time offer!"
|
||||
# sale_new_heroes: "New heroes!"
|
||||
# sale_title: "Back to School Sale"
|
||||
# sale_view_button: "Buy 1 year subscription for"
|
||||
sale_ends: "Fine"
|
||||
sale_extended: "*Gli abbonamenti in corso verranno estesi per 1 anno."
|
||||
sale_feature_here: "Qui cio' che otterrai:"
|
||||
sale_feature2: "Accesso a 9 nuovi potenti <strong>eroi</strong> con abilita' uniche!"
|
||||
sale_feature4: "<strong>42,000 gemme premio</strong> riconosciute immediatamente!"
|
||||
sale_continue: "Pronto per continuare l'avventura?"
|
||||
sale_limited_time: "Offerta limitata!"
|
||||
sale_new_heroes: "Nuovi eroi!"
|
||||
sale_title: "Saldi di inizio scuola"
|
||||
sale_view_button: "Compra un abbonamento annuale per "
|
||||
stripe_description: "Sottoscrizione mensile"
|
||||
# stripe_description_year_sale: "1 Year Subscription (35% discount)"
|
||||
# subscription_required_to_play: "You'll need a subscription to play this level."
|
||||
# unlock_help_videos: "Subscribe to unlock all video tutorials."
|
||||
stripe_description_year_sale: "Abbonamneto annuale (sconto 35%)"
|
||||
subscription_required_to_play: "Devi essere abbonato per giocare su questo livello."
|
||||
unlock_help_videos: "Abbonati per accedere a tutti i tutorial video."
|
||||
personal_sub: "Sottoscrizione Personale" # Accounts Subscription View below
|
||||
# loading_info: "Loading subscription information..."
|
||||
# managed_by: "Managed by"
|
||||
# will_be_cancelled: "Will be cancelled on"
|
||||
# currently_free: "You currently have a free subscription"
|
||||
# currently_free_until: "You currently have a subscription until"
|
||||
# was_free_until: "You had a free subscription until"
|
||||
# managed_subs: "Managed Subscriptions"
|
||||
# managed_subs_desc: "Add subscriptions for other players (students, children, etc.)"
|
||||
# managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide."
|
||||
# group_discounts: "Group discounts"
|
||||
# group_discounts_1: "We also offer group discounts for bulk subscriptions."
|
||||
loading_info: "Caricamento informazioni abbonamento..."
|
||||
managed_by: "Gestito da"
|
||||
will_be_cancelled: "Sara' rimosso il"
|
||||
currently_free: "Al momento hai un abbonamento gratuito"
|
||||
currently_free_until: "Hai un abbonamento valido fino al"
|
||||
was_free_until: "Hai avuto un abbonamento gratuito fino al "
|
||||
managed_subs: "Gestione Abbonamenti"
|
||||
managed_subs_desc: "Aggiungi un abbonamento per altri giocatori (studenti, bambini, etc.)"
|
||||
managed_subs_desc_2: "I beneficiari devono avere un abbonamento CodeCombat associato con l'email fornito."
|
||||
group_discounts: "Sconto comitiva"
|
||||
group_discounts_1: "Offriamo sconto comitiva anche per abbonamenti in grandi volumi."
|
||||
group_discounts_1st: "Prima sottoscrizione"
|
||||
group_discounts_full: "Prezzo completo"
|
||||
group_discounts_2nd: "Sottoscrizione 2-11" #"Subscriptions 2-11"
|
||||
group_discounts_20: "20% disconto"
|
||||
group_discounts_12th: "Sottoscrizione 12+" #"Subscriptions 12+"
|
||||
group_discounts_40: "40% di sconto"
|
||||
# subscribing: "Subscribing..."
|
||||
# recipient_emails_placeholder: "Enter email address to subscribe, one per line."
|
||||
subscribing: "Abbonamento..."
|
||||
recipient_emails_placeholder: "Inserisci l'indirizzo email per abbonarti, uno per linea."
|
||||
subscribe_users: "Iscrivere Utenti"
|
||||
users_subscribed: "Utenti iscritti:"
|
||||
# no_users_subscribed: "No users subscribed, please double check your email addresses."
|
||||
# current_recipients: "Current Recipients"
|
||||
# unsubscribing: "Unsubscribing"
|
||||
# subscribe_prepaid: "Click Subscribe to use prepaid code"
|
||||
# using_prepaid: "Using prepaid code for monthly subscription"
|
||||
no_users_subscribed: "Utente non abbonato, per favore verifica il tuo indirizzo di email."
|
||||
current_recipients: "Destinatari attuali"
|
||||
unsubscribing: "Deregistrazione"
|
||||
subscribe_prepaid: "Clicca su Registrazione per usare un codice pre pagato"
|
||||
using_prepaid: "Usa un codice pre pagato per un abbonamento mensile"
|
||||
|
||||
choose_hero:
|
||||
choose_hero: "Scegli il tuo eroe"
|
||||
|
@ -1612,7 +1612,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
# inactive_developers: "Inactive Developers"
|
||||
|
||||
admin:
|
||||
# av_espionage: "Espionage" # Really not important to translate /admin controls.
|
||||
av_espionage: "Spionaggio" # Really not important to translate /admin controls.
|
||||
av_espionage_placeholder: "Email o nome utente"
|
||||
av_usersearch: "Cerca utenti"
|
||||
av_usersearch_placeholder: "Email, username, nome, qualsiasi cosa"
|
||||
|
@ -1621,8 +1621,8 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
av_entities_sub_title: "Entità"
|
||||
av_entities_users_url: "Utenti"
|
||||
av_entities_active_instances_url: "Istanze attive"
|
||||
# av_entities_employer_list_url: "Employer List"
|
||||
# av_entities_candidates_list_url: "Candidate List"
|
||||
av_entities_employer_list_url: "Lista datore di lavoro"
|
||||
av_entities_candidates_list_url: "Lista Candidati"
|
||||
av_entities_user_code_problems_list_url: "Lista problemi codice utenti"
|
||||
av_other_sub_title: "Altro"
|
||||
av_other_debug_base_url: "Base (per il debug di base.jade)"
|
||||
|
|
|
@ -7,6 +7,7 @@ TrialRequestSchema = c.object {
|
|||
|
||||
_.extend TrialRequestSchema.properties,
|
||||
applicant: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}])
|
||||
created: c.date()
|
||||
prepaidCode: c.objectId()
|
||||
reviewDate: c.date({readOnly: true})
|
||||
reviewer: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}])
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
.name-col-cell
|
||||
max-width: 170px
|
||||
|
||||
&.ai
|
||||
color: #3f44bf
|
||||
|
||||
.histogram-display
|
||||
height: 130px
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
td.hero-portrait-cell(style="background-image: url(/file/db/thang.type/#{(session.get('heroConfig') || {}).thangType || '529ffbf1cf1818f2be000001'}/portrait.png)")
|
||||
td.rank-cell= rank + 1
|
||||
td.score-cell= Math.round(sessionStats.totalScore * 100)
|
||||
td.name-col-cell= session.get('creatorName') || "Anonymous"
|
||||
td(class='name-col-cell' + ((new RegExp('(Simple|Shaman|Brawler|Chieftain|Thoktar) AI')).test(session.get('creatorName')) ? ' ai' : ''))= session.get('creatorName') || "Anonymous"
|
||||
td.age-cell= moment(session.get('submitDate')).fromNow().replace('a few ', '')
|
||||
td.fight-cell
|
||||
a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}" + (league ? "&league=" + league.id : ""))
|
||||
|
@ -51,7 +51,7 @@
|
|||
td.hero-portrait-cell(style="background-image: url(/file/db/thang.type/#{(session.get('heroConfig') || {}).thangType || '529ffbf1cf1818f2be000001'}/portrait.png)")
|
||||
td.rank-cell= session.rank
|
||||
td.score-cell= Math.round(sessionStats.totalScore * 100)
|
||||
td.name-col-cell= session.get('creatorName') || "Anonymous"
|
||||
td(class='name-col-cell' + ((new RegExp('(Simple|Shaman|Brawler|Chieftain|Thoktar) AI')).test(session.get('creatorName')) ? ' ai' : ''))= session.get('creatorName') || "Anonymous"
|
||||
td.age-cell= moment(session.get('submitDate')).fromNow().replace('a few ', '')
|
||||
td.fight-cell
|
||||
a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}" + (league ? "&league=" + league.id : ""))
|
||||
|
|
|
@ -7,6 +7,6 @@ module.exports = class TeachersView extends RootView
|
|||
|
||||
constructor: ->
|
||||
super()
|
||||
|
||||
# Redirect to HoC version of /courses/teachers until we update the /teachers landing page
|
||||
application.router.navigate "/courses/teachers?hoc=true", trigger: true
|
||||
_.defer ->
|
||||
# Redirect to HoC version of /courses/teachers until we update the /teachers landing page
|
||||
application.router.navigate "/courses/teachers?hoc=true", trigger: true
|
||||
|
|
|
@ -37,11 +37,9 @@ module.exports = class AccountSettingsView extends CocoView
|
|||
|
||||
#- Form input callbacks
|
||||
onChangePanelInput: (e) ->
|
||||
return if $(e.target).closest('.form').attr('id') in ['reset-progress-form', 'delete-account-form']
|
||||
$(e.target).addClass 'changed'
|
||||
if (JSON.stringify(document.getElementById('email1').className)).indexOf("changed") > -1 or (JSON.stringify(document.getElementById('password1').className)).indexOf("changed") > -1
|
||||
$(e.target).removeClass 'changed'
|
||||
else
|
||||
@trigger 'input-changed'
|
||||
@trigger 'input-changed'
|
||||
|
||||
onClickToggleAllButton: ->
|
||||
subs = @getSubscriptions()
|
||||
|
|
|
@ -102,7 +102,7 @@ module.exports = class PatchesView extends RootView
|
|||
@render()
|
||||
|
||||
modelNamesRequest = @supermodel.addRequestResource 'patches', {
|
||||
url: "/db/#{collection}/names"
|
||||
url: "/db/#{collection.replace('_', '.')}/names"
|
||||
data: {ids: ids}
|
||||
method: 'POST'
|
||||
success: success
|
||||
|
|
|
@ -196,7 +196,7 @@ module.exports = class ClanDetailsView extends RootView
|
|||
if level.concepts?
|
||||
for concept in level.concepts
|
||||
@conceptsProgression.push concept unless concept in @conceptsProgression
|
||||
if level.type is 'hero-ladder'
|
||||
if level.type is 'hero-ladder' and level.slug not in ['capture-their-flag']
|
||||
@arenas.push level
|
||||
@campaignLevelProgressions.push campaignLevelProgression
|
||||
@render?()
|
||||
|
|
|
@ -221,9 +221,9 @@ module.exports = class CourseDetailsView extends RootView
|
|||
levelID = $(e.target).data('level-id')
|
||||
level = @campaign.get('levels')[levelID]
|
||||
if level.type is 'course-ladder'
|
||||
Backbone.Mediator.publish 'router:navigate', {
|
||||
route: '/play/ladder/' + levelSlug + '/course/' + @courseInstance.id
|
||||
}
|
||||
route = '/play/ladder/' + levelSlug
|
||||
route += '/course/' + @courseInstance.id if @courseInstance.get('members').length > 1 # No league for solo courses
|
||||
Backbone.Mediator.publish 'router:navigate', route: route
|
||||
else
|
||||
Backbone.Mediator.publish 'router:navigate', {
|
||||
route: @getLevelURL levelSlug
|
||||
|
|
|
@ -10,13 +10,13 @@ CourseInstance = require 'models/CourseInstance'
|
|||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/teacher-courses-view'
|
||||
utils = require 'core/utils'
|
||||
InviteToClassroomModal = require 'views/courses/InviteToClassroomModal'
|
||||
InviteToClassroomModal = require 'views/courses/InviteToClassroomModal'
|
||||
ClassroomSettingsModal = require 'views/courses/ClassroomSettingsModal'
|
||||
|
||||
module.exports = class TeacherCoursesView extends RootView
|
||||
id: 'teacher-courses-view'
|
||||
template: template
|
||||
|
||||
|
||||
events:
|
||||
'click #create-new-class-btn': 'onClickCreateNewclassButton'
|
||||
'click .add-students-btn': 'onClickAddStudentsButton'
|
||||
|
@ -67,7 +67,7 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
@listenTo classroom, 'sync', ->
|
||||
classroom.saving = false
|
||||
@fillMissingCourseInstances()
|
||||
|
||||
|
||||
renderManageTab: ->
|
||||
isActive = @$('#manage-tab-pane').hasClass('active')
|
||||
@renderSelectors('#manage-tab-pane')
|
||||
|
@ -85,7 +85,7 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
classroom = @classrooms.get(classroomID)
|
||||
modal = new InviteToClassroomModal({classroom: classroom})
|
||||
@openModalView(modal)
|
||||
|
||||
|
||||
onLoaded: ->
|
||||
super()
|
||||
@linkCourseIntancesToCourses()
|
||||
|
@ -94,7 +94,7 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
linkCourseIntancesToCourses: ->
|
||||
for courseInstance in @courseInstances.models
|
||||
courseInstance.course = @courses.get(courseInstance.get('courseID'))
|
||||
|
||||
|
||||
fillMissingCourseInstances: ->
|
||||
# TODO: Give teachers control over which courses are enabled for a given class.
|
||||
# Add/remove course instances and columns in the view to match.
|
||||
|
@ -135,7 +135,7 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
@usersToRedeem = new CocoCollection(_.values(usersToRedeem), {model: User})
|
||||
@numCourseInstancesToAddTo = checkedBoxes.length
|
||||
@renderSelectors '#fixed-area'
|
||||
|
||||
|
||||
onClickSaveChangesButton: ->
|
||||
@$('.course-instance-membership-checkbox').attr('disabled', true)
|
||||
checkedBoxes = @$('.course-instance-membership-checkbox:checked')
|
||||
|
@ -154,12 +154,12 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
@state = 'saving-changes'
|
||||
@renderSelectors '#fixed-area'
|
||||
@redeemUsers()
|
||||
|
||||
|
||||
redeemUsers: ->
|
||||
if not @usersToRedeem.size()
|
||||
@addMemberships()
|
||||
return
|
||||
|
||||
|
||||
user = @usersToRedeem.first()
|
||||
prepaid = @prepaids.find (prepaid) -> prepaid.openSpots()
|
||||
$.ajax({
|
||||
|
@ -186,7 +186,7 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
@renderSelectors '#fixed-area'
|
||||
document.location.reload()
|
||||
return
|
||||
|
||||
|
||||
membershipAddition = @membershipAdditions.first()
|
||||
courseInstance = membershipAddition.get('courseInstance')
|
||||
userID = membershipAddition.get('userID')
|
||||
|
@ -210,4 +210,4 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
})
|
||||
|
||||
onClickManageTabLink: ->
|
||||
@$('.nav-tabs a[href="#manage-tab-pane"]').tab('show')
|
||||
@$('.nav-tabs a[href="#manage-tab-pane"]').tab('show')
|
||||
|
|
|
@ -150,7 +150,7 @@ module.exports = class LadderTabView extends CocoView
|
|||
|
||||
refreshLadder: ->
|
||||
@supermodel.resetProgress()
|
||||
@ladderLimit ?= parseInt @getQueryVariable('top_players', 20)
|
||||
@ladderLimit ?= parseInt @getQueryVariable('top_players', if @options.league then 100 else 20)
|
||||
for team in @teams
|
||||
if oldLeaderboard = @leaderboards[team.id]
|
||||
@supermodel.removeModelResource oldLeaderboard
|
||||
|
|
|
@ -11,10 +11,14 @@ module.exports.teamDataFromLevel = (level) ->
|
|||
color = teamConfigs[team].color
|
||||
bgColor = hslToHex([color.hue, color.saturation, color.lightness + (1 - color.lightness) * 0.5])
|
||||
primaryColor = hslToHex([color.hue, 0.5, 0.5])
|
||||
if level.get('slug') in ['wakka-maul']
|
||||
displayName = _.string.titleize(team)
|
||||
else
|
||||
displayName = $.i18n.t("ladder.#{team}") # Use Red/Blue instead of Humans/Ogres
|
||||
teams.push({
|
||||
id: team
|
||||
name: _.string.titleize(team)
|
||||
displayName: $.i18n.t("ladder.#{team}") # Use Red/Blue instead of Humans/Ogres
|
||||
displayName: displayName
|
||||
otherTeam: otherTeam
|
||||
otherTeamDisplayName: $.i18n.t("ladder.#{otherTeam}")
|
||||
bgColor: bgColor
|
||||
|
|
|
@ -33,9 +33,9 @@ class LevelSessionsCollection extends CocoCollection
|
|||
@url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID,state.difficulty,playtime"
|
||||
|
||||
class CampaignsCollection extends CocoCollection
|
||||
url: '/db/campaign/-/overworld'
|
||||
# We don't send all of levels, just the parts needed in countLevels
|
||||
url: '/db/campaign/-/overworld?project=slug,adjacentCampaigns,name,fullName,description,i18n,color,levels'
|
||||
model: Campaign
|
||||
project: ['name', 'fullName', 'description', 'i18n']
|
||||
|
||||
module.exports = class CampaignView extends RootView
|
||||
id: 'campaign-view'
|
||||
|
|
|
@ -381,12 +381,13 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
returnToLadder: ->
|
||||
# Preserve the supermodel as we navigate back to the ladder.
|
||||
viewArgs = [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @level.get('slug')]
|
||||
ladderURL = "/play/ladder/#{@level.get('slug') || @level.id}#my-matches"
|
||||
ladderURL = "/play/ladder/#{@level.get('slug') || @level.id}"
|
||||
if leagueID = @getQueryVariable 'league'
|
||||
leagueType = if @level.get('type') is 'course-ladder' then 'course' else 'clan'
|
||||
viewArgs.push leagueType
|
||||
viewArgs.push leagueID
|
||||
ladderURL += "/#{leagueType}/#{leagueID}"
|
||||
ladderURL += '#my-matches'
|
||||
Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: viewArgs
|
||||
|
||||
playSelectionSound: (hero, preload=false) ->
|
||||
|
|
|
@ -74,12 +74,13 @@ module.exports = class MultiplayerView extends CocoView
|
|||
onGameSubmitted: (e) ->
|
||||
# Preserve the supermodel as we navigate back to the ladder.
|
||||
viewArgs = [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @levelID]
|
||||
ladderURL = "/play/ladder/#{@levelID}#my-matches"
|
||||
ladderURL = "/play/ladder/#{@levelID}"
|
||||
if leagueID = @getQueryVariable 'league'
|
||||
leagueType = if @level?.get('type') is 'course-ladder' then 'course' else 'clan'
|
||||
viewArgs.push leagueType
|
||||
viewArgs.push leagueID
|
||||
ladderURL += "/#{leagueType}/#{leagueID}"
|
||||
ladderURL += '#my-matches'
|
||||
Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: viewArgs
|
||||
|
||||
updateLinkSection: ->
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// Add user.courseInstances properties and then add those to session leagues
|
||||
// Usage:
|
||||
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
||||
|
||||
addCourseInstancesToUsers();
|
||||
|
||||
function uniq(array) {
|
||||
var u = {}, a = [];
|
||||
for(var i = 0, l = array.length; i < l; ++i){
|
||||
if(u.hasOwnProperty(array[i])) {
|
||||
continue;
|
||||
}
|
||||
a.push(array[i]);
|
||||
u[array[i]] = 1;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function addCourseInstancesToUsers() {
|
||||
print("Adding courseInstances to users...");
|
||||
var cursor = db.course.instances.find({$where: "this.members && this.members.length > 1"}, {members: 1});
|
||||
print("CourseInstances with users found: " + cursor.count());
|
||||
var courseInstances = cursor.toArray();
|
||||
var userIDList = [];
|
||||
courseInstances.forEach(function (courseInstance, courseInstanceIndex) {
|
||||
userIDList = userIDList.concat(courseInstance.members);
|
||||
var conditions = {_id: {$in: courseInstance.members}};
|
||||
var operations = {$addToSet: {courseInstances: courseInstance._id}};
|
||||
//print('Fetching', JSON.stringify(conditions), 'with operations', JSON.stringify(operations));
|
||||
//print("... Have this many:", db.users.count(conditions));
|
||||
var result = db.users.update(conditions, operations, {multi: true});
|
||||
if (courseInstanceIndex % 100 === 0)
|
||||
print("Done", courseInstanceIndex, "\tof", courseInstances.length, "course instances.");
|
||||
});
|
||||
print("Done adding course instances to users; now going to add them to sessions for leagues.");
|
||||
addCourseInstancesToSessions(userIDList);
|
||||
}
|
||||
|
||||
function addCourseInstancesToSessions(userIDList) {
|
||||
userIDList = uniq(userIDList);
|
||||
print("Adding courseInstance leagues to sessions for", userIDList.length, "users...");
|
||||
|
||||
var cursor = db.users.find({_id: {$in: userIDList}, courseInstances: {$exists: true}}, {courseInstances: 1, name: 1, leagues: 1});
|
||||
print("Users with courseInstances found: " + cursor.count(), '-- supposed to have:', userIDList.length);
|
||||
var users = cursor.toArray();
|
||||
var arenas = [
|
||||
"5442ba0e1e835500007eb1c7",
|
||||
"550363b4ec31df9c691ab629",
|
||||
"5469643c37600b40e0e09c5b",
|
||||
"54b83c2629843994803c838e",
|
||||
"544437e0645c0c0000c3291d",
|
||||
"5630eab0c0fcbd86057cc2f8",
|
||||
"55de80407a57948705777e89"
|
||||
];
|
||||
users.forEach(function (user, userIndex) {
|
||||
var sessions = db.level.sessions.find({creator: user._id + '', 'level.original': {$in: arenas}, submitted: true}).toArray();
|
||||
//print("Found sessions", sessions, "for user", user._id, user.name, 'who has courseInstances', user.courseInstances.join(', '));
|
||||
sessions.forEach(function(session, sessionIndex) {
|
||||
var leagues = session.leagues || [];
|
||||
for (var i = 0; i < user.courseInstances.length; ++i) {
|
||||
var alreadyHave = false;
|
||||
for (var j = 0; j < leagues.length; ++j)
|
||||
if (leagues[j].leagueID == user.courseInstances[i])
|
||||
alreadyHave = true;
|
||||
if (!alreadyHave)
|
||||
leagues.push({leagueID: user.courseInstances[i] + '', stats: {standardDeviation: 25 / 3, numberOfWinsAndTies: 0, numberOfLosses: 0, totalScore: 10, meanStrength: 25}});
|
||||
}
|
||||
//print(" Setting leagues to...");
|
||||
//printjson(leagues);
|
||||
session.leagues = leagues;
|
||||
db.level.sessions.save(session);
|
||||
});
|
||||
|
||||
if (userIndex % 100 === 0)
|
||||
print("Done", userIndex, "\tof", users.length, "users.");
|
||||
});
|
||||
print("Done.");
|
||||
}
|
|
@ -54,12 +54,17 @@ CampaignHandler = class CampaignHandler extends Handler
|
|||
|
||||
getOverworld: (req, res) ->
|
||||
return @sendForbiddenError(res) if not @hasAccess(req)
|
||||
q = @modelClass.find {}, slug: 1, adjacentCampaigns: 1, fullName: 1, description: 1, color: 1
|
||||
projection = {}
|
||||
if req.query.project
|
||||
projection[field] = 1 for field in req.query.project.split(',')
|
||||
q = @modelClass.find {}, projection
|
||||
q.exec (err, documents) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
formatCampaign = (doc) =>
|
||||
obj = @formatEntity(req, doc)
|
||||
obj.adjacentCampaigns = _.mapValues(obj.adjacentCampaigns, (a) -> _.pick(a, ['showIfUnlocked', 'color', 'name', 'description' ]))
|
||||
for original, level of obj.levels
|
||||
obj.levels[original] = _.pick level, ['locked', 'disabled', 'original', 'rewards']
|
||||
obj
|
||||
documents = (formatCampaign(doc) for doc in documents)
|
||||
@sendSuccess(res, documents)
|
||||
|
|
|
@ -80,13 +80,11 @@ PrepaidHandler = class PrepaidHandler extends Handler
|
|||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res, 'User for given ID not found') if not user
|
||||
userID = user.get('_id')
|
||||
# Prepaid.count {'redeemers.userID': userID}, (err, count) =>
|
||||
# return @sendDatabaseError(res, err) if err
|
||||
# return @sendSuccess(res, @formatEntity(req, prepaid)) if count
|
||||
return @sendSuccess(res, @formatEntity(req, prepaid)) if user.get('coursePrepaidID')
|
||||
|
||||
query =
|
||||
_id: prepaid.get('_id')
|
||||
'redeemers.userID': { $ne: req.user.get('_id') }
|
||||
'redeemers.userID': { $ne: user.get('_id') }
|
||||
$where: "this.redeemers.length < #{prepaid.get('maxRedeemers')}"
|
||||
update = { $push: { redeemers : { date: new Date(), userID: userID } }}
|
||||
Prepaid.update query, update, (err, nMatched) =>
|
||||
|
|
|
@ -61,6 +61,7 @@ TrialRequestSchema.post 'save', (doc) ->
|
|||
|
||||
TrialRequestSchema.statics.privateProperties = []
|
||||
TrialRequestSchema.statics.editableProperties = [
|
||||
'created'
|
||||
'prepaidCode'
|
||||
'properties'
|
||||
'reviewDate'
|
||||
|
|
|
@ -19,6 +19,7 @@ TrialRequestHandler = class TrialRequestHandler extends Handler
|
|||
makeNewInstance: (req) ->
|
||||
instance = super(req)
|
||||
instance.set 'applicant', req.user._id
|
||||
instance.set 'created', new Date()
|
||||
instance.set 'status', 'submitted'
|
||||
instance
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ describe '/db/prepaid', ->
|
|||
redeemers: [],
|
||||
creator: user1.get('_id')
|
||||
code: 0
|
||||
type: 'course'
|
||||
})
|
||||
prepaid.save (err, prepaid) ->
|
||||
otherUser = new User()
|
||||
|
@ -67,6 +68,7 @@ describe '/db/prepaid', ->
|
|||
redeemers: [],
|
||||
creator: user1.get('_id')
|
||||
code: 1
|
||||
type: 'course'
|
||||
})
|
||||
prepaid.save (err, prepaid) ->
|
||||
otherUser = new User()
|
||||
|
@ -84,6 +86,7 @@ describe '/db/prepaid', ->
|
|||
redeemers: [],
|
||||
creator: user1.get('_id')
|
||||
code: 2
|
||||
type: 'course'
|
||||
})
|
||||
prepaid.save (err, prepaid) ->
|
||||
loginNewUser (user2) ->
|
||||
|
@ -97,11 +100,14 @@ describe '/db/prepaid', ->
|
|||
|
||||
it 'is idempotent across prepaids collection', (done) ->
|
||||
loginNewUser (user1) ->
|
||||
otherUser = new User()
|
||||
otherUser = new User({
|
||||
'coursePrepaidID': new ObjectId()
|
||||
})
|
||||
otherUser.save (err, otherUser) ->
|
||||
prepaid1 = new Prepaid({
|
||||
redeemers: [{userID: otherUser.get('_id')}],
|
||||
code: 3
|
||||
type: 'course'
|
||||
})
|
||||
prepaid1.save (err, prepaid1) ->
|
||||
prepaid2 = new Prepaid({
|
||||
|
@ -109,6 +115,7 @@ describe '/db/prepaid', ->
|
|||
redeemers: [],
|
||||
creator: user1.get('_id')
|
||||
code: 4
|
||||
type: 'course'
|
||||
})
|
||||
prepaid2.save (err, prepaid2) ->
|
||||
url = getURL("/db/prepaid/#{prepaid2.id}/redeemers")
|
||||
|
@ -118,6 +125,64 @@ describe '/db/prepaid', ->
|
|||
return done() unless res.statusCode is 200
|
||||
expect(body.redeemers.length).toBe(0)
|
||||
done()
|
||||
|
||||
it 'is idempotent to itself for a user other than the creator', (done) ->
|
||||
loginNewUser (user1) ->
|
||||
prepaid = new Prepaid({
|
||||
maxRedeemers: 2,
|
||||
redeemers: [],
|
||||
creator: user1.get('_id')
|
||||
code: 0
|
||||
type: 'course'
|
||||
})
|
||||
prepaid.save (err, prepaid) ->
|
||||
otherUser = new User()
|
||||
otherUser.save (err, otherUser) ->
|
||||
url = getURL("/db/prepaid/#{prepaid.id}/redeemers")
|
||||
redeemer = { userID: otherUser.id }
|
||||
request.post {uri: url, json: redeemer }, (err, res, body) ->
|
||||
expect(body.redeemers?.length).toBe(1)
|
||||
expect(res.statusCode).toBe(200)
|
||||
request.post {uri: url, json: redeemer }, (err, res, body) ->
|
||||
expect(body.redeemers?.length).toBe(1)
|
||||
expect(res.statusCode).toBe(200)
|
||||
prepaid = Prepaid.findById body._id, (err, prepaid) ->
|
||||
expect(err).toBeNull()
|
||||
expect(prepaid.get('redeemers').length).toBe(1)
|
||||
User.findById otherUser.id, (err, user) ->
|
||||
expect(user.get('coursePrepaidID').equals(prepaid.get('_id'))).toBe(true)
|
||||
done()
|
||||
|
||||
it 'is idempotent to itself for the creator', (done) ->
|
||||
loginNewUser (user1) ->
|
||||
prepaid = new Prepaid({
|
||||
maxRedeemers: 2,
|
||||
redeemers: [],
|
||||
creator: user1.get('_id')
|
||||
code: 0
|
||||
type: 'course'
|
||||
})
|
||||
prepaid.save (err, prepaid) ->
|
||||
otherUser = new User()
|
||||
otherUser.save (err, otherUser) ->
|
||||
url = getURL("/db/prepaid/#{prepaid.id}/redeemers")
|
||||
redeemer = { userID: user1.id }
|
||||
request.post {uri: url, json: redeemer }, (err, res, body) ->
|
||||
expect(body.redeemers?.length).toBe(1)
|
||||
expect(res.statusCode).toBe(200)
|
||||
request.post {uri: url, json: redeemer }, (err, res, body) ->
|
||||
expect(body.redeemers?.length).toBe(1)
|
||||
expect(res.statusCode).toBe(200)
|
||||
prepaid = Prepaid.findById body._id, (err, prepaid) ->
|
||||
expect(err).toBeNull()
|
||||
expect(prepaid.get('redeemers').length).toBe(1)
|
||||
User.findById user1.id, (err, user) ->
|
||||
expect(user.get('coursePrepaidID').equals(prepaid.get('_id'))).toBe(true)
|
||||
redeemer = { userID: otherUser.id }
|
||||
request.post {uri: url, json: redeemer }, (err, res, body) ->
|
||||
expect(body.redeemers?.length).toBe(2)
|
||||
expect(res.statusCode).toBe(200)
|
||||
done()
|
||||
|
||||
it 'Clear database', (done) ->
|
||||
clearModels [Course, CourseInstance, Payment, Prepaid, User], (err) ->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue