From 806d856d6559f433d8a0eb334d7bf33606992cdb Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Tue, 18 Nov 2014 16:08:56 -0500 Subject: [PATCH 001/991] Version bump to v1.2.0.beta2 --- lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.rb b/lib/version.rb index a496adb9db..e5c9757d7f 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -5,7 +5,7 @@ module Discourse MAJOR = 1 MINOR = 2 TINY = 0 - PRE = 'beta1' + PRE = 'beta2' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end From 7ad10b5ea6d50063ca940849f2b19eab192122d6 Mon Sep 17 00:00:00 2001 From: Guilherme Carreiro Date: Tue, 18 Nov 2014 20:29:46 -0200 Subject: [PATCH 002/991] =?UTF-8?q?FIX:=20Sharing=20link=20stood=20visible?= =?UTF-8?q?=20after=20using=20browser=E2=80=99s=20back=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/javascripts/discourse/views/share.js.es6 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/discourse/views/share.js.es6 b/app/assets/javascripts/discourse/views/share.js.es6 index 2b4d6e0358..1c354e46d0 100644 --- a/app/assets/javascripts/discourse/views/share.js.es6 +++ b/app/assets/javascripts/discourse/views/share.js.es6 @@ -104,6 +104,8 @@ export default Discourse.View.extend({ }, willDestroyElement: function() { + this.get('controller').send('close'); + $('html').off('click.discoure-share-link') .off('mousedown.outside-share-link') .off('keydown.share-view'); From a211f542f5d2e6a6e47e0352aabbafb1af01aeaa Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 19 Nov 2014 15:11:09 +1100 Subject: [PATCH 003/991] update rails --- Gemfile_master.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile_master.lock b/Gemfile_master.lock index c638188101..acb65ae644 100644 --- a/Gemfile_master.lock +++ b/Gemfile_master.lock @@ -8,13 +8,13 @@ GIT GIT remote: https://github.com/rails/arel.git - revision: 590c784a30b13153667f8db7915998d7731e24e5 + revision: a04851702b0e8e694a92139c3ee9f3b1622f3f5d specs: arel (6.0.0.beta2) GIT remote: https://github.com/rails/rails.git - revision: eb26f24bde62cbbcd8ef0e7ee9c64060b098baff + revision: 08576b94ad4f19dfc368619d7751e211d23dcad8 specs: actionmailer (4.2.0.beta4) actionpack (= 4.2.0.beta4) From eb9eada894d53ffb9ff5a814c7cdf65756b34254 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 18 Nov 2014 21:17:38 +0530 Subject: [PATCH 004/991] FEATURE: log out user everywhere and refresh/redirect --- .../discourse/initializers/logout.js.es6 | 23 +++++++++++++++++++ app/controllers/admin/users_controller.rb | 1 + config/locales/client.en.yml | 2 ++ 3 files changed, 26 insertions(+) create mode 100644 app/assets/javascripts/discourse/initializers/logout.js.es6 diff --git a/app/assets/javascripts/discourse/initializers/logout.js.es6 b/app/assets/javascripts/discourse/initializers/logout.js.es6 new file mode 100644 index 0000000000..a65de91d7a --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/logout.js.es6 @@ -0,0 +1,23 @@ +/** + Subscribe to "logout" change events via the Message Bus +**/ +export default { + name: "logout", + after: "message-bus", + + initialize: function () { + if (!Discourse.MessageBus) { return; } + + Discourse.MessageBus.subscribe("/logout", function (user_id) { + var refresher = function() { + var redirect = Discourse.SiteSettings.logout_redirect; + if(redirect.length === 0){ + window.location.pathname = Discourse.getURL('/'); + } else { + window.location.href = redirect; + } + }; + bootbox.dialog(I18n.t("logout"), {label: I18n.t("refresh"), callback: refresher}, {onEscape: refresher, backdrop: 'static'}) + }); + } +}; diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index ed69ad463f..c43622ba3a 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -68,6 +68,7 @@ class Admin::UsersController < Admin::AdminController def log_out @user.auth_token = nil @user.save! + MessageBus.publish "/logout", @user.id, user_ids: [@user.id] render nothing: true end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 5e5810ef19..f54a95c06e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -535,6 +535,8 @@ en: fixed: "Load Page" close: "Close" assets_changed_confirm: "This site was just updated. Refresh now for the latest version?" + logout: "You were logged out." + refresh: "Refresh" read_only_mode: enabled: "An administrator enabled read-only mode. You can continue to browse the site but interactions may not work." login_disabled: "Login is disabled while the site is in read only mode." From c4fa0edde43032e73e6042b7195408c6cdf3d205 Mon Sep 17 00:00:00 2001 From: fwoelm Date: Wed, 19 Nov 2014 09:18:09 -0700 Subject: [PATCH 005/991] Update DEVELOPER-ADVANCED.md To install the gem pg-0.15.1, you need to have libpq-dev installed. Otherwise bundle install will fail. See http://stackoverflow.com/questions/16788062/error-running-gem-install-pg-v-0-15-1 --- docs/DEVELOPER-ADVANCED.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/DEVELOPER-ADVANCED.md b/docs/DEVELOPER-ADVANCED.md index 7b5f3ff0ff..c5293a0188 100644 --- a/docs/DEVELOPER-ADVANCED.md +++ b/docs/DEVELOPER-ADVANCED.md @@ -13,8 +13,9 @@ Note: If you are developing on a Mac, you will probably want to look at [these i 1. Run `redis-server -v` to see if you already have it. 3. Install ImageMagick 4. Install libxml2, g++, and make. -5. Install Ruby 2.1.3 and Bundler. -6. Clone the project and bundle. +5. Install libpq-dev. +6. Install Ruby 2.1.3 and Bundler. +7. Clone the project and bundle. ## Before you start Rails From 626da65339882e7fcf2a03e083d91aa50ec66788 Mon Sep 17 00:00:00 2001 From: cpradio Date: Wed, 19 Nov 2014 11:26:57 -0500 Subject: [PATCH 006/991] FIX: TL4 flag on TL0 post as spam, hides the post --- app/models/post_action.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/post_action.rb b/app/models/post_action.rb index 6eec1ac6ff..e40c34c188 100644 --- a/app/models/post_action.rb +++ b/app/models/post_action.rb @@ -384,7 +384,7 @@ class PostAction < ActiveRecord::Base return if post.hidden if post_action_type == :spam && - acting_user.trust_level == TrustLevel[3] && + acting_user.has_trust_level?(TrustLevel[3]) && post.user.trust_level == TrustLevel[0] hide_post!(post, post_action_type, Post.hidden_reasons[:flagged_by_tl3_user]) From c84b51d4aedcfb78a2c3a2a02ff814c8e9d37e68 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Wed, 19 Nov 2014 21:19:12 +0530 Subject: [PATCH 007/991] FEATURE: show exact error for test email --- .../admin/controllers/admin-email-index.js.es6 | 8 ++++++-- app/controllers/admin/email_controller.rb | 8 ++++++-- config/environments/development.rb | 1 - config/locales/client.en.yml | 1 + 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 index 58eff83cd2..341ca4f652 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 @@ -36,8 +36,12 @@ export default DiscourseController.extend({ data: { email_address: this.get('testEmailAddress') } }).then(function () { self.set('sentTestEmail', true); - }).catch(function () { - bootbox.alert(I18n.t('admin.email.test_error')); + }, function(e) { + if (e.responseJSON && e.responseJSON.errors) { + bootbox.alert(I18n.t('admin.email.error', { server_error: e.responseJSON.errors[0] })); + } else { + bootbox.alert(I18n.t('admin.email.test_error')); + } }).finally(function() { self.set('sendingEmail', false); }); diff --git a/app/controllers/admin/email_controller.rb b/app/controllers/admin/email_controller.rb index 8d706b7c03..0a8e3d3235 100644 --- a/app/controllers/admin/email_controller.rb +++ b/app/controllers/admin/email_controller.rb @@ -9,8 +9,12 @@ class Admin::EmailController < Admin::AdminController def test params.require(:email_address) - Jobs::TestEmail.new.execute(to_address: params[:email_address]) - render nothing: true + begin + Jobs::TestEmail.new.execute(to_address: params[:email_address]) + render nothing: true + rescue => e + render json: {errors: [e.message]}, status: 422 + end end def all diff --git a/config/environments/development.rb b/config/environments/development.rb index dd225cf990..160ee7dc68 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -52,4 +52,3 @@ Discourse::Application.configure do config.developer_emails = emails.split(",").map(&:strip) end end - diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 5e5810ef19..5d7369364e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1760,6 +1760,7 @@ en: settings: "Settings" all: "All" sending_test: "Sending test Email..." + error: "ERROR - %{server_error}" test_error: "There was a problem sending the test email. Please double-check your mail settings, verify that your host is not blocking mail connections, and try again." sent: "Sent" skipped: "Skipped" From aebf36c356b3128044de6f426f1b4153e240a8f4 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Wed, 19 Nov 2014 22:39:23 +0530 Subject: [PATCH 008/991] remove /download from csv file url --- app/controllers/admin/export_csv_controller.rb | 5 +++-- app/jobs/regular/export_csv_file.rb | 4 ++-- config/routes.rb | 6 +++--- spec/controllers/admin/export_csv_controller_spec.rb | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/controllers/admin/export_csv_controller.rb b/app/controllers/admin/export_csv_controller.rb index f0e574105a..82fec5d09e 100644 --- a/app/controllers/admin/export_csv_controller.rb +++ b/app/controllers/admin/export_csv_controller.rb @@ -1,6 +1,6 @@ class Admin::ExportCsvController < Admin::AdminController - skip_before_filter :check_xhr, only: [:download] + skip_before_filter :check_xhr, only: [:show] def export_user_list # export csv file in a background thread @@ -8,7 +8,8 @@ class Admin::ExportCsvController < Admin::AdminController render json: success_json end - def download + # download + def show filename = params.fetch(:id) if export_csv_path = ExportCsv.get_download_path(filename) send_file export_csv_path diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index 2bd8a29894..79f9ea38a6 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -6,7 +6,7 @@ module Jobs class ExportCsvFile < Jobs::Base CSV_USER_ATTRS = ['id','name','username','email','title','created_at','trust_level','active','admin','moderator','ip_address'] CSV_USER_STATS = ['topics_entered','posts_read_count','time_read','topic_count','post_count','likes_given','likes_received'] - + sidekiq_options retry: false attr_accessor :current_user @@ -108,7 +108,7 @@ module Jobs def notify_user if @current_user if @file_name != "" && File.exists?("#{ExportCsv.base_directory}/#{@file_name}") - SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/admin/export_csv/#{@file_name}/download", file_name: @file_name) + SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/admin/export_csv/#{@file_name}", file_name: @file_name) else SystemMessage.create_from_system_user(@current_user, :csv_export_failed) end diff --git a/config/routes.rb b/config/routes.rb index f33712d4bb..abcb00680e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -156,12 +156,12 @@ Discourse::Application.routes.draw do end resources :export_csv, constraints: AdminConstraint.new do - member do - get "download" => "export_csv#download", constraints: { id: /[^\/]+/ } - end collection do get "users" => "export_csv#export_user_list" end + member do + get "" => "export_csv#show", constraints: { id: /[^\/]+/ } + end end resources :badges, constraints: AdminConstraint.new do diff --git a/spec/controllers/admin/export_csv_controller_spec.rb b/spec/controllers/admin/export_csv_controller_spec.rb index cbf037e4cc..644deb6d90 100644 --- a/spec/controllers/admin/export_csv_controller_spec.rb +++ b/spec/controllers/admin/export_csv_controller_spec.rb @@ -19,12 +19,12 @@ describe Admin::ExportCsvController do export = ExportCsv.new() ExportCsv.expects(:get_download_path).with(export_filename).returns(export) subject.expects(:send_file).with(export) - get :download, id: export_filename + get :show, id: export_filename end it "returns 404 when the export file does not exist" do ExportCsv.expects(:get_download_path).returns(nil) - get :download, id: export_filename + get :show, id: export_filename response.should be_not_found end From 0947191060757efee411d05afcc267c968af316e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 19 Nov 2014 20:37:43 +0100 Subject: [PATCH 009/991] UX: improved our footer handling - new "show-footer" mixins - converted most of the routes to ES6 - FIX: handling of "indexStream" in user pages There will now be a footer on all the following pages - /exception - /about - /latest - /new - /unread - /starred - /top - /categories - /c/:category - /c/:category/l/latest - /c/:category/l/new - /c/:category/l/unread - /c/:category/l/top - /t/:topic/:id - /groups/:name/members - /user/activity - /user/activity/topics - /user/activity/posts - /user/activity/replies - /user/activity/likes-given - /user/activity/likes-received - /user/activity/bookmarks - /user/activity/starred - /user/badges - /user/notifications - /user/flagged-posts - /user/deleted-posts - /user/private-messages - /user/private-messages/mine - /user/private-messages/unread - /user/invited - /user/:username/preferences - /faq (static pages) - /badges - /badges/:id/:badge --- .../admin/views/admin-flags-list.js.es6 | 4 +- .../discourse/controllers/application.js.es6 | 1 + .../controllers/avatar-selector.js.es6 | 33 +++------- .../discourse/controllers/badges/show.js.es6 | 12 ++-- .../bulk-notification-level.js.es6 | 10 +-- .../discourse/controllers/change-owner.js.es6 | 11 +--- .../controllers/composer-messages.js.es6 | 9 +-- .../controllers/create-account.js.es6 | 1 - .../discourse/controllers/discovery.js.es6 | 6 +- .../controllers/edit-category.js.es6 | 11 +--- .../controllers/edit-topic-auto-close.js.es6 | 10 +-- .../discourse/controllers/exception.js.es6 | 10 +-- .../controllers/flag-action-type.js.es6 | 9 +-- .../controllers/forgot-password.js.es6 | 1 - .../discourse/controllers/group.js.es6 | 9 +-- .../discourse/controllers/history.js.es6 | 11 +--- .../controllers/invite-private.js.es6 | 1 - .../discourse/controllers/invite.js.es6 | 1 - .../keyboard-shortcuts-help.js.es6 | 1 - .../discourse/controllers/merge-topic.js.es6 | 11 +--- .../controllers/not-activated.js.es6 | 1 - .../discourse/controllers/raw-email.js.es6 | 11 +--- .../discourse/controllers/search-help.js.es6 | 1 - .../discourse/controllers/split-topic.js.es6 | 11 +--- .../controllers/topic-admin-menu.js.es6 | 9 +-- .../controllers/topic-bulk-actions.js.es6 | 10 +-- .../controllers/topic-list-item.js.es6 | 9 +-- .../discourse/controllers/topic.js.es6 | 8 ++- .../controllers/upload-selector.js.es6 | 1 - .../controllers/user-activity.js.es6 | 16 ++++- .../discourse/controllers/user-invited.js.es6 | 9 +-- .../controllers/user-notifications.js.es6 | 7 ++- .../discourse/controllers/user-posts.js.es6 | 7 +++ .../controllers/user-topics-list.js.es6 | 14 ++--- .../discourse/controllers/user.js.es6 | 23 +------ .../dynamic-route-builders.js.es6 | 2 + .../discourse/lib/static-route-builder.js.es6 | 4 +- .../mixins/{load_more.js => load-more.js.es6} | 12 +--- .../discourse/mixins/show-footer.js.es6 | 16 +++++ .../discourse/models/user_posts_stream.js | 7 ++- .../javascripts/discourse/routes/about.js.es6 | 4 +- .../discourse/routes/application_routes.js | 1 - .../discourse/routes/badges-index.js.es6 | 4 +- .../discourse/routes/badges-show.js.es6 | 11 +++- .../build-admin-user-posts-route.js.es6 | 32 ++++++++++ .../routes/build-category-route.js.es6 | 2 +- .../discourse/routes/build-topic-route.js.es6 | 3 +- .../routes/build-user-topic-list-route.js.es6 | 31 ++++++++++ .../routes/discourse_restricted_user_route.js | 20 ------ ...e.js => discovery-categories-route.js.es6} | 6 +- ...covery_route.js => discovery-route.js.es6} | 26 ++++---- .../discourse/routes/exception.js.es6 | 4 +- .../discourse/routes/group-index.js.es6 | 10 ++- .../discourse/routes/group-members.js.es6 | 4 +- .../discourse/routes/preferences-about.js.es6 | 5 +- .../routes/preferences-badge-title.js.es6 | 4 +- .../routes/preferences-card-badge.js.es6 | 4 +- .../discourse/routes/preferences-email.js.es6 | 4 +- .../discourse/routes/preferences-index.js.es6 | 4 +- .../routes/preferences-username.js.es6 | 4 +- .../discourse/routes/preferences.js.es6 | 28 +++++---- .../discourse/routes/restricted-user.js.es6 | 12 ++++ .../routes/topic-from-params-near.js.es6 | 3 + ...rams_route.js => topic-from-params.js.es6} | 13 +--- .../{topic_route.js => topic-route.js.es6} | 11 +++- .../discourse/routes/unknown.js.es6 | 5 ++ .../discourse/routes/unknown_route.js | 5 -- .../routes/user-activity-bookmarks.js.es6 | 5 ++ .../routes/user-activity-edits.js.es6 | 5 ++ .../routes/user-activity-index.js.es6 | 14 +++++ .../routes/user-activity-likes-given.js.es6 | 5 ++ .../user-activity-likes-received.js.es6 | 5 ++ .../routes/user-activity-posts.js.es6 | 5 ++ .../routes/user-activity-replies.js.es6 | 5 ++ .../routes/user-activity-starred.js.es6 | 9 +++ ...m_route.js => user-activity-stream.js.es6} | 23 +++---- .../routes/user-activity-topics.js.es6 | 9 +++ .../discourse/routes/user-activity.js.es6 | 1 - .../discourse/routes/user-badges.js.es6 | 9 +-- .../routes/user-deleted-posts.js.es6 | 3 + .../routes/user-flagged-posts.js.es6 | 3 + .../discourse/routes/user-index.js.es6 | 3 +- .../discourse/routes/user-invited.js.es6 | 5 +- .../routes/user-notifications.js.es6 | 21 ++++--- .../routes/user-private-messages-index.js.es6 | 3 + .../routes/user-private-messages-mine.js.es6 | 3 + .../user-private-messages-unread.js.es6 | 3 + .../routes/user-private-messages.js.es6 | 10 ++- .../discourse/routes/user-topic-list.js.es6 | 14 +++++ .../javascripts/discourse/routes/user.js.es6 | 13 +++- .../routes/user_admin_posts_routes.js | 23 ------- .../routes/user_topic_list_routes.js | 61 ------------------- .../javascripts/discourse/templates/about.hbs | 2 - .../discourse/templates/application.hbs | 4 ++ .../discourse/templates/badges/index.hbs | 2 - .../discourse/templates/badges/show.hbs | 3 - .../discourse/templates/discovery.hbs | 3 - .../discourse/templates/static.hbs | 2 - .../javascripts/discourse/templates/topic.hbs | 4 -- .../discourse/templates/user/user.hbs | 4 -- .../discourse/views/badges-show.js.es6 | 4 +- .../discourse/views/discovery-topics.js.es6 | 3 +- .../discourse/views/group-index.js.es6 | 10 +-- .../discourse/views/user-invited.js.es6 | 4 +- .../views/user-notification-history.js.es6 | 4 +- .../discourse/views/user-posts.js.es6 | 4 +- .../discourse/views/user-stream.js.es6 | 4 +- .../discourse/views/user-topics-list.js.es6 | 4 +- app/assets/javascripts/main_include.js | 5 +- 109 files changed, 460 insertions(+), 466 deletions(-) create mode 100644 app/assets/javascripts/discourse/controllers/user-posts.js.es6 rename app/assets/javascripts/discourse/mixins/{load_more.js => load-more.js.es6} (62%) create mode 100644 app/assets/javascripts/discourse/mixins/show-footer.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/build-admin-user-posts-route.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/build-user-topic-list-route.js.es6 delete mode 100644 app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js rename app/assets/javascripts/discourse/routes/{discovery_categories_route.js => discovery-categories-route.js.es6} (93%) rename app/assets/javascripts/discourse/routes/{discovery_route.js => discovery-route.js.es6} (74%) create mode 100644 app/assets/javascripts/discourse/routes/restricted-user.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/topic-from-params-near.js.es6 rename app/assets/javascripts/discourse/routes/{topic_from_params_route.js => topic-from-params.js.es6} (84%) rename app/assets/javascripts/discourse/routes/{topic_route.js => topic-route.js.es6} (96%) create mode 100644 app/assets/javascripts/discourse/routes/unknown.js.es6 delete mode 100644 app/assets/javascripts/discourse/routes/unknown_route.js create mode 100644 app/assets/javascripts/discourse/routes/user-activity-bookmarks.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-activity-edits.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-activity-index.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-activity-likes-given.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-activity-likes-received.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-activity-posts.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-activity-replies.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-activity-starred.js.es6 rename app/assets/javascripts/discourse/routes/{user_activity_stream_route.js => user-activity-stream.js.es6} (64%) create mode 100644 app/assets/javascripts/discourse/routes/user-activity-topics.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-deleted-posts.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-flagged-posts.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-private-messages-index.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-private-messages-mine.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-private-messages-unread.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/user-topic-list.js.es6 delete mode 100644 app/assets/javascripts/discourse/routes/user_admin_posts_routes.js delete mode 100644 app/assets/javascripts/discourse/routes/user_topic_list_routes.js diff --git a/app/assets/javascripts/admin/views/admin-flags-list.js.es6 b/app/assets/javascripts/admin/views/admin-flags-list.js.es6 index b3035fe742..0f720b1e56 100644 --- a/app/assets/javascripts/admin/views/admin-flags-list.js.es6 +++ b/app/assets/javascripts/admin/views/admin-flags-list.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Discourse.View.extend(LoadMore, { loading: false, eyelineSelector: '.admin-flags tbody tr', diff --git a/app/assets/javascripts/discourse/controllers/application.js.es6 b/app/assets/javascripts/discourse/controllers/application.js.es6 index 176e0afff6..6017af3214 100644 --- a/app/assets/javascripts/discourse/controllers/application.js.es6 +++ b/app/assets/javascripts/discourse/controllers/application.js.es6 @@ -1,4 +1,5 @@ export default Ember.Controller.extend({ + showFooter: false, styleCategory: null, canSignUp: function() { diff --git a/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 b/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 index fe6d9c24d7..e856b5a09f 100644 --- a/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 +++ b/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 @@ -1,35 +1,21 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend(ModalFunctionality, { selectedUploadId: function(){ - switch(this.get("selected")){ - case "system": - return this.get("system_avatar_upload_id"); - case "gravatar": - return this.get("gravatar_avatar_upload_id"); - default: - return this.get("custom_avatar_upload_id"); + switch (this.get("selected")) { + case "system": return this.get("system_avatar_upload_id"); + case "gravatar": return this.get("gravatar_avatar_upload_id"); + default: return this.get("custom_avatar_upload_id"); } - }.property( - 'selected', - 'system_avatar_upload_id', - 'gravatar_avatar_upload_id', - 'custom_avatar_upload_id'), + }.property('selected', 'system_avatar_upload_id', 'gravatar_avatar_upload_id', 'custom_avatar_upload_id'), actions: { - useUploadedAvatar: function() { - this.set("selected", "uploaded"); - }, - useGravatar: function() { - this.set("selected", "gravatar"); - }, - useSystem: function() { - this.set("selected", "system"); - }, - refreshGravatar: function(){ + useUploadedAvatar: function() { this.set("selected", "uploaded"); }, + useGravatar: function() { this.set("selected", "gravatar"); }, + useSystem: function() { this.set("selected", "system"); }, + refreshGravatar: function() { var self = this; self.set("gravatarRefreshDisabled", true); Discourse @@ -40,4 +26,5 @@ export default DiscourseController.extend(ModalFunctionality, { }); } } + }); diff --git a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 index 6da73596fa..fc1934a80f 100644 --- a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 +++ b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 @@ -9,6 +9,7 @@ import ObjectController from 'discourse/controllers/object'; @module Discourse **/ export default ObjectController.extend({ + needs: ["application"], actions: { loadMore: function() { @@ -36,14 +37,17 @@ export default ObjectController.extend({ }.property("userBadges"), canLoadMore: function() { - if(this.get('noMoreBadges')) { - return false; - } + if (this.get('noMoreBadges')) { return false; } if (this.get('userBadges')) { return this.get('model.grant_count') > this.get('userBadges.length'); } else { return false; } - }.property('noMoreBadges', 'model.grant_count', 'userBadges.length') + }.property('noMoreBadges', 'model.grant_count', 'userBadges.length'), + + _showFooter: function() { + this.set("controllers.application.showFooter", !this.get("canLoadMore")); + }.observes("canLoadMore") + }); diff --git a/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 b/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 index bde60c8eb5..094960251a 100644 --- a/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 +++ b/app/assets/javascripts/discourse/controllers/bulk-notification-level.js.es6 @@ -1,14 +1,6 @@ -/** - Support for changing the notification level of various topics - - @class BulkNotificationLevelControler - @extends Ember.Controller - @namespace Discourse - @module Discourse -**/ +// Support for changing the notification level of various topics export default Em.Controller.extend({ needs: ['topic-bulk-actions'], - notificationLevelId: null, notificationLevels: function() { diff --git a/app/assets/javascripts/discourse/controllers/change-owner.js.es6 b/app/assets/javascripts/discourse/controllers/change-owner.js.es6 index ab4cc4639d..63f86045e9 100644 --- a/app/assets/javascripts/discourse/controllers/change-owner.js.es6 +++ b/app/assets/javascripts/discourse/controllers/change-owner.js.es6 @@ -1,16 +1,7 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; -/** - Modal related to changing the ownership of posts - - @class ChangeOwnerController - @extends ObjectController - @namespace Discourse - @uses ModalFunctionality - @module Discourse - **/ +// Modal related to changing the ownership of posts export default ObjectController.extend(Discourse.SelectedPostsCount, ModalFunctionality, { needs: ['topic'], diff --git a/app/assets/javascripts/discourse/controllers/composer-messages.js.es6 b/app/assets/javascripts/discourse/controllers/composer-messages.js.es6 index 3b8865416e..f47829e50a 100644 --- a/app/assets/javascripts/discourse/controllers/composer-messages.js.es6 +++ b/app/assets/javascripts/discourse/controllers/composer-messages.js.es6 @@ -1,11 +1,4 @@ -/** - A controller for displaying messages as the user composes a message. - - @class ComposerMessagesController - @extends Ember.ArrayController - @namespace Discourse - @module Discourse -**/ +// A controller for displaying messages as the user composes a message. export default Ember.ArrayController.extend({ needs: ['composer'], diff --git a/app/assets/javascripts/discourse/controllers/create-account.js.es6 b/app/assets/javascripts/discourse/controllers/create-account.js.es6 index e990bcfb64..5e9094aebc 100644 --- a/app/assets/javascripts/discourse/controllers/create-account.js.es6 +++ b/app/assets/javascripts/discourse/controllers/create-account.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/discovery.js.es6 b/app/assets/javascripts/discourse/controllers/discovery.js.es6 index 68ac68f45f..0817323504 100644 --- a/app/assets/javascripts/discourse/controllers/discovery.js.es6 +++ b/app/assets/javascripts/discourse/controllers/discovery.js.es6 @@ -2,7 +2,7 @@ import ObjectController from 'discourse/controllers/object'; import TopPeriod from 'discourse/models/top-period'; export default ObjectController.extend({ - needs: ['navigation/category', 'discovery/topics'], + needs: ['navigation/category', 'discovery/topics', 'application'], loading: false, category: Em.computed.alias('controllers.navigation/category.category'), @@ -10,6 +10,10 @@ export default ObjectController.extend({ loadedAllItems: Em.computed.not("controllers.discovery/topics.canLoadMore"), + _showFooter: function() { + this.set("controllers.application.showFooter", this.get("loadedAllItems")); + }.observes("loadedAllItems"), + showMoreUrl: function(period) { var url = '', category = this.get('category'); if (category) { diff --git a/app/assets/javascripts/discourse/controllers/edit-category.js.es6 b/app/assets/javascripts/discourse/controllers/edit-category.js.es6 index a8161bbf6a..7f0f665495 100644 --- a/app/assets/javascripts/discourse/controllers/edit-category.js.es6 +++ b/app/assets/javascripts/discourse/controllers/edit-category.js.es6 @@ -1,16 +1,7 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; -/** - Modal for editing / creating a category - - @class EditCategoryController - @extends ObjectController - @namespace Discourse - @uses ModalFunctionality - @module Discourse -**/ +// Modal for editing / creating a category export default ObjectController.extend(ModalFunctionality, { foregroundColors: ['FFFFFF', '000000'], categoryUploadUrl: '/category/uploads', diff --git a/app/assets/javascripts/discourse/controllers/edit-topic-auto-close.js.es6 b/app/assets/javascripts/discourse/controllers/edit-topic-auto-close.js.es6 index b9526f4138..9955c2197f 100644 --- a/app/assets/javascripts/discourse/controllers/edit-topic-auto-close.js.es6 +++ b/app/assets/javascripts/discourse/controllers/edit-topic-auto-close.js.es6 @@ -1,15 +1,7 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ObjectController from 'discourse/controllers/object'; -/** - Modal related to auto closing of topics - - @class EditTopicAutoCloseController - @extends ObjectController - @namespace Discourse - @uses ModalFunctionality - @module Discourse -**/ +// Modal related to auto closing of topics export default ObjectController.extend(ModalFunctionality, { auto_close_valid: true, auto_close_invalid: Em.computed.not('auto_close_valid'), diff --git a/app/assets/javascripts/discourse/controllers/exception.js.es6 b/app/assets/javascripts/discourse/controllers/exception.js.es6 index ab60fe0868..ccaf907c14 100644 --- a/app/assets/javascripts/discourse/controllers/exception.js.es6 +++ b/app/assets/javascripts/discourse/controllers/exception.js.es6 @@ -1,6 +1,5 @@ import ObjectController from 'discourse/controllers/object'; - var ButtonBackBright = { classes: "btn-primary", action: "back", @@ -22,14 +21,7 @@ var ButtonBackBright = { key: "errors.buttons.fixed" }; -/** - The controller for the nice error page - - @class ExceptionController - @extends ObjectController - @namespace Discourse - @module Discourse -**/ +// The controller for the nice error page export default ObjectController.extend({ thrown: null, lastTransition: null, diff --git a/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 b/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 index 888f55c3ee..484fd12f66 100644 --- a/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 +++ b/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 @@ -1,13 +1,6 @@ import ObjectController from 'discourse/controllers/object'; -/** - Supports logic for flags in the modal - - @class FlagActionTypeController - @extends ObjectController - @namespace Discourse - @module Discourse -**/ +// Supports logic for flags in the modal export default ObjectController.extend({ needs: ['flag'], diff --git a/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 b/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 index 08e4019573..9003d53dc4 100644 --- a/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 +++ b/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/group.js.es6 b/app/assets/javascripts/discourse/controllers/group.js.es6 index baf2dca201..8355eff56d 100644 --- a/app/assets/javascripts/discourse/controllers/group.js.es6 +++ b/app/assets/javascripts/discourse/controllers/group.js.es6 @@ -1,13 +1,6 @@ import ObjectController from 'discourse/controllers/object'; -/** - The basic controller for a group - - @class GroupController - @extends ObjectController - @namespace Discourse - @module Discourse -**/ +// The basic controller for a group export default ObjectController.extend({ counts: null, diff --git a/app/assets/javascripts/discourse/controllers/history.js.es6 b/app/assets/javascripts/discourse/controllers/history.js.es6 index 344a1fccf8..2a00dc18d5 100644 --- a/app/assets/javascripts/discourse/controllers/history.js.es6 +++ b/app/assets/javascripts/discourse/controllers/history.js.es6 @@ -1,16 +1,7 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; -/** - This controller handles displaying of history - - @class HistoryController - @extends ObjectController - @namespace Discourse - @uses ModalFunctionality - @module Discourse -**/ +// This controller handles displaying of history export default ObjectController.extend(ModalFunctionality, { loading: true, viewMode: "side_by_side", diff --git a/app/assets/javascripts/discourse/controllers/invite-private.js.es6 b/app/assets/javascripts/discourse/controllers/invite-private.js.es6 index fe00c85bfa..94e1d6632d 100644 --- a/app/assets/javascripts/discourse/controllers/invite-private.js.es6 +++ b/app/assets/javascripts/discourse/controllers/invite-private.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; export default ObjectController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/invite.js.es6 b/app/assets/javascripts/discourse/controllers/invite.js.es6 index 09a2fe4e43..965d4128dc 100644 --- a/app/assets/javascripts/discourse/controllers/invite.js.es6 +++ b/app/assets/javascripts/discourse/controllers/invite.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; export default ObjectController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/keyboard-shortcuts-help.js.es6 b/app/assets/javascripts/discourse/controllers/keyboard-shortcuts-help.js.es6 index 20a9823fdc..30d4a00664 100644 --- a/app/assets/javascripts/discourse/controllers/keyboard-shortcuts-help.js.es6 +++ b/app/assets/javascripts/discourse/controllers/keyboard-shortcuts-help.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 b/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 index 7b1b684823..658efcd5fc 100644 --- a/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 @@ -1,16 +1,7 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; -/** - Modal related to merging of topics - - @class MergeTopicController - @extends ObjectController - @namespace Discourse - @uses ModalFunctionality - @module Discourse -**/ +// Modal related to merging of topics export default ObjectController.extend(Discourse.SelectedPostsCount, ModalFunctionality, { needs: ['topic'], diff --git a/app/assets/javascripts/discourse/controllers/not-activated.js.es6 b/app/assets/javascripts/discourse/controllers/not-activated.js.es6 index 40fdf41b0d..3ce160c880 100644 --- a/app/assets/javascripts/discourse/controllers/not-activated.js.es6 +++ b/app/assets/javascripts/discourse/controllers/not-activated.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/raw-email.js.es6 b/app/assets/javascripts/discourse/controllers/raw-email.js.es6 index 2cff6cf2a2..8c74ce1110 100644 --- a/app/assets/javascripts/discourse/controllers/raw-email.js.es6 +++ b/app/assets/javascripts/discourse/controllers/raw-email.js.es6 @@ -1,16 +1,7 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; -/** - This controller handles displaying of raw email - - @class RawEmailController - @extends ObjectController - @namespace Discourse - @uses ModalFunctionality - @module Discourse -**/ +// This controller handles displaying of raw email export default ObjectController.extend(ModalFunctionality, { rawEmail: "", diff --git a/app/assets/javascripts/discourse/controllers/search-help.js.es6 b/app/assets/javascripts/discourse/controllers/search-help.js.es6 index c62a9db6c0..2630424ff4 100644 --- a/app/assets/javascripts/discourse/controllers/search-help.js.es6 +++ b/app/assets/javascripts/discourse/controllers/search-help.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/split-topic.js.es6 b/app/assets/javascripts/discourse/controllers/split-topic.js.es6 index 38c2ba38e2..7ef7702000 100644 --- a/app/assets/javascripts/discourse/controllers/split-topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/split-topic.js.es6 @@ -1,16 +1,7 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import ObjectController from 'discourse/controllers/object'; -/** - Modal related to auto closing of topics - - @class SplitTopicController - @extends ObjectController - @namespace Discourse - @uses ModalFunctionality - @module Discourse -**/ +// Modal related to auto closing of topics export default ObjectController.extend(Discourse.SelectedPostsCount, ModalFunctionality, { needs: ['topic'], diff --git a/app/assets/javascripts/discourse/controllers/topic-admin-menu.js.es6 b/app/assets/javascripts/discourse/controllers/topic-admin-menu.js.es6 index 8f77b08a39..e4ab7a8c30 100644 --- a/app/assets/javascripts/discourse/controllers/topic-admin-menu.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic-admin-menu.js.es6 @@ -1,13 +1,6 @@ import ObjectController from 'discourse/controllers/object'; -/** - This controller supports the admin menu on topics - - @class TopicAdminMenuController - @extends ObjectController - @namespace Discourse - @module Discourse -**/ +// This controller supports the admin menu on topics export default ObjectController.extend({ menuVisible: false, showRecover: Em.computed.and('deleted', 'details.can_recover'), diff --git a/app/assets/javascripts/discourse/controllers/topic-bulk-actions.js.es6 b/app/assets/javascripts/discourse/controllers/topic-bulk-actions.js.es6 index 06e4276bac..4c0ddcfdeb 100644 --- a/app/assets/javascripts/discourse/controllers/topic-bulk-actions.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic-bulk-actions.js.es6 @@ -1,14 +1,6 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; -/** - Modal for performing bulk actions on topics - - @class TopicBulkActionsController - @extends Ember.ArrayController - @namespace Discourse - @uses ModalFunctionality - @module Discourse -**/ +// Modal for performing bulk actions on topics export default Ember.ArrayController.extend(ModalFunctionality, { needs: ['discovery/topics'], diff --git a/app/assets/javascripts/discourse/controllers/topic-list-item.js.es6 b/app/assets/javascripts/discourse/controllers/topic-list-item.js.es6 index 47c9d999f2..e5128e2744 100644 --- a/app/assets/javascripts/discourse/controllers/topic-list-item.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic-list-item.js.es6 @@ -1,13 +1,6 @@ import ObjectController from 'discourse/controllers/object'; -/** - Handles displaying of a topic as a list item - - @class TopicListItemController - @extends ObjectController - @namespace Discourse - @module Discourse -**/ +// Handles displaying of a topic as a list item export default Ember.ObjectController.extend({ needs: ['discovery/topics'], diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index c3f1342b32..fac35fa4d9 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -3,7 +3,7 @@ import { spinnerHTML } from 'discourse/helpers/loading-spinner'; export default ObjectController.extend(Discourse.SelectedPostsCount, { multiSelect: false, - needs: ['header', 'modal', 'composer', 'quote-button', 'search', 'topic-progress'], + needs: ['header', 'modal', 'composer', 'quote-button', 'search', 'topic-progress', 'application'], allPostsSelected: false, editingTopic: false, selectedPosts: null, @@ -697,6 +697,10 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, { if (lastLoadedPost && lastLoadedPost === post) { postStream.appendMore(); } - } + }, + + _showFooter: function() { + this.set("controllers.application.showFooter", this.get("postStream.loadedAllPosts")); + }.observes("postStream.loadedAllPosts") }); diff --git a/app/assets/javascripts/discourse/controllers/upload-selector.js.es6 b/app/assets/javascripts/discourse/controllers/upload-selector.js.es6 index d762ebf7ca..edd2d56943 100644 --- a/app/assets/javascripts/discourse/controllers/upload-selector.js.es6 +++ b/app/assets/javascripts/discourse/controllers/upload-selector.js.es6 @@ -1,5 +1,4 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; - import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend(ModalFunctionality, { diff --git a/app/assets/javascripts/discourse/controllers/user-activity.js.es6 b/app/assets/javascripts/discourse/controllers/user-activity.js.es6 index 5ca4a2c2ca..878fa3fde4 100644 --- a/app/assets/javascripts/discourse/controllers/user-activity.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-activity.js.es6 @@ -1 +1,15 @@ -export default Ember.ObjectController.extend(); +export default Ember.ObjectController.extend({ + needs: ["application"], + + _showFooter: function() { + var showFooter; + if (this.get("userActionType")) { + var stat = _.find(this.get("stats"), { action_type: this.get("userActionType") }); + showFooter = stat && stat.count <= this.get("stream.itemsLoaded"); + } else { + showFooter = this.get("statsCountNonPM") <= this.get("stream.itemsLoaded"); + } + this.set("controllers.application.showFooter", showFooter); + }.observes("userActionType", "stream.itemsLoaded") + +}); diff --git a/app/assets/javascripts/discourse/controllers/user-invited.js.es6 b/app/assets/javascripts/discourse/controllers/user-invited.js.es6 index 3274963eac..4d5fea2f4e 100644 --- a/app/assets/javascripts/discourse/controllers/user-invited.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-invited.js.es6 @@ -1,11 +1,4 @@ -/** - This controller handles actions related to a user's invitations - - @class UserInvitedController - @extends Ember.ArrayController - @namespace Discourse - @module Discourse -**/ +// This controller handles actions related to a user's invitations export default Ember.ObjectController.extend({ user: null, model: null, diff --git a/app/assets/javascripts/discourse/controllers/user-notifications.js.es6 b/app/assets/javascripts/discourse/controllers/user-notifications.js.es6 index e2519e2e5c..3943daa868 100644 --- a/app/assets/javascripts/discourse/controllers/user-notifications.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-notifications.js.es6 @@ -1,9 +1,12 @@ export default Ember.ArrayController.extend({ - needs: ['user-notifications'], - canLoadMore: true, + needs: ['user-notifications', 'application'], loading: false, + _showFooter: function() { + this.set("controllers.application.showFooter", !this.get("canLoadMore")); + }.observes("canLoadMore"), + showDismissButton: function() { return this.get('user').total_unread_notifications > 0; }.property('user'), diff --git a/app/assets/javascripts/discourse/controllers/user-posts.js.es6 b/app/assets/javascripts/discourse/controllers/user-posts.js.es6 new file mode 100644 index 0000000000..eacec20213 --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/user-posts.js.es6 @@ -0,0 +1,7 @@ +export default Ember.ObjectController.extend({ + needs: ["application"], + + _showFooter: function() { + this.set("controllers.application.showFooter", !this.get("canLoadMore")) + }.observes("canLoadMore") +}); diff --git a/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 b/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 index 8e81c0f220..c39d34df95 100644 --- a/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 @@ -1,17 +1,15 @@ import ObjectController from 'discourse/controllers/object'; -/** - Lists of topics on a user's page. - - @class UserTopicsListController - @extends ObjectController - @namespace Discourse - @module Discourse -**/ +// Lists of topics on a user's page. export default ObjectController.extend({ + needs: ["application"], hideCategory: false, showParticipants: false, + _showFooter: function() { + this.set("controllers.application.showFooter", !this.get("canLoadMore")); + }.observes("canLoadMore"), + actions: { loadMore: function() { this.get('model').loadMore(); diff --git a/app/assets/javascripts/discourse/controllers/user.js.es6 b/app/assets/javascripts/discourse/controllers/user.js.es6 index c88a24264b..5fb5b5bb84 100644 --- a/app/assets/javascripts/discourse/controllers/user.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user.js.es6 @@ -2,7 +2,7 @@ import ObjectController from 'discourse/controllers/object'; import CanCheckEmails from 'discourse/mixins/can-check-emails'; export default ObjectController.extend(CanCheckEmails, { - indexStream: true, + indexStream: false, needs: ['user-notifications', 'user_topics_list'], viewingSelf: function() { @@ -42,27 +42,6 @@ export default ObjectController.extend(CanCheckEmails, { return this.get('can_be_deleted') && this.get('can_delete_all_posts'); }.property('can_be_deleted', 'can_delete_all_posts'), - loadedAllItems: function() { - switch (this.get("datasource")) { - case "badges": { return true; } - case "notifications": { return !this.get("controllers.user-notifications.canLoadMore"); } - case "topic_list": { return !this.get("controllers.user_topics_list.canLoadMore"); } - case "stream": { - if (this.get("userActionType")) { - var stat = _.find(this.get("stats"), { action_type: this.get("userActionType") }); - return stat && stat.count <= this.get("stream.itemsLoaded"); - } else { - return this.get("statsCountNonPM") <= this.get("stream.itemsLoaded"); - } - } - } - - return false; - }.property("datasource", - "userActionType", "stats", "stream.itemsLoaded", - "controllers.user_topics_list.canLoadMore", - "controllers.user-notifications.canLoadMore"), - privateMessagesActive: Em.computed.equal('pmView', 'index'), privateMessagesMineActive: Em.computed.equal('pmView', 'mine'), privateMessagesUnreadActive: Em.computed.equal('pmView', 'unread'), diff --git a/app/assets/javascripts/discourse/initializers/dynamic-route-builders.js.es6 b/app/assets/javascripts/discourse/initializers/dynamic-route-builders.js.es6 index f5f22866ba..a2222aa3f9 100644 --- a/app/assets/javascripts/discourse/initializers/dynamic-route-builders.js.es6 +++ b/app/assets/javascripts/discourse/initializers/dynamic-route-builders.js.es6 @@ -22,8 +22,10 @@ export default { Discourse.DiscoveryTopRoute = buildTopicRoute('top', { actions: { willTransition: function() { + this._super(); Discourse.User.currentProp("should_be_redirected_to_top", false); Discourse.User.currentProp("redirected_to_top_reason", null); + return true; } } }); diff --git a/app/assets/javascripts/discourse/lib/static-route-builder.js.es6 b/app/assets/javascripts/discourse/lib/static-route-builder.js.es6 index a1140f89a3..79365f4655 100644 --- a/app/assets/javascripts/discourse/lib/static-route-builder.js.es6 +++ b/app/assets/javascripts/discourse/lib/static-route-builder.js.es6 @@ -1,3 +1,5 @@ +import ShowFooter from "discourse/mixins/show-footer"; + var configs = { 'faq': 'faq_url', 'tos': 'tos_url', @@ -5,7 +7,7 @@ var configs = { }; export default function(page) { - return Discourse.Route.extend({ + return Discourse.Route.extend(ShowFooter, { renderTemplate: function() { this.render('static'); }, diff --git a/app/assets/javascripts/discourse/mixins/load_more.js b/app/assets/javascripts/discourse/mixins/load-more.js.es6 similarity index 62% rename from app/assets/javascripts/discourse/mixins/load_more.js rename to app/assets/javascripts/discourse/mixins/load-more.js.es6 index 1f6ebab29a..136c5aeca4 100644 --- a/app/assets/javascripts/discourse/mixins/load_more.js +++ b/app/assets/javascripts/discourse/mixins/load-more.js.es6 @@ -1,14 +1,8 @@ /** - This mixin provides the ability to load more items for a view which is - scrolled to the bottom. - - @class Discourse.LoadMore - @extends Ember.Mixin - @uses Discourse.Scrolling - @namespace Discourse - @module Discourse + Provides the ability to load more items for a view which is scrolled to the bottom. **/ -Discourse.LoadMore = Em.Mixin.create(Ember.ViewTargetActionSupport, Discourse.Scrolling, { + +export default Em.Mixin.create(Ember.ViewTargetActionSupport, Discourse.Scrolling, { scrolled: function() { var eyeline = this.get('eyeline'); diff --git a/app/assets/javascripts/discourse/mixins/show-footer.js.es6 b/app/assets/javascripts/discourse/mixins/show-footer.js.es6 new file mode 100644 index 0000000000..8ae74e967e --- /dev/null +++ b/app/assets/javascripts/discourse/mixins/show-footer.js.es6 @@ -0,0 +1,16 @@ +export default Em.Mixin.create({ + actions: { + didTransition: function() { + var self = this; + Em.run.schedule("afterRender", function() { + self.controllerFor("application").set("showFooter", true); + }); + return true; + }, + + willTransition: function() { + this.controllerFor("application").set("showFooter", false); + return true; + } + } +}) diff --git a/app/assets/javascripts/discourse/models/user_posts_stream.js b/app/assets/javascripts/discourse/models/user_posts_stream.js index 7447cbe63a..b155464566 100644 --- a/app/assets/javascripts/discourse/models/user_posts_stream.js +++ b/app/assets/javascripts/discourse/models/user_posts_stream.js @@ -12,6 +12,7 @@ Discourse.UserPostsStream = Discourse.Model.extend({ _initialize: function () { this.setProperties({ itemsLoaded: 0, + canLoadMore: true, content: [] }); }.on("init"), @@ -24,6 +25,7 @@ Discourse.UserPostsStream = Discourse.Model.extend({ this.setProperties({ filter: filter, itemsLoaded: 0, + canLoadMore: true, content: [] }); @@ -32,7 +34,7 @@ Discourse.UserPostsStream = Discourse.Model.extend({ findItems: function () { var self = this; - if (this.get("loading")) { return Ember.RSVP.reject(); } + if (this.get("loading") || !this.get("canLoadMore")) { return Ember.RSVP.reject(); } this.set("loading", true); @@ -42,7 +44,8 @@ Discourse.UserPostsStream = Discourse.Model.extend({ self.get("content").pushObjects(posts); self.setProperties({ loaded: true, - itemsLoaded: self.get("itemsLoaded") + posts.length + itemsLoaded: self.get("itemsLoaded") + posts.length, + canLoadMore: posts.length === 0 || posts.length < 60 }); } }).finally(function () { diff --git a/app/assets/javascripts/discourse/routes/about.js.es6 b/app/assets/javascripts/discourse/routes/about.js.es6 index 3021dd0835..5258ea7fbd 100644 --- a/app/assets/javascripts/discourse/routes/about.js.es6 +++ b/app/assets/javascripts/discourse/routes/about.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { model: function() { return Discourse.ajax("/about.json").then(function(result) { return result.about; diff --git a/app/assets/javascripts/discourse/routes/application_routes.js b/app/assets/javascripts/discourse/routes/application_routes.js index 79edca418b..a7d0200d63 100644 --- a/app/assets/javascripts/discourse/routes/application_routes.js +++ b/app/assets/javascripts/discourse/routes/application_routes.js @@ -51,7 +51,6 @@ Discourse.Route.buildRoutes(function() { this.route('categoryNone', { path: '/c/:slug/none' }); this.route('category', { path: '/c/:parentSlug/:slug' }); - // homepage this.route(Discourse.Utilities.defaultHomepage(), { path: '/' }); }); diff --git a/app/assets/javascripts/discourse/routes/badges-index.js.es6 b/app/assets/javascripts/discourse/routes/badges-index.js.es6 index fb7e990565..67af4625f2 100644 --- a/app/assets/javascripts/discourse/routes/badges-index.js.es6 +++ b/app/assets/javascripts/discourse/routes/badges-index.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { model: function() { if (PreloadStore.get('badges')) { return PreloadStore.getAndRemove('badges').then(function(json) { diff --git a/app/assets/javascripts/discourse/routes/badges-show.js.es6 b/app/assets/javascripts/discourse/routes/badges-show.js.es6 index 5ee4fa72d8..7b897f03bf 100644 --- a/app/assets/javascripts/discourse/routes/badges-show.js.es6 +++ b/app/assets/javascripts/discourse/routes/badges-show.js.es6 @@ -1,4 +1,13 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { + actions: { + didTransition: function() { + this.controllerFor("badges/show")._showFooter(); + return true; + } + }, + serialize: function(model) { return {id: model.get('id'), slug: model.get('name').replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase()}; }, diff --git a/app/assets/javascripts/discourse/routes/build-admin-user-posts-route.js.es6 b/app/assets/javascripts/discourse/routes/build-admin-user-posts-route.js.es6 new file mode 100644 index 0000000000..6d79b3b487 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/build-admin-user-posts-route.js.es6 @@ -0,0 +1,32 @@ +import ShowFooter from "discourse/mixins/show-footer"; + +export default function (filter) { + return Discourse.Route.extend(ShowFooter, { + actions: { + didTransition: function() { + this.controllerFor('user').set('indexStream', true); + this.controllerFor("user-posts")._showFooter(); + return true; + } + }, + + model: function () { + return this.modelFor("user").get("postsStream"); + }, + + afterModel: function () { + return this.modelFor("user").get("postsStream").filterBy(filter); + }, + + setupController: function(controller, model) { + // initialize "canLoadMore" + model.set("canLoadMore", model.get("itemsLoaded") === 60); + + this.controllerFor("user-posts").set("model", model); + }, + + renderTemplate: function() { + this.render("user/posts", { into: "user" }); + } + }); +} diff --git a/app/assets/javascripts/discourse/routes/build-category-route.js.es6 b/app/assets/javascripts/discourse/routes/build-category-route.js.es6 index ec736d5e27..643a0063eb 100644 --- a/app/assets/javascripts/discourse/routes/build-category-route.js.es6 +++ b/app/assets/javascripts/discourse/routes/build-category-route.js.es6 @@ -1,6 +1,6 @@ -// A helper function to create a category route with parameters import { queryParams, filterQueryParams } from 'discourse/routes/build-topic-route'; +// A helper function to create a category route with parameters export default function(filter, params) { return Discourse.Route.extend({ queryParams: queryParams, diff --git a/app/assets/javascripts/discourse/routes/build-topic-route.js.es6 b/app/assets/javascripts/discourse/routes/build-topic-route.js.es6 index f00bd5d177..dcfd77143e 100644 --- a/app/assets/javascripts/discourse/routes/build-topic-route.js.es6 +++ b/app/assets/javascripts/discourse/routes/build-topic-route.js.es6 @@ -1,7 +1,6 @@ -// A helper to build a topic route for a filter - import { queryParams } from 'discourse/controllers/discovery-sortable'; +// A helper to build a topic route for a filter export function filterQueryParams(params, defaultParams) { var findOpts = defaultParams || {}; if (params) { diff --git a/app/assets/javascripts/discourse/routes/build-user-topic-list-route.js.es6 b/app/assets/javascripts/discourse/routes/build-user-topic-list-route.js.es6 new file mode 100644 index 0000000000..a65533f9e0 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/build-user-topic-list-route.js.es6 @@ -0,0 +1,31 @@ +import UserTopicListRoute from "discourse/routes/user-topic-list"; +import ShowFooter from "discourse/mixins/show-footer"; + +// A helper to build a user topic list route +export default function (viewName, path) { + return UserTopicListRoute.extend(ShowFooter, { + userActionType: Discourse.UserAction.TYPES.messages_received, + + actions: { + didTransition: function() { + this.controllerFor("user-topics-list")._showFooter(); + return true; + } + }, + + model: function() { + return Discourse.TopicList.find('topics/' + path + '/' + this.modelFor('user').get('username_lower')); + }, + + setupController: function() { + this._super.apply(this, arguments); + + this.controllerFor('user_topics_list').setProperties({ + hideCategory: true, + showParticipants: true + }); + + this.controllerFor('user').set("pmView", viewName); + } + }); +} diff --git a/app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js b/app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js deleted file mode 100644 index d489e6f5d0..0000000000 --- a/app/assets/javascripts/discourse/routes/discourse_restricted_user_route.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - A base route that allows us to redirect when access is restricted - - @class RestrictedUserRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.RestrictedUserRoute = Discourse.Route.extend({ - - afterModel: function() { - var user = this.modelFor('user'); - if (!user.get('can_edit')) { - this.replaceWith('userActivity'); - } - } - -}); - - diff --git a/app/assets/javascripts/discourse/routes/discovery_categories_route.js b/app/assets/javascripts/discourse/routes/discovery-categories-route.js.es6 similarity index 93% rename from app/assets/javascripts/discourse/routes/discovery_categories_route.js rename to app/assets/javascripts/discourse/routes/discovery-categories-route.js.es6 index 13f066865b..31278dfca5 100644 --- a/app/assets/javascripts/discourse/routes/discovery_categories_route.js +++ b/app/assets/javascripts/discourse/routes/discovery-categories-route.js.es6 @@ -1,4 +1,6 @@ -Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenComposer, { +import ShowFooter from "discourse/mixins/show-footer"; + +Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenComposer, ShowFooter, { renderTemplate: function() { this.render('navigation/categories', { outlet: 'navigation-bar' }); this.render('discovery/categories', { outlet: 'list-container' }); @@ -58,3 +60,5 @@ Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenCompos } } }); + +export default Discourse.DiscoveryCategoriesRoute; diff --git a/app/assets/javascripts/discourse/routes/discovery_route.js b/app/assets/javascripts/discourse/routes/discovery-route.js.es6 similarity index 74% rename from app/assets/javascripts/discourse/routes/discovery_route.js rename to app/assets/javascripts/discourse/routes/discovery-route.js.es6 index a4db51aa2f..f4e3a0a686 100644 --- a/app/assets/javascripts/discourse/routes/discovery_route.js +++ b/app/assets/javascripts/discourse/routes/discovery-route.js.es6 @@ -1,13 +1,11 @@ /** - The parent route for all discovery routes. Handles the logic for showing - the loading spinners. - - @class DiscoveryRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse + The parent route for all discovery routes. + Handles the logic for showing the loading spinners. **/ -Discourse.DiscoveryRoute = Discourse.Route.extend(Discourse.ScrollTop, Discourse.OpenComposer, { + +import ShowFooter from "discourse/mixins/show-footer"; + +Discourse.DiscoveryRoute = Discourse.Route.extend(Discourse.ScrollTop, Discourse.OpenComposer, ShowFooter, { redirect: function() { return this.redirectIfLoginRequired(); }, beforeModel: function(transition) { @@ -20,24 +18,21 @@ Discourse.DiscoveryRoute = Discourse.Route.extend(Discourse.ScrollTop, Discourse actions: { loading: function() { - var controller = this.controllerFor('discovery'); - - // If we're already loading don't do anything - if (controller.get('loading')) { return; } - controller.set('loading', true); + this.controllerFor('discovery').set("loading", true); return true; }, loadingComplete: function() { - var controller = this.controllerFor('discovery'); - controller.set('loading', false); + this.controllerFor('discovery').set('loading', false); if (!Discourse.Session.currentProp('topicListScrollPosition')) { this._scrollTop(); } }, didTransition: function() { + this.controllerFor("discovery")._showFooter(); this.send('loadingComplete'); + return true; }, // clear a pinned topic @@ -65,3 +60,4 @@ Discourse.DiscoveryRoute = Discourse.Route.extend(Discourse.ScrollTop, Discourse }); +export default Discourse.DiscoveryRoute; diff --git a/app/assets/javascripts/discourse/routes/exception.js.es6 b/app/assets/javascripts/discourse/routes/exception.js.es6 index 26152e058b..a5c5fab878 100644 --- a/app/assets/javascripts/discourse/routes/exception.js.es6 +++ b/app/assets/javascripts/discourse/routes/exception.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { serialize: function() { return ""; } diff --git a/app/assets/javascripts/discourse/routes/group-index.js.es6 b/app/assets/javascripts/discourse/routes/group-index.js.es6 index a3a19968c1..63a1a23f76 100644 --- a/app/assets/javascripts/discourse/routes/group-index.js.es6 +++ b/app/assets/javascripts/discourse/routes/group-index.js.es6 @@ -1,4 +1,12 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { + actions: { + didTransition: function() { + return true; + } + }, + model: function() { return this.modelFor('group').findPosts(); }, diff --git a/app/assets/javascripts/discourse/routes/group-members.js.es6 b/app/assets/javascripts/discourse/routes/group-members.js.es6 index 93d1274012..b95fdfd88b 100644 --- a/app/assets/javascripts/discourse/routes/group-members.js.es6 +++ b/app/assets/javascripts/discourse/routes/group-members.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { model: function() { return this.modelFor('group'); }, diff --git a/app/assets/javascripts/discourse/routes/preferences-about.js.es6 b/app/assets/javascripts/discourse/routes/preferences-about.js.es6 index b01c1941e6..4293cd5f9f 100644 --- a/app/assets/javascripts/discourse/routes/preferences-about.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-about.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.RestrictedUserRoute.extend({ +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend({ model: function() { return this.modelFor('user'); }, @@ -35,4 +37,3 @@ export default Discourse.RestrictedUserRoute.extend({ } }); - diff --git a/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6 b/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6 index 382c61a0f4..0c7a22d8b4 100644 --- a/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.RestrictedUserRoute.extend({ +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend({ model: function() { return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username')); }, diff --git a/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6 b/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6 index e75a05092d..37ad604ec0 100644 --- a/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.RestrictedUserRoute.extend({ +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend({ model: function() { return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username')); }, diff --git a/app/assets/javascripts/discourse/routes/preferences-email.js.es6 b/app/assets/javascripts/discourse/routes/preferences-email.js.es6 index 4c17b9b288..b9c02c3a85 100644 --- a/app/assets/javascripts/discourse/routes/preferences-email.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-email.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.RestrictedUserRoute.extend({ +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend({ model: function() { return this.modelFor('user'); }, diff --git a/app/assets/javascripts/discourse/routes/preferences-index.js.es6 b/app/assets/javascripts/discourse/routes/preferences-index.js.es6 index 2d3a66111d..bb568ff672 100644 --- a/app/assets/javascripts/discourse/routes/preferences-index.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-index.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.RestrictedUserRoute.extend({ +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend({ renderTemplate: function() { this.render('preferences', { into: 'user', controller: 'preferences' }); } diff --git a/app/assets/javascripts/discourse/routes/preferences-username.js.es6 b/app/assets/javascripts/discourse/routes/preferences-username.js.es6 index 2a9bbe0fbb..09fbc0ec6b 100644 --- a/app/assets/javascripts/discourse/routes/preferences-username.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-username.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.RestrictedUserRoute.extend({ +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend({ model: function() { return this.modelFor('user'); }, diff --git a/app/assets/javascripts/discourse/routes/preferences.js.es6 b/app/assets/javascripts/discourse/routes/preferences.js.es6 index 40d879b2a0..6ad9efb529 100644 --- a/app/assets/javascripts/discourse/routes/preferences.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences.js.es6 @@ -1,16 +1,19 @@ -export default Discourse.RestrictedUserRoute.extend({ +import ShowFooter from "discourse/mixins/show-footer"; +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend(ShowFooter, { model: function() { return this.modelFor('user'); }, setupController: function(controller, user) { controller.setProperties({ model: user, newNameInput: user.get('name') }); - this.controllerFor('user').set('indexStream', false); }, actions: { showAvatarSelector: function() { Discourse.Route.showModal(this, 'avatar-selector'); + // all the properties needed for displaying the avatar selector modal var controller = this.controllerFor('avatar-selector'); var user = this.modelFor('user'); @@ -20,17 +23,17 @@ export default Discourse.RestrictedUserRoute.extend({ 'system_avatar_upload_id', 'gravatar_avatar_upload_id', 'custom_avatar_upload_id' - ); + ); - switch(props.uploaded_avatar_id){ - case props.system_avatar_upload_id: - props.selected = "system"; - break; - case props.gravatar_avatar_upload_id: - props.selected = "gravatar"; - break; - default: - props.selected = "uploaded"; + switch (props.uploaded_avatar_id) { + case props.system_avatar_upload_id: + props.selected = "system"; + break; + case props.gravatar_avatar_upload_id: + props.selected = "gravatar"; + break; + default: + props.selected = "uploaded"; } controller.setProperties(props); @@ -40,7 +43,6 @@ export default Discourse.RestrictedUserRoute.extend({ var user = this.modelFor('user'); var avatarSelector = this.controllerFor('avatar-selector'); - // sends the information to the server if it has changed if (avatarSelector.get('selectedUploadId') !== user.get('uploaded_avatar_id')) { user.pickAvatar(avatarSelector.get('selectedUploadId')); diff --git a/app/assets/javascripts/discourse/routes/restricted-user.js.es6 b/app/assets/javascripts/discourse/routes/restricted-user.js.es6 new file mode 100644 index 0000000000..ad9cb411ef --- /dev/null +++ b/app/assets/javascripts/discourse/routes/restricted-user.js.es6 @@ -0,0 +1,12 @@ +// A base route that allows us to redirect when access is restricted + +export default Discourse.Route.extend({ + + afterModel: function() { + var user = this.modelFor('user'); + if (!user.get('can_edit')) { + this.replaceWith('userActivity'); + } + } + +}); diff --git a/app/assets/javascripts/discourse/routes/topic-from-params-near.js.es6 b/app/assets/javascripts/discourse/routes/topic-from-params-near.js.es6 new file mode 100644 index 0000000000..8a09c86d36 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/topic-from-params-near.js.es6 @@ -0,0 +1,3 @@ +import TopicFromParamsRoute from "discourse/routes/topic-from-params"; + +export default TopicFromParamsRoute; diff --git a/app/assets/javascripts/discourse/routes/topic_from_params_route.js b/app/assets/javascripts/discourse/routes/topic-from-params.js.es6 similarity index 84% rename from app/assets/javascripts/discourse/routes/topic_from_params_route.js rename to app/assets/javascripts/discourse/routes/topic-from-params.js.es6 index bc9e62f052..0f648039ba 100644 --- a/app/assets/javascripts/discourse/routes/topic_from_params_route.js +++ b/app/assets/javascripts/discourse/routes/topic-from-params.js.es6 @@ -1,12 +1,6 @@ -/** - This route is used for retrieving a topic based on params +// This route is used for retrieving a topic based on params - @class TopicFromParamsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.TopicFromParamsRoute = Discourse.Route.extend({ +export default Discourse.Route.extend({ setupController: function(controller, params) { params = params || {}; @@ -56,6 +50,3 @@ Discourse.TopicFromParamsRoute = Discourse.Route.extend({ } }); - -Discourse.TopicFromParamsNearRoute = Discourse.TopicFromParamsRoute; - diff --git a/app/assets/javascripts/discourse/routes/topic_route.js b/app/assets/javascripts/discourse/routes/topic-route.js.es6 similarity index 96% rename from app/assets/javascripts/discourse/routes/topic_route.js rename to app/assets/javascripts/discourse/routes/topic-route.js.es6 index 6f631e6d51..ba13ca1372 100644 --- a/app/assets/javascripts/discourse/routes/topic_route.js +++ b/app/assets/javascripts/discourse/routes/topic-route.js.es6 @@ -3,7 +3,9 @@ var isTransitioning = false, lastScrollPos = null, SCROLL_DELAY = 500; -Discourse.TopicRoute = Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +Discourse.TopicRoute = Discourse.Route.extend(ShowFooter, { redirect: function() { return this.redirectIfLoginRequired(); }, queryParams: { @@ -108,7 +110,13 @@ Discourse.TopicRoute = Discourse.Route.extend({ } }, + didTransition: function() { + this.controllerFor("topic")._showFooter(); + return true; + }, + willTransition: function() { + this._super(); this.controllerFor("quote-button").deselectText(); Em.run.cancel(scheduledReplace); isTransitioning = true; @@ -228,3 +236,4 @@ Discourse.TopicRoute = Discourse.Route.extend({ }); RSVP.EventTarget.mixin(Discourse.TopicRoute); +export default Discourse.TopicRoute; diff --git a/app/assets/javascripts/discourse/routes/unknown.js.es6 b/app/assets/javascripts/discourse/routes/unknown.js.es6 new file mode 100644 index 0000000000..db0cde0c52 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/unknown.js.es6 @@ -0,0 +1,5 @@ +export default Discourse.Route.extend({ + model: function() { + return Discourse.ajax("/404-body", { dataType: 'html' }); + } +}); diff --git a/app/assets/javascripts/discourse/routes/unknown_route.js b/app/assets/javascripts/discourse/routes/unknown_route.js deleted file mode 100644 index 334ce02742..0000000000 --- a/app/assets/javascripts/discourse/routes/unknown_route.js +++ /dev/null @@ -1,5 +0,0 @@ -Discourse.UnknownRoute = Discourse.Route.extend({ - model: function() { - return Discourse.ajax("/404-body", {dataType: 'html'}); - } -}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-bookmarks.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-bookmarks.js.es6 new file mode 100644 index 0000000000..ad2b2bd986 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-bookmarks.js.es6 @@ -0,0 +1,5 @@ +import UserActivityStreamRoute from "discourse/routes/user-activity-stream"; + +export default UserActivityStreamRoute.extend({ + userActionType: Discourse.UserAction.TYPES["bookmarks"] +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-edits.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-edits.js.es6 new file mode 100644 index 0000000000..a3192d2a1d --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-edits.js.es6 @@ -0,0 +1,5 @@ +import UserActivityStreamRoute from "discourse/routes/user-activity-stream"; + +export default UserActivityStreamRoute.extend({ + userActionType: Discourse.UserAction.TYPES["edits"] +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-index.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-index.js.es6 new file mode 100644 index 0000000000..3f5ae1272a --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-index.js.es6 @@ -0,0 +1,14 @@ +import UserActivityStreamRoute from "discourse/routes/user-activity-stream"; + +export default UserActivityStreamRoute.extend({ + userActionType: undefined, + + actions: { + didTransition: function() { + this._super(); + this.controllerFor('user').set('indexStream', true); + return true; + } + } + +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-likes-given.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-likes-given.js.es6 new file mode 100644 index 0000000000..84ed7b4501 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-likes-given.js.es6 @@ -0,0 +1,5 @@ +import UserActivityStreamRoute from "discourse/routes/user-activity-stream"; + +export default UserActivityStreamRoute.extend({ + userActionType: Discourse.UserAction.TYPES["likes_given"] +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-likes-received.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-likes-received.js.es6 new file mode 100644 index 0000000000..3e7e5cdc4c --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-likes-received.js.es6 @@ -0,0 +1,5 @@ +import UserActivityStreamRoute from "discourse/routes/user-activity-stream"; + +export default UserActivityStreamRoute.extend({ + userActionType: Discourse.UserAction.TYPES["likes_received"] +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-posts.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-posts.js.es6 new file mode 100644 index 0000000000..0be8704abf --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-posts.js.es6 @@ -0,0 +1,5 @@ +import UserActivityStreamRoute from "discourse/routes/user-activity-stream"; + +export default UserActivityStreamRoute.extend({ + userActionType: Discourse.UserAction.TYPES["posts"] +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-replies.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-replies.js.es6 new file mode 100644 index 0000000000..29fd25a2a2 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-replies.js.es6 @@ -0,0 +1,5 @@ +import UserActivityStreamRoute from "discourse/routes/user-activity-stream"; + +export default UserActivityStreamRoute.extend({ + userActionType: Discourse.UserAction.TYPES["replies"] +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-starred.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-starred.js.es6 new file mode 100644 index 0000000000..2e74e1ddcb --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-starred.js.es6 @@ -0,0 +1,9 @@ +import UserTopicListRoute from "discourse/routes/user-topic-list"; + +export default UserTopicListRoute.extend({ + userActionType: Discourse.UserAction.TYPES.starred, + + model: function() { + return Discourse.TopicList.find('starred', { user_id: this.modelFor('user').get('id') }); + } +}); diff --git a/app/assets/javascripts/discourse/routes/user_activity_stream_route.js b/app/assets/javascripts/discourse/routes/user-activity-stream.js.es6 similarity index 64% rename from app/assets/javascripts/discourse/routes/user_activity_stream_route.js rename to app/assets/javascripts/discourse/routes/user-activity-stream.js.es6 index 62e8228b89..712be34bda 100644 --- a/app/assets/javascripts/discourse/routes/user_activity_stream_route.js +++ b/app/assets/javascripts/discourse/routes/user-activity-stream.js.es6 @@ -1,4 +1,6 @@ -var UserActivityStreamRoute = Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { model: function() { return this.modelFor('user').get('stream'); }, @@ -13,16 +15,16 @@ var UserActivityStreamRoute = Discourse.Route.extend({ setupController: function(controller, model) { controller.set('model', model); - this.controllerFor('user_activity').set('userActionType', this.get('userActionType')); - - this.controllerFor('user').setProperties({ - indexStream: !this.get('userActionType'), - datasource: "stream" - }); + this.controllerFor('user-activity').set('userActionType', this.get('userActionType')); }, actions: { + didTransition: function() { + this.controllerFor("user-activity")._showFooter(); + return true; + }, + removeBookmark: function(userAction) { var self = this; Discourse.Post.bookmark(userAction.get('post_id'), false) @@ -40,10 +42,3 @@ var UserActivityStreamRoute = Discourse.Route.extend({ } }); - -// Build all activity stream routes -['bookmarks', 'edits', 'likes_given', 'likes_received', 'replies', 'posts', 'index'].forEach(function (userAction) { - Discourse["UserActivity" + userAction.classify() + "Route"] = UserActivityStreamRoute.extend({ - userActionType: Discourse.UserAction.TYPES[userAction] - }); -}); diff --git a/app/assets/javascripts/discourse/routes/user-activity-topics.js.es6 b/app/assets/javascripts/discourse/routes/user-activity-topics.js.es6 new file mode 100644 index 0000000000..ba5e6aa748 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-activity-topics.js.es6 @@ -0,0 +1,9 @@ +import UserTopicListRoute from "discourse/routes/user-topic-list"; + +export default UserTopicListRoute.extend({ + userActionType: Discourse.UserAction.TYPES.topics, + + model: function() { + return Discourse.TopicList.find('topics/created-by/' + this.modelFor('user').get('username_lower')); + } +}); diff --git a/app/assets/javascripts/discourse/routes/user-activity.js.es6 b/app/assets/javascripts/discourse/routes/user-activity.js.es6 index be81d64573..f214ce88b9 100644 --- a/app/assets/javascripts/discourse/routes/user-activity.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-activity.js.es6 @@ -5,7 +5,6 @@ export default Discourse.Route.extend({ setupController: function(controller, user) { this.controllerFor('user-activity').set('model', user); - this.controllerFor('user').set('pmView', null); // Bring up a draft var composerController = this.controllerFor('composer'); diff --git a/app/assets/javascripts/discourse/routes/user-badges.js.es6 b/app/assets/javascripts/discourse/routes/user-badges.js.es6 index 0ccfa32a0b..5455257881 100644 --- a/app/assets/javascripts/discourse/routes/user-badges.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-badges.js.es6 @@ -1,14 +1,11 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { model: function() { return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username_lower'), {grouped: true}); }, setupController: function(controller, model) { - this.controllerFor('user').setProperties({ - indexStream: false, - datasource: "badges", - }); - if (this.controllerFor('user_activity').get('content')) { this.controllerFor('user_activity').set('userActionType', -1); } diff --git a/app/assets/javascripts/discourse/routes/user-deleted-posts.js.es6 b/app/assets/javascripts/discourse/routes/user-deleted-posts.js.es6 new file mode 100644 index 0000000000..75b35d804f --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-deleted-posts.js.es6 @@ -0,0 +1,3 @@ +import createAdminUserPostsRoute from "discourse/routes/build-admin-user-posts-route"; + +export default createAdminUserPostsRoute("deleted"); diff --git a/app/assets/javascripts/discourse/routes/user-flagged-posts.js.es6 b/app/assets/javascripts/discourse/routes/user-flagged-posts.js.es6 new file mode 100644 index 0000000000..3485c8b72a --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-flagged-posts.js.es6 @@ -0,0 +1,3 @@ +import createAdminUserPostsRoute from "discourse/routes/build-admin-user-posts-route"; + +export default createAdminUserPostsRoute("flagged"); diff --git a/app/assets/javascripts/discourse/routes/user-index.js.es6 b/app/assets/javascripts/discourse/routes/user-index.js.es6 index 8d46f92be0..a2e100000a 100644 --- a/app/assets/javascripts/discourse/routes/user-index.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-index.js.es6 @@ -1,8 +1,7 @@ export default Discourse.Route.extend({ beforeModel: function() { - this.controllerFor('user').set('indexStream', true); - return this.replaceWith('userActivity'); + this.replaceWith('userActivity'); } }); diff --git a/app/assets/javascripts/discourse/routes/user-invited.js.es6 b/app/assets/javascripts/discourse/routes/user-invited.js.es6 index 42acec4a2f..5a03f2797a 100644 --- a/app/assets/javascripts/discourse/routes/user-invited.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-invited.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { renderTemplate: function() { this.render({ into: 'user' }); }, @@ -14,7 +16,6 @@ export default Discourse.Route.extend({ searchTerm: '', totalInvites: model.invites.length }); - this.controllerFor('user').set('indexStream', false); }, actions: { diff --git a/app/assets/javascripts/discourse/routes/user-notifications.js.es6 b/app/assets/javascripts/discourse/routes/user-notifications.js.es6 index 2abd0f464a..d5d942417f 100644 --- a/app/assets/javascripts/discourse/routes/user-notifications.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-notifications.js.es6 @@ -1,21 +1,28 @@ -export default Discourse.Route.extend({ +import ShowFooter from "discourse/mixins/show-footer"; + +export default Discourse.Route.extend(ShowFooter, { + actions: { + didTransition: function() { + this.controllerFor("user_notifications")._showFooter(); + return true; + } + }, + model: function() { var user = this.modelFor('user'); return Discourse.NotificationContainer.loadHistory(undefined, user.get('username')); }, setupController: function(controller, model) { - this.controllerFor('user').setProperties({ - indexStream: false, - datasource: "notifications" - }); + controller.set('model', model); + controller.set('user', this.modelFor('user')); if (this.controllerFor('user_activity').get('content')) { this.controllerFor('user_activity').set('userActionType', -1); } - controller.set('model', model); - controller.set('user', this.modelFor('user')); + // properly initialize "canLoadMore" + controller.set("canLoadMore", model.get("length") === 60); }, renderTemplate: function() { diff --git a/app/assets/javascripts/discourse/routes/user-private-messages-index.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages-index.js.es6 new file mode 100644 index 0000000000..5265bfb670 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-private-messages-index.js.es6 @@ -0,0 +1,3 @@ +import createPMRoute from "discourse/routes/build-user-topic-list-route"; + +export default createPMRoute('index', 'private-messages'); diff --git a/app/assets/javascripts/discourse/routes/user-private-messages-mine.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages-mine.js.es6 new file mode 100644 index 0000000000..47db699924 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-private-messages-mine.js.es6 @@ -0,0 +1,3 @@ +import createPMRoute from "discourse/routes/build-user-topic-list-route"; + +export default createPMRoute('mine', 'private-messages-sent'); diff --git a/app/assets/javascripts/discourse/routes/user-private-messages-unread.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages-unread.js.es6 new file mode 100644 index 0000000000..19e2a5167d --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-private-messages-unread.js.es6 @@ -0,0 +1,3 @@ +import createPMRoute from "discourse/routes/build-user-topic-list-route"; + +export default createPMRoute('unread', 'private-messages-unread'); diff --git a/app/assets/javascripts/discourse/routes/user-private-messages.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages.js.es6 index b3103444a9..328a304ca5 100644 --- a/app/assets/javascripts/discourse/routes/user-private-messages.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-private-messages.js.es6 @@ -1,3 +1,11 @@ import UserActivityRoute from 'discourse/routes/user-activity'; -export default UserActivityRoute.extend(); +export default UserActivityRoute.extend({ + actions: { + willTransition: function() { + this._super(); + this.controllerFor('user').set('pmView', null); + return true; + } + } +}); diff --git a/app/assets/javascripts/discourse/routes/user-topic-list.js.es6 b/app/assets/javascripts/discourse/routes/user-topic-list.js.es6 new file mode 100644 index 0000000000..5455b03e2f --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-topic-list.js.es6 @@ -0,0 +1,14 @@ +export default Discourse.Route.extend({ + renderTemplate: function() { + this.render('user_topics_list'); + }, + + setupController: function(controller, model) { + this.controllerFor('user-activity').set('userActionType', this.get('userActionType')); + this.controllerFor('user-topics-list').setProperties({ + model: model, + hideCategory: false, + showParticipants: false + }); + } +}); diff --git a/app/assets/javascripts/discourse/routes/user.js.es6 b/app/assets/javascripts/discourse/routes/user.js.es6 index 7ae758de0b..910abb196f 100644 --- a/app/assets/javascripts/discourse/routes/user.js.es6 +++ b/app/assets/javascripts/discourse/routes/user.js.es6 @@ -1,3 +1,5 @@ +var INDEX_STREAM_ROUTES = ["user.deletedPosts", "user.flaggedPosts", "userActivity.index"]; + export default Discourse.Route.extend({ titleToken: function() { @@ -21,12 +23,19 @@ export default Discourse.Route.extend({ archetypeId: 'private_message', draftKey: 'new_private_message' }); + }, + + willTransition: function(transition) { + // will reset the indexStream when transitioning to routes that aren't "indexStream" + // otherwise the "header" will jump + var isIndexStream = ~INDEX_STREAM_ROUTES.indexOf(transition.targetName); + this.controllerFor('user').set('indexStream', isIndexStream); + return true; } }, model: function(params) { - // If we're viewing the currently logged in user, return that object - // instead. + // If we're viewing the currently logged in user, return that object instead var currentUser = Discourse.User.current(); if (currentUser && (params.username.toLowerCase() === currentUser.get('username_lower'))) { return currentUser; diff --git a/app/assets/javascripts/discourse/routes/user_admin_posts_routes.js b/app/assets/javascripts/discourse/routes/user_admin_posts_routes.js deleted file mode 100644 index 0eb874627f..0000000000 --- a/app/assets/javascripts/discourse/routes/user_admin_posts_routes.js +++ /dev/null @@ -1,23 +0,0 @@ -function createAdminPostRoute (filter) { - return Discourse.Route.extend({ - model: function () { - return this.modelFor("user").get("postsStream"); - }, - - afterModel: function () { - return this.modelFor("user").get("postsStream").filterBy(filter); - }, - - setupController: function (controller, model) { - controller.set("model", model); - this.controllerFor("user").set("indexStream", true); - }, - - renderTemplate: function() { - this.render("user/posts", { into: "user" }); - } - }); -} - -Discourse.UserDeletedPostsRoute = createAdminPostRoute("deleted"); -Discourse.UserFlaggedPostsRoute = createAdminPostRoute("flagged"); diff --git a/app/assets/javascripts/discourse/routes/user_topic_list_routes.js b/app/assets/javascripts/discourse/routes/user_topic_list_routes.js deleted file mode 100644 index 35b4a86ddb..0000000000 --- a/app/assets/javascripts/discourse/routes/user_topic_list_routes.js +++ /dev/null @@ -1,61 +0,0 @@ -Discourse.UserTopicListRoute = Discourse.Route.extend({ - renderTemplate: function() { - this.render('user_topics_list'); - }, - - setupController: function(controller, model) { - this.controllerFor('user').setProperties({ - indexStream: false, - datasource: "topic_list" - }); - this.controllerFor('user-activity').set('userActionType', this.get('userActionType')); - this.controllerFor('user_topics_list').setProperties({ - model: model, - hideCategory: false, - showParticipants: false - }); - } -}); - -function createPMRoute(viewName, path) { - return Discourse.UserTopicListRoute.extend({ - userActionType: Discourse.UserAction.TYPES.messages_received, - - model: function() { - return Discourse.TopicList.find('topics/' + path + '/' + this.modelFor('user').get('username_lower')); - }, - - setupController: function() { - this._super.apply(this, arguments); - this.controllerFor('user_topics_list').setProperties({ - hideCategory: true, - showParticipants: true - }); - this.controllerFor('user').setProperties({ - pmView: viewName, - indexStream: false, - datasource: "topic_list" - }); - } - }); -} - -Discourse.UserPrivateMessagesIndexRoute = createPMRoute('index', 'private-messages'); -Discourse.UserPrivateMessagesMineRoute = createPMRoute('mine', 'private-messages-sent'); -Discourse.UserPrivateMessagesUnreadRoute = createPMRoute('unread', 'private-messages-unread'); - -Discourse.UserActivityTopicsRoute = Discourse.UserTopicListRoute.extend({ - userActionType: Discourse.UserAction.TYPES.topics, - - model: function() { - return Discourse.TopicList.find('topics/created-by/' + this.modelFor('user').get('username_lower')); - } -}); - -Discourse.UserActivityStarredRoute = Discourse.UserTopicListRoute.extend({ - userActionType: Discourse.UserAction.TYPES.starred, - - model: function() { - return Discourse.TopicList.find('starred', { user_id: this.modelFor('user').get('id') }); - } -}); diff --git a/app/assets/javascripts/discourse/templates/about.hbs b/app/assets/javascripts/discourse/templates/about.hbs index 2a240a4f59..09b02a525c 100644 --- a/app/assets/javascripts/discourse/templates/about.hbs +++ b/app/assets/javascripts/discourse/templates/about.hbs @@ -62,5 +62,3 @@ - -{{custom-html "footer"}} diff --git a/app/assets/javascripts/discourse/templates/application.hbs b/app/assets/javascripts/discourse/templates/application.hbs index 8577bf11f3..b5ff68ac9d 100644 --- a/app/assets/javascripts/discourse/templates/application.hbs +++ b/app/assets/javascripts/discourse/templates/application.hbs @@ -5,6 +5,10 @@ {{render "user-card"}} +{{#if showFooter}} + {{custom-html "footer"}} +{{/if}} + {{render "modal"}} {{render "topic-entrance"}} {{render "composer"}} diff --git a/app/assets/javascripts/discourse/templates/badges/index.hbs b/app/assets/javascripts/discourse/templates/badges/index.hbs index 5c8197ec3e..35b52a7fe6 100644 --- a/app/assets/javascripts/discourse/templates/badges/index.hbs +++ b/app/assets/javascripts/discourse/templates/badges/index.hbs @@ -19,5 +19,3 @@ - -{{custom-html "footer"}} diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs index d36ae91417..85dc9da6a7 100644 --- a/app/assets/javascripts/discourse/templates/badges/show.hbs +++ b/app/assets/javascripts/discourse/templates/badges/show.hbs @@ -37,8 +37,5 @@ {{loading-spinner condition=canLoadMore}} - {{#unless canLoadMore}} - {{custom-html "footer"}} - {{/unless}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/discovery.hbs b/app/assets/javascripts/discourse/templates/discovery.hbs index 49b4151483..04c3e4e5e5 100644 --- a/app/assets/javascripts/discourse/templates/discovery.hbs +++ b/app/assets/javascripts/discourse/templates/discovery.hbs @@ -28,6 +28,3 @@ -{{#if loadedAllItems}} - {{custom-html "footer"}} -{{/if}} diff --git a/app/assets/javascripts/discourse/templates/static.hbs b/app/assets/javascripts/discourse/templates/static.hbs index 5e094986f9..e6aa2f99a7 100644 --- a/app/assets/javascripts/discourse/templates/static.hbs +++ b/app/assets/javascripts/discourse/templates/static.hbs @@ -7,5 +7,3 @@ {{/if}} - -{{custom-html "footer"}} diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index 56cbdb9986..e203fdaf48 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -134,10 +134,6 @@ {{/if}} -{{#if postStream.loadedAllPosts}} - {{custom-html "footer"}} -{{/if}} - {{render "share"}} {{#if currentUser.enable_quoting}} diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index 2ced5bce85..bf7074dbdf 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -194,7 +194,3 @@ - -{{#if loadedAllItems}} - {{custom-html "footer"}} -{{/if}} diff --git a/app/assets/javascripts/discourse/views/badges-show.js.es6 b/app/assets/javascripts/discourse/views/badges-show.js.es6 index 6279068586..9b74c3e3f9 100644 --- a/app/assets/javascripts/discourse/views/badges-show.js.es6 +++ b/app/assets/javascripts/discourse/views/badges-show.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Discourse.View.extend(LoadMore, { eyelineSelector: '.badge-user', tickOrX: function(field){ var icon = this.get('controller.model.' + field) ? "fa-check" : "fa-times"; diff --git a/app/assets/javascripts/discourse/views/discovery-topics.js.es6 b/app/assets/javascripts/discourse/views/discovery-topics.js.es6 index 2885f09e00..02d374c4ab 100644 --- a/app/assets/javascripts/discourse/views/discovery-topics.js.es6 +++ b/app/assets/javascripts/discourse/views/discovery-topics.js.es6 @@ -1,6 +1,7 @@ import UrlRefresh from 'discourse/mixins/url-refresh'; +import LoadMore from "discourse/mixins/load-more"; -export default Discourse.View.extend(Discourse.LoadMore, UrlRefresh, { +export default Discourse.View.extend(LoadMore, UrlRefresh, { eyelineSelector: '.topic-list-item', actions: { diff --git a/app/assets/javascripts/discourse/views/group-index.js.es6 b/app/assets/javascripts/discourse/views/group-index.js.es6 index 5f26a832bd..b1da6e3363 100644 --- a/app/assets/javascripts/discourse/views/group-index.js.es6 +++ b/app/assets/javascripts/discourse/views/group-index.js.es6 @@ -1,11 +1,5 @@ -/** - Displays all posts within a group +import LoadMore from "discourse/mixins/load-more"; - @class Discourse.GroupIndexView - @extends Ember.Mixin - @namespace Discourse - @module Discourse -**/ -export default Discourse.View.extend(Discourse.ScrollTop, Discourse.LoadMore, { +export default Discourse.View.extend(Discourse.ScrollTop, LoadMore, { eyelineSelector: '.user-stream .item', }); diff --git a/app/assets/javascripts/discourse/views/user-invited.js.es6 b/app/assets/javascripts/discourse/views/user-invited.js.es6 index 55c4d94c85..a94430b7ce 100644 --- a/app/assets/javascripts/discourse/views/user-invited.js.es6 +++ b/app/assets/javascripts/discourse/views/user-invited.js.es6 @@ -1,4 +1,6 @@ -export default Ember.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Ember.View.extend(LoadMore, { classNames: ['paginated-topics-list'], eyelineSelector: '.paginated-topics-list .invite-list tr', templateName: 'user/invited' diff --git a/app/assets/javascripts/discourse/views/user-notification-history.js.es6 b/app/assets/javascripts/discourse/views/user-notification-history.js.es6 index a31bea4931..90dfc8e401 100644 --- a/app/assets/javascripts/discourse/views/user-notification-history.js.es6 +++ b/app/assets/javascripts/discourse/views/user-notification-history.js.es6 @@ -1,4 +1,6 @@ -export default Ember.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Ember.View.extend(LoadMore, { eyelineSelector: '.user-stream .notification', classNames: ['user-stream', 'notification-history'], templateName: 'user/notifications' diff --git a/app/assets/javascripts/discourse/views/user-posts.js.es6 b/app/assets/javascripts/discourse/views/user-posts.js.es6 index ebe35fe025..6a4d254fcf 100644 --- a/app/assets/javascripts/discourse/views/user-posts.js.es6 +++ b/app/assets/javascripts/discourse/views/user-posts.js.es6 @@ -1,4 +1,6 @@ -export default Ember.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Ember.View.extend(LoadMore, { loading: false, eyelineSelector: ".user-stream .item", classNames: ["user-stream"], diff --git a/app/assets/javascripts/discourse/views/user-stream.js.es6 b/app/assets/javascripts/discourse/views/user-stream.js.es6 index 09e6eb5dcf..367c089ed0 100644 --- a/app/assets/javascripts/discourse/views/user-stream.js.es6 +++ b/app/assets/javascripts/discourse/views/user-stream.js.es6 @@ -1,4 +1,6 @@ -export default Ember.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Ember.View.extend(LoadMore, { loading: false, eyelineSelector: '.user-stream .item', classNames: ['user-stream'], diff --git a/app/assets/javascripts/discourse/views/user-topics-list.js.es6 b/app/assets/javascripts/discourse/views/user-topics-list.js.es6 index 21e7e57856..8a97f78ff1 100644 --- a/app/assets/javascripts/discourse/views/user-topics-list.js.es6 +++ b/app/assets/javascripts/discourse/views/user-topics-list.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Discourse.View.extend(LoadMore, { classNames: ['paginated-topics-list'], eyelineSelector: '.paginated-topics-list .topic-list tr', templateName: 'list/user_topics_list' diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 102cba9b5c..6e3ca4c7d3 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -39,7 +39,10 @@ //= require ./discourse/views/pagedown-preview //= require ./discourse/routes/discourse_route //= require ./discourse/routes/build-topic-route -//= require ./discourse/routes/discourse_restricted_user_route +//= require ./discourse/routes/restricted-user +//= require ./discourse/routes/user-topic-list +//= require ./discourse/routes/user-activity-stream +//= require ./discourse/routes/topic-from-params //= require ./discourse/components/top-title //= require ./discourse/components/text-field //= require ./discourse/components/visible From 98e8523eec51f9cafd1b03cce88569ba5409f457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 19 Nov 2014 21:38:53 +0100 Subject: [PATCH 010/991] UX: improve ip-lookup modal - replace close button with cross - use table instead of ul/li - use trust level number instead of full name - sort users by descending trust level - add post count --- .../admin/components/ip-lookup.js.es6 | 3 +- .../templates/components/ip-lookup.hbs | 33 +++++++++++++------ app/serializers/admin_user_serializer.rb | 2 +- config/locales/client.en.yml | 7 ++-- lib/admin_user_index_query.rb | 10 ++++-- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index d3140eecf6..06a8387be4 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -26,7 +26,8 @@ export default Ember.Component.extend({ this.set("otherAccountsLoading", true); Discourse.AdminUser.findAll("active", { "ip": this.get("ip"), - "exclude": this.get("user_id") + "exclude": this.get("user_id"), + "order": "trust_level DESC" }).then(function (users) { self.setProperties({ other_accounts: users, diff --git a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs index 4afa200b19..6fd3bea83e 100644 --- a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs @@ -5,6 +5,7 @@ {{/if}} {{#if show}}
+ {{fa-icon "times"}}

{{i18n ip_lookup.title}}

{{#if location}} @@ -40,21 +41,33 @@
{{#loading-spinner size="small" condition=otherAccountsLoading}} {{#if other_accounts}} -
    - {{#each other_accounts}} -
  • - {{#link-to "adminUser" this}}{{avatar this usernamePath="user.username" imageSize="small"}} {{username}}{{/link-to}} - ({{trustLevel.name}}), - {{i18n ip_lookup.read_time}} {{time_read}}, - {{i18n ip_lookup.topics_entered}} {{topics_entered}} -
  • - {{/each}} + + + + + + + + + + + + {{#each other_accounts}} + + + + + + + + {{/each}} + +
    {{i18n ip_lookup.username}}{{i18n ip_lookup.trust_level}}{{i18n ip_lookup.read_time}}{{i18n ip_lookup.topics_entered}}{{i18n ip_lookup.post_count}}
    {{#link-to "adminUser" this}}{{avatar this usernamePath="user.username" imageSize="small"}} {{username}}{{/link-to}}{{trustLevel.id}}{{time_read}}{{topics_entered}}{{post_count}}
    {{else}} {{i18n ip_lookup.no_other_accounts}} {{/if}} {{/loading-spinner}}
-
{{/if}} diff --git a/app/serializers/admin_user_serializer.rb b/app/serializers/admin_user_serializer.rb index 4c9f309ce9..9d40ee61d8 100644 --- a/app/serializers/admin_user_serializer.rb +++ b/app/serializers/admin_user_serializer.rb @@ -30,7 +30,7 @@ class AdminUserSerializer < BasicUserSerializer has_one :single_sign_on_record, serializer: SingleSignOnRecordSerializer, embed: :objects - [:days_visited,:posts_read_count,:topics_entered].each do |sym| + [:days_visited, :posts_read_count, :topics_entered, :post_count].each do |sym| attributes sym define_method sym do object.user_stat.send(sym) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 5d7369364e..16793af595 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -280,8 +280,11 @@ en: phone: Phone other_accounts: "Other accounts with this IP address:" no_other_accounts: (none) - read_time: "read time:" - topics_entered: "topics entered:" + username: "username" + trust_level: "TL" + read_time: "read time" + topics_entered: "topics entered" + post_count: "# posts" user: said: "{{username}}:" diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index cf20f2103c..c0e11cdc72 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -10,11 +10,17 @@ class AdminUserIndexQuery attr_reader :params, :trust_levels def initialize_query_with_order(klass) + order = [params[:order]] + if params[:query] == "active" - klass.order("COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC, username") + order << "COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC" else - klass.order("created_at DESC, username") + order << "created_at DESC" end + + order << "username" + + klass.order(order.reject(&:blank?).join(",")) end def filter_by_trust From 6bed4e1bf0d551238bbd6e8fb43f9e5959f17cf8 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 20 Nov 2014 14:53:15 +1100 Subject: [PATCH 011/991] add allowed_ips to api_keys update annotations --- app/models/api_key.rb | 1 + app/models/badge.rb | 1 + app/models/category.rb | 65 +++++------ app/models/post.rb | 5 +- app/models/post_action.rb | 1 + app/models/post_revision.rb | 1 + app/models/site_customization.rb | 2 + app/models/site_text.rb | 12 +-- app/models/topic.rb | 102 +++++++++--------- app/models/topic_user.rb | 1 + app/models/upload.rb | 1 + app/models/user.rb | 3 +- app/models/user_field.rb | 14 +++ app/models/user_history.rb | 1 + app/models/user_profile.rb | 6 +- app/models/warning.rb | 17 +++ ...41120035016_add_allowed_ips_to_api_keys.rb | 7 ++ 17 files changed, 147 insertions(+), 93 deletions(-) create mode 100644 db/migrate/20141120035016_add_allowed_ips_to_api_keys.rb diff --git a/app/models/api_key.rb b/app/models/api_key.rb index dc6195baeb..099506ebf6 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -31,6 +31,7 @@ end # created_by_id :integer # created_at :datetime not null # updated_at :datetime not null +# allowed_ips :inet is an Array # # Indexes # diff --git a/app/models/badge.rb b/app/models/badge.rb index ea50d782d6..e040d761be 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -303,6 +303,7 @@ end # trigger :integer # show_posts :boolean default(FALSE), not null # system :boolean default(FALSE), not null +# image :string(255) # # Indexes # diff --git a/app/models/category.rb b/app/models/category.rb index 9af8a4ca7b..846f5b36b1 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -393,38 +393,39 @@ end # # Table name: categories # -# id :integer not null, primary key -# name :string(50) not null -# color :string(6) default("AB9364"), not null -# topic_id :integer -# topic_count :integer default(0), not null -# created_at :datetime not null -# updated_at :datetime not null -# user_id :integer not null -# topics_year :integer default(0) -# topics_month :integer default(0) -# topics_week :integer default(0) -# slug :string(255) not null -# description :text -# text_color :string(6) default("FFFFFF"), not null -# read_restricted :boolean default(FALSE), not null -# auto_close_hours :float -# post_count :integer default(0), not null -# latest_post_id :integer -# latest_topic_id :integer -# position :integer -# parent_category_id :integer -# posts_year :integer default(0) -# posts_month :integer default(0) -# posts_week :integer default(0) -# email_in :string(255) -# email_in_allow_strangers :boolean default(FALSE) -# topics_day :integer default(0) -# posts_day :integer default(0) -# logo_url :string(255) -# background_url :string(255) -# allow_badges :boolean default(TRUE), not null -# name_lower :string(50) not null +# id :integer not null, primary key +# name :string(50) not null +# color :string(6) default("AB9364"), not null +# topic_id :integer +# topic_count :integer default(0), not null +# created_at :datetime not null +# updated_at :datetime not null +# user_id :integer not null +# topics_year :integer default(0) +# topics_month :integer default(0) +# topics_week :integer default(0) +# slug :string(255) not null +# description :text +# text_color :string(6) default("FFFFFF"), not null +# read_restricted :boolean default(FALSE), not null +# auto_close_hours :float +# post_count :integer default(0), not null +# latest_post_id :integer +# latest_topic_id :integer +# position :integer +# parent_category_id :integer +# posts_year :integer default(0) +# posts_month :integer default(0) +# posts_week :integer default(0) +# email_in :string(255) +# email_in_allow_strangers :boolean default(FALSE) +# topics_day :integer default(0) +# posts_day :integer default(0) +# logo_url :string(255) +# background_url :string(255) +# allow_badges :boolean default(TRUE), not null +# name_lower :string(50) not null +# auto_close_based_on_last_post :boolean default(FALSE) # # Indexes # diff --git a/app/models/post.rb b/app/models/post.rb index b4b64f9696..81f03cd911 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -580,13 +580,14 @@ end # version :integer default(1), not null # cook_method :integer default(1), not null # wiki :boolean default(FALSE), not null -# via_email :boolean default(FALSE), not null -# raw_email :text # baked_at :datetime # baked_version :integer # hidden_at :datetime # self_edits :integer default(0), not null # reply_quoted :boolean default(FALSE), not null +# via_email :boolean default(FALSE), not null +# raw_email :text +# public_version :integer default(1), not null # # Indexes # diff --git a/app/models/post_action.rb b/app/models/post_action.rb index e40c34c188..b224f45276 100644 --- a/app/models/post_action.rb +++ b/app/models/post_action.rb @@ -464,5 +464,6 @@ end # Indexes # # idx_unique_actions (user_id,post_action_type_id,post_id,targets_topic) UNIQUE +# idx_unique_flags (user_id,post_id,targets_topic) UNIQUE # index_post_actions_on_post_id (post_id) # diff --git a/app/models/post_revision.rb b/app/models/post_revision.rb index 1eb7192655..a11a1f35ed 100644 --- a/app/models/post_revision.rb +++ b/app/models/post_revision.rb @@ -47,6 +47,7 @@ end # number :integer # created_at :datetime not null # updated_at :datetime not null +# hidden :boolean default(FALSE), not null # # Indexes # diff --git a/app/models/site_customization.rb b/app/models/site_customization.rb index 6b88d9dfdd..b0ca624591 100644 --- a/app/models/site_customization.rb +++ b/app/models/site_customization.rb @@ -215,6 +215,8 @@ end # mobile_stylesheet :text # mobile_header :text # mobile_stylesheet_baked :text +# footer :text +# mobile_footer :text # # Indexes # diff --git a/app/models/site_text.rb b/app/models/site_text.rb index f3f7559d5c..3c481cfcad 100644 --- a/app/models/site_text.rb +++ b/app/models/site_text.rb @@ -33,14 +33,14 @@ end # == Schema Information # -# Table name: site_text +# Table name: site_texts # -# text_type :string(255) not null, primary key -# value :text not null -# created_at :datetime not null -# updated_at :datetime not null +# text_type :string(255) not null, primary key +# value :text not null +# created_at :datetime not null +# updated_at :datetime not null # # Indexes # -# index_site_text_on_text_type (text_type) UNIQUE +# index_site_texts_on_text_type (text_type) UNIQUE # diff --git a/app/models/topic.rb b/app/models/topic.rb index 9eae898bb3..d37e93d585 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -855,56 +855,58 @@ end # # Table name: topics # -# id :integer not null, primary key -# title :string(255) not null -# last_posted_at :datetime -# created_at :datetime not null -# updated_at :datetime not null -# views :integer default(0), not null -# posts_count :integer default(0), not null -# user_id :integer -# last_post_user_id :integer not null -# reply_count :integer default(0), not null -# featured_user1_id :integer -# featured_user2_id :integer -# featured_user3_id :integer -# avg_time :integer -# deleted_at :datetime -# highest_post_number :integer default(0), not null -# image_url :string(255) -# off_topic_count :integer default(0), not null -# like_count :integer default(0), not null -# incoming_link_count :integer default(0), not null -# bookmark_count :integer default(0), not null -# star_count :integer default(0), not null -# category_id :integer -# visible :boolean default(TRUE), not null -# moderator_posts_count :integer default(0), not null -# closed :boolean default(FALSE), not null -# archived :boolean default(FALSE), not null -# bumped_at :datetime not null -# has_summary :boolean default(FALSE), not null -# vote_count :integer default(0), not null -# archetype :string(255) default("regular"), not null -# featured_user4_id :integer -# notify_moderators_count :integer default(0), not null -# spam_count :integer default(0), not null -# illegal_count :integer default(0), not null -# inappropriate_count :integer default(0), not null -# pinned_at :datetime -# score :float -# percent_rank :float default(1.0), not null -# notify_user_count :integer default(0), not null -# subtype :string(255) -# slug :string(255) -# auto_close_at :datetime -# auto_close_user_id :integer -# auto_close_started_at :datetime -# deleted_by_id :integer -# participant_count :integer default(1) -# word_count :integer -# excerpt :string(1000) -# pinned_globally :boolean default(FALSE), not null +# id :integer not null, primary key +# title :string(255) not null +# last_posted_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# views :integer default(0), not null +# posts_count :integer default(0), not null +# user_id :integer +# last_post_user_id :integer not null +# reply_count :integer default(0), not null +# featured_user1_id :integer +# featured_user2_id :integer +# featured_user3_id :integer +# avg_time :integer +# deleted_at :datetime +# highest_post_number :integer default(0), not null +# image_url :string(255) +# off_topic_count :integer default(0), not null +# like_count :integer default(0), not null +# incoming_link_count :integer default(0), not null +# bookmark_count :integer default(0), not null +# star_count :integer default(0), not null +# category_id :integer +# visible :boolean default(TRUE), not null +# moderator_posts_count :integer default(0), not null +# closed :boolean default(FALSE), not null +# archived :boolean default(FALSE), not null +# bumped_at :datetime not null +# has_summary :boolean default(FALSE), not null +# vote_count :integer default(0), not null +# archetype :string(255) default("regular"), not null +# featured_user4_id :integer +# notify_moderators_count :integer default(0), not null +# spam_count :integer default(0), not null +# illegal_count :integer default(0), not null +# inappropriate_count :integer default(0), not null +# pinned_at :datetime +# score :float +# percent_rank :float default(1.0), not null +# notify_user_count :integer default(0), not null +# subtype :string(255) +# slug :string(255) +# auto_close_at :datetime +# auto_close_user_id :integer +# auto_close_started_at :datetime +# deleted_by_id :integer +# participant_count :integer default(1) +# word_count :integer +# excerpt :string(1000) +# pinned_globally :boolean default(FALSE), not null +# auto_close_based_on_last_post :boolean default(FALSE) +# auto_close_hours :float # # Indexes # diff --git a/app/models/topic_user.rb b/app/models/topic_user.rb index b252eca730..a70bf224ad 100644 --- a/app/models/topic_user.rb +++ b/app/models/topic_user.rb @@ -297,4 +297,5 @@ end # Indexes # # index_topic_users_on_topic_id_and_user_id (topic_id,user_id) UNIQUE +# index_topic_users_on_user_id_and_topic_id (user_id,topic_id) UNIQUE # diff --git a/app/models/upload.rb b/app/models/upload.rb index 9880acece1..33432a28a4 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -150,6 +150,7 @@ end # updated_at :datetime not null # sha1 :string(40) # origin :string(1000) +# retain_hours :integer # # Indexes # diff --git a/app/models/user.rb b/app/models/user.rb index cafd7ddc3e..56cad0d705 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -875,12 +875,13 @@ end # uploaded_avatar_id :integer # email_always :boolean default(FALSE), not null # mailing_list_mode :boolean default(FALSE), not null -# primary_group_id :integer # locale :string(10) +# primary_group_id :integer # registration_ip_address :inet # last_redirected_to_top_at :datetime # disable_jump_reply :boolean default(FALSE), not null # edit_history_public :boolean default(FALSE), not null +# trust_level_locked :boolean default(FALSE), not null # # Indexes # diff --git a/app/models/user_field.rb b/app/models/user_field.rb index 55a85d2b22..b87fd35b0c 100644 --- a/app/models/user_field.rb +++ b/app/models/user_field.rb @@ -1,3 +1,17 @@ class UserField < ActiveRecord::Base validates_presence_of :name, :description, :field_type end + +# == Schema Information +# +# Table name: user_fields +# +# id :integer not null, primary key +# name :string(255) not null +# field_type :string(255) not null +# created_at :datetime +# updated_at :datetime +# editable :boolean default(FALSE), not null +# description :string(255) not null +# required :boolean default(TRUE), not null +# diff --git a/app/models/user_history.rb b/app/models/user_history.rb index e10cdd9760..954a66ac06 100644 --- a/app/models/user_history.rb +++ b/app/models/user_history.rb @@ -126,6 +126,7 @@ end # new_value :text # topic_id :integer # admin_only :boolean default(FALSE) +# post_id :integer # # Indexes # diff --git a/app/models/user_profile.rb b/app/models/user_profile.rb index c89498d7f3..3059d2a289 100644 --- a/app/models/user_profile.rb +++ b/app/models/user_profile.rb @@ -106,10 +106,12 @@ end # website :string(255) # bio_raw :text # bio_cooked :text -# dismissed_banner_key :integer # profile_background :string(255) -# card_background :string(255) +# dismissed_banner_key :integer # bio_cooked_version :integer +# badge_granted_title :boolean default(FALSE) +# card_background :string(255) +# card_image_badge_id :integer # # Indexes # diff --git a/app/models/warning.rb b/app/models/warning.rb index 7d60d2deb5..0d177fb0c6 100644 --- a/app/models/warning.rb +++ b/app/models/warning.rb @@ -3,3 +3,20 @@ class Warning < ActiveRecord::Base belongs_to :topic belongs_to :created_by, class_name: 'User' end + +# == Schema Information +# +# Table name: warnings +# +# id :integer not null, primary key +# topic_id :integer not null +# user_id :integer not null +# created_by_id :integer not null +# created_at :datetime +# updated_at :datetime +# +# Indexes +# +# index_warnings_on_topic_id (topic_id) UNIQUE +# index_warnings_on_user_id (user_id) +# diff --git a/db/migrate/20141120035016_add_allowed_ips_to_api_keys.rb b/db/migrate/20141120035016_add_allowed_ips_to_api_keys.rb new file mode 100644 index 0000000000..846c889195 --- /dev/null +++ b/db/migrate/20141120035016_add_allowed_ips_to_api_keys.rb @@ -0,0 +1,7 @@ +class AddAllowedIpsToApiKeys < ActiveRecord::Migration + def change + change_table :api_keys do |t| + t.inet :allowed_ips, array: true + end + end +end From 4aec3c8c4caef587f88fb628ac34c5fb8053effb Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 20 Nov 2014 14:53:30 +1100 Subject: [PATCH 012/991] correct import script --- script/import_scripts/bespoke_1.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/script/import_scripts/bespoke_1.rb b/script/import_scripts/bespoke_1.rb index e91a001a57..2b94283aea 100644 --- a/script/import_scripts/bespoke_1.rb +++ b/script/import_scripts/bespoke_1.rb @@ -28,8 +28,8 @@ class ImportScripts::Bespoke < ImportScripts::Base end def execute - #import_users - #import_categories + import_users + import_categories import_posts end @@ -146,6 +146,8 @@ class ImportScripts::Bespoke < ImportScripts::Base created_at = DateTime.parse(row.dcreate) username = name if username == "NULL" + username = email.split("@")[0] if username.blank? + name = email.split("@")[0] if name.blank? users << { id: id, @@ -180,7 +182,7 @@ class ImportScripts::Bespoke < ImportScripts::Base # purple and #1223f3 raw.gsub!(/\[color=[#a-z0-9]+\]/i, "") raw.gsub!(/\[\/color\]/i, "") - raw.gsub!(/\[signature\].+\[\/signature\]/i,"") + raw.gsub!(/\[signature\].+\[\/signature\]/im,"") raw end From a9cda0f947c41b6c01ea844229010c83d3afaf2f Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 20 Nov 2014 15:21:49 +1100 Subject: [PATCH 013/991] FEATURE: allow restricting API keys to a particular range --- lib/auth/default_current_user_provider.rb | 8 +++++-- .../default_current_user_provider_spec.rb | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/auth/default_current_user_provider.rb b/lib/auth/default_current_user_provider.rb index 94c7a8e914..9772a037d7 100644 --- a/lib/auth/default_current_user_provider.rb +++ b/lib/auth/default_current_user_provider.rb @@ -107,12 +107,16 @@ class Auth::DefaultCurrentUserProvider api_key = ApiKey.where(key: api_key_value).includes(:user).first if api_key api_username = request["api_username"] + + if api_key.allowed_ips.present? && !api_key.allowed_ips.any?{|ip| ip.include?(request.ip)} + Rails.logger.warn("Unauthorized API access: #{api_username} ip address: #{request.ip}") + return nil + end + if api_key.user api_key.user if !api_username || (api_key.user.username_lower == api_username.downcase) elsif api_username User.find_by(username_lower: api_username.downcase) - else - nil end end end diff --git a/spec/components/auth/default_current_user_provider_spec.rb b/spec/components/auth/default_current_user_provider_spec.rb index ccad63bbed..99c5e2e96c 100644 --- a/spec/components/auth/default_current_user_provider_spec.rb +++ b/spec/components/auth/default_current_user_provider_spec.rb @@ -31,6 +31,27 @@ describe Auth::DefaultCurrentUserProvider do }.to raise_error(Discourse::InvalidAccess) end + it "raises for a user with a mismatching ip" do + user = Fabricate(:user) + ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1, allowed_ips: ['10.0.0.0/24']) + + expect{ + provider("/?api_key=hello&api_username=#{user.username.downcase}", "REMOTE_ADDR" => "10.1.0.1").current_user + }.to raise_error(Discourse::InvalidAccess) + + end + + it "allows a user with a matching ip" do + user = Fabricate(:user) + ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1, allowed_ips: ['10.0.0.0/24']) + + found_user = provider("/?api_key=hello&api_username=#{user.username.downcase}", + "REMOTE_ADDR" => "10.0.0.22").current_user + + found_user.id.should == user.id + + end + it "finds a user for a correct system api key" do user = Fabricate(:user) ApiKey.create!(key: "hello", created_by_id: -1) From 6b10c4dc547e2f82b05d4cc5a4d3ccd88296fa15 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 20 Nov 2014 15:38:20 +1100 Subject: [PATCH 014/991] add support for hidden api keys, used in hosting scenarios --- app/controllers/admin/api_controller.rb | 2 +- app/models/api_key.rb | 2 +- db/migrate/20141120043401_add_hidden_to_api_keys.rb | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20141120043401_add_hidden_to_api_keys.rb diff --git a/app/controllers/admin/api_controller.rb b/app/controllers/admin/api_controller.rb index 472b4b9c3e..57d9bce4e9 100644 --- a/app/controllers/admin/api_controller.rb +++ b/app/controllers/admin/api_controller.rb @@ -1,7 +1,7 @@ class Admin::ApiController < Admin::AdminController def index - render_serialized(ApiKey.all.to_a, ApiKeySerializer) + render_serialized(ApiKey.where(hidden: false).to_a, ApiKeySerializer) end def regenerate_key diff --git a/app/models/api_key.rb b/app/models/api_key.rb index 099506ebf6..1a197e1b79 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -12,7 +12,7 @@ class ApiKey < ActiveRecord::Base end def self.create_master_key - api_key = ApiKey.find_by(user_id: nil) + api_key = ApiKey.find_by(user_id: nil, hidden: false) if api_key.blank? api_key = ApiKey.create(key: SecureRandom.hex(32), created_by: Discourse.system_user) end diff --git a/db/migrate/20141120043401_add_hidden_to_api_keys.rb b/db/migrate/20141120043401_add_hidden_to_api_keys.rb new file mode 100644 index 0000000000..565baa0504 --- /dev/null +++ b/db/migrate/20141120043401_add_hidden_to_api_keys.rb @@ -0,0 +1,7 @@ +class AddHiddenToApiKeys < ActiveRecord::Migration + def change + change_table :api_keys do |t| + t.boolean :hidden, null: false, default: false + end + end +end From e1be1e5fd9c0907b8bd6e81f36cc925f9d642e18 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 20 Nov 2014 16:08:06 +1100 Subject: [PATCH 015/991] FIX: don't crash from observer if user record is bad --- app/models/search_observer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/search_observer.rb b/app/models/search_observer.rb index 7739801bcb..5d60df8f63 100644 --- a/app/models/search_observer.rb +++ b/app/models/search_observer.rb @@ -66,7 +66,7 @@ class SearchObserver < ActiveRecord::Observer end end if obj.class == User && (obj.username_changed? || obj.name_changed?) - SearchObserver.update_users_index(obj.id, obj.username_lower, obj.name.downcase) + SearchObserver.update_users_index(obj.id, obj.username_lower || '', obj.name ? obj.name.downcase : '') end if obj.class == Topic && obj.title_changed? From 57ef9bb21b9a217ab5d42301d57ae6c198c68c7d Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Wed, 19 Nov 2014 21:46:11 -0800 Subject: [PATCH 016/991] fix pop up composer tips display for mobile --- app/assets/stylesheets/mobile/compose.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/mobile/compose.scss b/app/assets/stylesheets/mobile/compose.scss index c338db0821..463d4b9062 100644 --- a/app/assets/stylesheets/mobile/compose.scss +++ b/app/assets/stylesheets/mobile/compose.scss @@ -6,11 +6,11 @@ } .composer-popup-container { -display: none; +display: none !important; // can be removed if inline JS CSS is removed from composer-popup } .composer-popup { -display: none; +display: none !important; // can be removed if inline JS CSS is removed from composer-popup } #reply-control { From 73041c0e144884f93d35cf3e92b7c2d7418cac28 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 20 Nov 2014 16:47:59 +1100 Subject: [PATCH 017/991] uniqueness constraint is not correct scoping on hidden is not fully correct simpler just to handle it in the DB --- app/models/api_key.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/api_key.rb b/app/models/api_key.rb index 1a197e1b79..514c1ba4b6 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -3,7 +3,6 @@ class ApiKey < ActiveRecord::Base belongs_to :created_by, class_name: User validates_presence_of :key - validates_uniqueness_of :user_id def regenerate!(updated_by) self.key = SecureRandom.hex(32) From 0d6e5470d46d4956df090ca1c67c83e71c2d612f Mon Sep 17 00:00:00 2001 From: cpradio Date: Wed, 19 Nov 2014 17:46:55 -0500 Subject: [PATCH 018/991] FEATURE: Add ?status=deleted querystring Add tests around the ?status=deleted querystring --- lib/topic_query.rb | 10 +++++++++- spec/components/topic_query_spec.rb | 11 +++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/topic_query.rb b/lib/topic_query.rb index a38bd8b67e..0fa638475e 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -233,7 +233,7 @@ class TopicQuery options.reverse_merge!(per_page: SiteSetting.topics_per_page) # Start with a list of all topics - result = Topic + result = Topic.unscoped if @user result = result.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user.id.to_i})") @@ -286,6 +286,7 @@ class TopicQuery notification_level = ?)', @user.id, level) end + require_deleted_clause = true if status = options[:status] case status when 'open' @@ -298,9 +299,16 @@ class TopicQuery result = result.where('topics.visible') when 'invisible' result = result.where('NOT topics.visible') + when 'deleted' + guardian = Guardian.new(@user) + if guardian.is_staff? + result = result.where('topics.deleted_at IS NOT NULL') + require_deleted_clause = false + end end end + result = result.where('topics.deleted_at IS NULL') if require_deleted_clause result = result.where('topics.posts_count <= ?', options[:max_posts]) if options[:max_posts].present? result = result.where('topics.posts_count >= ?', options[:min_posts]) if options[:min_posts].present? diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb index 1774a89035..48c2e3e779 100644 --- a/spec/components/topic_query_spec.rb +++ b/spec/components/topic_query_spec.rb @@ -40,6 +40,17 @@ describe TopicQuery do end + context 'deleted filter' do + it "filters deleted topics correctly" do + topic = Fabricate(:topic, deleted_at: 1.year.ago) + + TopicQuery.new(admin, status: 'deleted').list_latest.topics.size.should == 1 + TopicQuery.new(moderator, status: 'deleted').list_latest.topics.size.should == 1 + TopicQuery.new(user, status: 'deleted').list_latest.topics.size.should == 0 + TopicQuery.new(nil, status: 'deleted').list_latest.topics.size.should == 0 + end + end + context 'category filter' do let(:category) { Fabricate(:category) } From 50de22801f93aa29486b6f50eed005511fba2ba8 Mon Sep 17 00:00:00 2001 From: "Jason W. May" Date: Thu, 20 Nov 2014 09:29:56 -0800 Subject: [PATCH 019/991] API addition: HTTP PATCH support for /groups/xxx: incremental membership changes --- app/controllers/admin/groups_controller.rb | 42 +++++++++-- app/models/group.rb | 4 ++ .../admin/groups_controller_spec.rb | 70 ++++++++++++++++--- 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 21920d8a58..df21a4328a 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -20,18 +20,36 @@ class Admin::GroupsController < Admin::AdminController render json: success_json end - def update - group = Group.find(params[:id].to_i) + def update_patch(group) + raise Discourse::InvalidAccess.new("automatic groups do not permit membership changes") if group.automatic - group.alias_level = params[:group][:alias_level].to_i if params[:group][:alias_level].present? + actions = params[:changes] + Array(actions[:add]).each do |username| + if user = User.find_by_username(username) + group.add(user) + end + end + Array(actions[:delete]).each do |username| + if user = User.find_by_username(username) + group.remove(user) + end + end + + render json: success_json + end + + def update_put(group) + payload = params[:group] + + group.alias_level = payload[:alias_level].to_i if payload[:alias_level].present? + group.visible = payload[:visible] == "true" if group.automatic - # we can only change the alias level on automatic groups + # group rename & membership changes are ignored/prohibited for automatic groups else - group.usernames = params[:group][:usernames] - group.name = params[:group][:name] if params[:group][:name] + group.usernames = payload[:usernames] if payload[:usernames] + group.name = payload[:name] if payload[:name] end - group.visible = params[:group][:visible] == "true" if group.save render json: success_json @@ -40,6 +58,16 @@ class Admin::GroupsController < Admin::AdminController end end + def update + group = Group.find(params[:id].to_i) + + if request.patch? + update_patch(group) + else + update_put(group) + end + end + def create group = Group.new group.name = (params[:group][:name] || '').strip diff --git a/app/models/group.rb b/app/models/group.rb index ff143a9c1f..2a451f192e 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -273,6 +273,10 @@ class Group < ActiveRecord::Base self.users.push(user) end + def remove(user) + self.group_users.where(user: user).each(&:destroy) + end + protected def name_format_validator diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 7c5192ea33..5979523a1e 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -79,18 +79,66 @@ describe Admin::GroupsController do end end - it "is able to update group members" do - user1 = Fabricate(:user) - user2 = Fabricate(:user) - group = Fabricate(:group) + context '.update' do + let (:group) { Fabricate(:group) } - xhr :put, :update, id: group.id, name: 'fred', group: { - name: 'fred', - usernames: "#{user1.username},#{user2.username}" - } + it "is able to update group members" do + user1 = Fabricate(:user) + user2 = Fabricate(:user) - group.reload - group.users.count.should == 2 - group.name.should == 'fred' + xhr :put, :update, id: group.id, name: 'fred', group: { + name: 'fred', + usernames: "#{user1.username},#{user2.username}" + } + + group.reload + group.users.count.should == 2 + group.name.should == 'fred' + end + + context 'incremental' do + before do + @user1 = Fabricate(:user) + group.add(@user1) + group.reload + end + + it "can make incremental adds" do + user2 = Fabricate(:user) + xhr :patch, :update, id: group.id, changes: {add: user2.username} + response.status.should == 200 + group.reload + group.users.count.should eq(2) + end + + it "succeeds silently when adding non-existent users" do + xhr :patch, :update, id: group.id, changes: {add: "nosuchperson"} + response.status.should == 200 + group.reload + group.users.count.should eq(1) + end + + it "can make incremental deletes" do + xhr :patch, :update, id: group.id, changes: {delete: @user1.username} + response.status.should == 200 + group.reload + group.users.count.should eq(0) + end + + it "succeeds silently when removing non-members" do + user2 = Fabricate(:user) + xhr :patch, :update, id: group.id, changes: {delete: user2.username} + response.status.should == 200 + group.reload + group.users.count.should eq(1) + end + + it "cannot patch automatic groups" do + auto_group = Fabricate(:group, name: "auto_group", automatic: true) + + xhr :patch, :update, id: auto_group.id, changes: {add: "bob"} + response.status.should == 403 + end + end end end From 5f4e4de02a93ac27209e173faea42dd8bdf680d2 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 20 Nov 2014 13:42:37 -0500 Subject: [PATCH 020/991] FIX: Make `user_id` uniqueness check pending. It still seems to check but the test fails. cc @SamSaffron --- spec/models/api_key_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/api_key_spec.rb b/spec/models/api_key_spec.rb index 7df92e574c..4a378a5a01 100644 --- a/spec/models/api_key_spec.rb +++ b/spec/models/api_key_spec.rb @@ -8,7 +8,7 @@ describe ApiKey do it { should validate_presence_of :key } - it 'validates uniqueness of user_id' do + pending 'validates uniqueness of user_id' do Fabricate(:api_key) should validate_uniqueness_of(:user_id) end From b8d806ee07564b83151a757d88869520bb19e606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 20 Nov 2014 19:59:20 +0100 Subject: [PATCH 021/991] FEATURE: delete all accounts from this IP in the IP lookup modal --- .../admin/components/ip-lookup.js.es6 | 16 +++++++++++ .../templates/components/ip-lookup.hbs | 28 +++++++++++-------- .../stylesheets/common/admin/admin_base.scss | 7 ++--- app/controllers/admin/users_controller.rb | 20 ++++++++++--- config/locales/client.en.yml | 2 +- config/routes.rb | 1 + .../admin/users_controller_spec.rb | 13 +++++++++ 7 files changed, 67 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index 06a8387be4..cf9829f7e5 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -39,6 +39,22 @@ export default Ember.Component.extend({ hide: function () { this.set("show", false); + }, + + deleteAllOtherAccounts: function() { + var self = this; + this.setProperties({ other_accounts: null, otherAccountsLoading: true }); + + Discourse.ajax("/admin/users/delete-others-with-same-ip.json", { + type: "DELETE", + data: { + "ip": this.get("ip"), + "exclude": this.get("user_id"), + "order": "trust_level DESC" + } + }).then(function() { + self.send("lookup"); + }); } } }); diff --git a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs index 6fd3bea83e..ff002f5f1a 100644 --- a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs @@ -1,11 +1,11 @@ {{#if ip}} {{/if}} {{#if show}}
- {{fa-icon "times"}} + {{fa-icon "times"}}

{{i18n ip_lookup.title}}

{{#if location}} @@ -37,10 +37,18 @@ {{loading-spinner size="small"}} {{/if}} -
{{i18n ip_lookup.other_accounts}} {{other_accounts.length}}
-
- {{#loading-spinner size="small" condition=otherAccountsLoading}} - {{#if other_accounts}} +
+ {{i18n ip_lookup.other_accounts}} + {{other_accounts.length}} + {{#if other_accounts.length}} + + {{/if}} +
+ {{#loading-spinner size="small" condition=otherAccountsLoading}} + {{#if other_accounts.length}} +
@@ -63,11 +71,9 @@ {{/each}}
- {{else}} - {{i18n ip_lookup.no_other_accounts}} - {{/if}} - {{/loading-spinner}} -
+
+ {{/if}} + {{/loading-spinner}}
{{/if}} diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index fc25af6e6a..8ccc201cce 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -82,16 +82,15 @@ td.flaggers td { margin-top: -2px; background-color: $secondary; padding: 12px 12px 5px; - .close { - float: right; - } .other-accounts { - margin: 0; + margin: 5px 0 0; max-height: 200px; overflow: auto; + width: 455px; ul { margin: 0; } li { list-style: none; } + tr td:first-of-type { width: 130px; } } } } diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index ed69ad463f..28f1dbe60c 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -264,10 +264,7 @@ class Admin::UsersController < Admin::AdminController end def sync_sso - unless SiteSetting.enable_sso - render nothing: true, status: 404 - return - end + return render nothing: true, status: 404 unless SiteSetting.enable_sso sso = DiscourseSingleSignOn.parse("sso=#{params[:sso]}&sig=#{params[:sig]}") user = sso.lookup_or_create_user @@ -275,6 +272,21 @@ class Admin::UsersController < Admin::AdminController render_serialized(user, AdminDetailedUserSerializer, root: false) end + def delete_other_accounts_with_same_ip + params.require(:ip) + params.require(:exclude) + params.require(:order) + + user_destroyer = UserDestroyer.new(current_user) + options = { delete_posts: true, block_email: true, block_urls: true, block_ip: true, delete_as_spammer: true } + + AdminUserIndexQuery.new(params).find_users.each do |user| + user_destroyer.destroy(user, options) rescue nil + end + + render json: success_json + end + private def fetch_user diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 16793af595..43363649d9 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -279,7 +279,7 @@ en: organisation: Organization phone: Phone other_accounts: "Other accounts with this IP address:" - no_other_accounts: (none) + delete_all: "Delete all" username: "username" trust_level: "TL" read_time: "read time" diff --git a/config/routes.rb b/config/routes.rb index abcb00680e..fd8c3e357a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,7 @@ Discourse::Application.routes.draw do collection do get "list/:query" => "users#index" get "ip-info" => "users#ip_info" + delete "delete-others-with-same-ip" => "users#delete_other_accounts_with_same_ip" put "approve-bulk" => "users#approve_bulk" delete "reject-bulk" => "users#reject_bulk" end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 767dc021f1..d65359f22c 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -414,6 +414,19 @@ describe Admin::UsersController do end + context "delete_other_accounts_with_same_ip" do + + it "works" do + Fabricate(:user, ip_address: "42.42.42.42") + Fabricate(:user, ip_address: "42.42.42.42") + + UserDestroyer.any_instance.expects(:destroy).twice + + xhr :delete, :delete_other_accounts_with_same_ip, ip: "42.42.42.42", exclude: -1, order: "trust_level DESC" + end + + end + end it 'can sync up sso' do From dd1ebb535bc1f8eddec70294b71a6953b1de2519 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 20 Nov 2014 14:01:48 -0500 Subject: [PATCH 022/991] FIX: Could not download exported data on some sites --- app/models/topic_link_click.rb | 6 ++++++ spec/models/topic_link_click_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/models/topic_link_click.rb b/app/models/topic_link_click.rb index a682808b4c..e06ad2ce7e 100644 --- a/app/models/topic_link_click.rb +++ b/app/models/topic_link_click.rb @@ -31,6 +31,12 @@ class TopicLinkClick < ActiveRecord::Base unless link.present? return args[:url] if args[:url] =~ /^\// + begin + uri = URI.parse(args[:url]) + return args[:url] if uri.host == URI.parse(Discourse.base_url).host + rescue + end + # If we have it somewhere else on the site, just allow the redirect. This is # likely due to a onebox of another topic. link = TopicLink.find_by(url: args[:url]) diff --git a/spec/models/topic_link_click_spec.rb b/spec/models/topic_link_click_spec.rb index 9fd7d1f24a..e6c91b6230 100644 --- a/spec/models/topic_link_click_spec.rb +++ b/spec/models/topic_link_click_spec.rb @@ -79,6 +79,27 @@ describe TopicLinkClick do end end + context "relative urls" do + let(:host) { URI.parse(Discourse.base_url).host } + + it 'returns the url' do + url = TopicLinkClick.create_from(url: '/relative-url', post_id: @post.id, ip: '127.0.0.1') + url.should == "/relative-url" + end + + it 'finds a protocol relative urls with a host' do + url = "//#{host}/relative-url" + redirect = TopicLinkClick.create_from(url: url) + redirect.should == url + end + + it "returns the url if it's on our host" do + url = "http://#{host}/relative-url" + redirect = TopicLinkClick.create_from(url: url) + redirect.should == url + end + end + context 'with a HTTPS version of the same URL' do before do @url = TopicLinkClick.create_from(url: 'https://twitter.com', topic_id: @topic.id, ip: '127.0.0.3') From d4f7db3a1d0800d29b6828ba3189800c2dc07f11 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 20 Nov 2014 12:00:58 -0800 Subject: [PATCH 023/991] UX: hide share button by default --- config/site_settings.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/site_settings.yml b/config/site_settings.yml index 284615fe09..49b54d71f6 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -95,7 +95,7 @@ basic: post_menu: client: true type: list - default: 'like|share|flag|edit|bookmark|delete|admin|reply' + default: 'like|flag|bookmark|edit|share|delete|admin|reply' choices: - like - edit @@ -108,7 +108,7 @@ basic: post_menu_hidden_items: client: true type: list - default: 'edit|delete|admin' + default: 'edit|share|delete|admin' choices: - like - edit From 97a44b4c43419f28abded3ef265acac834ee1ee2 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 20 Nov 2014 12:23:21 -0800 Subject: [PATCH 024/991] post-date can be same size as other post info --- app/assets/stylesheets/desktop/topic-post.scss | 2 +- app/assets/stylesheets/mobile/topic-post.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 89c505f358..52db9ca6a6 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -1004,7 +1004,7 @@ a.attachment:before { .post-info { display: inline-block; float: right; - font-size: 12px; + font-size: 13px; margin-top: 1px; a {color: scale-color($primary, $lightness: 50%);} } diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 962691d38d..068018ff97 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -475,7 +475,7 @@ span.highlighted { } .post-info { float: right; - font-size: 12px; + font-size: 13px; margin: 4px 10px 0 0; .edits { margin-right: 5px; } } From 4a7e0416d118b49ca7d48d51238cdcc97152eade Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 21 Nov 2014 08:32:16 +1100 Subject: [PATCH 025/991] FIX: expire stylesheet cache on save --- app/models/color_scheme.rb | 3 +++ app/models/site_customization.rb | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/models/color_scheme.rb b/app/models/color_scheme.rb index f79db403c9..d4a1b02dec 100644 --- a/app/models/color_scheme.rb +++ b/app/models/color_scheme.rb @@ -1,3 +1,5 @@ +require_dependency 'sass/discourse_stylesheets' + class ColorScheme < ActiveRecord::Base attr_accessor :is_base @@ -96,6 +98,7 @@ class ColorScheme < ActiveRecord::Base def publish_discourse_stylesheet MessageBus.publish("/discourse_stylesheet", self.name) + DiscourseStylesheets.cache.clear end end diff --git a/app/models/site_customization.rb b/app/models/site_customization.rb index b0ca624591..47d224b6e8 100644 --- a/app/models/site_customization.rb +++ b/app/models/site_customization.rb @@ -1,4 +1,5 @@ require_dependency 'sass/discourse_sass_compiler' +require_dependency 'sass/discourse_stylesheets' class SiteCustomization < ActiveRecord::Base ENABLED_KEY = '7e202ef2-56d7-47d5-98d8-a9c8d15e57dd' @@ -43,6 +44,7 @@ class SiteCustomization < ActiveRecord::Base end MessageBus.publish "/header-change/#{key}", header if header_changed? MessageBus.publish "/footer-change/#{key}", footer if footer_changed? + DiscourseStylesheets.cache.clear end after_destroy do From d16f125f89b5aa3496e08d35bc97b2e9b16e36fc Mon Sep 17 00:00:00 2001 From: fwoelm Date: Thu, 20 Nov 2014 15:34:09 -0700 Subject: [PATCH 026/991] Update DEVELOPER-ADVANCED.md Merged installation instruction for libpq-dev into step 4. --- docs/DEVELOPER-ADVANCED.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/DEVELOPER-ADVANCED.md b/docs/DEVELOPER-ADVANCED.md index c5293a0188..8c447a51fb 100644 --- a/docs/DEVELOPER-ADVANCED.md +++ b/docs/DEVELOPER-ADVANCED.md @@ -12,8 +12,7 @@ Note: If you are developing on a Mac, you will probably want to look at [these i 2. Install and configure Redis 2+. 1. Run `redis-server -v` to see if you already have it. 3. Install ImageMagick -4. Install libxml2, g++, and make. -5. Install libpq-dev. +4. Install libxml2, libpq-dev, g++, and make. 6. Install Ruby 2.1.3 and Bundler. 7. Clone the project and bundle. From c41af0aa7256ba6d2a938f73d222e57fc02d2dca Mon Sep 17 00:00:00 2001 From: fwoelm Date: Thu, 20 Nov 2014 15:35:17 -0700 Subject: [PATCH 027/991] Update DEVELOPER-ADVANCED.md Fixed numbering of list items. --- docs/DEVELOPER-ADVANCED.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DEVELOPER-ADVANCED.md b/docs/DEVELOPER-ADVANCED.md index 8c447a51fb..4d531a9de1 100644 --- a/docs/DEVELOPER-ADVANCED.md +++ b/docs/DEVELOPER-ADVANCED.md @@ -13,8 +13,8 @@ Note: If you are developing on a Mac, you will probably want to look at [these i 1. Run `redis-server -v` to see if you already have it. 3. Install ImageMagick 4. Install libxml2, libpq-dev, g++, and make. -6. Install Ruby 2.1.3 and Bundler. -7. Clone the project and bundle. +5. Install Ruby 2.1.3 and Bundler. +6. Clone the project and bundle. ## Before you start Rails From ced35cb3bbe821a3a957c7697ad766a118ab0ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 21 Nov 2014 00:25:44 +0100 Subject: [PATCH 028/991] FEATURE: don't limit registration from an IP address if a staff member has that IP address --- config/locales/server.en.yml | 2 +- lib/spam_handler.rb | 7 +++++++ spec/components/spam_handler_spec.rb | 17 +++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index ad5d9e1224..09e542ca8f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -941,7 +941,7 @@ en: staff_like_weight: "How much extra weighting factor to give staff likes." levenshtein_distance_spammer_emails: "When matching spammer emails, number of characters difference that will still allow a fuzzy match." - max_new_accounts_per_registration_ip: "If there are already (n) trust level 0 accounts from this IP (and none at TL2 or higher), stop accepting new signups from that IP." + max_new_accounts_per_registration_ip: "If there are already (n) trust level 0 accounts from this IP (and none is a staff member or at TL2 or higher), stop accepting new signups from that IP." reply_by_email_enabled: "Enable replying to topics via email." reply_by_email_address: "Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" diff --git a/lib/spam_handler.rb b/lib/spam_handler.rb index e6bfdf3419..1df501babd 100644 --- a/lib/spam_handler.rb +++ b/lib/spam_handler.rb @@ -9,6 +9,13 @@ class SpamHandler return false if tl2_plus_accounts_with_same_ip > 0 + staff_user_ids = Group[:staff].user_ids - [-1] + staff_members_with_same_ip = User.where(id: staff_user_ids) + .where("ip_address = ?", ip_address.to_s) + .count + + return false if staff_members_with_same_ip > 0 + tl0_accounts_with_same_ip = User.unscoped .where(trust_level: TrustLevel[0]) .where("ip_address = ?", ip_address.to_s) diff --git a/spec/components/spam_handler_spec.rb b/spec/components/spam_handler_spec.rb index 532eda0723..1f6c02770b 100644 --- a/spec/components/spam_handler_spec.rb +++ b/spec/components/spam_handler_spec.rb @@ -22,13 +22,26 @@ describe SpamHandler do -> { Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) }.should raise_error(ActiveRecord::RecordInvalid) end - it "only limit new registrations from an IP if *all* the users from that IP are TL1 or TL0" do + it "doesn't limit registrations since there is a TL2+ user with that IP" do # setup SiteSetting.stubs(:max_new_accounts_per_registration_ip).returns(0) Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[2]) - # should not limit registrations since there is a TL2 user with that IP + # should not limit registration + SiteSetting.stubs(:max_new_accounts_per_registration_ip).returns(1) + Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) + end + + it "doesn't limit registrations since there is a staff member with that IP" do + # setup + SiteSetting.stubs(:max_new_accounts_per_registration_ip).returns(0) + Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) + Fabricate(:moderator, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) + + Group.refresh_automatic_groups!(:staff) + + # should not limit registration SiteSetting.stubs(:max_new_accounts_per_registration_ip).returns(1) Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) end From acb8030d126a21d9f1f41c8a6eb30bd3230f7fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 21 Nov 2014 00:31:22 +0100 Subject: [PATCH 029/991] add confirm dialog when deleting other accounts with same IP address --- .../admin/components/ip-lookup.js.es6 | 23 +++++++++++-------- config/locales/client.en.yml | 1 + 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index cf9829f7e5..b7dbaf764b 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -43,17 +43,20 @@ export default Ember.Component.extend({ deleteAllOtherAccounts: function() { var self = this; - this.setProperties({ other_accounts: null, otherAccountsLoading: true }); - - Discourse.ajax("/admin/users/delete-others-with-same-ip.json", { - type: "DELETE", - data: { - "ip": this.get("ip"), - "exclude": this.get("user_id"), - "order": "trust_level DESC" + bootbox.confirm(I18n.t("ip_lookup.confirm_delete_other_accounts"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { + if (confirmed) { + self.setProperties({ other_accounts: null, otherAccountsLoading: true }); + Discourse.ajax("/admin/users/delete-others-with-same-ip.json", { + type: "DELETE", + data: { + "ip": self.get("ip"), + "exclude": self.get("user_id"), + "order": "trust_level DESC" + } + }).then(function() { + self.send("lookup"); + }); } - }).then(function() { - self.send("lookup"); }); } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 43363649d9..664e6bda4c 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -285,6 +285,7 @@ en: read_time: "read time" topics_entered: "topics entered" post_count: "# posts" + confirm_delete_other_accounts: "Are you sure you want to delete these accounts?" user: said: "{{username}}:" From 0cbfb43adbf9ec2d00a6bb35ecc40a6c0cd3fe94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 21 Nov 2014 00:32:06 +0100 Subject: [PATCH 030/991] UX: use warning (nuke?) icon instead of trash icon --- .../javascripts/discourse/templates/components/ip-lookup.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs index ff002f5f1a..979ee6b4da 100644 --- a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs @@ -42,7 +42,7 @@ {{other_accounts.length}} {{#if other_accounts.length}} {{/if}} From 508efe302958e64a40e271b2da0e9b90fe06c8dc Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 21 Nov 2014 11:08:20 +1100 Subject: [PATCH 031/991] FIX: PM title not editable --- app/assets/javascripts/discourse/models/topic.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js index 08f669aa49..817376fbf2 100644 --- a/app/assets/javascripts/discourse/models/topic.js +++ b/app/assets/javascripts/discourse/models/topic.js @@ -207,9 +207,15 @@ Discourse.Topic = Discourse.Model.extend({ // Don't save unless we can if (!this.get('details.can_edit')) return; + var data = { title: this.get('title') }; + + if(this.get('category')){ + data.category_id = this.get('category.id'); + } + return Discourse.ajax(this.get('url'), { type: 'PUT', - data: { title: this.get('title'), category_id: this.get('category.id') } + data: data }); }, From 034967328cc06e4b0a2cee805fcbd1e16fbd4644 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 21 Nov 2014 11:36:14 +1100 Subject: [PATCH 032/991] FIX: sso with avatar override may fail on first account creation --- app/models/discourse_single_sign_on.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/discourse_single_sign_on.rb b/app/models/discourse_single_sign_on.rb index 0476c384f5..610f6aafe4 100644 --- a/app/models/discourse_single_sign_on.rb +++ b/app/models/discourse_single_sign_on.rb @@ -134,6 +134,10 @@ class DiscourseSingleSignOn < SingleSignOn upload = Upload.create_for(user.id, tempfile, "external-avatar." + ext, File.size(tempfile.path), { origin: avatar_url }) user.uploaded_avatar_id = upload.id + unless user.user_avatar + user.build_user_avatar + end + if !user.user_avatar.contains_upload?(upload.id) user.user_avatar.custom_upload_id = upload.id end From 479d63cf1c11a94c13eea0fc3bfad651c53c2107 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 20 Nov 2014 16:36:26 -0800 Subject: [PATCH 033/991] I hate all these periods so much --- config/locales/client.en.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 664e6bda4c..5494d88e58 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -387,30 +387,30 @@ en: email: title: "Email" - instructions: "Never shown to the public." - ok: "We will email you to confirm." - invalid: "Please enter a valid email address." - authenticated: "Your email has been authenticated by {{provider}}." + instructions: "Never shown to the public" + ok: "We will email you to confirm" + invalid: "Please enter a valid email address" + authenticated: "Your email has been authenticated by {{provider}}" frequency: "We'll only email you if we haven't seen you recently and you haven't already seen the thing we're emailing you about." name: title: "Name" - instructions: "Your full name (optional)." - too_short: "Your name is too short." - ok: "Your name looks good." + instructions: "Your full name (optional)" + too_short: "Your name is too short" + ok: "Your name looks good" username: title: "Username" - instructions: "Unique, no spaces, short." - short_instructions: "People can mention you as @{{username}}." - available: "Your username is available." - global_match: "Email matches the registered username." + instructions: "Unique, no spaces, short" + short_instructions: "People can mention you as @{{username}}" + available: "Your username is available" + global_match: "Email matches the registered username" global_mismatch: "Already registered. Try {{suggestion}}?" not_available: "Not available. Try {{suggestion}}?" - too_short: "Your username is too short." - too_long: "Your username is too long." + too_short: "Your username is too short" + too_long: "Your username is too long" checking: "Checking username availability..." - enter_email: 'Username found. Enter matching email.' - prefilled: "Email matches this registered username." + enter_email: 'Username found; enter matching email' + prefilled: "Email matches this registered username" locale: title: "Interface language" From baf8a5cac20163ed28094b463d6bd9c80877ef5e Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 21 Nov 2014 11:46:10 +1100 Subject: [PATCH 034/991] Revert "UX: hide share button by default" This reverts commit d4f7db3a1d0800d29b6828ba3189800c2dc07f11. --- config/site_settings.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/site_settings.yml b/config/site_settings.yml index 49b54d71f6..284615fe09 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -95,7 +95,7 @@ basic: post_menu: client: true type: list - default: 'like|flag|bookmark|edit|share|delete|admin|reply' + default: 'like|share|flag|edit|bookmark|delete|admin|reply' choices: - like - edit @@ -108,7 +108,7 @@ basic: post_menu_hidden_items: client: true type: list - default: 'edit|share|delete|admin' + default: 'edit|delete|admin' choices: - like - edit From df99591f10c8c795b947582d64a1d6152f5db6f4 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 21 Nov 2014 11:52:46 +1100 Subject: [PATCH 035/991] FEATURE: hide bookmarks but always show if post is bookmarked --- app/assets/javascripts/discourse/views/post-menu.js.es6 | 4 ++++ config/site_settings.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/views/post-menu.js.es6 b/app/assets/javascripts/discourse/views/post-menu.js.es6 index de2ddd298b..4925a38c09 100644 --- a/app/assets/javascripts/discourse/views/post-menu.js.es6 +++ b/app/assets/javascripts/discourse/views/post-menu.js.es6 @@ -97,6 +97,10 @@ export default Discourse.View.extend({ } } + if(post.get("bookmarked")){ + hiddenButtons.removeObject("bookmark"); + } + var yours = post.get('yours'); Discourse.SiteSettings.post_menu.split("|").forEach(function(i) { var creator = self["buttonFor" + i.replace(/\+/, '').capitalize()]; diff --git a/config/site_settings.yml b/config/site_settings.yml index 284615fe09..8ce50b26fd 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -108,7 +108,7 @@ basic: post_menu_hidden_items: client: true type: list - default: 'edit|delete|admin' + default: 'bookmark|edit|delete|admin' choices: - like - edit From e04cc1c9f5ffa48be8489572939cf013e381d1c7 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Fri, 21 Nov 2014 18:09:29 +0530 Subject: [PATCH 036/991] Update Translations --- config/locales/client.cs.yml | 17 +- config/locales/client.da.yml | 29 +-- config/locales/client.de.yml | 23 +- config/locales/client.es.yml | 18 +- config/locales/client.fi.yml | 15 -- config/locales/client.fr.yml | 19 +- config/locales/client.he.yml | 15 -- config/locales/client.id.yml | 7 - config/locales/client.it.yml | 28 +- config/locales/client.ja.yml | 14 - config/locales/client.ko.yml | 40 +-- config/locales/client.nb_NO.yml | 30 +-- config/locales/client.nl.yml | 14 - config/locales/client.pl_PL.yml | 32 +-- config/locales/client.pt.yml | 12 - config/locales/client.pt_BR.yml | 15 -- config/locales/client.ru.yml | 27 +- config/locales/client.sq.yml | 36 +-- config/locales/client.sv.yml | 242 ++++++++++++++++-- config/locales/client.uk.yml | 11 - config/locales/client.zh_CN.yml | 20 +- config/locales/client.zh_TW.yml | 15 -- config/locales/server.es.yml | 39 +++ config/locales/server.fi.yml | 3 - config/locales/server.he.yml | 3 - config/locales/server.it.yml | 3 - config/locales/server.ko.yml | 166 +++++++++++- config/locales/server.ru.yml | 5 + config/locales/server.sq.yml | 43 ++-- config/locales/server.zh_CN.yml | 3 +- public/503.sq.html | 2 +- .../lib/discourse_imgur/locale/server.ko.yml | 6 +- 32 files changed, 575 insertions(+), 377 deletions(-) diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index fcbdcd1958..a49d1f90d0 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -267,7 +267,6 @@ cs: location_not_found: (neznámá) organisation: Organizace phone: Telefon - no_other_accounts: (žádné) user: said: "{{username}}:" profile: "Profil" @@ -351,27 +350,14 @@ cs: title: "Pozadí profilu" email: title: "Emailová adresa" - instructions: "Nikdy nebude zveřejněno." - invalid: "Prosím zadejte platnou emailovou adresu." - authenticated: "Vaše emailová adresa byla autorizována přes službu {{provider}}." frequency: "Budeme vás informovat emailem pouze pokud jste se na našem webu dlouho neukázali a pokud jste obsah, o kterém vás chceme informovat, doposud neviděli." name: title: "Jméno" - too_short: "Vaše jméno je příliš krátké." - ok: "Vaše jméno vypadá dobře" username: title: "Uživatelské jméno" - instructions: "Unikátní, bez mezer, krátké." - short_instructions: "Ostatní vás mohou zmínit pomocí @{{username}}." - available: "Toto uživatelské jméno je volné." - global_match: "Emailová adresa odpovídá registrovaného uživatelskému jménu." global_mismatch: "již zaregistrováno. Co třeba {{suggestion}}?" not_available: "Není k dispozici. Co třeba {{suggestion}}?" - too_short: "Vaše uživatelské jméno je příliš krátké." - too_long: "Vaše uživatelské jméno je příliš dlouhé." checking: "Zjišťuji, zda je uživatelské jméno volné..." - enter_email: 'Uživatelské jméno nalezeno. Zadejte odpovídající emailovou adresu.' - prefilled: "Emailová adresa odpovídá registrovaného uživatelskému jménu." locale: title: "Jazyk rozhraní" instructions: "Jazyk uživatelského prostředí. Změna obnoví stránku." @@ -739,6 +725,7 @@ cs: top: "Nejsou tu žádná další populární témata." topic: filter_to: "{{post_count}} příspěvků v tématu" + create: 'Nové téma' create_long: 'Vytvořit nové téma' private_message: 'Vytvořit soukromou konverzaci' list: 'Témata' @@ -901,6 +888,7 @@ cs: one: "Je zobrazen pouze 1 příspěvek" few: "Jsou zobrazeny pouze {{count}} příspěvky" other: "Je zobrazeno pouze {{count}} příspěvků" + cancel: "Zrušit filtr" split_topic: title: "Rozdělit téma" action: "do nového téma" @@ -1154,6 +1142,7 @@ cs: general: 'Základní' settings: 'Nastavení' delete: 'Smazat kategorii' + create: 'Nová kategorie' save: 'Uložit kategorii' creation_error: Během vytváření nové kategorie nastala chyba. save_error: Během ukládání kategorie nastala chyba. diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index 2009b810ff..80b608b2cd 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -90,6 +90,7 @@ da: facebook: 'del dette link på Facebook' google+: 'del dette link på Google+' email: 'send dette link i en e-mail' + topic_admin_menu: "administrationshandlinger på emne" edit: 'redigér titel og kategori for dette emne' not_implemented: "Beklager, denne feature er ikke blevet implementeret endnu." no_value: "Nej" @@ -123,6 +124,7 @@ da: daily: "dagligt" weekly: "ugentligt" every_two_weeks: "hver anden uge" + max_of_count: "max af {{count}}" character_count: one: "{{count}} tegn" other: "{{count}} tegn" @@ -138,6 +140,7 @@ da: all_time: "Alt" last_7_days: "De sidste 7 Dage" topic_count: "Emner" + post_count: "Indlæg" user_count: "Brugere" bookmarks: not_logged_in: "Beklager, du skal været logget ind for at bogmærke indlæg" @@ -242,7 +245,7 @@ da: location_not_found: (ukendt) organisation: Organisation phone: Telefon - no_other_accounts: (ingen) + other_accounts: "Andre konti med denne IP adresse" user: said: "{{username}}:" profile: "Profil" @@ -259,6 +262,7 @@ da: trust_level: "Tillidsniveau" notifications: "Underretninger" dismiss_notifications: "Marker alle som læst" + dismiss_notifications_tooltip: "Marker alle ulæste meddelelser som læst" disable_jump_reply: "Spring ikke til dit nye indlæg efter du har svaret" dynamic_favicon: "Vis indkommende underretninger i favicon" edit_history_public: "Lad andre brugere se mine tidligere revisioner" @@ -276,6 +280,7 @@ da: watched_categories: "Overvåget" watched_categories_instructions: "Du overvåger automatisk alle emner i disse kategorier" tracked_categories: "Fulgt" + tracked_categories_instructions: "Du vil automatisk følge alle nye emner i disse kategorier. En optælling af ulæste og nye indlæg vises ved emnet." muted_categories: "Ignoreret" muted_categories_instructions: "Du ignorerer automatisk alle emner i disse kategorier" delete_account: "Slet min konto" @@ -283,7 +288,9 @@ da: deleted_yourself: "Din konto er nu slettet." delete_yourself_not_allowed: "Du kan ikke slette din konto lige nu. Kontakt en administrator for at få din konto slettet." unread_message_count: "Beskeder" + admin_delete: "Slet" staff_counters: + flags_given: "hjælpsomme markeringer" flagged_posts: "markerede indlæg" deleted_posts: "slettede indlæg" suspensions: "suspenderinger" @@ -326,28 +333,14 @@ da: instructions: "Profil baggrunde vil blive centrerede og have en standard bredde på 850 pixels" email: title: "E-mail" - instructions: "Vis aldrig offentligt" - ok: "Vi sender en email for at bekræfte" - invalid: "Skriv venligst en gyldig e-mail-adresse." - authenticated: "Din e-mail er bekræftet af {{provider}}." frequency: "Vi sender dig kun e-mail, hvis du ikke har været på siden for nylig, og du ikke allerede har set de ting vi ville e-mail’e dig om." name: title: "Navn" - instructions: "Dit fulde navn (valgfrit)" - too_short: "Dit navn er for kort." - ok: "Dit navn ser fint ud." username: title: "Brugernavn" - short_instructions: "Andre brugere kan referere til dig som @{{username}}." - available: "Brugernavnet er tilgængeligt." - global_match: "E-mail-adressen matcher det registrerede brugernavn." global_mismatch: "Allerede registreret. Prøv {{suggestion}}?" not_available: "Ikke ledigt. Prøv {{suggestion}}?" - too_short: "Dit brugernavn er for kort." - too_long: "Dit brugernavn er for langt." checking: "Kontrollerer om brugernavnet er ledigt…" - enter_email: 'Brugernavn fundet. Skriv den tilhørende e-mail-adresse.' - prefilled: "E-mail-adressen matcher det registrerede brugernavn." locale: title: "sprog" instructions: "Brugerinterface sprog. Det skifter når de reloader siden." @@ -565,6 +558,8 @@ da: reply_here: "Svar her" reply: "Svar" cancel: "Annullér" + create_topic: "Nyt emne" + create_pm: "Privat besked" title: "Eller tryk Ctrl+Enter" users_placeholder: "Tilføj bruger" title_placeholder: "Hvad handler diskussionen om i korte træk?" @@ -679,6 +674,8 @@ da: hot: "Der er ingen populære emner." category: "Der er ingen emner i kategorien {{category}}." top: "Der er ingen top emner" + educate: + starred: '

Dine favoritemner optræder her.

For at gøre et emne til favorit, kan du brug: :

  • ved siden af emnets titel
  • stjerne-knappen ved bunden af hvert emne
' bottom: latest: "Der er ikke flere populære emner." hot: "There are no more hot topics." @@ -691,6 +688,7 @@ da: top: "Der er ikke flere top emner" topic: filter_to: "Vis {{post_count}} indlæg i emnet" + create: 'Nyt emne' create_long: 'Opret et nyt emne i debatten' private_message: 'Start en privat samtale' list: 'Emner' @@ -1064,6 +1062,7 @@ da: general: 'Overordnet' settings: 'Indstillinger' delete: 'Slet kategori' + create: 'Ny kategori' save: 'Gem kategori' creation_error: Der opstod en fejl under oprettelsen af kategorien. save_error: Der opstod en fejl da kategorien skulle gemmes. diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index 7eff1cae34..3bf5395b1b 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -246,7 +246,7 @@ de: location_not_found: (unbekannt) organisation: Organisation phone: Telefon - no_other_accounts: (keine) + other_accounts: "Andere Konten mit dieser IP-Adresse:" user: said: "{{username}}:" profile: "Profil" @@ -337,29 +337,14 @@ de: instructions: "Hintergrundbilder werden zentriert und haben eine Standardbreite von 590px." email: title: "E-Mail" - instructions: "Wird niemals öffentlich angezeigt." - ok: "Wir werden dir eine E-Mail zur Bestätigung senden." - invalid: "Bitte gib eine gültige E-Mail-Adresse ein." - authenticated: "Deine E-Mail-Adresse wurde von {{provider}} bestätigt." frequency: "Wir werden dir nur E-Mails senden, wenn wir dich kürzlich nicht gesehen haben und wenn du die betroffenen Inhalte noch nicht gesehen hast." name: title: "Name" - instructions: "Dein vollständiger Name (optional)." - too_short: "Dein Name ist zu kurz." - ok: "Dein Name schaut gut aus." username: title: "Benutzername" - instructions: "Eindeutig, keine Leerzeichen, kurz." - short_instructions: "Andere Benutzer können dich mit @{{username}} erwähnen." - available: "Dein Benutzername ist verfügbar." - global_match: "Die E-Mail-Adresse stimmt mit dem registrierten Benutzernamen überein." global_mismatch: "Bereits registriert. Wie wäre es mit {{suggestion}}?" not_available: "Nicht verfügbar. Wie wäre es mit {{suggestion}}?" - too_short: "Dein Benutzername ist zu kurz." - too_long: "Dein Benutzername ist zu lang." checking: "Verfügbarkeit wird geprüft..." - enter_email: 'Benutzername gefunden. Gib die zugehörige E-Mail-Adresse ein.' - prefilled: "Die E-Mail-Adresse passt zu diesem registrierten Benutzernamen." locale: title: "Oberflächensprache" instructions: "Die Sprache der Forumsoberfläche. Diese Änderung tritt nach dem Neuladen der Seite in Kraft." @@ -574,7 +559,7 @@ de: drafts_offline: "Entwürfe offline" min_length: need_more_for_title: "im Titel fehlen noch {{n}} Zeichen" - need_more_for_reply: "noch {{n}}" + need_more_for_reply: "noch {{n}} Zeichen" error: title_missing: "Titel ist erforderlich" title_too_short: "Titel muss mindestens {{min}} Zeichen lang sein" @@ -588,6 +573,7 @@ de: reply: "Antworten" cancel: "Abbrechen" create_topic: "Thema erstellen" + create_pm: "Private Nachricht" title: "Oder drücke Strg+Eingabetaste" users_placeholder: "Benutzer hinzufügen" title_placeholder: "Um was geht es in dieser Diskussion? Schreib einen kurzen Satz." @@ -1176,6 +1162,7 @@ de: title: "Stummgeschaltet" description: "Du erhältst keine Benachrichtigungen über neue Themen in diesen Kategorien und sie werden nicht in deiner Liste ungelesener Themen aufscheinen." flagging: + title: 'Danke für deine Mithilfe!' private_reminder: 'Meldungen sind vertraulich und nur für Mitarbeiter sichtbar' action: 'Beitrag melden' take_action: "Reagieren" @@ -1199,6 +1186,7 @@ de: more: "{{n}} weitere..." left: "{{n}} übrig" flagging_topic: + title: "Danke für deine Mithilfe!" action: "Thema melden" notify_action: "Private Nachricht" topic_map: @@ -1601,6 +1589,7 @@ de: sent_test: "Gesendet!" delivery_method: "Versandmethode" preview_digest: "Vorschau auf Neuigkeiten anzeigen" + preview_digest_desc: "Vorschau auf Neuigkeits-Mails anzeigen. Diese werden wöchentlich an inaktive Benutzer gesendet." refresh: "Aktualisieren" format: "Format" html: "HTML" diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index 2382db1f7c..19d81539c6 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -246,7 +246,7 @@ es: location_not_found: (desconocido) organisation: Organización phone: Teléfono - no_other_accounts: (ninguna) + other_accounts: "Otras cuentas con esta dirección IP:" user: said: "{{username}}:" profile: "Perfil" @@ -337,29 +337,14 @@ es: instructions: "Imágenes de fondo serán centrados y tendrán un ancho por default de 590px." email: title: "E-mail" - instructions: "Nunca se mostrará públicamente." - ok: "Te enviaremos un email para confirmar." - invalid: "Por favor ingresa una dirección de e-mail válida." - authenticated: "Tu e-mail ha sido autenticado por {{provider}}." frequency: "Sólo te enviaremos e-mails si no te hemos visto recientemente y todavía no has visto lo que te estamos enviando." name: title: "Nombre" - instructions: "Tu nombre completo (opcional)" - too_short: "Tu nombre es demasiado corto." - ok: "Tu nombre es válido." username: title: "Nombre de usuario" - instructions: "Debe ser único, sin espacios y conciso." - short_instructions: "La gente puede mencionarte como @{{username}}." - available: "Tu nombre de usuario está disponible." - global_match: "El e-mail coincide con el nombre de usuario registrado." global_mismatch: "Ya está registrado. ¿Prueba {{suggestion}}?" not_available: "No disponible. ¿Prueba {{suggestion}}?" - too_short: "Tu nombre de usuario es demasiado corto." - too_long: "Tu nombre de usuario es demasiado largo." checking: "Comprobando la disponibilidad del nombre de usuario..." - enter_email: 'Nombre de usuario encontrado. Por favor, Introduce el e-mail correspondiente.' - prefilled: "El e-mail coincide con el nombre de usuario registrado." locale: title: "Idioma de la interfaz" instructions: "El idioma de la interfaz. Cambiará cuando recargues la página." @@ -588,6 +573,7 @@ es: reply: "Responder" cancel: "Cancelar" create_topic: "Crear tema" + create_pm: "Mensaje Privado" title: "O pulsa Ctrl+Intro" users_placeholder: "Añadir usuario" title_placeholder: "En una frase breve, ¿de qué trata este tema?" diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index f58c8625e6..4223da7375 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -246,7 +246,6 @@ fi: location_not_found: (tuntematon) organisation: Yritys phone: Puhelin - no_other_accounts: (ei mitään) user: said: "{{username}}:" profile: "Profiili" @@ -335,28 +334,14 @@ fi: instructions: "Taustakuvan leveys on 590 pikseliä." email: title: "Sähköposti" - instructions: "Ei tule julkiseksi." - invalid: "Ole hyvä ja anna toimiva sähköpostiosoite." - authenticated: "{{provider}} on todentanut sähköpostisi." frequency: "Lähetämme sähköpostia vain aiheista, joita et ole nähnyt, ja kun emme ole nähneet sinua viime aikoina." name: title: "Nimi" - instructions: "Koko nimesi (valinnainen)" - too_short: "Nimesi on liian lyhyt." - ok: "Nimesi vaikuttaa hyvältä." username: title: "Käyttäjätunnus" - instructions: "Uniikki, lyhyt ja ilman välilyöntejä." - short_instructions: "Muut käyttäjät voivat viitata sinuun nimellä @{{username}}." - available: "Käyttäjätunnuksesi on vapaana." - global_match: "Sähköposti vastaa rekisteröityä käyttäjänimeä." global_mismatch: "Nimi on jo käytössä. Kokeile {{suggestion}}?" not_available: "Ei saatavilla. Kokeile {{suggestion}}?" - too_short: "Käyttäjätunnus on liian lyhyt." - too_long: "Käyttäjätunnus on liian pitkä." checking: "Tarkistetaan käyttäjätunnusta..." - enter_email: 'Käyttäjänimi löydetty. Kirjoita sitä vastaava sähköpostiosoite.' - prefilled: "Sähköposti vastaa tätä käyttäjänimeä." locale: title: "Käyttöliittymän kieli" instructions: "Käyttöliittymän kieli. Kieli vaihtuu sivun uudelleen lataamisen yhteydessä." diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index bab5013563..2697f82221 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -246,7 +246,7 @@ fr: location_not_found: (inconnu) organisation: Société phone: Téléphone - no_other_accounts: (aucun) + other_accounts: "Autres comptes avec cette adresse IP :" user: said: "{{username}} :" profile: "Profil" @@ -289,6 +289,7 @@ fr: deleted_yourself: "Votre compte a été supprimé avec succès." delete_yourself_not_allowed: "Vous ne pouvez pas supprimer votre compte maintenant. Contactez un administrateur pour faire supprimer votre compte pour vous." unread_message_count: "Messages" + admin_delete: "Supprimer" staff_counters: flags_given: "signalements obligeants" flagged_posts: "messages signalés" @@ -336,29 +337,14 @@ fr: instructions: "Les images d'arrière plan seront centrées avec une taille par défaut de 590 pixels." email: title: "Courriel" - instructions: "Ne sera jamais visible publiquement." - ok: "On vous envoie un mail pour confirmer." - invalid: "Merci de saisir une adresse de courriel valide." - authenticated: "Votre adresse de courriel a été authentifiée par {{provider}}." frequency: "Nous vous envoyons des courriels contenant uniquement des informations que vous n'avez pas déjà vues lors d'une précédente connexion." name: title: "Nom" - instructions: "Votre nom complet (facultatif)." - too_short: "Votre nom est trop court." - ok: "Votre nom semble correct." username: title: "Pseudo" - instructions: "Unique, sans espace, court." - short_instructions: "Les utilisateurs peuvent vous mentionner avec @{{username}}." - available: "Votre pseudo est disponible." - global_match: "L'adresse de courriel correspond au pseudo enregistré." global_mismatch: "Déjà enregistré. Essayez {{suggestion}} ?" not_available: "Non disponible. Essayez {{suggestion}} ?" - too_short: "Votre pseudo est trop court." - too_long: "Votre pseudo est trop long." checking: "Vérification de la disponibilité de votre pseudo..." - enter_email: 'Pseudo trouvé. Entrez l''adresse de courriel correspondante.' - prefilled: "L'adresse de courriel correspond à ce pseudo enregistré." locale: title: "Langue de l'interface" instructions: "Langage de votre interface. Cette dernière changera lorsque vous actualiserez la page." @@ -586,6 +572,7 @@ fr: reply_here: "Répondre ici" reply: "Répondre" cancel: "Annuler" + create_pm: "Message privé" title: "ou appuyez sur Ctrl+Entrée" users_placeholder: "Ajouter un utilisateur" title_placeholder: "Quel est votre sujet en une phrase descriptive ?" diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml index 037dd8339f..f05898e173 100644 --- a/config/locales/client.he.yml +++ b/config/locales/client.he.yml @@ -246,7 +246,6 @@ he: location_not_found: (לא ידוע) organisation: ארגון phone: טלפון - no_other_accounts: (ללא) user: said: "{{username}}:" profile: "פרופיל" @@ -336,28 +335,14 @@ he: instructions: "תמונות רקע ימורכזו ויוצגו עם ברירת מחדל של רוחב 590px." email: title: "דואר אלקטרוני" - instructions: "לעולם לא מוצג לציבור." - invalid: "בבקשה הכנס כתובת דואר אלקטרוני חוקית" - authenticated: "כתובת הדואר האלקטרוני שלך אושרה על ידי {{provider}}." frequency: "נשלח לך דואר אלקטרוני רק אם לא ראינו אותך לאחרונה ועדיין לא ראית את מה שאנחנו רוצים לשלוח" name: title: "שם" - instructions: "שמך המלא (רשות)." - too_short: "השם שלך קצר מידי." - ok: "השם נראה טוב." username: title: "שם משתמש" - instructions: "ייחדוי, ללא רווחים, קצר." - short_instructions: "אנשים יכולים לאזכר אותך כ @{{username}}." - available: "שם המשתמש שלך פנוי." - global_match: "הדואר האלקטרוני תואם את שם המשתמש הרשום" global_mismatch: "כבר רשום. נסה {{suggestion}}?" not_available: "לא זמין. נסה {{suggestion}}?" - too_short: "שם המשתמש שלך קצר מידי." - too_long: "שם המשתמש שלך ארוך מידי." checking: "בודק זמינות שם משתמש..." - enter_email: 'נמצא שם משתמש. הכנס דואר אלקטרוני תואם.' - prefilled: "הדואר האלקטרוני תואם את שם המשתמש הזה." locale: title: "שפת ממשק" instructions: "שפת ממשק המשתמש. היא תתחלף כשתרעננו את העמוד." diff --git a/config/locales/client.id.yml b/config/locales/client.id.yml index 868ff8a7a4..d0f80c0f61 100644 --- a/config/locales/client.id.yml +++ b/config/locales/client.id.yml @@ -193,15 +193,8 @@ id: private_messages: "Messages" activity_stream: "Activity" bio: "About me" - email: - invalid: "Masukkan email yang valid." name: title: "Name" - too_short: "Nama kamu terlalu pendek." - username: - available: "Username tersedia." - too_short: "Username kamu terlalu pendek." - too_long: "Username kamu terlalu panjang." last_seen: "Seen" created: "Joined" email_settings: "Email" diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index bbf7e3a15a..caa4f4e29d 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -246,7 +246,7 @@ it: location_not_found: (sconosciuto) organisation: Organizzazione phone: Telefono - no_other_accounts: (nessuno) + other_accounts: "Altri account con questo indirizzo IP:" user: said: "{{username}}:" profile: "Profilo" @@ -337,29 +337,14 @@ it: instructions: "Le immagini di sfondo saranno centrate e per difetto avranno un'ampiezza di 590px." email: title: "Email" - instructions: "Mai mostrato pubblicamente." - ok: "Ti invieremo una email di conferma." - invalid: "Inserisci un indirizzo email valido." - authenticated: "{{provider}} ha autenticato la tua email." frequency: "Ti invieremo mail solo se è un po' che non ti si vede; le email riguarderanno solo le conversazioni che non hai letto." name: title: "Nome" - instructions: "Nome completo (facoltativo)." - too_short: "Il nome è troppo breve." - ok: "Il nome è adeguato." username: title: "Nome utente" - instructions: "Deve essere univoco, senza spazi e breve." - short_instructions: "Gli utenti possono citarti scrivendo @{{username}}." - available: "Il nome utente è disponibile." - global_match: "L'email corrisponde allo nome utente registrato." global_mismatch: "Già registrato. Prova {{suggestion}}?" not_available: "Non disponibile. Prova {{suggestion}}?" - too_short: "Il nome utente è troppo breve." - too_long: "Il nome utente è troppo lungo." checking: "Controllo la disponibilità del nome utente..." - enter_email: 'Nome utente trovato. Inserisci l''email corrispondente.' - prefilled: "L'email corrisponde allo username registrato." locale: title: "Lingua dell'interfaccia" instructions: "Lingua dell'interfaccia utente. Cambierà quando aggiornerai la pagina." @@ -587,6 +572,8 @@ it: reply_here: "Rispondi Qui" reply: "Rispondi" cancel: "Annulla" + create_topic: "Crea Argomento" + create_pm: "Messaggio Privato" title: "O premi Ctrl+Enter" users_placeholder: "Aggiunti un utente" title_placeholder: "In breve, di cosa tratta questa discussione?" @@ -657,7 +644,10 @@ it: title_with_attachments: "Aggiungi un'immagine o un file" from_my_computer: "Dal mio dispositivo" from_the_web: "Dal web" + remote_tip: "collegamento all'immagine" + remote_tip_with_attachments: "collegamento ad un'immagine o a un file ({{authorized_extensions}})" local_tip: "clicca per selezionare un'immagine dal tuo dispositivo" + local_tip_with_attachments: "clicca per selezionare un'immagine o un file dal tuo dispositivo ({{authorized_extensions}})" hint: "(puoi anche trascinarle nell'editor per caricarle)" hint_for_supported_browsers: "(puoi anche trascinare o incollare immagini nell'editor per caricarle)" uploading: "In caricamento" @@ -724,6 +714,7 @@ it: top: "Non ci sono altri argomenti di punta." topic: filter_to: "{{post_count}} suoi messaggi" + create: 'Nuovo Argomento' create_long: 'Crea un nuovo Argomento' private_message: 'Inizia un messaggio privato' list: 'Argomenti' @@ -1118,6 +1109,7 @@ it: general: 'Generale' settings: 'Impostazioni' delete: 'Elimina Categoria' + create: 'Crea Categoria' save: 'Salva Categoria' creation_error: Si è verificato un errore nella creazione della categoria. save_error: Si è verificato un errore durante il salvataggio della categoria. @@ -1168,6 +1160,7 @@ it: title: "Ignorato" description: "Non ti verrà notificato nulla sui nuovi argomenti di queste categorie, e non compariranno nella tua tab dei non letti." flagging: + title: 'Grazie per aiutarci a mantenere la nostra comunità civile!' action: 'Segnala Messaggio' take_action: "Procedi" notify_action: 'Messaggio privato' @@ -1188,6 +1181,7 @@ it: more: "{{n}} alla fine..." left: "{{n}} rimanenti" flagging_topic: + title: "Grazie per aiutarci a mantenere la nostra comunità civile!" action: "Segnala Argomento" notify_action: "Messaggio privato" topic_map: @@ -1505,7 +1499,9 @@ it: customize: title: "Personalizza" long_title: "Personalizzazioni Sito" + css: "CSS" header: "Intestazione" + footer: "Fondo pagina" override_default: "Non includere fogli di stile standard" enabled: "Attivo?" preview: "anteprima" diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml index e838fe7eef..e82e775cd7 100644 --- a/config/locales/client.ja.yml +++ b/config/locales/client.ja.yml @@ -219,7 +219,6 @@ ja: location_not_found: (不明) organisation: 組織 phone: 電話 - no_other_accounts: (なし) user: said: "{{username}}:" profile: "プロフィール" @@ -302,27 +301,14 @@ ja: title: "プロフィール背景" email: title: "メールアドレス" - instructions: "Eメールアドレスは外部に公開されません。" - invalid: "正しいメールアドレスを入力してください。" - authenticated: "あたなのメールは {{provider}} により認証されました。" frequency: "メールは、あなたがしばらくの間サイトにログインせず未読トピックが溜まった時のみ送信されます。" name: title: "名前" - too_short: "名前が短すぎます。" - ok: "名前 OK。" username: title: "ユーザ名" - instructions: "空白を含まないユニークな名前を入力してください。" - short_instructions: "他のユーザはあなたを @{{username}} でタグ付けできます。" - available: "ユーザ名は利用可能です。" - global_match: "メールアドレスが登録済のユーザ名と一致しました。" global_mismatch: "既に利用されています。{{suggestion}} などはいかがでしょう?" not_available: "利用できません。{{suggestion}} などはいかがでしょう?" - too_short: "ユーザ名が短すぎます。" - too_long: "ユーザ名が長すぎます。" checking: "ユーザ名が利用可能か確認しています..." - enter_email: 'ユーザ名が見つかりました。メールアドレスを入力してください。' - prefilled: "この登録ユーザが使用するメールアドレスが見つかりました。" locale: title: "表示言語" instructions: "ユーザインタフェイス表示言語。ページを更新する際には、表示言語を更新されます。" diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index 80bd4f76ae..b916965da7 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -223,7 +223,7 @@ ko: location_not_found: (알수없음) organisation: 소속 phone: 전화 - no_other_accounts: (없음) + other_accounts: "현재 IP주소의 다른 계정들:" user: said: "{{username}}:" profile: "프로필" @@ -266,6 +266,7 @@ ko: deleted_yourself: "성공적으로 당신의 계정이 삭제 되었습니다." delete_yourself_not_allowed: "지금은 당신의 계정을 삭제할 수 없습니다. 관리자에게 당신의 계정을 삭제해달라고 연락해보세요." unread_message_count: "메시지" + admin_delete: "삭제" staff_counters: flags_given: "유용한 신고" flagged_posts: "신고된 게시글" @@ -313,29 +314,14 @@ ko: instructions: "배경 이미지는 가운데를 기준으로 표시되며 590px이 기본 가로 사이즈 입니다." email: title: "이메일" - instructions: "절대로 공개되지 않습니다." - ok: "당신에게 확인 메일을 보내겠습니다." - invalid: "유효한 이메일 주소를 입력해주세요." - authenticated: "당신의 이메일은 {{provider}}에 의해 인증되었습니다." frequency: "당신의 활동이 뜸해지거나 저희가 보낸 이메일에 포함된 글들을 읽지 못하셨다면 확인 이메일을 보내드립니다." name: title: "이름" - instructions: "이름을 적어주세요. (선택사항)" - too_short: "이름이 너무 짧습니다." - ok: "사용 가능한 이름입니다." username: title: "아이디" - instructions: "중복될 수 없으며 띄어쓰기 사용이 불가합니다. 짧을 수록 좋아요." - short_instructions: "@{{username}}으로 언급이 가능합니다." - available: "아이디\x1D로 사용가능합니다." - global_match: "이메일이 등록된 아이디와 일치합니다." global_mismatch: "이미 등록된 아이디입니다. 다시 시도해보세요. {{suggestion}}" not_available: "사용할 수 없는 아이디입니다. 다시 시도해보세요. {{suggestion}}" - too_short: "아이디가 너무 짧습니다." - too_long: "아이디가 너무 깁니다." checking: "사용가능한지 확인 중..." - enter_email: '아이디를 찾았습니다. 일치하는 이메일을 입력해주세요.' - prefilled: "이메일이 등록된 아이디와 연결되어 있습니다." locale: title: "인터페이스 언어" instructions: "UI 언어. 변경 후 새로 고침하면 반영됩니다." @@ -559,6 +545,8 @@ ko: reply_here: "여기에 답글을 작성하세요." reply: "답글 전송" cancel: "취소" + create_topic: "토픽 게시" + create_pm: "비공개 메세지" title: "혹은 Ctrl + Enter 누름" users_placeholder: "사용자 추가" title_placeholder: "이야기 나누고자 하는 내용을 한문장으로 적는다면?" @@ -601,6 +589,7 @@ ko: auto_close: label: "자동-닫기 토픽 시간:" error: "유효한 값은 눌러주세요." + based_on_last_post: "적어도 토픽의 마지막 글이 이만큼 오래되지 않았으면 닫지 마세요." all: examples: '시간을 숫자(24이하)로 입력하거나 분을 포함한 시간(17:30) 혹은 타임스탬프(2013-11-22 14:00) 형식으로 입력하세요.' limited: @@ -628,7 +617,10 @@ ko: title_with_attachments: "이미지 또는 파일 추가하기" from_my_computer: "컴퓨터에서 가져오기" from_the_web: "인터넷에서 가져오기" + remote_tip: "이미지 링크" + remote_tip_with_attachments: "이미지 또는 파일 링크 ({{authorized_extensions}})" local_tip: "내 컴퓨터에서 이미지를 가져오기" + local_tip_with_attachments: "기기에서 이미지나 파일 선택을 위해 클릭 ({{authorized_extensions}})" hint: "(드래그&드랍으로 업로드 가능)" hint_for_supported_browsers: "(이미지 파일을 드래그&드랍하거나 붙여넣기하여 업로드 할 수 있습니다.)" uploading: "업로드 중입니다..." @@ -694,6 +686,7 @@ ko: top: "더 이상 인기 토픽이 없습니다." topic: filter_to: "이 토픽에서 {{username}}님의 {{post_count}}건의 게시물만 보기" + create: '새 토픽 만들기' create_long: '새로운 토픽을 개설' private_message: '개인 메시지를 작성' list: '토픽 목록' @@ -774,12 +767,16 @@ ko: description: "이 토픽의 새로운 게시글은 항상 알림이 동작합니다. 읽지 않은 글의 수는 토픽 옆에 표시됩니다." tracking_pm: title: "알림 : 새 글 표시 중" + description: "읽지 않은 글과 새로운 게시물 수를 볼 수 있습니다. 누군가 당신의 @이름 을 언급했거나 당신의 글에 답글을 작성하면 알림을 받습니다." tracking: title: "새 글 표시 중" + description: "읽지 않은 게시글의 수는 토픽 옆에 표시됩니다. 당신의 @이름 을 언급하거나 당신의 게시글에 답글을 작성한 경우에 알림이 작동합니다." regular: title: "알림: 일반" + description: "누군가 당신의 @이름 을 언급했거나 당신의 글에 답글이 달릴 때 알림을 받게 됩니다." regular_pm: title: "알림: 일반" + description: "누군가 당신의 @이름을 언급했거나 당신의 글에 답글이 달릴 때만 알림을 받게 됩니다." muted_pm: title: "알림 : 끔" description: "이 개인 메시지에 대해 어떠한 알림도 받지 않지 않습니다." @@ -1051,6 +1048,7 @@ ko: general: '일반' settings: '설정' delete: '카테고리 삭제' + create: '새 카테고리' save: '카테고리 저장' creation_error: 카테고리 생성 중 오류가 발생했습니다. save_error: 카테고리 저장 중 오류가 발생했습니다. @@ -1101,6 +1099,8 @@ ko: title: "알림 꺼짐" description: "이 카테고리의 새로운 토픽에 대한 알림을 받지 않고 읽지 않\x1C은 탭에도 표시하지 않습니다." flagging: + title: '우리 커뮤니티에 기여해 주셔서 감사합니다.' + private_reminder: '신고는 오직 관리자만 볼 수 있습니다.' action: '게시물 신고하기' take_action: "조치를 취하기" notify_action: '알림' @@ -1116,11 +1116,14 @@ ko: off_topic: "오프 토픽입니다." inappropriate: "부적절함" spam: "스팸입니다" + custom_placeholder_notify_user: "구체적이고, 건설적이며, 항상 친절하세요." + custom_placeholder_notify_moderators: "구체적으로 사용자님께서 걱정하는 것과 가능한 모든 관련된 링크를 제공해주세요." custom_message: at_least: "최소한 {{n}}자를 입력하세요" more: "{{n}} 이동합니다" left: "{{n}} 나머지" flagging_topic: + title: "우리 커뮤니티에 기여해 주셔서 감사합니다." action: "신고된 토픽" notify_action: "비공개 메시지" topic_map: @@ -1432,7 +1435,9 @@ ko: customize: title: "사용자 지정" long_title: "사이트 사용자 지정" + css: "CSS" header: "헤더" + footer: "푸터(하단영역)" override_default: "표준 스타일 시트를 포함하지 마십시오" enabled: "사용가능?" preview: "미리 보기" @@ -1515,6 +1520,7 @@ ko: sent_test: "전송됨!" delivery_method: "전달 방법" preview_digest: "요약 미리보기" + preview_digest_desc: "비활성화중인 사용자에게 보내는 매주 요약된 이메일링의 컨텐츠 미리보기" refresh: "새로고침" format: "형식" html: "html" @@ -1576,6 +1582,7 @@ ko: check_email: "이메일 확인" delete_topic: "토픽 삭제" delete_post: "게시글 삭제" + impersonate: "대역" screened_emails: title: "블락된 이메일들" description: "누군가가 새로운 계정을 만들면 아래 이메일 주소는 체크되고 등록은 블락됩니다, 또는 다른 조치가 취해집니다." @@ -1913,6 +1920,7 @@ ko: actions: title: 'Actions' star: 'f 별표시 토픽' + pin_unpin_topic: 'Shift + p 핀고정/핀해제' share_topic: 'shift s 토픽 공유' share_post: 's 게시글 공유' reply_as_new_topic: 't 링크된 토픽으로 답글 작성하기' diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml index 28a4f23258..59bc842404 100644 --- a/config/locales/client.nb_NO.yml +++ b/config/locales/client.nb_NO.yml @@ -246,7 +246,7 @@ nb_NO: location_not_found: (ukjent) organisation: Organisasjon phone: Telefon - no_other_accounts: (ingen) + other_accounts: "Andre kontoer med denne IP-adressen:" user: said: "{{username}}:" profile: "Profil" @@ -333,33 +333,18 @@ nb_NO: title: "Profilbakgrunn" instructions: "Profil bakgrunner vil bli sentrert med en standard bredde på 850px" change_card_background: - title: "Profilbakgrunn" + title: "Brukerkort bakgrunn" instructions: "Bakgrunnsbilder vil bli sentrert og ha en standard bredde på 590px." email: title: "E-post" - instructions: "Vises aldri offentlig" - ok: "Vi sender deg en email for å bekrefte" - invalid: "Vennligst skriv inn en gyldig e-postadresse." - authenticated: "Din e-post har blitt autentisert av {{provider}}." frequency: "Vi sender deg bare en e-post om vi ikke har sett deg nylig og du ikke allerede har sett det vi varsler deg om." name: title: "Navn" - instructions: "Ditt fulle navn (valgfritt)." - too_short: "Navnet ditt er for kort." - ok: "Navnet ditt ser bra ut." username: title: "Brukernavn" - instructions: "Unikt, kort og uten mellomrom." - short_instructions: "Folk kan nevne deg som @{{username}}." - available: "Ditt brukernavn er tilgjengelig." - global_match: "E-post stemmer med det registrerte brukernavnet." global_mismatch: "Allerede registrert. Prøv {{suggestion}}?" not_available: "Ikke tilgjengelig. Prøv {{suggestion}}?" - too_short: "Ditt brukernavn er for kort." - too_long: "Ditt brukernavn er for langt." checking: "Sjekker brukernavnets tilgjengelighet..." - enter_email: 'Brukernavn funnet. Skriv inn tilhørende e-postadresse.' - prefilled: "E-postadresse passer med dette registrerte brukernavnet." locale: title: "Språk for grensesnitt" instructions: "Språk for grensesnitt. Endringen vil tre i kraft når du oppdaterer siden." @@ -587,6 +572,8 @@ nb_NO: reply_here: "Svar her" reply: "Svar" cancel: "Avbryt" + create_topic: "Nytt Emne" + create_pm: "Privat Melding" title: "Eller trykk Ctrl+Enter" users_placeholder: "Legg til en bruker" title_placeholder: "Oppsummert i en setning, hva handler denne diskusjonen om?" @@ -596,6 +583,7 @@ nb_NO: view_new_post: "Se ditt nye innlegg." saving: "Lagrer..." saved: "Lagret!" + saved_draft: "Innleggsutkast. Velg for å fortsette." uploading: "Laster opp..." show_preview: 'se forhånsvisning »' hide_preview: '« skjul forhåndsvisning' @@ -656,6 +644,8 @@ nb_NO: title_with_attachments: "Legg til et bilde eller en fil" from_my_computer: "Fra Min Enhet" from_the_web: "Fra nettet" + remote_tip: "link til bilde" + remote_tip_with_attachments: "link til bilde eller fil ({{authorized_extensions}})" local_tip: "klikk for å velge et bilde fra din enhet." hint: "(du kan også drag & drop inn i editoren for å laste dem opp)" hint_for_supported_browsers: "(du kan også drag og drop eller lime inn bilder inn i editoren for å laste dem opp)" @@ -723,6 +713,7 @@ nb_NO: top: "Det er ingen flere populære emner." topic: filter_to: "{{post_count}} innlegg i dette emnet." + create: 'Nytt Emne' create_long: 'Opprett et nytt emne' private_message: 'Start en privat melding' list: 'Emner' @@ -1117,6 +1108,7 @@ nb_NO: general: 'Generellt' settings: 'Innstillinger' delete: 'Slett kategori' + create: 'Ny Kategori' save: 'Lagre Kategori' creation_error: Det oppstod en feil ved å lage denne kategorien. save_error: Det oppstod en feil ved lagrinen av denne kategorien. @@ -1182,6 +1174,7 @@ nb_NO: off_topic: "Det er off-topic " inappropriate: "Det er upassende" spam: "Det er reklame" + custom_placeholder_notify_user: "Vær spesifikk, konstruktiv og snill." custom_message: at_least: "skriv minst {{n}} bokstaver" more: "{{n}} igjen..." @@ -1344,6 +1337,8 @@ nb_NO: 7_days_ago: "7 Dager Siden" 30_days_ago: "30 Dager Siden" all: "Alle" + start_date: "Startdato" + end_date: "Sluttdato" commits: latest_changes: "Siste endringer: Vennligst oppgrader ofte!" by: "av" @@ -1987,6 +1982,7 @@ nb_NO: actions: title: 'Handlinger' star: 'f Legg emne i favoritter' + pin_unpin_topic: 'shift p Fastsett/Løsgjør emne' share_topic: 'shift s Del emne' share_post: 's Del innlegg' reply_as_new_topic: 't Svar med lenket emne' diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index ec475c5118..8fde1c35e5 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -241,7 +241,6 @@ nl: location_not_found: (onbekend) organisation: Organisatie phone: Telefoon - no_other_accounts: (geen) user: said: "{{username}}:" profile: "Profiel" @@ -324,27 +323,14 @@ nl: title: "Profielachtergrond" email: title: "E-mail" - instructions: "Je e-mail adres zal nooit publieklijk zichtbaar zijn." - invalid: "Vul een geldig e-mailadres in." - authenticated: "Je e-mailadres is bevestigd door {{provider}}." frequency: "We zullen je alleen maar mailen als we je een tijd niet gezien hebben, en als je toevallig hetgeen waarover we je mailen nog niet hebt gezien op onze site." name: title: "Naam" - too_short: "Je naam is te kort." - ok: "Je naam ziet er prima uit." username: title: "Gebruikersnaam" - instructions: "Je gebruikersnaam moet uniek zijn en mag geen spaties bevatten." - short_instructions: "Mensen kunnen naar je verwijzen als @{{username}}." - available: "Je gebruikersnaam is beschikbaar." - global_match: "Je e-mailadres komt overeen met je geregistreerde gebruikersnaam." global_mismatch: "Is al geregistreerd. Gebruikersnaam {{suggestion}} proberen?" not_available: "Niet beschikbaar. Gebruikersnaam {{suggestion}} proberen?" - too_short: "Je gebruikersnaam is te kort." - too_long: "Je gebruikersnaam is te lang." checking: "Kijken of gebruikersnaam beschikbaar is..." - enter_email: 'Gebruikersnaam gevonden. Vul het gekoppelde e-mailadres in.' - prefilled: "E-mail hoort bij deze gebruikersnaam." locale: title: "Interfacetaal" instructions: "De taal van de foruminterface. Deze verandert als je de pagina herlaadt" diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index c9c802c1eb..42ed15d668 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -269,7 +269,7 @@ pl_PL: location_not_found: (nieznane) organisation: Organizacja phone: Numer telefonu - no_other_accounts: (brak) + other_accounts: "Inne konta z tym adresem IP:" user: said: "{{username}}:" profile: "Profil" @@ -312,6 +312,7 @@ pl_PL: deleted_yourself: "Twoje konto zostało usunięte." delete_yourself_not_allowed: "Nie możesz usunąć swojego konta w tej chwili. Skontaktuj się z administratorem, by usunął Twoje konto za Ciebie." unread_message_count: "Wiadomości" + admin_delete: "Usuń" staff_counters: flags_given: "uczynnych oflagowań" flagged_posts: "oflagowane wpisy" @@ -359,29 +360,14 @@ pl_PL: instructions: "Tło karty użytkownika est wycentrowane i posiada domyślną szerokość 590px." email: title: "Email" - instructions: "Nie będzie publicznie widoczny." - ok: "Otrzymasz potwierdzenie emailem." - invalid: "Podaj poprawny adres email." - authenticated: "Twój adres email został potwierdzony przez {{provider}}." frequency: "Wyślemy do Ciebie email tylko jeżeli dawno Cię nie widzieliśmy i wyłącznie na temat rzeczy których jeszcze nie widziałeś." name: title: "Pełna nazwa" - instructions: "Twoja pełna nazwa (opcjonalna)" - too_short: "Twoja nazwa jest za krótka." - ok: "Twoja nazwa jest poprawna." username: title: "Nazwa konta" - instructions: "Unikalna, krótka i bez spacji." - short_instructions: "Aby o tobie wspomnieć, wystarczy napisać @{{username}}." - available: "Nazwa jest dostępna." - global_match: "Email zgadza się z zarejestrowaną nazwą użytkownika." global_mismatch: "Zajęta. Może spróbuj {{suggestion}}?" not_available: "Niedostępna. Może spróbuj {{suggestion}}?" - too_short: "Nazwa jest za krótka." - too_long: "Nazwa jest za długa." checking: "Sprawdzanie, czy nazwa jest dostępna…" - enter_email: 'Nazwa znaleziona. Wpisz przypisany adres email.' - prefilled: "Email zgadza się z zarejestrowaną nazwą użytkownika." locale: title: "Język interfejsu" instructions: "Język interfejsu użytkownika. Zmieni się, gdy odświeżysz stronę." @@ -613,6 +599,8 @@ pl_PL: reply_here: "Odpowiedz tutaj" reply: "Odpowiedz" cancel: "Anuluj" + create_topic: "Utwórz temat" + create_pm: "Prywatna wiadomość" title: "Lub naciśnij Ctrl+Enter" users_placeholder: "Dodaj osobę" title_placeholder: "O czym jest ta dyskusja w jednym zwartym zdaniu. " @@ -683,7 +671,10 @@ pl_PL: title_with_attachments: "Dodaj obraz lub plik" from_my_computer: "Z mojego urządzenia" from_the_web: "Z Internetu" + remote_tip: "link do obrazu" + remote_tip_with_attachments: "odnośnik do obrazu lub pliku ({{authorized_extensions}})" local_tip: "kliknij, aby wybrać obraz z Twojego urządzenia" + local_tip_with_attachments: "kliknij aby wybrać obraz lub plik ze swojego urządzenia ({{authorized_extensions}})" hint: "(możesz także upuścić plik z katalogu komputera w okno edytora)" hint_for_supported_browsers: "(możesz przeciągnąć lub wkleić obrazy do edytora, aby je wgrać)" uploading: "Wgrywanie" @@ -753,6 +744,7 @@ pl_PL: top: "Nie ma już więcej najlepszych tematów." topic: filter_to: "{{post_count}} wpisów w temacie" + create: 'Nowy temat' create_long: 'Utwórz nowy temat' private_message: 'Napisz Prywatną Wiadomość' list: 'Tematy' @@ -1180,6 +1172,7 @@ pl_PL: general: 'Ogólne' settings: 'Ustawienia' delete: 'Usuń kategorię' + create: 'Nowa kategoria' save: 'Zapisz kategorię' creation_error: Podczas tworzenia tej kategorii wystąpił błąd. save_error: Podczas zapisywania tej kategorii wystąpił błąd. @@ -1230,6 +1223,8 @@ pl_PL: title: "Wyciszone" description: "Nie będziesz powiadamiany o nowych tematach w tych kategoriach i nie będą się one pojawiać w karcie Nieprzeczytane." flagging: + title: 'Dziękujemy za pomoc w utrzymaniu porządku w naszej społeczności!' + private_reminder: 'oflagowania są poufne i widoczne jedynie dla obsługi serwisu' action: 'Oflaguj wpis' take_action: "Podejmij działanie" notify_action: 'Prywatna wiadomość' @@ -1245,11 +1240,14 @@ pl_PL: off_topic: "Jest nie-na-temat" inappropriate: "Jest nieodpowiednie" spam: "Jest odebrane jako spam" + custom_placeholder_notify_user: "Napisz konkretnie, konstuktywnie i kulturalnie." + custom_placeholder_notify_moderators: "Dlaczego ten wpis wymaga uwagi moderatora? Opisz co konkretnie Cię zaniepokoiło i jeśli to możliwe umieść odpowiednie odnośniki." custom_message: at_least: "wprowadź co najmniej {{n}} znaków" more: "{{n}} aby wysłać…" left: "{{n}} pozostało" flagging_topic: + title: "Dziękujemy za pomoc w utrzymaniu porządku w naszej społeczności!" action: "Zgłoś temat" notify_action: "Wyślij prywatną wiadomość" topic_map: @@ -1652,6 +1650,7 @@ pl_PL: sent_test: "wysłany!" delivery_method: "Metoda Dostarczenia" preview_digest: "Pokaż zestawienie aktywności" + preview_digest_desc: "Podgląd treści emaila z zestawieniem aktywności jaki jest wysyłany nieaktywnym użytkownikom." refresh: "Odśwież" format: "Format" html: "html" @@ -2065,6 +2064,7 @@ pl_PL: actions: title: 'Operacje' star: 'f oznacz temat gwiazdką' + pin_unpin_topic: 'shift p przypnij/odepnij temat' share_topic: 'shift s udostępnij temat' share_post: 's udostępnij wpis' reply_as_new_topic: 't odpowiedz w nowym temacie' diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml index feee8df49d..2121ee8fca 100644 --- a/config/locales/client.pt.yml +++ b/config/locales/client.pt.yml @@ -239,7 +239,6 @@ pt: location_not_found: (desconhecido) organisation: Organização phone: Telefóne - no_other_accounts: (nenhum) user: profile: "Perfil" mute: "Silenciar" @@ -321,25 +320,14 @@ pt: title: "Fundo de Perfil" email: title: "Email" - invalid: "Por favor coloque um endereço de email válido." - authenticated: "O seu email foi autenticado por {{provider}}." frequency: "Vamos enviar-lhe emails apenas quando não o virmos há algum tempo e não tiver visto as coisas que temos enviado." name: title: "Nome" - too_short: "O seu nome é muito curto." - ok: "O seu nome parece bom." username: title: "Nome de Utilizador" - short_instructions: "As pessoas podem mencioná-lo utilizando @{{username}}." - available: "O seu nome de utilizador está disponível." - global_match: "O email corresponde ao nome de utilizador registado." global_mismatch: "Já está registado. Tente {{suggestion}}?" not_available: "Não está disponível. Tente {{suggestion}}?" - too_short: "O seu nome de utilizador é muito curto." - too_long: "O seu nome de utilizador é muito comprido." checking: "A verificar a disponibilidade do nome de utilizador..." - enter_email: 'Nome de utilizador encontrado. Coloque o email referente a ele.' - prefilled: "O email corresponde ao nome de um utilizador registado." locale: title: "Idioma de Interface" instructions: "Idioma da interface de utilizador. Será alterado quando atualizar a página." diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml index 03f1399832..2acd387757 100644 --- a/config/locales/client.pt_BR.yml +++ b/config/locales/client.pt_BR.yml @@ -245,7 +245,6 @@ pt_BR: location_not_found: (desconhecido) organisation: Organização phone: Telefone - no_other_accounts: (nenhum) user: said: "{{username}}:" profile: "Perfil" @@ -333,28 +332,14 @@ pt_BR: instructions: "As Imagens de fundo serão centralizadas e deverão ter largura de 590px" email: title: "Email" - instructions: "Não visível publicamente." - invalid: "Por favor, coloque um email válido." - authenticated: "O seu email foi autenticado por {{provider}}." frequency: "Vamos lhe enviar emails apenas quando não o virmos há algum tempo e você não tiver visto as coisas que temos enviado." name: title: "Nome" - instructions: "Nome Completo ( campo opcional )" - too_short: "O seu nome é muito curto." - ok: "O seu nome parece ok." username: title: "Nome de Usuário" - instructions: "Único, sem espaços e curto." - short_instructions: "As pessoas podem mencionar você usando @{{username}}." - available: "O seu Nome de Usuário está disponível." - global_match: "O email corresponde ao Nome de Usuário registado." global_mismatch: "Já está registado. Tente {{suggestion}}?" not_available: "Não está disponível. Tente {{suggestion}}?" - too_short: "O seu Nome de Usuário é muito curto." - too_long: "O seu Nome de Usuário é muito grande." checking: "Verificando disponibilidade do Nome de Usuário..." - enter_email: 'Nome de Usuário encontrado. Coloque o email referente a ele.' - prefilled: "E-mail corresponde a este nome de usuário registrado." locale: title: "idioma da interface" instructions: "Idioma da interface de usuário. Irá mudar quando você atualizar a página." diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index 9e0e7601ed..37ecf88e7a 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -269,7 +269,7 @@ ru: location_not_found: (неизвестно) organisation: Организация phone: Телефон - no_other_accounts: (нет) + other_accounts: "Другие учетные записи с этим IP адресом" user: said: "{{username}}:" profile: "Профиль" @@ -360,28 +360,14 @@ ru: instructions: "Картинки фона будут отцентрированы и по-умолчанию имеют ширину 590 пикселей." email: title: "E-mail" - instructions: "Всегда скрыт от публики." - invalid: "Введите корректный адрес электронной почты." - authenticated: "Ваш адрес электронной почты подтвержден через {{provider}}." frequency: "В случае вашего отсутствия на форуме вы будете получать уведомления, но только о новых сообщениях." name: title: "Имя" - instructions: "Ваше полное имя (опционально)." - too_short: "Ваше имя слишком короткое." - ok: "Допустимое имя." username: title: "Псевдоним" - instructions: "Уникальный, без пробелов и покороче." - short_instructions: "Пользователи могут упоминать вас по @{{username}}." - available: "Псевдоним доступен." - global_match: "Адрес электронной почты совпадает с зарегистрированным." global_mismatch: "Уже занято. Попробуйте {{suggestion}}?" not_available: "Недоступно. Попробуйте {{suggestion}}?" - too_short: "Псевдоним слишком короткий." - too_long: "Псевдоним слишком длинный." checking: "Проверяю доступность псевдонима..." - enter_email: 'Псевдоним найден. Введите соответствующий адрес электронной почты.' - prefilled: "Адрес электронной почты совпадает с зарегистрированным псевдонимом." locale: title: "Язык интерфейса" instructions: "Язык сайта. Необходимо перезагрузить страницу, чтобы изменения вступили в силу." @@ -614,6 +600,7 @@ ru: reply: "Ответить" cancel: "Отменить" create_topic: "Новая тема" + create_pm: "Личное сообщение" title: "Или нажмите Ctrl+Enter" users_placeholder: "Добавить пользователя" title_placeholder: "Название: суть обсуждения коротким предложением" @@ -685,7 +672,9 @@ ru: from_my_computer: "From my device" from_the_web: "From the web" remote_tip: "ссылка на изображение" + remote_tip_with_attachments: "ссылка на изображение или файл ({{authorized_extensions}})" local_tip: "кликните для выбора изображения с вашего устройства" + local_tip_with_attachments: "кликните для выбора изображения с вашего устройства ({{authorized_extensions}})" hint: "(вы так же можете перетащить объект в редактор для его загрузки)" hint_for_supported_browsers: "(вы так же можете перетащить или вставить изображения в редактор для загрузки их на сервер)" uploading: "Загрузка" @@ -1232,6 +1221,7 @@ ru: title: "Без уведомлений" description: "Не получать уведомлений о новых темах из этих разделов и не показывать новые темы во вкладке «Непрочитанные»." flagging: + title: 'Спасибо за вашу помощь!' action: 'Пожаловаться' take_action: "Принять меры" notify_action: 'Личное сообщение' @@ -1247,11 +1237,14 @@ ru: off_topic: "Это не по теме" inappropriate: "Это неприемлемо" spam: "Это спам" + custom_placeholder_notify_user: "Будьте точны, конструктивны и всегда доброжелательны." + custom_placeholder_notify_moderators: "Сообщите нам, чем конкретно вы обеспокоены и предоставьте соответствующие ссылки, если это возможно." custom_message: at_least: "введите как минимум {{n}} символов" more: "ещё {{n}} символов..." left: "осталось {{n}} символов" flagging_topic: + title: "Спасибо за вашу помощь!" action: "Пометить тему" notify_action: "Личное сообщение" topic_map: @@ -1577,6 +1570,7 @@ ru: long_title: "Стили и заголовки" css: "CSS" header: "Заголовок" + footer: "нижний колонтитул" override_default: "Не использовать стандартную таблицу стилей" enabled: "Разрешить?" preview: "как будет" @@ -1659,6 +1653,7 @@ ru: sent_test: "отправлено!" delivery_method: "Метод отправки" preview_digest: "Просмотр сводки" + preview_digest_desc: "Предпросмотр содержимого сводки, отсылаемой форумом по электронной почте пользователям." refresh: "Обновить" format: "Формат" html: "html" @@ -1720,6 +1715,7 @@ ru: check_email: "открыть e-mail" delete_topic: "удаление темы" delete_post: "удаление сообщения" + impersonate: "выдать себя за" screened_emails: title: "Почтовые адреса" description: "Когда кто-то создает новую учетную запись, проверяется данный почтовый адрес и регистрация блокируется или производятся другие дополнительные действия." @@ -2071,6 +2067,7 @@ ru: actions: title: 'Действия' star: 'f Отметить тему' + pin_unpin_topic: 'шифт з Закрепить/Открепить тему' share_topic: 'shift s Поделиться темой' share_post: 's Поделиться сообщением' reply_as_new_topic: 't Ответить в новой связанной теме' diff --git a/config/locales/client.sq.yml b/config/locales/client.sq.yml index cc21a5b8b6..8208bc4b06 100644 --- a/config/locales/client.sq.yml +++ b/config/locales/client.sq.yml @@ -163,7 +163,7 @@ sq: cancel: "anulo" save: "Ruaj Ndryshimet" saving: "Duke e ruajtur..." - saved: "Ruajtur!" + saved: "U ruajt!" upload: "Ngarko" uploading: "Duke nga ngarkuar..." uploaded: "U ngarkua!" @@ -221,7 +221,7 @@ sq: '12': "Sent Items" '13': "Inbox" categories: - all: "të gjitha kategoritë" + all: "shfaq kategoritë" all_subcategories: "të gjitha" no_subcategory: "asnjë" category: "Kategori" @@ -246,7 +246,6 @@ sq: location_not_found: (i panjohur) organisation: Organizata phone: Telefoni - no_other_accounts: (asnjë) user: said: "{{username}}:" profile: "Profili" @@ -337,29 +336,14 @@ sq: instructions: "Sfondi profilit do të vendoset në qendër dhe do të ketë një gjerësi prej 590px." email: title: "Email" - instructions: "Mos e shfaq në publik." - ok: "Do ju nisim emailin e konfirmimit." - invalid: "Ju lutemi të vendosni një adresë emaili të vlefshme." - authenticated: "Emaili juaj duhet të aprovohet nga {{provider}}." frequency: "We'll only email you if we haven't seen you recently and you haven't already seen the thing we're emailing you about." name: title: "Emri" - instructions: "Emër Mbiemër (fakultativ)." - too_short: "Emri juaj është shumë i shkurtër." - ok: "Emri juaj është i pranueshëm." username: title: "Pseudonimi" - instructions: "Unik, pa hapësira, i shkurtër." - short_instructions: "Personat mund tu citojnë si @{{username}}." - available: "Pseudonimi është i disponueshëm." - global_match: "Email matches the registered username." global_mismatch: "Jeni vallë regjistruar më parë. Provo {{suggestion}}?" not_available: "Nuk është i disponueshëm. Provo {{suggestion}}?" - too_short: "Pseudonimi është shumë i shkurtër." - too_long: "Pseudonimi është shumë i gjatë." checking: "Duke kontrolluar disponibilitetin e pseudonimit...." - enter_email: 'Pseudonimi u gjet. Shkruaj emailin relativ.' - prefilled: "Email matches this registered username." locale: title: "Gjuha e faqes" instructions: "User interface language. It will change when you refresh the page." @@ -594,7 +578,7 @@ sq: edit_reason_placeholder: "why are you editing?" show_edit_reason: "(add edit reason)" reply_placeholder: "Type here. Use Markdown or BBCode to format. Drag or paste an image to upload it." - view_new_post: "View your new post." + view_new_post: "Shikoni postimin tuaj te ri." saving: "Duke ruajtur..." saved: "U Ruajt!" saved_draft: "Post draft in progress. Select to resume." @@ -881,7 +865,7 @@ sq: email_placeholder: 'name@example.com' success: "We mailed out an invitation to {{email}}. We'll notify you when the invitation is redeemed. Check the invitations tab on your user page to keep track of your invites." error: "Sorry, we couldn't invite that person. Perhaps they are already a user?" - login_reply: 'Log In to Reply' + login_reply: 'Përgjigju tek Diskutimi' filters: n_posts: one: "1 postim" @@ -1259,7 +1243,7 @@ sq: title: "Të Fundit" help: "temat me postime të fundit" hot: - title: "Popullore" + title: "Kryesoret" help: "a selection of the hottest topics" starred: title: "Preferuar" @@ -1286,9 +1270,9 @@ sq: other: "{{count}} të ri" lower_title: "i ri" title: - zero: "I Ri" - one: "I Ri (1)" - other: "New ({{count}})" + zero: "Tema të Reja" + one: "Tema të Reja (1)" + other: "Tema të Reja ({{count}})" help: "topics created in the last few days" posted: title: "Postimet e Mia" @@ -1300,7 +1284,7 @@ sq: other: "{{categoryName}} ({{count}})" help: "latest topics in the {{categoryName}} category" top: - title: "Top" + title: "Kryesoret" help: "the most active topics in the last year, month, week or day" yearly: title: "Top Vjetore" @@ -1996,7 +1980,7 @@ sq: next_prev: 'shift j/shift k Next/previous section' application: title: 'Aplikacion' - create: 'c Create a new topic' + create: 'c Filloni një diskutim të ri' notifications: 'n Open notifications' site_map_menu: '= Open site menu' user_profile_menu: 'p Open user menu' diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml index 29e189d2c6..9c70d9eeee 100644 --- a/config/locales/client.sv.yml +++ b/config/locales/client.sv.yml @@ -84,7 +84,7 @@ sv: other: "%{count} dagar sedan" share: topic: 'dela en länk till denna tråd' - post: 'dela en länk till denna tråd' + post: 'dela en länk till inlägg' close: 'stäng' twitter: 'dela denna länk på Twitter' facebook: 'dela denna länk på Facebook' @@ -127,7 +127,7 @@ sv: one: "{{count}} tecken" other: "{{count}} tecken" suggested_topics: - title: "Föreslagna trådar" + title: "Föreslagna ämnen" about: simple_title: "Om" title: "Om %{title}" @@ -138,6 +138,7 @@ sv: all_time: "Alla dagar" last_7_days: "Senaste 7 dagarna" like_count: "Likes" + topic_count: "Ämnen" post_count: "Inlägg" user_count: "Användare" bookmarks: @@ -168,12 +169,13 @@ sv: disable: "Avaktivera" undo: "Ångra" revert: "Återställ" + failed: "Misslyckades" banner: close: "Stäng denna banner" choose_topic: - none_found: "Inga trådar hittades." + none_found: "Inga ämnen hittades." title: - search: "Sök tråd baserat på namn, url eller id:" + search: "Sök efter ett Ämne baserat på namn, url eller id:" placeholder: "skriv trådens titel här" user_action: user_posted_topic: "{{user}} postade titeln" @@ -242,7 +244,7 @@ sv: location_not_found: (okänd) organisation: Organisation phone: Telefon - no_other_accounts: (ingen) + other_accounts: "Andra konton med samma IP-adress" user: profile: "Profil" mute: "Dämpa" @@ -284,7 +286,9 @@ sv: deleted_yourself: "Ditt konto har framgångsrikt tagits bort." delete_yourself_not_allowed: "Du kan inte ta bort ditt konto just nu. Kontakta en admin och be om att få ditt konto borttaget." unread_message_count: "Meddelanden" + admin_delete: "Radera" staff_counters: + flags_given: "Hjälpsamma flaggor" flagged_posts: "flaggade inlägg" deleted_posts: "borttagna inlägg" suspensions: "avstängningar" @@ -325,31 +329,22 @@ sv: change_profile_background: title: "Profilbakgrund" instructions: "Bakgrunderna är centrerade och har en förinställd bredd på 850px" + change_card_background: + title: "Visitkort Bakgrund" + instructions: "Bakgrundsbilder kommer att vara centrerade och ha en standardbredd på 590 px." email: title: "E-post" - instructions: "Visas aldrig publikt" - invalid: "Vad god ange en giltig e-postadress." - authenticated: "Din e-post har autentiserats av {{provider}}." frequency: "Vi kommer bara maila dig om vi inte har sett dig nyligen och du inte redan sett det vi mailar dig om." name: title: "Namn" - too_short: "Ditt namn är för kort." - ok: "Ditt namn ser bra ut." username: title: "Användarnamn" - instructions: "Måste vara unikt, kort och utan mellanslag." - short_instructions: "Personer kan omnämna dig som @{{username}}." - available: "Ditt användarnamn är tillgängligt." - global_match: "E-posten matchar det registrerade användarnamnet." global_mismatch: "Redan registrerat. Prova {{suggestion}}?" not_available: "Inte tillgängligt. Prova {{suggestion}}?" - too_short: "Ditt användarnamn är för kort." - too_long: "Ditt användarnamn är för långt." checking: "Kollar användarnamnets tillgänglighet..." - enter_email: 'Användarnamn hittat. Ange matchande e-post.' - prefilled: "Email matchar det registrerade användarnamnet." locale: title: "gränssnittsspråk" + instructions: "Språket som används av forumsgränssnittet. Det kommer att ändras när du laddar om sidan." default: "(förvalt värde)" password_confirmation: title: "Lösenord Igen" @@ -368,6 +363,7 @@ sv: bi_weekly: "varannan vecka" email_direct: "Ta emot ett mail när någon citerar dig, svarar på dina inlägg, eller nämner ditt @användarnamn" email_private_messages: "Ta emot ett mail när någon skickar dig ett privat meddelande" + email_always: "Ta emot notifieringar via mail även om jag är aktiv på forumet" other_settings: "Övrigt" categories_settings: "Kategorier" new_topic_duration: @@ -461,6 +457,7 @@ sv: read_only_mode: enabled: "En administratör har aktiverat läsläge. Du kan fortsätta kolla runt på sidan men kanske inte interagera med den." login_disabled: "Det går inte att logga in medan siten är i skrivskyddat läge." + too_few_topics_notice: "Skapa åtminstone 5 publika ämnen och %{posts} publika inlägg för att få igång diskussionen. Nya användare kommer inte kunna nå nya förtroendenivåer om det inte finns innehåll för dem att läsa. Detta meddelande syns enbart för funktionärer." learn_more: "lär dig mer..." year: 'år' year_desc: 'diskussioner skapade de senaste 365 dagarna' @@ -495,6 +492,7 @@ sv: created: 'Skapad' created_lowercase: 'skapad' trust_level: 'Förtroendenivå' + search_hint: 'användarnamn, email eller IP-adress' create_account: title: "Registrera nytt konto" failed: "Något gick fel, kanske är denna e-post redan registrerad, försök glömt lösenordslänken" @@ -505,6 +503,10 @@ sv: reset: "Återställ Lösenord" complete_username: "Om ett konto matchar användarnamnet %{username} bör du inom kort få ett e-postmeddelande med instruktioner för hur du återställer ditt lösenord." complete_email: "Om ett konto matchar %{email} bör du inom kort få ett e-postmeddelande med instruktioner för hur du återställer ditt lösenord." + complete_username_found: "Vi hittade ett konto som matchade användarnamnet %{username} , du kommer snart att få ett email med instruktioner om hur du ska återställa dit lösenord." + complete_email_found: "Vi hittade ett konto som matchade %{email} , du kommer snart att få ett email med instruktioner om hur du ska återställa dit lösenord." + complete_username_not_found: "Det finns inget konto som matchar användarnamnet %{username}" + complete_email_not_found: "Det finns inget konto som matchar %{email}" login: title: "Logga in" username: "Användare" @@ -565,6 +567,8 @@ sv: reply_here: "Svara Här" reply: "Svara" cancel: "Avbryt" + create_topic: "Nytt ämne" + create_pm: "Privat Meddelande" title: "eller tryck Ctrl+Enter" users_placeholder: "Lägg till en användare" title_placeholder: "Vad handlar denna diskussion om i en kort mening?" @@ -574,6 +578,7 @@ sv: view_new_post: "Visa ditt nya inlägg." saving: "Sparar..." saved: "Sparat!" + saved_draft: "Utkast för inlägg. Välj för att fortsätta." uploading: "Laddar upp..." show_preview: 'visa förhandsgranskning »' hide_preview: '« dölj förhandsgranskning' @@ -589,6 +594,7 @@ sv: quote_title: "Citat" quote_text: "Citat" code_title: "Förformaterad text" + code_text: "indentera förformatterad text med 4 mellanslag" upload_title: "Bild" upload_description: "skriv en bildbeskrivning här" olist_title: "Numrerad Lista" @@ -605,6 +611,12 @@ sv: auto_close: label: "Stäng automatiskt tråden efter:" error: "Vänligen ange ett giltigt värde." + based_on_last_post: "Stäng inte förrän det sista inlägget i tråden är åtminstone så här gammalt." + all: + examples: 'Ange antalet timmar (24), klockslag (17:30) eller tidstämpel (2013-11-22 14:00).' + limited: + units: "(# antal timmar)" + examples: 'Ange antal timmar (24).' notifications: title: "notifikationer med omnämnanden av @namn, svar på dina inlägg och trådar, privata meddelanden, etc" none: "Du har inte notifikationer just nu." @@ -627,7 +639,10 @@ sv: title_with_attachments: "Lägg till en bild eller en fil" from_my_computer: "Från min enhet" from_the_web: "Från webben" + remote_tip: "länk till bild" + remote_tip_with_attachments: "länk till bild eller fil ({{authorized_extensions}})" local_tip: "klicka för att välja en bild från din enhet." + local_tip_with_attachments: "klicka för att välja en bild eller fil från din enhet ({{authorized_extensions}})" hint: "(du kan också dra & släppa in i redigeraren för att ladda upp dem)" hint_for_supported_browsers: "(du kan också dra och släppa eller klistra in bilder i redigeraren för att ladda upp dem)" uploading: "Laddar upp bild" @@ -636,6 +651,7 @@ sv: title: "sök efter trådar, inlägg, användare, eller kategorier" no_results: "Inga resultat hittades." searching: "Söker ..." + post_format: "#{{post_number}} av {{username}}" context: user: "Sök inlägg av @{{username}}" category: "Sök i kategorin \"{{category}}\"" @@ -645,7 +661,7 @@ sv: not_logged_in_user: 'användarsida med sammanställning av aktuell aktivitet och inställningar' current_user: 'gå till din användarsida' starred: - title: 'stjärna' + title: 'Stjärnmarkera' help: star: 'stjärnmarkera denna tråd' unstar: 'ta bort stjärnmarkering från denna tråd' @@ -677,6 +693,10 @@ sv: hot: "Det finns inga heta trådar." category: "Det finns inga {{category}}-trådar." top: "Det finns inga topptrådar." + educate: + new: '

Dina nya trådar hamnar här.

Som standard är trådar sedda som nya och kommer att visa en ny indikator om de skapats de senaste 2 dagarna.

Du kan ändra detta i dina inställningar.

' + unread: '

Dina olästa trådar hamnar här

Som standard är trådar sedda som olästa och kommer att visa antal olästa 1 om du:

  • Skapade tråden
  • Svarade på tråden
  • Läst tråden längre än 4 minuter

Eller om du explicit har satt tråden till Följd eller Sedd via notifieringspanelen längst ned i varje tråd.

Du kan ändra detta i dina inställningar.

' + starred: '

Dina stjärnmarkerade trådar hamnar här.

För att stjärnmarkera eller avstjärnmarkera en tråd, använd:

  • brevid varje trådtitel
  • Stjärnknappen längst ned i varje tråd
' bottom: latest: "Det finns inga fler senaste trådar att läsa." hot: "Det finns inga fler heta trådar att läsa." @@ -689,6 +709,7 @@ sv: top: "Det finns inga fler topptrådar." topic: filter_to: "{{post_count}} inlägg i tråd" + create: 'Ny tråd' create_long: 'Skapa en nytt Tråd' private_message: 'Starta en privat konversation' list: 'Trådar' @@ -762,21 +783,29 @@ sv: '2_4': 'Du kommer ta emot notifikationer för att du postade ett svar till denna tråd.' '2_2': 'Du kommer ta emot notifikationer för att du följer denna tråd.' '2': 'Du kommer ta emot notifikationer för att du läser denna tråd.' + '1_2': 'Du kommer att få en notifiering om någon nämner ditt @namn eller svarar på ditt inlägg.' + '1': 'Du kommer att få en notifiering om någon nämner ditt @namn eller svarar på ditt inlägg.' '0_7': 'Du ignorerar alla notifikationer i den här kategorin.' '0_2': 'Du ignorerar alla notifikationer för denna tråd.' '0': 'Du ignorerar alla notifikationer för denna tråd.' watching_pm: title: "följda" + description: "Du kommer att bli notifierad om varje nytt inlägg i det här privata meddelandet. En räknare över antalet olästa och nya inlägg kommer också att dyka upp brevid ämnesraden." watching: title: "Kollar" + description: "Du kommer att notifieras om varje nytt inlägg i det här ämnet. En räknare över olästa och nya inlägg kommer också att visas brevid ämnet." tracking_pm: title: "bevakade" + description: "En räknare över olästa och nya poster kommer att visas brevid privata meddelanden. Du kommer att notifieras om någon nämner ditt @namn eller svarar på ditt inlägg." tracking: title: "Följer" + description: "En räknare över olästa och nya inlägg kommer att visas brevid ämnet. Du kommer att notifieras om någon nämner ditt @namn eller svarar på ditt inlägg." regular: title: "Vanlig" + description: "Du kommer att notifieras om någon nämner ditt @namn eller svarar på ditt inlägg." regular_pm: title: "Vanlig(t)" + description: "Du kommer att notifieras om någon nämner ditt @namn eller svarar på ditt inlägg i det privata meddelandet." muted_pm: title: "tystade" description: "Du kommer aldrig att få notifieringar om något angående detta privatmeddelande." @@ -796,6 +825,8 @@ sv: pin_globally: "Nåla diskussionen globalt" unarchive: "Dearkivera Tråd" archive: "Arkivera Tråd" + invisible: "Markera Olistad" + visible: "Markera Listad" reset_read: "Återställ Läsdata" multi_select: "Markera inlägg" reply: @@ -808,7 +839,7 @@ sv: title: 'Dela' help: 'dela en länk till denna tråd' flag_topic: - title: 'flagga' + title: 'Flagga' help: 'flagga privat denna tråd för uppmärksamhet eller skicka en privat notifiering om den' success_message: 'Du flaggade framgångsrikt denna tråd.' inviting: "Bjuder in..." @@ -836,6 +867,7 @@ sv: n_posts: one: "1 inlägg" other: "{{count}} inlägg" + cancel: "Ta bort filter" split_topic: title: "Flytta till nyn tråd" action: "flytta till ny tråd" @@ -903,14 +935,19 @@ sv: edit: "Tyvärr, det uppstod ett fel under ändringen av ditt inlägg. Var god försök igen." upload: "Tyvärr, det uppstod ett fel under uppladdandet av den filen. Vad god försök igen." attachment_too_large: "Tyvärr, filen du försöker ladda upp är för stor (maximal storlek är {{max_size_kb}} kb)." + file_too_large: "Tyvärr, filen du försöker ladda upp är för stor (maximal filstorlek är {{max_size_kb}}kb)" too_many_uploads: "Tyvärr, du kan bara ladda upp en bild i taget." upload_not_authorized: "Tyvärr, filen du försökte ladda upp är inte tillåten (tillåtna filtyper: {{authorized_extensions}})." image_upload_not_allowed_for_new_user: "Tyvärr, nya användare kan inte ladda upp bilder." attachment_upload_not_allowed_for_new_user: "Tyvärr, nya användare kan inte bifoga filer." + attachment_download_requires_login: "Tyvärr, du måste vara inloggad för att kunna ladda ned bifogade filer." abandon: confirm: "Är du säker på att du vill avbryta ditt inlägg?" no_value: "nej, behåll" yes_value: "Ja, överge" + via_email: "det här inlägget har gjorts via epost" + wiki: + about: "det här inlägget är en wiki; vanliga användare kan redigera det" archetypes: save: 'Spara Inställningar' controls: @@ -934,6 +971,7 @@ sv: wiki: "Skapa wiki" unwiki: "Ta bort wiki" convert_to_moderator: "Lägg till " + revert_to_regular: "Ta bort Funktionär färg" rebake: "Generera HTML" unhide: "Visa" actions: @@ -954,6 +992,9 @@ sv: like: "Ångra gillning" vote: "Ångra röstning" people: + off_topic: "{{icons}} flaggade det här som off-topic" + spam: "{{icons}} flaggade det här som spam" + inappropriate: "{{icons}} flaggade det här som olämpligt" notify_moderators: "{{icons}} notifierade moderatorer" notify_moderators_with_url: "{{icons}} notifierade moderatorer" notify_user: "{{icons}} skicka ett privat meddelande" @@ -1046,6 +1087,7 @@ sv: button: ' HTML' side_by_side_markdown: title: "Visa " + button: ' Rå' details: edited_by: "Ändrad av" category: @@ -1058,12 +1100,15 @@ sv: general: 'Allmänt' settings: 'Inställningar' delete: 'Radera Kategori' + create: 'Ny Kategori' save: 'Spara kategori' creation_error: Det uppstod ett fel när kategorin skulle skapas. save_error: Ett fel inträffade då kategorin skulle sparas. name: "Kategorinamn" description: "Beskrivning" topic: "Kategoritråd" + logo: "Kategori Logotypbild" + background_image: "Kategori Bakgrundsbild" badge_colors: "Emblemsfärg" background_color: "Bakgrundsfärg" foreground_color: "Förgrundsfärg" @@ -1072,56 +1117,118 @@ sv: delete_confirm: "Är du säker på att du vill radera den kategorin?" delete_error: "Ett fel inträffade vid borttagning av kategorin." list: "Lista Kategorier" + no_description: "Lägg till en beskrivning för den här kategorin." change_in_category_topic: "besök kategorins tråd för att ändra beskrivning" already_used: 'Den här färgen används redan av en annan kategori' security: "Säkerhet" images: "Bilder" auto_close_label: "Stäng automatiskt tråden efter:" auto_close_units: "timmar" + email_in: "Egenvald inkommande e-postadress:" + email_in_allow_strangers: "Acceptera e-post från anonyma användare utan konton" + email_in_disabled: "Att skapa nya ämnen via e-post är avaktiverat i Inställningarna. För att aktivera ämnen skapade via e-post," + email_in_disabled_click: 'aktivera "inkommande e-post" inställningen.' + allow_badges_label: "Tillåt utmärkelser i den här kategorin" edit_permissions: "Redigera behörigheter" add_permission: "Lägg till behörighet" this_year: "i år" position: "position" default_position: "Standardposition" + position_disabled: "Katergorier kommer att sorteras efter deras aktivitet. För att ställa in sorteringen av kategorier i den här listan," + position_disabled_click: 'aktivera "fast kategori positioner" inställningen.' parent: "Förälderkategori" notifications: + watching: + title: "Bevakar" + description: "Du kommer automatiskt att bevaka alla nya ämnen i de här kategorierna. Du kommer att få notifieringar om alla nya inlägg och ämnen, och en räknare över olästa och nya inlägg kommer att visas brevid ämnen." tracking: title: "Följer" + description: "Du kommer automatiskt följa alla nya ämnen i de här kategorierna. En räknare över olästa och nya inlägg kommer att visas brevid ämnen." + regular: + title: "Vanlig" + description: "Du kommer att notifieras om någon nämner ditt @namn eller svarar på ditt inlägg." muted: title: "Tystad" + description: "Du kommer inte att notifieras om något som rör nya ämnen i de här kategorierna, och de kommer inte att dyka upp i din olästa tabb." flagging: + title: 'Tack för att du hjälper till att hålla vår gemenskap civiliserad!' + private_reminder: 'flaggor är privata, endast synliga för funktionärer' action: 'Flagga Inlägg' take_action: "Åtgärda" notify_action: 'Privat meddelande' delete_spammer: "Radera spammare" delete_confirm: "Du håller på att radera %{posts} inlägg och %{topics} trådar från den här användaren, radera hans/hennes konto, blockera IP-adressen %{ip_address}, och lägga till email-adressen %{email} till en permanent blockeringslista. Är du säker på att den här användaren verkligen är en spammare?" yes_delete_spammer: "Ja, radera spammare" + ip_address_missing: "(N/A)" + hidden_email_address: "(gömd)" + submit_tooltip: "Använd den privata flaggan" + take_action_tooltip: "Nå flaggränsen omedelbart, snarare än att vänta på mer flaggor från användarna" cant: "Tyvärr, du kan inte flagga detta inlägg just nu." + formatted_name: + off_topic: "Det är Off-topic" + inappropriate: "Det är Olämpligt" + spam: "Det är Spam" + custom_placeholder_notify_user: "Var specifik, var konstruktiv och var alltid trevlig." + custom_placeholder_notify_moderators: "Låt oss veta i detalj vad du är bekymrad över, och skicka med relevanta länkar och exempel om möjligt." custom_message: at_least: "skriv åtminstone {{n}} tecken" more: "{{n}} fler..." left: "{{n}} kvar" flagging_topic: + title: "Tack för att du hjälper oss hålla gemenskapen civiliserad!" action: "Flagga tråd" notify_action: "Privatmeddelande" topic_map: title: "Sammanfattning av tråd" links_shown: "visa alla {{totalLinks}} länkar..." + topic_statuses: + warning: + help: "Det här är en officiell varning." + locked: + help: "Det här ämnet är stängt; det går inte längre att svara på inlägg" + unpinned: + title: "Avklistrat" + help: "Det här ämnet är avklistrat, det kommer att visas i standard ordningen" + pinned_globally: + title: "Klistrat Globalt" + help: "Det här ämnet är klistrat globalt; det kommer att visas högst upp i alla listor" + pinned: + title: "Klistrat" + help: "Det här ämnet är klistrat; det kommer att visas högst upp i sin kategori" + archived: + help: "Det här ämnet är arkiverat; det är fryst och kan inte ändras" + invisible: + help: "Det här ämnet är olistat; det kommer inte visas i ämneslistorna och kan bara nås via en direktlänk" posts: "Inlägg" + posts_lowercase: "inlägg" posts_long: "{{number}} inlägg i den här tråden" + posts_likes_MF: | + Det här ämnet har {count, plural, en {1 post} annan {# posts}} {ratio, välj, + låg {med en hög gilla mot inlägg ratio} + medel {med en väldigt hög gilla mot inlägg ratio} + hög {med en extremt hög gilla mot inlägg ratio} + annan {}} original_post: "Originalinlägg" views: "Visningar" + views_lowercase: "visningar" replies: "Svar" views_long: "denna tråd har visats {{number}} gånger" activity: "Aktivitet" likes: "Gillningar" + likes_lowercase: "gillningar" likes_long: "det finns {{number}} gillningar i den här tråden" users: "Användare" + users_lowercase: "användare" category_title: "Kategori" history: "Historik" changed_by: "av {{author}}" + raw_email: + title: "Rå Epost" + not_available: "Ej tillgänglig!" categories_list: "Kategorilista" filters: + with_topics: "%{filter} ämnen" + with_category: "%{filter} %{category} ämnen" latest: title: "Senaste" help: "Trådar med nya inlägg" @@ -1156,6 +1263,7 @@ sv: zero: "Nya" one: "Nya (1)" other: "Nya ({{count}})" + help: "ämnen skapade de senaste dagarna" posted: title: "Mina Inlägg" help: "trådar som du har postat i" @@ -1167,6 +1275,7 @@ sv: help: "senaste trådarna i {{categoryName}}-kategorin" top: title: "Topp" + help: "de mest aktiva ämnena det senaste året, månaden, veckan och dagen" yearly: title: "Topp i år" monthly: @@ -1180,6 +1289,7 @@ sv: this_week: "Den här veckan" today: "Idag" other_periods: "se fler topptrådar" + browser_update: 'Tyvärr, din webbläsare är för gammal för att fungera på den här sidan. Vänligenuppgradera din webbläsare.' permission_types: full: "Skapa / svara / se" create_post: "Svara / se" @@ -1294,6 +1404,7 @@ sv: backup: text: "säkerhetskopia" title: "skapa en säkerhetskopia" + without_uploads: "Ja (inkluder inte filer)" download: text: "ladda ned" title: "ladda ned säkerhetskopian" @@ -1310,18 +1421,59 @@ sv: text: "rollback" title: "Gör rollback på databasen till ett tidigare fungerande tillstånd." confirm: "Är du säker på att du vill göra rollback på databasen till det tidigare fungerande tillståndet?" + export_csv: + users: + text: "Exportera användare" + title: "Exportera användare i en CSV fil." + success: "Exporteringen har påbörjats, du kommer snart att få en notifiering över hur det går." + failed: "Exporteringen misslyckades. Kontrollera loggarna." customize: title: "Anpassa" long_title: "Sidanpassningar" + css: "CSS" header: "Sidhuvud" + footer: "Footer" override_default: "Skriv över standard?" enabled: "Aktiverad?" preview: "förhandsgranska" + undo_preview: "ta bort förhandsgranskning" + rescue_preview: "standard stil" save: "Spara" new: "Ny" new_style: "Ny Stil" delete: "Radera" delete_confirm: "Radera denna anpassning?" + color: "Färg" + opacity: "Opacitet" + copy: "Kopiera" + css_html: + title: "CSS/HTML" + long_title: "CSS och HTML anpassningar" + colors: + title: "Färger" + long_title: "Färgscheman" + about: "Modifiera färgerna som används utan att skriva CSS. Lägg till ett schema för att börja." + new_name: "Nytt färgschema" + copy_name_prefix: "Kopia av" + delete_confirm: "Ta bort det här färgschemat?" + undo: "ångra" + undo_title: "Återställ ändringarna för den här färgen till den senast sparade versionen." + revert: "återställ" + revert_title: "Återställ den här färgen till Discourse standard färgschema." + primary: + name: 'primär' + secondary: + name: 'sekundär' + description: 'Den huvudsakliga bakgrundsfärgen, och textfärgen på vissa knappar.' + quaternary: + description: "Navigationslänkar." + danger: + name: 'fara' + love: + description: "Gillaknappens färg." + wiki: + name: 'wiki' + description: "Huvudfärg som används som bakgrund till wikiinlägg." email: title: "E-postloggar" settings: "Inställningar" @@ -1335,6 +1487,7 @@ sv: email_type: "E-posttyp" to_address: "Till adress" test_email_address: "e-postadress att testa" + send_test: "Skicka Test Mail" sent_test: "skickat!" delivery_method: "Leveransmetod" preview_digest: "Sammandrag" @@ -1350,6 +1503,8 @@ sv: filters: title: "filter" user_placeholder: "användarnamn" + address_placeholder: "namn@exempel.se" + reply_key_placeholder: "svara knapp" skipped_reason_placeholder: "anledning" logs: title: "Loggar" @@ -1358,6 +1513,8 @@ sv: last_match_at: "Senast matchad" match_count: "Träffar" ip_address: "IP" + topic_id: "Tråd ID" + post_id: "Inlägg ID" delete: 'Radera' edit: 'Redigera' save: 'Spara' @@ -1391,6 +1548,9 @@ sv: unsuspend_user: "Ej avstängd användare" grant_badge: "ge utmärkelse" revoke_badge: "upphäv utmärkelse" + check_email: "kolla epost" + delete_topic: "ta bort tråd" + delete_post: "ta bort inlägg" screened_emails: title: "Kontrollerade email" description: "När någon försöker skapa ett nytt konto, kommer följande emailadresser att kontrolleras och registrationen blockeras, eller någon annan åtgärd vidtas." @@ -1422,6 +1582,7 @@ sv: last_emailed: "Senast Mailad" not_found: "Tyvärr den användaren existerar inte i vårt system." active: "Aktiv" + show_emails: "Visa Epost" nav: new: "Ny" active: "Aktiv" @@ -1453,6 +1614,10 @@ sv: reject_failures: one: "Avvisning av användaren misslyckades." other: "Avvisning av %{count} användare misslyckades." + not_verified: "Ej verifierad" + check_email: + title: "Visa den här användarens epostadress" + text: "Visa" user: suspend_failed: "Någonting gick fel under avstängningen av denna användare {{error}}" unsuspend_failed: "Någonting gick fel under upplåsningen av denna användare {{error}}" @@ -1473,8 +1638,12 @@ sv: edit_title: "Redigera titel" save_title: "Spara titel" refresh_browsers: "Tvinga webbläsaruppdatering" + refresh_browsers_message: "Meddelande skickat till alla klienter!" show_public_profile: "Visa Publik Profil" impersonate: 'Imitera' + ip_lookup: "Kolla upp IP-adress" + log_out: "Logga ut" + logged_out: "Användaren loggades ut från alla enheter." revoke_admin: 'Återkalla Administratör' grant_admin: 'Bevilja Administratör' revoke_moderation: 'Återkalla Moderering' @@ -1484,12 +1653,15 @@ sv: reputation: Rykte permissions: Rättigheter activity: Aktivitet + like_count: Gillningar Gedda / Mottagna + last_100_days: 'de senaste 100 dagarna' private_topics_count: Antal Privata Trådar posts_read_count: Inlägg Lästa post_count: Inlägg Skapade topics_entered: Besökta trådar flags_given_count: Givna Flaggnignar flags_received_count: Mottagna Flaggningar + warnings_received_count: Varningar Mottagningar approve: 'Godkänn' approved_by: "godkänd av" approve_success: "Användaren är godkänd och ett email kommer att skickas med aktiveringsinstruktioner." @@ -1535,6 +1707,24 @@ sv: days: "dagar" topics_replied_to: "Trådar svarade på" flagged_posts: "flaggade inlägg" + sso: + external_id: "Externt ID" + external_username: "Användarnamn" + external_name: "Namn" + external_email: "Epost" + user_fields: + title: "Användarfält" + create: "Skapa ett användarfält" + untitled: "Namnlös" + save: "Spara" + edit: "Redigera" + delete: "Ta bort" + cancel: "Avbryt" + delete_confirm: "Är du säker på att fu vill ta bort det här användarfältet?" + required: + title: "Krävs vid registrering?" + enabled: "krävs" + disabled: "krävs ej" site_settings: show_overriden: 'Visa bara överskrivna' title: 'Webbplatsinställningar' @@ -1560,6 +1750,7 @@ sv: title: Utmärkelser new_badge: Ny utmärkelse new: Ny + name: Namn badge: Utmärkelse description: beskrivning badge_type: Utmärkelsetyp @@ -1583,6 +1774,8 @@ sv: multiple_grant: Kan utfärdas flera gånger listable: Visa utmärkelse på den offentliga utmärkelsesidan enabled: Aktivera utmärkelse + icon: Ikon + image: Bild lightbox: download: "ladda ned" keyboard_shortcuts_help: @@ -1661,3 +1854,12 @@ sv: reader: name: Läsare description: Läs varje inlägg i en diskussion med över 100 inlägg + google_search: | +

Sök med Google

+

+

+

diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml index 63b0af96f7..0bc0f0349f 100644 --- a/config/locales/client.uk.yml +++ b/config/locales/client.uk.yml @@ -243,25 +243,14 @@ uk: title: "Фон Профіля" email: title: "Електронна пошта" - invalid: "Будь ласка, введіть правильну адресу електронної пошти." - authenticated: "Вашу скриньку було автентифіковано {{provider}}." frequency: "Ми будемо надсилати Вам листи тільки якщо ми Вас останнім часом не бачили і Ви ще не бачили те, про що ми Вам пишемо." name: title: "Ім'я" - too_short: "Ваше ім'я надто коротке." - ok: "Ваше ім'я виглядає добре." username: title: "Ім'я користувача" - short_instructions: "Інші можуть посилатися на Вас як на @{{username}}." - available: "Ім'я користувача доступне." - global_match: "Електронна пошта відповідає зареєстрованому імені користувача." global_mismatch: "Вже зареєстровано. Спробуєте {{suggestion}}?" not_available: "Не доступно. Спробуєте {{suggestion}}?" - too_short: "Ім'я користувача надто коротке." - too_long: "Ім'я користувача надто довге." checking: "Перевірка доступності імені користувача..." - enter_email: 'Ім''я користувача знайдено. Введіть відповідну електронну скриньку.' - prefilled: "Електронна скринька відповідає цьому зареєстрованому імені користувача." locale: title: "Interface language" default: "(default)" diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index 396c29b180..6b79753b89 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -223,7 +223,7 @@ zh_CN: location_not_found: (未知) organisation: 组织 phone: 电话 - no_other_accounts: (无) + other_accounts: "其他使用该 IP 地址的账户:" user: said: "{{username}}:" profile: "个人资料" @@ -314,29 +314,14 @@ zh_CN: instructions: "背景图片将被居中并且默认宽度为 590px。" email: title: "电子邮箱" - instructions: "绝不会被公开显示。" - ok: "我们将邮件跟您确认。" - invalid: "请填写正确的电子邮箱地址。" - authenticated: "您的电子邮箱已经被 {{provider}} 确认有效。" frequency: "只有当您最近一段时间没有访问时,我们才会把您未读过的内容发送到您的电子邮箱。" name: title: "名字" - instructions: "您的全名(可选)。" - too_short: "您设置的名字太短了。" - ok: "您的名字符合要求。" username: title: "用户名" - instructions: "唯一,没有空格,简短。" - short_instructions: "其他人可以用 @{{username}} 来提及您。" - available: "您的用户名可用。" - global_match: "电子邮箱与注册用户名相匹配。" global_mismatch: "已被注册。试试 {{suggestion}} ?" not_available: "不可用。试试 {{suggestion}} ?" - too_short: "您设置的用户名太短了。" - too_long: "您设置的用户名太长了。" checking: "查看用户名是否可用..." - enter_email: '找到用户名,请输入对应电子邮箱。' - prefilled: "电子邮箱与用户名匹配。" locale: title: "界面语言" instructions: "用户界面语言。将在您刷新页面后改变。" @@ -561,6 +546,7 @@ zh_CN: reply: "回复" cancel: "取消" create_topic: "新主题" + create_pm: "私信" title: "或者按下 Ctrl + 回车" users_placeholder: "添加一个用户" title_placeholder: "简述此讨论内容是关于什么?" @@ -727,7 +713,7 @@ zh_CN: unread_posts: other: "在这个主题中,您有 {{count}} 条未读的帖子" new_posts: - other: "从你最近一次阅读此主题后,又有 {{new_posts}} 个新帖子发表" + other: "自您上一次阅读此主题后,又有 {{count}} 个新帖子发表" likes: other: "本主题已有 {{number}} 次赞" back_to_list: "返回主题列表" diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index 82f958f9c5..f43a899a38 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -223,7 +223,6 @@ zh_TW: location_not_found: (unknown) organisation: 組織 phone: 電話 - no_other_accounts: (none) user: said: "{{username}}:" profile: "基本資料" @@ -313,28 +312,14 @@ zh_TW: instructions: "背景會被置中,且默認寬度為850px。" email: title: "電子郵件" - instructions: "我們不會公開您的電子郵件信箱。" - invalid: "請輸入有效的電子郵件地址。" - authenticated: "你的電子郵件地址已得到 {{provider}} 的認証。" frequency: "我們只會在一段時間未看到你及你尚未看過我們通知你的事物時才會寄信給你。" name: title: "名稱" - instructions: "您的全名(選填)。" - too_short: "你的名稱太短。" - ok: "你的名稱符合要求。" username: title: "用戶名稱" - instructions: "獨一無二,沒有空白,夠短。" - short_instructions: "其他人可以輸入 @{{username}} 提到你。" - available: "你的用戶名稱可以使用。" - global_match: "電子郵件地址與註冊的用戶名稱相符。" global_mismatch: "已經註冊過了,請試試看 {{suggestion}}?" not_available: "無法使用,請試試看 {{suggestion}}?" - too_short: "你的用戶名稱太短。" - too_long: "你的用戶名稱太長。" checking: "正在檢查用戶名稱是否已經有人使用..." - enter_email: '找到用戶名稱,請輸入相符的電子郵件地址。' - prefilled: "電子郵件地址與此註冊的用戶名稱相符。" locale: title: "界面語言" instructions: "使用者介面的語言,當頁面重新整理的時候會更換成你的設定。" diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index ac36ce390c..fea96f7a7f 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -752,6 +752,7 @@ es: purge_inactive_users_grace_period_days: "Periodo de gracia (en días) durante los cuales un usuario que no haya activado su cuenta no será aún eliminado." enable_s3_uploads: "Coloca los archivos subidos en el almacén Amazon S3. IMPORTANTE: requiere de credenciales de S3 validas. (ambas clave de acceso y clave de acceso secreta)." s3_use_iam_profile: 'Usar el rol de AWS EC2 IAM para descargar las llaves. NOTA: habilitando esta opción ignorará la "llave de acceso s3" y "la llave de acceso secreta s3".' + s3_upload_bucket: "El nombre del bucket Amazon S3 donde se subirán los archivos. AVISO: debe ser en minúsculas, sin puntos ni guiones bajos." s3_access_key_id: "La clave id de acceso de Amazon S3 que se utilizará para subir imágenes." s3_secret_access_key: "La clave secreta de acceso de Amazon S3 que se utilizará para subir imágenes." s3_region: "El nombre de región de Amazon S3 que se utilizará para subir imágenes." @@ -848,6 +849,7 @@ es: logout_redirect: "Ubicación a redirigir después que el usuario cierre sesión Ejem: (http://somesite.com/logout)" allow_uploaded_avatars: "Permitir a los usuarios subir su propio avatar personalizado." allow_animated_avatars: "Permitir a los usuarios usar avatares con imágenes animadas tipo gif. AVISO: ejecuta el rake task avatars:refresh después de cambiar esta opción." + allow_animated_thumbnails: "Generar miniaturas en movimiento de los gifs animados." automatically_download_gravatars: "Descargar Gravatars para usuarios cuando se creen una cuenta o cambien el email." digest_topics: "El número máximo de temas a mostrar en el resumen por email." digest_min_excerpt_length: "La extensión mínima, en caracteres, del extracto de un post en el resumen por email." @@ -937,6 +939,18 @@ es: archived_disabled: "Este tema se ha desarchivado. Se desparaliza y ahora puede ser cambiado." closed_enabled: "Este tema está ahora cerrado. No se admiten nuevas respuestas." closed_disabled: "Este tema ahora está abierto. Se admiten nuevas respuestas." + autoclosed_enabled_days: + one: "Este tema se cerró automáticamente después de 1 día. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente después de %{count} días. No se permiten nuevas respuestas." + autoclosed_enabled_hours: + one: "Este tema se cerró automáticamente después de 1 hora. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente después de %{count} horas. No se permiten nuevas respuestas." + autoclosed_enabled_minutes: + one: "Este tema se cerró automáticamente después de 1 minuto. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente después de %{count} minutos. No se permiten nuevas respuestas." + autoclosed_enabled_lastpost_days: + one: "Este tema se cerró automáticamente 1 día después del último post. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente %{count} días después del último post. No se permiten nuevas respuestas." autoclosed_disabled: "El tema ahora está en abierto, se permiten respuestas." autoclosed_disabled_lastpost: "Este tema está ahora abierto. Se permiten nuevas respuestas." pinned_enabled: "Este tema ahora está destacado. Aparecerá en primer lugar en la lista de su categoría hasta que se deshaga el destacado de forma general por los moderadores o de forma particular por cada usuario para sí." @@ -1090,6 +1104,7 @@ es: other: "Estos reportes fueron enviados desde hace %{count} horas." please_review: "Por favor, revísalos." post_number: "post" + how_to_disable: 'Puedes desactivar o cambiar la frecuencia de este email de recordatorio mediante la opción "notify about flags after".' subject_template: one: "1 reporte esperando ser atendidos." other: "%{count} reportes esperando ser atendidos." @@ -1353,10 +1368,12 @@ es: --- %{respond_instructions} digest: + why: "Un breve resumen de %{site_link} desde tu última visita el %{last_seen_at}" subject_template: "[%{site_name}] Resumen del %{date}" new_activity: "Actividad nueva en tus temas y posts:" top_topics: "Posts populares" other_new_topics: "Temas populares" + unsubscribe: "Este resumen es enviado desde %{site_link} cuando pasa un tiempo desde tu última visita. Para cancelar tu suscripción %{unsubscribe_link}." click_here: "clic aquí" from: "resumen de %{site_name}" read_more: "Leer más" @@ -1571,3 +1588,25 @@ es: title: "Términos de Servicio" privacy_topic: title: "Políticas de Privacidad" + static: + search_help: | +

Consejos

+

+

    +
  • Se priorizan las coincidencias con el título, por lo que si tienes dudas, busca por títulos
  • +
  • Buscar palabras diferentes, no muy comunes, siempre dará mejores resultados
  • +
  • Cuando sea posible, centra tu búsqueda en una categoría, un tema o un usuario en concreto
  • +
+

+

Opciones

+

+ + + + + +
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:tracking
+

+

+ arcoiris category:parques status:open order:latest buscará temas que contengan la palabra "arcoiris" en la categoría "parques" que no estén cerrados o archivados, ordenados por la fecha de la última publicación. +

diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index f3e8fb0a07..d603543278 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -933,9 +933,6 @@ fi: autoclosed_enabled_lastpost_days: one: "Tämä ketju suljettiin automaattisesti 1 päivän kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." other: "Tämä ketju suljettiin automaattisesti %{count} päivän kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." - autoclosed_enabled_lastpost_hours: - one: "Tämä ketju suljettiin automaattisesti 1 tunnin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." - other: "Tämä ketju suljettiin automaattisesti %{count} tunnin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." autoclosed_enabled_lastpost_minutes: one: "Tämä ketju suljettiin automaattisesti 1 minuutin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." other: "Tämä ketju suljettiin automaattisesti %{count} minuutin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." diff --git a/config/locales/server.he.yml b/config/locales/server.he.yml index 40cc520015..bd08cd1378 100644 --- a/config/locales/server.he.yml +++ b/config/locales/server.he.yml @@ -926,9 +926,6 @@ he: autoclosed_enabled_lastpost_days: one: "נושא זה ננעל אוטומטית לאחר יום אחד מהתגובה האחרונה. תגובות חדשות לא מתקבלות." other: "נושא זה ננעל אוטומטית לאחר %{count} ימים מהתגובה האחרונה. תגובות חדשות לא מתקבלות." - autoclosed_enabled_lastpost_hours: - one: "נושא זה ננעל אוטומטית לאחר שעה מהתגובה האחרונה. תגובות חדשות לא מתקבלות." - other: "נושא זה ננעל אוטומטית לאחר %{count} שעות מהתגובה האחרונה. תגובות חדשות לא מתקבלות." autoclosed_enabled_lastpost_minutes: one: "נושא זה ננעל אוטומטית לאחר דקה מהתגובה האחרונה. תגובות חדשות לא מתקבלות." other: "נושא זה ננעל אוטומטית לאחר %{count} דקות מהתגובה האחרונה. תגובות חדשות לא מתקבלות." diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index d077e183e4..e5e2e771ed 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -798,9 +798,6 @@ it: autoclosed_enabled_lastpost_days: one: "Questo argomento è stato automaticamente chiuso un giorno dopo l'ultima risposta. Non sono permesse altre risposte." other: "Questo argomento è stato automaticamente chiuso %{count} giorni dopo l'ultima risposta. Non sono permesse altre risposte." - autoclosed_enabled_lastpost_hours: - one: "Questo argomento è stato automaticamente chiuso un'ora dopo l'ultima risposta. Non sono permesse altre risposte." - other: "Questo argomento è stato automaticamente chiuso %{count} ore dopo l'ultima risposta. Non sono permesse altre risposte." autoclosed_enabled_lastpost_minutes: one: "Questo argomento è stato automaticamente chiuso un minuto dopo l'ultima risposta. Non sono permesse altre risposte." other: "Questo argomento è stato automaticamente chiuso %{count} minuti dopo l'ultima risposta. Non sono permesse altre risposte." diff --git a/config/locales/server.ko.yml b/config/locales/server.ko.yml index 2b825e7f13..01a1c6b9c6 100644 --- a/config/locales/server.ko.yml +++ b/config/locales/server.ko.yml @@ -6,6 +6,16 @@ # https://www.transifex.com/projects/p/discourse-org/ ko: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "MMM D" short_date: "YYYY-MMM-D" @@ -23,9 +33,13 @@ ko: log_in: "로그인" via: "%{site_name}의 %{username}" is_reserved: "예약됨" + purge_reason: "오래되고 검수되지 않은 글은 자동 삭제" + disable_remote_images_download_reason: "서버에 저장공간이 부족해서 원격 이미지 저장이 비활성화됨. " errors: messages: too_long_validation: "%{length}자를 입력하셨습니다. 최대 %{max}자까지 입력 가능합니다. " + invalid_boolean: "잘못된 값" + taken: "이미 사용 중." embed: load_from_remote: "글 로딩 중 오류가 발생하였습니다." bulk_invite: @@ -309,12 +323,14 @@ ko: activation: action: "계정 활성화" already_done: "죄송합니다. 이 계정 확인 링크는 더 이상 유효하지 않습니다." + please_continue: "계정이 활성화 되었습니다; 홈페이지로 이동합니다." continue_button: "%{site_name}으로 가기" welcome_to: "%{site_name}에 오신것을 환영합니다." approval_required: "이 포럼을 사용하기 위해선 운영자가 수동으로 당신의 새로운 계정을 수락해야 합니다. 계정이 수락이 되면 자동으로 이메일이 발송됩니다." post_action_types: off_topic: title: '오프 토픽' + description: '이 게시물은 이곳의 토픽과 관련된것 같지 않습니다. 다른 토픽으로 옮기거나 새로운 토픽을 시작하는 것이 어떤가요?' long_form: '오프 토픽으로 신고하기' spam: title: '스팸' @@ -325,10 +341,15 @@ ko: description: '이 게시물은 다른 사용자들에게 공격적이나 모욕적 또는 침해적인 글을 담고 있습니다.' long_form: '부적절함으로 신고하기' notify_user: + title: '비공개 메세지 @{{username}}' description: '이 게시물에 내가 작성자와 직접 대화하고 싶은 내용이 있습니다. 신고에서 제외해주세요.' + long_form: '비공개 메세지' email_title: '"%{title}" 내의 당신의 게시글' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "뭔가 다른것" + description: '이 게시물은 리스트에 없는 이유로 관리자의 주의가 필요합니다.' + long_form: '관리자의 주의를 위해 신고' email_title: '"%{title}" 게시물에 대한 운영자의 확인이 필요합니다' email_body: "%{link}\n\n%{message}" bookmark: @@ -355,7 +376,9 @@ ko: 커뮤니티 가이드라인에 맞지 않습니다.' long_form: '부적절함으로 신고하였습니다.' notify_moderators: + title: "뭔가 다른것" description: '이 게시물은 가이드라인, 이용약관, 또는 다른 이유와 관련해서 운영자의 주의가 필요합니다.' + long_form: '관리자의 주의를 위해 신고' email_title: '토픽 "%{title}" 은 운영자의 확인이 필요합니다' email_body: "%{link}\n\n%{message}" flagging: @@ -471,6 +494,7 @@ ko: twitter_config_warning: '당신의 서버는 트위터를 통한 가입을 설정하였습니다(enable_twitter_logins), 그러나 key 와 secret 값이 입력하지 않았습니다. 사이트 설정 에서 업데이트 해주세요. 가이드 읽어보기.' github_config_warning: '당신의 서버는 Github를 통한 가입을 설정하였습니다(enable_github_logins), 그러나 client id 와 secret 값을 입력하지 않았습니다. 사이트 설정 에서 업데이트 해주세요. 가이드 읽어보기.' s3_config_warning: '파일을 Amazon S3로 업로드 하도록 설정하였습니다. 하지만 아직 Access key id(s3_access_key_id), Secret access key(s3_secret_access_key) 혹은 S3 bucket(s3_upload_bucketat) 중 하나를 설정하지 않았습니다. 사이트 세팅에서 설정해주세요. 자세히 알아보기("How to set up image uploads to S3?").' + s3_backup_config_warning: 's3를 이용한 서버 설정이 완료 되지 않았습니다: s3_access_key_id, s3_secret_access_key, s3_secret_access_key 또는 s3_upload_bucket. 사이트 설정 에 가셔서 업데이트 하세요. "How to set up image uploads to S3?" 읽어 보세요.' image_magick_warning: '큰 이미지의 섬네일 만드는 설정이 있지만, ImageMagick가 설치되지 않았습니다. 최신버전 받기.' failing_emails_warning: '%{num_failed_jobs} 개의 이메일 잡이 실패하였습니다. config.action_mailer 설정이 정확한지 config/environments/production.rb 파일을 체크해보세요.' default_logo_warning: "당신은 사이트의 커스텀 로고를 사용하지 않고 있습니다. logo_url, logo_small_url, 와 favicon_url 를 사이트 설정 에서 설정하세요." @@ -491,6 +515,7 @@ ko: description: "새로운 사용자가 첫 두개의 토픽을 작성하면 자동 팝업됩니다." usage_tips: title: "새로운 사용자 가이드" + description: "새로운 사용자를 위한 가이드와 팁." welcome_user: title: "환영: 새로운 사용자" description: "새로운 사용자가 가입하면 자동으로 개인 메시지를 보냅니다." @@ -511,7 +536,10 @@ ko: description: "모든 페이지 상단에 추가할 HTML(헤더 아래, 네비게이션이나 토픽 제목 위)." bottom: title: "페이지 하단" + description: "모든 페이지 하단에 추가할 HTML" site_settings: + censored_words: "단어는 자동적으로 `■■■■` 로 대체 됩니다." + delete_old_hidden_posts: "30일이 지난 숨겨진 글은 자동으로 삭제됩니다." default_locale: "Discourse 인스턴스가 사용하는 기본 언어 (ISO 639-1 Code)" allow_user_locale: "사용자에게 자신이 원하는 언어를 선택할 수 있는 옵션을 제공한다." min_post_length: "포스트의 최소 글자 수" @@ -559,11 +587,14 @@ ko: apple_touch_icon_url: "애플 디바이스는 144px의 아이콘을 사용함. 144px X 144px 사이즈를 추천함" notification_email: "The from: 이 이메일 주소는 모든 기본 시스템 메일을 보내는데 사용됩니다. 여기에 명시된 도메인은 SPF, DKIM가 적용되어 있어야하며, reverse PTR 레코드가 제대로 설정되어 있어야 메일이 도착 할 수 있습니다." email_custom_headers: "커스텀 이메일 해더의 pipe-delimited" + email_subject: "일반 메일을 위한 제목 커스터마이징. 이곳을 참고 https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" use_https: "사이트의 사이트의 전체 url((Discourse.base_url)이 http인가 아니면 https인가? 이미 HTTPS를 설정하고 동작중이 아니라면 활성화하지 마세요." summary_score_threshold: "요약본에 포함되기 위한 게시글의 최소 점수 값" summary_posts_required: "하나의 토픽에 대하여 요약본 보기 모드가 활성화되기 전까지 요구되는 최소 게시글 수" summary_likes_required: "하나의 토픽에 대하여 요약본 보기 모드가 활성화되기 전까지 요구되는 최소 좋아요 수" summary_percent_filter: "요약본 보기를 클릭시, 글 중에 몇 %의 상위 글을 보여줄 것인가?" + summary_max_results: "이 주제에 대한 요약 글 최대 갯수" + enable_private_messages: "신뢰도 1 사용자들만 개인 메세지와 개인 메세지에 대한 답글을 허용합니다" enable_long_polling: "Message bus used for notification can use long polling" anon_polling_interval: "How often should anonymous clients poll in milliseconds" auto_track_topics_after: "토픽이 자동으로 추적되기까지의 기본 시간, 사용자에 따라 재정의 될 수 있음.(밀리초, 0 : 항상, -1 : 안함)" @@ -594,11 +625,14 @@ ko: send_welcome_message: "신규 사용자에게 가입 환영 메세지를 보냅니까?" suppress_reply_directly_below: "게시글의 바로 아래에 단 하나의 댓글만 있는 경우 '댓글 수'를 보여주지 않음" suppress_reply_directly_above: "단 하나의 댓글 위의 게시글이 하나 있는 상황에서 '~에 대한 댓글'을 보여주지 않음." + max_reply_history: "덧글 확장해서 보여지는 최대 갯수" topics_per_period_in_top_summary: "인기 토픽 요약에 기본으로 보여질 토픽 수" topics_per_period_in_top_page: "인기 토픽에서 '더 보기'를 요청할 시 보여질 토픽 수" redirect_users_to_top_page: "자동으로 신규 사용자와 오래간만에 들어온 사용자를 탑 페이지로 리다이렉트 시킴" + show_email_on_profile: "사용자 프로필에서 자신의 이메일 주소 보기( 오직 본인과 관리자만 볼 수 있음)" email_token_valid_hours: "비밀번호 찾기, 계정 활성화에 사용되는 토큰의 유효 기간(시간)" email_token_grace_period_hours: "비밀번호 찾기, 계정 활성화에 사용되는 토큰은 사용되어진 이후 유효한 기간(시간)" + enable_badges: "훈장 시스템 활성화" allow_index_in_robots_txt: "이 사이트가 검색엔진에 의해 인덱스되는 것을 허용합니다.(robots.txt 수정)" email_domains_blacklist: "허용하지 않는 Email 도메인을 파이프(|)기호로 구분하여 입력. 예: mailinator.com|trashmail.net" email_domains_whitelist: "허용하는 Email 도메인을 파이프(|)기호로 구분하여 입력. 경고: 여기에 주어진 email domain이 아닌 경우 모두 거부됩니다." @@ -636,8 +670,10 @@ ko: allow_restore: "데이터 복원을 허용한다. 이 사이트의 모든 데이터가 변경될 수 있다. 백업이나 복원 계획이 없다면 비활성화로 놔둔다." maximum_backups: "디스크에 유지할 최대 백업 개수. 오래된 백업순으로 자동으로 삭제된다." backup_daily: "하루에 한 번 자동으로 백업한다." + enable_s3_backups: "백업이 완료되면 Amazon S3로 업로드한다. s3 credentials을 기입되어있는지 확인해야한다." s3_backup_bucket: "백업본을 유지할 s3 버켓 이름. 주의 : 프라이빗 버켓인지 확인해야한다." active_user_rate_limit_secs: "'last_seen_at'을 업데이트 하는 주기(초)" + verbose_localization: "UI에 언어 팁을 확장해 보여줌" previous_visit_timeout_hours: "최근 방문한 시간을 저장할 주기(시간)" rate_limit_create_topic: "사용자가 토픽을 생성하고 난 후 다음 토픽을 생성할 때 까지의 대기 기간(초)" rate_limit_create_post: "사용자가 포스팅을하고 난 후 다음 포스팅까지 대기 기간(초)" @@ -655,12 +691,34 @@ ko: clean_up_uploads: "불법 호스팅을 막기 위해서 참조되지 않은 업로드 파일은 제거한다. 주의 : 이 설정을 활성화 하기 전에 `/uploads` 디렉토리를 백업하는 것이 좋다." clean_orphan_uploads_grace_period_hours: "참조되지 않은 업로드 파일을 제거하기 전 기간(시간)" purge_deleted_uploads_grace_period_days: "참조되지 않은 업로드 파일을 완전 삭제하지 전 기간(일)" + purge_inactive_users_grace_period_days: "활성화 되지 않은 사용자를 삭제하기 까지의 기간(일)" s3_access_key_id: "이미지를 업로드 할 때 사용할 Amazon S3의 access key id" s3_secret_access_key: "이미지를 업로드 할 때 사용할 Amazon S3의 secret access key" s3_region: "이미지를 업로드 할 때 사용할 Amazon S3 region" enable_flash_video_onebox: "swf, flv(어도비 플래쉬)링크를 embed 할 수 있도록 함. 주의: 보안에 대한 위험성을 알려주는 것이 좋다." default_invitee_trust_level: "사용자를 초대하기 위한 기본 신뢰도(0-4)" default_trust_level: "새로운 사용자의 기본 신뢰도(0-4)" + tl1_requires_topics_entered: "새로운 사용자가 신뢰받는 사용자-1 이 되기 위해 들어가봐야되는 토픽의 개수" + tl1_requires_read_posts: "새로운 사용자가 신뢰받는 사용자-1 가 되기 위해 읽어야 하는 포스트 개수" + tl1_requires_time_spent_mins: "새로운 사용자가 신뢰받는 사용자-1 이 되기 위해 몇 분 동안 포스트를 읽어야 하는지." + tl2_requires_topics_entered: "기본 사용자가 자주오는 사용자-2 가 되기 위해 들어가 봐야 되는 토픽 갯수" + tl2_requires_read_posts: "기본 사용자가 자주오는 사용자-2 가 되기 위해 읽어야 하는 토픽 갯수" + tl2_requires_time_spent_mins: "기본 사용자가 자주오는 사용자-2 가 되기 위해 몇 분 동안 포스트를 읽어야 하는지." + tl2_requires_days_visited: "기본 사용자가 자주오는 사용자-2 가 되기 위해 몇 일을 방문해야 하는지." + tl2_requires_likes_received: "기본 사용자가 자주오는 사용자-2 가 되기 위해 받아야 하는 좋아요 갯수" + tl2_requires_likes_given: "기본 사용자가 자주오는 사용자-2 가 되기 위해 눌러야하는 좋아요 수" + tl2_requires_topic_reply_count: "기본 사용자가 자주오는 사용자-2 가 되기 위해 달아야 하는 덧글 수" + tl3_requires_days_visited: "VIP 사용자-3 이 되기 위해 지난 100일 동안 최소한 사이트에 방문해야 하는 수 (0 ~ 100)" + tl3_requires_topics_replied_to: "VIP 사용자-3 이 되기 위해 지난 100일 동안 답글을 달아야하는 최소 토픽수(0 ~ )" + tl3_requires_topics_viewed: "VIP 사용자-3 이 되기 위해 지난 100일 동안 생성된 전체 토픽 중 본 토픽의 비율(%, 0 ~ 100)" + tl3_requires_posts_read: "VIP 사용자-3 이 되기 위해 지난 100일 동안 생성된 전체 포스트 중 본 포스트의 비율(%, 0 ~ 100)" + tl3_requires_topics_viewed_all_time: "VIP 사용자-3 이 되기 위해 꼭 보아야 하는 토픽의 전체 개수" + tl3_requires_posts_read_all_time: "VIP 사용자-3 이 되기 위해 꼭 보아야 하는 토픽의 전체 개수" + tl3_requires_max_flagged: "사용자가 VIP 사용자-3 이 되기 위해 지난 100일동안 서로 다른 사용자에게 최소한으로 받지 말아야하는 신고된 포스트 수(0 ~ )" + tl3_promotion_min_duration: "신뢰도가 2로 떨어진 후 다시 VIP 사용자-3 이 될 수 있는 최소 일 수" + tl3_requires_likes_given: "VIP 사용자-3 이 되기 위해 지난 100일 동안 해야 할 좋아요 수" + tl3_requires_likes_received: "VIP 사용자-3 이 되기 위해 지난 100일 동안 받아야 할 좋아요 수" + tl3_links_no_follow: "VIP 사용자-3 의 글에 있는 링크에서 rel=nofollow 를 제거하지 마시오." min_trust_to_create_topic: "새로운 토픽을 생성하기 위한 최소 신뢰도" min_trust_to_edit_wiki_post: "위키로 설정된 포스트를 수정할 수 있는 최소 신뢰도" newuser_max_links: "새로운 사용자가 포스트에 붙일 수 있는 최대 링크 개수" @@ -685,6 +743,12 @@ ko: authorized_extensions: "파일 업로드에 허용되는 확장자 리스트 '*'을 사용하면 모든 타입의 파일이 가능하다." max_similar_results: "새로운 토픽을 작성할 때, 에디터 위에 보여줄 비슷한 토픽들의 개수. 제목과 본문을 바탕으로 비교한다." title_prettify: "일반적인 제목의 오타 및 오류를 수정해준다. 모두 대문자로 쓰거나, 첫자가 소문자이거나(영문), 복수의 !, ? 혹은 마침표(.)가 중복으로 들어간 것 등" + topic_views_heat_low: "토픽이 연하게 하이라이트 되기 위한 조회수" + topic_views_heat_medium: "토픽이 적당하게 하이라이트 되기 위한 조회수" + topic_views_heat_high: "토픽이 진하게 하이라이트 되기 위한 조회수" + cold_age_days_low: "대화가 몇 일 이상 지난 후, 마지막 활동 날짜를 연하게 하이라이트" + cold_age_days_medium: "대화가 몇 일 이상 지난 후, 마지막 활동 날짜를 적당히 하이라이트" + cold_age_days_high: "대화가 몇 일 이상 지난 후, 마지막 활동 날짜를 강하게 하이라이트" faq_url: "FAQ주소가 있으면 전체 URL을 적어주세요." tos_url: "이용약관이 있으면 전체 URL을 적어주세요." privacy_policy_url: "개인정보 보호가 있으면 전체 URL을 적어주세요." @@ -694,6 +758,13 @@ ko: levenshtein_distance_spammer_emails: "스패머 메일을 체크할 때, 허용할 다른 글자 개수(fuzzy match)" reply_by_email_enabled: "이메일을 통해 토픽에 댓글을 달 수 있음." reply_by_email_address: "이메일 주소로 답글을 다는 템플릿. 예: %{reply_key}@reply.myforum.com" + enable_email_names: "이메일에서 사용자의 전체 이름을 보여준다." + pop3_polling_enabled: "POP3를 이용한 이메일 설문조사" + pop3_polling_period_mins: "이메일을 받기 위해 POP3S 계정을 체크하는 분 단위 NOTE: 재시동 필요." + pop3_polling_port: "이메일 설문조사를 위한 POP3 포트번호 " + pop3_polling_host: "이메일 설문조사를 위한 POP3 호스트" + pop3_polling_username: "이메일 설문조사를 위한 POP3 사용자 계정 이름" + pop3_polling_password: "이메일 설문조사를 위한 POP3 사용자 비밀번호" email_in: "이메일을 통해 새로운 토픽을 포스팅할 수 있도록 허가한다.(POP3 polling 필요) 각 카테고리의 \"Setting\" 탭에서 주소를 설정한다." email_in_min_trust: "이메일을 통해 새 토픽을 포스팅 할 수 있는 최소 사용자 신뢰도" email_prefix: "이메일 제목에 쓰일 [라벨]. 설정하지 않으면 기본적으로 'title(필수 설정의)' 이 된다." @@ -716,11 +787,13 @@ ko: public_user_custom_fields: "A whitelist of custom fields for a user that can be shown publically." allow_profile_backgrounds: "사용자에게 프로필 배경 이미지 업로드를 허용한다." enable_mobile_theme: "모바일 디바이스는 모바일 환경에 친화적인 테마를 사용합니다, 그리고 PC용 화면으로 전환할 수 있습니다. 만약 커스텀 스타일 시트를 사용한다면 이것을 비활성화 시키세요." + dominating_topic_minimum_percent: "한 토픽에서 한 사용자의 영향력을 결정하는 게시글 수의 퍼센트" suppress_uncategorized_badge: "토픽 리스트에서 카테고리가 없는 토픽에 대한 훈장을 보여주지 않는다." enable_names: "사용자의 전체 이름을 보여준다." display_name_on_posts: "포스트에 @username 뿐만 아니라 사용자의 전체 이름도 보여준다." short_progress_text_threshold: "토픽의 포스트 개수가 이 값을 넘어서면, 포스트 프로그래스바는 오직 현재 포스트 넘버만 보여준다. 만약 포스트 프로그래스바의 넓이는 변경하면, 이 숫자도 변경해야한다." default_code_lang: "기본 programming language syntax highlighting은 GitHub code blocks이 적용된다. (lang-auto, ruby, python etc.)" + warn_reviving_old_topic_age: "이 값보다 오래된 토픽에 답글을 달면, 오래된 토론이라는 것을 상기시키기 위해 주의 알림이 보여진다. 비활성화 값음 0 이다. " autohighlight_all_code: "형식이 지정되기 전의(되지 않은) 모든 코드 블럭들에 대해, 사용자가 언어를 지정하지 않아도 강제로 code highlighting이 적용된다." errors: invalid_email: "유효하지 않은 이메일 주소입니다." @@ -780,8 +853,6 @@ ko: other: "이 토픽은 %{count}분 뒤 자동적으로 닫혔습니다. 새로운 답글을 다실 수 없습니다." autoclosed_enabled_lastpost_days: other: "이 토픽은 마지막 답글이 달리고 %{count}일 뒤 자동적으로 닫혔습니다. 새로운 답글을 다실 수 없습니다." - autoclosed_enabled_lastpost_hours: - other: "이 토픽은 마지막 답글이 달리고 %{count}시간 뒤 자동적으로 닫혔습니다. 새로운 답글을 다실 수 없습니다." autoclosed_enabled_lastpost_minutes: other: "이 토픽은 마지막 답글이 달리고 %{count}분 뒤 자동적으로 닫혔습니다. 새로운 답글을 다실 수 없습니다." autoclosed_disabled: "이 토픽은 이제 열렸습니다. 새로운 답글을 허용합니다." @@ -798,6 +869,7 @@ ko: wait_approval: "가입해 주셔서 감사합니다. 곧 활성화 메일이 도착할 것 입니다." active: "당신의 계정이 활성화되었습니다." not_activated: "당신은 아직 로그인 할 수 없습니다. 계정 활성화 이메일을 보냈습니다. 계정을 활성화하기 위해 지침을 따라주세요." + not_allowed_from_ip_address: "이 IP 주소에서 %{username} 으로 로그인 할 수 없습니다." suspended: "당신은 %{date} 까지 로그인할 수 없습니다. " suspended_with_reason: "당신은 %{date} 까지 로그인할 수 없습니다. 이유: %{reason}" errors: "%{errors}" @@ -805,7 +877,12 @@ ko: something_already_taken: "뭔가가 이상합니다. 아마도 계정의 아이디 또는 이메일이 이미 등록된것 같습니다. 비밀번호 찾기 링크를 이용해주세요." omniauth_error: "죄송합니다, 당신의 계정을 인증하는데 문제가 있습니다. 이미 인증이 되었나요?" omniauth_error_unknown: "로그인에 문제가 있습니다. 다시 시도해주세요." + new_registrations_disabled: "지금은 새로 가입할 수 없습니다." + password_too_long: "비밀번호는 200글자 이내만 허용됩니다." + missing_user_field: "사용자 정보 입력을 덜 끝마쳤습니다." + close_window: "사용자 인증 완료. 계속하기 위해 브라우저 창을 닫으세요." user: + no_accounts_associated: "연계된 계정이 없음" username: short: "최소 %{min}자 이상이어야 합니다." long: "%{max}자 이상 사용할 수 없습니다." @@ -818,8 +895,36 @@ ko: blocked: "는 허용되지 않습니다" ip_address: blocked: "는 블락되었습니다." + invite_forum_mailer: + subject_template: "%{site_domain_name}의 %{invitee_name}님께서 당신을 초대하였습니다." + text_body_template: | + %{invitee_name} 님께서 당신을 초대하였습니다. + + > **%{site_title}** + > + > %{site_description} + + 초대를 허락하시려면 아래 링크를 클릭하여 주세요. + + %{invite_link} + + 당신은 신뢰받는 사용자로부터 초대를 받으셨습니다. 그래서 로그인 하지 않아도 됩니다. + invite_password_instructions: + subject_template: "%{site_name} 계정을 위해 비밀번호 설정" test_mailer: subject_template: "[%{site_name}] 이메일 발송 테스트" + new_version_mailer: + subject_template: "[%{site_name}] 새 Discourse 버전으로 업데이트 가능." + new_version_mailer_with_notes: + subject_template: "[%{site_name}] 업데이트 가능." + flags_reminder: + post_number: "게시글" + flags_dispositions: + agreed: "알려줘서 감사합니다. 문제가 있음에 동의하며 내용을 살펴보겠습니다." + agreed_and_deleted: "알려줘서 감사합니다. 문제가 있음에 동의하며 해당 글을 삭제했습니다." + disagreed: "알려줘서 감사합니다. 살펴보겠습니다." + deferred: "알려줘서 감사합니다. 살펴보겠습니다." + deferred_and_deleted: "알려줘서 감사합니다. 해당 글을 삭제했습니다." system_messages: post_hidden: subject_template: "포럼 신고로 인한 게시글 숨김" @@ -835,8 +940,26 @@ ko: 다시한번 환영합니다! welcome_invite: subject_template: "%{site_name} 사이트에 오신것을 환영합니다!" + backup_succeeded: + subject_template: "백업 성공" backup_failed: subject_template: "백업에 실패했습니다." + restore_succeeded: + subject_template: "복구 성공" + text_body_template: "복구 성공하였습니다." + restore_failed: + subject_template: "복구 실패" + csv_export_failed: + subject_template: "추출 실패" + text_body_template: "내보내기가 실패했습니다. 로그를 확인해주세요" + email_reject_no_account: + subject_template: "이메일 문제 -- 모르는 계정" + email_reject_empty: + subject_template: "이메일 문제 -- 컨텐츠 없음" + email_reject_post_error: + subject_template: "이메일 문제 -- 글쓰기 에러" + email_reject_post_error_specified: + subject_template: "이메일 문제 -- 글쓰기 에러" too_many_spam_flags: subject_template: "새로운 계정은 블락되었습니다." blocked_by_staff: @@ -857,6 +980,8 @@ ko: [운영자 색션에서 새로운 사용자들을 리뷰하세요](/admin/users/list/pending). unsubscribe_link: "만약 구독해지를 원하시면 [사용자 환경설정](%{user_preferences_url})을 방문하세요." + subject_re: "덧: " + subject_pm: "[PM] " user_notifications: previous_discussion: "이전 답글" unsubscribe: @@ -911,6 +1036,7 @@ ko: click_here: "클릭" from: "%{site_name} 요약" read_more: "더 읽기" + more_topics_category: "더 많은 새글:" posts: other: "%{count} 글" forgot_password: @@ -920,6 +1046,10 @@ ko: 아래 링크를 클릭하여 패스워드를 재설정하세요: %{base_url}/users/password-reset/%{email_token} + set_password: + subject_template: "[%{site_name}] 패스워드 재설정" + account_created: + subject_template: "[%{site_name}] 당신의 새 계정" authorize_email: subject_template: "[%{site_name}] 이메일 확인" text_body_template: | @@ -939,7 +1069,13 @@ ko: 만약 위의 링크를 클릭 할 수 없으면 주소를 복사하여 당신의 웹브라우저에 붙여넣으세요. page_not_found: title: "당신이 요청한 페이지를 찾을 수 없습니다. 혹시 아래와 같은 토픽을 찾으신 것은 아닌가요?" + popular_topics: "인기" + recent_topics: "최근" + see_more: "더" + search_title: "이 사이트 검색" + search_google: "Google" terms_of_service: + title: "서비스 이용약관" signup_form_message: 'I have read and accept the Terms of Service.' deleted: '삭제되었습니다' upload: @@ -954,5 +1090,31 @@ ko: size_not_found: "죄송합니다. 이미지 사이즈가 잘못 되었습니다. 혹시 깨진 이미지가 아닌가요?" flag_reason: sockpuppet: "새로운 사용자가 토픽을 만들었습니다. 그리고 같은 IP를 사용하는 새 사용자가 답글을 달았습니다. 사이트 설정의 flag_sockpuppets를 확인하세요." + email_log: + no_user: "ID %{user_id} 인 사용자를 찾을 수 없습니다" + suspended_not_pm: "사용자는 정지되어 메세지 불가" + seen_recently: "최근 보여진 사용자" + post_not_found: "해당하는 ID %{post_id} 을 찾을 수 없습니다. " + notification_already_read: "이 알림 이미 읽은 것에 대한 이메일 입니다." + topic_nil: "글 제목이 없음" + post_deleted: "글쓴이에 의해 삭제된 글" + user_suspended: "정지된 사용자" + already_read: "이미 읽은 글" + message_blank: "메세지가 비어있음" + message_to_blank: "메세지가 비어있음" + text_part_body_blank: "본문이 비어있음" + body_blank: "본문이 비어있음" color_schemes: base_theme_name: "기본 테마 색상" + guidelines: "가이드라인" + privacy: "개인정보 취급방침" + edit_this_page: "이 페이지 수정" + static_topic_first_reply: |+ + 이 토픽인 %{page_name} 페이지의 첫글 수정. + + guidelines_topic: + title: "FAQ/가이드" + tos_topic: + title: "서비스 이용약관" + privacy_topic: + title: "개인정보취급방침" diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 7db295eee0..35b6c90ef4 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -367,12 +367,14 @@ ru: activation: action: "Активируйте вашу учетную запись" already_done: "Извините, ссылка на активацию учетной записи устарела. Возможно, ваша учетная запись уже активирована?" + please_continue: "Ваша новая учетная запись успешно активирована, вы будете перенаправлены на главную страницу." continue_button: "Перейти на %{site_name}" welcome_to: "Добро пожаловать на сайт %{site_name}!" approval_required: "Ваша учетная запись должна быть вручную подтверждена модератором, чтобы вы смогли зайти на форум. Вы получите электронное письмо, когда ваша учетная запись будет подтверждена!" post_action_types: off_topic: title: 'Не по теме' + description: 'Это сообщение не имеет отношения к данному обсуждению. Возможно, его стоит перенести в соответствующий раздел.' long_form: 'отмечена как не по теме' spam: title: 'СПАМ' @@ -383,10 +385,13 @@ ru: description: 'Это сообщение может быть оскорбительным или нарушает правила поведения.' long_form: 'отметить как неуместное' notify_user: + title: 'Отправить личное сообщение @{{username}}' description: 'Это сообщение содержит что-то, на что я хочу обратить внимание автора в приватном диалоге. Не отправлять жалобу модераторам.' + long_form: 'оповещаемый пользователь' email_title: 'Ваше сообщение в теме "%{title}"' email_body: "%{link}\n\n%{message}\n" notify_moderators: + description: 'Это сообщение требует внимания модератора по другой причине.' email_title: 'Сообщение в теме "%{title}" требует внимания модератора' email_body: "%{link}\n\n%{message}\n" bookmark: diff --git a/config/locales/server.sq.yml b/config/locales/server.sq.yml index 3dc45edfc6..9418e3cccd 100644 --- a/config/locales/server.sq.yml +++ b/config/locales/server.sq.yml @@ -68,32 +68,32 @@ sq: other: "%{count} përgjigje" too_many_mentions: zero: "Na vjen keq, por s'mund të citosh anëtarë të tjerë." - one: "Sorry, you can only mention one other user in a post." - other: "Sorry, you can only mention %{count} users in a post." + one: "Na vjen keq, you can only mention one other user in a post." + other: "Na vjen keq, you can only mention %{count} users in a post." too_many_mentions_newuser: zero: "Sorry, new users can't mention other users." - one: "Sorry, new users can only mention one other user in a post." - other: "Sorry, new users can only mention %{count} users in a post." + one: "Na vjen keq, new users can only mention one other user in a post." + other: "Na vjen keq, new users can only mention %{count} users in a post." too_many_images: - zero: "Sorry, new users can't put images in posts." - one: "Sorry, new users can only put one image in a post." - other: "Sorry, new users can only put %{count} images in a post." + zero: "Na vjen keq, new users can't put images in posts." + one: "Na vjen keq, new users can only put one image in a post." + other: "Na vjen keq, new users can only put %{count} images in a post." too_many_attachments: - zero: "Sorry, new users can't put attachments in posts." - one: "Sorry, new users can only put one attachment in a post." - other: "Sorry, new users can only put %{count} attachments in a post." + zero: "Na vjen keq, new users can't put attachments in posts." + one: "Na vjen keq, new users can only put one attachment in a post." + other: "Na vjen keq, new users can only put %{count} attachments in a post." too_many_links: - zero: "Sorry, new users can't put links in posts." - one: "Sorry, new users can only put one link in a post." - other: "Sorry, new users can only put %{count} links in a post." - spamming_host: "Sorry you cannot post a link to that host." + zero: "Na vjen keq, new users can't put links in posts." + one: "Na vjen keq, new users can only put one link in a post." + other: "Na vjen keq, new users can only put %{count} links in a post." + spamming_host: "Na vjen keq, you cannot post a link to that host." user_is_suspended: "Suspended users are not allowed to post." just_posted_that: "is too similar to what you recently posted" has_already_been_used: "është përdorur më parë" invalid_characters: "përmban karaktere jo të vlefshëm" is_invalid: "is invalid; try to be a little more descriptive" next_page: "faqja tjetër →" - prev_page: "← previous page" + prev_page: "← faqe mëparshme" page_num: "Faqe %{num}" topics_in_category: "Topics in the '%{category}' category" rss_posts_in_topic: "Burimi RSS i '%{topic}'" @@ -105,7 +105,7 @@ sq: private_message_abbrev: "PM" rss_description: latest: "Temat e fundit" - hot: "Tema të nxehta" + hot: "Temat Kryesore" too_late_to_edit: "That post was created too long ago. It can no longer be edited or deleted." groups: errors: @@ -311,11 +311,11 @@ sq: one: "1y" other: "%{count}y" over_x_years: - one: "> 1y" - other: "> %{count}y" + one: "> 1v" + other: "> %{count}v" almost_x_years: - one: "1y" - other: "%{count}y" + one: "1v" + other: "%{count}v" distance_in_words_verbose: half_a_minute: "tani" less_than_x_seconds: @@ -951,9 +951,6 @@ sq: autoclosed_enabled_lastpost_days: one: "This topic was automatically closed 1 day after the last reply. New replies are no longer allowed." other: "This topic was automatically closed %{count} days after the last reply. New replies are no longer allowed." - autoclosed_enabled_lastpost_hours: - one: "This topic was automatically closed 1 hour after the last reply. New replies are no longer allowed." - other: "This topic was automatically closed count} hours after the last reply. New replies are no longer allowed." autoclosed_enabled_lastpost_minutes: one: "This topic was automatically closed 1 minute after the last reply. New replies are no longer allowed." other: "This topic was automatically closed %{count} minutes after the last reply. New replies are no longer allowed." diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index ea595c2c3a..4c1872a9cd 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -915,8 +915,6 @@ zh_CN: other: "本主题在创建 %{count} 分钟后自动关闭。不再允许添加新回复。" autoclosed_enabled_lastpost_days: other: "本主题在最后一个回复创建后 %{count} 天后自动关闭。不再允许添加新回复。" - autoclosed_enabled_lastpost_hours: - other: "本主题在最后一个回复创建后 %{count} 小时后自动关闭。不再允许添加新回复。" autoclosed_enabled_lastpost_minutes: other: "本主题在最后一个回复创建后 %{count} 分钟后自动关闭。不再允许添加新回复。" autoclosed_disabled: "本主题是开放的,可以添加新的回复。" @@ -1443,6 +1441,7 @@ zh_CN: --- %{respond_instructions} digest: + why: "在您上一次于 %{last_seen_at} 访问后,在 %{site_link} 上的简洁的摘要。" subject_template: "[%{site_name}] %{date} 的摘要" new_activity: "在您的主题和帖子里的动态:" top_topics: "热门帖子" diff --git a/public/503.sq.html b/public/503.sq.html index 07ab20d4a9..1ce9f13064 100644 --- a/public/503.sq.html +++ b/public/503.sq.html @@ -6,6 +6,6 @@

Siç e kishim planifikuar, faqja është në një fazë mirëmbajtje.

Ju lutem na vizitoni pas disa minutash.

-

Na vjen keq për shqetësimin!

+

Sorry for the inconvenience!

diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml index ecf6fffaf1..c0c2107c64 100644 --- a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml @@ -5,4 +5,8 @@ # To work with us on translations, join this project: # https://www.transifex.com/projects/p/discourse-org/ -ko_KR: {} +ko: + site_settings: + enable_imgur: "로컬에 이미지를 저장하지 않고 imgur api를 통해 이미지 업로드 활성화. " + imgur_client_id: "이미지 업로드를 위해 imgur.com의 client ID 가 필요합니다." + imgur_client_secret: "imgur.com의 client secret 키. 현재 이미지 업로드를 위해 꼭 필요하지는 않지만 언젠가를 위해." From 0398ab7514114154cd5b6c0782f80f1ff8523990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 21 Nov 2014 18:16:06 +0100 Subject: [PATCH 037/991] FIX: when adding an IP to the spammer list, check the broader ban list first FEATURE: allow admins to search users using IP ranges --- app/models/screened_ip_address.rb | 13 ++----------- app/services/user_destroyer.rb | 11 +++++++++-- lib/admin_user_index_query.rb | 4 ++-- lib/ip_addr.rb | 16 ++++++++++++++++ lib/spam_handler.rb | 6 +++--- 5 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 lib/ip_addr.rb diff --git a/app/models/screened_ip_address.rb b/app/models/screened_ip_address.rb index a4eb6e148f..d554766027 100644 --- a/app/models/screened_ip_address.rb +++ b/app/models/screened_ip_address.rb @@ -1,4 +1,5 @@ require_dependency 'screening_model' +require_dependency 'ip_addr' # A ScreenedIpAddress record represents an IP address or subnet that is being watched, # and possibly blocked from creating accounts. @@ -47,18 +48,8 @@ class ScreenedIpAddress < ActiveRecord::Base end # Return a string with the ip address and mask in standard format. e.g., "127.0.0.0/8". - # Ruby's IPAddr class has no method for getting this. def ip_address_with_mask - if ip_address - mask = ip_address.instance_variable_get(:@mask_addr).to_s(2).count('1') - if mask == 32 - ip_address.to_s - else - "#{ip_address}/#{ip_address.instance_variable_get(:@mask_addr).to_s(2).count('1')}" - end - else - nil - end + ip_address.try(:to_cidr_s) end def self.match_for_ip_address(ip_address) diff --git a/app/services/user_destroyer.rb b/app/services/user_destroyer.rb index def4bc3a91..4c0b34741c 100644 --- a/app/services/user_destroyer.rb +++ b/app/services/user_destroyer.rb @@ -1,3 +1,5 @@ +require_dependency 'ip_addr' + # Responsible for destroying a User record class UserDestroyer @@ -45,16 +47,21 @@ class UserDestroyer user.destroy.tap do |u| if u + if opts[:block_email] b = ScreenedEmail.block(u.email, ip_address: u.ip_address) b.record_match! if b end + if opts[:block_ip] && u.ip_address - b.record_match! if b = ScreenedIpAddress.watch(u.ip_address) + b = ScreenedIpAddress.watch(u.ip_address) + b.record_match! if b if u.registration_ip_address && u.ip_address != u.registration_ip_address - b.record_match! if b = ScreenedIpAddress.watch(u.registration_ip_address) + b = ScreenedIpAddress.watch(u.registration_ip_address) + b.record_match! if b end end + Post.with_deleted.where(user_id: user.id).update_all("user_id = NULL") # If this user created categories, fix those up: diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index c0e11cdc72..17d07feb11 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -42,8 +42,8 @@ class AdminUserIndexQuery def filter_by_search if params[:filter].present? - if params[:filter] =~ Resolv::IPv4::Regex || params[:filter] =~ Resolv::IPv6::Regex - @query.where('ip_address = :ip OR registration_ip_address = :ip', ip: params[:filter]) + if ip = IPAddr.new(params[:filter]) rescue nil + @query.where('ip_address <<= :ip OR registration_ip_address <<= :ip', ip: ip.to_cidr_s) else @query.where('username_lower ILIKE :filter OR email ILIKE :filter', filter: "%#{params[:filter]}%") end diff --git a/lib/ip_addr.rb b/lib/ip_addr.rb new file mode 100644 index 0000000000..c8afcde480 --- /dev/null +++ b/lib/ip_addr.rb @@ -0,0 +1,16 @@ +class IPAddr + + def to_cidr_s + if @addr + mask = @mask_addr.to_s(2).count('1') + if mask == 32 + to_s + else + "#{to_s}/#{mask}" + end + else + nil + end + end + +end diff --git a/lib/spam_handler.rb b/lib/spam_handler.rb index 1df501babd..6fc184e748 100644 --- a/lib/spam_handler.rb +++ b/lib/spam_handler.rb @@ -4,21 +4,21 @@ class SpamHandler return false if SiteSetting.max_new_accounts_per_registration_ip <= 0 tl2_plus_accounts_with_same_ip = User.where("trust_level >= ?", TrustLevel[2]) - .where("ip_address = ?", ip_address.to_s) + .where(ip_address: ip_address.to_s) .count return false if tl2_plus_accounts_with_same_ip > 0 staff_user_ids = Group[:staff].user_ids - [-1] staff_members_with_same_ip = User.where(id: staff_user_ids) - .where("ip_address = ?", ip_address.to_s) + .where(ip_address: ip_address.to_s) .count return false if staff_members_with_same_ip > 0 tl0_accounts_with_same_ip = User.unscoped .where(trust_level: TrustLevel[0]) - .where("ip_address = ?", ip_address.to_s) + .where(ip_address: ip_address.to_s) .count tl0_accounts_with_same_ip >= SiteSetting.max_new_accounts_per_registration_ip From 98404d19c5bd03ede586656a44730991b77b3122 Mon Sep 17 00:00:00 2001 From: "Jason W. May" Date: Fri, 21 Nov 2014 10:03:29 -0800 Subject: [PATCH 038/991] check that `changes` param is present --- app/controllers/admin/groups_controller.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index df21a4328a..0dd4dc8afa 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -23,15 +23,16 @@ class Admin::GroupsController < Admin::AdminController def update_patch(group) raise Discourse::InvalidAccess.new("automatic groups do not permit membership changes") if group.automatic - actions = params[:changes] - Array(actions[:add]).each do |username| - if user = User.find_by_username(username) - group.add(user) + if actions = params[:changes] + Array(actions[:add]).each do |username| + if user = User.find_by_username(username) + group.add(user) + end end - end - Array(actions[:delete]).each do |username| - if user = User.find_by_username(username) - group.remove(user) + Array(actions[:delete]).each do |username| + if user = User.find_by_username(username) + group.remove(user) + end end end From 4e0104686269f8a907ef6ed6f6a0efeda57cd46e Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 21 Nov 2014 11:13:33 -0800 Subject: [PATCH 039/991] UX: suppress long cat names in topic lists --- app/assets/stylesheets/common/base/_topic-list.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index ae53dc8a8f..c0d5c9a157 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -122,6 +122,12 @@ padding-top: 15px; padding-bottom: 15px; border-left: 6px solid; + // suppress extra long category names in tables + a { + max-width:150px; + overflow:hidden; + text-overflow:ellipsis; + } } td.stats { .unit { From 515882d224f84425b0db5440f1e0f72e1952e166 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Fri, 21 Nov 2014 20:55:04 +0530 Subject: [PATCH 040/991] FEATURE: export screened IPs list in a CSV file --- .../javascripts/admin/models/export_csv.js | 9 ++++ .../admin/routes/admin-logs.js.es6 | 15 ++++++ .../javascripts/admin/templates/logs.hbs | 3 ++ .../admin/export_csv_controller.rb | 6 +++ app/jobs/regular/export_csv_file.rb | 54 +++++++++++++------ config/locales/client.en.yml | 3 ++ config/routes.rb | 1 + 7 files changed, 75 insertions(+), 16 deletions(-) create mode 100644 app/assets/javascripts/admin/routes/admin-logs.js.es6 diff --git a/app/assets/javascripts/admin/models/export_csv.js b/app/assets/javascripts/admin/models/export_csv.js index 3afe84a1ce..9994db97d8 100644 --- a/app/assets/javascripts/admin/models/export_csv.js +++ b/app/assets/javascripts/admin/models/export_csv.js @@ -22,5 +22,14 @@ Discourse.ExportCsv.reopenClass({ bootbox.alert(I18n.t("admin.export_csv.failed")); } }); + }, + + /** + Exports screened IPs list + + @method export_screened_ips_list + **/ + exportScreenedIpsList: function() { + return Discourse.ajax("/admin/export_csv/screened_ips.json"); } }); diff --git a/app/assets/javascripts/admin/routes/admin-logs.js.es6 b/app/assets/javascripts/admin/routes/admin-logs.js.es6 new file mode 100644 index 0000000000..a16a9f1a6b --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-logs.js.es6 @@ -0,0 +1,15 @@ +export default Discourse.Route.extend({ + + actions: { + exportScreenedIps: function() { + Discourse.ExportCsv.exportScreenedIpsList().then(function(result) { + if (result.success) { + bootbox.alert(I18n.t("admin.export_csv.success")); + } else { + bootbox.alert(I18n.t("admin.export_csv.failed")); + } + }); + } + } + +}); diff --git a/app/assets/javascripts/admin/templates/logs.hbs b/app/assets/javascripts/admin/templates/logs.hbs index fc194af035..35321dd463 100644 --- a/app/assets/javascripts/admin/templates/logs.hbs +++ b/app/assets/javascripts/admin/templates/logs.hbs @@ -8,6 +8,9 @@
  • {{i18n admin.logs.logster.title}}
  • +
    + +
    diff --git a/app/controllers/admin/export_csv_controller.rb b/app/controllers/admin/export_csv_controller.rb index 82fec5d09e..2a5d53a00f 100644 --- a/app/controllers/admin/export_csv_controller.rb +++ b/app/controllers/admin/export_csv_controller.rb @@ -8,6 +8,12 @@ class Admin::ExportCsvController < Admin::AdminController render json: success_json end + def export_screened_ips_list + # export csv file in a background thread + Jobs.enqueue(:export_csv_file, entity: 'screened_ips', user_id: current_user.id) + render json: success_json + end + # download def show filename = params.fetch(:id) diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index 79f9ea38a6..f3cbadec05 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -6,6 +6,7 @@ module Jobs class ExportCsvFile < Jobs::Base CSV_USER_ATTRS = ['id','name','username','email','title','created_at','trust_level','active','admin','moderator','ip_address'] CSV_USER_STATS = ['topics_entered','posts_read_count','time_read','topic_count','post_count','likes_given','likes_received'] + SCREENED_IP_ATTRS = ['ip_address','action_type','match_count','last_match_at','created_at'] sidekiq_options retry: false attr_accessor :current_user @@ -24,20 +25,26 @@ module Jobs when 'user' query = ::AdminUserIndexQuery.new user_data = query.find_users_query.to_a - data = Array.new - + data = [] user_data.each do |user| - user_array = Array.new group_names = get_group_names(user).join(';') user_array = get_user_fields(user) user_array.push(group_names) if group_names != '' data.push(user_array) end + when 'screened_ips' + screened_ips_data = ScreenedIpAddress.order('id desc').to_a + data = [] + screened_ips_data.each do |screened_ip| + screened_ip_array = get_screened_ip_fields(screened_ip) + data.push(screened_ip_array) + end end if data && data.length > 0 set_file_path - write_csv_file(data) + header = get_header(entity) + write_csv_file(data, header) end notify_user @@ -46,7 +53,7 @@ module Jobs private def get_group_names(user) - group_names = Array.new + group_names = [] groups = user.groups groups.each do |group| group_names.push(group.name) @@ -55,7 +62,7 @@ module Jobs end def get_user_fields(user) - user_array = Array.new + user_array = [] CSV_USER_ATTRS.each do |attr| user_array.push(user.attributes[attr]) @@ -74,16 +81,31 @@ module Jobs return user_array end - def get_header - header_array = CSV_USER_ATTRS + CSV_USER_STATS + def get_screened_ip_fields(screened_ip) + screened_ip_array = [] - user_custom_fields = UserField.all - if user_custom_fields.present? - user_custom_fields.each do |custom_field| - header_array.push("#{custom_field.name} (custom user field)") - end + SCREENED_IP_ATTRS.each do |attr| + screened_ip_array.push(screened_ip.attributes[attr]) + end + + return screened_ip_array + end + + def get_header(entity) + + case entity + when 'user' + header_array = CSV_USER_ATTRS + CSV_USER_STATS + user_custom_fields = UserField.all + if user_custom_fields.present? + user_custom_fields.each do |custom_field| + header_array.push("#{custom_field.name} (custom user field)") + end + end + header_array.push("group_names") + when 'screened_ips' + header_array = SCREENED_IP_ATTRS end - header_array.push("group_names") return header_array end @@ -95,10 +117,10 @@ module Jobs FileUtils.mkdir_p(dir) unless Dir.exists?(dir) end - def write_csv_file(data) + def write_csv_file(data, header) # write to CSV file CSV.open(File.expand_path("#{ExportCsv.base_directory}/#{@file_name}", __FILE__), "w") do |csv| - csv << get_header + csv << header data.each do |value| csv << value end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index f33e0d720d..05b18c4f81 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1686,6 +1686,9 @@ en: users: text: "Export Users" title: "Export user list in a CSV file." + screened_ips: + text: "Export Screened IPs" + title: "Export screened IPs list in a CSV file." success: "Export has been initiated, you will be notified shortly with progress." failed: "Export failed. Please check the logs." diff --git a/config/routes.rb b/config/routes.rb index fd8c3e357a..c417eeb8cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -159,6 +159,7 @@ Discourse::Application.routes.draw do resources :export_csv, constraints: AdminConstraint.new do collection do get "users" => "export_csv#export_user_list" + get "screened_ips" => "export_csv#export_screened_ips_list" end member do get "" => "export_csv#show", constraints: { id: /[^\/]+/ } From 304b96aa8197b61522e0b608b6fd0aebfbf1b35a Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 21 Nov 2014 11:40:11 -0800 Subject: [PATCH 041/991] UX: suppress long cat names in cat drop down too --- .../stylesheets/common/base/_topic-list.scss | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index c0d5c9a157..29ee46e008 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -102,6 +102,13 @@ } } + td.category a { + // suppress extra long category names in tables + max-width:150px; + overflow:hidden; + text-overflow:ellipsis; + } + } @@ -122,12 +129,6 @@ padding-top: 15px; padding-bottom: 15px; border-left: 6px solid; - // suppress extra long category names in tables - a { - max-width:150px; - overflow:hidden; - text-overflow:ellipsis; - } } td.stats { .unit { @@ -228,15 +229,16 @@ ol.category-breadcrumb { a.badge-category { font-size: 13px; font-weight: bold; - padding: 4px 0; float: none; line-height: 19px; text-transform: none; width: 100%; min-width: 102px; - text-align: center; margin-right: 20px; - margin-bottom: 7px; + margin-bottom: 0; + max-width:200px; + overflow:hidden; + text-overflow:ellipsis; } } From 428b71687fde95a91256627589974b92cb8135ea Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Sat, 22 Nov 2014 01:16:10 +0530 Subject: [PATCH 042/991] return a promise instead of triggering bootbox in model --- app/assets/javascripts/admin/models/export_csv.js | 8 +------- .../javascripts/admin/routes/admin_users_list_routes.js | 8 +++++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/admin/models/export_csv.js b/app/assets/javascripts/admin/models/export_csv.js index 9994db97d8..b747cd449b 100644 --- a/app/assets/javascripts/admin/models/export_csv.js +++ b/app/assets/javascripts/admin/models/export_csv.js @@ -15,13 +15,7 @@ Discourse.ExportCsv.reopenClass({ @method export_user_list **/ exportUserList: function() { - return Discourse.ajax("/admin/export_csv/users.json").then(function(result) { - if (result.success) { - bootbox.alert(I18n.t("admin.export_csv.success")); - } else { - bootbox.alert(I18n.t("admin.export_csv.failed")); - } - }); + return Discourse.ajax("/admin/export_csv/users.json"); }, /** diff --git a/app/assets/javascripts/admin/routes/admin_users_list_routes.js b/app/assets/javascripts/admin/routes/admin_users_list_routes.js index bc1516c182..a6b2b330b7 100644 --- a/app/assets/javascripts/admin/routes/admin_users_list_routes.js +++ b/app/assets/javascripts/admin/routes/admin_users_list_routes.js @@ -13,7 +13,13 @@ Discourse.AdminUsersListRoute = Discourse.Route.extend({ actions: { exportUsers: function() { - Discourse.ExportCsv.exportUserList(); + Discourse.ExportCsv.exportUserList().then(function(result) { + if (result.success) { + bootbox.alert(I18n.t("admin.export_csv.success")); + } else { + bootbox.alert(I18n.t("admin.export_csv.failed")); + } + }); } } }); From 7455e81b316b3db2601268a567ebd206911e2c18 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Sat, 22 Nov 2014 01:41:59 +0530 Subject: [PATCH 043/991] sort screened IPs by match_count --- app/controllers/admin/screened_ip_addresses_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/screened_ip_addresses_controller.rb b/app/controllers/admin/screened_ip_addresses_controller.rb index 00b5acb27f..efd9e9ce9a 100644 --- a/app/controllers/admin/screened_ip_addresses_controller.rb +++ b/app/controllers/admin/screened_ip_addresses_controller.rb @@ -3,7 +3,7 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController before_filter :fetch_screened_ip_address, only: [:update, :destroy] def index - screened_ip_addresses = ScreenedIpAddress.limit(200).order('id desc').to_a + screened_ip_addresses = ScreenedIpAddress.limit(200).order('match_count desc').to_a render_serialized(screened_ip_addresses, ScreenedIpAddressSerializer) end From 3f67ace40dcdd3a46ffb749e59a503d1463fa9ca Mon Sep 17 00:00:00 2001 From: Mikulas Date: Sat, 22 Nov 2014 11:09:41 +0100 Subject: [PATCH 044/991] translate welcome message images to cs --- public/images/welcome/quote-reply-cs.png | Bin 0 -> 40858 bytes .../images/welcome/username-completion-cs.png | Bin 0 -> 37457 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/images/welcome/quote-reply-cs.png create mode 100644 public/images/welcome/username-completion-cs.png diff --git a/public/images/welcome/quote-reply-cs.png b/public/images/welcome/quote-reply-cs.png new file mode 100644 index 0000000000000000000000000000000000000000..d3bb1bc934dfcb50d41bd9166fa6c0261979a271 GIT binary patch literal 40858 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%0AbmSai8GW zqzY5X7nHHpVG{sX7CiH7Vcjhl-mvQ#T9YSPFUyzrYfGaCmZ43qn)vd0Hz%tsekr#~ zR>_fP!D$6?|~|zP*-S5Q)8zT^Q;d4;aC%Yj~7ClpRK?G+;TaU1a%&yeRj z)l`n#TV@vZW!7l_qGKNU({zyxBp2A42`Z^?pq&;1E@J3Ctq@5o&Jj!pKQ8d$V*Ly% zG@6gmEz$~2$W4PO&#PAB$MnDfF43!jt0swqyc?pk&1Oj67R~ijCDf$fBk{1{o}ifB zL4Sl3iA4xf(7xgKXiZP&RC!lI5SU0t+ntUf|u?$e8}(x=!~-#0kXVt8gj zConN#TY9ETP=rYb4Aq>GTSas(HXN*jCk3{OU@=Cuei7CA;NV?gk~9;~6gv!WeWbty z(Kgwj=|~N-pBO{NCa%8Cm8({-k_?&IftkYtJGquHhc6H+g*lmlYc+HkT#y~(byrw- zQQfMgG=PT$WDD9nE4WN|)^i^VT;aJphEMA6J)THO(KT7}gdd4K$bl!CtWm%fe=ay_ zJL0Ypyx3g8T<@_~P%)sdb+JnsJ*td7m~Vj+pL92t_o%n6_ecbglXH0yfxd%qFs_8# zy#$i%r}B+RZUJq0wxEiXiRd~mS=-bhU05Z?K^#bmEhqE~>r(*$t*g;h%tn6`#D7 z@2BN4s;@g=4w2|}pg)}3+Gf{btSw#jKHaQ3d4nAGIsKsTw#U#=Wb$ICJ{s20O4wMxR*K6MP)Xio@?G1$& z!#|4VKiz!NceKdeAKItUbe3(=Nar8&b!3JHYbd?Ot+2isU{V*R_$e>1CnSFfj;{Gg#Gfj$Q$Mucdme3U^HDKFt)5zpTntKj~o+5vj~OZ83d- z=PPBG>;i-Xv&=(6aW+dk?T9NWW@#kV41?E0thCzug}jn6la}WRSl5$bd@5obUvblQ z-TugJ>kl{NuFZHD?B5b2)Ye4LA@Ittx{h`XD%H#We^{t(`h)U0Yu9(bXNu_-WMspSUxD`e*0%x^RAzWBuXr~UET??f+ArnlNK43g~Dj_W>{U$qH& z%wCF26g}7gBT-bLJ&D07N7kwh0HG%}Lx&*t%OUAS0ODD*4ce~6qHw`nC8yI-Acu

    j_6}S*4eGH*sVcor&y*v5g_ogK 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 literal 0 HcmV?d00001 diff --git a/public/images/welcome/username-completion-cs.png b/public/images/welcome/username-completion-cs.png new file mode 100644 index 0000000000000000000000000000000000000000..bce11a1a12b3291c82e9a64b4571d1131f639488 GIT binary patch literal 37457 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 literal 0 HcmV?d00001 From d3b24b625b8215fbb4b006215ec34cd7447bfca2 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Nov 2014 10:02:22 +1100 Subject: [PATCH 045/991] Add more SSO logging for failure conditions --- app/controllers/session_controller.rb | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 1f8680e31f..3f9c2fd4aa 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -43,15 +43,25 @@ class SessionController < ApplicationController return_path = sso.return_path sso.expire_nonce! - if user = sso.lookup_or_create_user - if SiteSetting.must_approve_users? && !user.approved? - # TODO: need an awaiting approval message here + begin + if user = sso.lookup_or_create_user + if SiteSetting.must_approve_users? && !user.approved? + # TODO: need an awaiting approval message here + else + log_on_user user + end + redirect_to return_path else - log_on_user user + render text: "unable to log on user", status: 500 end - redirect_to return_path - else - render text: "unable to log on user", status: 500 + rescue => e + details = {} + SingleSignOn::ACCESSORS.each do |a| + details[a] = sso.send(a) + end + Discourse.handle_exception(e, details) + + render text: "unable to log on user contact site admin (see /logs for more info)", status: 500 end end From 7e3df4287f74843ffc8118ec3c616e80b82534d7 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Nov 2014 10:34:29 +1100 Subject: [PATCH 046/991] Add more logging for blank posts to detect culprit --- app/models/post.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models/post.rb b/app/models/post.rb index 81f03cd911..0d6252e199 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -170,7 +170,16 @@ class Post < ActiveRecord::Base cloned[1][:omit_nofollow] = true post_analyzer.cook(*cloned) end - Plugin::Filter.apply( :after_post_cook, self, cooked ) + + new_cooked = Plugin::Filter.apply(:after_post_cook, self, cooked) + + if new_cooked != cooked && new_cooked.blank? + Rails.logger.warn("Plugin is blanking out post: #{self.url}\nraw: #{self.raw}") + elsif new_cooked.blank? + Rails.logger.warn("Blank post detected post: #{self.url}\nraw: #{self.raw}") + end + + new_cooked end # Sometimes the post is being edited by someone else, for example, a mod. From 9e1e3df6c968b0c209daa3f19878e2c6bed059e9 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Nov 2014 12:15:51 +1100 Subject: [PATCH 047/991] FEATURE: Localize SSO error messages --- app/controllers/session_controller.rb | 8 ++++---- config/locales/server.en.yml | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 3f9c2fd4aa..f8efa8db5f 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -36,7 +36,7 @@ class SessionController < ApplicationController sso = DiscourseSingleSignOn.parse(request.query_string) if !sso.nonce_valid? - render text: "Timeout expired, please try logging in again.", status: 500 + render text: I18n.t("sso.timeout_expired"), status: 500 return end @@ -46,13 +46,13 @@ class SessionController < ApplicationController begin if user = sso.lookup_or_create_user if SiteSetting.must_approve_users? && !user.approved? - # TODO: need an awaiting approval message here + render text: I18n.t("sso.account_not_approved"), status: 403 else log_on_user user end redirect_to return_path else - render text: "unable to log on user", status: 500 + render text: I18n.t("sso.not_found"), status: 500 end rescue => e details = {} @@ -61,7 +61,7 @@ class SessionController < ApplicationController end Discourse.handle_exception(e, details) - render text: "unable to log on user contact site admin (see /logs for more info)", status: 500 + render text: I18n.t("sso.unknown_error"), status: 500 end end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 09e542ca8f..e4aceb309d 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1060,6 +1060,12 @@ en: 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" From 16cdfcffb50552152591109ae1457bd45d67f146 Mon Sep 17 00:00:00 2001 From: Kris Aubuchon Date: Sun, 23 Nov 2014 20:51:14 -0500 Subject: [PATCH 048/991] fixing the reply-to tab z-index bug --- .../javascripts/discourse/templates/post.hbs | 23 ++++++++++--------- .../stylesheets/desktop/topic-post.scss | 8 +++---- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/post.hbs b/app/assets/javascripts/discourse/templates/post.hbs index 47a468537f..6ff402a4cc 100644 --- a/app/assets/javascripts/discourse/templates/post.hbs +++ b/app/assets/javascripts/discourse/templates/post.hbs @@ -6,17 +6,7 @@

    - {{#if showUserReplyTab}} - - {{#if loadingReplyHistory}} - {{i18n loading}} - {{else}} - - {{avatar reply_to_user imageSize="tiny"}} - {{reply_to_user.username}} - {{/if}} - - {{/if}} +
    {{#unless userDeleted}} @@ -59,6 +49,17 @@ {{/if}} {{/if}} + {{#if showUserReplyTab}} + + {{#if loadingReplyHistory}} + {{i18n loading}} + {{else}} + + {{avatar reply_to_user imageSize="tiny"}} + {{reply_to_user.username}} + {{/if}} + + {{/if}}
    {{fa-icon "circle"}}
    diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 52db9ca6a6..1ffac9265f 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -30,13 +30,11 @@ h1 .topic-statuses .topic-status i { } .reply-to-tab { - position: absolute; - right: 420px; z-index: 400; - padding: 13px 6px 5px; - font-size: 12px; + font-size: 13px; + float: right; + margin: 1px 25px 0 0; color: scale-color($primary, $lightness: 50%); - } .via-email .reply-to-tab { From 1c498eb49107faae853484ef1f33ad3861fc1a02 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Nov 2014 15:42:39 +1100 Subject: [PATCH 049/991] FEATURE: API endpoint for inviting an admin --- app/controllers/admin/users_controller.rb | 29 +++++++++++++++++++ config/routes.rb | 1 + .../admin/users_controller_spec.rb | 12 ++++++++ 3 files changed, 42 insertions(+) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index bd191bbe48..c3208b369e 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -288,6 +288,35 @@ class Admin::UsersController < Admin::AdminController render json: success_json end + def invite_admin + + email = params[:email] + unless user = User.find_by_email(email) + name = params[:name] if params[:name].present? + username = params[:username] if params[:username].present? + + user = User.new(email: email) + user.password = SecureRandom.hex + user.username = UserNameSuggester.suggest(username || name || email) + user.name = User.suggest_name(name || username || email) + end + + user.active = true + user.save! + user.grant_admin! + user.change_trust_level!(4) + user.email_tokens.update_all confirmed: true + + email_token = user.email_tokens.create(email: user.email) + Jobs.enqueue(:user_email, + type: :account_created, + user_id: user.id, + email_token: email_token.token) + + render json: success_json + + end + private def fetch_user diff --git a/config/routes.rb b/config/routes.rb index c417eeb8cd..854b009101 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -84,6 +84,7 @@ Discourse::Application.routes.draw do post "users/sync_sso" => "users#sync_sso", constraints: AdminConstraint.new + post "users/invite_admin" => "users#invite_admin", constraints: AdminConstraint.new resources :impersonate, constraints: AdminConstraint.new diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index d65359f22c..1740be819f 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -427,6 +427,18 @@ describe Admin::UsersController do end + context ".invite_admin" do + it 'should invite admin' do + xhr :post, :invite_admin, name: 'Bill', username: 'bill22', email: 'bill@bill.com' + response.should be_success + + u = User.find_by(email: 'bill@bill.com') + u.name.should == "Bill" + u.username.should == "bill22" + u.admin.should == true + end + end + end it 'can sync up sso' do From 4d936325e747db44a0dd50aea5b5f3f4cd90ee8e Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Nov 2014 17:16:11 +1100 Subject: [PATCH 050/991] test forwarding works as expected --- .../auth/default_current_user_provider_spec.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/components/auth/default_current_user_provider_spec.rb b/spec/components/auth/default_current_user_provider_spec.rb index 99c5e2e96c..153ac13955 100644 --- a/spec/components/auth/default_current_user_provider_spec.rb +++ b/spec/components/auth/default_current_user_provider_spec.rb @@ -43,13 +43,18 @@ describe Auth::DefaultCurrentUserProvider do it "allows a user with a matching ip" do user = Fabricate(:user) - ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1, allowed_ips: ['10.0.0.0/24']) + ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1, allowed_ips: ['100.0.0.0/24']) found_user = provider("/?api_key=hello&api_username=#{user.username.downcase}", - "REMOTE_ADDR" => "10.0.0.22").current_user + "REMOTE_ADDR" => "100.0.0.22").current_user found_user.id.should == user.id + + found_user = provider("/?api_key=hello&api_username=#{user.username.downcase}", + "HTTP_X_FORWARDED_FOR" => "10.1.1.1, 100.0.0.22").current_user + found_user.id.should == user.id + end it "finds a user for a correct system api key" do From 0111cb4cc2baec873f6cded5c630729ed9d7352a Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Nov 2014 17:54:17 +1100 Subject: [PATCH 051/991] FEATURE: serialize the fact we are https in about --- app/models/about.rb | 4 ++++ app/serializers/about_serializer.rb | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/about.rb b/app/models/about.rb index ceaff5ea96..83dea16ec1 100644 --- a/app/models/about.rb +++ b/app/models/about.rb @@ -8,6 +8,10 @@ class About Discourse::VERSION::STRING end + def https + SiteSetting.use_https + end + def title SiteSetting.title end diff --git a/app/serializers/about_serializer.rb b/app/serializers/about_serializer.rb index b56a165880..63d795499e 100644 --- a/app/serializers/about_serializer.rb +++ b/app/serializers/about_serializer.rb @@ -6,5 +6,6 @@ class AboutSerializer < ApplicationSerializer :description, :title, :locale, - :version + :version, + :https end From 23a97bffbeab66287d6ebed256f70fcc9c88da7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 11:08:13 +0100 Subject: [PATCH 052/991] FIX: make IP lookup available to moderators --- app/assets/javascripts/admin/templates/user_index.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/templates/user_index.hbs b/app/assets/javascripts/admin/templates/user_index.hbs index 2b5f9b8d36..5ce3303734 100644 --- a/app/assets/javascripts/admin/templates/user_index.hbs +++ b/app/assets/javascripts/admin/templates/user_index.hbs @@ -106,7 +106,7 @@
    {{i18n user.ip_address.title}}
    {{ip_address}}
    - {{#if currentUser.admin}} + {{#if currentUser.staff}} @@ -119,7 +119,7 @@
    {{i18n user.registration_ip_address.title}}
    {{registration_ip_address}}
    - {{#if currentUser.admin}} + {{#if currentUser.staff}} {{ip-lookup ip=registration_ip_address user_id=id}} {{/if}}
    From 7611eb4e78fe25f0ee1911608f015a15ddc34b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 11:26:29 +0100 Subject: [PATCH 053/991] UX: bold all the entries in the screened IP list that are more than a single IP --- .../controllers/admin-log-screened-ip-address.js.es6 | 4 ++++ .../templates/logs/screened_ip_addresses_list_item.hbs | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 b/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 index edd2e09148..786797f665 100644 --- a/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 @@ -2,6 +2,10 @@ export default Ember.ObjectController.extend({ editing: false, savedIpAddress: null, + isRange: function() { + return this.get("ip_address").indexOf("/") > 0; + }.property("ip_address"), + actions: { allow: function(record) { record.set('action_name', 'do_nothing'); diff --git a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs index 23a21becc6..ed9e7fe8b2 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs @@ -2,7 +2,13 @@ {{#if editing}} {{text-field value=ip_address autofocus="autofocus"}} {{else}} - {{ip_address}} + + {{#if isRange}} + {{ip_address}} + {{else}} + {{ip_address}} + {{/if}} + {{/if}}
    From 20044da47423e481f68aaef2d69b32e45d84d514 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 24 Nov 2014 19:46:15 +0530 Subject: [PATCH 054/991] FIX: cleanup commas in email From and Reply-to fields --- lib/email.rb | 2 +- spec/components/email/message_builder_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/email.rb b/lib/email.rb index d0029b3d0c..06413f0f25 100644 --- a/lib/email.rb +++ b/lib/email.rb @@ -23,7 +23,7 @@ module Email def self.cleanup_alias(name) # TODO: I'm sure there are more, but I can't find a list - name ? name.gsub(/[:<>]/, '') : name + name ? name.gsub(/[:<>,]/, '') : name end end diff --git a/spec/components/email/message_builder_spec.rb b/spec/components/email/message_builder_spec.rb index 8586416f41..bdadaed3ee 100644 --- a/spec/components/email/message_builder_spec.rb +++ b/spec/components/email/message_builder_spec.rb @@ -58,7 +58,7 @@ describe Email::MessageBuilder do end it "cleans up the site title" do - SiteSetting.stubs(:title).returns(">>>Obnoxious Title: Deal With It<<<") + SiteSetting.stubs(:title).returns(">>>Obnoxious Title: Deal, With It<<<") expect(reply_by_email_builder.header_args['Reply-To']).to eq("Obnoxious Title Deal With It ") end end @@ -253,12 +253,12 @@ describe Email::MessageBuilder do end it "cleans up aliases in the from_alias arg" do - builder = Email::MessageBuilder.new(to_address, from_alias: "Finn: the Dog <3", from: finn_email) + builder = Email::MessageBuilder.new(to_address, from_alias: "Finn: the Dog, <3", from: finn_email) builder.build_args[:from].should == "Finn the Dog 3 <#{finn_email}>" end it "cleans up the email_site_title" do - SiteSetting.stubs(:email_site_title).returns("::>>>Best Forum EU: Award Winning<<<") + SiteSetting.stubs(:email_site_title).returns("::>>>Best Forum, EU: Award Winning<<<") expect(build_args[:from]).to eq("Best Forum EU Award Winning <#{SiteSetting.notification_email}>") end From 102319131588b19ba843d54f536a4466b1cd12dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 17:25:48 +0100 Subject: [PATCH 055/991] FEATURE: roll up function for 123.456.789.* ranges --- .../admin-logs-screened-ip-addresses.js.es6 | 8 +++ .../admin/models/screened_ip_address.js | 4 ++ .../templates/logs/screened_ip_addresses.hbs | 2 +- .../components/screened-ip-address-form.hbs | 2 +- .../admin/screened_ip_addresses_controller.rb | 52 +++++++++++++++++++ config/locales/client.en.yml | 3 ++ config/locales/server.en.yml | 1 + config/routes.rb | 6 ++- config/site_settings.yml | 1 + .../screened_ip_addresses_controller_spec.rb | 35 ++++++++++--- 10 files changed, 104 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 index aeaaffeaf3..f6adb5b941 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 @@ -14,6 +14,14 @@ export default Ember.ArrayController.extend(Discourse.Presence, { actions: { recordAdded: function(arg) { this.get("model").unshiftObject(arg); + }, + + rollUp: function() { + var self = this; + this.set("loading", true) + return Discourse.ScreenedIpAddress.rollUp().then(function() { + self.send("show"); + }); } } }); diff --git a/app/assets/javascripts/admin/models/screened_ip_address.js b/app/assets/javascripts/admin/models/screened_ip_address.js index 985ee81c39..22ff7d9153 100644 --- a/app/assets/javascripts/admin/models/screened_ip_address.js +++ b/app/assets/javascripts/admin/models/screened_ip_address.js @@ -51,5 +51,9 @@ Discourse.ScreenedIpAddress.reopenClass({ return Discourse.ScreenedIpAddress.create(b); }); }); + }, + + rollUp: function() { + return Discourse.ajax("/admin/logs/screened_ip_addresses/roll_up", { type: "POST" }); } }); diff --git a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs index 438e51b395..4a4350fafa 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs @@ -1,5 +1,5 @@

    {{i18n admin.logs.screened_ips.description}}

    - + {{screened-ip-address-form action="recordAdded"}}
    diff --git a/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs b/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs index 6b497d6ab5..d4a7df9a96 100644 --- a/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs +++ b/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs @@ -1,4 +1,4 @@ {{i18n admin.logs.screened_ips.form.label}} {{text-field value=ip_address disabled=formSubmitted class="ip-address-input" placeholderKey="admin.logs.screened_ips.form.ip_address" autocorrect="off" autocapitalize="off"}} {{combo-box content=actionNames value=actionName}} - + diff --git a/app/controllers/admin/screened_ip_addresses_controller.rb b/app/controllers/admin/screened_ip_addresses_controller.rb index efd9e9ce9a..e9c25aa402 100644 --- a/app/controllers/admin/screened_ip_addresses_controller.rb +++ b/app/controllers/admin/screened_ip_addresses_controller.rb @@ -29,6 +29,58 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController render json: success_json end + def roll_up + # 1 - retrieve all subnets that needs roll up + sql = <<-SQL + SELECT network(inet(host(ip_address) || './24')) AS ip_range + FROM screened_ip_addresses + WHERE action_type = :action_type + AND family(ip_address) = 4 + AND masklen(ip_address) = 32 + GROUP BY ip_range + HAVING COUNT(*) >= :min_count + SQL + + subnets = ScreenedIpAddress.exec_sql(sql, + action_type: ScreenedIpAddress.actions[:block], + min_count: SiteSetting.min_ban_entries_for_roll_up).values.flatten + + subnets.each do |subnet| + # 2 - create subnet if not already exists + ScreenedIpAddress.new(ip_address: subnet).save unless ScreenedIpAddress.where(ip_address: subnet).first + + # 3 - update stats + sql = <<-SQL + UPDATE screened_ip_addresses + SET match_count = sum_match_count, + created_at = min_created_at, + last_match_at = max_last_match_at + FROM ( + SELECT SUM(match_count) AS sum_match_count, + MIN(created_at) AS min_created_at, + MAX(last_match_at) AS max_last_match_at + FROM screened_ip_addresses + WHERE action_type = :action_type + AND family(ip_address) = 4 + AND masklen(ip_address) = 32 + AND ip_address << :ip_address + ) s + WHERE ip_address = :ip_address + SQL + + ScreenedIpAddress.exec_sql(sql, action_type: ScreenedIpAddress.actions[:block], ip_address: subnet) + + # 4 - remove old matches + ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block]) + .where("family(ip_address) = 4") + .where("masklen(ip_address) = 32") + .where("ip_address << ?", subnet) + .delete_all + end + + render json: success_json + end + private def allowed_params diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 05b18c4f81..91a1dc961f 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1870,6 +1870,9 @@ en: label: "New:" ip_address: "IP address" add: "Add" + roll_up: + text: "Roll up" + title: "Creates new subnet ban entries if there are at least 'min_ban_entries_for_roll_up' entries." logster: title: "Error Logs" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index e4aceb309d..1f5b0ef100 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -942,6 +942,7 @@ en: levenshtein_distance_spammer_emails: "When matching spammer emails, number of characters difference that will still allow a fuzzy match." max_new_accounts_per_registration_ip: "If there are already (n) trust level 0 accounts from this IP (and none is a staff member or at TL2 or higher), stop accepting new signups from that IP." + min_ban_entries_for_roll_up: "When clicking the Roll up button, will create a new subnet ban entry if there are at least (N) entries." reply_by_email_enabled: "Enable replying to topics via email." reply_by_email_address: "Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" diff --git a/config/routes.rb b/config/routes.rb index 854b009101..78f923d67b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -101,7 +101,11 @@ Discourse::Application.routes.draw do scope "/logs" do resources :staff_action_logs, only: [:index] resources :screened_emails, only: [:index, :destroy] - resources :screened_ip_addresses, only: [:index, :create, :update, :destroy] + resources :screened_ip_addresses, only: [:index, :create, :update, :destroy] do + collection do + post "roll_up" + end + end resources :screened_urls, only: [:index] end diff --git a/config/site_settings.yml b/config/site_settings.yml index 8ce50b26fd..6bd32e1d08 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -577,6 +577,7 @@ spam: min: 0 max: 3 max_new_accounts_per_registration_ip: 3 + min_ban_entries_for_roll_up: 5 rate_limits: unique_posts_mins: diff --git a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb index edd6af13bb..ae56646f49 100644 --- a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb +++ b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe Admin::ScreenedIpAddressesController do + it "is a subclass of AdminController" do (Admin::ScreenedIpAddressesController < Admin::AdminController).should == true end @@ -8,15 +9,35 @@ describe Admin::ScreenedIpAddressesController do let!(:user) { log_in(:admin) } describe 'index' do - before do - xhr :get, :index - end - - subject { response } - it { should be_success } it 'returns JSON' do - ::JSON.parse(subject.body).should be_a(Array) + xhr :get, :index + response.should be_success + JSON.parse(response.body).should be_a(Array) end + end + + describe 'roll_up' do + + it "works" do + SiteSetting.expects(:min_ban_entries_for_roll_up).returns(3) + + Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.3.6", match_count: 1) + + Fabricate(:screened_ip_address, ip_address: "42.42.42.4", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "42.42.42.5", match_count: 1) + + xhr :post, :roll_up + response.should be_success + + subnet = ScreenedIpAddress.where(ip_address: "1.2.3.0/24").first + subnet.should be_present + subnet.match_count.should == 3 + end + + end + end From 17b6d3a2fe011c19b7283559d9d9444d519f5c88 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 24 Nov 2014 11:40:21 -0500 Subject: [PATCH 056/991] FIX: Mailing list mode was not checking for user deleted posts --- .../notify_mailing_list_subscribers.rb | 7 +-- .../notify_mailing_list_subscribers_spec.rb | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 spec/jobs/notify_mailing_list_subscribers_spec.rb diff --git a/app/jobs/regular/notify_mailing_list_subscribers.rb b/app/jobs/regular/notify_mailing_list_subscribers.rb index c8db65406e..5ee7897974 100644 --- a/app/jobs/regular/notify_mailing_list_subscribers.rb +++ b/app/jobs/regular/notify_mailing_list_subscribers.rb @@ -4,13 +4,10 @@ module Jobs def execute(args) post_id = args[:post_id] - if post_id - post = Post.with_deleted.find_by(id: post_id) - # our topic can be deleted as well - return if (post && post.trashed?) || !post.topic - end + post = post_id ? Post.with_deleted.find_by(id: post_id) : nil raise Discourse::InvalidParameters.new(:post_id) unless post + return if post.trashed? || post.user_deleted? || (!post.topic) users = User.activated.not_blocked.not_suspended.real diff --git a/spec/jobs/notify_mailing_list_subscribers_spec.rb b/spec/jobs/notify_mailing_list_subscribers_spec.rb new file mode 100644 index 0000000000..c7b9923b3c --- /dev/null +++ b/spec/jobs/notify_mailing_list_subscribers_spec.rb @@ -0,0 +1,60 @@ +require "spec_helper" + +describe Jobs::NotifyMailingListSubscribers do + + context "with mailing list on" do + let(:user) { Fabricate(:user, mailing_list_mode: true) } + + context "with a valid post" do + let!(:post) { Fabricate(:post, user: user) } + + it "sends the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).once + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + context "with a deleted post" do + let!(:post) { Fabricate(:post, user: user, deleted_at: Time.now) } + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + context "with a user_deleted post" do + let!(:post) { Fabricate(:post, user: user, user_deleted: true) } + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + context "with a deleted topic" do + let!(:post) { Fabricate(:post, user: user) } + + before do + post.topic.update_column(:deleted_at, Time.now) + end + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + end + + context "with mailing list off" do + let(:user) { Fabricate(:user, mailing_list_mode: false) } + let!(:post) { Fabricate(:post, user: user) } + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + +end From 7f9747f55cb2e59c83374e10c46bab8de938cefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 18:04:34 +0100 Subject: [PATCH 057/991] moar camelCase --- app/assets/javascripts/admin/components/ip-lookup.js.es6 | 6 +++--- app/assets/javascripts/admin/templates/user_index.hbs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index b7dbaf764b..ee6cc03738 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -26,7 +26,7 @@ export default Ember.Component.extend({ this.set("otherAccountsLoading", true); Discourse.AdminUser.findAll("active", { "ip": this.get("ip"), - "exclude": this.get("user_id"), + "exclude": this.get("userId"), "order": "trust_level DESC" }).then(function (users) { self.setProperties({ @@ -41,7 +41,7 @@ export default Ember.Component.extend({ this.set("show", false); }, - deleteAllOtherAccounts: function() { + deleteOtherAccounts: function() { var self = this; bootbox.confirm(I18n.t("ip_lookup.confirm_delete_other_accounts"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { if (confirmed) { @@ -50,7 +50,7 @@ export default Ember.Component.extend({ type: "DELETE", data: { "ip": self.get("ip"), - "exclude": self.get("user_id"), + "exclude": self.get("userId"), "order": "trust_level DESC" } }).then(function() { diff --git a/app/assets/javascripts/admin/templates/user_index.hbs b/app/assets/javascripts/admin/templates/user_index.hbs index 5ce3303734..63b9d64253 100644 --- a/app/assets/javascripts/admin/templates/user_index.hbs +++ b/app/assets/javascripts/admin/templates/user_index.hbs @@ -110,7 +110,7 @@ - {{ip-lookup ip=ip_address user_id=id}} + {{ip-lookup ip=ip_address userId=id}} {{/if}}
    @@ -120,7 +120,7 @@
    {{registration_ip_address}}
    {{#if currentUser.staff}} - {{ip-lookup ip=registration_ip_address user_id=id}} + {{ip-lookup ip=registration_ip_address userId=id}} {{/if}}
    From 7b1c0019323c5b7b2d21d3950ce122deeb4594c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 18:05:40 +0100 Subject: [PATCH 058/991] FIX: limit other accounts deletion to 50 accounts otherwise it'll feel too slow --- .../discourse/templates/components/ip-lookup.hbs | 4 ++-- app/controllers/admin/users_controller.rb | 2 ++ config/locales/client.en.yml | 2 +- lib/admin_user_index_query.rb | 7 ++++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs index 979ee6b4da..f7c7851bfe 100644 --- a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs @@ -41,8 +41,8 @@ {{i18n ip_lookup.other_accounts}} {{other_accounts.length}} {{#if other_accounts.length}} - {{/if}} diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index c3208b369e..9158fa52de 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -278,6 +278,8 @@ class Admin::UsersController < Admin::AdminController params.require(:exclude) params.require(:order) + params[:limit] = 50 + user_destroyer = UserDestroyer.new(current_user) options = { delete_posts: true, block_email: true, block_urls: true, block_ip: true, delete_as_spammer: true } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 91a1dc961f..0d189d9386 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -279,7 +279,7 @@ en: organisation: Organization phone: Phone other_accounts: "Other accounts with this IP address:" - delete_all: "Delete all" + delete_other_accounts: "Delete up to 50" username: "username" trust_level: "TL" read_time: "read time" diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index 17d07feb11..a5d3d66ff7 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -62,6 +62,11 @@ class AdminUserIndexQuery end end + def limit + limit = params[:limit].to_i + @query.limit(limit > 0 ? limit : 100) + end + # this might not be needed in rails 4 ? def append(active_relation) @query = active_relation if active_relation @@ -73,6 +78,7 @@ class AdminUserIndexQuery append filter_by_ip append filter_exclude append filter_by_search + append limit @query end @@ -85,6 +91,5 @@ class AdminUserIndexQuery .includes(:google_user_info) .includes(:oauth2_user_info) .includes(:user_open_ids) - .take(100) end end From d3d517108d73a92d808f6107d4f2ab936931fedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 19:34:04 +0100 Subject: [PATCH 059/991] FIX: display total number of other accounts with the same IP address in the IP lookup dialog --- .../admin/components/ip-lookup.js.es6 | 25 ++- .../templates/components/ip-lookup.hbs | 4 +- app/controllers/admin/users_controller.rb | 12 +- config/locales/client.en.yml | 2 +- config/routes.rb | 1 + lib/admin_user_index_query.rb | 151 +++++++++--------- 6 files changed, 112 insertions(+), 83 deletions(-) diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index ee6cc03738..bf46c9a2da 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -9,6 +9,13 @@ export default Ember.Component.extend({ ].filter(Boolean).join(", "); }.property("location.{city,region,country}"), + otherAccountsToDelete: function() { + // can only delete up to 50 accounts at a time + var total = Math.min(50, this.get("totalOthersWithSameIP") || 0); + var visible = Math.min(50, this.get("other_accounts.length") || 0); + return Math.max(visible, total); + }.property("other_accounts", "totalOthersWithSameIP"), + actions: { lookup: function () { var self = this; @@ -24,11 +31,18 @@ export default Ember.Component.extend({ if (!this.get("other_accounts")) { this.set("otherAccountsLoading", true); - Discourse.AdminUser.findAll("active", { + + var data = { "ip": this.get("ip"), "exclude": this.get("userId"), "order": "trust_level DESC" - }).then(function (users) { + }; + + Discourse.ajax("/admin/users/total-others-with-same-ip.json", { data: data }).then(function (result) { + self.set("totalOthersWithSameIP", result.total); + }); + + Discourse.AdminUser.findAll("active", data).then(function (users) { self.setProperties({ other_accounts: users, otherAccountsLoading: false, @@ -45,7 +59,12 @@ export default Ember.Component.extend({ var self = this; bootbox.confirm(I18n.t("ip_lookup.confirm_delete_other_accounts"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { if (confirmed) { - self.setProperties({ other_accounts: null, otherAccountsLoading: true }); + self.setProperties({ + other_accounts: null, + otherAccountsLoading: true, + totalOthersWithSameIP: null + }); + Discourse.ajax("/admin/users/delete-others-with-same-ip.json", { type: "DELETE", data: { diff --git a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs index f7c7851bfe..396235b569 100644 --- a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs @@ -39,10 +39,10 @@
    {{i18n ip_lookup.other_accounts}} - {{other_accounts.length}} + {{totalOthersWithSameIP}} {{#if other_accounts.length}} {{/if}}
    diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 9158fa52de..6649d2223c 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -278,18 +278,24 @@ class Admin::UsersController < Admin::AdminController params.require(:exclude) params.require(:order) - params[:limit] = 50 - user_destroyer = UserDestroyer.new(current_user) options = { delete_posts: true, block_email: true, block_urls: true, block_ip: true, delete_as_spammer: true } - AdminUserIndexQuery.new(params).find_users.each do |user| + AdminUserIndexQuery.new(params).find_users(50).each do |user| user_destroyer.destroy(user, options) rescue nil end render json: success_json end + def total_other_accounts_with_same_ip + params.require(:ip) + params.require(:exclude) + params.require(:order) + + render json: { total: AdminUserIndexQuery.new(params).count_users } + end + def invite_admin email = params[:email] diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0d189d9386..4e4ef72b08 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -279,7 +279,7 @@ en: organisation: Organization phone: Phone other_accounts: "Other accounts with this IP address:" - delete_other_accounts: "Delete up to 50" + delete_other_accounts: "Delete %{count}" username: "username" trust_level: "TL" read_time: "read time" diff --git a/config/routes.rb b/config/routes.rb index 78f923d67b..d56d603376 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -53,6 +53,7 @@ Discourse::Application.routes.draw do get "list/:query" => "users#index" get "ip-info" => "users#ip_info" delete "delete-others-with-same-ip" => "users#delete_other_accounts_with_same_ip" + get "total-others-with-same-ip" => "users#total_other_accounts_with_same_ip" put "approve-bulk" => "users#approve_bulk" delete "reject-bulk" => "users#reject_bulk" end diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index a5d3d66ff7..f01094704f 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -1,6 +1,7 @@ require_dependency 'trust_level' class AdminUserIndexQuery + def initialize(params = {}, klass = User, trust_levels = TrustLevel.levels) @params = params @query = initialize_query_with_order(klass) @@ -9,80 +10,7 @@ class AdminUserIndexQuery attr_reader :params, :trust_levels - def initialize_query_with_order(klass) - order = [params[:order]] - - if params[:query] == "active" - order << "COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC" - else - order << "created_at DESC" - end - - order << "username" - - klass.order(order.reject(&:blank?).join(",")) - end - - def filter_by_trust - levels = trust_levels.map { |key, _| key.to_s } - if levels.include?(params[:query]) - @query.where('trust_level = ?', trust_levels[params[:query].to_sym]) - end - end - - def filter_by_query_classification - case params[:query] - when 'admins' then @query.where(admin: true) - when 'moderators' then @query.where(moderator: true) - when 'blocked' then @query.blocked - when 'suspended' then @query.suspended - when 'pending' then @query.not_suspended.where(approved: false) - end - end - - def filter_by_search - if params[:filter].present? - if ip = IPAddr.new(params[:filter]) rescue nil - @query.where('ip_address <<= :ip OR registration_ip_address <<= :ip', ip: ip.to_cidr_s) - else - @query.where('username_lower ILIKE :filter OR email ILIKE :filter', filter: "%#{params[:filter]}%") - end - end - end - - def filter_by_ip - if params[:ip].present? - @query.where('ip_address = :ip OR registration_ip_address = :ip', ip: params[:ip]) - end - end - - def filter_exclude - if params[:exclude].present? - @query.where('id != ?', params[:exclude]) - end - end - - def limit - limit = params[:limit].to_i - @query.limit(limit > 0 ? limit : 100) - end - - # this might not be needed in rails 4 ? - def append(active_relation) - @query = active_relation if active_relation - end - - def find_users_query - append filter_by_trust - append filter_by_query_classification - append filter_by_ip - append filter_exclude - append filter_by_search - append limit - @query - end - - def find_users + def find_users(limit=100) find_users_query.includes(:user_stat) .includes(:single_sign_on_record) .includes(:facebook_user_info) @@ -91,5 +19,80 @@ class AdminUserIndexQuery .includes(:google_user_info) .includes(:oauth2_user_info) .includes(:user_open_ids) + .limit(limit) end + + def count_users + find_users_query.count + end + + private + + def initialize_query_with_order(klass) + order = [params[:order]] + + if params[:query] == "active" + order << "COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC" + else + order << "created_at DESC" + end + + order << "username" + + klass.order(order.reject(&:blank?).join(",")) + end + + def filter_by_trust + levels = trust_levels.map { |key, _| key.to_s } + if levels.include?(params[:query]) + @query.where('trust_level = ?', trust_levels[params[:query].to_sym]) + end + end + + def filter_by_query_classification + case params[:query] + when 'admins' then @query.where(admin: true) + when 'moderators' then @query.where(moderator: true) + when 'blocked' then @query.blocked + when 'suspended' then @query.suspended + when 'pending' then @query.not_suspended.where(approved: false) + end + end + + def filter_by_search + if params[:filter].present? + if ip = IPAddr.new(params[:filter]) rescue nil + @query.where('ip_address <<= :ip OR registration_ip_address <<= :ip', ip: ip.to_cidr_s) + else + @query.where('username_lower ILIKE :filter OR email ILIKE :filter', filter: "%#{params[:filter]}%") + end + end + end + + def filter_by_ip + if params[:ip].present? + @query.where('ip_address = :ip OR registration_ip_address = :ip', ip: params[:ip]) + end + end + + def filter_exclude + if params[:exclude].present? + @query.where('id != ?', params[:exclude]) + end + end + + # this might not be needed in rails 4 ? + def append(active_relation) + @query = active_relation if active_relation + end + + def find_users_query + append filter_by_trust + append filter_by_query_classification + append filter_by_ip + append filter_exclude + append filter_by_search + @query + end + end From 2c38f969ccd57de364bc9401cbe964ed2bdc8b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 19:38:47 +0100 Subject: [PATCH 060/991] UX: add confirm dialog when rolling up IP ban entries --- .../admin-logs-screened-ip-addresses.js.es6 | 10 +++++++--- config/locales/client.en.yml | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 index f6adb5b941..03f2d4bdb3 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 @@ -18,9 +18,13 @@ export default Ember.ArrayController.extend(Discourse.Presence, { rollUp: function() { var self = this; - this.set("loading", true) - return Discourse.ScreenedIpAddress.rollUp().then(function() { - self.send("show"); + return bootbox.confirm(I18n.t("admin.logs.screened_ips.roll_up_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { + if (confirmed) { + self.set("loading", true) + return Discourse.ScreenedIpAddress.rollUp().then(function() { + self.send("show"); + }); + } }); } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 4e4ef72b08..e3f108426b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1862,6 +1862,7 @@ en: title: "Screened IPs" description: 'IP addresses that are being watched. Use "Allow" to whitelist IP addresses.' delete_confirm: "Are you sure you want to remove the rule for %{ip_address}?" + roll_up_confirm: "Are you sure you want to roll up ban entries?" actions: block: "Block" do_nothing: "Allow" From a609e1b655a863a2c32245daadca980259b2150a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 19:48:08 +0100 Subject: [PATCH 061/991] fix the build :fired: --- lib/admin_user_index_query.rb | 112 +++++++++++++++++----------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index f01094704f..cfcdd4ca4f 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -26,73 +26,71 @@ class AdminUserIndexQuery find_users_query.count end - private + def initialize_query_with_order(klass) + order = [params[:order]] - def initialize_query_with_order(klass) - order = [params[:order]] + if params[:query] == "active" + order << "COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC" + else + order << "created_at DESC" + end - if params[:query] == "active" - order << "COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC" + order << "username" + + klass.order(order.reject(&:blank?).join(",")) + end + + def filter_by_trust + levels = trust_levels.map { |key, _| key.to_s } + if levels.include?(params[:query]) + @query.where('trust_level = ?', trust_levels[params[:query].to_sym]) + end + end + + def filter_by_query_classification + case params[:query] + when 'admins' then @query.where(admin: true) + when 'moderators' then @query.where(moderator: true) + when 'blocked' then @query.blocked + when 'suspended' then @query.suspended + when 'pending' then @query.not_suspended.where(approved: false) + end + end + + def filter_by_search + if params[:filter].present? + if ip = IPAddr.new(params[:filter]) rescue nil + @query.where('ip_address <<= :ip OR registration_ip_address <<= :ip', ip: ip.to_cidr_s) else - order << "created_at DESC" - end - - order << "username" - - klass.order(order.reject(&:blank?).join(",")) - end - - def filter_by_trust - levels = trust_levels.map { |key, _| key.to_s } - if levels.include?(params[:query]) - @query.where('trust_level = ?', trust_levels[params[:query].to_sym]) + @query.where('username_lower ILIKE :filter OR email ILIKE :filter', filter: "%#{params[:filter]}%") end end + end - def filter_by_query_classification - case params[:query] - when 'admins' then @query.where(admin: true) - when 'moderators' then @query.where(moderator: true) - when 'blocked' then @query.blocked - when 'suspended' then @query.suspended - when 'pending' then @query.not_suspended.where(approved: false) - end + def filter_by_ip + if params[:ip].present? + @query.where('ip_address = :ip OR registration_ip_address = :ip', ip: params[:ip]) end + end - def filter_by_search - if params[:filter].present? - if ip = IPAddr.new(params[:filter]) rescue nil - @query.where('ip_address <<= :ip OR registration_ip_address <<= :ip', ip: ip.to_cidr_s) - else - @query.where('username_lower ILIKE :filter OR email ILIKE :filter', filter: "%#{params[:filter]}%") - end - end + def filter_exclude + if params[:exclude].present? + @query.where('id != ?', params[:exclude]) end + end - def filter_by_ip - if params[:ip].present? - @query.where('ip_address = :ip OR registration_ip_address = :ip', ip: params[:ip]) - end - end + # this might not be needed in rails 4 ? + def append(active_relation) + @query = active_relation if active_relation + end - def filter_exclude - if params[:exclude].present? - @query.where('id != ?', params[:exclude]) - end - end - - # this might not be needed in rails 4 ? - def append(active_relation) - @query = active_relation if active_relation - end - - def find_users_query - append filter_by_trust - append filter_by_query_classification - append filter_by_ip - append filter_exclude - append filter_by_search - @query - end + def find_users_query + append filter_by_trust + append filter_by_query_classification + append filter_by_ip + append filter_exclude + append filter_by_search + @query + end end From 7b0ae702e7811c8b41eb22129efcfaad52db9a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Nov 2014 19:48:54 +0100 Subject: [PATCH 062/991] FEATURE: log a new staff action when rolling up banned IP addresses --- .../admin/screened_ip_addresses_controller.rb | 9 ++++++--- app/models/user_history.rb | 6 ++++-- app/services/staff_action_logger.rb | 16 ++++++++++++---- .../screened_ip_addresses_controller_spec.rb | 5 +++-- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/app/controllers/admin/screened_ip_addresses_controller.rb b/app/controllers/admin/screened_ip_addresses_controller.rb index e9c25aa402..ea77a8b984 100644 --- a/app/controllers/admin/screened_ip_addresses_controller.rb +++ b/app/controllers/admin/screened_ip_addresses_controller.rb @@ -45,11 +45,14 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController action_type: ScreenedIpAddress.actions[:block], min_count: SiteSetting.min_ban_entries_for_roll_up).values.flatten + # 2 - log the call + StaffActionLogger.new(current_user).log_roll_up(subnets) unless subnets.blank? + subnets.each do |subnet| - # 2 - create subnet if not already exists + # 3 - create subnet if not already exists ScreenedIpAddress.new(ip_address: subnet).save unless ScreenedIpAddress.where(ip_address: subnet).first - # 3 - update stats + # 4 - update stats sql = <<-SQL UPDATE screened_ip_addresses SET match_count = sum_match_count, @@ -70,7 +73,7 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController ScreenedIpAddress.exec_sql(sql, action_type: ScreenedIpAddress.actions[:block], ip_address: subnet) - # 4 - remove old matches + # 5 - remove old matches ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block]) .where("family(ip_address) = 4") .where("masklen(ip_address) = 32") diff --git a/app/models/user_history.rb b/app/models/user_history.rb index 954a66ac06..90aae0c0bd 100644 --- a/app/models/user_history.rb +++ b/app/models/user_history.rb @@ -33,7 +33,8 @@ class UserHistory < ActiveRecord::Base :check_email, :delete_post, :delete_topic, - :impersonate) + :impersonate, + :roll_up) end # Staff actions is a subset of all actions, used to audit actions taken by staff users. @@ -50,7 +51,8 @@ class UserHistory < ActiveRecord::Base :check_email, :delete_post, :delete_topic, - :impersonate] + :impersonate, + :roll_up] end def self.staff_action_ids diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb index dfd6e281c8..7e0ca21dae 100644 --- a/app/services/staff_action_logger.rb +++ b/app/services/staff_action_logger.rb @@ -1,5 +1,6 @@ # Responsible for logging the actions of admins and moderators. class StaffActionLogger + def initialize(admin) @admin = admin raise Discourse::InvalidParameters.new('admin is nil') unless @admin && @admin.is_a?(User) @@ -164,10 +165,17 @@ class StaffActionLogger })) end - private - - def params(opts) - { acting_user_id: @admin.id, context: opts[:context] } + def log_roll_up(subnets) + UserHistory.create(params(opts).merge({ + action: UserHistory.action[:roll_up], + details: subnets.join(", ") + })) end + private + + def params(opts) + { acting_user_id: @admin.id, context: opts[:context] } + end + end diff --git a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb index ae56646f49..ddd1d1212c 100644 --- a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb +++ b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb @@ -21,8 +21,6 @@ describe Admin::ScreenedIpAddressesController do describe 'roll_up' do it "works" do - SiteSetting.expects(:min_ban_entries_for_roll_up).returns(3) - Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1) Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1) Fabricate(:screened_ip_address, ip_address: "1.2.3.6", match_count: 1) @@ -30,6 +28,9 @@ describe Admin::ScreenedIpAddressesController do Fabricate(:screened_ip_address, ip_address: "42.42.42.4", match_count: 1) Fabricate(:screened_ip_address, ip_address: "42.42.42.5", match_count: 1) + StaffActionLogger.any_instance.expects(:log_roll_up) + SiteSetting.expects(:min_ban_entries_for_roll_up).returns(3) + xhr :post, :roll_up response.should be_success From 55e2126b1eb1eeb3605beedf977bf1655c4ea619 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 24 Nov 2014 23:10:33 +0530 Subject: [PATCH 063/991] FEATURE: add topic status namespace in RSS feed --- app/controllers/list_controller.rb | 2 +- app/views/list/list.rss.erb | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index 8cb13c9d68..717bd0a8aa 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -134,7 +134,7 @@ class ListController < ApplicationController render 'list', formats: [:rss] end - + def top(options=nil) options ||= {} period = ListController.best_period_for(current_user.try(:previous_visit_at), options[:category]) diff --git a/app/views/list/list.rss.erb b/app/views/list/list.rss.erb index 6589239be3..afeed83e0a 100644 --- a/app/views/list/list.rss.erb +++ b/app/views/list/list.rss.erb @@ -1,5 +1,5 @@ - + <% lang = SiteSetting.find_by_name('default_locale').try(:value) %> <% site_email = SiteSetting.find_by_name('contact_email').try(:value) %> @@ -27,6 +27,9 @@ ]]> <%= topic_url %> <%= topic.created_at.rfc2822 %> + <%= topic.pinned_at ? 'Yes' : 'No' %> + <%= topic.closed ? 'Yes' : 'No' %> + <%= topic.archived ? 'Yes' : 'No' %> topic-<%= topic.id %> <%= topic.title %> From adb570fe538038d7631c55a868cbf250abe27b2d Mon Sep 17 00:00:00 2001 From: "Jason W. May" Date: Mon, 24 Nov 2014 12:12:48 -0800 Subject: [PATCH 064/991] use limit & offset for pagination of group members --- app/controllers/groups_controller.rb | 7 +++++-- spec/controllers/groups_controller_spec.rb | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index dd1feec96b..a687cded1f 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -18,8 +18,11 @@ class GroupsController < ApplicationController def members group = find_group(:group_id) - members = group.users.order('username_lower asc') - members = members.limit(200) if group.automatic + + limit = (params[:limit] || 200).to_i + offset = (params[:offset] || 0).to_i + members = group.users.order('username_lower asc').limit(limit).offset(offset) + render_serialized(members.to_a, GroupUserSerializer) end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index bbaa136d20..04881cbc86 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -67,5 +67,19 @@ describe GroupsController do xhr :get, :posts, group_id: group.name response.should be_success end + + it "ensures that membership can be paginated" do + 5.times { group.add(Fabricate(:user)) } + xhr :get, :members, group_id: group.name, limit: 3 + response.should be_success + members = JSON.parse(response.body) + members.count.should eq(3) + + xhr :get, :members, group_id: group.name, limit: 250, offset: 250 + xhr :get, :members, group_id: group.name, limit: 3, offset: 3 + response.should be_success + members = JSON.parse(response.body) + members.count.should eq(2) + end end end From 728e8a262c3bf32bde5e9ab296174ae1a8a429f3 Mon Sep 17 00:00:00 2001 From: riking Date: Mon, 24 Nov 2014 10:04:06 -0800 Subject: [PATCH 065/991] FIX: Admin panel referral stats not counting topics correctly Due to what seems to be a bug in ActiveRecord, the distinct: true option is not recognized on counts with string column names. This commit fixes that by moving the DISTINCT into the count string. For robustness, the integration spec for IncomingLinksReport was rewritten to be an actual integration spec, running the actual interface on actual fake data. --- app/models/incoming_links_report.rb | 6 +- spec/fabricators/incoming_link_fabricator.rb | 5 + spec/models/incoming_links_report_spec.rb | 97 +++++++++++++++----- 3 files changed, 79 insertions(+), 29 deletions(-) create mode 100644 spec/fabricators/incoming_link_fabricator.rb diff --git a/app/models/incoming_links_report.rb b/app/models/incoming_links_report.rb index 6786eb015c..2d1563f686 100644 --- a/app/models/incoming_links_report.rb +++ b/app/models/incoming_links_report.rb @@ -46,7 +46,6 @@ class IncomingLinksReport @per_user_query ||= IncomingLink .where('incoming_links.created_at > ? AND incoming_links.user_id IS NOT NULL', 30.days.ago) .joins(:user) - .joins(:post) .group('users.username') end @@ -55,7 +54,7 @@ class IncomingLinksReport end def self.topic_count_per_user - per_user.count('topic_id', distinct: true) + per_user.joins(:post).count("DISTINCT posts.topic_id") end @@ -85,14 +84,13 @@ class IncomingLinksReport def self.per_domain(domains) IncomingLink .joins(:incoming_referer => :incoming_domain) - .joins(:post) .where('incoming_links.created_at > ? AND incoming_domains.name IN (?)', 30.days.ago, domains) .group('incoming_domains.name') end def self.topic_count_per_domain(domains) # COUNT(DISTINCT) is slow - per_domain(domains).count('topic_id', distinct: true) + per_domain(domains).joins(:post).count("DISTINCT posts.topic_id") end diff --git a/spec/fabricators/incoming_link_fabricator.rb b/spec/fabricators/incoming_link_fabricator.rb new file mode 100644 index 0000000000..82e5245ddb --- /dev/null +++ b/spec/fabricators/incoming_link_fabricator.rb @@ -0,0 +1,5 @@ +Fabricator(:incoming_link) do + user + post + ip_address { sequence(:ip_address) { |n| "123.#{(n*3)%255}.#{(n*2)%255}.#{n%255}" } } +end diff --git a/spec/models/incoming_links_report_spec.rb b/spec/models/incoming_links_report_spec.rb index 780d0fa3d9..a27001eb7f 100644 --- a/spec/models/incoming_links_report_spec.rb +++ b/spec/models/incoming_links_report_spec.rb @@ -5,40 +5,76 @@ describe IncomingLinksReport do describe 'integration' do it 'runs correctly' do p1 = create_post + p2 = create_post - IncomingLink.add( - referer: 'http://test.com', - host: 'http://boo.com', - topic_id: p1.topic.id, - ip_address: '10.0.0.2', - username: p1.user.username - ) + p1.topic.save + p2.topic.save + 7.times do |n| + IncomingLink.add( + referer: 'http://test.com', + host: 'http://discourse.example.com', + topic_id: p1.topic.id, + ip_address: "10.0.0.#{n}", + username: p1.user.username + ) + end + 3.times do |n| + IncomingLink.add( + referer: 'http://foo.com', + host: 'http://discourse.example.com', + topic_id: p2.topic.id, + ip_address: "10.0.0.#{n + 7}", + username: p2.user.username + ) + end + 2.times do |n| + IncomingLink.add( + referer: 'http://foo.com', + host: 'http://discourse.example.com', + topic_id: p2.topic.id, + ip_address: "10.0.0.#{n + 7 + 3}", + username: p1.user.username # ! user1 is the referer ! + ) + end - c = IncomingLinksReport.link_count_per_topic - c[p1.topic_id].should == 1 + r = IncomingLinksReport.find('top_referrers').as_json + r[:data].should == [ + {username: p1.user.username, num_clicks: 7 + 2, num_topics: 2}, + {username: p2.user.username, num_clicks: 3, num_topics: 1} + ] - c = IncomingLinksReport.link_count_per_domain - c["test.com"].should == 1 + r = IncomingLinksReport.find('top_traffic_sources').as_json + r[:data].should == [ + {domain: 'test.com', num_clicks: 7, num_topics: 1}, + {domain: 'foo.com', num_clicks: 3 + 2, num_topics: 1} + ] - c = IncomingLinksReport.topic_count_per_domain(['test.com', 'foo.com']) - c["test.com"].should == 1 - - c = IncomingLinksReport.topic_count_per_user() - c[p1.username].should == 1 + r = IncomingLinksReport.find('top_referred_topics').as_json + r[:data].should == [ + {topic_id: p1.topic.id, topic_title: p1.topic.title, topic_slug: p1.topic.slug, num_clicks: 7}, + {topic_id: p2.topic.id, topic_title: p2.topic.title, topic_slug: p2.topic.slug, num_clicks: 2 + 3}, + ] end end describe 'top_referrers' do subject(:top_referrers) { IncomingLinksReport.find('top_referrers').as_json } - def stub_empty_referrers_data - IncomingLinksReport.stubs(:link_count_per_user).returns({}) - IncomingLinksReport.stubs(:topic_count_per_user).returns({}) + let(:amy) { Fabricate(:user, username: 'amy') } + let(:bob) { Fabricate(:user, username: 'bob') } + let(:post1) { Fabricate(:post) } + let(:post2) { Fabricate(:post) } + let(:topic1) { post1.topic } + let(:topic2) { post2.topic } + + def save_base_objects + amy.save; bob.save + post1.save; post2.save + topic1.save; topic2.save end it 'returns localized titles' do - stub_empty_referrers_data top_referrers[:title].should be_present top_referrers[:xaxis].should be_present top_referrers[:ytitles].should be_present @@ -47,21 +83,30 @@ describe IncomingLinksReport do end it 'with no IncomingLink records, it returns correct data' do - stub_empty_referrers_data + IncomingLink.delete_all top_referrers[:data].size.should == 0 end it 'with some IncomingLink records, it returns correct data' do - IncomingLinksReport.stubs(:link_count_per_user).returns({'luke' => 4, 'chewie' => 2}) - IncomingLinksReport.stubs(:topic_count_per_user).returns({'luke' => 2, 'chewie' => 1}) - top_referrers[:data][0].should == {username: 'luke', num_clicks: 4, num_topics: 2} - top_referrers[:data][1].should == {username: 'chewie', num_clicks: 2, num_topics: 1} + save_base_objects + + 2.times do + Fabricate(:incoming_link, user: amy, post: post1).save + end + Fabricate(:incoming_link, user: amy, post: post2).save + 2.times do + Fabricate(:incoming_link, user: bob, post: post1).save + end + + top_referrers[:data][0].should == {username: 'amy', num_clicks: 3, num_topics: 2} + top_referrers[:data][1].should == {username: 'bob', num_clicks: 2, num_topics: 1} end end describe 'top_traffic_sources' do subject(:top_traffic_sources) { IncomingLinksReport.find('top_traffic_sources').as_json } + # TODO: STOP THE STUBBING def stub_empty_traffic_source_data IncomingLinksReport.stubs(:link_count_per_domain).returns({}) IncomingLinksReport.stubs(:topic_count_per_domain).returns({}) @@ -94,6 +139,7 @@ describe IncomingLinksReport do describe 'top_referred_topics' do subject(:top_referred_topics) { IncomingLinksReport.find('top_referred_topics').as_json } + # TODO: STOP THE STUBBING def stub_empty_referred_topics_data IncomingLinksReport.stubs(:link_count_per_topic).returns({}) end @@ -113,6 +159,7 @@ describe IncomingLinksReport do it 'with some IncomingLink records, it returns correct data' do topic1 = Fabricate.build(:topic, id: 123); topic2 = Fabricate.build(:topic, id: 234) + # TODO: OMG OMG THE STUBBING IncomingLinksReport.stubs(:link_count_per_topic).returns({topic1.id => 8, topic2.id => 3}) Topic.stubs(:select).returns(Topic); Topic.stubs(:where).returns(Topic) # bypass some activerecord methods Topic.stubs(:all).returns([topic1, topic2]) From f355d8226574c0d05eaae23d922f051fb0c321ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 25 Nov 2014 16:53:26 +0100 Subject: [PATCH 066/991] FIX: changing category shows error for TL3 --- lib/post_revisor.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index dc2efb994b..f0d73d93f2 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -42,8 +42,13 @@ class PostRevisor @post_successfully_saved = true @topic_successfully_saved = true - @validate_post = @opts[:validate_post] || !@opts[:skip_validations] - @validate_topic = @opts[:validate_topic] || !@opts[:skip_validations] + @validate_post = true + @validate_post = @opts[:validate_post] if @opts.has_key?(:validate_post) + @validate_post = !@opts[:skip_validations] if @opts.has_key?(:skip_validations) + + @validate_topic = true + @validate_topic = @opts[:validate_topic] if @opts.has_key?(:validate_topic) + @validate_topic = !@opts[:validate_topic] if @opts.has_key?(:skip_validations) Post.transaction do revise_post From e23a25994dde607ffaaa30de56be2062e2f54d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 25 Nov 2014 17:12:49 +0100 Subject: [PATCH 067/991] FEATURE: clean up unmatched email/ip entries after a year --- app/jobs/scheduled/clean_up_unmatched_emails.rb | 16 ++++++++++++++++ app/jobs/scheduled/clean_up_unmatched_ips.rb | 16 ++++++++++++++++ config/locales/server.en.yml | 3 +++ config/site_settings.yml | 2 ++ 4 files changed, 37 insertions(+) create mode 100644 app/jobs/scheduled/clean_up_unmatched_emails.rb create mode 100644 app/jobs/scheduled/clean_up_unmatched_ips.rb diff --git a/app/jobs/scheduled/clean_up_unmatched_emails.rb b/app/jobs/scheduled/clean_up_unmatched_emails.rb new file mode 100644 index 0000000000..47cb0d4bab --- /dev/null +++ b/app/jobs/scheduled/clean_up_unmatched_emails.rb @@ -0,0 +1,16 @@ +module Jobs + + class CleanUpUnmatchedEmails < Jobs::Scheduled + every 1.day + + def execute + last_match_threshold = SiteSetting.max_age_unmatched_emails.days.ago + + ScreenedEmail.where(action_type: ScreenedEmail.actions[:block]) + .where("last_match_at < ?", last_match_threshold) + .destroy_all + end + + end + +end diff --git a/app/jobs/scheduled/clean_up_unmatched_ips.rb b/app/jobs/scheduled/clean_up_unmatched_ips.rb new file mode 100644 index 0000000000..08de1547cd --- /dev/null +++ b/app/jobs/scheduled/clean_up_unmatched_ips.rb @@ -0,0 +1,16 @@ +module Jobs + + class CleanUpUnmatchedIPs < Jobs::Scheduled + every 1.day + + def execute + last_match_threshold = SiteSetting.max_age_unmatched_ips.days.ago + + ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block]) + .where("last_match_at < ?", last_match_threshold) + .destroy_all + end + + end + +end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 1f5b0ef100..e62e3f4d3e 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -944,6 +944,9 @@ en: max_new_accounts_per_registration_ip: "If there are already (n) trust level 0 accounts from this IP (and none is a staff member or at TL2 or higher), stop accepting new signups from that IP." min_ban_entries_for_roll_up: "When clicking the Roll up button, will create a new subnet ban entry if there are at least (N) entries." + max_age_unmatched_emails: "Delete unmatched screened email entries after (N) days." + max_age_unmatched_ips: "Delete unmatched screened IP entries after (N) days." + reply_by_email_enabled: "Enable replying to topics via email." reply_by_email_address: "Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" diff --git a/config/site_settings.yml b/config/site_settings.yml index 6bd32e1d08..b4a1062553 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -578,6 +578,8 @@ spam: max: 3 max_new_accounts_per_registration_ip: 3 min_ban_entries_for_roll_up: 5 + max_age_unmatched_emails: 365 + max_age_unmatched_ips: 365 rate_limits: unique_posts_mins: From c5229a02681891c064bc3b505b4e4a34b4248620 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 25 Nov 2014 22:25:09 +0530 Subject: [PATCH 068/991] upload bulk invite csv file to /public/uploads/csv --- app/models/invite.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/invite.rb b/app/models/invite.rb index 0c526ae076..28fb95526d 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -185,7 +185,7 @@ class Invite < ActiveRecord::Base end def self.base_directory - File.join(Rails.root, "public", "csv", RailsMultisite::ConnectionManagement.current_db) + File.join(Rails.root, "public", "uploads", "csv", RailsMultisite::ConnectionManagement.current_db) end def self.chunk_path(identifier, filename, chunk_number) From 610c2a4d652ef61b6a405089ee6395b02ba45f0f Mon Sep 17 00:00:00 2001 From: "Jason W. May" Date: Tue, 25 Nov 2014 09:12:24 -0800 Subject: [PATCH 069/991] checking actual values in the spec, not just counts --- spec/controllers/groups_controller_spec.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 04881cbc86..63ac27d4b3 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -70,16 +70,17 @@ describe GroupsController do it "ensures that membership can be paginated" do 5.times { group.add(Fabricate(:user)) } + usernames = group.users.map{ |m| m['username'] }.sort + xhr :get, :members, group_id: group.name, limit: 3 response.should be_success members = JSON.parse(response.body) - members.count.should eq(3) + members.map{ |m| m['username'] }.should eq(usernames[0..2]) - xhr :get, :members, group_id: group.name, limit: 250, offset: 250 xhr :get, :members, group_id: group.name, limit: 3, offset: 3 response.should be_success members = JSON.parse(response.body) - members.count.should eq(2) + members.map{ |m| m['username'] }.should eq(usernames[3..4]) end end end From 515b616d30dfd974d18611a01f843f284f2acb51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 25 Nov 2014 18:45:45 +0100 Subject: [PATCH 070/991] ooops. forgot the args :fired: --- app/jobs/scheduled/clean_up_unmatched_emails.rb | 2 +- app/jobs/scheduled/clean_up_unmatched_ips.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/jobs/scheduled/clean_up_unmatched_emails.rb b/app/jobs/scheduled/clean_up_unmatched_emails.rb index 47cb0d4bab..6ce62f3f23 100644 --- a/app/jobs/scheduled/clean_up_unmatched_emails.rb +++ b/app/jobs/scheduled/clean_up_unmatched_emails.rb @@ -3,7 +3,7 @@ module Jobs class CleanUpUnmatchedEmails < Jobs::Scheduled every 1.day - def execute + def execute(args) last_match_threshold = SiteSetting.max_age_unmatched_emails.days.ago ScreenedEmail.where(action_type: ScreenedEmail.actions[:block]) diff --git a/app/jobs/scheduled/clean_up_unmatched_ips.rb b/app/jobs/scheduled/clean_up_unmatched_ips.rb index 08de1547cd..8759274af5 100644 --- a/app/jobs/scheduled/clean_up_unmatched_ips.rb +++ b/app/jobs/scheduled/clean_up_unmatched_ips.rb @@ -3,7 +3,7 @@ module Jobs class CleanUpUnmatchedIPs < Jobs::Scheduled every 1.day - def execute + def execute(args) last_match_threshold = SiteSetting.max_age_unmatched_ips.days.ago ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block]) From 183c93904dce9b324aa35e0204290060cc177a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 25 Nov 2014 19:09:17 +0100 Subject: [PATCH 071/991] FIX: don't show the banner topic on error page --- .../javascripts/discourse/components/discourse-banner.js.es6 | 4 ++-- app/assets/javascripts/discourse/templates/topic.hbs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/components/discourse-banner.js.es6 b/app/assets/javascripts/discourse/components/discourse-banner.js.es6 index 421510f072..a3f8f07d67 100644 --- a/app/assets/javascripts/discourse/components/discourse-banner.js.es6 +++ b/app/assets/javascripts/discourse/components/discourse-banner.js.es6 @@ -10,8 +10,8 @@ export default VisibleComponent.extend({ if (bannerKey) { bannerKey = parseInt(bannerKey, 10); } if (dismissedBannerKey) { dismissedBannerKey = parseInt(dismissedBannerKey, 10); } - return bannerKey && dismissedBannerKey !== bannerKey; - }.property("user.dismissed_banner_key", "banner.key"), + return !this.get("hide") && bannerKey && dismissedBannerKey !== bannerKey; + }.property("user.dismissed_banner_key", "banner.key", "hide"), actions: { dismiss: function () { diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index e203fdaf48..37feb1da48 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -1,7 +1,7 @@
    {{custom-html "top"}} {{global-notice}} - {{discourse-banner user=currentUser banner=site.banner overlay=view.hasScrolled}} + {{discourse-banner user=currentUser banner=site.banner overlay=view.hasScrolled hide=errorLoading}}
    {{#if postStream.loaded}} From 4c9f55d1e157ba65ce757b1d91c9dd7f251c215b Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 25 Nov 2014 16:14:21 -0500 Subject: [PATCH 072/991] FIX: Suspended users should have links stripped from their profiles. --- app/models/user_profile.rb | 4 ++-- spec/models/user_profile_spec.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/models/user_profile.rb b/app/models/user_profile.rb index 3059d2a289..06be18d41c 100644 --- a/app/models/user_profile.rb +++ b/app/models/user_profile.rb @@ -12,12 +12,12 @@ class UserProfile < ActiveRecord::Base def bio_excerpt excerpt = PrettyText.excerpt(bio_cooked, 350) - return excerpt if excerpt.blank? || user.has_trust_level?(TrustLevel[1]) + return excerpt if excerpt.blank? || (user.has_trust_level?(TrustLevel[1]) && !user.suspended?) PrettyText.strip_links(excerpt) end def bio_processed - return bio_cooked if bio_cooked.blank? || user.has_trust_level?(TrustLevel[1]) + return bio_cooked if bio_cooked.blank? || (user.has_trust_level?(TrustLevel[1]) && !user.suspended?) PrettyText.strip_links(bio_cooked) end diff --git a/spec/models/user_profile_spec.rb b/spec/models/user_profile_spec.rb index ad18eb8617..a30403fc23 100644 --- a/spec/models/user_profile_spec.rb +++ b/spec/models/user_profile_spec.rb @@ -103,6 +103,14 @@ describe UserProfile do expect(user_profile.bio_processed).to eq("

    I love http://discourse.org

    ") end + it 'removes the link if the user is suspended' do + user.suspended_till = 1.month.from_now + puts user.suspended?.inspect + user_profile.send(:cook) + expect(user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(user_profile.bio_processed).to eq("

    I love http://discourse.org

    ") + end + context 'tl3_links_no_follow is false' do before { SiteSetting.stubs(:tl3_links_no_follow).returns(false) } From d171d6db19d724578b0ed27ef70dc3b59c22fdee Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Nov 2014 09:43:17 +1100 Subject: [PATCH 073/991] FEATURE: export sso data if sso is enabled --- app/jobs/regular/export_csv_file.rb | 100 +++++++++++++++++----------- spec/jobs/export_csv_file_spec.rb | 23 ++++++- 2 files changed, 82 insertions(+), 41 deletions(-) diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index f3cbadec05..926aee562d 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -6,6 +6,7 @@ module Jobs class ExportCsvFile < Jobs::Base CSV_USER_ATTRS = ['id','name','username','email','title','created_at','trust_level','active','admin','moderator','ip_address'] CSV_USER_STATS = ['topics_entered','posts_read_count','time_read','topic_count','post_count','likes_given','likes_received'] + CSV_USER_SSO_ATTRS = ['external_id','external_email', 'external_username', 'external_name', 'external_avatar_url'] SCREENED_IP_ATTRS = ['ip_address','action_type','match_count','last_match_at','created_at'] sidekiq_options retry: false @@ -19,27 +20,15 @@ module Jobs entity = args[:entity] @current_user = User.find_by(id: args[:user_id]) - raise Discourse::InvalidParameters.new(:entity) if entity.blank? - case entity - when 'user' - query = ::AdminUserIndexQuery.new - user_data = query.find_users_query.to_a - data = [] - user_data.each do |user| - group_names = get_group_names(user).join(';') - user_array = get_user_fields(user) - user_array.push(group_names) if group_names != '' - data.push(user_array) - end - when 'screened_ips' - screened_ips_data = ScreenedIpAddress.order('id desc').to_a - data = [] - screened_ips_data.each do |screened_ip| - screened_ip_array = get_screened_ip_fields(screened_ip) - data.push(screened_ip_array) - end - end + data = + if entity == 'user' + user_export + elsif entity == 'screened_ips' + screened_ips_export + else + raise Discourse::InvalidParameters.new(:entity) if entity.blank? + end if data && data.length > 0 set_file_path @@ -50,6 +39,47 @@ module Jobs notify_user end + def user_export + query = ::AdminUserIndexQuery.new + user_data = query.find_users_query.to_a + user_data.map do |user| + group_names = get_group_names(user).join(';') + user_array = get_user_fields(user) + user_array.push(group_names) if group_names != '' + user_array + end + end + + + def screened_ips_export + screened_ips_data = ScreenedIpAddress.order('id desc').to_a + screened_ips_data.map do |screened_ip| + get_screened_ip_fields(screened_ip) + end + end + + def get_header(entity) + + case entity + when 'user' + header_array = CSV_USER_ATTRS + CSV_USER_STATS + if SiteSetting.enable_sso + header_array.concat(CSV_USER_SSO_ATTRS) + end + user_custom_fields = UserField.all + if user_custom_fields.present? + user_custom_fields.each do |custom_field| + header_array.push("#{custom_field.name} (custom user field)") + end + end + header_array.push("group_names") + when 'screened_ips' + header_array = SCREENED_IP_ATTRS + end + + header_array + end + private def get_group_names(user) @@ -72,13 +102,21 @@ module Jobs user_array.push(user.user_stat.attributes[stat]) end + if SiteSetting.enable_sso + sso = user.single_sign_on_record + CSV_USER_SSO_ATTRS.each do |stat| + field = sso.attributes[stat] if sso + user_array.push(field) + end + end + if user.user_fields.present? user.user_fields.each do |custom_field| user_array.push(custom_field[1]) end end - return user_array + user_array end def get_screened_ip_fields(screened_ip) @@ -88,27 +126,9 @@ module Jobs screened_ip_array.push(screened_ip.attributes[attr]) end - return screened_ip_array + screened_ip_array end - def get_header(entity) - - case entity - when 'user' - header_array = CSV_USER_ATTRS + CSV_USER_STATS - user_custom_fields = UserField.all - if user_custom_fields.present? - user_custom_fields.each do |custom_field| - header_array.push("#{custom_field.name} (custom user field)") - end - end - header_array.push("group_names") - when 'screened_ips' - header_array = SCREENED_IP_ATTRS - end - - return header_array - end def set_file_path @file_name = "export_#{SecureRandom.hex(4)}.csv" diff --git a/spec/jobs/export_csv_file_spec.rb b/spec/jobs/export_csv_file_spec.rb index 269fb12b2a..8c41b43bc1 100644 --- a/spec/jobs/export_csv_file_spec.rb +++ b/spec/jobs/export_csv_file_spec.rb @@ -3,11 +3,32 @@ require 'spec_helper' describe Jobs::ExportCsvFile do context '.execute' do - it 'raises an error when the entity is missing' do lambda { Jobs::ExportCsvFile.new.execute(user_id: "1") }.should raise_error(Discourse::InvalidParameters) end + end + let :user_header do + Jobs::ExportCsvFile.new.get_header('user') + end + + let :user_export do + Jobs::ExportCsvFile.new.user_export + end + + def to_hash(row) + Hash[*user_header.zip(row).flatten] + end + + it 'exports sso data' do + SiteSetting.enable_sso = true + user = Fabricate(:user) + user.create_single_sign_on_record(external_id: "123", last_payload: "xxx", external_email: 'test@test.com') + + user = to_hash(user_export.find{|u| u[0] == user.id}) + + user["external_id"].should == "123" + user["external_email"].should == "test@test.com" end end From 220f2424b5b65a3281efd9da1e6c34f33164b37e Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 25 Nov 2014 18:13:38 -0500 Subject: [PATCH 074/991] FIX: Load order issue / header category badge when not surpressed --- app/assets/javascripts/discourse/controllers/header.js.es6 | 4 +--- .../discourse/routes/{topic-route.js.es6 => topic.js.es6} | 0 2 files changed, 1 insertion(+), 3 deletions(-) rename app/assets/javascripts/discourse/routes/{topic-route.js.es6 => topic.js.es6} (100%) diff --git a/app/assets/javascripts/discourse/controllers/header.js.es6 b/app/assets/javascripts/discourse/controllers/header.js.es6 index 2b64251f8b..2511aa5008 100644 --- a/app/assets/javascripts/discourse/controllers/header.js.es6 +++ b/app/assets/javascripts/discourse/controllers/header.js.es6 @@ -12,9 +12,7 @@ export default DiscourseController.extend({ hasCategory: function() { var cat = this.get('topic.category'); - return cat && - !cat.get('isUncategorizedCategory') || - !this.siteSettings.suppress_uncategorized_badge; + return cat && (!cat.get('isUncategorizedCategory') || !this.siteSettings.suppress_uncategorized_badge); }.property('topic.category'), showPrivateMessageGlyph: function() { diff --git a/app/assets/javascripts/discourse/routes/topic-route.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6 similarity index 100% rename from app/assets/javascripts/discourse/routes/topic-route.js.es6 rename to app/assets/javascripts/discourse/routes/topic.js.es6 From 81eedf3a1240cc0b6379f75724e9795d36a3d2e6 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 25 Nov 2014 18:21:21 -0500 Subject: [PATCH 075/991] No need to call it `Discourse.TopicRoute` -- that is done automatically by the compatibility layer. cc @ZogStrIP --- app/assets/javascripts/discourse/routes/topic.js.es6 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/routes/topic.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6 index ba13ca1372..0efd068371 100644 --- a/app/assets/javascripts/discourse/routes/topic.js.es6 +++ b/app/assets/javascripts/discourse/routes/topic.js.es6 @@ -5,7 +5,7 @@ var isTransitioning = false, import ShowFooter from "discourse/mixins/show-footer"; -Discourse.TopicRoute = Discourse.Route.extend(ShowFooter, { +var TopicRoute = Discourse.Route.extend(ShowFooter, { redirect: function() { return this.redirectIfLoginRequired(); }, queryParams: { @@ -235,5 +235,5 @@ Discourse.TopicRoute = Discourse.Route.extend(ShowFooter, { }); -RSVP.EventTarget.mixin(Discourse.TopicRoute); -export default Discourse.TopicRoute; +RSVP.EventTarget.mixin(TopicRoute); +export default TopicRoute; From 12e587c9b3e29d51b42fb1080c11f5488854dbbb Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Nov 2014 10:31:10 +1100 Subject: [PATCH 076/991] update rails master --- Gemfile_master.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Gemfile_master.lock b/Gemfile_master.lock index acb65ae644..c962d2baec 100644 --- a/Gemfile_master.lock +++ b/Gemfile_master.lock @@ -8,32 +8,32 @@ GIT GIT remote: https://github.com/rails/arel.git - revision: a04851702b0e8e694a92139c3ee9f3b1622f3f5d + revision: 1fefe71b1872c0a83f09231164863cd8dbb57174 specs: - arel (6.0.0.beta2) + arel (6.0.0) GIT remote: https://github.com/rails/rails.git - revision: 08576b94ad4f19dfc368619d7751e211d23dcad8 + revision: 2f8be7ebafcf7815f9f3ec7983789157525a60fa specs: actionmailer (4.2.0.beta4) actionpack (= 4.2.0.beta4) actionview (= 4.2.0.beta4) activejob (= 4.2.0.beta4) mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 1.0, >= 1.0.4) + rails-dom-testing (~> 1.0, >= 1.0.5) actionpack (4.2.0.beta4) actionview (= 4.2.0.beta4) activesupport (= 4.2.0.beta4) rack (~> 1.6.0.beta) rack-test (~> 0.6.2) - rails-dom-testing (~> 1.0, >= 1.0.4) + rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.1) actionview (4.2.0.beta4) activesupport (= 4.2.0.beta4) builder (~> 3.1) erubis (~> 2.7.0) - rails-dom-testing (~> 1.0, >= 1.0.4) + rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.1) activejob (4.2.0.beta4) activesupport (= 4.2.0.beta4) @@ -44,7 +44,7 @@ GIT activerecord (4.2.0.beta4) activemodel (= 4.2.0.beta4) activesupport (= 4.2.0.beta4) - arel (>= 6.0.0.beta2, < 6.1) + arel (~> 6.0) activesupport (4.2.0.beta4) i18n (>= 0.7.0.beta1, < 0.8) json (~> 1.7, >= 1.7.7) @@ -312,7 +312,7 @@ GEM rack (>= 1.0) rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.4) + rails-dom-testing (1.0.5) activesupport (>= 4.2.0.beta, < 5.0) nokogiri (~> 1.6.0) rails-deprecated_sanitizer (>= 1.0.1) @@ -321,7 +321,7 @@ GEM rails-observers (0.1.2) activemodel (~> 4.0) raindrops (0.13.0) - rake (10.3.2) + rake (10.4.0) rake-compiler (0.9.3) rake rb-fsevent (0.9.4) From c10e3df0126fa4be23100c49983eb4fbdb53153f Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Nov 2014 17:25:54 +1100 Subject: [PATCH 077/991] FEATURE: implement SSO provider on Discourse so Auth can be farmed to it FEATURE: pass return_sso_url to SSO endpoints, for easier return --- app/controllers/session_controller.rb | 30 +++++++++++++- app/models/discourse_single_sign_on.rb | 1 + config/locales/server.en.yml | 1 + config/routes.rb | 1 + config/site_settings.yml | 1 + lib/single_sign_on.rb | 2 +- spec/controllers/session_controller_spec.rb | 41 ++++++++++++++++++-- spec/models/discourse_single_sign_on_spec.rb | 6 +-- 8 files changed, 74 insertions(+), 9 deletions(-) diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index f8efa8db5f..cae2504719 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -1,9 +1,10 @@ require_dependency 'rate_limiter' +require_dependency 'single_sign_on' class SessionController < ApplicationController skip_before_filter :redirect_to_login_if_required - skip_before_filter :check_xhr, only: ['sso', 'sso_login', 'become'] + skip_before_filter :check_xhr, only: ['sso', 'sso_login', 'become', 'sso_provider'] def csrf render json: {csrf: form_authenticity_token } @@ -17,6 +18,25 @@ class SessionController < ApplicationController end end + def sso_provider(payload=nil) + payload ||= request.query_string + if SiteSetting.enable_sso_provider + sso = SingleSignOn.parse(payload, SiteSetting.sso_secret) + if current_user + sso.name = current_user.name + sso.username = current_user.username + sso.email = current_user.email + sso.external_id = current_user.id.to_s + redirect_to sso.to_url(sso.return_sso_url) + else + session[:sso_payload] = request.query_string + redirect_to '/login' + end + else + render nothing: true, status: 404 + end + end + # For use in development mode only when login options could be limited or disabled. # NEVER allow this to work in production. def become @@ -83,6 +103,7 @@ class SessionController < ApplicationController login = params[:login].strip login = login[1..-1] if login[0] == "@" + if user = User.find_by_username_or_email(login) # If their password is correct @@ -200,7 +221,12 @@ class SessionController < ApplicationController def login(user) log_on_user(user) - render_serialized(user, UserSerializer) + + if payload = session.delete(:sso_payload) + sso_provider(payload) + else + render_serialized(user, UserSerializer) + end end end diff --git a/app/models/discourse_single_sign_on.rb b/app/models/discourse_single_sign_on.rb index 610f6aafe4..1e3cee24c1 100644 --- a/app/models/discourse_single_sign_on.rb +++ b/app/models/discourse_single_sign_on.rb @@ -14,6 +14,7 @@ class DiscourseSingleSignOn < SingleSignOn sso = new sso.nonce = SecureRandom.hex sso.register_nonce(return_path) + sso.return_sso_url = Discourse.base_url + "/session/sso_login" sso.to_url end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index e62e3f4d3e..233d0a0739 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -787,6 +787,7 @@ en: block_common_passwords: "Don't allow passwords that are in the 10,000 most common passwords." enable_sso: "Enable single sign on via an external site (Note: disables invites)" + enable_sso_provider: "Implement Discourse SSO protocol at the /session/sso_provider endpoint, requires sso_secret to be set" sso_url: "URL of single sign on endpoint" sso_secret: "Secret string used to encrypt/decrypt SSO information, be sure it is 10 chars or longer" sso_overrides_email: "Overrides local email with external site email from SSO payload (WARNING: discrepancies can occur due to normalization of local emails)" diff --git a/config/routes.rb b/config/routes.rb index d56d603376..f694e87d90 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -197,6 +197,7 @@ Discourse::Application.routes.draw do get "session/sso" => "session#sso" get "session/sso_login" => "session#sso_login" + get "session/sso_provider" => "session#sso_provider" get "session/current" => "session#current" get "session/csrf" => "session#csrf" get "composer-messages" => "composer_messages#index" diff --git a/config/site_settings.yml b/config/site_settings.yml index b4a1062553..7bde8b1e45 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -221,6 +221,7 @@ login: enable_sso: client: true default: false + enable_sso_provider: false sso_url: '' sso_secret: '' sso_overrides_email: false diff --git a/lib/single_sign_on.rb b/lib/single_sign_on.rb index bd01633c96..722fce4d3e 100644 --- a/lib/single_sign_on.rb +++ b/lib/single_sign_on.rb @@ -1,6 +1,6 @@ class SingleSignOn ACCESSORS = [:nonce, :name, :username, :email, :avatar_url, :avatar_force_update, - :about_me, :external_id] + :about_me, :external_id, :return_sso_url] FIXNUMS = [] NONCE_EXPIRY_TIME = 10.minutes diff --git a/spec/controllers/session_controller_spec.rb b/spec/controllers/session_controller_spec.rb index a46c6b9c0a..7eca71f3bb 100644 --- a/spec/controllers/session_controller_spec.rb +++ b/spec/controllers/session_controller_spec.rb @@ -26,9 +26,9 @@ describe SessionController do @sso_url = "http://somesite.com/discourse_sso" @sso_secret = "shjkfdhsfkjh" - SiteSetting.stubs("enable_sso").returns(true) - SiteSetting.stubs("sso_url").returns(@sso_url) - SiteSetting.stubs("sso_secret").returns(@sso_secret) + SiteSetting.enable_sso = true + SiteSetting.sso_url = @sso_url + SiteSetting.sso_secret = @sso_secret # We have 2 options, either fabricate an admin or don't # send welcome messages @@ -97,6 +97,7 @@ describe SessionController do it 'allows login to existing account with valid nonce' do sso = get_sso('/hello/world') sso.external_id = '997' + sso.sso_url = "http://somewhere.over.com/sso_login" user = Fabricate(:user) user.create_single_sign_on_record(external_id: '997', last_payload: '') @@ -116,6 +117,40 @@ describe SessionController do response.code.should == '500' end + it 'can act as an SSO provider' do + SiteSetting.enable_sso_provider = true + SiteSetting.enable_sso = false + SiteSetting.enable_local_logins = true + SiteSetting.sso_secret = "topsecret" + + sso = SingleSignOn.new + sso.nonce = "mynonce" + sso.sso_secret = SiteSetting.sso_secret + sso.return_sso_url = "http://somewhere.over.rainbow/sso" + + get :sso_provider, Rack::Utils.parse_query(sso.payload) + + response.should redirect_to("/login") + + user = Fabricate(:user, password: "frogs", active: true) + EmailToken.update_all(confirmed: true) + + xhr :post, :create, login: user.username, password: "frogs", format: :json + + location = response.header["Location"] + location.should =~ /^http:\/\/somewhere.over.rainbow\/sso/ + + payload = location.split("?")[1] + + sso2 = SingleSignOn.parse(payload, "topsecret") + + sso2.email.should == user.email + sso2.name.should == user.name + sso2.username.should == user.username + sso2.external_id == user.id.to_s + + end + describe 'local attribute override from SSO payload' do before do SiteSetting.stubs("sso_overrides_email").returns(true) diff --git a/spec/models/discourse_single_sign_on_spec.rb b/spec/models/discourse_single_sign_on_spec.rb index 5f174c8b49..3f2d1c4646 100644 --- a/spec/models/discourse_single_sign_on_spec.rb +++ b/spec/models/discourse_single_sign_on_spec.rb @@ -5,9 +5,9 @@ describe DiscourseSingleSignOn do @sso_url = "http://somesite.com/discourse_sso" @sso_secret = "shjkfdhsfkjh" - SiteSetting.stubs("enable_sso").returns(true) - SiteSetting.stubs("sso_url").returns(@sso_url) - SiteSetting.stubs("sso_secret").returns(@sso_secret) + SiteSetting.enable_sso = true + SiteSetting.sso_url = @sso_url + SiteSetting.sso_secret = @sso_secret end def make_sso From 37a9164fa0130cb18c0695cc9fafe9a96ee36ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 26 Nov 2014 13:05:43 +0100 Subject: [PATCH 078/991] FIX: click counter for attachments --- app/assets/javascripts/discourse/views/post_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/views/post_view.js b/app/assets/javascripts/discourse/views/post_view.js index 0eb45c93d4..77e0b917ee 100644 --- a/app/assets/javascripts/discourse/views/post_view.js +++ b/app/assets/javascripts/discourse/views/post_view.js @@ -157,7 +157,7 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, { self.$(".cooked a[href]").each(function() { var link = $(this); - if (!lc.internal && link.attr('href') === lc.url) { + if ((!lc.internal || lc.url[0] === "/") && link.attr('href') === lc.url) { // don't display badge counts on category badge if (link.closest('.badge-category').length === 0 && ((link.closest(".onebox-result").length === 0 && link.closest('.onebox-body').length === 0) || link.hasClass("track-link"))) { link.append("I love http://discourse.org

    ") From 1524612719cf78f110d029d603e74b7ad2c46e29 Mon Sep 17 00:00:00 2001 From: Kris Aubuchon Date: Wed, 26 Nov 2014 10:49:50 -0500 Subject: [PATCH 080/991] fixing reply-tab bug and user profile background adjustment --- app/assets/javascripts/discourse/templates/user/user.hbs | 4 ++-- app/assets/stylesheets/desktop/topic-post.scss | 4 ---- app/assets/stylesheets/desktop/user.scss | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index bf7074dbdf..3f8323efe4 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -5,7 +5,7 @@
    -
    +
    {{#if number_of_flags_given}}
    {{number_of_flags_given}} {{i18n user.staff_counters.flags_given}}
    @@ -31,7 +31,7 @@
    {{number_of_warnings}} {{i18n user.staff_counters.warnings_received}}
    {{/if}}
    -
    +
    {{bound-avatar model "huge"}} diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 1ffac9265f..c2f82954e5 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -37,10 +37,6 @@ h1 .topic-statuses .topic-status i { color: scale-color($primary, $lightness: 50%); } -.via-email .reply-to-tab { - padding: 13px 15px 5px; -} - .gutter { .reply-new { .discourse-no-touch & { diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index dc22e2845b..e3ac208f71 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -182,8 +182,8 @@ } .about { - background-size: 1110px 250px; background-position: center center; + background-size: cover; width: 100%; margin-bottom: 10px; overflow: hidden; From a3e53e0d2845967ce38c8a7a97e0e491cf3c1ac5 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Nov 2014 13:05:49 -0500 Subject: [PATCH 081/991] Use far fewer admin user list routes, instead pass the query as a parameter. --- .../controllers/admin-users-list-show.js.es6 | 74 +++++++++ .../admin/controllers/admin-users-list.js.es6 | 140 ------------------ .../admin/routes/admin-users-list-show.js.es6 | 15 ++ .../javascripts/admin/routes/admin_routes.js | 6 +- .../admin/routes/admin_users_list_routes.js | 137 ----------------- .../admin_users_list_trust_level_routes.js | 69 --------- .../javascripts/admin/templates/admin.hbs | 2 +- .../javascripts/admin/templates/dashboard.hbs | 8 +- .../templates/reports/trust_levels_report.hbs | 10 +- .../admin/templates/user_tl3_requirements.hbs | 2 +- .../admin/templates/users-list-show.hbs | 84 +++++++++++ .../admin/templates/users_list.hbs | 99 ++----------- .../discourse/ember/resolver.js.es6 | 2 +- 13 files changed, 196 insertions(+), 452 deletions(-) create mode 100644 app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 delete mode 100644 app/assets/javascripts/admin/controllers/admin-users-list.js.es6 create mode 100644 app/assets/javascripts/admin/routes/admin-users-list-show.js.es6 delete mode 100644 app/assets/javascripts/admin/routes/admin_users_list_routes.js delete mode 100644 app/assets/javascripts/admin/routes/admin_users_list_trust_level_routes.js create mode 100644 app/assets/javascripts/admin/templates/users-list-show.hbs diff --git a/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 new file mode 100644 index 0000000000..20a48ea5d3 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 @@ -0,0 +1,74 @@ +export default Ember.ArrayController.extend({ + query: null, + showEmails: false, + refreshing: false, + listFilter: null, + selectAll: false, + + queryNew: Em.computed.equal('query', 'new'), + queryPending: Em.computed.equal('query', 'pending'), + queryHasApproval: Em.computed.or('queryNew', 'queryPending'), + showApproval: Em.computed.and('siteSettings.must_approve_users', 'queryHasApproval'), + searchHint: Discourse.computed.i18n('search_hint'), + hasSelection: Em.computed.gt('selectedCount', 0), + + selectedCount: function() { + var model = this.get('model'); + if (!model || !model.length) return 0; + return model.filterProperty('selected').length; + }.property('model.@each.selected'), + + selectAllChanged: function() { + var val = this.get('selectAll'); + this.get('model').forEach(function(user) { + if (user.get('can_approve')) { + user.set('selected', val); + } + }); + }.observes('selectAll'), + + title: function() { + return I18n.t('admin.users.titles.' + this.get('query')); + }.property('query'), + + _filterUsers: Discourse.debounce(function() { + this._refreshUsers(); + }, 250).observes('listFilter'), + + _refreshUsers: function() { + var self = this; + this.set('refreshing', true); + + Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('listFilter'), show_emails: this.get('showEmails') }).then(function (result) { + self.set('model', result); + }).finally(function() { + self.set('refreshing', false); + }); + }, + + actions: { + approveUsers: function() { + Discourse.AdminUser.bulkApprove(this.get('model').filterProperty('selected')); + this._refreshUsers(); + }, + + rejectUsers: function() { + var controller = this; + Discourse.AdminUser.bulkReject(this.get('model').filterProperty('selected')).then(function(result){ + var message = I18n.t("admin.users.reject_successful", {count: result.success}); + if (result.failed > 0) { + message += ' ' + I18n.t("admin.users.reject_failures", {count: result.failed}); + message += ' ' + I18n.t("admin.user.delete_forbidden", {count: Discourse.SiteSettings.delete_user_max_post_age}); + } + bootbox.alert(message); + controller._refreshUsers(); + }); + }, + + showEmails: function() { + this.set('showEmails', true); + this._refreshUsers(true); + } + } + +}); diff --git a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 b/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 deleted file mode 100644 index e3c237d9d2..0000000000 --- a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 +++ /dev/null @@ -1,140 +0,0 @@ -/** - This controller supports the interface for listing users in the admin section. - - @class AdminUsersListController - @extends Ember.ArrayController - @namespace Discourse - @module Discourse -**/ -export default Ember.ArrayController.extend(Discourse.Presence, { - username: null, - query: null, - selectAll: false, - loading: false, - - mustApproveUsers: Discourse.computed.setting('must_approve_users'), - queryNew: Em.computed.equal('query', 'new'), - queryPending: Em.computed.equal('query', 'pending'), - queryHasApproval: Em.computed.or('queryNew', 'queryPending'), - - searchHint: function() { return I18n.t("search_hint"); }.property(), - - /** - Triggered when the selectAll property is changed - - @event selectAll - **/ - selectAllChanged: function() { - var _this = this; - _.each(this.get('model'),function(user) { - user.set('selected', _this.get('selectAll')); - }); - }.observes('selectAll'), - - /** - Triggered when the username filter is changed - - @event filterUsers - **/ - filterUsers: Discourse.debounce(function() { - this.refreshUsers(); - }, 250).observes('username'), - - /** - Triggered when the order of the users list is changed - - @event orderChanged - **/ - orderChanged: function() { - this.refreshUsers(); - }.observes('query'), - - /** - The title of the user list, based on which query was performed. - - @property title - **/ - title: function() { - return I18n.t('admin.users.titles.' + this.get('query')); - }.property('query'), - - /** - Do we want to show the approval controls? - - @property showApproval - **/ - showApproval: function() { - return Discourse.SiteSettings.must_approve_users && this.get('queryHasApproval'); - }.property('queryPending'), - - /** - How many users are currently selected - - @property selectedCount - **/ - selectedCount: function() { - if (this.blank('model')) return 0; - return this.get('model').filterProperty('selected').length; - }.property('model.@each.selected'), - - /** - Do we have any selected users? - - @property hasSelection - **/ - hasSelection: Em.computed.gt('selectedCount', 0), - - /** - Refresh the current list of users. - - @method refreshUsers - **/ - refreshUsers: function(showEmails) { - var adminUsersListController = this; - adminUsersListController.set('loading', true); - - Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('username'), show_emails: showEmails }).then(function (result) { - adminUsersListController.set('model', result); - adminUsersListController.set('loading', false); - }); - }, - - - /** - Show the list of users. - - @method show - **/ - show: function(term) { - if (this.get('query') === term) { - this.refreshUsers(); - return; - } - this.set('query', term); - }, - - actions: { - approveUsers: function() { - Discourse.AdminUser.bulkApprove(this.get('model').filterProperty('selected')); - this.refreshUsers(); - }, - - rejectUsers: function() { - var controller = this; - Discourse.AdminUser.bulkReject(this.get('model').filterProperty('selected')).then(function(result){ - var message = I18n.t("admin.users.reject_successful", {count: result.success}); - if (result.failed > 0) { - message += ' ' + I18n.t("admin.users.reject_failures", {count: result.failed}); - message += ' ' + I18n.t("admin.user.delete_forbidden", {count: Discourse.SiteSettings.delete_user_max_post_age}); - } - bootbox.alert(message); - controller.refreshUsers(); - }); - }, - - showEmails: function() { - this.refreshUsers(true); - } - } - -}); diff --git a/app/assets/javascripts/admin/routes/admin-users-list-show.js.es6 b/app/assets/javascripts/admin/routes/admin-users-list-show.js.es6 new file mode 100644 index 0000000000..87d5fce68c --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-users-list-show.js.es6 @@ -0,0 +1,15 @@ +export default Discourse.Route.extend({ + model: function(params) { + this.userFilter = params.filter; + return Discourse.AdminUser.findAll(params.filter); + }, + + setupController: function(controller, model) { + controller.setProperties({ + model: model, + query: this.userFilter, + showEmails: false, + refreshing: false, + }); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin_routes.js b/app/assets/javascripts/admin/routes/admin_routes.js index 6640df9198..737e6e9eec 100644 --- a/app/assets/javascripts/admin/routes/admin_routes.js +++ b/app/assets/javascripts/admin/routes/admin_routes.js @@ -49,11 +49,9 @@ Discourse.Route.buildRoutes(function() { this.route('badges'); this.route('tl3Requirements', { path: '/tl3_requirements' }); }); + this.resource('adminUsersList', { path: '/list' }, function() { - _.each(['active', 'new', 'pending', 'admins', 'moderators', 'blocked', 'suspended', - 'newuser', 'basicuser', 'regular', 'leaders', 'elders'], function(x) { - this.route(x, { path: '/' + x }); - }, this); + this.route('show', {path: '/:filter'}); }); }); diff --git a/app/assets/javascripts/admin/routes/admin_users_list_routes.js b/app/assets/javascripts/admin/routes/admin_users_list_routes.js deleted file mode 100644 index a6b2b330b7..0000000000 --- a/app/assets/javascripts/admin/routes/admin_users_list_routes.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - Handles the route that deals with listing users - - @class AdminUsersListRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListRoute = Discourse.Route.extend({ - renderTemplate: function() { - this.render('admin/templates/users_list', {into: 'admin/templates/admin'}); - }, - - actions: { - exportUsers: function() { - Discourse.ExportCsv.exportUserList().then(function(result) { - if (result.success) { - bootbox.alert(I18n.t("admin.export_csv.success")); - } else { - bootbox.alert(I18n.t("admin.export_csv.failed")); - } - }); - } - } -}); - -/** - Index should just redirect to active - - @class AdminUsersIndexRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListIndexRoute = Discourse.Route.extend({ - redirect: function() { - this.transitionTo('adminUsersList.active'); - } -}); - -/** - Handles the route that lists active users. - - @class AdminUsersListActiveRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListActiveRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('active'); - } -}); - -/** - Handles the route that lists new users. - - @class AdminUsersListNewRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListNewRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('new'); - } -}); - -/** - Handles the route that lists pending users. - - @class AdminUsersListPendingRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListPendingRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('pending'); - } -}); - -/** - Handles the route that lists admin users. - - @class AdminUsersListAdminsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListAdminsRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('admins'); - } -}); - -/** - Handles the route that lists moderators. - - @class AdminUsersListModeratorsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListModeratorsRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('moderators'); - } -}); - -/** - Handles the route that lists blocked users. - - @class AdminUsersListBlockedRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListBlockedRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('blocked'); - } -}); - -/** - Handles the route that lists suspended users. - - @class AdminUsersListSuspendedRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListSuspendedRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('suspended'); - } -}); diff --git a/app/assets/javascripts/admin/routes/admin_users_list_trust_level_routes.js b/app/assets/javascripts/admin/routes/admin_users_list_trust_level_routes.js deleted file mode 100644 index 4575299b2e..0000000000 --- a/app/assets/javascripts/admin/routes/admin_users_list_trust_level_routes.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - Handles the route that lists users at trust level 0. - - @class AdminUsersListNewuserRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListNewuserRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('newuser'); - } -}); - -/** - Handles the route that lists users at trust level 1. - - @class AdminUsersListBasicuserRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListBasicuserRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('basic'); - } -}); - -/** - Handles the route that lists users at trust level 2. - - @class AdminUsersListRegularRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListRegularRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('regular'); - } -}); - -/** - Handles the route that lists users at trust level 3. - - @class AdminUsersListLeadersRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListLeadersRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('leader'); - } -}); - -/** - Handles the route that lists users at trust level 4. - - @class AdminUsersListEldersRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListEldersRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('elder'); - } -}); diff --git a/app/assets/javascripts/admin/templates/admin.hbs b/app/assets/javascripts/admin/templates/admin.hbs index d67291e9ad..09eadbdb7d 100644 --- a/app/assets/javascripts/admin/templates/admin.hbs +++ b/app/assets/javascripts/admin/templates/admin.hbs @@ -8,7 +8,7 @@ {{#if currentUser.admin}}
  • {{#link-to 'adminSiteSettings'}}{{i18n admin.site_settings.title}}{{/link-to}}
  • {{/if}} -
  • {{#link-to 'adminUsersList'}}{{i18n admin.users.title}}{{/link-to}}
  • +
  • {{#link-to 'adminUsersList.show' 'active'}}{{i18n admin.users.title}}{{/link-to}}
  • {{#if showBadges}}
  • {{#link-to 'adminBadges.index'}}{{i18n admin.badges.title}}{{/link-to}}
  • {{/if}} diff --git a/app/assets/javascripts/admin/templates/dashboard.hbs b/app/assets/javascripts/admin/templates/dashboard.hbs index cb597d1cdc..97d0390ded 100644 --- a/app/assets/javascripts/admin/templates/dashboard.hbs +++ b/app/assets/javascripts/admin/templates/dashboard.hbs @@ -25,15 +25,15 @@ - + - + - + - +
    {{i18n admin.dashboard.admins}}{{#link-to 'adminUsersList.admins'}}{{admins}}{{/link-to}}{{#link-to 'adminUsersList.show' 'admins'}}{{admins}}{{/link-to}} {{i18n admin.dashboard.suspended}}{{#link-to 'adminUsersList.suspended'}}{{suspended}}{{/link-to}}{{#link-to 'adminUsersList.show' 'suspended'}}{{suspended}}{{/link-to}}
    {{i18n admin.dashboard.moderators}}{{#link-to 'adminUsersList.moderators'}}{{moderators}}{{/link-to}}{{#link-to 'adminUsersList.show' 'moderators'}}{{moderators}}{{/link-to}} {{i18n admin.dashboard.blocked}}{{#link-to 'adminUsersList.blocked'}}{{blocked}}{{/link-to}}{{#link-to 'adminUsersList.show' 'blocked'}}{{blocked}}{{/link-to}}
    diff --git a/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs b/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs index b483755fe1..b2ab0c92ef 100644 --- a/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs +++ b/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs @@ -1,8 +1,8 @@ {{title}} - {{#link-to 'adminUsersList.newuser'}}{{valueAtTrustLevel data 0}}{{/link-to}} - {{#link-to 'adminUsersList.basicuser'}}{{valueAtTrustLevel data 1}}{{/link-to}} - {{#link-to 'adminUsersList.regular'}}{{valueAtTrustLevel data 2}}{{/link-to}} - {{#link-to 'adminUsersList.leaders'}}{{valueAtTrustLevel data 3}}{{/link-to}} - {{#link-to 'adminUsersList.elders'}}{{valueAtTrustLevel data 4}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'newuser'}}{{valueAtTrustLevel data 0}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'basic'}}{{valueAtTrustLevel data 1}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'regular'}}{{valueAtTrustLevel data 2}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'leader'}}{{valueAtTrustLevel data 3}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'elder'}}{{valueAtTrustLevel data 4}}{{/link-to}} diff --git a/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs b/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs index 7bb228867b..cb57aa4875 100644 --- a/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs +++ b/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs @@ -2,7 +2,7 @@
    diff --git a/app/assets/javascripts/admin/templates/users-list-show.hbs b/app/assets/javascripts/admin/templates/users-list-show.hbs new file mode 100644 index 0000000000..3f2dd2ceab --- /dev/null +++ b/app/assets/javascripts/admin/templates/users-list-show.hbs @@ -0,0 +1,84 @@ +{{#if hasSelection}} +
    + + +
    +{{/if}} + +
    +
    +

    {{title}}

    +
    +
    +
    + {{text-field value=listFilter placeholder=searchHint}} + {{#unless showEmails}} +
    + +
    + {{/unless}} +
    + +{{#loading-spinner condition=refreshing}} + {{#if model}} + + + {{#if showApproval}} + + {{/if}} + + + + + + + + + + {{#if showApproval}} + + {{/if}} + + + + {{#each model}} + + {{#if controller.showApproval}} + + {{/if}} + + + + + + + + + + + + {{#if showApproval}} + + {{/if}} + + {{/each}} + +
    {{input type="checkbox" checked=selectAll}} {{i18n username}}{{i18n email}}{{i18n admin.users.last_emailed}}{{i18n last_seen}}{{i18n admin.user.topics_entered}}{{i18n admin.user.posts_read_count}}{{i18n admin.user.time_read}}{{i18n created}}{{i18n admin.users.approved}} 
    + {{#if can_approve}} + {{input type="checkbox" checked=selected}} + {{/if}} + {{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}}{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}{{{unbound email}}}{{{unbound last_emailed_age}}}{{{unbound last_seen_age}}}{{{unbound topics_entered}}}{{{unbound posts_read_count}}}{{{unbound time_read}}}{{{unbound created_at_age}}} + {{#if approved}} + {{i18n yes_value}} + {{else}} + {{i18n no_value}} + {{/if}} + + {{#if admin}}{{/if}} + {{#if moderator}}{{/if}} + +
    + {{else}} +

    {{i18n search.no_results}}

    + {{/if}} +{{/loading-spinner}} diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index c4ab6dac64..7850023360 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -1,103 +1,22 @@
    -
    - {{text-field value=username placeholder=searchHint}} -
    - {{#if hasSelection}} -
    - - -
    - {{/if}} - -
    -
    -

    {{title}}

    -
    -
    - -
    -
    - - {{#loading-spinner condition=loading}} - {{#if model.length}} - - - {{#if showApproval}} - - {{/if}} - - - - - - - - - - {{#if showApproval}} - - {{/if}} - - - - {{#each model}} - - {{#if controller.showApproval}} - - {{/if}} - - - - - - - - - - - - {{#if showApproval}} - - {{/if}} - - {{/each}} - -
    {{view Ember.Checkbox checkedBinding="selectAll"}} {{i18n username}}{{i18n email}}{{i18n admin.users.last_emailed}}{{i18n last_seen}}{{i18n admin.user.topics_entered}}{{i18n admin.user.posts_read_count}}{{i18n admin.user.time_read}}{{i18n created}}{{i18n admin.users.approved}} 
    - {{#if can_approve}} - {{view Ember.Checkbox checkedBinding="selected"}} - {{/if}} - {{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}}{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}{{{unbound email}}}{{{unbound last_emailed_age}}}{{{unbound last_seen_age}}}{{{unbound topics_entered}}}{{{unbound posts_read_count}}}{{{unbound time_read}}}{{{unbound created_at_age}}} - {{#if approved}} - {{i18n yes_value}} - {{else}} - {{i18n no_value}} - {{/if}} - - {{#if admin}}{{/if}} - {{#if moderator}}{{/if}} - -
    - {{else}} -

    {{i18n search.no_results}}

    - {{/if}} - {{/loading-spinner}} + {{outlet}}
    diff --git a/app/assets/javascripts/discourse/ember/resolver.js.es6 b/app/assets/javascripts/discourse/ember/resolver.js.es6 index a11aa48788..b4cb4bd0d9 100644 --- a/app/assets/javascripts/discourse/ember/resolver.js.es6 +++ b/app/assets/javascripts/discourse/ember/resolver.js.es6 @@ -4,7 +4,7 @@ var classify = Ember.String.classify; var get = Ember.get; var LOADING_WHITELIST = ['badges', 'userActivity', 'userPrivateMessages', 'admin', 'adminFlags', - 'user', 'preferences', 'adminEmail'], + 'user', 'preferences', 'adminEmail', 'adminUsersList'], _dummyRoute, _loadingView; From cb124d5836b978b93da508ead7eefea3e627785b Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Nov 2014 13:10:12 -0500 Subject: [PATCH 082/991] UX: Consolidate "Admins" and "Moderators" into "Staff" on admin users list --- app/assets/javascripts/admin/templates/users_list.hbs | 3 +-- config/locales/client.en.yml | 4 ++-- lib/admin_user_index_query.rb | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index 7850023360..e5d1a413ac 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -6,8 +6,7 @@ {{#if siteSettings.must_approve_users}}
  • {{#link-to 'adminUsersList.show' 'pending'}}{{i18n admin.users.nav.pending}}{{/link-to}}
  • {{/if}} -
  • {{#link-to 'adminUsersList.show' 'admins'}}{{i18n admin.users.nav.admins}}{{/link-to}}
  • -
  • {{#link-to 'adminUsersList.show' 'moderators'}}{{i18n admin.users.nav.moderators}}{{/link-to}}
  • +
  • {{#link-to 'adminUsersList.show' 'staff'}}{{i18n admin.users.nav.staff}}{{/link-to}}
  • {{#link-to 'adminUsersList.show' 'suspended'}}{{i18n admin.users.nav.suspended}}{{/link-to}}
  • {{#link-to 'adminUsersList.show' 'blocked'}}{{i18n admin.users.nav.blocked}}{{/link-to}}
  • diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e3f108426b..15a3320393 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1892,8 +1892,7 @@ en: new: "New" active: "Active" pending: "Pending" - admins: 'Admins' - moderators: 'Mods' + staff: 'Staff' suspended: 'Suspended' blocked: 'Blocked' approved: "Approved?" @@ -1912,6 +1911,7 @@ en: regular: 'Users at Trust Level 2 (Member)' leader: 'Users at Trust Level 3 (Regular)' elder: 'Users at Trust Level 4 (Leader)' + staff: "Staff" admins: 'Admin Users' moderators: 'Moderators' blocked: 'Blocked Users' diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index cfcdd4ca4f..281049910a 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -49,6 +49,7 @@ class AdminUserIndexQuery def filter_by_query_classification case params[:query] + when 'staff' then @query.where("admin or moderator") when 'admins' then @query.where(admin: true) when 'moderators' then @query.where(moderator: true) when 'blocked' then @query.blocked From 27d78332c4d02ae3b9b64a232c5dcf2a1325d8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 26 Nov 2014 19:20:03 +0100 Subject: [PATCH 083/991] FEATURE: restrict some user fields for TL0 users when viewed by anonymous users --- app/serializers/user_serializer.rb | 40 +++++++++++------------- spec/serializers/user_serializer_spec.rb | 12 +++++++ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index a5b116b0be..4f6d18f4c4 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -18,6 +18,17 @@ class UserSerializer < BasicUserSerializer end end + # attributes that are hidden for TL0 users when seen by anonymous + def self.untrusted_attributes(*attrs) + attrs.each do |attr| + method_name = "include_#{attr}?" + define_method(method_name) do + return false if object.trust_level == TrustLevel[0] && scope.anonymous? + send(attr).present? + end + end + end + attributes :name, :email, :last_posted_at, @@ -87,6 +98,14 @@ class UserSerializer < BasicUserSerializer :card_image_badge, :card_image_badge_id + untrusted_attributes :bio_raw, + :bio_cooked, + :bio_excerpt, + :location, + :website, + :profile_background, + :card_background + ### ### ATTRIBUTES ### @@ -99,15 +118,10 @@ class UserSerializer < BasicUserSerializer object.user_profile.card_image_badge end - def bio_raw object.user_profile.bio_raw end - def include_bio_raw? - bio_raw.present? - end - def bio_cooked object.user_profile.bio_processed end @@ -116,10 +130,6 @@ class UserSerializer < BasicUserSerializer object.user_profile.website end - def include_website? - website.present? - end - def card_image_badge_id object.user_profile.card_image_badge.try(:id) end @@ -140,26 +150,14 @@ class UserSerializer < BasicUserSerializer object.user_profile.profile_background end - def include_profile_background? - profile_background.present? - end - def card_background object.user_profile.card_background end - def include_card_background? - card_background.present? - end - def location object.user_profile.location end - def include_location? - location.present? - end - def can_edit scope.can_edit?(object) end diff --git a/spec/serializers/user_serializer_spec.rb b/spec/serializers/user_serializer_spec.rb index c26bc8217a..dc332dcf0c 100644 --- a/spec/serializers/user_serializer_spec.rb +++ b/spec/serializers/user_serializer_spec.rb @@ -3,6 +3,18 @@ require_dependency 'user' describe UserSerializer do + context "with a TL0 user seen as anonymous" do + let(:user) { Fabricate.build(:user, trust_level: 0, user_profile: Fabricate.build(:user_profile)) } + let(:serializer) { UserSerializer.new(user, scope: Guardian.new, root: false) } + let(:json) { serializer.as_json } + + let(:untrusted_attributes) { %i{bio_raw bio_cooked bio_excerpt location website profile_background card_background} } + + it "doesn't serialize untrusted attributes" do + untrusted_attributes.each { |attr| json.should_not have_key(attr) } + end + end + context "with a user" do let(:user) { Fabricate.build(:user, user_profile: Fabricate.build(:user_profile) ) } let(:serializer) { UserSerializer.new(user, scope: Guardian.new, root: false) } From 10b503218889b1e920b9c6144e74884f91555c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 26 Nov 2014 19:51:07 +0100 Subject: [PATCH 084/991] FIX: auto-closing attribution when a TL4 user auto-closes a topic --- app/models/topic.rb | 4 ++-- spec/models/topic_spec.rb | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/models/topic.rb b/app/models/topic.rb index d37e93d585..d8572832e0 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -797,10 +797,10 @@ class Topic < ActiveRecord::Base else self.auto_close_started_at ||= Time.zone.now end - if by_user.try(:staff?) + if by_user.try(:staff?) || by_user.try(:trust_level) == TrustLevel[4] self.auto_close_user = by_user else - self.auto_close_user ||= (self.user.staff? ? self.user : Discourse.system_user) + self.auto_close_user ||= (self.user.staff? || self.user.trust_level == TrustLevel[4] ? self.user : Discourse.system_user) end end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index d78d7369fb..b47c8c0332 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -1078,6 +1078,7 @@ describe Topic do let(:topic) { Fabricate.build(:topic) } let(:closing_topic) { Fabricate.build(:topic, auto_close_hours: 5, auto_close_at: 5.hours.from_now, auto_close_started_at: 5.hours.from_now) } let(:admin) { Fabricate.build(:user, id: 123) } + let(:trust_level_4) { Fabricate.build(:trust_level_4) } before { Discourse.stubs(:system_user).returns(admin) } @@ -1131,17 +1132,22 @@ describe Topic do end end - it 'sets auto_close_user to given user if it is a staff user' do + it 'sets auto_close_user to given user if it is a staff or TL4 user' do topic.set_auto_close(3, admin) expect(topic.auto_close_user_id).to eq(admin.id) end - it 'sets auto_close_user to system user if given user is not staff' do + it 'sets auto_close_user to given user if it is a TL4 user' do + topic.set_auto_close(3, trust_level_4) + expect(topic.auto_close_user_id).to eq(trust_level_4.id) + end + + it 'sets auto_close_user to system user if given user is not staff or a TL4 user' do topic.set_auto_close(3, Fabricate.build(:user, id: 444)) expect(topic.auto_close_user_id).to eq(admin.id) end - it 'sets auto_close_user to system_user if user is not given and topic creator is not staff' do + it 'sets auto_close_user to system user if user is not given and topic creator is not staff nor TL4 user' do topic.set_auto_close(3) expect(topic.auto_close_user_id).to eq(admin.id) end @@ -1152,6 +1158,12 @@ describe Topic do expect(staff_topic.auto_close_user_id).to eq(999) end + it 'sets auto_close_user to topic creator if it is a TL4 user' do + tl4_topic = Fabricate.build(:topic, user: Fabricate.build(:trust_level_4, id: 998)) + tl4_topic.set_auto_close(3) + expect(tl4_topic.auto_close_user_id).to eq(998) + end + it 'clears auto_close_at if arg is nil' do closing_topic.set_auto_close(nil) expect(closing_topic.auto_close_at).to be_nil From 257bde8e2bf7dccd555718ece3e5e83f7626bbe1 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Nov 2014 13:56:12 -0500 Subject: [PATCH 085/991] FEATURE: "Suspect" users list in admin. --- .../admin/templates/users_list.hbs | 1 + app/controllers/admin/users_controller.rb | 2 +- app/serializers/admin_user_list_serializer.rb | 78 +++++++++++++++++ app/serializers/admin_user_serializer.rb | 86 ++----------------- config/locales/client.en.yml | 2 + lib/admin_user_index_query.rb | 29 ++++--- 6 files changed, 107 insertions(+), 91 deletions(-) create mode 100644 app/serializers/admin_user_list_serializer.rb diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index e5d1a413ac..36ae00e22a 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -9,6 +9,7 @@
  • {{#link-to 'adminUsersList.show' 'staff'}}{{i18n admin.users.nav.staff}}{{/link-to}}
  • {{#link-to 'adminUsersList.show' 'suspended'}}{{i18n admin.users.nav.suspended}}{{/link-to}}
  • {{#link-to 'adminUsersList.show' 'blocked'}}{{i18n admin.users.nav.blocked}}{{/link-to}}
  • +
  • {{#link-to 'adminUsersList.show' 'suspect'}}{{i18n admin.users.nav.suspect}}{{/link-to}}
  • diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 6649d2223c..efd9b3be43 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -32,7 +32,7 @@ class Admin::UsersController < Admin::AdminController StaffActionLogger.new(current_user).log_show_emails(users) end - render_serialized(users, AdminUserSerializer) + render_serialized(users, AdminUserListSerializer) end def show diff --git a/app/serializers/admin_user_list_serializer.rb b/app/serializers/admin_user_list_serializer.rb new file mode 100644 index 0000000000..0f04ce90fd --- /dev/null +++ b/app/serializers/admin_user_list_serializer.rb @@ -0,0 +1,78 @@ +class AdminUserListSerializer < BasicUserSerializer + + attributes :email, + :active, + :admin, + :moderator, + :last_seen_age, + :last_emailed_age, + :created_at_age, + :username_lower, + :trust_level, + :trust_level_locked, + :flag_level, + :username, + :title, + :avatar_template, + :can_approve, + :approved, + :suspended_at, + :suspended_till, + :suspended, + :blocked, + :time_read + + [:days_visited, :posts_read_count, :topics_entered, :post_count].each do |sym| + attributes sym + define_method sym do + object.user_stat.send(sym) + end + end + + def include_email? + # staff members can always see their email + (scope.is_staff? && object.id == scope.user.id) || scope.can_see_emails? + end + + alias_method :include_associated_accounts?, :include_email? + + def suspended + object.suspended? + end + + def can_impersonate + scope.can_impersonate?(object) + end + + def last_emailed_age + return nil if object.last_emailed_at.blank? + AgeWords.age_words(Time.now - object.last_emailed_at) + end + + def last_seen_age + return nil if object.last_seen_at.blank? + AgeWords.age_words(Time.now - object.last_seen_at) + end + + def time_read + return nil if object.user_stat.time_read.blank? + AgeWords.age_words(object.user_stat.time_read) + end + + def created_at_age + AgeWords.age_words(Time.now - object.created_at) + end + + def can_approve + scope.can_approve?(object) + end + + def include_can_approve? + SiteSetting.must_approve_users + end + + def include_approved? + SiteSetting.must_approve_users + end + +end diff --git a/app/serializers/admin_user_serializer.rb b/app/serializers/admin_user_serializer.rb index 9d40ee61d8..717316039d 100644 --- a/app/serializers/admin_user_serializer.rb +++ b/app/serializers/admin_user_serializer.rb @@ -1,88 +1,16 @@ -class AdminUserSerializer < BasicUserSerializer +require_dependency 'admin_user_list_serializer' - attributes :email, - :active, - :admin, - :moderator, - :last_seen_age, - :last_emailed_age, - :created_at_age, - :username_lower, - :trust_level, - :trust_level_locked, - :flag_level, - :username, - :title, - :avatar_template, - :can_approve, - :approved, - :suspended_at, - :suspended_till, - :suspended, - :ip_address, - :registration_ip_address, +class AdminUserSerializer < AdminUserListSerializer + + attributes :associated_accounts, :can_send_activation_email, :can_activate, - :can_deactivate, - :blocked, - :time_read, - :associated_accounts + :ip_address, + :registration_ip_address, + :can_send_activation_email has_one :single_sign_on_record, serializer: SingleSignOnRecordSerializer, embed: :objects - [:days_visited, :posts_read_count, :topics_entered, :post_count].each do |sym| - attributes sym - define_method sym do - object.user_stat.send(sym) - end - end - - def include_email? - # staff members can always see their email - (scope.is_staff? && object.id == scope.user.id) || scope.can_see_emails? - end - - alias_method :include_associated_accounts?, :include_email? - - def suspended - object.suspended? - end - - def can_impersonate - scope.can_impersonate?(object) - end - - def last_emailed_age - return nil if object.last_emailed_at.blank? - AgeWords.age_words(Time.now - object.last_emailed_at) - end - - def last_seen_age - return nil if object.last_seen_at.blank? - AgeWords.age_words(Time.now - object.last_seen_at) - end - - def time_read - return nil if object.user_stat.time_read.blank? - AgeWords.age_words(object.user_stat.time_read) - end - - def created_at_age - AgeWords.age_words(Time.now - object.created_at) - end - - def can_approve - scope.can_approve?(object) - end - - def include_can_approve? - SiteSetting.must_approve_users - end - - def include_approved? - SiteSetting.must_approve_users - end - def can_send_activation_email scope.can_send_activation_email?(object) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 15a3320393..a22f8aab1c 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1895,6 +1895,7 @@ en: staff: 'Staff' suspended: 'Suspended' blocked: 'Blocked' + suspect: 'Suspect' approved: "Approved?" approved_selected: one: "approve user" @@ -1916,6 +1917,7 @@ en: moderators: 'Moderators' blocked: 'Blocked Users' suspended: 'Suspended Users' + suspect: 'Suspect Users' reject_successful: one: "Successfully rejected 1 user." other: "Successfully rejected %{count} users." diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index 281049910a..f2420f15a3 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -11,15 +11,7 @@ class AdminUserIndexQuery attr_reader :params, :trust_levels def find_users(limit=100) - find_users_query.includes(:user_stat) - .includes(:single_sign_on_record) - .includes(:facebook_user_info) - .includes(:twitter_user_info) - .includes(:github_user_info) - .includes(:google_user_info) - .includes(:oauth2_user_info) - .includes(:user_open_ids) - .limit(limit) + find_users_query.includes(:user_stat).limit(limit) end def count_users @@ -32,10 +24,10 @@ class AdminUserIndexQuery if params[:query] == "active" order << "COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC" else - order << "created_at DESC" + order << "users.created_at DESC" end - order << "username" + order << "users.username" klass.order(order.reject(&:blank?).join(",")) end @@ -47,6 +39,20 @@ class AdminUserIndexQuery end end + def suspect_users + where_conds = [] + + # One signal: no reading yet the user has bio text + where_conds << "user_stats.posts_read_count = 0 AND user_stats.topics_entered = 0 AND COALESCE(user_profiles.bio_raw, '') = ''" + # Another surprising signal: Username ends with a number + where_conds << "users.username ~ '[0-9]+$'" + + @query.activated + .references(:user_stats) + .includes(:user_profile) + .where(where_conds.map {|c| "(#{c})"}.join(" AND ")) + end + def filter_by_query_classification case params[:query] when 'staff' then @query.where("admin or moderator") @@ -55,6 +61,7 @@ class AdminUserIndexQuery when 'blocked' then @query.blocked when 'suspended' then @query.suspended when 'pending' then @query.not_suspended.where(approved: false) + when 'suspect' then suspect_users end end From c7bda41ddac96cbdf5d137ef3330f40865a9f93a Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Nov 2014 16:29:58 -0500 Subject: [PATCH 086/991] FIX: Use `OR` query instead of `AND`, also check that the profile is blank --- lib/admin_user_index_query.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index f2420f15a3..f8ac8a8ca8 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -43,14 +43,13 @@ class AdminUserIndexQuery where_conds = [] # One signal: no reading yet the user has bio text - where_conds << "user_stats.posts_read_count = 0 AND user_stats.topics_entered = 0 AND COALESCE(user_profiles.bio_raw, '') = ''" - # Another surprising signal: Username ends with a number - where_conds << "users.username ~ '[0-9]+$'" + where_conds << "user_stats.posts_read_count = 0 AND user_stats.topics_entered = 0" @query.activated .references(:user_stats) .includes(:user_profile) - .where(where_conds.map {|c| "(#{c})"}.join(" AND ")) + .where("COALESCE(user_profiles.bio_raw, '') != ''") + .where(where_conds.map {|c| "(#{c})"}.join(" OR ")) end def filter_by_query_classification From 8366fcd78fd4427b6c0ee9019c9499b9d7a24616 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Nov 2014 18:01:45 -0500 Subject: [PATCH 087/991] FIX: Don't lose focus on site settings text field (was changing routes during filtering) --- .../admin-site-settings-category.js.es6 | 19 +++---------------- .../admin_site_settings_category_route.js | 7 +++++++ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 index f9f477ac9a..4f5fed4539 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 @@ -3,29 +3,16 @@ export default Ember.ObjectController.extend({ needs: ['adminSiteSettings'], filteredContent: function() { - if (!this.get('categoryNameKey')) { return Em.A(); } - - var category = this.get('controllers.adminSiteSettings.content').find(function(siteSettingCategory) { - return siteSettingCategory.nameKey === this.get('categoryNameKey'); - }, this); + if (!this.get('categoryNameKey')) { return []; } + var category = this.get('controllers.adminSiteSettings.content').findProperty('nameKey', this.get('categoryNameKey')); if (category) { return category.siteSettings; } else { - return Em.A(); + return []; } }.property('controllers.adminSiteSettings.content', 'categoryNameKey'), - emptyContentHandler: function() { - if (this.get('filteredContent').length < 1) { - if ( this.get('controllers.adminSiteSettings.filtered') ) { - this.transitionToRoute('adminSiteSettingsCategory', 'all_results'); - } else { - this.transitionToRoute('adminSiteSettings'); - } - } - }.observes('filteredContent'), - actions: { /** diff --git a/app/assets/javascripts/admin/routes/admin_site_settings_category_route.js b/app/assets/javascripts/admin/routes/admin_site_settings_category_route.js index b07967e970..1267040386 100644 --- a/app/assets/javascripts/admin/routes/admin_site_settings_category_route.js +++ b/app/assets/javascripts/admin/routes/admin_site_settings_category_route.js @@ -8,6 +8,13 @@ **/ Discourse.AdminSiteSettingsCategoryRoute = Discourse.Route.extend({ model: function(params) { + if (params.category_id === "all_results") { + var category = this.controllerFor('adminSiteSettings').get('content').findProperty('nameKey', 'all_results'); + if (!category || !category.siteSettings.length) { + this.replaceWith('adminSiteSettings.index'); + return; + } + } // The model depends on user input, so let the controller do the work: this.controllerFor('adminSiteSettingsCategory').set('categoryNameKey', params.category_id); return Em.Object.create({ From 5c965dcb0b01f5583bfe5970621b65c43f53c95e Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Nov 2014 10:46:32 +1100 Subject: [PATCH 088/991] FEATURE: show card when clicking on avatar in admin user list --- .../admin/templates/users-list-show.hbs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/admin/templates/users-list-show.hbs b/app/assets/javascripts/admin/templates/users-list-show.hbs index 3f2dd2ceab..67361e92b3 100644 --- a/app/assets/javascripts/admin/templates/users-list-show.hbs +++ b/app/assets/javascripts/admin/templates/users-list-show.hbs @@ -41,7 +41,7 @@   - {{#each model}} + {{#each user in model}} {{#if controller.showApproval}} @@ -50,20 +50,20 @@ {{/if}} {{/if}} - {{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}} - {{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}} - {{{unbound email}}} - {{{unbound last_emailed_age}}} - {{{unbound last_seen_age}}} - {{{unbound topics_entered}}} - {{{unbound posts_read_count}}} - {{{unbound time_read}}} + {{avatar user imageSize="small"}} + {{#link-to 'adminUser' user}}{{unbound user.username}}{{/link-to}} + {{{unbound user.email}}} + {{{unbound user.last_emailed_age}}} + {{{unbound user.last_seen_age}}} + {{{unbound user.topics_entered}}} + {{{unbound user.posts_read_count}}} + {{{unbound user.time_read}}} - {{{unbound created_at_age}}} + {{{unbound user.created_at_age}}} {{#if showApproval}} - {{#if approved}} + {{#if user.approved}} {{i18n yes_value}} {{else}} {{i18n no_value}} @@ -71,8 +71,8 @@ {{/if}} - {{#if admin}}{{/if}} - {{#if moderator}}{{/if}} + {{#if user.admin}}{{/if}} + {{#if user.moderator}}{{/if}} {{/each}} From 3fd4fc679e9469033d37dd86ac1921f76671faef Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Nov 2014 11:02:54 +1100 Subject: [PATCH 089/991] a few missing spots --- app/assets/javascripts/admin/templates/users-list-show.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/templates/users-list-show.hbs b/app/assets/javascripts/admin/templates/users-list-show.hbs index 67361e92b3..333e3f664a 100644 --- a/app/assets/javascripts/admin/templates/users-list-show.hbs +++ b/app/assets/javascripts/admin/templates/users-list-show.hbs @@ -42,10 +42,10 @@ {{#each user in model}} - + {{#if controller.showApproval}} - {{#if can_approve}} + {{#if user.can_approve}} {{input type="checkbox" checked=selected}} {{/if}} From 4e735e8aabe2bd103d1d1d7fafe1a84cba058d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 27 Nov 2014 01:12:23 +0100 Subject: [PATCH 090/991] FIX: roll up staff action wasn't working --- app/services/staff_action_logger.rb | 4 ++-- spec/services/staff_action_logger_spec.rb | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb index 7e0ca21dae..75fb96efa2 100644 --- a/app/services/staff_action_logger.rb +++ b/app/services/staff_action_logger.rb @@ -165,9 +165,9 @@ class StaffActionLogger })) end - def log_roll_up(subnets) + def log_roll_up(subnets, opts={}) UserHistory.create(params(opts).merge({ - action: UserHistory.action[:roll_up], + action: UserHistory.actions[:roll_up], details: subnets.join(", ") })) end diff --git a/spec/services/staff_action_logger_spec.rb b/spec/services/staff_action_logger_spec.rb index 937e595449..be31523615 100644 --- a/spec/services/staff_action_logger_spec.rb +++ b/spec/services/staff_action_logger_spec.rb @@ -220,4 +220,15 @@ describe StaffActionLogger do log_record.details.should == badge.name end end + + describe 'log_roll_up' do + let(:subnets) { ["1.2.3.0/24", "42.42.42.0/24"] } + subject(:log_roll_up) { described_class.new(admin).log_roll_up(subnets) } + + it 'creates a new UserHistory record' do + log_record = logger.log_roll_up(subnets) + log_record.should be_valid + log_record.details.should == subnets.join(", ") + end + end end From f918d3e89b7421be112529e54caedcdda433b88b Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Wed, 26 Nov 2014 16:35:56 -0800 Subject: [PATCH 091/991] FEATURE: add Message-ID header to emails when topic present for email of course --- lib/email/sender.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/email/sender.rb b/lib/email/sender.rb index 8fcbd1c5f1..19319b1d77 100644 --- a/lib/email/sender.rb +++ b/lib/email/sender.rb @@ -67,6 +67,8 @@ module Email email_log.topic_id = topic_id topic_identifier = "" + post_identifier = "" + @message.header['Message-ID'] = post_identifier @message.header['In-Reply-To'] = topic_identifier @message.header['References'] = topic_identifier From 800ae5265f98ff8177a4ddde53c566d53fe00f99 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Nov 2014 12:24:21 +1100 Subject: [PATCH 092/991] Add admin and moderator state to sso provider --- app/controllers/session_controller.rb | 2 ++ lib/single_sign_on.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index cae2504719..1ac1cba1b5 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -27,6 +27,8 @@ class SessionController < ApplicationController sso.username = current_user.username sso.email = current_user.email sso.external_id = current_user.id.to_s + sso.admin = current_user.admin? + sso.moderator = current_user.moderator? redirect_to sso.to_url(sso.return_sso_url) else session[:sso_payload] = request.query_string diff --git a/lib/single_sign_on.rb b/lib/single_sign_on.rb index 722fce4d3e..1df3240071 100644 --- a/lib/single_sign_on.rb +++ b/lib/single_sign_on.rb @@ -1,6 +1,6 @@ class SingleSignOn ACCESSORS = [:nonce, :name, :username, :email, :avatar_url, :avatar_force_update, - :about_me, :external_id, :return_sso_url] + :about_me, :external_id, :return_sso_url, :admin, :moderator] FIXNUMS = [] NONCE_EXPIRY_TIME = 10.minutes From 013f1a6dd02ad423089a42cd06995ab1404499fc Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Nov 2014 12:39:00 +1100 Subject: [PATCH 093/991] FEATURE: allow creating admin and moderator accounts via SSO --- app/models/discourse_single_sign_on.rb | 3 +++ lib/single_sign_on.rb | 6 +++++- spec/controllers/session_controller_spec.rb | 24 +++++++++++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/app/models/discourse_single_sign_on.rb b/app/models/discourse_single_sign_on.rb index 1e3cee24c1..b66ea4e2b1 100644 --- a/app/models/discourse_single_sign_on.rb +++ b/app/models/discourse_single_sign_on.rb @@ -67,6 +67,9 @@ class DiscourseSingleSignOn < SingleSignOn user.custom_fields[k] = v end + user.admin = admin unless admin.nil? + user.moderator = moderator unless moderator.nil? + # optionally save the user and sso_record if they have changed user.save! sso_record.save! diff --git a/lib/single_sign_on.rb b/lib/single_sign_on.rb index 1df3240071..bed24b53b1 100644 --- a/lib/single_sign_on.rb +++ b/lib/single_sign_on.rb @@ -2,6 +2,7 @@ class SingleSignOn ACCESSORS = [:nonce, :name, :username, :email, :avatar_url, :avatar_force_update, :about_me, :external_id, :return_sso_url, :admin, :moderator] FIXNUMS = [] + BOOLS = [:avatar_force_update, :admin, :moderator] NONCE_EXPIRY_TIME = 10.minutes attr_accessor(*ACCESSORS) @@ -30,6 +31,9 @@ class SingleSignOn ACCESSORS.each do |k| val = decoded_hash[k.to_s] val = val.to_i if FIXNUMS.include? k + if BOOLS.include? k + val = ["true", "false"].include?(val) ? val == "true" : nil + end sso.send("#{k}=", val) end @@ -77,7 +81,7 @@ class SingleSignOn def unsigned_payload payload = {} ACCESSORS.each do |k| - next unless (val = send k) + next if (val = send k) == nil payload[k] = val end diff --git a/spec/controllers/session_controller_spec.rb b/spec/controllers/session_controller_spec.rb index 7eca71f3bb..fac96a16aa 100644 --- a/spec/controllers/session_controller_spec.rb +++ b/spec/controllers/session_controller_spec.rb @@ -65,6 +65,23 @@ describe SessionController do logged_on_user.single_sign_on_record.external_username.should == 'sam' end + it 'allows you to create an admin account' do + sso = get_sso('/a/') + sso.external_id = '666' # the number of the beast + sso.email = 'bob@bob.com' + sso.name = 'Sam Saffron' + sso.username = 'sam' + sso.custom_fields["shop_url"] = "http://my_shop.com" + sso.custom_fields["shop_name"] = "Sam" + sso.admin = true + + get :sso_login, Rack::Utils.parse_query(sso.payload) + + logged_on_user = Discourse.current_user_provider.new(request.env).current_user + logged_on_user.admin.should == true + + end + it 'allows you to create an account' do sso = get_sso('/a/') sso.external_id = '666' # the number of the beast @@ -82,6 +99,7 @@ describe SessionController do # ensure nothing is transient logged_on_user = User.find(logged_on_user.id) + logged_on_user.admin.should == false logged_on_user.email.should == 'bob@bob.com' logged_on_user.name.should == 'Sam Saffron' logged_on_user.username.should == 'sam' @@ -132,7 +150,7 @@ describe SessionController do response.should redirect_to("/login") - user = Fabricate(:user, password: "frogs", active: true) + user = Fabricate(:user, password: "frogs", active: true, admin: true) EmailToken.update_all(confirmed: true) xhr :post, :create, login: user.username, password: "frogs", format: :json @@ -147,7 +165,9 @@ describe SessionController do sso2.email.should == user.email sso2.name.should == user.name sso2.username.should == user.username - sso2.external_id == user.id.to_s + sso2.external_id.should == user.id.to_s + sso2.admin.should == true + sso2.moderator.should == false end From c3a47aee70906500a1a303b1b72377c04f43d50c Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Wed, 26 Nov 2014 23:57:14 -0800 Subject: [PATCH 094/991] use shorter Export button copy --- app/assets/javascripts/admin/templates/logs.hbs | 2 +- app/assets/javascripts/admin/templates/users_list.hbs | 2 +- config/locales/client.en.yml | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/admin/templates/logs.hbs b/app/assets/javascripts/admin/templates/logs.hbs index 35321dd463..483381353c 100644 --- a/app/assets/javascripts/admin/templates/logs.hbs +++ b/app/assets/javascripts/admin/templates/logs.hbs @@ -9,7 +9,7 @@
    - +
    diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index 36ae00e22a..6cdf5c5ddf 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -13,7 +13,7 @@
    - +
    diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index a22f8aab1c..1da4d87090 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1684,13 +1684,12 @@ en: export_csv: users: - text: "Export Users" - title: "Export user list in a CSV file." + title: "Export full user list in CSV format." screened_ips: - text: "Export Screened IPs" - title: "Export screened IPs list in a CSV file." - success: "Export has been initiated, you will be notified shortly with progress." + title: "Export full screened IPs list in CSV format." + success: "Export initiated, you will be notified shortly with progress." failed: "Export failed. Please check the logs." + button_text: "Export" customize: title: "Customize" From 4589f28676f685a9fde2a8fbeb82f97dc0d5dc69 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 27 Nov 2014 02:13:15 -0800 Subject: [PATCH 095/991] remove unwanted global text input font sizing --- app/assets/stylesheets/desktop/topic-post.scss | 6 ------ app/assets/stylesheets/mobile/topic-post.scss | 8 -------- 2 files changed, 14 deletions(-) diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index c2f82954e5..f0875d0f06 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -610,12 +610,6 @@ a.mention { } -.modal-body { - input[type=text] { - font-size: 16px; - } -} - .moderator { .topic-body { background-color: dark-light-diff($highlight, $secondary, 70%, -80%); diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 068018ff97..5fbdddf577 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -382,14 +382,6 @@ iframe { position: relative; } -.modal-body { - input[type=text] { - font-size: 16px; - display: block; - margin-top: 10px; - } -} - #share-link { width: 290px; } From 31b769d4fa8a129e710425868b962ab3ded05b76 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 27 Nov 2014 03:35:05 -0800 Subject: [PATCH 096/991] remove unneeded mobile footer left margin --- app/assets/stylesheets/common/base/modal.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index 2688a18475..0d82357416 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -71,7 +71,6 @@ margin-bottom: 0; } .modal-footer { - margin-left: 15px; padding: 14px 15px 15px; border-top: 1px solid scale-color-diff(); } From cd8ab37507c96b024a54da203a22be244c4ae593 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Thu, 27 Nov 2014 20:28:52 +0530 Subject: [PATCH 097/991] Update Translations --- config/locales/client.cs.yml | 178 ++++++++++++++++++- config/locales/client.da.yml | 11 +- config/locales/client.de.yml | 48 ++++-- config/locales/client.es.yml | 32 +++- config/locales/client.fi.yml | 6 - config/locales/client.fr.yml | 43 ++++- config/locales/client.he.yml | 97 +++++++---- config/locales/client.it.yml | 12 +- config/locales/client.ja.yml | 6 - config/locales/client.ko.yml | 6 - config/locales/client.nb_NO.yml | 20 ++- config/locales/client.nl.yml | 6 - config/locales/client.pl_PL.yml | 30 +++- config/locales/client.pt.yml | 6 - config/locales/client.pt_BR.yml | 106 +++++++++--- config/locales/client.ru.yml | 31 +++- config/locales/client.sq.yml | 6 - config/locales/client.sv.yml | 6 - config/locales/client.uk.yml | 2 - config/locales/client.zh_CN.yml | 30 +++- config/locales/client.zh_TW.yml | 6 - config/locales/server.cs.yml | 296 ++++++++++++++++++++++++++++++++ config/locales/server.de.yml | 103 ++++++++--- config/locales/server.es.yml | 42 +++++ config/locales/server.fr.yml | 34 ++++ config/locales/server.ja.yml | 29 ++++ config/locales/server.pt_BR.yml | 22 +++ config/locales/server.ru.yml | 70 +++++++- config/locales/server.zh_CN.yml | 3 + 29 files changed, 1088 insertions(+), 199 deletions(-) diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index a49d1f90d0..31592740bc 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -106,6 +106,7 @@ cs: facebook: 'sdílet odkaz na Facebooku' google+: 'sdílet odkaz na Google+' email: 'odeslat odkaz emailem' + topic_admin_menu: "akce administrátora tématu" edit: 'upravit název a kategorii příspěvku' not_implemented: "Tato fičura ještě není implementovaná" no_value: "Ne" @@ -267,6 +268,13 @@ cs: location_not_found: (neznámá) organisation: Organizace phone: Telefon + other_accounts: "Další účty s touto IP adresou:" + username: "uživatelské jméno" + trust_level: "Důvěra" + read_time: "čas k přečtení" + topics_entered: "témat zadáno" + post_count: "počet příspěvků" + confirm_delete_other_accounts: "Určitě chcete smazat tyto účty?" user: said: "{{username}}:" profile: "Profil" @@ -283,6 +291,7 @@ cs: trust_level: "Důvěryhodnost" notifications: "Oznámení" dismiss_notifications: "Označ vše jako přečtené" + dismiss_notifications_tooltip: "Označit všechny nepřečtené notifikace jako přečtené" disable_jump_reply: "Po odpovědi nepřeskakovat na nový příspěvek" dynamic_favicon: "Zobrazovat notifikace na favikoně" edit_history_public: "Povolit ostatním zobrazení všechn verzí mého příspěvku" @@ -295,6 +304,7 @@ cs: admin_tooltip: "Tento uživatel je admi" suspended_notice: "Uživatel je suspendován do {{date}}." suspended_reason: "Důvod: " + github_profile: "Github" mailing_list_mode: "Obdržet email za každý nový příspěvek (pokud si neztišíte téma nebo kateogii)" watched_categories: "Hlídané" watched_categories_instructions: "Nová témata v těchto kategoriích budou hlídaná. Na všechny nové příspěvky budete upozorněni." @@ -307,6 +317,7 @@ cs: deleted_yourself: "Váš účet byl úspěšně odstraněn." delete_yourself_not_allowed: "Váš účet teď nejde odstranit. Obraťte se na správce aby váš účet smazal za vás." unread_message_count: "Zprávy" + admin_delete: "Smazat" staff_counters: flags_given: "užitečná nahlášení" flagged_posts: "nahlášených příspěvků" @@ -350,14 +361,28 @@ cs: title: "Pozadí profilu" email: title: "Emailová adresa" + ok: "Pro potvrzení vám pošleme email." + invalid: "Zadejte prosím správnou emailovou adresu" + authenticated: "Vaše emailová adresa byla autorizována přes službu {{provider}}." frequency: "Budeme vás informovat emailem pouze pokud jste se na našem webu dlouho neukázali a pokud jste obsah, o kterém vás chceme informovat, doposud neviděli." name: title: "Jméno" + instructions: "Celé jméno (volitelně)" + too_short: "Máte moc krátké jméno" + ok: "Parádní jméno" username: title: "Uživatelské jméno" + instructions: "Unikátní, bez mezer, radši kratší" + short_instructions: "Lidé vás mohou zmínit pomocí @{{username}}" + available: "Vaše uživatelské jméno je volné" + global_match: "Email odpovídá registrovanému uživatelskému jménu." global_mismatch: "již zaregistrováno. Co třeba {{suggestion}}?" not_available: "Není k dispozici. Co třeba {{suggestion}}?" + too_short: "Uživatelské jméno je moc krátké" + too_long: "Uživatelské jméno je moc dlouhé" checking: "Zjišťuji, zda je uživatelské jméno volné..." + enter_email: 'Uživatelské jméno nalezeno; vyplňte propojený email' + prefilled: "Email je propojen s tímto uživatelským jménem" locale: title: "Jazyk rozhraní" instructions: "Jazyk uživatelského prostředí. Změna obnoví stránku." @@ -423,6 +448,7 @@ cs: rescind: "Smazat" rescinded: "Pozvánka odstraněna" reinvite: "Znovu poslat pozvánku" + reinvited: "Pozvánka byla opětovně odeslána." time_read: "Čas čtení" days_visited: "Přítomen dnů" account_age_days: "Stáří účtu ve dnech" @@ -475,6 +501,8 @@ cs: fixed: "Nahrávám" close: "Zavřít" assets_changed_confirm: "Tento web se právě aktualizoval. Chcete obnovit stránku a mít nejnovější verzi?" + logout: "Byli jste odhlášeni." + refresh: "Obnovit" read_only_mode: enabled: "Správce fóra zapnul režim jen pro čtení. Můžete pokračovat v procházení webu, ale interakce nemusí fungovat správně." login_disabled: "Přihlášení je zakázáno jelikož fórum je v režimu jen pro čtení." @@ -513,6 +541,7 @@ cs: created: 'Vytvořeno' created_lowercase: 'vytvořeno' trust_level: 'Důvěryhodnost' + search_hint: 'uživatelské jméno, email nebo IP adresa' create_account: title: "Vytvořit nový účet" failed: "Něco se nepovedlo, možná je tato e-mailová adresa již použita. Zkuste použít formulář pro obnovení hesla." @@ -587,6 +616,8 @@ cs: reply_here: "Odpovědět sem" reply: "Odpovědět" cancel: "Zrušit" + create_topic: "Nové Téma" + create_pm: "Soukromá zpráva" title: "Nebo zmáčkněte Ctrl+Enter" users_placeholder: "Přidat uživatele" title_placeholder: "O čem je ve zkratce tato diskuze?" @@ -657,7 +688,10 @@ cs: title_with_attachments: "Nahrát obrázek nebo soubor" from_my_computer: "Z mého zařízení" from_the_web: "Z webu" + remote_tip: "odkaz na obrázek" + remote_tip_with_attachments: "odkaz na obrázek nebo osubor ({{authorized_extensions}})" local_tip: "klikněte sem pro výběr obrázku z vašeho zařízení." + local_tip_with_attachments: "klikněte pro vybrání obrázku nebo souboru z vašeho zařízení ({{authorized_extensions}})" hint: "(můžete také rovnou soubor do editoru přetáhnout)" hint_for_supported_browsers: "(můžete také rovnou obrázky do editoru přetáhnout)" uploading: "Nahrávám" @@ -819,13 +853,16 @@ cs: description: "Budete informováni o každém novém příspěvku v tomto tématu. Vedle názvu tématu se objeví počet nepřečtených a nových příspěvků." tracking_pm: title: "Sledování" + description: "U soukromých zpráv se zobrazí počet nových a nepřečtených příspěvků. Budete uporozněni když vás někdo zmíní jako @jmeno nebo odpoví na váš příspěvek." tracking: title: "Sledované" + description: "U tématu se zobrazí počet nepřečtených a nových příspěvků. Budete uporozněni když vás někdo zmíní jako @jmeno nebo odpoví na váš příspěvek." regular: title: "Klasicky" description: "Budete informováni pokud někdo zmíní vaše @jméno nebo odpoví na váš příspěvek." regular_pm: title: "Normální" + description: "Budete uporozněni když vás někdo zmíní jako @jmeno nebo odpoví na váš příspěvek v soukromé zprávě." muted_pm: title: "Ztišení" description: "Nikdy nedostanete oznámení týkající se čehokoliv o této soukromé zprávě." @@ -962,6 +999,7 @@ cs: edit: "Bohužel nastala chyba při editaci příspěvku. Prosím zkuste to znovu." upload: "Bohužel nastala chyba při nahrávání příspěvku. Prosím zkuste to znovu." attachment_too_large: "Soubor, který se snažíte nahrát je bohužel příliš velký (maximální velikost je {{max_size_kb}}kb). Prosím zmenšete ho zkuste to znovu." + file_too_large: "Omlouváme se, ale soubor, který se snažíte nahrát, je příliš veliký (maximální velikost je {{max_size_kb}}kb)" too_many_uploads: "Bohužel, najednou smíte nahrát jen jeden soubor." upload_not_authorized: "Bohužel, soubor, který se snažíte nahrát, není povolený (povolené přípony: {{authorized_extensions}})." image_upload_not_allowed_for_new_user: "Bohužel, noví uživatelé nemohou nahrávat obrázky." @@ -1004,6 +1042,10 @@ cs: unhide: "Odkrýt" actions: flag: 'Nahlásit' + defer_flags: + one: "Odložit nahlášení" + few: "Odložit nahlášení" + other: "Odložit nahlášení" it_too: off_topic: "Také nahlásit" spam: "Také nahlásit" @@ -1130,6 +1172,9 @@ cs: side_by_side: title: "Rozdíli mezi vykreslenými příspěveky vedle sebe" button: ' HTML' + side_by_side_markdown: + title: "Show the raw source diffs side-by-side" + button: ' Kód' details: edited_by: "Upravil" category: @@ -1186,6 +1231,8 @@ cs: title: "Ztišený" description: "Nebudete upozorněni na žádná nová témata v těchto kategoriích a ani se nebudou zobrazovat jako nepřečtené." flagging: + title: 'Děkujeme, že pomáháte udržovat komunitu zdvořilou!' + private_reminder: 'nahlášení jsou soukromá, viditelná pouze pro správce' action: 'Nahlásit příspěvek' take_action: "Zakročit" notify_action: 'Private message' @@ -1194,12 +1241,19 @@ cs: yes_delete_spammer: "Ano, odstranit spamera" ip_address_missing: "(N/A)" hidden_email_address: "(skrytý)" + submit_tooltip: "Podat soukromé nahlášení" cant: "Bohužel nyní nemůžete tento příspěvek nahlásit." + formatted_name: + off_topic: "Je to mimo téma." + inappropriate: "Je to nevhodné" + spam: "Je to spam" + custom_placeholder_notify_user: "Buďte věcný, konstruktivní a vždy zdvořilý." custom_message: at_least: "zadejte alespoň {{n}} znaků" more: "ještě {{n}}..." left: "{{n}} zbývá" flagging_topic: + title: "Děkujeme, že pomáháte udržovat komunitu zdvořilou!" action: "Nahlásit téma" notify_action: "Private message" topic_map: @@ -1248,6 +1302,9 @@ cs: category_title: "Kategorie" history: "Historie" changed_by: "od uživatele {{author}}" + raw_email: + title: "Neupravený email" + not_available: "Není k dispozici!" categories_list: "Seznam kategorií" filters: with_topics: "%{filter} témata" @@ -1353,6 +1410,11 @@ cs: 7_days_ago: "Týden" 30_days_ago: "Měsíc" all: "Celkem" + view_table: "tabulka" + view_chart: "sloupcový graf" + refresh_report: "Obnovit hlášení" + start_date: "Datum začátku" + end_date: "Datum konce" commits: latest_changes: "Poslední změny:" by: "od" @@ -1364,18 +1426,24 @@ cs: agree_title: "Potvrdit toto hlášení jako právoplatné a korektní" agree_flag_modal_title: "Schvaluji a..." agree_flag_hide_post: "Schvaluji (skrýt příspěvek + poslat soukromou zprávu)" + agree_flag_hide_post_title: "Skrýt tento příspěvek a automaticky odeslat soukromou zprávu, která uživatele žádá o editaci" agree_flag_restore_post: "Schvaluji (obnovit příspěvek)" agree_flag_restore_post_title: "Obnovit tento příspěvek" agree_flag: "Souhlasit s hlášením" agree_flag_title: "Schválit hlášení a nechat příspěvek nezměněný" + defer_flag: "Odložit" + defer_flag_title: "Odstranit nahlášení; teď nevyžaduje žádné opatření." delete: "Smazat" delete_title: "Smazat příspěvek, na který toto hlášení odkazuje." + delete_post_defer_flag: "Smazat příspěvek a odložit nahlášení" delete_post_defer_flag_title: "Smazat příspěvek; pokud je to první příspěvek, tak smazat téma" delete_post_agree_flag: "Smazat příspěvek a Schválit hlášení" delete_post_agree_flag_title: "Smazat příspěvek; pokud je to první příspěvek, tak smazat téma" delete_flag_modal_title: "Smazat a..." + delete_spammer: "Odstranit spamera" delete_spammer_title: "Odstranit uživatele a všechny příspěvky a témata tohoto uživatele." disagree_flag_unhide_post: "Neschvaluji (zviditelnit příspěvek)" + disagree_flag_unhide_post_title: "Odstranit všechna nahlášení u tohoto příspěvku a znovu ho zviditelnit" disagree_flag: "Neschvaluji" disagree_flag_title: "Odmítnout hlášení jako neprávoplatné a nekorektní" clear_topic_flags: "Hotovo" @@ -1384,13 +1452,18 @@ cs: dispositions: agreed: "schváleno" disagreed: "neschváleno" + deferred: "odloženo" flagged_by: "Nahlásil" resolved_by: "Vyřešeno" + took_action: "Zakročit" system: "Systémové soukromé zprávy" error: "Něco se pokazilo" reply_message: "Odpovědět" no_results: "Nejsou zde žádná nahlášení." topic_flagged: "Tohle téma bylo označeno." + visit_topic: "Zobrazit téma pro přijmutí opatření." + was_edited: "Příspěvek byl upraven po prvním nahlášení" + previous_flags_count: "Tento příspěvek byl již nahlášen {{count}} krát." summary: action_type_3: one: "off-topic" @@ -1490,13 +1563,13 @@ cs: title: "Rollback the database to previous working state" confirm: "Are your sure you want to rollback the database to the previous working state?" export_csv: - users: - text: "Exportovat uživatele" - title: "Exportovat uživatele jako CSV soubor." + failed: "Exportování selhalo. Prosím zkontrolujte logy." customize: title: "Přizpůsobení" long_title: "Přizpůsobení webu" + css: "CSS" header: "header" + footer: "Patička" override_default: "Přetížit výchozí?" enabled: "Zapnuto?" preview: "náhled" @@ -1535,6 +1608,7 @@ cs: settings: "Nastavení" all: "Všechny emaily" sending_test: "Zkušební email se odesílá..." + error: "CHYBA - %{server_error}" sent: "Odeslané" skipped: "Přeskočené" sent_at: "Odesláno" @@ -1634,12 +1708,11 @@ cs: last_emailed: "Email naposledy zaslán" not_found: "Bohužel uživatel s tímto jménem není v našem systému." active: "Aktivní" + show_emails: "Ukázat emailové adresy" nav: new: "Noví" active: "Aktivní" pending: "Čeká na schválení" - admins: 'Administrátoři' - moderators: 'Moderátoři' suspended: 'Zakázaní' blocked: 'Blokovaní' approved: "Schválen?" @@ -1674,6 +1747,7 @@ cs: other: "Failed to reject %{count} users." not_verified: "Neověřeno" check_email: + title: "Odhal emailovou adresu tohoto uživatele" text: "Zobrazit" user: suspend_failed: "Nastala chyba při zakazování uživatele {{error}}" @@ -1695,8 +1769,10 @@ cs: edit_title: "Upravit titul" save_title: "Uložit nadpis" refresh_browsers: "Vynutit obnovení prohlížeče" + refresh_browsers_message: "Zpráva odeslána všem klientům!" show_public_profile: "Zobrazit veřejný profil" impersonate: 'Vydávat se za uživatele' + ip_lookup: "Vyhledávání IP adresy" log_out: "Odhlásit se" logged_out: "Uživatel byl odhlášen na všech zařízeních." revoke_admin: 'Odebrat administrátorská práva' @@ -1744,6 +1820,8 @@ cs: suspend_modal_title: "Suspend User" trust_level_2_users: "Uživatelé důvěryhodnosti 2" trust_level_3_requirements: "Požadavky pro důvěryhodnost 3" + lock_trust_level: "Zamknout úroveň důvěryhodnosti" + unlock_trust_level: "Odemknout úroveň důvěryhodnosti" tl3_requirements: title: "Požadavky pro důvěryhodnost 3" table_title: "Za posledních 100 dní:" @@ -1752,8 +1830,15 @@ cs: visits: "Návštěv" days: "dní" topics_replied_to: "Odpovědí na témata" + topics_viewed: "Zobrazeno témat" + topics_viewed_all_time: "Zobrazeno témat (od počátku věků)" + posts_read: "Přečteno příspěvků" + posts_read_all_time: "Přečteno příspěvků (od počátku věků)" flagged_posts: "Nahlášené příspěvky" + likes_given: "Rozdaných 'líbí se'" + likes_received: "Obdržených 'líbí se'" sso: + external_id: "Externí ID" external_username: "Uživatelské jméno" external_name: "Jméno" external_email: "Email" @@ -1763,6 +1848,14 @@ cs: edit: "Upravit" delete: "Smazat" cancel: "Zrušit" + required: + title: "Povinné pro registraci?" + enabled: "povinné" + disabled: "není povinné" + editable: + title: "Editovatelné po registraci?" + enabled: "editovatelné" + disabled: "není editovatelné" field_types: confirm: 'Potvrzení' site_settings: @@ -1816,11 +1909,15 @@ cs: no_badges: Nejsou tu žádné odznaky, které by se dali rozdat. multiple_grant: Může být přiděleno několikrát enabled: Povolit odznaky + icon: Ikona + image: Obrázek preview: bad_count_warning: header: "VAROVÁNÍ!" lightbox: download: "download" + search_help: + title: 'Vyhledat v nápovědě' keyboard_shortcuts_help: title: 'Klávesové zkratky' jump_to: @@ -1853,6 +1950,7 @@ cs: actions: title: 'Actions' star: 'f Star topic' + pin_unpin_topic: 'shift p Připnout/Odepnout téma' share_topic: 'shift s Sdílet téma' share_post: 's Share post' reply_as_new_topic: 't Odpovědět v propojeném tématu' @@ -1885,14 +1983,84 @@ cs: select_badge_for_title: Vyberte odznak, který chcete použít jako svůj titul none: "" badge_grouping: + community: + name: Komunita + trust_level: + name: Věrohodnost other: name: Ostatní + posting: + name: Přispívání badge: + editor: + name: Editor basic_user: name: Základní member: name: Člen + regular: + name: Normální leader: name: Vůdce + welcome: + name: Vítejte + description: Obdrženo líbí se mi + autobiographer: + name: Autor vlastního životopisu + nice_post: + name: Zdařilý příspěvek + description: Obdrženo 10 líbí se na příspěvku. Tento odznak můžete získat opakovaně + good_post: + name: Dobrý příspěvek + description: Obdrženo 25 líbí se na příspěvku. Tento odznak můžete získat opakovaně + great_post: + name: Výborný příspěvek + description: Obdrženo 50 líbí se na příspěvku. Tento odznak můžete získat opakovaně + nice_topic: + name: Zdařilé téma + description: Obdrženo 10 líbí se v tématu. Tento odznak můžete získat opakovaně + good_topic: + name: Dobré téma + description: Obdrženo 25 líbí se v tématu. Tento odznak můžete získat opakovaně + great_topic: + name: Výborné téma + description: Obdrženo 50 líbí se v tématu. Tento odznak můžete získat opakovaně + nice_share: + name: Zdařilé sdílení + description: Sdílení příspěvku s 25 unikátními návštěvníky + good_share: + name: Dobré sdílení + description: Sdílení příspěvku s 300 unikátními návštěvníky + great_share: + name: Výborné sdílení + description: Sdílení příspěvku s 1000 unikátními návštěvníky + first_like: + name: První lajk + description: Líbil se příspěvek + first_flag: + name: První nahlášení + description: Nahlášen příspěvek + first_share: + name: První sdílení + description: Sdílený příspěvek + first_link: + name: První odkaz + description: Vložen interní odkaz na jiné téma + first_quote: + name: První citace + description: Citovat uživatele + read_guidelines: + name: Přečíst pokyny + description: Přečíst komunitní pokyny reader: name: Čtenář + description: Přečíst každý příspěvek v tématu s více než 100 příspěvky + google_search: | +

    Vyhledání v Google

    +

    +

    +

    diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index 80b608b2cd..3f4a927140 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -599,8 +599,13 @@ da: toggler: "skjul eller vis editor-panelet" admin_options_title: "Valgfrie staff-indstillinger for dette emne" auto_close: + label: "Tidspunkt for automatisk lukning af emne:" error: "Indtast venligst en gyldig værdi" + based_on_last_post: "Luk ikke før det seneste indlæg i emnet er mindst så gammel." + all: + examples: 'Indtast et antal timer (24), et bestemt tidspunkt (17:30) eller et tidsstempel (2013-11-22 14:00).' limited: + units: "(# timer)" examples: 'Indtast antal timer (24).' notifications: title: "notifikation ved @navns nævnelse, svar til dine indlæg og emner, private beskeder, mv." @@ -1427,10 +1432,6 @@ da: title: "Rul databasen tilbage til et tidspunkt hvor tingene virkede" confirm: "Er du sikker på du vil rulle databasen tilbage?" export_csv: - users: - text: "Eksporter Brugere" - title: "Eksporter brugerliste i en CSV-fil" - success: "Eksport er påbegyndt, du får snart besked om hvordan det skrider frem." failed: "Eksport fejlede. Tjek venligst loggen." customize: title: "Tilpasning" @@ -1619,8 +1620,6 @@ da: new: "Ny" active: "Aktiv" pending: "Afventer" - admins: 'Admins' - moderators: 'Mods' suspended: 'Suspenderet' blocked: 'Blokeret' approved: "Godkendt?" diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index 3bf5395b1b..ab744d3d8a 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -144,7 +144,7 @@ de: post_count: "Beiträge" user_count: "Benutzer" bookmarks: - not_logged_in: "Entschuldigung, du musst eingeloggt sein, um ein Lesezeichen zu setzen." + not_logged_in: "Entschuldigung, du musst angemeldet sein, um ein Lesezeichen zu setzen." created: "du hast ein Lesezeichen zu diesem Beitrag hinzugefügt" not_bookmarked: "Du hast diesen Beitrag gelesen. Klicke, um ein Lesezeichen zu setzen." last_read: "Das ist der letzte Beitrag, den du gelesen hast. Klicke, um ein Lesezeichen zu setzen." @@ -247,6 +247,13 @@ de: organisation: Organisation phone: Telefon other_accounts: "Andere Konten mit dieser IP-Adresse:" + delete_other_accounts: "%{count} löschen" + username: "Benutzername" + trust_level: "VS" + read_time: "Lesezeit" + topics_entered: "betrachtete Themen" + post_count: "# Beiträge" + confirm_delete_other_accounts: "Bist du sicher, dass du diese Konten löschen willst?" user: said: "{{username}}:" profile: "Profil" @@ -337,14 +344,29 @@ de: instructions: "Hintergrundbilder werden zentriert und haben eine Standardbreite von 590px." email: title: "E-Mail" + instructions: "Wird niemals öffentlich angezeigt" + ok: "Wir senden dir zur Bestätigung eine E-Mail" + invalid: "Bitte gib eine gültige E-Mail-Adresse ein" + authenticated: "Deine E-Mail-Adresse wurde von {{provider}} bestätigt" frequency: "Wir werden dir nur E-Mails senden, wenn wir dich kürzlich nicht gesehen haben und wenn du die betroffenen Inhalte noch nicht gesehen hast." name: title: "Name" + instructions: "Dein vollständiger Name (optional)" + too_short: "Dein Name ist zu kurz" + ok: "Dein Name sieht in Ordnung aus" username: title: "Benutzername" + instructions: "Eindeutig, keine Leerzeichen, kurz" + short_instructions: "Leute können dich mit @{{username}} erwähnen" + available: "Dein Benutzername ist verfügbar" + global_match: "E-Mail-Adresse entspricht dem registrierten Benutzernamen" global_mismatch: "Bereits registriert. Wie wäre es mit {{suggestion}}?" not_available: "Nicht verfügbar. Wie wäre es mit {{suggestion}}?" + too_short: "Dein Benutzername ist zu kurz" + too_long: "Dein Benutzername ist zu lang" checking: "Verfügbarkeit wird geprüft..." + enter_email: 'Benutzername gefunden; gib die zugehörige E-Mail-Adresse ein' + prefilled: "E-Mail-Adresse entspricht diesem registrierten Benutzernamen" locale: title: "Oberflächensprache" instructions: "Die Sprache der Forumsoberfläche. Diese Änderung tritt nach dem Neuladen der Seite in Kraft." @@ -421,7 +443,7 @@ de: title: "Passwort" too_short: "Dein Passwort ist zu kurz." common: "Das Passwort wird zu häufig verwendet." - ok: "Dein Passwort sieht gut aus." + ok: "Dein Passwort sieht in Ordnung aus." instructions: "Mindestens %{count} Zeichen." associated_accounts: "Zugehörige Konten" ip_address: @@ -459,6 +481,8 @@ de: fixed: "Seite laden" close: "Schließen" assets_changed_confirm: "Diese Website wurde gerade aktualisiert. Neu laden für die neuste Version?" + logout: "Du wurdest abgemeldet." + refresh: "Aktualisieren" read_only_mode: enabled: "Ein Administrator hat den Nur-Lesen-Modus aktiviert. Du kannst die Website weiter durchsuchen und lesen. Einige Funktionen werden jedoch wahrscheinlich nicht funktionieren." login_disabled: "Die Anmeldung ist deaktiviert während sich die Website im Nur-Lesen-Modus befindet." @@ -527,7 +551,7 @@ de: awaiting_confirmation: "Dein Konto ist noch nicht aktiviert. Verwende den 'Passwort vergessen'-Link, um eine weitere E-Mail mit Anweisungen zur Aktivierung zu erhalten." awaiting_approval: "Dein Konto wurde noch nicht von einem Mitarbeiter genehmigt. Du bekommst eine E-Mail, sobald das geschehen ist." requires_invite: "Entschuldige, der Zugriff auf dieses Forum ist nur mit einer Einladung möglich." - not_activated: "Du kannst dich noch nicht einloggen. Wir haben dir schon eine E-Mail zur Aktivierung an {{sentTo}} geschickt. Bitte folge den Anweisungen in dieser E-Mail, um dein Benutzerkonto zu aktivieren." + not_activated: "Du kannst dich noch nicht anmelden. Wir haben dir schon eine E-Mail zur Aktivierung an {{sentTo}} geschickt. Bitte folge den Anweisungen in dieser E-Mail, um dein Benutzerkonto zu aktivieren." not_allowed_from_ip_address: "Du kannst dich mit dieser IP-Adresse nicht anmelden." resend_activation_email: "Klicke hier um eine neue Aktivierungsmail zu schicken." sent_activation_email_again: "Wir haben dir eine weitere E-Mail zur Aktivierung an {{currentEmail}} geschickt. Es könnte ein paar Minuten dauern, bis diese ankommt; sieh auch im Spam-Ordner nach." @@ -947,7 +971,7 @@ de: upload_not_authorized: "Entschuldige, die Datei, die du hochladen wolltest, ist nicht erlaubt (erlaubte Endungen: {{authorized_extensions}})." image_upload_not_allowed_for_new_user: "Entschuldige, neue Benutzer dürfen keine Bilder hochladen." attachment_upload_not_allowed_for_new_user: "Entschuldige, neue Benutzer dürfen keine Dateien hochladen." - attachment_download_requires_login: "Du musst eingeloggt sein, um Dateien herunterladen zu können." + attachment_download_requires_login: "Du musst angemeldet sein, um Dateien herunterladen zu können." abandon: confirm: "Möchtest du deinen Beitrag wirklich verwerfen?" no_value: "Nein, beibehalten" @@ -1305,9 +1329,9 @@ de: other_periods: "zeige weitere angesagte Themen" browser_update: 'Dein Webbrowser ist leider zu alt, um dieses Forum zu besuchen. Bitte installiere einen neueren Browser.' permission_types: - full: "Erstellen / Antworten / Anschauen" - create_post: "Antworten / Anschauen" - readonly: "Anschauen" + full: "Erstellen / Antworten / Ansehen" + create_post: "Antworten / Ansehen" + readonly: "Ansehen" admin_js: type_to_filter: "zum Filtern hier eingeben..." admin: @@ -1496,11 +1520,8 @@ de: title: "Die Datenbank auf den letzten funktionierenden Zustand zurücksetzen" confirm: "Möchtest du wirklich die Datenbank auf den letzten funktionierenden Stand zurücksetzen?" export_csv: - users: - text: "Exportieren" - title: "Exportiere eine Benutzerliste als CSV-Datei." - success: "Der Export wurde gestartet. Du wirst in Kürze über den Fortschritt benachrichtigt." failed: "Der Export ist fehlgeschlagen. Bitte überprüfe die Logs." + button_text: "Exportieren" customize: title: "Anpassen" long_title: "Website-Anpassungen" @@ -1576,6 +1597,7 @@ de: settings: "Einstellungen" all: "Alle" sending_test: "Versende Test-E-Mail..." + error: "FEHLER - %{server_error}" test_error: "Es gab ein Problem beim Senden der Test-E-Mail. Bitte überprüfe nochmals deine E-Mail-Einstellungen, stelle sicher dass dein Anbieter keine E-Mail-Verbindungen blockiert und probiere es erneut." sent: "Gesendet" skipped: "Übersprungen" @@ -1691,8 +1713,6 @@ de: new: "Neu" active: "Aktiv" pending: "Genehmigung" - admins: 'Administratoren' - moderators: 'Moderatoren' suspended: 'Gesperrt' blocked: 'Blockiert' approved: "Genehmigt?" @@ -1801,7 +1821,7 @@ de: unblock_failed: 'Beim Aufheben der Blockierung des Benutzers ist ein Fehler aufgetreten.' block_failed: 'Beim Blocken des Benutzers ist ein Fehler aufgetreten.' deactivate_explanation: "Ein deaktivierter Benutzer muss seine E-Mail-Adresse erneut bestätigen." - suspended_explanation: "Ein gesperrter Benutzer kann sich nicht einloggen." + suspended_explanation: "Ein gesperrter Benutzer kann sich nicht anmelden." block_explanation: "Ein geblockter Benutzer kann keine Themen erstellen oder Beiträge veröffentlichen." trust_level_change_failed: "Beim Wechsel der Vertrauensstufe ist ein Fehler aufgetreten." suspend_modal_title: "Benutzer sperren" diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index 19d81539c6..9f21f7d951 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -247,6 +247,12 @@ es: organisation: Organización phone: Teléfono other_accounts: "Otras cuentas con esta dirección IP:" + username: "usuario" + trust_level: "NC" + read_time: "tiempo de lectura" + topics_entered: "temas vistos" + post_count: "# posts" + confirm_delete_other_accounts: "¿Seguro que quieres eliminar estas cuentas?" user: said: "{{username}}:" profile: "Perfil" @@ -337,14 +343,29 @@ es: instructions: "Imágenes de fondo serán centrados y tendrán un ancho por default de 590px." email: title: "E-mail" + instructions: "Nunca se mostrará públicamente" + ok: "Te enviaremos un email para confirmar" + invalid: "Por favor, introduce una dirección de correo válida" + authenticated: "Tu dirección de correo ha sido autenticada por {{provider}}" frequency: "Sólo te enviaremos e-mails si no te hemos visto recientemente y todavía no has visto lo que te estamos enviando." name: title: "Nombre" + instructions: "Tu nombre completo (opcional)" + too_short: "Tu nombre es demasiado corto" + ok: "Tu nombre es válido" username: title: "Nombre de usuario" + instructions: "Debe ser único, sin espacios y conciso" + short_instructions: "Los demás usuarios pueden mencionarte como @{{username}}" + available: "Tu nombre de usuario está disponible" + global_match: "La dirección coincide con la del nombre de usuario registrado" global_mismatch: "Ya está registrado. ¿Prueba {{suggestion}}?" not_available: "No disponible. ¿Prueba {{suggestion}}?" + too_short: "Tu nombre de usuario es demasiado corto" + too_long: "Tu nombre de usuario es demasiado largo" checking: "Comprobando la disponibilidad del nombre de usuario..." + enter_email: 'Nombre de usuario encontrado; introduce la dirección de correo correspondiente' + prefilled: "El email coincide con el nombre de usuario registrado" locale: title: "Idioma de la interfaz" instructions: "El idioma de la interfaz. Cambiará cuando recargues la página." @@ -459,6 +480,8 @@ es: fixed: "Cargar Página" close: "Cerrar" assets_changed_confirm: "Este sitio acaba de ser actualizado justo ahora. ¿Quieres recargar la página para ver la última versión?" + logout: "Has cerrado sesión." + refresh: "Actualizar" read_only_mode: enabled: "Un administrador ha activado el modo sólo-lectura. Puedes continuar navegando por el sitio pero las interacciones podrían no funcionar." login_disabled: "Iniciar sesión está desactivado mientras el foro esté en modo solo lectura." @@ -1494,10 +1517,6 @@ es: title: "Regresar la base de datos al estado funcional anterior" confirm: "¿Estás seguro que quieres regresar la base de datos al estado funcional anterior?" export_csv: - users: - text: "Exportar usuarios" - title: "Exportar la lista de usuarios en un archivo CSV." - success: "La exportación ha sido iniciada. Serás notificado en breves con los avances." failed: "Exportación fallida, revisa los logs." customize: title: "Personalizar" @@ -1574,6 +1593,7 @@ es: settings: "Ajustes" all: "Todos" sending_test: "Enviando e-mail de prueba..." + error: "ERROR - %{server_error}" test_error: "Hubo un error al enviar el email de prueba. Por favor, revisa la configuración de correo, verifica que tu servicio de alojamiento no esté bloqueando los puertos de conexión de correo, y prueba de nuevo." sent: "Enviado" skipped: "Omitidos" @@ -1684,13 +1704,11 @@ es: last_emailed: "Último email enviado" not_found: "Lo sentimos, ese usuario no existe." active: "Activo" - show_emails: "Mostrat emails" + show_emails: "Mostrar emails" nav: new: "Nuevo" active: "Activo" pending: "Pendiente" - admins: 'Administradores' - moderators: 'Moderadores' suspended: 'Suspendidos' blocked: 'Bloqueados' approved: "Aprobado/s?" diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index 4223da7375..416e956223 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -1469,10 +1469,6 @@ fi: title: "Palauta tietokanta edelliseen toimivaan tilaan" confirm: "Oletko varma, että haluat palauttaa tietokannan edelliseen toimivaan tilaan?" export_csv: - users: - text: "Vie käyttäjätiedot" - title: "Vie lista käyttäjistä CSV-tiedostoon." - success: "Vienti on käynnissä, saat pian ilmoituksen sen edistymisestä." failed: "Vienti epäonnistui. Tarkista loki-tiedostot." customize: title: "Mukauta" @@ -1659,8 +1655,6 @@ fi: new: "Uudet" active: "Aktiiviset" pending: "Odottaa" - admins: 'Ylläpitäjät' - moderators: 'Valvojat' suspended: 'Hyllytetyt' blocked: 'Estetyt' approved: "Hyväksytty?" diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index 2697f82221..25441ab04e 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -247,6 +247,12 @@ fr: organisation: Société phone: Téléphone other_accounts: "Autres comptes avec cette adresse IP :" + username: "nom d'utilisateur" + trust_level: "NC" + read_time: "temps de lecture" + topics_entered: "sujets postées" + post_count: "# messages" + confirm_delete_other_accounts: "Êtes-vous sûr de vouloir supprimer tous ces comptes ?" user: said: "{{username}} :" profile: "Profil" @@ -337,14 +343,29 @@ fr: instructions: "Les images d'arrière plan seront centrées avec une taille par défaut de 590 pixels." email: title: "Courriel" + instructions: "Ne sera jamais visible publiquement" + ok: "On vous envoie un mail pour confirmer" + invalid: "Merci d'entrer une adresse email valide" + authenticated: "Votre adresse email a été authentifiée par {{provider}}" frequency: "Nous vous envoyons des courriels contenant uniquement des informations que vous n'avez pas déjà vues lors d'une précédente connexion." name: title: "Nom" + instructions: "Votre nom complet (facultatif)" + too_short: "Votre nom est trop court" + ok: "Votre nom a l'air bon." username: title: "Pseudo" + instructions: "Unique, sans espace, court" + short_instructions: "Les gens peuvent vous mentionner avec @{{username}}" + available: "Votre pseudo est disponible" + global_match: "L'adresse email correspond au pseudo enregistré" global_mismatch: "Déjà enregistré. Essayez {{suggestion}} ?" not_available: "Non disponible. Essayez {{suggestion}} ?" + too_short: "Votre pseudo est trop court" + too_long: "Votre pseudo est trop long" checking: "Vérification de la disponibilité de votre pseudo..." + enter_email: 'Pseudo trouvé; Entrez l''adresse email correspondante' + prefilled: "L'adresse email correspond à ce pseudo enregistré" locale: title: "Langue de l'interface" instructions: "Langage de votre interface. Cette dernière changera lorsque vous actualiserez la page." @@ -459,6 +480,8 @@ fr: fixed: "Charger la page" close: "Fermer" assets_changed_confirm: "Ce site vient d'être mis à jour. Rafraîchir maintenant pour accéder à la nouvelle version ?" + logout: "Vous avez été déconnecté" + refresh: "Rafraîchir" read_only_mode: enabled: "Un administrateur a activé le mode lecture seule. Vous pouvez continuer à naviguer sur le site mais les interactions ne fonctionneront pas correctement." login_disabled: "Impossible de se connecté quand le site est en mode lecture seule." @@ -572,6 +595,7 @@ fr: reply_here: "Répondre ici" reply: "Répondre" cancel: "Annuler" + create_topic: "Nouvelle discussion" create_pm: "Message privé" title: "ou appuyez sur Ctrl+Entrée" users_placeholder: "Ajouter un utilisateur" @@ -643,7 +667,10 @@ fr: title_with_attachments: "Ajouter une image ou un fichier" from_my_computer: "Depuis mon appareil" from_the_web: "Depuis le web" + remote_tip: "lien vers l'image" + remote_tip_with_attachments: "lien vers l'image ou le fichier ({{authorized_extensions}})" local_tip: "cliquez pour sélectionner une image depuis votre ordinateur" + local_tip_with_attachments: "Cliquez pour sélectionner une image ou un fichier depuis votre ordinateur ({{authorized_extensions}})" hint: "(vous pouvez également faire un glisser-déposer dans l'éditeur pour les télécharger)" hint_for_supported_browsers: "(vous pouvez aussi glisser et déposer ou coller des images dans l'éditeur pour les envoyer) " uploading: "Fichier en cours d'envoi" @@ -714,6 +741,7 @@ fr: top: "Il n'y a plus de meilleurs sujets." topic: filter_to: "{{post_count}} messages sur ce sujet" + create: 'Nouvelle discussion' create_long: 'Créer un nouveau sujet' private_message: 'Commencer un message privé' list: 'Sujets' @@ -1108,6 +1136,7 @@ fr: general: 'Général' settings: 'Paramètres' delete: 'Supprimer la catégorie' + create: 'Nouvelle catégorie' save: 'Enregistrer la catégorie' creation_error: Il y a eu une erreur lors de la création de la catégorie. save_error: Il y a eu une erreur lors de la sauvegarde de la catégorie. @@ -1158,6 +1187,8 @@ fr: title: "Silencieux" description: "Vous ne recevrez aucune notification sur les sujets de ces catégories, et ils n'apparaîtront pas dans votre onglet \"non-lus\"." flagging: + title: 'Merci de nous aider à garder notre communauté civilisé !' + private_reminder: 'les signalements sont privés, seulement visible aux modérateurs' action: 'Signaler ce message' take_action: "Signaler" notify_action: 'Message privé' @@ -1173,11 +1204,14 @@ fr: off_topic: "C'est hors-sujet" inappropriate: "C'est inapproprié" spam: "C'est un pourriel" + custom_placeholder_notify_user: "Soyez précis, constructif, et toujours respectueux." + custom_placeholder_notify_moderators: "Dites-nous ce qui vous dérange spécifiquement, et fournissez des liens pertinents et exemples si possible." custom_message: at_least: "saisir au moins {{n}} caractères" more: "{{n}} restants..." left: "{{n}} restants" flagging_topic: + title: "Merci de nous aider à garder notre communauté civilisé !" action: "Signaler Sujet" notify_action: "Message privé" topic_map: @@ -1487,10 +1521,6 @@ fr: title: "Restaurer (RollBack) la base de données à l'état de travail précédent" confirm: "Êtes-vous sûr de vouloir restaurer (rollback) la base de données à l'état de fonctionnement précédent?" export_csv: - users: - text: "Exporter les utilisateurs" - title: "Exporte la liste des utilisateurs dans un fichier CSV." - success: "L'export a commencé, vous serez notifié prochainement de sa progression." failed: "L'export a échoué. Veuillez consulter les logs." customize: title: "Personnaliser" @@ -1567,6 +1597,7 @@ fr: settings: "Paramètrage" all: "Tous" sending_test: "Envoi en cours du courriel de test..." + error: "ERREUR - %{server_error}" test_error: "Il y a eu un problème avec l'envoi du courriel de test. Veuillez vérifier vos paramètres, que votre hébergeur ne bloque pas les connections aux courriels, et réessayer." sent: "Envoyé" skipped: "Passé" @@ -1580,6 +1611,7 @@ fr: sent_test: "Envoyé !" delivery_method: "Méthode d'envoi" preview_digest: "Prévisualisation du courriel" + preview_digest_desc: "Prévisualiser le contenu des courriels hebdomadaires sommaires envoyés aux utilisateurs inactifs." refresh: "Rafraîchir" format: "Format" html: "html" @@ -1681,8 +1713,6 @@ fr: new: "Nouveaux" active: "Actifs" pending: "En attente" - admins: 'Administrateurs' - moderators: 'Modérateurs' suspended: 'Suspendus' blocked: 'Bloqués' approved: "Approuvé ?" @@ -1986,6 +2016,7 @@ fr: actions: title: 'Actions' star: 'f Sujet Favoris' + pin_unpin_topic: 'shift p Épingler/De-épingler le sujet' share_topic: 'shift s Partager un sujet' share_post: 's Partager un message' reply_as_new_topic: 't Répondre en créant un sujet lié' diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml index f05898e173..1a96734de1 100644 --- a/config/locales/client.he.yml +++ b/config/locales/client.he.yml @@ -92,7 +92,7 @@ he: email: 'שלח קישור בדוא"ל' topic_admin_menu: "פעולות ניהול לנושא" edit: 'ערוך את הכותרת והקטגוריה של הנושא' - not_implemented: "סליחה, האפשרות הזו עדיין לא מומשה!" + not_implemented: "סליחה, תכונה זו עדיין לא מומשה!" no_value: "לא" yes_value: "כן" generic_error: "סליחה, ארעה שגיאה." @@ -192,7 +192,7 @@ he: posted_by_user: "פורסם על ידי {{user}}" posted_by_you: "פורסם על ידך" sent_by_user: "נשלח על ידי {{user}}" - sent_by_you: "נשלח על ידך" + sent_by_you: "נשלח על ידך" groups: visible: "הקבוצה זמינה לכל המשתמשים" title: @@ -201,15 +201,15 @@ he: members: "חברים" posts: "הודעות" alias_levels: - title: "מי יכול להשתמש בקבוצה הזו ככינוי?" + title: "מי יכול להשתמש בקבוצה זו ככינוי?" nobody: "אף אחד" - only_admins: "רק מנהלים ראשיים" - mods_and_admins: "רק מנהלים ומנהלים ראשיים" - members_mods_and_admins: "רק חברי הקבוצה, מנהלים ומנהלים ראשיים" + only_admins: "רק מנהלים" + mods_and_admins: "רק מנחים ומנהלים" + members_mods_and_admins: "רק חברי הקבוצה, מנחים ומנהלים" everyone: "כולם" user_action_groups: - '1': "לייקים ניתנו" - '2': "לייקים התקבלו" + '1': "לייקים שניתנו" + '2': "לייקים שהתקבלו" '3': "מועדפים" '4': "נושאים" '5': "הודעות" @@ -246,6 +246,13 @@ he: location_not_found: (לא ידוע) organisation: ארגון phone: טלפון + other_accounts: "חשבונות נוספים עם כתובת IP זו:" + username: "שם משתמש" + trust_level: "TL" + read_time: "זמן צפייה" + topics_entered: "כניסה לנושאים" + post_count: "# פרסומים" + confirm_delete_other_accounts: "אתה בטוח שברצונך למחוק חשבונות אלו?" user: said: "{{username}}:" profile: "פרופיל" @@ -262,7 +269,7 @@ he: trust_level: "רמת אמון" notifications: "התראות" dismiss_notifications: "סימון הכל כנקרא" - dismiss_notifications_tooltip: "סימון כל התראות ה\"לא נקרא\" ככאלה שנקראו" + dismiss_notifications_tooltip: "סימון כל ההתראות שלא נקראו כהתראות שנקראו" disable_jump_reply: "לא לקפוץ לפוסט החדש שלך לאחר משלוח התשובה" dynamic_favicon: "הראה התראה על הודעות חדשות באייקון העמוד (נסיוני)" edit_history_public: "אפשרו למשתמשים אחרים לראות את תיקוני הפרסומים שלי" @@ -278,16 +285,17 @@ he: github_profile: "גיטהאב" mailing_list_mode: "קבלו דוא\"ל על כל פרסום חדש (אלא אם כן השתקתם את הנושא או את הקטגוריה)" watched_categories: "נצפה" - watched_categories_instructions: "את/ה אוטומטית תצפו בכל הנושאים החדשים בקטגוריות האלה. תקבלו התראות על כל ההודעות והנושאים החדשים, ובנוסף, כמות ההודעות שלא נקראו יופיעו לצד כל נושא." + watched_categories_instructions: "את/ה תצפו אוטומטית בכל הנושאים החדשים בקטגוריות האלה ותקבלו התראות על כל ההודעות והנושאים החדשים. כמו כן כמות ההודעות החדשות ואלו שלא נקראו תופיע לצד כל נושא." tracked_categories: "במעקב" - tracked_categories_instructions: "מעקב אוטומטי אחרי נושאים חדשים בקטגוריות אלו. סך הפרסומים החדשים ושלא נקראו יופיע לצד הנושא." + tracked_categories_instructions: "מעקב אוטומטי אחרי נושאים חדשים בקטגוריות אלו. מספר הפרסומים החדשים ואלו שלא נקראו יופיע לצד הנושא." muted_categories: "מושתק" - muted_categories_instructions: "לא תקבלו התראות בנוגע לנושאים חדשים בקטגוריות האלה, והם לא יופיעו בעמוד הלא נקראו שלך." + muted_categories_instructions: "לא תקבלו התראות על נושאים חדשים בקטגוריות אלו, והם לא יופיעו בעמוד הלא נקראו שלך." delete_account: "מחק את החשבון שלי" delete_account_confirm: "אתה בטוח שברצונך למחוק את החשבון? לא ניתן לבטל פעולה זו!" deleted_yourself: "חשבונך נמחק בהצלחה." delete_yourself_not_allowed: "אתה לא יכול למחוק את חשבונך כרגע. צור קשר עם מנהל כדי שימחק אותו בשבילך." unread_message_count: "הודעות" + admin_delete: "מחק" staff_counters: flags_given: "סימונים שעוזרים" flagged_posts: "הודעות מסומנות" @@ -318,7 +326,7 @@ he: error: "הייתה שגיאה בשינוי כתובת הדואר האלקטרוני שלך. אולי היא תפוסה?" success: "שלחנו דואר אלקטרוני לכתובת הדואר הזו. בבקשה עקוב אחרי הוראות האישור שם." change_avatar: - title: "שנה את האוואטר שלך" + title: "שנה את האווטר שלך" gravatar: "Gravatar, מבוסס על" refresh_gravatar_title: "רענון האווטר שלכם" letter_based: "אווטר שהוגדרר על ידי המערכת" @@ -329,27 +337,42 @@ he: image_is_not_a_square: "אזהרה: חתכנו את התמונה שלך, היא לא ריבועית" change_profile_background: title: "שינוי רקע פרופיל" - instructions: "רקעי הפרופיל ימורכזו ויוצגו עם ברירת מחדל של רוחב של 850px." + instructions: "רקעי הפרופיל ימורכזו ויוצגו ברוחב ברירת מחדל של 850px." change_card_background: title: "כרטיס הרקע של המשתמש/ת" - instructions: "תמונות רקע ימורכזו ויוצגו עם ברירת מחדל של רוחב 590px." + instructions: "תמונות רקע ימורכזו ויוצגו ברוחב ברירת מחדל של 590px." email: title: "דואר אלקטרוני" + instructions: "לא נצפו מעולם" + ok: "נשלח לך דואר אלקטרוני לאישור" + invalid: "בבקשה הכנס כתובת דואר אלקטרוני חוקית" + authenticated: "כתובת הדואר האלקטרוני שלך אושרה על ידי {{provider}}" frequency: "נשלח לך דואר אלקטרוני רק אם לא ראינו אותך לאחרונה ועדיין לא ראית את מה שאנחנו רוצים לשלוח" name: title: "שם" + instructions: "שמך המלא (רשות)" + too_short: "השם שלך קצר מידי" + ok: "השם נראה טוב" username: title: "שם משתמש" + instructions: "ייחודי, ללא רווחים וקצר" + short_instructions: "אנשים יכולים לאזכר אותך כ @{{username}}" + available: "שם המשתמש שלך פנוי" + global_match: "הדואר האלקטרוני תואם את שם המשתמש הרשום" global_mismatch: "כבר רשום. נסה {{suggestion}}?" not_available: "לא זמין. נסה {{suggestion}}?" + too_short: "שם המשתמש שלך קצר מידי" + too_long: "שם המשתמש שלך ארוך מידי" checking: "בודק זמינות שם משתמש..." + enter_email: 'נמצא שם משתמש - הכנס דואר אלקטרוני תואם' + prefilled: "הדואר האלקטרוני תואם לשם משתמש זה" locale: title: "שפת ממשק" instructions: "שפת ממשק המשתמש. היא תתחלף כשתרעננו את העמוד." default: "(ברירת מחדל)" password_confirmation: title: "סיסמה שוב" - last_posted: "הודעה אחרונה" + last_posted: "פרסום אחרון" last_emailed: "נשלח לאחרונה בדואר אלקטרוני" last_seen: "נראה" created: "הצטרף" @@ -370,7 +393,7 @@ he: other_settings: "אחר" categories_settings: "קטגוריות" new_topic_duration: - label: "החשב נושא חדש כאשר" + label: "נושא יחשב כנושא חדש כאשר" not_viewed: "עדיין לא צפית בהם" last_here: "נוצר(ו) מאז שהייתם כאן לאחרונה" after_n_days: @@ -446,7 +469,7 @@ he: forbidden: "תקלת גישה" unknown: "תקלה" desc: - network: "אנא בדקו את ההתקשרות שלכם" + network: "אנא בדקו את החיבור שלכם" network_fixed: "נראה שזה חזר לעבוד." server: "קוד שגיאה: {{status}}" forbidden: "אינך רשא/ית לצפות בזה." @@ -457,6 +480,8 @@ he: fixed: "טעינת עמוד" close: "סגור" assets_changed_confirm: "האתר עבר עדכון. תרצו לרענן לגרסא המתקדמת ביותר?" + logout: "נותקת מהמערכת" + refresh: "רענן" read_only_mode: enabled: "מצב קריאה בלבד למנהל ראשי. ניתן להמשיך לקרוא באתר אבל פעולות לא יעבדו כראוי." login_disabled: "התחברות אינה מתאפשרת כשהאתר במצב קריאה בלבד." @@ -481,10 +506,10 @@ he: enable: 'סכם נושא זה' disable: 'הצג את כל ההודעות' deleted_filter: - enabled_description: "נושא זה מכיל פרסומים שנמחקו, אשר הוסתרו." + enabled_description: "נושא זה מכיל פרסומים שנמחקו ולכן אינם מוצגים." disabled_description: "פרסומים שנמחקו בנושא זה מוצגים כעת." - enable: "הסתר פוסטים מחוקים" - disable: "הצגת פרסומים שנמחקו." + enable: "הסתר פרסומים שנמחקו" + disable: "הצגת פרסומים שנמחקו" private_message_info: title: "הודעה פרטית" invite: "הזמינו אחרים..." @@ -505,8 +530,8 @@ he: invite: "הזן שם משתמש או כתובת דואר אלקטרוני ונשלח לך קישור לאיפוס סיסמה" reset: "איפוס סיסמה" complete_username: "אם קיים חשבון שמתאים לשם המשתמש %{username}, אתה אמור לקבל בקרוב מייל עם הוראות לאיפוס הסיסמא. " - complete_email: "אם החשבון מתאים ל %{email}, אתה אמור לקבל בקרוב מעעל עם הוראות לאיפוס הסיסמא." - complete_username_found: "מצאנו חשבון שתואם את שם המשתמש %{username}, קרוב לודאי שתקבלו דוא\"ל עם הנחיות כיצד לאתחל את הסיסמא שלכם תוך זמן קצר." + complete_email: "במידה והחשבון מתאים לכתובת %{email}, אתם אמורים לקבל בקרוב מייל עם הוראות לאיפוס הסיסמא." + complete_username_found: "מצאנו חשבון שתואם לשם המשתמש %{username}, קרוב לודאי שתקבלו דוא\"ל עם הנחיות כיצד לאתחל את הסיסמא שלכם תוך זמן קצר." complete_email_found: "מצאנו חשבון תואם ל%{email}. בתוך זמן קצר תקבלו אליו דוא\"ל עם הנחיות כיצד לאתחל את הסיסמא שלכם." complete_username_not_found: "שום חשבון אינו תואם לשם המשתמש %{username}" complete_email_not_found: "שום חשבון אינו תואם ל %{email}" @@ -570,6 +595,8 @@ he: reply_here: "תגובה כאן" reply: "תגובה" cancel: "ביטול" + create_topic: "נושא חדש" + create_pm: "הודעה פרטית" title: "או לחץ Ctrl+Enter" users_placeholder: "הוספת משתמש" title_placeholder: " במשפט אחד, במה עוסק הדיון הזה?" @@ -640,7 +667,10 @@ he: title_with_attachments: "הוספת תמונה או קובץ" from_my_computer: "מהמחשב שלי" from_the_web: "מהאינטרנט" + remote_tip: "קישור לתמונה" + remote_tip_with_attachments: "קישור לתמונה או קובץ ({{authorized_extensions}})" local_tip: "לחץ לבחירת תמונה מהמכשיר שלך" + local_tip_with_attachments: "לחץ לבחירת תמונה או קובץ מהמכשיר שלך ({{authorized_extensions}})" hint: "(ניתן גם לגרור לעורך להעלאה)" hint_for_supported_browsers: "(אתה יכול גם לעשות drag and drop או להדביק תמונות אל תוך העורך כדי להעלות אותן)" uploading: "מעלה" @@ -707,6 +737,7 @@ he: top: "אין עוד נושאים מובילים." topic: filter_to: "{{post_count}} הודעות בנושא" + create: 'נושא חדש' create_long: 'יצירת נושא חדש' private_message: 'התחלת הודעה פרטית' list: 'נושאים' @@ -1101,6 +1132,7 @@ he: general: 'כללי' settings: 'הגדרות' delete: 'מחק קטגוריה' + create: 'קטגוריה חדשה' save: 'שמור קטגוריה' creation_error: ארעה שגיאה במהלך יצירת הקטגוריה הזו. save_error: ארעה שגיאה בשמירת הקטגוריה הזו @@ -1151,6 +1183,8 @@ he: title: "מושתק" description: "לא תקבל התראות בנוגע לנושאים חדשים בקטגוריות האלה, והם לא יופיעו בלשונית ה\"לא נקראו\" שלך." flagging: + title: 'תודה על עזרתך לשמירה על תרבות הקהילה שלנו!' + private_reminder: 'דגלים הם פרטיים וניתנים לצפייה ע"י הצוות בלבד' action: 'סימון פרסום' take_action: "בצע פעולה" notify_action: 'הודעה פרטית' @@ -1166,11 +1200,14 @@ he: off_topic: "מחוץ לנושא" inappropriate: "לא ראוי" spam: "זהו ספאם" + custom_placeholder_notify_user: "היה ממוקד, חיובי ואדיב תמיד." + custom_placeholder_notify_moderators: "ספר לנו מה בדיוק מטריד אותך וצרף קישורים רלוונטיים ודוגמאות במידת האפשר." custom_message: at_least: "הזן לפחות {{n}} תווים" more: "{{n}} נשארו..." left: "{{n}} נותרו" flagging_topic: + title: "תודה על עזרתך לשמירה על תרבות הקהילה שלנו!" action: "סימון נושא" notify_action: "הודעה פרטית" topic_map: @@ -1480,15 +1517,13 @@ he: title: "הזחר את מסד הנתונים למצב עבודה קודם" confirm: "אתה בטוח שברצונך להחזיר את מסד הנתונים למצב עבודה קודם?" export_csv: - users: - text: "ייצוא משתמשים" - title: "ייצאו רשימת המשתמשים בקובץ CSV." - success: "תהליך הייצוא החל, נעדכן אתכם תוך זמן קצר עם התקדמותו." failed: "הייצוא נכשל. אנא בדקו ברישומי הלוג." customize: title: "התאם אישית" long_title: "התאמה של האתר" + css: "CSS" header: "כותרת" + footer: "כותרת תחתית" override_default: "אל תכלול את ה-Stylesheet הסטנדרטי" enabled: "מאופשר?" preview: "תצוגה מקדימה" @@ -1558,6 +1593,7 @@ he: settings: "הגדרות" all: "הכל" sending_test: "שולח דואר אלקטרוני לבדיקה..." + error: "שגיאה - %{server_error}" test_error: "הייתה בעיה בשליחת הדואר האלקטרוני. בבקשה בדוק את ההגדרות שלך ונסה שנית." sent: "נשלח" skipped: "דולג" @@ -1571,6 +1607,7 @@ he: sent_test: "נשלח!" delivery_method: "שיטת העברה" preview_digest: "תצוגה מקדימה של סיכום" + preview_digest_desc: "צפה בתקציר השבועי הנשלח בדואר אלקטרוני למשתמשים לא פעילים." refresh: "רענן" format: "פורמט" html: "html" @@ -1632,6 +1669,7 @@ he: check_email: "בדיקת דוא\"ל" delete_topic: "מחיקת נושא" delete_post: "מחיקת פרסום" + impersonate: "התחזה" screened_emails: title: "הודעות דואר מסוננות" description: "כשמישהו מנסה ליצור חשבון חדש, כתובות הדואר האלקטרוני הבאות ייבדקו וההרשמה תחסם או שיבוצו פעולות אחרות." @@ -1671,8 +1709,6 @@ he: new: "חדש" active: "פעיל" pending: "ממתין" - admins: 'מנהלים ראשיים' - moderators: 'מנהלים' suspended: 'מושעים' blocked: 'חסום' approved: "מאושר?" @@ -1976,6 +2012,7 @@ he: actions: title: 'פעולות' star: 'f סמן נושא בכוכב' + pin_unpin_topic: 'shift p הצמדת נושא' share_topic: ' shift s שיתוף נושא' share_post: 's שיתוף הודעה' reply_as_new_topic: 't תגובה כנושא מקושר' @@ -2083,7 +2120,7 @@ he: description: ציטוט משתמש read_guidelines: name: קריאת כללים מנחים - description: קראו את הכללים המנחים של הקהילה + description: קראו את הכללים המנחים של הקהילה reader: name: מקראה description: קראו כל פרסום בנושא עם יותר מ-100 פרסומים diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index caa4f4e29d..b27f036e0c 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -247,6 +247,8 @@ it: organisation: Organizzazione phone: Telefono other_accounts: "Altri account con questo indirizzo IP:" + username: "nome utente" + trust_level: "TL" user: said: "{{username}}:" profile: "Profilo" @@ -342,8 +344,11 @@ it: title: "Nome" username: title: "Nome utente" + available: "Il tuo nome utente è disponibile" global_mismatch: "Già registrato. Prova {{suggestion}}?" not_available: "Non disponibile. Prova {{suggestion}}?" + too_short: "Il tuo nome utente è troppo corto" + too_long: "Il tuo nome utente è troppo lungo" checking: "Controllo la disponibilità del nome utente..." locale: title: "Lingua dell'interfaccia" @@ -459,6 +464,7 @@ it: fixed: "Carica Pagina" close: "Chiudi" assets_changed_confirm: "Questo sito è stato aggiornato. Aggiornare ora alla nuova versione?" + refresh: "Ricarica" read_only_mode: enabled: "Un amministratore ha attivato la modalità di sola lettura. Puoi continuare a navigare nel sito ma le interazioni potrebbero non funzionare." login_disabled: "L'accesso è disabilitato quando il sito è in modalità di sola lettura." @@ -1491,10 +1497,6 @@ it: title: "Ripristina il database a una versione funzionante precedente" confirm: "Sicuro di voler ripristinare una precedente versione funzionante del database?" export_csv: - users: - text: "Esporta Utenti" - title: "Esporta la lista utenti in un file CSV." - success: "L'esportazione è stata avviata. Sarai notificato a breve con il progresso del processo." failed: "Esportazione fallita. Controlla i log." customize: title: "Personalizza" @@ -1684,8 +1686,6 @@ it: new: "Nuovi" active: "Attivi" pending: "In attesa" - admins: 'Amministratori' - moderators: 'Moderatori' suspended: 'Sospesi' blocked: 'Bloccati' approved: "Approvato?" diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml index e82e775cd7..291293ae37 100644 --- a/config/locales/client.ja.yml +++ b/config/locales/client.ja.yml @@ -1321,10 +1321,6 @@ ja: title: "データベースを元の作業状態にロールバックする" confirm: "本当にデータベースを元の作業状態にロールバックしますか?" export_csv: - users: - text: "ユーザ情報を出力する" - title: "ユーザ情報をCSVファイルに出力する" - success: "出力作業が始まりました。" failed: "出力失敗。詳しくはログに参考してください。" customize: title: "カスタマイズ" @@ -1487,8 +1483,6 @@ ja: new: "新規" active: "アクティブ" pending: "保留中" - admins: '管理者' - moderators: 'モデレータ' suspended: 'サスペンド中' blocked: 'ブロック中' approved: "承認?" diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index b916965da7..05aad45ccb 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -1427,10 +1427,6 @@ ko: title: "데이터베이스를 이전 workiong state로 되돌리기" confirm: "정말로 이전 작업 상태로 데이터베이스를 롤백하시겠습니까?" export_csv: - users: - text: "사용자 내보내기" - title: "CSV 파일로 사용자 목록 내보내기" - success: "내보내기가 시작되었습니다. 곧 진행사항을 알려드릴께요." failed: "내보내기가 실패했습니다. 로그를 확인해주세요" customize: title: "사용자 지정" @@ -1622,8 +1618,6 @@ ko: new: "새로운 사용자" active: "활성화 사용자" pending: "보류된 사용자" - admins: '관리자 목록' - moderators: '운영자' suspended: '접근 금지 사용자' blocked: '블락된 사용자' approved: "승인?" diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml index 59bc842404..574aa88ac8 100644 --- a/config/locales/client.nb_NO.yml +++ b/config/locales/client.nb_NO.yml @@ -247,6 +247,9 @@ nb_NO: organisation: Organisasjon phone: Telefon other_accounts: "Andre kontoer med denne IP-adressen:" + username: "brukernavn" + read_time: "lesetid" + post_count: "# innlegg" user: said: "{{username}}:" profile: "Profil" @@ -337,13 +340,22 @@ nb_NO: instructions: "Bakgrunnsbilder vil bli sentrert og ha en standard bredde på 590px." email: title: "E-post" + ok: "Vi sender deg en email for å bekrefte" frequency: "Vi sender deg bare en e-post om vi ikke har sett deg nylig og du ikke allerede har sett det vi varsler deg om." name: title: "Navn" + instructions: "Ditt fulle navn (valgfritt)" + too_short: "Navnet ditt er for kort." + ok: "Navnet ditt ser bra ut." username: title: "Brukernavn" + instructions: "Unikt, kort og uten mellomrom." + short_instructions: "Folk kan nevne deg som @{{username}}." + available: "Ditt brukernavn er tilgjengelig." global_mismatch: "Allerede registrert. Prøv {{suggestion}}?" not_available: "Ikke tilgjengelig. Prøv {{suggestion}}?" + too_short: "Ditt brukernavn er for kort." + too_long: "Ditt brukernavn er for langt." checking: "Sjekker brukernavnets tilgjengelighet..." locale: title: "Språk for grensesnitt" @@ -459,6 +471,7 @@ nb_NO: fixed: "Last side" close: "Lukk" assets_changed_confirm: "Dette nettstedet ble nettopp oppdatert. Oppdater nå for nyeste versjon?" + refresh: "Refresh" read_only_mode: enabled: "En administrator har aktivert skrivebeskyttet modus. Du kan fortsette å benytte siden, men enkelte operasjoner vil kanskje ikke fungere." login_disabled: "Innlogging er deaktivert mens nettsiden er i skrivebeskyttet modus." @@ -1485,10 +1498,6 @@ nb_NO: title: "Gjenopprett databasen til en tidligere fungerende tilstand" confirm: "Er du sikker på at du vil gjenopprette databasen til en tidligere fungerende tilstand?" export_csv: - users: - text: "Eksporter brukere" - title: "Eksporter brukerlisten til en CSV-fil." - success: "Eksporteringen er startet. Du vil bli varslet snarlig om fremgangen." failed: "Eksporteringen feilet. Venligst undersøk loggene." customize: title: "Tilpasse" @@ -1578,6 +1587,7 @@ nb_NO: sent_test: "sendt!" delivery_method: "Leveringsmetode" preview_digest: "Forhåndsvis Oppsummering" + preview_digest_desc: "Forhåndsvis innholdet i den ukentlige sammendrags emailen sendt til innaktive melemer." refresh: "Refresh" format: "Format" html: "html" @@ -1678,8 +1688,6 @@ nb_NO: new: "Ny" active: "Aktiv" pending: "Ventende" - admins: 'Administratorer' - moderators: 'Moderatorer' suspended: 'Bannlyst' blocked: 'Blokkert' approved: "Godkjent?" diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index 8fde1c35e5..9e0ccb0904 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -1409,10 +1409,6 @@ nl: title: "Herstel de database naar de laatst werkende versie" confirm: "Weet je zeker dat je de database wil herstellen naar de laatste versie?" export_csv: - users: - text: "Exporteer gebruikers" - title: "Exporteer gebruikerslijst in *.CSV-formaat" - success: "Exporteren is gestart; over enkele momenten krijg je meer informatie over de voortgang" failed: "Exporteren is mislukt. Controleer de logbestanden." customize: title: "Aanpassingen" @@ -1595,8 +1591,6 @@ nl: new: "Nieuw" active: "Actief" pending: "Te beoordelen" - admins: 'Admins' - moderators: 'Moderatoren' suspended: 'Geschorst' blocked: 'Geblokt' approved: "Goedgekeurd?" diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index 42ed15d668..3c392e5ea6 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -270,6 +270,12 @@ pl_PL: organisation: Organizacja phone: Numer telefonu other_accounts: "Inne konta z tym adresem IP:" + username: "nazwa użytkownika" + trust_level: "TL" + read_time: "czas czytania:" + topics_entered: "wprowadzone tematy:" + post_count: "# wpisów" + confirm_delete_other_accounts: "Czy na pewno chcesz usunąć wybrane konta?" user: said: "{{username}}:" profile: "Profil" @@ -360,14 +366,29 @@ pl_PL: instructions: "Tło karty użytkownika est wycentrowane i posiada domyślną szerokość 590px." email: title: "Email" + instructions: "Nie będzie publicznie widoczny" + ok: "Otrzymasz potwierdzenie emailem" + invalid: "Podaj poprawny adres email" + authenticated: "Twój email został potwierdzony przez {{provider}}" frequency: "Wyślemy do Ciebie email tylko jeżeli dawno Cię nie widzieliśmy i wyłącznie na temat rzeczy których jeszcze nie widziałeś." name: title: "Pełna nazwa" + instructions: "Twoja pełna nazwa (opcjonalna)" + too_short: "Twoja nazwa jest zbyt krótka" + ok: "Twoja nazwa jest ok" username: title: "Nazwa konta" + instructions: "Unikalna, krótka i bez spacji" + short_instructions: "Inni mogą o tobie wspomnieć pisząc @{{username}}" + available: "Nazwa użytkownika jest dostępna" + global_match: "Email zgadza się z zarejestrowaną nazwą użytkownika" global_mismatch: "Zajęta. Może spróbuj {{suggestion}}?" not_available: "Niedostępna. Może spróbuj {{suggestion}}?" + too_short: "Nazwa użytkownika jest zbyt krótka" + too_long: "Nazwa użytkownika jest zbyt długa" checking: "Sprawdzanie, czy nazwa jest dostępna…" + enter_email: 'Nazwa użytkownika znaleziona – wpisz przypisany adres email' + prefilled: "Email zgadza się z zarejestrowaną nazwą użytkownika" locale: title: "Język interfejsu" instructions: "Język interfejsu użytkownika. Zmieni się, gdy odświeżysz stronę." @@ -486,6 +507,8 @@ pl_PL: fixed: "Załaduj stronę" close: "Zamknij" assets_changed_confirm: "Serwis został zmieniony, czy pozwolisz na przeładowanie strony w celu aktualizacji do najnowszej wersji?" + logout: "Nastąpiło wylogowanie." + refresh: "Odśwież" read_only_mode: enabled: "Administrator włączył tryb tylko do odczytu. Możesz przeglądać serwis, jednak zmiany nie będą możliwe." login_disabled: "Logowanie jest zablokowane, gdy strona jest w trybie tylko do odczytu." @@ -1557,10 +1580,6 @@ pl_PL: title: "Wycofaj bazę danych do poprzedniego poprawnego stanu" confirm: "Czy na pewno chcesz przywrócić bazę danych do poprzedniego poprawnego stanu?" export_csv: - users: - text: "Eksport użytkowników" - title: "Wyeksportuj listę użytkowników do pliku CSV" - success: "Rozpoczęto eksport, za jakiś czas otrzymasz powiadomienie z informacją o postępach." failed: "Eksport zakończył się niepowodzeniem. Sprawdź logi." customize: title: "Wygląd" @@ -1637,6 +1656,7 @@ pl_PL: settings: "Ustawienia" all: "Wszystkie" sending_test: "Wysyłanie testowego emaila…" + error: "BŁAD - %{server_error}" test_error: "Wystąpił problem podczas wysyłania testowego maila. Sprawdź ustawienia poczty, sprawdź czy Twój serwer nie blokuje połączeń pocztowych i spróbuj ponownie." sent: "Wysłane" skipped: "Pominięte" @@ -1752,8 +1772,6 @@ pl_PL: new: "Nowi" active: "Aktywni" pending: "Oczekujący" - admins: 'Administratorzy' - moderators: 'Moderatorzy' suspended: 'Zawieszeni' blocked: 'Zablokowani' approved: "Zatwierdzam?" diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml index 2121ee8fca..1f73ac3deb 100644 --- a/config/locales/client.pt.yml +++ b/config/locales/client.pt.yml @@ -1409,10 +1409,6 @@ pt: title: "Reverter a base de dados para um estado anterior operacional" confirm: "Tem a certeza que deseja reverter a base de dados para um estado anterior operacional?" export_csv: - users: - text: "Exportar Utilizadores" - title: "Exportar lista de utilizadores num ficheiro CSV." - success: "Exportação foi iniciada, será notificado brevemente do progresso da mesma." failed: "Exportação falhou. Por favor verifique os logs." customize: title: "Personalizar" @@ -1591,8 +1587,6 @@ pt: new: "Novos" active: "Ativos" pending: "Pendentes" - admins: 'Administradores' - moderators: 'Moderadores' suspended: 'Suspenso' blocked: 'Bloqueados' approved: "Aprovado?" diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml index 2acd387757..44d1387434 100644 --- a/config/locales/client.pt_BR.yml +++ b/config/locales/client.pt_BR.yml @@ -90,7 +90,7 @@ pt_BR: facebook: 'compartilhe este link no Facebook' google+: 'compartilhe este link no Google+' email: 'enviar esse link para um email' - topic_admin_menu: "ações de tópico do admin" + topic_admin_menu: "ações administrativas do tópico" edit: 'edite o título e a categoria deste tópico' not_implemented: "Esse recurso ainda não foi implementado, desculpe!" no_value: "Não" @@ -134,20 +134,21 @@ pt_BR: simple_title: "Sobre" title: "Sobre %{title}" stats: "Estatísticas do Site" - our_admins: "Nossos Administradores" - our_moderators: "Nossos Moderadores" + our_admins: "Nossos administradores" + our_moderators: "Nossos moderadores" stat: - all_time: "Sempre" - last_7_days: "Últimos 7 Dias" - like_count: "Likes" + all_time: "Desde o começo" + last_7_days: "Últimos 7 dias" + like_count: "Curtidas" topic_count: "Tópicos" + post_count: "Mensagens" user_count: "Usuários" bookmarks: not_logged_in: "desculpe, você precisa estar logado para favoritar mensagens" created: "você favoritou essa resposta" not_bookmarked: "você já leu esta mensagem; clique para favoritar." last_read: "este é a última resposta que você leu; clique para favoritar." - remove: "Remover Favorito" + remove: "Remover favorito" topic_count_latest: one: "{{count}} tópico novo ou atualizado." other: "{{count}} tópicos novos ou atualizados." @@ -166,8 +167,8 @@ pt_BR: upload: "Enviar" uploading: "Enviando..." uploaded: "Enviado!" - enable: "Ligar" - disable: "Desligar" + enable: "Habilitar" + disable: "Desabilitar" undo: "Desfazer" revert: "Reverter" failed: "Falhou" @@ -245,6 +246,13 @@ pt_BR: location_not_found: (desconhecido) organisation: Organização phone: Telefone + other_accounts: "Outras contas com esse endereço de IP:" + username: "nome de usuário" + trust_level: "TL" + read_time: "tempo de leitura" + topics_entered: "tópicos em que entrou" + post_count: "# mensagens" + confirm_delete_other_accounts: "Você tem certeza que deseja apagar essas contas?" user: said: "{{username}}:" profile: "Perfil" @@ -274,6 +282,7 @@ pt_BR: admin_tooltip: "Esse usuário é da administração" suspended_notice: "Esse usuário está suspenso até {{date}}." suspended_reason: "Motivo:" + github_profile: "Github" mailing_list_mode: "Receber um email para cada post novo (a menos que você coloque o tópico ou categoria em \"mudo\")" watched_categories: "Acompanhados" watched_categories_instructions: "Você vai acompanhar automaticamente todos os novos tópicos dessas categorias. Você será notificado de todas as novas mensagens e tópicos. Além disso, a contagem de mensagens não lidas e novas também aparecerá ao lado a lista do tópico." @@ -286,6 +295,7 @@ pt_BR: deleted_yourself: "Sua conta foi excluída com sucesso." delete_yourself_not_allowed: "Você não pode excluir a sua conta agora. Contate um administrador para apagar a sua conta para você." unread_message_count: "Mensagens Privadas" + admin_delete: "Apagar" staff_counters: flags_given: "sinalizadas úteis" flagged_posts: "posts marcados" @@ -329,17 +339,33 @@ pt_BR: title: "Fundo do perfil" instructions: "Fundos do perfil será centralizado e tera uma largura padrão de 850px." change_card_background: + title: "Plano de fundo de usuário" instructions: "As Imagens de fundo serão centralizadas e deverão ter largura de 590px" email: title: "Email" + instructions: "Nunca mostrar ao público" + ok: "Nós vamos pedir confirmação por email" + invalid: "Insira um endereço de email" + authenticated: "Seu email foi autenticado por {{provider}}" frequency: "Vamos lhe enviar emails apenas quando não o virmos há algum tempo e você não tiver visto as coisas que temos enviado." name: title: "Nome" + instructions: "Seu nome completo (opcional)" + too_short: "Seu nome é muito curto" + ok: "Seu nome parece bom" username: title: "Nome de Usuário" + instructions: "Únicos, sem espaços e curto" + short_instructions: "As pessoas podem mencionar você usando @{{username}}." + available: "Seu nome de usuário está disponível" + global_match: "O email corresponde ao nome de usuário registrado" global_mismatch: "Já está registado. Tente {{suggestion}}?" not_available: "Não está disponível. Tente {{suggestion}}?" + too_short: "Seu nome de usuário é muito curto" + too_long: "Seu nome de usuário é muito longo" checking: "Verificando disponibilidade do Nome de Usuário..." + enter_email: 'Nome de usuário encontrado, insira o email correspondente. ' + prefilled: "Email corresponde a esse nome de usuário registrado" locale: title: "idioma da interface" instructions: "Idioma da interface de usuário. Irá mudar quando você atualizar a página." @@ -352,6 +378,8 @@ pt_BR: created: "Entrou" log_out: "Log Out" location: "Localização" + card_badge: + title: "Cartão de emblemas do usuário" website: "Web Site" email_settings: "Email" email_digests: @@ -452,6 +480,8 @@ pt_BR: fixed: "Carregar Página" close: "Fechar" assets_changed_confirm: "Este site foi atualizado. Obter a última versão?" + logout: "Você foi desconectado." + refresh: "Atualizar" read_only_mode: enabled: "Um modo somente de leitura. Você pode continuar a navegar no site, mas as interações podem não funcionar." login_disabled: "Login é desativado enquanto o site está em modo de somente leitura." @@ -490,6 +520,7 @@ pt_BR: created: 'Criado' created_lowercase: 'criado' trust_level: 'Nível de confiança' + search_hint: 'nome de usuário, email ou endereço de IP' create_account: title: "Criar nova conta" failed: "Alguma coisa deu errado, talvez este email já esteja registrado, tente usar o Esqueci a Senha." @@ -564,6 +595,8 @@ pt_BR: reply_here: "Responda aqui" reply: "Responder" cancel: "Cancelar" + create_topic: "Novo tópico" + create_pm: "Mensagem privada" title: "Ou pressione Ctrl+Enter" users_placeholder: "Adicionar um usuário" title_placeholder: "Sobre o que é esta discussão em uma pequena frase?" @@ -634,7 +667,10 @@ pt_BR: title_with_attachments: "Adicionar uma imagem ou arquivo" from_my_computer: "Do meu dispositivo" from_the_web: "Da internet" + remote_tip: "link da imagem" + remote_tip_with_attachments: "link da imagem ou arquivo ({{authorized_extensions}})" local_tip: "clique para selecionar um arquivo do seu dispositivo" + local_tip_with_attachments: "clique para selecionar uma imagem ou arquivo do seu dispositivo ({{authorized_extensions}})" hint: "(Você também pode arrastar e soltar para o editor para carregá-las)" hint_for_supported_browsers: "(você também pode arrastar e soltar ou colar imagens no editor para carregá-las)" uploading: "Enviando" @@ -660,7 +696,7 @@ pt_BR: topics: bulk: reset_read: "Redefinir Lido" - delete: "Tópicos Deletados" + delete: "Apagar Tópicos" dismiss_posts: "Ignorar Posts" dismiss_posts_tooltip: "Zerar contagem de não lidos nestes tópicos, mas continuar a mostrar eles em minha lista de não lidos quando novos posts forem feitos." dismiss_topics: "Ignorar Tópicos" @@ -701,13 +737,14 @@ pt_BR: top: "Não há mais tópicos em alta." topic: filter_to: "{{post_count}} mensagens no tópico" + create: 'Novo tópico' create_long: 'Criar um novo tópico' private_message: 'Começar uma nova conversa privada' list: 'Tópicos' new: 'novo tópico' unread: 'não lido' new_topics: - one: '1 novo tópico' + one: '1 tópico novo' other: '{{count}} novos tópicos' unread_topics: one: '1 tópico não lido' @@ -858,10 +895,11 @@ pt_BR: n_posts: one: "1 mensagem" other: "{{count}} mensagens" + cancel: "Remover filtro" split_topic: title: "Mover para novo tópico" action: "mover para novo tópico" - topic_name: "Novo nome do tópico" + topic_name: "Nome do tópico novo" error: "Houve um erro ao mover as mensagens para o novo tópico." instructions: one: "Você está prestes a criar um novo tópico e populá-lo com a resposta que você selecionou." @@ -1094,6 +1132,7 @@ pt_BR: general: 'Geral' settings: 'Configurações' delete: 'Apagar categoria' + create: 'Nova categoria' save: 'Salvar categoria' creation_error: Houve um erro durante a criação da categoria. save_error: Houve um erro ao salvar a categoria. @@ -1144,6 +1183,8 @@ pt_BR: title: "Silenciar" description: "Você não será notificado sobre novos tópicos dessas categorias e eles não vão aparecer na guia de mensagens não lidas." flagging: + title: 'Obrigado por ajudar a manter a civilidade da nossa comunidade!' + private_reminder: 'sinalizações são privadas, apenas ficam visíveis a moderação' action: 'Sinalizar resposta' take_action: "Tomar Atitude" notify_action: 'Mensagem privada' @@ -1155,11 +1196,18 @@ pt_BR: submit_tooltip: "Enviar uma sinalização privada" take_action_tooltip: "Atingir o limiar de denuncias imediatamente, ao invés de esperar para mais denuncias da comunidade" cant: "Desculpe, não é possível colocar uma sinalização neste momento." + formatted_name: + off_topic: "É Off-Tópico" + inappropriate: "É inapropriado" + spam: "É spam" + custom_placeholder_notify_user: "Seja específico, construtivo e sempre seja gentil." + custom_placeholder_notify_moderators: "Deixe-nos saber especificamente com o que você está preocupado, e nos forneça links relevantes e exemplos quando possível." custom_message: at_least: "insira pelo menos {{n}} caracteres" more: "{{n}} em falta..." left: "{{n}} restantes" flagging_topic: + title: "Obrigado por ajudar a manter a civilidade da nossa comunidade!" action: "Sinalizar Tópico" notify_action: "Mensagem privada" topic_map: @@ -1318,6 +1366,11 @@ pt_BR: 7_days_ago: "7 Dias Atrás" 30_days_ago: "30 Dias Atrás" all: "Tudo" + view_table: "tabela" + view_chart: "Gráfico de barras" + refresh_report: "Atualizar Relatório" + start_date: "Data de Início" + end_date: "Data do Final" commits: latest_changes: "Últimas atualizações: atualize com frequência!" by: "por" @@ -1464,15 +1517,13 @@ pt_BR: title: "Reverter o banco de dados para seu estado anterior" confirm: "Tem certeza de que quer reverter o banco de dados para o estado anterior?" export_csv: - users: - text: "Exportar Usuários" - title: "Exportar lista de usuários como arquivo CSV." - success: "Exportação iniciada, você será notificado brevemente com o progresso." failed: "Falha na exportação. Por favor verifique os logs." customize: title: "Personalizar" long_title: "Personalizações do Site" + css: "CSS" header: "Cabeçalho" + footer: "Rodapé" override_default: "Sobrepor padrão?" enabled: "Habilitado?" preview: "pré-visualização" @@ -1542,6 +1593,7 @@ pt_BR: settings: "Settings" all: "Todas" sending_test: "Enviando e-mail de teste..." + error: "ERRO - %{server_error}" test_error: "Houve um problema ao enviar o email de teste. Por favor, verifique as configurações de email, se o seu provedor não está bloqueando conexões de email e tente novamente." sent: "Enviado" skipped: "Ignorado" @@ -1555,6 +1607,7 @@ pt_BR: sent_test: "enviado!" delivery_method: "Delivery Method" preview_digest: "Preview Digest" + preview_digest_desc: "Prever o conteúdo do email de resumo semanal enviado para usuários inativos." refresh: "Atualizar" format: "Formato" html: "html" @@ -1578,7 +1631,7 @@ pt_BR: last_match_at: "Última Correspondência" match_count: "Resultados" ip_address: "IP" - topic_id: "ID Tópico" + topic_id: "ID do Tópico" post_id: "ID Mensagem" delete: 'Excluir' edit: 'Editar' @@ -1616,6 +1669,7 @@ pt_BR: check_email: "checar email" delete_topic: "apagar tópico" delete_post: "apagar mensagem" + impersonate: "personificar" screened_emails: title: "Emails Filtrados" description: "Quando alguém tenta cria uma nova conta, os seguintes endereços de email serão verificados e o registro será bloqueado, ou outra ação será executada." @@ -1650,12 +1704,11 @@ pt_BR: last_emailed: "Último email enviado" not_found: "Desculpe, esse nome de usuário não existe no nosso sistema." active: "Ativo" + show_emails: "Mostrar Emails" nav: new: "Novos" active: "Ativos" pending: "Pendentes" - admins: 'Administradores' - moderators: 'Moderadores' suspended: 'Suspenso' blocked: 'Bloqueados' approved: "Aprovado?" @@ -1925,6 +1978,8 @@ pt_BR: with_time: %{username} às %{time} lightbox: download: "download" + search_help: + title: 'Procurar na Ajuda' keyboard_shortcuts_help: title: 'Atalhos de teclado' jump_to: @@ -1945,7 +2000,7 @@ pt_BR: next_prev: 'shift j/shift k Sessão próxima/anterior' application: title: 'Aplicação' - create: 'c Cria um novo tópico' + create: 'c Criar um tópico novo' notifications: 'n Abre notificações' site_map_menu: '= Abrir menu do site' user_profile_menu: 'p Abrir menu do usuário' @@ -1957,6 +2012,7 @@ pt_BR: actions: title: 'Ações' star: 'f Tópico favorito' + pin_unpin_topic: 'shift p Fixar/Desfixar o tópico' share_topic: 'shift s Compartilhar tópico' share_post: 's Compartilhar mensagem' reply_as_new_topic: 't Responder como tópico linkado' @@ -1986,6 +2042,7 @@ pt_BR: one: "1 emblema concedido" other: "%{count} emblemas concedidos" select_badge_for_title: Selecione um emblema para usar como título + none: "" badge_grouping: getting_started: name: Começando @@ -2067,3 +2124,12 @@ pt_BR: reader: name: Leitor description: Leia cada resposta em um tópico com mais de 100 respostas + google_search: | +

    Procurar com Google

    +

    +

    +

    diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index 37ecf88e7a..46a56b8692 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -270,6 +270,12 @@ ru: organisation: Организация phone: Телефон other_accounts: "Другие учетные записи с этим IP адресом" + username: "псевдоним" + trust_level: "Уровень" + read_time: "время чтения" + topics_entered: "посещено тем" + post_count: "сообщений" + confirm_delete_other_accounts: "Вы уверены, что хотите удалить эти учетные записи?" user: said: "{{username}}:" profile: "Профиль" @@ -360,14 +366,29 @@ ru: instructions: "Картинки фона будут отцентрированы и по-умолчанию имеют ширину 590 пикселей." email: title: "E-mail" + instructions: "Всегда скрыт от публики" + ok: "Мы вышлем вам письмо для подтверждения" + invalid: "Введите действующий адрес электронной почты" + authenticated: "Ваш адрес электронной почты подтвержден {{provider}}" frequency: "В случае вашего отсутствия на форуме вы будете получать уведомления, но только о новых сообщениях." name: title: "Имя" + instructions: "Ваше полное имя (опционально)" + too_short: "Ваше имя слишком короткое" + ok: "Допустимое имя" username: title: "Псевдоним" + instructions: "Уникальный, без пробелов и покороче" + short_instructions: "Пользователи могут упоминать вас по псевдониму @{{username}}" + available: "Псевдоним доступен" + global_match: "Адрес электронной почты совпадает с зарегистрированным псевдонимом" global_mismatch: "Уже занято. Попробуйте {{suggestion}}?" not_available: "Недоступно. Попробуйте {{suggestion}}?" + too_short: "Псевдоним слишком короткий" + too_long: "Псевдоним слишком длинный" checking: "Проверяю доступность псевдонима..." + enter_email: 'Псевдоним найден; введите адрес электронной почты' + prefilled: "Адрес электронной почты совпадает с зарегистрированным псевдонимом" locale: title: "Язык интерфейса" instructions: "Язык сайта. Необходимо перезагрузить страницу, чтобы изменения вступили в силу." @@ -486,6 +507,8 @@ ru: fixed: "Загрузить страницу" close: "Закрыть" assets_changed_confirm: "Сайт только что был обновлен. Перезагрузить страницу для перехода к новой версии?" + logout: "Вы вышли." + refresh: "Обновить" read_only_mode: enabled: "Администратор включил режим только для чтения. Вы можете продолжать просматривать сайт, но взаимодействие может не работать." login_disabled: "Вход отключён, пока сайт в режиме «только для чтения»" @@ -1222,6 +1245,7 @@ ru: description: "Не получать уведомлений о новых темах из этих разделов и не показывать новые темы во вкладке «Непрочитанные»." flagging: title: 'Спасибо за вашу помощь!' + private_reminder: 'жалобы анонимны и видны только персоналу' action: 'Пожаловаться' take_action: "Принять меры" notify_action: 'Личное сообщение' @@ -1560,10 +1584,6 @@ ru: title: "Откатить базу данных к предыдущему рабочему состоянию" confirm: "Вы уверены, что хотите откатить базу данных к предыдущему рабочему состоянию?" export_csv: - users: - text: "Экспорт пользователей" - title: "Экспортировать список пользователей в CSV файл." - success: "Экспорт начат, вы будете уведомлены о процессе." failed: "Экспорт не удался. Пожалуйста, проверьте логи." customize: title: "Оформление" @@ -1640,6 +1660,7 @@ ru: settings: "Настройки" all: "Все" sending_test: "Отправка тестового письма..." + error: "ОШИБКА - %{server_error}" test_error: "При отправке тестового письма произошла ошибка. Пожалуйста, внимательно проверьте ваши почтовые настройки, проверьте, что ваш сервер не блокирует почтовые соединения, и попытайтесь снова." sent: "Отправлено" skipped: "Пропущенные" @@ -1755,8 +1776,6 @@ ru: new: "Новые" active: "Активные" pending: "Ожидает одобрения" - admins: 'Администраторы' - moderators: 'Модераторы' suspended: 'Замороженные' blocked: 'Заблокированные' approved: "Подтвердить?" diff --git a/config/locales/client.sq.yml b/config/locales/client.sq.yml index 8208bc4b06..1f6e47af8f 100644 --- a/config/locales/client.sq.yml +++ b/config/locales/client.sq.yml @@ -1492,10 +1492,6 @@ sq: title: "Rollback the database to previous working state" confirm: "Are your sure you want to rollback the database to the previous working state?" export_csv: - users: - text: "Export Users" - title: "Export user list in a CSV file." - success: "Export has been initiated, you will be notified shortly with progress." failed: "Export failed. Please check the logs." customize: title: "Personalizo" @@ -1687,8 +1683,6 @@ sq: new: "New" active: "Aktiv" pending: "Pezulluar" - admins: 'Admins' - moderators: 'Mods' suspended: 'Suspended' blocked: 'Blocked' approved: "Aprovuar?" diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml index 9c70d9eeee..f7fe315273 100644 --- a/config/locales/client.sv.yml +++ b/config/locales/client.sv.yml @@ -1422,10 +1422,6 @@ sv: title: "Gör rollback på databasen till ett tidigare fungerande tillstånd." confirm: "Är du säker på att du vill göra rollback på databasen till det tidigare fungerande tillståndet?" export_csv: - users: - text: "Exportera användare" - title: "Exportera användare i en CSV fil." - success: "Exporteringen har påbörjats, du kommer snart att få en notifiering över hur det går." failed: "Exporteringen misslyckades. Kontrollera loggarna." customize: title: "Anpassa" @@ -1587,8 +1583,6 @@ sv: new: "Ny" active: "Aktiv" pending: "Avvaktande" - admins: 'Admins' - moderators: 'Mods' suspended: 'Avstängd' blocked: 'Blockerad' approved: "Godkänd?" diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml index 0bc0f0349f..41b1504912 100644 --- a/config/locales/client.uk.yml +++ b/config/locales/client.uk.yml @@ -1203,8 +1203,6 @@ uk: new: "Нові" active: "Активні" pending: "Очікують" - admins: 'Адміни' - moderators: 'Моди' suspended: 'Призупинені' blocked: 'Заблоковані' approved: "Схвалено?" diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index 6b79753b89..8bff1bc089 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -224,6 +224,12 @@ zh_CN: organisation: 组织 phone: 电话 other_accounts: "其他使用该 IP 地址的账户:" + username: "用户名" + trust_level: "信任等级" + read_time: "阅读时间" + topics_entered: "进入的主题" + post_count: "# 帖子" + confirm_delete_other_accounts: "您确定您想要删除这些账户吗?" user: said: "{{username}}:" profile: "个人资料" @@ -314,14 +320,29 @@ zh_CN: instructions: "背景图片将被居中并且默认宽度为 590px。" email: title: "电子邮箱" + instructions: "绝不会被公开显示" + ok: "我们将邮件跟您确认" + invalid: "请填写正确的电子邮箱地址" + authenticated: "你的电子邮箱已经被 {{provider}} 验证了。" frequency: "只有当您最近一段时间没有访问时,我们才会把您未读过的内容发送到您的电子邮箱。" name: title: "名字" + instructions: "您的全名(可选)" + too_short: "您设置的名字太短了" + ok: "您的名字符合要求" username: title: "用户名" + instructions: "唯一,没有空格,简短" + short_instructions: "其他人可以用 @{{username}} 来提及您" + available: "您的用户名可用" + global_match: "电子邮箱与注册用户名相匹配" global_mismatch: "已被注册。试试 {{suggestion}} ?" not_available: "不可用。试试 {{suggestion}} ?" + too_short: "您设置的用户名太短了" + too_long: "您设置的用户名太长了" checking: "查看用户名是否可用..." + enter_email: '找到用户名;请输入对应电子邮箱' + prefilled: "电子邮箱与注册用户名匹配" locale: title: "界面语言" instructions: "用户界面语言。将在您刷新页面后改变。" @@ -432,6 +453,8 @@ zh_CN: fixed: "载入页面" close: "关闭" assets_changed_confirm: "此网页刚刚更新. 刷新查看新版本?" + logout: "您已登出。" + refresh: "刷新" read_only_mode: enabled: "一个管理员启用了只读模式。您可以继续浏览这个站点但是无法进行交互。" login_disabled: "只读模式下不允许登录。" @@ -1427,10 +1450,6 @@ zh_CN: title: "将数据库回滚到之前的工作状态" confirm: "您确定要将数据库回滚到之前的工作状态吗?" export_csv: - users: - text: "导出用户" - title: "导出用户列表至 CSV 文件。" - success: "导出开始了,您将会看到进度提示。" failed: "导出失败。请检查日志。" customize: title: "定制" @@ -1507,6 +1526,7 @@ zh_CN: settings: "设置" all: "所有" sending_test: "发送测试邮件..." + error: "错误 - %{server_error}" test_error: "发送测试邮件时遇到问题。请再检查一遍邮件设置,确认您的主机没有封锁邮件链接,然后重试。" sent: "已发送" skipped: "跳过" @@ -1622,8 +1642,6 @@ zh_CN: new: "新建" active: "活跃" pending: "待定" - admins: '管理' - moderators: '版主' suspended: '禁止登录' blocked: '禁止参与讨论' approved: "已批准?" diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index f43a899a38..5decf3294d 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -1387,10 +1387,6 @@ zh_TW: title: "回溯資料庫到以前的工作階段" confirm: "你確定要回溯資料庫到以前的工作階段?" export_csv: - users: - text: "匯出用戶" - title: "以CSV格式匯出用戶清單" - success: "開始匯出,你很快就會收到進度通知。" failed: "匯出失敗。請觀看紀錄。" customize: title: "客製化" @@ -1577,8 +1573,6 @@ zh_TW: new: "新用戶" active: "啟用的" pending: "申請中" - admins: '管理員' - moderators: '版主' suspended: '已停權' blocked: '已封鎖' approved: "已批准?" diff --git a/config/locales/server.cs.yml b/config/locales/server.cs.yml index 34284c598f..7085343663 100644 --- a/config/locales/server.cs.yml +++ b/config/locales/server.cs.yml @@ -6,6 +6,16 @@ # https://www.transifex.com/projects/p/discourse-org/ cs: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "D. MMM" short_date: "D. MMM, YYYY" @@ -20,8 +30,20 @@ cs: posts: "příspěvky" loading: "Nahrávám" powered_by_html: 'Systém běží na Discourse, nejlepší zážitek je se zapnutým JavaScriptem' + log_in: "Přihlásit se" via: "%{username} přes %{site_name}" is_reserved: "je rezervováno" + purge_reason: "Automaticky smazáno: staré a neověřené" + disable_remote_images_download_reason: "Stahování obrázků z cizích serverů bylo vypnuto protože na disku není dostatek místa." + errors: + messages: + too_long_validation: "je limitováno na %{max} znaků; zadali jste %{length}." + invalid_boolean: "Nevalidní boolean." + taken: "už je zabráno" + embed: + load_from_remote: "Při načítání příspěvku nastala chyba." + bulk_invite: + file_should_be_csv: "Nahraný soubrou by měl být ve formátu csv nebo txt." backup: operation_already_running: "Právě probíhá operace %{operation}. V tuto chvíli nelze zahájit novou operaci %{operation}." backup_file_should_be_tar_gz: "Záloha by měla být archiv s příponou .tar.gz." @@ -33,6 +55,8 @@ cs: few: "Je nám líto, ale noví uživatelé jsou dočasně omezeni na %{count} odpovědi v tématu." other: "Je nám líto, ale noví uživatelé jsou dočasně omezeni na %{count} odpovědí v tématu." embed: + start_discussion: "Začít diskuzi" + continue: "Pokračovat v diskuzi" more_replies: one: "1 další odpověď" few: "%{count} další odpověďi" @@ -40,6 +64,7 @@ cs: loading: "Načítám diskuzi..." permalink: "Permalink" imported_from: "Diskuze k původnímu příspěvku na blogu: %{link}" + in_reply_to: "▶ %{username}" replies: one: "1 reakce" few: "%{count} reakce" @@ -89,6 +114,7 @@ cs: errors: can_not_modify_automatic: "Automatickou skupinu nelze upravit" default_names: + everyone: "kdokoliv" admins: "admins" moderators: "moderators" staff: "staff" @@ -101,6 +127,26 @@ cs: one: "příspěvek" few: "%{count} příspěvky" other: "%{count} příspěvků" + new-topic: | + Vítejte na %{site_name} — **díky za vytvoření nové konverzace!** + + - Popisuje vybraný název nové téma přesně? Zní dostatečně zajímavě? + + - O čem to je? Proč by mělo lidi zajímat? V jaké odpovědi od komunity doufáte? + + - Používejte běžná slova podle kterých ostatní budou toto téma najít při **vyhledávání**. Pro lepší zařazení vyberte i kategorii. + + Další doporučení máme v [pravidlech komunity](/guidelines). Tenhle panel se zobrazí pouze pro vaše první %{education_posts_text}. + new-reply: | + Vítejte na %{site_name} — **díky za přispívání!** + + - Přispívá nějak vaše odpověď k diskuzi? + + - Buďte na ostatní členy komunity milí. + + - Konstruktivní kritika je vítaná, ale kritizujte *myšlenky*, ne lidi. + + Další doporučení máme v [pravidlech komunity](/guidelines). Tenhle panel se zobrazí pouze pro vaše první %{education_posts_text}. avatar: | ### How about a new picture for your account? @@ -129,6 +175,12 @@ cs: We're sorry, but new users are temporarily limited to %{newuser_max_replies_per_topic} replies in the same topic. Instead of adding another reply, please consider editing your previous replies, or visiting other topics. + reviving_old_topic: | + ### Oživit téma? + + Poslední odpověď na tohle téma je přes %{days} dní stará. Vaše odpověď vyšvyhne toto téma v seznamu nahoru a uporozní všechny kdo se tohoto tématu účastní. + + Určitě chcete pokračovat v této staré konverzaci? activerecord: attributes: category: @@ -144,10 +196,15 @@ cs: models: topic: attributes: + base: + warning_requires_pm: "Varování můžete přidat pouze k soukromým zprávám." + too_many_users: "Varování můžete najednou poslat pouze jednomu uživateli." archetype: cant_send_pm: "Bohužel, tomuto uživateli nemůžete poslat soukromou zprávu." user: attributes: + password: + common: "je jedno z 10 000 nejčastějších hesel. Použijte prosím bezpečnější heslo." ip_address: signup_not_allowed: "Registrace z této adresy není povolena." color_scheme_color: @@ -157,12 +214,31 @@ cs: user_profile: no_info_me: "
    Pole 'o mně' je v tuto chvíli prázdné, nechcete si ho vyplnit?
    " no_info_other: "
    %{name} o sobě zatím žádné informace nevyplnil
    " + vip_category_name: "VIP" vip_category_description: "Kategorie je přístupná výhradně členům s důvěryhodností 3 a vyšší." + meta_category_name: "Meta" + meta_category_description: "Diskutuje tento web, jeho organizaci, jak funguje a jak ho vylepšit." + staff_category_name: "Redakce" staff_category_description: "Privátní kategorie pro diskuze štábu. Témata jsou viditelná pouze pro moderátory a správce." + assets_topic_body: "Tohle je permanentí téma, viditelné pouze redaktorům, pro uchovávání obrázků a souborů použitých v designu tohoto webu. Nemažte ho!\n\n\nTady je jak:\n\n1. Odpovězte na toto téma.\n2. Nahrajte sem všechny obrázky které chcete používat pro loga, favicony a podobně. (Použijte ikonku „nahrát“ v panelu nad editorem, nebo obrázky rovnou přetáhněte nebo „vložte“)\n3. Pošlete tuto odpověď\n4. Klikněte pravým tlačítkem na obrázky ve vašem novém příspěvku a zkopírujte si adresy, nebo klikněte na úpravu přípěvku a adresy vykopírujte tam.\n5. Vložte tyto adresy do [základního nastavení](/admin/site_settings/category/required).\n\nPokud potřebujete nahrát jiné soubory, změňte nastavení `authorized_extensions` v [nastavení souborů](/admin/site_settings/category/files)." + lounge_welcome: + title: "Vítejte v Redakci" category: topic_prefix: "Definice kategorie pro %{category}" replace_paragraph: "[Nahraďte tento první odstavec krátkých popisem nové kategorie. Zkuste se vejít do 200 znaků.]" post_template: "%{replace_paragraph}\\\n\\\nPoužijte toto místo níže pro delší popis a stanovení pravidel diskuze!\n" + errors: + uncategorized_parent: "„Bez kategorie“ nemůže mít nadřazenou kategorii" + self_parent: "Nadřazená kategorie nemůže zároveň být podkategorie" + depth: "Pokategorie se nedají vnořovat" + cannot_delete: + uncategorized: "„Bez kategorie“ nejde smazat" + has_subcategories: "Tato kategorie nejde smazat protože obsahuje podkategorie." + topic_exists: + one: "Tato kategorie nejde smazat protože obsahuje 1 téma: %{topic_link}." + few: "Tato kategorie nejde smazat protože obsahuje %{count} témata. Nejstarší téma je %{topic_link}." + other: "Tato kategorie nejde smazat protože obsahuje %{count} témat. Nejstarší téma je %{topic_link}." + topic_exists_no_oldest: "Kategorii nelze smazat protože počet příspěvků je %{count}." trust_levels: newuser: title: "nový uživatel" @@ -175,6 +251,7 @@ cs: elder: title: "vůdce" rate_limiter: + slow_down: "Provedli jste tuhle akci moooockrát, zkuste to později" too_many_requests: "Děláte tuto akci příliš často. Prosím počkejte %{time_left} a zkuste to znovu." hours: one: "1 hodina" @@ -293,24 +370,35 @@ cs: continue: "Pokračovat na %{site_name}" change_email: confirmed: "Vaše emailová adresa bylo změněna." + please_continue: "Pokračovat na %{site_name}" error: "Nastala chyba běhěm změny emailové adresy. Není nová adresa již někým používána?" activation: + action: "Aktivovat účet" already_done: "Bohužel, tento odkaz pro aktivaci účtu již není platný. Není váš účet jíž aktivní?" + please_continue: "Váš účet je aktivovaný; budete přesměrování na výchozí stránku." + continue_button: "Pokračovat na %{site_name}" welcome_to: "Vítejte na %{site_name}!" approval_required: "Váš nový účet musí být schválen moderátorem, než budete moci používat fórum. Jakmile se tak stane, budete informováni emailem." post_action_types: off_topic: title: 'Off-Topic' + description: 'Tento příspěvek se nevztahuje k diskuzi definované názvem a obsahem prvního příspěvku a měl by asi být přesunut jinam.' long_form: 'označil příspěvek jako off-topic' spam: title: 'Spam' + description: 'Tento příspěvek je reklama. Není užitečný ani relevantní k tématu.' long_form: 'označeno jako spam' inappropriate: title: 'Nevhodné' long_form: 'nahlášeno jako nevhodné' notify_user: + description: 'Tato zpráva obsahuje něco o čem bych s autorem rád mluvil přímo a soukromě. Nevytvoří varování moderátorům.' + email_title: 'Váš příspěvek v „%{title}“' email_body: "%{link}\\\n\\\n%{message}\n" notify_moderators: + title: "Něco jiného" + description: 'Tento příspěvek vyžaduje pozornost moderátor z důvodů nevypsaných výše.' + long_form: 'nahlášeno moderátorům' email_title: 'Příspěvek v tématu "%{title}" vyžaduje pozornost moderátora' email_body: "%{link}\\\n\\\n%{message}\n" bookmark: @@ -332,18 +420,26 @@ cs: long_form: 'označeno jako spam' inappropriate: title: 'Nevhodné' + description: 'Obsah tohoto příspěvku by rozumný člověk shledal urážlivý, hrubý nebo v rozporu s pravidly komunity.' long_form: 'nahlášeno jako nevhodné' notify_moderators: + title: "Něco jiného" + description: 'Toto téma vyžaduje pozornost moderátorů na základě pravidel komunity, pravidel použítí, nebo z jiného výše neuvedeného důvodu.' email_title: 'The topic "%{title}" requires moderator attention' email_body: "%{link}\\\n\\\n%{message}\n" archetypes: regular: title: "Běžné téma" + banner: + message: + make: "Tento příspěvek je nyní banner. Bude se zobrazovat na vršku každé stránky dokud ho uživatel nezavře." + remove: "Tento příspěvek už není banner. Nebude se dále zobrazovat na vršku každé stránky." unsubscribed: title: 'Odhlášen' description: "Byli jste odhlášeni ze seznamu. Již vás nebudeme kontaktovat." oops: "Pokud jste tuto akci provedli omylem, klikněte na odkaz níže." error: "Chyba při odhlašování" + preferences_link: "Také se můžete z souhrných emailů odhlásit na stránce nastavení" not_found_description: "Bohužel jsme vás nemohli odhlásit ze seznamu. Je možné, že váš odkaz z emailu již není platný." resubscribe: action: "Znovu přihlásit" @@ -561,6 +657,9 @@ cs: most_posts: "Více příspěvků" most_recent_poster: "Poslední zasilatel" frequent_poster: "Častý zasilatel" + redirected_to_top_reasons: + new_user: "Vítejte v naší komunitě! Tohle jsou poslední populární témata." + not_seen_in_a_month: "Vítejte zprátky! Chvíli jsme se neviděli. Tohle jsou nejpopulárnější témata od vaší poslední návštěvy." move_posts: new_topic_moderator_post: one: "Přesunul jsem příspěvek do nového tématu: %{topic_link}" @@ -601,6 +700,12 @@ cs: blocked: "není povolen." ip_address: blocked: "is blocked." + invite_password_instructions: + text_body_template: | + Díky za potvrzení pozvánky na %{site_name} - vítejte! + + Pro další přihlášení, klikněte na následující odkaz a nastavte si heslo: + %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Zkušební email" new_version_mailer_with_notes: @@ -608,10 +713,99 @@ cs: system_messages: post_hidden: subject_template: "%{site_name} Oznámení: Příspěvek skryt kvůli hlášením od komunity" + usage_tips: + text_body_template: | + Tahle soukromá zpráva obsahuje pár tipů jak začít. + + ## Roluj dolů + + Nejsou zde žádné šipky na další stránky - když chcete číst víc, **prostě rolujte dolů!** + + Nové odpovědi se zobrazí automaticky jak budou přibývat. + + ## Kde jsem? + + - K vyhledávání, zobrazení vašeho profilu nebo pro další možnosti vyberte **ikonková tračítka v pravém horním rohu**. + + - Název tématu vás vezme na poslední nepřečtený příspěvek. Kliknutím na čas poslední aktivity nebo na počet příspěvků se přesunete na začátek nebo konec vlákna. + + - Při čtení tématu můžete přeskočit nahoru ↑ kliknutím na název v hlavičce. Kliknutím na zelenou listu otevřete další možnosti navigace. A taky můžete použít klávesy home a end. + + + + ## Jak odpovídat? + + - Pokud chcete přidat další příspěvek do diskuze, klikněte na tlačítko Opovědi úplně na konci stránky. + + - Nebo můžete odpovědět na konkrétní příspěvěk - to klikněte na tlačítko Odpovědi u konkrétního příspěvku. + + - Pokud chcete konverzaci odvést jinam, použijte tlačítko Opovědět v propojeném tématu . + + Citovat ve své odpovědi můžete tak, že něčí text vyberete a kliknete na tlačítko Odpověď s citací. + + + + To ping someone in your reply, mention their name. Type `@` and an autocompleter will pop up. + Upozornit někoho na vaši odpověď můžete zmíněním jejich jména. Napište `@` a zobrazí se doplňovač se všemy jmény. + + + + Smajlíky ([standartní Emoji](http://www.emoji.codes/)), napište `:` a zobrazí se doplňovač. A nebo používejte tradiční smajlíky `:)` :smile: + + ## Co můžu dělat dát? + + U každého příspěvku je několik tlačítek. + + + + Nechat někoho vědět, že s jeho příspěvkem souhlasíte a nebo vás třeba pobavil, můžete tlačítkem **to se mi líbí**. Naopak pokud je příspěvek problematický, kliknutím na vlaječku ho můžete soukromě **nahlásit** moderátorům. + + You can also **share** a link to a post, or **bookmark** it for later reference on your user page. + Také můžete **sdílet** odkaz na příspěvek nebo si ho dát do **záložek** na později. + + ## Kdo to ke mě mluví? + + Když někdo odpoví na váš příspěvek, cituje vás nebo vás zmíní `@jmenem`, na vrchu stránky se okamžitě zobrazí číslo. Můžete tím otevřít svoje **uporoznění**. + + + + Nestrachujte se že propásnete nějakou odpověď - pokud nejste zrovna online, na přímé odpovědi (a soukromé zprávy) dostanete upozornění emailem. + + ## Kdy jsou konverzace nové? + + Obyčejně jsou za nové považovány všechny konverzace mladší než 2 dny. Některé konverzace (vámi vytvořené, s odpovědí, nebo ty které jste dlouho četli) se nastaví jako „hlídané“. + + U takovýhle témat uvidíte modré „nové“ a číslo nových příspěvků: + + + + Stavy témat můžete nastavit přepínačem na spodku stránky (a dají se nastavit i pro celé kategorie). Pro nová témata můžete taky pravidla [upravit v nastavení](/my/preferences). + + ## Proč něco dělat nemůžu? + + Noví uživatelé mají z bezpečnostních důvodů trochu omezená práva. Čím víc se do komunity zapojíte, tím větší důvěryhodnost získáte a všechny omezení se automaticky odstraní. Na dostatečně vysoké [důvěryhodnosti](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) můžete získat i moderátorská oprávnění. welcome_user: subject_template: "Vítejte na %{site_name}!" welcome_invite: subject_template: "Vítejte na %{site_name}!" + text_body_template: | + Díky za přijmutí pozvánky na %{site_name} -- vítejte! + + Automaticky jsme vám vytvořili uživatelské jméno **%{username}**, ale můžete si ho změnit [na svém profilu][prefs]. + + Pro příští přihlášení, buď: + + 1. Přihlaste se libovolnou metodou -- ale musíte použít stejnou **emailovou adresu** na kterou jste dostali pozvánku. Jinak nedokážeme rozpoznat, že jste to vy. + + 2. V [nastavení][prefs] si vyplňte heslo, které budeme moct pro přihlášení použít. + + %{new_user_tips} + + Věříme v [civilizovanou komunitu](%{base_url}/guidelines). + + Užijte si to tu! + + [prefs]: %{user_preferences_url} too_many_spam_flags: subject_template: "Účet zablokován" blocked_by_staff: @@ -733,6 +927,21 @@ cs: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "Byli jste schváleni na %{site_name}!" + text_body_template: | + Vítejte na %{site_name}! + + Moderátor potvrdil vaši registraci na %{site_name}. + + Na následujícím odkazu můžete aktivovat váš nový účet: + %{base_url}/users/activate-account/%{email_token} + + Jestli se vám na odkaz nedaří kliknout, zkopírujte si ho a vložte ho do adresní řádky internetového prohlížeče. + + %{new_user_tips} + + Věříme v [civilizovanou komunitu](%{base_url}/guidelines). + + Užijte si to tu! signup: subject_template: "[%{site_name}] Aktivujte svůj nový účet" text_body_template: | @@ -744,6 +953,10 @@ cs: Pokud tento odkaz nefunguje, zkuste ho zkopírovat a vložit přes schránku do adresního řádku vaše webového prohlížeče. page_not_found: title: "Stránka, kterou žádáte, na tomto diskuzním fóru neexistuje. Možná vám můžeme pomoci ji najít, nebo poradit jiné téma, které se vám může líbit:" + login_required: + welcome_message: | + #[Vítejte na %{title}](#welcome) + Je vyžadován uživatelský účet. Prosím vytvořte si nový účet nebo se přihlaste. terms_of_service: signup_form_message: 'I have read and accept the Terms of Service.' deleted: 'smazáno' @@ -772,3 +985,86 @@ cs: message_to_blank: "message.to is blank" text_part_body_blank: "text_part.body is blank" body_blank: "body is blank" + privacy_topic: + body: | + + + ## [Jaké informace sbíráme?](#collect) + + Sbíráme informace od vaší registrace a shromažďujeme data o tom když se účastníte chodu fóra čtením, přispíváním a hodnocením zdejšího obsahu. + + Při registraci na náš web můžete být požádání o jméno a emailovou adresu. Můžete ale tento web navštěvovat i bez registrace. Vaše emailová adresa bude ověřena emailem obsahujícím unikátní odkaz. Po návštěvě tohoto odkazu víme, že spravujete onu emailovou adresu. + + Při registraci a při přispívání uchováváme IP adresu ze které byl požadavek odeslán. Dále také můžeme mít na serveru logy které uchovávají IP adresu každého požadavku na tento server. + + + + ## [Na co vaše informace používáme?](#use) + + Každá informace kterou shromažďujeme může být použivate následovně: + + * Přizpůsobení webu — vaše informace nám pomáhá lépe upravit fórum pro vaše osobní potřeby. + * Zdokonalování našeho webu — neustále se na základě vaší odezvy snažíme náš web vylepšovat. + * Lepší uživatelská podpora — vaše informace nám pomáhají efektivněji řešit uživatelskou podporu. + * Odesílání periodických emailů — Emailová adresa kterou nám poskytnete může být použita k tomu, že vám odešleme informace a nebo upozornění na změny na webu a nebo akce ostatních uživatelů nebo další otázky, požadavky nebo odpovědi. + + + + ## [Jak chráníme vaše informace?](#protect) + + Zavedli jsme celou řadu bezpečnostních opatření abychom zajistili bezpočnost vašich soukromých informací vždy když vkládáte, odesíláte nebo čtete vaše soukromé informace. + + + + ## [Jaká je vaše politika uchovávání osobních údajů?](#data-retention) + + Snažíme se: + + * Uchovávat logy obsahující IP adresu všech požadavků na tento server méně než 90 dní. + * Uchovávat IP adresu asociovanou s registrovaným uživatelem a jejich příspěvky méně než 5 let. + + + + ## [Používáme cookies?](#cookies) + + Samozřejmě. Cookies jsou miniaturní soubory, které stránka nebo její provozovatel vytvoří na vašem počítači skrz webový prohlížeč (pokud to ten povolí). Tato cookies umožňují rozpoznat váš prohlížeč a, pokud máte registrovaný účet, asociovat vás s registrovaným účtem. + + Používáme cookie k pochopení a uložení vašich preferencí pro budoucí návštěvi a kompilujeme souhrná data o provozu na webu a interakcí s webem tak, že můžeme nabídnout lepší uživatelský zážitek a nástroje. Můžeme tato souhrnná data poskytnout třetí straně, která nám pomůže lépe porozumět uživatelům našeho webu. Tyto třetí strany nemají povoleno tyto souhrné informace využívat kromě toho, když nám pomáhají vylepšovat náš web. + + + + ## [Prozrazujeme libovolné informace cizím stranám?](#disclose) + + Neprodáváme, neobchodujeme s, ani jinak nepřenášíme cizím stranám vaše osobní údaje. Toto se nevztahuje na důvěryhodné třetí strany které nám pomáhají provozovat tento web, vést podnikání, dokud tyto strany souhlasí, že vaše osobní data zůstanou důvěrná. Můžeme také uvolnit informace v důsledku dodržování zákonů, pro vynucování politiky tohoto webu, k ochranně našich nebo cizích práv, majetku nebo bezpečnosti. Neosobní informace ale můžeme poskytnout třetím stranám pro účely marketingu, reklamy nebo další použití. + + + + ## [Odkazy třetích stran](#third-party) + + Podle našeho uvážení můžeme příležitostně na tomto webu zobrazovat produkty a služby třetích stran. Tyto třetí strany mohou mít jiné a nezávislé zásady ochrany osobních údajů. Nemáme tedy žádnou odpovědnost ani ručení za obsah těchto stran. Snažíme se nicméně ochraňovat integritu tohoto webu a vítáme zpětnou vazbu o obsahu cizích stran na tomto webu. + + + + ## [Children's Online Privacy Protection Act Compliance](#coppa) + + Tento web, produkty a služby jsou určeny pro lidi staré alespoň 13 let. Pokud je tento server v USA a jste mladší než 13 let, nesmíte podle požadavků COPPA ([Children's Online Privacy Protection Act](http://en.wikipedia.org/wiki/Children)) tento web používat. + + + + ## [Pouze online zásady](#online) + + Tyto zásady ochrany osobních údajů jsou platné pouze online a nevztahují se na informace sbírané offline. + + + + ## [Váš souhlas](#consent) + + Používáním tohoto webu souhlasíte s těmito zásadami ochrany osobních údajů. + + + + ## [Změny v zásadách ochrany osobních údajů](#changes) + + Pokud se rozhodneme změnit naše zásady ochrany osobních údajů, oznámíme tyto změny na této stránce. + + Tento text je pod licencí CC-BY-SA. Naposledy byl změněn 22. listopadu 2014. diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index afba5332fc..c2e4033be9 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -33,6 +33,7 @@ de: log_in: "Anmelden" via: "%{username} via %{site_name}" is_reserved: "ist reserviert" + purge_reason: "Automatisch gelöscht, da alt und ungeprüft" disable_remote_images_download_reason: "Da nicht mehr genug Plattenplatz vorhanden ist, wurde der Download von Bildern deaktiviert." errors: messages: @@ -345,12 +346,14 @@ de: activation: action: "Aktiviere dein Benutzerkonto" already_done: "Entschuldige, dieser Link zur Aktivierung des Benutzerkontos ist nicht mehr gültig. Ist dein Konto schon aktiviert?" + please_continue: "Dein neuer Account ist jetzt bestätigt; du wirst auf die Startseite weitergeleitet." continue_button: "Weiter zu %{site_name}" welcome_to: "Willkommen bei %{site_name}!" approval_required: "Bevor Du auf das Forum zugreifen kannst, muss Dein neues Konto noch von einem Moderator genehmigt werden. Du erhälst eine E-Mail, sobald dies geschehen ist!" post_action_types: off_topic: title: 'Am Thema vorbei' + description: 'Dieser Beitrag hat nichts mit dem Thema zu tun wie es im Titel und ersten Beitrag steht. Deshalb sollte er woanders hin verschoben werden.' long_form: 'dies als am Thema vorbei gemeldet' spam: title: 'Werbung' @@ -362,10 +365,13 @@ de: long_form: 'dies als unangemessen gemeldet' notify_user: title: 'Private Nachricht an {{username}}' + description: 'Dieser Beitrag enthält etwas, worüber ich mit dem Autor direkt und persönlich reden möchte. Wird nicht gemeldet.' + long_form: 'privat angeschriebener Benutzer' email_title: 'Dein Beitrag in „%{title}“' email_body: "%{link}\n\n%{message}" notify_moderators: title: "Irgendetwas anderes" + description: 'Auf diesen Beitrag sollte aus einem anderen Grund ein Moderator aufmerksam gemacht werden.' email_title: 'Der Beitrag "%{title}" sollte von einem Moderator begutachtet werden' email_body: "%{link}\n\n%{message}" bookmark: @@ -374,7 +380,7 @@ de: long_form: 'ein Lesezeichen auf diesen Beitrag gesetzt' like: title: 'Gefällt mir' - description: 'Dieser Beitrag gefällt mit' + description: 'Dieser Beitrag gefällt mir' long_form: 'dies gefällt mit' vote: title: 'Abstimmung' @@ -416,7 +422,7 @@ de: visits: title: "Nutzerbesuche" xaxis: "Tag" - yaxis: "Zahl der Besuche" + yaxis: "Anzahl der Besuche" signups: title: "Benutzer" xaxis: "Tag" @@ -436,43 +442,43 @@ de: flags: title: "Meldungen" xaxis: "Tag" - yaxis: "Zahl der Meldungen" + yaxis: "Anzahl der Meldungen" bookmarks: title: "Lesezeichen" xaxis: "Tag" - yaxis: "Zahl der Lesezeichen" + yaxis: "Anzahl der Lesezeichen" starred: title: "Favoriten" xaxis: "Tag" - yaxis: "Zahl der Favoriten" + yaxis: "Anzahl der Favoriten" users_by_trust_level: title: "Benutzer nach Stufe" xaxis: "Stufe" - yaxis: "Zahl der Nutzer" + yaxis: "Anzahl der Nutzer" emails: title: "Gesendete E-Mails" xaxis: "Tag" - yaxis: "Zahl der E-Mails" + yaxis: "Anzahl der E-Mails" user_to_user_private_messages: title: "Benutzer-zu-Benutzer" xaxis: "Tag" - yaxis: "Zahl der privaten Nachrichten" + yaxis: "Anzahl privater Nachrichten" system_private_messages: title: "System" xaxis: "Tag" - yaxis: "Zahl der privaten Nachrichten" + yaxis: "Anzahl privater Nachrichten" moderator_warning_private_messages: title: "Warnungen von Moderatoren" xaxis: "Tag" - yaxis: "Zahl der privaten Nachrichten" + yaxis: "Anzahl privater Nachrichten" notify_moderators_private_messages: title: "Meldungen an Moderatoren" xaxis: "Tag" - yaxis: "Zahl der privaten Nachrichten" + yaxis: "Anzahl privater Nachrichten" notify_user_private_messages: title: "Meldungen an Benutzer" xaxis: "Tag" - yaxis: "Zahl der privaten Nachrichten" + yaxis: "Anzahl privater Nachrichten" top_referrers: title: "Top Referrers" xaxis: "Benutzer" @@ -572,12 +578,12 @@ de: download_remote_images_to_local: "Lade eine Kopie von extern gehosteten Bildern herunter und ersetze Links in Beiträgen entsprechend; dies verhindert defekte Bilder." download_remote_images_threshold: "Minimal benötigter freier Festplattenspeicher um externe Bilder lokal herunterzuladen (in Prozent)" disabled_image_download_domains: "Liste von Domänen, von denen verlinkte Bilder niemals heruntergeladen werden sollen." - ninja_edit_window: "Anzahl Sekunden nach Erstellen eines Beitrags, während der Änderungen am Beitrag nicht im Bearbeitungsverlauf gespeichert werden." + ninja_edit_window: "Für (n) Sekunden wird nach dem Bearbeiten keine neue Revision im Beitragsverlauf angelegt." post_edit_time_limit: "Der Verfasser eines Beitrags kann diesen nur für (n) Minuten nach Absenden des Beitrags bearbeiten. 0 deaktiviert diese Beschränkung." edit_history_visible_to_public: "Erlaube jedem, vorherige Versionen eines bearbeiteten Beitrags zu sehen. Wenn deaktiviert sind diese nur für Mitarbeiter sichtbar." max_image_width: "Maximale Breite von Thumbnails von Bildern in einem Beitrag." max_image_height: "Maximale Höhe von Vorschaubildern in einer Nachricht" - category_featured_topics: "Zahl der angezeigten Themen je Kategorie auf der Kategorieseite /categories." + category_featured_topics: "Anzahl der angezeigten Themen je Kategorie auf der Kategorieseite /categories. Nachdem dieser Wert geändert wurde, dauert es bis zu 15 Minuten bis die Kategorieseite aktualisiert ist." show_subcategory_list: "Zeige Liste von Unterkategorien statt einer Liste von Themen wenn eine Kategorie ausgewählt wird." fixed_category_positions: "Falls aktiviert können Kategorien in einer fest vorgegebenen Reihenfolge angeordnet werden. Andernfalls werden Kategorien nach Aktivität sortiert aufgelistet." add_rel_nofollow_to_user_content: "Füge mit Ausnahme interner Links allen nutzergenerierten Inhalten 'rel nofollow' hinzu (inkludiert übergeordnete Domains). Die Änderung dieser Einstellung erfordert, dass Du sämtliche Markdown-Beiträge aktualisierst." @@ -602,7 +608,7 @@ de: anon_polling_interval: "Pollingintervall in Millisekunden für anonyme Clients." auto_track_topics_after: "Millisekunden, bevor ein Thema automatisch verfolgt wird. Nutzer können diesen Wert überschreiben (0 heißt immer, -1 heißt niemals)." new_topic_duration_minutes: "Minuten, die ein neues Thema als neu aufgeführt wird. Nutzer können diesen Wert überschreiben (-1 heißt immer, -2 heißt seit dem letzten Besuch)." - flags_required_to_hide_post: "Zahl der Meldungen, die dazu führen, dass ein Beitrag automatisch versteckt und eine private Nachricht an den Nutzer geschickt wird (0 heißt niemals)." + flags_required_to_hide_post: "Anzahl der Meldungen, die dazu führen, dass ein Beitrag automatisch versteckt und eine private Nachricht an den Nutzer geschickt wird (0 für niemals)." cooldown_minutes_after_hiding_posts: "Minuten, die ein Nutzer warten muss, bevor ein Beitrag, der wegen Meldungen anderer Nutzer versteckt wurde, bearbeitet werden kann." max_topics_in_first_day: "Maximale Anzahl an Themen, die ein Benutzer an seinem ersten Tag auf der Seite erstellen kann" max_replies_in_first_day: "Maximale Anzahl an Antworten, die ein Benutzer an seinem ersten Tag auf der Seite erstellen kann" @@ -625,11 +631,12 @@ de: post_menu_hidden_items: "Die Einträge im Menü eines Beitrags, die standardmäßig hinter einer erweiterbaren Ellipse versteckt werden sollen." share_links: "Bestimme, welche Dienste in welcher Reihenfolge im Teilen-Dialog auftauchen." track_external_right_clicks: "Verfolge, welche externen Links per Rechtsklick geöffnet werden (zum Beispiel in einem neuen Browser-Tab). Standardmäßig deaktiviert, da dies URL-Rewrites erfordert." - topics_per_page: "Zahl der Themen, die auf der Themen-Übersichtsseite zu Beginn und beim Scrollen geladen werden." + topics_per_page: "Anzahl der Themen, die auf der Themen-Übersichtsseite zu Beginn und beim Scrollen geladen werden." send_welcome_message: "Sende allen neuen Nutzern eine private Willkommensnachricht mit Hinweisen zur Benutzung des Forums." suppress_reply_directly_below: "Zeige die erweiterbare Anzahl der Antworten auf einen Beitrag nicht, falls die einzige Antwort direkt darunter folgt." suppress_reply_directly_above: "Verstecke das erweiterbare „Antwort auf“-Feld in einem Beitrag, wenn der beantwortete Beitrag direkt darüber angezeigt wird." suppress_reply_when_quoting: "Verstecke das erweiterbare „Antwort auf“-Feld in einem Beitrag, wenn der Beitrag den beantworteten Beitrag zitiert." + max_reply_history: "Maximale Anzahl an Antworten beim Ausklappen von in-reply-to" topics_per_period_in_top_summary: "Anzahl der Themen, die in der Top-Themübersicht angezeigt werden." topics_per_period_in_top_page: "Anzahl der Themen, die in der mit \"Mehr zeigen\" erweiterten Top-Themenübersicht angezeigt werden." redirect_users_to_top_page: "Verweise neue und länger abwesende Nutzer automatisch zur Top Übersichtsseite" @@ -684,14 +691,14 @@ de: rate_limit_create_post: "Nach Schreiben eines Beitrags muss ein Nutzer (n) Sekunden warten, bevor ein weiterer Beitrag erstellt werden kann." rate_limit_new_user_create_topic: "Nach Erstellen eines Themas muss ein neuer Nutzer (n) Sekunden warten, bevor ein weiteres Thema erstellt werden kann." rate_limit_new_user_create_post: "Nach Schreiben eines Beitrags muss ein neuer Nutzer (n) Sekunden warten, bevor ein weiterer Beitrag erstellt werden kann." - max_likes_per_day: "Maximale Zahl der „Gefällt mir“ pro Nutzer pro Tag." - max_flags_per_day: "Maximale Zahl der Meldungen pro Nutzer pro Tag." - max_bookmarks_per_day: "Maximale Zahl der Lesezeichen pro Nutzer pro Tag." - max_edits_per_day: "Maximale Zahl der Bearbeitungen pro Nutzer pro Tag." + max_likes_per_day: "Maximale Anzahl der Likes pro Nutzer pro Tag." + max_flags_per_day: "Maximale Anzahl der Meldungen pro Nutzer pro Tag." + max_bookmarks_per_day: "Maximale Anzahl der Lesezeichen pro Nutzer pro Tag." + max_edits_per_day: "Maximale Anzahl der Bearbeitungen pro Nutzer pro Tag." max_stars_per_day: "Maximale Anzahl der Themen, die ein User pro Tag favorisieren kann." - max_topics_per_day: "Maximale Zahl der Themen, die ein Nutzer pro Tag erstellen kann." + max_topics_per_day: "Maximale Anzahl der Themen, die ein Nutzer pro Tag erstellen kann." max_private_messages_per_day: "Maximale Zahl privater Nachrichten, die ein Nutzer pro Tag verschicken kann." - suggested_topics: "Zahl der empfohlenen Themen am Ende eines Themas." + suggested_topics: "Anzahl der empfohlenen Themen am Ende eines Themas." limit_suggested_to_category: "Zeige nur Themen der aktuellen Kategorie in vorgeschlagenen Themen." clean_up_uploads: "Lösche verwaiste Uploads, um illegales Hosting zu vermeiden. ACHTUNG: Du solltest ein Backup deines /uploads Verzeichnisses erstellen, bevor Du diese Funktion aktivierst." clean_orphan_uploads_grace_period_hours: "Frist (in Stunden) bevor ein verwaister Upload entfernt wird." @@ -705,12 +712,12 @@ de: default_trust_level: "Standardwert für die Vertrauensstufe von neuen Nutzern (0-4)." min_trust_to_create_topic: "Die minimale Vertrauensstufe wird benötigt um eine neues Thema zu erstellen." min_trust_to_edit_wiki_post: "Die minimal benötigte Vertrauensstufe, um als Wiki markierte Beiträge bearbeiten zu können." - newuser_max_links: "Maximale Zahl der Links, die neue Benutzer Beiträgen hinzufügen dürfen." - newuser_max_images: "Maximale Zahl der Bilder, die neue Benutzer Beiträgen hinzufügen dürfen." - newuser_max_attachments: "Maximale Zahl der Dateien, die neue Benutzer Beiträgen hinzufügen dürfen." - newuser_max_mentions_per_post: "Maximale Zahl der @Namens-Erwähnungen, die neue Benutzer in Beiträgen nutzen dürfen." + newuser_max_links: "Maximale Anzahl der Links, die neue Benutzer Beiträgen hinzufügen dürfen." + newuser_max_images: "Maximale Anzahl der Bilder, die neue Benutzer Beiträgen hinzufügen dürfen." + newuser_max_attachments: "Maximale Anzahl der Dateien, die neue Benutzer Beiträgen hinzufügen dürfen." + newuser_max_mentions_per_post: "Maximale Anzahl der @Namens-Erwähnungen, die neue Benutzer in Beiträgen nutzen dürfen." newuser_max_replies_per_topic: "Maximale Anzahl an Antworten, die ein neuer Benutzer in einem einzigen Thema geben darf, bevor jemand auf diese antwortet." - max_mentions_per_post: "Maximale Zahl der @Namens-Erwähnungen, die jemand in einem Beitrag nutzen kann." + max_mentions_per_post: "Maximale Anzahl der @Namens-Erwähnungen, die jemand in einem Beitrag nutzen kann." create_thumbnails: "Erzeuge ein Vorschaubild und eine Lightbox für Bilder, die zu groß sind, um in einem Beitrag angezeigt zu werden." email_time_window_mins: "Warte (n) Minuten, bevor Nutzern eine Hinweis-E-Mail geschickt wird, um ihnen Gelegenheit zu geben, ihre Beiträge abschließend bearbeiten zu können." email_posts_context: "Anzahl der Antworten welche als Konext einer Notifikations-Mail hinzugefügt werden." @@ -754,6 +761,7 @@ de: logout_redirect: "Ziel für Weiterleitung nach einem Logout (z. B.: http://somesite.com/logout)" allow_uploaded_avatars: "Erlaube das Hochladen benutzerdefinierter Avatare." allow_animated_avatars: "Erlaube den Benutzern animierte GIFs als Avatar zu benutzen. ACHTUNG: Wenn Du diese Einstellung änderst, solltest Du den Rake-Task avatars:refresh ausführen." + allow_animated_thumbnails: "Generiert animierte Vorschaubilder von animierten gifs." automatically_download_gravatars: "Avatare von Gravatar herunterladen, wenn ein Nutzer sich registriert oder seine E-Mail-Adresse ändert." digest_topics: "Maximale Anzahl von Themen, die in der E-Mail-Zusammenfassung angezeigt werden." digest_min_excerpt_length: "Minimale Länge des Auszugs aus einem Beitrag in der E-Mail-Zusammenfassung, in Zeichen." @@ -841,11 +849,32 @@ de: archived_disabled: "Dieses Thema wurde aus dem Archiv geholt. Es ist nicht länger eingefroren und kann verändert werden." closed_enabled: "Dieses Thema ist nun geschlossen. Es ist nicht länger möglich, auf dieses Thema zu antworten." closed_disabled: "Dieses Thema ist nun geöffnet. Es ist wieder möglich, auf dieses Thema zu antworten." + autoclosed_enabled_days: + one: "Dieses Thema wurde nach einem Tag automatisch geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + other: "Dieses Thema wurde nach %{count} Tagen automatisch geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + autoclosed_enabled_hours: + one: "Dieses Thema wurde nach einer Stunde automatisch geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + other: "Dieses Thema wurde nach %{count} Stunden automatisch geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + autoclosed_enabled_minutes: + one: "Dieses Thema wurde nach einer Minute automatisch geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + other: "Dieses Thema wurde nach %{count} Minuten automatisch geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + autoclosed_enabled_lastpost_days: + one: "Dieses Thema wurde automatisch einen Tag nach der letzten Antwort geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + other: "Dieses Thema wurde automatisch %{count} Tage nach der letzten Antwort geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + autoclosed_enabled_lastpost_hours: + one: "Dieses Thema wurde automatisch eine Stunde nach der letzten Antwort geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + other: "Dieses Thema wurde automatisch %{count} Stunden nach der letzten Antwort geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + autoclosed_enabled_lastpost_minutes: + one: "Dieses Thema wurde automatisch eine Minute nach der letzten Antwort geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." + other: "Dieses Thema wurde automatisch %{count} Minuten nach der letzten Antwort geschlossen. Es sind keine neuen Nachrichten mehr erlaubt." autoclosed_disabled: "Dieses Thema ist nun offen. Neue Beiträge werden angenommen." + autoclosed_disabled_lastpost: "Dieses Thema ist jetzt geöffnet. Neue Antworten sind erlaubt." pinned_enabled: "Dieses Thema ist nun angepinnt. In seiner Kategorie wird es nun oben aufgelistet, solange der Pin nicht von einem Moderator gelöst wird, oder nicht jeder Nutzer selbst den Pin löst." pinned_disabled: "Dieses Thema ist nun nicht mehr angepinnt. In seiner Kategorie wird es nicht länger oben aufgelistet." pinned_globally_enabled: "Dieses Thema ist nun global angepinnt. Es wird sowohl in seiner Kategorie als auch in allen anderen Themenlisten oben aufgelistet, solange der Pin nicht von einem Moderator gelöst wird, oder nicht jeder Nutzer selbst den Pin löst." pinned_globally_disabled: "Dieses Thema ist nun nicht mehr angepinnt. In seiner Kategorie wird es nicht länger oben aufgelistet." + visible_enabled: "Das Thema ist jetzt gelistet. Es wird in der Themenliste angezeigt." + visible_disabled: "Das Thema ist jetzt ungelistet. Es wird nicht mehr in der Themenliste angezeigt. Dieses Thema kann nur mit einem direkten Link erreicht werden." login: not_approved: "Dein Benutzerkonto wurde noch nicht genehmigt. Du wirst per E-Mail benachrichtigt, sobald dies geschehen ist." incorrect_username_email_or_password: "Benutzername, Mailadresse oder Passwort falsch" @@ -1021,6 +1050,8 @@ de: Es tut uns leid, aber deine E-Mail-Nachricht an %{destination} (titled %{former_title}) hat nicht funktioniert. Du hast nicht die notwendige Vertrauensstufe, um neue Themen über diese E-Mail-Adresse zu erstellen. Wenn du glaubst, dass das ein Irrtum ist, dann kontaktiere einen Mitarbeiter. + email_reject_no_account: + subject_template: "E-Mail-Problem -- Unbekannter Account" email_reject_empty: subject_template: "E-Mail-Problem -- Kein Inhalt" email_reject_parsing: @@ -1042,9 +1073,16 @@ de: Bitte versuch es erneut, wenn du das Problem beheben kannst. email_reject_reply_key: + subject_template: "E-Mail Problem -- Unbekannter Reply Key" text_body_template: | Es tut uns leid, aber die E-Mail-Nachricht an %{destination} (Titel: „%{former_title}“) hat nicht geklappt. Der angegebene Antwort-Schlüssel ist ungültig oder unbekannt. Wir wissen daher nicht auf welchen Beitrag diese E-Mail antwortet. Kontaktiere einen Mitarbeiter. + email_reject_destination: + subject_template: "E-Mail Problem -- Unbekannte Adresse" + email_reject_topic_not_found: + subject_template: "E-Mail Problem -- Thema nicht gefunden" + email_reject_topic_closed: + subject_template: "E-Mail Problem -- Thema geschlossen" email_error_notification: subject_template: "E-Mail-Problem -- POP-Authentifizierungsfehler" text_body_template: | @@ -1109,7 +1147,7 @@ de: subject_template: "Download von externen Bildern deaktiviert" text_body_template: "Die `download_remote_images_to_local` Einstellung wurde deaktiviert, da das Speicherplatz Limit von `download_remote_images_threshold` erreicht wurde." unsubscribe_link: "Wenn Du diese Mails nicht mehr erhalten möchtest, verändere deine [Benutzereinstellungen](%{user_preferences_url})." - subject_re: "Antw: " + subject_re: "Re: " subject_pm: "[PN]" user_notifications: previous_discussion: "Vorangehende Antworten" @@ -1171,13 +1209,17 @@ de: --- %{respond_instructions} digest: + why: "Eine kurze Zusammenfassung von %{site_link} seit deinem letzten BEsucht am %{last_seen_at}" subject_template: "[%{site_name}] Kurzfassung für %{date}" new_activity: "Neues in deinen Themen und Beiträgen:" top_topics: "Beliebte Beiträge" other_new_topics: "Beliebte Themen" + unsubscribe: "Diese Zusammenfassung wurde von %{site_link} gesendet, da wir dich eine Weile nicht mehr gesehen haben. Zum Abmelden %{unsubscribe_link}." click_here: "klicke hier" from: "%{site_name} Übersicht" read_more: "Weiterlesen" + more_topics: "Es gab %{new_topics_since_seen} andere neue Themen." + more_topics_category: "Weitere neue Themen:" posts: one: "1 Beitrag" other: "%{count} Beiträge" @@ -1201,6 +1243,11 @@ de: %{base_url}/users/password-reset/%{email_token} account_created: subject_template: "[%{site_name}] Dein neues Konto" + text_body_template: | + Ein neuer Account wurde für dich auf %{site_name} erstellt. + + Klicke auf den folgenden Link um ein Passwort für deinen neuen Account zu setzen: + %{base_url}/users/password-reset/%{email_token} authorize_email: subject_template: "[%{site_name}] Bestätige deine neue Mailadresse" text_body_template: | diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index fea96f7a7f..44121790b5 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -823,6 +823,7 @@ es: white_listed_spam_host_domains: "Una lista de dominios a excluir de las pruebas de spam. A los nuevos usuarios no se les restringirá la posibilidad de crear posts con enlaces a estos dominios." staff_like_weight: "Qué ponderación extra otorgan los me gusta provenientes de los miembros del Staff." levenshtein_distance_spammer_emails: "Al revisar coincidencias por correos electrónicos de spammers, qué número de caracteres permiten una coincidencia parcial." + max_new_accounts_per_registration_ip: "Si ya hay (n) cuentas con nivel de confianza 0 desde esta IP (y ninguna es de un miembro del staff o de nivel de confianza 2 o más), prohibir nuevos registros desde esa IP." reply_by_email_enabled: "Habilitar la respuesta a temas por email." reply_by_email_address: "Plantilla para la dirección de email que aparecerá al recibir correos con la función de respuesta por email: %{reply_key}@respuesta.ejemplo.com o respuestas+%{reply_key}@ejemplo.com" disable_emails: "Impedir que Discourse envié cualquier tipo de e-mail." @@ -951,6 +952,12 @@ es: autoclosed_enabled_lastpost_days: one: "Este tema se cerró automáticamente 1 día después del último post. No se permiten nuevas respuestas." other: "Este tema se cerró automáticamente %{count} días después del último post. No se permiten nuevas respuestas." + autoclosed_enabled_lastpost_hours: + one: "Este tema se cerró automáticamente 1 hora después del último post. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente %{count} horas después del último post. No se permiten nuevas respuestas." + autoclosed_enabled_lastpost_minutes: + one: "Este tema se cerró automáticamente 1 minuto después del último post. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente %{count} minutos después del último post. No se permiten nuevas respuestas." autoclosed_disabled: "El tema ahora está en abierto, se permiten respuestas." autoclosed_disabled_lastpost: "Este tema está ahora abierto. Se permiten nuevas respuestas." pinned_enabled: "Este tema ahora está destacado. Aparecerá en primer lugar en la lista de su categoría hasta que se deshaga el destacado de forma general por los moderadores o de forma particular por cada usuario para sí." @@ -1122,6 +1129,20 @@ es: system_messages: post_hidden: subject_template: "Post oculto al haber sido reportado por la comunidad" + text_body_template: | + Hola, + + Este se un mensaje automatizado de %{site_name} para informarte de que tu post fue ocultado. + + %{base_url}%{url} + + %{flag_reason} + + Varios miembros de la comunidad reportaron este post antes de ocultarlo, por favor considera revisar tu post para reflejar la retroalimentación de los demás miembros. **Puedes editar tu post después de %{edit_delay} minutos, y será automáticamente mostrado de vuelta.** Esto incrementara tu nivel de confianza. + + Sin embargo, si el post es ocultado por la comunidad una segunda vez, este se mantendrá oculto hasta que sea atendido por el staff – y tal vez vez se tomen medidas, incluyendo la posible suspensión de tu cuenta. + + Para más información, por favor consulta nuestras [reglas de comunidad](%{base_url}/guidelines). usage_tips: text_body_template: "Este mensaje privado tiene unos cuantos consejos para ayudarte a empezar.\n\n## Sigue desplazándote\n\nNo hay botones de siguiente página o número de páginas - para leer más, **¡solo sigue desplazándote hacia abajo!**\n\nTan pronto como llegue nuevos posts, aparecerán automáticamente.\n\n## ¿Dónde estoy?\n\n- Para buscar, ver tu página de usuario, o el menú, usa el **los botones con el icono en la parte superior derecha**.\n\n- Cualquier título de un tema te llevara al siguiente post sin leer. Usa el tiempo de la última actividad y el contador de posts para ir al inicio o al final.\n\n- Mientras estés leyendo un tema, regresa al inicio ↑ seleccionando el título del tema. Selecciona la barra de proceso verde en la parte inferior derecha para mostrar todos los controles de navegación, o usa las teclas inicio y fin.\n\n\n\n## ¿Cómo respondo?\n\n- Para responder para el tema en general, usa el botón Responder al final de la página.\n\n- Para responder a un post especifico, usa el botón Responder en ese post.\n\n- Para llevar la conversación a una dirección diferente, pero manteniéndola enlazada, usa Responder como Tema enlazado a la derecha del post.\n\nPara citar a alguien en tu respuesta, selecciona el texto que desees citar, y presiona cualquier botón de Responder.\n\n\n\nPara mencionar a alguien en tu respuesta, menciona su nombre. Escribe `@` y el autocompletado aparecerá.\n\ @@ -1216,10 +1237,19 @@ es: Tu cuenta no tiene el nivel de confianza suficiente para publicar nuevos temas a esta dirección de correo electrónico. Si crees que esto es un error, contacta con algún miembro del Staff. email_reject_no_account: subject_template: "Problema con el e-mail -- Cuenta Desconocida" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) se ha rechazado. + + No hay una cuenta de usuario asociada con esta dirección de email. Intenta enviarlo con otra dirección de email o contacta con algún miembro del Staff. email_reject_empty: subject_template: "Error con el email -- No hay contenido" + text_body_template: "Lo sentimos, pero tu email para %{destination} (titled %{former_title}) no funcionó.\n\nNo pudimos encontrar contenido en este e-mail. Asegúrate de que hayas escrito algo al inicio del e-mail - no podemos analizar respuestas en una sola línea.\n\nSi estás teniendo este problema y _sí_ incluiste contenido, intenta otra vez con contenido HTML incluido en tu e-mail (no solo texto plano). \n" email_reject_parsing: subject_template: "Error con el email -- Contenido desconocido" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + No hemos encontrado tu respuesta en el email. **Asegúrate de escribir tu entera respuesta al principio del email** - no podemos analizar respuestas entre líneas. email_reject_post_error: subject_template: "Error con el email -- Error de publicación" text_body_template: | @@ -1244,10 +1274,22 @@ es: La clave de respuesta proporcionada no es válida o es desconocida, por lo que no sabemos a qué responde este email. Contacta con algún miembro del staff. email_reject_destination: subject_template: "Problema con el e-mail -- Desconocido Para: Dirección" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + No se ha podido reconocer ninguna de las direcciones destino. Por favor, asegúrate de que la dirección del sitio está en Para: (no CC ni BCC), y que lo estás enviando a la dirección de e-mail proporcionada por los administradores. email_reject_topic_not_found: subject_template: "Problema con el email -- Tema No Encontrado" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + No hemos encontrado el tema, debe haber sido eliminado. Si crees que esto es un error, contacta a un administrador. email_reject_topic_closed: subject_template: "Problema con el email -- Tema Cerrado" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + El tema está cerrado a respuestas. Si crees que esto es un error, contacta a un administrador. email_error_notification: subject_template: "Error con el email -- Autenticación POP errónea" text_body_template: | diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index ba1617cf3b..25928ab069 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -6,6 +6,9 @@ # https://www.transifex.com/projects/p/discourse-org/ fr: + stringex: + characters: + number: "-" i18n: transliterate: rule: @@ -378,11 +381,15 @@ fr: description: 'Ce message contient du contenu qu''une personne raisonnable jugerait offensant, abusif ou en violation du règlement de notre communauté.' long_form: 'signalé comme inapproprié' notify_user: + title: 'Contacter @{{username}} en privé' description: 'Ce message contient quelque chose sur lequel je souhaite discuter en privé avec cet utilisateur. N''envoi pas de signalement.' + long_form: 'utilisateur contacté en privé' email_title: 'Votre message sur "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "Autre chose" description: 'Ce message nécessite l''attention d''un modérateur pour une autre raison que celles listées ci-dessus.' + long_form: 'signalé pour modération' email_title: 'Un message dans "%{title}" requière l''attention d''un modérateur' email_body: "%{link}\n\n%{message}" bookmark: @@ -407,7 +414,9 @@ fr: description: 'Ce message contient du contenu qu''une personne raisonnable jugerait offensant, abusif ou en violation du règlement de notre communauté.' long_form: 'signalé comme inapproprié' notify_moderators: + title: "Autre chose" description: 'Ce message requiert l''attention de la modération d''après le règlement de la communauté, TOS, ou pour une autre raison non listée ici.' + long_form: 'signalé pour modération' email_title: 'Ce sujet "%{title}" requière l''attention d''un modérateur' email_body: "%{link}\n\n%{message}" flagging: @@ -738,6 +747,7 @@ fr: purge_inactive_users_grace_period_days: "Période de grâce (en jours) avant qu'un utilisateur qui n'a pas activé son compte soit supprimé." enable_s3_uploads: "Placez les fichiers envoyés sur un stockage Amazon S3. IMPORTANT: nécessite un accès valide à S3 (l'identifiant et la clé secrète)." s3_use_iam_profile: 'Utiliser un role AWS EC2 IAM pour la récupération des clés. NOTE: si activé, surcharge les paramètres "s3 access key id" et "s3 secret access key".' + s3_upload_bucket: "Le nom du bucket Amazon S3 qui contiendra les fichiers téléchargés. ATTENTION : doit être en minuscule, sans points et sans caractères de soulignement." s3_access_key_id: "L' access key Amazon S3 qui sera utilisée pour uploader les images." s3_secret_access_key: "La clé secrète Amazon S3 qui va être utilisée pour uploader des images." s3_region: "Le nom de la région Amazon S3 qui va être utilisée pour uploader des images." @@ -808,6 +818,7 @@ fr: white_listed_spam_host_domains: "Une liste des domaines exclus des hôtes testés comme spam. Les nouveaux utilisateurs ne seront jamais restreint dans la création de message contenant des liens vers ses domaines. " staff_like_weight: "Quel poids supplémentaire donner aux J'aime de l'équipe." levenshtein_distance_spammer_emails: "Lorsque des courriels correspondent à des spammer, la différence du nombre de caractère permettra toujours une correspondance floue." + max_new_accounts_per_registration_ip: "S'il y a déjà (n) Niveau de confiance 0 comptes à partir de cette adresse IP ( et aucun n'est un membre du personnel ou au NC2 ou ultérieure), ne plus accepter de nouvelles inscriptions de cette IP." reply_by_email_enabled: "Activer les réponses aux sujets via courriel." reply_by_email_address: "Modèle pour la réponse par courriel entrant; exemple : %{reply_key}@reply.example.com ou replies+%{reply_key}@example.com" disable_emails: "Désactiver l'envoi de les courriels depuis Discourse." @@ -834,6 +845,7 @@ fr: logout_redirect: "Adresse de redirection après déconnexion, ex: http://monsite.com/logout" allow_uploaded_avatars: "Autoriser les utilisateurs à envoyer leurs propres avatars." allow_animated_avatars: "Autoriser les utilisateurs à utiliser des avatars ens gif animés. ATTENTION: il est hautement recommandé d'exécuter la tâche rake avatars:refresh après avoir changé ce paramètre." + allow_animated_thumbnails: "Créer des aperçus animés pour les gifs animés." automatically_download_gravatars: "Télécharger les gravatars pour les utilisateurs lors de la création de compte ou du changement de courriel." digest_topics: "Nombre maximum de sujets à afficher dans le courriel de résumé." digest_min_excerpt_length: "Taille minimum du résumé des messages dans les courriels, en caractères." @@ -935,6 +947,9 @@ fr: autoclosed_enabled_lastpost_days: one: "Cette discussion a été automatiquement fermée après un jour. Aucune réponse n'est permise dorénavant." other: "Cette discussion a été automatiquement fermée après %{count} jours. Aucune réponse n'est permise dorénavant." + autoclosed_enabled_lastpost_hours: + one: "Cette discussion a été automatiquement fermée après 1 heure suivant le dernier commentaire. Aucune réponse n'est permise dorénavant." + other: "Cette discussion a été automatiquement fermée après %{count} heures suivant le dernier commentaire. Aucune réponse n'est permise dorénavant." autoclosed_enabled_lastpost_minutes: one: "Cette discussion a été automatiquement fermée une minute après le dernier message. Aucune réponse n'est permise dorénavant." other: "Cette discussion a été automatiquement fermée %{count} minutes après le dernier message. Aucune réponse n'est permise dorénavant." @@ -1102,6 +1117,7 @@ fr: other: "Il y a des signalements qui ont été soumis il y a plus de %{count} heures." please_review: "Veuillez examiner cela." post_number: "message" + how_to_disable: 'Vous pouvez désactiver ou modifier la fréquence de ce courriel de rappel via le réglage "m''informer sur les signalements".' subject_template: one: "un signalement en attente de traitement" other: "%{count} signalements en attente de traitement." @@ -1119,6 +1135,20 @@ fr: system_messages: post_hidden: subject_template: "Message caché suite à un signalement par la communauté" + text_body_template: | + Bonjour, + + Ceci est un message automatique de %{site_name} pour vous informer que votre message a été caché. + + %{base_url}%{url} + + %{flag_reason} + + Plusieurs membres ont signalé ce message avant qu'il ne soit caché, donc veuillez prendre en compte leurs remarques pour revoir votre message. **Vous pouvez modifier le message après %{edit_delay} minutes, et il sera automatiquement ré-affiché.** Ceci augmentera votre niveau de confiance sur le forum. + + Cependant, si le message est signalé par la communauté une seconde fois, il restera marqué jusqu’à l'intervention d'un modérateur -- et ils pourront prendre d'autres mesures y compris l'éventuelle suspension de votre compte. + + Pour plus d'informations, merci de vous en référer aux [règles de la communauté](%{base_url}/guidelines). usage_tips: text_body_template: "Ce message privé contient quelques astuces pour vous aider à démarrer rapidement.\n\n## Continuez de descendre\n\nIl n'y a pas de bouton Page Suivante ni de numéro de page – pour en lire plus, **il vous suffit de descendre !**\n\nLorsque de nouvelles réponses arrivent, elles apparaissent automatiquement. \n\n## Où suis-je ?\n\n- Pour la recherche, votre page d'utilisateur, ou le menu, utiliser les **boutons icônes en haut à droite**.\n\n- Dans la liste des sujets, le titre vous emmènera toujours vers le prochain message non lu. Utiliser les colonnes Activité ou Messages pour allez au premier ou au dernier message. \n\n- Lorsque vous lisez un sujet, retournez en haut de celui ↑ en cliquant sur le titre. Cliquez sur la barre de progression en bas à droite pour avoir une navigation complète, ou utilisez les touches Home et Fin. \n\n \n\n## Comment je réponds ?\n\n- Pour répondre au sujet dans sa globalité, utilisez le bouton \"Répondre\" tout en bas de la page.\n\n- Pour répondre à un message spécifique, utilisez le bouton \"Répondre\" sur le message.\n\n - Si vous voulez continuer le sujet dans une section différente, mais garder le lien entre votre sujet et le message qui vous l'a inspiré, utilisez la fonction Répondre en créant un nouveau sujet à droite de chaque message.\n\nPour citer quelqu'un dans votre message, sélectionnez le texte que vous voulez citer et appuyez sur un des boutons Répondre.\n\n\n\nPour mentionner le pseudo d'un utilisateur, commencez à taper `@` et une liste d'auto-complétion apparaîtra.\n\n\n\nConcernant les [icones Emoji](http://www.emoji.codes/), commencez par écrire `:` ou le traditionnel smiley `:)` :smile: \n\n## Que puis-je faire d'autre ?\n\nÀ la fin de chaque message il y a un ensemble de boutons pour les différentes actions possibles.\n\n\n\nPour faire savoir à quelqu'un que vous avez apprécié son message, cliquez sur le bouton *j'aime** en bas du message. Si vous voyez un problème avec un message, n'hésitez pas à cliquer sur le bouton **signaler**\ @@ -1381,13 +1411,17 @@ fr: --- %{respond_instructions} digest: + why: "Voici un bref résumé de ce qu'il s'est passé sur %{site_link} depuis votre dernière visite le %{last_seen_at}" subject_template: "[%{site_name}] Résumé du %{date}" new_activity: "Nouvelles activités sur vos sujets et messages :" top_topics: "Messages populaires" other_new_topics: "Sujets populaires" + unsubscribe: "Ce courriel résumé vous est transmis par %{site_link} car nous ne vous avons pas vu depuis quelques temps. Pour vous désabonner %{unsubscribe_link}." click_here: "cliquez ici" from: "Résumé de %{site_name}" read_more: "Lire la suite" + more_topics: "Il y a eu %{new_topics_since_seen} nouvelles discussions." + more_topics_category: "Plus de nouvelles discussions:" posts: one: "1 message" other: "%{count} messages" diff --git a/config/locales/server.ja.yml b/config/locales/server.ja.yml index d38949c0b5..69d6d6f599 100644 --- a/config/locales/server.ja.yml +++ b/config/locales/server.ja.yml @@ -6,6 +6,16 @@ # https://www.transifex.com/projects/p/discourse-org/ ja: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "MMM D" short_date: "MMM D, YYYY" @@ -23,12 +33,31 @@ ja: log_in: "ログイン" via: "%{username} via %{site_name}" is_reserved: "is reserved" + purge_reason: "古い、もしくは未検証の為、自動的に削除された" + disable_remote_images_download_reason: "ディスク容量が不足しているため、リモートでの画像ダウンロードは無効になっています。" errors: messages: too_long_validation: "は、最大文字数(%{max}文字)を超えています。(入力したのは%{length}文字 ) " + invalid_boolean: "無効なboolean." + taken: "既に取得されています" + embed: + load_from_remote: "投稿の読み込みに失敗しました。" + bulk_invite: + file_should_be_csv: "アップロードするファイルは、csv または txt 形式である必要があります。" + backup: + operation_already_running: "操作を実行しています。他の操作はできません。" + backup_file_should_be_tar_gz: "バックアップファイルは .tar .gz 形式でなければいけません。" + not_enough_space_on_disk: "このバックアップファイルをアップロードするディスクの空き容量が足りません。" + not_logged_in: "ログインしてください。" + read_only_mode_enabled: "このサイトは読み取り専用モードです。会話は無効になっています。" + too_many_replies: + other: "申し訳ありません、新しいユーザーの同じトピックへの返信は、一時的に %{count} 回に制限されています。" embed: start_discussion: "議論の開始" continue: "議論を続ける" + more_replies: + other: "%{count} 以上の返信" + loading: "会話をロードしています…" permalink: "パーマリンク" in_reply_to: "▶ %{username}" replies: diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index 9763af7bf8..f7d4538dc2 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -6,6 +6,9 @@ # https://www.transifex.com/projects/p/discourse-org/ pt_BR: + stringex: + characters: + number: "-" i18n: transliterate: rule: @@ -247,6 +250,7 @@ pt_BR: title: "regular" elder: title: "líder" + change_failed_explanation: "Você tentou rebaixar %{user_name} para '%{new_trust_level}'. No entanto o nível de confiança dele já é '%{current_trust_level}'. %{user_name} permanecerá em '%{current_trust_level}' - se você deseja rebaixar um usuário, tranque o nível de confiança primeiro" rate_limiter: slow_down: "Você executou esta ação muitas vezes, tente novamente mais tarde" too_many_requests: "Nós possuímos um limite diário do número de vezes que uma ação pode ser tomada. Por favor aguarde %{time_left} antes de tentar novamente." @@ -347,12 +351,14 @@ pt_BR: activation: action: "Ativar sua conta" already_done: "Desculpe, este link de confirmação não está mais válido. Talvez a sua conta já esteja ativa?" + please_continue: "Sua conta agora está confirmada; você vai ser redirecionado para a página inicial." continue_button: "Continuar no %{site_name}" welcome_to: "Bem-vindo a %{site_name}!" approval_required: "Um moderador tem que aprovar a sua conta para que você possa acessar este fórum. Você receberá um email quando sua conta for aprovada!" post_action_types: off_topic: title: 'Off-Topic' + description: 'Essa mensagem não é relevante para a discussão definida pelo título e primeira mensagem, e deveria ser movida para outro local.' long_form: 'sinalizou isto como off-topic' spam: title: 'Spam' @@ -363,10 +369,15 @@ pt_BR: description: 'Este post contém conteúdo que uma pessoa razoável consideraria ofensivo, abusivo, ou uma violação das nossas diretrizes da comunidade.' long_form: 'sinalizado como inapropriado' notify_user: + title: 'Mensagem privada @{{username}}' description: 'Este post contém algo que eu quero falar com essa pessoa diretamente e em particular sobre. Não lançar uma flag.' + long_form: 'usuário de mensagem privada' email_title: 'Sobre a sua postagem "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "Algo mais" + description: 'Essa mensagem requer atenção da moderação por outra razão não listada acima.' + long_form: 'sinalizar isso para atenção da moderação' email_title: 'Uma postagem em "%{title}" requer atenção do moderador' email_body: "%{link}\n\n%{message}" bookmark: @@ -391,7 +402,9 @@ pt_BR: description: 'Este tópico contém um conteúdo que uma pessoa razoável consideraria ofensivo, abusivo, ou violação de nossas diretrizes da comunidade.' long_form: 'sinalizar como impróprio' notify_moderators: + title: "Algo mais" description: 'Este tópico requer atenção moderadora geral, com base na diretrizes, TOS , ou por outro motivo não listados acima.' + long_form: 'sinalizar isso para atenção da moderação' email_title: 'O tópico "%{title}" requer atenção do moderador' email_body: "%{link}\n\n%{message}" flagging: @@ -549,6 +562,7 @@ pt_BR: description: "HTML que será adicionado no topo de cada página (após o cabeçalho, antes da navegação ou o título do tópico)." bottom: title: "Rodapé das páginas" + description: "HTML que será adicionado antes da tag " site_settings: censored_words: "Palavras que serão substituídos automaticamente por ■■■■" delete_old_hidden_posts: "Auto-apagar todas as mensagens ocultas que ficar oculta por mais de 30 dias." @@ -568,6 +582,7 @@ pt_BR: educate_until_posts: "Quando o usuário começa a digitar suas primeiras (n) novos posts, mostrar o novo painel pop-up de educação do usuário no compositor." title: "Breve título deste site, utilizado na tag title." site_description: "Descreva este site em uma frase, usada na meta tag description." + contact_email: "Endereço de email ou chave de contato para o site. Avisos importantes do discourse.org em relação a atualizações críticas vão ser enviadas para esse endereço." queue_jobs: "APENAS DESENVOLVEDORES! ATENÇÃO! Por padrão, enfileira tarefas no sidekiq. Se desativado, seu site ficará defeituoso." crawl_images: "Recupere imagens de URLs remotas para inserir as dimensões de largura e altura corretos." download_remote_images_to_local: "Converta imagens remotas para imagens locais, transferindo-as; isto evita imagens quebradas." @@ -716,7 +731,11 @@ pt_BR: archived_disabled: "Este tópico foi agora desarquivado. Já não está congelado, e pode ser alterado." closed_enabled: "Este tópico está agora fechado. Novas respostas não são aceitas." closed_disabled: "Este tópico está agora aberto. Novas respostas serão aceitas." + autoclosed_enabled_lastpost_minutes: + one: "Este tópico foi fechado automaticamente após 1 minuto após a última resposta. Novas respostas não são mais permitidas." + other: "Este tópico foi fechado automaticamente após %{count} minutos depois da última resposta. Novas respostas não são mais permitidas." autoclosed_disabled: "Este tópico está aberto agora. Novas respostas estão permitidas." + autoclosed_disabled_lastpost: "Este tópico está aberto agora. Novas respostas estão permitidas." pinned_enabled: "Este tópico está agora afixado. Irá aparecer no topo das suas categorias" pinned_disabled: "Este tópico deixou de estár afixado. Não irá mais aparecer no topo das suas categorias." pinned_globally_enabled: "Esse tópico agora está fixado globalmente. Ele irá aparecer no topo dessa categoria e de todas as listagens de tópicos até que seja desfixado pela equipe para todo mundo, ou para usuários individuais por eles mesmo." @@ -1115,6 +1134,7 @@ pt_BR: --- %{respond_instructions} digest: + why: "Um breve resumo de %{site_link} desde que viu pela última vez em %{last_seen_at}." subject_template: "[%{site_name}] Atividade do Fórum para %{date}" new_activity: "Nova atividade nos seus tópicos e postagens:" top_topics: "Tópicos populares" @@ -1122,6 +1142,8 @@ pt_BR: click_here: "clique aqui" from: "resumo de %{site_name}" read_more: "Leia Mais" + more_topics: "Há %{new_topics_since_seen} outros novos tópicos." + more_topics_category: "Mais tópicos novos:" posts: one: "1 mensagem" other: "%{count} mensagens" diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 35b6c90ef4..e54de7b714 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -6,6 +6,16 @@ # https://www.transifex.com/projects/p/discourse-org/ ru: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "D MMM" short_date: "D MMM YYYY" @@ -391,7 +401,9 @@ ru: email_title: 'Ваше сообщение в теме "%{title}"' email_body: "%{link}\n\n%{message}\n" notify_moderators: + title: "Другое" description: 'Это сообщение требует внимания модератора по другой причине.' + long_form: 'жалоба модератору' email_title: 'Сообщение в теме "%{title}" требует внимания модератора' email_body: "%{link}\n\n%{message}\n" bookmark: @@ -416,7 +428,9 @@ ru: description: 'Эта тема может быть сочтена оскорбительной или нарушает правила поведения на сайте.' long_form: 'отмеченно как неуместное' notify_moderators: + title: "Другое" description: 'Эта тема требует внимания модератора, т.к. нарушает правила поведения или пользовательское соглашение, или по другой причине.' + long_form: 'жалоба модератору' email_title: 'Тема "%{title}" требует внимания модератора' email_body: "%{link}\n\n%{message}" flagging: @@ -570,6 +584,7 @@ ru: description: "HTML, который будет добавлен в начале страницы (после заголовка, перед панелью навигации или заголовком темы)." bottom: title: "Внизу страниц" + description: "HTML код, который будет добавлен перед тегом ." site_settings: censored_words: "Слова, которые будут автоматически заменены на ■■■■" delete_old_hidden_posts: "Автоматически удалять сообщения, скрытые дольше чем 30 дней." @@ -662,8 +677,8 @@ ru: port: "Внимание, только для разработчиков! Использовать порт HTTP отличный от значения по умолчанию. Оставьте это поле пустым для использования стандартного 80-го порта." force_hostname: "Внимание, только для разработчиков! Укажите имя хоста для формирования URL или оставьте пустым для значения по умолчанию." invite_expiry_days: "Срок валидности ключей, высланных приглашенному пользователю, в днях" - min_username_length: "Минимальная длина имени пользователя в символах. ВНИМАНИЕ: ЛЮБЫЕ СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ИМЕНЕМ КОРОЧЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ВОЙТИ НА САЙТ." - max_username_length: "Максимальная длина имени пользователя в символах. ВНИМАНИЕ: ЛЮБЫЕ СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ИМЕНЕМ ДЛИННЕЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ВОЙТИ НА САЙТ." + min_username_length: "Минимально допустимая длина псевдонима. ВНИМАНИЕ: СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ПСЕВДОНИМОМ КОРОЧЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ПОЛЬЗОВАТЬСЯ САЙТОМ." + max_username_length: "Максимально допустимая длина псевдонима. ВНИМАНИЕ: СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ПСЕВДОНИМОМ ДЛИННЕЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ПОЛЬЗОВАТЬСЯ САЙТОМ." min_password_length: "Минимальная длина пароля" block_common_passwords: "Не позволять использовать пароли из списка 10 000 самых частоиспользуемых паролей." enable_sso: "Включить единый вход через внешний сайт (Примечание: отключает приглашения)" @@ -683,6 +698,7 @@ ru: maximum_backups: "Максимальное количество резервных копий к сохранению. Более старые резервные копии будут автоматически удалены." backup_daily: "Автоматически создавать резервную копию сайта раз в день." active_user_rate_limit_secs: "Как часто мы обновляем поле 'last_seen_at', в секундах" + verbose_localization: "Показывать ключи используемых строк в интерфейсе для перевода на другой язык" previous_visit_timeout_hours: "Как долго должно длиться посещение сайта, чтобы мы посчитали его «предыдущим посещением», в часах" rate_limit_create_topic: "Пользователи не могут создавать больше одной новой темы в указанное количество секунд." rate_limit_create_post: "Пользователи не могут писать более одного нового сообщения в указанное количество секунд." @@ -763,9 +779,10 @@ ru: relative_date_duration: "Количество дней после отправки сообщения, в течении которых его дата будет отображаться в относительном виде (7д), а позже - в абсолютном (20 Фев)." delete_user_max_post_age: "Запретить удаление пользователей, чье первое сообщение старее (x) дней." delete_all_posts_max: "Максимальное количество сообщений, которое может быть удалено за один раз через кнопку \"Удалить все сообщения\". Если у пользователя сообщений больше этого числа, сообщения удаляются не полностью и пользователь не удаляется." - username_change_period: "Количество дней после регистрации, когда пользователь может сменить свой логин (0 запрещает изменение логина)." + username_change_period: "Количество дней после регистрации, когда пользователь сможет изменить свой псевдоним (0 - запретить изменение псевдонима)." email_editable: "Позволять пользователям изменять свой адрес электронной почты после регистрации." allow_uploaded_avatars: "Разрешить пользователям загружать собственные картинки для аватарок." + allow_animated_thumbnails: "Генерировать анимированные миниатюры gif-картинок." automatically_download_gravatars: "Скачивать аватарку Gravatar пользователя во время создания учетной записи или изменения e-mail." digest_topics: "Максимальное количество тем в письме - сводке новостей." digest_min_excerpt_length: "Минимальная длина (в символах) вытяжки из сообщения в письме - сводке новостей." @@ -840,7 +857,32 @@ ru: archived_disabled: "Эта тема разархивирована. Она более не заморожена, и может быть изменена." closed_enabled: "Эта тема закрыта. В ней больше нельзя отвечать." closed_disabled: "Эта тема открыта. В ней можно отвечать." + autoclosed_enabled_days: + one: "Эта тема была автоматически закрыта спустя 1 день. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта спустя %{count} дня. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта спустя %{count} дней. В ней больше нельзя отвечать." + autoclosed_enabled_hours: + one: "Эта тема была автоматически закрыта спустя 1 час. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта спустя %{count} часа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта спустя %{count} часов. В ней больше нельзя отвечать." + autoclosed_enabled_minutes: + one: "Эта тема была автоматически закрыта спустя 1 минуту. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта спустя %{count} минуты. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта спустя %{count} минут. В ней больше нельзя отвечать." + autoclosed_enabled_lastpost_days: + one: "Эта тема была автоматически закрыта через 1 день после последнего ответа. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта через %{count} дня после последнего ответа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта через %{count} дней после последнего ответа. В ней больше нельзя отвечать." + autoclosed_enabled_lastpost_hours: + one: "Эта тема была автоматически закрыта через 1 час после последнего ответа. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта через %{count} часа после последнего ответа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта через %{count} часов после последнего ответа. В ней больше нельзя отвечать." + autoclosed_enabled_lastpost_minutes: + one: "Эта тема была автоматически закрыта через 1 минуту после последнего ответа. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта через %{count} минуты после последнего ответа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта через %{count} минут после последнего ответа. В ней больше нельзя отвечать." autoclosed_disabled: "Эта тема открыта. В ней можно отвечать." + autoclosed_disabled_lastpost: "Эта тема теперь открыта, и в ней можно отвечать." pinned_enabled: "Данная тема прилеплена. Тема будет находиться наверху списка тем раздела, пока кто нибудь из администраторов не открепит ее." pinned_disabled: "Эта тема отлеплена. Она больше не будет отображаться наверху списка тем раздела." pinned_globally_enabled: "Эта тема теперь прилеплена глобально и будет появляться вверху списка тем соответствующего раздела и всех списков тем, пока персонал не отлепит ее глобально или пользователь не отлепит лично для себя." @@ -852,6 +894,7 @@ ru: incorrect_username_email_or_password: "Неверное имя пользователя, адрес электронной почты или пароль" wait_approval: "Спасибо за регистрацию. Мы оповестим вас, когда ваша учетная запись будет одобрена." active: "Ваша учетная запись активирована и готова к использованию." + activate_email: "

    Почти готово! Мы выслали письмо на %{email}. Пожалуйста, следуйте инструкциям в этом письме для активации вашей учетной записи.

    Если письмо не пришло, пожалуйста, проверьте папку \"спам\", или попробуйте войти еще раз, чтобы выслать активационное письмо повторно.

    " not_activated: "Вы пока что не можете войти на сайт. Пожалуйста, следуйте инструкциям по активации учетной записи, которые мы отправили вам по электронной почтой." not_allowed_from_ip_address: "Вам нельзя входить как %{username} с этого IP адреса." suspended: "Вы не можете войти до %{date}." @@ -863,8 +906,10 @@ ru: omniauth_error_unknown: "В процессе входа на сайт произошла ошибка. Пожалуйста, повторите попытку." new_registrations_disabled: "Новые регистрации сейчас ограничены." password_too_long: "Максимальная длина пароля - 200 символов." + missing_user_field: "Вы не заполнили все поля пользователя" close_window: "Аутентификация выполнена. Закройте это окно для продолжения." user: + no_accounts_associated: "Нет ассоциированных учетных записей" username: short: "минимум %{min} знаков" long: "должно быть более %{max} знаков" @@ -911,6 +956,11 @@ ru: Это персональное приглашение от зарегистрированного пользователя, которому мы доверяем, поэтому вам не понадобиться авторизоваться на сайте. invite_password_instructions: subject_template: "Задайте пароль для вашей учетной записи на сайте %{site_name}" + text_body_template: | + Спасибо, что приняли приглашение на сайт %{site_name} -- добро пожаловать! + + Чтобы войти, нажмите следующую следующую ссылку и установите пароль: + %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Проверка доставки писем" new_version_mailer: @@ -1005,6 +1055,7 @@ ru: [prefs]: %{user_preferences_url} backup_succeeded: subject_template: "Резервное копирование успешно завершено" + text_body_template: "Резервная копия создана.\nПерейдите в [Админку > Резервные копии](/admin/backups), чтобы скачать её." backup_failed: subject_template: "Резервное копирование не удалось" text_body_template: | @@ -1062,6 +1113,12 @@ ru: Если вы считаете, что произошла ошибка, свяжитесь с персоналом (модератором или администратором). email_reject_no_account: subject_template: "Проблема с письмом - не найдена учетная запись" + text_body_template: | + К сожалению, ваше письмо к %{destination} (под названием %{former_title}) не может быть обработано. + + Мы не смогли найти учетную запись с электронным адресом, с которого пришло ваше письмо. + + Попробуйте отправить с другого почтового ящика, или, если вы считаете, что произошла ошибка, свяжитесь с персоналом - модератором или администратором. email_reject_empty: subject_template: "Проблема с письмом - отсутствует текст" email_reject_parsing: @@ -1078,6 +1135,10 @@ ru: Ключ ответа неверный, поэтому мы не можем определить, кому адресован ваш ответ. Пожалуйста, свяжитесь с персоналом (модератором или администратором). email_reject_destination: subject_template: "Проблема с письмом - неизвестный адрес получателя To:" + email_reject_topic_not_found: + subject_template: "Проблема с обработкой письма - тема не найдена" + email_reject_topic_closed: + subject_template: "Проблема с обработкой письма - тема уже закрыта" email_error_notification: subject_template: "Проблема с e-mail -- ошибка POP аутентификации" too_many_spam_flags: @@ -1191,6 +1252,7 @@ ru: --- %{respond_instructions} digest: + why: "Сводка обсуждений на сайте %{site_link} с момента вашего последнего визита %{last_seen_at}" subject_template: "[%{site_name}] Новые сообщения от %{date}" new_activity: "Новая активность в ваших темах и сообщениях:" top_topics: "Популярные темы" @@ -1198,6 +1260,8 @@ ru: click_here: "нажмите здесь" from: "Cводка новостей сайта %{site_name}" read_more: "Читать еще" + more_topics: "Было создано еще %{new_topics_since_seen} новых тем." + more_topics_category: "Еще новые темы:" posts: one: "1 сообщение" few: "1 сообщения" diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 4c1872a9cd..237fae728d 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -793,6 +793,7 @@ zh_CN: white_listed_spam_host_domains: "广告主机白名单域名列表。新用户可以任意链接至这些域名。" staff_like_weight: "职员赞时的额外权重。" levenshtein_distance_spammer_emails: "当匹配广告邮件时,模糊匹配判断差异的字符数。" + max_new_accounts_per_registration_ip: "如果已经有了从这个 IP 创建的(n)个信任等级0的账户(并且没有一个是职员或者是信任等级2以上的用户),不再允许来自该 IP 地址的注册请求。" reply_by_email_enabled: "启用通过邮件回复。" reply_by_email_address: "通过邮件回复的回复地址模板,例如:%{reply_key}@reply.example.com 或 replies+%{reply_key}@example.com" disable_emails: "禁止 Discourse 发送任何邮件" @@ -915,6 +916,8 @@ zh_CN: other: "本主题在创建 %{count} 分钟后自动关闭。不再允许添加新回复。" autoclosed_enabled_lastpost_days: other: "本主题在最后一个回复创建后 %{count} 天后自动关闭。不再允许添加新回复。" + autoclosed_enabled_lastpost_hours: + other: "本主题在最后一个回复创建后 %{count} 小时后自动关闭。不再允许添加新回复。" autoclosed_enabled_lastpost_minutes: other: "本主题在最后一个回复创建后 %{count} 分钟后自动关闭。不再允许添加新回复。" autoclosed_disabled: "本主题是开放的,可以添加新的回复。" From 5b90ceb71d2a6540187900ab5c538f2f667dffd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 27 Nov 2014 19:29:30 +0100 Subject: [PATCH 098/991] FEATURE: rolls up 1.2.*.* IP ranges when number of entries > 10 --- .../admin-logs-screened-ip-addresses.js.es6 | 12 ++++- .../admin/screened_ip_addresses_controller.rb | 54 ++++++++++++++----- config/locales/client.en.yml | 2 + script/import_scripts/tnation.rb | 24 +++++++++ .../screened_ip_addresses_controller_spec.rb | 24 ++++++++- 5 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 script/import_scripts/tnation.rb diff --git a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 index 03f2d4bdb3..414461edac 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 @@ -21,8 +21,16 @@ export default Ember.ArrayController.extend(Discourse.Presence, { return bootbox.confirm(I18n.t("admin.logs.screened_ips.roll_up_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { if (confirmed) { self.set("loading", true) - return Discourse.ScreenedIpAddress.rollUp().then(function() { - self.send("show"); + return Discourse.ScreenedIpAddress.rollUp().then(function(results) { + if (results && results.subnets) { + if (results.subnets.length > 0) { + self.send("show"); + bootbox.alert(I18n.t("admin.logs.screened_ips.rolled_up_some_subnets", { subnets: results.subnets.join(", ") })); + } else { + self.set("loading", false); + bootbox.alert(I18n.t("admin.logs.screened_ips.rolled_up_no_subnet")); + } + } }); } }); diff --git a/app/controllers/admin/screened_ip_addresses_controller.rb b/app/controllers/admin/screened_ip_addresses_controller.rb index ea77a8b984..079e674fb5 100644 --- a/app/controllers/admin/screened_ip_addresses_controller.rb +++ b/app/controllers/admin/screened_ip_addresses_controller.rb @@ -29,21 +29,51 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController render json: success_json end - def roll_up - # 1 - retrieve all subnets that needs roll up - sql = <<-SQL - SELECT network(inet(host(ip_address) || './24')) AS ip_range + def star_subnets_query + @star_subnets_query ||= <<-SQL + SELECT network(inet(host(ip_address) || '/24')) AS ip_range FROM screened_ip_addresses - WHERE action_type = :action_type + WHERE action_type = #{ScreenedIpAddress.actions[:block]} AND family(ip_address) = 4 AND masklen(ip_address) = 32 GROUP BY ip_range HAVING COUNT(*) >= :min_count SQL + end - subnets = ScreenedIpAddress.exec_sql(sql, - action_type: ScreenedIpAddress.actions[:block], - min_count: SiteSetting.min_ban_entries_for_roll_up).values.flatten + def star_star_subnets_query + @star_star_subnets_query ||= <<-SQL + WITH weighted_subnets AS ( + SELECT network(inet(host(ip_address) || '/16')) AS ip_range, + CASE masklen(ip_address) + WHEN 32 THEN 1 + WHEN 24 THEN :roll_up_weight + ELSE 0 + END AS weight + FROM screened_ip_addresses + WHERE action_type = #{ScreenedIpAddress.actions[:block]} + AND family(ip_address) = 4 + ) + SELECT ip_range + FROM weighted_subnets + GROUP BY ip_range + HAVING SUM(weight) >= :min_count + SQL + end + + def star_subnets + min_count = SiteSetting.min_ban_entries_for_roll_up + ScreenedIpAddress.exec_sql(star_subnets_query, min_count: min_count).values.flatten + end + + def star_star_subnets + weight = SiteSetting.min_ban_entries_for_roll_up + ScreenedIpAddress.exec_sql(star_star_subnets_query, min_count: 10, roll_up_weight: weight).values.flatten + end + + def roll_up + # 1 - retrieve all subnets that needs roll up + subnets = [star_subnets, star_star_subnets].flatten # 2 - log the call StaffActionLogger.new(current_user).log_roll_up(subnets) unless subnets.blank? @@ -63,25 +93,23 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController MIN(created_at) AS min_created_at, MAX(last_match_at) AS max_last_match_at FROM screened_ip_addresses - WHERE action_type = :action_type + WHERE action_type = #{ScreenedIpAddress.actions[:block]} AND family(ip_address) = 4 - AND masklen(ip_address) = 32 AND ip_address << :ip_address ) s WHERE ip_address = :ip_address SQL - ScreenedIpAddress.exec_sql(sql, action_type: ScreenedIpAddress.actions[:block], ip_address: subnet) + ScreenedIpAddress.exec_sql(sql, ip_address: subnet) # 5 - remove old matches ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block]) .where("family(ip_address) = 4") - .where("masklen(ip_address) = 32") .where("ip_address << ?", subnet) .delete_all end - render json: success_json + render json: success_json.merge!({ subnets: subnets }) end private diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 1da4d87090..317d37e421 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1862,6 +1862,8 @@ en: description: 'IP addresses that are being watched. Use "Allow" to whitelist IP addresses.' delete_confirm: "Are you sure you want to remove the rule for %{ip_address}?" roll_up_confirm: "Are you sure you want to roll up ban entries?" + rolled_up_some_subnets: "Successfully rolled up IP ban entries to these subnets: %{subnets}." + rolled_up_no_subnet: "There was nothing to roll up." actions: block: "Block" do_nothing: "Allow" diff --git a/script/import_scripts/tnation.rb b/script/import_scripts/tnation.rb new file mode 100644 index 0000000000..bbc03abd62 --- /dev/null +++ b/script/import_scripts/tnation.rb @@ -0,0 +1,24 @@ +# custom importer for www.t-nation.com, feel free to borrow ideas + +require File.expand_path(File.dirname(__FILE__) + "/base.rb") +require "mysql2" + +class ImportScripts::Tnation < ImportScripts::Base + + DATABASE = "tnation" + + def initialize + super + + @client = Mysql2::Client.new( + host: "localhost", + database: DATABASE + ) + end + + def execute + end + +end + +ImportScripts::Tnation.new.perform diff --git a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb index ddd1d1212c..528134c9d4 100644 --- a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb +++ b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb @@ -20,7 +20,7 @@ describe Admin::ScreenedIpAddressesController do describe 'roll_up' do - it "works" do + it "rolls up 1.2.3.* entries" do Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1) Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1) Fabricate(:screened_ip_address, ip_address: "1.2.3.6", match_count: 1) @@ -29,7 +29,7 @@ describe Admin::ScreenedIpAddressesController do Fabricate(:screened_ip_address, ip_address: "42.42.42.5", match_count: 1) StaffActionLogger.any_instance.expects(:log_roll_up) - SiteSetting.expects(:min_ban_entries_for_roll_up).returns(3) + SiteSetting.stubs(:min_ban_entries_for_roll_up).returns(3) xhr :post, :roll_up response.should be_success @@ -39,6 +39,26 @@ describe Admin::ScreenedIpAddressesController do subnet.match_count.should == 3 end + it "rolls up 1.2.*.* entries" do + Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.4.6", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.7.8", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.9.1", match_count: 1) + + Fabricate(:screened_ip_address, ip_address: "1.2.42.0/24", match_count: 1) + + StaffActionLogger.any_instance.expects(:log_roll_up) + SiteSetting.stubs(:min_ban_entries_for_roll_up).returns(5) + + xhr :post, :roll_up + response.should be_success + + subnet = ScreenedIpAddress.where(ip_address: "1.2.0.0/16").first + subnet.should be_present + subnet.match_count.should == 6 + end + end end From 65c106325cf0600fd5bfe2f18d7e3849eb0c7268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 27 Nov 2014 19:30:19 +0100 Subject: [PATCH 099/991] remove old & useless import script --- script/import_scripts/tnation.rb | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 script/import_scripts/tnation.rb diff --git a/script/import_scripts/tnation.rb b/script/import_scripts/tnation.rb deleted file mode 100644 index bbc03abd62..0000000000 --- a/script/import_scripts/tnation.rb +++ /dev/null @@ -1,24 +0,0 @@ -# custom importer for www.t-nation.com, feel free to borrow ideas - -require File.expand_path(File.dirname(__FILE__) + "/base.rb") -require "mysql2" - -class ImportScripts::Tnation < ImportScripts::Base - - DATABASE = "tnation" - - def initialize - super - - @client = Mysql2::Client.new( - host: "localhost", - database: DATABASE - ) - end - - def execute - end - -end - -ImportScripts::Tnation.new.perform From 07211489f08f2c9168ab031f296d4b4300091aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 27 Nov 2014 19:51:13 +0100 Subject: [PATCH 100/991] FIX: hide restricted profile info from TL0 users to anonymous in 'JS-off' page --- app/controllers/users_controller.rb | 1 + app/serializers/user_serializer.rb | 2 +- app/views/users/show.html.erb | 16 ++++++++++++---- lib/guardian/user_guardian.rb | 4 ++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f74d506412..6295f0b21a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -30,6 +30,7 @@ class UsersController < ApplicationController user_serializer = UserSerializer.new(@user, scope: guardian, root: 'user') respond_to do |format| format.html do + @restrict_fields = guardian.restrict_user_fields?(@user) store_preloaded("user_#{@user.username}", MultiJson.dump(user_serializer)) end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 4f6d18f4c4..1313db0b76 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -23,7 +23,7 @@ class UserSerializer < BasicUserSerializer attrs.each do |attr| method_name = "include_#{attr}?" define_method(method_name) do - return false if object.trust_level == TrustLevel[0] && scope.anonymous? + return false if scope.restrict_user_fields?(object) send(attr).present? end end diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 4c58b90cde..8dfa390743 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,9 +1,17 @@

    <%= @user.username %>

    +<% unless @restrict_fields %>

    <%= raw @user.user_profile.bio_processed %>

    - -<% content_for :head do %> - <%= crawlable_meta_data(title: @user.username, description: @user.user_profile.bio_summary, image: @user.small_avatar_url) %> <% end %> -<% content_for :title do %><%=t("js.user.profile")%> - <%= @user.username %><% end %> +<% content_for :head do %> + <% if @restrict_fields %> + <%= crawlable_meta_data(title: @user.username, image: @user.small_avatar_url) %> + <% else %> + <%= crawlable_meta_data(title: @user.username, description: @user.user_profile.bio_summary, image: @user.small_avatar_url) %> + <% end %> +<% end %> + +<% content_for :title do %> + <%= t("js.user.profile")%> - <%= @user.username %> +<% end %> diff --git a/lib/guardian/user_guardian.rb b/lib/guardian/user_guardian.rb index ba8689f6e1..abcef6ab45 100644 --- a/lib/guardian/user_guardian.rb +++ b/lib/guardian/user_guardian.rb @@ -51,4 +51,8 @@ module UserGuardian is_admin? || (is_staff? && SiteSetting.show_email_on_profile) end + def restrict_user_fields?(user) + user.trust_level == TrustLevel[0] && anonymous? + end + end From 781807191e58a84b59f67386383f884c0985dedc Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Thu, 27 Nov 2014 16:48:08 -0500 Subject: [PATCH 101/991] Version bump to v1.2.0.beta3 --- lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.rb b/lib/version.rb index e5c9757d7f..2a87a0cc27 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -5,7 +5,7 @@ module Discourse MAJOR = 1 MINOR = 2 TINY = 0 - PRE = 'beta2' + PRE = 'beta3' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end From a85a3da1674a41306b6425373d918305c2e7f1bb Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 28 Nov 2014 00:38:08 -0800 Subject: [PATCH 102/991] move digest email featured post category underneath --- app/views/user_notifications/digest.html.erb | 1 + lib/email/styles.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/user_notifications/digest.html.erb b/app/views/user_notifications/digest.html.erb index 4367821cb0..84db1389c3 100644 --- a/app/views/user_notifications/digest.html.erb +++ b/app/views/user_notifications/digest.html.erb @@ -21,6 +21,7 @@ <%- @featured_topics.each_with_index do |t, i| %> diff --git a/lib/email/styles.rb b/lib/email/styles.rb index b3cbeac05a..c031a6caaf 100644 --- a/lib/email/styles.rb +++ b/lib/email/styles.rb @@ -136,7 +136,7 @@ module Email style('pre', 'word-wrap: break-word; max-width: 694px;') style('code', 'background-color: #f1f1ff; padding: 2px 5px;') style('pre code', 'display: block; background-color: #f1f1ff; padding: 5px;') - style('.featured-topic a', 'text-decoration: none; font-weight: bold; color: #006699; margin-right: 5px') + style('.featured-topic a', 'text-decoration: none; font-weight: bold; color: #006699; line-height:2em;') onebox_styles plugin_styles From 37a3e956b64b7a1f1c729178917a27081e8bb8ef Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 28 Nov 2014 13:44:40 -0500 Subject: [PATCH 103/991] UX: Link categories in digest emails --- app/helpers/user_notifications_helper.rb | 10 ++++++---- app/views/user_notifications/digest.html.erb | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/helpers/user_notifications_helper.rb b/app/helpers/user_notifications_helper.rb index 1b3dccf049..da0baf4e9f 100644 --- a/app/helpers/user_notifications_helper.rb +++ b/app/helpers/user_notifications_helper.rb @@ -69,14 +69,16 @@ module UserNotificationsHelper result = "" + category_url = "#{Discourse.base_url}#{category.url}" + if opts[:only_stripe] - result << " " - result << "#{category.name}" + result << " " + result << "#{category.name}" else if category.parent_category.present? - result << " " + result << " " end - result << "#{category.name}" + result << "#{category.name}" end result.html_safe diff --git a/app/views/user_notifications/digest.html.erb b/app/views/user_notifications/digest.html.erb index 84db1389c3..d9c6c16816 100644 --- a/app/views/user_notifications/digest.html.erb +++ b/app/views/user_notifications/digest.html.erb @@ -58,7 +58,7 @@
    <%- @new_by_category.each do |c| %> - <%= c[0].name %> <%= c[1] %> + <%= c[0].name %> <%= c[1] %> <%- end %>
    From cb0e7a5724834ff13f523b5cb47d984d5e5238f9 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 28 Nov 2014 14:20:43 -0500 Subject: [PATCH 104/991] For performance reasons, use `delete_all` when removing a user's email logs --- app/models/user.rb | 2 +- spec/services/user_destroyer_spec.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index 56cad0d705..8a846ee2d9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -25,7 +25,7 @@ class User < ActiveRecord::Base has_many :post_actions, dependent: :destroy has_many :user_badges, -> {where('user_badges.badge_id IN (SELECT id FROM badges where enabled)')}, dependent: :destroy has_many :badges, through: :user_badges - has_many :email_logs, dependent: :destroy + has_many :email_logs, dependent: :delete_all has_many :post_timings has_many :topic_allowed_users, dependent: :destroy has_many :topics_allowed, through: :topic_allowed_users, source: :topic diff --git a/spec/services/user_destroyer_spec.rb b/spec/services/user_destroyer_spec.rb index efb50f0bd2..b7b685a558 100644 --- a/spec/services/user_destroyer_spec.rb +++ b/spec/services/user_destroyer_spec.rb @@ -283,6 +283,17 @@ describe UserDestroyer do end end + context 'user got an email' do + let(:user) { Fabricate(:user) } + let!(:email_log) { Fabricate(:email_log, user: user) } + + it "deletes the email log" do + expect { + UserDestroyer.new(@admin).destroy(user, {delete_posts: true}) + }.to change { EmailLog.count }.by(-1) + end + end + context 'user liked things' do before do @topic = Fabricate(:topic, user: Fabricate(:user)) From cb9507d292b87648a8137b6a76fc44e713c59644 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 28 Nov 2014 11:44:59 -0800 Subject: [PATCH 105/991] tighten up title line height in digest email --- lib/email/styles.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/email/styles.rb b/lib/email/styles.rb index c031a6caaf..1d421dd529 100644 --- a/lib/email/styles.rb +++ b/lib/email/styles.rb @@ -136,7 +136,7 @@ module Email style('pre', 'word-wrap: break-word; max-width: 694px;') style('code', 'background-color: #f1f1ff; padding: 2px 5px;') style('pre code', 'display: block; background-color: #f1f1ff; padding: 5px;') - style('.featured-topic a', 'text-decoration: none; font-weight: bold; color: #006699; line-height:2em;') + style('.featured-topic a', 'text-decoration: none; font-weight: bold; color: #006699; line-height:1.5em;') onebox_styles plugin_styles From 003d32babeec033200429dcef5c8720852e6fddd Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Sat, 29 Nov 2014 03:58:44 -0800 Subject: [PATCH 106/991] decrease size of cat color stripe in digests a bit --- app/helpers/user_notifications_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/user_notifications_helper.rb b/app/helpers/user_notifications_helper.rb index da0baf4e9f..dbbe353f7c 100644 --- a/app/helpers/user_notifications_helper.rb +++ b/app/helpers/user_notifications_helper.rb @@ -72,7 +72,7 @@ module UserNotificationsHelper category_url = "#{Discourse.base_url}#{category.url}" if opts[:only_stripe] - result << " " + result << " " result << "#{category.name}" else if category.parent_category.present? From 529014d56ea5085a9f64621401f5f8b63c14001e Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 25 Nov 2014 22:14:59 +0530 Subject: [PATCH 107/991] FEATURE: switch to GitHub email_reply_parser library and parse plain text email content --- Gemfile | 6 +- Gemfile.lock | 4 +- lib/email/receiver.rb | 6 +- spec/components/email/receiver_spec.rb | 79 ++++++++++ spec/fixtures/emails/android_gmail.eml | 177 +++++++++++++++++++++ spec/fixtures/emails/gmail_web.eml | 181 ++++++++++++++++++++++ spec/fixtures/emails/ios_default.eml | 136 ++++++++++++++++ spec/fixtures/emails/iphone_signature.eml | 29 ++++ spec/fixtures/emails/newlines.eml | 84 ++++++++++ spec/fixtures/emails/previous_replies.eml | 180 +++++++++++++++++++++ spec/fixtures/emails/windows_8_metro.eml | 173 +++++++++++++++++++++ 11 files changed, 1045 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/emails/android_gmail.eml create mode 100644 spec/fixtures/emails/gmail_web.eml create mode 100644 spec/fixtures/emails/ios_default.eml create mode 100644 spec/fixtures/emails/iphone_signature.eml create mode 100644 spec/fixtures/emails/newlines.eml create mode 100644 spec/fixtures/emails/previous_replies.eml create mode 100644 spec/fixtures/emails/windows_8_metro.eml diff --git a/Gemfile b/Gemfile index cf663f22a9..362809608b 100644 --- a/Gemfile +++ b/Gemfile @@ -118,11 +118,7 @@ gem 'fastimage' gem 'fog', '1.22.1', require: false gem 'unf', require: false -# see: https://twitter.com/samsaffron/status/412360162297393152 -# Massive amount of changes made in branch we use, no PR upstreamed -# We need to get this sorted -# https://github.com/samsaffron/email_reply_parser -gem 'email_reply_parser-discourse', require: 'email_reply_parser' +gem 'email_reply_parser' # note: for image_optim to correctly work you need # sudo apt-get install -y advancecomp gifsicle jpegoptim libjpeg-progs optipng pngcrush diff --git a/Gemfile.lock b/Gemfile.lock index 0f56e0cbc1..bc96345db3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -65,7 +65,7 @@ GEM dotenv (0.11.1) dotenv-deployment (~> 0.0.2) dotenv-deployment (0.0.2) - email_reply_parser-discourse (0.6) + email_reply_parser (0.5.8) ember-data-source (0.14) ember-source ember-rails (0.14.1) @@ -412,7 +412,7 @@ DEPENDENCIES better_errors binding_of_caller certified - email_reply_parser-discourse + email_reply_parser ember-rails ember-source (= 1.6.0.beta.2) eventmachine diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 5d2dfcfa11..1a7a1ee199 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -117,9 +117,9 @@ module Email if message.multipart? html = fix_charset message.html_part text = fix_charset message.text_part - # TODO picking text if available may be better - # in case of email reply from MS Outlook client, prefer text - if (text && !html) || (text && (message.header.to_s =~ /X-MS-Has-Attach/ || message.header.to_s =~ /Microsoft Outlook/)) + + # prefer plain text + if text return text end elsif message.content_type =~ /text\/html/ diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index 55a7df2027..a7e93873f2 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -93,6 +93,85 @@ Pleasure to have you here! ) end + it "handles newlines" do + test_parse_body(fixture_file("emails/newlines.eml")). + should == ( +"This is my reply. +It is my best reply. +It will also be my *only* reply." + ) + end + + it "should not include previous replies" do + test_parse_body(fixture_file("emails/previous_replies.eml")).should_not match /Previous Replies/ + end + + it "strips iPhone signature" do + test_parse_body(fixture_file("emails/iphone_signature.eml")).should_not match /Sent from my iPhone/ + end + + it "properly renders email reply from gmail web client" do + test_parse_body(fixture_file("emails/gmail_web.eml")). + should == ( +"### This is a reply from standard GMail in Google Chrome. + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. + +Here's some **bold** text in Markdown. + +Here's a link http://example.com" + ) + end + + it "properly renders email reply from iOS default mail client" do + test_parse_body(fixture_file("emails/ios_default.eml")). + should == ( +"### this is a reply from iOS default mail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. + +Here's some **bold** markdown text. + +Here's a link http://example.com" + ) + end + + it "properly renders email reply from Android 5 gmail client" do + test_parse_body(fixture_file("emails/android_gmail.eml")). + should == ( +"### this is a reply from Android 5 gmail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. + +This is **bold** in Markdown. + +This is a link to http://example.com" + ) + end + + it "properly renders email reply from Windows 8.1 Metro default mail client" do + test_parse_body(fixture_file("emails/windows_8_metro.eml")). + should == ( +"### reply from default mail client in Windows 8.1 Metro + + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. + + +This is a **bold** word in Markdown + + +This is a link http://example.com" + ) + end + it "properly renders email reply from MS Outlook client" do test_parse_body(fixture_file("emails/outlook.eml")).should == "Microsoft Outlook 2010" end diff --git a/spec/fixtures/emails/android_gmail.eml b/spec/fixtures/emails/android_gmail.eml new file mode 100644 index 0000000000..21c5dde234 --- /dev/null +++ b/spec/fixtures/emails/android_gmail.eml @@ -0,0 +1,177 @@ +Delivered-To: reply@discourse.org +Return-Path: +MIME-Version: 1.0 +In-Reply-To: +References: + +Date: Fri, 28 Nov 2014 12:53:21 -0800 +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +From: Walter White +To: Discourse Meta +Content-Type: multipart/alternative; boundary=089e0149cfa485c6630508f173df + +--089e0149cfa485c6630508f173df +Content-Type: text/plain; charset=UTF-8 + +### this is a reply from Android 5 gmail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. + +This is **bold** in Markdown. + +This is a link to http://example.com +On Nov 28, 2014 12:36 PM, "Arpit Jalan" wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +--089e0149cfa485c6630508f173df +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +

    ### this is a reply from Android 5 gmail

    +

    The quick brown fox jumps over the lazy dog. The quick brown= + fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. = +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over= + the lazy dog. The quick brown fox jumps over the lazy dog.

    +

    This is **bold** in Markdown.

    +

    This is a link to http://exam= +ple.com

    +
    On Nov 28, 2014 12:36 PM, "Arpit Jalan"= +; <info@discourse.org> wrot= +e:
    + + + + + + + + + + + +
    + + + techAPJ
    + November 28 +
    +

    Test reply.

    + +

    First paragraph.

    + +

    Second paragraph.

    +
    + + +
    +

    To respond, reply to this email or visit https:/= +/meta.discourse.org/t/testing-default-email-replies/22638/3 in your bro= +wser.

    +
    +
    +

    Previous Replies

    + + + + + + + + + + + +
    + + + codinghorror + November 28 +
    +

    We're testing the latest GitHub emai= +l processing library which we are integrating now.

    + +

    https://github.com/github/email_reply_parser

    + +

    Go ahead and reply to this topic and I&#= +39;ll reply from various email clients for testing.

    +
    + + +
    + +
    +

    To respond, reply to this email or visit https://met= +a.discourse.org/t/testing-default-email-replies/22638/3 in your browser= +.

    +
    +
    +

    To unsubscribe from these emails, visit your user preferences.

    +
    +
    +
    + +--089e0149cfa485c6630508f173df-- diff --git a/spec/fixtures/emails/gmail_web.eml b/spec/fixtures/emails/gmail_web.eml new file mode 100644 index 0000000000..8bb8383571 --- /dev/null +++ b/spec/fixtures/emails/gmail_web.eml @@ -0,0 +1,181 @@ +Delivered-To: reply@discourse.org +Return-Path: +MIME-Version: 1.0 +In-Reply-To: +References: + +Date: Fri, 28 Nov 2014 12:36:49 -0800 +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +From: Walter White +To: Discourse Meta +Content-Type: multipart/alternative; boundary=001a11c2e04e6544f30508f138ba + +--001a11c2e04e6544f30508f138ba +Content-Type: text/plain; charset=UTF-8 + +### This is a reply from standard GMail in Google Chrome. + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. + +Here's some **bold** text in Markdown. + +Here's a link http://example.com + +On Fri, Nov 28, 2014 at 12:35 PM, Arpit Jalan wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +--001a11c2e04e6544f30508f138ba +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +
    ### This is a reply from standard GMail in Google Chr= +ome.

    The quick brown fox jumps over the lazy dog. = +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over= + the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown= + fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. = +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over= + the lazy dog.=C2=A0

    Here's some **bold** text= + in Markdown.

    Here's a link http://example.com
    <= +br>
    On Fri, Nov 28, 2014 at 12:35 PM, Arpit Jalan= + <info@discourse.org> wrote:
    + + + + + + + + + + + +
    + + + techAPJ
    + November 28 +
    +

    Test reply.

    + +

    First paragraph.

    + +

    Second paragraph.

    +
    + + +
    +

    To respond, reply to this email or visit https:/= +/meta.discourse.org/t/testing-default-email-replies/22638/3 in your bro= +wser.

    +
    +
    +

    Previous Replies

    + + + + + + + + + + + +
    + + + codinghorror + November 28 +
    +

    We're testing the latest GitHub emai= +l processing library which we are integrating now.

    + +

    https://github.com/github/email_reply_parser

    + +

    Go ahead and reply to this topic and I&#= +39;ll reply from various email clients for testing.

    +
    + + +
    + +
    +

    To respond, reply to this email or visit https://met= +a.discourse.org/t/testing-default-email-replies/22638/3 in your browser= +.

    +
    +
    +

    To unsubscribe from these emails, visit your user preferences.

    +
    +
    +

    + +--001a11c2e04e6544f30508f138ba-- diff --git a/spec/fixtures/emails/ios_default.eml b/spec/fixtures/emails/ios_default.eml new file mode 100644 index 0000000000..8d4d58feb1 --- /dev/null +++ b/spec/fixtures/emails/ios_default.eml @@ -0,0 +1,136 @@ +Delivered-To: reply@discourse.org +Return-Path: +From: Walter White +Content-Type: multipart/alternative; + boundary=Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105 +Content-Transfer-Encoding: 7bit +Mime-Version: 1.0 (1.0) +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +Date: Fri, 28 Nov 2014 12:41:41 -0800 +References: +In-Reply-To: +To: Discourse Meta +X-Mailer: iPhone Mail (12B436) + + +--Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105 +Content-Type: text/plain; + charset=us-ascii +Content-Transfer-Encoding: quoted-printable + +### this is a reply from iOS default mail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over t= +he lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fo= +x jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The q= +uick brown fox jumps over the lazy dog. The quick brown fox jumps over the l= +azy dog.=20 + +Here's some **bold** markdown text. + +Here's a link http://example.com + + +> On Nov 28, 2014, at 12:35 PM, Arpit Jalan wrote: +>=20 +>=20 +> techAPJ +> November 28 +> Test reply. +>=20 +> First paragraph. +>=20 +> Second paragraph. +>=20 +> To respond, reply to this email or visit https://meta.discourse.org/t/test= +ing-default-email-replies/22638/3 in your browser. +>=20 +> Previous Replies +>=20 +> codinghorror +> November 28 +> We're testing the latest GitHub email processing library which we are inte= +grating now. +>=20 +> https://github.com/github/email_reply_parser +>=20 +> Go ahead and reply to this topic and I'll reply from various email clients= + for testing. +>=20 +> To respond, reply to this email or visit https://meta.discourse.org/t/test= +ing-default-email-replies/22638/3 in your browser. +>=20 +> To unsubscribe from these emails, visit your user preferences. + +--Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105 +Content-Type: text/html; + charset=utf-8 +Content-Transfer-Encoding: 7bit + +
    ### this is a reply from iOS default mail

    The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. 

    Here's some **bold** markdown text.

    Here's a link http://example.com


    On Nov 28, 2014, at 12:35 PM, Arpit Jalan <info@discourse.org> wrote:

    + + + + + + + + + + + +
    + + + techAPJ
    + November 28 +
    +

    Test reply.

    + +

    First paragraph.

    + +

    Second paragraph.

    +
    + + +
    +

    To respond, reply to this email or visit https://meta.discourse.org/t/testing-default-email-replies/22638/3 in your browser.

    +
    +
    +

    Previous Replies

    + + + + + + + + + + + +
    + + + codinghorror
    + November 28 +
    +

    We're testing the latest GitHub email processing library which we are integrating now.

    + +

    https://github.com/github/email_reply_parser

    + +

    Go ahead and reply to this topic and I'll reply from various email clients for testing.

    +
    + + +
    + +
    +

    To respond, reply to this email or visit https://meta.discourse.org/t/testing-default-email-replies/22638/3 in your browser.

    +
    +
    +

    To unsubscribe from these emails, visit your user preferences.

    +
    +
    +
    +--Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105-- diff --git a/spec/fixtures/emails/iphone_signature.eml b/spec/fixtures/emails/iphone_signature.eml new file mode 100644 index 0000000000..d314ad1f1e --- /dev/null +++ b/spec/fixtures/emails/iphone_signature.eml @@ -0,0 +1,29 @@ +Delivered-To: test@mail.com +Return-Path: +From: Walter White +Content-Type: multipart/alternative; + boundary=Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 +Content-Transfer-Encoding: 7bit +Mime-Version: 1.0 (1.0) +Subject: Re: Signature in email replies! +Date: Thu, 23 Oct 2014 14:43:49 +0530 +References: <1234@mail.gmail.com> +In-Reply-To: <1234@mail.gmail.com> +To: Arpit Jalan +X-Mailer: iPhone Mail (12A405) + + +--Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 +Content-Type: text/plain; + charset=us-ascii +Content-Transfer-Encoding: 7bit + +This post should not include signature. + +Sent from my iPhone + +> On 23-Oct-2014, at 9:45 am, Arpit Jalan wrote: +> +> Signature in email replies! + +--Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 diff --git a/spec/fixtures/emails/newlines.eml b/spec/fixtures/emails/newlines.eml new file mode 100644 index 0000000000..cf03b9d18b --- /dev/null +++ b/spec/fixtures/emails/newlines.eml @@ -0,0 +1,84 @@ +In-Reply-To: +Date: Wed, 8 Oct 2014 10:36:19 +0530 +Delivered-To: walter.white@googlemail.com +Subject: Re: [Discourse] Welcome to Discourse +From: Walter White +To: Discourse +Content-Type: multipart/alternative; boundary=bcaec554078cc3d0c10504e24661 + +--bcaec554078cc3d0c10504e24661 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +This is my reply. +It is my best reply. +It will also be my *only* reply. + +On Wed, Oct 8, 2014 at 10:33 AM, ajalan +wrote: + +> ajalan +> +> October 8 +> +> Awesome! Thank You! [image: +1] +> +> To respond, reply to this email or visit +> http://discourse.techapj.com/t/welcome-to-discourse/8/2 +> +> in your browser. +> ------------------------------ +> Previous Replies system +> +> October 8 +> +> The first paragraph of this pinned topic will be visible as a welcome +> message to all new visitors on your homepage. It's important! +> +> *Edit this* into a brief description of your community: +> +> - Who is it for? +> - What can they find here? +> - Why should they come here? +> - Where can they read more (links, resources, etc)? +> +> You may want to close this topic via the wrench icon at the upper right, +> so that replies don't pile up on an announcement. +> ------------------------------ +> +> To respond, reply to this email or visit +> http://discourse.techapj.com/t/welcome-to-discourse/8/2 +> +> in your browser. +> +> To unsubscribe from these emails, visit your user preferences +> +> . +> + +--bcaec554078cc3d0c10504e24661 diff --git a/spec/fixtures/emails/previous_replies.eml b/spec/fixtures/emails/previous_replies.eml new file mode 100644 index 0000000000..3fd74482c0 --- /dev/null +++ b/spec/fixtures/emails/previous_replies.eml @@ -0,0 +1,180 @@ +Delivered-To: reply@discourse.org +MIME-Version: 1.0 +In-Reply-To: +References: + +Date: Fri, 28 Nov 2014 12:55:32 -0800 +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +From: Walter White +To: Discourse Meta +Content-Type: multipart/alternative; boundary=001a1137c9285318bb0508f17bc5 + +--001a1137c9285318bb0508f17bc5 +Content-Type: text/plain; charset=UTF-8 + +### this is a reply from iOS Gmail app + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy +dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps +over the lazy dog. + +This is **bold** text in Markdown. + +This is a link to http://example.com + +On Friday, November 28, 2014, Arpit Jalan wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +--001a1137c9285318bb0508f17bc5 +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +### this is a reply from iOS Gmail app

    The quick brown f= +ox jumps over the lazy dog.=C2=A0The quick brown fox jumps over the lazy dog.=C2=A0The quic= +k brown fox jumps over the lazy dog.=C2=A0The quick brown fox jumps over th= +e lazy dog.=C2=A0The quick brown fox jumps over the lazy dog.=C2=A0The quic= +k brown fox jumps over the lazy dog.=C2=A0The quick brown fox jumps over th= +e lazy dog.=C2=A0

    This is **bold** text in Markdown.

    This is a link to http://example.com

    On Friday, November 28, 2014, Arpit Jalan <
    info@discourse.org> wrote:
    + + + + + + + + + + + +
    + + + techAPJ
    + November 28 +
    +

    Test reply.

    + +

    First paragraph.

    + +

    Second paragraph.

    +
    + + +
    +

    To respond, reply to this email or visit https:/= +/meta.discourse.org/t/testing-default-email-replies/22638/3 in your bro= +wser.

    +
    +
    +

    Previous Replies

    + + + + + + + + + + + +
    + + + codinghorror + November 28 +
    +

    We're testing the latest GitHub emai= +l processing library which we are integrating now.

    + +

    https://github.com/github/email_reply_parser

    + +

    Go ahead and reply to this topic and I&#= +39;ll reply from various email clients for testing.

    +
    + + +
    + +
    +

    To respond, reply to this email or visit https://met= +a.discourse.org/t/testing-default-email-replies/22638/3 in your browser= +.

    +
    +
    +

    To unsubscribe from these emails, visit your user preferences.

    +
    +
    +
    + +--001a1137c9285318bb0508f17bc5-- diff --git a/spec/fixtures/emails/windows_8_metro.eml b/spec/fixtures/emails/windows_8_metro.eml new file mode 100644 index 0000000000..67d204af56 --- /dev/null +++ b/spec/fixtures/emails/windows_8_metro.eml @@ -0,0 +1,173 @@ +Delivered-To: reply@discourse.org +Return-Path: +MIME-Version: 1.0 +From: +To: + =?utf-8?Q?Discourse_Meta?= + +Subject: + =?utf-8?Q?Re:_[Discourse_Meta]_[Lounge]_Testing_default_email_replies?= +Importance: Normal +Date: Fri, 28 Nov 2014 21:29:10 +0000 +In-Reply-To: +References: + , +Content-Type: multipart/alternative; + boundary="_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_" + +--_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_ +Content-Transfer-Encoding: base64 +Content-Type: text/plain; charset="utf-8" + +IyMjIHJlcGx5IGZyb20gZGVmYXVsdCBtYWlsIGNsaWVudCBpbiBXaW5kb3dzIDguMSBNZXRybw0K +DQoNClRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWlj +ayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gg +anVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0 +aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cu +IFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBi +cm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVt +cHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUg +bGF6eSBkb2cuDQoNCg0KVGhpcyBpcyBhICoqYm9sZCoqIHdvcmQgaW4gTWFya2Rvd24NCg0KDQpU +aGlzIGlzIGEgbGluayBodHRwOi8vZXhhbXBsZS5jb20NCiANCg0KDQoNCg0KDQpGcm9tOiBBcnBp +dCBKYWxhbg0KU2VudDog4oCORnJpZGF54oCOLCDigI5Ob3ZlbWJlcuKAjiDigI4yOOKAjiwg4oCO +MjAxNCDigI4xMuKAjjrigI4zNeKAjiDigI5QTQ0KVG86IGplZmYgYXR3b29kDQoNCg0KDQoNCg0K +DQogdGVjaEFQSg0KTm92ZW1iZXIgMjggDQoNClRlc3QgcmVwbHkuDQoNCkZpcnN0IHBhcmFncmFw +aC4NCg0KU2Vjb25kIHBhcmFncmFwaC4NCg0KDQoNClRvIHJlc3BvbmQsIHJlcGx5IHRvIHRoaXMg +ZW1haWwgb3IgdmlzaXQgaHR0cHM6Ly9tZXRhLmRpc2NvdXJzZS5vcmcvdC90ZXN0aW5nLWRlZmF1 +bHQtZW1haWwtcmVwbGllcy8yMjYzOC8zIGluIHlvdXIgYnJvd3Nlci4NCg0KDQoNClByZXZpb3Vz +IFJlcGxpZXMNCg0KIGNvZGluZ2hvcnJvcg0KTm92ZW1iZXIgMjggDQoNCldlJ3JlIHRlc3Rpbmcg +dGhlIGxhdGVzdCBHaXRIdWIgZW1haWwgcHJvY2Vzc2luZyBsaWJyYXJ5IHdoaWNoIHdlIGFyZSBp +bnRlZ3JhdGluZyBub3cuDQoNCmh0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIvZW1haWxfcmVwbHlf +cGFyc2VyDQoNCkdvIGFoZWFkIGFuZCByZXBseSB0byB0aGlzIHRvcGljIGFuZCBJJ2xsIHJlcGx5 +IGZyb20gdmFyaW91cyBlbWFpbCBjbGllbnRzIGZvciB0ZXN0aW5nLg0KDQoNCg0KDQoNClRvIHJl +c3BvbmQsIHJlcGx5IHRvIHRoaXMgZW1haWwgb3IgdmlzaXQgaHR0cHM6Ly9tZXRhLmRpc2NvdXJz +ZS5vcmcvdC90ZXN0aW5nLWRlZmF1bHQtZW1haWwtcmVwbGllcy8yMjYzOC8zIGluIHlvdXIgYnJv +d3Nlci4NCg0KDQpUbyB1bnN1YnNjcmliZSBmcm9tIHRoZXNlIGVtYWlscywgdmlzaXQgeW91ciB1 +c2VyIHByZWZlcmVuY2VzLg== + +--_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_ +Content-Transfer-Encoding: base64 +Content-Type: text/html; charset="utf-8" + +CjxodG1sPgo8aGVhZD4KPG1ldGEgbmFtZT0iZ2VuZXJhdG9yIiBjb250ZW50PSJXaW5kb3dzIE1h +aWwgMTcuNS45NjAwLjIwNjA1Ij4KPHN0eWxlIGRhdGEtZXh0ZXJuYWxzdHlsZT0idHJ1ZSI+PCEt +LQpwLk1zb0xpc3RQYXJhZ3JhcGgsIGxpLk1zb0xpc3RQYXJhZ3JhcGgsIGRpdi5Nc29MaXN0UGFy +YWdyYXBoIHsKbWFyZ2luLXRvcDowaW47Cm1hcmdpbi1yaWdodDowaW47Cm1hcmdpbi1ib3R0b206 +MGluOwptYXJnaW4tbGVmdDouNWluOwptYXJnaW4tYm90dG9tOi4wMDAxcHQ7Cn0KcC5Nc29Ob3Jt +YWwsIGxpLk1zb05vcm1hbCwgZGl2Lk1zb05vcm1hbCB7Cm1hcmdpbjowaW47Cm1hcmdpbi1ib3R0 +b206LjAwMDFwdDsKfQpwLk1zb0xpc3RQYXJhZ3JhcGhDeFNwRmlyc3QsIGxpLk1zb0xpc3RQYXJh +Z3JhcGhDeFNwRmlyc3QsIGRpdi5Nc29MaXN0UGFyYWdyYXBoQ3hTcEZpcnN0LCAKcC5Nc29MaXN0 +UGFyYWdyYXBoQ3hTcE1pZGRsZSwgbGkuTXNvTGlzdFBhcmFncmFwaEN4U3BNaWRkbGUsIGRpdi5N +c29MaXN0UGFyYWdyYXBoQ3hTcE1pZGRsZSwgCnAuTXNvTGlzdFBhcmFncmFwaEN4U3BMYXN0LCBs +aS5Nc29MaXN0UGFyYWdyYXBoQ3hTcExhc3QsIGRpdi5Nc29MaXN0UGFyYWdyYXBoQ3hTcExhc3Qg +ewptYXJnaW4tdG9wOjBpbjsKbWFyZ2luLXJpZ2h0OjBpbjsKbWFyZ2luLWJvdHRvbTowaW47Cm1h +cmdpbi1sZWZ0Oi41aW47Cm1hcmdpbi1ib3R0b206LjAwMDFwdDsKbGluZS1oZWlnaHQ6MTE1JTsK +fQotLT48L3N0eWxlPjwvaGVhZD4KPGJvZHkgZGlyPSJsdHIiPgo8ZGl2IGRhdGEtZXh0ZXJuYWxz +dHlsZT0iZmFsc2UiIGRpcj0ibHRyIiBzdHlsZT0iZm9udC1mYW1pbHk6ICdDYWxpYnJpJywgJ1Nl +Z29lIFVJJywgJ01laXJ5bycsICdNaWNyb3NvZnQgWWFIZWkgVUknLCAnTWljcm9zb2Z0IEpoZW5n +SGVpIFVJJywgJ01hbGd1biBHb3RoaWMnLCAnc2Fucy1zZXJpZic7Zm9udC1zaXplOjEycHQ7Ij48 +ZGl2IHN0eWxlPSJmb250LXNpemU6IDE0cHQ7Ij4jIyMgcmVwbHkgZnJvbSBkZWZhdWx0IG1haWwg +Y2xpZW50IGluIFdpbmRvd3MgOC4xIE1ldHJvPC9kaXY+PGRpdiBzdHlsZT0iZm9udC1zaXplOiAx +NHB0OyI+PGJyPjwvZGl2PjxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTRwdDsiPlRoZSBxdWljayBi +cm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVt +cHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUg +bGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRo +ZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93 +biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMg +b3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6 +eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuPC9kaXY+ +PGRpdiBzdHlsZT0iZm9udC1zaXplOiAxNHB0OyI+PGJyPjwvZGl2PjxkaXYgc3R5bGU9ImZvbnQt +c2l6ZTogMTRwdDsiPlRoaXMgaXMgYSAqKmJvbGQqKiB3b3JkIGluIE1hcmtkb3duPC9kaXY+PGRp +diBzdHlsZT0iZm9udC1zaXplOiAxNHB0OyI+PGJyPjwvZGl2PjxkaXYgc3R5bGU9ImZvbnQtc2l6 +ZTogMTRwdDsiPlRoaXMgaXMgYSBsaW5rIDxhIGhyZWY9Imh0dHA6Ly9leGFtcGxlLmNvbSI+aHR0 +cDovL2V4YW1wbGUuY29tPC9hPjxicj4mbmJzcDs8L2Rpdj48ZGl2IHN0eWxlPSJmb250LXNpemU6 +IDE0cHQ7Ij48YnI+PC9kaXY+PGRpdiBzdHlsZT0icGFkZGluZy10b3A6IDVweDsgYm9yZGVyLXRv +cC1jb2xvcjogcmdiKDIyOSwgMjI5LCAyMjkpOyBib3JkZXItdG9wLXdpZHRoOiAxcHg7IGJvcmRl +ci10b3Atc3R5bGU6IHNvbGlkOyI+PGRpdj48Zm9udCBmYWNlPSIgJ0NhbGlicmknLCAnU2Vnb2Ug +VUknLCAnTWVpcnlvJywgJ01pY3Jvc29mdCBZYUhlaSBVSScsICdNaWNyb3NvZnQgSmhlbmdIZWkg +VUknLCAnTWFsZ3VuIEdvdGhpYycsICdzYW5zLXNlcmlmJyIgc3R5bGU9J2xpbmUtaGVpZ2h0OiAx +NXB0OyBsZXR0ZXItc3BhY2luZzogMC4wMmVtOyBmb250LWZhbWlseTogIkNhbGlicmkiLCAiU2Vn +b2UgVUkiLCAiTWVpcnlvIiwgIk1pY3Jvc29mdCBZYUhlaSBVSSIsICJNaWNyb3NvZnQgSmhlbmdI +ZWkgVUkiLCAiTWFsZ3VuIEdvdGhpYyIsICJzYW5zLXNlcmlmIjsgZm9udC1zaXplOiAxMnB0Oyc+ +PGI+RnJvbTo8L2I+Jm5ic3A7PGEgaHJlZj0ibWFpbHRvOmluZm9AZGlzY291cnNlLm9yZyIgdGFy +Z2V0PSJfcGFyZW50Ij5BcnBpdCBKYWxhbjwvYT48YnI+PGI+U2VudDo8L2I+Jm5ic3A74oCORnJp +ZGF54oCOLCDigI5Ob3ZlbWJlcuKAjiDigI4yOOKAjiwg4oCOMjAxNCDigI4xMuKAjjrigI4zNeKA +jiDigI5QTTxicj48Yj5Ubzo8L2I+Jm5ic3A7PGEgaHJlZj0ibWFpbHRvOmphdHdvb2RAY29kaW5n +aG9ycm9yLmNvbSIgdGFyZ2V0PSJfcGFyZW50Ij5qZWZmIGF0d29vZDwvYT48L2ZvbnQ+PC9kaXY+ +PC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdiBkaXI9IiI+PGRpdj4KCjx0YWJsZSB0YWJpbmRleD0i +LTEiIHN0eWxlPSJtYXJnaW4tYm90dG9tOiAyNXB4OyIgYm9yZGVyPSIwIiBjZWxsc3BhY2luZz0i +MCIgY2VsbHBhZGRpbmc9IjAiPgogIDx0Ym9keT4KICAgIDx0cj4KICAgICAgPHRkIHN0eWxlPSJ3 +aWR0aDogNTVweDsgdmVydGljYWwtYWxpZ246IHRvcDsiPgogICAgICAgIDxpbWcgd2lkdGg9IjQ1 +IiBoZWlnaHQ9IjQ1IiB0YWJpbmRleD0iLTEiIHN0eWxlPSJtYXgtd2lkdGg6IDEwMCU7IiBzcmM9 +Imh0dHBzOi8vbWV0YS1kaXNjb3Vyc2UuZ2xvYmFsLnNzbC5mYXN0bHkubmV0L3VzZXJfYXZhdGFy +L21ldGEuZGlzY291cnNlLm9yZy90ZWNoYXBqLzQ1LzMyODEucG5nIiBkYXRhLW1zLWltZ3NyYz0i +aHR0cHM6Ly9tZXRhLWRpc2NvdXJzZS5nbG9iYWwuc3NsLmZhc3RseS5uZXQvdXNlcl9hdmF0YXIv +bWV0YS5kaXNjb3Vyc2Uub3JnL3RlY2hhcGovNDUvMzI4MS5wbmciPgogICAgICA8L3RkPgogICAg +ICA8dGQ+CiAgICAgICAgPGEgc3R5bGU9J2NvbG9yOiByZ2IoNTksIDg5LCAxNTIpOyBmb250LWZh +bWlseTogImx1Y2lkYSBncmFuZGUiLHRhaG9tYSx2ZXJkYW5hLGFyaWFsLHNhbnMtc2VyaWY7IGZv +bnQtc2l6ZTogMTNweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsn +IGhyZWY9Imh0dHBzOi8vbWV0YS5kaXNjb3Vyc2Uub3JnL3VzZXJzL3RlY2hhcGoiIHRhcmdldD0i +X3BhcmVudCI+dGVjaEFQSjwvYT48YnI+CiAgICAgICAgPHNwYW4gc3R5bGU9J3RleHQtYWxpZ246 +IHJpZ2h0OyBjb2xvcjogcmdiKDE1MywgMTUzLCAxNTMpOyBwYWRkaW5nLXJpZ2h0OiA1cHg7IGZv +bnQtZmFtaWx5OiAibHVjaWRhIGdyYW5kZSIsdGFob21hLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJp +ZjsgZm9udC1zaXplOiAxMXB4Oyc+Tm92ZW1iZXIgMjg8L3NwYW4+CiAgICAgIDwvdGQ+CiAgICA8 +L3RyPgogICAgPHRyPgogICAgICA8dGQgc3R5bGU9InBhZGRpbmctdG9wOiA1cHg7IiBjb2xzcGFu +PSIyIj4KPHAgc3R5bGU9ImJvcmRlcjogMHB4IGJsYWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IG1h +cmdpbi10b3A6IDBweDsiPlRlc3QgcmVwbHkuPC9wPgoKPHAgc3R5bGU9ImJvcmRlcjogMHB4IGJs +YWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IG1hcmdpbi10b3A6IDBweDsiPkZpcnN0IHBhcmFncmFw +aC48L3A+Cgo8cCBzdHlsZT0iYm9yZGVyOiAwcHggYmxhY2s7IGJvcmRlci1pbWFnZTogbm9uZTsg +bWFyZ2luLXRvcDogMHB4OyI+U2Vjb25kIHBhcmFncmFwaC48L3A+CjwvdGQ+CiAgICA8L3RyPgog +IDwvdGJvZHk+CjwvdGFibGU+CgoKICA8ZGl2IHN0eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAx +MDIpOyI+CiAgICA8cD5UbyByZXNwb25kLCByZXBseSB0byB0aGlzIGVtYWlsIG9yIHZpc2l0IDxh +IHN0eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyBmb250LXdlaWdodDogYm9sZDsgdGV4 +dC1kZWNvcmF0aW9uOiBub25lOyIgaHJlZj0iaHR0cHM6Ly9tZXRhLmRpc2NvdXJzZS5vcmcvdC90 +ZXN0aW5nLWRlZmF1bHQtZW1haWwtcmVwbGllcy8yMjYzOC8zIiB0YXJnZXQ9Il9wYXJlbnQiPmh0 +dHBzOi8vbWV0YS5kaXNjb3Vyc2Uub3JnL3QvdGVzdGluZy1kZWZhdWx0LWVtYWlsLXJlcGxpZXMv +MjI2MzgvMzwvYT4gaW4geW91ciBicm93c2VyLjwvcD4KICA8L2Rpdj4KICA8aHIgc3R5bGU9ImJv +cmRlcjogMXB4IGJsYWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3Jv +dW5kLWNvbG9yOiByZ2IoMjIxLCAyMjEsIDIyMSk7Ij4KICA8aDQ+UHJldmlvdXMgUmVwbGllczwv +aDQ+CgogIDx0YWJsZSB0YWJpbmRleD0iLTEiIHN0eWxlPSJtYXJnaW4tYm90dG9tOiAyNXB4OyIg +Ym9yZGVyPSIwIiBjZWxsc3BhY2luZz0iMCIgY2VsbHBhZGRpbmc9IjAiPgogIDx0Ym9keT4KICAg +IDx0cj4KICAgICAgPHRkIHN0eWxlPSJ3aWR0aDogNTVweDsgdmVydGljYWwtYWxpZ246IHRvcDsi +PgogICAgICAgIDxpbWcgd2lkdGg9IjQ1IiBoZWlnaHQ9IjQ1IiB0YWJpbmRleD0iLTEiIHN0eWxl +PSJtYXgtd2lkdGg6IDEwMCU7IiBzcmM9Imh0dHBzOi8vbWV0YS1kaXNjb3Vyc2UuZ2xvYmFsLnNz +bC5mYXN0bHkubmV0L3VzZXJfYXZhdGFyL21ldGEuZGlzY291cnNlLm9yZy9jb2Rpbmdob3Jyb3Iv +NDUvNTI5Ny5wbmciIGRhdGEtbXMtaW1nc3JjPSJodHRwczovL21ldGEtZGlzY291cnNlLmdsb2Jh +bC5zc2wuZmFzdGx5Lm5ldC91c2VyX2F2YXRhci9tZXRhLmRpc2NvdXJzZS5vcmcvY29kaW5naG9y +cm9yLzQ1LzUyOTcucG5nIj4KICAgICAgPC90ZD4KICAgICAgPHRkPgogICAgICAgIDxhIHN0eWxl +PSdjb2xvcjogcmdiKDU5LCA4OSwgMTUyKTsgZm9udC1mYW1pbHk6ICJsdWNpZGEgZ3JhbmRlIix0 +YWhvbWEsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmOyBmb250LXNpemU6IDEzcHg7IGZvbnQtd2Vp +Z2h0OiBib2xkOyB0ZXh0LWRlY29yYXRpb246IG5vbmU7JyBocmVmPSJodHRwczovL21ldGEuZGlz +Y291cnNlLm9yZy91c2Vycy9jb2Rpbmdob3Jyb3IiIHRhcmdldD0iX3BhcmVudCI+Y29kaW5naG9y +cm9yPC9hPjxicj4KICAgICAgICA8c3BhbiBzdHlsZT0ndGV4dC1hbGlnbjogcmlnaHQ7IGNvbG9y +OiByZ2IoMTUzLCAxNTMsIDE1Myk7IHBhZGRpbmctcmlnaHQ6IDVweDsgZm9udC1mYW1pbHk6ICJs +dWNpZGEgZ3JhbmRlIix0YWhvbWEsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmOyBmb250LXNpemU6 +IDExcHg7Jz5Ob3ZlbWJlciAyODwvc3Bhbj4KICAgICAgPC90ZD4KICAgIDwvdHI+CiAgICA8dHI+ +CiAgICAgIDx0ZCBzdHlsZT0icGFkZGluZy10b3A6IDVweDsiIGNvbHNwYW49IjIiPgo8cCBzdHls +ZT0iYm9yZGVyOiAwcHggYmxhY2s7IGJvcmRlci1pbWFnZTogbm9uZTsgbWFyZ2luLXRvcDogMHB4 +OyI+V2UncmUgdGVzdGluZyB0aGUgbGF0ZXN0IEdpdEh1YiBlbWFpbCBwcm9jZXNzaW5nIGxpYnJh +cnkgd2hpY2ggd2UgYXJlIGludGVncmF0aW5nIG5vdy48L3A+Cgo8cCBzdHlsZT0iYm9yZGVyOiAw +cHggYmxhY2s7IGJvcmRlci1pbWFnZTogbm9uZTsgbWFyZ2luLXRvcDogMHB4OyI+PGEgc3R5bGU9 +ImNvbG9yOiByZ2IoMCwgMTAyLCAxNTMpOyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1kZWNvcmF0 +aW9uOiBub25lOyIgaHJlZj0iaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9lbWFpbF9yZXBseV9w +YXJzZXIiIHRhcmdldD0iX3BhcmVudCI+aHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9lbWFpbF9y +ZXBseV9wYXJzZXI8L2E+PC9wPgoKPHAgc3R5bGU9ImJvcmRlcjogMHB4IGJsYWNrOyBib3JkZXIt +aW1hZ2U6IG5vbmU7IG1hcmdpbi10b3A6IDBweDsiPkdvIGFoZWFkIGFuZCByZXBseSB0byB0aGlz +IHRvcGljIGFuZCBJJ2xsIHJlcGx5IGZyb20gdmFyaW91cyBlbWFpbCBjbGllbnRzIGZvciB0ZXN0 +aW5nLjwvcD4KPC90ZD4KICAgIDwvdHI+CiAgPC90Ym9keT4KPC90YWJsZT4KCgo8aHIgc3R5bGU9 +ImJvcmRlcjogMXB4IGJsYWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNr +Z3JvdW5kLWNvbG9yOiByZ2IoMjIxLCAyMjEsIDIyMSk7Ij4KCjxkaXYgc3R5bGU9ImNvbG9yOiBy +Z2IoMTAyLCAxMDIsIDEwMik7Ij4KPHA+VG8gcmVzcG9uZCwgcmVwbHkgdG8gdGhpcyBlbWFpbCBv +ciB2aXNpdCA8YSBzdHlsZT0iY29sb3I6IHJnYigxMDIsIDEwMiwgMTAyKTsgZm9udC13ZWlnaHQ6 +IGJvbGQ7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsiIGhyZWY9Imh0dHBzOi8vbWV0YS5kaXNjb3Vy +c2Uub3JnL3QvdGVzdGluZy1kZWZhdWx0LWVtYWlsLXJlcGxpZXMvMjI2MzgvMyIgdGFyZ2V0PSJf +cGFyZW50Ij5odHRwczovL21ldGEuZGlzY291cnNlLm9yZy90L3Rlc3RpbmctZGVmYXVsdC1lbWFp +bC1yZXBsaWVzLzIyNjM4LzM8L2E+IGluIHlvdXIgYnJvd3Nlci48L3A+CjwvZGl2Pgo8ZGl2IHN0 +eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyI+CjxwPlRvIHVuc3Vic2NyaWJlIGZyb20g +dGhlc2UgZW1haWxzLCB2aXNpdCB5b3VyIDxhIHN0eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAx +MDIpOyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1kZWNvcmF0aW9uOiBub25lOyIgaHJlZj0iaHR0 +cHM6Ly9tZXRhLmRpc2NvdXJzZS5vcmcvbXkvcHJlZmVyZW5jZXMiIHRhcmdldD0iX3BhcmVudCI+ +dXNlciBwcmVmZXJlbmNlczwvYT4uPC9wPgo8L2Rpdj4KPC9kaXY+CjwvZGl2PjxkaXYgc3R5bGU9 +ImZvbnQtc2l6ZTogMTRwdDsiPjxicj48L2Rpdj48L2Rpdj4KPC9ib2R5Pgo8L2h0bWw+Cg== + +--_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_-- From 9defb6879bad42d61a88aab28ade69a3ac636331 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 1 Dec 2014 15:34:50 +1100 Subject: [PATCH 108/991] upgrade to rails master --- Gemfile | 17 ++++-- Gemfile.lock | 4 +- Gemfile_master.lock | 144 +++++++++++++++++++++++--------------------- 3 files changed, 92 insertions(+), 73 deletions(-) diff --git a/Gemfile b/Gemfile index cf663f22a9..08e62becf2 100644 --- a/Gemfile +++ b/Gemfile @@ -67,17 +67,19 @@ unless Bundler::Dependency::PLATFORM_MAP.include? :mri_21 end end -gem 'seed-fu', '~> 2.3.3' if rails_master? gem 'arel', git: 'https://github.com/rails/arel.git' gem 'rails', git: 'https://github.com/rails/rails.git' + gem 'rails-observers', git: 'https://github.com/SamSaffron/rails-observers.git' + gem 'seed-fu', git: 'https://github.com/SamSaffron/seed-fu.git', branch: 'discourse' else + gem 'seed-fu', '~> 2.3.3' gem 'rails' + gem 'rails-observers' end gem 'actionpack-action_caching' -gem 'rails-observers' # Rails 4.1.6+ will relax the mail gem version requirement to `~> 2.5, >= 2.5.4`. # However, mail gem 2.6.x currently does not work with discourse because of the @@ -144,8 +146,15 @@ gem 'omniauth-github-discourse', require: 'omniauth-github' gem 'omniauth-oauth2', require: false gem 'omniauth-google-oauth2' gem 'oj' -# while resolving https://groups.google.com/forum/#!topic/ruby-pg/5_ylGmog1S4 -gem 'pg', '0.15.1' + +if rails_master? + # native casting + gem 'pg', '0.18.0.pre20141117110243' +else + # while resolving https://groups.google.com/forum/#!topic/ruby-pg/5_ylGmog1S4 + gem 'pg', '0.15.1' +end + gem 'pry-rails', require: false gem 'rake' diff --git a/Gemfile.lock b/Gemfile.lock index 0f56e0cbc1..8c5c8a7b60 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,7 @@ GEM fastimage (1.6.3) addressable (~> 2.3, >= 2.3.5) ffi (1.9.5) - flamegraph (0.0.8) + flamegraph (0.0.9) fast_stack fog (1.22.1) fog-brightbox @@ -273,7 +273,7 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.13.0) - rake (10.3.2) + rake (10.4.0) rake-compiler (0.9.3) rake rb-fsevent (0.9.4) diff --git a/Gemfile_master.lock b/Gemfile_master.lock index c962d2baec..c3f6d3a39b 100644 --- a/Gemfile_master.lock +++ b/Gemfile_master.lock @@ -7,66 +7,26 @@ GIT activemodel (>= 3.0) GIT - remote: https://github.com/rails/arel.git - revision: 1fefe71b1872c0a83f09231164863cd8dbb57174 + remote: https://github.com/SamSaffron/rails-observers.git + revision: 7d2222d758603a004f6599f82a7068ffeb2d7ebf specs: - arel (6.0.0) + rails-observers (0.1.2) + activemodel (> 4.0) GIT - remote: https://github.com/rails/rails.git - revision: 2f8be7ebafcf7815f9f3ec7983789157525a60fa + remote: https://github.com/SamSaffron/seed-fu.git + revision: d93df3b6364ea938d87c5629bf950b0d1ffe037e + branch: discourse specs: - actionmailer (4.2.0.beta4) - actionpack (= 4.2.0.beta4) - actionview (= 4.2.0.beta4) - activejob (= 4.2.0.beta4) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.0.beta4) - actionview (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - rack (~> 1.6.0.beta) - rack-test (~> 0.6.2) - rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.1) - actionview (4.2.0.beta4) - activesupport (= 4.2.0.beta4) - builder (~> 3.1) - erubis (~> 2.7.0) - rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.1) - activejob (4.2.0.beta4) - activesupport (= 4.2.0.beta4) - globalid (>= 0.3.0) - activemodel (4.2.0.beta4) - activesupport (= 4.2.0.beta4) - builder (~> 3.1) - activerecord (4.2.0.beta4) - activemodel (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - arel (~> 6.0) - activesupport (4.2.0.beta4) - i18n (>= 0.7.0.beta1, < 0.8) - json (~> 1.7, >= 1.7.7) - minitest (~> 5.1) - thread_safe (~> 0.1) - tzinfo (~> 1.1) - rails (4.2.0.beta4) - actionmailer (= 4.2.0.beta4) - actionpack (= 4.2.0.beta4) - actionview (= 4.2.0.beta4) - activejob (= 4.2.0.beta4) - activemodel (= 4.2.0.beta4) - activerecord (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - bundler (>= 1.3.0, < 2.0) - railties (= 4.2.0.beta4) - sprockets-rails (~> 3.0.0.beta1) - railties (4.2.0.beta4) - actionpack (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) + seed-fu (2.3.3) + activerecord (>= 3.1) + activesupport (>= 3.1) + +GIT + remote: https://github.com/rails/arel.git + revision: 98fc25991137ee09b6800578117f8c1c322680f2 + specs: + arel (6.0.0) GIT remote: https://github.com/rails/sass-rails.git @@ -78,6 +38,61 @@ GIT sprockets (~> 2.12) sprockets-rails (>= 2.0, < 4.0) +PATH + remote: ../rails + specs: + actionmailer (5.0.0.alpha) + actionpack (= 5.0.0.alpha) + actionview (= 5.0.0.alpha) + activejob (= 5.0.0.alpha) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 1.0, >= 1.0.5) + actionpack (5.0.0.alpha) + actionview (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + rack (~> 1.6.0.beta2) + rack-test (~> 0.6.2) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.1) + actionview (5.0.0.alpha) + activesupport (= 5.0.0.alpha) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.1) + activejob (5.0.0.alpha) + activesupport (= 5.0.0.alpha) + globalid (>= 0.3.0) + activemodel (5.0.0.alpha) + activesupport (= 5.0.0.alpha) + builder (~> 3.1) + activerecord (5.0.0.alpha) + activemodel (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + arel (~> 6.0) + activesupport (5.0.0.alpha) + i18n (>= 0.7.0.beta1, < 0.8) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.1) + tzinfo (~> 1.1) + rails (5.0.0.alpha) + actionmailer (= 5.0.0.alpha) + actionpack (= 5.0.0.alpha) + actionview (= 5.0.0.alpha) + activejob (= 5.0.0.alpha) + activemodel (= 5.0.0.alpha) + activerecord (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + bundler (>= 1.3.0, < 2.0) + railties (= 5.0.0.alpha) + sprockets-rails + railties (5.0.0.alpha) + actionpack (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + PATH remote: vendor/gems/rails_multisite specs: @@ -150,7 +165,7 @@ GEM fastimage (1.6.3) addressable (~> 2.3, >= 2.3.5) ffi (1.9.5) - flamegraph (0.0.8) + flamegraph (0.0.9) fast_stack fog (1.22.1) fog-brightbox @@ -285,7 +300,7 @@ GEM openid-redis-store (0.0.2) redis ruby-openid - pg (0.15.1) + pg (0.18.0.pre20141117110243) polyglot (0.3.5) progress (3.0.1) pry (0.10.1) @@ -300,7 +315,7 @@ GEM rack (>= 1.1, < 2.0) qunit-rails (0.0.7) railties - rack (1.6.0.beta) + rack (1.6.0.beta2) rack-mini-profiler (0.9.2) rack (>= 1.1.3) rack-openid (1.3.1) @@ -318,8 +333,6 @@ GEM rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.1) loofah (~> 2.0) - rails-observers (0.1.2) - activemodel (~> 4.0) raindrops (0.13.0) rake (10.4.0) rake-compiler (0.9.3) @@ -373,9 +386,6 @@ GEM nokogiri (>= 1.4.4) nokogumbo (= 1.1.12) sass (3.2.19) - seed-fu (2.3.3) - activerecord (>= 3.1, < 4.2) - activesupport (>= 3.1, < 4.2) shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) shoulda-matchers (>= 1.4.1, < 3.0) @@ -498,7 +508,7 @@ DEPENDENCIES omniauth-twitter onebox openid-redis-store - pg (= 0.15.1) + pg (= 0.18.0.pre20141117110243) pry-nav pry-rails puma @@ -506,7 +516,7 @@ DEPENDENCIES rack-mini-profiler rack-protection rails! - rails-observers + rails-observers! rails_multisite! rake rb-fsevent @@ -525,7 +535,7 @@ DEPENDENCIES sanitize sass sass-rails! - seed-fu (~> 2.3.3) + seed-fu! shoulda sidekiq simple-rss From 5352a7f53ca1cb588576ea7883ba8afb142cbe32 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sun, 30 Nov 2014 21:25:54 -0800 Subject: [PATCH 109/991] Don't `use_route` See https://github.com/rails/rails/pull/17453 and https://github.com/rails/rails/pull/17725 --- .../spec/poll_plugin/poll_controller_spec.rb | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/plugins/poll/spec/poll_plugin/poll_controller_spec.rb b/plugins/poll/spec/poll_plugin/poll_controller_spec.rb index a621db5762..8f912d8492 100644 --- a/plugins/poll/spec/poll_plugin/poll_controller_spec.rb +++ b/plugins/poll/spec/poll_plugin/poll_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe PollPlugin::PollController, type: :controller do + routes { PollPlugin::Engine.routes } + let(:topic) { create_topic(title: "Poll: Chitoge vs Onodera") } let!(:post) { create_post(topic: topic, raw: "Pick one.\n\n[poll]\n* Chitoge\n* Onodera\n[/poll]") } let(:user1) { Fabricate(:user) } @@ -9,43 +11,43 @@ describe PollPlugin::PollController, type: :controller do describe 'vote' do it "returns 403 if no user is logged in" do - xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Chitoge" response.should be_forbidden end it "returns 400 if post_id or invalid option is not specified" do log_in_user user1 - xhr :put, :vote, use_route: :poll + xhr :put, :vote response.status.should eq(400) - xhr :put, :vote, post_id: post.id, use_route: :poll + xhr :put, :vote, post_id: post.id response.status.should eq(400) - xhr :put, :vote, option: "Chitoge", use_route: :poll + xhr :put, :vote, option: "Chitoge" response.status.should eq(400) - xhr :put, :vote, post_id: post.id, option: "Tsugumi", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Tsugumi" response.status.should eq(400) end it "returns 400 if post_id doesn't correspond to a poll post" do log_in_user user1 post2 = create_post(topic: topic, raw: "Generic reply") - xhr :put, :vote, post_id: post2.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post2.id, option: "Chitoge" end it "saves votes correctly" do MessageBus.expects(:publish).times(3) log_in_user user1 - xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Chitoge" PollPlugin::Poll.new(post).get_vote(user1).should eq("Chitoge") log_in_user user2 - xhr :put, :vote, post_id: post.id, option: "Onodera", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Onodera" PollPlugin::Poll.new(post).get_vote(user2).should eq("Onodera") PollPlugin::Poll.new(post).details["Chitoge"].should eq(1) PollPlugin::Poll.new(post).details["Onodera"].should eq(1) - xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Chitoge" PollPlugin::Poll.new(post).get_vote(user2).should eq("Chitoge") PollPlugin::Poll.new(post).details["Chitoge"].should eq(2) @@ -57,21 +59,21 @@ describe PollPlugin::PollController, type: :controller do it "returns 400 if post_id doesn't correspond to a poll post" do log_in_user admin post2 = create_post(topic: topic, raw: "Generic reply") - xhr :put, :toggle_close, post_id: post2.id, use_route: :poll + xhr :put, :toggle_close, post_id: post2.id response.status.should eq(400) end it "returns 400 if the topic is locked" do log_in_user admin topic.update_attributes closed: true - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id response.status.should eq(400) end it "raises Discourse::InvalidAccess is the user is not authorized" do log_in_user user1 expect do - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id end.to raise_error(Discourse::InvalidAccess) end @@ -79,10 +81,10 @@ describe PollPlugin::PollController, type: :controller do I18n.stubs(:t).with('poll.prefix').returns("Poll ") I18n.stubs(:t).with('poll.closed_prefix').returns("Closed Poll ") log_in_user admin - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id response.status.should eq(200) topic.reload.title.should == "Closed Poll : Chitoge vs Onodera" - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id response.status.should eq(200) topic.reload.title.should == "Poll : Chitoge vs Onodera" end From a3a04e06b61c7f60943985aa5535a4486874caa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 1 Dec 2014 11:38:33 +0100 Subject: [PATCH 110/991] fix images in readme --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ae82fb76fc..006e797675 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ To learn more about the philosophy and goals of the project, [visit **discourse. ## Screenshots -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/boing-boing-latest-small2.png)](http://bbs.boingboing.net) -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/how-to-geek-profile-small2.png?breakcache=1)](http://discuss.howtogeek.com) -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/new-relic-categories-small2.png)](http://discuss.newrelic.com) -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/turtle-rock-topic-small2.jpg)](https://talk.turtlerockstudios.com/) -[![](https://raw.github.com/discourse/discourse-docimages/master/readme/nexus-7-mobile-discourse-small3.png)](http://discuss.atom.io) -[![](https://raw.github.com/discourse/discourse-docimages/master/readme/iphone-5s-mobile-discourse-small4.png)](http://discourse.soylent.me) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/boing-boing-latest-small2.png)](http://bbs.boingboing.net) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/how-to-geek-profile-small2.png)](http://discuss.howtogeek.com) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/new-relic-categories-small2.png)](http://discuss.newrelic.com) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/turtle-rock-topic-small2.jpg)](https://talk.turtlerockstudios.com/) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/nexus-7-mobile-discourse-small3.png)](http://discuss.atom.io) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/iphone-5s-mobile-discourse-small4.png)](http://discourse.soylent.me) ## Development From c00c5e1024b467fe5c29b59ba5e5a89e91b1c3d4 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Mon, 1 Dec 2014 03:36:25 -0800 Subject: [PATCH 111/991] our previous mobile avatar tap fix was incorrect --- app/assets/stylesheets/mobile/topic-post.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 5fbdddf577..cfd7cff339 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -450,17 +450,18 @@ span.highlighted { .topic-avatar { float: left; position: relative; + z-index:999; /* must render on top of topic-body + topic-meta-data, otherwise not tappable */ } .topic-meta-data { - margin-left: 60px; white-space: nowrap; position: absolute; - width: 80%; + width: 100%; left: 0px; .names { margin: 5px 0 0 5px; line-height: 17px; + padding-left: 60px; span { display: block; } From cc76087f59b5039ec60d5458345a18354fd24e0e Mon Sep 17 00:00:00 2001 From: James Kiesel Date: Sun, 30 Nov 2014 15:09:34 +1300 Subject: [PATCH 112/991] Add new private message button on topics list That'll teach me to wildly refactor things. --- .../discourse/controllers/user-topics-list.js.es6 | 2 +- app/assets/javascripts/discourse/routes/user.js.es6 | 6 +++--- .../discourse/templates/list/user_topics_list.hbs | 6 ++++++ app/assets/stylesheets/common/base/user.scss | 3 +++ app/serializers/user_serializer.rb | 7 +++++++ config/locales/client.en.yml | 1 + 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 b/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 index c39d34df95..58320d8c62 100644 --- a/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 @@ -1,7 +1,7 @@ import ObjectController from 'discourse/controllers/object'; // Lists of topics on a user's page. -export default ObjectController.extend({ +export default ObjectController.extend(Discourse.HasCurrentUser, { needs: ["application"], hideCategory: false, showParticipants: false, diff --git a/app/assets/javascripts/discourse/routes/user.js.es6 b/app/assets/javascripts/discourse/routes/user.js.es6 index 910abb196f..ac4ddb49cf 100644 --- a/app/assets/javascripts/discourse/routes/user.js.es6 +++ b/app/assets/javascripts/discourse/routes/user.js.es6 @@ -15,11 +15,11 @@ export default Discourse.Route.extend({ Discourse.logout(); }, - composePrivateMessage: function() { - var user = this.modelFor('user'); + composePrivateMessage: function(user) { + var recipient = user ? user.username : ''; return this.controllerFor('composer').open({ action: Discourse.Composer.PRIVATE_MESSAGE, - usernames: user.get('username'), + usernames: recipient, archetypeId: 'private_message', draftKey: 'new_private_message' }); diff --git a/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs b/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs index ac54a240bf..f0270c6ad1 100644 --- a/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs +++ b/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs @@ -1,3 +1,9 @@ +{{#if currentUser.can_send_private_messages}} + +{{/if}} + {{basic-topic-list topicList=model hideCategory=hideCategory showParticipants=showParticipants diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index 8f8d35fded..1e77ac0b82 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -73,3 +73,6 @@ } } +.new-private-message { + margin-bottom: 15px; +} diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 1313db0b76..d7560c6c89 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -45,6 +45,7 @@ class UserSerializer < BasicUserSerializer :can_edit_email, :can_edit_name, :stats, + :can_send_private_messages, :can_send_private_message_to_user, :bio_excerpt, :trust_level, @@ -178,6 +179,12 @@ class UserSerializer < BasicUserSerializer UserAction.stats(object.id, scope) end + # Needed because 'send_private_message_to_user' will always return false + # when the current user is being serialized + def can_send_private_messages + scope.can_send_private_message?(Discourse.system_user) + end + def can_send_private_message_to_user scope.can_send_private_message?(object) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 317d37e421..f026183e87 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -293,6 +293,7 @@ en: mute: "Mute" edit: "Edit Preferences" download_archive: "download archive of my posts" + new_private_message: "New Private Message" private_message: "Private Message" private_messages: "Messages" activity_stream: "Activity" From bdc92eec70f4c6a1d8766e4fe8d8d8f5405c47d8 Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Mon, 1 Dec 2014 06:03:25 -0700 Subject: [PATCH 113/991] Have log_out method return json. This commit helps improve the discourse_api experience so that we can check the json response if it was a success or not. This commit also checks that a 404 is sent instead of a 500 if a bad user_id is passed in. --- app/controllers/admin/users_controller.rb | 12 ++++++---- config/locales/client.en.yml | 1 + .../admin/users_controller_spec.rb | 23 +++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index efd9b3be43..0688f38948 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -66,10 +66,14 @@ class Admin::UsersController < Admin::AdminController end def log_out - @user.auth_token = nil - @user.save! - MessageBus.publish "/logout", @user.id, user_ids: [@user.id] - render nothing: true + if @user + @user.auth_token = nil + @user.save! + MessageBus.publish "/logout", @user.id, user_ids: [@user.id] + render json: success_json + else + render json: {error: I18n.t('admin_js.admin.users.id_not_found')}, status: 404 + end end def refresh_browsers diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 317d37e421..083c4704c1 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1887,6 +1887,7 @@ en: create: 'Add Admin User' last_emailed: "Last Emailed" not_found: "Sorry, that username doesn't exist in our system." + id_not_found: "Sorry, that user id doesn't exist in our system." active: "Active" show_emails: "Show Emails" nav: diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 1740be819f..7a5566827e 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -360,6 +360,29 @@ describe Admin::UsersController do end end + context 'log_out' do + before do + @reg_user = Fabricate(:user) + end + + it 'returns JSON' do + xhr :put, :log_out, user_id: @reg_user.id + ::JSON.parse(response.body).should be_present + end + + it "returns success" do + xhr :put, :log_out, user_id: @reg_user.id + response.should be_success + json = ::JSON.parse(response.body) + json['success'].should == "OK" + end + + it "returns 404 when user_id does not exist" do + xhr :put, :log_out, user_id: 123123 + response.should_not be_success + end + end + context 'block' do before do @reg_user = Fabricate(:user) From e2fb5310d85cc52f760a644c4da3e0321f1c7818 Mon Sep 17 00:00:00 2001 From: Greg Kempe Date: Mon, 17 Nov 2014 16:46:58 +0200 Subject: [PATCH 114/991] Traverse symlinks to plugins in dev mode when compiling stylesheets When developing plugins it's useful to symlink the to the plugin directory from the discourse directory, since that means the two are separate git repos. However, Dir.glob doesn't by default traverse symlinks. This change means that the SASS compilation caching detects when any of a plugin's files have changed. --- lib/sass/discourse_stylesheets.rb | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/sass/discourse_stylesheets.rb b/lib/sass/discourse_stylesheets.rb index 028def076e..ca0c5a9d63 100644 --- a/lib/sass/discourse_stylesheets.rb +++ b/lib/sass/discourse_stylesheets.rb @@ -56,10 +56,17 @@ class DiscourseStylesheets end def self.max_file_mtime - [ "#{Rails.root}/app/assets/stylesheets/**/*.*css", - "#{Rails.root}/plugins/**/*.*css", - "#{Rails.root}/plugins/**/plugin.rb" ].map do |pattern| - Dir.glob(pattern).map { |x| File.mtime(x) }.max + globs = ["#{Rails.root}/app/assets/stylesheets/**/*.*css"] + + for path in (Discourse.plugins || []).map { |plugin| File.dirname(plugin.path) } + globs += [ + "#{path}/plugin.rb", + "#{path}/**/*.*css", + ] + end + + globs.map do |pattern| + Dir.glob(pattern).map { |x| File.mtime(x) }.max end.compact.max.to_i end From 15c105eba5360c59c56cb548c4374b754b3577a4 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 1 Dec 2014 23:51:14 +0530 Subject: [PATCH 115/991] Add test for email with inline reply --- spec/components/email/receiver_spec.rb | 47 ++++++++++++++++++++ spec/fixtures/emails/inline_reply.eml | 60 ++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 spec/fixtures/emails/inline_reply.eml diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index a7e93873f2..9982060bdf 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -102,6 +102,53 @@ It will also be my *only* reply." ) end + it "handles inline reply" do + test_parse_body(fixture_file("emails/inline_reply.eml")). + should == ( +"On Wed, Oct 8, 2014 at 11:12 AM, techAPJ wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog." + ) + end + it "should not include previous replies" do test_parse_body(fixture_file("emails/previous_replies.eml")).should_not match /Previous Replies/ end diff --git a/spec/fixtures/emails/inline_reply.eml b/spec/fixtures/emails/inline_reply.eml new file mode 100644 index 0000000000..39625a225d --- /dev/null +++ b/spec/fixtures/emails/inline_reply.eml @@ -0,0 +1,60 @@ + +MIME-Version: 1.0 +In-Reply-To: +References: + <5434ced4ee0f9_663fb0b5f76070593b@discourse-app.mail> +Date: Mon, 1 Dec 2014 20:48:40 +0530 +Delivered-To: someone@googlemail.com +Subject: Re: [Discourse] [Meta] Testing reply via email +From: Walter White +To: Discourse +Content-Type: multipart/alternative; boundary=20cf30363f8522466905092920a6 + +--20cf30363f8522466905092920a6 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +On Wed, Oct 8, 2014 at 11:12 AM, techAPJ +wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. + +--20cf30363f8522466905092920a6-- From b547be44b2bc1a54841a0ea17a7fbdb9bbac3939 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 1 Dec 2014 14:53:03 -0500 Subject: [PATCH 116/991] Better error message output --- .../discourse/routes/application.js.es6 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/routes/application.js.es6 b/app/assets/javascripts/discourse/routes/application.js.es6 index 794448f8d1..ca598b7748 100644 --- a/app/assets/javascripts/discourse/routes/application.js.es6 +++ b/app/assets/javascripts/discourse/routes/application.js.es6 @@ -27,10 +27,16 @@ var ApplicationRoute = Discourse.Route.extend({ } var exceptionController = this.controllerFor('exception'), - errorString = err.toString(); - if (err.statusText) { - errorString = err.statusText; - } + errorString = err.toString(), + stack = err.stack; + + // If we have a stack call `toString` on it. It gives us a better + // stack trace since `console.error` uses the stack track of this + // error callback rather than the original error. + if (stack) { errorString = stack.toString(); } + + if (err.statusText) { errorString = err.statusText; } + var c = window.console; if (c && c.error) { c.error(errorString); From 9bdac79ba697c91b1a0827d40af0cfa4a390fa83 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 1 Dec 2014 17:15:07 -0500 Subject: [PATCH 117/991] Make suspect report use <= 1 instead of = 0 --- lib/admin_user_index_query.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index f8ac8a8ca8..8af37f0861 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -43,7 +43,7 @@ class AdminUserIndexQuery where_conds = [] # One signal: no reading yet the user has bio text - where_conds << "user_stats.posts_read_count = 0 AND user_stats.topics_entered = 0" + where_conds << "user_stats.posts_read_count <= 1 AND user_stats.topics_entered <= 1" @query.activated .references(:user_stats) From f226e4efc0ab4a0ed2de497d3d8db808caa44b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 2 Dec 2014 02:16:30 +0100 Subject: [PATCH 118/991] FIX: don't error out when updating a topic with no changes --- app/controllers/topics_controller.rb | 4 ++-- spec/controllers/topics_controller_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 8e8d226d37..9e976f2124 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -123,8 +123,8 @@ class TopicsController < ApplicationController guardian.ensure_can_edit!(topic) changes = {} - changes[:title] = params[:title] if params[:title] - changes[:category_id] = params[:category_id] if params[:category_id] + changes[:title] = params[:title] if params[:title] && topic.title != params[:title] + changes[:category_id] = params[:category_id] if params[:category_id] && topic.category_id != params[:category_id].to_i success = true diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb index d8500e82d0..c8a05095de 100644 --- a/spec/controllers/topics_controller_spec.rb +++ b/spec/controllers/topics_controller_spec.rb @@ -783,6 +783,12 @@ describe TopicsController do expect(response).not_to be_success end + it "doesn't call the PostRevisor when there is no changes" do + PostRevisor.any_instance.expects(:revise!).never + xhr :put, :update, topic_id: @topic.id, slug: @topic.title, title: @topic.title, category_id: @topic.category_id + expect(response).to be_success + end + context "allow_uncategorized_topics is false" do before do SiteSetting.stubs(:allow_uncategorized_topics).returns(false) From e3bcd848b34fa5ee176f863f62f239c22c714969 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Mon, 1 Dec 2014 17:24:44 -0800 Subject: [PATCH 119/991] minor copyedit --- 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 083c4704c1..3a9a8f9fab 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -391,7 +391,7 @@ en: ok: "We will email you to confirm" invalid: "Please enter a valid email address" authenticated: "Your email has been authenticated by {{provider}}" - frequency: "We'll only email you if we haven't seen you recently and you haven't already seen the thing we're emailing you about." + frequency: "We'll only email you if we haven't seen you recently and you haven't read the thing we're emailing you about." name: title: "Name" From 0503599de707bc3d84c044a22351efb9779036b7 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 2 Dec 2014 00:19:42 -0800 Subject: [PATCH 120/991] suppress long category names on mobile --- app/assets/stylesheets/mobile/topic-list.scss | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/mobile/topic-list.scss b/app/assets/stylesheets/mobile/topic-list.scss index 726865574a..6d61c1f956 100644 --- a/app/assets/stylesheets/mobile/topic-list.scss +++ b/app/assets/stylesheets/mobile/topic-list.scss @@ -58,12 +58,14 @@ .topic-item-stats { margin-top: 8px; - } - - .topic-item-stats { .category, .num, .last-poster { float: left; } + .category a { + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + } .num .fa { color: scale-color($primary, $lightness: 50%); } @@ -261,9 +263,7 @@ tr.category-topic-link:nth-of-type(odd) { } ol.category-breadcrumb { - margin: 5px 10px 0 0; - } .category-dropdown-menu { @@ -274,10 +274,6 @@ ol.category-breadcrumb { line-height: 26px !important; margin-bottom: 0 !important; } - - div { - margin-bottom: 10px; - } } .top-lists { From 3fcde726567944f659a5e0c9b40d74fa2fb546f0 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 2 Dec 2014 00:41:51 -0800 Subject: [PATCH 121/991] bit shorter max width on mobile categories --- app/assets/stylesheets/mobile/topic-list.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/mobile/topic-list.scss b/app/assets/stylesheets/mobile/topic-list.scss index 6d61c1f956..c1ec6e5bc8 100644 --- a/app/assets/stylesheets/mobile/topic-list.scss +++ b/app/assets/stylesheets/mobile/topic-list.scss @@ -62,7 +62,7 @@ float: left; } .category a { - max-width: 200px; + max-width: 160px; overflow: hidden; text-overflow: ellipsis; } From 2ead3fca69e15f14d1102f0628b729c53dd59689 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 2 Dec 2014 16:05:43 +0530 Subject: [PATCH 122/991] FIX: exportUsers action was deleted, so user export was failing --- .../admin/routes/admin-users-list.js.es6 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 app/assets/javascripts/admin/routes/admin-users-list.js.es6 diff --git a/app/assets/javascripts/admin/routes/admin-users-list.js.es6 b/app/assets/javascripts/admin/routes/admin-users-list.js.es6 new file mode 100644 index 0000000000..3a9c406b2c --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-users-list.js.es6 @@ -0,0 +1,15 @@ +export default Discourse.Route.extend({ + + actions: { + exportUsers: function() { + Discourse.ExportCsv.exportUserList().then(function(result) { + if (result.success) { + bootbox.alert(I18n.t("admin.export_csv.success")); + } else { + bootbox.alert(I18n.t("admin.export_csv.failed")); + } + }); + } + } + +}); From 121048662368ea9b0813cd892b655a2481222864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 2 Dec 2014 16:04:45 +0100 Subject: [PATCH 123/991] FIX: editing the 1st post of a private message wasn't working --- .../javascripts/discourse/models/composer.js | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/discourse/models/composer.js b/app/assets/javascripts/discourse/models/composer.js index 164e3c604b..ad6d1c3a20 100644 --- a/app/assets/javascripts/discourse/models/composer.js +++ b/app/assets/javascripts/discourse/models/composer.js @@ -100,33 +100,29 @@ Discourse.Composer = Discourse.Model.extend({ hidePreview: Em.computed.not('showPreview'), - // Whether to disable the post button + // whether to disable the post button cantSubmitPost: function() { - - // Can't submit while loading + // can't submit while loading if (this.get('loading')) return true; - // Title is required when: - // - creating a new topic - // - editing the 1st post - // - creating a private message - + // title is required when + // - creating a new topic/private message + // - editing the 1st post if (this.get('canEditTitle') && !this.get('titleLengthValid')) return true; - // Need at least one user when sending a private message - if ( this.get('creatingPrivateMessage') && - this.get('targetUsernames') && - (this.get('targetUsernames').trim() + ',').indexOf(',') === 0) { - return true; - } - // reply is always required if (this.get('missingReplyCharacters') > 0) return true; - return this.get('canCategorize') && - !Discourse.SiteSettings.allow_uncategorized_topics && - !this.get('categoryId') && - !Discourse.User.currentProp('staff'); + if (this.get("privateMessage")) { + // need at least one user when sending a PM + return this.get('targetUsernames') && (this.get('targetUsernames').trim() + ',').indexOf(',') === 0; + } else { + // has a category? (when needed) + return this.get('canCategorize') && + !Discourse.SiteSettings.allow_uncategorized_topics && + !this.get('categoryId') && + !Discourse.User.currentProp('staff'); + } }.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryId', 'missingReplyCharacters'), /** From 87667cfe17d7d31660241793cc009fd30994c415 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 2 Dec 2014 11:46:21 -0500 Subject: [PATCH 124/991] FIX: Editing a category definition topic was clearing its permissions --- app/assets/javascripts/discourse/models/site.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/models/site.js b/app/assets/javascripts/discourse/models/site.js index c66481c991..c8e09a9dce 100644 --- a/app/assets/javascripts/discourse/models/site.js +++ b/app/assets/javascripts/discourse/models/site.js @@ -66,7 +66,12 @@ Discourse.Site = Discourse.Model.extend({ updateCategory: function(newCategory) { var existingCategory = this.get('categories').findProperty('id', Em.get(newCategory, 'id')); - if (existingCategory) existingCategory.setProperties(newCategory); + if (existingCategory) { + // Don't update null permissions + if (newCategory.permission === null) { delete newCategory.permission; } + + existingCategory.setProperties(newCategory); + } } }); From 99928cac26120ddfc740845bd1deb156551f0c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 2 Dec 2014 18:15:32 +0100 Subject: [PATCH 125/991] FEATURE: use actual time in preference > email notifications' instruction --- .../javascripts/discourse/templates/user/preferences.hbs | 2 +- config/locales/client.en.yml | 5 ++++- config/site_settings.yml | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user/preferences.hbs b/app/assets/javascripts/discourse/templates/user/preferences.hbs index f5719294ba..7e7e099314 100644 --- a/app/assets/javascripts/discourse/templates/user/preferences.hbs +++ b/app/assets/javascripts/discourse/templates/user/preferences.hbs @@ -181,7 +181,7 @@ {{preference-checkbox labelKey="user.email_always" checked=email_always}}
    - {{i18n user.email.frequency}} + {{i18n user.email.frequency count=siteSettings.email_time_window_mins}}
    diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 3a9a8f9fab..c8b8d963b5 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -391,7 +391,10 @@ en: ok: "We will email you to confirm" invalid: "Please enter a valid email address" authenticated: "Your email has been authenticated by {{provider}}" - frequency: "We'll only email you if we haven't seen you recently and you haven't read the thing we're emailing you about." + frequency: + zero: "We'll email you immediately if you haven't read the thing we're emailing you about." + one: "We'll only email you if we haven't seen you in the last minute and you haven't read the thing we're emailing you about." + other: "We'll only email you if we haven't seen you in the last {{count}} minutes and you haven't read the thing we're emailing you about." name: title: "Name" diff --git a/config/site_settings.yml b/config/site_settings.yml index 7bde8b1e45..2b6eb17b47 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -391,7 +391,9 @@ posting: type: list email: - email_time_window_mins: 10 + email_time_window_mins: + default: 10 + client: true email_posts_context: 5 digest_min_excerpt_length: 100 digest_topics: 20 From 461196f0893f703adf42a6045b7fbc750fe477e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 2 Dec 2014 18:52:56 +0100 Subject: [PATCH 126/991] FEATURE: 'delete user' button in the user card in the admin section --- .../discourse/controllers/user-card.js.es6 | 17 +++++++++-------- .../discourse/templates/user-card.hbs | 16 ++++++++++------ app/serializers/user_serializer.rb | 7 ++++++- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index 90cfd46013..1557cc195b 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -13,23 +13,20 @@ export default ObjectController.extend({ postStream: Em.computed.alias('controllers.topic.postStream'), enoughPostsForFiltering: Em.computed.gte('participant.post_count', 2), viewingTopic: Em.computed.match('controllers.application.currentPath', /^topic\./), + viewingAdmin: Em.computed.match('controllers.application.currentPath', /^admin\./), showFilter: Em.computed.and('viewingTopic', 'postStream.hasNoFilters', 'enoughPostsForFiltering'), - - // showFilter: Em.computed.and('postStream.hasNoFilters', 'enoughPostsForFiltering'), showName: Discourse.computed.propertyNotEqual('user.name', 'user.username'), - hasUserFilters: Em.computed.gt('postStream.userFilters.length', 0), - isSuspended: Em.computed.notEmpty('user.suspend_reason'), - showBadges: Discourse.computed.setting('enable_badges'), + showMoreBadges: Em.computed.gt('moreBadgesCount', 0), + canDelete: Em.computed.not("user.deleteForbidden"), + showDelete: Em.computed.and("viewingAdmin", "showName", "canDelete"), moreBadgesCount: function() { return this.get('user.badge_count') - this.get('user.featured_user_badges.length'); }.property('user.badge_count', 'user.featured_user_badges.@each'), - showMoreBadges: Em.computed.gt('moreBadgesCount', 0), - hasCardBadgeImage: function() { var img = this.get('user.card_badge.image'); return img && img.indexOf('fa-') !== 0; @@ -77,7 +74,7 @@ export default ObjectController.extend({ self.set('cardTarget', target); Discourse.User.findByUsername(username).then(function (user) { - self.setProperties({ user: user, avatar: user, visible: true}); + self.setProperties({ user: Discourse.AdminUser.create(user), avatar: user, visible: true}); self.appEvents.trigger('usercard:shown'); }).finally(function(){ self.set('userLoading', null); @@ -101,6 +98,10 @@ export default ObjectController.extend({ postStream.cancelFilter(); postStream.refresh(); this.close(); + }, + + deleteUser: function(user) { + user.destroy({ deletePosts: true }); } } diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index b5dc6f39c5..c4326652fa 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -27,11 +27,15 @@ {{/if}} {{#if showFilter}} -
  • {{fa-icon "filter"}}{{i18n topic.filter_to username="username" post_count="participant.post_count"}}
  • +
  • {{fa-icon "filter"}}{{i18n topic.filter_to username="username" post_count="participant.post_count"}}
  • {{/if}} {{#if hasUserFilters}} -
  • {{fa-icon "times"}}{{i18n topic.filters.cancel}}
  • +
  • {{fa-icon "times"}}{{i18n topic.filters.cancel}}
  • + {{/if}} + + {{#if showDelete}} +
  • {{fa-icon "exclamation-triangle"}}{{i18n admin.user.delete}}
  • {{/if}} @@ -52,10 +56,10 @@ {{/if}} {{#if user}} - + {{/if}} {{#if showBadges}} diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 1313db0b76..0e1badaba9 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -66,7 +66,8 @@ class UserSerializer < BasicUserSerializer has_many :featured_user_badges, embed: :ids, serializer: UserBadgeSerializer, root: :user_badges has_one :card_badge, embed: :object, serializer: BadgeSerializer - staff_attributes :number_of_deleted_posts, + staff_attributes :post_count, + :number_of_deleted_posts, :number_of_flagged_posts, :number_of_flags_given, :number_of_suspensions, @@ -207,6 +208,10 @@ class UserSerializer < BasicUserSerializer ### STAFF ATTRIBUTES ### + def post_count + object.user_stat.try(:post_count) + end + def number_of_deleted_posts Post.with_deleted .where(user_id: object.id) From 67c4c90159428d700766ac610ca374d764cea6a7 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 2 Dec 2014 15:24:05 -0500 Subject: [PATCH 127/991] FIX: Clicking avatars broke for non-admins --- .../javascripts/discourse/controllers/user-card.js.es6 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index 1557cc195b..2ea6b91e46 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -74,7 +74,14 @@ export default ObjectController.extend({ self.set('cardTarget', target); Discourse.User.findByUsername(username).then(function (user) { - self.setProperties({ user: Discourse.AdminUser.create(user), avatar: user, visible: true}); + + // A bit hacky. If viewing admin, wrap it in Discourse.AdminUser + // TODO: Restructure this to be cleaner + var wrapped = user; + if (self.get('viewingAdmin')) { + wrapped = Discourse.AdminUser.create(user); + } + self.setProperties({ user: wrapped, avatar: user, visible: true}); self.appEvents.trigger('usercard:shown'); }).finally(function(){ self.set('userLoading', null); From 008337b018a9c7ca4b9c52a251c2a680e42469dc Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 2 Dec 2014 15:30:57 -0500 Subject: [PATCH 128/991] FIX: Selecting one user in admin would select them all --- app/assets/javascripts/admin/templates/users-list-show.hbs | 4 ++-- app/assets/stylesheets/common/admin/admin_base.scss | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/admin/templates/users-list-show.hbs b/app/assets/javascripts/admin/templates/users-list-show.hbs index 333e3f664a..69e02575bd 100644 --- a/app/assets/javascripts/admin/templates/users-list-show.hbs +++ b/app/assets/javascripts/admin/templates/users-list-show.hbs @@ -42,11 +42,11 @@ {{#each user in model}} - + {{#if controller.showApproval}} {{#if user.can_approve}} - {{input type="checkbox" checked=selected}} + {{input type="checkbox" checked=user.selected}} {{/if}} {{/if}} diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 8ccc201cce..11650c5f98 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -10,7 +10,7 @@ th {border-top: 1px solid scale-color-diff();} td {border-bottom: 1px solid scale-color-diff(); border-top: 1px solid scale-color-diff();} tr:hover { background-color: darken($secondary, 2.5%); } - tr.selected { background-color: lighten($primary, 50%); } + tr.selected { background-color: lighten($primary, 80%); } .filters input { margin-bottom: 0; } } From c90668ae532971cd9a904900c7be970719c05099 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 2 Dec 2014 17:03:12 -0500 Subject: [PATCH 129/991] FIX: Better protection for invites scrolling --- .../javascripts/discourse/controllers/user-invited.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/controllers/user-invited.js.es6 b/app/assets/javascripts/discourse/controllers/user-invited.js.es6 index 4d5fea2f4e..7499b2e5a4 100644 --- a/app/assets/javascripts/discourse/controllers/user-invited.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-invited.js.es6 @@ -80,7 +80,7 @@ export default Ember.ObjectController.extend({ var self = this; var model = self.get('model'); - if(self.get('canLoadMore')) { + if (self.get('canLoadMore') && !self.get('invitesLoading')) { self.set('invitesLoading', true); Discourse.Invite.findInvitedBy(self.get('user'), self.get('searchTerm'), model.invites.length).then(function(invite_model) { self.set('invitesLoading', false); From ea37946d00f09de4d8f03e7893e329ceddb9991b Mon Sep 17 00:00:00 2001 From: Kris Aubuchon Date: Tue, 2 Dec 2014 17:10:14 -0500 Subject: [PATCH 130/991] a few very minor visual tweaks --- app/assets/stylesheets/common/base/_topic-list.scss | 2 +- app/assets/stylesheets/common/base/header.scss | 1 + app/assets/stylesheets/common/base/topic-post.scss | 3 +-- app/assets/stylesheets/mobile/discourse.scss | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index 29ee46e008..de9501cdf8 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -1,6 +1,6 @@ .show-more { position: absolute; - top: 5px; + top: 4px; width: 100%; } diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index 45b6311b3c..38b5ada1d4 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -266,6 +266,7 @@ .search-context { padding: 0 5px; + label { margin-bottom: 0; } } .searching { diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss index 0626826bf3..d524b5e045 100644 --- a/app/assets/stylesheets/common/base/topic-post.scss +++ b/app/assets/stylesheets/common/base/topic-post.scss @@ -118,7 +118,7 @@ aside.quote { } .wiki .topic-body { - background-color: scale-color($wiki, $lightness: 95%); + background-color: dark-light-diff($wiki, $secondary, 95%, -50%); } .post-info { @@ -140,7 +140,6 @@ aside.quote { } } - pre { code { white-space: pre-wrap; diff --git a/app/assets/stylesheets/mobile/discourse.scss b/app/assets/stylesheets/mobile/discourse.scss index af61f9c35e..7cf2887525 100644 --- a/app/assets/stylesheets/mobile/discourse.scss +++ b/app/assets/stylesheets/mobile/discourse.scss @@ -91,7 +91,7 @@ blockquote { .topic-status { i { font-size: 20px; - color: darken($secondary, 60%); + color: dark-light-diff($secondary, $primary, 40%, -20%); } } } From 44c1a895b5b55c0e687c9f0cfb254da540057362 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 2 Dec 2014 21:25:43 -0800 Subject: [PATCH 131/991] move two user related "other" settings to "user" --- config/site_settings.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config/site_settings.yml b/config/site_settings.yml index 2b6eb17b47..c076ba4254 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -285,6 +285,10 @@ users: default: false email_token_valid_hours: 24 email_token_grace_period_hours: 0 + purge_inactive_users_grace_period_days: 7 + public_user_custom_fields: + type: list + default: '' posting: min_post_length: @@ -792,10 +796,6 @@ uncategorized: notify_about_flags_after: 48 - public_user_custom_fields: - type: list - default: '' - enable_cdn_js_debugging: false show_create_topics_notice: @@ -807,7 +807,6 @@ uncategorized: default: true disable_edit_notifications: false - purge_inactive_users_grace_period_days: 7 company_full_name: 'My Unconfigured Forum Ltd.' company_short_name: 'Unconfigured Forum' From ea269ccfb215ecba7f11c28bbb70f76e6aa5db01 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 2 Dec 2014 21:36:25 -0800 Subject: [PATCH 132/991] rename purge_inactive to purge_unactivated --- app/jobs/scheduled/purge_inactive.rb | 2 +- app/models/user.rb | 10 +++------- config/locales/server.en.yml | 2 +- config/locales/server.es.yml | 2 +- config/locales/server.fi.yml | 2 +- config/locales/server.fr.yml | 2 +- config/locales/server.he.yml | 2 +- config/locales/server.ko.yml | 2 +- config/locales/server.pt_BR.yml | 2 +- config/locales/server.sq.yml | 2 +- config/locales/server.zh_CN.yml | 2 +- config/site_settings.yml | 2 +- spec/models/user_spec.rb | 6 +++--- 13 files changed, 17 insertions(+), 21 deletions(-) diff --git a/app/jobs/scheduled/purge_inactive.rb b/app/jobs/scheduled/purge_inactive.rb index ee5e30a43b..a6615f5fe2 100644 --- a/app/jobs/scheduled/purge_inactive.rb +++ b/app/jobs/scheduled/purge_inactive.rb @@ -3,7 +3,7 @@ module Jobs every 1.day def execute(args) - User.purge_inactive + User.purge_unactivated end end end diff --git a/app/models/user.rb b/app/models/user.rb index 8a846ee2d9..7fc88a0b6f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -791,16 +791,12 @@ class User < ActiveRecord::Base end end - # Delete inactive accounts that are over a week old - def self.purge_inactive + # Delete unactivated accounts (without verified email) that are over a week old + def self.purge_unactivated - # You might be wondering why this query matches on post_count = 0. The reason - # is a long time ago we had a bug where users could post before being activated - # and some sites still have those records which can't be purged. to_destroy = User.where(active: false) .joins('INNER JOIN user_stats AS us ON us.user_id = users.id') - .where("created_at < ?", SiteSetting.purge_inactive_users_grace_period_days.days.ago) - .where('us.post_count = 0') + .where("created_at < ?", SiteSetting.purge_unactivated_users_grace_period_days.days.ago) .where('NOT admin AND NOT moderator') .limit(100) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 233d0a0739..be11d2e8f2 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -845,7 +845,7 @@ en: clean_up_uploads: "Remove orphan unreferenced uploads to prevent illegal hosting. WARNING: you may want to back up of your /uploads directory before enabling this setting." clean_orphan_uploads_grace_period_hours: "Grace period (in hours) before an orphan upload is removed." purge_deleted_uploads_grace_period_days: "Grace period (in days) before a deleted upload is erased." - purge_inactive_users_grace_period_days: "Grace period (in days) before a user who has not activated their account is deleted." + purge_unactivated_users_grace_period_days: "Grace period (in days) before a user who has not activated their account is deleted." enable_s3_uploads: "Place uploads on Amazon S3 storage. IMPORTANT: requires valid S3 credentials (both access key id & secret access key)." s3_use_iam_profile: 'Use AWS EC2 IAM role to retrieve keys. NOTE: enabling will override "s3 access key id" and "s3 secret access key" settings.' s3_upload_bucket: "The Amazon S3 bucket name that files will be uploaded into. WARNING: must be lowercase, no periods, no underscores." diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index 44121790b5..d2ff1c237f 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -749,7 +749,7 @@ es: 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" clean_orphan_uploads_grace_period_hours: "Período de gracia (en horas) antes de que una subida huérfana sea eliminada." purge_deleted_uploads_grace_period_days: "Período de gracia (en días) antes de que una subida eliminada sea borrada totalmente." - purge_inactive_users_grace_period_days: "Periodo de gracia (en días) durante los cuales un usuario que no haya activado su cuenta no será aún eliminado." + purge_unactivated_users_grace_period_days: "Periodo de gracia (en días) durante los cuales un usuario que no haya activado su cuenta no será aún eliminado." enable_s3_uploads: "Coloca los archivos subidos en el almacén Amazon S3. IMPORTANTE: requiere de credenciales de S3 validas. (ambas clave de acceso y clave de acceso secreta)." s3_use_iam_profile: 'Usar el rol de AWS EC2 IAM para descargar las llaves. NOTA: habilitando esta opción ignorará la "llave de acceso s3" y "la llave de acceso secreta s3".' s3_upload_bucket: "El nombre del bucket Amazon S3 donde se subirán los archivos. AVISO: debe ser en minúsculas, sin puntos ni guiones bajos." diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index d603543278..7192c3c767 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -733,7 +733,7 @@ fi: clean_up_uploads: "Poista orpoutuneet liitetiedostot, joita ei käytetä viesteissä, laittoman hostauksen estämiseksi. VAROITUS: kannattaa varmuuskopioida /uploads kansio ennen tämän asetuksen ottamista käyttöön." clean_orphan_uploads_grace_period_hours: "Varoaika (tunteina) kunnes orpoutuneet liitetiedostot poistetaan" purge_deleted_uploads_grace_period_days: "Varoaika (päivinä) kunnes poistettu liitetiedosto tuhotaan." - purge_inactive_users_grace_period_days: "Varoaika (päivinä) kunnes aktivoimaton käyttäjätili tuhotaan." + purge_unactivated_users_grace_period_days: "Varoaika (päivinä) kunnes aktivoimaton käyttäjätili tuhotaan." enable_s3_uploads: "Lataa liitetiedostot Amazon S3:een. Tärkeää: edellyttää toimivat S3 kirjautumistiedot (access key id ja secret access key)." s3_use_iam_profile: 'Käytä AWS EC2 IAM roolia avainten hakuun. HUOM: käyttöönotto korvaa "s3 access key id" ja "s3 secret access key" asetukset.' s3_access_key_id: "Amazon S3 access key id, jota käytetään kuvien sijoittamisessa." diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index 25928ab069..96256d502d 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -744,7 +744,7 @@ fr: clean_up_uploads: "Retirer les fichiers téléchargés orphelins pour prévenir les hébergements illégaux. ATTENTION: vous devriez faire une sauvegarde de votre répertoire /uploads avant d'activer ce paramètre." clean_orphan_uploads_grace_period_hours: "La période de grâce (en heures) avant qu'un fichier envoyé orphelin soit retiré." purge_deleted_uploads_grace_period_days: "La période de grâce (en jours) avant qu'un fichier envoyé et supprimé soit effacé." - purge_inactive_users_grace_period_days: "Période de grâce (en jours) avant qu'un utilisateur qui n'a pas activé son compte soit supprimé." + purge_unactivated_users_grace_period_days: "Période de grâce (en jours) avant qu'un utilisateur qui n'a pas activé son compte soit supprimé." enable_s3_uploads: "Placez les fichiers envoyés sur un stockage Amazon S3. IMPORTANT: nécessite un accès valide à S3 (l'identifiant et la clé secrète)." s3_use_iam_profile: 'Utiliser un role AWS EC2 IAM pour la récupération des clés. NOTE: si activé, surcharge les paramètres "s3 access key id" et "s3 secret access key".' s3_upload_bucket: "Le nom du bucket Amazon S3 qui contiendra les fichiers téléchargés. ATTENTION : doit être en minuscule, sans points et sans caractères de soulignement." diff --git a/config/locales/server.he.yml b/config/locales/server.he.yml index bd08cd1378..0c870d5586 100644 --- a/config/locales/server.he.yml +++ b/config/locales/server.he.yml @@ -726,7 +726,7 @@ he: clean_up_uploads: "הסירו העלאות יתומות וללא הפניה כדי למנוע אירוח בלתי חוקי של חומר. אזהרה: אתם עלולי לרצות לגבות את תיקיית ה-/uploads שלכם לפנו שתאפשרו הגדרה זו." clean_orphan_uploads_grace_period_hours: "Grace period (in hours) before an orphan upload is removed." purge_deleted_uploads_grace_period_days: "Grace period (in days) before a deleted upload is erased." - purge_inactive_users_grace_period_days: "תקופת המתנה (בימים) לפני שמשתמש שלא הפעיל את החשבון שלו יימחק." + purge_unactivated_users_grace_period_days: "תקופת המתנה (בימים) לפני שמשתמש שלא הפעיל את החשבון שלו יימחק." enable_s3_uploads: "אחסן העלאות (uploads) על תשתית של Amazon S3. חשוב: מצריך מפתח גישה + מפתח גישה סודי שיהיו חוקיים." s3_use_iam_profile: 'השתמש ב-AWS EC2 IAM role על מנת לאחזר מפתחות. שימו לב: איפשור של זה ידרוס את ההגדרות "S3 access key id" וכן את "s3 secret access key".' s3_access_key_id: "מפתח הגישה (access key id) של Amazon S3 שישמש להעלאת התמונות." diff --git a/config/locales/server.ko.yml b/config/locales/server.ko.yml index 01a1c6b9c6..56edfb3e29 100644 --- a/config/locales/server.ko.yml +++ b/config/locales/server.ko.yml @@ -691,7 +691,7 @@ ko: clean_up_uploads: "불법 호스팅을 막기 위해서 참조되지 않은 업로드 파일은 제거한다. 주의 : 이 설정을 활성화 하기 전에 `/uploads` 디렉토리를 백업하는 것이 좋다." clean_orphan_uploads_grace_period_hours: "참조되지 않은 업로드 파일을 제거하기 전 기간(시간)" purge_deleted_uploads_grace_period_days: "참조되지 않은 업로드 파일을 완전 삭제하지 전 기간(일)" - purge_inactive_users_grace_period_days: "활성화 되지 않은 사용자를 삭제하기 까지의 기간(일)" + purge_unactivated_users_grace_period_days: "활성화 되지 않은 사용자를 삭제하기 까지의 기간(일)" s3_access_key_id: "이미지를 업로드 할 때 사용할 Amazon S3의 access key id" s3_secret_access_key: "이미지를 업로드 할 때 사용할 Amazon S3의 secret access key" s3_region: "이미지를 업로드 할 때 사용할 Amazon S3 region" diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index f7d4538dc2..49e29d31d6 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -661,7 +661,7 @@ pt_BR: clean_up_uploads: "Remover envios sem referência para evitar hospedagem ilegal. AVISO: você pode querer fazer um backup do seu diretório de /uploads antes de habilitar essa configuração." clean_orphan_uploads_grace_period_hours: "Carência (em horas) antes de um upload órfão ser removido." purge_deleted_uploads_grace_period_days: "Carência (em dias) antes que um upload excluído seja apagado." - purge_inactive_users_grace_period_days: "Carência (em dias) antes de um usuário que não tenha ativado sua conta seja deletado." + purge_unactivated_users_grace_period_days: "Carência (em dias) antes de um usuário que não tenha ativado sua conta seja deletado." 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." title_fancy_entities: "Converter caracteres ASCII comuns em entidades HTML nos títulos dos tópicos, ala SmartyPants http://daringfireball.net/projects/smartypants/" diff --git a/config/locales/server.sq.yml b/config/locales/server.sq.yml index 9418e3cccd..d1e69b1e1d 100644 --- a/config/locales/server.sq.yml +++ b/config/locales/server.sq.yml @@ -749,7 +749,7 @@ sq: clean_up_uploads: "Remove orphan unreferenced uploads to prevent illegal hosting. WARNING: you may want to back up of your /uploads directory before enabling this setting." clean_orphan_uploads_grace_period_hours: "Grace period (in hours) before an orphan upload is removed." purge_deleted_uploads_grace_period_days: "Grace period (in days) before a deleted upload is erased." - purge_inactive_users_grace_period_days: "Grace period (in days) before a user who has not activated their account is deleted." + purge_unactivated_users_grace_period_days: "Grace period (in days) before a user who has not activated their account is deleted." enable_s3_uploads: "Place uploads on Amazon S3 storage. IMPORTANT: requires valid S3 credentials (both access key id & secret access key)." s3_use_iam_profile: 'Use AWS EC2 IAM role to retrieve keys. NOTE: enabling will override "s3 access key id" and "s3 secret access key" settings.' s3_upload_bucket: "The Amazon S3 bucket name that files will be uploaded into. WARNING: must be lowercase, no periods, no underscores." diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 237fae728d..6a04252c2f 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -719,7 +719,7 @@ zh_CN: clean_up_uploads: "移除孤立的已上传资料。警告:您可能想要在启用这个设定前备份一下 /uploads 目录。" clean_orphan_uploads_grace_period_hours: "删除孤立上传资料的宽限期(单位:小时)" purge_deleted_uploads_grace_period_days: "移除已删除的孤立上传资料的宽限期(单位:小时)" - purge_inactive_users_grace_period_days: "删除不活跃用户账户的宽限期(单位:天)。" + purge_unactivated_users_grace_period_days: "删除不活跃用户账户的宽限期(单位:天)。" enable_s3_uploads: "上传至 Amazon S3 存储的地址。重要:需要有效的 S3 验证资料(包括 access key id & secret access key)。" s3_use_iam_profile: '使用 AWS EC2 IAM 角色来获得 key。注意:启用这个会覆盖“S3 access key id”和“S3 secret access key” 设置。' s3_upload_bucket: "上传文件保存于 Amazon S3 的 bucket 名字。警告:必须为小写,无句点,无下划线。" diff --git a/config/site_settings.yml b/config/site_settings.yml index c076ba4254..97bf9a60af 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -285,7 +285,7 @@ users: default: false email_token_valid_hours: 24 email_token_grace_period_hours: 0 - purge_inactive_users_grace_period_days: 7 + purge_unactivated_users_grace_period_days: 7 public_user_custom_fields: type: list default: '' diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d2d1d79afe..ce009f1cb5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1155,13 +1155,13 @@ describe User do end end - describe "#purge_inactive" do + describe "#purge_unactivated" do let!(:user) { Fabricate(:user) } let!(:inactive) { Fabricate(:user, active: false) } let!(:inactive_old) { Fabricate(:user, active: false, created_at: 1.month.ago) } - it 'should only remove old, inactive users' do - User.purge_inactive + it 'should only remove old, unactivated users' do + User.purge_unactivated all_users = User.all all_users.include?(user).should == true all_users.include?(inactive).should == true From a05408ac9d34c96c530e65bf17d69d52e3b56324 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 2 Dec 2014 21:39:54 -0800 Subject: [PATCH 133/991] minor copyedit --- 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 be11d2e8f2..b98c507f12 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -47,7 +47,7 @@ en: via: "%{username} via %{site_name}" is_reserved: "is reserved" - purge_reason: "Automatically deleted due to being old and unverified" + purge_reason: "Automatically deleted as abandoned, unactivated account" disable_remote_images_download_reason: "Remote images download was disabled because there wasn't enough disk space available." errors: From f84bdfdde3be6d9d9a87cb3a0f3818d2fc3af84f Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Wed, 3 Dec 2014 13:12:05 +0530 Subject: [PATCH 134/991] FIX: if full user name is not provided, username should be present in email From header --- app/mailers/user_notifications.rb | 4 ++-- spec/mailers/user_notifications_spec.rb | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index 9192ea6e7c..218247690d 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -127,7 +127,7 @@ class UserNotifications < ActionMailer::Base title: post.topic.title, post: post, username: post.user.username, - from_alias: SiteSetting.enable_email_names ? post.user.name : post.user.username, + from_alias: (SiteSetting.enable_email_names && !post.user.name.empty?) ? post.user.name : post.user.username, allow_reply_by_email: true, use_site_subject: true, add_re_to_subject: true, @@ -173,7 +173,7 @@ class UserNotifications < ActionMailer::Base user_name = @notification.data_hash[:original_username] if @post && SiteSetting.enable_email_names - user_name = User.find_by(id: @post.user_id).name + user_name = User.find_by(id: @post.user_id).name if !User.find_by(id: @post.user_id).name.empty? end notification_type = opts[:notification_type] || Notification.types[@notification.notification_type].to_s diff --git a/spec/mailers/user_notifications_spec.rb b/spec/mailers/user_notifications_spec.rb index a8ddc4246d..fa03d5ac71 100644 --- a/spec/mailers/user_notifications_spec.rb +++ b/spec/mailers/user_notifications_spec.rb @@ -131,11 +131,11 @@ describe UserNotifications do end describe '.user_posted' do - let(:response_by_user) { Fabricate(:user, name: "John Doe") } + let(:response_by_user) { Fabricate(:user, name: "John Doe", username: "john") } let(:post) { Fabricate(:post) } let(:response) { Fabricate(:post, topic: post.topic, user: response_by_user)} let(:user) { Fabricate(:user) } - let(:notification) { Fabricate(:notification, user: user) } + let(:notification) { Fabricate(:notification, user: user, data: {original_username: response_by_user.username}.to_json) } it 'generates a correct email' do SiteSetting.stubs(:enable_email_names).returns(false) @@ -144,6 +144,9 @@ describe UserNotifications do # from should not include full user name if "show user full names" is disabled expect(mail[:from].display_names).to_not eql(['John Doe']) + # from should include username if "show user full names" is disabled + expect(mail[:from].display_names).to eql(['john']) + # subject should not include category name expect(mail.subject).not_to match(/Uncategorized/) @@ -160,18 +163,18 @@ describe UserNotifications do end describe '.user_private_message' do - let(:response_by_user) { Fabricate(:user, name: "John Doe") } + let(:response_by_user) { Fabricate(:user, name: "", username: "john") } let(:topic) { Fabricate(:private_message_topic) } let(:response) { Fabricate(:post, topic: topic, user: response_by_user)} let(:user) { Fabricate(:user) } - let(:notification) { Fabricate(:notification, user: user) } + let(:notification) { Fabricate(:notification, user: user, data: {original_username: response_by_user.username}.to_json) } it 'generates a correct email' do SiteSetting.stubs(:enable_email_names).returns(true) mail = UserNotifications.user_private_message(response.user, post: response, notification: notification) - # from should include full user name - expect(mail[:from].display_names).to eql(['John Doe']) + # from should include username if full user name is not provided + expect(mail[:from].display_names).to eql(['john']) # subject should include "[PM]" expect(mail.subject).to match("[PM]") From 7edb88a5a8f1d0d98537f1e7ecb36a04f8fd1199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 3 Dec 2014 11:51:49 +0100 Subject: [PATCH 135/991] FIX: change top score formula Adds more weight to `likes` (it's now the same weight as `post_count`) --- app/models/top_topic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/top_topic.rb b/app/models/top_topic.rb index cad2eebc97..9905a7b327 100644 --- a/app/models/top_topic.rb +++ b/app/models/top_topic.rb @@ -119,7 +119,7 @@ class TopTopic < ActiveRecord::Base WITH top AS ( SELECT CASE WHEN topics.created_at < :from THEN 0 - ELSE log(greatest(#{period}_views_count, 1)) + #{period}_likes_count + #{period}_posts_count * 2 + ELSE log(greatest(#{period}_views_count, 1)) + #{period}_likes_count * 2 + #{period}_posts_count * 2 END AS score, topic_id FROM top_topics From acc62f2ec2c10dc5a6295ceb19123b900774b81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 3 Dec 2014 12:47:28 +0100 Subject: [PATCH 136/991] SECURITY: prevent direct download of backups --- config/nginx.sample.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/nginx.sample.conf b/config/nginx.sample.conf index 39a02ef632..434a3ec88b 100644 --- a/config/nginx.sample.conf +++ b/config/nginx.sample.conf @@ -58,6 +58,11 @@ server { # further more etags are based on the file in nginx not sha of data # use dates, it solves the problem fine even cross server etag off; + + # prevent direct download of backups + location ^~ /backups/ { + internal; + } location / { root $public; From f9f54e2626ebe550908b694c46f0914fa6789853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 3 Dec 2014 13:00:02 +0100 Subject: [PATCH 137/991] refactor fix in 67c4c90159428d700766ac610ca374d764cea6a7 --- app/assets/javascripts/admin/models/admin_user.js | 4 +--- .../discourse/controllers/user-card.js.es6 | 13 +++---------- app/assets/javascripts/discourse/models/user.js | 3 +++ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/admin/models/admin_user.js b/app/assets/javascripts/admin/models/admin_user.js index c89b540973..816690031b 100644 --- a/app/assets/javascripts/admin/models/admin_user.js +++ b/app/assets/javascripts/admin/models/admin_user.js @@ -298,9 +298,7 @@ Discourse.AdminUser = Discourse.User.extend({ }); }, - deleteForbidden: function() { - return (!this.get('can_be_deleted') || this.get('post_count') > 0); - }.property('post_count'), + deleteForbidden: Em.computed.not("canBeDeleted"), deleteExplanation: function() { if (this.get('deleteForbidden')) { diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index 2ea6b91e46..369bf7c81a 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -20,8 +20,7 @@ export default ObjectController.extend({ isSuspended: Em.computed.notEmpty('user.suspend_reason'), showBadges: Discourse.computed.setting('enable_badges'), showMoreBadges: Em.computed.gt('moreBadgesCount', 0), - canDelete: Em.computed.not("user.deleteForbidden"), - showDelete: Em.computed.and("viewingAdmin", "showName", "canDelete"), + showDelete: Em.computed.and("viewingAdmin", "showName", "user.canBeDeleted"), moreBadgesCount: function() { return this.get('user.badge_count') - this.get('user.featured_user_badges.length'); @@ -74,14 +73,8 @@ export default ObjectController.extend({ self.set('cardTarget', target); Discourse.User.findByUsername(username).then(function (user) { - - // A bit hacky. If viewing admin, wrap it in Discourse.AdminUser - // TODO: Restructure this to be cleaner - var wrapped = user; - if (self.get('viewingAdmin')) { - wrapped = Discourse.AdminUser.create(user); - } - self.setProperties({ user: wrapped, avatar: user, visible: true}); + user = Discourse.User.create(user); + self.setProperties({ user: user, avatar: user, visible: true}); self.appEvents.trigger('usercard:shown'); }).finally(function(){ self.set('userLoading', null); diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js index 7088433927..47e36a415e 100644 --- a/app/assets/javascripts/discourse/models/user.js +++ b/app/assets/javascripts/discourse/models/user.js @@ -11,6 +11,9 @@ Discourse.User = Discourse.Model.extend({ hasPMs: Em.computed.gt("private_messages_stats.all", 0), hasStartedPMs: Em.computed.gt("private_messages_stats.mine", 0), hasUnreadPMs: Em.computed.gt("private_messages_stats.unread", 0), + hasPosted: Em.computed.gt("post_count", 0), + hasNotPosted: Em.computed.not("hasPosted"), + canBeDeleted: Em.computed.and("can_be_deleted", "hasNotPosted"), /** The user's stream From 0f0a329e7cca1e0361132d559b68026569b8698f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 3 Dec 2014 14:55:43 +0100 Subject: [PATCH 138/991] FIX: private message button wasn't adding the recipient (cc @gdpelican) --- app/assets/javascripts/discourse/routes/application.js.es6 | 2 +- app/assets/javascripts/discourse/routes/user.js.es6 | 2 +- app/assets/javascripts/discourse/templates/user/user.hbs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/routes/application.js.es6 b/app/assets/javascripts/discourse/routes/application.js.es6 index ca598b7748..83f8a53a7e 100644 --- a/app/assets/javascripts/discourse/routes/application.js.es6 +++ b/app/assets/javascripts/discourse/routes/application.js.es6 @@ -15,7 +15,7 @@ var ApplicationRoute = Discourse.Route.extend({ composePrivateMessage: function(user) { var self = this; this.transitionTo('userActivity', user).then(function () { - self.controllerFor('user-activity').send('composePrivateMessage'); + self.controllerFor('user-activity').send('composePrivateMessage', user); }); }, diff --git a/app/assets/javascripts/discourse/routes/user.js.es6 b/app/assets/javascripts/discourse/routes/user.js.es6 index ac4ddb49cf..e72b26d9b7 100644 --- a/app/assets/javascripts/discourse/routes/user.js.es6 +++ b/app/assets/javascripts/discourse/routes/user.js.es6 @@ -16,7 +16,7 @@ export default Discourse.Route.extend({ }, composePrivateMessage: function(user) { - var recipient = user ? user.username : ''; + var recipient = user ? user.get('username') : ''; return this.controllerFor('composer').open({ action: Discourse.Composer.PRIVATE_MESSAGE, usernames: recipient, diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index 3f8323efe4..a12eb5946d 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -39,7 +39,7 @@ {{/if}} + + {{#if viewingSelf}} +
    + +
    + {{/if}}
    diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 920c912638..63f9997d02 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -56,6 +56,7 @@ //= require ./discourse/helpers/cold-age-class //= require ./discourse/helpers/loading-spinner //= require ./discourse/helpers/category-link +//= require ./discourse/lib/export-result //= require ./discourse/dialects/dialect //= require ./discourse/lib/emoji/emoji @@ -69,4 +70,3 @@ //= require_tree ./discourse/templates //= require_tree ./discourse/routes //= require_tree ./discourse/initializers - diff --git a/app/assets/javascripts/main_include_admin.js b/app/assets/javascripts/main_include_admin.js index 00050ed3d9..dd77d21b00 100644 --- a/app/assets/javascripts/main_include_admin.js +++ b/app/assets/javascripts/main_include_admin.js @@ -1,8 +1,8 @@ //= require list-view -//= require admin/lib/export-result //= require admin/models/user-field //= require admin/controllers/admin-email-skipped //= require admin/controllers/change-site-customization-details +//= require discourse/lib/export-result //= require_tree ./admin //= require resumable.js diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 41e6923ab2..1eea26e448 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -139,6 +139,11 @@ } } + .user-archive { + margin-top: 10px; + margin-bottom: 10px; + } + .user-right.groups { margin-top: 0; } diff --git a/app/controllers/admin/export_csv_controller.rb b/app/controllers/export_csv_controller.rb similarity index 50% rename from app/controllers/admin/export_csv_controller.rb rename to app/controllers/export_csv_controller.rb index 0ab4aefe06..a97f675782 100644 --- a/app/controllers/admin/export_csv_controller.rb +++ b/app/controllers/export_csv_controller.rb @@ -1,17 +1,27 @@ -class Admin::ExportCsvController < Admin::AdminController +class ExportCsvController < ApplicationController skip_before_filter :check_xhr, only: [:show] def export_entity params.require(:entity) - # export csv file in a background thread + params.require(:entity_type) + if params[:entity_type] == "admin" + guardian.ensure_can_export_admin_entity!(current_user) + end + Jobs.enqueue(:export_csv_file, entity: params[:entity], user_id: current_user.id) render json: success_json end # download def show - filename = params.fetch(:id) + params.require(:entity) + params.require(:file_id) + if params[:entity] == "system" + guardian.ensure_can_export_admin_entity!(current_user) + end + + filename = params.fetch(:file_id) if export_csv_path = ExportCsv.get_download_path(filename) send_file export_csv_path else diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index f8c2a2f79f..2113aa932a 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -5,6 +5,7 @@ module Jobs class ExportCsvFile < Jobs::Base HEADER_ATTRS_FOR = {} + HEADER_ATTRS_FOR['user_archive'] = ['raw','like_count','reply_count','created_at'] HEADER_ATTRS_FOR['user'] = ['id','name','username','email','title','created_at','trust_level','active','admin','moderator','ip_address'] HEADER_ATTRS_FOR['user_stats'] = ['topics_entered','posts_read_count','time_read','topic_count','post_count','likes_given','likes_received'] HEADER_ATTRS_FOR['user_sso'] = ['external_id','external_email', 'external_username', 'external_name', 'external_avatar_url'] @@ -18,10 +19,16 @@ module Jobs def initialize @file_name = "" + @entity_type = "admin" end def execute(args) entity = args[:entity] + + if entity == "user_archive" + @entity_type = "user" + end + @current_user = User.find_by(id: args[:user_id]) export_method = "#{entity}_export".to_sym @@ -41,6 +48,13 @@ module Jobs notify_user end + def user_archive_export + user_archive_data = Post.where(user_id: @current_user.id).select(HEADER_ATTRS_FOR['user_archive']).with_deleted.to_a + user_archive_data.map do |user_archive| + get_user_archive_fields(user_archive) + end + end + def user_export query = ::AdminUserIndexQuery.new user_data = query.find_users_query.to_a @@ -113,6 +127,16 @@ module Jobs return group_names end + def get_user_archive_fields(user_archive) + user_archive_array = [] + + HEADER_ATTRS_FOR['user_archive'].each do |attr| + user_archive_array.push(user_archive.attributes[attr]) + end + + user_archive_array + end + def get_user_fields(user) user_array = [] @@ -234,7 +258,11 @@ module Jobs def notify_user if @current_user if @file_name != "" && File.exists?("#{ExportCsv.base_directory}/#{@file_name}") - SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/admin/export_csv/#{@file_name}", file_name: @file_name) + if @entity_type == "admin" + SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/export_csv/system/#{@file_name}", file_name: @file_name) + else + SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/export_csv/#{@current_user.username}/#{@file_name}", file_name: @file_name) + end else SystemMessage.create_from_system_user(@current_user, :csv_export_failed) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 33c117ab6f..df0a64b668 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -292,7 +292,7 @@ en: profile: "Profile" mute: "Mute" edit: "Edit Preferences" - download_archive: "download archive of my posts" + download_archive: "Download archive of my posts" new_private_message: "New Private Message" private_message: "Private Message" private_messages: "Messages" diff --git a/config/routes.rb b/config/routes.rb index 308bbf3cf5..7f58681e00 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -164,15 +164,6 @@ Discourse::Application.routes.draw do end end - resources :export_csv, constraints: AdminConstraint.new do - collection do - get "export_entity" => "export_csv#export_entity" - end - member do - get "" => "export_csv#show", constraints: { id: /[^\/]+/ } - end - end - resources :badges, constraints: AdminConstraint.new do collection do get "types" => "badges#badge_types" @@ -441,6 +432,13 @@ Discourse::Application.routes.draw do get "invites/redeem/:token" => "invites#redeem_disposable_invite" delete "invites" => "invites#destroy" + resources :export_csv do + collection do + get "export_entity" => "export_csv#export_entity" + end + end + get "export_csv/:entity/:file_id" => "export_csv#show", constraints: {entity: USERNAME_ROUTE_FORMAT, file_id: /[^\/]+/} + get "onebox" => "onebox#show" get "error" => "forums#error" diff --git a/lib/guardian.rb b/lib/guardian.rb index f0998e0577..97643ff3a4 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -249,6 +249,10 @@ class Guardian @can_see_emails end + def can_export_admin_entity?(user) + user.staff? + end + private def is_my_own?(obj) diff --git a/spec/controllers/admin/export_csv_controller_spec.rb b/spec/controllers/admin/export_csv_controller_spec.rb deleted file mode 100644 index 644deb6d90..0000000000 --- a/spec/controllers/admin/export_csv_controller_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require "spec_helper" - -describe Admin::ExportCsvController do - - it "is a subclass of AdminController" do - (Admin::ExportCsvController < Admin::AdminController).should == true - end - - let(:export_filename) { "export_b6a2bc87.csv" } - - context "while logged in as an admin" do - - before { @admin = log_in(:admin) } - - describe ".download" do - - it "uses send_file to transmit the export file" do - controller.stubs(:render) - export = ExportCsv.new() - ExportCsv.expects(:get_download_path).with(export_filename).returns(export) - subject.expects(:send_file).with(export) - get :show, id: export_filename - end - - it "returns 404 when the export file does not exist" do - ExportCsv.expects(:get_download_path).returns(nil) - get :show, id: export_filename - response.should be_not_found - end - - end - - end - -end diff --git a/spec/controllers/export_csv_controller_spec.rb b/spec/controllers/export_csv_controller_spec.rb new file mode 100644 index 0000000000..d1ced5560d --- /dev/null +++ b/spec/controllers/export_csv_controller_spec.rb @@ -0,0 +1,78 @@ +require "spec_helper" + +describe ExportCsvController do + + let(:export_filename) { "export_b6a2bc87.csv" } + + + context "while logged in as normal user" do + before { @user = log_in(:user) } + + describe ".export_entity" do + it "enqueues export job" do + Jobs.expects(:enqueue).with(:export_csv_file, has_entries(entity: "user_archive", user_id: @user.id)) + xhr :post, :export_entity, entity: "user_archive", entity_type: "user" + response.should be_success + end + + it "returns 404 when normal user tries to export admin entity" do + xhr :post, :export_entity, entity: "staff_action", entity_type: "admin" + response.should_not be_success + end + end + + describe ".download" do + it "uses send_file to transmit the export file" do + controller.stubs(:render) + export = ExportCsv.new() + ExportCsv.expects(:get_download_path).with(export_filename).returns(export) + subject.expects(:send_file).with(export) + get :show, entity: "username", file_id: export_filename + response.should be_success + end + + it "returns 404 when the normal user tries to access admin export file" do + controller.stubs(:render) + get :show, entity: "system", file_id: export_filename + response.should_not be_success + end + + it "returns 404 when the export file does not exist" do + ExportCsv.expects(:get_download_path).returns(nil) + get :show, entity: "username", file_id: export_filename + response.should be_not_found + end + end + end + + + context "while logged in as an admin" do + before { @admin = log_in(:admin) } + + describe ".export_entity" do + it "enqueues export job" do + Jobs.expects(:enqueue).with(:export_csv_file, has_entries(entity: "staff_action", user_id: @admin.id)) + xhr :post, :export_entity, entity: "staff_action", entity_type: "admin" + response.should be_success + end + end + + describe ".download" do + it "uses send_file to transmit the export file" do + controller.stubs(:render) + export = ExportCsv.new() + ExportCsv.expects(:get_download_path).with(export_filename).returns(export) + subject.expects(:send_file).with(export) + get :show, entity: "system", file_id: export_filename + response.should be_success + end + + it "returns 404 when the export file does not exist" do + ExportCsv.expects(:get_download_path).returns(nil) + get :show, entity: "system", file_id: export_filename + response.should be_not_found + end + end + end + +end From 7c7474aa10327972ab1c7014e750ade0cd01c5d7 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Wed, 24 Dec 2014 14:41:41 +0530 Subject: [PATCH 347/991] create a new table to maintain csv export log --- app/controllers/export_csv_controller.rb | 14 +++---- app/jobs/regular/export_csv_file.rb | 16 ++++---- app/jobs/scheduled/clean_up_exports.rb | 2 +- app/models/api_key.rb | 1 + app/models/csv_export_log.rb | 40 +++++++++++++++++++ app/models/export_csv.rb | 29 -------------- config/routes.rb | 4 +- .../20141223145058_create_csv_export_logs.rb | 9 +++++ .../controllers/export_csv_controller_spec.rb | 34 ++++++++-------- 9 files changed, 86 insertions(+), 63 deletions(-) create mode 100644 app/models/csv_export_log.rb delete mode 100644 app/models/export_csv.rb create mode 100644 db/migrate/20141223145058_create_csv_export_logs.rb diff --git a/app/controllers/export_csv_controller.rb b/app/controllers/export_csv_controller.rb index a97f675782..b108405408 100644 --- a/app/controllers/export_csv_controller.rb +++ b/app/controllers/export_csv_controller.rb @@ -15,14 +15,14 @@ class ExportCsvController < ApplicationController # download def show - params.require(:entity) - params.require(:file_id) - if params[:entity] == "system" - guardian.ensure_can_export_admin_entity!(current_user) - end + params.require(:id) + filename = params.fetch(:id) + export_id = filename.split('_')[1].split('.')[0] + export_initiated_by_user_id = 0 + export_initiated_by_user_id = CsvExportLog.where(id: export_id)[0].user_id unless CsvExportLog.where(id: export_id).empty? + export_csv_path = CsvExportLog.get_download_path(filename) - filename = params.fetch(:file_id) - if export_csv_path = ExportCsv.get_download_path(filename) + if export_csv_path && export_initiated_by_user_id == current_user.id send_file export_csv_path else render nothing: true, status: 404 diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index 2113aa932a..c0daa07bdd 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -239,15 +239,17 @@ module Jobs def set_file_path - @file_name = "export_#{SecureRandom.hex(4)}.csv" + @file = CsvExportLog.create(export_type: @entity_type, user_id: @current_user.id) + @file_name = "export_#{@file.id}.csv" + # ensure directory exists - dir = File.dirname("#{ExportCsv.base_directory}/#{@file_name}") + dir = File.dirname("#{CsvExportLog.base_directory}/#{@file_name}") FileUtils.mkdir_p(dir) unless Dir.exists?(dir) end def write_csv_file(data, header) # write to CSV file - CSV.open(File.expand_path("#{ExportCsv.base_directory}/#{@file_name}", __FILE__), "w") do |csv| + CSV.open(File.expand_path("#{CsvExportLog.base_directory}/#{@file_name}", __FILE__), "w") do |csv| csv << header data.each do |value| csv << value @@ -257,12 +259,8 @@ module Jobs def notify_user if @current_user - if @file_name != "" && File.exists?("#{ExportCsv.base_directory}/#{@file_name}") - if @entity_type == "admin" - SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/export_csv/system/#{@file_name}", file_name: @file_name) - else - SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/export_csv/#{@current_user.username}/#{@file_name}", file_name: @file_name) - end + if @file_name != "" && File.exists?("#{CsvExportLog.base_directory}/#{@file_name}") + SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/export_csv/#{@file_name}", file_name: @file_name) else SystemMessage.create_from_system_user(@current_user, :csv_export_failed) end diff --git a/app/jobs/scheduled/clean_up_exports.rb b/app/jobs/scheduled/clean_up_exports.rb index 27469d10a9..e61a96bd06 100644 --- a/app/jobs/scheduled/clean_up_exports.rb +++ b/app/jobs/scheduled/clean_up_exports.rb @@ -3,7 +3,7 @@ module Jobs every 2.day def execute(args) - ExportCsv.remove_old_exports # delete exported CSV files older than 2 days + CsvExportLog.remove_old_exports # delete exported CSV files older than 2 days end end end diff --git a/app/models/api_key.rb b/app/models/api_key.rb index 514c1ba4b6..9145ce4372 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -31,6 +31,7 @@ end # created_at :datetime not null # updated_at :datetime not null # allowed_ips :inet is an Array +# hidden :boolean default(FALSE), not null # # Indexes # diff --git a/app/models/csv_export_log.rb b/app/models/csv_export_log.rb new file mode 100644 index 0000000000..c16dfe0221 --- /dev/null +++ b/app/models/csv_export_log.rb @@ -0,0 +1,40 @@ +class CsvExportLog < ActiveRecord::Base + + def self.get_download_path(filename) + path = File.join(CsvExportLog.base_directory, filename) + if File.exists?(path) + return path + else + nil + end + end + + def self.remove_old_exports + expired_exports = CsvExportLog.where('created_at < ?', 2.days.ago).to_a + expired_exports.map do |expired_export| + file_name = "export_#{expired_export.id}.csv" + file_path = "#{CsvExportLog.base_directory}/#{file_name}" + + if File.exist?(file_path) + File.delete(file_path) + end + CsvExportLog.find(expired_export.id).destroy + end + end + + def self.base_directory + File.join(Rails.root, "public", "uploads", "csv_exports", RailsMultisite::ConnectionManagement.current_db) + end + +end + +# == Schema Information +# +# Table name: csv_export_logs +# +# id :integer not null, primary key +# export_type :string(255) not null +# user_id :integer not null +# created_at :datetime +# updated_at :datetime +# diff --git a/app/models/export_csv.rb b/app/models/export_csv.rb deleted file mode 100644 index fcbae3705d..0000000000 --- a/app/models/export_csv.rb +++ /dev/null @@ -1,29 +0,0 @@ -class ExportCsv - - def self.get_download_path(filename) - path = File.join(ExportCsv.base_directory, filename) - if File.exists?(path) - return path - else - nil - end - end - - def self.remove_old_exports - if Dir.exists?(ExportCsv.base_directory) - Dir.foreach(ExportCsv.base_directory) do |file| - path = File.join(ExportCsv.base_directory, file) - next if File.directory? path - - if (File.mtime(path) < 2.days.ago) - File.delete(path) - end - end - end - end - - def self.base_directory - File.join(Rails.root, "public", "uploads", "csv_exports", RailsMultisite::ConnectionManagement.current_db) - end - -end diff --git a/config/routes.rb b/config/routes.rb index 7f58681e00..81a8d29efe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -436,8 +436,10 @@ Discourse::Application.routes.draw do collection do get "export_entity" => "export_csv#export_entity" end + member do + get "" => "export_csv#show", constraints: { id: /[^\/]+/ } + end end - get "export_csv/:entity/:file_id" => "export_csv#show", constraints: {entity: USERNAME_ROUTE_FORMAT, file_id: /[^\/]+/} get "onebox" => "onebox#show" diff --git a/db/migrate/20141223145058_create_csv_export_logs.rb b/db/migrate/20141223145058_create_csv_export_logs.rb new file mode 100644 index 0000000000..83291db28b --- /dev/null +++ b/db/migrate/20141223145058_create_csv_export_logs.rb @@ -0,0 +1,9 @@ +class CreateCsvExportLogs < ActiveRecord::Migration + def change + create_table :csv_export_logs do |t| + t.string :export_type, null: false + t.integer :user_id, null: false + t.timestamps + end + end +end diff --git a/spec/controllers/export_csv_controller_spec.rb b/spec/controllers/export_csv_controller_spec.rb index d1ced5560d..4de76e21c9 100644 --- a/spec/controllers/export_csv_controller_spec.rb +++ b/spec/controllers/export_csv_controller_spec.rb @@ -1,8 +1,7 @@ require "spec_helper" describe ExportCsvController do - - let(:export_filename) { "export_b6a2bc87.csv" } + let(:export_filename) { "export_999.csv" } context "while logged in as normal user" do @@ -23,23 +22,24 @@ describe ExportCsvController do describe ".download" do it "uses send_file to transmit the export file" do + file = CsvExportLog.create(export_type: "user", user_id: @user.id) + file_name = "export_#{file.id}.csv" controller.stubs(:render) - export = ExportCsv.new() - ExportCsv.expects(:get_download_path).with(export_filename).returns(export) + export = CsvExportLog.new() + CsvExportLog.expects(:get_download_path).with(file_name).returns(export) subject.expects(:send_file).with(export) - get :show, entity: "username", file_id: export_filename + get :show, id: file_name response.should be_success end - it "returns 404 when the normal user tries to access admin export file" do - controller.stubs(:render) - get :show, entity: "system", file_id: export_filename - response.should_not be_success + it "returns 404 when the user tries to export another user's csv file" do + get :show, id: export_filename + response.should be_not_found end it "returns 404 when the export file does not exist" do - ExportCsv.expects(:get_download_path).returns(nil) - get :show, entity: "username", file_id: export_filename + CsvExportLog.expects(:get_download_path).returns(nil) + get :show, id: export_filename response.should be_not_found end end @@ -59,17 +59,19 @@ describe ExportCsvController do describe ".download" do it "uses send_file to transmit the export file" do + file = CsvExportLog.create(export_type: "admin", user_id: @admin.id) + file_name = "export_#{file.id}.csv" controller.stubs(:render) - export = ExportCsv.new() - ExportCsv.expects(:get_download_path).with(export_filename).returns(export) + export = CsvExportLog.new() + CsvExportLog.expects(:get_download_path).with(file_name).returns(export) subject.expects(:send_file).with(export) - get :show, entity: "system", file_id: export_filename + get :show, id: file_name response.should be_success end it "returns 404 when the export file does not exist" do - ExportCsv.expects(:get_download_path).returns(nil) - get :show, entity: "system", file_id: export_filename + CsvExportLog.expects(:get_download_path).returns(nil) + get :show, id: export_filename response.should be_not_found end end From 3b945920d4b8d39be56b52293a5f39bb28d862c5 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Wed, 24 Dec 2014 15:41:21 -0800 Subject: [PATCH 348/991] copy improvements on data export --- app/assets/stylesheets/desktop/user.scss | 5 ----- config/locales/client.en.yml | 6 +++--- config/locales/server.en.yml | 12 ++++++------ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 1eea26e448..41e6923ab2 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -139,11 +139,6 @@ } } - .user-archive { - margin-top: 10px; - margin-bottom: 10px; - } - .user-right.groups { margin-top: 0; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index df0a64b668..0d8abc63b6 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -292,7 +292,7 @@ en: profile: "Profile" mute: "Mute" edit: "Edit Preferences" - download_archive: "Download archive of my posts" + download_archive: "Download My Posts" new_private_message: "New Private Message" private_message: "Private Message" private_messages: "Messages" @@ -494,7 +494,7 @@ en: none: "You haven't invited anyone here yet. You can send individual invites, or invite a bunch of people at once by uploading a bulk invite file." text: "Bulk Invite from File" uploading: "Uploading..." - success: "File uploaded successfully, you will be notified shortly with progress." + success: "File uploaded successfully, you will be notified via private message when the process is complete." error: "There was an error uploading '{{filename}}': {{message}}" password: @@ -1695,7 +1695,7 @@ en: confirm: "Are your sure you want to rollback the database to the previous working state?" export_csv: - success: "Export initiated, you will be notified shortly with progress." + success: "Export initiated, you will be notified via private message when the process is complete." failed: "Export failed. Please check the logs." button_text: "Export" button_title: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 2908e950ef..a58fd3fae8 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1481,17 +1481,17 @@ en: ``` csv_export_succeeded: - subject_template: "Data Export completed successfully" + subject_template: "Data export complete" text_body_template: | - The data export was successful. + Your data export was successful! :dvd: - Download CSV file: %{file_name} + %{file_name} - CSV file download link will expire after 48 hours. + The above download link will be valid for 48 hours. csv_export_failed: - subject_template: "Export failed" - text_body_template: "The export has failed. Please check the logs." + subject_template: "Data export failed" + text_body_template: "We're sorry, but your data export failed. Please check the logs or contact a staff member." email_reject_trust_level: subject_template: "Email issue -- Insufficient Trust Level" From 4dd7610bb2b8fca42be9a4972065f4bd6f6884d0 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Wed, 24 Dec 2014 15:43:21 -0800 Subject: [PATCH 349/991] add + to keyboard help key simul combos --- config/locales/client.en.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0d8abc63b6..294010a1cc 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2217,7 +2217,7 @@ en: back: 'u Back' up_down: 'k/j Move selection ↑ ↓' open: 'o or Enter Open selected topic' - next_prev: 'shift j/shift k Next/previous section' + next_prev: 'shift+j/shift+k Next/previous section' application: title: 'Application' create: 'c Create a new topic' @@ -2232,11 +2232,11 @@ en: actions: title: 'Actions' star: 'f Star topic' - pin_unpin_topic: 'shift p Pin/Unpin topic' - share_topic: 'shift s Share topic' + pin_unpin_topic: 'shift+p Pin/Unpin topic' + share_topic: 'shift+s Share topic' share_post: 's Share post' reply_as_new_topic: 't Reply as linked topic' - reply_topic: 'shift r Reply to topic' + reply_topic: 'shift+r Reply to topic' reply_post: 'r Reply to post' quote_post: 'q Quote post' like: 'l Like post' From b91f9f4142b0e092aef33b1273e39b66befb63c2 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 25 Dec 2014 01:05:45 -0800 Subject: [PATCH 350/991] remove a bunch of needless css prefixes --- .../discourse/views/post-menu.js.es6 | 2 +- .../stylesheets/common/base/discourse.scss | 9 --------- .../common/base/magnific-popup.scss | 3 --- app/assets/stylesheets/common/base/modal.scss | 3 --- app/assets/stylesheets/desktop/modal.scss | 3 --- .../stylesheets/desktop/topic-post.scss | 4 ---- .../vendor/font_awesome/_mixins.scss | 10 ---------- .../vendor/font_awesome/_spinning.scss | 19 ------------------- 8 files changed, 1 insertion(+), 52 deletions(-) diff --git a/app/assets/javascripts/discourse/views/post-menu.js.es6 b/app/assets/javascripts/discourse/views/post-menu.js.es6 index bc7e8b51a7..c1d86e79a4 100644 --- a/app/assets/javascripts/discourse/views/post-menu.js.es6 +++ b/app/assets/javascripts/discourse/views/post-menu.js.es6 @@ -19,7 +19,7 @@ function animateHeart($elem, start, end, complete) { .animate({ textIndent: end }, { complete: complete, step: function(now) { - $(this).css('-webkit-transform','scale('+now+')'); + $(this).css('transform','scale('+now+')'); }, duration: 150 }, 'linear'); diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index c7659f0218..e869bcd1ce 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -182,15 +182,6 @@ body { } -@-webkit-keyframes rotate-forever { - 0% { - -webkit-transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - } -} - @keyframes rotate-forever { 0% { transform: rotate(0deg); diff --git a/app/assets/stylesheets/common/base/magnific-popup.scss b/app/assets/stylesheets/common/base/magnific-popup.scss index a0c98d3051..18837345cd 100644 --- a/app/assets/stylesheets/common/base/magnific-popup.scss +++ b/app/assets/stylesheets/common/base/magnific-popup.scss @@ -599,15 +599,12 @@ button { // Scale navigation arrows and reduce padding from sides @media all and (max-width: 900px) { .mfp-arrow { - -webkit-transform: scale(0.75); transform: scale(0.75); } .mfp-arrow-left { - -webkit-transform-origin: 0; transform-origin: 0; } .mfp-arrow-right { - -webkit-transform-origin: 100%; transform-origin: 100%; } .mfp-container { diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index f3c9178b8e..3cae71690b 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -34,9 +34,6 @@ //fade in -@-webkit-keyframes fade { - from {opacity: 0} - to {opacity: .8} }@keyframes fade { from {opacity: 0} to {opacity: .8} diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index 59cdbd74f4..dba91df4dd 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -31,9 +31,6 @@ animation: modal .25s; //fade in and animate up -@-webkit-keyframes modal { - from {opacity: 0} - to {opacity: 1} }@keyframes fade { from {opacity: 0} to {opacity: 1} diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index a2107338c5..fef95418de 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -514,10 +514,6 @@ video { from {opacity: 0;} to {opacity: 1;} } -@-webkit-keyframes fadein { - from {opacity: 0;} - to {opacity: 1;} -} .extra-info-wrapper { overflow: hidden; diff --git a/app/assets/stylesheets/vendor/font_awesome/_mixins.scss b/app/assets/stylesheets/vendor/font_awesome/_mixins.scss index 9f555963f8..e7b9cbd1ae 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_mixins.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_mixins.scss @@ -2,19 +2,9 @@ // -------------------------- @mixin fa-icon-rotate($degrees, $rotation) { - filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation); - -webkit-transform: rotate($degrees); - -moz-transform: rotate($degrees); - -ms-transform: rotate($degrees); - -o-transform: rotate($degrees); transform: rotate($degrees); } @mixin fa-icon-flip($horiz, $vert, $rotation) { - filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation); - -webkit-transform: scale($horiz, $vert); - -moz-transform: scale($horiz, $vert); - -ms-transform: scale($horiz, $vert); - -o-transform: scale($horiz, $vert); transform: scale($horiz, $vert); } diff --git a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss index ba1e4f1623..c352dc4f2c 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss @@ -2,28 +2,9 @@ // -------------------------- .#{$fa-css-prefix}-spin { - -webkit-animation: spin 2s infinite linear; - -moz-animation: spin 2s infinite linear; - -o-animation: spin 2s infinite linear; animation: spin 2s infinite linear; } -@-moz-keyframes spin { - 0% { -moz-transform: rotate(0deg); } - 100% { -moz-transform: rotate(359deg); } -} -@-webkit-keyframes spin { - 0% { -webkit-transform: rotate(0deg); } - 100% { -webkit-transform: rotate(359deg); } -} -@-o-keyframes spin { - 0% { -o-transform: rotate(0deg); } - 100% { -o-transform: rotate(359deg); } -} -@-ms-keyframes spin { - 0% { -ms-transform: rotate(0deg); } - 100% { -ms-transform: rotate(359deg); } -} @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(359deg); } From a82fda0a50dd3a01a61144835be308fc0143cbc9 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 25 Dec 2014 01:14:20 -0800 Subject: [PATCH 351/991] who the hell puts a } in front of a SASS line? --- app/assets/stylesheets/common/base/modal.scss | 2 +- app/assets/stylesheets/desktop/modal.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index 3cae71690b..dc9cde6fd4 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -34,7 +34,7 @@ //fade in -}@keyframes fade { +@keyframes fade { from {opacity: 0} to {opacity: .8} } diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index dba91df4dd..531cd225a0 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -31,7 +31,7 @@ animation: modal .25s; //fade in and animate up -}@keyframes fade { +@keyframes fade { from {opacity: 0} to {opacity: 1} } From 8b3babdbde4f307cdfddb4db430f59e568071315 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 25 Dec 2014 01:41:44 -0800 Subject: [PATCH 352/991] add back in a few -webkit- css3 prefixes due to http://shouldiprefix.com/ and iOS mainly! --- app/assets/stylesheets/common/base/discourse.scss | 2 ++ app/assets/stylesheets/common/base/magnific-popup.scss | 3 +++ app/assets/stylesheets/desktop/modal.scss | 4 ++-- app/assets/stylesheets/vendor/font_awesome/_mixins.scss | 6 ++++-- app/assets/stylesheets/vendor/font_awesome/_spinning.scss | 5 +++-- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index e869bcd1ce..039e9fe2d3 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -185,9 +185,11 @@ body { @keyframes rotate-forever { 0% { transform: rotate(0deg); + -webkit-transform: rotate(0deg); } 100% { transform: rotate(360deg); + -webkit-transform: rotate(360deg); } } diff --git a/app/assets/stylesheets/common/base/magnific-popup.scss b/app/assets/stylesheets/common/base/magnific-popup.scss index 18837345cd..441ee54b53 100644 --- a/app/assets/stylesheets/common/base/magnific-popup.scss +++ b/app/assets/stylesheets/common/base/magnific-popup.scss @@ -600,12 +600,15 @@ button { @media all and (max-width: 900px) { .mfp-arrow { transform: scale(0.75); + -webkit-transform: scale(0.75); } .mfp-arrow-left { transform-origin: 0; + -webkit-transform-origin: 0; } .mfp-arrow-right { transform-origin: 100%; + -webkit-transform-origin: 100%; } .mfp-container { padding-left: $popup-padding-left-mobile; diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index 531cd225a0..2f0e040517 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -25,8 +25,8 @@ } .modal.in { --webkit-animation: modal .25s; -animation: modal .25s; + -webkit-animation: modal .25s; + animation: modal .25s; } //fade in and animate up diff --git a/app/assets/stylesheets/vendor/font_awesome/_mixins.scss b/app/assets/stylesheets/vendor/font_awesome/_mixins.scss index e7b9cbd1ae..2f6f8cf034 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_mixins.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_mixins.scss @@ -2,9 +2,11 @@ // -------------------------- @mixin fa-icon-rotate($degrees, $rotation) { - transform: rotate($degrees); + transform: rotate($degrees); + -webkit-transform: rotate($degrees); } @mixin fa-icon-flip($horiz, $vert, $rotation) { - transform: scale($horiz, $vert); + transform: scale($horiz, $vert); + -webkit-transform: scale($horiz, $vert); } diff --git a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss index c352dc4f2c..1e97236eb7 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss @@ -3,9 +3,10 @@ .#{$fa-css-prefix}-spin { animation: spin 2s infinite linear; + -webkit-animation: spin 2s infinite linear; } @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(359deg); } + 0% { transform: rotate(0deg); -webkit-transform: rotate(0deg); } + 100% { transform: rotate(359deg); -webkit-transform: rotate(359deg); } } From 2a464182842c1cd44561a42cc505f0520738a6a2 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 25 Dec 2014 02:19:06 -0800 Subject: [PATCH 353/991] add back in @-webkit-keyframes --- app/assets/stylesheets/common/base/discourse.scss | 13 +++++++++---- app/assets/stylesheets/desktop/modal.scss | 5 +++++ app/assets/stylesheets/desktop/topic-post.scss | 5 +++++ .../stylesheets/vendor/font_awesome/_spinning.scss | 5 +++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 039e9fe2d3..941fbbf7fd 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -181,17 +181,22 @@ body { opacity: 1; } - -@keyframes rotate-forever { +@-webkit-keyframes rotate-forever { 0% { - transform: rotate(0deg); -webkit-transform: rotate(0deg); } 100% { - transform: rotate(360deg); -webkit-transform: rotate(360deg); } } +@keyframes rotate-forever { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} .inline-spinner { display: inline-block; diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index 2f0e040517..b51f8a7501 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -31,6 +31,11 @@ //fade in and animate up +@-webkit-keyframes modal { + from {opacity: 0} + to {opacity: 1} +} + @keyframes fade { from {opacity: 0} to {opacity: 1} diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index fef95418de..b134adf7b6 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -510,6 +510,11 @@ video { max-height: 500px; } +@-webkit-keyframes fadein { + from {opacity: 0;} + to {opacity: 1;} +} + @keyframes fadein { from {opacity: 0;} to {opacity: 1;} diff --git a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss index 1e97236eb7..8eace85262 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss @@ -10,3 +10,8 @@ 0% { transform: rotate(0deg); -webkit-transform: rotate(0deg); } 100% { transform: rotate(359deg); -webkit-transform: rotate(359deg); } } + +@-webkit-keyframes spin { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(359deg); } +} From 92d61755b37fc66f2652014e8ebdad11034ba7b7 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Thu, 25 Dec 2014 02:29:39 -0800 Subject: [PATCH 354/991] clean up CSS3 animations a bit --- app/assets/stylesheets/common/base/modal.scss | 6 ++++++ app/assets/stylesheets/desktop/modal.scss | 16 ++-------------- app/assets/stylesheets/desktop/topic-post.scss | 3 +-- .../vendor/font_awesome/_spinning.scss | 4 ++-- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index dc9cde6fd4..8e35ab13b6 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -38,6 +38,12 @@ from {opacity: 0} to {opacity: .8} } + +@-webkit-keyframes fade { + from {opacity: 0} + to {opacity: 1} +} + .modal-outer-container { display:table; width:100%; diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index b51f8a7501..ae3c1f80a3 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -25,20 +25,8 @@ } .modal.in { - -webkit-animation: modal .25s; - animation: modal .25s; -} - -//fade in and animate up - -@-webkit-keyframes modal { - from {opacity: 0} - to {opacity: 1} -} - -@keyframes fade { - from {opacity: 0} - to {opacity: 1} + -webkit-animation: fade .25s; + animation: fade .25s; } .modal-body { diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index b134adf7b6..b2ec22dfcf 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -525,8 +525,7 @@ video { .star, .badge-wrapper, i, .topic-link:not(.loading) { -webkit-animation: fadein .7s; - animation-duration: .7s; - animation-name: fadein; + animation: fadein .7s; } .topic-statuses { diff --git a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss index 8eace85262..e51b9d0b94 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_spinning.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_spinning.scss @@ -7,8 +7,8 @@ } @keyframes spin { - 0% { transform: rotate(0deg); -webkit-transform: rotate(0deg); } - 100% { transform: rotate(359deg); -webkit-transform: rotate(359deg); } + 0% { transform: rotate(0deg); } + 100% { transform: rotate(359deg); } } @-webkit-keyframes spin { From 9932bea7ce449b685959d6c2df10b5965fddb772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 25 Dec 2014 17:58:15 +0100 Subject: [PATCH 355/991] FEATURE: default emoji override --- app/controllers/admin/emojis_controller.rb | 2 +- spec/controllers/admin/emojis_controller_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/emojis_controller.rb b/app/controllers/admin/emojis_controller.rb index 9354a3bb41..ea230f1224 100644 --- a/app/controllers/admin/emojis_controller.rb +++ b/app/controllers/admin/emojis_controller.rb @@ -14,7 +14,7 @@ class Admin::EmojisController < Admin::AdminController .downcase # check the name doesn't already exist - if Emoji.all.detect { |e| e.name == name } + if Emoji.custom.detect { |e| e.name == name } render json: failed_json.merge(message: I18n.t("emoji.errors.name_already_exists", name: name)), status: 422 else if emoji = Emoji.create_for(file, name) diff --git a/spec/controllers/admin/emojis_controller_spec.rb b/spec/controllers/admin/emojis_controller_spec.rb index 43293cf51d..9aa79c44d0 100644 --- a/spec/controllers/admin/emojis_controller_spec.rb +++ b/spec/controllers/admin/emojis_controller_spec.rb @@ -29,7 +29,7 @@ describe Admin::EmojisController do context '.create' do - before { Emoji.expects(:all).returns([custom_emoji]) } + before { Emoji.expects(:custom).returns([custom_emoji]) } context 'name already exist' do it "throws an error" do From b65ac132b7c2962fd89951c569641cabe5b8212e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 25 Dec 2014 18:04:23 +0100 Subject: [PATCH 356/991] FIX: 1.9 regression on i18n strings with variables --- .../javascripts/discourse/templates/modal/not_activated.hbs | 4 ++-- app/assets/javascripts/discourse/templates/user-card.hbs | 2 +- .../javascripts/discourse/templates/user/preferences.hbs | 2 +- app/assets/javascripts/discourse/templates/user/user.hbs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/modal/not_activated.hbs b/app/assets/javascripts/discourse/templates/modal/not_activated.hbs index f4d0f2e14f..4266126c8d 100644 --- a/app/assets/javascripts/discourse/templates/modal/not_activated.hbs +++ b/app/assets/javascripts/discourse/templates/modal/not_activated.hbs @@ -1,8 +1,8 @@ diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index b04b659f58..3f65a1865e 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -43,7 +43,7 @@ {{#if isSuspended}}
    {{fa-icon "ban"}} - {{i18n 'user.suspended_notice' date="user.suspendedTillDate"}}
    + {{i18n 'user.suspended_notice' date=user.suspendedTillDate}}
    {{i18n 'user.suspended_reason'}} {{user.suspend_reason}}
    {{else}} diff --git a/app/assets/javascripts/discourse/templates/user/preferences.hbs b/app/assets/javascripts/discourse/templates/user/preferences.hbs index 4a9e7e436c..a1d50a4b56 100644 --- a/app/assets/javascripts/discourse/templates/user/preferences.hbs +++ b/app/assets/javascripts/discourse/templates/user/preferences.hbs @@ -17,7 +17,7 @@ {{/if}}
    - {{{i18n 'user.username.short_instructions' username="username"}}} + {{{i18n 'user.username.short_instructions' username=username}}}
    diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index 524a0232fd..cccf2d5b27 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -79,7 +79,7 @@ {{#if isSuspended}}
    {{fa-icon "ban"}} - {{i18n 'user.suspended_notice' date="suspendedTillDate"}}
    + {{i18n 'user.suspended_notice' date=suspendedTillDate}}
    {{i18n 'user.suspended_reason'}} {{suspend_reason}}
    {{/if}} From 8edf2afb83c39c8715b9eab0fcb61f1c1c384e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 25 Dec 2014 18:25:07 +0100 Subject: [PATCH 357/991] FIX: proper redirection when deleting a user --- app/assets/javascripts/admin/models/admin_user.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/models/admin_user.js b/app/assets/javascripts/admin/models/admin_user.js index 06dd114e47..9c1e397e66 100644 --- a/app/assets/javascripts/admin/models/admin_user.js +++ b/app/assets/javascripts/admin/models/admin_user.js @@ -331,7 +331,11 @@ Discourse.AdminUser = Discourse.User.extend({ data: formData }).then(function(data) { if (data.deleted) { - document.location = location; + if (/^\/admin\/users\/list\//.test(location)) { + document.location = location; + } else { + document.location = "/admin/users/list/active"; + } } else { bootbox.alert(I18n.t("admin.user.delete_failed")); if (data.user) { From 6d770d0e6e5dd09419ec80c186d4b8f39f4a36ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Sat, 27 Dec 2014 14:03:48 +0100 Subject: [PATCH 358/991] Fix typo in Gemfile [ci skip] --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index e5837a1af7..5ea606a1c9 100644 --- a/Gemfile +++ b/Gemfile @@ -230,7 +230,7 @@ gem 'lru_redux' gem 'htmlentities', require: false # IMPORTANT: mini profiler monkey patches, so it better be required last -# If you want to amend mini profiler to do the monkey patches in the railstie +# If you want to amend mini profiler to do the monkey patches in the railties # we are open to it. by deferring require to the initializer we can configure discourse installs without it gem 'flamegraph', require: false From 8d03ff6f82291c459500a6ea6bab45a9e60cfed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Sun, 28 Dec 2014 11:10:03 +0100 Subject: [PATCH 359/991] FIX: cache emojis for 1 year --- config/nginx.sample.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/nginx.sample.conf b/config/nginx.sample.conf index f13a987a12..112614197c 100644 --- a/config/nginx.sample.conf +++ b/config/nginx.sample.conf @@ -87,6 +87,12 @@ server { expires 1y; add_header Cache-Control public; } + + # cache emojis + location ~ /_?emoji/ { + expires 1y; + add_header Cache-Control public; + } location ~ ^/uploads/ { From 68e66f3a25fa8afbd14c7d2d9d2240505c6274a7 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Sun, 28 Dec 2014 21:43:49 +0530 Subject: [PATCH 360/991] Rename CsvExportLog to UserExport --- app/controllers/export_csv_controller.rb | 4 ++-- app/jobs/regular/export_csv_file.rb | 10 +++++----- app/jobs/scheduled/clean_up_exports.rb | 2 +- app/models/{csv_export_log.rb => user_export.rb} | 14 +++++++------- ...019_rename_csv_export_logs_to_user_exports.rb | 5 +++++ spec/controllers/export_csv_controller_spec.rb | 16 ++++++++-------- 6 files changed, 28 insertions(+), 23 deletions(-) rename app/models/{csv_export_log.rb => user_export.rb} (68%) create mode 100644 db/migrate/20141228151019_rename_csv_export_logs_to_user_exports.rb diff --git a/app/controllers/export_csv_controller.rb b/app/controllers/export_csv_controller.rb index b108405408..0db074d1f4 100644 --- a/app/controllers/export_csv_controller.rb +++ b/app/controllers/export_csv_controller.rb @@ -19,8 +19,8 @@ class ExportCsvController < ApplicationController filename = params.fetch(:id) export_id = filename.split('_')[1].split('.')[0] export_initiated_by_user_id = 0 - export_initiated_by_user_id = CsvExportLog.where(id: export_id)[0].user_id unless CsvExportLog.where(id: export_id).empty? - export_csv_path = CsvExportLog.get_download_path(filename) + export_initiated_by_user_id = UserExport.where(id: export_id)[0].user_id unless UserExport.where(id: export_id).empty? + export_csv_path = UserExport.get_download_path(filename) if export_csv_path && export_initiated_by_user_id == current_user.id send_file export_csv_path diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index c0daa07bdd..4f80a7bd6e 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -239,17 +239,17 @@ module Jobs def set_file_path - @file = CsvExportLog.create(export_type: @entity_type, user_id: @current_user.id) + @file = UserExport.create(export_type: @entity_type, user_id: @current_user.id) @file_name = "export_#{@file.id}.csv" - + # ensure directory exists - dir = File.dirname("#{CsvExportLog.base_directory}/#{@file_name}") + dir = File.dirname("#{UserExport.base_directory}/#{@file_name}") FileUtils.mkdir_p(dir) unless Dir.exists?(dir) end def write_csv_file(data, header) # write to CSV file - CSV.open(File.expand_path("#{CsvExportLog.base_directory}/#{@file_name}", __FILE__), "w") do |csv| + CSV.open(File.expand_path("#{UserExport.base_directory}/#{@file_name}", __FILE__), "w") do |csv| csv << header data.each do |value| csv << value @@ -259,7 +259,7 @@ module Jobs def notify_user if @current_user - if @file_name != "" && File.exists?("#{CsvExportLog.base_directory}/#{@file_name}") + if @file_name != "" && File.exists?("#{UserExport.base_directory}/#{@file_name}") SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/export_csv/#{@file_name}", file_name: @file_name) else SystemMessage.create_from_system_user(@current_user, :csv_export_failed) diff --git a/app/jobs/scheduled/clean_up_exports.rb b/app/jobs/scheduled/clean_up_exports.rb index e61a96bd06..b439d7f71c 100644 --- a/app/jobs/scheduled/clean_up_exports.rb +++ b/app/jobs/scheduled/clean_up_exports.rb @@ -3,7 +3,7 @@ module Jobs every 2.day def execute(args) - CsvExportLog.remove_old_exports # delete exported CSV files older than 2 days + UserExport.remove_old_exports # delete exported CSV files older than 2 days end end end diff --git a/app/models/csv_export_log.rb b/app/models/user_export.rb similarity index 68% rename from app/models/csv_export_log.rb rename to app/models/user_export.rb index c16dfe0221..9b1e9aadd8 100644 --- a/app/models/csv_export_log.rb +++ b/app/models/user_export.rb @@ -1,7 +1,7 @@ -class CsvExportLog < ActiveRecord::Base +class UserExport < ActiveRecord::Base def self.get_download_path(filename) - path = File.join(CsvExportLog.base_directory, filename) + path = File.join(UserExport.base_directory, filename) if File.exists?(path) return path else @@ -10,15 +10,15 @@ class CsvExportLog < ActiveRecord::Base end def self.remove_old_exports - expired_exports = CsvExportLog.where('created_at < ?', 2.days.ago).to_a + expired_exports = UserExport.where('created_at < ?', 2.days.ago).to_a expired_exports.map do |expired_export| file_name = "export_#{expired_export.id}.csv" - file_path = "#{CsvExportLog.base_directory}/#{file_name}" - + file_path = "#{UserExport.base_directory}/#{file_name}" + if File.exist?(file_path) File.delete(file_path) end - CsvExportLog.find(expired_export.id).destroy + UserExport.find(expired_export.id).destroy end end @@ -30,7 +30,7 @@ end # == Schema Information # -# Table name: csv_export_logs +# Table name: user_exports # # id :integer not null, primary key # export_type :string(255) not null diff --git a/db/migrate/20141228151019_rename_csv_export_logs_to_user_exports.rb b/db/migrate/20141228151019_rename_csv_export_logs_to_user_exports.rb new file mode 100644 index 0000000000..12d15bb5e1 --- /dev/null +++ b/db/migrate/20141228151019_rename_csv_export_logs_to_user_exports.rb @@ -0,0 +1,5 @@ +class RenameCsvExportLogsToUserExports < ActiveRecord::Migration + def change + rename_table :csv_export_logs, :user_exports + end +end diff --git a/spec/controllers/export_csv_controller_spec.rb b/spec/controllers/export_csv_controller_spec.rb index 4de76e21c9..12b70046e3 100644 --- a/spec/controllers/export_csv_controller_spec.rb +++ b/spec/controllers/export_csv_controller_spec.rb @@ -22,11 +22,11 @@ describe ExportCsvController do describe ".download" do it "uses send_file to transmit the export file" do - file = CsvExportLog.create(export_type: "user", user_id: @user.id) + file = UserExport.create(export_type: "user", user_id: @user.id) file_name = "export_#{file.id}.csv" controller.stubs(:render) - export = CsvExportLog.new() - CsvExportLog.expects(:get_download_path).with(file_name).returns(export) + export = UserExport.new() + UserExport.expects(:get_download_path).with(file_name).returns(export) subject.expects(:send_file).with(export) get :show, id: file_name response.should be_success @@ -38,7 +38,7 @@ describe ExportCsvController do end it "returns 404 when the export file does not exist" do - CsvExportLog.expects(:get_download_path).returns(nil) + UserExport.expects(:get_download_path).returns(nil) get :show, id: export_filename response.should be_not_found end @@ -59,18 +59,18 @@ describe ExportCsvController do describe ".download" do it "uses send_file to transmit the export file" do - file = CsvExportLog.create(export_type: "admin", user_id: @admin.id) + file = UserExport.create(export_type: "admin", user_id: @admin.id) file_name = "export_#{file.id}.csv" controller.stubs(:render) - export = CsvExportLog.new() - CsvExportLog.expects(:get_download_path).with(file_name).returns(export) + export = UserExport.new() + UserExport.expects(:get_download_path).with(file_name).returns(export) subject.expects(:send_file).with(export) get :show, id: file_name response.should be_success end it "returns 404 when the export file does not exist" do - CsvExportLog.expects(:get_download_path).returns(nil) + UserExport.expects(:get_download_path).returns(nil) get :show, id: export_filename response.should be_not_found end From ef6293303480b319668bfdaca4e98492bc7e2451 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 29 Dec 2014 13:30:54 +1100 Subject: [PATCH 361/991] Ruby 2.2 fixes --- Gemfile | 3 +++ Gemfile.lock | 16 ++++++++-------- config/initializers/06-mini_profiler.rb | 5 ++++- spec/models/discourse_single_sign_on_spec.rb | 14 ++++++++++++++ spec/spec_helper.rb | 7 +++++-- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index e5837a1af7..ddf9def285 100644 --- a/Gemfile +++ b/Gemfile @@ -245,6 +245,9 @@ gem 'rbtrace', require: false, platform: :mri gem 'ruby-readability', require: false gem 'simple-rss', require: false + +# TODO mri_22 should be here, but bundler was real slow to pick it up +# not even in production bundler yet, monkey patching it in feels bad gem 'gctools', require: false, platform: :mri_21 gem 'stackprof', require: false, platform: :mri_21 gem 'memory_profiler', require: false, platform: :mri_21 diff --git a/Gemfile.lock b/Gemfile.lock index cb946d0706..cd0beb92b2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,7 +45,7 @@ GEM ember-source execjs handlebars-source (>= 1.0.0.rc.4) - better_errors (2.0.0) + better_errors (2.1.0) coderay (>= 1.0.0) erubis (>= 2.6.6) rack (>= 0.9.0) @@ -98,8 +98,8 @@ GEM fast_xs (0.8.0) fastimage (1.6.3) addressable (~> 2.3, >= 2.3.5) - ffi (1.9.5) - flamegraph (0.0.9) + ffi (1.9.6) + flamegraph (0.1.0) fast_stack fog (1.22.1) fog-brightbox @@ -134,7 +134,7 @@ GEM highline (1.6.21) hike (1.2.3) hiredis (0.5.2) - htmlentities (4.3.2) + htmlentities (4.3.3) i18n (0.6.11) image_optim (0.9.1) exifr (~> 1.1.3) @@ -196,7 +196,7 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) - oj (2.10.2) + oj (2.11.1) omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) @@ -273,8 +273,8 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.13.0) - rake (10.4.0) - rake-compiler (0.9.3) + rake (10.4.2) + rake-compiler (0.9.4) rake rb-fsevent (0.9.4) rb-inotify (0.9.5) @@ -284,7 +284,7 @@ GEM msgpack (>= 0.4.3) trollop (>= 1.16.2) redcarpet (3.1.2) - redis (3.1.0) + redis (3.2.0) redis-namespace (1.5.1) redis (~> 3.0, >= 3.0.4) ref (1.0.5) diff --git a/config/initializers/06-mini_profiler.rb b/config/initializers/06-mini_profiler.rb index 11dc894fbf..89254a7d2e 100644 --- a/config/initializers/06-mini_profiler.rb +++ b/config/initializers/06-mini_profiler.rb @@ -2,7 +2,10 @@ if Rails.configuration.respond_to?(:load_mini_profiler) && Rails.configuration.load_mini_profiler require 'rack-mini-profiler' require 'flamegraph' - require 'memory_profiler' if RUBY_VERSION >= "2.1.0" + + # TODO support Ruby 2.2 once bundler fixes itself + require 'memory_profiler' if RUBY_VERSION >= "2.1.0" && RUBY_VERSION < "2.2.0" + # initialization is skipped so trigger it Rack::MiniProfilerRails.initialize!(Rails.application) end diff --git a/spec/models/discourse_single_sign_on_spec.rb b/spec/models/discourse_single_sign_on_spec.rb index 3f2d1c4646..a9668bbbcb 100644 --- a/spec/models/discourse_single_sign_on_spec.rb +++ b/spec/models/discourse_single_sign_on_spec.rb @@ -34,6 +34,20 @@ describe DiscourseSingleSignOn do parsed.custom_fields["b.b"].should == "B.b" end + it "can do round trip parsing correctly" do + sso = SingleSignOn.new + sso.sso_secret = "test" + sso.name = "sam saffron" + sso.username = "sam" + sso.email = "sam@sam.com" + + sso = SingleSignOn.parse(sso.payload, "test") + + sso.name.should == "sam saffron" + sso.username.should == "sam" + sso.email.should == "sam@sam.com" + end + it "can lookup or create user when name is blank" do # so we can create system messages Fabricate(:admin) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3510c09401..c58755c01c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -110,8 +110,11 @@ Spork.prefork do end def freeze_time(now=Time.now) - DateTime.stubs(:now).returns(DateTime.parse(now.to_s)) - Time.stubs(:now).returns(Time.parse(now.to_s)) + datetime = DateTime.parse(now.to_s) + time = Time.parse(now.to_s) + + DateTime.stubs(:now).returns(datetime) + Time.stubs(:now).returns(time) end def file_from_fixtures(filename) From a6fbf7d86b9d4d8435846206792b14f9b654cc8f Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 29 Dec 2014 16:27:41 +1100 Subject: [PATCH 362/991] PERF: improve rendering speed of topic header This also ensures we have a clean point to override rendering of topic list header in case we decide to add/remove columns, you no longer need to override the entire discovery template, only the header and item templates. --- .../components/sortable-heading.js.es6 | 27 -------- .../components/topic-list-header.js.es6 | 67 +++++++++++++++++++ .../templates/components/sortable-heading.hbs | 2 - .../discourse/templates/discovery/topics.hbs | 26 +------ 4 files changed, 68 insertions(+), 54 deletions(-) delete mode 100644 app/assets/javascripts/discourse/components/sortable-heading.js.es6 create mode 100644 app/assets/javascripts/discourse/components/topic-list-header.js.es6 delete mode 100644 app/assets/javascripts/discourse/templates/components/sortable-heading.hbs diff --git a/app/assets/javascripts/discourse/components/sortable-heading.js.es6 b/app/assets/javascripts/discourse/components/sortable-heading.js.es6 deleted file mode 100644 index c1f469ecc9..0000000000 --- a/app/assets/javascripts/discourse/components/sortable-heading.js.es6 +++ /dev/null @@ -1,27 +0,0 @@ -/** - Renders a heading for a table with optional sorting controls. - - @class SortableHeadingComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ -export default Ember.Component.extend({ - tagName: 'th', - classNameBindings: ['number:num', 'sortBy', 'iconSortClass:sorting', 'sortable:sortable'], - attributeBindings: ['colspan'], - - sortable: function() { - return this.get('sortBy'); - }.property('sortBy'), - - iconSortClass: function() { - if (this.get('sortable') && this.get('sortBy') === this.get('order')) { - return this.get('ascending') ? 'fa fa-chevron-up' : 'fa fa-chevron-down'; - } - }.property('sortable', 'order', 'ascending'), - - click: function() { - this.sendAction('action', this.get('sortBy')); - } -}); diff --git a/app/assets/javascripts/discourse/components/topic-list-header.js.es6 b/app/assets/javascripts/discourse/components/topic-list-header.js.es6 new file mode 100644 index 0000000000..eb85cf9604 --- /dev/null +++ b/app/assets/javascripts/discourse/components/topic-list-header.js.es6 @@ -0,0 +1,67 @@ +import StringBuffer from 'discourse/mixins/string-buffer'; + +export default Ember.Component.extend(StringBuffer, { + tagName: 'tr', + + rerenderTriggers: ['order', 'ascending'], + + click: function(e) { + var target = $(e.target); + + if(target.parents().andSelf().hasClass('bulk-select')){ + this.sendAction('toggleBulkSelect'); + } else { + var th = target.closest('th.sortable'); + if(th.length > 0) { + this.sendAction('changeSort', th.data('sort-order')); + } + } + + }, + + renderColumn: function(buffer, options){ + var className = options.sortable ? "sortable " : ""; + className += options.order || ""; + + var sortIcon = ""; + if(this.get("order") === options.order){ + className += " sorting"; + sortIcon = " "; + } + + if(options.number){ + className += " num"; + } + + buffer.push("" + I18n.t(options.name) + sortIcon + ""); + }, + + renderString: function(buffer){ + var self = this; + + if(this.get('currentUser')){ + buffer.push(""); + if(this.get('canBulkSelect')){ + var title = I18n.t('topics.bulk.toggle'); + buffer.push(""); + } + buffer.push(""); + } + + var column = function(options){ + self.renderColumn(buffer, options); + }; + + column({name: 'topic.title', sortable: false, order: 'default'}); + + if(!this.get('hideCategory')) { + column({name: 'category_title', sortable: true, order: 'category'}); + } + + column({sortable: false, order: 'posters', name: 'users'}); + column({sortable: true, order: 'posts', name: 'posts', number: true}); + column({sortable: true, order: 'views', name: 'views', number: true}); + column({sortable: true, order: 'activity', name: 'activity', number: true}); + } + +}); diff --git a/app/assets/javascripts/discourse/templates/components/sortable-heading.hbs b/app/assets/javascripts/discourse/templates/components/sortable-heading.hbs deleted file mode 100644 index 36824ad2b1..0000000000 --- a/app/assets/javascripts/discourse/templates/components/sortable-heading.hbs +++ /dev/null @@ -1,2 +0,0 @@ -{{yield}} - diff --git a/app/assets/javascripts/discourse/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/discovery/topics.hbs index 23878a1a54..234edfb772 100644 --- a/app/assets/javascripts/discourse/templates/discovery/topics.hbs +++ b/app/assets/javascripts/discourse/templates/discovery/topics.hbs @@ -37,31 +37,7 @@ {{#if hasTopics}} - - {{#if currentUser}} - - {{/if}} - {{#sortable-heading class="default"}} {{i18n 'topic.title'}} {{/sortable-heading}} - {{#unless controller.hideCategory}} - {{#sortable-heading sortBy="category" action="changeSort" order=order ascending=ascending}} - {{i18n 'category_title'}} - {{/sortable-heading}} - {{/unless}} - {{#sortable-heading class="posters"}} {{i18n 'users'}} {{/sortable-heading}} - {{#sortable-heading sortBy="posts" number=true action="changeSort" order=order ascending=ascending}} - {{i18n 'posts'}} - {{/sortable-heading}} - {{#sortable-heading sortBy="views" number=true action="changeSort" order=order ascending=ascending}} - {{i18n 'views'}} - {{/sortable-heading}} - {{#sortable-heading sortBy="activity" number=true action="changeSort" order=order ascending=ascending}} - {{i18n 'activity'}} - {{/sortable-heading}} - + {{topic-list-header currentUser=currentUser canBulkSelect=canBulkSelect changeSort="changeSort" toggleBulkSelect="toggleBulkSelect" hideCategory=hideCategory order=order ascending=ascending}} {{each content in topics itemController="topic-list-item" itemView="topic-list-item"}} From b48f04e772504e6d97c45b12766584a35f4ae73f Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 29 Dec 2014 16:32:31 +1100 Subject: [PATCH 363/991] possible regression fix --- .../javascripts/discourse/components/topic-list-header.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/components/topic-list-header.js.es6 b/app/assets/javascripts/discourse/components/topic-list-header.js.es6 index eb85cf9604..2be92c0b6b 100644 --- a/app/assets/javascripts/discourse/components/topic-list-header.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-list-header.js.es6 @@ -24,7 +24,7 @@ export default Ember.Component.extend(StringBuffer, { className += options.order || ""; var sortIcon = ""; - if(this.get("order") === options.order){ + if(this.get("order") === options.order && options.sortable){ className += " sorting"; sortIcon = " "; } From bfbc49ef6fdfbf1ad93c8fec203d8d4535f03428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 29 Dec 2014 11:50:36 +0100 Subject: [PATCH 364/991] FIX: log only 1 'show email' record --- app/services/staff_action_logger.rb | 18 +++++------------- .../controllers/admin/users_controller_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb index 0c9e6c115c..fc6bfdf100 100644 --- a/app/services/staff_action_logger.rb +++ b/app/services/staff_action_logger.rb @@ -143,20 +143,12 @@ class StaffActionLogger })) end - def log_show_emails(users) + def log_show_emails(users, opts={}) return if users.blank? - - values = [] - - users.each do |user| - values << "(#{@admin.id}, #{UserHistory.actions[:check_email]}, #{user.id}, current_timestamp, current_timestamp)" - end - - # bulk insert - UserHistory.exec_sql <<-SQL - INSERT INTO user_histories (acting_user_id, action, target_user_id, created_at, updated_at) - VALUES #{values.join(",")} - SQL + UserHistory.create(params(opts).merge({ + action: UserHistory.actions[:check_email], + details: users.map { |u| "[#{u.id}] #{u.username}"}.join("\n") + })) end def log_impersonate(user, opts={}) diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 954bd80318..31271da27c 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -33,13 +33,13 @@ describe Admin::UsersController do end end - it "logs an enty for all email shown" do + it "logs only 1 enty" do UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == 0 xhr :get, :index, show_emails: "true" data = ::JSON.parse(response.body) - UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == data.length + UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == 1 end end From 489feac121928dba24601bb4c20b3136674ebba3 Mon Sep 17 00:00:00 2001 From: Erick Guan Date: Mon, 29 Dec 2014 19:15:58 +0800 Subject: [PATCH 365/991] FIX: user card can't expand when the username is number --- app/assets/javascripts/discourse/controllers/user-card.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index a3b71cfb18..ce1c665f86 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -33,7 +33,7 @@ export default ObjectController.extend({ show: function(username, target) { // XSS protection (should be encapsulated) - username = username.replace(/[^A-Za-z0-9_]/g, ""); + username = username.toString().replace(/[^A-Za-z0-9_]/g, ""); var url = "/users/" + username; // Don't show on mobile From fa8ba67523a8e72dc906abbc8a8a65ad9ab8c4fc Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 29 Dec 2014 19:06:33 +0530 Subject: [PATCH 366/991] Update Translations --- config/locales/client.ar.yml | 80 +- config/locales/client.cs.yml | 8 - config/locales/client.da.yml | 5 - config/locales/client.de.yml | 75 +- config/locales/client.es.yml | 15 +- config/locales/client.fi.yml | 9 - config/locales/client.fr.yml | 15 +- config/locales/client.he.yml | 9 - config/locales/client.id.yml | 2 - config/locales/client.it.yml | 11 +- config/locales/client.ja.yml | 7 - config/locales/client.ko.yml | 9 - config/locales/client.nb_NO.yml | 17 +- config/locales/client.nl.yml | 128 +- config/locales/client.pl_PL.yml | 19 +- config/locales/client.pt.yml | 1467 +++++++++-------- config/locales/client.pt_BR.yml | 9 - config/locales/client.ru.yml | 9 - config/locales/client.sq.yml | 8 - config/locales/client.sv.yml | 56 +- config/locales/client.tr_TR.yml | 283 ++-- config/locales/client.uk.yml | 3 - config/locales/client.zh_CN.yml | 395 +++-- config/locales/client.zh_TW.yml | 101 +- config/locales/server.ar.yml | 10 +- config/locales/server.cs.yml | 1 - config/locales/server.ja.yml | 1 - config/locales/server.nl.yml | 5 - config/locales/server.pt.yml | 1105 ++++++++++++- config/locales/server.pt_BR.yml | 6 - config/locales/server.sv.yml | 14 +- plugins/poll/config/locales/server.pt.yml | 4 +- plugins/poll/config/locales/server.zh_CN.yml | 6 +- public/403.pt.html | 4 +- public/422.pt.html | 6 +- public/500.pt.html | 6 +- public/503.pt.html | 4 +- .../discourse_imgur/locale/server.zh_CN.yml | 4 +- 38 files changed, 2548 insertions(+), 1368 deletions(-) diff --git a/config/locales/client.ar.yml b/config/locales/client.ar.yml index b3f66c129c..c5e2fbdc8f 100644 --- a/config/locales/client.ar.yml +++ b/config/locales/client.ar.yml @@ -57,7 +57,6 @@ ar: other: "%{count} يوم مضى" share: topic: 'ضع رابطاً في هذا الموضوع.' - post: 'ضع رابطاً في المشاركة #%{postNumber}' close: 'اغلق' twitter: 'شارك هذا الرابط عن طريق تويتر' facebook: 'شارك هذا الرابط عن طريق فيس بوك' @@ -215,7 +214,6 @@ ar: profile: "الملف الشخصي" mute: "كتم" edit: "تعديل التفضيلات" - download_archive: " تحميل أرشيف مواضيعي" new_private_message: "انشاء رسالة خاصة" private_message: "رسائل خاصة" private_messages: "الرسائل" @@ -247,10 +245,21 @@ ar: tracked_categories: "Tracked" tracked_categories_instructions: "تلقائيا ستقوم بمتابعة جميع المواضيع في هذا التصنيف , وسيتم اشعارك لجميع المشاركات والمواضيع وكذلك عداد الغيرمقروء والمشاركات الجديدة" muted_categories: "كتم" + muted_categories_instructions: "تلقائيا ستقوم بمتابعة جميع المواضيع في هذا التصنيف , وسيتم اشعارك لجميع المشاركات والمواضيع وكذلك عداد الغيرمقروء والمشاركات الجديدة" + delete_account: "حذف الحساب" + delete_account_confirm: "هل انت متاكد من انك تريد حذف هذا المستخدم؟ هذا الإجراء دائماً!" + deleted_yourself: "حسابك تم حذفه بنجاح" + unread_message_count: "الرسائل" + admin_delete: "حذف" + staff_counters: + deleted_posts: "حذف جميع المشاركات" + messages: + all: "الكل" change_password: success: "(تم ارسال الرسالة)" in_progress: "(يتم ارسال رسالة)" error: "(خطأ)" + set_password: " إعادة تعين الرمز السري" change_about: title: "تعديل معلومات عنّي" change_username: @@ -264,6 +273,15 @@ ar: taken: "نأسف، البريد الالكتروني غير متاح." error: "حدث خطأ عند تغيير البريد الالكتروني. ربما يكون هذا البريد مستخدم من قبل؟" success: "تم ارسال رسالة الى البريد الكتروني. يرجى اتباع تعليمات التأكيد." + change_avatar: + title: "غير صورتك الشخصية" + gravatar: "Gravatar, based on" + refresh_gravatar_title: "حدّث Gravatar" + letter_based: "الصورة الافتراضية " + uploaded_avatar: "تخصيص صورة" + uploaded_avatar_empty: "اضافة صورة " + upload_title: "رفع صورتك " + upload_picture: "رفع الصورة" email: title: "بريد الكتروني" name: @@ -294,29 +312,46 @@ ar: new_topic_duration: label: " \nإعتبر الوضوع جديد حين" not_viewed: "لم تقم بالاطلاع عليهم حتى الان." + last_here: "تم انشائها منذ آخر زيارة لك" + auto_track_topics: "تلقائيا متابعة المواضيع التي دخلتها " auto_track_options: never: "ابداً" always: "دائما" invited: + search: "نوع البحث عن الدعوات" title: "دعوة" user: "المستخدمين المدعويين" none: ".لم تقم بدعوة أي شخص إلى هنا حتى الأن" + truncated: "اظهار اوائل {{count}} المدعويين" redeemed: "دعوات مستخدمة" redeemed_at: "مستخدمة" pending: "دعوات قيد الإنتضار" topics_entered: " مواضيع شوهدت" posts_read_count: "مشاركات شوهدت" + expired: "الدعوة انتهت صلاحيتها " rescind: "حذف" rescinded: "الدعوة حذفت" + reinvite: "اعادة ارسال الدعوة" + reinvited: "اعادة ارسال الدعوة" time_read: "وقت القراءة" days_visited: "أيام الزيارة" account_age_days: "عمر العضوية بالأيام" + create: "ارسال دعوة" + bulk_invite: + none: "لم تقم بدعوة اي احد حتى الان. تستطيع ارسال دعوة , أو ارسال عدة دعوات عن طريقuploading a bulk invite file." + text: "الدعوة من ملف" + uploading: "جاري الرقع..." + error: "كان هناك مشكلة في رفع الملف '{{filename}}': {{message}}" password: title: "كلمة المرور" too_short: "كلمة المرور قصيرة جداً" + common: "كلمة المرور هذه شائعة " ok: "كلمة المرور هذة تعتبر جيدة." + instructions: "على الاقل %{count} حرف" ip_address: title: "أخر عنوان أيبي" + registration_ip_address: + title: "ايبي مسجل" avatar: title: "صورة العرض" title: @@ -329,7 +364,26 @@ ar: private_message: "رسالة خاصة" the_topic: "موضوع جديد" loading: "يتم التحميل..." + errors: + prev_page: "محاولة تحميل" + reasons: + network: "خطأ في الشبكة" + server: "خطأ في السيرفر" + forbidden: "غير مصرح" + unknown: "خطأ" + desc: + network: "الرجاء التحقق من اتصالك" + network_fixed: "يبدوا أنه رجع" + server: "رقم الخطأ: {{status}}" + forbidden: "ليس لديك الصلاحية" + unknown: "حدث خطأ ما" + buttons: + back: "الرجوع" + again: "أعد المحاولة" + fixed: "تحميل" close: "اغلاق" + assets_changed_confirm: "هناك تغيير في الصفحة, هل تريد التحديث للحصول على أحدث نسخة ؟" + logout: "تم تسجيل خروجك" learn_more: "تعلم المزيد..." year: 'سنة' year_desc: 'المواضيع المكتوبة خلال 365 يوم الماضية' @@ -337,25 +391,45 @@ ar: month_desc: 'المواضيع المكتوبة خلال 30 يوم الماضية' week: 'أسبوع' week_desc: ' المواضيع التي كتبت خلال 7 أيام الماضية' + day: 'يوم' first_post: الموضوع الأول mute: كتم unmute: إلغاء الكتم last_post: أخر مشاركة + last_post_lowercase: أخر مشاركة + summary: + enabled_description: "أنت تنظر الى ملخص لهذا الموضوع , مشاركات مثيرة للإهتمام بحسب رأي المجتمع" + description: "هناك {{count}} ردود" + description_time: "هناك {{count}} ردود مع تقدير وقت القراءة {{readingTime}} دقائق." + enable: 'لخّص هذا الموضوع' + disable: 'عرض جميع المشاركات' + deleted_filter: + enabled_description: "هذا الموضوع يحوي على مشاركات محذوفة تم اخفائها " + disabled_description: "المشاركات المحذوفة في هذا الموضوع ممكن مشاهدتها " + enable: "إخفاء المشاركات المحذوفة" + disable: "عرض المشاركات المحذوفة" private_message_info: title: " رسالة خاصة" invite: " إستدعي الأخرين" + remove_allowed_user: "هل تريد حقا ازالة {{name}} من الرسائل الخاصة ?" email: 'البريد الإلكتروني' username: 'إسم المستخدم' last_seen: 'شوهدت' created: 'مكتوبة' + created_lowercase: 'منشأة' trust_level: 'مستوى التقة' + search_hint: 'اسم مستخدم, بريد الكتروني او ايبي' create_account: title: "إنشاء حساب جديد" + failed: "حدث خطأ ما, ربما بريدك الالكتروني مسجل مسبقا, جرب رابط نسيان كلمة المرور " forgot_password: title: "نسيت الرمز السري" - action: "لقد نسيت رمزي السري" + action: "نسيت كلمة المرور" + invite: "ادخل اسم مستخدمك او بريدك الالكتروني وسنقوم بإرسال اعاذة ضبط كلمة المرور على بريدك" reset: " إعادة تعين الرمز السري" + complete_username: "اذا كان اسم المسنخدم موجود %{username}, سيتم ارسال رسالة لبريدك لإعادة ضبط كلمة المرور " login: + title: "تسجيل دخول" username: "المستخدم" password: "الرمز السري" email_placeholder: "البريد الإلكتروني أو إسم المستخدم" diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index 136149ca75..f2120abcbe 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -100,7 +100,6 @@ cs: other: "před %{count} dny" share: topic: 'sdílet odkaz na toto téma' - post: 'sdílet odkaz na příspěvek #%{postNumber}' close: 'zavřít' twitter: 'sdílet odkaz na Twitteru' facebook: 'sdílet odkaz na Facebooku' @@ -280,7 +279,6 @@ cs: profile: "Profil" mute: "Ignorovat" edit: "Upravit nastavení" - download_archive: "stáhnout archiv mých příspěvků" private_message: "Soukromé zprávy" private_messages: "Zprávy" activity_stream: "Aktivita" @@ -456,7 +454,6 @@ cs: none: "Zatím jste nikoho nepozval. Můžete poslat individuální pozvánku nebo pozvat skupinu lidí naráz pomocí nahrání souboru." text: "Hromadné pozvání s pomocí souboru" uploading: "Nahrávám..." - success: "Soubor byl úspěšně nahrán. Budete informováni o dalším postupu." error: "Nastala chyba při nahrávání '{{filename}}': {{message}}" password: title: "Heslo" @@ -464,7 +461,6 @@ cs: common: "Toto heslo je používané moc často." ok: "Vaše heslo je v pořádku." instructions: "Alespo %{count} znaků." - associated_accounts: "Přidružené účty" ip_address: title: "Poslední IP adresa" registration_ip_address: @@ -1943,7 +1939,6 @@ cs: back: 'u Back' up_down: 'k/j Move selection ↑ ↓' open: 'o or Enter Open selected topic' - next_prev: 'shift j/shift k Následující/předchozí výběr' application: title: 'Application' create: 'c Create a new topic' @@ -1958,11 +1953,8 @@ cs: actions: title: 'Actions' star: 'f Star topic' - pin_unpin_topic: 'shift p Připnout/Odepnout téma' - share_topic: 'shift s Sdílet téma' share_post: 's Share post' reply_as_new_topic: 't Odpovědět v propojeném tématu' - reply_topic: 'shift r Odpovědět na téma' reply_post: 'r Odpovědět na příspěvek' quote_post: 'q Citovat příspěvek' like: 'l Like post' diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index b8de4b6b14..1f4f3b7b2f 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -84,7 +84,6 @@ da: other: "%{count} dage siden" share: topic: 'del et link til dette emne' - post: 'del et link til dette indlæg' close: 'luk' twitter: 'del dette link på Twitter' facebook: 'del dette link på Facebook' @@ -251,7 +250,6 @@ da: profile: "Profil" mute: "Mute" edit: "Redigér indstillinger" - download_archive: "download arkiv med alle mine indlæg" private_message: "Private beskeder" private_messages: "Beskeder" activity_stream: "Aktivitet" @@ -407,7 +405,6 @@ da: bulk_invite: none: "Du har ikke inviteret nogen her endnu. Du kan sende individuelle invitationer eller invitere en masse mennesker på én gang ved at uploade en samlet liste over invitationer." text: "Masse invitering fra en fil" - success: "Filen er uploadet uden problemer, du vil snart blive notificeret om hvordan det går." error: "Der var en fejl ved upload af filen '{{filename}}': {{message}}" password: title: "Adgangskode" @@ -1888,9 +1885,7 @@ da: actions: title: 'Handlinger' star: 'f Markér emne som favorit' - share_topic: 'shift s Del emne' share_post: 's Del opslag' - reply_topic: 'shift r Svar på emnet' reply_post: 'r Svar på kommentaren' quote_post: 'q Citer emne' like: 'l Like indlæg' diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index 6bb16c85df..2e6a523b37 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -59,8 +59,8 @@ de: almost_x_years: one: "1a" other: "%{count}a" - date_month: "DD. MMM" - date_year: "MMM 'YY" + date_month: "DD. MMM" + date_year: "MMM 'YY" medium: x_minutes: one: "1 Minute" @@ -84,7 +84,6 @@ de: other: "vor %{count} Tagen" share: topic: 'Teile einen Link zu diesem Thema' - post: 'Teile einen Link zu Beitrag Nr. %{postNumber}' close: 'Schließen' twitter: 'diesen Link auf Twitter teilen' facebook: 'diesen Link auf Facebook teilen' @@ -92,10 +91,10 @@ de: email: 'diesen Link per E-Mail senden' topic_admin_menu: "Thema administrieren" edit: 'Titel und Kategorie dieses Themas ändern' - not_implemented: "Tut uns leid, diese Funktion wurde noch nicht implementiert!" + not_implemented: "Entschuldige, diese Funktion wurde noch nicht implementiert!" no_value: "Nein" yes_value: "Ja" - generic_error: "Entschuldigung. Ein Fehler ist aufgetreten." + generic_error: "Entschuldige, es ist ein Fehler aufgetreten." generic_error_with_reason: "Ein Fehler ist aufgetreten: %{error}" sign_up: "Registrieren" log_in: "Anmelden" @@ -144,11 +143,11 @@ de: post_count: "Beiträge" user_count: "Benutzer" bookmarks: - not_logged_in: "Entschuldigung, du musst angemeldet sein, um ein Lesezeichen zu setzen." + not_logged_in: "Entschuldige, du musst angemeldet sein, um ein Lesezeichen setzen zu können." created: "du hast ein Lesezeichen zu diesem Beitrag hinzugefügt" not_bookmarked: "Du hast diesen Beitrag gelesen. Klicke, um ein Lesezeichen zu setzen." last_read: "Das ist der letzte Beitrag, den du gelesen hast. Klicke, um ein Lesezeichen zu setzen." - remove: "Entferne Lesezeichen" + remove: "Lesezeichen entfernen" topic_count_latest: one: "{{count}} neues oder geändertes Thema." other: "{{count}} neue oder geänderte Themen." @@ -259,7 +258,6 @@ de: profile: "Profil" mute: "Stummschalten" edit: "Einstellungen bearbeiten" - download_archive: "Ein Archiv meiner Beiträge herunterladen" new_private_message: "Neue private Nachricht" private_message: "Private Nachricht" private_messages: "Nachrichten" @@ -324,7 +322,7 @@ de: invalid: "Der Benutzernamen ist nicht zulässig. Er darf nur Zahlen und Buchstaben enthalten." change_email: title: "E-Mail-Adresse ändern" - taken: "Es tut uns leid. Diese E-Mail-Adresse ist nicht verfügbar." + taken: "Entschuldige, diese E-Mail-Adresse ist nicht verfügbar." error: "Beim Ändern der E-Mail-Adresse ist ein Fehler aufgetreten. Möglicherweise wird diese Adresse schon benutzt." success: "Wir haben eine E-Mail an die angegebene E-Mail-Adresse gesendet. Folge zur Bestätigung der Adresse bitte den darin enthaltenen Anweisungen." change_avatar: @@ -441,7 +439,6 @@ de: none: "Du hast noch niemanden hierher eingeladen. Du kannst individuelle Einladungen verschicken oder eine Masseneinladung an eine Gruppe von Leuten verschicken indem du eine Datei für Masseneinladung hochlädst." text: "Masseneinladung aus Datei" uploading: "Wird hochgeladen..." - success: "Datei erfolgreich hochgeladen. Du wirst in Kürze über den Fortschritt benachrichtigt." error: "Beim Hochladen der Datei '{{filename}}' ist ein Fehler aufgetreten: {{message}}" password: title: "Passwort" @@ -449,7 +446,6 @@ de: common: "Das Passwort wird zu häufig verwendet." ok: "Dein Passwort sieht in Ordnung aus." instructions: "Mindestens %{count} Zeichen." - associated_accounts: "Zugehörige Konten" ip_address: title: "Letzte IP-Adresse" registration_ip_address: @@ -642,14 +638,14 @@ de: toggler: "Eingabebereich aus- oder einblenden" admin_options_title: "Optionale Mitarbeiter-Einstellungen für dieses Thema" auto_close: - label: "Thema automatisch schließen:" + label: "Zeitpunkt der automatischen Schließung:" error: "Bitte gib einen gültigen Wert ein." - based_on_last_post: "Das Thema nicht schließen, wenn der letzte Beitrag nicht mindestens so alt ist." + based_on_last_post: "Das Thema erst schließen, wenn der letzte Beitrag mindestens so alt ist." all: - examples: 'Gib die Anzahl von Stunden (24), eine absolute Uhrzeit (17:30) oder einen Zeitstempel (2013-11-22 14:00) ein.' + examples: 'Gib die Anzahl der Stunden (24), eine Uhrzeit (17:30) oder einen Zeitstempel (2013-11-22 14:00) ein.' limited: units: "(# Stunden)" - examples: 'Geben Sie die Anzahl der Stunden ein (24).' + examples: 'Gib die Anzahl der Stunden ein (24).' notifications: title: "Benachrichtigung über Erwähnungen mit @Name, Antworten auf deine Beiträge und Themen, private Nachrichten etc." none: "Du hast momentan keine Benachrichtigungen." @@ -795,7 +791,7 @@ de: jump_reply_down: zur nachfolgenden Antwort springen deleted: "Das Thema wurde gelöscht" auto_close_notice: "Dieses Thema wird %{timeLeft} automatisch geschlossen." - auto_close_notice_based_on_last_post: "Dieses Thema wird nach %{duration} der letzten Antwort geschlossen." + auto_close_notice_based_on_last_post: "Dieses Thema wird %{duration} nach der letzten Antwort geschlossen." auto_close_title: 'Automatisches Schließen' auto_close_save: "Speichern" auto_close_remove: "Dieses Thema nicht automatisch schließen" @@ -826,7 +822,7 @@ de: '0': 'Du ignorierst alle Benachrichtigungen dieses Themas.' watching_pm: title: "Beobachten" - description: "Du wirst über jeden neuen Beitrag in dieser privaten Unterhaltung benachrichtigt. Die Anzahl der ungelesenen und neuen Beiträge wird auch neben dem Thema erscheinen." + description: "Du wirst über jeden neuen Beitrag in dieser privaten Unterhaltung benachrichtigt. Die Anzahl der ungelesenen und neuen Beiträge wird auch neben der Nachricht erscheinen." watching: title: "Beobachten" description: "Du wirst über jeden neuen Beitrag in diesem Thema benachrichtigt. Die Anzahl der ungelesenen und neuen Beiträge wird auch neben dem Thema erscheinen." @@ -876,7 +872,7 @@ de: help: 'teile einen Link zu diesem Thema' flag_topic: title: 'Melden' - help: 'Melde dieses Thema vertraulich zur Kenntnisnahme oder versende eine private Benachrichtigung darüber.' + help: 'Dieses Thema den Moderatoren melden oder eine private Nachricht senden.' success_message: 'Du hast dieses Thema erfolgreich gemeldet.' inviting: "Einladungen werden gesendet..." automatically_add_to_groups_optional: "Diese Einladung beinhaltet auch Zugang zu den folgenden Gruppen: (optional, nur Admin)" @@ -892,7 +888,7 @@ de: invite_reply: title: 'Einladen' action: 'Einladung senden' - help: 'Sendet Freunden eine Einladung, so dass mit einem Klick auf dieses Thema antworten können.' + help: 'Freunden eine Einladung senden, sodass sie auf das Thema mit einem Klick antworten können' to_topic: "Wir senden deinem Freund eine kurze E-Mail, die es ihm ermöglicht, direkt auf dieses Thema zu antworten. Es ist keine Anmeldung erforderlich." to_forum: "Wir senden deinem Freund eine kurze E-Mail, die es ihm ermöglicht, dem Forum sofort beizutreten. Es ist keine Anmeldung erforderlich." email_placeholder: 'name@example.com' @@ -971,12 +967,12 @@ de: edit: "Entschuldige, es gab einen Fehler beim Bearbeiten des Beitrags. Bitte versuche es noch einmal." upload: "Entschuldige, es gab einen Fehler beim Hochladen der Datei. Bitte versuche es noch einmal." attachment_too_large: "Entschuldige, die Datei, die du hochladen wolltest, ist zu groß (Maximalgröße {{max_size_kb}} KB)." - file_too_large: "Achtung, die Datei, die du hochladen wolltest, ist zu groß (Maximalgröße {{max_size_kb}}kb)." + file_too_large: "Entschuldige, die Datei, die du hochladen wolltest, ist zu groß (Maximalgröße {{max_size_kb}} KB)." too_many_uploads: "Entschuldige, du darfst immer nur eine Datei hochladen." upload_not_authorized: "Entschuldige, die Datei, die du hochladen wolltest, ist nicht erlaubt (erlaubte Endungen: {{authorized_extensions}})." image_upload_not_allowed_for_new_user: "Entschuldige, neue Benutzer dürfen keine Bilder hochladen." attachment_upload_not_allowed_for_new_user: "Entschuldige, neue Benutzer dürfen keine Dateien hochladen." - attachment_download_requires_login: "Du musst angemeldet sein, um Dateien herunterladen zu können." + attachment_download_requires_login: "Entschuldige, du musst angemeldet sein, um Dateien herunterladen zu können." abandon: confirm: "Möchtest du deinen Beitrag wirklich verwerfen?" no_value: "Nein, beibehalten" @@ -992,8 +988,8 @@ de: has_liked: "dir gefällt dieser Beitrag" undo_like: "gefällt mir nicht mehr" edit: "diesen Beitrag bearbeiten" - edit_anonymous: "Du musst angemeldet sein, um diesen Beitrag zu bearbeiten." - flag: "Diesen Beitrag den Moderatoren melden" + edit_anonymous: "Entschuldige, du musst angemeldet sein, um diesen Beitrag zu bearbeiten." + flag: "Diesen Beitrag den Moderatoren melden oder eine private Nachricht senden." delete: "diesen Beitrag löschen" undelete: "diesen Beitrag wiederherstellen" share: "Link zu diesem Beitrag teilen" @@ -1203,7 +1199,7 @@ de: hidden_email_address: "(versteckt)" submit_tooltip: "Private Meldung abschicken" take_action_tooltip: "Den Meldungsschwellenwert sofort erreichen, anstatt auf weitere Meldungen aus der Community zu warten." - cant: "Entschuldige, Du kannst diesen Beitrag augenblicklich nicht melden." + cant: "Entschuldige, du kannst diesen Beitrag derzeit nicht melden." formatted_name: off_topic: "Es ist am Thema vorbei" inappropriate: "Es ist unangemessen" @@ -1525,9 +1521,14 @@ de: title: "Die Datenbank auf den letzten funktionierenden Zustand zurücksetzen" confirm: "Möchtest du wirklich die Datenbank auf den letzten funktionierenden Stand zurücksetzen?" export_csv: - success: "Der Export wurde gestartet. Du wirst in Kürze über den Fortschritt benachrichtigt." failed: "Der Export ist fehlgeschlagen. Bitte überprüfe die Logs." button_text: "Exportieren" + button_title: + user: "Vollständige Benutzerliste im CSV-Format exportieren." + staff_action: "Vollständiges Moderations-Protokoll im CSV-Format exportieren." + screened_email: "Vollständige Liste der überprüften E-Mail-Adressen im CSV-Format exportieren." + screened_ip: "Vollständige Liste der überprüften IP-Adressen im CSV-Format exportieren." + screened_url: "Vollständige Liste der überprüften URLs im CSV-Format exportieren." customize: title: "Anpassen" long_title: "Website-Anpassungen" @@ -1585,7 +1586,7 @@ de: description: "Text und Symbole im Kopfbereich der Website." highlight: name: 'hervorheben' - description: 'Die Hintergrundfarbe der hervorgehobenen Elemente auf der Seite, so wie Beiträge und Themen.' + description: 'Die Hintergrundfarbe von hervorgehobenen Elementen, wie etwa Beiträge und Themen.' danger: name: 'Gefahr' description: 'Hervorhebungsfarbe für Aktionen wie Löschen von Beiträgen und Themen.' @@ -1697,7 +1698,7 @@ de: delete_confirm: "Möchtest du wirklich die Regel für %{ip_address} entfernen?" roll_up_confirm: "Möchtest du wirklich die häufig blockierten IP-Adressen zu Subnetzen zusammenfassen?" rolled_up_some_subnets: "Die geblockten IP-Adressen wurden erfolgreich zu diesen Subnetzen zusammengefasst: %{subnets}" - rolled_up_no_subnet: "Es konnte nichts zusammengefasst werden." + rolled_up_no_subnet: "Es gab nichts zum Zusammenfassen." actions: block: "Blockieren" do_nothing: "Erlauben" @@ -1718,8 +1719,8 @@ de: title: 'Benutzer' create: 'Administrator hinzufügen' last_emailed: "Letzte E-Mail" - not_found: "Entschuldige, dieser Benutzername existiert im System nicht." - id_not_found: "Entschuldigung, diese Benutzerkennung ist in unserem System nicht vorhanden." + not_found: "Entschuldige, dieser Benutzername ist im System nicht vorhanden." + id_not_found: "Entschuldige, diese Benutzerkennung ist im System nicht vorhanden." active: "Aktiv" show_emails: "E-Mails anzeigen" nav: @@ -1729,7 +1730,7 @@ de: staff: 'Mitarbeiter' suspended: 'Gesperrt' blocked: 'Blockiert' - suspect: 'Verdächtiger' + suspect: 'Verdächtig' approved: "Genehmigt?" approved_selected: one: "Benutzer genehmigen" @@ -1844,8 +1845,8 @@ de: suspend_modal_title: "Benutzer sperren" trust_level_2_users: "Benutzer mit Vertrauensstufe 2" trust_level_3_requirements: "Anforderungen für Vertrauensstufe 3" - trust_level_locked_tip: "Vertrauensstufe ist nicht gesperrt. Das System wird den Benutzer nicht befördern oder degradieren. " - trust_level_unlocked_tip: "Vertrauensstufe ist nicht gesperrt. Das System kann den Benutzer befördern oder degradieren. " + trust_level_locked_tip: "Vertrauensstufe ist nicht gesperrt. Das System wird den Benutzer nicht befördern oder zurückstufen. " + trust_level_unlocked_tip: "Vertrauensstufe ist nicht gesperrt. Das System kann den Benutzer befördern oder zurückstufen. " lock_trust_level: "Vertrauensstufe sperren" unlock_trust_level: "Vertrauensstufe entsperren" tl3_requirements: @@ -1869,10 +1870,10 @@ de: qualifies: "Erfüllt die Anforderungen für Vertrauensstufe 3." does_not_qualify: "Erfüllt nicht die Anforderungen für Vertrauensstufe 3." will_be_promoted: "Wird bald befördert werden." - will_be_demoted: "Wird bald degradiert werden." - on_grace_period: "Wird nicht degradiert. Derzeit gilt die Schonfrist der letzten Beförderung." + will_be_demoted: "Wird bald zurückgestuft werden." + on_grace_period: "Wird nicht zurückgestuft. Derzeit gilt die Schonfrist der letzten Beförderung." locked_will_not_be_promoted: "Vertrauensstufe ist gesperrt. Wird nie befördert werden." - locked_will_not_be_demoted: "Vertrauensstufe ist gesperrt. Wird nie degradiert werden." + locked_will_not_be_demoted: "Vertrauensstufe ist gesperrt. Wird nie zurückgestuft werden." sso: title: "Single Sign-on" external_id: "Externe ID" @@ -2018,7 +2019,6 @@ de: back: 'u Zurück' up_down: 'k/j Auswahl ↑ ↓ bewegen' open: 'o oder ↵ Eingabe Ausgewähltes Thema anzeigen' - next_prev: '⇧ Umsch + j / ⇧ Umsch + k Nächster/vorheriger Abschnitt' application: title: 'Anwendung' create: 'c Neues Thema erstellen' @@ -2033,11 +2033,8 @@ de: actions: title: 'Aktionen' star: 'f Thema favorisieren' - pin_unpin_topic: '⇧ Umsch + p Thema anheften/loslösen' - share_topic: '⇧ Umsch + s Thema teilen' share_post: 's Beitrag teilen' reply_as_new_topic: 't Mit verknüpftem Thema antworten' - reply_topic: '⇧ Umsch + r Auf Thema antworten' reply_post: 'r Auf Beitrag antworten' quote_post: 'q Beitrag zitieren' like: 'l Beitrag gefällt mir' diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index c43a1e6645..f9813d9da8 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -84,7 +84,6 @@ es: other: "hace %{count} días" share: topic: 'comparte un enlace a este tema' - post: 'comparte un enlace al post #%{postNumber}' close: 'cerrar' twitter: 'comparte este enlace en Twitter' facebook: 'comparte este enlace en Facebook' @@ -259,7 +258,6 @@ es: profile: "Perfil" mute: "Silenciar" edit: "Editar Preferencias" - download_archive: "descargar un archivo con mis publicaciones" new_private_message: "Nuevo Mensaje Privado" private_message: "Mensaje Privado" private_messages: "Mensajes" @@ -441,7 +439,6 @@ es: none: "No has invitado a nadie todavía. Puede enviar invitaciones individuales o invitar a un grupo de personas a la vez subiendo un archivo para invitaciones en masa." text: "Archivo de Invitación en Masa" uploading: "Subiendo..." - success: "Archivo subido satisfactoriamente, se te notificará en breve con los avances." error: "Hubo un error al subir '{{filename}}': {{message}}" password: title: "Contraseña" @@ -449,7 +446,6 @@ es: common: "Esa contraseña es demasiado común." ok: "Tu contraseña es válida." instructions: "Debe contener al menos %{count} caracteres." - associated_accounts: "Cuentas asociadas" ip_address: title: "Última dirección IP" registration_ip_address: @@ -1523,9 +1519,14 @@ es: title: "Regresar la base de datos al estado funcional anterior" confirm: "¿Estás seguro que quieres regresar la base de datos al estado funcional anterior?" export_csv: - success: "Exportación iniciada, serás notificado dentro de poco sobre el progreso." failed: "Exportación fallida, revisa los logs." button_text: "Exportar" + button_title: + user: "Exportar la lista completa de usuarios en formato CSV." + staff_action: "Exportar el registro completo de acciones de administradores en formato CSV." + screened_email: "Exportar la lista completa de emails vistos en formato CSV." + screened_ip: "Exportar la lista completa de IP vistas en formato CSV." + screened_url: "Exportar la lista completa de URL vistas en formato CSV." customize: title: "Personalizar" long_title: "Personalizaciones del sitio" @@ -2016,7 +2017,6 @@ es: back: 'u Atrás' up_down: 'k/j Desplazar selección ↑ ↓' open: 'o or Entrar Abrir tema seleccionado' - next_prev: 'shift j/shift k Siguiente/anterior sección' application: title: 'Aplicación' create: 'c Crear un tema nuevo' @@ -2031,11 +2031,8 @@ es: actions: title: 'Acciones' star: 'f Añadir tema a favoritos' - pin_unpin_topic: 'shift p Destacar/No destacar tema' - share_topic: 'shift s Compartir tema' share_post: 's Compartir post' reply_as_new_topic: 't Responder como un tema enlazado.' - reply_topic: 'shift r Responder al tema' reply_post: 'r Responder al post' quote_post: 'q Citar post' like: 'l Me gusta el post' diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index b25953a4e8..7469e9c102 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -84,7 +84,6 @@ fi: other: "%{count} päivää sitten" share: topic: 'jaa linkki tähän ketjuun' - post: 'jaa linkki viestiin #%{postNumber}' close: 'sulje' twitter: 'jaa tämä linkki Twitterissä' facebook: 'jaa tämä linkki Facebookissa' @@ -259,7 +258,6 @@ fi: profile: "Profiili" mute: "Vaimenna" edit: "Muokkaa asetuksia" - download_archive: "lataa arkisto viesteistäsi" new_private_message: "Uusi yksityisviesti" private_message: "Yksityisviesti" private_messages: "Viestit" @@ -441,7 +439,6 @@ fi: none: "Et ole kutsunut vielä ketään. Voit lähettää yksittäisiä kutsuja tai kutsua useita ihmisiä kerralla lähettämällä massakutsun tiedostosta." text: "Lähetä massakutsu tiedostosta" uploading: "Lähettää..." - success: "Tiedoston lähettäminen onnistui, saat pikaisesti ilmoituksen edistymisestä." error: "Tiedoston '{{filename}}' lähetyksen aikana tapahtui virhe: {{message}}" password: title: "Salasana" @@ -449,7 +446,6 @@ fi: common: "Annettu salasana on liian yleinen." ok: "Salasana vaikuttaa hyvältä." instructions: "Vähintään %{count} merkkiä." - associated_accounts: "Liittyvät käyttäjätilit" ip_address: title: "Viimeinen IP-osoite" registration_ip_address: @@ -1523,7 +1519,6 @@ fi: title: "Palauta tietokanta edelliseen toimivaan tilaan" confirm: "Oletko varma, että haluat palauttaa tietokannan edelliseen toimivaan tilaan?" export_csv: - success: "Vienti on käynnissä, saat pian ilmoituksen sen edistymisestä." failed: "Vienti epäonnistui. Tarkista loki-tiedostot." button_text: "Vie" customize: @@ -2015,7 +2010,6 @@ fi: back: 'u Takaisin' up_down: 'k/j Siirrä valintaa ↑ ↓' open: 'o tai Enter Avaa valittu ketju' - next_prev: 'shift j/shift k Seuraava/edellinen osio' application: title: 'Ohjelmisto' create: 'c Luo uusi ketju' @@ -2030,11 +2024,8 @@ fi: actions: title: 'Toiminnot' star: 'f Merkitse ketju tähdellä' - pin_unpin_topic: 'shift p Kiinnitä ketju/poista kiinnitys' - share_topic: 'shift s Jaa ketju' share_post: 's Jaa viesti' reply_as_new_topic: 't Aloita uusi yhdistetty ketju' - reply_topic: 'shift r Vastaa ketjuun' reply_post: 'r Vastaa viestiin' quote_post: 'q Lainaa viesti' like: 'l Tykkää viestistä' diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index 7e54de4988..953b2092f8 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -84,7 +84,6 @@ fr: other: "Il y a %{count} jours" share: topic: 'partager ce sujet' - post: 'partager le message n° %{postNumber}' close: 'fermer' twitter: 'partager ce lien sur Twitter' facebook: 'partager ce lien sur Facebook' @@ -259,7 +258,6 @@ fr: profile: "Profil" mute: "Silencieux" edit: "Modifier les préférences" - download_archive: "télécharger une archive de mes messages" new_private_message: "Nouveau Message Privé" private_message: "Message privé" private_messages: "Messages privés" @@ -441,7 +439,6 @@ fr: none: "Vous n'avez encore invité personne. Vous pouvez envoyé des invitations individuelles, ou en masse en envoyant un fichier d'invitation contenant la liste des courriels." text: "Invitation massive depuis un fichier" uploading: "Envoi en cours..." - success: "Ficher téléchargé avec succès, vous recevrez bientôt une notification avec la progression." error: "Il y a eu une erreur lors de l'envoi de '{{filename}}': {{message}}" password: title: "Mot de passe" @@ -449,7 +446,6 @@ fr: common: "Ce mot de passe est trop commun." ok: "Votre mot de passe semble correct." instructions: "Au moins %{count} caractères." - associated_accounts: "Comptes associés" ip_address: title: "Dernières adresses IP" registration_ip_address: @@ -1527,9 +1523,14 @@ fr: title: "Restaurer (RollBack) la base de données à l'état de travail précédent" confirm: "Êtes-vous sûr de vouloir restaurer (rollback) la base de données à l'état de fonctionnement précédent?" export_csv: - success: "L'export a commencé, vous serez notifié prochainement de sa progression." failed: "L'export a échoué. Veuillez consulter les logs." button_text: "Exporter" + button_title: + user: "Exporter la liste des utilisateurs dans un fichier CSV." + staff_action: "Exporter la liste des actions du staff dans un fichier CSV." + screened_email: "Exporter la liste des emails sous surveillance dans un fichier CSV." + screened_ip: "Exporter la liste complète des adresses IP sous surveillance dans un fichier CSV." + screened_url: "Exporter toutes les URL affichées vers un fichier CSV" customize: title: "Personnaliser" long_title: "Personnalisation du site" @@ -2019,7 +2020,6 @@ fr: back: 'u Retour' up_down: 'k/j Déplacer la sélection ↑ ↓' open: 'o ou Entrée Ouvrir le sujet sélectionné' - next_prev: 'shift j/shift k Suivante/précédente section' application: title: 'Application' create: 'c Créer un nouveau sujet' @@ -2034,11 +2034,8 @@ fr: actions: title: 'Actions' star: 'f Sujet Favoris' - pin_unpin_topic: 'shift p Épingler/De-épingler le sujet' - share_topic: 'shift s Partager un sujet' share_post: 's Partager un message' reply_as_new_topic: 't Répondre en créant un sujet lié' - reply_topic: 'shift r Répondre au sujet' reply_post: 'r Répondre à un message' quote_post: 'q Citer un message' like: 'l Aimer un message' diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml index d077ec3ff4..ec22352e1e 100644 --- a/config/locales/client.he.yml +++ b/config/locales/client.he.yml @@ -84,7 +84,6 @@ he: other: "לפני %{count} ימים" share: topic: 'שתפו קישור לנושא זה' - post: 'שתפו קישור להודעה מספר #%{postNumber}' close: 'סגור' twitter: 'שתפו קישור זה בטוויטר' facebook: 'שתפו קישור זה בפייסבוק' @@ -259,7 +258,6 @@ he: profile: "פרופיל" mute: "השתק" edit: "ערוך העדפות" - download_archive: "הורד ארכיון של ההודעות שלי" new_private_message: "מסר פרטי חדש" private_message: "הודעה פרטית" private_messages: "הודעות" @@ -441,7 +439,6 @@ he: none: "נכון לעכשיו לא הזמנת לכאן אף אחד. תוכלו לשלוח הזמנות אישיות, או להזמין כמה אנשים בבת אחת באמצעות העלאת קובץ הזמנה קבוצתית." text: "הזמנה קבוצתית מקובץ" uploading: "העלאה..." - success: "הקובץ הועלה בהצלחה, תקבל/י התראה תוך זמן קצר בנוגע להתקדמות." error: "חלה תקלה בהעלאת \"'{{filename}}': \n{{message}}" password: title: "סיסמה" @@ -449,7 +446,6 @@ he: common: "הסיסמה הזו נפוצה מידי." ok: "הסיסמה שלך נראית טוב." instructions: "לפחות %{count} תווים." - associated_accounts: "חשבונות מקושרים" ip_address: title: "כתובת IP אחרונה" registration_ip_address: @@ -1523,7 +1519,6 @@ he: title: "הזחר את מסד הנתונים למצב עבודה קודם" confirm: "אתה בטוח שברצונך להחזיר את מסד הנתונים למצב עבודה קודם?" export_csv: - success: "פעולת הייצוא החלה, תוך זמן קצר תעודכנו בהתקדמות." failed: "הייצוא נכשל. אנא בדקו ברישומי הלוג." button_text: "ייצוא" customize: @@ -2015,7 +2010,6 @@ he: back: 'u חזרה' up_down: 'k/j Move selection ↑ ↓' open: 'o או Enter פתח נושא נבחר' - next_prev: 'shift j/shift k Next/previous section' application: title: 'יישום' create: 'c צור נושא חדש' @@ -2030,11 +2024,8 @@ he: actions: title: 'פעולות' star: 'f סמן נושא בכוכב' - pin_unpin_topic: 'shift p הצמדת נושא' - share_topic: ' shift s שיתוף נושא' share_post: 's שיתוף הודעה' reply_as_new_topic: 't תגובה כנושא מקושר' - reply_topic: ' shift r להגיב לנושא' reply_post: 'r להגיב להודעה' quote_post: ' q ציטוט פוסט' like: 'l תן לייק להודעה' diff --git a/config/locales/client.id.yml b/config/locales/client.id.yml index d0f80c0f61..7d5d9c06f2 100644 --- a/config/locales/client.id.yml +++ b/config/locales/client.id.yml @@ -68,7 +68,6 @@ id: other: "%{count} hari yang lalu" share: topic: 'share link ke topik ini' - post: 'share link ke post #%{postNumber}' close: 'tutup' twitter: 'share link ini di Twitter' facebook: 'share link ini di Facebook' @@ -188,7 +187,6 @@ id: user: profile: "Profil" edit: "Ubah Preferensi" - download_archive: "download arsip postingan saya" private_message: "Private Message" private_messages: "Messages" activity_stream: "Activity" diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index 2a389c74e9..833852d70d 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -84,7 +84,6 @@ it: other: "%{count} giorni fa" share: topic: 'Condividi un link a questa conversazione' - post: 'Condividi un collegamento al messaggio n°%{postNumber}' close: 'chiudi' twitter: 'Condividi questo link su Twitter' facebook: 'Condividi questo link su Facebook' @@ -259,7 +258,6 @@ it: profile: "Profilo" mute: "Ignora" edit: "Modifica opzioni" - download_archive: "scarica lo storico dei miei messaggi" new_private_message: "Nuovo Messaggio Privato" private_message: "Messaggio privato" private_messages: "Messaggi" @@ -441,7 +439,6 @@ it: none: "Non hai ancora invitato nessuno qui. Puoi inviare inviti individuali, o invitare un gruppo di persone caricando un file di invito di massa." text: "Invito di Massa da File" uploading: "In caricamento..." - success: "Il file è stato caricato correttamente, verrai notificato a breve con i progressi." error: "Si è verificato un errore durante il caricamento {{filename}}': {{message}}" password: title: "Password" @@ -449,7 +446,6 @@ it: common: "Questa password è troppo comune." ok: "La password è adeguata" instructions: "Minimo %{count} caratteri." - associated_accounts: "Account associati" ip_address: title: "Ultimo indirizzo IP" registration_ip_address: @@ -1523,9 +1519,10 @@ it: title: "Ripristina il database a una versione funzionante precedente" confirm: "Sicuro di voler ripristinare una precedente versione funzionante del database?" export_csv: - success: "L'esportazione è stata avviata, ti verrà a breve notificato il progresso del processo." failed: "Esportazione fallita. Controlla i log." button_text: "Esporta" + button_title: + user: "Esporta l'intero elenco di utenti in formato CSV." customize: title: "Personalizza" long_title: "Personalizzazioni Sito" @@ -2016,7 +2013,6 @@ it: back: 'u Indietro' up_down: 'k/j Sposta la selezione ↑ ↓' open: 'o o Invio Apri l''argomento selezionato' - next_prev: 'shift j/shift k Sezione prossima/precedente ' application: title: 'Applicazione' create: 'c Crea un nuovo argomento' @@ -2031,11 +2027,8 @@ it: actions: title: 'Azioni' star: 'f Aggiungi l''argomento ai preferiti' - pin_unpin_topic: 'shift p Appunta/Spunta argomento' - share_topic: 'shift s Condividi argomento' share_post: 's Condividi messaggio' reply_as_new_topic: 't Rispondi con argomento collegato' - reply_topic: 'shift r Rispondi all''argomento' reply_post: 'r Rispondi al messaggio' quote_post: 'q Cita messaggio' like: 'l Metti "Mi piace" al messaggio' diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml index 483a74cf6e..f227c0040f 100644 --- a/config/locales/client.ja.yml +++ b/config/locales/client.ja.yml @@ -68,7 +68,6 @@ ja: other: "%{count} 日前" share: topic: 'このトピックのリンクをシェアする' - post: 'ポスト #%{postNumber} のリンクをシェアする' close: '閉じる' twitter: 'Twitter でこのリンクを共有する' facebook: 'Facebook でこのリンクを共有する' @@ -224,7 +223,6 @@ ja: profile: "プロフィール" mute: "ミュート" edit: "プロフィールを編集" - download_archive: "ポストのアーカイブをダウンロード" private_message: "プライベートメッセージ" private_messages: "メッセージ" activity_stream: "アクティビティ" @@ -372,7 +370,6 @@ ja: none: "あなたはまだ誰も招待していません。" text: "ファイルからまとめて招待をする" uploading: "アップロード中..." - success: "ファイル通常にアップされていました。" error: "ファイルアップロードエラー:'{{filename}}': {{message}}" password: title: "パスワード" @@ -380,7 +377,6 @@ ja: common: "このパスワードは他の人が使用している可能性があります。" ok: "パスワード OK。" instructions: "%{count} 文字以上の長さにしてください。" - associated_accounts: "関連アカウント" ip_address: title: "最終 IP アドレス" registration_ip_address: @@ -1683,7 +1679,6 @@ ja: back: 'u 戻る' up_down: 'k/j トピック・投稿 ↑ ↓移動' open: 'o or Enter トピックへ' - next_prev: 'shift j/shift k 次・前の選択へ移動' application: title: 'アプリケーション' create: 'c 新しいトピックを作成' @@ -1698,9 +1693,7 @@ ja: actions: title: '操作' star: 'f お気に入りトピック' - share_topic: 'shift s トピックをシェアする' share_post: 's 投稿をシェアする' - reply_topic: 'shift r トピックに返信' reply_post: 'r 投稿に返信' quote_post: 'q 投稿を引用する' like: 'l 投稿を「いいね!」する' diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index 38d2e0a326..8a9a3f9a0e 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -68,7 +68,6 @@ ko: other: "%{count}일 전" share: topic: '토픽을 공유합니다.' - post: '#%{postNumber} 게시물을 공유합니다.' close: '닫기' twitter: 'twitter로 공유' facebook: 'Facebook으로 공유' @@ -235,7 +234,6 @@ ko: profile: "프로필" mute: "알림 끄기" edit: "환경 설정 편집" - download_archive: "내 게시물을 아카이브로 다운로드" private_message: "개인 메시지" private_messages: "메시지" activity_stream: "활동" @@ -407,7 +405,6 @@ ko: none: "아직 아무도 초대하지 않았습니다. 초대장을 각각 보내거나, uploading a bulk invite file을 이용하여 단체 초대를 보낼 수 있습니다." text: "파일로 대량 초대하기" uploading: "업로드 중..." - success: "파일이 성공적으로 업로드되었습니다. 곧 진행 상황을 알려드리겠습니다." error: "'{{filename}}': {{message}} 업로드중 에러가 있었습니다." password: title: "비밀번호" @@ -415,7 +412,6 @@ ko: common: "That password is too common." ok: "적절한 암호입니다." instructions: "글자 수가 %{count}자 이상이어야 합니다." - associated_accounts: "관련된 계정" ip_address: title: "마지막 IP 주소" registration_ip_address: @@ -1448,7 +1444,6 @@ ko: title: "데이터베이스를 이전 workiong state로 되돌리기" confirm: "정말로 이전 작업 상태로 데이터베이스를 롤백하시겠습니까?" export_csv: - success: "내보내기가 시작되었습니다. 곧 진행사항을 알려드릴께요." failed: "내보내기가 실패했습니다. 로그를 확인해주세요" button_text: "내보니기" customize: @@ -1925,7 +1920,6 @@ ko: back: 'u Back' up_down: 'k/j 선택된 게시글 이동 ↑ ↓' open: 'o or Enter 선택한 토픽에 들어갑니다.' - next_prev: 'shift j/shift k 다음/이전 섹션' application: title: 'Application' create: 'c 새 토픽을 만듭니다.' @@ -1940,11 +1934,8 @@ ko: actions: title: 'Actions' star: 'f 별표시 토픽' - pin_unpin_topic: 'Shift + p 핀고정/핀해제' - share_topic: 'shift s 토픽 공유' share_post: 's 게시글 공유' reply_as_new_topic: 't 링크된 토픽으로 답글 작성하기' - reply_topic: 'shift r 토픽에 답변 달기' reply_post: 'r 게시글에 답글 달기' quote_post: 'q 인용 글' like: 'l Like post' diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml index 6f8b9b127d..ffc6983c57 100644 --- a/config/locales/client.nb_NO.yml +++ b/config/locales/client.nb_NO.yml @@ -84,7 +84,6 @@ nb_NO: other: "%{count} dager siden" share: topic: 'del en lenke til dette emnet' - post: 'del en lenke til innlegg #%{postNumber}' close: 'lukk' twitter: 'del denne lenken på Twitter' facebook: 'del denne lenken på Facebook' @@ -259,7 +258,6 @@ nb_NO: profile: "Profil" mute: "Demp" edit: "Rediger innstillinger" - download_archive: "last ned arkiv av mine innlegg" new_private_message: "Ny Privat Melding" private_message: "Privat melding" private_messages: "Meldinger" @@ -345,7 +343,7 @@ nb_NO: instructions: "Bakgrunnsbilder vil bli sentrert og ha en standard bredde på 590px." email: title: "E-post" - instructions: "Aldri vist i offentlighet" + instructions: "Blir aldri vist offentlig" ok: "Vi sender deg en email for å bekrefte" invalid: "Vennligst gi en gyldig email-addresse" authenticated: "Din email har blitt autentisert av {{provider}}" @@ -441,7 +439,6 @@ nb_NO: none: "Du har ikke invitert noen hit enda. Du kan sende individuelle invitasjoner, eller invitere en gruppe folk på en gang ved å laste opp en fil med flere invitasjoner." text: "Masseinvitasjon fra fil" uploading: "Laster opp..." - success: "Filen har blitt lastet opp. Du vil bli varslet om kort tid om fremgangen." error: "En feil oppsto ved opplastingen av '{{filename}}': {{message}}" password: title: "Passord" @@ -449,7 +446,6 @@ nb_NO: common: "Det passordet er for vanlig." ok: "Passordet ditt ser bra ut" instructions: "Minst %{count} tegn." - associated_accounts: "Tilknyttede kontoer" ip_address: title: "Siste IP-adresse" registration_ip_address: @@ -1523,9 +1519,14 @@ nb_NO: title: "Gjenopprett databasen til en tidligere fungerende tilstand" confirm: "Er du sikker på at du vil gjenopprette databasen til en tidligere fungerende tilstand?" export_csv: - success: "Eksport igangsatt; du vil motta en prosessrapport om kort tid." failed: "Eksporteringen feilet. Venligst undersøk loggene." button_text: "Eksporter" + button_title: + user: "Eksporter full medlemsliste i CSV format." + staff_action: "Eksporter full handligslogg i CSV format." + screened_email: "Eksporter komplett liste over filtrerte epostadresser i CSV format." + screened_ip: "Eksporter komplett liste over filtrerte IP-addresser i CSV format." + screened_url: "Eksporter komplett liste over filtrerte URL'er i CSV format." customize: title: "Tilpasse" long_title: "Nettstedstilpasninger" @@ -2015,7 +2016,6 @@ nb_NO: back: 'u Tilbake' up_down: 'k/j Flytt markering ↑ ↓' open: 'o or Enter Åpne valgt emne' - next_prev: 'shift j/shift k Neste/forrige seksjon' application: title: 'Applikasjon' create: 'c Opprett nytt emne' @@ -2030,11 +2030,8 @@ nb_NO: actions: title: 'Handlinger' star: 'f Legg emne i favoritter' - pin_unpin_topic: 'shift p Fastsett/Løsgjør emne' - share_topic: 'shift s Del emne' share_post: 's Del innlegg' reply_as_new_topic: 't Svar med lenket emne' - reply_topic: 'shift r Svar på emne' reply_post: 'r Svar på innlegg' quote_post: 'q Siter innlegg' like: 'l Lik innlegg' diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index 6249345e96..3d8344f2a9 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -84,7 +84,6 @@ nl: other: "%{count} dagen geleden" share: topic: 'deel een link naar deze topic' - post: 'deel een link naar bericht #%{postNumber}' close: 'sluit' twitter: 'deel deze link op Twitter' facebook: 'deel deze link op Facebook' @@ -255,7 +254,6 @@ nl: profile: "Profiel" mute: "Negeer" edit: "Wijzig voorkeuren" - download_archive: "download een archief van mijn berichten" new_private_message: "Nieuw persoonlijk bericht" private_message: "Privébericht" private_messages: "Berichten" @@ -266,8 +264,8 @@ nl: invited_by: "Uitgenodigd door" trust_level: "Trustlevel" notifications: "Notificaties" - dismiss_notifications: "Markeer als gelezen" - dismiss_notifications_tooltip: "Markeer alle niet gelezen berichten als gelezen" + dismiss_notifications: "Markeer alles als gelezen" + dismiss_notifications_tooltip: "Markeer alle ongelezen berichten als gelezen" disable_jump_reply: "Niet na reageren naar je nieuwe bericht gaan" dynamic_favicon: "Laat notificatie voor nieuw bericht zien in favicon (experiment)" edit_history_public: "Laat andere gebruikers mijn aanpassingen aan dit bericht zien." @@ -364,7 +362,7 @@ nl: bi_weekly: "elke twee weken" email_direct: "Ontvang een mail wanneer iemand je citeert, reageert op je bericht of je @gebruikersnaam noemt." email_private_messages: "Ontvang een mail wanneer iemand je een privébericht heeft gestuurd." - email_always: "Notificaties niet onderdrukken als ik actief ben op de site" + email_always: "E-mailnotificaties niet onderdrukken als ik actief ben op de site" other_settings: "Overige" categories_settings: "Categorieën" new_topic_duration: @@ -409,9 +407,8 @@ nl: create: "Stuur een uitnodiging" bulk_invite: none: "Je hebt nog niemand uitgenodigd. Je kan individueel uitnodigen of een groep mensen tegelijk door een groepsuitnodiging-bestand te uploaden" - text: "Groepsuitnodiging van bestan" + text: "Groepsuitnodiging via bestand" uploading: "Uploaden..." - success: "Het uploaden van het bestand is gelukt, over enkele momenten krijg je meer informatie over de voortgang" error: "Het uploaden van '{{filename}}' is niet gelukt: {{message}}" password: title: "Wachtwoord" @@ -419,7 +416,6 @@ nl: common: "Dat wachtwoord wordt al te vaak gebruikt." ok: "Je wachtwoord ziet er goed uit." instructions: "Minimaal %{count} tekens." - associated_accounts: "Geassocieerde accounts" ip_address: title: "Laatste IP-adres" registration_ip_address: @@ -437,26 +433,26 @@ nl: the_topic: "de topic" loading: "Laden..." errors: - prev_page: "tijdens het uploaden" + prev_page: "tijdens het laden" reasons: - network: "Netwerk Fout" - server: "Server Fout" - forbidden: "Toegang Gewijgerd" + network: "Netwerkfout" + server: "Serverfout" + forbidden: "Toegang geweigerd" unknown: "Fout" desc: - network: "Controleer je verbinding" + network: "Controleer je verbinding." network_fixed: "Het lijkt er op dat het terug is" server: "Fout code: {{status}}" - forbidden: "Dat mag je niet zien." + forbidden: "Je hebt geen toestemming om dit te bekijken." unknown: "Er is iets mis gegaan" buttons: back: "Ga terug" again: "Probeer opnieuw" fixed: "Pagina laden" close: "Sluit" - assets_changed_confirm: "Discourse is bijgewerkt, will je een refresh doen om de laatste versie te laden?" + assets_changed_confirm: "De site is bijgewerkt. Wil je een de pagina vernieuwen om de laatste versie te laden?" read_only_mode: - enabled: "Dit forum is in read-only modus. Je kan rondkijken, maar sommige elementen zullen mogelijk niet goed werken." + enabled: "Een administrator heeft dit forum op alleen-lezen gezet. Je kan rondkijken, maar sommige elementen zullen mogelijk niet werken." login_disabled: "Zolang de site in read-only modus is, kan er niet ingelogd worden." too_few_topics_notice: "Maak ten minste 5 openbare discussies en %{posts} openbare berichten om de discussie te starten. Nieuwe gebruikers zullen geen vertrouwensniveau's kunnen verdienen tenzij er berichten zijn die zij kunnen lezen. Dit bericht wordt alleen getoond aan de beheerders." learn_more: "leer meer..." @@ -473,16 +469,16 @@ nl: last_post: Laatste bericht last_post_lowercase: laatste bericht summary: - enabled_description: "Je leest een samenvatting van dit topic: alleen de meeste interessante berichten bepaald door de community. " + enabled_description: "Je leest een samenvatting van dit topic: alleen de meeste interessante berichten zoals bepaald door de community. " description: "Er zijn {{count}} reacties." description_time: "Er zijn {{count}} reacties met een gemiddelde leestijd van {{readingTime}} minuten." enable: 'Samenvatting van topic' disable: 'Alle berichten' deleted_filter: - enabled_description: "Deze discussie bevat verwijderde berichten, die niet getoond worden" - disabled_description: "Verwijderde berichten in deze discussie worden getoond." - enable: "Verberg Verwijderde Berichten" - disable: "Toon Verwijderde Berichten" + enabled_description: "Dit topic bevat verwijderde berichten, die niet getoond worden." + disabled_description: "Verwijderde berichten in dit topic worden getoond." + enable: "Verberg verwijderde berichten" + disable: "Toon verwijderde berichten" private_message_info: title: "Privébericht" invite: "Nodig anderen uit..." @@ -501,12 +497,12 @@ nl: action: "Ik ben mijn wachtwoord vergeten" invite: "Vul je gebruikersnaam of e-mailadres in en we sturen je een wachtwoord-herstel-mail." reset: "Herstel wachtwoord" - complete_username: "Als er een account gevonden kan worden met gebruikersnaam %{username} dan zul je in een ogenblik een wachtwoord-herstel-mail ontvangen." - complete_email: "Als er een account gevonden kan worden met het emailadres %{email} dan zul je in een ogenblik een wachtwoord-herstel-mail ontvangen." - complete_username_found: "We hebben een account met gebruikersnaam %{username} gevonden, je zult in een ogenblik een wachtwoord-herstel-mail ontvangen." - complete_email_found: "We hebben een account gevonden met het emailadres %{email} je zult in een ogenblik een wachtwoord-herstel-mail ontvangen." - complete_username_not_found: "Er is geen account met gebruikersnaam %{username}" - complete_email_not_found: "Geen account voor %{email}" + complete_username: "Als er een account gevonden kan worden met de gebruikersnaam %{username}, dan zal je spoedig een e-mail ontvangen met daarin instructies om je wachtwoord te resetten." + complete_email: "Als er een account gevonden kan worden met het e-mailadres %{email}, dan zal je spoedig een e-mail ontvangen met daarin instructies om je wachtwoord te resetten." + complete_username_found: "We hebben een account met de gebruikersnaam %{username} gevonden. Je zal spoedig een e-mail ontvangen met daarin instructies om je wachtwoord te resetten." + complete_email_found: "We hebben een account gevonden met het emailadres %{email}. Je zal spoedig een e-mail ontvangen met daarin instructies om je wachtwoord te resetten." + complete_username_not_found: "Geen account met de gebruikersnaam %{username} gevonden" + complete_email_not_found: "Geen account met het e-mailadres %{email} gevonden" login: title: "Inloggen" username: "Gebruiker" @@ -514,7 +510,7 @@ nl: email_placeholder: "e-mail of gebruikersnaam" caps_lock_warning: "Caps Lock staat aan" error: "Er is een onbekende fout opgetreden" - blank_username_or_password: "Vul je email of gebruikersnaam in en je wachtwoord." + blank_username_or_password: "Vul je email of gebruikersnaam en je wachtwoord in." reset_password: 'Herstel wachtwoord' logging_in: "Inloggen..." or: "Of" @@ -523,7 +519,7 @@ nl: awaiting_approval: "Je account is nog niet goedgekeurd door iemand van de staf. Je krijgt van ons een mail wanneer dat gebeurd is." requires_invite: "Toegang tot dit forum is alleen op uitnodiging." not_activated: "Je kan nog niet inloggen. We hebben je een activatie-mail gestuurd (naar {{sentTo}}). Volg de instructies in die mail om je account te activeren." - not_allowed_from_ip_address: "Je kunt niet inloggen vanaf dat IP adres." + not_allowed_from_ip_address: "Je kunt niet inloggen vanaf dat IP-adres." resend_activation_email: "Klik hier om de activatiemail opnieuw te ontvangen." sent_activation_email_again: "We hebben een nieuwe activatiemail gestuurd naar {{currentEmail}}. Het kan een aantal minuten duren voor deze aan komt. Check ook je spamfolder." google: @@ -531,7 +527,7 @@ nl: message: "Inloggen met een Google-account (zorg ervoor dat je popup blocker uit staat)" google_oauth2: title: "met Google" - message: "Authenticeren met Google (zorg er voor dat popup-blockers uit staan)" + message: "Authenticeren met Google (zorg er voor dat pop-up blockers uit staan)" twitter: title: "met Twitter" message: "Inloggen met een Twitteraccount (zorg ervoor dat je popup blocker uit staat)" @@ -605,7 +601,7 @@ nl: toggler: "verberg of toon de editor" admin_options_title: "Optionele stafinstellingen voor deze topic" notifications: - title: "notificaties van @naam vermeldingen, reacties op je berichten en topics, privé-berichten, etc." + title: "notificaties van meldingen van @naam, reacties op je berichten en topics, privé-berichten, etc." none: "Er zijn nu geen notificaties." more: "bekijk oudere notificaties" total_flagged: "aantal gemarkeerde berichten" @@ -617,10 +613,10 @@ nl: liked: "

    {{username}} {{description}}

    " private_message: "

    {{username}} {{description}}

    " invited_to_private_message: "

    {{username}} {{description}}

    " - invitee_accepted: "

    {{username}} accepted your invitation

    " - moved_post: "

    {{username}} moved {{description}}

    " + invitee_accepted: "

    {{username}} heeft jouw uitnodiging geaccepteerd

    " + moved_post: "

    {{username}} verplaatste {{description}}

    " linked: "

    {{username}} {{description}}

    " - granted_badge: "

    '{{description}}' gekregen

    " + granted_badge: "

    '{{description}}' ontvangen

    " upload_selector: title: "Voeg een afbeelding toe" title_with_attachments: "Voeg een afbeelding of bestand toe" @@ -638,27 +634,27 @@ nl: post_format: "#{{post_number}} door {{username}}" context: user: "Zoek berichten van @{{username}}" - category: "Zoek door de \"{{category}}\" categorie" + category: "Doorzoek de \"{{category}}\" categorie" topic: "Zoek in deze topic" site_map: "ga naar een andere topiclijst of categorie" go_back: 'ga terug' - not_logged_in_user: 'gebruikerspagina met samenvatting van de activiteiten en voorkeuren' + not_logged_in_user: 'gebruikerspagina met samenvatting van huidige activiteit en voorkeuren' current_user: 'ga naar je gebruikerspagina' starred: title: 'Ster' help: - star: 'Markeer deze topic met een ster om makkelijk terug te vinden' - unstar: 'Verwijder de stermarkering van deze topic' + star: 'Voeg dit topic toe aan je favorietenlijst' + unstar: 'Verwijder dit topic uit je favorietenlijst' topics: bulk: reset_read: "markeer als ongelezen" - delete: "Verwijder discussies" + delete: "Verwijder topics" dismiss_posts: "Verwijder berichten" - dismiss_posts_tooltip: "Reset de teller voor ongelezen berichten voor deze discussies, maar houd ze wel in mijn lijst van ongelezen discussies als er nieuwe berichten worden toegevoegd." - dismiss_topics: "Verwijder discussies" - dismiss_topics_tooltip: "Laat deze discussies niet meer zien in mijn ongelezen discussielijst wanneer nieuwe berichten worden geplaatst." + dismiss_posts_tooltip: "Reset de teller voor ongelezen berichten voor deze topics, maar houd ze wel in mijn lijst van ongelezen topics als er nieuwe berichten worden toegevoegd" + dismiss_topics: "Verwijder topics" + dismiss_topics_tooltip: "Laat deze topics niet meer in mijn lijst van ongelezen topics zien wanneer er nieuwe berichten worden geplaatst." dismiss_new: "markeer nieuwe berichten als gelezen" - toggle: "selecteer meerdere topics tegelijkertijd" + toggle: "toggle bulkselectie van topics" actions: "Bulk Acties" change_category: "Wijzig categorie" close_topics: "Sluit topics" @@ -668,7 +664,7 @@ nl: one: "Je hebt 1 topic geselecteerd." other: "Je hebt {{count}} topics geselecteerd." none: - starred: "Je hebt nog geen topics met een ster." + starred: "Je hebt nog geen favoriete topics." unread: "Je hebt geen ongelezen topics." new: "Je hebt geen nieuwe topics." read: "Je hebt nog geen topics gelezen." @@ -706,16 +702,16 @@ nl: invalid_access: title: "Topic is privé" description: "Sorry, je hebt geen toegang tot deze topic." - login_required: "Je moet inloggen om deze discussie te kunnen lezen." + login_required: "Je moet inloggen om dit topic te kunnen bekijken." server_error: title: "Laden van topic is mislukt" - description: "Sorry, we konden deze topic niet laden, waarschijnlijk door een verbindingsprobleem. Probeer het later opnieuw. Als het probleem blijft, laat het ons dan weten." + description: "Sorry, we konden dit topic niet laden, waarschijnlijk door een verbindingsprobleem. Probeer het later opnieuw. Als het probleem zich blijft voordoen, laat het ons dan weten." not_found: title: "Topic niet gevonden" - description: "Sorry, we konden de opgevraagde topic niet vinden. Wellicht is het verwijderd door een moderator?" + description: "Sorry, we konden het opgevraagde topic niet vinden. Wellicht is het verwijderd door een moderator?" total_unread_posts: one: "je hebt 1 ongelezen bericht in deze discussie" - other: "je hebt {{count}} ongelezen berichten in deze discussie" + other: "je hebt {{count}} ongelezen berichten in dit topic" unread_posts: one: "je hebt 1 ongelezen bericht in deze topic" other: "je hebt {{count}} ongelezen berichten in deze topic" @@ -743,8 +739,8 @@ nl: auto_close_save: "Opslaan" auto_close_remove: "Sluit deze topic niet automatisch" progress: - title: topic voortgang - go_top: "top" + title: voortgang van topic + go_top: "bovenaan" go_bottom: "onderkant" go: "ga" jump_bottom_with_number: "spring naar bericht %{post_number}" @@ -755,13 +751,13 @@ nl: reasons: '3_6': 'Je ontvangt notificaties omdat je deze categorie in de gaten houdt.' '3_5': 'Je ontvangt notificaties omdat je deze topic automatisch in de gaten houdt.' - '3_2': 'Je ontvangt notificaties omdat je deze topic in de gaten houdt.' - '3_1': 'Je ontvangt notificaties omdat jij deze topic gemaakt hebt.' - '3': 'Je ontvangt notificaties omdat je deze topic in de gaten houdt.' - '2_8': 'Je krijg een melding omdat je dit topic volgt.' - '2_4': 'Je ontvangt notificaties omdat je een reactie aan deze topic hebt geplaatst.' - '2_2': 'Je ontvangt notificaties omdat je deze topic volgt.' - '2': 'Je ontvangt notificaties omdat je deze topic hebt gelezen.' + '3_2': 'Je ontvangt notificaties omdat je dit topic in de gaten houdt.' + '3_1': 'Je ontvangt notificaties omdat je dit topic hebt gemaakt.' + '3': 'Je ontvangt notificaties omdat je dit topic in de gaten houdt.' + '2_8': 'Je ontvangt notificaties omdat je deze categorie volgt.' + '2_4': 'Je ontvangt notificaties omdat je een reactie in dit topic hebt geplaatst.' + '2_2': 'Je ontvangt notificaties omdat je dit topic volgt.' + '2': 'Je ontvangt notificaties omdat je dit topic hebt gelezen.' '0_7': 'Je negeert alle notificaties in deze categorie.' '0_2': 'Je negeert alle notificaties in deze topic.' '0': 'Je negeert alle notificaties in deze topic.' @@ -776,23 +772,23 @@ nl: regular: title: "Normaal" regular_pm: - title: "Vaste Bezoeker" + title: "Normaal" muted_pm: title: "Negeren" description: "Je zal geen notificaties krijgen voor dit privébericht." muted: title: "Negeren" - description: "Je zal geen notificaties krijgen voor deze topic en het zal ook niet verschijnen in je 'ongelezen'-tab." + description: "Je zal geen notificaties krijgen van dit topic en het zal ook niet verschijnen in je lijst ongelezen topics." actions: recover: "Herstel topic" delete: "Verwijder topic" open: "Open topic" close: "Sluit topic" auto_close: "Automatisch sluiten" - make_banner: "Verwijder topic" + make_banner: "Zet topic in als banner" remove_banner: "Verwijder bannertopic" unpin: "Ontpin topic" - pin: "Pin topic" + pin: "Pin topic vast" pin_globally: "Pin topic globaal vast" unarchive: "De-archiveer topic" archive: "Archiveer topic" @@ -803,13 +799,13 @@ nl: help: 'Schrijf een reactie op deze topic' clear_pin: title: "Verwijder pin" - help: "Annuleer de gepinde status van deze topic, zodat het niet langer bovenaan je topiclijst verschijnt." + help: "Verwijder de gepinde status van deze topic, zodat het niet langer bovenaan je topiclijst verschijnt." share: title: 'Deel' help: 'deel een link naar deze topic' flag_topic: title: 'Markeer' - help: 'Markeer dit bericht (privé) of zend een privébericht aan de schrijver' + help: 'geef een privé-markering aan dit topic of stuur er een privé-bericht over' success_message: 'Je hebt dit topic gemarkeerd' inviting: "Uitnodigen..." automatically_add_to_groups_optional: "Deze uitnodiging geeft ook toegang tot de volgende groepen: (optioneel, alleen voor beheerders)" @@ -819,11 +815,11 @@ nl: email_or_username: "E-mail of gebruikersnaam van genodigde" email_or_username_placeholder: "e-mailadres of gebruikersnaam" action: "Uitnodigen" - success: "Deze gebruiker is uitgenodigd om in de privé-conversatie deel te nemen" + success: "We hebben die gebruiker uitgenodigd om in deze privé-conversatie deel te nemen" error: "Sorry, er is iets misgegaan bij het uitnodigen van deze persoon" group_name: "groepsnaam" invite_reply: - title: 'Anderen uitnodigen' + title: 'Uitnodigen' action: 'Mail uitnodiging' help: 'verstuur uitnodigingen naar vrienden zodat zij met één klik kunnen reageren op deze topic' to_topic: "We zullen je vriend een korte e-mail sturen waardoor hij of zij meteen kan aanmelden en op dit topic kan reageren door op een link te klikken, hij of zij hoeft niet in te loggen." @@ -1871,9 +1867,7 @@ nl: actions: title: 'Acties' star: 'f Markeer topic met ster' - share_topic: 'shift s Deel topic' share_post: 's Deel bericht' - reply_topic: 'shift r Reageer op topic' reply_post: 'shift r Reageer op topic' quote_post: 'q Citeer bericht' like: 'l Vind bericht leuk' diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index db573d577b..67b024dbb1 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -100,7 +100,7 @@ pl_PL: other: "%{count} dni temu" share: topic: 'udostępnij odnośnik do tego tematu' - post: 'udostępnij odnośnik do wpisu #%{postNumber}' + post: 'wpis #%{postNumber}' close: 'zamknij' twitter: 'udostępnij ten odnośnik na Twitterze' facebook: 'udostępnij ten odnośnik na Facebooku' @@ -282,7 +282,6 @@ pl_PL: profile: "Profil" mute: "Wycisz" edit: "Edytuj ustawienia" - download_archive: "pobierz archiwum moich wpisów" new_private_message: "Nowa Prywatna Wiadomość" private_message: "Prywatna wiadomość" private_messages: "Wiadomości" @@ -374,6 +373,8 @@ pl_PL: authenticated: "Twój email został potwierdzony przez {{provider}}" frequency: zero: "Otrzymasz email natychmiast jeśli nie widziałeś rzeczy na temat której wysyłamy email." + one: "Otrzymasz e-mail jeśli nie widzieliśmy Cie w ciągu ostatniej minuty i jeszcze nie widziałeś rzeczy której dotyczy powiadomienie." + other: "Otrzymasz e-mail jeśli nie widzieliśmy Cie w ciągu ostatnich {{count}} minut i jeszcze nie widziałeś rzeczy której dotyczy powiadomienie." name: title: "Pełna nazwa" instructions: "Twoja pełna nazwa (opcjonalna)" @@ -466,7 +467,6 @@ pl_PL: none: "Jeszcze nikogo nie zaproszono. Możesz wysłać pojedyncze zaproszenie lub zaprosić wiele osób na raz wysyłając odpowiedni plik." text: "Zaproszenia hurtowe z pliku" uploading: "Wysyłanie…" - success: "Plik został przesłany pomyślnie, wkrótce dostaniesz powiadomienie z postępem." error: "Podczas przesyłania wystąpił błąd '{{filename}}': {{message}}" password: title: "Hasło" @@ -474,7 +474,6 @@ pl_PL: common: "To hasło jest zbyt popularne." ok: "Twoje hasło jest poprawne." instructions: "Co najmniej %{count} znaków." - associated_accounts: "Powiązane konta" ip_address: title: "Ostatni adres IP" registration_ip_address: @@ -1584,9 +1583,14 @@ pl_PL: title: "Wycofaj bazę danych do poprzedniego poprawnego stanu" confirm: "Czy na pewno chcesz przywrócić bazę danych do poprzedniego poprawnego stanu?" export_csv: - success: "Rozpoczęto eksport, wkrótce otrzymasz powiadomienie z informacją o postępie." failed: "Eksport zakończył się niepowodzeniem. Sprawdź logi." button_text: "Eksportuj" + button_title: + user: "Eksportuj listę wszystkich użytkowników do formatu CSV." + staff_action: "Eksportuj log zmian wykonanych przez zespół do formatu CSV." + screened_email: "Eksportuj listę monitorowanych adresów email do formatu CSV." + screened_ip: "Eksportuj listę monitorowanych IP do formatu CSV." + screened_url: "Eksportuj listę monitorowanych URLi do formatu CSV." customize: title: "Wygląd" long_title: "Personalizacja strony" @@ -1754,6 +1758,7 @@ pl_PL: title: "Ekranowane adresy IP" description: 'Adres IP który teraz oglądasz. Użyj "Zezwól" aby dodać do białej listy adresów IP.' delete_confirm: "Czy na pewno chcesz usunąć regułę dla %{ip_address}?" + roll_up_confirm: "Czy na pewno chcesz zgrupować monitorowane IP w podsieci?" rolled_up_some_subnets: "Pomyślnie zwinięto ban IP dla podsieci: %{subnets}." rolled_up_no_subnet: "Brak pozycji do zwinięcia." actions: @@ -2083,7 +2088,6 @@ pl_PL: back: 'u wstecz' up_down: 'k/j zaznacz ↑ ↓' open: 'o lub Enter otwórz wybrany temat' - next_prev: 'shift j/shift k następna/poprzednia sekcja' application: title: 'Aplikacja' create: 'c utwórz nowy temat' @@ -2098,11 +2102,8 @@ pl_PL: actions: title: 'Operacje' star: 'f oznacz temat gwiazdką' - pin_unpin_topic: 'shift p przypnij/odepnij temat' - share_topic: 'shift s udostępnij temat' share_post: 's udostępnij wpis' reply_as_new_topic: 't odpowiedz w nowym temacie' - reply_topic: 'shift r odpowiedz w temacie' reply_post: 'r odpowiedz na wpis' quote_post: 'q cytuj wpis' like: 'l polub wpis' diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml index 1f7c78f01f..d1a7bc7231 100644 --- a/config/locales/client.pt.yml +++ b/config/locales/client.pt.yml @@ -20,16 +20,16 @@ pt: mb: MB tb: TB dates: - time: "H:mm" - long_no_year: "DD MMM H:mm" - long_no_year_no_time: "D MMM" - long_with_year: "DD MMM YYY H:mm" + time: "hh:mm" + long_no_year: "DD MMM hh:mm" + long_no_year_no_time: "DD MMM" + long_with_year: "DD MMM YYYY hh:mm" long_with_year_no_time: "DD MMM 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 LT" - long_date_without_year_with_linebreak: "D MMM
    LT" - long_date_with_year_with_linebreak: "D MMM, 'YY
    LT" + long_date_with_year: "DD MMM, 'YY LT" + long_date_without_year: "DD MMM, LT" + long_date_with_year_without_time: "DD MMM, 'YY" + long_date_without_year_with_linebreak: "DD MMM
    LT" + long_date_with_year_with_linebreak: "DD MMM, 'YY
    LT" tiny: half_a_minute: "< 1m" less_than_x_seconds: @@ -59,7 +59,7 @@ pt: almost_x_years: one: "1a" other: "%{count}a" - date_month: "D MMM" + date_month: "DD MMM" date_year: "MMM 'YY" medium: x_minutes: @@ -71,7 +71,7 @@ pt: x_days: one: "1 dia" other: "%{count} dias" - date_year: "D MMM, 'YY" + date_year: "DD MMM, 'YY" medium_with_ago: x_minutes: one: "1 minuto atrás" @@ -83,16 +83,15 @@ pt: one: "1 dia atrás" other: "%{count} dias atrás" share: - topic: 'partilhar um link para este tópico' - post: 'partilhar um link para o post #%{postNumber}' + topic: 'partilhar uma hiperligação para este tópico' close: 'fechar' - twitter: 'partilhar este link no Twitter' - facebook: 'partilhar este link no Facebook' - google+: 'partilhar este link no Google+' - email: 'enviar este link num email' + twitter: 'partilhar esta hiperligação no Twitter' + facebook: 'partilhar esta hiperligação no Facebook' + google+: 'partilhar esta hiperligação no Google+' + email: 'enviar esta hiperligação por email' topic_admin_menu: "Ações administrativas dos Tópicos" - edit: 'editar o título ou a categoria deste tópico' - not_implemented: "Essa característica ainda não foi implementada, desculpe!" + edit: 'editar o título e a categoria deste tópico' + not_implemented: "Essa funcionalidade ainda não foi implementada, pedimos desculpa!" no_value: "Não" yes_value: "Sim" generic_error: "Pedimos desculpa, ocorreu um erro." @@ -101,12 +100,12 @@ pt: log_in: "Entrar" age: "Idade" joined: "Juntou-se" - admin_title: "Admin" + admin_title: "Administração" flags_title: "Sinalizações" show_more: "mostrar mais" show_help: "Ajuda" - links: "Links" - links_lowercase: "links" + links: "Hiperligações" + links_lowercase: "hiperligações" faq: "FAQ" guidelines: "Diretrizes" privacy_policy: "Política de Privacidade" @@ -116,7 +115,7 @@ pt: desktop_view: "Visualização Desktop" you: "Você" or: "ou" - now: "agora" + now: "ainda agora" read_more: 'ler mais' more: "Mais" less: "Menos" @@ -134,8 +133,8 @@ pt: simple_title: "Acerca" title: "Acerca de %{title}" stats: "Estatísticas do site" - our_admins: "Os nossos admins" - our_moderators: "Os nossos moderadores" + our_admins: "Os Nossos Administradores" + our_moderators: "Os Nossos Moderadores" stat: all_time: "Sempre" last_7_days: "Últimos 7 Dias" @@ -144,25 +143,25 @@ pt: post_count: "Mensagens" user_count: "Utilizadores" bookmarks: - not_logged_in: "desculpe, necessita de ter a sessão iniciada para marcar posts" - created: "adicionou este post aos marcadores" - not_bookmarked: "leu este post; clique para o adicionar aos marcadores" - last_read: "este é o último post que leu; clique para o adicionar aos marcadores" + not_logged_in: "Pedimos desculpa, é necessário ter sessão iniciada para marcar mensagens" + created: "adicionou esta mensagem aos marcadores" + not_bookmarked: "leu esta mensagem; carregue para adicionar aos marcadores" + last_read: "esta foi a última mensagem que leu; carregue para adicionar aos marcadores" remove: "Remover Marcador" topic_count_latest: - one: "{{count}} tópico novo ou actualizado." - other: "{{count}} tópicos novos ou actualizados." + one: "{{count}} tópico novo ou atualizado." + other: "{{count}} tópicos novos ou atualizados." topic_count_unread: one: "{{count}} tópico não lido." other: "{{count}} tópicos não lidos." topic_count_new: one: "{{count}} novo tópico." other: "{{count}} novos tópicos." - click_to_show: "Clique para mostrar." - preview: "prever" + click_to_show: "Carregue para mostrar." + preview: "pré-visualizar" cancel: "cancelar" - save: "Gravar alterações" - saving: "A gravar..." + save: "Guardar alterações" + saving: "A guardar..." saved: "Guardado!" upload: "Enviar" uploading: "A enviar..." @@ -173,43 +172,43 @@ pt: revert: "Reverter" failed: "Falhou" banner: - close: "Dispensar este banner." + close: "Destituir esta faixa." choose_topic: none_found: "Nenhum tópico encontrado." title: - search: "Procurar Tópico pelo nome, URL ou id:" + search: "Procurar Tópico por nome, URL ou id:" placeholder: "digite o título do tópico aqui" user_action: - user_posted_topic: "{{user}} inseriu o tópico" - you_posted_topic: "Você inseriu o tópico" - user_replied_to_post: "{{user}} respondeu {{post_number}}" - you_replied_to_post: "Você respondeu a {{post_number}}" + user_posted_topic: "{{user}} publicou o tópico" + you_posted_topic: " publicouo tópico" + user_replied_to_post: "{{user}} respondeu a {{post_number}}" + you_replied_to_post: " respondeu a {{post_number}}" user_replied_to_topic: "{{user}} respondeu ao tópico" - you_replied_to_topic: "Você respondeu ao tópico" + you_replied_to_topic: " respondeu ao tópico" user_mentioned_user: "{{user}} mencionou {{another_user}}" - user_mentioned_you: "{{user}} mencionou você" - you_mentioned_user: "Você mencionou {{another_user}}" - posted_by_user: "Inserido por {{user}}" - posted_by_you: "Inserido por você" + user_mentioned_you: "{{user}} mencionou-o" + you_mentioned_user: " mencionou {{another_user}}" + posted_by_user: "Publicado por {{user}}" + posted_by_you: "Publicado por si" sent_by_user: "Enviado por {{user}}" - sent_by_you: "Enviado por você" + sent_by_you: "Enviado por si" groups: - visible: "Grupo é visível para todos os utilizadores" + visible: "O grupo é visível para todos os utilizadores" title: one: "grupo" other: "grupos" members: "Membros" - posts: "Posts" + posts: "Mensagens" alias_levels: - title: "Quem pode usar este grupo como nome de utilizador?" + title: "Quem pode usar este grupo como pseudónimo?" nobody: "Ninguém" - only_admins: "Apenas admins" - mods_and_admins: "Apenas moderadores e admins" - members_mods_and_admins: "Apenas membros do grupo, moderadores e admins" + only_admins: "Apenas administradores" + mods_and_admins: "Apenas moderadores e Administradores" + members_mods_and_admins: "Apenas membros do grupo, moderadores e administradores" everyone: "Todos" user_action_groups: - '1': "Gostos dados" - '2': "Gostos recebidos" + '1': "Gostos Dados" + '2': "Gostos Recebidos" '3': "Marcadores" '4': "Tópicos" '5': "Mensagens" @@ -225,7 +224,7 @@ pt: all_subcategories: "todas" no_subcategory: "nenhuma" category: "Categoria" - posts: "Posts" + posts: "Mensagens" topics: "Tópicos" latest: "Mais recente" latest_by: "mais recente por" @@ -235,23 +234,23 @@ pt: topic_stat_sentence: one: "%{count} novo tópico no passado %{unit}." other: "%{count} novos tópicos no passado %{unit}." - post_stats: "Número de posts novos." + post_stats: "Número de mensagens novas." post_stat_sentence: - one: "%{count} novo post no passado %{unit}." - other: "%{count} novos posts no passado %{unit}." + one: "%{count} nova mensagem no passado %{unit}." + other: "%{count} novas mensagens no passado %{unit}." ip_lookup: title: Pesquisa de Endereço IP hostname: Hostname - location: Local + location: Localização location_not_found: (desconhecido) organisation: Organização - phone: Telefóne + phone: Telefone other_accounts: "Outras contas com este endereço IP:" delete_other_accounts: "Apagar %{count}" username: "nome de utilizador" trust_level: "TL" read_time: "tempo de leitura" - topics_entered: "entrada de tópicos" + topics_entered: "tópicos inseridos" post_count: "# mensagens" confirm_delete_other_accounts: "Tem a certeza que quer apagar estas contas?" user: @@ -259,9 +258,8 @@ pt: profile: "Perfil" mute: "Silenciar" edit: "Editar Preferências" - download_archive: "fazer o download do arquivo das minhas mensagens" - new_private_message: "Nova mensagem privada" - private_message: "Mensagem Particular" + new_private_message: "Nova Mensagem Privada" + private_message: "Mensagem Privada" private_messages: "Mensagens" activity_stream: "Atividade" preferences: "Preferências" @@ -272,36 +270,36 @@ pt: notifications: "Notificações" dismiss_notifications: "Marcar tudo como lido" dismiss_notifications_tooltip: "Marcar como lidas todas as notificações por ler" - disable_jump_reply: "Não vá para o seu novo post após ter respondido" + disable_jump_reply: "Não ir para a sua nova mensagem após ter respondido" dynamic_favicon: "Exibir notificações de novas mensagens no favicon (experimental)" edit_history_public: "Permitir que outros utilizadores vejam as minhas revisões de publicação" - external_links_in_new_tab: "Abrir todos os links externos numa nova aba" - enable_quoting: "Citar texto selecionado na resposta" + external_links_in_new_tab: "Abrir todas as hiperligações externas num novo separador" + enable_quoting: "Ativar resposta usando citação de texto destacado" change: "alterar" moderator: "{{user}} é um moderador" admin: "{{user}} é um administrador" moderator_tooltip: "Este utilizador é um moderador" - admin_tooltip: "Este utilizador é um admin" - suspended_notice: "Utilizador suspenso até {{date}}." - suspended_reason: "Razão: " + admin_tooltip: "Este utilizador é um administrador" + suspended_notice: "Este utilizador está suspenso até {{date}}." + suspended_reason: "Motivo: " github_profile: "Github" - mailing_list_mode: "Receber um email para cada post novo (salvo quando silenciar o tópico ou categoria)" + mailing_list_mode: "Receber um email por cada mensagem nova (salvo quando silenciar o tópico ou categoria)" watched_categories: "Observado" - watched_categories_instructions: "Observará automaticamente todos os novos tópicos nestas categorias. Será notificado de todos os novos posts e tópicos. Além disso, a contagem de posts novos e não lidos irá surgir ao lado da listagem do tópico. " - tracked_categories: "Seguido " - tracked_categories_instructions: "Seguirá automaticamente todos os tópicos novos nestas categorias. Uma contagem de posts novos e não lidos irá surgir ao lado do tópico." + watched_categories_instructions: "Irá vigiar automaticamente todos os novos tópicos nestas categorias. Será notificado de todos as novas mensagens e tópicos. Além disso, a contagem de mensagens novas e não lidas irá surgir junto do tópico. " + tracked_categories: "Acompanhado" + tracked_categories_instructions: "Acompanhará automaticamente todos os tópicos novos nestas categorias. Uma contagem de mensagens novas e não lidas irá surgir ao lado do tópico." muted_categories: "Silenciado" - muted_categories_instructions: "Não será notificado relativamente a novos tópicos nestas categorias, e não aparecerão na sua lista de não lidos." - delete_account: "Eliminar Conta" - delete_account_confirm: "Tem a certeza que quer eliminar de forma permanente a sua conta? Esta acção é definitiva!" + muted_categories_instructions: "Não será notificado relativamente a novos tópicos nestas categorias, e não aparecerão no seu separador de tópicos não lidos." + delete_account: "Eliminar A Minha Conta" + delete_account_confirm: "Tem a certeza que pretende eliminar a sua conta de forma permanente? Esta acção não pode ser desfeita!" deleted_yourself: "A sua conta foi eliminada com sucesso." - delete_yourself_not_allowed: "Neste momento não pode eliminar a sua conta. Contacte um administrador para que este elimine a sua conta por sí." + delete_yourself_not_allowed: "Neste momento não pode eliminar a sua conta. Contacte um administrador para que este elimine a sua conta por si." unread_message_count: "Mensagens" admin_delete: "Apagar" staff_counters: - flags_given: "denúncias úteis" - flagged_posts: "posts sinalizados" - deleted_posts: "posts eleminados" + flags_given: "sinalizações úteis" + flagged_posts: "mensagens sinalizadas" + deleted_posts: "mensagens eliminadas" suspensions: "suspensões" warnings_received: "avisos" messages: @@ -312,40 +310,40 @@ pt: success: "(email enviado)" in_progress: "(a enviar email)" error: "(erro)" - action: "Enviar email para recuperar a palavra-chave" - set_password: "Defenir Senha" + action: "Enviar email para recuperar a palavra-passe" + set_password: "Definir Palavra-passe" change_about: title: "Modificar Sobre Mim" change_username: title: "Alterar Nome de Utilizador" - confirm: "É possível haver consequências ao alterar o nome de utilizador. Tem a certeza?" - taken: "Desculpe, esse nome de utilizador já está a ser utilizado." - error: "Houve um erro ao alterar o seu nome de utilizador." + confirm: "Se mudar o seu nome de utilizador, todas as citações anteriores das suas mensagens e mençoes a @nome irão ser quebradas. Tem a certeza que deseja fazê-lo?" + taken: "Pedimos desculpa, esse nome de utilizador já está a ser utilizado." + error: "Ocorreu um erro ao alterar o seu nome de utilizador." invalid: "Esse nome de utilizador é inválido. Deve conter apenas números e letras." change_email: title: "Alterar Email" - taken: "Desculpe, esse email não é válido." - error: "Houve um erro ao alterar o email. Talvez ele já esteja a ser utilizado neste fórum?" - success: "Enviamos um email para esse endereço. Por favor siga as instruções de confirmação." + taken: "Pedimos desculpa, esse email não está disponível." + error: "Ocorreu um erro ao alterar o email. Talvez esse endereço já esteja a ser utilizado neste fórum?" + success: "Enviámos um email para esse endereço. Por favor siga as instruções de confirmação." change_avatar: - title: "Alterar o seu avatar" + title: "Altere o seu avatar" gravatar: "Gravatar, baseado em" - refresh_gravatar_title: "Actualize o seu Gravatar" + refresh_gravatar_title: "Atualize o seu Gravatar" letter_based: "Avatar atribuído pelo sistema" - uploaded_avatar: "Foto pessoal" - uploaded_avatar_empty: "Adicionar foto pessoal" + uploaded_avatar: "Foto personalizada" + uploaded_avatar_empty: "Adicionar foto personalizada" upload_title: "Enviar a sua foto" upload_picture: "Carregar Imagem" - image_is_not_a_square: "Alerta: nós cortamos a sua imagem, pois ela não era rectangular." + image_is_not_a_square: "Alerta: cortámos a sua imagem; não é quadrada." change_profile_background: title: "Fundo de Perfil" - instructions: "A imagem de fundo será centrada e terá por defeito uma largura de 850px." + instructions: "O fundo do perfil será centrado e terá por defeito uma largura de 850px." change_card_background: title: "Fundo do cartão de utilizador" instructions: "As imagens de fundo serão centradas e terão por defeito uma largura de 590px." email: title: "Email" - instructions: "Nunca mostrar ao público" + instructions: "Nunca mostrado ao público" ok: "Enviar-lhe-emos um email para confirmar" invalid: "Por favor introduza um endereço de email válido" authenticated: "O seu email foi autenticado por {{provider}}" @@ -370,14 +368,14 @@ pt: too_long: "O seu nome de utilizador é demasiado longo" checking: "A verificar a disponibilidade do nome de utilizador..." enter_email: 'Nome de utilizador encontrado, introduza o email correspondente' - prefilled: "Email corresponde com nome de utilizador registado" + prefilled: "Email corresponde com o nome de utilizador registado" locale: - title: "Idioma de Interface" + title: "Idioma da Interface" instructions: "Idioma da interface de utilizador. Será alterado quando atualizar a página." - default: "(predefindio)" + default: "(pré-definido)" password_confirmation: - title: "Palavra-chave novamente" - last_posted: "Última Entrada" + title: "Palavra-passe novamente" + last_posted: "Última Publicação" last_emailed: "Último Email" last_seen: "Visto" created: "Juntou-se" @@ -385,15 +383,15 @@ pt: location: "Localização" card_badge: title: "Medalha de cartão de utilizador" - website: "Site" + website: "Sitío da Internet" email_settings: "Email" email_digests: - title: "Quando não visitar o site, envie-me um email com um resumo das novidades:" + title: "Quando não visitar o site, enviar-me um email com um resumo das novidades:" daily: "diariamente" weekly: "semanalmente" - bi_weekly: "de duas em duas semanas" - email_direct: "Receber um email quando alguém o cita, responde às suas mensagens, ou menciona o seu @nome_de_utilizador" - email_private_messages: "Recebe um email quando alguém lhe envia uma mensagem particular" + bi_weekly: "a cada duas semanas" + email_direct: "Receber um email quando alguém o cita, responde às suas mensagens, ou menciona o seu @nomedeutilizador" + email_private_messages: "Recebe um email quando alguém lhe envia uma mensagem privada" email_always: "Não suprimir notificações de email quando estou ativo no site" other_settings: "Outros" categories_settings: "Categorias" @@ -407,7 +405,7 @@ pt: after_n_weeks: one: "criada na última semana" other: "criadas nas últimas {{count}} semanas" - auto_track_topics: "Seguir automaticamente os tópicos onde entra" + auto_track_topics: "Acompanhar automaticamente os tópicos onde entra" auto_track_options: never: "nunca" always: "sempre" @@ -418,16 +416,16 @@ pt: one: "passado 1 minuto" other: "passado {{count}} minutos" invited: - search: "escreva para procurar convites..." + search: "digite para procurar convites..." title: "Convites" - user: "Utilizadores convidados" - none: "Ainda não convidou alguém para aqui." - truncated: "Mostra os primeiros {{count}} convites." - redeemed: "Convites usados" - redeemed_at: "Compensado" + user: "Utilizadores Convidados" + none: "Ainda não convidou ninguém." + truncated: "A mostrar os primeiros {{count}} convites." + redeemed: "Convites Resgatados" + redeemed_at: "Resgatado" pending: "Convites Pendentes" topics_entered: "Tópicos Visualizados" - posts_read_count: "Entradas Vistas" + posts_read_count: "Mensagens Lidas" expired: "Este convite expirou." rescind: "Remover" rescinded: "Convite Removido" @@ -435,25 +433,23 @@ pt: reinvited: "Convite reenviado" time_read: "Tempo de Leitura" days_visited: "Dias Visitados" - account_age_days: "Idade da conta em dias" + account_age_days: "Idade da conta, em dias" create: "Enviar um Convite" bulk_invite: none: "Ainda não convidou ninguém. Pode enviar convites individuais, ou convidar um grupo de pessoas enviando um ficheiro com convites em massa." - text: "Convite em massa a partir do ficheiro" + text: "Convite em massa a partir de ficheiro" uploading: "Enviando..." - success: "Envio de ficheiro feito com sucesso, será notificado em breve do seu progresso." error: "Erro de envio '{{filename}}': {{message}}" password: - title: "Palavra-chave" - too_short: "A sua palavra-chave é muito curta." - common: "Essa senha é demasiada comum." - ok: "A sua palavra-chave parece estar OK." + title: "Palavra-passe" + too_short: "A sua palavra-passe é muito curta." + common: "Essa palavra-passe é demasiado comum." + ok: "A sua palavra-passe parece correta." instructions: "Pelo menos %{count} caracteres." - associated_accounts: "Contas associadas" ip_address: title: "Último endereço IP" registration_ip_address: - title: "Registo endreço IP" + title: "Endereço IP de registo" avatar: title: "Avatar" title: @@ -461,7 +457,7 @@ pt: filters: all: "Todos" stream: - posted_by: "Colocado por" + posted_by: "Publicado por" sent_by: "Enviado por" private_message: "mensagem privada" the_topic: "o tópico" @@ -484,14 +480,14 @@ pt: again: "Tentar Novamente" fixed: "Carregar Página" close: "Fechar" - assets_changed_confirm: "Este site foi actualizado. Recarregar agora para a versão mais recente?" - logout: "Foi-lhe feito 'log out'" + assets_changed_confirm: "Este site foi atualizado. Recarregar agora para a versão mais recente?" + logout: "A sua sessão estava encerrada." refresh: "Atualizar" read_only_mode: - enabled: "Um administrador activou o modo só de leitura. Pode continuar a navegar no site mas as interacções poderão não funcionar. " - login_disabled: "A função de login está desactivada enquanto o site se encontrar no modo só de leitura." - too_few_topics_notice: "Crie pelo menos 5 tópicos públicos e %{posts} posts públicos para iniciar a discussão. Novos utilizadores não poderão ganhar níveis de confiança se não existir conteúdo para lerem. Esta mensagem surge apenas para membros do staff." - learn_more: "sabe mais..." + enabled: "Um administrador ativou o modo só de leitura. Pode continuar a navegar no site mas as interações poderão não funcionar. " + login_disabled: "A função de início de sessão está desactivada enquanto o site se encontrar no modo só de leitura." + too_few_topics_notice: "Crie pelo menos 5 tópicos públicos e %{posts} mensagens públicas para iniciar a discussão. Novos utilizadores não poderão ganhar Níveis de Confiança se não existir conteúdo para lerem. Esta mensagem surge apenas para membros do pessoal." + learn_more: "saber mais..." year: 'ano' year_desc: 'tópicos criados nos últimos 365 dias' month: 'mês' @@ -503,20 +499,20 @@ pt: mute: Silenciar unmute: Reativar last_post: Última mensagem - last_post_lowercase: último post + last_post_lowercase: última mensagem summary: - enabled_description: "Está a ler um resumo deste tópico: os posts mais interessantes são determinados pela comunidade." + enabled_description: "Está a ver um resumo deste tópico: as mensagens mais interessantes são determinados pela comunidade." description: "Existem {{count}} respostas." - description_time: "Existem {{count}} respostas com um tempo de leitura estimado {{readingTime}} minutos." - enable: 'Sumarize este tópico' - disable: 'Mostrar todas as mensagens' + description_time: "Existem {{count}} respostas com um tempo de leitura estimado de {{readingTime}} minutos." + enable: 'Resumir Este Tópico' + disable: 'Mostrar Todas As Mensagens' deleted_filter: - enabled_description: "Este tópico contém posts eliminados, os quais foram ocultados." - disabled_description: "Tópicos eliminados no tópico são apresentados." - enable: "Ocultar Posts Eliminados" - disable: "Mostrar Posts Eliminados" + enabled_description: "Este tópico contém mensagens eliminados, as quais foram ocultadas." + disabled_description: "Mensagens eliminadas no tópico são exibidos." + enable: "Ocultar mensagens eliminadas" + disable: "Mostrar mensagens eliminadas" private_message_info: - title: "Conversas Privadas" + title: "Mensagem Privada" invite: "Convidar Outros..." remove_allowed_user: "Quer mesmo remover {{name}} desta mensagem privada?" email: 'Email' @@ -524,94 +520,94 @@ pt: last_seen: 'Visto' created: 'Criado' created_lowercase: 'criado' - trust_level: 'Nível de confiança' + trust_level: 'Nível de Confiança' search_hint: 'nome de utilizador, email ou endereço de IP' create_account: - title: "Criar Conta Nova" - failed: "Houve um erro, talvez este email já esteja registado, tente usar o link Esqueci-me da Palavra-chave." + title: "Criar Nova Conta" + failed: "Ocorreu um erro, talvez este email já esteja registado, tente usar a hiperligação \"Esqueci-me da Palavra-passe\"." forgot_password: - title: "Esqueci-me da Palavra-chave" - action: "Esqueci-me da minha palavra-chave" - invite: "Coloque seu nome de utilizador ou endereço de email, e enviar-lhe-emos um email para refazer a sua palavra-chave." - reset: "Recuperar Palavra-chave" - complete_username: "Se uma conta corresponder ao nome de utilizador %{username}, receberá um email com instruções sobre como redefinir rapidamente a sua senha." - complete_email: "Se uma conta corresponder %{email}, receberá um email com instruções sobre como redefinir rapidamente a sua senha." - complete_username_found: "Encontramos uma conta correspondente ao nome de utilizador %{username}, receberá em breve um email com instruções para repor a sua senha." - complete_email_found: "Encontramos uma conta correspondente a %{email}, receberá em breve um email com instruções para repor a sua senha." + title: "Esqueci-me da Palavra-passe" + action: "Esqueci-me da minha palavra-passe" + invite: "Insira o seu nome de utilizador ou endereço de email, e enviar-lhe-emos um email para refazer a sua palavra-passe." + reset: "Recuperar Palavra-passe" + complete_username: "Se uma conta corresponder ao nome de utilizador %{username}, receberá em pouco tempo um email com instruções para repor a sua palavra-passe." + complete_email: "Se uma conta corresponder %{email}, receberá em pouco tempo um email com instruções para repor a sua palavra-passe." + complete_username_found: "Encontrámos uma conta correspondente ao nome de utilizador %{username}, receberá em pouco tempo um email com instruções para repor a sua palavra-passe." + complete_email_found: "Encontrámos uma conta correspondente a %{email}, receberá em breve um email com instruções para repor a sua palavra-passe." complete_username_not_found: "Não existe nenhuma conta correspondente ao nome de utilizador %{username}" complete_email_not_found: "Não existe nenhuma conta correspondente a %{email}" login: title: "Entrar" username: "Utilizador" - password: "Palavra-chave" + password: "Palavra-passe" email_placeholder: "email ou nome de utilizador" - caps_lock_warning: "Caps Lock ligado" + caps_lock_warning: "Caps Lock está ligado" error: "Erro desconhecido" - blank_username_or_password: "Por favor insira o seu e-mail ou nome de utilizador, e senha." - reset_password: 'Recuperar palavra-chave' + blank_username_or_password: "Por favor insira o seu email ou nome de utilizador, e palavra-passe." + reset_password: 'Recuperar palavra-passe' logging_in: "A iniciar sessão..." or: "Ou" authenticating: "A autenticar..." - awaiting_confirmation: "A sua conta está a aguardar ativação. Utilize o link 'Esqueci a Palavra-chave' para pedir um novo link para ativar o email" - awaiting_approval: "A sua conta ainda não foi aprovada por um membro do staff. Receberá um email quando a sua conta for aprovada." - requires_invite: "Desculpe, o acesso a este fórum é permitido somente por convite de outro membro." - not_activated: "Não pode entrar ainda. Enviámos anteriormente um email de ativação para o endereço {{sentTo}}. Por favor siga as instruções contidas neste email para ativar a sua conta." + awaiting_confirmation: "A sua conta está a aguardar ativação. Utilize a hiperligação \"Esqueci a Palavra-passe\" para pedir um novo email de ativação." + awaiting_approval: "A sua conta ainda não foi aprovada por um membro do pessoal. Receberá um email quando a sua conta for aprovada." + requires_invite: "Pedimos desculpa, o acesso a este fórum é permitido somente por convite de outro membro." + not_activated: "Ainda não pode iniciar sessão. Enviámos anteriormente um email de ativação para o endereço {{sentTo}}. Por favor siga as instruções contidas nesse email para ativar a sua conta." not_allowed_from_ip_address: "Não pode iniciar sessão a partir desse endereço IP." resend_activation_email: "Carregue aqui para enviar o email de ativação novamente." - sent_activation_email_again: "Enviámos mais um email de ativação para o endereço {{currentEmail}}. Pode ser que demore alguns minutos; verifique sempre a sua pasta de spam ou lixo." + sent_activation_email_again: "Enviámos mais um email de ativação para o endereço {{currentEmail}}. Pode ser que demore alguns minutos; certifique-se que verifica a sua pasta de spam ou lixo." google: - title: "Entrar com Google" + title: "com Google" message: "A autenticar com Google (certifique-se de que os bloqueadores de popup estão desativados)" google_oauth2: - title: "Entrar com Google" - message: "Autenticando com Google (certifique-se de que os bloqueadores de popup estão desactivados)" + title: "com Google" + message: "A autenticar com Google (certifique-se de que os bloqueadores de popup estão desativados)" twitter: - title: "Entrar com Twitter" + title: "com Twitter" message: "A autenticar com Twitter (certifique-se de que os bloqueadores de popup estão desativados)" facebook: - title: "Entrar com Facebook" + title: "com Facebook" message: "A autenticar com o Facebook (certifique-se de que os bloqueadores de popup estão desativados)" yahoo: - title: "Entrar com Yahoo" + title: "com Yahoo" message: "A autenticar com Yahoo (certifique-se de que os bloqueadores de popup estão desativados)" github: title: "com GitHub" message: "A autenticar com GitHub (certifique-se de que os bloqueadores de popup estão desativados)" composer: - add_warning: "Este é aviso oficial." - posting_not_on_topic: "A que tópico gostaria de responder?" + add_warning: "Este é um aviso oficial." + posting_not_on_topic: "A que tópico quer responder?" saving_draft_tip: "a guardar" saved_draft_tip: "guardado" saved_local_draft_tip: "guardado localmente" similar_topics: "O seu tópico é similar a..." drafts_offline: "rascunhos offline" min_length: - need_more_for_title: "{{n}} no título" + need_more_for_title: "{{n}} para avançar para o título" need_more_for_reply: "{{n}} para avançar" error: - title_missing: "O Título é obrigatório" - title_too_short: "Título tem que ter no mínimo {{min}} caracteres." - title_too_long: "Título não pode conter mais que {{max}} caracteres." - post_missing: "O post não pode estar vazio" - post_length: "Post tem que ter no mínimo {{min}} caracteres." - category_missing: "Tem de escolher uma categoria" + title_missing: "O título é obrigatório" + title_too_short: "O título tem que ter pelo menos {{min}} caracteres." + title_too_long: "O tíítulo não pode conter mais do que {{max}} caracteres." + post_missing: "A mensagem não pode estar vazia" + post_length: "A mensagem tem que ter pelo menos {{min}} caracteres." + category_missing: "Tem que escolher uma categoria" save_edit: "Guardar alterações" - reply_original: "Responder no Tópico original" + reply_original: "Responder no Tópico Original" reply_here: "Responda Aqui" reply: "Responder" cancel: "Cancelar" create_topic: "Novo Tópico" - create_pm: "Mensagem privada" + create_pm: "Mensagem Privada" title: "Ou prima Ctrl+Enter" users_placeholder: "Adicionar um utilizador" title_placeholder: "Numa breve frase, de que se trata esta discussão?" edit_reason_placeholder: "Porque está a editar?" show_edit_reason: "(adicione a razão para a edição)" reply_placeholder: "Escreva a sua resposta aqui. Utilize Markdown ou BBCode para formatar. Arraste ou cole aqui uma imagem para enviar." - view_new_post: "Ver as suas novas mensagens." - saving: "A Gravar..." - saved: "Gravado!" - saved_draft: "Mensagem rascunho em progresso. Selecione para continuar." + view_new_post: "Ver a sua nova mensagem." + saving: "A Guardar..." + saved: "Guardado!" + saved_draft: "Rascunho da mensagem em progresso. Selecione para continuar." uploading: "A enviar..." show_preview: 'mostrar pré-visualização »' hide_preview: '« esconder pré-visualização' @@ -620,14 +616,14 @@ pt: bold_text: "texto em negrito" italic_title: "Itálico" italic_text: "texto em itálico" - link_title: "Link" - link_description: "digite a descrição do link aqui" - link_dialog_title: "Inserir Link" + link_title: "Hiperligação" + link_description: "digite a descrição da hiperligação aqui" + link_dialog_title: "Inserir Hiperligação" link_optional_text: "título opcional" quote_title: "Bloco de Citação" quote_text: "Bloco de Citação" code_title: "Texto pré-formatado" - code_text: "encaixar texto pré-formatado até 4 espaços" + code_text: "identar texto pré-formatado até 4 espaços" upload_title: "Enviar" upload_description: "digite aqui a descrição do ficheiro enviado" olist_title: "Lista numerada" @@ -638,9 +634,9 @@ pt: hr_title: "Barra horizontal" undo_title: "Desfazer" redo_title: "Refazer" - help: "Ajuda da edição Markdown" + help: "Ajuda de Edição Markdown" toggler: "esconder ou exibir o painel de composição" - admin_options_title: "Configurações opcionais de staff para este tópico" + admin_options_title: "Configurações opcionais do pessoal para este tópico" auto_close: label: "Tempo de fecho automático do tópico:" error: "Por favor introduza um valor válido." @@ -652,7 +648,7 @@ pt: examples: 'Introduza o número de horas (24).' notifications: title: "notificações de menção de @nome, respostas às suas mensagens e tópicos, mensagens privadas, etc" - none: "Não há notifcações neste momento." + none: "Não tem notificações neste momento." more: "ver notificações antigas" total_flagged: "mensagens sinalizadas totais" mentioned: "@

    {{username}} {{description}}

    " @@ -663,8 +659,8 @@ pt: liked: "

    {{username}} {{description}}

    " private_message: "

    {{username}} {{description}}

    " invited_to_private_message: "

    {{username}} {{description}}

    " - invitee_accepted: "

    {{username}} accepted your invitation

    " - moved_post: "

    {{username}} moved {{description}}

    " + invitee_accepted: "

    {{username}} aceitou o seu convite

    " + moved_post: "

    {{username}} moveu {{description}}

    " linked: "

    {{username}} {{description}}

    " granted_badge: "

    Ganhou '{{description}}'

    " upload_selector: @@ -675,24 +671,24 @@ pt: remote_tip: "hiperligação para imagem" remote_tip_with_attachments: "hiperligação para imagem ou ficheiro ({{authorized_extensions}})" local_tip: "Carregue para selecionar um ficheiro do seu dispositivo" - local_tip_with_attachments: "pressione para selecionar uma imagem ou ficheiro do seu dispositivo ({{authorized_extensions}})" - hint: "(poderá também arrastar o ficheiro para o editor para fazer o upload)" - hint_for_supported_browsers: "(também pode arrastar ou colar imagens no editor de forma a carregá-las)" + local_tip_with_attachments: "carregue para selecionar uma imagem ou ficheiro do seu dispositivo ({{authorized_extensions}})" + hint: "(pode também arrastar o ficheiro para o editor para fazer o upload)" + hint_for_supported_browsers: "(pode também arrastar ou colar imagens no editor de forma a carregá-las)" uploading: "A enviar" - image_link: "link para a imagem aponta para" + image_link: "hiperligação da imagem irá apontar para" search: - title: "pesquisar tópicos, posts, utilizadores, ou categorias" + title: "pesquisar tópicos, mensagens, utilizadores, ou categorias" no_results: "Não foi encontrado nenhum resultado." searching: "A procurar..." - post_format: "#{{post_number}} por {{username}}" + post_format: "#{{post_number}} de {{username}}" context: - user: "Procurar posts por @{{username}}" + user: "Procurar mensagens de @{{username}}" category: "Procurar na categoria \"{{category}}\"" topic: "Pesquisar este tópico" - private_messages: "Procurar mensagens privadas" + private_messages: "Pesquisar mensagens privadas" site_map: "ir para outra lista de tópicos ou categorias" go_back: 'voltar atrás' - not_logged_in_user: 'página de utilizador com resumo da actividade actual e preferências ' + not_logged_in_user: 'página de utilizador com resumo da atividade atual e preferências ' current_user: 'ir para a sua página de utilizador' starred: title: 'Favoritos' @@ -703,47 +699,49 @@ pt: bulk: reset_read: "Repor Leitura" delete: "Apagar Tópicos" - dismiss_posts: "Dispensar Posts" - dismiss_posts_tooltip: "Remover contagem de não lidos nestes tópicos, mas manter a apresentação dos mesmos no meu separador de não lidos quando são feitos novos posts" - dismiss_topics: "Dispensar Tópicos" - dismiss_topics_tooltip: "Parar a apresentação destes tópicos no meu separador de não lidos quando são feitos novos posts" - dismiss_new: "Dispensar Novo" - toggle: "activar selecção em massa de tópicos" - actions: "Acções de Selecção" + dismiss_posts: "Destituir mensagens" + dismiss_posts_tooltip: "Remover contagem de não lidos nestes tópicos, mas manter a exibição dos mesmos na minha lista de não lidos quando novas mensagens são criadas." + dismiss_topics: "Destituir Tópicos" + dismiss_topics_tooltip: "Parar a exibição destes tópicos na minha lista de não lidos quando são criadas novas mensagens" + dismiss_new: "Destituir Novo" + toggle: "ativar seleção em massa de tópicos" + actions: "Ações em Massa" change_category: "Mudar Categoria" close_topics: "Fechar Tópicos" archive_topics: "Arquivar tópicos" notification_level: "Mudar Nível de Notificação" selected: - one: "Seleccionou 1 tópico." - other: "Seleccionou {{count}} tópicos." + one: "Selecionou 1 tópico." + other: "Selecionou {{count}} tópicos." none: starred: "Não tem tópicos favoritos." - unread: "Há tópicos não lidos." - new: "Não há novos tópicos." + unread: "Tem tópicos não lidos." + new: "Não tem novos tópicos." read: "Ainda não leu nenhum tópico." - posted: "Ainda não escreveu nenhum tópico." - latest: "Não há tópicos populares. Isso é triste." + posted: "Ainda não publicou nenhum tópico." + latest: "Não há tópicos recentes. Isso é triste." hot: "Não há tópicos quentes." category: "Não há tópicos na categoria {{category}}." - top: "Não existem tópicos." + top: "Não existem tópicos populares." educate: - new: '

    Os tópicos novos serão apresentados aqui.

    Por defeito, os tópicos são considerados novo e mostrarão o indicador novo caso tenham sido criados nos últimos 2 dias.

    Pode alterar isto nas suas preferências.

    ' + new: '

    Os seus novos tópicos aparecem aqui.

    Por defeito, os tópicos são considerados novos e mostrarão o indicador novo caso tenham sido criados nos últimos 2 dias.

    Pode alterar isto nas suas preferências.

    ' + unread: '

    Os seus tópicos não lidos aparecem aqui.

    Por defeito, os tópicos são considerados não lidos e aparecem nas contagens de não lidos 1 Se:

    • Criou o tópico
    • Respondeu ao tópico
    • Leu o tópico por mais de 4 minutos

    Ou, se definiu explicitamente o tópico para acompanhar ou vigiar através do controlo de notificações que se encontra na parte inferior de cada tópico.

    Pode alterar isto nas suas preferências.

    ' + starred: '

    Os seus tópicos favoritos aparecem aqui.

    Para remover a estrela de um tópico, use:

    • o mais próxima do título de qualquer tópico
    • O botão de Estrela na parte final de cada tópico
    ' bottom: - latest: "Não há mais tópicos recentes." - hot: "Não mais tópicos quentes." - posted: "Não há mais tópicos inseridos." - read: "Não há mais tópicos lidos." - new: "Não há mais tópicos novos." - unread: "Não há mais tópicos não lidos." - starred: "Não existem mais tópicos nos favoritos." - category: "Não há mais tópicos na categoria {{category}}." + latest: "Não existem mais tópicos recentes." + hot: "Não existem mais tópicos quentes." + posted: "Não existem mais tópicos publicados." + read: "Não existem mais tópicos lidos." + new: "Não existem mais tópicos novos." + unread: "Não existem mais tópicos não lidos." + starred: "Não existem mais tópicos favoritos." + category: "Não existem mais tópicos na categoria {{category}}." top: "Não existem mais tópicos populares." topic: - filter_to: "{{post_count}} posts no tópico" - create: 'Novo tópico' + filter_to: "{{post_count}} mensagens no tópico" + create: 'Novo Tópico' create_long: 'Criar um novo Tópico' - private_message: 'Começar uma nova conversa privada' + private_message: 'Começar uma mensagem privada' list: 'Tópicos' new: 'novo tópico' unread: 'não lido' @@ -754,67 +752,67 @@ pt: one: '1 tópico não lido' other: '{{count}} tópicos não lidos' title: 'Tópico' - loading: 'A Carregar tópico...' + loading: 'A carregar tópico...' invalid_access: - title: "Tópico é particular" - description: "Desculpe, mas não tem acesso àquele tópico!" + title: "O tópico é privado" + description: "Pedimos desculpa, mas não tem acesso a esse tópico!" login_required: "Necessita de ter sessão iniciada para ver este tópico." server_error: title: "Falha ao carregar tópico" - description: "Desculpe, não conseguimos carregar este tópico, possivelmente devido a um problema na conexão. Por favor teste novamente. Se o problema persistir, diga-nos." + description: "Pedimos desculpa, não conseguimos carregar esse tópico, possivelmente devido a um problema na conexão. Por favor teste novamente. Se o problema persistir, avise-nos." not_found: title: "Tópico não encontrado" - description: "Desculpe, não foi possível encontrar esse tópico. Pode ser que ele tenha sido apagado?" + description: "Pedimos desculpa, não foi possível encontrar esse tópico. Talvez tenha sido removido por um moderador?" total_unread_posts: - one: "tem 1 post não lido neste tópico" - other: "tem {{count}} posts não lidos neste tópico" + one: "tem 1 mensagem não lido neste tópico" + other: "tem {{count}} mensagens não lidas neste tópico" unread_posts: - one: "você possui 1 posts antigo que não foi lido neste tópico" - other: "possui {{count}} mensagens antigas que não foram lidas neste tópico" + one: "tem 1 mensagem antiga não lida neste tópico" + other: "tem {{count}} mensagens antigas não lidas neste tópico" new_posts: - one: "há 1 nova postagem neste tópico desde a sua última leitura" - other: "há {{count}} novas mensagens neste tópico desde a sua última leitura" + one: "existe 1 nova mensagem neste tópico desde a sua última leitura" + other: "existem {{count}} novas mensagens neste tópico desde a sua última leitura" likes: - one: "há 1 curtida neste tópico" - other: "há {{count}} gostos neste tópico" - back_to_list: "Voltar à lista dos Tópicos" + one: "existe 1 gosto neste tópico" + other: "existem {{count}} gostos neste tópico" + back_to_list: "Voltar à lista de Tópicos" options: "Opções do Tópico" - show_links: "mostrar links dentro desta mensagem" + show_links: "mostrar hiperligações dentro deste tópico" toggle_information: "alternar detalhes do tópico" - read_more_in_category: "Pretende ler mais? Procurw outros tópicos em {{catLink}} ou {{latestLink}}." + read_more_in_category: "Pretende ler mais? Procure outros tópicos em {{catLink}} ou {{latestLink}}." read_more: "Pretende ler mais? {{catLink}} ou {{latestLink}}." read_more_MF: "There { UNREAD, plural, =0 {} one { is 1 unread } other { are # unread } } { NEW, plural, =0 {} one { {BOTH, select, true{and } false {is } other{}} 1 new topic} other { {BOTH, select, true{and } false {are } other{}} # new topics} } remaining, or {CATEGORY, select, true {browse other topics in {catLink}} false {{latestLink}} other {}}." - browse_all_categories: Procurar todas as categorias - view_latest_topics: ver tópicos mais recentes - suggest_create_topic: Que tal começar um assunto? + browse_all_categories: Pesquisar em todas as categorias + view_latest_topics: ver os tópicos mais recentes + suggest_create_topic: Porque não começar um tópico? jump_reply_up: avançar para resposta mais recente jump_reply_down: avançar para resposta mais antiga deleted: "Este tópico foi apagado" - auto_close_notice: "Este tópico vai ser automaticamente fechado em %{timeLeft}." - auto_close_notice_based_on_last_post: "Este tópico será fechado %{duration} depois da última resposta" + auto_close_notice: "Este tópico vai ser automaticamente encerrado em %{timeLeft}." + auto_close_notice_based_on_last_post: "Este tópico será encerrado %{duration} depois da última resposta" auto_close_title: 'Configurações para Fechar Automaticamente' auto_close_save: "Guardar" - auto_close_remove: "Não Fechar Automaticamente Este Tópico" + auto_close_remove: "Não Fechar Este Tópico Automaticamente" progress: title: progresso do tópico - go_top: "top" + go_top: "topo" go_bottom: "fim" go: "ir" - jump_bottom_with_number: "ir para o post %{post_number}" + jump_bottom_with_number: "avançar para a mensagem %{post_number}" total: total de mensagens current: mensagem atual - position: "post %{current} de %{total}" + position: "mensagem %{current} de %{total}" notifications: reasons: - '3_6': 'Receberá notificações porque está a acompanhar este tópico.' - '3_5': 'Receberá notificações por começou a acompanhar automaticamente este tópico.' - '3_2': 'Receberá notificações porque está a observar este tópico.' - '3_1': 'Receberá notificações porque criou este tópico.' - '3': 'Receberá notificações porque você está a acompanhar este tópico.' - '2_8': 'Receberá notificações porque está a monitorizar esta categoria.' - '2_4': 'Receberá notificações porque inseriu uma resposta neste tópico.' - '2_2': 'Receberá notificações porque está a monitorizar este tópico.' - '2': 'Receberá notificações porque leu este tópico.' + '3_6': 'Irá receber notificações porque está a vigiar esta categoria.' + '3_5': 'Irá receber notificações porque começou a vigiar automaticamente este tópico.' + '3_2': 'Irá receber notificações porque está a vigiar este tópico.' + '3_1': 'Irá receber notificações porque criou este tópico.' + '3': 'Irá receber notificações porque está a vigiar este tópico.' + '2_8': 'Irá receber notificações porque está a acompanhar esta categoria.' + '2_4': 'Irá receber notificações porque publicou uma resposta a este tópico.' + '2_2': 'Irá receber notificações porque está a acompanhar este tópico.' + '2': 'Irá receber notificações porque leu este tópico.' '1_2': 'Será notificado se alguém mencionar o seu @nome ou responder às suas mensagens' '1': 'Será notificado se alguém mencionar o seu @nome ou responder às suas mensagens' '0_7': 'Está a ignorar todas as notificações nesta categoria.' @@ -822,22 +820,28 @@ pt: '0': 'Está a ignorar todas as notificações para este tópico.' watching_pm: title: "Observar" + description: "Será notificado de todas as novas mensagens nesta mensagem privada. Será também apresentada uma contagem de mensagens novas e não lidas junto do tópico." watching: title: "Observar" + description: "Será notificado sobre todas as novas mensagens neste tópico. Será também apresentada uma contagem de mensagens novas e não lidas junto do tópico." tracking_pm: - title: "Seguir" + title: "Acompanhar" + description: "Será apresentada uma contagem de mensagens novas e não lidas junto da mensagem privada. Será notificado se alguém mencionar o seu @nome ou responder à sua mensagem." tracking: - title: "Monitorar" + title: "Acompanhar" + description: "Será apresentada uma contagem de mensagens novas e não lidas junto do tópico Será notificado se alguém mencionar o seu @nome ou responder à sua mensagem." regular: - title: "Regular" + title: "Habitual" + description: "Será notificado se alguém mencionar o seu @nome ou responder às suas mensagens." regular_pm: - title: "Regular" + title: "Habitual" + description: "Irá receber notificações se alguém mencionar o seu @nome ou responder à sua mensagem na mensagem privada." muted_pm: - title: "Silenciar" - description: "Não será notificado relativamente a nada sobre esta mensagem privada." + title: "Silenciado" + description: "Não será notificado de nada que esteja relacionado com esta mensagem privada." muted: - title: "Silenciar" - description: "Nunca será notificado relativamente ao quer que seja deste tópico, e não será apresentado no seu separador de 'não lido'." + title: "Silenciado" + description: "Não será notificado de nada que esteja relacionado com este tópico, e este não será apresentado no seu separador de 'não lido'." actions: recover: "Recuperar Tópico" delete: "Apagar Tópico" @@ -854,136 +858,139 @@ pt: invisible: "Tornar Não Listado" visible: "Tornar Listado" reset_read: "Repor Data de Leitura" - multi_select: "Seleccionar Posts" + multi_select: "Selecionar Mensagens" reply: title: 'Responder' help: 'começa a compor uma resposta a este tópico' clear_pin: title: "Remover destaque" - help: "Retirar destaque deste tópico para que ele não apareça mais no topo da sua lista de tópicos" + help: "Remover destaque deste tópico para que o mesmo deixe de aparecer no topo da sua lista de tópicos" share: title: 'Partilhar' - help: 'Partilhar um link para este tópico' + help: 'Partilhar uma hiperligação para este tópico' flag_topic: title: 'Denunciar' help: 'denunciar privadamente este tópico para consideração ou enviar uma notificação privada sobre este' success_message: 'Denúncia do tópico realizada com sucesso.' inviting: "A Convidar..." - automatically_add_to_groups_optional: "Este convite também inclui acesso a estes grupo: (opcional, apenas Admin)" - automatically_add_to_groups_required: "Esse convite também inclui acesso a estes grupos: (Obrigatório, apenas Admin)" + automatically_add_to_groups_optional: "Este convite também inclui acesso a estes grupos: (opcional, apenas Administração)" + automatically_add_to_groups_required: "Esse convite também inclui acesso a estes grupos: (Obrigatório, apenas Administração)" invite_private: - title: 'Convidar para Conversa Privada' + title: 'Convidar para Mensagem Privada' email_or_username: "Email ou Nome de Utilizador do Convidado" - email_or_username_placeholder: "endereço de email ou username" - action: "Convite" - success: "Convidamos esse utilizador para participar nesta conversa privada." - error: "Desculpe, houve um erro ao convidar esse utilizador." + email_or_username_placeholder: "endereço de email ou nome de utilizador" + action: "Convidar" + success: "Convidámos esse utilizador para participar nesta mensagem privada" + error: "Pedimos desculpa, ocorreu um erro ao convidar esse utilizador." group_name: "nome do grupo" invite_reply: title: 'Convidar' action: 'Email de Convite' - help: 'envie convites aos seus amigos para que eles possam responder a este tópico com um simples toque do rato.' - to_topic: "Enviaremos um breve email que permitirá ao seu amigo juntar-se imediatamente e responder a este tópico clicando num link, não sendo necessário ter sessão iniciada. " - to_forum: "Enviaremos um breve email que permitirá ao seu amigo juntar-se imediatamente clicando num link, não sendo necessário ter sessão iniciada." - email_placeholder: 'name@example.com' - success: "Enviamos um convite para {{email}}. Será notificado quando o convite for resgatado. Verifique o separador de convites na sua página de utilizador para poder acompanhar os seus convites." - error: "Desculpe, não podíamos convidar essa pessoa. Talvez já seja um utilizador?" - login_reply: 'Iniciar sessão para responder' + help: 'envie convites aos seus amigos para que estes possam responder a este tópico com um simples toque do rato.' + to_topic: "Enviaremos um breve email que permitirá ao seu amigo juntar-se imediatamente e responder a este tópico carregando numa hiperligação, não sendo necessário ter sessão iniciada. " + to_forum: "Enviaremos um breve email que permitirá ao seu amigo juntar-se imediatamente carregando numa hiperligação, não sendo necessário ter sessão iniciada." + email_placeholder: 'nome@exemplo.com' + success: "Enviámos um convite para {{email}}. Será notificado quando o convite for resgatado. Verifique o separador de convites na sua página de utilizador para poder acompanhar os seus convites." + error: "Pedimos desculpa, não conseguimos convidar essa pessoa. Talvez já seja um utilizador?" + login_reply: 'Iniciar sessão para Responder' filters: n_posts: - one: "1 postagem" + one: "1 mensagem" other: "{{count}} mensagens" + cancel: "Remover filtro" split_topic: - title: "Mover para um novo Tópico" - action: "mover para novo tópico" + title: "Mover para um Novo Tópico" + action: "mover para um novo tópico" topic_name: "Nome do Novo Tópico" - error: "Houve um erro ao mover as mensagens para um novo tópico." + error: "Ocorreu um erro ao mover as mensagens para um novo tópico." instructions: - one: "Você está prestes a criar um novo tópico e populá-lo com a postagem que você selecionou." + one: "Está prestes a criar um novo tópico e populá-lo com a mensagem que selecionou." other: "Está prestes a criar um novo tópico e populá-lo com as {{count}} mensagens que selecionou." merge_topic: title: "Mover para Tópico Existente" action: "mover para tópico existente" - error: "Houve um erro ao mover as mensagens para aquele tópico." + error: "Ocorreu um erro ao mover as mensagens para esse tópico." instructions: - one: "Por favor selecione o tópico para o qual você gostaria de mover esta postagem." + one: "Por favor selecione o tópico para o qual gostaria de mover esta mensagem." other: "Por favor selecione o tópico para o qual gostaria de mover estas {{count}} mensagens." change_owner: - title: "Mudar proprietário dos posts" + title: "Mudar Proprietário das Mensagens" action: "mudar titularidade" - error: "Verificou-se um erro na mudança de titularidade dos posts." - label: "Novo proprietário dos Posts" + error: "Ocorreu um erro na mudança de titularidade das mensagens." + label: "Novo Proprietário das Mensagens" placeholder: "nome de utilizador do novo proprietário" instructions: - one: "Por favor seleccione o novo titular do post por {{old_user}}." - other: "Por favor seleccione o novo titular dos {{count}} posts por {{old_user}}." - instructions_warn: "Atenção que nenhumas notificações relacionadas com este post serão transferidas retroactivamente para o novo utilizador.
    Aviso: Actualmente nenhum dado dependente do post é transferido para o novo utilizador. Usar com cautela." + one: "Por favor seleccione o novo titular da mensagem de {{old_user}}." + other: "Por favor selecione o novo titular das {{count}} mensagens de {{old_user}}." + instructions_warn: "Note que quaisquer notificações relacionadas com esta mensagem serão transferidas retroativamente para o novo utilizador.
    Aviso: Atualmente nenhum dado dependente da mensagem é transferido para o novo utilizador. Usar com cautela." multi_select: select: 'selecionar' selected: '({{count}}) selecionados' select_replies: 'selecione +respostas' delete: apagar selecionados cancel: cancelar seleção - select_all: 'seleccionar tudo ' + select_all: 'selecionar tudo ' deselect_all: desmarcar tudo description: - one: 1 postagem selecionada. - other: {{count}} mensagens selecionadas. + one: Selecionou 1 mensagem. + other: Selecionou {{count}} mensagens. post: - reply: "Em resposta a {{link}} por {{replyAvatar}} {{username}}" + reply: "Em resposta a {{link}} de {{replyAvatar}} {{username}}" reply_topic: "Responder a {{link}}" quote_reply: "citar resposta" - edit: "Editar {{link}}" - edit_reason: "Razão:" + edit: "Editar {{link}} de {{replyAvatar}} {{username}}" + edit_reason: "Motivo:" post_number: "mensagem {{number}}" in_reply_to: "responder a" last_edited_on: "mensagem editada pela última vez em" + reply_as_new_topic: "Responder como tópico hiperligado" continue_discussion: "Continuar a discussão desde {{postLink}}:" - follow_quote: "ir para a mensagem citada" - show_full: "Mostrar Post Completo" + follow_quote: "avançar para a mensagem citada" + show_full: "Mostrar Mensagem Completa" show_hidden: 'Ver conteúdo ocultado.' deleted_by_author: - one: "(postagens abandonadas pelo autor, serão removidas automaticamente em %{count} hora a menos que forem sinalizadas)" - other: "(mensagens abandonadas pelo autor, serão removidas automaticamente em %{count} horas a menos que sejam sinalizadas)" - expand_collapse: "expandir/encolher" + one: "(mensagens abandonadas pelo autor serão removidas automaticamente em %{count} hora a não ser que estejam sinalizadas)" + other: "(mensagens abandonadas pelo autor serão removidas automaticamente em %{count} horas a não ser que estejam sinalizadas)" + expand_collapse: "expandir/colapsar" gap: - one: "1 post ocultado" - other: "{{count}} posts ocultados" + one: "1 mensagem oculta" + other: "{{count}} mensagens ocultas" more_links: "{{count}} mais..." - unread: "Post não lido" + unread: "Mensagem não lida" has_replies: one: "Resposta" other: "Respostas" errors: - create: "Desculpe, houve um erro ao criar a sua mensagem. Por favor, tente outra vez." - edit: "Desculpe, houve um erro ao editar a sua mensagem. Por favor, tente outra vez." - upload: "Desculpe, houve um erro ao enviar esse ficheiro. Por favor, tente outra vez." - attachment_too_large: "Desculpe, o ficheiro que está a enviar é muito grande (o tamanho máximo permitido é {{max_size_kb}}kb)." - too_many_uploads: "Desculpe, apenas pode enviar um ficheiro de cada vez." - upload_not_authorized: "Desculpe, o tipo de ficheiro que está a enviar não está autorizado (extensões autorizadas: {{authorized_extensions}})." - image_upload_not_allowed_for_new_user: "Desculpe, novos utilizadores não podem enviar imagens." - attachment_upload_not_allowed_for_new_user: "Desculpe, utilizadores novos não podem enviar anexos." - attachment_download_requires_login: "Desculpe, necessita de ter sessão iniciada para descarregar anexos." + create: "Pedimos desculpa, ocorreu um erro ao criar a sua mensagem. Por favor, tente novamente." + edit: "Pedimos desculpa, ocorreu um erro ao editar a sua mensagem. Por favor, tente novamente." + upload: "Pedimos desculpa, ocorreu um erro ao enviar esse ficheiro. Por favor, tente novamente." + attachment_too_large: "Pedimos desculpa, o ficheiro que está a enviar é muito grande (o tamanho máximo permitido é {{max_size_kb}}kb)." + file_too_large: "Pedimos desculpa, o ficheiro que está a tentar enviar é muito grande (o tamanho máximo permitido é {{max_size_kb}}kb)." + too_many_uploads: "Pedimos desculpa, só pode enviar um ficheiro de cada vez." + upload_not_authorized: "Pedimos desculpa, o tipo de ficheiro que está a enviar não está autorizado (extensões autorizadas: {{authorized_extensions}})." + image_upload_not_allowed_for_new_user: "Pedimos desculpa, os novos utilizadores não podem enviar imagens." + attachment_upload_not_allowed_for_new_user: "Pedimos desculpa, os novos utilizadores não podem enviar anexos." + attachment_download_requires_login: "Pedimos desculpa, necessita de ter sessão iniciada para descarregar anexos." abandon: - confirm: "Tem a certeza que quer abandonar o seu post?" + confirm: "Tem a certeza que deseja abandonar a sua mensagem?" no_value: "Não, manter" yes_value: "Sim, abandonar" - via_email: "este post chegou por email" + via_email: "esta mensagem chegou por email" wiki: - about: "este post é um wiki; utilizadores comuns podem editá-lo" + about: "esta mensagem é uma wiki; utilizadores comuns podem editá-la" archetypes: save: 'Guardar as Opções' controls: - reply: "começa a compor uma resposta a este tópico" + reply: "começar a compor uma resposta a este tópico" like: "gostar deste tópico" - has_liked: "gostaste deste post" + has_liked: "gostou desta mensagem" undo_like: "desfazer gosto" edit: "editar este tópico" - edit_anonymous: "Desculpe, mas necessita de ter sessão iniciada para editar este post." + edit_anonymous: "Pedimos desculpa, mas necessita de ter sessão iniciada para editar esta mensagem." flag: "denunciar privadamente este post para consideração ou enviar uma notificação privada sobre este" delete: "apagar esta mensagem" undelete: "repor esta mensagem" - share: "partilhar um link para esta mensagem" + share: "partilhar uma hiperligação para esta mensagem" more: "Mais" delete_replies: confirm: @@ -991,12 +998,13 @@ pt: other: "Também quer remover as {{count}} respostas diretas a esta mensagem?" yes_value: "Sim, remover as respostas também" no_value: "Não, somente esta mensagem" - admin: "acções administrativas de post" - wiki: "Tornar Wiki" - unwiki: "Retirar Wiki" - convert_to_moderator: "Adicionar Cor do Staff" - revert_to_regular: "Remover Cor do Staff" + admin: "ações administrativas de mensagens" + wiki: "Fazer Wiki" + unwiki: "Remover Wiki" + convert_to_moderator: "Adicionar Cor do Pessoal" + revert_to_regular: "Remover Cor do Pessoal" rebake: "Reconstruir HTML" + unhide: "Mostrar" actions: flag: 'Sinalização' defer_flags: @@ -1007,65 +1015,65 @@ pt: spam: "Sinalizar também" inappropriate: "Sinalizar também" custom_flag: "Sinalizar também" - bookmark: "Adicionar marcador também" - like: "Dê um Gosto também" - vote: "Vote neste também" + bookmark: "Também adicionar marcador" + like: "Também adicionar um Gosto" + vote: "Também adicionar um voto" undo: off_topic: "Retirar sinalização" spam: "Retirar sinalização" inappropriate: "Retirar sinalização" bookmark: "Remover marcador" like: "Retirar gosto" - vote: "Retirar votar" + vote: "Retirar voto" people: - off_topic: "{{icons}} denunciar isto como off-topic" + off_topic: "{{icons}} denunciar isto como fora de contexto" spam: "{{icons}} denunciar isto como spam" inappropriate: "{{icons}} denunciar isto como inapropriado" - notify_moderators: "{{icons}} notificaram os moderadores" - notify_moderators_with_url: "{{icons}} notificaram os moderadores" - notify_user: "{{icons}} enviou uma mensagem particular" - notify_user_with_url: "{{icons}} enviou uma mensagem particular" - bookmark: "{{icons}} adicionaram marcador a isto" + notify_moderators: "{{icons}} moderadores notificados" + notify_moderators_with_url: "{{icons}} moderadores notificados" + notify_user: "{{icons}} enviou uma mensagem privada" + notify_user_with_url: "{{icons}} enviou uma mensagem privada" + bookmark: "{{icons}} adicionaram um marcador a isto" like: "{{icons}} gostaram disto" vote: "{{icons}} votaram nisto" by_you: - off_topic: "Sinalizou isto como off-topic" + off_topic: "Sinalizou isto como fora de contexto" spam: "Sinalizou isto como spam" inappropriate: "Sinalizou isto como inapropriado" notify_moderators: "Sinalizou isto para moderação" - notify_user: "Enviou uma mensagem particular para este utilizador" - bookmark: "Adicionou um marcador para esta mensagem" + notify_user: "Enviou uma mensagem privada a este utilizador" + bookmark: "Adicionou um marcador a esta mensagem" like: "Gostou disto" vote: "Votou nesta mensagem" by_you_and_others: off_topic: - one: "Você e mais 1 pessoa sinalizaram isto como off-topic" - other: "{{count}} pessoas sinalizaram isto como off-topic, contando consigo" + one: "Para além de si, 1 pessoa sinalizou isto como fora de contexto" + other: "Para além de si, {{count}} pessoas sinalizaram isto como fora de contexto" spam: - one: "Você e mais 1 pessoa sinalizaram isto como spam" - other: "{{count}} pessoas sinalizaram isto como spam, contando consigo" + one: "Para além de si, 1 pessoa sinalizou isto como spam" + other: "Para além de si, {{count}} pessoas sinalizaram isto como spam" inappropriate: - one: "Você e mais 1 pessoa sinalizaram isto como inapropriado" - other: "{{count}} pessoas sinalizaram isto como inapropriado, contando consigo" + one: "Para além de si, 1 pessoa sinalizou isto como inapropriado" + other: "Para além de si, {{count}} pessoas sinalizaram isto como inapropriado" notify_moderators: - one: "Você e mais 1 pessoa sinalizaram isto para moderação" - other: "{{count}} pessoas sinalizaram isto para moderação, contando consigo" + one: "Para além de si, 1 pessoa sinalizaram isto para moderação" + other: "Para além de si, {{count}} pessoas sinalizaram isto para moderação" notify_user: - one: "Você e mais 1 pessoa enviaram mensagens particulares para este usuário" - other: "{{count}} pessoas enviaram mensagens particulares para este utilizador, contando consigo" + one: "Para além de si, 1 pessoa enviou mensagens privadas a este utilizador" + other: "Para além de si, {{count}} pessoas enviaram mensagens privadas a este utilizador" bookmark: - one: "Você e mais 1 pessoa adicionaram um marcador a esta postagem" - other: "{{count}} adicionaram um marcador a esta mensagem, contando consigo" + one: "Para além de si, 1 pessoa adicionou um marcador a esta mensagem" + other: "Para além de si, {{count}} adicionaram um marcador a esta mensagem" like: - one: "Você e mais 1 pessoa curtiu isto" - other: "{{count}} pessoas gostaram disto, contando consigo" + one: "Para além de si, 1 pessoa gostou disto" + other: "Para além de si, {{count}} pessoas gostaram disto" vote: - one: "Você e mais 1 pessoa votaram nesta postagem" - other: "{{count}} pessoas votaram nesta mensagem, contando consigo" + one: "Para além de si, 1 pessoa votou nesta mensagem" + other: "Para além de si, {{count}} pessoas votaram nesta mensagem" by_others: off_topic: - one: "1 pessoa sinalizou isto como off-topic" - other: "{{count}} pessoas sinalizaram isto como off-topic" + one: "1 pessoa sinalizou isto como fora de contexto" + other: "{{count}} pessoas sinalizaram isto como fora de contexto" spam: one: "1 pessoa sinalizou isto como spam" other: "{{count}} pessoas sinalizaram isto como spam" @@ -1076,16 +1084,16 @@ pt: one: "1 pessoa sinalizou isto para moderação" other: "{{count}} pessoas sinalizaram isto para moderação" notify_user: - one: "1 pessoa enviou mensagem particular para este usuário" - other: "{{count}} enviaram mensagem particular para este utilizador" + one: "1 pessoa enviou uma mensagem privada a este utilizador" + other: "{{count}} enviaram mensagens privadas a este utilizador" bookmark: - one: "1 pessoa adicionou um marcador a esta postagem" + one: "1 pessoa adicionou um marcador a esta mensagem" other: "{{count}} pessoas adicionaram um marcador a esta mensagem" like: - one: "1 pessoa deu curtiu esta postagem" - other: "{{count}} pessoas gostaram desta mensagem" + one: "1 pessoa gostou disto" + other: "{{count}} pessoas gostaram disto" vote: - one: "1 pessoa votou nesta postagem" + one: "1 pessoa votou nesta mensagem" other: "{{count}} pessoas votaram nesta mensagem" edits: one: 1 edição @@ -1093,23 +1101,27 @@ pt: zero: sem edições delete: confirm: - one: "Tem certeza que quer apagar esta postagem?" - other: "Tem certeza que quer apagar todos essas mensagens?" + one: "Tem a certeza que quer apagar esta mensagem?" + other: "Tem a certeza que quer apagar todas essas mensagens?" revisions: controls: first: "Primeira revisão" previous: "Revisão anterior" - next: "Próximo revisão" + next: "Próxima revisão" last: "Última revisão" + hide: "Esconder revisão" + show: "Mostrar revisão" comparing_previous_to_current_out_of_total: "{{previous}} vs {{current}} / {{total}}" displays: inline: + title: "Mostrar o resultado renderizado com inserções e remoções em-linha." button: ' HTML' side_by_side: + title: "Mostrar o resultado renderizado das diferenças lado-a-lado" button: ' HTML' side_by_side_markdown: - title: "Show the raw source diffs side-by-side" - button: ' Raw' + title: "Mostrar em bruto a fonte das diferenças lado-a-lado" + button: ' Em bruto' details: edited_by: "Editado por" category: @@ -1122,72 +1134,87 @@ pt: general: 'Geral' settings: 'Configurações' delete: 'Apagar Categoria' + create: 'Nova Categoria' save: 'Guardar Categoria' - creation_error: Houve um erro durante a criação da categoria. - save_error: Houve um erro ao guardar a categoria. + creation_error: Ocorreu um erro durante a criação da categoria. + save_error: Ocorreu um erro ao guardar a categoria. name: "Nome da Categoria" description: "Descrição" topic: "tópico da categoria" logo: "Logótipo da Categoria" background_image: "Imagem de Fundo da Categoria" - badge_colors: "Cores da miniatura" + badge_colors: "Cores do distintivo" background_color: "Cor de fundo" foreground_color: "Cor frontal" name_placeholder: "Máximo de uma ou duas palavras" - color_placeholder: "Qualquer cor web" - delete_confirm: "Tem certeza que quer apagar esta categoria?" - delete_error: "Houve um erro ao apagar a categoria." + color_placeholder: "Qualquer cor da internet" + delete_confirm: "Tem a certeza que deseja apagar esta categoria?" + delete_error: "Ocorreu um erro ao apagar a categoria." list: "Lista de Categorias" - no_description: "Por favor adicione uma descrição para este categoria." + no_description: "Por favor adicione uma descrição para esta categoria." change_in_category_topic: "Editar Descrição" already_used: 'Esta cor já foi usada para outra categoria' security: "Segurança" images: "Imagens" - auto_close_label: "Fechar automaticamente tópicos depois de:" + auto_close_label: "Fechar tópicos automaticamente depois de:" auto_close_units: "horas" - email_in: "Endereço personalizado de entrada de email:" + email_in: "Endereço de email personalizado para emails recebidos:" email_in_allow_strangers: "Aceitar emails de utilizadores anónimos sem conta" - email_in_disabled: "Publicar tópicos novos via email está desactivado nas Definições do Site. Para permitir a publicação de novos tópicos por email," - email_in_disabled_click: 'active a definição "email in".' - allow_badges_label: "Permitir a atribuição de medalhas nesta categoria" + email_in_disabled: "Publicar novos tópicos através do email está desactivado nas Configurações do Sitío. Para permitir a publicação de novos tópicos através do email," + email_in_disabled_click: 'ative a definição "email em".' + allow_badges_label: "Permitir a atribuição de distintivos nesta categoria" edit_permissions: "Editar Permissões" add_permission: "Adicionar Permissões" this_year: "este ano" position: "posição" default_position: "Posição Padrão" - position_disabled: "As categorias serão apresentadas por ordem de actividade. Para controlar a ordenação das categorias nas listas," - position_disabled_click: 'active a definição "categoria em posição fixa".' - parent: "Categoria-Mãe" + position_disabled: "As categorias serão exibidas por ordem de actividade. Para controlar a ordenação das categorias nas listas," + position_disabled_click: 'ative a definição "categoria em posição fixa".' + parent: "Categoria Principal" notifications: watching: title: "Vigiar" + description: "Irá vigiar automaticamente todos os novos tópicos nestas categorias. Será notificado de todas as novas mensagens e tópicos. Além disso, a contagem de mensagens novas e não lidas irá surgir junto do tópico." tracking: title: "Acompanhar" + description: "Irá acompanhar automaticamente todos os novos tópicos nestas categorias. Uma contagem de mensagens novas e não lidas irá surgir junto do tópico." regular: - title: "Normal" + title: "Habitual" + description: "Será notificado se alguém mencionar o seu @nome ou responder às suas mensagens." muted: - title: "Silenciar" - description: "Não será notificado relativamente a nada deste tópico, e não aparecerão no seu separador de não lidos." + title: "Silenciado" + description: "Não será notificado relativamente acerca de novos tópicos nestas categorias, e não aparecerão no seu separador de não lidos." flagging: + title: 'Obrigado por ajudar a manter a nossa comunidade cívica!' + private_reminder: 'flags são privadas, apenasestão visíveis para os admins' action: 'Sinalizar Postagem' - take_action: "Tomar Atitude" + take_action: "Acionar" notify_action: 'Mensagem privada' delete_spammer: "Apagar Spammer" - delete_confirm: "Vai apagar %{posts} mensagens e %{topics} tópicos deste utilizador, remover a sua conta, bloquear acessos do seu endereço IP %{ip_address}, e adicionar o seu endereço de email %{email} a uma lista negra. Tem a certeza que este utilizador é de facto um spammer?" + delete_confirm: "Está prestes a apagar %{posts} mensagens e %{topics} tópicos deste utilizador, remover a sua conta, bloquear acessos do seu endereço IP %{ip_address}, e adicionar o seu endereço de email %{email} a uma lista negra. Tem a certeza que este utilizador é de facto um spammer?" yes_delete_spammer: "Sim, Apagar Spammer" + ip_address_missing: "(N/A)" + hidden_email_address: "(escondido)" submit_tooltip: "Submeter a denúncia privada" take_action_tooltip: "Atingir imediatamente o limite de denúncias, em vez de esperar por mais denúncias da comunidade" cant: "Desculpe, não é possível colocar uma sinalização neste momento." + formatted_name: + off_topic: "Está fora do contexto" + inappropriate: "É inapropriado" + spam: "É Spam" + custom_placeholder_notify_user: "Seja específico, seja construtivo e seja sempre amável." + custom_placeholder_notify_moderators: "Diga-nos especificamente quais são as suas preocupações, e forneça-nos hiperligações relevantes e exemplo se possível." custom_message: at_least: "insira pelo menos {{n}} caracteres" more: "{{n}} em falta..." - left: "{{n}} restantes" + left: "{{n}} remanescentes" flagging_topic: + title: "Obrigado por ajudar a manter a nossa comunidade cívica!" action: "Denunciar Tópico" notify_action: "Mensagem privada" topic_map: - title: "Sumário de Tópico" - links_shown: "mostrar todas as {{totalLinks}} ligações..." + title: "Sumário do Tópico" + links_shown: "mostrar todas as {{totalLinks}} hiperligações..." clicks: one: "1 clique" other: "%{count} cliques" @@ -1197,21 +1224,21 @@ pt: locked: help: "Este tópico está fechado; já não são aceites novas respostas" unpinned: - title: "Desmarcadao" - help: "Este tópico está desmarcado; será mostrado na ordem predefinida" + title: "Desafixado" + help: "Este tópico está desafixado; será mostrado na ordem pré-definida" pinned_globally: - title: "Marcado Globalmente" - help: "Este tópico esta marcado globalmente; ser mostrado no topo de todas as listas" + title: "Fixado Globalmente" + help: "Este tópico está fixado globalmente; será mostrado no topo de todas as listas" pinned: - title: "Marcado" - help: "Este tópico está marcado; será mostrada no topo da sua categoria" + title: "Fixado" + help: "Este tópico está fixado; será mostrada no topo da sua categoria" archived: - help: "Este tópico está arquivado; está bloqueado e não pode ser alterado" + help: "Este tópico está arquivado; está congelado e não pode ser alterado" invisible: - help: "Este tópico não está listado; não será apresentado na listagem de tópicos e poderá ser apenas acedido através de um link directo" + help: "Este tópico não está listado; não será apresentado na lista de tópicos e poderá ser acedido apenas através de uma hiperligação direta" posts: "Mensagens" - posts_lowercase: "posts" - posts_long: "há {{number}} mensagens neste tópico" + posts_lowercase: "mensagens" + posts_long: "existem {{number}} mensagens neste tópico" posts_likes_MF: | This topic has {count, plural, one {1 post} other {# posts}} {ratio, select, low {with a high like to post ratio} @@ -1226,28 +1253,31 @@ pt: activity: "Atividade" likes: "Gostos" likes_lowercase: "gostos" - likes_long: "há {{number}} gostos neste tópico" + likes_long: "existem {{number}} gostos neste tópico" users: "Utilizadores" users_lowercase: "utilizadores" category_title: "Categoria" history: "Histórico" changed_by: "por {{author}}" + raw_email: + title: "Email em bruto" + not_available: "Indisponível!" categories_list: "Lista de Categorias" filters: with_topics: "%{filter} tópicos" with_category: "%{filter} %{category} tópicos" latest: - title: "Populares" - help: "tópicos com posts recentes" + title: "Mais Recentes" + help: "tópicos com mensagens recentes" hot: title: "Quente" help: "uma seleção dos tópicos mais quentes" starred: title: "Favoritos" - help: "tópicos favoritos" + help: "tópicos que marcou como favoritos" read: title: "Lido" - help: "tópicos que leu" + help: "tópicos que leu, na ordem que os leu" categories: title: "Categorias" title_in: "Categoria - {{categoryName}}" @@ -1257,7 +1287,7 @@ pt: zero: "Não lido" one: "Não lido (1)" other: "Não lidos ({{count}})" - help: "tópicos que está actualmente a vigiar ou a acompanhar com posts não lidos" + help: "tópicos que está atualmente a vigiar ou a acompanhar com mensagens não lidas" lower_title_with_count: one: "1 não lido" other: "{{count}} não lidos" @@ -1272,39 +1302,39 @@ pt: other: "Novos ({{count}})" help: "tópicos criados nos últimos dias" posted: - title: "Minhas mensagens" - help: "tópicos nos quais enviou mensagem" + title: "As Minhas mensagens" + help: "tópicos nos quais publicou uma mensagem" category: title: zero: "{{categoryName}}" one: "{{categoryName}} (1)" other: "{{categoryName}} ({{count}})" - help: "tópicos populares na categoria {{categoryName}}" + help: "tópicos recentes na categoria {{categoryName}}" top: - title: "Top" - help: "os tópicos mais activos no último ano, mês, semana ou dia" + title: "Os Melhores" + help: "os tópicos mais ativos no último ano, mês, semana ou dia" yearly: - title: "Top Anual" + title: "Os Melhores do Ano" monthly: - title: "Top Mensal" + title: "Os Melhores Mensais" weekly: - title: "Top Semanal" + title: "Os Melhores Semanais" daily: - title: "Top Diário" + title: "Os melhores Diários" this_year: "Este ano" this_month: "Este mês" this_week: "Esta semana" today: "Hoje" - other_periods: "ver mais tópicos populares" - browser_update: 'Infelzimente, o seu browser é demasiado antigo para funcionar com este site. Por favor actualize o seu browser.' + other_periods: "ver mais tópicos principais" + browser_update: 'Infelizmente, o seu navegador é demasiado antigo para funcionar com este sitío. Por favor atualize o seu navegador.' permission_types: full: "Criar / Responder / Ver" create_post: "Responder / Ver" readonly: "Ver" admin_js: - type_to_filter: "escreva para filtrar..." + type_to_filter: "digite para filtrar..." admin: - title: 'Discourse Admin' + title: 'Administração Discourse' moderator: 'Moderador' dashboard: title: "Painel Administrativo" @@ -1312,23 +1342,23 @@ pt: version: "Versão" up_to_date: "Está atualizado!" critical_available: "Uma atualização crítica está disponível." - updates_available: "Atualizações estão disponíveis." + updates_available: "Há atualizações disponíveis." please_upgrade: "Por favor, atualize!" - no_check_performed: "Não foi feita verificação por atualizações. Certifique-se de o sidekiq está em execução." + no_check_performed: "Não foi feita nenhuma verificação por atualizações. Certifique-se que o sidekiq está em execução." stale_data: "Não foi feita verificação por atualizações ultimamente. Certifique-se de que o sidekiq está em execução." version_check_pending: "Parece que atualizou recentemente. Fantástico!" installed_version: "Instalado" - latest_version: "Última versão" + latest_version: "Mais recente" problems_found: "Alguns problemas foram encontrados na sua instalação do Discourse:" last_checked: "Última verificação" refresh_problems: "Atualizar" no_problems: "Nenhum problema encontrado." moderators: 'Moderadores:' - admins: 'Admins:' + admins: 'Administradores:' blocked: 'Bloqueado:' suspended: 'Suspenso: ' private_messages_short: "MPs" - private_messages_title: "Mensagens Particulares" + private_messages_title: "Mensagens Privadas" reports: today: "Hoje" yesterday: "Ontem" @@ -1338,8 +1368,13 @@ pt: 7_days_ago: "7 Dias Atrás" 30_days_ago: "30 Dias Atrás" all: "Tudo" + view_table: "tabela" + view_chart: "gráfico de barras" + refresh_report: "Atualizar relatório" + start_date: "Data de Início" + end_date: "Data final" commits: - latest_changes: "Últimas atualizações: atualize com frequência!" + latest_changes: "Últimas alterações: atualize com frequência!" by: "por" flags: title: "Sinalizações" @@ -1348,29 +1383,29 @@ pt: agree: "Aceitar" agree_title: "Confirmar esta denúncia como válida e correcta" agree_flag_modal_title: "Aceitar e..." - agree_flag_hide_post: "Aceitar (esconder post + enviar MP)" - agree_flag_hide_post_title: "Esconder este post e enviar automaticamente uma mensagem privada ao utilizador solicitando a edição do post com urgência" - agree_flag_restore_post: "Concordar (restaurar post)" - agree_flag_restore_post_title: "Restaurar este post" + agree_flag_hide_post: "Aceitar (esconder mensagem + enviar MP)" + agree_flag_hide_post_title: "Esconder esta mensagem e enviar automaticamente uma mensagem privada ao utilizador solicitando a edição da mensagem com urgência" + agree_flag_restore_post: "Concordar (restaurar mensagem)" + agree_flag_restore_post_title: "Restaurar esta mensagem" agree_flag: "Aceitar a denúncia" - agree_flag_title: "Aceitar a denúncia e manter o post inalterado" + agree_flag_title: "Aceitar a denúncia e manter a mensagem inalterada" defer_flag: "Diferir" defer_flag_title: "Remover esta denúncia; neste momento não requer qualquer acção." delete: "Apagar" - delete_title: "Apagar o post associado a esta denúncia." - delete_post_defer_flag: "Apagar post e diferir a denúncia" - delete_post_defer_flag_title: "Apagar post; apagar o tópico se se tratar do primeiro post" - delete_post_agree_flag: "Apagar post e aceitar a denúncia" - delete_post_agree_flag_title: "Apagar post; apagar tópico se se tratar do primeiro post" + delete_title: "Apagar a mensagem associada a esta denúncia." + delete_post_defer_flag: "Apagar mensagem e diferir a denúncia" + delete_post_defer_flag_title: "Apagar mensagem; apagar o tópico se se tratar da primeira mensagem" + delete_post_agree_flag: "Apagar mensagem e aceitar a denúncia" + delete_post_agree_flag_title: "Apagar mensagem; apagar tópico se se tratar da primeira mensagem" delete_flag_modal_title: "Apagar e..." delete_spammer: "Apagar Spammer" - delete_spammer_title: "Remover utilizador e todos os posts e tópicos do mesmo." - disagree_flag_unhide_post: "Rejeitar (exibir post)" + delete_spammer_title: "Remover utilizador e todos as mensagens e tópicos do mesmo." + disagree_flag_unhide_post: "Rejeitar (exibir mensagens)" disagree_flag_unhide_post_title: "Remover qualquer denúncia deste post e tornar o post novamente visível" - disagree_flag: "Rejeitar" + disagree_flag: "Discordar" disagree_flag_title: "Negar esta denúncia como invalida ou incorrecta" clear_topic_flags: "Concluído" - clear_topic_flags_title: "Este tópico foi investigado e os problemas foram resolvidos. Clique Concluído para remover as denúncias." + clear_topic_flags_title: "Este tópico foi investigado e os problemas foram resolvidos. Carregue em Concluído para remover as denúncias." more: "(mais respostas...)" dispositions: agreed: "aceite" @@ -1378,25 +1413,25 @@ pt: deferred: "diferido" flagged_by: "Sinalizado por" resolved_by: "Resolvido por" - took_action: "Tomou acção" + took_action: "Realizou uma ação" system: "Sistema" error: "Aconteceu um erro" reply_message: "Responder" no_results: "Não há sinalizações." topic_flagged: "Este topic foi denunciado." - visit_topic: "Visitar tópico para tomar medidas" - was_edited: "Post foi editado após a primeira denúncia" - previous_flags_count: "Este já foi denunciado {{count}} vezes." + visit_topic: "Visitar tópico para acionar medidas" + was_edited: "A mensagem foi editado após a primeira denúncia" + previous_flags_count: "Esta mensagem já foi denunciada {{count}} vezes." summary: action_type_3: - one: "off-topic" - other: "off-topic x{{count}}" + one: "fora do contexto" + other: "fora do contexto x{{count}}" action_type_4: one: "inapropriado" other: "inapropriado x{{count}}" action_type_6: - one: "customizado" - other: "customizados x{{count}}" + one: "personalizado" + other: "personalizado x{{count}}" action_type_7: one: "personalizado" other: "personalizado x{{count}}" @@ -1408,18 +1443,18 @@ pt: no_primary: "(nenhum grupo primário)" title: "Grupos" edit: "Editar Grupos" - refresh: "Actualizar" + refresh: "Atualizar" new: "Novo" selector_placeholder: "adicionar utilizadores" - name_placeholder: "Nome do grupo, sem espaços, regras iguais ao nome de utilizador" - about: "Editar participação no grupo e nomes aqui" + name_placeholder: "Nome do grupo, sem espaços, com as mesmas regras do nome de utilizador" + about: "Editar aqui a sua participação e nomes no grupo" group_members: "Membros do grupo" delete: "Apagar" delete_confirm: "Apagar este grupo?" delete_failed: "Impossível apagar grupo. Se se trata de um grupo automático, não pode ser eliminado." api: - generate_master: "Gerar chave API Master" - none: "Não existem chave API activas neste momento." + generate_master: "Gerar Chave Mestra API " + none: "Não existem chaves API ativas neste momento." user: "Utilizador" title: "API" key: "Chave API" @@ -1428,23 +1463,23 @@ pt: revoke: "Revogar" confirm_regen: "Tem a certeza que quer substituir essa chave API por uma nova?" confirm_revoke: "Tem a certeza que quer revogar essa chave?" - info_html: "A sua chave de API permitirá a criação e edição de tópicos usando requests JSON." + info_html: "A sua chave API permitirá a criação e edição de tópicos usando pedidos JSON." all_users: "Todos os Utilizadores" - note_html: "Manter esta chave secreta, todos os utilizadores que a tenham poderão criar posts arbitrários como qualquer utilizador." + note_html: "Manter esta chave secreta, todos os utilizadores que a tenham poderão criar mensagens arbitrárias como qualquer utilizador." backups: - title: "Backups" + title: "Cópias de Segurança" menu: - backups: "Backups" + backups: "Cópias de Segurança" logs: "Logs" - none: "Nenhum backup disponível." + none: "Nenhuma cópia de segurança disponível." read_only: enable: - title: "Activar o modo apenas leitura" - text: "Activa modo apenas leitura" - confirm: "Tem a certeza que quer activar o modo apenas leitura?" + title: "Ativar o modo só de leitura" + text: "Ativar modo só de leitura" + confirm: "Tem a certeza que quer ativar o modo só de leitura?" disable: - title: "Desactivar o modo apenas leitura" - text: "Desactivar modo apenas leitura" + title: "Desativar o modo só de leitura" + text: "Desativar o modo só de leitura" logs: none: "Nenhuns logs ainda..." columns: @@ -1456,52 +1491,62 @@ pt: success: "'{{filename}}' foi carregado com sucesso." error: "Verificou-se um erro no carregamento de '{{filename}}': {{message}}" operations: - is_running: "Existe actualmente uma operação em andamento..." - failed: "A {{operation}} falhou. Por favor verifique os logs." + is_running: "Existe atualmente uma operação em execução..." + failed: "A {{operation}} falhou. Por favor verifique o registo dos logs." cancel: text: "Cancelar" - title: "Cancelar a operação actual" - confirm: "Tem a certeza que deseja cancelar a operação actual?" + title: "Cancelar a operação atual" + confirm: "Tem a certeza que deseja cancelar a operação atual?" backup: - text: "Backup" - title: "Criar um backup" - confirm: "Deseja criar um novo backup?" + text: "Cópia de Segurança" + title: "Criar uma cópia de segurança" + confirm: "Deseja criar uma nova cópia de segurança?" + without_uploads: "Sim (não incluir ficheiros)" download: text: "Descarregar" title: "Descarregar o backup" destroy: text: "Apagar" title: "Remover o backup" - confirm: "Tem a certeza que deseja destruir o backup?" + confirm: "Tem a certeza que deseja destruir esta cópia de segurança?" restore: - is_disabled: "Opção de restauro encontra-se desactivada nas definições do site." + is_disabled: "A opção de restauro encontra-se desativada nas configurações do sítio." text: "Restaurar" - title: "Restaurar o backup" - confirm: "Tem a certeza que deseja restaurar este backup?" + title: "Restaurar a cópia de segurança" + confirm: "Tem a certeza que deseja restaurar esta cópia de segurança?" rollback: text: "Reverter" title: "Reverter a base de dados para um estado anterior operacional" confirm: "Tem a certeza que deseja reverter a base de dados para um estado anterior operacional?" export_csv: - failed: "Exportação falhou. Por favor verifique os logs." + failed: "A exportação falhou. Por favor verifique os registos dos logs." + button_text: "Exportar" + button_title: + user: "Exportar lista total de utilizadores em formato CSV." + staff_action: "Exportar registo total das acções de início de sessão do pessoal em formato CSV." + screened_email: "Exportar lista total de emails visíveis em formato CSV." + screened_ip: "Exportar lista total de IP visíveis em formato CSV." + screened_url: "Exportar lista total de URL visíveis em formato CSV." customize: title: "Personalizar" - long_title: "Personalizações do Site" + long_title: "Personalizações do Sitío" + css: "CSS" header: "Cabeçalho" - override_default: "Sobrepor padrão?" - enabled: "Habilitado?" + footer: "Rodapé" + override_default: "Não incluir a folha de estilo por defeito" + enabled: "Ativado?" preview: "pré-visualização" undo_preview: "remover pré-visualização" - rescue_preview: "estilo padrão" - explain_preview: "Ver o site com a folha de estilo personalizada" - explain_undo_preview: "Voltar atrás para a actual folha de estilo personalizada activada" - explain_rescue_preview: "Ver o site com a folha de estilo padrão" + rescue_preview: "estilo por defeito" + explain_preview: "Ver o site com esta folha de estilo personalizada" + explain_undo_preview: "Voltar atrás para a atual folha de estilo personalizada ativa" + explain_rescue_preview: "Ver o site com a folha de estilo por defeito" save: "Guardar" new: "Novo" new_style: "Novo Estilo" delete: "Apagar" delete_confirm: "Apagar esta personalização?" - about: "Modificar folha de estilo CSS e cabeçalhos HTML no site. Adicionar personalização para iniciar." + about: "Modificar folha de estilo CSS e cabeçalhos HTML no sitío. Adicionar personalização para iniciar." color: "Cor" opacity: "Opacidade" copy: "Copiar" @@ -1510,13 +1555,13 @@ pt: long_title: "Personalizações CSS e HTML" colors: title: "Cores" - long_title: "Esquemas de cores" - about: "Modificar as cores usadas no site sem escrever CSS. Adicionar um esquema para iniciar." + long_title: "Esquemas de Cores" + about: "Modificar as cores usadas no sitío sem escrever CSS. Adicionar um esquema para iniciar." new_name: "Novo Esquema de Cores" - copy_name_prefix: "Cópia" + copy_name_prefix: "Cópia de" delete_confirm: "Apagar este esquema de cor?" - undo: "anular" - undo_title: "Anular alterações a esta cor desde a última gravação." + undo: "desfazer" + undo_title: "Desfazer as alterações a esta cor desde a última gravação." revert: "reverter" revert_title: "Repor esta cor para o esquema de cor padrão do Discourse." primary: @@ -1527,46 +1572,51 @@ pt: description: 'A principal cor de fundo, e cor do texto de alguns botões.' tertiary: name: 'terciária' - description: 'Links, alguns botões, notificações, e cores tônicas.' + description: 'Hiperligações, alguns botões, notificações, e cores acentuadas.' quaternary: name: "quaternária" - description: "Links de navegação." + description: "Hiperligações de navegação." header_background: name: "fundo do cabeçalho" - description: "Cor de fundo do cabeçalho do site." + description: "Cor de fundo do cabeçalho do sitío." header_primary: - name: "cabeçalho primária" - description: "Texto e ícones no cabeçalho do site." + name: "cabeçalho primário" + description: "Texto e ícones no cabeçalho do sitío." highlight: name: 'destaque' - description: 'A cor de fundo de elementos destacados na página, tais como posts e tópicos.' + description: 'A cor de fundo de elementos destacados na página, tais como mensagens e tópicos.' danger: name: 'perigo' - description: 'Cor de destaque para acções como apagar posts e tópicos.' + description: 'Cor de destaque para ações como apagar mensagens e tópicos.' success: name: 'sucesso' - description: 'Usado para indicar que uma acção foi bem sucedida.' + description: 'Usado para indicar que uma ação foi bem sucedida.' love: name: 'amor' description: "A cor do botão 'gosto'." + wiki: + name: 'wiki' + description: "Cor base utilizada para o fundo de mensagens wiki" email: title: "Email" settings: "Configurações" all: "Todos" sending_test: "A enviar Email de teste..." - test_error: "Houve um problema no envio do email de teste. Por favor verifique novamente as suas definições de email, verifique se o seu host não está a bloquear conexões de email, e tente novamente." + error: "ERRO - %{server_error}" + test_error: "Occorreu um problema no envio do email de teste. Por favor verifique novamente as suas definições de email, verifique se o seu host não está a bloquear conexões de email, e tente novamente." sent: "Enviado" skipped: "Ignorado" - sent_at: "Enviado para " + sent_at: "Enviado em" time: "Tempo" user: "Utilizador" email_type: "Tipo de Email" - to_address: "Para (endereço)" + to_address: "Endereço Para" test_email_address: "endereço de email para testar" send_test: "Enviar Email de Teste" sent_test: "enviado!" delivery_method: "Método de Entrega" - preview_digest: "Pré-vizualizar modo Digest" + preview_digest: "Pré-visualizar Resumo" + preview_digest_desc: "Pre-visualizar o conteúdo do resumo semanal de emails enviados para utilizadores inactivos" refresh: "Atualizar" format: "Formato" html: "html" @@ -1579,7 +1629,7 @@ pt: filters: title: "Filtrar" user_placeholder: "nome de utilizador" - address_placeholder: "name@example.com" + address_placeholder: "nome@exemplo.com" type_placeholder: "resumo, subscrever..." reply_key_placeholder: "chave de resposta" skipped_reason_placeholder: "motivo" @@ -1588,8 +1638,10 @@ pt: action: "Ação" created_at: "Criado" last_match_at: "Última Correspondência" - match_count: "Resultados" + match_count: "Correspondência" ip_address: "IP" + topic_id: "ID do Tópico" + post_id: "ID da Mensagem" delete: 'Apagar' edit: 'Editar' save: 'Guardar' @@ -1597,32 +1649,36 @@ pt: block: "bloquear" do_nothing: "não fazer nada" staff_actions: - title: "Ações do Staff" - instructions: "Selecione os nomes de utilizador e as ações para filtrar a lista. Clique nos avatares para ir para as páginas dos utilizadores." + title: "Ações do Pessoal" + instructions: "Selecione os nomes de utilizador e as ações para filtrar a lista. Carregue nos avatars para ir para as páginas dos utilizadores." clear_filters: "Mostrar Tudo" - staff_user: "Utilizador do Staff" + staff_user: "Utilizador do Pessoal" target_user: "Utilizador Destino" subject: "Assunto" when: "Quando" context: "Contexto" details: "Detalhes" previous_value: "Anterior" - new_value: "Nova" + new_value: "Novo" diff: "Diferenças" show: "Exibir" modal_title: "Detalhes" no_previous: "Não há valor anterior." - deleted: "Não há valor novo. O registo foi removido." + deleted: "Não há nenhum valor novo. O registo foi removido." actions: delete_user: "removeu utilizador" - change_trust_level: "modificou nível de confiança" - change_site_setting: "alterar configurações do site" - change_site_customization: "alterar personalização do site" - delete_site_customization: "remover personalização do site" + change_trust_level: "modificar Nível de Confiança" + change_site_setting: "alterar configurações do sitío" + change_site_customization: "alterar personalização do sitío" + delete_site_customization: "remover personalização do sitío" suspend_user: "utilizador suspenso" unsuspend_user: "utilizador não suspenso" - grant_badge: "conceder medalha" - revoke_badge: "revogar medalha" + grant_badge: "conceder distintivo" + revoke_badge: "revogar distintivo" + check_email: "verificar email" + delete_topic: "apagar tópico" + delete_post: "apagar mensagem" + impersonate: "personificar" screened_emails: title: "Emails Filtrados" description: "Quando alguém tenta criar uma nova conta, os seguintes endereços de email serão verificados e o registo será bloqueado, ou outra ação será executada." @@ -1638,75 +1694,90 @@ pt: title: "IPs Filtrados" description: 'Endereços IP que estão sob observação. Utilize "Permitir" para aprovar os endereços IP.' delete_confirm: "Tem a certeza que quer remover esta regra para %{ip_address}?" + roll_up_confirm: "Tem a certeza que quer trazer os endereços IP frequentemente vistoriados para as sub-redes?" + rolled_up_some_subnets: "Interdições das sub-redes %{subnets} inseridas com sucesso." + rolled_up_no_subnet: "Não há nada para atualizar." actions: block: "Bloquear" do_nothing: "Permitir" - allow_admin: "Permitir Admin" + allow_admin: "Permitir Administração" form: label: "Novo:" ip_address: "Endereço IP" add: "Adicionar" + roll_up: + text: "Adicionar" + title: "Cria interdições de sub-redes se existir pelo menos 'min_ban_entries_for_roll_up' entradas." logster: - title: "Erros de logo de dados" + title: "Registo de Erros em Logs" impersonate: title: "Personificar" - help: "Usar este ferramente de forma personificar uma conta de utilizador para fins de depuração. Terá de fechar a sessão assim que terminar." + help: "Utilize este ferramenta de forma a personificar uma conta de utilizador para fins de depuração. Terá de encerrar a sessão assim que terminar." users: title: 'Utilizadores' - create: 'Adicionar Utilizador Admin' + create: 'Adicionar Utilizador da Admnistração' last_emailed: "Último email enviado" - not_found: "Desculpe, esse nome de utilizador não existe no nosso sistema." + not_found: "Pedimos desculpa, esse nome de utilizador não existe no nosso sistema." + id_not_found: "Pedimos desculpa, esse id de utilizador não existe no nosso sistema." active: "Ativo" + show_emails: "Mostrar Emails" nav: - new: "Novos" - active: "Ativos" - pending: "Pendentes" + new: "Novo" + active: "Ativo" + pending: "Pendente" + staff: 'Pessoal' suspended: 'Suspenso' - blocked: 'Bloqueados' + blocked: 'Bloqueado' + suspect: 'Suspeito' approved: "Aprovado?" approved_selected: - one: "aprovar usuário" + one: "aprovar utilizador" other: "aprovar utilizadores ({{count}})" reject_selected: - one: "rejeitar usuário" + one: "rejeitar utilizador" other: "rejeitar utilizadores ({{count}})" titles: active: 'Utilizadores Ativos' new: 'Utilizadores Novos' pending: 'Utilizadores com Confirmação Pendente' - newuser: 'Utilizadores no Nível de Confiança 0 (Utilizador Novo)' + newuser: 'Utilizadores no Nível de Confiança 0 (Novo Utilizador)' basic: 'Utilizadores no Nível de Confiança 1 (Utilizador Básico)' - regular: 'Utilizadores no Nível 2 de Confiança (Membro)' - leader: 'Utilizadores no Nível 3 de Confiança (Regular)' - elder: 'Utilizadores no Nível 4 de Confiança (Líder)' - admins: 'Utilizadores Administradores' + regular: 'Utilizadores no Nível de Confiança 2 (Membro)' + leader: 'Utilizadores no Nível de Confiança 3 (Habitual)' + elder: 'Utilizadores no Nível de Confiança 4(Líder)' + staff: "Pessoal" + admins: 'Utilizadores da Administração' moderators: 'Moderadores' blocked: 'Utilizadores Bloqueados' suspended: 'Utilizadores Suspensos' + suspect: 'Utilizadores Suspeitos' reject_successful: - one: "1 usuário foi rejeitado com sucesso." + one: "1 utilizador foi rejeitado com sucesso." other: "%{count} utilizadores foram rejeitados com sucesso." reject_failures: - one: "Falha ao rejeitar 1 usuário." + one: "Falha ao rejeitar 1 utilizador." other: "Falha ao rejeitar %{count} utilizadores." not_verified: "Não verificado" + check_email: + title: "Revelar o endereço de email deste utilizador" + text: "Mostrar" user: - suspend_failed: "Houve um erro ao suspender este utilizador {{error}}" - unsuspend_failed: "Houve um erro ao respirar a suspensão a este utilizador {{error}}" + suspend_failed: "Ocorreu um erro ao suspender este utilizador {{error}}" + unsuspend_failed: "Ocorreu um erro ao retirar a suspensão deste utilizador {{error}}" suspend_duration: "Durante quanto tempo o utilizador estará suspenso?" suspend_duration_units: "(dias)" - suspend_reason_label: "Qual o motivo da suspensão? Este texto estará visível para todos na página do perfil do utilizador, e será mostrada ao utilizador quando tentar o login. Mantenha-o breve." - suspend_reason: "Razão" - suspended_by: "Suspenso por" + suspend_reason_label: "Qual é o motivo da sua suspensão? Este texto estará visível para todos na página do perfil deste utilizador, e será mostrada ao utilizador quando tentar iniciar sessão. Mantenha-o breve." + suspend_reason: "Motivo" + suspended_by: "Suspendido por" delete_all_posts: "Apagar todas as mensagens" delete_all_posts_confirm: "Está prestes a apagar %{posts} mensagens e %{topics} tópicos. Tem a certeza de que quer continuar?" - suspend: "Suspenso" - unsuspend: "Não suspenso" - suspended: "Suspenso?" + suspend: "Suspender" + unsuspend: "Retirar a suspensão" + suspended: "Suspendido?" moderator: "Moderador?" - admin: "Admin?" + admin: "Administração?" blocked: "Bloqueado?" - show_admin_profile: "Admin" + show_admin_profile: "Administração" edit_title: "Editar Título" save_title: "Guardar Título" refresh_browsers: "Forçar atualização da página no browser" @@ -1714,10 +1785,10 @@ pt: show_public_profile: "Mostrar Perfil Público" impersonate: 'Personificar' ip_lookup: "Pesquisa de IP" - log_out: "Terminar sessão" - logged_out: "Sessão do utilizador terminada em todos os utilizadores" - revoke_admin: 'Revogar Admin' - grant_admin: 'Conceder Admin' + log_out: "Sair" + logged_out: "Sessão do utilizador encerrada em todos os dispositivos" + revoke_admin: 'Revogar Administração' + grant_admin: 'Conceder Administração' revoke_moderation: 'Revogar Moderação' grant_moderation: 'Conceder Moderação' unblock: 'Desbloquear' @@ -1730,7 +1801,7 @@ pt: private_topics_count: Tópicos Privados posts_read_count: Mensagens lidas post_count: Mensagens criadas - topics_entered: Tópicos Vistos + topics_entered: Tópicos Visualizados flags_given_count: Sinalizações dadas flags_received_count: Sinalizações recebidas warnings_received_count: Avisos Recebidos @@ -1758,133 +1829,177 @@ pt: delete_failed: "Houve um erro ao apagar o utilizador. Certifique-se de que todas as suas mensagens foram apagadas antes de tentar apagá-lo." send_activation_email: "Enviar Email de Ativação" activation_email_sent: "Um email de ativação foi enviado." - send_activation_email_failed: "Houve um problema ao enviar um novo email de ativação. %{error}" + send_activation_email_failed: "Ocorreu um problema ao enviar um novo email de ativação. %{error}" activate: "Ativar Conta" - activate_failed: "Houve um problema ao tornar o utilizador ativo." + activate_failed: "Ocorreu um problema ao ativar o utilizador." deactivate_account: "Desativar Conta" - deactivate_failed: "Houve um problema ao desativar o utilizador." - unblock_failed: 'Houve um problema ao desbloquear o utilizador.' - block_failed: 'Houve um problema ao bloquear o utilizador.' + deactivate_failed: "Ocorreu um problema ao desativar o utilizador." + unblock_failed: 'Ocorreu um problema ao desbloquear o utilizador.' + block_failed: 'Ocorreu um problema ao bloquear o utilizador.' deactivate_explanation: "Um utilizador desativado deve revalidar o seu email." suspended_explanation: "Um utilizador suspenso não pode fazer login." - block_explanation: "Um utilizador bloqueado não pode enviar mensagens ou iniciar tópicos." - trust_level_change_failed: "Houve um problema ao trocar o nível de confiança do utilizador." + block_explanation: "Um utilizador bloqueado não pode publicar mensagens ou iniciar tópicos." + trust_level_change_failed: "Ocorreu um problema ao alterar o Nível de Confiança do utilizador." suspend_modal_title: "Utilizador Suspenso" - trust_level_2_users: "Utilizadores no Nível 2 de Confiança" - trust_level_3_requirements: "Nível 3 de Confiança: Requisitos" + trust_level_2_users: "Utilizadores no Nível de Confiança 2" + trust_level_3_requirements: "Requisitos do Nível de Confiança 3" + trust_level_locked_tip: "o Nível de Confiança está bloqueado, o sistema não irá promover ou despromover o utilizador" + trust_level_unlocked_tip: "o Nível de Confiança está desbloqueado, o sistema poderá promover ou despromover o utilizador" + lock_trust_level: "Bloquear Nível de Confiança" + unlock_trust_level: "Desbloquear Nível de Confiança" tl3_requirements: - title: "Requisitos para o Nível 3 de Confiança" + title: "Requisitos para o Nível de Confiança 3" table_title: "Nos últimos 100 dias:" value_heading: "Valor" requirement_heading: "Requisito" visits: "Visitas" days: "dias" - topics_replied_to: "Respostas a Tópicos" - topics_viewed: "Tópicos Vistos" - topics_viewed_all_time: "Tópicos Vistos (desde sempre)" - posts_read: "Posts Lidos" - posts_read_all_time: "Posts Lidos (desde sempre)" - flagged_posts: "Posts Denunciados" + topics_replied_to: "Tópicos com Respostas" + topics_viewed: "Tópicos Visualizados" + topics_viewed_all_time: "Tópicos Visualizados (desde sempre)" + posts_read: "Mensagens lidas" + posts_read_all_time: "Mensagens lidas (desde sempre)" + flagged_posts: "Mensagens denunciadas" flagged_by_users: "Utilizadores Que Denunciaram" likes_given: "Gostos Dados" likes_received: "Gostos Recebidos" - qualifies: "Qualifica-se para nível 3 de confiança." - does_not_qualify: "Não se qualifica para o nível 3 de confiança." + likes_received_days: "Gostos recebidos: dias únicos" + likes_received_users: "Gostos recebidos: utilizadores únicos" + qualifies: "Qualifica-se para Nível de Confiança 3." + does_not_qualify: "Não se qualifica para o nível de confiança 3." + will_be_promoted: "Será promovido brevemente." + will_be_demoted: "Será despromovido brevemente." + on_grace_period: "Atualmente no período de carência da promoção, não será despromovido." + locked_will_not_be_promoted: "Nível de Confiança bloqueado. Nunca será promovido." + locked_will_not_be_demoted: "Nível de Confiança bloqueado. Nunca será despromovido." sso: - title: "Single Sign On" + title: "Inscrição Única" external_id: "ID Externo" - external_username: "Nome de utilizador" + external_username: "Nome de Utilizador" external_name: "Nome" external_email: "Email" external_avatar_url: "URL do Avatar" + user_fields: + title: "Campos de utilizador" + help: "Adicione campos que os seus utilizadores poderão preencher." + create: "Criar Campo de Utilizador" + untitled: "Sem título" + name: "Nome do Campo" + type: "Tipo do Campo" + description: "Descrição do Campo" + save: "Guardar" + edit: "Editar" + delete: "Apagar" + cancel: "Cancelar" + delete_confirm: "Tem a certeza que quer eliminar esse campo de utilizador?" + required: + title: "Obrigatório na inscrição?" + enabled: "obrigatório" + disabled: "não obrigatório" + editable: + title: "Editável depois da inscrição?" + enabled: "editável" + disabled: "não editável" + field_types: + text: 'Campo de Texto' + confirm: 'Confirmação' + site_text: + none: "Escolha um tipo de conteúdo para começar a editar." + title: 'Conteúdo do Texto' site_settings: show_overriden: 'Apenas mostrar valores alterados' - title: 'Configurações do Site' + title: 'Configurações' reset: 'repor' none: 'nenhum' no_results: "Não foi encontrado nenhum resultado." - clear_filter: "Remover" + clear_filter: "Limpar" categories: all_results: 'Todos' required: 'Necessário' - basic: 'Configuração básica' + basic: 'Configuração Básica' users: 'Utilizadores' - posting: 'Escrever mensagem' + posting: 'A publicar' email: 'Email' files: 'Ficheiros' trust: 'Níveis de Confiança' security: 'Segurança' - onebox: "Onebox" + onebox: "Caixa Única" seo: 'SEO' spam: 'Spam' - rate_limits: 'Limites de classificação' + rate_limits: 'Limites de Classificação' developer: 'Programador' embedding: "Incorporação" legal: "Legal" uncategorized: 'Outro' - backups: "Backups" + backups: "Cópias de Segurança" login: "Entrar" badges: - title: Medalhas - new_badge: Nova Medalha + title: Distintivos + new_badge: Novo Distintivo new: Novo name: Nome - badge: Medalha - display_name: Apresentar Nome + badge: Distintivo + display_name: Exibir Nome description: Descrição - badge_type: Tipe de Medalha + badge_type: Tipo de Distintivo badge_grouping: Grupo badge_groupings: - modal_title: Agrupamento de Medalhas + modal_title: Agrupamento de Distintivos granted_by: Concedido Por granted_at: Concedido Em save: Guardar delete: Apagar delete_confirm: Tem a certeza que quer eliminar esta medalha? revoke: Revogar - revoke_confirm: Tem a certeza que quer revogar esta medalha? - edit_badges: Editar Medalhas - grant_badge: Conceder Medalha - granted_badges: Medalhas Concedidas + revoke_confirm: Tem a certeza que quer revogar este distintivo? + edit_badges: Editar Distintivos + grant_badge: Conceder Distintivo + granted_badges: Distintivos Concedidos grant: Conceder - no_user_badges: "%{name} não recebeu qualquer medalha." - no_badges: Não existe qualquer medalha que pode ser concedida. - allow_title: Permitir o uso de medalha como um título - multiple_grant: Pode ser concedido diversas vezes - listable: Mostrar medalha no página pública de medalhas - enabled: Ativar medalha - icon: Icon - query: Query de Medalha (SQL) - target_posts: Query direccionado a posts - auto_revoke: 'Correr diariamente query de revogação ' - show_posts: Mostrar post de concessão de medalha na página de medalha - trigger: Accionar + no_user_badges: "%{name} não recebeu qualquer distintivo." + no_badges: Não existe qualquer distintivo que possa ser concedido. + none_selected: "Selecione um distintivo para iniciar" + allow_title: Permitir o uso de distintivos como título + multiple_grant: Pode ser concedido múltiplas vezes + listable: Mostrar distintivo na página pública de distintivos + enabled: Ativar distintivos + icon: Ícone + image: Imagem + icon_help: "Use uma classe Font Awesome ou um URL para uma imagem" + query: '"Query" de Distintivo (SQL)' + target_posts: '"Query" direcionada a mensagens' + auto_revoke: 'Executar diariamente a "query" de revogação ' + show_posts: Mostrar mensagens de concessão de distintivo na página de distintivos + trigger: Acionar trigger_type: - none: "Actualizado diariamente" - post_action: "Quando um utilizador actua num post" - post_revision: "Quando um utilizador edita ou cria um post" - trust_level_change: "Quando um utilizador muda de nível de confiança" + none: "Atualizado diariamente" + post_action: "Quando um utilizador atua numa mensagem" + post_revision: "Quando um utilizador edita ou cria uma mensagem" + trust_level_change: "Quando um utilizador muda de Nível de Confiança" user_change: "Quando um utilizador é editado ou criado" preview: - link_text: "Pré-visualizar medalhas concedidas" - plan_text: "Pré-visualizar com plano query" - modal_title: "Pré-visualização do Query da Medalha" - sql_error_header: "Houve um erro com o query." - error_help: "Para ajuda com queries de medalhas, ver os seguintes links" + link_text: "Pré-visualizar distintivos concedidos" + plan_text: "Pré-visualizar com plano de consulta" + modal_title: "Pré-visualização da \"Query\" de Distintivo" + sql_error_header: "Ocorreu um erro com a \"query\"." + error_help: "Veja as seguintes hiperligações para obter ajuda com \"queries\" de distintivos" bad_count_warning: header: "AVISO!" + text: "Estão em falta amostras de concessão. Isto acontece quando a \"query\" do sistema de distintivos devolve IDs de nomes de utilizador ou IDs de mensagens que não existem. Isto pode causar resultados inesperados futuramente, sendo que deverá rever a sua \"query\"." grant_count: - zero: "Nenhumas medalhas a atribuir." - one: "1 medalha a atribuir." - other: "%{count} medalhas a atribuir." + zero: "Nenhuns distintivos a atribuir." + one: "1 distintivo a atribuir." + other: "%{count} distintivos a atribuir." sample: "Amostra:" grant: with: %{username} - with_post: %{username} por o post em %{link} - with_post_time: %{username} por o post em %{link} às %{time} + with_post: %{username} pela mensagem em %{link} + with_post_time: %{username} pela mensagem em %{link} às %{time} with_time: %{username} às %{time} lightbox: download: "descarregar" + search_help: + title: 'Pesquisar Ajuda' keyboard_shortcuts_help: title: 'Atalhos de Teclado' jump_to: @@ -1895,50 +2010,55 @@ pt: unread: 'g, u Não lido' starred: 'g, f Favoritos' categories: 'g, c Categorias' - top: 'g, t Início' + top: 'g, t Melhores' navigation: title: 'Navegação' jump: '# Ir para o post #' back: 'u Retroceder' - up_down: 'k/j Mover selecção ↑ ↓' - open: 'o ou Enter Abrir tópico seleccionado' - next_prev: 'shift j/shift k Próxima seccção ou secção anterior' + up_down: 'k/j Mover seleção ↑ ↓' + open: 'o ou Enter Abrir tópico selecionado' application: title: 'Aplicação' create: 'c Criar um novo tópico' notifications: 'n Abrir notificações' - site_map_menu: '= Abrir menu do site' + site_map_menu: '= Abrir menu do sítio' user_profile_menu: 'p Abrir menu do utilizador' - show_incoming_updated_topics: '. Mostrar tópicos actualizados' + show_incoming_updated_topics: '. Mostrar tópicos atualizados' search: '/ Pesquisar' help: '? Abrir ajuda do teclado' - dismiss_new_posts: 'x, r Dispensar Novos/Posts' - dismiss_topics: 'x, t Dispensar Tópicos' + dismiss_new_posts: 'x, r Destituir Novos/Mensagens' + dismiss_topics: 'x, t Destituir Tópicos' actions: - title: 'Acções' + title: 'Ações' star: 'f Adicionar tópico aos favoritos' - share_topic: 'shift s Partilhar tópico' - share_post: 's Partilhar tópico' - reply_topic: 'shift r Responder ao tópico' - reply_post: 'r Responder ao post' - quote_post: 'q Citar post' - like: 'l Gostar do post' - flag: '! Denunciar post' - bookmark: 'b Adicionar post aos marcadores' - edit: 'e Editar post' - delete: 'd Apagar post' + share_post: 's Partilhar mensagem' + reply_as_new_topic: 't Responder como tópico hiperligado' + reply_post: 'r Responder à mensagem' + quote_post: 'q Citar mensagem' + like: 'l Gostar da mensagem' + flag: '! Denunciar mensagem' + bookmark: 'b Adicionar mensagem aos marcadores' + edit: 'e Editar mensagem' + delete: 'd Apagar mensagem' mark_muted: 'm, m Silenciar tópico' + mark_regular: 'm, r Tópico Habitual (por defeito)' + mark_tracking: 'm, t Acompanhar tópico' + mark_watching: 'm, w Vigiar este tópico' badges: - title: Medalhas - allow_title: "permitir uso de medalha como título?" - multiple_grant: "conceder múltiplas vezes?" + title: Distintivos + allow_title: "permitir uso de distintivo como título?" + multiple_grant: " premiado múltiplas vezes?" badge_count: - one: "1 Medalha" - other: "%{count} Medalhas" + one: "1 Distintivo" + other: "%{count} Distintivos" + more_badges: + one: "+1 Mais" + other: "+%{count} Mais" granted: one: "1 concedida" other: "%{count} concedidas" - select_badge_for_title: Selecionar uma medalha para usar como título + select_badge_for_title: Selecionar um distintivo para usar como título + none: "" badge_grouping: getting_started: name: Dar Início @@ -1948,14 +2068,24 @@ pt: name: Nível de Confiança other: name: Outro + posting: + name: A publicar badge: editor: name: Editor - description: Primeira edição de um post + description: Primeira edição de uma mensagem + basic_user: + name: Básico + description: Atribuídas todas as funções comunitárias essenciais member: name: Membro + description: Atribuídos convites + regular: + name: Habitual + description: Atribuída re-categorização, renomeação, seguimento de hiperligações e lounge leader: name: Líder + description: Atribuída a edição, destaque, encerramento, arquivo, separação e junção globais welcome: name: Bem-vindo description: Recebeu um gosto @@ -1963,50 +2093,59 @@ pt: name: Autobiógrafo description: 'Preencheu informações no perfil de utilizador ' nice_post: - name: Bom Post - description: Recebeu 10 gostos num post. Esta medalha pode ser atribuída diversas vezes + name: Boa Mensagem + description: Recebeu 10 gostos numa mensagem. Este distintivo pode ser concedido diversas vezes good_post: - name: Óptimo Post - description: 'Recebeu 25 gostos num post. Esta medalha pode ser atribuída diversas vezes ' + name: Ótima Mensagem + description: 'Recebeu 25 gostos numa mensagem. Este distintivo pode ser concedido diversas vezes ' great_post: - name: Excelente Post - description: Recebeu 50 gostos num post. Esta medalha pode ser atribuída diversas vezes + name: Excelente Mensagem + description: Recebeu 50 gostos numa mensagem. Este distintivo pode ser concedido diversas vezes nice_topic: name: Bom Tópico - description: Recebeu 10 gostos num tópico. Esta medalha pode ser atribuída diversas vezes + description: Recebeu 10 gostos num tópico. Este distintivo pode ser concedido diversas vezes good_topic: - name: Óptimo Tópico - description: 'Recebeu 25 gostos num tópico. Esta medalha pode ser atribuída diversas vezes ' + name: Ótimo Tópico + description: 'Recebeu 25 gostos num tópico. Este distintivo pode ser concedido diversas vezes ' great_topic: name: Excelente Tópico - description: Recebeu 50 gostos num tópico. Esta medalha pode ser atribuída diversas vezes + description: Recebeu 50 gostos num tópico. Este distintivo pode ser concedido diversas vezes nice_share: name: Boa Partilha - description: Partilhou um post com 25 visitantes únicos + description: Partilhou uma mensagem com 25 visitantes únicos good_share: - name: Óptima Partilha - description: Partilhou um post com 300 visitantes únicos + name: Ótima Partilha + description: Partilhou uma mensagem com 300 visitantes únicos great_share: name: Excelente Partilha - description: Partilhou um post com 1000 visitantes únicos + description: Partilhou uma mensagem com 1000 visitantes únicos first_like: name: Primeiro Gosto - description: Gostou de um post + description: Gostou de uma mensagem first_flag: name: Primeira Denúncia - description: Denunciou um post + description: Denunciou uma mensagem first_share: name: Primeira Partilha - description: Partilhou um post + description: Partilhou uma mensagem first_link: - name: Primeiro Link - description: Adicionou um link interno para outro tópico + name: Primeira Hiperligação + description: Adicionou uma hiperligação interna para outro tópico first_quote: name: Primeira Citação description: Citou um utilizador read_guidelines: - name: Ler Directrizes - description: Leu as directrizes da comunidade + name: Ler Diretrizes + description: Leu as diretrizes da comunidade reader: name: Leitor - description: Leu todos os posts num tópico com mais de 100 posts + description: Ler todas as mensagens num tópico com mais de 100 mensagens + google_search: | +

    Pesquisar com o Google

    +

    + + + + + +

    diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml index 6b98b41b08..a86a282f66 100644 --- a/config/locales/client.pt_BR.yml +++ b/config/locales/client.pt_BR.yml @@ -84,7 +84,6 @@ pt_BR: other: "%{count} dias atrás" share: topic: 'compartilhe o link desse tópico' - post: 'compartilhe o link da resposta #%{postNumber}' close: 'fechar' twitter: 'compartilhe este link no Twitter' facebook: 'compartilhe este link no Facebook' @@ -259,7 +258,6 @@ pt_BR: profile: "Perfil" mute: "Silenciar" edit: "Editar Preferências" - download_archive: "baixar o arquivo das minhas mensagens" new_private_message: "Nova mensagem privada" private_message: "Mensagem Privada" private_messages: "Mensagens" @@ -441,7 +439,6 @@ pt_BR: none: "Você ainda não convidou ninguém. Você pode enviar convites individuais, ou enviar vários de uma vez através da ferramenta de enviar em massa." text: "Convidar em massa a partir de arquivo" uploading: "Subindo..." - success: "Arquivo enviado com sucesso, você será notificado em breve sobre o progresso." error: "Houve um erro ao enviar '{{filename}}': {{message}}" password: title: "Senha" @@ -449,7 +446,6 @@ pt_BR: common: "Essa senha é muito comum." ok: "A sua senha parece boa." instructions: "Deve ter pelo menos %{count} caracteres." - associated_accounts: "Contas associadas" ip_address: title: "Último endereço IP" registration_ip_address: @@ -1523,7 +1519,6 @@ pt_BR: title: "Reverter o banco de dados para seu estado anterior" confirm: "Tem certeza de que quer reverter o banco de dados para o estado anterior?" export_csv: - success: "Exportação iniciada, você será notificado brevemente com o progresso." failed: "Falha na exportação. Por favor verifique os logs." button_text: "Exportar" customize: @@ -2010,7 +2005,6 @@ pt_BR: back: 'u Voltar' up_down: 'k/j Move seleção ↑ ↓' open: 'o ou Enter Abre tópico selecionado' - next_prev: 'shift j/shift k Sessão próxima/anterior' application: title: 'Aplicação' create: 'c Criar um tópico novo' @@ -2025,11 +2019,8 @@ pt_BR: actions: title: 'Ações' star: 'f Tópico favorito' - pin_unpin_topic: 'shift p Fixar/Desfixar o tópico' - share_topic: 'shift s Compartilhar tópico' share_post: 's Compartilhar mensagem' reply_as_new_topic: 't Responder como tópico linkado' - reply_topic: 'shift r Responder ao tópico' reply_post: 'r Responder a mensagem' quote_post: 'q Citar resposta' like: 'l Curtir a mensagem' diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index 87483e9bd5..fba1827d27 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -100,7 +100,6 @@ ru: other: "%{count} дней назад" share: topic: 'поделиться ссылкой на тему' - post: 'поделить ссылкой на сообщение №%{postNumber}' close: 'закрыть' twitter: 'поделиться ссылкой через Twitter' facebook: 'поделиться ссылкой через Facebook' @@ -282,7 +281,6 @@ ru: profile: "Профиль" mute: "Отключить" edit: "Настройки" - download_archive: "скачать архив ваших сообщений" new_private_message: "Новое Личное Сообщение" private_message: "Личное сообщение" private_messages: "Личные сообщения" @@ -464,7 +462,6 @@ ru: none: "Вы еще никого не приглашали сюда. Вы можете отправить индивидуальные приглашения или пригласить группу людей сразу загрузив групповой файл приглашений." text: "Групповое приглашение из файла" uploading: "Загрузка..." - success: "Файл загружен успешно, вы будете уведомлены о результате." error: "В процессе загрузки файла '{{filename}}' произошла ошибка: {{message}}" password: title: "Пароль" @@ -472,7 +469,6 @@ ru: common: "Пароль слишком короткий." ok: "Допустимый пароль." instructions: "Не менее %{count} символов." - associated_accounts: "Связанные учетные записи" ip_address: title: "Последний IP адрес" registration_ip_address: @@ -1586,7 +1582,6 @@ ru: title: "Откатить базу данных к предыдущему рабочему состоянию" confirm: "Вы уверены, что хотите откатить базу данных к предыдущему рабочему состоянию?" export_csv: - success: "Экспорт начат, вы будете уведомлены о прогрессе." failed: "Экспорт не удался. Пожалуйста, проверьте логи." button_text: "Экспорт" customize: @@ -2085,7 +2080,6 @@ ru: back: 'u Назад' up_down: 'k/j Переместить выделение ↑ ↓' open: 'o или Enter Открыть выбранную тему' - next_prev: 'shift j/shift k Следующая/предыдущая секция' application: title: 'Приложение' create: 'c Создать новую тему' @@ -2100,11 +2094,8 @@ ru: actions: title: 'Действия' star: 'f Отметить тему' - pin_unpin_topic: 'шифт з Закрепить/Открепить тему' - share_topic: 'shift s Поделиться темой' share_post: 's Поделиться сообщением' reply_as_new_topic: 't Ответить в новой связанной теме' - reply_topic: 'shift r Ответить на тему' reply_post: 'r Ответить на сообщение' quote_post: 'q Цитировать сообщение' like: 'l Лайкнуть сообщение' diff --git a/config/locales/client.sq.yml b/config/locales/client.sq.yml index f3dba01673..0c1310f32b 100644 --- a/config/locales/client.sq.yml +++ b/config/locales/client.sq.yml @@ -84,7 +84,6 @@ sq: other: "%{count} ditë më parë" share: topic: 'shpërnda një lidhje tek kjo temë' - post: 'shpërnda një lidhje tek tema #%{postNumber}' close: 'mbylle' twitter: 'shpërndaje këtë lidhe në Twitter' facebook: 'shpërndaje këtë lidhje ne Facebook' @@ -251,7 +250,6 @@ sq: profile: "Profili" mute: "Mute" edit: "Ndrysho Preferencat" - download_archive: "shkarko arkivin e postimeve të mia" private_message: "Mesazhet Private" private_messages: "Mesazhet" activity_stream: "Aktiviteti" @@ -413,7 +411,6 @@ sq: none: "Ju nuk keni ftuar askënd deri tani. Mund të dërgoni ftesa individuale ose mund të ftoni një grup personash duke ngarkuar skedarin." text: "Skedari për Ftesat në Grup" uploading: "Duke ngarkuar..." - success: "File uploaded successfully, you will be notified shortly with progress." error: "There was an error uploading '{{filename}}': {{message}}" password: title: "Fjalëkalimi" @@ -421,7 +418,6 @@ sq: common: "Ky fjalëkalim është shumë i përdorur." ok: "Fjalëkalimi është i pranueshëm." instructions: "Të paktën %{count} karaktere." - associated_accounts: "Associated accounts" ip_address: title: "Adresa IP e Fundit" registration_ip_address: @@ -1970,7 +1966,6 @@ sq: back: 'u Mbrapa' up_down: 'k/j Move selection ↑ ↓' open: 'o or Enter Open selected topic' - next_prev: 'shift j/shift k Next/previous section' application: title: 'Aplikacion' create: 'c Filloni një diskutim të ri' @@ -1985,11 +1980,8 @@ sq: actions: title: 'Actions' star: 'f Star topic' - pin_unpin_topic: 'shift p Pin/Unpin temën' - share_topic: 'shift s Share topic' share_post: 's Share post' reply_as_new_topic: 't Reply as linked topic' - reply_topic: 'shift r Reply to topic' reply_post: 'r Reply to post' quote_post: 'q Quote post' like: 'l Like post' diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml index ed29635598..8c7c616783 100644 --- a/config/locales/client.sv.yml +++ b/config/locales/client.sv.yml @@ -84,7 +84,6 @@ sv: other: "%{count} dagar sedan" share: topic: 'dela en länk till denna tråd' - post: 'dela en länk till inlägg' close: 'stäng' twitter: 'dela denna länk på Twitter' facebook: 'dela denna länk på Facebook' @@ -259,7 +258,6 @@ sv: profile: "Profil" mute: "Dämpa" edit: "Redigera inställningar" - download_archive: "ladda ned ett arkiv med mina inlägg" new_private_message: "Nytt Privat Meddelande" private_message: "Privat meddelande" private_messages: "Meddelanden" @@ -441,7 +439,6 @@ sv: none: "Du har inte skickat några inbjudningar. Du kan skicka individuella inbjudningar, eller så kan du bjuda in flera på en gång genom att ladda upp en bulkfil." text: "Massinbjudan från fil" uploading: "Laddar upp..." - success: "Uppladdning av filen lyckades" error: "Det blev ett fel vid uppladdning av '{{filename}}': {{message}}" password: title: "Lösenord" @@ -449,7 +446,6 @@ sv: common: "Det lösenordet är för vanligt." ok: "Ditt lösenord ser bra ut." instructions: "Måste vara minst %{count} tecken lång." - associated_accounts: "Kopplade konton" ip_address: title: "Senaste IP-adress" registration_ip_address: @@ -502,7 +498,7 @@ sv: first_post: Första inlägget mute: Dämpa unmute: Avdämpa - last_post: Sista inlägget + last_post: Senaste inlägg last_post_lowercase: senaste inlägg summary: enabled_description: "Sammanfattning över de inlägg som användarna tycker är mest intressanta." @@ -744,7 +740,7 @@ sv: topic: filter_to: "{{post_count}} inlägg i tråd" create: 'Ny tråd' - create_long: 'Skapa en nytt Tråd' + create_long: 'Skapa en ny tråd' private_message: 'Starta en privat konversation' list: 'Trådar' new: 'ny tråd' @@ -1011,6 +1007,9 @@ sv: unhide: "Visa" actions: flag: 'Flaga' + defer_flags: + one: "Skjut upp" + other: "Skjut upp" it_too: off_topic: "Flagga det också" spam: "Flagga det också" @@ -1216,6 +1215,9 @@ sv: topic_map: title: "Sammanfattning av tråd" links_shown: "visa alla {{totalLinks}} länkar..." + clicks: + one: "1 klick" + other: "%{count} klick" topic_statuses: warning: help: "Det här är en officiell varning." @@ -1367,6 +1369,8 @@ sv: 30_days_ago: "30 dagar sedan" all: "Alla" view_table: "tabell" + view_chart: "Stapeldiagram" + refresh_report: "Uppdatera rapport" start_date: "Startdatum" end_date: "Slutdatum" commits: @@ -1385,9 +1389,11 @@ sv: agree_flag_restore_post_title: "Återställ detta inlägg" agree_flag: "Godkänn flaggning" agree_flag_title: "Godkänn flaggning och behåll inlägget oförändrat" + defer_flag: "Skjut upp" defer_flag_title: "Ta bort den här flaggan; den kräver ingen åtgärd för tillfället." delete: "Ta bort" delete_title: "Ta bort inlägget som den här flaggan refererar till." + delete_post_defer_flag: "Ta bort inlägg och skjut upp flagga" delete_post_defer_flag_title: "Ta bort inlägg; om det är det första inlägget, ta bort ämnet" delete_post_agree_flag: "Ta bort inlägg och godkänn flaggning." delete_post_agree_flag_title: "Ta bort inlägg; om det är det första inlägget, ta bort ämnet" @@ -1401,8 +1407,13 @@ sv: clear_topic_flags: "Klar" clear_topic_flags_title: "Tråden har undersökts och eventuella problem har lösts. Klicka på klar för att ta bort flaggorna." more: "(mer svar...)" + dispositions: + agreed: "Godkände" + disagreed: "Godkände ej" + deferred: "Sköt upp" flagged_by: "Flaggad av" resolved_by: "Löst av" + took_action: "Agerade" system: "System" error: "Någonting gick snett" reply_message: "Svara" @@ -1508,9 +1519,14 @@ sv: title: "Gör rollback på databasen till ett tidigare fungerande tillstånd." confirm: "Är du säker på att du vill göra rollback på databasen till det tidigare fungerande tillståndet?" export_csv: - success: "Exporteringen har påbörjats, du kommer snart att få en notifiering över hur det går." failed: "Exporteringen misslyckades. Kontrollera loggarna." button_text: "Exportera" + button_title: + user: "Exportera alla användare i CSV-format" + staff_action: "Exportera medarbetarloggen i CSV-format." + screened_email: "Exportera hela e-postlistan i CSV-format." + screened_ip: "Exportera hela IP-listan i CSV-format." + screened_url: "Exportera hela URL-listan i CSV-format." customize: title: "Anpassa" long_title: "Sidanpassningar" @@ -1555,8 +1571,10 @@ sv: name: 'sekundär' description: 'Den huvudsakliga bakgrundsfärgen, och textfärgen på vissa knappar.' tertiary: + name: 'tertiär' description: 'Länkar, några knappar, notiser, och accentfärger.' quaternary: + name: "kvartär" description: "Navigationslänkar." header_background: name: "Bakgrund för sidhuvud" @@ -1566,9 +1584,12 @@ sv: description: "Text och ikoner i sidans sidhuvud." highlight: name: 'highlight' + description: 'Bakgrundsfärgen på markerade element på sidan, som inlägg och ämnen.' danger: name: 'fara' + description: 'Markeringsfärg när man tar bort inlägg eller ämnen.' success: + name: 'lyckades' description: 'Används för att indikera att en åtgärd lyckades.' love: name: 'älska' @@ -1609,6 +1630,7 @@ sv: title: "filter" user_placeholder: "användarnamn" address_placeholder: "namn@exempel.se" + type_placeholder: "Sammanfattning, registrering..." reply_key_placeholder: "svara knapp" skipped_reason_placeholder: "anledning" logs: @@ -1697,8 +1719,10 @@ sv: new: "Ny" active: "Aktiv" pending: "Avvaktande" + staff: 'Medarbetare' suspended: 'Avstängd' blocked: 'Blockerad' + suspect: 'Misstänkt' approved: "Godkänd?" approved_selected: one: "godkänd användare" @@ -1712,10 +1736,15 @@ sv: pending: 'Användare under granskning' newuser: 'Användare på Förtroendenivå 0 (ny användare)' basic: 'Användare på Förtroendenivå 1 (grundnivå)' + regular: 'Användare på förtroendenivå 2 (Medlem)' + leader: 'Användare på förtroendenivå 3 (Stammis)' + elder: 'Användare på förtroendenivå 4 (ledare)' + staff: "Medarbetare" admins: 'Admin-användare' moderators: 'Moderatorer' blocked: 'Blockerade användare' suspended: 'Avstängda användare' + suspect: 'Misstänkta användare' reject_successful: one: "1 användare har avvisats." other: "%{count} användare har avvisats." @@ -1770,6 +1799,7 @@ sv: flags_given_count: Givna Flaggnignar flags_received_count: Mottagna Flaggningar warnings_received_count: Varningar Mottagningar + flags_given_received_count: 'Flaggor utdelade / mottagna' approve: 'Godkänn' approved_by: "godkänd av" approve_success: "Användaren är godkänd och ett email kommer att skickas med aktiveringsinstruktioner." @@ -1788,6 +1818,7 @@ sv: other: "Kan inte radera alla inlägg, då användaren har fler än %{count} inlägg. (delete_all_posts_max)" delete_confirm: "Är du SÄKER på att du vill radera den användaren? Detta är permanent!" delete_and_block: "Radera och blockera denna e-post och IP-adress" + delete_dont_block: "Radera enbart" deleted: "Användaren har raderats." delete_failed: "Ett problem uppstod då användaren skulle raderas. Kontrollera att alla inlägg är borttagna innan du försöker radera användaren." send_activation_email: "Skicka aktiveringsmail" @@ -1814,7 +1845,16 @@ sv: visits: "besök" days: "dagar" topics_replied_to: "Trådar svarade på" + topics_viewed: "Besökta Ämnen" + topics_viewed_all_time: "Besökta Ämnen (totalt)" + posts_read: "Lästa Inlägg" + posts_read_all_time: "Lästa Inlägg (totalt)" flagged_posts: "flaggade inlägg" + flagged_by_users: "Användare som flaggade" + likes_given: "Utdelade Gillningar" + likes_received: "Mottagna Gillningar" + likes_received_days: "Mottagna Gillningar: unika dagar" + likes_received_users: "Mottagna Gillningar: unika användare" sso: external_id: "Externt ID" external_username: "Användarnamn" @@ -1887,6 +1927,7 @@ sv: lightbox: download: "ladda ned" keyboard_shortcuts_help: + title: 'Snabbkommandon' jump_to: title: 'Hoppa till' navigation: @@ -1897,6 +1938,7 @@ sv: create: 's Skapa en ny tråd' notifications: 'n Öppna notifikationer' search: 'Sök' + help: '? Öppna tangentbordshjälp' actions: star: 'f stjärnmärk tråd' like: 'Gilla inlägg' diff --git a/config/locales/client.tr_TR.yml b/config/locales/client.tr_TR.yml index 3d2a8ab850..f3e9854d2b 100644 --- a/config/locales/client.tr_TR.yml +++ b/config/locales/client.tr_TR.yml @@ -68,12 +68,11 @@ tr_TR: other: "%{count} gün önce" share: topic: 'bu konunun bağlantısını paylaşın' - post: '#%{postNumber}başlığın bağlantısını paylaşın' close: 'kapat' twitter: 'bu bağlantıyı Twitter''da paylaşın' facebook: 'bu bağlantıyı Facebook''da paylaşın' google+: 'bu bağlantıyı Google+''da paylaşın' - email: 'bu bağlantıyı email ile gönderin' + email: 'bu bağlantıyı e-posta ile gönderin' topic_admin_menu: "konuyla alakalı admin aksiyonları" edit: 'bu konunun başlığını ve kategorisini düzenleyin' not_implemented: "Bu özellik henüz geliştirilmedi, üzgünüz!" @@ -91,11 +90,11 @@ tr_TR: show_help: "yardım" links: "Bağlantılar" links_lowercase: "bağlantılar" - faq: "SSS" - guidelines: "Yönerge" - privacy_policy: "Gizlilik Politikası" - privacy: "Gizlilik" - terms_of_service: "Hizmet Kullanım Koşulları" + faq: "SSS/Yönergeler" + guidelines: "Yönergeler" + privacy_policy: "Gizlilik İlkeleri" + privacy: "Gizlilik İlkeleri" + terms_of_service: "Üyelik Sözleşmesi" mobile_view: "Mobil Görüntüleme" desktop_view: "Masaüstü Görüntüleme" you: "Siz" @@ -138,7 +137,7 @@ tr_TR: other: "{{count}} okunmamış konular." topic_count_new: other: "{{count}} yeni konular." - click_to_show: "Görüntülemek için tıkla." + click_to_show: "Görüntülemek için tıklayın." preview: "önizleme" cancel: "iptal" save: "Değişiklikleri Kaydet" @@ -207,7 +206,7 @@ tr_TR: posts: "Gönderiler" topics: "Konular" latest: "En Son" - latest_by: "göre sonuncu" + latest_by: "son gönderen" toggle_ordering: "sıralama kontrolünü aç/kapa" subcategories: "Alt kategoriler" topic_stats: "Yeni konu sayısı" @@ -235,13 +234,12 @@ tr_TR: said: "{{username}}:" profile: "Profil" mute: "Sustur" - edit: "Tercihleri Düzenle" - download_archive: "gönderilerimin arşivini indir" + edit: "Ayarları Düzenle" new_private_message: "Yeni Özel Mesaj" private_message: "Özel Mesaj" private_messages: "Mesajlar" activity_stream: "Aktivite" - preferences: "Tercihler" + preferences: "Ayarlar" bookmarks: "İşaretlenenler" bio: "Hakkımda" invited_by: "Tarafından Davet Edildi" @@ -262,7 +260,7 @@ tr_TR: suspended_notice: "Bu kullanıcı {{tarih}} tarihine kadar uzaklaştırıldı." suspended_reason: "Neden:" github_profile: "Github" - mailing_list_mode: "Her yeni gönderi için email alın (susturduğunuz konu ve kategorilerdekiler hariç)" + mailing_list_mode: "Her yeni gönderi için e-posta alın (susturduğunuz konu ve kategorilerdekiler hariç)" watched_categories: "Gözlendi" watched_categories_instructions: "Bu kategorilerdeki tüm yeni konuları otomatik olarak gözleyeceksiniz. Tüm yeni gönderi ve konular size bildirilecek. Ayrıca, okunmamış ve yeni gönderilerin sayısı ilgili konu yanında belirecek. " tracked_categories: "Takip edildi" @@ -286,11 +284,11 @@ tr_TR: mine: "Benimkiler" unread: "Okunmamışlar" change_password: - success: "(email gönderildi)" - in_progress: "(email yollanıyor)" + success: "(e-posta gönderildi)" + in_progress: "(e-posta yollanıyor)" error: "(hata)" - action: "Şifre Sıfırlama Emaili Yolla" - set_password: "Şifre Belirle" + action: "Parola Sıfırlama E-postası Yolla" + set_password: "Parola Belirle" change_about: title: "Hakkımda'yı Değiştir" change_username: @@ -300,10 +298,10 @@ tr_TR: error: "Kullanıcı adınızı değiştirirken bir hata oluştu." invalid: "Bu kullanıcı adı geçersiz. Sadece sayı ve harf içermelidir." change_email: - title: "Email Adresinizi Değiştirin" - taken: "Üzgünüz, bu email müsait değil." - error: "Email adresinizi değiştirirken bir hata oluştu. Belki bu adres zaten kullanımdadır?" - success: "Adresinize bir email gönderdik. Lütfen onaylama talimatlarını uygulayınız." + title: "E-posta Adresinizi Değiştirin" + taken: "Üzgünüz, bu e-posta müsait değil." + error: "E-posta adresinizi değiştirirken bir hata oluştu. Belki bu adres zaten kullanımdadır?" + success: "Adresinize bir e-posta gönderdik. Lütfen onaylama talimatlarını uygulayınız." change_avatar: title: "Avatarınızı değiştirin" gravatar: "Gravatar, baz alındı" @@ -321,15 +319,15 @@ tr_TR: title: "Kullanıcı Kartı Arkaplanı" instructions: "Profil arkaplanları ortalanacak ve genişlikleri 590px olacak. " email: - title: "Email" - instructions: "Kimseye gösterilmedi" - ok: "Onay için size email atacağız" - invalid: "Lütfen geçerli bir email adresini giriniz" - authenticated: "Email adresiniz {{provider}} tarafından doğrulanmıştır" + title: "E-posta" + instructions: "Kimseye gösterilmeyecek" + ok: "Onay için size e-posta atacağız" + invalid: "Lütfen geçerli bir e-posta adresini giriniz" + authenticated: "E-posta adresiniz {{provider}} tarafından doğrulanmıştır" frequency: - zero: "Eğer yollamak üzere olduğumuz şeyi okumadıysanız size direk email yollayacağız." - one: "Sadece, son bir dakika içinde forumu ziyaret etmediyseniz ve yollamak üzere olduğumuz şeyi okumadıysanız size email yollayacağız." - other: "Sadece, son {{count}} dakika içinde forumu ziyaret etmediyseniz ve yollamak üzere olduğumuz şeyi okumadıysanız size email yollayacağız." + zero: "Eğer yollamak üzere olduğumuz şeyi okumadıysanız size direk e-posta yollayacağız." + one: "Sadece, son bir dakika içinde forumu ziyaret etmediyseniz ve yollamak üzere olduğumuz şeyi okumadıysanız size e-posta yollayacağız." + other: "Sadece, son {{count}} dakika içinde forumu ziyaret etmediyseniz ve yollamak üzere olduğumuz şeyi okumadıysanız size e-posta yollayacağız." name: title: "İsim" instructions: "Adınız ve soyadınız (zorunlu değil)" @@ -340,38 +338,38 @@ tr_TR: instructions: "Özgün, boşluksuz ve kısa" short_instructions: "Kişiler sizden @{{username}} olarak bahsedebilirler" available: "Kullanıcı adınız müsait" - global_match: "Email kayıtlı kullanıcı adıyla eşleşiyor" + global_match: "E-posta kayıtlı kullanıcı adıyla eşleşiyor" global_mismatch: "Zaten mevcut. {{suggestion}} deneyin?" not_available: "Müsait değil. {{suggestion}} deneyin?" too_short: "Kullanıcı adınız çok kısa" too_long: "Kullanıcı adınız çok uzun" checking: "Kullanıcı adı müsait mi kontrol ediliyor..." - enter_email: 'Kullanıcı adı bulundu; eşleşen email adresini girin' - prefilled: "Email bu kullanıcı adı ile eşleşiyor" + enter_email: 'Kullanıcı adı bulundu; eşleşen e-posta adresini girin' + prefilled: "E-posta bu kullanıcı adı ile eşleşiyor" locale: title: "Arayüz dili" instructions: "Kullanıcı arayüzünün dili. Sayfayı yenilediğiniz zaman değişecektir." default: "(varsayılan)" password_confirmation: - title: "Tekrar Şifre" + title: "Tekrar Parola" last_posted: "Son Gönderi" - last_emailed: "Son Email Atılan" - last_seen: "Bakılmış" + last_emailed: "Son E-posta Atılan" + last_seen: "Görülmüş" created: "Katıldı" log_out: "Çıkış Yap" location: "Yer" card_badge: title: "Kullanıcı Kartı Rozeti" website: "Web Sayfası" - email_settings: "Email" + email_settings: "E-posta" email_digests: - title: "Siteyi ziyaret etmediğiniz zamanlar, yeni olayların özetini içeren bir email yolla:" + title: "Siteyi ziyaret etmediğiniz zamanlar, yeni olayların özetini içeren bir e-posta yolla:" daily: "günlük" weekly: "haftalık" bi_weekly: "her iki haftada bir" - email_direct: "Birisi gönderinize cevap verdiği, sizden alıntı yaptığı ya da sizden @username bahsettiği zaman email alın" - email_private_messages: "Biri size özel mesaj yolladığında email alın" - email_always: "Sayfada aktif olduğum anlarda da email bildirimleri yollamaya devam et" + email_direct: "Birisi gönderinize cevap verdiği, sizden alıntı yaptığı ya da sizden @username bahsettiği zaman e-posta alın" + email_private_messages: "Biri size özel mesaj yolladığında e-posta alın" + email_always: "Sayfada etkin olduğum anlarda da e-posta bildirimleri yollamaya devam et" other_settings: "Diğer" categories_settings: "Kategoriler" new_topic_duration: @@ -414,15 +412,13 @@ tr_TR: none: "Henüz kimseyi buraya davet etmediniz. Tek tek davetiye gönderebilirsiniz, ya da toplu bir davetiye dosyası yükleyerek birçok kişiyi aynı anda davet edebilirsiniz. " text: "Dosyadan Toplu Davet Gönder" uploading: "Yükleniyor..." - success: "Dosya yüklemesi başarılı, kısa bir süre içerisinde işlem durumuyla ilgili bir bildirim alacaksınız." error: "'{{filename}}' yüklenirken bir hata oluştu: {{message}}" password: - title: "Şifre" - too_short: "Şifreniz çok kısa." - common: "Bu şifre çok yaygın." - ok: "Şifreniz uygun gözüküyor." + title: "Parola" + too_short: "Parolanız çok kısa." + common: "Bu parola çok yaygın." + ok: "Parolanız uygun gözüküyor." instructions: "En az %{count} karakter." - associated_accounts: "İlgili hesaplar" ip_address: title: "Son IP Adresi" registration_ip_address: @@ -492,46 +488,46 @@ tr_TR: title: "Özel Mesaj" invite: "Diğerlerini Davet Et..." remove_allowed_user: "Bu özel mesajdan {{name}} isimli kullanıcıyı çıkarmak istediğinize emin misiniz?" - email: 'Email' + email: 'E-posta' username: 'Kullanıcı Adı' - last_seen: 'Bakılmış' + last_seen: 'Görülmüş' created: 'Oluşturuldu' created_lowercase: 'oluşturuldu' trust_level: 'Güven Seviyesi' - search_hint: 'kullanıcı adı, email veya IP adresi' + search_hint: 'kullanıcı adı, e-posta veya IP adresi' create_account: title: "Yeni Hesap Oluştur" - failed: "Bir şeyler ters gitti. Bu email adına daha önce bir kayıt oluşturulmuş olabilir, şifremi unuttum bağlantısını dene." + failed: "Bir şeyler ters gitti. Bu e-posta adına daha önce bir kayıt oluşturulmuş olabilir, parolamı unuttum bağlantısını dene." forgot_password: - title: "Şifremi Unuttum" - action: "Şifremi unuttum" - invite: "Kullanıcı adınızı ya da email adresinizi girin, size şifre sıfırlama emaili yollayalım." - reset: "Şifre Sıfırla" - complete_username: " %{username} kullanıcı adı ile eşleşen bir hesap bulunması durumunda, kısa bir süre içerisinde şifrenizi nasıl sıfırlayacağınızı açıklayan bir email alacaksınız." - complete_email: " %{email} adresi ile eşleşen bir hesap bulunması durumunda, kısa bir süre içerisinde şifrenizi nasıl sıfırlayacağınızı açıklayan bir email alacaksınız." - complete_username_found: "%{username} kullanıcı adı ile eşleşen bir hesap bulduk, kısa bir süre içerisinde şifrenizi nasıl sıfırlayacağınızı açıklayan bir email alacaksınız." - complete_email_found: "%{email} adresi ile eşleşen bir hesap bulduk, kısa bir süre içerisinde şifrenizi nasıl sıfırlayacağınızı açıklayan bir email alacaksınız." + title: "Parolamı Unuttum" + action: "Parolamı unuttum" + invite: "Kullanıcı adınızı ya da e-posta adresinizi girin, size parola sıfırlama e-postası yollayalım." + reset: "Parola Sıfırla" + complete_username: " %{username} kullanıcı adı ile eşleşen bir hesap bulunması durumunda, kısa bir süre içerisinde parolanızı nasıl sıfırlayacağınızı açıklayan bir e-posta alacaksınız." + complete_email: " %{email} adresi ile eşleşen bir hesap bulunması durumunda, kısa bir süre içerisinde parolanızı nasıl sıfırlayacağınızı açıklayan bir e-posta alacaksınız." + complete_username_found: "%{username} kullanıcı adı ile eşleşen bir hesap bulduk, kısa bir süre içerisinde parolanızı nasıl sıfırlayacağınızı açıklayan bir e-posta alacaksınız." + complete_email_found: "%{email} adresi ile eşleşen bir hesap bulduk, kısa bir süre içerisinde parolanızı nasıl sıfırlayacağınızı açıklayan bir e-posta alacaksınız." complete_username_not_found: "Hiçbir hesap kullanıcı adı %{username} ile eşleşmiyor" complete_email_not_found: "Hiçbir hesap %{email} adresi ile eşleşmiyor" login: title: "Giriş Yap" username: "Kullanıcı" - password: "Şifre" - email_placeholder: "email veya kullanıcı adı" + password: "Parola" + email_placeholder: "e-posta veya kullanıcı adı" caps_lock_warning: "Caps Lock açık" error: "Bilinmeyen hata" - blank_username_or_password: "Lütfen email adresinizi ya da kullanıcı adınızı, ve şifrenizi girin." - reset_password: 'Şifre Sıfırlama' + blank_username_or_password: "Lütfen e-posta adresinizi ya da kullanıcı adınızı, ve parolanızı girin." + reset_password: 'Parola Sıfırlama' logging_in: "Giriş yapılıyor..." or: "Veya" authenticating: "Kimlik doğrulanıyor ..." - awaiting_confirmation: "Hesabınız aktive edilmemiş. Yeni bir aktivasyon emaili almak için şifremi unuttum bağlantısını kullanabilirsiniz. " - awaiting_approval: "Hesabınız henüz bir görevli tarafından onaylanmadı. Onaylandığında email ile haberdar edileceksiniz." + awaiting_confirmation: "Hesabınız etkinleştirilmemiş. Yeni bir etkinleştirme e-postası almak için parolamı unuttum bağlantısını kullanabilirsiniz. " + awaiting_approval: "Hesabınız henüz bir görevli tarafından onaylanmadı. Onaylandığında e-posta ile haberdar edileceksiniz." requires_invite: "Üzgünüz, bu foruma sadece davetliler erişebilir." - not_activated: "Henüz giriş yapamazsınız. Hesabınızı aktive etmek için lütfen daha önceden {{sentTo}} adresine yollanan aktivasyon emailindeki açıklamaları okuyun." + not_activated: "Henüz giriş yapamazsınız. Hesabınızı etkinleştirmek için lütfen daha önceden {{sentTo}} adresine yollanan etkinleştirme e-postasındaki açıklamaları okuyun." not_allowed_from_ip_address: "Bu IP adresiyle giriş yapamazsınız." - resend_activation_email: "Aktivasyon emailini tekrar yollamak için buraya tıklayın. " - sent_activation_email_again: "{{currentEmail}} adresine yeni bir aktivasyon emaili yolladık. Bu emailin size ulaşması bir kaç dakika sürebilir; spam klasörüzü kontrol etmeyi unutmayın." + resend_activation_email: "Etkinleştirme e-postasını tekrar yollamak için buraya tıklayın. " + sent_activation_email_again: "{{currentEmail}} adresine yeni bir etkinleştirme e-postası yolladık. Bu e-postanın size ulaşması bir kaç dakika sürebilir; spam klasörüzü kontrol etmeyi unutmayın." google: title: "Google ile" message: "Google ile kimlik doğrulaması yapılıyor (pop-up engelleyicilerin etkinleştirilmediğinden emin olun)" @@ -577,7 +573,7 @@ tr_TR: create_pm: "Özel Mesaj" title: "Ya da Ctrl+Enter'a bas" users_placeholder: "Kullanıcı ekle" - title_placeholder: "Tek kısa bir cümlede açılamak gerekirse bu tartışmanın konusu nedir?" + title_placeholder: "Kısa bir cümlede açılamak gerekirse bu tartışmanın konusu nedir?" edit_reason_placeholder: "neden düzenleme yapıyorsunuz?" show_edit_reason: "(düzenleme sebebi ekle)" reply_placeholder: "Buraya yazın. Markdown veya BBCode kullanarak biçimlendirin. Resmi yüklemek için sürükleyip bırakın ya da yapıştırın." @@ -633,7 +629,7 @@ tr_TR: replied: "

    {{username}} {{description}}

    " posted: "

    {{username}} {{description}}

    " edited: "

    {{username}} {{description}}

    " - liked: "

    {{username}} {{description}}

    " + liked: "

    {{username}} {{description}}

    " private_message: "

    {{username}} {{description}}

    " invited_to_private_message: "

    {{username}} {{description}}

    " invitee_accepted: "

    {{username}} davetiyeni kabul etti

    " @@ -665,10 +661,10 @@ tr_TR: private_messages: "Özel mesajlarda ara" site_map: "başka bir konu listesine veya kategoriye git" go_back: 'geri dön' - not_logged_in_user: 'güncel aktivitelerin ve tercihlerin özetinin bulunduğu kullanıcı sayfası' + not_logged_in_user: 'güncel aktivitelerin ve ayarların özetinin bulunduğu kullanıcı sayfası' current_user: 'kendi kullanıcı sayfana git' starred: - title: 'Yıldız' + title: 'Yıldızla' help: star: 'bu konuyu yıldızlılar listenize ekleyin' unstar: 'bu konuyu yıldızlılar listenizden çıkarın' @@ -700,11 +696,11 @@ tr_TR: category: "{{category}} konusu yok." top: "Popüler bir konu yok." educate: - new: '

    Yeni konularınız burada belirir.

    Varsayılan ayarlarda, son 2 gün içerisinde yaratılan konular yeni sayılır ve yeni işaretiyle gösterilir.

    Dilerseniz bu ayarları seçeneklersayfanızdan düzenleyebilirsiniz.

    ' - unread: '

    Okunmamış konularınız burada belirecek.

    Varsayılan ayarlarda, şu durumlarda konular okunmamış sayılır ve okunmamışların sayısı 1 gösterilir:

    • Konuyu oluşturduysanız
    • Konuyu cevapladıysanız
    • Konuyu 4 dakikadan uzun bir süre okuduysanız

    Ya da, konunun altında bulunan bildirim kontrol bölümünden, konuyu Takip Edildi veya Gözlendi diye işaretlediyseniz.

    Bu ayarları seçenekler sayfasından değiştirebilirsiniz.

    ' + new: '

    Yeni konularınız burada belirir.

    Varsayılan ayarlarda, son 2 gün içerisinde yaratılan konular yeni sayılır ve yeni işaretiyle gösterilir.

    Dilerseniz bu ayarları ayarlarsayfanızdan düzenleyebilirsiniz.

    ' + unread: '

    Okunmamış konularınız burada belirecek.

    Varsayılan ayarlarda, şu durumlarda konular okunmamış sayılır ve okunmamışların sayısı 1 gösterilir:

    • Konuyu oluşturduysanız
    • Konuyu cevapladıysanız
    • Konuyu 4 dakikadan uzun bir süre okuduysanız

    Ya da, konunun altında bulunan bildirim kontrol bölümünden, konuyu Takip Edildi veya Gözlendi diye işaretlediyseniz.

    Bu ayarları ayarlar sayfasından değiştirebilirsiniz.

    ' starred: '

    Yıldızlanan konularınız burada belirir.

    Konuları yıldızlamak ya da yıldızlarını kaldırmak için şunları kullanın:

    • konu başlıklarının yanında bulunan butonunu
    • Her konunun en alt kısmında bulunan Yıldız butonunu
    ' bottom: - latest: "Daha fazla son bir konu yok." + latest: "Daha fazla son konu yok." hot: "Daha fazla sıcak bir konu yok." posted: "Daha fazla konu yok." read: "Daha fazla okunmuş konu yok." @@ -749,7 +745,7 @@ tr_TR: options: "Konu Seçenekleri" show_links: "Bu konunun içindeki bağlantıları göster. " toggle_information: "konu ayrıntılarını aç/kapa" - read_more_in_category: " Daha fazla okumak mı istiyorsunuz? {{catLink}} veya {{latestLink}} categorilerindeki diğer konulara göz atabilirsiniz." + read_more_in_category: "Daha fazlası için {{catLink}} kategorisine göz atabilir ya da {{latestLink}}bilirsiniz." read_more: "Daha fazla okumak mı istiyorsunuz? {{catLink}} ya da {{latestLink}}." read_more_MF: "Kalan { UNREAD, plural, =0 {} one { 1 okunmamış } other { # okunmamış } } { NEW, plural, =0 {} one { {BOTH, select, true{ve} false {} other{}} 1 yeni konu} other { {BOTH, select, true{and } false {} other{}} # yeni konu} } var, veya {CATEGORY, select, true { {catLink}} false {{latestLink}} kategorilerindeki diğer {} konulara göz atabilirsiniz }." browse_all_categories: Bütün kategorilere göz at @@ -818,7 +814,7 @@ tr_TR: open: "Konuyu Aç" close: "Konuyu Kapat" auto_close: "Otomatik Kapatma" - make_banner: "Manşet Konusu" + make_banner: "Manşet Konusu Yap" remove_banner: "Manşet Konusunu Kaldır" unpin: "Konuyu Başa Tutturma" pin: "Konuyu Başa Tuttur" @@ -847,18 +843,18 @@ tr_TR: automatically_add_to_groups_required: "Bu davet şu gruplara erişimi de içerir: (Gerekli, sadece adminler için)" invite_private: title: 'Özel Mesajlaşmaya Davet Et' - email_or_username: "Davet edilenin Emaili veya Kullanıcı Adı" - email_or_username_placeholder: "email veya kullanıcı adı" + email_or_username: "Davet edilenin E-postası veya Kullanıcı Adı" + email_or_username_placeholder: "e-posta veya kullanıcı adı" action: "Davet et" success: "O kullanıcıyı bu özel mesajlaşmaya katılması için davet ettik. " error: "Üzgünüz, kullanıcı davet edilirken bir hata oluştu." group_name: "grup adı" invite_reply: title: 'Davet et' - action: 'E-Posta ile davet et' + action: 'E-posta ile davet et' help: 'arkadaşlarınıza davet gönderin ki tek bir tık ile bu konuyu cevaplayabilsinler' - to_topic: "Arkadaşınıza, giriş yapması gerekmeden, bir linke tıklayarak hemen konuya katılıp cevap yazabilmesi için kısa bir email göndereceğiz." - to_forum: "Arkadaşınıza, giriş yapması gerekmeden, bir linke tıklayarak katılabilmesi için kısa bir email göndereceğiz. " + to_topic: "Arkadaşınıza, giriş yapması gerekmeden, bir linke tıklayarak hemen konuya katılıp cevap yazabilmesi için kısa bir e-posta göndereceğiz." + to_forum: "Arkadaşınıza, giriş yapması gerekmeden, bir linke tıklayarak katılabilmesi için kısa bir e-posta göndereceğiz. " email_placeholder: 'isim@örnek.com' success: " {{email}} adresine bir davet emaili attık. Davetiniz kabul edildiğinde size bir bildirim göndereceğiz. Kullanıcı sayfanızdaki davetiyeler sekmesinden davetlerinizi takip edebilirsiniz." error: "Üzgünüz, o kişiyi davet edemedik. Zaten kullanıcı olabilir mi?" @@ -908,7 +904,7 @@ tr_TR: post_number: "gönderi {{number}}" in_reply_to: "cevapla" last_edited_on: "gönderinin en son düzenlenme tarihi" - reply_as_new_topic: "Bağlantılı Konu oluşturarak Cevapla" + reply_as_new_topic: "Bağlantılı Konu Oluşturarak Cevapla" continue_discussion: "{{postLink}} Gönderisinden tartışmaya devam ediliyor:" follow_quote: "alıntılanan mesaja git" show_full: "Gönderinin Tamamını Göster" @@ -926,8 +922,8 @@ tr_TR: create: "Üzgünüz, gönderiniz oluşturulurken bir hata oluştu. Lütfen tekrar deneyin." edit: "Üzgünüz, gönderiniz düzenlenirken bir hata oluştu. Lütfen tekrar deneyin. " upload: "Üzgünüz, dosya yüklenirken bir hata oluştu. Lütfen tekrar deneyin." - attachment_too_large: "Üzgünüz, yükleyeme çalıştığınız dosya çok büyük (maksimum {{max_size_kb}}kb olabilir)" - file_too_large: "Üzgünüz, yüklemeye çalıştığınız dosya çok büyük. (maksimum boyut {{max_size_kb}}kb)" + attachment_too_large: "Üzgünüz, yükleyeme çalıştığınız dosya çok büyük (en fazla {{max_size_kb}}kb olabilir)" + file_too_large: "Üzgünüz, yüklemeye çalıştığınız dosya çok büyük. (en fazla boyut {{max_size_kb}}kb)" too_many_uploads: "Üzgünüz, aynı anda birden fazla dosya yükleyemezsiniz." upload_not_authorized: "Üzgünüz, yüklemeye çalıştığınız dosya tipine izin verilmiyor. (izin verilen uzantılar: {{authorized_extensions}})." image_upload_not_allowed_for_new_user: "Üzgünüz, yeni kullanıcılar resim yükleyemiyorlar." @@ -937,7 +933,7 @@ tr_TR: confirm: "Gönderinizden vazgeçtiğinize emin misiniz?" no_value: "Hayır, kalsın" yes_value: "Evet, vazgeç" - via_email: "bu gönderi email ile iletildi" + via_email: "bu gönderi e-posta ile iletildi" wiki: about: "bu gönderi bir wiki; acemi kullanıcılar düzenleyebilir" archetypes: @@ -976,7 +972,7 @@ tr_TR: inappropriate: "Bir de bayrakla" custom_flag: "Bir de bayrakla" bookmark: "Bir de işaretle" - like: "Bir de beğen" + like: "Sen de beğen" vote: "Bir de oyla" undo: off_topic: "Bayrağı geri al" @@ -1019,7 +1015,7 @@ tr_TR: bookmark: other: "Siz ve {{count}} diğer kişi bu gönderiyi işaretledi" like: - other: "Siz ve {{count}} diğer kişi bunu beğendi" + other: "Siz ve {{count}} başka kişi bunu beğendi" vote: other: "Siz ve {{count}} kişi bu gönderiyi oyladı" by_others: @@ -1089,7 +1085,7 @@ tr_TR: badge_colors: "Rozet renkleri" background_color: "Arka plan rengi" foreground_color: "Ön plan rengi" - name_placeholder: "Maksimum bir ya da iki kelime" + name_placeholder: "En fazla bir ya da iki kelime" color_placeholder: "Herhangi bir web rengi" delete_confirm: "Bu kategoriyi silmek istediğinize emin misiniz?" delete_error: "Kategoriyi silinirken bir hata oluştu." @@ -1101,9 +1097,9 @@ tr_TR: images: "Resimler" auto_close_label: "Şu kadar süreden sonra konuları otomatik olarak kapat: " auto_close_units: "saat" - email_in: "Özel gelen-email adresi:" - email_in_allow_strangers: "Hesabı olmayan, anonim kullanıcılardan email kabul et" - email_in_disabled: "Email üzerinden yeni konu oluşturma özelliği Site Ayarları'nda devre dışı bırakılmış. Email üzerinden yeni konu oluşturma özelliğini etkinleştirmek için," + email_in: "Özel gelen e-posta adresi:" + email_in_allow_strangers: "Hesabı olmayan, anonim kullanıcılardan e-posta kabul et" + email_in_disabled: "E-posta üzerinden yeni konu oluşturma özelliği Site Ayarları'nda devre dışı bırakılmış. E-posta üzerinden yeni konu oluşturma özelliğini etkinleştirmek için," email_in_disabled_click: '"emailla" ayarını etkinleştir' allow_badges_label: "Bu kategoride rozet verilmesine izin ver" edit_permissions: "İzinleri Düzenle" @@ -1111,7 +1107,7 @@ tr_TR: this_year: "bu yıl" position: "pozisyon" default_position: "Varsayılan Pozisyon" - position_disabled: "Kategoriler aktiflik sıralarına göre görünecekler. Listelerdeki kategorilerin sıralamalarını kontrol edebilmek için," + position_disabled: "Kategoriler etkinlik sıralarına göre görünecekler. Listelerdeki kategorilerin sıralamalarını kontrol edebilmek için," position_disabled_click: '"sabitlenmiş kategori pozisyonları" ayarını etklinleştirin.' parent: "Üst Kategori" notifications: @@ -1134,7 +1130,7 @@ tr_TR: take_action: "Aksiyon Al" notify_action: 'Özel mesaj' delete_spammer: "Spamciyi Sil" - delete_confirm: "Bu kullanıcının %{posts} gönderisini ve %{topics} konusunu silmek, hesabını kapatmak, kullandığı IP Adresi %{ip_address} üzerinden hesap açılmasını engellemek, ve %{email} email adresini kalıcı engellenenler listesine eklemek üzeresiniz. Bu kullanıcının gerçekten spamci olduğuna emin misiniz?" + delete_confirm: "Bu kullanıcının %{posts} gönderisini ve %{topics} konusunu silmek, hesabını kapatmak, kullandığı IP Adresi %{ip_address} üzerinden hesap açılmasını engellemek, ve %{email} e-posta adresini kalıcı engellenenler listesine eklemek üzeresiniz. Bu kullanıcının gerçekten spamci olduğuna emin misiniz?" yes_delete_spammer: "Evet, Spamciyi Sil" ip_address_missing: "(uygulanamaz)" hidden_email_address: "(gizli)" @@ -1178,8 +1174,8 @@ tr_TR: help: "Bu başlık arşive kaldırıldı; donduruldu ve değiştirilemez" invisible: help: "Bu konu listelenmemiş; konu listelerinde görünmeyecek, ve sadece doğrudan bağlantı aracılığıyla erişilebilecek" - posts: "Gönderiler" - posts_lowercase: "gönderiler" + posts: "Gönderi" + posts_lowercase: "gönderi" posts_long: "bu konuda {{number}} gönderi var" posts_likes_MF: | Bu konuda {ratio, select, @@ -1188,21 +1184,21 @@ tr_TR: high {beğeni/gönderi oranı aşırı yüksek} other {}} {count, plural, one {1 gönderi} other {# gönderi}} var original_post: "Ana Gönderi" - views: "Görüntülemeler" - views_lowercase: "görüntülemeler" + views: "Görüntüleme" + views_lowercase: "görüntüleme" replies: "Cevaplar" views_long: "bu konu {{number}} defa görüntülendi" activity: "Aktivite" - likes: "Beğeniler" - likes_lowercase: "beğeniler" + likes: "Beğeni" + likes_lowercase: "beğeni" likes_long: "bu konuda {{number}} beğeni var" - users: "Kullanıcılar" - users_lowercase: "kullanıcılar" + users: "Kullanıcı" + users_lowercase: "kullanıcı" category_title: "Kategori" history: "Geçmiş" changed_by: "Yazan {{author}}" raw_email: - title: "Ham Email" + title: "Ham e-posta" not_available: "Müsait değil!" categories_list: "Kategori Listesi" filters: @@ -1254,7 +1250,7 @@ tr_TR: help: "{{categoryName}} kategorisindeki en son konular" top: title: "En Popüler" - help: "geçtiğimiz yıl, ay, hafta veya gündeki en aktif başlıklar" + help: "geçtiğimiz yıl, ay, hafta veya gündeki en etkin başlıklar" yearly: title: "Yılın En Popülerleri" monthly: @@ -1321,7 +1317,7 @@ tr_TR: flags: title: "Bayraklar" old: "Eski" - active: "Aktif" + active: "Etkin" agree: "Onayla" agree_title: "Bu bayrağı geçerli ve doğru olarak onayla" agree_flag_modal_title: "Onayla ve..." @@ -1391,7 +1387,7 @@ tr_TR: delete_failed: "Grup silinemedi. Bu otomatik oluşturulmuş bir grup ise, yok edilemez." api: generate_master: "Master API Anahtarı üret" - none: "Şu aktif API anahtarı bulunmuyor." + none: "Şu etkin API anahtarı bulunmuyor." user: "Kullanıcı" title: "API" key: "API Anahtarı" @@ -1456,9 +1452,14 @@ tr_TR: title: "Veritabanını calışan son haline geri al." confirm: "Veri tabanını çalışan bir önceki versyonuna geri almak istediğinizden emin misiniz?" export_csv: - success: "Dışa aktarım başlatıldı, kısa süre içinde gelişmelerden haberdar edileceksiniz." failed: "Dışa aktarırken hata oluştu. Lütfen kayıtları kontrol edin." button_text: "Dışa aktar" + button_title: + user: "Tüm kullanıcı listesini CSV formatında dışa aktar." + staff_action: "Tüm görevli aksiyonları logunu CSV formatında dışa aktar." + screened_email: "Tüm taranmış e-postalar listesini CSV formatında dışa aktar." + screened_ip: "Tüm taranmış IPler listesini CSV formatında dışa aktar." + screened_url: "Tüm taranmış URLler listesini CSV formatında dışa aktar." customize: title: "Özelleştir" long_title: "Site Özelleştirmeleri" @@ -1530,25 +1531,25 @@ tr_TR: name: 'wiki' description: "wiki gönderilerinin arka plan rengi." email: - title: "Email" + title: "E-posta" settings: "Ayarlar" all: "Hepsi" - sending_test: "Test emaili gönderiliyor..." + sending_test: "Test e-postası gönderiliyor..." error: "HATA - %{server_error}" - test_error: "Test emailinin gönderilmesinde sorun yaşandı. Lütfen email ayarlarınızı tekrar kontrol edin, yer sağlayıcınızın email bağlantılarını bloke etmediğinden emin olun, ve tekrar deneyin." + test_error: "Test e-postasının gönderilmesinde sorun yaşandı. Lütfen e-posta ayarlarınızı tekrar kontrol edin, yer sağlayıcınızın e-posta bağlantılarını bloke etmediğinden emin olun, ve tekrar deneyin." sent: "Gönderildi" skipped: "Atlandı" sent_at: "Gönderildiği Zaman" time: "Zaman" user: "Kullanıcı" - email_type: "Email Tipi" + email_type: "E-posta Tipi" to_address: "Gönderi Adresi" - test_email_address: "test için email adresi" - send_test: "Test Emaili Gönder" + test_email_address: "test için e-posta adresi" + send_test: "Test E-postası Gönder" sent_test: "gönderildi!" delivery_method: "Gönderme Metodu" preview_digest: "Özeti Önizle" - preview_digest_desc: "Aktif olmayan kullanıcılara gönderilen haftalık özet emaillarının içeriğini özleyin" + preview_digest_desc: "Etkin olmayan kullanıcılara gönderilen haftalık özet e-postalarının içeriğini özleyin" refresh: "Yenile" format: "Format" html: "html" @@ -1607,14 +1608,14 @@ tr_TR: unsuspend_user: "kullanıcıyı uzaklaştırma" grant_badge: "rozet ver" revoke_badge: "rozeti iptal et" - check_email: "emaili kontrol et" + check_email: "e-posta kontrol et" delete_topic: "konuyu sil" delete_post: "gönderiyi sil" impersonate: "rolüne gir" screened_emails: - title: "Taranmış Emaillar" - description: "Biri yeni bir hesap oluşturmaya çalıştığında, aşağıdaki email adresleri kontrol edilecek ve kayıt önlenecek veya başka bir aksiyon alınacak." - email: "Email Adresi" + title: "Taranmış E-postalar" + description: "Biri yeni bir hesap oluşturmaya çalıştığında, aşağıdaki e-posta adresleri kontrol edilecek ve kayıt önlenecek veya başka bir aksiyon alınacak." + email: "E-posta Adresi" actions: allow: "İzin ver" screened_urls: @@ -1648,14 +1649,14 @@ tr_TR: users: title: 'Kullanıcılar' create: 'Admin Kullanıcısı Ekle' - last_emailed: "Son Email Gönderilen" + last_emailed: "Son E-posta Gönderilen" not_found: "Üzgünüz, o kullanıcıadı sistemde yok." id_not_found: "Üzgünüz, o kullanıcı adı sistemimizde bulunmuyor." - active: "Aktif" - show_emails: "Emailleri Göster" + active: "Etkin" + show_emails: "E-postaları Göster" nav: new: "Yeni" - active: "Aktif" + active: "Etkin" pending: "Bekleyen" staff: 'Görevli' suspended: 'Uzaklaştırılmış' @@ -1667,7 +1668,7 @@ tr_TR: reject_selected: other: "kullanıcıları ({{count}}) reddet" titles: - active: 'Aktif Kullanıcılar' + active: 'Etkin Kullanıcılar' new: 'Yeni Kullanıcılar' pending: 'Gözden Geçirilmeyi Bekleyen Kullanıcılar' newuser: 'Güven Seviyesi 0 (Yeni Kullanıcı) olan Kullanıcılar' @@ -1687,7 +1688,7 @@ tr_TR: other: "Reddedilemeyen %{count} kullanıcılar." not_verified: "Onaylanmayan" check_email: - title: "Bu kullanıcının email adresini ortaya çıkar" + title: "Bu kullanıcının e-posta adresini ortaya çıkar" text: "Göster" user: suspend_failed: "Bu kullanıcı uzaklaştırılırken bir şeyler ters gitti {{error}}" @@ -1736,7 +1737,7 @@ tr_TR: flags_given_received_count: 'Alınan / Verilen Bayraklar' approve: 'Onayla' approved_by: "onaylayan" - approve_success: "Kullanıcı onaylandı ve aktivasyon bilgilerini içeren bir email yollandı." + approve_success: "Kullanıcı onaylandı ve etkinleştirme bilgilerini içeren bir e-posta yollandı." approve_bulk_success: "Tebrikler! Seçilen tüm kullanıcılar onaylandı ve bilgilendirildi." time_read: "Okunma Zamanı" delete: "Kullanıcıyı Sil" @@ -1748,20 +1749,20 @@ tr_TR: cant_delete_all_too_many_posts: other: "Tüm gönderileri silemezsiniz çünkü kullanıcının %{count} 'ten daha fazla gönderisi var. (delete_all_posts_max)" delete_confirm: "Bu kullanıcıyı silmek istediğinize EMİN misiniz? İşlem geri alınamaz!" - delete_and_block: "Sil ve bu email ve IP adresini engelle" + delete_and_block: "Sil ve bu e-posta ve IP adresini engelle" delete_dont_block: "Sadece sil" deleted: "Kullanıcı silinmiş." delete_failed: "Kullanıcı silinirken bir hata oluştu. Kullanıcıyı silmeye çalışmadan önce tüm gönderilerin silindiğinden emin olun. " - send_activation_email: "Aktivasyon Emaili Yolla" - activation_email_sent: "Aktivasyon emaili yollandı." - send_activation_email_failed: "Tekrar aktivasyon emaili gönderilirken bir sorun yaşandı. %{error}" - activate: "Hesabı Aktifleştir" - activate_failed: "Kullanıcı aktifleştirilirken bir sorun yaşandı." + send_activation_email: "Etkinleştirme E-postası Yolla" + activation_email_sent: "Etkinleştirme e-postası yollandı." + send_activation_email_failed: "Tekrar etkinleştirme e-postası gönderilirken bir sorun yaşandı. %{error}" + activate: "Hesabı Etkinleştir" + activate_failed: "Kullanıcı etkinleştirilirken bir sorun yaşandı." deactivate_account: "Hesabı Deaktive Et" deactivate_failed: "Kullanıcı deaktive edilirken bir sorun yaşandı." unblock_failed: 'Kullanıcının engeli kaldırılırken bir sorun yaşandı.' block_failed: 'Kullanıcı engellenirken bir sorun yaşandı.' - deactivate_explanation: "Deaktive edilmiş bir kullanıcı emailini tekrar doğrulamalı." + deactivate_explanation: "Deaktive edilmiş bir kullanıcı e-postasını tekrar doğrulamalı." suspended_explanation: "Uzaklaştırılmış kullanıcılar sisteme giriş yapamaz." block_explanation: "Engellenmiş bir kullanıcı gönderi oluşturamaz veya konu başlatamaz." trust_level_change_failed: "Kullanıcının güven seviyesi değiştirilirken bir sorun yaşandı." @@ -1802,7 +1803,7 @@ tr_TR: external_id: "Harici ID" external_username: "Kullanıcı adı" external_name: "İsim" - external_email: "Email" + external_email: "E-posta" external_avatar_url: "Avatar URL" user_fields: title: "Kullanıcı Alanları" @@ -1844,7 +1845,7 @@ tr_TR: basic: 'Basit Kurulum' users: 'Kullanıcılar' posting: 'Gönderiliyor' - email: 'Email' + email: 'E-posta' files: 'Dosyalar' trust: 'Güven Seviyeleri' security: 'Güvenlik' @@ -1931,18 +1932,17 @@ tr_TR: title: 'Şuraya Geç' home: 'g, h Anasayfa (En Son)' latest: 'g, l En Son' - new: 'Yeni' - unread: 'Okunmamış' + new: 'g, n Yeni' + unread: 'g, u Okunmamış' starred: 'g, f Yıldızlı' - categories: 'Kategoriler' + categories: 'g, c Kategoriler' top: 'g, t En Popüler' navigation: title: 'Navigasyon' jump: '# # numaralı gönderiye git' - back: 'Geri' + back: 'u Geri' up_down: 'k/j seçileni taşı ↑ ↓' open: 'o or Enter Seçili konuyu aç' - next_prev: 'shift j/shift k Sonraki/önceki bölüm' application: title: 'Uygulama' create: 'c Yeni konu aç' @@ -1950,18 +1950,15 @@ tr_TR: site_map_menu: '= Site menüsünü aç' user_profile_menu: 'p Kullanıcı menüsünü aç' show_incoming_updated_topics: '. Güncellenmiş konuları göster' - search: 'Arama' + search: '/ Arama' help: '? Klavye yardımını göster' dismiss_new_posts: 'x, r Yeni/Gönderleri Yoksay' dismiss_topics: 'x, t Konuları Yoksay' actions: title: 'Aksiyonlar' star: 'f Konuyu yıldızla' - pin_unpin_topic: 'shift p Konuyu başa tuttur / tutturma' - share_topic: 'shift s Konuyu paylaş' share_post: 's Gönderiyi paylaş' reply_as_new_topic: 't Bağlantılı konu olarak cevapla' - reply_topic: 'shift r Konuya cevap yaz' reply_post: 'r Gönderiyi cevapla' quote_post: 'q Gönderiyi alıntıla' like: 'l Gönderiyi beğen' @@ -2061,7 +2058,7 @@ tr_TR: name: İlk alıntı description: Bir kullanıcıyı alıntıladı read_guidelines: - name: Yönergeleri oku + name: Yönergeler okundu description: Topluluk yönergelerini oku reader: name: Okuyucu diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml index 2ccd656803..32d85eab81 100644 --- a/config/locales/client.uk.yml +++ b/config/locales/client.uk.yml @@ -35,7 +35,6 @@ uk: other: "днів тому: %{count}" share: topic: 'поширити посилання на цю тему' - post: 'поширити посилання на допис #%{postNumber}' close: 'сховати' twitter: 'поширити це посилання в Twitter' facebook: 'поширити це посилання в Facebook' @@ -174,7 +173,6 @@ uk: profile: "Профіль" mute: "Mute" edit: "Редагувати налаштування" - download_archive: "звантажити архів з моїми дописами" private_message: "Приватне повідомлення" private_messages: "Повідомлення" activity_stream: "Активність" @@ -1352,7 +1350,6 @@ uk: title: 'Дії' star: 'f Позначити тему зірочкою' share_post: 's Поширити допис' - reply_topic: 'shift r Відповісти на тему' reply_post: 'r Відповісти на допис' quote_post: 'q Цитувати допис' like: 'l Вподобати допис' diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index 7f3fdaeb89..cf40132260 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -68,7 +68,6 @@ zh_CN: other: "%{count}日前" share: topic: '分享本主题的链接' - post: '分享 #%{postNumber} 帖的链接' close: '关闭' twitter: '分享这个链接到 Twitter' facebook: '分享这个链接到 Facebook' @@ -83,10 +82,10 @@ zh_CN: generic_error_with_reason: "发生了一个错误:%{error}" sign_up: "注册" log_in: "登录" - age: "时间" + age: "年龄" joined: "加入时间:" admin_title: "管理" - flags_title: "报告" + flags_title: "旗帜" show_more: "显示更多" show_help: "帮助" links: "链接" @@ -98,7 +97,7 @@ zh_CN: terms_of_service: "服务条款" mobile_view: "移动版" desktop_view: "桌面版" - you: "您" + you: "你" or: "或" now: "刚刚" read_more: '阅读更多' @@ -127,10 +126,10 @@ zh_CN: post_count: "帖子" user_count: "用户" bookmarks: - not_logged_in: "抱歉,您需要先登录才能给帖子加书签" - created: "您已经为此贴添加书签" - not_bookmarked: "您已经阅读过此帖;点此为其添加书签" - last_read: "这是您阅读过的最后一帖;点此给它加上书签" + not_logged_in: "抱歉,你需要先登录才能给帖子加书签" + created: "你已经为此贴添加书签" + not_bookmarked: "你已经阅读过此帖;点此为其添加书签" + last_read: "这是你阅读过的最后一帖;点此给它加上书签" remove: "删除书签" topic_count_latest: other: "{{count}} 个新的或更新的主题。" @@ -161,18 +160,18 @@ zh_CN: placeholder: "在此输入主题标题" user_action: user_posted_topic: "{{user}} 发起了 本主题" - you_posted_topic: " 发起了 本主题" + you_posted_topic: " 发起了 本主题" user_replied_to_post: "{{user}} 回复了 {{post_number}}" - you_replied_to_post: " 回复了 {{post_number}}" + you_replied_to_post: " 回复了 {{post_number}}" user_replied_to_topic: "{{user}} 回复了 本主题" - you_replied_to_topic: " 回复了 本主题" + you_replied_to_topic: " 回复了 本主题" user_mentioned_user: "{{user}} 提到了 {{another_user}}" - user_mentioned_you: "{{user}} 提到了 " - you_mentioned_user: " 提到了 {{another_user}}" + user_mentioned_you: "{{user}} 提到了 " + you_mentioned_user: " 提到了 {{another_user}}" posted_by_user: "发起人 {{user}}" - posted_by_you: "发起人 " + posted_by_you: "发起人 " sent_by_user: "发送人 {{user}}" - sent_by_you: "发送人 " + sent_by_you: "发送人 " groups: visible: "群组对所有用户可见" title: @@ -187,8 +186,8 @@ zh_CN: members_mods_and_admins: "仅组员、版主与管理员" everyone: "任何人" user_action_groups: - '1': "赞过的" - '2': "被赞过的" + '1': "给赞" + '2': "被赞" '3': "书签" '4': "主题" '5': "帖子" @@ -230,13 +229,12 @@ zh_CN: read_time: "阅读时间" topics_entered: "进入的主题" post_count: "# 帖子" - confirm_delete_other_accounts: "您确定您想要删除这些账户吗?" + confirm_delete_other_accounts: "你确定你想要删除这些账户吗?" user: said: "{{username}}:" profile: "个人资料" mute: "防打扰" edit: "修改设置" - download_archive: "下载我的帖子的存档" new_private_message: "新私信" private_message: "私信" private_messages: "消息" @@ -249,7 +247,7 @@ zh_CN: notifications: "通知" dismiss_notifications: "标记所有为已读" dismiss_notifications_tooltip: "标记所有未读通知为已读" - disable_jump_reply: "不要在回复后跳转到您的新帖子" + disable_jump_reply: "不要在回复后跳转到你的新帖子" dynamic_favicon: "在标签页图标上动态显示未读消息提醒(测试)" edit_history_public: "让其他用户查看我的帖子的以前版本" external_links_in_new_tab: "始终在新的标签页打开外部链接" @@ -262,24 +260,24 @@ zh_CN: suspended_notice: "该用户将被禁止登录,直至 {{date}}。" suspended_reason: "原因:" github_profile: "Github" - mailing_list_mode: "对每个新帖发送一封邮件(除非您屏蔽了主题或者分类)" + mailing_list_mode: "对每个新帖发送一封邮件(除非你屏蔽了主题或者分类)" watched_categories: "已关注" - watched_categories_instructions: "您将自动关注这些分类的所有新主题。您将收到新的帖子和主题的通知,并且会在靠近话题列表的地方增加一个关于未读和新帖子的数字。" + watched_categories_instructions: "你将自动关注这些分类的所有新主题。你将收到新的帖子和主题的通知,并且会在靠近话题列表的地方增加一个关于未读和新帖子的数字。" tracked_categories: "已跟踪" - tracked_categories_instructions: "您将会自动追踪这些分类中的所有新主题。未读和新帖子数量将出现在每个主题后。" + tracked_categories_instructions: "你将会自动追踪这些分类中的所有新主题。未读和新帖子数量将出现在每个主题后。" muted_categories: "已屏蔽" - muted_categories_instructions: "您不会收到这些分类的新主题的任何通知,他们也不会出现在您的未读标签中。" + muted_categories_instructions: "你不会收到这些分类的新主题的任何通知,他们也不会出现在你的未读标签中。" delete_account: "删除我的帐号" - delete_account_confirm: "您真的要永久删除自己的账号吗?删除之后无法恢复!" - deleted_yourself: "您的帐号已被成功删除。" - delete_yourself_not_allowed: "您目前不能删除自己的帐号。联系管理员帮助您删除帐号。" + delete_account_confirm: "你真的要永久删除自己的账号吗?删除之后无法恢复!" + deleted_yourself: "你的帐号已被成功删除。" + delete_yourself_not_allowed: "你目前不能删除自己的帐号。联系管理员帮助你删除帐号。" unread_message_count: "消息" admin_delete: "删除" staff_counters: flags_given: "有用的标记" flagged_posts: "被报告的帖子" deleted_posts: "删除的帖子" - suspensions: "禁止的" + suspensions: "禁用的" warnings_received: "警告" messages: all: "所有" @@ -295,25 +293,25 @@ zh_CN: title: "更改个人简介" change_username: title: "修改用户名" - confirm: "如果您修改用户名,所有引用您的帖子和 @您 的链接将失效。您真的确定要这么做么?" + confirm: "如果你修改用户名,所有引用你的帖子和 @你 的链接将失效。你真的确定要这么做么?" taken: "抱歉,此用户名已经被使用了。" - error: "在修改您的用户名时发生了错误。" + error: "在修改你的用户名时发生了错误。" invalid: "此用户名不合法,用户名只能包含字母和数字" change_email: title: "修改电子邮箱" taken: "抱歉,此电子邮箱不可用。" - error: "抱歉在修改您的电子邮箱时发生了错误,可能此邮箱已经被使用了?" + error: "抱歉在修改你的电子邮箱时发生了错误,可能此邮箱已经被使用了?" success: "我们已经发送了一封确认信到此邮箱地址,请按照邮箱内指示完成确认。" change_avatar: title: "修改头像" gravatar: "Gravatar头像,基于:" - refresh_gravatar_title: "刷新您的 Gravatar 头像" + refresh_gravatar_title: "刷新你的头像" letter_based: "系统指定的头像" uploaded_avatar: "自定义图片" uploaded_avatar_empty: "添加自定义图片" upload_title: "上传图片" upload_picture: "上传图片" - image_is_not_a_square: "注意:我们已经裁剪了您的图片;它不是正方形的。" + image_is_not_a_square: "注意:我们已经裁剪了你的图片;它不是正方形的。" change_profile_background: title: "个人资料背景" instructions: "个人资料背景将被居中,且默认宽度为 850px。" @@ -323,34 +321,34 @@ zh_CN: email: title: "电子邮箱" instructions: "绝不会被公开显示" - ok: "我们将邮件跟您确认" + ok: "我们将邮件跟你确认" invalid: "请填写正确的电子邮箱地址" authenticated: "你的电子邮箱已经被 {{provider}} 验证了。" frequency: - zero: "如果您没有阅读过我们想寄给您的内容,我们会立即发送电子邮件给您。" - one: "如果您没有阅读过我们想寄给您的内容,我们只会在您没有访问时才会发送电子邮件给您。" - other: "如果您没有阅读过我们想寄给您的内容,我们只会在您最近 {{count}} 分钟内没有访问时才会发送电子邮件给您。" + zero: "如果你没有阅读过我们想寄给你的内容,我们会立即发送电子邮件给你。" + one: "如果你没有阅读过我们想寄给你的内容,我们只会在你没有访问时才会发送电子邮件给你。" + other: "如果你没有阅读过我们想寄给你的内容,我们只会在你最近 {{count}} 分钟内没有访问时才会发送电子邮件给你。" name: title: "名字" - instructions: "您的全名(可选)" - too_short: "您设置的名字太短了" - ok: "您的名字符合要求" + instructions: "你的全名(可选)" + too_short: "你设置的名字太短了" + ok: "你的名字符合要求" username: title: "用户名" instructions: "唯一,没有空格,简短" - short_instructions: "其他人可以用 @{{username}} 来提及您" - available: "您的用户名可用" + short_instructions: "其他人可以用 @{{username}} 来提及你" + available: "你的用户名可用" global_match: "电子邮箱与注册用户名相匹配" global_mismatch: "已被注册。试试 {{suggestion}} ?" not_available: "不可用。试试 {{suggestion}} ?" - too_short: "您设置的用户名太短了" - too_long: "您设置的用户名太长了" + too_short: "你设置的用户名太短了" + too_long: "你设置的用户名太长了" checking: "查看用户名是否可用..." enter_email: '找到用户名;请输入对应电子邮箱' prefilled: "电子邮箱与注册用户名匹配" locale: title: "界面语言" - instructions: "用户界面语言。将在您刷新页面后改变。" + instructions: "用户界面语言。将在你刷新页面后改变。" default: "(默认)" password_confirmation: title: "请再次输入密码" @@ -365,19 +363,19 @@ zh_CN: website: "网站" email_settings: "电子邮箱" email_digests: - title: "当您长期未访问此站时,发送新的摘要信息邮件" + title: "当你长期未访问此站时,发送新的摘要信息邮件" daily: "每天" weekly: "每周" bi_weekly: "每两周" - email_direct: "当有人引用、回复或提及您时发送一封邮件给您" - email_private_messages: "当有人给您发私信时发送一封邮件给您" + email_direct: "当有人引用、回复或提及你时发送一封邮件给你" + email_private_messages: "当有人给你发私信时发送一封邮件给你" email_always: "即使在论坛中活跃时也接收电子邮件提醒" other_settings: "其它" categories_settings: "分类" new_topic_duration: label: "认为主题是新主题的条件:" not_viewed: "我还没有浏览过它们" - last_here: "在您最近一次访问之后创建的" + last_here: "在你最近一次访问之后创建的" after_n_days: other: "最近 {{count}} 天内创建" after_n_weeks: @@ -394,7 +392,7 @@ zh_CN: search: "输入以搜索邀请..." title: "邀请" user: "邀请用户" - none: "您还没有邀请过任何人。" + none: "你还没有邀请过任何人。" truncated: "只显示前 {{count}} 个邀请。" redeemed: "确认邀请" redeemed_at: "已确认" @@ -411,18 +409,16 @@ zh_CN: account_age_days: "账号建立天数" create: "发送邀请" bulk_invite: - none: "您没有邀请过任何人。您可以发送个人邀请,或者上传一个批量邀请文件一次性邀请一批人。" + none: "你没有邀请过任何人。你可以发送个人邀请,或者上传一个批量邀请文件一次性邀请一批人。" text: "通过文件批量邀请" uploading: "上传中……" - success: "文件上传成功,您将很快被通知状态。" error: "在上传 '{{filename}}' 时出现错误:{{message}}" password: title: "密码" - too_short: "您设置的密码太短了。" - common: "您设置的密码太常见了。" - ok: "您设置的密码符合要求。" + too_short: "你设置的密码太短了。" + common: "你设置的密码太常见了。" + ok: "你设置的密码符合要求。" instructions: "至少需要 %{count} 个字符。" - associated_accounts: "关联账户" ip_address: title: "最后使用的 IP 地址" registration_ip_address: @@ -447,7 +443,7 @@ zh_CN: forbidden: "访问被阻止" unknown: "错误" desc: - network: "请检查您的网络连接。" + network: "请检查你的网络连接。" network_fixed: "似乎恢复正常了。" server: "错误代码:{{status}}" forbidden: "你没有权限读这个。" @@ -458,10 +454,10 @@ zh_CN: fixed: "载入页面" close: "关闭" assets_changed_confirm: "此网页刚刚更新. 刷新查看新版本?" - logout: "您已登出。" + logout: "你已登出。" refresh: "刷新" read_only_mode: - enabled: "一个管理员启用了只读模式。您可以继续浏览这个站点但是无法进行交互。" + enabled: "一个管理员启用了只读模式。你可以继续浏览这个站点但是无法进行交互。" login_disabled: "只读模式下不允许登录。" too_few_topics_notice: "创建至少 5 个公开主题和 %{posts} 个公开帖子以开始讨论。如果没有内容让新用户阅读,他们就无法得到信任等级。这个消息仅显示给职员。" learn_more: "了解更多..." @@ -478,7 +474,7 @@ zh_CN: last_post: 最后一帖 last_post_lowercase: 最后一帖 summary: - enabled_description: "您正在查看这个主题的概括版本:由社群认定的最有意思的帖子。" + enabled_description: "你正在查看这个主题的概括版本:由社群认定的最有意思的帖子。" description: "有 {{count}} 个回复。" description_time: "有 {{count}} 个回复,大约需要 {{readingTime}} 分钟阅读。" enable: '概括本主题' @@ -505,12 +501,12 @@ zh_CN: forgot_password: title: "忘记密码" action: "我忘记了我的密码" - invite: "输入您的用户名和电子邮箱地址,我们会发送密码重置邮件给您。" + invite: "输入你的用户名和电子邮箱地址,我们会发送密码重置邮件给你。" reset: "重置密码" - complete_username: "如果您的账户名 %{username} 存在,您将马上收到一封电子邮件,告诉您如何重置密码。" - complete_email: "如果您的账户 %{email} 存在,您将马上收到一封电子邮件,告诉您如何重置密码。" - complete_username_found: "您的账户名 %{username} 存在,您将马上收到一封电子邮件,告诉您如何重置密码。" - complete_email_found: "您的账户 %{email} 存在,您将马上收到一封电子邮件,告诉您如何重置密码。" + complete_username: "如果你的账户名 %{username} 存在,你将马上收到一封电子邮件,告诉你如何重置密码。" + complete_email: "如果你的账户 %{email} 存在,你将马上收到一封电子邮件,告诉你如何重置密码。" + complete_username_found: "你的账户名 %{username} 存在,你将马上收到一封电子邮件,告诉你如何重置密码。" + complete_email_found: "你的账户 %{email} 存在,你将马上收到一封电子邮件,告诉你如何重置密码。" complete_username_not_found: "没有帐户匹配这个用户名 %{username}" complete_email_not_found: "没有帐户匹配 %{email} " login: @@ -520,43 +516,43 @@ zh_CN: email_placeholder: "电子邮箱地址或用户名" caps_lock_warning: "大小写锁定开启" error: "未知错误" - blank_username_or_password: "请输入您的邮件地址或用户名,以及密码。" + blank_username_or_password: "请输入你的邮件地址或用户名,以及密码。" reset_password: '重置密码' logging_in: "登录中..." or: "或" authenticating: "验证中..." - awaiting_confirmation: "您的帐号尚未激活,点击忘记密码链接来重新发送激活邮件。" - awaiting_approval: "您的帐号尚未被论坛版主批准。一旦您的帐号获得批准,您会收到一封电子邮件。" + awaiting_confirmation: "你的帐号尚未激活,点击忘记密码链接来重新发送激活邮件。" + awaiting_approval: "你的帐号尚未被论坛版主批准。一旦你的帐号获得批准,你会收到一封电子邮件。" requires_invite: "抱歉,本论坛仅接受邀请注册。" - not_activated: "您还不能登录。我们之前在 {{sentTo}} 发送了一封激活邮件给您。请按照邮件中的介绍来激活您的帐号。" - not_allowed_from_ip_address: "您不能从该 IP 地址登陆。" + not_activated: "你还不能登录。我们之前在 {{sentTo}} 发送了一封激活邮件给你。请按照邮件中的介绍来激活你的帐号。" + not_allowed_from_ip_address: "你不能从该 IP 地址登录。" resend_activation_email: "点击此处重新发送激活邮件。" - sent_activation_email_again: "我们在 {{currentEmail}} 又发送了一封激活邮件给您,邮件送达可能需要几分钟;请检查一下您邮箱的垃圾邮件文件夹。" + sent_activation_email_again: "我们在 {{currentEmail}} 又发送了一封激活邮件给你,邮件送达可能需要几分钟;请检查一下你邮箱的垃圾邮件文件夹。" google: title: "使用 Google 帐号登录" - message: "正使用 Google 帐号验证登录(请确保没有禁止浏览器弹出对话框)" + message: "正使用 Google 帐号验证登录(请确保浏览器没有禁止弹出窗口)" google_oauth2: title: "使用 Google 帐号登录" - message: "使用 Google 帐号验证登录(请确保没有禁止浏览器弹出对话框)" + message: "使用 Google 帐号验证登录(请确保浏览器没有禁止弹出窗口)" twitter: title: "使用 Twitter 帐号登录" - message: "正使用 Twitter 帐号验证登录(请确保没有禁止浏览器弹出对话框)" + message: "正使用 Twitter 帐号验证登录(请确保浏览器没有禁止弹出窗口)" facebook: title: "使用 Facebook 帐号登录" - message: "正使用 Facebook 帐号验证登录(请确保没有禁止浏览器弹出对话框)" + message: "正使用 Facebook 帐号验证登录(请确保浏览器没有禁止弹出窗口)" yahoo: title: "使用 Yahoo 帐号登录" - message: "正使用 Yahoo 帐号验证登录(请确保没有禁止浏览器弹出对话框)" + message: "正使用 Yahoo 帐号验证登录(请确保浏览器没有禁止弹出窗口)" github: title: "使用 GitHub 帐号登录" - message: "正使用 GitHub 帐号验证登录(请确保没有禁止浏览器弹出对话框)" + message: "正使用 GitHub 帐号验证登录(请确保浏览器没有禁止弹出窗口)" composer: add_warning: "这是一个正式的警告。" - posting_not_on_topic: "您想回复哪一个主题?" + posting_not_on_topic: "你想回复哪一个主题?" saving_draft_tip: "保存中" saved_draft_tip: "已保存" saved_local_draft_tip: "已本地保存" - similar_topics: "您的主题有些类似于..." + similar_topics: "你的主题有些类似于..." drafts_offline: "离线草稿" min_length: need_more_for_title: "请给标题再输入至少 {{n}} 个字符" @@ -567,7 +563,7 @@ zh_CN: title_too_long: "标题太长,至多 {{max}} 个字符。" post_missing: "帖子不能为空" post_length: "帖子至少应有 {{min}} 个字符" - category_missing: "您必须选择一个分类" + category_missing: "你必须选择一个分类" save_edit: "保存编辑" reply_original: "回复原始话题" reply_here: "在此回复" @@ -580,8 +576,8 @@ zh_CN: title_placeholder: "简述此讨论内容是关于什么?" edit_reason_placeholder: "编辑理由" show_edit_reason: "(添加编辑理由)" - reply_placeholder: "在此输入您的内容。您可以使用 Markdown 或 BBCode 来格式化内容。拖拽或粘贴一幅图片到这儿即可将它上传。" - view_new_post: "浏览您的新帖子。" + reply_placeholder: "在此输入你的内容。你可以使用 Markdown 或 BBCode 来格式化内容。拖拽或粘贴一幅图片到这儿即可将它上传。" + view_new_post: "浏览你的新帖子。" saving: "保存中..." saved: "已保存!" saved_draft: "帖子还没写完,点击继续" @@ -624,8 +620,8 @@ zh_CN: units: "(# 小时数)" examples: '输入小时数(24)。' notifications: - title: "使用 @用户名称 提及到您,回复您的帖子和主题,私信等等的通知消息" - none: "您当前没有任何通知。" + title: "使用 @用户名称 提及到你,回复你的帖子和主题,私信等等的通知消息" + none: "你当前没有任何通知。" more: "浏览以前的通知" total_flagged: "被报告帖子的总数" mentioned: "@

    {{username}} {{description}}

    " @@ -636,7 +632,7 @@ zh_CN: liked: "

    {{username}} {{description}}

    " private_message: "

    {{username}} {{description}}

    " invited_to_private_message: "

    {{username}} {{description}}

    " - invitee_accepted: "

    {{username}} 已接受您的邀请

    " + invitee_accepted: "

    {{username}} 已接受你的邀请

    " moved_post: "

    {{username}} 移动了 {{description}}

    " linked: "

    {{username}} {{description}}

    " granted_badge: "

    获得“{{description}}”

    " @@ -647,12 +643,12 @@ zh_CN: from_the_web: "来自网络" remote_tip: "图片链接" remote_tip_with_attachments: "图片或文件链接({{authorized_extensions}})" - local_tip: "点击从您的设备中选择一张图片。" + local_tip: "点击从你的设备中选择一张图片。" local_tip_with_attachments: "点击从你的设备中选择图片或文件(支持的格式: {{authorized_extensions}})。" - hint: "(您也可以通过拖放至编辑器的方式来上传)" - hint_for_supported_browsers: "(您也可以通过拖放或粘帖图片至编辑器的方式来上传)" + hint: "(你也可以通过拖放至编辑器的方式来上传)" + hint_for_supported_browsers: "(你也可以通过拖放或粘帖图片至编辑器的方式来上传)" uploading: "上传中" - image_link: "链接您的图片到" + image_link: "链接你的图片到" search: title: "搜索主题、帖子、用户或分类" no_results: "没有找到结果。" @@ -666,12 +662,12 @@ zh_CN: site_map: "去另一个主题列表或分类" go_back: '返回' not_logged_in_user: '显示当前活动和设置的用户页面' - current_user: '去您的用户页面' + current_user: '去你的用户页面' starred: title: '收藏' help: - star: '将此主题加入您的收藏列表' - unstar: '将此主题从您的收藏列表中移除' + star: '将此主题加入你的收藏列表' + unstar: '将此主题从你的收藏列表中移除' topics: bulk: reset_read: "设为未读" @@ -688,21 +684,21 @@ zh_CN: archive_topics: "存档主题" notification_level: "更改提示等级" selected: - other: "您已经选择了 {{count}}个主题" + other: "你已经选择了 {{count}}个主题" none: - starred: "您没有收藏任何主题。" - unread: "您没有未阅主题。" - new: "您没有新主题可读。" - read: "您尚未阅读任何主题。" - posted: "您尚未在任何主题中发帖。" + starred: "你没有收藏任何主题。" + unread: "你没有未阅主题。" + new: "你没有新主题可读。" + read: "你尚未阅读任何主题。" + posted: "你尚未在任何主题中发帖。" latest: "没有最新的讨论主题。" hot: "没有热门主题。" category: "没有 {{category}} 分类的主题。" top: "没有最佳主题。" educate: - new: '

    这里是您的新主题。

    默认情况下,最近两天创建的主题是新主题,并会显示一个 的标识。

    您可以在您的 设置中改变这一行为。

    ' - unread: '

    这里是您的未读主题。

    默认情况下,下述主题将被认为是未读的,并会显示未读数目: 1 如果您:

    • 创建了该主题
    • 回复了该主题
    • 阅读该主题超过 4 分钟

    或者你在主题底部的通知控制中选择了追踪或监视。

    您可以改变您的用户设置.

    ' - starred: '

    您收藏的主题会出现在这里。

    要收藏主题或者取消,请使用

    • 在主题标题的右边
    • 主题底部的星标按钮
    ' + new: '

    这里是你的新主题。

    默认情况下,最近两天创建的主题是新主题,并会显示一个 的标识。

    你可以在你的 设置中改变这一行为。

    ' + unread: '

    这里是你的未读主题。

    默认情况下,下述主题将被认为是未读的,并会显示未读数目: 1 如果你:

    • 创建了该主题
    • 回复了该主题
    • 阅读该主题超过 4 分钟

    或者你在主题底部的通知控制中选择了追踪或监视。

    你可以改变你的用户设置.

    ' + starred: '

    你收藏的主题会出现在这里。

    要收藏主题或者取消,请使用

    • 在主题标题的右边
    • 主题底部的星标按钮
    ' bottom: latest: "没有更多主题可看了。" hot: "没有更多热门主题可看了。" @@ -729,8 +725,8 @@ zh_CN: loading: '载入主题中...' invalid_access: title: "这是私密主题" - description: "抱歉,您没有访问此主题的权限!" - login_required: "您需要登陆才能阅读此主题。" + description: "抱歉,你没有访问此主题的权限!" + login_required: "你需要登录才能阅读此主题。" server_error: title: "载入主题失败" description: "抱歉,无法载入此主题。有可能是网络连接问题导致的,请重试。如果问题始终存在,请告诉我们。" @@ -738,11 +734,11 @@ zh_CN: title: "未找到主题" description: "抱歉,无法找到此主题。可能已被版主删除。" total_unread_posts: - other: "在这个主题中,您有 {{count}} 条未读的帖子" + other: "在这个主题中,你有 {{count}} 条未读的帖子" unread_posts: - other: "在这个主题中,您有 {{count}} 条未读的帖子" + other: "在这个主题中,你有 {{count}} 条未读的帖子" new_posts: - other: "自您上一次阅读此主题后,又有 {{count}} 个新帖子发表" + other: "自你上一次阅读此主题后,又有 {{count}} 个新帖子发表" likes: other: "本主题已有 {{number}} 次赞" back_to_list: "返回主题列表" @@ -774,44 +770,44 @@ zh_CN: position: "%{total}帖中的第%{current}帖" notifications: reasons: - '3_6': '因为您在关注此分类,所以您将收到相关通知。' - '3_5': '因为您自动关注了此主题,所以您将收到相关通知。' - '3_2': '因为您在关注此主题,所以您将收到相关通知。' - '3_1': '因为您创建了此主题,所以您将收到相关通知。' - '3': '因为您在关注此主题,所以您将收到相关通知。' + '3_6': '因为你在关注此分类,所以你将收到相关通知。' + '3_5': '因为你自动关注了此主题,所以你将收到相关通知。' + '3_2': '因为你在关注此主题,所以你将收到相关通知。' + '3_1': '因为你创建了此主题,所以你将收到相关通知。' + '3': '因为你在关注此主题,所以你将收到相关通知。' '2_8': '因为你在跟踪此分类,所以你将收到相关通知。' - '2_4': '因为您在此主题内发表了回复,所以您将收到相关通知。' - '2_2': '因为您在追踪此主题,所以您将收到相关通知。' - '2': '因为您阅读了此主题,所以您将收到相关通知。' - '1_2': '只有当有人@您或者回复您的帖子时,您才会收到通知。' - '1': '只有当有人@您或者回复您的帖子时,您才会收到通知。' + '2_4': '因为你在此主题内发表了回复,所以你将收到相关通知。' + '2_2': '因为你在追踪此主题,所以你将收到相关通知。' + '2': '因为你阅读了此主题,所以你将收到相关通知。' + '1_2': '只有当有人@你或者回复你的帖子时,你才会收到通知。' + '1': '只有当有人@你或者回复你的帖子时,你才会收到通知。' '0_7': '你将忽略关于此分类的所有通知。' - '0_2': '您将忽略关于此主题的所有通知。' - '0': '您将忽略关于此主题的所有通知。' + '0_2': '你将忽略关于此主题的所有通知。' + '0': '你将忽略关于此主题的所有通知。' watching_pm: title: "关注" - description: "一旦私信中有新消息,您都会收到通知。未读和新帖子的数量将显示在主题列表的每个主题后。" + description: "一旦私信中有新消息,你都会收到通知。未读和新帖子的数量将显示在主题列表的每个主题后。" watching: title: "关注" - description: "一旦有关于这个主题的新帖子发表,您都会收到通知。未读贴的数量将出现在主题列表中每个主题的标题后。" + description: "一旦有关于这个主题的新帖子发表,你都会收到通知。未读贴的数量将出现在主题列表中每个主题的标题后。" tracking_pm: title: "追踪" - description: "未读和新帖子的数量将出现在私信旁。您只会在别人@您或回复您的主题时才会被提醒。" + description: "未读和新帖子的数量将出现在私信旁。你只会在别人@你或回复你的主题时才会被提醒。" tracking: title: "追踪" - description: "未读贴和新帖的数量将出现在主题列表中每个主题的标题后。您只会在别人@您或有人回复了您的帖子时才会收到通知。" + description: "未读贴和新帖的数量将出现在主题列表中每个主题的标题后。你只会在别人@你或有人回复了你的帖子时才会收到通知。" regular: title: "常规" - description: "当有人@您或者回复您的帖子时,您才会收到通知。" + description: "当有人@你或者回复你的帖子时,你才会收到通知。" regular_pm: title: "常规" - description: "当有人@您或者回复您的私信时,您才会收到通知。" + description: "当有人@你或者回复你的私信时,你才会收到通知。" muted_pm: title: "防打扰" - description: "您不会收到关于此私信的任何通知。" + description: "你不会收到关于此私信的任何通知。" muted: title: "防打扰" - description: "您不会收到关于此主题的任何通知,也不会在您的未阅选项卡中显示。" + description: "你不会收到关于此主题的任何通知,也不会在你的未阅选项卡中显示。" actions: recover: "撤销删除主题" delete: "删除主题" @@ -841,7 +837,7 @@ zh_CN: flag_topic: title: '报告' help: '私下报告本帖以引起注意或者发送一条匿名通知' - success_message: '您已成功报告本帖。' + success_message: '你已成功报告本帖。' inviting: "邀请中..." automatically_add_to_groups_optional: "这个邀请也包括了这些群组的权限:(可选,仅管理员)" automatically_add_to_groups_required: "这个邀请也包括了访问这些群组的权限:(可选,仅管理员)" @@ -856,11 +852,11 @@ zh_CN: invite_reply: title: ' 邀请' action: '邮件邀请' - help: '向您的朋友发送邀请,他们只需要一个点击就能回复这个主题' - to_topic: "我们将发送一封简洁的邮件。您的朋友可以立即加入并通过点击一个链接回复这个主题,不需要登录。" - to_forum: "我们将发送一封简洁的邮件让您的朋友通过点击一个链接参与讨论,不需要登陆。" + help: '向你的朋友发送邀请,他们只需要一个点击就能回复这个主题' + to_topic: "我们将发送一封简洁的邮件。你的朋友可以立即加入并通过点击一个链接回复这个主题,不需要登录。" + to_forum: "我们将发送一封简洁的邮件让你的朋友通过点击一个链接参与讨论,不需要登录。" email_placeholder: '电子邮箱地址' - success: "我们发了一封邀请邮件给 {{email}}。我们将在邀请被接受后通知您。检查您的用户中的邀请标签页来追踪您的邀请。" + success: "我们发了一封邀请邮件给 {{email}}。我们将在邀请被接受后通知你。检查你的用户中的邀请标签页来追踪你的邀请。" error: "抱歉,我们不能邀请此人,可能他/她已经是本站用户了?" login_reply: '登录后回复' filters: @@ -873,7 +869,7 @@ zh_CN: topic_name: "新主题名" error: "拆分主题时发生错误。" instructions: - other: "您将创建一个新的主题,并包含 {{count}} 个您选择的帖子。" + other: "你将创建一个新的主题,并包含 {{count}} 个你选择的帖子。" merge_topic: title: "合并主题" action: "合并主题" @@ -898,7 +894,7 @@ zh_CN: select_all: 全选 deselect_all: 全不选 description: - other: 您已经选择了 {{count}} 个帖子。 + other: 你已经选择了 {{count}} 个帖子。 post: reply: "回复 {{replyAvatar}} {{username}} 发表的 {{link}}" reply_topic: "回复 {{link}}" @@ -923,18 +919,18 @@ zh_CN: has_replies: other: "回复" errors: - create: "抱歉,在创建您的帖子时发生了错误。请重试。" - edit: "抱歉,在编辑您的帖子时发生了错误。请重试。" + create: "抱歉,在创建你的帖子时发生了错误。请重试。" + edit: "抱歉,在编辑你的帖子时发生了错误。请重试。" upload: "抱歉,在上传文件时发生了错误。请重试。" - attachment_too_large: "抱歉,您上传的附件太大了(最大不能超过 {{max_size_kb}}kb)。" + attachment_too_large: "抱歉,你上传的附件太大了(最大不能超过 {{max_size_kb}}kb)。" file_too_large: "抱歉,你上传的附件太大了(最大限制 {{max_size_kb}}kb)。" - too_many_uploads: "抱歉, 您只能一次上传一张图片。" - upload_not_authorized: "抱歉, 您不能上传此类型文件(可上传的文件类型有: {{authorized_extensions}})。" + too_many_uploads: "抱歉, 你只能一次上传一张图片。" + upload_not_authorized: "抱歉, 你不能上传此类型文件(可上传的文件类型有: {{authorized_extensions}})。" image_upload_not_allowed_for_new_user: "抱歉,新注册用户无法上传图片。" attachment_upload_not_allowed_for_new_user: "抱歉,新注册用户无法上传附件。" - attachment_download_requires_login: "抱歉,但是您需要登陆后才能下载附件。" + attachment_download_requires_login: "抱歉,但是你需要登录后才能下载附件。" abandon: - confirm: "您确定要放弃编辑您的帖子吗?" + confirm: "你确定要放弃编辑你的帖子吗?" no_value: "否" yes_value: "是" via_email: "通过电子邮件发送的帖子" @@ -945,10 +941,10 @@ zh_CN: controls: reply: "开始给本帖撰写回复" like: "赞本帖" - has_liked: "您已经赞了本帖" + has_liked: "你已经赞了本帖" undo_like: "撤销赞" edit: "编辑本帖" - edit_anonymous: "抱歉,但是您需要登陆后才能编辑该贴。" + edit_anonymous: "抱歉,但是你需要登录后才能编辑该贴。" flag: "私下报告本帖以提醒管理人员关注或发送私信通知" delete: "删除本帖" undelete: "恢复本帖" @@ -956,7 +952,7 @@ zh_CN: more: "更多" delete_replies: confirm: - other: "您也想要删除 {{count}} 个直接回复这个帖子的回复么?" + other: "你也想要删除 {{count}} 个直接回复这个帖子的回复么?" yes_value: "是,删除回复" no_value: "否,仅删除该帖" admin: "帖子管理" @@ -997,31 +993,31 @@ zh_CN: like: "{{icons}} 赞了它" vote: "{{icons}} 对它投票" by_you: - off_topic: "您报告它偏离主题" - spam: "您报告它为垃圾信息" - inappropriate: "您报告它为不当内容" - notify_moderators: "您向版主报告了它" - notify_user: "您对该用户发起了一个私下交流" - bookmark: "您对该帖做了书签" - like: "您赞了它" - vote: "您对该帖投票支持" + off_topic: "你报告它偏离主题" + spam: "你报告它为垃圾信息" + inappropriate: "你报告它为不当内容" + notify_moderators: "你向版主报告了它" + notify_user: "你对该用户发起了一个私下交流" + bookmark: "你对该帖做了书签" + like: "你赞了它" + vote: "你对该帖投票支持" by_you_and_others: off_topic: - other: "您和其他 {{count}} 人报告它偏离主题" + other: "你和其他 {{count}} 人报告它偏离主题" spam: - other: "您和其他 {{count}} 人报告它为垃圾信息" + other: "你和其他 {{count}} 人报告它为垃圾信息" inappropriate: - other: "您和其他 {{count}} 人报告它为不当内容" + other: "你和其他 {{count}} 人报告它为不当内容" notify_moderators: - other: "您和其他 {{count}} 人报告它需要审核" + other: "你和其他 {{count}} 人报告它需要审核" notify_user: - other: "您和其他 {{count}} 人发送了一条私信给这个用户" + other: "你和其他 {{count}} 人发送了一条私信给这个用户" bookmark: - other: "您和其他 {{count}} 人收藏了这个帖子" + other: "你和其他 {{count}} 人收藏了这个帖子" like: - other: "您和其他 {{count}} 人赞了这个帖子" + other: "你和其他 {{count}} 人赞了这个帖子" vote: - other: "您和其他 {{count}} 人支持这个帖子" + other: "你和其他 {{count}} 人支持这个帖子" by_others: off_topic: other: "{{count}} 人报告它偏离主题" @@ -1045,7 +1041,7 @@ zh_CN: zero: 未编辑 delete: confirm: - other: "您确定要删除这些帖子吗?" + other: "你确定要删除这些帖子吗?" revisions: controls: first: "第一版" @@ -1091,7 +1087,7 @@ zh_CN: foreground_color: "前景色" name_placeholder: "应该简明扼要。" color_placeholder: "任何网页颜色" - delete_confirm: "您确定要删除此分类吗?" + delete_confirm: "你确定要删除此分类吗?" delete_error: "在删除此分类时发生了错误。" list: "列出分类" no_description: "请为本分类添加描述信息。" @@ -1117,16 +1113,16 @@ zh_CN: notifications: watching: title: "关注" - description: "您将会自动监视这些分类中的所有新主题。您会收到新帖子或新主题的同志,未读和新帖子数量将出现在每个主题后。" + description: "你将会自动监视这些分类中的所有新主题。你会收到新帖子或新主题的同志,未读和新帖子数量将出现在每个主题后。" tracking: title: "追踪" - description: "您将会自动追踪这些分类中的所有新主题。未读和新帖子数量将出现在每个主题后。" + description: "你将会自动追踪这些分类中的所有新主题。未读和新帖子数量将出现在每个主题后。" regular: title: "常规" - description: "当有人@您或者回复您的帖子时,您才会收到通知。" + description: "当有人@你或者回复你的帖子时,你才会收到通知。" muted: title: "免打扰" - description: "你不会收到这些分类中的任何新主题通知,并且他们将不会出现在您的未读列表中。" + description: "你不会收到这些分类中的任何新主题通知,并且他们将不会出现在你的未读列表中。" flagging: title: '感谢帮助社群远离邪恶!' private_reminder: '标记是不公开的,只有职员才可以见到' @@ -1134,19 +1130,19 @@ zh_CN: take_action: "立即执行" notify_action: '通知' delete_spammer: "删除垃圾发布者" - delete_confirm: "您将删除该用户的 %{posts} 个帖子和 %{topics} 个主题,删除该账户,阻止其IP地址 %{ip_address} 再次注册,并将其邮件地址 %{email} 加入黑名单。确定吗?" + delete_confirm: "你将删除该用户的 %{posts} 个帖子和 %{topics} 个主题,删除该账户,阻止其IP地址 %{ip_address} 再次注册,并将其邮件地址 %{email} 加入黑名单。确定吗?" yes_delete_spammer: "确定" ip_address_missing: "(N/A)" hidden_email_address: "(隐藏)" submit_tooltip: "提交私有标记" take_action_tooltip: "与其等待更多的社区的标志, 不如立刻到达标志." - cant: "抱歉,当前您不能报告本帖。" + cant: "抱歉,当前你不能报告本帖。" formatted_name: off_topic: "偏题" inappropriate: "不合适" spam: "广告" custom_placeholder_notify_user: "请具体说明,有建设性的,再友好一些。" - custom_placeholder_notify_moderators: "让我们知道您关心地是什么,并尽可能地提供相关链接和例子。" + custom_placeholder_notify_moderators: "让我们知道你关心地是什么,并尽可能地提供相关链接和例子。" custom_message: at_least: "输入至少 {{n}} 个字符" more: "还差 {{n}} 个..." @@ -1216,10 +1212,10 @@ zh_CN: help: "最近最受欢迎的主题" starred: title: "收藏" - help: "您收藏的主题" + help: "你收藏的主题" read: title: "已阅" - help: "您已经阅读过的主题" + help: "你已经阅读过的主题" categories: title: "分类" title_in: "分类 - {{categoryName}}" @@ -1229,7 +1225,7 @@ zh_CN: zero: "未读" one: "1 个未读主题" other: "{{count}} 个未读主题" - help: "您正在监视或追踪的主题中有未阅帖子的主题" + help: "你正在监视或追踪的主题中有未阅帖子的主题" lower_title_with_count: one: "1 条未读" other: "{{count}} 条未读" @@ -1245,7 +1241,7 @@ zh_CN: help: "最近几天创建的主题" posted: title: "我的帖子" - help: "您发表过帖子的主题" + help: "你发表过帖子的主题" category: title: zero: "{{categoryName}}" @@ -1268,7 +1264,7 @@ zh_CN: this_week: "本周" today: "今天" other_periods: "查看更多热门主题" - browser_update: '抱歉,您的浏览器版本太低,无法正常访问该站点。。请升级您的浏览器。' + browser_update: '抱歉,你的浏览器版本太低,无法正常访问该站点。。请升级你的浏览器。' permission_types: full: "创建 / 回复 / 阅读" create_post: "回复 / 阅读" @@ -1282,16 +1278,16 @@ zh_CN: title: "仪表盘" last_updated: "最近更新于:" version: "安装的版本" - up_to_date: "您正在运行最新的论坛版本。" + up_to_date: "你正在运行最新的论坛版本。" critical_available: "有一个关键更新可用。" updates_available: "目前有可用更新。" please_upgrade: "请升级!" no_check_performed: "检测更新未执行,请确保 sidekiq 在正常运行。" stale_data: "最近一次检查更新未执行,请确保 sidekiq 在正常运行。" - version_check_pending: "看来您最近刚更新过。太棒了!" + version_check_pending: "看来你最近刚更新过。太棒了!" installed_version: "已安装" latest_version: "最新版本" - problems_found: "您安装的论坛目前有以下问题:" + problems_found: "你安装的论坛目前有以下问题:" last_checked: "上次检查" refresh_problems: "刷新" no_problems: "找不到问题." @@ -1413,7 +1409,7 @@ zh_CN: enable: title: "启用只读模式" text: "打开只读模式" - confirm: "您确定要打开只读模式吗?" + confirm: "你确定要打开只读模式吗?" disable: title: "禁用只读模式" text: "关闭只读模式" @@ -1433,11 +1429,11 @@ zh_CN: cancel: text: "取消" title: "取消当前操作" - confirm: "您确定要取消当前操作吗?" + confirm: "你确定要取消当前操作吗?" backup: text: "备份" title: "建立一个备份" - confirm: "您确定要开始建立一个备份吗?" + confirm: "你确定要开始建立一个备份吗?" without_uploads: "是(不包括文件)" download: text: "下载" @@ -1445,20 +1441,25 @@ zh_CN: destroy: text: "删除" title: "删除备份" - confirm: "您确定要删除该备份吗?" + confirm: "你确定要删除该备份吗?" restore: is_disabled: "站点设置中禁用了恢复功能。" text: "恢复" title: "恢复该备份" - confirm: "您确定要重置该备份吗?" + confirm: "你确定要重置该备份吗?" rollback: text: "回滚" title: "将数据库回滚到之前的工作状态" - confirm: "您确定要将数据库回滚到之前的工作状态吗?" + confirm: "你确定要将数据库回滚到之前的工作状态吗?" export_csv: - success: "导出开始,您将马上看到进度提示。" failed: "导出失败。请检查日志。" button_text: "导出" + button_title: + user: "以CSV格式导出所有用户列表" + staff_action: "以CSV格式导出所有职员操作历史记录" + screened_email: "以 CSV 格式导出所有已显示的电子邮件列表。" + screened_ip: "以 CSV 格式导出所有已显示的IP地址列表。" + screened_url: "以 CSV 格式导出所有已显示的URL列表。" customize: title: "定制" long_title: "站点定制" @@ -1488,12 +1489,12 @@ zh_CN: colors: title: "颜色" long_title: "颜色方案" - about: "颜色方案让您能够让您在不写 CSS 的情况下更改色彩。添加一种颜色以开始。" + about: "颜色方案让你能够让你在不写 CSS 的情况下更改色彩。添加一种颜色以开始。" new_name: "新的颜色方案" copy_name_prefix: "复制于" delete_confirm: "删除这个颜色方案?" undo: "重做" - undo_title: "撤销您对这个颜色的编辑至上一次保存的状态。" + undo_title: "撤销你对这个颜色的编辑至上一次保存的状态。" revert: "撤销" revert_title: "重置这个颜色至 Discourse 的默认颜色方案" primary: @@ -1535,7 +1536,7 @@ zh_CN: all: "所有" sending_test: "发送测试邮件..." error: "错误 - %{server_error}" - test_error: "发送测试邮件时遇到问题。请再检查一遍邮件设置,确认您的主机没有封锁邮件链接,然后重试。" + test_error: "发送测试邮件时遇到问题。请再检查一遍邮件设置,确认你的主机没有封锁邮件链接,然后重试。" sent: "已发送" skipped: "跳过" sent_at: "发送时间" @@ -1626,7 +1627,7 @@ zh_CN: title: "被屏蔽的 IP" description: '受监视的 IP 地址,使用“放行”可将 IP 地址加入白名单。' delete_confirm: "确定要撤销对 IP 地址为 %{ip_address} 的规则?" - roll_up_confirm: "您确定要将常用的 IP 地址归类为子网地址吗?" + roll_up_confirm: "你确定要将常用的 IP 地址归类为子网地址吗?" rolled_up_some_subnets: "成功地折叠了 IP 封禁记录至这些子网: %{subnets}。" rolled_up_no_subnet: "无法折叠。" actions: @@ -1644,7 +1645,7 @@ zh_CN: title: "错误日志" impersonate: title: "检视用户视角" - help: "使用此工具来检视其他用户的帐号以方便调试。您应该在完成后立即登出。" + help: "使用此工具来检视其他用户的帐号以方便调试。你应该在完成后立即登出。" users: title: '用户' create: '添加管理员用户' @@ -1698,7 +1699,7 @@ zh_CN: suspend_reason: "封禁的理由" suspended_by: "封禁操作者:" delete_all_posts: "删除所有帖子" - delete_all_posts_confirm: "您将删除 %{posts} 个帖子和 %{topics} 个主题,确认吗?" + delete_all_posts_confirm: "你将删除 %{posts} 个帖子和 %{topics} 个主题,确认吗?" suspend: "禁止" unsuspend: "解禁" suspended: "已禁止?" @@ -1747,7 +1748,7 @@ zh_CN: other: "不能删除所有帖子。一些帖子发表于 %{count} 天前。(设置项:delete_user_max_post_age)" cant_delete_all_too_many_posts: other: "不能删除所有帖子,因为用户有超过 %{count} 个帖子。(delete_all_posts_max)" - delete_confirm: "您确定要删除这个用户吗?这个操作是不可逆的!" + delete_confirm: "你确定要删除这个用户吗?这个操作是不可逆的!" delete_and_block: "删除并封禁该邮件地址和IP地址" delete_dont_block: "仅删除" deleted: "该用户已被删除。" @@ -1816,7 +1817,7 @@ zh_CN: edit: "编辑" delete: "删除" cancel: "取消" - delete_confirm: "您确定要删除这个用户字段么?" + delete_confirm: "你确定要删除这个用户字段么?" required: title: "在注册时需要填写?" enabled: "必填" @@ -1874,9 +1875,9 @@ zh_CN: granted_at: 授予于 save: 保存 delete: 删除 - delete_confirm: 您确定要删除此徽章吗? + delete_confirm: 你确定要删除此徽章吗? revoke: 撤销 - revoke_confirm: 您确定要撤销此徽章吗? + revoke_confirm: 你确定要撤销此徽章吗? edit_badges: 编辑徽章 grant_badge: 授予徽章 granted_badges: 已授予的徽章 @@ -1910,7 +1911,7 @@ zh_CN: error_help: "查看下列关于徽章查询的帮助链接。" bad_count_warning: header: "警告!" - text: "有授予的样本消失。这在徽章查询返回用户 ID 或者帖子 ID 不存在的时候发生。这可能导致未预期的结果发生——请再次检查您的查询。" + text: "有授予的样本消失。这在徽章查询返回用户 ID 或者帖子 ID 不存在的时候发生。这可能导致未预期的结果发生——请再次检查你的查询。" grant_count: zero: "没有徽章可以被授予。" one: "已授予 1 个徽章。" @@ -1942,7 +1943,6 @@ zh_CN: back: 'u 返回' up_down: 'kj 移动选择焦点 ↑ ↓' open: 'o 或者 Enter 打开选中的主题' - next_prev: 'shift j/shift k 下一个/上一个选区' application: title: '应用' create: 'c 创建一个新主题' @@ -1957,11 +1957,8 @@ zh_CN: actions: title: '动作' star: 'f 收藏主题' - pin_unpin_topic: 'shift p 置顶/接触置顶主题' - share_topic: 'shift s 分享主题' share_post: 's 分享帖子' reply_as_new_topic: 't 回复为联结主题' - reply_topic: 'shift r 回复主题' reply_post: 'r 回复帖子' quote_post: 'q 引用帖子' like: 'l 赞帖子' @@ -1983,7 +1980,7 @@ zh_CN: other: "+%{count} 更多" granted: other: "%{count} 个已授予" - select_badge_for_title: 选择一个徽章用作您的头衔 + select_badge_for_title: 选择一个徽章用作你的头衔 none: "" badge_grouping: getting_started: diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index 7d625dbbaf..98eea8cc7f 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -68,7 +68,6 @@ zh_TW: other: "%{count} 天前" share: topic: '在此話題內分享連結' - post: '在文章 #%{postNumber} 內分享連結' close: '關閉' twitter: '在 Twitter 分享此連結' facebook: '在 Facebook 分享此連結' @@ -223,12 +222,20 @@ zh_TW: location_not_found: (unknown) organisation: 組織 phone: 電話 + other_accounts: "其他帳號正在使用相同 IP 地址" + delete_other_accounts: "刪除 %{count}" + username: "用戶名稱" + trust_level: "TL" + read_time: "閱讀時間" + topics_entered: "已閱讀的討論話題" + post_count: "# 文章" + confirm_delete_other_accounts: "你確定要刪除這些帳號?" user: said: "{{username}}:" profile: "基本資料" mute: "靜音" edit: "編輯喜好設定" - download_archive: "下載我的文章存檔" + new_private_message: "新私人訊息" private_message: "私人訊息" private_messages: "訊息" activity_stream: "活動" @@ -265,6 +272,7 @@ zh_TW: deleted_yourself: "你的帳號已成功刪除" delete_yourself_not_allowed: "帳號不能刪除。請聯絡管理人。" unread_message_count: "訊息" + admin_delete: "刪除" staff_counters: flags_given: "有幫助的投訴" flagged_posts: "已投訴的文章" @@ -312,13 +320,32 @@ zh_TW: instructions: "背景會被置中,且默認寬度為850px。" email: title: "電子郵件" + instructions: "我們不會公開您的電子郵件信箱。" + ok: "我們將寄一封確認郵件給您。" + invalid: "請輸入有效的電子郵件地址。" + authenticated: "你的 Email 已由 {{provider}} 驗證完成。" + frequency: + zero: "如果您沒有閱讀過重要通知,我們會立即發送電子郵件給您。" + one: "如果您沒有閱讀過重要通知,我們只會在您沒有登入網站時才會發送電子郵件給您。" + other: "如果您沒有閱讀過重要通知,我們只會在您最近 {{count}} 分鐘內沒有登入網站時才會發送電子郵件給您。" name: title: "名稱" + instructions: "您的全名 (選填)。" + too_short: "你的名稱太短。" + ok: "你的名稱符合要求。" username: title: "用戶名稱" + instructions: "獨一無二,沒有空白,夠短。" + short_instructions: "其他人可以輸入 @{{username}} 提到你。" + available: "你的用戶名稱可以使用。" + global_match: "電子郵件地址與註冊的用戶名稱相符。" global_mismatch: "已經註冊過了,請試試看 {{suggestion}}?" not_available: "無法使用,請試試看 {{suggestion}}?" + too_short: "你的用戶名稱太短。" + too_long: "你的用戶名稱太長。" checking: "正在檢查用戶名稱是否已經有人使用..." + enter_email: '找到用戶名稱,請輸入相符的電子郵件地址。' + prefilled: "電子郵件地址與此註冊的用戶名稱相符。" locale: title: "界面語言" instructions: "使用者介面的語言,當頁面重新整理的時候會更換成你的設定。" @@ -385,7 +412,6 @@ zh_TW: none: "你尚未邀請任何人。你可以發送個別邀請,或者透過上傳邀請名單一次邀請一群人。" text: "從檔案大量邀請" uploading: "正在上傳..." - success: "檔案已成功上傳,你很快就會收到進度通知。" error: "上傳 '{{filename}}' 時發生問題:{{message}}" password: title: "密碼" @@ -393,7 +419,6 @@ zh_TW: common: "此密碼太簡單。" ok: "你的密碼符合要求。" instructions: "至少 %{count} 個字。" - associated_accounts: "關聯的帳號" ip_address: title: "最近的 IP 位址" registration_ip_address: @@ -429,6 +454,8 @@ zh_TW: fixed: "載入頁面" close: "關閉" assets_changed_confirm: "此網站剛剛已更新,你要重整頁面以獲得最新版本嗎?" + logout: "已登出" + refresh: "重新整理" read_only_mode: enabled: "管理員開啟了唯讀模式。你可以繼續瀏覽網站,但一些互動功能可能無法使用。" login_disabled: "在唯讀模式下不能登入" @@ -542,6 +569,8 @@ zh_TW: reply_here: "在此回覆" reply: "回覆" cancel: "取消" + create_topic: "發表話題" + create_pm: "私人訊息" title: "或者按 Ctrl+Enter" users_placeholder: "新增用戶" title_placeholder: "用一個簡短的句子來描述想討論的內容。" @@ -612,7 +641,10 @@ zh_TW: title_with_attachments: "加入一張圖片或一個檔案" from_my_computer: "從我的電腦" from_the_web: "從網站" + remote_tip: "圖片連結" + remote_tip_with_attachments: "圖片或文件連結 ({{authorized_extensions}})" local_tip: "按此從你的電腦選擇圖片" + local_tip_with_attachments: "從你的裝置中選擇圖片或文件(支援的格式: ({{authorized_extensions}}))。" hint: "(你也可以將檔案拖放至編輯器直接上傳)" hint_for_supported_browsers: "( 你也可以將圖片拖放或貼上至編輯器內直接上傳 )" uploading: "正在上傳" @@ -626,6 +658,7 @@ zh_TW: user: "搜尋 @{{username}} 的文章" category: "搜尋 \"{{category}}\" 分類" topic: "搜尋此討論話題" + private_messages: "查詢私人訊息" site_map: "到另一個討論話題列表或分類" go_back: '返回' not_logged_in_user: '用戶頁面包含目前活動及喜好的總結' @@ -678,6 +711,7 @@ zh_TW: top: "沒有更多精選討論話題。" topic: filter_to: "在討論話題裡顯示 {{post_count}} 文章" + create: '新討論話題' create_long: '建立新討論話題' private_message: '建立私人訊息' list: '討論話題' @@ -761,10 +795,13 @@ zh_TW: description: "未讀和新文章的數量將會出現在私訊旁。你只會在別人標記您 @name 或回覆您的主題時才會被提醒。" tracking: title: "追蹤" + description: "未讀和新文章的數量將出現在討論話題旁。您只會在別人@您或回覆您的主題時才會收到通知。" regular: title: "普通" + description: "只有當有人@您或者回覆您的文章時,您才會收到通知。" regular_pm: title: "一般" + description: "只有當有人@您或者用私人訊息回覆您的文章時,您才會收到通知。" muted_pm: title: "靜音" description: "你將不會收到任何關於此私人訊息的通知,此討論話題也不會出現在你的未讀清單裡。" @@ -825,6 +862,7 @@ zh_TW: filters: n_posts: other: "{{count}} 則文章" + cancel: "取消過濾" split_topic: title: "移至新討論話題" action: "移至新討論話題" @@ -885,6 +923,7 @@ zh_TW: edit: "抱歉,編輯你的文章時發生錯誤,請再試一次。" upload: "抱歉,上傳你的檔案時發生錯誤,請再試一次。" attachment_too_large: "抱歉,你想上傳的檔案太大 (最大限制為 {{max_size_kb}} kb)。" + file_too_large: "抱歉,你想上傳的檔案太大 (最大限制為 {{max_size_kb}} kb)。" too_many_uploads: "抱歉,一次只能上傳一個檔案。" upload_not_authorized: "抱歉,你想上傳的是不允許的檔案 (允許的副檔名: {{authorized_extensions}})." image_upload_not_allowed_for_new_user: "抱歉,新用戶不可上傳圖片。" @@ -1034,6 +1073,7 @@ zh_TW: general: '一般' settings: '設定' delete: '刪除分類' + create: '新分類' save: '儲存分類' creation_error: 建立分類時發生錯誤。 save_error: 儲存分類時發生錯誤。 @@ -1073,14 +1113,19 @@ zh_TW: notifications: watching: title: "關注" + description: "您將會自動監視這些分類中的所有新主題。您會收到新文章或新主題的通知,未讀和新文章數量將出現在每個主題後。" tracking: title: "追蹤" + description: "您將會自動追蹤這些分類中的所有新主題。未讀和新文章數量將出現在每個主題後。" regular: title: "一般" + description: "只有當有人@您或回覆您的文章時,您才會收到通知。" muted: title: "靜音" description: "你將不會收到這些分類中的新討論話題通知,它們也不會出現在你的未讀欄內。" flagging: + title: '感謝幫助社群遠離邪惡!' + private_reminder: '標記是不公開的,只有 工作人員才可看到' action: '投訴文章' take_action: "執行動作" notify_action: '通知' @@ -1092,11 +1137,18 @@ zh_TW: submit_tooltip: "送出私人投訴" take_action_tooltip: "使其立刻達到投訴門檻,不用等待更多社群投訴" cant: "抱歉,你目前無法投訴此文章。" + formatted_name: + off_topic: "離題內容" + inappropriate: "不當內容" + spam: "垃圾內容" + custom_placeholder_notify_user: "請具體說明出有建設性且溫和的意見。" + custom_placeholder_notify_moderators: "讓我們知道您的意見,並請盡可能地提供相關連結和例子。" custom_message: at_least: "請至少輸入 {{n}} 個字" more: "還需要 {{n}} 個字..." left: "還剩下 {{n}} 個字" flagging_topic: + title: "感謝幫助社群遠離邪惡!" action: "投訴討論話題" notify_action: "Notify" topic_map: @@ -1120,9 +1172,17 @@ zh_TW: help: "此討論話題已置頂,將顯示在它所屬分類的討論話題列表的最上方" archived: help: "此討論話題已封存,已被凍結無法再修改" + invisible: + help: "此討論話題已隱藏,將不會出現在討論話題列表,只能以直接連結開啟。" posts: "文章" posts_lowercase: "文章" posts_long: "此討論話題有 {{number}} 篇文章" + posts_likes_MF: | + 這個主題有 {count, plural, one {1 篇文章} other {# 篇文章}},{ratio, select, + low {並被很多人按讚了} + med {並被非常多人按讚了} + high {並被特別多人按讚了} + other {}} original_post: "原始文章" views: "觀看" views_lowercase: "觀看" @@ -1137,6 +1197,9 @@ zh_TW: category_title: "分類" history: "歷史" changed_by: "作者 {{author}}" + raw_email: + title: "原始 Email" + not_available: "不可使用" categories_list: "分類清單" filters: with_topics: "%{filter} 討論話題" @@ -1201,6 +1264,7 @@ zh_TW: this_week: "本週" today: "今天" other_periods: "看更多精選討論話題" + browser_update: '抱歉,您的瀏覽器版本太舊,無法正常訪問該站點。。請升級您的瀏覽器。' permission_types: full: "建立 / 回覆 / 觀看" create_post: "回覆 / 觀看" @@ -1264,7 +1328,9 @@ zh_TW: agree_flag: "同意投訴" agree_flag_title: "同意投訴且不更動文章" defer_flag: "延遲" + defer_flag_title: "移除標記;不需處理。" delete: "刪除" + delete_title: "刪除此標記文章。" delete_post_defer_flag: "刪除文章並且延緩檢舉" delete_post_defer_flag_title: "刪除文章,如果刪除的是討論話題的第一則文章,討論話題也將一併刪除" delete_post_agree_flag: "刪除文章並且同意檢舉" @@ -1387,10 +1453,19 @@ zh_TW: confirm: "你確定要回溯資料庫到以前的工作階段?" export_csv: failed: "匯出失敗。請觀看紀錄。" + button_text: "匯出" + button_title: + user: "以 CSV 格式匯出用戶清單" + staff_action: "以 CSV 格式匯出所有工作人員操作紀錄" + screened_email: "以 CSV 格式匯出所有已顯示的電子郵件列表" + screened_ip: "以 CSV 格式匯出所有已顯示的 IP 列表" + screened_url: "以 CSV 格式匯出所有已顯示的 URL 列表" customize: title: "客製化" long_title: "網站客製化" + css: "CSS" header: "標頭" + footer: "頁尾" override_default: "不要保含標準樣式" enabled: "已啟用?" preview: "預覽" @@ -1460,6 +1535,7 @@ zh_TW: settings: "設定" all: "所有" sending_test: "傳送測試郵件" + error: "錯誤 - %{server_error}" test_error: "發送測試電子郵件時遇到錯誤。請檢查你輸入的電子郵件地址,並確認網路提供者沒有封鎖郵件的發送,然後再試一次。" sent: "送出" skipped: "跳過" @@ -1473,6 +1549,7 @@ zh_TW: sent_test: "已送出!" delivery_method: "傳送方式" preview_digest: "預覽文摘" + preview_digest_desc: "預覽每周寄送給不活躍用戶的摘要郵件內容。" refresh: "重新整理" format: "格式" html: "html" @@ -1534,6 +1611,7 @@ zh_TW: check_email: "檢查電子郵件" delete_topic: "刪除討論話題" delete_post: "刪除文章" + impersonate: "檢視" screened_emails: title: "過濾的電子郵件地址" description: "以下的電子郵件地址將無法用來建立新用戶。" @@ -1549,6 +1627,9 @@ zh_TW: title: "過濾的 IP 位址" description: '受監控的 IP 位址,使用 "允許" 將 IP 位址加入白名單。' delete_confirm: "你確定要刪除 %{ip_address} 的規則嗎?" + roll_up_confirm: "您確定要將常用的 IP 地址歸類為子網域位址嗎?" + rolled_up_some_subnets: "成功地 roll up 了 IP 封鎖記錄至這些子網域: %{subnets}。" + rolled_up_no_subnet: "無法 roll up" actions: block: "封鎖" do_nothing: "允許" @@ -1557,23 +1638,30 @@ zh_TW: label: "新增:" ip_address: "IP 位址" add: "加入" + roll_up: + text: "Roll up" + title: "如果有至少 'min_ban_entries_for_roll_up' 個記錄,建立一個子網域封鎖記錄" logster: title: "錯誤紀錄" impersonate: title: "檢視角度" + help: "使用此工具以用戶的檢視角度進行除錯。完成後將需登出。" users: title: '用戶' create: '新增管理員' last_emailed: "最近寄出電子郵件" not_found: "抱歉,系統裡無此用戶名稱。" + id_not_found: "抱歉,系統裡無此用戶名稱。" active: "啟用的" show_emails: "顯示電子郵件" nav: new: "新用戶" active: "啟用的" pending: "申請中" + staff: '管理員' suspended: '已停權' blocked: '已封鎖' + suspect: '嫌疑' approved: "已批准?" approved_selected: other: "批准用戶 ({{count}})" @@ -1588,10 +1676,12 @@ zh_TW: regular: '信任等級 2 的使用者(成員)' leader: '信任等級 3 的使用者(正規)' elder: '信任等級 4 的使用者(領導者)' + staff: "管理員" admins: '管理員' moderators: '板主' blocked: '已封鎖的用戶' suspended: '已停權的用戶' + suspect: '嫌疑使用者' reject_successful: other: "成功拒絕 %{count} 個用戶。" reject_failures: @@ -1853,7 +1943,6 @@ zh_TW: back: 'u 返回' up_down: 'k/j 移動選區 ↑ ↓' open: 'oEnter 開啟已選擇討論話題' - next_prev: 'shift j/shift k 下一個/上一個選區' application: title: 'Application' create: 'c 表示新討論話題' @@ -1868,10 +1957,8 @@ zh_TW: actions: title: '行動' star: 'f 收藏討論話題' - share_topic: 'shift s 分享討論話題' share_post: 's 分享文章' reply_as_new_topic: 't 回覆為關聯主題' - reply_topic: 'shift r 回覆討論話題' reply_post: 'r 回覆文章' quote_post: 'q 引用文章' like: 'l 按讚' diff --git a/config/locales/server.ar.yml b/config/locales/server.ar.yml index 027d72f94b..15337e5d9c 100644 --- a/config/locales/server.ar.yml +++ b/config/locales/server.ar.yml @@ -24,7 +24,6 @@ ar: messages: too_long_validation: "إنها محددة بعدد حروف %{max} حرف; لقد أدخلت %{length}." invalid_boolean: "قيمة منطقية غير صحيحة" - taken: "تم إختياره من قبل" embed: load_from_remote: "هناك خطأ في تحميل هذا المنشور." bulk_invite: @@ -39,6 +38,15 @@ ar: too_many_mentions: zero: "عذراً، لا يمكنكم تنويه مستخدمين آخرين." one: "عذراً، يمكنكم فقط تنويه مستخدم واحد في هذا المنشور." + too_many_images: + zero: "عذراً. الأعضاء الجدد لا يمكنهم وضع الصور في المنشورات." + one: "عذراً. الأعضاء الجدد يمكنهم وضع صورة واحدة فقط في المنشور." + too_many_attachments: + zero: "عذراً. الأعضاء الجدد لا يمكنهم وضع مرفقات في المنشورات." + one: "عذراً. الأعضاء الجدد يمكنهم وضع مرفق واحد فقط في المنشور." + too_many_links: + zero: "عذراً. الأعضاء الجدد لا يمكنهم وضع الروابط في المنشورات." + one: "عذراً. الأعضاء الجدد يمكنهم وضع رابط واحد فقط في المنشور." has_already_been_used: "هو مستخدم بالفعل" next_page: "الصفحة التالية ←" prev_page: "→ الصفحة السابقة " diff --git a/config/locales/server.cs.yml b/config/locales/server.cs.yml index ad39de8d10..1f50888a25 100644 --- a/config/locales/server.cs.yml +++ b/config/locales/server.cs.yml @@ -38,7 +38,6 @@ cs: messages: too_long_validation: "je limitováno na %{max} znaků; zadali jste %{length}." invalid_boolean: "Nevalidní boolean." - taken: "už je zabráno" embed: load_from_remote: "Při načítání příspěvku nastala chyba." bulk_invite: diff --git a/config/locales/server.ja.yml b/config/locales/server.ja.yml index 8f094c4933..2a49ce7078 100644 --- a/config/locales/server.ja.yml +++ b/config/locales/server.ja.yml @@ -38,7 +38,6 @@ ja: messages: too_long_validation: "は、最大文字数(%{max}文字)を超えています。(入力したのは%{length}文字 ) " invalid_boolean: "無効なboolean." - taken: "既に取得されています" embed: load_from_remote: "投稿の読み込みに失敗しました。" bulk_invite: diff --git a/config/locales/server.nl.yml b/config/locales/server.nl.yml index 17825cca34..c0999c1409 100644 --- a/config/locales/server.nl.yml +++ b/config/locales/server.nl.yml @@ -780,11 +780,6 @@ nl: Geniet van je verblijf! welcome_invite: subject_template: "Welkom bij %{site_name}!" - csv_export_succeeded: - subject_template: "Data Export succesvol afgerond." - csv_export_failed: - subject_template: "Export mislukt" - text_body_template: "De export is mislukt. Bekijk de logbestanden." too_many_spam_flags: subject_template: "Account geblokkeerd" text_body_template: | diff --git a/config/locales/server.pt.yml b/config/locales/server.pt.yml index 85f89497b6..5ddfb5816f 100644 --- a/config/locales/server.pt.yml +++ b/config/locales/server.pt.yml @@ -6,10 +6,20 @@ # https://www.transifex.com/projects/p/discourse-org/ pt: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: - short_date_no_year: "D MMM" - short_date: "D MMM, YYYY" - long_date: "D de MMMM de YYYY h:mma" + short_date_no_year: "DD MMM" + short_date: "DD MMM, YYYY" + long_date: "DD de MMMM de YYYY hh:mm" time: formats: short: "%d/%m/%Y" @@ -17,25 +27,31 @@ pt: date_only: "%b %-d, %Y" title: "Discourse" topics: "Tópicos" - posts: "posts" + posts: "mensagens" loading: "Carregando" - powered_by_html: 'Desenvolvido por Discourse, melhor visualizado com JavaScript ativado' + powered_by_html: 'Desenvolvido por Discourse, e melhor visualizado com o JavaScript ativo' log_in: "Entrar" - via: "%{username} via %{site_name}" + via: "%{username} através de %{site_name}" is_reserved: "está reservado" + purge_reason: "Automaticamente eliminado devido a abandono, conta inativada" + disable_remote_images_download_reason: "O download remoto de imagens foi desativado por não haver espaço disponível no disco." errors: messages: too_long_validation: "está limitado a %{max} caracteres; inseriu %{length}." + invalid_boolean: "Valor lógico inválido." embed: - load_from_remote: "Houve um erro no carregamento desse post." + load_from_remote: "Ocorreu um erro no carregamento dessa mensagem." bulk_invite: file_should_be_csv: "O ficheiro a enviar deve estar em formato csv ou txt." backup: - operation_already_running: "Existe actualmente uma operação em andamento. Neste momento não é possível iniciar um novo trabalho. " - backup_file_should_be_tar_gz: "O ficheiro de backup deve ser um arquivo .tar.gz" - not_enough_space_on_disk: "Não existe espaço suficiente no disco para carregar este backup." + operation_already_running: "Existe atualmente uma operação em execução. Neste momento não é possível iniciar um novo trabalho. " + backup_file_should_be_tar_gz: "O ficheiro da cópia de segurança deve ser um arquivo .tar.gz" + not_enough_space_on_disk: "Não existe espaço suficiente no disco para carregar esta cópia de segurança." not_logged_in: "Necessita de ter sessão iniciada para fazer isso." - read_only_mode_enabled: "Este site encontra-se no modo apenas leitura. As interacções estão desctivadas." + read_only_mode_enabled: "Este sitío encontra-se no modo só de leitura. As interações estão desativadas." + too_many_replies: + one: "Pedimos desculpa mas novos utilizadores estão temporariamente limitados a 1 resposta no mesmo tópico." + other: "Pedimos desculpa mas novos utilizadores estão temporariamente limitados a %{count} respostas no mesmo tópico." embed: start_discussion: "Iniciar Discussão" continue: "Continuar Discussão" @@ -43,78 +59,82 @@ pt: one: "mais 1 resposta" other: "mais %{count} respostas" loading: "A carregar Discussão..." - permalink: "Permalink" + permalink: "Hiperligação Permanente" imported_from: "Este é um tópico de discussão de acompanhamento para a entrada original em %{link}" in_reply_to: "▶ %{username}" replies: one: "1 resposta" other: "%{count} respostas" too_many_mentions: - zero: "Desculpe, você não pode mencionar outros usuários." - one: "Desculpe, você pode mencionar apenas um outro usuário em um post." - other: "Desculpe, você pode mencionar apenas %{count} usuários em um post." + zero: "Pedimos desculpa, não pode mencionar outros utilizadores." + one: "Pedimos desculpa, pode mencionar apenas um outro utilizador numa mensagem." + other: "Pedimos desculpa, pode mencionar apenas %{count} utilizadores numa mensagem." too_many_mentions_newuser: - zero: "Desculpe, usuários novos não podem mencionar outros usuários." - one: "Desculpe, usuários novos podem mencionar apenas um outro usuário em um post." - other: "Desculpe, usuários novos podem mencionar apenas %{count} usuários em um post." + zero: "Pedimos desculpa, utilizadores novos não podem mencionar outros utilizadores." + one: "Pedimos desculpa, utilizadores novos podem mencionar apenas um outro utilizador numa mensagem." + other: "Pedimos desculpa, utilizadores novos podem mencionar apenas %{count} utilizadores numa mensagem." too_many_images: - zero: "Desculpe, usuários novos não podem colocar imagens nas postagens." - one: "Desculpe, usuários novos podem colocar apenas uma imagem nas postagens." - other: "Desculpe, usuários novos podem colocar apenas %{count} imagens nas postagens." + zero: "Pedimos desculpa, utilizadores novos não podem colocar imagens nas mensagens." + one: "Pedimos desculpa, utilizadores novos podem colocar apenas uma imagem nas mensagens." + other: "Pedimos desculpa, utilizadores novos podem colocar apenas %{count} imagens nas mensagens." too_many_attachments: - zero: "Desculpe, novos usuários não podem colocar anexos nas postagens." - one: "Desculpe, novos usuários podem colocar apenas um anexo nas suas postagens." - other: "Desculpe, novos usuários podem colocar apenas %{count} anexos nas suas postagens." + zero: "Pedimos desculpa, novos utilizadores não podem colocar anexos nas mensagens." + one: "Pedimos desculpa, novos utilizadores podem colocar apenas um anexo nas suas mensagens." + other: "Pedimos desculpa, novos utilizadores podem colocar apenas %{count} anexos nas suas mensagens." too_many_links: - zero: "Desculpe, usuários novos não podem colocar links nas postagens." - one: "Desculpe, usuários novos podem colocar apenas um link nas postagens." - other: "Desculpe, usuários novos podem colocar apenas %{count} links nas postagens." - spamming_host: "Desculpe, você não pode colocar um link para este site." - user_is_suspended: "Não é permitida a criação de posts para utilizadores suspensos." - just_posted_that: "é demasiado similar ao que postaste recentemente" + zero: "Pedimos desculpa, utilizadores novos não podem colocar hiperligações nas mensagens." + one: "Pedimos desculpa, utilizadores novos podem colocar apenas uma hiperligação nas mensagens." + other: "Pedimos desculpa, utilizadores novos podem colocar apenas %{count} hiperligações nas mensagens." + spamming_host: "Pedimos desculpa, não pode colocar uma hiperligação para esse host." + user_is_suspended: "Utilizadores suspensos não têm permissão para publicar." + just_posted_that: "é demasiado semelhante ao que publicaste recentemente" has_already_been_used: "já foi usado" - invalid_characters: "contém caracteres inválidos" - is_invalid: "é inválido; tenta ser um bocado mais descritivo" + invalid_characters: "contem caracteres inválidos" + is_invalid: "é inválido; tente ser um pouco mais descritivo" next_page: "próxima página →" prev_page: "← página anterior" page_num: "Página %{num}" topics_in_category: "Tópicos na categoria '%{category}'" - rss_posts_in_topic: "RSS feed de '%{topic}'" - rss_topics_in_category: "RSS feed dos tópicos da categoria '%{category}'" + rss_posts_in_topic: "Feed RSS de '%{topic}'" + rss_topics_in_category: "Feed RSS dos tópicos da categoria '%{category}'" author_wrote: "%{author} escreveu:" - num_posts: "Posts:" + num_posts: "Mensagens:" num_participants: "Participantes:" read_full_topic: "Ler tópico completo" private_message_abbrev: "MP" rss_description: - latest: "Últimos tópicos" + latest: "Tópicos mais recentes" hot: "Tópicos quentes" - too_late_to_edit: "Esse poste foi criada há muito tempo. Já não pode ser editado ou apagado." + too_late_to_edit: "Essa mensagem foi criada há muito tempo. Já não pode ser editada ou apagada." groups: errors: - can_not_modify_automatic: "Você não pode modificar um grupo automático" + can_not_modify_automatic: "Não pode modificar um grupo automático" default_names: everyone: "todos" - admins: "admins" + admins: "administradores" moderators: "moderadores" - staff: "staff" - trust_level_0: "trust_level_0" + staff: "pessoal" + trust_level_0: "nivel_de_confianca_0" trust_level_1: "nivel_de_confianca_1" trust_level_2: "nivel_de_confianca_2" trust_level_3: "nivel_de_confianca_3" trust_level_4: "nivel_de_confianca_4" education: until_posts: - one: "1 post" - other: "%{count} posts" - avatar: | - ### Que tal uma nova imagem para a sua conta? ⏎ - ⏎ - Você postou alguns tópicos e respostas, mas seu avatar não é tão original como você é -- é o mesmo avatar padrão que todos os novos usuários têm ⏎. - ⏎ - Já considerou **[visitar o seu perfil de usuário](%{profile_path})** e fazer o envio de uma imagem personalizada que representa você?⏎ - ⏎ - É mais fácil para acompanhar as discussões da comunidade e encontrar pessoas interessantes em conversas quando todo mundo tem um avatar exclusivo!⏎ + one: "1 mensagem" + other: "%{count} mensagens" + new-topic: "Bem-vindo a %{site_name}— **obrigado por começar uma nova conversação!** \n\n- O título descreve o seu tópico com precisão? Parece interessante?\n \n- Qual é o tema do tópico? Quem estaria interessado no mesmo? Porque é importante? Que tipo de respostas está à espera por parte da comunidade? \n\n- Inclua boas palavras de pesquisa no seu tópico para que outros possam *encontrá-lo*. Para agrupar o seu tópico com tópicos relacionados, seleccione a categoria. \n\nPara mais, [verifique as diretrizes da comunidade](/guidelines). Este painel irá aparecer apenas para o seu primeiro %{education_posts_text}.\n" + new-reply: | + Bem-vindo a %{site_name} — **obrigado por contribuir!** + + - A sua resposta melhora de alguma maneira esta conversação? + + - Seja gentil com os restantes membros da comunidade. + + - Críticas construtivas são bem-vindas, mas critique *ideias*, não pessoas. + + Para mais, [verifique as diretrizes da comunidade](/guidelines). Este painel irá aparecer apenas para o seu primeiro %{education_posts_text}. + avatar: "### Que tal uma nova imagem para a sua conta? \n\nVocê publicou alguns tópicos e respostas, mas seu avatar não é tão original como você é -- é o mesmo avatar padrão que todos os novos utilizadores têm .\n\nJá considerou **[visitar o seu perfil de utilizador](%{profile_path})** e fazer o envio de uma imagem personalizada que o representa?\n\nÉ mais fácil para acompanhar as discussões da comunidade e encontrar pessoas interessantes em conversas quando todas as pessoas têm um avatar exclusivo!\n" sequential_replies: | ### Considere responder várias mensagens de uma só vez @@ -129,6 +149,19 @@ pt: Este tópico é claramente importante para você – você postou mais de %{percent}% das respostas aqui.⏎ ⏎ Tem certeza de que você está fornecendo o tempo adequado para outras pessoas também poderem compartilhar seus pontos de vista?⏎ + too_many_replies: |+ + ### Chegou ao limite de respostas para este tópico + + Pedimos desculpa, mas novos utilizadores estão temporariamente limitados a %{newuser_max_replies_per_topic} respostas no mesmo tópico. + + Em vez de adicionar outra resposta, por favor considere editar as suas respostas anteriores, ou visitar outros tópicos. + + reviving_old_topic: | + ### Reavivar este tópico? + + A última resposta a este tópico tem agora %{days} dias. A sua resposta irá sobressair no tópico, no topo da sua lista e notificar todos os que anteriormente estavam envolvidos nesta conversação. + + Tem a certeza que pretende continuar esta conversação antiga? activerecord: attributes: category: @@ -168,11 +201,39 @@ pt: meta_category_description: "Discussão sobre este site, a sua organização, como funciona, e como o podemos melhorar." staff_category_name: "Staff" staff_category_description: "Categoria privada para discussões do staff. Os tópicos estão apenas visíveis para administradores e moderadores." + assets_topic_body: "Este é um tópico permanente, visível apenas ao pessoal, para armazenar imagens e ficheiros usados no design do sítio. Não o apague!\n\n\nVeja como:\n\n\n1. Responda a este tópico.\n2. Carregue todas as imagens que deseja usar para os logótipos, favicons, e tudo o mais. (Use o ícone da ferramenta de carregamento neste editor de mensagens, arraste ou cole imagens).\n3. Submeta a sua resposta.\n4. Carregue com o botão direito do rato nas imagens da sua nova mensagem para obter o caminho das imagens submetidas, ou carregue no ícone de edição para editar a sua mensagem e recuperar o caminho para as imagens. Copie os caminhos das imagens.\n5. Cole esses caminhos em [basic settings](/admin/site_settings/category/required).\n\n\nSe precisar de ativar o upload de diferentes tipos de ficheiros, edite `authorized_extensions` em [file settings](/admin/site_settings/category/files)." lounge_welcome: title: "Bem-vindo ao Lounge" + body: |2+ + + Parabéns! :confetti_ball: + + Se consegue ver este tópico, então é porque foi recentemente promovido a **regular** (nível de confiança 3). + + Pode agora … + + * Editar o título de qualquer tópico + * Modificar a categoria de qualquer tópico + * Ter todas as suas hiperligações a serem seguidas ([nãoseguimento automático](http://en.wikipedia.org/wiki/Nofollow) é removido) + * Aceder a um salão privado de categorias, apenas visível a utilizadores com nível de confiança 3 ou superior + * Esconder spam com uma única sinalização + + Aqui está a [lista actual de membros regulares](/badges/3/regular). Certifique-se que diz olá. + + Obrigado por ser uma parte importante na nossa comunidade! + + (Para mais informação acerca dos níveis de confiança, [veja este tópico][confiança]. Por favor tenha conhecimento que apenas membros que continuam a corresponder aos requisitos ao longo do tempo, irão manter-se regulares.) + + [confiança]: https://meta.discourse.org/t/what-do-user-trust-levels-do/4924 + category: topic_prefix: "Acerca da categoria %{category}" + replace_paragraph: "[Substituir este primeiro parágrafo, com uma breve descrição de sua nova categoria. Esta orientação será exibida na área de seleção de categoria, por isso tente mantê-la abaixo de 200 caracteres. Até que você editar este texto ou criar tópicos, esta categoria não aparece na página de categorias.]" post_template: "%{replace_paragraph}\n\nUse os parágrados a seguir para uma descrição longa, assim como para estabelecer diretrizes ou regras da categoria.\n\nAlgumas coisas para se considerar em qualquer resposta de discussão:\n\n- Para que serve esta categoria? Porque algum usaria esta categoria para seu tópico?\n\n- Como esta categoria se difere das demais categorias já existentes?\n\n- Nós precisamos desta categoria?\n\n- Deveríamos mesclar esta categoria com outra, ou dividir esta categoria em mais categorias?\n" + errors: + uncategorized_parent: "Sem categoria não podem ter categorias de nível superior" + self_parent: "Uma subcategoria não pode ser superior a ela própria" + depth: "Não pode juntar uma subcategoria dentro de outra" cannot_delete: uncategorized: "Não é possível apagar Sem Categoria" has_subcategories: "Não é possível apagar esta categoria porque contém sub-categorias." @@ -191,6 +252,7 @@ pt: title: "regular" elder: title: "líder" + change_failed_explanation: "Tentou despromover %{user_name} para '%{new_trust_level}'. Contudo o nível de confiança é actualmente '%{current_trust_level}'. %{user_name} irá permanecer em '%{current_trust_level}' - se deseja despromover o utilizador, bloqueie o nível de confiança primeiro" rate_limiter: slow_down: "Realizou esta acção demasiadas vezes, tente novamente mais tarde" too_many_requests: "Nós possuímos um limite diário do número de vezes que uma ação pode ser tomada. Por favor aguarde %{time_left} antes de tentar novamente." @@ -226,10 +288,10 @@ pt: other: "%{count}d" about_x_months: one: "1mês" - other: "%{count}mon" + other: "%{count}mês" x_months: one: "1mês" - other: "%{count}mon" + other: "%{count}mês" about_x_years: one: "1ano" other: "%{count}anos" @@ -275,7 +337,7 @@ pt: one: "há quase 1 ano atrás" other: "há quase %{count} anos atrás" password_reset: - no_token: "Desculpe, o seu token expirou. Por favor tenta redefinir a sua password novamente." + no_token: "Desculpe, o seu token expirou. Por favor tente redefinir a sua palavra-passe novamente." choose_new: "Por favor escolhe uma password nova" choose: "Por favor escolha uma senha" update: 'Actualizar Senha' @@ -287,27 +349,37 @@ pt: change_email: confirmed: "O seu email foi atualizado." please_continue: "Continuar para %{site_name}" - error: "Houve um erro ao alterar o seu endereço de email. Talvez o endereço já esteja sendo utilizado?" + error: "Ocorreu um erro ao alterar o seu endereço de email. Talvez o endereço já esteja a ser utilizado?" activation: - action: "Activar a sua conta" - already_done: "Desculpe, este link de confirmação não está mais válido. Talvez a sua conta já esteja ativa?" + action: "Ativar a sua conta" + already_done: "Pedimos desculpa, esta hiperligação de confirmação já não está válida. Talvez a sua conta já esteja ativa?" + please_continue: "A sua nova conta foi confirmada; irá ser redirecionado para a página principal." continue_button: "Continuar para %{site_name}" welcome_to: "Bem-vindo a %{site_name}!" - approval_required: "Um moderador tem que aprovar a sua conta para que você possa acessar este fórum. Você receberá um email quando sua conta for aprovada!" + approval_required: "Um moderador tem que aprovar a sua conta antes de poder aceder a este fórum. Irá receber um email quando a sua conta for aprovada!" post_action_types: off_topic: - title: 'Off-Topic' - long_form: 'sinalizou isto como off-topic' + title: 'Fora de Contexto' + description: 'Esta mensagem não é relevante para a discussão corrente, conforme definido pelo título e pela primeira mensagem, e provavelmente deverá ser transferida para outro tópico.' + long_form: 'sinalizou isto como fora de contexto' spam: title: 'Spam' + description: 'Esta mensagem é publicidade. Não é útil nem relevante para o tópico atual, mas apenas de natureza promocional.' long_form: 'sinalizado como spam' inappropriate: title: 'Inapropriado' + description: 'Esta mensagem contém conteúdo que uma pessoa sensata iria considerar ofensivo, abusivo, ou como uma violação das directrizes da nossa comunidade.' long_form: 'sinalizado como inapropriado' notify_user: + title: 'Mensagem Privada @{{username}}' + description: 'Esta mensagem contém algo sobre o qual quero falar com esta pessoa directamente e em particular. Não levanta bandeira.' + long_form: 'utilizador com mensagens privadas' email_title: 'O seu post em "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "Algo Mais" + description: 'Esta mensagem requer a atenção do moderador por outra razão não mencionada acima.' + long_form: 'Marque isto para obter a atenção do moderador.' email_title: 'Uma postagem em "%{title}" requer atenção do moderador' email_body: "%{link}\n\n%{message}" bookmark: @@ -332,6 +404,9 @@ pt: description: 'Este tópico contém conteúdo que uma pessoa razoável consideraria ofensivo, abusivo ou uma violação das directrizes da nossa comunidades.' long_form: 'denunciar isto como impróprio' notify_moderators: + title: "Algo Mais" + description: 'Este tópico requer a atenção geral do moderador baseado nas diretrizes, TOS, ou por outra razão não mencionada acima.' + long_form: 'Marcado para obter a atenção do moderador' email_title: 'O tópico "%{title}" requer a atenção da moderação' email_body: "%{link}\n\n%{message}" flagging: @@ -340,79 +415,86 @@ pt: archetypes: regular: title: "Tópico Regular" + banner: + message: + make: "Este tópico é agora um banner. Ele aparecerá no topo de cada página até que seja julgado improcedente pelo utilizador." + remove: "Este tópico não é mais um banner. Ele deixará de aparecer no topo de cada página" unsubscribed: title: 'Desinscrito' description: "Você se desinscreveu. Não vamos mais enviar mensagens!" oops: "Caso não seja esta sua intenção, clique em baixo." + error: "Erro na desinscrição" + preferences_link: "Também pode cancelar a assinatura de sumário por e-mails na sua página de preferências" + different_user_description: "Você está logado como um utilizador diferente do que aquele que o resumo foi enviado. Por favor, efetue logout e tente novamente." not_found_description: "Desculpe, não conseguimos cancelar sua inscrição. É possível que o link no seu email tenha expirado." resubscribe: action: "Re-Inscrever" title: "Re-Inscrever!" - description: "Foste re-inscrito." + description: "Foi re-inscrito." reports: visits: - title: "Visitas do Usuário" + title: "Visitas do Utilizador" xaxis: "Dia" yaxis: "Número de visitas" signups: - title: "Usuários" + title: "Utilizadores" xaxis: "Dia" - yaxis: "Número de usuários novos" + yaxis: "Número de novos utilizadores" topics: title: "Tópicos" xaxis: "Dia" - yaxis: "Número de tópicos novos" + yaxis: "Número de novos tópicos" posts: - title: "Posts" + title: "Mensagens" xaxis: "Dia" - yaxis: "Número de posts novos" + yaxis: "Número de novas mensagens" likes: - title: "Curtidas" + title: "Gostos" xaxis: "Dia" - yaxis: "Número de novas curtidas" + yaxis: "Número de novos gostos" flags: title: "Sinalizações" xaxis: "Dia" yaxis: "Número de sinalizações" bookmarks: - title: "Marcadores Adicionados" + title: "Marcadores" xaxis: "Dia" - yaxis: "Número de novos marcadores adicionados" + yaxis: "Número de novos marcadores" starred: title: "Favoritos" xaxis: "Dia" yaxis: "Número de novos tópicos favoritos" users_by_trust_level: - title: "Usuários por Nível de Confiança" + title: "Utilizadores por Nível de Confiança" xaxis: "Nível de Confiança" - yaxis: "Número de Usuários" + yaxis: "Número de Utilizadores" emails: title: "Emails Enviados" xaxis: "Dia" yaxis: "Número de Emails" user_to_user_private_messages: - title: "Usuário-a-Usuário" + title: "Utilizador-a-Utilizador" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" system_private_messages: title: "Sistema" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" moderator_warning_private_messages: title: "Alerta ao Moderador" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" notify_moderators_private_messages: title: "Notificação aos Moderadores" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" notify_user_private_messages: - title: "Avisar utilizador" + title: "Notificar o Uilizador" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" top_referrers: title: "Top Referrers" - xaxis: "Usuário" + xaxis: "Utilizador" num_clicks: "Clicks" num_topics: "Tópicos" top_traffic_sources: @@ -420,21 +502,29 @@ pt: xaxis: "Domínio" num_clicks: "Clicks" num_topics: "Tópicos" - num_users: "Usuários" + num_users: "Utilizadores" top_referred_topics: title: "Top Tópicos Citados" xaxis: "Tópico" num_clicks: "Clicks" dashboard: - rails_env_warning: "Seu servidor está rodando no modo %{env}." + rails_env_warning: "O seu servidor está a executar em modo %{env}." ruby_version_warning: "Está a utilizar uma versão do Ruby 2.0.0 que é conhecida por ter alguns problemas. Por favor atualize para o nível 247 ou posterior." host_names_warning: "O arquivo config/database.yml está usando hostname padrão localhost. Modifique para usar o hostname do seu site." gc_warning: 'Seu servidor está rodando a coleta de lixo do ruby com os parâmetros padrão (default ruby garbage collection parameters), os quais não irão propiciar a melhor performance. Leia este tópico sobre ajuste de performance: Tuning Ruby and Rails for Discourse.' sidekiq_warning: 'Sidekiq não está em execução. Muitas tarefas, como envio de emails, são executadas de forma assíncrona pelo sidekiq. Por favor certifique-se de que ao menos um processo sidekiq esteja execução. Aprenda sobre Sidekiq aqui.' queue_size_warning: 'O número de tarefas agendadas é %{queue_size}, o que é alto. Isto pode indicar um problema com o(s) processo(s) Sidekiq, ou você pode estar precisando de mais Sidekiq workers.' memory_warning: 'Seu servidor está rodando com menos de 1 GB de memória total. Pelo menos 1 GB é quantidade de memória recomendada.' + enable_google_logins_warning: "Está a usar uma versão obsoleta da autenticação do OpenID da Google. A Google irá terminar o suporte do OpenID em 20 de Abril de 2015. Comece a usar o OAuth2 da Google assim que possível. Verifique este guia para aprender mais" + both_googles_warning: "Tanto enable_google_logins e enable_google_oauth2_logins estão assinalados nas configurações do sítio. Desative enable_google_logins." + google_oauth2_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o Google OAuth2 (enable_google_oauth2_logins), mas o id e os valores privados do cliente não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' + facebook_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o Facebook (enable_facebook_logins), mas o id e os valores privados da aplicação não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' + twitter_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o Twitter (enable_twitter_logins), mas a chave e os valores privados não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' + github_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o GitHub (enable_github_logins), mas o id e valores privados do cliente não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' s3_config_warning: 'O servidor está configurado para fazer upload de arquivos para o s3, mas pelo menos uma destas configurações não está definida: s3_access_key_id, s3_secret_access_key ou s3_upload_bucket. Vá até as Configurações do Site e atualize estas definições. Veja "How to set up image uploads to S3?" para saber mais.' + s3_backup_config_warning: 'O servidor está configurado para carregar cópias de segurança para s3, mas pelo menos uma das seguintes configurações não está definida: s3_access_key_id, s3_secret_access_key ou s3_backup_bucket. Vá às Configurações do Sítio e atualize as definições. Veja "Como configurar o carregamento de imagens para S3?" para saber mais.' image_magick_warning: 'O servidor está configurado para criar miniaturas de imagens grandes, mas o ImageMagick não está instalado. Instale o ImageMagick usando seu gerenciador de pacotes preferido ou acesse para fazer download da última versão.' + failing_emails_warning: 'Há %{num_failed_jobs} tarefas de envio de emails que falharam. Verifique o ficheiro config/discourse.conf e assegure-se que as configurações do servidor de email estão corretas. Veja as tarefas que falharam no Sidekiq.' default_logo_warning: "Você não personalizou as imagens do logo para seu site. Atualize logo_url, logo_small_url, e favicon_url nas Configurações do Site." contact_email_missing: "Você não forneceu um email de contato do site. Por favor atualize o contact_email nas Configurações do Site." contact_email_invalid: "O email de contato do site é inválido. Por favor atualize o contact_email nas Configurações do Site." @@ -466,35 +556,147 @@ pt: login_required: title: "É requerido que você entre em sua conta: Página Inicial" description: "Texto exibido para usuários não autorizados quando é se exige entrar na conta para acessar o site." + head: + title: "Cabeçalho HTML" + description: "HTML que será inserido dentro do código ." top: title: "Topo das páginas" + description: "HTML que será adicionado no cimo de cada página (depois do cabeçalho, antes da navegação ou título do tópico)." bottom: title: "Rodapé das páginas" + description: "HTML que será adicionado antes do código ." site_settings: + censored_words: "Palavras que irão ser automaticamente substituídas por ■■■■" + delete_old_hidden_posts: "Auto-apagar quaisquer mensagens escondidas que permaneçam escondidas por mais de 30 dias." default_locale: "Idioma padrão para esta instância do Discourse (ISO 639-1 Code)" + allow_user_locale: "Permitir aos utilizadores escolherem a sua própria língua preferencial para a interface." + min_post_length: "Tamanho mínimo permitido por mensagem, em caracteres" + min_private_message_post_length: "Tamanho mínimo permitido para mensagens privadas, em caracteres" + max_post_length: "Tamanho máximo permitido por mensagem, em caracteres" + min_topic_title_length: "Tamanho mínimo permitido por título de cada tópico, em caracteres" + max_topic_title_length: "Tamanho máximo permitido por título de cada tópico, em caracteres" + min_private_message_title_length: "Tamanho mínimo permitido por título de cada tópico nas mensagens privadas, em caracteres" + min_search_term_length: "Tamanho mínimo válido para termos de pesquisa, em caracteres" + allow_uncategorized_topics: "Permitir a criação de tópicos sem categoria." + uncategorized_description: "Descrição da categoria sem classificação. Deixar em branco para nenhuma descrição." allow_duplicate_topic_titles: "Permitir tópicos com títulos idênticos e duplicados." unique_posts_mins: "Quantos minutos antes para um utilizador poder criar uma postagem com o mesmo conteúdo outra vez?" + educate_until_posts: "Quando um utilizador começar a escrever as primeiras (n) novas mensagens, mostrar o painel pop-up de educação do novo utilizador no editor." title: "Breve título deste site, usado na tag título." site_description: "Descrever este site num frase, usada na tag de descrição meta," + contact_email: "Endereço de email da pessoa principal a contactar sobre este sítio. Avisos importantes sobre o discourse.org em relação a atualizações críticas podem ser enviadas para este endereço." queue_jobs: "APENAS DESENVOLVEDORES! ATENÇÃO! Por padrão, enfileira tarefas no sidekiq. Se desativado, seu site ficará defeituoso." + crawl_images: "Recuperar imagens de URLs remotos para inserir o comprimento e largura corretos." + download_remote_images_to_local: "Converta imagens remotas em imagens locais ao descarregá-las; isto previne imagens corrompidas." + download_remote_images_threshold: "Espaço mínimo necessário em disco para descarregar imagens remotas localmente (em percentagem)" + disabled_image_download_domains: "Imagens remotas não irão ser descarregas destes domínios. " + ninja_edit_window: "Durante (n) segundos após a publicação da mensagem, editá-la não irá criar uma nova versão no histórico de mensagens." + post_edit_time_limit: "O autor pode editar ou apagar a sua mensagem por um período de (n) minutos após a publicação da mensagem. Definir a 0 para ser para sempre." + edit_history_visible_to_public: "Permitir que todos vejam versões anteriores de uma mensagem editada. Quando desativado, apenas membros do pessoal podem ver." + delete_removed_posts_after: "Mensagens removidas pelo autor irão ser automaticamente eliminadas após um período de (n) horas. Se estiver definido a 0, as mensagens irão ser eliminadas imediatamente." + max_image_width: "Comprimento máximo de miniaturas de imagens numa mensagem." + max_image_height: "Altura máxima de miniaturas de imagens numa mensagem." + category_featured_topics: "Número de tópicos visíveis por categoria na página de /categorias. Após a alteração deste valor, irá demorar até 15 minutos para que a página de categorias fique atualizada." + show_subcategory_list: "Mostrar lista de subcategorias em vez de lista de tópicos quando inserida uma categoria." + fixed_category_positions: "Se estiver marcado, irá conseguir organizar as categorias por uma ordem fixa. Se não estiver marcado, as categorias são listadas por ordem de actividade." + add_rel_nofollow_to_user_content: "Adicionar a etiqueta rel nofollow em todos os conteúdos submetidos pelo utilizador, excepto para hiperligações internas (incluindo domínios pai). Se mudar isto, terá que atualizar todas as suas mensagens com: \"rake posts:rebake\"" + exclude_rel_nofollow_domains: "Lista de domínios delimitada em que nofollow não é adicionado (tld.com irá automaticamente permitir sub.tld.com)" + post_excerpt_maxlength: "Tamanho máximo do excerto/sumário de uma mensagem." + post_onebox_maxlength: "Tamanho máximo de uma mensagem Discourse de caixa única, em caracteres." + onebox_domains_whitelist: "Lista de domínios que permitem colocar em caixa única; estes domínios devem suportar OpenGraph ou oEmbed. Teste-os em http://iframely.com/debug" + logo_url: "Imagem do logótipo no canto superior esquerdo do seu sítio ex: http://example.com/logo.png" + digest_logo_url: "O logótipo alternativo usado no topo do resumo de email do seu sítio. Se for deixado em branco `logo_url` irá ser usado. ex: http://example.com/logo.png" + logo_small_url: "A pequena imagem do logótipo no canto superior esquerdo do seu sítio, visível quando arrastado para baixo. ex: http://example.com/logo-small.png" favicon_url: "Um favicon para o seu site" + mobile_logo_url: "A imagem do logótipo com posição fixa usada no canto superior esquerdo do seu sítio móvel. Se deixado em branco, `logo_url` irá ser usado. ex: eg: http://example.com/uploads/default/logo.png" apple_touch_icon_url: "Ícone usado para dispositivos Apple. Tamanho recomendado é 144px por 144px." + notification_email: "Para: endereço de email usado ao enviar emails essenciais do sistema. O domínio especificado aqui deverá ter SPF, DKIM e registos PTR inversos configurados corretamente para a chegada do email." email_custom_headers: "A lista delimitada por barras verticais de cabeçalhos de e-mail personalizados" + email_subject: "Formato de assunto personalizável para emails padrão. Veja https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" + use_https: "Deverá o url completo do sítio (Discourse.base_url) ser http ou https? NÃO ATIVAR ISTO A NÃO SER QUE HTTPS JÁ ESTEJA CONFIGURADO E A FUNCIONAR!" + summary_score_threshold: "Pontuação mínima necessária para que uma mensagem seja incluída em 'Resumir Este Tópico'" + summary_posts_required: "Número mínimo de mensagens num tópico antes que 'Resumir Este Tópico' seja ativo." + summary_likes_required: "Número mínimo de gostos num tópico antes que 'Resumir Este Tópico' seja ativo." + summary_percent_filter: "Quando um utilizador carrega em 'Resumir Este Tópico', mostrar o topo % o das mensagens" + summary_max_results: "Número máximo de mensagens devolvidas por 'Resumo deste Tópico'" + enable_private_messages: "Permitir que utilizadores de nível de confiança 1 possam criar e responder a mensagens privadas" enable_long_polling: "O sistema de mensagens das notificações pode fazer solicitações longas." + long_polling_base_url: "URL base usada para solicitação ao servidor (quando um CDN serve conteúdo dinâmico, certifique-se de configurar este para a 'pull' original) ex: http://origin.site.com" + long_polling_interval: "Quantidade de tempo que um servidor deve esperar antes de notificar os clientes quando não há dados para serem enviados (apenas utilizadores ligados)" + polling_interval: "Quando não está a ocorrer uma solicitação ao servidor, com que frequência devem os clientes ligados requerer uma atualização ao servidor, em milissegundos" anon_polling_interval: "Com que frequencia os clientes não registrados podem solicitar o servidor em milisegundos" + background_polling_interval: "Com que frequência deverão os clientes solicitar o servidor, em milissegundos (quando a janela está em plano de fundo)" auto_track_topics_after: "Quantos milisegundos esperar antes que um tópico seja automaticamente rastreado (0 para sempre, -1 para nunca)" new_topic_duration_minutes: "Quantos minutos um tópico permanece considerado como novo (-1 para sempre, -2 para desde a última visita)" flags_required_to_hide_post: "As postagens vão ser escondidos automaticamente quando o númereo de sinalizações atingir este número (0 para nunca)" + cooldown_minutes_after_hiding_posts: "Número de minutos que o utilizador deve esperar antes de poder editar uma mensagem escondida devido a sinalizações por parte da comunidade" max_topics_in_first_day: "O número máximo de tópicos que é permitido a um utilizador no seu primeiro dia no Fórum." max_replies_in_first_day: "O número máximo de mensagens que é permitido a um utilizador no seu primeiro dia no Fórum." + num_flags_to_block_new_user: "Se uma mensagem de um novo utilizador receber esta quantidade de sinalizações da parte de num_users_to_block_new_user diferentes utilizadores, esconder todas as suas mensagens e prevenir mensagens futuras. 0 para desativar." + num_users_to_block_new_user: "Se uma mensagem de um novo utilizador receber num_flags_to_block_new_user sinalizações de spam deste número de diferentes utilizadores, esconder todas as suas mensagens e prevenir mensagens futuras. 0 para desativar." notify_mods_when_user_blocked: "Se um usuário for bloqueado de forma automática, enviar uma mensagem para todos os moderadores." + flag_sockpuppets: "Se um novo utilizador responde a um tópico a partir do mesmo endereço IP do novo utilizador que iniciou o tópico, sinalizar ambas as mensagens como spam potencial." + traditional_markdown_linebreaks: "Utilize tradicionais quebras de linha no Markdown, que requer dois espaços no final para uma quebra de linha." + post_undo_action_window_mins: "Número de minutos durante o qual os utilizadores têm permissão para desfazer ações numa mensagem (gostos, sinalizações, etc)." + must_approve_users: "O pessoal deverá aprovar todas as contas de novos utilizadores antes de terem permissão para aceder ao sítio." + ga_tracking_code: "Código de acompanhamento do Google Analytics (ga.js), eg: UA-12345678-9; ver http://google.com/analytics" + ga_domain_name: "Nome de domínio da Google Analytics (ga.js), eg: mysite.com; ver http://google.com/analytics" + ga_universal_tracking_code: "Código de acompanhamento do Google Universal Analytics (analytics.js), eg: UA-12345678-9; ver http://google.com/analytics" + ga_universal_domain_name: "Nome de domínio do Google Universal Analytics (analytics.js), ex: mysite.com; ver http://google.com/analytics" + enable_escaped_fragments: "Ir à API do Google Ajax-Crawling se nenhum webcrawler for detectado. Ver https://support.google.com/webmasters/answer/174992?hl=en" enable_noscript_support: "Ativar suporte a tag <noscript>" + allow_moderators_to_create_categories: "Permitir que os moderadores criem novas categorias" + cors_origins: "Permitidos compartilhamentos de recursos de origem-cruzada (CORS). Cada origem deve incluir http:// or https://. A variável de ambiente DISCOURSE_ENABLE_CORS tem que estar configurada a verdadeiro para ativar o CORS." + top_menu: "Determinar que elementos aparecem na navegação da página principal, e em que ordem. Exemplo latest|new|unread|starred|categories|top|read|posted" post_menu: "A ordem dos items no menu da postagem." + post_menu_hidden_items: "Os elementos do menu a serem escondidos por padrão no menu de mensagens a não ser que se carregue na elipse de expansão." + share_links: "Determinar que elementos aparecem no diálogo de partilha, e em que ordem." track_external_right_clicks: "Rastrear cliques externos que são clicados com o botão direito (ex: abrir em nova aba) desativado por padrão, pois tem que reescrever urls, quebrando a usabilidade" + site_contact_username: "Todas as mensagens privadas automatizadas serão deste utilizador; se deixado em branco a conta padrão do Sistema irá ser usada." + send_welcome_message: "Enviar a todos os novos utilizadores uma mensagem privada de boas-vindas com um guia de inicio rápido." + suppress_reply_directly_below: "Não mostrar a contagem de respostas expansível quando há apenas uma única resposta diretamente abaixo desta publicação." + suppress_reply_directly_above: "Não mostrar a contagem de em-resposta-a expansível quando há apenas uma única resposta diretamente acima desta publicação." + suppress_reply_when_quoting: "Não mostrar o em-resposta-a expandível numa mensagem quando a mensagem cita uma resposta." + max_reply_history: "Número máximo de respostas a serem expandidas quando se expande em-resposta-a" + experimental_reply_expansion: "Esconder respostas intermédias ao expandir uma resposta a (experimental)" + topics_per_period_in_top_summary: "Número de tópicos principais mostrados no resumo padrão de tópicos principais." + topics_per_period_in_top_page: "Número de tópicos principais mostrados no expandido 'Mostrar Mais' tópicos principais." + redirect_users_to_top_page: "Redirecionar automaticamente os utilizadores novos e ausentes por períodos longos para o topo da página." + show_email_on_profile: "Mostrar o email do utilizador no seu perfil (apenas visível para si próprios e para o pessoal)" + email_token_valid_hours: "Os tokens para password esquecida / conta ativada são válidos por (n) horas." + email_token_grace_period_hours: "Os tokens para password esquecida / conta ativada são ainda válidos por um período de carência de (n) horas após serem recuperados." + enable_badges: "Ativar o distintivo do sistema" + allow_index_in_robots_txt: "Especificar em robots.txt que este sítio permite ser indexado pelos motores de pesquisa." + email_domains_blacklist: "Lista de domínios de email que os utilizadores não podem usar para registo de contas. Exemplo: mailinator.com trashmail.net" + email_domains_whitelist: "Listá de domínios de email que os utilizadores DEVEM usar para registar contas. AVISO: Utilizadores com domínios de email diferentes dos listados não irão ser permitidos!" + forgot_password_strict: "Não informar os utilizadores da existência da conta quando estes utilizam o diálogo de password esquecida." + version_checks: "Fazer o ping do Discourse Hub para atualização de versões e mostrar mensagens sobre novas versões no painel /admin" + new_version_emails: "Enviar um email para o endereço email_contacto quando uma nova versão do Discourse estiver disponível." port: "Se você quiser especificar a porta na URL. Útil no modo de desenvolvimento. Deixe em branco para nada." force_hostname: "Se você quiser especificar um hostname na URL. Útil no modo de desenvolvimento. Deixe em branco para nada." invite_expiry_days: "Quantos dias as chaves de convite são válidas." + invite_passthrough_hours: "Quanto tempo pode um utilizador utilizar uma chave de convite previamente recuperada, em horas" + invite_only: "O registo público está desativado, todos os novos utilizadores devem ser explicitamente convidados por outros membros ou pelo pessoal." + login_required: "Requer autenticação para ler conteúdo neste sítio, não permitir acesso anónimo." + min_username_length: "Tamanho mínimo do nome de utilizador, em caracteres. AVISO: QUAISQUER UTILIZADORES COM NOMES MAIS PEQUENOS QUE ISTO IRÃO SER PROIBIDOS DE ACEDER AO SÍTIO." + max_username_length: "Tamanho máximo do nome de utilizador, em caracteres. AVISO: QUAISQUER UTILIZADORES COM NOMES MAIORES QUE ISTO IRÃO SER PROIBIDOS DE ACEDER AO SÍTIO." + min_password_length: "Tamanho mínimo da password." + block_common_passwords: "Não permitir passwords que estão nas 10,000 passwords mais comuns." + enable_sso: "Permitir uma inscrição única através de um sítio externo (Nota: desativa convites)" + enable_sso_provider: "Implementa o protocolo SSO para o Discourse em /session/sso_provider endpoint, requer que sso_secret esteja configurado" + sso_url: "URL da inscrição única" + sso_secret: "String secreta usada para encriptar/desencriptar informação, certifique-se que são 10 ou mais caracteres" + sso_overrides_email: "Substitui o email local por um email externo de um SSO (AVISO: discrepâncias podem ocorrer devido a normalização de emails locais)" + sso_overrides_username: "Substitui nomes de utilizador locais por nomes de utilizadores externos de um SSO (AVISO: discrepâncias podem ocorrer diferenças no nome de utilizador length/requirements)" + sso_overrides_name: "Substitui o nome local por um nome externo de um SSO (AVISO: discrepâncias podem ocorrer devido a normalização de nomes locais)" + sso_overrides_avatar: "Substitui o avatar do utilizador com avatares externos de um SSO. Se ativo, é altamente recomendada a desativação de allow_uploaded_avatars" + enable_local_logins: "Ativar contas com nome de utilizador e senha de forma local (Nota: isto tem que estar ativo para que os convites funcionem)" + allow_new_registrations: "Permitir registo de novos utilizadores. Desmarcar isto para prevenir que alguém crie uma nova conta." + enable_google_logins: "(obsoleto) Ativa a autenticação Google. Este é um método de autenticação OpenID que o Google deixou de atualizar e passou a estar obsoleto. Novas instalações NÃO irão funcionar com isto. Utilize o Google Oauth2 como alternativa. Instalações existentes deverão passar a utilizar o Google Oauth2 antes de 20 de Abril de 2015." enable_yahoo_logins: "Ativar autenticação pelo Yahoo" + enable_google_oauth2_logins: "Ativar autenticação Google Oauth2. Este é o método que permite a autenticação que a Google atualmente suporta. Requer chave e segredo." + google_oauth2_client_id: "ID do cliente da sua aplicação Google." + google_oauth2_client_secret: "Segredo do cliente da sua aplicação Google." enable_twitter_logins: "Ativar autenticação pelo Twitter, requer twitter_consumer_key e twitter_consumer_secret" twitter_consumer_key: "consumer key registrada em dev.twitter.com (usada para fazer autenticação via twitter)" twitter_consumer_secret: "consumer secret registrada em dev.twitter.com (usada para fazer autenticação via twitter)" @@ -504,20 +706,186 @@ pt: enable_github_logins: "Ativar autenticação via Github, requer github_client_id e github_client_secret" github_client_id: "Client id para autenticação via Github, registrado em https://github.com/settings/applications" github_client_secret: "Client secret para autenticação via Github, registrado em https://github.com/settings/applications" + allow_restore: "Permitir o restauro, que pode substituir TODOS os dados do sítio. Deixar a falso a menos que planeie restaurar uma cópia de segurança" + maximum_backups: "Valor máximo de backups a serem guardados em disco. Backups antigos são automaticamente apagados." + backup_daily: "Criar automaticamente uma cópia de segurança local, uma vez por dia." + enable_s3_backups: "Carregar cópias de segurança para S3 quando completo. IMPORTANTE: requer credenciais S3 válidas inseridas nas configurações dos ficheiros." + s3_backup_bucket: "Bucket remoto para guardar cópias de segurança. AVISO: Certifique-se que este é um bucket privado." active_user_rate_limit_secs: "Qual a frequencia de atualização do campo 'última vez visto em', em segundos." + verbose_localization: "Mostrar extensas dicas de localização na IU" previous_visit_timeout_hours: "Quanto tempo uma visita dura antes de considerarmos como 'última visita', em horas." + rate_limit_create_topic: "Após a criação de um tópico, os utilizadores devem esperar (n) segundos antes de criarem um novo tópico." + rate_limit_create_post: "Após a publicação, os utilizadores devem esperar (n) segundos antes de criarem outra mensagem." + rate_limit_new_user_create_topic: "Após a criação de um tópico, novos utilizadores devem esperar (n) segundos antes de criarem um novo tópico." + rate_limit_new_user_create_post: "Após a publicação, novos utilizadores devem esperar (n) segundos antes de criarem novas mensagens." + max_likes_per_day: "Número máximo de gostos por utilizador por dia." + max_flags_per_day: "Número máximo de sinalizações por utilizador por dia." + max_bookmarks_per_day: "Número máximo de gostos de marcadores por utilizador por dia." + max_edits_per_day: "Número máximo de edições por utilizador por dia." + max_stars_per_day: "Número máximo de tópicos que podem ser estrelados por utilizador por dia." + max_topics_per_day: "Número máximo de tópicos que um utilizador pode criar por dia." + max_private_messages_per_day: "Número máximo de mensagens privadas que os utilizadores podem criar por dia." + suggested_topics: "Número de tópicos sugeridos mostrados no final de um tópico." + limit_suggested_to_category: "Apenas mostrar tópicos da categoria actual nos tópicos sugeridos." + clean_up_uploads: "Remover carregamentos orfãos não referenciados para prevenir alojamento ilegal. AVISO: poderá querer criar uma cópia de segurança da diretoria /uploads antes de ativar esta configuração." + clean_orphan_uploads_grace_period_hours: "Período de carência (em horas) antes de um carregamento orfão ser removido." + purge_deleted_uploads_grace_period_days: "Período de carência (em dias) antes de um carregamento eliminado ser apagado." + purge_unactivated_users_grace_period_days: "Período de carência (em dias) antes de um utilizador que não ativou a sua conta ser eliminado." + enable_s3_uploads: "Coloca os carregamentos no armazenamento Amazon S3. IMPORTANTE: requer credenciais S3 válidas (tanto id de chave de acesso como a chave de acesso secreta)." + s3_use_iam_profile: 'Utiliza AWS EC2 IAM para recuperar chaves. NOTA: a ativação irá substituir as configurações de "id da chave de acesso s3" e a "chave de acesso secreta s3".' + s3_upload_bucket: "Nome do bucket Amazon S3 que contém os ficheiros carregados. AVISO: tem que ser minúsculo, sem pontos e sem sublinhados." + s3_access_key_id: "Id da chave de acesso Amazon S3 que irá ser usada para carregar imagens." + s3_secret_access_key: "Chave de acesso secreta Amazon S3 que irá ser usada para carregar imagens." + s3_region: "Nome da região Amazon S3 que irá ser usado para carregar imagens." + enable_flash_video_onebox: "Ativar a incorporação de hiperligações swf e flv (Adobe Flash) em caixas únicas. AVISO: pode introduzir riscos de segurança." + default_invitee_trust_level: "Nível de confiança padrão (0-4) para utilizadores convidados." + default_trust_level: "Nível de confiança padrão (0-4) para todos os novos utilizadores." + tl1_requires_topics_entered: "Quantos tópicos um novo utilizador deve introduzir para ser promovido para o nível de confiança 1." + tl1_requires_read_posts: "Quantas mensagens um novo utilizador deve ler para ser promovido para o nível de confiança 1." + tl1_requires_time_spent_mins: "Quantos minutos um novo utilizador deve ler para ser promovido para o nível de confiança 1." + tl2_requires_topics_entered: "Quantos tópicos um utilizador deve introduzir para ser promovido para o nível de confiança 2." + tl2_requires_read_posts: "Quantas mensagens um utilizador deve ler para ser promovido para o nível de confiança 2." + tl2_requires_time_spent_mins: "Quantos minutos um utilizador deve ler para ser promovido para o nível de confiança 2." + tl2_requires_days_visited: "Durante quantos dias um utilizador deve visitar o sítio antes de ser promovido para o nível de confiança 2." + tl2_requires_likes_received: "Quantos gostos um utilizador deve receber antes de ser promovido para o nível de confiança 2." + tl2_requires_likes_given: "Quantos gostos um utilizador deve atribuir antes de ser promovido para o nível de confiança 2." + tl2_requires_topic_reply_count: "Quantos tópicos um utilizador deve responder antes de ser promovido para o nível de confiança 2." + tl3_requires_days_visited: "Número mínimo de dias que o utilizador necessita de ter visitado o sítio nos últimos 100 dias para se qualificar à promoção para o nível de confiança 3. (0 a 100)" + tl3_requires_topics_replied_to: "Número mínimo de tópicos que o utilizador necessita de ter respondido nos últimos 100 dias para se qualificar à promoção para o nível de confiança 3. (0 ou mais)" + tl3_requires_topics_viewed: "Percentagem de tópicos criados nos últimos 100 dias que o utilizador precisa de ter visto para se qualificar à promoção para o nível de confiança 3. (0 a 100)" + tl3_requires_posts_read: "Percentagem de mensagens criadas nos últimos 100 dias que o utilizador precisa de ter visto para se qualificar à promoção para o nível de confiança 3. (0 a 100)" + tl3_requires_topics_viewed_all_time: "Número mínimo total de tópicos que o utilizador deve ter visto para se qualificar ao nível de confiança 3." + tl3_requires_posts_read_all_time: "Número mínimo total de mensagens que o utilizador deve ter lido para se qualificar ao nível de confiança 3." + tl3_requires_max_flagged: "O utilizador não deverá ter mais do que x mensagens marcadas por x differentes utilizadores nos últimos 100 dias para se qualificar à promoção para o nível de confiança 3, onde x é o valor definido. (0 ou superior)" + tl3_promotion_min_duration: "Número mínimo de dias em que a promoção para nível de confiança 3 dura antes que o utilizador seja despromovido para o nível de confiança 2." + tl3_requires_likes_given: "Número mínimo de gostos que o utilizador deve ter atribuído nos últimos 100 dias para se qualificar à promoção para o nível de confiança 3." + tl3_requires_likes_received: "Número mínimo de gostos que o utilizador deve ter recebido nos últimos 100 dias para se qualificar à promoção para o nível de confiança 3." + tl3_links_no_follow: "Não remover rel=nofollow das hiperligações publicadas por utilizadores com 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 mínimo de confiança necessário para editar mensagens marcadas como wiki." + newuser_max_links: "Quantas hiperligações um novo utilizador pode adicionar a uma mensagem." + newuser_max_images: "Quantas imagens um novo utilizador pode adicionar a uma mensagem." + newuser_max_attachments: "Quantos anexos um novo utilizador pode adicionar a uma mensagem." + newuser_max_mentions_per_post: "Número máximo de notificações @nome um novo utilizador pode usar numa mensagem." + newuser_max_replies_per_topic: "Número máximo de respostas que um novo utilizador pode fazer num único tópico até alguém lhe responder." + max_mentions_per_post: "Número máximo de notificações @nome que qualquer pessoa pode usar numa mensagem." + create_thumbnails: "Criar imagens miniatura e lightbox que são demasiado largas para caber numa mensagem." + email_time_window_mins: "Espere (n) minutos antes de enviar quaisquer emails de notificação, para dar aos utilizadores a hipótese de editarem e finalizarem as suas mensagens." + email_posts_context: "Quantas respostas prévias a serem incluídas como contexto em emails de notificação." + flush_timings_secs: "Com que frequência o servidor é alimentado com dados de sincronização, em segundos." + max_word_length: "Tamanho máximo permitido de palavras, em caracteres, no título de um tópico." + title_min_entropy: "Entropia mínima (caracteres únicos, contagem não-inglesa para mais) necessária para o título de um tópico." + body_min_entropy: "Entropia mínima (caracteres únicos, contagem não-inglesa para mais) necessária para o corpo de uma mensagem." title_fancy_entities: "Converter caracteres ASCII comuns em entidades HTML nos títulos dos tópicos, ala SmartyPants http://daringfireball.net/projects/smartypants/" + min_title_similar_length: "Tamanho mínimo do título antes de ser verificado por tópicos semelhantes." + min_body_similar_length: "Tamanho mínimo do corpo de uma mensagem antes de ser verificado por tópicos semelhantes." + category_colors: "Lista de valores hexadecimais das cores permitidas nas categorias." + max_image_size_kb: "Tamanho máximo da imagem carregada em kB. Este deverá ser configurado em nginx (client_max_body_size) / apache ou também proxy." + max_attachment_size_kb: "Tamanho máximo dos anexos carregados em kB. Este deverá ser configurado em nginx (client_max_body_size) / apache ou também proxy." + authorized_extensions: "Lista de extensões permitidas para carregamento (utilizar '*' para ativar todos os tipos de ficheiros)" + max_similar_results: "Quantos tópicos semelhantes a serem mostrados acima do editor ao compor um novo tópico. A comparação é baseada no título e no corpo." title_prettify: "Prevenir erros comuns em títulos, incluindo caps-lock ligado, primeira letra minúscula, excesso de ! e ?, pontos extras no final, etc." + topic_views_heat_low: "Após todas estas visualizações, o campo de visitas fica ligeiramente destacado." + topic_views_heat_medium: "Após todas estas visualizações, o campo de visitas fica moderadamente destacado." + topic_views_heat_high: "Após todas estas visualizações, o campo de visitas fica fortemente destacado." + cold_age_days_low: "Após todos estes dias de conversação, a data da última atividade fica ligeiramente destacada." + cold_age_days_medium: "Após todos estes dias de conversação, a data da última atividade fica moderadamente destacada." + cold_age_days_high: "Após todos estes dias de conversação, a data da última atividade fica fortemente destacada." + history_hours_low: "Uma mensagem editada dentro destas horas tem o indicador de edição ligeiramente destacado." + history_hours_medium: "Uma mensagem editada dentro destas horas tem o indicador de edição moderadamente destacado." + history_hours_high: "Uma mensagem editada dentro destas horas tem o indicador de edição fortemente destacado." + topic_post_like_heat_low: "Depois da proporção gostos:mensagem exceeder este rácio, o campo de contagem da mensagem irá ficar ligeiramente destacado." + topic_post_like_heat_medium: "Depois da proporção gostos:mensagem exceeder este rácio, o campo de contagem da mensagem irá ficar moderadamente destacado." + topic_post_like_heat_high: "Depois da proporção gostos:mensagem exceeder este rácio, o campo de contagem da mensagem irá ficar fortemente destacado." faq_url: "Se você possui um FAQ hospedado em outro local e que você queira usar, forneça a URL completa aqui." tos_url: "Se você tem um documento de Termos de Serviço hospedado em algum outro local que você queira usar, forneça a URL completa aqui." privacy_policy_url: "Se você tem um documento de Política de Privacidade hospedado em algum outro local que você queira usar, forneça a URL completa aqui." newuser_spam_host_threshold: "Quantas vezes um usuário pode postar um link para o mesmo site dentro de`newuser_spam_host_posts` posts antes de ser considerado spam." + white_listed_spam_host_domains: "Lista de domínios excluídos dos testes de spam. Novos utilizadores nunca irão ser restritos da criação de mensagens com hiperligações a esses domínios." + staff_like_weight: "Quanto é o fator de ponderação extra a dar aos gostos provenientes do pessoal." + levenshtein_distance_spammer_emails: "Ao fazer a correspondência de e-mails de spam, o número de caracteres de diferença que ainda permitirá uma correspondência difusa." + max_new_accounts_per_registration_ip: "Se já há (n) contas com nível de confiança 0 a partir deste IP (e nenhum é um membro do pessoal ou em nível de confiança 2 ou superior), não permitir novos registos a partir desse IP." + min_ban_entries_for_roll_up: "Ao carregar no botão Agrupar, irá criar uma nova entrada de sub-rede banida se houver pelo menos (N) entradas." + max_age_unmatched_emails: "Apagar entradas de email não encontradas após (N) dias." + max_age_unmatched_ips: "Apagar entradas IP não encontradas após (N) dias." + num_flaggers_to_close_topic: "Número mínimo de sinalizações únicas que são necessárias para automaticamente pausar um tópico para intervenção." + num_flags_to_close_topic: "Número mínimo de sinalizações ativas que são necessárias para automaticamente pausar um tópico para intervenção." + reply_by_email_enabled: "Permitir respostas aos tópicos por email." + reply_by_email_address: "Modelo para endereços emails recebidos com função resposta por email, por exemplo: %{reply_key}@resposta.exemplo.com ou resposta+%{reply_key}@exemplo.com" + disable_emails: "Impedir que Discourse envie qualquer tipo de emails" + strip_images_from_short_emails: "Remover imagens de emails cujo tamanho seja inferior a 2800 Bytes" + short_email_length: "Comprimento de email curto em Bytes" + enable_email_names: "Permitir exibição de nome completo do utilizador nos emails. Desativar para esconder nome completo nos emails." + pop3_polling_enabled: "Solicitação através de POP3 para respostas de emails" + pop3_polling_ssl: "Utilize SSL ao ligar a um servidor POP3. (Recomendado)" + pop3_polling_period_mins: "Período em minutos entre a verificação da conta POP3 para o email. NOTA: requer reinicialização." + pop3_polling_port: "Porto para resgatar uma conta POP3." + pop3_polling_host: "Host para resgatar email via POP3" + pop3_polling_username: "Nome de utilizador para a conta POP3 para resgatar emails." + pop3_polling_password: "Senha para a conta POP3 para resgatar emails." + email_in: "Permitir que utilizadores publiquem novos tópicos através de email (requer pop3). Configurar os endereços no separador \"Configurações\" de cada categoria." + email_in_min_trust: "Nível de confiança mínimo que um utilizador necessita de ter para poder publicar novos tópicos por email." + email_prefix: "A [etiqueta] usada no assunto dos emails. Se não estiver configurado, será 'title' por defeito." + email_site_title: "Título do sítio usado como remetente de emails. Se não for configurado, será 'title' por defeito. Se o seu 'title' contém caracteres que não são permitidos na string do remetente de email, utilize esta configuração. " + minimum_topics_similar: "Quantos tópicos precisam de existir antes de serem apresentados tópicos semelhantes ao compor novos tópicos." + relative_date_duration: "Número de dias após uma publicação durante o qual as datas das mensagens irão ser mostradas como relativas(7d) em vez de absolutas(20 Fev)." + delete_user_max_post_age: "Não permitir eliminação de utilizadores cuja primeira mensagem é mais antiga que (x) dias." delete_all_posts_max: "Número máximo de postagens que podem ser apagadas de uma vez com o botão Remover Todos Postagens. Se um usuário tiver mais postagens do que este número, as postagens não poderão ser todas removidas de uma vez e o usuário não poderá ser removido." username_change_period: "O número de dias após o registro para que as contas possam mudar o seu nome de usuário (0 para não permitir a mudança de nome de usuário)." email_editable: "Permitir que os usuários alterem o seu endereço de e-mail após o registro." + logout_redirect: "Localização de redireccionamento do navegador após encerramento da sessão EX: (http://somesite.com/logout)" + allow_uploaded_avatars: "Permitir que utilizadores carreguem avatars personalizados." + allow_animated_avatars: "Permitir que os utilizadores usem avatars com imagens animadas gifs. AVISO: executar os avatars:atualizar tarefa após mudança de configuração." + allow_animated_thumbnails: "Gera miniaturas ou gifs animados." + automatically_download_gravatars: "Descarregar Gravatars para os utilizadores após criação de conta ou mudança de email." + digest_topics: "Número máximo de tópicos a serem apresentados no resumo do email." + digest_min_excerpt_length: "Tamanho mínimo do excerto da mensagem no resumo do email, em caracteres" default_digest_email_frequency: "Quantas vezes os usuários recebem emails de resumo por padrão. Eles podem alterar essa configuração em suas preferências." + default_external_links_in_new_tab: "Abrir hiperligações externas num novo separador. Os utilizadores podem alterar isto nas suas preferências." + detect_custom_avatars: "Verificar ou não que os utilizadores carregaram avatars personalizados." + max_daily_gravatar_crawls: "Número máximo de vezes que o Discourse irá verificar o Gravatar para avatars personalizados num dia." + public_user_custom_fields: "Lista de campos personalizados para um utilizador que podem ser exibidos publicamente." + allow_profile_backgrounds: "Permitir que os utilizadores carreguem fundos de perfil." + sequential_replies_threshold: "Número de mensagens que um utilizador tem que fazer em linha num tópico antes de ser relembrado acerca de demasiadas respostas sequenciais." enable_mobile_theme: "Os dispositivos móveis usam um tema mobile-friendly, com a possibilidade de mudar para o site completo. Desative isso se você quiser usar um estilo personalizado que é totalmente responsivo." + dominating_topic_minimum_percent: "Que percentagem de mensagens um utilizador tem que fazer num tópico antes de ser relembrado sobre dominar demasiado um tópico." + suppress_uncategorized_badge: "Não mostrar distintivos para tópicos sem categoria nas listagens de tópicos." + global_notice: "Exibir um banner global URGENTE, EMERGÊNCIA a todos os visitantes, mudar para branco para esconder (HTML permitido)." + disable_edit_notifications: "Desativa notificações de edição pelo sistema quando 'download_remote_images_to_local' está ativo." + enable_names: "Permite a exibição de nomes completos de utilizadores. Desativar para esconder nomes completos." + display_name_on_posts: "Mostrar o nome completo de um utilizador nas suas mensagens em adição ao seu @nome-de-utilizador." + invites_per_page: "Convites por defeito exibidos na página do utilizador." + short_progress_text_threshold: "Após o número de mensagens num tópico passar acima deste número, a barra de progresso irá apenas mostrar o número da mensagem atual. Se alterar a largura da barra de progresso, poderá ter que mudar este valor." + default_code_lang: "Linguagem de programação por defeito a aplicar no destaque da sintaxe em blocos de código GitHub (lang-auto, ruby, python, etc.)" + warn_reviving_old_topic_age: "Quando alguém começa a responder a um tópico em que a última resposta é mais antiga que estes dias, um aviso irá ser exibido. Desativar ao configurar para 0." + autohighlight_all_code: "Forçar o destaque do código a todos os blocos de código pré-formatados mesmo quando não se especifica a linguagem." + embeddable_host: "Host que pode incorporar os comentários deste fórum Discourse." + feed_polling_enabled: "INCORPORAR APENAS: embutir feeds RSS/ATOM como mensagens." + feed_polling_url: "INCORPORAR APENAS: URL dos feeds de RSS/ATOM para embutir." + embed_by_username: "Nome de utilizador Discourse do utilizador que cria tópicos embebidos." + embed_username_key_from_feed: "Chave para recuperar o nome de utilizador discourse do feed" + embed_truncate: "Truncar mensagens embutidas" + embed_category: "Categoria de tópicos embutidos" + embed_post_limit: "Número máximo de mensagens a serem incorporadas." + embed_whitelist_selector: "Seletor CSS para elementos permitidos em embeds." + embed_blacklist_selector: "Seletor CSS para elementos que foram removidos de embeds." + notify_about_flags_after: "Se houver sinalizações que não tenham sido tratadas após tantas horas, envie um email para contact_email. Configurar a 0 para desativar." + enable_cdn_js_debugging: "Permitir que /logs exiba erros próprios ao adicionar permissões de origem-cruzada em todos os js incluídos." + show_create_topics_notice: "Se o sítio tem menos de 5 tópicos públicos, mostrar um aviso pedindo aos administradores a criação de mais tópicos." + vacuum_db_days: "Executar VACUUM FULL ANALYZE para reclamar espaço na Base de Dados após a migração (configurar a 0 para desativar)" + prevent_anons_from_downloading_files: "Previna utilizadores anónimos de descarregarem anexos. AVISO: isto irá fazer com que quaisquer atributos não-imagens publicados como anexos não funcionem." + errors: + invalid_email: "Endereço de email inválido." + invalid_username: "Não existe nenhum utilizador com esse nome de utilizador" + invalid_integer_min_max: "O valor deve estar entre %{min} e %{max}." + invalid_integer_min: "O valor deve ser %{min} ou superior." + invalid_integer_max: "O valor não pode ser superior a %{max}." + invalid_integer: "O valor deve ser um inteiro." + regex_mismatch: "O valor não coincide com o formato exigido." + invalid_string: "Valor inválido." + invalid_string_min_max: "Deve ser entre %{min} e %{max} caracteres." + invalid_string_min: "Deve ser pelo menos %{min} caracteres." + invalid_string_max: "Não deve ser mais que %{max} caracteres." notification_types: mentioned: "%{display_username} mencionou-te em %{link}" liked: "%{display_username} curtiu sua postagem em %{link}" @@ -529,14 +897,26 @@ pt: private_message: "%{display_username} enviou-te uma mensagem particular: %{link}" invited_to_private_message: "%{display_username} convidou-te para uma conversa privada: %{link}" invitee_accepted: "%{display_username} aceitou o seu convite" + linked: "%{display_username} ligou-o em %{link}" + granted_badge: "Ganhou %{link}" search: + within_post: "#%{post_number} por %{username}" types: category: 'Categorias' + topic: 'Resultados' user: 'Utilizadores' + sso: + not_found: "Impossível de encontrar ou criar uma conta, contacte a administração do sítio" + account_not_approved: "A conta está pendente de aprovação, irá receber uma notificação por email assim que for aprovada." + unknown_error: "Erro ao atualizar a informação, contacte a administração do sítio" + timeout_expired: "Tempo para início de sessão de conta expirado, por favor tente entrar novamente" original_poster: "Postador original" most_posts: "Maior parte das postagens" most_recent_poster: "Maior parte das Postagens Recentes" frequent_poster: "Postador frequente" + redirected_to_top_reasons: + new_user: "Bem-vindo à nossa comunidade! Estes são os mais populares e recentes tópicos." + not_seen_in_a_month: "Bem-vindo novamente! Não o vemos há algum tempo. Estes são os tópicos mais populares desde que esteve ausente." move_posts: new_topic_moderator_post: one: "Eu movi uma postagem para este novo tópico: %{topic_link}" @@ -544,19 +924,47 @@ pt: existing_topic_moderator_post: one: "Eu movi uma postagem para este tópico: %{topic_link}" other: "Eu movi %{count} postagens para este tópico: %{topic_link}" + change_owner: + post_revision_text: "Direito de posse transferido de %{old_user} para %{new_user}" topic_statuses: archived_enabled: "Este tópico está agora arquivado. Está congelado e não pode ser alterado de qualquer forma." archived_disabled: "Este tópico foi agora desarquivado. Já não está congelado, e pode ser alterado." closed_enabled: "Este tópico está agora fechado. Novas respostas não são aceites." closed_disabled: "Este tópico está agora aberto. Novas respostas serão aceites." + autoclosed_enabled_days: + one: "Este tópico foi automaticamente encerrado após 1 dia. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente encerrado após %{count} dias. Novas respostas não são permitidas." + autoclosed_enabled_hours: + one: "Este tópico foi automaticamente encerrado após 1 hora. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente encerrado após %{count} horas. Novas respostas não são permitidas." + autoclosed_enabled_minutes: + one: "Este tópico foi automaticamente fechado após 1 minuto. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado após %{count} minutos. Novas respostas não são permitidas." + autoclosed_enabled_lastpost_days: + one: "Este tópico foi automaticamente fechado 1 dia após a última resposta. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado %{count} dias após a última resposta. Novas respostas não são permitidas." + autoclosed_enabled_lastpost_hours: + one: "Este tópico foi automaticamente fechado 1 hora após a última resposta. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado %{count} horas após a última resposta. Novas respostas não são permitidas." + autoclosed_enabled_lastpost_minutes: + one: "Este tópico foi automaticamente fechado 1 minuto após a última resposta. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado %{count} minutos após a última resposta. Novas respostas não são permitidas." autoclosed_disabled: "Este tópico está aberto agora. Novas respostas estão permitidas." + autoclosed_disabled_lastpost: "O tópico está agora aberto. Novas respostas são agora permitidas." + pinned_enabled: "Este tópico está agora fixado. Irá aparecer no topo da sua categoria até ser desafixado pelo pessoal para todas as pessoas, ou por utilizadores individuais para si próprios." pinned_disabled: "Este tópico deixou de estár afixado. Não irá mais aparecer no topo das suas categorias." + pinned_globally_enabled: "Este tópico está globalmente fixado. Irá aparecer no topo da sua categoria e em todas as listas de tópicos até ser desafixado pelo pessoal para todas as pessoas, ou por utilizadores individuais para si próprios." + pinned_globally_disabled: "Este tópico está agora desafixado. Não irá aparecer no topo da sua categoria." + visible_enabled: "Este tópico está agora listado. Irá ser exibido na lista de tópicos." + visible_disabled: "Este tópico deixou de estar listado. Não será exibido em qualquer lista de tópicos. A única maneira de aceder a este tópico é através de hiperligação direta." login: not_approved: "A sua conta ainda não foi aprovada. Vais ser notificado por email assim que estiver pronto para fazeres log in." incorrect_username_email_or_password: "Usuário, email ou senha incorretos" wait_approval: "Obrigado por se registar. Você será notificado por email quando a sua conta for aprovada." active: "A sua conta está ativa e pronta." + activate_email: "

    Está quase no final! Enviámos um email de ativação para %{email}. Por favor siga as instruções no email para ativar a sua conta.

    Se não receber o email, verifique a sua pasta de spam, ou tente autenticar-se novamente para enviar outro email de ativação.

    " not_activated: "Ainda não podes fazer log in. Enviamos um email de ativação para você. Por favor siga as instruções no email para ativar a sua conta." + not_allowed_from_ip_address: "Não pode autenticar-se como %{username} desse endereço IP" suspended: "Não pode iniciar sessão até %{date}" suspended_with_reason: "Não pode iniciar sessão até %{date}, pois encontra-se ativa uma suspensão com o seguinte motivo: %{reason}" errors: "%{errors}" @@ -564,8 +972,16 @@ pt: something_already_taken: "Algo deu errado, talvez o nome de usuário ou o email já estejam registrados. Tente o link Esqueci minha Senha." omniauth_error: "Desculpe, Houve um erro ao autorizar a sua conta %{strategy}. Talvez você não tenha aprovado a autorização?" omniauth_error_unknown: "Algo deu errado no processamento do seu login, por favor tente novamente." + new_registrations_disabled: "Novo registo de contas não é permitido neste momento" + password_too_long: "As senhas estão limitadas a 200 caracteres" + missing_user_field: "Não completou todos os campos de utilizador." + close_window: "A autenticação está completa, Feche esta janela para continuar." user: + no_accounts_associated: "Sem contas associadas" username: + short: "deve ter pelo menos %{min} caracteres" + long: "não deve ter mais que %{max} caracteres" + characters: "pode incluir apenas números, letras e underscores" unique: "tem que ser único" blank: "tem que ser preenchido" must_begin_with_alphanumeric: "tem de começar com uma letra ou um número" @@ -574,19 +990,305 @@ pt: blocked: "não é permitido." ip_address: blocked: "está bloqueado." + invite_mailer: + subject_template: "%{invitee_name} convidou-o a juntar-se a '%{topic_title}' em %{site_domain_name}" + text_body_template: | + %{invitee_name} convidou-o para um debate + + > **%{topic_title}** + > + > %{topic_excerpt} + + em + + > %{site_title} -- %{site_description} + + Se está interessado, carregue na hiperligação abaixo: + + %{invite_link} + + Este convite é de um utilizador confiável, por isso pode responder ao debate imediatamente. + invite_forum_mailer: + subject_template: "%{invitee_name} convidou-o a juntar-se a %{site_domain_name}" + text_body_template: | + %{invitee_name} convidou-o a juntar-se + + > **%{site_title}** + > + >%{site_description} + + Se está interessado, carregue na hiperligação abaixo: + + %{invite_link} + + Este convite é de um utilizador confiável, por isso não precisa de autenticar-se. + invite_password_instructions: + subject_template: "Configurar senha para a sua conta %{site_name} " + text_body_template: | + Obrigado por ter aceite o seu convite para %{site_name} -- bem-vindo! + + Para entrar novamente, carregue na seguinte hiperligação para escolher uma senha: + %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Teste de entrega de email" + text_body_template: "Este é um email de teste de\n[**%{base_url}**][0]\nO envio do email é complicado. Aqui estão alguns pontos importantes a verificar primeiro.\n- *Certifique-se* que configurou o `email de notificação` de: endereço correto nas configurações do seu sítio. **O domínio especificado no endereço “De” nos emails que envia é o domínio que irá ser validado**.\n- Conhecer como observAr o código fonte dos emails no seu email cliente, de modo a que possa examinAr cabeçalhos de email para pistas importantes. No Gmail, é a opção “mostrar original” no menu drop down no canto superior direito de cada email. \n\n- **IMPORTANTE:** O seu ISP tem um registo de DNS inverso inserido para associar os nomes de domínio aos endereços IP de onde envia o seu email? [Teste o seu registo de PTR Inverso][2] aqui. Se o seu ISP não inserir o DNS inverso apropriado, é muito improvável que qualquer um dos seus emails seja entregue.\n\n- Estará o seu domínio [registo SPF][8] correto? [Teste o seu registo SPF][1] aqui. Note que TXT é o tipo de registo oficial para SPF.\n\n- Estará o seu domínio [registo DKIM][3] correto? Isto irá melhorar significativamente a entrega de emails. [Teste o seu registo DKIM][7] aqui.\n- Se correr o seu próprio servidor de email, certifique-se que os IPs do seu servidor de email não [estão em nenhuma listanegraemail] [4]. Verifique também que é enviado um um hostname qualificado que resolve o DNS na mensagem HELO.\ + \ Se não for, isto irá fazer com que o seu email seja rejeitado por muitos serviços de email. \n(A maneira *fácil* é criar uma conta gratuita em [Mandrill][md] ou [Mailgun][mg] ou [Mailjet][mj], que tem vários planos de email gratuitos e será bom para pequenas comunidades. Irá precisar na mesma de configurar os registos SPF e o DKIM no seu DNS!)\n\nEsperamos que tenha recebido este teste de entrega de email sem problema!\n\nBoa sorte,\n\nOs seus amigos em [Discourse](http://www.discourse.org)\n\n[0]: %{base_url}\n[1]: http://www.kitterman.com/spf/validate.html\n[2]: http://mxtoolbox.com/ReverseLookup.aspx\n[3]: http://www.dkim.org/\n[4]: http://whatismyipaddress.com/blacklist-check\n[5]: %{base_url}/unsubscribe\n[7]: http://dkimcore.org/tools/dkimrecordcheck.html\n[8]: http://www.openspf.org/SPF_Record_Syntax\n[md]: http://mandrill.com\n[mg]: http://www.mailgun.com/\n[mj]: http://www.mailjet.com/pricing\n\n----\n\nDeverá haver um rodapé de não subscrição em todos os emails que envia por isso vamos simular um. Este email foi enviado por Nome da Empresa, 55 Rua Principal, Cidade, PT 12345. Se desejar não receber mais emails, [carregue aqui para remover a subscrição][ 5].\n" + new_version_mailer: + subject_template: "[%{site_name}] Nova versão Discourse, atualização disponível" + text_body_template: | + Uma nova versão de [Discourse](http://www.discourse.org) está disponível. + + A sua versão: %{installed_version} + Nova versão: **%{new_version}** + + Pode desejar: + + - Ver o que há de novo em [GitHub changelog](https://github.com/discourse/discourse/commits/master). + + - Atualizar visitando [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade), e pressionando o botão "Atualizar". + + - Visitar [meta.discourse.org](http://meta.discourse.org) para notícias, debates, e suporte para o Discourse. + new_version_mailer_with_notes: + subject_template: "[%{site_name}] atualização disponível" + text_body_template: | + Uma nova versão de [Discourse](http://www.discourse.org) está disponível. + + A sua versão: %{installed_version} + Nova versão: **%{new_version}** + + Pode desejar: + + - Ver o que há de novo em [GitHub changelog](https://github.com/discourse/discourse/commits/master). + + - Atualizar visitando [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade), e pressionando o botão "Atualizar". + + - Visitar [meta.discourse.org](http://meta.discourse.org) para notícias, debates, e suporte para o Discourse. + + ### Notas de lançamento + + %{notes} + flags_reminder: + flags_were_submitted: + one: "Estas sinalizações foram submetidas há 1 hora atrás." + other: "Estas sinalizações foram submetidas há %{count} horas atrás." + please_review: "Por favor examine." + post_number: "mensagem" + how_to_disable: 'Pode desativar ou mudar a frequência deste email notificativo através de "notify about flags after" nas configurações.' + subject_template: + one: "1 sinalização à espera de ser tratada" + other: "%{count} sinalizações à espera de serem tratadas" + flag_reasons: + off_topic: "A sua mensagem foi sinalizada como **fora do contexto**: a comunidade pensa que não se enquadra neste tópico, conforme o seu título e a sua primeira mensagem." + inappropriate: "A sua mensagem foi sinalizada como **inapropriada**: a comunidade pensa que é ofensiva, abusiva ou que é uma violação das [diretrizes da comunidade](/guidelines)." + spam: "A sua mensagem foi sinalizada como **spam**: a comunidade pensa que é um anúncio, sem utilidade ou relevância para este tópico, mas promocional na sua natureza." + notify_moderators: "A sua mensagem foi sinalizada **para atenção do moderador**: a comunidade pensa que algo na sua mensagem requer intervenção do moderador." + flags_dispositions: + agreed: "Obrigado por nos informar. Concordamos que existe um problema e estamos a analisá-lo." + agreed_and_deleted: "Obrigado por nos informar. Concordamos que existe um problema e removemos a mensagem." + disagreed: "Obrigado por nos informar. Estamos a analisá-lo." + deferred: "Obrigado por nos informar. Estamos a analisá-lo." + deferred_and_deleted: "Obrigado por nos informar. Removemos a mensagem." + temporarily_closed_due_to_flags: "O tópico está temporariamente fechado devido a um grande número de sinalizações da comunidade." system_messages: post_hidden: subject_template: "%{site_name} Aviso: Postagem escondida devido a Sinalização pela Comunidade" + text_body_template: | + Olá, + + Esta é uma mensagem automática de %{site_name} para informá-lo que a sua mensagem foi escondida. + + %{base_url}%{url} + + %{flag_reason} + + Múltiplos membros da comunidade sinalizaram esta mensagem antes de ser escondida, por isso considere como poderá rever a sua mensagem para refletir o seu feedback. **Pode editar a sua mensagem após %{edit_delay} minutos, e esta irá ser automaticamente mostrada.** Isto irá aumentar o seu nível de confiança. + + Contudo, se a mensagem for escondida pela comunidade uma segunda vez, irá manter-se escondida até ser tratada pelo pessoal – e poderão ainda ocorrer ações, incluindo uma possível suspensão da sua conta. + + Para orientação adicional, por favor consulte as [diretrizes da comunidade](%{base_url}/guidelines). + usage_tips: + text_body_template: "Esta mensagem privada tem algumas dicas para que possa começar.\n\n## Continue a arrastar para baixo\n\nNão há botões para páginas seguintes nem números de páginas – para ler mais, **simplesmente continue a arrastar para baixo!**\n\nÀ medida que novas mensagens vão surgindo, estas irão aparecer automaticamente.\n\n## Onde estou?\n\n- Para pesquisar, a sua página de utilizador ou o menu, use o botão com o **icone no canto superior direito**.\n\n- Qualquer título de tópico irá levá-lo à próxima mensagem não lida. Utilize o tempo da última atividade e o contador de mensagens para ir para o inicio ou para o final.\n- Ao ler um tópico, salte para o topo ↑ ao selecionar o título do tópico. Selecione a barra de progresso verde no canto inferior direito para controlos de navegação completos, ou utilize página principal e chavesfinais.\n\n\n\n## Como posso responder?\n\n- Para responder ao tópico, utilize o botão de Resposta no final de cada página.\n\n- Para responder a uma mensagem específica, utilize o botão de Resposta nessa mensagem.\n\n-Para levar a conversa para um rumo diferente, mas mantê-las juntas utilize Responder como Tópico ligado à direita desta mensagem.\n\nPara citar alguém na sua resposta, selecione o texto que pretende citar, e de seguida pressione qualquer um dos botões de Resposta.\n\n\nPara mencionar alguém na sua resposta, mencione os seus nomes. Escreva `@` e um autocompletador irá aparecer. \n\n\nPara [Emoji padrão](http://www.emoji.codes/), simplesmente comece a digitar `:` ou os tradicionais risonhos `:)`\n:smile:\n\n## O que mais posso fazer?\n\nHá botões de ação no final de cada publicação.\n\n\n\nPara deixar alguém saber que gostou da sua mensagem, utilize o botão **gusto**. Se vir um problema com a mensage, avise em privado através do botão de **sinalização**.\n\nPode também **partilhar** uma hiperligação de uma publicação, ou **marcá-lo** para mais tarde ter a referência na sua página de perfil. \n\nQuem está a falar comigo?\n\nQuando alguém responde à sua mensagem, cita a sua mensagem ou menciona o seu `@username`, um número irá aparecer imediatamente no canto superior direito na página. Utilize-o ao aceder às suas **notificações**.\n\n\n\nNão se preocupe em\ + \ falhar uma resposta – irá receber emails com respostas diretas (e mensagens privadas) se não estiver online quando estas chegam.\n\n## Quando são as conversações consideradas novas?\n\nPor defeito, todas as conversações com menos de dois dias são consideradas novas, e qualquer conversãção onde ainda não tenha participado (respondido, criado, ou lido por um extenso período) irão ser automaticamente acompanhadas. \n\n\n\nPode alterar o estado individual das notificações de um tópico através do controlo no final do tópico (também pode ser definido por categoria). Para mudar a maneira como os tópicos são acompanhados, ou a definição de novo, consulte [as suas preferências](/my/preferences).\n\n## Porque não posso fazer certas coisas?\n\nNovos utilizadores estão um pouco limitados por razões de segurança. À medida que for participando, irá ganhar confiança por parte da comunidade, tornar-se um cidadão completo e essas limitações serão automaticamente removidas. Com um [nível de confiança] suficientemente alto (https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), irá ganhar ainda mais capacidades para ajudar-nos a gerir a comunidade em conjunto.\n" welcome_user: subject_template: "Bem-vindo em %{site_name}!" + text_body_template: | + Obrigado por se juntar a %{site_name}, e bem-vindo! + + %{new_user_tips} + + Acreditamos no [comportamento civilizado da comunidade](%{base_url}/guidelines) em todas as alturas. + + Desfrute da sua estadia! welcome_invite: subject_template: "Bem-vindo em %{site_name}!" + text_body_template: | + Obrigado por aceitar o seu convite para %{site_name} -- bem-vindo! + + Gerámos automaticamente um nome de utilizador para si: **%{username}**, mas poderá mudá-lo em qualquer altura ao visitar [o seu perfil de utilizador][prefs]. + + Para entrar novamente, pode: + + 1. Entrar usando qualquer método que prefira -- desde que se refira ao **mesmo endereço de email** no qual recebeu o seu convite original. De outra maneira, não poderemos saber se é você! + + 2. Criar uma senha única para [o seu perfil de utilizador][prefs], e usá-la para entrar. + + %{new_user_tips} + + Acreditamos no [comportamento civilizado da comunidade](%{base_url}/guidelines) em todas as alturas. + + Desfrute da sua estadia! + + [prefs]: %{user_preferences_url} + backup_succeeded: + subject_template: "Cópia de segurança completa com sucesso" + text_body_template: "A cópia de segurança foi feita com sucesso.\nVisite a [admin > secção de cópias de segurança](/admin/backups) para descarregar a sua nova cópia de segurança." + backup_failed: + subject_template: "A cópia de segurança falhou" + text_body_template: | + A cópia de segurança falhou. + + Aqui está o registo do log: + + ``` + %{logs} + ``` + restore_succeeded: + subject_template: "Restauração completa corretamente" + text_body_template: "A restauração foi bem sucedida." + restore_failed: + subject_template: "A restauração falhou" + text_body_template: | + A restauração falhou. + + Aqui está o registo: + + ``` + %{logs} + ``` + bulk_invite_succeeded: + subject_template: "Convites de utilizadores em massa processados com sucesso" + text_body_template: "O seu ficheiro de convites de utilizadores em massa foi processado, %{sent} convites enviados." + bulk_invite_failed: + subject_template: "Convite de utilizadores em massa processado com erros" + text_body_template: | + O seu ficheiro de convite de utilizadores em massa foi processado, %{sent} convites enviados com %{failed} erro(s). + + Aqui está o registo do log: + + ``` + %{logs} + ``` + email_reject_trust_level: + subject_template: "Problema de email -- Nível de Confiança Insuficiente" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + A sua conta não tem o nível de confiança necessário para publicar novos tópicos para este endereço de email. Se acredita que isto é um erro, contacte um membro do pessoal. + email_reject_no_account: + subject_template: "Problema de email -- Conta Desconhecida" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email %{destination} (titled %{former_title}) não funcionou. + + Não há nenhuma conta de utilizador conhecida com este endereço de email. Tente enviar a partir de um endereço de email diferente, ou contacte um membro do pessoal. + email_reject_empty: + subject_template: "Problema de email -- Sem Conteúdo" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Não conseguimos encontrar nenhum conteúdo no email. Certifique-se que a sua resposta está no topo do email -- não podemos processar respostas em linha. + + Se está a ter este erro e se _realmente_ colocou conteúdo, tente novamente com conteúdo HTML incluído no seu email (e não apenas texto simples). + email_reject_parsing: + subject_template: "Problema de email -- Conteúdo não reconhecido" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Não conseguimos encontrar nenhuma resposta no email fornecido. **Certifique-se que a sua resposta está no topo do email** -- não conseguimos processar respostas em linha. + email_reject_post_error: + subject_template: "Problema de email -- Erro de publicação" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Algumas causas possíveis são: formatação complexa, mensagem demasiado grande, mensagem demasiado pequena. Por favor tente novamente, ou publique através do sítio se isto continuar. + email_reject_post_error_specified: + subject_template: "Problema de email -- Erro de publicação" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Motivo: + + %{post_error} + + Se conseguir corrigir o problema, tente novamente. + email_reject_reply_key: + subject_template: "Problema de email -- Chave de Resposta Desconhecida" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + A chave de resposta fornecida é inválida ou desconhecida, por isso não sabemos a que resposta corresponde este email. Contacte um membro do pessoal. + email_reject_destination: + subject_template: "Problema de email -- Endereço Para: Desconhecido" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Nenhum dos endereços de destino é reconhecido. Por favor, certifique-se que o endereço do sítio está na linha Para: (não em Cc: ou em Bcc:), e que está a enviar para o endereço de email correto fornecido pelo pessoal. + email_reject_topic_not_found: + subject_template: "Problema de email -- Tópico Não Encontrado" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + O tópico não foi encontrado, poderá ter sido eliminado. Se acredita que isto é um erro, contacte um membro do pessoal. + email_reject_topic_closed: + subject_template: "Problema de email -- Tópico Encerrado" + text_body_template: | + Pedimos desculpa, mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + O tópico está fechado. Se acredita que isto é um erro, contacte um membro do pessoal. + email_error_notification: + subject_template: "Problema de email -- Erro de autenticação POP" + text_body_template: | + Ocorreu um erro de autenticação ao recuperar emails do servidor POP. + + Por favor certifique-se que configurou corretamente as credenciais POP em [configurações do sítio](%{base_url}/admin/site_settings/category/email). too_many_spam_flags: subject_template: "Nova conta bloqueada" + text_body_template: | + Olá, + + Esta é uma mensagem automática de %{site_name} para informá-lo que as suas mensagens foram automaticamente eliminadas por terem sido sinalizadas por parte da comunidade. + + Como medida de precaução, a sua nova conta foi bloqueada de criar novas respostas ou tópicos até que um membro do pessoal possa rever a sua conta. + + Para orientação adicional, por favor consulte as [diretrizes da comunidade](%{base_url}/guidelines). blocked_by_staff: subject_template: "Conta bloqueada" + text_body_template: | + Olá, + + Esta é uma mensagem automática de %{site_name} para informá-lo de que a sua conta foi bloqueada por um membro do pessoal. + + Para orientação adicional, por favor consulte as [diretrizes da comunidade](%{base_url}/guidelines). + user_automatically_blocked: + subject_template: "Novo utilizador %{username} bloqueado devido a sinalizações da comunidade." + text_body_template: | + Esta é uma mensagem automática. + + O novo utilizador [%{username}](%{base_url}%{user_url}) foi automaticamente bloqueado devido a múltiplos utilizadores terem sinalizado a(s) mensagen(s) de %{username}. + + Por favor [reveja as sinalizações](%{base_url}/admin/flags). Se %{username} foi incorretamente bloqueado de publicar, carregue no botão de desbloqueio em [a página de administração para este utilizador](%{base_url}%{user_url}). + + Este limite pode ser alterado através de `block_new_user` nas configurações do sítio. + spam_post_blocked: + subject_template: "Novas mensagens do novo utilizador %{username} foram bloqueadas devido a hiperligações repetidas" + text_body_template: | + Esta é uma mensagem automática. + + O novo utilizador [%{username}](%{base_url}%{user_url}) tentou criar múltiplas mensagens com hiperligações para %{domains}, mas estas foram bloqueadas para evitar spam. O utilizador ainda pode criar novas mensagens que não tem hiperligação a %{domains}. + + Por favor [reveja o utilizador](%{base_url}%{user_url}). + + Isto pode ser modificado através de `newuser_spam_host_threshold` e `white_listed_spam_host_domains`nas configurações do sítio. unblocked: subject_template: "Conta desbloqueada" text_body_template: | @@ -599,7 +1301,16 @@ pt: subject_template: one: "1 usuário aguardando aprovação" other: "%{count} usuários aguardando aprovação" + text_body_template: | + Há registos de novos utilizadores à espera de serem aprovados (ou rejeitados) antes de poderem aceder a este fórum. + + [Por favor reveja-os na secção de administração](%{base_url}/admin/users/list/pending). + download_remote_images_disabled: + subject_template: "Descarregamento de imagens remotas desativado" + text_body_template: "A configuração `download_remote_images_to_local` foi desativada porque o limite de espaço em disco em `download_remote_images_threshold` foi alcançado." unsubscribe_link: "Se você deseja se desinscrever destes emails, visite suas [preferências do usuário](%{user_preferences_url})." + subject_re: "Re:" + subject_pm: "[MP]" user_notifications: previous_discussion: "Respostas Anteriores" unsubscribe: @@ -608,7 +1319,14 @@ pt: reply_by_email: "Para responder, responda este email ou visite %{base_url}%{url} no seu navegador." visit_link_to_respond: "Para responder visite %{base_url}%{url} no seu navegador." posted_by: "Postado por %{username} em %{post_date}" + user_invited_to_private_message_pm: + subject_template: "[%{site_name}] %{username} convidou-o para uma mensagem privada '%{topic_title}'" + text_body_template: | + %{username} convidou-o para uma mensagem privada '%{topic_title}' em %{site_name}: + + Por favor visite a hiperligação para visualizar este tópico: %{base_url}%{url} user_replied: + subject_template: "[%{site_name}] %{topic_title}" text_body_template: | %{username} respondeu à sua postagem no tópico '%{topic_title}' de %{site_name}: @@ -618,6 +1336,7 @@ pt: --- %{respond_instructions} user_quoted: + subject_template: "[%{site_name}] %{topic_title}" text_body_template: | %{username} citou você no tópico '%{topic_title}' de %{site_name}: @@ -627,6 +1346,7 @@ pt: --- %{respond_instructions} user_mentioned: + subject_template: "[%{site_name}] %{topic_title}" text_body_template: | %{username} mencionou você no tópico '%{topic_title}' de %{site_name}: @@ -636,19 +1356,39 @@ pt: --- %{respond_instructions} user_posted: + subject_template: "[%{site_name}] %{topic_title}" text_body_template: | %{username} postou no tópico '%{topic_title}' de %{site_name}: --- %{message} + --- + %{respond_instructions} + user_posted_pm: + subject_template: "[%{site_name}] [PM] %{topic_title}" + text_body_template: | + %{message} + + %{context} + --- %{respond_instructions} digest: + why: "Um breve sumário de %{site_link} desde a sua última visita em %{last_seen_at}" + subject_template: "[%{site_name}] Resumo para %{date}" new_activity: "Nova atividade nos seus tópicos e postagens:" + top_topics: "Mensagens populares" + other_new_topics: "Tópicos populares" + unsubscribe: "O resumo é enviado de %{site_link} quando não o vemos há algum tempo. Para anular a subscrição %{unsubscribe_link}." click_here: "clique aqui" from: "resumo de %{site_name}" read_more: "Leia Mais" + more_topics: "Houve %{new_topics_since_seen} outros novos tópicos." + more_topics_category: "Mais novos tópicos:" + posts: + one: "1 mensagem" + other: "%{count} mensagens" forgot_password: subject_template: "[%{site_name}] Redefinição de senha" text_body_template: | @@ -658,6 +1398,22 @@ pt: Para escolher a sua nova senha clique no link a seguir: %{base_url}/users/password-reset/%{email_token} + set_password: + subject_template: "[%{site_name}] Configurar Password" + text_body_template: | + Alguém pediu para adicionar uma palavra-passe à sua conta em [%{site_name}](%{base_url}). Como alternativa, pode entrar usando qualquer serviço online suportado (Google, Facebook, etc) que estiver associado a este endereço de email validado. + + Se não fez este pedido, pode ignorar este email. + + Carregue na hiperligação seguinte para escolher uma palavra-passe. + %{base_url}/users/password-reset/%{email_token} + account_created: + subject_template: "[%{site_name}] A Sua Nova Conta" + text_body_template: | + Uma nova conta foi criada para si em %{site_name} + + Carregue na hiperligação seguinte para escolher uma password para a sua nova conta. + %{base_url}/users/password-reset/%{email_token} authorize_email: subject_template: "[%{site_name}] Confirma o seu novo endereço de email" text_body_template: | @@ -666,6 +1422,21 @@ pt: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "Você foi aprovado no %{site_name}!" + text_body_template: | + Bem-vindo a %{site_name}! + + Um membro do pessoal aprovou a sua conta em %{site_name}. + + Carregue na hiperligação seguinte para confirmar e ativar a sua conta: + %{base_url}/users/activate-account/%{email_token} + + Se a hiperligação não for carregável, tente copiá-la e colá-la na barra de endereço no seu navegador de internet. + + %{new_user_tips} + + Acreditamos no [comportamento civilizado da comunidade](%{base_url}/guidelines) at all times. + + Desfrute da sua estadia! signup: subject_template: "[%{site_name}] Ativar a sua nova conta" text_body_template: | @@ -675,10 +1446,26 @@ pt: %{base_url}/users/activate-account/%{email_token} Se não conseguires clicar no link em cima, tenta copia-lo e cola-lo na barra de endereções do seu browser de internet. + page_not_found: + title: "A página que solicitou não existe ou é privada." + popular_topics: "Popular" + recent_topics: "Recente" + see_more: "Mais" + search_title: "Pesquisar neste sítio" + search_google: "Google" + login_required: + welcome_message: | + #[Bem-vindo a %{title}](#welcome) + É necessário ter uma conta. Por favor crie uma conta ou entre para continuar. + terms_of_service: + title: "Termos de Serviço" + signup_form_message: 'Li e aceito os Termos de Serviço.' deleted: 'removido' upload: + edit_reason: "descarregas cópias locais de imagens" unauthorized: "Desculpe-nos, o arquivo que você está tentando enviar não é autorizado (extensões autorizadas: %{authorized_extensions})." pasted_image_filename: "Imagem colada" + store_failure: "Falha ao armazenar o carregamento #%{upload_id} para o utilizador #%{user_id}." attachments: too_large: "Desculpe, o arquivo que você está tentando enviar é muito grande (tamanho máximo é %{max_size_kb}%kb)." images: @@ -688,3 +1475,155 @@ pt: size_not_found: "Desculpe, mas não conseguimos determinar o tamanho da imagem. É possível que seu arquivo de imagem esteja corrompido?" flag_reason: sockpuppet: "Um novo utilizador criou um tópico, que um outro novo utilizador com o mesmo IP respondeu. Veja as configurações do sítio em flag_sockpuppets" + spam_hosts: "Este novo utilizador tentou criar múltiplas mensagens com hiperligações ao mesmo domínio. Verifique as configurações do sítio em newuser_spam_host_threshold." + email_log: + no_user: "Não foi possível encontrar o utilizador com o id %{user_id}" + suspended_not_pm: "O utilizador está suspenso, sem mensagens privadas" + seen_recently: "O utilizador foi visto recentemente" + post_not_found: "Não foi possível encontrar a mensagem com o id %{post_id}" + notification_already_read: "A notificação sobre o qual é este email já foi lida" + topic_nil: "post.topic está vazio" + post_deleted: "a mensagem foi apagada pelo autor" + user_suspended: "o utilizador foi suspenso" + already_read: "o utilizador já leu esta mensagem" + message_blank: "a mensagem está em branco" + message_to_blank: "message.to está em branco" + text_part_body_blank: "text_part.body está em branco" + body_blank: "corpo está em branco" + color_schemes: + base_theme_name: "Base" + guidelines: "Diretrizes" + privacy: "Privacidade" + edit_this_page: "Editar esta página" + static_topic_first_reply: | + Edite a primeira mensagem neste tópico para alterar os conteúdos da página %{page_name}. + guidelines_topic: + title: "FAQ/Diretrizes" + body: "\n\n## [Isto é um Local Civilizado para Debate](#civilizado)\n\nPor favor trate este fórum de debate com o mesmo respeito que faria num parque público. Nós somos também uma comunidade de partilha de recursos — um local para partilhar habilidades, conhecimento e interesses através de conversações.\n\nEstas não são regras rígidas e rápidas, meramente para ajuda do julgamento humano da nossa comunidade. Utilize estas diretrizes para este local claro para debates públicos civilizados.\n\n\n\n## [Melhorar o Debate](#melhorar)\n\nAjude-nos a fazer deste um óptimo local para debates ao trabalhar constantemente para melhorar de alguma maneira, mesmo que pequena. Se não tem a certeza que a sua mensagem adiciona algo à conversação, pense novamente no que quer dizer e tente novamente mais tarde. \n\nOs tópicos discutidos aqui importam-nos, e nós queremos agir como se também fossem importantes para si. Seja respeitador dos tópicos e das pessoas que o debatem, mesmo que discorde do que está a ser dito. \n\nUma maneira de melhorar o debate é descobrindo os que já estão a ocorrer. Por favor perca algum tempo a pesquisar os tópicos existentes aqui antes de responder ou começar um novo, e terá melhores hipóteses de conhecer outros que partilhem os mesmos interesses que você. \n\n\n\n## [Seja Agradável, Mesmo Quando Não Concorde](#agradável)\n\nPode desejar\ + \ responder a algo ao discordar do mesmo. Não há qualquer problema nisso. Mas lembre-se de _criticar ideias, não pessoas_. Por favor, evite:\n\n*Chamar Nomes.\n*Adicionar ataques pessoais.\n* Responder ao tom de uma mensagem e não ao seu conteúdo.\n*Contradições instintivas\nPelo contrário, forneça contra-argumentos que melhorem a conversação.\n\n\n\n## [A Sua Participação Conta](#participe)\n\nAs conversações que temos aqui definem o tom para toda a gente. Ajude-nos a influenciar o futuro da comunidade ao escolher comprometer-se nos debates que tornam este fórum um local interessante para se estar — e evite os que não o fazem.\n\nO Discourse fornece ferramentas que permitem que a comunidade identifique as melhores (e piores) contribuições: favoritas, marcas, gostos, sinalizações, respostas, edições, e por aí adiante. Utilize estas ferramentas para melhorar a sua própria experiência e de todos os outros também.\n \nVamos tentar deixar o nosso parquet melhor do que quando o encontrámos.\n\n\n\n## [Se vir um Problema, Sinalize-o](#sinalize-problemas)\n\nOs moderadores têm uma autoridade especial; estes são responsáveis por este fórum. Assim como você. Com a sua ajuda, os moderadores podem ser facilitadores da comunidade, não apenas zeladores ou polícias.\n\nQuando vir mau comportamento, não responda. Isto encoraja o mau comportamento ao reconhecê-lo, consome a sua\ + \ energia e gasta o tempo de todos. Apenas sinalize-o. Se houver sinalizações suficientes, ações irão ser tomadas, seja automaticamente ou por intervenção dos moderadores.\n\nDe maneira a manter a ordem da comunidade, os moderadores reservam-se ao direito de remover qualquer conteúdo e qualquer conta de utilizador por qualquer razão em qualquer altura. Os moderadores não prevêem novas mensagens em qualquer maneira; os moderadores e os operadores do sítio não têm qualquer responsabilidade por qualquer conteúdo publicado pela comunidade.\n\n\n\n## [Seja sempre civilizado](#seja-civilizado)\n\nNada sabote uma conversação saudável como insolências:\n\n*Seja civilizado. Não publique nada que uma pessoa sensata consideraria ofensiva, abusiva ou de ódio.\n*Mantenha-o limpo. Não publique nada obsceno ou sexualmente explicito.\n*Respeite os outros. Não assedie ou magoe ninguém, não personifique pessoas, e não exponha as suas informações pessoais.\n*Respeite o fórum. Não publique spam ou qualquer outra coisa que vandalize o fórum.\n\nEstes não são termos concretos com definições precisas — evite até a _aparência_ de qualquer uma destas coisas. Se não tem certeza, pergunte-se a si próprio como se sentiria se a sua mensagem fosse capa no New York Times.\n\nEste é um fórum público, e motores de pesquisa indexam estes debates. Mantenha a linguagem, hiperligações e imagens seguras para família e amigos.\n\n\n\n## [Mantenha-o Organizado](#manter-organizado)\n\nFaça o esforço para colocar as coisas no local certo, para que possamos passar mais tempo a debater e menos a limpar. Por isso:\n\n*Não comece um tópico na categoria errada.\n*Não cruze mensagens sobre o mesmo tema em múltiplos tópicos.\n*Não publique respostas sem conteúdo.\n*Não desvie um tópico ao mudar o seu centro de atenção.\n*Não assine as suas mensagens — qualquer mensagem tem a sua informação de perfil anexada à mesma.\n\nEm vez de publicar “+1” ou “Concordo”, utilize o botão Gosto. Em vez de levar um tópico existente para uma direção completamente diferente, utilize Responder como Novo Tópico.\n\n\n\n## [Publique Apenas As Suas Próprias Coisas](#roubo)\n\nNão pode publicar nada digital que pertença a qualquer outra pessoa, sem a sua permissão. Não pode publicar descrições de, hiperligações a, ou métodos para roubar propriedade intelectual de outras pessoas (software, vídeo, áudio, imagens), ou para quebrar qualquer outra lei. \n\n\n\n## [Termos de Serviço](#tds)\n\nSim, a legalidade é chata, mas temos que nos proteger – e por extensão, protegê-lo a si e aos seus dados – contra companheiros inimigos. Temos os [Termos de Serviço](/tds) que descrevem o seu (e o nosso) comportamento e direitos relativamente a conteúdo, privacidade e leis. Para usar este serviço tem que concordar\ + \ em cumprir os nossos [TDS](/tds).\n" + tos_topic: + title: "Termos de Serviço" + body: "Os seguintes termos e condições governam todo o uso do sítio %{company_domain} e todo o conteúdo, serviços e produtos disponíveis no ou através do sítio, incluindo mas não limitado a, %{company_domain} Software do Fórum, %{company_domain} Suporte do Fórum e serviço de alojamento %{company_domain} (\"Alojamento\"), (em conjunto, o Sítio). O Sítio é propriedade e operado por %{company_full_name} (\"%{company_name}\"). O Sítio oferecido está sujeito à sua aprovação sem nenhuma modificação de todos os termos e condições contidos aqui e todas as outras regras de operação políticas (incluindo, sem limitação, %{company_domain}’s [Política de Privacidade](/privacy) e [Diretrizes da Comunidade](/faq)) e procedimentos que possam ser publicados de tempos em tempos neste Sítio por %{company_name} (coletivamente, the \"Acordo\").\n\nPor favor leia este Acordo com cuidado antes de aceder ou usar o Sítio. Ao aceder ou usar qualquer parte do Sítio, concorda em estar limitado pelos termos e condições deste acordo. Se não concorda com todos os termos e condições deste acordo, então não deve aceder ao Sítio ou usar qualquer um dos serviços. Se estes termos e condições são considerados uma oferta de %{company_name}, a sua aceitação é expressamente limitada a estes termos. O Sítio está disponível apenas a indíviduos que têm pelo menos 13 anos de idade.\n\n\n\n## [1. A Sua Conta %{company_domain} ](#1)\n\nSe criar uma conta\ + \ no Sítio, será responsável por manter a segurança da sua conta e será totalmente responsável por todas as atividades que ocorrerem sob a sua conta. Terá que notificar imediatamente %{company_name} de qualquer uso não autorizado da sua conta ou qualquer outra falha de segurança. %{company_name} não será passível de quaisquer actos ou omissões da sua parte, incluindo danos de quaisquer tipos que incorram como resultado de tais atos ou omissões.\n\n\n\n## [2.Responsabilidade dos Contribuintes](#2)\n\nSe publicar material no Sítio, publicar hiperligações no Sítio, ou de outra maneira disponibilizar (ou permitir que qualquer terceira parte o faça) material no Sítio (qualquer material, “Conteúdo”), você será inteiramente responsáveis pelo conteúdo de, e qualquer dano resultante de tal Conteúdo. Esse é o caso independentemente de o Conteúdo em questão constituir texto, gráficos, ficheiros áudio, ou software de computadores. Ao tornar o Conteúdo disponível, está a representar e garantir que:\n\n* o descarregamento, cópia e uso do Conteúdo não irá infringir os direitos do proprietário, incluindo mas não limitado aos direitos de autor, patentes, marcas registadas ou direitos de segredos de troca, de qualquer terceira parte;\n* se o seu empregado sob a propriedade intelectual que você criou, você terá que (i) ou ter recebido permissões do seu empregado para publicar ou tornar o Conteúdo disponível, incluindo mas não\ + \ limitado a qualquer software, ou (ii) ter garantido uma renúncia do seu empregado a todos os direitos no e para o Conteúdo;\n* você está de acordo com licenças de qualquer terceira-parte relacionada com o Conteúdo, e fez todas as coisas necessárias para passar com sucesso quaisquer termos requeridos aos utilizadores finais;\n* o Conteúdo não contém ou instala quaisquer vírus, minhocas ou malware, cavalos de Tróia ou outro conteúdo destrutivo;\n* o Conteúdo não é spam, não é gerado por máquina, e não contém conteúdo indesejado sem ética ou comercial com intuito de conduzir o tráfico de sítios terceiros ou aumentar a posição de sites terceiros em motores de pesquisam, ou ainda outros atos ilegais (tais como phishing) ou destinatários enganadores relativamente à origem do material (tais como spoofing);\n* o Conteúdo não é pornográfico, nem contém ameaças ou incita violência, e não viola os direitos de privacidade ou publicidade de qualquer terceira parte;\n* o conteúdo não está a ser anunciado através de quaisquer mensagens eletrónicas tais como hiperligações de spam ou grupos de notícias, listas de email, blogs e sítios, e similares métodos promocionais não solicitados;\n* o seu conteúdo não está nomeado de nenhuma maneira que leve os seus leitores a pensarem que é outra pessoa ou empresa; e \n* você terá, no caso do Conteúdo incluir código de computador, categorizado e/ou descrito o tipo, natureza, uso e efeito dos materiais,\ + \ quer tenha sido pedido para fazê-lo por %{company_name} ou pelo contrário.\n\n\n\n## [3. Licença do Conteúdo do Utilizador](#3)\n\nAs contribuições dos utilizadores estão licenciadas sob a [Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US). Sem qualquer limitação de qualquer dessas representações ou garantias, %{company_name} tem o direito (embora não tenha a obrigação) de, em critério exclusivo de %{company_name} (i) recusar ou remover qualquer conteúdo que, na sensata opinião de %{company_name} viola qualquer política de %{company_name} ou é de alguma maneira prejudicial ou censurável, ou (ii) termina ou nega acesso a, e usa o Sítio para qualquer individuo ou entidade por alguma razão, em critério exclusivo de %{company_name}. %{company_name} não terá obrigação de fornecer uma compensação de quaisquer montantes previamente pagos.\n\n\n\n## [4. Pagamento e Renovação](#4)\n\n### Termos Gerais\n\nAtualizações ou serviços opcionais pagos podem estar disponíveis no Sítio. Ao utilizar uma atualização ou serviço opcional pago, está a concordar em pagar a %{company_name} as taxas de subscrição mensais ou anuais indicadas. Os pagamentos irão ser cobrados numa base pré-paga no dia em que utilizar o serviço ou a atualização e irá cobrir o uso desse serviço ou atualização por um período de subscrição mensal ou anual\ + \ tal como indicado. Estas taxas não são reembolsáveis.\n\n### Renovação Automática\n\nA não ser que notifique %{company_name} antes do final do período da subscrição que quer cancelar, a sua subscrição irá ser automaticamente renovada e você autoriza-nos a colecionar as taxas anuais ou mensais (assim como outras taxas) usando qualquer cartão de crédito ou outro mecanismo de pagamento que tivermos no seu registo. As subscrições podem ser canceladas em qualquer altura.\n\n\n\n## [5. Serviços](#5)\n\n### Serviços de Alojamento, Suporte\n\nServiços opcionais de Alojamento e Suporte pode ser fornecido por %{company_name} sob os termos e condições para cada serviço. Ao inscrever-se numa conta com serviços de Alojamento ou Suporte, irá concordar e agir sob tais termos e condições.\n\n\n\n## [6. Responsabilidade dos Visitantes do Sítio](#6)\n\n%{company_name} não reviu, nem pode rever, todo o material, incluindo software de computador, publicado no Sítio, e não pode portanto ser responsável pelo conteúdo do material, pelo seu uso ou efeitos. Ao operar o Sítio, %{company_name} não representa ou implica que corrobora com o material publicado, ou que acredita que o material seja preciso, útil e não prejudicial. Você será responsável por tomar precauções necessárias para proteger-se a si próprio e o seu sistema computacional de vírus, minhocas, cavalos de Tróia e outros conteúdos destrutivos. O Sítio\ + \ pode ter conteúdo ofensivo, indecente assim como também imprecisões técnicas, erros tipográficos entre outros erros. O Sítio pode também ter material que viola os direitos de privacidade e publicidade, ou que infringe a propriedade inteletual e outros direitos do proprietário, de terceiras partes, ou o descarregamento, cópia ou uso que é sujeito a termos e condições adicionais, estabelecido ou não.\n\n%{company_name} está isenta de qualquer responsabilidade de qualquer dano resultante do uso por parte dos visitantes do Sítio, ou de qualquer descarregamento feito por esses visitantes do conteúdo lá publicado.\n\n\n\n## [7. Conteúdo Publicado Noutros Sítios](#7)\n\nNão revimos nem podemos rever todo o material, incluindo software de computador, disponibilizado através de sítios e páginas ao qual contem hiperligações %{company_domain} e que se ligam a %{company_domain}. %{company_name} não tem qualquer controlo dos sítios e páginas não-%{company_domain}, e não é responsável pelo seu conteúdo ou o seu uso.Ao carregar num sítio ou página para um sítio ou página não--%{company_domain}, %{company_name} não representa ou implica que corrobora com tais sítios ou páginas. Você é responsável por tomar ações necessárias para proteger-se a si próprio ou o seu sistema computacional de vírus, minhocas, cavalos de Tróia e outros conteúdos destrutivos. %{company_name} renuncia qualquer responsabilidade por qualquer dano resultante\ + \ do uso de sítios e páginas não-%{company_domain}.\n\n\n\n## [8. Violação de Direitos de Autor e Política](#8)\n\nAssim como %{company_name} pede aos outros para respeitarem os direitos de propriedade inteletual, a mesma respeita os direitos de propriedade inteletual dos outros. Se acredita que material localizado em ou com hiperligação para, por %{company_domain} viola os seus direitos de autor, e se este sítio reside nos EUA, encorajamo-lo a notificar a %{company_name} em concordância com a Política (“DMCA”) [Digital Millennium Copyright Act](http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act) da %{company_name}.\n%{company_name} irá responder por tais notícias, incluindo tal como requerido ou apropriado por remover o material ilícito ou desativando as hiperligações para o material ilícito.\n%{company_name} irá terminar o acesso e o uso do Sítio se, sob circunstâncias apropriadas, o visitante é determinado como sendo um infrator repetido dos direitos de autor e de outros direitos de propriedade inteletual de %{company_name} ou outros. No caso de tal terminação, %{company_name} não terá qualquer obrigação de fornecer uma compensação de qualquer valor previamente pago a %{company_name}.\n\n\n\n## [9. Intellectual Property](#9)\n\nEste Acordo não transfere totalmente de %{company_name} para si qualquer %{company_name} ou propriedade inteletual de terceira parte, e todos os direitos,\ + \ títulos e interesse na e para a propriedade remanescente (entre as partes) apenas com %{company_name}. %{company_name}, %{company_domain}, o logótipo %{company_domain} e todos as outras marcas, marcas de serviço, gráficos, logótipos usados na ligação com %{company_domain}, ou o Sítio é marca registada ou de %{company_name} ou licenciadores de %{company_name}. Outras marcas, marcas de serviço, gráficos e logótipos usados na ligação com o Sítio podem ser marcas de terceiras partes. O seu uso do Sítio não lhe concede direitos ou licença para reproduzir ou de outra forma usar qualquer %{company_name} ou marcas de terceiras partes.\n\n\n\n## [10. Anúncios](#10)\n\n%{company_name} reserva-se ao direito de mostrar anúncios no seu conteúdo a não ser que tenha comprado a Atualização Ad-free ou uma conta de Serviços.\n\n\n\n## [11. Atribuição](#11)\n\n%{company_name} reserva-se ao direito de mostrar hiperligações de atribuição tais como ‘Patrocinado por %{company_domain},’ e atribuição de fontes ao conteúdo do seu rodapé ou cabeçalho. Os créditos do rodapé e a ferramenta de %{company_domain} não poderão ser removidas independentemente das atualizações compradas.\n\n\n\n## [12. Mudanças](#12)\n\n%{company_name} reserva-se ao direito, por critério exclusivo, de modificar ou substituir qualquer parte deste Acordo. É sua responsabilidade verificar periodicamente o Acordo e verificar\ + \ as suas mudanças. O seu uso ou acesso contínuo ao Sítio seguido da publicação de qualquer mudança a este Acordo constitui aceitação a essas mudanças. %{company_name} pode também, no futuro, oferecer novos serviços e/ou funcionalidades através do Sítio (incluindo, o lançamento de novas ferramentas e recursos). Estas novas funcionalidades e/ou serviços devem ser sujeitos aos termos e condições deste Acordo.\n\n\n\n## [13. Terminação](#13)\n\n%{company_name} pode terminar o seu acesso a todas ou alguma parte do Sítio em qualquer altura, com ou sem motivo, com ou sem aviso, com efeitos imediatos. Se desejar terminar este Acordo ou a sua conta %{company_domain} (se tem uma), pode simplesmente descontinuar usando o Sítio. Todas as provisões para este Acordo que pela sua natureza deverão sobreviver à terminação, incluindo, sem limitações, provisões de propriedade, isenções de garantia, indemnizações e limitações de responsabilidade.\n\n\n\n## [14. Isenção de Garantias](#14)\n\nO Sítio é fornecido “tal como está”. %{company_name} e os seus fornecedores e licenciadores ficam aqui isentos de garantias de qualquer tipo, expressas ou implícitas, sem limitação, as garantias de comerciabilidade, adequadas a uma finalidade específica. Nem %{company_name} nem os seus fornecedores e licenciadores, fazem garantias de que o Sítio está livre de erros ou que o acesso seja contínuo e ininterrupto. Se está\ + \ a ler isto, aqui está [uma ajuda] (http://www.newyorker.com/online/blogs/shouts/2012/12/the-hundred-best-lists-of-all-time.html). Compreende que descarregou de, ou de outra maneira obteve conteúdo ou serviços através do Sítio a seu próprio risco e descrição.\n\n\n\n## [15. Limitação da Responsabilidade](#15)\n\nEm nenhum evento %{company_name} ou os seus fornecedores ou licenciadores, irão ser responsáveis em respeito a qualquer assunto deste acordo sob qualquer contrato, negligência, estrita responsabilidade ou outras teorias legais para: (i) quaisquer danos especiais, de incidentes ou consequenciais; (ii) o custo de procura de produtos ou serviços substitutos; (iii) por interrupção do uso, perda ou corrupção dos dados; ou (iv) por quaisquer valores que excedem as taxas pagas por si a %{company_name} sob este acordo durante um período doze (12) meses anterior à causa da ação. %{company_name} não deverá ter qualquer responsabilidade de qualquer falha ou atraso devido a matérias para além do seu controlo razoável. O disposto neste artigo não se aplica nas medidas proibidas pela lei aplicável.\n\n\n\n## [16. Representações Gerais e Garantia](#16)\n\nVocê representa e garante que (i) o seu uso do Sítio irá estar em estrita concordância com a [Política de Privacidade](/privacy) e das [Diretrizes da Comunidade](/guidelines) da %{company_name}, com este Acordo e com as leis e regulações aplicáveis\ + \ (incluindo sem limitação de quaisquer leis ou regulações locais no seu país, estado, cidade ou outras áreas governamentais, relativamente à conduta online e ao conteúdo aceitável, e incluindo quaisquer leis aplicáveis relativamente à transmissão de dados técnicos exportados de qualquer país em que o sítio reside ou do país em que você reside) e (ii) o seu uso do Sítio não irá infringir os direitos de propriedade inteletual de qualquer terceira parte.\n\n\n\n## [17. Indemnização](#17)\n\nVocê concorda em indemnizar e não prejudicar %{company_name}, os seus contratados, e os seus licenciadores, os seus respetivos diretores, empregados e agentes de e contra quaisquer reinvidicações e despesas, incluindo taxas de advogados, decorrentes do seu uso do Sítio, incluindo mas não limitado à sua violação deste Acordo.\n\n\n\n## [18. Diversos](#18)\n\nEste Acordo constitui o acordo integral entre %{company_name} e você em relação ao assunto em questão, e poderá ser modificado apenas por uma emenda escrita assinada por um executivo autorizado da %{company_name}, ou pela publicação por parte da %{company_name}, de uma versão revista. Excepto o definido na lei, se houver, disposição em contrário, o presente Acordo, qualquer acesso ou uso do sítio irá ser governado pelas leis do estado da Califórnia, E.U.A, excluindo aos conflitos das disposições da lei, e a receita própria por qualquer disputa emergente\ + \ de ou relativa a qualquer do mesmo irá ser o tribunal federal e estatal localizado em São Francisco, California. \n\nExceto para os pedidos de medida cautelar ou equitativa ou reclamações relativas aos direitos de propriedade inteletual (que podem ser introduzidos em qualquer tribunal competente, sem a prestação de uma caução), qualquer litígio decorrente do presente acordo serão resolvidos de acordo com as Regras de Arbitragem de Judicial Arbitration and Mediation Service, Inc.” (“JAMS”) por três árbitros apontados em concordância com tais Regras. A arbitragem deverá ter lugar em São Francisco, Califórnia, em língua Inglesa e a decisão arbitral pode ser forçada em qualquer tribunal. A parte prevalecente em qualquer ação ou processo para executar este Acordo terá direito a custos e honorários dos advogados. Se qualquer parte deste Acordo for considerada inválida ou inexequível, essa parte será interpretada de forma a refletir a intenção original das partes e as porções restantes permanecerão em pleno vigor e efeito. A renúncia por qualquer das partes de qualquer termo ou condição deste Acordo ou qualquer violação dos mesmos, em qualquer instância, não vai renunciar a tal termo ou condição ou qualquer violação subsequente. Pode atribuir os seus direitos sob este acordo a qualquer parte que consente a, e concorda em ficar vinculado pelos seus termos e condições; %{company_name} pode assignar os seus direitos sob este Acordo\ + \ sem condição. O presente Acordo será vinculativo e reverterá em benefício das partes e dos seus sucessores.\n\nEste documento é CC-BY-SA. Foi atualizado pela última vez em 31 de Maio de 2013.\n\nOriginalmente adaptado de [Termos de Serviço WordPress](http://en.wordpress.com/tos/).\n\n" + privacy_topic: + title: "Política de Privacidade" + body: | + + + ## [Que informações reunimos?](#reunião) + + Reunimos informação sobre si assim que se regista no nosso sítio e juntamos dados quando participa no fórum ao ler, escrever e avaliar o conteúdo aí partilhado. + + Ao inscrever-se no nosso sítio, pode ser-lhe pedido para inserir o seu nome e endereço de email. Pode, contudo, visitar o nosso sítio sem se registar. O seu endereço de email irá ser verificado por um email contendo uma hiperligação única. Se essa hiperligação for visitada, sabemos que tem controlo do seu endereço de email. + + Ao inscrever-se e publicar, recordamos o endereço IP de onde a publicação tem origem. Podemos também reter logs de servidores que incluem endereços IP de todos os pedidos ao servidor. + + + + ## [Para que usamos as suas informações?](#usar) + + Todas as informações que reunimos sobre si podem ser usadas numa das seguintes maneiras: + + * Para personalizar a sua experiência — a sua informação ajuda-nos a responder melhor às suas necessidades individuais. + * Para melhorar o nosso sítio — continuamos a lutar por melhorar as ofertas do nosso sítio com base na informação e feedback que recebemos de si. + * Para melhorar o serviço ao cliente — a sua informação ajuda-nos a responder mais eficazmente aos pedidos de serviço ao cliente e pedidos de suporte. + *Para enviar emais periodicamente — O endereço de email que nos fornece pode ser usado para lhe enviar informação, notificações que peça sobre mudanças em tópicos ou em resposta ao seu nome de utilizador, respostas a inquéritos e/ou outros pedidos ou questões. + + + + ## [Como protegemos a sua informação?](#protect) + + Implementámos uma variedade de medidas de precaução para manter as suas informações pessoais em segurança quando insere, submete ou acede à sua informação pessoal. + + + + ## [Qual é a sua política de retenção de dados?](#retenção-dados) + + Iremos fazer um esforço de boa fé para: + + * Reter logs de servidores contendo o endereço IP de todos os pedidos para este servidor não mais que 90 dias. + * Reter endereços IP associados a utilizadores registados e às suas publicações não mais que 5 anos. + + + + ## [Usamos cookies?](#cookies) + + Sim. Os cookies são pequenos ficheiros que um sítio ou um fornecedor de serviços transfere para o disco do seu computador através do navegador de Internet (se assim o permitir). Estes cookies permitem que o sítio reconheça o seu navegador e, se tiver uma conta registada, associá-lo a essa. + + Usamos cookies para entender e guardar as suas preferências para visitas futuras e compilar dados agregados sobre o tráfego do sítio e a interação com o mesmo para lhe podermos oferecer as melhores experiências e ferramentas no futuro. Podemos contratar fornecedores de serviços para nos ajudarem a entender melhor os visitantes do nosso sítio. Estes fornecedores de serviços não têm permissão para usar a informação reunida em nosso nome excepto para nos ajudar a conduzir e melhorar o nosso negócio. + + + + ## [Divulgamos informação com partes exteriores?](#divulgar) + + Não vendemos, trocamos ou de outra maneira transferimos a sua informação pessoal a partes exteriores. Isto não inclui terceiras partes confiáveis que nos ajudam a operar o nosso sítio, conduzir o nosso negócio, ou servi-lo, enquanto estas partes acordarem em manter a informação confidencial. Podemos também partilhar a sua informação quando acreditamos que é apropriado para estar de acordo com a lei, reforçar as políticas do nosso sítio ou proteger os nossos, ou de outros, direitos, propriedades ou segurança. Contudo, informação não-pessoal do visitante pode ser fornecida a outras partes para marketing, publicidade ou outros usos. + + + + ## [Hiperligações de Terceiras Partes](#terceira-parte) + + Ocasionalmente, à nossa descrição, podemos incluir ou oferecer serviços ou produtos de terceiros no nosso sítio. Estes sítios de terceiros têm políticas de privacidade separadas e independentes. Por este motivo, não temos responsabilidade no conteúdo ou atividades destes sítios hiperligados. No entanto, procuramos proteger a integridade do nosso sítio e damos as boas-vindas a qualquer feedback sobre estes sítios. + + + + ## [Cumprimento da Proteção da Privacidade Online Infantil](#coppa) + + O nosso sítio, produtos e serviços são direcionados a pessoas com pelo menos 13 anos de idade. Se este servidor está nos EUA e tem menos de 13 anos de idade, como parte dos requisitos do COPPA ([Proteção da Privacidade Online Infantil](http://en.wikipedia.org/wiki/Children)), não utilize este sítio. + + + + ## [Política de Privacidade Online](#online) + + Estas políticas de privacidade online aplicam-se apenas à informação reunida através do nosso sítio e não à informação reunida offline. + + + + ## [O seu consentimento](#consentimento) + + Ao utilizar o nosso sítio, está a consentir a política de privacidade do nosso sítio. + + + + ## [Mudanças à nossa Política de Privacidade](#mudanças) + + Se decidirmos alterar a nossa política de privacidade, iremos publicar as mudanças nesta página. + + Este documento é CC-BY-SA. Foi atualizado pela última vez em 31 de Maio de 2013. + static: + search_help: | +

    Dicas

    +

    +

      +
    • A correspondência de títulos está priorizada, por isso em caso de dúvida, pesquise por títulos
    • +
    • Palavras únicas e incomuns irão sempre produzir melhores resultados
    • +
    • Sempre que possível, direccione a sua pesquisa para um tópico, utilizador ou categoria em particular
    • +
    +

    +

    Opções

    +

    +

    - {{#if canBulkSelect}} - - {{/if}} -
    + + + + +
    encomenda:visualizaçõesencomenda:última
    estado:abertoestado:fechado + estado:arquivadoestado:semrespostas + estado:utilizadorúnico
    categoria:fooutilizador:foo
    em:gostosem:publicadoem:vigilância + em:acompanhamentoem:privado
    +

    +

    + categoria arco-íris:parques estado:aberto encomenda:última irá retornar uma pesquisa por tópicos que contém a palavra "arco-íris" na categoria "parques" que não estão fechados ou arquivados e que estão ordenados pela data da última mensagem. diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index f654dacc8d..0c0ff72f46 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -39,7 +39,6 @@ pt_BR: messages: too_long_validation: "está limitado para %{max} caracteres; você entrou com %{length}." invalid_boolean: "Boleano inválido." - taken: "já foi tomado" embed: load_from_remote: "Houve um erro ao carregar essa mensagem." bulk_invite: @@ -1031,11 +1030,6 @@ pt_BR: text_body_template: "Seus convites foram enviados com sucesso: %{sent}." bulk_invite_failed: subject_template: "Seus convites não forma enviados, contate os moderadores." - csv_export_succeeded: - subject_template: "Dados foram exportados com sucesso" - csv_export_failed: - subject_template: "Exporte Falhou" - text_body_template: "O export falhou, verifique os logs" email_reject_trust_level: subject_template: "Mensagem rejeitada" text_body_template: | diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index 19fa3cf263..e7a6c6e153 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -28,7 +28,6 @@ sv: messages: too_long_validation: "är begränsad till %{max} karaktärer; du skrev in %{length}. " invalid_boolean: "Ogiltigt boolean." - taken: "används redan" embed: load_from_remote: "Det uppstod ett fel vid laddning av artikeln." bulk_invite: @@ -450,6 +449,13 @@ sv: faq_url: "Om du har en FAQ någon annanstans som du vill använda, skriv den fullständiga URL:en här." privacy_policy_url: "Om du har ett dokument med en integritetspolicy någon annanstans som du vill använda, skriv den fullständiga URL:en här." default_digest_email_frequency: "Hur ofta användare får emailutskick som standard. De kan ändra detta val under sina inställningar." + errors: + invalid_email: "Felaktig e-postadress." + invalid_username: "Det finns ingen användare med detta användarnamn." + invalid_integer_min_max: "Värdet måste vara mellan %{min} och %{max}." + invalid_integer_min: "Värdet måste vara %{max} eller större." + invalid_integer_max: "Värdet får inte vara större än %{max}." + invalid_integer: "Värdet måste vara ett heltal." notification_types: mentioned: "%{display_username} mentioned you in %{link}" liked: "%{display_username} liked your post in %{link}" @@ -512,6 +518,10 @@ sv: subject_template: "Välkommen till %{site_name}!" welcome_invite: subject_template: "Välkommen till %{site_name}!" + restore_succeeded: + text_body_template: "Återställningen lyckades." + restore_failed: + subject_template: "Återställningen misslyckades" too_many_spam_flags: subject_template: "Nytt konto blockerat" blocked_by_staff: @@ -600,6 +610,8 @@ sv: signup: subject_template: "[%{site_name}] Activate your new account" 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: + see_more: "Mer" deleted: 'raderad' upload: pasted_image_filename: "Inklistrad bild" diff --git a/plugins/poll/config/locales/server.pt.yml b/plugins/poll/config/locales/server.pt.yml index 397cf5ed8c..61af621360 100644 --- a/plugins/poll/config/locales/server.pt.yml +++ b/plugins/poll/config/locales/server.pt.yml @@ -12,7 +12,7 @@ pt: poll_options: "Opções da votação" poll: must_contain_poll_options: "tem que conter uma lista de opções de votação" - cannot_have_modified_options: "não podem ser modificadas depois dos primeiros cinco minutos. Contacte um moderador se precisar de as alterar." - cannot_add_or_remove_options: "apenas podem ser editadas, não adicionadas ou removidas. Se precisar de adicionar ou remover opções, deverá bloquear este tópico e criar um novo." + cannot_have_modified_options: "não podem ser modificadas depois dos primeiros cinco minutos. Contacte um moderador se precisar de alterá-las." + cannot_add_or_remove_options: "podem apenas ser editadas, não podendo ser adicionadas ou removidas. Se precisar de adicionar ou remover opções, deverá bloquear este tópico e criar um novo." prefix: "Votação" closed_prefix: "Votação encerrada" diff --git a/plugins/poll/config/locales/server.zh_CN.yml b/plugins/poll/config/locales/server.zh_CN.yml index 876dee963e..64e2a1ea24 100644 --- a/plugins/poll/config/locales/server.zh_CN.yml +++ b/plugins/poll/config/locales/server.zh_CN.yml @@ -11,8 +11,8 @@ zh_CN: post: poll_options: "投票选项" poll: - must_contain_poll_options: "必须包含投票选项列表" - cannot_have_modified_options: "在开始的五分钟后不能修改。如果需要修改他们,请联系版主。" - cannot_add_or_remove_options: "只能被编辑,不能添加或者删除。如果您需要添加或者删除选项,您需要锁定这个主题并创建新的。" + must_contain_poll_options: "必须包含投票选项" + cannot_have_modified_options: "在开始的五分钟后不能修改。如果需要修改他们,请联系一位版主。" + cannot_add_or_remove_options: "只能被编辑,不能添加或者删除。如果您需要添加或者删除选项,你需要锁定这个投票并创建新的投票。" prefix: "投票" closed_prefix: "已关闭的投票:" diff --git a/public/403.pt.html b/public/403.pt.html index 0467920ab5..674df0279a 100644 --- a/public/403.pt.html +++ b/public/403.pt.html @@ -18,9 +18,9 @@

    403

    -

    Não pode aceder a esse recurso!

    +

    Não pode ver esse recurso!

    -

    Isto será substituído por uma página 403 personalizada do Discourse

    +

    Isto será substituído por uma página 403 personalizada do Discourse.

    diff --git a/public/422.pt.html b/public/422.pt.html index 903ec6cb30..c50064b60d 100644 --- a/public/422.pt.html +++ b/public/422.pt.html @@ -1,6 +1,6 @@ -A alteração que você solicitou foi rejeitada (422) +A alteração que solicitou foi rejeitada (422) <%- unless customization_disabled? %> <%= SiteCustomization.custom_header(session[:preview_style]) %> <%- end %> -
    -
    -
    -
    -
    - -
    -
    -
    -
    +
    +
    - <%= yield %> -