From faf63114a96cb9f351ffd6b29d7169dc8a048e47 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Mon, 14 Mar 2016 17:40:30 -0700 Subject: [PATCH 001/162] update redis dependency b1e0da starts using the redis ROLE command which is available in redis-server 2.8.12 and later. --- docs/DEVELOPER-ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DEVELOPER-ADVANCED.md b/docs/DEVELOPER-ADVANCED.md index 4288d3fdd4..c8e893345a 100644 --- a/docs/DEVELOPER-ADVANCED.md +++ b/docs/DEVELOPER-ADVANCED.md @@ -9,7 +9,7 @@ Note: If you are developing on a Mac, you will probably want to look at [these i 1. Install and configure PostgreSQL 9.3+. 1. Run `postgres -V` to see if you already have it. 1. Make sure that the server's messages language is English; this is [required](https://github.com/rails/rails/blob/3006c59bc7a50c925f6b744447f1d94533a64241/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L1140) by the ActiveRecord Postgres adapter. -2. Install and configure Redis 2+. +2. Install and configure Redis 2.8.12+. 1. Run `redis-server -v` to see if you already have it. 3. Install ImageMagick 4. Install libxml2, libpq-dev, g++, gifsicle, libjpeg-progs and make. From d95728dd16b952890a7ac040b794af315f80d41f Mon Sep 17 00:00:00 2001 From: jeremylan Date: Thu, 17 Mar 2016 17:43:21 +1100 Subject: [PATCH 002/162] Fixed anonymizer when 'full name required' setting is on When the setting 'full name required' is on the anonymizer was trying to set the user name to nil and this caused the user name and email to remain not anonymized. Now in this scenario the user name is set to the anonimized username and the email is anonymized correctly. --- app/services/user_anonymizer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/user_anonymizer.rb b/app/services/user_anonymizer.rb index e9ff162dbc..ac692d28d7 100644 --- a/app/services/user_anonymizer.rb +++ b/app/services/user_anonymizer.rb @@ -20,7 +20,7 @@ class UserAnonymizer @user.reload @user.password = SecureRandom.hex @user.email = "#{@user.username}@example.com" - @user.name = nil + @user.name = SiteSetting.full_name_required ? @user.username : nil @user.date_of_birth = nil @user.title = nil @user.uploaded_avatar_id = nil From 213950e4cf1ba0462f990425b12ec383d274068f Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Thu, 17 Mar 2016 17:35:23 -0400 Subject: [PATCH 003/162] FEATURE: add option to include topics from trust level 0 users in digest emails --- app/assets/javascripts/discourse/models/user.js.es6 | 3 ++- .../discourse/templates/user/preferences.hbs | 2 ++ app/models/topic.rb | 5 ++++- app/models/user_option.rb | 2 ++ app/serializers/user_option_serializer.rb | 3 ++- app/services/user_updater.rb | 3 ++- config/locales/client.en.yml | 1 + config/locales/server.en.yml | 1 + config/site_settings.yml | 1 + ...01955_add_include_tl0_in_digests_to_user_options.rb | 5 +++++ spec/controllers/post_actions_controller_spec.rb | 1 - spec/models/topic_spec.rb | 10 ++++++++++ 12 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20160317201955_add_include_tl0_in_digests_to_user_options.rb diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index 5ae7433316..e1199a190d 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -168,7 +168,8 @@ const User = RestModel.extend({ 'digest_after_minutes', 'new_topic_duration_minutes', 'auto_track_topics_after_msecs', - 'like_notification_frequency' + 'like_notification_frequency', + 'include_tl0_in_digests' ].forEach(s => { data[s] = this.get(`user_option.${s}`); }); diff --git a/app/assets/javascripts/discourse/templates/user/preferences.hbs b/app/assets/javascripts/discourse/templates/user/preferences.hbs index eb80ccd418..d5e4699834 100644 --- a/app/assets/javascripts/discourse/templates/user/preferences.hbs +++ b/app/assets/javascripts/discourse/templates/user/preferences.hbs @@ -174,8 +174,10 @@
{{combo-box valueAttribute="value" content=digestFrequencies value=model.user_option.digest_after_minutes}}
+ {{preference-checkbox labelKey="user.include_tl0_in_digests" checked=model.user_option.include_tl0_in_digests}} {{/if}} {{/if}} +
{{combo-box valueAttribute="value" content=previousRepliesOptions value=model.user_option.email_previous_replies}} diff --git a/app/models/topic.rb b/app/models/topic.rb index 1db44a557f..a38c2b2e4e 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -314,11 +314,14 @@ class Topic < ActiveRecord::Base .joins("LEFT OUTER JOIN users ON users.id = topics.user_id") .where(closed: false, archived: false) .where("COALESCE(topic_users.notification_level, 1) <> ?", TopicUser.notification_levels[:muted]) - .where("COALESCE(users.trust_level, 0) > 0") .created_since(since) .listable_topics .includes(:category) + unless user.user_option.try(:include_tl0_in_digests) + topics = topics.where("COALESCE(users.trust_level, 0) > 0") + end + if !!opts[:top_order] topics = topics.joins("LEFT OUTER JOIN top_topics ON top_topics.topic_id = topics.id") .order(TopicQuerySQL.order_top_for(score)) diff --git a/app/models/user_option.rb b/app/models/user_option.rb index de4ec067a8..715b98e0c7 100644 --- a/app/models/user_option.rb +++ b/app/models/user_option.rb @@ -49,6 +49,8 @@ class UserOption < ActiveRecord::Base self.digest_after_minutes ||= SiteSetting.default_email_digest_frequency.to_i end + self.include_tl0_in_digests = SiteSetting.default_include_tl0_in_digests + true end diff --git a/app/serializers/user_option_serializer.rb b/app/serializers/user_option_serializer.rb index 6a40c7a260..591b07e457 100644 --- a/app/serializers/user_option_serializer.rb +++ b/app/serializers/user_option_serializer.rb @@ -16,7 +16,8 @@ class UserOptionSerializer < ApplicationSerializer :new_topic_duration_minutes, :email_previous_replies, :email_in_reply_to, - :like_notification_frequency + :like_notification_frequency, + :include_tl0_in_digests def include_edit_history_public? diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb index 25510fd250..e7136f74e9 100644 --- a/app/services/user_updater.rb +++ b/app/services/user_updater.rb @@ -23,7 +23,8 @@ class UserUpdater :auto_track_topics_after_msecs, :email_previous_replies, :email_in_reply_to, - :like_notification_frequency + :like_notification_frequency, + :include_tl0_in_digests ] def initialize(actor, user) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 9d65923672..0ff27a0c40 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -652,6 +652,7 @@ en: weekly: "weekly" every_two_weeks: "every two weeks" + include_tl0_in_digests: "Include posts from new users in digest emails" email_in_reply_to: "Include an excerpt of replied to post in emails" email_direct: "Send me an email when someone quotes me, replies to my post, mentions my @username, or invites me to a topic" email_private_messages: "Send me an email when someone messages me" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 8385c043f3..6dd216ece3 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1267,6 +1267,7 @@ en: notify_about_queued_posts_after: "If there are posts that have been waiting to be reviewed for more than this many hours, an email will be sent to the contact email. Set to 0 to disable these emails." default_email_digest_frequency: "How often users receive digest emails by default." + default_include_tl0_in_digests: "Include posts from new users in digest emails by default. Users can change this in their preferences." default_email_private_messages: "Send an email when someone messages the user by default." default_email_direct: "Send an email when someone quotes/replies to/mentions or invites the user by default." default_email_mailing_list_mode: "Send an email for every new post by default." diff --git a/config/site_settings.yml b/config/site_settings.yml index b4c4da72b9..dd1e45019a 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1085,6 +1085,7 @@ user_preferences: default_email_digest_frequency: enum: 'DigestEmailSiteSetting' default: 10080 + default_include_tl0_in_digests: false default_email_private_messages: true default_email_direct: true default_email_mailing_list_mode: false diff --git a/db/migrate/20160317201955_add_include_tl0_in_digests_to_user_options.rb b/db/migrate/20160317201955_add_include_tl0_in_digests_to_user_options.rb new file mode 100644 index 0000000000..512f5d6df5 --- /dev/null +++ b/db/migrate/20160317201955_add_include_tl0_in_digests_to_user_options.rb @@ -0,0 +1,5 @@ +class AddIncludeTl0InDigestsToUserOptions < ActiveRecord::Migration + def change + add_column :user_options, :include_tl0_in_digests, :boolean, default: false + end +end diff --git a/spec/controllers/post_actions_controller_spec.rb b/spec/controllers/post_actions_controller_spec.rb index 4a828893ab..0a441a0af0 100644 --- a/spec/controllers/post_actions_controller_spec.rb +++ b/spec/controllers/post_actions_controller_spec.rb @@ -17,7 +17,6 @@ describe PostActionsController do PostAction.expects(:act).once.raises(RateLimiter::LimitExceeded.new(60, 'create_like')) expect(-> { xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like] - puts response.success? }).to change(UserHistory, :count).by(1) end end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 6d0e7074e2..e2ae6c0549 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -1303,6 +1303,16 @@ describe Topic do expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank end + it "returns topics from TL0 users if enabled in preferences" do + new_user = Fabricate(:user, trust_level: 0) + topic = Fabricate(:topic, user_id: new_user.id) + + u = Fabricate(:user) + u.user_option.include_tl0_in_digests = true + + expect(Topic.for_digest(u, 1.year.ago, top_order: true)).to eq([topic]) + end + end describe 'secured' do From 1b4e0f3300e5927c4531322a0beb1928a484a9e7 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Tue, 8 Mar 2016 17:50:06 -0500 Subject: [PATCH 004/162] FIX: vertical centering of header, using display: table --- .../components/header-extra-info.js.es6 | 1 + .../discourse/components/home-logo.js.es6 | 2 +- .../discourse/templates/header.hbs | 118 ++++---- .../stylesheets/common/base/header.scss | 259 ++++++++++-------- app/assets/stylesheets/desktop/header.scss | 5 + app/assets/stylesheets/mobile/header.scss | 9 + 6 files changed, 222 insertions(+), 172 deletions(-) diff --git a/app/assets/javascripts/discourse/components/header-extra-info.js.es6 b/app/assets/javascripts/discourse/components/header-extra-info.js.es6 index 2182f3ea34..9659bbf6af 100644 --- a/app/assets/javascripts/discourse/components/header-extra-info.js.es6 +++ b/app/assets/javascripts/discourse/components/header-extra-info.js.es6 @@ -1,6 +1,7 @@ import DiscourseURL from 'discourse/lib/url'; const TopicCategoryComponent = Ember.Component.extend({ + classNames: ['extra-info-cell'], needsSecondRow: Ember.computed.gt('secondRowItems.length', 0), secondRowItems: function() { return []; }.property(), diff --git a/app/assets/javascripts/discourse/components/home-logo.js.es6 b/app/assets/javascripts/discourse/components/home-logo.js.es6 index c0846e268b..0169008031 100644 --- a/app/assets/javascripts/discourse/components/home-logo.js.es6 +++ b/app/assets/javascripts/discourse/components/home-logo.js.es6 @@ -2,7 +2,7 @@ import DiscourseURL from 'discourse/lib/url'; import { setting } from 'discourse/lib/computed'; export default Ember.Component.extend({ - classNames: ["title"], + classNameBindings: [":title", "minimized"], targetUrl: function() { // For overriding by customizations diff --git a/app/assets/javascripts/discourse/templates/header.hbs b/app/assets/javascripts/discourse/templates/header.hbs index ad1af73529..ecf49f3b05 100644 --- a/app/assets/javascripts/discourse/templates/header.hbs +++ b/app/assets/javascripts/discourse/templates/header.hbs @@ -1,66 +1,70 @@ -
-
- {{home-logo minimized=showExtraInfo}} - {{plugin-outlet "header-after-home-logo"}} +
+
+
+ {{home-logo minimized=showExtraInfo}} + {{plugin-outlet "header-after-home-logo"}} -
- {{#unless currentUser}} - {{#if showSignUpButton}} - {{d-button action="showCreateAccount" class="btn-primary btn-small sign-up-button" label="sign_up"}} - {{/if}} - {{d-button action="showLogin" class="btn-primary btn-small login-button" icon="user" label="log_in"}} - {{/unless}} - + {{plugin-outlet "header-before-dropdowns"}} + {{user-menu visible=userMenuVisible logoutAction="logout"}} + {{hamburger-menu visible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}} + {{search-menu visible=searchVisible}} +
+
- - {{#if showExtraInfo}} - {{header-extra-info topic=topic}} - {{/if}}
{{plugin-outlet "header-under-content"}} diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index 63c41255d7..e2adc73105 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -1,136 +1,167 @@ .d-header { + width: 100%; + position: absolute; + top: 0; + z-index: 1001; + background-color: $header_background; + box-shadow: 0 2px 4px -1px rgba(0,0,0, .25); + + .docked & { + position: fixed; + backface-visibility: hidden; /** do magic for scrolling performance **/ + } + + .d-header-table { + display: table; + table-layout: fixed; /* or else long topic titles break everything */ width: 100%; - position: absolute; - top: 0; - z-index: 1001; - background-color: $header_background; - box-shadow: 0 2px 4px -1px rgba(0,0,0, .25); - - .docked & { - position: fixed; - backface-visibility: hidden; /** do magic for scrolling performance **/ + margin: 8px auto; + } + .d-header-row { + display: table-header-group; /* table-row doesn't work on mobile */ + } + .d-header-wrap { + padding: 0; + } + .title, .extra-info-cell, .panel { + display: table-cell; + } + .title { + vertical-align: middle; + padding-left: 8px; + overflow: hidden; + width: 65%; + } + .extra-info-cell { + vertical-align: middle; + padding-left: 8px; + } + .panel { + width:35%; + text-align: right; + position: relative; + .menu-panel { + text-align: left; } + } - .contents { - margin: 8px 0; - } + .not-minimized { + .title { width: 65%; } + .panel { width: 35%; } + } + /* .minimized is different for desktop and mobile */ - .title { + .d-header-nav { + position: relative; + float: right; + } + + #site-logo { + max-height: 40px; + } + + .fa-home { + font-size: 1.643em; + } + + .login-button, button.sign-up-button { + float: none; + margin-top: 7px; + padding: 6px 10px; + .fa { margin-right: 3px; } + } + + button.login-button { + margin-left: 7px; + } + + .icons { + text-align: center; + margin: 0 0 0 5px; + list-style: none; + + > li { float: left; } + .icon { + display: block; + padding: 3px; + color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); + text-decoration: none; + cursor: pointer; + border-top: 1px solid transparent; + border-left: 1px solid transparent; + border-right: 1px solid transparent; + transition: all linear .15s; - #site-logo { - max-height: 40px; - } - .fa-home { - font-size: 1.643em; - } - - .panel { - float: right; - position: relative; - } - - .login-button, button.sign-up-button { - float: left; - margin-top: 7px; - padding: 6px 10px; - .fa { margin-right: 3px; } - } - - button.login-button { - margin-left: 7px; - } - - .icons { - float: left; - text-align: center; - margin: 0 0 0 5px; - list-style: none; - - > li { - float: left; - } - .icon { - display: block; - padding: 3px; - color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); - text-decoration: none; - cursor: pointer; + &:hover { + color: $primary; + background-color: dark-light-diff($primary, $secondary, 90%, -60%); border-top: 1px solid transparent; border-left: 1px solid transparent; border-right: 1px solid transparent; - transition: all linear .15s; - - - &:hover { - color: $primary; - background-color: dark-light-diff($primary, $secondary, 90%, -60%); - border-top: 1px solid transparent; - border-left: 1px solid transparent; - border-right: 1px solid transparent; - } - &:active { - color: $primary; - background-color: dark-light-diff($primary, $secondary, 90%, -60%); - } } - .drop-down-visible & { - .active .icon { - position: relative; - color: #7b7b7b; - background-color: $secondary; - cursor: default; - border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); - border-left: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); - border-right: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); - &:after { - display: block; - position: absolute; - top: 100%; - left: 0; - z-index: 1101; - width: 100%; - height: 0; - content: ""; - border-top: 1px solid $secondary; - } - &:hover { - border-bottom: none; - } - } + &:active { + color: $primary; + background-color: dark-light-diff($primary, $secondary, 90%, -60%); } - [class^="fa fa-"] { - width: 32px; - height: 32px; - font-size: 1.714em; - line-height: 32px; - display: inline-block; - } - .notifications { + } + .drop-down-visible & { + .active .icon { position: relative; + color: #7b7b7b; + background-color: $secondary; + cursor: default; + border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + border-left: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + border-right: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + &:after { + display: block; + position: absolute; + top: 100%; + left: 0; + z-index: 1101; + width: 100%; + height: 0; + content: ""; + border-top: 1px solid $secondary; + } + &:hover { + border-bottom: none; + } } - .badge-notification { - position: absolute; - top: -9px; - z-index: 1; - margin-left: 0; - } - .unread-notifications { - right: 0; - background-color: scale-color($tertiary, $lightness: 50%); - } - .unread-private-messages { - right: 25px; - } - .flagged-posts { - right: 65px; - } + } + [class^="fa fa-"] { + width: 32px; + height: 32px; + font-size: 1.714em; + line-height: 32px; + display: inline-block; + } + .notifications { + position: relative; + } + .badge-notification { + position: absolute; + top: -9px; + z-index: 1; + margin-left: 0; + } + .unread-notifications { + right: 0; + background-color: scale-color($tertiary, $lightness: 50%); + } + .unread-private-messages { + right: 25px; } .flagged-posts { - background: $danger; + right: 65px; } + } + .flagged-posts { + background: $danger; + } } diff --git a/app/assets/stylesheets/desktop/header.scss b/app/assets/stylesheets/desktop/header.scss index ff74c6714b..b5df37bba3 100644 --- a/app/assets/stylesheets/desktop/header.scss +++ b/app/assets/stylesheets/desktop/header.scss @@ -11,6 +11,11 @@ padding:8px; font-size: 2.1em; } + .minimized { + .title { width: 40px; vertical-align: baseline; } + .extra-info-cell { width: 74%; } + .panel { width: 25%; } + } } @media all diff --git a/app/assets/stylesheets/mobile/header.scss b/app/assets/stylesheets/mobile/header.scss index 3b8e8e15f4..33ab899a08 100644 --- a/app/assets/stylesheets/mobile/header.scss +++ b/app/assets/stylesheets/mobile/header.scss @@ -17,6 +17,10 @@ text-overflow: clip; } + .extra-info-cell { + display: none; + } + .icons { .badge-notification { top: -5px; @@ -31,6 +35,11 @@ button.sign-up-button { display:none; } + + .not-minimized, .minimized { + .title { width: auto; } + .panel { width: auto; } + } } #main-outlet { From 03a1aa0000403cae836d1b2d2bd112471e58c4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 17 Mar 2016 23:10:46 +0100 Subject: [PATCH 005/162] SECURITY: only add elided part of email in PM --- lib/email/receiver.rb | 17 +++++++++-------- spec/components/email/receiver_spec.rb | 11 ++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 340b0447d1..5c16c7e091 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -55,16 +55,9 @@ module Email user = find_or_create_user(@from_email, @from_display_name) @incoming_email.update_columns(user_id: user.id) - body, elided = select_body + body, @elided = select_body body ||= "" - if elided.present? - body << "\n\n" << "
" << "\n" - body << "···" << "\n" - body << elided << "\n" - body << "
" << "\n" - end - raise AutoGeneratedEmailError if is_auto_generated? raise NoBodyDetectedError if body.blank? && !@mail.has_attachments? raise InactiveUserError if !user.active && !user.staged @@ -358,6 +351,14 @@ module Email # ensure posts aren't created in the future options[:created_at] = [@mail.date, DateTime.now].min + # only add elided part in messages + if @elided.present? && options[:topic].try(:private_message?) + options[:raw] << "\n\n" << "
" << "\n" + options[:raw] << "···" << "\n" + options[:raw] << @elided << "\n" + options[:raw] << "
" << "\n" + end + manager = NewPostManager.new(options[:user], options) result = manager.perform diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index 80413c3f5f..0dce786dc5 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -119,7 +119,7 @@ describe Email::Receiver do it "removes the 'on , wrote' quoting line" do expect { process(:on_date_contact_wrote) }.to change { topic.posts.count } - expect(topic.posts.last.raw).to eq("This is the actual reply.\n\n
\n···\nOn Tue, Jan 14, 2016 at 0:42 AM, Bar Foo wrote:\n\n> This is the previous email.\n> And it had\n>\n> a lot\n>\n>\n> of lines ;)\n
") + expect(topic.posts.last.raw).to eq("This is the actual reply.") end it "removes the 'Previous Replies' marker" do @@ -193,6 +193,15 @@ describe Email::Receiver do end it "strips 'original message' context" do + expect { process(:original_message) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("This is a reply :)") + end + + it "add the 'elided' part of the original message only for private messages" do + topic.update_columns(category_id: nil, archetype: Archetype.private_message) + topic.allowed_users << user + topic.save + expect { process(:original_message) }.to change { topic.posts.count } expect(topic.posts.last.raw).to eq("This is a reply :)\n\n
\n···\n---Original Message---\nThis part should not be included\n
") end From 0cb936bdcd1bcc73c3fa9e2715153a06049f819e Mon Sep 17 00:00:00 2001 From: Tomas Ibarra Date: Thu, 17 Mar 2016 15:29:39 -0700 Subject: [PATCH 006/162] Fix pink hearts on the badges section. --- app/assets/stylesheets/common/base/user-badges.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss index 8f4d7a21b4..0b1df1891f 100644 --- a/app/assets/stylesheets/common/base/user-badges.scss +++ b/app/assets/stylesheets/common/base/user-badges.scss @@ -20,15 +20,15 @@ } &.badge-type-gold .fa { - color: #ffd700; + color: #ffd700 !important; } &.badge-type-silver .fa { - color: #c0c0c0; + color: #c0c0c0 !important; } &.badge-type-bronze .fa { - color: #cd7f32; + color: #cd7f32 !important; } } From 1c7a0cb5142faf5744c26ee6802faca43eec41ce Mon Sep 17 00:00:00 2001 From: jeremylan Date: Fri, 18 Mar 2016 09:43:48 +1100 Subject: [PATCH 007/162] Updated test to check for Site Setting full_name_required Added context and new test to check for correct user anonymizing depending on full_name_required Site Setting --- spec/services/user_anonymizer_spec.rb | 67 ++++++++++++++++++--------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/spec/services/user_anonymizer_spec.rb b/spec/services/user_anonymizer_spec.rb index 9cef2507fc..8753b8d665 100644 --- a/spec/services/user_anonymizer_spec.rb +++ b/spec/services/user_anonymizer_spec.rb @@ -32,32 +32,53 @@ describe UserAnonymizer do expect(user.user_option.mailing_list_mode).to eq(false) end - it "resets profile to default values" do - user.update_attributes( name: "Bibi", date_of_birth: 19.years.ago, title: "Super Star" ) + context "Site Settings do not require full name" do + before do + SiteSetting.full_name_required = false + end - profile = user.user_profile(true) - profile.update_attributes( location: "Moose Jaw", - website: "www.bim.com", - bio_raw: "I'm Bibi from Moosejaw. I sing and dance.", - bio_cooked: "I'm Bibi from Moosejaw. I sing and dance.", - profile_background: "http://example.com/bg.jpg", - bio_cooked_version: 2, - card_background: "http://example.com/cb.jpg") - make_anonymous - user.reload + it "resets profile to default values" do + user.update_attributes( name: "Bibi", date_of_birth: 19.years.ago, title: "Super Star" ) - expect(user.name).not_to be_present - expect(user.date_of_birth).to eq(nil) - expect(user.title).not_to be_present - expect(user.auth_token).to eq(nil) + profile = user.user_profile(true) + profile.update_attributes( location: "Moose Jaw", + website: "www.bim.com", + bio_raw: "I'm Bibi from Moosejaw. I sing and dance.", + bio_cooked: "I'm Bibi from Moosejaw. I sing and dance.", + profile_background: "http://example.com/bg.jpg", + bio_cooked_version: 2, + card_background: "http://example.com/cb.jpg") + make_anonymous + user.reload - profile = user.user_profile(true) - expect(profile.location).to eq(nil) - expect(profile.website).to eq(nil) - expect(profile.bio_cooked).to eq(nil) - expect(profile.profile_background).to eq(nil) - expect(profile.bio_cooked_version).to eq(nil) - expect(profile.card_background).to eq(nil) + expect(user.name).not_to be_present + expect(user.date_of_birth).to eq(nil) + expect(user.title).not_to be_present + expect(user.auth_token).to eq(nil) + + profile = user.user_profile(true) + expect(profile.location).to eq(nil) + expect(profile.website).to eq(nil) + expect(profile.bio_cooked).to eq(nil) + expect(profile.profile_background).to eq(nil) + expect(profile.bio_cooked_version).to eq(nil) + expect(profile.card_background).to eq(nil) + end + end + + context "Site Settings require full name" do + before do + SiteSetting.full_name_required = true + end + + it "changes name to anonymized username" do + user.update_attributes( name: "Bibi", date_of_birth: 19.years.ago, title: "Super Star" ) + + make_anonymous + user.reload + + expect(user.name).to eq(user.username) + end end it "removes the avatar" do From c2fa314684dacc319acd150dd1c6336f11e64987 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 18 Mar 2016 12:32:09 +1100 Subject: [PATCH 008/162] test username actually changes --- spec/services/user_anonymizer_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/services/user_anonymizer_spec.rb b/spec/services/user_anonymizer_spec.rb index 8753b8d665..6e7a0227a5 100644 --- a/spec/services/user_anonymizer_spec.rb +++ b/spec/services/user_anonymizer_spec.rb @@ -48,9 +48,13 @@ describe UserAnonymizer do profile_background: "http://example.com/bg.jpg", bio_cooked_version: 2, card_background: "http://example.com/cb.jpg") + + prev_username = user.username + make_anonymous user.reload + expect(user.username).not_to eq(prev_username) expect(user.name).not_to be_present expect(user.date_of_birth).to eq(nil) expect(user.title).not_to be_present @@ -72,11 +76,14 @@ describe UserAnonymizer do end it "changes name to anonymized username" do + prev_username = user.username + user.update_attributes( name: "Bibi", date_of_birth: 19.years.ago, title: "Super Star" ) make_anonymous user.reload + expect(user.name).not_to eq(prev_username) expect(user.name).to eq(user.username) end end From 37ccfbdb2a63a4ffe84dab056469f894d69b7b3a Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 18 Mar 2016 14:29:39 +1100 Subject: [PATCH 009/162] Revert "FIX: vertical centering of header, using display: table" This reverts commit 1b4e0f3300e5927c4531322a0beb1928a484a9e7. --- .../components/header-extra-info.js.es6 | 1 - .../discourse/components/home-logo.js.es6 | 2 +- .../discourse/templates/header.hbs | 118 ++++---- .../stylesheets/common/base/header.scss | 261 ++++++++---------- app/assets/stylesheets/desktop/header.scss | 5 - app/assets/stylesheets/mobile/header.scss | 9 - 6 files changed, 173 insertions(+), 223 deletions(-) diff --git a/app/assets/javascripts/discourse/components/header-extra-info.js.es6 b/app/assets/javascripts/discourse/components/header-extra-info.js.es6 index 9659bbf6af..2182f3ea34 100644 --- a/app/assets/javascripts/discourse/components/header-extra-info.js.es6 +++ b/app/assets/javascripts/discourse/components/header-extra-info.js.es6 @@ -1,7 +1,6 @@ import DiscourseURL from 'discourse/lib/url'; const TopicCategoryComponent = Ember.Component.extend({ - classNames: ['extra-info-cell'], needsSecondRow: Ember.computed.gt('secondRowItems.length', 0), secondRowItems: function() { return []; }.property(), diff --git a/app/assets/javascripts/discourse/components/home-logo.js.es6 b/app/assets/javascripts/discourse/components/home-logo.js.es6 index 0169008031..c0846e268b 100644 --- a/app/assets/javascripts/discourse/components/home-logo.js.es6 +++ b/app/assets/javascripts/discourse/components/home-logo.js.es6 @@ -2,7 +2,7 @@ import DiscourseURL from 'discourse/lib/url'; import { setting } from 'discourse/lib/computed'; export default Ember.Component.extend({ - classNameBindings: [":title", "minimized"], + classNames: ["title"], targetUrl: function() { // For overriding by customizations diff --git a/app/assets/javascripts/discourse/templates/header.hbs b/app/assets/javascripts/discourse/templates/header.hbs index ecf49f3b05..ad1af73529 100644 --- a/app/assets/javascripts/discourse/templates/header.hbs +++ b/app/assets/javascripts/discourse/templates/header.hbs @@ -1,70 +1,66 @@ -
-
-
- {{home-logo minimized=showExtraInfo}} - {{plugin-outlet "header-after-home-logo"}} +
+
+ {{home-logo minimized=showExtraInfo}} + {{plugin-outlet "header-after-home-logo"}} - {{#if showExtraInfo}} - {{header-extra-info topic=topic}} - {{/if}} +
+ {{#unless currentUser}} + {{#if showSignUpButton}} + {{d-button action="showCreateAccount" class="btn-primary btn-small sign-up-button" label="sign_up"}} + {{/if}} + {{d-button action="showLogin" class="btn-primary btn-small login-button" icon="user" label="log_in"}} + {{/unless}} + + {{plugin-outlet "header-before-dropdowns"}} + {{user-menu visible=userMenuVisible logoutAction="logout"}} + {{hamburger-menu visible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}} + {{search-menu visible=searchVisible}}
+ + {{#if showExtraInfo}} + {{header-extra-info topic=topic}} + {{/if}}
{{plugin-outlet "header-under-content"}} diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index e2adc73105..63c41255d7 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -1,167 +1,136 @@ .d-header { - width: 100%; - position: absolute; - top: 0; - z-index: 1001; - background-color: $header_background; - box-shadow: 0 2px 4px -1px rgba(0,0,0, .25); - - .docked & { - position: fixed; - backface-visibility: hidden; /** do magic for scrolling performance **/ - } - - .d-header-table { - display: table; - table-layout: fixed; /* or else long topic titles break everything */ width: 100%; - margin: 8px auto; - } - .d-header-row { - display: table-header-group; /* table-row doesn't work on mobile */ - } - .d-header-wrap { - padding: 0; - } - .title, .extra-info-cell, .panel { - display: table-cell; - } - .title { - vertical-align: middle; - padding-left: 8px; - overflow: hidden; - width: 65%; - } - .extra-info-cell { - vertical-align: middle; - padding-left: 8px; - } - .panel { - width:35%; - text-align: right; - position: relative; - .menu-panel { - text-align: left; + position: absolute; + top: 0; + z-index: 1001; + background-color: $header_background; + box-shadow: 0 2px 4px -1px rgba(0,0,0, .25); + + .docked & { + position: fixed; + backface-visibility: hidden; /** do magic for scrolling performance **/ } - } - .not-minimized { - .title { width: 65%; } - .panel { width: 35%; } - } - /* .minimized is different for desktop and mobile */ + .contents { + margin: 8px 0; + } - .d-header-nav { - position: relative; - float: right; - } - - #site-logo { - max-height: 40px; - } - - .fa-home { - font-size: 1.643em; - } - - .login-button, button.sign-up-button { - float: none; - margin-top: 7px; - padding: 6px 10px; - .fa { margin-right: 3px; } - } - - button.login-button { - margin-left: 7px; - } - - .icons { - text-align: center; - margin: 0 0 0 5px; - list-style: none; - - > li { + .title { float: left; } - .icon { - display: block; - padding: 3px; - color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); - text-decoration: none; - cursor: pointer; - border-top: 1px solid transparent; - border-left: 1px solid transparent; - border-right: 1px solid transparent; - transition: all linear .15s; + #site-logo { + max-height: 40px; + } - &:hover { - color: $primary; - background-color: dark-light-diff($primary, $secondary, 90%, -60%); + .fa-home { + font-size: 1.643em; + } + + .panel { + float: right; + position: relative; + } + + .login-button, button.sign-up-button { + float: left; + margin-top: 7px; + padding: 6px 10px; + .fa { margin-right: 3px; } + } + + button.login-button { + margin-left: 7px; + } + + .icons { + float: left; + text-align: center; + margin: 0 0 0 5px; + list-style: none; + + > li { + float: left; + } + .icon { + display: block; + padding: 3px; + color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); + text-decoration: none; + cursor: pointer; border-top: 1px solid transparent; border-left: 1px solid transparent; border-right: 1px solid transparent; - } - &:active { - color: $primary; - background-color: dark-light-diff($primary, $secondary, 90%, -60%); - } - } - .drop-down-visible & { - .active .icon { - position: relative; - color: #7b7b7b; - background-color: $secondary; - cursor: default; - border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); - border-left: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); - border-right: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); - &:after { - display: block; - position: absolute; - top: 100%; - left: 0; - z-index: 1101; - width: 100%; - height: 0; - content: ""; - border-top: 1px solid $secondary; - } + transition: all linear .15s; + + &:hover { - border-bottom: none; + color: $primary; + background-color: dark-light-diff($primary, $secondary, 90%, -60%); + border-top: 1px solid transparent; + border-left: 1px solid transparent; + border-right: 1px solid transparent; + } + &:active { + color: $primary; + background-color: dark-light-diff($primary, $secondary, 90%, -60%); } } - } - [class^="fa fa-"] { - width: 32px; - height: 32px; - font-size: 1.714em; - line-height: 32px; - display: inline-block; - } - .notifications { - position: relative; - } - .badge-notification { - position: absolute; - top: -9px; - z-index: 1; - margin-left: 0; - } - .unread-notifications { - right: 0; - background-color: scale-color($tertiary, $lightness: 50%); - } - .unread-private-messages { - right: 25px; + .drop-down-visible & { + .active .icon { + position: relative; + color: #7b7b7b; + background-color: $secondary; + cursor: default; + border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + border-left: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + border-right: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + &:after { + display: block; + position: absolute; + top: 100%; + left: 0; + z-index: 1101; + width: 100%; + height: 0; + content: ""; + border-top: 1px solid $secondary; + } + &:hover { + border-bottom: none; + } + } + } + [class^="fa fa-"] { + width: 32px; + height: 32px; + font-size: 1.714em; + line-height: 32px; + display: inline-block; + } + .notifications { + position: relative; + } + .badge-notification { + position: absolute; + top: -9px; + z-index: 1; + margin-left: 0; + } + .unread-notifications { + right: 0; + background-color: scale-color($tertiary, $lightness: 50%); + } + .unread-private-messages { + right: 25px; + } + .flagged-posts { + right: 65px; + } } .flagged-posts { - right: 65px; + background: $danger; } - } - .flagged-posts { - background: $danger; - } } diff --git a/app/assets/stylesheets/desktop/header.scss b/app/assets/stylesheets/desktop/header.scss index b5df37bba3..ff74c6714b 100644 --- a/app/assets/stylesheets/desktop/header.scss +++ b/app/assets/stylesheets/desktop/header.scss @@ -11,11 +11,6 @@ padding:8px; font-size: 2.1em; } - .minimized { - .title { width: 40px; vertical-align: baseline; } - .extra-info-cell { width: 74%; } - .panel { width: 25%; } - } } @media all diff --git a/app/assets/stylesheets/mobile/header.scss b/app/assets/stylesheets/mobile/header.scss index 33ab899a08..3b8e8e15f4 100644 --- a/app/assets/stylesheets/mobile/header.scss +++ b/app/assets/stylesheets/mobile/header.scss @@ -17,10 +17,6 @@ text-overflow: clip; } - .extra-info-cell { - display: none; - } - .icons { .badge-notification { top: -5px; @@ -35,11 +31,6 @@ button.sign-up-button { display:none; } - - .not-minimized, .minimized { - .title { width: auto; } - .panel { width: auto; } - } } #main-outlet { From 88f38f34cc1ac97a3a5081c3c183dd4dc4d77468 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 18 Mar 2016 15:12:55 +1100 Subject: [PATCH 010/162] improve text only header --- app/assets/stylesheets/desktop/header.scss | 11 +++++++++++ app/assets/stylesheets/mobile/header.scss | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/desktop/header.scss b/app/assets/stylesheets/desktop/header.scss index ff74c6714b..f4ce15a022 100644 --- a/app/assets/stylesheets/desktop/header.scss +++ b/app/assets/stylesheets/desktop/header.scss @@ -11,6 +11,11 @@ padding:8px; font-size: 2.1em; } + + .site-text-logo { + position: relative; + top: 10px; + } } @media all @@ -36,3 +41,9 @@ and (max-width : 570px) { color: dark-light-choose(scale-color($primary, $lightness: 25%), scale-color($secondary, $lightness: 75%)); } } + +header { + #site-text-logo { + line-height: 40px; + } +} diff --git a/app/assets/stylesheets/mobile/header.scss b/app/assets/stylesheets/mobile/header.scss index 3b8e8e15f4..f72f099586 100644 --- a/app/assets/stylesheets/mobile/header.scss +++ b/app/assets/stylesheets/mobile/header.scss @@ -2,6 +2,12 @@ // Discourse header // -------------------------------------------------- +@media only screen and (max-width: 320px) { + #site-text-logo { + font-size: 18px; + } +} + .d-header { #site-logo { @@ -10,7 +16,7 @@ // some protection for text-only site titles .title { - max-width: 130px; + max-width: 50%; height: 39px; overflow: hidden; padding: 0; From 50f7616d0491091bf575e962f4652b56bdfcd61c Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 18 Mar 2016 16:26:20 +1100 Subject: [PATCH 011/162] FIX: include pinned status in search results --- app/controllers/search_controller.rb | 2 ++ lib/search/grouped_search_results.rb | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 0b857278f0..92380185d2 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -25,6 +25,8 @@ class SearchController < ApplicationController search = Search.new(params[:q], search_args) result = search.execute + result.find_user_data(guardian) + serializer = serialize_data(result, GroupedSearchResultSerializer, result: result) respond_to do |format| diff --git a/lib/search/grouped_search_results.rb b/lib/search/grouped_search_results.rb index 387696c300..2fa450bb74 100644 --- a/lib/search/grouped_search_results.rb +++ b/lib/search/grouped_search_results.rb @@ -25,6 +25,14 @@ class Search @users = [] end + def find_user_data(guardian) + if user = guardian.user + topics = @posts.map(&:topic) + topic_lookup = TopicUser.lookup_for(user, topics) + topics.each { |ft| ft.user_data = topic_lookup[ft.id] } + end + end + def blurb(post) GroupedSearchResults.blurb_for(post.cooked, @term, @blurb_length) end From 77242e4680d310d0c152396824713ca2f8eb275a Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 18 Mar 2016 16:26:54 +1100 Subject: [PATCH 012/162] FEATURE: in:pinned and in:unpinned search filters --- config/locales/server.en.yml | 2 +- lib/search.rb | 12 ++++++++++++ spec/components/search_spec.rb | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 6dd216ece3..2f1a11bbed 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2722,7 +2722,7 @@ en: status:openstatus:closedstatus:archivedstatus:norepliesstatus:single_user category:foouser:foogroup:foobadge:foo in:likesin:postedin:watchingin:trackingin:private - in:bookmarksin:first + in:bookmarksin:firstin:pinnedin:unpinned posts_count:numbefore:days or dateafter:days or date

diff --git a/lib/search.rb b/lib/search.rb index fb8b989a41..5bfd9d4e2e 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -219,6 +219,18 @@ class Search posts.where("posts.post_number = 1") end + advanced_filter(/in:pinned/) do |posts| + posts.where("topics.pinned_at IS NOT NULL") + end + + advanced_filter(/in:unpinned/) do |posts| + if @guardian.user + posts.where("topics.pinned_at IS NOT NULL AND topics.id IN ( + SELECT topic_id FROM topic_users WHERE user_id = ? AND cleared_pinned_at IS NOT NULL + )", @guardian.user.id) + end + end + advanced_filter(/badge:(.*)/) do |posts,match| badge_id = Badge.where('name ilike ? OR id = ?', match, match.to_i).pluck(:id).first if badge_id diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 42a4f1866d..7a0e580643 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -408,6 +408,24 @@ describe Search do describe 'Advanced search' do + it 'supports pinned and unpinned' do + topic = Fabricate(:topic) + Fabricate(:post, raw: 'hi this is a test 123 123', topic: topic) + _post = Fabricate(:post, raw: 'boom boom shake the room', topic: topic) + + topic.update_pinned(true) + + user = Fabricate(:user) + guardian = Guardian.new(user) + + expect(Search.execute('boom in:pinned').posts.length).to eq(1) + expect(Search.execute('boom in:unpinned', guardian: guardian).posts.length).to eq(0) + + topic.clear_pin_for(user) + + expect(Search.execute('boom in:unpinned', guardian: guardian).posts.length).to eq(1) + end + it 'supports before and after in:first user:' do time = Time.zone.parse('2001-05-20 2:55') From be3a5a56ccc284b352aa65080fc6b955f73cc72a Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Fri, 18 Mar 2016 13:10:10 +0530 Subject: [PATCH 013/162] UX: show accurate date and time on admin user page --- app/assets/javascripts/admin/templates/user-index.hbs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/admin/templates/user-index.hbs b/app/assets/javascripts/admin/templates/user-index.hbs index 5c1c402d42..608d7c38ba 100644 --- a/app/assets/javascripts/admin/templates/user-index.hbs +++ b/app/assets/javascripts/admin/templates/user-index.hbs @@ -386,15 +386,15 @@
{{i18n 'created'}}
-
{{{model.created_at_age}}}
+
{{format-date model.created_at leaveAgo="true"}}
{{i18n 'admin.users.last_emailed'}}
-
{{{model.last_emailed_age}}}
+
{{format-date model.last_emailed_at leaveAgo="true"}}
{{i18n 'last_seen'}}
-
{{{model.last_seen_age}}}
+
{{format-date model.last_seen_at leaveAgo="true"}}
{{i18n 'admin.user.like_count'}}
From f595ba336a8f5102d680c5c01c603f8b90cf5231 Mon Sep 17 00:00:00 2001 From: James Kiesel Date: Fri, 18 Mar 2016 22:11:30 +1300 Subject: [PATCH 014/162] DOCS: Add additional help for how to use 'h' helper with virtual DOM --- .../javascripts/discourse/widgets/decorator-helper.js.es6 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 b/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 index 13db7ce836..1679a708ec 100644 --- a/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 +++ b/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 @@ -19,6 +19,8 @@ class DecoratorHelper { * // renders `

paragraph

` * return helper.h('div.some-class', helper.h('p', 'paragraph')); * ``` + * Check out https://github.com/Matt-Esch/virtual-dom/blob/master/virtual-hyperscript/README.md + * for more details on how to construct markup with h. **/ // h() is attached via `prototype` below From f15d463eb8a73edd180ec0d6f3eff15187eded51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 18 Mar 2016 12:16:37 +0100 Subject: [PATCH 015/162] FIX: user 'UserBlocker' when blocking a new user --- app/models/queued_post.rb | 4 +--- app/services/spam_rule/auto_block.rb | 2 +- app/services/user_blocker.rb | 4 ++-- lib/new_post_manager.rb | 9 +++------ spec/services/auto_block_spec.rb | 4 ++-- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/models/queued_post.rb b/app/models/queued_post.rb index 66e75e9601..9dd479a73c 100644 --- a/app/models/queued_post.rb +++ b/app/models/queued_post.rb @@ -64,9 +64,7 @@ class QueuedPost < ActiveRecord::Base QueuedPost.transaction do change_to!(:approved, approved_by) - if user.blocked? - user.update_columns(blocked: false) - end + UserBlocker.unblock(user, approved_by) if user.blocked? creator = PostCreator.new(user, create_options.merge(skip_validations: true)) created_post = creator.create diff --git a/app/services/spam_rule/auto_block.rb b/app/services/spam_rule/auto_block.rb index 8a6cea9f30..6c5d501872 100644 --- a/app/services/spam_rule/auto_block.rb +++ b/app/services/spam_rule/auto_block.rb @@ -38,7 +38,7 @@ class SpamRule::AutoBlock def block_user Post.transaction do - if UserBlocker.block(@user, nil, {message: :too_many_spam_flags}) and SiteSetting.notify_mods_when_user_blocked + if UserBlocker.block(@user, Discourse.system_user, message: :too_many_spam_flags) && SiteSetting.notify_mods_when_user_blocked GroupMessage.create(Group[:moderators].name, :user_automatically_blocked, {user: @user, limit_once_per: false}) end end diff --git a/app/services/user_blocker.rb b/app/services/user_blocker.rb index 1ff9983b7e..ee9ee1b68e 100644 --- a/app/services/user_blocker.rb +++ b/app/services/user_blocker.rb @@ -27,8 +27,8 @@ class UserBlocker def hide_posts Post.where(user_id: @user.id).update_all(["hidden = true, hidden_reason_id = COALESCE(hidden_reason_id, ?)", Post.hidden_reasons[:new_user_spam_threshold_reached]]) - topic_ids = Post.where('user_id = ? and post_number = ?', @user.id, 1).pluck(:topic_id) - Topic.where(id: topic_ids).update_all({ visible: false }) unless topic_ids.empty? + topic_ids = Post.where(user_id: @user.id, post_number: 1).pluck(:topic_id) + Topic.where(id: topic_ids).update_all(visible: false) unless topic_ids.empty? end def unblock diff --git a/lib/new_post_manager.rb b/lib/new_post_manager.rb index 923315d915..f354a277ad 100644 --- a/lib/new_post_manager.rb +++ b/lib/new_post_manager.rb @@ -82,14 +82,11 @@ class NewPostManager result = manager.enqueue('default') - block = is_fast_typer?(manager) - - block ||= matches_auto_block_regex?(manager) - - manager.user.update_columns(blocked: true) if block + if is_fast_typer?(manager) || matches_auto_block_regex?(manager) + UserBlocker.block(manager.user, Discourse.system_user, keep_posts: true) + end result - end end diff --git a/spec/services/auto_block_spec.rb b/spec/services/auto_block_spec.rb index dcecb04c8e..d23738835a 100644 --- a/spec/services/auto_block_spec.rb +++ b/spec/services/auto_block_spec.rb @@ -100,7 +100,7 @@ describe SpamRule::AutoBlock do context 'user is not blocked' do before do - UserBlocker.expects(:block).with(user, nil, has_entries(message: :too_many_spam_flags)).returns(true) + UserBlocker.expects(:block).with(user, Discourse.system_user, message: :too_many_spam_flags).returns(true) end it 'prevents the user from making new posts' do @@ -126,7 +126,7 @@ describe SpamRule::AutoBlock do context 'user is already blocked' do before do - UserBlocker.expects(:block).with(user, nil, has_entries(message: :too_many_spam_flags)).returns(false) + UserBlocker.expects(:block).with(user, Discourse.system_user, message: :too_many_spam_flags).returns(false) end it "doesn't send a pm to moderators if the user is already blocked" do From 99d93264b7d1043f73976a4b343f8417822b7cd8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 18 Mar 2016 09:48:57 -0400 Subject: [PATCH 016/162] Move autosize from admin/lib to discourse/lib --- .eslintignore | 2 +- .../javascripts/admin/components/expanding-text-area.js.es6 | 2 +- app/assets/javascripts/{admin => discourse}/lib/autosize.js.es6 | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename app/assets/javascripts/{admin => discourse}/lib/autosize.js.es6 (100%) diff --git a/.eslintignore b/.eslintignore index 47d86595d9..a520c62c91 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,7 +7,7 @@ app/assets/javascripts/vendor.js app/assets/javascripts/locales/i18n.js app/assets/javascripts/defer/html-sanitizer-bundle.js app/assets/javascripts/ember-addons/ -app/assets/javascripts/admin/lib/autosize.js.es6 +app/assets/javascripts/discourse/lib/autosize.js.es6 lib/javascripts/locale/ lib/javascripts/messageformat.js lib/javascripts/moment.js diff --git a/app/assets/javascripts/admin/components/expanding-text-area.js.es6 b/app/assets/javascripts/admin/components/expanding-text-area.js.es6 index 3b5a260690..f7259e8301 100644 --- a/app/assets/javascripts/admin/components/expanding-text-area.js.es6 +++ b/app/assets/javascripts/admin/components/expanding-text-area.js.es6 @@ -1,5 +1,5 @@ import { on, observes } from 'ember-addons/ember-computed-decorators'; -import autosize from 'admin/lib/autosize'; +import autosize from 'discourse/lib/autosize'; export default Ember.TextArea.extend({ @on('didInsertElement') diff --git a/app/assets/javascripts/admin/lib/autosize.js.es6 b/app/assets/javascripts/discourse/lib/autosize.js.es6 similarity index 100% rename from app/assets/javascripts/admin/lib/autosize.js.es6 rename to app/assets/javascripts/discourse/lib/autosize.js.es6 From 1fba835d4f47990d31b49e6f037e3cf249e687bb Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 17 Mar 2016 14:41:00 -0400 Subject: [PATCH 017/162] FIX: Use a logging table for daily likes given. Use it for badges. --- app/controllers/post_actions_controller.rb | 9 --- app/models/badge.rb | 10 +-- app/models/given_daily_like.rb | 34 ++++++++++ app/models/post_action.rb | 4 +- app/models/user_history.rb | 3 +- ...20160317174357_create_given_daily_likes.rb | 34 ++++++++++ .../post_actions_controller_spec.rb | 14 ---- spec/models/given_daily_like_spec.rb | 50 ++++++++++++++ spec/models/post_action_spec.rb | 67 +++++++++++-------- 9 files changed, 165 insertions(+), 60 deletions(-) create mode 100644 app/models/given_daily_like.rb create mode 100644 db/migrate/20160317174357_create_given_daily_likes.rb create mode 100644 spec/models/given_daily_like_spec.rb diff --git a/app/controllers/post_actions_controller.rb b/app/controllers/post_actions_controller.rb index f707c57e67..4a7585890e 100644 --- a/app/controllers/post_actions_controller.rb +++ b/app/controllers/post_actions_controller.rb @@ -23,15 +23,6 @@ class PostActionsController < ApplicationController @post.reload render_post_json(@post, _add_raw = false) end - rescue RateLimiter::LimitExceeded => e - # Special case: if we hit the create like rate limit, record it in user history - # so we can award a badge - if e.type == "create_like" - UserHistory.create!(action: UserHistory.actions[:rate_limited_like], - target_user_id: current_user.id, - post_id: @post.id) - end - render_rate_limit_error(e) end def destroy diff --git a/app/models/badge.rb b/app/models/badge.rb index 7e6b237f94..8baf6180ed 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -286,11 +286,11 @@ SQL def self.like_rate_limit(count) <<-SQL - SELECT uh.target_user_id AS user_id, MAX(uh.created_at) AS granted_at - FROM user_histories AS uh - WHERE uh.action = #{UserHistory.actions[:rate_limited_like]} - AND (:backfill OR uh.target_user_id IN (:user_ids)) - GROUP BY uh.target_user_id + SELECT gdl.user_id, current_timestamp AS granted_at + FROM given_daily_likes AS gdl + WHERE gdl.limit_reached + AND (:backfill OR gdl.user_id IN (:user_ids)) + GROUP BY gdl.user_id HAVING COUNT(*) >= #{count} SQL end diff --git a/app/models/given_daily_like.rb b/app/models/given_daily_like.rb new file mode 100644 index 0000000000..38161e0eb5 --- /dev/null +++ b/app/models/given_daily_like.rb @@ -0,0 +1,34 @@ +class GivenDailyLike < ActiveRecord::Base + belongs_to :user + + def self.find_for(user_id, date) + where(user_id: user_id, given_date: date) + end + + def self.increment_for(user_id) + return unless user_id + given_date = Date.today + + # upsert would be nice here + rows = find_for(user_id, given_date).update_all('likes_given = likes_given + 1') + + if rows == 0 + create(user_id: user_id, given_date: given_date, likes_given: 1) + else + find_for(user_id, given_date) + .where('limit_reached = false AND likes_given >= :limit', limit: SiteSetting.max_likes_per_day) + .update_all(limit_reached: true) + end + end + + def self.decrement_for(user_id) + return unless user_id + + given_date = Date.today + + find_for(user_id, given_date).update_all('likes_given = likes_given - 1') + find_for(user_id, given_date) + .where('limit_reached = true AND likes_given < :limit', limit: SiteSetting.max_likes_per_day) + .update_all(limit_reached: false) + end +end diff --git a/app/models/post_action.rb b/app/models/post_action.rb index 31f221cc9f..08aaae4a0c 100644 --- a/app/models/post_action.rb +++ b/app/models/post_action.rb @@ -286,12 +286,11 @@ SQL BadgeGranter.queue_badge_grant(Badge::Trigger::PostAction, post_action: post_action) end end + GivenDailyLike.increment_for(user.id) # agree with other flags if staff_took_action PostAction.agree_flags!(post, user) - - # update counters post_action.try(:update_counters) end @@ -311,6 +310,7 @@ SQL if action = finder.first action.remove_act!(user) action.post.unhide! if action.staff_took_action + GivenDailyLike.decrement_for(user.id) end end diff --git a/app/models/user_history.rb b/app/models/user_history.rb index ccabc8ce69..d2afa1c526 100644 --- a/app/models/user_history.rb +++ b/app/models/user_history.rb @@ -52,7 +52,8 @@ class UserHistory < ActiveRecord::Base grant_moderation: 34, revoke_moderation: 35, backup_operation: 36, - rate_limited_like: 37) + rate_limited_like: 37 # not used anymore + ) end # Staff actions is a subset of all actions, used to audit actions taken by staff users. diff --git a/db/migrate/20160317174357_create_given_daily_likes.rb b/db/migrate/20160317174357_create_given_daily_likes.rb new file mode 100644 index 0000000000..42cf767b95 --- /dev/null +++ b/db/migrate/20160317174357_create_given_daily_likes.rb @@ -0,0 +1,34 @@ +class CreateGivenDailyLikes < ActiveRecord::Migration + def up + create_table :given_daily_likes, id: false, force: true do |t| + t.integer :user_id, null: false + t.integer :likes_given, null: false + t.date :given_date, null: false + t.boolean :limit_reached, null: false, default: false + end + add_index :given_daily_likes, [:user_id, :given_date], unique: true + add_index :given_daily_likes, [:limit_reached, :user_id] + + max_likes_rows = execute("SELECT value FROM site_settings WHERE name = 'max_likes_per_day'") + if max_likes_rows && max_likes_rows.cmd_tuples > 0 + max_likes = max_likes_rows[0]['value'].to_i + end + max_likes ||= 50 + + execute "INSERT INTO given_daily_likes (user_id, likes_given, limit_reached, given_date) + SELECT pa.user_id, + COUNT(*), + CASE WHEN COUNT(*) >= #{max_likes} THEN true + ELSE false + END, + pa.created_at::date + FROM post_actions AS pa + WHERE pa.post_action_type_id = 2 + AND pa.deleted_at IS NULL + GROUP BY pa.user_id, pa.created_at::date" + end + + def down + drop_table :given_daily_likes + end +end diff --git a/spec/controllers/post_actions_controller_spec.rb b/spec/controllers/post_actions_controller_spec.rb index 0a441a0af0..ca822b832e 100644 --- a/spec/controllers/post_actions_controller_spec.rb +++ b/spec/controllers/post_actions_controller_spec.rb @@ -7,20 +7,6 @@ describe PostActionsController do expect { xhr :post, :create }.to raise_error(Discourse::NotLoggedIn) end - describe 'logged in as regular user' do - before do - @user = log_in(:user) - @post = Fabricate(:post, user: Fabricate(:coding_horror)) - end - - it 'creates user history if the rate limit for a like is hit' do - PostAction.expects(:act).once.raises(RateLimiter::LimitExceeded.new(60, 'create_like')) - expect(-> { - xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like] - }).to change(UserHistory, :count).by(1) - end - end - describe 'logged in as moderator' do before do @user = log_in(:moderator) diff --git a/spec/models/given_daily_like_spec.rb b/spec/models/given_daily_like_spec.rb new file mode 100644 index 0000000000..b3a8636776 --- /dev/null +++ b/spec/models/given_daily_like_spec.rb @@ -0,0 +1,50 @@ +require 'rails_helper' + +describe GivenDailyLike do + + it 'no errors without a user' do + expect(-> { GivenDailyLike.increment_for(nil) }).not_to raise_error + expect(-> { GivenDailyLike.decrement_for(nil) }).not_to raise_error + end + + context 'with a user' do + let(:user) { Fabricate(:user) } + + def value_for(user_id, date) + GivenDailyLike.find_for(user_id, date).pluck(:likes_given)[0] || 0 + end + + def limit_reached_for(user_id, date) + GivenDailyLike.find_for(user_id, date).pluck(:limit_reached)[0] || false + end + + it 'can be incremented and decremented' do + SiteSetting.max_likes_per_day = 2 + + Timecop.freeze(Date.today) do + dt = Date.today + + expect(value_for(user.id, dt)).to eq(0) + expect(limit_reached_for(user.id, dt)).to eq(false) + + GivenDailyLike.increment_for(user.id) + expect(value_for(user.id, dt)).to eq(1) + expect(limit_reached_for(user.id, dt)).to eq(false) + + GivenDailyLike.increment_for(user.id) + expect(value_for(user.id, dt)).to eq(2) + expect(limit_reached_for(user.id, dt)).to eq(true) + + GivenDailyLike.decrement_for(user.id) + expect(value_for(user.id, dt)).to eq(1) + expect(limit_reached_for(user.id, dt)).to eq(false) + + GivenDailyLike.decrement_for(user.id) + expect(value_for(user.id, dt)).to eq(0) + expect(limit_reached_for(user.id, dt)).to eq(false) + end + end + + end + +end diff --git a/spec/models/post_action_spec.rb b/spec/models/post_action_spec.rb index e7a906bc60..d3b2a9ad4f 100644 --- a/spec/models/post_action_spec.rb +++ b/spec/models/post_action_spec.rb @@ -168,19 +168,24 @@ describe PostAction do describe "update_counters" do it "properly updates topic counters" do - # we need this to test it - TopicUser.change(codinghorror, post.topic, posted: true) + Timecop.freeze(Date.today) do + # we need this to test it + TopicUser.change(codinghorror, post.topic, posted: true) - PostAction.act(moderator, post, PostActionType.types[:like]) - PostAction.act(codinghorror, second_post, PostActionType.types[:like]) + expect(GivenDailyLike.value_for(moderator.id, Date.today)).to eq(0) - post.topic.reload - expect(post.topic.like_count).to eq(2) + PostAction.act(moderator, post, PostActionType.types[:like]) + PostAction.act(codinghorror, second_post, PostActionType.types[:like]) - tu = TopicUser.get(post.topic, codinghorror) - expect(tu.liked).to be true - expect(tu.bookmarked).to be false + post.topic.reload + expect(post.topic.like_count).to eq(2) + expect(GivenDailyLike.value_for(moderator.id, Date.today)).to eq(1) + + tu = TopicUser.get(post.topic, codinghorror) + expect(tu.liked).to be true + expect(tu.bookmarked).to be false + end end end @@ -239,29 +244,33 @@ describe PostAction do end it 'should increase the `like_count` and `like_score` when a user likes something' do - PostAction.act(codinghorror, post, PostActionType.types[:like]) - post.reload - expect(post.like_count).to eq(1) - expect(post.like_score).to eq(1) - post.topic.reload - expect(post.topic.like_count).to eq(1) + Timecop.freeze(Date.today) do + PostAction.act(codinghorror, post, PostActionType.types[:like]) + post.reload + expect(post.like_count).to eq(1) + expect(post.like_score).to eq(1) + post.topic.reload + expect(post.topic.like_count).to eq(1) + expect(GivenDailyLike.value_for(codinghorror.id, Date.today)).to eq(1) - # When a staff member likes it - PostAction.act(moderator, post, PostActionType.types[:like]) - post.reload - expect(post.like_count).to eq(2) - expect(post.like_score).to eq(4) + # When a staff member likes it + PostAction.act(moderator, post, PostActionType.types[:like]) + post.reload + expect(post.like_count).to eq(2) + expect(post.like_score).to eq(4) - # Removing likes - PostAction.remove_act(codinghorror, post, PostActionType.types[:like]) - post.reload - expect(post.like_count).to eq(1) - expect(post.like_score).to eq(3) + # Removing likes + PostAction.remove_act(codinghorror, post, PostActionType.types[:like]) + post.reload + expect(post.like_count).to eq(1) + expect(post.like_score).to eq(3) + expect(GivenDailyLike.value_for(codinghorror.id, Date.today)).to eq(0) - PostAction.remove_act(moderator, post, PostActionType.types[:like]) - post.reload - expect(post.like_count).to eq(0) - expect(post.like_score).to eq(0) + PostAction.remove_act(moderator, post, PostActionType.types[:like]) + post.reload + expect(post.like_count).to eq(0) + expect(post.like_score).to eq(0) + end end end From 5d4ee2ca1d746abcd2cb15e80a56418f614cff79 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 18 Mar 2016 11:17:51 -0400 Subject: [PATCH 018/162] FEATURE: Warn a user when they have few likes remaining --- .../javascripts/discourse/mixins/ajax.js | 4 ++++ .../discourse/models/action-summary.js.es6 | 20 ++++++++++------- .../javascripts/discourse/widgets/post.js.es6 | 22 ++++++++++++++++++- .../discourse/widgets/widget.js.es6 | 1 + app/controllers/post_actions_controller.rb | 6 +++++ config/locales/client.en.yml | 2 ++ lib/rate_limiter.rb | 9 ++++++++ spec/components/rate_limiter_spec.rb | 16 ++++++++++---- spec/models/post_action_spec.rb | 12 ++++++---- 9 files changed, 75 insertions(+), 17 deletions(-) diff --git a/app/assets/javascripts/discourse/mixins/ajax.js b/app/assets/javascripts/discourse/mixins/ajax.js index 36e42f5122..d68d457d25 100644 --- a/app/assets/javascripts/discourse/mixins/ajax.js +++ b/app/assets/javascripts/discourse/mixins/ajax.js @@ -66,6 +66,10 @@ Discourse.Ajax = Em.Mixin.create({ }); } + if (args.returnXHR) { + data = { result: data, xhr }; + } + Ember.run(null, resolve, data); }; diff --git a/app/assets/javascripts/discourse/models/action-summary.js.es6 b/app/assets/javascripts/discourse/models/action-summary.js.es6 index a39d78fb50..1a58dc4a0e 100644 --- a/app/assets/javascripts/discourse/models/action-summary.js.es6 +++ b/app/assets/javascripts/discourse/models/action-summary.js.es6 @@ -18,10 +18,7 @@ export default RestModel.extend({ }, togglePromise(post) { - if (!this.get('acted')) { - return this.act(post).then(() => true); - } - return this.undo(post).then(() => false); + return this.get('acted') ? this.undo(post) : this.act(post); }, toggle(post) { @@ -64,11 +61,15 @@ export default RestModel.extend({ message: opts.message, take_action: opts.takeAction, flag_topic: this.get('flagTopic') ? true : false - } - }).then(function(result) { + }, + returnXHR: true, + }).then(function(data) { if (!self.get('flagTopic')) { - return post.updateActionsSummary(result); + post.updateActionsSummary(data.result); } + const remaining = parseInt(data.xhr.getResponseHeader('Discourse-Actions-Remaining') || 0); + const max = parseInt(data.xhr.getResponseHeader('Discourse-Actions-Max') || 0); + return { acted: true, remaining, max }; }).catch(function(error) { popupAjaxError(error); self.removeAction(post); @@ -83,7 +84,10 @@ export default RestModel.extend({ return Discourse.ajax("/post_actions/" + post.get('id'), { type: 'DELETE', data: { post_action_type_id: this.get('id') } - }).then(result => post.updateActionsSummary(result)); + }).then(result => { + post.updateActionsSummary(result); + return { acted: false }; + }); }, deferFlags(post) { diff --git a/app/assets/javascripts/discourse/widgets/post.js.es6 b/app/assets/javascripts/discourse/widgets/post.js.es6 index f992101401..ffa1b1df67 100644 --- a/app/assets/javascripts/discourse/widgets/post.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post.js.es6 @@ -423,7 +423,27 @@ export default createWidget('post', { const likeAction = post.get('likeAction'); if (likeAction && likeAction.get('canToggle')) { - return likeAction.togglePromise(post); + return likeAction.togglePromise(post).then(result => this._warnIfClose(result)); + } + }, + + _warnIfClose(result) { + if (!result || !result.acted) { return; } + + const kvs = this.keyValueStore; + const lastWarnedLikes = kvs.get('lastWarnedLikes'); + + // only warn once per day + const yesterday = new Date().getTime() - 1000 * 60 * 60 * 24; + if (lastWarnedLikes && parseInt(lastWarnedLikes) > yesterday) { + return; + } + + const { remaining, max } = result; + const threshold = Math.ceil(max * 0.1); + if (remaining === threshold) { + bootbox.alert(I18n.t('post.few_likes_left')); + kvs.set({ key: 'lastWarnedLikes', value: new Date().getTime() }); } }, diff --git a/app/assets/javascripts/discourse/widgets/widget.js.es6 b/app/assets/javascripts/discourse/widgets/widget.js.es6 index c05b2500e5..63f6930485 100644 --- a/app/assets/javascripts/discourse/widgets/widget.js.es6 +++ b/app/assets/javascripts/discourse/widgets/widget.js.es6 @@ -119,6 +119,7 @@ export default class Widget { this.currentUser = container.lookup('current-user:main'); this.store = container.lookup('store:main'); this.appEvents = container.lookup('app-events:main'); + this.keyValueStore = container.lookup('key-value-store:main'); if (this.name) { const custom = _customSettings[this.name]; diff --git a/app/controllers/post_actions_controller.rb b/app/controllers/post_actions_controller.rb index 4a7585890e..c23d2ed6f4 100644 --- a/app/controllers/post_actions_controller.rb +++ b/app/controllers/post_actions_controller.rb @@ -21,6 +21,12 @@ class PostActionsController < ApplicationController else # We need to reload or otherwise we are showing the old values on the front end @post.reload + + if @post_action_type_id == PostActionType.types[:like] + limiter = post_action.post_action_rate_limiter + response.headers['Discourse-Actions-Remaining'] = limiter.remaining.to_s + response.headers['Discourse-Actions-Max'] = limiter.max.to_s + end render_post_json(@post, _add_raw = false) end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0ff27a0c40..2ab59d1301 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1511,6 +1511,8 @@ en: archetypes: save: 'Save Options' + few_likes_left: "Warning: You have few likes left to give today. Use them wisely!" + controls: reply: "begin composing a reply to this post" like: "like this post" diff --git a/lib/rate_limiter.rb b/lib/rate_limiter.rb index df678960d7..0985db9116 100644 --- a/lib/rate_limiter.rb +++ b/lib/rate_limiter.rb @@ -67,6 +67,15 @@ class RateLimiter $redis.lpop(@key) end + def remaining + return @max if @user && @user.staff? + + arr = $redis.lrange(@key, 0, @max) || [] + t0 = Time.now.to_i + arr.reject! {|a| (t0 - a.to_i) > @secs} + @max - arr.size + end + private def seconds_to_wait diff --git a/spec/components/rate_limiter_spec.rb b/spec/components/rate_limiter_spec.rb index e997db7c68..1cf43b11c9 100644 --- a/spec/components/rate_limiter_spec.rb +++ b/spec/components/rate_limiter_spec.rb @@ -39,6 +39,16 @@ describe RateLimiter do end end + context "remaining" do + it "updates correctly" do + expect(rate_limiter.remaining).to eq(2) + rate_limiter.performed! + expect(rate_limiter.remaining).to eq(1) + rate_limiter.performed! + expect(rate_limiter.remaining).to eq(0) + end + end + context "multiple calls" do before do rate_limiter.performed! @@ -47,6 +57,7 @@ describe RateLimiter do it "returns false for can_perform when the limit has been hit" do expect(rate_limiter.can_perform?).to eq(false) + expect(rate_limiter.remaining).to eq(0) end it "raises an error the third time called" do @@ -54,10 +65,10 @@ describe RateLimiter do end context "as an admin/moderator" do - it "returns true for can_perform if the user is an admin" do user.admin = true expect(rate_limiter.can_perform?).to eq(true) + expect(rate_limiter.remaining).to eq(2) end it "doesn't raise an error when an admin performs the task" do @@ -74,8 +85,6 @@ describe RateLimiter do user.moderator = true expect { rate_limiter.performed! }.not_to raise_error end - - end context "rollback!" do @@ -90,7 +99,6 @@ describe RateLimiter do it "raises no error now that there is room" do expect { rate_limiter.performed! }.not_to raise_error end - end end diff --git a/spec/models/post_action_spec.rb b/spec/models/post_action_spec.rb index d3b2a9ad4f..e5809521e7 100644 --- a/spec/models/post_action_spec.rb +++ b/spec/models/post_action_spec.rb @@ -12,6 +12,10 @@ describe PostAction do let(:second_post) { Fabricate(:post, topic_id: post.topic_id) } let(:bookmark) { PostAction.new(user_id: post.user_id, post_action_type_id: PostActionType.types[:bookmark] , post_id: post.id) } + def value_for(user_id, dt) + GivenDailyLike.find_for(user_id, dt).pluck(:likes_given)[0] || 0 + end + describe "rate limits" do it "limits redo/undo" do @@ -172,7 +176,7 @@ describe PostAction do # we need this to test it TopicUser.change(codinghorror, post.topic, posted: true) - expect(GivenDailyLike.value_for(moderator.id, Date.today)).to eq(0) + expect(value_for(moderator.id, Date.today)).to eq(0) PostAction.act(moderator, post, PostActionType.types[:like]) PostAction.act(codinghorror, second_post, PostActionType.types[:like]) @@ -180,7 +184,7 @@ describe PostAction do post.topic.reload expect(post.topic.like_count).to eq(2) - expect(GivenDailyLike.value_for(moderator.id, Date.today)).to eq(1) + expect(value_for(moderator.id, Date.today)).to eq(1) tu = TopicUser.get(post.topic, codinghorror) expect(tu.liked).to be true @@ -251,7 +255,7 @@ describe PostAction do expect(post.like_score).to eq(1) post.topic.reload expect(post.topic.like_count).to eq(1) - expect(GivenDailyLike.value_for(codinghorror.id, Date.today)).to eq(1) + expect(value_for(codinghorror.id, Date.today)).to eq(1) # When a staff member likes it PostAction.act(moderator, post, PostActionType.types[:like]) @@ -264,7 +268,7 @@ describe PostAction do post.reload expect(post.like_count).to eq(1) expect(post.like_score).to eq(3) - expect(GivenDailyLike.value_for(codinghorror.id, Date.today)).to eq(0) + expect(value_for(codinghorror.id, Date.today)).to eq(0) PostAction.remove_act(moderator, post, PostActionType.types[:like]) post.reload From a4a01d4122a91ca33a5cbee3b7ba517f4c4b1947 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Fri, 18 Mar 2016 12:03:14 -0400 Subject: [PATCH 019/162] FIX: admin javascript bundle was broken --- app/assets/javascripts/main_include_admin.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/main_include_admin.js b/app/assets/javascripts/main_include_admin.js index 1217131b4f..7816da8382 100644 --- a/app/assets/javascripts/main_include_admin.js +++ b/app/assets/javascripts/main_include_admin.js @@ -9,7 +9,6 @@ //= require admin/routes/admin-email-logs //= require admin/controllers/admin-email-skipped //= require discourse/lib/export-result -//= require_tree ./admin/lib //= require_tree ./admin //= require resumable.js From cb8abd09a9158403cc1f69c56c2dfb2b9d6e98ef Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Fri, 18 Mar 2016 12:21:37 -0400 Subject: [PATCH 020/162] FIX: users with invalid website in profile get 422 error when viewing topics and other routes --- app/models/user_profile.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user_profile.rb b/app/models/user_profile.rb index 2dca5c2f10..7137290326 100644 --- a/app/models/user_profile.rb +++ b/app/models/user_profile.rb @@ -4,7 +4,7 @@ class UserProfile < ActiveRecord::Base 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 :website, format: { with: WEBSITE_REGEXP }, allow_blank: true + validates :website, format: { with: WEBSITE_REGEXP }, allow_blank: true, if: Proc.new { |c| c.new_record? || c.website_changed? } validates :user, presence: true before_save :cook after_save :trigger_badges From 54445f21c1c4f539b2bdd5979629a51aae77c4da Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 18 Mar 2016 12:53:48 -0400 Subject: [PATCH 021/162] FIX: .js files can't use ES6 syntax --- app/assets/javascripts/discourse/mixins/ajax.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/mixins/ajax.js b/app/assets/javascripts/discourse/mixins/ajax.js index d68d457d25..9c1d2d4dfe 100644 --- a/app/assets/javascripts/discourse/mixins/ajax.js +++ b/app/assets/javascripts/discourse/mixins/ajax.js @@ -67,7 +67,7 @@ Discourse.Ajax = Em.Mixin.create({ } if (args.returnXHR) { - data = { result: data, xhr }; + data = { result: data, xhr: xhr }; } Ember.run(null, resolve, data); From bd83cf7f4c3e51ff40b564b9d1d417e0d6935546 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Fri, 18 Mar 2016 21:49:45 +0530 Subject: [PATCH 022/162] FEATURE: add group posts and mentions RSS --- app/controllers/groups_controller.rb | 19 +++++++++++++++++++ config/locales/server.en.yml | 2 ++ config/routes.rb | 3 +++ spec/controllers/groups_controller_spec.rb | 16 ++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index e33dfa449e..311a11b2f9 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,6 +1,7 @@ class GroupsController < ApplicationController before_filter :ensure_logged_in, only: [:set_notifications] + skip_before_filter :preload_json, :check_xhr, only: [:posts_feed, :mentions_feed] def show render_serialized(find_group(:id), BasicGroupSerializer) @@ -29,6 +30,15 @@ class GroupsController < ApplicationController render_serialized posts.to_a, GroupPostSerializer end + def posts_feed + group = find_group(:group_id) + @posts = group.posts_for(guardian).limit(50) + @title = "#{SiteSetting.title} - #{I18n.t("rss_description.group_posts", group_name: group.name)}" + @link = Discourse.base_url + @description = I18n.t("rss_description.group_posts", group_name: group.name) + render 'posts/latest', formats: [:rss] + end + def topics group = find_group(:group_id) posts = group.posts_for(guardian, params[:before_post_id]).where(post_number: 1).limit(20) @@ -41,6 +51,15 @@ class GroupsController < ApplicationController render_serialized posts.to_a, GroupPostSerializer end + def mentions_feed + group = find_group(:group_id) + @posts = group.mentioned_posts_for(guardian).limit(50) + @title = "#{SiteSetting.title} - #{I18n.t("rss_description.group_mentions", group_name: group.name)}" + @link = Discourse.base_url + @description = I18n.t("rss_description.group_mentions", group_name: group.name) + render 'posts/latest', formats: [:rss] + end + def messages group = find_group(:group_id) posts = if guardian.can_see_group_messages?(group) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 2f1a11bbed..a85bd940cf 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -204,6 +204,8 @@ en: hot: "Hot topics" top: "Top topics" posts: "Latest posts" + group_posts: "Latest posts from %{group_name}" + group_mentions: "Latest mentions from %{group_name}" too_late_to_edit: "That post was created too long ago. It can no longer be edited or deleted." revert_version_same: "The current version is same as the version you are trying to revert to." diff --git a/config/routes.rb b/config/routes.rb index ce8a19fc7d..fab10862bb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -354,6 +354,9 @@ Discourse::Application.routes.draw do get "posts/:username/flagged" => "posts#flagged_posts", constraints: {username: USERNAME_ROUTE_FORMAT} resources :groups do + get "posts.rss" => "groups#posts_feed", format: :rss + get "mentions.rss" => "groups#mentions_feed", format: :rss + get 'members' get 'posts' get 'topics' diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 8a42a6fe63..fded427a08 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -228,4 +228,20 @@ describe GroupsController do end + describe '.posts_feed' do + it 'renders RSS' do + get :posts_feed, group_id: group.name, format: :rss + expect(response).to be_success + expect(response.content_type).to eq('application/rss+xml') + end + end + + describe '.mentions_feed' do + it 'renders RSS' do + get :mentions_feed, group_id: group.name, format: :rss + expect(response).to be_success + expect(response.content_type).to eq('application/rss+xml') + end + end + end From b1ac7491debc5538ba3f29b4379918bcbcfc6c8a Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 18 Mar 2016 13:17:16 -0400 Subject: [PATCH 023/162] Include the topic's creator in transformed posts --- app/assets/javascripts/discourse/lib/transform-post.js.es6 | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/discourse/lib/transform-post.js.es6 b/app/assets/javascripts/discourse/lib/transform-post.js.es6 index b3379004ed..f1fb42c19b 100644 --- a/app/assets/javascripts/discourse/lib/transform-post.js.es6 +++ b/app/assets/javascripts/discourse/lib/transform-post.js.es6 @@ -87,6 +87,7 @@ export default function transformPost(currentUser, site, post, prevPost, nextPos postAtts.topicId = topic.id; postAtts.topicOwner = details.created_by.id === post.user_id; + postAtts.topicCreatedById = details.created_by.id; postAtts.post_type = postType; postAtts.via_email = post.via_email; postAtts.isModeratorAction = postType === postTypes.moderator_action; From a7eec3da5c9af2f8d03b65453bdfbad214f05757 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Fri, 18 Mar 2016 23:37:20 +0530 Subject: [PATCH 024/162] FIX: blank search was broken --- app/controllers/search_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 92380185d2..bdbb163a77 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -25,7 +25,7 @@ class SearchController < ApplicationController search = Search.new(params[:q], search_args) result = search.execute - result.find_user_data(guardian) + result.find_user_data(guardian) if result serializer = serialize_data(result, GroupedSearchResultSerializer, result: result) From b4f306ce0332a51386e7bf971e2d904b69db4675 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 18 Mar 2016 14:41:27 -0400 Subject: [PATCH 025/162] FEATURE: Site Customizations can use the plugin api --- app/assets/javascripts/discourse.js | 17 ++++++++++ app/models/site_customization.rb | 22 +++++++++++++ .../tilt/es6_module_transpiler_template.rb | 16 +++++++-- spec/models/site_customization_spec.rb | 33 +++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index ab0a429832..48e0b663d4 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -6,6 +6,8 @@ define('ember', ['exports'], function(__exports__) { __exports__.default = Ember; }); +var _pluginCallbacks = []; + window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { rootElement: '#main', _docTitle: document.title, @@ -127,6 +129,18 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { } }); + // Plugins that are registered via `") end + doc.css('script[type="text/discourse-plugin"]').each do |node| + if node['version'].present? + begin + code = transpile(node.inner_html, node['version']) + node.replace("") + rescue Tilt::ES6ModuleTranspilerTemplate::JavaScriptError => ex + node.replace("") + end + end + end + doc.to_s end diff --git a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb index d54524fe68..b0d6f1b917 100644 --- a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb +++ b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb @@ -94,6 +94,14 @@ module Tilt @@whitelisted.include?(path) || path =~ /discourse\/mixins/ end + def babel_transpile(source) + klass = self.class + klass.protect do + klass.v8['console'] = Console.new("BABEL: babel-eval: ") + @output = klass.v8.eval(babel_source(source)) + end + end + def evaluate(scope, locals, &block) return @output if @output @@ -139,11 +147,15 @@ module Tilt @output end + def babel_source(source) + js_source = ::JSON.generate(source, quirks_mode: true) + "babel.transform(#{js_source}, {ast: false, whitelist: ['es6.constants', 'es6.properties.shorthand', 'es6.arrowFunctions', 'es6.blockScoping', 'es6.destructuring', 'es6.spread', 'es6.parameters', 'es6.templateLiterals', 'es6.regex.unicode', 'es7.decorators', 'es6.classes']})['code']" + end + private def generate_source(scope) - js_source = ::JSON.generate(data, quirks_mode: true) - js_source = "babel.transform(#{js_source}, {ast: false, whitelist: ['es6.constants', 'es6.properties.shorthand', 'es6.arrowFunctions', 'es6.blockScoping', 'es6.destructuring', 'es6.spread', 'es6.parameters', 'es6.templateLiterals', 'es6.regex.unicode', 'es7.decorators', 'es6.classes']})['code']" + js_source = babel_source(data) "new module.exports.Compiler(#{js_source}, '#{module_name(scope.root_path, scope.logical_path)}', #{compiler_options}).#{compiler_method}()" end diff --git a/spec/models/site_customization_spec.rb b/spec/models/site_customization_spec.rb index 571e95ddd0..ca87b12bca 100644 --- a/spec/models/site_customization_spec.rb +++ b/spec/models/site_customization_spec.rb @@ -119,4 +119,37 @@ HTML expect(SiteCustomization.custom_head_tag).to match(/test<\/b>/) end + context "plugin api" do + def transpile(html) + c = SiteCustomization.create!(user_id: -1, name: "test", head_tag: html, body_tag: html) + c.head_tag_baked + end + + it "transpiles ES6 code" do + html = < + const x = 1; + +HTML + + transpiled = transpile(html) + expect(transpiled).to match(/\/) + expect(transpiled).to match(/var x = 1;/) + expect(transpiled).to match(/_registerPluginCode\('0.1'/) + end + + it "converts errors to a script type that is not evaluated" do + html = < + const x = 1; + x = 2; + +HTML + + transpiled = transpile(html) + expect(transpiled).to match(/text\/discourse-js-error/) + expect(transpiled).to match(/read-only/) + end + end + end From b83b9d2b75712fb20b281a88cdda9e18173fab18 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 18 Mar 2016 14:53:25 -0400 Subject: [PATCH 026/162] FIX: Another ES2015 feature that snuck by --- app/assets/javascripts/discourse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index 48e0b663d4..f791de454c 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -148,7 +148,7 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { return desired && Discourse.get("currentAssetVersion") !== desired; }.property("currentAssetVersion", "desiredAssetVersion"), - _registerPluginCode(version, code) { + _registerPluginCode: function(version, code) { _pluginCallbacks.push({ version: version, code: code }); }, From fbf45426e411e0e38fc3d49538106fd8e08ccf4e Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 18 Mar 2016 16:31:59 -0400 Subject: [PATCH 027/162] Autoload widgets since they are named when created, not by file --- .../initializers/auto-load-modules.js.es6 | 15 +++++++++++++++ .../initializers/load-all-helpers.js.es6 | 12 ------------ test/javascripts/helpers/component-test.js.es6 | 4 ++-- 3 files changed, 17 insertions(+), 14 deletions(-) create mode 100644 app/assets/javascripts/discourse/initializers/auto-load-modules.js.es6 delete mode 100644 app/assets/javascripts/discourse/initializers/load-all-helpers.js.es6 diff --git a/app/assets/javascripts/discourse/initializers/auto-load-modules.js.es6 b/app/assets/javascripts/discourse/initializers/auto-load-modules.js.es6 new file mode 100644 index 0000000000..d870f52531 --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/auto-load-modules.js.es6 @@ -0,0 +1,15 @@ +export function autoLoadModules() { + Ember.keys(requirejs.entries).forEach(entry => { + if ((/\/helpers\//).test(entry)) { + require(entry, null, null, true); + } + if ((/\/widgets\//).test(entry)) { + require(entry, null, null, true); + } + }); +} + +export default { + name: 'auto-load-modules', + initialize: autoLoadModules +}; diff --git a/app/assets/javascripts/discourse/initializers/load-all-helpers.js.es6 b/app/assets/javascripts/discourse/initializers/load-all-helpers.js.es6 deleted file mode 100644 index 9347778775..0000000000 --- a/app/assets/javascripts/discourse/initializers/load-all-helpers.js.es6 +++ /dev/null @@ -1,12 +0,0 @@ -export function loadAllHelpers() { - Ember.keys(requirejs.entries).forEach(entry => { - if ((/\/helpers\//).test(entry)) { - require(entry, null, null, true); - } - }); -} - -export default { - name: 'load-all-helpers', - initialize: loadAllHelpers -}; diff --git a/test/javascripts/helpers/component-test.js.es6 b/test/javascripts/helpers/component-test.js.es6 index 0fd3c8d6b2..652424a95f 100644 --- a/test/javascripts/helpers/component-test.js.es6 +++ b/test/javascripts/helpers/component-test.js.es6 @@ -1,6 +1,6 @@ import AppEvents from 'discourse/lib/app-events'; import createStore from 'helpers/create-store'; -import { loadAllHelpers } from 'discourse/initializers/load-all-helpers'; +import { autoLoadModules } from 'discourse/initializers/auto-load-modules'; export default function(name, opts) { opts = opts || {}; @@ -18,7 +18,7 @@ export default function(name, opts) { this.siteSettings = Discourse.SiteSettings; - loadAllHelpers(); + autoLoadModules(); if (opts.setup) { const store = createStore(); From 0ad5f226444be53bb3851e26f46b6b95cd010a9f Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 18 Mar 2016 16:38:29 -0700 Subject: [PATCH 028/162] more positive copy on like remain warning --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 2ab59d1301..b2db32c186 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1511,7 +1511,7 @@ en: archetypes: save: 'Save Options' - few_likes_left: "Warning: You have few likes left to give today. Use them wisely!" + few_likes_left: "Thanks for sharing the love! You only have a few likes left for today, so use them wisely." controls: reply: "begin composing a reply to this post" From d14bcc568124c814c7e5fd9afd34c56f155c74b9 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 18 Mar 2016 16:46:34 -0700 Subject: [PATCH 029/162] let's avoid advising users on being wise --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index b2db32c186..3c317af660 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1511,7 +1511,7 @@ en: archetypes: save: 'Save Options' - few_likes_left: "Thanks for sharing the love! You only have a few likes left for today, so use them wisely." + few_likes_left: "Thanks for sharing the love! You only have a few likes left for today." controls: reply: "begin composing a reply to this post" From 7df33ca2875f191a6a326522a81ca47bfea6c107 Mon Sep 17 00:00:00 2001 From: Erick Guan Date: Sat, 19 Mar 2016 13:16:11 +0100 Subject: [PATCH 030/162] FIX: redirect output omniauth log to Rails logger instead of stdout --- config/initializers/009-omniauth.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/initializers/009-omniauth.rb b/config/initializers/009-omniauth.rb index 1632ba0250..6138ec9778 100644 --- a/config/initializers/009-omniauth.rb +++ b/config/initializers/009-omniauth.rb @@ -9,3 +9,5 @@ Rails.application.config.middleware.use OmniAuth::Builder do authenticator.register_middleware(self) end end + +OmniAuth.config.logger = Rails.logger From 81e174c47a6569e0312696ff34d92a6463035faa Mon Sep 17 00:00:00 2001 From: Gerhard Schlager Date: Sat, 19 Mar 2016 22:37:29 +0100 Subject: [PATCH 031/162] Fix Permalink normalization regex example --- config/locales/server.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index a85bd940cf..adf0b0dcd8 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1218,7 +1218,7 @@ en: suppress_uncategorized_badge: "Don't show the badge for uncategorized topics in topic lists." - permalink_normalizations: "Apply the following regex before matching permalinks, for example: /(\\/topic.*)\\?.*/\\1 will strip query strings from topic routes. Format is regex+string use \\1 etc. to access captures" + permalink_normalizations: "Apply the following regex before matching permalinks, for example: /(topic.*)\\?.*/\\1 will strip query strings from topic routes. Format is regex+string use \\1 etc. to access captures" global_notice: "Display an URGENT, EMERGENCY global banner notice to all visitors, change to blank to hide it (HTML allowed)." From c3507a324281716769466c93b554a7bdd93f3e9e Mon Sep 17 00:00:00 2001 From: Aryan Raj Date: Sat, 19 Mar 2016 17:33:10 +0530 Subject: [PATCH 032/162] Fix: Added underscore to my_redirect regex --- app/controllers/users_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 586e7fded0..d20d090c4f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -165,7 +165,7 @@ class UsersController < ApplicationController end def my_redirect - raise Discourse::NotFound if params[:path] !~ /^[a-z\-\/]+$/ + raise Discourse::NotFound if params[:path] !~ /^[a-z_\-\/]+$/ if current_user.blank? cookies[:destination_url] = "/my/#{params[:path]}" From c03b8940df9ece9c6fe39379bad3a417a66306cf Mon Sep 17 00:00:00 2001 From: Aryan Raj Date: Sun, 20 Mar 2016 14:35:59 +0530 Subject: [PATCH 033/162] Fixed: DecoratorHelper.cooked passing wrong parameters to PostCooked class --- .../javascripts/discourse/widgets/decorator-helper.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 b/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 index 1679a708ec..024e6d4421 100644 --- a/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 +++ b/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 @@ -79,8 +79,8 @@ class DecoratorHelper { * return helper.cooked(`

Cook me

`); * ``` **/ - cooked(cookedText) { - return new PostCooked({ cookedText }); + cooked(cooked) { + return new PostCooked({ cooked }); } /** From ffeca8c2d42661df1385f25b75ef2743b72f4e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fernandes?= Date: Sun, 20 Mar 2016 18:27:28 +0000 Subject: [PATCH 034/162] Prevent moderators from seeing other users bookmarks --- app/models/user_action.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user_action.rb b/app/models/user_action.rb index 2fa8490eb4..23678754cf 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -342,11 +342,11 @@ SQL builder.where("COALESCE(p.post_type, p2.post_type) IN (:visible_post_types)", visible_post_types: visible_post_types) unless (guardian.user && guardian.user.id == user_id) || guardian.is_staff? - builder.where("a.action_type not in (#{BOOKMARK})") builder.where("t.visible") end unless guardian.can_see_notifications?(User.where(id: user_id).first) + builder.where("a.action_type not in (#{BOOKMARK})") builder.where('a.action_type <> :pending', pending: UserAction::PENDING) end From c4da6ed88b61fc5f79a3193de33f290e753aa376 Mon Sep 17 00:00:00 2001 From: Rafael dos Santos Silva Date: Sun, 20 Mar 2016 22:18:40 -0400 Subject: [PATCH 035/162] Better Add to Homescreen on Android Adds standalone screen, and top-bar color and background color on splash screen --- app/controllers/manifest_json_controller.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/manifest_json_controller.rb b/app/controllers/manifest_json_controller.rb index 5462ec1fd6..8c7b6bc42b 100644 --- a/app/controllers/manifest_json_controller.rb +++ b/app/controllers/manifest_json_controller.rb @@ -1,13 +1,15 @@ class ManifestJsonController < ApplicationController layout false - skip_before_filter :preload_json, :check_xhr + skip_before_filter :preload_json, :check_xhr, :redirect_to_login_if_required def index manifest = { short_name: SiteSetting.title, - display: 'browser', + display: 'standalone', orientation: 'portrait', - start_url: "#{Discourse.base_uri}/" + start_url: "#{Discourse.base_uri}/", + background_color: "##{ColorScheme.hex_for_name('secondary')}", + theme_color: "##{ColorScheme.hex_for_name('header_background')}" } render json: manifest.to_json From 54bdcd9b98b61e85070ab1b36b246449678028c2 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 8 Mar 2016 16:33:13 +0800 Subject: [PATCH 036/162] Update to new rate limit Redis config. --- Gemfile.lock | 2 +- config/initializers/100-logster.rb | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1bfaa7ae12..79cd48ccc0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -150,7 +150,7 @@ GEM thor (~> 0.15) libv8 (3.16.14.13) listen (0.7.3) - logster (1.1.1) + logster (1.2.0) loofah (2.0.3) nokogiri (>= 1.5.9) lru_redux (1.1.0) diff --git a/config/initializers/100-logster.rb b/config/initializers/100-logster.rb index 8315587eaf..13904d283e 100644 --- a/config/initializers/100-logster.rb +++ b/config/initializers/100-logster.rb @@ -57,21 +57,22 @@ Logster.config.subdirectory = "#{GlobalSetting.relative_url_root}/logs" Logster.config.application_version = Discourse.git_version +store = Logster.store redis = Logster.store.redis -Logster.config.redis_prefix = "#{redis.namespace}" -Logster.config.redis_raw_connection = redis.without_namespace +store.redis_prefix = Proc.new { redis.namespace } +store.redis_raw_connection = redis.without_namespace +severities = [Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN] -%w{minute hour}.each do |duration| - site_setting_error_rate = SiteSetting.public_send("alert_admins_if_errors_per_#{duration}") +RailsMultisite::ConnectionManagement.each_connection do + if error_rate_per_minute = SiteSetting.alert_admins_if_errors_per_minute > 0 + store.register_rate_limit_per_minute(severities, error_rate_per_minute) do |rate| + MessageBus.publish("/logs_error_rate_exceeded", { rate: rate, duration: 'minute' }) + end + end - if site_setting_error_rate > 0 - Logster.store.public_send( - "register_rate_limit_per_#{duration}", - [Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN], - site_setting_error_rate - ) do |rate| - - MessageBus.publish("/logs_error_rate_exceeded", { rate: rate, duration: duration }) + if error_rate_per_hour = SiteSetting.alert_admins_if_errors_per_hour > 0 + store.register_rate_limit_per_hour(severities, error_rate_per_hour) do |rate| + MessageBus.publish("/logs_error_rate_exceeded", { rate: rate, duration: 'hour' }) end end end From 5dc5767851bdadaceec9d91793d749bdbe6fa6cc Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Mon, 21 Mar 2016 15:17:11 +0800 Subject: [PATCH 037/162] FIX: Assigning wrong value to variable. --- config/initializers/100-logster.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/config/initializers/100-logster.rb b/config/initializers/100-logster.rb index 13904d283e..ada7b9dd08 100644 --- a/config/initializers/100-logster.rb +++ b/config/initializers/100-logster.rb @@ -64,13 +64,17 @@ store.redis_raw_connection = redis.without_namespace severities = [Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN] RailsMultisite::ConnectionManagement.each_connection do - if error_rate_per_minute = SiteSetting.alert_admins_if_errors_per_minute > 0 + error_rate_per_minute = SiteSetting.alert_admins_if_errors_per_minute + + if error_rate_per_minute > 0 store.register_rate_limit_per_minute(severities, error_rate_per_minute) do |rate| MessageBus.publish("/logs_error_rate_exceeded", { rate: rate, duration: 'minute' }) end end - if error_rate_per_hour = SiteSetting.alert_admins_if_errors_per_hour > 0 + error_rate_per_hour = SiteSetting.alert_admins_if_errors_per_hour + + if error_rate_per_hour > 0 store.register_rate_limit_per_hour(severities, error_rate_per_hour) do |rate| MessageBus.publish("/logs_error_rate_exceeded", { rate: rate, duration: 'hour' }) end From dd2605bae7bbb54fb00f88ad37580787e5502034 Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Mon, 21 Mar 2016 08:49:49 +0100 Subject: [PATCH 038/162] FIX: Allow displaying posts by deleted users. --- .../javascripts/discourse/lib/transform-post.js.es6 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/lib/transform-post.js.es6 b/app/assets/javascripts/discourse/lib/transform-post.js.es6 index f1fb42c19b..5c5961cbfc 100644 --- a/app/assets/javascripts/discourse/lib/transform-post.js.es6 +++ b/app/assets/javascripts/discourse/lib/transform-post.js.es6 @@ -85,9 +85,11 @@ export default function transformPost(currentUser, site, post, prevPost, nextPos const postAtts = transformBasicPost(post); + const createdBy = details.created_by || {}; + postAtts.topicId = topic.id; - postAtts.topicOwner = details.created_by.id === post.user_id; - postAtts.topicCreatedById = details.created_by.id; + postAtts.topicOwner = createdBy.id === post.user_id; + postAtts.topicCreatedById = createdBy.id; postAtts.post_type = postType; postAtts.via_email = post.via_email; postAtts.isModeratorAction = postType === postTypes.moderator_action; @@ -120,8 +122,8 @@ export default function transformPost(currentUser, site, post, prevPost, nextPos if (showTopicMap) { postAtts.showTopicMap = true; postAtts.topicCreatedAt = topic.created_at; - postAtts.createdByUsername = details.created_by.username; - postAtts.createdByAvatarTemplate = details.created_by.avatar_template; + postAtts.createdByUsername = createdBy.username; + postAtts.createdByAvatarTemplate = createdBy.avatar_template; postAtts.lastPostUrl = topic.get('lastPostUrl'); postAtts.lastPostUsername = details.last_poster.username; From b575f97ece7215e22c0c0e227de136e4d3050e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 21 Mar 2016 12:12:25 +0100 Subject: [PATCH 039/162] FIX: allows polls on closed topics --- .../poll/assets/javascripts/controllers/poll.js.es6 | 11 +++++------ plugins/poll/plugin.rb | 8 ++++---- .../poll/spec/controllers/polls_controller_spec.rb | 8 +++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/plugins/poll/assets/javascripts/controllers/poll.js.es6 b/plugins/poll/assets/javascripts/controllers/poll.js.es6 index 576bf7433c..dc5e549a39 100644 --- a/plugins/poll/assets/javascripts/controllers/poll.js.es6 +++ b/plugins/poll/assets/javascripts/controllers/poll.js.es6 @@ -8,12 +8,12 @@ export default Ember.Controller.extend({ // shows the results when // - poll is closed - // - topic is archived/closed + // - topic is archived // - user wants to see the results - showingResults: Em.computed.or("isClosed", "post.topic.closed", "post.topic.archived", "showResults"), + showingResults: Em.computed.or("isClosed", "post.topic.archived", "showResults"), showResultsDisabled: Em.computed.equal("poll.voters", 0), - hideResultsDisabled: Em.computed.or("isClosed", "post.topic.closed", "post.topic.archived"), + hideResultsDisabled: Em.computed.or("isClosed", "post.topic.archived"), @computed("model", "vote", "model.voters", "model.options", "model.status") poll(poll, vote) { @@ -100,12 +100,11 @@ export default Ember.Controller.extend({ castVotesDisabled: Em.computed.not("canCastVotes"), - @computed("loading", "post.user_id", "post.topic.closed", "post.topic.archived") - canToggleStatus(loading, userId, topicClosed, topicArchived) { + @computed("loading", "post.user_id", "post.topic.archived") + canToggleStatus(loading, userId, topicArchived) { return this.currentUser && (this.currentUser.get("id") === userId || this.currentUser.get("staff")) && !loading && - !topicClosed && !topicArchived; }, diff --git a/plugins/poll/plugin.rb b/plugins/poll/plugin.rb index 45fb14e630..ce251f91a2 100644 --- a/plugins/poll/plugin.rb +++ b/plugins/poll/plugin.rb @@ -54,8 +54,8 @@ after_initialize do raise StandardError.new I18n.t("poll.post_is_deleted") end - # topic must be open - if post.topic.try(:closed) || post.topic.try(:archived) + # topic must not be archived + if post.topic.try(:archived) raise StandardError.new I18n.t("poll.topic_must_be_open_to_vote") end @@ -107,8 +107,8 @@ after_initialize do raise StandardError.new I18n.t("poll.post_is_deleted") end - # topic must be open - if post.topic.try(:closed) || post.topic.try(:archived) + # topic must not be archived + if post.topic.try(:archived) raise StandardError.new I18n.t("poll.topic_must_be_open_to_toggle_status") end diff --git a/plugins/poll/spec/controllers/polls_controller_spec.rb b/plugins/poll/spec/controllers/polls_controller_spec.rb index bb45816f0d..b1679d7d6e 100644 --- a/plugins/poll/spec/controllers/polls_controller_spec.rb +++ b/plugins/poll/spec/controllers/polls_controller_spec.rb @@ -41,12 +41,10 @@ describe ::DiscoursePoll::PollsController do expect(json["poll"]["options"][1]["votes"]).to eq(1) end - it "ensures topic is not closed" do + it "works even if topic is closed" do topic.update_attribute(:closed, true) - xhr :put, :vote, { post_id: poll.id, poll_name: "poll", options: ["A"] } - expect(response).not_to be_success - json = ::JSON.parse(response.body) - expect(json["errors"][0]).to eq(I18n.t("poll.topic_must_be_open_to_vote")) + xhr :put, :vote, { post_id: poll.id, poll_name: "poll", options: ["5c24fc1df56d764b550ceae1b9319125"] } + expect(response).to be_success end it "ensures topic is not archived" do From 34469e725b84a9f58aed8cde3c7d3f61fe9f4597 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 21 Mar 2016 15:52:36 +0530 Subject: [PATCH 040/162] FEATURE: separate API endpoints for public and private posts --- app/controllers/posts_controller.rb | 35 +++++++++---- config/locales/server.en.yml | 1 + config/routes.rb | 3 +- spec/controllers/posts_controller_spec.rb | 61 +++++++++++++++++------ 4 files changed, 73 insertions(+), 27 deletions(-) diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index fdf3dbb687..f97fc1aad1 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -37,14 +37,29 @@ class PostsController < ApplicationController last_post_id = params[:before].to_i last_post_id = Post.last.id if last_post_id <= 0 - # last 50 post IDs only, to avoid counting deleted posts in security check - posts = Post.order(created_at: :desc) - .where('posts.id <= ?', last_post_id) - .where('posts.id > ?', last_post_id - 50) - .includes(topic: :category) - .includes(user: :primary_group) - .includes(:reply_to_user) - .limit(50) + if params[:id] == "private_posts" + raise Discourse::NotFound if current_user.nil? + posts = Post.private_posts + .order(created_at: :desc) + .where('posts.id <= ?', last_post_id) + .where('posts.id > ?', last_post_id - 50) + .includes(topic: :category) + .includes(user: :primary_group) + .includes(:reply_to_user) + .limit(50) + rss_description = I18n.t("rss_description.private_posts") + else + posts = Post.public_posts + .order(created_at: :desc) + .where('posts.id <= ?', last_post_id) + .where('posts.id > ?', last_post_id - 50) + .includes(topic: :category) + .includes(user: :primary_group) + .includes(:reply_to_user) + .limit(50) + rss_description = I18n.t("rss_description.posts") + end + # Remove posts the user doesn't have permission to see # This isn't leaking any information we weren't already through the post ID numbers posts = posts.reject { |post| !guardian.can_see?(post) || post.topic.blank? } @@ -53,7 +68,7 @@ class PostsController < ApplicationController respond_to do |format| format.rss do @posts = posts - @title = "#{SiteSetting.title} - #{I18n.t("rss_description.posts")}" + @title = "#{SiteSetting.title} - #{rss_description}" @link = Discourse.base_url @description = I18n.t("rss_description.posts") render 'posts/latest', formats: [:rss] @@ -62,7 +77,7 @@ class PostsController < ApplicationController render_json_dump(serialize_data(posts, PostSerializer, scope: guardian, - root: 'latest_posts', + root: params[:id], add_raw: true, add_title: true, all_post_actions: counts) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index adf0b0dcd8..020235985f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -204,6 +204,7 @@ en: hot: "Hot topics" top: "Top topics" posts: "Latest posts" + private_posts: "Latest private messages" group_posts: "Latest posts from %{group_name}" group_mentions: "Latest mentions from %{group_name}" too_late_to_edit: "That post was created too long ago. It can no longer be edited or deleted." diff --git a/config/routes.rb b/config/routes.rb index fab10862bb..80a1533d4d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -347,7 +347,8 @@ Discourse::Application.routes.draw do # used to download attachments (old route) get "uploads/:site/:id/:sha" => "uploads#show", constraints: { site: /\w+/, id: /\d+/, sha: /[a-f0-9]{16}/ } - get "posts" => "posts#latest" + get "posts" => "posts#latest", id: "latest_posts" + get "private-posts" => "posts#latest", id: "private_posts" get "posts/by_number/:topic_id/:post_number" => "posts#by_number" get "posts/:id/reply-history" => "posts#reply_history" get "posts/:username/deleted" => "posts#deleted_posts", constraints: {username: USERNAME_ROUTE_FORMAT} diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 67392c081c..635798e8d9 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -55,27 +55,56 @@ describe PostsController do describe 'latest' do let(:user) { log_in } - let!(:post) { Fabricate(:post, user: user) } + let!(:public_topic) { Fabricate(:topic) } + let!(:post) { Fabricate(:post, user: user, topic: public_topic) } + let!(:private_topic) { Fabricate(:topic, archetype: Archetype.private_message, category: nil) } + let!(:private_post) { Fabricate(:post, user: user, topic: private_topic) } let!(:topicless_post) { Fabricate(:post, user: user, raw: '

Car 54, where are you?

') } - before do - topicless_post.update topic_id: -100 + context "public posts" do + before do + topicless_post.update topic_id: -100 + end + + it 'returns public posts with topic for json' do + xhr :get, :latest, id: "latest_posts", format: :json + expect(response).to be_success + json = ::JSON.parse(response.body) + post_ids = json['latest_posts'].map { |p| p['id'] } + expect(post_ids).to include post.id + expect(post_ids).to_not include private_post.id + expect(post_ids).to_not include topicless_post.id + end + + it 'returns public posts with topic for rss' do + xhr :get, :latest, id: "latest_posts", format: :rss + expect(response).to be_success + expect(assigns(:posts)).to include post + expect(assigns(:posts)).to_not include private_post + expect(assigns(:posts)).to_not include topicless_post + end end - it 'does not return posts without a topic for json' do - xhr :get, :latest, format: :json - expect(response).to be_success - json = ::JSON.parse(response.body) - post_ids = json['latest_posts'].map { |p| p['id'] } - expect(post_ids).to include post.id - expect(post_ids).to_not include topicless_post.id - end + context 'private posts' do + before do + Guardian.any_instance.expects(:can_see?).with(private_post).returns(true) + end - it 'does not return posts without a topic for rss' do - xhr :get, :latest, format: :rss - expect(response).to be_success - expect(assigns(:posts)).to include post - expect(assigns(:posts)).to_not include topicless_post + it 'returns private posts for json' do + xhr :get, :latest, id: "private_posts", format: :json + expect(response).to be_success + json = ::JSON.parse(response.body) + post_ids = json['private_posts'].map { |p| p['id'] } + expect(post_ids).to include private_post.id + expect(post_ids).to_not include post.id + end + + it 'returns private posts for rss' do + xhr :get, :latest, id: "private_posts", format: :rss + expect(response).to be_success + expect(assigns(:posts)).to include private_post + expect(assigns(:posts)).to_not include post + end end end From c54dc4a8d9079672ba4f5a0c984bcbd86d2f04d1 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 21 Mar 2016 18:45:16 +0530 Subject: [PATCH 041/162] FIX: update RSS description for public/private posts --- app/controllers/posts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index f97fc1aad1..230eac8323 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -70,7 +70,7 @@ class PostsController < ApplicationController @posts = posts @title = "#{SiteSetting.title} - #{rss_description}" @link = Discourse.base_url - @description = I18n.t("rss_description.posts") + @description = rss_description render 'posts/latest', formats: [:rss] end format.json do From 9bcb841a8b271ed93d06e4c18732161332530d29 Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Mon, 21 Mar 2016 07:47:54 -0600 Subject: [PATCH 042/162] Change SendGrid info to use api key While it does work I don't think using your SendGrid username and password is good practice so we probably shouldn't promote it. API Keys should be used instead. --- docs/INSTALL-email.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/INSTALL-email.md b/docs/INSTALL-email.md index db40eea4e3..ee499ad470 100644 --- a/docs/INSTALL-email.md +++ b/docs/INSTALL-email.md @@ -26,9 +26,10 @@ If not using **the exact** domain you verified (e.g. you're using a subdomain of ```yml DISCOURSE_SMTP_ADDRESS: smtp.sendgrid.net DISCOURSE_SMTP_PORT: 587 -DISCOURSE_SMTP_USER_NAME: [SendGrid username] -DISCOURSE_SMTP_PASSWORD: [SendGrid password] +DISCOURSE_SMTP_USER_NAME: apikey +DISCOURSE_SMTP_PASSWORD: [SendGrid API Key] ``` +We recommend creating an [API Key][sg2] instead of using your SendGrid username and password. #### [Mailgun][gun] (10k emails/month) @@ -49,3 +50,4 @@ Go to [My Account page](https://www.mailjet.com/account) and click on the ["SMTP [jet]: https://www.mailjet.com/pricing [gun]: http://www.mailgun.com/ [sg]: https://sendgrid.com/ + [sg2]: https://sendgrid.com/docs/Classroom/Send/How_Emails_Are_Sent/api_keys.html From 9c61f45bf9f0d253324422bec979af34d405b6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 21 Mar 2016 18:49:01 +0100 Subject: [PATCH 043/162] FIX: properly handle failure in poll mailbox job --- app/jobs/scheduled/poll_mailbox.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/jobs/scheduled/poll_mailbox.rb b/app/jobs/scheduled/poll_mailbox.rb index 9a69f14094..c01da2d2d6 100644 --- a/app/jobs/scheduled/poll_mailbox.rb +++ b/app/jobs/scheduled/poll_mailbox.rb @@ -57,6 +57,7 @@ module Jobs end template_args = {} + client_message = nil # there might be more information available in the exception if message_template == :email_reject_invalid_post && e.message.size > 6 @@ -77,12 +78,12 @@ module Jobs client_message = RejectionMailer.send_rejection(message_template, message.from, template_args) Email::Sender.new(client_message, message_template).send - - client_message else mark_as_errored! Discourse.handle_job_exception(e, error_context(@args, "Unrecognized error type when processing incoming email", mail: mail_string)) end + + client_message end def poll_pop3 From a9daa339534cc37175562ae348f05ae84bfb0af3 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 21 Mar 2016 14:16:05 -0400 Subject: [PATCH 044/162] Add tests to `home-logo` component --- .../templates/components/home-logo.hbs | 2 +- .../components/home-logo-test.js.es6 | 96 +++++++++++++++++++ .../javascripts/helpers/component-test.js.es6 | 4 +- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 test/javascripts/components/home-logo-test.js.es6 diff --git a/app/assets/javascripts/discourse/templates/components/home-logo.hbs b/app/assets/javascripts/discourse/templates/components/home-logo.hbs index b2b8f9e6db..8cb81d9c4b 100644 --- a/app/assets/javascripts/discourse/templates/components/home-logo.hbs +++ b/app/assets/javascripts/discourse/templates/components/home-logo.hbs @@ -1,7 +1,7 @@ {{#if showSmallLogo}} {{#if smallLogoUrl}} - + {{unbound title}} {{else}} {{/if}} diff --git a/test/javascripts/components/home-logo-test.js.es6 b/test/javascripts/components/home-logo-test.js.es6 new file mode 100644 index 0000000000..770343d499 --- /dev/null +++ b/test/javascripts/components/home-logo-test.js.es6 @@ -0,0 +1,96 @@ +import componentTest from 'helpers/component-test'; + +moduleForComponent('home-logo', {integration: true}); + +const bigLogo = '/images/d-logo-sketch.png?test'; +const smallLogo = '/images/d-logo-sketch-small.png?test'; +const mobileLogo = '/images/d-logo-sketch.png?mobile'; +const title = "Cool Forum"; + +componentTest('basics', { + template: '{{home-logo minimized=minimized}}', + setup() { + this.siteSettings.logo_url = bigLogo; + this.siteSettings.logo_small_url= smallLogo; + this.siteSettings.title = title; + this.set('minimized', false); + }, + + test(assert) { + assert.ok(this.$('a[data-auto-route]').length === 1); + + assert.ok(this.$('img#site-logo.logo-big').length === 1); + assert.equal(this.$('#site-logo').attr('src'), bigLogo); + assert.equal(this.$('#site-logo').attr('alt'), title); + + this.set('minimized', true); + andThen(() => { + assert.ok(this.$('img.logo-small').length === 1); + assert.equal(this.$('img.logo-small').attr('src'), smallLogo); + assert.equal(this.$('img.logo-small').attr('alt'), title); + }); + } +}); + +componentTest('no logo', { + template: '{{home-logo minimized=minimized}}', + setup() { + this.siteSettings.logo_url = ''; + this.siteSettings.logo_small_url = ''; + this.siteSettings.title = title; + this.set('minimized', false); + }, + + test(assert) { + assert.ok(this.$('a[data-auto-route]').length === 1); + + assert.ok(this.$('h2#site-text-logo.text-logo').length === 1); + assert.equal(this.$('#site-text-logo').text(), title); + + this.set('minimized', true); + andThen(() => { + assert.ok(this.$('i.fa-home').length === 1); + }); + } +}); + +componentTest('mobile logo', { + template: "{{home-logo minimized=minimized}}", + setup() { + this.siteSettings.mobile_logo_url = mobileLogo; + this.siteSettings.logo_small_url= smallLogo; + this.set('minimized', true); + this.site.mobileView = true; + }, + + test(assert) { + assert.ok(this.$('img#site-logo.logo-big').length === 1); + assert.equal(this.$('#site-logo').attr('src'), mobileLogo); + + this.set('minimized', true); + andThen(() => { + assert.equal(this.$('#site-logo').attr('src'), mobileLogo); + }); + } +}); + +componentTest('mobile without logo', { + template: "{{home-logo minimized=minimized}}", + setup() { + this.set('minimized', true); + this.siteSettings.logo_url = bigLogo; + this.site.mobileView = true; + }, + + test(assert) { + assert.ok(this.$('img#site-logo.logo-big').length === 1); + assert.equal(this.$('#site-logo').attr('src'), bigLogo); + } +}); + +componentTest("changing url", { + template: '{{home-logo targetUrl="https://www.discourse.org"}}', + test(assert) { + assert.equal(this.$('a').attr('href'), 'https://www.discourse.org'); + } +}); diff --git a/test/javascripts/helpers/component-test.js.es6 b/test/javascripts/helpers/component-test.js.es6 index 652424a95f..995fb47621 100644 --- a/test/javascripts/helpers/component-test.js.es6 +++ b/test/javascripts/helpers/component-test.js.es6 @@ -7,10 +7,12 @@ export default function(name, opts) { test(name, function(assert) { const appEvents = AppEvents.create(); + this.site = Discourse.Site.current(); + this.container.register('site-settings:main', Discourse.SiteSettings, { instantiate: false }); this.container.register('app-events:main', appEvents, { instantiate: false }); this.container.register('capabilities:main', Ember.Object); - this.container.register('site:main', Discourse.Site.current(), { instantiate: false }); + this.container.register('site:main', this.site, { instantiate: false }); this.container.injection('component', 'siteSettings', 'site-settings:main'); this.container.injection('component', 'appEvents', 'app-events:main'); this.container.injection('component', 'capabilities', 'capabilities:main'); From 7dd89be7413dabfdbe92ac2187dd8bcb24e537f4 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Mon, 21 Mar 2016 14:32:00 -0400 Subject: [PATCH 045/162] FIX: text title in header should use header primary colour instead of blue --- app/assets/stylesheets/common/base/header.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index 63c41255d7..610832b1c8 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -17,6 +17,9 @@ .title { float: left; + a, a:visited { + color: $header_primary; + } } #site-logo { From afacc70fbeb01f589ff246b1e19d966f53039462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 21 Mar 2016 19:36:26 +0100 Subject: [PATCH 046/162] improve error message when trying to change email address to one used by a staged user --- config/locales/server.en.yml | 1 + lib/email_updater.rb | 6 +++++- spec/components/email_updater_spec.rb | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 020235985f..22245e32d8 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -522,6 +522,7 @@ en: confirmed: "Your email has been updated." please_continue: "Continue to %{site_name}" error: "There was an error changing your email address. Perhaps the address is already in use?" + error_staged: "There was an error changing your email address. The address is already in use by a staged user." already_done: "Sorry, this confirmation link is no longer valid. Perhaps your email was already changed?" authorizing_old: title: "Thanks for confirming your current email address" diff --git a/lib/email_updater.rb b/lib/email_updater.rb index 71c57f05c8..1bda27bb27 100644 --- a/lib/email_updater.rb +++ b/lib/email_updater.rb @@ -26,7 +26,11 @@ class EmailUpdater email = Email.downcase(email_input.strip) EmailValidator.new(attributes: :email).validate_each(self, :email, email) - errors.add(:base, I18n.t('change_email.error')) if User.find_by_email(email) + if existing_user = User.find_by_email(email) + error_message = 'change_email.error' + error_message << '_staged' if existing_user.staged? + errors.add(:base, I18n.t(error_message)) + end if errors.blank? args = { diff --git a/spec/components/email_updater_spec.rb b/spec/components/email_updater_spec.rb index b868e6c9a5..74ec89e2f2 100644 --- a/spec/components/email_updater_spec.rb +++ b/spec/components/email_updater_spec.rb @@ -5,6 +5,17 @@ describe EmailUpdater do let(:old_email) { 'old.email@example.com' } let(:new_email) { 'new.email@example.com' } + it "provides better error message when a staged user has the same email" do + Fabricate(:user, staged: true, email: new_email) + + user = Fabricate(:user, email: old_email) + updater = EmailUpdater.new(user.guardian, user) + updater.change_to(new_email) + + expect(updater.errors).to be_present + expect(updater.errors.messages[:base].first).to be I18n.t("change_email.error_staged") + end + context 'as a regular user' do let(:user) { Fabricate(:user, email: old_email) } let(:updater) { EmailUpdater.new(user.guardian, user) } From d1b9a9370aa1f998690818155b5a3203d9a2a5d1 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 21 Mar 2016 15:14:16 -0400 Subject: [PATCH 047/162] PERF: Render logo significantly faster --- .../discourse/components/home-logo.js.es6 | 66 ++++++++++++------- .../discourse/components/mount-widget.js.es6 | 6 ++ .../templates/components/home-logo.hbs | 19 ------ .../components/home-logo-test.js.es6 | 12 +--- 4 files changed, 52 insertions(+), 51 deletions(-) delete mode 100644 app/assets/javascripts/discourse/templates/components/home-logo.hbs diff --git a/app/assets/javascripts/discourse/components/home-logo.js.es6 b/app/assets/javascripts/discourse/components/home-logo.js.es6 index c0846e268b..c1e39a437c 100644 --- a/app/assets/javascripts/discourse/components/home-logo.js.es6 +++ b/app/assets/javascripts/discourse/components/home-logo.js.es6 @@ -1,32 +1,28 @@ import DiscourseURL from 'discourse/lib/url'; -import { setting } from 'discourse/lib/computed'; +import { iconHTML } from 'discourse/helpers/fa-icon'; +import { observes } from 'ember-addons/ember-computed-decorators'; export default Ember.Component.extend({ - classNames: ["title"], + widget: 'home-logo', + showMobileLogo: null, + linkUrl: null, + classNames: ['title'], - targetUrl: function() { - // For overriding by customizations - return '/'; - }.property(), + init() { + this._super(); + this.showMobileLogo = this.site.mobileView && !Ember.isEmpty(this.siteSettings.mobile_logo_url); + this.linkUrl = this.get('targetUrl') || '/'; + }, - linkUrl: function() { - return Discourse.getURL(this.get('targetUrl')); - }.property('targetUrl'), + @observes('minimized') + _updateLogo() { + // On mobile we don't minimize the logo + if (!this.site.mobileView) { + this.rerender(); + } + }, - showSmallLogo: function() { - return !this.site.mobileView && this.get("minimized"); - }.property("minimized"), - - showMobileLogo: function() { - return this.site.mobileView && !Ember.isBlank(this.get('mobileBigLogoUrl')); - }.property(), - - smallLogoUrl: setting('logo_small_url'), - bigLogoUrl: setting('logo_url'), - mobileBigLogoUrl: setting('mobile_logo_url'), - title: setting('title'), - - click: function(e) { + click(e) { // if they want to open in a new tab, let it so if (e.shiftKey || e.metaKey || e.ctrlKey || e.which === 2) { return true; } @@ -34,5 +30,29 @@ export default Ember.Component.extend({ DiscourseURL.routeTo(this.get('targetUrl')); return false; + }, + + render(buffer) { + const { siteSettings } = this; + const logoUrl = siteSettings.logo_url || ''; + const title = siteSettings.title; + + buffer.push(``); + if (!this.site.mobileView && this.get('minimized')) { + const logoSmallUrl = siteSettings.logo_small_url || ''; + if (logoSmallUrl.length) { + buffer.push(``); + } else { + buffer.push(iconHTML('home')); + } + } else if (this.showMobileLogo) { + buffer.push(``); + } else if (logoUrl.length) { + buffer.push(``); + } else { + buffer.push(``); + } + buffer.push(''); } + }); diff --git a/app/assets/javascripts/discourse/components/mount-widget.js.es6 b/app/assets/javascripts/discourse/components/mount-widget.js.es6 index 8958cfd057..b7a8fdb020 100644 --- a/app/assets/javascripts/discourse/components/mount-widget.js.es6 +++ b/app/assets/javascripts/discourse/components/mount-widget.js.es6 @@ -56,6 +56,8 @@ export default Ember.Component.extend({ rerenderWidget() { Ember.run.cancel(this._timeout); if (this._rootNode) { + const t0 = new Date().getTime(); + const opts = { model: this.get('model') }; const newTree = new this._widgetClass(this.get('args'), this.container, opts); @@ -86,6 +88,10 @@ export default Ember.Component.extend({ } renderedKey('*'); + if (this.profileWidget) { + console.log(new Date().getTime() - t0); + } + } } diff --git a/app/assets/javascripts/discourse/templates/components/home-logo.hbs b/app/assets/javascripts/discourse/templates/components/home-logo.hbs deleted file mode 100644 index 8cb81d9c4b..0000000000 --- a/app/assets/javascripts/discourse/templates/components/home-logo.hbs +++ /dev/null @@ -1,19 +0,0 @@ - - {{#if showSmallLogo}} - {{#if smallLogoUrl}} - {{unbound title}} - {{else}} - - {{/if}} - {{else}} - {{#if showMobileLogo}} - - {{else}} - {{#if bigLogoUrl}} - - {{else}} - - {{/if}} - {{/if}} - {{/if}} - diff --git a/test/javascripts/components/home-logo-test.js.es6 b/test/javascripts/components/home-logo-test.js.es6 index 770343d499..60eefd94b9 100644 --- a/test/javascripts/components/home-logo-test.js.es6 +++ b/test/javascripts/components/home-logo-test.js.es6 @@ -17,6 +17,7 @@ componentTest('basics', { }, test(assert) { + assert.ok(this.$('.title').length === 1); assert.ok(this.$('a[data-auto-route]').length === 1); assert.ok(this.$('img#site-logo.logo-big').length === 1); @@ -55,29 +56,22 @@ componentTest('no logo', { }); componentTest('mobile logo', { - template: "{{home-logo minimized=minimized}}", + template: "{{home-logo}}", setup() { this.siteSettings.mobile_logo_url = mobileLogo; this.siteSettings.logo_small_url= smallLogo; - this.set('minimized', true); this.site.mobileView = true; }, test(assert) { assert.ok(this.$('img#site-logo.logo-big').length === 1); assert.equal(this.$('#site-logo').attr('src'), mobileLogo); - - this.set('minimized', true); - andThen(() => { - assert.equal(this.$('#site-logo').attr('src'), mobileLogo); - }); } }); componentTest('mobile without logo', { - template: "{{home-logo minimized=minimized}}", + template: "{{home-logo}}", setup() { - this.set('minimized', true); this.siteSettings.logo_url = bigLogo; this.site.mobileView = true; }, From b8929b906cefaebaba3d7d376a9a49e44f6b4f22 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 21 Mar 2016 15:16:07 -0400 Subject: [PATCH 048/162] FIX: Broken link --- app/assets/javascripts/discourse/components/home-logo.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/components/home-logo.js.es6 b/app/assets/javascripts/discourse/components/home-logo.js.es6 index c1e39a437c..4798205e2e 100644 --- a/app/assets/javascripts/discourse/components/home-logo.js.es6 +++ b/app/assets/javascripts/discourse/components/home-logo.js.es6 @@ -28,7 +28,7 @@ export default Ember.Component.extend({ e.preventDefault(); - DiscourseURL.routeTo(this.get('targetUrl')); + DiscourseURL.routeTo(this.linkUrl); return false; }, From 6c579ba9328a7c1a0c11ae0469d704105fa7aa40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 21 Mar 2016 23:09:36 +0100 Subject: [PATCH 049/162] FIX: close existing polls in closed topics to keep old behaviour --- ...60321164925_close_polls_in_closed_topics.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 plugins/poll/db/migrate/20160321164925_close_polls_in_closed_topics.rb diff --git a/plugins/poll/db/migrate/20160321164925_close_polls_in_closed_topics.rb b/plugins/poll/db/migrate/20160321164925_close_polls_in_closed_topics.rb new file mode 100644 index 0000000000..5ca969c68d --- /dev/null +++ b/plugins/poll/db/migrate/20160321164925_close_polls_in_closed_topics.rb @@ -0,0 +1,18 @@ +class ClosePollsInClosedTopics < ActiveRecord::Migration + + def up + PostCustomField.joins(post: :topic) + .where("post_custom_fields.name = 'polls'") + .where("topics.closed") + .find_each do |pcf| + polls = ::JSON.parse(pcf.value || "{}") + polls.values.each { |poll| poll["status"] = "closed" } + pcf.value = polls.to_json + pcf.save + end + end + + def down + end + +end From 643d7f07c3433f6924aa371d2480bdf350c65c8c Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 22 Mar 2016 10:01:54 +1100 Subject: [PATCH 050/162] correct very odd error on connect to redis --- lib/discourse_redis.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/discourse_redis.rb b/lib/discourse_redis.rb index 4bb3cd156d..8f865d9ac9 100644 --- a/lib/discourse_redis.rb +++ b/lib/discourse_redis.rb @@ -75,6 +75,9 @@ class DiscourseRedis end def resolve + + return @options unless @slave_options[:host] + begin options = @options.dup options.delete(:connector) From d31cc98ea756611f06ba8e880add256be5aa275e Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Mon, 21 Mar 2016 17:06:00 -0700 Subject: [PATCH 051/162] copyedit on some badge short descriptions --- config/locales/client.en.yml | 60 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 3c317af660..f72e8d4883 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2885,40 +2885,40 @@ en: description: Granted all essential community functions member: name: Member - description: Granted invitations + description: Granted invitations, group messaging, more likes regular: name: Regular - description: Granted recategorize, rename, followed links and lounge + description: Granted recategorize, rename, followed links, wiki, more likes leader: name: Leader - description: Granted global edit, pin, close, archive, split and merge + description: Granted global edit, pin, close, archive, split and merge, more likes welcome: name: Welcome description: Received a like autobiographer: name: Autobiographer - description: Filled user profile information + description: Filled out profile information anniversary: name: Anniversary description: Active member for a year, posted at least once nice_post: - name: Nice Post - description: Received 10 likes on a post. This badge can be granted multiple times + name: Nice Reply + description: Received 10 likes on a reply good_post: - name: Good Post - description: Received 25 likes on a post. This badge can be granted multiple times + name: Good Reply + description: Received 25 likes on a reply great_post: - name: Great Post - description: Received 50 likes on a post. This badge can be granted multiple times + name: Great Reply + description: Received 50 likes on a reply nice_topic: name: Nice Topic - description: Received 10 likes on a topic. This badge can be granted multiple times + description: Received 10 likes on a topic good_topic: name: Good Topic - description: Received 25 likes on a topic. This badge can be granted multiple times + description: Received 25 likes on a topic great_topic: name: Great Topic - description: Received 50 likes on a topic. This badge can be granted multiple times + description: Received 50 likes on a topic nice_share: name: Nice Share description: Shared a post with 25 unique visitors @@ -2939,61 +2939,61 @@ en: description: Invited a user campaigner: name: Campaigner - description: Invited 3 basic users (trust level 1) + description: Invited 3 basic users champion: name: Champion - description: Invited 5 members (trust level 2) + description: Invited 5 members first_share: name: First Share description: Shared a post first_link: name: First Link - description: Added an internal link to another topic + description: Added a link to another topic first_quote: name: First Quote - description: Quoted a user + description: Quoted a post read_guidelines: name: Read Guidelines description: Read the community guidelines reader: name: Reader - description: Read every post in a topic with more than 100 posts + description: Read every reply in a topic with more than 100 replies popular_link: name: Popular Link - description: Posted an external link with at least 50 clicks + description: Posted an external link with 50 clicks hot_link: name: Hot Link - description: Posted an external link with at least 300 clicks + description: Posted an external link with 300 clicks famous_link: name: Famous Link - description: Posted an external link with at least 1000 clicks + description: Posted an external link with 1000 clicks appreciated: name: Appreciated - description: Has received at least 1 like on 20 posts + description: Received 1 like on 20 posts respected: name: Respected - description: Has received at least 2 likes on 100 posts + description: Received 2 likes on 100 posts admired: name: Admired - description: Has received at least 5 likes on 300 posts + description: Received 5 likes on 300 posts out_of_love: name: Out of Love - description: Used the maximum amount of likes in a day + description: Used 50 likes in a day higher_love: name: Higher Love - description: Used the maximum amount of likes in a day 5 times + description: Used 50 likes in a day 5 times crazy_in_love: name: Crazy in Love - description: Used the maximum amount of likes in a day 20 times + description: Used 50 likes in a day 20 times thank_you: name: Thank You - description: Has at least 20 liked posts + description: Has 20 liked posts gives_back: name: Gives Back - description: Has at least 100 liked posts and a very high like ratio + description: Has 100 liked posts and a high like ratio empathetic: name: Empathetic - description: Has at least 500 liked posts and a superlative like ratio + description: Has 500 liked posts and a superlative like ratio google_search: |

Search with Google

From 6bdd5107a6dd5e13bf0501f2f7a20cd76ef33e1e Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 22 Mar 2016 12:44:55 +1100 Subject: [PATCH 052/162] UX: center align community title text in mobile --- app/assets/stylesheets/mobile/header.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/mobile/header.scss b/app/assets/stylesheets/mobile/header.scss index f72f099586..ab112d8bda 100644 --- a/app/assets/stylesheets/mobile/header.scss +++ b/app/assets/stylesheets/mobile/header.scss @@ -21,6 +21,11 @@ overflow: hidden; padding: 0; text-overflow: clip; + display: table; + a { + display: table-cell; + vertical-align: middle; + } } .icons { From f9e5c49350be058179a5e50e986bd14253570f31 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 22 Mar 2016 14:28:14 +1100 Subject: [PATCH 053/162] FIX: blue notification instead of green for replies/mentions in PMs --- app/services/post_alerter.rb | 41 +++++++++++++++--------------- spec/services/post_alerter_spec.rb | 37 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/app/services/post_alerter.rb b/app/services/post_alerter.rb index 150e33c35b..5ad5d3990d 100644 --- a/app/services/post_alerter.rb +++ b/app/services/post_alerter.rb @@ -47,12 +47,12 @@ class PostAlerter end expand_group_mentions(mentioned_groups, post) do |group, users| - notify_users(users - notified, :group_mentioned, post, mentioned_opts.merge({group: group})) + notify_non_pm_users(users - notified, :group_mentioned, post, mentioned_opts.merge({group: group})) notified += users end if mentioned_users - notify_users(mentioned_users - notified, :mentioned, post, mentioned_opts) + notify_non_pm_users(mentioned_users - notified, :mentioned, post, mentioned_opts) notified += mentioned_users end end @@ -61,41 +61,42 @@ class PostAlerter reply_to_user = post.reply_notification_target if new_record && reply_to_user && !notified.include?(reply_to_user) && post.post_type == Post.types[:regular] - notify_users(reply_to_user, :replied, post) + notify_non_pm_users(reply_to_user, :replied, post) notified += [reply_to_user] end # quotes quoted_users = extract_quoted_users(post) - notify_users(quoted_users - notified, :quoted, post) + notify_non_pm_users(quoted_users - notified, :quoted, post) notified += quoted_users # linked linked_users = extract_linked_users(post) - notify_users(linked_users - notified, :linked, post) + notify_non_pm_users(linked_users - notified, :linked, post) notified += linked_users # private messages if new_record if post.topic.private_message? - # users that aren't part of any mentionned groups + # users that aren't part of any mentioned groups directly_targeted_users(post).each do |user| - if !notified.include?(user) + notification_level = TopicUser.get(post.topic, user).try(:notification_level) + if notified.include?(user) || notification_level == TopicUser.notification_levels[:watching] create_notification(user, Notification.types[:private_message], post) - notified += [user] end end # users that are part of all mentionned groups indirectly_targeted_users(post).each do |user| - if !notified.include?(user) - # only create a notification when watching the group - notification_level = TopicUser.get(post.topic, user).try(:notification_level) - if notification_level == TopicUser.notification_levels[:watching] + # only create a notification when watching the group + notification_level = TopicUser.get(post.topic, user).try(:notification_level) + + if notification_level == TopicUser.notification_levels[:watching] + create_notification(user, Notification.types[:private_message], post) + elsif notification_level == TopicUser.notification_levels[:tracking] + if notified.include?(user) create_notification(user, Notification.types[:private_message], post) - notified += [user] - elsif notification_level == TopicUser.notification_levels[:tracking] + else notify_group_summary(user, post) - notified += [user] end end end @@ -400,13 +401,11 @@ class PostAlerter end # Notify a bunch of users - def notify_users(users, type, post, opts=nil) - users = [users] unless users.is_a?(Array) + def notify_non_pm_users(users, type, post, opts=nil) - if post.topic.try(:private_message?) - whitelist = all_allowed_users(post) - users.reject! { |u| !whitelist.include?(u) } - end + return if post.topic.try(:private_message?) + + users = [users] unless users.is_a?(Array) users.each do |u| create_notification(u, Notification.types[type], post, opts) diff --git a/spec/services/post_alerter_spec.rb b/spec/services/post_alerter_spec.rb index 6d444e7d83..d6889f7493 100644 --- a/spec/services/post_alerter_spec.rb +++ b/spec/services/post_alerter_spec.rb @@ -10,6 +10,43 @@ describe PostAlerter do PostAlerter.post_created(post) end + context "private message" do + it "notifies for pms correctly" do + pm = Fabricate(:topic, archetype: 'private_message', category_id: nil) + op = Fabricate(:post, user_id: pm.user_id) + pm.allowed_users << pm.user + PostAlerter.post_created(op) + reply = Fabricate(:post, user_id: pm.user_id, topic_id: pm.id, reply_to_post_number: 1) + PostAlerter.post_created(reply) + + reply2 = Fabricate(:post, topic_id: pm.id, reply_to_post_number: 1) + PostAlerter.post_created(reply2) + + # we get a green notification for a reply + expect(Notification.where(user_id: pm.user_id).pluck(:notification_type).first).to eq(Notification.types[:private_message]) + + TopicUser.change(pm.user_id, pm.id, notification_level: TopicUser.notification_levels[:tracking]) + + Notification.destroy_all + + reply3 = Fabricate(:post, topic_id: pm.id) + PostAlerter.post_created(reply3) + + # no notification cause we are tracking + expect(Notification.where(user_id: pm.user_id).count).to eq(0) + + Notification.destroy_all + + reply4 = Fabricate(:post, topic_id: pm.id, reply_to_post_number: 1) + PostAlerter.post_created(reply4) + + # yes notification cause we were replied to + expect(Notification.where(user_id: pm.user_id).count).to eq(1) + + + end + end + context "unread" do it "does not return whispers as unread posts" do op = Fabricate(:post) From d09a20210b8994ee181601bcb276571f3f6391f2 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 22 Mar 2016 14:50:12 +1100 Subject: [PATCH 054/162] correct bad spec --- spec/models/notification_spec.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index 09b9f58433..ef64bcfbe3 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -159,8 +159,11 @@ describe Notification do before do @topic = Fabricate(:private_message_topic) @post = Fabricate(:post, topic: @topic, user: @topic.user) - PostAlerter.post_created(@post) @target = @post.topic.topic_allowed_users.reject{|a| a.user_id == @post.user_id}[0].user + + TopicUser.change(@target.id, @topic.id, notification_level: TopicUser.notification_levels[:watching]) + + PostAlerter.post_created(@post) end it 'should create and rollup private message notifications' do From a202ec2028eaaf56d9f76287867fe8d487cf02d8 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 22 Mar 2016 14:50:35 +1100 Subject: [PATCH 055/162] don't run mailing list if mailing list mode is disabled --- app/jobs/regular/notify_mailing_list_subscribers.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/jobs/regular/notify_mailing_list_subscribers.rb b/app/jobs/regular/notify_mailing_list_subscribers.rb index 29436e4232..540b472c23 100644 --- a/app/jobs/regular/notify_mailing_list_subscribers.rb +++ b/app/jobs/regular/notify_mailing_list_subscribers.rb @@ -3,6 +3,8 @@ module Jobs class NotifyMailingListSubscribers < Jobs::Base def execute(args) + return if SiteSetting.disable_mailing_list_mode + post_id = args[:post_id] post = post_id ? Post.with_deleted.find_by(id: post_id) : nil From 8fea5ad35eb28e949c2ccdf66d6111ae9922a4e0 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 22 Mar 2016 15:23:46 +0800 Subject: [PATCH 056/162] Update Logster. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 79cd48ccc0..4c6557e5fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -150,7 +150,7 @@ GEM thor (~> 0.15) libv8 (3.16.14.13) listen (0.7.3) - logster (1.2.0) + logster (1.2.1) loofah (2.0.3) nokogiri (>= 1.5.9) lru_redux (1.1.0) From 69268786c40650aedba69d38f6991b75a1dc7383 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 22 Mar 2016 04:19:41 -0700 Subject: [PATCH 057/162] lots more + better long badge descriptions --- config/locales/server.en.yml | 59 +++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 22245e32d8..b254d87132 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2741,54 +2741,63 @@ en: badges: long_descriptions: autobiographer: | - This badge is granted for filling out your user profile and selecting a profile picture. Letting the community know more about who you are and what you're interested in makes for a better, more connected community. + This badge is granted for filling out your user profile and selecting a profile picture. Letting the community know a bit more about who you are and what you're interested in makes for a better, more connected community. Join us! first_like: | This badge is granted the first time you like a post using the :heart: button. Liking posts is a great way to let your fellow community members know that what they posted was interesting, useful, cool, or fun. Share the love! first_link: | - This badge is granted the first time you place a link to another topic in a reply. Linking topics helps fellow readers find interesting related conversations, by showing connections between topics in both directions. + This badge is granted the first time you add a link to another topic. Linking topics helps fellow readers find interesting related conversations, by showing the connections between topics in both directions. Link freely! first_quote: | - This badge is granted the first time you quote a post in your reply. Quoting relevant sections of earlier posts in your reply helps keep discussions focused and on topic. + This badge is granted the first time you quote a post in your reply. Quoting relevant sections of earlier posts in your reply helps keep discussions focused and on topic. And it's easy: you can quickly quote by highlighting a section of any post and using the Quote Reply button that appears near the selection. Quote generously! first_share: | This badge is granted the first time you share a link to a reply or topic using the share button. Sharing links is a great way to show off interesting discussions with the rest of the world and grow your community. read_guidelines: | - This badge is granted for reading the community guidelines. Following and sharing these simple guidelines helps build a safe, fun, and sustainable community. + This badge is granted for reading the community guidelines. Following and sharing these simple guidelines helps build a safe, fun, and sustainable community for everyone. Always remember there's another human being, one very much like yourself, on the other side of that screen. Be nice! reader: | - This badge is granted for reading a long topic. Reading is fundamental. Reading closely helps you follow the conversation and leads to better, more complete replies. + This badge is granted the first time you read a long topic with more than 100 replies. Reading a conversation closely helps you follow the discussion, understand different viewpoints, and leads to more interesting conversations. The more you read, the better the conversation gets. As we like to say, Reading is Fundamental! :simple_smile: editor: | - This badge is granted for editing your post. Don't hesitate to edit your posts any time to improve them, fix small mistakes, or add anything you forgot. + This badge is granted the first time you edit one of your posts. While you won't be able to edit your posts forever, editing is always a good idea -- you can improve your posts, fix small mistakes, or add anything you missed when you originally posted. Edit to make your posts even better! first_flag: | - This badge is granted for flagging a post. Flagging is critical to the health of your community. If you notice any posts that require moderator attention please - do not hesitate to flag. You may also use the flagging dialog to send messages to fellow users. + This badge is granted the first time you flag a post. Flagging is how we all help keep this a clean, well lit place for everyone. If you notice any posts that require moderator attention for any reason please don't hesitate to flag. You can also use flag to send personal messages to fellow users if you see an issue with their post. If you see a problem, flag it! nice_share: | This badge is granted for sharing a link to a post that's visited by 25 outside visitors. Nice job! Sharing links to interesting discussions with friends is an excellent way to grow our community. welcome: | - This badge is granted when you receive your first like on a post. Congratulations, you've posted something that your fellow community members found interesting, cool, or useful! + This badge is granted when you receive your first like on a post. Congratulations, you've posted something that your fellow community members found interesting, cool, or useful! Now keep going! anniversary: | - This badge is granted when you've been a member for a year with at least one post in that year. Thanks for sticking around and contributing to our community! + This badge is granted when you've been a member for a year with at least one post in that year. Thank you for sticking around and contributing to our community. We couldn't do it without you. + nice_share: | + This badge is granted for sharing a link that was clicked by 25 outside visitors. Thanks for spreading the word about our discussions, and this community. good_share: | - This badge is granted for sharing a link to a post that's visited by 300 outside visitors. Good work! You've shown off an interesting discussion to a lot of new people and helped us grow. + This badge is granted for sharing a link that was clicked by 300 outside visitors. Good work! You've shown off a great discussion to a bunch of new people and helped this community grow. great_share: | - This badge is granted for sharing a link to a post that's visited by 100 outside visitors. Wow! You've promoted an interesting discussion to a huge new audience for this community, and helped us grow in a big way. - nice_post: | - This badge is granted for creating a reply that gets 10 likes. Nice job! + This badge is granted for sharing a link that was clicked by 1000 outside visitors. Wow! You've promoted an interesting discussion to a huge new audience, and helped us grow our community in a big way! nice_topic: | - This badge is granted for creating a topic that gets 10 likes. Nice job! - good_post: | - This badge is granted for creating a reply that gets 25 likes. Good work! + This badge is granted when your topic gets 10 likes. You started an interesting conversation that the community enjoyed. + nice_post: | + This badge is granted when your reply gets 10 likes. Your reply made an impression on the community and helped move the conversation forward. good_topic: | - This badge is granted for creating a topic that gets 25 likes. Good work! - great_post: | - This badge is granted for creating a post that gets 50 likes. Wow! + This badge is granted when your topic gets 25 likes. You launched a vibrant conversation that the community really responded to. + good_post: | + This badge is granted when your reply gets 25 likes. Your reply was exceptional and it made the conversation a whole lot better for everyone. great_topic: | - This badge is granted for creating a reply that gets 50 likes. Wow! + This badge is granted when your topic gets 50 likes. Wow! You kicked off a fascinating conversation and the community loved the dynamic discussion that resulted. + great_post: | + This badge is granted when your reply gets 50 likes. Wow! Your reply was inspiring, fascinating, hilarious, or insightful and the community loved it. basic: | - This badge is granted when you reach trust level 1. Thanks for sticking around a little while and reading a few topics to learn what our community is about. Your new user restrictions have been lifted, and you've been granted all essential community abilities, such as personal messaging, flagging, wiki editing, and the ability to post images and multiple links. + This badge is granted when you reach trust level 1. Thanks for sticking around a little while and reading a few topics to learn what our community is about. Your new user restrictions have been lifted; you've been granted all essential community abilities, such as personal messaging, flagging, wiki editing, and the ability to post multiple images and links. + appreciated: | + This badge is granted when you receive at least one like on 20 different posts. The community is enjoying your contributions to the conversations here, so keep them coming! + out_of_love: | + This badge is granted when you use all 50 of your daily likes. Letting the community know what's great by regularly liking those posts you enjoy and appreciate is the best way to encourage people to create even more great discussions in the future. + promoter: | + This badge is granted when you invite someone to join the community via the invite button on your user page, or at the bottom of a topic. Inviting friends who might be interested in specific discussions is an great way to introduce new people to our community, so thanks! + thank_you: | + This badge is granted when you have at least 20 liked posts. member: | - This badge is granted when you reach trust level 2. Thanks for participating over a period of weeks to more join our community. You can now send personal invitations from your user page or individual topics, create group messages, and a few more likes per day. + This badge is granted when you reach trust level 2. Thanks for participating over a period of weeks to truly join our community. You can now send invitations from your user page or individual topics, create group personal messages, and have a few more likes per day. regular: | - This badge is granted when you reach trust level 3. Thanks for being a regular part of our community over a period of months, one of the most active readers and a reliable contributor to what makes this community great. You can now recategorize and rename topics, access a private lounge area, more powerful spam flags, and lots more likes per day. + This badge is granted when you reach trust level 3. Thanks for being a regular part of our community over a period of months. You're now one of the most active readers, and a reliable contributor that makes our community great. You can now recategorize and rename topics, take advantage of more powerful spam flags, access a private lounge area, and you'll also get lots more likes per day. leader: | - This badge is granted when you reach trust level 4. You're a leader in this community as selected by staff, and set a positive example for the community in your deeds and words. You have the ability to edit all posts, take topic moderator actions such as pin, close, unlist, archive, split, and merge, and tons of likes per day. + This badge is granted when you reach trust level 4. You're a leader in this community as selected by staff, and you set a positive example for the rest of the community in your actions and words here. You have the ability to edit all posts, take common topic moderator actions such as pin, close, unlist, archive, split, and merge, and you have tons of likes per day. admin_login: success: "Email Sent" From 1f2aa3aa8bb55dcef7877459883a9be8a032e21c Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 22 Mar 2016 04:38:56 -0700 Subject: [PATCH 058/162] clearer "gives back" descriptions --- config/locales/client.en.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index f72e8d4883..7b3f762848 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2987,13 +2987,13 @@ en: description: Used 50 likes in a day 20 times thank_you: name: Thank You - description: Has 20 liked posts + description: Has 20 liked posts and gave 10 likes gives_back: name: Gives Back - description: Has 100 liked posts and a high like ratio + description: Has 100 liked posts and gave 100 likes empathetic: name: Empathetic - description: Has 500 liked posts and a superlative like ratio + description: Has 500 liked posts and gave 1000 likes google_search: |

Search with Google

From 717b54d64bb1d62a32bd31dc04e5cedbfb2787ea Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 22 Mar 2016 10:51:42 -0400 Subject: [PATCH 059/162] Adjust badges to match descriptions cc @coding-horror --- app/models/badge.rb | 15 ++++++--------- db/fixtures/006_badges.rb | 6 +++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/app/models/badge.rb b/app/models/badge.rb index 8baf6180ed..95f2b0daa4 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -295,16 +295,13 @@ SQL SQL end - def self.liked_back(min_posts, ratio) + def self.liked_back(likes_received, likes_given) <<-SQL - SELECT p.user_id, current_timestamp AS granted_at - FROM posts AS p - INNER JOIN user_stats AS us ON us.user_id = p.user_id - WHERE p.like_count > 0 - AND (:backfill OR p.user_id IN (:user_ids)) - GROUP BY p.user_id, us.likes_given - HAVING count(*) > #{min_posts} - AND (us.likes_given / count(*)::float) > #{ratio} + SELECT us.user_id, current_timestamp AS granted_at + FROM user_stats AS us + WHERE us.likes_received >= #{likes_received} + AND us.likes_given >= #{likes_given} + AND (:backfill OR us.user_id IN (:user_ids)) SQL end end diff --git a/db/fixtures/006_badges.rb b/db/fixtures/006_badges.rb index c8c30236e4..5d594a2f01 100644 --- a/db/fixtures/006_badges.rb +++ b/db/fixtures/006_badges.rb @@ -314,9 +314,9 @@ end [ - [Badge::ThankYou, "Thank You", BadgeType::Bronze, 20, 0.00], - [Badge::GivesBack, "Gives Back", BadgeType::Silver, 100, 1.0], - [Badge::Empathetic, "Empathetic", BadgeType::Gold, 500, 2.0], + [Badge::ThankYou, "Thank You", BadgeType::Bronze, 20, 10], + [Badge::GivesBack, "Gives Back", BadgeType::Silver, 100, 100], + [Badge::Empathetic, "Empathetic", BadgeType::Gold, 500, 1000] ].each do |spec| id, name, level, count, ratio = spec Badge.seed do |b| From a9320f668c1f856cd84712101eda80608f20651e Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 22 Mar 2016 11:15:15 -0400 Subject: [PATCH 060/162] FIX: Invalid YAML --- config/locales/server.en.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index b254d87132..c6dc3a4c45 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2759,13 +2759,11 @@ en: first_flag: | This badge is granted the first time you flag a post. Flagging is how we all help keep this a clean, well lit place for everyone. If you notice any posts that require moderator attention for any reason please don't hesitate to flag. You can also use flag to send personal messages to fellow users if you see an issue with their post. If you see a problem, flag it! nice_share: | - This badge is granted for sharing a link to a post that's visited by 25 outside visitors. Nice job! Sharing links to interesting discussions with friends is an excellent way to grow our community. + This badge is granted for sharing a link that was clicked by 25 outside visitors. Thanks for spreading the word about our discussions, and this community. welcome: | This badge is granted when you receive your first like on a post. Congratulations, you've posted something that your fellow community members found interesting, cool, or useful! Now keep going! anniversary: | This badge is granted when you've been a member for a year with at least one post in that year. Thank you for sticking around and contributing to our community. We couldn't do it without you. - nice_share: | - This badge is granted for sharing a link that was clicked by 25 outside visitors. Thanks for spreading the word about our discussions, and this community. good_share: | This badge is granted for sharing a link that was clicked by 300 outside visitors. Good work! You've shown off a great discussion to a bunch of new people and helped this community grow. great_share: | From 6a2fe44e513e352e9831b9ff27bc7dacc96cfd0d Mon Sep 17 00:00:00 2001 From: Manel Villar Date: Tue, 22 Mar 2016 16:42:54 +0100 Subject: [PATCH 061/162] Support for Galician language added --- config/locales/client.gl.yml | 2764 +++++++++++++++++ config/locales/server.gl.yml | 492 +++ plugins/poll/config/locales/client.gl.yml | 41 + plugins/poll/config/locales/server.gl.yml | 32 + public/403.gl.html | 26 + public/422.gl.html | 25 + public/500.gl.html | 12 + public/503.gl.html | 11 + .../lib/discourse_imgur/locale/server.gl.yml | 5 + 9 files changed, 3408 insertions(+) create mode 100644 config/locales/client.gl.yml create mode 100644 config/locales/server.gl.yml create mode 100644 plugins/poll/config/locales/client.gl.yml create mode 100644 plugins/poll/config/locales/server.gl.yml create mode 100644 public/403.gl.html create mode 100644 public/422.gl.html create mode 100644 public/500.gl.html create mode 100644 public/503.gl.html create mode 100644 vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.gl.yml diff --git a/config/locales/client.gl.yml b/config/locales/client.gl.yml new file mode 100644 index 0000000000..d44605a45c --- /dev/null +++ b/config/locales/client.gl.yml @@ -0,0 +1,2764 @@ +gl: + js: + number: + format: + separator: "." + delimiter: "," + human: + storage_units: + format: '%n %u' + units: + byte: + one: Byte + other: Bytes + gb: GB + kb: KB + mb: MB + tb: TB + short: + thousands: "{{number}}k" + millions: "{{number}}M" + dates: + time: "h:mm a" + long_no_year: "D MMM h:mm a" + long_no_year_no_time: "D MMM" + full_no_year_no_time: "D MMMM" + long_with_year: "D MMM, YYYY h:mm a" + long_with_year_no_time: "D MMM, YYYY" + full_with_year_no_time: "D MMMM, YYYY" + long_date_with_year: "D MMM, 'YY LT" + long_date_without_year: "D MMM, LT" + long_date_with_year_without_time: "D MMM, 'YY" + long_date_without_year_with_linebreak: "D MMM
LT" + long_date_with_year_with_linebreak: "D MMM, 'YY
LT" + tiny: + half_a_minute: "< 1m" + less_than_x_seconds: + one: "< 1s" + other: "< %{count}s" + x_seconds: + one: "1s" + other: "%{count}s" + less_than_x_minutes: + one: "< 1m" + other: "< %{count}m" + x_minutes: + one: "1m" + other: "%{count}m" + about_x_hours: + one: "1h" + other: "%{count}h" + x_days: + one: "1d" + other: "%{count}d" + about_x_years: + one: "1ano" + other: "%{count}anos" + over_x_years: + one: "> 1ano" + other: "> %{count}anos" + almost_x_years: + one: "1ano" + other: "%{count}anos" + date_month: "D MMM" + date_year: "MMM 'YY" + medium: + x_minutes: + one: "1 min" + other: "%{count} mins" + x_hours: + one: "1 hora" + other: "%{count} horas" + x_days: + one: "1 día" + other: "%{count} días" + date_year: "D MMM, 'YY" + medium_with_ago: + x_minutes: + one: "Hai 1 min." + other: "Hai %{count} min." + x_hours: + one: "Hai 1 hora" + other: "Hai %{count} horas" + x_days: + one: "Hai 1 día" + other: "Hai %{count} días" + later: + x_days: + one: "1 día despois" + other: "%{count} días despois" + x_months: + one: "1 mes despois" + other: "%{count} meses despois" + x_years: + one: "1 anos despois" + other: "%{count} anos despois" + previous_month: 'Mes anterior' + next_month: 'Mes seguinte' + share: + topic: 'compartir unha ligazón a este tema' + post: 'publicación %{postNumber}' + close: 'pechar' + twitter: 'compartir esta ligazón no Twitter' + facebook: 'compartir esta ligazón no Facebook' + google+: 'compartir esta ligazón no Google+' + email: 'enviar esta ligazón nun correo electrónico' + action_codes: + split_topic: "este tema dividiuse o %{when}" + invited_user: "convidou a %{who} %{when}" + removed_user: "eliminou a %{who} %{when}" + autoclosed: + enabled: 'pechado o %{when}' + disabled: 'aberto o %{when}' + closed: + enabled: 'pechado o %{when}' + disabled: 'aberto o %{when}' + archived: + enabled: 'arquivado o %{when}' + disabled: 'desarquivado o %{when}' + pinned: + enabled: 'pegado o %{when}' + disabled: 'despegado o %{when}' + pinned_globally: + enabled: 'pegado globalmente o %{when}' + disabled: 'despegado o %{when}' + visible: + enabled: 'listado o %{when}' + disabled: 'retirado da lista o %{when}' + topic_admin_menu: "accións do administrador de temas" + emails_are_disabled: "Todos os correos electrónicos saíntes foron desactivados globalmente por un administrador. Non se enviará ningún tipo de notificación por correo electrónico." + s3: + regions: + us_east_1: "EE.UU. Leste (N. Virxinia)" + us_west_1: "EE.UU. Oeste (N. California)" + us_west_2: "EE.UU. Oeste (Oregón)" + us_gov_west_1: "AWS GovCloud (EE.UU)" + eu_west_1: "EU (Irlanda)" + eu_central_1: "EU (Frankfurt)" + ap_southeast_1: "Asia Pacífico (Singapur)" + ap_southeast_2: "Asia Pacífico (Sidney)" + ap_northeast_1: "Asia Pacífico (Tokio)" + ap_northeast_2: "Asia Pacífico (Seúl)" + sa_east_1: "América do Sur (São Paulo)" + edit: 'editar o título e a categoría deste tema' + not_implemented: "Sentímolo pero esta funcionalidade non se implementou aínda." + no_value: "Non" + yes_value: "Si" + generic_error: "Sentímolo pero produciuse un erro." + generic_error_with_reason: "Produciuse un erro: %{error}" + sign_up: "Crear unha conta" + log_in: "Iniciar sesión" + age: "Idade" + joined: "Inscrito" + admin_title: "Admin" + flags_title: "Denuncias" + show_more: "amosar máis" + show_help: "opcións" + links: "Ligazóns" + links_lowercase: + one: "ligazón" + other: "ligazóns" + faq: "FAQ" + guidelines: "Directrices" + privacy_policy: "Normas de confidencialidade" + privacy: "Confidencialidade" + terms_of_service: "Termos do servizo" + mobile_view: "Visualización móbil" + desktop_view: "Visualización en escritorio" + you: "Ti" + or: "ou" + now: "agora mesmiño" + read_more: 'ler máis' + more: "Máis" + less: "Menos" + never: "nunca" + every_30_minutes: "cada 30 minutos" + every_hour: "cada hora" + daily: "diariamente" + weekly: "semanalmente" + every_two_weeks: "cada dúas semanas" + every_three_days: "cada tres días" + max_of_count: "máx. de {{count}}" + alternation: "ou" + character_count: + one: "{{count}} carácter" + other: "{{count}} caracteres" + suggested_topics: + title: "Temas suxeridos" + pm_title: "Mensaxes suxeridas" + about: + simple_title: "Verbo de" + title: "Verbo de %{title}" + stats: "Estatísticas do sitio" + our_admins: "Administradores" + our_moderators: "Os moderadores" + stat: + all_time: "De sempre" + last_7_days: "Últimos 7 días" + last_30_days: "Últimos 30 días" + like_count: "Gústames" + topic_count: "Temas" + post_count: "Publicacións" + user_count: "Novos usuarios" + active_user_count: "Usuarios activos" + contact: "Contacta connosco" + contact_info: "No caso dunha incidencia crítica ou asunto urxente que afecte este sitio, contacta connosco en %{contact_info}." + bookmarked: + title: "Marcador" + clear_bookmarks: "Limpar marcadores" + help: + bookmark: "Preme para engadir aos marcadores a publicación inicial deste tema" + unbookmark: "Preme para retirar todos os marcadores deste tema" + bookmarks: + not_logged_in: "cómpre que teñas a sesión iniciada para engadir unha publicación aos marcadores" + created: "engadiches aos marcadores esta publicación" + not_bookmarked: "Acabas de ler esta publicación; preme para engadila aos marcadores" + last_read: "esta é a última publicación lida por ti; preme para engadila aos marcadores" + remove: "Eliminar marcador" + confirm_clear: "Confirmas o borrado de todos os marcadores deste tema?" + topic_count_latest: + one: "{{count}} tema novo ou actualizado." + other: "{{count}} temas novos ou actualizados." + topic_count_unread: + one: "{{count}} tópico sen ler." + other: "{{count}} temas sen ler." + topic_count_new: + one: "{{count}} tópico novo" + other: "{{count}} temas novos" + click_to_show: "Preme para amosar." + preview: "previsualizar" + cancel: "cancelar" + save: "Gardar cambios" + saving: "Gardando...." + saved: "Gardado!" + upload: "Actualizar" + uploading: "Actualizando..." + uploading_filename: "Actualizando {{filename}}..." + uploaded: "Actualizado!" + enable: "Activar" + disable: "Desactivar" + undo: "Desfacer" + revert: "Reverter" + failed: "Fallou" + switch_to_anon: "Modo anónimo" + switch_from_anon: "Saír do Modo anónimo" + banner: + close: "Desbotar este báner." + edit: "Editar este báner »" + choose_topic: + none_found: "Non se atoparon temas." + title: + search: "Buscar un tema polo nome, URL ou ID:" + placeholder: "escribe o título do tema aquí" + queue: + topic: "Tema:" + approve: 'Aprobar' + reject: 'Rexeitar' + delete_user: 'Eliminar usuario' + title: "Cómpre aprobación" + none: "Non hai publicacións para revisar." + edit: "Editar" + cancel: "Cancelar" + view_pending: "ver as publicacións pendentes" + has_pending_posts: + one: "Este tema ten {{count}} publicación agardando aprobación" + other: "Este tema ten {{count}} publicacións agardando aprobación." + confirm: "Gardar os cambios" + delete_prompt: "Confirmas a eliminación de %{username}? Eliminaranse todas as súas publicacións e bloquearase o seu correo electrónico e enderezo IP." + approval: + title: "A publicación necesita aprobación" + description: "Recibimos a túa nova publicación pero cómpre que sexa aprobada por un moderador antes de aparecer. Ten paciencia." + pending_posts: + one: "Tes {{count}} publicación pendente." + other: "Tes {{count}} publicacións pendentes." + ok: "De acordo" + user_action: + user_posted_topic: "{{user}} publicou o tema" + you_posted_topic: "Ti publicaches o tema" + user_replied_to_post: "{{user}} respondeu a {{post_number}}" + you_replied_to_post: "Ti respondiches a {{post_number}}" + user_replied_to_topic: "{{user}} respondeu ao tema " + you_replied_to_topic: "Ti respondiches ao tema " + user_mentioned_user: "{{user}} citou a {{another_user}}" + user_mentioned_you: "{{user}} citou a , vaia, a ti." + you_mentioned_user: "Ti citaches a {{another_user}}" + posted_by_user: "Publicado por {{user}}" + posted_by_you: "Publicado por ti" + sent_by_user: "Enviado por {{user}}" + sent_by_you: "Enviado por , vaia, ti mesmo." + directory: + filter_name: "filtrar por nome de usuario" + title: "Usuarios" + likes_given: "Dados" + likes_received: "Recibidos" + topics_entered: "Introducidos" + topics_entered_long: "Temas introducidos" + time_read: "Tempo de lectura" + topic_count: "Temas" + topic_count_long: "Temas creados" + post_count: "Respostas" + post_count_long: "Respostas publicadas" + no_results: "Non se atoparon resultados." + days_visited: "Visitas" + days_visited_long: "Días visitados" + posts_read: "Lidas" + posts_read_long: "Publicacións lidas" + total_rows: + one: "Un usuario" + other: "%{count} usuarios" + groups: + empty: + posts: "Non hai publicacións de membros deste grupo." + members: "Non hai ningún membro neste grupo." + mentions: "Non hai ningunha mención deste grupo." + messages: "Non hai ningunha mensaxe para este grupo." + topics: "Non hai temas por membros deste grupo." + add: "Engadir" + selector_placeholder: "Engadir membros" + owner: "propietario" + visible: "O grupo é visíbel para todos os usuarios." + title: + one: "grupo" + other: "grupos" + members: "Membros" + topics: "Temas" + posts: "Publicacións" + mentions: "Mencións" + messages: "Mensaxes" + alias_levels: + title: "Quen pode enviar mensaxes e @mención a este grupo?" + nobody: "Ninguén" + only_admins: "Só administradores" + mods_and_admins: "Só moderadores e administradores" + members_mods_and_admins: "Só membros do grupo, moderadores e administradores" + everyone: "Todos" + trust_levels: + title: "Nivel de confianza automático concedido aos membros cando son engadidos:" + none: "Ningún" + notifications: + watching: + title: "Ver" + description: "Notificaráseche cada publicación nova en cada mensaxe e amosarase o número de novas respostas." + tracking: + title: "Seguimento" + description: "Notificaráseche se alguén menciona o teu @nome ou che responde e tamén aparecerá o número de novas respostas." + regular: + title: "Normal" + description: "Notificaráseche se alguén menciona o teu @nome ou che responde." + muted: + title: "Silenciado" + description: "Non recibirás notificacións de nada relacionado con novos temas neste grupo." + user_action_groups: + '1': "Gústames dados" + '2': "Gústames recibidos" + '3': "Marcadores" + '4': "Temas" + '5': "Respostas" + '6': "Respostas" + '7': "Mencións" + '9': "CItas" + '11': "Edicións" + '12': "Enviar elementos" + '13': "Caixa de entrada" + '14': "Pendente" + categories: + all: "todas as categorías" + all_subcategories: "todas" + no_subcategory: "ningunha" + category: "Categoría" + category_list: "Amosar a lista de categorías" + reorder: + title: "Reordenar as categorías" + title_long: "Reorganizar a lista de categorías" + fix_order: "Fixar as posicións" + fix_order_tooltip: "Non todas as categorías teñen un número de posición único, e iso pode causar resultados inesperados." + save: "Gardar orde" + apply_all: "Aplicar" + position: "Posición" + posts: "Publicacións" + topics: "Temas" + latest: "Últimos" + latest_by: "últimos de" + toggle_ordering: "trocar o control de ordenación" + subcategories: "Subcategorías" + topic_stats: "Número de temas novos." + topic_stat_sentence: + one: "%{count} tema novo nos últimos %{unit}." + other: "%{count} temas novos nos últimos %{unit}." + post_stats: "O número de publicacións novas." + post_stat_sentence: + one: "%{count} nova publicación no último %{unit}." + other: "%{count} novas publicacións nos últimos %{unit}." + ip_lookup: + title: Busca do enderezo IP + hostname: Nome do servidor + location: Localización + location_not_found: (descoñecido) + organisation: Organización + phone: Teléfono + other_accounts: "Outras contas co mesmo enderezo IP:" + delete_other_accounts: "Eliminar %{count}" + username: "nome do usuario" + trust_level: "NdeC" + read_time: "tempo de lectura" + topics_entered: "temas introducidos" + post_count: "# publicacións" + confirm_delete_other_accounts: "Confirma que quere eliminar estas contas?" + user_fields: + none: "(seleccione unha opción)" + user: + said: "{{username}}:" + profile: "Perfil" + mute: "Silenciar" + edit: "Editar preferencias" + download_archive: "Descargar as miñas publicacións" + new_private_message: "Nova mensaxe" + private_message: "Mensaxe" + private_messages: "Mensaxes" + activity_stream: "Actividade" + preferences: "Preferencias" + expand_profile: "Expandir" + bookmarks: "Marcadores" + bio: "Verbo de min" + invited_by: "Convidado por" + trust_level: "Nivel de confianza" + notifications: "Notificacións" + statistics: "Estatísticas" + desktop_notifications: + label: "Notificacións en escritorio" + not_supported: "Este navegador non admite notificacións. Desculpe." + perm_default: "Acender notificacións" + perm_denied_btn: "Permiso denegado" + perm_denied_expl: "Denegaches o permiso para notificacións no teu navegador. Modifica os axustes para recibir notificacións no teu navegador." + disable: "Desactivar as notificacións" + enable: "Activar as notificacións" + each_browser_note: "Nota: Tes que cambiar este axuste en cadanseu navegador que utilices." + dismiss_notifications: "Marcar todas como lidas" + dismiss_notifications_tooltip: "Marcar todas notificacións sen ler como lidas" + disable_jump_reply: "Non saltar á miña publicación despois de que responda" + dynamic_favicon: "Amosar o número de temas novos / actualizados na icona do navegador" + edit_history_public: "Permitirlles a outros usuarios ver as revisións das miñas publicacións" + external_links_in_new_tab: "Abrir todas as ligazóns externas nunha nova lapela" + enable_quoting: "Activar as comiñas de resposta para o texto realzado" + change: "cambiar" + moderator: "{{user}} é moderador" + admin: "{{user}} é administrador" + moderator_tooltip: "Este usuario é un moderador" + admin_tooltip: "Este usuario é un administrador" + blocked_tooltip: "Este usuario está bloqueado" + suspended_notice: "Este usuario está suspendido até o {{date}}." + suspended_reason: "Razón:" + github_profile: "Github" + mailing_list_mode: "Enviar un correo por cada nova publicación (a non ser que eu silencie o tema ou categoría)" + watched_categories: "Visto" + watched_categories_instructions: "Mirarás automaticamente todos os temas novos nestas categorías. Notificaránseche todas as novas publicacións e temas, e o número de novas publicacións aparecerá tamén preto do tema." + tracked_categories: "Seguido" + tracked_categories_instructions: "Seguirás automaticamente todos os temas novos nestas categorías. Un número de novas publicacións aparecerá preto do tema." + muted_categories: "Silenciado" + muted_categories_instructions: "Non se che notificará nada sobre os temas novos nestas categorías e non aparecerán na lista de últimos." + delete_account: "Eliminar a miña conta" + delete_account_confirm: "Confirmas que queres eliminar definitivamente a túa conta? Esta acción non se pode desfacer!" + deleted_yourself: "A túa conta acaba de ser eliminada completamente." + delete_yourself_not_allowed: "Non podes eliminar a túa conta neste intre. Contacta cun administrador para que este a elimine por ti. " + unread_message_count: "Mensaxes" + admin_delete: "Eliminar" + users: "Usuarios" + muted_users: "Silenciado" + muted_users_instructions: "Suprimir todas as notificacións destes usuarios." + muted_topics_link: "Amosar os temas silenciados" + automatically_unpin_topics: "Despegar os temas automaticamente cando eu chegue á banda inferior." + staff_counters: + flags_given: "denuncias útiles" + flagged_posts: "publicacións denunciadas" + deleted_posts: "publicacións eliminadas" + suspensions: "suspensións" + warnings_received: "advertencias" + messages: + all: "Todas" + inbox: "Caixa de entrada" + sent: "Enviados" + archive: "Arquivo" + groups: "Os meus grupos" + bulk_select: "Seleccionar mensaxes" + move_to_inbox: "Mover á caixa de entrada" + move_to_archive: "Arquivo" + failed_to_move: "Produciuse un fallo ao mover as mensaxes seleccionadas (quizais a rede está caída)" + select_all: "Seleccionar todo" + change_password: + success: "(correo enviado)" + in_progress: "(enviando o correo)" + error: "(erro)" + action: "Enviar correo para restabelecer o contrasinal" + set_password: "Estabelecer o contrasinal" + change_about: + title: "Cambiar «Verbo de min»" + error: "Produciuse un erro ao cambiar este valor." + change_username: + title: "Cambiar o nome do usuario" + confirm: "Se cambias o nome de usuario, todas as citas anteriores das túas publicacións e as mencións ao teu @nome romperanse. Estás totalmente seguro?" + taken: "Sentímolo pero este nome xa está en uso." + error: "Produciuse un erro cambiando o teu nome de usuario." + invalid: "Este usuario é incorrecto. Só pode contar números e letras." + change_email: + title: "Cambiar o correo electrónico" + taken: "Sentímolo pero este correo non está dispoñíbel." + error: "Produciuse un erro cambiando o correo electrónico. Quizais ese enderezo xa está en uso." + success: "Enviamos un correo electrónico a ese enderezo. Sigue as instrucións de confirmación." + change_avatar: + title: "Cambia a foto do perfil" + gravatar: "Gravatar, baseado en" + gravatar_title: "Cambia o avatar no sitio web de Gravatar" + refresh_gravatar_title: "Actualiza o teu Gravatar" + letter_based: "Imaxe do perfil asignada polo sistema" + uploaded_avatar: "Imaxe personalizada" + uploaded_avatar_empty: "Engadir unha imaxe personalizada" + upload_title: "Envía a túa imaxe" + upload_picture: "Enviar imaxe" + image_is_not_a_square: "Aviso: recortamos a túa imaxe; a largura e a altura eran distintas." + cache_notice: "Cambiaches correctamente a túa imaxe do perfil pero quizais tarde un chisco en aparecer debido á xestión da caché do navegador." + change_profile_background: + title: "Fondo do perfil" + instructions: "Os fondos dos perfís centraranse e terán unha largura predeterminada de 850px." + change_card_background: + title: "Fondo das fichas dos usuarios" + instructions: "As imaxes dos fondos centraranse e terán unha largura predeterminada de 590px." + email: + title: "Correo electrónico" + instructions: "Non se verá nunca en público" + ok: "Enviarémosche un correo electrónico para confirmar" + invalid: "Introduce un enderezo de correo electrónico correcto" + authenticated: "O teu enderezo de correo electrónico foi autenticado por {{provider}}" + frequency_immediately: "Enviarémosche un correo-e axiña se non liches sobre o que che estamos a enviar." + frequency: + one: "Só che eviaremos un correo-e se non te vimos no último minuto." + other: "Só che eviaremos un correo-e se non te vimos nos últimos {{count}} minutos." + name: + title: "Nome" + instructions: "Nome completo (opcional)" + instructions_required: "Nome completo" + too_short: "O nome é curto de mais" + ok: "O nome parece correcto" + username: + title: "Nome do usuario" + instructions: "Único, sen espazos, curto" + short_instructions: "A xente pode mencionarte como @{{username}}" + available: "O nome de usuario está dispoñíbel" + global_match: "O correo electrónico correspóndese co nome do usuario rexistrado" + global_mismatch: "Xa rexistrado. Tentar {{suggestion}}?" + not_available: "Non dispoñíbel. Tentar {{suggestion}}?" + too_short: "O nome do usuario é curto de máis" + too_long: "O nome do usuario é longo de máis" + checking: "Comprobando a dispoñibilidade do nome do usuario..." + enter_email: 'Atopouse o nome do usuario; introduce o correo electrónico' + prefilled: "O correo electrónico coincide co nome do usuario rexistrado" + locale: + title: "Idioma da interface" + instructions: "Idioma da interface do usuario. Cambiará cando actualices a páxina." + default: "(predeterminado)" + password_confirmation: + title: "O contrasinal outra vez" + last_posted: "Última publicación" + last_emailed: "Últimos envíos por correo-e" + last_seen: "Visto" + created: "Inscrito" + log_out: "Saír da sesión" + location: "Localización" + card_badge: + title: "Insignia na ficha do usuario" + website: "Sitio web" + email_settings: "Correo electrónico" + like_notification_frequency: + title: "Notificar cando reciba gústames" + always: "Sempre" + first_time_and_daily: "A primeira vez que unha publicación reciba un gústame e diariamente" + first_time: "A primeira vez que unha publicación lle gusta a alguén" + never: "Nunca" + email_previous_replies: + title: "Incluír as respostas previas no final dos correos electrónicos" + unless_emailed: "excepto os enviados anteriormente" + always: "sempre" + never: "nunca" + email_digests: + title: "Cando non veña por aquí, enviarme un compendio das novidade por correo-e:" + every_30_minutes: "cada 30 minutos" + every_hour: "cada hora" + daily: "diariamente" + every_three_days: "cada tres días" + weekly: "semanalmente" + every_two_weeks: "cada dúas semanas" + email_in_reply_to: "Incluír nos correos un extracto das respostas á publicación" + email_direct: "Enviarme un correo electrónico cando alguén me cite, responda a unha das miñas publicacións, mencione o meu @nome_do_usuario ou me convide a un tema." + email_private_messages: "Enviar correo electrónico cando alguén me mande unha mensaxe" + email_always: "Enviádeme notificación por correo-e incluso cando estea activo no sitio" + other_settings: "Outro" + categories_settings: "Categorías" + new_topic_duration: + label: "Considerar novos temas cando" + not_viewed: "Aínda non os vin" + last_here: "creados desde a última vez que estiven aquí" + after_1_day: "creados no último día" + after_2_days: "creados nos últimos 2 días" + after_1_week: "creados na última semana" + after_2_weeks: "creados nas última 2 semanas" + auto_track_topics: "Facer seguimento automático dos temas nos que entro" + auto_track_options: + never: "nunca" + immediately: "inmediatamente" + after_30_seconds: "despois de 30 segundos " + after_1_minute: "despois de 1 minuto" + after_2_minutes: "despois de 2 minutos" + after_3_minutes: "despois de 3 minutos" + after_4_minutes: "despois de 4 minutos" + after_5_minutes: "despois de 5 minutos" + after_10_minutes: "despois de 10 minutos" + invited: + search: "escribir para buscar convites..." + title: "Convites" + user: "Usuario convidado" + sent: "Enviado" + none: "Non hai convites pendentes de ver." + truncated: + one: "Amosando o primeiro convite." + other: "Amosando os primeiros {{count}} convites." + redeemed: "Convites utilizados" + redeemed_tab: "Utilizados" + redeemed_tab_with_count: "Utilizados ({{count}})" + redeemed_at: "Utilizados" + pending: "Convites pendentes" + pending_tab: "Pendente" + pending_tab_with_count: "Pendentes ({{count}})" + topics_entered: "Temas vistos" + posts_read_count: "Publicacións lidas" + expired: "Este convite caducou." + rescind: "Eliminar" + rescinded: "Convite eliminado" + reinvite: "Reenviar convite" + reinvited: "Convite reenviado" + time_read: "Tempo de lectura" + days_visited: "Días visitado" + account_age_days: "Tempo da conta en días" + create: "Enviar un convite" + generate_link: "Copiar a ligazón do convite" + generated_link_message: '

A ligazón do convite xerouse correctamente.

A ligazón do convite só é válida para este enderezo de correo-e: %{invitedEmail}

' + bulk_invite: + none: "Aínda non convidaches a ninguén. Podes enviar convites individuais ou en grupo se subes un ficheiro para convites múltiples." + text: "Convidar en grupo desde un ficheiro" + uploading: "Enviando..." + success: "O ficheiro enviouse correctamente, notificaráseche por mensaxe cando remate o proceso." + error: "Produciuse un erro ao subir «{{filename}}»: {{message}}" + password: + title: "Contrasinal" + too_short: "O teu contrasinal é demasiado curto." + common: "O contrasinal é demasiado habitual." + same_as_username: "O contrasinal é igual ao nome do usuario." + same_as_email: "O contrasinal é igual ao correo electrónico." + ok: "O contrasinal semella bo." + instructions: "Como mínimo %{count} caracteres." + summary: + title: "Resumo" + stats: "Estatísticas" + topic_count: "Temas creados" + post_count: "Publicacións creadas" + likes_given: "Gústames dados" + likes_received: "Gústames recibidos" + days_visited: "Días visitado" + posts_read_count: "Publicacións lidas" + top_replies: "Respostas destacadas" + top_topics: "Temas destacados" + top_badges: "Insignias principais" + more_topics: "Máis temas" + more_replies: "Máis respostas" + more_badges: "Máis insignias" + associated_accounts: "Accesos" + ip_address: + title: "Último enderezo IP" + registration_ip_address: + title: "Rexistro de enderezos IP" + avatar: + title: "Imaxe do perfil" + header_title: "perfil, mensaxes, marcadores e preferencias" + title: + title: "Título" + filters: + all: "Todas" + stream: + posted_by: "Publicado por" + sent_by: "Enviado por" + private_message: "mensaxe" + the_topic: "o tema" + loading: "Cargando..." + errors: + prev_page: "ao tentar cargar" + reasons: + network: "Erro de rede" + server: "Erro do servidor" + forbidden: "Acceso denegado" + unknown: "Erro" + not_found: "Páxina non atopada" + desc: + network: "Por favor, comproba a conexión." + network_fixed: "Parece que xa estamos de volta." + server: "Código do erro: {{status}}" + forbidden: "Non tes permiso para ver isto" + not_found: "Vaites, o aplicativo tentou cargar unha URL inexistente." + unknown: "Algo foi mal." + buttons: + back: "Volver" + again: "Tentar de novo" + fixed: "Cargar páxina" + close: "Pechar" + assets_changed_confirm: "Este sitio acaba de actualizarse. Queres recargar a páxina para ter a última versión?" + logout: "Fuches desconectado." + refresh: "Actualizar" + read_only_mode: + enabled: "Este sitio está en modo só-lectura. Continúe navegando pero responder, gustar e outras accións estarán desactivadas polo de agora." + login_disabled: "Cando o sitio está no modo de só-lectura, desactívase o inicio de sesión." + logout_disabled: "O peche de sesión desactívase mentres o sitio está en modo de só lectura." + too_few_topics_and_posts_notice: "Comecemos a discusión! Hai actualmente %{currentTopics} / %{requiredTopics} temas e %{currentPosts} / %{requiredPosts} publicacións. Os novos visitantes precisan algunhas conversas par ler e participar." + too_few_topics_notice: "Comecemos a discusión! Hai actualmente %{currentTopics} / %{requiredTopics} temas. Os novos visitantes precisan algunhas conversas par ler e participar." + too_few_posts_notice: "Comecemos a discusión! Hai actualmente %{currentPosts} / %{requiredPosts} publicacións. Os novos visitantes precisan algunhas conversas par ler e participar." + logs_error_rate_exceeded_notice: "%{timestamp}: A taxa actual de erros %{rate} errors/%{duration} excedeu o límite do sitio de %{siteSettingLimit} erros/%{duration}." + learn_more: "saber máis..." + year: 'ano' + year_desc: 'temas creados nos últimos 365 días' + month: 'mes' + month_desc: 'temas creados nos últimos 30 días' + week: 'semana' + week_desc: 'temas creados nos últimos 7 días' + day: 'día' + first_post: Publicación inicial + mute: Silenciar + unmute: Non silenciar + last_post: Última publicación + last_reply_lowercase: última resposta + replies_lowercase: + one: resposta + other: respostas + signup_cta: + sign_up: "Rexistrarse" + hide_session: "Lembrarmo mañá" + hide_forever: "non grazas" + hidden_for_session: "De acordo, preguntareicho mañá. Tamén podes usar «Iniciar sesión» para crear unha conta." + intro: "Ei! :heart_eyes: Semella que gozas coa discusión pero non abriches ningunha conta." + value_prop: "Cando creas unha conta, lembramos exactamente o que liches. Deste xeito sempre volves exactamente onde o deixaches. Podes recibir notificación aquí e vía correo electrónico sempre que se fagan publicacións. E podes darlle a Gústame nas publicacións para compartir o cariño. :heartbeat: " + summary: + enabled_description: "Estás vendo un resumo deste tema: as publicacións máis interesantes determinadas pola comunidade" + description: "Hai {{replyCount}} respostas." + description_time: "Hai {{replyCount}} respostas cun tempo estimado de lectura de {{readingTime}} minutos." + enable: 'Resumir este tema' + disable: 'Amosar todas as publicacións' + deleted_filter: + enabled_description: "Este tema contén publicacións eliminadas, que se ocultaron." + disabled_description: "Móstranse as publicacións eliminadas do tema." + enable: "Ocultar publicacións eliminadas" + disable: "Amosar as publicacións eliminadas" + private_message_info: + title: "Mensaxe" + invite: "Convidar a outros..." + remove_allowed_user: "Confirmas a eliminación de {{name}} desta mensaxe?" + email: 'Correo electrónico' + username: 'Nome do usuario' + last_seen: 'Visto' + created: 'Creado' + created_lowercase: 'creado' + trust_level: 'Nivel de confianza' + search_hint: 'nome do usuario, correo-e ou enderezo IP' + create_account: + title: "Crear unha conta nova" + failed: "Algo foi mal, quizais este correo electrónico xa está rexistrado, tenta coa ligazón de «Esquecín o contrasinal»." + forgot_password: + title: "Contrasinal restabelecido" + action: "Esquecín o contrasinal" + invite: "Introduce o nome do usuario ou correo electrónico, e enviaráseche un correo para restabelecer o contrasinal" + reset: "Restabelecer contrasinal" + complete_username: "Se unha conta corresponde ao nome de usuario %{username}, deberas recibir en breve un correo-e coas instrucións sobre como restabelecer o teu contrasinal." + complete_email: "Se unha conta coincide con %{email}, deberías recibir en breve un correo-e con instrucións sobre como restabelecer o teu contrasinal." + complete_username_found: "Atopamos unha conta co mesmo nome de usuario %{username}, deberas recibir en breve un correo-e coas instrucións sobre como restabelecer o teu contrasinal." + complete_email_found: "Atopamos unha conta que coincide con %{email}, deberas recibir en breve unha mensaxe coas instrucións sobre como restabelecer o contrasinal." + complete_username_not_found: "Ningunha conta coincide co nome do usuario %{username}" + complete_email_not_found: "Ningunha conta coincide co %{email}" + login: + title: "Iniciar sesión" + username: "Usuario" + password: "Contrasinal" + email_placeholder: "correo electrónico ou nome de usuario" + caps_lock_warning: " Bloqueo de maiúsculas activado" + error: "Erro descoñecido" + rate_limit: "Por favor, agarda antes de tentalo outra vez." + blank_username_or_password: "Introduce o teu correo electrónico ou nome de usuario e o contrasinal." + reset_password: 'Restabelecer contrasinal' + logging_in: "Iniciando sesión..." + or: "ou" + authenticating: "Autenticando..." + awaiting_confirmation: "A túa conta está pendente de se activar, emprega a ligazón de contrasinal esquecido para emitir outro correo de activación." + awaiting_approval: "A túa conta non foi aínda aprobada polos membros do equipo. Enviaráseche unha mensaxe cando así for." + requires_invite: "Sentímolo pero o acceso a este foro é unicamente por convite." + not_activated: "Non podes acceder aínda. Antes debemos enviarche unha mensaxe a {{sentTo}}. Por favor, sigue as instrucións desta mensaxe para activar a túa conta." + not_allowed_from_ip_address: "Non podes acceder desde este enderezo IP." + admin_not_allowed_from_ip_address: "Non podes acceder como administrador desde este enderezo IP." + resend_activation_email: "Preme aquí para enviar outro correo de activación." + sent_activation_email_again: "Enviamos outro correo-e de activación a {{currentEmail}}. Pode tardar uns minutos en chegar; asegúrate de revisar o cartafol do spam." + to_continue: "Por favor, inicia sesión" + preferences: "Precisas iniciar sesión para cambiar as túas preferencias de usuario." + forgot: "Non lembro os detalles da miña conta" + google: + title: "co Google" + message: "Autenticación mediante Google (asegúrate de ter desactivado o bloqueador de xanelas emerxentes)" + google_oauth2: + title: "co Google" + message: "Autenticación mediante Google (asegúrate de ter desactivado o bloqueador de xanelas emerxentes)" + twitter: + title: "co Twitter" + message: "Autenticación mediante Twitter (asegúrate de ter desactivado o bloqueador de xanelas emerxentes)" + instagram: + title: "con Instagram" + message: "Autenticación con Instagram (asegúrate que os bloqueadores de publicidade estean desactivados)" + facebook: + title: "co Facebook" + message: "Autenticación mediante Facebook (asegúrate de ter desactivado o bloqueador de xanelas emerxentes)" + yahoo: + title: "co Yahoo" + message: "Autenticación mediante Yahoo (asegúrate de ter desactivado o bloqueador de xanelas emerxentes)" + github: + title: "co GitHub" + message: "Autenticación mediante Github (asegúrate de ter desactivado o bloqueador de xanelas emerxentes)" + apple_international: "Apple/Internacional" + google: "Google" + twitter: "Twitter" + emoji_one: "Emoji One" + shortcut_modifier_key: + shift: 'Maiús.' + ctrl: 'Ctrl' + alt: 'Alt' + composer: + emoji: "Emoji :)" + more_emoji: "máis..." + options: "Opcións" + whisper: "bisbar" + add_warning: "Este é un aviso oficial." + toggle_whisper: "Cambiar Bisbar" + posting_not_on_topic: "A que tema queres responder?" + saving_draft_tip: "gardando..." + saved_draft_tip: "gardado" + saved_local_draft_tip: "gardado localmente" + similar_topics: "O teu tema é semellante a..." + drafts_offline: "borradores sen conexión" + group_mentioned: "Usando {{group}}, vas enviar a notificación a {{count}} persoas." + error: + title_missing: "O título é obrigatorio" + title_too_short: "O título debe ter alomenos {{min}} caracteres" + title_too_long: "O título non debe ter máis de {{max}} caracteres" + post_missing: "A publicación non pode estar baleira" + post_length: "A publicación debe ter alomenos {{min}} caracteres" + try_like: 'Probaches o botón ?' + category_missing: "Debes seleccionar unha categoría" + save_edit: "Gardar a edición" + reply_original: "Responder no tema orixinal" + reply_here: "Responder aquí" + reply: "Responder" + cancel: "Cancelar" + create_topic: "Crear tema" + create_pm: "Mensaxe" + title: "Ou preme Ctrl+Intro" + users_placeholder: "Engadir un usuario" + title_placeholder: "Sobre que trata a discusión nunha soa frase?" + edit_reason_placeholder: "por que estás editando?" + show_edit_reason: "(engadir unha razón para editar)" + reply_placeholder: "Escribe aquí. Usa Markdown, BBCode ou HTML para formatar. Arrastra ou pega imaxes." + view_new_post: "Ver a nova publicación." + saving: "Gardando" + saved: "Gardado!" + saved_draft: "A publicación do borrador está en proceso. Selecciona continuar." + uploading: "Enviando..." + show_preview: 'amosar visualización »' + hide_preview: '« ocultar previsualización' + quote_post_title: "Citar a publicación enteira" + bold_title: "Grosa" + bold_text: "Texto groso" + italic_title: "Resalte" + italic_text: "texto resaltado" + link_title: "Hiperligazón" + link_description: "introducir a descrición da ligazón aquí" + link_dialog_title: "Inserir hiperligazón" + link_optional_text: "título opcional" + link_placeholder: "http://exemplo.com \"texto opcional\"" + quote_title: "Citación" + quote_text: "Citación" + code_title: "Texto preformatado" + code_text: "Texto preformatado cun sangrado de 4 espazos" + upload_title: "Enviar" + upload_description: "introducir a descrición do envío aquí" + olist_title: "Lista numerada" + ulist_title: "Lista con símbolos" + list_item: "Elemento da lista" + heading_title: "Cabeceira" + heading_text: "Cabeceira" + hr_title: "Regra horizontal" + help: "Axuda para edición con Markdown" + toggler: "agochar ou amosar o panel de composición" + modal_ok: "De acordo" + modal_cancel: "Cancelar" + cant_send_pm: "Sentímolo pero non podes enviar unha mensaxe a %{username}." + admin_options_title: "Axustes do equipo para este tema" + auto_close: + label: "Tempo para o peche automático deste tema:" + error: "Introduce un valor correcto." + based_on_last_post: "Non pechar até que a última publicación do tema teña alomenos este tempo." + all: + examples: 'Introducir o número de horas (24), hora absoluta (17:30) ou a marca data/hora (2013-11-22 14:00).' + limited: + units: "(# de horas)" + examples: 'Introducir o número de horas (24).' + notifications: + title: "notificacións das mencións ao teu @nome, respostas ás túas publicacións e temas, mensaxes, etc" + none: "Non é posíbel cargar as notificacións neste intre" + more: "ver notifiacións anteriores" + total_flagged: "total de publicacións denunciadas" + mentioned: "

{{username}} {{description}}

" + group_mentioned: "

{{username}} {{description}}

" + quoted: "

{{username}} {{description}}

" + replied: "

{{username}} {{description}}

" + posted: "

{{username}} {{description}}

" + edited: "

{{username}} {{description}}

" + liked: "

{{username}} {{description}}

" + liked_2: "

{{username}}, {{username2}} {{description}}

" + liked_many: + one: "

{{username}}, {{username2}} and 1 máis {{description}}

" + other: "

{{username}}, {{username2}} e {{count}} máis {{description}}

" + private_message: "

{{username}} {{description}}

" + invited_to_private_message: "

{{username}} {{description}}

" + invited_to_topic: "

{{username}} {{description}}

" + invitee_accepted: "

{{username}} aceptou o teu convite

" + moved_post: "

{{username}} moveu {{description}}

" + linked: "

{{username}} {{description}}

" + granted_badge: "

Obtiveches «{{description}}»

" + group_message_summary: + one: "

{{count}} mensaxe na caixa do correo de {{group_name}}

" + other: "

{{count}} mensaxes na caixa do correo de {{group_name}}

" + alt: + mentioned: "Mencionado por" + quoted: "Citado por" + replied: "Respondido" + posted: "Publicado por" + edited: "A túa publicación ediotuna" + liked: "Gustoulles a túa publicación" + private_message: "Mensaxe privada de" + invited_to_private_message: "Convidado a unha mensaxe privada de" + invited_to_topic: "Convidado a un tema de" + invitee_accepted: "Convite aceptado por" + moved_post: "A túa publicación foi movida por" + linked: "Ligazón á túa publicación" + granted_badge: "Insignias concedidas" + group_message_summary: "Mensaxes na caixa do grupo" + popup: + mentioned: '{{username}} mencionoute en "{{topic}}" - {{site_title}}' + group_mentioned: '{{username}} mencionoute en "{{topic}}" - {{site_title}}' + quoted: '{{username}} citoute en "{{topic}}" - {{site_title}}' + replied: '{{username}} respondeute en "{{topic}}" - {{site_title}}' + posted: '{{username}} publicou en "{{topic}}" - {{site_title}}' + private_message: '{{username}} enviouche unha mensaxe privada en "{{topic}}" - {{site_title}}' + linked: '{{username}} ligou a túa publicación desde "{{topic}}" - {{site_title}}' + upload_selector: + title: "Engadir unha imaxe" + title_with_attachments: "Engadir imaxe ou ficheiro" + from_my_computer: "Desde o meu dispositivo" + from_the_web: "Desde a web" + remote_tip: "ligazón á imaxe" + remote_tip_with_attachments: "ligazón á imaxe ou ficheiro {{authorized_extensions}}" + local_tip: "seleccionar imaxes do teu dispositivo" + local_tip_with_attachments: "selecciona imaxes ou ficheiros do teu dispositivo {{authorized_extensions}}" + hint: "(tamén podes arrastrar e soltar no editor para envialos)" + hint_for_supported_browsers: "tamén podes arrastrar e soltar ou pegar imaxes no editor" + uploading: "Enviando" + select_file: "Seleccionar ficheiro" + image_link: "ligazón onde levará a túa imaxe" + search: + sort_by: "Ordenar por" + relevance: "Relevancia" + latest_post: "Últimas publicacións" + most_viewed: "Máis vistos" + most_liked: "Con máis Gústames" + select_all: "Seleccionar todo" + clear_all: "Borrar todo" + result_count: + one: "Un resultado para \"{{term}}\"" + other: "{{count}} resultados para \"{{term}}\"" + title: "buscar temas, publicacións, usuarios ou categorías" + no_results: "Non se atoparon resultados." + no_more_results: "Non se atoparon máis resultados." + search_help: Buscar axuda + searching: "Buscando..." + post_format: "#{{post_number}} de {{username}}" + context: + user: "Buscar publicacións de @{{username}}" + category: "Buscar na categoría \"{{category}}\"" + topic: "Buscar neste tema" + private_messages: "Buscar mensaxes" + hamburger_menu: "ir a outra lista de temas ou categoría" + new_item: "novo" + go_back: 'volver' + not_logged_in_user: 'páxina do usuario cun resumo das actividades e preferencias actuais' + current_user: 'ir á túa páxina do usuario' + topics: + bulk: + unlist_topics: "Retirar temas da lista" + reset_read: "Restabelecer Lidos" + delete: "Eliminar temas" + dismiss: "Desbotar" + dismiss_read: "Desbotar os non lidos" + dismiss_button: "Desbotar..." + dismiss_tooltip: "Desbotar só as publicacións novas ou deixar de seguir temas" + also_dismiss_topics: "Deter o seguimento destes temas para que non se me amosen como non lidos" + dismiss_new: "Desbotar novas" + toggle: "cambiar a selección en bloque dos temas" + actions: "Accións en bloque" + change_category: "Cambiar categoría" + close_topics: "Pechar temas" + archive_topics: "Arquivar temas" + notification_level: "Cambiar o nivel de notificación" + choose_new_category: "Seleccionar a nova categoría dos temas:" + selected: + one: "Seleccionaches un tema." + other: "Seleccionaches {{count}} temas." + none: + unread: "Non tes temas sen ler." + new: "Non tes novos temas." + read: "Aínda non liches ningún tema." + posted: "Aínda non publicaches en ningún tema." + latest: "Non hai últimos temas. Triste." + hot: "Non hai temas quentes." + bookmarks: "Aínda non marcaches este tema." + category: "Non hai temas en {{category}}." + top: "Non hai temas destacados." + search: "Non hai resultados da busca." + educate: + new: '

Aquí aparecen os teus temas novos.

De xeito predeterminado, os temas considéranse novos e amosan un indicador novo se se crearon nos últimos dous días.

Podes ir ás preferencias para cambiar este axuste.

' + unread: '

Os teus temas sen ler aparecen aquí.

De xeito predeterminado, os temas considéranse sen ler e amosarase o número dos non lidos. 1 se ti:

  • Creaches o tema
  • Respondiches o tema
  • Liches o tema durante máis de catro minutos

Ou estabeleciches o tema para ser Seguido ou Visto no control de notificacións na banda inferior de cada tema.

Vai ás túas preferencias se queres cambiar isto.

' + bottom: + latest: "Non hai máis últimos temas." + hot: "Non hai máis temas quentes." + posted: "Non hai máis temas publicados." + read: "Non hai máis temas lidos." + new: "Non hai máis temas novos." + unread: "Non hai máis temas sen ler." + category: "Non hai máis temas en {{category}}." + top: "Non hai máis temas destacados." + bookmarks: "Non hai máis temas marcados." + search: "Non hai máis resultados da busca." + topic: + unsubscribe: + stop_notifications: "Agora recibirás menos notificacións sobre {{title}}" + change_notification_state: "O estado actual das túas notificacións é" + filter_to: "{{post_count}} publicacións no tema" + create: 'Novo tema' + create_long: 'Crear un novo tema' + private_message: 'Iniciar unha mensaxe' + archive_message: + help: 'Mover mensaxes ao arquivo' + title: 'Arquivo' + move_to_inbox: + title: 'Mover á caixa de entrada' + help: 'Mover mensaxes á caixa de entrada' + list: 'Temas' + new: 'novo tema' + unread: 'sen ler' + new_topics: + one: 'Un tema novo' + other: '{{count}} temas novos' + unread_topics: + one: 'Un tema sen ler' + other: '{{count}} temas sen ler' + title: 'Tema' + invalid_access: + title: "O tema é privado" + description: "Sentímolo pero non tes acceso a este tema." + login_required: "Debes iniciar sesión para ver este tema." + server_error: + title: "A carga do tema fallou" + description: "Sentímolo pero non podemos cargar este tema, posibelmente debido a problemas de conexión. Ténato de novo e se o problema continúa fáinolo saber." + not_found: + title: "Non foi posíbel atopar o tema" + description: "Sentímolo pero non foi posíbel atopar este tema. Quizais foi eliminado por un moderador." + total_unread_posts: + one: "Tes unha publicación sen ler neste tema" + other: "Tes {{count}} publicacións sen ler neste tema" + unread_posts: + one: "Tes unha publicación antiga sen ler neste tema" + other: "Tes {{count}} publicacións antigas sen ler neste tema" + new_posts: + one: "hai unha nova publicación neste tema desde a túa última lectura" + other: "hai {{count}} novas publicacións neste tema desde a túa última lectura" + likes: + one: "hai un gústame neste tema" + other: "hai {{count}} gústames neste tema" + back_to_list: "Volver á lista de temas" + options: "Opcións de temas" + show_links: "amosar as ligazóns cara este tema" + toggle_information: "cambiar detalles do tema" + read_more_in_category: "Queres ler máis? explora outros temas en {{catLink}} ou {{latestLink}}." + read_more: "Queres ler máis? {{catLink}} ou {{latestLink}}." + read_more_MF: "Hai { UNREAD, plural, =0 {} one { 1 sen ler } other { # sen ler } } { NEW, plural, =0 {} one { {BOTH, select, true{e} false { } other{}} 1 novo topic} other { {BOTH, select, true{e} false { } other{}} # novos topics} } restantes, ou {CATEGORY, select, true {explora outros temas en {catLink}} false {{latestLink}} other {}}" + browse_all_categories: Explorar todas as categorías + view_latest_topics: ver últimos temas + suggest_create_topic: Porque non crear un tema? + jump_reply_up: ir a unha resposta anterior + jump_reply_down: ir a unha resposta posterior + deleted: "Eliminouse o tema" + auto_close_notice: "Este tema pechará automaticamente en %{timeLeft}." + auto_close_notice_based_on_last_post: "Este tema pechará %{duration} despois da última resposta." + auto_close_title: 'Axustes do peche automático' + auto_close_save: "Gardar" + auto_close_remove: "arriba" + auto_close_immediate: "A última publicación deste tema xa ten %{hours} horas. O tema pecharase inmediatamente." + progress: + title: progreso do tema + go_top: "principio" + go_bottom: "final" + go: "ir" + jump_bottom: "ir á última publicación" + jump_bottom_with_number: "ir á publicación %{post_number}" + total: publicacións totais + current: publicación actual + position: "publicación %{current} de %{total}" + notifications: + reasons: + '3_6': 'Recibirás notificacións porque estás vendo esta categoría.' + '3_5': 'Recibirás notificacións porque comezaches a ver este tema automaticamente.' + '3_2': 'Recibirás notificacións porque estás vendo este tema.' + '3_1': 'Recibirás notificacións por ser o creador deste tema.' + '3': 'Recibirás notificacións porque estás vendo este tema.' + '2_8': 'Recibirás notificacións porque segues esta categoría.' + '2_4': 'Recibirás notificacións porque publicaches unha resposta neste tema.' + '2_2': 'Recibirás notificacións porque segues este tema.' + '2': 'Recibirás notificacións porque liches este tema.' + '1_2': 'Notificarémosche se alguén menciona o teu @nome ou che responde.' + '1': 'Notificarémosche se alguén menciona o teu @nome ou che responde.' + '0_7': 'Estás ignorando todas as notificacións desta categoría.' + '0_2': 'Estás ignorando todas as notificacións deste tema.' + '0': 'Estás ignorando todas as notificacións deste tema.' + watching_pm: + title: "Ver" + description: "Recibirás notificacións de cada resposta a esta mensaxe e aparecerá o número de novas respostas." + watching: + title: "Ver" + description: "Notificaránseche as respostas recibidas neste tema e amosarase o número de novas respostas." + tracking_pm: + title: "Seguimento" + description: "Amosarase o número de novas respostas desta mensaxe. Notificaránseche as mencións ao teu @name ou cando alguén che responda." + tracking: + title: "Seguimento" + description: "Amosarase o número de novas respostas para este tema. Notificaránseche as mencións ao teu @name ou cando alguén che responda." + regular: + title: "Normal" + description: "Notificarémosche se alguén menciona o teu @nome ou che responde." + regular_pm: + title: "Normal" + description: "Notificarémosche se alguén menciona o teu @nome ou che responde." + muted_pm: + title: "Silenciado" + description: "Non recibirás ningunha notificación sobre esta mensaxe." + muted: + title: "Silenciado" + description: "Non se che notificará nada sobre este tema e non aparecerá no listado de últimos." + actions: + recover: "Recuperar tema" + delete: "Eliminar tema" + open: "Abrir tema" + close: "Pechar tema" + multi_select: "Seleccionar publicacións..." + auto_close: "Pechar automaticamente..." + pin: "Pegar tema..." + unpin: "Despegar tema..." + unarchive: "Desarquivar tema" + archive: "Arquivar tema" + invisible: "Retirar da lista" + visible: "Engadir á lista" + reset_read: "Restabelecer datos de lecturas" + feature: + pin: "Pegar tema" + unpin: "Despegar tema" + pin_globally: "Pegar tema globalmente" + make_banner: "Tema do báner" + remove_banner: "Eliminar o tema do báner" + reply: + title: 'Responder' + help: 'responder a este tema' + clear_pin: + title: "Borrar o estado Pegar" + help: "Borra o estado Pegado deste tema para que non apareza na banda superior da lista de temas." + share: + title: 'Compartir' + help: 'compartir unha ligazón a este tema' + flag_topic: + title: 'Denunciar' + help: 'denunciar privadamente este tema para revisalo ou enviar unha notificación privada sobre el' + success_message: 'Denunciaches o tema correctamente.' + feature_topic: + title: "Destacar este tema" + pin: "Facer que este tema apareza no alto da categoría {{categoryLink}} até" + confirm_pin: "Xa tes {{count}} temas pegados. Demasiados temas pegados pode resultar pesado para usuarios novos e anónimos. Confirmas que queres pegar outro tema nesta categoría?" + unpin: "Eliminar este tema da banda superior da categoría {{categoryLink}}." + unpin_until: "Retirar este tema do alto da {{categoryLink}} ou agardar até %{until}." + pin_note: "Os usuarios poden despegar o tema por si mesmos." + pin_validation: "Requírese unha data para pegar este tema." + not_pinned: "Non hai temas pegados en {{categoryLink}}." + already_pinned: + one: "Temas pegados actualmente en {{categoryLink}}: 1" + other: "Temas pegados actualmente en {{categoryLink}}: {{count}}" + pin_globally: "Facer que este tema apareza no alto de todas as listas de temas até" + confirm_pin_globally: "Xa tes {{count}} temas pegados globalmente. Demasiados temas pegados pode resultar pesado para usuarios novos e anónimos. Confirmas que queres pegar outro tema globalmente?" + unpin_globally: "Eliminar este tema da banda superior de todas as listas de temas." + unpin_globally_until: "Eliminar este tema do alto de todas as listas de temas ou agardar até %{until}." + global_pin_note: "Os usuarios poden despegar o tema por si mesmos." + not_pinned_globally: "Non hai temas pegados globalmente." + already_pinned_globally: + one: "Temas pegados globalmente neste intre: 1" + other: "Temas pegados globalmente neste intre: {{count}}" + make_banner: "Facer deste tema un báner que apareza na banda superior de todas as páxinas." + remove_banner: "Eliminar o báner que aparece na banda superior de todas as páxinas." + banner_note: "Os usuarios poden desbotar un báner se o pechan. Unicamente pode haber un tema que sexa un báner ao mesmo tempo." + no_banner_exists: "Non hai tema para o báner." + banner_exists: "Hai actualmente un tema para o báner." + inviting: "Convidando..." + automatically_add_to_groups_optional: "Este convite tamén inclúe o acceso a estes grupos: (opcional, só administradores)" + automatically_add_to_groups_required: "Este convite tamén inclúe o acceso a estes grupos: (Obrigatorio, só administradores)" + invite_private: + title: 'Convidar á mensaxe' + email_or_username: "Nome do usuario ou correo-e do convidado" + email_or_username_placeholder: "correo electrónico e nome do usuario" + action: "Convidar" + success: "Convidamos este usuario a participar nesta mensaxe." + error: "Sentímolo pero houbo un erro convidando este usuario." + group_name: "nome do grupo" + controls: "Controis do tema" + invite_reply: + title: 'Convidar' + username_placeholder: "nome do usuario" + action: 'Enviar convite' + help: 'convidar a outros a este tema por correo electrónico ou notificacións' + to_forum: "Enviaremos un correo electrónico permitindo ao teu amigo que se una inmediatamente ao premer nunha ligazón. Non require iniciar sesión." + sso_enabled: "Introduce o nome do usuario da persoa que desexas convidar a este tema." + to_topic_blank: "Introduce o nome do usuario ou o correo electrónico da persoa que desexas convidar a este tema." + to_topic_email: "Introduciches un enderezo de correo-e. Enviarémosche un convite que permitirá os teus amigos responder inmediatamente a este tema." + to_topic_username: "Introduciches un nome de usuario. Enviarémoslle unha notificación cunha ligazón convidándoo a este tema." + to_username: "Introduce o nome do usuario da persoa que desexas convidar. Enviarémoslle unha notificación cunha ligazón convidándoa a este tema." + email_placeholder: 'name@example.com' + success_email: "Enviamos un convite a {{emailOrUsername}}. Notificarémosche cando utilice a invitación. Mira a lapela de convites na túa páxina de usuario para facer un seguimento das túas invitacións." + success_username: "Convidamos este usuario a participar neste tema." + error: "Sentímolo, non foi posíbel convidar esta persoa. Quizais xa foi convidada? (os convites teñen un límite)" + login_reply: 'Inicia sesión para responder' + filters: + n_posts: + one: "Unha publicación" + other: "{{count}} publicacións" + cancel: "Eliminar filtro" + split_topic: + title: "Mover ao tema novo" + action: "mover ao tema novo" + topic_name: "Nome do tema novo" + error: "Produciuse un erro movendo as publicacións ao novo tema." + instructions: + one: "Vas crear un novo tema e enchelo coa publicación que seleccionaches." + other: "Vas crear un novo tema e enchelo coas {{count}} publicacións que seleccionaches." + merge_topic: + title: "Mover a un tema existente" + action: "mover a un tema existente" + error: "Produciuse un erro movendo publicacións nese tema." + instructions: + one: "Selecciona o tema ao que queres mover esta publicación." + other: "Selecciona o tema ao que queres mover estas {{count}} publicacións." + change_owner: + title: "Cambiar propietario das publicacións" + action: "cambiar propiedade" + error: "Produciuse un erro cambiando a propiedade das publicacións." + label: "Novo propietario das publicacións" + placeholder: "nome do usuario do novo propietario" + instructions: + one: "Selecciona o novo propietario da publicación de {{old_user}}." + other: "Selecciona o novo propietario das {{count}} publicacións de {{old_user}}." + instructions_warn: "Decátate que ningunha notificación sobre esta publicación se transferirá retroactivamente ao novo usuario.
Aviso: actualmente, non se transfire ao novo usuario ningún dato relacionado cunha publicación. Usa isto con tino." + change_timestamp: + title: "Cambiar a marca data/hora" + action: "cambiar a marca data/hora" + invalid_timestamp: "A marca data/hora non pode ser no futuro." + error: "Produciuse un erro cambiando a marca data/hora do tema." + instructions: "Selecciona a marca data/hora para o tema. As publicacións do tema actualizaranse para ter a mesma diferenza de tempo." + multi_select: + select: 'seleccionar' + selected: 'seleccionados ({{count}})' + select_replies: 'seleccionar +respostas' + delete: eliminar seleccionados + cancel: 'cancelar selección ' + select_all: seleccionar todo + deselect_all: deseleccionar todo + description: + one: Seleccionaches unha publicación. + other: Seleccionaches {{count}} publicacións. + post: + reply: " {{replyAvatar}} {{usernameLink}}" + reply_topic: " {{link}}" + quote_reply: "citar resposta" + edit: "Editando {{link}} {{replyAvatar}} {{username}}" + edit_reason: "Razón:" + post_number: "publicación {{number}}" + last_edited_on: "última edición da publicación" + reply_as_new_topic: "Responder como tema ligado" + continue_discussion: "Continuar a discusión de {{postLink}}:" + follow_quote: "ir á publicación citada" + show_full: "Amosar a publicación completa" + show_hidden: 'Ver o contido oculto.' + deleted_by_author: + one: "(as publicacións retiradas polo autor serán automaticamente eliminadas en %{count} hora, excepto que fosen denunciadas)" + other: "(as publicacións retiradas polo autor serán automaticamente eliminadas en %{count} horas, excepto que fosen denunciadas)" + expand_collapse: "ampliar/reducir" + gap: + one: "ver unha resposta oculta" + other: "ver {{count}} respostas ocultas" + more_links: "{{count}} máis..." + unread: "Publicación sen ler" + has_replies: + one: "{{count}} resposta" + other: "{{count}} respostas" + has_likes: + one: "{{count}} gústame" + other: "{{count}} gústames" + has_likes_title: + one: "A unha persoa gustoulle esta publicación" + other: "A {{count}} persoas gustoulles esta publicación" + has_likes_title_only_you: "gustouche esta publicación" + has_likes_title_you: + one: "A ti e a outro máis gustouvos esta publicación" + other: "A ti e a {{count}} persoas máis gustouvos esta publicación" + errors: + create: "Sentímolo pero produciuse un erro creando a publicación. Téntao de novo." + edit: "Sentímolo pero produciuse un erro editando a publicación. Téntao de novo." + upload: "Sentímolo pero produciuse un erro enviando a publicación. Téntao de novo." + attachment_too_large: "Sentímolo pero o ficheiro é demasiado grande (o tamaño máximo son {{max_size_kb}}kb)." + file_too_large: "Sentímolo pero o ficheiro é demasiado grande (o tamaño máximo son {{max_size_kb}}kb)" + too_many_uploads: "Sentímolo pero só podes enviar un ficheiro de cada vez." + too_many_dragged_and_dropped_files: "Sentímolo pero só podes arrastrar e soltar até 10 ficheiros dunha vez." + upload_not_authorized: "Sentímolo pero o ficheiro que tentas enviar non está autorizado (extensións autorizadas: {{authorized_extensions}})." + image_upload_not_allowed_for_new_user: "Sentímolo pero os novos usuarios non poden subir imaxes." + attachment_upload_not_allowed_for_new_user: "Sentímolo pero os novos usuarios non poden subir anexos." + attachment_download_requires_login: "Sentímolo pero debes iniciar sesión para descargar anexos." + abandon: + confirm: "Confirmas o abandono da túa publicación?" + no_value: "Non, seguir" + yes_value: "Si, abandonar" + via_email: "esta publicación chegou por correo-e" + whisper: "este é un bisbar privado para moderadores" + wiki: + about: "esta publicación é unha wiki" + archetypes: + save: 'Gardar opcións' + controls: + reply: "escribir unha resposta a esta publicación" + like: "gústame esta publicación" + has_liked: "gustouche esta publicación" + undo_like: "desfacer o gústame" + edit: "editar publicación" + edit_anonymous: "Sentímolo pero debes iniciar sesión para editar esta publicación." + flag: "denunciar privadamente esta publicación ou enviar unha notificación privada sobre ela" + delete: "eliminar publicación" + undelete: "recuperar publicación" + share: "compartir ligazón á publicación" + more: "Máis" + delete_replies: + confirm: + one: "Tamén desexas eliminar a resposta directa desta publicación?" + other: "Tamén desexas eliminar as {{count}} respostas directas desta publicación?" + yes_value: "Si, eliminar tamén as respostas" + no_value: "Non, só esta publicación" + admin: "accións admin. nas publicacións" + wiki: "Crear wiki" + unwiki: "Eliminar wiki" + convert_to_moderator: "Engadir cor do Equipo" + revert_to_regular: "Eliminar cor do Equipo" + rebake: "Reconstruír HTML" + unhide: "Non ocultar" + change_owner: "Cambiar propietario" + actions: + flag: 'Denunciar' + defer_flags: + one: "Pospor denuncia" + other: "Pospor denuncias" + it_too: + off_topic: "Denuncialo tamén" + spam: "Denuncialo tamén" + inappropriate: "Denuncialo tamén" + custom_flag: "Denuncialo tamén" + bookmark: "Marcalo tamén" + like: "Gústame tamén" + vote: "Votalo tamén" + undo: + off_topic: "Desfacer denuncia" + spam: "Desfacer denuncia" + inappropriate: "Desfacer denuncia" + bookmark: "Desfacer marcador" + like: "Desfacer o gústame" + vote: "Desfacer voto" + people: + off_topic: "notificado como sen relación co tema" + spam: "denunciou isto como spam" + inappropriate: "denunciou isto como inapropiado" + notify_moderators: "moderadores notificados" + notify_user: "enviou unha mensaxe" + bookmark: "marcou isto" + like: "gustou disto" + vote: "votou por isto" + by_you: + off_topic: "Informaches disto como non relacionado" + spam: "Denunciaches isto como spam" + inappropriate: "Denunciaches isto como inapropiado" + notify_moderators: "Denunciaches isto para moderación" + notify_user: "Enviaches unha mensaxe a este usuario" + bookmark: "Marcaches esta publicación" + like: "Gustouche isto" + vote: "Votaches esta publicación" + by_you_and_others: + off_topic: + one: "Ti e outro máis informastes disto como non relacionado" + other: "Ti e {{count}} máis informastes disto como non relacionado" + spam: + one: "Ti e outro máis denunciastes isto como spam" + other: "Ti e {{count}} máis denunciastes isto com spam" + inappropriate: + one: "Ti e outro máis denunciastes isto como inapropiado" + other: "Ti e {{count}} máis denunciastes isto como inapropiado" + notify_moderators: + one: "Ti e outro máis denunciastes isto para moderación" + other: "Ti e {{count}} máis denunciastes isto para moderación" + notify_user: + one: "Ti e unha persoa máis enviastes unha mensaxe a este usuario" + other: "Ti e {{count}} persoas máis enviastes unha mensaxe a este usuario" + bookmark: + one: "Ti e outra persoa marcastes esta publicación" + other: "Ti e {{count}} persoas máis marcastes esta publicación" + like: + one: "A ti e a un máis gustouvos isto" + other: "A ti e a {{count}} máis gustouvos isto" + vote: + one: "Ti e outra persoa votastes esta publicación" + other: "Ti e {{count}} persoas máis votastes esta publicación" + by_others: + off_topic: + one: "Unha persoa marcou isto como non relacionado" + other: "{{count}} persoas informaron disto como non relacionado" + spam: + one: "Unha persoa marcou isto como spam" + other: "{{count}} persoas denunciaron isto como spam" + inappropriate: + one: "Unha persoa denunciou isto como inapropiado" + other: "{{count}} persoas denunciaron isto como inapropiado" + notify_moderators: + one: "Unha persoa denunciou isto para moderación" + other: "{{count}} persoas denunciaron isto para moderación" + notify_user: + one: "Unha persoa enviou unha mensaxe a este usuario" + other: "{{count}} persoas enviaron unha mensaxe a este usuario" + bookmark: + one: "Unha persoa marcou esta publicación" + other: "{{count}} persoas marcaron esta publicación" + like: + one: "A unha persoa gustoulle isto" + other: "A {{count}} persoas gustoulles isto" + vote: + one: "Unha persoa votou por esta publicación" + other: "{{count}} persoas votaron por esta publicación" + delete: + confirm: + one: "Confirmas a eliminación desta publicación?" + other: "Confirmas a eliminación de todas estas publicacións?" + revisions: + controls: + first: "Primeira revisión" + previous: "Revisión anterior" + next: "Revisión seguinte" + last: "Última revisión" + hide: "Ocultar revisión" + show: "Amosar revisión" + revert: "Reverter a esta revisión" + comparing_previous_to_current_out_of_total: "{{previous}} {{current}} / {{total}}" + displays: + inline: + title: "Amosar o resultado coas adicións e eliminacións inseridas" + button: ' HTML' + side_by_side: + title: "Amosar o resultado coas diferenzas comparadas" + button: ' HTML' + side_by_side_markdown: + title: "Amosar a fonte crúa coas diferenzas comparadas" + button: ' Crúa' + category: + can: 'podes… ' + none: '(sen categoría)' + all: 'Todas as categorías' + choose: 'Seleccionar unha categoría…' + edit: 'editar' + edit_long: "Editar" + view: 'Ver os Temas na Categoría' + general: 'Xeral' + settings: 'Axustes' + topic_template: "Modelo para o tema" + delete: 'Eliminar categoría' + create: 'Nova categoría' + create_long: 'Crear unha nova categoría' + save: 'Gardar categoría' + slug: '«Slug» da Categoría' + slug_placeholder: 'Inserir guións entre palabras para url (opcional) ' + creation_error: Produciuse un erro durante a creación desta categoría. + save_error: Produciuse un erro gardando a categoría. + name: "Nome da categoría" + description: "Descrición" + topic: "tema da categoría" + logo: "Logotipo da categoría" + background_image: "Imaxe do fondo da categoría" + badge_colors: "Cores das insignias" + background_color: "Cor do fondo" + foreground_color: "Cor do primeiro plano" + name_placeholder: "Dúas palabras como máximo" + color_placeholder: "Calquera cor web" + delete_confirm: "Confirmas a eliminación desta categoría?" + delete_error: "Produciuse un erro elimando esta categoría." + list: "Listar categorías" + no_description: "Engade unha descrición a esta categoría." + change_in_category_topic: "Editar a descrición" + already_used: 'Esta cor usouna outra categoría' + security: "Seguranza" + special_warning: "Aviso: esta categoría ten axustes predeterminados e as opcións de seguranza non se poden modificar. Se non queres usala, elimínaa no canto de reciclala." + images: "Imaxes" + auto_close_label: "Pechar automaticamente os temas despois de:" + auto_close_units: "horas" + email_in: "Personalizar enderezos de correos-e entrantes:" + email_in_allow_strangers: "Aceptar correos-e de usuarios anónimos sen contas" + email_in_disabled: "A publicación de novos temas vía correo-e está desactivada nos axustes do sitio. Para activala," + email_in_disabled_click: 'activar o axuste «email in».' + suppress_from_homepage: "Retirar esta categoría da páxina de inicio." + allow_badges_label: "Permitir adxudicar insignias nesta categoría" + edit_permissions: "Editar permisos" + add_permission: "Engadir permisos" + this_year: "este ano" + position: "posición" + default_position: "Posición predeterminada" + position_disabled: "As categorías amosaranse en orde de actividade. Para controlar a orde das categorías nas listas." + position_disabled_click: 'activar o axuste «fixed category positions».' + parent: "Categoría pai" + notifications: + watching: + title: "Ver" + description: "Verás automaticamente todos os novos temas destas categorías. Notificaránseche as novas publicacións de cada tema e amosaráseche o número de novas respostas." + tracking: + title: "Seguimento" + description: "Seguirás automaticamente todos os novos temas destas categorías. Notificaránseche ás mencións ao teu @name e as respostas que recibas, e amosaráseche o número de novas respostas." + regular: + title: "Normal" + description: "Notificarémosche se alguén menciona o teu @nome ou che responde." + muted: + title: "Silenciado" + description: "Non se che notificarán novos temas nestas categorías e non aparecerán nos Últimos." + flagging: + title: 'Grazas por axudar a manter a nosa comunidade.' + action: 'Denunciar publicación' + take_action: "Tomar medidas" + notify_action: 'Mensaxe' + delete_spammer: "Eliminar spammer" + delete_confirm: "Vas borrar %{posts} publicacións e %{topics} temas deste usuario, eliminar a súa conta, bloquear o rexistro desde os seus enderezos IP %{ip_address} e engadir o seu correo-e %{email} a unha lista de bloqueos permanentes. Confirmas que este usuario é un spammer?" + yes_delete_spammer: "Si, eliminar o spammer" + ip_address_missing: "(N/D)" + hidden_email_address: "(oculto)" + submit_tooltip: "Enviar a denuncia privada" + take_action_tooltip: "Alcanzar o limiar de denuncias inmediatamente no canto de agardar por máis denuncias da comunidade" + cant: "Sentímolo pero non podes denunciar esta publicación neste intre." + notify_staff: 'Notificar ao equipo privadamente' + formatted_name: + off_topic: "Non está relacionado" + inappropriate: "É inapropiado" + spam: "É spam" + custom_placeholder_notify_user: "Se específico, construtivo e sempre amábel." + custom_placeholder_notify_moderators: "Especifícanos sobre o que estás preocupado e proporciónanos ligazóns relevantes e exemplos cando sexa posíbel." + custom_message: + at_least: "introduce cando menos {{n}} caracteres" + more: "{{n}} restantes..." + left: "{{n}} restantes" + flagging_topic: + title: "Grazas por axudar a manter a nosa comunidade." + action: "Denunciar tema" + notify_action: "Mensaxe" + topic_map: + title: "Resumo do tema" + participants_title: "Publicadores frecuentes" + links_title: "Ligazóns populares" + links_shown: "amosar as {{totalLinks}} ligazóns..." + clicks: + one: "Un clic" + other: "%{count} clics" + topic_statuses: + warning: + help: "Este é un aviso oficial." + bookmarked: + help: "Marcaches este tema" + locked: + help: "Este tema está pechado, xa non acepta respostas" + archived: + help: "Este tema está arquivado; está conxelado e non se pode cambiar" + locked_and_archived: + help: "Este tema está pechado e arquivado; xa non acepta novas respostas e non se pode cambiar" + unpinned: + title: "Despegado" + help: "Este tema está despegado para vostede; presentarase na orde normal" + pinned_globally: + title: "Pegado globalmente" + help: "Este tema pegouse globalmente; presentarase na banda superior dos máis recentes e na súa categoría" + pinned: + title: "Pegado" + help: "Este tema pegouse globalmente; presentarase na banda superior da súa categoría" + invisible: + help: "Este tema non está listado. Non se presentará nas listas de temas e só estará accesíbel vía ligazón directa" + posts: "Publicacións" + posts_lowercase: "publicacións" + posts_long: "hai {{number}} publicacións neste tema" + posts_likes_MF: | + Este tema ten {count, plural, one {1 resposta} other {# respostas}} {ratio, select, + low {cun alto índice de gústames} + med {cun moi alto índice de gústames} + high {cun extremadamente alto índice de gústames} + other {}} + original_post: "Publicación orixinal" + views: "Vistas" + views_lowercase: + one: "vista" + other: "vistas" + replies: "Respostas" + views_long: "este tema visitouse {{number}} veces" + activity: "Actividade" + likes: "Gústames" + likes_lowercase: + one: "gústame" + other: "gústames" + likes_long: "hai {{number}} gústames neste tema" + users: "Usuarios" + users_lowercase: + one: "usuario" + other: "usuarios" + category_title: "Categoría" + history: "Historial" + changed_by: "por {{author}}" + raw_email: + title: "Fonte do correo" + not_available: "Non dispoñíbel." + categories_list: "Lista de categorías" + filters: + with_topics: "%{filter} temas" + with_category: "Temas de %{filter} %{category}" + latest: + title: "Últimos" + title_with_count: + one: "Último (1)" + other: "({{count}}) últimos" + help: "temas con publicacións recentes" + hot: + title: "Quentes" + help: "unha selección dos temas máis quentes" + read: + title: "Lidos" + help: "temas que liches, partindo da última lectura" + search: + title: "Buscar" + help: "buscar todos os temas" + categories: + title: "Categorías" + title_in: "Categoría - {{categoryName}}" + help: "todos os temas agrupados por categoría" + unread: + title: "Sen ler" + title_with_count: + one: "Un sen ler" + other: "({{count}}) sen ler" + help: "temas con publicacións sen ler que estás vendo ou seguindo" + lower_title_with_count: + one: "Unha sen ler" + other: "{{count}} sen ler" + new: + lower_title_with_count: + one: "Un novo" + other: "{{count}} novos" + lower_title: "novo" + title: "Novo" + title_with_count: + one: "Un novo" + other: "({{count}}) novos" + help: "temas creados nos últimos días" + posted: + title: "As miñas publicacións" + help: "temas nos que publicaches" + bookmarks: + title: "Marcadores" + help: "temas que marcaches" + category: + title: "{{categoryName}}" + title_with_count: + one: "{{categoryName}} (1)" + other: "{{categoryName}} ({{count}})" + help: "últimos temas na categoría {{categoryName}}" + top: + title: "Banda superior" + help: "os temas máis activos no último ano, mes, semana ou día" + all: + title: "De sempre" + yearly: + title: "Anual" + quarterly: + title: "Trimestral" + monthly: + title: "Mensual" + weekly: + title: "Semanal" + daily: + title: "Diario" + all_time: "De sempre" + this_year: "Ano" + this_quarter: "Trimestre" + this_month: "Mes" + this_week: "Semana" + today: "Hoxe" + other_periods: "ver arriba" + browser_update: 'Desgraciadamente, o navegador é demasiado vello para funcionar nesta web. Anóvao.' + permission_types: + full: "Crear / Responder / Ver" + create_post: "Responder / Ver" + readonly: "Ver" + admin_js: + type_to_filter: "escribe para filtrar..." + admin: + title: 'Administrador de Discourse' + moderator: 'Moderador' + dashboard: + title: "Panel" + last_updated: "Última actualización de Panel:" + version: "Versión" + up_to_date: "Estás actualizado." + critical_available: "Está dispoñíbel unha actualización crítica." + updates_available: "Hai actualizacións dispoñíbeis." + please_upgrade: "Por favor actualiza." + no_check_performed: "Non se efectuou unha busca de actualizacións. Asegúrate de que sidekiq está executándose." + stale_data: "Non se efectuou unha busca de actualizacións ultimamente. Asegúrate de que sidekiq está executándose." + version_check_pending: "Parece que actualizaches recentemente. Caralludo!" + installed_version: "Instalado" + latest_version: "Últimos" + problems_found: "Atopáronse algúns problemas coa instalación do Discourse." + last_checked: "Última comprobación" + refresh_problems: "Actualizar" + no_problems: "Non se atoparon problemas." + moderators: 'Moderadores:' + admins: 'Administradores:' + blocked: 'Bloqueados:' + suspended: 'Suspendidos:' + private_messages_short: "Mensaxes" + private_messages_title: "Mensaxes" + mobile_title: "Móbil" + space_free: "{{size}} libres" + uploads: "subidas" + backups: "copias de seguranza" + traffic_short: "Tráfico" + traffic: "Peticións web de aplicativos" + page_views: "Peticións API" + page_views_short: "Peticións API" + show_traffic_report: "Amosar o informe detallado do tráfico" + reports: + today: "Hoxe" + yesterday: "Onte" + last_7_days: "Últimos 7 días" + last_30_days: "Últimos 30 días" + all_time: "De sempre" + 7_days_ago: "Hai 7 días" + 30_days_ago: "Hai 30 días" + all: "Todas" + view_table: "táboa" + view_chart: "gráfica de barras" + refresh_report: "Actualiza informe" + start_date: "Data de inicio" + end_date: "Data de remate" + groups: "Todos os grupos" + commits: + latest_changes: "Últimos cambios: por favor, actualiza a miúdo." + by: "por" + flags: + title: "Denuncias" + old: "Antigo" + active: "Activo" + agree: "Aceptar" + agree_title: "Confirmar esta denuncia como válida" + agree_flag_modal_title: "Aceptar e..." + agree_flag_hide_post: "Aceptar (ocultar publicación + enviar msx. privada)" + agree_flag_hide_post_title: "Ocultar esta publicación e enviar automaticamente unha mensaxe ao usuario para que a edite axiña" + agree_flag_restore_post: "Aceptar (restabelecer publicación)" + agree_flag_restore_post_title: "Restabelecer esta publicación" + agree_flag: "Aceptar denuncia" + agree_flag_title: "Aceptar a denuncia e manter a publicación sen cambios" + defer_flag: "Pospor" + defer_flag_title: "Eliminar esta denuncia; non precisa medidas neste momento." + delete: "Eliminar" + delete_title: "Eliminar a publicación á que se refire esta denuncia." + delete_post_defer_flag: "Eliminar publicación e pospor a denuncia" + delete_post_defer_flag_title: "Eliminar publicación. Se é a inicial, eliminar tamén o tema" + delete_post_agree_flag: "Eliminar a publicación e aceptar a denuncia" + delete_post_agree_flag_title: "Eliminar publicación. Se é a inicial, eliminar tamén o tema." + delete_flag_modal_title: "Eliminar e..." + delete_spammer: "Eliminar spammer" + delete_spammer_title: "Eliminar o usuario e todas as súas publicacións e temas." + disagree_flag_unhide_post: "En desacordo (amosar publicación)" + disagree_flag_unhide_post_title: "Eliminar as denuncias desta publicación e facela visíbel de novo" + disagree_flag: "Non aceptar" + disagree_flag_title: "Denegar esta denuncia por incorrecta" + clear_topic_flags: "Feito" + clear_topic_flags_title: "Este tema investigouse e as incidencias arranxáronse. Preme Feito para eliminar as denuncias." + more: "(máis respostas...)" + dispositions: + agreed: "aceptar" + disagreed: "non aceptar" + deferred: "posposta" + flagged_by: "Denunciado por" + resolved_by: "Arranxado por" + took_action: "Tomadas medidas" + system: "Sistema" + error: "Algo foi mal" + reply_message: "Responder" + no_results: "Non hai denuncias" + topic_flagged: "Este tema foi denunciado." + visit_topic: "Visite o tema para actuar" + was_edited: "A publicación editouse despois da primeira denuncia" + previous_flags_count: "Esta publicación xa se denunciou {{count}} veces." + summary: + action_type_3: + one: "sen relación" + other: "sen relación x{{count}}" + action_type_4: + one: "inapropiado" + other: "inapropiados x{{count}}" + action_type_6: + one: "personalizar" + other: "personalizar x{{count}}" + action_type_7: + one: "personalizar" + other: "personalizar x{{count}}" + action_type_8: + one: "spam" + other: "spam x{{count}}" + groups: + primary: "Grupo primario" + no_primary: "(non hai un grupo primario)" + title: "Grupos" + edit: "Editar grupos" + refresh: "Actualizar" + new: "Novo" + selector_placeholder: "escribe o nome do usuario" + name_placeholder: "Nome do grupo sen espazos, como na regra do nome do usuario" + about: "Edita aquí a túa pertenza a un grupo e nomes" + group_members: "Membros do grupo" + delete: "Eliminar" + delete_confirm: "Eliminar este grupo?" + delete_failed: "Non é posíbel eliminar o grupo. Se este é un grupo automático, non se pode destruír." + delete_member_confirm: "Queres eliminar a «%{username}» do grupo «%{group}»?" + delete_owner_confirm: "Eliminar os privilexios de usuario de «%{username}»?" + name: "Nome" + add: "Engadir" + add_members: "Engadir membros" + custom: "Personalizar" + bulk_complete: "Engadíronse os usuarios ao grupo." + bulk: "Engadir ao grupo en bloque" + bulk_paste: "Pegar unha lista de nomes de usuario ou correos-e, un por liña:" + bulk_select: "(seleccionar un grupo)" + automatic: "Automático" + automatic_membership_email_domains: "Os usuarios que se rexistraron cun dominio de correo electrónico que coincida exactamente con algún da lista engadiranse automaticamente a este grupo:" + automatic_membership_retroactive: "Aplicar a regra do mesmo dominio de correo electrónico para engadir os usuarios rexistrados." + default_title: "Título predeterminado para os usuarios deste grupo" + primary_group: "Estabelecer automaticamente como grupo primario" + group_owners: Propietarios + add_owners: Engadir propietarios + incoming_email: "Personalizar enderezos de correo-e entrantes" + incoming_email_placeholder: "introducir enderezos de correo-e" + api: + generate_master: "Xerar unha chave maestra da API" + none: "Non hai chaves activas da API agora mesmo." + user: "Usuario" + title: "API" + key: "Chave da API" + generate: "Xerar" + regenerate: "Rexenerar" + revoke: "Revogar" + confirm_regen: "Confirmas a substitución da chave da API por unha nova?" + confirm_revoke: "Confirmas a revogación desta chave?" + info_html: "A chave da API permitirache crear e actualizar temas usando chamadas JSON." + all_users: "Todos os usuarios" + note_html: "Manter esta chave en secreto, todos os usuarios que a teñan poderían crear e publicar co nome da calquera usuario." + plugins: + title: "Plugins" + installed: "Plugins instalados" + name: "Nome" + none_installed: "Non tes ningún plugin instalado." + version: "Versión" + enabled: "Activado?" + is_enabled: "S" + not_enabled: "N" + change_settings: "Cambiar axustes" + change_settings_short: "Axustes" + howto: "Como podo instalar un plugin?" + backups: + title: "Copias de seguranza" + menu: + backups: "Copias de seguranza" + logs: "Rexistros" + none: "Non hai copia de seguranza dispoñíbel" + read_only: + enable: + title: "Activar o modo de só-lectura" + label: "Activar modo de só-lectura" + confirm: "Confirmas a activación do modo de só-lectura?" + disable: + title: "Desactivar o modo de só-lectura" + label: "Desactivar modo de só-lectura" + logs: + none: "Aínda non hai rexistros..." + columns: + filename: "Nome do ficheiro" + size: "Tamaño" + upload: + label: "Enviar" + title: "Subir unha copia de seguranza a esta instancia" + uploading: "Enviando..." + success: "«{{filename}}» subiuse correctamente." + error: "Produciuse un erro durante o envío de «{{filename}}»: {{message}}" + operations: + is_running: "Estase executando unha operación..." + failed: "Produciuse un fallo {{operation}} . Revisa os rexistros." + cancel: + label: "Cancelar" + title: "Cancelar a operación actual" + confirm: "Confirmas a cancelación da operación actual?" + backup: + label: "Copia de seguranza" + title: "Crear unha copia de seguranza" + confirm: "Confirmas a execución dunha nova copia de seguranza?" + without_uploads: "Si (non incluír ficheiros)" + download: + label: "Descargar" + title: "Descargar a copia de seguranza" + destroy: + title: "Eliminar a copia de seguranza" + confirm: "Confirmas a destrución desta copia de seguranza?" + restore: + is_disabled: "«Restabelecer» está desactivado nos axustes do sitio." + label: "Restabelecer" + title: "Restabelecer a copia de seguranza" + confirm: "Confirmas a restauración desta copia de seguranza?" + rollback: + label: "Reverter" + title: "Reverter a base de datos ao estado de funcionamento anterior" + confirm: "Confirmas a reversión da base de datos ao estado de funcionamento anterior?" + export_csv: + user_archive_confirm: "Confirmas a descarga das túas publicacións?" + success: "Iniciouse a exportación, notificarémosche cunha mensaxe o remate do proceso." + failed: "Produciuse un fallo na exportación. Comproba os rexistros." + rate_limit_error: "Só se poden descargar as publicacións unha vez por día. Téntao de novo mañá." + button_text: "Exportar" + button_title: + user: "Exportar a lista completa de usuarios en formato CSV." + staff_action: "Exportar o rexistro con todas as accións do equipo en formato CSV." + screened_email: "Exportar a lista de correos-e controlados en formato CSV." + screened_ip: "Exportar a lista de IP controladas en formato CSV." + screened_url: "Exportar a lista de URL controladas en formato CSV." + export_json: + button_text: "Exportar" + invite: + button_text: "Enviar convites" + button_title: "Enviar convites" + customize: + title: "Personalizar" + long_title: "Personalización do sitio" + css: "CSS" + header: "Cabeceira" + top: "Banda superior" + footer: "Pé de páxina" + embedded_css: "CSS encaixado" + head_tag: + text: "" + title: "HTML que se inserirá antes da etiqueta " + body_tag: + text: "" + title: "HTML que se inserirá antes da etiqueta " + override_default: "Non incluír folla de estilo estándar" + enabled: "Activado?" + preview: "previsualizar" + undo_preview: "eliminar previsualización" + rescue_preview: "estilo predeterminado" + explain_preview: "Ver o sitio con esta folla de estilo personalizada" + explain_undo_preview: "Volver á folla de estilo personalizada activada actualmente" + explain_rescue_preview: "Ver o sitio coa folla de estilo predeterminada" + save: "Gardar" + new: "Novo" + new_style: "Novo estilo" + import: "Importar" + import_title: "Seleccionar un ficheiro ou pegar un texto" + delete: "Eliminar" + delete_confirm: "Queres eliminar esta personalización?" + about: "Modificar as follas de estilo CSS e as cabeceiras HTML do sitio. Engadir unha personalización para comezar." + color: "Cor" + opacity: "Opacidade" + copy: "Copiar" + email_templates: + title: "Modelos de correo-e" + subject: "Asunto" + multiple_subjects: "Este modelo de correo-e ten varios asuntos." + body: "Corpo" + none_selected: "Selecciona un modelo de correo-e para comezar a edición." + revert: "Reverter os cambios" + revert_confirm: "Confirmas a reversión dos cambios?" + css_html: + title: "CSS/HTML" + long_title: "Personalizacións do CSS e do HTML" + colors: + title: "Cores" + long_title: "Esquemas de cor" + about: "Modifica as cores usadas no sitio sen ter que escribir en CSS. Engade un esquema para comezar." + new_name: "Novo esquema de cores" + copy_name_prefix: "Copiar de" + delete_confirm: "Queres eliminar este esquema de cor?" + undo: "desfacer" + undo_title: "Desfai os teus cambio nesta cor desde a última vez que a gardaches." + revert: "reverter" + revert_title: "Restabelecer o esquema de cor ao predeterminado de Discourse." + primary: + name: 'primario' + description: 'Principalmente texto, iconas e bordos.' + secondary: + name: 'secundario' + description: 'Cor principal do fondo e cor do texto dalgúns botóns.' + tertiary: + name: 'terciario' + description: 'Ligazóns, algúns botóns, notificacións e cor de resalte.' + quaternary: + name: "cuaternario" + description: "Ligazóns de navegación" + header_background: + name: "Fondo da cabeceira" + description: "Cor do fondo na cabeceira do sitio." + header_primary: + name: "cabeceira primaria" + description: "Texto e iconas na cabeceira do sitio." + highlight: + name: 'resaltes' + description: 'Cor do fondo dos elementos resaltados da páxina, como publicacións e temas.' + danger: + name: 'perigo' + description: 'Cor de resalte para accións como eliminar publicacións e temas.' + success: + name: 'correcto' + description: 'Usado para indicar que unha acción se realizou correctamente.' + love: + name: 'gústame' + description: "Cor do botón Gústame." + email: + title: "Correos electrónicos" + settings: "Axustes" + templates: "Modelos" + preview_digest: "Previsualización do compendio" + sending_test: "Enviando correo-e de proba..." + error: "ERRO - %{server_error}" + test_error: "Produciuse un problema enviando o correo-e de proba. Revisa os teus axustes de correo electrónico, comproba que o teu host non está bloqueando as conexións de correo e téntao de novo." + sent: "Enviado" + skipped: "Saltado" + received: "Recibidos" + rejected: "Rexeitado" + sent_at: "Enviado ás" + time: "Hora" + user: "Usuario" + email_type: "Tipo de correo-e" + to_address: "Ao enderezo" + test_email_address: "enderezo de correo-e de proba" + send_test: "Enviar correo-e de proba" + sent_test: "enviado!" + delivery_method: "Metodo de entrega" + preview_digest_desc: "Previsualiza o contido dos correos-e compendio enviados a usuarios inactivos." + refresh: "Actualizar" + format: "Formato" + html: "html" + text: "texto" + last_seen_user: "Último usuario visto:" + reply_key: "Tecla para responder" + skipped_reason: "Saltar razón" + incoming_emails: + from_address: "De" + to_addresses: "A" + cc_addresses: "Cc" + subject: "Asunto" + error: "Erro" + none: "Non se atoparon correos entrantes." + modal: + title: "Detalles dos correos-e entrantes" + error: "Erro" + return_path: "Ruta de retorno" + message_id: "Id da mensaxe" + in_reply_to: "En resposta a" + references: "Referencias" + date: "Data" + from: "De" + to: "A" + cc: "Cc" + subject: "Asunto" + body: "Corpo" + rejection_message: "Correo-e de rexeitamento" + filters: + from_placeholder: "de@exemplo.com" + to_placeholder: "a@exemplo.com" + cc_placeholder: "cc@exemplo.com" + subject_placeholder: "Asunto..." + error_placeholder: "Erro" + logs: + none: "Non se atoparon rexistros." + filters: + title: "Filtro" + user_placeholder: "nome de usuario" + address_placeholder: "nome@exemplo.com" + type_placeholder: "compendio, rexistro..." + reply_key_placeholder: "tecla para responder" + skipped_reason_placeholder: "razón" + logs: + title: "Rexistros" + action: "Acción" + created_at: "Creado" + last_match_at: "Último resultado" + match_count: "Resultados" + ip_address: "IP" + topic_id: "ID do tema" + post_id: "ID da publicación" + category_id: "ID da categoría" + delete: 'Eliminar' + edit: 'Editar' + save: 'Gardar' + screened_actions: + block: "bloquear" + do_nothing: "non facer nada" + staff_actions: + title: "Accións do equipo" + instructions: "Premer nos nomes dos usuarios e nas accións para filtrar a lista. Premer nas imaxes dos perfís para ir ás páxinas dos usuarios." + clear_filters: "Amosar todo" + staff_user: "Usuario do equipo" + target_user: "Usuario obxectivo" + subject: "Asunto" + when: "Cando" + context: "Contexto" + details: "Detalles" + previous_value: "Anterior" + new_value: "Novo" + diff: "Diferenzas" + show: "Amosar" + modal_title: "Detalles" + no_previous: "Non hai ningún valor previo." + deleted: "Non hai ningún valor. O rexistro foi eliminado." + actions: + delete_user: "eliminar usuario" + change_trust_level: "cambiar nivel de confianza" + change_username: "cambiar nome do usuario" + change_site_setting: "cambiar axuste do sitio" + change_site_customization: "cambiar a personalización do sitio" + delete_site_customization: "eliminar a personalización do sitio" + change_site_text: "cambiar o texto do sitio" + suspend_user: "suspender usuario" + unsuspend_user: "non suspender usuario" + grant_badge: "conceder insignia" + revoke_badge: "revogar insignia" + check_email: "comprobar correo-e" + delete_topic: "eliminar tema" + delete_post: "eliminar publicación" + impersonate: "suplantar" + anonymize_user: "facer o usuario anónimo" + roll_up: "encartar os bloques de IP" + change_category_settings: "cambiar axustes da categoría" + delete_category: "eliminar categoría" + create_category: "crear categoría" + block_user: "bloquear usuario" + unblock_user: "desbloquear usuario" + grant_admin: "conceder administración" + revoke_admin: "revogar administración" + grant_moderation: "conceder moderación" + revoke_moderation: "revogar moderación" + backup_operation: "operación de copia de seguranza" + screened_emails: + title: "Correos-e controlados" + description: "Cando alguén tente crear unha nova conta, comprobaranse os seguintes enderezos de correo electrónico e bloquearase o rexistro ou se tomará outra medida." + email: "Enderezo de correo-e" + actions: + allow: "Permitir" + screened_urls: + title: "URL controladas" + description: "As URL listadas aquí usáronse en publicacións de usuarios que foron identificados como spammers." + url: "URL" + domain: "Dominio" + screened_ips: + title: "IP controladas" + description: 'Enderezos IP observados. Usa «Permitir» para engadilos á lista branca.' + delete_confirm: "Confirmas a eliminación da regra para %{ip_address}?" + roll_up_confirm: "Confirmas que queres agrupar en subredes os enderezos IP controlados comunmente?" + rolled_up_some_subnets: "Agrupáronse correctamente en subredes as entradas de IP para bloquear: %{subnets}." + rolled_up_no_subnet: "Non hai nada que agrupar." + actions: + block: "Bloquear" + do_nothing: "Permitir" + allow_admin: "Permitir administrador" + form: + label: "Novo:" + ip_address: "Enderezo IP" + add: "Engadir" + filter: "Buscar" + roll_up: + text: "Agrupar" + title: "Crea unha nova subrede de entradas para bloquear se hai cando menos «min_ban_entries_for_roll_up» entradas." + logster: + title: "Rexistros de erros" + impersonate: + title: "Suplantar" + help: "Usar esta ferramenta para suplantar unha conta de usuario co obxecto de detectar e corrixir erros. Deberás saír da sesión ao rematar." + not_found: "Non é posíbel atopar o usuario." + invalid: "Sentímolo pero non podes suplantar este usuario." + users: + title: 'Usuarios' + create: 'Engadir usuario administrador' + last_emailed: "Últimos envíos por correo-e" + not_found: "Sentímolo pero o nome do usuario non existe no sistema." + id_not_found: "Sentímolo pero o usuario non existe no sistema." + active: "Activo" + show_emails: "Amosar correos-e" + nav: + new: "Novo" + active: "Activo" + pending: "Pendente" + staff: 'Equipo' + suspended: 'Suspendido' + blocked: 'Bloqueado' + suspect: 'Sospeitoso' + approved: "Aprobado?" + approved_selected: + one: "aprobar usuario" + other: "aprobar os ({{count}}) usuarios" + reject_selected: + one: "rexeitar usuario" + other: "rexeitar os ({{count}}) usuarios" + titles: + active: 'Usuarios activos' + new: 'Novos usuarios' + pending: 'Usuarios pendentes de revisión' + newuser: 'Usuarios cun nivel de confianza 0 (novo usuario)' + basic: 'Usuarios cun nivel de confianza 1 (usuario básico)' + member: 'Usuarios cun nivel de confianza 2 (membro)' + regular: 'Usuarios cun nivel de confianza 3 (normal)' + leader: 'Usuarios cun nivel de confianza 4 (líder)' + staff: "Equipo" + admins: 'Usuarios administradores' + moderators: 'Moderadores' + blocked: 'Usuarios bloqueados' + suspended: 'Usuarios suspendidos' + suspect: 'Usuarios sospeitosos' + reject_successful: + one: "Rexeitouse un usuario correctamente." + other: "Rexeitáronse %{count} usuarios correctamente." + reject_failures: + one: "Produciuse un fallo rexeitando o usuario." + other: "Produciuse un fallo rexeitando os %{count} usuarios." + not_verified: "Sen verificar" + check_email: + title: "Amosar o enderezo de correo-e deste usuario" + text: "Amosar" + user: + suspend_failed: "Algo fallou rexeitando este usuario {{error}}" + unsuspend_failed: "Algo foi mal levantando a suspensión deste usuario {{error}}" + suspend_duration: "Canto tempo estará suspendido o usuario?" + suspend_duration_units: "(días)" + suspend_reason_label: "Cal é o motivo da suspensión? Este texto será visíbel para todo o mundo na páxina do perfil deste usuario e amosaráselle ao usuario cando tente iniciar sesión. Procura ser breve." + suspend_reason: "Razón" + suspended_by: "Suspendido por" + delete_all_posts: "Eliminar todas as publicacións" + delete_all_posts_confirm: "Confirmas a eliminación de %{posts} publicacións e %{topics} temas?" + suspend: "Suspender" + unsuspend: "Non suspender" + suspended: "Suspendido?" + moderator: "Moderador?" + admin: "Administrador?" + blocked: "Bloqueado?" + staged: "Transitorio?" + show_admin_profile: "Administración" + edit_title: "Editar título" + save_title: "Gardar título" + refresh_browsers: "Forzar a actualización do navegador" + refresh_browsers_message: "Mensaxe enviada a todos os clientes." + show_public_profile: "Amosar o perfil público" + impersonate: 'Suplantar' + ip_lookup: "Busca de IP" + log_out: "Saír da sesión" + logged_out: "O usuario foi desconectado en todos os dispositivos" + revoke_admin: 'Revogar administrador' + grant_admin: 'Facer administrador' + revoke_moderation: 'Revogar moderación' + grant_moderation: 'Conceder moderación' + unblock: 'Desbloquear' + block: 'Bloquear' + reputation: Reputación + permissions: Permisos + activity: Actividade + like_count: Gústames dados / Recibidos + last_100_days: 'nos últimos 100 días' + private_topics_count: Temas privados + posts_read_count: Publicacións lidas + post_count: Publicacións creadas + topics_entered: Temas vistos + flags_given_count: Denuncias dadas + flags_received_count: Denuncias recibidas + warnings_received_count: Avisos recibidos + flags_given_received_count: 'Denuncias dadas / Recibidas' + approve: 'Aprobar' + approved_by: "aprobado por" + approve_success: "Usuario aprobado, enviouse un correo-e coas instrucións de activación." + approve_bulk_success: "Todos os usuarios seleccionados foron aprobados e notificados." + time_read: "Tempo de lectura" + anonymize: "Facer o usuario anónimo" + anonymize_confirm: "Confirmas que desexas converter esta conta en anónima? Cambiaranse o nome do usuario e o correo electrónico e restabelecerase toda a información do perfil." + anonymize_yes: "Si, converter a conta en anónima" + anonymize_failed: "Produciuse un problema convertendo a conta en anónima." + delete: "Eliminar usuario" + delete_forbidden_because_staff: "Non é posíbel eliminiar os administradores e moderadores." + delete_posts_forbidden_because_staff: "Non é posíbel eliminar todas as publicacións dos administradores ou moderadores." + delete_forbidden: + one: "Non é posíbel eliminar usuarios se teñen publicacións. Elimina as publicacións antes de eliminar o usuario (non é posíbel eliminar publicacións de máis de %{count} día)" + other: "Non é posíbel eliminar usuarios se teñen publicacións. Elimina as publicacións antes de eliminar o usuario (non é posíbel eliminar publicacións de máis de %{count} días)" + cant_delete_all_posts: + one: "Non é posíbel eliminar todas as publicacións. Algunhas teñen máis de %{count} día. (o axuste delete_user_max_post_age.)" + other: "Non é posíbel eliminar todas as publicacións. Algunhas teñen máis de %{count} días. (o axuste delete_user_max_post_age.)" + cant_delete_all_too_many_posts: + one: "Non é posíbel eliminar todas as publicacións porque o usuario ten máis dunha publicación. (delete_all_posts_max)" + other: "Non é posíbel eliminar todas as publicacións porque o usuario ten máis de %{count} publicacións. (delete_all_posts_max)" + delete_confirm: "CONFIRMAS a eliminación deste usuario? Non se poderá desfacer!" + delete_and_block: "Eliminar e bloquear este correo-e e enderezo IP" + delete_dont_block: "Só eliminar" + deleted: "Eliminouse o usuario." + delete_failed: "Produciuse un erro eliminando este usuario. Asegúrate que se eliminan todas as publicacións antes de tentar eliminar o usuario." + send_activation_email: "Enviar correo-e de activación" + activation_email_sent: "Enviouse un correo electrónico de activación." + send_activation_email_failed: "Produciuse un problema enviando outro correo-e de activación. %{error}" + activate: "Activar conta" + activate_failed: "Produciuse un problema activando o usuario." + deactivate_account: "Desactivar a conta" + deactivate_failed: "Produciuse un problema desactivando o usuario." + unblock_failed: 'Produciuse un problema desbloqueando o usuario.' + block_failed: 'Produciuse un problema bloqueando o usuario.' + block_confirm: 'Confirmas o bloqueo deste usuario? Non poderá crear máis temas nin publicacións.' + block_accept: 'Si, bloquealo' + deactivate_explanation: "Un usuario desactivado debe revalidar o seu correo-e." + suspended_explanation: "Un usuario suspendido non pode iniciar sesión." + block_explanation: "Un usuario bloquedo non pode publicar nin iniciar temas." + stage_explanation: "Un usuario transitorio só pode publicar por correo electrónico en temas específicos." + trust_level_change_failed: "Produciuse un problema cambiando o nivel de confianza do usuario." + suspend_modal_title: "Suspender usuario" + trust_level_2_users: "Usuarios con nivel 2 de confianza" + trust_level_3_requirements: "Requirimentos para o nivel de confianza 3" + trust_level_locked_tip: "o nivel de confianza está bloqueado, o sistema non promocionará nin rebaixará o usuario" + trust_level_unlocked_tip: "o nivel de confianza está desbloqueado, o sistema pode promocionar ou rebaixar o usuario" + lock_trust_level: "Bloquear o nivel de confianza" + unlock_trust_level: "Desbloquear o nivel de confianza" + tl3_requirements: + title: "Requerimentos para o nivel 3 de confianza" + table_title: "Nos últimos %{time_period} días:" + value_heading: "Valor" + requirement_heading: "Requirimento" + visits: "Visitas" + days: "días" + topics_replied_to: "Temas os que respondeu" + topics_viewed: "Temas vistos" + topics_viewed_all_time: "Temas vistos (todo o tempo)" + posts_read: "Publicacións lidas" + posts_read_all_time: "Publicacións lidas (todo o tempo)" + flagged_posts: "Publicacións denunciadas" + flagged_by_users: "Usuarios que denunciaron" + likes_given: "Gústames dados" + likes_received: "Gústames recibidos" + likes_received_days: "Gústames recibidos: por días" + likes_received_users: "Gústames recibidos: usuarios" + qualifies: "Cumpre os requisitos para o nivel de confianza 3." + does_not_qualify: "Non cumpre os requisitos para o nivel de confianza 3." + will_be_promoted: "Será promocionado pronto." + will_be_demoted: "Será rebaixado pronto." + on_grace_period: "Actualmente ne periodo de graza da promoción, non será rebaixado" + locked_will_not_be_promoted: "Nivel de confianza bloqueado. Non se promocionará nunca." + locked_will_not_be_demoted: "Nivel de confianza bloqueado. Non será rebaixado nunca." + sso: + title: "Inicio de sesión simple" + external_id: "ID externo" + external_username: "Nome do usuario" + external_name: "Nome" + external_email: "Correo electrónico" + external_avatar_url: "URL da imaxe do perfil" + user_fields: + title: "Campos do usuario" + help: "Engade campos que os usuarios poidan encher." + create: "Crear un campo de usuario" + untitled: "Sen título" + name: "Nome do campo" + type: "TIpo de campo" + description: "Descrición do campo" + save: "Gardar" + edit: "Editar" + delete: "Eliminar" + cancel: "Cancelar" + delete_confirm: "Confirmas a eliminación deste campo do usuario?" + options: "Opcións" + required: + title: "Requirido para rexistrarse?" + enabled: "obrigatorio" + disabled: "non obrigatorio" + editable: + title: "Editábel despois do rexistro?" + enabled: "editábel" + disabled: "non editábel" + show_on_profile: + title: "Amosar no perfil público?" + enabled: "amosado no perfil" + disabled: "non amosado no perfil" + field_types: + text: 'Campo de texto' + confirm: 'Confirmación' + dropdown: "Despregábel" + site_text: + description: "Podes personalizar calquera texto do teu foro. Comeza por buscar debaixo:" + search: "Busca o texto que queres editar" + title: 'Contido do texto' + edit: 'editar' + revert: "Reverter os cambios" + revert_confirm: "Confirmas a reversión dos cambios?" + go_back: "Volver á busca" + recommended: "Recomendamos personalizar o seguinte texto para axeitalo ás túas necesidades:" + show_overriden: 'Amosar só os cambios' + site_settings: + show_overriden: 'Amosar só os cambios' + title: 'Axustes' + reset: 'restabelecer' + none: 'ningunha' + no_results: "Non se atoparon resultados." + clear_filter: "Borrar" + add_url: "engadir URL" + add_host: "engadir host" + categories: + all_results: 'Todas' + required: 'Obrigatorio' + basic: 'Configuración básica' + users: 'Usuarios' + posting: 'Publicación' + email: 'Correo electrónico' + files: 'Ficheiros' + trust: 'Niveis de confianza' + security: 'Seguranza' + onebox: "Onebox" + seo: 'SEO' + spam: 'Spam' + rate_limits: 'Límites de frecuencia' + developer: 'Desenvolvedor' + embedding: "Encaixado" + legal: "Legal" + uncategorized: 'Outro' + backups: "Copias de seguranza" + login: "Iniciar sesión" + plugins: "Plugins" + user_preferences: "Preferencias do usuario" + badges: + title: Insignias + new_badge: Nova insignia + new: Novo + name: Nome + badge: Insignia + display_name: Nome público + description: Descrición + badge_type: Tipo de insignia + badge_grouping: Grupo + badge_groupings: + modal_title: Grupos de insignias + granted_by: Concedido por + granted_at: Concedido + reason_help: (Ligazón a unha publicación ou tema) + save: Gardar + delete: Eliminar + delete_confirm: Confirmas a eliminación desta insignia? + revoke: Revogar + reason: Razón + expand: Expandir … + revoke_confirm: Confirmas a revogación desta insignia? + edit_badges: Editar insignias + grant_badge: Conceder insignia + granted_badges: Insignias concedidas + grant: Conceder + no_user_badges: "A %{name} non se lle concedeu ningunha insignia." + no_badges: Non hai insignias para conceder. + none_selected: "Selecciona unha insignia para comezar" + allow_title: Permitir que se use a insignia como un título + multiple_grant: Pode concederse múltiples veces + listable: Amosar a insignia na páxina de insignias públicas + enabled: Activar insignia + icon: Icona + image: Imaxe + icon_help: "Usar a clase Font Awesome ou unha URL a unha imaxe" + query: Consulta sobre insignia (SQL) + target_posts: Consulta dirixida a mensaxes + auto_revoke: Executar a consulta de revogación diariamente + show_posts: Amosar a publicación concedendo a insignia na páxina das insignias + trigger: Activador + trigger_type: + none: "Actualizar diariamente" + post_action: "Cando un usuario actúa sobre unha publicación" + post_revision: "Cando un usuario edita ou crea unha publicación" + trust_level_change: "Cando un usuario cambia o nivel de confianza" + user_change: "Cando se crea ou edita un usuario" + preview: + link_text: "Previsualizar as insignias concedidas" + plan_text: "Vista previa do plan da consulta" + modal_title: "Previsualizar a consulta de insignias" + sql_error_header: "Produciuse un erro coa consulta." + error_help: "Busca axuda nas seguintes ligazóns sobre consultas de insignias." + bad_count_warning: + header: "AVISO" + text: "Faltan algunhas mostras para a concesión da insignia. Isto acontece cando a consulta sobre insignias devolve unha ID do usuario, ou de publicacións que non existen. Pode causar resultados inesperados posteriormente. Revisa outra vez a solicitude." + no_grant_count: "Non hai insignias para asignar." + grant_count: + one: "1 insignia para asignar." + other: "%{count} insignias para asignar." + sample: "Mostra:" + grant: + with: %{username} + with_post: %{username} para publicación en %{link} + with_post_time: %{username} para publicación en %{link} o %{time} + with_time: %{username} o %{time} + emoji: + title: "Emoji" + help: "Engade novo emoji que estará dispoñíbel para todos. (Suxestión: arrastra e solta múltiples ficheiros dunha vez)" + add: "Engadir novo Emoji" + name: "Nome" + image: "Imaxe" + delete_confirm: "Confirmas a eliminación do emoji :%{name}:?" + embedding: + get_started: "Se queres encaixar o Discourse noutro sitio web, comeza engadindo o seu host." + confirm_delete: "Confirmas a eliminación deste host?" + sample: "Usa o seguinte código HTML no teu sitio para crear e encaixar temas do Discourse. Substitúe REPLACE_ME pola URL canónica da páxina que recibe o encaixado. " + title: "Encaixado" + host: "Hosts permitidos" + edit: "editar" + category: "Publicación a categoría" + add_host: "Engadir host" + settings: "Axustes para o encaixado" + feed_settings: "Axustes das fontes" + feed_description: "Dotar dunha fonte RSS/ATOM o teu sitio pode mellorar a capacidade de Discourse para importar o teu contido." + crawling_settings: "Axustes do extractor" + crawling_description: "Cando o Discourse crea temas para as túas publicacións, se non existe unha fonte RSS/ATOM, tentará analizar o contido do teu HTML. Ás veces pode ser difícil extraer o contido, por iso damos a posibilidade de especificar as regras do CSS para facilitar a extracción." + embed_by_username: "Nome do usuario para crear o tema" + embed_post_limit: "Número máximo de publicacións a encaixar" + embed_username_key_from_feed: "Clave para extraer da fonte o nome do usuario do discourse" + embed_truncate: "Truncar as publicacións encaixadas" + embed_whitelist_selector: "Selector CSS de elementos permitidos nos encaixados" + embed_blacklist_selector: "Selector CSS para elementos retirados nos encaixados" + feed_polling_enabled: "Importar publicacións vía RSS/ATOM" + feed_polling_url: "URL da fonte RSS/ATOM para facer a extracción" + save: "Gardar axustes de encaixado" + permalink: + title: "Ligazóns permanentes" + url: "URL" + topic_id: "ID do tema" + topic_title: "Tema" + post_id: "ID da publicación" + post_title: "Publicación" + category_id: "ID da categoría" + category_title: "Categoría" + external_url: "URL externa" + delete_confirm: Confirmas a eliminación desta ligazón permanente? + form: + label: "Novo:" + add: "Engadir" + filter: "Buscar (URLou URL externa)" + lightbox: + download: "descargar" + search_help: + title: 'Buscar axuda' + keyboard_shortcuts_help: + title: 'Atallos do teclado' + jump_to: + title: 'Ir a' + home: 'g, h Inicio' + latest: 'g, l Último' + new: 'g, n Novo' + unread: 'g, u Sen ler' + categories: 'g, c Categorías' + top: 'g, t Arriba' + bookmarks: 'g, b Marcadores' + profile: 'g, p Perfil' + messages: 'g, m Mensaxes' + navigation: + title: 'Navegación' + jump: '# Ir á publicación #' + back: 'u Volver' + up_down: 'k/j Mover selección ↑ ↓' + open: 'o ou Intro Abrir tema seleccionado' + next_prev: 'maiús.+j/maiús.+k Sección Seguinte/Anterior' + application: + title: 'Aplicativo' + create: 'c Crear un novo tema' + notifications: 'n Abrir notificacións' + hamburger_menu: '= Abrir o menú hamburguesa' + user_profile_menu: 'p Abrir o menú do usuario' + show_incoming_updated_topics: '. Amosar temas actualizados' + search: '/ Buscar' + help: '? Abrir a axuda do teclado' + dismiss_new_posts: 'x, r Desbotar Novas/Publicacións' + dismiss_topics: 'x, t Desbotar temas' + log_out: 'maiús.+z maiús.+z Saír da sesión' + actions: + title: 'Accións' + bookmark_topic: 'f Cambiar marcar tema' + pin_unpin_topic: 'Maiús+p Pegar/Despegar tema' + share_topic: 'maiús.+s Compartir tema' + share_post: 's Compartir publicación' + reply_as_new_topic: 't Responder como tema ligado' + reply_topic: 'maiús.+r Responder o tema' + reply_post: 'r Responder a publicación' + quote_post: 'q Citar publicación' + like: 'l Gústame a publicación' + flag: '! Denunciar publicación' + bookmark: 'b Marcar publicación' + edit: 'e Editar publicación' + delete: 'd Eliminar publicación' + mark_muted: 'm, m Silenciar tema' + mark_regular: 'm, r Tema normal (predeterminado)' + mark_tracking: 'm, t Seguir tema' + mark_watching: 'm, w Ver tema' + badges: + earned_n_times: + one: "Conseguiu esta insignia unha vez" + other: "Conseguiu esta insignia %{count} veces" + more_with_badge: "Máis con esta insignia" + title: Insignias + allow_title: "pode usarse como título" + multiple_grant: "pode ser recompensado múltiples veces" + badge_count: + one: "1 insignia" + other: "%{count} insignias" + more_badges: + one: "1 máis" + other: "%{count} máis" + granted: + one: "1 concedido" + other: "%{count} concedidos" + select_badge_for_title: Selecciona insignia para usar como o teu título + none: "" + badge_grouping: + getting_started: + name: Comezar + community: + name: Comunidade + trust_level: + name: Nivel de confianza + other: + name: Outro + posting: + name: Publicación + badge: + editor: + name: Editor + description: Edición da publicación inicial + basic_user: + name: Básico + description: Concedidas todas as funcións esenciais da comunidade + member: + name: Membro + description: Concedidos convites + regular: + name: Normal + description: Concedido recategorizar, renomear, publicar ligazóns computábeis para o posicionamento en motores de busca e acceso á Taberna + leader: + name: Líder + description: Concedido editar globalmente, pegar, pechar, arquivar, dividir e fusionar + welcome: + name: Benvido/a + description: Recibiu un gústame + autobiographer: + name: Autobiógrafo + description: Completou a información do perfil de usuario + anniversary: + name: Aniversario + description: Membro activo durante un ano, publicou cando menos unha vez + nice_post: + name: Publicación riquiña + description: Recibiu 10 gústames nunha publicación. Esta insignia pode concederse múltiples veces. + good_post: + name: Publicación boa + description: Recibiu 25 gústames nunha publicación. Esta insignia pode concederse múltiples veces. + great_post: + name: Gran publicación + description: Recibiu 50 gústames nunha publicación. Esta insignia pode concederse múltiples veces. + nice_topic: + name: Tema riquiño + description: Recibiu 10 gústames nun tema. Esta insignia pode concederse múltiples veces. + good_topic: + name: Tema bo + description: Recibiu 25 gústames nun tema. Esta insignia pode concederse múltiples veces. + great_topic: + name: Gran tema + description: Recibiu 50 gústames nun tema. Esta insignia pode concederse múltiples veces. + nice_share: + name: Compartición riquiña + description: Compartiu unha publicación con 25 visistantes únicos + good_share: + name: Compartición boa + description: Compartiu unha publicación con 300 visistantes únicos + great_share: + name: Gran compartición + description: Compartiu unha publicación con 1000 visistantes únicos + first_like: + name: Primeiro Gústame + description: Gustou unha publicación + first_flag: + name: Primeira denuncia + description: Denunciou unha publicación + promoter: + name: Promotor + description: Convidou un usuario + campaigner: + name: Padriño + description: Convidou 3 usuarios básicos (nivel de confianza 1) + champion: + name: Campión + description: Convidou 5 membros (nivel de confianza 2) + first_share: + name: Primeira compartición + description: Compartiu unha publicación + first_link: + name: Primeira ligazón + description: Engadiu unha ligazón interna a outro tema + first_quote: + name: Primeira cita + description: Citou un usuario + read_guidelines: + name: Ler as Directrices + description: Ler as directrices da comunidade + reader: + name: Lector + description: Leu todas as publicación dun tema con máis de 100 publicacións + popular_link: + name: Ligazón popular + description: Publicou unha ligazón externa con máis de 50 clics + hot_link: + name: Ligazón quente + description: Publicou unha ligazón externa con máis de 300 clics + famous_link: + name: Ligazón popular + description: Publicou unha ligazón externa con máis de 1000 clics + google_search: | +

Buscar no Google

+

+

+

diff --git a/config/locales/server.gl.yml b/config/locales/server.gl.yml new file mode 100644 index 0000000000..dcc3d6fd46 --- /dev/null +++ b/config/locales/server.gl.yml @@ -0,0 +1,492 @@ +gl: + dates: + short_date_no_year: "D MMM" + short_date: "D MMM, YYYY" + long_date: "D MMMM, YYYY h:mm a" + datetime_formats: + formats: + short: "%d-%m-%Y" + short_no_year: "%-d de %B" + date_only: "%-d de %B, %Y" + date: + month_names: [null, Xaneiro, Febreiro, Marzo, Abril, Maio, Xuño, Xullo, Agosto, Setembro, Outubro, Novembro, Decembro] + title: "Discourse" + topics: "Temas" + posts: "publicacións" + loading: "Cargndo" + log_in: "Iniciar sesión" + anonymous: "Anónimo" + errors: + format: '%{attribute} %{message}' + messages: + taken: "xa está en uso" + accepted: debe ser aceptado + blank: non pode quedar baleiro + present: debe quedar baleiro + confirmation: "%{attribute} non coincide" + empty: non pode quedar baleiro + equal_to: debe ser igual a %{count} + exclusion: está reservado + greater_than: debe ser maior de %{count} + greater_than_or_equal_to: debe ser igual ou maior de %{count} + has_already_been_used: "xa está en uso" + inclusion: no está incluído na lista + invalid: é incorrecto + less_than: debe ser menor de %{count} + less_than_or_equal_to: debe ser igual ou menor de %{count} + not_a_number: non é un número + not_an_integer: debe ser un enteiro + odd: debe ser impar + record_invalid: 'Fallou a validación: %{errors}' + restrict_dependent_destroy: + one: "Non é posíbel eliminalo porque existe outro %{record} dependente" + many: "Non é posíbel eliminalo porque existen outros %{record} dependentes" + too_long: + one: demasiado longo (máximo un caracterer) + other: demasiado longo (máximo %{count} caracteres) + too_short: + one: demasiado curto (mínimo un caracter) + other: demasiado curto (mínimo %{count} caracteres) + other_than: "debe ser distinto de %{count}" + template: + body: 'Producíronse problemas cos seguintes campos:' + embed: + load_from_remote: "Produciuse un erro cargando esta publicación." + site_settings: + min_username_length_range: "O mínimo non pode ser maior que o máximo." + not_logged_in: "Debes iniciar sesión para facer iso." + not_found: "Non foi posíbel atopar o recurso ou URL solicitado." + invalid_access: "Non se che permite ver o recurso solicitado." + read_only_mode_enabled: "O sitio está en modo só-lectura. As interaccións están desactivadas." + reading_time: "Tempo de lectura" + likes: "Gústames" + embed: + start_discussion: "Comezar a discusión" + continue: "Continuar a discusión" + more_replies: + one: "Unha resposta máis" + other: "%{count} respostas máis" + loading: "Cargando a discusión..." + permalink: "Ligazón permanente" + in_reply_to: "▶ %{username}" + replies: + one: "Unha resposta" + other: "%{count} respostas" + no_mentions_allowed: "Sentímolo pero non podes mencionar outros usuarios" + has_already_been_used: "xa está en uso" + invalid_characters: "contén caracteres incorrectos" + next_page: "páxina seguinte →" + prev_page: "← páxina anterior" + page_num: "Páxina %{num}" + home_title: "Inicio" + rss_posts_in_topic: "Fonte RSS de '%{topic}»" + author_wrote: "%{author} escribiu:" + num_posts: "Publicacións:" + num_participants: "Participantes:" + read_full_topic: "Ler todo o tema" + private_message_abbrev: "Msx." + rss_description: + latest: "Últimos temas" + hot: "Temas quentes" + posts: "Últimas publicacións" + too_late_to_edit: "Esta publicación ten moito tempo. Xa non se pode modificar nin eliminar." + excerpt_image: "imaxe" + queue: + delete_reason: "Eliminación por medio da cola de moderación de publicacións" + groups: + default_names: + everyone: "todos" + admins: "administradores" + moderators: "moderadores" + staff: "equipo" + trust_level_0: "nivel_de_confianza_0" + trust_level_1: "nivel_de_confianza_1" + trust_level_2: "nivel_de_confianza_2" + trust_level_3: "nivel_de_confianza_3" + trust_level_4: "nivel_de_confianza_4" + education: + until_posts: + one: "Unha publicación" + other: "%{count} publicacións" + activerecord: + attributes: + category: + name: "Nome da categoría" + post: + raw: "Corpo" + user_profile: + bio_raw: "Verbo de min" + errors: + models: + topic: + attributes: + base: + warning_requires_pm: "Só podes anexar avisos a mensaxes privadas." + no_user_selected: "Debes seleccionar un usuario correcto." + user: + attributes: + password: + common: "é un dos 10000 contrasinais máis habituais. Usa un máis seguro." + same_as_username: "é igual ao teu nome de usuario. Usa un contrasinal máis seguro." + same_as_email: "é igual ao teu correo electrónico. Usa un contrasinal máis seguro." + ip_address: + signup_not_allowed: "Non esta permitido rexistrarse desde esta conta." + color_scheme_color: + attributes: + hex: + invalid: "non é unha cor válida" + vip_category_name: "Taberna" + vip_category_description: "Unha categoría exclusiva para membros cun nivel de confianza igual ou maior a 3." + meta_category_name: "Opinións sobre o sitio" + meta_category_description: "Discusións sobre este sitio, a súa organización, como funciona e como se pode mellorar." + staff_category_name: "Equipo" + staff_category_description: "Categoría privada para discusións do equipo. Os temas son visíbeis unicamente para administradores e moderadores." + lounge_welcome: + title: "Benvido/a á Taberna" + category: + topic_prefix: "Sobre a categoría %{category}" + errors: + depth: "Non podes aniñar unha subcategoría dentro doutra" + trust_levels: + newuser: + title: "novo usuario" + basic: + title: "usuario básico" + member: + title: "membro" + regular: + title: "normal" + leader: + title: "líder" + rate_limiter: + hours: + one: "Unha hora" + other: "%{count} horas" + datetime: + distance_in_words: + half_a_minute: "< 1m" + less_than_x_seconds: + one: "< 1s" + other: "< %{count}s" + x_seconds: + one: "1s" + other: "%{count}s" + less_than_x_minutes: + one: "< 1m" + other: "< %{count}m" + about_x_hours: + one: "1h" + other: "%{count}h" + x_days: + one: "1d" + other: "%{count}d" + about_x_months: + one: "1mes" + other: "%{count}mes" + x_months: + one: "1mes" + other: "%{count}mes" + about_x_years: + one: "1ano" + other: "%{count}ano" + over_x_years: + one: "> 1ano" + other: "> %{count}ano" + almost_x_years: + one: "1ano" + other: "%{count}ano" + distance_in_words_verbose: + half_a_minute: "agora mesmiño" + password_reset: + choose_new: "Selecciona un novo contrasinal" + choose: "Selecciona un contrasinal" + update: 'Actualizar contrasinal' + save: 'Estabelecer o contrasinal' + title: 'Contrasinal restabelecido' + continue: "Continuar a %{site_name}" + change_email: + confirmed: "O teu correo electrónico foi actualizado." + please_continue: "Continuar a %{site_name}" + error: "Produciuse un erro cambiando o enderezo de correo electrónico. Quizais xa está en uso?" + activation: + action: "Preme aquí para activar a conta" + continue_button: "Continuar a %{site_name}" + welcome_to: "Benvido/a a %{site_name}!" + post_action_types: + off_topic: + title: 'Sen-relación' + long_form: 'marcou isto como non relacionado' + spam: + title: 'Spam' + long_form: 'denunciou isto como spam' + inappropriate: + title: 'Inapropiado' + long_form: 'denunciou isto como inapropiado' + notify_moderators: + title: "Algo máis" + description: 'Esta publicación require a atención dos responsábeis do sitio por unha razón non listada arriba.' + long_form: 'denunciou isto para revisión polo equipo' + email_title: 'Unha publicación en «%{title}» require a atención dos responsábeis do sitio' + bookmark: + title: 'Marcador' + description: 'Marcar esta publicación' + long_form: 'Marcar este tema' + like: + title: 'Gústame' + description: 'Gústame esta publicación' + long_form: 'gustoume isto' + vote: + title: 'Votar' + description: 'Votar por esta publicación' + long_form: 'votei esta publicación' + topic_flag_types: + spam: + title: 'Spam' + long_form: 'denunciou isto como spam' + inappropriate: + title: 'Inapropiado' + long_form: 'denunciou isto como inapropiado' + notify_moderators: + title: "Algo máis" + long_form: 'denunciou isto para revisión por un moderador' + archetypes: + regular: + title: "Tema normal" + banner: + title: "Tema do báner" + unsubscribed: + title: 'Dado de baixa' + description: "Démoste de baixa. Non contactaremos contigo de novo." + resubscribe: + action: "Re-subscribir" + title: "Subscrito de novo" + description: "Subscribícheste de novo" + reports: + visits: + title: "Visitas de usuarios" + xaxis: "Día" + yaxis: "Número de visitas" + signups: + title: "Novos usuarios" + xaxis: "Día" + yaxis: "Número de novos usuarios" + profile_views: + title: "Vistas dos perfís de usuario" + xaxis: "Día" + yaxis: "Número de perfís de usuario vistos" + topics: + title: "Temas" + xaxis: "Día" + yaxis: "Número de temas vistos" + posts: + title: "Publicacións" + xaxis: "Día" + yaxis: "Número de novas publicacións" + likes: + title: "Gústames" + xaxis: "Día" + yaxis: "Número de novos gústames" + flags: + title: "Denuncias" + xaxis: "Día" + yaxis: "Número de denuncias" + bookmarks: + title: "Marcadores" + xaxis: "Día" + yaxis: "Número de novos marcadores" + starred: + title: "Con estrela" + xaxis: "Día" + yaxis: "Número de novos temas con estrela" + users_by_trust_level: + title: "Usuarios por nivel de confianza" + xaxis: "Nivel de confianza" + yaxis: "Número de usuarios" + emails: + title: "Correos-e enviados" + xaxis: "Día" + yaxis: "Número de correos-e enviados" + user_to_user_private_messages: + title: "Entre usuarios" + xaxis: "Día" + yaxis: "Número de mensaxes" + system_private_messages: + title: "Sistema" + xaxis: "Día" + yaxis: "Número de mensaxes" + moderator_warning_private_messages: + title: "Avisos dun moderador" + xaxis: "Día" + yaxis: "Número de mensaxes" + notify_moderators_private_messages: + title: "Notificacións os moderadores" + xaxis: "Día" + yaxis: "Número de mensaxes" + notify_user_private_messages: + title: "Notificacións os usuarios" + xaxis: "Día" + yaxis: "Número de mensaxes" + top_referrers: + xaxis: "Usuario" + num_clicks: "Clics" + num_topics: "Temas" + top_traffic_sources: + title: "As maiores fontes de tráfico" + xaxis: "Dominio" + num_clicks: "Clics" + num_topics: "Temas" + num_users: "Usuarios" + top_referred_topics: + xaxis: "Tema" + num_clicks: "Clics" + page_view_anon_reqs: + title: "Anónimo" + xaxis: "Día" + yaxis: "Peticións API anónimas" + page_view_logged_in_reqs: + title: "Sesión iniciada" + xaxis: "Día" + page_view_crawler_reqs: + title: "Extractores web" + xaxis: "Día" + yaxis: "Peticións API dos extractores web" + page_view_total_reqs: + title: "Total" + xaxis: "Día" + yaxis: "Total de peticións API" + page_view_logged_in_mobile_reqs: + xaxis: "Día" + page_view_anon_mobile_reqs: + xaxis: "Día" + http_background_reqs: + title: "Fondo" + xaxis: "Día" + yaxis: "Peticións usadas para actualizacións e seguimentos" + http_2xx_reqs: + title: "Estado 2xx (OK)" + xaxis: "Día" + yaxis: "Peticións con éxito (estado 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (redireccionar)" + xaxis: "Día" + yaxis: "Redireccionar peticións (estado 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (erro do cliente)" + xaxis: "Día" + yaxis: "Erros do cliente (estado 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (erro do servidor)" + xaxis: "Día" + http_total_reqs: + title: "Total" + xaxis: "Día" + time_to_first_response: + xaxis: "Día" + topics_with_no_response: + xaxis: "Día" + yaxis: "Total" + mobile_visits: + title: "Visitas de usuarios" + xaxis: "Día" + yaxis: "Número de visitas" + site_settings: + delete_old_hidden_posts: "Eliminar automaticamente as publicacións ocultas durante máis de 30 días." + default_locale: "Idioma predeterminado desta instancia (código ISO 639-1)" + allow_user_locale: "Permitir os usuarios escoller o idioma da interface" + min_post_length: "Número mínimo de caracteres permitido para unha publicación" + min_first_post_length: "Número mínimo de caracteres (corpo do tema) permitido para unha primeira publicación" + min_private_message_post_length: "Número mínimo de caracteres permitido para unha mensaxe" + max_post_length: "Número máximo de caracteres permitido para unha publicación" + post_undo_action_window_mins: "Número de minutos que os usuarios teñen para desfacer accións recentes nunha publicación (gústames, denuncias, etc)." + enable_badges: "Activar o sistema de insignias" + max_flags_per_day: "Número máximo de denuncias por usuario e día." + category_style: "Estilo visual para as insignias de categoría." + enable_user_directory: "Proporcionar un directorio de usuarios para navegación" + allow_anonymous_posting: "Permitir os usuarios cambiar ao modo anónimo" + suppress_uncategorized_badge: "Non mostrar a insignia para temas sen categoría nas listas de temas." + automatically_unpin_topics: "Despegar os temas automaticamente cando o usuario chega ao fondo." + full_name_required: "O nome completo no perfil do usuario é obrigatorio." + embed_username_key_from_feed: "Clave para recuperar o nome do usuario desde a fonte." + enable_emoji: "Activar emojis" + notification_types: + liked: "A %{display_username} gustoulle a túa publicación en %{link}" + search: + types: + category: 'Categorías' + user: 'Usuarios' + frequent_poster: "Publicador frecuente" + redirected_to_top_reasons: + new_user: "Benvido/a á nosa comunidade. Estes son os temas recentes máis populares." + login: + admin_not_allowed_from_ip_address: "Non podes acceder como administrador desde este enderezo IP." + new_registrations_disabled: "Neste momento non se permite o rexistro de novas contas." + flags_dispositions: + agreed: "Grazas por comunicárnolo. Estamos de acordo en que hai un problema e estamos botándolle un ollo." + agreed_and_deleted: "Grazas por comunicárnolo. Estamos de acordo en que hai un problema e xa eliminamos a publicación." + disagreed: "Grazas por comunicárnolo. Estamos botándolle un ollo." + deferred: "Grazas por comunicárnolo. Estamos botándolle un ollo." + deferred_and_deleted: "Grazas por comunicárnolo. Xa eliminamos a publicación." + temporarily_closed_due_to_flags: "Este tema está pechado temporalmente polo gran número de denuncias da comunidade." + system_messages: + post_hidden: + subject_template: "Publicación oculta polas denuncias da comunidade." + welcome_user: + subject_template: "Benvido/a a %{site_name}!" + text_body_template: | + Grazas por unirte %{site_name}, e benvido/a! + + %{new_user_tips} + + Cremos nun [comportamento civilizado da comunidade](%{base_url}/guidelines) en todo momento. + + Goza da túa estancia! + + (Se precisas comunicarte cos [membros do equipo](%{base_url}/about) como novo usuario responde a esta mensaxe.) + welcome_invite: + subject_template: "Benvido/a a %{site_name}!" + backup_succeeded: + subject_template: "A copia de seguranza completouse correctamente" + bulk_invite_succeeded: + subject_template: "O convite en bloque procesouse correctamente" + csv_export_succeeded: + subject_template: "Completouse a exportación dos datos" + page_not_found: + see_more: "Máis" + search_google: "Google" + terms_of_service: + title: "Termos do servizo" + about: "Sobre" + guidelines: "Directrices" + privacy: "Confidencialidade" + csv_export: + boolean_yes: "Si" + boolean_no: "Non" + tos_topic: + title: "Termos do servizo" + privacy_topic: + title: "Política de intimidade" + badges: + long_descriptions: + first_like: | + Esta insignia concédese a primeira vez que che gusta unha publicación e usas o botón :heart:. Darlle Gústame a unha publicción e unha gran forma de dicirlles aos membros da comunidade que publicaron algo interesante, útil, riquiño ou divertido. Comparte o amor! + first_flag: | + Esta insignia concédese por marcar unha publicación. Marcar publicacións é fundamental para a saúde da comunidade. Se ves algunha publicación que + + requira a atención dun moderador, non dubides en marcala. Tamén podes usar o diálogo de marcar para enviar mensaxes a outros usuarios. + welcome: | + Esta insignia concédese cando recibes o primeiro Gústame nunha publicación. Parabéns, publicaches algo que os membros da comunidade consideraron interesante, riquiño ou útil. + nice_post: | + Esta insignia concédese por crear unha resposta que recibe 10 gústames. Traballo riquiño. + nice_topic: | + Esta insignia concédese por crear un tema que recibe 10 gústames. Traballo riquiño. + good_post: | + Esta insignia concédese por crear unha resposta que recibe 25 gústames. Bo traballo. + good_topic: | + Esta insignia concédese por crear un tema que recibe 25 gústames. Bo traballo. + great_post: | + Esta insignia concédese por crear unha publicación que recibe 50 gústames. Moi ben! + great_topic: | + Esta insignia concédese por crear unha resposta que recibe 50 gústames. Moi ben! + admin_login: + error: "Erro!" + submit_button: "Enviar correo-e" diff --git a/plugins/poll/config/locales/client.gl.yml b/plugins/poll/config/locales/client.gl.yml new file mode 100644 index 0000000000..48f4e872b6 --- /dev/null +++ b/plugins/poll/config/locales/client.gl.yml @@ -0,0 +1,41 @@ +gl: + js: + poll: + voters: + one: "votante" + other: "votantes" + total_votes: + one: "votos totais" + other: "votos totais" + average_rating: "Valoración media: %{average}." + multiple: + help: + at_least_min_options: + one: "Debes seleccionar cando menos 1 opción." + other: "Debes seleccionar cando menos %{count} opcións." + up_to_max_options: + one: "Podes escoller 1 única opción." + other: "Podes escoller até %{count} opcións." + x_options: + one: "Debes escoller 1 opción." + other: "Debes escoller %{count} opcións." + between_min_and_max_options: "Debes escoller entre %{min} e %{max} opcións." + cast-votes: + title: "Vota" + label: "Vota agora!" + show-results: + title: "Mostrar os resultados da votación" + label: "Mostrar os resultados" + hide-results: + title: "Volver aos teus votos" + label: "Ocultar os resultados" + open: + title: "Abrir a enquisa" + label: "Abrir" + confirm: "Confirmas a apertura da enquisa?" + close: + title: "Pechar a enquisa" + label: "Pechar" + confirm: "Confirmas o peche desta enquisa?" + error_while_toggling_status: "Produciuse un erro durante o cambio do estado da enquisa." + error_while_casting_votes: "Produciuse un erro durante a emisión do teu voto." diff --git a/plugins/poll/config/locales/server.gl.yml b/plugins/poll/config/locales/server.gl.yml new file mode 100644 index 0000000000..ac744aa6e4 --- /dev/null +++ b/plugins/poll/config/locales/server.gl.yml @@ -0,0 +1,32 @@ +gl: + site_settings: + poll_enabled: "Queres permitir os usuarios crear enquisas?" + poll_maximum_options: "Número máximo de opcións permitidas nunha enquisa." + poll: + multiple_polls_without_name: "Hai múltiples enquisas sen nome. Utiliza o atributo 'nome' para singularizar as túas enquisas." + multiple_polls_with_same_name: "Hai múltiples enquisas co mesmo nome: %{name}. Utiliza o atributo 'nome' para singularizar as túas enquisas." + default_poll_must_have_at_least_2_options: "A enquisa debe ter alomenos 2 opcións." + named_poll_must_have_at_least_2_options: "A enquisa chamada %{name} debe ter alomenos 2 opcións." + default_poll_must_have_less_options: + one: "A enquisa debe ter cando menos 1 opción." + other: "A enquisa debe ter alomenos %{count} opcións." + named_poll_must_have_less_options: + one: "A enquisa chamada %{name} debe ter menos de 1 opción." + other: "A enquisa chamada %{name} debe ter menos de %{count} opcións." + default_poll_must_have_different_options: "A enquisa debe ter diferentes opcións." + named_poll_must_have_different_options: "A enquisa chamada strong>%{name} debe ter diferentes opcións." + default_poll_with_multiple_choices_has_invalid_parameters: "A enquisa con varios apartados ten parámetros incorrectos." + named_poll_with_multiple_choices_has_invalid_parameters: "A enquisa chamada %{name} con varios apartados ten parámetros incorrectos." + requires_at_least_1_valid_option: "Debes seleccionar alomenos 1 opción válida." + cannot_change_polls_after_5_minutes: "Non podes engadir, retirar ou renomear enquisas despois dos primeiros 5 minutos." + op_cannot_edit_options_after_5_minutes: "Non podes engadir nin retirar opcións da enquisa despois de 5 minutos. Contacta cun moderador se precisas editar unha opción da enquisa." + staff_cannot_add_or_remove_options_after_5_minutes: "Non podes engadir nin retirar opcións da enquisa despois de 5 minutos. Deberías pechar este tema e crear outro novo para substituílo." + no_polls_associated_with_this_post: "Non hai enquisas asociadas con esta publicación." + no_poll_with_this_name: "Non hai ningunha enquisa chamada %{name} asociada con esta publicación." + post_is_deleted: "Non se pode actuar sobre unha publicación eliminada." + topic_must_be_open_to_vote: "O tema debe estar aberto para votalo." + poll_must_be_open_to_vote: "A enquisa debe estar aberta para votala." + topic_must_be_open_to_toggle_status: "O tema debe estar aberto para cambiarlle o estado." + only_staff_or_op_can_toggle_status: "Soamente un membro do equipo ou o publicador orixinal poden trocar o estado dunha enquisa." + email: + link_to_poll: "Preme para ver a enquisa." diff --git a/public/403.gl.html b/public/403.gl.html new file mode 100644 index 0000000000..4a2ac9508d --- /dev/null +++ b/public/403.gl.html @@ -0,0 +1,26 @@ + + +Non podes facer iso (403) + + + + +
+

403

+

Non podes ver este recurso.

+ +

Substituirase pola páxina 403 personalizada de Discourse.

+
+ + diff --git a/public/422.gl.html b/public/422.gl.html new file mode 100644 index 0000000000..98d3a1b87c --- /dev/null +++ b/public/422.gl.html @@ -0,0 +1,25 @@ + + +Rexeitouse o cambio pretendido (422) + + + + + +
+

Rexeitouse o cambio pretendido.

+

Se cadra tentaches cambiar algo ao que non tes acceso.

+
+ + diff --git a/public/500.gl.html b/public/500.gl.html new file mode 100644 index 0000000000..998201529d --- /dev/null +++ b/public/500.gl.html @@ -0,0 +1,12 @@ + + +Vaites - Erro 500 + + + +

Vaites!

+

O software con que funciona este foro de discusión atopou un problema inesperado. Sentimos as molestias.

+

Rexistrouse información detallada do erro e xerouse unha notificación automática. Revisarémolo.

+

Non é precisa ningunha outra medida. Con todo, se persisten as condicións do erro, podes subministrar información adicional, incluíndo os pasos para reproducilo, publicando un tema de discusión na metacategoría.

+ + diff --git a/public/503.gl.html b/public/503.gl.html new file mode 100644 index 0000000000..7ffae6239a --- /dev/null +++ b/public/503.gl.html @@ -0,0 +1,11 @@ + + +Este sitio está en mantemento - Discourse.org + + + +

Estamos realizando tarefas planificadas de mantemento

+

Por favor, téntao dentro duns minutos.

+

Sentimos as molestias.

+ + diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.gl.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.gl.yml new file mode 100644 index 0000000000..1f56762e30 --- /dev/null +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.gl.yml @@ -0,0 +1,5 @@ +gl: + site_settings: + enable_imgur: "Activar a api de imgur para enviar, non gardar ficheiros localmente" + imgur_client_id: "ID do teu cliente de imgur.com, requirido para que funcione o envío de imaxes" + imgur_client_secret: "O teu secreto para o cliente de imgur.com. Non requirido neste intre para enviar imaxes pero quizais posteriormente." From ec004fc66ab28b50a9772aefe2fb3bd8bae762ee Mon Sep 17 00:00:00 2001 From: Manel Villar Date: Tue, 22 Mar 2016 16:48:00 +0100 Subject: [PATCH 062/162] Support for Galician language js bundle added --- app/assets/javascripts/locales/gl.js.erb | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 app/assets/javascripts/locales/gl.js.erb diff --git a/app/assets/javascripts/locales/gl.js.erb b/app/assets/javascripts/locales/gl.js.erb new file mode 100644 index 0000000000..f38e7660de --- /dev/null +++ b/app/assets/javascripts/locales/gl.js.erb @@ -0,0 +1,3 @@ +//= depend_on 'client.gl.yml' +//= require locales/i18n +<%= JsLocaleHelper.output_locale(:gl) %> From f0fafd5d78d5a3f53c9916fc88162a9a965e98c4 Mon Sep 17 00:00:00 2001 From: Tomas Ibarra Date: Tue, 22 Mar 2016 11:03:57 -0700 Subject: [PATCH 063/162] Update VAGRANT.md, link to become eviltrout admin. --- docs/VAGRANT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/VAGRANT.md b/docs/VAGRANT.md index b22a77cc30..865eaab8b1 100644 --- a/docs/VAGRANT.md +++ b/docs/VAGRANT.md @@ -94,7 +94,7 @@ In a few seconds, rails will start serving pages. To access them, open a web bro If you want to log in as a user, a shortcut you can use in development mode is to follow this link to log in as `eviltrout`: -http://localhost:4000/session/eviltrout/become +[http://localhost:4000/session/eviltrout/become](http://localhost:4000/session/eviltrout/become) You can now edit files on your local file system, using your favorite text editor or IDE. When you reload your web browser, it should have the latest changes. From 9acaecac4a19c43c44c5eef8241e658073eec781 Mon Sep 17 00:00:00 2001 From: James Kiesel Date: Wed, 23 Mar 2016 08:10:15 +1300 Subject: [PATCH 064/162] Fix for long emoji names in autocomplete --- .../templates/emoji-selector-autocomplete.raw.hbs | 3 ++- app/assets/stylesheets/common/base/emoji.scss | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/templates/emoji-selector-autocomplete.raw.hbs b/app/assets/javascripts/discourse/templates/emoji-selector-autocomplete.raw.hbs index ac1f2a4755..11a51ce6b7 100644 --- a/app/assets/javascripts/discourse/templates/emoji-selector-autocomplete.raw.hbs +++ b/app/assets/javascripts/discourse/templates/emoji-selector-autocomplete.raw.hbs @@ -4,7 +4,8 @@
  • {{#if option.src}} - {{option.code}} + + {{option.code}} {{else}} {{option.label}} {{/if}} diff --git a/app/assets/stylesheets/common/base/emoji.scss b/app/assets/stylesheets/common/base/emoji.scss index c11d968702..e97a2d186a 100644 --- a/app/assets/stylesheets/common/base/emoji.scss +++ b/app/assets/stylesheets/common/base/emoji.scss @@ -100,3 +100,11 @@ table.emoji-page td { .emoji-modal .nav a { color: dark-light-choose(#333, #ccc); } + +.emoji-shortname { + display: inline-block; + max-width: 200px; + text-overflow: ellipsis; + overflow: hidden; + vertical-align: middle; +} From 86d2773d3a73c63dd57c180a4de16310014af307 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 22 Mar 2016 16:36:20 -0400 Subject: [PATCH 065/162] FIX: Don't process infinite scrolling while safari hacks are active --- .../discourse/components/scrolling-post-stream.js.es6 | 2 ++ app/assets/javascripts/discourse/lib/safari-hacks.js.es6 | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 b/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 index e5303d3d84..38ad5fea69 100644 --- a/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 +++ b/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 @@ -2,6 +2,7 @@ import DiscourseURL from 'discourse/lib/url'; import { keyDirty } from 'discourse/widgets/widget'; import MountWidget from 'discourse/components/mount-widget'; import { cloak, uncloak } from 'discourse/widgets/post-stream'; +import { isWorkaroundActive } from 'discourse/lib/safari-hacks'; function findTopView($posts, viewportTop, min, max) { if (max < min) { return min; } @@ -38,6 +39,7 @@ export default MountWidget.extend({ scrolled() { if (this.isDestroyed || this.isDestroying) { return; } + if (isWorkaroundActive()) { return; } const $w = $(window); const windowHeight = window.innerHeight ? window.innerHeight : $w.height(); diff --git a/app/assets/javascripts/discourse/lib/safari-hacks.js.es6 b/app/assets/javascripts/discourse/lib/safari-hacks.js.es6 index 3506b06d6b..888bf34fc6 100644 --- a/app/assets/javascripts/discourse/lib/safari-hacks.js.es6 +++ b/app/assets/javascripts/discourse/lib/safari-hacks.js.es6 @@ -6,6 +6,11 @@ function applicable() { !navigator.userAgent.match(/Trident/g); } +let workaroundActive = false; +export function isWorkaroundActive() { + return workaroundActive; +} + // per http://stackoverflow.com/questions/29001977/safari-in-ios8-is-scrolling-screen-when-fixed-elements-get-focus/29064810 function positioningWorkaround($fixedElement) { if (!applicable()) { @@ -37,12 +42,12 @@ function positioningWorkaround($fixedElement) { if (evt) { evt.target.removeEventListener('blur', blurred); } + workaroundActive = false; }; var blurred = _.debounce(blurredNow, 250); var positioningHack = function(evt){ - const self = this; done = false; @@ -76,6 +81,7 @@ function positioningWorkaround($fixedElement) { evt.preventDefault(); self.focus(); + workaroundActive = true; }; function attachTouchStart(elem, fn) { From 0b558f5874f994e0abcc9d83d1a9a7e4fc6799a4 Mon Sep 17 00:00:00 2001 From: James Kiesel Date: Wed, 23 Mar 2016 14:45:22 +1300 Subject: [PATCH 066/162] Fix for category stripe misalignment --- lib/category_badge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/category_badge.rb b/lib/category_badge.rb index 3175372d9c..196d17691a 100644 --- a/lib/category_badge.rb +++ b/lib/category_badge.rb @@ -50,7 +50,7 @@ module CategoryBadge case (SiteSetting.category_style || :box).to_sym when :bar then inline_category_stripe(category.color, 'display: inline-block; padding: 1px;', true) when :box then inline_category_stripe(category.color, 'left: 5px; display: block; position: absolute; width: calc(100% - 5px); height: 100%;') - when :bullet then inline_category_stripe(category.color, "display: inline-block; width: #{category.parent_category_id.nil? ? 10 : 5}px; height: 10px;", true) + when :bullet then inline_category_stripe(category.color, "display: inline-block; width: #{category.parent_category_id.nil? ? 10 : 5}px; height: 10px;") end else category_stripe(category.color, 'badge-category-bg') From c095304d6df578edff399cf19876385e9aabaf06 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 23 Mar 2016 15:08:34 +1100 Subject: [PATCH 067/162] FEATURE: limit daily emails per user to 100 per day via site setting - controlled via max_emails_per_day_per_user, 0 to disable - when limit is reached user is warned --- .../notify_mailing_list_subscribers.rb | 15 +++++++++-- app/jobs/regular/user_email.rb | 4 +++ app/mailers/user_notifications.rb | 9 ++++++- app/models/email_log.rb | 10 +++++++ app/views/email/notification.html.erb | 7 +++-- config/locales/server.en.yml | 5 ++++ config/site_settings.yml | 1 + .../notify_mailing_list_subscribers_spec.rb | 27 ++++++++++++++++++- spec/jobs/user_email_spec.rb | 13 ++++++++- spec/mailers/user_notifications_spec.rb | 25 +++++++++++++++++ spec/models/email_log_spec.rb | 15 +++++++++++ 11 files changed, 124 insertions(+), 7 deletions(-) diff --git a/app/jobs/regular/notify_mailing_list_subscribers.rb b/app/jobs/regular/notify_mailing_list_subscribers.rb index 540b472c23..c273ce0e47 100644 --- a/app/jobs/regular/notify_mailing_list_subscribers.rb +++ b/app/jobs/regular/notify_mailing_list_subscribers.rb @@ -35,8 +35,19 @@ module Jobs users.each do |user| if Guardian.new(user).can_see?(post) begin - message = UserNotifications.mailing_list_notify(user, post) - Email::Sender.new(message, :mailing_list, user).send if message + if EmailLog.reached_max_emails?(user) + EmailLog.create!( + email_type: 'mailing_list', + to_address: user.email, + user_id: user.id, + post_id: post.id, + skipped: true, + skipped_reason: "[MailingList] #{I18n.t('email_log.exceeded_limit')}" + ) + else + message = UserNotifications.mailing_list_notify(user, post) + Email::Sender.new(message, :mailing_list, user).send if message + end rescue => e Discourse.handle_job_exception(e, error_context(args, "Sending post to mailing list subscribers", { user_id: user.id, user_email: user.email })) end diff --git a/app/jobs/regular/user_email.rb b/app/jobs/regular/user_email.rb index ca7b73dc71..160629dfef 100644 --- a/app/jobs/regular/user_email.rb +++ b/app/jobs/regular/user_email.rb @@ -113,6 +113,10 @@ module Jobs email_args[:new_email] = user.email end + if EmailLog.reached_max_emails?(user) + return skip_message(I18n.t('email_log.exceeded_limit')) + end + message = UserNotifications.send(type, user, email_args) # Update the to address if we have a custom one diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index 943b2a127a..f549b606c2 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -317,6 +317,11 @@ class UserNotifications < ActionMailer::Base end end + reached_limit = SiteSetting.max_emails_per_day_per_user > 0 + reached_limit &&= (EmailLog.where(user_id: user.id, skipped: false) + .where('created_at > ?', 1.day.ago) + .count) >= (SiteSetting.max_emails_per_day_per_user-1) + topic_excerpt = "" if opts[:use_template_html] topic_excerpt = post.excerpt.gsub("\n", " ") if post.is_first_post? && post.excerpt @@ -326,6 +331,7 @@ class UserNotifications < ActionMailer::Base template: 'email/notification', format: :html, locals: { context_posts: context_posts, + reached_limit: reached_limit, post: post, in_reply_to_post: in_reply_to_post, classes: RTL.new(user).css_class @@ -339,10 +345,11 @@ class UserNotifications < ActionMailer::Base template << "_staged" if user.staged? end + email_opts = { topic_title: title, topic_excerpt: topic_excerpt, - message: email_post_markdown(post), + message: email_post_markdown(post) + (reached_limit ? "\n\n#{I18n.t "user_notifications.reached_limit", count: SiteSetting.max_emails_per_day_per_user}" : ""), url: post.url, post_id: post.id, topic_id: post.topic_id, diff --git a/app/models/email_log.rb b/app/models/email_log.rb index 7a8e9ef667..eb6c1a3173 100644 --- a/app/models/email_log.rb +++ b/app/models/email_log.rb @@ -13,6 +13,16 @@ class EmailLog < ActiveRecord::Base User.where(id: user_id).update_all("last_emailed_at = CURRENT_TIMESTAMP") if user_id.present? && !skipped end + def self.reached_max_emails?(user) + return false if SiteSetting.max_emails_per_day_per_user == 0 + + count = sent.where('created_at > ?', 1.day.ago) + .where(user_id: user.id) + .count + + count >= SiteSetting.max_emails_per_day_per_user + end + def self.count_per_day(start_date, end_date) sent.where("created_at BETWEEN ? AND ?", start_date, end_date) .group("DATE(created_at)") diff --git a/app/views/email/notification.html.erb b/app/views/email/notification.html.erb index 363fa65ca4..d4c200807b 100644 --- a/app/views/email/notification.html.erb +++ b/app/views/email/notification.html.erb @@ -10,12 +10,12 @@ <% end %> <% if in_reply_to_post.present? %> -

    <%= t "user_notifications.in_reply_to" %>

    +

    <%= t "user_notifications.in_reply_to" %>

    <%= render partial: 'email/post', locals: { post: in_reply_to_post, use_excerpt: true} %> <% end %> <% if context_posts.present? %> -

    <%= t "user_notifications.previous_discussion" %>

    +

    <%= t "user_notifications.previous_discussion" %>

    <% context_posts.each do |p| %> <%= render partial: 'email/post', locals: { post: p, use_excerpt: false } %> <% end %> @@ -23,6 +23,9 @@
    + <% if reached_limit %> + + <% end %> diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index c6dc3a4c45..6d6505b781 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1161,6 +1161,7 @@ en: unsubscribe_via_email_footer: "Attach an unsubscribe link to the footer of sent emails" delete_email_logs_after_days: "Delete email logs after (N) days. 0 to keep indefinitely" + max_emails_per_day_per_user: "Maximum number of emails to send users per day. 0 to disable the limit" manual_polling_enabled: "Push emails using the API for email replies." pop3_polling_enabled: "Poll via POP3 for email replies." @@ -2037,6 +2038,9 @@ en: user_notifications: previous_discussion: "Previous Replies" + reached_limit: + one: "WARNING: you reached the limit of daily emails. Further email notifications will be suppressed." + other: "WARNING: you reached the limit of %{count} daily emails. Further email notifications will be suppressed." in_reply_to: "In Reply To" unsubscribe: title: "Unsubscribe" @@ -2363,6 +2367,7 @@ en: post_deleted: "post was deleted by the author" user_suspended: "user was suspended" already_read: "user has already read this post" + exceeded_limit: "Exceeded max_emails_per_day_per_user" message_blank: "message is blank" message_to_blank: "message.to is blank" text_part_body_blank: "text_part.body is blank" diff --git a/config/site_settings.yml b/config/site_settings.yml index dd1e45019a..096b4eab5a 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -559,6 +559,7 @@ email: delete_email_logs_after_days: default: 365 min: 0 + max_emails_per_day_per_user: 100 files: max_image_size_kb: 3072 diff --git a/spec/jobs/notify_mailing_list_subscribers_spec.rb b/spec/jobs/notify_mailing_list_subscribers_spec.rb index d3a0022f1f..f63b4225cc 100644 --- a/spec/jobs/notify_mailing_list_subscribers_spec.rb +++ b/spec/jobs/notify_mailing_list_subscribers_spec.rb @@ -3,9 +3,34 @@ require "rails_helper" describe Jobs::NotifyMailingListSubscribers do context "with mailing list on" do - before { SiteSetting.stubs(:default_email_mailing_list_mode).returns(true) } + before { SiteSetting.default_email_mailing_list_mode = true } let(:user) { Fabricate(:user) } + context "SiteSetting.max_emails_per_day_per_user" do + + it 'stops sending mail once limit is reached' do + SiteSetting.max_emails_per_day_per_user = 2 + post = Fabricate(:post) + + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id) + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id) + + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + expect(EmailLog.where(user_id: user.id, skipped: true).count).to eq(1) + end + end + + context "totally skipped if mailing list mode disabled" do + + it "sends no email to the user" do + SiteSetting.disable_mailing_list_mode = true + + post = Fabricate(:post) + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + expect(EmailLog.count).to eq(0) + end + end + context "with a valid post" do let!(:post) { Fabricate(:post, user: user) } diff --git a/spec/jobs/user_email_spec.rb b/spec/jobs/user_email_spec.rb index 92fd5abd2a..e7405bade7 100644 --- a/spec/jobs/user_email_spec.rb +++ b/spec/jobs/user_email_spec.rb @@ -193,6 +193,17 @@ describe Jobs::UserEmail do Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id) end + it "does not send notification if limit is reached" do + SiteSetting.max_emails_per_day_per_user = 2 + + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id) + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id) + + Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) + + expect(EmailLog.where(user_id: user.id, skipped: true).count).to eq(1) + end + it "doesn't send the mail if the user is using mailing list mode" do Email::Sender.any_instance.expects(:send).never user.user_option.update_column(:mailing_list_mode, true) @@ -205,7 +216,7 @@ describe Jobs::UserEmail do # When post does not have a topic post = Fabricate(:post) post.topic.destroy - Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post) + Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_type: "posted", post_id: post.id) end it "doesn't send the email if the post has been user deleted" do diff --git a/spec/mailers/user_notifications_spec.rb b/spec/mailers/user_notifications_spec.rb index ebc9b27e4c..00ceb74a3a 100644 --- a/spec/mailers/user_notifications_spec.rb +++ b/spec/mailers/user_notifications_spec.rb @@ -243,6 +243,31 @@ describe UserNotifications do end end + + it 'adds a warning when mail limit is reached' do + SiteSetting.max_emails_per_day_per_user = 2 + user = Fabricate(:user) + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id, skipped: false) + + post = Fabricate(:post) + reply = Fabricate(:post, topic_id: post.topic_id) + + notification = Fabricate(:notification, topic_id: post.topic_id, post_number: reply.post_number, + user: post.user, data: {original_username: 'bob'}.to_json) + + mail = UserNotifications.user_replied( + user, + post: reply, + notification_type: notification.notification_type, + notification_data_hash: notification.data_hash + ) + + # WARNING: you reached the limit of 100 email notifications per day. Further emails will be suppressed. + # Consider watching less topics or disabling mailing list mode. + expect(mail.html_part.to_s).to match("WARNING: ") + expect(mail.body.to_s).to match("WARNING: ") + end + def expects_build_with(condition) UserNotifications.any_instance.expects(:build_email).with(user.email, condition) mailer = UserNotifications.send(mail_type, user, diff --git a/spec/models/email_log_spec.rb b/spec/models/email_log_spec.rb index 8c33e2b5f8..3b5ca7df46 100644 --- a/spec/models/email_log_spec.rb +++ b/spec/models/email_log_spec.rb @@ -26,6 +26,21 @@ describe EmailLog do end end + describe '#reached_max_emails?' do + it "tracks when max emails are reached" do + SiteSetting.max_emails_per_day_per_user = 2 + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id, skipped: true) + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id) + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id, created_at: 3.days.ago) + + expect(EmailLog.reached_max_emails?(user)).to eq(false) + + user.email_logs.create(email_type: 'blah', to_address: user.email, user_id: user.id) + + expect(EmailLog.reached_max_emails?(user)).to eq(true) + end + end + describe '#count_per_day' do it "counts sent emails" do user.email_logs.create(email_type: 'blah', to_address: user.email) From cc07f421f1145ae12d97b11b3f56084c89c4d288 Mon Sep 17 00:00:00 2001 From: jomaxro Date: Wed, 23 Mar 2016 00:21:15 -0400 Subject: [PATCH 068/162] Update automated account blocked by staff PM When a user's account is blocked by staff, they receive an automated PM informing them of such. Unlike the new user too many flags PM, this PM does not inform that user what blocked means. This PR adds two lines to the PM, one explaining what blocking means, the other informing the user to contact staff with questions. --- config/locales/server.en.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 6d6505b781..c293a5b58e 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1981,6 +1981,10 @@ en: This is an automated message from %{site_name} to inform you that your account has been blocked by a staff member. + Please be aware that you will not be able to create new replies or topics until unblocked by a staff member. + + If you have questions regarding this block, please contact a [staff members](%{base_url}/about). + For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines). user_automatically_blocked: From 2862c4e5a3027fd7f2bcb552bfddab3c0b09a967 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Wed, 23 Mar 2016 13:59:17 +0800 Subject: [PATCH 069/162] FIX: Quote reply button was covered by iOS menu. --- .../javascripts/discourse/controllers/quote-button.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 index b946cef8ea..53f711f8d3 100644 --- a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 +++ b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 @@ -85,7 +85,7 @@ export default Ember.Controller.extend({ let topOff = markerOffset.top; let leftOff = markerOffset.left; - if (isMobileDevice) { + if (isMobileDevice || this.capabilities.isIOS) { topOff = topOff + 20; leftOff = Math.min(leftOff + 10, $(window).width() - $quoteButton.outerWidth()); } else { From 6dd6bbd5095659f46cfadc149d18902253c2eb02 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Wed, 23 Mar 2016 14:28:17 +0800 Subject: [PATCH 070/162] FIX: Incorrect scope. --- .../javascripts/discourse/controllers/quote-button.js.es6 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 index 53f711f8d3..c1ef10fdea 100644 --- a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 +++ b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 @@ -62,6 +62,7 @@ export default Ember.Controller.extend({ markerElement.appendChild(document.createTextNode("\ufeff")); const isMobileDevice = this.site.isMobileDevice; + const isIOS = this.capabilities.isIOS; // collapse the range at the beginning/end of the selection range.collapse(!isMobileDevice); @@ -85,7 +86,7 @@ export default Ember.Controller.extend({ let topOff = markerOffset.top; let leftOff = markerOffset.left; - if (isMobileDevice || this.capabilities.isIOS) { + if (isMobileDevice || isIOS) { topOff = topOff + 20; leftOff = Math.min(leftOff + 10, $(window).width() - $quoteButton.outerWidth()); } else { From 1b8010ed71d2bf23bf1c458a5095ea6e54bf1890 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 23 Mar 2016 17:27:03 +1100 Subject: [PATCH 071/162] display long description for badges --- .../discourse/controllers/badges/show.js.es6 | 6 +++++- .../javascripts/discourse/templates/badges/show.hbs | 11 ++++++----- app/assets/stylesheets/common/base/user-badges.scss | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 index afe640bc63..ad83dd8031 100644 --- a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 +++ b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 @@ -61,8 +61,12 @@ export default Ember.Controller.extend({ this.set("controllers.application.showFooter", !this.get("canLoadMore")); }.observes("canLoadMore"), + longDescription: function(){ + return Discourse.Emoji.unescape(this.get('model.long_description')); + }.property('model.long_description'), + showLongDescription: function(){ - return window.location.search.match("long-description"); + return this.get('model.long_description'); }.property('userBadges') }); diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs index e41eb32ef1..2c30458fae 100644 --- a/app/assets/javascripts/discourse/templates/badges/show.hbs +++ b/app/assets/javascripts/discourse/templates/badges/show.hbs @@ -5,6 +5,12 @@ {{model.displayName}} + {{#if showLongDescription}} + + {{/if}} +
    {{user-badge badge=model}}
    @@ -18,11 +24,6 @@
    - {{#if showLongDescription}} - - {{/if}} {{#if user}}
  • rainbows category:"parks and gardens" in:bookmarks irá retornar uma pesquisa de tópicos que contêm a palavra “rainbows” na categoria “parks and gardens” e que estão marcados por si.
  • - badges: - long_descriptions: - autobiographer: | - Este distintivo é concedido por preencher o seu perfil de utilizador e por selecionar uma imagem de perfil. Deixar a comunidade saber mais sobre quem você é e no que está interessado torna a comunidade melhor e mais ligada. - first_like: | - Este distintivo é concedido na primeira vez que gosta de uma mensagem utilizando o botão :heart:. Gostar de mensagens é uma excelente maneira de deixar os seus companheiros membros da comunidade saberem que o que eles postaram foi interessante, útil, giro, ou divertido. Partilhe o amor! - first_link: | - Este distintivo é concedido na primeira vez que coloca uma hiperligação para outro tópico numa resposta. Ligar tópicos ajuda os leitores a encontrarem conversas relacionadas interessantes, ao exibir ligação entre tópicos em ambas as direções. - first_quote: | - Este distintivo é concedido na primeira vez que cita uma mensagem na sua resposta. Citar secções relevantes de mensagens anteriores na sua resposta ajuda a manter as discussões focadas e no tópico. - first_share: | - Este distintivo é concedido na primeira vez que partilha uma hiperligação para uma mensagem ou tópico utilizando o botão de partilha. Partilhar hiperligações é uma excelente maneira de mostrar discussões interessantes com o resto do mundo e de fazer crescer a sua comunidade. - read_guidelines: | - Este distintivo é concedido por ler as diretrizes da comunidade. Seguir e partilhar estas simples diretrizes ajuda a construir uma comunidade segura, divertida e sustentável. - reader: | - Este distintivo é concedido ao ler um tópico longo. Ler é fundamental. Ler atentamente ajuda-o a seguir a conversação e leva-o a respostas melhores e mais completas. - editor: | - Este distintivo é concedido ao editar a sua mensagem. Não hesite em editar as suas mensagens em qualquer altura para melhorá-las, corrigir pequenos erros, ou adicionar algo que se tenha esquecido. - first_flag: | - Este distintivo é concedido ao sinalizar uma mensagem. Sinalizar é crítico para a saúde da sua comunidade. Se notar em alguma mensagem que necessite de atenção do moderador por favor - não hesite em sinalizá-la. Pode também utilizar o diálogo de sinalização para enviar mensagens para colegas utilizadores. - nice_share: | - Este distintivo é concedido ao partilhar uma hiperligação para uma mensagem que seja visitada por 25 visitantes externos. Bom trabalho! Partilhar hiperligações para discussões interessantes com amigos é uma excelente maneira de fazer crescer a nossa comunidade. - welcome: | - Este distintivo é concedido quando recebe o seu primeiro gosto numa mensagem. Parabéns, publicou algo que os colegas membros da comunidade acham interessante, giro, ou útil! - anniversary: | - Este distintivo é concedido quando tiver sido membro por um ano com pelo menos uma mensagem nesse ano. Obrigado por ficar por cá e por contribuir para a nossa comunidade! - good_share: | - Este distintivo é concedido por partilhar uma hiperligação para uma mensagem que seja visitada por 300 visitantes externos. Bom trabalho! Mostrou uma discussão interessante a lotnovas pessoas e ajudou-nos a crescer. - great_share: | - Este distintivo é concedido por partilhar uma hiperligação para uma mensagem que seja visitada por 100 visitantes externos. Uau! Promoveu uma discussão interessante junto de uma enorme nova audiência para esta comunidade, e ajudou-nos a crescer de uma forma exponencial. - nice_post: | - Este distintivo é concedido ao criar uma resposta que obtém 10 gostos. Bom trabalho! - nice_topic: | - Este distintivo é concedido ao criar um tópico que obtém 10 gostos. Bom trabalho! - good_post: | - Este distintivo é concedido ao criar uma resposta que obtém 25 gostos. Bom trabalho! - good_topic: |+ - Este distintivo é concedido ao criar um tópico que obtém 25 gostos. Bom trabalho! - - great_post: | - Este distintivo é concedido ao criar uma mensagem que obtém 50 gostos. Uau! - great_topic: | - Este distintivo é concedido ao criar uma resposta que obtém 50 gostos. Uau! - basic: | - Este distintivo é concedido quando atinge o nível de confiança 1. Obrigado por ficar por cá durante algum tempo e por ler alguns tópicos para aprender do que se trata a nossa comunidade. As suas restrições de novo utilizador foram removidas, e foram-lhe atribuídas habilidades essenciais da comunidade, tais como mensagens pessoais, sinalização, edição da wiki, e a habilidade de publicar imagens e múltiplas hiperligações. - member: | - Este distintivo é concedido quando atinge o nível de confiança 2. Obrigado por participar durante um período de semanas para se juntar à nossa comunidade. Agora pode enviar convites pessoais da sua página de utilizador ou tópicos individuais, criar mensagens de grupo, e mais alguns gostos por dia! - regular: | - Este distintivo é concedido quando atingir o nível de confiança 3. Obrigado por ser uma parte regular da nossa comunidade por um período de meses, um dos leitores mais ativos e um contribuinte fiável no que torna a comunidade fantástica. Agora pode recategorizar e renomear tópicos, aceder à área do salão privado, sinalizações de spam mais poderosas, e muitos mais gostos por dia. - leader: | - Este distintivo é concedido quando atinge o nível de confiança 4. É um líder nesta comunidade, selecionado pelo pessoal, e define um exemplo positivo para a comunidade nos seus trabalhos e palavras. Tem a habilidade de editar todas as mensagens, realizar ações de moderador de tópicos tais como fixar, fechar, arquivar, dividir e unir, e toneladas de gostos por dia. admin_login: success: "Email Enviado" error: "Erro!" diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index 5b3dd1c3bd..8ee0083147 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -861,15 +861,9 @@ pt_BR: tl2_requires_likes_received: "Quantos likes um usuário deve receber antes de ser promovido a nível de confiança 2." tl2_requires_likes_given: "Quantos likes um usuário deve dar antes de ser promovido a nível de confiança 2." tl2_requires_topic_reply_count: "Quantos tópicos um usuário deve responder antes de ser promovido a nível de confiança 2." - tl3_requires_topics_replied_to: "Número mínimo de tópicos que um usuário precisa ter respondido o site nos últimos 100 dias para se qualificar a promoção ao nível de confiança 3. (0 ou mais)" - tl3_requires_topics_viewed: "O percentual de tópicos criados nos últimos 100 dias que um usuário precisa ter visto para se qualificar a promoção ao nível de confiança 3. (0 até 100)" - tl3_requires_posts_read: "O percentual de posts criados nos últimos 100 dias que um usuário precisa ter visto para se qualificar a promoção ao nível de confiança 3. (0 até 100)" tl3_requires_topics_viewed_all_time: "O mínimo total de tópicos que um usuário precisa ter visto para se qualificar a promoção ao nível de confiança 3." tl3_requires_posts_read_all_time: "O mínimo total de postagens que um usuário precisa ter lido para se qualificar a promoção ao nível de confiança 3." - tl3_requires_max_flagged: "Usuário precisa não ter mais que x postagens sinalizadas por x usuários diferentes nos últimos 100 dias para se qualificar à promoção ao nível de confiança 3, onde x é o valor dessa configuração. (0 ou mais)" tl3_promotion_min_duration: "O número mínimo de dias que uma promoção ao nível 3 dura antes que um usuário possa ser rebaixado novamente ao nível de confiança 2." - tl3_requires_likes_given: "O número mínimo de likes que devem ser dados nos últimos 100 dias para qualificar para promoção ao nível de confiança 3." - tl3_requires_likes_received: "O número mínimo de likes que devem ser recebidos nos últimos 100 dias para se qualificar para promoção ao nível de confiança 3." tl3_links_no_follow: "Não remover rel=nofollow de links postados por usuários do nível de confiança 3." min_trust_to_create_topic: "O nível de confiança mínimo necessário para criar um novo tópico." min_trust_to_edit_wiki_post: "O nível de confiança mínimo necessário para editar uma postagem marcada como wiki." @@ -1031,11 +1025,6 @@ pt_BR: category: 'Categorias' topic: 'Resultados' user: 'Utilizadores' - sso: - not_found: "Não foi possível procurar ou criar conta, contate o administrador do site" - account_not_approved: "A conta está pendente de aprovação, você receberá uma notificação por email assim que for aprovada" - unknown_error: "Erro ao fazer update de informação, contate o administrador do site" - timeout_expired: "O tempo de login ultrapassou o limite, por favor tente logar de novo" original_poster: "Postador original" most_posts: "Maior parte das postagens" most_recent_poster: "Maior parte das Postagens Recentes" @@ -1310,7 +1299,6 @@ pt_BR: Para informações adicionais, por favor consulte as nossas [diretrizes da comunidade](%{base_url}/guidelines). blocked_by_staff: subject_template: "Conta bloqueada" - text_body_template: "Olá, \n\nEsta é uma mensagem automática do %{site_name} para informá-lo de que a sua conta foi bloqueada por um membro da administração. \n\nPara informações adicionais, por favor consulte as nossas [diretrizes da comunidade](%{base_url}/guidelines).\n" user_automatically_blocked: subject_template: "Novo usuário %{username} foi bloqueado devido a denúncias da comunidade" spam_post_blocked: @@ -1341,33 +1329,6 @@ pt_BR: posted_by: "Postado por %{username} em %{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} convidou você para uma mensagem '%{topic_title}'" - text_body_template: |2 - - %{username} convidou você para uma mensagem - - > **%{topic_title}** - > - > %{topic_excerpt} - - a - - > %{site_title} -- %{site_description} - - Por favor visitar este link para visualizar a mensagem: %{base_url}%{url} - user_invited_to_topic: - text_body_template: |2 - - %{username} convidou você para uma discussão - - > **%{topic_title}** - > - > %{topic_excerpt} - - em - - > %{site_title} -- %{site_description} - - Por favor visitar o link para visualizar a mensagem: %{base_url}%{url} user_replied: subject_template: "[%{site_name}] %{topic_title}" user_quoted: @@ -1502,57 +1463,6 @@ pt_BR: title: "Termos de Serviço" privacy_topic: title: "Política de Privacidade" - badges: - long_descriptions: - autobiographer: | - Este emblema é concedido por preencher seu perfil e selecionar uma foto de perfil. Deixar a comunidade conhecer mais sobre quem você é, e, quais são seus interesses, a torna melhor e mais conectada. - first_like: | - Este emblema é concedito na primeira vez que você curtir uma m,ensagem usando o botão :heart: . Curtir mensagens é uma ótima maneira de dizer aos membros da sua comunidade que suas mensagens foram interessantes, úteis, legais ou divertidas. Compatilhe o amor! - first_link: | - Este emblema é concedido na primeira vez que você põe um link para outro tópico numa mensagem. Linkar tópicos ajuda outros leitores a encontrar conversas interessantes relacionadas, monstrando ligações entre tópicos em ambas direções. - first_quote: | - Este emblema é concedido na primeira vez que você cita uma mensagem na sua resposta. Citar seções relevantes de mensagens anteriories na sua resposta ajuda a manter a discussão focada e no assunto original. - first_share: | - Este emblema é concedido na primeira vez que você compartilha um link para uma mensagem ou tópico usando o botão compartilhar. Compartilhar links é uma ótima maneira de mostrar discussões interessantes com o resto do mundo e assim, crescer sua comunidade. - read_guidelines: | - Este emblema é concedido por ler o guia da comunidade. Seguir e compartilhar estas simples regras ajuda a construir uma segura, divertida e sustentável comunidade. - reader: | - Este emblema é concedido por ler um tópico longo. Ler é fundamental. Ler atentamente ajuda a seguir a conversa e leva a melhores, e mais completas, respostas. - editor: | - Este emblema é concedido por editar sua mensagem. Não exite em editar sua mensagem a qualquer tempo para melhora-la , consertar pequenos enganos, ou adicionar qualquer coisa que você tenha esquecido. - first_flag: | - Este emblema é concedido por sinalizar uma mensagem. Sinalizar é critico para saúde da comunidade. Se você notar qualquer mensagem que necessita de atenção da moderação, por favor, não exite em sinalizar. Você pode também usar o diálogo de sinalização para enviar mensagem para outros usuários. - nice_share: | - Este emblema é concedido por compartilhar um link para uma mensagem que foi visitada por 25 visitantes externos. Bom trabalho! Compartilhar links para discussões interessantes é uma excelente forma de crescer sua comunidade. - welcome: | - Este emblema é concedido quando você recebe sua primeira curtida numa mensagem. Parabéns, você postou algo que sua comunidade achou interessante, legal ou divertido! - anniversary: | - Este emblema é concedido quanto você é membro por um ano com pelo menos uma mensagem nesse ano. Obrigado por fica por aqui e contribuir com nossa comunidade! - good_share: | - Este emblema é concedido por compartilhar um link de uma mensagem que foi visitada por 300 visitantes externos. Bom Trabalho! Você mostrou uma discussão interessante para um monte de novas pessoas e nos ajudou a crescer. - great_share: | - Este emblema é concedido por compartilhar um link de uma mensagem que foi visitada por 100 visitantes externos. Nossa! Você promoveu uma discussão interessante para uma grande nova audiência para esta comunidade, e nos ajudou a crescer bastante. - nice_post: | - Este emblema é concedido por criar uma resposta que conseguiu 10 curtidas. Bom trabalho! - nice_topic: | - Este emblema é concedido por criar um tópico que conseguiu 10 curtidas. Bom trabalho! - good_post: | - Este emblema é concedido por criar uma resposta que conseguiu 25 curtidas. Bom trabalho! - good_topic: | - Este emblema é concedido por criar um tópico que conseguiu 25 curtidas. Bom trabalho! - great_post: | - Este emblema é concedido por criar um tópico que conseguiu 50 curtidas. Uau! - great_topic: | - Este emblema é concedido por criar uma resposta que conseguiu 50 curtidas. Uau! - basic: | - Este emblema é concedido quando você alcança o nível de confiança 1. Obrigado por permanecer por aqui e ler alguns tópicos para aprender sobre o que é nossa comunidade. Suas restrições de novo usuário foram suspensas, e você ganhou todas habilidades essenciais de comunidade, como mensagens pessoais, sinalizações, edições de wiki, e habilidade de postar imagens e múltiplos links. - member: | - Este emblema é concedido quando você alcança o nível de confiança 2. Obrigado por participar durante essas semanas da nossa comuniadde. Você pode agora enviar convites pessoais da sua página de usuário ou de tópocos individuais, criar mensagens de grupo, e algumas curtidas a mais por dia. - regular: |+ - Este emblema é concedido quando você alcança o nível de confiança 3. Obrigado por ser parte regular da nossa comunidade durante todos esses meses, um dos mais ativos leitores e colaborador confiável para o que faz essa comunidade grande. Você pode agora recategorizar e renomear tópicos, acessar a área privada do lounge, sinalizações mais fortes de spam, e muito mais curtidas por dia. - - leader: | - Este emblema é concedido quando você alcança o nível de confiança 4. Você é um líder desta comunidade selecionado pela equipe, e define um exemplo positivo em suas ações e palavras. Você tem habilidade de editar todos tópicos, agir como moderador nas ações como fixar, fechar, delistar, arquivar, separar, agrupar e vários curtidas a mais por dia. admin_login: success: "Email Enviado" error: "Erro!" diff --git a/config/locales/server.ro.yml b/config/locales/server.ro.yml index a2e5670033..4b35e601d5 100644 --- a/config/locales/server.ro.yml +++ b/config/locales/server.ro.yml @@ -870,12 +870,6 @@ ro: Pentru ajutor adițional, faceți referie la [regulile de comunitate](%{base_url}/). blocked_by_staff: subject_template: "Cont blocat" - text_body_template: | - Salut, - - Acesta e un mesaj automat din partea %{site_name} pentru a vă informa de blocarea contului dvs de către un membru al personalului. - - Pentru ajutor adițional, faceți referie la [regulile de comunitate](%{base_url}).. user_automatically_blocked: subject_template: "Noul utilizator %{username} a fost blocat datorită marcajelor date de comunitate" spam_post_blocked: diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 91cbd6472f..708dbc1310 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -911,15 +911,9 @@ ru: tl2_requires_likes_received: "Сколько симпатий пользователь должен получить для продвижения до уровня доверия 2." tl2_requires_likes_given: "Сколько симпатий пользователь должен выразить для продвижения до уровня доверия 2." tl2_requires_topic_reply_count: "В скольких темах пользователь должен ответить для продвижения до уровня доверия 2." - tl3_requires_topics_replied_to: "Минимальное количество тем, в которых пользователь должен ответить за последние 100 дней, для возможности продвижения до уровня доверия 3. (0 и больше)" - tl3_requires_topics_viewed: "Какой процент тем созданных за последние 100 дней, должен просмотреть пользовательзователь для повышения уровня доверия до 3. (0 to 100)" - tl3_requires_posts_read: "Какой процент сообщений написанных за последние 100 дней, должен просмотреть пользовательзователь для повышения уровня доверия до 3. (0 to 100)" tl3_requires_topics_viewed_all_time: "Сколько сообщений пользователь должен прочитать для поднятия уровня доверия до 3." tl3_requires_posts_read_all_time: "Сколько сообщений пользователь должен прочитать для поднятия уровня доверия до 3." - tl3_requires_max_flagged: "Допустимое колличество сообщений пользователя, на которые пожаловалось x разных пользователей за последние 100 дней, для того чтобы была возможность поднять уровень доверия до 3, где x это значение (от 0 и выше)" tl3_promotion_min_duration: "Минимальное количество дней , в течении которых пользователь с уровнем доверия 3 не может быть понижен до урованя доверия 2." - tl3_requires_likes_given: "Минимальное количество лайков, которое должен получить пользователь за последние 100 дней, для возможности продвижения до уровня доверия 3." - tl3_requires_likes_received: "Минимальное количество лайков, которое должен получить пользователь за последние 100 дней, для возможности продвижения до уровня доверия 3." tl3_links_no_follow: "Не удалять rel=nofollow из ссылок от пользователей с уровнем доверия 3." min_trust_to_create_topic: "Минимальный уровень доверия для создания новой темы." min_trust_to_edit_wiki_post: "Минимальный уровень доверия, требуемый для редактирования вики-сообщения." @@ -1080,11 +1074,6 @@ ru: category: 'Разделы' topic: 'Результаты' user: 'Пользователи' - sso: - not_found: "Мы не можем найти или создать данный аккаунт, свяжитесь с администратором сайта." - account_not_approved: "Аккаунт ожидает проверки. Вы получите уведомление на почту, когда ваш аккаунт будет одобрен." - unknown_error: "Ошибка обновления информации, сообщите об этом администратору сайта." - timeout_expired: "Закончилась сессия авторизации, пожалуйста войдите заново на сайт." original_poster: "Автор" most_posts: "Большинство сообщений" most_recent_poster: "Последний автор" @@ -1420,12 +1409,6 @@ ru: За дополнительной информацией обратитесь к [инструкции пользователя](%{base_url}/guidelines). blocked_by_staff: subject_template: "Учетная запись заблокирована" - text_body_template: | - Здравствуйте! - - Это автоматическое сообщение с сайта %{site_name} для информирования вас о том, что ваша учетная запись заблокирована администратором или модератором. - - Получить дополнительную информацию вы можете в нашей [инструкции пользователя](%{base_url}/guidelines). user_automatically_blocked: subject_template: "Пользователь %{username} заблокирован участниками сообщества" spam_post_blocked: @@ -1463,33 +1446,6 @@ ru: posted_by: "Отправлено %{username} %{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} приглашает вас к обсуждению сообщения '%{topic_title}'" - text_body_template: |2 - - %{username} написал Вам сообщение - - > **%{topic_title}** - > - > %{topic_excerpt} - - на сайте - - > %{site_title} -- %{site_description} - - Просмотреть сообщение вы можете по ссылке: %{base_url}%{url} - user_invited_to_topic: - text_body_template: |2 - - %{username} приглашает вас к обсуждению - - > **%{topic_title}** - > - > %{topic_excerpt} - - на сайте: - - > %{site_title} -- %{site_description} - - Просмотреть сообщение вы можете по ссылке: %{base_url}%{url} user_replied: subject_template: "[%{site_name}] %{topic_title}" text_body_template: | @@ -1731,23 +1687,6 @@ ru: title: "Пользовательское соглашение" privacy_topic: title: "Политика конфиденциальности" - badges: - long_descriptions: - autobiographer: | - Эта награда выдается за заполнение - своей страницы профиля и выбор картинки профиля. Возможность узнать больше о дуг друге позволяет форумчанам сплотиться в тесное сообщество. - nice_post: | - Эта награда выдается за сообщение, собравшее 10 симпатий. Отличная работа! - nice_topic: | - Эта награда выдается за тему, собравшую 10 симпатий. Отличная работа! - good_post: | - Эта награда выдается за сообщение, собравшее 25 симпатий. Превосходная работа! - good_topic: | - Эта награда выдается за тему, собравшую 25 симпатий. Превосходная работа! - great_post: | - Эта награда выдается за сообщение, собравшее 50 симпаний. Вау! - great_topic: | - Эта награда выдается за тему, собравшую 50 симпаний. Вау! admin_login: success: "письмо отправлено" error: "Ошибка!" diff --git a/config/locales/server.sk.yml b/config/locales/server.sk.yml index 492684b0d1..7461986996 100644 --- a/config/locales/server.sk.yml +++ b/config/locales/server.sk.yml @@ -930,17 +930,9 @@ sk: tl2_requires_likes_received: "Koľko \"Páči sa\" musí nový užívateľ dostať pred povýšením na stupeň dôvery 2." tl2_requires_likes_given: "Koľko \"Páči sa\" musí nový užívateľ rozdať pred povýšením na stupeň dôvery 2." tl2_requires_topic_reply_count: "Na koľko tém musí nový užívateľ odpovedať pred povýšením na stupeň dôvery 2." - tl3_time_period: "Požiadavok na časovú dobu pre stupeň dôvery 3" - tl3_requires_days_visited: "Minimálny počet dní, koľkokrát používateľ musí navštíviť stránku za posledných 100 dní, aby sa kvalifikoval na úroveň dôvery 3. Ak chcete vypnúť povýšenie na úroveň dôvery 3, nastavte dobu vyššiu ako dobu na úroveň dôvery 3. (0 a viac)" - tl3_requires_topics_replied_to: "Minimálny počet tém, na ktoré musí mať užívateľ odpovedať za posledných 100 dní potrebných pre kvalifikáciu na dosiahnute 3 levelu dôvery. (0 a viac)" - tl3_requires_topics_viewed: "Percento tém ktoré musí mať užívateľ pozreté, za posledných 100 dní pre kvalifikáciu na dosiahnute 3 levelu dôvery. (0 až 100)" - tl3_requires_posts_read: "Percento príspevkov, ktoré musí mať užívateľ pozreté za posledných 100 dní pre kvalifikáciu na dosiahnute 3 levelu dôvery. (0 až 100)" tl3_requires_topics_viewed_all_time: "Minimálny počet všetkých tém, ktoré musí mať užívateľ pozreté pre kvalifikáciu na dosiahnute 3 levelu dôvery. " tl3_requires_posts_read_all_time: "Minimálny počet všetkých príspevkov, ktoré musí mať užívateľ prečítané pre kvalifikáciu na 3 level dôvery. " - tl3_requires_max_flagged: "V posledných 100 dňoch použivateľ nemôže mať viac ako x príspevkou označených viac ako x rôznymi používateľmi aby sa kvalifikoval na postup na úroveň dôvery 3, kde x je hodnota ktorú nastavujete. (0 alebo viac)" tl3_promotion_min_duration: "MInimálny počet dní po pridelení 3 levelu dôvery než môže byť užívateľ degradovaný späť na 3 level dôvery." - tl3_requires_likes_given: "Minimálny počet \"Páči sa\", ktoré musia byť udelené za posledných 100 dní pre kvalifikáciu na dosiahnutie 3 levelu dôvery. " - tl3_requires_likes_received: "Minimálny počet \"Páči sa\", ktoré musia byť prijaté za posledných 100 dní pre kvalifikáciu na dosiahnutie 3 levelu dôvery. " tl3_links_no_follow: "Neodstraňujte rel = nofollow z odkazov pridaných užívateľmi s úrovňou dôveryhodnosti 3 ." min_trust_to_create_topic: "MInimálna úroveň dôvery na vytvorenie novej témy." min_trust_to_edit_wiki_post: "MInimálna úroveň dôvery na úpravu wiki príspevku." @@ -955,7 +947,6 @@ sk: max_users_notified_per_group_mention: "Maximálny počet používateľov, ktorý môžu dostať notifikácie, v prípade notifikovania celej skupiny (ak sa dosiahne maximum, žiadne ďalšie notifikácie nebudú zaslané)" create_thumbnails: "Vytvor náhľad a okraje pre obrázoky, ktoré sú príliš veľké aby sa zmestili do príspevku." email_time_window_mins: "Počkať (n) minút pred poslaním akýchkoľvek notifikačných emailov, aby mali používatelia čas úpraviť a dokončiť svoje príspevky." - private_email_time_window_seconds: "Počkať (n) sekúnd pred poslaním akýchkoľvek súkromných notifikačných emailov, aby mali používatelia čas úpraviť a dokončiť svoje príspevky." email_posts_context: "Koľko posledných odpovedí zahrnúť do obsahu v notifikačných emailoch." flush_timings_secs: "Ako často posielať časové dáta na server, v sekundách." title_max_word_length: "Maximálna povolená dĺžka slov v názve témy, v znakoch." @@ -1057,7 +1048,6 @@ sk: disable_avatar_education_message: "Vypnúť informatívnu hlášku na zmenu avatara." daily_performance_report: "Denne analyzuj NGINX logy a z detailov vytváraj témy iba pre zamestnancov." suppress_uncategorized_badge: "V zozname tém nezobrazuj odznak za nekategorizované príspevky." - permalink_normalizations: "Pred vyhľadaním trvalého odkazu použi následujúci regulérny výraz, napríklad : /(\\/topic.*)\\?.*/\\1 oddelí dotazovaný reťazec od témy. Formátom je regex+reťazec. Použi \\1 atp. k prístupu na zachytenie" global_notice: "Pre všetkých návštevníkov zobraz URGENTNÉ, KRÍZOVÉ globálné záhlavie, zmeň na čierne a schovaj (HTML povolené)." disable_edit_notifications: "Ak je premenná 'download_remote_images_to_local' aktívna, vypnúť notifikácie o úpravách systémového používateľa." automatically_unpin_topics: "Automaticky odopnúť témy ak používateľ dostanie posledný príspevok." @@ -1147,11 +1137,6 @@ sk: category: 'Kategórie' topic: 'Výsledky' user: 'Používatelia' - sso: - not_found: "Nepodarilo sa nájsť, alebo vytvoriť účet, kontaktujte správcu stránky" - account_not_approved: "Účet čaká na schválenie, o schválení budete upovedomení emailom" - unknown_error: "Zlyhala aktualizácia informácie, kontaktujte správcu stránky" - timeout_expired: "Vypršal časový limit príhlásenia, prosím skúste sa prihlásiť znova" original_poster: "Pôvodný autor" most_posts: "Najviac príspevkov" most_recent_poster: "Najaktuálnejší prispievateľ" @@ -1582,20 +1567,6 @@ sk: subject_template: "[%{site_name}] %{username} Vás pozval k správe '%{topic_title}'" user_invited_to_private_message_pm_staged: subject_template: "[%{site_name}] %{username} Vás pozval k správe '%{topic_title}'" - user_invited_to_topic: - text_body_template: |2 - - %{username} Vás pozval do diskusie - - > **%{topic_title}** - > - > %{topic_excerpt} - - na - - > %{site_title} -- %{site_description} - - Prosím návštívte tento odkaz pre pozretie správy: %{base_url}%{url} user_replied: subject_template: "[%{site_name}] %{topic_title}" text_body_template: | @@ -1874,45 +1845,6 @@ sk: title: "Podmienky používania" privacy_topic: title: "Ochrana súkromia" - badges: - long_descriptions: - autobiographer: "Tento odznak je udelený za vypnenie Vášho užívateľského profilu a zvolenie profilovej fotky. Dávaním najavo komunite kto ste a čo Vás zaujíma vytvára lepšiu a prepojenejšiu komunitu. \n" - first_like: | - Tento odznak je udelený za prvé udelené "Páči sa" použitím :heart: talčidla. Označenie príspevku "Páči sa" je najlepšia cesta ako ukázať členom komunity, že ich príspevky sú zaujímavé, užitočné, výnimočné, alebo zábavné. Podeľte sa o to! - first_link: "Tento odznak je udelený za prvý vložený odkaz na inú tému v odpovedi. Spájanie tém pomáha kolegom čitateľom nájsť zaujímavé súvisiace konverzácie, poukázaním na prepojenia medzi témami oboma smermi. \n" - first_quote: "Tento odznak je udelený za prvý použitý citát príspevku vo Vašej odpovedi. Citovanie relevantných častí predchádzajúcich príspevkov pomáha udržiavať diskusiu v rámci témy. \n" - first_share: "Tento odznak je udelený za prvý zdieľaný odkaz na odpoveď, alebo tému pužítím zdieľacieho tlačidla. Zdieľanie odkazov je výborná cesta, ako ukázať zaujímavé diskusie zvyšku sveta a rozširovať Vašu komunitu. \n" - read_guidelines: | - Tento odznak je udelený za prečítanie pravidiel komunity. Dodržiavanie a zdieľanie týchto jednoduchých pravidiel pomáha budovať bezpečnú a udržateľnú komunitu. - reader: "Tento odznak je udelený za prečítanie dlhej témy. Čítanie je základ. Pozorné ťítanie pomáha sledovať konverzáciu a vedie k lepším, ucelenejším odpovediam. \n" - editor: | - Tento odznak je udelený za úpravu Vášho príspevku. Nebojte sa kedykoľvek upravovať svoje príspevky za účelom ich vylepšenia, opravenia chýb, alebo pridania čohokoľvek, čo ste zabudli. - first_flag: | - Tento odznak je udelený za označenie príspevku. Označovanie príspevkov je dôležité pre zdravú komunitu. Keď zbadáte príspevok, ktorý vyžaduje pozornosť moderátora, prosím - nebojte sa ho označiť. Taktiež môžte použiť označovací dialóg na poslanie správy kolegovi užívateľovi. - nice_share: "Tento odznak je udelený za zdieľanie odkazu na príspevok, ktorý bol navštívený 25 návštevníkmi zvonku. Pekná práca! Zdieľanie odkazov na zaujímavé diskusie s priateľmi je výborná cesta, ako rozširiť Vašu komunitu. \n" - welcome: | - Tento odznak je udelený za prvé získané "Páči sa" na príspevok. Gratulujeme, prispeli ste niečim, čo členovia komunity považujú za zaujímavé, užitočné, alebo výnimočné! - anniversary: | - Tento odznak je udelený za to, že ste boli členom celý rok a prispeli najmenej jedným príspevkom v tomto roku. Ďakujeme za priazeň a prispievanie do našej komunity! - good_share: | - Tento odznak je udelený za zdieľanie odkazu na príspevok, ktorý bol navštívený 300 návštevníkmi zvonku. Dobrá práca! Ukázali ste zaujímavú diskusiu veľa novým ľuďom a pomohli ste nám rásť. - great_share: | - Tento odznak je udelený za zdieľanie odkazu na príspevok, ktorý bol navštívený 100 návštevníkmi zvonku. Super! Spropagovalí ste zaujímavú diskusiu obrovskému publiku, a pomohli ste nám rásť vo veľkom štýle. - nice_post: | - Tento odznak je udelený za vytvorenie odpovede, ktorá dostane 10 "Páči sa". Pekná práca. - nice_topic: | - Tento odznak je udelený za vytvorenie témy, ktorá dostane 10 "Páči sa". Pekná práca. - good_post: | - Tento odznak je udelený za vytvorenie odpovede, ktorá dostane 25 "Páči sa". Dobrá práca. - good_topic: | - Tento odznak je udelený za vytvorenie témy, ktorá dostane 25 "Páči sa". Dobrá práca. - great_post: | - Tento odznak je udelený za vytvorenie príspevku, ktorý dostane 50 "Páči sa". Super! - great_topic: | - Tento odznak je udelený za vytvorenie odpovede, ktorá dostane 50 "Páči sa". Super! - basic: | - Tento odznak je udelený ak dosiahnete stupeň dóvery 1. Ďakujeme za Váš čas , prečítanie niekoľkých tém a zoznámenie sa s tým o čom je naša komunita. Odstránili sme obmedzenia pre nového používateľa a odteraz máte všetky základné práva, ako je zasielanie osobných správ, značkovanie, wiki úpravy a možnosť publikovať obrázky a viaceré odkazy. admin_login: success: "Email odoslaný" error: "Chyba !" diff --git a/config/locales/server.sq.yml b/config/locales/server.sq.yml index 8724129c5c..09361c7070 100644 --- a/config/locales/server.sq.yml +++ b/config/locales/server.sq.yml @@ -257,7 +257,6 @@ sq: uncategorized_parent: "Uncategorized can't have a parent category" self_parent: "A subcategory's parent cannot be itself" depth: "You can't nest a subcategory under another" - email_in_already_exist: "Incoming email address '%{email_in}' is already in use for '%{category_name}' category." cannot_delete: uncategorized: "Can't delete Uncategorized" has_subcategories: "Can't delete this category because it has sub-categories." @@ -618,7 +617,6 @@ sq: min_private_message_title_length: "Minimum allowed title length for a message in characters" min_search_term_length: "Minimum valid search term length in characters" allow_uncategorized_topics: "Allow topics to be created without a category. WARNING: If there are any uncategorized topics, you must recategorize them before turning this off." - uncategorized_description: "The description of the uncategorized category. Leave blank for no description." allow_duplicate_topic_titles: "Allow topics with identical, duplicate titles." unique_posts_mins: "How many minutes before a user can make a post with the same content again" educate_until_posts: "When the user starts typing their first (n) new posts, show the pop-up new user education panel in the composer." @@ -792,15 +790,9 @@ sq: tl2_requires_likes_received: "How many likes a user must receive before promotion to trust level 2." tl2_requires_likes_given: "How many likes a user must cast before promotion to trust level 2." tl2_requires_topic_reply_count: "How many topics user must reply to before promotion to trust level 2." - tl3_requires_topics_replied_to: "Minimum number of topics a user needs to have replied to in the last 100 days to qualify for promotion to trust level 3. (0 or higher)" - tl3_requires_topics_viewed: "The percentage of topics created in the last 100 days that a user needs to have viewed to qualify for promotion to trust level 3. (0 to 100)" - tl3_requires_posts_read: "The percentage of posts created in the last 100 days that a user needs to have viewed to qualify for promotion to trust level 3. (0 to 100)" tl3_requires_topics_viewed_all_time: "The minimum total number of topics a user must have viewed to qualify for trust level 3." tl3_requires_posts_read_all_time: "The minimum total number of posts a user must have read to qualify for trust level 3." - tl3_requires_max_flagged: "User must not have had more than x posts flagged by x different users in the last 100 days to qualify for promotion to trust level 3, where x is this setting's value. (0 or higher)" tl3_promotion_min_duration: "The minimum number of days that a promotion to trust level 3 lasts before a user can be demoted back to trust level 2." - tl3_requires_likes_given: "The minimum number of likes that must be given in the last 100 days to qualify for promotion to trust level 3." - tl3_requires_likes_received: "The minimum number of likes that must be received in the last 100 days to qualify for promotion to trust level 3." tl3_links_no_follow: "Do not remove rel=nofollow from links posted by trust level 3 users." min_trust_to_create_topic: "The minimum trust level required to create a new topic." min_trust_to_edit_wiki_post: "The minimum trust level required to edit post marked as wiki." @@ -888,7 +880,6 @@ sq: automatically_download_gravatars: "Download Gravatars for users upon account creation or email change." digest_topics: "The maximum number of topics to display in the email digest." digest_min_excerpt_length: "Minimum post excerpt in the email digest, in characters." - delete_digest_email_after_days: "Suppress digest emails for users not seen on the site for more than (n) days." disable_digest_emails: "Disable digest emails for all users." detect_custom_avatars: "Whether or not to check that users have uploaded custom profile pictures." max_daily_gravatar_crawls: "Maximum number of times Discourse will check Gravatar for custom avatars in a day" @@ -903,7 +894,6 @@ sq: dominating_topic_minimum_percent: "What percentage of posts a user has to make in a topic before being reminded about overly dominating a topic." daily_performance_report: "Analyze NGINX logs daily and post a Staff Only topic with details" suppress_uncategorized_badge: "Don't show the badge for uncategorized topics in topic lists." - permalink_normalizations: "Apply the following regex before matching permalinks, for example: /(\\/topic.*)\\?.*/\\1 will strip query strings from topic routes. Format is regex+string use \\1 etc. to access captures" global_notice: "Display an URGENT, EMERGENCY global banner notice to all visitors, change to blank to hide it (HTML allowed)." disable_edit_notifications: "Disables edit notifications by the system user when 'download_remote_images_to_local' is active." full_name_required: "Full name is a required field of a user's profile." @@ -933,7 +923,6 @@ sq: enable_emoji: "Enable emoji" emoji_set: "How would you like your emoji?" enforce_square_emoji: "Force a square aspect ratio to all emojis." - approve_post_count: "The amount of posts from a new user that must be approved" approve_unless_trust_level: "Posts for users below this trust level must be approved" notify_about_queued_posts_after: "If there are posts that have been waiting to be reviewed for more than this many hours, an email will be sent to the contact email. Set to 0 to disable these emails." errors: @@ -960,7 +949,6 @@ sq: moved_post: "%{display_username} moved your post to %{link}" private_message: "%{display_username} sent you a message: %{link}" invited_to_private_message: "%{display_username} invited you to a message: %{link}" - invited_to_topic: "%{display_username} invited you to a topic: %{link}" invitee_accepted: "%{display_username} accepted your invitation" linked: "%{display_username} linked you in %{link}" granted_badge: "You earned %{link}" @@ -970,11 +958,6 @@ sq: category: 'Categories' topic: 'Results' user: 'Users' - sso: - not_found: "Unable to lookup or create account, contact site admin" - account_not_approved: "Account is pending approval, you will receive an email notification once approved" - unknown_error: "Error updating information, contact site admin" - timeout_expired: "Account login timed out, please try logging in again" original_poster: "Original Poster" most_posts: "Most Posts" most_recent_poster: "Most Recent Poster" @@ -1110,87 +1093,10 @@ sq: (If the link above has expired, choose "I forgot my password" when logging in with your email address.) test_mailer: subject_template: "[%{site_name}] Email Deliverability Test" - text_body_template: | - This is a test email from - - [**%{base_url}**][0] - - Email deliverability is complicated. Here are a few important things you should check first: - - - Be *sure* to set the `notification email` from: address correctly in your site settings. **The domain specified in the "from" address of the emails you send is the domain your email will be validated against**. - - - Know how to view the raw source of the email in your mail client, so you can examine email headers for important clues. in Gmail, it is the "show original" option in the drop-down menu at the top right of each mail. - - - **IMPORTANT:** Does your ISP have a reverse DNS record entered to associate the domain names and IP addresses you send mail from? [Test your Reverse PTR record][2] here. If your ISP does not enter the proper reverse DNS pointer record, it's very unlikely any of your email will be delivered. - - - Is your domain's [SPF record][8] correct? [Test your SPF record][1] here. Note that TXT is the correct official record type for SPF. - - - Is your domain's [DKIM record][3] correct? This will significantly improve email deliverability. [Test your DKIM record][7] here. - - - 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. - - (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! - - Good luck, - - Your friends at [Discourse](http://www.discourse.org) - - [0]: %{base_url} - [1]: http://www.kitterman.com/spf/validate.html - [2]: http://mxtoolbox.com/ReverseLookup.aspx - [3]: http://www.dkim.org/ - [4]: http://whatismyipaddress.com/blacklist-check - [7]: http://dkimcore.org/tools/dkimrecordcheck.html - [8]: http://www.openspf.org/SPF_Record_Syntax - [md]: http://mandrill.com - [mg]: http://www.mailgun.com/ - [mj]: https://www.mailjet.com/pricing new_version_mailer: subject_template: "[%{site_name}] verion i ri i Discourse, përditësoje!" - text_body_template: | - A new version of [Discourse](http://www.discourse.org) is available. - - Your version: %{installed_version} - New version: **%{new_version}** - - You may want to: - - - See what's new in the [GitHub changelog](https://github.com/discourse/discourse/commits/master). - - - Upgrade from your browser at [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade). - - - Visit [meta.discourse.org](http://meta.discourse.org) for news, discussion, and support for Discourse. new_version_mailer_with_notes: subject_template: "[%{site_name}] ka perditesime" - text_body_template: | - A new version of [Discourse](http://www.discourse.org) is available. - - Your version: %{installed_version} - New version: **%{new_version}** - - You may want to: - - - See what's new in the [GitHub changelog](https://github.com/discourse/discourse/commits/master). - - - Upgrade from your browser at [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade). - - - Visit [meta.discourse.org](http://meta.discourse.org) for news, discussion, and support for Discourse. - - ### Release 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: "postim" - 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: subject_template: one: "[%{site_name}] 1 post waiting to be reviewed" @@ -1314,10 +1220,6 @@ sq: text_body_template: "We're sorry, but your data export failed. Please check the logs or contact a staff member." email_reject_no_account: subject_template: "[%{site_name}] Email issue -- Unknown Account" - text_body_template: | - We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. - - There is no known user account with this email address. Try sending from a different email address, or contact a staff member. email_reject_empty: subject_template: "[%{site_name}] Email issue -- No Content" email_reject_parsing: @@ -1334,16 +1236,8 @@ sq: Your account does not have the privileges to post new topics in that category. If you believe this is in error, contact a staff member. email_reject_reply_key: subject_template: "[%{site_name}] Email issue -- Unknown Reply Key" - text_body_template: | - We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. - - The provided reply key is invalid or unknown, so we don't know what this email is in reply to. Contact a staff member. email_reject_topic_not_found: subject_template: "[%{site_name}] Email issue -- Topic Not Found" - text_body_template: | - We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. - - The topic you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member. email_reject_topic_closed: subject_template: "[%{site_name}] Email issue -- Topic Closed" text_body_template: | @@ -1352,16 +1246,8 @@ sq: The topic you are replying to is currently closed and no longer accepting replies. If you believe this is in error, contact a staff member. email_reject_auto_generated: subject_template: "[%{site_name}] Email issue -- Auto Generated Reply" - text_body_template: | - We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. - - Your email was marked as "auto generated", which we can't accept. If you believe this is in error, contact a staff member. email_error_notification: subject_template: "[%{site_name}] Email issue -- POP authentication error" - text_body_template: | - There has been an authentication error while polling mails from the POP server. - - Please make sure you have properly configured the POP credentials in [the site settings](%{base_url}/admin/site_settings/category/email). too_many_spam_flags: subject_template: "New account blocked" text_body_template: | @@ -1374,12 +1260,6 @@ sq: For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines). blocked_by_staff: subject_template: "Account blocked" - text_body_template: | - Hello, - - This is an automated message from %{site_name} to inform you that your account has been blocked by a staff member. - - For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines). user_automatically_blocked: subject_template: "New user %{username} blocked due to community flags" spam_post_blocked: @@ -1413,34 +1293,6 @@ sq: posted_by: "Posted by %{username} on %{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} invited you to a message '%{topic_title}'" - text_body_template: |2 - - %{username} invited you to a message - - > **%{topic_title}** - > - > %{topic_excerpt} - - at - - > %{site_title} -- %{site_description} - - Please visit this link to view the message: %{base_url}%{url} - user_invited_to_topic: - subject_template: "[%{site_name}] %{username} invited you to a topic '%{topic_title}'" - text_body_template: |2 - - %{username} invited you to a discussion - - > **%{topic_title}** - > - > %{topic_excerpt} - - at - - > %{site_title} -- %{site_description} - - Please visit this link to view the message: %{base_url}%{url} user_replied: subject_template: "[%{site_name}] %{topic_title}" user_quoted: @@ -1497,12 +1349,6 @@ sq: Click the following link to choose a password for your new account: %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - subject_template: "[%{site_name}] Confirm your new email address" - text_body_template: | - Confirm your new email address for %{site_name} by clicking on the following link: - - %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "You've been approved on %{site_name}!" text_body_template: | @@ -1532,7 +1378,6 @@ sq: If the above link is not clickable, try copying and pasting it into the address bar of your web browser. page_not_found: - title: "The page you requested doesn't exist or is private." popular_topics: "Popular" recent_topics: "Recent" see_more: "More" @@ -1824,57 +1669,6 @@ sq: Originally adapted from the [WordPress Terms of Service](http://en.wordpress.com/tos/). privacy_topic: title: "Politika e Privatësisë" - badges: - long_descriptions: - autobiographer: | - This badge is granted for filling out your user profile and selecting a profile picture. Letting the community know more about who you are and what you're interested in makes for a better, more connected community. - first_like: | - This badge is granted the first time you like a post using the :heart: button. Liking posts is a great way to let your fellow community members know that what they posted was interesting, useful, cool, or fun. Share the love! - first_link: | - This badge is granted the first time you place a link to another topic in a reply. Linking topics helps fellow readers find interesting related conversations, by showing connections between topics in both directions. - first_quote: | - This badge is granted the first time you quote a post in your reply. Quoting relevant sections of earlier posts in your reply helps keep discussions focused and on topic. - first_share: | - This badge is granted the first time you share a link to a reply or topic using the share button. Sharing links is a great way to show off interesting discussions with the rest of the world and grow your community. - read_guidelines: | - This badge is granted for reading the community guidelines. Following and sharing these simple guidelines helps build a safe, fun, and sustainable community. - reader: | - This badge is granted for reading a long topic. Reading is fundamental. Reading closely helps you follow the conversation and leads to better, more complete replies. - editor: | - This badge is granted for editing your post. Don't hesitate to edit your posts any time to improve them, fix small mistakes, or add anything you forgot. - first_flag: | - This badge is granted for flagging a post. Flagging is critical to the health of your community. If you notice any posts that require moderator attention please - do not hesitate to flag. You may also use the flagging dialog to send messages to fellow users. - nice_share: | - This badge is granted for sharing a link to a post that's visited by 25 outside visitors. Nice job! Sharing links to interesting discussions with friends is an excellent way to grow our community. - welcome: | - This badge is granted when you receive your first like on a post. Congratulations, you've posted something that your fellow community members found interesting, cool, or useful! - anniversary: | - This badge is granted when you've been a member for a year with at least one post in that year. Thanks for sticking around and contributing to our community! - good_share: | - This badge is granted for sharing a link to a post that's visited by 300 outside visitors. Good work! You've shown off an interesting discussion to a lot of new people and helped us grow. - great_share: | - This badge is granted for sharing a link to a post that's visited by 100 outside visitors. Wow! You've promoted an interesting discussion to a huge new audience for this community, and helped us grow in a big way. - nice_post: | - This badge is granted for creating a reply that gets 10 likes. Nice job! - nice_topic: | - This badge is granted for creating a topic that gets 10 likes. Nice job! - good_post: | - This badge is granted for creating a reply that gets 25 likes. Good work! - good_topic: | - This badge is granted for creating a topic that gets 25 likes. Good work! - great_post: | - This badge is granted for creating a post that gets 50 likes. Wow! - great_topic: | - This badge is granted for creating a reply that gets 50 likes. Wow! - basic: | - This badge is granted when you reach trust level 1. Thanks for sticking around a little while and reading a few topics to learn what our community is about. Your new user restrictions have been lifted, and you've been granted all essential community abilities, such as personal messaging, flagging, wiki editing, and the ability to post images and multiple links. - member: | - This badge is granted when you reach trust level 2. Thanks for participating over a period of weeks to more join our community. You can now send personal invitations from your user page or individual topics, create group messages, and a few more likes per day. - regular: | - This badge is granted when you reach trust level 3. Thanks for being a regular part of our community over a period of months, one of the most active readers and a reliable contributor to what makes this community great. You can now recategorize and rename topics, access a private lounge area, more powerful spam flags, and lots more likes per day. - leader: | - This badge is granted when you reach trust level 4. You're a leader in this community as selected by staff, and set a positive example for the community in your deeds and words. You have the ability to edit all posts, take topic moderator actions such as pin, close, unlist, archive, split, and merge, and tons of likes per day. admin_login: success: "Email Sent" error: "Error!" diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index a2b6a98263..2648fa087e 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -652,10 +652,8 @@ sv: tl2_requires_likes_received: "Antalet gillningar en användare måste mottaga innan en befordran till förtroendenivå 2." tl2_requires_likes_given: "Antalet gillningar en användare måste ge innan en befordran till förtroendenivå 2." tl2_requires_topic_reply_count: "Antalet ämnen en användare måste svara på innan en befordran till förtroendenivå 2." - tl3_requires_topics_replied_to: "Minsta antalet ämnen en användare behöver ha svarat på de senaste 100 dagarna för att bli kvalificerad till en befordran till förtroendenivå 3. (0 eller högre)" tl3_requires_topics_viewed_all_time: "Minsta antalet ämnen en användare måste ha tittat på för att bli kvalificerad för förtroendenivå 3." tl3_requires_posts_read_all_time: "Minsta antalet inlägg en användare måste ha läst för att bli kvalificerad för förtroendenivå 3." - tl3_requires_likes_received: "Minsta antalet mottagna gillningar under de senaste 100 dagarna för att bli kvalificerad för en befordran till förtroendenivå 3." min_trust_to_create_topic: "Lägsta förtroendenivå som krävs för att skapa ett nytt ämne." newuser_max_links: "Antalet länkar en ny användare kan lägga till i ett inlägg." newuser_max_images: "Antalet bilder en ny användare kan lägga till i ett inlägg." @@ -715,7 +713,6 @@ sv: moved_post: "%{display_username} flyttade ditt inlägg till %{link}" private_message: "%{display_username} har skickat ett meddelande till dig: %{link}" invited_to_private_message: "%{display_username} har bjudit in dig till en privat konversation: %{link}" - invited_to_topic: "%{display_username} har bjudit in dig till ett ämne: %{link}" invitee_accepted: "%{display_username} accepterade din inbjudan" linked: "%{display_username} länkade dig i %{link}" granted_badge: "Du förtjänade %{link}" @@ -725,8 +722,6 @@ sv: category: 'Kategorier' topic: 'Resultat' user: 'Användare' - sso: - account_not_approved: "Konto inväntar godkännande. Du kommer att få ett e-postmeddelande när kontot väl är godkänt." original_poster: "Ursprunglig skribent" most_posts: "Mest inlägg" most_recent_poster: "Senaste skribent" @@ -830,47 +825,8 @@ sv: subject_template: "[%{site_name}] Email Deliverability Test" new_version_mailer: subject_template: "[%{site_name}] Ny version av Discourse, uppdatering tillgänglig" - text_body_template: | - En ny version av [Discourse](http://www.discourse.org) finns tillgängligt. - - Din version: %{installed_version} - Ny version: **%{new_version}** - - Du kanske vill: - - - Se vad som är nytt i [GitHub changelog](https://github.com/discourse/discourse/commits/master). - - - Uppgradera från din webbläsare på [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade). - - - Besöka [meta.discourse.org](http://meta.discourse.org) för nyheter, diskussioner och support för Discourse. new_version_mailer_with_notes: subject_template: "[%{site_name}] uppdatering tillgänglig" - text_body_template: | - En ny version av [Discourse](http://www.discourse.org) finns tillgängligt. - - Din version: %{installed_version} - Ny version: **%{new_version}** - - Du kanske vill: - - - Se vad som är nytt i [GitHub changelog](https://github.com/discourse/discourse/commits/master). - - - Uppgradera från din webbläsare på [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade). - - - Besöka [meta.discourse.org](http://meta.discourse.org) för nyheter, diskussioner och support för Discourse. - - ### Release notes - - %{notes} - flags_reminder: - flags_were_submitted: - one: "Dessa flaggor skickades in för mer än 1 timme sedan." - other: "Dessa flaggor skickades in för mer än %{count} timmar sedan." - please_review: "Var god och granska dessa." - post_number: "inlägg" - subject_template: - one: "1 flagga väntar på att bli hanterad" - other: "%{count} flaggor väntar på att bli hanterade" flag_reasons: off_topic: "Ditt inlägg blev flaggat som **off-topic**. Medlemmarna i forumet kände att det inte är passande för ämnet, som definieras av titeln och det första inlägget." inappropriate: "Ditt inlägg blev flaggat som **olämpligt**. Medlemmarna i forumet kände att det är störande, kränkande eller att det strider mot [våra riktlinjer](/guidelines)." @@ -954,8 +910,6 @@ sv: posted_by: "Postat av %{username} den %{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} har bjudit in dig till en privat konversation '%{topic_title}'" - user_invited_to_topic: - subject_template: "[%{site_name}] %{username} har bjudit in dig till ett ämne '%{topic_title}'" user_replied: subject_template: "[%{site_name}] %{topic_title}" user_quoted: @@ -987,18 +941,11 @@ sv: subject_template: "[%{site_name}] Logga in" account_created: subject_template: "[%{site_name}] Ditt nya konto" - confirm_new_email: - subject_template: "[%{site_name}] Confirm your new email address" - text_body_template: | - Confirm your new email address for %{site_name} by clicking on the following link: - - %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "Du har blivit godkänd på %{site_name}!" signup: text_body_template: "Välkommen till %{site_name}!\n\nKlicka på följande länk för att bekräfta och aktivera ditt nya konto:\n%{base_url}/users/activate-account/%{email_token}\n\nOm länken ovan inte går att klicka på kan du kopiera och klistra in länken i adressfältet i din webbläsare. \n" page_not_found: - title: "Sidan du sökte existerar inte eller är privat." popular_topics: "Populära" recent_topics: "Senaste" see_more: "Mer" diff --git a/config/locales/server.tr_TR.yml b/config/locales/server.tr_TR.yml index a3fb674144..0090d73e28 100644 --- a/config/locales/server.tr_TR.yml +++ b/config/locales/server.tr_TR.yml @@ -815,15 +815,9 @@ tr_TR: tl2_requires_likes_received: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce alması gereken beğeni sayısı." tl2_requires_likes_given: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce vermesi gereken beğeni sayısı." tl2_requires_topic_reply_count: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce cevaplaması gereken konu sayısı." - tl3_requires_topics_replied_to: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için son 100 günde cevap yazması gereken en az konu sayısı. (0 ya da daha fazla)" - tl3_requires_topics_viewed: "Bir kulanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde oluşturulmuş konulardan görüntülemesi gereken yüzde oranı. (0 ile 100 arası)" - tl3_requires_posts_read: "Bir kulanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde oluşturulmuş gönderilerden görüntülemesi gereken yüzde oranı. (0 ile 100 arası)" tl3_requires_topics_viewed_all_time: "Bir kullanıcının güven seviyesi 3'e yükselmeye hak kazanması için görüntülemesi gereken toplam en az konu sayısı." tl3_requires_posts_read_all_time: "Bir kullanıcının güven seviyesi 3'e yükselmeye hak kazanması için görüntülemesi gereken toplam en az gönderi sayısı." - tl3_requires_max_flagged: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde x farklı kullanıcı tarafından x adetten fazla gönderisinin bayraklanmamış olmaması gerekir. x bu ayara verilen değerdir. (0 ya da daha yüksek) " tl3_promotion_min_duration: "Bir kullanıcının güven seviyesi 2'ye düşürülebilmesi için güven seviyesi 3'te geçirmesi gereken en az gün sayısı." - tl3_requires_likes_given: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için son 100 günde vermesi gereken en az beğeni sayısı. " - tl3_requires_likes_received: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için son 100 günde alması gereken en az beğeni sayısı. " tl3_links_no_follow: "Güven seviyesi 3'teki kullanıcılar tarafından paylaşılan linklerdeki rel=nofollow'u kaldırma." min_trust_to_create_topic: "Yeni bir konu oluşturmak için gereken en az güven seviyesi. " min_trust_to_edit_wiki_post: "Wiki olarak işaretlenmiş bir gönderiyi düzenleyebilmek için gereken en az güven seviyesi." @@ -931,7 +925,6 @@ tr_TR: dominating_topic_minimum_percent: "Konuyu domine ettiğine dair uyarı almadan önce konudaki gönderilerin yüzde kaçının kullanıcıya ait olması gerekir." daily_performance_report: "NGINX kayıtlarını analiz edip detaylı bir şekilde günlük Yetkili kategorisinde bir konu içerisinde paylaş." suppress_uncategorized_badge: "Kategorisiz konular için olan rozeti konu listesinde gösterme." - permalink_normalizations: "Kalıcı bağlantıları eşleştirmeden önce aşağıdaki regex'i uygula, mesela: /(\\/topic.*)\\?.*/\\1 konu yönlendirmelerindeki sorgu dizelerini kaldırır. Formatlama regex+string, yakalamalara erişmek \\1 vs." global_notice: "Tüm ziyaretçilere İVEDİ ACİL DURUM global manşet uyarısı göster, saklamak için boş bırakın (HTML kullanılabilir)." disable_edit_notifications: " 'download_remote_images_to_local' etkin olduğunda, sistem kullanıcısından gelen düzenleme bildirilerini devre dışı bırakır" automatically_unpin_topics: "Kullanıcı sayfa sonuna eriştiğinde tutturulmuş konuları otomatik olarak sayfadan ayır." @@ -1016,11 +1009,6 @@ tr_TR: category: 'Kategoriler' topic: 'Sonuçlar' user: 'Kullanıcılar' - sso: - not_found: "Hesap aranamıyor ya da oluşturulamıyor, site admini ile iletişime geçin" - account_not_approved: "Hesap henüz onaylanmamış, onaylandığında e-posta ile haberdar edileceksiniz" - unknown_error: "Bilgi güncellenirken hata oluştu, site admini ile iletişime geçin" - timeout_expired: "Seansınız zaman aşımına uğradı, lütfen tekrar giriş yapmayı deneyin" original_poster: "Orjinal Poster" most_posts: "En Çok Gönderi" most_recent_poster: "En Son Gönderen" @@ -1266,12 +1254,6 @@ tr_TR: Daha fazla bilgi için lütfen [topluluk yönergelerine](%{base_url}/guidelines) bakın. blocked_by_staff: subject_template: "Hesap engellendi" - text_body_template: | - Merhaba. - - Bu, %{site_name} sitesinin, hesabınızın bir görevli tarafından engellendiğini bildirmek için gönderdiği otomatik bir mesajdır. - - Daha fazla bilgi için lütfen [topluluk yönergelerine](%{base_url}/guidelines) bakın. user_automatically_blocked: subject_template: "Topluluk bayrakları nedeniyle yeni kullanıcı %{username} engellendi" spam_post_blocked: @@ -1301,33 +1283,6 @@ tr_TR: posted_by: "%{post_date} tarihinde %{username} tarafından gönderildi" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} sizi bir mesaja davet etti '%{topic_title}'" - text_body_template: |2 - - %{username} sizi bir mesaja davet etti - - > %{site_title} -- %{site_description} - - adresinde - - > **%{topic_title}** - > - > %{topic_excerpt} - - Mesajı görüntülemek için lütfen bu bağlantıyı ziyaret edin: %{base_url}%{url} - user_invited_to_topic: - text_body_template: |2 - - %{username} sizi bir tartışmaya davet etti - - > %{site_title} -- %{site_description} - - adresinde - - > **%{topic_title}** - > - > %{topic_excerpt} - - Mesajı görüntülemek için lütfen bu bağlantıyı ziyaret edin: %{base_url}%{url} user_replied: subject_template: "[%{site_name}] %{topic_title}" user_quoted: @@ -1460,56 +1415,6 @@ tr_TR: title: "Üyelik Sözleşmesi" privacy_topic: title: "Gizlilik İlkeleri" - badges: - long_descriptions: - autobiographer: | - Bu rozet profilini doldurduğunda ve profil resmini seçtiğinde verilir. Kim olduğun ve nelerle ilgilendiğin hakkında topluluğa daha fazla bilgi vermen daha iyi ve daha yakın bir topluluğun oluşmasına yardım eder. - first_like: | - Bu rozet :heart: tuşunu kullanarak bir gönderiyi ilk kez beğendiğinizde verilir. Gönderileri beğenmek hemcins topluluk üyelerinin ilgi çekici, faydalı, havalı ve eğlenceli nelerin gönderildiğini bilmelerini sağlamak için mükemmel bir yoldur. Sevgiyi paylaş! - first_link: | - Bu rozet bir cevapta başka bir konuya bir bağlantı verdiğinde verilir. Konuları bağlamak, her iki yönde konular arasındaki ilişkiyi göstererek, hemcins okuyucuların ilgi çekici ve ilgili konuşmaları bulmalarına yardım eder. - first_quote: | - Bu rozet bir cevapta ilk kez bir gönderiden alıntı yaptığınızda verilir. Cevabında önceki gönderilerin ilgili bölümlerinden alıntı yapmak tartışmaların odaklanmış halde ve konu içinde tutulmasına yardımcı olur. - first_share: | - Bu rozet paylaş tuşunu kullanarak bir cevaba ya da konuya ilk kez bir bağlantı paylaştığında verilir. Bağlantıları paylaşmak dünyanın geri kalanıyla yapılan ilginç tartışmalarla gösteriş yapmak ve topluluğunu büyütmek için mükemmel bir yoldur. - read_guidelines: | - Bu rozet topluluk kurallarını okunmasına verilir. Bu basit kuralları benimsemek ve paylaşmak güvenli, eğlenceli ve sürdürülebilir bir topluluğun inşa edilmesine yardım eder. - reader: | - Bu rozet uzun bir konuyu okumaya verilir. Okumak temeldir. Yakından okumak tartışmayı takip etmene ve daha iyi, daha eksiksiz cevaplar yazmana yardım eder. - editor: | - Bu rozet gönderini değiştirmene verilir. Geliştirmek, küçük hataları gidermek ya da unuttuğun bir şeyi eklemek için gönderilerini değiştirmekten hiçbir zaman çekinme. - first_flag: | - Bu rozet bir gönderinin işaretlenmesine verilir. İşaretleme, topluluğunun sağlığı için çok önemlidir. Yönetici incelemesi gereken herhangi bir gönderi görürseniz lütfen işaretlemekten çekinmeyin. Ayrıca hemcins kullanıcılara mesajlar göndermek için işaretleme diyaloğunu da kullanabilirsiniz. - nice_share: | - Bu rozet bir gönderide paylaşılan bir bağlantının 25 yabancı ziyaretçi tarafından ziyaret edilmesine verilir. Tebrikler! İlginç tartışmalara bağlantıları arkadaşlarla paylaşmak topluluğumuzu büyütmek için harika bir yoldur. - welcome: | - Bu rozet bir gönderide ilk beğeninizi aldığınızda verilir. Tebrikler, hemcins topluluk üyelerinin ilginç, havalı ya da kullanışlı bulduğu bir şeyler gönderdin. - anniversary: | - Bu rozet en az bir gönderiye sahip olunan bir yıllık üyeliğe verilir. Buralarda olup topluluğumuza katkıda bulunduğunuz için teşekkür ederiz. - good_share: | - Bu rozet bir gönderide paylaşılan bir bağlantı, 300 yabancı ziyaretçi tarafından ziyaret edildiğinde verilir. İyi iş! Bir sürü yeni kişiye ilgi çekici bir tartışma gösterdin ve böylece büyümemize yardımcı oldun. - great_share: | - Bu rozet bir gönderide paylaşılan bir bağlantının 100 yabancı ziyaretçi tarafından ziyaret edilmesine verilir. Vay be! Bu topluluk için ilginç bir tartışmayı yeni kocaman bir izleyici kitlesine terfi ettirdin ve büyümemize önemli bir katkı sağladın. - nice_post: | - Bu rozet bir cevap 10 beğeni aldığında verilir. İyi iş! - nice_topic: | - Bu rozet bir konu 10 beğeni aldığında verilir. İyi iş! - good_post: | - Bu rozet bir cevap 25 beğeni aldığında verilir. İyi iş! - good_topic: | - Bu rozet bir konu 25 beğeni aldığında verilir. İyi iş! - great_post: | - Bu rozet bir gönderi 50 beğeni aldığında verilir. Vay be! - great_topic: | - Bu rozet bir cevap 50 beğeni aldığında verilir. Vay be! - basic: | - Bu rozet güven seviyesi 1'e ulaştığında verilir. Bir süredir buralarda olduğun ve topluluğumuzun ne hakkında olduğu konusunda birkaç konu okuduğun için teşekkür ederiz. Yeni kullanıcı kısıtlamaların kaldırıldı ve kişisel mesajlaşma, işaretleme, wiki düzenleme, resim ve birden çok bağlantı gönderme yeteneği gibi ana topluluk yeteneklerini aldın. - member: | - Bu rozet güven seviyesi 2'ye ulaştığında verilir. Haftalar boyunca buralarda olup katkıda bulunduğun için teşekkür ederiz. Artık kullanıcı sayfandan ya da konularından kişisel davetler gönderebilir, grup mesajları oluşturabilir ve günlük birkaç daha fazla beğeni ekleyebilirsin. - regular: | - Bu rozet güven seviyesi 3'e ulaştığında verilir. Aylardır topluluğumuzun düzenli bir parçası, bu topluluğu harika yapan en etkin okuyucu ve güvenilir katkıcılardan biri olduğun için teşekkür ederiz. Artık konuların kategorilerini ve isimlerini değiştirebilirsin, özel bir lobi alanına erişebilir, daha güçlü spam işaretleri koyabilir ve günlük çok daha fazla beğeni ekleyebilirsin. - leader: | - Bu rozet güven seviyesi 4'e ulaştığında verilir. Eylem ve kelimelerin ile topluluk için olumlu bir izlenim bırakman sebebiyle forum kadrosu tarafından bu topluluğun bir önderi seçildin. Artık tüm gönderileri düzenleyebilir, sabitleme, kapatma, listelememe, arşivleme, ayırma ve birleştirme gibi yönetici eylemlerini yapabilir ve günlük yığınca beğeni ekleyebilirsiniz. admin_login: success: "E-posta Gönderildi" error: "Hata!" diff --git a/config/locales/server.vi.yml b/config/locales/server.vi.yml index 12e994c48e..d62310340f 100644 --- a/config/locales/server.vi.yml +++ b/config/locales/server.vi.yml @@ -838,17 +838,9 @@ vi: tl2_requires_likes_received: "Số like một thành viên phải nhận được trước khi được thăng lên bậc tin cậy cấp 2." tl2_requires_likes_given: "Số like một thành viên phải thực hiện trước khi được thăng lên bậc tin cậy cấp 2." tl2_requires_topic_reply_count: "Số chủ đề thành viên phải trả lời trước khi được thăng lên bậc tin cậy cấp 2." - tl3_time_period: "Khoảng thời gian độ tin cậy cấp 3 yêu cầu" - tl3_requires_days_visited: "Số ngày tối thiểu thành viên cần phải ghé thăm site trong khoảng 100 ngày gần đây trước khi được thăng lên độ tin cậy cấp 3. Đặt cao hơn khoảng thời gian tl3 để vô hiệu hóa thăng hạng tl3. (0 hoặc cao hơn)" - tl3_requires_topics_replied_to: "Số tối thiểu chủ đề thành viên phải trả lời trong 100 ngày gần đây trước khi được thăng lên bậc tin cậy cấp 3. (0 hoặc cao hơn)" - tl3_requires_topics_viewed: "Số phần trăm chủ đề được tạo trong khoảng 100 ngày gần đây mà thành viên cần phải xem trước khi được thăng lên độ tin cậy cấp 3. (từ 0 đến 100)" - tl3_requires_posts_read: "Phần trăm số bài viết được tạo trong khoảng 100 ngày gần đây mà thành viên cần xem để được thăng lên độ tin cậy cấp 3. (từ 0 tới 100)" tl3_requires_topics_viewed_all_time: "Số lượng chủ đề tối thiểu mà thành viên phải xem trước khi được thăng lên độ tin cậy cấp 3." tl3_requires_posts_read_all_time: "Tổng số bài viết tối thiểu mà thành viên phải đọc trước khi được thăng lên độ tin cậy cấp 3." - tl3_requires_max_flagged: "Thành viên phải không có nhiều hơn x bài viết bị đánh dấu bởi x thành viên khác trong khoảng 100 ngày gần đây để được thăng lên độ tin cậy cấp 3, với x là giá trị thiết lập. (0 hoặc cao hơn)" tl3_promotion_min_duration: "Số ngày tối thiểu chương trình thăng hạng cấp 3 kéo dài trước khi thành viên có thể bị giáng cấp xuống độ tin cậy cấp 2." - tl3_requires_likes_given: "Số like tối thiểu mà thành viên phải thực hiện trong khoảng 100 ngày gần đây để được thăng lên độ tin cậy cấp 3." - tl3_requires_likes_received: "Số like tối thiểu mà thành viên phải nhận được trong khoảng 100 ngày gần đây để được thăng lên độ tin cậy cấp 3." tl3_links_no_follow: "Không loại bỏ rel=nofollow khỏi các liên kết trong bài viết của thành viên có độ tin cậy cấp 3." min_trust_to_create_topic: "Bậc tin tưởng tối thiểu để tạo một chủ đề mới." min_trust_to_edit_wiki_post: "Bậc tin cậy tối thiểu cần thiết để có thể sửa bài viết được đánh dấu là wiki." @@ -921,11 +913,6 @@ vi: category: 'Thư mục' topic: 'Kết quả' user: 'Thành viên' - sso: - not_found: "Không thể tìm hoặc tạo tài khoản, vui lòng liên hệ quản trị trang" - account_not_approved: "Tài khoản đang chờ duyệt, bạn sẽ nhận được email thông báo khi được duyệt" - unknown_error: "Có lỗi khi cập nhật thông tin, liên hệ quản trị trang" - timeout_expired: "Tài khoản đăng nhập bị quá thời gian, vui lòng đăng nhập lại" original_poster: "Người viết gốc" most_posts: "Bài viết Phỏ biến" redirected_to_top_reasons: @@ -1161,16 +1148,6 @@ vi: title: "Điều khoản Dịch vụ" privacy_topic: title: "Chính sách Riêng tư" - badges: - long_descriptions: - good_post: | - Huy hiệu này được trao khi câu trả lời nhận được 24 lượt thích. Làm tốt lắm! - good_topic: | - Huy hiệu này được trao khi bài viết nhận được 25 lượt thích. Làm tốt lắm! - great_post: | - Huy hiệu này được trao khi bài viết nhận được 50 lượt thích. Wow! - great_topic: | - Huy hiệu này được trao khi câu trả lời nhận được 50 lượt thích. Wow! admin_login: success: "Gửi mail lỗi" error: "Lỗi!" diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 25cbaacd07..d05bb85312 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -157,6 +157,9 @@ zh_CN: hot: "最热主题" top: "热门主题" posts: "最新帖子" + private_posts: "最近的私信" + group_posts: "%{group_name}组最近的帖子" + group_mentions: "%{group_name}组最近的提及" too_late_to_edit: "这个主题在很早之前创建。不能被编辑或者被删除。" revert_version_same: "目前的版本和你想要回退至的版本一样。" excerpt_image: "图片" @@ -416,6 +419,7 @@ zh_CN: confirmed: "你的电子邮箱已被更新。" please_continue: "转入到 %{site_name}" error: "在修改你的电子邮箱地址时出现了错误,可能此邮箱已经在论坛中使用了?" + error_staged: "在修改你的电子邮箱地址时出现了错误。这个邮箱已经被一个用户占用了。" already_done: "抱歉,此激活链接已经失效。可能你已经修改了邮箱?" authorizing_old: title: "感谢你确认你目前的邮箱地址" @@ -670,11 +674,14 @@ zh_CN: site_contact_username_warning: "输入一个友善的管理人员账户名,并以他的名义发送重要的自动消息。在站点设置中更新 site_contact_username。" notification_email_warning: "通知邮件不是从你域名的一个有效地址发出的;邮件分发将会变得危险和不可靠。请在站点设置中将 notification_email 设置为一个有效的本地邮件地址。" subfolder_ends_in_slash: "你的子目录设置不正确;DISCOURSE_RELATIVE_URL_ROOT以斜杠结尾。" + email_polling_errored_recently: + other: "邮件轮询在过去的 24 小时内发生了 %{count} 个错误。看一看日志寻找详情。" site_settings: censored_words: "将被自动替换为 ■■■■" delete_old_hidden_posts: "自动删除被隐藏超过 30 天的帖子。" default_locale: "此论坛的缺省语言(ISO 639-1 代码)" allow_user_locale: "允许用户选择他们自己的语言界面" + set_locale_from_accept_language_header: "为未登录用户按照他们的浏览器发送的请求头部设置界面语言。(实验性,无法和匿名缓存共同使用)" min_post_length: "帖子允许的最少字符数" min_first_post_length: "第一帖(主题内容)允许的最少字符数" min_private_message_post_length: "消息允许的最小字符数" @@ -886,17 +893,17 @@ zh_CN: tl2_requires_likes_received: "一个初级用户升级到信任等级2所需要获得的赞数。" tl2_requires_likes_given: "一个初级用户升级到信任等级2所需要给出的赞数。" tl2_requires_topic_reply_count: "一个初级用户升级到信任等级2所需要回复的主题数量。" - tl3_time_period: "3级信任等级时间期" - tl3_requires_days_visited: "要维持信任等级 3 升级的用户,需要在最近 100 天内访问的最低次数。将其设置为高于信任等级 3 的评估时间来禁用升级至信任等级 3 功能。(设置为 0 或比那个值更高)" - tl3_requires_topics_replied_to: "在最近 100 天内升至信任等级3所需的回复主题的最小数量。(0或更高)" - tl3_requires_topics_viewed: "升至信任等级 3 所需阅读的最近 100 天内新创建主题的百分比。(0到100)" - tl3_requires_posts_read: "升至信任等级 3 所需阅读的最近 100 天内新创建帖子的百分比。(0到100)" + tl3_time_period: "信任等级 3 考察期限(天数)" + tl3_requires_days_visited: "要升到信任等级 3 所需要的,在最近考察时间段内访问的最低天数。将其设置为高于信任等级 3 的考察时间可禁用信任等级 3。(设置为 0 或比考察期更长)" + tl3_requires_topics_replied_to: "要升到信任等级 3 所需要的,在最近考察时间段内回复的主题数量。(设置为 0 或更高)" + tl3_requires_topics_viewed: "要升到信任等级 3 所需要的,在最近考察时间段内所查看的新主题的百分比。将其设置为高于信任等级 3 的考察时间可禁用信任等级 3。(设置为 0 到 100)" + tl3_requires_posts_read: "要升到信任等级 3 所需要的,在最近考察时间段内所查看的新主题的百分比。将其设置为高于信任等级 3 的考察时间可禁用信任等级 3。(设置为 0 或比考察期更长)" tl3_requires_topics_viewed_all_time: "用户升至信任等级 3 所需查看的最小主题数量。" tl3_requires_posts_read_all_time: "用户升至信任等级 3 所需查看的最小帖子数量。" - tl3_requires_max_flagged: "用户在最近 100 天内升至信任等级3所需的必须没有超过 x 个帖子被 x 个不同的用户标记数量,x为数量。(0或更高)" + tl3_requires_max_flagged: "要升到信任等级 3 所需要的,在最近考察时间段内,用户不得被 x 个不同用户标记 x 篇帖子的 x 值。(设置为 0 或更高)" tl3_promotion_min_duration: "信任等级3的用户可被降级至信任等级2前最小持续天数。" - tl3_requires_likes_given: "在最近 100 天内升至信任等级3所需给出的赞。" - tl3_requires_likes_received: "在最近 100 天内升至信任等级3所需收到的赞。" + tl3_requires_likes_given: "要升到信任等级 3 所需要的,在最近考察时间段内用户必须要给出的赞(设置为 0 或更高)" + tl3_requires_likes_received: "要升到信任等级 3 所需要的,在最近考察时间段内用户必须要收到的赞(设置为 0 或更高)" tl3_links_no_follow: "不移除信任等级3用户帖子中的链接中的 rel=nofollow 属性。" min_trust_to_create_topic: "创建主题所需的最低信任等级。" min_trust_to_edit_wiki_post: "能编辑维基模式帖子的最小信任等级" @@ -911,7 +918,7 @@ zh_CN: max_users_notified_per_group_mention: "当整个群组被提及,用户可能收到的最多提醒数量(超过阈值后将不会有更多新提醒)" create_thumbnails: "为太大而无法恰当地显示在帖子里的图片创建 lightbox 缩略图。" email_time_window_mins: "等待多少(n)分钟才给用户发送通知电子邮件,好让他们有机会自己来编辑和完善他们的帖子。" - private_email_time_window_seconds: "等待多少(n)秒后再给用户发送通知私人通知邮件,这让用户有机会编辑和完善他们的帖子。" + private_email_time_window_seconds: "等待多少(n)秒再给用户发送通知电子邮件,这可以让用户有时间来编辑和完善他们的消息。" email_posts_context: "在通知邮件中包含的作为上下文的回复数量。" flush_timings_secs: "向服务器刷新时间数据的频率,以秒为单位。" title_max_word_length: "在主题的标题中,允许的词语长度的最大字符数。" @@ -969,6 +976,8 @@ zh_CN: unsubscribe_via_email: "允许用户在发送的邮件的主题或正文中包含“unsubscribe”(未知中文版用什么)来退订邮件" unsubscribe_via_email_footer: "在发出的邮件底部包含退订链接" delete_email_logs_after_days: "在(N)天后删除邮件日志。设置为 0 无限期保留" + max_emails_per_day_per_user: "每日发送给用户的最大帖子数量。设置为 0 禁止限制" + manual_polling_enabled: "用 API 推送邮件回复。" pop3_polling_enabled: "轮询 POP3 收取邮件回复。" pop3_polling_ssl: "连接至 POP3 服务器时使用 SSL。(推荐)" pop3_polling_period_mins: "查询用于邮件的 POP3 账户的间隔(以分钟计)。注意:需要重新启动。" @@ -1013,7 +1022,7 @@ zh_CN: disable_avatar_education_message: "禁用更改头像操作的教育消息。" daily_performance_report: "每日分析 NGINX 日志并且发布详情主题到管理人员才能看到的主题" suppress_uncategorized_badge: "不要为主题列表中的未分类主题显示徽章。" - permalink_normalizations: "在匹配永久链接之前应用如下正则表达式,例如:/(\\/topic.*)\\?.*/\\1 将去掉所有主题路径的参数字符串。格式为使用正则表达式,以及在字符串使用 \\1 等等来访问捕获内容" + permalink_normalizations: "在匹配永久链接之前应用如下正则表达式,例如:/(topic.*)\\?.*/\\1 将去掉所有主题路径的参数字符串。格式为使用正则表达式+使用 \\1 等字符串来访问捕获内容" global_notice: "为所有访客显示“紧急的”全局横幅,留空隐藏它(可以使用 HTML)" disable_edit_notifications: "当 'download_remote_images_to_local' 启用时禁用系统编辑提醒。" automatically_unpin_topics: "当用户到达底部时自动解除主题置顶。" @@ -1051,6 +1060,7 @@ zh_CN: approve_unless_trust_level: "该信任等级之下的用户的帖子必须被审核" notify_about_queued_posts_after: "如果有帖子等待了设定的小时数后仍在等候处理,发送一封邮件给联络邮件地址。设为 0 将禁用这些邮件。" default_email_digest_frequency: "用户默认收到摘要邮件的默认频率。" + default_include_tl0_in_digests: "用户在摘要邮件中是否收到新用户帖子的默认设置。用户可以在参数设置中更改这个设置。" default_email_private_messages: "默认在有人发消息给用户时发送一封邮件通知。" default_email_direct: "默认在有人引用、回复、提及或者邀请用户时发送一封邮件通知。" default_email_mailing_list_mode: "默认为每一个新帖子发送一封邮件通知。" @@ -1089,7 +1099,8 @@ zh_CN: pop3_polling_password_is_empty: "在启用 POP3 轮询前,你必须设置 'pop3 polling password'。" pop3_polling_authentication_failed: "POP3 验证失败。请验证你的 pop3 账户信息。" reply_by_email_address_is_empty: "在启用邮件回复之前,你必须设置“reply by email address”。" - pop3_polling_disabled: "在启用邮件回复之前,你必须先启用 POP3 轮询。" + email_polling_disabled: "在启用邮件回复功能前,你必须启用手动或者 POP3 轮询。" + user_locale_not_enabled: "你必须先设置 'allow user locale' 再启用该设置。" notification_types: group_mentioned: "%{group_name} 在 %{link} @ 了你" mentioned: "%{display_username} 在 %{link} @ 了你" @@ -1111,11 +1122,6 @@ zh_CN: category: '分类' topic: '结果' user: '用户' - sso: - not_found: "无法找到或创建账户,联系站点管理员" - account_not_approved: "账户正在等待验证,完成后你将收到一封邮件提醒" - unknown_error: "更新信息时错误,联系站点管理员" - timeout_expired: "账户登录超时,请重试登录" original_poster: "原始作者" most_posts: "大部分帖子" most_recent_poster: "当前大部分帖子作者" @@ -1344,9 +1350,9 @@ zh_CN: spam: "你的帖子被标记为 **广告**:社群成员觉得它是广告,像是在过度地推广着什么,而不是预期中与主题有关的内容。" notify_moderators: "你的帖子被标记为 **需要版主关注**:社群成员认为帖子需要管理人员介入。" flags_dispositions: - agreed: "感谢通知我们。我们认为这是一个问题,并且我们正在了解情况。" + agreed: "谢谢你的消息。我们认为这是个问题。我们正在进行处理。" agreed_and_deleted: "感谢通知我们。我们认为这是一个问题,并且我们已经删除了帖子。" - disagreed: "感谢通知我们。我们正在了解情况。" + disagreed: "谢谢你的消息。我们正在进行处理。" deferred: "感谢通知我们。我们正在调查情况。" deferred_and_deleted: "感谢通知我们。我们已经删除了帖子。" temporarily_closed_due_to_flags: "主题因为大量的社群标记暂时关闭" @@ -1356,7 +1362,7 @@ zh_CN: text_body_template: | 你好, - 这是一封从%{site_name}自动发出的邮件,通知你的帖子已被隐藏。 + 这是%{site_name}自动发出的邮件,通知你的帖子已被隐藏。 %{base_url}%{url} @@ -1652,7 +1658,7 @@ zh_CN: text_body_template: | 你好, - 这是一封自 %{site_name} 自动发出的邮件,以告知你的帖子已因被社群多次标记而被自动隐藏。 + 这是%{site_name}自动发出的邮件,以告知你的帖子已因被社群多次标记而被自动隐藏。 处于谨慎的考虑,你的新账户被禁止创建新回复或主题。除非一个管理人员能复核你的账户。 @@ -1662,13 +1668,17 @@ zh_CN: text_body_template: | 你好, - 这是一封自 %{site_name} 自动发出的邮件,以告知你的账户被管理人员封禁。 + 这是%{site_name}自动发出的邮件。你的账户已经被管理人员封禁。 + + 请知晓在管理人员解封你之前,你不能够再创建新的回复或者主题。 + + 如果你对封禁有异议,请联系我们的[管理人员](%{base_url}/about)。 欲查看额外的指导,请查看我们的[社群指引](%{base_url}/guidelines)。 user_automatically_blocked: subject_template: "因标记而被封禁的新用户 %{username}" text_body_template: | - 这是一封自动发出的邮件。 + 这是自动发出的邮件。 因多位用户标记%{username}的帖子,新用户[%{username}](%{base_url}%{user_url})已被自动封禁。 @@ -1678,7 +1688,7 @@ zh_CN: spam_post_blocked: subject_template: "新用户 %{username} 因重复发布链接而被禁止发表相关帖子" text_body_template: | - 这是一封自动发出的邮件。 + 这是自动发出的邮件。 新用户[%{username}](%{base_url}%{user_url})试图创建多个链接至 %{domains} 的帖子,但这些帖子因为反垃圾策略而被阻挡了。用户仍能够发表不包含到 %{domains} 的帖子。 @@ -1690,7 +1700,7 @@ zh_CN: text_body_template: | 你好, - 这是一封自 %{site_name} 自动发出的邮件,以告知你的账户已经在管理人员审核后被解封。 + 这是%{site_name}自动发出的邮件,以告知你的账户已经在管理人员审核后被解封。 你现在又可以创建新的回复和主题了。 pending_users_reminder: @@ -1711,6 +1721,8 @@ zh_CN: subject_pm: "[私信]" user_notifications: previous_discussion: "之前的回复" + reached_limit: + other: "警告:你达到了每日邮件限额(%{count})。之后邮件通知将被禁用。" in_reply_to: "回复给" unsubscribe: title: "取消订阅" @@ -1723,49 +1735,10 @@ zh_CN: posted_by: "%{username}发表于%{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} 邀请你加入消息交流:'%{topic_title}'" - text_body_template: |2 - - %{username} 邀请你至消息交流: - - > **%{topic_title}** - > - > %{topic_excerpt} - - 论坛: - - > %{site_title} -- %{site_description} - - 请访问 %{base_url}%{url} 来查看该主题。 user_invited_to_private_message_pm_staged: subject_template: "[%{site_name}] %{username} 邀请你加入消息交流:'%{topic_title}'" - text_body_template: |2 - - %{username} 邀请你至消息交流: - - > **%{topic_title}** - > - > %{topic_excerpt} - - 论坛: - - > %{site_title} -- %{site_description} - - 请访问 %{base_url}%{url} 来查看该主题。 user_invited_to_topic: subject_template: "[%{site_name}] %{username}邀请你参与“%{topic_title}”" - text_body_template: |2 - - %{username} 邀请你参与讨论: - - > **%{topic_title}** - > - > %{topic_excerpt} - - 论坛: - - > %{site_title} -- %{site_description} - - 请访问 %{base_url}%{url} 来查看该主题。 user_replied: subject_template: "[%{site_name}] %{topic_title}" text_body_template: | @@ -1925,7 +1898,7 @@ zh_CN: notify_old_email: subject_template: "[%{site_name}] 你的邮箱地址已经修改成功" text_body_template: | - 这是一封自 %{site_name} 自动发出的邮件,以告知你的邮箱地址已经被修改了。如果这是一个错误,请联系站点管理人员。 + 这是%{site_name}自动发出的邮件,以告知你的邮箱地址已经被修改了。如果这是一个错误,请联系站点管理人员。 你的邮箱地址被修改为: @@ -2000,6 +1973,7 @@ zh_CN: post_deleted: "主题被作者删除" user_suspended: "用户被封禁" already_read: "用户已经阅读了主题" + exceeded_limit: "超过 max_emails_per_day_per_user" message_blank: "消息为空" message_to_blank: "message.to 为空" text_part_body_blank: "text_part.body 为空" @@ -2240,86 +2214,7 @@ zh_CN: 文档以 CC-BY-SA 发布。最后更新时间为2013年5月31日。 static: - search_help: | -

    小技巧

    -

    -

      -
    • 标题匹配优先 – 不知道搜什么时,搜索标题
    • -
    • 搜索独一无二且不常见的词可以找到最好的结果
    • -
    • 试试搜索特定的分类、用户或主题
    • -
    -

    -

    选项

    -

    - - - - - - - -
    order:views(按照显示次数排序)order:latest(按照更新时间排序)order:likes(按照赞的次数排序)
    status:open(搜索正常状态的主题)status:closed(搜索已经关闭的主题)status:archived(搜索已经存档的主题)status:noreplies(搜索没有回复的主题)status:single_user(搜索单个用户的主题)
    category:分类名user:用户名group:群组名badge:徽章名
    in:likes(在已经赞的帖子里搜索)in:posted(在发表的帖子里搜索)in:watching(在关注的帖子里搜索)in:tracking(在追踪的帖子里搜索)in:private(在私密的帖子里搜索)
    in:bookmarks(在加书签的帖子里搜索)in:first(在第一帖里搜索)
    posts_count:数字(帖子数量)before:天数或者日期after:天数或者日期
    -

    -

    例子

    -

    -

      -
    • 彩虹 category:公园 status:open order:latest 将搜索在“公园”分类中没有关闭或存档中的名字包含“彩虹”的主题,并按最后一个帖子的日期来排序。
    • -
    • 彩虹 category:"公园和花园" in:bookmarks 将搜索在“公园和花园”分类中已被你加过书签的且名字包含“彩虹”的主题。
    • -
    -

    - badges: - long_descriptions: - autobiographer: | - 该徽章授予给填写了用户页面并选择了用户头像的成员。让社群更多地了解你是谁以及你感兴趣的内容能帮助创造一个更好的更团结的社群。 - first_like: | - 该徽章授予给第一次使用 :heart: 按钮赞了帖子的成员。给帖子点赞是一个极好的让社群成员知道他们的帖子有意思、有用、酷炫或者好玩的方法。分享爱! - first_link: |+ - 该徽章授予给第一次在回复中包含了到另一个主题的链接。链接主题在两个主题的右侧显示连接,可以帮助阅读者迅速地找到相关讨论。 - - first_quote: | - 该徽章授予给第一次在回复中包含了到另一个主题的链接。链接主题在两个主题的右侧显示连接,可以帮助阅读者迅速地找到相关讨论。 - first_share: | - 该徽章授予给第一次用分享按钮分享了回复或者主题的链接的成员。分享链接是一个极好的方式向外部世界展示有趣的讨论的方法,并且能推动社群增长。 - read_guidelines: |+ - 该徽章授予给阅读了社群指引的成员。遵守和分享简单的指引能帮助打造一个安全、有趣和可持续的社群。 - - reader: | - 该徽章授予给阅读了一个较长的主题的成员。阅读是最基础的。详细阅读帮助你更好地了解讨论并帮助你编写更好的、更完整的回复。 - editor: | - 该徽章授予给编辑自己帖子的成员。任何时候都不要犹豫编辑你的帖子以改善他们、改正小错误或者增加你忘记的内容。 - first_flag: | - 该徽章授予给标记过帖子的用户。标记对社群的健康很重要。如果你注意到了任何需要版主注意的帖子,请不要犹豫,直接标记 它。你可能也使用了标记对话框向用户发送了消息。 - nice_share: | - 该徽章授予给分享帖子的链接至超过 25 个外部访客的成员。干得好!分享有趣的讨论给朋友是一个帮助社群成长的极好方式。 - welcome: | - 该徽章授予给帖子收到了第一个赞的成员。恭喜,有社群成员发现你发表的内容有意思、酷炫或者有用! - anniversary: | - 该徽章授予给在社群超过一年并且这一年中至少发布了一个帖子的成员。谢谢你的关注和对我们社群的贡献! - good_share: | - 该徽章授予给分享帖子的链接至超过 300 个外部访客的成员。做得真棒!你已经把一个有意思的讨论分享给了许多新人,并且帮助了社群成长。 - great_share: | - 该徽章授予给分享帖子的链接至超过 100 个外部访客的成员。哇!你已经把一个有趣的主题分享给了许多新读者,并且帮助了社群成长。 - nice_post: |+ - 该徽章授予给创建了得到 10 个赞的回复的成员。干得好! - - nice_topic: | - 该徽章授予给创建了得到 10 个赞的主题的成员。干得好! - good_post: | - 该徽章授予给创建了得到 25 个赞的回复的成员。干得好! - good_topic: | - 该徽章授予给创建了得到 25 个赞的主题的成员。干得好! - great_post: | - 该徽章授予给创建了得到 50 个赞的帖子的成员。哇! - great_topic: | - 该徽章授予给创建了得到 50 个赞的回复的成员。哇! - basic: | - 该徽章授予给用户等级达到 1 的成员。感谢你在社群里花了一些时间并且阅读了一些帖子,了解了我们的社群。你的新用户限制已经被取消,并且你已经被授予了所有基本的社群权限,必须个人消息、标记、维基编辑和发布图片和多个链接的能力。 - member: | - 该徽章授予给用户等级达到 2 的成员。感谢你在社群的这几周时间。你现在已经可以在你的用户页或者主题中发送私人邀请、创建群组消息,以及每天可以赞更多次了。 - regular: | - 该徽章授予给用户等级达到 3 的成员。感谢你在社群的这几月的活跃参与,并且是我们最活跃的读者质疑,也是社群中的可靠贡献者。你现在可以重新分类和重命名主题、访问隐藏的贵宾分类、使用更多强大的防骚扰标记功能,以及每天可以赞更多次了。 - leader: | - 该徽章授予给用户等级达到 4 的成员。你是由管理人员选择的社群领袖,鉴于你在社群的话语和贡献,你为社群树立了一个正面的例子。你有权限编辑所有的帖子,使用主题管理操作,例如置顶、关闭、不在列表中显示、存档、分割和合并,并且可以每天点赞非常多次。 + search_help: "

    小技巧

    \n

    \n

      \n
    • 标题匹配优先 – 不知道搜什么时,搜索标题
    • \n
    • 搜索独一无二且不常见的词可以找到最好的结果
    • \n
    • 试试搜索特定的分类、用户或主题
    • \n
    \n

    \n

    选项

    \n

    \n\n\n\n<;td>badge:徽章名\n\n\n\n\n
    order:views(按照显示次数排序)order:latest(按照更新时间排序)order:likes(按照赞的次数排序)
    status:open(搜索正常状态的主题)status:closed(搜索已经关闭的主题)status:archived(搜索已经存档的主题)status:noreplies(搜索没有回复的主题)status:single_user(搜索单个用户的主题)
    category:分类名user:用户名<;code>group:群组名
    in:likes(在已经赞的帖子里搜索)in:posted(在发表的帖子里搜索)in:watching(在关注的帖子里搜索)in:tracking(在追踪的帖子里搜索)in:private(在私密的帖子里搜索)
    in:bookmarks(在加书签的帖子里搜索)in:first(在第一帖里搜索)
    posts_count:数字(帖子数量)before:天数或者日期after:天数或者日期
    \n

    \n

    例子

    \n

    \n

      \n
    • 彩虹 category:公园 status:open order:latest 将搜索在“公园”分类中没有关闭或存档中的名字包含“彩虹”的主题,并按最后一个帖子的日期来排序。
    • \n
    • 彩虹 category:\"公园和花园\" in:bookmarks<;/code> 将搜索在“公园和花园”分类中已被你加过书签的且名字包含“彩虹”的主题。
    • \n
    \n

    \n" admin_login: success: "邮件已发送" error: "错误!" diff --git a/config/locales/server.zh_TW.yml b/config/locales/server.zh_TW.yml index 228829eb41..86b2e2e27a 100644 --- a/config/locales/server.zh_TW.yml +++ b/config/locales/server.zh_TW.yml @@ -126,7 +126,6 @@ zh_TW: can_not_modify_automatic: "你無法修改自動群組" member_already_exist: "'%{username}' 已經是群組成員了。" invalid_domain: "'%{domain}' 不是有效的域名." - invalid_incoming_email: "'%{incoming_email}' 不是有效的電子郵件地址." default_names: everyone: "所有人" admins: "管理員" @@ -520,7 +519,6 @@ zh_TW: max_topic_title_length: "標題允許的最大文字數" min_private_message_title_length: "標題允許的最小文字數" min_search_term_length: "搜尋條件允許的最小文字數" - uncategorized_description: "\"未分類\"的分類的描述,留空則無描述" allow_duplicate_topic_titles: "允許話題有相同,重複的標題" unique_posts_mins: "使用者再次發表包含相同內容文章的間隔時間" educate_until_posts: "當用戶開始鍵入他們的前幾個 (n) 新帖子時,在編輯器上顯示教育面板視窗。" @@ -671,15 +669,9 @@ zh_TW: tl2_requires_likes_received: "一個初級用戶升級到信任等級2所需要獲得的讚賞數。" tl2_requires_likes_given: "初級用戶升級到信任等級2所需要付出的讚賞數。" tl2_requires_topic_reply_count: "一個初級用戶升級到信任等級2所需要回覆的主題數量。" - tl3_requires_topics_replied_to: "在最近 100 天內升至信任等級3所需的回覆主題的最少數量。(0或更高)" - tl3_requires_topics_viewed: "在最近 100 天內升至信任等級3所需的創建主題的百分比。(0到100)" - tl3_requires_posts_read: "在最近 100 天內升信任等級3所需的創建帖子的百分比。(0到100)" tl3_requires_topics_viewed_all_time: "用戶升至信任等級3所需查看的最少主題數量。" tl3_requires_posts_read_all_time: "用戶升至信任等級3所需查看的最少帖子數量。" - tl3_requires_max_flagged: "用戶在最近 100 天內升至信任等級3所需的必須沒有超過 x 個帖子被 x 個不同的用戶標記數量,x為數量。(0或更高)" tl3_promotion_min_duration: "信任等級3的用戶可被降級至信任等級2前最少持續天數。" - tl3_requires_likes_given: "升至信任等級3需要在最近 100 天內所給出最少的讚。" - tl3_requires_likes_received: "升至信任等級3需要在最近 100 天內所收到最少的讚。" tl3_links_no_follow: "禁止從信任等級 3 之用戶所發表的連結中刪除 rel=nofollow 標籤" min_trust_to_create_topic: "建立話題最低所需的信任等級" min_trust_to_edit_wiki_post: "編輯被標示為維基的文章所需之最低信任等級。" @@ -737,7 +729,6 @@ zh_TW: automatically_download_gravatars: "當用戶註冊或更改EMail時下載 Gravatars 圖片" digest_topics: "EMail 摘要中顯示的最大話題數量" digest_min_excerpt_length: "EMail 摘要中每篇文章最少顯示的字元數量" - delete_digest_email_after_days: "不發送摘要郵件給超過 (n) 天閒置的用戶。" disable_digest_emails: "禁用發送摘要郵件給所有用戶。" max_daily_gravatar_crawls: "一天內 Discourse 將自動檢查 Gravatar 自訂個人圖示的次數" public_user_custom_fields: "用戶可設定公開顯示的自定欄位白名單。" @@ -784,8 +775,6 @@ zh_TW: category: '分類' topic: '結果' user: '用戶' - sso: - account_not_approved: "帳戶正在等待驗證,完成後你將收到一封電郵提醒" original_poster: "原始作者" most_posts: "大部分文章" most_recent_poster: "當前大部分文章作者" @@ -852,11 +841,6 @@ zh_TW: subject_template: "[%{site_name}] 有可用的新版 Discourse 更新" new_version_mailer_with_notes: subject_template: "[%{site_name}] 可以升級" - flags_reminder: - flags_were_submitted: - other: "這些標記在過去 %{count} 小時內被提交。" - please_review: "請再次確認。" - post_number: "文章" flags_dispositions: disagreed: "感謝你讓我們知道。我們正在調查。" deferred: "感謝你讓我們知道。我們正在調查。" @@ -944,12 +928,6 @@ zh_TW: subject_template: "[%{site_name}] 設定密碼" account_created: subject_template: "[%{site_name}] 你的新帳號" - confirm_new_email: - subject_template: "[%{site_name}] 確認你的新電子郵箱位址" - text_body_template: | - 點擊下面的連結來確認你在 %{site_name} 上的新電子郵箱位址: - - %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "你已受到 %{site_name} 的認可!" signup: @@ -961,7 +939,6 @@ zh_TW: 如果上面的連結無法點擊,請拷貝該連結並粘貼到你的流覽器的地址欄裡。 page_not_found: - title: "你請求的頁面在本論壇不存在。可能我們能幫助你找到它,或者類似的其它討論話題:" popular_topics: "熱門" recent_topics: "最近" see_more: "更多" @@ -1011,20 +988,6 @@ zh_TW: title: "服務條款" privacy_topic: title: "隱私政策" - badges: - long_descriptions: - nice_post: | - 此徽章授予創建一個能獲取10個讚的回應。幹得不錯! - nice_topic: | - 此徽章授予創建一個能獲取10個讚的話題。幹得不錯! - good_post: | - 此徽章授予創建一個能獲取25個讚的回應。幹得好! - good_topic: | - 此徽章授予創建一個能獲取25個讚的話題。幹得好! - great_post: | - 此徽章授予創建一個能獲取50個讚的又章。勁! - great_topic: | - 此徽章授予創建一個能獲取50個讚的回應。勁! admin_login: success: "已發送郵件" error: "錯誤" From ef4877e009e6c164fb076caa4682f23b41bacd0c Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 28 Mar 2016 12:17:37 -0400 Subject: [PATCH 135/162] FIX: Stability issues with multiple polls --- .../assets/javascripts/initializers/extend-for-poll.js.es6 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/poll/assets/javascripts/initializers/extend-for-poll.js.es6 b/plugins/poll/assets/javascripts/initializers/extend-for-poll.js.es6 index c27957849d..68fe20140e 100644 --- a/plugins/poll/assets/javascripts/initializers/extend-for-poll.js.es6 +++ b/plugins/poll/assets/javascripts/initializers/extend-for-poll.js.es6 @@ -67,6 +67,7 @@ function initializePolls(api) { if (!$polls.length) { return; } const post = helper.getModel(); + api.preventCloak(post.id); const votes = post.get('polls_votes') || {}; post.pollsChanged(); @@ -82,11 +83,12 @@ function initializePolls(api) { const $poll = $(pollElem); const pollName = $poll.data("poll-name"); + const pollId = `${pollName}-${post.id}`; const pollView = createPollView(helper.container, post, polls[pollName], votes[pollName]); $poll.replaceWith($div); Em.run.next(() => pollView.renderer.replaceIn(pollView, $div[0])); - postPollViews[pollName] = pollView; + postPollViews[pollId] = pollView; }); _pollViews = postPollViews; From e29806b9d3c9131cd09c9558f6a9ee23323731e9 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 28 Mar 2016 22:11:08 +0530 Subject: [PATCH 136/162] UX: center align exceptions page button on mobile --- app/assets/javascripts/discourse/controllers/exception.js.es6 | 3 ++- app/assets/javascripts/discourse/templates/exception.hbs | 2 +- app/assets/stylesheets/common/base/exception.scss | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/exception.js.es6 b/app/assets/javascripts/discourse/controllers/exception.js.es6 index 2adcff6c0f..a44d0b9c93 100644 --- a/app/assets/javascripts/discourse/controllers/exception.js.es6 +++ b/app/assets/javascripts/discourse/controllers/exception.js.es6 @@ -11,7 +11,8 @@ var ButtonBackBright = { ButtonTryAgain = { classes: "btn-primary", action: "tryLoading", - key: "errors.buttons.again" + key: "errors.buttons.again", + icon: "refresh" }, ButtonLoadPage = { classes: "btn-primary", diff --git a/app/assets/javascripts/discourse/templates/exception.hbs b/app/assets/javascripts/discourse/templates/exception.hbs index 9416f2b436..f8e486bb6c 100644 --- a/app/assets/javascripts/discourse/templates/exception.hbs +++ b/app/assets/javascripts/discourse/templates/exception.hbs @@ -14,7 +14,7 @@
    {{#each buttonData in enabledButtons}} - + {{d-button icon=buttonData.icon action=buttonData.action label=buttonData.key class=buttonData.classes}} {{/each}} {{conditional-loading-spinner condition=loading}}
    diff --git a/app/assets/stylesheets/common/base/exception.scss b/app/assets/stylesheets/common/base/exception.scss index 3b3282104d..c9a7087651 100644 --- a/app/assets/stylesheets/common/base/exception.scss +++ b/app/assets/stylesheets/common/base/exception.scss @@ -21,6 +21,7 @@ } } .buttons { + display: inline-flex; margin-top: 15px; button { From b6a0b891b8d0216a649db409eb909f61a2128721 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 28 Mar 2016 23:34:04 +0530 Subject: [PATCH 137/162] Update Translations --- config/locales/client.de.yml | 48 +++--- config/locales/client.ro.yml | 42 +++++- config/locales/client.zh_CN.yml | 14 +- config/locales/client.zh_TW.yml | 34 +++++ config/locales/server.fr.yml | 6 + config/locales/server.ro.yml | 1 + config/locales/server.zh_CN.yml | 255 +++++++++++++++++++++++++++++++- 7 files changed, 365 insertions(+), 35 deletions(-) diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index 52a4a4bede..b3aa7df027 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -271,7 +271,7 @@ de: one: "Dieses Thema hat einen Beitrag, der genehmigt werden muss" other: "Dieses Thema hat {{count}} Beiträge, die genehmigt werden müssen" confirm: "Änderungen speichern" - delete_prompt: "Möchtest du wirklich %{username} löschen? Damit werden alle ihre/seine Beiträge entfernt und ihre/seine E-Mail- und IP-Adresse geblockt." + delete_prompt: "Möchtest du wirklich %{username} löschen? Damit werden alle Beiträge des Benutzers entfernt und dessen E-Mail- und IP-Adresse geblockt." approval: title: "Beitrag muss genehmigt werden" description: "Wir haben deinen neuen Beitrag erhalten. Dieser muss allerdings zunächst durch einen Moderator freigeschaltet werden. Bitte habe etwas Geduld. " @@ -345,16 +345,16 @@ de: notifications: watching: title: "Beobachten" - description: "Du wirst über jeden neuen Beitrag in jeder Nachricht benachrichtigt und die Anzahl der neuen Antworten wird angezeigt." + description: "Du wirst über jeden neuen Beitrag in jeder Nachricht benachrichtigt und die Anzahl neuer Antworten wird angezeigt." tracking: title: "Verfolgen" - description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet, und die Anzahl der neuen Antworten wird angezeigt." + description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet, und die Anzahl neuer Antworten wird angezeigt." regular: title: "Normal" - description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet." + description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet." muted: title: "Stummgeschaltet" - description: "Du erhältst keine Benachrichtigungen im Zusammenhang mit Nachrichten in dieser Gruppe." + description: "Du erhältst keine Benachrichtigungen über neue Themen in dieser Gruppe." user_action_groups: '1': "Abgegebene Likes" '2': "Erhaltene Likes" @@ -436,7 +436,7 @@ de: not_supported: "Dieser Browser unterstützt leider keine Benachrichtigungen." perm_default: "Benachrichtigungen einschalten" perm_denied_btn: "Zugriff verweigert" - perm_denied_expl: "Du hast Benachrichtigungen blockiert. Aktiviere die Benachrichtigungen über deine Browser Einstellungen." + perm_denied_expl: "Du hast das Anzeigen von Benachrichtigungen verboten. Aktiviere die Benachrichtigungen über deine Browser-Einstellungen." disable: "Benachrichtigungen deaktivieren" enable: "Benachrichtigungen aktivieren" each_browser_note: "Hinweis: Du musst diese Einstellung in jedem von dir verwendeten Browser ändern." @@ -473,7 +473,7 @@ de: muted_users: "Stummgeschaltet" muted_users_instructions: "Alle Benachrichtigungen von diesem Benutzer unterdrücken." muted_topics_link: "Zeige stummgeschaltete Themen" - automatically_unpin_topics: "Themen automatisch loslösen, wenn ich das Ende erreiche." + automatically_unpin_topics: "Angeheftete Themen automatisch loslösen, wenn ich deren letzten Beitrag gelesen habe." staff_counters: flags_given: "hilfreiche Meldungen" flagged_posts: "gemeldete Beiträge" @@ -489,7 +489,7 @@ de: bulk_select: "Nachrichten auswählen" move_to_inbox: "In Posteingang verschieben" move_to_archive: "Archivieren" - failed_to_move: "Die ausgewählten Nachrichten konnten nicht bewegt werden (vielleicht gibt es ein Netzwerkproblem)." + failed_to_move: "Die ausgewählten Nachrichten konnten nicht verschoben werden. Vielleicht gibt es ein Netzwerkproblem." select_all: "Alle auswählen" change_password: success: "(E-Mail gesendet)" @@ -575,14 +575,14 @@ de: website: "Website" email_settings: "E-Mail" like_notification_frequency: - title: "Melde es wenn du es magst" - always: "Immer" - first_time_and_daily: "Das erste mal das jemandem der tägliche Beitrag gefällt" - first_time: "Das erste mal das der Beitrag jemandem gefällt" - never: "Nie" + title: "Benachrichtigung für erhaltene Likes anzeigen" + always: "immer" + first_time_and_daily: "erster Like eines Beitrags und täglich" + first_time: "nur erster Like eines Beitrags" + never: "nie" email_previous_replies: - title: "Füge vorherige Antworten ans Ende der E-Mail an" - unless_emailed: "soweit nicht vorher gesendet" + title: "Füge vorherige Beiträge ans Ende von E-Mails an" + unless_emailed: "sofern noch nicht gesendet" always: "immer" never: "nie" email_digests: @@ -610,7 +610,7 @@ de: after_2_weeks: "in den letzten 2 Wochen erstellt" auto_track_topics: "Betrachteten Themen automatisch folgen" auto_track_options: - never: "niemals" + never: "nie" immediately: "sofort" after_30_seconds: "nach 30 Sekunden" after_1_minute: "nach 1 Minute" @@ -1130,8 +1130,8 @@ de: '2_4': 'Du wirst Benachrichtigungen erhalten, weil du eine Antwort zu diesem Thema verfasst hast.' '2_2': 'Du wirst Benachrichtigungen erhalten, weil du dieses Thema verfolgst.' '2': 'Du wirst Benachrichtigungen erhalten, weil du dieses Thema gelesen hast.' - '1_2': 'Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet.' - '1': 'Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet.' + '1_2': 'Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet.' + '1': 'Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet.' '0_7': 'Du ignorierst alle Benachrichtigungen dieser Kategorie.' '0_2': 'Du ignorierst alle Benachrichtigungen dieses Themas.' '0': 'Du ignorierst alle Benachrichtigungen dieses Themas.' @@ -1143,16 +1143,16 @@ de: description: "Du wirst über jeden neuen Beitrag in diesem Thema benachrichtigt und die Anzahl der neuen Antworten wird angezeigt." tracking_pm: title: "Verfolgen" - description: "Die Anzahl der neuen Antworten wird bei dieser Unterhaltung angezeigt. Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet." + description: "Die Anzahl der neuen Antworten wird bei dieser Unterhaltung angezeigt. Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deine Nachricht antwortet." tracking: title: "Verfolgen" - description: "Die Anzahl der neuen Antworten wird bei diesem Thema angezeigt. Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet." + description: "Die Anzahl der neuen Antworten wird bei diesem Thema angezeigt. Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet." regular: title: "Normal" - description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet." + description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet." regular_pm: title: "Normal" - description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet." + description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deine Nachricht antwortet." muted_pm: title: "Stummgeschaltet" description: "Du erhältst keine Benachrichtigungen im Zusammenhang mit dieser Unterhaltung." @@ -1540,10 +1540,10 @@ de: description: "Du wirst automatisch alle neuen Themen in diesen Kategorien beobachten. Du wirst über jeden neuen Beitrag in jedem Thema benachrichtigt und die Anzahl neuer Antworten wird angezeigt." tracking: title: "Verfolgen" - description: "Du wirst automatisch allen neuen Themen in diesen Kategorien folgen. Du wirst benachrichtigt, wenn dich jemand mit @Name erwähnt oder dir antwortet, und die Anzahl neuer Antworten wird angezeigt." + description: "Du wirst automatisch allen neuen Themen in diesen Kategorien folgen. Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet, und die Anzahl neuer Antworten wird angezeigt." regular: title: "Normal" - description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet." + description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet." muted: title: "Stummgeschaltet" description: "Du erhältst nie mehr Benachrichtigungen über neue Themen in dieser Kategorie und die Themen werden auch nicht in der Liste der letzten Themen erscheinen." diff --git a/config/locales/client.ro.yml b/config/locales/client.ro.yml index 4d18b98c9f..3987863a9e 100644 --- a/config/locales/client.ro.yml +++ b/config/locales/client.ro.yml @@ -220,7 +220,7 @@ ro: all_time: "Tot timpul" last_7_days: "Ultimele 7 zile" last_30_days: "Ultimele 30 de zile" - like_count: "Like-uri" + like_count: "Aprecieri" topic_count: "Subiecte" post_count: "Postări" user_count: "Utilizatori noi" @@ -346,6 +346,8 @@ ro: other: "grupuri" members: "Membri" posts: "Postări" + mentions: "Mențiuni" + messages: "Mesaje" alias_levels: nobody: "Nimeni" only_admins: "Doar Adminii" @@ -353,8 +355,13 @@ ro: members_mods_and_admins: "Doar membri grupului, moderatorii și adminii" everyone: "Toată lumea" notifications: + watching: + title: "Urmărit" + tracking: + title: "Urmărit" regular: title: "Normal" + description: "Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply." user_action_groups: '1': "Aprecieri Date" '2': "Aprecieri Primite" @@ -1021,30 +1028,39 @@ ro: '3_2': 'Veți primi notificări fiindcă citiți această discuție.' '3_1': 'Veți primi notificări fiindcă ați creat această discuție.' '3': 'Veți primi notificări fiindcă priviți această discuție.' - '2_8': 'Veți primi notificări fiindcă urmariți această categorie.' + '2_8': 'Veți primi notificări fiindcă urmăriți această categorie.' '2_4': 'Veți primi notificări fiindcă ați postat un răspuns în această discuție.' - '2_2': 'Veți primi notificări fiindcă urmariți această discuție.' + '2_2': 'Veți primi notificări fiindcă urmăriți această discuție.' '2': 'Veți primi notificări fiindcă citiți această discuție.' + '1_2': 'Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply.' + '1': 'Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply.' '0_7': 'Ignorați toate notificările din această categorie.' '0_2': 'Ignorați toate notificările din această discuție.' '0': 'Ignorați toate notificările din această discuție.' watching_pm: - title: "Privind" + title: "Urmărit Activ" + description: "Numărul postărilor noi va fi arătat pentru acest mesaj și veți fi notificat pentru orice reply scris." watching: - title: "Privind" + title: "Urmărit Activ" + description: "Numărul postărilor noi va fi arătat pentru acest topic și veți fi notificat pentru orice reply scris." tracking_pm: - title: "Urmărind" + title: "Urmărit" + description: "Numărul postărilor noi va fi arătat pentru acest mesaj. Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply." tracking: - title: "Urmărind" + title: "Urmărit" + description: "Numărul postărilor noi va fi arătat pentru acest topic. Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply." regular: title: "Normal" + description: "Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply." regular_pm: title: "Normal" + description: "Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply." muted_pm: title: "Silențios" description: "Nu veţi fi niciodată notificat despre acest mesaj." muted: title: "Silențios" + description: "Nu veți fi notificat de discuțiile noi din acest topic și nu vor apărea în tabul necitite." actions: recover: "Rescrie discuție" delete: "Șterge Discuție" @@ -1185,6 +1201,15 @@ ro: other: "vedeţi {{count}} răspunsuri ascunse" more_links: "{{count}} mai multe..." unread: "postarea nu a fost citită" + has_likes: + one: "O Apreciere" + few: "{{count}} Aprecieri" + other: "{{count}} de Aprecieri" + has_likes_title_only_you: "Ai apreciat acest post" + has_likes_title_you: + one: "Ați apreciat acest post împreună cu un alt utilizator." + few: "Ați apreciat acest post împreună cu alți {{count}} utilizatori." + other: "Ați apreciat acest post împreună cu alți {{count}} utilizatori." errors: create: "Ne pare rău , s-a semnalat o eroare în creerea postării dumneavoastră.Vă rugăm încercati iar." edit: "Ne pare rău , s-a semnalat o eroare în editarea postării dumneavoastră . Vă rugăm încercati iar." @@ -1207,7 +1232,7 @@ ro: controls: reply: "începe compunerea unui răspuns pentru această postare" like: "apreciează acestă postăre" - has_liked: "ai retras aprecierea acestei postări " + has_liked: "ai apreciat acest răspuns" undo_like: "anuleazaă aprecierea" edit: "editează această postare" edit_anonymous: "Ne pare rău, dar trebuie să fiţi autentificat pentru a edita." @@ -1407,6 +1432,7 @@ ro: title: "Urmărire" regular: title: "Normal" + description: "Veți fi notificat dacă cineva vă menționează @numele sau vă scrie un reply." muted: title: "Silențios" flagging: diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index 1b1f3f6275..1b3cdfae8b 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -693,6 +693,11 @@ zh_CN: too_few_topics_and_posts_notice: "让我们开始讨论!目前有 %{currentTopics} / %{requiredTopics} 个主题和 %{currentPosts} / %{requiredPosts} 个帖子。新访客需要能够阅读和回复一些讨论。" too_few_topics_notice: "让我们开始讨论!目前有 %{currentTopics} / %{requiredTopics} 个主题。新访客需要能够阅读和回复一些讨论。" too_few_posts_notice: "让我们开始讨论!目前有 %{currentPosts} / %{requiredPosts} 个帖子。新访客需要能够阅读和回复一些讨论。" + logs_error_rate_notice: + reached: "%{timestamp}:目前的错误率 %{rate} 已经达到了站点设置中的 %{siteSettingRate}。" + exceeded: "%{timestamp}:目前的错误率 %{rate} 已经超出了站点设置中的 %{siteSettingRate}。" + rate: + other: "%{count} 错误/%{duration}" learn_more: "了解更多..." year: '年' year_desc: '365 天以前创建的主题' @@ -2198,7 +2203,7 @@ zh_CN: moderator: "版主?" admin: "管理员?" blocked: "已封?" - staged: "部分权限?" + staged: "暂存?" show_admin_profile: "管理员" edit_title: "编辑头衔" save_title: "保存头衔" @@ -2265,7 +2270,7 @@ zh_CN: deactivate_explanation: "已停用的用户必须重新验证他们的电子邮件。" suspended_explanation: "一个被封禁的用户不能登录。" block_explanation: "被封禁的用户不能发表主题或者评论。" - stage_explanation: "部分访问权限的用户只能通过邮件在特定主题内发表帖子。" + stage_explanation: "暂存用户只能通过邮件在特定主题内发表帖子。" trust_level_change_failed: "改变用户等级时出现了一个问题。" suspend_modal_title: "被禁用户" trust_level_2_users: "二级信任等级用户" @@ -2385,6 +2390,7 @@ zh_CN: badge: 徽章 display_name: 显示名称 description: 描述 + long_description: 详情 badge_type: 徽章分类 badge_grouping: 群组 badge_groupings: @@ -2545,7 +2551,11 @@ zh_CN: badges: earned_n_times: other: "授予徽章 %{count} 次" + granted_on: "授予于 %{date}" + others_count: "其他有该徽章的人(%{count})" title: 徽章 + allow_title: "能用作头衔" + multiple_grant: "能被授予多次" badge_count: other: "%{count} 个徽章" more_badges: diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index 517b01bce0..03b74dc0de 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -272,7 +272,9 @@ zh_TW: title: other: "群組" members: "成員" + topics: "主題" posts: "文章" + messages: "訊息" alias_levels: title: "誰可以在這個群組發送訊息和使用@提到" nobody: "沒有" @@ -283,6 +285,11 @@ zh_TW: trust_levels: title: "當這些成員加入時自動提升信任等級:" none: "無" + notifications: + watching: + title: "關注" + regular: + title: "一般" user_action_groups: '1': "已按讚" '2': "已收到的讚" @@ -398,7 +405,9 @@ zh_TW: warnings_received: "警告" messages: all: "全部" + archive: "封存" groups: "我的群組" + move_to_archive: "封存" change_password: success: "( 寄出的郵件 )" in_progress: "( 正在傳送郵件 )" @@ -839,6 +848,8 @@ zh_TW: create: '新討論話題' create_long: '建立新討論話題' private_message: '發送訊息' + archive_message: + title: '封存' list: '討論話題' new: '新討論話題' unread: '未讀' @@ -887,6 +898,7 @@ zh_TW: go_top: "頂部" go_bottom: "底部" go: "前往" + jump_bottom: "跳至最後一則帖子" jump_bottom_with_number: "跳至第 %{post_number} 篇文章" total: 所有文章 current: 目前的文章 @@ -1042,6 +1054,7 @@ zh_TW: other: "{{count}} 個讚" has_likes_title: other: "{{count}} 個使用者對此文章讚好" + has_likes_title_only_you: "你已按讚" errors: create: "抱歉,建立你的文章時發生錯誤,請再試一次。" edit: "抱歉,編輯你的文章時發生錯誤,請再試一次。" @@ -1105,6 +1118,12 @@ zh_TW: bookmark: "移除書籤" like: "撤回讚" vote: "撤回投票" + people: + off_topic: "投訴為離題內容" + spam: "投訴為垃圾內容" + inappropriate: "投訴為不當內容" + notify_moderators: "已通知的版主" + notify_user: "已送出一則訊息" by_you: off_topic: "你已投訴此文章偏離討論話題" spam: "你已投訴此文章為垃圾" @@ -1228,6 +1247,8 @@ zh_TW: title: "關注" tracking: title: "追蹤" + regular: + title: "一般" muted: title: "靜音" flagging: @@ -1696,6 +1717,7 @@ zh_TW: name: '愛' description: "按讚按鈕的顏色" email: + title: "電子郵件" settings: "設定" preview_digest: "預覽文摘" sending_test: "傳送測試郵件" @@ -1719,6 +1741,12 @@ zh_TW: last_seen_user: "最近出現的用戶:" reply_key: "回覆金鑰" skipped_reason: "跳過原因" + incoming_emails: + error: "錯誤" + modal: + error: "錯誤" + filters: + error_placeholder: "錯誤" logs: none: "找不到記錄。" filters: @@ -1780,6 +1808,12 @@ zh_TW: change_category_settings: "變更分類設定" delete_category: "刪除分類" create_category: "建立分類" + block_user: "封鎖用戶" + unblock_user: "解除封鎖" + grant_admin: "授予管理員權限" + revoke_admin: "撤銷管理員權限" + grant_moderation: "授予板主權限" + revoke_moderation: "撤銷板主權限" screened_emails: title: "過濾的電子郵件地址" description: "以下的電子郵件地址將無法用來建立新用戶。" diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index 860db973c6..355c0edc42 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -2057,6 +2057,12 @@ fr: ## [Changements à notre politique de confidentialité](#changes) Si nous décidons de changer notre politique de confidentialité, nous afficherons ces modifications sur cette page. Ce document est soumis à la licence creative commons CC-BY-SA. Il a été mis à jour le 31 mai 2013. + badges: + basic_user: + name: Actif + description: Accordé toutes les fonctions communautaires essentielles sont accessibles + long_description: | + Ce badge est accordé lorsque vous atteignez le niveau de confiance 1. Merci d'être resté dans le coin un petit moment et d'avoir lu quelques sujets pour en apprendre plus sur notre communauté. Vos restrictions "nouvel utilisateur" ont été levées, et vous avez accès aux fonctionnalités essentielles telles que la messagerie personnelle, le signalement, l'édition des wikis, et la possibilité de poster des images et de multiples liens. admin_login: success: "Courriel envoyé" error: "Erreur !" diff --git a/config/locales/server.ro.yml b/config/locales/server.ro.yml index 4b35e601d5..0337cb7d42 100644 --- a/config/locales/server.ro.yml +++ b/config/locales/server.ro.yml @@ -73,6 +73,7 @@ ro: not_found: "Adresa URL sau resursă cerută nu pot fi găsite." invalid_access: "Nu aveţi permisiunea să vedeţi această resursă." read_only_mode_enabled: "Site-ul este în modul doar-citire. Interacțiunile sunt dezactivate." + likes: "Aprecieri" too_many_replies: one: "Ne pare rău, dar utilizatori noi sunt limitaţi la 1 răspuns pe discuţie." few: "Ne pare rău, dar utilizatori noi sunt limitaţi la %{count} răspunsuri pe discuţie." diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index d05bb85312..3e2d34d4f7 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -419,7 +419,7 @@ zh_CN: confirmed: "你的电子邮箱已被更新。" please_continue: "转入到 %{site_name}" error: "在修改你的电子邮箱地址时出现了错误,可能此邮箱已经在论坛中使用了?" - error_staged: "在修改你的电子邮箱地址时出现了错误。这个邮箱已经被一个用户占用了。" + error_staged: "在修改你的电子邮箱地址时出现了错误。这个邮箱已经被一个暂存用户占用了。" already_done: "抱歉,此激活链接已经失效。可能你已经修改了邮箱?" authorizing_old: title: "感谢你确认你目前的邮箱地址" @@ -977,6 +977,7 @@ zh_CN: unsubscribe_via_email_footer: "在发出的邮件底部包含退订链接" delete_email_logs_after_days: "在(N)天后删除邮件日志。设置为 0 无限期保留" max_emails_per_day_per_user: "每日发送给用户的最大帖子数量。设置为 0 禁止限制" + enable_staged_users: "处理进站邮件时自动创建暂存用户。" manual_polling_enabled: "用 API 推送邮件回复。" pop3_polling_enabled: "轮询 POP3 收取邮件回复。" pop3_polling_ssl: "连接至 POP3 服务器时使用 SSL。(推荐)" @@ -1005,6 +1006,7 @@ zh_CN: digest_topics: "邮件摘要中显示的最大主题数目。" digest_min_excerpt_length: "在邮件摘要中每个帖子最少显示的字符数量。" delete_digest_email_after_days: "不发送摘要邮件给超过(n)天没访问的用户" + digest_suppress_categories: "不在摘要邮件中显示关注这个分类的任何内容。" disable_digest_emails: "为所有用户禁用摘要邮件。" detect_custom_avatars: "检测用户是否上传了自定义个人头像。" max_daily_gravatar_crawls: "一天内 Discourse 将自动检查 gravatar 自定义头像的次数" @@ -1122,6 +1124,11 @@ zh_CN: category: '分类' topic: '结果' user: '用户' + sso: + not_found: "无法找到你的账户。请联系站点管理人员。" + account_not_approved: "你的帐号尚未被审核通过。一旦你的帐号获得批准,你就会收到一封电子邮件提醒。" + unknown_error: "你的账户发生了问题。请联系站点管理人员。" + timeout_expired: "账户登录超市,请重新尝试登录。" original_poster: "原始作者" most_posts: "大部分帖子" most_recent_poster: "当前大部分帖子作者" @@ -1537,6 +1544,12 @@ zh_CN: 我们非常抱歉,你发送至 %{destination}(名为 %{former_title})的邮件出问题了。 你的账户没有足够的信任等级向该邮件地址发布新主题。如果你坚信这是错误,联系管理人员。 + email_reject_user_not_found: + subject_template: "[%{site_name}] 邮件问题 -- 用户未找到" + text_body_template: | + 我们非常抱歉,但是你发送至 %{destination}(名为%{former_title}) 的邮件出问题了。 + + 你发送回复的邮件地址是未知的邮件地址。试试从另外一个邮件地址发送,或者联系管理人员。 email_reject_inactive_user: subject_template: "[%{site_name}] 电子邮件错误 -- 未激活用户" text_body_template: | @@ -1733,12 +1746,53 @@ zh_CN: visit_link_to_respond: "访问[Visit Topic](%{base_url}%{url})以回复" visit_link_to_respond_pm: "访问[Visit Message](%{base_url}%{url})以回复" posted_by: "%{username}发表于%{post_date}" + invited_to_private_message_body: | + %{username} 邀请你至消息交流: + + > **%{topic_title}** + > + > %{topic_excerpt} + + 论坛: + + > %{site_title} -- %{site_description} + invited_to_topic_body: | + %{username} 邀请你参与讨论: + + > **%{topic_title}** + > + > %{topic_excerpt} + + 论坛: + + > %{site_title} -- %{site_description} user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} 邀请你加入消息交流:'%{topic_title}'" + text_body_template: | + %{header_instructions} + + %{message} + + --- + %{respond_instructions} user_invited_to_private_message_pm_staged: subject_template: "[%{site_name}] %{username} 邀请你加入消息交流:'%{topic_title}'" + text_body_template: | + %{header_instructions} + + %{message} + + --- + %{respond_instructions} user_invited_to_topic: subject_template: "[%{site_name}] %{username}邀请你参与“%{topic_title}”" + text_body_template: | + %{header_instructions} + + %{message} + + --- + %{respond_instructions} user_replied: subject_template: "[%{site_name}] %{topic_title}" text_body_template: | @@ -2215,6 +2269,205 @@ zh_CN: 文档以 CC-BY-SA 发布。最后更新时间为2013年5月31日。 static: search_help: "

    小技巧

    \n

    \n

      \n
    • 标题匹配优先 – 不知道搜什么时,搜索标题
    • \n
    • 搜索独一无二且不常见的词可以找到最好的结果
    • \n
    • 试试搜索特定的分类、用户或主题
    • \n
    \n

    \n

    选项

    \n

    \n\n\n\n<;td>badge:徽章名\n\n\n\n\n
    order:views(按照显示次数排序)order:latest(按照更新时间排序)order:likes(按照赞的次数排序)
    status:open(搜索正常状态的主题)status:closed(搜索已经关闭的主题)status:archived(搜索已经存档的主题)status:noreplies(搜索没有回复的主题)status:single_user(搜索单个用户的主题)
    category:分类名user:用户名<;code>group:群组名
    in:likes(在已经赞的帖子里搜索)in:posted(在发表的帖子里搜索)in:watching(在关注的帖子里搜索)in:tracking(在追踪的帖子里搜索)in:private(在私密的帖子里搜索)
    in:bookmarks(在加书签的帖子里搜索)in:first(在第一帖里搜索)
    posts_count:数字(帖子数量)before:天数或者日期after:天数或者日期
    \n

    \n

    例子

    \n

    \n

      \n
    • 彩虹 category:公园 status:open order:latest 将搜索在“公园”分类中没有关闭或存档中的名字包含“彩虹”的主题,并按最后一个帖子的日期来排序。
    • \n
    • 彩虹 category:\"公园和花园\" in:bookmarks<;/code> 将搜索在“公园和花园”分类中已被你加过书签的且名字包含“彩虹”的主题。
    • \n
    \n

    \n" + badges: + editor: + name: 编辑 + description: 首次编辑帖子 + long_description: | + 该徽章授予给第一次编辑自己帖子的你。虽然你不能永远在编辑自己的帖子,但是编辑他们总是一个好办法——你可以改进你的帖子、修正小错误,或者是增补你原来没有写的内容。编辑能让你的帖子质量变得更好! + basic_user: + name: 初级用户 + description: 授予所有常用社群功能 + long_description: | + 该徽章授予给用户等级达到 1 的成员。感谢你在社群里花了一些时间并且阅读了一些帖子,了解了我们的社群。你的新用户限制已经被取消;并且你已经被授予了所有基本的社群权限,必须个人消息、标记、维基编辑和发布多张图片和多个链接的能力。 + member: + name: 成员 + description: 授予 邀请、群组消息和更多的赞 + long_description: | + 该徽章授予给达到用户等级 2 的你。感谢你在社群待了几周,真正融入到了社群中。你现在可以在你的用户页或者主题中邀请他人或者创建群组消息了。每天你也可以点更多次赞了。 + regular: + name: 活跃用户 + description: 授予 重分类、重命名、跟踪链接、维基功能和更多的赞 + long_description: | + 该徽章授予给达到用户等级 3 的你。感谢你在这几个月持续地参与社群。你现在是我们之中最活跃的读者之一了,你持续的贡献极大地帮助了我们的社群。你现在可以社群待了几周,真正融入到了社群中。你现在可以重新分类或者重命名主题、使用能力强大的垃圾标记功能以及访问隐藏的贵宾分类了。而且每天你可以点更多次赞了。 + leader: + name: 资深 + description: 授予 全局编辑、固定、关闭、存档、分割、合并和更多的赞 + long_description: | + 该徽章授予给达到用户等级 4 的你。你被管理人员选作了社群的领导力量,你用你的行动和言辞为社群树立了正面的形象。你现在可以修改所有帖子,使用常见的版主的主题管理功能,比如置顶、关闭、隐藏、存档、分割和合并功能。你每天可以点更多的赞了。 + welcome: + name: 欢迎 + description: 得到一个赞 + long_description: | + 该徽章授予给帖子收到了第一个赞的成员。恭喜,有社群成员发现你发表的内容有意思、酷炫或者有用! + autobiographer: + name: 自传作者 + description: 已填写用户资料信息 + long_description: | + 该徽章授予给填写了用户页面并选择了用户头像的你。让社群成员们多了解你一点以及你感兴趣的内容,能帮助我们打造一个更好、更团结的社群。让我们一起努力! + anniversary: + name: 年度纪念日 + description: 一年活跃用户,至少发了一个帖子 + long_description: | + 该徽章授予给在社群注册一年并超至少发布了一个帖子的你。感谢你的持续关注和对我们社群的贡献!我们希望你继续参与我们的社群。 + nice_post: + name: 不错的回复 + description: 回复被赞了 10 次 + long_description: | + 该徽章授予给回复被赞了 10 次的你。你的回复给社群成员们留下了印象,并且你推动了讨论的进程! + good_post: + name: 很好的回复 + description: 回复被赞了 25 次 + long_description: | + 该徽章授予给回复被赞了 25 次的你。你的回复很杰出,这个讨论对每个人都变得更有意义了! + great_post: + name: 精彩的回复 + description: 回复被赞了 50 次 + long_description: | + 该徽章授予给回复被赞了 50 次的你。哇!你的回复很有启发、引经据典、令人冷俊不禁或者十分有内涵,整个社群都喜欢它。 + nice_topic: + name: 不错的主题 + description: 主题被赞了 10 次 + long_description: | + 该徽章授予给主题获得 10 个赞的你。嗨,你开启了一个社群成员觉得有意思的讨论! + good_topic: + name: 很好的主题 + description: 主题被赞了 25 次 + long_description: | + 该徽章授予给主题获得 25 个赞的你。你开启了一个有意义的主题,整个社群都在积极响应,而且大家喜欢你的主题! + great_topic: + name: 精彩的主题 + description: 主题被赞了 50 次 + long_description: | + 该徽章授予给主题获得 50 个赞的你。你开启了一个引人入胜的主题,整个社群都沉浸于讨论之中。 + nice_share: + name: 不错的分享 + description: 分享了一个有 25 个独立访问者的帖子 + long_description: | + 该徽章授予给分享链接给 25 个其他访客的你。感谢你广而告之我们的讨论,和对这个社群的帮助。 + good_share: + name: 很棒的分享 + description: 分享了一个有 300 个独立访问者的帖子 + long_description: | + 该徽章授予给分享链接给 300 个其他访客的你。干得漂亮!你把一个有意思的讨论介绍给了许多新的朋友,并且帮助社群前进了一步。 + great_share: + name: 精彩的分享 + description: 分享了一个有 1000 个独立访问者的帖子 + long_description: |+ + 该徽章授予给分享链接给 1000 个其他访客的你。哇!你把一个有意思的讨论推广给了广大的读者们,并且帮助社群前进了一大步! + + first_like: + name: 首个赞 + description: 已赞过了一个帖子 + long_description: | + 该徽章授予给第一次使用 :heart: 按钮赞了帖子的成员。给帖子点赞是一个极好的让社群成员知道他们的帖子有意思、有用、酷炫或者好玩的方法。分享爱! + first_flag: + name: 首个标记 + description: 已标记过了一个帖子 + long_description: | + 该徽章授予给第一次标记帖子的你。标记是帮助我们帮助社群保持健康、合宜的工具。如果你注意到哪个帖子需要版主的注意,不用在乎什么原因,直接标记。如果你注意到别人帖子里的某个问题,你也可用标记功能发送私信给该成员。如果你发现了问题,:flag_black: 标记吧! + promoter: + name: 推广者 + description: 已邀请了 1 个用户 + long_description: | + 该徽章授予给邀请他人加入社群的你。你曾使用了用户页面的邀请按钮或主题底部的邀请按钮。邀请对某个讨论感兴趣的朋友是引介新人的好办法,非常感谢! + campaigner: + name: 活动家 + description: 已邀请了 3 个初级用户 + long_description: | + 该徽章授予给邀请了 3 人加入社群的你,你的朋友在站点上度过了一段时间,已经成为了初级用户。一个活跃的社群需要不断有新鲜血液的参与,这样新观点才能推进讨论进程。 + champion: + name: 外交官 + description: 已邀请了 5 个成员 + long_description: | + 该徽章授予给邀请了 5 人加入社群的你,你的朋友在站点上度过了很长的时间,已经成为了成员。哇!感谢你帮助社群成长,社群因此变得更多元了! + first_share: + name: 首次分享 + description: 已分享了一个帖子 + long_description: | + 该徽章授予给第一次用分享按钮分享了回复或者主题的链接的成员。分享链接是一个极好的方式向外部世界展示有趣的讨论的方法,并且能推动社群增长。 + first_link: + name: 首个链接 + description: 已链接了另一个主题 + long_description: | + 该徽章授予给第一次链接到另一个主题的你。联结主题会在各个主题的右侧显示链接和标题,这可以帮助阅读者迅速地找到相关讨论。多链接一些主题吧! + first_quote: + name: 首次引用 + description: 已引用过一个帖子 + long_description: | + 该徽章授予给第一次在回复中引用帖子的你。在你的回复中引用之前帖子中的相关的段落可以让讨论更有主题和重点。而且这很简单:你只要选中别的帖子中的任何文字,然后点击在旁边的“引用回复”按钮。多引用一些吧! + read_guidelines: + name: 阅读指引 + description: 阅读社群指引 + long_description: | + 该徽章授予给阅读了社群指引的你。遵守和分享简单的指引能让我们打造一个稳定、有趣和可持续的社群。永远记住别人,一个和你有很多共同点的人,就在屏幕的那一侧。友好一些! + reader: + name: 读者 + description: 阅读了有至少 100 个回复的主题中的所有回复 + long_description: | + 该徽章授予给第一次阅读了超过 100 个回复的长主题的你。仔细地阅读对话内容能让你关注到讨论的中心,理解不同的观点,而且能帮助我们打造更有趣的对话情景。你读得越多,对话的效果就会越好。就如我们常说的那般,阅读是最重要的! :slight_smile: + popular_link: + name: 流行链接 + description: 分享的链接被点击了 50 次 + long_description: | + 该徽章授予给分享的链接被点击过 50 次的你。感谢你贴出了有用的链接,这让讨论的情节更形象了! + hot_link: + name: 热门链接 + description: 分享的链接被点击了 300 次 + long_description: | + 该徽章授予给分享的链接被点击过 300 次的你。感谢你贴出了精彩的链接,这推进了讨论的进程,并且装点了讨论! + famous_link: + name: 著名链接 + description: 分享的链接被点击了 1000 次 + long_description: | + 该徽章授予给分享的链接被点击过 1000 次的你。哇!感谢你贴出了推动讨论的重要链接,这让讨论更加具体,更有情节,并且传达了跟多的信息。干的好! + appreciated: + name: 感谢 + description: 有 20 个帖子都被赞了 1 次 + long_description: | + 该徽章授予给 20 个不同的帖子都收到了至少 1 个赞的你。社群感谢你对讨论的贡献! + respected: + name: 尊敬 + description: 有 100 个帖子都被赞了 2 次 + long_description: | + 该徽章授予给 100 个不同的帖子都收到了至少 2 个赞的你。社群越来越尊重你在讨论中的贡献了。 + admired: + name: 敬仰 + description: 有 300 个帖子都被赞了 5 次 + long_description: |+ + 该徽章授予给 300 个不同的帖子都收到了至少 5 个赞的你。哇!社群敬仰你在讨论中频繁和高质量的贡献。 + + out_of_love: + name: 热情 + description: 1 天内赞了 50 次 + long_description: | + 该徽章授予给一天内赞了 50 次的你。别忘记休息一下。赞你喜欢的帖子能鼓励成员不断改进讨论的内容。 + higher_love: + name: "激情\b" + description: 5 天都赞过 50 次 + long_description: | + 该徽章授予给 5 天赞过 50 次的你。感谢你每天都花点时间鼓励讨论的进行! + crazy_in_love: + name: 狂热 + description: 20 天都赞过 50 次 + long_description: |+ + 该徽章授予给 20 天赞过 50 次的你。哇!你为社群成员们树立了一个令人鼓舞的模范! + + thank_you: + name: 感谢你 + description: 有 20 个被赞的帖子,给出过 10 个赞 + long_description: | + 该徽章授予给收到了 20 个赞,且赞过别人 10 次的你。当别人感谢你的帖子时,你也感谢他们的帖子。 + gives_back: + name: 回馈 + description: 有 100 个被赞的帖子,给出过 100 个赞 + long_description: | + 该徽章授予给收到了 100 个赞,且赞过别人 100 次的你。感谢你回馈赞给社群! + empathetic: + name: 感性 + description: 有 500 个被赞的帖子,给出过 1000 个赞 + long_description: | + 该徽章授予给收到了 500 个赞,且赞过别人 1000 次的你。哇!你是一个富有同情心且会换位思考的模范。 :two_hearts: admin_login: success: "邮件已发送" error: "错误!" From bae836c7c5493cca1be4c3af39fa705610e56c96 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 28 Mar 2016 23:59:53 +0530 Subject: [PATCH 138/162] FIX: badge display name should be translated from server.en.yml file --- app/models/badge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/badge.rb b/app/models/badge.rb index e00692e591..620cca3622 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -379,7 +379,7 @@ SQL def display_name if self.system? - key = "admin_js.badges.badge.#{i18n_name}.name" + key = "badges.#{i18n_name}.name" I18n.t(key, default: self.name) else self.name From c53ef51c91b6d8495039508a479974e4daf2a163 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Mon, 28 Mar 2016 11:39:55 -0700 Subject: [PATCH 139/162] some badge image layout fixes --- app/assets/stylesheets/common/base/user-badges.scss | 11 ++++++++--- app/assets/stylesheets/mobile/topic-post.scss | 5 +++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss index 49eb08b767..1986cc5a45 100644 --- a/app/assets/stylesheets/common/base/user-badges.scss +++ b/app/assets/stylesheets/common/base/user-badges.scss @@ -158,16 +158,20 @@ background-color: dark-light-diff($primary, $secondary, 92%, -60%); font-size: 3em; + img { + max-width: 80px; + } + &.badge-type-gold .fa { - color: #ffd700 !important; + color: #ffd700; } &.badge-type-silver .fa { - color: #c0c0c0 !important; + color: #c0c0c0; } &.badge-type-bronze .fa { - color: #cd7f32 !important; + color: #cd7f32; } } @@ -187,6 +191,7 @@ .badge-card.medium { width: 350px; + vertical-align: top; } .badge-card.large { diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 94220ad0cb..7a946e328a 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -14,6 +14,11 @@ margin-top: 5px; } +.topic-post article { + border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + padding: 8px 0; +} + .post-stream { padding-bottom: 30px; } From a3076896215e85de634dff0fb01b3a6fdbb0b870 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Mon, 28 Mar 2016 12:04:18 -0700 Subject: [PATCH 140/162] slightly better avatar missing copy --- config/locales/server.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 0183f24ce9..0067f05574 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2367,7 +2367,7 @@ en: size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?" avatar: - missing: "Sorry, but the avatar you have selected is not present on the server. Can you try uploading it again?" + missing: "Sorry, we can't find any avatar associated with that email address. Can you try uploading it again?" flag_reason: sockpuppet: "A new user created a topic, and another new user at the same IP address replied. See the flag_sockpuppets site setting." From b2f402dc049c83717a9f154efb45efa187767bd3 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 28 Mar 2016 13:27:21 -0400 Subject: [PATCH 141/162] FIX: Incorrect badge counts when restricted to a username --- app/assets/javascripts/discourse/templates/badges/index.hbs | 2 +- app/assets/javascripts/discourse/templates/badges/show.hbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/badges/index.hbs b/app/assets/javascripts/discourse/templates/badges/index.hbs index f82722c2d6..0ad5dc88dd 100644 --- a/app/assets/javascripts/discourse/templates/badges/index.hbs +++ b/app/assets/javascripts/discourse/templates/badges/index.hbs @@ -9,7 +9,7 @@
    {{#each bg.badges as |b|}} - {{#link-to 'badges.show' b.id b.slug}} + {{#link-to 'badges.show' b.id b.slug (query-params username=null)}} {{badge-card badge=b}} {{/link-to}} {{/each}} diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs index 3e6630ed91..9a0bd1e059 100644 --- a/app/assets/javascripts/discourse/templates/badges/show.hbs +++ b/app/assets/javascripts/discourse/templates/badges/show.hbs @@ -6,7 +6,7 @@
    - {{badge-card badge=model size="large"}} + {{badge-card badge=model size="large" count=userBadges.grant_count}}
    From e364eb2aad298135d6025b43c96d423aa70474b0 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 07:53:03 +1100 Subject: [PATCH 142/162] correct coloring for user nav --- app/assets/stylesheets/common/base/user.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index 19af88da24..39926e061b 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -23,6 +23,15 @@ a { color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); } + + a:hover i { + color: $quaternary; + } + + a.active i { + color: $secondary; + } + i { color: dark-light-choose(scale-color($primary, $lightness: 55%), scale-color($secondary, $lightness: 55%)); } From c650c2a16ff8fc045228cb3a2719b5df813de2b5 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 07:58:45 +1100 Subject: [PATCH 143/162] FIX: regression in badge grant admin page --- app/assets/javascripts/admin/templates/user_badges.hbs | 2 +- config/routes.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/templates/user_badges.hbs b/app/assets/javascripts/admin/templates/user_badges.hbs index 281a37e08c..772360d21a 100644 --- a/app/assets/javascripts/admin/templates/user_badges.hbs +++ b/app/assets/javascripts/admin/templates/user_badges.hbs @@ -16,7 +16,7 @@
    - {{combo-box valueAttribute="id" value=controller.selectedBadgeId content=controller.grantableBadges nameProperty="displayName"}} + {{combo-box valueAttribute="id" value=controller.selectedBadgeId content=controller.grantableBadges nameProperty="name"}}
    {{#each bg.badges as |b|}} - {{#link-to 'badges.show' b.id b.slug (query-params username=null)}} - {{badge-card badge=b}} - {{/link-to}} + {{badge-card badge=b navigateOnClick="true"}} {{/each}}
    {{/each}} diff --git a/app/assets/javascripts/discourse/templates/user/badges.hbs b/app/assets/javascripts/discourse/templates/user/badges.hbs index 1c7c660be0..042b08fbfa 100644 --- a/app/assets/javascripts/discourse/templates/user/badges.hbs +++ b/app/assets/javascripts/discourse/templates/user/badges.hbs @@ -1,7 +1,5 @@
    {{#each controller as |ub|}} - {{#link-to 'badges.show' ub.badge.id ub.badge.slug (query-params username=user.username_lower)}} - {{badge-card badge=ub.badge count=ub.count}} - {{/link-to}} + {{badge-card badge=ub.badge count=ub.count navigateOnClick="true" username=user.username_lower}} {{/each}}
    diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss index 1986cc5a45..3add206d1a 100644 --- a/app/assets/stylesheets/common/base/user-badges.scss +++ b/app/assets/stylesheets/common/base/user-badges.scss @@ -246,3 +246,6 @@ background-color: $danger; } +.hyperlink { + cursor: pointer; +} From 418bc4a844820352f7ab94400c5e51285b000967 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 16:41:03 +1100 Subject: [PATCH 147/162] FIX: when ensuring consistency also delete orphan badges --- app/models/badge.rb | 7 +++++++ spec/models/badge_spec.rb | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/app/models/badge.rb b/app/models/badge.rb index 620cca3622..de1b821391 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -374,6 +374,13 @@ SQL end def self.ensure_consistency! + exec_sql < Date: Tue, 29 Mar 2016 16:56:29 +1100 Subject: [PATCH 148/162] FIX: double XHR request when loading more badges --- .../javascripts/discourse/controllers/badges/show.js.es6 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 index 714635f603..5df73481ac 100644 --- a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 +++ b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 @@ -21,6 +21,11 @@ export default Ember.Controller.extend({ actions: { loadMore() { + if (this.get('loadingMore')) { + return; + } + this.set('loadingMore', true); + const userBadges = this.get('userBadges'); UserBadge.findByBadgeId(this.get('model.id'), { @@ -31,6 +36,8 @@ export default Ember.Controller.extend({ if (userBadges.length === 0){ this.set('noMoreBadges', true); } + }).finally(()=>{ + this.set('loadingMore', false); }); } }, From 2af4e4152e97ef1af2343fd3119c9d118373c216 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 29 Mar 2016 13:50:21 +0800 Subject: [PATCH 149/162] Update Logster. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index b940a9e745..757d462321 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -150,7 +150,7 @@ GEM thor (~> 0.15) libv8 (3.16.14.13) listen (0.7.3) - logster (1.2.1) + logster (1.2.2) loofah (2.0.3) nokogiri (>= 1.5.9) lru_redux (1.1.0) From 24630f6b54325dfec7017f336c399d9faa27792b Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 17:01:36 +1100 Subject: [PATCH 150/162] update puma dependency --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 757d462321..af80fc857f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -233,7 +233,7 @@ GEM pry (>= 0.9.10, < 0.11.0) pry-rails (0.3.4) pry (>= 0.9.10) - puma (2.15.3) + puma (3.2.0) r2 (0.2.6) rack (1.6.4) rack-mini-profiler (0.9.9.2) From adbc22530cbc25395cb4e16ad12913ee3bacbbaa Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 17:11:47 +1100 Subject: [PATCH 151/162] FIX: don't show badges in summary page if badges disabled --- app/assets/javascripts/discourse/models/user.js.es6 | 12 +++++++----- app/serializers/user_summary_serializer.rb | 4 ++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index e1199a190d..2853625ab4 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -410,11 +410,13 @@ const User = RestModel.extend({ summary.topics = summary.topic_ids.map(id => topicMap[id]); - summary.badges = summary.badges.map(ub => { - const badge = badgeMap[ub.badge_id]; - badge.count = ub.count; - return badge; - }); + if (summary.badges) { + summary.badges = summary.badges.map(ub => { + const badge = badgeMap[ub.badge_id]; + badge.count = ub.count; + return badge; + }); + } return summary; }); } diff --git a/app/serializers/user_summary_serializer.rb b/app/serializers/user_summary_serializer.rb index 4be285f4a0..1c043d4b6f 100644 --- a/app/serializers/user_summary_serializer.rb +++ b/app/serializers/user_summary_serializer.rb @@ -15,4 +15,8 @@ class UserSummarySerializer < ApplicationSerializer attributes :likes_given, :likes_received, :posts_read_count, :days_visited, :topic_count, :post_count + + def include_badges? + SiteSetting.enable_badges + end end From ed750cac39a3bb7e94a6f0f0215e52bb994024f3 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 17:21:32 +1100 Subject: [PATCH 152/162] FIX: if badges are disabled badge pages should 404 --- app/controllers/badges_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/controllers/badges_controller.rb b/app/controllers/badges_controller.rb index 728a3dbf69..4b15a7796e 100644 --- a/app/controllers/badges_controller.rb +++ b/app/controllers/badges_controller.rb @@ -2,6 +2,8 @@ class BadgesController < ApplicationController skip_before_filter :check_xhr, only: [:index, :show] def index + raise Discourse::NotFound unless SiteSetting.enable_badges + badges = Badge.all if (params[:only_listable] == "true") || !request.xhr? @@ -28,6 +30,8 @@ class BadgesController < ApplicationController end def show + raise Discourse::NotFound unless SiteSetting.enable_badges + params.require(:id) badge = Badge.enabled.find(params[:id]) From 95076050f47a45945c9f1faeab755f69561d5643 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 18:50:17 +1100 Subject: [PATCH 153/162] FEATURE: warn about mailing list mode if it is checked --- .../components/preference-checkbox.js.es6 | 14 +++++++++++++- .../discourse/controllers/preferences.js.es6 | 16 ++++++++++++++++ .../discourse/templates/user/preferences.hbs | 2 +- app/models/post.rb | 10 ++++++++++ app/serializers/user_serializer.rb | 8 +++++++- config/locales/client.en.yml | 4 ++++ 6 files changed, 51 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 b/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 index 98dc570796..0ec786b534 100644 --- a/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 +++ b/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 @@ -3,5 +3,17 @@ export default Em.Component.extend({ label: function() { return I18n.t(this.get('labelKey')); - }.property('labelKey') + }.property('labelKey'), + + click() { + const warning = this.get('warning'); + + if (warning && !this.get('checked')) { + debugger; + this.sendAction('warning'); + return false; + } + + return true; + } }); diff --git a/app/assets/javascripts/discourse/controllers/preferences.js.es6 b/app/assets/javascripts/discourse/controllers/preferences.js.es6 index 005ff085a8..b237017f26 100644 --- a/app/assets/javascripts/discourse/controllers/preferences.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences.js.es6 @@ -106,6 +106,22 @@ export default Ember.Controller.extend(CanCheckEmails, { actions: { + checkMailingList(){ + Em.run.next(()=>{ + const postsPerDay = this.get('model.mailing_list_posts_per_day'); + if (!postsPerDay || postsPerDay < 2) { + this.set('model.user_option.mailing_list_mode', true); + return; + } + + bootbox.confirm(I18n.t("user.enable_mailing_list", {count: postsPerDay}), I18n.t("no_value"), I18n.t("yes_value"), (success) => { + if (success) { + this.set('model.user_option.mailing_list_mode', true); + } + }); + }); + }, + save() { this.set('saved', false); diff --git a/app/assets/javascripts/discourse/templates/user/preferences.hbs b/app/assets/javascripts/discourse/templates/user/preferences.hbs index 6591c9eb82..ea5e4b7e1b 100644 --- a/app/assets/javascripts/discourse/templates/user/preferences.hbs +++ b/app/assets/javascripts/discourse/templates/user/preferences.hbs @@ -186,7 +186,7 @@ {{preference-checkbox labelKey="user.email_private_messages" checked=model.user_option.email_private_messages}} {{preference-checkbox labelKey="user.email_direct" checked=model.user_option.email_direct}} {{#unless siteSettings.disable_mailing_list_mode}} - {{preference-checkbox labelKey="user.mailing_list_mode" checked=model.user_option.mailing_list_mode}} + {{preference-checkbox warning="checkMailingList" labelKey="user.mailing_list_mode" checked=model.user_option.mailing_list_mode}} {{/unless}} {{preference-checkbox labelKey="user.email_always" checked=model.user_option.email_always}} {{#unless model.user_option.email_always}} diff --git a/app/models/post.rb b/app/models/post.rb index 8a63e8d76f..31a60e760a 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -451,6 +451,16 @@ class Post < ActiveRecord::Base PostCreator.before_create_tasks(self) end + def self.estimate_posts_per_day + val = $redis.get("estimated_posts_per_day") + return val.to_i if val + + posts_per_day = Topic.listable_topics.secured.joins(:posts).merge(Post.created_since(30.days.ago)).count / 30 + $redis.setex("estimated_posts_per_day", 1.day.to_i, posts_per_day.to_s) + posts_per_day + + end + # This calculates the geometric mean of the post timings and stores it along with # each post. def self.calculate_avg_time(min_topic_age=nil) diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index b1acb42fe8..143279ba80 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -95,7 +95,8 @@ class UserSerializer < BasicUserSerializer :has_title_badges, :card_image_badge, :card_image_badge_id, - :muted_usernames + :muted_usernames, + :mailing_list_posts_per_day untrusted_attributes :bio_raw, :bio_cooked, @@ -109,6 +110,11 @@ class UserSerializer < BasicUserSerializer ### ATTRIBUTES ### + def mailing_list_posts_per_day + val = Post.estimate_posts_per_day + [val,SiteSetting.max_emails_per_day_per_user].min + end + def groups if scope.is_admin? || object.id == scope.user.try(:id) object.groups diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 704e4e3fb4..2fd92789a6 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -661,6 +661,10 @@ en: other_settings: "Other" categories_settings: "Categories" + enable_mailing_list: + one: "Are you sure you want to be emailed for every new post?" + other: "Are you sure you want to be emailed for every new post?

    This will result in approximately {{count}} emails per day." + new_topic_duration: label: "Consider topics new when" not_viewed: "I haven't viewed them yet" From f907b42f0a548ab7416deed50b5de96b379d5887 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 19:02:59 +1100 Subject: [PATCH 154/162] oops --- .../javascripts/discourse/components/preference-checkbox.js.es6 | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 b/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 index 0ec786b534..6b76240951 100644 --- a/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 +++ b/app/assets/javascripts/discourse/components/preference-checkbox.js.es6 @@ -9,7 +9,6 @@ export default Em.Component.extend({ const warning = this.get('warning'); if (warning && !this.get('checked')) { - debugger; this.sendAction('warning'); return false; } From b40efb98b8c32c6ff6b8949912aa954c38e81e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 29 Mar 2016 12:12:33 +0200 Subject: [PATCH 155/162] FIX: remove deleted wiki color from existing color schemes --- db/migrate/20160329101122_remove_wiki_color.rb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 db/migrate/20160329101122_remove_wiki_color.rb diff --git a/db/migrate/20160329101122_remove_wiki_color.rb b/db/migrate/20160329101122_remove_wiki_color.rb new file mode 100644 index 0000000000..4b6007ceae --- /dev/null +++ b/db/migrate/20160329101122_remove_wiki_color.rb @@ -0,0 +1,8 @@ +class RemoveWikiColor < ActiveRecord::Migration + def up + execute "DELETE FROM color_scheme_colors WHERE name = 'wiki'" + end + + def down + end +end From 480e8d0dc7c5627b36ad3151719c67780fa3c5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 29 Mar 2016 12:35:50 +0200 Subject: [PATCH 156/162] update 'email_reply_trimmer' to latest version --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 8d2f214737..74c6d6a99f 100644 --- a/Gemfile +++ b/Gemfile @@ -66,7 +66,7 @@ gem 'aws-sdk', require: false gem 'excon', require: false gem 'unf', require: false -gem 'email_reply_trimmer', '0.1.1' +gem 'email_reply_trimmer', '0.1.2' # note: for image_optim to correctly work you need to follow # https://github.com/toy/image_optim diff --git a/Gemfile.lock b/Gemfile.lock index af80fc857f..d81d4d6de3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,7 +76,7 @@ GEM docile (1.1.5) domain_name (0.5.25) unf (>= 0.0.5, < 1.0.0) - email_reply_trimmer (0.1.1) + email_reply_trimmer (0.1.2) ember-data-source (1.0.0.beta.16.1) ember-source (~> 1.8) ember-handlebars-template (0.1.5) @@ -415,7 +415,7 @@ DEPENDENCIES byebug certified discourse-qunit-rails - email_reply_trimmer (= 0.1.1) + email_reply_trimmer (= 0.1.2) ember-rails ember-source (= 1.12.2) excon From aae835a42fed5f873356a068ca72b07eceb29c1a Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 29 Mar 2016 23:54:18 +1100 Subject: [PATCH 157/162] FIX: all badges got revoked due to ensure consistency query --- app/models/badge.rb | 2 +- spec/models/badge_spec.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/badge.rb b/app/models/badge.rb index de1b821391..38011348a6 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -378,7 +378,7 @@ SQL DELETE FROM user_badges USING user_badges ub LEFT JOIN users u ON u.id = ub.user_id - WHERE u.id IS NULL + WHERE u.id IS NULL AND user_badges.id = ub.id SQL Badge.find_each(&:reset_grant_count!) diff --git a/spec/models/badge_spec.rb b/spec/models/badge_spec.rb index 59e997b66b..3ee6bf8ce0 100644 --- a/spec/models/badge_spec.rb +++ b/spec/models/badge_spec.rb @@ -49,11 +49,12 @@ describe Badge do b.save UserBadge.create!(user_id: -100, badge_id: b.id, granted_at: 1.minute.ago, granted_by_id: -1) + UserBadge.create!(user_id: User.first.id, badge_id: b.id, granted_at: 1.minute.ago, granted_by_id: -1) Badge.ensure_consistency! b.reload - expect(b.grant_count).to eq(0) + expect(b.grant_count).to eq(1) end end From e763337f872630bc693c3622723851bbdcdb01b1 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 29 Mar 2016 19:05:08 +0530 Subject: [PATCH 158/162] update onebox gem --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index d81d4d6de3..d21aedc8e8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -214,7 +214,7 @@ GEM omniauth-twitter (1.2.1) json (~> 1.3) omniauth-oauth (~> 1.1) - onebox (1.5.36) + onebox (1.5.37) htmlentities (~> 4.3.4) moneta (~> 0.8) multi_json (~> 1.11) From aec88dc44330082d04d98b598c8bcf914be06b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 29 Mar 2016 15:46:41 +0200 Subject: [PATCH 159/162] FIX: like badges should respect bronze/silver/gold colors --- app/assets/stylesheets/common/base/user-badges.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss index 3add206d1a..56cfc6b791 100644 --- a/app/assets/stylesheets/common/base/user-badges.scss +++ b/app/assets/stylesheets/common/base/user-badges.scss @@ -163,15 +163,15 @@ } &.badge-type-gold .fa { - color: #ffd700; + color: #ffd700 !important; } &.badge-type-silver .fa { - color: #c0c0c0; + color: #c0c0c0 !important; } &.badge-type-bronze .fa { - color: #cd7f32; + color: #cd7f32 !important; } } From b3354bdc7bd73672096f7ede656010abfbd23985 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 29 Mar 2016 19:27:11 +0530 Subject: [PATCH 160/162] Update Translations --- config/locales/client.de.yml | 16 +-- config/locales/client.es.yml | 13 ++ config/locales/client.fi.yml | 7 + config/locales/client.pl_PL.yml | 2 + config/locales/server.es.yml | 28 ++++ config/locales/server.fi.yml | 218 +++++++++++++++++++++++++++++++- 6 files changed, 275 insertions(+), 9 deletions(-) diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index b3aa7df027..d05ad339af 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -593,8 +593,8 @@ de: every_three_days: "alle drei Tage" weekly: "wöchentlich" every_two_weeks: "jede zweite Woche" - include_tl0_in_digests: "Füge Beiträge von neuen Benutzern in Zusammenfassungs-E-Mails an" - email_in_reply_to: "füge " + include_tl0_in_digests: "Beiträge von neuen Benutzern in E-Mail mit Neuigkeiten einfügen" + email_in_reply_to: "Einen Auszug aus dem beantworteten Beitrag in E-Mails einfügen." email_direct: "Sende mir eine E-Mail, wenn mich jemand zitiert, auf meine Beiträge antwortet, meinen @Namen erwähnt oder mich zu einem Thema einlädt." email_private_messages: "Sende mir eine E-Mail, wenn mir jemand eine Nachricht sendet." email_always: "Benachrichtige mich per E-Mail auch während ich auf dieser Website aktiv bin" @@ -951,7 +951,7 @@ de: moved_post: "Dein Beitrag wurde verschoben von" linked: "Link zu deinem Beitrag" granted_badge: "Abzeichen erhalten" - group_message_summary: "Nachrichten in dem Gruppenpostfach" + group_message_summary: "Nachrichten im Gruppenpostfach" popup: mentioned: '{{username}} hat dich in "{{topic}}" - {{site_title}} erwähnt' group_mentioned: '{{username}} hat dich in "{{topic}}" - {{site_title}} erwähnt' @@ -996,7 +996,7 @@ de: category: "Kategorie „{{category}}“ durchsuchen" topic: "Dieses Thema durchsuchen" private_messages: "Nachrichten durchsuchen" - hamburger_menu: "wechsel zu einem anderen Beitragsliste oder Kategorie" + hamburger_menu: "zu einer anderen Themenliste oder Kategorie wechseln" new_item: "neu" go_back: 'zurückgehen' not_logged_in_user: 'Benutzerseite mit einer Zusammenfassung der Benutzeraktivitäten und Einstellungen' @@ -1850,9 +1850,9 @@ de: add: "Hinzufügen" add_members: "Mitglieder hinzufügen" custom: "Benutzerdefiniert" - bulk_complete: "Der Benutzer wurde der Gruppe hinzugefügt." + bulk_complete: "Die Benutzer wurden der Gruppe hinzugefügt." bulk: "Mehrere der Gruppe hinzufügen" - bulk_paste: "Füge eine Liste an Benutzernamen oder E-Mail-Adressen ein, jeweils pro Zeile:" + bulk_paste: "Füge eine Liste an Benutzernamen oder E-Mail-Adressen ein (ein Eintrag je Zeile):" bulk_select: "(wähle eine Gruppe aus)" automatic: "Automatisch" automatic_membership_email_domains: "Benutzer, deren E-Mail-Domain mit einem der folgenden Listeneinträge genau übereinstimmt, werden automatisch zu dieser Gruppe hinzugefügt:" @@ -2062,7 +2062,7 @@ de: send_test: "Test-E-Mail senden" sent_test: "Gesendet!" delivery_method: "Versandmethode" - preview_digest_desc: "Vorschau der Zusammenfassung der letzten Aktivitäten, die als E-Mail an inaktive Nutzer gesendet wird." + preview_digest_desc: "Vorschau der Neuigkeiten, die als E-Mail an inaktive Nutzer gesendet werden." refresh: "Aktualisieren" format: "Format" html: "HTML" @@ -2330,7 +2330,7 @@ de: unblock_failed: 'Beim Aufheben der Blockierung des Benutzers ist ein Fehler aufgetreten.' block_failed: 'Beim Blocken des Benutzers ist ein Fehler aufgetreten.' block_confirm: 'Bust du sicher, dass du diesen Benutzer blockieren willst? Sie werden keine Möglichkeit mehr haben, Themen oder Beiträge zu erstellen.' - block_accept: 'Ja, den Nutzer blockieren.' + block_accept: 'Ja, diesen Benutzer blockieren.' deactivate_explanation: "Ein deaktivierter Benutzer muss seine E-Mail-Adresse erneut bestätigen." suspended_explanation: "Ein gesperrter Benutzer kann sich nicht anmelden." block_explanation: "Ein geblockter Benutzer kann keine Themen erstellen oder Beiträge veröffentlichen." diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index 11fdd54b75..963f84cabd 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -593,6 +593,7 @@ es: every_three_days: "cada tres días" weekly: "semanalmente" every_two_weeks: "cada dos semanas" + include_tl0_in_digests: "Incluir posts de nuevos usuarios en los resúmenes por email" email_in_reply_to: "Incluir un extracto del post al que se responde en los emails" email_direct: "Envíame un email cuando alguien me cite, responda a mis posts, mencione mi @usuario o me invite a un tema" email_private_messages: "Notifícame por email cuando alguien me envíe un mensaje" @@ -724,6 +725,12 @@ es: too_few_topics_and_posts_notice: "¡Vamos a dar por comenzada la comunidad! Hay %{currentTopics} / %{requiredTopics} temas y %{currentPosts} / %{requiredPosts} mensajes. Los nuevos visitantes necesitan algo que leer y a lo que responder." too_few_topics_notice: "¡Vamos a dar por comenzada la comunidad! Hay %{currentTopics} / %{requiredTopics} temas. Los nuevos visitantes necesitan algo que leer y a lo que responder." too_few_posts_notice: "¡Vamos a dar por empezada la comunidad! Hay %{currentPosts} / %{requiredPosts} mensajes. Los nuevos visitantes necesitan algo que leer y a lo que responder." + logs_error_rate_notice: + reached: "%{timestamp}: La tasa actual del %{rate} ha alcanzado el límite establecido en las opciones del sitio en el %{siteSettingRate}." + exceeded: "%{timestamp}: La tasa actual del %{rate} ha excedido el límite establecido en las opciones del sitio en el %{siteSettingRate}." + rate: + one: "1 error/%{duration}" + other: "%{count} errores/%{duration}" learn_more: "saber más..." year: 'año' year_desc: 'temas creados en los últimos 365 días' @@ -1357,6 +1364,7 @@ es: about: "este post es tipo wiki" archetypes: save: 'Guardar opciones' + few_likes_left: "¡Gracias por compartir tu afecto! Te quedan solo unos pocos me gusta para hoy." controls: reply: "componer una respuesta para este post" like: "me gusta este post" @@ -2480,6 +2488,7 @@ es: badge: Distintivo display_name: Nombre a mostrar description: Descripción + long_description: Descripción completa badge_type: Tipo de distintivo badge_grouping: Grupo badge_groupings: @@ -2642,7 +2651,11 @@ es: earned_n_times: one: "Ganó este distintivo 1 vez" other: "Ganó este distintivo %{count} veces" + granted_on: "Concedido el %{date}" + others_count: "Otras personas con este distintivo (%{count})" title: Distintivos + allow_title: "título disponible" + multiple_grant: "puede ser concedido varias veces" badge_count: one: "1 distintivo" other: "%{count} distintivos" diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index 2df45f8aee..8a5c0ca68e 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -593,6 +593,7 @@ fi: every_three_days: "joka kolmas päivä" weekly: "viikottain" every_two_weeks: "joka toinen viikko" + include_tl0_in_digests: "Sisällytä uusien käyttäjien viestit sähköpostitiivistelmiin oletuksena." email_in_reply_to: "Liitä sähköpostiin lyhennelmä viestistä, johon vastataan" email_direct: "Lähetä minulle sähköposti, jos joku lainaa viestiäni, vastaa viestiini, viittaa @nimeeni, tai kutsuu minut viestiketjuun" email_private_messages: "Lähetä minulle sähköposti, kun joku lähettää minulle viestin" @@ -1357,6 +1358,7 @@ fi: about: "tämä viesti on wiki" archetypes: save: 'Tallennusasetukset' + few_likes_left: "Kiitos hyvän mielen levittämisestä! Sinulla on enää muutama tykkäys jäljellä tälle päivälle." controls: reply: "aloita vastaamaan tähän viestiin" like: "tykkää viestistä" @@ -2480,6 +2482,7 @@ fi: badge: Arvomerkki display_name: Nimi description: Kuvaus + long_description: Pitkä kuvaus badge_type: Arvomerkin tyyppi badge_grouping: Ryhmä badge_groupings: @@ -2642,7 +2645,11 @@ fi: earned_n_times: one: "Ansaitsi tämän arvomerkin yhden kerran" other: "Ansaitsi tämän arvomerkin %{count} kertaa" + granted_on: "Myönnetty %{date}" + others_count: "Muita, joilla on tämä arvomerkki (%{count})" title: Arvomerkit + allow_title: "sallittu titteli" + multiple_grant: "myönnettävissä useaan kertaan" badge_count: one: "1 Arvomerkki" other: "%{count} Arvomerkkiä" diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index 509f42081d..60481f8a5d 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -1443,6 +1443,8 @@ pl_PL: notify_moderators: "Powiadomiono moderatorów" notify_user: "Wysłano wiadomość" bookmark: "dodaj do zakładek" + like: "polubiło to" + vote: "zagłosowało" by_you: off_topic: "Oznaczono jako nie-na-temat" spam: "Oflagowano jako spam" diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index 6ae83d908e..b2a17b830d 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -169,6 +169,9 @@ es: hot: "Temas candentes" top: "Top temas" posts: "Últimos posts" + private_posts: "Últimos mensajes privados" + group_posts: "Últimos posts de %{group_name}" + group_mentions: "Últimas menciones de %{group_name}" too_late_to_edit: "Ese post fue publicado hace demasiado tiempo. No puede ser editado ni eliminado." revert_version_same: "La versión actual es la misma que la versión a la que intentas volver." excerpt_image: "imagen" @@ -343,11 +346,15 @@ es: first_day_topics_per_day: "Has llegado al límite de temas que un nuevo usuario puede crear en su primer día. Por favor, espera %{time_left} antes de intentarlo de nuevo." create_topic: "Estás creando temas demasiado rápido. Por favor, espera %{time_left} antes de intentarlo de nuevo." create_post: "Estás respondiendo demasiado rápido. Por favor, espera %{time_left} antes de intentarlo de nuevo." + delete_post: "Estás eliminando posts demasiado rápido. Por favor espera %{time_left} antes de intentarlo de nuevo." topics_per_day: "Has llegado al límite de nuevos temas de hoy. Por favor, espera %{time_left} antes de intentarlo de nuevo." pms_per_day: "Has llegado al límite de mensajes de hoy. Por favor, espera %{time_left} antes de intentarlo de nuevo." create_like: "Has llegado al límite de Me gusta de hoy. Por favor, espera %{time_left} antes de intentarlo de nuevo." create_bookmark: "Has llegado al límite de marcadores de hoy. Por favor, espera %{time_left} antes de intentarlo de nuevo." edit_post: "Has llegado al límite de ediciones de hoy. Por favor, espera %{time_left} antes de intentarlo de nuevo." + live_post_counts: "Estás solicitando actualizaciones de posts demasiado rápido. Por favor, espera %{time_left} antes de intentarlo de nuevo." + unsubscribe_via_email: "Has alcanzado el máximo de bajas de suscripción vía email por hoy. Por favor espera %{time_left} antes de intentarlo de nuevo." + topic_invitations_per_day: "Has alcanzado el máximo de invitaciones a temas por hoy. Por favor, espera %{time_left} antes de intentarlo de nuevo." hours: one: "1 hora" other: "%{count} horas" @@ -442,6 +449,11 @@ es: confirmed: "Tu email ha sido actualizado." please_continue: "Continuar a %{site_name}" error: "Hubo un problema cambiando tu dirección de email. ¿Quizás la dirección ya está en uso?" + error_staged: "Hubo un error al cambiar tu email. La dirección ya está en uso por un usuario provisional." + already_done: "Lo sentimos, este enlace de confirmación ya no es válido. ¿Quizá tu email ya fue cambiado?" + authorizing_old: + title: "Gracias por confirmar tu dirección de correo actual" + description: "Te enviaremos un email a tu nueva dirección para confirmar." activation: action: "Haz clic aquí para activar tu cuenta" already_done: "Lo sentimos, este link de confirmación de cuenta ya no es válido. ¿Quizás tu cuenta ya está activa?" @@ -692,11 +704,15 @@ es: site_contact_username_warning: "Introduce el nombre de un administrador o moderador simpático desde el cual se enviarán mensajes automáticos importantes. Actualiza site_contact_username en Ajustes del sitio." notification_email_warning: "Las notificaciones por email no están siendo enviadas desde una dirección de correo electrónico válida en tu dominio; la entrega de emails será errática y poco fiable. Por favor, establece para notification_email una dirección de correo electrónico local válida en Ajustes del sitio." subfolder_ends_in_slash: "La configuación del subdirectorio no es correcta; el campo DISCOURSE_RELATIVE_URL_ROOT termina con una barra." + email_polling_errored_recently: + one: "El email polling ha generado un error en las pasadas 24 horas. Mira en los logs para más detalles." + other: "El email polling ha generado %{count} errores en las pasadas 24 horas. Mira en los logs para más detalles." site_settings: censored_words: "Las palabras serán reemplazadas con ■■■■" delete_old_hidden_posts: "Auto-borrar cualquier post que se quede oculto por mas de 30 días." default_locale: "El idioma por defecto de Discourse (ISO 639-1 Code)" allow_user_locale: "Permitir que los usuarios escojan su propio idioma para la interfaz" + set_locale_from_accept_language_header: "Establece el lenguaje de la interfaz para usuarios anónimos desde el lenguaje declarado por su navegador web. (EXPERIMENTAL, no funciona con caché anónimo)" min_post_length: "Extensión mínima de los posts, en número de caracteres" min_first_post_length: "Extensión mínima permitida en el primer mensaje (cuerpo del tema) en caracteres" min_private_message_post_length: "Extensión mínima de los posts en los mensajes, en número de caracteres" @@ -818,6 +834,7 @@ es: max_username_length: "Longitud máxima del nombre de usuario en caracteres." reserved_usernames: "Nombres de usuario no permitidos en el registro." min_password_length: "Longitud mínima de contraseña." + min_admin_password_length: "Longitud mínima de la contraseña para un Admin." block_common_passwords: "No permitir contraseñas que están entre las 10.000 más comunes." enable_sso: "Activar single sign on a través de un sitio externo (AVISO: ¡LAS DIRECCIONES DE EMAIL SERÁN VALIDADAS POR EL SITIO EXTERNO!)" enable_sso_provider: "Implementar el protocolo del proveedor de SSO de Discourse en el endpoint /session/sso_provider requiere establecer un sso_secret" @@ -838,6 +855,9 @@ es: enable_twitter_logins: "Activar autenticación por Twitter, requiere una twitter_consumer_key y un twitter_consumer_secret" twitter_consumer_key: "Comsumer key para autenticación por Twitter, registrado en http://dev.twitter.com" twitter_consumer_secret: "Comsumer secret para autenticación por Twitter, registrado en http://dev.twitter.com" + enable_instagram_logins: "Activar la autenticación por Instagram, requiere instagram_consumer_key y instagram_consumer_secret" + instagram_consumer_key: "Consumer key para autenticación por Instagram" + instagram_consumer_secret: "Consumer secret para autenticación por Instagram" enable_facebook_logins: "Activar autenticación por Facebook, requiere una facebook_app_id y un facebook_app_secret" facebook_app_id: "App id para la autenticación por Facebook, registrado en https://developers.facebook.com/apps" facebook_app_secret: "App secret para autenticación por Facebook, registrado en https://developers.facebook.com/apps" @@ -850,11 +870,15 @@ es: backup_frequency: "Con qué frecuencia, en días, crearemos un backup del sitio." enable_s3_backups: "Sube copias de seguridad a S3 cuando complete. IMPORTANTE: requiere credenciales validas de S3 puestas Archivos configuración." s3_backup_bucket: "El bucket remoto para mantener copias de seguridad. AVISO: Asegúrate de que es un bucket privado." + s3_disable_cleanup: "Desactivar el eliminado de backups de S3 cuando se eliminen de forma local." backup_time_of_day: "Hora UTC del día cuando debería ejecutarse el backup." backup_with_uploads: "Incluir archivos adjuntos en los backups programados. Desactivando esta opción tan solo se ejecutará una copia de seguridad de la base de datos." active_user_rate_limit_secs: "Con qué frecuencia actualizaremos el campo 'last_seen_at', en segundos" verbose_localization: "Mostrar sugerencias de localización extendida en la interface de usuario " previous_visit_timeout_hours: "Cuanto tiempo debe pasar antes de que una visita sea considerada la 'visita previa', en horas" + top_topics_formula_log_views_multiplier: "valor del multiplicador de visitas (n) en la fórmula de temas top: `log(views_count) * (n) + op_likes_count * 0.5 + LEAST(likes_count / posts_count, 3) + 10 + log(posts_count)`" + top_topics_formula_first_post_likes_multiplier: "valor del multiplicador de me gusta en el primer post (n) en la fórmula de temas top:: `log(views_count) * 2 + op_likes_count * (n) + LEAST(likes_count / posts_count, 3) + 10 + log(posts_count)`" + top_topics_formula_least_likes_per_post_multiplier: "valor del multiplicador de me gusta por post (n) en la fórmula de temas top: `log(views_count) * 2 + op_likes_count * 0.5 + LEAST(likes_count / posts_count, (n)) + 10 + log(posts_count)`" rate_limit_create_topic: "Después de crear un tema, los usuarios deben esperar (n) segundos antes de crear otro tema." rate_limit_create_post: "Después de publicar un post, los usuarios deben esperar (n) segundos antes de crear otro post." rate_limit_new_user_create_topic: "Después de crear un tema, los nuevos usuarios deben esperar (n) segundos antes de crear otro tema." @@ -867,6 +891,8 @@ es: max_private_messages_per_day: "Máximo número de mensajes por usuario y día." max_invites_per_day: "Máximo número de invitaciones que un usuario puede enviar al día." max_topic_invitations_per_day: "Máximo número de invitaciones a un tema que un usuario puede enviar por día." + alert_admins_if_errors_per_minute: "Número de errores por minuto que activa la alerta a administración. Un valor de 0 desactiva esta funcionalidad. NOTA: requiere reiniciar la instancia." + alert_admins_if_errors_per_hour: "Número de errores por hora que activa la alerta a administración. Un valor de 0 desactiva esta funcionalidad. NOTA: requiere reiniciar la instancia." suggested_topics: "Número de temas sugeridos mostrados al pie del tema." limit_suggested_to_category: "Solo mostrar temas de la categoría actual en los temas sugeridos." clean_up_uploads: "Eliminar subidas huérfanas sin referencia para prevenir hosting ilegal. AVISO: antes de habilitar esta opción quizá quieres hacer un backup de tu directorio de /uploads" @@ -898,6 +924,7 @@ es: tl2_requires_likes_received: "¿Cuántos 'me gusta' un usuario debe de recibir antes de promoverlo a nivel de confianza 2?" tl2_requires_likes_given: "¿Cuántos 'me gusta' un usuario debe de dar antes de promoverlo a nivel de confianza 2?" tl2_requires_topic_reply_count: "¿Cuántos temas un usuario debe de contestar antes de promoverlo a nivel de confianza 2?" + tl3_time_period: "Período de tiempo (en días) para los requisitos de nivel de confianza 3" tl3_requires_topics_viewed_all_time: "El número total mínimo de temas que un usuario debió de haber visto para calificar a promoción de nivel de confianza 3." tl3_requires_posts_read_all_time: "El número mínimo total de posts que un usuario debió de haber leído para calificar a nivel de confianza 3." tl3_promotion_min_duration: "El número mínimo de días que una promoción de nivel de confianza 3 dura antes que el usuario pueda ser degradado de vuelta a nivel de confianza 2." @@ -972,6 +999,7 @@ es: unsubscribe_via_email: "Permitir a los usuarios darse de baja de los emails respondiendo con el texto 'unsubscribe' en el asunto o el cuerpo del mensaje" unsubscribe_via_email_footer: "Adjuntar un enlace para darse de baja al pie de los emails enviados" delete_email_logs_after_days: "Eliminar logs de email después de (N) días. Si es 0 permanecerán de forma indefinida." + enable_staged_users: "Crear cuentas provisionales automáticamente al procesar emails entrantes." pop3_polling_enabled: "Poll vía POP3 para respuestas de e-mail." pop3_polling_ssl: "Usar SSL mientras se conecta al servidor POP3. (Recomendado)" pop3_polling_period_mins: "El período en minutos entre revisiones de correo de la cuenta POP3. NOTA: requiere reiniciar." diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index 412e554264..a49c66c4cf 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -169,6 +169,9 @@ fi: hot: "Kuumat ketjut" top: "Huippuketjut" posts: "Uusimmat viestit" + private_posts: "Uusimmat yksityisviestit" + group_posts: "Uusimmat viestit ryhmässä %{group_name}" + group_mentions: "Uusimmat maininnat ryhmässä %{group_name}" too_late_to_edit: "Tämä viesti luotiin liian kauan sitten. Sitä ei voi enää muokata tai poistaa." revert_version_same: "Nykyinen revisio on sama, kuin jonka yrität palauttaa." excerpt_image: "kuva" @@ -709,11 +712,15 @@ fi: site_contact_username_warning: "Aseta käyttäjänimi, jonka nimissä palsta automaattiset viestit lähetetään. Muokkaa asetusta site_contact sivuston asetuksissa." notification_email_warning: "Palstan sähköpostien lähettäjäosoite ei ole kelvollinen, josta johtuen sähköpostit eivät mene perille luotettavasti. Aseta toimiva sähköpostiosoite kohtaan notification_email sivuston asetuksissa." subfolder_ends_in_slash: "Alihakemiston asetuksesi ei kelpaa; DISCOURSE_RELATIVE_URL_ROOT päättyy vinoviivaan." + email_polling_errored_recently: + one: "Sähköpostin pollaus on aiheuttanut virheen edellisen 24 tunnin aikana. Tarkastele lokeja saadaksesi lisätietoja." + other: "Sähköpostin pollaus aiheutti %{count} virhettä edellisen 24 tunnin aikana. Tarkastele lokeja saadaksesi lisätietoja." site_settings: censored_words: "Sanat, jotka korvataan automaattisesti merkeillä ■■■■" delete_old_hidden_posts: "Poista automaattisesti kaikki yli 30 päivää piilotettuna olleet viestit." default_locale: "Sivuston oletuskieli (ISO 639-1 koodi)" allow_user_locale: "Salli käyttäjien vaihtaa käyttöliittymän kieli omista asetuksista" + set_locale_from_accept_language_header: "Aseta sivuston kieli kirjautumattomille käyttäjille selaimen kielivalinnan perusteella. (KOKEELLINEN, ei toimi anonyymin välimuistin kanssa)" min_post_length: "Viestin merkkien minimimäärä" min_first_post_length: "Ketjun aloitusviestin (leipätekstin) merkkien minimimäärä" min_private_message_post_length: "Viestin merkkien minimimäärä viesteille" @@ -722,7 +729,7 @@ fi: max_topic_title_length: "Viestin otsikon merkkien maksimimäärä" min_private_message_title_length: "Viestin otsikon merkkien minimimäärä viestille" min_search_term_length: "Haun merkkien minimimäärä" - allow_uncategorized_topics: "Salli ketjujen luominen valitsematta aluetta. VAROITUS: Sinun täytyy siirtää alueettomat ketjut jollekin alueelle, ennen tämän asetuksen poistamista käytöstä." + allow_uncategorized_topics: "Salli ketjujen luominen valitsematta aluetta. VAROITUS: Alueettomat ketjut täytyy siirtää jollekin alueelle ennen kuin poistat asetuksen käytöstä." allow_duplicate_topic_titles: "Salli ketjun luominen identtisellä otsikolla." unique_posts_mins: "Kuinka monta minuuttia, kunnes käyttäjä voi luoda uudestaan saman sisältöisen viestin" educate_until_posts: "Näytä uuden käyttäjän ohje, kun käyttäjä alkaa kirjoittamaan ensimmäistä (n) viestiään viestikenttään." @@ -924,9 +931,17 @@ fi: tl2_requires_likes_received: "Kuinka monta tykkäystä käyttäjän täytyy saada ennen ylentämistä luottamustasolle 2." tl2_requires_likes_given: "Kuinka monta tykkäystä käyttäjän täytyy antaa ennen ylentämistä luottamustasolle 2." tl2_requires_topic_reply_count: "Kuinka moneen ketjuun käyttäjän täytyy vastata ennen ylentämistä luottamustasolle 2." + tl3_time_period: "Luottamustason 3 vaatimuksiin liittyvän ajanjakson pituus (päivissä)" + tl3_requires_days_visited: "Monenako päivänä vähintään käyttäjän täytyy olla vieraillut sivustolla viimeisen (tl3 time period) päivän aikana voidakseen saavuttaa luottamustason 3. Ota ylennykset käytöstä asettamalla arvo korkeammaksi kuin lt3 aikaraja, (0 tai korkeampi)" + tl3_requires_topics_replied_to: "Moneenko ketjuun vähintään käyttäjän täytyy olla vastannut viimeisen (tl3 time period) päivän aikana voidakseen saavuttaa luottamustason 3. (0 tai korkeampi)" + tl3_requires_topics_viewed: "Montako prosenttia viimeisen (tl3 time period) päivän aikana luoduista ketjuista käyttäjän täytyy olla katsellut voidakseen saavuttaa luottamustason 3. (asteikko 0-100)" + tl3_requires_posts_read: "Montako prosenttia viimeisen (tl3 time period) päivän aikana luoduista viesteistä käyttäjän täytyy olla katsellut voidakseen saavuttaa luottamustason 3. (asteikko 0-100)" tl3_requires_topics_viewed_all_time: "Montako ketjua käyttäjän täytyy olla katsonut voidakseen saavuttaa luottamustason 3." tl3_requires_posts_read_all_time: "Montako viestiä käyttäjän täytyy olla katsonut voidakseen saavuttaa luottamustason 3." + tl3_requires_max_flagged: "Käyttäjällä ei saa olla enempää kuin x viestiä liputettuna x eri käyttäjältä viimeisen (tl3 time period) päivän aikana voidakseen saavuttaa luottamustason 3. (0 tai korkeampi)" tl3_promotion_min_duration: "Kuinka montaa päivää luottamustasolle 3 ylennyksen jälkeen käyttäjä voidaa jälleen alentaa luottamustasolle 2." + tl3_requires_likes_given: "Montako tykkäystä käyttäjän täytyy olla vähintään antanut viimeisen (tl3 time period) päivän aikana voidakseen saavuttaa luottamustason 3." + tl3_requires_likes_received: "Montako tykkäystä käyttäjän täytyy olla vähintään saanut viimeisen (tl3 time period) päivän aikana voidakseen saavuttaa luottamustason 3." tl3_links_no_follow: "Älä poista rel=nofollow -attribuuttia linkeistä luottamustason 3 käyttäjiltä." min_trust_to_create_topic: "Ketjun luomiseksi vaadittava luottamustaso." min_trust_to_edit_wiki_post: "Wikiviestin muokkaamiseen vaadittava luottamustaso." @@ -941,6 +956,7 @@ fi: max_users_notified_per_group_mention: "Kuinka moni voi saada ilmoituksen, kun ryhmään viitataan (jos raja ylittyy, kukaan ei saa ilmoitusta)" create_thumbnails: "Luo esikatselu- ja lightbox-kuvia, jotka ovat liian suuria mahtuakseen viestiin." email_time_window_mins: "Odota (n) minuuttia ennen ilmoitussähköpostien lähettämistä, jotta käyttäjällä on aikaa muokata ja viimeistellä viestinsä." + private_email_time_window_seconds: "Odota (n) sekuntia ennen sähköposti-ilmoitusten lähettämistä käyttäjille, jotta kirjoittajalla on mahdollisuus tehdä muokkaukset ja viimeistellä viestinsä." email_posts_context: "Kuinka monta edellistä vastausta liitetään kontekstiksi sähköposti-ilmoituksessa." flush_timings_secs: "Kuinka usein timing data päivitetään palvelimelle, sekunneissa." title_max_word_length: "Sanan enimmäispituus merkkeinä ketjun otsikossa" @@ -998,6 +1014,7 @@ fi: unsubscribe_via_email: "Salli käyttäjän lakkauttaa sähköposti-ilmoitukset lähettämällä sähköpostiviesti, jonka otsikossa tai leipätekstissä esiintyy sana \"unsubscribe\"" unsubscribe_via_email_footer: "Liitä sähköpostiviestien alaosaan linkki, jonka avulla saaja voi lakkauttaa sähköposti-ilmoitukset" delete_email_logs_after_days: "Poista sähköpostilokit (N) päivän jälkeen. Aseta 0 säilyttääksesi ikuisesti." + max_emails_per_day_per_user: "Käyttäjälle päivässä lähetettävien sähköpostien enimmäismäärä. Aseta 0, jos et halua rajoittaa." pop3_polling_enabled: "Pollaa sähköpostivastaukset POP3:lla." pop3_polling_ssl: "Käytä SSL-salausta yhdistettäessä POP3-palvelimeen. (Suositellaan)" pop3_polling_period_mins: "Tiheys minuuteissa kuinka usein POP3 tililtä tarkastetaan uudet sähköpostit. HUOM: vaatii uudelleenkäynnistyksen." @@ -1025,6 +1042,7 @@ fi: digest_topics: "Sähköpostitiivistelmässä näytettävien ketjujen maksimimäärä." digest_min_excerpt_length: "Viestin katkelman vähimmäispituus sähköpostitiivistelmässä, merkeissä" delete_digest_email_after_days: "Jätä lähettämättä tiivistelmäsähköpostit käyttäjille, joita ei ole nähty (n) päivän aikana." + digest_suppress_categories: "Älä liitä näiden alueiden viestejä sähköpostitiivistelmiin." disable_digest_emails: "Ota tiivistelmäsähköpostit pois käytöstä kaikilta käyttäjiltä." detect_custom_avatars: "Tarkistetaanko, ovatko käyttäjät ladanneet oman profiilikuvan." max_daily_gravatar_crawls: "Korkeintaan kuinka monta kertaa Discourse tarkistaa avatarit Gravatarista päivässä" @@ -1079,6 +1097,7 @@ fi: approve_unless_trust_level: "Tätä luottamustasoa alhaisempien käyttäjien viestit tarkastetaan" notify_about_queued_posts_after: "Jos hyväksyntää odottavia viestejä on odottanut näin monta tuntia, lähetä sähköposti contact_email osoitteeseen. Aseta 0 ottaaksesi pois käytöstä." default_email_digest_frequency: "Kuinka usein käyttäjille lähetetään tiivistelmäsähköposti oletuksena." + default_include_tl0_in_digests: "Sisällytä uusien käyttäjien viestit sähköpostitiivistelmiin oletuksena. Tätä voi muuttaa käyttäjäasetuksissa." default_email_private_messages: "Lähetä oletuksena sähköposti, kun joku lähettää käyttäjälle viestin." default_email_direct: "Lähetä oletuksena sähköposti, kun joku lainaa/vastaa/mainitsee tai kutsuu käyttäjän." default_email_mailing_list_mode: "Lähetä oletuksena sähköposti jokaisesta uudesta viestistä." @@ -1117,6 +1136,7 @@ fi: pop3_polling_password_is_empty: "Sinun täytyy asettaa 'pop3 polling password' ennen POP3-pollauksen ottamista käyttöön." pop3_polling_authentication_failed: "POP3 autentikaatio epäonnistui. Tarkasta pop3 kirjautumistiedot." reply_by_email_address_is_empty: "'reply by email address' täytyy olla asetettuna ennen sähköpostivastausten ottamista käyttöön." + user_locale_not_enabled: "'allow user locale' täytyy olla asetettuna ennen tämän asetuksen ottamista käyttöön." notification_types: group_mentioned: "%{group_name} mainittiin täällä %{link}" mentioned: "%{display_username} viittasi sinuun viestissä %{link}" @@ -1138,6 +1158,11 @@ fi: category: 'Alueet' topic: 'Tulokset' user: 'Käyttäjät' + sso: + not_found: "Tiliäsi ei löydetty. Ota yhteyttä sivuston ylläpitäjään." + account_not_approved: "Tilisi odottaa hyväksyntää. Saat sähköpostin, kun tunnuksesi on hyväksytty." + unknown_error: "Tiliisi liittyen on tapahtunut virhe. Ota yhteyttä sivuston ylläpitäjään." + timeout_expired: "Kirjautuminen on vanhentunut, yritä kirjautua sisään uudestaan." original_poster: "Alkuperäinen kirjoittaja" most_posts: "Eniten viestejä" most_recent_poster: "Uusin kirjoittaja" @@ -1703,6 +1728,9 @@ fi: subject_pm: "[YV]" user_notifications: previous_discussion: "Edelliset vastaukset" + reached_limit: + one: "VAROITUS: Olet saavuttanut päivittäisen rajan sähköpostien määrälle. Seuraavia sähköposti-ilmoituksia ei lähetetä." + other: "VAROITUS: Sinulle on lähetetty tänään %{count} sähköpostia, joka on päivittäinen raja sähköpostien määrälle. Seuraavia sähköposti-ilmoituksia ei lähetetä." in_reply_to: "Vastauksena" unsubscribe: title: "Peru tilaus" @@ -1713,12 +1741,53 @@ fi: visit_link_to_respond: "Vastaa [vierailemalla ketjussa](%{base_url}%{url})" visit_link_to_respond_pm: "Vastaa [vierailemalla ketjussa](%{base_url}%{url})." posted_by: "Käyttäjältä %{username} %{post_date}" + invited_to_private_message_body: | + %{username} kutsui sinut viestiketjuun + + > **%{topic_title}** + > + > %{topic_excerpt} + + sivustolla + + > %{site_title} -- %{site_description} + invited_to_topic_body: | + %{username} kutsui sinut keskusteluun + + > **%{topic_title}** + > + > %{topic_excerpt} + + sivustolla + + > %{site_title} -- %{site_description} user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} kutsui sinut viestiin '%{topic_title}'" + text_body_template: | + %{header_instructions} + + %{message} + + --- + %{respond_instructions} user_invited_to_private_message_pm_staged: subject_template: "[%{site_name}] %{username} kutsui sinut yksityiseen keskusteluun '%{topic_title}'" + text_body_template: | + %{header_instructions} + + %{message} + + --- + %{respond_instructions} user_invited_to_topic: subject_template: "[%{site_name}] %{username} kutsui sinut ketjuun '%{topic_title}'" + text_body_template: | + %{header_instructions} + + %{message} + + --- + %{respond_instructions} user_replied: subject_template: "[%{site_name}] %{topic_title}" text_body_template: | @@ -1954,6 +2023,7 @@ fi: post_deleted: "kirjoittaja poisti viestin" user_suspended: "käyttäjä hyllytettiin" already_read: "käyttäjä on jo lukenut tämän viestin" + exceeded_limit: "Ylitti max_emails_per_day_per_user" message_blank: "viesti on tyhjä" message_to_blank: "message.to on tyhjä" text_part_body_blank: "text_part.body on tyhjä" @@ -2073,6 +2143,152 @@ fi: title: "Käyttöehdot" privacy_topic: title: "Rekisteriseloste" + static: + search_help: | +

    Vinkkejä

    +

    +

      +
    • Otsikon vastaavuudet ovat etusijalla – jos olet epävarma, etsi otsikkoa
    • +
    • Parhaat tulokset saa uniikeilla ja harvinaisilla sanoilla
    • +
    • Voit kokeilla etsiä tietyn alueen, ketjun tai käyttäjän viesteistä
    • +
    +

    +

    Options

    +

    + + + + + + + +
    order:viewsorder:latestorder:likes
    status:openstatus:closedstatus:archivedstatus:norepliesstatus:single_user
    category:foouser:foogroup:foobadge:foo
    in:likesin:postedin:watchingin:trackingin:private
    in:bookmarksin:firstin:pinnedin:unpinned
    posts_count:numbefore:days or dateafter:days or date
    +

    +

    Esimerkkejä

    +

    +

      +
    • rainbows category:parks status:open order:latest etsii alueelta "parks" ketjuja, joissa esiintyy sana "rainbows" ja joita ei suljettu tai arkistoitu, ja esittää ylimmäisinä ne ketjut, joihin on viimeksi kirjoitettu.
    • +
    • rainbows category:"parks and gardens" in:bookmarks etsii alueelta "parks and gardens" kirjanmerkkeihin lisäämiäsi ketjuja, joissa esiintyy sana "rainbows".
    • +
    +

    + badges: + editor: + name: Muokkaaja + description: Ensimmäinen viestin muokkaus + long_description: | + Tämä arvomerkki myönnetään, kun ensimmäistä kertaa muokkaat viestiäsi. Viestiä ei voi muokata kuin määrätyn ajan viestin lähettämisen jälkeen, mutta sinä aikana muokkaaminen on aina hyvä ajatus. Voit parannella viestiä, korjata pieniä virheitä tai lisätä jotakin, mikä pääsi unohtumaan viestin kirjoittamisen hetkellä. Muokkaa viesteistäsi entistäkin parempia! + basic_user: + name: Haastaja + description: Myönnetty kaikki palstan välttämättömimmät toiminnot + long_description: | + Tämä arvomerkki myönnetään, kun nouset luottamustasolle 1. Kiitos siitä, että olet päättänyt antaa palstalle mahdollisuuden ja lukenut muutamaa ketjua ottaaksesi selvää, mistä yhteisössä on kyse. Uuden käyttäjän rajoituksia on nyt poistettu; sait käyttöösi välttämättömimmät palstan toiminnot kuten yksityisviestit, liputtamisen, wiki-viestien muokkaamisen ja oikeuden laittaa viestiin useampia kuvia ja linkkejä. + member: + name: Konkari + description: Myönnetty kutsut, ryhmäviestit ja lisää tykkäyksiä + long_description: | + Tämä arvomerkki myönnetään, kun nouset luottamustasolle 2. Kiitos siitä, että olet ollut keskuudessamme viikkojen ajan ja tullut osaksi yhteisöä. Voit nyt lähettää kutsuja käyttäjäsivultasi ja ketjuista, luoda ryhmäviestejä ja käytössäsi on muutama tykkäys enemmän päivää kohden. + regular: + name: Mestari + description: Myönnetty ketjujen siirto toiselle alueelle ja uudelleen nimeäminen, hakukoneiden seuraamat linkit, wiki-viestit ja lisää tykkäyksiä + leader: + name: Johtaja + description: Myönnetty minkä tahansa viestin muokkaaminen, kiinnittäminen, sulkeminen, arkistoiminen, pilkkominen ja yhdistäminen ja lisää tykkäyksiä + welcome: + name: Tervetuloa + description: Sai tykkäyksen + autobiographer: + name: Omaelämäkerta + description: Täytti käyttäjätiedot + anniversary: + name: Vuosipäivä + description: Aktiivinen jäsen vuoden ajan, kirjoittanut ainakin yhden viestin + nice_post: + name: Hyvä vastaus + description: Vastaus sai 10 tykkäystä + good_post: + name: Erinomainen vastaus + description: Vastaus sai 25 tykkäystä + great_post: + name: Loistava vastaus + description: Vastaus sai 50 tykkäystä + nice_topic: + name: Hyvä ketju + description: Ketjunavaus sai 10 tykkäystä + good_topic: + name: Erinomainen ketju + description: Ketjunavaus sai 25 tykkäystä + great_topic: + name: Loistava ketju + description: Ketjunavaus sai 50 tykkäystä + nice_share: + name: Hyvä jako + description: Jakoi viestin, ja linkin kautta saapui 25 vierailijaa + good_share: + name: Erinomainen jako + description: Jakoi viestin, ja linkin kautta saapui 300 vierailijaa + great_share: + name: Loistava jako + description: Jakoi viestin, ja linkin kautta saapui 1000 vierailijaa + first_like: + name: Ensimmäinen tykkäys + description: Tykkäsi viestistä + first_flag: + name: Ensimmäinen liputus + description: Liputti viestin + promoter: + name: Markkinoija + description: Kutsui käyttäjän + campaigner: + name: Kampanjoija + description: Kutsui kolme haastajaa (luottamustaso 1) + champion: + name: Kampanjapäällikkö + description: Kutsui viisi konkaria (luottamustaso 2) + first_share: + name: Ensimmäinen jako + description: Jakoi viestin + first_link: + name: Ensimmäinen linkki + description: Linkitti toiseen ketjuun + first_quote: + name: Ensimmäinen lainaus + description: Lainasi viestiä + read_guidelines: + name: Luki ohjeet + description: Luki palstan säännöt + reader: + name: Lukutoukka + description: Luki jokaisen viestin ketjusta, jossa on enemmän kuin 100 vastausta + popular_link: + name: Suosittu linkki + description: Linkitti ulkoiselle sivustolle, ja linkkiä klikattiin 50 kertaa + hot_link: + name: Kuuma linkki + description: Linkitti ulkoiselle sivustolle, ja linkkiä klikattiin 300 kertaa + famous_link: + name: Kuuluisa linkki + description: Linkitti ulkoiselle sivustolle, ja linkkiä klikattiin 1000 kertaa + appreciated: + name: Arvostettu + description: 20 viestiä sai tykkäyksen + respected: + name: Kunnioitettu + description: 100 viestiä sai ainakin 2 tykkäystä + admired: + name: Ihailtu + description: 300 viestiä sai ainakin 5 tykkäystä + out_of_love: + name: Rakkauden rajat + description: Tykkäsi 50 kertaa päivän aikana + thank_you: + name: Kiitos + description: 20 viestistä on tykätty ja on tykännyt 10 viestistä + gives_back: + name: Vastavuoroinen + description: 100 viestistä on tykätty ja on tykännyt 100 viestistä + empathetic: + name: Empaattinen + description: 500 viestistä on tykätty ja on tykännyt 1000 viestistä admin_login: success: "Sähköposti lähetetty" error: "Virhe!" From 54f8bdd13b43637d3a3e3742c3c3b6d3312dc518 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Tue, 29 Mar 2016 11:53:07 -0400 Subject: [PATCH 161/162] FIX: only validate username when changing it --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index e0663db08e..9444fb8396 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -69,7 +69,7 @@ class User < ActiveRecord::Base before_validation :strip_downcase_email validates_presence_of :username - validate :username_validator + validate :username_validator, if: :username_changed? validates :email, presence: true, uniqueness: true validates :email, email: true, if: :email_changed? validate :password_validator From 25235eca1cd776904e4b0ba688ad4e42e5ce2920 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Tue, 29 Mar 2016 15:13:43 -0400 Subject: [PATCH 162/162] Version bump to v1.5.0.beta14 --- lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.rb b/lib/version.rb index 1ff19323bd..407ea4e310 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -5,7 +5,7 @@ module Discourse MAJOR = 1 MINOR = 5 TINY = 0 - PRE = 'beta13b' + PRE = 'beta14' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end