Merge branch 'master' into vdom

This commit is contained in:
Sam 2016-02-22 12:20:59 +11:00
commit c404da248a
20 changed files with 3315 additions and 29 deletions

View File

@ -1,6 +1,6 @@
[main] [main]
host = https://www.transifex.com host = https://www.transifex.com
lang_map = es_ES: es, fr_FR: fr, ko_KR: ko, pt_PT: pt, sk_SK: sk lang_map = es_ES: es, fr_FR: fr, ko_KR: ko, pt_PT: pt, sk_SK: sk, vi_VN: vi
[discourse-org.clientenyml] [discourse-org.clientenyml]
file_filter = config/locales/client.<lang>.yml file_filter = config/locales/client.<lang>.yml

View File

@ -69,6 +69,9 @@ export default Ember.Controller.extend(ModalFunctionality, {
sentTo: result.sent_to_email, sentTo: result.sent_to_email,
currentEmail: result.current_email currentEmail: result.current_email
}); });
} else if (result.reason === 'suspended' ) {
self.send("closeModal");
bootbox.alert(result.error);
} else { } else {
self.flash(result.error, 'error'); self.flash(result.error, 'error');
} }

View File

@ -261,8 +261,10 @@ class SessionController < ApplicationController
def failed_to_login(user) def failed_to_login(user)
message = user.suspend_reason ? "login.suspended_with_reason" : "login.suspended" message = user.suspend_reason ? "login.suspended_with_reason" : "login.suspended"
render json: { error: I18n.t(message, { date: I18n.l(user.suspended_till, format: :date_only), render json: {
reason: user.suspend_reason}) } error: I18n.t(message, { date: I18n.l(user.suspended_till, format: :date_only), reason: user.suspend_reason}),
reason: 'suspended'
}
end end
def login(user) def login(user)

View File

@ -11,8 +11,14 @@ module Jobs
PostAction.flagged_posts_count > 0 && PostAction.flagged_posts_count > 0 &&
FlagQuery.flagged_post_actions('active').where('post_actions.created_at < ?', SiteSetting.notify_about_flags_after.to_i.hours.ago).pluck(:id).count > 0 FlagQuery.flagged_post_actions('active').where('post_actions.created_at < ?', SiteSetting.notify_about_flags_after.to_i.hours.ago).pluck(:id).count > 0
message = PendingFlagsMailer.notify PostCreator.create(
Email::Sender.new(message, :pending_flags_reminder).send Discourse.system_user,
target_group_names: ["staff"],
archetype: Archetype.private_message,
subtype: TopicSubtype.system_message,
title: I18n.t('flags_reminder.subject_template', { count: PostAction.flagged_posts_count }),
raw: I18n.t('flags_reminder.flags_were_submitted', { count: SiteSetting.notify_about_flags_after })
)
end end
end end

View File

@ -1,8 +1,10 @@
class UserProfile < ActiveRecord::Base class UserProfile < ActiveRecord::Base
belongs_to :user, inverse_of: :user_profile belongs_to :user, inverse_of: :user_profile
WEBSITE_REGEXP = /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,9}(([0-9]{1,5})?\/.*)?$)/ix
validates :bio_raw, length: { maximum: 3000 } validates :bio_raw, length: { maximum: 3000 }
validates :website, format: { with: /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,9}(([0-9]{1,5})?\/.*)?$)/ix }, allow_blank: true validates :website, format: { with: WEBSITE_REGEXP }, allow_blank: true
validates :user, presence: true validates :user, presence: true
before_save :cook before_save :cook
after_save :trigger_badges after_save :trigger_badges

View File

