diff --git a/Gemfile.lock b/Gemfile.lock index 82c7c984d3..18cd36b58b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,31 +6,31 @@ PATH GEM remote: https://rubygems.org/ specs: - actionmailer (4.1.6) - actionpack (= 4.1.6) - actionview (= 4.1.6) + actionmailer (4.1.7) + actionpack (= 4.1.7) + actionview (= 4.1.7) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.6) - actionview (= 4.1.6) - activesupport (= 4.1.6) + actionpack (4.1.7) + actionview (= 4.1.7) + activesupport (= 4.1.7) rack (~> 1.5.2) rack-test (~> 0.6.2) actionpack-action_caching (1.1.1) actionpack (>= 4.0.0, < 5.0) - actionview (4.1.6) - activesupport (= 4.1.6) + actionview (4.1.7) + activesupport (= 4.1.7) builder (~> 3.1) erubis (~> 2.7.0) active_model_serializers (0.8.2) activemodel (>= 3.0) - activemodel (4.1.6) - activesupport (= 4.1.6) + activemodel (4.1.7) + activesupport (= 4.1.7) builder (~> 3.1) - activerecord (4.1.6) - activemodel (= 4.1.6) - activesupport (= 4.1.6) + activerecord (4.1.7) + activemodel (= 4.1.7) + activesupport (= 4.1.7) arel (~> 5.0.0) - activesupport (4.1.6) + activesupport (4.1.7) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -171,7 +171,7 @@ GEM method_source (0.8.2) mime-types (1.25.1) mini_portile (0.6.0) - minitest (5.4.1) + minitest (5.4.2) mocha (1.1.0) metaclass (~> 0.0.1) mock_redis (0.13.2) @@ -255,21 +255,21 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (4.1.6) - actionmailer (= 4.1.6) - actionpack (= 4.1.6) - actionview (= 4.1.6) - activemodel (= 4.1.6) - activerecord (= 4.1.6) - activesupport (= 4.1.6) + rails (4.1.7) + actionmailer (= 4.1.7) + actionpack (= 4.1.7) + actionview (= 4.1.7) + activemodel (= 4.1.7) + activerecord (= 4.1.7) + activesupport (= 4.1.7) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.6) + railties (= 4.1.7) sprockets-rails (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.6) - actionpack (= 4.1.6) - activesupport (= 4.1.6) + railties (4.1.7) + actionpack (= 4.1.7) + activesupport (= 4.1.7) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.13.0) diff --git a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 index 9a8a4555c8..6bb36989b7 100644 --- a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 @@ -2,17 +2,27 @@ export default Ember.ObjectController.extend({ viewMode: 'table', viewingTable: Em.computed.equal('viewMode', 'table'), viewingBarChart: Em.computed.equal('viewMode', 'barChart'), + startDate: null, + endDate: null, + refreshing: false, actions: { - // Changes the current view mode to 'table' + refreshReport: function() { + var self = this; + this.set('refreshing', true); + Discourse.Report.find(this.get('type'), this.get('startDate'), this.get('endDate')).then(function(r) { + self.set('model', r); + }).finally(function() { + self.set('refreshing', false); + }); + }, + viewAsTable: function() { this.set('viewMode', 'table'); }, - // Changes the current view mode to 'barChart' viewAsBarChart: function() { this.set('viewMode', 'barChart'); } } - }); diff --git a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 b/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 index 00940ddfb9..36209fc212 100644 --- a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 @@ -90,11 +90,11 @@ export default Ember.ArrayController.extend(Discourse.Presence, { @method refreshUsers **/ - refreshUsers: function() { + refreshUsers: function(showEmails) { var adminUsersListController = this; adminUsersListController.set('loading', true); - Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('username') }).then(function (result) { + Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('username'), show_emails: showEmails }).then(function (result) { adminUsersListController.set('content', result); adminUsersListController.set('loading', false); }); @@ -140,6 +140,10 @@ export default Ember.ArrayController.extend(Discourse.Presence, { bootbox.alert(message); controller.refreshUsers(); }); + }, + + showEmails: function() { + this.refreshUsers(true); } }); diff --git a/app/assets/javascripts/admin/models/report.js b/app/assets/javascripts/admin/models/report.js index a182d84681..8157adc0b4 100644 --- a/app/assets/javascripts/admin/models/report.js +++ b/app/assets/javascripts/admin/models/report.js @@ -140,9 +140,12 @@ Discourse.Report = Discourse.Model.extend({ }); Discourse.Report.reopenClass({ - find: function(type) { - var model = Discourse.Report.create({type: type}); - Discourse.ajax("/admin/reports/" + type).then(function (json) { + find: function(type, startDate, endDate) { + + return Discourse.ajax("/admin/reports/" + type, {data: { + start_date: startDate, + end_date: endDate + }}).then(function (json) { // Add a percent field to each tuple var maxY = 0; json.report.data.forEach(function (row) { @@ -153,9 +156,9 @@ Discourse.Report.reopenClass({ row.percentage = Math.round((row.y / maxY) * 100); }); } + var model = Discourse.Report.create({type: type}); model.setProperties(json.report); - model.set('loaded', true); + return model; }); - return(model); } }); diff --git a/app/assets/javascripts/admin/routes/admin_reports_route.js b/app/assets/javascripts/admin/routes/admin_reports_route.js index cdc06720ec..21b842a9f8 100644 --- a/app/assets/javascripts/admin/routes/admin_reports_route.js +++ b/app/assets/javascripts/admin/routes/admin_reports_route.js @@ -9,5 +9,13 @@ Discourse.AdminReportsRoute = Discourse.Route.extend({ model: function(params) { return Discourse.Report.find(params.type); + }, + + setupController: function(controller, model) { + controller.setProperties({ + model: model, + startDate: moment(model.get('start_date')).format('YYYY-MM-DD'), + endDate: moment(model.get('end_date')).format('YYYY-MM-DD') + }); } }); diff --git a/app/assets/javascripts/admin/templates/email_preview_digest.hbs b/app/assets/javascripts/admin/templates/email_preview_digest.hbs index abd414dbdc..1e56304d85 100644 --- a/app/assets/javascripts/admin/templates/email_preview_digest.hbs +++ b/app/assets/javascripts/admin/templates/email_preview_digest.hbs @@ -19,7 +19,7 @@ {{#if loading}} -
+ {{loading-spinner}} {{else}} {{#if showHtml}} {{{html_content}}} diff --git a/app/assets/javascripts/admin/templates/flags.hbs b/app/assets/javascripts/admin/templates/flags.hbs index 2b34210b59..d5756938b1 100644 --- a/app/assets/javascripts/admin/templates/flags.hbs +++ b/app/assets/javascripts/admin/templates/flags.hbs @@ -9,7 +9,7 @@
{{#if loading}} -
+ {{loading-spinner}} {{else}} {{#if length}} @@ -160,7 +160,7 @@
{{#if view.loading}} -
+ {{loading-spinner}} {{/if}} {{else}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_emails.hbs b/app/assets/javascripts/admin/templates/logs/screened_emails.hbs index 68704f8ee0..a3a2bb8bd4 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_emails.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_emails.hbs @@ -1,7 +1,7 @@

{{i18n admin.logs.screened_emails.description}}

{{#if loading}} -
+ {{loading-spinner}} {{else}} {{#if model.length}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs index 042116be1f..7b61f357bf 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs @@ -4,7 +4,7 @@
{{#if loading}} -
+ {{loading-spinner}} {{else}} {{#if model.length}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_urls.hbs b/app/assets/javascripts/admin/templates/logs/screened_urls.hbs index e20c57ed26..077ae151ed 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_urls.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_urls.hbs @@ -1,7 +1,7 @@

{{i18n admin.logs.screened_urls.description}}

{{#if loading}} -
+ {{loading-spinner}} {{else}} {{#if model.length}} diff --git a/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs b/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs index 9216c394af..e422308d62 100644 --- a/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs +++ b/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs @@ -45,7 +45,7 @@ {{#if loading}}
-
+ {{loading-spinner}} {{else}} {{#if model.length}} {{view "staff-action-logs-list" content=controller}} diff --git a/app/assets/javascripts/admin/templates/reports.hbs b/app/assets/javascripts/admin/templates/reports.hbs index 6fbfeb39ce..a675a74480 100644 --- a/app/assets/javascripts/admin/templates/reports.hbs +++ b/app/assets/javascripts/admin/templates/reports.hbs @@ -1,14 +1,28 @@ -{{#if loaded}} -

{{title}}

+

{{title}}

- +
+ {{i18n admin.dashboard.reports.start_date}} {{input type="date" value=startDate}} + {{i18n admin.dashboard.reports.end_date}} {{input type="date" value=endDate}} + +
- +
+ {{#if viewingTable}} + {{i18n admin.dashboard.reports.view_table}} + {{else}} + {{i18n admin.dashboard.reports.view_table}} + {{/if}} + | + {{#if viewingBarChart}} + {{i18n admin.dashboard.reports.view_chart}} + {{else}} + {{i18n admin.dashboard.reports.view_chart}} + {{/if}} +
+{{#if refreshing}} + {{loading-spinner}} +{{else}} @@ -31,7 +45,4 @@ {{/each}}
{{xaxis}}
- -{{else}} - {{i18n loading}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/user_badges.hbs b/app/assets/javascripts/admin/templates/user_badges.hbs index 5b24155378..dfe9ed4690 100644 --- a/app/assets/javascripts/admin/templates/user_badges.hbs +++ b/app/assets/javascripts/admin/templates/user_badges.hbs @@ -7,7 +7,7 @@
{{#if loading}} -
+ {{loading-spinner}} {{else}}

{{i18n admin.badges.grant_badge}}

diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index 285182aebf..352f15b3ab 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -28,12 +28,17 @@
{{/if}} -

{{title}}

- -
+
+
+

{{title}}

+
+
+ +
+
{{#if loading}} -
+ {{loading-spinner}} {{else}} {{#if model.length}} @@ -43,6 +48,7 @@ {{/if}} + @@ -53,7 +59,6 @@ {{/if}} - {{#each model}} @@ -67,6 +72,7 @@ {{/if}} + diff --git a/app/assets/javascripts/discourse/components/groups-list.js.es6 b/app/assets/javascripts/discourse/components/groups-list.js.es6 deleted file mode 100644 index 09ac3c3dba..0000000000 --- a/app/assets/javascripts/discourse/components/groups-list.js.es6 +++ /dev/null @@ -1,12 +0,0 @@ -/** - Displays a list of groups that a user belongs to. - - @class Discourse.GroupsListComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ -export default Em.Component.extend({ - classNames: ['groups'] -}); - diff --git a/app/assets/javascripts/discourse/components/poster-avatar.js.es6 b/app/assets/javascripts/discourse/components/poster-avatar.js.es6 index 4d534e3199..03875b6a79 100644 --- a/app/assets/javascripts/discourse/components/poster-avatar.js.es6 +++ b/app/assets/javascripts/discourse/components/poster-avatar.js.es6 @@ -1,14 +1,9 @@ export default Ember.Component.extend({ tagName: 'a', - attributeBindings: ['href'], + attributeBindings: ['href','data-user-card'], classNames: ['trigger-user-card'], - href: Em.computed.alias('post.usernameUrl'), - - click: function(e) { - this.appEvents.trigger('poster:expand', $(e.target)); - this.sendAction('action', this.get('post')); - return false; - }, + href: Em.computed.oneWay('post.usernameUrl'), + "data-user-card": Em.computed.oneWay('post.username'), render: function(buffer) { var avatar = Handlebars.helpers.avatar(this.get('post'), {hash: {imageSize: 'large'}}); diff --git a/app/assets/javascripts/discourse/components/poster-name.js.es6 b/app/assets/javascripts/discourse/components/poster-name.js.es6 index be3ef940d8..ae1617f182 100644 --- a/app/assets/javascripts/discourse/components/poster-name.js.es6 +++ b/app/assets/javascripts/discourse/components/poster-name.js.es6 @@ -26,7 +26,7 @@ var PosterNameComponent = Em.Component.extend({ linkClass += ' ' + primaryGroupName; } // Main link - buffer.push("" + username + ""); + buffer.push("" + username + ""); // Add a glyph if we have one var glyph = this.posterGlyph(post); @@ -38,7 +38,7 @@ var PosterNameComponent = Em.Component.extend({ // Are we showing full names? if (name && this.get('displayNameOnPosts') && (this.sanitizeName(name) !== this.sanitizeName(username))) { name = Handlebars.Utils.escapeExpression(name); - buffer.push("" + name + ""); + buffer.push("" + name + ""); } // User titles @@ -59,20 +59,6 @@ var PosterNameComponent = Em.Component.extend({ } }, - click: function(e) { - var $target = $(e.target), - href = $target.attr('href'), - url = this.get('post.usernameUrl'); - - if (!Em.isEmpty(href) && href !== url) { - return true; - } else { - this.appEvents.trigger('poster:expand', $target); - this.sendAction('expandAction', this.get('post')); - } - return false; - }, - /** Overwrite this to give a user a custom font awesome glyph. diff --git a/app/assets/javascripts/discourse/components/text-overflow.js.es6 b/app/assets/javascripts/discourse/components/text-overflow.js.es6 new file mode 100644 index 0000000000..ae4e2a8259 --- /dev/null +++ b/app/assets/javascripts/discourse/components/text-overflow.js.es6 @@ -0,0 +1,9 @@ +export default Ember.Component.extend({ + _parse: function() { + this.$().ellipsis(); + }.on('didInsertElement'), + + render: function(buffer) { + buffer.push(this.get('text')); + } +}); diff --git a/app/assets/javascripts/discourse/components/topic-status.js.es6 b/app/assets/javascripts/discourse/components/topic-status.js.es6 index fe99221da5..1d0883f2e7 100644 --- a/app/assets/javascripts/discourse/components/topic-status.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-status.js.es6 @@ -40,13 +40,11 @@ export default Ember.Component.extend({ var renderIconIf = function(conditionProp, name, key, actionable) { if (!self.get(conditionProp)) { return; } - var title = I18n.t("topic_statuses." + key + ".help"); - + var title = Handlebars.Utils.escapeExpression(I18n.t("topic_statuses." + key + ".help")); var startTag = actionable ? "a href='#'" : "span"; var endTag = actionable ? "a" : "span"; - buffer.push("<" + startTag + - " title='" + title +"' class='topic-status'>"); + buffer.push("<" + startTag + " title='" + title + "' class='topic-status'>"); }; // Allow a plugin to add a custom icon to a topic diff --git a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 index eb1f7afc2d..650eb1c83a 100644 --- a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 +++ b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 @@ -85,7 +85,7 @@ var controllerOpts = { if (selected.length > 0) { promise = Discourse.Topic.bulkOperation(selected, operation); } else { - promise = Discourse.Topic.bulkOperationByFilter(this.get('filter'), operation); + promise = Discourse.Topic.bulkOperationByFilter('unread', operation, this.get('category.id')); } promise.then(function(result) { if (result && result.topic_ids) { @@ -105,8 +105,12 @@ var controllerOpts = { return Discourse.TopicTrackingState.current(); }.property(), + isFilterPage: function(filter, filterType) { + return filter.match(new RegExp(filterType + '$', 'gi')) ? true : false; + }, + showDismissRead: function() { - return this.get('filter') === 'unread' && this.get('topics.length') > 0; + return this.isFilterPage(this.get('filter'), 'unread') && this.get('topics.length') > 0; }.property('filter', 'topics.length'), showResetNew: function() { @@ -114,8 +118,8 @@ var controllerOpts = { }.property('filter', 'topics.length'), showDismissAtTop: function() { - return (this.get('filter') === 'new' || - this.get('filter') === 'unread') && + return (this.isFilterPage(this.get('filter'), 'new') || + this.isFilterPage(this.get('filter'), 'unread')) && this.get('topics.length') >= 30; }.property('filter', 'topics.length'), diff --git a/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 b/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 index f4b2fd84de..888f55c3ee 100644 --- a/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 +++ b/app/assets/javascripts/discourse/controllers/flag-action-type.js.es6 @@ -18,8 +18,12 @@ export default ObjectController.extend({ }.property('name_key'), formattedName: function(){ - return this.get('name').replace("{{username}}", this.get('controllers.flag.username')); - }.property('name'), + if (this.get("is_custom_flag")) { + return this.get('name').replace("{{username}}", this.get('controllers.flag.username')); + } else { + return I18n.t("flagging.formatted_name." + this.get('name_key')); + } + }.property('name', 'name_key', 'is_custom_flag'), selected: function() { return this.get('model') === this.get('controllers.flag.selected'); diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index ebfbc01e73..7e027eff33 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -1,4 +1,5 @@ import ObjectController from 'discourse/controllers/object'; +import { spinnerHTML } from 'discourse/helpers/loading-spinner'; export default ObjectController.extend(Discourse.SelectedPostsCount, { multiSelect: false, @@ -8,6 +9,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, { selectedPosts: null, selectedReplies: null, queryParams: ['filter', 'username_filters', 'show_deleted'], + searchHighlight: null, maxTitleLength: Discourse.computed.setting('max_topic_title_length'), @@ -509,7 +511,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, { }.property('isPrivateMessage'), loadingHTML: function() { - return "
"; + return spinnerHTML; }.property(), recoverTopic: function() { diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index a2058d9356..8fbfec442d 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -7,6 +7,8 @@ export default ObjectController.extend({ username: null, participant: null, avatar: null, + userLoading: null, + cardTarget: null, postStream: Em.computed.alias('controllers.topic.postStream'), enoughPostsForFiltering: Em.computed.gte('participant.post_count', 2), @@ -28,7 +30,12 @@ export default ObjectController.extend({ showMoreBadges: Em.computed.gt('moreBadgesCount', 0), - show: function(username, uploadedAvatarId) { + hasCardBadgeImage: function() { + var img = this.get('user.card_badge.image'); + return img && img.indexOf('fa-') !== 0; + }.property('user.card_badge.image'), + + show: function(username, target) { // XSS protection (should be encapsulated) username = username.replace(/[^A-Za-z0-9_]/g, ""); var url = "/users/" + username; @@ -42,17 +49,17 @@ export default ObjectController.extend({ var currentUsername = this.get('username'), wasVisible = this.get('visible'); - if (uploadedAvatarId) { - this.set('avatar', {username: username, uploaded_avatar_id: uploadedAvatarId}); - } else { - this.set('avatar', null); + this.set('avatar', null); + this.set('username', username); + + // If we click the avatar again, close it (unless its diff element on the screen). + if (target === this.get('cardTarget') && wasVisible) { + this.setProperties({ visible: false, username: null, avatar: null, cardTarget: null }); + return; } - this.setProperties({visible: true, username: username}); - - // If we click the avatar again, close it. - if (username === currentUsername && wasVisible) { - this.setProperties({ visible: false, username: null, avatar: null }); + if (username === currentUsername && this.get('userLoading') === username) { + // debounce return; } @@ -66,14 +73,19 @@ export default ObjectController.extend({ var self = this; self.set('user', null); + self.set('userLoading', username); + self.set('cardTarget', target); + Discourse.User.findByUsername(username).then(function (user) { - self.set('user', user); - self.set('avatar', user); + self.setProperties({ user: user, avatar: user, visible: true}); + }).finally(function(){ + self.set('userLoading', null); }); }, close: function() { this.set('visible', false); + this.set('cardTarget', null); }, actions: { diff --git a/app/assets/javascripts/discourse/helpers/loading-spinner.es6 b/app/assets/javascripts/discourse/helpers/loading-spinner.es6 new file mode 100644 index 0000000000..c7ce1c9346 --- /dev/null +++ b/app/assets/javascripts/discourse/helpers/loading-spinner.es6 @@ -0,0 +1,7 @@ +var spinnerHTML = "
"; + +Handlebars.registerHelper('loading-spinner', function() { + return new Handlebars.SafeString(spinnerHTML); +}); + +export { spinnerHTML }; diff --git a/app/assets/javascripts/discourse/lib/autocomplete.js.es6 b/app/assets/javascripts/discourse/lib/autocomplete.js.es6 index a79d76c99a..c945e37a47 100644 --- a/app/assets/javascripts/discourse/lib/autocomplete.js.es6 +++ b/app/assets/javascripts/discourse/lib/autocomplete.js.es6 @@ -331,7 +331,7 @@ export default function(options) { } // ESC - if (e.which === keys.escape) { + if (e.which === keys.esc) { if (completeStart !== null) { closeAutocomplete(); return false; diff --git a/app/assets/javascripts/discourse/lib/page_tracker.js b/app/assets/javascripts/discourse/lib/page_tracker.js index b94b5ab81e..842f2d9b11 100644 --- a/app/assets/javascripts/discourse/lib/page_tracker.js +++ b/app/assets/javascripts/discourse/lib/page_tracker.js @@ -19,7 +19,13 @@ Discourse.PageTracker = Ember.Object.extend(Ember.Evented, { router.on('didTransition', function() { this.send('refreshTitle'); - self.trigger('change', this.get('url'), Discourse.get('_docTitle')); + var url = this.get('url'); + + // Refreshing the title is debounced, so we need to trigger this in the + // next runloop to have the correct title. + Em.run.next(function() { + self.trigger('change', url, Discourse.get('_docTitle')); + }); }); this.set('started', true); } diff --git a/app/assets/javascripts/discourse/lib/utilities.js b/app/assets/javascripts/discourse/lib/utilities.js index 3ee9f2a981..2835ac843e 100644 --- a/app/assets/javascripts/discourse/lib/utilities.js +++ b/app/assets/javascripts/discourse/lib/utilities.js @@ -269,7 +269,7 @@ Discourse.Utilities = { @param {String} path The path **/ isAnImage: function(path) { - return (/\.(png|jpg|jpeg|gif|bmp|tif|tiff)$/i).test(path); + return (/\.(png|jpg|jpeg|gif|bmp|tif|tiff|svg|webp)$/i).test(path); }, /** @@ -279,7 +279,7 @@ Discourse.Utilities = { **/ allowsAttachments: function() { return Discourse.Utilities.authorizesAllExtensions() || - !(/((png|jpg|jpeg|gif|bmp|tif|tiff)(,\s)?)+$/i).test(Discourse.Utilities.authorizedExtensions()); + !(/((png|jpg|jpeg|gif|bmp|tif|tiff|svg|webp)(,\s)?)+$/i).test(Discourse.Utilities.authorizedExtensions()); }, displayErrorForUpload: function(data) { diff --git a/app/assets/javascripts/discourse/models/post_stream.js b/app/assets/javascripts/discourse/models/post_stream.js index 6955fdce12..026b84e3cf 100644 --- a/app/assets/javascripts/discourse/models/post_stream.js +++ b/app/assets/javascripts/discourse/models/post_stream.js @@ -130,7 +130,7 @@ Discourse.PostStream = Em.Object.extend({ hasNoFilters: function() { var streamFilters = this.get('streamFilters'); - return !(streamFilters && ((streamFilters.filter === 'summary') || streamFilters.userFilters)); + return !(streamFilters && ((streamFilters.filter === 'summary') || streamFilters.username_filters)); }.property('streamFilters.[]', 'topic.posts_count', 'posts.length'), /** diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js index f748e2b33e..08f669aa49 100644 --- a/app/assets/javascripts/discourse/models/topic.js +++ b/app/assets/javascripts/discourse/models/topic.js @@ -471,10 +471,12 @@ Discourse.Topic.reopenClass({ }); }, - bulkOperationByFilter: function(filter, operation) { + bulkOperationByFilter: function(filter, operation, categoryId) { + var data = { filter: filter, operation: operation }; + if (categoryId) data['category_id'] = categoryId; return Discourse.ajax("/topics/bulk", { type: 'PUT', - data: { filter: filter, operation: operation } + data: data }); }, diff --git a/app/assets/javascripts/discourse/routes/application.js.es6 b/app/assets/javascripts/discourse/routes/application.js.es6 index daacd5b852..794448f8d1 100644 --- a/app/assets/javascripts/discourse/routes/application.js.es6 +++ b/app/assets/javascripts/discourse/routes/application.js.es6 @@ -19,11 +19,6 @@ var ApplicationRoute = Discourse.Route.extend({ }); }, - expandUser: function(user) { - this.controllerFor('user-card').show(user.get('username'), user.get('uploaded_avatar_id')); - return true; - }, - error: function(err, transition) { if (err.status === 404) { // 404 diff --git a/app/assets/javascripts/discourse/routes/topic_route.js b/app/assets/javascripts/discourse/routes/topic_route.js index 38a4ffcd53..6f631e6d51 100644 --- a/app/assets/javascripts/discourse/routes/topic_route.js +++ b/app/assets/javascripts/discourse/routes/topic_route.js @@ -38,18 +38,6 @@ Discourse.TopicRoute = Discourse.Route.extend({ this.controllerFor("topic-admin-menu").send("show"); }, - // Modals that can pop up within a topic - expandPostUser: function(post) { - this.controllerFor('user-card').show(post.get('username'), post.get('uploaded_avatar_id')); - }, - - expandPostUsername: function(username) { - username = username.replace(/^@/, ''); - if (!Em.isEmpty(username)) { - this.controllerFor('user-card').show(username); - } - }, - showFlags: function(post) { Discourse.Route.showModal(this, 'flag', post); this.controllerFor('flag').setProperties({ selected: null }); diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs index 5f68cf2ddf..9c585d855e 100644 --- a/app/assets/javascripts/discourse/templates/badges/show.hbs +++ b/app/assets/javascripts/discourse/templates/badges/show.hbs @@ -36,11 +36,11 @@ {{/each}} {{#if canLoadMore}} -
+ {{loading-spinner}} {{/if}} {{else}} {{#unless userBadgesLoaded}} -
+ {{loading-spinner}} {{/unless}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs index 02d90715ae..32802cca7a 100644 --- a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs +++ b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs @@ -56,5 +56,5 @@ {{/if}} {{else}} -
+ {{loading-spinner}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/groups-list.hbs b/app/assets/javascripts/discourse/templates/components/groups-list.hbs deleted file mode 100644 index 95a012d1e1..0000000000 --- a/app/assets/javascripts/discourse/templates/components/groups-list.hbs +++ /dev/null @@ -1,6 +0,0 @@ -{{#if groups}} - {{i18n groups.title count=groups.length}}: - {{#each groups}} - {{#link-to 'group' this class="group-link"}}{{name}}{{/link-to}} - {{/each}} -{{/if}} diff --git a/app/assets/javascripts/discourse/templates/composer.hbs b/app/assets/javascripts/discourse/templates/composer.hbs index 0a12c2389b..7dd2e37f04 100644 --- a/app/assets/javascripts/discourse/templates/composer.hbs +++ b/app/assets/javascripts/discourse/templates/composer.hbs @@ -1,4 +1,4 @@ -
+{{loading-spinner}}
diff --git a/app/assets/javascripts/discourse/templates/discovery.hbs b/app/assets/javascripts/discourse/templates/discovery.hbs index 3c143c9780..eb3a50edb5 100644 --- a/app/assets/javascripts/discourse/templates/discovery.hbs +++ b/app/assets/javascripts/discourse/templates/discovery.hbs @@ -11,7 +11,7 @@
-
+ {{loading-spinner}}
diff --git a/app/assets/javascripts/discourse/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/discovery/topics.hbs index 4e05e65e4c..121fb1da57 100644 --- a/app/assets/javascripts/discourse/templates/discovery/topics.hbs +++ b/app/assets/javascripts/discourse/templates/discovery/topics.hbs @@ -72,7 +72,7 @@
  {{i18n username}}{{i18n email}} {{i18n admin.users.last_emailed}} {{i18n last_seen}} {{i18n admin.user.topics_entered}}{{i18n admin.users.approved}} 
{{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}} {{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}{{{unbound email}}} {{{unbound last_emailed_age}}} {{{unbound last_seen_age}}} {{{unbound topics_entered}}}
{{#if invitesLoading}} -
+ {{loading-spinner}} {{/if}} {{else}} diff --git a/app/assets/javascripts/discourse/templates/user/notifications.hbs b/app/assets/javascripts/discourse/templates/user/notifications.hbs index e1c280b530..52427b1a34 100644 --- a/app/assets/javascripts/discourse/templates/user/notifications.hbs +++ b/app/assets/javascripts/discourse/templates/user/notifications.hbs @@ -23,7 +23,7 @@ {{/each}} {{#if loading}} -
+ {{loading-spinner}} {{/if}} {{#unless canLoadMore}}
diff --git a/app/assets/javascripts/discourse/templates/user/posts.hbs b/app/assets/javascripts/discourse/templates/user/posts.hbs index 46c4313e7f..42220d4899 100644 --- a/app/assets/javascripts/discourse/templates/user/posts.hbs +++ b/app/assets/javascripts/discourse/templates/user/posts.hbs @@ -27,5 +27,5 @@ {{/each}} {{#if loading}} -
+ {{loading-spinner}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/stream.hbs b/app/assets/javascripts/discourse/templates/user/stream.hbs index c3e5da2672..34b4677351 100644 --- a/app/assets/javascripts/discourse/templates/user/stream.hbs +++ b/app/assets/javascripts/discourse/templates/user/stream.hbs @@ -2,7 +2,7 @@ diff --git a/app/assets/javascripts/discourse/views/topic.js.es6 b/app/assets/javascripts/discourse/views/topic.js.es6 index 0cfc4a3d76..76733c7950 100644 --- a/app/assets/javascripts/discourse/views/topic.js.es6 +++ b/app/assets/javascripts/discourse/views/topic.js.es6 @@ -70,35 +70,6 @@ export default Discourse.View.extend(AddCategoryClass, Discourse.Scrolling, { }.on('willDestroyElement'), - debounceLoadSuggested: Discourse.debounce(function(){ - if (this.get('isDestroyed') || this.get('isDestroying')) { return; } - - var incoming = this.get('topicTrackingState.newIncoming'), - suggested = this.get('topic.details.suggested_topics'), - topicId = this.get('topic.id'); - - if(suggested) { - var existing = _.invoke(suggested, 'get', 'id'), - lookup = _.chain(incoming) - .last(Discourse.SiteSettings.suggested_topics) - .reverse() - .union(existing) - .uniq() - .without(topicId) - .first(Discourse.SiteSettings.suggested_topics) - .value(); - - Discourse.TopicList.loadTopics(lookup, "").then(function(topics){ - suggested.clear(); - suggested.pushObjects(topics); - }); - } - }, 1000), - - hasNewSuggested: function(){ - this.debounceLoadSuggested(); - }.observes('topicTrackingState.incomingCount'), - gotFocus: function(){ if (Discourse.get('hasFocus')){ this.scrolled(); diff --git a/app/assets/javascripts/discourse/views/user-card.js.es6 b/app/assets/javascripts/discourse/views/user-card.js.es6 index a9e05e5855..96ef15947f 100644 --- a/app/assets/javascripts/discourse/views/user-card.js.es6 +++ b/app/assets/javascripts/discourse/views/user-card.js.es6 @@ -6,7 +6,7 @@ var clickOutsideEventName = "mousedown.outside-user-card", export default Discourse.View.extend(CleansUp, { elementId: 'user-card', - classNameBindings: ['controller.visible::hidden', 'controller.showBadges'], + classNameBindings: ['controller.visible::hidden', 'controller.showBadges', 'controller.hasCardBadgeImage'], allowBackgrounds: Discourse.computed.setting('allow_profile_backgrounds'), addBackground: function() { @@ -30,8 +30,11 @@ export default Discourse.View.extend(CleansUp, { $('html').off(clickOutsideEventName).on(clickOutsideEventName, function(e) { if (self.get('controller.visible')) { var $target = $(e.target); - if ($target.closest('.trigger-user-card').length > 0) { return; } - if (self.$().has(e.target).length !== 0) { return; } + if ($target.closest('[data-user-card]').data('userCard') || + $target.closest('a.mention').length > 0 || + $target.closest('#user-card').length > 0) { + return; + } self.get('controller').close(); } @@ -39,19 +42,22 @@ export default Discourse.View.extend(CleansUp, { return true; }); + var expand = function(username, $target){ + self._posterExpand($target); + self.get('controller').show(username, $target[0]); + return false; + }; + $('#main-outlet').on(clickDataExpand, '[data-user-card]', function(e) { var $target = $(e.currentTarget); - self._posterExpand($target); - self.get('controller').show($target.data('user-card')); - return false; + var username = $target.data('user-card'); + return expand(username, $target); }); $('#main-outlet').on(clickMention, 'a.mention', function(e) { var $target = $(e.target); - self._posterExpand($target); var username = $target.text().replace(/^@/, ''); - self.get('controller').show(username); - return false; + return expand(username, $target); }); }.on('didInsertElement'), @@ -67,9 +73,11 @@ export default Discourse.View.extend(CleansUp, { var overage = ($(window).width() - 50) - (position.left + width); if (overage < 0) { - position.left += overage; - position.top += target.height() + 5; + position.left -= (width/2) - 10; + position.top += target.height() + 8; } + + position.top -= $('#main-outlet').offset().top; self.$().css(position); } } diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 79c2e6e1ec..0423e7040a 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -45,6 +45,7 @@ //= require ./discourse/components/visible //= require ./discourse/helpers/user-avatar //= require ./discourse/helpers/cold-age-class +//= require ./discourse/helpers/loading-spinner //= require ./discourse/dialects/dialect //= require_tree ./discourse/dialects diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js index 4e769ed9ba..a4258f2003 100644 --- a/app/assets/javascripts/vendor.js +++ b/app/assets/javascripts/vendor.js @@ -40,4 +40,5 @@ //= require ember-cloaking //= require break_string //= require buffered-proxy +//= require jquery.autoellipsis-1.0.10.min.js //= require_tree ./discourse/ember diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index ce887d58f5..03b4b10853 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -39,6 +39,9 @@ td.flaggers td { } } + .view-options { + float: right; + } table.report { margin-top: 20px; tr { @@ -89,6 +92,10 @@ td.flaggers td { margin-top: 20px; } +.admin-title { + height: 45px; +} + .admin-controls { background-color: dark-light-diff($primary, $secondary, 90%, -75%); padding: 10px 10px 3px 0; diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index 4fd2a54671..ae53dc8a8f 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -149,17 +149,6 @@ } } -.topics-loading { - width: 20px; - margin: 0 auto; - padding: 25px 15px; - background: { - image: image-url("spinner_96.gif"); - repeat: no-repeat; - size: 50px; - }; -} - .list-controls { .home { background-color: scale-color-diff(); diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 4d2e4137c8..93a1b716aa 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -15,6 +15,10 @@ big { font-size: 28px; } +small { + font-size: 10px; +} + blockquote { background-color: scale-color-diff(); border-left: 5px solid darken(scale-color-diff(), 10%); @@ -143,28 +147,6 @@ body { } } -.spinner { - width: 20px; - margin: 0 auto; - padding: 25px 15px; - background: { - image: image-url("spinner_96.gif"); - repeat: no-repeat; - size: 50px; - }; - -webkit-animation: spinner .25s; - animation: spinner .25s; - margin-top: 10px; -} - -//loading spinner fade-in -@-webkit-keyframes spinner { - from {opacity: 0} - to {opacity: 1} -}@keyframes fade { - from {opacity: 0} - to {opacity: 1} -} .avatar-wrapper { background-color: $secondary; @@ -193,3 +175,115 @@ body { .fade.in { opacity: 1; } + +.spinner { + margin: 20px auto 0 auto; + width: 35px; + height: 35px; + position: relative; + + .container1 > div, .container2 > div, .container3 > div { + width: 6px; + height: 6px; + background-color: $primary; + + border-radius: 100%; + position: absolute; + -webkit-animation: bouncedelay 1.2s infinite ease-in-out; + animation: bouncedelay 1.2s infinite ease-in-out; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + } + + .spinner-container { + position: absolute; + width: 100%; + height: 100%; + } + + .container2 { + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); + } + + .container3 { + -webkit-transform: rotateZ(90deg); + transform: rotateZ(90deg); + } + + .circle1 { top: 0; left: 0; } + .circle2 { top: 0; right: 0; } + .circle3 { right: 0; bottom: 0; } + .circle4 { left: 0; bottom: 0; } + + .container2 .circle1 { + -webkit-animation-delay: -1.1s; + animation-delay: -1.1s; + } + + .container3 .circle1 { + -webkit-animation-delay: -1.0s; + animation-delay: -1.0s; + } + + .container1 .circle2 { + -webkit-animation-delay: -0.9s; + animation-delay: -0.9s; + } + + .container2 .circle2 { + -webkit-animation-delay: -0.8s; + animation-delay: -0.8s; + } + + .container3 .circle2 { + -webkit-animation-delay: -0.7s; + animation-delay: -0.7s; + } + + .container1 .circle3 { + -webkit-animation-delay: -0.6s; + animation-delay: -0.6s; + } + + .container2 .circle3 { + -webkit-animation-delay: -0.5s; + animation-delay: -0.5s; + } + + .container3 .circle3 { + -webkit-animation-delay: -0.4s; + animation-delay: -0.4s; + } + + .container1 .circle4 { + -webkit-animation-delay: -0.3s; + animation-delay: -0.3s; + } + + .container2 .circle4 { + -webkit-animation-delay: -0.2s; + animation-delay: -0.2s; + } + + .container3 .circle4 { + -webkit-animation-delay: -0.1s; + animation-delay: -0.1s; + } + +} + +@-webkit-keyframes bouncedelay { + 0%, 80%, 100% { -webkit-transform: scale(0.0) } + 40% { -webkit-transform: scale(1.0) } +} + +@keyframes bouncedelay { + 0%, 80%, 100% { + transform: scale(0.0); + -webkit-transform: scale(0.0); + } 40% { + transform: scale(1.0); + -webkit-transform: scale(1.0); + } +} diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index 0dfb381db6..594722caa3 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -270,18 +270,17 @@ } .searching { - display: block; position: absolute; - top: 17px; - right: 15px; - width: 22px; - height: 22px; - background: { - image: image-url("spinner_96.gif"); - repeat: no-repeat; - position: 0px 50%; - size: 18px; - }; + top: 0; + right: 0; + .spinner { + width: 16px; + height: 16px; + } + .container1 > div, .container2 > div, .container3 > div { + width: 4px; + height: 4px; + } } // I am ghetto using this to display "Show More".. be warned .no-results { @@ -366,5 +365,3 @@ #search-help table td { padding-right: 10px; } - - diff --git a/app/assets/stylesheets/desktop/discourse.scss b/app/assets/stylesheets/desktop/discourse.scss index ac611113ad..d346a4fec1 100644 --- a/app/assets/stylesheets/desktop/discourse.scss +++ b/app/assets/stylesheets/desktop/discourse.scss @@ -33,7 +33,6 @@ body { .boxed { height: 100%; - @include border-radius-all(5px); &.white { background-color: $secondary; } diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss index cb110a9217..c1539bd278 100644 --- a/app/assets/stylesheets/desktop/topic-list.scss +++ b/app/assets/stylesheets/desktop/topic-list.scss @@ -68,7 +68,7 @@ background-color: $secondary; } &.highlighted { - background-color: scale-color($tertiary, $lightness: 85%); + background-color: dark-light-diff($tertiary, $secondary, 90%, -41%); } } button.bulk-select { diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index ea3c2c0b6f..a889c97026 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -242,7 +242,7 @@ nav.post-controls { .topic-body { background: dark-light-diff($primary, $secondary, 90%, -65%) !important; - width: 86%; + width: 87%; padding-left: 1%; padding-right: 1%; border-top: none; @@ -253,7 +253,7 @@ nav.post-controls { .topic-avatar { width: 45px; - margin: 0 10px 0 15px; + margin: 0 5px 0 15px; border: none; } @@ -614,11 +614,11 @@ iframe { position: relative; } - a.mention { - padding: 2px 4px; - color: $primary; - background: scale-color-diff(); - } +a.mention { + padding: 2px 4px; + color: $primary; + background: scale-color-diff(); +} .modal-body { diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index d03ec4eab4..2a4f7ff681 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -11,11 +11,13 @@ color: $secondary; background-size: cover; background-position: center center; + min-height: 175px; .card-content { - padding: 12px; + padding: 12px 12px 0 12px; background: rgba($primary, .85); - margin-top: 100px; + margin-top: 80px; + &:after { content: ''; display: block; @@ -24,6 +26,8 @@ } &.no-bg { + min-height: 50px; + .card-content { margin-top: 0; } @@ -49,6 +53,10 @@ a { color: $secondary; } + i { + font-size: 18px; + color: $secondary; + } } h2 { @@ -69,11 +77,11 @@ font-size: 13px; font-weight: normal; margin-top: 0; - color: scale-color($primary, $lightness: 50%); + color: dark-light-diff($secondary, $primary, 25%, -25%); overflow: hidden; text-overflow: ellipsis; a { - color: $secondary; + color: dark-light-diff($primary, $secondary, 50%, -50%); } } .groups { @@ -88,11 +96,19 @@ } .metadata { - position: absolute; - right: 20px; - top: 10px; - max-width: 180px; - text-align: right; + width: 100%; + clear: both; + padding-top: 5px; + h3 { + display: inline; + margin-right: 5px; + .desc, a { + color: dark-light-diff($secondary, $primary, 50%, -50%); + } + } + div {display: inline; color: scale-color($primary, $lightness: 50%); + .group-link {color: scale-color($primary, $lightness: 50%);} + } } .bottom { @@ -100,12 +116,14 @@ padding-top: 10px; } + &.has-card-badge-image .bio { + width: 75%; + } + .bio { - max-height: 55px; - overflow: auto; - float: left; - margin: 10px 0; - width: 70%; + padding: 15px 0 0 0; + clear: left; + a { color: $secondary; text-decoration: underline; @@ -113,23 +131,26 @@ img { max-width: 100%; } + + a.mention { + background-color: dark-light-diff($secondary, $primary, 50%, -60%); + } + .overflow { + max-height: 60px; + overflow: hidden; + } } img.avatar { float: left; padding-right: 10px; - margin-top: -60px; + margin-top: -53px; } p { margin: 0 0 5px 0; } - p.loading { - margin-top: 45px; - color: $primary; - } - .btn { margin: 0 0 7px 0; box-sizing: border-box; @@ -137,9 +158,8 @@ .usercard-controls { list-style-type: none; + float: right; margin: 0; - position: absolute; - right: 12px; a {width: 100%; min-width: 150px;} } @@ -156,24 +176,27 @@ height: 60px; position: relative; width: 45%; + margin-top: 11px; span { position: absolute; bottom: 0; display: block; + width: 250px; } } .badge-section { - margin-top: 10px; float: left; - width: 390px; + width: 500px; + padding-bottom: 10px; + margin-top: 5px; .user-badge { background: transparent; - color: $secondary; - + color: dark-light-diff($primary, $secondary, 50%, -50%); + border-color: dark-light-diff($primary, $secondary, 50%, -50%); } h3 { @@ -186,20 +209,24 @@ .more-user-badges { @extend .user-badge; padding: 3px 8px; - font-size: 13px; } } .suspended { color: $danger; - margin-bottom: 10px; + margin-bottom: 5px; + clear: left; + padding-top: 15px; } .card-badge { img { - max-width: 100px; + max-width: 120px; } - float: right; - margin-right: 5px; + position: absolute; + right: 12px; + bottom: 12px; + font-size: 30px; + i {color: $secondary;} } } diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 44975240f0..8c8355b103 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -198,17 +198,19 @@ dl dd { display: inline; - margin: 0 15px 0 5px; + margin: 0 10px 0 0; padding: 0; } dl dt { display: inline-block; + margin: 0 5px 0 0; + padding: 0; } dl { - padding: 10px 15px; margin: 0; + padding: 8px 10px; } dd { @@ -217,6 +219,15 @@ color: $primary; } + dd.groups { + span:after { + content: ',' + } + span:last-of-type:after { + content:'' + } + } + dt { color: dark-light-diff($secondary, $primary, 50%, -40%); margin: 0; @@ -271,24 +282,28 @@ width: 100%; position: relative; float: left; - color: dark-light-diff($secondary, $primary, 75%, 0%); + color: $secondary; h1 {font-weight: bold;} .primary-textual { padding: 3px; a[href] { - color: dark-light-diff($secondary, $primary, 75%, -10%); + color: $secondary; } } .bio { - color: dark-light-diff($secondary, $primary, 75%, 0%); + color: $secondary; max-height: 300px; overflow: auto; max-width: 750px; + a.mention { + background-color: dark-light-diff($secondary, $primary, 20%, -50%); + } + a[href] { color: dark-light-diff($secondary, $primary, 75%, 0%); text-decoration: underline; @@ -304,6 +319,7 @@ .controls { padding: 0 0 12px 0; float: right; + width: 180px; ul {list-style-type: none;} a { padding: 5px 10px; @@ -321,6 +337,7 @@ .about.collapsed-info { .controls { margin-top: 0; + width: auto; ul { li {display: inline;} a { diff --git a/app/assets/stylesheets/mobile/discourse.scss b/app/assets/stylesheets/mobile/discourse.scss index 12262ba751..af61f9c35e 100644 --- a/app/assets/stylesheets/mobile/discourse.scss +++ b/app/assets/stylesheets/mobile/discourse.scss @@ -10,9 +10,8 @@ body { .boxed { height: 100%; - @include border-radius-all(5px); .contents { - padding: 10px 10px 10px 10px; + padding: 10px; } &.white { background-color: $secondary; diff --git a/app/assets/stylesheets/mobile/header.scss b/app/assets/stylesheets/mobile/header.scss index 59736598cb..adfb6888be 100644 --- a/app/assets/stylesheets/mobile/header.scss +++ b/app/assets/stylesheets/mobile/header.scss @@ -50,6 +50,12 @@ // Search + .searching { + position: absolute; + top: 0; + right: 25px; + } + input[type='text'] { width: 265px; font-size: 16px; @@ -75,7 +81,6 @@ font-size: 14px; } -.d-dropdown .no-results .show-help { - display: none; +.d-dropdown#search-dropdown .heading { + padding: 0; } - diff --git a/app/assets/stylesheets/mobile/modal.scss b/app/assets/stylesheets/mobile/modal.scss index fc5e991f36..bfacdb308b 100644 --- a/app/assets/stylesheets/mobile/modal.scss +++ b/app/assets/stylesheets/mobile/modal.scss @@ -163,3 +163,16 @@ float: none !important; } } + +#search-help { + max-width: 300px; +} + +#search-help h2 { + margin: 0; + font-size: 12px; +} + +#search-help p { + margin: 5px; +} \ No newline at end of file diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 3194bbf977..c605a3f2dc 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -364,10 +364,6 @@ iframe { display: none; } -.topic-meta-data { - margin-bottom: 10px; -} - .open>.dropdown-menu { display: block; } @@ -470,12 +466,13 @@ span.highlighted { } .topic-meta-data { + margin-left: 60px; white-space: nowrap; position: absolute; - width: 100%; + width: 80%; left: 0px; .names { - margin: 5px 0 0 65px; + margin: 5px 0 0 5px; line-height: 17px; span { display: block; diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index 97cb40a72d..7acd33b644 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -151,12 +151,11 @@ .user-navigation { width: 100%; - margin-right: 1.8018%; margin-top: 20px; h3 { color: $primary; - margin: 20px 0 10px 0; + margin: 10px; } } @@ -241,7 +240,6 @@ .btn { padding: 3px 12px; } dl dd { - display: inline; margin: 0 15px 0 5px; padding: 0; } @@ -344,18 +342,16 @@ } .controls { - padding: 0 0 12px 0; - float: right; - ul {list-style-type: none;} + width: 55%; + float:right; + ul { + list-style-type: none; + margin: 0; + } a { padding: 5px 10px; width: 140px; margin-bottom: 10px; - } - - .right { - float: right; - margin-left: 5px; } } } @@ -411,6 +407,7 @@ .user-stream { + padding: 0 10px; .end-of-stream { width: auto; } @@ -445,8 +442,7 @@ .time, .delete-info { display: block; float: right; - color: $primary; - margin-right: 8px; + color: lighten($primary, 40%); font-size: 11px; } .delete-info i { diff --git a/app/controllers/admin/impersonate_controller.rb b/app/controllers/admin/impersonate_controller.rb index 8e3129942d..f57692d923 100644 --- a/app/controllers/admin/impersonate_controller.rb +++ b/app/controllers/admin/impersonate_controller.rb @@ -4,11 +4,13 @@ class Admin::ImpersonateController < Admin::AdminController params.require(:username_or_email) user = User.find_by_username_or_email(params[:username_or_email]) - raise Discourse::NotFound if user.blank? guardian.ensure_can_impersonate!(user) + # log impersonate + StaffActionLogger.new(current_user).log_impersonate(user) + # Log on as the user log_on_user(user) diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 8b2d4f066f..fe6408ccd8 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -3,12 +3,17 @@ require_dependency 'report' class Admin::ReportsController < Admin::AdminController def show - report_type = params[:type] raise Discourse::NotFound.new unless report_type =~ /^[a-z0-9\_]+$/ - report = Report.find(report_type) + start_date = 1.month.ago + start_date = Time.parse(params[:start_date]) if params[:start_date].present? + + end_date = start_date + 1.month + end_date = Time.parse(params[:end_date]) if params[:end_date].present? + + report = Report.find(report_type, {start_date: start_date, end_date: end_date}) raise Discourse::NotFound.new if report.blank? render_json_dump(report: report) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index e6750c2b19..ed69ad463f 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -25,8 +25,14 @@ class Admin::UsersController < Admin::AdminController :revoke_api_key] def index - query = ::AdminUserIndexQuery.new(params) - render_serialized(query.find_users, AdminUserSerializer) + users = ::AdminUserIndexQuery.new(params).find_users + + if params[:show_emails] == "true" + guardian.can_see_emails = true + StaffActionLogger.new(current_user).log_show_emails(users) + end + + render_serialized(users, AdminUserSerializer) end def show diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index f9b8b6454c..c144648130 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -99,7 +99,7 @@ class CategoriesController < ApplicationController category_id = params[:category_id].to_i notification_level = params[:notification_level].to_i - CategoryUser.set_notification_level_for_category(current_user, notification_level , category_id) + CategoryUser.set_notification_level_for_category(current_user, notification_level, category_id) render json: success_json end diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index a527086354..e4e31cb55b 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -364,7 +364,9 @@ class TopicsController < ApplicationController topic_ids = params[:topic_ids].map {|t| t.to_i} elsif params[:filter] == 'unread' tq = TopicQuery.new(current_user) - topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.pluck(:id) + topics = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics + topics = topics.where('category_id = ?', params[:category_id]) if params[:category_id] + topic_ids = topics.pluck(:id) else raise ActionController::ParameterMissing.new(:topic_ids) end @@ -439,7 +441,7 @@ class TopicsController < ApplicationController end def perform_show_response - topic_view_serializer = TopicViewSerializer.new(@topic_view, scope: guardian, root: false) + topic_view_serializer = TopicViewSerializer.new(@topic_view, scope: guardian, root: false, include_raw: !!params[:include_raw]) respond_to do |format| format.html do diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 521f1b6170..00b2e61862 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -245,7 +245,7 @@ class UsersController < ApplicationController activation.finish # save user email in session, to show on account-created page - session["user_created_email"] = user.email + session["user_created_message"] = activation.message render json: { success: true, @@ -364,6 +364,7 @@ class UsersController < ApplicationController end def account_created + @message = session['user_created_message'] expires_now render layout: 'no_js' end diff --git a/app/helpers/user_notifications_helper.rb b/app/helpers/user_notifications_helper.rb index a86871ba5b..99807e2db4 100644 --- a/app/helpers/user_notifications_helper.rb +++ b/app/helpers/user_notifications_helper.rb @@ -1,13 +1,5 @@ module UserNotificationsHelper - def self.sanitize_options - return @sanitize_options if @sanitize_options - @sanitize_options = Sanitize::Config::RELAXED.deep_dup - @sanitize_options[:elements] << 'aside' << 'div' - @sanitize_options[:attributes][:all] << 'class' - @sanitize_options - end - def indent(text, by=2) spacer = " " * by result = "" @@ -57,21 +49,15 @@ module UserNotificationsHelper end def email_excerpt(html, posts_count) - # If there's only one post, include the whole thing. - if posts_count == 1 - raw Sanitize.clean(html, UserNotificationsHelper.sanitize_options) - else - # Otherwise, try just the first paragraph. - para = first_paragraph_from(html) - raw Sanitize.clean(para.to_s, UserNotificationsHelper.sanitize_options) - end + # only include 1st paragraph when more than 1 posts + html = first_paragraph_from(html).to_s if posts_count > 1 + raw format_for_email(html) end - def cooked_post_for_email(post) - PrettyText.format_for_email(post.cooked).html_safe + def format_for_email(html) + PrettyText.format_for_email(html).html_safe end - def email_category(category, opts=nil) opts = opts || {} diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index 8c215bdef8..dad9ee5540 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -26,7 +26,7 @@ module Jobs user_data.each do |user| user_array = Array.new group_names = get_group_names(user).join(';') - user_array.push(user['id']).push(user['name']).push(user['username']).push(user['email']) + user_array = get_user_fields(user) user_array.push(group_names) if group_names != '' data.push(user_array) end @@ -51,6 +51,22 @@ module Jobs return group_names end + def get_user_fields(user) + csv_user_attrs = ['id','name','username','email','created_at'] + csv_user_stats_attr = ['topics_entered','posts_read_count','time_read','topic_count','post_count','likes_given','likes_received'] + user_array = [] + + csv_user_attrs.each do |user_attr| + user_array.push(user.attributes[user_attr]) + end + + csv_user_stats_attr.each do |user_stat_attr| + user_array.push(user.user_stat.attributes[user_stat_attr]) + end + + return user_array + end + def set_file_path @file_name = "export_#{SecureRandom.hex(4)}.csv" # ensure directory exists diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index c76dbf2db1..476a090392 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -250,9 +250,7 @@ class UserNotifications < ActionMailer::Base ) template = "user_notifications.user_#{notification_type}" - if post.topic.private_message? - template << "_pm" - end + template << "_pm" if post.topic.private_message? email_opts = { topic_title: title, diff --git a/app/models/category_user.rb b/app/models/category_user.rb index 4ba4a130d1..d53fb804e2 100644 --- a/app/models/category_user.rb +++ b/app/models/category_user.rb @@ -16,19 +16,17 @@ class CategoryUser < ActiveRecord::Base end def self.auto_track_new_topic(topic) - apply_default_to_topic( - topic, - TopicUser.notification_levels[:tracking], - TopicUser.notification_reasons[:auto_track_category] - ) + apply_default_to_topic(topic, + TopicUser.notification_levels[:tracking], + TopicUser.notification_reasons[:auto_track_category] + ) end def self.auto_watch_new_topic(topic) - apply_default_to_topic( - topic, - TopicUser.notification_levels[:watching], - TopicUser.notification_reasons[:auto_watch_category] - ) + apply_default_to_topic(topic, + TopicUser.notification_levels[:watching], + TopicUser.notification_reasons[:auto_watch_category] + ) end def self.batch_set(user, level, category_ids) @@ -49,8 +47,6 @@ class CategoryUser < ActiveRecord::Base def self.set_notification_level_for_category(user, level, category_id) record = CategoryUser.where(user: user, category_id: category_id).first - # oder CategoryUser.where(user: user, category_id: category_id).destroy_all - # und danach mir create anlegen. if record.present? record.notification_level = level @@ -62,23 +58,21 @@ class CategoryUser < ActiveRecord::Base def self.apply_default_to_topic(topic, level, reason) # Can not afford to slow down creation of topics when a pile of users are watching new topics, reverting to SQL for max perf here - sql = < ? and skipped = false', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count + def self.count_per_day(start_date, end_date) + where('created_at >= ? and created_at < ? AND skipped = false', start_date, end_date).group('date(created_at)').order('date(created_at)').count end def self.for(reply_key) @@ -22,8 +22,7 @@ class EmailLog < ActiveRecord::Base end def self.last_sent_email_address - where(email_type: 'signup').order('created_at DESC') - .first.try(:to_address) + where(email_type: 'signup').order('created_at DESC').first.try(:to_address) end end diff --git a/app/models/optimized_image.rb b/app/models/optimized_image.rb index 5f5ecb645d..5df84700ea 100644 --- a/app/models/optimized_image.rb +++ b/app/models/optimized_image.rb @@ -31,9 +31,15 @@ class OptimizedImage < ActiveRecord::Base extension = File.extname(original_path) temp_file = Tempfile.new(["discourse-thumbnail", extension]) temp_path = temp_file.path - original_path += "[0]" unless opts[:allow_animation] - if resize(original_path, temp_path, width, height) + if extension =~ /\.svg$/i + FileUtils.cp(original_path, temp_path) + resized = true + else + resized = resize(original_path, temp_path, width, height, opts[:allow_animation]) + end + + if resized thumbnail = OptimizedImage.create!( upload_id: upload.id, sha1: Digest::SHA1.file(temp_path).hexdigest, @@ -72,7 +78,8 @@ class OptimizedImage < ActiveRecord::Base end end - def self.resize(from, to, width, height) + def self.resize(from, to, width, height, allow_animation=false) + from << "[0]" unless allow_animation # NOTE: ORDER is important! instructions = %W{ #{from} diff --git a/app/models/post.rb b/app/models/post.rb index 82fa47a262..8ce68da34d 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -460,8 +460,8 @@ class Post < ActiveRecord::Base Jobs.enqueue(:process_post, args) end - def self.public_posts_count_per_day(since_days_ago=30) - public_posts.where('posts.created_at > ?', since_days_ago.days.ago).group('date(posts.created_at)').order('date(posts.created_at)').count + def self.public_posts_count_per_day(start_date, end_date) + public_posts.where('posts.created_at >= ? AND posts.created_at <= ?', start_date, end_date).group('date(posts.created_at)').order('date(posts.created_at)').count end def self.private_messages_count_per_day(since_days_ago, topic_subtype) diff --git a/app/models/report.rb b/app/models/report.rb index bf6b04b26f..1ddf45cf13 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -2,39 +2,51 @@ require_dependency 'topic_subtype' class Report - attr_accessor :type, :data, :total, :prev30Days + attr_accessor :type, :data, :total, :prev30Days, :start_date, :end_date + + def self.default_days + 30 + end def initialize(type) @type = type @data = nil @total = nil @prev30Days = nil + @start_date ||= 1.month.ago + @end_date ||= Time.now end def as_json(options = nil) { - type: self.type, - title: I18n.t("reports.#{self.type}.title"), - xaxis: I18n.t("reports.#{self.type}.xaxis"), - yaxis: I18n.t("reports.#{self.type}.yaxis"), - data: self.data, - total: self.total, + type: type, + title: I18n.t("reports.#{type}.title"), + xaxis: I18n.t("reports.#{type}.xaxis"), + yaxis: I18n.t("reports.#{type}.yaxis"), + data: data, + total: total, + start_date: start_date, + end_date: end_date, prev30Days: self.prev30Days } end - def self.find(type, opts={}) + def self.find(type, opts=nil) + opts ||= {} report_method = :"report_#{type}" return nil unless respond_to?(report_method) # Load the report report = Report.new(type) + + report.start_date = opts[:start_date] if opts[:start_date] + report.end_date = opts[:end_date] if opts[:end_date] send(report_method, report) report end def self.report_visits(report) - basic_report_about report, UserVisit, :by_day + basic_report_about report, UserVisit, :by_day, report.start_date, report.end_date end def self.report_signups(report) @@ -42,15 +54,15 @@ class Report end def self.report_topics(report) - basic_report_about report, Topic, :listable_count_per_day + basic_report_about report, Topic, :listable_count_per_day, report.start_date, report.end_date report.total = Topic.listable_topics.count - report.prev30Days = Topic.listable_topics.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = Topic.listable_topics.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_posts(report) - basic_report_about report, Post, :public_posts_count_per_day + basic_report_about report, Post, :public_posts_count_per_day, report.start_date, report.end_date report.total = Post.public_posts.count - report.prev30Days = Post.public_posts.where('posts.created_at > ? and posts.created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = Post.public_posts.where('posts.created_at > ? and posts.created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_emails(report) @@ -58,20 +70,20 @@ class Report end def self.report_about(report, subject_class, report_method = :count_per_day) - basic_report_about report, subject_class, report_method + basic_report_about report, subject_class, report_method, report.start_date, report.end_date add_counts(report, subject_class) end def self.basic_report_about(report, subject_class, report_method, *args) report.data = [] - subject_class.send(report_method, 30, *args).each do |date, count| + subject_class.send(report_method, *args).each do |date, count| report.data << {x: date, y: count} end end def self.add_counts(report, subject_class) report.total = subject_class.count - report.prev30Days = subject_class.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = subject_class.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_users_by_trust_level(report) @@ -82,10 +94,10 @@ class Report end def self.report_starred(report) - basic_report_about report, Topic, :starred_counts_per_day + basic_report_about report, Topic, :starred_counts_per_day, default_days query = TopicUser.where(starred: true) report.total = query.count - report.prev30Days = query.where('starred_at > ? and starred_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = query.where('starred_at > ? and starred_at < ?', report.start_date - 30.days, report.start_date).count end # Post action counts: @@ -102,7 +114,7 @@ class Report end flagsQuery = PostAction.where(post_action_type_id: PostActionType.flag_types.values) report.total = flagsQuery.count - report.prev30Days = flagsQuery.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = flagsQuery.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_likes(report) @@ -120,15 +132,15 @@ class Report end query = PostAction.unscoped.where(post_action_type_id: post_action_type) report.total = query.count - report.prev30Days = query.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = query.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end # Private messages counts: def self.private_messages_report(report, topic_subtype) - basic_report_about report, Post, :private_messages_count_per_day, topic_subtype + basic_report_about report, Post, :private_messages_count_per_day, default_days, topic_subtype report.total = Post.private_posts.with_topic_subtype(topic_subtype).count - report.prev30Days = Post.private_posts.with_topic_subtype(topic_subtype).where('posts.created_at > ? and posts.created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = Post.private_posts.with_topic_subtype(topic_subtype).where('posts.created_at > ? and posts.created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_user_to_user_private_messages(report) diff --git a/app/models/topic.rb b/app/models/topic.rb index b80519aa00..a2a35d229d 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -345,8 +345,8 @@ class Topic < ActiveRecord::Base custom_fields[key.to_s] end - def self.listable_count_per_day(sinceDaysAgo=30) - listable_topics.where('created_at > ?', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count + def self.listable_count_per_day(start_date, end_date) + listable_topics.where('created_at >= ? and created_at < ?', start_date, end_date).group('date(created_at)').order('date(created_at)').count end def private_message? @@ -421,9 +421,9 @@ class Topic < ActiveRecord::Base WHEN last_read_post_number > :highest THEN :highest ELSE last_read_post_number END, - seen_post_count = CASE - WHEN seen_post_count > :highest THEN :highest - ELSE seen_post_count + highest_seen_post_number = CASE + WHEN highest_seen_post_number > :highest THEN :highest + ELSE highest_seen_post_number END WHERE topic_id = :topic_id", highest: highest_post_number, diff --git a/app/models/topic_user.rb b/app/models/topic_user.rb index cc4497b2fa..b252eca730 100644 --- a/app/models/topic_user.rb +++ b/app/models/topic_user.rb @@ -152,8 +152,8 @@ class TopicUser < ActiveRecord::Base threshold: SiteSetting.auto_track_topics_after } - # In case anyone seens "seen_post_count" and gets confused, like I do. - # seen_post_count represents the highest_post_number of the topic when + # In case anyone seens "highest_seen_post_number" and gets confused, like I do. + # highest_seen_post_number represents the highest_post_number of the topic when # the user visited it. It may be out of alignment with last_read, meaning # ... user visited the topic but did not read the posts # @@ -161,7 +161,7 @@ class TopicUser < ActiveRecord::Base rows = exec_sql("UPDATE topic_users SET last_read_post_number = GREATEST(:post_number, tu.last_read_post_number), - seen_post_count = t.highest_post_number, + highest_seen_post_number = t.highest_post_number, total_msecs_viewed = LEAST(tu.total_msecs_viewed + :msecs,86400000), notification_level = case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) > @@ -210,7 +210,7 @@ class TopicUser < ActiveRecord::Base TopicTrackingState.publish_read(topic_id, post_number, user.id, args[:new_status]) user.update_posts_read!(post_number) - exec_sql("INSERT INTO topic_users (user_id, topic_id, last_read_post_number, seen_post_count, last_visited_at, first_visited_at, notification_level) + exec_sql("INSERT INTO topic_users (user_id, topic_id, last_read_post_number, highest_seen_post_number, last_visited_at, first_visited_at, notification_level) SELECT :user_id, :topic_id, :post_number, ft.highest_post_number, :now, :now, :new_status FROM topics AS ft JOIN users u on u.id = :user_id @@ -241,7 +241,7 @@ class TopicUser < ActiveRecord::Base UPDATE topic_users t SET last_read_post_number = LEAST(GREATEST(last_read, last_read_post_number), max_post_number), - seen_post_count = LEAST(max_post_number,GREATEST(t.seen_post_count, last_read)) + highest_seen_post_number = LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read)) FROM ( SELECT topic_id, user_id, MAX(post_number) last_read FROM post_timings @@ -259,7 +259,7 @@ X.topic_id = t.topic_id AND X.user_id = t.user_id AND ( last_read_post_number <> LEAST(GREATEST(last_read, last_read_post_number), max_post_number) OR - seen_post_count <> LEAST(max_post_number,GREATEST(t.seen_post_count, last_read)) + highest_seen_post_number <> LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read)) ) SQL @@ -281,7 +281,7 @@ end # starred :boolean default(FALSE), not null # posted :boolean default(FALSE), not null # last_read_post_number :integer -# seen_post_count :integer +# highest_seen_post_number :integer # starred_at :datetime # last_visited_at :datetime # first_visited_at :datetime diff --git a/app/models/upload.rb b/app/models/upload.rb index 8b6455769a..d9a5437df4 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -75,13 +75,24 @@ class Upload < ActiveRecord::Base # deal with width & height for images if FileHelper.is_image?(filename) begin - # fix orientation first - Upload.fix_image_orientation(file.path) - # retrieve image info - image_info = FastImage.new(file, raise_on_failure: true) - # compute image aspect ratio - upload.width, upload.height = ImageSizer.resize(*image_info.size) - # make sure we're at the beginning of the file (FastImage moves the pointer) + if filename =~ /\.svg$/i + svg = Nokogiri::XML(file).at_css("svg") + width, height = svg["width"].to_i, svg["height"].to_i + if width == 0 || height == 0 + upload.errors.add(:base, I18n.t("upload.images.size_not_found")) + else + upload.width, upload.height = ImageSizer.resize(width, height) + end + else + # fix orientation first + Upload.fix_image_orientation(file.path) + # retrieve image info + image_info = FastImage.new(file, raise_on_failure: true) + # compute image aspect ratio + upload.width, upload.height = ImageSizer.resize(*image_info.size) + end + # make sure we're at the beginning of the file + # (FastImage and Nokogiri move the pointer) file.rewind rescue FastImage::ImageFetchFailure upload.errors.add(:base, I18n.t("upload.images.fetch_failure")) diff --git a/app/models/user.rb b/app/models/user.rb index 5c5de05877..cafd7ddc3e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -531,8 +531,8 @@ class User < ActiveRecord::Base .limit(3) end - def self.count_by_signup_date(sinceDaysAgo=30) - where('created_at > ?', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count + def self.count_by_signup_date(start_date, end_date) + where('created_at >= ? and created_at < ?', start_date, end_date).group('date(created_at)').order('date(created_at)').count end diff --git a/app/models/user_history.rb b/app/models/user_history.rb index a5f8a61165..e10cdd9760 100644 --- a/app/models/user_history.rb +++ b/app/models/user_history.rb @@ -32,7 +32,8 @@ class UserHistory < ActiveRecord::Base :auto_trust_level_change, :check_email, :delete_post, - :delete_topic) + :delete_topic, + :impersonate) end # Staff actions is a subset of all actions, used to audit actions taken by staff users. @@ -48,7 +49,8 @@ class UserHistory < ActiveRecord::Base :revoke_badge, :check_email, :delete_post, - :delete_topic] + :delete_topic, + :impersonate] end def self.staff_action_ids diff --git a/app/models/user_visit.rb b/app/models/user_visit.rb index 6af6bf8efc..669a4d1843 100644 --- a/app/models/user_visit.rb +++ b/app/models/user_visit.rb @@ -1,8 +1,8 @@ class UserVisit < ActiveRecord::Base # A count of visits in the last month by day - def self.by_day(sinceDaysAgo=30) - where("visited_at >= ?", sinceDaysAgo.days.ago).group(:visited_at).order(:visited_at).count + def self.by_day(start_date, end_date) + where("visited_at >= ? and visited_at < ?", start_date, end_date).group(:visited_at).order(:visited_at).count end def self.ensure_consistency! diff --git a/app/serializers/admin_user_serializer.rb b/app/serializers/admin_user_serializer.rb index 461bf67d20..4c9f309ce9 100644 --- a/app/serializers/admin_user_serializer.rb +++ b/app/serializers/admin_user_serializer.rb @@ -39,7 +39,7 @@ class AdminUserSerializer < BasicUserSerializer def include_email? # staff members can always see their email - scope.is_staff? && object.id == scope.user.id + (scope.is_staff? && object.id == scope.user.id) || scope.can_see_emails? end alias_method :include_associated_accounts?, :include_email? diff --git a/app/serializers/post_stream_serializer_mixin.rb b/app/serializers/post_stream_serializer_mixin.rb index f87edaa284..e5edb78978 100644 --- a/app/serializers/post_stream_serializer_mixin.rb +++ b/app/serializers/post_stream_serializer_mixin.rb @@ -21,6 +21,7 @@ module PostStreamSerializerMixin object.posts.each_with_index do |p, idx| highest_number_in_posts = p.post_number if p.post_number > highest_number_in_posts ps = PostSerializer.new(p, scope: scope, root: false) + ps.add_raw = true if @options[:include_raw] ps.topic_view = object p.topic = object.topic diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb index bc99302b9b..dfd6e281c8 100644 --- a/app/services/staff_action_logger.rb +++ b/app/services/staff_action_logger.rb @@ -142,10 +142,32 @@ class StaffActionLogger })) end + def log_show_emails(users) + values = [] + + users.each do |user| + values << "(#{@admin.id}, #{UserHistory.actions[:check_email]}, #{user.id}, current_timestamp, current_timestamp)" + end + + # bulk insert + UserHistory.exec_sql <<-SQL + INSERT INTO user_histories (acting_user_id, action, target_user_id, created_at, updated_at) + VALUES #{values.join(",")} + SQL + end + + def log_impersonate(user, opts={}) + raise Discourse::InvalidParameters.new("user is nil") unless user + UserHistory.create(params(opts).merge({ + action: UserHistory.actions[:impersonate], + target_user_id: user.id + })) + end + private def params(opts) - {acting_user_id: @admin.id, context: opts[:context]} + { acting_user_id: @admin.id, context: opts[:context] } end end diff --git a/app/views/email/_post.html.erb b/app/views/email/_post.html.erb index 1a10b57f77..8cacf66f99 100644 --- a/app/views/email/_post.html.erb +++ b/app/views/email/_post.html.erb @@ -10,7 +10,7 @@ - <%= cooked_post_for_email(post) %> + <%= format_for_email(post.cooked) %> diff --git a/app/views/user_notifications/digest.text.erb b/app/views/user_notifications/digest.text.erb index f05ed22ff4..b0735a373a 100644 --- a/app/views/user_notifications/digest.text.erb +++ b/app/views/user_notifications/digest.text.erb @@ -10,10 +10,7 @@ <%= raw(@markdown_linker.create(t.title, t.relative_url)) %> <%- if t.best_post.present? %> - <%= raw(t.best_post.excerpt(1000, - strip_links: true, - text_entities: true, - markdown_images: true)) %> + <%= raw(t.best_post.excerpt(1000, strip_links: true, text_entities: true, markdown_images: true)) %> -------------------------------------------------------------------------------- <%- end %> diff --git a/app/views/users/_auto_redirect_home.html.erb b/app/views/users/_auto_redirect_home.html.erb index 5b91befd43..b5c9a057e9 100644 --- a/app/views/users/_auto_redirect_home.html.erb +++ b/app/views/users/_auto_redirect_home.html.erb @@ -2,6 +2,6 @@ (function() { setTimeout(function() { window.location.href = '/'; - }, 5000); + }, 2000); })(); \ No newline at end of file diff --git a/app/views/users/account_created.html.erb b/app/views/users/account_created.html.erb index ea8cf7d9ed..0bc9c8ccdb 100644 --- a/app/views/users/account_created.html.erb +++ b/app/views/users/account_created.html.erb @@ -1,5 +1,3 @@
- <% if session["user_created_email"] %> - <%= t('login.activate_email', email: session["user_created_email"]).html_safe %> - <% end %> + <%= @message.html_safe %>
diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index 4ca15a1bb3..b763b40c2f 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -359,7 +359,6 @@ cs: frequency: "Budeme vás informovat emailem pouze pokud jste se na našem webu dlouho neukázali a pokud jste obsah, o kterém vás chceme informovat, doposud neviděli." name: title: "Jméno" - instructions: "Vaše celé jméno." too_short: "Vaše jméno je příliš krátké." ok: "Vaše jméno vypadá dobře" username: @@ -387,6 +386,8 @@ cs: created: "Účet vytvořen" log_out: "Odhlásit se" location: "Lokace" + card_badge: + title: "User Card Badge" website: "Webová stránka" email_settings: "Emailová upozornění" email_digests: @@ -589,6 +590,7 @@ cs: drafts_offline: "koncepty offline" min_length: need_more_for_title: "ještě {{n}} znaků nadpisu tématu" + need_more_for_reply: "ještě {{n}} znaků" error: title_missing: "Název musí být vyplněn" title_too_short: "Název musí být dlouhý alespoň {{min}} znaků" @@ -612,6 +614,7 @@ cs: view_new_post: "Zobrazit váš nový příspěvek." saving: "Ukládám..." saved: "Uloženo!" + saved_draft: "Máte rozepsaný příspěvek. Klikněte pro obnovení." uploading: "Nahrávám..." show_preview: 'zobrazit náhled »' hide_preview: '« skrýt náhled' @@ -906,7 +909,6 @@ cs: one: "Je zobrazen pouze 1 příspěvek" few: "Jsou zobrazeny pouze {{count}} příspěvky" other: "Je zobrazeno pouze {{count}} příspěvků" - cancel: "Zobrazí znovu všechny příspěvky v tomto tématu." split_topic: title: "Rozdělit téma" action: "do nového téma" @@ -1375,8 +1377,6 @@ cs: 7_days_ago: "Týden" 30_days_ago: "Měsíc" all: "Celkem" - view_table: "Zobrazit jako tabulku" - view_chart: "Zobrazit jako sloupcový graf" commits: latest_changes: "Poslední změny:" by: "od" @@ -1883,6 +1883,7 @@ cs: star: 'f Star topic' share_topic: 'shift s Sdílet téma' share_post: 's Share post' + reply_as_new_topic: 't Odpovědět v propojeném tématu' reply_topic: 'shift r Odpovědět na téma' reply_post: 'r Odpovědět na příspěvek' quote_post: 'q Citovat příspěvek' @@ -1910,6 +1911,7 @@ cs: few: "1 udělen" other: "%{count} uděleno" select_badge_for_title: Vyberte odznak, který chcete použít jako svůj titul + none: "" badge_grouping: other: name: Ostatní diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index 3ba27b66bc..b1dddf605f 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -824,7 +824,6 @@ da: n_posts: one: "1 indlæg" other: "{{count}} indlæg" - cancel: "Se alle indlæg i emnet." split_topic: title: "Flyt til nyt emne" action: "flyt til nyt emne" @@ -1263,8 +1262,6 @@ da: 7_days_ago: "7 dage siden" 30_days_ago: "30 dage siden" all: "Alle" - view_table: "Vis som tabel" - view_chart: "Vis som søjlediagram" commits: latest_changes: "Seneste ændringer: opdatér ofte!" by: "af" diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index a873b92795..794e507e9c 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -277,6 +277,7 @@ de: admin_tooltip: "Dieser Benutzer ist ein Administrator" suspended_notice: "Dieser Benutzer ist bis zum {{date}} gesperrt." suspended_reason: "Grund: " + github_profile: "Github" mailing_list_mode: "Erhalte bei jedem neuen Beitrag eine E-Mail (sofern du das Thema oder die Kategorie nicht stummgeschaltet hast)" watched_categories: "Beobachtet" watched_categories_instructions: "Du wirst in diesen Kategorien automatisch alle neuen Themen beobachten. Du wirst über alle neuen Beiträge und Themen benachrichtigt werden. Außerdem wird die Anzahl der ungelesenen und neuen Beiträge neben dem Eintrag in der Themenliste angezeigt." @@ -343,7 +344,7 @@ de: frequency: "Wir werden dir nur E-Mails senden, wenn wir dich kürzlich nicht gesehen haben und wenn du die betroffenen Inhalte noch nicht gesehen hast." name: title: "Name" - instructions: "Dein vollständiger Name." + instructions: "Ihr vollständiger Name (optional)." too_short: "Dein Name ist zu kurz." ok: "Dein Name schaut gut aus." username: @@ -511,8 +512,7 @@ de: created: 'Erstellt' created_lowercase: 'erstellt' trust_level: 'Vertrauensstufe' - search_hint: 'Benutzername oder IP-Adresse' - search_hint_admin: 'Benutzername, E-Mail- oder IP-Adresse' + search_hint: 'Benutzername, E-Mail- oder IP-Adresse' create_account: title: "Neues Benutzerkonto erstellen" failed: "Etwas ist fehlgeschlagen. Vielleicht ist diese E-Mail-Adresse bereits registriert. Versuche den 'Passwort vergessen'-Link." @@ -712,6 +712,8 @@ de: hot: "Es gibt keine beliebten Themen." category: "Es gibt keine Themen in {{category}}." top: "Es gibt keine Top-Themen." + educate: + new: '

Hier werden neue Themen angezeigt.

Standardmäßig werden jene Themen als neu angesehen und mit dem neu Indikator versehen, die in den letzten 2 Tagen erstellt wurden.

Du kannst das in deinen Einstellungen ändern.

' bottom: latest: "Das waren die aktuellen Themen." hot: "Das waren alle beliebten Themen." @@ -881,7 +883,7 @@ de: n_posts: one: "1 Beitrag" other: "{{count}} Beiträge" - cancel: "Zeige in diesem Thema wieder alle Beiträge an." + cancel: "Filter entfernen" split_topic: title: "In neues Thema verschieben" action: "in ein neues Thema verschieben" @@ -1181,6 +1183,9 @@ de: submit_tooltip: "Private Meldung abschicken" take_action_tooltip: "Den Meldungsschwellenwert sofort erreichen, anstatt auf weitere Meldungen aus der Community zu warten." cant: "Entschuldige, Du kannst diesen Beitrag augenblicklich nicht melden." + formatted_name: + inappropriate: "Es ist unangemessen" + spam: "Es ist Spam" custom_placeholder_notify_user: "Weshalb erfordert der Beitrag, dass du den Benutzer direkt und privat kontaktieren möchtest? Sei spezifisch, konstruktiv und immer freundlich." custom_placeholder_notify_moderators: "Warum soll ein Moderator sich diesen Beitrag ansehen? Bitte lass uns wissen, was genau Dich beunruhigt, und wenn möglich dafür relevante Links." custom_message: @@ -1239,6 +1244,7 @@ de: history: "Verlauf" changed_by: "von {{author}}" raw_email: + title: "Unverarbeitete E-Mail" not_available: "Nicht verfügbar!" categories_list: "Liste der Kategorien" filters: @@ -1346,8 +1352,11 @@ de: 7_days_ago: "vor 7 Tagen" 30_days_ago: "vor 30 Tagen" all: "alle" - view_table: "Als Tabelle anzeigen" - view_chart: "Als Balkendiagramm anzeigen" + view_table: "Tabelle" + view_chart: "Säulendiagramm" + refresh_report: "Bericht aktualisieren" + start_date: "Startdatum" + end_date: "Enddatum" commits: latest_changes: "Letzte Änderungen: bitte häufig updaten!" by: "von" @@ -1684,6 +1693,7 @@ de: last_emailed: "Letzte E-Mail" not_found: "Entschuldige, dieser Benutzername existiert im System nicht." active: "Aktiv" + show_emails: "E-Mails anzeigen" nav: new: "Neu" active: "Aktiv" diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 2a74b7e102..5a7474378a 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -310,6 +310,7 @@ en: admin_tooltip: "This user is an admin" suspended_notice: "This user is suspended until {{date}}." suspended_reason: "Reason: " + github_profile: "Github" mailing_list_mode: "Receive an email for every new post (unless you mute the topic or category)" watched_categories: "Watched" watched_categories_instructions: "You will automatically watch all new topics in these categories. You will be notified of all new posts and topics, plus the count of unread and new posts will also appear next to the topic's listing." @@ -380,7 +381,7 @@ en: email: title: "Email" instructions: "Never shown to the public." - ok: "Looks good. We will email you to confirm." + ok: "We will email you to confirm." invalid: "Please enter a valid email address." authenticated: "Your email has been authenticated by {{provider}}." frequency: "We'll only email you if we haven't seen you recently and you haven't already seen the thing we're emailing you about." @@ -1003,7 +1004,7 @@ en: n_posts: one: "1 post" other: "{{count}} posts" - cancel: "Show all posts in this topic again." + cancel: "Remove filter" split_topic: title: "Move to New Topic" @@ -1323,6 +1324,10 @@ en: submit_tooltip: "Submit the private flag" take_action_tooltip: "Reach the flag threshold immediately, rather than waiting for more community flags" cant: "Sorry, you can't flag this post at this time." + formatted_name: + off_topic: "It's Off-Topic" + inappropriate: "It's Inappropriate" + spam: "It's Spam" custom_placeholder_notify_user: "Why does this post require you to speak to this user directly and privately? Be specific, be constructive, and always be kind." custom_placeholder_notify_moderators: "Why does this post require moderator attention? Let us know specifically what you are concerned about, and provide relevant links where possible." custom_message: @@ -1507,8 +1512,11 @@ en: 7_days_ago: "7 Days Ago" 30_days_ago: "30 Days Ago" all: "All" - view_table: "View as Table" - view_chart: "View as Bar Chart" + view_table: "table" + view_chart: "bar chart" + refresh_report: "Refresh Report" + start_date: "Start Date" + end_date: "End Date" commits: latest_changes: "Latest changes: please update often!" @@ -1825,6 +1833,7 @@ en: check_email: "check email" delete_topic: "delete topic" delete_post: "delete post" + impersonate: "impersonate" 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." @@ -1861,6 +1870,7 @@ en: last_emailed: "Last Emailed" not_found: "Sorry, that username doesn't exist in our system." active: "Active" + show_emails: "Show Emails" nav: new: "New" active: "Active" diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index e321eda212..65ce115f4e 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -343,7 +343,6 @@ es: frequency: "Sólo te enviaremos e-mails si no te hemos visto recientemente y todavía no has visto lo que te estamos enviando." name: title: "Nombre" - instructions: "Tu nombre completo." too_short: "Tu nombre es demasiado corto." ok: "Tu nombre es válido." username: @@ -511,8 +510,6 @@ es: created: 'Creado' created_lowercase: 'creado' trust_level: 'Nivel de Confianza' - search_hint: 'nombre de usuario o dirección IP' - search_hint_admin: 'nombre de usuario, e-mail o dirección IP' create_account: title: "Crear nueva cuenta" failed: "Algo ha salido mal, tal vez este e-mail ya fue registrado, intenta con el enlace 'olvidé la contraseña'" @@ -887,7 +884,6 @@ es: n_posts: one: "1 post" other: "{{count}} posts" - cancel: "Mostrar de nuevo todos los posts de este tema." split_topic: title: "Mover a un tema nuevo" action: "mover a un tema nuevo" @@ -1353,8 +1349,6 @@ es: 7_days_ago: "Hace 7 días" 30_days_ago: "Hace 30 días" all: "Todo" - view_table: "Ver como tabla" - view_chart: "Ver como gráfico de tablas" commits: latest_changes: "Cambios recientes: ¡actualiza a menudo!" by: "por" diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index d37afd7780..019e617aa3 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -104,6 +104,7 @@ fi: admin_title: "Ylläpito" flags_title: "Liput" show_more: "näytä lisää" + show_help: "ohje" links: "Linkit" links_lowercase: "linkit" faq: "UKK" @@ -138,6 +139,10 @@ fi: stat: all_time: "Kaikki" last_7_days: "Viimeiset 7 päivää" + like_count: "Tykkäyksiä" + topic_count: "Ketjuja" + post_count: "Viestejä" + user_count: "Käyttäjiä" bookmarks: not_logged_in: "pahoittelut, sinun täytyy kirjautua sisään voidaksesi lisätä viestin kirjanmerkin" created: "olet lisännyt tämän viestin kirjainmerkkeihisi" @@ -325,6 +330,10 @@ fi: image_is_not_a_square: "Varoitus: olemme rajanneet kuvaasti; se ei ole neliön muotoinen." change_profile_background: title: "Profiilin taustakuva" + instructions: "Profiilin taustakuvan leveys on 850 pikseliä." + change_card_background: + title: "Käyttäjäkortin taustakuva" + instructions: "Taustakuvan leveys on 590 pikseliä." email: title: "Sähköposti" instructions: "Ei tule julkiseksi." @@ -334,7 +343,7 @@ fi: frequency: "Lähetämme sähköpostia vain aiheista, joita et ole nähnyt, ja kun emme ole nähneet sinua viime aikoina." name: title: "Nimi" - instructions: "Koko nimesi." + instructions: "Koko nimesi (valinnainen)" too_short: "Nimesi on liian lyhyt." ok: "Nimesi vaikuttaa hyvältä." username: @@ -362,6 +371,8 @@ fi: created: "Liittynyt" log_out: "Kirjaudu ulos" location: "Sijainti" + card_badge: + title: "Käyttäjäkortin tunnus" website: "Nettisivu" email_settings: "Sähköposti" email_digests: @@ -500,6 +511,7 @@ fi: created: 'Luotu' created_lowercase: 'luotu' trust_level: 'Luottamustaso' + search_hint: 'käyttäjätunnus, sähköposti tai IP-osoite' create_account: title: "Luo uusi tunnus" failed: "Jotain meni pieleen. Ehkäpä tämä sähköpostiosoite on jo rekisteröity, kokeile salasana unohtui -linkkiä." @@ -585,6 +597,7 @@ fi: view_new_post: "Katsele uutta viestiäsi." saving: "Tallennetaan..." saved: "Tallennettu!" + saved_draft: "Viestiluonnos kesken. Klikkaa tähän jatkaaksesi." uploading: "Lähettää..." show_preview: 'näytä esikatselu »' hide_preview: '« piilota esikatselu' @@ -873,7 +886,6 @@ fi: n_posts: one: "1 viesti" other: "{{count}} viestiä" - cancel: "Näytä tämän ketjun kaikki viestit." split_topic: title: "Siirrä uuteen ketjuun" action: "siirrä uuteen ketjuun" @@ -1231,6 +1243,7 @@ fi: history: "Historia" changed_by: "käyttäjältä {{author}}" raw_email: + title: "Alkuperäinen sähköposti" not_available: "Ei käytettävissä!" categories_list: "Lista alueista" filters: @@ -1338,8 +1351,6 @@ fi: 7_days_ago: "7 päivää sitten" 30_days_ago: "30 päivää sitten" all: "Kaikki" - view_table: "Näytä taulukkona" - view_chart: "Katsele pylväsdiagrammina" commits: latest_changes: "Viimeisimmät muutokset: päivitä usein!" by: "käyttäjältä" @@ -1858,7 +1869,7 @@ fi: confirm: 'Vahvistus' site_text: none: "Valitse sisällön tyyppi aloittaaksesi muokkaamisen." - title: 'Tekstin sisältö' + title: 'Tekstit' site_settings: show_overriden: 'Näytä vain muokatut' title: 'Asetukset' @@ -1911,11 +1922,14 @@ fi: grant: Myönnä no_user_badges: "%{name} ei ole saanut yhtään arvomerkkiä." no_badges: Myönnettäviä arvomerkkejä ei ole. + none_selected: "Valitse arvomerkki aloittaaksesi" allow_title: Salli arvomerkin käyttäminen tittelinä multiple_grant: Voidaan myöntää useita kertoja listable: Näytä arvomerkki julkisella arvomerkkisivulla enabled: Ota arvomerkki käyttöön icon: Ikoni + image: Kuva + icon_help: "Käytä joko Font Awesome -luokkaa tai kuvan URL-osoitetta" query: Arvomerkkien haku tietokannasta (SQL) target_posts: Tietokantakyselyn kohdeviestit auto_revoke: Aja kumoamis-ajo päivittäin @@ -1948,6 +1962,8 @@ fi: with_time: %{username} %{time} lightbox: download: "lataa" + search_help: + title: 'Etsi ohjetta' keyboard_shortcuts_help: title: 'Näppäinoikotiet' jump_to: @@ -2009,6 +2025,7 @@ fi: one: "1 myönnetty" other: "%{count} myönnettyä" select_badge_for_title: Valitse tittelisi arvomerkeistä + none: "" badge_grouping: getting_started: name: Ensiaskeleet @@ -2090,3 +2107,12 @@ fi: reader: name: Lukija description: Luki kaikki viestit ketjusta, jossa on yli 100 viestiä + google_search: | +

Etsi Googlella

+

+

+

diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index a6e262eb6c..8f2249e6f1 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -277,6 +277,7 @@ fr: admin_tooltip: "Cet utilisateur est un admin" suspended_notice: "L'utilisateur est suspendu jusqu'au {{date}}." suspended_reason: "Raison :" + github_profile: "Github" mailing_list_mode: "Recevoir un e-mail pour tous les nouveaux messages (sauf si vous avez rendu silencieux le sujet ou la catégorie)" watched_categories: "Surveillés" watched_categories_instructions: "Vous pourrez suivre attentivement automatiquement toutes les nouveaux sujets dans ces catégories. Vous serez notifié de tous les nouveaux messages et sujets, de plus les nombres de messages non lus et de nouveaux messages apparaîtront également à côté de la liste des sujets." @@ -343,7 +344,7 @@ fr: frequency: "Nous vous envoyons des courriels contenant uniquement des informations que vous n'avez pas déjà vues lors d'une précédente connexion." name: title: "Nom" - instructions: "Votre nom complet." + instructions: "Votre nom complet (facultatif)." too_short: "Votre nom est trop court." ok: "Votre nom semble correct." username: @@ -511,8 +512,7 @@ fr: created: 'Créé' created_lowercase: 'créé' trust_level: 'Niveau de confiance' - search_hint: 'pseudo ou adresse IP' - search_hint_admin: 'pseudo, courriel ou adresse IP' + search_hint: 'pseudo, courriel ou adresse IP' create_account: title: "Créer un nouveau compte" failed: "Quelque chose s'est mal passé, peut-être que cette adresse de courriel est déjà enregistrée, essayez le lien Mot de passe oublié." @@ -891,7 +891,7 @@ fr: n_posts: one: "1 message" other: "{{count}} messages" - cancel: "Réafficher tous les messages de ce sujet." + cancel: "Supprimer le filtre" split_topic: title: "Déplacer vers Nouveau Sujet" action: "déplacer vers un nouveau sujet" @@ -1191,6 +1191,10 @@ fr: submit_tooltip: "Soumettre le signalement privé" take_action_tooltip: "Atteindre le seuil de signalement immédiatement, plutôt que d'attendre plus de signalement de la communauté." cant: "Désolé, vous ne pouvez pas signaler ce message pour le moment" + formatted_name: + off_topic: "C'est hors-sujet" + inappropriate: "C'est inapproprié" + spam: "C'est un pourriel" custom_placeholder_notify_user: "Pour quelles raisons ce message nécessite que vous contactiez cet utilisateur par messagerie privée ? Soyez précis, constructif et toujours aimable." custom_placeholder_notify_moderators: "Pourquoi ce message requière-t-il l'attention des modérateurs ? Dites-nous ce qui vous dérange spécifiquement, et fournissez des liens pertinents si possible." custom_message: @@ -1357,8 +1361,11 @@ fr: 7_days_ago: "il y a 7 jours" 30_days_ago: "il y a 30 jours" all: "Tous" - view_table: "Tableau" - view_chart: "Graphique à barre" + view_table: "tableau" + view_chart: "histogramme" + refresh_report: "Actualiser le rapport" + start_date: "Date de début" + end_date: "Date de fin" commits: latest_changes: "Derniers changements..." by: "par" @@ -1695,6 +1702,7 @@ fr: last_emailed: "Derniers contacts" not_found: "Désolé ce pseudo n'existe pas dans notre système." active: "Actifs" + show_emails: "Afficher les adresses de courriels" nav: new: "Nouveaux" active: "Actifs" diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml index 40e1c683f2..fd8c7f1dea 100644 --- a/config/locales/client.he.yml +++ b/config/locales/client.he.yml @@ -104,6 +104,7 @@ he: admin_title: "ניהול" flags_title: "סימוני הודעה" show_more: "הראה עוד" + show_help: "עזרה" links: "קישורים" links_lowercase: "קישורים" faq: "שאלות נפוצות" @@ -138,6 +139,10 @@ he: stat: all_time: "כל הזמנים" last_7_days: "7 הימים האחרונים" + like_count: "לייקים" + topic_count: "נושאים" + post_count: "פרסומים" + user_count: "משתמשים" bookmarks: not_logged_in: "סליחה, עליך להיות מחובר כדי להוסיף פוסט למועדפים" created: "סימנת הודעה זו כמועדפת" @@ -258,6 +263,8 @@ he: invited_by: "הוזמן/הוזמנה על ידי" trust_level: "רמת אמון" notifications: "התראות" + dismiss_notifications: "סימון הכל כנקרא" + dismiss_notifications_tooltip: "סימון כל התראות ה\"לא נקרא\" ככאלה שנקראו" disable_jump_reply: "לא לקפוץ לפוסט החדש שלך לאחר משלוח התשובה" dynamic_favicon: "הראה התראה על הודעות חדשות באייקון העמוד (נסיוני)" edit_history_public: "אפשרו למשתמשים אחרים לראות את תיקוני הפרסומים שלי" @@ -323,6 +330,10 @@ he: image_is_not_a_square: "אזהרה: חתכנו את התמונה שלך, היא לא ריבועית" change_profile_background: title: "שינוי רקע פרופיל" + instructions: "רקעי הפרופיל ימורכזו ויוצגו עם ברירת מחדל של רוחב של 850px." + change_card_background: + title: "כרטיס הרקע של המשתמש/ת" + instructions: "תמונות רקע ימורכזו ויוצגו עם ברירת מחדל של רוחב 590px." email: title: "דואר אלקטרוני" instructions: "לעולם לא מוצג לציבור." @@ -332,7 +343,7 @@ he: frequency: "נשלח לך דואר אלקטרוני רק אם לא ראינו אותך לאחרונה ועדיין לא ראית את מה שאנחנו רוצים לשלוח" name: title: "שם" - instructions: "השם המלא שלכם." + instructions: "שמך המלא (רשות)." too_short: "השם שלך קצר מידי." ok: "השם נראה טוב." username: @@ -360,6 +371,8 @@ he: created: "הצטרף" log_out: "התנתקות" location: "מיקום" + card_badge: + title: "תג כרטיס משתמש/ת" website: "אתר" email_settings: "דואר אלקטרוני" email_digests: @@ -498,6 +511,7 @@ he: created: 'נוצר' created_lowercase: 'נוצר/ו' trust_level: 'רמת אמון' + search_hint: 'שם משתמש/ת, דוא"ל או כתובת IP' create_account: title: "יצירת חשבון חדש" failed: "משהו לא בסדר, אולי כבר קיימת כתובת דואר אלקטרוני כזו. נסה את קישור שכחתי סיסמה." @@ -583,6 +597,7 @@ he: view_new_post: "הצגת את ההודעה החדשה שלך." saving: "שומר..." saved: "נשמר!" + saved_draft: "טיוטאת פרסום בתהליך, לחצו כדי להמשיך." uploading: "מעלה..." show_preview: 'הראה תצוגה מקדימה »' hide_preview: '« הסתר תצוגה מקדימה' @@ -871,7 +886,6 @@ he: n_posts: one: "הודעה אחת" other: "{{count}} הודעות" - cancel: "הצג שוב את כל ההודעות בנושא זה." split_topic: title: "העבר לנושא חדש" action: "העבר לנושא חדש" @@ -1208,6 +1222,12 @@ he: posts: "הודעות" posts_lowercase: "פרסומים" posts_long: "יש {{number}} הודעות בנושא הזה" + posts_likes_MF: | + לנושא זה יש {count, plural, one {פרסום אחד} other {# פרסומים}} + נמוך {עם יחס גבוה של לייקים לפרסום} + בינוני {עם יחס גבוה מאוד של לייקים לפרסום} + גבוה {עם יחס גבוה בצורה יוצאת דופן של לייקים לפרסום} + אחר {}} original_post: "הודעה מקורית" views: "צפיות" views_lowercase: "תצוגות" @@ -1222,6 +1242,9 @@ he: category_title: "קטגוריה" history: "היסטוריה" changed_by: "מאת {{author}}" + raw_email: + title: "גלם הדוא\"ל" + not_available: "לא זמין!" categories_list: "רשימת קטגוריות" filters: with_topics: "%{filter} נושאים" @@ -1328,8 +1351,6 @@ he: 7_days_ago: "לפני שבעה ימים" 30_days_ago: "לפני 30 ימים" all: "הכל" - view_table: "הצג כטבלה" - view_chart: "הצג כתרשים עמודות" commits: latest_changes: "שינויים אחרונים: בבקשה עדכן תכופות!" by: "על ידי" @@ -1901,11 +1922,14 @@ he: grant: הענק no_user_badges: "ל%{name} לא הוענקו תגים." no_badges: אין תגים שניתן להעניק. + none_selected: "בחרו תג כדי להתחיל" allow_title: אפשר לתג להיות בשימוש ככותרת. multiple_grant: יכול/ה להינתן מספר פעמים listable: הצגת תגים בעמוד התגים הפומבי enabled: אפשר תג icon: סמליל + image: תמונה + icon_help: "השתמשו ב-class בשם Font Awesome או ב-URL לתמונה" query: שאילתת תגים (SQL) target_posts: פרסומי מטרות שאילתה auto_revoke: הפעלת שאילתת ביטול יומית @@ -1938,6 +1962,8 @@ he: with_time: %{username} ב %{time} lightbox: download: "הורד" + search_help: + title: 'חיפוש בעזרה' keyboard_shortcuts_help: title: 'קיצורי מקלדת' jump_to: @@ -1999,6 +2025,7 @@ he: one: "1 הוענק" other: "%{count} הוענקו" select_badge_for_title: בחר תג שיופיע בכותרת הפרופיל שלך + none: "" badge_grouping: getting_started: name: מתחילים @@ -2080,3 +2107,12 @@ he: reader: name: מקראה description: קראו כל פרסום בנושא עם יותר מ-100 פרסומים + google_search: | +

חפשו עם גוגל

+

+

+

diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index c08f6a5fe0..42dfe651eb 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -343,7 +343,6 @@ it: frequency: "Ti invieremo mail solo se è un po' che non ti si vede; le email riguarderanno solo le conversazioni che non hai letto." name: title: "Nome" - instructions: "Nome completo." too_short: "Il nome è troppo breve." ok: "Il nome è adeguato." username: @@ -725,7 +724,7 @@ it: category: "Non ci sono altri argomenti nella categoria {{category}}." top: "Non ci sono altri argomenti di punta." topic: - filter_to: "{{post_count}} filtra messaggi in questo argomento" + filter_to: "{{post_count}} suoi messaggi" create: 'Crea Argomento' create_long: 'Crea un nuovo Argomento' private_message: 'Inizia un messaggio privato' @@ -882,7 +881,6 @@ it: n_posts: one: "1 post" other: "{{count}} messaggi" - cancel: "Mostra nuovamente tutti i messaggi di questo argomento." split_topic: title: "Sposta in un Nuovo Argomento" action: "sposta in un nuovo argomento" @@ -1348,8 +1346,6 @@ it: 7_days_ago: "7 Giorni Fa" 30_days_ago: "30 Giorni Fa" all: "Tutti" - view_table: "Visualizza come Tabella" - view_chart: "Visualizza come Grafico a Barre" commits: latest_changes: "Ultime modifiche: per favore aggiorna spesso!" by: "da" diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml index 546cf4ef5d..1f62c4ebfc 100644 --- a/config/locales/client.ja.yml +++ b/config/locales/client.ja.yml @@ -308,7 +308,6 @@ ja: frequency: "メールは、あなたがしばらくの間サイトにログインせず未読トピックが溜まった時のみ送信されます。" name: title: "名前" - instructions: "フルネーム" too_short: "名前が短すぎます。" ok: "名前 OK。" username: @@ -818,7 +817,6 @@ ja: filters: n_posts: other: "{{count}} ポスト" - cancel: "Show all posts in this topic again." split_topic: title: "新規トピックに移動" action: "新規トピックに移動" @@ -1229,8 +1227,6 @@ ja: 7_days_ago: "7日前" 30_days_ago: "30日前" all: "全て" - view_table: "テーブル表示" - view_chart: "棒グラフ表示" commits: latest_changes: "最新の更新内容:" by: "by" diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index 362de5c85a..85e2de94a1 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -320,7 +320,7 @@ ko: frequency: "당신의 활동이 뜸해지거나 저희가 보낸 이메일에 포함된 글들을 읽지 못하셨다면 확인 이메일을 보내드립니다." name: title: "이름" - instructions: "이름을 적어주세요." + instructions: "이름을 적어주세요. (선택사항)" too_short: "이름이 너무 짧습니다." ok: "사용 가능한 이름입니다." username: @@ -484,8 +484,7 @@ ko: created: '생성' created_lowercase: '최초 글' trust_level: '신뢰도' - search_hint: '아이디 혹은 IP 주소' - search_hint_admin: '아이디, 이메일 혹은 IP 주소' + search_hint: '아이디, 이메일 혹은 IP 주소' create_account: title: "회원 가입" failed: "뭔가 잘못되었습니다. 이 메일은 등록이 되어있습니다. 비밀번호를 잊으셨다면 비밀번호 찾기를 눌러주세요." @@ -604,6 +603,8 @@ ko: auto_close: label: "자동-닫기 토픽 시간:" error: "유효한 값은 눌러주세요." + all: + examples: '시간을 숫자(24이하)로 입력하거나 분을 포함한 시간(17:30) 혹은 타임스탬프(2013-11-22 14:00) 형식으로 입력하세요.' limited: units: "(# 시간)" examples: '시간에 해당하는 숫자를 입력하세요. (24)' @@ -742,6 +743,7 @@ ko: jump_reply_down: 이후 답글로 이동 deleted: "토픽이 삭제되었습니다" auto_close_notice: "이 토픽은 곧 자동으로 닫힙니다. %{timeLeft}." + auto_close_notice_based_on_last_post: "이 토픽은 마지막 답글이 달린 %{duration} 후 닫힙니다." auto_close_title: '자동으로 닫기 설정' auto_close_save: "저장" auto_close_remove: "이 토픽을 자동으로 닫지 않기" @@ -765,6 +767,8 @@ ko: '2_4': '이 토픽에 답글을 게시하여서 알림을 받게 됩니다.' '2_2': '이 토픽을 추적하고 있어서 알림을 받게 됩니다.' '2': '이 토픽을 읽어서 알림을 받게 됩니다. (설정)' + '1_2': '누군가 당신의 @아아디 으로 언급했거나 당신의 글에 답글이 달릴 때 알림을 받게 됩니다.' + '1': '누군가 당신의 @아이디 로 언급했거나 당신의 글에 답글이 달릴 때 알림을 받게 됩니다.' '0_7': '당신은 이 토픽에 관한 모든 알림을 무시합니다.' '0_2': '당신은 이 토픽에 관한 모든 알림을 무시합니다.' '0': '당신은 이 토픽에 관한 모든 알림을 무시합니다.' @@ -842,7 +846,6 @@ ko: filters: n_posts: other: "{{count}} 글" - cancel: "다시 이 토픽의 모든 게시물을 표시합니다." split_topic: title: "새로운 토픽으로 이동" action: "새로운 토픽으로 이동" @@ -1170,6 +1173,9 @@ ko: category_title: "카테고리" history: "기록" changed_by: "{{author}}에 의해" + raw_email: + title: "Raw 이메일" + not_available: "Raw 이메일이 가능하지 않습니다." categories_list: "카테고리 목록" filters: with_topics: "%{filter} 토픽" @@ -1276,8 +1282,6 @@ ko: 7_days_ago: "7일" 30_days_ago: "30일" all: "전체" - view_table: "테이블로 보기" - view_chart: "차트로 보기" commits: latest_changes: "최근 변경 사항: 자주 업데이트하십시오!" by: "에 의해" @@ -1492,6 +1496,9 @@ ko: love: name: '사랑' description: "좋아요 버튼 색" + wiki: + name: '위키' + description: "위키 게시글의 배경색으로 사용될 기본 색상" email: title: "이메일" settings: "설정" @@ -1739,6 +1746,7 @@ ko: flagged_by_users: "신고한 사용자들" likes_given: "선사한 '좋아요'" likes_received: "받은 '좋아요'" + likes_received_days: "받은 '좋아요' : 특정일" qualifies: "신뢰 등급 3의 조건에 부합합니다." does_not_qualify: "신뢰 등급 3의 조건에 부합하지 않습니다." will_be_promoted: "곧 승급 됩니다." diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml index cad337c0ed..e8de9be672 100644 --- a/config/locales/client.nb_NO.yml +++ b/config/locales/client.nb_NO.yml @@ -339,7 +339,6 @@ nb_NO: frequency: "Vi sender deg bare en e-post om vi ikke har sett deg nylig og du ikke allerede har sett det vi varsler deg om." name: title: "Navn" - instructions: "Ditt fulle navn." too_short: "Navnet ditt er for kort." ok: "Navnet ditt ser bra ut." username: @@ -878,7 +877,6 @@ nb_NO: n_posts: one: "1 innlegg" other: "{{count}} innlegg" - cancel: "Vis alle innlegg i dette emnet igjen." split_topic: title: "Del opp Emne" action: "del opp emne" @@ -1343,8 +1341,6 @@ nb_NO: 7_days_ago: "7 Dager Siden" 30_days_ago: "30 Dager Siden" all: "Alle" - view_table: "Se som Tabell" - view_chart: "Se som Stolpediagram" commits: latest_changes: "Siste endringer: Vennligst oppgrader ofte!" by: "av" diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index 150b02b9a7..4d881ef452 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -332,7 +332,6 @@ nl: frequency: "We zullen je alleen maar mailen als we je een tijd niet gezien hebben, en als je toevallig hetgeen waarover we je mailen nog niet hebt gezien op onze site." name: title: "Naam" - instructions: "Je volledige naam." too_short: "Je naam is te kort." ok: "Je naam ziet er prima uit." username: @@ -846,7 +845,6 @@ nl: n_posts: one: "één bericht" other: "{{count}} berichten" - cancel: "Laat alle berichten in deze topic weer zien." split_topic: title: "Verplaats naar nieuwe topic" action: "verplaats naar nieuwe topic" @@ -1294,8 +1292,6 @@ nl: 7_days_ago: "7 Dagen geleden" 30_days_ago: "30 Dagen geleden" all: "Alle" - view_table: "Bekijk als tabel" - view_chart: "Bekijk als staafdiagram" commits: latest_changes: "Laatste wijzigingen: update regelmatig!" by: "door" diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index 312745da0c..8e6912cdc5 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -300,6 +300,7 @@ pl_PL: admin_tooltip: "Ten użytkownik jest administratorem" suspended_notice: "ten użytkownik jest zawieszony do {{date}}." suspended_reason: "Powód: " + github_profile: "Github" mailing_list_mode: "Otrzymuj email z każdym nowym wpisem (o ile nie wyciszysz kategorii lub tematu)." watched_categories: "Obserwowane" watched_categories_instructions: "Będziesz automatycznie śledzić wszystkie nowe tematy w tych kategoriach: liczba nieprzeczytanych i nowych wpisów będzie wyświetlana obok tytułów na liście tematów. Dodatkowo będziesz otrzymywać powiadomienie o każdym nowym wpisie i temacie." @@ -366,7 +367,7 @@ pl_PL: frequency: "Wyślemy do Ciebie email tylko jeżeli dawno Cię nie widzieliśmy i wyłącznie na temat rzeczy których jeszcze nie widziałeś." name: title: "Pełna nazwa" - instructions: "Pełna wersja twojej nazwy." + instructions: "Twoja pełna nazwa (opcjonalna)" too_short: "Twoja nazwa jest za krótka." ok: "Twoja nazwa jest poprawna." username: @@ -538,8 +539,7 @@ pl_PL: created: 'Utworzono' created_lowercase: 'utworzono' trust_level: 'Poziom zaufania' - search_hint: 'nazwa użytkownika lub adres IP' - search_hint_admin: 'nazwa użytkownika, email lub IP' + search_hint: 'nazwa użytkownika, email lub IP' create_account: title: "Utwórz konto" failed: "Coś poszło nie tak, możliwe, że wybrany adres email jest już zarejestrowany, spróbuj użyć odnośnika przypomnienia hasła" @@ -924,7 +924,7 @@ pl_PL: one: "1 wpis" few: "{{count}} wpisy" other: "{{count}} wpisów" - cancel: "Pokaż ponownie wszystkie wpisy w tym temacie." + cancel: "Usuń filtr" split_topic: title: "Przenieś do nowego tematu" action: "przenieś do nowego tematu" @@ -1250,6 +1250,10 @@ pl_PL: submit_tooltip: "Zapisz prywatną flagę." take_action_tooltip: "Nie czekaj, aż wpis zostanie zgłoszony przez innych, natychmiast oflaguj do działania . " cant: "Przepraszamy, nie możesz oflagować teraz tego wpisu." + formatted_name: + off_topic: "Jest nie-na-temat" + inappropriate: "Jest nieodpowiednie" + spam: "Jest odebrane jako spam" custom_placeholder_notify_user: "Dlaczego ten wpis wymaga bezpośredniej, prywatnej rozmowy z tym użytkownikiem? Napisz konkretnie, konstuktywnie i kulturalnie." custom_placeholder_notify_moderators: "Dlaczego ten wpis wymaga uwagi moderatora? Daj nam znać co konkretnie Cię zaniepokoiło i dostarcz nam odpowiednie odnośniki jeśli to możliwe." custom_message: @@ -1411,8 +1415,11 @@ pl_PL: 7_days_ago: "7 dni temu" 30_days_ago: "30 dni temu" all: "Wszystkie" - view_table: "Pokaż jako Tabelę" - view_chart: "Pokaż jako wykres słupkowy" + view_table: "tabela" + view_chart: "wykres słupkowy" + refresh_report: "Odśwież raport" + start_date: "Data początkowa" + end_date: "Data końcowa" commits: latest_changes: "Ostatnie zmiany: aktualizuj często!" by: "przez" @@ -1754,6 +1761,7 @@ pl_PL: last_emailed: "Ostatnio wysłano email" not_found: "Przepraszamu, taka nazwa użytkowanika nie istnieje w naszym systemie." active: "Aktywny" + show_emails: "Pokaż emaile" nav: new: "Nowi" active: "Aktywni" diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml index e9a438c1b7..d410f47002 100644 --- a/config/locales/client.pt.yml +++ b/config/locales/client.pt.yml @@ -328,7 +328,6 @@ pt: frequency: "Vamos enviar-lhe emails apenas quando não o virmos há algum tempo e não tiver visto as coisas que temos enviado." name: title: "Nome" - instructions: "O seu nome completo." too_short: "O seu nome é muito curto." ok: "O seu nome parece bom." username: @@ -840,7 +839,6 @@ pt: n_posts: one: "1 postagem" other: "{{count}} mensagens" - cancel: "Mostrar novamente todas as mensagens deste tópico." split_topic: title: "Mover para um novo Tópico" action: "mover para novo tópico" @@ -1291,8 +1289,6 @@ pt: 7_days_ago: "7 Dias Atrás" 30_days_ago: "30 Dias Atrás" all: "Tudo" - view_table: "Visualizar como Tabela" - view_chart: "Visualizar como Gráfico de Barras" commits: latest_changes: "Últimas atualizações: atualize com frequência!" by: "por" diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml index f01d0179f0..49fe78b7dc 100644 --- a/config/locales/client.pt_BR.yml +++ b/config/locales/client.pt_BR.yml @@ -139,6 +139,7 @@ pt_BR: stat: all_time: "Sempre" last_7_days: "Últimos 7 Dias" + like_count: "Likes" topic_count: "Tópicos" user_count: "Usuários" bookmarks: @@ -329,6 +330,8 @@ pt_BR: change_profile_background: title: "Fundo do perfil" instructions: "Fundos do perfil será centralizado e tera uma largura padrão de 850px." + change_card_background: + instructions: "As Imagens de fundo serão centralizadas e deverão ter largura de 590px" email: title: "Email" instructions: "Não visível publicamente." @@ -338,7 +341,6 @@ pt_BR: frequency: "Vamos lhe enviar emails apenas quando não o virmos há algum tempo e você não tiver visto as coisas que temos enviado." name: title: "Nome" - instructions: "Seu nome completo." too_short: "O seu nome é muito curto." ok: "O seu nome parece ok." username: @@ -504,7 +506,6 @@ pt_BR: created: 'Criado' created_lowercase: 'criado' trust_level: 'Nível de confiança' - search_hint: 'Nome de usuário ou endereço de IP' create_account: title: "Criar nova conta" failed: "Alguma coisa deu errado, talvez este email já esteja registrado, tente usar o Esqueci a Senha." @@ -590,6 +591,7 @@ pt_BR: view_new_post: "Ver sua nova resposta." saving: "Salvando..." saved: "Salvo!" + saved_draft: "Rascunho salvo, clique em selecionar para continuar editando." uploading: "Enviando..." show_preview: 'mostrar pré-visualização »' hide_preview: '« esconder pré-visualização' @@ -878,7 +880,6 @@ pt_BR: n_posts: one: "1 mensagem" other: "{{count}} mensagens" - cancel: "Mostrar novamente todas as mensagens deste tópico." split_topic: title: "Mover para novo tópico" action: "mover para novo tópico" @@ -1344,8 +1345,6 @@ pt_BR: 7_days_ago: "7 Dias Atrás" 30_days_ago: "30 Dias Atrás" all: "Tudo" - view_table: "Visualizar como Tabela" - view_chart: "Visualizar como Gráfico de Barras" commits: latest_changes: "Últimas atualizações: atualize com frequência!" by: "por" @@ -1923,6 +1922,7 @@ pt_BR: listable: Mostrar emblema na página pública de emblemas enabled: Habilitar emblema icon: Ícone + image: Imagem icon_help: "Use uma classe do Font Awesome ou uma URL de uma imagem" query: Badge Query (SQL) target_posts: Consultar respostas selecionadas diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index 58674cbbb2..cb0301e9f9 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -300,6 +300,7 @@ ru: admin_tooltip: "{{user}} - админ" suspended_notice: "Пользователь заморожен до {{date}}." suspended_reason: "Причина:" + github_profile: "Github" mailing_list_mode: "Получать письмо каждый раз, когда появляется новое сообщение (кроме тех случаев, когда тема или раздел отключены)" watched_categories: "Наблюдение" watched_categories_instructions: "Вы будете автоматически отслеживать новые темы в этих разделах. Вам будут приходить уведомления о новых сообщениях и темах, плюс рядом со списком тем будет отображено количество непрочитанных и новых сообщений." @@ -366,7 +367,7 @@ ru: frequency: "В случае вашего отсутствия на форуме вы будете получать уведомления, но только о новых сообщениях." name: title: "Имя" - instructions: "Ваше полное имя." + instructions: "Ваше полное имя (опционально)." too_short: "Ваше имя слишком короткое." ok: "Допустимое имя." username: @@ -538,8 +539,7 @@ ru: created: 'Создан' created_lowercase: 'создано' trust_level: 'Уровень доверия' - search_hint: 'псевдоним или IP адрес' - search_hint_admin: 'псевдоним, e-mail или IP адрес' + search_hint: 'псевдоним, e-mail или IP адрес' create_account: title: "Зарегистрироваться" failed: "Произошла ошибка. Возможно, этот Email уже используется. Попробуйте восстановить пароль" @@ -922,7 +922,7 @@ ru: one: "1 сообщение" few: "{{count}} сообщения" other: "{{count}} сообщений" - cancel: "Показать все сообщения в этой теме еще раз." + cancel: "Отменить фильтр" split_topic: title: "Переместить в новую тему" action: "переместить в новую тему" @@ -1248,6 +1248,10 @@ ru: submit_tooltip: "Отправить приватную отметку" take_action_tooltip: "Достигнуть порога жалоб не дожидаясь большего количества жалоб от сообщества" cant: "Извините, но вы не можете сейчас послать жалобу." + formatted_name: + off_topic: "Это не по теме" + inappropriate: "Это неприемлемо" + spam: "Это спам" custom_placeholder_notify_user: "Почему это сообщение побудило вас обратиться к этому пользователю напрямую и в частном порядке? Будьте конкретны, будьте конструктивны и всегда доброжелательны." custom_placeholder_notify_moderators: "Почему это сообщение побудило вас обратиться с жалобой к модератору? Сообщите нам конкретно, чем вы обеспокоены и предоставьте соответствующие ссылки, где это возможно." custom_message: @@ -1415,8 +1419,11 @@ ru: 7_days_ago: "7 дней" 30_days_ago: "30 дней" all: "Все" - view_table: "Просмотр в виде таблицы" - view_chart: "Просмотр в графическом виде" + view_table: "таблица" + view_chart: "гистограмма" + refresh_report: "Обновить отчет" + start_date: "Дата от" + end_date: "Дата до" commits: latest_changes: "Обновления в репозитории Github" by: "от" @@ -1758,6 +1765,7 @@ ru: last_emailed: "Последнее письмо" not_found: "К сожалению, такой псевдоним не зарегистрирован." active: "Активные" + show_emails: "Показать адреса e-mail" nav: new: "Новые" active: "Активные" diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml index 79ce03070d..9cace9dd0f 100644 --- a/config/locales/client.sv.yml +++ b/config/locales/client.sv.yml @@ -103,6 +103,7 @@ sv: admin_title: "Admin" flags_title: "Flaggningar" show_more: "visa mer" + show_help: "hjälp" links: "Länkar" links_lowercase: "länkar" faq: "FAQ" @@ -136,6 +137,9 @@ sv: stat: all_time: "Alla dagar" last_7_days: "Senaste 7 dagarna" + like_count: "Likes" + post_count: "Inlägg" + user_count: "Användare" bookmarks: not_logged_in: "förlåt, men du måste vara inloggad för att kunna bokmärka inlägg" created: "du har bokmärkt det här inlägget" @@ -268,6 +272,7 @@ sv: admin_tooltip: "Den här användaren är administrator" suspended_notice: "Den här användaren är avstängd till {{date}}." suspended_reason: "Anledning:" + github_profile: "Github" mailing_list_mode: "Skicka ett e-postmeddelande för varje nytt inlägg (gäller ej tystade ämnen eller kategorier)" watched_categories: "Tittade på" watched_categories_instructions: "Du kommer automatiskt att följa alla nya trådar inom dessa kategorier. Du kommer att få notifieringar om alla nya inlägg och trådar; antalet olästa och nya inlägg kommer även att visas vid sidan av trådens namn." @@ -284,6 +289,7 @@ sv: flagged_posts: "flaggade inlägg" deleted_posts: "borttagna inlägg" suspensions: "avstängningar" + warnings_received: "varningar" messages: all: "Alla" mine: "Mina" @@ -329,7 +335,6 @@ sv: frequency: "Vi kommer bara maila dig om vi inte har sett dig nyligen och du inte redan sett det vi mailar dig om." name: title: "Namn" - instructions: "Ditt fullständiga namn" too_short: "Ditt namn är för kort." ok: "Ditt namn ser bra ut." username: @@ -839,7 +844,6 @@ sv: n_posts: one: "1 inlägg" other: "{{count}} inlägg" - cancel: "Visa alla inlägg i den här tråden igen." split_topic: title: "Flytta till nyn tråd" action: "flytta till ny tråd" @@ -1230,8 +1234,6 @@ sv: 7_days_ago: "7 dagar sedan" 30_days_ago: "30 dagar sedan" all: "Alla" - view_table: "Visa som tabell" - view_chart: "Visa som stapeldiagram" commits: latest_changes: "Senaste ändringarna: snälla uppdatera ofta!" by: "av" diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml index 20b23c21b9..8d761fbd02 100644 --- a/config/locales/client.uk.yml +++ b/config/locales/client.uk.yml @@ -646,8 +646,6 @@ uk: help: 'надіслати запрошення друзям, щоб вони могли відповісти на цю тему в один клац' email_placeholder: 'електронна скринька' error: "Вибачте, ми не змогли запросити цю особу. Можливо, вона вже є користувачем?" - filters: - cancel: "Знову показати всі дописи в цій темі." split_topic: title: "Перенесення до нової теми" action: "перенести до нової теми" @@ -979,8 +977,6 @@ uk: 7_days_ago: "7 днів тому" 30_days_ago: "30 днів тому" all: "Все" - view_table: "Таблиця" - view_chart: "Стовпчикова діаграма" commits: latest_changes: "Останні зміни: оновлюйте Вашу копію часто!" by: "від" diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index 4ed684e5ba..f0dddb1d5b 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -254,6 +254,7 @@ zh_CN: admin_tooltip: "用户是管理员" suspended_notice: "该用户将被禁止登录,直至 {{date}}。" suspended_reason: "原因:" + github_profile: "Github" mailing_list_mode: "对每个新帖发送一封邮件(除非您屏蔽了主题或者分类)" watched_categories: "已关注" watched_categories_instructions: "您将自动关注这些分类的所有新主题。您将收到新的帖子和主题的通知,并且会在靠近话题列表的地方增加一个关于未读和新帖子的数字。" @@ -320,7 +321,7 @@ zh_CN: frequency: "只有当您最近一段时间没有访问时,我们才会把您未读过的内容发送到您的电子邮箱。" name: title: "名字" - instructions: "您的全名。" + instructions: "您的全名(可选)。" too_short: "您设置的名字太短了。" ok: "您的名字符合要求。" username: @@ -484,8 +485,7 @@ zh_CN: created: '创建时间' created_lowercase: '创建时间' trust_level: '用户级别' - search_hint: '用户名或IP地址' - search_hint_admin: '用户名,email或IP地址' + search_hint: '用户名、电子邮件或 IP 地址' create_account: title: "创建新帐号" failed: "出问题了,有可能这个电子邮箱已经被注册了。试试忘记密码链接?" @@ -852,7 +852,7 @@ zh_CN: filters: n_posts: other: "{{count}} 个帖子" - cancel: "再次显示本主题下的所有帖子。" + cancel: "取消过滤器" split_topic: title: "拆分主题" action: "拆分主题" @@ -1126,6 +1126,10 @@ zh_CN: submit_tooltip: "提交私有标记" take_action_tooltip: "与其等待更多的社区的标志, 不如立刻到达标志." cant: "抱歉,当前您不能报告本帖。" + formatted_name: + off_topic: "偏题" + inappropriate: "不合适" + spam: "广告" custom_placeholder_notify_user: "为何您要私下联系该用户?" custom_placeholder_notify_moderators: "为何本帖需要论坛版主的关注?为何本帖需要论坛版主的注意?" custom_message: @@ -1163,10 +1167,10 @@ zh_CN: posts_lowercase: "帖子" posts_long: "本主题有 {{number}} 个帖子" posts_likes_MF: | - 这个主题有 {count, plural, one {1 个帖子}其他{# 帖子数}} {ratio, select, - low {with a high like to like ratio} - med {with a very high post to like ratio} - high {with an extremely high post to like ratio} + 这个主题有 {count, plural, other {# 个帖子}},{ratio, select, + low {并被很多人赞了} + med {并被非常多人赞了} + high {并被特别多人赞了} other {}} original_post: "原始帖" views: "浏览" @@ -1291,8 +1295,11 @@ zh_CN: 7_days_ago: "7 天之前" 30_days_ago: "30 天之前" all: "全部" - view_table: "以表格展示" - view_chart: "以柱状图展示" + view_table: "表格" + view_chart: "柱状图" + refresh_report: "刷新报告" + start_date: "开始日期" + end_date: "结束日期" commits: latest_changes: "最近的更新:请经常升级!" by: "来自" @@ -1624,6 +1631,7 @@ zh_CN: last_emailed: "最后一次邮寄" not_found: "抱歉,在我们的系统中此用户名不存在。" active: "活跃" + show_emails: "显示邮件" nav: new: "新建" active: "活跃" diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index 86f5fb31ab..58914a1bd1 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -318,7 +318,6 @@ zh_TW: frequency: "我們只會在一段時間未看到你及你尚未看過我們通知你的事物時才會寄信給你。" name: title: "名稱" - instructions: "您的全名。" too_short: "你的名稱太短。" ok: "你的名稱符合要求。" username: @@ -482,8 +481,6 @@ zh_TW: created: '已建立' created_lowercase: '已建立' trust_level: '信任等級' - search_hint: '用戶名稱或 IP 位址' - search_hint_admin: '用戶名稱、電子郵件、或 IP 位址' create_account: title: "建立新帳號" failed: "發生了某些錯誤,可能此電子郵件地址已經註冊過,請試試看忘記密碼連結" @@ -833,7 +830,6 @@ zh_TW: filters: n_posts: other: "{{count}} 則文章" - cancel: "再次顯示此討論話題的所有文章。" split_topic: title: "移至新討論話題" action: "移至新討論話題" @@ -1256,8 +1252,6 @@ zh_TW: 7_days_ago: "7 天前" 30_days_ago: "30 天前" all: "全部" - view_table: "以數字檢視" - view_chart: "以直條圖檢視" commits: latest_changes: "最近的變更:請經常更新!" by: "由" diff --git a/config/locales/server.cs.yml b/config/locales/server.cs.yml index ffd584b720..989c069724 100644 --- a/config/locales/server.cs.yml +++ b/config/locales/server.cs.yml @@ -582,18 +582,6 @@ cs: archived_disabled: "Toto téma je vráceno z archivu. Již není zmraženo a může být měněno." closed_enabled: "Toto téma je uzavřeno. Nové odpovědi nebudou přijímány." closed_disabled: "Toto téma je otevřeno. Nové odpovědi jsou povoleny." - autoclosed_enabled_days: - zero: "Tohle téma bylo automaticky uzavřeno po 1 dni. Nové odpovědi už se nepřijímají." - one: "Tohle téma bylo automaticky uzavřeno po 1 dni. Nové odpovědi už se nepřijímají." - other: "Toto téma bylo po %{count} dnech automaticky zavřeno. Nové odpovědi se už nepřijímají." - autoclosed_enabled_hours: - zero: "Toto téma bylo po 1 hodině automaticky uzavřeno. Nové odpovědi se už nepřijímají." - one: "Toto téma bylo po 1 hodině automaticky uzavřeno. Nové odpovědi se už nepřijímají." - other: "Toto téma bylo po %{count} hodinách automaticky uzavřeno. Nové odpovědi se už nepřijímají." - autoclosed_enabled_minutes: - zero: "Toto téma bylo po 1 minutě automaticky uzavřeno. Nové odpovědi se už nepřijímají." - one: "Toto téma bylo po 1 minutě automaticky uzavřeno. Nové odpovědi se už nepřijímají." - other: "Toto téma bylo po %{count} minutách automaticky uzavřeno. Nové odpovědi se už nepřijímají." autoclosed_disabled: "Toto téma je otevřeno. Nové odpovědi jsou povoleny." pinned_disabled: "Toto téma již není připevněno. Nebude se zobrazovat na vrcholu seznamu témat v kategorii." login: diff --git a/config/locales/server.da.yml b/config/locales/server.da.yml index 6b1b629371..8e76a579d9 100644 --- a/config/locales/server.da.yml +++ b/config/locales/server.da.yml @@ -619,18 +619,6 @@ da: archived_disabled: "Dette emne er nu ikke længere arkiveret. Det er ikke længere frosset fast, og kan ændres." closed_enabled: "Dette emne er nu lukket. Nye indlæg tillades ikke." closed_disabled: "Dette emne er nu åbent. Nye indlæg tillades." - autoclosed_enabled_days: - zero: "Dette emne blev automatisk lukket efter 1 dag. Nye svar er ikke længere tilladt." - one: "Dette emne blev automatisk lukket efter 1 dag. Nye svar er ikke længere tilladt." - other: "Dette emne blev automatisk lukket efter %{count} dage. Nye svar er ikke længere tilladt." - autoclosed_enabled_hours: - zero: "Dette emne blev automatisk lukket efter 1 time. Nye svar er ikke længere tilladt." - one: "Dette emne blev automatisk lukket efter 1 time. Nye svar er ikke længere tilladt." - other: "Dette emne blev automatisk lukket efter %{count} timer. Nye svar er ikke længere tilladt." - autoclosed_enabled_minutes: - zero: "Dette emne blev automatisk lukket efter 1 minut. Nye svar er ikke længere tilladt." - one: "Dette emne blev automatisk lukket efter 1 minut. Nye svar er ikke længere tilladt." - other: "Dette emne blev automatisk lukket efter %{count} minutter. Nye svar er ikke længere tilladt." autoclosed_disabled: "Dette emne er nu åbnet. Nye svar er tilladt." pinned_enabled: "Dette emne er nu fastgjort. Det vil stå i toppen af dets kategori indtil det bliver frigjort af \"staff\" for alle eller af hver bruger individuelt for dem selv." pinned_disabled: "Dette emne er nu frigjort. Det optræder ikke længere i toppen af dets kategori." diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index 73d761db05..9a2ab12a6f 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -845,18 +845,6 @@ de: archived_disabled: "Dieses Thema wurde aus dem Archiv geholt. Es ist nicht länger eingefroren und kann verändert werden." closed_enabled: "Dieses Thema ist nun geschlossen. Es ist nicht länger möglich, auf dieses Thema zu antworten." closed_disabled: "Dieses Thema ist nun geöffnet. Es ist wieder möglich, auf dieses Thema zu antworten." - autoclosed_enabled_days: - zero: "Dieses Thema wurde automatisch nach 1 Tag geschlossen. Neue Antworten sind nicht länger erlaubt." - one: "Dieses Thema wurde automatisch nach 1 Tag geschlossen. Neue Antworten sind nicht länger erlaubt." - other: "Dieses Thema wurde automatisch nach %{count} Tagen geschlossen. Neue Antworten sind nicht länger erlaubt." - autoclosed_enabled_hours: - zero: "Dieses Thema wurde automatisch nach 1 Stunde geschlossen. Neue Antworten sind nicht länger erlaubt." - one: "Dieses Thema wurde automatisch nach 1 Stunde geschlossen. Neue Antworten sind nicht länger erlaubt." - other: "Dieses Thema wurde automatisch nach %{count} Stunden geschlossen. Neue Antworten sind nicht länger erlaubt." - autoclosed_enabled_minutes: - zero: "Dieses Thema wurde automatisch nach 1 Minute geschlossen. Neue Antworten sind nicht länger erlaubt." - one: "Dieses Thema wurde automatisch nach 1 Minute geschlossen. Neue Antworten sind nicht länger erlaubt." - other: "Dieses Thema wurde automatisch nach %{count} Minuten geschlossen. Neue Antworten sind nicht länger erlaubt." autoclosed_disabled: "Dieses Thema ist nun offen. Neue Beiträge werden angenommen." pinned_enabled: "Dieses Thema ist nun angepinnt. In seiner Kategorie wird es nun oben aufgelistet, solange der Pin nicht von einem Moderator gelöst wird, oder nicht jeder Nutzer selbst den Pin löst." pinned_disabled: "Dieses Thema ist nun nicht mehr angepinnt. In seiner Kategorie wird es nicht länger oben aufgelistet." @@ -1017,6 +1005,9 @@ de: ``` %{logs} ``` + restore_succeeded: + subject_template: "Wiederherstellung erfolgreich abgeschlossen" + text_body_template: "Die Wiederherstellung war erfolgreich." restore_failed: subject_template: "Wiederherstellung fehlgeschlagen" bulk_invite_succeeded: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 67a2df56b8..684bd7dfeb 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -414,7 +414,7 @@ en: activation: action: "Activate your account" already_done: "Sorry, this account confirmation link is no longer valid. Perhaps your account is already active?" - please_continue: "Your new account is confirmed, and you are now logged in." + please_continue: "Your new account is confirmed! You will now be redirected to the home page." continue_button: "Continue to %{site_name}" welcome_to: "Welcome to %{site_name}!" approval_required: "A moderator must manually approve your new account before you can access this forum. You'll get an email when your account is approved!" @@ -422,7 +422,7 @@ en: post_action_types: off_topic: title: 'Off-Topic' - description: 'This post is radically off-topic in the current topic, and should probably be moved. If this is a topic, perhaps it does not belong here.' + description: 'This post is not relevant to the current discussion as defined by the title and first post, and should probably be moved elsewhere.' long_form: 'flagged this as off-topic' spam: title: 'Spam' @@ -440,7 +440,7 @@ en: email_body: "%{link}\n\n%{message}" notify_moderators: title: 'Notify moderators' - description: 'This post requires general moderator attention based on the guidelines, TOS, or for another reason not listed above.' + description: 'This post requires moderator attention for another reason not listed above.' long_form: 'notified moderators' email_title: 'A post in "%{title}" requires moderator attention' email_body: "%{link}\n\n%{message}" diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index 2a1b68cbb7..35b03c6c75 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -924,18 +924,6 @@ es: archived_disabled: "Este tema se ha desarchivado. Se desparaliza y ahora puede ser cambiado." closed_enabled: "Este tema está ahora cerrado. No se admiten nuevas respuestas." closed_disabled: "Este tema ahora está abierto. Se admiten nuevas respuestas." - autoclosed_enabled_days: - zero: "Este tema ha sido cerrado automáticamente después de 1 día. No se admiten nuevas respuestas." - one: "Este tema ha sido cerrado automáticamente después de 1 día. No se admiten nuevas respuestas." - other: "Este tema ha sido cerrado automáticamente después de %{count} días. No se admiten nuevas respuestas." - autoclosed_enabled_hours: - zero: "Este tema ha sido cerrado automáticamente después de 1 hora. No se admiten nuevas respuestas." - one: "Este tema ha sido cerrado automáticamente después de 1 hora. No se admiten nuevas respuestas." - other: "Este tema ha sido cerrado automáticamente después de %{count} horas. No se admiten nuevas respuestas." - autoclosed_enabled_minutes: - zero: "Este tema ha sido cerrado automáticamente después de 1 minuto. No se admiten nuevas respuestas." - one: "Este tema ha sido cerrado automáticamente después de 1 minuto. No se admiten nuevas respuestas." - other: "Este tema ha sido cerrado automáticamente después de %{count} minutos. No se admiten nuevas respuestas." autoclosed_disabled: "El tema ahora está en abierto, se permiten respuestas." pinned_enabled: "Este tema ahora está destacado. Aparecerá en primer lugar en la lista de su categoría hasta que se deshaga el destacado de forma general por los moderadores o de forma particular por cada usuario para sí." pinned_disabled: "Este tema está ahora sin destacar. No aparecerá más en la parte superior de su categoría." diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index bb8bb5d5bc..2f81a88b87 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -630,9 +630,14 @@ fi: summary_posts_required: "Montako viestiä ketjussa täytyy olla, jotta ketjun tiivistelmä otetaan käyttöön" summary_likes_required: "Montako tykkäystä ketjussa pitää olla, jotta ketjun tiivistelmä otetaan käyttöön" summary_percent_filter: "Kun käyttäjä klikkaa 'Näytä ketjun tiivistelmä', näytä paras % viesteistä" + summary_max_results: "Maksimimäärä viestejä, jotka näytetään ketjun tiivistelmässä" enable_private_messages: "Salli luottamustason 1 käyttäjien luoda ja vastata yksityisviesteihin" enable_long_polling: "Ilmoitusten käyttämä viestiväylä voi käyttää long pollingia" + long_polling_base_url: "Base URL, jota käytetään long pollingissa (kun CDN on käytössä, varmista että tähän on asetettu origin pull) esim: http://origin.site.com" + long_polling_interval: "Kuinka kauan palvelimen pitäisi odottaa ennen vastaamista asiakkaalle, kun ei ole mitään dataa jota lähettää (vain kirjautuneille käyttäjille)" + polling_interval: "Kun long polling ei ole käytössä, kuinka usein kirjautuneet käyttäjät pollaavat, millisekunneissa." anon_polling_interval: "Kuinka usein anonyymit käyttäjät pollaavat millisekunneissa" + background_polling_interval: "Kuinka usein asiakkaat pollaavat, millisekunneissa (kun ikkuna ei ole aktiivisena)" auto_track_topics_after: "Missä ajassa ketjua aletaan seurata oletusarvoisesti millisekunneissa, käyttäjän muokattavissa (0 aina, -1 ei koskaan)" new_topic_duration_minutes: "Kuinka kauan ketju lasketaan uudeksi oletusarvona minuuteissa, käyttäjän muokattavissa (-1 aina, -2 edellisen vierailun jälkeen luodut ketjut)" flags_required_to_hide_post: "Lippujen lukumäärä, joka automaattisesti piilottaa viestin ja lähettää YV:n sen kirjoittajalle (0 ei koskaan)" @@ -660,11 +665,14 @@ fi: share_links: "Mitkä painikkeet näytetään Jaa-valikossa ja missä järjestyksessä." track_external_right_clicks: "Seuraa pois sivustolta vieviä linkkejä, jotka avataan hiiren oikealla näppäimellä (esim. avaa uudessa välilehdessä) oletuksena poistettu käytöstä, koska tämä kirjoittaa URL:n uudelleen" topics_per_page: "Kuinka monta ketjua ladataan avattaessa ketjulista ja kerrallaan lisää vieritettäessä sivua alaspäin" + posts_chunksize: "Kuinka monta viestiä ladataa avattaessa viestiketju ja kerrallaan lisää vieritetäessä sivua alaspäin" site_contact_username: "Kaikki automaattiset yksityisviestit lähetetään tämän käyttäjän nimissä; jos jätetään tyhjäksi oletuksena on System-käyttäjä." send_welcome_message: "Lähetä kaikille uusille käyttäjille yksityinen tervetuliaisviesti, jossa on pikakäyttöopas." suppress_reply_directly_below: "Älä näytä vastausten lukumäärää viestissä, jos ainoa vastaus on seuraavassa viestissä." suppress_reply_directly_above: "Älä näytä vastauksena-painiketta viestin yläreunassa, jos viestissä on vastattu vain edelliseen viestiin." suppress_reply_when_quoting: "Älä näytä vastauksena-painiketta viestin yläreunassa, kun viestissä on lainaus." + max_reply_history: "Maksimimäärä vastauksia, jotka avataan klikattaessa 'vastauksena' painiketta" + experimental_reply_expansion: "Piilota välilliset vastaukset, kun 'vastauksena' avataan (kokeellinen)" topics_per_period_in_top_summary: "Kejujen lukumäärä, joka näytetään oletuksena Huiput-listauksissa." topics_per_period_in_top_page: "Kejujen lukumäärä, joka näytetään laajennetussa Huiput-listauksessa." redirect_users_to_top_page: "Ohjaa uudet ja kauan poissa olleet käyttäjät automaattisesti huiput-sivulle." @@ -814,6 +822,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" + enable_email_names: "Salli koko nimen käyttäminen sähköposteissa. Ota pois käytöstä piilottaaksesi koko nimen sähköposteissa." 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." @@ -870,6 +879,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." vacuum_db_days: "Aja VACUUM FULL ANALYZE vapauttaaksesi tilaa tietokantaan migraatioiden jälkeen (aseta 0 ottaaksesi pois 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." errors: invalid_email: "Sähköpostiosoite ei kelpaa." invalid_username: "Tällä nimellä ei löydy käyttäjää." @@ -923,18 +933,25 @@ fi: closed_enabled: "Tämä ketju on suljettu. Uusia vastauksia ei voi enää kirjoittaa." closed_disabled: "Tämä ketju on nyt avattu. Uusia vastauksia voi taas kirjoittaa." autoclosed_enabled_days: - zero: "Tämä ketju suljettiin automaattisesti 1 päivän kuluttua. Uusia vastauksia ei voi enää kirjoittaa." one: "Tämä ketju suljettiin automaattisesti 1 päivän kuluttua. Uusia vastauksia ei voi enää kirjoittaa." other: "Tämä ketju suljettiin automaattisesti %{count} päivän kuluttua. Uusia vastauksia ei voi enää kirjoittaa." autoclosed_enabled_hours: - zero: "Tämä ketju suljettiin automaattisesti 1 päivän kuluttua. Uusia vastauksia ei voi enää kirjoittaa." - one: "Tämä ketju suljettiin automaattisesti 1 päivän kuluttua. Uusia vastauksia ei voi enää kirjoittaa." + one: "Tämä ketju suljettiin automaattisesti 1 tunnin kuluttua. Uusia vastauksia ei voi enää kirjoittaa." other: "Tämä ketju suljettiin automaattisesti %{count} tunnin kuluttua. Uusia vastauksia ei voi enää kirjoittaa." autoclosed_enabled_minutes: - zero: "Tämä ketju suljettiin automaattisesti 1 minuutin kuluttua. Uusia vastauksia ei voi enää kirjoittaa." one: "Tämä ketju suljettiin automaattisesti 1 minuutin kuluttua. Uusia vastauksia ei voi enää kirjoittaa." other: "Tämä ketju suljettiin automaattisesti %{count} minuutin kuluttua. Uusia vastauksia ei voi enää kirjoittaa." + autoclosed_enabled_lastpost_days: + one: "Tämä ketju suljettiin automaattisesti 1 päivän kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." + other: "Tämä ketju suljettiin automaattisesti %{count} päivän kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." + autoclosed_enabled_lastpost_hours: + one: "Tämä ketju suljettiin automaattisesti 1 tunnin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." + other: "Tämä ketju suljettiin automaattisesti %{count} tunnin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." + autoclosed_enabled_lastpost_minutes: + one: "Tämä ketju suljettiin automaattisesti 1 minuutin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." + other: "Tämä ketju suljettiin automaattisesti %{count} minuutin kuluttua viimeisestä viestistä. Uusia vastauksia ei voi enää kirjoittaa." autoclosed_disabled: "Tämä ketju on nyt avattu. Uusia vastauksia voi taas kirjoittaa." + autoclosed_disabled_lastpost: "Tämä ketju on nyt avattu. Uusia vastauksia voi taas kirjoittaa." pinned_enabled: "Tämä ketju on nyt kiinnitetty. Se näytetään alueensa ylimmäisenä. Kiinnityksen voi poistaa joko henkilökunnan jäsen kaikilta yhteisesti tai yksittäiset käyttäjät itseltään." pinned_disabled: "Ketjun kiinnitys on poistettu. Sitä ei enää näytetä alueen ylimmäisenä." pinned_globally_enabled: "Tämä ketju on nyt kiinnitetty yleisesti. Se näytetään alueensa ja kaikkien listausten ylimmäisenä. Kiinnityksen voi poistaa joko henkilökunnan jäsen kaikilta yhteisesti tai yksittäiset käyttäjät itseltään." @@ -1287,10 +1304,26 @@ fi: Pahoittelut, sähköpostiviestiäsi tänne: %{destination} (otsikolla %{former_title}) ei voitu toimittaa. Tilisi luottamustaso ei ole riittävä, jotta saisit lähettää uusia ketjuja tähän sähköpostiosoitteeseen. Jos uskot, että tämä johtuu virheestä, ota yhteyttä henkilökuntaan. + email_reject_no_account: + subject_template: "Sähköpostiongelma -- Tuntematon tili" + text_body_template: | + Pahoittelemme, mutta sähköpostin lähettäminen tänne %{destination} (otsikolla %{former_title}) ei onnistunut. + + Tälle sähköpostiosoitteelle ei löydy käyttäjätiliä. Yritä lähettää sähköposti toisesta osoitteesta tai ota yhteyttä henkilökuntaan. email_reject_empty: subject_template: "Sähköpostiongelma -- Ei sisältöä" + text_body_template: | + Pahoittelemme, että sähköpostin lähettäminen tänne %{destination} (otsikolla %{former_title}) ei onnistunut. + + Emme löytäneet sähköpostista viestin sisältöä. Varmista, että viestisi on sähköpostin alussa -- emme pysty käsittelemään lainausten sekaan kirjoitettuja vastauksia. + + Jos saat tämän viestin, vaikka sähköpostissa _oli_ sisältöä, yritä uudestaan siten, että viesti sisältää HTML-sisältöä (ei pelkkää selkotekstiä). email_reject_parsing: subject_template: "Sähköpostionelma -- Tunnistamaton sisältö" + text_body_template: | + Pahoittelemme, että sähköpostin lähettäminen tänne %{destination} (otsikolla %{former_title}) ei onnistunut. + + Emme löytäneet viestisi sisältöä sähköpostista. **Varmista, että kirjoitit viestisi sähköpostin alkuun** -- emme pysty käsittelemään lainausten sekaan kirjoitettuja vastauksia email_reject_post_error: subject_template: "Sähköpostiongelma -- Lähetysvirhe" text_body_template: | @@ -1308,10 +1341,29 @@ fi: Jos voit korjata ongelman, yritä uudelleen. email_reject_reply_key: + subject_template: "Sähköpostiongelma -- Tuntematon vastausavain" text_body_template: | Pahoittelemme, mutta sähköpostin lähettäminen tänne %{destination} (otsikolla %{former_title}) ei onnistunut. Annettu vastausavain on väärä tai tuntematon, joten emme pystyneet selvittämään mihin tämä sähköposti lähetettiin vastauksena. Ota yhteys henkilökuntaan. + email_reject_destination: + subject_template: "Sähköpostiongelma -- Tuntematon Vastaanottaja: -osoite" + text_body_template: | + Pahoittelut, sähköpostiviestiäsi tänne: %{destination} (otsikolla %{former_title}) ei voitu toimittaa. + + Mitään sähköpostin kohdeosoitteista ei tunnistettu. Varmista, että sähköpostiosoite on "Vastaanottaja:"-rivillä (ei CC tai BCC) ja että yrität lähettää sähköpostia osoitteeseen, jonka palstan henkilökunta on antanut. + email_reject_topic_not_found: + subject_template: "Sähköpostiongelma -- Ketjua ei löytynyt" + text_body_template: | + Pahoittelut, sähköpostiviestiäsi tänne: %{destination} (otsikolla %{former_title}) ei voitu toimittaa. + + Ketjua ei löytynyt, se on voitu poistaa. Jos uskot, että on tapahtunut virhe, ota yhteyttä palstan henkilökuntaan. + email_reject_topic_closed: + subject_template: "Sähköpostiongelma -- Suljettu ketju" + text_body_template: | + Pahoittelut, sähköpostiviestiäsi tänne: %{destination} (otsikolla %{former_title}) ei voitu toimittaa. + + Ketju on suljettu. Jos uskot, että on tapahtunut virhe, ota yhteys palstan henkilökuntaan. email_error_notification: subject_template: "Sähköpostiongelma -- POP autentikoinnin virhe" text_body_template: | diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index 2f0895eb4c..b846f45087 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -628,6 +628,7 @@ fr: summary_posts_required: "Nombre minimum de messages dans un sujet avant que le 'Résumé du sujet' soit activé" summary_likes_required: "Nombre de J'aime minimum dans un sujet avant que le 'Résumé du sujet' soit activé" summary_percent_filter: "Quand un utilisateur clique sur 'Résumé du sujet', montrer le top % des messages" + summary_max_results: "Nombre maximum de messages retournés par 'Résumé de ce sujet'" enable_private_messages: "Autoriser les utilisateurs du niveau de confiance 1 à créer des messages et conversations privés." enable_long_polling: "Utiliser les requêtes longues pour le flux de notifications." long_polling_base_url: "Racine de l'url utilisée pour les requêtes longues (dans le cas de l'utilisation d'un CDN pour fournir du contenu dynamique, pensez à le configurer en mode \"origin pull\") par exemple: http://origin.site.com" @@ -930,18 +931,22 @@ fr: closed_enabled: "Ce sujet est maintenant fermé. Les nouvelles réponses ne sont plus autorisées." closed_disabled: "Ce sujet est maintenant ouvert. Les nouvelles réponses sont autorisées." autoclosed_enabled_days: - zero: "Ce sujet a été automatiquement fermé après 1 jour. Aucune réponse n'est permise dorénavant." - one: "Ce sujet a été automatiquement fermé après 1 jour. Aucune réponse n'est permise dorénavant." - other: "Ce sujet a été automatiquement fermé après %{count} jours. Aucune réponse n'est permise dorénavant." + one: "Cette discussion a été automatiquement fermée après un jour. Aucune réponse n'est permise dorénavant." + other: "Cette discussion a été automatiquement fermée après %{count} jours. Aucune réponse n'est permise dorénavant." autoclosed_enabled_hours: - zero: "Ce sujet a été automatiquement fermé après 1 heure. Aucune réponse n'est permise dorénavant." - one: "Ce sujet a été automatiquement fermé après 1 heure. Aucune réponse n'est permise dorénavant." - other: "Ce sujet a été automatiquement fermé après %{count} heures. Aucune réponse n'est permise dorénavant." + one: "Cette discussion a été automatiquement fermée après une heure. Aucune réponse n'est permise dorénavant." + other: "Cette discussion a été automatiquement fermée après %{count} heures. Aucune réponse n'est permise dorénavant." autoclosed_enabled_minutes: - zero: "Ce sujet a été automatiquement fermé après 1 minute. Aucune réponse n'est permise dorénavant." - one: "Ce sujet a été automatiquement fermé après 1 minute. Aucune réponse n'est permise dorénavant." - other: "Ce sujet a été automatiquement fermé après %{count} minutes. Aucune réponse n'est permise dorénavant." + one: "Cette discussion a été automatiquement fermée après une minute. Aucune réponse n'est permise dorénavant." + other: "Cette discussion a été automatiquement fermée après %{count} minutes. Aucune réponse n'est permise dorénavant." + autoclosed_enabled_lastpost_days: + one: "Cette discussion a été automatiquement fermée après un jour. Aucune réponse n'est permise dorénavant." + other: "Cette discussion a été automatiquement fermée après %{count} jours. Aucune réponse n'est permise dorénavant." + autoclosed_enabled_lastpost_minutes: + one: "Cette discussion a été automatiquement fermée une minute après le dernier message. Aucune réponse n'est permise dorénavant." + other: "Cette discussion a été automatiquement fermée %{count} minutes après le dernier message. Aucune réponse n'est permise dorénavant." autoclosed_disabled: "Ce sujet est maintenant ouvert. Les nouvelles réponses sont autorisées." + autoclosed_disabled_lastpost: "Cette discussion est maintenant ouverte. Vous pouvez y participer." pinned_enabled: "Ce sujet est maintenant épinglé. Il apparaîtra en haut de sa catégorie jusqu'à ce qu'il soit désépinglé par un modérateur, ou individuellement par les utilisateurs eux-mêmes." pinned_disabled: "Ce sujet est maintenant dé-épinglé. Il n'apparaîtra plus en haut de sa categorie." pinned_globally_enabled: "Ce sujet est maintenant épinglé pour tous. Il apparaîtra en haut de sa catégorie et de chaque liste de sujets jusqu'à ce qu'il soit désépinglé par un modérateur, ou individuellement par les utilisateurs eux-mêmes." diff --git a/config/locales/server.he.yml b/config/locales/server.he.yml index 6d0fdac566..bd36bfeed0 100644 --- a/config/locales/server.he.yml +++ b/config/locales/server.he.yml @@ -603,6 +603,7 @@ he: summary_posts_required: "מספר הפרסומים המנימאלי בנושא לפני שהאפשרות \"סיכום נושא זה\" תתאפשר" summary_likes_required: "מינימום הלייקים לנושא לפני שהאפשרות \"סיכום נושא זה\" תתאפשר" summary_percent_filter: "כאשר משתמש/ת מקליקים על \"סיכום נושא זה\", הציגו את % o הפרסומים הראשונים" + summary_max_results: "מספר הפרסומים שיוחזרו באמצעות \"סיכום נושא זה\"" enable_private_messages: "אפשרו למשתמשים בעלי רמת אמון 1 ליצור מסרים פרטיים ולהשיב למסרים פרטיים" enable_long_polling: "Message bus used for notification can use long polling" long_polling_base_url: "בסיס ה-URL שנמצא בשימוש עבור long polling (כאשר CDN מחזיר תוכן דינמי, זכרו להגדיר את ערך זה ל-Origin pull, דוגמת http://origin.site.com)" @@ -643,6 +644,7 @@ he: suppress_reply_directly_below: "אל תציגו את סך התגובות המצטבר בפרסום כאשר ישנה תגובה ישירה אחת לפרסום זה." suppress_reply_directly_above: "אל תציגו את את אפשרות ההרחבה \"בתגובה ל..\" לפרסום כאשר יש רק תגובה אחת ישירה מעל לפרסום זה." suppress_reply_when_quoting: "אל תציגו את הפרסום המקורי בפרסומים שמצטטים תגובות" + max_reply_history: "מספר התגובות המקסימלי להרחבה כאשר מרחיבים \"בתגובה ל\"" experimental_reply_expansion: "החבא תגובות ביניים כאשר מרחיבים תגובה (ניסיוני)" topics_per_period_in_top_summary: "מספר הנושאים המוצגים בבריכת המחדל של סיכום הנושאים." topics_per_period_in_top_page: "מספר הנושאים הראשונים המוצגים בתצוגה המורחבת של \"הצג עוד\"." @@ -742,6 +744,9 @@ he: tl3_requires_posts_read_all_time: "מספר מינימלי של פרסומים שמשתמש קרא על מנת שיוכל להיות משודרג לרמת אמון 3." tl3_requires_max_flagged: "משתמש לא פרסם יותר מ-X פרסומים שסומנו (flagged) ע\"י משתמשים ב-100 הימים האחרונים, על מנת שיוכל להיות משודרג לרמת אמון 3, כאשר X הוא הערך הזה (0 או גבוה יותר)." tl3_promotion_min_duration: "מספר הימים המינימלי ששדרוג לרמת אמון 3 תארך לפני שמשתמש יורד בדרגה לרמת אמון 2." + tl3_requires_likes_given: "מינימום הלייקים הנדרשים ב-100 הימים האחרונים כדי להכשיר קידום לרמת אמון 3." + tl3_requires_likes_received: "מינימום הלייקים הנדרש ב-100 הימים האחרונים כדי לאפשר קידום לרמת אמון 3." + tl3_links_no_follow: "מניעת הסרת rel=nofollow מקישורים שמפורסמים על ידי משתמשים ברמת אמון 3." min_trust_to_create_topic: "The minimum trust level required to create a new topic." min_trust_to_edit_wiki_post: "דרגת האמון המינימלי הנדרשת כדי לערוך פרסום שמסומן כ-wiki." newuser_max_links: "כמה קישורים יכול/ה משתמש/ת חדשים להוספים לפרסום." @@ -766,6 +771,18 @@ he: authorized_extensions: "רשימה של הרחבות מותרות להעלאה (השתמשו ב '*' כדי לאפשר את כל סוגי הקבצים)" max_similar_results: "כמה נושאים דומים להציג מעל לעורך כאשר מחברים נושא חדש. ההשוואה מבוססת על הכותרת וגוף הפרסום." title_prettify: "Prevent common title typos and errors, including all caps, lowercase first character, multiple ! and ?, extra . at end, etc." + topic_views_heat_low: "לאחר כמות זו של צפיות,שדה הצפיות יהיה קצת יותר בהיר." + topic_views_heat_medium: "לאחר כמות צפיות זו, שדה הצפיות יודגש באופן בינוני." + topic_views_heat_high: "לאחר כמות צפיות זו, שדה הצפיות יודגש באופן בולט." + cold_age_days_low: "לאחר מספר זה של ימי שיחה, תאריך הפעולה האחרונה מודגש מעט." + cold_age_days_medium: "לאחר מספר זה של ימי שיחה, תאריך הפעולה האחרונה מודגש באופן בינוני." + cold_age_days_high: "לאחר מספר זה של ימי שיחה, תאריך הפעולה האחרונה מודגש חזק." + history_hours_low: "פרסום שנערך תוך כדי מספר שעות זה יופיע עם אינידקציית עריכה מודגשת קלות." + history_hours_medium: "פרסום שנערך תוך כדי מספר שעות זה יופיע עם אינידקציית עריכה מודגשת באופן בינוני." + history_hours_high: "פרסום שנערך תוך כדי מספר שעות זה יופיע עם אינידקציית עריכה מודגשת באופן חזק." + topic_post_like_heat_low: "לאחר שהיחס בין לייקים למספר הפרסומים גבוהה מערך זה, שדה ספירת הפרסומים מודגש קלות." + topic_post_like_heat_medium: "לאחר שהיחס בין לייקים למספר הפרסומים גבוהה מערך זה, שדה ספירת הפרסומים מודגש באופן בינוני." + topic_post_like_heat_high: "לאחר שהיחס בין לייקים למספר הפרסומים גבוהה מערך זה, שדה ספירת הפרסומים מודגש באופן חזק." faq_url: "If you have a FAQ hosted elsewhere that you want to use, provide the full URL here." tos_url: "If you have a Terms of Service document hosted elsewhere that you want to use, provide the full URL here." privacy_policy_url: "If you have a Privacy Policy document hosted elsewhere that you want to use, provide the full URL here." @@ -776,6 +793,16 @@ he: reply_by_email_enabled: "אפשרו תגובה לנושאים באמצעות הדוא\"ל." reply_by_email_address: "Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" disable_emails: "מנעו מ-Discourse ממשלו דוא\"ל כלשהו." + strip_images_from_short_emails: "הסרת תמונות מדוא\"ל בגודל של פחות מ-2800 בייטים" + short_email_length: "אורכו של דוא\"ל קצר בבייטים (Bytes)" + enable_email_names: "אפשרו הצגת שם מלא של משתמש בדוא\"ל. נטרלו אפשרות זו כדי להסתיר את השם המלא בדוא\"ל." + pop3_polling_enabled: "משיכה דרך POP3 לתגובות דוא\"ל." + pop3_polling_ssl: "שימוש ב-SSL בעת חיבור לשרת POP3. (מומלץ)" + pop3_polling_period_mins: "משך הזמן בדקות בין בדיקת הדוא\"ל בחשבון ה-POP3. הערה: דורש אתחול." + pop3_polling_port: "החיבור (Port) ממנו יש למשוך נתונים מחשבון POP3." + pop3_polling_host: "השרת המארח (Host) למשיכת דוא\"ל דרך POP3." + pop3_polling_username: "שם המשתמש/ת לחשבון ה-POP3 למשיכת דוא\"ל." + pop3_polling_password: "הסיסא לחשבון ה-POP3 למשיכת הדוא\"ל." email_in: "אפשרו למשתמשים לפרסם נושאים חדשים באמצעות דוא\"ל (דורש משיכה באמצעוצ pop3). הגדירו את הכתובת בלשונית \"הגדרות\" עבור כל קטגוריה." email_in_min_trust: "רמת האמון המינימלית הנדרשת למשתמש/ת כדי שיוכלו להעלות נושאים חדשים באמצעות הדוא\"ל." email_prefix: "ה[תווית] שתשמש בנושא הודעות הדוא\"ל. אם לא יוגדר, ברירת המחדל תכוון ל'כותרת' אם לא יוגדר אחרת." @@ -786,6 +813,7 @@ he: delete_all_posts_max: "The maximum number of posts that can be deleted at once with the Delete All Posts button. If a user has more than this many posts, the posts cannot all be deleted at once and the user can't be deleted." username_change_period: "The number of days after registration that accounts can change their username (0 to disallow username change)." email_editable: "Allow users to change their e-mail address after registration." + logout_redirect: "מיקום להכוונת הדפדפן לאחר ההתנתקות לדוגמא: (http://somesite.com/logout)" allow_uploaded_avatars: "אפשרו למשתמשים להעלות אווטרים מותאמים אישית." allow_animated_avatars: "אפשרו למשתמשים להשתמש באווטרים מסוג GIF אנימציה. אזהרה: הריצו את האווטרים: רעננו לאחר שינוי הגדרה זו." automatically_download_gravatars: "הורדת גראווטרים למשתמשים בעת יצירת החשבון או שינוי כתובת הדוא\"ל." @@ -802,6 +830,7 @@ he: dominating_topic_minimum_percent: "איזה אחוז מהפרסומים משתמש צריך לייצר בנושא לפני שיקבל תזכורת לגבי שליטת/שתלטנות יתר על הנושא." suppress_uncategorized_badge: "אל תציגו את התג (badge) לנושאים נטולי קטגוריה ברשימת הנושאים." global_notice: "הציגו הודעת אזהרה דחופה כללית לכל המבקרים, החליפו לריק כדי להסתיר אותה (ניתן להשתמש ב-HTML)." + disable_edit_notifications: "ביטול התראות עריכה על ידי משתמש המערכת כאשר 'download_remote_images_to_local' פעיל." enable_names: "אפשרו הצגת שמות מלאים של משתמשים. בטלו אפשרות זו כדי להסתיר את השמות המלאים." display_name_on_posts: "הצגת שמם המלא של משתמשים בפרסומיהם, בנוסף ל@שם_המשתמש שלהם." invites_per_page: "ברירת המחדל, הזמנות המוצגות בעמוד המשתמש." @@ -848,7 +877,9 @@ he: invited_to_private_message: "%{display_username} הזמין אותך להודעה פרטית: %{link}" invitee_accepted: "%{display_username} קיבל את הזמנתך" linked: "%{display_username} קישר אליך ב %{link}" + granted_badge: "הרווחת %{link}" search: + within_post: "#%{post_number} פרסומים של %{username}" types: category: 'קטגוריות' topic: 'תוצאות' @@ -875,18 +906,25 @@ he: closed_enabled: "הנושא הזה נעול. לא ניתן להגיב תגובות חדשות." closed_disabled: "הנושא הזה פתוח. ניתן להגיב תגובות חדשות." autoclosed_enabled_days: - zero: "הנושא הזה ננעל אוטומטית לאחר יום אחד. תגובות חדשות לא מתקבלות." one: "הנושא הזה ננעל אוטומטית לאחר יום אחד. תגובות חדשות לא מתקבלות." - other: "הנושא הזה ננעל אוטומטית לאחר %{count} ימים. תגובות חדשות לא מתקבלות." + other: "נושא זה נסגר באופן אוטומטי לאחר %{count}. לא ניתן להוסיף תגובות חדשות." autoclosed_enabled_hours: - zero: "הנושא הזה ננעל אוטומטית לאחר שעה אחת. תגובות חדשות לא מתקבלות." - one: "הנושא הזה ננעל אוטומטית לאחר שעה אחת. תגובות חדשות לא מתקבלות." - other: "הנושא הזה ננעל אוטומטית לאחר %{count} שעות. תגובות חדשות לא מתקבלות." + one: "נושא זה ננעל לאחר שעה אחת. לא ניתן להוסיף תגובות חדשות." + other: "נושא זה נסגר אוטומטית לאחר %{count}. לא ניתן להוסיף תגובות חדשות." autoclosed_enabled_minutes: - zero: "הנושא הזה ננעל אוטומטית לאחר דקה אחת. תגובות חדשות לא מתקבלות." - one: "הנושא הזה ננעל אוטומטית לאחר דקה אחת. תגובות חדשות לא מתקבלות." + one: "הנושא הזה ננעל אוטומטית לאחר דקה. תגובות חדשות לא מתקבלות." other: "הנושא הזה ננעל אוטומטית לאחר %{count} דקות. תגובות חדשות לא מתקבלות." + autoclosed_enabled_lastpost_days: + one: "נושא זה ננעל אוטומטית לאחר יום אחד מהתגובה האחרונה. תגובות חדשות לא מתקבלות." + other: "נושא זה ננעל אוטומטית לאחר %{count} ימים מהתגובה האחרונה. תגובות חדשות לא מתקבלות." + autoclosed_enabled_lastpost_hours: + one: "נושא זה ננעל אוטומטית לאחר שעה מהתגובה האחרונה. תגובות חדשות לא מתקבלות." + other: "נושא זה ננעל אוטומטית לאחר %{count} שעות מהתגובה האחרונה. תגובות חדשות לא מתקבלות." + autoclosed_enabled_lastpost_minutes: + one: "נושא זה ננעל אוטומטית לאחר דקה מהתגובה האחרונה. תגובות חדשות לא מתקבלות." + other: "נושא זה ננעל אוטומטית לאחר %{count} דקות מהתגובה האחרונה. תגובות חדשות לא מתקבלות." autoclosed_disabled: "הנושא הזה נפתח. ניתן להגיב תגובות חדשות." + autoclosed_disabled_lastpost: "הנושא הזה נפתח. ניתן להגיב תגובות חדשות." pinned_enabled: "הנושא הזה ננעץ. הוא יופיע בראש הקטגוריה שלו עד שיוסר מנעיצה על ידי מנהל או שכפתור נקה נעיצות נלחץ." pinned_disabled: "הנושא הזה הוסר מנעיצה. הוא לא יוצג יותר בראש הקטגוריה שלו." pinned_globally_enabled: "הנושא הזה עכשיו נעוץ גלובאלית. הוא יופיע בראש הקטגוריה שלו ובראש כל רשימות הנושאים עד שיוסר מנעיצה על ידי מנהל או שכפתור הנקה נעיצות יילחץ." @@ -911,7 +949,9 @@ he: new_registrations_disabled: "הוספת חשבונות חדשים אינה מותרת בעת זו." password_too_long: "סיסמאות מוגבלות ל-200 תווים." missing_user_field: "לא מילאת את כל שדות המשתמש/ת" + close_window: "האימות הושלם. סגרו חלון זה כדי להמשיך." user: + no_accounts_associated: "אין חשבונות מקושרים." username: short: "חייבים להיות לפחות %{min} תווים" long: "נדרשים לא יותר מ-%{max} תווים" @@ -958,6 +998,7 @@ he: הודעה זו נשלחה על ידי משתמש/ת מוכרים כך שאינך חייב/ת להתחבר. invite_password_instructions: + subject_template: "צרו סיסמא עבור חשבון ה-%{site_name} שלכם." text_body_template: | תודה על שאישרת את ההזמנה שלך אל %{site_name} - ברוכים הבאים! diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index 0a47b26dcc..fb90ac21d8 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -783,18 +783,6 @@ it: archived_disabled: "Questo argomento non è più archiviato. Non è più congelato e può essere modificato." closed_enabled: "Questo argomento è stato chiuso. Non è più possibile inviare risposte." closed_disabled: "Questo argomento è stato aperto. È di nuovo possibile rispondere." - autoclosed_enabled_days: - zero: "Questo argomento è stato chiuso automaticamente dopo 1 giorno. Non sono più permesse altre risposte." - one: "Questo argomento è stato chiuso automaticamente dopo 1 giorno. Non sono più permesse risposte." - other: "Questo argomento è stato chiuso automaticamente dopo %{count} giorni. Non sono più permesse altre risposte." - autoclosed_enabled_hours: - zero: "Questo argomento è stato chiuso automaticamente dopo 1 ora. Non sono più permesse altre risposte." - one: "Questo argomento è stato chiuso automaticamente dopo 1 ora. Non sono più permesse altre risposte." - other: "Questo argomento è stato chiuso automaticamente dopo %{count} ore. Non sono più permesse altre risposte." - autoclosed_enabled_minutes: - zero: "Questo argomento è stato chiuso automaticamente dopo 1 minuto. Non sono più permesse altre risposte." - one: "Questo argomento è stato chiuso automaticamente dopo 1 minuto. Non sono più permesse altre risposte." - other: "Questo argomento è stato chiuso automaticamente dopo %{count} minuti. Non sono più permesse altre risposte." autoclosed_disabled: "Questo argomento è ora aperto. Sono permesse altre risposte." pinned_enabled: "Questo argomento è stato puntato. Sarà mostrato in cima alla sua categoria finché non sarà spuntato da un membro dello staff - e allora varrà per tutti - oppure dai singoli utenti - e varrà solo per loro stessi." pinned_disabled: "Questo argomento è stato spuntato. Non sarà più mostrato in cima alla sua categoria." diff --git a/config/locales/server.ja.yml b/config/locales/server.ja.yml index ce5600071c..2c8b606694 100644 --- a/config/locales/server.ja.yml +++ b/config/locales/server.ja.yml @@ -497,18 +497,6 @@ ja: archived_disabled: "このトピックはアーカイブ解除されました。凍結解除され、変更可能になりました。" closed_enabled: "このトピックは終了しました。新たに回答を投稿することはできません。" closed_disabled: "このトピックはオープンされました。新たに回答を投稿することができます。" - autoclosed_enabled_days: - zero: "このトピックは1日が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - one: "このトピックは1日が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - other: "このトピックは%{count}日が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - autoclosed_enabled_hours: - zero: "このトピックは1時間が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - one: "このトピックは1時間が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - other: "このトピックは%{count}時間が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - autoclosed_enabled_minutes: - zero: "このトピックは1分が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - one: "このトピックは1分が経過したので自動的に終了しました。新たに回答を投稿することはできません。" - other: "このトピックは%{count}分が経過したので自動的に終了しました。新たに回答を投稿することはできません。" autoclosed_disabled: "このトピックは再オープンされました。新しい回答が投稿できるようになりました。。" pinned_disabled: "このトピックのピン留めが解除されました。以後、カテゴリのトップに表示されません。" login: diff --git a/config/locales/server.ko.yml b/config/locales/server.ko.yml index 70ff939087..00d648a8bb 100644 --- a/config/locales/server.ko.yml +++ b/config/locales/server.ko.yml @@ -760,18 +760,6 @@ ko: archived_disabled: "이 토픽의 보관이 풀렸고 이제 변경하실 수 있습니다." closed_enabled: "이 토픽은 이제 닫혔습니다. 새로운 답글을 다실 수 없습니다." closed_disabled: "이 토픽은 이제 열렸습니다. 새로운 답글을 다실 수 있습니다." - autoclosed_enabled_days: - zero: "이 토픽은 하루 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - one: "이 토픽은 하루 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - other: "이 토픽은 %{count}일 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - autoclosed_enabled_hours: - zero: "이 토픽은 한 시간 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - one: "이 토픽은 한 시간 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - other: "이 토픽은 %{count} 시간 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - autoclosed_enabled_minutes: - zero: "이 토픽은 1분 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - one: "이 토픽은 1분 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." - other: "이 토픽은 %{count} 분 뒤 자동으로 닫혔습니다. 더이상 답글을 달 수 없습니다." autoclosed_disabled: "이 토픽은 이제 열렸습니다. 새로운 답글을 허용합니다." pinned_disabled: "이 토픽은 이제 고정이 풀렸습니다. 더이상 카테고리 상단에 보이지 않을 것입니다." login: diff --git a/config/locales/server.nl.yml b/config/locales/server.nl.yml index fdcf88f2c8..3d8a2ede17 100644 --- a/config/locales/server.nl.yml +++ b/config/locales/server.nl.yml @@ -711,18 +711,6 @@ nl: archived_disabled: "Deze topic is niet langer gearchiveerd en kan weer veranderd worden." closed_enabled: "Deze topic is nu gesloten. Nieuwe reacties worden niet langer geaccepteerd." closed_disabled: "Deze topic is nu geopend. Nieuwe reacties worden weer geaccepteerd." - autoclosed_enabled_days: - zero: "Deze topic is automatisch na 1 dag gesloten. Reageren is niet meer mogelijk." - one: "Deze topic is automatisch na 1 dag gesloten. Reageren is niet meer mogelijk." - other: "Deze topic is automatisch na %{count} dagen gesloten. Reageren is niet meer mogelijk." - autoclosed_enabled_hours: - zero: "Deze topic is automatisch na 1 uur gesloten. Reageren is niet meer mogelijk." - one: "Deze topic is automatisch na 1 uur gesloten. Reageren is niet meer mogelijk." - other: "Deze topic is automatisch na %{count} uren gesloten. Reageren is niet meer mogelijk." - autoclosed_enabled_minutes: - zero: "Deze topic is automatisch na 1 minuut gesloten. Reageren is niet meer mogelijk." - one: "Deze topic is automatisch na 1 minuut gesloten. Reageren is niet meer mogelijk." - other: "Deze topic is automatisch na %{count} minuten gesloten. Reageren is niet meer mogelijk." autoclosed_disabled: "Deze topic is geopend. Reageren is mogelijk." pinned_enabled: "Deze topic is nu vastgepind en zal bovenaan de categorie verschijnen totdat de pin door de staf voor iedereen of een gebruiker voor zichzelf verwijderd wordt." pinned_disabled: "Deze topic is niet langer gepind en zal niet meer bovenaan de lijst van topics in zijn categorie staan." diff --git a/config/locales/server.pl_PL.yml b/config/locales/server.pl_PL.yml index 2999b10392..200d235be7 100644 --- a/config/locales/server.pl_PL.yml +++ b/config/locales/server.pl_PL.yml @@ -705,18 +705,6 @@ pl_PL: archived_disabled: "Temat został przywrócony z archiwum. Został odblokowany i może ponownie być zmieniany." closed_enabled: "Temat został zamknięty. Dodawanie nowych odpowiedzi nie jest możliwe." closed_disabled: "Temat został otwarty. Dodawanie odpowiedzi jest ponownie możliwe." - autoclosed_enabled_days: - zero: "Temat został automatycznie zamknięty po 1 dniu. Dodawanie nowych odpowiedzi nie jest możliwe." - one: "Temat został automatycznie zamknięty po 1 dniu. Dodawanie nowych odpowiedzi nie jest możliwe." - other: "Temat został automatycznie zamknięty po %{count} dniach. Dodawanie nowych odpowiedzi nie jest możliwe." - autoclosed_enabled_hours: - zero: "Temat został automatycznie zamknięty po 1 godzinie. Dodawanie nowych odpowiedzi nie jest możliwe." - one: "Temat został automatycznie zamknięty po 1 godzinie. Dodawanie nowych odpowiedzi nie jest możliwe." - other: "Temat został automatycznie zamknięty po %{count} godzinach. Dodawanie nowych odpowiedzi nie jest możliwe." - autoclosed_enabled_minutes: - zero: "Temat został automatycznie zamknięty po 1 minucie. Dodawanie nowych odpowiedzi nie jest możliwe." - one: "Temat został automatycznie zamknięty po 1 minucie. Dodawanie nowych odpowiedzi nie jest możliwe." - other: "Temat został automatycznie zamknięty po %{count} minutach. Dodawanie nowych odpowiedzi nie jest możliwe." autoclosed_disabled: "Temat został otwarty. Dodawanie odpowiedzi jest ponownie możliwe." pinned_enabled: "Temat został przypięty. Będzie pojawiać się na początku swojej kategorii dopóki nie zostanie odpięty przez obsługę lub prywatnie przez użytkownika." pinned_disabled: "Temat został odpięty. Już nie będzie pojawiać się na początku swojej kategorii." diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index 760e0029e5..009ffdc177 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -725,18 +725,6 @@ pt_BR: archived_disabled: "Este tópico foi agora desarquivado. Já não está congelado, e pode ser alterado." closed_enabled: "Este tópico está agora fechado. Novas respostas não são aceitas." closed_disabled: "Este tópico está agora aberto. Novas respostas serão aceitas." - autoclosed_enabled_days: - zero: "Este tópico foi fechado automaticamente após 1 dia. Novas respostas não são mais permitidas." - one: "Este tópico foi fechado automaticamente após 1 dia. Novas respostas não são mais permitidas." - other: "Este tópico foi fechado automaticamente após %{count} dias. Novas respostas não são mais permitidas." - autoclosed_enabled_hours: - zero: "Este tópico foi fechado automaticamente após 1 hora. Novas respostas não são mais permitidas." - one: "Este tópico foi fechado automaticamente após 1 hora. Novas respostas não são mais permitidas." - other: "Este tópico foi fechado automaticamente após %{count} horas. Novas respostas não são mais permitidas." - autoclosed_enabled_minutes: - zero: "Este tópico foi fechado automaticamente após 1 minuto. Novas respostas não são mais permitidas." - one: "Este tópico foi fechado automaticamente após 1 minuto. Novas respostas não são mais permitidas." - other: "Este tópico foi fechado automaticamente após %{count} minutos. Novas respostas não são mais permitidas." autoclosed_disabled: "Este tópico está aberto agora. Novas respostas estão permitidas." pinned_enabled: "Este tópico está agora afixado. Irá aparecer no topo das suas categorias" pinned_disabled: "Este tópico deixou de estár afixado. Não irá mais aparecer no topo das suas categorias." diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index edf128589b..a1b63d9999 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -845,18 +845,6 @@ ru: archived_disabled: "Эта тема разархивирована. Она более не заморожена, и может быть изменена." closed_enabled: "Эта тема закрыта. В ней больше нельзя отвечать." closed_disabled: "Эта тема открыта. В ней можно отвечать." - autoclosed_enabled_days: - zero: "Эта тема была автоматически закрыта через 1 день. В ней больше нельзя отвечать." - one: "Эта тема была автоматически закрыта через 1 день. В ней больше нельзя отвечать." - other: "Эта тема была автоматически закрыта спустя %{count} дней. В ней больше нельзя отвечать." - autoclosed_enabled_hours: - zero: "Эта тема была автоматически закрыта через 1 час. В ней больше нельзя отвечать." - one: "Эта тема была автоматически закрыта через 1 час. В ней больше нельзя отвечать." - other: "Эта тема была автоматически закрыта спустя %{count} часов. В ней больше нельзя отвечать." - autoclosed_enabled_minutes: - zero: "Эта тема была автоматически закрыта через 1 минуту. В ней больше нельзя отвечать." - one: "Эта тема была автоматически закрыта через 1 минуту. В ней больше нельзя отвечать." - other: "Эта тема была автоматически закрыта спустя %{count} минут. В ней больше нельзя отвечать." autoclosed_disabled: "Эта тема открыта. В ней можно отвечать." pinned_enabled: "Данная тема прилеплена. Тема будет находиться наверху списка тем раздела, пока кто нибудь из администраторов не открепит ее." pinned_disabled: "Эта тема отлеплена. Она больше не будет отображаться наверху списка тем раздела." diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index 934cbf1b7a..44b9d0ed86 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -20,15 +20,41 @@ sv: posts: "inlägg" loading: "Laddar" powered_by_html: 'Drivs av Discourse, visas bäst med JavaScript påslaget' + log_in: "Logga in" via: "%{username} via %{site_name}" is_reserved: "är reserverat" purge_reason: "Automatiskt borttagen på grund av att vara gammal och ej verifierad" + disable_remote_images_download_reason: "Fjärrbilds nedladdning är inaktiverad eftersom det inte fanns tillräckligt mycket lagringsutrymme tillgängligt." errors: messages: + too_long_validation: "är begränsad till %{max} karaktärer; du skrev in %{length}. " invalid_boolean: "Ogiltigt boolean." taken: "används redan" embed: load_from_remote: "Det uppstod ett fel vid laddning av artikeln." + bulk_invite: + file_should_be_csv: "Den uppladdade filen ska vara i csv eller txt format." + backup: + backup_file_should_be_tar_gz: "Backup filen ska vara ett .tar.gz arkiv." + not_enough_space_on_disk: "Det finns inte tillräckligt mycket utrymme på disken för att ladda upp denna backup." + not_logged_in: "Du måste vara inloggad för att göra detta." + read_only_mode_enabled: "Webbplatsen är i read only läge. Interaktioner är inaktiverade." + too_many_replies: + one: "Vi är ledsna men nya användare är tillfälligt begränsade till 1 svar i samma diskussion." + other: "Vi är ledsna men nya användare är tillfälligt begränsade till &{count} svar i samma ämne." + embed: + start_discussion: "Starta Discussion" + continue: "Fortsätt Discussion" + more_replies: + one: "1 till svar" + other: "%{count} till svar" + loading: "Laddar Discussion..." + permalink: "Permalänk" + imported_from: "Detta är ett syskon diskussions ämne för det ursprungliga inlägget på %{link}" + in_reply_to: "▶ %{username}" + replies: + one: "1 svar" + other: "%{count} svar" too_many_mentions: zero: "Tyvärr, du kan inte omnämna andra användare." one: "Tyvärr, du kan bara omnämna en annan användare i ett inlägg." @@ -50,26 +76,35 @@ sv: one: "Tyvärr, besökare kan bara ha en länk i ett inlägg." other: "Tyvärr, besökare kan bara ha %{count} länkar i ett inlägg." spamming_host: "Tyvärr, du kan inte posta en länk till det värdnamnet." + user_is_suspended: "Avstängda användare är inte tillåtna att göra inlägg" just_posted_that: "är för likt det du nyligen postade" has_already_been_used: "har redan använts" invalid_characters: "innehåller otillåtna tecken" is_invalid: "är otillåtet; försök att vara lite mer beskrivande" next_page: "nästa sida →" + prev_page: "← föregående sida" + page_num: "Sida %{num}" topics_in_category: "Trådar i kategorin '%{category}'" rss_posts_in_topic: "RSS-flöde för '%{topic}'" rss_topics_in_category: "RSS-flöde för trådar i kategorin '%{category}'" author_wrote: "%{author} skrev:" + num_posts: "Inlägg:" + num_participants: "Deltagare" + read_full_topic: "Läs hela ämnet" private_message_abbrev: "PM" rss_description: latest: "Senaste trådar" hot: "Heta trådar" + too_late_to_edit: "Inlägget skapades för långt tillbaka i tiden. Det kan inte lägre redigeras eller tas bort." groups: errors: can_not_modify_automatic: "Du kan inte modifiera en automatisk grupp" default_names: + everyone: "alla" admins: "administratörer" moderators: "moderatorer" staff: "personal" + trust_level_0: "trust_level_0" trust_level_1: "trust_level_1" trust_level_2: "trust_level_2" trust_level_3: "trust_level_3" @@ -78,12 +113,24 @@ sv: until_posts: one: "1 inlägg" other: "%{count} inlägg" + new-topic: | + Välkommen till %{site_name} — **tack för att du startar en ny konversation!** + + - Beskriver titeln ditt ämne? Är titeln intressant? + + - Vad handlar det om? Vem är intresserad av det? Varför spelar det roll? vilken slags respons hoppas du på att få av communityn? + + - Inkludera bra ord att söka på i ditt ämne så andra kan *hitta* det. För att kategorisera ditt ämne med relaterade ämne, välj en kategori. + + Vill du veta mer, [läs våra community riktlinjer](/guidelines). Det här fältet dyker bara upp för din första %{education_posts_text}. activerecord: attributes: category: name: "Kategorinamn" post: raw: "Inlägg" + user_profile: + bio_raw: "Om mig" errors: messages: is_invalid: "är ogiltig; försök att vara lite mer beskrivande" @@ -95,11 +142,14 @@ sv: cant_send_pm: "Tyvärr, du kan inte skicka ett privat meddelande till den användaren." user: attributes: + password: + common: "är ett av de 10000 vanligaste lösenorden. Vänligen använd ett säkrare lösenord." ip_address: signup_not_allowed: "Registration är inte tillåtet från detta konto." user_profile: no_info_me: "
Din profils Om Mig-fält är för närvarande tomt, skulle du vilja fylla i det?
" no_info_other: "
%{name} har inte skrivit någonting i dess profils Om Mig-fält ännu
" + vip_category_name: "Lounge" category: post_template: "%{replace_paragraph}\n\nUse the following paragraphs for a longer description, as well as to establish any category guidelines or rules.\n\nSome things to consider in any discussion replies below:\n\n- What is this category for? Why should people select this category for their topic?\n\n- How is this different than the other categories we already have?\n\n- Do we need this category?\n\n- Should we merge this with another category, or split it into more categories?\n" trust_levels: @@ -192,16 +242,22 @@ sv: password_reset: no_token: "Tyvärr, din token har gått ut. Var god försök återställa ditt lösenord igen." choose_new: "Var god välj ett nytt lösenord" + choose: "Var god välj ett nytt lösenord" update: 'Uppdatera Lösenord' + save: 'Välj lösenord' title: 'Återställ Lösenord' success: "Du har lyckats med att byta ditt lösenord och är nu inloggad." success_unapproved: "Du lyckades med att byta ditt lösenord." continue: "Continue to %{site_name}" change_email: confirmed: "Din e-post har uppdaterats." + please_continue: "Fortsätt till %{site_name}" error: "Det uppstod ett fel med ändringen av din e-postadress. Adressen kanske redan används?" activation: + action: "Aktivera ditt konto" already_done: "Tyvärr, denna kontoaktiveringslänk är inte längre giltig. Kanske är ditt konto redan aktiverat?" + please_continue: "Ditt nya konto är konfirmerat och du är nu inloggad." + continue_button: "Fortsätt till %{site_name}" welcome_to: "Välkommen till %{site_name}!" approval_required: "En moderator måste manuellt godkänna ditt nya konto innan du kan komma åt detta forum. Du kommer få ett mail när ditt konto har godkänts!" post_action_types: @@ -210,9 +266,11 @@ sv: long_form: 'flagga detta som off-topic' spam: title: 'Spam' + description: 'Detta inlägg är en annons. Den bidrar inte till eller är inte relevant för det aktuella ämnet, utan är promotion.' long_form: 'flagga detta som spam' inappropriate: title: 'Olämpligt' + description: 'Detta inläggs innehåll är inkluderar saker som som en förnuftig person skulle betrakta som stötande, kränkande eller överträdelse av våra community riktlinjerr.' long_form: 'flagga detta som olämpligt' notify_user: title: 'Notifiera {{username}}' @@ -235,6 +293,15 @@ sv: title: 'Rösta' description: 'Rösta för detta inlägg' long_form: 'röstade för detta inlägg' + topic_flag_types: + spam: + title: 'Spam' + long_form: 'flaggad som spam' + inappropriate: + title: 'Olämpligt' + long_form: 'flaggad som olämplig' + notify_moderators: + title: 'Notifiera moderatorer' archetypes: regular: title: "Vanlig Tråd" diff --git a/config/locales/server.uk.yml b/config/locales/server.uk.yml index b23a6fb58b..264573f2f2 100644 --- a/config/locales/server.uk.yml +++ b/config/locales/server.uk.yml @@ -476,18 +476,6 @@ uk: archived_disabled: "Цю тему тепер розархівовано. Вона більше не заморожена, і її можна змінювати." closed_enabled: "Цю тему закрито. Нові відповіді заборонено." closed_disabled: "Цю тему відкрито. Нові відповіді дозволено." - autoclosed_enabled_days: - zero: "Ця тема була автоматично закрита через 1 день. Нові відповіді більше не дозволяються." - one: "This topic was automatically closed after 1 day. New replies are no longer allowed." - other: "This topic was automatically closed after 1 day. New replies are no longer allowed." - autoclosed_enabled_hours: - zero: "Ця тема була автоматично закрита через 1 годину. Нові відповіді більше не дозволяються." - one: "This topic was automatically closed after 1 hour. New replies are no longer allowed." - other: "This topic was automatically closed after 1 hour. New replies are no longer allowed." - autoclosed_enabled_minutes: - zero: "Ця тема була автоматично закрита через 1 хвилину. Нові відповіді більше не дозволяються." - one: "This topic was automatically closed after 1 minute. New replies are no longer allowed." - other: "This topic was automatically closed after 1 minute. New replies are no longer allowed." autoclosed_disabled: "Тепер ця тема відкрита. Дозволено нові відповіді." pinned_disabled: "Тепер цю тему відкріплено. Вона більше не з'являтиметься вгорі своєї категорії." login: diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index f54b472363..bcafa2f5f4 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -603,6 +603,7 @@ zh_CN: summary_posts_required: "在一个主题启用'摘要模式'的最小帖子数量" summary_likes_required: "在一个主题启用'摘要模式'的最小赞的数量" summary_percent_filter: "当用户点击摘要,显示前 % 几的帖子" + summary_max_results: "“概括主题”返回的最大帖子数量" enable_private_messages: "允许信任等级1的用户创建私信或者以私信回复" enable_long_polling: "启用 Message bus 使通知功能可以使用长轮询(long polling)" long_polling_base_url: "长轮询的基本 URL(当用 CDN 分发动态能让时,请设置此至原始拉取地址)例如:http://origin.site.com" @@ -707,7 +708,7 @@ zh_CN: max_flags_per_day: "每个用户每天能标记的数量的最大值。" max_bookmarks_per_day: "每个用户每天能做书签数量的最大值。" max_edits_per_day: "每个用户每天能编辑的次数的最大值。" - max_stars_per_day: "每个用户每天能心标的主题数量的最大值。" + max_stars_per_day: "每个用户每天能星标的主题数量的最大值。" max_topics_per_day: "每个用户每天能创建的主题数量的最大值。" max_private_messages_per_day: "每个用户每天能发私信数量的最大值。" suggested_topics: "在一个主题底部显示的推荐主题的数量。" @@ -903,18 +904,19 @@ zh_CN: closed_enabled: "本主题已关闭,不再接受新的回复。" closed_disabled: "本主题已开放,可以发表新的回复。" autoclosed_enabled_days: - zero: "本主题为创建 1 天后自动关闭,不允许添加新的回复。" - one: "本主题为创建 1 天后自动关闭,不允许添加新的回复。" - other: "本主题为创建 %{count} 天后自动关闭。不允许添加新回复。" + other: "本主题在创建 %{count} 天后自动关闭。不再允许添加新回复。" autoclosed_enabled_hours: - zero: "本主题为创建 1 小时后自动关闭,不允许添加新的回复。" - one: "本主题为创建 1 小时后自动关闭,不允许添加新的回复。" - other: "本主题为创建 %{count} 小时后自动关闭。不允许添加新回复。" + other: "本主题在创建 %{count} 小时后自动关闭。不再允许添加新回复。" autoclosed_enabled_minutes: - zero: "本主题为创建 1 分钟后自动关闭,不允许添加新的回复。" - one: "本主题为创建 1 分钟后自动关闭,不允许添加新的回复。" - other: "本主题为创建 %{count} 分钟后自动关闭。不允许添加新回复。" + other: "本主题在创建 %{count} 分钟后自动关闭。不再允许添加新回复。" + autoclosed_enabled_lastpost_days: + other: "本主题在最后一个回复创建后 %{count} 天后自动关闭。不再允许添加新回复。" + autoclosed_enabled_lastpost_hours: + other: "本主题在最后一个回复创建后 %{count} 小时后自动关闭。不再允许添加新回复。" + autoclosed_enabled_lastpost_minutes: + other: "本主题在最后一个回复创建后 %{count} 分钟后自动关闭。不再允许添加新回复。" autoclosed_disabled: "本主题是开放的,可以添加新的回复。" + autoclosed_disabled_lastpost: "本主题现在开放了。可以添加新的回复。" pinned_enabled: "本主题已置顶,它将始终显示在它所属分类的顶部。可由职员对所有人解除置顶,或者由用户自己取消置顶。" pinned_disabled: "本主题已被解除置顶,它将不再显示在它所属分类的顶部。" pinned_globally_enabled: "本主题已全局置顶,它将始终显示在它所属分类的顶部。可由职员对所有人解除置顶,或者由用户自己取消置顶。" diff --git a/config/locales/server.zh_TW.yml b/config/locales/server.zh_TW.yml index e3ff2c7f79..a8361d8dfc 100644 --- a/config/locales/server.zh_TW.yml +++ b/config/locales/server.zh_TW.yml @@ -629,17 +629,6 @@ zh_TW: archived_disabled: "此討論話題已被解除封存,即不再凍結,可以修改。" closed_enabled: "此討論話題已關閉,不再接受新的回覆。" closed_disabled: "此討論話題已開放,可以發表新的回覆。" - autoclosed_enabled_days: - zero: "本話題在建立 1 天後自動關閉,不允許再增加新的回覆。" - one: "本話題在建立 1 天後自動關閉,不允許再增加新的回覆。" - other: "本話題在建立 %{count} 天後自動關閉,不允許再增加新的回覆。" - autoclosed_enabled_hours: - zero: "本話題在建立 1 小時後自動關閉,不允許再增加新的回覆。" - one: "本話題在建立 1 小時後自動關閉,不允許再增加新的回覆。" - other: "本話題在建立 %{count} 小時後自動關閉,不允許再增加新的回覆。" - autoclosed_enabled_minutes: - zero: "本話題在建立 1 分鐘後自動關閉,不允許再增加新的回覆。" - one: "本話題在建立 1 分鐘後自動關閉,不允許再增加新的回覆。" autoclosed_disabled: "此討論話題已開放,可以發表新的回覆。" pinned_disabled: "此討論話題已被解除置頂,它將不再顯示在它所屬分類的頂部。" pinned_globally_disabled: "本主題已被解除置頂,它將不再置頂於所屬分類" diff --git a/db/migrate/20141030222425_rename_seen_post_count.rb b/db/migrate/20141030222425_rename_seen_post_count.rb new file mode 100644 index 0000000000..dac8d14467 --- /dev/null +++ b/db/migrate/20141030222425_rename_seen_post_count.rb @@ -0,0 +1,5 @@ +class RenameSeenPostCount < ActiveRecord::Migration + def change + rename_column :topic_users, :seen_post_count, :highest_seen_post_number + end +end diff --git a/discourse.sublime-project b/discourse.sublime-project index 16bc37e55d..6ce1a8c6ea 100644 --- a/discourse.sublime-project +++ b/discourse.sublime-project @@ -12,6 +12,7 @@ "file_exclude_patterns": ["*.sqlite3"] }, { "path": "lib" }, + { "path": "plugins" }, { "path": "script" }, { "path": "spec" }, { "path": "test", diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index 6376640e0a..cf20f2103c 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -78,6 +78,7 @@ class AdminUserIndexQuery .includes(:github_user_info) .includes(:google_user_info) .includes(:oauth2_user_info) + .includes(:user_open_ids) .take(100) end end diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index b58bc33797..57af1276d5 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -181,9 +181,9 @@ class CookedPostProcessor informations = "#{original_width}x#{original_height}" informations << " #{number_to_human_size(upload.filesize)}" if upload - a["title"] = filename + a["title"] = img["title"] || filename - meta.add_child create_span_node("filename", filename) + meta.add_child create_span_node("filename", img["title"] || filename) meta.add_child create_span_node("informations", informations) meta.add_child create_span_node("expand") end diff --git a/lib/crawler_detection.rb b/lib/crawler_detection.rb index c1bdc9e75d..d82cf443f6 100644 --- a/lib/crawler_detection.rb +++ b/lib/crawler_detection.rb @@ -1,5 +1,5 @@ module CrawlerDetection def self.crawler?(user_agent) - !/Googlebot|Mediapartners|AdsBot|curl|Twitterbot|facebookexternalhit|bingbot|Baiduspider/.match(user_agent).nil? + !/Googlebot|Mediapartners|AdsBot|curl|Twitterbot|facebookexternalhit|bingbot|Baiduspider|ia_archiver/.match(user_agent).nil? end end diff --git a/lib/guardian.rb b/lib/guardian.rb index 5fab43dfd4..f0998e0577 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -26,6 +26,8 @@ class Guardian def email; nil; end end + attr_accessor :can_see_emails + def initialize(user=nil) @user = user.presence || AnonymousUser.new end @@ -243,6 +245,10 @@ class Guardian (is_staff? || target.is_a?(Group) || !target.suspended?) end + def can_see_emails? + @can_see_emails + end + private def is_my_own?(obj) diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index bedda0757a..a3e008d734 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -11,7 +11,7 @@ class Plugin::Instance def self.find_all(parent_path) [].tap { |plugins| # also follows symlinks - http://stackoverflow.com/q/357754 - Dir["#{parent_path}/**/*/**/plugin.rb"].each do |path| + Dir["#{parent_path}/**/*/**/plugin.rb"].sort.each do |path| source = File.read(path) metadata = Plugin::Metadata.parse(source) plugins << self.new(metadata, path) diff --git a/lib/post_creator.rb b/lib/post_creator.rb index ae1c6fc178..af0a1c3e7f 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -298,7 +298,7 @@ class PostCreator @topic.id, posted: true, last_read_post_number: @post.post_number, - seen_post_count: @post.post_number) + highest_seen_post_number: @post.post_number) # assume it took us 5 seconds of reading time to make a post diff --git a/lib/post_destroyer.rb b/lib/post_destroyer.rb index 2835739554..9bba2444d4 100644 --- a/lib/post_destroyer.rb +++ b/lib/post_destroyer.rb @@ -101,9 +101,6 @@ class PostDestroyer @post.update_flagged_posts_count @post.topic_links.each(&:destroy) end - - # covered by PostRevisor - # @post.publish_change_to_clients! :revised end def user_recovered @@ -113,9 +110,6 @@ class PostDestroyer @post.revise(@user, { raw: @post.revisions.last.modifications["raw"][0] }, force_new_version: true) @post.update_flagged_posts_count end - - # covered by PostRevisor - # @post.publish_change_to_clients! :revised end diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index e100f4df0c..cb616389b3 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -67,7 +67,7 @@ class PostRevisor publish_changes grant_badge - @post_successfully_saved && @topic_successfully_saved + successfully_saved_post_and_topic end def cleanup_whitespaces(raw) @@ -93,11 +93,7 @@ class PostRevisor end def revise_post - if should_create_new_version? - revise_and_create_new_version - else - revise - end + should_create_new_version? ? revise_and_create_new_version : revise end def should_create_new_version? @@ -178,11 +174,9 @@ class PostRevisor end def create_or_update_revision - if @version_changed - create_revision - else - update_revision - end + # don't create an empty revision if something failed + return unless successfully_saved_post_and_topic + @version_changed ? create_revision : update_revision end def create_revision @@ -305,4 +299,8 @@ class PostRevisor BadgeGranter.queue_badge_grant(Badge::Trigger::PostRevision, post: @post) end + def successfully_saved_post_and_topic + @post_successfully_saved && @topic_successfully_saved + end + end diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index 6a1625b2df..2c4622815c 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -241,6 +241,11 @@ module PrettyText end def self.excerpt(html, max_length, options={}) + # TODO: properly fix this HACK in ExcerptParser without introducing XSS + doc = Nokogiri::HTML.fragment(html) + strip_image_wrapping(doc) + html = doc.to_html + ExcerptParser.get_excerpt(html, max_length, options) end diff --git a/lib/topics_bulk_action.rb b/lib/topics_bulk_action.rb index fbdea22057..71defb0e3d 100644 --- a/lib/topics_bulk_action.rb +++ b/lib/topics_bulk_action.rb @@ -22,7 +22,7 @@ class TopicsBulkAction def dismiss_posts sql = " UPDATE topic_users tu - SET seen_post_count = t.posts_count , last_read_post_number = highest_post_number + SET highest_seen_post_number = t.highest_post_number , last_read_post_number = highest_post_number FROM topics t WHERE t.id = tu.topic_id AND tu.user_id = :user_id AND t.id IN (:topic_ids) " diff --git a/lib/unread.rb b/lib/unread.rb index 04c2002602..a5a062d319 100644 --- a/lib/unread.rb +++ b/lib/unread.rb @@ -10,17 +10,17 @@ class Unread def unread_posts return 0 if do_not_notify?(@topic_user.notification_level) - result = ((@topic_user.seen_post_count||0) - (@topic_user.last_read_post_number||0)) + result = ((@topic_user.highest_seen_post_number||0) - (@topic_user.last_read_post_number||0)) result = 0 if result < 0 result end def new_posts - return 0 if @topic_user.seen_post_count.blank? + return 0 if @topic_user.highest_seen_post_number.blank? return 0 if do_not_notify?(@topic_user.notification_level) return 0 if (@topic_user.last_read_post_number||0) > @topic.highest_post_number - new_posts = (@topic.highest_post_number - @topic_user.seen_post_count) + new_posts = (@topic.highest_post_number - @topic_user.highest_seen_post_number) new_posts = 0 if new_posts < 0 return new_posts end diff --git a/lib/version.rb b/lib/version.rb index e35de04db6..a496adb9db 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -3,9 +3,9 @@ module Discourse unless defined? ::Discourse::VERSION module VERSION #:nodoc: MAJOR = 1 - MINOR = 1 + MINOR = 2 TINY = 0 - PRE = 'beta8' + PRE = 'beta1' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/plugins/emoji/assets/javascripts/emoji.js.erb b/plugins/emoji/assets/javascripts/emoji.js.erb index 8e3ce36003..147f91ca34 100644 --- a/plugins/emoji/assets/javascripts/emoji.js.erb +++ b/plugins/emoji/assets/javascripts/emoji.js.erb @@ -1,9 +1,22 @@ (function() { var emoji = <%= Dir.glob(File.expand_path("../../../public/images/*.png", __FILE__)).map{|f| File.basename(f).split(".")[0]}.inspect %>; + var _extendedEmoji = {}; + Discourse.Dialect.registerEmoji = function(code, url) { + _extendedEmoji[code] = url; + }; + + function urlFor(code) { + var url = _extendedEmoji[code]; + if (!url && emoji.indexOf(code) !== -1) { + url = Discourse.getURL('/plugins/emoji/images/' + code + '.png'); + } + return url; + } + function imageFor(code) { - if (emoji.indexOf(code) !== -1) { - var url = Discourse.getURL('/plugins/emoji/images/' + code + '.png'); + var url = urlFor(code); + if (url) { return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; } } @@ -115,49 +128,55 @@ "{{#each options}}" + "
  • " + "" + - " " + - "{{this}}" + + " " + + "{{code}}" + "
  • " + "{{/each}}" + "" + ""); + var toSearch = emoji.concat(Object.keys(_extendedEmoji)); $('#wmd-input').autocomplete({ template: template, key: ":", - transformComplete: function(v){ return v + ":"; }, + transformComplete: function(v){ return v.code + ":"; }, dataSource: function(term){ + return new Ember.RSVP.Promise(function(resolve) { + var full = ":" + term; + term = term.toLowerCase(); - var full = ":" + term; - term = term.toLowerCase(); - - if (term === "") { - return Ember.RSVP.resolve(["smile", "smiley", "wink", "sunny", "blush"]); - } - - if (translations[full]) { - return Ember.RSVP.resolve([translations[full]]); - } - - var options = []; - var i; - for (i=0; i < emoji.length; i++) { - if (emoji[i].indexOf(term) === 0) { - options.push(emoji[i]); - if(options.length > 4) { break; } + if (term === "") { + return resolve(["smile", "smiley", "wink", "sunny", "blush"]); } - } - if (options.length <= 4) { - for (i=0; i < emoji.length; i++) { - if (emoji[i].indexOf(term) > 0) { - options.push(emoji[i]); + if (translations[full]) { + return resolve([translations[full]]); + } + + var options = []; + var i; + for (i=0; i < toSearch.length; i++) { + if (toSearch[i].indexOf(term) === 0) { + options.push(toSearch[i]); if(options.length > 4) { break; } } } - } - return Ember.RSVP.resolve(options); + if (options.length <= 4) { + for (i=0; i < toSearch.length; i++) { + if (toSearch[i].indexOf(term) > 0) { + options.push(toSearch[i]); + if(options.length > 4) { break; } + } + } + } + + return resolve(options); + }).then(function(list) { + return list.map(function(i) { + return {code: i, src: urlFor(i)}; + }); + }); } }); }); diff --git a/plugins/poll/assets/javascripts/initializers/poll.js.es6 b/plugins/poll/assets/javascripts/initializers/poll.js.es6 index 2e7e2eaa6b..c55cac7eca 100644 --- a/plugins/poll/assets/javascripts/initializers/poll.js.es6 +++ b/plugins/poll/assets/javascripts/initializers/poll.js.es6 @@ -9,9 +9,12 @@ var Poll = Discourse.Model.extend({ this.updateFromJson(this.get('post.poll_details')); }.observes('post.poll_details'), - fetchNewPostDetails: function() { - this.get('post.topic.postStream').triggerChangedPost(this.get('post.id'), this.get('post.topic.updated_at')); - }.observes('post.topic.title'), + fetchNewPostDetails: Discourse.debounce(function() { + var self = this; + Discourse.debounce(function() { + self.get('post.topic.postStream').triggerChangedPost(self.get('post.id'), self.get('post.topic.updated_at')); + }, 500); + }).observes('post.topic.title'), updateFromJson: function(json) { var selectedOption = json["selected"]; @@ -24,8 +27,8 @@ var Poll = Discourse.Model.extend({ checked: (option === selectedOption) })); }); - this.set('options', options); + this.set('options', options); this.set('closed', json.closed); }, diff --git a/script/import_scripts/base.rb b/script/import_scripts/base.rb index 9016dea7ce..09d0c805b6 100644 --- a/script/import_scripts/base.rb +++ b/script/import_scripts/base.rb @@ -364,6 +364,10 @@ class ImportScripts::Base new_category end + def created_post(post) + # override if needed + end + # Iterates through a collection of posts to be imported. # It can create topics and replies. # Attributes will be passed to the PostCreator. @@ -396,6 +400,8 @@ class ImportScripts::Base url: new_post.url, } + created_post(new_post) + created += 1 else skipped += 1 diff --git a/script/import_scripts/bespoke_1.rb b/script/import_scripts/bespoke_1.rb new file mode 100644 index 0000000000..e91a001a57 --- /dev/null +++ b/script/import_scripts/bespoke_1.rb @@ -0,0 +1,284 @@ +# bespoke importer for a customer, feel free to borrow ideas +# +# +require 'csv' +require File.expand_path(File.dirname(__FILE__) + "/base.rb") + +# Call it like this: +# RAILS_ENV=production bundle exec ruby script/import_scripts/bespoke_1.rb +class ImportScripts::Bespoke < ImportScripts::Base + + BATCH_SIZE = 1000 + + def initialize(path) + @path = path + super() + @bbcode_to_md = true + + puts "loading post mappings..." + @post_number_map = {} + Post.pluck(:id, :post_number).each do |post_id, post_number| + @post_number_map[post_id] = post_number + end + end + + def created_post(post) + @post_number_map[post.id] = post.post_number + super + end + + def execute + #import_users + #import_categories + import_posts + + end + + class RowResolver + def load(row) + @row = row + end + + def self.create(cols) + Class.new(RowResolver).new(cols) + end + + def initialize(cols) + cols.each_with_index do |col,idx| + self.class.send(:define_method, col) do + @row[idx] + end + end + end + end + + def load_user_batch!(users, offset, total) + if users.length > 0 + create_users(users, offset: offset, total: total) do |user| + user + end + users.clear + end + end + + def csv_parse(name) + filename = "#{@path}/#{name}.csv" + first = true + row = nil + + current_row = ""; + double_quote_count = 0 + + File.open(filename).each_line do |line| + + # escaping is mental here + line.gsub!(/\\(.{1})/){|m| m[-1] == '"'? '""': m[-1]} + line.strip! + + current_row << "\n" unless current_row.empty? + current_row << line + + double_quote_count += line.scan('"').count + + if double_quote_count % 2 == 1 + next + end + + raw = begin + CSV.parse(current_row) + rescue CSV::MalformedCSVError => e + puts e.message + puts "*" * 100 + puts "Bad row skipped, line is: #{line}" + puts + puts current_row + puts + puts "double quote count is : #{double_quote_count}" + puts "*" * 100 + + current_row = "" + double_quote_count = 0 + next + end[0] + + if first + row = RowResolver.create(raw) + + current_row = "" + double_quote_count = 0 + first = false + next + end + + row.load(raw) + + yield row + + current_row = "" + double_quote_count = 0 + end + end + + def total_rows(table) + File.foreach("#{@path}/#{table}.csv").inject(0) {|c, line| c+1} - 1 + end + + def import_users + puts "", "creating users" + + count = 0 + users = [] + + total = total_rows("users") + + csv_parse("users") do |row| + + id = row.id + email = row.email + + # fake it + if row.email.blank? || row.email !~ /@/ + email = SecureRandom.hex << "@domain.com" + end + + name = row.display_name + username = row.key_custom + created_at = DateTime.parse(row.dcreate) + + username = name if username == "NULL" + + users << { + id: id, + email: email, + name: name, + username: username, + created_at: created_at + } + + count += 1 + if count % BATCH_SIZE == 0 + load_user_batch! users, count - users.length, total + end + + end + + load_user_batch! users, count, total + end + + def import_categories + rows = [] + csv_parse("categories") do |row| + rows << {id: row.id, name: row.name, description: row.description} + end + + create_categories(rows) do |row| + row + end + end + + def normalize_raw!(raw) + # purple and #1223f3 + raw.gsub!(/\[color=[#a-z0-9]+\]/i, "") + raw.gsub!(/\[\/color\]/i, "") + raw.gsub!(/\[signature\].+\[\/signature\]/i,"") + raw + end + + def import_post_batch!(posts, topics, offset, total) + create_posts(posts, total: total, offset: offset) do |post| + + mapped = {} + + mapped[:id] = post[:id] + mapped[:user_id] = user_id_from_imported_user_id(post[:user_id]) || -1 + mapped[:raw] = post[:body] + mapped[:created_at] = post[:created_at] + + topic = topics[post[:topic_id]] + + unless topic[:post_id] + mapped[:category] = category_from_imported_category_id(topic[:category_id]).try(:name) + mapped[:title] = post[:title] + topic[:post_id] = post[:id] + else + parent = topic_lookup_from_imported_post_id(topic[:post_id]) + next unless parent + + mapped[:topic_id] = parent[:topic_id] + + reply_to_post_id = post_id_from_imported_post_id(post[:reply_id]) + if reply_to_post_id + reply_to_post_number = @post_number_map[reply_to_post_id] + if reply_to_post_number && reply_to_post_number > 1 + mapped[:reply_to_post_number] = reply_to_post_number + end + end + end + + next if topic[:deleted] or post[:deleted] + + mapped + end + + posts.clear + end + + def import_posts + puts "", "creating topics and posts" + + topic_map = {} + + csv_parse("topics") do |topic| + topic_map[topic.id] = { + id: topic.id, + category_id: topic.forum_category_id, + deleted: topic.is_deleted.to_i == 1, + locked: topic.is_locked.to_i == 1, + pinned: topic.is_pinned.to_i == 1 + } + end + + total = total_rows("posts") + + posts = [] + count = 0 + csv_parse("posts") do |row| + + unless row.dcreate + puts "NO CREATION DATE FOR POST" + p row + next + end + + row = { + id: row.id, + topic_id: row.forum_topic_id, + reply_id: row.reply_id, + user_id: row.user_id, + title: row.title, + body: normalize_raw!(row.body), + deleted: row.is_deleted.to_i == 1, + created_at: DateTime.parse(row.dcreate) + } + posts << row + count+=1 + + if posts.length > 0 && posts.length % BATCH_SIZE == 0 + import_post_batch!(posts, topic_map, count - posts.length, total) + end + end + + import_post_batch!(posts, topic_map, count - posts.length, total) if posts.length > 0 + + exit + end + + +end + +unless ARGV[0] && Dir.exist?(ARGV[0]) + puts "", "Usage:", "", "bundle exec ruby script/import_scripts/bespoke_1.rb DIRNAME", "" + exit 1 +end + +ImportScripts::Bespoke.new(ARGV[0]).perform diff --git a/spec/components/cooked_post_processor_spec.rb b/spec/components/cooked_post_processor_spec.rb index fd8ba3a733..ae90bf3569 100644 --- a/spec/components/cooked_post_processor_spec.rb +++ b/spec/components/cooked_post_processor_spec.rb @@ -98,6 +98,33 @@ describe CookedPostProcessor do end + context "with title" do + + let(:upload) { Fabricate(:upload) } + let(:post) { build(:post_with_large_image_and_title) } + let(:cpp) { CookedPostProcessor.new(post) } + + before do + SiteSetting.max_image_height = 2000 + SiteSetting.create_thumbnails = true + + Upload.expects(:get_from_url).returns(upload) + FastImage.stubs(:size).returns([1000, 2000]) + + # hmmm this should be done in a cleaner way + OptimizedImage.expects(:resize).returns(true) + end + + it "generates overlay information" do + cpp.post_process_images + cpp.html.should match_html '' + cpp.should be_dirty + end + + end + context "topic image" do let(:topic) { build(:topic, id: 1) } diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 6591e726e5..1cdcda9dd6 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -178,7 +178,7 @@ describe PostCreator do topic_user.should be_present topic_user.should be_posted topic_user.last_read_post_number.should == first_post.post_number - topic_user.seen_post_count.should == first_post.post_number + topic_user.highest_seen_post_number.should == first_post.post_number user2 = Fabricate(:coding_horror) user2.user_stat.topic_reply_count.should == 0 diff --git a/spec/components/post_destroyer_spec.rb b/spec/components/post_destroyer_spec.rb index b067653398..7785c2ef6c 100644 --- a/spec/components/post_destroyer_spec.rb +++ b/spec/components/post_destroyer_spec.rb @@ -241,7 +241,7 @@ describe PostDestroyer do end it "sets the second user's last_read_post_number back to 1" do - topic_user.seen_post_count.should == 1 + topic_user.highest_seen_post_number.should == 1 end end diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 0a6857d69c..76e5122121 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -3,6 +3,9 @@ require 'pretty_text' describe PrettyText do + let(:wrapped_image) { "" } + let(:wrapped_image_excerpt) { } + describe "Cooking" do describe "with avatar" do @@ -111,6 +114,10 @@ describe PrettyText do PrettyText.excerpt("
    ", 100).should match_html "[image]" PrettyText.excerpt("spoiler", 100).should match_html "spoiler" end + + it "should remove meta informations" do + PrettyText.excerpt(wrapped_image, 100).should match_html "[image]" + end end it "should have an option to strip links" do @@ -276,10 +283,8 @@ describe PrettyText do strip_image_wrapping(html).should == html end - let(:wrapped_image) { "" } - it "strips the metadata" do - strip_image_wrapping(wrapped_image).should == "
    " + strip_image_wrapping(wrapped_image).should match_html "
    " end end diff --git a/spec/components/topics_bulk_action_spec.rb b/spec/components/topics_bulk_action_spec.rb index 325c6e402f..533c876a27 100644 --- a/spec/components/topics_bulk_action_spec.rb +++ b/spec/components/topics_bulk_action_spec.rb @@ -6,13 +6,17 @@ describe TopicsBulkAction do describe "dismiss_posts" do it "dismisses posts" do post1 = create_post + p = create_post(topic_id: post1.topic_id) create_post(topic_id: post1.topic_id) + PostDestroyer.new(Fabricate(:admin), p).destroy + TopicsBulkAction.new(post1.user, [post1.topic_id], type: 'dismiss_posts').perform! tu = TopicUser.find_by(user_id: post1.user_id, topic_id: post1.topic_id) - tu.last_read_post_number.should == 2 - tu.seen_post_count = 2 + + tu.last_read_post_number.should == 3 + tu.highest_seen_post_number.should == 3 end end diff --git a/spec/components/unread_spec.rb b/spec/components/unread_spec.rb index 45c11cc7f8..61a9b9bc4a 100644 --- a/spec/components/unread_spec.rb +++ b/spec/components/unread_spec.rb @@ -16,19 +16,19 @@ describe Unread do describe 'unread_posts' do it 'should have 0 unread posts if the user has seen all posts' do @topic_user.stubs(:last_read_post_number).returns(13) - @topic_user.stubs(:seen_post_count).returns(13) + @topic_user.stubs(:highest_seen_post_number).returns(13) @unread.unread_posts.should == 0 end it 'should have 6 unread posts if the user has seen all but 6 posts' do @topic_user.stubs(:last_read_post_number).returns(5) - @topic_user.stubs(:seen_post_count).returns(11) + @topic_user.stubs(:highest_seen_post_number).returns(11) @unread.unread_posts.should == 6 end it 'should have 0 unread posts if the user has seen more posts than exist (deleted)' do @topic_user.stubs(:last_read_post_number).returns(100) - @topic_user.stubs(:seen_post_count).returns(13) + @topic_user.stubs(:highest_seen_post_number).returns(13) @unread.unread_posts.should == 0 end end @@ -40,23 +40,23 @@ describe Unread do end it 'returns 0 when the topic is the same length as when you last saw it' do - @topic_user.stubs(:seen_post_count).returns(13) + @topic_user.stubs(:highest_seen_post_number).returns(13) @unread.new_posts.should == 0 end it 'has 3 new posts if the user has read 10 posts' do - @topic_user.stubs(:seen_post_count).returns(10) + @topic_user.stubs(:highest_seen_post_number).returns(10) @unread.new_posts.should == 3 end it 'has 0 new posts if the user has read 10 posts but is not tracking' do - @topic_user.stubs(:seen_post_count).returns(10) + @topic_user.stubs(:highest_seen_post_number).returns(10) @topic_user.stubs(:notification_level).returns(TopicUser.notification_levels[:regular]) @unread.new_posts.should == 0 end it 'has 0 new posts if the user read more posts than exist (deleted)' do - @topic_user.stubs(:seen_post_count).returns(16) + @topic_user.stubs(:highest_seen_post_number).returns(16) @unread.new_posts.should == 0 end diff --git a/spec/controllers/admin/impersonate_controller_spec.rb b/spec/controllers/admin/impersonate_controller_spec.rb index 1bd528d3f3..841e72ff34 100644 --- a/spec/controllers/admin/impersonate_controller_spec.rb +++ b/spec/controllers/admin/impersonate_controller_spec.rb @@ -6,7 +6,6 @@ describe Admin::ImpersonateController do (Admin::ImpersonateController < Admin::AdminController).should == true end - context 'while logged in as an admin' do let!(:admin) { log_in(:admin) } let(:user) { Fabricate(:user) } @@ -21,7 +20,7 @@ describe Admin::ImpersonateController do context 'create' do it 'requires a username_or_email parameter' do - lambda { xhr :put, :create }.should raise_error(ActionController::ParameterMissing) + -> { xhr :put, :create }.should raise_error(ActionController::ParameterMissing) end it 'returns 404 when that user does not exist' do @@ -37,6 +36,11 @@ describe Admin::ImpersonateController do context 'success' do + it "logs the impersonation" do + StaffActionLogger.any_instance.expects(:log_impersonate) + xhr :post, :create, username_or_email: user.username + end + it "changes the current user session id" do xhr :post, :create, username_or_email: user.username session[:current_user_id].should == user.id @@ -58,6 +62,4 @@ describe Admin::ImpersonateController do end - - end diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 2412319fd3..5a3cf0ab03 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -31,7 +31,7 @@ describe Admin::ReportsController do context 'missing report' do before do - Report.expects(:find).with('active').returns(nil) + Report.expects(:find).with('active', instance_of(Hash)).returns(nil) xhr :get, :show, type: 'active' end @@ -42,7 +42,7 @@ describe Admin::ReportsController do context 'a report is found' do before do - Report.expects(:find).with('active').returns(Report.new('active')) + Report.expects(:find).with('active', instance_of(Hash)).returns(Report.new('active')) xhr :get, :show, type: 'active' end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 3482d02201..767dc021f1 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -22,6 +22,27 @@ describe Admin::UsersController do xhr :get, :index ::JSON.parse(response.body).should be_present end + + context 'when showing emails' do + + it "returns email for all the users" do + xhr :get, :index, show_emails: "true" + data = ::JSON.parse(response.body) + data.each do |user| + user["email"].should be_present + end + end + + it "logs an enty for all email shown" do + UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == 0 + + xhr :get, :index, show_emails: "true" + data = ::JSON.parse(response.body) + + UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == data.length + end + + end end describe '.show' do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 2d4b23e0ab..d5e1db7974 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -357,8 +357,8 @@ describe UsersController do expect(JSON.parse(response.body)['active']).to be_falsey - # should save user_created_email in session - session["user_created_email"].should == @user.email + # should save user_created_message in session + session["user_created_message"].should be_present end context "and 'must approve users' site setting is enabled" do @@ -393,8 +393,8 @@ describe UsersController do User.any_instance.expects(:enqueue_welcome_message).with('welcome_user') post_user - # should save user_created_email in session - session["user_created_email"].should == @user.email + # should save user_created_message in session + session["user_created_message"].should be_present end it "shows the 'active' message" do @@ -479,7 +479,7 @@ describe UsersController do json["success"].should == true # should not change the session - session["user_created_email"].should be_blank + session["user_created_message"].should be_blank end end @@ -523,7 +523,7 @@ describe UsersController do json["success"].should_not == true # should not change the session - session["user_created_email"].should be_blank + session["user_created_message"].should be_blank end end diff --git a/spec/fabricators/post_fabricator.rb b/spec/fabricators/post_fabricator.rb index 2ff3bb7921..cab02e02ed 100644 --- a/spec/fabricators/post_fabricator.rb +++ b/spec/fabricators/post_fabricator.rb @@ -76,6 +76,10 @@ Fabricator(:post_with_large_image, from: :post) do cooked '' end +Fabricator(:post_with_large_image_and_title, from: :post) do + cooked '' +end + Fabricator(:post_with_uploads, from: :post) do cooked ' Link diff --git a/spec/fixtures/images/image.svg b/spec/fixtures/images/image.svg new file mode 100644 index 0000000000..b95c0f4a79 --- /dev/null +++ b/spec/fixtures/images/image.svg @@ -0,0 +1,3 @@ + + Discourse + diff --git a/spec/models/email_log_spec.rb b/spec/models/email_log_spec.rb index 789fe9d4a0..4cabf766d5 100644 --- a/spec/models/email_log_spec.rb +++ b/spec/models/email_log_spec.rb @@ -30,7 +30,7 @@ describe EmailLog do it "counts sent emails" do user.email_logs.create(email_type: 'blah', to_address: user.email) user.email_logs.create(email_type: 'blah', to_address: user.email, skipped: true) - described_class.count_per_day.first[1].should == 1 + described_class.count_per_day(1.day.ago, Time.now).first[1].should == 1 end end @@ -49,7 +49,7 @@ describe EmailLog do context "when user's email does not exist email logs" do it "returns nil" do - expect(user.email_logs.last_sent_email_address).should == nil + expect(user.email_logs.last_sent_email_address).to be_nil end end end diff --git a/spec/models/post_timing_spec.rb b/spec/models/post_timing_spec.rb index 3cdedc9ca3..c3694386d2 100644 --- a/spec/models/post_timing_spec.rb +++ b/spec/models/post_timing_spec.rb @@ -18,12 +18,12 @@ describe PostTiming do PostTiming.create!(topic_id: topic_id, user_id: user_id, post_number: post_number, msecs: 0) end - def topic_user(user_id, last_read_post_number, seen_post_count) + def topic_user(user_id, last_read_post_number, highest_seen_post_number) TopicUser.create!( topic_id: topic_id, user_id: user_id, last_read_post_number: last_read_post_number, - seen_post_count: seen_post_count + highest_seen_post_number: highest_seen_post_number ) end @@ -35,9 +35,9 @@ describe PostTiming do timing(3,2) timing(3,3) - tu_one = topic_user(1,1,1) - tu_two = topic_user(2,2,2) - tu_three = topic_user(3,3,3) + _tu_one = topic_user(1,1,1) + _tu_two = topic_user(2,2,2) + _tu_three = topic_user(3,3,3) PostTiming.pretend_read(topic_id, 2, 3) @@ -47,15 +47,15 @@ describe PostTiming do tu = TopicUser.find_by(topic_id: topic_id, user_id: 1) tu.last_read_post_number.should == 1 - tu.seen_post_count.should == 1 + tu.highest_seen_post_number.should == 1 tu = TopicUser.find_by(topic_id: topic_id, user_id: 2) tu.last_read_post_number.should == 3 - tu.seen_post_count.should == 3 + tu.highest_seen_post_number.should == 3 tu = TopicUser.find_by(topic_id: topic_id, user_id: 3) tu.last_read_post_number.should == 3 - tu.seen_post_count.should == 3 + tu.highest_seen_post_number.should == 3 end end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index f8ca3809f1..4fa690804d 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -1181,6 +1181,16 @@ describe Topic do Topic.for_digest(user, 1.year.ago, top_order: true).should == [topic] end + it "doesn't return topics from muted categories" do + user = Fabricate(:user) + category = Fabricate(:category) + topic = Fabricate(:topic, category: category) + + CategoryUser.set_notification_level_for_category(user, CategoryUser.notification_levels[:muted], category.id) + + Topic.for_digest(user, 1.year.ago, top_order: true).should be_blank + end + end describe 'secured' do diff --git a/spec/models/topic_user_spec.rb b/spec/models/topic_user_spec.rb index 382f8e3ef6..c1a62ac4d5 100644 --- a/spec/models/topic_user_spec.rb +++ b/spec/models/topic_user_spec.rb @@ -254,7 +254,7 @@ describe TopicUser do p2 = Fabricate(:post, user: p1.user, topic: p1.topic, post_number: 2) p1.topic.notifier.watch_topic!(p1.user_id) - TopicUser.exec_sql("UPDATE topic_users set seen_post_count=100, last_read_post_number=0 + TopicUser.exec_sql("UPDATE topic_users set highest_seen_post_number=1, last_read_post_number=0 WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: p1.topic_id, user_id: p1.user_id) [p1,p2].each do |p| @@ -265,7 +265,8 @@ describe TopicUser do tu = TopicUser.find_by(user_id: p1.user_id, topic_id: p1.topic_id) tu.last_read_post_number.should == p2.post_number - tu.seen_post_count.should == 2 + tu.highest_seen_post_number.should == 2 + end describe "mailing_list_mode" do diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb index d55b0df42a..573d53191a 100644 --- a/spec/models/upload_spec.rb +++ b/spec/models/upload_spec.rb @@ -15,11 +15,15 @@ describe Upload do let(:user_id) { 1 } let(:url) { "http://domain.com" } - let(:image) { file_from_fixtures("logo.png") } let(:image_filename) { "logo.png" } + let(:image) { file_from_fixtures(image_filename) } let(:image_filesize) { File.size(image) } let(:image_sha1) { Digest::SHA1.file(image).hexdigest } + let(:image_svg_filename) { "image.svg" } + let(:image_svg) { file_from_fixtures(image_svg_filename) } + let(:image_svg_filesize) { File.size(image_svg) } + let(:attachment_path) { __FILE__ } let(:attachment) { File.new(attachment_path) } let(:attachment_filename) { File.basename(attachment_path) } @@ -96,6 +100,27 @@ describe Upload do upload.url.should == url end + context "when svg is authorized" do + + before { SiteSetting.stubs(:authorized_extensions).returns("svg") } + + it "consider SVG as an image" do + store = {} + Discourse.expects(:store).returns(store) + store.expects(:store_upload).returns(url) + + upload = Upload.create_for(user_id, image_svg, image_svg_filename, image_svg_filesize) + + upload.user_id.should == user_id + upload.original_filename.should == image_svg_filename + upload.filesize.should == image_svg_filesize + upload.width.should == 100 + upload.height.should == 50 + upload.url.should == url + end + + end + end context ".get_from_url" do diff --git a/spec/models/user_profile_spec.rb b/spec/models/user_profile_spec.rb index 99c2042770..ad18eb8617 100644 --- a/spec/models/user_profile_spec.rb +++ b/spec/models/user_profile_spec.rb @@ -24,12 +24,12 @@ describe UserProfile do let(:user_profile) { Fabricate.build(:user_profile) } it 'is not valid without user' do - expect(user_profile.valid?).should == false + expect(user_profile.valid?).to be false end it 'is is valid with user' do user_profile.user = Fabricate.build(:user) - expect(user_profile.valid?).should == true + expect(user_profile.valid?).to be true end it "doesn't support really long bios" do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8cf56a5bd2..d2d1d79afe 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -179,7 +179,7 @@ describe User do expect(Post.where(id: @posts.map(&:id))).to be_empty @posts.each do |p| if p.post_number == 1 - expect(Topic.find_by(id: p.topic_id)).should == nil + expect(Topic.find_by(id: p.topic_id)).to be_nil end end end @@ -777,7 +777,7 @@ describe User do expect(found_user).to eq bob found_user = User.find_by_username_or_email('bob1') - expect(found_user).should == nil + expect(found_user).to be_nil found_user = User.find_by_email('bob@Example.com') expect(found_user).to eq bob @@ -786,7 +786,7 @@ describe User do expect(found_user).to eq bob found_user = User.find_by_email('bob') - expect(found_user).should == nil + expect(found_user).to be_nil found_user = User.find_by_username('bOb') expect(found_user).to eq bob diff --git a/vendor/assets/javascripts/jquery.autoellipsis-1.0.10.min.js b/vendor/assets/javascripts/jquery.autoellipsis-1.0.10.min.js new file mode 100644 index 0000000000..ffc82eb436 --- /dev/null +++ b/vendor/assets/javascripts/jquery.autoellipsis-1.0.10.min.js @@ -0,0 +1,23 @@ +/*! + + Copyright (c) 2011 Peter van der Spek + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + */(function(a){function m(){if(!d){d=!0;for(var c in b)a(c).each(function(){var d,e;d=a(this),e=d.data("jqae"),(e.containerWidth!=d.width()||e.containerHeight!=d.height())&&f(d,b[c])});d=!1}}function l(a){b[a]&&(delete b[a],b.length||c&&(window.clearInterval(c),c=undefined))}function k(a,d){b[a]=d,c||(c=window.setInterval(function(){m()},200))}function j(){return this.nodeType===3}function i(b){if(b.contents().length){var c=b.contents(),d=c.eq(c.length-1);if(d.filter(j).length){var e=d.get(0).nodeValue;e=a.trim(e);if(e==""){d.remove();return!0}return!1}while(i(d));if(d.contents().length)return!1;d.remove();return!0}return!1}function h(a){if(a.contents().length){var b=a.contents(),c=b.eq(b.length-1);return c.filter(j).length?c:h(c)}a.append("");var b=a.contents();return b.eq(b.length-1)}function g(b){var c=h(b);if(c.length){var d=c.get(0).nodeValue,e=d.lastIndexOf(" ");e>-1?(d=a.trim(d.substring(0,e)),c.get(0).nodeValue=d):c.get(0).nodeValue="";return!0}return!1}function f(b,c){var d=b.data("jqae");d||(d={});var e=d.wrapperElement;e||(e=b.wrapInner("
    ").find(">div"),e.css({margin:0,padding:0,border:0}));var f=e.data("jqae");f||(f={});var j=f.originalContent;j?e=f.originalContent.clone(!0).data("jqae",{originalContent:j}).replaceAll(e):e.data("jqae",{originalContent:e.clone(!0)}),b.data("jqae",{wrapperElement:e,containerWidth:b.width(),containerHeight:b.height()});var k=b.height(),l=(parseInt(b.css("padding-top"),10)||0)+(parseInt(b.css("border-top-width"),10)||0)-(e.offset().top-b.offset().top),m=!1,n=e;c.selector&&(n=a(e.find(c.selector).get().reverse())),n.each(function(){var b=a(this),d=b.text(),f=!1;if(e.innerHeight()-b.innerHeight()>k+l)b.remove();else{i(b);if(b.contents().length){m&&(h(b).get(0).nodeValue+=c.ellipsis,m=!1);while(e.innerHeight()>k+l){f=g(b);if(!f){m=!0,b.remove();break}i(b);if(b.contents().length)h(b).get(0).nodeValue+=c.ellipsis;else{m=!0,b.remove();break}}c.setTitle=="onEllipsis"&&f||c.setTitle=="always"?b.attr("title",d):c.setTitle!="never"&&b.removeAttr("title")}}})}var b={},c,d=!1,e={ellipsis:"...",setTitle:"never",live:!1};a.fn.ellipsis=function(b,c){var d,g;d=a(this),typeof b!="string"&&(c=b,b=undefined),g=a.extend({},e,c),g.selector=b,d.each(function(){var b=a(this);f(b,g)}),g.live?k(d.selector,g):l(d.selector);return this}})(jQuery) \ No newline at end of file