Merge branch 'master' into production

This commit is contained in:
phoenixeliot 2016-06-02 12:53:23 -07:00
commit 6424a52837
6 changed files with 113 additions and 73 deletions

View file

@ -735,7 +735,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
mission_description_1: "<strong>Lập trình thật kì diệu</strong>. Bạn có thể tạo ra một thứ gì đó chỉ từ trí tưởng tượng. Chúng tôi bắt đầu CodeCombat để đem tới cho học viên những trải nghiệm nhiệm màu khi <strong>viết code</strong> thực tế."
mission_description_2: "Trên thực tế, việc này giúp cho bạn học nhanh hơn. Nhanh hơn RẤT NHIỀU. Bạn được thực hành thay vì chỉ đọc lý thuyết. Chúng tôi muốn đưa môi trường thực hành này đến với trường học và đến tay <strong>mọi học sinh</strong>, bởi vì mọi người đều cần có cơ hội biết đến sự nhiệm màu của lập trình."
team_title: "Đội ngũ của CodeCombat"
# team_values: "We value open and respectful dialog, where the best idea wins. Our decisions are grounded in customer research and our process is focused on delivering tangible results for them. Everyone is hands-on, from our CEO to our Github contributors, because we value growth and learning in our team."
team_values: "Chúng tôi chân trọng những cuộc đối thoại mở và có sự tôn trọng lẫn nhau, nơi mà những ý tưởng tốt nhất giành chiến thắng. Những quyết định của chúng tôi được đưa ra hoàn toàn dựa trên những báo cáo nghiên cứu ý kiến khách hàng và quy trình của chúng tôi chú trọng vào mục tiêu đưa đến những giá trị hữu hình cho cho khách hàng. Mọi người đều có vai trò của mình, từ CEO của chúng tôi cho đến những người tham gia công đồng trên Github, bởi vì chúng tôi chân trọng sự phát triển và học hỏi của từng thành viên trong nhóm."
nick_title: "Đồng Sáng Lập, CEO" # {change}
nick_blurb: "Người truyền cảm hứng"
matt_title: "Đồng Sáng Lập, CTO" # {change}
@ -853,10 +853,10 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
college_plus: "Cao đẳng/Đại học hoặc cao hơn"
anything_else: "Còn điều gì chúng tôi nên biết thêm nữa không?"
thanks_header: "Đã nhận yêu cầu!"
# thanks_sub_header: "Thanks for expressing interest in CodeCombat for your school."
thanks_sub_header: "Cảm ơn vì đã bày tỏ sự quan tâm của trường bạn với CodeCombat."
thanks_p: "Chúng tôi sẽ sớm trả lời lại! Nếu bạn cần liên hệ, hãy liên lạc với chúng tôi tại:"
# back_to_classes: "Back to Classes"
# finish_signup: "Finish creating your teacher account:"
back_to_classes: "Quay lại Lớp học"
finish_signup: "Hoàn thiện tài khoản giáo viên:"
# finish_signup_p: "Create an account to set up a class, add your students, and monitor their progress as they learn computer science."
signup_with: "Đăng ký bằng:"
connect_with: "Kết nối với:"
@ -866,7 +866,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
# 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: "Năng cấp lên tài khoản Giáo Viên"
not: "Không phải"
# setup_a_class: "Set Up a Class"
setup_a_class: "Thiết lập một Lớp học"
versions:
save_version_title: "Lưu phiên bản mới"
@ -922,10 +922,10 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
email_announcements: "Thông báo"
email_announcements_description: "Nhận email về tin tức mới nhất và sự phát triển của Codecombat."
email_notifications: "Thông báo"
# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity."
# email_any_notes: "Any Notifications"
# email_any_notes_description: "Disable to stop all activity notification emails."
# email_news: "News"
email_notifications_summary: "Kiểm soát các thông báo cá nhân tự động liên quan đến các hoạt động của CodeCombat."
email_any_notes: "Bất kỳ thông báo nào"
email_any_notes_description: "Vô hiệu hóa để ngừng mọi thông báo qua email."
email_news: "Tin tức"
email_recruit_notes: "Cơ hội việc làm"
email_recruit_notes_description: "Nếu bạn chơi trò này rất giỏi, chúng tôi có thể sẽ liên lạc với bạn về cơ hội nghề nghiệp."
contributor_emails: "Email tham gia đóng góp"
@ -957,7 +957,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
# toggle_grid: "Toggle grid overlay."
# toggle_pathfinding: "Toggle pathfinding overlay."
# beautify: "Beautify your code by standardizing its formatting."
# maximize_editor: "Maximize/minimize code editor."
maximize_editor: "Phong to/thu nhỏ trình soạn thảo code."
community:
main_title: "Cộng đồng CodeCombat"
@ -1000,8 +1000,8 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
edit_description: "sửa mô tả"
private: "(kín)"
summary: "Tóm tắt"
average_level: "Cấp độ trng bình"
# average_achievements: "Average Achievements"
average_level: "Cấp độ trung bình"
average_achievements: "Thành tựu trung bình"
delete_clan: "Xóa Clan"
leave_clan: "Rời Clan"
join_clan: "Tham gia Clan"
@ -1013,29 +1013,29 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
started_1: "đã bắt đầu"
complete_1: "hoàn thành"
# exp_levels: "Expand levels"
# rem_hero: "Remove Hero"
# status: "Status"
rem_hero: "Xóa Tướng"
status: "Trạng thái"
complete_2: "Hoàn thành"
started_2: "Đã bắt đầu"
not_started_2: "Chưa bắt đầu"
view_solution: "Click để xem lời giải."
# view_attempt: "Click to view attempt."
# latest_achievement: "Latest Achievement"
view_attempt: "Click để xem thử."
latest_achievement: "Thành tựu mới nhất"
playtime: "Thời gian chơi"
last_played: "Lần chơi cuối"
# leagues_explanation: "Play in a league against other clan members in these multiplayer arena instances."
# track_concepts1: "Track concepts"
# track_concepts2a: "learned by each student"
# track_concepts2b: "learned by each member"
# track_concepts3a: "Track levels completed for each student"
# track_concepts3b: "Track levels completed for each member"
track_concepts3a: "Theo dõi các màn chơi được hoàn thành bởi mỗi học viên"
track_concepts3b: "Theo dõi các màn chơi được hoàn thành bởi mỗi thành viên"
track_concepts4a: "Xem các học viên của bạn'"
track_concepts4b: "Xem các thành viên của bạn'"
track_concepts5: "lời giải"
track_concepts6a: "Sắp xếp học viên theo tên hoặc tiến trình"
track_concepts6b: "Sắp xếp thành viên theo tên hoặc tiến trình"
# track_concepts7: "Requires invitation"
# track_concepts8: "to join"
track_concepts7: "Yêu cầu lời mời"
track_concepts8: "để tham gia"
private_require_sub: "Các Clan kín cần mua subscription để tạo hoặc tham gia."
courses:
@ -1047,7 +1047,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
visit_suf: "để tham gia."
select_class: "Chọn một trong các lớp học của bạn"
unnamed: "*unnamed*"
# select: "Select"
select: "Lựa chọn"
unnamed_class: "Lớp học chưa đặt tên"
edit_settings: "thay đổi tùy chỉnh lớp học"
edit_settings1: "Thay đổi tùy chỉnh lớp học"
@ -1055,24 +1055,24 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
add_students: "Thêm học sinh"
stats: "Thống kê"
total_students: "Tổng số học sinh:"
# average_time: "Average level play time:"
average_time: "Thời gian chơi trung bình:"
total_time: "Tổng thời gian chơi:"
average_levels: "Lượng cấp độ trung bình đã hoàn thành:"
total_levels: "Tổng số cấp độ đã hoàn thành"
# furthest_level: "Furthest level completed:"
# students: "Students"
# students1: "students"
# concepts: "Concepts"
# levels: "levels"
# played: "Played"
# play_time: "Play time:"
# completed: "Completed:"
# invite_students: "Invite students to join this class."
# invite_link_header: "Link to join course"
furthest_level: "Màn chơi xa nhất đã hoàn thành:"
students: "Học viên"
students1: "học viên"
concepts: "Các khái niệm"
levels: "màn chơi"
played: "Đã chơi"
play_time: "Thời gian chơi:"
completed: "Đã hoàn thành:"
invite_students: "Mời các học viên tham gia lớp học này."
invite_link_header: "Đường link để tham gia khóa học"
# invite_link_p_1: "Give this link to students you would like to have join the course."
# invite_link_p_2: "Or have us email them directly:"
# capacity_used: "Course slots used:"
# enter_emails: "Enter student emails to invite, one per line"
capacity_used: "Số chỗ đã đăng ký:"
enter_emails: "Nhập email học viên để gửi lời mời, mỗi email một dòng"
send_invites: "Gửi lời mời"
creating_class: "Đang tạo lớp..."
purchasing_course: "Đang mua khóa học..."
@ -1086,15 +1086,15 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
name_class: "Đặt tên lớp của bạn"
# displayed_course_page: "This will be displayed on the course page for you and your students. It can be changed later."
buy: "Mua"
# purchasing_for: "You are purchasing a license for"
# creating_for: "You are creating a class for"
# for: "for" # Like in 'for 30 students'
purchasing_for: "Bạn đang muc giấy phép cho"
creating_for: "Bạn đang tạo một lớp cho"
for: "dành cho" # Like in 'for 30 students'
# receive_code: "Afterwards you will receive an unlock code to distribute to your students, which they can use to enroll in your class."
# free_trial: "Free trial for teachers!"
# get_access: "to get individual access to all courses for evalutaion purposes."
# questions: "Questions?"
# teachers_click: "Teachers Click Here"
# students_click: "Students Click Here"
questions: "Có câu hỏi?"
teachers_click: "Giáo viên click vào đây"
students_click: "Học viên click vào đây"
courses_on_coco: "Những khóa học trên CodeCombat"
# designed_to: "Courses are designed to introduce computer science concepts using CodeCombat's fun and engaging environment. CodeCombat levels are organized around key topics to encourage progressive learning, over the course of 5 hours."
# more_in_less: "Learn more in less time"
@ -1406,7 +1406,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
# level_title: "Level Editor"
# achievement_title: "Achievement Editor"
# poll_title: "Poll Editor"
# back: "Back"
back: "Quay lại"
# revert: "Revert"
# revert_models: "Revert Models"
pick_a_terrain: "Chọn Địa Hình"
@ -1418,8 +1418,8 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
glacier: "Núi băng"
small: ""
large: "Lớn"
# fork_title: "Fork New Version"
# fork_creating: "Creating Fork..."
fork_title: "Fork phiên bản mới"
fork_creating: "Đang tạo Fork..."
# generate_terrain: "Generate Terrain"
more: "Thêm"
wiki: "Wiki"