@ -2116,11 +2116,11 @@ en:
is_disabled: "Restore is disabled in the site settings." is_disabled: "Restore is disabled in the site settings."
label: "Restore" label: "Restore"
title: "Restore the backup" title: "Restore the backup"
confirm: "Are your sure you want to restore this backup?" confirm: "Are you sure you want to restore this backup?"
rollback: rollback:
label: "Rollback" label: "Rollback"
title: "Rollback the database to previous working state" title: "Rollback the database to previous working state"
confirm: "Are your sure you want to rollback the database to the previous working state?" confirm: "Are you sure you want to rollback the database to the previous working state?"
export_csv: export_csv:
user_archive_confirm: "Are you sure you want to download your posts?" user_archive_confirm: "Are you sure you want to download your posts?"

2089
config/locales/client.vi.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1406,6 +1406,14 @@ en:
blocked: "New registrations are not allowed from your IP address." blocked: "New registrations are not allowed from your IP address."
max_new_accounts_per_registration_ip: "New registrations are not allowed from your IP address (maximum limit reached). Contact a staff member." max_new_accounts_per_registration_ip: "New registrations are not allowed from your IP address (maximum limit reached). Contact a staff member."
flags_reminder:
flags_were_submitted:
one: "Flags were submitted over 1 hour ago. Please review them."
other: "Flags were submitted over %{count} hours ago. Please review them."
subject_template:
one: "1 flag waiting to be handled"
other: "%{count} flags waiting to be handled"
unsubscribe_mailer: unsubscribe_mailer:
subject_template: "Confirm you no longer want to receive email updates from %{site_title}" subject_template: "Confirm you no longer want to receive email updates from %{site_title}"
text_body_template: | text_body_template: |
@ -1482,6 +1490,8 @@ en:
- If you run your own mail server, check to make sure the IPs of your mail server are [not on any email blacklists][4]. Also verify that it is definitely sending a fully-qualified hostname that resolves in DNS in its HELO message. If not, this will cause your email to be rejected by many mail services. - If you run your own mail server, check to make sure the IPs of your mail server are [not on any email blacklists][4]. Also verify that it is definitely sending a fully-qualified hostname that resolves in DNS in its HELO message. If not, this will cause your email to be rejected by many mail services.
- Send a test email to [mail-tester.com][mt] to verify that everything is working correctly.
(The *easy* way is to create a free account on [Mandrill][md] or [Mailgun][mg] or [Mailjet][mj], which have free generous free mailing plans and will be fine for small communities. You'll still need to set up the SPF and DKIM records in your DNS, though!) (The *easy* way is to create a free account on [Mandrill][md] or [Mailgun][mg] or [Mailjet][mj], which have free generous free mailing plans and will be fine for small communities. You'll still need to set up the SPF and DKIM records in your DNS, though!)
We hope you received this email deliverability test OK! We hope you received this email deliverability test OK!
@ -1500,6 +1510,7 @@ en:
[md]: http://mandrill.com [md]: http://mandrill.com
[mg]: http://www.mailgun.com/ [mg]: http://www.mailgun.com/
[mj]: https://www.mailjet.com/pricing [mj]: https://www.mailjet.com/pricing
[mt]: http://www.mail-tester.com/
new_version_mailer: new_version_mailer:
subject_template: "[%{site_name}] New Discourse version, update available" subject_template: "[%{site_name}] New Discourse version, update available"
@ -1537,17 +1548,6 @@ en:
%{notes} %{notes}
flags_reminder:
flags_were_submitted:
one: "These flags were submitted over 1 hour ago."
other: "These flags were submitted over %{count} hours ago."
please_review: "Please review them."
post_number: "post"
how_to_disable: 'You can disable or change the frequency of this email reminder via the "notify about flags after" setting.'
subject_template:
one: "1 flag waiting to be handled"
other: "%{count} flags waiting to be handled"
queued_posts_reminder: queued_posts_reminder:
subject_template: subject_template:
one: "[%{site_name}] 1 post waiting to be reviewed" one: "[%{site_name}] 1 post waiting to be reviewed"

