diff --git a/.tx/config b/.tx/config index bba3633be7..9dfdf375f1 100644 --- a/.tx/config +++ b/.tx/config @@ -1,6 +1,6 @@ [main] host = https://www.transifex.com -lang_map = es_ES: es, fr_FR: fr, ko_KR: ko, pt_PT: pt +lang_map = es_ES: es, fr_FR: fr, ko_KR: ko, pt_PT: pt, sk_SK: sk [discourse-org.clientenyml] file_filter = config/locales/client..yml diff --git a/Gemfile b/Gemfile index 2ac6a1402f..b131b3d791 100644 --- a/Gemfile +++ b/Gemfile @@ -83,7 +83,9 @@ gem 'omniauth-twitter' gem 'omniauth-github-discourse', require: 'omniauth-github' gem 'omniauth-oauth2', require: false -gem 'omniauth-google-oauth2' + +# this removes the dependency on 'addressable' +gem 'omniauth-google-oauth2', git: 'git://github.com/zquestz/omniauth-google-oauth2.git', ref: 'b492c4bb8286d35' gem 'oj' gem 'pg' gem 'pry-rails', require: false @@ -183,7 +185,7 @@ begin gem 'stackprof', require: false, platform: [:mri_21, :mri_22, :mri_23] gem 'memory_profiler', require: false, platform: [:mri_21, :mri_22, :mri_23] rescue Bundler::GemfileError - begin + begin STDERR.puts "You are running an old version of bundler, please upgrade bundler ASAP, if you are using Discourse docker, rebuild your container." gem 'stackprof', require: false, platform: [:mri_21, :mri_22] gem 'memory_profiler', require: false, platform: [:mri_21, :mri_22] diff --git a/Gemfile.lock b/Gemfile.lock index 673f9a3b95..33f6f37731 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,14 @@ +GIT + remote: git://github.com/zquestz/omniauth-google-oauth2.git + revision: b492c4bb8286d35ae1168d7f2e5c57769bfe45a0 + ref: b492c4bb8286d35 + specs: + omniauth-google-oauth2 (0.3.0) + jwt (~> 1.0) + multi_json (~> 1.3) + omniauth (>= 1.1.1) + omniauth-oauth2 (>= 1.3.1) + GEM remote: https://rubygems.org/ specs: @@ -38,17 +49,17 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - annotate (2.6.10) - activerecord (>= 3.2, <= 4.3) + annotate (2.7.0) + activerecord (>= 3.2, < 6.0) rake (~> 10.4) arel (6.0.3) - aws-sdk (2.1.29) - aws-sdk-resources (= 2.1.29) - aws-sdk-core (2.1.29) + aws-sdk (2.2.9) + aws-sdk-resources (= 2.2.9) + aws-sdk-core (2.2.9) jmespath (~> 1.0) - aws-sdk-resources (2.1.29) - aws-sdk-core (= 2.1.29) - babel-source (5.8.19) + aws-sdk-resources (2.2.9) + aws-sdk-core (= 2.2.9) + babel-source (5.8.34) babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) execjs (~> 2.0) @@ -62,7 +73,7 @@ GEM binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.2) - byebug (6.0.2) + byebug (8.2.1) certified (1.0.0) coderay (1.1.0) concurrent-ruby (1.0.0) @@ -94,10 +105,10 @@ GEM eventmachine (1.0.8) excon (0.45.4) execjs (2.6.0) - exifr (1.2.3.1) + exifr (1.2.4) fabrication (2.9.8) fakeweb (1.3.0) - faraday (0.9.1) + faraday (0.9.2) multipart-post (>= 1.2, < 3) fast_blank (1.0.0) fast_stack (0.1.0) @@ -115,15 +126,15 @@ GEM thor (~> 0.19.1) fspath (2.1.1) gctools (0.2.3) - given_core (3.5.4) + given_core (3.7.1) sorcerer (>= 0.3.7) globalid (0.3.6) activesupport (>= 4.1.0) guess_html_encoding (0.0.11) - hashie (3.4.2) - highline (1.7.7) + hashie (3.4.3) + highline (1.7.8) hike (1.2.3) - hiredis (0.6.0) + hiredis (0.6.1) htmlentities (4.3.4) http-cookie (1.0.2) domain_name (~> 0.5) @@ -142,12 +153,12 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) - jwt (1.5.1) + jwt (1.5.2) kgio (2.10.0) librarian (0.1.2) highline thor (~> 0.15) - libv8 (3.16.14.11) + libv8 (3.16.14.13) listen (0.7.3) logster (1.0.1) loofah (2.0.3) @@ -155,7 +166,7 @@ GEM lru_redux (1.1.0) mail (2.6.3) mime-types (>= 1.16, < 3) - memory_profiler (0.9.4) + memory_profiler (0.9.6) message_bus (2.0.0.beta.2) rack (>= 1.1.3) redis @@ -166,9 +177,9 @@ GEM minitest (5.8.3) mocha (1.1.0) metaclass (~> 0.0.1) - mock_redis (0.15.2) + mock_redis (0.15.4) moneta (0.8.0) - msgpack (0.6.2) + msgpack (0.7.4) multi_json (1.11.2) multi_xml (0.5.5) multipart-post (2.0.0) @@ -176,7 +187,7 @@ GEM netrc (0.11.0) nokogiri (1.6.7.1) mini_portile2 (~> 2.0.0.rc2) - nokogumbo (1.4.1) + nokogumbo (1.4.7) nokogiri oauth (0.4.7) oauth2 (1.0.0) @@ -185,18 +196,15 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) - oj (2.12.14) - omniauth (1.2.2) + oj (2.14.3) + omniauth (1.3.1) hashie (>= 1.2, < 4) - rack (~> 1.0) - omniauth-facebook (2.0.1) + rack (>= 1.0, < 3) + omniauth-facebook (3.0.0) omniauth-oauth2 (~> 1.2) omniauth-github-discourse (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-google-oauth2 (0.2.5) - omniauth (> 1.0) - omniauth-oauth2 (~> 1.1) omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) @@ -209,7 +217,7 @@ GEM omniauth-twitter (1.2.1) json (~> 1.3) omniauth-oauth (~> 1.1) - onebox (1.5.31) + onebox (1.5.33) moneta (~> 0.8) multi_json (~> 1.11) mustache @@ -217,9 +225,9 @@ GEM openid-redis-store (0.0.2) redis ruby-openid - pg (0.18.3) - progress (3.1.0) - pry (0.10.1) + pg (0.18.4) + progress (3.1.1) + pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) @@ -227,10 +235,10 @@ GEM pry (>= 0.9.10, < 0.11.0) pry-rails (0.3.4) pry (>= 0.9.10) - puma (2.14.0) - r2 (0.2.5) + puma (2.15.3) + r2 (0.2.6) rack (1.6.4) - rack-mini-profiler (0.9.7) + rack-mini-profiler (0.9.8) rack (>= 1.1.3) rack-openid (1.3.1) rack (>= 1.1.0) @@ -270,7 +278,7 @@ GEM rake (10.4.2) rake-compiler (0.9.5) rake - rb-fsevent (0.9.6) + rb-fsevent (0.9.7) rb-inotify (0.9.5) ffi (>= 0.5.0) rbtrace (0.4.7) @@ -296,16 +304,16 @@ GEM rspec-expectations (3.2.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.2.0) - rspec-given (3.5.4) - given_core (= 3.5.4) - rspec (>= 2.12) + rspec-given (3.7.1) + given_core (= 3.7.1) + rspec (>= 2.14.0) rspec-html-matchers (0.7.0) nokogiri (~> 1) rspec (~> 3) rspec-mocks (3.2.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.2.0) - rspec-rails (3.2.1) + rspec-rails (3.2.3) actionpack (>= 3.0, < 4.3) activesupport (>= 3.0, < 4.3) railties (>= 3.0, < 4.3) @@ -319,10 +327,10 @@ GEM ruby-readability (0.7.0) guess_html_encoding (>= 0.0.4) nokogiri (>= 1.6.0) - sanitize (4.0.0) + sanitize (4.0.1) crass (~> 1.0.2) nokogiri (>= 1.4.4) - nokogumbo (= 1.4.1) + nokogumbo (~> 1.4.1) sass (3.2.19) sass-rails (4.0.5) railties (>= 4.0.0, < 5.0) @@ -336,17 +344,16 @@ GEM shoulda-context (~> 1.0, >= 1.0.1) shoulda-matchers (>= 1.4.1, < 3.0) shoulda-context (1.2.1) - shoulda-matchers (2.7.0) + shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - sidekiq (4.0.1) + sidekiq (4.0.2) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) - json (~> 1.0) redis (~> 3.2, >= 3.2.1) sidekiq-statistic (1.2.0) sidekiq (>= 3.3.4, < 5) simple-rss (1.3.1) - simplecov (0.10.0) + simplecov (0.11.1) docile (~> 1.1.0) json (~> 1.8) simplecov-html (~> 0.10.0) @@ -382,7 +389,7 @@ GEM thread_safe (0.3.5) tilt (1.4.1) timecop (0.8.0) - trollop (2.1.1) + trollop (2.1.2) tzinfo (1.2.2) thread_safe (~> 0.1) uglifier (2.7.2) @@ -390,8 +397,8 @@ GEM json (>= 1.8.0) unf (0.1.4) unf_ext - unf_ext (0.0.6) - unicorn (4.9.0) + unf_ext (0.0.7.1) + unicorn (5.0.1) kgio (~> 2.6) rack raindrops (~> 0.7) @@ -445,7 +452,7 @@ DEPENDENCIES omniauth omniauth-facebook omniauth-github-discourse - omniauth-google-oauth2 + omniauth-google-oauth2! omniauth-oauth2 omniauth-openid omniauth-twitter diff --git a/README.md b/README.md index 6834114857..02ff312d2e 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Plus *lots* of Ruby Gems, a complete list of which is at [/master/Gemfile](https ## Contributing -[![Build Status](https://travis-ci.org/discourse/discourse.svg)](https://travis-ci.org/discourse/discourse) +[![Build Status](https://api.travis-ci.org/discourse/discourse.svg?branch=master)](https://travis-ci.org/discourse/discourse) [![Code Climate](https://codeclimate.com/github/discourse/discourse.svg)](https://codeclimate.com/github/discourse/discourse) Discourse is **100% free** and **open source**. We encourage and support an active, healthy community that diff --git a/app/assets/javascripts/admin/controllers/admin-email-all.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-all.js.es6 deleted file mode 100644 index 5f456c108f..0000000000 --- a/app/assets/javascripts/admin/controllers/admin-email-all.js.es6 +++ /dev/null @@ -1,3 +0,0 @@ -import AdminEmailSkippedController from "admin/controllers/admin-email-skipped"; - -export default AdminEmailSkippedController.extend(); diff --git a/app/assets/javascripts/admin/controllers/admin-email-incomings.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-incomings.js.es6 new file mode 100644 index 0000000000..deb7f0771c --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-email-incomings.js.es6 @@ -0,0 +1,11 @@ +import IncomingEmail from 'admin/models/incoming-email'; + +export default Ember.Controller.extend({ + loadMore() { + return IncomingEmail.findAll(this.get("filter"), this.get("model.length")) + .then(incoming => { + if (incoming.length < 50) { this.get("model").set("allLoaded", true); } + this.get("model").addObjects(incoming); + }); + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-email-logs.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-logs.js.es6 new file mode 100644 index 0000000000..2506a0cd99 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-email-logs.js.es6 @@ -0,0 +1,11 @@ +import EmailLog from 'admin/models/email-log'; + +export default Ember.Controller.extend({ + loadMore() { + return EmailLog.findAll(this.get("filter"), this.get("model.length")) + .then(logs => { + if (logs.length < 50) { this.get("model").set("allLoaded", true); } + this.get("model").addObjects(logs); + }); + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-email-received.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-received.js.es6 new file mode 100644 index 0000000000..69ebd5e4c4 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-email-received.js.es6 @@ -0,0 +1,9 @@ +import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings'; +import debounce from 'discourse/lib/debounce'; +import IncomingEmail from 'admin/models/incoming-email'; + +export default AdminEmailIncomingsController.extend({ + filterIncomingEmails: debounce(function() { + IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings)); + }, 250).observes("filter.{from,to,subject}") +}); diff --git a/app/assets/javascripts/admin/controllers/admin-email-rejected.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-rejected.js.es6 new file mode 100644 index 0000000000..317a669cd0 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-email-rejected.js.es6 @@ -0,0 +1,9 @@ +import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings'; +import debounce from 'discourse/lib/debounce'; +import IncomingEmail from 'admin/models/incoming-email'; + +export default AdminEmailIncomingsController.extend({ + filterIncomingEmails: debounce(function() { + IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings)); + }, 250).observes("filter.{from,to,subject,error}") +}); diff --git a/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 index 6ea640672c..d73d640adc 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 @@ -1,12 +1,9 @@ +import AdminEmailLogsController from 'admin/controllers/admin-email-logs'; import debounce from 'discourse/lib/debounce'; import EmailLog from 'admin/models/email-log'; -export default Ember.Controller.extend({ - +export default AdminEmailLogsController.extend({ filterEmailLogs: debounce(function() { - var self = this; - EmailLog.findAll(this.get("filter")).then(function(logs) { - self.set("model", logs); - }); - }, 250).observes("filter.user", "filter.address", "filter.type", "filter.reply_key") + EmailLog.findAll(this.get("filter")).then(logs => this.set("model", logs)); + }, 250).observes("filter.{user,address,type,reply_key}") }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 index b392ea8e98..ae75d18715 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 @@ -1,8 +1,9 @@ +import AdminEmailLogsController from 'admin/controllers/admin-email-logs'; import debounce from 'discourse/lib/debounce'; +import EmailLog from 'admin/models/email-log'; -export default Ember.Controller.extend({ +export default AdminEmailLogsController.extend({ filterEmailLogs: debounce(function() { - const EmailLog = require('admin/models/email-log').default; EmailLog.findAll(this.get("filter")).then(logs => this.set("model", logs)); - }, 250).observes("filter.user", "filter.address", "filter.type", "filter.skipped_reason") + }, 250).observes("filter.{user,address,type,skipped_reason}") }); diff --git a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 index d11e67305a..bdafff9f0a 100644 --- a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 @@ -1,5 +1,6 @@ import { exportEntity } from 'discourse/lib/export-csv'; import { outputExportResult } from 'discourse/lib/export-result'; +import Report from 'admin/models/report'; export default Ember.Controller.extend({ viewMode: 'table', @@ -20,9 +21,9 @@ export default Ember.Controller.extend({ var q; this.set("refreshing", true); if (this.get('categoryId') === "all") { - q = Discourse.Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate")); + q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate")); } else { - q = Discourse.Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId")); + q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId")); } q.then(m => this.set("model", m)).finally(() => this.set("refreshing", false)); }, diff --git a/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 index 74c4f79e6c..6c0f6b9ccd 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 @@ -2,15 +2,13 @@ export default Ember.Controller.extend({ needs: ['modal'], modelChanged: function(){ - - var grouping = Em.Object.extend({}); - - var model = this.get('model'); - var copy = Em.A(); + const model = this.get('model'); + const copy = Em.A(); + const store = this.store; if(model){ model.forEach(function(o){ - copy.pushObject(grouping.create(o)); + copy.pushObject(store.createRecord('badge-grouping', o)); }); } @@ -18,8 +16,8 @@ export default Ember.Controller.extend({ }.observes('model'), moveItem: function(item, delta){ - var copy = this.get('workingCopy'); - var index = copy.indexOf(item); + const copy = this.get('workingCopy'); + const index = copy.indexOf(item); if (index + delta < 0 || index + delta >= copy.length){ return; } @@ -50,14 +48,14 @@ export default Ember.Controller.extend({ item.set("editing", false); }, add: function(){ - var obj = Em.Object.create({editing: true, name: "Enter Name"}); + const obj = this.store.createRecord('badge-grouping', {editing: true, name: I18n.t('admin.badges.badge_grouping')}); this.get('workingCopy').pushObject(obj); }, saveAll: function(){ - var self = this; + const self = this; var items = this.get('workingCopy'); - var groupIds = items.map(function(i){return i.get("id") || -1;}); - var names = items.map(function(i){return i.get("name");}); + const groupIds = items.map(function(i){return i.get("id") || -1;}); + const names = items.map(function(i){return i.get("name");}); Discourse.ajax('/admin/badges/badge_groupings',{ data: {ids: groupIds, names: names}, @@ -66,14 +64,13 @@ export default Ember.Controller.extend({ items = self.get("model"); items.clear(); data.badge_groupings.forEach(function(g){ - items.pushObject(Em.Object.create(g)); + items.pushObject(self.store.createRecord('badge-grouping', g)); }); self.set('model', null); self.set('workingCopy', null); self.send('closeModal'); },function(){ - // TODO we can do better - bootbox.alert("Something went wrong"); + bootbox.alert(I18n.t('generic_error')); }); } } diff --git a/app/assets/javascripts/admin/models/admin-user.js.es6 b/app/assets/javascripts/admin/models/admin-user.js.es6 index a4341d09da..267d951e56 100644 --- a/app/assets/javascripts/admin/models/admin-user.js.es6 +++ b/app/assets/javascripts/admin/models/admin-user.js.es6 @@ -9,6 +9,8 @@ const AdminUser = Discourse.User.extend({ customGroups: Em.computed.filter("groups", (g) => !g.automatic && Group.create(g)), automaticGroups: Em.computed.filter("groups", (g) => g.automatic && Group.create(g)), + canViewProfile: Ember.computed.or("active", "staged"), + generateApiKey() { const self = this; return Discourse.ajax("/admin/users/" + this.get('id') + "/generate_api_key", { @@ -264,6 +266,7 @@ const AdminUser = Discourse.User.extend({ }, unblock() { + this.set('blockingUser', true); return Discourse.ajax('/admin/users/' + this.id + '/unblock', { type: 'PUT' }).then(function() { @@ -275,14 +278,33 @@ const AdminUser = Discourse.User.extend({ }, block() { - return Discourse.ajax('/admin/users/' + this.id + '/block', { - type: 'PUT' - }).then(function() { - window.location.reload(); - }).catch(function(e) { - var error = I18n.t('admin.user.block_failed', { error: "http: " + e.status + " - " + e.body }); - bootbox.alert(error); - }); + const user = this, + message = I18n.t("admin.user.block_confirm"); + + const performBlock = function() { + user.set('blockingUser', true); + return Discourse.ajax('/admin/users/' + user.id + '/block', { + type: 'PUT' + }).then(function() { + window.location.reload(); + }).catch(function(e) { + var error = I18n.t('admin.user.block_failed', { error: "http: " + e.status + " - " + e.body }); + bootbox.alert(error); + user.set('blockingUser', false); + }); + }; + + const buttons = [{ + "label": I18n.t("composer.cancel"), + "class": "cancel", + "link": true + }, { + "label": '' + I18n.t('admin.user.block_accept'), + "class": "btn btn-danger", + "callback": function() { performBlock(); } + }]; + + bootbox.dialog(message, buttons, { "classes": "delete-user-modal" }); }, sendActivationEmail() { diff --git a/app/assets/javascripts/admin/models/email-log.js.es6 b/app/assets/javascripts/admin/models/email-log.js.es6 index ce7d8a2420..2b19eeff4f 100644 --- a/app/assets/javascripts/admin/models/email-log.js.es6 +++ b/app/assets/javascripts/admin/models/email-log.js.es6 @@ -4,7 +4,7 @@ const EmailLog = Discourse.Model.extend({}); EmailLog.reopenClass({ - create: function(attrs) { + create(attrs) { attrs = attrs || {}; if (attrs.user) { @@ -14,16 +14,15 @@ EmailLog.reopenClass({ return this._super(attrs); }, - findAll: function(filter) { + findAll(filter, offset) { filter = filter || {}; - var status = filter.status || "all"; + offset = offset || 0; + + const status = filter.status || "sent"; filter = _.omit(filter, "status"); - return Discourse.ajax("/admin/email/" + status + ".json", { data: filter }).then(function(logs) { - return _.map(logs, function (log) { - return EmailLog.create(log); - }); - }); + return Discourse.ajax(`/admin/email/${status}.json?offset=${offset}`, { data: filter }) + .then(logs => _.map(logs, log => EmailLog.create(log))); } }); diff --git a/app/assets/javascripts/admin/models/incoming-email.js.es6 b/app/assets/javascripts/admin/models/incoming-email.js.es6 new file mode 100644 index 0000000000..677fbebbc0 --- /dev/null +++ b/app/assets/javascripts/admin/models/incoming-email.js.es6 @@ -0,0 +1,29 @@ +import AdminUser from 'admin/models/admin-user'; + +const IncomingEmail = Discourse.Model.extend({}); + +IncomingEmail.reopenClass({ + + create(attrs) { + attrs = attrs || {}; + + if (attrs.user) { + attrs.user = AdminUser.create(attrs.user); + } + + return this._super(attrs); + }, + + findAll(filter, offset) { + filter = filter || {}; + offset = offset || 0; + + const status = filter.status || "received"; + filter = _.omit(filter, "status"); + + return Discourse.ajax(`/admin/email/${status}.json?offset=${offset}`, { data: filter }) + .then(incomings => _.map(incomings, incoming => IncomingEmail.create(incoming))); + } +}); + +export default IncomingEmail; diff --git a/app/assets/javascripts/admin/routes/admin-badges.js.es6 b/app/assets/javascripts/admin/routes/admin-badges.js.es6 index 5927437669..5efa86491f 100644 --- a/app/assets/javascripts/admin/routes/admin-badges.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-badges.js.es6 @@ -1,4 +1,5 @@ import Badge from 'discourse/models/badge'; +import BadgeGrouping from 'discourse/models/badge-grouping'; export default Discourse.Route.extend({ _json: null, @@ -13,14 +14,19 @@ export default Discourse.Route.extend({ setupController: function(controller, model) { var json = this._json, - triggers = []; + triggers = [], + badgeGroupings = []; _.each(json.admin_badges.triggers,function(v,k){ triggers.push({id: v, name: I18n.t('admin.badges.trigger_type.'+k)}); }); + json.badge_groupings.forEach(function(badgeGroupingJson) { + badgeGroupings.push(BadgeGrouping.create(badgeGroupingJson)); + }); + controller.setProperties({ - badgeGroupings: json.badge_groupings, + badgeGroupings: badgeGroupings, badgeTypes: json.badge_types, protectedSystemFields: json.admin_badges.protected_system_fields, badgeTriggers: triggers, diff --git a/app/assets/javascripts/admin/routes/admin-email-all.js.es6 b/app/assets/javascripts/admin/routes/admin-email-all.js.es6 deleted file mode 100644 index be310b9c38..0000000000 --- a/app/assets/javascripts/admin/routes/admin-email-all.js.es6 +++ /dev/null @@ -1,2 +0,0 @@ -import AdminEmailLogs from 'admin/routes/admin-email-logs'; -export default AdminEmailLogs.extend({ status: "all" }); diff --git a/app/assets/javascripts/admin/routes/admin-email-incomings.js.es6 b/app/assets/javascripts/admin/routes/admin-email-incomings.js.es6 new file mode 100644 index 0000000000..7eefb322cb --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-email-incomings.js.es6 @@ -0,0 +1,14 @@ +import IncomingEmail from 'admin/models/incoming-email'; + +export default Discourse.Route.extend({ + + model() { + return IncomingEmail.findAll({ status: this.get("status") }); + }, + + setupController(controller, model) { + controller.set("model", model); + controller.set("filter", { status: this.get("status") }); + } + +}); diff --git a/app/assets/javascripts/admin/routes/admin-email-index.js.es6 b/app/assets/javascripts/admin/routes/admin-email-index.js.es6 index 9e8aa7d899..1b75e39f6f 100644 --- a/app/assets/javascripts/admin/routes/admin-email-index.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-email-index.js.es6 @@ -1,11 +1,11 @@ import EmailSettings from 'admin/models/email-settings'; export default Discourse.Route.extend({ - model: function() { + model() { return EmailSettings.find(); }, - renderTemplate: function() { + renderTemplate() { this.render('admin/templates/email_index', { into: 'adminEmail' }); } }); diff --git a/app/assets/javascripts/admin/routes/admin-email-logs.js.es6 b/app/assets/javascripts/admin/routes/admin-email-logs.js.es6 index f6cbd90eae..27791bcaec 100644 --- a/app/assets/javascripts/admin/routes/admin-email-logs.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-email-logs.js.es6 @@ -1,27 +1,14 @@ import EmailLog from 'admin/models/email-log'; -/** - Handles routes related to viewing email logs. - - @class AdminEmailSentRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ export default Discourse.Route.extend({ - model: function() { + model() { return EmailLog.findAll({ status: this.get("status") }); }, - setupController: function(controller, model) { + setupController(controller, model) { controller.set("model", model); - // resets the filters controller.set("filter", { status: this.get("status") }); - }, - - renderTemplate: function() { - this.render("admin/templates/email_" + this.get("status"), { into: "adminEmail" }); } }); diff --git a/app/assets/javascripts/admin/routes/admin-email-received.js.es6 b/app/assets/javascripts/admin/routes/admin-email-received.js.es6 new file mode 100644 index 0000000000..4bea62c1eb --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-email-received.js.es6 @@ -0,0 +1,2 @@ +import AdminEmailIncomings from 'admin/routes/admin-email-incomings'; +export default AdminEmailIncomings.extend({ status: "received" }); diff --git a/app/assets/javascripts/admin/routes/admin-email-rejected.js.es6 b/app/assets/javascripts/admin/routes/admin-email-rejected.js.es6 new file mode 100644 index 0000000000..ed662e2118 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-email-rejected.js.es6 @@ -0,0 +1,2 @@ +import AdminEmailIncomings from 'admin/routes/admin-email-incomings'; +export default AdminEmailIncomings.extend({ status: "rejected" }); diff --git a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 index 406a001170..3626ea48d3 100644 --- a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 @@ -8,9 +8,10 @@ export default { }); this.resource('adminEmail', { path: '/email'}, function() { - this.route('all'); this.route('sent'); this.route('skipped'); + this.route('received'); + this.route('rejected'); this.route('previewDigest', { path: '/preview-digest' }); }); diff --git a/app/assets/javascripts/admin/templates/badges-show.hbs b/app/assets/javascripts/admin/templates/badges-show.hbs index 2a6b74f169..173b8c60d1 100644 --- a/app/assets/javascripts/admin/templates/badges-show.hbs +++ b/app/assets/javascripts/admin/templates/badges-show.hbs @@ -2,25 +2,22 @@
- {{input type="text" name="name" value=buffered.name}} + {{#if readOnly}} + {{input type="text" name="name" value=buffered.displayName disabled=true}} + {{else}} + {{input type="text" name="name" value=buffered.name}} + {{/if}}
- {{#if showDisplayName}} -
- {{i18n 'admin.badges.display_name'}} - {{buffered.displayName}} -
- {{/if}} -
- - {{input type="text" name="name" value=buffered.icon}} + + {{input type="text" name="icon" value=buffered.icon}}

{{i18n 'admin.badges.icon_help'}}

- - {{input type="text" name="name" value=buffered.image}} + + {{input type="text" name="image" value=buffered.image}}

{{i18n 'admin.badges.icon_help'}}

@@ -40,7 +37,7 @@ value=buffered.badge_grouping_id content=badgeGroupings optionValuePath="content.id" - optionLabelPath="content.name"}} + optionLabelPath="content.displayName"}}   diff --git a/app/assets/javascripts/admin/templates/email-received.hbs b/app/assets/javascripts/admin/templates/email-received.hbs new file mode 100644 index 0000000000..95e0cb2dc5 --- /dev/null +++ b/app/assets/javascripts/admin/templates/email-received.hbs @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + {{#each email in model}} + + + + + + + {{else}} + + {{/each}} + + + +{{conditional-loading-spinner condition=view.loading}} diff --git a/app/assets/javascripts/admin/templates/email-rejected.hbs b/app/assets/javascripts/admin/templates/email-rejected.hbs new file mode 100644 index 0000000000..6e055ffb2f --- /dev/null +++ b/app/assets/javascripts/admin/templates/email-rejected.hbs @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + {{#each email in model}} + + + + + + + + {{else}} + + {{/each}} + + + +{{conditional-loading-spinner condition=view.loading}} diff --git a/app/assets/javascripts/admin/templates/email_sent.hbs b/app/assets/javascripts/admin/templates/email-sent.hbs similarity index 93% rename from app/assets/javascripts/admin/templates/email_sent.hbs rename to app/assets/javascripts/admin/templates/email-sent.hbs index 0287492f17..4e4f5dbf77 100644 --- a/app/assets/javascripts/admin/templates/email_sent.hbs +++ b/app/assets/javascripts/admin/templates/email-sent.hbs @@ -1,4 +1,4 @@ - +
@@ -37,3 +37,5 @@ {{/each}} + +{{conditional-loading-spinner condition=view.loading}} diff --git a/app/assets/javascripts/admin/templates/email_skipped.hbs b/app/assets/javascripts/admin/templates/email-skipped.hbs similarity index 94% rename from app/assets/javascripts/admin/templates/email_skipped.hbs rename to app/assets/javascripts/admin/templates/email-skipped.hbs index c2c6d261a7..d983b09378 100644 --- a/app/assets/javascripts/admin/templates/email_skipped.hbs +++ b/app/assets/javascripts/admin/templates/email-skipped.hbs @@ -1,4 +1,4 @@ - +
@@ -37,3 +37,5 @@ {{/each}} + +{{conditional-loading-spinner condition=view.loading}} diff --git a/app/assets/javascripts/admin/templates/email.hbs b/app/assets/javascripts/admin/templates/email.hbs index e14d8ba4b5..1a7d5bbfe7 100644 --- a/app/assets/javascripts/admin/templates/email.hbs +++ b/app/assets/javascripts/admin/templates/email.hbs @@ -1,10 +1,11 @@ {{#admin-nav}} {{nav-item route='adminEmail.index' label='admin.email.settings'}} - {{nav-item route='adminEmail.all' label='admin.email.all'}} + {{nav-item route='adminEmail.previewDigest' label='admin.email.preview_digest'}} + {{nav-item route='adminCustomizeEmailTemplates' label='admin.email.templates'}} {{nav-item route='adminEmail.sent' label='admin.email.sent'}} {{nav-item route='adminEmail.skipped' label='admin.email.skipped'}} - {{nav-item route='adminEmail.previewDigest' label='admin.email.preview_digest'}} - {{nav-item route='adminCustomizeEmailTemplates' label='admin.customize.email_templates.title'}} + {{nav-item route='adminEmail.received' label='admin.email.received'}} + {{nav-item route='adminEmail.rejected' label='admin.email.rejected'}} {{/admin-nav}}
diff --git a/app/assets/javascripts/admin/templates/email_all.hbs b/app/assets/javascripts/admin/templates/email_all.hbs deleted file mode 100644 index c2c6d261a7..0000000000 --- a/app/assets/javascripts/admin/templates/email_all.hbs +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - {{#each l in model}} - - - - - - - - {{else}} - - {{/each}} - -
{{i18n 'admin.email.time'}}{{i18n 'admin.email.user'}}{{i18n 'admin.email.to_address'}}{{i18n 'admin.email.email_type'}}{{i18n 'admin.email.skipped_reason'}}
{{i18n 'admin.email.logs.filters.title'}}{{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}}{{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}}{{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}}{{text-field value=filter.skipped_reason placeholderKey="admin.email.logs.filters.skipped_reason_placeholder"}}
{{format-date l.created_at}} - {{#if l.user}} - {{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}} - {{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}} - {{else}} - — - {{/if}} - {{l.to_address}}{{l.email_type}}{{l.skipped_reason}}
{{i18n 'admin.email.logs.none'}}
diff --git a/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs b/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs index 1dd273e0fc..f2a6581bbe 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs @@ -5,15 +5,15 @@
  • {{#if wc.editing}} {{input value=wc.name}} - + {{else}} - {{wc.name}} + {{wc.displayName}} {{/if}}
    - - - - + + + +
  • {{/each}} diff --git a/app/assets/javascripts/admin/templates/user-index.hbs b/app/assets/javascripts/admin/templates/user-index.hbs index 32190c9670..5c1c402d42 100644 --- a/app/assets/javascripts/admin/templates/user-index.hbs +++ b/app/assets/javascripts/admin/templates/user-index.hbs @@ -1,11 +1,13 @@
    - {{#if model.active}} + {{#if model.canViewProfile}} {{#link-to 'user' model class="btn"}} {{fa-icon "user"}} {{i18n 'admin.user.show_public_profile'}} {{/link-to}} + {{/if}} + {{#if model.active}} {{#if model.can_impersonate}} - {{i18n 'admin.user.block_explanation'}} - {{/if}} + {{#conditional-loading-spinner size="small" condition=model.blockingUser}} + {{#if model.blocked}} + + {{i18n 'admin.user.block_explanation'}} + {{else}} + + {{i18n 'admin.user.block_explanation'}} + {{/if}} + {{/conditional-loading-spinner}}
    + +
    +
    {{i18n 'admin.user.staged'}}
    +
    {{model.staged}}
    +
    {{i18n 'admin.user.stage_explanation'}}
    +
    diff --git a/app/assets/javascripts/admin/views/admin-email-incomings.js.es6 b/app/assets/javascripts/admin/views/admin-email-incomings.js.es6 new file mode 100644 index 0000000000..6360f857b7 --- /dev/null +++ b/app/assets/javascripts/admin/views/admin-email-incomings.js.es6 @@ -0,0 +1,14 @@ +import LoadMore from "discourse/mixins/load-more"; + +export default Ember.View.extend(LoadMore, { + loading: false, + eyelineSelector: ".email-list tr", + + actions: { + loadMore() { + if (this.get("loading") || this.get("model.allLoaded")) { return; } + this.set("loading", true); + return this.get("controller").loadMore().then(() => this.set("loading", false)); + } + } +}); diff --git a/app/assets/javascripts/admin/views/admin-email-logs.js.es6 b/app/assets/javascripts/admin/views/admin-email-logs.js.es6 new file mode 100644 index 0000000000..6360f857b7 --- /dev/null +++ b/app/assets/javascripts/admin/views/admin-email-logs.js.es6 @@ -0,0 +1,14 @@ +import LoadMore from "discourse/mixins/load-more"; + +export default Ember.View.extend(LoadMore, { + loading: false, + eyelineSelector: ".email-list tr", + + actions: { + loadMore() { + if (this.get("loading") || this.get("model.allLoaded")) { return; } + this.set("loading", true); + return this.get("controller").loadMore().then(() => this.set("loading", false)); + } + } +}); diff --git a/app/assets/javascripts/admin/views/admin-email-received.js.es6 b/app/assets/javascripts/admin/views/admin-email-received.js.es6 new file mode 100644 index 0000000000..da0d8de157 --- /dev/null +++ b/app/assets/javascripts/admin/views/admin-email-received.js.es6 @@ -0,0 +1,5 @@ +import AdminEmailIncomingsView from "admin/views/admin-email-incomings"; + +export default AdminEmailIncomingsView.extend({ + templateName: "admin/templates/email-received" +}); diff --git a/app/assets/javascripts/admin/views/admin-email-rejected.js.es6 b/app/assets/javascripts/admin/views/admin-email-rejected.js.es6 new file mode 100644 index 0000000000..b89fe8d46a --- /dev/null +++ b/app/assets/javascripts/admin/views/admin-email-rejected.js.es6 @@ -0,0 +1,5 @@ +import AdminEmailIncomingsView from "admin/views/admin-email-incomings"; + +export default AdminEmailIncomingsView.extend({ + templateName: "admin/templates/email-rejected" +}); diff --git a/app/assets/javascripts/admin/views/admin-email-sent.js.es6 b/app/assets/javascripts/admin/views/admin-email-sent.js.es6 new file mode 100644 index 0000000000..d007a79644 --- /dev/null +++ b/app/assets/javascripts/admin/views/admin-email-sent.js.es6 @@ -0,0 +1,5 @@ +import AdminEmailLogsView from "admin/views/admin-email-logs"; + +export default AdminEmailLogsView.extend({ + templateName: "admin/templates/email-sent" +}); diff --git a/app/assets/javascripts/admin/views/admin-email-skipped.js.es6 b/app/assets/javascripts/admin/views/admin-email-skipped.js.es6 new file mode 100644 index 0000000000..e3c4467099 --- /dev/null +++ b/app/assets/javascripts/admin/views/admin-email-skipped.js.es6 @@ -0,0 +1,5 @@ +import AdminEmailLogsView from "admin/views/admin-email-logs"; + +export default AdminEmailLogsView.extend({ + templateName: "admin/templates/email-skipped" +}); diff --git a/app/assets/javascripts/discourse/components/category-chooser.js.es6 b/app/assets/javascripts/discourse/components/category-chooser.js.es6 index 1611fcbe5d..110785db14 100644 --- a/app/assets/javascripts/discourse/components/category-chooser.js.es6 +++ b/app/assets/javascripts/discourse/components/category-chooser.js.es6 @@ -6,9 +6,9 @@ import PermissionType from 'discourse/models/permission-type'; export default ComboboxView.extend({ classNames: ['combobox category-combobox'], - overrideWidths: true, dataAttributes: ['id', 'description_text'], valueBinding: Ember.Binding.oneWay('source'), + overrideWidths: true, castInteger: true, @computed("scopedCategoryId", "categories") @@ -22,7 +22,6 @@ export default ComboboxView.extend({ return categories.filter(c => { if (scopedCategoryId && c.get('id') !== scopedCategoryId && c.get('parent_category_id') !== scopedCategoryId) { return false; } if (c.get('isUncategorizedCategory')) { return false; } - if (c.get('contains_messages')) { return false; } return c.get('permission') === PermissionType.FULL; }); }, diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index 631429f37e..83d9b5ce6f 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -3,6 +3,7 @@ import loadScript from 'discourse/lib/load-script'; import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators'; import { showSelector } from "discourse/lib/emoji/emoji-toolbar"; import Category from 'discourse/models/category'; +import { SEPARATOR as categoryHashtagSeparator } from 'discourse/lib/category-hashtags'; // Our head can be a static string or a function that returns a string // based on input (like for numbered lists). @@ -42,7 +43,7 @@ function Toolbar() { id: 'italic', group: 'fontStyles', shortcut: 'I', - perform: e => e.applySurround('*', '*', 'italic_text') + perform: e => e.applySurround('_', '_', 'italic_text') }); this.addButton({id: 'link', group: 'insertions', shortcut: 'K', action: 'showLinkModal'}); @@ -255,7 +256,7 @@ export default Ember.Component.extend({ template: template, key: '#', transformComplete(category) { - return category.get('slug'); + return Category.slugFor(category, categoryHashtagSeparator); }, dataSource(term) { return Category.search(term); diff --git a/app/assets/javascripts/discourse/components/date-picker.js.es6 b/app/assets/javascripts/discourse/components/date-picker.js.es6 index 3f7c446f5c..d1c8e4fcef 100644 --- a/app/assets/javascripts/discourse/components/date-picker.js.es6 +++ b/app/assets/javascripts/discourse/components/date-picker.js.es6 @@ -3,22 +3,24 @@ import loadScript from "discourse/lib/load-script"; import { on } from "ember-addons/ember-computed-decorators"; export default Em.Component.extend({ - tagName: "input", - classNames: ["date-picker"], + classNames: ["date-picker-wrapper"], _picker: null, @on("didInsertElement") _loadDatePicker() { - const input = this.$()[0]; + const input = this.$(".date-picker")[0]; loadScript("/javascripts/pikaday.js").then(() => { - this._picker = new Pikaday({ + let default_opts = { field: input, + container: this.$()[0], format: "YYYY-MM-DD", defaultDate: moment().add(1, "day").toDate(), minDate: new Date(), - onSelect: date => this.set("value", moment(date).format("YYYY-MM-DD")), - }); + onSelect: date => this.set("value", moment(date).format("YYYY-MM-DD")) + }; + + this._picker = new Pikaday(Object.assign(default_opts, this._opts())); }); }, @@ -27,4 +29,8 @@ export default Em.Component.extend({ this._picker = null; }, + _opts() { + return null; + } + }); diff --git a/app/assets/javascripts/discourse/components/home-logo.js.es6 b/app/assets/javascripts/discourse/components/home-logo.js.es6 index d2deabb8ae..1cd706d09e 100644 --- a/app/assets/javascripts/discourse/components/home-logo.js.es6 +++ b/app/assets/javascripts/discourse/components/home-logo.js.es6 @@ -4,10 +4,15 @@ import { setting } from 'discourse/lib/computed'; export default Ember.Component.extend({ classNames: ["title"], - linkUrl: function() { - return Discourse.getURL('/'); + targetUrl: function() { + // For overriding by customizations + return '/'; }.property(), + linkUrl: function() { + return Discourse.getURL(this.get('targetUrl')); + }.property('targetUrl'), + showSmallLogo: function() { return !Discourse.Mobile.mobileView && this.get("minimized"); }.property("minimized"), @@ -27,7 +32,7 @@ export default Ember.Component.extend({ e.preventDefault(); - DiscourseURL.routeTo('/'); + DiscourseURL.routeTo(this.get('targetUrl')); return false; } }); diff --git a/app/assets/javascripts/discourse/components/notification-item.js.es6 b/app/assets/javascripts/discourse/components/notification-item.js.es6 index 66520e002f..8eef09c0c8 100644 --- a/app/assets/javascripts/discourse/components/notification-item.js.es6 +++ b/app/assets/javascripts/discourse/components/notification-item.js.es6 @@ -29,7 +29,9 @@ export default Ember.Component.extend({ badgeSlug = badgeName.replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase(); } - return Discourse.getURL('/badges/' + badgeId + '/' + badgeSlug); + var username = it.get('data.username'); + username = username ? "?username=" + username.toLowerCase() : ""; + return Discourse.getURL('/badges/' + badgeId + '/' + badgeSlug + username); } const topicId = it.get('topic_id'); diff --git a/app/assets/javascripts/discourse/components/post-menu.js.es6 b/app/assets/javascripts/discourse/components/post-menu.js.es6 index 2b3e28e72a..ef63cd9d24 100644 --- a/app/assets/javascripts/discourse/components/post-menu.js.es6 +++ b/app/assets/javascripts/discourse/components/post-menu.js.es6 @@ -349,6 +349,21 @@ const PostMenuComponent = Ember.Component.extend(StringBuffer, { this.sendAction('toggleBookmark', post); }, + // Wiki button + buttonForWiki(post) { + if (!post.get('can_wiki')) return; + + if (post.get('wiki')) { + return new Button('wiki', 'post.controls.unwiki', 'pencil-square-o', {className: 'wiki wikied'}); + } else { + return new Button('wiki', 'post.controls.wiki', 'pencil-square-o', {className: 'wiki'}); + } + }, + + clickWiki(post) { + this.sendAction('toggleWiki', post); + }, + buttonForAdmin() { if (!Discourse.User.currentProp('canManageTopic')) { return; } return new Button('admin', 'post.controls.admin', 'wrench'); @@ -357,10 +372,7 @@ const PostMenuComponent = Ember.Component.extend(StringBuffer, { renderAdminPopup(post, buffer) { if (!Discourse.User.currentProp('canManageTopic')) { return; } - const isWiki = post.get('wiki'), - wikiIcon = iconHTML('pencil-square-o'), - wikiText = isWiki ? I18n.t('post.controls.unwiki') : I18n.t('post.controls.wiki'), - isModerator = post.get('post_type') === this.site.get('post_types.moderator_action'), + const isModerator = post.get('post_type') === this.site.get('post_types.moderator_action'), postTypeIcon = iconHTML('shield'), postTypeText = isModerator ? I18n.t('post.controls.revert_to_regular') : I18n.t('post.controls.convert_to_moderator'), rebakePostIcon = iconHTML('cog'), @@ -373,7 +385,6 @@ const PostMenuComponent = Ember.Component.extend(StringBuffer, { const html = '
    ' + '

    ' + I18n.t('admin_title') + '

    ' + '
      ' + - '
    • ' + wikiIcon + wikiText + '
    • ' + (Discourse.User.currentProp('staff') ? '
    • ' + postTypeIcon + postTypeText + '
    • ' : '') + '
    • ' + rebakePostIcon + rebakePostText + '
    • ' + (post.hidden ? '
    • ' + unhidePostIcon + unhidePostText + '
    • ' : '') + @@ -393,10 +404,6 @@ const PostMenuComponent = Ember.Component.extend(StringBuffer, { }); }, - clickToggleWiki() { - this.sendAction('toggleWiki', this.get('post')); - }, - clickTogglePostType() { this.sendAction("togglePostType", this.get("post")); }, diff --git a/app/assets/javascripts/discourse/components/small-action.js.es6 b/app/assets/javascripts/discourse/components/small-action.js.es6 index 54ef41b9fa..5674363489 100644 --- a/app/assets/javascripts/discourse/components/small-action.js.es6 +++ b/app/assets/javascripts/discourse/components/small-action.js.es6 @@ -1,4 +1,5 @@ -import { relativeAge } from 'discourse/lib/formatter'; +import { autoUpdatingRelativeAge } from 'discourse/lib/formatter'; +import computed from 'ember-addons/ember-computed-decorators'; const icons = { 'closed.enabled': 'lock', @@ -13,16 +14,20 @@ const icons = { 'pinned_globally.disabled': 'thumb-tack unpinned', 'visible.enabled': 'eye', 'visible.disabled': 'eye-slash', - 'split_topic': 'sign-out' + 'split_topic': 'sign-out', + 'invited_user': 'plus-circle', + 'removed_user': 'minus-circle' }; -export function actionDescription(actionCode, createdAt) { +export function actionDescription(actionCode, createdAt, username) { return function() { const ac = this.get(actionCode); if (ac) { const dt = new Date(this.get(createdAt)); - const when = relativeAge(dt, {format: 'medium-with-ago'}); - return I18n.t(`action_codes.${ac}`, {when}).htmlSafe(); + const when = autoUpdatingRelativeAge(dt, { format: 'medium-with-ago' }); + const u = this.get(username); + const who = u ? `@${u}` : ""; + return I18n.t(`action_codes.${ac}`, { who, when }).htmlSafe(); } }.property(actionCode, createdAt); } @@ -31,18 +36,19 @@ export default Ember.Component.extend({ layoutName: 'components/small-action', // needed because `time-gap` inherits from this classNames: ['small-action'], - description: actionDescription('actionCode', 'post.created_at'), + description: actionDescription('actionCode', 'post.created_at', 'post.action_code_who'), - icon: function() { - return icons[this.get('actionCode')] || 'exclamation'; - }.property('actionCode'), + @computed("actionCode") + icon(actionCode) { + return icons[actionCode] || 'exclamation'; + }, actions: { - edit: function() { + edit() { this.sendAction('editPost', this.get('post')); }, - delete: function() { + delete() { this.sendAction('deletePost', this.get('post')); } } diff --git a/app/assets/javascripts/discourse/components/stream-item.js.es6 b/app/assets/javascripts/discourse/components/stream-item.js.es6 index 562ac59f03..d81366bec7 100644 --- a/app/assets/javascripts/discourse/components/stream-item.js.es6 +++ b/app/assets/javascripts/discourse/components/stream-item.js.es6 @@ -4,7 +4,7 @@ import { actionDescription } from "discourse/components/small-action"; export default Ember.Component.extend({ classNameBindings: [":item", "item.hidden", "item.deleted", "moderatorAction"], moderatorAction: propertyEqual("item.post_type", "site.post_types.moderator_action"), - actionDescription: actionDescription("item.action_code", "item.created_at"), + actionDescription: actionDescription("item.action_code", "item.created_at", "item.username"), actions: { removeBookmark(userAction) { diff --git a/app/assets/javascripts/discourse/components/user-badge.js.es6 b/app/assets/javascripts/discourse/components/user-badge.js.es6 index c7fce8b1a0..0db738d413 100644 --- a/app/assets/javascripts/discourse/components/user-badge.js.es6 +++ b/app/assets/javascripts/discourse/components/user-badge.js.es6 @@ -3,5 +3,13 @@ export default Ember.Component.extend({ showGrantCount: function() { return this.get('count') && this.get('count') > 1; - }.property('count') + }.property('count'), + + badgeUrl: function(){ + // NOTE: I tried using a link-to helper here but the queryParams mean it fails + var username = this.get('user.username_lower') || ''; + username = username !== '' ? "?username=" + username : ''; + return this.get('badge.url') + username; + }.property("badge", "user") + }); diff --git a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 index a5a899f649..afe640bc63 100644 --- a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 +++ b/app/assets/javascripts/discourse/controllers/badges/show.js.es6 @@ -1,17 +1,33 @@ import UserBadge from 'discourse/models/user-badge'; export default Ember.Controller.extend({ + queryParams: ['username'], noMoreBadges: false, userBadges: null, needs: ["application"], + user: function(){ + if (this.get("username")) { + return this.get('userBadges')[0].get('user'); + } + }.property("username"), + + grantCount: function() { + if (this.get("username")) { + return this.get('userBadges.grant_count'); + } else { + return this.get('model.grant_count'); + } + }.property('username', 'model', 'userBadges'), + actions: { loadMore() { const self = this; const userBadges = this.get('userBadges'); UserBadge.findByBadgeId(this.get('model.id'), { - offset: userBadges.length + offset: userBadges.length, + username: this.get('username'), }).then(function(result) { userBadges.pushObjects(result); if(userBadges.length === 0){ @@ -22,11 +38,12 @@ export default Ember.Controller.extend({ }, layoutClass: function(){ + var user = this.get("user") ? " single-user" : ""; var ub = this.get("userBadges"); if(ub && ub[0] && ub[0].post_id){ - return "user-badge-with-posts"; + return "user-badge-with-posts" + user; } else { - return "user-badge-no-posts"; + return "user-badge-no-posts" + user; } }.property("userBadges"), @@ -34,7 +51,7 @@ export default Ember.Controller.extend({ if (this.get('noMoreBadges')) { return false; } if (this.get('userBadges')) { - return this.get('model.grant_count') > this.get('userBadges.length'); + return this.get('grantCount') > this.get('userBadges.length'); } else { return false; } diff --git a/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 b/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 index 64aac27c9c..fa958a84d0 100644 --- a/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 +++ b/app/assets/javascripts/discourse/controllers/forgot-password.js.es6 @@ -7,6 +7,12 @@ export default Ember.Controller.extend(ModalFunctionality, { return Ember.isEmpty((this.get('accountEmailOrUsername') || '').trim()) || this.get('disabled'); }.property('accountEmailOrUsername', 'disabled'), + onShow: function() { + if ($.cookie('email')) { + this.set('accountEmailOrUsername', $.cookie('email')); + } + }, + actions: { submit: function() { var self = this; diff --git a/app/assets/javascripts/discourse/controllers/invite.js.es6 b/app/assets/javascripts/discourse/controllers/invite.js.es6 index d22217b567..fe6c83af41 100644 --- a/app/assets/javascripts/discourse/controllers/invite.js.es6 +++ b/app/assets/javascripts/discourse/controllers/invite.js.es6 @@ -6,6 +6,7 @@ export default Ember.Controller.extend(ModalFunctionality, { // If this isn't defined, it will proxy to the user model on the preferences // page which is wrong. emailOrUsername: null, + inviteIcon: "envelope", isAdmin: function(){ return Discourse.User.currentProp("admin"); @@ -88,8 +89,10 @@ export default Ember.Controller.extend(ModalFunctionality, { if (Ember.isEmpty(this.get('emailOrUsername'))) { return I18n.t('topic.invite_reply.to_topic_blank'); } else if (Discourse.Utilities.emailValid(this.get('emailOrUsername'))) { + this.set("inviteIcon", "envelope"); return I18n.t('topic.invite_reply.to_topic_email'); } else { + this.set("inviteIcon", "hand-o-right"); return I18n.t('topic.invite_reply.to_topic_username'); } } diff --git a/app/assets/javascripts/discourse/controllers/preferences/email.js.es6 b/app/assets/javascripts/discourse/controllers/preferences/email.js.es6 index 83262902d1..099e4a1e68 100644 --- a/app/assets/javascripts/discourse/controllers/preferences/email.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences/email.js.es6 @@ -9,10 +9,10 @@ export default Ember.Controller.extend({ newEmailEmpty: Em.computed.empty('newEmail'), saveDisabled: Em.computed.or('saving', 'newEmailEmpty', 'taken', 'unchanged'), - unchanged: propertyEqual('newEmailLower', 'email'), + unchanged: propertyEqual('newEmailLower', 'currentUser.email'), newEmailLower: function() { - return this.get('newEmail').toLowerCase(); + return this.get('newEmail').toLowerCase().trim(); }.property('newEmail'), saveButtonText: function() { @@ -26,10 +26,10 @@ export default Ember.Controller.extend({ this.set('saving', true); return this.get('content').changeEmail(this.get('newEmail')).then(function() { self.set('success', true); - }, function(data) { + }, function(e) { self.setProperties({ error: true, saving: false }); - if (data.responseJSON && data.responseJSON.errors && data.responseJSON.errors[0]) { - self.set('errorMessage', data.responseJSON.errors[0]); + if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors && e.jqXHR.responseJSON.errors[0]) { + self.set('errorMessage', e.jqXHR.responseJSON.errors[0]); } else { self.set('errorMessage', I18n.t('user.change_email.error')); } @@ -38,5 +38,3 @@ export default Ember.Controller.extend({ } }); - - diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index 5722d2f5d0..e5f5d19d33 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -6,6 +6,7 @@ import Quote from 'discourse/lib/quote'; import { popupAjaxError } from 'discourse/lib/ajax-error'; import computed from 'ember-addons/ember-computed-decorators'; import Composer from 'discourse/models/composer'; +import DiscourseURL from 'discourse/lib/url'; export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { needs: ['header', 'modal', 'composer', 'quote-button', 'topic-progress', 'application'], @@ -17,8 +18,8 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { queryParams: ['filter', 'username_filters', 'show_deleted'], loadedAllPosts: Em.computed.or('model.postStream.loadedAllPosts', 'model.postStream.loadingLastPost'), enteredAt: null, - firstPostExpanded: false, retrying: false, + firstPostExpanded: false, adminMenuVisible: false, showRecover: Em.computed.and('model.deleted', 'model.details.can_recover'), @@ -89,11 +90,14 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { this.set('selectedReplies', []); }.on('init'), - @computed("model.isPrivateMessage", "model.category_id") - showCategoryChooser(isPrivateMessage, categoryId) { - const category = Discourse.Category.findById(categoryId); - const containsMessages = category && category.get("contains_messages"); - return !isPrivateMessage && !containsMessages; + showCategoryChooser: Ember.computed.not("model.isPrivateMessage"), + + gotoInbox(name) { + var url = '/users/' + this.get('currentUser.username_lower') + '/messages'; + if (name) { + url = url + '/group/' + name; + } + DiscourseURL.routeTo(url); }, actions: { @@ -109,12 +113,19 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { this.deleteTopic(); }, + archiveMessage() { - this.get('model').archiveMessage(); + const topic = this.get('model'); + topic.archiveMessage().then(()=>{ + this.gotoInbox(topic.get("inboxGroupName")); + }); }, moveToInbox() { - this.get('model').moveToInbox(); + const topic = this.get('model'); + topic.moveToInbox().then(()=>{ + this.gotoInbox(topic.get("inboxGroupName")); + }); }, // Post related methods diff --git a/app/assets/javascripts/discourse/controllers/user-activity.js.es6 b/app/assets/javascripts/discourse/controllers/user-activity.js.es6 index 8cecec3db1..59c6b6dd81 100644 --- a/app/assets/javascripts/discourse/controllers/user-activity.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-activity.js.es6 @@ -1,3 +1,5 @@ +import { exportUserArchive } from 'discourse/lib/export-csv'; + export default Ember.Controller.extend({ userActionType: null, needs: ["application", "user"], @@ -14,6 +16,21 @@ export default Ember.Controller.extend({ showFooter = this.get("model.statsCountNonPM") <= this.get("model.stream.itemsLoaded"); } this.set("controllers.application.showFooter", showFooter); - }.observes("userActionType", "model.stream.itemsLoaded") + }.observes("userActionType", "model.stream.itemsLoaded"), + + actions: { + exportUserArchive() { + bootbox.confirm( + I18n.t("admin.export_csv.user_archive_confirm"), + I18n.t("no_value"), + I18n.t("yes_value"), + function(confirmed) { + if (confirmed) { + exportUserArchive(); + } + } + ); + } + } }); diff --git a/app/assets/javascripts/discourse/controllers/user-badges.js.es6 b/app/assets/javascripts/discourse/controllers/user-badges.js.es6 index 3ceae1ec13..76b2a05cd9 100644 --- a/app/assets/javascripts/discourse/controllers/user-badges.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-badges.js.es6 @@ -1,4 +1,6 @@ export default Ember.ArrayController.extend({ + needs: ["user"], + user: Em.computed.alias("controllers.user.model"), sortProperties: ['badge.badge_type.sort_order', 'badge.name'], orderBy: function(ub1, ub2){ var sr1 = ub1.get('badge.badge_type.sort_order'); diff --git a/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 b/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 index c51bf40729..eb8d13c721 100644 --- a/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 @@ -3,14 +3,23 @@ import Topic from 'discourse/models/topic'; export default Ember.Controller.extend({ - needs: ["user-topics-list"], + needs: ["user-topics-list", "user"], pmView: false, - + viewingSelf: Em.computed.alias("controllers.user.viewingSelf"), isGroup: Em.computed.equal('pmView', 'groups'), selected: Em.computed.alias('controllers.user-topics-list.selected'), bulkSelectEnabled: Em.computed.alias('controllers.user-topics-list.bulkSelectEnabled'), + mobileView: function() { + return Discourse.Mobile.mobileView; + }.property(), + + showNewPM: function(){ + return this.get('controllers.user.viewingSelf') && + Discourse.User.currentProp('can_send_private_messages'); + }.property('controllers.user.viewingSelf'), + @computed('selected.@each', 'bulkSelectEnabled') hasSelection(selected, bulkSelectEnabled){ return bulkSelectEnabled && selected && selected.length > 0; diff --git a/app/assets/javascripts/discourse/controllers/user-summary.js.es6 b/app/assets/javascripts/discourse/controllers/user-summary.js.es6 new file mode 100644 index 0000000000..b8c4145215 --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/user-summary.js.es6 @@ -0,0 +1,13 @@ +export default Ember.Controller.extend({ + needs: ['user'], + user: Em.computed.alias('controllers.user.model'), + moreTopics: function(){ + return this.get('model.topics').length > 5; + }.property('model'), + moreReplies: function(){ + return this.get('model.replies').length > 5; + }.property('model'), + moreBadges: function(){ + return this.get('model.badges').length > 5; + }.property('model') +}); 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 73a763d472..e4d428a4f9 100644 --- a/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 @@ -14,9 +14,4 @@ export default Ember.Controller.extend({ } }, - showNewPM: function(){ - return this.get('controllers.user.viewingSelf') && - Discourse.User.currentProp('can_send_private_messages'); - }.property('controllers.user.viewingSelf') - }); diff --git a/app/assets/javascripts/discourse/controllers/user.js.es6 b/app/assets/javascripts/discourse/controllers/user.js.es6 index b06b5a9cb3..3971558f88 100644 --- a/app/assets/javascripts/discourse/controllers/user.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user.js.es6 @@ -1,4 +1,3 @@ -import { exportUserArchive } from 'discourse/lib/export-csv'; import CanCheckEmails from 'discourse/mixins/can-check-emails'; import computed from 'ember-addons/ember-computed-decorators'; import UserAction from 'discourse/models/user-action'; @@ -89,17 +88,5 @@ export default Ember.Controller.extend(CanCheckEmails, { .then(user => user.destroy({deletePosts: true})); }, - exportUserArchive() { - bootbox.confirm( - I18n.t("admin.export_csv.user_archive_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - function(confirmed) { - if (confirmed) { - exportUserArchive(); - } - } - ); - } } }); diff --git a/app/assets/javascripts/discourse/dialects/category_hashtag_dialect.js b/app/assets/javascripts/discourse/dialects/category_hashtag_dialect.js index 4beb7ab537..f7a7f48d1e 100644 --- a/app/assets/javascripts/discourse/dialects/category_hashtag_dialect.js +++ b/app/assets/javascripts/discourse/dialects/category_hashtag_dialect.js @@ -4,7 +4,7 @@ **/ Discourse.Dialect.inlineRegexp({ start: '#', - matcher: /^#([\w-]{1,50})/i, + matcher: /^#([\w-:]{1,50})/i, spaceOrTagBoundary: true, emitter: function(matches) { @@ -15,7 +15,7 @@ Discourse.Dialect.inlineRegexp({ result = categoryHashtagLookup && categoryHashtagLookup(slug); if (result && result[0] === "category") { - return ['a', { class: attributeClass, href: result[1] }, hashtag]; + return ['a', { class: attributeClass, href: result[1] }, '#', ["span", {}, slug]]; } else { return ['span', { class: attributeClass }, hashtag]; } diff --git a/app/assets/javascripts/discourse/helpers/category-link.js.es6 b/app/assets/javascripts/discourse/helpers/category-link.js.es6 index 9f7d3f4ed4..1155a2b7bb 100644 --- a/app/assets/javascripts/discourse/helpers/category-link.js.es6 +++ b/app/assets/javascripts/discourse/helpers/category-link.js.es6 @@ -9,6 +9,16 @@ function categoryStripe(color, classes) { return ""; } +/** + Generates category badge HTML + + @param {Object} category The category to generate the badge for. + @param {Object} opts + @param {String} [opts.url] The url that we want the category badge to link to. + @param {Boolean} [opts.allowUncategorized] If false, returns an empty string for the uncategorized category. + @param {Boolean} [opts.link] If false, the category badge will not be a link. + @param {Boolean} [opts.hideParaent] If true, parent category will be hidden in the badge. +**/ export function categoryBadgeHTML(category, opts) { opts = opts || {}; @@ -21,7 +31,7 @@ export function categoryBadgeHTML(category, opts) { var description = get(category, 'description_text'), restricted = get(category, 'read_restricted'), - url = Discourse.getURL("/c/") + Discourse.Category.slugFor(category), + url = opts.url ? opts.url : Discourse.getURL("/c/") + Discourse.Category.slugFor(category), href = (opts.link === false ? '' : url), tagName = (opts.link === false || opts.link === "false" ? 'span' : 'a'), extraClasses = (opts.extraClasses ? (' ' + opts.extraClasses) : ''), @@ -50,6 +60,7 @@ export function categoryBadgeHTML(category, opts) { ">"; var name = escapeExpression(get(category, 'name')); + if (restricted) { html += iconHTML('lock') + " " + name; } else { diff --git a/app/assets/javascripts/discourse/lib/autocomplete.js.es6 b/app/assets/javascripts/discourse/lib/autocomplete.js.es6 index f1b24853ab..5aa01e495b 100644 --- a/app/assets/javascripts/discourse/lib/autocomplete.js.es6 +++ b/app/assets/javascripts/discourse/lib/autocomplete.js.es6 @@ -359,7 +359,7 @@ export default function(options) { return true; } } - prevIsGood = /[a-zA-Z\.]/.test(prev); + prevIsGood = /[a-zA-Z\.-]/.test(prev); } } diff --git a/app/assets/javascripts/discourse/lib/category-hashtags.js.es6 b/app/assets/javascripts/discourse/lib/category-hashtags.js.es6 new file mode 100644 index 0000000000..da2be12096 --- /dev/null +++ b/app/assets/javascripts/discourse/lib/category-hashtags.js.es6 @@ -0,0 +1,5 @@ +export const SEPARATOR = ":"; + +export function replaceSpan($elem, categorySlug, categoryLink) { + $elem.replaceWith(`#${categorySlug}`); +}; diff --git a/app/assets/javascripts/discourse/lib/click-track.js.es6 b/app/assets/javascripts/discourse/lib/click-track.js.es6 index a0e8264b78..627318a83e 100644 --- a/app/assets/javascripts/discourse/lib/click-track.js.es6 +++ b/app/assets/javascripts/discourse/lib/click-track.js.es6 @@ -1,5 +1,10 @@ import DiscourseURL from 'discourse/lib/url'; +export function isValidLink($link) { + return ($link.hasClass("track-link") || + $link.closest('.hashtag,.badge-category,.onebox-result,.onebox-body').length === 0); +}; + export default { trackClick(e) { // cancel click if triggered as part of selection. @@ -32,8 +37,7 @@ export default { var $badge = $('span.badge', $link); if ($badge.length === 1) { // don't update counts in category badge nor in oneboxes (except when we force it) - if ($link.hasClass("track-link") || - $link.closest('.badge-category,.onebox-result,.onebox-body').length === 0) { + if (isValidLink($link)) { var html = $badge.html(); if (/^\d+$/.test(html)) { $badge.html(parseInt(html, 10) + 1); } } diff --git a/app/assets/javascripts/discourse/lib/link-category-hashtags.js.es6 b/app/assets/javascripts/discourse/lib/link-category-hashtags.js.es6 index 446e1aa53a..53b3a8c2ab 100644 --- a/app/assets/javascripts/discourse/lib/link-category-hashtags.js.es6 +++ b/app/assets/javascripts/discourse/lib/link-category-hashtags.js.es6 @@ -1,12 +1,10 @@ +import { replaceSpan } from 'discourse/lib/category-hashtags'; + const validCategoryHashtags = {}; const checkedCategoryHashtags = []; const testedKey = 'tested'; const testedClass = `hashtag-${testedKey}`; -function replaceSpan($elem, categorySlug, categoryLink) { - $elem.replaceWith(`#${categorySlug}`); -} - function updateFound($hashtags, categorySlugs) { Ember.run.schedule('afterRender', () => { $hashtags.each((index, hashtag) => { diff --git a/app/assets/javascripts/discourse/lib/utilities.js b/app/assets/javascripts/discourse/lib/utilities.js index d3da0bcb02..fa1f063f01 100644 --- a/app/assets/javascripts/discourse/lib/utilities.js +++ b/app/assets/javascripts/discourse/lib/utilities.js @@ -262,7 +262,11 @@ Discourse.Utilities = { return ''; } else if (!Discourse.SiteSettings.prevent_anons_from_downloading_files && (/\.(mov|mp4|webm|ogv|mp3|ogg|wav)$/i).test(upload.original_filename)) { // is Audio/Video - return "http://" + Discourse.BaseUrl + upload.url; + if (Discourse.CDN) { + return Discourse.CDN.startsWith('//') ? "http:" + Discourse.getURLWithCDN(upload.url) : Discourse.getURLWithCDN(upload.url); + } else { + return "http://" + Discourse.BaseUrl + upload.url; + } } else { return '' + upload.original_filename + ' (' + I18n.toHumanSize(upload.filesize) + ')'; } diff --git a/app/assets/javascripts/discourse/models/badge-grouping.js.es6 b/app/assets/javascripts/discourse/models/badge-grouping.js.es6 index 605d4d6d2c..04574c4317 100644 --- a/app/assets/javascripts/discourse/models/badge-grouping.js.es6 +++ b/app/assets/javascripts/discourse/models/badge-grouping.js.es6 @@ -8,7 +8,7 @@ export default RestModel.extend({ return this.get('name').toLowerCase().replace(/\s/g, '_'); }, - @computed + @computed('name') displayName() { const i18nKey = `badges.badge_grouping.${this.get('i18nNameKey')}.name`; return I18n.t(i18nKey, {defaultValue: this.get('name')}); diff --git a/app/assets/javascripts/discourse/models/badge.js.es6 b/app/assets/javascripts/discourse/models/badge.js.es6 index 1e4d79433a..2b070a0ab9 100644 --- a/app/assets/javascripts/discourse/models/badge.js.es6 +++ b/app/assets/javascripts/discourse/models/badge.js.es6 @@ -5,6 +5,10 @@ const Badge = RestModel.extend({ newBadge: Em.computed.none('id'), + url: function() { + return Discourse.getURL(`/badges/${this.get('id')}/${this.get('slug')}`); + }.property(), + /** @private diff --git a/app/assets/javascripts/discourse/models/category.js.es6 b/app/assets/javascripts/discourse/models/category.js.es6 index e507419c0e..1a8261dd17 100644 --- a/app/assets/javascripts/discourse/models/category.js.es6 +++ b/app/assets/javascripts/discourse/models/category.js.es6 @@ -86,8 +86,7 @@ const Category = RestModel.extend({ allow_badges: this.get('allow_badges'), custom_fields: this.get('custom_fields'), topic_template: this.get('topic_template'), - suppress_from_homepage: this.get('suppress_from_homepage'), - contains_messages: this.get("contains_messages"), + suppress_from_homepage: this.get('suppress_from_homepage') }, type: this.get('id') ? 'PUT' : 'POST' }); @@ -205,14 +204,14 @@ Category.reopenClass({ return _uncategorized; }, - slugFor(category) { + slugFor(category, separator = "/") { if (!category) return ""; const parentCategory = Em.get(category, 'parentCategory'); let result = ""; if (parentCategory) { - result = Category.slugFor(parentCategory) + "/"; + result = Category.slugFor(parentCategory) + separator; } const id = Em.get(category, 'id'), @@ -299,11 +298,18 @@ Category.reopenClass({ } const emptyTerm = (term === ""); + let slugTerm = term; + + if (!emptyTerm) { + term = term.toLowerCase(); + slugTerm = term; + term = term.replace(/-/g, " "); + } + const categories = Category.listByActivity(); const length = categories.length; var i; var data = []; - term = term.toLowerCase(); const done = () => { return data.length === limit; @@ -312,7 +318,10 @@ Category.reopenClass({ for (i = 0; i < length && !done(); i++) { const category = categories[i]; if ((emptyTerm && !category.get('parent_category_id')) || - (!emptyTerm && category.get('name').toLowerCase().indexOf(term) === 0)) { + (!emptyTerm && + (category.get('name').toLowerCase().indexOf(term) === 0 || + category.get('slug').toLowerCase().indexOf(slugTerm) === 0))) { + data.push(category); } } @@ -321,7 +330,10 @@ Category.reopenClass({ for (i = 0; i < length && !done(); i++) { const category = categories[i]; - if ((!emptyTerm && category.get('name').toLowerCase().indexOf(term) > 0)) { + if (!emptyTerm && + (category.get('name').toLowerCase().indexOf(term) > 0 || + category.get('slug').toLowerCase().indexOf(slugTerm) > 0)) { + if (data.indexOf(category) === -1) data.push(category); } } diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6 index f0bc7eaca1..cc094dc084 100644 --- a/app/assets/javascripts/discourse/models/composer.js.es6 +++ b/app/assets/javascripts/discourse/models/composer.js.es6 @@ -67,17 +67,16 @@ const Composer = RestModel.extend({ creatingPrivateMessage: Em.computed.equal('action', PRIVATE_MESSAGE), notCreatingPrivateMessage: Em.computed.not('creatingPrivateMessage'), - @computed("privateMessage", "archetype.hasOptions", "categoryId") - showCategoryChooser(isPrivateMessage, hasOptions, categoryId) { + @computed("privateMessage", "archetype.hasOptions") + showCategoryChooser(isPrivateMessage, hasOptions) { const manyCategories = Discourse.Category.list().length > 1; - const category = Discourse.Category.findById(categoryId); - const containsMessages = category && category.get("contains_messages"); - return !isPrivateMessage && !containsMessages && (hasOptions || manyCategories); + return !isPrivateMessage && (hasOptions || manyCategories); }, - privateMessage: function(){ - return this.get('creatingPrivateMessage') || this.get('topic.archetype') === 'private_message'; - }.property('creatingPrivateMessage', 'topic'), + @computed("creatingPrivateMessage", "topic") + privateMessage(creatingPrivateMessage, topic) { + return creatingPrivateMessage || (topic && topic.get('archetype') === 'private_message'); + }, topicFirstPost: Em.computed.or('creatingTopic', 'editingFirstPost'), diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6 index c7c8679469..24d0638a53 100644 --- a/app/assets/javascripts/discourse/models/topic.js.es6 +++ b/app/assets/javascripts/discourse/models/topic.js.es6 @@ -424,8 +424,12 @@ const Topic = RestModel.extend({ this.set("archiving", true); var promise = Discourse.ajax(`/t/${this.get('id')}/archive-message`, {type: 'PUT'}); - promise.then(()=>this.set('message_archived', true)) - .finally(()=>this.set('archiving', false)); + promise.then((msg)=> { + this.set('message_archived', true); + if (msg && msg.group_name) { + this.set('inboxGroupName', msg.group_name); + } + }).finally(()=>this.set('archiving', false)); return promise; }, @@ -434,8 +438,12 @@ const Topic = RestModel.extend({ this.set("archiving", true); var promise = Discourse.ajax(`/t/${this.get('id')}/move-to-inbox`, {type: 'PUT'}); - promise.then(()=>this.set('message_archived', false)) - .finally(()=>this.set('archiving', false)); + promise.then((msg)=> { + this.set('message_archived', false); + if (msg && msg.group_name) { + this.set('inboxGroupName', msg.group_name); + } + }).finally(()=>this.set('archiving', false)); return promise; } diff --git a/app/assets/javascripts/discourse/models/user-badge.js.es6 b/app/assets/javascripts/discourse/models/user-badge.js.es6 index 8f938298db..a99c997d63 100644 --- a/app/assets/javascripts/discourse/models/user-badge.js.es6 +++ b/app/assets/javascripts/discourse/models/user-badge.js.es6 @@ -48,7 +48,7 @@ UserBadge.reopenClass({ if ("user_badge" in json) { userBadges = [json.user_badge]; } else { - userBadges = json.user_badges; + userBadges = (json.user_badge_info && json.user_badge_info.user_badges) || json.user_badges; } userBadges = userBadges.map(function(userBadgeJson) { @@ -73,6 +73,10 @@ UserBadge.reopenClass({ if ("user_badge" in json) { return userBadges[0]; } else { + if (json.user_badge_info) { + userBadges.grant_count = json.user_badge_info.grant_count; + userBadges.username = json.user_badge_info.username; + } return userBadges; } }, diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index 2dbeea5763..eff6ef3d7a 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -10,6 +10,7 @@ import UserBadge from 'discourse/models/user-badge'; import UserActionStat from 'discourse/models/user-action-stat'; import UserAction from 'discourse/models/user-action'; import Group from 'discourse/models/group'; +import Topic from 'discourse/models/topic'; const User = RestModel.extend({ @@ -355,6 +356,38 @@ const User = RestModel.extend({ }); } }); + }, + + summary() { + return Discourse.ajax(`/users/${this.get("username_lower")}/summary.json`) + .then(json => { + const topicMap = {}; + + json.topics.forEach(t => { + topicMap[t.id] = Topic.create(t); + }); + + const badgeMap = {}; + Badge.createFromJson(json).forEach(b => { + badgeMap[b.id] = b; + }); + const summary = json["user_summary"]; + + summary.replies.forEach(r => { + r.topic = topicMap[r.topic_id]; + r.url = r.topic.urlForPostNumber(r.post_number); + r.createdAt = new Date(r.created_at); + }); + + summary.topics = summary.topic_ids.map(id => topicMap[id]); + + summary.badges = summary.badges.map(ub => { + const badge = badgeMap[ub.badge_id]; + badge.count = ub.count; + return badge; + }); + return summary; + }); } }); diff --git a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 index f24ee0ef3d..f8c278db19 100644 --- a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 +++ b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 @@ -58,11 +58,13 @@ export default function() { // User routes this.resource('users'); this.resource('user', { path: '/users/:username' }, function() { + this.route('summary'); this.resource('userActivity', { path: '/activity' }, function() { this.route('topics'); this.route('replies'); this.route('likesGiven', {path: 'likes-given'}); this.route('bookmarks'); + this.route('pending'); }); this.resource('userNotifications', {path: '/notifications'}, function(){ diff --git a/app/assets/javascripts/discourse/routes/badges-show.js.es6 b/app/assets/javascripts/discourse/routes/badges-show.js.es6 index 42bc89e9a2..74e04c6440 100644 --- a/app/assets/javascripts/discourse/routes/badges-show.js.es6 +++ b/app/assets/javascripts/discourse/routes/badges-show.js.es6 @@ -2,6 +2,11 @@ import UserBadge from 'discourse/models/user-badge'; import Badge from 'discourse/models/badge'; export default Discourse.Route.extend({ + queryParams: { + username: { + refreshModel: true + } + }, actions: { didTransition() { this.controllerFor("badges/show")._showFooter(); @@ -24,10 +29,13 @@ export default Discourse.Route.extend({ } }, - afterModel(model) { - return UserBadge.findByBadgeId(model.get("id")).then(userBadges => { + afterModel(model,transition) { + const username = transition.queryParams && transition.queryParams.username; + + return UserBadge.findByBadgeId(model.get("id"), {username}).then(userBadges => { this.userBadges = userBadges; }); + }, titleToken() { diff --git a/app/assets/javascripts/discourse/routes/user-statistics.js.es6 b/app/assets/javascripts/discourse/routes/user-statistics.js.es6 deleted file mode 100644 index 610a104899..0000000000 --- a/app/assets/javascripts/discourse/routes/user-statistics.js.es6 +++ /dev/null @@ -1,2 +0,0 @@ -export default Discourse.Route.extend({ -}); diff --git a/app/assets/javascripts/discourse/routes/user-summary.js.es6 b/app/assets/javascripts/discourse/routes/user-summary.js.es6 new file mode 100644 index 0000000000..4b24ecc6ae --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-summary.js.es6 @@ -0,0 +1,5 @@ +export default Discourse.Route.extend({ + model() { + return this.modelFor("User").summary(); + } +}); diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs index 4b245ee2f4..7cfada2430 100644 --- a/app/assets/javascripts/discourse/templates/badges/show.hbs +++ b/app/assets/javascripts/discourse/templates/badges/show.hbs @@ -9,7 +9,9 @@
      {{user-badge badge=model}}
      {{{model.displayDescriptionHtml}}}
      -
      {{i18n 'badges.granted' count=model.grant_count}}
      + {{#unless user}} +
      {{i18n 'badges.granted' count=grantCount}}
      + {{/unless}}
      {{i18n 'badges.allow_title'}} {{{view.allowTitle}}}
      {{i18n 'badges.multiple_grant'}} {{{view.multipleGrant}}}
      @@ -22,23 +24,48 @@
    {{/if}} + {{#if user}} + + {{/if}} + {{#if userBadges}}
    {{#each ub in userBadges}}
    - {{#link-to 'user' ub.user classNames="badge-info"}} - {{avatar ub.user imageSize="large"}} -
    - {{ub.user.username}} - {{format-date ub.granted_at}} -
    - {{/link-to}} + {{#if user}} + {{format-date ub.granted_at}} + {{else}} + {{#link-to 'user' ub.user classNames="badge-info"}} + {{avatar ub.user imageSize="large"}} +
    + {{ub.user.username}} + {{format-date ub.granted_at}} +
    + {{/link-to}} + {{/if}} {{#if ub.post_number}} {{{ub.topic.fancyTitle}}} {{/if}}
    {{/each}} + + {{#unless canLoadMore}} + {{#if user}} + {{i18n 'badges.more_with_badge'}} + {{/if}} + {{/unless}} +
    {{conditional-loading-spinner condition=canLoadMore}} diff --git a/app/assets/javascripts/discourse/templates/components/date-picker.hbs b/app/assets/javascripts/discourse/templates/components/date-picker.hbs new file mode 100644 index 0000000000..7d9e48080a --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/date-picker.hbs @@ -0,0 +1 @@ + diff --git a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs index 1a7fa9b421..01aeb2d15b 100644 --- a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs @@ -20,18 +20,13 @@
    {{#if emailInEnabled}} -
    - -
    +
    + diff --git a/app/assets/javascripts/discourse/templates/modal/invite.hbs b/app/assets/javascripts/discourse/templates/modal/invite.hbs index a5ccb897f1..0a016002d6 100644 --- a/app/assets/javascripts/discourse/templates/modal/invite.hbs +++ b/app/assets/javascripts/discourse/templates/modal/invite.hbs @@ -28,7 +28,7 @@ {{#if model.finished}} {{d-button class="btn-primary" action="closeModal" label="close"}} {{else}} - {{d-button icon="envelope" action="createInvite" class="btn-primary" disabled=disabled label=buttonTitle}} + {{d-button icon=inviteIcon action="createInvite" class="btn-primary" disabled=disabled label=buttonTitle}} {{#if showCopyInviteButton}} {{d-button icon="link" action="generateInvitelink" class="btn-primary" disabled=disabledCopyLink label='user.invited.generate_link'}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 6c0762d228..9b8bf40bb7 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -68,7 +68,7 @@ {{#if showBadges}}
    {{#each ub in user.featured_user_badges}} - {{user-badge badge=ub.badge}} + {{user-badge badge=ub.badge user=user}} {{/each}} {{#if showMoreBadges}} {{#link-to 'user.badges' user class="btn more-user-badges"}} diff --git a/app/assets/javascripts/discourse/templates/user/activity.hbs b/app/assets/javascripts/discourse/templates/user/activity.hbs index 01bc42ee16..e94b740c9c 100644 --- a/app/assets/javascripts/discourse/templates/user/activity.hbs +++ b/app/assets/javascripts/discourse/templates/user/activity.hbs @@ -1,5 +1,5 @@
    -
    diff --git a/app/assets/javascripts/discourse/views/create-account.js.es6 b/app/assets/javascripts/discourse/views/create-account.js.es6 index da0e281582..964c8f8ec5 100644 --- a/app/assets/javascripts/discourse/views/create-account.js.es6 +++ b/app/assets/javascripts/discourse/views/create-account.js.es6 @@ -6,9 +6,14 @@ export default ModalBodyView.extend({ classNames: ['create-account'], _setup: function() { - // allows the submission the form when pressing 'ENTER' on *any* text input field - // but only when the submit button is enabled + // Allows submitting the form when pressing 'ENTER' on *any* text input field + // but only when the submit button is enabled. const createAccountController = this.get('controller'); + + if ($.cookie('email')) { + createAccountController.set('accountEmail', $.cookie('email')); + } + Em.run.schedule('afterRender', function() { $("input[type='text'], input[type='password']").keydown(function(e) { if (createAccountController.get('submitDisabled') === false && e.keyCode === 13) { diff --git a/app/assets/javascripts/discourse/views/login.js.es6 b/app/assets/javascripts/discourse/views/login.js.es6 index f2b61efb5a..656c476bad 100644 --- a/app/assets/javascripts/discourse/views/login.js.es6 +++ b/app/assets/javascripts/discourse/views/login.js.es6 @@ -15,8 +15,13 @@ export default ModalBodyView.extend({ // Get username and password from the browser's password manager, // if it filled the hidden static login form: - loginController.set('loginName', $('#hidden-login-form input[name=username]').val()); - loginController.set('loginPassword', $('#hidden-login-form input[name=password]').val()); + var prefillUsername = $('#hidden-login-form input[name=username]').val(); + if (prefillUsername) { + loginController.set('loginName', prefillUsername); + loginController.set('loginPassword', $('#hidden-login-form input[name=password]').val()); + } else if ($.cookie('email')) { + loginController.set('loginName', $.cookie('email')); + } Em.run.schedule('afterRender', function() { $('#login-account-password, #login-account-name').keydown(function(e) { diff --git a/app/assets/javascripts/discourse/views/post.js.es6 b/app/assets/javascripts/discourse/views/post.js.es6 index 28c4fcbe7c..21535c72d5 100644 --- a/app/assets/javascripts/discourse/views/post.js.es6 +++ b/app/assets/javascripts/discourse/views/post.js.es6 @@ -3,6 +3,7 @@ import { number } from 'discourse/lib/formatter'; import DiscourseURL from 'discourse/lib/url'; import { default as computed, on } from 'ember-addons/ember-computed-decorators'; import { fmt } from 'discourse/lib/computed'; +import { isValidLink } from 'discourse/lib/click-track'; const DAY = 60 * 50 * 1000; @@ -192,8 +193,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, { if (valid) { // don't display badge counts on category badge & oneboxes (unless when explicitely stated) - if ($link.hasClass("track-link") || - $link.closest('.badge-category,.onebox-result,.onebox-body').length === 0) { + if (isValidLink($link)) { $link.append("" + number(lc.clicks) + ""); } } diff --git a/app/assets/javascripts/locales/sk.js.erb b/app/assets/javascripts/locales/sk.js.erb new file mode 100644 index 0000000000..42603f0b4f --- /dev/null +++ b/app/assets/javascripts/locales/sk.js.erb @@ -0,0 +1,9 @@ +//= depend_on 'client.sk.yml' +//= require locales/i18n +<%= JsLocaleHelper.output_locale(:sk) %> + +I18n.pluralizationRules['sk'] = function (n) { + if (n == 1) return "one"; + if (n >= 2 && n <= 4) return "few"; + return "other"; +}; diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 7ef592b19d..85e9ac30e9 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -1532,7 +1532,7 @@ button.ru { @media all and (max-width : 850px) { - html:not(.mobile-view) .nav-stacked { + html:not(.mobile-view) .admin-content .nav-stacked { .glyph {width: auto; position: relative;} > li > a {padding: 13px} } @@ -1606,10 +1606,9 @@ and (max-width : 500px) { border-bottom: 1px solid #dfdfdf; } .actions { - font-size: 1.214em; float: right; - a { - margin-left: 5px; + .btn { + padding: 3px 6px; } } } @@ -1769,6 +1768,30 @@ table#user-badges { } } +// Emails + +.email-list { + .filters input { + width: 100%; + } + .time { + width: 50px; + } + .username div { + max-width: 180px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .addresses p { + margin: 2px 0; + max-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + // Mobile specific styles // Mobile view text-inputs need some padding .mobile-view .admin-contents { diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index d0a66d7a8a..612d43c8ad 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -241,15 +241,17 @@ ol.category-breadcrumb { h2 { float: left; margin: 5px 0 10px; - .top-date-string { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); - font-weight: normal; font-size: 0.7em; - text-transform: uppercase; } } + .top-date-string { + color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + font-weight: normal; + text-transform: uppercase; + } + button { outline: 0; background: transparent; @@ -262,6 +264,7 @@ ol.category-breadcrumb { @include unselectable; + font-size: 1.2em; border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); padding: 5px; background: $secondary; @@ -277,9 +280,15 @@ ol.category-breadcrumb { li { margin: 0; padding: 0; - a { + font-weight: bold; + a, a:visited { display: block; padding: 5px; + color: $primary; + } + .top-date-string { + font-weight: normal; + font-size: 0.8em; } &:hover { background-color: dark-light-diff($highlight, $secondary, 50%, -70%); diff --git a/app/assets/stylesheets/common/base/topic-admin-menu.scss b/app/assets/stylesheets/common/base/topic-admin-menu.scss index 651c7ba203..667496f923 100644 --- a/app/assets/stylesheets/common/base/topic-admin-menu.scss +++ b/app/assets/stylesheets/common/base/topic-admin-menu.scss @@ -33,6 +33,15 @@ } } +.date-picker-wrapper { + display: inline-block; + position: relative; +} + +.pika-single { + position: absolute !important; +} + .modal-body.feature-topic { padding: 5px; max-height: 500px; diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss index fc931a22cd..7ac426abb8 100644 --- a/app/assets/stylesheets/common/base/topic-post.scss +++ b/app/assets/stylesheets/common/base/topic-post.scss @@ -156,9 +156,6 @@ aside.quote { background-color: dark-light-diff($tertiary, $secondary, 85%, -65%); } } -.wiki .topic-body { - background-color: dark-light-diff($wiki, $secondary, 95%, -50%); -} // this ensures consistent top margin on topic posts even if the first line of a post // is a top-margin-less element like a list or image. diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss index a7d25e8771..8f4d7a21b4 100644 --- a/app/assets/stylesheets/common/base/user-badges.scss +++ b/app/assets/stylesheets/common/base/user-badges.scss @@ -187,6 +187,58 @@ } } +.show-badge .badge-user-info { + margin-left: 2%; + .earned { + margin-top: 15px; + font-size: 1.3em; + } + .username { + margin-top: 5px; + display: block; + color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + } +} + +.show-badge .single-user { + margin-left: 2%; + padding-bottom: 20px; + .load-more { + padding-top: 30px; + display: block; + font-size: 1.2em; + } +} + +.show-badge .single-user .badge-user { + padding-left: 0; + + text-align: left; + display: block; + margin: 20px 0; + .badge-info { + display: none; + } + .date { + display: inline-block; + font-size: 1.1em; + margin-left: 10px; + } + .post-link { + font-size: 1.3em; + width: 500px; + margin: 0; + padding: 0; + } + width: 800px; + + &:after { + content: ""; + clear: both; + display: table; + } +} + .long-description.banner { width: 88%; margin-bottom: 20px; diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index fac2284a68..8ec1047f4c 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -149,3 +149,61 @@ } } +.top-section { + display: inline-block; + width: 45%; + max-width: 500px; + padding-right: 20px; + vertical-align: top; + margin-bottom: 30px; + .more { + display: block; + margin-top: 10px; + color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + } + h3 { + margin-bottom: 15px; + } + .relative-date { + color: lighten($primary, 40%); + font-size: 0.8em; + margin-left: 5px; + } + .like-count { + margin-left: 5px; + } + ul { + list-style-type: none; + padding: 0; + margin: 0; + li { + margin: 0; + padding: 8px 0; + .fa-heart { + margin-left: 3px; + } + } + } + + dt,dd { + float:left; + } + dd { + min-width: 80px; + text-align: right; + } + dt { + clear: left; + min-width: 100px; + color: dark-light-choose(scale-color($primary, $lightness: 25%), scale-color($secondary, $lightness: 75%)); + } +} + +@media all +and (max-width : 600px) { + .top-section { + width: 90%; + } +} + + diff --git a/app/assets/stylesheets/common/components/badges.css.scss b/app/assets/stylesheets/common/components/badges.css.scss index 3f5239d1fe..afe7ebd9b2 100644 --- a/app/assets/stylesheets/common/components/badges.css.scss +++ b/app/assets/stylesheets/common/components/badges.css.scss @@ -30,7 +30,7 @@ color: $primary !important; padding: 3px; vertical-align: text-top; - margin-top: -3px; //vertical alignment fix + margin-top: -2px; //vertical alignment fix display: inline-block; overflow: hidden; text-overflow: ellipsis; @@ -38,21 +38,18 @@ .extra-info-wrapper & { color: $header-primary !important; } - } + } - .badge-category-parent-bg, .badge-category-bg { - display: inline-block; - padding: 1px; - - &:before { - content: "\a0"; - } - - } + .badge-category-parent-bg, .badge-category-bg { + display: inline-block; + padding: 1px; + &:before { + content: "\a0"; + } + } } - &.bullet { //bullet category style display: inline-flex; align-items: baseline; @@ -71,31 +68,30 @@ .extra-info-wrapper & { color: $header-primary !important; } - } + } .badge-category-parent-bg, .badge-category-bg { - width: 10px; - height: 10px; - margin-right: 5px; - display: inline-block; - line-height: 1; + width: 10px; + height: 10px; + margin-right: 5px; + display: inline-block; + line-height: 1; - &:before { - content: "\a0"; - } - } + &:before { + content: "\a0"; + } + } - span { - &.badge-category-parent-bg { //subcategory style - width: 5px; - margin-right: 0; - & + .badge-category-bg { - width: 5px; - } - } - } - - } + span { + &.badge-category-parent-bg { //subcategory style + width: 5px; + margin-right: 0; + & + .badge-category-bg { + width: 5px; + } + } + } + } &.box { //box category style (apply custom widths to the wrapper, not the children) @@ -134,6 +130,30 @@ } } +@mixin cooked-badge-bullet($length, $offset:0px) { + .badge-wrapper.bullet { + span { + position: relative; + + &.badge-category-bg { + width: $length; + height: $length; + top: $offset; + } + + &.badge-category-parent-bg { + width: $length / 2; + height: $length; + top: $offset; + + & + .badge-category-bg { + width: $length / 2; + } + } + } + } +} + // Category badge dropdown // -------------------------------------------------- @@ -190,6 +210,8 @@ line-height: 1; } .badge-wrapper { + box-sizing: border-box; + &.bar { padding: 5px 0; width: 100%; diff --git a/app/assets/stylesheets/common/components/hashtag.scss b/app/assets/stylesheets/common/components/hashtag.scss new file mode 100644 index 0000000000..3cea8a783f --- /dev/null +++ b/app/assets/stylesheets/common/components/hashtag.scss @@ -0,0 +1,14 @@ +a.hashtag { + color: black; + font-weight: bold; + + &:visited, &:hover { + color: black; + } + + &:hover { + span { + text-decoration: underline; + } + } +} diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 0e1b719540..42f2676b0e 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -174,6 +174,10 @@ nav.post-controls { box-shadow: none; } + &.wikied { + color: green; + } + &.bookmark {padding: 8px 11px; } .read-icon { @@ -1026,3 +1030,16 @@ and (max-width : 767px) { } } + +@media all +and (max-height: 700px) { + + .post-menu-area { + margin-bottom: 0px; + margin-top: -18px; + } + + .topic-body { + padding: 5px 11px 2px; + } +} diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 56e196f73c..eb11d0af8c 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -79,11 +79,27 @@ margin-top: 18px; } +.user-table { + margin-top: 30px; + width: 100%; + display: table; + .wrapper { + display: table-row; + } +} + +.user-navigation .nav-stacked .glyph { + display: none; + // float: right; + // display: block; + // position: static; +} + .user-navigation { - width: 21.62%; - margin-right: 1.8018%; - float: left; - margin-top: 20px; + display: table-cell; + vertical-align: top; + width: 170px; + padding-right: 30px; h3 { color: $primary; @@ -161,9 +177,9 @@ } .user-right { - width: 75%; - float: right; + max-width: 700px; margin-top: 20px; + display: table-cell; } .user-content { @@ -654,13 +670,18 @@ } .user-main .nav-stacked { + &.notification-list { + padding-top: 40px; + } + &.activity-list { + padding-top: 20px; + } background-color: transparent; - margin-top: 20px; > li { border-bottom: none; > a { - padding: 8px 8px 8px 30px; + padding: 8px 13px; color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); } } @@ -671,8 +692,13 @@ background-color: transparent; } li > a.active:after { - border-left-color: none; + display: none; + } + + li.archive { + padding-left: 15px; } } + diff --git a/app/assets/stylesheets/mobile/topic-list.scss b/app/assets/stylesheets/mobile/topic-list.scss index 7f70b260fa..01f656b6e5 100644 --- a/app/assets/stylesheets/mobile/topic-list.scss +++ b/app/assets/stylesheets/mobile/topic-list.scss @@ -77,6 +77,7 @@ td { padding: 7px 0; color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + max-width: 300px; } a.title {color: $primary;} @@ -404,6 +405,7 @@ td .main-link { display: inline-block; a.title { padding: 5px 10px 5px 0; + word-wrap: break-word; } } .topic-list { diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index c0ffdc2350..05ae0a6646 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -58,6 +58,7 @@ button { margin:10px 0 10px 0; } &.has-like {color: $love;} + &.wikied { color: green; } .read-icon { &:before { font-family: "FontAwesome"; diff --git a/app/assets/stylesheets/mobile/topic.scss b/app/assets/stylesheets/mobile/topic.scss index b32607a7e2..75b772802f 100644 --- a/app/assets/stylesheets/mobile/topic.scss +++ b/app/assets/stylesheets/mobile/topic.scss @@ -23,6 +23,7 @@ a { color: $primary; vertical-align: middle; + word-wrap: break-word; } } .title-category-wrapper { diff --git a/app/controllers/admin/email_controller.rb b/app/controllers/admin/email_controller.rb index 5ae6a32056..4f9837e8b7 100644 --- a/app/controllers/admin/email_controller.rb +++ b/app/controllers/admin/email_controller.rb @@ -17,11 +17,6 @@ class Admin::EmailController < Admin::AdminController end end - def all - email_logs = filter_email_logs(EmailLog.all, params) - render_serialized(email_logs, EmailLogSerializer) - end - def sent email_logs = filter_email_logs(EmailLog.sent, params) render_serialized(email_logs, EmailLogSerializer) @@ -32,6 +27,16 @@ class Admin::EmailController < Admin::AdminController render_serialized(email_logs, EmailLogSerializer) end + def received + incoming_emails = filter_incoming_emails(IncomingEmail, params) + render_serialized(incoming_emails, IncomingEmailSerializer) + end + + def rejected + incoming_emails = filter_incoming_emails(IncomingEmail.errored, params) + render_serialized(incoming_emails, IncomingEmailSerializer) + end + def preview_digest params.require(:last_seen_at) params.require(:username) @@ -49,13 +54,33 @@ class Admin::EmailController < Admin::AdminController private def filter_email_logs(email_logs, params) - email_logs = email_logs.limit(50).includes(:user).order("email_logs.created_at desc").references(:user) - email_logs = email_logs.where("users.username LIKE ?", "%#{params[:user]}%") if params[:user].present? - email_logs = email_logs.where("email_logs.to_address LIKE ?", "%#{params[:address]}%") if params[:address].present? - email_logs = email_logs.where("email_logs.email_type LIKE ?", "%#{params[:type]}%") if params[:type].present? - email_logs = email_logs.where("email_logs.reply_key LIKE ?", "%#{params[:reply_key]}%") if params[:reply_key].present? - email_logs = email_logs.where("email_logs.skipped_reason LIKE ?", "%#{params[:skipped_reason]}%") if params[:skipped_reason].present? - email_logs.to_a + email_logs = email_logs.includes(:user) + .references(:user) + .order(created_at: :desc) + .offset(params[:offset] || 0) + .limit(50) + + email_logs = email_logs.where("users.username ILIKE ?", "%#{params[:user]}%") if params[:user].present? + email_logs = email_logs.where("email_logs.to_address ILIKE ?", "%#{params[:address]}%") if params[:address].present? + email_logs = email_logs.where("email_logs.email_type ILIKE ?", "%#{params[:type]}%") if params[:type].present? + email_logs = email_logs.where("email_logs.reply_key ILIKE ?", "%#{params[:reply_key]}%") if params[:reply_key].present? + email_logs = email_logs.where("email_logs.skipped_reason ILIKE ?", "%#{params[:skipped_reason]}%") if params[:skipped_reason].present? + + email_logs + end + + def filter_incoming_emails(incoming_emails, params) + incoming_emails = incoming_emails.includes(:user, { post: :topic }) + .order(created_at: :desc) + .offset(params[:offset] || 0) + .limit(50) + + incoming_emails = incoming_emails.where("from_address ILIKE ?", "%#{params[:from]}%") if params[:from].present? + incoming_emails = incoming_emails.where("to_addresses ILIKE ? OR cc_addresses ILIKE ?", "%#{params[:to]}%") if params[:to].present? + incoming_emails = incoming_emails.where("subject ILIKE ?", "%#{params[:subject]}%") if params[:subject].present? + incoming_emails = incoming_emails.where("error ILIKE ?", "%#{params[:error]}%") if params[:error].present? + + incoming_emails end def delivery_settings diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index ee03ac7058..dacf04175d 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -216,7 +216,7 @@ class Admin::UsersController < Admin::AdminController def block guardian.ensure_can_block_user! @user - UserBlocker.block(@user, current_user) + UserBlocker.block(@user, current_user, keep_posts: true) render nothing: true end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 97c3b55a1e..05f850e30d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -251,8 +251,8 @@ class ApplicationController < ActionController::Base user = if params[:username] username_lower = params[:username].downcase username_lower.gsub!(/\.json$/, '') - find_opts = {username_lower: username_lower} - find_opts[:active] = true unless opts[:include_inactive] + find_opts = { username_lower: username_lower } + find_opts[:active] = true unless opts[:include_inactive] || current_user.try(:staff?) User.find_by(find_opts) elsif params[:external_id] external_id = params[:external_id].gsub(/\.json$/, '') diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index 5e6a25f1b8..be98b20896 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -178,15 +178,14 @@ class CategoriesController < ApplicationController :position, :email_in, :email_in_allow_strangers, - :contains_messages, :suppress_from_homepage, :parent_category_id, :auto_close_hours, :auto_close_based_on_last_post, :logo_url, :background_url, - :allow_badges, :slug, + :allow_badges, :topic_template, :custom_fields => [params[:custom_fields].try(:keys)], :permissions => [*p.try(:keys)]) diff --git a/app/controllers/category_hashtags_controller.rb b/app/controllers/category_hashtags_controller.rb index 6e78a92ec7..ca4a1f7006 100644 --- a/app/controllers/category_hashtags_controller.rb +++ b/app/controllers/category_hashtags_controller.rb @@ -5,8 +5,10 @@ class CategoryHashtagsController < ApplicationController category_slugs = params[:category_slugs] category_slugs.each(&:downcase!) - valid_categories = Category.secured(guardian).where(slug: category_slugs).map do |category| - { slug: category.slug, url: category.url_with_id } + ids = category_slugs.map { |category_slug| Category.query_from_hashtag_slug(category_slug).try(:id) } + + valid_categories = Category.secured(guardian).where(id: ids).map do |category| + { slug: category.hashtag_slug, url: category.url_with_id } end.compact render json: { valid: valid_categories } diff --git a/app/controllers/email_controller.rb b/app/controllers/email_controller.rb index ca8255334e..4e0ff301af 100644 --- a/app/controllers/email_controller.rb +++ b/app/controllers/email_controller.rb @@ -11,6 +11,7 @@ class EmailController < ApplicationController def unsubscribe @user = DigestUnsubscribeKey.user_for_key(params[:key]) + RateLimiter.new(@user, "unsubscribe_via_email", 3, 1.day).performed! unless @user && @user.staff? # Don't allow the use of a key while logged in as a different user if current_user.present? && (@user != current_user) @@ -23,7 +24,12 @@ class EmailController < ApplicationController return end - @user.update_column(:email_digests, false) + if params[:from_all] + @user.update_columns(email_digests: false, email_direct: false, email_private_messages: false, email_always: false) + else + @user.update_column(:email_digests, false) + end + @success = true end diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index cd59e30987..a5aa226c05 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -54,15 +54,17 @@ class ListController < ApplicationController list_opts.merge!(options) if options user = list_target_user - if filter == :latest && params[:category].blank? - list_opts[:no_definitions] = true - end - - if filter.to_s == current_homepage - list_opts.merge!(exclude_category_ids: get_excluded_category_ids(list_opts[:category])) + if params[:category].blank? + if filter == :latest + list_opts[:no_definitions] = true + end + if filter.to_s == current_homepage + list_opts[:exclude_category_ids] = get_excluded_category_ids(list_opts[:category]) + end end list = TopicQuery.new(user, list_opts).public_send("list_#{filter}") + list.more_topics_url = construct_url_with(:next, list_opts) list.prev_topics_url = construct_url_with(:prev, list_opts) if Discourse.anonymous_filters.include?(filter) @@ -165,7 +167,7 @@ class ListController < ApplicationController top_options[:per_page] = SiteSetting.topics_per_period_in_top_page if "top".freeze == current_homepage - top_options.merge!(exclude_category_ids: get_excluded_category_ids(top_options[:category])) + top_options[:exclude_category_ids] = get_excluded_category_ids(top_options[:category]) end user = list_target_user diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 2b172ed8c4..553e0e036c 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -96,14 +96,6 @@ class PostsController < ApplicationController def create - if !is_api? && current_user.blocked? - - # error has parity with what user would get if they posted when blocked - # and it went through post creator - render json: {errors: [I18n.t("topic_not_found")]}, status: 422 - return - end - @manager_params = create_params @manager_params[:first_post_checks] = !is_api? @@ -305,9 +297,9 @@ class PostsController < ApplicationController end def wiki - guardian.ensure_can_wiki! - post = find_post_from_params + guardian.ensure_can_wiki!(post) + post.revise(current_user, { wiki: params[:wiki] }) render nothing: true diff --git a/app/controllers/static_controller.rb b/app/controllers/static_controller.rb index c2d4d6f176..fc2bbe09f4 100644 --- a/app/controllers/static_controller.rb +++ b/app/controllers/static_controller.rb @@ -6,8 +6,10 @@ class StaticController < ApplicationController skip_before_filter :check_xhr, :redirect_to_login_if_required skip_before_filter :verify_authenticity_token, only: [:cdn_asset, :enter, :favicon] + PAGES_WITH_EMAIL_PARAM = ['login', 'password_reset', 'signup'] + def show - return redirect_to(path '/') if current_user && params[:id] == 'login' + return redirect_to(path '/') if current_user && (params[:id] == 'login' || params[:id] == 'signup') map = { "faq" => {redirect: "faq_url", topic_id: "guidelines_topic_id"}, @@ -44,6 +46,10 @@ class StaticController < ApplicationController return end + if PAGES_WITH_EMAIL_PARAM.include?(@page) && params[:email] + cookies[:email] = { value: params[:email], expires: 1.day.from_now } + end + file = "static/#{@page}.#{I18n.locale}" file = "static/#{@page}.en" if lookup_context.find_all("#{file}.html").empty? file = "static/#{@page}" if lookup_context.find_all("#{file}.html").empty? diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index b049438b68..bc53a2e955 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -281,6 +281,9 @@ class TopicsController < ApplicationController def toggle_archive_message(archive) topic = Topic.find(params[:id].to_i) + + group_id = nil + group_ids = current_user.groups.pluck(:id) if group_ids.present? allowed_groups = topic.allowed_groups @@ -289,6 +292,7 @@ class TopicsController < ApplicationController GroupArchivedMessage.where(group_id: id, topic_id: topic.id).destroy_all if archive + group_id = id GroupArchivedMessage.create!(group_id: id, topic_id: topic.id) end end @@ -302,7 +306,12 @@ class TopicsController < ApplicationController end end - render nothing: true + if group_id + name = Group.find_by(id: group_id).try(:name) + render_json_dump(group_name: name) + else + render nothing: true + end end def bookmark @@ -347,7 +356,7 @@ class TopicsController < ApplicationController topic = Topic.find_by(id: params[:topic_id]) guardian.ensure_can_remove_allowed_users!(topic) - if topic.remove_allowed_user(params[:username]) + if topic.remove_allowed_user(current_user, params[:username]) render json: success_json else render json: failed_json, status: 422 diff --git a/app/controllers/user_badges_controller.rb b/app/controllers/user_badges_controller.rb index 397ce503fb..5f1499f916 100644 --- a/app/controllers/user_badges_controller.rb +++ b/app/controllers/user_badges_controller.rb @@ -1,16 +1,28 @@ class UserBadgesController < ApplicationController def index - params.permit [:granted_before, :offset] + params.permit [:granted_before, :offset, :username] badge = fetch_badge_from_params user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(96) user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic) + grant_count = nil + + if params[:username] + user_id = User.where(username_lower: params[:username].downcase).pluck(:id).first + user_badges = user_badges.where(user_id: user_id) if user_id + grant_count = user_badges.count + end + if offset = params[:offset] user_badges = user_badges.offset(offset.to_i) end - render_serialized(user_badges, UserBadgeSerializer, root: "user_badges", include_long_description: true) + user_badges = UserBadges.new(user_badges: user_badges, + username: params[:username], + grant_count: grant_count) + + render_serialized(user_badges, UserBadgesSerializer, root: :user_badge_info, include_long_description: true) end def username @@ -28,7 +40,7 @@ class UserBadgesController < ApplicationController .includes(post: :topic) .includes(:granted_by) - render_serialized(user_badges, DetailedUserBadgeSerializer, root: "user_badges") + render_serialized(user_badges, DetailedUserBadgeSerializer, root: :user_badges) end def create diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index fb537e6a58..3a9f77942f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -176,6 +176,13 @@ class UsersController < ApplicationController end end + def summary + user = fetch_user_from_params + summary = UserSummary.new(user, guardian) + serializer = UserSummarySerializer.new(summary, scope: guardian) + render_json_dump(serializer) + end + def invited inviter = fetch_user_from_params offset = params[:offset].to_i || 0 @@ -472,10 +479,11 @@ class UsersController < ApplicationController RateLimiter.new(user, "change-email-hr-#{request.remote_ip}", 6, 1.hour).performed! RateLimiter.new(user, "change-email-min-#{request.remote_ip}", 3, 1.minute).performed! + EmailValidator.new(attributes: :email).validate_each(user, :email, lower_email) + return render_json_error(user.errors.full_messages) if user.errors[:email].present? + # Raise an error if the email is already in use - if User.find_by_email(lower_email) - raise Discourse::InvalidParameters.new(:email) - end + return render_json_error(I18n.t('change_email.error')) if User.find_by_email(lower_email) email_token = user.email_tokens.create(email: lower_email) Jobs.enqueue( diff --git a/app/jobs/base.rb b/app/jobs/base.rb index 1ccbdbdee7..427a720079 100644 --- a/app/jobs/base.rb +++ b/app/jobs/base.rb @@ -197,6 +197,11 @@ module Jobs class Scheduled < Base extend Scheduler::Schedule + + def perform(*args) + return if Discourse.readonly_mode? + super + end end def self.enqueue(job_name, opts={}) @@ -233,30 +238,23 @@ module Jobs end def self.cancel_scheduled_job(job_name, params={}) - jobs = scheduled_for(job_name, params) - return false if jobs.empty? - jobs.each { |job| job.delete } - true + scheduled_for(job_name, params).each(&:delete) end def self.scheduled_for(job_name, params={}) + params = params.with_indifferent_access job_class = "Jobs::#{job_name.to_s.camelcase}" Sidekiq::ScheduledSet.new.select do |scheduled_job| - if scheduled_job.klass == 'Sidekiq::Extensions::DelayedClass' - job_args = YAML.load(scheduled_job.args[0]) - job_args_class, _, (job_args_params, *) = job_args - if job_args_class.to_s == job_class && job_args_params - matched = true - params.each do |key, value| - unless job_args_params[key] == value - matched = false - break - end + if scheduled_job.klass.to_s == job_class + matched = true + job_params = scheduled_job.item["args"][0].with_indifferent_access + params.each do |key, value| + if job_params[key] != value + matched = false + break end - matched - else - false end + matched else false end diff --git a/app/jobs/scheduled/badge_grant.rb b/app/jobs/scheduled/badge_grant.rb index 5f218f9118..dc5def0684 100644 --- a/app/jobs/scheduled/badge_grant.rb +++ b/app/jobs/scheduled/badge_grant.rb @@ -10,7 +10,7 @@ module Jobs def execute(args) return unless SiteSetting.enable_badges - Badge.all.each do |b| + Badge.enabled.find_each do |b| begin BadgeGranter.backfill(b) rescue => ex diff --git a/app/jobs/scheduled/poll_mailbox.rb b/app/jobs/scheduled/poll_mailbox.rb index 552833731a..2d141e3b1b 100644 --- a/app/jobs/scheduled/poll_mailbox.rb +++ b/app/jobs/scheduled/poll_mailbox.rb @@ -1,6 +1,3 @@ -# -# Connects to a mailbox and checks for replies -# require 'net/pop' require_dependency 'email/receiver' require_dependency 'email/sender' @@ -10,6 +7,7 @@ module Jobs class PollMailbox < Jobs::Scheduled every SiteSetting.pop3_polling_period_mins.minutes sidekiq_options retry: false + include Email::BuildEmailHelper def execute(args) @@ -17,53 +15,42 @@ module Jobs poll_pop3 if SiteSetting.pop3_polling_enabled? end - def handle_mail(mail) + def process_popmail(popmail) begin - mail_string = mail.pop + mail_string = popmail.pop Email::Receiver.new(mail_string).process rescue => e handle_failure(mail_string, e) - ensure - mail.delete end end def handle_failure(mail_string, e) Rails.logger.warn("Email can not be processed: #{e}\n\n#{mail_string}") if SiteSetting.log_mail_processing_failures + message_template = case e + when Email::Receiver::EmptyEmailError then :email_reject_empty + when Email::Receiver::NoBodyDetectedError then :email_reject_empty + when Email::Receiver::NoMessageIdError then :email_reject_no_message_id + when Email::Receiver::AutoGeneratedEmailError then :email_reject_auto_generated + when Email::Receiver::InactiveUserError then :email_reject_inactive_user + when Email::Receiver::BadDestinationAddress then :email_reject_bad_destination_address + when Email::Receiver::StrangersNotAllowedError then :email_reject_strangers_not_allowed + when Email::Receiver::InsufficientTrustLevelError then :email_reject_insufficient_trust_level + when Email::Receiver::ReplyUserNotMatchingError then :email_reject_reply_user_not_matching + when Email::Receiver::TopicNotFoundError then :email_reject_topic_not_found + when Email::Receiver::TopicClosedError then :email_reject_topic_closed + when Email::Receiver::InvalidPost then :email_reject_invalid_post + when ActiveRecord::Rollback then :email_reject_invalid_post + when Email::Receiver::InvalidPostAction then :email_reject_invalid_post_action + when Discourse::InvalidAccess then :email_reject_invalid_access + end + template_args = {} - case e - when Email::Receiver::UserNotSufficientTrustLevelError - message_template = :email_reject_trust_level - when Email::Receiver::UserNotFoundError - message_template = :email_reject_no_account - when Email::Receiver::EmptyEmailError - message_template = :email_reject_empty - when Email::Receiver::EmailUnparsableError - message_template = :email_reject_parsing - when Email::Receiver::EmailLogNotFound - message_template = :email_reject_reply_key - when Email::Receiver::BadDestinationAddress - message_template = :email_reject_destination - when Email::Receiver::TopicNotFoundError - message_template = :email_reject_topic_not_found - when Email::Receiver::TopicClosedError - message_template = :email_reject_topic_closed - when Email::Receiver::AutoGeneratedEmailError - message_template = :email_reject_auto_generated - when Discourse::InvalidAccess - message_template = :email_reject_invalid_access - when ActiveRecord::Rollback - message_template = :email_reject_post_error - when Email::Receiver::InvalidPost - if e.message.length < 6 - message_template = :email_reject_post_error - else - message_template = :email_reject_post_error_specified - template_args[:post_error] = e.message - end - else - message_template = nil + + # there might be more information available in the exception + if message_template == :email_reject_invalid_post && e.message.size > 6 + message_template = :email_reject_invalid_post_specified + template_args[:post_error] = e.message end if message_template @@ -81,19 +68,16 @@ module Jobs end def poll_pop3 - connection = Net::POP3.new(SiteSetting.pop3_polling_host, SiteSetting.pop3_polling_port) - connection.enable_ssl if SiteSetting.pop3_polling_ssl + pop3 = Net::POP3.new(SiteSetting.pop3_polling_host, SiteSetting.pop3_polling_port) + pop3.enable_ssl if SiteSetting.pop3_polling_ssl - connection.start(SiteSetting.pop3_polling_username, SiteSetting.pop3_polling_password) do |pop| - unless pop.mails.empty? - pop.each { |mail| handle_mail(mail) } + pop3.start(SiteSetting.pop3_polling_username, SiteSetting.pop3_polling_password) do |pop| + pop.delete_all do |p| + process_popmail(p) end - pop.finish end rescue Net::POPAuthenticationError => e Discourse.handle_job_exception(e, error_context(@args, "Signing in to poll incoming email")) - rescue Net::POPError => e - Discourse.handle_job_exception(e, error_context(@args, "Generic POP error")) end end diff --git a/app/mailers/rejection_mailer.rb b/app/mailers/rejection_mailer.rb index 1c1a7558b1..2821f9abf6 100644 --- a/app/mailers/rejection_mailer.rb +++ b/app/mailers/rejection_mailer.rb @@ -3,13 +3,23 @@ require_dependency 'email/message_builder' class RejectionMailer < ActionMailer::Base include Email::BuildEmailHelper - DISALLOWED_TEMPLATE_ARGS = [:to, :from, :base_url, + DISALLOWED_TEMPLATE_ARGS = [:to, + :from, + :base_url, :user_preferences_url, - :include_respond_instructions, :html_override, - :add_unsubscribe_link, :respond_instructions, - :style, :body, :post_id, :topic_id, :subject, - :template, :allow_reply_by_email, - :private_reply, :from_alias] + :include_respond_instructions, + :html_override, + :add_unsubscribe_link, + :respond_instructions, + :style, + :body, + :post_id, + :topic_id, + :subject, + :template, + :allow_reply_by_email, + :private_reply, + :from_alias] # Send an email rejection message. # diff --git a/app/mailers/subscription_mailer.rb b/app/mailers/subscription_mailer.rb new file mode 100644 index 0000000000..c362287130 --- /dev/null +++ b/app/mailers/subscription_mailer.rb @@ -0,0 +1,14 @@ +require_dependency 'email/message_builder' + +class SubscriptionMailer < ActionMailer::Base + include Email::BuildEmailHelper + + def confirm_unsubscribe(user, opts={}) + unsubscribe_key = DigestUnsubscribeKey.create_key_for(user) + build_email user.email, + template: "unsubscribe_mailer", + site_title: SiteSetting.title, + site_domain_name: Discourse.current_hostname, + confirm_unsubscribe_link: "#{Discourse.base_url}/unsubscribe/#{unsubscribe_key}?from_all=true" + end +end diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index acc2ff7ed8..947735eaa6 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -307,6 +307,7 @@ class UserNotifications < ActionMailer::Base context: context, username: username, add_unsubscribe_link: !user.staged, + add_unsubscribe_via_email_link: user.mailing_list_mode, unsubscribe_url: post.topic.unsubscribe_url, allow_reply_by_email: allow_reply_by_email, use_site_subject: use_site_subject, diff --git a/app/models/badge_grouping.rb b/app/models/badge_grouping.rb index 3512806d69..f1201e7214 100644 --- a/app/models/badge_grouping.rb +++ b/app/models/badge_grouping.rb @@ -9,7 +9,7 @@ class BadgeGrouping < ActiveRecord::Base has_many :badges def system? - id && id < 5 + id && id <= 5 end def default_position=(pos) diff --git a/app/models/category.rb b/app/models/category.rb index 0ee810e641..2eab836c0c 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -5,6 +5,7 @@ class Category < ActiveRecord::Base include Positionable include HasCustomFields + include CategoryHashtag belongs_to :topic, dependent: :destroy belongs_to :topic_only_relative_url, @@ -75,6 +76,7 @@ class Category < ActiveRecord::Base scoped_to_permissions(guardian, [:create_post, :full]) end } + delegate :post_template, to: 'self.class' # permission is just used by serialization @@ -398,8 +400,8 @@ SQL @@url_cache.clear end - def full_slug - url[3..-1].gsub("/", "-") + def full_slug(separator = "-") + url[3..-1].gsub("/", separator) end def url diff --git a/app/models/category_user.rb b/app/models/category_user.rb index cd7b719afd..2922b68588 100644 --- a/app/models/category_user.rb +++ b/app/models/category_user.rb @@ -108,3 +108,8 @@ end # user_id :integer not null # notification_level :integer not null # +# Indexes +# +# idx_category_users_u1 (user_id,category_id,notification_level) UNIQUE +# idx_category_users_u2 (category_id,user_id,notification_level) UNIQUE +# diff --git a/app/models/concerns/category_hashtag.rb b/app/models/concerns/category_hashtag.rb new file mode 100644 index 0000000000..8f2d807fbd --- /dev/null +++ b/app/models/concerns/category_hashtag.rb @@ -0,0 +1,23 @@ +module CategoryHashtag + extend ActiveSupport::Concern + + SEPARATOR = ":".freeze + + class_methods do + def query_from_hashtag_slug(category_slug) + parent_slug, child_slug = category_slug.split(SEPARATOR, 2) + + category = Category.where(slug: parent_slug, parent_category_id: nil) + + if child_slug + Category.where(slug: child_slug, parent_category_id: category.pluck(:id).first).first + else + category.first + end + end + end + + def hashtag_slug + full_slug(SEPARATOR) + end +end diff --git a/app/models/directory_item.rb b/app/models/directory_item.rb index 61fc7e400c..680b35c082 100644 --- a/app/models/directory_item.rb +++ b/app/models/directory_item.rb @@ -13,7 +13,12 @@ class DirectoryItem < ActiveRecord::Base end def self.period_types - @types ||= Enum.new(:all, :yearly, :monthly, :weekly, :daily, :quarterly) + @types ||= Enum.new(all: 1, + yearly: 2, + monthly: 3, + weekly: 4, + daily: 5, + quarterly: 6) end def self.refresh! @@ -70,6 +75,26 @@ class DirectoryItem < ActiveRecord::Base new_topic_type: UserAction::NEW_TOPIC, reply_type: UserAction::REPLY, regular_post_type: Post.types[:regular] + + if period_type == :all + exec_sql < d.likes_given OR + s.likes_received <> d.likes_received OR + s.topic_count <> d.topic_count OR + s.post_count <> d.post_count + ) + +SQL + end end end end diff --git a/app/models/embeddable_host.rb b/app/models/embeddable_host.rb index ee51105bcd..98eb59a2f5 100644 --- a/app/models/embeddable_host.rb +++ b/app/models/embeddable_host.rb @@ -1,5 +1,5 @@ class EmbeddableHost < ActiveRecord::Base - validates_format_of :host, :with => /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,7}(:[0-9]{1,5})?(\/.*)?\Z/i + validate :host_must_be_valid belongs_to :category before_validation do @@ -21,6 +21,14 @@ class EmbeddableHost < ActiveRecord::Base record_for_host(host).present? end + private + + def host_must_be_valid + if host !~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,7}(:[0-9]{1,5})?(\/.*)?\Z/i && + host !~ /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ + errors.add(:host, I18n.t('errors.messages.invalid')) + end + end end # == Schema Information diff --git a/app/models/group.rb b/app/models/group.rb index 925c3d7096..a508335a77 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -442,8 +442,11 @@ end # primary_group :boolean default(FALSE), not null # title :string(255) # grant_trust_level :integer +# incoming_email :string +# has_messages :boolean default(FALSE), not null # # Indexes # -# index_groups_on_name (name) UNIQUE +# index_groups_on_incoming_email (incoming_email) UNIQUE +# index_groups_on_name (name) UNIQUE # diff --git a/app/models/group_archived_message.rb b/app/models/group_archived_message.rb index 7c346518e4..b32092cb39 100644 --- a/app/models/group_archived_message.rb +++ b/app/models/group_archived_message.rb @@ -2,3 +2,18 @@ class GroupArchivedMessage < ActiveRecord::Base belongs_to :user belongs_to :topic end + +# == Schema Information +# +# Table name: group_archived_messages +# +# id :integer not null, primary key +# group_id :integer not null +# topic_id :integer not null +# created_at :datetime +# updated_at :datetime +# +# Indexes +# +# index_group_archived_messages_on_group_id_and_topic_id (group_id,topic_id) UNIQUE +# diff --git a/app/models/group_mention.rb b/app/models/group_mention.rb index 65faeb6f03..30eb647ebc 100644 --- a/app/models/group_mention.rb +++ b/app/models/group_mention.rb @@ -2,3 +2,19 @@ class GroupMention < ActiveRecord::Base belongs_to :post belongs_to :group end + +# == Schema Information +# +# Table name: group_mentions +# +# id :integer not null, primary key +# post_id :integer +# group_id :integer +# created_at :datetime +# updated_at :datetime +# +# Indexes +# +# index_group_mentions_on_group_id_and_post_id (group_id,post_id) UNIQUE +# index_group_mentions_on_post_id_and_group_id (post_id,group_id) UNIQUE +# diff --git a/app/models/group_user.rb b/app/models/group_user.rb index e3129ebc4a..ddd0de85f2 100644 --- a/app/models/group_user.rb +++ b/app/models/group_user.rb @@ -61,12 +61,13 @@ end # # Table name: group_users # -# id :integer not null, primary key -# group_id :integer not null -# user_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null -# owner :boolean default(FALSE), not null +# id :integer not null, primary key +# group_id :integer not null +# user_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# owner :boolean default(FALSE), not null +# notification_level :integer default(3), not null # # Indexes # diff --git a/app/models/incoming_email.rb b/app/models/incoming_email.rb new file mode 100644 index 0000000000..1ba3fb88c3 --- /dev/null +++ b/app/models/incoming_email.rb @@ -0,0 +1,32 @@ +class IncomingEmail < ActiveRecord::Base + belongs_to :user + belongs_to :topic + belongs_to :post + + scope :errored, -> { where.not(error: nil) } +end + +# == Schema Information +# +# Table name: incoming_emails +# +# id :integer not null, primary key +# user_id :integer +# topic_id :integer +# post_id :integer +# raw :text +# error :text +# message_id :text +# from_address :text +# to_addresses :text +# cc_addresses :text +# subject :text +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_incoming_emails_on_created_at (created_at) +# index_incoming_emails_on_error (error) +# index_incoming_emails_on_message_id (message_id) +# diff --git a/app/models/post.rb b/app/models/post.rb index 85320e133b..746a7a33bb 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -90,6 +90,10 @@ class Post < ActiveRecord::Base includes(:post_details).find_by(post_details: { key: key, value: value }) end + def whisper? + post_type == Post.types[:whisper] + end + def add_detail(key, value, extra = nil) post_details.build(key: key, value: value, extra: extra) end @@ -355,6 +359,10 @@ class Post < ActiveRecord::Base publish_change_to_clients!(:acted) end + def full_url + "#{Discourse.base_url}#{url}" + end + def url if topic Post.url(topic.slug, topic.id, post_number) diff --git a/app/models/post_analyzer.rb b/app/models/post_analyzer.rb index dce65b4e8d..cf20921cca 100644 --- a/app/models/post_analyzer.rb +++ b/app/models/post_analyzer.rb @@ -52,8 +52,11 @@ class PostAnalyzer cooked_stripped.css("code").remove cooked_stripped.css(".onebox").remove - results = cooked_stripped.to_html.scan(PrettyText.mention_matcher) - @raw_mentions = results.uniq.map { |un| un.first.downcase.sub!(/^@/, '') } + @raw_mentions = cooked_stripped.to_html + .scan(PrettyText.mention_matcher) + .flatten + .map(&:downcase) + .uniq end # from rack ... compat with ruby 2.2 diff --git a/app/models/s3_region_site_setting.rb b/app/models/s3_region_site_setting.rb index 991813f966..24710f89af 100644 --- a/app/models/s3_region_site_setting.rb +++ b/app/models/s3_region_site_setting.rb @@ -6,7 +6,7 @@ class S3RegionSiteSetting < EnumSiteSetting end def self.values - @values ||= valid_values.sort.map { |x| { name: x, value: x } } + @values ||= valid_values.sort.map { |x| { name: "s3.regions.#{x.tr("-", "_")}", value: x } } end def self.valid_values @@ -19,7 +19,13 @@ class S3RegionSiteSetting < EnumSiteSetting 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', - 'sa-east-1'] + 'ap-northeast-2', + 'sa-east-1' + ] + end + + def self.translate_names? + true end private_class_method :valid_values diff --git a/app/models/top_topic.rb b/app/models/top_topic.rb index 908e97797e..29f206ea30 100644 --- a/app/models/top_topic.rb +++ b/app/models/top_topic.rb @@ -147,6 +147,15 @@ class TopTopic < ActiveRecord::Base def self.compute_top_score_for(period) + log_views_multiplier = SiteSetting.top_topics_formula_log_views_multiplier.to_f + log_views_multiplier = 2 if log_views_multiplier == 0 + + first_post_likes_multiplier = SiteSetting.top_topics_formula_first_post_likes_multiplier.to_f + first_post_likes_multiplier = 0.5 if first_post_likes_multiplier == 0 + + least_likes_per_post_multiplier = SiteSetting.top_topics_formula_least_likes_per_post_multiplier.to_f + least_likes_per_post_multiplier = 3 if least_likes_per_post_multiplier == 0 + if period == :all top_topics = "( SELECT t.like_count all_likes_count, @@ -167,11 +176,11 @@ class TopTopic < ActiveRecord::Base WITH top AS ( SELECT CASE WHEN #{time_filter} THEN 0 - ELSE log(GREATEST(#{period}_views_count, 1)) * 2 + - #{period}_op_likes_count * 0.5 + + ELSE log(GREATEST(#{period}_views_count, 1)) * #{log_views_multiplier} + + #{period}_op_likes_count * #{first_post_likes_multiplier} + CASE WHEN #{period}_likes_count > 0 AND #{period}_posts_count > 0 THEN - LEAST(#{period}_likes_count / #{period}_posts_count, 3) + LEAST(#{period}_likes_count / #{period}_posts_count, #{least_likes_per_post_multiplier}) ELSE 0 END + CASE WHEN topics.posts_count < 10 THEN diff --git a/app/models/topic.rb b/app/models/topic.rb index 5f470c509b..7a370a9261 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -27,7 +27,7 @@ class Topic < ActiveRecord::Base attr_accessor :allowed_user_ids def self.max_sort_order - 2**31 - 1 + @max_sort_order ||= (2 ** 31) - 1 end def featured_users @@ -208,7 +208,7 @@ class Topic < ActiveRecord::Base def cancel_auto_close_job if (auto_close_at_changed? && !auto_close_at_was.nil?) || (auto_close_user_id_changed? && auto_close_at) self.auto_close_started_at ||= Time.zone.now if auto_close_at - Jobs.cancel_scheduled_job(:close_topic, { topic_id: id }) + Jobs.cancel_scheduled_job(:close_topic, topic_id: id) end end @@ -514,6 +514,12 @@ class Topic < ActiveRecord::Base true end + def add_small_action(user, action_code, who=nil) + custom_fields = {} + custom_fields["action_code_who"] = who if who.present? + add_moderator_post(user, nil, post_type: Post.types[:small_action], action_code: action_code, custom_fields: custom_fields) + end + def add_moderator_post(user, text, opts=nil) opts ||= {} new_post = nil @@ -524,7 +530,8 @@ class Topic < ActiveRecord::Base no_bump: opts[:bump].blank?, skip_notifications: opts[:skip_notifications], topic_id: self.id, - skip_validations: true) + skip_validations: true, + custom_fields: opts[:custom_fields]) new_post = creator.create increment!(:moderator_posts_count) if new_post.persisted? @@ -543,7 +550,6 @@ class Topic < ActiveRecord::Base def change_category_to_id(category_id) return false if private_message? - return false if category.try(:contains_messages) new_category_id = category_id.to_i # if the category name is blank, reset the attribute @@ -557,11 +563,12 @@ class Topic < ActiveRecord::Base changed_to_category(cat) end - def remove_allowed_user(username) + def remove_allowed_user(removed_by, username) if user = User.find_by(username: username) topic_user = topic_allowed_users.find_by(user_id: user.id) if topic_user topic_user.destroy + add_small_action(removed_by, "removed_user", user.username) return true end end @@ -575,6 +582,8 @@ class Topic < ActiveRecord::Base # If the user exists, add them to the message. user = User.find_by_username_or_email(username_or_email) if user && topic_allowed_users.create!(user_id: user.id) + # Create a small action message + add_small_action(invited_by, "invited_user", user.username) # Notify the user they've been invited user.notifications.create(notification_type: Notification.types[:invited_to_private_message], @@ -909,8 +918,10 @@ class Topic < ActiveRecord::Base sql = < 0 - end TIME_TO_FIRST_RESPONSE_SQL ||= <<-SQL diff --git a/app/models/translation_override.rb b/app/models/translation_override.rb index a433605e8e..3046832e61 100644 --- a/app/models/translation_override.rb +++ b/app/models/translation_override.rb @@ -22,3 +22,19 @@ class TranslationOverride < ActiveRecord::Base end end + +# == Schema Information +# +# Table name: translation_overrides +# +# id :integer not null, primary key +# locale :string not null +# translation_key :string not null +# value :string not null +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_translation_overrides_on_locale_and_translation_key (locale,translation_key) UNIQUE +# diff --git a/app/models/user.rb b/app/models/user.rb index 9a30cbce94..40cf3bdacb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -169,8 +169,7 @@ class User < ActiveRecord::Base def self.suggest_name(email) return "" if email.blank? - name = email.split(/[@\+]/)[0].gsub(".", " ") - name.titleize + email[/\A[^@]+/].tr(".", " ").titleize end def self.find_by_username_or_email(username_or_email) @@ -621,7 +620,7 @@ class User < ActiveRecord::Base user_badges.select('distinct badge_id').count end - def featured_user_badges + def featured_user_badges(limit=3) user_badges .joins(:badge) .order("CASE WHEN badges.id = (SELECT MAX(ub2.badge_id) FROM user_badges ub2 @@ -631,7 +630,7 @@ class User < ActiveRecord::Base .includes(:user, :granted_by, badge: :badge_type) .where("user_badges.id in (select min(u2.id) from user_badges u2 where u2.user_id = ? group by u2.badge_id)", id) - .limit(3) + .limit(limit) end def self.count_by_signup_date(start_date, end_date) @@ -1092,6 +1091,7 @@ end # edit_history_public :boolean default(FALSE), not null # trust_level_locked :boolean default(FALSE), not null # staged :boolean default(FALSE), not null +# automatically_unpin_topics :boolean default(TRUE) # # Indexes # diff --git a/app/models/user_action.rb b/app/models/user_action.rb index 320b94d078..2fa8490eb4 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -242,10 +242,13 @@ SQL action.save! user_id = hash[:user_id] - update_like_count(user_id, hash[:action_type], 1) topic = Topic.includes(:category).find_by(id: hash[:target_topic_id]) + if topic && !topic.private_message? + update_like_count(user_id, hash[:action_type], 1) + end + # move into Topic perhaps group_ids = nil if topic && topic.category && topic.category.read_restricted diff --git a/app/models/user_archived_message.rb b/app/models/user_archived_message.rb index b24c26b54a..3f7c94e474 100644 --- a/app/models/user_archived_message.rb +++ b/app/models/user_archived_message.rb @@ -2,3 +2,18 @@ class UserArchivedMessage < ActiveRecord::Base belongs_to :user belongs_to :topic end + +# == Schema Information +# +# Table name: user_archived_messages +# +# id :integer not null, primary key +# user_id :integer not null +# topic_id :integer not null +# created_at :datetime +# updated_at :datetime +# +# Indexes +# +# index_user_archived_messages_on_user_id_and_topic_id (user_id,topic_id) UNIQUE +# diff --git a/app/models/user_badges.rb b/app/models/user_badges.rb new file mode 100644 index 0000000000..187ee1bc9f --- /dev/null +++ b/app/models/user_badges.rb @@ -0,0 +1,12 @@ +# view model for user badges +class UserBadges + alias :read_attribute_for_serialization :send + + attr_accessor :user_badges, :username, :grant_count + + def initialize(opts={}) + @user_badges = opts[:user_badges] + @username = opts[:username] + @grant_count = opts[:grant_count] + end +end diff --git a/app/models/user_email_observer.rb b/app/models/user_email_observer.rb index e19c51e2f7..67bcc5d6fe 100644 --- a/app/models/user_email_observer.rb +++ b/app/models/user_email_observer.rb @@ -42,18 +42,24 @@ class UserEmailObserver < ActiveRecord::Observer private + EMAILABLE_POST_TYPES ||= Set.new [Post.types[:regular], Post.types[:whisper]] + def enqueue(type, delay=default_delay) - return unless notification.user.email_direct? && (notification.user.active? || notification.user.staged?) + return unless notification.user.email_direct? + return unless notification.user.active? || notification.user.staged? + return unless EMAILABLE_POST_TYPES.include? notification.post.try(:post_type) Jobs.enqueue_in(delay, - :user_email, - type: type, - user_id: notification.user_id, - notification_id: notification.id) + :user_email, + type: type, + user_id: notification.user_id, + notification_id: notification.id) end def enqueue_private(type, delay=default_delay) - return unless notification.user.email_private_messages? && (notification.user.active? || notification.user.staged?) + return unless notification.user.email_private_messages? + return unless notification.user.active? || notification.user.staged? + return unless EMAILABLE_POST_TYPES.include? notification.post.try(:post_type) Jobs.enqueue_in(delay, :user_email, diff --git a/app/models/user_history.rb b/app/models/user_history.rb index 8382c9a6c9..252b5cc5b2 100644 --- a/app/models/user_history.rb +++ b/app/models/user_history.rb @@ -44,7 +44,9 @@ class UserHistory < ActiveRecord::Base change_category_settings: 26, delete_category: 27, create_category: 28, - change_site_text: 29) + change_site_text: 29, + block_user: 30, + unblock_user: 31) end # Staff actions is a subset of all actions, used to audit actions taken by staff users. @@ -70,7 +72,9 @@ class UserHistory < ActiveRecord::Base :reviewed_post, :change_category_settings, :delete_category, - :create_category] + :create_category, + :block_user, + :unblock_user] end def self.staff_action_ids diff --git a/app/models/user_stat.rb b/app/models/user_stat.rb index 3eb8ab9819..21590b9c80 100644 --- a/app/models/user_stat.rb +++ b/app/models/user_stat.rb @@ -30,8 +30,11 @@ class UserStat < ActiveRecord::Base (SELECT pt.user_id, COUNT(*) AS c FROM users AS u - INNER JOIN post_timings AS pt ON pt.user_id = u.id - WHERE u.last_seen_at > :seen_at + JOIN post_timings AS pt ON pt.user_id = u.id + JOIN topics t ON t.id = pt.topic_id + WHERE u.last_seen_at > :seen_at AND + t.archetype = 'regular' AND + t.deleted_at IS NULL GROUP BY pt.user_id) AS X WHERE X.user_id = user_stats.user_id AND X.c <> posts_read_count diff --git a/app/models/user_summary.rb b/app/models/user_summary.rb new file mode 100644 index 0000000000..2525a82b5f --- /dev/null +++ b/app/models/user_summary.rb @@ -0,0 +1,49 @@ +# ViewModel used on Summary tab on User page + +class UserSummary + + MAX_FEATURED_BADGES = 10 + MAX_TOPICS = 6 + + alias :read_attribute_for_serialization :send + + def initialize(user, guardian) + @user = user + @guardian = guardian + end + + def topics + Topic + .secured(@guardian) + .listable_topics + .where(user: @user) + .order('like_count desc, created_at asc') + .includes(:user, :category) + .limit(MAX_TOPICS) + end + + def replies + Post + .secured(@guardian) + .where(user: @user) + .where('post_number > 1') + .where('topics.archetype <> ?', Archetype.private_message) + .order('posts.like_count desc, posts.created_at asc') + .includes(:user, {topic: :category}) + .references(:topic) + .limit(MAX_TOPICS) + end + + def badges + @user.featured_user_badges(MAX_FEATURED_BADGES) + end + + def user_stat + @user.user_stat + end + + delegate :likes_given, :likes_received, :days_visited, + :posts_read_count, :topic_count, :post_count, + to: :user_stat + +end diff --git a/app/models/username_validator.rb b/app/models/username_validator.rb index e0ba6cfacb..1ef41e17ef 100644 --- a/app/models/username_validator.rb +++ b/app/models/username_validator.rb @@ -36,6 +36,8 @@ class UsernameValidator errors.empty? end + CONFUSING_EXTENSIONS ||= /\.(js|json|css|htm|html|xml|jpg|jpeg|png|gif|bmp|ico|tif|tiff|woff)$/i + private def username_exist? @@ -68,29 +70,29 @@ class UsernameValidator def username_first_char_valid? return unless errors.empty? - if username[0] =~ /[^\w]/ + if username[0] =~ /\W/ self.errors << I18n.t(:'user.username.must_begin_with_alphanumeric') end end def username_last_char_valid? return unless errors.empty? - if username[-1] =~ /[^\w]/ + if username[-1] =~ /[^A-Za-z0-9]/ self.errors << I18n.t(:'user.username.must_end_with_alphanumeric') end end def username_no_double_special? return unless errors.empty? - if username =~ /[\-_\.]{2,}/ + if username =~ /[-_.]{2,}/ self.errors << I18n.t(:'user.username.must_not_contain_two_special_chars_in_seq') end end def username_does_not_end_with_confusing_suffix? return unless errors.empty? - if username =~ /\.(json|gif|jpeg|png|htm|js|json|xml|woff|tif|html|ico)/i - self.errors << I18n.t(:'user.username.must_not_contain_confusing_suffix') + if username =~ CONFUSING_EXTENSIONS + self.errors << I18n.t(:'user.username.must_not_end_with_confusing_suffix') end end end diff --git a/app/serializers/admin_detailed_user_serializer.rb b/app/serializers/admin_detailed_user_serializer.rb index 29a93fc2cd..ea7bfbabba 100644 --- a/app/serializers/admin_detailed_user_serializer.rb +++ b/app/serializers/admin_detailed_user_serializer.rb @@ -56,10 +56,6 @@ class AdminDetailedUserSerializer < AdminUserSerializer scope.can_anonymize_user?(object) end - def moderator - object.moderator - end - def topic_count object.topics.count end @@ -72,18 +68,10 @@ class AdminDetailedUserSerializer < AdminUserSerializer object.suspend_record.try(:acting_user) end - def tl3_requirements - object.tl3_requirements - end - def include_tl3_requirements? object.has_trust_level?(TrustLevel[2]) end - def user_fields - object.user_fields - end - def include_user_fields? object.user_fields.present? end diff --git a/app/serializers/admin_user_list_serializer.rb b/app/serializers/admin_user_list_serializer.rb index df46788135..ba3953e5bb 100644 --- a/app/serializers/admin_user_list_serializer.rb +++ b/app/serializers/admin_user_list_serializer.rb @@ -23,7 +23,8 @@ class AdminUserListSerializer < BasicUserSerializer :suspended_till, :suspended, :blocked, - :time_read + :time_read, + :staged [:days_visited, :posts_read_count, :topics_entered, :post_count].each do |sym| attributes sym @@ -46,7 +47,7 @@ class AdminUserListSerializer < BasicUserSerializer def can_impersonate scope.can_impersonate?(object) end - + def last_emailed_at return nil if object.last_emailed_at.blank? object.last_emailed_at @@ -56,7 +57,7 @@ class AdminUserListSerializer < BasicUserSerializer return nil if object.last_emailed_at.blank? AgeWords.age_words(Time.now - object.last_emailed_at) end - + def last_seen_at return nil if object.last_seen_at.blank? object.last_seen_at @@ -71,10 +72,6 @@ class AdminUserListSerializer < BasicUserSerializer return nil if object.user_stat.time_read.blank? AgeWords.age_words(object.user_stat.time_read) end - - def created_at - object.created_at - end def created_at_age AgeWords.age_words(Time.now - object.created_at) diff --git a/app/serializers/badge_grouping_serializer.rb b/app/serializers/badge_grouping_serializer.rb index 7331a610cf..79abba78c0 100644 --- a/app/serializers/badge_grouping_serializer.rb +++ b/app/serializers/badge_grouping_serializer.rb @@ -1,3 +1,7 @@ class BadgeGroupingSerializer < ApplicationSerializer - attributes :id, :name, :description, :position + attributes :id, :name, :description, :position, :system + + def system + object.system? + end end diff --git a/app/serializers/basic_category_serializer.rb b/app/serializers/basic_category_serializer.rb index 2bf18d8561..c229b94faf 100644 --- a/app/serializers/basic_category_serializer.rb +++ b/app/serializers/basic_category_serializer.rb @@ -11,16 +11,15 @@ class BasicCategorySerializer < ApplicationSerializer :description, :description_text, :topic_url, + :logo_url, + :background_url, :read_restricted, :permission, :parent_category_id, :notification_level, - :logo_url, - :background_url, :can_edit, :topic_template, - :has_children, - :contains_messages + :has_children def include_parent_category_id? parent_category_id diff --git a/app/serializers/incoming_email_serializer.rb b/app/serializers/incoming_email_serializer.rb new file mode 100644 index 0000000000..53916e24cd --- /dev/null +++ b/app/serializers/incoming_email_serializer.rb @@ -0,0 +1,32 @@ +class IncomingEmailSerializer < ApplicationSerializer + + attributes :id, + :created_at, + :from_address, + :to_addresses, + :cc_addresses, + :subject, + :error, + :post_url + + has_one :user, serializer: BasicUserSerializer, embed: :objects + + def post_url + object.post.url + end + + def include_post_url? + object.post.present? + end + + def to_addresses + return if object.to_addresses.blank? + object.to_addresses.split(";") + end + + def cc_addresses + return if object.cc_addresses.blank? + object.cc_addresses.split(";") + end + +end diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb index 259c781d29..6e0ea158f4 100644 --- a/app/serializers/post_serializer.rb +++ b/app/serializers/post_serializer.rb @@ -38,6 +38,7 @@ class PostSerializer < BasicPostSerializer :can_edit, :can_delete, :can_recover, + :can_wiki, :link_counts, :read, :user_title, @@ -62,7 +63,8 @@ class PostSerializer < BasicPostSerializer :user_custom_fields, :static_doc, :via_email, - :action_code + :action_code, + :action_code_who def initialize(object, opts) super(object, opts) @@ -129,6 +131,10 @@ class PostSerializer < BasicPostSerializer scope.can_recover_post?(object) end + def can_wiki + scope.can_wiki?(object) + end + def display_username object.user.try(:name) end @@ -313,6 +319,14 @@ class PostSerializer < BasicPostSerializer object.action_code.present? end + def action_code_who + post_custom_fields["action_code_who"] + end + + def include_action_code_who? + include_action_code? && action_code_who.present? + end + private def post_actions diff --git a/app/serializers/user_action_serializer.rb b/app/serializers/user_action_serializer.rb index d9609ff9b4..965f4e61e4 100644 --- a/app/serializers/user_action_serializer.rb +++ b/app/serializers/user_action_serializer.rb @@ -31,7 +31,7 @@ class UserActionSerializer < ApplicationSerializer def excerpt cooked = object.cooked || PrettyText.cook(object.raw) - PrettyText.excerpt(cooked, 300, keep_emojis: true) if cooked + PrettyText.excerpt(cooked, 300, keep_emoji_images: true) if cooked end def avatar_template diff --git a/app/serializers/user_badge_serializer.rb b/app/serializers/user_badge_serializer.rb index cb6d2341b5..9a5e6d9ebd 100644 --- a/app/serializers/user_badge_serializer.rb +++ b/app/serializers/user_badge_serializer.rb @@ -1,9 +1,14 @@ class UserBadgeSerializer < ApplicationSerializer + + class UserSerializer < BasicUserSerializer + attributes :name, :moderator, :admin + end + attributes :id, :granted_at, :count, :post_id, :post_number has_one :badge - has_one :user, serializer: BasicUserSerializer, root: :users - has_one :granted_by, serializer: BasicUserSerializer, root: :users + has_one :user, serializer: UserSerializer, root: :users + has_one :granted_by, serializer: UserSerializer, root: :users has_one :topic, serializer: BasicTopicSerializer def include_count? diff --git a/app/serializers/user_badges_serializer.rb b/app/serializers/user_badges_serializer.rb new file mode 100644 index 0000000000..178438b177 --- /dev/null +++ b/app/serializers/user_badges_serializer.rb @@ -0,0 +1,4 @@ +class UserBadgesSerializer < ApplicationSerializer + has_many :user_badges, embed: :objects + attributes :grant_count, :username +end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 06abd889a1..480fbd9c61 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -229,7 +229,7 @@ class UserSerializer < BasicUserSerializer end def bio_excerpt - object.user_profile.bio_excerpt(350 , { keep_newlines: true, keep_emojis: true }) + object.user_profile.bio_excerpt(350 , { keep_newlines: true, keep_emoji_images: true }) end def include_suspend_reason? diff --git a/app/serializers/user_summary_serializer.rb b/app/serializers/user_summary_serializer.rb new file mode 100644 index 0000000000..4be285f4a0 --- /dev/null +++ b/app/serializers/user_summary_serializer.rb @@ -0,0 +1,18 @@ +class UserSummarySerializer < ApplicationSerializer + class TopicSerializer < BasicTopicSerializer + attributes :like_count, :slug, :created_at + end + + class ReplySerializer < ApplicationSerializer + attributes :post_number, :like_count, :created_at + has_one :topic, serializer: TopicSerializer + end + + has_many :topics, serializer: TopicSerializer + has_many :replies, serializer: ReplySerializer, embed: :object + has_many :badges, serializer: UserBadgeSerializer, embed: :object + + attributes :likes_given, :likes_received, :posts_read_count, + :days_visited, :topic_count, :post_count + +end diff --git a/app/services/badge_granter.rb b/app/services/badge_granter.rb index b6d964a655..7e237703d2 100644 --- a/app/services/badge_granter.rb +++ b/app/services/badge_granter.rb @@ -44,7 +44,7 @@ class BadgeGranter I18n.with_locale(@user.effective_locale) do notification = @user.notifications.create( notification_type: Notification.types[:granted_badge], - data: { badge_id: @badge.id, badge_name: @badge.display_name, badge_slug: @badge.slug }.to_json) + data: { badge_id: @badge.id, badge_name: @badge.display_name, badge_slug: @badge.slug, username: @user.username}.to_json) user_badge.update_attributes notification_id: notification.id end end diff --git a/app/services/spam_rule/auto_block.rb b/app/services/spam_rule/auto_block.rb index 2a5fa9dd66..8a6cea9f30 100644 --- a/app/services/spam_rule/auto_block.rb +++ b/app/services/spam_rule/auto_block.rb @@ -18,7 +18,8 @@ class SpamRule::AutoBlock def block? @user.blocked? or - (!@user.has_trust_level?(TrustLevel[1]) and + (!@user.staged? and + !@user.has_trust_level?(TrustLevel[1]) and SiteSetting.num_flags_to_block_new_user > 0 and SiteSetting.num_users_to_block_new_user > 0 and num_spam_flags_against_user >= SiteSetting.num_flags_to_block_new_user and diff --git a/app/services/spam_rule/flag_sockpuppets.rb b/app/services/spam_rule/flag_sockpuppets.rb index b6223df721..4c96a0687f 100644 --- a/app/services/spam_rule/flag_sockpuppets.rb +++ b/app/services/spam_rule/flag_sockpuppets.rb @@ -21,6 +21,8 @@ class SpamRule::FlagSockpuppets !first_post.user.staff? && !@post.user.staff? && + !first_post.user.staged? && + !@post.user.staged? && @post.user != first_post.user && @post.user.ip_address == first_post.user.ip_address && @post.user.new_user? && diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb index c076655102..a6354ecb24 100644 --- a/app/services/staff_action_logger.rb +++ b/app/services/staff_action_logger.rb @@ -134,10 +134,8 @@ class StaffActionLogger })) end - def log_site_text_change(subject, new_text, old_text, opts={}) + def log_site_text_change(subject, new_text=nil, old_text=nil, opts={}) raise Discourse::InvalidParameters.new(:subject) unless subject.present? - raise Discourse::InvalidParameters.new(:new_text) unless new_text.present? - raise Discourse::InvalidParameters.new(:old_text) unless old_text.present? UserHistory.create( params(opts).merge({ action: UserHistory.actions[:change_site_text], subject: subject, @@ -280,6 +278,22 @@ class StaffActionLogger })) end + def log_block_user(user, opts={}) + raise Discourse::InvalidParameters.new(:user) unless user + UserHistory.create( params(opts).merge({ + action: UserHistory.actions[:block_user], + target_user_id: user.id + })) + end + + def log_unblock_user(user, opts={}) + raise Discourse::InvalidParameters.new(:user) unless user + UserHistory.create( params(opts).merge({ + action: UserHistory.actions[:unblock_user], + target_user_id: user.id + })) + end + private def params(opts=nil) diff --git a/app/services/user_blocker.rb b/app/services/user_blocker.rb index 9a8dd74935..1ff9983b7e 100644 --- a/app/services/user_blocker.rb +++ b/app/services/user_blocker.rb @@ -13,11 +13,12 @@ class UserBlocker end def block - hide_posts + hide_posts unless @opts[:keep_posts] unless @user.blocked? @user.blocked = true if @user.save SystemMessage.create(@user, @opts[:message] || :blocked_by_staff) + StaffActionLogger.new(@by_user).log_block_user(@user) if @by_user end else false @@ -34,6 +35,7 @@ class UserBlocker @user.blocked = false if @user.save SystemMessage.create(@user, :unblocked) + StaffActionLogger.new(@by_user).log_unblock_user(@user) if @by_user end end diff --git a/app/views/email/_post.html.erb b/app/views/email/_post.html.erb index 48da134ce4..75510c19a9 100644 --- a/app/views/email/_post.html.erb +++ b/app/views/email/_post.html.erb @@ -1,4 +1,4 @@ - +
    '; - } var arr = []; + if (opts.isEmpty) { + if (opts.showDaysInNextAndPreviousMonths) { + arr.push('is-outside-current-month'); + } else { + return ''; + } + } if (opts.isDisabled) { arr.push('is-disabled'); } @@ -417,7 +427,7 @@ return; } - if (!hasClass(target.parentNode, 'is-disabled')) { + if (!hasClass(target, 'is-disabled')) { if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty')) { self.setDate(new Date(target.getAttribute('data-pika-year'), target.getAttribute('data-pika-month'), target.getAttribute('data-pika-day'))); if (opts.bound) { @@ -472,7 +482,7 @@ return; } if (hasMoment) { - date = moment(opts.field.value, opts.format); + date = moment(opts.field.value, opts.format, opts.formatStrict); date = (date && date.isValid()) ? date.toDate() : null; } else { @@ -638,9 +648,7 @@ this.setMinDate(opts.minDate); } if (opts.maxDate) { - setToStartOfDay(opts.maxDate); - opts.maxYear = opts.maxDate.getFullYear(); - opts.maxMonth = opts.maxDate.getMonth(); + this.setMaxDate(opts.maxDate); } if (isArray(opts.yearRange)) { @@ -828,6 +836,7 @@ this._o.minDate = value; this._o.minYear = value.getFullYear(); this._o.minMonth = value.getMonth(); + this.draw(); }, /** @@ -835,7 +844,11 @@ */ setMaxDate: function(value) { + setToStartOfDay(value); this._o.maxDate = value; + this._o.maxYear = value.getFullYear(); + this._o.maxMonth = value.getMonth(); + this.draw(); }, setStartRange: function(value) @@ -967,6 +980,11 @@ before += 7; } } + var previousMonth = month === 0 ? 11 : month - 1, + nextMonth = month === 11 ? 0 : month + 1, + yearOfPreviousMonth = month === 0 ? year - 1 : year, + yearOfNextMonth = month === 11 ? year + 1 : year, + daysInPreviousMonth = getDaysInMonth(yearOfPreviousMonth, previousMonth); var cells = days + before, after = cells; while(after > 7) { @@ -979,24 +997,41 @@ isSelected = isDate(this._d) ? compareDates(day, this._d) : false, isToday = compareDates(day, now), isEmpty = i < before || i >= (days + before), + dayNumber = 1 + (i - before), + monthNumber = month, + yearNumber = year, isStartRange = opts.startRange && compareDates(opts.startRange, day), isEndRange = opts.endRange && compareDates(opts.endRange, day), isInRange = opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange, isDisabled = (opts.minDate && day < opts.minDate) || (opts.maxDate && day > opts.maxDate) || (opts.disableWeekends && isWeekend(day)) || - (opts.disableDayFn && opts.disableDayFn(day)), - dayConfig = { - day: 1 + (i - before), - month: month, - year: year, + (opts.disableDayFn && opts.disableDayFn(day)); + + if (isEmpty) { + if (i < before) { + dayNumber = daysInPreviousMonth + dayNumber; + monthNumber = previousMonth; + yearNumber = yearOfPreviousMonth; + } else { + dayNumber = dayNumber - days; + monthNumber = nextMonth; + yearNumber = yearOfNextMonth; + } + } + + var dayConfig = { + day: dayNumber, + month: monthNumber, + year: yearNumber, isSelected: isSelected, isToday: isToday, isDisabled: isDisabled, isEmpty: isEmpty, isStartRange: isStartRange, isEndRange: isEndRange, - isInRange: isInRange + isInRange: isInRange, + showDaysInNextAndPreviousMonths: opts.showDaysInNextAndPreviousMonths }; row.push(renderDay(dayConfig)); diff --git a/script/import_scripts/jive.rb b/script/import_scripts/jive.rb index bb5cedd854..c64e87a66b 100644 --- a/script/import_scripts/jive.rb +++ b/script/import_scripts/jive.rb @@ -6,7 +6,7 @@ require File.expand_path(File.dirname(__FILE__) + "/base.rb") class ImportScripts::Jive < ImportScripts::Base BATCH_SIZE = 1000 - CATEGORY_IDS = [2023,2003,2004,2042,2036,2029] # categories that should be skipped + CATEGORY_IDS = [2023,2003,2004,2042,2036,2029] # categories that should be imported def initialize(path) @path = path @@ -127,7 +127,7 @@ class ImportScripts::Jive < ImportScripts::Base puts "", "importing groups..." rows = [] - csv_parse("group") do |row| + csv_parse("groups") do |row| rows << {id: row.groupid, name: row.name} end @@ -142,14 +142,13 @@ class ImportScripts::Jive < ImportScripts::Base count = 0 users = [] - total = total_rows("user") + total = total_rows("users") - csv_parse("user") do |row| + csv_parse("users") do |row| id = row.userid - # append "-x" at the end of each email - email = "#{row.email}-x" + email = "#{row.email}" # fake it if row.email.blank? || row.email !~ /@/ @@ -174,7 +173,7 @@ class ImportScripts::Jive < ImportScripts::Base created_at: created_at, last_seen_at: last_seen_at, active: is_activated.to_i == 1, - approved: is_activated.to_i == 1 + approved: true } count += 1 @@ -203,7 +202,7 @@ class ImportScripts::Jive < ImportScripts::Base def import_categories rows = [] - csv_parse("community") do |row| + csv_parse("communities") do |row| next unless CATEGORY_IDS.include?(row.communityid.to_i) rows << {id: row.communityid, name: "#{row.name} (#{row.communityid})"} end @@ -279,7 +278,7 @@ class ImportScripts::Jive < ImportScripts::Base topic_map = {} thread_map = {} - csv_parse("message") do |thread| + csv_parse("messages") do |thread| next unless CATEGORY_IDS.include?(thread.containerid.to_i) @@ -288,6 +287,37 @@ class ImportScripts::Jive < ImportScripts::Base thread_map[thread.threadid] = thread.messageid + #IMAGE UPLOADER + if thread.imagecount + Dir.foreach("/var/www/discourse/script/import_scripts/jive/img/#{thread.messageid}") do |item| + next if item == '.' or item == '..' or item == '.DS_Store' + photo_path = "/var/www/discourse/script/import_scripts/jive/img/#{thread.messageid}/#{item}" + upload = create_upload(thread.userid, photo_path, File.basename(photo_path)) + if upload.persisted? + puts "Image upload is successful for #{photo_path}, new path is #{upload.url}!" + thread.body.gsub!(item,upload.url) + else + puts "Error: Image upload is not successful for #{photo_path}!" + end + end + end + + #ATTACHMENT UPLOADER + if thread.attachmentcount + Dir.foreach("/var/www/discourse/script/import_scripts/jive/attach/#{thread.messageid}") do |item| + next if item == '.' or item == '..' or item == '.DS_Store' + attach_path = "/var/www/discourse/script/import_scripts/jive/attach/#{thread.messageid}/#{item}" + upload = create_upload(thread.userid, attach_path, File.basename(attach_path)) + if upload.persisted? + puts "Attachment upload is successful for #{attach_path}, new path is #{upload.url}!" + thread.body.gsub!(item,upload.url) + thread.body << "

    #{attachment_html(upload,item)}" + else + puts "Error: Attachment upload is not successful for #{attach_path}!" + end + end + end + topic_map[thread.messageid] = { id: thread.messageid, topic_id: thread.messageid, @@ -301,7 +331,7 @@ class ImportScripts::Jive < ImportScripts::Base end end - total = total_rows("message") + total = total_rows("messages") posts = [] count = 0 @@ -310,12 +340,44 @@ class ImportScripts::Jive < ImportScripts::Base count+=1 end - csv_parse("message") do |thread| + csv_parse("messages") do |thread| # post next unless CATEGORY_IDS.include?(thread.containerid.to_i) if thread.parentmessageid + + #IMAGE UPLOADER + if thread.imagecount + Dir.foreach("/var/www/discourse/script/import_scripts/jive/img/#{thread.messageid}") do |item| + next if item == '.' or item == '..' or item == '.DS_Store' + photo_path = "/var/www/discourse/script/import_scripts/jive/img/#{thread.messageid}/#{item}" + upload = create_upload(thread.userid, photo_path, File.basename(photo_path)) + if upload.persisted? + puts "Image upload is successful for #{photo_path}, new path is #{upload.url}!" + thread.body.gsub!(item,upload.url) + else + puts "Error: Image upload is not successful for #{photo_path}!" + end + end + end + + #ATTACHMENT UPLOADER + if thread.attachmentcount + Dir.foreach("/var/www/discourse/script/import_scripts/jive/attach/#{thread.messageid}") do |item| + next if item == '.' or item == '..' or item == '.DS_Store' + attach_path = "/var/www/discourse/script/import_scripts/jive/attach/#{thread.messageid}/#{item}" + upload = create_upload(thread.userid, attach_path, File.basename(attach_path)) + if upload.persisted? + puts "Attachment upload is successful for #{attach_path}, new path is #{upload.url}!" + thread.body.gsub!(item,upload.url) + thread.body << "

    #{attachment_html(upload,item)}" + else + puts "Error: Attachment upload is not successful for #{attach_path}!" + end + end + end + row = { id: thread.messageid, topic_id: thread_map["#{thread.threadid}"], diff --git a/script/import_scripts/phpbb3/importers/attachment_importer.rb b/script/import_scripts/phpbb3/importers/attachment_importer.rb index e41ca7a120..37f7695c9c 100644 --- a/script/import_scripts/phpbb3/importers/attachment_importer.rb +++ b/script/import_scripts/phpbb3/importers/attachment_importer.rb @@ -22,7 +22,7 @@ module ImportScripts::PhpBB3 filename = CGI.unescapeHTML(row[:real_filename]) upload = @uploader.create_upload(user_id, path, filename) - if upload.nil? || !upload.valid? + if upload.nil? || !upload.persisted? puts "Failed to upload #{path}" puts upload.errors.inspect if upload else diff --git a/script/import_scripts/phpbb3/importers/avatar_importer.rb b/script/import_scripts/phpbb3/importers/avatar_importer.rb index 3db8b70100..825bc445fa 100644 --- a/script/import_scripts/phpbb3/importers/avatar_importer.rb +++ b/script/import_scripts/phpbb3/importers/avatar_importer.rb @@ -24,14 +24,15 @@ module ImportScripts::PhpBB3 filename = "avatar#{File.extname(path)}" upload = @uploader.create_upload(user.id, path, filename) - if upload.persisted? + if upload.present? && upload.persisted? user.import_mode = false user.create_user_avatar user.import_mode = true user.user_avatar.update(custom_upload_id: upload.id) user.update(uploaded_avatar_id: upload.id) else - Rails.logger.error("Could not persist avatar for user #{user.username}") + puts "Failed to upload avatar for user #{user.username}: #{path}" + puts upload.errors.inspect if upload end rescue SystemCallError => err Rails.logger.error("Could not import avatar for user #{user.username}: #{err.message}") diff --git a/script/import_scripts/phpbb3/importers/poll_importer.rb b/script/import_scripts/phpbb3/importers/poll_importer.rb index b3099f20c1..e6bae5b40b 100644 --- a/script/import_scripts/phpbb3/importers/poll_importer.rb +++ b/script/import_scripts/phpbb3/importers/poll_importer.rb @@ -22,6 +22,8 @@ module ImportScripts::PhpBB3 poll_text = get_poll_text(options, poll) extracted_poll = extract_default_poll(topic_id, poll_text) + return if extracted_poll.nil? + update_poll(extracted_poll, options, topic_id, poll) mapped_poll = { @@ -83,6 +85,9 @@ module ImportScripts::PhpBB3 extracted_polls.each do |poll| return poll if poll['name'] == @default_poll_name end + + puts "Failed to extract poll for topic id #{topic_id}. The poll text is:" + puts poll_text end # @param poll [ImportScripts::PhpBB3::Poll] diff --git a/script/import_scripts/phpbb3/support/smiley_processor.rb b/script/import_scripts/phpbb3/support/smiley_processor.rb index f79a24c465..1737e446ce 100644 --- a/script/import_scripts/phpbb3/support/smiley_processor.rb +++ b/script/import_scripts/phpbb3/support/smiley_processor.rb @@ -65,7 +65,7 @@ module ImportScripts::PhpBB3 filename = File.basename(path) upload = @uploader.create_upload(Discourse::SYSTEM_USER_ID, path, filename) - if upload.nil? || !upload.valid? + if upload.nil? || !upload.persisted? puts "Failed to upload #{path}" puts upload.errors.inspect if upload html = nil diff --git a/script/import_scripts/vanilla_mysql.rb b/script/import_scripts/vanilla_mysql.rb index e472ce525b..13237e7b87 100644 --- a/script/import_scripts/vanilla_mysql.rb +++ b/script/import_scripts/vanilla_mysql.rb @@ -7,6 +7,7 @@ class ImportScripts::VanillaSQL < ImportScripts::Base VANILLA_DB = "vanilla_mysql" TABLE_PREFIX = "GDN_" BATCH_SIZE = 1000 + CONVERT_HTML = true def initialize super @@ -29,6 +30,10 @@ class ImportScripts::VanillaSQL < ImportScripts::Base def import_users puts '', "creating users" + @user_is_deleted = false + @last_deleted_username = nil + username = nil + total_count = mysql_query("SELECT count(*) count FROM #{TABLE_PREFIX}User;").first['count'] batches(BATCH_SIZE) do |offset| @@ -42,21 +47,37 @@ class ImportScripts::VanillaSQL < ImportScripts::Base break if results.size < 1 - next if all_records_exist? :users, users.map {|u| u['UserID'].to_i} + next if all_records_exist? :users, results.map {|u| u['UserID'].to_i} create_users(results, total: total_count, offset: offset) do |user| next if user['Email'].blank? next if user['Name'].blank? + + if user['Name'] == '[Deleted User]' + # EVERY deleted user record in Vanilla has the same username: [Deleted User] + # Save our UserNameSuggester some pain: + @user_is_deleted = true + username = @last_deleted_username || user['Name'] + else + @user_is_deleted = false + username = user['Name'] + end + { id: user['UserID'], email: user['Email'], - username: user['Name'], + username: username, name: user['Name'], created_at: user['DateInserted'] == nil ? 0 : Time.zone.at(user['DateInserted']), bio_raw: user['About'], registration_ip_address: user['InsertIPAddress'], last_seen_at: user['DateLastActive'] == nil ? 0 : Time.zone.at(user['DateLastActive']), location: user['Location'], - admin: user['Admin'] == 1 } + admin: user['Admin'] == 1, + post_create_action: proc do |newuser| + if @user_is_deleted + @last_deleted_username = newuser.username + end + end } end end end @@ -169,17 +190,19 @@ class ImportScripts::VanillaSQL < ImportScripts::Base # [SAMP]...[/SAMP] raw.gsub!(/\[\/?samp\]/i, "`") - # replace all chevrons with HTML entities - # NOTE: must be done - # - AFTER all the "code" processing - # - BEFORE the "quote" processing - raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub("<", "\u2603") + "`" } - .gsub("<", "<") - .gsub("\u2603", "<") + unless CONVERT_HTML + # replace all chevrons with HTML entities + # NOTE: must be done + # - AFTER all the "code" processing + # - BEFORE the "quote" processing + raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub("<", "\u2603") + "`" } + .gsub("<", "<") + .gsub("\u2603", "<") - raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub(">", "\u2603") + "`" } - .gsub(">", ">") - .gsub("\u2603", ">") + raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub(">", "\u2603") + "`" } + .gsub(">", ">") + .gsub("\u2603", ">") + end # [URL=...]...[/URL] raw.gsub!(/\[url="?(.+?)"?\](.+)\[\/url\]/i) { "[#{$2}](#{$1})" } @@ -224,7 +247,8 @@ class ImportScripts::VanillaSQL < ImportScripts::Base end def mysql_query(sql) - @client.query(sql, cache_rows: false) + @client.query(sql) + # @client.query(sql, cache_rows: false) #segfault: cache_rows: false causes segmentation fault end end diff --git a/spec/components/concern/category_hashtag_spec.rb b/spec/components/concern/category_hashtag_spec.rb new file mode 100644 index 0000000000..be60931f79 --- /dev/null +++ b/spec/components/concern/category_hashtag_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' + +describe CategoryHashtag do + describe '#query_from_hashtag_slug' do + let(:parent_category) { Fabricate(:category) } + let(:child_category) { Fabricate(:category, parent_category: parent_category) } + + it "should return the right result for a parent category slug" do + expect(Category.query_from_hashtag_slug(parent_category.slug)) + .to eq(parent_category) + end + + it "should return the right result for a parent and child category slug" do + expect(Category.query_from_hashtag_slug("#{parent_category.slug}#{CategoryHashtag::SEPARATOR}#{child_category.slug}")) + .to eq(child_category) + end + + it "should return nil for incorrect parent category slug" do + expect(Category.query_from_hashtag_slug("random-slug")).to eq(nil) + end + + it "should return nil for incorrect parent and child category slug" do + expect(Category.query_from_hashtag_slug("random-slug#{CategoryHashtag::SEPARATOR}random-slug")).to eq(nil) + end + end +end diff --git a/spec/components/email/message_builder_spec.rb b/spec/components/email/message_builder_spec.rb index 5f8a1d7638..8ff2dc5114 100644 --- a/spec/components/email/message_builder_spec.rb +++ b/spec/components/email/message_builder_spec.rb @@ -181,6 +181,23 @@ describe Email::MessageBuilder do end + context "with unsubscribe_via_email_link true" do + let(:message_with_unsubscribe_via_email) { Email::MessageBuilder.new(to_address, + body: 'hello world', + add_unsubscribe_link: true, + add_unsubscribe_via_email_link: true, + unsubscribe_url: "/t/1234/unsubscribe") } + + it "can add an unsubscribe via email link" do + SiteSetting.stubs(:unsubscribe_via_email_footer).returns(true) + expect(message_with_unsubscribe_via_email.body).to match(/mailto:reply@#{Discourse.current_hostname}\?subject=unsubscribe/) + end + + it "does not add unsubscribe via email link without site setting set" do + expect(message_with_unsubscribe_via_email.body).to_not match(/mailto:reply@#{Discourse.current_hostname}\?subject=unsubscribe/) + end + end + end context "template_args" do diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index 796ce440d4..1296cb9915 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -1,791 +1,293 @@ -# -*- encoding : utf-8 -*- - -require 'rails_helper' -require 'email/receiver' +require "rails_helper" +require "email/receiver" describe Email::Receiver do before do - SiteSetting.reply_by_email_address = "reply+%{reply_key}@appmail.adventuretime.ooo" - SiteSetting.email_in = false - SiteSetting.title = "Discourse" + SiteSetting.email_in = true + SiteSetting.reply_by_email_address = "reply+%{reply_key}@bar.com" end - describe 'parse_body' do - def test_parse_body(mail_string) - Email::Receiver.new(nil).parse_body(Mail::Message.new mail_string) + def email(email_name) + fixture_file("emails/#{email_name}.eml") + end + + def process(email_name) + Email::Receiver.new(email(email_name)).process + end + + it "raises an EmptyEmailError when 'mail_string' is blank" do + expect { Email::Receiver.new(nil) }.to raise_error(Email::Receiver::EmptyEmailError) + expect { Email::Receiver.new("") }.to raise_error(Email::Receiver::EmptyEmailError) + end + + it "raises an NoMessageIdError when 'mail_string' is not an email" do + expect { Email::Receiver.new("wat") }.to raise_error(Email::Receiver::NoMessageIdError) + end + + it "raises an NoMessageIdError when 'mail_string' is missing the message_id" do + expect { Email::Receiver.new(email(:missing_message_id)) }.to raise_error(Email::Receiver::NoMessageIdError) + end + + it "raises an AutoGeneratedEmailError when the mail has no return path" do + expect { process(:no_return_path) }.to raise_error(Email::Receiver::AutoGeneratedEmailError) + end + + it "raises an AutoGeneratedEmailError when the mail is auto generated" do + expect { process(:auto_generated_precedence) }.to raise_error(Email::Receiver::AutoGeneratedEmailError) + expect { process(:auto_generated_header) }.to raise_error(Email::Receiver::AutoGeneratedEmailError) + end + + it "raises a NoBodyDetectedError when the body is blank" do + expect { process(:no_body) }.to raise_error(Email::Receiver::NoBodyDetectedError) + end + + it "raises an InactiveUserError when the sender is inactive" do + Fabricate(:user, email: "inactive@bar.com", active: false) + expect { process(:inactive_sender) }.to raise_error(Email::Receiver::InactiveUserError) + end + + skip "doesn't raise an InactiveUserError when the sender is staged" do + Fabricate(:user, email: "staged@bar.com", active: false, staged: true) + expect { process(:staged_sender) }.not_to raise_error + end + + it "raises a BadDestinationAddress when destinations aren't matching any of the incoming emails" do + expect { process(:bad_destinations) }.to raise_error(Email::Receiver::BadDestinationAddress) + end + + context "reply" do + + let(:reply_key) { "4f97315cc828096c9cb34c6f1a0d6fe8" } + let(:user) { Fabricate(:user, email: "discourse@bar.com") } + let(:topic) { create_topic(user: user) } + let(:post) { create_post(topic: topic, user: user) } + let!(:email_log) { Fabricate(:email_log, reply_key: reply_key, user: user, topic: topic, post: post) } + + it "raises a ReplyUserNotMatchingError when the email address isn't matching the one we sent the notification to" do + expect { process(:reply_user_not_matching) }.to raise_error(Email::Receiver::ReplyUserNotMatchingError) end - it "raises EmptyEmailError if the message is blank" do - expect { test_parse_body("") }.to raise_error(Email::Receiver::EmptyEmailError) + it "raises a TopicNotFoundError when the topic was deleted" do + topic.update_columns(deleted_at: 1.day.ago) + expect { process(:reply_user_matching) }.to raise_error(Email::Receiver::TopicNotFoundError) end - it "raises EmptyEmailError if the message is not an email" do - expect { test_parse_body("asdf" * 30) }.to raise_error(Email::Receiver::EmptyEmailError) + it "raises a TopicClosedError when the topic was closed" do + topic.update_columns(closed: true) + expect { process(:reply_user_matching) }.to raise_error(Email::Receiver::TopicClosedError) end - it "raises EmptyEmailError if there is no reply content" do - expect { test_parse_body(fixture_file("emails/no_content_reply.eml")) }.to raise_error(Email::Receiver::EmptyEmailError) + it "raises an InvalidPost when there was an error while creating the post" do + expect { process(:too_small) }.to raise_error(Email::Receiver::InvalidPost) end - skip "raises EmailUnparsableError if the headers are corrupted" do - expect { ; }.to raise_error(Email::Receiver::EmailUnparsableError) + it "raises an InvalidPost when there are too may mentions" do + SiteSetting.max_mentions_per_post = 1 + Fabricate(:user, username: "user1") + Fabricate(:user, username: "user2") + expect { process(:too_many_mentions) }.to raise_error(Email::Receiver::InvalidPost) end - it "can parse the html section" do - expect(test_parse_body(fixture_file("emails/html_only.eml"))).to eq("The EC2 instance - I've seen that there tends to be odd and " + - "unrecommended settings on the Bitnami installs that I've checked out.") + it "raises an InvalidPostAction when they aren't allowed to like a post" do + topic.update_columns(archived: true) + expect { process(:like) }.to raise_error(Email::Receiver::InvalidPostAction) end - it "supports a Dutch reply" do - expect(test_parse_body(fixture_file("emails/dutch.eml"))).to eq("Dit is een antwoord in het Nederlands.") + it "works" do + expect { process(:text_reply) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("This is a text reply :)") + expect(topic.posts.last.via_email).to eq(true) + expect(topic.posts.last.cooked).not_to match(/
    HTML reply ;)") + + expect { process(:hebrew_reply) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("שלום! מה שלומך היום?") + + expect { process(:chinese_reply) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("您好! 你今天好吗?") end - it "supports a Hebrew reply" do - I18n.stubs(:t).with('user_notifications.previous_discussion').returns('כלטוב') - - # The force_encoding call is only needed for the test - it is passed on fine to the cooked post - expect(test_parse_body(fixture_file("emails/hebrew.eml"))).to eq("שלום") + it "prefers text over html" do + expect { process(:text_and_html_reply) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("This is the *text* part.") end - it "supports a BIG5-encoded reply" do - # The force_encoding call is only needed for the test - it is passed on fine to the cooked post - expect(test_parse_body(fixture_file("emails/big5.eml"))).to eq("媽!我上電視了!") + it "removes the 'on , wrote' quoting line" do + expect { process(:on_date_contact_wrote) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("This is the actual reply.") end - it "removes 'via' lines if they match the site title" do - SiteSetting.title = "Discourse" - - expect(test_parse_body(fixture_file("emails/via_line.eml"))).to eq("Hello this email has content!") - end - - it "removes an 'on date wrote' quoting line" do - expect(test_parse_body(fixture_file("emails/on_wrote.eml"))).to eq("Sure, all you need to do is frobnicate the foobar and you'll be all set!") - end - - it "removes the 'Previous Discussion' marker" do - expect(test_parse_body(fixture_file("emails/previous.eml"))).to eq("This will not include the previous discussion that is present in this email.") + it "removes the 'Previous Replies' marker" do + expect { process(:previous_replies) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("This will not include the previous discussion that is present in this email.") end it "handles multiple paragraphs" do - expect(test_parse_body(fixture_file("emails/paragraphs.eml"))). - to eq( -"Is there any reason the *old* candy can't be be kept in silos while the new candy -is imported into *new* silos? - -The thing about candy is it stays delicious for a long time -- we can just keep -it there without worrying about it too much, imo. - -Thanks for listening." - ) + expect { process(:paragraphs) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("Do you like liquorice?\n\nI really like them. One could even say that I am *addicted* to liquorice. Anf if\nyou can mix it up with some anise, then I'm in heaven ;)") end - it "handles multiple paragraphs when parsing html" do - expect(test_parse_body(fixture_file("emails/html_paragraphs.eml"))). - to eq( -"Awesome! + describe 'Unsubscribing via email' do + let(:last_email) { ActionMailer::Base.deliveries.last } -Pleasure to have you here! + describe 'unsubscribe_subject.eml' do + it 'sends an email asking the user to confirm the unsubscription' do + expect { process("unsubscribe_subject") }.to change { ActionMailer::Base.deliveries.count }.by(1) + expect(last_email.to.length).to eq 1 + expect(last_email.from.length).to eq 1 + expect(last_email.from).to include "noreply@#{Discourse.current_hostname}" + expect(last_email.to).to include "discourse@bar.com" + expect(last_email.subject).to eq I18n.t(:"unsubscribe_mailer.subject_template").gsub("%{site_title}", SiteSetting.title) + end -:boom:" - ) - end + it 'does nothing unless unsubscribe_via_email is turned on' do + SiteSetting.stubs("unsubscribe_via_email").returns(false) + before_deliveries = ActionMailer::Base.deliveries.count + expect { process("unsubscribe_subject") }.to raise_error { Email::Receiver::BadDestinationAddress } + expect(before_deliveries).to eq ActionMailer::Base.deliveries.count + end + end - it "handles newlines" do - expect(test_parse_body(fixture_file("emails/newlines.eml"))). - to eq( -"This is my reply. -It is my best reply. -It will also be my *only* reply." - ) + describe 'unsubscribe_body.eml' do + it 'sends an email asking the user to confirm the unsubscription' do + expect { process("unsubscribe_body") }.to change { ActionMailer::Base.deliveries.count }.by(1) + expect(last_email.to.length).to eq 1 + expect(last_email.from.length).to eq 1 + expect(last_email.from).to include "noreply@#{Discourse.current_hostname}" + expect(last_email.to).to include "discourse@bar.com" + expect(last_email.subject).to eq I18n.t(:"unsubscribe_mailer.subject_template").gsub("%{site_title}", SiteSetting.title) + end + + it 'does nothing unless unsubscribe_via_email is turned on' do + SiteSetting.stubs(:unsubscribe_via_email).returns(false) + before_deliveries = ActionMailer::Base.deliveries.count + expect { process("unsubscribe_body") }.to raise_error { Email::Receiver::InvalidPost } + expect(before_deliveries).to eq ActionMailer::Base.deliveries.count + end + end end it "handles inline reply" do - expect(test_parse_body(fixture_file("emails/inline_reply.eml"))). - to eq( -"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." - ) + expect { process(:inline_reply) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("On Tue, Jan 15, 2016 at 11:12 AM, Bar Foo wrote:\n\n> WAT November 28\n>\n> This is the previous post.\n\nAnd this is *my* reply :+1:") end - it "can retrieve the first part of multiple replies" do - expect(test_parse_body(fixture_file("emails/inline_mixed.eml"))).to eq( -"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. - -> First paragraph. -> -> Second paragraph. - -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" - ) - + it "retrieves the first part of multiple replies" do + expect { process(:inline_mixed_replies) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("On Tue, Jan 15, 2016 at 11:12 AM, Bar Foo wrote:\n\n> WAT November 28\n>\n> This is the previous post.\n\nAnd this is *my* reply :+1:\n\n> This is another post.\n\nAnd this is **another** reply.") end - it "should not include previous replies" do - expect(test_parse_body(fixture_file("emails/previous_replies.eml"))).not_to match(/Previous Replies/) - end + it "strips signatures" do + expect { process(:iphone_signature) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("This is not the signature you're looking for.") - it "strips iPhone signature" do - expect(test_parse_body(fixture_file("emails/iphone_signature.eml"))).not_to match(/Sent from my iPhone/) - end - - it "strips regular signature" do - expect(test_parse_body(fixture_file("emails/signature.eml"))).not_to match(/Arpit/) + expect { process(:signature) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("You shall not sign!") end it "strips 'original message' context" do - expect(test_parse_body(fixture_file("emails/original_message_context.eml"))).not_to match(/Context/) + expect { process(:original_message) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to eq("This is a reply :)") end - it "properly renders email reply from gmail web client" do - expect(test_parse_body(fixture_file("emails/gmail_web.eml"))). - to eq( -"### This is a reply from standard GMail in Google Chrome. + it "supports attachments" do + expect { process(:no_body_with_attachments) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to match(//) - expect(Upload.find_by(sha1: upload_sha)).not_to eq(nil) - end - - describe 'Liking via email' do - let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } - let(:replied_user_like_params) { { user: replying_user, post: post, post_action_type_id: PostActionType.types[:like] } } - let(:replied_user_like) { PostAction.find_by(replied_user_like_params) } - - describe "plus_one.eml" do - let!(:email_raw) { - fixture_file("emails/plus_one.eml") - .gsub("TO", "reply+#{reply_key}@appmail.adventuretime.ooo") - .gsub("FROM", replying_user_email) - } - - it "adds a user like to the post" do - expect { receiver.process }.to change { PostAction.count }.by(1) - expect(replied_user_like).to be_present - end - - it "does not create a duplicate like" do - PostAction.create(replied_user_like_params) - before_count = PostAction.count - expect { receiver.process }.to raise_error(Email::Receiver::InvalidPostAction) - expect(PostAction.count).to eq before_count - expect(replied_user_like).to be_present - end - - it "does not allow unauthorized happiness" do - post.trash! - before_count = PostAction.count - expect { receiver.process }.to raise_error(Email::Receiver::InvalidPostAction) - expect(PostAction.count).to eq before_count - expect(replied_user_like).to_not be_present - end - end - - describe "like.eml" do - let!(:email_raw) { - fixture_file("emails/like.eml") - .gsub("TO", "reply+#{reply_key}@appmail.adventuretime.ooo") - .gsub("FROM", replying_user_email) - } - - it 'adds a user like to the post' do - expect { receiver.process }.to change { PostAction.count }.by(1) - expect(replied_user_like).to be_present - end - end - end - end - - # === Failure Conditions === - - describe "too_short.eml" do - let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } - let!(:email_raw) { - fixture_file("emails/too_short.eml") - .gsub("TO", "reply+#{reply_key}@appmail.adventuretime.ooo") - .gsub("FROM", replying_user_email) - .gsub("SUBJECT", "re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'") - } - - it "raises an InvalidPost error" do - SiteSetting.min_post_length = 5 - expect { receiver.process }.to raise_error(Email::Receiver::InvalidPost) - end - end - - describe "too_many_mentions.eml" do - let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } - let!(:email_raw) { fixture_file("emails/too_many_mentions.eml") } - - it "raises an InvalidPost error" do - SiteSetting.max_mentions_per_post = 10 - (1..11).each do |i| - Fabricate(:user, username: "user#{i}").save - end - - expect { receiver.process }.to raise_error(Email::Receiver::InvalidPost) - end - end - - describe "auto response email replies should not be accepted" do - let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } - let!(:email_raw) { fixture_file("emails/auto_reply.eml") } - it "raises a AutoGeneratedEmailError" do - expect { receiver.process }.to raise_error(Email::Receiver::AutoGeneratedEmailError) - end + it "ensures posts aren't dated in the future" do + expect { process(:from_the_future) }.to change { topic.posts.count } + expect(topic.posts.last.created_at).to be_within(1.minute).of(DateTime.now) end end - describe "posting reply to a closed topic" do - let(:reply_key) { raise "Override this in a lower describe block" } - let(:email_raw) { raise "Override this in a lower describe block" } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:receiver) { Email::Receiver.new(email_raw) } - let(:topic) { Fabricate(:topic, closed: true) } - let(:post) { Fabricate(:post, topic: topic, post_number: 1) } - let(:replying_user_email) { 'jake@adventuretime.ooo' } - let(:replying_user) { Fabricate(:user, email: replying_user_email, trust_level: 2) } - let(:email_log) { EmailLog.new(reply_key: reply_key, - post: post, - post_id: post.id, - topic_id: topic.id, - email_type: 'user_posted', - user: replying_user, - user_id: replying_user.id, - to_address: replying_user_email - ) } + context "new message to a group" do - before do - email_log.save + let!(:group) { Fabricate(:group, incoming_email: "team@bar.com") } + + it "handles encoded display names" do + expect { process(:encoded_display_name) }.to change(Topic, :count) + + topic = Topic.last + expect(topic.private_message?).to eq(true) + expect(topic.allowed_groups).to include(group) + + user = topic.user + expect(user.staged).to eq(true) + expect(user.username).to eq("random.name") + expect(user.name).to eq("Случайная Имя") end - describe "should not create post" do - let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' } - let!(:email_raw) { fill_email(fixture_file("emails/valid_reply.eml"), replying_user_email, to) } - it "raises a TopicClosedError" do - expect { receiver.process }.to raise_error(Email::Receiver::TopicClosedError) - end - end - end - - describe "posting reply to a deleted topic" do - let(:reply_key) { raise "Override this in a lower describe block" } - let(:email_raw) { raise "Override this in a lower describe block" } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:receiver) { Email::Receiver.new(email_raw) } - let(:deleted_topic) { Fabricate(:deleted_topic) } - let(:post) { Fabricate(:post, topic: deleted_topic, post_number: 1) } - let(:replying_user_email) { 'jake@adventuretime.ooo' } - let(:replying_user) { Fabricate(:user, email: replying_user_email, trust_level: 2) } - let(:email_log) { EmailLog.new(reply_key: reply_key, - post: post, - post_id: post.id, - topic_id: deleted_topic.id, - email_type: 'user_posted', - user: replying_user, - user_id: replying_user.id, - to_address: replying_user_email - ) } - - before do - email_log.save + it "invites everyone in the chain but emails configured as 'incoming' (via reply, group or category)" do + expect { process(:cc) }.to change(Topic, :count) + emails = Topic.last.allowed_users.pluck(:email) + expect(emails.size).to eq(3) + expect(emails).to include("someone@else.com", "discourse@bar.com", "wat@bar.com") end - describe "should not create post" do - let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' } - let!(:email_raw) { fill_email(fixture_file("emails/valid_reply.eml"), replying_user_email, to) } - it "raises a TopicNotFoundError" do - expect { receiver.process }.to raise_error(Email::Receiver::TopicNotFoundError) - end - end - end + it "associates email replies using both 'In-Reply-To' and 'References' headers" do + expect { process(:email_reply_1) }.to change(Topic, :count) - describe "posting reply as a inactive user" do - let(:reply_key) { raise "Override this in a lower describe block" } - let(:email_raw) { raise "Override this in a lower describe block" } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:receiver) { Email::Receiver.new(email_raw) } - let(:topic) { Fabricate(:topic) } - let(:post) { Fabricate(:post, topic: topic, post_number: 1) } - let(:replying_user_email) { 'jake@adventuretime.ooo' } - let(:replying_user) { Fabricate(:user, email: replying_user_email, trust_level: 2, active: false) } - let(:email_log) { EmailLog.new(reply_key: reply_key, - post: post, - post_id: post.id, - topic_id: topic.id, - email_type: 'user_posted', - user: replying_user, - user_id: replying_user.id, - to_address: replying_user_email - ) } + topic = Topic.last - before do - email_log.save - end + expect { process(:email_reply_2) }.to change { topic.posts.count } + expect { process(:email_reply_3) }.to change { topic.posts.count } - describe "should not create post" do - let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' } - let!(:email_raw) { fill_email(fixture_file("emails/valid_reply.eml"), replying_user_email, to) } - it "raises a InactiveUserError" do - expect { receiver.process }.to raise_error(Email::Receiver::InactiveUserError) - end - end - end + # Why 5 when we only processed 3 emails? + # - 3 of them are indeed "regular" posts generated from the emails + # - The 2 others are "small action" posts automatically added because + # we invited 2 users (two@foo.com and three@foo.com) + expect(topic.posts.count).to eq(5) - describe "posting a new topic in a category" do - let(:category_destination) { raise "Override this in a lower describe block" } - let(:email_raw) { raise "Override this in a lower describe block" } - let(:allow_strangers) { false } - # ---- - let(:receiver) { Email::Receiver.new(email_raw) } - let(:user_email) { 'jake@adventuretime.ooo' } - let(:user) { Fabricate(:user, email: user_email, trust_level: 2)} - let(:category) { Fabricate(:category, email_in: category_destination, email_in_allow_strangers: allow_strangers) } - - before do - SiteSetting.email_in = true - user.save - category.save - end - - describe "too_short.eml" do - let!(:category_destination) { 'incoming+amazing@appmail.adventuretime.ooo' } - let(:email_raw) { - fixture_file("emails/too_short.eml") - .gsub("TO", category_destination) - .gsub("FROM", user_email) - .gsub("SUBJECT", "A long subject that passes the checks") - } - - it "does not create a topic if the post fails" do - before_topic_count = Topic.count - - expect { receiver.process }.to raise_error(Email::Receiver::InvalidPost) - - expect(Topic.count).to eq(before_topic_count) - end + # trash all but the 1st post + topic.ordered_posts[1..-1].each(&:trash!) + expect { process(:email_reply_4) }.to change { topic.posts.count } end end - def process_email(opts) - incoming_email = fixture_file("emails/valid_incoming.eml") - email = fill_email(incoming_email, opts[:from], opts[:to], opts[:body], opts[:subject], opts[:cc]) - Email::Receiver.new(email).process - end + context "new topic in a category" do - describe "with a valid email" do - let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:user_email) { "test@test.com" } - let(:user) { Fabricate(:user, email: user_email, trust_level: 2)} - let(:post) { create_post(user: user) } - - let(:valid_reply) { - reply = fixture_file("emails/valid_reply.eml") - fill_email(reply, user.email, to) - } - - let(:receiver) { Email::Receiver.new(valid_reply) } - let(:email_log) { EmailLog.new(reply_key: reply_key, - post_id: post.id, - topic_id: post.topic_id, - user_id: post.user_id, - post: post, - user: user, - email_type: 'test', - to_address: user.email - ) } - let(:reply_body) { -"I could not disagree more. I am obviously biased but adventure time is the -greatest show ever created. Everyone should watch it. - -- Jake out" } - - describe "with an email log" do - - it "extracts data" do - expect { receiver.process }.to raise_error(Email::Receiver::EmailLogNotFound) - - email_log.save! - receiver.process - - expect(receiver.body).to eq(reply_body) - expect(receiver.email_log).to eq(email_log) - end + let!(:category) { Fabricate(:category, email_in: "category@bar.com", email_in_allow_strangers: false) } + it "raises a StrangersNotAllowedError when 'email_in_allow_strangers' is disabled" do + expect { process(:stranger_not_allowed) }.to raise_error(Email::Receiver::StrangersNotAllowedError) end - end - - describe "with a valid email from a different user" do - let(:reply_key) { SecureRandom.hex(16) } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:user) { Fabricate(:user, email: "test@test.com", trust_level: 2)} - let(:post) { create_post(user: user) } - let!(:email_log) { EmailLog.create(reply_key: reply_key, - post_id: post.id, - topic_id: post.topic_id, - user_id: post.user_id, - post: post, - user: user, - email_type: 'test', - to_address: user.email) } - - it "raises ReplyUserNotFoundError when user doesn't exist" do - reply = fill_email(fixture_file("emails/valid_reply.eml"), "unknown@user.com", to) - receiver = Email::Receiver.new(reply) - expect { receiver.process }.to raise_error(Email::Receiver::ReplyUserNotFoundError) + it "raises an InsufficientTrustLevelError when user's trust level isn't enough" do + SiteSetting.email_in_min_trust = 4 + Fabricate(:user, email: "insufficient@bar.com", trust_level: 3) + expect { process(:insufficient_trust_level) }.to raise_error(Email::Receiver::InsufficientTrustLevelError) end - it "raises ReplyUserNotMatchingError when user is not matching the reply key" do - another_user = Fabricate(:user, email: "existing@user.com") - reply = fill_email(fixture_file("emails/valid_reply.eml"), another_user.email, to) - receiver = Email::Receiver.new(reply) - expect { receiver.process }.to raise_error(Email::Receiver::ReplyUserNotMatchingError) - end - end + it "raises an InvalidAccess when the user is part of a readonly group" do + user = Fabricate(:user, email: "readonly@bar.com", trust_level: SiteSetting.email_in_min_trust) + group = Fabricate(:group) - describe "processes an email to a category" do - let(:to) { "some@email.com" } + group.add(user) + group.save - before do - SiteSetting.email_in = true - SiteSetting.email_in_min_trust = TrustLevel[4].to_s - end - - it "correctly can target categories" do - Fabricate(:category, email_in_allow_strangers: false, email_in: to) - - # no email in for user - expect{ - process_email(from: "cobb@dob.com", to: "invalid@address.com") - }.to raise_error(Email::Receiver::BadDestinationAddress) - - # valid target invalid user - expect{ - process_email(from: "cobb@dob.com", to: to) - }.to raise_error(Email::Receiver::UserNotFoundError) - - # untrusted - user = Fabricate(:user) - expect{ - process_email(from: user.email, to: to) - }.to raise_error(Email::Receiver::UserNotSufficientTrustLevelError) - - # trusted - user.trust_level = 4 - user.save - - process_email(from: user.email, to: to) - expect(user.posts.count).to eq(1) - - # email too short - message = nil - begin - process_email(from: user.email, to: to, body: "x", subject: "this is my new topic title") - rescue Email::Receiver::InvalidPost => e - message = e.message - end - - expect(e.message).to include("too short") - end - - - it "blocks user in restricted group from creating topic" do - to = "some@email.com" - - restricted_user = Fabricate(:user, trust_level: 4) - restricted_group = Fabricate(:group) - restricted_group.add(restricted_user) - restricted_group.save - - category = Fabricate(:category, email_in_allow_strangers: false, email_in: to) - category.set_permissions(restricted_group => :readonly) + category.set_permissions(group => :readonly) category.save - expect{ - process_email(from: restricted_user.email, to: to) - }.to raise_error(Discourse::InvalidAccess) + expect { process(:readonly) }.to raise_error(Discourse::InvalidAccess) end - end - - describe "processes an unknown email sender to category" do - let(:email_in) { "bob@bob.com" } - let(:user_email) { "#{SecureRandom.hex(32)}@foobar.com" } - let(:body) { "This is a new topic created\n\ninside a category ! :)" } - - before do - SiteSetting.email_in = true - SiteSetting.allow_staged_accounts = true - end - - it "rejects anon email" do - Fabricate(:category, email_in_allow_strangers: false, email_in: email_in) - - expect { - process_email(from: user_email, to: email_in, body: body) - }.to raise_error(Email::Receiver::UserNotFoundError) - end - - it "creates a topic for matching category" do - Fabricate(:category, email_in_allow_strangers: true, email_in: email_in) - process_email(from: user_email, to: email_in, body: body) - - staged_account = User.find_by_email(user_email) - expect(staged_account).to be - expect(staged_account.staged).to be(true) - expect(staged_account.posts.order(id: :desc).limit(1).pluck(:raw).first).to eq(body) - end - - end - - describe "processes an unknown email sender to group" do - let(:incoming_email) { "foo@bar.com" } - let(:user_email) { "#{SecureRandom.hex(32)}@foobar.com" } - let(:body) { "This is a message to\n\na group ;)" } - - before do - SiteSetting.email_in = true - SiteSetting.allow_staged_accounts = true - end - - it "creates a message for matching group" do - Fabricate(:group, incoming_email: incoming_email) - process_email(from: user_email, to: incoming_email, body: body) - - staged_account = User.find_by_email(user_email) - expect(staged_account).to be - expect(staged_account.name).to eq("Jake the Dog") - expect(staged_account.staged).to be(true) - - post = staged_account.posts.order(id: :desc).first - expect(post).to be - expect(post.raw).to eq(body) - expect(post.topic.private_message?).to eq(true) - end - - end - - describe "supports incoming mail in CC fields" do - - let(:incoming_email) { "foo@bar.com" } - let(:user_email) { "#{SecureRandom.hex(32)}@foobar.com" } - let(:body) { "This is a message to\n\na group via CC ;)" } - - before do - SiteSetting.email_in = true - SiteSetting.allow_staged_accounts = true - end - - it "creates a message for matching group" do - Fabricate(:group, incoming_email: incoming_email) - process_email(from: user_email, to: "some@email.com", body: body, cc: incoming_email) - - staged_account = User.find_by_email(user_email) - expect(staged_account).to be - expect(staged_account.staged).to be(true) - - post = staged_account.posts.order(id: :desc).first - expect(post).to be - expect(post.raw).to eq(body) - expect(post.topic.private_message?).to eq(true) + it "works" do + Fabricate(:user, email: "sufficient@bar.com", trust_level: SiteSetting.email_in_min_trust) + expect { process(:sufficient_trust_level) }.to change(Topic, :count) end end diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index e5ae07942b..35244c52a9 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -7,6 +7,7 @@ describe Guardian do let(:user) { build(:user) } let(:moderator) { build(:moderator) } let(:admin) { build(:admin) } + let(:trust_level_2) { build(:user, trust_level: 2) } let(:trust_level_3) { build(:user, trust_level: 3) } let(:trust_level_4) { build(:user, trust_level: 4) } let(:another_admin) { build(:admin) } @@ -186,6 +187,22 @@ describe Guardian do expect(Guardian.new(user).can_send_private_message?(suspended_user)).to be_falsey end end + + context "author is blocked" do + before do + user.blocked = true + user.save + end + + it "returns true if target is staff" do + expect(Guardian.new(user).can_send_private_message?(admin)).to be_truthy + expect(Guardian.new(user).can_send_private_message?(moderator)).to be_truthy + end + + it "returns false if target is not staff" do + expect(Guardian.new(user).can_send_private_message?(another_user)).to be_falsey + end + end end describe 'can_reply_as_new_topic' do @@ -660,11 +677,34 @@ describe Guardian do it "doesn't allow new posts from admins" do expect(Guardian.new(admin).can_create?(Post, topic)).to be_falsey end - end + context "private message" do + let(:private_message) { Fabricate(:topic, archetype: Archetype.private_message, category_id: nil) } - end + before { user.save! } + + it "allows new posts by people included in the pm" do + private_message.topic_allowed_users.create!(user_id: user.id) + expect(Guardian.new(user).can_create?(Post, private_message)).to be_truthy + end + + it "doesn't allow new posts by people not invited to the pm" do + expect(Guardian.new(user).can_create?(Post, private_message)).to be_falsey + end + + it "allows new posts from blocked users included in the pm" do + user.update_attribute(:blocked, true) + private_message.topic_allowed_users.create!(user_id: user.id) + expect(Guardian.new(user).can_create?(Post, private_message)).to be_truthy + end + + it "doesn't allow new posts from blocked users not invited to the pm" do + user.update_attribute(:blocked, true) + expect(Guardian.new(user).can_create?(Post, private_message)).to be_falsey + end + end + end # can_create? a Post end @@ -1998,16 +2038,34 @@ describe Guardian do end describe 'can_wiki?' do + let(:post) { build(:post) } + it 'returns false for regular user' do - expect(Guardian.new(coding_horror).can_wiki?).to be_falsey + expect(Guardian.new(coding_horror).can_wiki?(post)).to be_falsey + end + + it "returns false when user does not satisfy trust level but owns the post" do + own_post = Fabricate(:post, user: trust_level_2) + expect(Guardian.new(trust_level_2).can_wiki?(own_post)).to be_falsey + end + + it "returns false when user satisfies trust level but tries to wiki someone else's post" do + SiteSetting.min_trust_to_allow_self_wiki = 2 + expect(Guardian.new(trust_level_2).can_wiki?(post)).to be_falsey + end + + it 'returns true when user satisfies trust level and owns the post' do + SiteSetting.min_trust_to_allow_self_wiki = 2 + own_post = Fabricate(:post, user: trust_level_2) + expect(Guardian.new(trust_level_2).can_wiki?(own_post)).to be_truthy end it 'returns true for admin user' do - expect(Guardian.new(admin).can_wiki?).to be_truthy + expect(Guardian.new(admin).can_wiki?(post)).to be_truthy end it 'returns true for trust_level_4 user' do - expect(Guardian.new(trust_level_4).can_wiki?).to be_truthy + expect(Guardian.new(trust_level_4).can_wiki?(post)).to be_truthy end end end diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 2cdef4aef8..01006058e1 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -478,6 +478,10 @@ describe PostCreator do expect(unrelated.notifications.count).to eq(0) expect(post.topic.subtype).to eq(TopicSubtype.user_to_user) + # PMs do not increase post count or topic count + expect(post.user.user_stat.post_count).to eq(0) + expect(post.user.user_stat.topic_count).to eq(0) + # archive this message and ensure archive is cleared for all users on reply UserArchivedMessage.create(user_id: target_user2.id, topic_id: post.topic_id) @@ -490,6 +494,16 @@ describe PostCreator do expect(post.topic.topic_allowed_users.where(user_id: admin.id).count).to eq(1) expect(UserArchivedMessage.where(user_id: target_user2.id, topic_id: post.topic_id).count).to eq(0) + + # if another admin replies and is already member of the group, don't add them to topic_allowed_users + group = Fabricate(:group) + post.topic.topic_allowed_groups.create!(group: group) + admin2 = Fabricate(:admin) + group.add(admin2) + + PostCreator.create(admin2, raw: 'I am also an admin, and a mod', topic_id: post.topic_id) + + expect(post.topic.topic_allowed_users.where(user_id: admin2.id).count).to eq(0) end end @@ -595,12 +609,12 @@ describe PostCreator do describe "word_count" do it "has a word count" do - creator = PostCreator.new(user, title: 'some inspired poetry for a rainy day', raw: 'mary had a little lamb, little lamb, little lamb. mary had a little lamb') + creator = PostCreator.new(user, title: 'some inspired poetry for a rainy day', raw: 'mary had a little lamb, little lamb, little lamb. mary had a little lamb. Здравствуйте') post = creator.create - expect(post.word_count).to eq(14) + expect(post.word_count).to eq(15) post.topic.reload - expect(post.topic.word_count).to eq(14) + expect(post.topic.word_count).to eq(15) end end diff --git a/spec/components/post_revisor_spec.rb b/spec/components/post_revisor_spec.rb index 0c035fcea4..4667ede9cd 100644 --- a/spec/components/post_revisor_spec.rb +++ b/spec/components/post_revisor_spec.rb @@ -278,14 +278,14 @@ describe PostRevisor do describe 'with a new body' do let(:changed_by) { Fabricate(:coding_horror) } - let!(:result) { subject.revise!(changed_by, { raw: "lets update the body" }) } + let!(:result) { subject.revise!(changed_by, { raw: "lets update the body. Здравствуйте" }) } it 'returns true' do expect(result).to eq(true) end it 'updates the body' do - expect(post.raw).to eq("lets update the body") + expect(post.raw).to eq("lets update the body. Здравствуйте") end it 'sets the invalidate oneboxes attribute' do @@ -306,9 +306,9 @@ describe PostRevisor do end it "updates the word count" do - expect(post.word_count).to eq(4) + expect(post.word_count).to eq(5) post.topic.reload - expect(post.topic.word_count).to eq(4) + expect(post.topic.word_count).to eq(5) end context 'second poster posts again quickly' do diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index f9cd29081c..0cca316e1b 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -243,9 +243,14 @@ HTML expect(PrettyText.excerpt("'", 500, text_entities: true)).to eq("'") end - it "should have an option to preserve emojis" do + it "should have an option to preserve emoji images" do emoji_image = "heart" - expect(PrettyText.excerpt(emoji_image, 100, { keep_emojis: true })).to match_html(emoji_image) + expect(PrettyText.excerpt(emoji_image, 100, { keep_emoji_images: true })).to match_html(emoji_image) + end + + it "should have an option to preserve emoji codes" do + emoji_code = ":heart:" + expect(PrettyText.excerpt(emoji_code, 100, { keep_emoji_codes: true })).to eq(":heart:") end end @@ -390,7 +395,7 @@ HTML end it "doesn't replace unicode emoji if emoji is disabled" do - SiteSetting.enable_emoji = false + SiteSetting.enable_emoji = false expect(PrettyText.cook("💣")).not_to match(/\:bomb\:/) end end diff --git a/spec/components/system_message_spec.rb b/spec/components/system_message_spec.rb index 3a0ffd8d77..dbbeff71ed 100644 --- a/spec/components/system_message_spec.rb +++ b/spec/components/system_message_spec.rb @@ -4,22 +4,27 @@ require 'topic_subtype' describe SystemMessage do - let!(:admin) { Fabricate(:admin) } context 'send' do - let(:user) { Fabricate(:user) } - let(:system_message) { SystemMessage.new(user) } - let(:post) { system_message.create(:welcome_invite) } - let(:topic) { post.topic } - it 'should create a post correctly' do + + admin = Fabricate(:admin) + user = Fabricate(:user) + SiteSetting.site_contact_username = admin.username + system_message = SystemMessage.new(user) + post = system_message.create(:welcome_invite) + topic = post.topic + expect(post).to be_present expect(post).to be_valid expect(topic).to be_private_message expect(topic).to be_valid expect(topic.subtype).to eq(TopicSubtype.system_message) expect(topic.allowed_users.include?(user)).to eq(true) + expect(topic.allowed_users.include?(admin)).to eq(true) + + expect(UserArchivedMessage.where(user_id: admin.id, topic_id: topic.id).length).to eq(1) end end diff --git a/spec/components/user_name_suggester_spec.rb b/spec/components/user_name_suggester_spec.rb index ca404ed4bf..f6fde98996 100644 --- a/spec/components/user_name_suggester_spec.rb +++ b/spec/components/user_name_suggester_spec.rb @@ -56,15 +56,19 @@ describe UserNameSuggester do end it "removes leading character if it is not alphanumeric" do - expect(UserNameSuggester.suggest("_myname")).to eq('myname') + expect(UserNameSuggester.suggest(".myname")).to eq('myname') + end + + it "allows leading _" do + expect(UserNameSuggester.suggest("_myname")).to eq('_myname') end it "removes trailing characters if they are invalid" do expect(UserNameSuggester.suggest("myname!^$=")).to eq('myname') end - it "replace dots" do - expect(UserNameSuggester.suggest("my.name")).to eq('my_name') + it "allows dots in the middle" do + expect(UserNameSuggester.suggest("my.name")).to eq('my.name') end it "remove leading dots" do @@ -81,7 +85,7 @@ describe UserNameSuggester do end it 'should handle typical facebook usernames' do - expect(UserNameSuggester.suggest('roger.nelson.3344913')).to eq('roger_nelson_33') + expect(UserNameSuggester.suggest('roger.nelson.3344913')).to eq('roger.nelson.33') end end diff --git a/spec/controllers/category_hashtags_controller_spec.rb b/spec/controllers/category_hashtags_controller_spec.rb index 01d5eeb74f..e5f1515614 100644 --- a/spec/controllers/category_hashtags_controller_spec.rb +++ b/spec/controllers/category_hashtags_controller_spec.rb @@ -12,7 +12,7 @@ describe CategoryHashtagsController do xhr :get, :check, category_slugs: [category.slug, 'none'] expect(JSON.parse(response.body)).to eq( - { "valid" => [{ "slug" => category.slug, "url" => category.url_with_id }] } + { "valid" => [{ "slug" => category.hashtag_slug, "url" => category.url_with_id }] } ) end @@ -32,7 +32,7 @@ describe CategoryHashtagsController do xhr :get, :check, category_slugs: [private_category.slug] expect(JSON.parse(response.body)).to eq( - { "valid" => [{ "slug" => private_category.slug, "url" => private_category.url_with_id }] } + { "valid" => [{ "slug" => private_category.hashtag_slug, "url" => private_category.url_with_id }] } ) end end diff --git a/spec/controllers/email_controller_spec.rb b/spec/controllers/email_controller_spec.rb index 411d3ff148..f5c6e91809 100644 --- a/spec/controllers/email_controller_spec.rb +++ b/spec/controllers/email_controller_spec.rb @@ -39,9 +39,23 @@ describe EmailController do context '.unsubscribe' do - let(:user) { Fabricate(:user) } + let(:user) { Fabricate(:user, email_digests: true, email_direct: true, email_private_messages: true, email_always: true) } let(:key) { DigestUnsubscribeKey.create_key_for(user) } + context 'from confirm unsubscribe email' do + before do + get :unsubscribe, key: key, from_all: true + user.reload + end + + it 'unsubscribes from all emails' do + expect(user.email_digests).to eq false + expect(user.email_direct).to eq false + expect(user.email_private_messages).to eq false + expect(user.email_always).to eq false + end + end + context 'with a valid key' do before do get :unsubscribe, key: key diff --git a/spec/controllers/list_controller_spec.rb b/spec/controllers/list_controller_spec.rb index 59c20da024..e2de6b6235 100644 --- a/spec/controllers/list_controller_spec.rb +++ b/spec/controllers/list_controller_spec.rb @@ -248,4 +248,30 @@ describe ListController do end + describe "categories suppression" do + let(:category_one) { Fabricate(:category) } + let(:sub_category) { Fabricate(:category, parent_category: category_one, suppress_from_homepage: true) } + let!(:topic_in_sub_category) { Fabricate(:topic, category: sub_category) } + + let(:category_two) { Fabricate(:category, suppress_from_homepage: true) } + let!(:topic_in_category_two) { Fabricate(:topic, category: category_two) } + + it "suppresses categories from the homepage" do + get SiteSetting.homepage, format: :json + expect(response).to be_success + + topic_titles = JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["title"] } + expect(topic_titles).not_to include(topic_in_sub_category.title, topic_in_category_two.title) + end + + it "does not suppress" do + get SiteSetting.homepage, category: category_one.id, format: :json + expect(response).to be_success + + topic_titles = JSON.parse(response.body)["topic_list"]["topics"].map { |t| t["title"] } + expect(topic_titles).to include(topic_in_sub_category.title) + end + + end + end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index f7afc42540..14896e52d6 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -408,7 +408,7 @@ describe PostsController do let(:post) {Fabricate(:post, user: user)} it "raises an error if the user doesn't have permission to wiki the post" do - Guardian.any_instance.expects(:can_wiki?).returns(false) + Guardian.any_instance.expects(:can_wiki?).with(post).returns(false) xhr :put, :wiki, post_id: post.id, wiki: 'true' @@ -416,7 +416,7 @@ describe PostsController do end it "can wiki a post" do - Guardian.any_instance.expects(:can_wiki?).returns(true) + Guardian.any_instance.expects(:can_wiki?).with(post).returns(true) xhr :put, :wiki, post_id: post.id, wiki: 'true' @@ -426,7 +426,7 @@ describe PostsController do it "can unwiki a post" do wikied_post = Fabricate(:post, user: user, wiki: true) - Guardian.any_instance.expects(:can_wiki?).returns(true) + Guardian.any_instance.expects(:can_wiki?).with(wikied_post).returns(true) xhr :put, :wiki, post_id: wikied_post.id, wiki: 'false' diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb index b92a14817b..69281fca35 100644 --- a/spec/controllers/topics_controller_spec.rb +++ b/spec/controllers/topics_controller_spec.rb @@ -898,18 +898,6 @@ describe TopicsController do end end - context 'when topic is in support category' do - let(:another_category) { Fabricate(:category) } - - it "cannot change the category of a topic that is in a support category" do - @topic.category = Fabricate(:category, contains_messages: true) - @topic.save! - xhr :put, :update, topic_id: @topic.id, slug: @topic.title, category_id: another_category.id - expect(response).not_to be_success - end - - end - context "allow_uncategorized_topics is false" do before do SiteSetting.stubs(:allow_uncategorized_topics).returns(false) diff --git a/spec/controllers/user_badges_controller_spec.rb b/spec/controllers/user_badges_controller_spec.rb index 3ad8840bc6..0046f61404 100644 --- a/spec/controllers/user_badges_controller_spec.rb +++ b/spec/controllers/user_badges_controller_spec.rb @@ -12,9 +12,11 @@ describe UserBadgesController do xhr :get, :index, badge_id: badge.id expect(response.status).to eq(200) + parsed = JSON.parse(response.body) expect(parsed["topics"]).to eq(nil) - expect(parsed["user_badges"][0]["post_id"]).to eq(nil) + expect(parsed["badges"].length).to eq(1) + expect(parsed["user_badge_info"]["user_badges"][0]["post_id"]).to eq(nil) end end @@ -38,7 +40,7 @@ describe UserBadgesController do expect(response.status).to eq(200) parsed = JSON.parse(response.body) - expect(parsed["user_badges"].length).to eq(1) + expect(parsed["user_badge_info"]["user_badges"].length).to eq(1) end it 'includes counts when passed the aggregate argument' do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 435f4fd2e1..651bf03087 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -242,11 +242,13 @@ describe UsersController do context 'when the new email address is taken' do let!(:other_user) { Fabricate(:coding_horror) } it 'raises an error' do - expect { xhr :put, :change_email, username: user.username, email: other_user.email }.to raise_error(Discourse::InvalidParameters) + xhr :put, :change_email, username: user.username, email: other_user.email + expect(response).to_not be_success end it 'raises an error if there is whitespace too' do - expect { xhr :put, :change_email, username: user.username, email: other_user.email + ' ' }.to raise_error(Discourse::InvalidParameters) + xhr :put, :change_email, username: user.username, email: other_user.email + ' ' + expect(response).to_not be_success end end @@ -254,10 +256,23 @@ describe UsersController do let!(:other_user) { Fabricate(:user, email: 'case.insensitive@gmail.com')} it 'raises an error' do - expect { xhr :put, :change_email, username: user.username, email: other_user.email.upcase }.to raise_error(Discourse::InvalidParameters) + xhr :put, :change_email, username: user.username, email: other_user.email.upcase + expect(response).to_not be_success end end + it 'raises an error when new email domain is present in email_domains_blacklist site setting' do + SiteSetting.email_domains_blacklist = "mailinator.com" + xhr :put, :change_email, username: user.username, email: "not_good@mailinator.com" + expect(response).to_not be_success + end + + it 'raises an error when new email domain is not present in email_domains_whitelist site setting' do + SiteSetting.email_domains_whitelist = "discourse.org" + xhr :put, :change_email, username: user.username, email: new_email + expect(response).to_not be_success + end + context 'success' do it 'has an email token' do @@ -1595,4 +1610,19 @@ describe UsersController do end + context '#summary' do + + it "generates summary info" do + user = Fabricate(:user) + create_post(user: user) + + xhr :get, :summary, username: user.username_lower + expect(response).to be_success + json = JSON.parse(response.body) + + expect(json["user_summary"]["topic_count"]).to eq(1) + expect(json["user_summary"]["post_count"]).to eq(1) + end + end + end diff --git a/spec/fixtures/emails/android_gmail.eml b/spec/fixtures/emails/android_gmail.eml deleted file mode 100644 index 21c5dde234..0000000000 --- a/spec/fixtures/emails/android_gmail.eml +++ /dev/null @@ -1,177 +0,0 @@ -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:
    - -
    diff --git a/app/views/email/notification.html.erb b/app/views/email/notification.html.erb index 044de83527..681dc96afc 100644 --- a/app/views/email/notification.html.erb +++ b/app/views/email/notification.html.erb @@ -19,7 +19,7 @@
    - + diff --git a/app/views/embed/comments.html.erb b/app/views/embed/comments.html.erb index 873229a992..29ab277232 100644 --- a/app/views/embed/comments.html.erb +++ b/app/views/embed/comments.html.erb @@ -10,17 +10,17 @@ <%- if @topic_view.posts.present? %> <%- @topic_view.posts.each do |post| %>
    - <%= link_to embed_post_date(post.created_at), post.url, title: post.created_at.strftime("%B %e, %Y %l:%M%P"), class: 'post-date', target: "_blank" %> + <%= link_to embed_post_date(post.created_at), post.full_url, title: post.created_at.strftime("%B %e, %Y %l:%M%P"), class: 'post-date', target: "_blank" %> <%- if post.reply_to_post.present? && !post.cooked.index('aside') %> - <%= link_to I18n.t('embed.in_reply_to', username: post.reply_to_post.username), post.reply_to_post.url, 'data-link-to-post' => post.reply_to_post.id.to_s, :class => 'in-reply-to' %> + <%= link_to I18n.t('embed.in_reply_to', username: post.reply_to_post.username), post.reply_to_post.full_url, 'data-link-to-post' => post.reply_to_post.id.to_s, :class => 'in-reply-to' %> <%- end %>
    - +

    - <%= post.user.username %> + <%= post.user.username %> <%- if post.user.title.present? %> <%= post.user.title %> <%- end %> @@ -29,9 +29,9 @@ <%- if post.reply_count > 0 %> <%- if post.reply_count == 1 %> - <%= link_to I18n.t('embed.replies', count: post.reply_count), post.url, 'data-link-to-post' => post.replies.first.id.to_s, :class => 'post-replies button' %> + <%= link_to I18n.t('embed.replies', count: post.reply_count), post.full_url, 'data-link-to-post' => post.replies.first.id.to_s, :class => 'post-replies button' %> <% else %> - <%= link_to I18n.t('embed.replies', count: post.reply_count), post.url, class: 'post-replies button', target: "_blank" %> + <%= link_to I18n.t('embed.replies', count: post.reply_count), post.full_url, class: 'post-replies button', target: "_blank" %> <%- end %> <%- end %>

    @@ -41,7 +41,7 @@ <% if @topic_view.topic.posts_count > 0 %>
    <%= link_to(image_tag(SiteSetting.logo_url, class: 'logo'), Discourse.base_url, target: '_blank') %> - <%= link_to(I18n.t('embed.continue'), @topic_view.posts.last.url, class: 'button', target: '_blank') %> + <%= link_to(I18n.t('embed.continue'), @topic_view.posts.last.full_url, class: 'button', target: '_blank') %> <%- if @posts_left > 0 %> <%= I18n.t('embed.more_replies', count: @posts_left) %> <%- end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index fb45a9b9df..4d6575b777 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -46,10 +46,10 @@
    diff --git a/app/views/layouts/crawler.html.erb b/app/views/layouts/crawler.html.erb index e68f58beb2..a68cfade2b 100644 --- a/app/views/layouts/crawler.html.erb +++ b/app/views/layouts/crawler.html.erb @@ -27,10 +27,10 @@

    <%= t 'powered_by_html' %>

    diff --git a/app/views/static/signup.html.erb b/app/views/static/signup.html.erb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/config/initializers/100-sidekiq.rb b/config/initializers/100-sidekiq.rb index bfac8028f1..3940cdd4c8 100644 --- a/config/initializers/100-sidekiq.rb +++ b/config/initializers/100-sidekiq.rb @@ -74,5 +74,3 @@ end Sidekiq.error_handlers.clear Sidekiq.error_handlers << SidekiqLogsterReporter.new - - diff --git a/config/locales/client.ar.yml b/config/locales/client.ar.yml index c8bfe022eb..87dc245d48 100644 --- a/config/locales/client.ar.yml +++ b/config/locales/client.ar.yml @@ -462,6 +462,7 @@ ar: all_subcategories: "جميع" no_subcategory: "لا شيء" category: "تصنيف" + category_list: "أعرض قائمة الأقسام." reorder: title: "إعادة ترتيب الفئات" title_long: "إعادة تنظيم قائمة الفئة" @@ -526,6 +527,7 @@ ar: invited_by: "مدعو بواسطة" trust_level: "مستوى الثقة" notifications: "الاشعارات" + statistics: "احصائيات" desktop_notifications: label: "إشعارات سطح المكتب" not_supported: "عذراً , الإشعارات غير مدعومة على هذا المتصفح " @@ -579,7 +581,9 @@ ar: warnings_received: "تحذيرات" messages: all: "الكل" + inbox: "البريد الوارد" sent: "مرسلة" + archive: "الارشيف" groups: "مجموعاتي" bulk_select: "إختيار رسائل" move_to_inbox: "الذهاب إلى الرسائل الواردة" @@ -1130,6 +1134,12 @@ ar: create: 'موضوع جديد' create_long: 'كتابة موضوع جديد' private_message: 'أرسل رسالة خاصة' + archive_message: + help: 'انقل الرسالة لارشيفك.' + title: 'الارشيف' + move_to_inbox: + title: 'انقل للبريد الوارد.' + help: 'انقل الرسالة مرة آخرى للبريد الوارد.' list: 'المواضيع' new: 'موضوع جديد' unread: 'غير مقروء' @@ -1493,7 +1503,7 @@ ar: via_email: "وصلت هذه المشاركة من خلال الإيميل" whisper: "هذه المشاركة همسة خاصة للمشرفين" wiki: - about: "هذه المشاركة عبارة عن ويكي بمعنى أنها متاحة للمستخدمين العاديين لتحريرها ، " + about: "هذه المشاركة تعتبر ويكي." archetypes: save: 'حفظ الخيارات' controls: @@ -2292,6 +2302,7 @@ ar: email_templates: title: "قالب البريد الالكتروني " subject: "الموضوع" + multiple_subjects: "قالب البريد الإلكتروني هذا لديه موضوعات متعددة." body: "المحتوى" none_selected: "اختر قالب بريد الكتروني لتبدا بتعديله " revert: "اعاده التغيرات " @@ -2419,6 +2430,7 @@ ar: change_site_setting: "تغيير اعدادات الموقع" change_site_customization: "تخصيص الموقع" delete_site_customization: "حذف هذا التخصيص؟" + change_site_text: "تغيير نص الموقع." suspend_user: "حظر المستخدم" unsuspend_user: "رفع الحظر " grant_badge: "منح شارة" @@ -2638,6 +2650,7 @@ ar: unlock_trust_level: "فتح مستوى الثقة " tl3_requirements: title: "المتطلبات لمستوى الثقة 3." + table_title: "في أخر %{time_period} أيام:" value_heading: "تصويت" requirement_heading: "متطلبات" visits: "الزيارات" @@ -2704,6 +2717,7 @@ ar: revert: "حفظ التعديلات" revert_confirm: "هل انت متاكد من انك تريد اعاده التغيرات؟ " go_back: "العودة إلى البحث" + recommended: "نوصيك بتخصيص النص التالي ليلائم احتياجاتك:" show_overriden: 'اظهر التجاوزات فقط' site_settings: show_overriden: 'تظهر فقط تجاوز' diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index 388bbbc732..e7f3b937ec 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -321,10 +321,13 @@ de: none: "keine" notifications: watching: + title: "Beobachten" description: "Du wirst über jeden neuen Beitrag in jeder Nachricht benachrichtigt und die Anzahl der neuen Antworten wird angezeigt." tracking: + title: "Verfolgen" description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet, und die Anzahl der neuen Antworten wird angezeigt." regular: + title: "Normal" description: "Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder dir antwortet." muted: title: "Stummgeschaltet" @@ -347,6 +350,7 @@ de: all_subcategories: "alle" no_subcategory: "keine" category: "Kategorie" + category_list: "Kategorieliste anzeigen" reorder: title: "Kategorien neu sortieren" title_long: "Neustrukturierung der Kategorieliste" @@ -462,7 +466,7 @@ de: archive: "Archiv" groups: "Meine Gruppen" bulk_select: "Nachrichten auswählen" - move_to_inbox: "Zum Posteingang verschieben" + move_to_inbox: "In Posteingang verschieben" failed_to_move: "Die ausgewählten Nachrichten konnten nicht bewegt werden (vielleicht gibt es ein Netzwerkproblem)." select_all: "Alle auswählen" change_password: @@ -748,9 +752,9 @@ de: admin_not_allowed_from_ip_address: "Von dieser IP-Adresse darfst du dich nicht als Administrator 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." - to_continue: "Bitte einloggen" - preferences: "Du musst eingeloggt sein, um deine Benutzereinstellungen bearbeiten zu können." - forgot: "Ich kann mich an meine Konto-Daten nicht erinnern" + to_continue: "Melde dich bitte an" + preferences: "Du musst angemeldet sein, um deine Benutzereinstellungen bearbeiten zu können." + forgot: "Ich kann mich nicht an meine Zugangsdaten erinnern" google: title: "mit Google" message: "Authentifiziere mit Google (stelle sicher, dass keine Pop-up-Blocker aktiviert sind)" @@ -947,6 +951,7 @@ de: dismiss_read: "Blende alle ungelesenen Beiträge aus" dismiss_button: "Ignorieren..." dismiss_tooltip: "Nur die neuen Beiträge ignorieren oder Themen nicht mehr verfolgen" + also_dismiss_topics: "Diese Themen nicht mehr verfolgen, sodass mir diese nicht mehr als ungelesen angezeigt werden" dismiss_new: "Neue Themen ignorieren" toggle: "zu Massenoperationen auf Themen umschalten" actions: "Massenoperationen" @@ -993,6 +998,12 @@ de: create: 'Neues Thema' create_long: 'Ein neues Thema erstellen' private_message: 'Eine Unterhaltung beginnen' + archive_message: + help: 'Nachricht ins Archiv verschieben' + title: 'Archivieren' + move_to_inbox: + title: 'In Posteingang verschieben' + help: 'Nachricht in den Posteingang zurück verschieben' list: 'Themen' new: 'neues Thema' unread: 'ungelesen' @@ -1043,6 +1054,7 @@ de: auto_close_title: 'Automatisches Schließen' auto_close_save: "Speichern" auto_close_remove: "Dieses Thema nicht automatisch schließen" + auto_close_immediate: "Der letzte Beitrag in diesem Thema ist bereits %{hours} Stunden alt. Das Thema wird daher sofort geschlossen." progress: title: Themen-Fortschritt go_top: "Anfang" @@ -1163,6 +1175,7 @@ de: success: "Wir haben den Benutzer gebeten, sich an dieser Unterhaltung zu beteiligen." error: "Entschuldige, es gab einen Fehler beim Einladen des Benutzers." group_name: "Gruppenname" + controls: "Weitere Aktionen" invite_reply: title: 'Einladen' username_placeholder: "Benutzername" @@ -1280,7 +1293,7 @@ de: via_email: "dieser Beitrag ist per E-Mail eingetroffen" whisper: "Dieser Beitrag ist Privat für Moderatoren." wiki: - about: "dieser Beitrag ist ein Wiki; Anwärter können diesen bearbeiten" + about: "dieser Beitrag ist ein Wiki" archetypes: save: 'Speicheroptionen' controls: @@ -1467,6 +1480,7 @@ de: email_in_allow_strangers: "Akzeptiere E-Mails von nicht registrierten, anonymen Benutzern" email_in_disabled: "Das Erstellen von neuen Themen per E-Mail ist in den Website-Einstellungen deaktiviert. Um das Erstellen von neuen Themen per E-Mail zu erlauben," email_in_disabled_click: 'aktiviere die Einstellung „email in“.' + contains_messages: "Ändere diese Kategorie, sodass diese nur noch Nachrichten enthält." suppress_from_homepage: "Löse diese Kategorie von der Webseite." allow_badges_label: "Erlaube das Verleihen von Abzeichen in dieser Kategorie" edit_permissions: "Berechtigungen bearbeiten" @@ -1805,6 +1819,7 @@ de: primary_group: "Automatisch als primäre Gruppe festlegen" group_owners: Eigentümer add_owners: Eigentümer hinzufügen + incoming_email: "Benutzerdefinierte Adresse für eingehende E-Mails" incoming_email_placeholder: "E-Mail-Adresse eingeben" api: generate_master: "Master API Key erzeugen" @@ -1903,7 +1918,7 @@ de: button_title: "Einladungen versenden" customize: title: "Anpassen" - long_title: "Website-Anpassungen" + long_title: "Anpassungen" css: "CSS" header: "Kopfbereich" top: "Anfang" @@ -1937,10 +1952,11 @@ de: email_templates: title: "E-Mail-Vorlagen" subject: "Betreff" + multiple_subjects: "Diese E-Mail-Vorlage enthält mehrere Betreffzeilen." body: "Nachrichtentext" none_selected: "Wähle eine E-Mail-Vorlage aus, um diese zu bearbeiten." - revert: "Änderungen rückgängig machen" - revert_confirm: "Möchtest du wirklich die Änderungen rückgängig machen?" + revert: "Änderungen verwerfen" + revert_confirm: "Möchtest du wirklich deine Änderungen verwerfen?" css_html: title: "CSS/HTML" long_title: "CSS und HTML Anpassungen" @@ -2061,9 +2077,10 @@ de: delete_user: "Benutzer löschen" change_trust_level: "Vertrauensstufe ändern" change_username: "Benutzernamen ändern" - change_site_setting: "Website-Einstellungen ändern" - change_site_customization: "Website-Anpassungen ändern" - delete_site_customization: "Website-Anpassungen löschen" + change_site_setting: "Einstellungen ändern" + change_site_customization: "Anpassungen ändern" + delete_site_customization: "Anpassungen löschen" + change_site_text: "Text ändern" suspend_user: "Benutzer sperren" unsuspend_user: "Benutzer entsperren" grant_badge: "Abzeichen verleihen" @@ -2255,6 +2272,7 @@ de: unlock_trust_level: "Vertrauensstufe entsperren" tl3_requirements: title: "Anforderungen für Vertrauensstufe 3" + table_title: "In den letzten %{time_period} Tagen:" value_heading: "Wert" requirement_heading: "Anforderung" visits: "Aufrufe" @@ -2315,10 +2333,14 @@ de: confirm: 'Bestätigung' dropdown: "Dropdown-Liste" site_text: + description: "Du kannst jeden Text deines Forums anpassen. Benutze dazu die Suche:" + search: "Suche nach dem Text, den du bearbeiten möchtest" title: 'Textinhalt' edit: 'bearbeiten' - revert: "Änderungen rückgängig machen" + revert: "Änderungen verwerfen" + revert_confirm: "Möchtest du wirklich deine Änderungen verwerfen?" go_back: "Zurück zur Suche" + recommended: "Wir empfehlen, dass du den folgenden Text an deine Bedürfnisse anpasst:" show_overriden: 'Nur geänderterte Texte anzeigen' site_settings: show_overriden: 'Nur geänderterte Einstellungen anzeigen' diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index d23712d877..113913db8f 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1,9 +1,9 @@ # encoding: utf-8 +# # This file contains content for the client portion of Discourse, sent out # to the Javascript app. # -# To work with us on translations, see: -# https://www.transifex.com/projects/p/discourse-org/ +# To work with us on translations, see: https://www.transifex.com/projects/p/discourse-org/ # # This is a "source" file, which is used by Transifex to get translations for other languages. # After this file is changed, it needs to be pushed by a maintainer to Transifex: @@ -12,8 +12,7 @@ # # Read more here: https://meta.discourse.org/t/contribute-a-translation-to-discourse/14882 # -# To validate this YAML file after you change it, please paste it into -# http://yamllint.com/ +# To validate this YAML file after you change it, please paste it into http://yamllint.com/ en: js: @@ -121,6 +120,8 @@ en: action_codes: split_topic: "split this topic %{when}" + invited_user: "invited %{who} %{when}" + removed_user: "removed %{who} %{when}" autoclosed: enabled: 'closed %{when}' disabled: 'opened %{when}' @@ -144,6 +145,20 @@ en: emails_are_disabled: "All outgoing email has been globally disabled by an administrator. No email notifications of any kind will be sent." + s3: + regions: + us_east_1: "US East (N. Virginia)" + us_west_1: "US West (N. California)" + us_west_2: "US West (Oregon)" + us_gov_west_1: "AWS GovCloud (US)" + eu_west_1: "EU (Ireland)" + eu_central_1: "EU (Frankfurt)" + ap_southeast_1: "Asia Pacific (Singapore)" + ap_southeast_2: "Asia Pacific (Sydney)" + ap_northeast_1: "Asia Pacific (Tokyo)" + ap_northeast_2: "Asia Pacific (Seoul)" + sa_east_1: "South America (Sao Paulo)" + edit: 'edit the title and category of this topic' not_implemented: "That feature hasn't been implemented yet, sorry!" no_value: "No" @@ -688,6 +703,23 @@ en: ok: "Your password looks good." instructions: "At least %{count} characters." + summary: + title: "Summary" + stats: "Stats" + topic_count: "Topics Created" + post_count: "Posts Created" + likes_given: "Likes Given" + likes_received: "Likes Received" + days_visited: "Days Visited" + posts_read_count: "Posts Read" + top_replies: "Top Replies" + top_topics: "Top Topics" + top_badges: "Top Badges" + more_topics: "More Topics" + more_replies: "More Replies" + more_badges: "More Badges" + + associated_accounts: "Logins" ip_address: title: "Last IP Address" @@ -733,7 +765,7 @@ en: logout: "You were logged out." refresh: "Refresh" read_only_mode: - enabled: "Read-only mode is enabled. You can continue to browse the site but interactions may not work." + enabled: "This site is in read only mode. Please continue to browse, but replying, likes, and other actions are disabled for now." login_disabled: "Login is disabled while the site is in read only mode." too_few_topics_and_posts_notice: "Let's get this discussion started! There are currently %{currentTopics} / %{requiredTopics} topics and %{currentPosts} / %{requiredPosts} posts. New visitors need some conversations to read and respond to." too_few_topics_notice: "Let's get this discussion started! There are currently %{currentTopics} / %{requiredTopics} topics. New visitors need some conversations to read and respond to." @@ -862,7 +894,7 @@ en: alt: 'Alt' composer: - emoji: "Emoji :smile:" + emoji: "Emoji :)" more_emoji: "more..." options: "Options" whisper: "whisper" @@ -1629,7 +1661,6 @@ en: email_in_allow_strangers: "Accept emails from anonymous users with no accounts" email_in_disabled: "Posting new topics via email is disabled in the Site Settings. To enable posting new topics via email, " email_in_disabled_click: 'enable the "email in" setting.' - contains_messages: "Change this category to only contain messages." suppress_from_homepage: "Suppress this category from the homepage." allow_badges_label: "Allow badges to be awarded in this category" edit_permissions: "Edit Permissions" @@ -2190,14 +2221,17 @@ en: email: - title: "Email" + title: "Emails" settings: "Settings" - all: "All" + templates: "Templates" + preview_digest: "Preview Digest" 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" + received: "Received" + rejected: "Rejected" sent_at: "Sent At" time: "Time" user: "User" @@ -2207,7 +2241,6 @@ en: send_test: "Send Test Email" sent_test: "sent!" delivery_method: "Delivery Method" - preview_digest: "Preview Digest" preview_digest_desc: "Preview the content of the digest emails sent to inactive users." refresh: "Refresh" format: "Format" @@ -2216,6 +2249,19 @@ en: last_seen_user: "Last Seen User:" reply_key: "Reply Key" skipped_reason: "Skip Reason" + incoming_emails: + from_address: "From" + to_addresses: "To" + cc_addresses: "Cc" + subject: "Subject" + error: "Error" + none: "No incoming emails found." + filters: + from_placeholder: "from@example.com" + to_placeholder: "to@example.com" + cc_placeholder: "cc@example.com" + subject_placeholder: "Subject..." + error_placeholder: "Error" logs: none: "No logs found." filters: @@ -2280,6 +2326,8 @@ en: change_category_settings: "change category settings" delete_category: "delete category" create_category: "create category" + block_user: "block user" + unblock_user: "unblock user" screened_emails: title: "Screened Emails" description: "When someone tries to create a new account, the following email addresses will be checked and the registration will be blocked, or some other action performed." @@ -2384,6 +2432,7 @@ en: moderator: "Moderator?" admin: "Admin?" blocked: "Blocked?" + staged: "Staged?" show_admin_profile: "Admin" edit_title: "Edit Title" save_title: "Save Title" @@ -2448,9 +2497,12 @@ en: deactivate_failed: "There was a problem deactivating the user." unblock_failed: 'There was a problem unblocking the user.' block_failed: 'There was a problem blocking the user.' + block_confirm: 'Are you sure you want to block this user? They will not be able to create any new topics or posts.' + block_accept: 'Yes, block this user' deactivate_explanation: "A deactivated user must re-validate their email." suspended_explanation: "A suspended user can't log in." block_explanation: "A blocked user can't post or start topics." + stage_explanation: "A staged user can only post via email in specific topics." trust_level_change_failed: "There was a problem changing the user's trust level." suspend_modal_title: "Suspend User" trust_level_2_users: "Trust Level 2 Users" @@ -2742,6 +2794,10 @@ en: mark_watching: 'm, w Watch topic' badges: + earned_n_times: + one: "Earned this badge 1 time" + other: "Earned this badge %{count} times" + more_with_badge: "Others with this badge" title: Badges allow_title: "can be used as a title" multiple_grant: "can be awarded multiple times" diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index af06b69d4f..96f3b18b22 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -346,7 +346,7 @@ es: '13': "Bandeja de entrada" '14': "Pendiente" categories: - all: "Todas las categorías" + all: "Categorías" all_subcategories: "todas" no_subcategory: "ninguna" category: "Categoría" @@ -948,7 +948,7 @@ es: reset_read: "Restablecer leídos" delete: "Eliminar temas" dismiss: "Descartar" - dismiss_read: "Descartar todos los temas no leídos" + dismiss_read: "Descartar todos los temas sin leer" dismiss_button: "Descartar..." dismiss_tooltip: "Descartar solo los nuevos posts o dejar de seguir los temas" also_dismiss_topics: "Parar de seguir estos temas para que no aparezcan más en mis mensajes no leídos" @@ -969,7 +969,7 @@ es: read: "Todavía no has leído ningún tema." posted: "Todavía no has publicado en ningún tema." latest: "No hay temas recientes. Qué pena..." - hot: "No hay temas calientes nuevos." + hot: "No hay temas candentes nuevos." bookmarks: "No tienes temas guardados en marcadores todavía." category: "No hay temas en la categoría {{category}}." top: "No hay temas en el top más vistos." @@ -979,7 +979,7 @@ es: unread: '

    Tus temas sin leer aparecerán aquí.

    Por defecto, los temas son considerados no leídos y mostrán contadores de post sin leer 1 si:

    • Creaste el tema
    • Respondiste al tema
    • Leíste el tema durante más de 4 minutos

    O si has establecido específicamente el tema a Seguir o Vigilar en el control de notificaciones al pie de cada tema.

    Puedes cambiar esto en tus preferencias.

    ' bottom: latest: "No hay más temas recientes para leer." - hot: "No hay más temas calientes." + hot: "No hay más temas candentes." posted: "No hay más temas publicados." read: "No hay más temas leídos." new: "No hay más nuevos temas." @@ -996,9 +996,15 @@ es: create: 'Crear tema' create_long: 'Crear un nuevo tema' private_message: 'Empezar un mensaje' + archive_message: + help: 'Archivar mensaje' + title: 'Archivar' + move_to_inbox: + title: 'Mover a la bandeja de entrada' + help: 'Restaurar mensaje a la bandeja de entrada' list: 'Temas' new: 'nuevo tema' - unread: 'No leídos' + unread: 'sin leer' new_topics: one: '1 tema nuevo' other: '{{count}} temas nuevos' @@ -1285,7 +1291,7 @@ es: via_email: "este post llegó por email" whisper: "esto post es un susurro privado para moderadores" wiki: - about: "Este post es tipo wiki, cualquier usuario registrado puede editarlo" + about: "este post es tipo wiki" archetypes: save: 'Guardar opciones' controls: @@ -1431,7 +1437,7 @@ es: category: can: 'puede… ' none: '(sin categoría)' - all: 'Todas las categorías' + all: 'Categorías' choose: 'Seleccionar una categoría…' edit: 'editar' edit_long: "Editar" @@ -1598,8 +1604,8 @@ es: other: "Recientes ({{count}})" help: "temas con posts recientes" hot: - title: "Popular" - help: "una selección de los temas más populares" + title: "Candente" + help: "una selección de los temas más candentes" read: title: "Leídos" help: "temas que ya has leído" @@ -1613,8 +1619,8 @@ es: unread: title: "Sin leer" title_with_count: - one: "Unread (1)" - other: "No leídos ({{count}})" + one: "Sin leer (1)" + other: "Sin leer ({{count}})" help: "temas que estás vigilando o siguiendo actualmente con posts no leídos" lower_title_with_count: one: "{{count}} sin leer" @@ -2647,7 +2653,7 @@ es: name: Enlace Popular description: Publicó un enlace externo con al menos 50 clicks hot_link: - name: Enlace Candente + name: Enlace popular description: Publicó un enlace externo con al menos 300 clicks famous_link: name: Enlace Famoso diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index 8ee2329582..ee94878919 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -130,7 +130,7 @@ fi: disabled: 'poisti listauksista %{when}' topic_admin_menu: "ketjun ylläpitotoimet" emails_are_disabled: "Ylläpitäjä on estänyt kaiken lähtevän sähköpostiliikenteen. Mitään sähköposti-ilmoituksia ei lähetetä." - edit: 'muokkaa tämän ketjun otsikkoa ja aluetta' + edit: 'muokkaa ketjun otsikkoa ja aluetta' not_implemented: "Tätä toimintoa ei ole vielä toteutettu, pahoittelut!" no_value: "Ei" yes_value: "Kyllä" @@ -231,7 +231,7 @@ fi: switch_from_anon: "Poistu anonyymitilasta" banner: close: "Sulje tämä banneri." - edit: "Muokkaa tätä banneria >>" + edit: "Muokkaa banneria >>" choose_topic: none_found: "Yhtään ketjua ei löydetty." title: @@ -295,7 +295,11 @@ fi: other: "%{count} käyttäjää" groups: empty: + posts: "Ryhmän jäsenet eivät ole kirjoittaneet viestejä." members: "Kukaan ei kuulu tähän ryhmään." + mentions: "Ryhmää ei ole mainittu." + messages: "Tällä ryhmällä ei ole yksityistä ketjua." + topics: "Ryhmän jäsenet eivät ole aloittaneet ketjuja." add: "Lisää" selector_placeholder: "Lisää jäseniä" owner: "omistaja" @@ -327,6 +331,7 @@ fi: description: "Saat ilmoituksen, jos joku mainitsee @nimesi tai vastaa sinulle." muted: title: "Vaimennetut" + description: "Et saa ilmoituksia uusista ketjuista tässä ryhmässä." user_action_groups: '1': "Annetut tykkäykset" '2': "Saadut tykkäykset" @@ -991,6 +996,12 @@ fi: create: 'Uusi ketju' create_long: 'Luo uusi ketju' private_message: 'Luo viesti' + archive_message: + help: 'Siirrä viesti arkistoosi' + title: 'Arkistoi' + move_to_inbox: + title: 'Siirrä saapuneisiin' + help: 'Siirrä takaisin saapuneisiin.' list: 'Ketjut' new: 'uusi ketju' unread: 'lukemattomat' @@ -1041,6 +1052,7 @@ fi: auto_close_title: 'Automaattisen sulkemisen asetukset' auto_close_save: "Tallenna" auto_close_remove: "Älä sulje tätä ketjua automaattisesti" + auto_close_immediate: "Viimeisin viesti tässä ketjussa on jo %{hours} tuntia vanha, joten ketju suljetaan heti." progress: title: ketjun edistyminen go_top: "alkuun" @@ -1279,7 +1291,7 @@ fi: via_email: "tämä viesti lähetettiin sähköpostitse" whisper: "tämä viesti on yksityinen kuiskaus valvojille" wiki: - about: "tämä viesti on wiki; peruskäyttäjät voivat muokata sitä" + about: "tämä viesti on wiki" archetypes: save: 'Tallennusasetukset' controls: @@ -2069,6 +2081,7 @@ fi: change_site_setting: "muuta sivuston asetusta" change_site_customization: "vaihda sivuston mukautusta" delete_site_customization: "poista sivuston mukautus" + change_site_text: "muutos sivuston teksteissä" suspend_user: "hyllytä käyttäjä" unsuspend_user: "poista hyllytys" grant_badge: "myönnä arvomerkki" @@ -2260,6 +2273,7 @@ fi: unlock_trust_level: "Avaa luottamustason lukitus" tl3_requirements: title: "Vaatimukset luottamustasolle 3." + table_title: "Edellisen %{time_period} päivän aikana:" value_heading: "Arvo" requirement_heading: "Vaatimus" visits: "Vierailua" @@ -2328,6 +2342,7 @@ fi: revert_confirm: "Oletko varma, että haluat kumota tekemäsi muutokset?" go_back: "Takaisin hakuun" recommended: "On suositeltavaa muokata seuraavaa tekstiä tarpeidesi mukaan:" + show_overriden: 'Näytä vain muokatut' site_settings: show_overriden: 'Näytä vain muokatut' title: 'Asetukset' diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index 3696f0690d..9c3327e99c 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -794,6 +794,7 @@ it: saved_local_draft_tip: "salvato in locale" similar_topics: "Il tuo argomento è simile a..." drafts_offline: "bozze offline" + group_mentioned: "Usando {{group}}, stai per mandare una notifica a {{count}} persone." error: title_missing: "Il titolo è richiesto" title_too_short: "Il titolo deve essere lungo almeno {{min}} caratteri" @@ -865,6 +866,7 @@ it: more: "visualizza le notifiche precedenti" total_flagged: "totale argomenti segnalati" mentioned: "

    {{username}} {{description}}

    " + group_mentioned: "

    {{username}} {{description}}

    " quoted: "

    {{username}} {{description}}

    " replied: "

    {{username}} {{description}}

    " posted: "

    {{username}} {{description}}

    " @@ -893,6 +895,7 @@ it: granted_badge: "Targhetta assegnata" popup: mentioned: '{{username}} ti ha menzionato in "{{topic}}" - {{site_title}}' + group_mentioned: '{{username}} ti ha menzionato in "{{topic}}" - {{site_title}}' quoted: '{{username}} ti ha citato in "{{topic}}" - {{site_title}}' replied: '{{username}} ti ha risposto in "{{topic}}" - {{site_title}}' posted: '{{username}} ha pubblicato in "{{topic}}" - {{site_title}}' @@ -948,6 +951,7 @@ it: dismiss_read: "Chiudi tutti i non letti" dismiss_button: "Chiudi..." dismiss_tooltip: "Chiudi solo gli ultimi messaggi o smetti di seguire gli argomenti" + also_dismiss_topics: "Smetti di tracciare questi topic così che non compaiano più come non letti" dismiss_new: "Chiudi Nuovo" toggle: "commuta la selezione multipla degli argomenti" actions: "Azioni Multiple" @@ -992,6 +996,12 @@ it: create: 'Nuovo Argomento' create_long: 'Crea un nuovo Argomento' private_message: 'Inizia a scrivere un messaggio' + archive_message: + help: 'Sposta il messaggio nel tuo archivio' + title: 'Archivio' + move_to_inbox: + title: 'Sposta in arrivo' + help: 'Sposta in arrivo' list: 'Argomenti' new: 'nuovo argomento' unread: 'non letto' @@ -1042,6 +1052,7 @@ it: auto_close_title: 'Impostazioni di auto-chiusura' auto_close_save: "Salva" auto_close_remove: "Non chiudere automaticamente questo argomento" + auto_close_immediate: "Il topic verrà chiuso immediatamente perché l'ultimo post risale a %{hours} ore fa." progress: title: Avanzamento dell'argomento go_top: "alto" @@ -1162,6 +1173,7 @@ it: success: "Abbiamo invitato l'utente a partecipare a questo messaggio." error: "Spiacenti, si è verificato un errore durante l'invito dell'utente." group_name: "nome gruppo" + controls: "Opzioni Argomento" invite_reply: title: 'Invita' username_placeholder: "nome utente" @@ -1184,7 +1196,7 @@ it: other: "{{count}} messaggi" cancel: "Rimuovi filtro" split_topic: - title: "Sposta in un Nuovo Argomento" + title: "Sposta in un nuovo argomento" action: "sposta in un nuovo argomento" topic_name: "Nome Nuovo Argomento" error: "Si è verificato un errore spostando il messaggio nel nuovo argomento." @@ -1279,7 +1291,7 @@ it: via_email: "questo messaggio è arrivato via email" whisper: "questo messaggio è un sussurro privato per i moderatori" wiki: - about: "questo messaggio è una guida; gli utenti base possono modificarla" + about: "questo post è una wiki" archetypes: save: 'Opzioni di salvataggio' controls: @@ -1805,6 +1817,7 @@ it: primary_group: "Imposta automaticamente come gruppo principale" group_owners: Proprietari add_owners: Aggiungi proprietari + incoming_email: "Indirizzo email personalizzato" incoming_email_placeholder: "inserisci l'indirizzo e-mail" api: generate_master: "Genera una Master API Key" @@ -1937,6 +1950,7 @@ it: email_templates: title: "Modelli e-mail" subject: "Oggetto" + multiple_subjects: "Questo template email ha più di un campo oggetto." body: "Corpo" none_selected: "Scegli un modello di e-mail per iniziare la modifica." revert: "Annulla Cambiamenti" @@ -2064,6 +2078,7 @@ it: change_site_setting: "modifica le impostazioni del sito" change_site_customization: "modifica la personalizzazione del sito" delete_site_customization: "cancella la personalizzazione del sito" + change_site_text: "cambia il testo del sito" suspend_user: "utente sospeso" unsuspend_user: "utente riattivato" grant_badge: "assegna targhetta" @@ -2316,9 +2331,15 @@ it: confirm: 'Conferma' dropdown: "A tendina" site_text: + description: "Puoi personalizzare qualsiasi testo del forum. Comincia effettuando una ricerca qui sotto:" search: "Trova il testo che vorresti modificare" title: 'Contenuto Testuale' edit: 'modifica' + revert: "Annulla i cambiamenti" + revert_confirm: "Sei sicuro di voler annullare i cambiamenti?" + go_back: "Torna alla ricerca" + recommended: "Consigliamo di personalizzare il testo seguente in modo da adattarlo alle necessità:" + show_overriden: 'Mostra solo le opzioni sovrascritte' site_settings: show_overriden: 'Mostra solo le opzioni sovrascritte' title: 'Impostazioni' @@ -2439,6 +2460,7 @@ it: crawling_description: "Quando Discourse crea gli argomenti per i tuoi messaggi, se non è presente nessun feed RSS/ATOM, cercherà di estrarre il contenuto dal codice HTML. Il contenuto può risultate a volte ostico da estrarre e, per semplificare il processo, forniamo la possibilità di specificare le regole CSS." embed_by_username: "Nome utente per la creazione dell'argomento" embed_post_limit: "Numero massimo di messaggi da includere" + embed_username_key_from_feed: "Chiave per ottenere il nome utente discourse dal feed" embed_truncate: "Tronca i messaggi incorporati" embed_whitelist_selector: "Selettore CSS per gli elementi da permettere negli embed" embed_blacklist_selector: "Selettore CSS per gli elementi da rimuovere dagli embed" diff --git a/config/locales/client.ro.yml b/config/locales/client.ro.yml index 7594d81a4d..9b9f16e1fa 100644 --- a/config/locales/client.ro.yml +++ b/config/locales/client.ro.yml @@ -98,6 +98,15 @@ ro: one: "acum o zi" few: "acum %{count} zile" other: "acum %{count} zile" + later: + x_days: + one: "După 1 zi" + few: "După %{count} zile" + other: "După %{count} zile" + x_years: + one: "După 1 an" + few: "După %{count} ani" + other: "După %{count} ani" share: topic: 'distribuie adresă către această discuție' post: 'distribuie o adresă către postarea #%{postNumber}' @@ -113,11 +122,19 @@ ro: disabled: 'deschis %{when}' closed: enabled: 'inchis %{when}' + disabled: 'deschis %{when}' archived: enabled: 'arhivat %{when}' disabled: 'dezarhivat %{when}' pinned: enabled: 'Prins %{when}' + disabled: 'desprinse %{when}' + pinned_globally: + enabled: 'fixat global %{when}' + disabled: 'desprins %{when}' + visible: + enabled: 'listat %{when}' + disabled: 'retras %{when}' topic_admin_menu: "acțiuni subiect administrator" emails_are_disabled: "Trimiterea de emailuri a fost dezactivată global de către un administrator. Nu vor fi trimise notificări email de nici un fel." edit: 'editează titlul și categoria acestui subiect' @@ -215,15 +232,18 @@ ro: saved: "Salvat!" upload: "Încarcă" uploading: "Încărcare..." + uploading_filename: "Se încarcă {{filename}}..." uploaded: "Încărcat!" enable: "Activează" disable: "Dezactivează" undo: "Anulează acțiunea precedentă" - revert: "Rescrie acțiunea precedentă" + revert: "Refacere" failed: "Eșuat" switch_to_anon: "Mod anonim" + switch_from_anon: "Ieșire din mod anonim" banner: close: "Ignoră acest banner." + edit: "Editează acest banner >>" choose_topic: none_found: "Nu au fost găsite discuții." title: @@ -239,6 +259,10 @@ ro: edit: "Editează" cancel: "Anulează" view_pending: "vezi postările în aşteptare" + has_pending_posts: + one: "Această discuție are 1 postare în așteptare" + few: "Această discuție are 1 postare în așteptare" + other: "Această discuţie are {{count}} postări în aşteptare." confirm: "Salvează Schimbările" delete_prompt: "Sunteţi sigur că vreţi să ştergeţi %{username}? Această operaţiune va şterge toate postările, va bloca adresa de email şi adresa de IP." approval: @@ -285,6 +309,7 @@ ro: few: "%{count} utilizatori" other: "%{count} utilizatori" groups: + add: "Adăugați" visible: "Grupul este vizibil tuturor utilizatorilor" title: one: "grup" @@ -1139,8 +1164,6 @@ ro: no_value: "Nu, pastrează" yes_value: "Da, abandonează" via_email: "acest post a sosit via email" - wiki: - about: "Acest post este un wiki; oricine poate edita" archetypes: save: 'Opțiuni de salvare' controls: @@ -1848,6 +1871,7 @@ ro: ip_address: "Adresa IP" topic_id: "ID Discuție" post_id: "ID Mesaj" + category_id: "ID categorie" delete: 'Șterge' edit: 'Editează' save: 'Salvează' @@ -2221,6 +2245,23 @@ ro: name: "Nume" image: "Imagine" delete_confirm: "Sunteţi sigur că doriţi să ștergeți :%{name}: emoji?" + embedding: + save: "Salvați setările pentru embeding" + permalink: + title: "Link-uri" + url: "URL" + topic_id: "ID discuție" + topic_title: "Discuție" + post_id: "ID postare" + post_title: "Postare" + category_id: "ID categorie" + category_title: "Categorie" + external_url: "URL extern" + delete_confirm: Sigur doriți să ștergeți acest link ? + form: + label: "Nou:" + add: "Adăugați" + filter: "Căutare (URL sau URL extern)" lightbox: download: "descarcă" search_help: @@ -2236,6 +2277,7 @@ ro: categories: 'g apoi c Categorii' top: 'g, t Top' bookmarks: 'g, b Semne de carte' + messages: 'g, m Mesaje' navigation: title: 'Navigare' jump: '# Mergi la mesajul #' @@ -2247,31 +2289,33 @@ ro: title: 'Applicația' create: 'c Crează discuție nouă' notifications: 'n Deschide notificare' + hamburger_menu: '= Deschide meniul hamburger' user_profile_menu: 'p Deschide meniu utilizator' show_incoming_updated_topics: '. Arată discuţiile actualizate' search: '/ Caută' - help: '? Deschide ajutorul de scurtături de tastatură' - dismiss_new_posts: 'x, r Respinge Nou/Mesaj' - dismiss_topics: 'x, t Respinge Discuţia' + help: '? Vezi comenzi rapide disponibile' + dismiss_new_posts: 'x, r Respinge Nou/Mesaje' + dismiss_topics: 'x, t Respinge discuţiile' + log_out: 'shift+z shift+z Ieșire din cont' actions: title: 'Acțiuni' bookmark_topic: 'f Comută semnul de carte pentru discuţie' - pin_unpin_topic: 'shift+p Pin/Unpin topic' + pin_unpin_topic: 'shift+p Fixează/Desprinde discuția' share_topic: 'shift s distribuie discuție' share_post: 's Distribuie mesajul' - reply_as_new_topic: 't Răspunde că discuţie legată' + reply_as_new_topic: 't Răspunde că discuţie conexă' reply_topic: 'shift r Raspunde la discuție' reply_post: 'r Răspunde la postare' quote_post: 'q Citează mesajul' like: 'l Apreciează mesajul' - flag: '! Marchează mesajul' - bookmark: 'b Marchează cu semn de carte postarea' + flag: '! Reclamă mesajul' + bookmark: 'b Salvează postarea' edit: 'e Editează mesaj' delete: 'd Șterge mesaj' - mark_muted: 'm apoi m Marchează discuția ca silențios' - mark_regular: 'm apoi r Marchează discuția ca normală' - mark_tracking: 'm apoi t Marchează discuția ca urmărită' - mark_watching: 'm apoi w Marchează discuția ca privită' + mark_muted: 'm apoi m Treceți discuția în mod silențios' + mark_regular: 'm, r Discuție normală (implicită)' + mark_tracking: 'm, t Urmăriți discuția' + mark_watching: 'm, w Urmăriți discuția îndeaproape' badges: title: Insigne allow_title: "poate fi folosit ca titlu" @@ -2292,7 +2336,7 @@ ro: none: "" badge_grouping: getting_started: - name: Să începem + name: Cum începem community: name: Communitate trust_level: @@ -2300,22 +2344,23 @@ ro: other: name: Altele posting: - name: Scrie mesaj + name: Postând badge: editor: name: Editor - description: Primul mesaj editat + description: Prima postare editată basic_user: name: De baza - description: Acordată toate funcțiile esențiale + description: Vi se permite acces la toate funcțiile esențiale ale comunității member: name: Membru - description: Invitaţii Acordate + description: Permisiuni invitații regular: - name: Normal + name: Obișnuit + description: Vi se permit recategorisirea, redenumirea, link-urile urmărite și acces lounge leader: name: Lider - description: Acrodată recategorisește , redenumește, adrese urmărite și lounge + description: Vi se permit editarea globală, fixarea, arhivarea, despărțirea și combinarea welcome: name: Bine ai venit description: A primit o apreceiere @@ -2344,39 +2389,59 @@ ro: name: Discuţie Foarte Bună description: A primit 50 de aprecieri pentru o discuţie. Această insignă poate fi acordată de mai multe ori nice_share: - name: Drăguţ + name: Distribuire drăguță description: A împărţit un mesaj cu 25 utilizatori unici good_share: - name: Bun - description: A împărţit un mesaj cu 300 utilizatori unici + name: Distribuire bună + description: A distribuit un mesaj către 300 utilizatori unici great_share: - name: Perfect - description: A împărţit un mesaj cu 1000 utilizatori unici + name: Super distribuire + description: A distribuit un mesaj către 1000 utilizatori unici first_like: name: Prima apreciere description: A apreciat un mesaj first_flag: - name: Primul marcaj - description: A marcat un mesaj + name: Prima reclamație + description: A reclamat un mesaj promoter: name: Promotor description: A invitat un utilizator campaigner: name: Combatant + description: A invitat 3 membri simpli (nivel de incredere 1) champion: name: Campion + description: A invitat 5 membri (nivel de incredere 2) first_share: name: Primul description: A distribuit un mesaj first_link: - name: Prima adresă + name: Primul link description: A adăugat o adresă internă catre altă discuție first_quote: name: Primul citat description: A citat un alt utilizator read_guidelines: - name: Citește reguli de ajutor - description: Citește comune + name: Citește ghidul comunității + description: Citește ghidul comunității reader: - name: Cititorul - description: Citeşte fiecare mesaj dintr-o discuție cu mai mult de 100 de mesaje + name: Cititor + description: A citit fiecare mesaj dintr-o discuție cu mai mult de 100 de mesaje + popular_link: + name: Link popular + description: A postat un link extern cu cel puțin 50 de accesări + hot_link: + name: Link interesant + description: A postat un link extern cu cel puțin 300 de accesări + famous_link: + name: Link foarte popular + description: A postat un link extern cu cel puțin 1000 de accesări + google_search: | +

    Căutați cu Google

    +

    + + + + + +

    diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index bda7b6b2a0..d080a32d22 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -970,6 +970,7 @@ ru: current_user: 'перейти на вашу страницу пользователя' topics: bulk: + unlist_topics: "Исключить из списков" reset_read: "Сбросить прочтённые" delete: "Удалить темы" dismiss: "OK" @@ -1021,6 +1022,10 @@ ru: create: 'Создать Тему' create_long: 'Создать новую тему' private_message: 'Написать сообщение' + archive_message: + help: 'Переместить сообщение в архив' + move_to_inbox: + help: 'Переместить сообщение во входящие' list: 'Темы' new: 'новая тема' unread: 'непрочитанно' @@ -1324,8 +1329,6 @@ ru: yes_value: "Да, отказаться" via_email: "это сообщение пришло с почты" whisper: "Это внутреннее сообщение, т.е. оно видно только модераторам" - wiki: - about: "это вики-сообщение - любой пользователь может отредактировать его, чтобы улучшить, дополнить или исправить ошибки" archetypes: save: 'Параметры сохранения' controls: @@ -1584,6 +1587,7 @@ ru: submit_tooltip: "Отправить приватную отметку" take_action_tooltip: "Достигнуть порога жалоб не дожидаясь большего количества жалоб от сообщества" cant: "Извините, но вы не можете сейчас послать жалобу." + notify_staff: 'Уведомить администрацию' formatted_name: off_topic: "Это не по теме" inappropriate: "Это неприемлемо" @@ -2530,6 +2534,8 @@ ru: categories: 'g, c Разделы' top: 'g, t Вверх' bookmarks: 'g, b Закладки' + profile: 'Профиль' + messages: 'Сообщения' navigation: title: 'Навигация' jump: '# Перейти к сообщение №' @@ -2541,12 +2547,14 @@ ru: title: 'Приложение' create: 'c Создать новую тему' notifications: 'n Открыть уведомления' + hamburger_menu: 'Открыть меню' user_profile_menu: 'p Открыть меню моего профиля' show_incoming_updated_topics: '. Показать обновленные темы' search: '/ Поиск' help: '? Открыть помощь по горячим клавишам' dismiss_new_posts: 'x, r Отложить новые сообщения' dismiss_topics: 'x, t Отложить темы' + log_out: 'Выйти' actions: title: 'Действия' bookmark_topic: 'f Добавить в Избранное' diff --git a/config/locales/client.sk.yml b/config/locales/client.sk.yml new file mode 100644 index 0000000000..97251a682b --- /dev/null +++ b/config/locales/client.sk.yml @@ -0,0 +1,2729 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +sk: + js: + number: + format: + separator: "," + delimiter: "," + human: + storage_units: + format: '%n %u' + units: + byte: + one: bajt + few: bajtov + other: bajtov + gb: GB + kb: KB + mb: MB + tb: TB + short: + thousands: "{{number}}tis" + millions: "{{number}}mil" + dates: + time: "h:mm a" + long_no_year: "MMM D h:mm a" + long_no_year_no_time: "MMM D" + full_no_year_no_time: "MMMM Do" + long_with_year: "MMM D, YYYY h:mm a" + long_with_year_no_time: "MMM D, YYYY" + full_with_year_no_time: "MMMM Do, YYYY" + long_date_with_year: "MMM D, 'YY LT" + long_date_without_year: "MMM D, LT" + long_date_with_year_without_time: "MMM D, 'YY" + long_date_without_year_with_linebreak: "MMM D
    LT" + long_date_with_year_with_linebreak: "MMM D, 'YY
    LT" + tiny: + half_a_minute: "< 1m" + less_than_x_seconds: + one: "< 1s" + few: "< %{count}s" + other: "< %{count}s" + x_seconds: + one: "1s" + few: "1s" + other: "%{count}s" + less_than_x_minutes: + one: "< 1m" + few: "< 1m" + other: "< %{count}m" + x_minutes: + one: "1m" + few: "%{count}m" + other: "%{count}m" + about_x_hours: + one: "1h" + few: "%{count}h" + other: "%{count}h" + x_days: + one: "1d" + few: "%{count}d" + other: "%{count}d" + about_x_years: + one: "1r" + few: "%{count}r" + other: "%{count}r" + over_x_years: + one: "> 1r" + few: "> %{count}r" + other: "> %{count}r" + almost_x_years: + one: "1r" + few: "%{count}r" + other: "%{count}r" + date_month: "MMM D" + date_year: "MMM 'YY" + medium: + x_minutes: + one: "1 minúta" + few: "%{count} minúty" + other: "%{count} minút" + x_hours: + one: "1 hodina" + few: "%{count} hodiny" + other: "%{count} hodín" + x_days: + one: "1 deň" + few: "%{count} dni" + other: "%{count} dní" + date_year: "MMM D, 'YY" + medium_with_ago: + x_minutes: + one: "pred 1 minútou" + few: "pred %{count} minútami" + other: "pred %{count} minútami" + x_hours: + one: "pred 1 hodinou" + few: "pred %{count} hodinami" + other: "pred %{count} hodinami" + x_days: + one: "pred 1 dňom" + few: "pred %{count} dňami" + other: "pred %{count} dňami" + later: + x_days: + one: "1 deň neskôr" + few: "%{count} dni neskôr" + other: "%{count} dní neskôr" + x_months: + one: "1 mesiac neskôr" + few: "%{count} mesiace neskôr" + other: "%{count} mesiacov neskôr" + x_years: + one: "1 rok neskôr" + few: "%{count} roky neskôr" + other: "%{count} rokov neskôr" + share: + topic: 'zdieľaj odkaz na túto tému' + post: 'príspevok #%{postNumber}' + close: 'zatvoriť' + twitter: 'zdieľaj odkaz na Twitteri' + facebook: 'zdieľaj odkaz na Facebooku' + google+: 'zdieľaj odkaz na Google+' + email: 'pošli odkaz emailom' + action_codes: + split_topic: "rozdeľ tému %{when}" + autoclosed: + enabled: 'uzavreté %{when}' + disabled: 'otvorené %{when}' + closed: + enabled: 'uzavreté %{when}' + disabled: 'otvorené %{when}' + archived: + enabled: 'archivované %{when}' + disabled: 'odarchivované %{when}' + pinned: + enabled: 'pripnuné %{when}' + disabled: 'odopnuté %{when}' + pinned_globally: + enabled: 'globálne pripnuté %{when}' + disabled: 'odopnuté %{when}' + visible: + enabled: 'zverejnené %{when}' + disabled: 'stiahnuté %{when}' + topic_admin_menu: "akcie administrátora témy" + emails_are_disabled: "Odosielanie emailov bolo globálne vypnuté administrátorom. Žiadne emailové notifikácie nebudú odoslané." + edit: 'upraviť názov a kategóriu témy' + not_implemented: "Táto funkcia ešte bohužiaľ nie je implementovaná." + no_value: "Nie" + yes_value: "Áno" + generic_error: "Bohužiaľ nastala chyba." + generic_error_with_reason: "Nastala chyba: %{error}" + sign_up: "Registrácia" + log_in: "Prihlásenie" + age: "Vek" + joined: "Registovaný" + admin_title: "Administrácia" + flags_title: "Nahlásenie" + show_more: "zobraz viac" + show_help: "možnosti" + links: "Odkazy" + links_lowercase: + one: "odkaz" + few: "odkazy" + other: "odkazy" + faq: "FAQ" + guidelines: "Pokyny" + privacy_policy: "Ochrana súkromia" + privacy: "Súkromie" + terms_of_service: "Podmienky používania" + mobile_view: "Mobilná verzia" + desktop_view: "Desktop verzia" + you: "Vy" + or: "alebo" + now: "práve teraz" + read_more: 'čítaj ďalej' + more: "Viac" + less: "Menej" + never: "nikdy" + daily: "denne" + weekly: "týždenne" + every_two_weeks: "každé dva týždne" + every_three_days: "každé tri dni" + max_of_count: "najviac {{count}}" + alternation: "alebo" + character_count: + one: "1 znak" + few: "{{count}} znakov" + other: "{{count}} znakov" + suggested_topics: + title: "Odporúčané témy" + about: + simple_title: "O fóre" + title: "O %{title}" + stats: "Štatistiky stránky" + our_admins: "Naši admini" + our_moderators: "Naši moderátori" + stat: + all_time: "Za celú dobu" + last_7_days: "Posledných 7 dní" + last_30_days: "Posledných 30 dní" + like_count: "Páči sa mi" + topic_count: "Témy" + post_count: "Príspevky" + user_count: "Noví používatelia" + active_user_count: "Aktívni používatelia" + contact: "Kontaktujte nás" + contact_info: "V prípade kritickej chyby alebo naliehavej záležitosti nás prosím konaktujte na %{contact_info}." + bookmarked: + title: "Záložka" + clear_bookmarks: "Odstrániť záložku" + help: + bookmark: "Kliknutím vložíte záložku na prvý príspevok tejto témy" + unbookmark: "Kliknutím odstánite všetky záložky v tejto téme" + bookmarks: + not_logged_in: "pre pridanie záložky sa musíte prihlásiť" + created: "záložka bola pridaná" + not_bookmarked: "príspevok je prečítaný, kliknite pre pridanie záložky" + last_read: "toto je posledný prečítaný príspevok, kliknite pre pridanie záložky" + remove: "Odstrániť záložku" + confirm_clear: "Ste si istý že chcete odstrániť všetky záložky z tejto témy?" + topic_count_latest: + one: "1 nová alebo upravená téma." + few: "{{count}} nové alebo upravené témy." + other: "{{count}} nových alebo upravených tém." + topic_count_unread: + one: "1 neprečítaná téma." + few: "{{count}} neprečítané témy." + other: "{{count}} neprečítaných tém." + topic_count_new: + one: "1 nová téma." + few: "{{count}} nové témy." + other: "{{count}} nových tém." + click_to_show: "Kliknite pre zobrazenie." + preview: "náhľad" + cancel: "zrušiť" + save: "Uložiť zmeny" + saving: "Ukladám zmeny..." + saved: "Zmeny uložené." + upload: "Upload" + uploading: "Upload prebieha..." + uploading_filename: "Nahrávám {{filename}}..." + uploaded: "Upload úspešne dokončený." + enable: "Zapnúť" + disable: "Vypnúť" + undo: "Späť" + revert: "Vrátiť zmeny" + failed: "Nepodarilo sa" + switch_to_anon: "Anonymný mód" + switch_from_anon: "Opustiť anonymný mód" + banner: + close: "Zamietnuť tento banner." + edit: "Upraviť tento banner >>" + choose_topic: + none_found: "Nenašli sa žiadne témy." + title: + search: "Hľadaj tému podľa názvu, url alebo id:" + placeholder: "sem napíšte názov témy" + queue: + topic: "Téma:" + approve: 'Schváliť' + reject: 'Zamietnuť' + delete_user: 'Odstrániť používateľa' + title: "Vyžaduje schválenie" + none: "Žiadne príspevky na kontrolu." + edit: "Upraviť" + cancel: "Zrušiť" + view_pending: "zobraziť príspevky čakajúce na schválenie" + has_pending_posts: + one: "Téma má {{count}} príspevkov čakajúci na schválenie" + few: "Téma má {{count}} príspevky čakajúce na schválenie" + other: "Téma má {{count}} príspevkov čakajúcich na schválenie" + confirm: "Uložiť zmeny" + delete_prompt: "Ste si istý, že chcete vymazať %{username}? To odstráni všetky ich príspevky a zablokuje ich emailové a IP adresy." + approval: + title: "Príspevok vyžaduje schválenie" + description: "Váš príspevok sme obdžali, ale skôr než bude zverejnený musí byť schválený moderátorom. Prosíme o trpezlivosť." + pending_posts: + one: "Máte 1 neprečítaný príspevok." + few: "Máte {{count}} neprečítané príspevky." + other: "Máte {{count}} neprečítaných príspevkov." + ok: "OK" + user_action: + user_posted_topic: "{{user}} založil tému" + you_posted_topic: "Vy ste založili tému" + user_replied_to_post: "{{user}} odpovedal na {{post_number}}" + you_replied_to_post: "Vy ste odpovedali na {{post_number}}" + user_replied_to_topic: "{{user}} odpovedal na tému" + you_replied_to_topic: "Vy ste odpovedali natému" + user_mentioned_user: "{{user}} spomenul {{another_user}}" + user_mentioned_you: "{{user}} spomenul Vás" + you_mentioned_user: "Vy ste spomenuli {{another_user}}" + posted_by_user: "Príspevok od {{user}}" + posted_by_you: "Príspevok od Vás" + sent_by_user: "Poslané od {{user}}" + sent_by_you: "Poslané Vami" + directory: + filter_name: "filtrovať podľa používateľského mena" + title: "Používatelia" + likes_given: "Rozdané" + likes_received: "Prijaté" + topics_entered: "Navštívené" + topics_entered_long: "Navšívených tém" + time_read: "Čas strávený čítaním" + topic_count: "Témy" + topic_count_long: "Vytvorených tém" + post_count: "Odpovede" + post_count_long: "Odpovedí" + no_results: "Žiadne výsledky" + days_visited: "Návštev" + days_visited_long: "Navštívených dní" + posts_read: "Prečítané" + posts_read_long: "Prečítaných príspevkov" + total_rows: + one: "1 užívateľ" + few: "%{count} užívatelia" + other: "%{count} užívateľov" + groups: + add: "Pridať" + selector_placeholder: "Pridať členov" + owner: "vlastník" + visible: "Skupina je viditeľná všetkým používateľom" + title: + one: "skupina" + few: "skupiny" + other: "skupiny" + members: "Členovia" + posts: "Príspevky" + alias_levels: + title: "Kto môže poslať správu a @uváadzať túto skupinu?" + nobody: "Nikto" + only_admins: "Iba administrátori" + mods_and_admins: "Iba moderátori a administrátori" + members_mods_and_admins: "Iba členovia skupiny, moderátori a administrátori" + everyone: "Každý" + trust_levels: + title: "Stupeň dôvery automaticky pridelený členom po ich pridaní:" + none: "Žiadny" + user_action_groups: + '1': "Rozdaných 'páči sa mi'" + '2': "Obdržaných 'páči sa mi'" + '3': "Záložky" + '4': "Témy" + '5': "Odpovede" + '6': "Odozvy" + '7': "Zmienky" + '9': "Citácie" + '10': "Obľúbené" + '11': "Úpravy" + '12': "Odoslané správy" + '13': "Prijaté správy" + '14': "Čakajúce správy" + categories: + all: "všetky kategórie" + all_subcategories: "všetky" + no_subcategory: "žiadne" + category: "Kategória" + reorder: + title: "Usporiadať Kategórie" + title_long: "Usporiadať zoznam kategórií" + fix_order: "Pevné pozície" + fix_order_tooltip: "Nie všetky kategórie majú unikátne číslo pozície, čo môže zpôsobovať neočakávané výsledky." + save: "Ulož poradie" + apply_all: "Použi" + position: "Pozícia" + posts: "Príspevky" + topics: "Témy" + latest: "Najnovšie" + latest_by: "najnovšie podľa" + toggle_ordering: "zmeniť radenie" + subcategories: "Podkategórie" + topic_stats: "Počet nových tém." + topic_stat_sentence: + one: "%{count} nová téma za posledných %{unit}." + few: "%{count} nové témy za posledných %{unit}." + other: "%{count} nových tém za posledných %{unit}." + post_stats: "Počet nových príspevkov." + post_stat_sentence: + one: "%{count} nový príspevok za posledných %{unit}." + few: "%{count} nové príspevky za posledných %{unit}." + other: "%{count} nových príspevkov za posledných %{unit}." + ip_lookup: + title: Vyhľadávanie podľa IP adresy + hostname: Hostname + location: Lokácia + location_not_found: (neznáma) + organisation: Organizácia + phone: Telefón + other_accounts: "Ostatné účty s touto IP adresou:" + delete_other_accounts: "Zmazaných %{count}" + username: "používateľské meno" + trust_level: "Dôvera" + read_time: "čas strávený čítaním" + topics_entered: "založených tém" + post_count: "# príspevkov" + confirm_delete_other_accounts: "Ste si istý že chcete zmazať tieto účty?" + user_fields: + none: "(vyberte možnosť)" + user: + said: "{{username}}:" + profile: "Profil" + mute: "Ignorovať" + edit: "Upraviť nastavenia" + download_archive: "Stiahnutie mojich prípevkov" + new_private_message: "Nová správa" + private_message: "Správa" + private_messages: "Správy" + activity_stream: "Aktivita" + preferences: "Nastavenia" + expand_profile: "Rozbaľ" + bookmarks: "Záložky" + bio: "O mne" + invited_by: "Pozvaný od" + trust_level: "Stupeň dôvery" + notifications: "Upozornenia" + desktop_notifications: + label: "Upozornenia na pracovnej ploche" + not_supported: "Tento prehliadač nepodporuje upozornenia. Prepáčte." + perm_default: "Zapnúť upozornenia" + perm_denied_btn: "Prístup zamietnutý" + perm_denied_expl: "Notifikácie nie sú povolené. Povoľte notifikácie vo vašom prehliadači a potom kliknite na tlačidlo. (Desktop: ľavá krajná ikona v adresnom riadku. Mobil: 'Site info')" + disable: "Zakázať upozornenia" + currently_enabled: "(momentálne povolené)" + enable: "Povoliť upozornenia" + currently_disabled: "(momentálne nepovolené)" + each_browser_note: "Poznámka: Toto nastavenie musíte zmeniť v každom používanom prehliadači." + dismiss_notifications: "Označiť všetky ako prečítané" + dismiss_notifications_tooltip: "Označiť všetky neprečítané upozornenia ako prečítané" + disable_jump_reply: "Neskočiť na môj príspevok po odpovedi" + dynamic_favicon: "Zobraziť počet nových/upravených tém na ikone prehliadača" + edit_history_public: "Povoliť ostatným užívateľom zobrazenie všetkých verzií môjho príspevku" + external_links_in_new_tab: "Otvoriť všekty externé odkazy v novej záložke" + enable_quoting: "Umožniť odpoveď s citáciou z označeného textu" + change: "zmeniť" + moderator: "{{user}} je moderátor" + admin: "{{user}} je administrátor" + moderator_tooltip: "Tento používateľ je moderátor" + admin_tooltip: "Tento používateľ je administrátor" + blocked_tooltip: "Tento používateľ je zablokovaný" + suspended_notice: "Tento používateľ je suspendovaný do {{date}}" + suspended_reason: "Dôvod:" + github_profile: "Github" + mailing_list_mode: "Pošli mi email pri každom novom príspevku (pokiaľ neignorujem tému alebo kategóriu)" + watched_categories: "Sledované" + watched_categories_instructions: "Budete automaticky sledovať všetky nové témy v týchto kategóriách. Budete upozornený na všetky nové príspevky a témy, a zároveň bude vedľa témy zobrazený počet nových príspevkov." + tracked_categories: "Sledované" + tracked_categories_instructions: "Budete automaticky sledovať všetky nové témy v týchto kategóriách. Počet nových príspevkov sa bude zobraziť vedľa témy." + muted_categories: "Ignorovaný" + muted_categories_instructions: "Nebudete informovaní o udalostiach v nových témach týchto kategórií. Tieto témy sa zároveň nebudú zobrazovať v zozname posledných udalostí." + delete_account: "Vymazať môj účet" + delete_account_confirm: "Ste si istý, že chcete permanentne vymazať váš účet? Táto akcia je nenávratná." + deleted_yourself: "Váš účet bol úspešne vymazaný." + delete_yourself_not_allowed: "Momentálne nie je možné vymazať váš učet. Kontaktujte administrátora a ten vám ho vymaže." + unread_message_count: "Správy" + admin_delete: "Vymazať" + users: "Používatelia" + muted_users: "Ignorovaný" + muted_users_instructions: "Pozastaviť všetky notifikácie od týchto užívateľov." + muted_topics_link: "Zobraziť umlčané témy" + automatically_unpin_topics: "Automaticky zrušiť pripnutie témy ak sa dočítate na koniec." + staff_counters: + flags_given: "nápomocné značky" + flagged_posts: "označkované príspevky" + deleted_posts: "vymazané príspevky" + suspensions: "pozastavenia" + warnings_received: "upozornenia" + messages: + all: "Všetky" + mine: "Moje" + unread: "Neprečítané" + groups: "Moje skupiny" + change_password: + success: "(email odoslaný)" + in_progress: "(email sa odosiela)" + error: "(chyba)" + action: "Odoslať email na reset hesla" + set_password: "Nastaviť heslo" + change_about: + title: "Upraviť O mne" + error: "Pri úprave hodnoty nastala chyba" + change_username: + title: "Zmeniť užívateľské meno" + confirm: "Ak si zmeníte vaše užívateľské meno, všetky predchádajúce cítacie vašich príspevkov a @zmienok prestanú platiť. Ste si určite istý, že to chcete?" + taken: "Sorry, toto užívateľské meno je obsadené." + error: "Pri zmene užívateľského mena prišlo k chybe." + invalid: "Toto užívateľské meno nie je platné. Musí obsahovať iba znaky a čísla." + change_email: + title: "Zmeniť email" + taken: "Prepáčte, tento email je už obsadený." + error: "Pri zmene emailu nastala chyba. Je možné, že je email už použitý?" + success: "Na email sme odoslali správu. Nasledujte prosím inštrukcie pre potvrdenie." + change_avatar: + title: "Zmeniť váš profilový obrázok" + gravatar: "Gravatar, podľa" + gravatar_title: "Zmeňte váš avatar na webe Gravatar " + refresh_gravatar_title: "Obnoviť váš Gravatar" + letter_based: "Systém pridelil profilový obrázok" + uploaded_avatar: "Vlastný obrázok" + uploaded_avatar_empty: "Pridať vlastný obrázok" + upload_title: "Nahrať váš obrázok" + upload_picture: "Nahrať obrázok" + image_is_not_a_square: "Upozornenie: váš obrázok sme orezali; mal rozdielnu šírku a výšku" + cache_notice: "Váš profilový obrázok bol úspešne zmenený, ale jeho zobrazenie môže chvíľu trvať kvôli vyrovnávacej pamäti prehliadača." + change_profile_background: + title: "Pozadie profilu" + instructions: "Pozadie profilu bude vystredené a s predvolenou šírkou 850px." + change_card_background: + title: "Pozadie karty používateľa" + instructions: "Obrázky pozadia budú vystredené a s predvolenou šírkou 590px." + email: + title: "Email" + instructions: "Nikdy verejne nezobrazené" + ok: "Pošleme vám email pre potvrdenie" + invalid: "Zadajte prosím platný email" + authenticated: "Váš email bude autentifikovaný pomocou {{provider}}" + frequency_immediately: "Odošleme vám email ak ste neprečítali to, čo vám posielame emailom." + frequency: + one: "Odošleme vám email iba ak sme vás nevideli poslednú minútu" + few: "Odošleme vám email iba ak sme vás nevideli posledné {{count}} minúty." + other: "Odošleme vám email iba ak sme vás nevideli posledných {{count}} minút" + name: + title: "Meno" + instructions: "Vaše celé meno (nepovinné)" + instructions_required: "Vaše celé meno" + too_short: "Vaše meno je prikrátke" + ok: "Vaše meno je v poriadku" + username: + title: "Užívateľské meno" + instructions: "Unikátne, bez medzier, krátke" + short_instructions: "Ostatní vás môžu označiť ako @{{username}}" + available: "Vaše užívateľské meno je voľné" + global_match: "Email zodpovedá registrovanému užívateľskému menu" + global_mismatch: "Už zaregistrované. Skúste {{suggestion}}?" + not_available: "Nie je k dispozícii. Skúste {{suggestion}}?" + too_short: "Vaše užívateľské meno je prikrátke" + too_long: "Vaše užívateľské meno je pridlhé" + checking: "Kontrolujeme dostupnosť užívateľského meno" + enter_email: 'Užívateľské meno nájdené, zadajte zodpovedajúci email' + prefilled: "Email zodpovedá tomuto registrovanému užívateľskému menu" + locale: + title: "Jazyk rozhrania" + instructions: "Jazyk úžívateľského rozhrania. Zmení sa po obnovení stránky." + default: "(predvolené)" + password_confirmation: + title: "Heslo znova" + last_posted: "Posledný príspevok" + last_emailed: "Posledný odemailovaný" + last_seen: "Videný" + created: "Spojený" + log_out: "Odhlásiť sa" + location: "Poloha" + card_badge: + title: "Odznak karty užívateľa" + website: "Webová stránka" + email_settings: "Email" + email_digests: + title: "Ak to tu nenavštívim, pošlite mi emailový súhrn s novinkami:" + daily: "denne" + every_three_days: "každé tri dni" + weekly: "týždenne" + every_two_weeks: "každé dva týždne" + email_direct: "Pošlite mi email ak ma niekto cituje, odpovie na môj príspevok, spomenie moje @užívateľské meno alebo ma pozve do témy." + email_private_messages: "Pošlite mi email keď mi niekto pošle správu" + email_always: "Pošlite mi emailovú notifikáciu aj keď som aktívny na stránke" + other_settings: "Ostatné" + categories_settings: "Kategórie" + new_topic_duration: + label: "Považuj témy za nové keď" + not_viewed: "Ešte som ich nevidel" + last_here: "vytvorené odkedy som tu bol naposledy" + after_1_day: "vytvorené za posledný deň" + after_2_days: "vytvorené posledné 2 dni" + after_1_week: "vytvorené za posledný týždeň" + after_2_weeks: "vytvorené za posledné 2 týždne" + auto_track_topics: "Automaticky sleduj témy, do ktorých vstúpim" + auto_track_options: + never: "nikdy" + immediately: "ihneď" + after_30_seconds: "po 30 sekundách" + after_1_minute: "po 1 minúte" + after_2_minutes: "po 2 minútach" + after_3_minutes: "po 3 minútach" + after_4_minutes: "po 4 minútach" + after_5_minutes: "po 5 minútach" + after_10_minutes: "po 10 minútach" + invited: + search: "začni písať pre hľadanie pozvánok" + title: "Pozvánky" + user: "Pozvaný užívateľ" + sent: "Odoslané" + none: "Nemáte žiadne čakajúce pozvánky." + truncated: + one: "Zobrazuje sa prvá pozvánka." + few: "Zobrazujú sa prvé {{count}} pozvánky." + other: "Zobrazuje sa prvých {{count}} pozvánok." + redeemed: "Použité pozvánky" + redeemed_tab: "Použitá" + redeemed_tab_with_count: "Použité ({{count}})" + redeemed_at: "Použitá" + pending: "Čakajúce pozvánky" + pending_tab: "Čakajúca" + pending_tab_with_count: "Čakajúce ({{count}})" + topics_entered: "Zobrazených tém" + posts_read_count: "Prečítaných príspevkov" + expired: "Táto pozvánka vypršala." + rescind: "Odstrániť" + rescinded: "Pozvánka odstránená" + reinvite: "Poslať pozvánku znovu" + reinvited: "Poslať pozvánku znovu" + time_read: "Doba čítania" + days_visited: "Dní na stránke" + account_age_days: "Vek účtu v dňoch" + create: "Poslať Pozvánku" + generate_link: "Kopírovať Odkaz Pozvánky" + generated_link_message: '

    Odkaz pre pozvánku bol úspešne vytvorený!

    Odkaz je platný iba pre túto adresu: %{invitedEmail}

    ' + bulk_invite: + none: "Zatiaľ ste tu nikoho nepozvali. Môžete odosielať pozvánky individuálne alebo pozvať skupinu ľudí naraz pomocou nahratia súboru." + text: "Hromadná pozvánka zo súboru" + uploading: "Prebieha nahrávanie..." + success: "Súbor bol úspešne odoslaný. Keď sa nahrávanie dokončí, budete na to upozornený cez správu." + error: "Pri nahrávaní '{{filename}}' sa vyskytla chyba: {{message}}" + password: + title: "Heslo" + too_short: "Vaše heslo je príliš krátke." + common: "Toto heslo je príliš časté." + same_as_username: "Vaše heslo je rovnaké ako používateľské meno." + same_as_email: "Vaše heslo je rovnaké ako e-mail." + ok: "Vaše heslo je v poriadku." + instructions: "Minimálne %{count} znakov." + associated_accounts: "Prihlásenia" + ip_address: + title: "Posledná IP adresa" + registration_ip_address: + title: "IP adresa pri registrácii" + avatar: + title: "Profilový obrázok" + header_title: "profil, správy, záložky a nastavenia" + title: + title: "Názov" + filters: + all: "Všetky" + stream: + posted_by: "Autor príspevku" + sent_by: "Odoslané používateľom" + private_message: "správa" + the_topic: "téma" + loading: "Prebieha načítavanie..." + errors: + prev_page: "pri pokuse o načítanie" + reasons: + network: "Chyba siete" + server: "Chyba na serveri" + forbidden: "Prístup zamietnutý" + unknown: "Chyba" + not_found: "Stránka nenájdená" + desc: + network: "Skontrolujte, prosím, svoje pripojenie." + network_fixed: "Zdá sa, že sme späť." + server: "Kód chyby: {{status}}" + forbidden: "Nemáte oprávnenie na zobrazenie." + not_found: "Hopla, aplikácia sa pokúsila načítať adresu, ktorá neexistuje." + unknown: "Niečo sa pokazilo." + buttons: + back: "Späť" + again: "Skúsiť znova" + fixed: "Načítať stránku" + close: "Zatvoriť" + assets_changed_confirm: "Táto stránka bola práve aktualizovaná. Obnoviť na najnovšiu verziu?" + logout: "Boli ste odhlásený." + refresh: "Obnoviť" + read_only_mode: + enabled: "Je zapnuté mód na čítanie. Môžete prezerať stránku, ale akákoľvek interakcia nemusí fungovať." + login_disabled: "Keď je zapnutý mód iba na čítanie, prihlásenie nie je možné." + too_few_topics_and_posts_notice: "Začnime diskusiu! Je tu %{currentTopics} / %{requiredTopics} tém a %{currentPosts} / %{requiredPosts} príspevkov. Noví návštevníci potrebujú mať témy, ktoré môžu čítať a na ktoré budú reagovať." + too_few_topics_notice: "Začnime diskusiu! Je tu %{currentTopics} / %{requiredTopics} tém. Noví návštevníci potrebujú mať témy, ktoré môžu čítať a na ktoré budú reagovať." + too_few_posts_notice: "Začnime diskusiu! Je tu %{currentPosts} / %{requiredPosts} príspevkov. Noví návštevníci potrebujú mať témy, ktoré môžu čítať a na ktoré budú reagovať." + learn_more: "zistiť viac..." + year: 'rok' + year_desc: 'témy vytvorené za posledných 365 dní' + month: 'mesiac' + month_desc: 'témy vytvorené za posledných 30 dní' + week: 'týždeň' + week_desc: 'témy vytvorené za posledných 7 dní' + day: 'deň' + first_post: Prvý príspevok + mute: Ignorovať + unmute: Prestať ignorovať + last_post: Posledný príspevok + last_reply_lowercase: posledná odpoveď + replies_lowercase: + one: odpoveď + few: odpovede + other: odpovedí + signup_cta: + sign_up: "Registrovať sa" + hide_session: "Pripomenúť zajtra" + hide_forever: "nie, ďakujem" + hidden_for_session: "Fajn, opýtam sa vás to zajtra. Stále môžete na vytvorenie účtu použiť aj možnosť 'Prihlásenie'." + intro: "Zdravím! :heart_eyes: Vyzerá, že sa vám diskusia páči, ale stále nie ste prihlásený na svojom účte." + value_prop: "Keď si vytvoríte účet, zapamätáme si čo ste čítali, takže sa môžete vrátiť presne tam, kde ste prestali. Okrem toho dostanete upozornenie tu, aj na váš e-mail, vždy keď pribudnú nové príspevky. A môžete označiť príspevky ktoré sa vám páčia. :heartbeat:" + summary: + enabled_description: "Pozeráte sa na zhrnutie tejto témy: najzaujímavejšie príspevky podľa výberu komunity." + description: "Je tu {{count}} odpovedí." + description_time: "Je tu {{count}} odpovedí s priemerným časom čítania {{readingTime}} minút." + enable: 'Zhrnutie tejto témy' + disable: 'Zobraziť všetky príspevky' + deleted_filter: + enabled_description: "Táto téma obsahuje zmazané príspevky, ktoré boli skryté." + disabled_description: "Zmazané príspevky sa v téme zobrazujú." + enable: "Skryť zmazané príspevky" + disable: "Zobraziť zmazané príspevky" + private_message_info: + title: "Správa" + invite: "Pozvať ostatných..." + remove_allowed_user: "Skutočne chcete odstrániť {{name}} z tejto správy?" + email: 'Email' + username: 'Používateľské meno' + last_seen: 'Videné' + created: 'Vytvorené' + created_lowercase: 'vytvorené' + trust_level: 'Stupeň dôvery' + search_hint: 'používateľské meno, email alebo IP adresa' + create_account: + title: "Vytvoriť nový účet" + failed: "Niečo sa pokazilo, možno je tento e-mail už registrovaný, vyskúšajte odkaz pre zabudnuté heslo" + forgot_password: + title: "Obnovenie hesla" + action: "Zabudol som svoje heslo" + invite: "Napíšte vaše používateľské meno alebo e-mailovú adresu a pošleme vám e-mail pre obnovu hesla." + reset: "Obnoviť heslo" + complete_username: "Ak je účet priradený k používateľskému menu %{username}, čoskoro dostanete e-mail s pokynmi, ako si obnoviť svoje heslo." + complete_email: "Ak je účet priradený k %{email}, čoskoro dostanete e-mail s pokynmi, ako si obnoviť svoje heslo." + complete_username_found: "Našli sme účet priradený k používateľskému menu %{username}, čoskoro dostanete e-mail s pokynmi, ako si obnoviť svoje heslo." + complete_email_found: "Našli sme účet priradený k %{email}, čoskoro dostanete e-mail s pokynmi, ako si obnoviť svoje heslo." + complete_username_not_found: "Žiadny účet nemá priradené používateľské meno %{username}" + complete_email_not_found: "Žiadny účet nie je s e-mailom %{email}" + login: + title: "Prihlásenie" + username: "Používateľ" + password: "Heslo" + email_placeholder: "e-mail alebo používateľské meno" + caps_lock_warning: "Caps Lock je zapnutý" + error: "Neznáma chyba" + rate_limit: "Pred opätovným prihlásením chvíľku počkajte." + blank_username_or_password: "Zadajte prosím svoj e-mail alebo používateľské meno a heslo." + reset_password: 'Obnoviť heslo' + logging_in: "Prebieha prihlásenie..." + or: "Alebo" + authenticating: "Prebieha overovanie..." + awaiting_confirmation: "Váš účet čaká na aktiváciu. Ak chcete zaslať aktivačný e-mail znova, použite odkaz pre zabudnuté heslo." + awaiting_approval: "Váš účet zatiaľ nebol schválený členom tímu. Keď bude schválený, dostanete e-mail." + requires_invite: "Prepáčte, ale prístup k tomuto fóru majú iba pozvaní používatelia." + not_activated: "Systém vás nemôže prihlásiť. Na emailovú adresu {{sentTo}} sme vám poslali aktivačný email. Prosím, postupujte podľa inštrukcií na aktiváciu účtu, ktoré sú uvedené v tomto emaile." + not_allowed_from_ip_address: "Nie je možné prihlásenie z tejto IP adresy." + admin_not_allowed_from_ip_address: "Nie je možné prihlásenie ako admin z tejto IP adresy." + resend_activation_email: "Kliknite sem pre opätovné odoslanie aktivačného emailu." + sent_activation_email_again: "Odoslali sme vám ďalší aktivačný email na {{currentEmail}}. Môže trvať niekoľko minút kým príde; pre istotu si skontrolujte spamový priečinok." + to_continue: "Prosím, prihláste sa" + preferences: "Na zmenu používateľských nastavení musíte byť prihlásený." + forgot: "Nepamätám si detaily môjho účtu" + google: + title: "pomocou Google" + message: "Prihlásenie pomocou Google účtu (prosím uistite sa, že vyskakovacie okná sú povolené)" + google_oauth2: + title: "pomocou Google" + message: "Prihlásenie pomocou Google účtu (prosím uistite sa, že vyskakovacie okná sú povolené)" + twitter: + title: "pomocou Twitter účtu" + message: "Prihlásenie pomocou Twitter účtu (prosím uistite sa, že vyskakovacie okná sú povolené)" + facebook: + title: "pomocou stránky Facebook" + message: "Prihlásenie pomocou Facebook účtu (prosím uistite sa, že vyskakovacie okná sú povolené)" + yahoo: + title: "pomocou Yahoo" + message: "Prihlásenie pomocou Yahoo účtu (prosím uistite sa, že vyskakovacie okná sú povolené)" + github: + title: "pomocou GitHub" + message: "Prihlásenie pomocou GitHub účtu (prosím uistite sa, že vyskakovacie okná sú povolené)" + apple_international: "Apple/Medzinárodné" + google: "Google" + twitter: "Twitter" + emoji_one: "Emoji One" + shortcut_modifier_key: + shift: 'Shift' + ctrl: 'Ctrl' + alt: 'Alt' + composer: + emoji: "Emoji :smile:" + more_emoji: "viac ..." + options: "Možnosti" + whisper: "šepot" + add_warning: "Toto je oficiálne varovanie." + toggle_whisper: "Prepnúť šepot" + posting_not_on_topic: "Na akú tému chcete odpovedať?" + saving_draft_tip: "ukladám..." + saved_draft_tip: "uložené" + saved_local_draft_tip: "uložené lokálne" + similar_topics: "Vaša téma je podobná..." + drafts_offline: "offline koncepty" + group_mentioned: "Použitím tejto {{group}} , upozorníte {{count}} ľudí." + error: + title_missing: "Názov je povinný" + title_too_short: "Názov musí mať minimálne {{min}} znakov" + title_too_long: "Názov nesmie byť dlhší než {{max}} znakov" + post_missing: "Príspevok nesmie byť prázdny" + post_length: "Príspevok musí mať minimálne {{min}} znakov" + try_like: 'Skúsili ste tlačítko?' + category_missing: "Musíte vybrať kategóriu" + save_edit: "Uložiť úpravy" + reply_original: "Odpovedať na pôvodnú tému" + reply_here: "Odpovedať tu" + reply: "Odpovedať" + cancel: "Zrušiť" + create_topic: "Vytvoriť tému" + create_pm: "Správa" + title: "Alebo stlačte Ctrl+Enter" + users_placeholder: "Pridať používateľa" + title_placeholder: "O čom je této diskusia v jednej stručnej vete?" + edit_reason_placeholder: "prečo upravujete?" + show_edit_reason: "(pridajte dôvod úpravy)" + reply_placeholder: "Píšte sem. Formátujte pomocou Markdown, BBCode alebo HTML. Pretiahnite alebo vložte obrázky." + view_new_post: "Zobraziť nový príspevok." + saving: "Ukladanie" + saved: "Uložené!" + saved_draft: "Návrh príspevku rozpracovaný. Vyberte na obnovenie." + uploading: "Upload prebieha..." + show_preview: 'zobraziť náhľad »' + hide_preview: '» skryť náhľad' + quote_post_title: "Citovať celý príspevok" + bold_title: "Výrazne" + bold_text: "výrazný text" + italic_title: "Zdôraznene" + italic_text: "zdôraznený text" + link_title: "Hyperlink" + link_description: "tu zadaj popis odkazu" + link_dialog_title: "Vložte hyperlink" + link_optional_text: "nepovinný názov" + link_placeholder: "http://example.com \"voliteľný text\"" + quote_title: "Úvodzovky" + quote_text: "Úvodzovky" + code_title: "Preformátovaný text" + code_text: "Odsaďte preformátovaný text 4 medzerami" + upload_title: "Upload" + upload_description: "tu zadajte popis uploadu" + olist_title: "Číslované odrážky" + ulist_title: "Odrážky" + list_item: "Položka zoznamu" + heading_title: "Nadpis" + heading_text: "Nadpis" + hr_title: "Horizonálny oddeľovač" + help: "Nápoveda úprav pre Markdown" + toggler: "skryť alebo zobraziť panel úprav" + modal_ok: "OK" + modal_cancel: "Zrušiť" + cant_send_pm: "Ľutujeme, nemôžete poslať správu pre %{username}." + admin_options_title: "Nepovinné zamestnanecké nastavenia pre túto tému" + auto_close: + label: "Čas na automatické uzavretie témy:" + error: "Prosím zadajte platnú hodnotu." + based_on_last_post: "Nezatvárať pokým posledný príspevok v téme nie je takto starý." + all: + examples: 'Zadajte číslo v hodinách (24), absolútny čas (17:30) alebo časovú značku (2013-11-22 14:00).' + limited: + units: "(# hodín)" + examples: 'Zadajte počet hodín (24).' + notifications: + title: "oznámenia o zmienkach pomocou @name, odpovede na vaše príspevky a témy, správy atď." + none: "Notifikácie sa nepodarilo načítať" + more: "zobraziť staršie upozornenia" + total_flagged: "označených príspevkov celkom" + mentioned: "

    {{username}} {{description}}

    " + group_mentioned: "

    {{username}} {{description}}

    " + quoted: "

    {{username}} {{description}}

    " + replied: "

    {{username}} {{description}}

    " + posted: "

    {{username}} {{description}}

    " + edited: "

    {{username}} {{description}}

    " + liked: "

    {{username}} {{description}}

    " + private_message: "

    {{username}} {{description}}

    " + invited_to_private_message: "

    {{username}} {{description}}

    " + invited_to_topic: "

    {{username}} {{description}}

    " + invitee_accepted: "

    {{username}} prijal Vaše pozvanie

    " + moved_post: "

    {{username}} presunul {{description}}

    " + linked: "

    {{username}} {{description}}

    " + granted_badge: "

    Získal '{{description}}'

    " + alt: + mentioned: "Spomenutý od" + quoted: "Citovaný od" + replied: "Odpovedané" + posted: "Príspevok od" + edited: "Upravte Váš príspevok do" + liked: "Váš príspevok sa páčil" + private_message: "Súkromná správa od" + invited_to_private_message: "Pozvaný na súkromné správy od" + invited_to_topic: "Pozvaný k téme od" + invitee_accepted: "Pozvánka akceptovaná " + moved_post: "Váš príspevok bol presunutý " + linked: "Odkaz na váš príspevok" + granted_badge: "Priznaný odznak" + popup: + mentioned: '{{username}} vás spomenul v "{{topic}}" - {{site_title}}' + quoted: '{{username}} vás citoval v "{{topic}}" - {{site_title}}' + replied: '{{username}} vám odpovedal v "{{topic}}" - {{site_title}}' + posted: '{{username}} prispel v "{{topic}}" - {{site_title}}' + private_message: '{{username}} vám poslal súkromnú správu v "{{topic}}" - {{site_title}}' + linked: '{{username}} odkázal na váš príspevok z "{{topic}}" - {{site_title}}' + upload_selector: + title: "Pridať obrázok" + title_with_attachments: "Pridať obrázok alebo súbor" + from_my_computer: "Z počítača" + from_the_web: "Z webu" + remote_tip: "odkaz na obrázok" + remote_tip_with_attachments: "odkaz na obrázok alebo súbor {{authorized_extensions}}" + local_tip: "zvoľte obrázok z vášho počítača" + local_tip_with_attachments: "zvoľte obrázok alebo súbor z vášho počítača {{authorized_extensions}}" + hint: "(môžete taktiež potiahnuť a pustiť do editora pre nahratie)" + hint_for_supported_browsers: "môžete taktiež potiahnuť a pustiť alebo priložiť obrázky do editora" + uploading: "Nahrávanie" + select_file: "Zvoľte súbor" + image_link: "adresa na ktorú bude odkazovať váš obrázok" + search: + sort_by: "Zoradiť podľa" + relevance: "Relevancia" + latest_post: "Najnovší príspevok" + most_viewed: "Najviac prezerané" + most_liked: "Najviac sa páčia" + select_all: "Označ všetky" + clear_all: "Odznač všetky" + result_count: + one: "1 výsledok pre \"{{term}}\"" + few: "{{count}} výsledky pre \"{{term}}\"" + other: "{{count}} výsledkov pre \"{{term}}\"" + title: "hľadaj témy, príspevky, užívateľov, alebo kategórie" + no_results: "Žiadne výsledky" + no_more_results: "Nenašlo sa viac výsledkov" + search_help: Pomoc pri vyhľadávaní + searching: "Vyhľadávam....." + post_format: "#{{post_number}} podľa {{username}}" + context: + user: "Vyhľadávanie podľa @{{username}}" + category: "Vyhľadávanie podľa \"{{category}}\" kategórie" + topic: "Hľadaj v tejto téme" + private_messages: "Hľadaj správy" + hamburger_menu: "prejsť na iné témy, alebo kategórie" + new_item: "nový" + go_back: 'späť' + not_logged_in_user: 'užívateľská stránka so súhrnom aktivít a nastavení' + current_user: 'prejsť na Vašu uťívateľskú stránku' + topics: + bulk: + unlist_topics: "Dôverné témy" + reset_read: "Obnoviť prečítané" + delete: "Zmazať témy" + dismiss: "Zahodiť" + dismiss_read: "Zahodiť všetký neprečítané" + dismiss_button: "Zahadzujem....." + dismiss_tooltip: "Zahoď nové príspevky, alebo prestaň sledovať témy" + also_dismiss_topics: "Prestať sledovať tieto témy. Už sa nikdy nebudu ukazovať medzi neprečítanými" + dismiss_new: "Zahodiť. Nová" + toggle: "prepnuť hromadne vybrané témy" + actions: "Hromadné akcie" + change_category: "Zmeň kategóriu" + close_topics: "Uzavrieť tému" + archive_topics: "Archivuj témy" + notification_level: "Zmeň úroveň upozorňovania" + choose_new_category: "Vyberte pre tému novú kategóriu:" + selected: + one: "Označíli ste 1 tému." + few: "Označíli ste {{count}} tém.y" + other: "Označíli ste {{count}} tém." + none: + unread: "Nemáte neprečítanú tému" + new: "Nemáte žiadnu novú tému" + read: "Neprečítali ste ešte žiadnu tému." + posted: "Nanapísali ste ešte žiadnu tému." + latest: "Nie sú žiadne nové témy. To je smutné." + hot: "Nie sú žiadne horúce témy." + bookmarks: "Nemáte žiadne témy v záložke" + category: "V kategórii {{category}} nie je žiadna téma" + top: "Nie sú žiadne populárne témy." + search: "Nenašli sa žiadne výsledky" + educate: + new: '

    Tu sa zobrazí Vaša nová téma.

    V predvolenom nastavení sú témy považované za nové a zobrazia sa s príznakom nová pokiaľ boli vytvorené za posledné 2 dni.

    Môžte to zmeniť vo Vašich nastaveniach.

    ' + unread: '

    Tu sa zobrazia Vaše neprečítané témy.

    V predvolenom nastavení sú témy považované za nové a zobrazí sa počet neprečítaných1 Ak ste:

    • Vytvorili tému
    • Odpovedali na tému
    • Čítali tému viac ako 4 minúty

    Alebo ste nastavili na tému Sledovať alebo Pozorovať prostredníctvom ovládania upozornení na konci každej témy.

    Môžte to zmeniť vo Vašich nastaveniach.

    ' + bottom: + latest: "Nie je už viac najnovšich tém." + hot: "Nie je už viac horúcich tém" + posted: "Žiadne ďalšie témy na čítanie." + read: "Žiadne ďalšie prečítané témy." + new: "Žiadne nové témy." + unread: "Žiadne ďalšie neprečítané témy." + category: "Žiadne ďalšie témy v {{category}}." + top: "Nie je už viac poulárnych tém" + bookmarks: "Žiadne ďalšie témy v záložkách." + search: "Nenašlo sa viac výsledkov." + topic: + unsubscribe: + stop_notifications: "Teraz budete dostávať menej upozornení na {{title}}" + change_notification_state: "Váš súčasný stav upozornení je" + filter_to: "{{post_count}} príspevkov k téme" + create: 'Nová téma' + create_long: 'Vytvoriť novú tému' + private_message: 'Vytvoríť správu' + list: 'Témy' + new: 'nová téma' + unread: 'neprečítané' + new_topics: + one: '1 nová téma' + few: '{{count}} nové témy' + other: '{{count}} nových tém' + unread_topics: + one: '1 neprečítaná téma' + few: '{{count}} neprečítané témy' + other: '{{count}} neprečítaných tém' + title: 'Témy' + invalid_access: + title: "Téma je súkromná" + description: "Prepáčte, nemáte prístup k tejto téme!" + login_required: "Musíte sa prihlásiť, aby ste videli túto tému." + server_error: + title: "Tému sa nepodarilo načítať" + description: "Prepáčte, nepodarllo sa nám načítať túto tému, možno je problém s Vaším pripojením. Prosim skúste znova. Ak problém pretrváva, dajte nám vedieť" + not_found: + title: "Téma sa nenašla" + description: "Prepáčte, hľadaná téma nebola nájdená. Nebola odstránená moderátorom?" + total_unread_posts: + one: "máte 1 neprečítaný príspevok k tejto téme" + few: "máte {{count}} neprečítanépríspevky k tejto téme" + other: "máte {{count}} neprečítaných príspevkov k tejto téme" + unread_posts: + one: "máte 1 starší neprečítaný príspevok k tejto téme" + few: "máte {{count}} staršie neprečítané príspevky k tejto téme" + other: "máte {{count}} starších neprečítaných príspevkov k tejto téme" + new_posts: + one: "pribudol 1 nový príspevok odkedy ste čítali túto tému naposledy " + few: "pribudlo {{count}} nové príspevky odkedy ste čítali túto tému naposledy " + other: "pribudlo {{count}} nových príspevkov odkedy ste čítali túto tému naposledy " + likes: + one: "v tejto téme je jedo \"Páči sa\"" + few: "v tejto téme je {{count}} \"Páči sa\"" + other: "v tejto téme je {{count}} \"Páči sa\"" + back_to_list: "Naspäť na zoznam tém" + options: "Možnosti tém" + show_links: "zobrazovať odkazy v tejto téme" + toggle_information: "zmeniť detaily témy" + read_more_in_category: "Chcete si prečítať viac? Prezrite si témy v {{catLink}} alebo v {{latestLink}}." + read_more: "Chcete si prečítať viac? {{catLink}} alebo v {{latestLink}}." + read_more_MF: "Máte { UNREAD, plural, =0 {} one { 1 neprečítanú } other { # neprečítané } } { NEW, plural, =0 {} one { {BOTH, select, true{a} false { } other{}} 1 novú tému} other { {BOTH, select, true{a } false {} other{}} # nových tém} } na prečítanie, prípadne {CATEGORY, select, true {si pozrite iné témy v {catLink}} false {{latestLink}} other {}}" + browse_all_categories: Prezrieť všetky kategórie + view_latest_topics: zobraziť najnošie témy + suggest_create_topic: Čo tak vytvoriť novú tému? + jump_reply_up: prejsť na predchádzajúcu odpoveď + jump_reply_down: prejsť na nasledujúcu odpoveď + deleted: "Téma bola vymazaná" + auto_close_notice: "Táto téma bude automaticky uzavretá o %{timeLeft}." + auto_close_notice_based_on_last_post: "Táto téma bude uzavretá %{duration} po poslednej odpovedi." + auto_close_title: 'Nastavenia automatického zatvárania' + auto_close_save: "Uložiť" + auto_close_remove: "Neuzatvárať túto tému automaticky" + progress: + title: pozícia v téme + go_top: "na začiatok" + go_bottom: "na spodok" + go: "Choď" + jump_bottom: "choď na posledný príspevok" + jump_bottom_with_number: "choď na príspevok číslo %{post_number}" + total: Všetkých príspevkov + current: tento príspevok + position: "%{current} príspevok z %{total}" + notifications: + reasons: + '3_6': 'Budete dostávať upozornenia, pretože sa pozeráte na túto kategóriu.' + '3_5': 'Budete automaticky dostávať upozornenie pretože ste začali pozorovať túto tému.' + '3_2': 'Budete dostávať upozornenia, pretože sa pozeráte na túto tému.' + '3_1': 'Budete dostávať upozornenia, pretože ste vytvorili túto tému.' + '3': 'Budete dostávať upozornenia, pretože sa pozeráte na túto tému.' + '2_8': 'Budete dostávať upozornenia, pretože sledujete túto kategóriu.' + '2_4': 'Budete dostávať upozornenia, pretože ste zaslali odpoveď na túto tému.' + '2_2': 'Budete dostávať upozornenia, pretože sledujete túto tému.' + '2': 'Budete dostávať upozornenia, pretože ste čítali túto tému.' + '1_2': 'Budete upozornený ak niekto spomenie Vaše @meno alebo Vám odpovie.' + '1': 'Budete upozornený ak niekto spomenie Vaše @meno alebo Vám odpovie.' + '0_7': 'Ignorujete všetky upozornenia v tejto kategórii.' + '0_2': 'Ignorujete všetky upozornenia k tejto téme.' + '0': 'Ignorujete všetky upozornenia k tejto téme.' + watching_pm: + title: "Pozerať" + description: "Budete upozornený na každú novu dopoveť na túto správu, a zobrazí sa počet nových odpovedí" + watching: + title: "Pozerať" + description: "Budete upozornený na každú novu dopoveť na túto tému, a zobrazí sa počet nových odpovedí" + tracking_pm: + title: "Sledovať" + description: "Zobrazí počet nových odpovedí na túto správu. Budete upozornený ak niekto spomenie Vaše @meno alebo Vám odpovie." + tracking: + title: "Sledovať" + description: "Zobrazí počet nových odpovedí na túto tému. Budete upozornený ak niekto spomenie Vaše @meno alebo Vám odpovie." + regular: + title: "Bežný" + description: "Budete upozornený ak niekto spomenie Vaše @meno alebo Vám odpovie." + regular_pm: + title: "Bežný" + description: "Budete upozornený ak niekto spomenie Vaše @meno alebo Vám odpovie." + muted_pm: + title: "Stíšené" + description: "Nikdy nebudete upozornení na nič ohľadom tejto správy" + muted: + title: "Stíšené" + description: "Nikdy nebudete upozornení na nič ohľadom tejto témy, a nebude sa zobrazovať medzi najnovšími." + actions: + recover: "Obnoviť zmazanú tému" + delete: "Zmazať tému" + open: "Otvoriť tému" + close: "Uzavrieť tému" + multi_select: "Označ príspevky...." + auto_close: "Automaticky zatvor...." + pin: "Pripni tému...." + unpin: "Odopni tému...." + unarchive: "Zruš archiváciu témy" + archive: "Archívuj tému" + invisible: "Skyť" + visible: "Zobraziť" + reset_read: "Zrušiť načítané údaje" + feature: + pin: "Pripni tému" + unpin: "Odopni tému" + pin_globally: "Pripni tému globálne" + make_banner: "Banerová téma" + remove_banner: "Odstrániť banerovú tému" + reply: + title: 'Odpovedať' + help: 'vytvor odpoveď k tejto téme' + clear_pin: + title: "Zruš pripnutie" + help: "Zruší pripnutie tejto témy takže sa už viac nebude objavovať na vrchu Vášho zoznamu tém" + share: + title: 'Zdielaj' + help: 'zdieľaj odkaz na túto tému' + flag_topic: + title: 'Označ' + help: 'súkromne označiť túto tému do pozornosti, alebo na ňu poslať súkromne upozornenie' + success_message: 'Úspešne ste označili tému.' + feature_topic: + title: "Vyzdvihni túto tému" + pin: "Zobrazuj túto tému na vrchu {{categoryLink}} kategórie do" + confirm_pin: "Máte už {{count}} pripnutých tém. Príliš veľa pripnutých tém môže byť na príťaž pre nových a anonymných užívateľov. Ste si istý že chcete pripnúť ďalšiu tému v tejto kategórii?" + unpin: "Zruš túto tému z vrcholu kategórie {{categoryLink}} . " + unpin_until: "Zruš túto tému z vrcholu kategórie {{categoryLink}} , alebo počkaj do %{until}." + pin_note: "Užívatelia si môžu sami odopnúť tému " + pin_validation: "Dátum je vyžadovaný k pripnutiu tejto témy." + not_pinned: "V {{categoryLink}} nie sú pripnuté žiadne témy." + already_pinned: + one: "Téma pripnutá k {{categoryLink}}: 1" + few: "Témy pripnuté ku {{categoryLink}}: {{count}}" + other: "Tém pripnutých k {{categoryLink}}: {{count}}" + pin_globally: "Zobrazuj túto tému na vrchu všetkých zoznamov tém do" + confirm_pin_globally: "Máte už {{count}} globálne pripnutých tém. Príliš veľa pripnutých tém môže byť na príťaž pre nových a anonymných užívateľov. Ste si istý že chcete pripnúť ďalšiu globálnu tému?" + unpin_globally: "Zruš túto tému z vrcholu všetkých zoznamov tém. " + unpin_globally_until: "Zruš túto tému z vrcholu všetkých zoznamov tém, alebo počkaj do %{until}." + global_pin_note: "Užívatelia si môžu sami odopnúť tému." + not_pinned_globally: "Nie sú pripnuté žiadne globálne témy." + already_pinned_globally: + one: "Globálne pripnutá téma : 1" + few: "Globálne pripnuté témy : {{count}}" + other: "Globálne pripnutých tém : {{count}}" + make_banner: "Spraviť z tejto témy baner, ktorý sa zobrazí navrchu každej stránky." + remove_banner: "Odstrániť baner, ktorý sa zobrazuje navrchu každej stránky." + banner_note: "Užívatelia môžu banner kedykoľvek zrušiť. Bannerom môže byť v jednom momente len jedna téma." + no_banner_exists: "Neexistuje žiadna banerová téma." + banner_exists: "Banerová téma je aktuálne nastavená." + inviting: "Pozývam..." + automatically_add_to_groups_optional: "Táto pozvánka obsahuje taktiež prístup do týchto skupín: ( nepovinné, iba správca) " + automatically_add_to_groups_required: "Táto pozvánka obsahuje taktiež prístup do týchto skupín: ( Povinné, iba správca) " + invite_private: + title: 'Pozvať do konverzácie' + email_or_username: "Email, alebo užívateľské meno pozvaného" + email_or_username_placeholder: "emailova adresa alebo uťívateľské meno" + action: "Pozvi" + success: "Pozvali sme tohoto uťívateľa aby sa podieľal na tejto správe" + error: "Prepáčte, pri pozývaní tohto užívateľa nastala chyba." + group_name: "názov skupiny" + invite_reply: + title: 'Pozvi' + username_placeholder: "používateľské meno" + action: 'Pošli pozvánku' + help: 'pozvite ostatných k tejto téme prostredníctvom emailu, alebo upozornení' + to_forum: "Pošleme krátký email dovoľujúci Vášmu priateľovi okamžité pripojenie kliknutím na odkaz bez potreby prihlasovania" + sso_enabled: "Zadajte uťívateľské meno osoby, ktorú by ste radi pozvali k tejto téme" + to_topic_blank: "Zadajte uťívateľské meno alebo email osoby, ktorú by ste radi pozvali k tejto téme" + to_topic_email: "Zadali ste emailovú adresu. Pošleme pozvánku ktorá umožní Vášmu priateľovi okamžitú odpoveď k tejto téme." + to_topic_username: "Zadali ste užívateľské meno. Pošleme mu pozvánku s odkazom na túto tému." + to_username: "Zadajte užívateľské meno osoby, ktorú chcete pozvať. Pošleme mu pozvánku s odkazom na túto tému." + email_placeholder: 'name@example.com' + success_email: "Poslali sme email s pozvánkou na {{emailOrUsername}}. Upozorníme vas keď bude pozvánka použítá. Svoje pozvánky môžte sledovať v tabuľke pozvánok vo svojom užívateľskom profile." + success_username: "Pozvali sme tohoto uťívateľa aby sa podieľal na tejto téme." + error: "Prepáčte, Nepodarilo sa nám pozvať túto osobu. Nebola už náhodou pozvaná ? (Počet opakovaných pozvánok je obmedzený)" + login_reply: 'Príhláste sa ak chcete odpovedať' + filters: + n_posts: + one: "1 príspevok" + few: "{{count}} príspevky" + other: "{{count}} príspevkov" + cancel: "Zruš filter" + split_topic: + title: "Presuň na novú tému" + action: "presuň na novú tému" + topic_name: "Názov novej témy" + error: "Nastala chyba pri presune príspevku na novú tému." + instructions: + one: "Vytvárate novú tému do ktorej bude vložený príspevok, ktorý ste označili. " + few: "Vytvárate novú tému do ktorej bude vložených {{count}} príspevkov, ktoré ste označili. " + other: "Vytvárate novú tému do ktorej budú vložené {{count}} príspevky, ktoré ste označili. " + merge_topic: + title: "Presuň do existujúcej témy." + action: "presuň do existujúcej témy" + error: "Nastala chyba pri presune príspevku do tejto témy." + instructions: + one: "Prosím vyberte tému do ktorej chcete presunúť tento príspevok." + few: "Prosím vyberte tému do ktorej chcete presunúť tieto {{count}} príspevky." + other: "Prosím vyberte tému do ktorej chcete presunúť týchto {{count}} príspevkov." + change_owner: + title: "Zmeň vlástníka príspevkov" + action: "zmeň vlastníka" + error: "Nastala chyba pri zmene vlastníka príspevkov." + label: "Príspevky nového vlastníka" + placeholder: "užívateľske meno nového vlastnika" + instructions: + one: "Prosím vyberte nového vlastníka príspevku vytvoreného {{old_user}}." + few: "Prosím vyberte nového vlastníka {{count}} príspevkov vytvorených {{old_user}}." + other: "Prosím vyberte nového vlastníka {{count}} príspevkov vytvorených {{old_user}}." + instructions_warn: "Poznámka: Žiadne upozornenie o tomto príspevku nebude spätne zaslané novým užívateľom
    Upozornenie: Momentálne nie sú prenášané žiadne dáta vťahujúce sa k príspevku na nových užívateľov. Používajte opatrne." + change_timestamp: + title: "Nastavte časovú značku" + action: "nastavte časovú značku" + invalid_timestamp: "Časová značka nemôže byť v budúcnosti. " + error: "Nastala chyba pri zmene časovej značky témy." + instructions: "Prosím vyberte novú časovú značku témy. Príspevky k téme budu aktualizované so zachovaním časoveho rozdielu." + multi_select: + select: 'označ' + selected: 'označených ({{count}})' + select_replies: 'označ +odpovede' + delete: zmaž označené + cancel: zrušiť výber + select_all: označ všetko + deselect_all: odznač všetko + description: + one: Označili ste 1 príspevok + few: Označili ste {{count}} príspevky + other: Označili ste {{count}} príspevkov + post: + reply: " {{replyAvatar}} {{usernameLink}}" + reply_topic: " {{link}}" + quote_reply: "citovať odpoveď" + edit: "Upravujete {{link}} {{replyAvatar}} {{username}}" + edit_reason: "Dôvod:" + post_number: "príspevok {{number}}" + last_edited_on: "príspevok naposledy upravený" + reply_as_new_topic: "Odpoveď ako súvisiaca téma" + continue_discussion: "Pokračovanie diskusie z {{postLink}}:" + follow_quote: "prejsť na citovaný príspevok" + show_full: "Zobraziť celý príspevok" + show_hidden: 'Zobraziť skrytý obsah.' + deleted_by_author: + one: "(príspevky stiahnuté autorom budú automaticky zmazané za jednu hodinu pokiaľ nie sú označené)" + few: "(príspevky stiahnuté autorom budú automaticky zmazané za %{count} hodiny pokiaľ nie sú označené)" + other: "(príspevky stiahnuté autorom budú automaticky zmazané za %{count} hodín pokiaľ nie sú označené)" + expand_collapse: "rozbaliť/zbaliť" + gap: + one: "zobraziť skrytú odpoveď" + few: "zobraziť {{count}} skryté odpovede" + other: "zobraziť {{count}} skrytých odpovedí" + more_links: "ešte {{count}} " + unread: "Príspevok je neprečítaný." + has_replies: + one: "{{count}} Odpoveď" + few: "{{count}} Odpovede" + other: "{{count}} Odpovedí" + has_likes: + one: "{{count}} \"Páči sa\"" + few: "{{count}} \"Páči sa\"" + other: "{{count}} \"Páči sa\"" + has_likes_title: + one: "Tento príspevok sa páčil jedej osobe" + few: "Tento príspevok sa páčil {{count}} ľuďom" + other: "Tento príspevok sa páčil {{count}} ľuďom" + has_likes_title_only_you: "tento príspevok sa Vám páči" + has_likes_title_you: + one: "Tento príspevok sa páčil Vám a jednej ďalšej osobe" + few: "Tento príspevok sa páčil Vám a ďalším {{count}} ľuďom" + other: "Tento príspevok sa páčil Vám a ďalším {{count}} ľuďom" + errors: + create: "Ľutujeme, pri vytváraní príspevku nastala chyba. Prosím, skúste znovu." + edit: "Ľutujeme, pri úprave príspevku nastala chyba. Prosím, skúste znovu." + upload: "Ľutujeme, pri nahrávaní súboru nastala chyba. Prosím, skúste znovu." + attachment_too_large: "Ľutujeme, súbor, ktorý sa pokúšate nahrať, je príliš veľký (maximálna veľkosť je {{max_size_kb}}kb)." + file_too_large: "Ľutujeme, súbor, ktorý sa pokúšate nahrať je príliš veľký (maximálna veľkosť je {{max_size_kb}}kb)" + too_many_uploads: "Ľutujeme, ale naraz je možné nahrať len jeden súbor." + too_many_dragged_and_dropped_files: "Prepáčte, naraz môžte presunúť maximálne 10 súborov." + upload_not_authorized: "Ľutujeme, súbor, ktorý sa pokúšate nahrať nemá povolenú príponu (povolené prípony sú: {{authorized_extensions}})." + image_upload_not_allowed_for_new_user: "Ľutujeme, noví použivatelia nemôžu nahrávať obrázky." + attachment_upload_not_allowed_for_new_user: "Ľutujeme, noví používatelia nemôžu nahrávať prílohy." + attachment_download_requires_login: "Ľutujeme, pre stiahnutie príloh musíte byť prihlásený." + abandon: + confirm: "Ste si istý, že chcete zahodiť tento príspevok?" + no_value: "Nie, ponechať." + yes_value: "Áno, zahodiť." + via_email: "tento príspevok prišiel emailom" + whisper: "tento príspevok je súkromným šepotom pre moderátorov" + wiki: + about: "toto je wiki príspevok; základní používatelia ho môžu upravovať" + archetypes: + save: 'Uložiť možnosti' + controls: + reply: "vytvorte odpoveď na tento príspevok" + like: "páči sa mi tento príspevok" + has_liked: "tento príspevok sa Vám páči" + undo_like: "zruš \"Páči sa\"" + edit: "Editovať tento príspevok." + edit_anonymous: "Ľutujeme, ale pre úpravu príspevku je potrebné sa prihlásiť." + flag: "súkromne označiť tento príspevok do pozornosti, alebo naň poslať súkromne upozornenie" + delete: "odstrániť tento príspevok" + undelete: "vrátiť späť odstránenie príspevku" + share: "zdieľať odkaz na tento príspevok" + more: "Viac" + delete_replies: + confirm: + one: "Chcete tiež odstrániť {{count}} priamu reakciu na tento príspevok?" + few: "Chcete tiež odstrániť {{count}} priame reakcie na tento príspevok?" + other: "Chcete tiež odstrániť {{count}} priamych reakcií na tento príspevok?" + yes_value: "Áno, odstrániť aj reakcie." + no_value: "Nie, len tento príspevok." + admin: "akcie administrátora príspevku" + wiki: "Spraviť Wiki" + unwiki: "Odstrániť Wiki" + convert_to_moderator: "Pridať farbu personálu" + revert_to_regular: "Odobrať farbu personálu" + rebake: "Pregenerovať HTML" + unhide: "Odokryť" + change_owner: "Zmeniť vlastníctvo" + actions: + flag: 'Označ' + defer_flags: + one: "Zrušiť označenie" + few: "Zrušiť označenia" + other: "Zrušiť označenia" + it_too: + off_topic: "Tiež označ" + spam: "Tiež označ" + inappropriate: "Tiež označ" + custom_flag: "Tiež označ" + bookmark: "Tiež vytvoriť záložku" + like: "Tiež sa mi páči" + vote: "Tiež hlasujem za" + undo: + off_topic: "Zruš označenie" + spam: "Zruš označenie" + inappropriate: "Zruš označenie" + bookmark: "Vrátiť záložku späť" + like: "Zruš \"Páči sa\"" + vote: "Zruš hlasovanie" + people: + off_topic: "{{icons}} to označíl ako mimo tému" + spam: "{{icons}} to označíl ako spam" + spam_with_url: "{{icons}} to označíl ako spam" + inappropriate: "{{icons}} to označíl ako nevhodné" + notify_moderators: "{{icons}} upozornil moderátorov" + notify_moderators_with_url: "{{icons}} upozornil moderátorov" + notify_user: "{{icons}} poslal správu" + notify_user_with_url: "{{icons}} poslal správu " + bookmark: "{{icons}} si na to vytvoril záložku" + like: "Páčilo sa to {{icons}}" + vote: "{{icons}} hlasoval za" + by_you: + off_topic: "Označíli ste to ako mimo tému" + spam: "Označíli ste to ako spam" + inappropriate: "Označíli ste to ako nevhodné" + notify_moderators: "Označíli ste to pre moderátora" + notify_user: "Poslali ste správu užívateľovi " + bookmark: "Vytvorili ste si záložku na tento príspevok" + like: "Páči sa Vám to" + vote: "Hlasoval ste za tento príspevok" + by_you_and_others: + off_topic: + one: "Vy a 1 ďalšia osoba to označílo ako mimo tému" + few: "Vy a ďalšie {{count}} osoby to označíli ako mimo tému" + other: "Vy a ďalších {{count}} osôb to označílo ako mimo tému" + spam: + one: "Vy a 1 ďalšia osoba to označíla ako spam" + few: "Vy a ďalšie {{count}} osoby to označíli ako spam" + other: "Vy a ďalších {{count}} osôb to označílo ako spam" + inappropriate: + one: "Vy a jedna ďalšia osoba to označila ako nevhodné" + few: "Vy a ďalšie {{count}} osoby to označili ako nevhodné" + other: "Vy a ďalších {{count}} osôb to označilo ako nevhodné" + notify_moderators: + one: "Vy a jedna ďalšia osoba to označila na moderovanie" + few: "Vy a ďalšie {{count}} osoby to označili na moderovanie" + other: "Vy a ďalších {{count}} osôb to označilo na moderovanie" + notify_user: + one: "Vy a jedna ďalšia osoba poslala správu tomuto užívateľovi" + few: "Vy a ďalšie {{count}} osoby poslali správu tomuto užívateľovi" + other: "Vy a ďalších {{count}} osôb poslalo správu tomuto užívateľovi" + bookmark: + one: "Vy a jedna ďalšia osoba si vytvorilo záložku na tento príspevok" + few: "Vy a ďalšie {{count}} osoby si vytvorili záložku na tento príspevok" + other: "Vy a ďalších {{count}} osôb si vytvorilo záložku na tento príspevok" + like: + one: "Páči sa to Vám a jendej ďalšej osobe" + few: "Páči sa to Vám a ďalším {{count}} osobám" + other: "Páči sa to Vám a ďalším {{count}} osobám" + vote: + one: "Vy a jenda ďalšia osoba hlasovala za tento príspevok" + few: "Vy a ďalšie {{count}} osoby hlasovalo za tento príspevok" + other: "Vy a ďalších {{count}} osôb hlasovalo za tento príspevok" + by_others: + off_topic: + one: "1 osoba to označíla ako mimo tému" + few: "{{count}} osoby to označíli ako mimo tému" + other: "{{count}} osôb to označílo ako mimo tému" + spam: + one: "1 osoba to označíla ako spam" + few: "{{count}} osoby to označíli ako spam" + other: "{{count}} osôb to označílo ako spam" + inappropriate: + one: "1 osoba to označila ako nevhodné" + few: "{{count}} osoby to označili ako nevhodné" + other: "{{count}} osôb to označilo ako nevhodné" + notify_moderators: + one: "1 osoba to označila na moderovanie" + few: "{{count}} osoby to označili na moderovanie" + other: "{{count}} osôb to označilo na moderovanie" + notify_user: + one: "1 osoba poslala správu tomuto užívateľovi" + few: "{{count}} osoby poslali správu tomuto užívateľovi" + other: "{{count}} osôb poslalo správu tomuto užívateľovi" + bookmark: + one: "1 osoba si vytvorila záložku na tento príspevok" + few: "{{count}} osoby si vytvorili záložku na tento príspevok" + other: "{{count}} osôb si vytvorilo záložku na tento príspevok" + like: + one: " {{count}} osobe sa to páčilo" + few: " {{count}} osobám sa to páčilo" + other: " {{count}} osobám sa to páčilo" + vote: + one: "1 osoba hlasovala za tento príspevok" + few: "{{count}} osoby hlasovali za tento príspevok" + other: "{{count}} osôb hlasovalo za tento príspevok" + delete: + confirm: + one: "Ste si istý že chcete zmazať tento príspevok?" + few: "Ste si istý že chcete zmazať všetky tieto príspevky?" + other: "Ste si istý že chcete zmazať všetky tieto príspevky?" + revisions: + controls: + first: "Prvá revízia" + previous: "Predchádzajúca revízia" + next: "Ďalšia revízia" + last: "Posledná revízia" + hide: "Skriť revíziu" + show: "Ukáza revíziu" + comparing_previous_to_current_out_of_total: "{{previous}} {{current}} / {{total}}" + displays: + inline: + title: "Zobraz výstup vrátane pridaného a zmazaného v riadku" + button: ' HTML' + side_by_side: + title: "Zobraziť rozdiely v generovanom výstupe vedľa seba" + button: ' HTML' + side_by_side_markdown: + title: "Zobraziť rozdiely v pôvodnom zdroji vedľa seba" + button: ' Neupravený' + category: + can: 'môže … ' + none: '(Bez kategórie)' + all: 'Všetky kategórie' + choose: 'Vyber kategóriu…' + edit: 'uprav' + edit_long: "Upraviť" + view: 'Prezerať témy v kategórii' + general: 'Všeobecné' + settings: 'Nastavenia' + topic_template: "Formulár témy" + delete: 'Odstrániť kategóriu' + create: 'Nová kategória' + create_long: 'Vytvoriť novú kategóriu' + save: 'Uložiť kategóriu' + slug: 'URL kategórie' + slug_placeholder: '(Voliteľné) pomlčkou-prerušované-slová pre url' + creation_error: Nastala chyba počas vytvárania kategórie. + save_error: Nastala chyba počas ukladania kategórie + name: "Názov kategórie" + description: "Popis" + topic: "kategória témy" + logo: "Logo kategórie" + background_image: "Pozadie kategórie" + badge_colors: "Farby odznakov" + background_color: "Farba pozadia" + foreground_color: "Farba popredia" + name_placeholder: "Maximálne jedno dve slová" + color_placeholder: "Ľubovoľná farba stránky" + delete_confirm: "Ste si istý že chcete zmazať túto kategóriu?" + delete_error: "Nastala chyba počas mazania kategórie" + list: "Zoznam kategórií" + no_description: "Prosím, pridajte popis k tejto kategórii." + change_in_category_topic: "Uprav popis" + already_used: 'Táto farba je už použitá inou kategóriou' + security: "Bezpečnosť" + special_warning: "Upozornenie: Toto je preddefinovaná kategória a jej bezpečnostné nastavenia sa nedajú upraviť. Pokiaľ si neželáte použiť túto kategóriu, neupravujte ju, ale zmažte." + images: "Obrázky" + auto_close_label: "Automaticky uzavrieť tému po:" + auto_close_units: "hodinách" + email_in: "Vlastná e-mailová adresa pre príchodziu poštu:" + email_in_allow_strangers: "Prijímať emaily od anonymných užívateľov bez účtu" + email_in_disabled: "Vkladanie nových tém cez email je zablokované v Nastaveniach stránky. Ak chcete povoliť vkladanie nových téme cez email," + email_in_disabled_click: 'povoľte nastavenie "email in"' + contains_messages: "Zmeň túto kategóriu tak, aby obsahovala len správy." + suppress_from_homepage: "Pozastaviť kategóriu z domovskej stránky." + allow_badges_label: "Povoliť získavanie odznakov v tejto kategórii" + edit_permissions: "Upraviť práva" + add_permission: "Pridať práva" + this_year: "tento rok" + position: "pozícia" + default_position: "Predvolená pozícia" + position_disabled: "Kategórie budú zobrazené podľa aktivity. Pre možnosť ovládania poradia kategórií v zozname," + position_disabled_click: 'povoľte možnosť "pevné poradie kategórií"' + parent: "Nadradená kategória" + notifications: + watching: + title: "Pozerať" + description: "Budete automaticky pozerať všetky nové témy v týchto kategóriách. Budete upozornený na všetky nové príspevky vo všetkých témach. Zároveň bude zobrazený počet nových odpovedí." + tracking: + title: "Sledovať" + description: "Budete automaticky sledovať všetky nové témy v týchto kategóriách. Budete upozornený ak niekto uvedie vaše @meno alebo Vám odpovie. Zároveň bude zobrazený počet nových odpovedí." + regular: + title: "Bežný" + description: "Budete upozornený ak niekto spomenie Vaše @meno alebo Vám odpovie." + muted: + title: "Stíšené" + description: "Nikdy nebudete informovaní o udalostiach v nových témach týchto kategórií. Tieto témy sa zároveň nebudú zobrazovať v zozname posledných udalostí." + flagging: + title: 'Ďakujeme, že pomáhate udržiavať slušnosť v našej komunite!' + private_reminder: 'Označenia sú súkromné viditeľné iba pre personál' + action: 'Označ príspevok' + take_action: "Vykonať akciu" + notify_action: 'Správa' + delete_spammer: "Zmazať spammera" + delete_confirm: "Idete vymazať %{posts} príspevky a %{topics} témy tohto užívateľa, zmazať jeho účet, zakázať prihlásenia z jeho IP adresy %{ip_address}, a pridať jeho email %{email} na zoznam trvalo zakázaných. Ste si istý, že tento užívateľ je skutočne spamer?" + yes_delete_spammer: "Áno, zmazať spammera" + ip_address_missing: "(nedostupné)" + hidden_email_address: "(skryté)" + submit_tooltip: "Odoslať súkromné označenie" + take_action_tooltip: "Dosiahnuť okamžite limit označení, namiesto čakania na ďalšie označenia od komunity" + cant: "Ľutujeme, ale tento príspevok sa teraz nedá označiť ." + notify_staff: 'Notifikovať redakciu' + formatted_name: + off_topic: "Je to mimo témy" + inappropriate: "Je to nevhodné" + spam: "Je to spam" + custom_placeholder_notify_user: "Buďte konkrétny, buďte konštruktívny a buďte vždy milý." + custom_placeholder_notify_moderators: "Dajte nám vedieť, z čoho konkrétne máte obavy, a priložte príslušné odkazy a príklady, ak je to možné." + custom_message: + at_least: "zadajte aspoň {{n}} znakov" + more: "zostáva ešte {{n}} ..." + left: "{{n}} zostáva" + flagging_topic: + title: "Ďakujeme, že pomáhate udržiavať slušnosť v našej komunite!" + action: "Označ príspevok" + notify_action: "Správa" + topic_map: + title: "Zhrnutie článku" + participants_title: "Častí prispievatelia" + links_title: "Populárne odkazy" + links_shown: "ukázať všetkých {{totalLinks}} odkazov..." + clicks: + one: "%{count} kilk" + few: "%{count} kliky" + other: "%{count} klikov" + topic_statuses: + warning: + help: "Toto je oficiálne varovanie." + bookmarked: + help: "Vytvorili ste si záložku na túto tému" + locked: + help: "Táto téma je už uzavretá. Nové odpovede už nebudú akceptované" + archived: + help: "Táto téma je archivovaná. Už sa nedá meniť. " + locked_and_archived: + help: "Táto téma je už uzavretá a archivovaná. Nové odpovede ani zmeny už nebudú akceptované " + unpinned: + title: "Odopnuté" + help: "Túto tému ste odopli. Bude zobrazená v bežnom poradí." + pinned_globally: + title: "Globálne pripnuté" + help: "Tento príspevok je globálne uprednostnený. Zobrazí sa na začiatku v: zozname posledných článkov a vo svojej kategórii." + pinned: + title: "Pripnutý" + help: "Túto tému ste pripli. Bude zobrazená na vrchole svojej kategórie" + invisible: + help: "Táto téma je skrytá. Nebude zobrazená v zozname tém a prístup k nej bude možný len prostrednictvom priameho odkazu na ňu" + posts: "Príspevky" + posts_lowercase: "príspevky" + posts_long: "v tejto téme je {{number}} príspevkov" + posts_likes_MF: | + Táto téma obsahuje {count, plural, one {1 odpoveď} other {# odpovedí}} {ratio, select, + low {s vysokým pomerom "Páči sa" na príspevok} + med {s veľmi vysokým pomerom "Páči sa" na príspevok} + high {s extrémne vysokým pomerom "Páči sa" na príspevok} + other {}} + original_post: "Pôvodný príspevok" + views: "Zobrazenia" + views_lowercase: + one: "zobrazenie" + few: "zobrazenia" + other: "zobrazení" + replies: "Odpovede" + views_long: "táto téma bola prezeraná {{number}} krát " + activity: "Aktivita" + likes: "Páči sa mi" + likes_lowercase: + one: "\"Páči sa\"" + few: "\"Páči sa\"" + other: "\"Páči sa\"" + likes_long: "v tejto téme je {{number}} \"Páči sa\"" + users: "Používatelia" + users_lowercase: + one: "užívateľ" + few: "užívatelia" + other: "užívatelia" + category_title: "Kategória" + history: "História" + changed_by: "od {{author}}" + raw_email: + title: "Neupravený email" + not_available: "Nedostupné!" + categories_list: "Zoznam kategórií" + filters: + with_topics: "%{filter} témy" + with_category: "%{filter} %{category} témy" + latest: + title: "Najnovšie" + title_with_count: + one: "Posledný (1)" + few: "Posledné ({{count}})" + other: "Posledných ({{count}})" + help: "témy s nedávnymi príspevkami" + hot: + title: "Horúca" + help: "výber najhorúcejších tém" + read: + title: "Prečítaná" + help: "prečítané témy, zoradené podľa času ich prečítania" + search: + title: "Hľadať" + help: "hľadaj vo všetkych témach" + categories: + title: "Kategórie" + title_in: "Kategória - {{categoryName}}" + help: "všetky témy zoskupené podľa kategórie" + unread: + title: "Neprečítané" + title_with_count: + one: "Neprečítaná (1)" + few: "Neprečítané ({{count}})" + other: "Neprečítaných ({{count}})" + help: "témy ktorých neprečítané príspevky v súčastnosti pozeráte alebo sledujete " + lower_title_with_count: + one: "1 neprečítaná" + few: "{{count}} neprečítané" + other: "{{count}} neprečítaných" + new: + lower_title_with_count: + one: "1 nová" + few: "{{count}} nové" + other: "{{count}} nových" + lower_title: "nový" + title: "Nový" + title_with_count: + one: "Nová (1)" + few: "Nové ({{count}})" + other: "Nových ({{count}})" + help: "témy vytvorené za posledných pár dní" + posted: + title: "Moje príspevky" + help: "témy s vašimi príspevkami" + bookmarks: + title: "Záložky" + help: "témy, ktoré máte v záložkách" + category: + title: "{{categoryName}}" + title_with_count: + one: "{{categoryName}} ({{count}})" + few: "{{categoryName}} ({{count}})" + other: "{{categoryName}} ({{count}})" + help: "najnovšie témy v kategórii {{categoryName}}" + top: + title: "Vrch" + help: "najaktívnejšie témy za posledný rok, mesiac, týždeň, alebo deň" + all: + title: "Za celú dobu" + yearly: + title: "Ročne" + quarterly: + title: "Štvrťročne" + monthly: + title: "Mesačne" + weekly: + title: "Týždenne" + daily: + title: "Denne" + all_time: "Za celú dobu" + this_year: "Rok" + this_quarter: "Štvrťrok" + this_month: "Mesiac" + this_week: "Týždeň" + today: "Dnes" + other_periods: "pozri hore" + browser_update: 'Ľutujeme, Váš prehliadač je príliš starý na prácu na tejto stránke. Prosím aktualizujte Váš prehliedač.' + permission_types: + full: "Vytvor / Odpovedz / Zobraz" + create_post: "Odpovedz / Zobraz" + readonly: "Zobraz" + admin_js: + type_to_filter: "zadajte, čo chcete filtrovať ..." + admin: + title: 'Administrátor Discourse' + moderator: 'Moderátor' + dashboard: + title: "Ovládací panel" + last_updated: "Dashboard naposledy aktualizovaný:" + version: "Verzia" + up_to_date: "Máte nainštalovanú najnovšiu verziu!" + critical_available: "Je dostupná kritická aktualizácia." + updates_available: "Aktualizácie sú k dispozícii." + please_upgrade: "Prosím aktualizujte!" + no_check_performed: "Neprebehlo zisťovanie aktualizácií. Uistite sa že je spustený sidekiq." + stale_data: "V poslednej dobe neprebehlo zisťovanie aktualizácií. Uistite sa že je spustený sidekiq." + version_check_pending: "Zdá sa že ste nedávno aktualizovali. Fantastické!" + installed_version: "Nainštalované" + latest_version: "Najnovšie" + problems_found: "Boli zistené nejaké problémy s Vašou inštaláciou Discourse." + last_checked: "Naposledy overené" + refresh_problems: "Obnoviť" + no_problems: "Nenašli sa žiadne problémy." + moderators: 'Moderátori:' + admins: 'Administrátori:' + blocked: 'Zablokované:' + suspended: 'Odobraté:' + private_messages_short: "Správy" + private_messages_title: "Správy" + mobile_title: "Mobil" + space_free: "{{size}} voľné" + uploads: "nahraté" + backups: "zálohy" + traffic_short: "Vyťaženie" + traffic: "Požiadavky webových aplikácií" + page_views: "Požiadavky API" + page_views_short: "Požiadavky API" + show_traffic_report: "Zobraziť detaily vyťaženia" + reports: + today: "Dnes" + yesterday: "Včera" + last_7_days: "Posledných 7 dní" + last_30_days: "Posledných 30 dní" + all_time: "Za celú dobu" + 7_days_ago: "Pred 7 dňami" + 30_days_ago: "Pred 30 dňami" + all: "Všetky" + view_table: "tabuľka" + view_chart: "stĺpcový graf" + refresh_report: "Obnoviť report" + start_date: "Od" + end_date: "Do" + commits: + latest_changes: "Najnov3ie zmeny. Prosime aktualizujte čo najčastejšie!" + by: "podľa" + flags: + title: "Označenia" + old: "Staré" + active: "Aktívny" + agree: "Súhlasiť" + agree_title: "Akceptovať toto označenie ako platné a správne" + agree_flag_modal_title: "Súhlasiť a ...." + agree_flag_hide_post: "Súhlasiť (skryť príspevok a poslať súkromnú správu)" + agree_flag_hide_post_title: "Skryť tento príspevok a automaticky poslať súkromnú správu s výzvou na úpravu príspevku." + agree_flag_restore_post: "Súhlasiť (obnoviť príspevok)" + agree_flag_restore_post_title: "Obnoviť tento príspevok" + agree_flag: "Súhlasiť s označením" + agree_flag_title: "Súhlasiť s označením, ale nemeníť príspevok" + defer_flag: "Odložiť" + defer_flag_title: "Zrušíť označenie. Žiadna akcia nie je nateraz potrebná." + delete: "Odstrániť" + delete_title: "Zmazať príspevok na ktorý označenie odkazuje ." + delete_post_defer_flag: "Zmazať príspevok a zrušiť označenie" + delete_post_defer_flag_title: "Zmazať prípspevok; ak ide o prvý príspevok, zmazať aj tému" + delete_post_agree_flag: "Zmazať príspevok a súhlasiť s označením" + delete_post_agree_flag_title: "Zmazať prípspevok; ak ide o prvý príspevok, zmazať aj tému" + delete_flag_modal_title: "Zmazať a..." + delete_spammer: "Zmazať spammera" + delete_spammer_title: "Zmazať užívateľa aj všetky príspevky a témy ktoré vytvoril." + disagree_flag_unhide_post: "Nesúhlasiť (odkryť príspevok)" + disagree_flag_unhide_post_title: "Zrušíť všetky označenia z príspevku a znova odkryť príspevok" + disagree_flag: "Nesúhlasiť" + disagree_flag_title: "Zamietnuť toto označenie ako neplatné, alebo nesprávne" + clear_topic_flags: "Hotovo" + clear_topic_flags_title: "Téma bola preskúmaná a problémy boli vyriešené. Kliknite Hotovo pre zrušenie označení." + more: "(viac odpovedí...)" + dispositions: + agreed: "odsúhlasené" + disagreed: "neodsúhlasené" + deferred: "odložené" + flagged_by: "Označené " + resolved_by: "Vyriešené" + took_action: "Prijal opatrenia" + system: "Systém" + error: "Niečo sa pokazilo" + reply_message: "Odpovedať" + no_results: "Žiadne označenia." + topic_flagged: "Táto téma bola označená. " + visit_topic: "Navšťívte tému pre prijatie opatrení" + was_edited: "Príspevok bol upravený po prvom označení" + previous_flags_count: "Tento príspevok bol už označený {{count}} krát." + summary: + action_type_3: + one: "mimo tému" + few: "mimo tému x{{count}}" + other: "mimo tému x{{count}}" + action_type_4: + one: "nevhodný" + few: "nevhodné x{{count}}" + other: "nevhodných x{{count}}" + action_type_6: + one: "vlastná" + few: "vlastné x{{count}}" + other: "vlastných x{{count}}" + action_type_7: + one: "vlastný" + few: "vlastné x{{count}}" + other: "vlastných x{{count}}" + action_type_8: + one: "spam" + few: "spam x{{count}}" + other: "spam x{{count}}" + groups: + primary: "Hlavná skupina" + no_primary: "(bez hlavnej skupiny)" + title: "Skupiny" + edit: "Upraviť skupiny" + refresh: "Obnoviť" + new: "Nový" + selector_placeholder: "zadať používateľské meno" + name_placeholder: "Názov skupiny, bez medzier, rovnaké pravidlá ako pre uťívateľa" + about: "Tu upravíte Vaše členstvo v skupinách a mená" + group_members: "Členovia skupiny" + delete: "Odstrániť" + delete_confirm: "Zmazať túto skupinu?" + delete_failed: "Nepodarilo sa zmazať skupinu. Pokiaľ je skupina automatická, nemôže byť zrušená." + delete_member_confirm: "Odstrániť '%{username}' zo skupiny '%{group}'?" + delete_owner_confirm: "Odobrať vlastnícke práva %{username}'?" + name: "Meno" + add: "Pridať" + add_members: "Pridať členov" + custom: "Vlastné" + bulk_complete: "Užívatelia boli pridaní do skupiny." + bulk: "Hromadné pridanie do skupiny" + bulk_paste: "Vlož zoznam používateľov alebo emailov, jeden na riadok:" + bulk_select: "(vyberte skupinu)" + automatic: "Automaticky" + automatic_membership_email_domains: "Užívatelia, ktorí sa zaregistrovali s emailovou doménou uvedenou v zozname budú automaticky pridaní do tejto skupiny. " + automatic_membership_retroactive: "Použi pravidlo rovnakej emailovej domény pre pridanie registrovaných užívateľov" + default_title: "Štandardné označenie pre všetkých používateľov v tejto skupine" + primary_group: "Automaticky nastav ako hlavnú skupinu" + group_owners: Vlastníci + add_owners: Pridať vlastníkov + incoming_email: "Vlastná e-mailová adresa pre príchodziu poštu" + incoming_email_placeholder: "zadajte emailovú adresu" + api: + generate_master: "Vygenerovať Master API kľúč" + none: "V súčasnosti neexistujú žiadne aktívne API kľúče." + user: "Používateľ" + title: "API" + key: "API kľúč" + generate: "Generovať" + regenerate: "Obnov" + revoke: "Zrušiť" + confirm_regen: "Ste si istý, že chcete nahradiť tento API kľúč novým?" + confirm_revoke: "Ste si istý, že chcete obnoviť tento kľúč?" + info_html: "Váš API kľúč Vám umožní vytváranie a aktualizovanie tém prostredníctvom volaní JSON." + all_users: "Všetci používatelia" + note_html: "Držte tento kľúč v tajnosti, všetci užívatelia ktorí ho vlastnia môžu vytvárať ľubovoľné príspevky pod ľubovoľným užívateľským menom. " + plugins: + title: "Pluginy" + installed: "Nainštalované pluginy" + name: "Meno" + none_installed: "Nemáte nainštalované žiadne pluginy." + version: "Verzia" + enabled: "Povolené?" + is_enabled: "A" + not_enabled: "N" + change_settings: "Zmeniť nastavenia" + change_settings_short: "Nastavenia" + howto: "Ako nainštalujem pluginy?" + backups: + title: "Zálohy" + menu: + backups: "Zálohy" + logs: "Logy" + none: "Nie je dostupná žiadna záloha." + read_only: + enable: + title: "Povoliť mód len na čítanie." + label: "Povoliť mód len na čítanie." + confirm: "Ste si istý, že chcete povoliť mód len na čítanie?" + disable: + title: "Zakázať mód len na čítanie" + label: "Zakázať mód len na čítanie" + logs: + none: "Zatiaľ žiadne logy..." + columns: + filename: "Názov súboru" + size: "Veľkosť" + upload: + label: "Upload" + title: "Nahrať zálohu do tejto inštancie" + uploading: "Upload prebieha..." + success: "'{{filename}}' bol úspešne nahratý." + error: "Počas nahrávania '{{filename}}' nastala chyba: {{message}}" + operations: + is_running: "Operácia práve prebieha..." + failed: " Zlyhalo vykonanie {{operation}} . Prosím skontrolujte logy. " + cancel: + label: "Zrušiť" + title: "Zrušiť prebiehajúcu operáciu" + confirm: "Ste si istý, že chcete zrušiť prebiehajúcu operáciu?" + backup: + label: "Záloha" + title: "Vytvoriť zálohu" + confirm: "Prajete si spustiť novú zálohu?" + without_uploads: "Áno (nezahŕňať súbory)" + download: + label: "Stiahnuť" + title: "Stiahnuť zálohu" + destroy: + title: "Odstrániť zálohu" + confirm: "Ste si istý, že chcete odstrániť túto zálohu?" + restore: + is_disabled: "Obnovenie je vypnuté na Nastaveniach stránky." + label: "Obnoviť" + title: "Obnoviť zálohu" + confirm: "Ste si istý, že chcete obnoviť túto zálohu?" + rollback: + label: "Vrátiť späť" + title: "Vrátiť databázu do predchádzajúceho funkčného stavu" + confirm: "Ste si istý, že chcete vrátiť databázu do predchádzajúceho funkčńeho stavu?" + export_csv: + user_archive_confirm: "Ste si istý, že si chcete stiahnut svoje príspevky?" + success: "Export bol spustený, o jeho skončení budete informovaný správou." + failed: "Export zlyhal. Skontrolujte prosím logy." + rate_limit_error: "Príspevky možu byť stiahnuté len raz za deň. Skúste opäť zajtra." + button_text: "Export" + button_title: + user: "Exportovať celý zoznam používateľov v CSV formáte." + staff_action: "Exportovať celý log akcií redakcie v CSV formáte." + screened_email: "Exportovať celý zobrazený zoznam emailov v CSV formáte." + screened_ip: "Exportovať celý zobrazený zoznam IP adries v CSV formáte." + screened_url: "Exportovať celý zobrazený zoznam URL adries v CSV formáte." + export_json: + button_text: "Export" + invite: + button_text: "Poslať pozvánky" + button_title: "Poslať pozvánky" + customize: + title: "Upraviť" + long_title: "Úpravy webu" + css: "CSS" + header: "Hlavička" + top: "Vrch" + footer: "Päta" + embedded_css: "Vnorené CSS" + head_tag: + text: "" + title: "HTML, ktoré bude vložené pred tag" + body_tag: + text: "" + title: "HTML, ktoré bude vložené pred tag" + override_default: "Nevkladať štandardné štýly" + enabled: "Povolené?" + preview: "náhľad" + undo_preview: "zmazať náhľad" + rescue_preview: "predvolený štýl" + explain_preview: "Nastaviť na stránke vlastné štýly" + explain_undo_preview: "Vrátiť sa k akruálnym vlastným štýlom" + explain_rescue_preview: "Nastavit na stránke štandardné štýly" + save: "Uložiť" + new: "Nový" + new_style: "Nový štýl" + import: "Import" + import_title: "Vyberte súbor alebo vložte text" + delete: "Odstrániť" + delete_confirm: "Zmazať túto úpravu?" + about: "Upraviť CSS štýly a HTML hlavičky na stránke. Začnite pridaním úpravy." + color: "Farba" + opacity: "Nepriesvitnosť" + copy: "Kopírovať" + email_templates: + title: "Emailové šablóny" + subject: "Predmet" + multiple_subjects: "Táto emailova šablóna obsahuje viac predmetov" + body: "Telo" + none_selected: "Vyberte šablénu emailu pre začatie úpravy." + revert: "Vrátiť zmeny" + revert_confirm: "Ste si istý, že chcete vrátiť vykonané zmeny späť?" + css_html: + title: "CSS/HTML" + long_title: "Úpravy CSS a HTML" + colors: + title: "Farby" + long_title: "Farebné schémy" + about: "Upravte farby použité na stránke bez použitia CSS. Začnite pridaním schémy." + new_name: "Nová farebná schéma" + copy_name_prefix: "Kópia" + delete_confirm: "Zmazať túto farebnú schému?" + undo: "späť" + undo_title: "Zrušiť zmeny farby a vrátiť sa k predchádzajucej uloženej verzii. " + revert: "vrátiť zmeny" + revert_title: "Nastaviť východziu farebnú schému Discourse. " + primary: + name: 'primárny' + description: 'Väčšina textov, ikony, a okraje.' + secondary: + name: 'sekundárny' + description: 'Hlavná farba pozadia a farba textu niektorých ovládacích prvkov.' + tertiary: + name: 'terciárny' + description: 'Odkazy, nejaké tlačidlá, upozornenia a zvýrazňovacie farby.' + quaternary: + name: "štvrťročne" + description: "Navigačné odkazy." + header_background: + name: "pozadie hlavičky" + description: "Farba pozadia hlavičky stránky." + header_primary: + name: "hlavné záhlavie" + description: "Texty a ikony v záhlaví stránky." + highlight: + name: 'zvýraznenie' + description: 'Farba pozadia zvýrazneného prvku na stránke, napríklad príspevku alebo témy.' + danger: + name: 'nebezpečenstvo' + description: 'Zvýrazňovacia farba pre akcie ako napríklad mazanie príspevkov a tém.' + success: + name: 'úspech' + description: 'Použitá pre úspešne vykonané akcie.' + love: + name: 'obľúbené' + description: "Farba tlačidla \"Páči sa\"" + wiki: + name: 'wiki' + description: "Základná farba pozadia wiki príspevkov." + email: + title: "Email" + settings: "Nastavenia" + all: "Všetky" + sending_test: "Odosielam testovací email..." + error: "CHYBA - %{server_error}" + test_error: "Pri posielaní testovacieho emailu nastala chyba. Prosím preverte Vaše emailové nastavenia, overte si, že váš hostiteľ neblokuje emailové spojenia a skúste znova." + sent: "Odoslané" + skipped: "Preskočené" + sent_at: "Odoslané" + time: "Čas" + user: "Používateľ" + email_type: "Typ emailu" + to_address: "Adresát" + test_email_address: "testovacia emailová adresa" + send_test: "Odoslať testovací email" + sent_test: "odoslané!" + delivery_method: "Spôsob doručenia" + preview_digest: "Súhrn" + preview_digest_desc: "Náhľad obsahu súhrnných emailov zaslaných neaktívnym užívateľom." + refresh: "Obnoviť" + format: "Formát" + html: "html" + text: "text" + last_seen_user: "Posledný videný užívateľ" + reply_key: "Tlačidlo odpovedať" + skipped_reason: "Preskočiť zdôvodnenie" + logs: + none: "Nenašli sa žiadne logy." + filters: + title: "Filter" + user_placeholder: "používateľské meno" + address_placeholder: "meno@príklad.com" + type_placeholder: "súhrn, registácia..." + reply_key_placeholder: "tlačidlo odpovedať" + skipped_reason_placeholder: "dôvod" + logs: + title: "Logy" + action: "Akcia" + created_at: "Vytvorené" + last_match_at: "Posledný zodpovedajúci" + match_count: "Zodpovedá" + ip_address: "IP" + topic_id: "ID témy" + post_id: "ID príspevku" + category_id: "ID kategórie" + delete: 'Odstrániť' + edit: 'Upraviť' + save: 'Uložiť' + screened_actions: + block: "blokovať" + do_nothing: "nerob nič" + staff_actions: + title: "Akcie personálu" + instructions: "Vyberte uťívateľské meno a akcie na filtrovanie zoznamu. Kliknite na profilovú fotku pre navigáciu na užívateľské stránky." + clear_filters: "Ukázať všetko" + staff_user: "Člen redakcie" + target_user: "Cieľový používateľ" + subject: "Predmet" + when: "Kedy" + context: "Kontext" + details: "Detaily" + previous_value: "Predchádzajúci" + new_value: "Nový" + diff: "Rozdiel" + show: "Zobraziť" + modal_title: "Detaily" + no_previous: "Neexistuje predchádzajúca hodnota" + deleted: "Žiadna nová hodnota. Záznam bol vymazaný." + actions: + delete_user: "odstrániť používateľa" + change_trust_level: "zmeniť stupeň dôvery" + change_username: "zmeniť používateľské meno" + change_site_setting: "zmeniť nastavenia webu" + change_site_customization: "zmeniť úpravy webu" + delete_site_customization: "zmazať úpravy webu" + suspend_user: "zruš práva užívateľovi" + unsuspend_user: "obnov práva užívateľovi" + grant_badge: "udeliť odznak" + revoke_badge: "odobrať odznak" + check_email: "skontrolovať email" + delete_topic: "odstrániť tému" + delete_post: "odstrániť príspevok" + impersonate: "privlastniť" + anonymize_user: "anonymizovať používateľa" + roll_up: "zbaliť IP bloky" + change_category_settings: "zmeniť nastavenia kategórie" + delete_category: "odstrániť kategóriu" + create_category: "vytvoriť kategóriu" + screened_emails: + title: "Kontrolované emaily" + description: "Keď niekto skúsi vytvoriť nový účet, nasledujúce emailove adresy budú preverené a registrácia bude zablokovaná, alebo bude vykonaná nejaka iná akcia. " + email: "Emailové adresy" + actions: + allow: "Povoliť" + screened_urls: + title: "Kontrolované URL adresy" + description: "URL adresy v tomto zozname boli použité v príspevkoch užívateľov, ktorí boli identifikovaní ako spameri." + url: "URL" + domain: "Doména" + screened_ips: + title: "Kontrolované IP adresy" + description: 'IP adresy pod dohľadom. Použi "Povoľ" pre povolenie IP adries.' + delete_confirm: "Ste si istý, že chcete zrušiť pravidlo pre %{ip_address}?" + roll_up_confirm: "Ste si istý, že chcete zosumarizovať bežne kontrolované IP do podsietí?" + rolled_up_some_subnets: "Zakázané IP adresy boli úspešne zosumarizované do podsietí: %{subnets}." + rolled_up_no_subnet: "Nebolo čo zbaliť." + actions: + block: "Blokovať" + do_nothing: "Povoliť" + allow_admin: "Povoliť admin" + form: + label: "Nový:" + ip_address: "IP adresy" + add: "Pridať" + filter: "Hľadať" + roll_up: + text: "Zbaliť" + title: "Vytvorí novú podsieť zakázaných záznamov pokiaľ existuje aspoň 'min_ban_entries_for_roll_up' záznamov" + logster: + title: "Chybové Logy" + impersonate: + title: "Privlastniť" + help: "Použite tento nástroj na privlastnenie si užívateľského účtu na účely debugovania. Po skončení sa budete musieť odhlásiť." + not_found: "Tento používateľ sa nenašiel." + invalid: "Ľutujeme, nesmiete si privlatniť tohto užívateľa." + users: + title: 'Používatelia' + create: 'Pridať admin používateľa' + last_emailed: "Posledný odemailovaný" + not_found: "Prepáčte, toto užívateľské meno sa nenachádza v našom systéme." + id_not_found: "Prepáčte, toto užívateľské id sa nenachádza v našom systéme." + active: "Aktívny" + show_emails: "Ukázať Emaily" + nav: + new: "Nový" + active: "Aktívny" + pending: "Čakajúca" + staff: 'Zamestnanci' + suspended: 'Odobrate práva' + blocked: 'Zablokovaný' + suspect: 'Podozrivý' + approved: "Schválený?" + approved_selected: + one: "schváliť užívateľa" + few: "schváliť ({{count}}) užívateľov " + other: "schváliť ({{count}}) užívateľov " + reject_selected: + one: "zamietnuť užívateľa" + few: "zamietnuť ({{count}}) užívateľov " + other: "zamietnuť ({{count}}) užívateľov " + titles: + active: 'Aktívni používatelia' + new: 'Noví používatelia' + pending: 'Užívatelia čakajúci na kontrolu' + newuser: 'Užívatelia na Stupni dôvery 0 (Noví užívatelia)' + basic: 'Užívatelia na Stupni dôvery 1 (Bežný užívateľ)' + member: 'Užívatelia na Stupni dôvery 2 (Člen)' + regular: 'Užívatelia na Stupni dôvery 3 (Stály člen)' + leader: 'Užívatelia na Stupni dôvery 4 (Vodca)' + staff: "Zamestnanci" + admins: 'Admin používatelia' + moderators: 'Moderátori' + blocked: 'Zablokovaní užívatelia' + suspended: 'Užívatelia s odobratými právami' + suspect: 'Podozriví užívatelia' + reject_successful: + one: "Úspešne zamietnutý užívateľ" + few: "Úspešne zamietnutí %{count} užívatelia" + other: "Úspešne zamietnutých %{count} užívateľov" + reject_failures: + one: "Nepodarilo sa zamietnuť 1 užívateľa" + few: "Nepodarilo sa zamietnuť %{count} užívateľov" + other: "Nepodarilo sa zamietnuť %{count} užívateľov" + not_verified: "Neoverený" + check_email: + title: "Odhaliť emailovú adresu tohto používateľa" + text: "Zobraziť" + user: + suspend_failed: "Niečo sa pokazilo pri odoberaní práv tomuto užívateľovi {{error}}" + unsuspend_failed: "Niečo sa pokazilo pri obnovovaní práv tomuto užívateľovi {{error}}" + suspend_duration: "Ako dlho budú užívateľovi odobrate práva?" + suspend_duration_units: "(dni)" + suspend_reason_label: "Prečo mu odoberáte práva? Tento text sa zobrazí každému na stránke profilu užívateľa a bude zobrazený užívateľovi pri pokuse o prihlásenie. Buďte strucný." + suspend_reason: "Dôvod" + suspended_by: "Práva odobraté" + delete_all_posts: "Zmazať všetky príspevky" + delete_all_posts_confirm: "Chystáte sa zmazať %{posts} príspevkov a %{topics} tém. Ste si istý?" + suspend: "Odobrať" + unsuspend: "Obnoviť" + suspended: "Odobrate práva?" + moderator: "Moderátor?" + admin: "Admin?" + blocked: "Blokovaný?" + show_admin_profile: "Admin" + edit_title: "Upraviť názov" + save_title: "Uložiť názov" + refresh_browsers: "Vynútiť refresh browsera." + refresh_browsers_message: "Správa odoslaná všetkým klientom!" + show_public_profile: "Ukázať verejný profil" + impersonate: 'Privlastniť' + ip_lookup: "Vyhľadávanie IP" + log_out: "Odhlásiť sa" + logged_out: "Užívateľ bol odhlásený na všetkých zariadeniach" + revoke_admin: 'Odobrať admin' + grant_admin: 'Udeliť admin' + revoke_moderation: 'Odobrať moderovanie' + grant_moderation: 'Udeliť moderovanie' + unblock: 'Odblokovať' + block: 'Blokovať' + reputation: Reputácia + permissions: Práva + activity: Aktivita + like_count: '"Páči sa" Rozdané / Prijaté' + last_100_days: 'za posledných 100 dní' + private_topics_count: Súkromné témy + posts_read_count: Prečítané príspevky + post_count: Vytvorené príspevky + topics_entered: Zobrazených tém + flags_given_count: Rozdané označenia + flags_received_count: Prijaté označenia + warnings_received_count: Prijaté varovania + flags_given_received_count: 'Rozdané a prijaté označenia' + approve: 'Schváliť' + approved_by: "schválený" + approve_success: "Uťívateľ schválený a bol zaslaný email s aktivačnými inštrukciami" + approve_bulk_success: "Úspech! Všetci vybraní uťívateľia boli schválení a oboznáamení." + time_read: "Doba Čítania" + anonymize: "Anonymizovať používateľa" + anonymize_confirm: "Ste si istý že chcete zmeniť tento účet na anonymný? Zmeni to užívateľské meno, email a zmažú sa všetky informácie z profilu. " + anonymize_yes: "Áno, zmeň tento účet na anonymný" + anonymize_failed: "Nastala chyba pri anonymizovaní účtu." + delete: "Odstrániť používateľa" + delete_forbidden_because_staff: "Správcovia a moderátori nemôžu byť vymazaní." + delete_posts_forbidden_because_staff: "Nedá sa zmazať príspevky správcov a moderátorov." + delete_forbidden: + one: "Užívatelia nemôžu byť vymazaní ak majú príspevky. Najprv zmažte príspevky až potom užívateľa. (Príspevky staršie ako %{count} deň nemožno zmazať)" + few: "Užívatelia nemôžu byť vymazaní ak majú príspevky. Najprv zmažte príspevky až potom užívateľa. (Príspevky staršie ako %{count} dni nemožno zmazať)" + other: "Užívatelia nemôžu byť vymazaní ak majú príspevky. Najprv zmažte príspevky až potom užívateľa. (Príspevky staršie ako %{count} dní nemožno zmazať)" + cant_delete_all_posts: + one: "Nepodarilo sa zmazať všetky príspevky. Niektoré príspevky sú staršie ako %{count} deň. (Nastavenie delete_user_max_post_age )" + few: "Nepodarilo sa zmazať všetky príspevky. Niektoré príspevky sú staršie ako %{count} dni. (Nastavenie delete_user_max_post_age )" + other: "Nepodarilo sa zmazať všetky príspevky. Niektoré príspevky sú staršie ako %{count} dní. (Nastavenie delete_user_max_post_age )" + cant_delete_all_too_many_posts: + one: "Nedá sa zmazať všetky píspevky, pretože užívateľ má viac ako 1 príspevok. (delete_all_posts_max)" + few: "Nedá sa zmazať všetky píspevky, pretože užívateľ má viac ako %{count} príspevky. (delete_all_posts_max)" + other: "Nedá sa zmazať všetky píspevky, pretože užívateľ má viac ako %{count} príspevkov. (delete_all_posts_max)" + delete_confirm: "Ste si ISTÝ, že chcete zmazať tohoto užívateľa? Už sa to nedá obnoviť!" + delete_and_block: "Zazať a zablokovať tento email a IP adresu" + delete_dont_block: "Iba vymazať" + deleted: "Používateľ bol vymazaný." + delete_failed: "Počas vymazávania používateľa nastala chyba. Pred vymazaním používateľa sa uistite, že všetky jeho príspevky sú zmazané." + send_activation_email: "Poslať aktivačný email." + activation_email_sent: "Aktivačný emial bol odoslaný." + send_activation_email_failed: "Počas odosielania ďalšieho aktivačného emailu nastala chyba. %{error}" + activate: "Aktivovať účet" + activate_failed: "Počas aktivácie používateľa nastala chyba." + deactivate_account: "Deaktivovať účet" + deactivate_failed: "Počas deaktivácie používateľa nastala chyba." + unblock_failed: 'Nastala chyba pri odblokovaní užívateľa.' + block_failed: 'Nastala chyba pri zablokovaní užívateľa.' + deactivate_explanation: "Deaktivovaý užívateľ musí znovu overiť svoj email" + suspended_explanation: "Suspendovaní užívatelia sa nemôžu prihlasovať." + block_explanation: "Zablokovaní uťívatelia nemôžu zakladať témy ani pridávať príspevky." + trust_level_change_failed: "Nastala chyba pri zmene úrovne dôveryhodnosti užívateľa." + suspend_modal_title: "Zruš práva užívateľovi" + trust_level_2_users: "Užívatelia na 2 Stupni dôvery" + trust_level_3_requirements: "Požiadavky pre 3 stupeň" + trust_level_locked_tip: "stupeň dôvery je zamknutý, systém užívateľovi stupeň nezvýši ani nezníži " + trust_level_unlocked_tip: "stupeň dôvery je odomknutý, systém môže užívateľovi stupeň zvýšiť alebo znížiť" + lock_trust_level: "Zamknúť stupeň dôvery" + unlock_trust_level: "Odomknúť stupeň dôvery" + tl3_requirements: + title: "Požiadavky pre stupeň dôvery 3" + table_title: "Za posledných 100 dní:" + value_heading: "Hodnota" + requirement_heading: "Požiadavka" + visits: "Návštev" + days: "dní" + topics_replied_to: "Témy na ktoré odpovedal" + topics_viewed: "Zobrazených tém" + topics_viewed_all_time: "Videné témy (za celú dobu)" + posts_read: "Prečítané príspevky" + posts_read_all_time: "Prečítaných príspevkov (za celú dobu)" + flagged_posts: "Označené príspevky" + flagged_by_users: "Užívatelia, ktorí označili" + likes_given: "Rozdaných 'páči sa mi'" + likes_received: "Obdržaných 'páči sa mi'" + likes_received_days: "Obdržaných 'páči sa mi' na jednotlivé dni" + likes_received_users: "Obdržaných 'páči sa mi' na jednotlivých užívateľov" + qualifies: "Spĺňa požiadavky pre stupeň dôvery 3" + does_not_qualify: "Nespĺňa požiadavky pre stupeň dôvery 3" + will_be_promoted: "Bude čoskoro povýšený" + will_be_demoted: "Čoskoro bude degradovaný" + on_grace_period: "V súčastnosti je v povyšovacej skúšobnej dobe, nebude degradovaný." + locked_will_not_be_promoted: "Stupeň dôvery je zamknutý. Nikdy nebude povýšený." + locked_will_not_be_demoted: "Stupeň dôvery je zamknutý. Nikdy nebude degradovaný" + sso: + title: "Jednotné prihlásenie" + external_id: "Externé ID" + external_username: "Používateľské meno" + external_name: "Meno" + external_email: "Email" + external_avatar_url: "URL profilovej fotky" + user_fields: + title: "Užívateľské polia" + help: "Pridaj polia, ktoré môžu užívatelia vyplniť" + create: "Vytvor užívateľske pole" + untitled: "Bez názvu" + name: "Názov poľa" + type: "Typ poľa" + description: "Popis poľa" + save: "Uložiť" + edit: "Upraviť" + delete: "Odstrániť" + cancel: "Zrušiť" + delete_confirm: "Ste si istý, že chcete zmazať toto užívateľské pole?" + options: "Možnosti" + required: + title: "Požadované pri registrácii?" + enabled: "povinné" + disabled: "nepovinné" + editable: + title: "Upravovateľné po registrácii?" + enabled: "upravovateľné " + disabled: "neupravovateľné " + show_on_profile: + title: "Ukázať na verejnom profile?" + enabled: "zobrazené na profile" + disabled: "nezobrazené na profile" + field_types: + text: 'Textové pole' + confirm: 'Potvrdenie' + dropdown: "Zoznam" + site_text: + description: "Môžete prispôsobiť hociktorý text na Vašom fóre. Prosím začnite hľadaním nižšie:" + search: "Hľadajte text, ktorý chcete upraviť" + title: 'Textový obsah' + edit: 'uprav' + revert: "Vrátiť zmeny" + revert_confirm: "Ste si istý, že chcete vrátiť vykonané zmeny späť?" + go_back: "Návrat na vyhľadávanie" + recommended: "Odporúčame prispôsobenie nasledujúceho textu podľa vašich potrieb:" + show_overriden: 'Ukázať iba zmenené' + site_settings: + show_overriden: 'Ukázať iba zmenené' + title: 'Nastavenia' + reset: 'zrušiť' + none: 'žiadne' + no_results: "Žiadne výsledky" + clear_filter: "Vyčistiť" + add_url: "pridaj URL" + add_host: "pridať hostiteľa" + categories: + all_results: 'Všetky' + required: 'Povinné' + basic: 'Základné nastavenia' + users: 'Používatelia' + posting: 'Prispievam' + email: 'Email' + files: 'Súbory' + trust: 'Stupne dôvery' + security: 'Bezpečnosť' + onebox: "Onebox" + seo: 'SEO' + spam: 'Spam' + rate_limits: 'Limity a obmedzenia' + developer: 'Vývojár' + embedding: "Vkladám" + legal: "Právne záležitosti" + uncategorized: 'Ostatné' + backups: "Zálohy" + login: "Prihlásenie" + plugins: "Pluginy" + user_preferences: "Užívateľské Nastavenia" + badges: + title: Odznaky + new_badge: Nový odznak + new: Nový + name: Meno + badge: Odznak + display_name: Zobrazované meno + description: Popis + badge_type: Typ odznaku + badge_grouping: Skupina + badge_groupings: + modal_title: Zoskupovanie odznakov + granted_by: Pridelené užívateľom + granted_at: Pridelené na + reason_help: (Odkaze na príspevok, alebo tému) + save: Uložiť + delete: Odstrániť + delete_confirm: Ste si istý, že chcete zmazať tento odznak? + revoke: Zrušiť + reason: Dôvod + expand: Rozbaliť … + revoke_confirm: Ste si istý, že chcete obnoviť tento odznak? + edit_badges: Upraviť odznaky + grant_badge: Prideliť odznaky + granted_badges: Pridelené odznaky + grant: Prideliť + no_user_badges: "%{name} nebol pridelený žiaden odznak." + no_badges: Nie sú žiadne odznaky, ktoré môžu byť pridelené. + none_selected: "Vyberte odznak, aby ste mohli začať" + allow_title: Povoliť použitie odznaku namiesto názvu + multiple_grant: Môže byť pridelené viacnásobne + listable: Zobraziť odznak na stránke verejných odznakov + enabled: Povoliť odznak + icon: Ikona + image: Obrázok + icon_help: "Použi buď font z triedy Awesome, alebo URL na obrázok" + query: Požiadavka na Odznak (SQL) + show_posts: Zobraziť príspevok o pridelení odznaku na stránke odznakov + trigger: Spúšťač + trigger_type: + none: "Obnovovať denne" + post_action: "Keď užívateľ zareaguje na príspevok" + post_revision: "Keď užívateľ vytvorí príspevok" + trust_level_change: "Keď užívateľ zmení stupeň dôvery" + user_change: "Keď je užívateľ vytvorený, alebo upravený" + preview: + link_text: "Prezerať pridelené odznaky" + plan_text: "Náhľad na plán požiadaviek" + modal_title: "Požiadavka na Odznak Prezeranie" + sql_error_header: "Nastala chyba s požiadavkou." + error_help: "Pozrite si nasledujäce odkazy pre pomoc s dopytovacími odznakmi." + bad_count_warning: + header: "UPOZORNENIE!" + text: "Chýbajú ukážky práv. Toto sa stane, ak dotaz na odznak vráti ID používateľa alebo príspevku, ktorý neexistuje. Toto môže zapríčiniť neskoršie neočakávané výsledky - prosíme znovu overte Váš dotaz." + no_grant_count: "Žiadne odznaky na pridelenie." + grant_count: + one: "1 odznak na pridelenie" + few: "%{count} odznaky na pridelenie" + other: "%{count} odznakov na pridelenie" + sample: "Vzor:" + grant: + with: %{username} + with_post: %{username} za príspevok v %{link} + with_post_time: %{username} za príspevok v %{link} v čase %{time} + with_time: %{username} v čase %{time} + emoji: + title: "Emoji" + help: "Pridaj nové emoji, ktoré bude dostupné pre všetkých (TIP: môžete pretiahnuť viac súborov naraz)" + add: "Pridaj nové Emoji" + name: "Meno" + image: "Obrázok" + delete_confirm: "Ste si istý, že chcete zmazať: %{name}: emoji?" + embedding: + get_started: "Pokiaľ chcete vložiť Discourse na inú stránku, začnite pridaním jej hostiteľa." + confirm_delete: "Ste si istý, že chcete zmazať tohoto hostiteľa?" + sample: "Použite nasledovný HTML kód vo Vašej stránke pre vytvorenie vloženej témy Discourse. Nahraďte REPLACE_ME kanonickou URL adresou stránky, do ktorej to vkladáte." + title: "Vkladám" + host: "Povolení hostitelia" + edit: "uprav" + category: "Prispievať do kategórií" + add_host: "Pridať hostiteľa" + settings: "Nastavenia vkladania" + feed_settings: "Nastavenie zdrojov" + feed_description: "Zadaním RSS/ATOM kanálu Vašich stránok zlepší schopnosť Discourse vladať Váš obsah." + crawling_settings: "Nastavenia vyhľadávača" + crawling_description: "Ak Discourse vytvorí tému pre Váš príspevok a neexistuje žiadny RSS/ATOM kanál tak sa pokúsime získať Váš obsah z HTML. Získanie obsahu môže byt niekedy výzva a preto poskytujeme možnosť špecifikovať CSS pravidlá na uľahčenie získania obsahu." + embed_by_username: "Užívateľské meno pre vytváranie tém" + embed_post_limit: "Maximálny počet vložených príspevkov" + embed_username_key_from_feed: "Kľúč na získanie užívateľského mena discourse zo zdroja" + embed_truncate: "Skrátiť vložené príspevky" + embed_whitelist_selector: "CSS selector pre elementy ktoré je možné vkladať" + embed_blacklist_selector: "CSS selector pre elementy ktoré nie je možné vkladať" + feed_polling_enabled: "importovať príspevky cez RSS/ATOM" + feed_polling_url: "URL adresa zdroja RSS/ATOM na preskúmanie" + save: "Uložiť Nastavenia vkladania" + permalink: + title: "Trvalé odkazy" + url: "URL" + topic_id: "IT témy" + topic_title: "Témy" + post_id: "ID príspevku" + post_title: "Príspevok" + category_id: "ID kategórie" + category_title: "Kategória" + external_url: "Externá URL" + delete_confirm: Ste si istý, že chcete zmazať tento trvalý odkaz? + form: + label: "Nový:" + add: "Pridať" + filter: "Hľadať (URL alebo externá URL)" + lightbox: + download: "stiahnuť" + search_help: + title: 'Pomoc pri vyhľadávaní' + keyboard_shortcuts_help: + title: 'Klávesové skratky' + jump_to: + title: 'Preskočiť na' + home: 'g, h Domov' + latest: 'g, l Najnovšie' + new: 'g, n Nové' + unread: 'g, u Neprečítané' + categories: 'g, c Kategórie' + top: 'g, t Hore' + bookmarks: 'g, b Záložky' + profile: 'g, p Profil' + messages: 'g, m Správy' + navigation: + title: 'Navigácia' + jump: '# Choď na príspevok #' + back: 'u Späť' + up_down: 'k/j Presuň označené ↑ ↓' + open: 'o or EnterOtvoriť zvolenú tému' + next_prev: 'shift+j/shift+k Nasledujúca/predchádzajúca sekcia' + application: + title: 'Aplikácia' + create: 'c Vytvoriť novú tému' + notifications: 'n Otvor upozornenia' + hamburger_menu: '= Otvoriť hamburger menu' + user_profile_menu: 'p Otvor užívateľské menu' + show_incoming_updated_topics: '. Zobraz aktualizované témy' + search: '/ Hľadať' + help: '? Pomoc s klávesovými skratkami' + dismiss_new_posts: 'x, r Zahodiť Nové/Príspevky' + dismiss_topics: 'x, t Zahodiť témy' + log_out: 'shift+z shift+z Odhlásiť sa' + actions: + title: 'Akcie' + bookmark_topic: 'f Zmeniť tému záložky' + pin_unpin_topic: 'shift+p Pripnúť/Odopnúť tému' + share_topic: 'shift+s Zdielať tému' + share_post: 's Zdielať príspevok' + reply_as_new_topic: 't Odpoveď ako súvisiaca téma' + reply_topic: 'shift+r Odpovedať na tému' + reply_post: 'r Odpovedať na príspevok' + quote_post: 'q Citovať príspevok' + like: 'l Označiť príspevok "Páči sa"' + flag: '! Označiť príspevok ' + bookmark: 'b Pridať príspevok do záložiek' + edit: 'e Editovat príspevok' + delete: 'd Zmazať príspevok' + mark_muted: 'm, m Umlčať tému' + mark_regular: 'm, r Obyčajná (preddefinovaná) téma' + mark_tracking: 'm, w Sledovať tému' + mark_watching: 'm, w Sledovať tému' + badges: + title: Odznaky + allow_title: "môže byť použitý ako názov" + multiple_grant: "Môže byť ocenené viacnásobne" + badge_count: + one: "1 Odznak" + few: "%{count} Odznaky" + other: "%{count} Odznakov" + more_badges: + one: "+1 Viac" + few: "+%{count} Viac" + other: "+%{count} Viac" + granted: + one: "1 povolené" + few: "%{count} povolené" + other: "%{count} povolených" + select_badge_for_title: Vyberte odznak, ktorý chcete použiť ako Váš titul + none: "" + badge_grouping: + getting_started: + name: Začíname + community: + name: Komunita + trust_level: + name: Stupeň dôvery + other: + name: Ostatné + posting: + name: Prispievam + badge: + editor: + name: Editor + description: Úprava prvého príspevku + basic_user: + name: Základné + description: Povolené všetky základné funkcie komunity + member: + name: Člen + description: Povolené pozývanie + regular: + name: Bežný + description: Povolené zmeny kategóriií, premenovávanie, odkazy a lóža + leader: + name: Vodca + description: Pridelené globálna editácia, pripnutia, uzatváranie, archivovanie, rozdeľovanie a spájanie + welcome: + name: Vitajte + description: Prijal "Páči sa" + autobiographer: + description: Vyplnený užívateľský profil + anniversary: + name: Výročie + description: Aktívny člen, napísal aspoň jeden príspevok za rok + nice_post: + name: Pekný príspevok + description: Príspevok získal 10 "Páči sa". Tento odznak môže byť pridelený viac krát + good_post: + name: Dobrý príspevok + description: Príspevok získal 25 "Páči sa". Tento odznak môže byť pridelený viac krát + great_post: + name: Vynikajúci príspevok + description: Príspevok získal 50 "Páči sa". Tento odznak môže byť pridelený viac krát + nice_topic: + name: Pekná téma + description: Téma získala 10 "Páči sa". Tento odznak môže byť pridelený viac krát + good_topic: + name: Dobrá téma + description: Téma získala 25 "Páči sa". Tento odznak môže byť pridelený viac krát + great_topic: + name: Vynikajúca téma + description: Téma získala 50 "Páči sa". Tento odznak môže byť pridelený viac krát + nice_share: + name: Pekné zdieľanie + description: Zdieľaný príspevok s 25 jedinečnými návštevami + good_share: + name: Dobré zdieľanie + description: Zdieľaný príspevok s 300 jedinečnými návštevami + great_share: + name: Výborné zdieľanie + description: Zdieľaný príspevok s 1000 jedinečnými návštevami + first_like: + name: Prvé "Páči sa mi" + description: Príspevok, ktorý sa páčil + first_flag: + name: Prvé označenie + description: Označený príspevok + promoter: + name: Propagátor + description: pozval užívateľa + campaigner: + description: Pozval 3 základných užívateľov (stupeň dôvery 1) + champion: + name: Šampión + description: Pozval 5 členov (stupeň dôvery 2) + first_share: + name: Prvé zdieľanie + description: Zdieľal príspevok + first_link: + name: Prvý odkaz + description: Pridaný vnútorný odkaz na ďalšiu tému + first_quote: + name: Prvý citát + description: Citoval užívateľa + read_guidelines: + name: 'Prečítať pravidlá ' + description: Prečítať pravidlá komunity + reader: + name: Čitateľ + description: Prečítať každý príspevok v téme, ktorá má viac ako 100 príspevkov + popular_link: + name: Populárny odkaz + description: Príspevok s externým odkazom s najmenej 50 kliknutiami + hot_link: + name: Horúci odkaz + description: Príspevok s externým odkazom s najmenej 300 kliknutiami + famous_link: + name: Skvelý odkaz + description: Príspevok s externým odkazom s najmenej 1000 kliknutiami + google_search: | +

    Vyhľadávať pomocou Google

    +

    +

    +

    diff --git a/config/locales/client.tr_TR.yml b/config/locales/client.tr_TR.yml index 74c2e0c08b..e5801a2468 100644 --- a/config/locales/client.tr_TR.yml +++ b/config/locales/client.tr_TR.yml @@ -267,6 +267,12 @@ tr_TR: total_rows: other: "%{count} kullanıcı" groups: + empty: + posts: "Bu grubun üyelerinden mesaj yok." + members: "Bu grupta üye yok." + mentions: "Bu gruptan söz edilmemiş." + messages: "Bu grup için bir mesaj yok." + topics: "Bu grubun üyelerinden konu yok." add: "Ekle" selector_placeholder: "Üye ekle" owner: "sahip" @@ -285,6 +291,16 @@ tr_TR: trust_levels: title: "Eklendiklerinde üyelere otomatik olarak güven seviyesi verilir:" none: "Hiç" + notifications: + watching: + title: "Gözleniyor" + tracking: + title: "Takip ediliyor" + regular: + title: "Normal" + muted: + title: "Susturuldu" + description: "Bu gruptan herhangi yeni konuyla ilgili asla bildirim almayacaksınız" user_action_groups: '1': "Verilen Beğeniler" '2': "Alınan Beğeniler" @@ -303,6 +319,7 @@ tr_TR: all_subcategories: "hepsi" no_subcategory: "hiçbiri" category: "Kategori" + category_list: "Kategori ekranı listesi" reorder: title: "Kategorileri Yeniden Sırala" title_long: "Kategori listesini yeniden yapılandır" @@ -357,6 +374,7 @@ tr_TR: invited_by: "Tarafından Davet Edildi" trust_level: "Güven Seviyesi" notifications: "Bildirimler" + statistics: "istatistikler" desktop_notifications: label: "Masaüstü Bildirimleri" not_supported: "Bildirimler bu tarayıcıda desteklenmiyor. Üzgünüz." @@ -410,7 +428,14 @@ tr_TR: warnings_received: "uyarılar" messages: all: "Hepsi" + inbox: "Gelen Kutusu" + sent: "Gönderildi" + archive: " Arşiv" groups: "Gruplarım" + bulk_select: "Mesajları seçin" + move_to_inbox: "Gelen kutusuna taşı" + failed_to_move: "Seçilen mesajları taşımak başarısız oldu (muhtemelen ağınız çöktü)" + select_all: "Tümünü seç" change_password: success: "(e-posta gönderildi)" in_progress: "(e-posta yollanıyor)" @@ -834,6 +859,7 @@ tr_TR: granted_badge: "Rozet alındı" popup: mentioned: '{{username}}, "{{topic}}" başlıklı konuda sizden bahsetti - {{site_title}}' + group_mentioned: '{{username}} sizden bahsetti "{{topic}}" - {{site_title}}' quoted: '{{username}}, "{{topic}}" başlıklı konuda sizden alıntı yaptı - {{site_title}}' replied: '{{username}}, "{{topic}}" başlıklı konuda size cevap verdi - {{site_title}}' posted: '{{username}}, "{{topic}}" başlıklı konuya yazdı - {{site_title}}' @@ -932,6 +958,12 @@ tr_TR: create: 'Yeni Konu' create_long: 'Yeni bir konu oluştur' private_message: 'Mesajlaşma başlat' + archive_message: + help: 'Mesajı arşivine taşı' + title: ' Arşiv' + move_to_inbox: + title: 'Gelen kutusuna taşı' + help: 'Mesajı yeniden gelen kutusuna taşı' list: 'Konular' new: 'yeni konu' unread: 'okunmamış' @@ -976,6 +1008,7 @@ tr_TR: auto_close_title: 'Otomatik Kapatma Ayarları' auto_close_save: "Kaydet" auto_close_remove: "Bu Konuyu Otomatik Olarak Kapatma" + auto_close_immediate: "Son konudaki mesaj %{hours} saat olmuş, bu yüzden konu hemen kapanacak" progress: title: konu gidişatı go_top: "en üst" @@ -1094,6 +1127,7 @@ tr_TR: success: "O kullanıcıyı bu mesajlaşmaya davet ettik." error: "Üzgünüz, kullanıcı davet edilirken bir hata oluştu." group_name: "grup adı" + controls: "Konu Kontrolleri" invite_reply: title: 'Davet Et' username_placeholder: "kullanıcıadı" @@ -1199,8 +1233,6 @@ tr_TR: yes_value: "Evet, vazgeç" via_email: "bu gönderi e-posta ile iletildi" whisper: "bu gönderi yöneticiler için özel bir fısıltıdır" - wiki: - about: "bu gönderi bir wiki; acemi kullanıcılar düzenleyebilir" archetypes: save: 'Seçenekleri kaydet' controls: @@ -2141,6 +2173,7 @@ tr_TR: unlock_trust_level: "Güvenlik Seviyesi Kilidini Aç" tl3_requirements: title: "Güven Seviyesi 3 için Gerekenler" + table_title: "Son %{time_period} günde" value_heading: "Değer" requirement_heading: "Gereksinim" visits: "Ziyaretler" diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index 28cc27323f..582f0a9b30 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -956,6 +956,12 @@ zh_CN: create: '新主题' create_long: '创建一个新主题' private_message: '发送消息' + archive_message: + help: '移动消息到存档' + title: '存档' + move_to_inbox: + title: '移动到收件箱' + help: '移动消息到收件箱' list: '主题' new: '新主题' unread: '未读' @@ -1225,7 +1231,7 @@ zh_CN: via_email: "通过电子邮件发送的帖子" whisper: "这个帖子是只对版主可见的密语" wiki: - about: "这个帖子是维基;基础用户能编辑它" + about: "这个帖子是维基" archetypes: save: '保存选项' controls: @@ -2337,7 +2343,7 @@ zh_CN: title: "Emoji" help: "增加所有人可用的 emoji。(高端技巧:一次性拖进多个文件)" add: "增加新的 Emoji" - name: "姓名" + name: "名称" image: "图片" delete_confirm: "你确定要删除 :%{name}: emoji 么?" embedding: diff --git a/config/locales/server.ar.yml b/config/locales/server.ar.yml index 15031669fc..dd2e7b3189 100644 --- a/config/locales/server.ar.yml +++ b/config/locales/server.ar.yml @@ -1034,7 +1034,7 @@ ar: tl2_requires_likes_received: "كمية الإعجابات التي يجب على العضو إرسالها قبل ترقيته لمستوى الثقة 2." tl2_requires_likes_given: "كمية الإعجابات التي يجب على العضو جمعها قبل ترقيته لمستوى الثقة 2." tl2_requires_topic_reply_count: "كمية مواضيع العضو التي يجب الرد عليها قبل الترقية لمستوى الثقة 2." - tl3_requires_days_visited: "أقل عدد من الأيام التي يجب على المستخدم زيارة الموقع في آخر مئةيوم للتأهل لمستوى الثقة 3. (0 حتى 100)" + tl3_time_period: "الفترة الزمنية لمتطلبات مستور الثقة 3." tl3_requires_topics_replied_to: "أقل عدد من المواضيع التي يجب على المستخدم الرد عليها في آخر 100 يوم للتأهل لمستوى الثقة 3. (0 أو أكثر)" tl3_requires_topics_viewed: "نسبة الموضوعات التي تم إنشاؤها في آخر 100 يوم، تجعل المستخدم مؤهلًا لعرضه للترقية إلى مستوى الثقة الثالث3 . (من 0 الى 100)" tl3_requires_posts_read: "النسبه المئويه للمنشورات التي تم انشاءها في اخر 100 يوم ,التي يحتاج المستخدم لمعينتها تؤهله للترقيه للمستوي الثقه 3. (0 الي 100)" @@ -1109,6 +1109,7 @@ ar: disable_emails: "منع Discourse من إرسال أي نوع من رسائل البريد الإلكتروني" strip_images_from_short_emails: "شريط الصور من البريد الإلكتروني لها حجم أقل من 2800 بايت" short_email_length: "طول أقصر بريد الكتروني بـ Bytes." + display_name_on_email_from: "أعرض الاسماء كاملة في البريد الالكتروني من المجال." pop3_polling_enabled: "تصويت عبرPOP3 لردود البريد الإلكتروني." pop3_polling_ssl: "أستخدم SSL عند الأتصال بمخدم POP3.(مُستحسن)" pop3_polling_period_mins: "الفترة دقائق بين التحقق من حساب POP3 للبريد الإلكتروني. ملاحظة : تتطلب إعادة تشغيل." @@ -1149,6 +1150,7 @@ ar: allow_profile_backgrounds: "اسمح للأعضاء برفع خلفيات ملف التعريف." enable_mobile_theme: "اجهزه الموبايل تستخدم ثيم مناسب للجوال,امكانيه التحويل للموقع الكامل .الغي هذا اذا اردت استخدام انماط سريعه الاستجابه" dominating_topic_minimum_percent: "ما هي النسبه المئويه للمشاركات التي يجب علي المستخدم عملها في الموضوع قبل تلقيه لتنبيه عن خروجه لنطاق الموضوع" + disable_avatar_education_message: "عطل الرسالة التعليمية لتغيير الصورة الرمزية." daily_performance_report: "تحليل سجلات NGINX يومي ونشر موضوع \"قاقم فقط\" مع التفاصيل" suppress_uncategorized_badge: "لا تظهر الوسام للمواضيع غير المصنفة في قائمة الموضوع." permalink_normalizations: "يتم تطبيق التعابير القياسية قبل مطابقة صور الروابط الثابتة، على سبيل المثال: /(\\/topic.*)\\?.*/\\1 سوف تقطع الاستعلامات من الموضوع. التنسيق هو : التعبير القياسي+النص المستخدم \\ إلخ.. للوصول للقطع" @@ -1217,6 +1219,7 @@ ar: invalid_string_max: "يجب ان لا تكون الاحرف اكثر من %{max} " invalid_reply_by_email_address: "القيمة يجب أن تحتوي '%{reply_key}' وتختلف عن إشعار البريد الإلكتروني." notification_types: + group_mentioned: "%{group_name} ذكرت في %{link}" mentioned: "%{display_username} ذكرك في %{link}" liked: "%{display_username} أعجب بمشاركتك في %{link}" replied: "%{display_username} رد على مشاركتك في %{link}" @@ -1809,6 +1812,8 @@ ar: unsubscribe: title: "غير مشترك " description: "لست مهتما في تلقي هذه الرسائل الالكترونيه؟ لا مشكله! اضغط تحت ليتم الغاء اشتركك فورا:" + reply_by_email: "للرد, قم بالرد لهذا البريد الالكتروني أو [زُرَ الموضوع](%{base_url}%{url})." + visit_link_to_respond: "للرد, [زُرَ الموضوع](%{base_url}%{url})." posted_by: "مشاركة بواسطة %{username} على %{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} دعاك لرسالة '%{topic_title}'" @@ -1824,6 +1829,21 @@ ar: > %{site_title} -- %{site_description} + يرجى زيارة هذا الرابط لعرض الرسالة: %{base_url}%{url} + user_invited_to_private_message_pm_staged: + subject_template: "[%{site_name}] %{username} دعاك للرسالة '%{topic_title}'" + text_body_template: |2 + + %{username} دعاك لرسالة + + > **%{topic_title}** + > + > %{topic_excerpt} + + في + + > %{site_title} -- %{site_description} + يرجى زيارة هذا الرابط لعرض الرسالة: %{base_url}%{url} user_invited_to_topic: subject_template: "[%{site_name}] %{username} دعوتك للموضوع '%{topic_title}'" @@ -1842,16 +1862,78 @@ ar: الرجاء زيارة هذا الرابط لعرض الرسالة: %{base_url}%{url} user_replied: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_quoted: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_mentioned: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_group_mentioned: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_posted: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_posted_pm: subject_template: "[%{site_name}] [مساءً] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} + user_posted_pm_staged: + subject_template: "%{optional_re}%{topic_title}" + text_body_template: |2 + + %{message} + + --- + %{respond_instructions} digest: why: "ملخص المؤجز من %{site_link} منذ زيارتك الاخيره في %{last_seen_at}" subject_template: "[%{site_name}] الخلاصة" diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index 30ae7ee653..157c2fa0cf 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -865,7 +865,6 @@ de: tl2_requires_likes_given: "Die Anzahl der Likes, die ein Benutzer vergeben muss, bevor er in die Vertrauensstufe 2 befördert wird." tl2_requires_topic_reply_count: "Die Anzahl der Beiträge, auf die ein Benutzer antworten muss, bevor er in die Vertrauensstufe 2 befördert wird." tl3_time_period: "Zeitraum für die Bedingungen für Vertrauensstufe 3" - tl3_requires_days_visited: "Die Mindestanzahl an Tagen, an denen ein Benutzer in den letzten 100 Tagen die Seite besucht haben muss, um die Vertrauensstufe 3 erreichen zu können. (0 bis 100)" tl3_requires_topics_replied_to: "Die Mindestanzahl an Themen, auf die ein Benutzer in den letzten 100 Tagen geantwortet haben muss, um die Vertrauensstufe 3 erreichen zu können. (0 oder mehr)" tl3_requires_topics_viewed: "Prozentualer Anteil aller Themen der letzten 100 Tage, die ein Nutzer mindestens gelesen haben muss, um die Vertrauensstufe Anführer (3) erreichen zu können. (0 bis 100)" tl3_requires_posts_read: "Prozentualer Anteil aller Beiträge der letzten 100 Tage, die ein Nutzer mindestens gelesen haben muss, um die Vertrauensstufe Anführer (3) erreichen zu können. (0 bis 100)" @@ -1620,6 +1619,8 @@ de: download_remote_images_disabled: 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: | + Um keine Benachrichtigungen mehr zu diesem Thema zu erhalten, bitte [hier klicken](%{unsubscribe_url}). Du kannst den Empfang von Benachrichtigungen per E-Mail auch in deinen [Profil-Eigenschaften](%{user_preferences_url}) deaktivieren. subject_re: "Re: " subject_pm: "[PN]" user_notifications: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 258770c833..4e8c0196c7 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -945,7 +945,9 @@ en: verbose_localization: "Show extended localization tips in the UI" previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours" - allow_staged_accounts: "[BETA] Automatically create staged accounts for incoming emails." + top_topics_formula_log_views_multiplier: "value of log views multiplier (n) in top topics formula: `log(views_count) * (n) + op_likes_count * 0.5 + LEAST(likes_count / posts_count, 3) + 10 + log(posts_count)`" + top_topics_formula_first_post_likes_multiplier: "value of first post likes multiplier (n) in top topics formula: `log(views_count) * 2 + op_likes_count * (n) + LEAST(likes_count / posts_count, 3) + 10 + log(posts_count)`" + top_topics_formula_least_likes_per_post_multiplier: "value of least likes per post multiplier (n) in top topics formula: `log(views_count) * 2 + op_likes_count * 0.5 + LEAST(likes_count / posts_count, (n)) + 10 + log(posts_count)`" rate_limit_create_topic: "After creating a topic, users must wait (n) seconds before creating another topic." rate_limit_create_post: "After posting, users must wait (n) seconds before creating another post." @@ -1017,6 +1019,8 @@ en: min_trust_to_edit_wiki_post: "The minimum trust level required to edit post marked as wiki." + min_trust_to_allow_self_wiki: "The minimum trust level required to make user's own post wiki." + min_trust_to_send_messages: "The minimum trust level required to create new private messages." newuser_max_links: "How many links a new user can add to a post." @@ -1104,6 +1108,9 @@ en: short_email_length: "Short email length in Bytes" display_name_on_email_from: "Display full names on email from fields" + unsubscribe_via_email: "Allow users to unsubscribe from emails by sending an email with 'unsubscribe' in the subject or body" + unsubscribe_via_email_footer: "Attach an unsubscribe link to the footer of sent emails" + pop3_polling_enabled: "Poll via POP3 for email replies." pop3_polling_ssl: "Use SSL while connecting to the POP3 server. (Recommended)" pop3_polling_period_mins: "The period in minutes between checking the POP3 account for email. NOTE: requires restart." @@ -1371,7 +1378,7 @@ en: must_begin_with_alphanumeric: "must begin with a letter or number or an underscore" must_end_with_alphanumeric: "must end with a letter or number or an underscore" must_not_contain_two_special_chars_in_seq: "must not contain a sequence of 2 or more special chars (.-_)" - must_not_contain_confusing_suffix: "must not contain a confusing suffix like .json or .png etc." + must_not_end_with_confusing_suffix: "must not end with a confusing suffix like .json or .png etc." email: not_allowed: "is not allowed from that email provider. Please use another email address." blocked: "is not allowed." @@ -1379,6 +1386,17 @@ en: blocked: "New registrations are not allowed from your IP address." max_new_accounts_per_registration_ip: "New registrations are not allowed from your IP address (maximum limit reached). Contact a staff member." + unsubscribe_mailer: + subject_template: "Confirm you no longer want to receive email updates from %{site_title}" + text_body_template: | + Someone (possibly you?) requested to no longer send email updates from %{site_domain_name} to this address. + If you with to confirm this, please click this link: + + %{confirm_unsubscribe_link} + + + If you want to continue receiving email updates, you may ignore this email. + invite_mailer: subject_template: "%{invitee_name} invited you to '%{topic_title}' on %{site_domain_name}" text_body_template: | @@ -1582,7 +1600,15 @@ en: - To reply with **a new topic**, use to the right of the post. Both old and new topics will be automatically linked together. - To insert a quote, select the text you wish to quote, then press any Reply button. Repeat for multiple quotes! + Your reply can be formatted using simple HTML, BBCode, or [Markdown](http://commonmark.org/help/): + + This is **bold**. + This is bold. + This is [b]bold[/b]. + + Want to learn Markdown? [Take our fun 10 minute interactive tutorial!](http://commonmark.org/help/tutorial/) + + To insert a quote, select the text you wish to quote, then press any Reply button. Repeat for multiple quotes. @@ -1734,13 +1760,34 @@ en: 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: + email_reject_insufficient_trust_level: subject_template: "[%{site_name}] Email issue -- Insufficient Trust Level" text_body_template: | We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. Your account does not have the required trust level to post new topics to this email address. If you believe this is in error, contact a staff member. + email_reject_inactive_user: + subject_template: "[%{site_name}] Email issue -- Inactive User" + text_body_template: | + We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. + + Your account associated with this email address is not activated. Please activate your account before sending emails in. + + email_reject_reply_user_not_matching: + subject_template: "[%{site_name}] Email issue -- Reply User Not Matching" + text_body_template: | + We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. + + The original notification was not sent to this email address. Try sending from a different email address, or contact a staff member. + + email_reject_no_message_id: + subject_template: "[%{site_name}] Email issue -- No Message Id" + text_body_template: | + We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. + + There was no Message-Id header in the email. Try sending from a different email address, or contact a staff member. + email_reject_no_account: subject_template: "[%{site_name}] Email issue -- Unknown Account" text_body_template: | @@ -1771,14 +1818,21 @@ en: Your account does not have the privileges to post new topics in that category. If you believe this is in error, contact a staff member. - email_reject_post_error: + email_reject_strangers_not_allowed: + subject_template: "[%{site_name}] Email issue -- Invalid Access" + text_body_template: | + We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. + + The category you sent this email to does not allow emails from unrestricted accounts. If you believe this is in error, contact a staff member. + + email_reject_invalid_post: subject_template: "[%{site_name}] Email issue -- Posting error" text_body_template: | We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. Some possible causes are: complex formatting, message too large, message too small. Please try again, or post via the website if this continues. - email_reject_post_error_specified: + email_reject_invalid_post_specified: subject_template: "[%{site_name}] Email issue -- Posting error" text_body_template: | We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. @@ -1789,6 +1843,14 @@ en: If you can correct the problem, please try again. + email_reject_invalid_post_action: + subject_template: "[%{site_name}] Email issue -- Invalid Post Action" + text_body_template: | + We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. + + The Post Action was not recognized. Please try again, or post via the website if this continues. + + email_reject_reply_key: subject_template: "[%{site_name}] Email issue -- Unknown Reply Key" text_body_template: | @@ -1796,12 +1858,12 @@ en: The provided reply key is invalid or unknown, so we don't know what this email is in reply to. Contact a staff member. - email_reject_destination: + email_reject_bad_destination_address: subject_template: "[%{site_name}] Email issue -- Unknown To: Address" text_body_template: | We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. - None of the destination addresses are recognized. Please make sure that the site address is in the To: line (not Cc: or Bcc:), and that you are sending to the correct email address provided by staff. + None of the destination addresses are recognized. Please make sure that you are sending to the correct email address provided by staff. email_reject_topic_not_found: subject_template: "[%{site_name}] Email issue -- Topic Not Found" @@ -1896,7 +1958,10 @@ en: text_body_template: "The `download_remote_images_to_local` setting was disabled because the disk space limit at `download_remote_images_threshold` was reached." unsubscribe_link: | - To stop receiving notifications for this particular topic, [click here](%{unsubscribe_url}). To unsubscribe from these emails, change your [user preferences](%{user_preferences_url}). + To stop receiving notifications for this particular topic, [click here](%{unsubscribe_url}). To unsubscribe from these emails, change your [user preferences](%{user_preferences_url}) + + unsubscribe_via_email_link: | + or, [click here](mailto:reply@%{hostname}?subject=unsubscribe) to unsubscribe via email. subject_re: "Re: " subject_pm: "[PM] " @@ -1908,8 +1973,8 @@ en: description: "Not interested in getting these emails? No problem! Click below to unsubscribe instantly:" header_instructions: '' - reply_by_email: "To respond, reply to this email or [visit the topic](%{base_url}%{url})." - visit_link_to_respond: "To respond, [visit the topic](%{base_url}%{url})." + reply_by_email: "[Visit Topic](%{base_url}%{url}) or reply to this email to respond." + visit_link_to_respond: "[Visit Topic](%{base_url}%{url}) to respond." posted_by: "Posted by %{username} on %{post_date}" @@ -1973,6 +2038,18 @@ en: --- %{respond_instructions} + user_replied_pm: + subject_template: "[%{site_name}] [PM] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} + user_quoted: subject_template: "[%{site_name}] %{topic_title}" text_body_template: | diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index eae1a85648..c4073d9ac3 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -149,7 +149,7 @@ es: private_message_abbrev: "Msj" rss_description: latest: "Temas recientes" - hot: "Temas calientes" + hot: "Temas candentes" posts: "Últimos posts" too_late_to_edit: "Ese post fue publicado hace demasiado tiempo. No puede ser editado ni eliminado." excerpt_image: "imagen" @@ -877,7 +877,7 @@ es: tl2_requires_likes_given: "¿Cuántos 'me gusta' un usuario debe de dar antes de promoverlo a nivel de confianza 2?" tl2_requires_topic_reply_count: "¿Cuántos temas un usuario debe de contestar antes de promoverlo a nivel de confianza 2?" tl3_time_period: "Tiempo para requisitos del nivel de confianza 3" - tl3_requires_days_visited: "El número mínimo de días que un usuario necesito haber visitado el sitio en los últimos 100 días para calificar a promoción de nivel de confianza 3. (0 a 100)" + tl3_requires_days_visited: "Mínimo número de días que un usuario necesita haber visitado el sitio en los últimos 100 para poder ser promocionado a nivel de confianza 3. Establece un valor superior para desactivar la posibilidad de subir a nivel de confianza 3. (De 0 a 100)" tl3_requires_topics_replied_to: "El número mínimo de temas que un usuario necesito haber respondido en los últimos 100 días para calificar a promoción de nivel de confianza 3. (0 o superior)." tl3_requires_topics_viewed: "El porcentaje de temas creados en los últimos 100 días que un usuario necesito haber visto para calificar a promoción de nivel de confianza 3. (0 a 100)" tl3_requires_posts_read: "El porcentaje de posts creados en los últimos 100 días que un usuario necesito haber visto para calificar a promoción de nivel de confianza 3. (0 a 100)" @@ -1002,6 +1002,7 @@ es: global_notice: "Mostrar una noticia de URGENCIA o EMERGENCIA para todos los visitantes, deja este campo en blanco para ocultarla (se puede utilizar código HTML)" disable_edit_notifications: "Inhabilitar editar notificaciones por el usuario system cuando 'download_remote_images_to_local' este activo." automatically_unpin_topics: "Quitar destacado automáticamente cuando el usuario llega al final del tema." + read_time_word_count: "Número de palabras por minuto para calcular el tiempo de lectura estimado." full_name_required: "El nombre completo es un campo obligatorio del perfil de usuario." enable_names: "Mostrar el nombre completo del usuario en su perfil, tarjeta de usuario y emails. Desactiva esta opción para ocultar el nombre completo en todas partes." display_name_on_posts: "Mostrar el nombre completo de un usuario en sus posts, además de su @usuario." diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index 326545f524..cb520fa05a 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -841,6 +841,7 @@ fi: active_user_rate_limit_secs: "Kuinka usein 'last_seen_at' kenttä päivitetään, sekunneissa" verbose_localization: "Näytä laajennetut lokalisointitiedot käyttöliittymässä" previous_visit_timeout_hours: "Kuinka kauan vierailun on täytynyt kestää, jotta se lasketaan 'edelliseksi' vierailuksi, tunneissa" + allow_staged_accounts: "[BETA] Luo automaattiset tunnukset vastaanotettujen sähköpostien mukaisesti." rate_limit_create_topic: "Ketjun luomisen jälkeen käyttäjän täytyy odottaa (n) sekuntia voidakseen luoda uuden ketjun." rate_limit_create_post: "Viestin luomisen jälkeen käyttäjän täytyy odottaa (n) sekuntia voidakseen luoda uuden viestin." rate_limit_new_user_create_topic: "Ketjun luomisen jälkeen uuden käyttäjän täytyy odottaa (n) sekuntia voidakseen luoda uuden ketjun." @@ -884,7 +885,7 @@ fi: tl2_requires_likes_given: "Kuinka monta tykkäystä käyttäjän täytyy antaa ennen ylentämistä luottamustasolle 2." tl2_requires_topic_reply_count: "Kuinka moneen ketjuun käyttäjän täytyy vastata ennen ylentämistä luottamustasolle 2." tl3_time_period: "Luottamustason 3 vaatimuksiin liittyvän ajanjakson pituus" - tl3_requires_days_visited: "Monenako päivänä vähintään käyttäjän täytyy olla vieraillut sivustolla viimeisen 100 päivän aikana voidakseen saavuttaa luottamustason 3. (asteikko 0-100)" + tl3_requires_days_visited: "Minimimäärä päiviä, joina käyttäjän täytyy olla vieraillut sivustolla viimeisen 100 päivän aikana voidakseen saavuttaa luottamustason 3. Poistaaksesi ylennykset aseta arvo korkeammaksi, kuin lt3 aikaraja, (0 tai korkeampi)" tl3_requires_topics_replied_to: "Moneenko ketjuun vähintään käyttäjän täytyy olla vastannut viimeisen 100 päivän aikana voidakseen saavuttaa luottamustason 3. (0 tai korkeampi)" tl3_requires_topics_viewed: "Montako prosenttia viimeisen 100 päivän aikana luoduista ketjuista käyttäjän täytyy olla katsellut voidakseen saavuttaa luottamustason 3. (asteikko 0-100)" tl3_requires_posts_read: "Montako prosenttia viimeisen 100 päivän aikana luoduista viesteistä käyttäjän täytyy olla katsellut voidakseen saavuttaa luottamustason 3. (asteikko 0-100)" @@ -960,6 +961,7 @@ fi: disable_emails: "Estä Discoursea lähettämästä mitään sähköpostia" strip_images_from_short_emails: "Poista kuvat sähköposteista, joiden koko on alle 2800 tavua" short_email_length: "Lyhyen sähköpostin pituus tavuissa" + display_name_on_email_from: "Näytä sähköpostien lähettäjinä käyttäjien koko nimet" pop3_polling_enabled: "Pollaa sähköpostivastaukset POP3:lla." pop3_polling_ssl: "Käytä SSL-salausta yhdistettäessä POP3-palvelimeen. (Suositellaan)" pop3_polling_period_mins: "Tiheys minuuteissa kuinka usein POP3 tililtä tarkastetaan uudet sähköpostit. HUOM: vaatii uudelleenkäynnistyksen." @@ -1007,6 +1009,7 @@ fi: global_notice: "Näytä KIIREELLISESTÄ HÄTÄTAPAUKSESTA kertova banneri kaikilla sivuilla kaikille käyttäjille, vaihda tyhjäksi piilottaaksesi sen (HTML sallittu)." disable_edit_notifications: "Poista muokkausilmoitukset system-käyttäjältä, kun 'download_remote_images_to_local' on asetettu." automatically_unpin_topics: "Poista ketjun kiinnitys automaattisesti, kun käyttäjä on sen lopussa." + read_time_word_count: "Sanamäärä minuutissa, jota käytetään lukuajan arviointiin." full_name_required: "Koko nimi on käyttäjäprofiilin vaadittu kohta" enable_names: "Näytä käyttäjän koko nimi profiilissa, käyttäjäkortissa ja sähköposteissa. Poista käytöstä piilottaaksesi koko nimen kaikkialla." display_name_on_posts: "Näytä käyttäjän pitkä nimi viesteissä @nimen lisäksi." @@ -1030,6 +1033,7 @@ fi: enable_cdn_js_debugging: "Salli /logs näyttää kunnolliset virheilmoitukset lisäämällä crossorigin permissions kaikkiin sisällytettyihin js-kirjastoihin." show_create_topics_notice: "Jos palstalla on vähemmän kuin 5 julkista ketjua, huomauta ylläpitäjiä ketjujen luonnista." delete_drafts_older_than_n_days: Poista yli (n) päivää vanhat luonnokset. + vacuum_db_days: "Aja VACUUM ANALYZE vapauttaaksesi tietokannan viemää tilaa migraatioiden jälkeen (aseta 0 poistaaksesi käytöstä)" prevent_anons_from_downloading_files: "Estä kirjautumattomia käyttäjiä lataamasta liitetiedostoja. VAROITUS: tämä estää viestin liitettyjen muiden tiedostojen, kuin kuvien käyttämisen sivustolla." slug_generation_method: "Valitse polkulyhenteen luomisen metodi. 'encoded' käyttää prosenttikoodausta, 'none' poistaa polkulyhenteet käytöstä." enable_emoji: "Ota emoji käyttöön" @@ -1068,7 +1072,12 @@ fi: invalid_string_min: "Täytyy olla vähintään %{min} merkkiä." invalid_string_max: "Ei saa olla yli %{max} merkkiä." invalid_reply_by_email_address: "Arvon täytyy sisältää '%{reply_key}' ja olla eri, kuin ilmoitusten sähköpostiosoite." + pop3_polling_host_is_empty: "Sinun täytyy asettaa 'pop3 polling host' ennen POP3-pollauksen ottamista käyttöön." + pop3_polling_username_is_empty: "Sinun täytyy asettaa 'pop3 polling username' ennen POP3-pollauksen ottamista käyttöön." + pop3_polling_password_is_empty: "Sinun täytyy asettaa 'pop3 polling password' ennen POP3-pollauksen ottamista käyttöön." + pop3_polling_authentication_failed: "POP3 autentikaatio epäonnistui. Tarkasta pop3 kirjautumistiedot." notification_types: + group_mentioned: "%{group_name} mainittiin täällä %{link}" mentioned: "%{display_username} viittasi sinuun viestissä %{link}" liked: "%{display_username} tykkäsi viestistäsi %{link}" replied: "%{display_username} vastasi viestiisi %{link}" @@ -1627,6 +1636,14 @@ fi: Kynnysarvoa voi muuttaa säätämällä asetuksen `block_new_user` arvoa sivuston asetuksissa. spam_post_blocked: subject_template: "Uuden käyttäjän %{username} viestit on estetty toistuvien linkkien vuoksi" + text_body_template: | + Tämä on automaattisesti luotu viesti. + + Uusi käyttäjä [%{username}](%{user_url}) yritti luoda useita viestejä, jotka sisälsivät linkkejä osoitteeseen %{domains}, mutta jotka estettiin roskapostin välttämiseksi. Käyttäjä voi edelleen luoda viesteijä, joissa ei ole linkkejä näihin osoitteisiin %{domains}. + + [Tarkasta käyttäjätili](%{user_url}). + + Tätä toimintoa voi muokata vaihtamalla asetuksia `newuser_spam_host_threshold` `white_listed_spam_host_domains` sivuston asetuksissa. unblocked: subject_template: "Tili avattu" text_body_template: | @@ -1646,6 +1663,8 @@ fi: download_remote_images_disabled: subject_template: "Linkattujen kuvien lataaminen on otettu pois käytöstä" text_body_template: "Asetus `download_remote_images_to_local` on otettu pois käytöstä, koska vapaan tilan rajoitus `download_remote_images_threshold` saavutettiin." + unsubscribe_link: | + Jos et enää halua ilmoituksia tästä ketjusta, [klikkaa tätä](%{unsubscribe_url}). Jos et halua sähköposti-ilmoituksia, muuta [tilisi asetuksia](%{user_preferences_url}). subject_re: "VS:" subject_pm: "[YV]" user_notifications: @@ -1671,6 +1690,21 @@ fi: > %{site_title} -- %{site_description} Seuraa tätä linkkiä nähdäksesi viestin: %{base_url}%{url} + user_invited_to_private_message_pm_staged: + subject_template: "[%{site_name}] %{username} kutsui sinut yksityiseen keskusteluun '%{topic_title}'" + text_body_template: |2 + + %{username} kutsui sinut yksityiseen keskusteluun + + > **%{topic_title}** + > + > %{topic_excerpt} + + sivustolla + + > %{site_title} -- %{site_description} + + Klikkaa tätä linkkiä nähdäksesi viestin: %{base_url}%{url} user_invited_to_topic: subject_template: "[%{site_name}] %{username} kutsui sinut viestiin '%{topic_title}'" text_body_template: |2 @@ -1688,16 +1722,78 @@ fi: Seuraa tätä linkkiä nähdäksesi keskustelun: %{base_url}%{url} user_replied: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_quoted: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_mentioned: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_group_mentioned: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_posted: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_posted_pm: subject_template: "[%{site_name}] [YV] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} + user_posted_pm_staged: + subject_template: "%{optional_re}%{topic_title}" + text_body_template: |2 + + %{message} + + --- + %{respond_instructions} digest: why: "Lyhyt tiivistelmä siitä mitä on tapahtunut sivustolla %{site_link} viimeisimmän vierailusi jälkeen %{last_seen_at}." subject_template: "[%{site_name}] Tiivistelmä" diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index 483057b3c1..e1c37e6c46 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -678,6 +678,8 @@ it: post_onebox_maxlength: "Lunghezza massima in caratteri di un messaggio Discourse in Onebox." onebox_domains_whitelist: "Lista di domini per i quali consentire la funzione di onebox; questi domini devono supportare OpenGraph o oEmbed. Testali su http://iframely.com/debug" logo_url: "L'immagine nel logo in alto a sinistra del sito, dovrebbe essere larga e di forma rettangolare. Se non impostata, verrà usato il testo del titolo del sito." + digest_logo_url: "Il logo alternativo usato in cima alla email di riepilogo. Dovrebbe essere a forma di un ampio rettangolo. Se lasciato vuoto, sarà utilizzato il campo `logo_url`." + logo_small_url: "Il piccolo logo in alto a sinistra del tuo sito, dovrebbe essere a forma di quadrato. Se non impostato, verrà mostrata l'icona di una casa." apple_touch_icon_url: "Icona usata per dispositivi touch Apple. La dimensione consigliata è 144 x 144 pixel." notification_email: "L'indirizzo presente nel campo from: usato per inviare tutte le email essenziali di sistema. Il dominio indicato deve avere i record SPF, DKIM e reverse PTR impostati correttamente perché l'email arrivi." email_custom_headers: "Una lista di intestazioni email personalizzate delimitata da una barra verticale (pipe |)" @@ -818,7 +820,6 @@ it: tl2_requires_likes_received: "Quanti \"Mi piace\" deve ricevere un utente per essere promosso al livello di esperienza 2." tl2_requires_likes_given: "Quanti \"Mi piace\" deve dare un utente per essere promosso al livello di esperienza 2." tl2_requires_topic_reply_count: "A quanti argomenti deve rispondere un utente per essere promosso al livello di esperienza 2." - tl3_requires_days_visited: "Per quanti giorni un utente deve aver visitato il sito negli ultimi 100 giorni per essere promosso al livello di esperienza 3 (da 0 a 100)." tl3_requires_topics_replied_to: "Numero minimo di argomenti cui un utente deve aver risposto negli ultinmi 100 giorni per essere promosso al livello di esperienza 3 (0 o maggiore)." tl3_requires_topics_viewed: "Percentuale di argomenti creati negli ultimi 100 giorni che un utente deve leggere per essere promosso al livello di esperienza 3 (da 0 a 100)." tl3_requires_posts_read: "Percentuale di messaggi creati negli ultimi 100 giorni che un utente deve leggere per essere promosso al livello di esperienza 3 (da 0 a 100)." @@ -844,6 +845,7 @@ it: title_max_word_length: "La lunghezza massima di una parola, in caratteri, nel titolo di un argomento." title_min_entropy: "Minima entropia (caratteri unici) richiesti come titolo di un argomento." body_min_entropy: "Minima entropia (caratteri unici) richiesti come corpo di un messaggio." + allow_uppercase_posts: "Permetti l'uso di tutti caratteri maiuscoli nel titolo di argomento o nel corpo di un messaggio. " title_fancy_entities: "Converti caratteri ASCII comuni in HTML nei titoli dell'argomento, tipo SmartyPants http://daringfireball.net/projects/smartypants/" min_title_similar_length: "Lunghezza minima di un titolo che attiva il controllo su argomenti simili." min_body_similar_length: "Lunghezza minima del corpo di un messaggio che attiva il controllo su argomenti simili." diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 8989ed87ef..87f3961ede 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -275,6 +275,8 @@ ru: title: "базовый пользователь" member: title: "участник" + regular: + title: "активный" leader: title: "лидер" change_failed_explanation: "Вы пытаетесь понизить пользователя %{user_name} до уровня доверия '%{new_trust_level}'. Однако, его уровень доверия уже '%{current_trust_level}'. %{user_name} останется с уровнем доверия '%{current_trust_level}'. Если вы все же хотите понизить пользователя, заблокируйте вначале уровень доверия." @@ -451,11 +453,14 @@ ru: description: 'Это сообщение может быть оскорбительным или нарушает правила поведения.' long_form: 'отметить как неуместное' notify_user: + title: 'Отправить @{{username}} сообщение' + description: 'Я хочу пообщаться приватно с этим человеком о его посте.' long_form: 'оповещаемый пользователь' email_title: 'Ваше сообщение в теме "%{title}"' email_body: "%{link}\n\n%{message}\n" notify_moderators: title: "Другое" + description: 'Этот пост требует внимания администрации по другой причине.' email_body: "%{link}\n\n%{message}\n" bookmark: title: 'Добавить в закладки' @@ -847,7 +852,6 @@ ru: tl2_requires_likes_received: "Сколько симпатий пользователь должен получить для продвижения до уровня доверия 2." tl2_requires_likes_given: "Сколько симпатий пользователь должен выразить для продвижения до уровня доверия 2." tl2_requires_topic_reply_count: "В скольких темах пользователь должен ответить для продвижения до уровня доверия 2." - tl3_requires_days_visited: "Минимальное количество дней посещения сайта пользователем за последние 100 дней, необходимое для продвижения до уровня доверия 3. (от 0 до 100)" tl3_requires_topics_replied_to: "Минимальное количество тем, в которых пользователь должен ответить за последние 100 дней, для возможности продвижения до уровня доверия 3. (0 и больше)" tl3_requires_topics_viewed: "Какой процент тем созданных за последние 100 дней, должен просмотреть пользовательзователь для повышения уровня доверия до 3. (0 to 100)" tl3_requires_posts_read: "Какой процент сообщений написанных за последние 100 дней, должен просмотреть пользовательзователь для повышения уровня доверия до 3. (0 to 100)" diff --git a/config/locales/server.sk.yml b/config/locales/server.sk.yml new file mode 100644 index 0000000000..4e982d9389 --- /dev/null +++ b/config/locales/server.sk.yml @@ -0,0 +1,1128 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +sk: + dates: + short_date_no_year: "MMM D" + short_date: "YYYY MMM D" + long_date: "YYYY MMM D h:mma" + datetime_formats: &datetime_formats + formats: + short: "%Y-%m-%d" + short_no_year: "%B %-d" + date_only: "%Y, %B %-d" + date: + month_names: [null, Január, Február, Marec, Apríl, Máj, Jún, Júl, August, September, Október, November, December] + <<: *datetime_formats + title: "DIscourse" + topics: "Témy" + posts: "príspevky" + loading: "Načítava sa" + powered_by_html: 'Systém beží na Discourse, najlepšie funguje so zapnutým JavaScriptom' + log_in: "Prihlásenie" + purge_reason: "Automaticky vymazaný ako opustený, neaktivovaný účet" + disable_remote_images_download_reason: "Sťahovanie vzdialených obrázkov je vypnuté kvôli nedostatku diskového priestoru." + anonymous: "Anonymný" + errors: &errors + format: '%{attribute} %{message}' + messages: + too_long_validation: "je obmedzený na %{max} znakov, zadali ste %{length}." + invalid_boolean: "Neplatná logická hodnota." + taken: "je už použité" + accepted: sa musí akceptovať + blank: nesmie byť prázdne + present: musí byť prázdne + confirmation: "sa nerovná %{attribute}" + empty: nesmie byť prázdne + equal_to: sa musí rovnať %{count} + even: musí byť párne + exclusion: je rezervované + greater_than: musí byť viac ako %{count} + greater_than_or_equal_to: musí byť väčšie alebo rovné %{count} + has_already_been_used: "je už použité" + inclusion: nie je v zozname + invalid: je nesprávne + is_invalid: "je nesprávne; skúste opísať lepšie" + less_than: musí byť menej ako %{count} + less_than_or_equal_to: musí byť menej alebo rovné %{count} + not_a_number: nie je číslo + not_an_integer: musí byť číslo + odd: musí byť nepárne + record_invalid: 'Validácia zlyhala s chybami: %{errors}' + restrict_dependent_destroy: + one: "Nedá sa zmazať záznam pretože existuje %{record} závislých záznamov" + many: "Záznam nemôže byť zmazaný z dôvodu zavislosti na zázname: %{record} " + too_long: + one: príliš dlhé (maximálne 1 znak) + few: príliiš dlhé (maximum je %{count} znaky) + other: príliiš dlhé (maximum je %{count} znakov) + too_short: + one: príliiš krátke (minimum je 1 znak) + few: príliiš krátke (minimum je %{count} znaky) + other: príliiš krátke (minimum je %{count} znakov) + wrong_length: + one: nesprávna dĺžka (musí byť 1 znak) + few: nesprávna dĺžka (musí byť %{count} znaky) + other: nesprávna dĺžka (musí byť %{count} znakov) + other_than: "musí byť iný než %{count}" + template: + body: 'Nastal problém s nasledujúcimi položkami:' + header: + one: Uloženie %{model} zlyhalo kôli chybe + few: Uloženie %{model} zlyhalo kôli %{count} chybám + other: 'Uloženie %{model} zlyhalo kôli %{count} chybám ' + embed: + load_from_remote: "Nastala chyba pri načítaní príspevku" + site_settings: + min_username_length_exists: "Nemôžete nastaviť minimálnu dĺžku používateľského mena viac ako najkratšie používateľské meno." + min_username_length_range: "Nemôžete nastaviť minimum viac ako maximum." + max_username_length_exists: "Nemôžete nastaviť maximálnu dĺžku používateľského mena kratšiu ako najdlhšie používateľské meno." + max_username_length_range: "Nemôžete nastaviť maximum menšie ako minimum." + default_categories_already_selected: "Nemôžete vybrať kategóriu použitú v inom zozname." + s3_upload_bucket_is_required: "Nemôžete nahrávať na S3 pokiaľ ste nezadali 's3_upload_bucket'." + bulk_invite: + file_should_be_csv: "Nahrávaný súbor by mal byť vo formáte csv alebo txt." + backup: + operation_already_running: "Prebieha spracovanie inej operácie. Momentálne sa nová úloha nedá spustiť. " + backup_file_should_be_tar_gz: "Záložný súbor musí byť archív .tar.gz" + not_enough_space_on_disk: "Na disku nie je dosť miesta na uloženie zálohy" + not_logged_in: "K tejto akcii musíte byť prihlásený." + not_found: "Požadovaná URL alebo zdroj sa nenašiel" + invalid_access: "Nemáte oprávnenie na zobrazenie požadovaných údajov!" + read_only_mode_enabled: "Táto stránka je v móde na čítanie. Zapisovanie je vypnuté" + too_many_replies: + one: "Lutujeme, noví užívatelia majú dočasne obmedzený počet príspevkov na jeden v rámci jednej témy." + few: "Lutujeme, noví užívatelia majú dočasne obmedzený počet príspevkov na %{count} v rámci jednej témy." + other: "Lutujeme, noví užívatelia majú dočasne obmedzený počet príspevkov na %{count} v rámci jednej témy." + embed: + start_discussion: "Začať diskusiu" + continue: "Pokračovať v diskusii" + more_replies: + one: "1 ďalšia odpoveď" + few: "%{count} ďalšie odpovede" + other: "%{count} ďalších odpovedí" + loading: "Nahrávanie Diskusie ..." + permalink: "Trvalý odkaz" + imported_from: "Toto je sprievodná diskusia k pôvodnej téme na %{link}" + in_reply_to: "▶ %{username}" + replies: + one: "1 odpoveď" + few: "%{count} odpovede" + other: "%{count} odpovedí" + no_mentions_allowed: "Ľutujeme, nesmiete menovať iných užívateľov" + too_many_mentions: + one: "Ľutujeme, v príspevku môžte menovat maximálne jedného užívateľa." + few: "Ľutujeme, v príspevku môžte menovat maximálne %{count} užívatelov." + other: "Ľutujeme, v príspevku môžte menovat maximálne %{count} užívatelov." + no_mentions_allowed_newuser: "Ľutujeme, noví užívatelia nesmú zmieňovať iných uživateľov" + too_many_mentions_newuser: + one: "Ľutujeme, noví užívatelia môžu menovat v príspevku maximálne jedného užívateľa." + few: "Ľutujeme, noví užívatelia môžu menovat v príspevku maximálne %{count} užívatelov." + other: "Ľutujeme, noví užívatelia môžu menovat v príspevku maximálne %{count} užívatelov." + no_images_allowed: "Ľutujeme, noví užívatelia nemôžu vkladať obrázky do príspevkov." + too_many_images: + one: "Ľutujeme, noví užívatelia môžu vložiť maximálne jeden obrázok do príspevku." + few: "Ľutujeme, noví užívatelia môžu vložiť maximálne %{count} obrázky do príspevku." + other: "Ľutujeme, noví užívatelia môžu vložiť maximálne %{count} obrázkov do príspevku." + no_attachments_allowed: "Ľutujeme, noví užívatelia nemôžu vkladať prílohy do príspevkov." + too_many_attachments: + one: "Ľutujeme, noví užívatelia môžu vložiť maximálne jednu prílohu do príspevku." + few: "Ľutujeme, noví užívatelia môžu vložiť maximálne %{count} prílohy do príspevku." + other: "Ľutujeme, noví užívatelia môžu vložiť maximálne %{count} príloh do príspevku." + no_links_allowed: "Ľutujeme, noví užívatelia nemôžu vkladať odkazy do príspevkov." + too_many_links: + one: "Ľutujeme, noví užívatelia môžu vložiť maximálne jeden odkaz do príspevku." + few: "Ľutujeme, noví užívatelia môžu vložiť maximálne %{count} odkazy do príspevku." + other: "Ľutujeme, noví užívatelia môžu vložiť maximálne %{count} odkazov do príspevku." + spamming_host: "Prepáčte, nemôžte publikovať odkazy na tento zdroj" + user_is_suspended: "Suspendovaní užívatelia nemôžu vkladať príspevky" + topic_not_found: "Niečo sa pokazilo. Téma mohla byť napríklad uzavretá, alebo zmazaná kým ste ju prezerali." + just_posted_that: "je to príliš podobné Vášmu predchádzajúcemu príspevku" + has_already_been_used: "je už použité" + invalid_characters: "obsahuje neplatné znaky" + is_invalid: "je nesprávne; skúste opísať lepšie" + next_page: "nasledujúca stránka →" + prev_page: "← predchádzajúca stránka" + page_num: "Stránka %{num}" + home_title: "Domov" + topics_in_category: " '%{category}' tém v tejto kategórii" + rss_posts_in_topic: "RSS čítačka na %{topic}'" + rss_topics_in_category: "RSS čítačka na tému v kategórii: '%{category}'" + author_wrote: "%{author} napísal:" + num_posts: "Príspevky:" + num_participants: "Prispievatelia:" + read_full_topic: "Čítať celý príspevok" + private_message_abbrev: "Správa" + rss_description: + latest: "Najnovšie témy" + hot: "Horúce témy" + posts: "Najnovšie príspevky" + too_late_to_edit: "Tento príspevok bol vytvorený príliš dávno. Už nemôže byť upravovaný či zmazaný" + excerpt_image: "Obrázok" + queue: + delete_reason: "Zmazané moderátorom" + groups: + errors: + can_not_modify_automatic: "Nemôžte upravovať automatickú skupinu" + member_already_exist: "'%{username}' už je členom tejto skupiny." + invalid_domain: "'%{domain}' nie je platnou doménou." + invalid_incoming_email: "'%{incoming_email}' je neplatná emailová addresa." + default_names: + everyone: "všetci" + admins: "administrátori" + moderators: "moderátori" + staff: "zamestnanci" + trust_level_0: "stupen_dovery_0" + trust_level_1: "stupen_dovery_1" + trust_level_2: "stupen_dovery_2" + trust_level_3: "stupen_dovery_3" + trust_level_4: "stupen_dovery_4" + education: + until_posts: + one: "jeden príspevok" + few: "%{count} príspevky" + other: "%{count} príspevkov" + new-topic: | + Víitajte na %{site_name} — **Ďakujeme za založenie novej konverzácie!** + + - Znie titulok zaujímavo ak ho čítate nahlas ? Zodpovedá obsahu ? + + - Koho by to mohlo zaujímať ? Prečo je to dôležité ? Akú reakciu očakávate ? + + - Vložte často používane slová vo vašej téme aby ju ostatní vedeli "nájsť". Pre zlúčenie vašej témy so súvisiacimi témami vyberte kategóriu . + + Pre viac informácií, [pozri návody](/guidelines). Tento panel sa zobrazí iba raz %{education_posts_text}. + new-reply: | + Vitajte na %{site_name} — **Ďakujeme za príspevok!** + + - Vylepší vaša odpoveď nejakým spôsobom diskusiu ? + + - Buďte ohľaduplní k ostatným členom komunity. + + - Konštruktívna kritika je vítana, avšak kritizujte "nápady", nie ľudí. + + Pre viac informácií, [pozri návody](/guidelines). Tento panel sa zobrazí iba raz %{education_posts_text}. + avatar: | + ### Čo tak pridať fotku k svojmu účtu? + + Už ste pridali zopár tém a odpovedi, ale Váš profil nie je taký jedinečný ako Vy -- Je to len písmeno. + + Zvážili ste **[pozrieť váš profil](%{profile_path})** a pridať obrázok , ktorý Vás vystihuje? + + Je jednoduchšie sledovať diskusie a nájsť zaujímave osoby v konverzácii pokiaľ ma každý z nich v profile jedinečný obrázok ! + sequential_replies: | + ### Zvážte možnosť odpovedať na viacero príspevkov naraz + + Namiesto množstva postupných odpovedí na tému prosím zvážte použitie jednej odpovede, ktorá bude obsahovať citácie z predchádzajúcich príspevkov, alebo odkazy na @meno + + Ak chcete dať časť svojho vloženého príspevku do úvodzoviek, stačí označiť text a stlačiť tlačidlo quote reply , ktoré sa následne objaví. + + Pre všetkých je jednoduchšie čítať témy, ktoré majú menšiu hĺbku vnorených odpovedi v porovnaní s množstvom malých individuálnych odpovedí. + dominating_topic: | + ### Umožnite ostatným nech sa zapoja do debaty + + Táto téma je pre Vás zjavne dôležitá – prispeli ste do nej viac než %{percent}% odpovedí. + + Ste si istí, že posktujete dostatok času aj ostatným aby prezentovali svoj názor ? + too_many_replies: | + ### Dosiahli ste limit počtu odpovedí na túto tému + + Ľutujeme, noví užívatelia môžu dočasne vložiť len %{newuser_max_replies_per_topic} odpovedí na jednu tému. + + Namiesto nového príspevku zvážte možnosť úpravy predchádzajúcich odpovedí, alebo skúste inú tému. + reviving_old_topic: | + ### Oživiť túto tému ? + + Posledná odpoveď k tejto téme je staršia ako %{days} dní. Vaša odpoveď posunie tému na vrchol tém a upozorní všetkých, ktorí sa zapojili do diskusie. + + Ste si istí, že chcete pokračovať v tejto starej diskusii? + activerecord: + attributes: + category: + name: "Názov kategórie" + post: + raw: "Telo" + user_profile: + bio_raw: "O mne" + errors: + models: + topic: + attributes: + base: + warning_requires_pm: "Výstraha môže byť pripojená len k súkromným spravam" + too_many_users: "Výstraha môže byť zaslaná len jednej osobe" + cant_send_pm: "Ľutujeme, nemôžte zaslať súkromnú správu tomuto užívateľovi" + no_user_selected: "Musíte zadať existujúceho užívateľa" + user: + attributes: + password: + common: "Toto je jedno z 10000 najbežnejších hesiel. Prosím použite bezpečnejšie heslo" + same_as_username: "je také isté ako vaše užívateľské meno. Prosím použite bezpečnejšie heslo" + same_as_email: "je také isté ako Váš email. Prosím použite bezpečnejšie heslo" + ip_address: + signup_not_allowed: "Registrácia z tohto účtu nie je povolená-" + color_scheme_color: + attributes: + hex: + invalid: "nesprávna farba" + <<: *errors + user_profile: + no_info_me: "
    Položka \"O mne\" vo Vašom profile je prázdna, želáte si ju doplníť?
    " + no_info_other: "
    %{name} nevložil zatiaľ nič do profilu \"O mne\"
    " + vip_category_name: "Salón" + vip_category_description: "Kategória výhradne pre členov s dôveryhodnosťou 3 a vyššou" + meta_category_name: "Podnety pre tvorcov stránky" + meta_category_description: "Diskusia o stránke, organizácii, ako funguje a ako ju môžme vylepšit" + staff_category_name: "Zamestnanci" + staff_category_description: "Súkromna kategória pre zamestnaneckú diskusiu. Témy su viditeľné len pre správcov a moderátorov diskusie" + assets_topic_body: "Toto je permanentná téma, viditeľná len pre redaktorov, na uchovávanie obrázkov a súborov použitých v dizajne tohto webu. Nezmazávajte ju!\n\n\nTu je návod:\n\n\n1. Odpovedzte na túto tému.\n2. Nahrajte sem všetky obrázky, ktoré si prajete použit pre logá, favikony a pod. (Použite ikonku „nahrať“ v paneli nástrojov editora, alebo obrázky potiahnite alebo „vložte“)\n3. Odošlite svoju odpoveď.\n4. Pre získanie adresy k nahratým obrázkom kliknite pravým tlačítkom na obrázky vo svojom novom príspevku, alebo kliknite na ikonku úpravy príspevku a následne si adresu skopírujte. \n5. Cesty k obrázkom vložte do [základného nastavenia](/admin/site_settings/category/required).\n\n\nAk potrebujete povoliť nahratie pre iné typy súborov, upravte `authorized_extensions` v [nastavení súborov](/admin/site_settings/category/files)." + lounge_welcome: + title: "Vítajte v Salóne" + category: + topic_prefix: "O kategórii: %{category} " + errors: + uncategorized_parent: "Nekategorizovaná nemôže mať nadradenú kategóriu" + self_parent: "Podkategória si nemôže byť zároveň kategóriou " + depth: "Nemôžte umiestniť podkategóriu pod inú podkategóriu" + email_in_already_exist: "Prichádzajúca emailová adresa '%{email_in}' sa už používa pre kategóriu '%{category_name}'." + cannot_delete: + uncategorized: "Nemôžte vymazať nekategorizované" + has_subcategories: "Nemôžte vymazať kategóriu pretože obsahuje podkategórie" + topic_exists: + one: "Nemôžte vymazať kategóriu pretože obsahuje tému %{topic_link}." + few: "Nemôžte vymazať kategóriu pretože obsahuje %{count} témy. Najstaršia téma je %{topic_link}." + other: "Nemôžte vymazať kategóriu pretože obsahuje %{count} tém. Najstaršia téma je %{topic_link}." + topic_exists_no_oldest: "Nemôžte vymazať túto kategóriu pretože obsahuje %{count} tém." + trust_levels: + newuser: + title: "nový používateľ" + basic: + title: "základný používateľ" + member: + title: "člen" + regular: + title: "bežný" + leader: + title: "vodca" + change_failed_explanation: "Pokúsili ste sa znížiť úroveň %{user_name} na '%{new_trust_level}'. Ale jeho/jej aktuálna úroveň už je '%{current_trust_level}'. %{user_name} zostane na úrovni '%{current_trust_level}' - Ak chcete znížiť úroveň používateľa, najskôr uzamknite úroveň" + rate_limiter: + slow_down: "Vykonali ste túto akciu príliš veľa krát, skúste neskôr" + too_many_requests: "Na vykonávanie tejto akcie máme nastavený denný limit. Prosím počkajte %{time_left} než skúsite znovu." + by_type: + first_day_replies_per_day: "Dosiahli ste maximálny počet odpovedí stanovený pre nového používateľa v jeho prvý deň. Prosím čakajte %{time_left} , než skúsite znova" + first_day_topics_per_day: "Dosiahli ste maximálny počet tém stanovený pre nového používateľa v jeho prvý deň. Prosím čakajte %{time_left} , než skúsite znova" + create_topic: "Výtvárate témy príliš rýchlo. Prosím čakajte %{time_left} , než skúsite znova" + create_post: "Odpovedáte príliš rýchlo. Prosím čakajte %{time_left} , než skúsite znova" + topics_per_day: "Dosiahli ste maximálny počet nových tém na tento deň. Prosím čakajte %{time_left} , než skúsite znova" + pms_per_day: "Dosiahli ste maximálny počet správ na tento deň. Prosím čakajte %{time_left} , než skúsite znova" + create_like: "Dosiahli ste maximálny počet \"Páči sa\" na tento deň. Prosím čakajte %{time_left} , než skúsite znova" + create_bookmark: "Dosiahli ste maximálny počet záložiek na tento deň. Prosím čakajte %{time_left} , než skúsite znova" + edit_post: "Dosiahli ste maximálny počet úprav na tento deň. Prosím čakajte %{time_left} , než skúsite znova" + hours: + one: "1 hodina" + few: "%{count} hodiny" + other: "%{count} hodín" + minutes: + one: "1 minútu" + few: "%{count} minúty" + other: "%{count} minút" + seconds: + one: " 1 sekundu" + few: "%{count} sekundy" + other: "%{count} sekúnd" + datetime: + distance_in_words: + half_a_minute: "< 1m" + less_than_x_seconds: + one: "< 1s" + few: "< %{count}s" + other: "< %{count}s" + x_seconds: + one: "1s" + few: "%{count}s" + other: "%{count}s" + less_than_x_minutes: + one: "< 1m" + few: "< %{count}m" + other: "< %{count}m" + x_minutes: + one: "1m" + few: "%{count}m" + other: "%{count}m" + about_x_hours: + one: "1h" + few: "%{count}h" + other: "%{count}h" + x_days: + one: "1d" + few: "%{count}d" + other: "%{count}d" + about_x_months: + one: " 1mes" + few: "%{count}mes" + other: "%{count}mes" + x_months: + one: "1mes" + few: "%{count}mes" + other: "%{count}mes" + about_x_years: + one: "1r" + few: "%{count}r" + other: "%{count}r" + over_x_years: + one: "> 1r" + few: "> %{count}r" + other: "> %{count}r" + almost_x_years: + one: "1r" + few: "%{count}r" + other: "%{count}r" + distance_in_words_verbose: + half_a_minute: "práve teraz" + less_than_x_seconds: + one: "práve teraz" + few: "práve teraz" + other: "práve teraz" + x_seconds: + one: "pred sekundou" + few: "pred %{count} sekundami" + other: "pred %{count} sekundami" + less_than_x_minutes: + one: "menej ako pred minútou" + few: "menej ako pred %{count} minútami" + other: "menej ako pred %{count} minútami" + x_minutes: + one: "pred minútou" + few: "pred %{count} minútami" + other: "pred %{count} minútami" + about_x_hours: + one: "pred hodinou" + few: "pred %{count} hodinami" + other: "pred %{count} hodinami" + x_days: + one: "včera" + few: "pred %{count} dňami" + other: "pred %{count} dňami" + about_x_months: + one: "približne pred mesiacom" + few: "približne pred %{count} mesiacmi" + other: "približne pred %{count} mesiacmi" + x_months: + one: "pred mesiacom" + few: "pred %{count} mesiacmi" + other: "pred %{count} mesiacmi" + about_x_years: + one: "približne pred rokom" + few: "približne pred %{count} rokmi" + other: "približne pred %{count} rokmi" + over_x_years: + one: "pred vyše rokom" + few: "pred vyše %{count} rokmi" + other: "pred vyše %{count} rokmi" + almost_x_years: + one: "pred takmer rokom" + few: "pred takmer %{count} rokmi" + other: "pred takmer %{count} rokmi" + password_reset: + no_token: "Prepáčte, táto linka na zmenu hesla je už príliš stará. Stlačte tlačitlo Prihlásiť a použite \"Zabudol som heslo\" pre vytvorenie novej linky" + choose_new: "Prosim zadajte nové heslo" + choose: "Prosím zadajte heslo" + update: 'Aktualizujte heslo' + save: 'Nastavte heslo' + title: 'Obnoviť heslo' + success: "Heslo bolo úspešne zmenené a ste prihlasený do systému. " + success_unapproved: "Heslo bolo úspešne zmenené." + continue: "Pokračujte na %{site_name}" + change_email: + confirmed: "Vaša emailova adresa bola aktualizovaná" + please_continue: "Pokračujte na %{site_name}" + error: "Nastala chyba pri aktualizácii emailu. Nieje už náhodou použitý?" + activation: + action: "Pre aktiváciu účtu kliknite sem" + already_done: "Prepáčte, táto potvrdzovacia linka je už neplatná. Nie je už účet aktivny ?" + please_continue: "Váš nový účet je potvrdený. Budete presmerovaní na domovskú stránku." + continue_button: "Pokračujte na %{site_name}" + welcome_to: "Vitajte na %{site_name}!" + approval_required: "Moderátor musí manuálne povoliť Váš prístup na toto fórum. O schválení prístupu budete upovedomení emailom." + missing_session: "Nevieme zistiť či Váš účet bol vytvorený, prosím uistite sa že máte v prehliadači povolené cookies." + post_action_types: + off_topic: + title: 'Mimo témy' + description: 'Tento príspevok nesúvisí s danou témou popísanou v názve a prvom príspevku a pravdepodobne by mal byť presunutý niekam inam' + long_form: 'toto je označené ako "Mimo témy"' + spam: + title: 'Spam' + description: 'Tento príspevok je reklama. Nijako nesúvisí s danou témou, má propagačný charakter.' + long_form: 'označiť toto ako spam' + email_title: '"%{title}" bol označený ako spam' + email_body: "%{link}\n\n%{message}" + inappropriate: + title: 'Nevhodné' + description: 'Obsah tohto príspevku môže byť nektorými osobami považovaný za urážlivý, hanlivý, alebo porušujúci pravidlá slušného správania.' + long_form: 'toto označ ako nevhodné' + notify_user: + title: 'Poslať @{{username}} správu' + description: 'Chcem sa s touto osobou rozprávať o jeho príspevku priamo a súkromne.' + long_form: 'úživateľovi bola zaslaná správa' + email_title: 'Váš príspevok v "%{title}"' + email_body: "%{link}\n\n%{message}" + notify_moderators: + title: "Niečo iné" + description: 'Tento príspevok vyžaduje pozornosť obsluhy z iného dôvodu ako je uvedené vyššie.' + long_form: 'označené do pozornosti obsluhy' + email_title: 'Príspevok "%{title}" vyžaduje pozornosť obsluhy' + email_body: "%{link}\n\n%{message}" + bookmark: + title: 'Záložka' + description: 'Vytvor záložku na tento príspevok' + long_form: 'záložka na tento príspevok bola vytvorená ' + like: + title: 'Páči sa mi' + description: 'Páči sa mi tento príspevok' + long_form: 'páčilo sa' + vote: + title: 'Hlasovať' + description: 'Hlasovať pre tento príspevok' + long_form: 'hlasoval pre tento príspevok' + topic_flag_types: + spam: + title: 'Spam' + description: 'Tento príspevok je reklama. Nijako nesúvisí s danou stránkou, má propagačný charakter.' + long_form: 'označiť toto ako spam' + inappropriate: + title: 'Nevhodné' + description: 'Táto téma môže byť nektorými osobami považovaná za urážlivú, hanlivú, alebo porušujúcu pravidlá slušného správania.' + long_form: 'toto označ ako nevhodné' + notify_moderators: + title: "Niečo iné" + description: 'Táto téma vyžaduje pozornosť obsluhy na základe pravidiel, TOS, alebo z iného dôvodu ako je uvedené vyššie.' + long_form: 'označené do pozornosti moderátora' + email_title: 'Téma "%{title}" vyžaduje pozornosť moderátora' + email_body: "%{link}\n\n%{message}" + flagging: + you_must_edit: '

    Váš príspevok bol označený komunitou. Prosim pozrite si Vaše správy.

    ' + user_must_edit: '

    Tento príspevok bol označený komunitou a je dočasne skrytý

    ' + archetypes: + regular: + title: "Bežná téma" + banner: + title: "Banerová téma" + message: + make: "Téma je odteraz baner. Bude sa zobrazovať navrchu každej stránky, pokiaľ ju používateľ nezavrie." + remove: "Téma odteraz nie je baner. Už sa nebude viac zobrazovat navrchu každej stránky." + unsubscribed: + title: 'Odhlásiť' + description: "Boli ste odhlásený. Už vás znovu nebudeme kontaktovať." + oops: "V prípade že ste to tak nemysleli, kliknite nižšie" + error: "Chyba pri odhlasovaní" + preferences_link: "Zo súhrnných emailov sa môžete odhlásiť na stránke nastavení" + different_user_description: "Aktuálne ste prihlásený ako iný používateľ než ten, komu bol zaslaný súhrnný email. Prosíme odhláste sa a skúste znovu." + not_found_description: "Prepáčte, nedokázali sme Vás odhlásiť. Je možné, že platnosť odkazu vo Vašom emaile vypršala." + resubscribe: + action: "Znovu-prihlásiť" + title: "Znovu-prihlásený!" + description: "Boli ste znovu prihlásený." + reports: + visits: + title: "Používateľské návštevy" + xaxis: "Deň" + yaxis: "Počet návštev" + signups: + title: "Noví používatelia" + xaxis: "Deň" + yaxis: "Počet nových používateľov" + profile_views: + title: "Prezreté používateľské profily" + xaxis: "Deň" + yaxis: "Počet prezretých používateľských profilov" + topics: + title: "Témy" + xaxis: "Deň" + yaxis: "Počet nových tém" + posts: + title: "Príspevky" + xaxis: "Deň" + yaxis: "Počet nových príspevkov" + likes: + title: "Páči sa mi" + xaxis: "Deň" + yaxis: "Počet nových páči sa mi" + flags: + title: "Označenia" + xaxis: "Deň" + yaxis: "Počet označení" + bookmarks: + title: "Záložky" + xaxis: "Deň" + yaxis: "Počet nových záložiek" + starred: + title: "Obľúbené" + xaxis: "Deň" + yaxis: "Počet nových ohviezdičkovaných tém" + users_by_trust_level: + title: "Užívatelia podľa stupňa dôvery" + xaxis: "Stupeň dôvery" + yaxis: "Počet použivateľov" + emails: + title: "Emaily odoslané" + xaxis: "Deň" + yaxis: "Počet emailov" + user_to_user_private_messages: + title: "Užívateľ užívateľovi" + xaxis: "Deň" + yaxis: "Počet správ" + system_private_messages: + title: "Systém" + xaxis: "Deň" + yaxis: "Počet správ" + moderator_warning_private_messages: + title: "Upozornenie od moderátora" + xaxis: "Deň" + yaxis: "Počet správ" + notify_moderators_private_messages: + title: "Upozorniť moderátora" + xaxis: "Deň" + yaxis: "Počet správ" + notify_user_private_messages: + title: "Upozorniť užívateľa" + xaxis: "Deň" + yaxis: "Počet správ" + top_referrers: + title: "Najlepší referenti" + xaxis: "Používateľ" + num_clicks: "Klinutia" + num_topics: "Témy" + top_traffic_sources: + title: "Najlepšie zdroje" + xaxis: "Doména" + num_clicks: "Klinutia" + num_topics: "Témy" + num_users: "Používatelia" + top_referred_topics: + title: "Najlepšie témy" + xaxis: "Témy" + num_clicks: "Klinutia" + page_view_anon_reqs: + title: "Anonymný" + xaxis: "Deň" + yaxis: "Anonymné požiadavky API" + page_view_logged_in_reqs: + title: "Prihlásený" + xaxis: "Deň" + yaxis: "Zaznamenané API požiadavky" + page_view_crawler_reqs: + title: "Webové prehľadávače" + xaxis: "Deň" + yaxis: "API dotazy web robota" + page_view_total_reqs: + title: "Celkovo" + xaxis: "Deň" + yaxis: "Všetky API požiadavky" + page_view_logged_in_mobile_reqs: + title: "Zaznamenané API požiadavky" + xaxis: "Deň" + yaxis: "Zaznamenané API požiadavky cez mobil" + page_view_anon_mobile_reqs: + title: "Anonymné požiadavky API" + xaxis: "Deň" + yaxis: "Zaznamenané API požiadavky cez mobil" + http_background_reqs: + title: "Pozadie" + xaxis: "Deň" + yaxis: "Požiadavky použité na aktívne aktualizácie a sledovanie" + http_2xx_reqs: + title: "Status 2xx (OK)" + xaxis: "Deň" + yaxis: "Úspešné požiadavky (Status 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (Presmerovanie)" + xaxis: "Deň" + yaxis: "Presmerované požiadavky (Status 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (Chyba na strane klienta)" + xaxis: "Deň" + yaxis: "Chyby na strane klienta (Status 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (Chyba na strane servera)" + xaxis: "Deň" + yaxis: "Chyby na strane servera (Status 5xx)" + http_total_reqs: + title: "Celkovo" + xaxis: "Deň" + yaxis: "celkový počet žiadostí" + time_to_first_response: + title: "Čas do prvej odpovede" + xaxis: "Deň" + yaxis: "Priemerný čas (hodiny)" + topics_with_no_response: + title: "Témy bez odozvy" + xaxis: "Deň" + yaxis: "Celkovo" + mobile_visits: + title: "Používateľské návštevy" + xaxis: "Deň" + yaxis: "Počet návštev" + dashboard: + rails_env_warning: "Váš server beží v %{env} móde" + ruby_version_warning: "Používate Ruby 2.0.0, ktorá má známe problémy. Aktualizujte prosím na záplatu 247, alebo novšiu." + host_names_warning: "Váš súbor config/database.yml používa lokálne meno hostiteľa. Aktualizujte ho menom Vašej stránky." + gc_warning: 'Váš server používa východzie nastavenia ruby garbage collectora, ktorý neposktytuje najlepší výkon. Prečítajte si tento príspevok ohľadom ladenia výkonu Ladenie Ruby and Rails pre Discourse.' + sidekiq_warning: 'Sidekiq neni spustený. Množstvo úloh, ako napríklad posielanie emailov, je vykonávaných asynchrónne prostrednictvom sidekiq. Prosim zabezpečte aby bol spustený aspoň jeden proces sidekiq Viac o Sidekiq.' + queue_size_warning: 'Počet úloh v zásobníku je %{queue_size}, čo je dosť veľa. To môže byť príznakom problému s procesom (procesmi) Sidekiq, alebo by ste mali spustiť viac Sidekiq procesov.' + memory_warning: 'Váš server beží s menej než 1 GB pamäte. Odporúča sa minimálne 1GB. ' + google_oauth2_config_warning: 'Server má nakonfigurovanú podporu registrácie a prihlásenia pomocou Google OAuth2 (enable_google_oauth2_logins), ale identifikačné údaje klienta a jeho tajné hodnoty nie sú nastavené. Navštívte Nastavenia stránky a aktualizujte nastavenia. Chcete vedieť viac? Pozrite si tento návod.' + facebook_config_warning: 'Server má nakonfigurovanú podporu registrácie a prihlásenia pomocou Facebooku (enable_facebook_logins), ale údaje "app Id" a tajné hodnoty nie sú nastavené. Navštívte Nastavenia stránky a aktualizujte nastavenia. Chcete vedieť viac? Pozrite si tento návod.' + twitter_config_warning: 'Server má nakonfigurovanú podporu registrácie a prihlásenia pomocou Twitteru (enable_twitter_logins), ale kľúč a tajné hodnoty nie sú nastavené. Navštívte Nastavenia stránky a aktualizujte nastavenia. Chcete vedieť viac? Pozrite si tento návod.' + github_config_warning: 'Server má nakonfigurovanú podporu registrácie a prihlásenia pomocou GitHub (enable_github_logins), ale identifikačné údaje klienta a jeho tajné hodnoty nie sú nastavené.. Navštívte Nastavenia stránky a aktualizujte nastavenia.Chcete vedieť viac? Pozrite si tento návod.' + s3_config_warning: 'Server má nakonfigurované nahrávanie súborov na S3, ale minimálne jedna z nasledujúcich hodnôt nie je nastavená: s3_access_key_id, s3_secret_access_key, alebo s3_upload_bucket. Navštívte Nastavenia stránky a aktualizujte nastavenia. Chcete vedieť viac? Pozrite si návod ako nastaviť nahrávanie súborov na S3.' + s3_backup_config_warning: 'Server má nakonfigurované nahrávanie záloh na S3, ale minimálne jedna z nasledujúcich hodnôt nie je nastavená: s3_access_key_id, s3_secret_access_key, alebo s3_backup_bucket. Navštívte Nastavenia stránky a aktualizujte nastavenia. Chcete vedieť viac? Pozrite si návod ako nastaviť nahrávanie súborov na S3.' + image_magick_warning: 'Server je nakonfigurovaný na vytváranie náhľadov z veľkých obrázkov, ale ImageMagick nie je nainštalovaný. Nainštalujte ImageMagick pomocou Vášho správcu balíčkov, alebo Stiahnite najnovšiu verziu.' + failing_emails_warning: 'Existuje %{num_failed_jobs} neuspešných emailých pokusov. Skontrolujte Váš app.yml a uistite sa, že nastavenia email serveru máte správne. Pozrieť neúspešné pokusy v Sidekiq.' + default_logo_warning: "Nastavte grafické logo Vašej stránky. Aktualizujte logo_url, logo_small_url, a favicon_url na Ňastaveniach stránky." + contact_email_missing: "Vložte kontaktnú email adresu aby ste mohli byť kontaktovaný v urgentných prípadov týkajúcich sa stránok. Vložte na Stránke nastavení." + contact_email_invalid: "Kontaktný email na stránke je neplatný. Aktualizujte ho v Nastavenia stránky." + title_nag: "Zadajte meno stránky. Aktualizujte ho v Nastavenia stránky." + site_description_missing: "Vložte jednovetný popis Vašej stránky, ktorý sa zobrazí vo výsledkoch vyhľadávania. Aktualizujte ho v Nastavenia stránky." + consumer_email_warning: "Vaše stránky sú nastavené tak aby na posielanie emailov používali Gmail (alebo iné služby pre spotrebiteľov). Gmail obmedzuje počet odoslaných emailov. Na zaistenie doručiteľonosti emailov zvážte použitie emailového poskytovateľa ako napríklad mandrill.com." + site_contact_username_warning: "Vložte názov účtu kolegu z personálu pre zasielanie dôležitých automatických správ. Aktualizujte kontakt v Nastavenia stránky." + notification_email_warning: "Notifikačné emaily sú posielané z neplatnej emailovej adresy vo Vašej doméne. Doručovanie emailov môže byť chybné a nespoľahlivé. Prosím nastavte platnú lokálnu emailovú adresu \"notification_email\" v Nastavenia stránky." + subfolder_ends_in_slash: "Vaše nastavenie podadresára je chybné, DISCOURSE_RELATIVE_URL_ROOT je ukončené lomítkom." + site_settings: + censored_words: "Slová, ktoré budu automatický nahradené znakmi ■■■■" + delete_old_hidden_posts: "Automatické zmazanie príspevkov, ktoré ostali skryté viac ako 30 dní" + default_locale: "Predvolený jazyk tejto inštancie Discourse je (ISO 639-1 Code)" + allow_user_locale: "Povoliť užívateľom zvoliť si vlastný jazyk" + min_post_length: "Minimálny povolený počet znakov v príspevkoch" + min_first_post_length: "Minimálny povolený počet znakov pre prvý príspevok (obsah témy)" + min_private_message_post_length: "Minimálny povolený počet znakov v správe" + max_post_length: "Maximálny povolený počet znakov v príspevkoch" + min_topic_title_length: "Minimálny povolený počet znakov v názve témy" + max_topic_title_length: "Maximálny povolený počet znakov v názve témy" + min_private_message_title_length: "Minimálny povolený počet znakov v správe" + min_search_term_length: "Minimálny povolený počet znakov vo vyhľadávaní" + allow_uncategorized_topics: "Pvoliť vytváranie tém bez kategórií. UPOZORNENIE: Pokiaľ existujú nekategorizované témy, musíte ich zaradiť do kategórii skôr než túto možnosť vypnete." + uncategorized_description: "Popis nekategorizovanej kategórie. Nechajte prázdne pre žiadny popis.." + allow_duplicate_topic_titles: "Povoliť témy s rovnakými, duplikovanými názvami" + unique_posts_mins: "Koľko minút musí byť medzi dvomi rovnakými príspevkami od jedného užívateľa." + educate_until_posts: "Ked užívateľ začne písať svojich prvých (n) príspevkov, zobraz sprievodcu vzdelávania pre nového užívateľa. " + title: "Meno stránok, tak ako je použité v značke title." + site_description: "Popíšte stránky v jednej vete, tak ako sa popisujú v tagu meta." + contact_email: "Emailová adresa kľúčovej kontaktnej osoby zodpovednej za túto stránku. Používa sa pri kritických situáciach, ako napríklad neošetrené návestia a tiež pri kontaktnom formulári pre dôležité prípady." + contact_url: "Kontaktná URL adresa pre túto stránku. Použitá tiež pri kontaktnom formulári pre dôležité prípady." + queue_jobs: "IBA VÝVOJÁRI VAROVANIE! Štandardne, požiadavky fronty v sidekiq. Ak je vypnuté, vaše stránky budú rozbité." + crawl_images: "Načítať obrázky z URL pre vloženie správnej výšky a šírky." + download_remote_images_to_local: "Stiahnuť obrázky zo vzdialených zdrojov na lokálne. Zabráni poškodeniu obrázkov. " + download_remote_images_threshold: "Minimálne miesto na lokálnom disku potrebné na stiahnutie obrázkov zo vzdialených zdrojov (v percentách)" + disabled_image_download_domains: "Nikdy nesťahovať obrázky z týchto domén. Zoznam oddeleny pipou \"|\"." + editing_grace_period: "Nevytvárať nové verzie editovaných príspevkov do (n) sekúnd po odoslaní príspevku." + post_edit_time_limit: "Autor môže upravovať alebo mazať svoje príspevky do (n) minút po ich odoslaní. Nastavte 0 pre nekonečno" + edit_history_visible_to_public: "Povoliť všetkým zobrazenie predchádzajúcich verzií upraveného príspevku. Pokiaľ je to vypnuté, verzie vidí iba obslužný personál" + delete_removed_posts_after: "Príspevky zmazané autorom budú automaticky zmazané po (n) hodinách. Pokiaľ je nastavená 0, príspevky budú zmazané okamžite." + max_image_width: "Maximálna šírka náhľadu obrázkov v príspevku" + max_image_height: "Maximálna výška náhľadu obrázkov v príspevku" + category_featured_topics: "Počet zobrazených tém na jednu kategóriu na stránke /katgórie. Zmena hodnoty sa na stránkach prejaví do 15 minút." + show_subcategory_list: "Zobraz zoznam podkategórií namiesto zoznamu tém po vstupe do menu kategórií" + fixed_category_positions: "Ak je to označené, budete môcť zoraďovať kategórie v pevnom poradí. Ak je neoznačené, kategórie budu zoradené podlľa aktivity." + fixed_category_positions_on_create: "Ak je označené, triedenie kategórií bude spravované v dialógu na vytváranie témy (vyžaduje fixed_category_positions)." + add_rel_nofollow_to_user_content: "Pridať rel nofollow ku všetkému zaslanému používateľskému kontextu s výnimkou interných odkazov (zahrňuje rodičovskú doménu). Ak to zmeníte, musíte vykonať prípaz rebake na všetky príspevky pomocou: \"rake posts:rebake\"" + exclude_rel_nofollow_domains: "Zoznam domén, na ktorých odkazy nebude pridávaný nofollow . tld.com automaticky umožní sub.tld.com. Aby ste umožnili internetovým vyhľadávačom nájsť kompletný obsah mali by ste pridať minimálne doménu najvyššej úrovne týchto stránok. Ak je nejaká časť stránok na iných doménach, pridajte ich tiež." + post_excerpt_maxlength: "Maximálna dĺžka príspevku okrem zhrnutia." + post_onebox_maxlength: "Maximálna dĺžka vloženého Discourse príspevku v znakoch." + onebox_domains_whitelist: "Zoznam domén, pre ktoré je umožnené vkladanie. Tieto domény by mali podporovať OpenGraph alebo oEmbed. Overte ich pomocou http://iframely.com/debug" + logo_url: "Obrázok loga na ľavej hornej časti stránky, mal by byť tvaru širokého obdĺžnika. Ak nebude uvedený, zobrazí sa nadpis stránok." + digest_logo_url: "Obrázok loga na začiatku sumarizačného emailu stránok. Mal by byť tvaru širokého obdĺžnika. Ak nebude uvedený, použije sa logo_url." + logo_small_url: "Malý obrázok loga na ľavej hornej strane stránok, mal by mať tvar štvorca a bude zobrazený pri skrolovaní nadol. Ak nebude uvedený, zobrazí sa piktogram domu." + favicon_url: "Favicon pre Vaše stránky, pozrite http://en.wikipedia.org/wiki/Favicon. Aby fungoval správne s CDN, musí byť vo formáte png" + mobile_logo_url: "Obrázok loga zobrazený na fixnej pozícií v ľavom hornom rohu mobilnej verzie. Mal byť mať tvar štvorca. Ak nie je uvedené, použije sa `logo_url`. Napríklad: http://example.com/uploads/default/logo.png" + apple_touch_icon_url: "Ikona použitá na zariadeniach Apple touch. Doporučovaná veľkosť je 144px na 144px." + notification_email: "Odosieľateľ: email adresa použitá na zasielanie všetkých systémových emailov. Uvedená doména musí mať správne nastavené záznamy SPF, DKIM a opačný PTR aby email dorazil." + email_custom_headers: "Zoznam voliteľných emailových hlavičiek oddelených |" + email_subject: "Voliteľný formát predmetu emailu pre štandardné emaily. Pozrite https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" + use_https: "Má byť url stránok (Discourse.base_url) http alebo https? NEZAPÍNAJTE MOŽNOSŤ AK HTTPS NIE JE NASTAVENÉ A FUNKČNÉ!" + summary_score_threshold: "Minimálne požadované skóre príspevku na to aby bol zahrnutý do 'Sumarizuj túto tému'" + summary_posts_required: "Minimálny počet príspevkou predtým ako je zapnuté 'Sumarizuj túto tému'" + summary_likes_required: "Minimálny počet páči sa mi predtým ako je zapnuté 'Sumarizuj túto tému'" + summary_percent_filter: "Ak používateľ klikne na 'Sumarizuj túto tému', zobraz najlepších % príspevkov" + summary_max_results: "Maximálny počet príspevkov vrátených pomocou 'Sumarizuj túto tému'" + enable_private_messages: "Umožni používateľom s úrovňou dôvery 1 (nastaviteľné pomocou min úroveň dôvery na posielanie správ) vytvárať správy a odpovedať na ne" + enable_long_polling: "Zbernica správ pre upozornenia môže používať techniku long-polling" + long_polling_base_url: "Base URL used for long polling (when a CDN is serving dynamic content, be sure to set this to origin pull) eg: http://origin.site.com" + long_polling_interval: "Ako dlho má server čakať pred odpovedaním klientom ak nie sú na zaslanie žiadne dáta (iba pre prihlásených používateľov)" + polling_interval: "Ak sa nepoužíva technika long-polling, ako často majú byť klienti dotazovaný v milisekundách" + anon_polling_interval: "Ako často majú byť dotazovaný anonymný klienti v milisekundách" + background_polling_interval: "Ako často majú byť dotazovaný klienti v milisekundách (ak je okno v pozadí)" + flags_required_to_hide_post: "Počet značiek ktoré automaticky príspevok skryjú a používateľovi bude zaslaná správa (0 pre nikdy)" + cooldown_minutes_after_hiding_posts: "Počet minút koľko musí používateľ počkať pred tým než môže upraviť príspevok skytý pomocou komunitných značiek" + max_topics_in_first_day: "Maximálny počet tém, ktoré používateľ môže vytvoriť v jeho prvý deň ." + max_replies_in_first_day: "Maximálny povolený počet odpovedí, ktoré môže používateľa vytvoriť v jeho prvý deň." + tl2_additional_likes_per_day_multiplier: "Zvýšiť počet páči sa mi na deň pre úroveň dôvery 2 (člen) vynásobením týmto číslom" + tl3_additional_likes_per_day_multiplier: "Zvýšiť počet páči sa mi na deň pre úroveň dôvery 3 (bežný) vynásobením týmto číslom" + tl4_additional_likes_per_day_multiplier: "Zvýšiť počet páči sa mi na deň pre úroveň dôvery 4 (vedúci) vynásobením týmto číslom" + num_flags_to_block_new_user: "Ak príspevky nového používateľa dostanú takéto množstvo spamových vlajok od num_users_to_block_new_user rôznych používateľov, skry všetky jeho príspevky a zabráň vytváraniu príspevkov v budúcnosti. 0 na vypnutie." + num_users_to_block_new_user: "Ak príspevky nového používateľa dostanú num_flags_to_block_new_user spamových vlajok od takéhoto množstva rôznych používateľov, skry všetky jeho príspevky a zabráň vytváraniu príspevkov v budúcnosti. 0 na vypnutie." + notify_mods_when_user_blocked: "Ak je používateľ automaticky zablokovaný, pošli správu všetkým moderátorom." + flag_sockpuppets: "Ak nový používateľ odpovedá na tému z rovnakej IP adresy, ako nový používateľ, ktorý danú tému vytvoril, označ oba ich príspevky ako potencionálny spam." + post_undo_action_window_mins: "Počet minút počas ktorých môžu užívatelia zrušiť poslednú akciu na príspevku (\"Páči sa\", označenie, atď..)." + must_approve_users: "Obsluha musí povoliť účty všetkým novým užívateľom skôr než im bude povolený prístup na stránku. UPOZORNENIE: zapnutie na živej stránke spôsobí zrušenie prístupu pre existujúcich používateľov, okrem obsluhy!" + pending_users_reminder_delay: "Upozorni moderátora ak nový užívateľ čaká na schválenie dlhšie ako tento počet hodín. Nastavte -1 pre vypnutie upozornenia." + allow_moderators_to_create_categories: "Povoliť moderátorom vytváranie nových kategórií" + use_admin_ip_whitelist: "Správcovia sa môťu prihlásiť iba pokiaľ pristupujú z adresy uvedenej v zozname kontrolovaných IP adries (Admin > Logs > Screened Ips)" + post_menu_hidden_items: "Položky vo východzích nastaveniach menu schované pri príspevkovom menu, dokiaľ neni kliknuté na rozbaľovaciu elipsu." + share_links: "Uveďte, ktoré položky sa maju zobraziť v zdieľacom dialógu a v akom poradí." + track_external_right_clicks: "Sledovať externé odkazy na ktore sa klkne pravým tlačidlom (npar. otvorenie v novej záložke) Vypnuté vo východzích nastaveniach, pretože to prepisuje URL adresy" + site_contact_username: "Platné uťívateľské meno obsluhy, ktorá bude posielať automatizované správy. Pokiaľ je vynechané, použije sa východzí systémový účet. " + send_welcome_message: "Poslač všetkým novým užívateľom uvítaciu správu s príručkou ako začať. " + suppress_reply_directly_below: "Nezobrazovať rozbaľovacie tlačidlo s počtom odpovedí ak je k príspevku len jedna odpoveď priamo pod ním." + suppress_reply_directly_above: "Nezobrazovať rozbaľovacie tlačidlo v-odpovedi-na ak je len jedna odpoveď priamo nad príspevkom." + suppress_reply_when_quoting: "Nezobrazovať rozbaľovacie tlačidlo v-odpovedi-na ak príspevok cituje odpoveď." + max_reply_history: "Maximálny počet odpovedí ktoré sa zobrazia po rozbalení v-odpovedi-na" + topics_per_period_in_top_summary: "Zobrazovaný počet najlepších tém vo východzom zozname najlepších tém." + topics_per_period_in_top_page: "Zobrazovaný počet najlepších tém vzozname po rozbalení 'Ukáž viac' najlepších tém." + redirect_users_to_top_page: "Automaticky presmeruj nových a dlho neprihlásených užívateľov na hlavnú stránku. " + top_page_default_timeframe: "Východzí interval pre presmerovanie na hlavnú stránku." + show_email_on_profile: "Zobraziť užívateľov email na ich profile (viditeľné len nimi samotnými a obsluhou)" + email_token_valid_hours: "Platnosť tokenov pre Zabudnuté heslo a aktiváciu účtu v (n) hodinách." + email_token_grace_period_hours: "Platnosť tokenov pre Zabudnuté heslo a aktiváciu účtu v (n) hodinách po ich uplatnení." + enable_badges: "Povoliť systém odznakov" + enable_whispers: "Povoliť súkromnú konverzáciu pre redakciu vrámci témy. (experimantálne)" + log_out_strict: "Pri odhlasovaní odhlásiť VŠETKY sessiony používateľa, na všetkých zariadeniach." + port: "POZOR! LEN PRE VÝVOJÁROV! Použiť tento HTTP port namiesto predvoleného portu 80. Nechajte prázdne pre port 80." + force_hostname: "POZOR! LEN PRE VÝVOJÁROV! V URL zadajte hostname. Ak chcete použiť predvolené, nechajte políčko prázdne." + min_username_length: "Minimálna dĺžka používateľského mena v znakoch." + max_username_length: "Maximálna dĺžka používateľského mena v znakoch." + reserved_usernames: "Používateľské mená, ktorých registrácia nie je povolená." + min_password_length: "Minimálna dĺžka hesla." + block_common_passwords: "Nepovoliť heslá, ktoré sú v zozname 10 000 najbežnejších hesiel." + sso_url: "URL pre single sign on." + enable_local_logins: "Povoliť pouťívanie lokálnych účtov a hesiel. (Poznámka: musí to byť zapnuté pre fungovanie pozvánok)" + allow_new_registrations: "Povoliť registráciu nových užívateľov. Odznačte to ak chcete zabrániť vytváraniu nových účtov. " + enable_signup_cta: "Zobraz oznámenie pre navrátilých anonymných užívateľov s výzvou na registráciu účtu. " + enable_yahoo_logins: "Povoliť autentifikáciu pomocou Yahoo." + enable_google_oauth2_logins: "Povoliť Google Oauth2 autentifikáciu. Táto métoda je podporovaná Googlom. Vyžaduje kľúč a šifru." + google_oauth2_client_id: "Client ID Vašej Google aplikácie." + google_oauth2_client_secret: "Client secret Vašej Google aplikácie." + enable_twitter_logins: "Povoliť autentifikáciu cez Twitter, vyžaduje twitter_consmer_key a twiiter_consumer_secret" + twitter_consumer_key: "Consumer key pre Twitter autentifikáciu, registrovaný na http://dev.twitter.com" + twitter_consumer_secret: "Consumer secret pre Twitter autentifikáciu, registrované na http://dev.twitter.com" + enable_facebook_logins: "Povoliť autentifikáciu pomocou Facebook, vyžaduje facebook_app_id a facebook_app_secret." + facebook_app_id: "App id pre Facebook autentifikáciu, registrované na https://developers.facebook.com/apps" + facebook_app_secret: "App secret pre Facebook autentifikáciu, registrované na https://developers.facebook.com/apps" + enable_github_logins: "Povoliť autentifikáciu cez Github, vyžaduje github_client_id a github_client_secret" + github_client_id: "Client id pre Github autentifikáciu, registrované na https://github.com/settings/applications" + github_client_secret: "Client secret pre Github autentifikáciu, registrované na https://github.com/settings/applications" + allow_restore: "Umožniť obnovu, ktorá nahradí VŠETKY data na stránkach! Ponechajte prázdne okrem prípadu ak chcete obnoviť zo zálohy." + maximum_backups: "Maximálny počet záloh udržiavaných na disku. Staršie zálohy budu automaticky vymazané" + automatic_backups_enabled: "Spustiť automatické zálohy podľa nastavenia intervalu záloh" + backup_frequency: "Ako často máme vytvárať zálohu stránok, v dňoch." + enable_s3_backups: "Po dokončení nahrať zálohy na S3. DÔLEŽITÉ: požaduje správne nastavenie prístupových údajov na S3 v menu Súbory." + s3_backup_bucket: "Vzdialený S3 bucket na uloženie záloh. VAROVANIE: Uistite sa, že ide o súkromný S3 bucket." + backup_time_of_day: "Čas v UTC, kedy sa má spustiť záloha." + backup_with_uploads: "Do pravidelných záloh ulož i nahrané súbory. Pri vypnutej voľbe sa uloží iba záloha databázy." + active_user_rate_limit_secs: "Ako často máme aktualizovať pole 'naposledy videný o', v sekundách" + verbose_localization: "Zobraziť rozšírené lokalizačné tipy v rozhraní" + previous_visit_timeout_hours: "Ako dlho trvá návšteva predtým, ako ju pokladáme za 'predchádzajúcu' návštevu, v hodinách" + rate_limit_create_topic: "Po vytvorení témy musí používateľ počkať (n) sekúnd pred vytvorením ďalšej témy." + rate_limit_create_post: "Po odoslaní prísprevku používateľ počkať (n) sekúnd pred vytvorením ďalšieho príspevku." + rate_limit_new_user_create_topic: "Po vytvorení témy musí používateľ počkať (n) sekúnd pred vytvorením ďalšej témy." + rate_limit_new_user_create_post: "Po odoslaní príspevku používateľ počkať (n) sekúnd pred vytvorením ďalšieho príspevku." + max_likes_per_day: "Maximálny počet páči sa mi používateľa denne." + max_flags_per_day: "Maximálny počet príznakov používateľa denne." + max_bookmarks_per_day: "Maximálny počet záložiek na jedného používateľa denne." + max_edits_per_day: "Maximálny počet úprav používateľa denne." + max_topics_per_day: "Maximálny počet tém používateľa denne." + max_private_messages_per_day: "Maximálny počet správ ktoré môže používateľ denne vytvoriť." + max_invites_per_day: "Maximálny počet pozvánok ktoré môže používateľ denne zaslať." + max_topic_invitations_per_day: "Maximálny počet pozvánok do tém ktoré môže používateľ denne zaslať." + suggested_topics: "Zobrazovaný počet navrhovaných tém na konci témy." + limit_suggested_to_category: "V navrhovaných témach zobraziť iba témy z aktuálnej kategórie." + clean_up_uploads: "Na zamedzenie nezákonného uloženia odstrániť opustené nahrané súbory na ktoré nevedie žiadny odkaz. VAROVANIE: pred zapnutím tohoto nastavenie možno chcete zálohovať Váš adresár s nahranými súbormi." + clean_orphan_uploads_grace_period_hours: "Doba (v hodinách) pred odstránením opustených nahraných súborov." + purge_deleted_uploads_grace_period_days: "Doba (v dňoch) pred úplným vymazaním odstráneného nahraného súboru." + purge_unactivated_users_grace_period_days: "Doba (v dňoch) pred tým, než je užívateľ, ktorý si neaktivoval svoj účet vymazaný." + enable_s3_uploads: "Ukladať nahrávané súbory na úložište S3. DÔLEŽITÉ: vyžaduje platné prístupové údaje S3 (id prístupového kľúča (access key id) a tajný prístupový kľúč (secret access key))." + s3_use_iam_profile: 'Použiť rolu AWS EC2 IAM na načítanie kľúčov. POZNÁMKA: zapnutie prepíše nastavený "s3 prístupový kľúč id (s3 access key id)" a "s3 tajný prístupový kľúč (s3 secret access key)".' + s3_upload_bucket: "Amazon S3 bucket name, do ktorého budú nahraté súbory. POZOR: musí byť malými písmenami, žiadne bodky a žiadne podtržítka." + s3_access_key_id: "Amazon S3 access key id, ktoré bude použité pre nahrávanie obrázkov." + s3_secret_access_key: "Amazon S3 secret access key, ktorý bude použitý pre nahrávanie obrázkov." + s3_region: "Amazon S3 region name, ktorý bude použitý pre nahrávanie súborov." + s3_cdn_url: "CDN URL ktoré sa použije na všetky dáta v s3 (napríklad https://cdn.niekde.com). VAROVANIE: po zmene tohoto nastavenia musíte použiť príkaz rebake na všetky staré príspevky." + avatar_sizes: "Zoznam automaticky generovaných veľkostí avatarov." + external_system_avatars_enabled: "Použiť externú avatar službu." + external_system_avatars_url: "URL externej avatar služby. Dostupné náhrady sú {username} {first_letter} {color} {size}" + default_opengraph_image_url: "URL štandardného opengraph obrázku." + enable_flash_video_onebox: "Povolenia vkladania odkazov na swf a flv (Adobe Flash) v onebox. VAROVANIE: môže vniesť bezpečnostné riziká." + default_invitee_trust_level: "Predvolená úroveň dôvery (0-4) pre pozvaných používateľov." + default_trust_level: "Východzí stupeň dôvery (0-4) pre všekých nových užívateľov. UPOZORNENIE: Zmena nastavenia Vás môže vystaviť riziku spamovania. " + tl1_requires_topics_entered: "Koľko tém musí nový užívateľ zadať pred povýšením na stupeň dôvery 1." + tl1_requires_read_posts: "Koľko príspevkov musí nový užívateľ prečítať pred povýšením na stupeň dôvery 1." + tl1_requires_time_spent_mins: "Koľko minút musí nový užívateľ čítať príspevky pred povýšením na stupeň dôvery 1." + tl2_requires_topics_entered: "Koľko tém musí nový užívateľ zadať pred povýšením na stupeň dôvery 2." + tl2_requires_read_posts: "Koľko príspevkov musí nový užívateľ prečítať pred povýšením na stupeň dôvery 2." + tl2_requires_time_spent_mins: "Koľko minút musí nový užívateľ čítať príspevky pred povýšením na stupeň dôvery 2." + tl2_requires_days_visited: "Koľko dní musí nový užívateľ navštevovať stránku pred povýšením na stupeň dôvery 2." + tl2_requires_likes_received: "Koľko \"Páči sa\" musí nový užívateľ dostať pred povýšením na stupeň dôvery 2." + tl2_requires_likes_given: "Koľko \"Páči sa\" musí nový užívateľ rozdať pred povýšením na stupeň dôvery 2." + tl2_requires_topic_reply_count: "Na koľko tém musí nový užívateľ odpovedať pred povýšením na stupeň dôvery 2." + tl3_requires_days_visited: "Minimálny počet navštevných dní, ktoré musí mať užívateľ za posledných 100 dní potrebných pre kvalifikáciu na dosiahnute 3 levelu dôvery. (0 až 100)" + tl3_requires_topics_replied_to: "Minimálny počet tém, na ktoré musí mať užívateľ odpovedať za posledných 100 dní potrebných pre kvalifikáciu na dosiahnute 3 levelu dôvery. (0 a viac)" + tl3_requires_topics_viewed: "Percento tém ktoré musí mať užívateľ pozreté, za posledných 100 dní pre kvalifikáciu na dosiahnute 3 levelu dôvery. (0 až 100)" + tl3_requires_posts_read: "Percento príspevkov, ktoré musí mať užívateľ pozreté za posledných 100 dní pre kvalifikáciu na dosiahnute 3 levelu dôvery. (0 až 100)" + tl3_requires_topics_viewed_all_time: "Minimálny počet všetkých tém, ktoré musí mať užívateľ pozreté pre kvalifikáciu na dosiahnute 3 levelu dôvery. " + tl3_requires_posts_read_all_time: "Minimálny počet všetkých príspevkov, ktoré musí mať užívateľ prečítané pre kvalifikáciu na 3 level dôvery. " + tl3_promotion_min_duration: "MInimálny počet dní po pridelení 3 levelu dôvery než môže byť užívateľ degradovaný späť na 3 level dôvery." + tl3_requires_likes_given: "Minimálny počet \"Páči sa\", ktoré musia byť udelené za posledných 100 dní pre kvalifikáciu na dosiahnutie 3 levelu dôvery. " + tl3_requires_likes_received: "Minimálny počet \"Páči sa\", ktoré musia byť prijaté za posledných 100 dní pre kvalifikáciu na dosiahnutie 3 levelu dôvery. " + tl3_links_no_follow: "Neodstraňujte rel = nofollow z odkazov pridaných užívateľmi s úrovňou dôveryhodnosti 3 ." + min_trust_to_create_topic: "MInimálna úroveň dôvery na vytvorenie novej témy." + min_trust_to_edit_wiki_post: "MInimálna úroveň dôvery na úpravu wiki príspevku." + min_trust_to_send_messages: "MInimálna úroveň dôvery na vytvorenie novej osobnej správy." + newuser_max_links: "Koľko odkazov môže nový používateľ pridať do príspevku." + newuser_max_images: "Koľko obrázkov môže nový používateľ pridať do príspevku." + newuser_max_attachments: "Koľko príloh môže nový používateľ pridať do príspevku." + newuser_max_mentions_per_post: "Maximálny počet notifikácií typu @meno môže nový užívateľ použiť v príspevku." + newuser_max_replies_per_topic: "Maximálny počet odpovedí ktoré môže nový používateľ pridať do jednej témy pokiaľ na ne niekto neodpovie." + max_mentions_per_post: "Maximálný počet notifikácií typu @meno, ktoré môže ktokoľvek použiť v rámci jedného príspevku." + max_users_notified_per_group_mention: "Maximálny počet používateľov, ktorý môžu dostať notifikácie, v prípade notifikovania celej skupiny (ak sa dosiahne maximum, žiadne ďalšie notifikácie nebudú zaslané)" + create_thumbnails: "Vytvor náhľad a okraje pre obrázoky, ktoré sú príliš veľké aby sa zmestili do príspevku." + title_max_word_length: "Maximálna povolená dĺžka slov v názve témy, v znakoch." + allow_uppercase_posts: "Povoliť kapitálky v názve témy, alebo tele príspevku." + search: + types: + category: 'Kategórie' + user: 'Používatelia' + login: + admin_not_allowed_from_ip_address: "Nie je možné prihlásenie ako admin z tejto IP adresy." + system_messages: + welcome_user: + subject_template: "Vitajte na %{site_name}!" + welcome_invite: + subject_template: "Vitajte na %{site_name}!" + subject_re: "Re:" + subject_pm: "[PM]" + user_notifications: + previous_discussion: "Prechádzajúce odpovede" + unsubscribe: + title: "Odhlásiť" + user_replied: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + %{context} + + --- + %{respond_instructions} + user_quoted: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + %{context} + + --- + %{respond_instructions} + user_mentioned: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + %{context} + + --- + %{respond_instructions} + user_group_mentioned: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + %{context} + + --- + %{respond_instructions} + user_posted: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + %{context} + + --- + %{respond_instructions} + user_posted_pm: + subject_template: "[%{site_name}] [PM] %{topic_title}" + text_body_template: | + %{message} + + %{context} + + --- + %{respond_instructions} + digest: + top_topics: "Populárne príspevky" + other_new_topics: "Populárne témy" + click_here: "kliknite tu" + set_password: + subject_template: "[%{site_name}] Nastaviť heslo" + admin_login: + subject_template: "[%{site_name}] Prihlásenie" + account_created: + subject_template: "[%{site_name}] Váš nový účet" + authorize_email: + subject_template: "[%{site_name}] Potvrďte Vašu novú email adresu" + signup_after_approval: + subject_template: "Váš účet bol schválený na %{site_name}!" + signup: + subject_template: "[%{site_name}] Potvrďte Váš nový účet" + page_not_found: + title: "Stránka ktorú požadujete neexistuje alebo je súkromná." + popular_topics: "Populárne" + recent_topics: "Nedávne" + see_more: "Viac" + search_title: "Prehľadať tieto stránky" + search_google: "Google" + terms_of_service: + title: "Podmienky používania" + signup_form_message: 'Prečítal som si Podmienky používania a súhlasím s nimi.' + upload: + pasted_image_filename: "Vložený obrázok" + file_missing: "Ľutujeme, ale musíte poskytnúť súbor na nahranie." + email_log: + anonymous_user: "Používateľ je anonymný" + seen_recently: "Používateľ bol nedávno online" + already_read: "používateľ si už prečítal tento príspevok" + message_blank: "správa je prázdna" + body_blank: "telo je prázdne" + color_schemes: + base_theme_name: "Základ" + about: "O stránke" + guidelines: "Pravidlá" + privacy: "Súkromie" + edit_this_page: "Upraviť túto stránku" + csv_export: + boolean_yes: "Áno" + boolean_no: "Nie" + guidelines_topic: + title: "FAQ/Pravidlá" + body: | + + + ## [Toto je miesto pre civilizovanú diskusiu](#civilized) + + Prosíme majte k tejto diskusii rešpekt podobný ako k verejnému parku. My, sme tiež zdieľaný zdroj — miesto na zdieľanie znalostí, vedomostí a záujmov pomocou nepretržitej diskusie. + + Toto nie sú jednoznačné pravidlá, skôr návod ľudskému úsudku naše komunity. Použite tieto pravidlá na udržanie prehľadnú a svetlého miesta pre civilizovanú verejnú diskusiu. + + + + ## [Vylepšujte diskusiu](#improve) + + Pomôžte nám tvoriť toto skvelé miesto akýmkoľvek snahou o zlepšenie diskuzie, stačí drobnosť. Ak nie ste si istý že Váš príspevok posúva diskuziu, premyslite si čo chcete povedať a skúste neskôr. + + Diskutované témy sú nám blízke a chceme, aby ste sa správali tak, akoby Vám boli blízke tiež. Rešpektujte témy a diskutujúcich dokonca i v prípade, ak nesúhlasíte s tým, čo bolo povedané. + + Jedným zo spôsobov ako vylepšiť diskusiu je objavenie tej ktoré už prebieha. Prosíme, venujte trochu času zoznámeniu sa s témami predtým, ako začnete odpovedať alebo začínať svoje vlastné a budete mať vačšiu šancu stretnúť tých, ktorý s Vami zdieľajú záujmy. + + + + ## [Buďte príjemný i keď nesúhlasíte](#agreeable) + + Môžete mať túžbu odpovedať na niečo s čím nesúhlasíte. To je v poriadku. Ale pamätajte _kritizujte myšienky a nie ľudí_. Prosíme, vyvarujte sa: + + * Posmeškom. + * Útokom na konkrétnu osobu. + * Odpovediam na tón príspevku miesto skutočného obsahu. + * Impulzívnym reakciám. + + Namiesto toho poskytnite logické proti argumenty, ktoré zlepšia konverzáciu. + + + + ## [Vaša účasť sa počíta](#participate) + + Konverzácie, ktoré tu máme nastavujú tón pre všetkých. Pomôžte nám ovplyvniť budúcnosť tejto komunity zapojením sa do diskusií ktoré z tohoto fóra robia zaujímavé miesto a vyhýbaním sa tým ktoré to nerobia. + + Discourse poskytuje nástroje, ktoré komunite spoločne umožňujú identifikovať najlepšie (a najhoršie) príspevky, záložky, páči sa mi, značky, odpovede, úpravy a podobne. Použite ich na vylepšenie Vašej skúsenosti i skúsenosti kohokoľvek iného. + + Poďme náš park zanechať v lepšom stave než v akom sme ho našli. + + + + ## [Ak vidíte problém označte ho](#flag-problems) + + Moderátori majú špeciálnu autoritu, sú zodpovedný za fórum. Ale Vy tiež. S Vašou pomocou sa moderátori môžu stať sprostredkovateľmi a nie iba školníkmi alebo políciou. + + Ak vidíte nevhodné chovanie, neodpovedajte. Reakciou naň ho posiľujete, míňate svoju energiu a strácate čas nás všetkých. _Oba ho označte_. Ak sa zíde viačej značiek, budú prijaté opatrenia, či už automatické alebo zásahom moderátora. + + Na správu komunity majú moderátori vyhradené práva na odstránenie akéhokoľvek obsahu a používateľského účtu kedukoľvek z akéhokoľvek dôvodu. Moderátori žiadnym spôsobom nekontrolujú príspevky, moderátori a vlastníci stránky nepreberajú žiadku zodpovednosť za obsah publikovaný komunitou. + + + + ## [Buďte civilizovaný](#be-civil) + + Nič nesabotuje zdravú diskusiu tak ako hrubosť: + + * Buďte zdvorilí, Neuverejňujte nič, čo by múdra osoba považovala za urážlivé, znevažujúce alebo nenávistné. + * Udržujte poriadok. Neuverejnujte nič nemravné alebo explicitne sexuálne. + * Rešpektujte iných. Neprenasledujte alebo nezarmucujte ostatných, nenapodobnujte ľudí, alebo nezverejňujte ich súkromné informácie. + * Rešpektujte naše fórum. Neuverejňujte spam alebo iným spôsobom nevandalizujte fórum. + + Toto nie sú jednoznačné pravidlá s presnými definiciami — vyhnite sa čo i len _náznaku_ akejkoľvek z týchto vecí. Ak nie ste si istý, spýtajte sa, či by Váš príspevok bol zverejnený na hlavnej strane New York Times. + + Toto je verejné fórum a diskusie sú indexované vyhľadávačmi. Udržujte jazyk, odkazy a obrázky bezpečné pre rodinu a priateĺov. + + + + ## [Udržujte poriadok](#keep-tidy) + + Vynaložte snahu dať veci na správne miesto, tak môžeme stráviť viac času diskusiou a menej udržiavaním poriadku. Takže: + + * Nezačínajte tému v nesprávnej kategórií. + * Neuverejňujte rovnaké veci vo viacetých témach. + * Nepublikujte príspevky bez obsahu. + * Nepresmerujte tému zmenou uprostred. + * Nepodpisujte sa — každý príspevok má pripojený odkaz na Váš profil. + + Namiesto poslania “+1” or “Súhlasím”, radšej použite tlačítko Páči sa mi. Namiesto radikálnej zmeny témy v priebehu diskusie radšej odpovedzte spojenou témou. + + + + ## [Uverejňujte iba svoj obsah](#stealing) + + Bez dovolenia nemôžete publikovať nič digitálne, čo patrí niekomu inému. Nemôžete publikovať popis, odkazy alebo metódy na kradnutie niekoho intelektuálneho vlastníctva (softvéru, videa, audia, obrázkov), alebo porušovať akýkoľvek iný zákon. + + + + ## [Založené na Vás](#power) + + Táto stránka je prevádzkovaná [miestnym priateľským personálom](/about) a *Vami*, komunitou. Ak máte akékoľvek ďalšie otázky ako by tu veci mali fungovať, založte novú tému v [kategórií spätná väzba](/c/site-feedback) a poďme diskutovať! Ak ste narazili na kritický alebo urgentný problém ktorý nemôže byť riešený pomocou tém v meta alebo príznakom, kontaktujte nás pomocou [stránky personálu](/about). + + + + ## [Podmienky](#tos) + + Áno, právnický žargón je nudný, ale musíme chrániť seba – tým následne i Vás a vaše dáta – voči nepriateľským ľuďom. Máme [Podmienky](/tos) popisujúce Vaše (a naše ) správanie a práva súvisiace s obsahom, súkromým a legislatívou. K používaniu tejto služby musíte dodžiavať naše [Pravidlá](/tos). + tos_topic: + title: "Podmienky používania" + badges: + long_descriptions: + basic: | + Tento odznak je udelený ak dosiahnete stupeň dóvery 1. Ďakujeme za Váš čas , prečítanie niekoľkých tém a zoznámenie sa s tým o čom je naša komunita. Odstránili sme obmedzenia pre nového používateľa a odteraz máte všetky základné práva, ako je zasielanie osobných správ, značkovanie, wiki úpravy a možnosť publikovať obrázky a viaceré odkazy. + admin_login: + success: "Email odoslaný" + error: "Chyba !" + email_input: "Email administrátora" + submit_button: "Poslať email" + discourse_hub: + access_token_problem: "Povedzte administrátorovi: Prosím aktualizujte nastavenia stránky, aby obsahovali správny discourse_org_access_key." + performance_report: + initial_post_raw: Táto téma obsahuje denné reporty rýchlosti Vašich stránok. + initial_topic_title: Reporty rýchlosti stránok + time: + <<: *datetime_formats + activemodel: + errors: + <<: *errors diff --git a/config/locales/server.sq.yml b/config/locales/server.sq.yml index 168a873471..efb40284ab 100644 --- a/config/locales/server.sq.yml +++ b/config/locales/server.sq.yml @@ -776,9 +776,9 @@ sq: 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." - s3_access_key_id: "The Amazon S3 access key id that will be used to upload images." - s3_secret_access_key: "The Amazon S3 secret access key that will be used to upload images." - s3_region: "The Amazon S3 region name that will be used to upload images." + s3_access_key_id: "The Amazon S3 access key id that will be used to upload images and/or backups." + s3_secret_access_key: "The Amazon S3 secret access key that will be used for uploading." + s3_region: "The Amazon S3 region name that will be used for uploading." s3_cdn_url: "The CDN URL to use for all s3 assets (for example: https://cdn.somewhere.com). WARNING: after changing this setting you must rebake all old posts." avatar_sizes: "List of automatically generated avatar sizes." enable_flash_video_onebox: "Enable embedding of swf and flv (Adobe Flash) links in oneboxes. WARNING: may introduce security risks." diff --git a/config/locales/server.tr_TR.yml b/config/locales/server.tr_TR.yml index ade4f9f735..d4ef2f17f3 100644 --- a/config/locales/server.tr_TR.yml +++ b/config/locales/server.tr_TR.yml @@ -86,6 +86,8 @@ tr_TR: not_found: "İstenilen URL ya da kaynak bulunamadı." invalid_access: "İstenilen kaynağı görüntüleyebilmeniz için izniniz yok." read_only_mode_enabled: "Bu site sadece okuma modunda. Etkileşimler etkisizleştirildi." + reading_time: "Okuma Zamanı" + likes: "Beğeniler" too_many_replies: other: "Üzgünüz, yeni kullanıcılar geçici olarak aynı konu içinde sadece %{count} cevap ile sınırlılar. " embed: @@ -817,7 +819,6 @@ tr_TR: tl2_requires_likes_received: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce alması gereken beğeni sayısı." tl2_requires_likes_given: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce vermesi gereken beğeni sayısı." tl2_requires_topic_reply_count: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce cevaplaması gereken konu sayısı." - tl3_requires_days_visited: "Bir kullanıcının güven seviyesi 3'e yükseltimeye hak kazanması için, son 100 günde siteyi ziyaret etmesi gereken en az gün sayısı. (0 - 100)" tl3_requires_topics_replied_to: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için son 100 günde cevap yazması gereken en az konu sayısı. (0 ya da daha fazla)" tl3_requires_topics_viewed: "Bir kulanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde oluşturulmuş konulardan görüntülemesi gereken yüzde oranı. (0 ile 100 arası)" tl3_requires_posts_read: "Bir kulanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde oluşturulmuş gönderilerden görüntülemesi gereken yüzde oranı. (0 ile 100 arası)" diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 04e26d6da2..d9506c7f1d 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -842,7 +842,7 @@ zh_CN: tl2_requires_likes_received: "一个初级用户升级到信任等级2所需要获得的赞数。" tl2_requires_likes_given: "一个初级用户升级到信任等级2所需要给出的赞数。" tl2_requires_topic_reply_count: "一个初级用户升级到信任等级2所需要回复的主题数量。" - tl3_requires_days_visited: "在最近 100 天内升至信任等级3所需的访问站点的天数。(0到100)" + tl3_time_period: "3级信任等级时间期" tl3_requires_topics_replied_to: "在最近 100 天内升至信任等级3所需的回复主题的最小数量。(0或更高)" tl3_requires_topics_viewed: "在最近 100 天内升至信任等级3所需的创建主题的百分比。(0到100)" tl3_requires_posts_read: "在最近 100 天内升信任等级3所需的创建帖子的百分比。(0到100)" @@ -917,6 +917,7 @@ zh_CN: disable_emails: "禁止 Discourse 发送任何邮件" strip_images_from_short_emails: "从邮件中除去小于 2800 比特的图片" short_email_length: "短邮件地址长度(以比特作为单位)" + display_name_on_email_from: "在Email栏显示全名" pop3_polling_enabled: "轮询 POP3 收取邮件回复。" pop3_polling_ssl: "连接至 POP3 服务器时使用 SSL。(推荐)" pop3_polling_period_mins: "查询用于邮件的 POP3 账户的间隔(以分钟计)。注意:需要重新启动。" @@ -1575,6 +1576,7 @@ zh_CN: unsubscribe: title: "取消订阅" description: "不再对这些邮件感兴趣?没问题!点击下面按钮来立即取消订阅:" + reply_by_email: "回复邮件或访问[visit the topic](%{base_url}%{url}),以回复。" visit_link_to_respond: "回复,[visit the topic](%{base_url}%{url})。" posted_by: "%{username}发表于%{post_date}" user_invited_to_private_message_pm: @@ -1592,6 +1594,8 @@ zh_CN: > %{site_title} -- %{site_description} 请访问 %{base_url}%{url} 来查看该主题。 + user_invited_to_private_message_pm_staged: + subject_template: "[%{site_name}] %{username} 邀请你加入消息交流:'%{topic_title}'" user_invited_to_topic: subject_template: "[%{site_name}] %{username} 邀请你至主题:'%{topic_title}'" text_body_template: |2 @@ -1609,16 +1613,70 @@ zh_CN: 请访问 %{base_url}%{url} 来查看该主题。 user_replied: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_quoted: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_mentioned: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_group_mentioned: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_posted: subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_posted_pm: subject_template: "[%{site_name}] [PM] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} user_posted_pm_staged: subject_template: "%{optional_re}%{topic_title}" text_body_template: |2 diff --git a/config/routes.rb b/config/routes.rb index 518fab5caa..daf6ba56b3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -119,9 +119,10 @@ Discourse::Application.routes.draw do resources :email, constraints: AdminConstraint.new do collection do post "test" - get "all" get "sent" get "skipped" + get "received" + get "rejected" get "preview-digest" => "email#preview_digest" post "handle_mail" end @@ -254,7 +255,7 @@ Discourse::Application.routes.draw do get "guidelines" => "static#show", id: "guidelines", as: 'guidelines' get "tos" => "static#show", id: "tos", as: 'tos' get "privacy" => "static#show", id: "privacy", as: 'privacy' - get "signup" => "list#latest" + get "signup" => "static#show", id: "signup" get "login-preferences" => "static#show", id: "login" get "users/admin-login" => "users#admin_login" @@ -301,16 +302,19 @@ Discourse::Application.routes.draw do put "users/:username/preferences/card-badge" => "users#update_card_badge", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/staff-info" => "users#staff_info", constraints: {username: USERNAME_ROUTE_FORMAT} + get "users/:username/summary" => "users#summary", constraints: {username: USERNAME_ROUTE_FORMAT} + get "users/:username/invited" => "users#invited", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/invited_count" => "users#invited_count", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/invited/:filter" => "users#invited", constraints: {username: USERNAME_ROUTE_FORMAT} post "users/action/send_activation_email" => "users#send_activation_email" + get "users/:username/summary" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/activity" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/activity/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/badges" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/notifications" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/notifications/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} - get "users/:username/pending" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + get "users/:username/activity/pending" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} delete "users/:username" => "users#destroy", constraints: {username: USERNAME_ROUTE_FORMAT} # The external_id constraint is to allow periods to be used in the value without becoming part of the format. ie: foo.bar.json get "users/by-external/:external_id" => "users#show", constraints: {external_id: /[^\/]+/} diff --git a/config/site_settings.yml b/config/site_settings.yml index ff7f6173c6..b4d132179f 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -105,7 +105,7 @@ basic: post_menu: client: true type: list - default: 'like-count|like|share|flag|edit|bookmark|delete|admin|reply' + default: 'like-count|like|share|flag|edit|bookmark|wiki|delete|admin|reply' choices: - like-count - like @@ -116,10 +116,11 @@ basic: - bookmark - admin - reply + - wiki post_menu_hidden_items: client: true type: list - default: 'bookmark|edit|delete|admin' + default: 'bookmark|edit|wiki|delete|admin' choices: - like - edit @@ -129,6 +130,7 @@ basic: - bookmark - admin - reply + - wiki share_links: client: true type: list @@ -325,9 +327,11 @@ users: show_email_on_profile: client: true default: false - email_token_valid_hours: 24 + email_token_valid_hours: + default: 24 + min: 1 email_token_grace_period_hours: 0 - purge_unactivated_users_grace_period_days: 7 + purge_unactivated_users_grace_period_days: 14 public_user_custom_fields: type: list default: '' @@ -531,6 +535,10 @@ email: short_email_length: 2800 display_name_on_email_from: default: true + unsubscribe_via_email: + default: true + unsubscribe_via_email_footer: + default: false files: max_image_size_kb: 3072 @@ -623,6 +631,9 @@ trust: min_trust_to_edit_wiki_post: default: 1 enum: 'TrustLevelSetting' + min_trust_to_allow_self_wiki: + default: 3 + enum: 'TrustLevelSetting' min_trust_to_send_messages: default: 1 enum: 'TrustLevelSetting' @@ -792,6 +803,15 @@ developer: verbose_localization: default: false client: true + top_topics_formula_log_views_multiplier: + default: 2 + min: 0 + top_topics_formula_first_post_likes_multiplier: + default: 0.5 + min: 0 + top_topics_formula_least_likes_per_post_multiplier: + default: 3 + min: 0 migrate_to_new_scheme: hidden: true default: false @@ -799,8 +819,6 @@ developer: default: 500 client: true hidden: true - allow_staged_accounts: - default: false embedding: feed_polling_enabled: diff --git a/db/migrate/20151109124147_drop_group_managers.rb b/db/migrate/20151109124147_drop_group_managers.rb index 2e197cfb59..41e07141c1 100644 --- a/db/migrate/20151109124147_drop_group_managers.rb +++ b/db/migrate/20151109124147_drop_group_managers.rb @@ -10,6 +10,6 @@ class DropGroupManagers < ActiveRecord::Migration end def down - raise ActiveRecord::IrriversableMigration + raise ActiveRecord::IrreversibleMigration end end diff --git a/db/migrate/20160110053003_archive_system_messages_with_no_replies.rb b/db/migrate/20160110053003_archive_system_messages_with_no_replies.rb new file mode 100644 index 0000000000..da9b1d71c3 --- /dev/null +++ b/db/migrate/20160110053003_archive_system_messages_with_no_replies.rb @@ -0,0 +1,24 @@ +class ArchiveSystemMessagesWithNoReplies < ActiveRecord::Migration + def up + # backdate archival of system messages send on behalf of site_contact_user + execute <]+)>', 'im'), '') + , users.email + , array_to_string(regexp_matches(array_to_string(regexp_matches(posts.raw_email, '^to:.+$', 'im'), ''), '[^<\s"''(]+@[^>\s"'')]+'), '') + , topics.title + FROM posts + JOIN topics ON posts.topic_id = topics.id + JOIN users ON posts.user_id = users.id + WHERE posts.user_id IS NOT NULL + AND posts.topic_id IS NOT NULL + AND posts.via_email = 't' + AND posts.raw_email ~* 'Message-Id' + ORDER BY posts.id; + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/discourse.sublime-project b/discourse.sublime-project index 20854867ba..6ec5f18c58 100644 --- a/discourse.sublime-project +++ b/discourse.sublime-project @@ -16,9 +16,7 @@ { "path": "script" }, { "path": "spec" }, { "path": "vendor" }, - { "path": "test", - "folder_exclude_patterns": ["fixtures"] - } + { "path": "test" }, ], "settings": { diff --git a/docs/INSTALL-cloud.md b/docs/INSTALL-cloud.md index d3387f1df5..0aa946fa93 100644 --- a/docs/INSTALL-cloud.md +++ b/docs/INSTALL-cloud.md @@ -124,8 +124,7 @@ Commands: stop: Stop a running container restart: Restart a container destroy: Stop and remove a container - enter: Use nsenter to enter a container - ssh: Start a bash shell in a running container + enter: Enter a container using docker exec logs: Docker logs for container bootstrap: Bootstrap a container for the config based on a template rebuild: Rebuild a container (destroy old, bootstrap, start new) diff --git a/lib/backup_restore/backuper.rb b/lib/backup_restore/backuper.rb index 0b3b26656d..b55d735907 100644 --- a/lib/backup_restore/backuper.rb +++ b/lib/backup_restore/backuper.rb @@ -258,7 +258,7 @@ module BackupRestore end log "Gzipping archive..." - `gzip #{tar_filename}` + `gzip -5 #{tar_filename}` end def after_create_hook diff --git a/lib/disk_space.rb b/lib/disk_space.rb index 18f4d1a9fa..7a97348430 100644 --- a/lib/disk_space.rb +++ b/lib/disk_space.rb @@ -57,7 +57,7 @@ class DiskSpace protected def self.free(path) - `df -Pk #{path} | awk 'NR==2 {print $4 * 1024;}'`.to_i + `df -Pk #{path} | awk 'NR==2 {print $4;}'`.to_i * 1024 end def self.used(path) diff --git a/lib/email/message_builder.rb b/lib/email/message_builder.rb index b1cf2bd575..f5d1ed58a9 100644 --- a/lib/email/message_builder.rb +++ b/lib/email/message_builder.rb @@ -63,8 +63,16 @@ module Email if @opts[:add_unsubscribe_link] unsubscribe_link = PrettyText.cook(I18n.t('unsubscribe_link', template_args), sanitize: false).html_safe html_override.gsub!("%{unsubscribe_link}", unsubscribe_link) + + if SiteSetting.unsubscribe_via_email_footer && @opts[:add_unsubscribe_via_email_link] + unsubscribe_via_email_link = PrettyText.cook(I18n.t('unsubscribe_via_email_link', hostname: Discourse.current_hostname), sanitize: false).html_safe + html_override.gsub!("%{unsubscribe_via_email_link}", unsubscribe_via_email_link) + else + html_override.gsub!("%{unsubscribe_via_email_link}", "") + end else html_override.gsub!("%{unsubscribe_link}", "") + html_override.gsub!("%{unsubscribe_via_email_link}", "") end header_instructions = @template_args[:header_instructions] @@ -103,6 +111,9 @@ module Email if @opts[:add_unsubscribe_link] body << "\n" body << I18n.t('unsubscribe_link', template_args) + if SiteSetting.unsubscribe_via_email_footer && @opts[:add_unsubscribe_via_email_link] + body << I18n.t('unsubscribe_via_email_link', hostname: Discourse.current_hostname) + end end body diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index bb2ad455ac..65788fe005 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -1,144 +1,201 @@ -require_dependency 'new_post_manager' -require_dependency 'email/html_cleaner' -require_dependency 'post_action_creator' +require_dependency "new_post_manager" +require_dependency "post_action_creator" +require_dependency "email/html_cleaner" module Email class Receiver - include ActionView::Helpers::NumberHelper + class ProcessingError < StandardError; end + class EmptyEmailError < ProcessingError; end + class NoMessageIdError < ProcessingError; end + class AutoGeneratedEmailError < ProcessingError; end + class NoBodyDetectedError < ProcessingError; end + class InactiveUserError < ProcessingError; end + class BadDestinationAddress < ProcessingError; end + class StrangersNotAllowedError < ProcessingError; end + class InsufficientTrustLevelError < ProcessingError; end + class ReplyUserNotMatchingError < ProcessingError; end + class TopicNotFoundError < ProcessingError; end + class TopicClosedError < ProcessingError; end + class InvalidPost < ProcessingError; end + class InvalidPostAction < ProcessingError; end - class ProcessingError < StandardError; end - class EmailUnparsableError < ProcessingError; end - class EmptyEmailError < ProcessingError; end - class UserNotFoundError < ProcessingError; end - class UserNotSufficientTrustLevelError < ProcessingError; end - class BadDestinationAddress < ProcessingError; end - class TopicNotFoundError < ProcessingError; end - class TopicClosedError < ProcessingError; end - class AutoGeneratedEmailError < ProcessingError; end - class EmailLogNotFound < ProcessingError; end - class InvalidPost < ProcessingError; end - class ReplyUserNotFoundError < ProcessingError; end - class ReplyUserNotMatchingError < ProcessingError; end - class InactiveUserError < ProcessingError; end - class InvalidPostAction < ProcessingError; end - - attr_reader :body, :email_log - - def initialize(raw, opts=nil) - @raw = raw - @opts = opts || {} + def initialize(mail_string) + raise EmptyEmailError if mail_string.blank? + @raw_email = mail_string + @mail = Mail.new(@raw_email) + raise NoMessageIdError if @mail.message_id.blank? end def process - raise EmptyEmailError if @raw.blank? - - @message = Mail.new(@raw) - - raise AutoGeneratedEmailError if @message.header.to_s =~ /auto-(replied|generated)/ - - @body = parse_body(@message) - - # 'smtp_envelope_to' is a combination of: to, cc and bcc fields - # prioriziting the `:reply` types - dest_infos = @message.smtp_envelope_to - .map { |to_address| check_address(to_address) } - .compact - .sort do |a, b| - if a[:type] == :reply && b[:type] != :reply - 1 - elsif a[:type] != :reply && b[:type] == :reply - -1 - else - 0 - end - end - - raise BadDestinationAddress if dest_infos.empty? - - from = @message[:from].address_list.addresses.first - user_email = from.address - user_name = from.display_name - - user = User.find_by_email(user_email) - raise InactiveUserError if user.present? && !user.active && !user.staged - - # TODO: take advantage of all the "TO"s - dest_info = dest_infos[0] - case dest_info[:type] - when :group - group = dest_info[:obj] - - if user.blank? - if SiteSetting.allow_staged_accounts - user = create_staged_account(user_email, user_name) - else - wrap_body_in_quote(user_email) - user = Discourse.system_user - end - end - - create_new_topic(user, archetype: Archetype.private_message, target_group_names: [group.name]) - when :category - category = dest_info[:obj] - - if user.blank? && category.email_in_allow_strangers - if SiteSetting.allow_staged_accounts - user = create_staged_account(user_email) - else - wrap_body_in_quote(user_email) - user = Discourse.system_user - end - end - - raise UserNotFoundError if user.blank? - raise UserNotSufficientTrustLevelError.new(user) unless category.email_in_allow_strangers || user.has_trust_level?(TrustLevel[SiteSetting.email_in_min_trust.to_i]) - - create_new_topic(user, category: category.id) - when :reply - @email_log = dest_info[:obj] - - raise EmailLogNotFound if @email_log.blank? - raise TopicNotFoundError if Topic.find_by_id(@email_log.topic_id).nil? - raise TopicClosedError if Topic.find_by_id(@email_log.topic_id).closed? - raise ReplyUserNotFoundError if user.blank? - raise ReplyUserNotMatchingError if @email_log.user_id != user.id - - if post_action_type = post_action_for(@body) - create_post_action(@email_log, post_action_type) - else - create_reply(@email_log) - end - end - - rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e - raise EmailUnparsableError.new(e) + @incoming_email = find_or_create_incoming_email + process_internal + rescue => e + @incoming_email.update_columns(error: e.to_s) + raise end - def create_staged_account(email, name=nil) - User.create( - email: email, - username: UserNameSuggester.suggest(name.presence || email), - name: name.presence || User.suggest_name(email), - staged: true, - ) + def find_or_create_incoming_email + IncomingEmail.find_or_create_by(message_id: @mail.message_id) do |incoming_email| + incoming_email.raw = @raw_email + incoming_email.subject = @mail.subject + incoming_email.from_address = @mail.from.first.downcase + incoming_email.to_addresses = @mail.to.map(&:downcase).join(";") if @mail.to.present? + incoming_email.cc_addresses = @mail.cc.map(&:downcase).join(";") if @mail.cc.present? + end + end + + def process_internal + raise AutoGeneratedEmailError if is_auto_generated? + + body = select_body || "" + + raise NoBodyDetectedError if body.blank? && !@mail.has_attachments? + + user = find_or_create_user(from) + + @incoming_email.update_columns(user_id: user.id) + + raise InactiveUserError if !user.active && !user.staged + + if action = subscription_action_for(body, @mail.subject) + message = SubscriptionMailer.send(action, user) + Email::Sender.new(message, :subscription).send + elsif post = find_related_post + create_reply(user: user, raw: body, post: post, topic: post.topic) + else + destination = destinations.first + + raise BadDestinationAddress if destination.blank? + + case destination[:type] + when :group + group = destination[:obj] + create_topic(user: user, raw: body, title: @mail.subject, archetype: Archetype.private_message, target_group_names: [group.name], skip_validations: true) + when :category + category = destination[:obj] + + raise StrangersNotAllowedError if user.staged? && !category.email_in_allow_strangers + raise InsufficientTrustLevelError if !user.has_trust_level?(SiteSetting.email_in_min_trust) + + create_topic(user: user, raw: body, title: @mail.subject, category: category.id) + when :reply + email_log = destination[:obj] + + raise ReplyUserNotMatchingError if email_log.user_id != user.id + + create_reply(user: user, raw: body, post: email_log.post, topic: email_log.post.topic) + end + end + end + + def is_auto_generated? + @mail.return_path.blank? || + @mail[:precedence].to_s[/list|junk|bulk|auto_reply/] || + @mail.header.to_s[/auto-(submitted|replied|generated)/] + end + + def select_body + text = nil + html = nil + + if @mail.multipart? + text = fix_charset(@mail.text_part) + html = fix_charset(@mail.html_part) + elsif @mail.content_type.to_s["text/html"] + html = fix_charset(@mail) + else + text = fix_charset(@mail) + end + + # prefer text over html + if text.present? + text_encoding = text.encoding + text = DiscourseEmailParser.parse_reply(text) + text = try_to_encode(text, text_encoding) + return text if text.present? + end + + # clean the html if that's all we've got + if html.present? + html_encoding = html.encoding + html = Email::HtmlCleaner.new(html).output_html + html = DiscourseEmailParser.parse_reply(html) + html = try_to_encode(html, html_encoding) + return html if html.present? + end + end + + def fix_charset(mail_part) + return nil if mail_part.blank? || mail_part.body.blank? + + string = mail_part.body.to_s + + # TODO (use charlock_holmes to properly detect encoding) + + # 1) use the charset provided + if mail_part.charset.present? + fixed = try_to_encode(string, mail_part.charset) + return fixed if fixed.present? + end + + # 2) default to UTF-8 + try_to_encode(string, "UTF-8") + end + + def try_to_encode(string, encoding) + string.encode("UTF-8", encoding) + rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError + nil + end + + def from + @from ||= @mail[:from].address_list.addresses.first + end + + def find_or_create_user(address_field) + # decode the address field + address_field.decoded + # extract email and name + email = address_field.address.downcase + name = address_field.display_name.try(:to_s) + username = UserNameSuggester.sanitize_username(name) if name.present? + + User.find_or_create_by(email: email) do |user| + user.username = UserNameSuggester.suggest(username.presence || email) + user.name = name.presence || User.suggest_name(email) + user.staged = true + end + end + + def destinations + [ @mail.destinations, + [@mail[:x_forwarded_to]].flatten.compact.map(&:decoded), + [@mail[:delivered_to]].flatten.compact.map(&:decoded), + ].flatten + .select(&:present?) + .uniq + .lazy + .map { |d| check_address(d) } + .drop_while(&:blank?) end def check_address(address) # only check for a group/category when 'email_in' is enabled if SiteSetting.email_in group = Group.find_by_email(address) - return { address: address, type: :group, obj: group } if group + return { type: :group, obj: group } if group category = Category.find_by_email(address) - return { address: address, type: :category, obj: category } if category + return { type: :category, obj: category } if category end + # reply match = reply_by_email_address_regex.match(address) if match && match[1].present? email_log = EmailLog.for(match[1]) - return { address: address, type: :reply, obj: email_log } + return { type: :reply, obj: email_log } if email_log end end @@ -147,173 +204,103 @@ module Email .gsub(Regexp.escape("%{reply_key}"), "([[:xdigit:]]{32})") end - def parse_body(message) - body = select_body(message) - encoding = body.encoding - raise EmptyEmailError if body.strip.blank? - - body = discourse_email_trimmer(body) - raise EmptyEmailError if body.strip.blank? - - body = DiscourseEmailParser.parse_reply(body) - raise EmptyEmailError if body.strip.blank? - - body.force_encoding(encoding).encode("UTF-8") + def group_incoming_emails_regex + @group_incoming_emails_regex ||= Regexp.union Group.pluck(:incoming_email).select(&:present?).uniq end - def select_body(message) - html = nil - - if message.multipart? - text = fix_charset message.text_part - # prefer text over html - return text if text - html = fix_charset message.html_part - elsif message.content_type =~ /text\/html/ - html = fix_charset message - end - - if html - body = HtmlCleaner.new(html).output_html - else - body = fix_charset message - end - - return body if @opts[:skip_sanity_check] - - # Certain trigger phrases that means we didn't parse correctly - if body =~ /Content\-Type\:/ || body =~ /multipart\/alternative/ || body =~ /text\/plain/ - raise EmptyEmailError - end - - body + def category_email_in_regex + @category_email_in_regex ||= Regexp.union Category.pluck(:email_in).select(&:present?).uniq end - # Force encoding to UTF-8 on a Mail::Message or Mail::Part - def fix_charset(object) - return nil if object.nil? + def find_related_post + message_ids = [@mail.in_reply_to, extract_references] + message_ids.flatten! + message_ids.select!(&:present?) + message_ids.uniq! + return if message_ids.empty? - if object.charset - object.body.decoded.force_encoding(object.charset.gsub(/utf8/i, "UTF-8")).encode("UTF-8").to_s - else - object.body.to_s + Post.where(id: IncomingEmail.where(message_id: message_ids).select(:post_id)) + .order(created_at: :desc) + .first + end + + def extract_references + if Array === @mail.references + @mail.references + elsif @mail.references.present? + @mail.references.split(/[\s,]/).map { |r| r.sub(/^$/, "") } end - rescue - nil end - REPLYING_HEADER_LABELS = ['From', 'Sent', 'To', 'Subject', 'In-Reply-To', 'Cc', 'Bcc', 'Date'] - REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |lbl| "#{lbl}:" }) - - def line_is_quote?(l) - l =~ /\A\s*\-{3,80}\s*\z/ || - l =~ Regexp.new("\\A\\s*" + I18n.t('user_notifications.previous_discussion') + "\\s*\\Z") || - (l =~ /via #{SiteSetting.title}(.*)\:$/) || - # This one might be controversial but so many reply lines have years, times and end with a colon. - # Let's try it and see how well it works. - (l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) || - (l =~ /On [\w, ]+\d+.*wrote:/) + def likes + @likes ||= Set.new ["+1", I18n.t('post_action_types.like.title').downcase] end - def discourse_email_trimmer(body) - lines = body.scrub.lines.to_a - range_start = 0 - range_end = 0 - - # If we started with a quote, skip it - lines.each_with_index do |l, idx| - break unless line_is_quote?(l) or l =~ /^>/ or l.blank? - range_start = idx + 1 + def subscription_action_for(body, subject) + return unless SiteSetting.unsubscribe_via_email + if ([subject, body].compact.map(&:to_s).map(&:downcase) & ['unsubscribe']).any? + :confirm_unsubscribe end - - lines[range_start..-1].each_with_index do |l, idx| - break if line_is_quote?(l) - - # Headers on subsequent lines - break if (0..2).all? { |off| lines[idx+off] =~ REPLYING_HEADER_REGEX } - # Headers on the same line - break if REPLYING_HEADER_LABELS.count { |lbl| l.include? lbl } >= 3 - range_end = range_start + idx - end - - lines[range_start..range_end].join.strip - end - - private - - def wrap_body_in_quote(user_email) - @body = "[quote=\"#{user_email}\"]\n#{@body}\n[/quote]" - end - - def create_post_action(email_log, type) - PostActionCreator.new(email_log.user, email_log.post).perform(type) - rescue Discourse::InvalidAccess, PostAction::AlreadyActed => e - raise InvalidPostAction.new(e) end def post_action_for(body) - if ['+1', I18n.t('post_action_types.like.title').downcase].include? body.downcase + if likes.include?(body.strip.downcase) PostActionType.types[:like] end end - def create_reply(email_log) - create_post_with_attachments(email_log.user, - raw: @body, - topic_id: email_log.topic_id, - reply_to_post_number: email_log.post.post_number) + def create_topic(options={}) + create_post_with_attachments(options) end - def create_new_topic(user, topic_options={}) - topic_options[:raw] = @body - topic_options[:title] = @message.subject + def create_reply(options={}) + raise TopicNotFoundError if options[:topic].nil? || options[:topic].trashed? + raise TopicClosedError if options[:topic].closed? - result = create_post_with_attachments(user, topic_options) - topic_id = result.post.present? ? result.post.topic_id : nil - - EmailLog.create( - email_type: "topic_via_incoming_email", - to_address: user.email, - topic_id: topic_id, - user_id: user.id, - ) - - result + if post_action_type = post_action_for(options[:raw]) + create_post_action(options[:user], options[:post], post_action_type) + else + options[:topic_id] = options[:post].try(:topic_id) + options[:reply_to_post_number] = options[:post].try(:post_number) + create_post_with_attachments(options) + end end - def create_post_with_attachments(user, post_options={}) - options = { - cooking_options: { traditional_markdown_linebreaks: true }, - }.merge(post_options) - - raw = options[:raw] + def create_post_action(user, post, type) + PostActionCreator.new(user, post).perform(type) + rescue PostAction::AlreadyActed + # it's cool, don't care + rescue Discourse::InvalidAccess => e + raise InvalidPostAction.new(e) + end + def create_post_with_attachments(options={}) # deal with attachments - @message.attachments.each do |attachment| + @mail.attachments.each do |attachment| tmp = Tempfile.new("discourse-email-attachment") begin # read attachment File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded } # create the upload for the user - upload = Upload.create_for(user.id, tmp, attachment.filename, tmp.size) + upload = Upload.create_for(options[:user].id, tmp, attachment.filename, tmp.size) if upload && upload.errors.empty? # try to inline images - if attachment.content_type.start_with?("image/") - if raw =~ /\[image: Inline image \d+\]/ - raw.sub!(/\[image: Inline image \d+\]/, attachment_markdown(upload)) - next - end + if attachment.content_type.start_with?("image/") && options[:raw][/\[image: .+ \d+\]/] + options[:raw].sub!(/\[image: .+ \d+\]/, attachment_markdown(upload)) + else + options[:raw] << "\n#{attachment_markdown(upload)}\n" end - raw << "\n#{attachment_markdown(upload)}\n" end ensure - tmp.close! + tmp.try(:close!) rescue nil end end - options[:raw] = raw + post_options = { + cooking_options: { traditional_markdown_linebreaks: true }, + }.merge(options) - create_post(user, options) + create_post(post_options) end def attachment_markdown(upload) @@ -324,20 +311,58 @@ module Email end end - def create_post(user, options) - # Mark the reply as incoming via email + def create_post(options={}) options[:via_email] = true - options[:raw_email] = @raw + options[:raw_email] = @raw_email - manager = NewPostManager.new(user, options) + # ensure posts aren't created in the future + options[:created_at] = [@mail.date, DateTime.now].min + + manager = NewPostManager.new(options[:user], options) result = manager.perform - if result.errors.present? - raise InvalidPost, result.errors.full_messages.join("\n") - end + raise InvalidPost, result.errors.full_messages.join("\n") if result.errors.any? - result + if result.post + @incoming_email.update_columns(topic_id: result.post.topic_id, post_id: result.post.id) + if result.post.topic && result.post.topic.private_message? + add_other_addresses(result.post.topic, options[:user]) + end + end + end + + def add_other_addresses(topic, sender) + %i(to cc bcc).each do |d| + if @mail[d] && @mail[d].address_list && @mail[d].address_list.addresses + @mail[d].address_list.addresses.each do |address_field| + begin + email = address_field.address.downcase + if should_invite?(email) + user = find_or_create_user(address_field) + if can_invite?(topic, user) + topic.topic_allowed_users.create!(user_id: user.id) + topic.add_small_action(sender, "invited_user", user.username) + end + end + rescue ActiveRecord::RecordInvalid + # don't care if user already allowed + end + end + end + end + end + + def should_invite?(email) + email !~ reply_by_email_address_regex && + email !~ group_incoming_emails_regex && + email !~ category_email_in_regex + end + + def can_invite?(topic, user) + !topic.topic_allowed_users.where(user_id: user.id).exists? && + !topic.topic_allowed_groups.where("group_id IN (SELECT group_id FROM group_users WHERE user_id = ?)", user.id).exists? end end + end diff --git a/lib/email/styles.rb b/lib/email/styles.rb index cfd3f674db..67447a10a0 100644 --- a/lib/email/styles.rb +++ b/lib/email/styles.rb @@ -89,6 +89,7 @@ module Email style('hr', 'background-color: #ddd; height: 1px; border: 1px;') style('.rtl', 'direction: rtl;') style('td.body', 'padding-top:5px;', colspan: "2") + style('.whisper td.body', 'font-style: italic; color: #9c9c9c;') correct_first_body_margin correct_footer_style reset_tables @@ -213,7 +214,7 @@ module Email element.css('a').each do |inner| # we want the first footer link to be specially highlighted as IMPORTANT if footernum == 0 and linknum == 0 - inner['style'] = "background-color:#006699;color:#fff;padding:4px 6px;" + inner['style'] = "background-color: #006699; color:#ffffff; border-top: 4px solid #006699; border-right: 6px solid #006699; border-bottom: 4px solid #006699; border-left: 6px solid #006699; display: inline-block;" else inner['style'] = "color:#666;" end diff --git a/lib/excerpt_parser.rb b/lib/excerpt_parser.rb index 95e9cf1de7..acfa88b88c 100644 --- a/lib/excerpt_parser.rb +++ b/lib/excerpt_parser.rb @@ -13,7 +13,8 @@ class ExcerptParser < Nokogiri::XML::SAX::Document @text_entities = options[:text_entities] == true @markdown_images = options[:markdown_images] == true @keep_newlines = options[:keep_newlines] == true - @keep_emojis = options[:keep_emojis] == true + @keep_emoji_images = options[:keep_emoji_images] == true + @keep_emoji_codes = options[:keep_emoji_codes] == true @start_excerpt = false end @@ -48,11 +49,14 @@ class ExcerptParser < Nokogiri::XML::SAX::Document def start_element(name, attributes=[]) case name when "img" - attributes = Hash[*attributes.flatten] - if @keep_emojis && attributes["class"] == 'emoji' - return include_tag(name, attributes) + if attributes["class"] == 'emoji' + if @keep_emoji_images + return include_tag(name, attributes) + elsif @keep_emoji_codes + return characters(attributes["alt"]) + end end # If include_images is set, include the image in markdown diff --git a/lib/freedom_patches/arel_patch.rb b/lib/freedom_patches/arel_patch.rb deleted file mode 100644 index 4c0ae309b1..0000000000 --- a/lib/freedom_patches/arel_patch.rb +++ /dev/null @@ -1,6 +0,0 @@ -# https://github.com/rails/arel/pull/206 -class Arel::Table - def hash - @name.hash - end -end diff --git a/lib/guardian.rb b/lib/guardian.rb index 78dcee9cd5..92c09f2ed2 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -256,7 +256,9 @@ class Guardian @user.username == SiteSetting.site_contact_username || @user == Discourse.system_user) && # Can't send PMs to suspended users - (is_staff? || target.is_a?(Group) || !target.suspended?) + (is_staff? || target.is_a?(Group) || !target.suspended?) && + # Blocked users can only send PM to staff + (!@user.blocked? || target.staff?) end def can_see_emails? diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index 2933cf194c..722c017485 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -73,7 +73,7 @@ module PostGuardian # Creating Method def can_create_post?(parent) - !SpamRule::AutoBlock.block?(@user) && ( + (!SpamRule::AutoBlock.block?(@user) || (!!parent.try(:private_message?) && parent.allowed_users.include?(@user))) && ( !parent || !parent.category || Category.post_create_allowed(self).where(:id => parent.category.id).count == 1 @@ -173,8 +173,9 @@ module PostGuardian is_admin? end - def can_wiki? - is_staff? || @user.has_trust_level?(TrustLevel[4]) + def can_wiki?(post) + return false unless authenticated? + is_staff? || @user.has_trust_level?(TrustLevel[4]) || (@user.has_trust_level?(SiteSetting.min_trust_to_allow_self_wiki) && is_my_own?(post)) end def can_change_post_type? diff --git a/lib/onebox/engine/discourse_local_onebox.rb b/lib/onebox/engine/discourse_local_onebox.rb index 5fd48971b7..cac4503d6b 100644 --- a/lib/onebox/engine/discourse_local_onebox.rb +++ b/lib/onebox/engine/discourse_local_onebox.rb @@ -63,7 +63,7 @@ module Onebox topic = post.topic slug = Slug.for(topic.title) - excerpt = post.excerpt(SiteSetting.post_onebox_maxlength) + excerpt = post.excerpt(SiteSetting.post_onebox_maxlength, { keep_emoji_codes: true }) excerpt.gsub!("\n"," ") # hack to make it render for now excerpt.gsub!("[/quote]", "[quote]") diff --git a/lib/post_creator.rb b/lib/post_creator.rb index 4d2b158b87..f7151442b2 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -172,7 +172,7 @@ class PostCreator def self.before_create_tasks(post) set_reply_info(post) - post.word_count = post.raw.scan(/\w+/).size + post.word_count = post.raw.scan(/[[:word:]]+/).size post.post_number ||= Topic.next_post_number(post.topic_id, post.reply_to_post_number.present?) cooking_options = post.cooking_options || {} @@ -262,10 +262,14 @@ class PostCreator end def ensure_in_allowed_users - return unless @topic.private_message? + return unless @topic.private_message? && @topic.id unless @topic.topic_allowed_users.where(user_id: @user.id).exists? - @topic.topic_allowed_users.create!(user_id: @user.id) + unless @topic.topic_allowed_groups.where('group_id IN ( + SELECT group_id FROM group_users where user_id = ? + )',@user.id).exists? + @topic.topic_allowed_users.create!(user_id: @user.id) + end end end @@ -350,8 +354,10 @@ class PostCreator @user.user_stat.first_post_created_at = @post.created_at end - @user.user_stat.post_count += 1 - @user.user_stat.topic_count += 1 if @post.is_first_post? + unless @post.topic.private_message? + @user.user_stat.post_count += 1 + @user.user_stat.topic_count += 1 if @post.is_first_post? + end # We don't count replies to your own topics if !@opts[:import_mode] && @user.id != @topic.user_id diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index e081864201..873be66e0d 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -229,7 +229,7 @@ class PostRevisor end @post.last_editor_id = @editor.id - @post.word_count = @fields[:raw].scan(/\w+/).size if @fields.has_key?(:raw) + @post.word_count = @fields[:raw].scan(/[[:word:]]+/).size if @fields.has_key?(:raw) @post.self_edits += 1 if self_edit? remove_flags_and_unhide_post diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index 126f3358c2..d952373ab8 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -49,9 +49,8 @@ module PrettyText end def category_hashtag_lookup(category_slug) - if category_slug - category = Category.find_by_slug(category_slug) - return ['category', category.url_with_id] if category + if category = Category.query_from_hashtag_slug(category_slug) + ['category', category.url_with_id] else nil end @@ -74,7 +73,7 @@ module PrettyText @ctx_init = Mutex.new def self.mention_matcher - Regexp.new("(\@[a-zA-Z0-9_]{#{User.username_length.begin},#{User.username_length.end}})") + Regexp.new("\\W@(\\w{#{SiteSetting.min_username_length},#{SiteSetting.max_username_length}})\\b") end def self.app_root diff --git a/lib/scheduler/manager.rb b/lib/scheduler/manager.rb index 2f2b42d4a9..78d1de7fa4 100644 --- a/lib/scheduler/manager.rb +++ b/lib/scheduler/manager.rb @@ -10,7 +10,6 @@ module Scheduler class Manager attr_accessor :random_ratio, :redis - class Runner def initialize(manager) @mutex = Mutex.new @@ -157,7 +156,6 @@ module Scheduler lock do schedule_info(klass).schedule! end - end def remove(klass) @@ -203,8 +201,8 @@ module Scheduler def schedule_next_job(hostname=nil) (key, due), _ = redis.zrange Manager.queue_key(hostname), 0, 0, withscores: true - return unless key + if due.to_i <= Time.now.to_i klass = get_klass(key) unless klass diff --git a/lib/sidekiq/pausable.rb b/lib/sidekiq/pausable.rb index 3ef356c831..c7cd9b5928 100644 --- a/lib/sidekiq/pausable.rb +++ b/lib/sidekiq/pausable.rb @@ -68,15 +68,13 @@ end # server middleware that will reschedule work whenever Sidekiq is paused class Sidekiq::Pausable - attr_reader :delay - def initialize(delay = 5.seconds) @delay = delay end def call(worker, msg, queue) if Sidekiq.paused? - worker.class.perform_in(delay, *msg['args']) + worker.class.perform_in(@delay, *msg['args']) else yield end diff --git a/lib/system_message.rb b/lib/system_message.rb index 5e4a82df8d..d38b5113f5 100644 --- a/lib/system_message.rb +++ b/lib/system_message.rb @@ -23,12 +23,22 @@ class SystemMessage title = I18n.t("system_messages.#{type}.subject_template", params) raw = I18n.t("system_messages.#{type}.text_body_template", params) - PostCreator.create(Discourse.site_contact_user, + creator = PostCreator.new(Discourse.site_contact_user, title: title, raw: raw, archetype: Archetype.private_message, target_usernames: @recipient.username, - subtype: TopicSubtype.system_message) + subtype: TopicSubtype.system_message, + skip_validations: true) + + post = creator.create + if creator.errors.present? + raise StandardError, creator.errors.to_s + end + + UserArchivedMessage.create!(user: Discourse.site_contact_user, topic: post.topic) + + post end def create_from_system_user(type, params = {}) diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake index 030e4db2ee..040f70e2b2 100644 --- a/lib/tasks/assets.rake +++ b/lib/tasks/assets.rake @@ -122,7 +122,7 @@ def compress_ruby(from,to) data = File.read("#{assets_path}/#{from}") uglified, map = Uglifier.new(comments: :none, - screw_ie8: false, + screw_ie8: true, source_filename: File.basename(from), output_filename: File.basename(to) ) @@ -135,7 +135,7 @@ end def gzip(path) STDERR.puts "gzip #{path}" - STDERR.puts `gzip -f -c -9 #{path} > #{path}.gz` + STDERR.puts `gzip -f -c -7 #{path} > #{path}.gz` end def compress(from,to) diff --git a/lib/tasks/emails.rake b/lib/tasks/emails.rake new file mode 100644 index 0000000000..1ab9c71d35 --- /dev/null +++ b/lib/tasks/emails.rake @@ -0,0 +1,56 @@ +def process_popmail(popmail) + begin + mail_string = popmail.pop + Email::Receiver.new(mail_string).process + rescue + putc "!" + else + putc "." + end +end + +desc "use this task to import a mailbox into Disourse" +task "emails:import" => :environment do + begin + unless SiteSetting.email_in + puts "ERROR: you should enable the 'email_in' site setting before running this task" + exit(1) + end + + address = ENV["ADDRESS"].presence || "pop.gmail.com" + port = (ENV["PORT"].presence || 995).to_i + ssl = (ENV["SSL"].presence || "1") == "1" + username = ENV["USERNAME"].presence + password = ENV["PASSWORD"].presence + + if username.blank? + puts "ERROR: expecting USERNAME= rake emails:import" + exit(2) + elsif password.blank? + puts "ERROR: expecting PASSWORD= rake emails:import" + exit(3) + end + + RateLimiter.disable + + mails_left = 1 + pop3 = Net::POP3.new(address, port) + pop3.enable_ssl if ssl + + while mails_left > 0 + pop3.start(username, password) do |pop| + pop.delete_all do |p| + process_popmail(p) + end + mails_left = pop.n_mails + end + end + + puts "Done" + rescue Net::POPAuthenticationError + puts "AUTH EXCEPTION: please make sure your credentials are correct." + exit(10) + ensure + RateLimiter.enable + end +end diff --git a/lib/topic_creator.rb b/lib/topic_creator.rb index c4791a2f7e..9ca18af011 100644 --- a/lib/topic_creator.rb +++ b/lib/topic_creator.rb @@ -193,6 +193,6 @@ class TopicCreator end def check_can_send_permission!(topic, obj) - rollback_with!(topic, :cant_send_pm) unless @guardian.can_send_private_message?(obj) + rollback_with!(topic, :cant_send_pm) unless @opts[:skip_validations] || @guardian.can_send_private_message?(obj) end end diff --git a/lib/topic_query.rb b/lib/topic_query.rb index cde31fffd9..c837c429c8 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -199,7 +199,6 @@ class TopicQuery end def prioritize_pinned_topics(topics, options) - pinned_clause = options[:category_id] ? "topics.category_id = #{options[:category_id].to_i} AND" : "pinned_globally AND " pinned_clause << " pinned_at IS NOT NULL " if @user diff --git a/lib/topic_view.rb b/lib/topic_view.rb index 7db989d45e..9072610f51 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -16,6 +16,10 @@ class TopicView 20 end + def self.default_post_custom_fields + @default_post_custom_fields ||= ["action_code_who"] + end + def self.post_custom_fields_whitelisters @post_custom_fields_whitelisters ||= Set.new end @@ -25,7 +29,8 @@ class TopicView end def self.whitelisted_post_custom_fields(user) - post_custom_fields_whitelisters.map { |w| w.call(user) }.flatten.uniq + wpcf = default_post_custom_fields + post_custom_fields_whitelisters.map { |w| w.call(user) } + wpcf.flatten.uniq end def initialize(topic_id, user=nil, options={}) diff --git a/lib/user_name_suggester.rb b/lib/user_name_suggester.rb index 4819a3f879..934035ed6c 100644 --- a/lib/user_name_suggester.rb +++ b/lib/user_name_suggester.rb @@ -35,10 +35,15 @@ module UserNameSuggester def self.sanitize_username(name) name = ActiveSupport::Inflector.transliterate(name) - name = name.gsub(/^[^[:alnum:]]+|\W+$/, "") - .gsub(/\W+/, "_") - .gsub(/^\_+/, '') - .gsub(/[\-_\.]{2,}/, "_") + # 1. replace characters that aren't allowed with '_' + name.gsub!(UsernameValidator::CONFUSING_EXTENSIONS, "_") + name.gsub!(/[^\w.-]/, "_") + # 2. removes unallowed leading characters + name.gsub!(/^\W+/, "") + # 3. removes unallowed trailing characters + name.gsub!(/[^A-Za-z0-9]+$/, "") + # 4. unify special characters + name.gsub!(/[-_.]{2,}/, "_") name end diff --git a/lib/version.rb b/lib/version.rb index d89abb8fe9..0e13f016b7 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -5,7 +5,7 @@ module Discourse MAJOR = 1 MINOR = 5 TINY = 0 - PRE = 'beta8' + PRE = 'beta9' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/plugins/poll/assets/javascripts/lib/even-round.js.es6 b/plugins/poll/assets/javascripts/lib/even-round.js.es6 index 3dcc57861c..0395f1f16a 100644 --- a/plugins/poll/assets/javascripts/lib/even-round.js.es6 +++ b/plugins/poll/assets/javascripts/lib/even-round.js.es6 @@ -5,11 +5,13 @@ function sumsUpTo100(percentages) { export default (percentages) => { const sumOfDecimals = Math.ceil(percentages.map(a => a % 1).reduce((a, b) => a + b)); - // compensate error by adding 1 to the first n items - for (let i = 0; i < sumOfDecimals; i++) { - percentages[i] = ++percentages[i]; - // quit early when there is a rounding issue - if (sumsUpTo100(percentages)) break; + // compensate error by adding 1 to the first n "non-zero" items + for (let i = 0, max = percentages.length; i < sumOfDecimals && i < max; i++) { + if (percentages[i] > 0) { + percentages[i] = ++percentages[i]; + // quit early when there is a rounding issue + if (sumsUpTo100(percentages)) break; + } } return percentages.map(p => Math.floor(p)); }; diff --git a/plugins/poll/config/locales/client.ro.yml b/plugins/poll/config/locales/client.ro.yml index 0176f9589d..82b8d8830a 100644 --- a/plugins/poll/config/locales/client.ro.yml +++ b/plugins/poll/config/locales/client.ro.yml @@ -8,9 +8,29 @@ ro: js: poll: + voters: + one: "participant" + few: "participanți" + other: "participanți" + total_votes: + one: "un vot" + few: "total voturi" + other: "total voturi" average_rating: "Media: %{average}." multiple: help: + at_least_min_options: + one: "Trebuie să selectați cel puțin 1 opțiune." + few: "Trebuie să selectați cel puțin %{count} opțiuni." + other: "Trebuie să selectați cel puțin %{count} opțiuni." + up_to_max_options: + one: "Puteţi alege cel mult o %{count} opţiune." + few: "Puteţi selecta cel mult 1 opţiune." + other: "Puteți selecta până la %{count} opțiuni." + x_options: + one: "Trebuie să selectați 1 opțiune." + few: "Trebuie să selectați %{count} opțiuni." + other: "Trebuie să alegeți %{count} opțiuni." between_min_and_max_options: "Puteţi alege între %{min} şi %{max} opţiuni." cast-votes: title: "Exprimaţi-vă votul" @@ -26,8 +46,8 @@ ro: label: "Deschis" confirm: "Sunteţi sigur că doriţi să deschideţi acest sondaj?" close: - title: "Închide sondaj" - label: "Închis" + title: "Închideți sondajul" + label: "Închideți" confirm: "Sunteţi sigur că vreţi să închideţi acest sondaj?" error_while_toggling_status: "A apărut o eroare în timpul schimbării stării acestui sondaj." error_while_casting_votes: "A apărut o eroare în timpul exprimării votului dvs." diff --git a/plugins/poll/config/locales/client.sk.yml b/plugins/poll/config/locales/client.sk.yml new file mode 100644 index 0000000000..2ae88650f9 --- /dev/null +++ b/plugins/poll/config/locales/client.sk.yml @@ -0,0 +1,53 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +sk: + js: + poll: + voters: + one: "volič" + few: "voliči" + other: "voliči" + total_votes: + one: "hlas celkom" + few: "hlasy celkom" + other: "hlasov celkom" + average_rating: "Priemerné hodnotenie: %{average}." + multiple: + help: + at_least_min_options: + one: "Musíte si vybrať minimálne %{count} možnosť." + few: "Musíte si vybrať minimálne %{count} možnosti." + other: "Musíte si vybrať minimálne %{count} možností." + up_to_max_options: + one: "Môžete si vybrať maximálne %{count} možnosť." + few: "Môžete si vybrať maximálne %{count} možnosti." + other: "Môžete si vybrať maximálne %{count} možností." + x_options: + one: "Musíte si vybrať %{count} možnosť." + few: "Musíte si vybrať %{count} možnosti." + other: "Musíte si vybrať %{count} možností." + between_min_and_max_options: "Môžete si vybrať medzi možnosťami %{min}%{max}." + cast-votes: + title: "Hlasovať" + label: "Hlasuj teraz!" + show-results: + title: "Zobraz výsledky hlasovania" + label: "Zobraz výsledky" + hide-results: + title: "Návrat na odovzdané hlasy" + label: "Skyť výsledky" + open: + title: "Zahájiť hlasovanie" + label: "Zahájiť" + confirm: "Ste si istý, že chcete zahájiť toto hlasovanie?" + close: + title: "Zatvoriť hlasovanie" + label: "Zatvoriť" + confirm: "Ste si istý, že chcete zatvoriť toto hlasovanie?" + error_while_toggling_status: "Pri zmene stavu hlasovania sa vyskytla chyba." + error_while_casting_votes: "Pri hlasovaní sa vyskytla chyba." diff --git a/plugins/poll/config/locales/server.sk.yml b/plugins/poll/config/locales/server.sk.yml new file mode 100644 index 0000000000..665bc70c74 --- /dev/null +++ b/plugins/poll/config/locales/server.sk.yml @@ -0,0 +1,41 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +sk: + site_settings: + poll_enabled: "Umožnit používaťeľom vytvárať hlasovania?" + poll_maximum_options: "Maximálny počet povolených možností v hlasovaní." + poll: + multiple_polls_without_name: "Existujú viaceré hlasovania bez mena. Použite atribút 'meno', aby ste hlasovanie jednoznačne rozlíšili." + multiple_polls_with_same_name: "Existujú viaceré hlasovania s rovnakým menom: %{name}. Použite atribút 'meno', aby ste hlasovanie jednoznačne rozlíšili." + default_poll_must_have_at_least_2_options: "Hlasovanie musí mať minimálne 2 možnosti." + named_poll_must_have_at_least_2_options: "Hlasovanie s názvom %{name} musí mať minimálne 2 možnosti." + default_poll_must_have_less_options: + one: "Hlasovanie musí mať menej ako jednu možnosť." + few: "Hlasovanie musí mať menej ako %{count} možnosti." + other: "Hlasovanie musí mať menej ako %{count} možnosti." + named_poll_must_have_less_options: + one: "Hlasovanie s názvom %{name} musí mať menej ako 1 možnost." + few: "Hlasovanie s názvom %{name} musí mať menej ako %{count} možnosti." + other: "Hlasovanie s názvom %{name} musí mať menej ako %{count} možností." + default_poll_must_have_different_options: "Hlasovanie musí mať rôzne možnosti." + named_poll_must_have_different_options: "Hlasovanie s názvom %{name} musí mať rôzne možnosti." + default_poll_with_multiple_choices_has_invalid_parameters: "Hlasovanie s viac možnosťami má nesprávne parametre." + named_poll_with_multiple_choices_has_invalid_parameters: "Hlasovanie s názvom %{name} s viac možnosťami má nesprávne parametre." + requires_at_least_1_valid_option: "Musite vybrať aspoň 1 platnú možnosť." + cannot_change_polls_after_5_minutes: "Po prvých 5 minútach už nemôžete pridať, odstrániť alebo premenovať hlasovanie." + op_cannot_edit_options_after_5_minutes: "Po prvých 5 minútach už nemôžete pridať alebo odstrániť možnosti hlasovania. Ak potrebujete upraviť možnosti hlasovania prosím kontaktujte moderátora." + staff_cannot_add_or_remove_options_after_5_minutes: "Po prvých 5 minútach už nemôžete pridať alebo odstrániť možnosti hlasovania. Miesto toho by ste mali uzavrieť túto tému a miesto nej vytvoriť inú." + no_polls_associated_with_this_post: "S týmto príspevkom nie sú spojené žiadne hlasovania." + no_poll_with_this_name: "S týmto príspevkom nie je spojené žiadne hlasovanie s názvom %{name}." + post_is_deleted: "Na vymazanom príspevku nie je možné vykonať." + topic_must_be_open_to_vote: "Na odovzdanie hlasu téma otvorená." + poll_must_be_open_to_vote: "Na odovzdanie hlasu musí byť hlasovanie otvorené." + topic_must_be_open_to_toggle_status: "Na zmenu stavu musí byť téma otvorená." + only_staff_or_op_can_toggle_status: "Zmeniť stav hlasovania môže iba zamestnanec alebo pôvodný prispievateľ." + email: + link_to_poll: "Kliknite na zobrazenie hlasovania." diff --git a/public/403.sk.html b/public/403.sk.html new file mode 100644 index 0000000000..eca5973106 --- /dev/null +++ b/public/403.sk.html @@ -0,0 +1,26 @@ + + +Na toto nemáte oprávnenie (403) + + + + +
    +

    403

    +

    Nemáte oprávnenie na zobrazenie týchto údajov!

    + +

    Toto bude nahradené vlastnou Discourse 403 stránkou.

    +
    + + diff --git a/public/422.sk.html b/public/422.sk.html new file mode 100644 index 0000000000..36e3684c14 --- /dev/null +++ b/public/422.sk.html @@ -0,0 +1,25 @@ + + +Požadovaná zmena bola zamietnutá (422) + + + + + +
    +

    Požadovaná zmena bola zamietnutá.

    +

    Možno ste skúšali zmeniť niečo k čomu nemáte prístup.

    +
    + + diff --git a/public/500.sk.html b/public/500.sk.html new file mode 100644 index 0000000000..625a96497d --- /dev/null +++ b/public/500.sk.html @@ -0,0 +1,12 @@ + + +Oops - Chyba 500 + + + +

    Oops

    +

    V softvéri poháňajúcom toto diskusné fórum došlo k neočakávanej chybe. Ospravedlňujeme sa za spôsobené nepríjemnosti.

    +

    Podrobnosti o chybe boli zaznamenané a vygenerovalo sa automatické upozornenie. Pozrieme sa na to.

    +

    Nie je potrebná žiadna ďalšia akcia. Ak však chyba pretrváva, môžete poskytnúť dodatočné informácie, vrátane krokov vedúcich k navodeniu chyby založením diskusnej témy v meta kategórií.

    + + diff --git a/public/503.sk.html b/public/503.sk.html new file mode 100644 index 0000000000..eae9f09226 --- /dev/null +++ b/public/503.sk.html @@ -0,0 +1,11 @@ + + +Na stránke sa práve vykonáva údržba. + + + +

    Stránka je momentálne nedostupná z dôvodu plánovanej údržby

    +

    Skúste prosím znova o niekoľko minút.

    +

    Ospravedlňujeme sa za spôsobené nepríjemnosti.

    + + diff --git a/public/javascripts/pikaday.js b/public/javascripts/pikaday.js index c0596d22d3..bc0c64474e 100644 --- a/public/javascripts/pikaday.js +++ b/public/javascripts/pikaday.js @@ -202,6 +202,9 @@ // first day of week (0: Sunday, 1: Monday etc) firstDay: 0, + // the default flag for moment's strict date parsing + formatStrict: false, + // the minimum/earliest date that can be selected minDate: null, // the maximum/latest date that can be selected @@ -230,6 +233,9 @@ // Render the month after year in the calendar title showMonthAfterYear: false, + // Render days of the calendar grid that fall in the next or previous month + showDaysInNextAndPreviousMonths: false, + // how many months are visible numberOfMonths: 1, @@ -274,10 +280,14 @@ renderDay = function(opts) { - if (opts.isEmpty) { - return '
    - - - - - - - - - -
    - - - 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/attachment.eml b/spec/fixtures/emails/attachment.eml deleted file mode 100644 index f25c3d1a44..0000000000 --- a/spec/fixtures/emails/attachment.eml +++ /dev/null @@ -1,351 +0,0 @@ -Message-ID: <51C22E52.1030509@darthvader.ca> -Date: Wed, 19 Jun 2013 18:18:58 -0400 -From: Anakin Skywalker -User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130510 Thunderbird/17.0.6 -MIME-Version: 1.0 -To: Han Solo via Death Star -Subject: Re: [Death Star] [PM] re: Regarding your post in "Site Customization - not working" -References: <51d23d33f41fb_5f4e4b35d7d60798@xwing.mail> -In-Reply-To: <51d23d33f41fb_5f4e4b35d7d60798@xwing.mail> -Content-Type: multipart/mixed; boundary=047d7b45041e19c68004eb9f3de8 - ---047d7b45041e19c68004eb9f3de8 -Content-Type: multipart/alternative; boundary=047d7b45041e19c67b04eb9f3de6 - ---047d7b45041e19c67b04eb9f3de6 -Content-Type: text/plain; charset=ISO-8859-1 - -here is an image attachment - - -On Tue, Nov 19, 2013 at 5:11 PM, Neil wrote: - -> Neil -> November 19 -> -> Actually, deleting a spammer does what it's supposed to. It does mark the -> topic as deleted. -> -> That topic has id 11002, and you're right that the user was deleted. -> -> @eviltrout Any idea why it showed up in -> suggested topics? -> -> To respond, reply to this email or visit -> http://meta.discourse.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5in your browser. -> ------------------------------ -> Previous Replies Neil -> November 19 -> -> Looks like a bug when deleting a spammer. I'll look at it. -> riking -> November 19 -> -> codinghorror: -> -> I can't even find that topic by name. -> -> In that case, I'm fairly certain someone used the 'Delete Spammer' -> function on the user, which would explain your inability to find it - it's -> gone. -> -> I'm raising this because, well, it's gone and shouldn't be showing up. And -> even if it was hanging around, it should be invisible to me, and not -> showing up in Suggested Topics. -> codinghorror -> November 19 -> -> Hmm, that's interesting -- can you have a look @eviltrout? -> I can't even find that topic by name. -> riking -> November 19 -> -> I'm one of the users who flagged this particular spam post, and it was -> promptly deleted/hidden, but it just popped up in the Suggested Topics box: -> -> Pasted image1125x220 27.7 KB -> -> -> We may want to recheck the suppression on these. -> ------------------------------ -> -> To respond, reply to this email or visit -> http://meta.discourse.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5in your browser. -> -> To unsubscribe from these emails, visit your user preferences -> . -> - ---047d7b45041e19c67b04eb9f3de6 -Content-Type: text/html; charset=ISO-8859-1 -Content-Transfer-Encoding: quoted-printable - -
    here is an image attachment


    On Tue, Nov 19, 2013 at 5:11 PM, Neil = -<info@discourse.org> wrote:
    -
    - - - - - - - - -
    - - - Neil<= -/a>
    -No= -vember 19 -
    -

    Actually, deleting a spammer does what it's s= -upposed to. It does mark the topic as deleted.

    - -

    That topic has id 11002, and you're right tha= -t the user was deleted.

    - -

    @eviltrout Any idea why it showed up in suggested topics?

    -
    -
    -

    To respond, reply to this email or visit http://meta.discourse.org/t/spam-post-pops-back= --up-in-suggested-topics/11005/5 in your browser.

    - -
    -
    -

    Previous Replies

    - - - - - - - - - -
    - - - Neil<= -/a>
    -No= -vember 19 -

    Looks= - like a bug when deleting a spammer. I'll look at it.

    - - - - - - - - -
    - - - rik= -ing
    -No= -vember 19 -
    -

    -
    -codinghorror:
    -

    I can't even find that topic by n= -ame.

    - -

    In that case, I'm fairly certain someone used= - the 'Delete Spammer' function on the user, which would explain you= -r inability to find it - it's gone.

    - -

    I'm raising this because, well, it's gone= - and shouldn't be showing up. And even if it was hanging around, it sho= -uld be invisible to me, and not showing up in Suggested Topics.

    -
    - - - - - - - - -
    - - - codinghorror
    -No= -vember 19 -

    Hmm, = -that's interesting -- can you have a look @eviltrout? I can't even find that topic by= - name.

    -
    - - - - - - - - -
    - - - rik= -ing
    -No= -vember 19 -
    -

    I'm one of the users who flagged this particu= -lar spam post, and it was promptly deleted/hidden, but it just popped up in= - the Suggested Topics box:

    - -

    - - -

    We may want to recheck the suppression on these.<= -/p> -

    -
    -
    -

    To respond, reply to this email or visit http://meta.discourse.org/t/spam-post-pops-back-up-= -in-suggested-topics/11005/5 in your browser.

    - -
    -
    -

    To unsubscribe from these emails, visit your user pre= -ferences.

    -
    -

    - ---047d7b45041e19c67b04eb9f3de6-- ---047d7b45041e19c68004eb9f3de8 -Content-Type: image/png; name="bricks.png" -Content-Disposition: attachment; filename="bricks.png" -Content-Transfer-Encoding: base64 -X-Attachment-Id: f_ho8uteve0 - -iVBORw0KGgoAAAANSUhEUgAAASEAAAB+CAIAAADk0DDaAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ -bWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp -bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6 -eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz -NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo -dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw -dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv -IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS -ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD -cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNl -SUQ9InhtcC5paWQ6MDYxQjcyOUUzMDM1MTFFM0JFRTFBOTQ1RUY4QUU4MDIiIHhtcE1NOkRvY3Vt -ZW50SUQ9InhtcC5kaWQ6MDYxQjcyOUYzMDM1MTFFM0JFRTFBOTQ1RUY4QUU4MDIiPiA8eG1wTU06 -RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNjFCNzI5QzMwMzUxMUUzQkVF -MUE5NDVFRjhBRTgwMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNjFCNzI5RDMwMzUxMUUz -QkVFMUE5NDVFRjhBRTgwMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1w -bWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pm2fyz0AAAyISURBVHja7F2/i11FFL6rL12aBdlGRDCF -EQmEbVJtChfSJJDGRkgZBBsVUhgQ7NSkCKiFVUr/AUGbhW1MlWaJBAkWVsFmG0HshMT7duJk9szc -uefOjzPn3vd9xfL2/bh35rtnznfOuXNnth7c/6ID2Lh261vO13669wm4SsZ7H3396gmePXu2OkH/ -Yr4Mv4IrCgAYY8Am4vnz51sn8EVsXth68P7eYq7Kj4cP3H+v79fq2tWDX/u/d25/7n/08/3PzIvb -u3vLs3sxhh/vXrOvb9/50v1o77W/X340B5IXMsbsta931eN24I6uRQ4wd3SJkUwYnqkLQ6wIAHWx -gn/Nx3ff3Ov/njvbWFcXFibESdZw3aFjAKBDx46Ofk/42e7u2/3f4G8jH5XF07+O7es3tnfSThps -beRNA/PRmd1rxrlGkMNDf8a2DLskJzOcRrJ5/7czb/Z/fzk8qESyjBlDxwBAZT4WGd/1/CtxLcaz -ZiLYWvOmezpXxMQwxKQYwzIkK2S4LMnQMQCorGMm4C7irhp6nUzPHfSs7un6176jffT4cULSuGkM -+1mWq5b2jDlqRpJGdWNsFqNLxqrstfejxEzjA8l+LBpkm+DihQucmodyhhErAoCOmkcvx4t3xsG4 -RaZEbgOeZZNMwu9u+P7EkkiGjgGADh2LDH21Ehd0Wvz82E/VqiLOsE6JizM8iWSZ2n0TM4aOAYAO -HUvzDW0RbNhoa8ld0Ui2cPHCBU7JCwz7DDPzMc7dEf0krzqAESsCIBmxIgAsN1YUSKMlU/9N8KxD -+b02hvn3oDWbMXQMADZMxyIOtUnqn1lTVluuWAzD+kmGjgGAeh2rcfMu7YDCd8PFKss10qRkhiV1 -Q7J2X8+Mpe+PuRcpOCEgp59lOWry1GCRfgVJdg+STFRxK4yTLFnzSCCZacaIFQGgcqworP5FvKlM -YFBwvuIGkszscny+Ij9WlJ/SyY+8oGMAUFnHZIa+tpnjRVrCn68o0PFFMqztdGkkQ8cAQCQfI87A -X0lGlZtJW4gmx9Mnr5lDGuyenawko82RJ5OczLCflfHriprNGDoGAOL5WD/63QX7tU1USV7oq2FH -yKmNf7Ukq2V4RiRrNuOVf+3LLsSrYXTlI7l2TwLUSgvxahhdNRhmRuMkQNVmxogVAUBEx9yh7zoz -STc2quwFHVKTdX7sc/WtGB4NUMsynH/AqXOpdJoxdAwAKuuYGwc3SXj0TL2NIFi7n+pfWyU8c2E4 -p6mazRg6BgAi+ZgbRIpF2yRDKIhRuRhdMJTTu8v7VyY9dpFAcr4nJhlCDZKTGS4uNTrNOLBXLeeU -beuhVefm8Q8bma/4ZLt756+XRyMkM0+xVJL5x4zU7nuGe1iSNZsxYkUAqBwrBoXbf1Os2F3E/cg0 -NeJle//qPyRLGkZiLcmJ83MhOVK7d8OEIZKDZizTcjwHDQCCOubGtfHbdpNSVc6+UuYL1/f33JRx -RttwRfKxvv2mI4Ze63pHb4zySWZuj9Z/gTDczWc3uUik4OqSJZljxt2UslYRM4aOAUBlHTNDPxJN -EmEx/wbfPBUcHy2fu4iXPeiOu22aPAyR7Eu3JTl4ITaH4QjWDPfYZjHc1oxXoxHL0DtumyIJJWl3 -8CHF0QZkJqxFbsj4ExE4aw0Er32wj3GG48Unsg4Zh2T/dHb05iy9mBnnE5KZ8xWHSK5nxt3Ak6DB -IyNWBADBmkca+P6YfPr08JS8vFD/kGc69au8+dTJP89xz5kkT2J4iGTTfkuy35jgNZJkOIdkYTM+ -RdeYGUPHAKCyjpV1BqXwZHs8nxGG8VsHR+u/r1+6sX7rdM3jj3/WPvjc2eNgR9QyrJPkqzfvBqtK -PcmEYf0kQ8cAoC62rr4FEibAKJipd333zb2hr/m+FphKskWwrjgjhrfO7+zgonLw8ae3bPRirrp5 -Jz7YgEm4vH/F/df4srmTjFgRAKBjOkQsqGAG7kdAvoL18jU0h2aOJEPHAKCyjn34wY2hz9xomIC4 -GfPNtJ1FyW8jJ423Ie7/cnpnvmzyAZIw1OtdPsnkXO4P7Uf1Llm9CxE5sqtywdSulJlN6iB0DAAq -69j3X92ND8rgqHXdwNBvR4e7+4W4L0xug+/5gv5s9Mi9g/QVLO5TM3vHVJtI++OdCrJX8JKNXohS -ZjYaZYiZ2dChoGMAUFnHvn1LS13xzM1bHH/z7kOU79Lx26XxLOXf+7jdl8uwa8Ar5sqsZPk482R1 -WRyZS3vSxKAo//nwh/Xfrru9u7e8a+Mv0FeD5O7EQ5GRZvHz/c/s600guR7Dj1DzAIDGsaIrbmlb -0dnFRsh+oaOyaX5lHa3RXNe/Xul2hprK34+UNM9/TY5vWz70acdexMZvedpWdP6pO/aq8f3X/Mjc -kkwY7pK21Q0yk8Yh+UICwzkkB814lGTXjKFjANBOx0aH/qjX4bwZdADGy3b/zwR1J1nb54KC25O6 -p+AIy1TxKQjOhmCZDEdIdlMyc+vWkuw+eRXcZdeehcleK5KVmDF0DABa6FiRhZzS3K3rAOzjDEwd -S0gXJ31UFkUWckpzt1bH3MlHHB3LbJiwrNUz4yE7CZrxKnigIovIBkkcqjQII3KB6117clXESN4o -hmXM2C/hRPaMR6wIAHWxqudaguVO88I9XbKaNdn3tZJrzyfZDxDs6XLihSb7vupk2Cd51IxNKA4d -AwARHavtVIJ3ISO5L//hnFn4VwGSh4gdKuEwl7kGyUN3g4LTGAjMcwDQMQCYrY4FnUHatKNMzGhP -syLulkNy2hPQINl9zTTjVUFC+UUIX3+rItIYzZYxtEULM34jYSRB8cVn5kiyjBkjVgQAlbFicHzz -d/4cFVmxJb40xzYJJPOfAzDL18ksDKqW5GQznhQrQscAYBE1j0ggG4QpemJV0KokAzIMQ8cAQETH -ZCo/m+BZI0wG64StGC5eu1fCsCozNjf6Vw2z0syqveZyRXA4geTaI00bw5h3DwAS4I6xzH24p6IX -2UlLw+e4wxpdS3ColVqiRHOKd61neC4kQ8cAoC64tfuykW6TJ3OL9MtNA4LTmpKJKp5LzJpkwrB7 -kByimpgxdAwAdOhYmqcfQsE5wcLzTYtIlgDD3dic4EnPjwncdQiS3LCqWcSM8Rw0AMxTx4r4Hm3P -QQs7coEuT5oNLNB3bc+/FGmJmRK4GurtpNPkEBRcbIQfyQTT4rRF8MWMLG21n2SSgwxPmncfNImE -RfAlh7EeM0asCACCsWLyQl8NJT64IHvaJh1imfTCSNY230qPGWMuFQAI6ljD9UAjixhPjZ5rLHat -wb+2YtgnucZi10rChBokG0DHAEBExwoO+iJF2KlPQFv/2mRaLTMJLEVykzK3q2AaSK7KcCmSTz0/ -1hCZlWX3h/LBmJ45gVMZnjTPw/62STA2X5IRKwKAYKxYMK0cXcuS4wKnPgnvxmnMXuS74d5pTT1v -keoIh+FRkgUYztc6PwgXq44UNGPoGACI61i9uXlFDvtk+8VmquZoZCIP8xRti871ihalGO66XJKb -l/U1mDHmUgFAIx2LD9Pm/qn3r/5DsqRtJNqWLHYX8fFtSXYVjJDsNoykJWIkFwlVZBg2+dhK59CP -VJbtmDEhjWsW8fs2/HoAcx3z/gvX9/dIUj6XLYLiDNuOWJI5DE+qB3BINp8Skme3CRNiRQAQjxWV -46A77jwFi0QCJPc1XjD45kv/fbT8Cx+p3a8Z7sEmmQiLZXjoQrzQug0gGTUPABDUsZwYt8gMJrub -06iXjTtXP/UayiLcLkeydvtmcJo/swH2+JkM55BMJvsw51KVJTnOcDcwzX8Sw6rMGPkYAIjomOsP -MudT5/ycOA/+jFX3hmmRNkf8Mfn06eEpz/cijQm5/+DPhUkmDE+aS2Xv+xdpc5zhU3QdUgG3JA8x -rMqMMZcKAATzseboncfB0dp/XL151//0j3/W7uHc2WNfwQq624Igt5WUMLzWgf9Jvnjyphsp9CQT -hn2SM6OGGgxrI9kw/PqlGy/HmG+prRAcXaMjjTDepDtPumOOKeghuY9hgtvicBgGyXGY0WXoRawI -ABLYOr+jYk6KWVGV1Dy6icvZAqMMu/7VAnvbFzdjN0yAjgHA0mseZukO4lnNv70zMI4BjrZgjOA7 -WqhZcZJde4aOAUDlfOz7r+6SYdd7OPJv51Si3AQp6CD9Hw65TytW/tCPwz9y/FyRb7r/Tu3pEFHx -/g7pCbOR8SP7Le/DBNI7v+Uckl2VC2YdkQMmXAi/zfGm+t8hJ2U2tdQldr/5nwADACLM1IGrPYuL -AAAAAElFTkSuQmCC ---047d7b45041e19c68004eb9f3de8-- diff --git a/spec/fixtures/emails/auto_generated_header.eml b/spec/fixtures/emails/auto_generated_header.eml new file mode 100644 index 0000000000..74e0e9d4fd --- /dev/null +++ b/spec/fixtures/emails/auto_generated_header.eml @@ -0,0 +1,8 @@ +Return-Path: +From: Foo Bar +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <3@foo.bar.mail> +Auto-Submitted: auto-generated +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit diff --git a/spec/fixtures/emails/auto_generated_precedence.eml b/spec/fixtures/emails/auto_generated_precedence.eml new file mode 100644 index 0000000000..bc82e2b3c2 --- /dev/null +++ b/spec/fixtures/emails/auto_generated_precedence.eml @@ -0,0 +1,8 @@ +Return-Path: +From: Foo Bar +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <2@foo.bar.mail> +Precedence: list +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit diff --git a/spec/fixtures/emails/auto_reply.eml b/spec/fixtures/emails/auto_reply.eml deleted file mode 100644 index 7999c8d78b..0000000000 --- a/spec/fixtures/emails/auto_reply.eml +++ /dev/null @@ -1,21 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+636ca428858779856c226bb145ef4fad@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -Auto-Submitted: auto-generated -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -Test reply to Discourse email digest diff --git a/spec/fixtures/emails/bad_destinations.eml b/spec/fixtures/emails/bad_destinations.eml new file mode 100644 index 0000000000..5690e66778 --- /dev/null +++ b/spec/fixtures/emails/bad_destinations.eml @@ -0,0 +1,11 @@ +Return-Path: +From: Foo Bar +To: wat@bar.com +Cc: foofoo@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <9@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/spec/fixtures/emails/big5.eml b/spec/fixtures/emails/big5.eml deleted file mode 100644 index 4a7b208248..0000000000 --- a/spec/fixtures/emails/big5.eml +++ /dev/null @@ -1,26 +0,0 @@ - -Delivered-To: discourse-reply+cd480e301683c9902891f15968bf07a5@discourse.org -Received: by 10.194.216.104 with SMTP id op8csp80593wjc; - Wed, 24 Jul 2013 07:59:14 -0700 (PDT) -Return-Path: -References: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -From: Walter White -In-Reply-To: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -Mime-Version: 1.0 (1.0) -Date: Wed, 24 Jul 2013 15:59:10 +0100 -Message-ID: <4597127794206131679@unknownmsgid> -Subject: Re: [Discourse] new reply to your post in 'Crystal Blue' -To: walter via Discourse -Content-Type: multipart/alternative; boundary=20cf301cc47ada510404f040b262 - ---20cf301cc47ada510404f040b262 -Content-Type: text/plain; charset=Big5 -Content-Transfer-Encoding: base64 - -tv2hSafapFe5cbX4pEahSQ0K ---20cf301cc47ada510404f040b262 -Content-Type: text/html; charset=Big5 -Content-Transfer-Encoding: base64 - -PGRpdiBkaXI9Imx0ciI+tv2hSafapFe5cbX4pEahSTxicj48L2Rpdj4NCg== ---20cf301cc47ada510404f040b262-- diff --git a/spec/fixtures/emails/bottom_reply.eml b/spec/fixtures/emails/bottom_reply.eml deleted file mode 100644 index 5fc992971f..0000000000 --- a/spec/fixtures/emails/bottom_reply.eml +++ /dev/null @@ -1,160 +0,0 @@ -Received: by 10.107.19.29 with SMTP id b29csp111716ioj; - Wed, 30 Jul 2014 17:52:05 -0700 (PDT) -X-Received: by 10.194.238.6 with SMTP id vg6mr11340975wjc.24.1406767925330; - Wed, 30 Jul 2014 17:52:05 -0700 (PDT) -Received: from localhost (localhost [127.0.0.1]) - by bendel.debian.org (Postfix) with QMQP - id 18F5C417; Thu, 31 Jul 2014 00:52:04 +0000 (UTC) -Old-Return-Path: -X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on bendel.debian.org -X-Spam-Level: -X-Spam-Status: No, score=-25.9 required=4.0 tests=FOURLA,LDOSUBSCRIBER, - LDO_WHITELIST,MURPHY_DEBIAN_MESSAGE,PGPSIGNATURE autolearn=unavailable - version=3.3.2 -X-Original-To: lists-debian-ctte@bendel.debian.org -Delivered-To: lists-debian-ctte@bendel.debian.org -Received: from localhost (localhost [127.0.0.1]) - by bendel.debian.org (Postfix) with ESMTP id CE6CDEE - for ; Thu, 31 Jul 2014 00:51:52 +0000 (UTC) -X-Virus-Scanned: at lists.debian.org with policy bank en-lt -X-Amavis-Spam-Status: No, score=-11.9 tagged_above=-10000 required=5.3 - tests=[BAYES_00=-2, FOURLA=0.1, LDO_WHITELIST=-5, PGPSIGNATURE=-5] - autolearn=ham -Received: from bendel.debian.org ([127.0.0.1]) - by localhost (lists.debian.org [127.0.0.1]) (amavisd-new, port 2525) - with ESMTP id SB451DwGZCOe for ; - Thu, 31 Jul 2014 00:51:47 +0000 (UTC) -X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_BL_NJABL=-1.5 CL_IP_EQ_HELO_IP=-2 (check from: .debian. - helo: .becquer.dodds. - helo-domain: .dodds.) FROM/MX_MATCHES_NOT_HELO(DOMAIN)=0; rate: -5 -Received: from becquer.dodds.net (becquer.dodds.net [207.224.24.209]) - by bendel.debian.org (Postfix) with ESMTP id 8E89A2B4 - for ; Thu, 31 Jul 2014 00:51:47 +0000 (UTC) -Received: from virgil.dodds.net (unknown [192.168.15.59]) - by becquer.dodds.net (Postfix) with ESMTPA id 9B0A9256EB - for ; Wed, 30 Jul 2014 17:51:19 -0700 (PDT) -Received: by virgil.dodds.net (Postfix, from userid 1000) - id 942FB60199; Wed, 30 Jul 2014 17:51:15 -0700 (PDT) -Date: Wed, 30 Jul 2014 17:51:15 -0700 -From: Jake -To: incoming+amazing@appmail.adventuretime.ooo -Subject: Re: Next Debian CTTE IRC Meeting at date -d'Thu Jul 31 17:00:00 UTC - 2014' -Message-ID: <20140731005115.GA19044@virgil.dodds.net> -Mail-Followup-To: debian-ctte@lists.debian.org -References: <20140730213924.GA12356@teltox.donarmstrong.com> -MIME-Version: 1.0 -Content-Type: multipart/signed; micalg=pgp-sha256; - protocol="application/pgp-signature"; boundary="qMm9M+Fa2AknHoGS" -Content-Disposition: inline -In-Reply-To: <20140730213924.GA12356@teltox.donarmstrong.com> -User-Agent: Mutt/1.5.23 (2014-03-12) -X-Debian-Message: Signature check passed for Debian member -X-Rc-Virus: 2007-09-13_01 -X-Rc-Spam: 2008-11-04_01 -Resent-Message-ID: -Resent-From: debian-ctte@lists.debian.org -X-Mailing-List: archive/latest/4791 -X-Loop: debian-ctte@lists.debian.org -List-Id: -List-Post: -List-Help: -List-Subscribe: -List-Unsubscribe: -Precedence: list -Resent-Sender: debian-ctte-request@lists.debian.org -Resent-Date: Thu, 31 Jul 2014 00:52:04 +0000 (UTC) - - ---qMm9M+Fa2AknHoGS -Content-Type: text/plain; charset=us-ascii -Content-Disposition: inline -Content-Transfer-Encoding: quoted-printable - -On Wed, Jul 30, 2014 at 02:39:24PM -0700, Don Armstrong wrote: -> The next Debian CTTE IRC meeting is at=20 - -> date -d 'Thu Jul 31 17:00:00 UTC 2014' on irc.debian.org in -> #debian-ctte. - -> The current meeting agenda is here, and more up-to-date ones may be -> found in the git repository. - -> #startmeeting - -> #topic Who is here? - -> #topic Next Meeting? - -> #topic #717076 Decide between libjpeg-turbo and libjpeg8 et al. - -This has been voted on; should probably be removed from the agenda, someone -just needs to confirm the vote results and get it on the website. (AIUI the -archive has already begun moving on accordingly.) - -> #topic #636783 constitution: super-majority bug - -> #topic #636783 constitution: casting vote - -> #topic #636783 constitution: minimum discussion period - -> #topic #636783 constitution: TC member retirement/rollover - -> #topic #636783 constitution: TC chair retirement/rollover - -> #topic #681419 Depends: foo | foo-nonfree - -> #topic #741573 menu systems and mime-support - -> #topic #746715 init system fallout - -Also voted and just needs to be confirmed. - -> #topic #750135 Maintainer of aptitude package ->=20 -> #topic #752400 Advice on util-linux - -This has been closed by mutual agreement of the people involved and doesn't -require any action from the TC. Removed from the agenda. - -There's also bug #744246, which was assigned to the TC at my request but -without any preamble so it may have escaped notice. However, that situation -has been evolving constructively among the related parties (apparently, an -informal comment from a member of the release team was mistaken for a -release team position, so that's now being revisited), so I don't believe -this is anything we need to put on the agenda for tomorrow despite being on -the open bug list. - ---=20 -Steve Langasek Give me a lever long enough and a Free OS -Debian Developer to set it on, and I can move the world. -Ubuntu Developer http://www.debian.org/ -slangasek@ubuntu.com vorlon@debian.org - ---qMm9M+Fa2AknHoGS -Content-Type: application/pgp-signature; name="signature.asc" -Content-Description: Digital signature - ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1 - -iQIcBAEBCAAGBQJT2ZMDAAoJEFaNMPMhshM9GLsP/244S3wtYZEeVgJWIdB5PE0A -sZVezEA692y++/0oVZVecwV67yBOyfSjPPetdAph2UDMRtfurwxfj2BkbOFA2+Y6 -++MErbmC3V7IGpd/L/fFGdXgvMQT2MNBpw0fnMA7bLpNjCvoj+Hr1HXRUcWoJSlj -WmHWwWSTVRcHg8a3iWYJzY6XfLyEEgHlahrlKvJExsTx/9mc1qg7g8KGdnhzHFBl -ttdH2fxpAk/624dReCcw5RKmOLfZ1HsEl9XcVe1cb4K+MDaQiXmoEK5v3xaNz1tS -NK5v2D5gDs229zoxKzQnnzOPLHxqI5E0L9PpI/mu4T9z7H2bHR3U5BvhnT99t5uw -ydf2cZNGY0uFCV3Rvn07BfAIW5WSXhOfN/5IymRKmdhjsTiwZ/wFjFrK8tVjtERu -yeyA7RIYiblGCEKYIYLWSxhoXeEdmAdfp6EA2/IA1CpgMB+ZdSfaeMgFY7xosgmG -ax3NTnaKyhr1QEUJ2gjAwHnKjuGbRVDAinYrSvP0o8Bh9sAs2BN2negWBCZVwwkN -S9hWTjVqsBmpaPOt5SEDwDo9O9dfzkmaamDsxOuUEz9F7v5jYg0mxA/WbogGty9R -vOMKxdxRkzflL/CferVbkzL/EkZRDfWDp9SleZggrpz7miiNDbS7jdRzJ4EttmJ8 -gHBAVrOzcnbIPOIkk9pw -=KXIu ------END PGP SIGNATURE----- - ---qMm9M+Fa2AknHoGS-- - - --- -To UNSUBSCRIBE, email to debian-ctte-REQUEST@lists.debian.org -with a subject of "unsubscribe". Trouble? Contact listmaster@lists.debian.org -Archive: https://lists.debian.org/20140731005115.GA19044@virgil.dodds.net diff --git a/spec/fixtures/emails/boundary.eml b/spec/fixtures/emails/boundary.eml deleted file mode 100644 index 1250fe498b..0000000000 --- a/spec/fixtures/emails/boundary.eml +++ /dev/null @@ -1,61 +0,0 @@ - -MIME-Version: 1.0 -Received: by 10.64.14.41 with HTTP; Wed, 19 Jun 2013 06:29:41 -0700 (PDT) -In-Reply-To: <51c19490e928a_13442dd8ae892548@tree.mail> -References: <51c19490e928a_13442dd8ae892548@tree.mail> -Date: Wed, 19 Jun 2013 09:29:41 -0400 -Delivered-To: finn@adventuretime.ooo -Message-ID: -Subject: Re: [Adventure Time] jake mentioned you in 'peppermint butler is - missing' -From: Finn the Human -To: jake via Adventure Time -Content-Type: multipart/alternative; boundary=001a11c206a073876a04df81d2a9 - ---001a11c206a073876a04df81d2a9 -Content-Type: text/plain; charset=ISO-8859-1 - -I'll look into it, thanks! - - -On Wednesday, June 19, 2013, jake via Discourse wrote: - -> jake mentioned you in 'peppermint butler is missing' on Adventure -> Time: -> ------------------------------ -> -> yeah, just noticed this cc @jake -> ------------------------------ -> -> Please visit this link to respond: -> http://adventuretime.ooo/t/peppermint-butler-is-missing/7628/2 -> -> To unsubscribe from these emails, visit your user preferences -> . -> - ---001a11c206a073876a04df81d2a9 -Content-Type: text/html; charset=ISO-8859-1 -Content-Transfer-Encoding: quoted-printable - -I'll look into it, thanks!

    On Wednesday, June 19, 2= -013, jake via Adventure Time wrote:

    sa= -m mentioned you in 'Duplicate message are shown in profile' on Adve= -nture Time

    - - -

    yeah, just noticed this cc @eviltrout

    - -

    Please visit this link to respond: http= -://adventuretime.ooo/t/peppermint-butler-is-missing/7628/2 - - -

    To unsubscribe from these emails, visit your user preferences.

    -
    - ---001a11c206a073876a04df81d2a9-- diff --git a/spec/fixtures/emails/cc.eml b/spec/fixtures/emails/cc.eml new file mode 100644 index 0000000000..c54363b909 --- /dev/null +++ b/spec/fixtures/emails/cc.eml @@ -0,0 +1,12 @@ +Return-Path: +From: Foo Bar +To: someone@else.com +CC: team@bar.com, wat@bar.com, reply+d400310beeae61d785c2ac6a2aacb210@bar.com +Subject: The more, the merrier +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <30@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +It is more fun with more people. diff --git a/spec/fixtures/emails/chinese_reply.eml b/spec/fixtures/emails/chinese_reply.eml new file mode 100644 index 0000000000..befb3b1cbb --- /dev/null +++ b/spec/fixtures/emails/chinese_reply.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <17@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=Big5 +Content-Transfer-Encoding: base64 + +5oKo5aW977yBIOS9oOS7iuWkqeWlveWQl++8nw== diff --git a/spec/fixtures/emails/dutch.eml b/spec/fixtures/emails/dutch.eml deleted file mode 100644 index 7be08dc493..0000000000 --- a/spec/fixtures/emails/dutch.eml +++ /dev/null @@ -1,20 +0,0 @@ - -Delivered-To: discourse-reply+cd480e301683c9902891f15968bf07a5@discourse.org -Received: by 10.194.216.104 with SMTP id op8csp80593wjc; - Wed, 24 Jul 2013 07:59:14 -0700 (PDT) -Return-Path: -References: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -From: Walter White -In-Reply-To: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -Mime-Version: 1.0 (1.0) -Date: Wed, 24 Jul 2013 15:59:10 +0100 -Message-ID: <4597127794206131679@unknownmsgid> -Subject: Re: [Discourse] new reply to your post in 'Crystal Blue' -To: walter via Discourse -Content-Type: multipart/alternative; boundary=001a11c20edc15a39304e2432790 - -Dit is een antwoord in het Nederlands. - -Op 18 juli 2013 10:23 schreef Sander Datema het volgende: - -Dit is de originele post. \ No newline at end of file diff --git a/spec/fixtures/emails/email_reply_1.eml b/spec/fixtures/emails/email_reply_1.eml new file mode 100644 index 0000000000..fdf4a5f2f8 --- /dev/null +++ b/spec/fixtures/emails/email_reply_1.eml @@ -0,0 +1,12 @@ +Return-Path: +From: One +To: team@bar.com +Cc: two@foo.com, three@foo.com +Subject: Testing email threading +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <34@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +This is email reply **1**. diff --git a/spec/fixtures/emails/email_reply_2.eml b/spec/fixtures/emails/email_reply_2.eml new file mode 100644 index 0000000000..fde2e03390 --- /dev/null +++ b/spec/fixtures/emails/email_reply_2.eml @@ -0,0 +1,13 @@ +Return-Path: +From: Two +To: one@foo.com +Cc: team@bar.com, three@foo.com +Subject: RE: Testing email threading +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <35@foo.bar.mail> +In-Reply-To: <34@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +This is email reply **2**. diff --git a/spec/fixtures/emails/email_reply_3.eml b/spec/fixtures/emails/email_reply_3.eml new file mode 100644 index 0000000000..3b4eb6d0c0 --- /dev/null +++ b/spec/fixtures/emails/email_reply_3.eml @@ -0,0 +1,14 @@ +Return-Path: +From: Three +To: one@foo.com +Cc: team@bar.com, two@foo.com +Subject: RE: Testing email threading +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <36@foo.bar.mail> +In-Reply-To: <35@foo.bar.mail> +References: <34@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +This is email reply **3**. diff --git a/spec/fixtures/emails/email_reply_4.eml b/spec/fixtures/emails/email_reply_4.eml new file mode 100644 index 0000000000..4c54d00849 --- /dev/null +++ b/spec/fixtures/emails/email_reply_4.eml @@ -0,0 +1,15 @@ +Return-Path: +From: One +To: two@foo.com +Cc: team@bar.com, three@foo.com +Subject: RE: Testing email threading +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <37@foo.bar.mail> +In-Reply-To: <36@foo.bar.mail> +References: <34@foo.bar.mail> + <35@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +This is email reply **4**. diff --git a/spec/fixtures/emails/empty.eml b/spec/fixtures/emails/empty.eml deleted file mode 100644 index 85bebc6624..0000000000 --- a/spec/fixtures/emails/empty.eml +++ /dev/null @@ -1,21 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - - - diff --git a/spec/fixtures/emails/encoded_display_name.eml b/spec/fixtures/emails/encoded_display_name.eml new file mode 100644 index 0000000000..cf31f703ef --- /dev/null +++ b/spec/fixtures/emails/encoded_display_name.eml @@ -0,0 +1,11 @@ +Return-Path: +From: =?UTF-8?B?0KHQu9GD0YfQsNC50L3QsNGP?= =?UTF-8?B?INCY0LzRjw==?= +To: team@bar.com +Subject: I need help +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <29@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Будьте здоровы! diff --git a/spec/fixtures/emails/from_the_future.eml b/spec/fixtures/emails/from_the_future.eml new file mode 100644 index 0000000000..6e14c442e1 --- /dev/null +++ b/spec/fixtures/emails/from_the_future.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Wed, 01 Jan 3000 00:00:00 +0100 +Message-ID: <4@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +Back to the future! diff --git a/spec/fixtures/emails/gmail_web.eml b/spec/fixtures/emails/gmail_web.eml deleted file mode 100644 index 8bb8383571..0000000000 --- a/spec/fixtures/emails/gmail_web.eml +++ /dev/null @@ -1,181 +0,0 @@ -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/hebrew.eml b/spec/fixtures/emails/hebrew.eml deleted file mode 100644 index f4c8f01adc..0000000000 --- a/spec/fixtures/emails/hebrew.eml +++ /dev/null @@ -1,17 +0,0 @@ - -Delivered-To: discourse-reply+cd480e301683c9902891f15968bf07a5@discourse.org -Received: by 10.194.216.104 with SMTP id op8csp80593wjc; - Wed, 24 Jul 2013 07:59:14 -0700 (PDT) -Return-Path: -References: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -From: Walter White -In-Reply-To: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -Mime-Version: 1.0 (1.0) -Date: Wed, 24 Jul 2013 15:59:10 +0100 -Message-ID: <4597127794206131679@unknownmsgid> -Subject: Re: [Discourse] new reply to your post in 'Crystal Blue' -To: walter via Discourse -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: base64 - -16nXnNeV150= \ No newline at end of file diff --git a/spec/fixtures/emails/hebrew_reply.eml b/spec/fixtures/emails/hebrew_reply.eml new file mode 100644 index 0000000000..450e2dea2f --- /dev/null +++ b/spec/fixtures/emails/hebrew_reply.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <16@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: base64 + +16nXnNeV150hINee15Qg16nXnNeV157XmiDXlNeZ15XXnT8= diff --git a/spec/fixtures/emails/html_only.eml b/spec/fixtures/emails/html_only.eml deleted file mode 100644 index db88f2c388..0000000000 --- a/spec/fixtures/emails/html_only.eml +++ /dev/null @@ -1,93 +0,0 @@ - -Delivered-To: walter@breakingbad.com -Received: by 10.64.13.41 with SMTP id m9csp29769iec; - Thu, 20 Jun 2013 08:53:22 -0700 (PDT) -X-Received: by 10.252.23.9 with SMTP id p9mr4055675lag.4.1371743601980; - Thu, 20 Jun 2013 08:53:21 -0700 (PDT) -Received: from mail-la0-x229.google.com (mail-la0-x229.google.com [2a00:1450:4010:c03::229]) - by mx.google.com with ESMTPS id u4si430203lae.48.2013.06.20.08.53.20 - for - (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); - Thu, 20 Jun 2013 08:53:21 -0700 (PDT) -X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=google.com; s=20120113; - h=x-forwarded-to:x-forwarded-for:delivered-to:x-return-path - :content-type:mime-version:content-transfer-encoding:x-mailer - :message-id:date:subject:from:in-reply-to:to:resent-date:resent-from - :resent-to:resent-subject:resent-message-id:resent-user-agent - :x-scanned-by:x-gm-message-state; - bh=9O67r74ofh9WkEaKTRB/frQ3MKOtQlbCac2mz0/MiyY=; - b=YVAo2/JDMP53RxDmqDEKNcEMtggtfaVyq2DoseZ6vBAfB7G6NtHC9ZEkRs4oGhk6LU - fnyAPe0wnz5d9WINoMAuuTRIhplLxzcqysduSnAJAQ2qqR7mFBnlj9wJeVEKltNwmUME - nPwxsf8go20VBzrZCtECPedcLi60wbl32NCXVn0qwt2LvKiy6ktSS5Xgb4zY8i4dfXAP - 6Y5gu32boooWIb9DkH1TJkn3C0RrEugNlw/DUnXrnkFefgxWF3pt/zcoW/wYRyikOdx+ - smBClgR9my6QmsS2KsQrMvWJZUva7fddTiZ6FC22e4hW+8Wha0RaZOZu5O7hjg6G4/1g - IEyg== -X-Received: by 10.112.55.9 with SMTP id n9mr5916187lbp.5.1371743600857; - Thu, 20 Jun 2013 08:53:20 -0700 (PDT) -X-Forwarded-To: walter@breakingbad.com -X-Forwarded-For: walter@breakingbad.com -Delivered-To: walter@breakingbad.com -Content-Type: text/html; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: quoted-printable -X-Mailer: BlackBerry Email (10.1.0.1720) -Message-ID: <20130619231548.6307981.74194.2379@breakingbad.com> -Date: Wed, 19 Jun 2013 19:15:48 -0400 -Subject: Re: [Discourse Meta] [PM] re: Regarding your post in "Site - Customization not working" -From: aaron@breakingbad.com -In-Reply-To: <51c238655a394_5f4e3ce6690667bd@tiefighter2.mail> -To: reply+20c1b0a8bd1a63c0163cc7e7641ca06b@appmail.adventuretime.ooo -ReSent-Date: Thu, 20 Jun 2013 11:53:08 -0400 (EDT) -ReSent-From: Aaron -ReSent-Subject: Re: [Discourse Meta] [PM] re: Regarding your post in "Site - Customization not working" -X-Gm-Message-State: ALoCoQl1BtN83rAX7At808XAPv1yCqUK3Du2IvK7eCyY3jsI77u4e5cak28307pYYHAo1JlO/Eu9 - -
    The EC2 instance - I've seen that th= -ere tends to be odd and unrecommended settings on the Bitnami installs that= - I've checked out.
    = - = -

    = - = -
    = - = - = -
    = -From: Grizzly B via Discourse Meta
    Sent: Wednesday, J= -une 19, 2013 19:02
    To: aaron@breakingbad.com
    = -Reply To: Grizzly B via Discourse Meta
    Subject: [Disc= -ourse Meta] [PM] re: Regarding your post in "Site Customization
    not wor= -king"

    Grizzly B just sent you a private message

    - -

    Log in to our EC2 instance -or- log into a new DigitalOcean instanc= -e?

    - -

    Please visit this link to respond: http://= -meta.discourse.org/t/regarding-your-post-in-site-customization-not-working/= -7641/5

    - -

    To unsubscribe from these emails, visit your user preferences.

    -
    diff --git a/spec/fixtures/emails/html_paragraphs.eml b/spec/fixtures/emails/html_paragraphs.eml deleted file mode 100644 index 3fe37fb8b1..0000000000 --- a/spec/fixtures/emails/html_paragraphs.eml +++ /dev/null @@ -1,205 +0,0 @@ - -MIME-Version: 1.0 -Received: by 10.25.161.144 with HTTP; Tue, 7 Oct 2014 22:17:17 -0700 (PDT) -X-Originating-IP: [117.207.85.84] -In-Reply-To: <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail> -References: - <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail> -Date: Wed, 8 Oct 2014 10:47:17 +0530 -Delivered-To: arpit@techapj.com -Message-ID: -Subject: Re: [Discourse] [Meta] Welcome to techAPJ's Discourse! -From: Arpit Jalan -To: Discourse -Content-Type: multipart/alternative; boundary=001a114119d8f4e46e0504e26d5b - ---001a114119d8f4e46e0504e26d5b -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: quoted-printable - -Awesome! - -Pleasure to have you here! - -:boom: - -On Wed, Oct 8, 2014 at 10:46 AM, ajalan -wrote: - -> ajalan -> -> October 8 -> -> Nice to be here! Thanks! [image: smile] -> -> To respond, reply to this email or visit -> http://discourse.techapj.com/t/welcome-to-techapjs-discourse/35/2 -> -> in your browser. -> ------------------------------ -> Previous Replies techAPJ -> -> October 8 -> -> Welcome to techAPJ's Discourse! -> ------------------------------ -> -> To respond, reply to this email or visit -> http://discourse.techapj.com/t/welcome-to-techapjs-discourse/35/2 -> -> in your browser. -> -> To unsubscribe from these emails, visit your user preferences -> -> . -> - ---001a114119d8f4e46e0504e26d5b -Content-Type: text/html; charset=UTF-8 -Content-Transfer-Encoding: quoted-printable - -
    Awesome!

    Pleasure to have you here!

    :boom:

    On Wed, Oct 8, 2014 at 10:46 AM, ajalan <info@unconfigured.discourse.org> wrote:
    - - - - - - - - - - - -
    - - - ajalan
    - October 8 -

    Nice to be here! Thanks! 3D"smile"

    - - -
    -

    To respond, reply to this email or visit http://discourse= -.techapj.com/t/welcome-to-techapjs-discourse/35/2 in your browser.

    -
    -
    -

    Previous Replies

    - - - - - - - - - - - -
    - - - techAPJ
    - October 8 -

    Welcome to techAPJ's Discourse!

    - - -
    - -
    -

    To respond, reply to this email or visit http://discourse.tec= -hapj.com/t/welcome-to-techapjs-discourse/35/2 in your browser.

    -
    -
    -

    To unsubscribe from these emails, visit your user preferences.

    -
    -
    - -
    = -
    - ---001a114119d8f4e46e0504e26d5b-- diff --git a/spec/fixtures/emails/html_reply.eml b/spec/fixtures/emails/html_reply.eml new file mode 100644 index 0000000000..28e5feff85 --- /dev/null +++ b/spec/fixtures/emails/html_reply.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <18@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +
    This is a HTML reply ;)
    diff --git a/spec/fixtures/emails/inactive_sender.eml b/spec/fixtures/emails/inactive_sender.eml new file mode 100644 index 0000000000..d22e950788 --- /dev/null +++ b/spec/fixtures/emails/inactive_sender.eml @@ -0,0 +1,9 @@ +Return-Path: +From: Foo Bar +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <8@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/spec/fixtures/emails/inline_attachment.eml b/spec/fixtures/emails/inline_attachment.eml new file mode 100644 index 0000000000..52188b2a78 --- /dev/null +++ b/spec/fixtures/emails/inline_attachment.eml @@ -0,0 +1,76 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <28@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: multipart/related; boundary=001a114b2eccff183a052998ec68 + +--001a114b2eccff183a052998ec68 +Content-Type: multipart/alternative; boundary=001a114b2eccff1836052998ec67 + +--001a114b2eccff1836052998ec67 +Content-Type: text/plain; charset=UTF-8 + +Before + +[image: 内嵌图片 1] + +After + +--001a114b2eccff1836052998ec67 +Content-Type: text/html; charset=UTF-8 + +
    Before

    内嵌图片 1
    +

    After
    + +--001a114b2eccff1836052998ec67-- +--001a114b2eccff183a052998ec68 +Content-Type: image/png; name="logo.png" +Content-Disposition: inline; filename="logo.png" +Content-Transfer-Encoding: base64 +Content-ID: +X-Attachment-Id: ii_1525434659ddb4cb + +iVBORw0KGgoAAAANSUhEUgAAAPQAAABCCAMAAABXYgukAAABhlBMVEUAAAAjHyAjHyAjHyAjHyAj +HyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAAqVAAru/lGyTxXCL/+a5UHiHZGyTq +NyPtRCM7HyHwWCLrPCOEHSL95Z31g0W1HCMgs1wAqnghKC0Aq5YJirsAqm6oHCMPb5QArccUXnsS +Z4hAvWj80ouP1oXoKyR4HSL0eTz4q2j+76XsQCOf24vpLyNsHiJgHiEYTGHyZiucHSPzcDTf76IL +ga7qMyMwuGIArdEaQ1T7yIJwzHn2l1f6vnovHyDuTCPNHCQfMTr3oV/wVCL5tHEHk8gArKDnJyTv +UCIAruUWVW4ArLMQrlYNeKEEnNVQwm0cOkcAq4KA0X/mHyQAqVoArKkAq4wCpeIArdvnIyT1jU4A +qmQUXV3v9KikRSHP6pwwIyBHHiHKUSLxaDTtSCMCpLpgx3PaHyTtSSy/5Zetw3b83JSVSiT0i1eg +MSI+qVa2JCMrbVbiX0Ygs2YArL2LkGApkWQLf2kSZmpFMZD0AAAAEHRSTlMAEFCAv8+vQCBw72CP +358w5xEcGAAABxJJREFUeF7lmuW/uzoMh1eKrUApbMfd9efu7u5u193d739+gbHQBtg49x3b99U5 +H7asD0mTNNAoE9GobohUukmtxqDLbhoCi5nOICNruigWc+0BRXY80UN8ELEtQ/QWo4OGTFzRX4Y/ +UMy+ISqpOUi7mYmK4oPDLKpLJ4PBzMVOZAwEdVNgje6dmZ+fX5vZ+1mhrwcwto+stTKNnRgdxH3t +q0B7r6W4d/fEirivHR24HE48Ja47yO+NX22nujlxuDU/ialrXq9NmWUmQd7TIQZNTX9zBCczGYCy +mjVrmoxyIgnrDvLJS5d3jUSa3XXr/fbU+Ayipvi2mTUMbmAeT5AvJcCgy3P3fkbUNrTs6QWrNtAU +Mb+cSJBnR7B27fv4U5TBkQlaG0czKW3HzPfiwAYvK9r/OXI1hq5hWzI5FkF/GTHPHRgp1vc/qq5G +WcGpC7QnQPMR88E4tEdK9ctvMjQjKYAB+bxufcloXKoQM9ah4ENAllxLzFodQ1wBWos29M12e99I +Ly0sHius1cSCXF6n6J6MHD3dbh+f7Qm9O/hqSaIm9e26IXVPtdu3RnrqVLB4GsV37eQo0X0Ggrtc +Z4Nz5wXIRQB129JjSbm60A/6YrC8gjZ13aQD85UouqOuBODKN/VqeF2A0nunxwK3E4fqOm3mU5vf +pKZu0qafB/BpZASuVJVNTd4kqW03Mq3lEK3YsEs1AtAsK1hJvbrRF/q7IAjXcVOmK+MU2rWqKwi2 +m/2aR5UUSKgnXcEdMgBkHX76ey4kFr/rPqZYtnlGyC0wA9BJ7v6hCvTGbQGyctDEQKMGIFDEpEsa +U65oVaE5lBBfsuBl0dJkQpZLiqCPj1SCDpfKoOFvnOlsQ2CZBNiQeDXolMhMmUHML5t2GiQHfbU9 +Vwn6Tpjl7yaGdgrPnzYTeRmQTLF4BWiQhm+08BAzokbQ+ytCbwIAxdCmCuDIIY/llg7caXVoFoUR +2jk+OkoZOkSCrkBfqQi9O4beKodOQ86yKAPmzJme61gaTUOdOXIMeC5tuh4ESDXoZsN2YIhBLY0D +Mxg2nE5Gy7wg5Dp9sAr0xXhPb5WHd7ZmYqTM4AnPgswFaZaDIeXhkl4BGjKGlX3FYt0dzZWdAvve +U6DXWtNVoM+q0FYxNE+Auow891yEGLzzDwE76tqEXQXalqGFlrClzETguR18Sh7wt/ZUgD4VBMH9 +cmjY057clTPYamV9MM1NNWgFaKOhAArdyhm2c4+uXBn6Sutl+0aVLf1BGD4ob04sITC21aNP5zCN +QLdIrwBtSjgY2+0Y1iV56Z1Sp7/3+peshSBYDqWS1UDQcvr2NMWbfnkfbBbch97QEA24Ipo2GC6R +Aj3aGu/bnBwKguBcGKIHeaUdGSc4H2ExHN2AWh0aMgHUhOrQYu3lVJ829KOFJLpXlEqLe2/CUTPQ +C1rsAJrkoUG+h3qDqtCTY9M3+tar4GEo9SYahk6kZStw+0B7O4C2CqBBhDIpu1aHFmNnTvbpxhJH +S0dLgqDzb2bZVfa0LkOb0Eo6laGV/kPoAM2tvJCnWxOXejF/8iiIi3T4GJjN8qU7DBbm98jeLq4s +UGAlShVa2RUYG8zR0iGHCn20NTXbj3lZcbRTBE1IZwUeLN7Lu7r7t5brIczMtKWeT90e0EQpFBYY +tvpBzxye68e8GobSCIEVBqlhEFgA1Ft00HXgfTQGjMqyGcnYvA4RgQJelL3TO2MAKoPvpvK5XQA9 +P3GhR7FKmO+H4ZMlgKZF0LzbY5sAbTN1cGBzdJKC+mqbimkDakB2IndlaGBODRAPoKna7xOanINy +0GNflHefT4OUOXwmQDaGhtbCoFSXFkYhNnRKuaG8fOhlB0DdQA+MHEhNOmRjH0MDKqdU2kiEde1S +zWpylk2wlDx291YZ8tdBrOcx8wN0HEbQrkDye7QKDvQVWBbQYOmovBed1j3IezkxW4U++u0BqE0v +Dp0C4t3bCfKr16HKzEgBNMdrhHWVzke0wrtRco3ZBdD4tintb36+oOSxn9KMdXEhhny0/fTFi+3t +R0FHb2I3vz2Gh3758C5+y47o5WTY18xBhyW0aAyNDXC5bGJmBP3rbEq8+ubhu403r4JMq69j5PD2 +deTDokRGMTOeTObHw8RUrti43uNBJ4aGW4r7AV/Ho0gM/XtE/MfinY37t1fWtx6H4cbr5cVIz+/8 ++S6M9XYTxxmGhlkzDGpkEQfYPI7qp9X9DuMJF56JY3t6Ism+ZaLpNjYsPNfP1+nJv/7+59+t891T +4/X1t6GsJ5tLaN9I8q1Yvvo/eBl/EK7kL9mNIpFO+9hHZR+y8W+KXjq2vtIBf3J681luXFlfYc6h +eOV7GJkb/5d5+KCbjaGDZlZjiKChwA8RNEx1hwcanvIPGbThAPKQQBvQuQ4FtKeb8GrOQOg/pxLS +uIDrr6oAAAAASUVORK5CYII= +--001a114b2eccff183a052998ec68-- diff --git a/spec/fixtures/emails/inline_mixed.eml b/spec/fixtures/emails/inline_mixed.eml deleted file mode 100644 index f8be9c7a54..0000000000 --- a/spec/fixtures/emails/inline_mixed.eml +++ /dev/null @@ -1,37 +0,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. - -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. - -> First paragraph. -> -> Second paragraph. - -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 diff --git a/spec/fixtures/emails/inline_mixed_replies.eml b/spec/fixtures/emails/inline_mixed_replies.eml new file mode 100644 index 0000000000..05c299e96a --- /dev/null +++ b/spec/fixtures/emails/inline_mixed_replies.eml @@ -0,0 +1,19 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <24@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 + +On Tue, Jan 15, 2016 at 11:12 AM, Bar Foo wrote: + +> WAT November 28 +> +> This is the previous post. + +And this is *my* reply :+1: + +> This is another post. + +And this is **another** reply. diff --git a/spec/fixtures/emails/inline_reply.eml b/spec/fixtures/emails/inline_reply.eml index 39625a225d..b65518e2aa 100644 --- a/spec/fixtures/emails/inline_reply.eml +++ b/spec/fixtures/emails/inline_reply.eml @@ -1,60 +1,15 @@ - -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 +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <23@foo.bar.mail> +Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: quoted-printable -On Wed, Oct 8, 2014 at 11:12 AM, techAPJ -wrote: +On Tue, Jan 15, 2016 at 11:12 AM, Bar Foo 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 -> . +> WAT November 28 > +> This is the previous post. -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-- +And this is *my* reply :+1: diff --git a/spec/fixtures/emails/insufficient_trust_level.eml b/spec/fixtures/emails/insufficient_trust_level.eml new file mode 100644 index 0000000000..4800b432be --- /dev/null +++ b/spec/fixtures/emails/insufficient_trust_level.eml @@ -0,0 +1,11 @@ +Return-Path: +From: Foo Bar +To: category@bar.com +Subject: This is a topic from a complete stranger +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <32@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hey, this is a topic from a complete stranger ;) diff --git a/spec/fixtures/emails/ios_default.eml b/spec/fixtures/emails/ios_default.eml deleted file mode 100644 index 8d4d58feb1..0000000000 --- a/spec/fixtures/emails/ios_default.eml +++ /dev/null @@ -1,136 +0,0 @@ -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 index d314ad1f1e..79183b4a3f 100644 --- a/spec/fixtures/emails/iphone_signature.eml +++ b/spec/fixtures/emails/iphone_signature.eml @@ -1,29 +1,11 @@ -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) +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <25@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +This is not the signature you're looking for. ---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 +Sent from my iMachine diff --git a/spec/fixtures/emails/like.eml b/spec/fixtures/emails/like.eml index 3bd9ae0fa7..39829fcaab 100644 --- a/spec/fixtures/emails/like.eml +++ b/spec/fixtures/emails/like.eml @@ -1,37 +1,10 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: FROM -To: TO -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <13@foo.bar.mail> Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 +Content-Type: text/plain Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 -LIKE - - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> ++1 diff --git a/spec/fixtures/emails/missing_message_id.eml b/spec/fixtures/emails/missing_message_id.eml new file mode 100644 index 0000000000..03405bed3c --- /dev/null +++ b/spec/fixtures/emails/missing_message_id.eml @@ -0,0 +1,5 @@ +From: Foo Bar +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit diff --git a/spec/fixtures/emails/multiple_destinations.eml b/spec/fixtures/emails/multiple_destinations.eml deleted file mode 100644 index 6d31bbf197..0000000000 --- a/spec/fixtures/emails/multiple_destinations.eml +++ /dev/null @@ -1,40 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: finn@adventuretime.ooo, reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -I could not disagree more. I am obviously biased but adventure time is the -greatest show ever created. Everyone should watch it. - -- Jake out - - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> \ No newline at end of file diff --git a/spec/fixtures/emails/newlines.eml b/spec/fixtures/emails/newlines.eml deleted file mode 100644 index cf03b9d18b..0000000000 --- a/spec/fixtures/emails/newlines.eml +++ /dev/null @@ -1,84 +0,0 @@ -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/no_body.eml b/spec/fixtures/emails/no_body.eml new file mode 100644 index 0000000000..02afbe7331 --- /dev/null +++ b/spec/fixtures/emails/no_body.eml @@ -0,0 +1,7 @@ +Return-Path: +From: Foo Bar +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <5@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit diff --git a/spec/fixtures/emails/no_body_with_attachments.eml b/spec/fixtures/emails/no_body_with_attachments.eml new file mode 100644 index 0000000000..7e768d118d --- /dev/null +++ b/spec/fixtures/emails/no_body_with_attachments.eml @@ -0,0 +1,75 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <6@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: multipart/mixed; + boundary="--==_mimepart_56990c8d3f66c_7cb53ffbb98602004746e"; + charset=UTF-8 +Content-Transfer-Encoding: 7bit + + +----==_mimepart_56990c8d3f66c_7cb53ffbb98602004746e +Content-Type: image/png; + charset=UTF-8; + filename=logo.png +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename=logo.png +Content-ID: <56990c92d6c64_7cb53ffbb986020047616@foo.bar.mail> + +iVBORw0KGgoAAAANSUhEUgAAAPQAAABCCAMAAABXYgukAAABhlBMVEUAAAAj +HyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAjHyAj +HyAAqVAAru/lGyTxXCL/+a5UHiHZGyTqNyPtRCM7HyHwWCLrPCOEHSL95Z31 +g0W1HCMgs1wAqnghKC0Aq5YJirsAqm6oHCMPb5QArccUXnsSZ4hAvWj80ouP +1oXoKyR4HSL0eTz4q2j+76XsQCOf24vpLyNsHiJgHiEYTGHyZiucHSPzcDTf +76ILga7qMyMwuGIArdEaQ1T7yIJwzHn2l1f6vnovHyDuTCPNHCQfMTr3oV/w +VCL5tHEHk8gArKDnJyTvUCIAruUWVW4ArLMQrlYNeKEEnNVQwm0cOkcAq4KA +0X/mHyQAqVoArKkAq4wCpeIArdvnIyT1jU4AqmQUXV3v9KikRSHP6pwwIyBH +HiHKUSLxaDTtSCMCpLpgx3PaHyTtSSy/5Zetw3b83JSVSiT0i1egMSI+qVa2 +JCMrbVbiX0Ygs2YArL2LkGApkWQLf2kSZmpFMZD0AAAAEHRSTlMAEFCAv8+v +QCBw72CP358w5xEcGAAABxJJREFUeF7lmuW/uzoMh1eKrUApbMfd9efu7u5u +193d739+gbHQBtg49x3b99U5H7asD0mTNNAoE9GobohUukmtxqDLbhoCi5nO +ICNruigWc+0BRXY80UN8ELEtQ/QWo4OGTFzRX4Y/UMy+ISqpOUi7mYmK4oPD +LKpLJ4PBzMVOZAwEdVNgje6dmZ+fX5vZ+1mhrwcwto+stTKNnRgdxH3tq0B7 +r6W4d/fEirivHR24HE48Ja47yO+NX22nujlxuDU/ialrXq9NmWUmQd7TIQZN +TX9zBCczGYCymjVrmoxyIgnrDvLJS5d3jUSa3XXr/fbU+Ayipvi2mTUMbmAe +T5AvJcCgy3P3fkbUNrTs6QWrNtAUMb+cSJBnR7B27fv4U5TBkQlaG0czKW3H +zPfiwAYvK9r/OXI1hq5hWzI5FkF/GTHPHRgp1vc/qq5GWcGpC7QnQPMR88E4 +tEdK9ctvMjQjKYAB+bxufcloXKoQM9ah4ENAllxLzFodQ1wBWos29M12e99I +Ly0sHius1cSCXF6n6J6MHD3dbh+f7Qm9O/hqSaIm9e26IXVPtdu3RnrqVLB4 +GsV37eQo0X0GgrtcZ4Nz5wXIRQB129JjSbm60A/6YrC8gjZ13aQD85UouqOu +BODKN/VqeF2A0nunxwK3E4fqOm3mU5vfpKZu0qafB/BpZASuVJVNTd4kqW03 +Mq3lEK3YsEs1AtAsK1hJvbrRF/q7IAjXcVOmK+MU2rWqKwi2m/2aR5UUSKgn +XcEdMgBkHX76ey4kFr/rPqZYtnlGyC0wA9BJ7v6hCvTGbQGyctDEQKMGIFDE +pEsaU65oVaE5lBBfsuBl0dJkQpZLiqCPj1SCDpfKoOFvnOlsQ2CZBNiQeDXo +lMhMmUHML5t2GiQHfbU9Vwn6Tpjl7yaGdgrPnzYTeRmQTLF4BWiQhm+08BAz +okbQ+ytCbwIAxdCmCuDIIY/llg7caXVoFoUR2jk+OkoZOkSCrkBfqQi9O4be +KodOQ86yKAPmzJme61gaTUOdOXIMeC5tuh4ESDXoZsN2YIhBLY0DMxg2nE5G +y7wg5Dp9sAr0xXhPb5WHd7ZmYqTM4AnPgswFaZaDIeXhkl4BGjKGlX3FYt0d +zZWdAvveU6DXWtNVoM+q0FYxNE+Auow891yEGLzzDwE76tqEXQXalqGFlrCl +zETguR18Sh7wt/ZUgD4VBMH9cmjY057clTPYamV9MM1NNWgFaKOhAArdyhm2 +c4+uXBn6Sutl+0aVLf1BGD4ob04sITC21aNP5zCNQLdIrwBtSjgY2+0Y1iV5 +6Z1Sp7/3+peshSBYDqWS1UDQcvr2NMWbfnkfbBbch97QEA24Ipo2GC6RAj3a +Gu/bnBwKguBcGKIHeaUdGSc4H2ExHN2AWh0aMgHUhOrQYu3lVJ829KOFJLpX +lEqLe2/CUTPQC1rsAJrkoUG+h3qDqtCTY9M3+tar4GEo9SYahk6kZStw+0B7 +O4C2CqBBhDIpu1aHFmNnTvbpxhJHS0dLgqDzb2bZVfa0LkOb0Eo6laGV/kPo +AM2tvJCnWxOXejF/8iiIi3T4GJjN8qU7DBbm98jeLq4sUGAlShVa2RUYG8zR +0iGHCn20NTXbj3lZcbRTBE1IZwUeLN7Lu7r7t5brIczMtKWeT90e0EQpFBYY +tvpBzxye68e8GobSCIEVBqlhEFgA1Ft00HXgfTQGjMqyGcnYvA4RgQJelL3T +O2MAKoPvpvK5XQA9P3GhR7FKmO+H4ZMlgKZF0LzbY5sAbTN1cGBzdJKC+mqb +imkDakB2IndlaGBODRAPoKna7xOanINy0GNflHefT4OUOXwmQDaGhtbCoFSX +FkYhNnRKuaG8fOhlB0DdQA+MHEhNOmRjH0MDKqdU2kiEde1SzWpylk2wlDx2 +91YZ8tdBrOcx8wN0HEbQrkDye7QKDvQVWBbQYOmovBed1j3IezkxW4U++u0B +qE0vDp0C4t3bCfKr16HKzEgBNMdrhHWVzke0wrtRco3ZBdD4tintb36+oOSx +n9KMdXEhhny0/fTFi+3tR0FHb2I3vz2Gh3758C5+y47o5WTY18xBhyW0aAyN +DXC5bGJmBP3rbEq8+ubhu403r4JMq69j5PD2deTDokRGMTOeTObHw8RUrti4 +3uNBJ4aGW4r7AV/Ho0gM/XtE/MfinY37t1fWtx6H4cbr5cVIz+/8+S6M9XYT +xxmGhlkzDGpkEQfYPI7qp9X9DuMJF56JY3t6Ism+ZaLpNjYsPNfP1+nJv/7+ +59+t891T4/X1t6GsJ5tLaN9I8q1Yvvo/eBl/EK7kL9mNIpFO+9hHZR+y8W+K +Xjq2vtIBf3J681luXFlfYc6heOV7GJkb/5d5+KCbjaGDZlZjiKChwA8RNEx1 +hwcanvIPGbThAPKQQBvQuQ4FtKeb8GrOQOg/pxLSuIDrr6oAAAAASUVORK5C +YII= + +----==_mimepart_56990c8d3f66c_7cb53ffbb98602004746e-- diff --git a/spec/fixtures/emails/no_content_reply.eml b/spec/fixtures/emails/no_content_reply.eml deleted file mode 100644 index 95eb2055ce..0000000000 --- a/spec/fixtures/emails/no_content_reply.eml +++ /dev/null @@ -1,34 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> \ No newline at end of file diff --git a/spec/fixtures/emails/no_return_path.eml b/spec/fixtures/emails/no_return_path.eml new file mode 100644 index 0000000000..3fae2e6bc8 --- /dev/null +++ b/spec/fixtures/emails/no_return_path.eml @@ -0,0 +1,7 @@ +From: Foo Bar +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <1@foo.bar.mail> +Precedence: list +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit diff --git a/spec/fixtures/emails/on_date_contact_wrote.eml b/spec/fixtures/emails/on_date_contact_wrote.eml new file mode 100644 index 0000000000..33f0527d93 --- /dev/null +++ b/spec/fixtures/emails/on_date_contact_wrote.eml @@ -0,0 +1,19 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <20@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 + +This is the actual reply. + +On Tue, Jan 14, 2016 at 0:42 AM, Bar Foo wrote: + +> This is the previous email. +> And it had +> +> a lot +> +> +> of lines ;) diff --git a/spec/fixtures/emails/on_wrote.eml b/spec/fixtures/emails/on_wrote.eml deleted file mode 100644 index feb59bd27b..0000000000 --- a/spec/fixtures/emails/on_wrote.eml +++ /dev/null @@ -1,277 +0,0 @@ - -MIME-Version: 1.0 -Received: by 10.107.9.17 with HTTP; Tue, 9 Sep 2014 16:18:19 -0700 (PDT) -In-Reply-To: <540f16d4c08d9_4a3f9ff6d61890391c@tiefighter4-meta.mail> -References: - <540f16d4c08d9_4a3f9ff6d61890391c@tiefighter4-meta.mail> -Date: Tue, 9 Sep 2014 16:18:19 -0700 -Delivered-To: kanepyork@gmail.com -Message-ID: -Subject: Re: [Discourse Meta] Badge icons - where to find them? -From: Kane York -To: Discourse Meta -Content-Type: multipart/alternative; boundary=001a11c34c389e728f0502aa26a0 - ---001a11c34c389e728f0502aa26a0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: quoted-printable - -Sure, all you need to do is frobnicate the foobar and you'll be all set! - -On Tue, Sep 9, 2014 at 8:03 AM, gordon_ryan wrote: - -> gordon_ryan -> September 9 -> -> @riking - willing to step by -> step of the custom icon method for an admittedly ignorant admin? Seriousl= -y -> confused. -> -> Or anyone else who knows how to do this [image: smiley] -> -> To respond, reply to this email or visit -> https://meta.discourse.org/t/badge-icons-where-to-find-them/18058/9 in -> your browser. -> ------------------------------ -> Previous Replies riking -> July 25 -> -> Check out the "HTML Head" section in the "Content" tab of the admin panel= -. -> meglio -> July 25 -> -> How will it load the related custom font? -> riking -> July 25 -> -> Here's an example of the styles that FA applies. I'll use fa-heart"> as the example. -> -> .fa { -> display: inline-block; -> font-family: FontAwesome; -> font-style: normal; -> font-weight: normal; -> line-height: 1; -> -webkit-font-smoothing: antialiased; -> -moz-osx-font-smoothing: grayscale; -> } -> .fa-heart:before { -> content: "\f004"; -> } -> -> So you could do this in your site stylesheet: -> -> .fa-custom-burger:before { -> content: "\01f354"; -> font-family: inherit; -> } -> -> And get =F0=9F=8D=94 as your badge icon when you enter custom-burger. -> ------------------------------ -> -> To respond, reply to this email or visit -> https://meta.discourse.org/t/badge-icons-where-to-find-them/18058/9 in -> your browser. -> -> To unsubscribe from these emails, visit your user preferences -> . -> - ---001a11c34c389e728f0502aa26a0 -Content-Type: text/html; charset=UTF-8 -Content-Transfer-Encoding: quoted-printable - -
    Sure, all you need to do is frobnicate the foobar and you'll be all s= -et!


    = -
    On Tue, Sep 9, 2014 at 8:03 AM, gordon_ryan = -<info@discourse.org> wrote:
    - - - - - - - - - - - - -
    - - - gordon_ryan
    - September 9 -
    -

    @riking- willing to step by step of the custom icon me= -thod for an admittedly ignorant admin? Seriously confused.

    - -

    Or anyone else who knows how to do this = -3D"smiley"

    -
    - - -
    -

    To respond, reply to this email or visit https:= -//meta.discourse.org/t/badge-icons-where-to-find-them/18058/9 in your b= -rowser.

    -
    -
    -

    Previous Replies

    - - - - - - - - - - - -
    - - - riking
    - July 25 -

    Check out the "HTML Head" section in the "Content&= -quot; tab of the admin panel.

    - - - - - - - - - - - -
    - - - meglio
    - July 25 -

    How will it load the related custom font?

    - - - - - - - - - - - -
    - - - riking
    - July 25 -
    -

    Here's an example of the styles that= - FA applies. I'll use <i class=3D"fa fa-heart"></i> as the e= -xample.

    - -

    -
    .fa {
    -  display: inline-block;
    -  font-family: FontAwesome;
    -  font-style: normal;
    -  font-weight: normal;
    -  line-height: 1;
    -  -webkit-font-smoothing: antialiased;
    -  -moz-osx-font-smoothing: grayscale;
    -}
    -.fa-heart:before {
    -  content: "\f004";
    -}
    - -

    So you could do this in your site styles= -heet:

    - -

    -
    .fa-custom-burger:before {
    -  content: "\01f354";
    -  font-family: inherit;
    -}
    - -

    And get =F0=9F=8D=94 as your badge icon = -when you enter cus= -tom-burger.

    -
    - - -
    - -
    -

    To respond, reply to this email or visit https://me= -ta.discourse.org/t/badge-icons-where-to-find-them/18058/9 in your brows= -er.

    -
    -
    -

    To unsubscribe from these emails, visit your user preferences.

    -
    -
    -

    - ---001a11c34c389e728f0502aa26a0-- \ No newline at end of file diff --git a/spec/fixtures/emails/original_message.eml b/spec/fixtures/emails/original_message.eml new file mode 100644 index 0000000000..8dbc5d1820 --- /dev/null +++ b/spec/fixtures/emails/original_message.eml @@ -0,0 +1,12 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <27@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 + +This is a reply :) + +---Original Message--- +This part should not be included diff --git a/spec/fixtures/emails/original_message_context.eml b/spec/fixtures/emails/original_message_context.eml deleted file mode 100644 index 31088c16e6..0000000000 --- a/spec/fixtures/emails/original_message_context.eml +++ /dev/null @@ -1,30 +0,0 @@ -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. -----Original Message---- - -Context here. - -> 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/outlook.eml b/spec/fixtures/emails/outlook.eml deleted file mode 100644 index fb1f590a30..0000000000 --- a/spec/fixtures/emails/outlook.eml +++ /dev/null @@ -1,188 +0,0 @@ - -MIME-Version: 1.0 -Received: by 10.25.161.144 with HTTP; Tue, 7 Oct 2014 22:17:17 -0700 (PDT) -X-Originating-IP: [117.207.85.84] -In-Reply-To: <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail> -References: - <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail> -Date: Wed, 8 Oct 2014 10:47:17 +0530 -Delivered-To: arpit@techapj.com -Message-ID: -Subject: Re: [Discourse] [Meta] Welcome to techAPJ's Discourse! -From: Arpit Jalan -To: Discourse Accept-Language: en-US -Content-Language: en-US -X-MS-Has-Attach: -X-MS-TNEF-Correlator: -x-originating-ip: [134.68.31.227] -Content-Type: multipart/alternative; - boundary="_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_" -MIME-Version: 1.0 - ---_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_ -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: base64 - -TWljcm9zb2Z0IE91dGxvb2sgMjAxMA0KDQpGcm9tOiBtaWNoYWVsIFttYWlsdG86dGFsa0BvcGVu -bXJzLm9yZ10NClNlbnQ6IE1vbmRheSwgT2N0b2JlciAxMywgMjAxNCA5OjM4IEFNDQpUbzogUG93 -ZXIsIENocmlzDQpTdWJqZWN0OiBbUE1dIFlvdXIgcG9zdCBpbiAiQnVyZ2VyaGF1czogTmV3IHJl -c3RhdXJhbnQgLyBsdW5jaCB2ZW51ZSINCg0KDQptaWNoYWVsPGh0dHA6Ly9jbC5vcGVubXJzLm9y -Zy90cmFjay9jbGljay8zMDAzOTkwNS90YWxrLm9wZW5tcnMub3JnP3A9ZXlKeklqb2liR2xaYTFW -MGVYaENZMDFNUlRGc1VESm1ZelZRTTBabGVqRTRJaXdpZGlJNk1Td2ljQ0k2SW50Y0luVmNJam96 -TURBek9Ua3dOU3hjSW5aY0lqb3hMRndpZFhKc1hDSTZYQ0pvZEhSd2N6cGNYRnd2WEZ4Y0wzUmhi -R3N1YjNCbGJtMXljeTV2Y21kY1hGd3ZkWE5sY25OY1hGd3ZiV2xqYUdGbGJGd2lMRndpYVdSY0lq -cGNJbVExWW1Nd04yTmtORFJqWkRRNE1HTTRZVGcyTXpsalpXSTFOemd6WW1ZMlhDSXNYQ0oxY214 -ZmFXUnpYQ0k2VzF3aVlqaGtPRGcxTWprNU56ZG1aalkxWldZeU5URTNPV1JpTkdZeU1XSTNOekZq -TnpoalpqaGtPRndpWFgwaWZRPg0KT2N0b2JlciAxMw0KDQpodHRwczovL3RhbGsub3Blbm1ycy5v -cmcvdC9idXJnZXJoYXVzLW5ldy1yZXN0YXVyYW50LWx1bmNoLXZlbnVlLzY3Mi8zPGh0dHA6Ly9j -bC5vcGVubXJzLm9yZy90cmFjay9jbGljay8zMDAzOTkwNS90YWxrLm9wZW5tcnMub3JnP3A9ZXlK -eklqb2lVRVJJU1VOeVIzbFZNRGRCVlZocFduUjNXV3g0TVdOc1RXNVpJaXdpZGlJNk1Td2ljQ0k2 -SW50Y0luVmNJam96TURBek9Ua3dOU3hjSW5aY0lqb3hMRndpZFhKc1hDSTZYQ0pvZEhSd2N6cGNY -Rnd2WEZ4Y0wzUmhiR3N1YjNCbGJtMXljeTV2Y21kY1hGd3ZkRnhjWEM5aWRYSm5aWEpvWVhWekxX -NWxkeTF5WlhOMFlYVnlZVzUwTFd4MWJtTm9MWFpsYm5WbFhGeGNMelkzTWx4Y1hDOHpYQ0lzWENK -cFpGd2lPbHdpWkRWaVl6QTNZMlEwTkdOa05EZ3dZemhoT0RZek9XTmxZalUzT0ROaVpqWmNJaXhj -SW5WeWJGOXBaSE5jSWpwYlhDSmlOelppWWprMFpURmlOekk1WlRrMlpUUmxaV000TkdSbU1qUTRN -RE13WWpZeVlXWXlNR00wWENKZGZTSjk+DQoNCkxvb2tzIGxpa2UgeW91ciByZXBseS1ieS1lbWFp -bCB3YXNuJ3QgcHJvY2Vzc2VkIGNvcnJlY3RseSBieSBvdXIgc29mdHdhcmUuIENhbiB5b3UgbGV0 -IG1lIGtub3cgd2hhdCB2ZXJzaW9uL09TIG9mIHdoYXQgZW1haWwgcHJvZ3JhbSB5b3UncmUgdXNp -bmc/IFdlIHdpbGwgd2FudCB0byB0cnkgdG8gZml4IHRoZSBidWcuIDpzbWlsZToNCg0KVGhhbmtz -IQ0KDQoNCl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQoNClRvIHJlc3BvbmQsIHJl -cGx5IHRvIHRoaXMgZW1haWwgb3IgdmlzaXQgaHR0cHM6Ly90YWxrLm9wZW5tcnMub3JnL3QveW91 -ci1wb3N0LWluLWJ1cmdlcmhhdXMtbmV3LXJlc3RhdXJhbnQtbHVuY2gtdmVudWUvNjc0LzE8aHR0 -cDovL2NsLm9wZW5tcnMub3JnL3RyYWNrL2NsaWNrLzMwMDM5OTA1L3RhbGsub3Blbm1ycy5vcmc/ -cD1leUp6SWpvaWVYaDJWbnBGTUhSMU1uRm5aRWR1TlhFd01GcFFPVlp0VFZvNElpd2lkaUk2TVN3 -aWNDSTZJbnRjSW5WY0lqb3pNREF6T1Rrd05TeGNJblpjSWpveExGd2lkWEpzWENJNlhDSm9kSFJ3 -Y3pwY1hGd3ZYRnhjTDNSaGJHc3ViM0JsYm0xeWN5NXZjbWRjWEZ3dmRGeGNYQzk1YjNWeUxYQnZj -M1F0YVc0dFluVnlaMlZ5YUdGMWN5MXVaWGN0Y21WemRHRjFjbUZ1ZEMxc2RXNWphQzEyWlc1MVpW -eGNYQzgyTnpSY1hGd3ZNVndpTEZ3aWFXUmNJanBjSW1RMVltTXdOMk5rTkRSalpEUTRNR000WVRn -Mk16bGpaV0kxTnpnelltWTJYQ0lzWENKMWNteGZhV1J6WENJNlcxd2lZamMyWW1JNU5HVXhZamN5 -T1dVNU5tVTBaV1ZqT0RSa1pqSTBPREF6TUdJMk1tRm1NakJqTkZ3aVhYMGlmUT4gaW4geW91ciBi -cm93c2VyLg0KDQpUbyB1bnN1YnNjcmliZSBmcm9tIHRoZXNlIGVtYWlscywgdmlzaXQgeW91ciB1 -c2VyIHByZWZlcmVuY2VzPGh0dHA6Ly9jbC5vcGVubXJzLm9yZy90cmFjay9jbGljay8zMDAzOTkw -NS90YWxrLm9wZW5tcnMub3JnP3A9ZXlKeklqb2lkVXh1V2xnNVZGYzBPV1pXUzBZNGJGZExkbWx5 -V0dzeFRWOXpJaXdpZGlJNk1Td2ljQ0k2SW50Y0luVmNJam96TURBek9Ua3dOU3hjSW5aY0lqb3hM -RndpZFhKc1hDSTZYQ0pvZEhSd2N6cGNYRnd2WEZ4Y0wzUmhiR3N1YjNCbGJtMXljeTV2Y21kY1hG -d3ZiWGxjWEZ3dmNISmxabVZ5Wlc1alpYTmNJaXhjSW1sa1hDSTZYQ0prTldKak1EZGpaRFEwWTJR -ME9EQmpPR0U0TmpNNVkyVmlOVGM0TTJKbU5sd2lMRndpZFhKc1gybGtjMXdpT2x0Y0ltSTRNV1V3 -WmpBMU5EWTVORE0wTnpneU0yRm1NakEyTmpGalpqYzNaR05pTjJOaFl6ZG1NakpjSWwxOUluMD4u -DQoNCg== - ---_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_ -Content-Type: text/html; charset="utf-8" -Content-Transfer-Encoding: base64 - -PGh0bWwgeG1sbnM6dj0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwiIHhtbG5zOm89InVy -bjpzY2hlbWFzLW1pY3Jvc29mdC1jb206b2ZmaWNlOm9mZmljZSIgeG1sbnM6dz0idXJuOnNjaGVt -YXMtbWljcm9zb2Z0LWNvbTpvZmZpY2U6d29yZCIgeG1sbnM6bT0iaHR0cDovL3NjaGVtYXMubWlj -cm9zb2Z0LmNvbS9vZmZpY2UvMjAwNC8xMi9vbW1sIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv -VFIvUkVDLWh0bWw0MCI+DQo8aGVhZD4NCjxtZXRhIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIg -Y29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04Ij4NCjxtZXRhIG5hbWU9IkdlbmVyYXRv -ciIgY29udGVudD0iTWljcm9zb2Z0IFdvcmQgMTQgKGZpbHRlcmVkIG1lZGl1bSkiPg0KPCEtLVtp -ZiAhbXNvXT48c3R5bGU+dlw6KiB7YmVoYXZpb3I6dXJsKCNkZWZhdWx0I1ZNTCk7fQ0Kb1w6KiB7 -YmVoYXZpb3I6dXJsKCNkZWZhdWx0I1ZNTCk7fQ0Kd1w6KiB7YmVoYXZpb3I6dXJsKCNkZWZhdWx0 -I1ZNTCk7fQ0KLnNoYXBlIHtiZWhhdmlvcjp1cmwoI2RlZmF1bHQjVk1MKTt9DQo8L3N0eWxlPjwh -W2VuZGlmXS0tPjxzdHlsZT48IS0tDQovKiBGb250IERlZmluaXRpb25zICovDQpAZm9udC1mYWNl -DQoJe2ZvbnQtZmFtaWx5OkNhbGlicmk7DQoJcGFub3NlLTE6MiAxNSA1IDIgMiAyIDQgMyAyIDQ7 -fQ0KQGZvbnQtZmFjZQ0KCXtmb250LWZhbWlseTpUYWhvbWE7DQoJcGFub3NlLTE6MiAxMSA2IDQg -MyA1IDQgNCAyIDQ7fQ0KLyogU3R5bGUgRGVmaW5pdGlvbnMgKi8NCnAuTXNvTm9ybWFsLCBsaS5N -c29Ob3JtYWwsIGRpdi5Nc29Ob3JtYWwNCgl7bWFyZ2luOjBpbjsNCgltYXJnaW4tYm90dG9tOi4w -MDAxcHQ7DQoJZm9udC1zaXplOjEyLjBwdDsNCglmb250LWZhbWlseToiVGltZXMgTmV3IFJvbWFu -Iiwic2VyaWYiO30NCmE6bGluaywgc3Bhbi5Nc29IeXBlcmxpbmsNCgl7bXNvLXN0eWxlLXByaW9y -aXR5Ojk5Ow0KCWNvbG9yOmJsdWU7DQoJdGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZTt9DQphOnZp -c2l0ZWQsIHNwYW4uTXNvSHlwZXJsaW5rRm9sbG93ZWQNCgl7bXNvLXN0eWxlLXByaW9yaXR5Ojk5 -Ow0KCWNvbG9yOnB1cnBsZTsNCgl0ZXh0LWRlY29yYXRpb246dW5kZXJsaW5lO30NCnANCgl7bXNv -LXN0eWxlLXByaW9yaXR5Ojk5Ow0KCW1zby1tYXJnaW4tdG9wLWFsdDphdXRvOw0KCW1hcmdpbi1y -aWdodDowaW47DQoJbXNvLW1hcmdpbi1ib3R0b20tYWx0OmF1dG87DQoJbWFyZ2luLWxlZnQ6MGlu -Ow0KCWZvbnQtc2l6ZToxMi4wcHQ7DQoJZm9udC1mYW1pbHk6IlRpbWVzIE5ldyBSb21hbiIsInNl -cmlmIjt9DQpzcGFuLkVtYWlsU3R5bGUxOA0KCXttc28tc3R5bGUtdHlwZTpwZXJzb25hbC1yZXBs -eTsNCglmb250LWZhbWlseToiQ2FsaWJyaSIsInNhbnMtc2VyaWYiOw0KCWNvbG9yOiMxRjQ5N0Q7 -fQ0KLk1zb0NocERlZmF1bHQNCgl7bXNvLXN0eWxlLXR5cGU6ZXhwb3J0LW9ubHk7DQoJZm9udC1m -YW1pbHk6IkNhbGlicmkiLCJzYW5zLXNlcmlmIjt9DQpAcGFnZSBXb3JkU2VjdGlvbjENCgl7c2l6 -ZTo4LjVpbiAxMS4waW47DQoJbWFyZ2luOjEuMGluIDEuMGluIDEuMGluIDEuMGluO30NCmRpdi5X -b3JkU2VjdGlvbjENCgl7cGFnZTpXb3JkU2VjdGlvbjE7fQ0KLS0+PC9zdHlsZT48IS0tW2lmIGd0 -ZSBtc28gOV0+PHhtbD4NCjxvOnNoYXBlZGVmYXVsdHMgdjpleHQ9ImVkaXQiIHNwaWRtYXg9IjEw -MjYiIC8+DQo8L3htbD48IVtlbmRpZl0tLT48IS0tW2lmIGd0ZSBtc28gOV0+PHhtbD4NCjxvOnNo -YXBlbGF5b3V0IHY6ZXh0PSJlZGl0Ij4NCjxvOmlkbWFwIHY6ZXh0PSJlZGl0IiBkYXRhPSIxIiAv -Pg0KPC9vOnNoYXBlbGF5b3V0PjwveG1sPjwhW2VuZGlmXS0tPg0KPC9oZWFkPg0KPGJvZHkgbGFu -Zz0iRU4tVVMiIGxpbms9ImJsdWUiIHZsaW5rPSJwdXJwbGUiPg0KPGRpdiBjbGFzcz0iV29yZFNl -Y3Rpb24xIj4NCjxwIGNsYXNzPSJNc29Ob3JtYWwiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6MTEu -MHB0O2ZvbnQtZmFtaWx5OiZxdW90O0NhbGlicmkmcXVvdDssJnF1b3Q7c2Fucy1zZXJpZiZxdW90 -Oztjb2xvcjojMUY0OTdEIj5NaWNyb3NvZnQgT3V0bG9vayAyMDEwPG86cD48L286cD48L3NwYW4+ -PC9wPg0KPHAgY2xhc3M9Ik1zb05vcm1hbCI+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZToxMS4wcHQ7 -Zm9udC1mYW1pbHk6JnF1b3Q7Q2FsaWJyaSZxdW90OywmcXVvdDtzYW5zLXNlcmlmJnF1b3Q7O2Nv -bG9yOiMxRjQ5N0QiPjxvOnA+Jm5ic3A7PC9vOnA+PC9zcGFuPjwvcD4NCjxwIGNsYXNzPSJNc29O -b3JtYWwiPjxiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6MTAuMHB0O2ZvbnQtZmFtaWx5OiZxdW90 -O1RhaG9tYSZxdW90OywmcXVvdDtzYW5zLXNlcmlmJnF1b3Q7Ij5Gcm9tOjwvc3Bhbj48L2I+PHNw -YW4gc3R5bGU9ImZvbnQtc2l6ZToxMC4wcHQ7Zm9udC1mYW1pbHk6JnF1b3Q7VGFob21hJnF1b3Q7 -LCZxdW90O3NhbnMtc2VyaWYmcXVvdDsiPiBtaWNoYWVsIFttYWlsdG86dGFsa0BvcGVubXJzLm9y -Z10NCjxicj4NCjxiPlNlbnQ6PC9iPiBNb25kYXksIE9jdG9iZXIgMTMsIDIwMTQgOTozOCBBTTxi -cj4NCjxiPlRvOjwvYj4gUG93ZXIsIENocmlzPGJyPg0KPGI+U3ViamVjdDo8L2I+IFtQTV0gWW91 -ciBwb3N0IGluICZxdW90O0J1cmdlcmhhdXM6IE5ldyByZXN0YXVyYW50IC8gbHVuY2ggdmVudWUm -cXVvdDs8bzpwPjwvbzpwPjwvc3Bhbj48L3A+DQo8cCBjbGFzcz0iTXNvTm9ybWFsIj48bzpwPiZu -YnNwOzwvbzpwPjwvcD4NCjxkaXY+DQo8dGFibGUgY2xhc3M9Ik1zb05vcm1hbFRhYmxlIiBib3Jk -ZXI9IjAiIGNlbGxzcGFjaW5nPSIwIiBjZWxscGFkZGluZz0iMCI+DQo8dGJvZHk+DQo8dHI+DQo8 -dGQgdmFsaWduPSJ0b3AiIHN0eWxlPSJwYWRkaW5nOjBpbiAwaW4gMGluIDBpbiI+PC90ZD4NCjx0 -ZCBzdHlsZT0icGFkZGluZzowaW4gMGluIDBpbiAwaW4iPg0KPHAgY2xhc3M9Ik1zb05vcm1hbCIg -c3R5bGU9Im1hcmdpbi1ib3R0b206MTguNzVwdCI+PGEgaHJlZj0iaHR0cDovL2NsLm9wZW5tcnMu -b3JnL3RyYWNrL2NsaWNrLzMwMDM5OTA1L3RhbGsub3Blbm1ycy5vcmc/cD1leUp6SWpvaWJHbFph -MVYwZVhoQ1kwMU1SVEZzVURKbVl6VlFNMFpsZWpFNElpd2lkaUk2TVN3aWNDSTZJbnRjSW5WY0lq -b3pNREF6T1Rrd05TeGNJblpjSWpveExGd2lkWEpzWENJNlhDSm9kSFJ3Y3pwY1hGd3ZYRnhjTDNS -aGJHc3ViM0JsYm0xeWN5NXZjbWRjWEZ3dmRYTmxjbk5jWEZ3dmJXbGphR0ZsYkZ3aUxGd2lhV1Jj -SWpwY0ltUTFZbU13TjJOa05EUmpaRFE0TUdNNFlUZzJNemxqWldJMU56Z3pZbVkyWENJc1hDSjFj -bXhmYVdSelhDSTZXMXdpWWpoa09EZzFNams1TnpkbVpqWTFaV1l5TlRFM09XUmlOR1l5TVdJM056 -RmpOemhqWmpoa09Gd2lYWDBpZlEiIHRhcmdldD0iX2JsYW5rIj48Yj48c3BhbiBzdHlsZT0iZm9u -dC1zaXplOjEwLjBwdDtmb250LWZhbWlseTomcXVvdDtUYWhvbWEmcXVvdDssJnF1b3Q7c2Fucy1z -ZXJpZiZxdW90Oztjb2xvcjojMDA2Njk5O3RleHQtZGVjb3JhdGlvbjpub25lIj5taWNoYWVsPC9z -cGFuPjwvYj48L2E+PGJyPg0KPHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTo4LjVwdDtmb250LWZhbWls -eTomcXVvdDtUYWhvbWEmcXVvdDssJnF1b3Q7c2Fucy1zZXJpZiZxdW90Oztjb2xvcjojOTk5OTk5 -Ij5PY3RvYmVyIDEzPC9zcGFuPg0KPG86cD48L286cD48L3A+DQo8L3RkPg0KPC90cj4NCjx0cj4N -Cjx0ZCBjb2xzcGFuPSIyIiBzdHlsZT0icGFkZGluZzozLjc1cHQgMGluIDBpbiAwaW4iPg0KPHAg -Y2xhc3M9Ik1zb05vcm1hbCIgc3R5bGU9Im1hcmdpbi1ib3R0b206MTguNzVwdCI+PGEgaHJlZj0i -aHR0cDovL2NsLm9wZW5tcnMub3JnL3RyYWNrL2NsaWNrLzMwMDM5OTA1L3RhbGsub3Blbm1ycy5v -cmc/cD1leUp6SWpvaVVFUklTVU55UjNsVk1EZEJWVmhwV25SM1dXeDRNV05zVFc1Wklpd2lkaUk2 -TVN3aWNDSTZJbnRjSW5WY0lqb3pNREF6T1Rrd05TeGNJblpjSWpveExGd2lkWEpzWENJNlhDSm9k -SFJ3Y3pwY1hGd3ZYRnhjTDNSaGJHc3ViM0JsYm0xeWN5NXZjbWRjWEZ3dmRGeGNYQzlpZFhKblpY -Sm9ZWFZ6TFc1bGR5MXlaWE4wWVhWeVlXNTBMV3gxYm1Ob0xYWmxiblZsWEZ4Y0x6WTNNbHhjWEM4 -elhDSXNYQ0pwWkZ3aU9sd2laRFZpWXpBM1kyUTBOR05rTkRnd1l6aGhPRFl6T1dObFlqVTNPRE5p -WmpaY0lpeGNJblZ5YkY5cFpITmNJanBiWENKaU56WmlZamswWlRGaU56STVaVGsyWlRSbFpXTTRO -R1JtTWpRNE1ETXdZall5WVdZeU1HTTBYQ0pkZlNKOSI+PGI+PHNwYW4gc3R5bGU9ImNvbG9yOiMw -MDY2OTk7dGV4dC1kZWNvcmF0aW9uOm5vbmUiPmh0dHBzOi8vdGFsay5vcGVubXJzLm9yZy90L2J1 -cmdlcmhhdXMtbmV3LXJlc3RhdXJhbnQtbHVuY2gtdmVudWUvNjcyLzM8L3NwYW4+PC9iPjwvYT4N -CjxvOnA+PC9vOnA+PC9wPg0KPHAgc3R5bGU9Im1hcmdpbi10b3A6MGluIj5Mb29rcyBsaWtlIHlv -dXIgcmVwbHktYnktZW1haWwgd2Fzbid0IHByb2Nlc3NlZCBjb3JyZWN0bHkgYnkgb3VyIHNvZnR3 -YXJlLiBDYW4geW91IGxldCBtZSBrbm93IHdoYXQgdmVyc2lvbi9PUyBvZiB3aGF0IGVtYWlsIHBy -b2dyYW0geW91J3JlIHVzaW5nPyBXZSB3aWxsIHdhbnQgdG8gdHJ5IHRvIGZpeCB0aGUgYnVnLiA6 -c21pbGU6PG86cD48L286cD48L3A+DQo8cCBzdHlsZT0ibWFyZ2luLXRvcDowaW4iPlRoYW5rcyE8 -bzpwPjwvbzpwPjwvcD4NCjwvdGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+DQo8ZGl2IGNs -YXNzPSJNc29Ob3JtYWwiIGFsaWduPSJjZW50ZXIiIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlciI+ -DQo8aHIgc2l6ZT0iMSIgd2lkdGg9IjEwMCUiIGFsaWduPSJjZW50ZXIiPg0KPC9kaXY+DQo8ZGl2 -Pg0KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiM2NjY2NjYiPlRvIHJlc3BvbmQsIHJlcGx5IHRvIHRo -aXMgZW1haWwgb3IgdmlzaXQgPGEgaHJlZj0iaHR0cDovL2NsLm9wZW5tcnMub3JnL3RyYWNrL2Ns -aWNrLzMwMDM5OTA1L3RhbGsub3Blbm1ycy5vcmc/cD1leUp6SWpvaWVYaDJWbnBGTUhSMU1uRm5a -RWR1TlhFd01GcFFPVlp0VFZvNElpd2lkaUk2TVN3aWNDSTZJbnRjSW5WY0lqb3pNREF6T1Rrd05T -eGNJblpjSWpveExGd2lkWEpzWENJNlhDSm9kSFJ3Y3pwY1hGd3ZYRnhjTDNSaGJHc3ViM0JsYm0x -eWN5NXZjbWRjWEZ3dmRGeGNYQzk1YjNWeUxYQnZjM1F0YVc0dFluVnlaMlZ5YUdGMWN5MXVaWGN0 -Y21WemRHRjFjbUZ1ZEMxc2RXNWphQzEyWlc1MVpWeGNYQzgyTnpSY1hGd3ZNVndpTEZ3aWFXUmNJ -anBjSW1RMVltTXdOMk5rTkRSalpEUTRNR000WVRnMk16bGpaV0kxTnpnelltWTJYQ0lzWENKMWNt -eGZhV1J6WENJNlcxd2lZamMyWW1JNU5HVXhZamN5T1dVNU5tVTBaV1ZqT0RSa1pqSTBPREF6TUdJ -Mk1tRm1NakJqTkZ3aVhYMGlmUSI+DQo8Yj48c3BhbiBzdHlsZT0iY29sb3I6IzAwNjY5OTt0ZXh0 -LWRlY29yYXRpb246bm9uZSI+aHR0cHM6Ly90YWxrLm9wZW5tcnMub3JnL3QveW91ci1wb3N0LWlu -LWJ1cmdlcmhhdXMtbmV3LXJlc3RhdXJhbnQtbHVuY2gtdmVudWUvNjc0LzE8L3NwYW4+PC9iPjwv -YT4gaW4geW91ciBicm93c2VyLjxvOnA+PC9vOnA+PC9zcGFuPjwvcD4NCjwvZGl2Pg0KPGRpdj4N -CjxwPjxzcGFuIHN0eWxlPSJjb2xvcjojNjY2NjY2Ij5UbyB1bnN1YnNjcmliZSBmcm9tIHRoZXNl -IGVtYWlscywgdmlzaXQgeW91ciA8YSBocmVmPSJodHRwOi8vY2wub3Blbm1ycy5vcmcvdHJhY2sv -Y2xpY2svMzAwMzk5MDUvdGFsay5vcGVubXJzLm9yZz9wPWV5SnpJam9pZFV4dVdsZzVWRmMwT1da -V1MwWTRiRmRMZG1seVdHc3hUVjl6SWl3aWRpSTZNU3dpY0NJNkludGNJblZjSWpvek1EQXpPVGt3 -TlN4Y0luWmNJam94TEZ3aWRYSnNYQ0k2WENKb2RIUndjenBjWEZ3dlhGeGNMM1JoYkdzdWIzQmxi -bTF5Y3k1dmNtZGNYRnd2YlhsY1hGd3ZjSEpsWm1WeVpXNWpaWE5jSWl4Y0ltbGtYQ0k2WENKa05X -SmpNRGRqWkRRMFkyUTBPREJqT0dFNE5qTTVZMlZpTlRjNE0ySm1ObHdpTEZ3aWRYSnNYMmxrYzF3 -aU9sdGNJbUk0TVdVd1pqQTFORFk1TkRNME56Z3lNMkZtTWpBMk5qRmpaamMzWkdOaU4yTmhZemRt -TWpKY0lsMTlJbjAiPg0KPGI+PHNwYW4gc3R5bGU9ImNvbG9yOiMwMDY2OTk7dGV4dC1kZWNvcmF0 -aW9uOm5vbmUiPnVzZXIgcHJlZmVyZW5jZXM8L3NwYW4+PC9iPjwvYT4uPG86cD48L286cD48L3Nw -YW4+PC9wPg0KPC9kaXY+DQo8L2Rpdj4NCjxwIGNsYXNzPSJNc29Ob3JtYWwiPjxpbWcgYm9yZGVy -PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBpZD0iX3gwMDAwX2kxMDI2IiBzcmM9Imh0dHA6Ly9j -bC5vcGVubXJzLm9yZy90cmFjay9vcGVuLnBocD91PTMwMDM5OTA1JmFtcDtpZD1kNWJjMDdjZDQ0 -Y2Q0ODBjOGE4NjM5Y2ViNTc4M2JmNiI+PG86cD48L286cD48L3A+DQo8L2Rpdj4NCjwvYm9keT4N -CjwvaHRtbD4NCg== - ---_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_-- diff --git a/spec/fixtures/emails/paragraphs.cooked b/spec/fixtures/emails/paragraphs.cooked deleted file mode 100644 index 2d44722107..0000000000 --- a/spec/fixtures/emails/paragraphs.cooked +++ /dev/null @@ -1,7 +0,0 @@ -

    Is there any reason the old candy can't be be kept in silos while the new candy -is imported into new silos?

    - -

    The thing about candy is it stays delicious for a long time -- we can just keep -it there without worrying about it too much, imo.

    - -

    Thanks for listening.

    diff --git a/spec/fixtures/emails/paragraphs.eml b/spec/fixtures/emails/paragraphs.eml index 2d5b5283f7..7fb2bd3733 100644 --- a/spec/fixtures/emails/paragraphs.eml +++ b/spec/fixtures/emails/paragraphs.eml @@ -1,42 +1,12 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <22@foo.bar.mail> Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 +Content-Type: text/plain; charset=UTF-8 -Is there any reason the *old* candy can't be be kept in silos while the new candy -is imported into *new* silos? +Do you like liquorice? -The thing about candy is it stays delicious for a long time -- we can just keep -it there without worrying about it too much, imo. - -Thanks for listening. - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> +I really like them. One could even say that I am *addicted* to liquorice. Anf if +you can mix it up with some anise, then I'm in heaven ;) diff --git a/spec/fixtures/emails/plus_one.eml b/spec/fixtures/emails/plus_one.eml deleted file mode 100644 index a3255e9699..0000000000 --- a/spec/fixtures/emails/plus_one.eml +++ /dev/null @@ -1,37 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: FROM -To: TO -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -+1 - - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> diff --git a/spec/fixtures/emails/previous.eml b/spec/fixtures/emails/previous.eml deleted file mode 100644 index 24ac5a63de..0000000000 --- a/spec/fixtures/emails/previous.eml +++ /dev/null @@ -1,38 +0,0 @@ - -Delivered-To: discourse-reply+cd480e301683c9902891f15968bf07a5@discourse.org -Received: by 10.194.216.104 with SMTP id op8csp80593wjc; - Wed, 24 Jul 2013 07:59:14 -0700 (PDT) -Return-Path: -References: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -From: Walter White -In-Reply-To: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -Mime-Version: 1.0 (1.0) -Date: Wed, 24 Jul 2013 15:59:10 +0100 -Message-ID: <4597127794206131679@unknownmsgid> -Subject: Re: [Discourse] new reply to your post in 'Crystal Blue' -To: walter via Discourse -Content-Type: multipart/alternative; boundary=001a11c20edc15a39304e2432790 - -This will not include the previous discussion that is present in this email. - ------------------------------ -Previous discussion -skylerwhite July 24 - -This is a reply. - fring July 24 - -This is an older reply. - hank_schrader July 24 - -Of course another reply here. - walterwhite July 24 - - ------------------------------ - -To respond, reply to this email or visit -http://discourse.org/t/crystal-blue/5043/10in -your browser. - -To unsubscribe from these emails, visit your user -preferences -. diff --git a/spec/fixtures/emails/previous_replies.eml b/spec/fixtures/emails/previous_replies.eml index 3fd74482c0..8b4cbc7e77 100644 --- a/spec/fixtures/emails/previous_replies.eml +++ b/spec/fixtures/emails/previous_replies.eml @@ -1,180 +1,23 @@ -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 +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <21@foo.bar.mail> +Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 -### this is a reply from iOS Gmail app +This will not include the previous discussion that is present in this email. -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. +--- +*Previous Replies* -This is **bold** text in Markdown. +This is previous reply #1. -This is a link to http://example.com +Posted by foo bar on 01/15/2016 -On Friday, November 28, 2014, Arpit Jalan wrote: +--- +[Visit the Topic](https://bar.com/t/wat/1/1) to respond. -> 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-- +To stop receiving notifications for this particular topic, [click here](h= +ttps://bar.com/t/wat/1/unsubscribe). To unsubscribe from these emails, ch= +ange your [user preferences](https://bar.com/my/preferences). diff --git a/spec/fixtures/emails/readonly.eml b/spec/fixtures/emails/readonly.eml new file mode 100644 index 0000000000..7572bff668 --- /dev/null +++ b/spec/fixtures/emails/readonly.eml @@ -0,0 +1,11 @@ +Return-Path: +From: Foo Bar +To: category@bar.com +Subject: This is a topic from a restricted user +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <33@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hey, this is a topic from a restricted user ;) diff --git a/spec/fixtures/emails/reply_user_matching.eml b/spec/fixtures/emails/reply_user_matching.eml new file mode 100644 index 0000000000..caead84676 --- /dev/null +++ b/spec/fixtures/emails/reply_user_matching.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <11@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/spec/fixtures/emails/reply_user_not_matching.eml b/spec/fixtures/emails/reply_user_not_matching.eml new file mode 100644 index 0000000000..c6523f966e --- /dev/null +++ b/spec/fixtures/emails/reply_user_not_matching.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <10@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/spec/fixtures/emails/signature.eml b/spec/fixtures/emails/signature.eml index 01a0dd7874..5352f48a25 100644 --- a/spec/fixtures/emails/signature.eml +++ b/spec/fixtures/emails/signature.eml @@ -1,29 +1,12 @@ -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) +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <26@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +You shall not sign! ---Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 -Content-Type: text/plain; - charset=us-ascii -Content-Transfer-Encoding: 7bit - -This post should not include signature. - -----Arpit - -> On 23-Oct-2014, at 9:45 am, Arpit Jalan wrote: -> -> Signature in email replies! - ---Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 +--- +Foo Bar diff --git a/spec/fixtures/emails/staged_sender.eml b/spec/fixtures/emails/staged_sender.eml new file mode 100644 index 0000000000..6ee856614e --- /dev/null +++ b/spec/fixtures/emails/staged_sender.eml @@ -0,0 +1,9 @@ +Return-Path: +From: Foo Bar +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <9@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/spec/fixtures/emails/stranger_not_allowed.eml b/spec/fixtures/emails/stranger_not_allowed.eml new file mode 100644 index 0000000000..1464e8ddd8 --- /dev/null +++ b/spec/fixtures/emails/stranger_not_allowed.eml @@ -0,0 +1,11 @@ +Return-Path: +From: Foo Bar +To: category@bar.com +Subject: This is a topic from a complete stranger +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <31@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hey, this is a topic from a complete stranger ;) diff --git a/spec/fixtures/emails/sufficient_trust_level.eml b/spec/fixtures/emails/sufficient_trust_level.eml new file mode 100644 index 0000000000..cee56f11be --- /dev/null +++ b/spec/fixtures/emails/sufficient_trust_level.eml @@ -0,0 +1,11 @@ +Return-Path: +From: Foo Bar +To: category@bar.com +Subject: This is a topic from a know user +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <33@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hey, this is a topic from a known user ;) diff --git a/spec/fixtures/emails/text_and_html_reply.eml b/spec/fixtures/emails/text_and_html_reply.eml new file mode 100644 index 0000000000..5fb87780ab --- /dev/null +++ b/spec/fixtures/emails/text_and_html_reply.eml @@ -0,0 +1,19 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <19@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: multipart/alternative; boundary=001a11469b1296cf8d052963bde5 + +--001a11469b1296cf8d052963bde5 +Content-Type: text/plain; charset=UTF-8 + +This is the *text* part. + +--001a11469b1296cf8d052963bde5 +Content-Type: text/html; charset=UTF-8 + +
    This is the html part.
    + +--001a11469b1296cf8d052963bde5-- diff --git a/spec/fixtures/emails/text_reply.eml b/spec/fixtures/emails/text_reply.eml new file mode 100644 index 0000000000..1d295a3f23 --- /dev/null +++ b/spec/fixtures/emails/text_reply.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <15@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +This is a text reply :) diff --git a/spec/fixtures/emails/too_many_mentions.eml b/spec/fixtures/emails/too_many_mentions.eml index 9cc7b75c94..6940955f9c 100644 --- a/spec/fixtures/emails/too_many_mentions.eml +++ b/spec/fixtures/emails/too_many_mentions.eml @@ -1,31 +1,10 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+636ca428858779856c226bb145ef4fad@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <14@foo.bar.mail> Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 +Content-Type: text/plain Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -@user1 -@user2 -@user3 -@user4 -@user5 -@user6 -@user7 -@user8 -@user9 -@user10 -@user11 \ No newline at end of file +@user1 @user2 diff --git a/spec/fixtures/emails/too_short.eml b/spec/fixtures/emails/too_short.eml deleted file mode 100644 index 69f5976978..0000000000 --- a/spec/fixtures/emails/too_short.eml +++ /dev/null @@ -1,21 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: TO -Message-ID: -Subject: SUBJECT -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - - -ok diff --git a/spec/fixtures/emails/too_small.eml b/spec/fixtures/emails/too_small.eml new file mode 100644 index 0000000000..fbc5c8d885 --- /dev/null +++ b/spec/fixtures/emails/too_small.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <12@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +Ok! diff --git a/spec/fixtures/emails/unsubscribe_body.eml b/spec/fixtures/emails/unsubscribe_body.eml new file mode 100644 index 0000000000..1ae876edbd --- /dev/null +++ b/spec/fixtures/emails/unsubscribe_body.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Thu, 13 Jun 2013 17:03:48 -0400 +Message-ID: <55@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain; +Content-Transfer-Encoding: 7bit + +UNSUBSCRIBE diff --git a/spec/fixtures/emails/unsubscribe_subject.eml b/spec/fixtures/emails/unsubscribe_subject.eml new file mode 100644 index 0000000000..84b89079bf --- /dev/null +++ b/spec/fixtures/emails/unsubscribe_subject.eml @@ -0,0 +1,11 @@ +Return-Path: +From: Foo Bar +To: reply@bar.com +Date: Thu, 13 Jun 2013 17:03:48 -0400 +Message-ID: <56@foo.bar.mail> +Subject: UnSuBScRiBe +Mime-Version: 1.0 +Content-Type: text/plain; +Content-Transfer-Encoding: 7bit + +I've basically had enough of your mailing list and would very much like it if you went away. diff --git a/spec/fixtures/emails/valid_incoming.cooked b/spec/fixtures/emails/valid_incoming.cooked deleted file mode 100644 index 2bf3582537..0000000000 --- a/spec/fixtures/emails/valid_incoming.cooked +++ /dev/null @@ -1,5 +0,0 @@ -

    Hey folks,

    - -

    I was thinking. Wouldn't it be great if we could post topics via email? Yes it would!

    - -

    Jakie

    diff --git a/spec/fixtures/emails/valid_incoming.eml b/spec/fixtures/emails/valid_incoming.eml deleted file mode 100644 index cad475d662..0000000000 --- a/spec/fixtures/emails/valid_incoming.eml +++ /dev/null @@ -1,26 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: -Cc: -Message-ID: -Subject: We should have a post-by-email-feature. -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -Hey folks, - -I was thinking. Wouldn't it be great if we could post topics via email? Yes it would! - -Jakie - diff --git a/spec/fixtures/emails/valid_reply.cooked b/spec/fixtures/emails/valid_reply.cooked deleted file mode 100644 index 4bce79ad12..0000000000 --- a/spec/fixtures/emails/valid_reply.cooked +++ /dev/null @@ -1,4 +0,0 @@ -

    I could not disagree more. I am obviously biased but adventure time is the -greatest show ever created. Everyone should watch it.

    - -
    • Jake out
    diff --git a/spec/fixtures/emails/valid_reply.eml b/spec/fixtures/emails/valid_reply.eml deleted file mode 100644 index 786b1d8516..0000000000 --- a/spec/fixtures/emails/valid_reply.eml +++ /dev/null @@ -1,40 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: FROM -To: TO -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -I could not disagree more. I am obviously biased but adventure time is the -greatest show ever created. Everyone should watch it. - -- Jake out - - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> diff --git a/spec/fixtures/emails/via_line.eml b/spec/fixtures/emails/via_line.eml deleted file mode 100644 index 0b2947ff95..0000000000 --- a/spec/fixtures/emails/via_line.eml +++ /dev/null @@ -1,25 +0,0 @@ - -Delivered-To: discourse-reply+cd480e301683c9902891f15968bf07a5@discourse.org -Received: by 10.194.216.104 with SMTP id op8csp80593wjc; - Wed, 24 Jul 2013 07:59:14 -0700 (PDT) -Return-Path: -References: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -From: Walter White -In-Reply-To: <51efeb9b36c34_66dc2dfce6811866@discourse.mail> -Mime-Version: 1.0 (1.0) -Date: Wed, 24 Jul 2013 15:59:10 +0100 -Message-ID: <4597127794206131679@unknownmsgid> -Subject: Re: [Discourse] new reply to your post in 'Crystal Blue' -To: walter via Discourse -Content-Type: multipart/alternative; boundary=001a11c20edc15a39304e2432790 - -Hello this email has content! - -codinghorror via Discourse wrote: -> [codinghorror] codinghorror -> -> August 7 -> -> It wouldn't be great at the moment for 100% email, since there's no -> way to be notified of new topics via email. (you can get notified of -> new replies to your posts and topics via email, and reply to them.) diff --git a/spec/fixtures/emails/windows_8_metro.eml b/spec/fixtures/emails/windows_8_metro.eml deleted file mode 100644 index 67d204af56..0000000000 --- a/spec/fixtures/emails/windows_8_metro.eml +++ /dev/null @@ -1,173 +0,0 @@ -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_-- diff --git a/spec/fixtures/emails/wrong_reply_key.eml b/spec/fixtures/emails/wrong_reply_key.eml deleted file mode 100644 index 1c30cfc51f..0000000000 --- a/spec/fixtures/emails/wrong_reply_key.eml +++ /dev/null @@ -1,40 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+03d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -I could not disagree more. I am obviously biased but adventure time is the -greatest show ever created. Everyone should watch it. - -- Jake out - - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> diff --git a/spec/jobs/jobs_spec.rb b/spec/jobs/jobs_spec.rb index fa31b7ea18..57d2717b25 100644 --- a/spec/jobs/jobs_spec.rb +++ b/spec/jobs/jobs_spec.rb @@ -1,3 +1,4 @@ +require "sidekiq/testing" require 'rails_helper' require_dependency 'jobs/base' @@ -76,23 +77,27 @@ describe Jobs do end describe 'cancel_scheduled_job' do + it 'deletes the matching job' do - job_to_delete = stub_everything(klass: 'Sidekiq::Extensions::DelayedClass', args: [YAML.dump(['Jobs::DrinkBeer', :delayed_perform, [{beer_id: 42}]])]) - job_to_delete.expects(:delete) - job_to_keep1 = stub_everything(klass: 'Sidekiq::Extensions::DelayedClass', args: [YAML.dump(['Jobs::DrinkBeer', :delayed_perform, [{beer_id: 43}]])]) - job_to_keep1.expects(:delete).never - job_to_keep2 = stub_everything(klass: 'Sidekiq::Extensions::DelayedClass', args: [YAML.dump(['Jobs::DrinkBeer', :delayed_perform, [{beer_id: 44}]])]) - job_to_keep2.expects(:delete).never - Sidekiq::ScheduledSet.stubs(:new).returns( [job_to_keep1, job_to_delete, job_to_keep2] ) - expect(Jobs.cancel_scheduled_job(:drink_beer, {beer_id: 42})).to eq(true) + SiteSetting.queue_jobs = true + + Sidekiq::Testing.disable! do + scheduled_jobs = Sidekiq::ScheduledSet.new + scheduled_jobs.clear + + expect(scheduled_jobs.size).to eq(0) + + Jobs.enqueue_in(1.year, :run_heartbeat, topic_id: 1234) + Jobs.enqueue_in(2.years, :run_heartbeat, topic_id: 5678) + + expect(scheduled_jobs.size).to eq(2) + + Jobs.cancel_scheduled_job(:run_heartbeat, topic_id: 1234) + + expect(scheduled_jobs.size).to eq(1) + end end - it 'returns false when no matching job is scheduled' do - job_to_keep = stub_everything(klass: 'Sidekiq::Extensions::DelayedClass', args: [YAML.dump(['Jobs::DrinkBeer', :delayed_perform, [{beer_id: 43}]])]) - job_to_keep.expects(:delete).never - Sidekiq::ScheduledSet.stubs(:new).returns( [job_to_keep] ) - expect(Jobs.cancel_scheduled_job(:drink_beer, {beer_id: 42})).to eq(false) - end end describe 'enqueue_at' do diff --git a/spec/jobs/poll_mailbox_spec.rb b/spec/jobs/poll_mailbox_spec.rb index 563bfab82c..451cf07e35 100644 --- a/spec/jobs/poll_mailbox_spec.rb +++ b/spec/jobs/poll_mailbox_spec.rb @@ -3,25 +3,20 @@ require_dependency 'jobs/regular/process_post' describe Jobs::PollMailbox do - let!(:poller) { Jobs::PollMailbox.new } + let(:poller) { Jobs::PollMailbox.new } describe ".execute" do it "does no polling if pop3_polling_enabled is false" do - SiteSetting.expects(:pop3_polling_enabled?).returns(false) + SiteSetting.expects(:pop3_polling_enabled).returns(false) poller.expects(:poll_pop3).never - poller.execute({}) end - describe "with pop3_polling_enabled" do - - it "calls poll_pop3" do - SiteSetting.expects(:pop3_polling_enabled?).returns(true) - poller.expects(:poll_pop3).once - - poller.execute({}) - end + it "polls when pop3_polling_enabled is true" do + SiteSetting.expects(:pop3_polling_enabled).returns(true) + poller.expects(:poll_pop3).once + poller.execute({}) end end @@ -29,13 +24,8 @@ describe Jobs::PollMailbox do describe ".poll_pop3" do it "logs an error on pop authentication error" do - error = Net::POPAuthenticationError.new - data = { limit_once_per: 1.hour, message_params: { error: error }} - - Net::POP3.any_instance.expects(:start).raises(error) - + Net::POP3.any_instance.expects(:start).raises(Net::POPAuthenticationError.new) Discourse.expects(:handle_job_exception) - poller.poll_pop3 end @@ -43,275 +33,15 @@ describe Jobs::PollMailbox do SiteSetting.pop3_polling_ssl = true Net::POP3.any_instance.stubs(:start) Net::POP3.any_instance.expects(:enable_ssl) - poller.poll_pop3 end - it "does not call enable_ssl when the setting is off" do + it "does not call enable_ssl when the setting is disabled" do SiteSetting.pop3_polling_ssl = false Net::POP3.any_instance.stubs(:start) Net::POP3.any_instance.expects(:enable_ssl).never - poller.poll_pop3 end end - # Testing mock for the email objects that you get - # from Net::POP3.start { |pop| pop.mails } - class MockPop3EmailObject - def initialize(mail_string) - @message = mail_string - @delete_called = 0 - end - - def pop - @message - end - - def delete - @delete_called += 1 - end - - # call 'assert email.deleted?' at the end of the test - def deleted? - @delete_called == 1 - end - end - - def expect_success - poller.expects(:handle_failure).never - end - - def expect_exception(clazz) - poller.expects(:handle_failure).with(anything, instance_of(clazz)) - end - - describe "processing emails" do - let(:category) { Fabricate(:category) } - let(:user) { Fabricate(:user) } - - before do - SiteSetting.email_in = true - SiteSetting.reply_by_email_address = "reply+%{reply_key}@appmail.adventuretime.ooo" - category.email_in = 'incoming+amazing@appmail.adventuretime.ooo' - category.save - user.change_trust_level! 2 - user.username = 'Jake' - user.email = 'jake@adventuretime.ooo' - user.save - end - - describe "a valid incoming email" do - let(:email) { - # this string replacing is kinda dumb - str = fixture_file('emails/valid_incoming.eml') - str = str.gsub("FROM", 'jake@adventuretime.ooo').gsub("TO", 'incoming+amazing@appmail.adventuretime.ooo') - MockPop3EmailObject.new str - } - let(:expected_post) { fixture_file('emails/valid_incoming.cooked') } - - it "posts a new topic with the correct content" do - expect_success - - poller.handle_mail(email) - - topic = Topic.where(category: category).where.not(id: category.topic_id).last - expect(topic).to be_present - expect(topic.title).to eq("We should have a post-by-email-feature") - - post = topic.posts.first - expect(post.cooked.strip).to eq(expected_post.strip) - - expect(email).to be_deleted - end - - describe "with insufficient trust" do - before do - user.change_trust_level! 0 - end - - it "raises a UserNotSufficientTrustLevelError" do - expect_exception Email::Receiver::UserNotSufficientTrustLevelError - - poller.handle_mail(email) - end - - it "posts the topic if allow_strangers is true" do - begin - category.email_in_allow_strangers = true - category.save - - expect_success - poller.handle_mail(email) - topic = Topic.where(category: category).where.not(id: category.topic_id).last - expect(topic).to be_present - expect(topic.title).to eq("We should have a post-by-email-feature") - ensure - category.email_in_allow_strangers = false - category.save - end - end - end - - describe "user in restricted group" do - - it "raises InvalidAccess error" do - restricted_group = Fabricate(:group) - restricted_group.add(user) - restricted_group.save - - category.set_permissions(restricted_group => :readonly) - category.save - - expect_exception Discourse::InvalidAccess - - poller.handle_mail(email) - expect(email).to be_deleted - end - end - end - - describe "a valid reply" do - let(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:raw_email) { fill_email(fixture_file("emails/valid_reply.eml"), user.email, to) } - let(:email) { MockPop3EmailObject.new(raw_email) } - let(:expected_post) { fixture_file('emails/valid_reply.cooked') } - let(:topic) { Fabricate(:topic) } - let(:first_post) { Fabricate(:post, user: user, topic: topic, post_number: 1) } - - before do - first_post.save - EmailLog.create(to_address: user.email, - email_type: 'user_posted', - reply_key: reply_key, - user: user, - post: first_post, - topic: topic) - end - - it "creates a new post" do - expect_success - - poller.handle_mail(email) - - new_post = Post.find_by(topic: topic, post_number: 2) - assert new_post.present? - assert_equal expected_post.strip, new_post.cooked.strip - - expect(email).to be_deleted - end - - it "works with multiple To addresses" do - email = MockPop3EmailObject.new fixture_file('emails/multiple_destinations.eml') - expect_success - - poller.handle_mail(email) - - new_post = Post.find_by(topic: topic, post_number: 2) - assert new_post.present? - assert_equal expected_post.strip, new_post.cooked.strip - - expect(email).to be_deleted - end - - describe "with the wrong reply key" do - let(:email) { MockPop3EmailObject.new fixture_file('emails/wrong_reply_key.eml') } - - it "raises an EmailLogNotFound error" do - expect_exception Email::Receiver::EmailLogNotFound - - poller.handle_mail(email) - expect(email).to be_deleted - end - end - end - - describe "when topic is closed" do - let(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:raw_email) { fill_email(fixture_file("emails/valid_reply.eml"), user.email, to) } - let(:email) { MockPop3EmailObject.new(raw_email) } - let(:topic) { Fabricate(:topic, closed: true) } - let(:first_post) { Fabricate(:post, user: user, topic: topic, post_number: 1) } - - before do - first_post.save - EmailLog.create(to_address: user.email, - email_type: 'user_posted', - reply_key: reply_key, - user: user, - post: first_post, - topic: topic) - end - - describe "should not create post" do - it "raises a TopicClosedError" do - expect_exception Email::Receiver::TopicClosedError - - poller.handle_mail(email) - expect(email).to be_deleted - end - end - end - - describe "when topic is deleted" do - let(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' } - let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) } - let(:raw_email) { fill_email(fixture_file("emails/valid_reply.eml"), user.email, to) } - let(:email) { MockPop3EmailObject.new(raw_email) } - let(:deleted_topic) { Fabricate(:deleted_topic) } - let(:first_post) { Fabricate(:post, user: user, topic: deleted_topic, post_number: 1)} - - before do - first_post.save - EmailLog.create(to_address: user.email, - email_type: 'user_posted', - reply_key: reply_key, - user: user, - post: first_post, - topic: deleted_topic) - end - - describe "should not create post" do - it "raises a TopicNotFoundError" do - expect_exception Email::Receiver::TopicNotFoundError - - poller.handle_mail(email) - expect(email).to be_deleted - end - end - end - - describe "in failure conditions" do - - it "a valid reply without an email log raises an EmailLogNotFound error" do - to = SiteSetting.reply_by_email_address.gsub("%{reply_key}", '59d8df8370b7e95c5a49fbf86aeb2c93') - raw_email = fill_email(fixture_file("emails/valid_reply.eml"), user.email, to) - email = MockPop3EmailObject.new(raw_email) - expect_exception Email::Receiver::EmailLogNotFound - - poller.handle_mail(email) - expect(email).to be_deleted - end - - it "a no content reply raises an EmptyEmailError" do - email = MockPop3EmailObject.new fixture_file('emails/no_content_reply.eml') - expect_exception Email::Receiver::EmptyEmailError - - poller.handle_mail(email) - expect(email).to be_deleted - end - - it "a fully empty email raises an EmptyEmailError" do - email = MockPop3EmailObject.new fixture_file('emails/empty.eml') - expect_exception Email::Receiver::EmptyEmailError - - poller.handle_mail(email) - expect(email).to be_deleted - end - - end - end - end diff --git a/spec/mailers/user_notifications_spec.rb b/spec/mailers/user_notifications_spec.rb index 3f07451639..fafe7b8ee2 100644 --- a/spec/mailers/user_notifications_spec.rb +++ b/spec/mailers/user_notifications_spec.rb @@ -108,7 +108,7 @@ describe UserNotifications do expect(mail.subject).to match(/India/) # 2 respond to links cause we have 1 context post - expect(mail.html_part.to_s.scan(/To respond/).count).to eq(2) + expect(mail.html_part.to_s.scan(/to respond/).count).to eq(2) # 1 unsubscribe expect(mail.html_part.to_s.scan(/To unsubscribe/).count).to eq(1) @@ -159,7 +159,7 @@ describe UserNotifications do expect(mail.subject).not_to match(/Uncategorized/) # 2 respond to links cause we have 1 context post - expect(mail.html_part.to_s.scan(/To respond/).count).to eq(2) + expect(mail.html_part.to_s.scan(/to respond/).count).to eq(2) # 1 unsubscribe link expect(mail.html_part.to_s.scan(/To unsubscribe/).count).to eq(1) @@ -188,7 +188,7 @@ describe UserNotifications do expect(mail.subject).to match("[PM]") # 1 respond to link - expect(mail.html_part.to_s.scan(/To respond/).count).to eq(1) + expect(mail.html_part.to_s.scan(/to respond/).count).to eq(1) # 1 unsubscribe link expect(mail.html_part.to_s.scan(/To unsubscribe/).count).to eq(1) diff --git a/spec/models/embeddable_host_spec.rb b/spec/models/embeddable_host_spec.rb index 2a4c306a09..4ee44c5608 100644 --- a/spec/models/embeddable_host_spec.rb +++ b/spec/models/embeddable_host_spec.rb @@ -20,6 +20,12 @@ describe EmbeddableHost do expect(eh.host).to eq('example.com') end + it "supports ip addresses" do + eh = EmbeddableHost.new(host: '192.168.0.1') + expect(eh).to be_valid + expect(eh.host).to eq('192.168.0.1') + end + describe "allows_embeddable_host" do let!(:host) { Fabricate(:embeddable_host) } diff --git a/spec/models/post_analyzer_spec.rb b/spec/models/post_analyzer_spec.rb index 096e90014f..57e68ee88a 100644 --- a/spec/models/post_analyzer_spec.rb +++ b/spec/models/post_analyzer_spec.rb @@ -205,5 +205,10 @@ describe PostAnalyzer do post_analyzer = PostAnalyzer.new("@Jake @Finn @Jake_Old", default_topic_id) expect(post_analyzer.raw_mentions).to eq(['jake', 'finn', 'jake_old']) end + + it "ignores emails" do + post_analyzer = PostAnalyzer.new("1@test.com 1@best.com @best @not", default_topic_id) + expect(post_analyzer.raw_mentions).to eq(['best', 'not']) + end end end diff --git a/spec/models/s3_region_site_setting_spec.rb b/spec/models/s3_region_site_setting_spec.rb index 2249fbad5e..ffba50b7f7 100644 --- a/spec/models/s3_region_site_setting_spec.rb +++ b/spec/models/s3_region_site_setting_spec.rb @@ -14,7 +14,7 @@ describe S3RegionSiteSetting do describe 'values' do it 'returns all the S3 regions' do - expect(S3RegionSiteSetting.values.map {|x| x[:value]}.sort).to eq(['us-east-1', 'us-west-1', 'us-west-2', 'us-gov-west-1', 'eu-west-1', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1'].sort) + expect(S3RegionSiteSetting.values.map {|x| x[:value]}.sort).to eq(['us-east-1', 'us-west-1', 'us-west-2', 'us-gov-west-1', 'eu-west-1', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'ap-northeast-2', 'sa-east-1'].sort) end end diff --git a/spec/models/top_topic_spec.rb b/spec/models/top_topic_spec.rb index 3b90232f1a..7c73a98b3c 100644 --- a/spec/models/top_topic_spec.rb +++ b/spec/models/top_topic_spec.rb @@ -38,9 +38,108 @@ describe TopTopic do it "should have top topics" do expect(TopTopic.pluck(:topic_id)).to match_array([t1.id, t2.id]) end - end - end + describe "#compute_top_score_for" do + + let(:user) { Fabricate(:user) } + let(:coding_horror) { Fabricate(:coding_horror) } + + let!(:topic_1) { Fabricate(:topic, posts_count: 10, like_count: 28) } + let!(:t1_post_1) { Fabricate(:post, topic: topic_1, like_count: 28, post_number: 1)} + + let!(:topic_2) { Fabricate(:topic, posts_count: 10, like_count: 20) } + let!(:t2_post_1) { Fabricate(:post, topic: topic_2, like_count: 10, post_number: 1) } + let!(:t2_post_2) { Fabricate(:post, topic: topic_2, like_count: 10) } + + let!(:topic_3) { Fabricate(:topic, posts_count: 10) } + let!(:t3_post_1) { Fabricate(:post, topic_id: topic_3.id) } + let!(:t3_view_1) { TopicViewItem.add(topic_3.id, '127.0.0.1', user) } + let!(:t3_view_2) { TopicViewItem.add(topic_3.id, '127.0.0.2', coding_horror) } + + # Note: all topics has 10 posts so we can skip "0 - ((10 - topics.posts_count) / 20) * #{period}_op_likes_count" calculation + + it "should compute top score" do + # Default Formula: log(views_count) * {2} + op_likes_count * {0.5} + LEAST(likes_count / posts_count, {3}) + 10 + log(posts_count) + # + # topic_1 => 0 + 14 + 3 + 10 + 0 => 27 + # topic_2 => 0 + 5 + 3 + 10 + 0.301029995664 => 18.301029995664 + # topic_3 => 0.602059991328 + 0 + 0 + 10 + 0 => 10.602059991328 + + TopTopic.refresh! + top_topics = TopTopic.all + + expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(27) + expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(18.301029995664) + expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328) + + # when 'top_topics_formula_log_views_multiplier' setting is changed + SiteSetting.top_topics_formula_log_views_multiplier = 4 + SiteSetting.top_topics_formula_first_post_likes_multiplier = 0.5 # unchanged + SiteSetting.top_topics_formula_least_likes_per_post_multiplier = 3 # unchanged + + # New Formula: log(views_count) * {4} + op_likes_count * {0.5} + LEAST(likes_count / posts_count, {3}) + 10 + log(posts_count) + # + # topic_1 => 0 + 14 + 3 + 10 + 0 => 27 + # topic_2 => 0 + 5 + 3 + 10 + 0.301029995664 => 18.301029995664 + # topic_3 => 1.2041199826559 + 0 + 0 + 10 + 0 => 11.2041199826559 + + TopTopic.refresh! + top_topics = TopTopic.all + + expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(27) + expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(18.301029995664) + expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(11.2041199826559) + + # when 'top_topics_formula_first_post_likes_multiplier' setting is changed + SiteSetting.top_topics_formula_log_views_multiplier = 2 # unchanged + SiteSetting.top_topics_formula_first_post_likes_multiplier = 2 + SiteSetting.top_topics_formula_least_likes_per_post_multiplier = 3 # unchanged + + # New Formula: log(views_count) * {2} + op_likes_count * {2} + LEAST(likes_count / posts_count, {3}) + 10 + log(posts_count) + # + # topic_1 => 0 + 56 + 3 + 10 + 0 => 69 + # topic_2 => 0 + 20 + 3 + 10 + 0.301029995664 => 33.301029995664 + # topic_3 => 0.602059991328 + 0 + 0 + 10 + 0 => 10.602059991328 + + TopTopic.refresh! + top_topics = TopTopic.all + + expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(69) + expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(33.301029995664) + expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328) + + # when 'top_topics_formula_least_likes_per_post_multiplier' setting is changed + SiteSetting.top_topics_formula_log_views_multiplier = 2 # unchanged + SiteSetting.top_topics_formula_first_post_likes_multiplier = 0.5 # unchanged + SiteSetting.top_topics_formula_least_likes_per_post_multiplier = 6 + + # New Formula: log(views_count) * {2} + op_likes_count * {0.5} + LEAST(likes_count / posts_count, {6}) + 10 + log(posts_count) + # + # topic_1 => 0 + 14 + 6 + 10 + 0 => 30 + # topic_2 => 0 + 5 + 6 + 10 + 0.301029995664 => 21.301029995664 + # topic_3 => 0.602059991328 + 0 + 0 + 10 + 0 => 10.602059991328 + + TopTopic.refresh! + top_topics = TopTopic.all + + expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(30) + expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(21.301029995664) + expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328) + + # handles invalid string value + SiteSetting.top_topics_formula_log_views_multiplier = "not good" + SiteSetting.top_topics_formula_first_post_likes_multiplier = "not good" + SiteSetting.top_topics_formula_least_likes_per_post_multiplier = "not good" + + TopTopic.refresh! + top_topics = TopTopic.all + + expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(27) + expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(18.301029995664) + expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328) + + end + end end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 6a8eb9d768..3799738ad1 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -378,7 +378,7 @@ describe Topic do expect(topic.invite(topic.user, walter.username)).to eq(true) expect(topic.allowed_users.include?(walter)).to eq(true) - expect(topic.remove_allowed_user(walter.username)).to eq(true) + expect(topic.remove_allowed_user(topic.user, walter.username)).to eq(true) topic.reload expect(topic.allowed_users.include?(walter)).to eq(false) end @@ -386,6 +386,11 @@ describe Topic do it 'creates a notification' do expect { topic.invite(topic.user, walter.username) }.to change(Notification, :count) end + + it 'creates a small action post' do + expect { topic.invite(topic.user, walter.username) }.to change(Post, :count) + expect { topic.remove_allowed_user(topic.user, walter.username) }.to change(Post, :count) + end end context 'by email' do @@ -1563,6 +1568,20 @@ describe Topic do expect(Guardian.new(walter).can_see?(group_private_topic)).to be_truthy end end + end + it "Correctly sets #message_archived?" do + topic = Fabricate(:private_message_topic) + user = topic.user + + expect(topic.message_archived?(user)).to eq(false) + + group = Fabricate(:group) + group.add(user) + + TopicAllowedGroup.create!(topic_id: topic.id, group_id: group.id) + GroupArchivedMessage.create!(topic_id: topic.id, group_id: group.id) + + expect(topic.message_archived?(user)).to eq(true) end end diff --git a/spec/models/user_email_observer_spec.rb b/spec/models/user_email_observer_spec.rb index 1fb5be42b3..9dfe1cdae1 100644 --- a/spec/models/user_email_observer_spec.rb +++ b/spec/models/user_email_observer_spec.rb @@ -2,10 +2,13 @@ require 'rails_helper' describe UserEmailObserver do + let(:topic) { Fabricate(:topic) } + let(:post) { Fabricate(:post, topic: topic) } + # something is off with fabricator def create_notification(type, user=nil) user ||= Fabricate(:user) - Notification.create(data: '', user: user, notification_type: type) + Notification.create(data: '', user: user, notification_type: type, topic: topic, post_number: post.post_number) end shared_examples "enqueue" do @@ -32,6 +35,16 @@ describe UserEmailObserver do end + context "small action" do + + it "doesn't enqueue a job" do + Post.any_instance.expects(:post_type).returns(Post.types[:small_action]) + Jobs.expects(:enqueue_in).with(delay, :user_email, has_entry(type: type)).never + UserEmailObserver.send(:new).after_commit(notification) + end + + end + end shared_examples "enqueue_public" do diff --git a/spec/models/user_stat_spec.rb b/spec/models/user_stat_spec.rb index dcd5f82428..e977d3b08a 100644 --- a/spec/models/user_stat_spec.rb +++ b/spec/models/user_stat_spec.rb @@ -102,6 +102,4 @@ describe UserStat do end end - - end diff --git a/spec/services/staff_action_logger_spec.rb b/spec/services/staff_action_logger_spec.rb index d1c7422c7c..676b899dce 100644 --- a/spec/services/staff_action_logger_spec.rb +++ b/spec/services/staff_action_logger_spec.rb @@ -176,8 +176,6 @@ describe StaffActionLogger do describe "log_site_text_change" do it "raises an error when params are invalid" do expect { logger.log_site_text_change(nil, 'new text', 'old text') }.to raise_error(Discourse::InvalidParameters) - expect { logger.log_site_text_change('created', nil, 'old text') }.to raise_error(Discourse::InvalidParameters) - expect { logger.log_site_text_change('created', 'new text', nil) }.to raise_error(Discourse::InvalidParameters) end it "creates a new UserHistory record" do diff --git a/test/javascripts/components/d-editor-test.js.es6 b/test/javascripts/components/d-editor-test.js.es6 index e43fdf686a..ecb486f3a5 100644 --- a/test/javascripts/components/d-editor-test.js.es6 +++ b/test/javascripts/components/d-editor-test.js.es6 @@ -130,7 +130,7 @@ testCase(`italic button with no selection`, function(assert, textarea) { click(`button.italic`); andThen(() => { const example = I18n.t(`composer.italic_text`); - assert.equal(this.get('value'), `hello world.*${example}*`); + assert.equal(this.get('value'), `hello world._${example}_`); assert.equal(textarea.selectionStart, 13); assert.equal(textarea.selectionEnd, 13 + example.length); @@ -143,7 +143,7 @@ testCase(`italic button with a selection`, function(assert, textarea) { click(`button.italic`); andThen(() => { - assert.equal(this.get('value'), `hello *world*.`); + assert.equal(this.get('value'), `hello _world_.`); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 12); }); @@ -166,7 +166,7 @@ testCase(`italic with a multiline selection`, function (assert, textarea) { click(`button.italic`); andThen(() => { - assert.equal(this.get('value'), `*hello*\n\n*world*\n\ntest.`); + assert.equal(this.get('value'), `_hello_\n\n_world_\n\ntest.`); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 16); }); diff --git a/test/javascripts/lib/click-track-test.js.es6 b/test/javascripts/lib/click-track-test.js.es6 index 3b228d59ae..403a2c15f5 100644 --- a/test/javascripts/lib/click-track-test.js.es6 +++ b/test/javascripts/lib/click-track-test.js.es6 @@ -16,21 +16,22 @@ module("lib:click-track", { windowOpen = sandbox.stub(window, "open").returns(win); sandbox.stub(win, "focus"); - fixture().html([ - '
    ', - ' ', - '
    '].join("\n")); + fixture().html( + ``); } }); @@ -64,6 +65,10 @@ test("does not track clicks on quote buttons", function() { ok(track(generateClickEventOn('.quote-other-topic'))); }); +test("does not track clicks on category badges", () => { + ok(!track(generateClickEventOn('.hashtag'))); +}); + test("removes the href and put it as a data attribute", function() { track(generateClickEventOn('a')); diff --git a/test/javascripts/lib/markdown-test.js.es6 b/test/javascripts/lib/markdown-test.js.es6 index fde64778ba..fc1a9261be 100644 --- a/test/javascripts/lib/markdown-test.js.es6 +++ b/test/javascripts/lib/markdown-test.js.es6 @@ -293,7 +293,7 @@ test("Category hashtags", () => { var alwaysTrue = { categoryHashtagLookup: (function() { return ["category", "http://test.discourse.org/category-hashtag"]; }) }; cookedOptions("Check out #category-hashtag", alwaysTrue, - "

    Check out #category-hashtag

    ", + "

    Check out #category-hashtag

    ", "it translates category hashtag into links"); cooked("Check out #category-hashtag", diff --git a/test/javascripts/models/category-test.js.es6 b/test/javascripts/models/category-test.js.es6 index 157e319189..cb6c9858e2 100644 --- a/test/javascripts/models/category-test.js.es6 +++ b/test/javascripts/models/category-test.js.es6 @@ -126,26 +126,22 @@ test('postCountStats', function() { equal(result.length, 0, "should show nothing"); }); -test('search', () => { - const result = (term, opts) => { - return Category.search(term, opts).map((category) => category.get('id')); - }; - +test('search with category name', () => { const store = createStore(), - category1 = store.createRecord('category', { id: 1, name: 'middle term' }), - category2 = store.createRecord('category', { id: 2, name: 'middle term' }); + category1 = store.createRecord('category', { id: 1, name: 'middle term', slug: 'different-slug' }), + category2 = store.createRecord('category', { id: 2, name: 'middle term', slug: 'another-different-slug' }); sandbox.stub(Category, "listByActivity").returns([category1, category2]); - deepEqual(result('term', { limit: 0 }), [], "returns an empty array when limit is 0"); - deepEqual(result(''), [category1.get('id'), category2.get('id')], "orders by activity if no term is matched"); - deepEqual(result('term'), [category1.get('id'), category2.get('id')], "orders by activity"); + deepEqual(Category.search('term', { limit: 0 }), [], "returns an empty array when limit is 0"); + deepEqual(Category.search(''), [category1, category2], "orders by activity if no term is matched"); + deepEqual(Category.search('term'), [category1, category2], "orders by activity"); category2.set('name', 'TeRm start'); - deepEqual(result('tErM'), [category2.get('id'), category1.get('id')], "ignores case of category name and search term"); + deepEqual(Category.search('tErM'), [category2, category1], "ignores case of category name and search term"); category2.set('name', 'term start'); - deepEqual(result('term'), [category2.get('id'), category1.get('id')], "orders matching begin with and then contains"); + deepEqual(Category.search('term'), [category2, category1], "orders matching begin with and then contains"); sandbox.restore(); @@ -154,21 +150,35 @@ test('search', () => { sandbox.stub(Category, "listByActivity").returns([read_restricted_category, category1, child_category1, category2]); - deepEqual(result(''), - [category1.get('id'), category2.get('id'), read_restricted_category.get('id')], + deepEqual(Category.search(''), + [category1, category2, read_restricted_category], "prioritize non read_restricted and does not include child categories when term is blank"); - deepEqual(result('', { limit: 3 }), - [category1.get('id'), category2.get('id'), read_restricted_category.get('id')], + deepEqual(Category.search('', { limit: 3 }), + [category1, category2, read_restricted_category], "prioritize non read_restricted and does not include child categories categories when term is blank with limit"); - deepEqual(result('term'), - [child_category1.get('id'), category2.get('id'), category1.get('id'), read_restricted_category.get('id')], + deepEqual(Category.search('term'), + [child_category1, category2, category1, read_restricted_category], "prioritize non read_restricted"); - deepEqual(result('term', { limit: 3 }), - [child_category1.get('id'), category2.get('id'), read_restricted_category.get('id')], + deepEqual(Category.search('term', { limit: 3 }), + [child_category1, category2, read_restricted_category], "prioritize non read_restricted with limit"); sandbox.restore(); }); + +test('search with category slug', () => { + const store = createStore(), + category1 = store.createRecord('category', { id: 1, name: 'middle term', slug: 'different-slug' }), + category2 = store.createRecord('category', { id: 2, name: 'middle term', slug: 'another-different-slug' }); + + sandbox.stub(Category, "listByActivity").returns([category1, category2]); + + deepEqual(Category.search('different-slug'), [category1, category2], "returns the right categories"); + deepEqual(Category.search('another-different'), [category2], "returns the right categories"); + + category2.set('slug', 'ANOTher-DIFfereNT'); + deepEqual(Category.search('anOtHer-dIfFeREnt'), [category2], "ignores case of category slug and search term"); +}); diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.sk.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.sk.yml new file mode 100644 index 0000000000..60f6f6db4e --- /dev/null +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.sk.yml @@ -0,0 +1,12 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +sk: + site_settings: + enable_imgur: "Zapnúť imgur api pre upload, nehostujte súbory lokálne" + imgur_client_id: "Vaše imgur.com klientské ID potrebné pre funkčnosť uploadu obrázku" + imgur_client_secret: "Váš imgur.com klientský kľúč. Nie je aktuálne potrebný pre funkčnosť uploadu obrázkov, ale je možné že v budúcnosti bude."