View file

@ -7,6 +7,7 @@ application = require 'core/application'
Classroom = require 'models/Classroom'
errors = require 'core/errors'
COPPADenyModal = require 'views/core/COPPADenyModal'
utils = require 'core/utils'
module.exports = class CreateAccountModal extends ModalView
@ -28,6 +29,8 @@ module.exports = class CreateAccountModal extends ModalView
initialize: (options={}) ->
@onNameChange = _.debounce(_.bind(@checkNameExists, @), 500)
options.initialValues ?= {}
options.initialValues?.classCode ?= utils.getQueryVariable('_cc', "")
@previousFormInputs = options.initialValues or {}
# TODO: Switch to promises and state, rather than using defer to hackily enable buttons after render

View file

@ -55,7 +55,7 @@ module.exports = class TeachersContactModal extends ModalView
return unless _.isEmpty(formErrors)
@state.set('sendingState', 'sending')
data = _.extend({ country: me.get('country'), recipientID: 'schools@codecombat.com' }, formValues)
data = _.extend({ country: me.get('country'), recipientID: 'schools@codecombat.com', enrollmentsNeeded: @enrollmentsNeeded }, formValues)
contact.send({
data
context: @

View file

@ -57,7 +57,7 @@ module.exports =
return done(error) if error
leads = JSON.parse(body)
return done("Unexpected leads format: " + body) unless leads.data?
return done(null, config.mail.supportSchools) unless leads.data?.length > 0
return done("No existing Close.IO lead found for #{email}") unless leads.data?.length > 0
lead = leads.data[0]
uri = "https://#{apiKey}:X@app.close.io/api/v1/activity/?lead_id=#{lead.id}"
request.get uri, (error, response, body) =>
@ -65,8 +65,33 @@ module.exports =
activities = JSON.parse(body)
return done("Unexpected activities format: " + body) unless activities.data?
for activity in activities.data when activity._type is 'Email'
return done(null, activity.sender) if /@codecombat\.com/ig.test(activity.sender)
return done(null, config.mail.supportSchools)
if /@codecombat\.com/ig.test(activity.sender) and not activity.sender?.indexOf(config.mail.username) >= 0
return done(null, activity.sender, lead.id)
return done(null, config.mail.supportSchools, lead.id)
catch error
log.error("closeIO.getSalesContactEmail Error for #{email}: #{JSON.stringify(error)}")
return done(error, config.mail.supportSchools)
return done(error)
sendMail: (fromAddress, subject, content, done) ->
# log.info("DEBUG: closeIO.sendMail #{fromAddress} #{subject} #{content}")
@getSalesContactEmail fromAddress, (err, salesContactEmail, leadID) ->
return done("Error getting sales contact for #{fromAddress}: #{err}") if err
matches = salesContactEmail.match(/^[a-zA-Z_]+ <(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})>$|(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})/i)
salesContactEmail = matches?[1] ? matches?[2] ? config.mail.supportSchools
postData =
to: [salesContactEmail]
sender: config.mail.username
subject: subject
body_text: content
lead_id: leadID
status: 'outbox'
options =
uri: "https://#{apiKey}:X@app.close.io/api/v1/activity/email/"
body: JSON.stringify(postData)
request.post options, (error, response, body) =>
return done(error) if error
result = JSON.parse(body);
if result.errors or result['field-errors']
errorMessage = "Close.io Send email POST error for #{fromAddress} #{JSON.stringify(result.errors)} #{JSON.stringify(result['field-errors'])}";
return done(errorMessage)
return done()