1028
config/locales/server.vi.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -42,14 +42,21 @@ module UserNameSuggester
# 2. removes unallowed leading characters # 2. removes unallowed leading characters
name.gsub!(/^\W+/, "") name.gsub!(/^\W+/, "")
# 3. removes unallowed trailing characters # 3. removes unallowed trailing characters
name.gsub!(/[^A-Za-z0-9]+$/, "") name = remove_unallowed_trailing_characters(name)
# 4. unify special characters # 4. unify special characters
name.gsub!(/[-_.]{2,}/, "_") name.gsub!(/[-_.]{2,}/, "_")
name name
end end
def self.remove_unallowed_trailing_characters(name)
name.gsub!(/[^A-Za-z0-9]+$/, "")
name
end
def self.rightsize_username(name) def self.rightsize_username(name)
name.ljust(User.username_length.begin, '1')[0, User.username_length.end] name = name[0, User.username_length.end]
name = remove_unallowed_trailing_characters(name)
name.ljust(User.username_length.begin, '1')
end end
end end

View File

@ -0,0 +1,31 @@
vi:
js:
poll:
voters:
other: "người bình chọn"
total_votes:
other: "tổng số bình chọn"
average_rating: "Đánh giá trung bình: <strong>%{average}</strong>."
multiple:
help:
between_min_and_max_options: "Bạn có thể chọn giữa <strong>%{min}</strong> và <strong>%{max}</strong>."
cast-votes:
title: "Bỏ phiếu của bạn"
label: "Bình chọn ngay!"
show-results:
title: "Hiển thị kết quả cuộc thăm dò"
label: "Hiện kết quả"
hide-results:
title: "Trở lại bầu chọn của bạn"
label: "Ẩn kết quả"
open:
title: "Mở bình chọn"
label: "Mở"
confirm: "Bạn có chắc mở bình chọn này?"
close:
title: "Đóng bình chọn"
label: "Đóng lại"
confirm: "Bạn có chắc chắn muốn đóng bình chọn này?"
error_while_toggling_status: "Có lỗi trong khi chuyển đổi qua lại các trạng thái của bình chọn này."
error_while_casting_votes: "Có lỗi trong khi tạo mãu bầu chọn của bạn"

View File

@ -0,0 +1,25 @@
vi:
site_settings:
poll_enabled: "Cho phép người dùng tạo các cuộc thăm dò?"
poll_maximum_options: "Số lượng tối đa tùy chọn trong một cuộc thăm dò."
poll:
multiple_polls_without_name: "Có nhiều cuộc thăm dò mà không có tên. Sử dụng thuộc tính '<code>name</code>' để xác định cuộc thăm dò của bạn."
multiple_polls_with_same_name: "Có nhiều cuộc thăm dò có cùng tên: <strong>%{name}</strong>. Sử dụng thuộc tính '<code>name</code>' để xác định cuộc thăm dò của bạn."
default_poll_must_have_at_least_2_options: "Thăm dò ý kiến phải có ít nhất 2 lựa chọn."
named_poll_must_have_at_least_2_options: "Thăm dò có tên <strong>%{name}</strong> phải có ít nhất 2 lựa chọn."
default_poll_must_have_different_options: "Thăm dò ý kiến phải có các tùy chọn khác nhau."
named_poll_must_have_different_options: "Thăm dò <strong>%{name}</strong> phải có các tùy chọn khác nhau."
default_poll_with_multiple_choices_has_invalid_parameters: "Thăm dò ý kiến với nhiều sự lựa chọn có các tham số không hợp lệ."
named_poll_with_multiple_choices_has_invalid_parameters: "Thăm dò <strong>%{name}</strong> với nhiều sự lựa chọn có các tham số không hợp lệ. "
requires_at_least_1_valid_option: "Bạn phải chọn ít nhất 1 lựa chọn hợp lệ."
cannot_change_polls_after_5_minutes: "Bạn không thể thêm, xóa hoặc đổi tên các cuộc thăm dò 5 phút đầu tiên."
op_cannot_edit_options_after_5_minutes: "Bạn không thể thêm hoặc loại bỏ các bình chọn sau khi 5 phút đầu tiên. Hãy liên hệ với người điều hành nếu bạn cần chỉnh sửa một bình chọn nào đó."
staff_cannot_add_or_remove_options_after_5_minutes: "Bạn không thể thêm hoặc loại bỏ các bình chọn sau khi 5 phút đầu tiên. Bạn nên đóng chủ đề này và tạo ra một cái mới để thay thế."
no_polls_associated_with_this_post: "Không có cuộc thăm dò được liên kết với bài này."
no_poll_with_this_name: "Không có thăm dò có tên <strong>%{name}</strong> liên kết với bài viết này."
post_is_deleted: "Không thể thực hiện trên bài viết đã xóa."
topic_must_be_open_to_vote: "Các chủ đề phải được mở để bầu chọn."
poll_must_be_open_to_vote: "Thăm dò ý kiến phải được mở để bầu chọn."
topic_must_be_open_to_toggle_status: "Các chủ đề phải được mở để chuyển trạng thái."
only_staff_or_op_can_toggle_status: "Chỉ có một BQT hoặc các người đăng bài có thể chuyển đổi một trạng thái thăm dò ý kiến"

27
public/403.vi.html Normal file
View File

@ -0,0 +1,27 @@
<html>
<head>
<title> Bạn không thể làm được điều đó (403)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 400%; color: #f00; line-height: 1em; }
</style>
</head>
<body>
<div class="dialog">
<h1>403</h1>
<p>Bạn không thể xem tài nguyên đó!</p>
<p>Trang này sẽ được thay thế bằng một trang lỗi 403 tùy chỉnh của Discourse.</p>
</div>
</body>
</html>

26
public/422.vi.html Normal file
View File

@ -0,0 +1,26 @@
<html>
<head>
<title>Thay đổi bạn muốn thực hiện đã bị từ chối (422)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
<!-- This file lives in public/422.html -->
<div class="dialog">
<h1>Thay đổi bạn muốn thực hiện đã bị từ chối.</h1>
<p>Có thể bạn đã cố thay đổi một số tính năng mà bạn không thể truy cập.</p>
</div>
</body>
</html>

13
public/500.vi.html Normal file
View File

@ -0,0 +1,13 @@
<html>
<head>
<title>Ôi - Lỗi 500</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1>Ối</h1>
<p>Phần mềm chạy diễn đàn thảo luận này bất ngờ gặp sự cố không mong muốn. Chúng tôi rất xin lỗi vì sự bất tiện này.</p>
<p>Thông tin chi tiết về lỗi đã được ghi lại và thông báo tự động đã được tạo. Chúng tôi sẽ xem xét lỗi này.</p>
<p>Không cần tiến hành bất cứ hành động nào. Tuy nhiên, nếu lỗi này vẫn tiếp tục, bạn có thể cung cấp thêm thông tin chi tiết bao gồm các bước để tái tạo lỗi hoặc tạo một thảo luận trên&nbsp;<a href="/category/meta">meta category</a>.</p>
</body>
</html>

12
public/503.vi.html Normal file
View File

@ -0,0 +1,12 @@
<html>
<head>
<title>Trang đang được bảo trì- Discourse.org</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1>Trang tạm dừng dịch vụ để bảo trì dịch vụ</h1>
<p>Vui lòng quay lại sau <span id="when-to-check-back">vài phút</span>.</p>
<p id="apology">Xin lỗi về sự bất tiện này!</p>
</body>
</html>

View File