View file

@ -11,21 +11,27 @@ module.exports.setup = (app) ->
app.post '/contact', (req, res) ->
return res.end() unless req.user
# log.info "Sending mail from #{req.body.email} saying #{req.body.message}"
createMailContext req, (context) ->
sendwithus.api.send context, (err, result) ->
if err
log.error "Error sending contact form email: #{err.message or err}"
fromAddress = req.body.sender or req.body.email or req.user.get('email')
createMailContent req, fromAddress, (subject, content) ->
if req.body.recipientID is 'schools@codecombat.com' or req.user.isTeacher()
req.user.update({$set: { enrollmentRequestSent: true }}).exec(_.noop) if req.body.recipientID is 'schools@codecombat.com'
closeIO.sendMail fromAddress, subject, content, (err) ->
log.error "Error sending contact form email via Close.io: #{err.message or err}" if err
else
createSendWithUsContext req, fromAddress, subject, content, (context) ->
sendwithus.api.send context, (err, result) ->
log.error "Error sending contact form email via sendwithus: #{err.message or err}" if err
return res.end()
createMailContext = (req, done) ->
sender = req.body.sender or req.body.email
createMailContent = (req, fromAddress, done) ->
country = req.body.country
enrollmentsNeeded = req.body.enrollmentsNeeded
message = req.body.message
user = req.user
recipientID = req.body.recipientID
subject = req.body.subject
country = req.body.country
sentFromLevel = levelID: req.body.levelID, courseID: req.body.courseID, courseInstanceID: req.body.courseInstanceID
subject = switch
when enrollmentsNeeded then "#{enrollmentsNeeded} Licenses needed for #{fromAddress}"
when req.body.subject then req.body.subject
else "Contact Us Form: #{fromAddress}"
level = if user?.get('points') > 0 then Math.floor(5 * Math.log((1 / 100) * (user.get('points') + 100))) + 1 else 0
premium = user?.isPremium()
teacher = user?.isTeacher()
@ -34,15 +40,25 @@ createMailContext = (req, done) ->
--
http://codecombat.com/user/#{user.get('slug') or user.get('_id')}
#{user.get('name') or 'Anonymous'} - Level #{level}#{if teacher then ' - Teacher' else ''}#{if premium then ' - Subscriber' else ''}#{if country then ' - ' + country else ''}
#{fromAddress} - #{user.get('name') or 'Anonymous'} - Level #{level}#{if teacher then ' - Teacher' else ''}#{if premium then ' - Subscriber' else ''}#{if country then ' - ' + country else ''}
"""
if req.body.browser
content += "\n#{req.body.browser} - #{req.body.screenSize}"
done(subject, content)
createSendWithUsContext = (req, fromAddress, subject, content, done) ->
user = req.user
recipientID = req.body.recipientID
sentFromLevel = levelID: req.body.levelID, courseID: req.body.courseID, courseInstanceID: req.body.courseInstanceID
premium = user?.isPremium()
teacher = user?.isTeacher()
if recipientID is 'schools@codecombat.com' or teacher
return done("Tried to send a teacher contact us email via sendwithus #{fromAddress} #{subject}")
toAddress = switch
when premium then config.mail.supportPremium
else config.mail.supportPrimary
fromAddress = sender or user.get('email')
context =
email_id: sendwithus.templates.plain_text_email
@ -53,30 +69,24 @@ createMailContext = (req, done) ->
reply_to: fromAddress
name: user.get('name')
email_data:
subject: "[CodeCombat] #{subject ? ('Feedback - ' + fromAddress)}"
subject: subject
content: content
contentHTML: content.replace /\n/g, '\n<br>'
if recipientID is 'schools@codecombat.com' or teacher
req.user.update({$set: { enrollmentRequestSent: true }}).exec(_.noop) if recipientID is 'schools@codecombat.com'
closeIO.getSalesContactEmail fromAddress, (err, salesContactEmail) ->
console.error "Error getting sales contact for #{sender}: #{err}" if err
context.recipient.address = salesContactEmail ? config.mail.supportSchools
done context
else if recipientID and (user.isAdmin() or ('employer' in (user.get('permissions') ? [])))
if recipientID and (user.isAdmin() or ('employer' in (user.get('permissions') ? [])))
User.findById(recipientID, 'email').exec (err, document) ->
if err
log.error "Error looking up recipient to email from #{recipientID}: #{err}" if err
else
context.recipient.bcc = [context.recipient.address, sender]
context.recipient.bcc = [context.recipient.address, fromAddress]
context.recipient.address = document.get('email')
context.email_data.content = message
context.email_data.content = content
done context
else
async.waterfall [
fetchRecentSessions.bind undefined, user, context, sentFromLevel
# Can add other data-grabbing stuff here if we want.
], (err, results) ->
console.error "Error getting contact message context for #{sender}: #{err}" if err
console.error "Error getting contact message context for #{fromAddress}: #{err}" if err
if req.body.screenshotURL
context.email_data.contentHTML += "\n<br><img src='#{req.body.screenshotURL}' />"
done context

View file

@ -6,6 +6,8 @@ describe 'AuthModal', ->
modal = null
beforeEach ->
application.facebookHandler.fakeAPI()
application.gplusHandler.fakeAPI()
modal = new AuthModal()
modal.render()
@ -20,4 +22,4 @@ describe 'AuthModal', ->
expect(args[0] instanceof RecoverModal).toBeTruthy()
it '(demo)', ->
jasmine.demoModal(modal)
jasmine.demoModal(modal)