@ -278,7 +278,6 @@ class ImportScripts::Base
avatar_url = opts.delete(:avatar_url) avatar_url = opts.delete(:avatar_url)
# Allow the || operations to work with empty strings '' # Allow the || operations to work with empty strings ''
opts[:name] = nil if opts[:name].blank?
opts[:username] = nil if opts[:username].blank? opts[:username] = nil if opts[:username].blank?
opts[:name] = User.suggest_name(opts[:email]) unless opts[:name] opts[:name] = User.suggest_name(opts[:email]) unless opts[:name]
@ -287,7 +286,8 @@ class ImportScripts::Base
opts[:username].length > User.username_length.end || opts[:username].length > User.username_length.end ||
!User.username_available?(opts[:username]) || !User.username_available?(opts[:username]) ||
!UsernameValidator.new(opts[:username]).valid_format? !UsernameValidator.new(opts[:username]).valid_format?
opts[:username] = UserNameSuggester.suggest(opts[:username] || opts[:name] || opts[:email])
opts[:username] = UserNameSuggester.suggest(opts[:username] || opts[:name].presence || opts[:email])
end end
opts[:email] = opts[:email].downcase opts[:email] = opts[:email].downcase
opts[:trust_level] = TrustLevel[1] unless opts[:trust_level] opts[:trust_level] = TrustLevel[1] unless opts[:trust_level]
@ -306,8 +306,8 @@ class ImportScripts::Base
User.transaction do User.transaction do
u.save! u.save!
if bio_raw.present? || website.present? || location.present? if bio_raw.present? || website.present? || location.present?
u.user_profile.bio_raw = bio_raw if bio_raw.present? u.user_profile.bio_raw = bio_raw[0..2999] if bio_raw.present?
u.user_profile.website = website if website.present? u.user_profile.website = website unless website.blank? || website !~ UserProfile::WEBSITE_REGEXP
u.user_profile.location = location if location.present? u.user_profile.location = location if location.present?
u.user_profile.save! u.user_profile.save!
end end

View File

@ -93,6 +93,15 @@ describe UserNameSuggester do
it 'should handle typical facebook usernames' do it 'should handle typical facebook usernames' do
expect(UserNameSuggester.suggest('roger.nelson.3344913')).to eq('roger.nelson.33') expect(UserNameSuggester.suggest('roger.nelson.3344913')).to eq('roger.nelson.33')
end end
it 'removes underscore at the end of long usernames that get truncated' do
expect(UserNameSuggester.suggest('uuuuuuuuuuuuuu_u')).to_not end_with('_')
end
it "adds number if it's too short after removing trailing underscore" do
User.stubs(:username_length).returns(8..8)
expect(UserNameSuggester.suggest('uuuuuuu_u')).to eq('uuuuuuu1')
end
end end
end end

View File

@ -14,17 +14,17 @@ describe Jobs::PendingFlagsReminder do
context "notify_about_flags_after is 48" do context "notify_about_flags_after is 48" do
before { SiteSetting.stubs(:notify_about_flags_after).returns(48) } before { SiteSetting.stubs(:notify_about_flags_after).returns(48) }
it "doesn't send email when flags are less than 48 hours old" do it "doesn't send message when flags are less than 48 hours old" do
Fabricate(:flag, created_at: 47.hours.ago) Fabricate(:flag, created_at: 47.hours.ago)
PostAction.stubs(:flagged_posts_count).returns(1) PostAction.stubs(:flagged_posts_count).returns(1)
Email::Sender.any_instance.expects(:send).never PostCreator.expects(:create).never
described_class.new.execute({}) described_class.new.execute({})
end end
it "sends email when there is a flag older than 48 hours" do it "sends message when there is a flag older than 48 hours" do
Fabricate(:flag, created_at: 49.hours.ago) Fabricate(:flag, created_at: 49.hours.ago)
PostAction.stubs(:flagged_posts_count).returns(1) PostAction.stubs(:flagged_posts_count).returns(1)
Email::Sender.any_instance.expects(:send).once.returns(true) PostCreator.expects(:create).once.returns(true)
described_class.new.execute({}) described_class.new.execute({})
end end
end end

View File

@ -0,0 +1,6 @@
vi:
site_settings:
enable_imgur: "Kích hoạt imgur api để tải file lên, không lưu trữ file tại máy chủ."
imgur_client_id: "Client ID imgur.com của bạn, cần cho chức năng tải ảnh lên. "
imgur_client_secret: "Client secret của bạn tạo imgur.com. Hiện tại không cần để tải ảnh lên, nhưng sẽ có trong tương lai."