From c1cd6a27920429badfce70f2a8b367d3d2cb1543 Mon Sep 17 00:00:00 2001
From: Jeff Atwood
Date: Thu, 3 Sep 2015 15:19:56 -0700
Subject: [PATCH 053/224] add links to the too many tracked topics warning
---
config/locales/client.en.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 7f4e13300e9..fd9845690b5 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -957,7 +957,7 @@ en:
current_user: 'go to your user page'
topics:
- too_many_tracked: "Warning: you have too many tracked new and unread topics, clear some using \"Dismiss New\" or \"Dismiss Posts\""
+ too_many_tracked: "Warning: you have too many tracked new and unread topics, clear some using Dismiss New or Dismiss Posts"
bulk:
reset_read: "Reset Read"
delete: "Delete Topics"
From 64d1c911794c2f172ccf8ee66acffb93a3ecae1d Mon Sep 17 00:00:00 2001
From: Neil Lalonde
Date: Thu, 3 Sep 2015 18:34:06 -0400
Subject: [PATCH 054/224] render those links instead of raw html
---
app/assets/javascripts/discourse/templates/discovery/topics.hbs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/assets/javascripts/discourse/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/discovery/topics.hbs
index e95136eacab..6afe9d80f82 100644
--- a/app/assets/javascripts/discourse/templates/discovery/topics.hbs
+++ b/app/assets/javascripts/discourse/templates/discovery/topics.hbs
@@ -1,5 +1,5 @@
{{#if tooManyTracked}}
-
{{i18n 'topics.too_many_tracked'}}
+
{{{i18n 'topics.too_many_tracked'}}}
{{/if}}
{{#if redirectedReason}}
From f019f5a62da1434b3218722bc5af8fbed518162c Mon Sep 17 00:00:00 2001
From: Sam
Date: Fri, 4 Sep 2015 10:33:16 +1000
Subject: [PATCH 055/224] FIX: return 0 if we are unable to count unread/new
topics
---
.../javascripts/discourse/models/topic-tracking-state.js.es6 | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
index 87c256e202e..9a4e9d374f8 100644
--- a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
+++ b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
@@ -236,6 +236,7 @@ const TopicTrackingState = Discourse.Model.extend({
},
countNew(category_id) {
+ if (this.tooManyTracked()) { return(0); }
return _.chain(this.states)
.where(isNew)
.where(topic => topic.category_id === category_id || !category_id)
@@ -256,6 +257,7 @@ const TopicTrackingState = Discourse.Model.extend({
},
countUnread(category_id) {
+ if (this.tooManyTracked()) { return(0); }
return _.chain(this.states)
.where(isUnread)
.where(topic => topic.category_id === category_id || !category_id)
@@ -264,6 +266,7 @@ const TopicTrackingState = Discourse.Model.extend({
},
countCategory(category_id) {
+ if (this.tooManyTracked()) { return(0); }
let sum = 0;
_.each(this.states, function(topic){
if (topic.category_id === category_id) {
From 8bc742304538aa7ac8b5e5c40c031b95cef16d29 Mon Sep 17 00:00:00 2001
From: Sam
Date: Fri, 4 Sep 2015 10:43:26 +1000
Subject: [PATCH 056/224] UX: On first load of notifications don't use cached
data
---
.../discourse/mixins/stale-local-storage.js.es6 | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6 b/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6
index 0d4baae0ca2..d8816c981e6 100644
--- a/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6
+++ b/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6
@@ -1,6 +1,8 @@
import StaleResult from 'discourse/lib/stale-result';
import { hashString } from 'discourse/lib/hash';
+var skipFirst = true;
+
// Mix this in to an adapter to provide stale caching in our key value store
export default {
storageKey(type, findArgs) {
@@ -11,10 +13,14 @@ export default {
findStale(store, type, findArgs) {
const staleResult = new StaleResult();
try {
- const stored = this.keyValueStore.getItem(this.storageKey(type, findArgs));
- if (stored) {
- const parsed = JSON.parse(stored);
- staleResult.setResults(parsed);
+ if (!skipFirst) {
+ const stored = this.keyValueStore.getItem(this.storageKey(type, findArgs));
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ staleResult.setResults(parsed);
+ }
+ } else {
+ skipFirst = false;
}
} catch(e) {
// JSON parsing error
From a54e8f3c5eebaae06c1041fb9f4c02d9a6704acc Mon Sep 17 00:00:00 2001
From: Sam
Date: Fri, 4 Sep 2015 13:20:33 +1000
Subject: [PATCH 057/224] FEATURE: live refresh notifications as they happen
---
.../discourse/components/user-menu.js.es6 | 14 ++++++--
.../subscribe-user-notifications.js.es6 | 33 ++++++++++++++++++-
.../discourse/lib/key-value-store.js.es6 | 7 ++++
.../mixins/stale-local-storage.js.es6 | 23 ++++++-------
.../javascripts/discourse/models/store.js.es6 | 12 +++----
app/models/user.rb | 9 ++++-
6 files changed, 75 insertions(+), 23 deletions(-)
diff --git a/app/assets/javascripts/discourse/components/user-menu.js.es6 b/app/assets/javascripts/discourse/components/user-menu.js.es6
index b60a0eb8282..7815295ef20 100644
--- a/app/assets/javascripts/discourse/components/user-menu.js.es6
+++ b/app/assets/javascripts/discourse/components/user-menu.js.es6
@@ -50,10 +50,20 @@ export default Ember.Component.extend({
// TODO: It's a bit odd to use the store in a component, but this one really
// wants to reach out and grab notifications
const store = this.container.lookup('store:main');
- const stale = store.findStale('notification', {recent: true, limit });
+ const stale = store.findStale('notification', {recent: true, limit }, {storageKey: 'recent-notifications'});
if (stale.hasResults) {
- this.set('notifications', stale.results);
+ const results = stale.results;
+ var content = results.get('content');
+
+ // we have to truncate to limit, otherwise we will render too much
+ if (content && (content.length > limit)) {
+ content = content.splice(0, limit);
+ results.set('content', content);
+ results.set('totalRows', limit);
+ }
+
+ this.set('notifications', results);
} else {
this.set('loadingNotifications', true);
}
diff --git a/app/assets/javascripts/discourse/initializers/subscribe-user-notifications.js.es6 b/app/assets/javascripts/discourse/initializers/subscribe-user-notifications.js.es6
index 19c64a61b73..92ab828c680 100644
--- a/app/assets/javascripts/discourse/initializers/subscribe-user-notifications.js.es6
+++ b/app/assets/javascripts/discourse/initializers/subscribe-user-notifications.js.es6
@@ -8,7 +8,12 @@ export default {
const user = container.lookup('current-user:main'),
site = container.lookup('site:main'),
siteSettings = container.lookup('site-settings:main'),
- bus = container.lookup('message-bus:main');
+ bus = container.lookup('message-bus:main'),
+ keyValueStore = container.lookup('key-value-store:main');
+
+ // clear old cached notifications
+ // they could be a week old for all we know
+ keyValueStore.remove('recent-notifications');
if (user) {
@@ -38,6 +43,32 @@ export default {
if (oldUnread !== data.unread_notifications || oldPM !== data.unread_private_messages) {
user.set('lastNotificationChange', new Date());
}
+
+ var stale = keyValueStore.getObject('recent-notifications');
+ const lastNotification = data.last_notification && data.last_notification.notification;
+
+ if (stale && stale.notifications && lastNotification) {
+
+ const oldNotifications = stale.notifications;
+ const staleIndex = _.findIndex(oldNotifications, {id: lastNotification.id});
+
+ if (staleIndex > -1) {
+ oldNotifications.splice(staleIndex, 1);
+ }
+
+ // this gets a bit tricky, uread pms are bumped to front
+ var insertPosition = 0;
+ if (lastNotification.notification_type !== 6) {
+ insertPosition = _.findIndex(oldNotifications, function(n){
+ return n.notification_type !== 6 || n.read;
+ });
+ insertPosition = insertPosition === -1 ? oldNotifications.length - 1 : insertPosition;
+ }
+
+ oldNotifications.splice(insertPosition, 0, lastNotification);
+ keyValueStore.setItem('recent-notifications', JSON.stringify(stale));
+
+ }
}, user.notification_channel_position);
bus.subscribe("/categories", function(data) {
diff --git a/app/assets/javascripts/discourse/lib/key-value-store.js.es6 b/app/assets/javascripts/discourse/lib/key-value-store.js.es6
index b5f13d024ca..30f86b16ec7 100644
--- a/app/assets/javascripts/discourse/lib/key-value-store.js.es6
+++ b/app/assets/javascripts/discourse/lib/key-value-store.js.es6
@@ -43,6 +43,13 @@ KeyValueStore.prototype = {
get(key) {
if (!safeLocalStorage) { return null; }
return safeLocalStorage[this.context + key];
+ },
+
+ getObject(key) {
+ if (!safeLocalStorage) { return null; }
+ try {
+ return JSON.parse(safeLocalStorage[this.context + key]);
+ } catch(e) {}
}
};
diff --git a/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6 b/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6
index d8816c981e6..07dcd548a55 100644
--- a/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6
+++ b/app/assets/javascripts/discourse/mixins/stale-local-storage.js.es6
@@ -1,8 +1,6 @@
import StaleResult from 'discourse/lib/stale-result';
import { hashString } from 'discourse/lib/hash';
-var skipFirst = true;
-
// Mix this in to an adapter to provide stale caching in our key value store
export default {
storageKey(type, findArgs) {
@@ -10,17 +8,14 @@ export default {
return `${type}_${hashedArgs}`;
},
- findStale(store, type, findArgs) {
+ findStale(store, type, findArgs, opts) {
const staleResult = new StaleResult();
+ const key = (opts && opts.storageKey) || this.storageKey(type, findArgs)
try {
- if (!skipFirst) {
- const stored = this.keyValueStore.getItem(this.storageKey(type, findArgs));
- if (stored) {
- const parsed = JSON.parse(stored);
- staleResult.setResults(parsed);
- }
- } else {
- skipFirst = false;
+ const stored = this.keyValueStore.getItem(key);
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ staleResult.setResults(parsed);
}
} catch(e) {
// JSON parsing error
@@ -28,9 +23,11 @@ export default {
return staleResult;
},
- find(store, type, findArgs) {
+ find(store, type, findArgs, opts) {
+ const key = (opts && opts.storageKey) || this.storageKey(type, findArgs)
+
return this._super(store, type, findArgs).then((results) => {
- this.keyValueStore.setItem(this.storageKey(type, findArgs), JSON.stringify(results));
+ this.keyValueStore.setItem(key, JSON.stringify(results));
return results;
});
}
diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6
index 851b712a61c..dec3fcec764 100644
--- a/app/assets/javascripts/discourse/models/store.js.es6
+++ b/app/assets/javascripts/discourse/models/store.js.es6
@@ -71,18 +71,18 @@ export default Ember.Object.extend({
// See if the store can find stale data. We sometimes prefer to show stale data and
// refresh it in the background.
- findStale(type, findArgs) {
- const stale = this.adapterFor(type).findStale(this, type, findArgs);
+ findStale(type, findArgs, opts) {
+ const stale = this.adapterFor(type).findStale(this, type, findArgs, opts);
if (stale.hasResults) {
stale.results = this._hydrateFindResults(stale.results, type, findArgs);
}
- stale.refresh = () => this.find(type, findArgs);
+ stale.refresh = () => this.find(type, findArgs, opts);
return stale;
},
- find(type, findArgs) {
- return this.adapterFor(type).find(this, type, findArgs).then((result) => {
- return this._hydrateFindResults(result, type, findArgs);
+ find(type, findArgs, opts) {
+ return this.adapterFor(type).find(this, type, findArgs, opts).then((result) => {
+ return this._hydrateFindResults(result, type, findArgs, opts);
});
},
diff --git a/app/models/user.rb b/app/models/user.rb
index a329f88dc05..31a37e2b7e6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -306,10 +306,17 @@ class User < ActiveRecord::Base
end
def publish_notifications_state
+ # publish last notification json with the message so we
+ # can apply an update
+ notification = notifications.visible.order('notifications.id desc').first
+ json = NotificationSerializer.new(notification).as_json if notification
+
MessageBus.publish("/notification/#{id}",
{unread_notifications: unread_notifications,
unread_private_messages: unread_private_messages,
- total_unread_notifications: total_unread_notifications},
+ total_unread_notifications: total_unread_notifications,
+ last_notification: json
+ },
user_ids: [id] # only publish the notification to this user
)
end
From 1f31435a7b7ff549fa8765923157d0c66682caba Mon Sep 17 00:00:00 2001
From: Sam
Date: Fri, 4 Sep 2015 13:34:21 +1000
Subject: [PATCH 058/224] correct handling of data hash parsing
---
app/models/notification.rb | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/app/models/notification.rb b/app/models/notification.rb
index 842d657263a..e142f57b2ea 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -77,8 +77,12 @@ class Notification < ActiveRecord::Base
# Be wary of calling this frequently. O(n) JSON parsing can suck.
def data_hash
@data_hash ||= begin
+
return nil if data.blank?
- JSON.parse(data).with_indifferent_access
+ parsed = JSON.parse(data)
+ return nil if parsed.blank?
+
+ parsed.with_indifferent_access
end
end
From b3d6cefef492738af953d67547a7f86203fcedc3 Mon Sep 17 00:00:00 2001
From: Sam
Date: Fri, 4 Sep 2015 17:22:26 +1000
Subject: [PATCH 059/224] FIX: never ask for less than 5 notifications
---
app/assets/javascripts/discourse/components/user-menu.js.es6 | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/assets/javascripts/discourse/components/user-menu.js.es6 b/app/assets/javascripts/discourse/components/user-menu.js.es6
index 7815295ef20..d934e9295b5 100644
--- a/app/assets/javascripts/discourse/components/user-menu.js.es6
+++ b/app/assets/javascripts/discourse/components/user-menu.js.es6
@@ -46,6 +46,9 @@ export default Ember.Component.extend({
// estimate (poorly) the amount of notifications to return
const limit = Math.round(($(window).height() - headerHeight()) / 50);
+ // we REALLY don't want to be asking for negative counts of notifications
+ // less than 5 is also not that useful
+ if (limit < 5) { limit = 5; }
// TODO: It's a bit odd to use the store in a component, but this one really
// wants to reach out and grab notifications
From 6709eaeb3cac113e44097d71f6622be037c8d9cf Mon Sep 17 00:00:00 2001
From: Sam
Date: Fri, 4 Sep 2015 17:23:37 +1000
Subject: [PATCH 060/224] better have some sane upper bound here.
---
app/assets/javascripts/discourse/components/user-menu.js.es6 | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/assets/javascripts/discourse/components/user-menu.js.es6 b/app/assets/javascripts/discourse/components/user-menu.js.es6
index d934e9295b5..0b094fceacf 100644
--- a/app/assets/javascripts/discourse/components/user-menu.js.es6
+++ b/app/assets/javascripts/discourse/components/user-menu.js.es6
@@ -49,6 +49,7 @@ export default Ember.Component.extend({
// we REALLY don't want to be asking for negative counts of notifications
// less than 5 is also not that useful
if (limit < 5) { limit = 5; }
+ if (limit > 40) { limit = 40; }
// TODO: It's a bit odd to use the store in a component, but this one really
// wants to reach out and grab notifications
From 7a5c0453992a2f0a591a260f2e50971d2c420919 Mon Sep 17 00:00:00 2001
From: Sam
Date: Fri, 4 Sep 2015 17:39:52 +1000
Subject: [PATCH 061/224] its not a const anymore
---
app/assets/javascripts/discourse/components/user-menu.js.es6 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/assets/javascripts/discourse/components/user-menu.js.es6 b/app/assets/javascripts/discourse/components/user-menu.js.es6
index 0b094fceacf..78438193cd7 100644
--- a/app/assets/javascripts/discourse/components/user-menu.js.es6
+++ b/app/assets/javascripts/discourse/components/user-menu.js.es6
@@ -45,7 +45,7 @@ export default Ember.Component.extend({
if (this.get('loadingNotifications')) { return; }
// estimate (poorly) the amount of notifications to return
- const limit = Math.round(($(window).height() - headerHeight()) / 50);
+ var limit = Math.round(($(window).height() - headerHeight()) / 50);
// we REALLY don't want to be asking for negative counts of notifications
// less than 5 is also not that useful
if (limit < 5) { limit = 5; }
From 4e2c932895749baf9d2bee4b35a9d9315994db00 Mon Sep 17 00:00:00 2001
From: Arpit Jalan
Date: Fri, 4 Sep 2015 18:59:15 +0530
Subject: [PATCH 062/224] Update Translations
---
config/locales/client.de.yml | 5 ++---
config/locales/client.fr.yml | 23 ++++++++++++++++++++---
config/locales/client.it.yml | 5 ++---
config/locales/client.nb_NO.yml | 5 ++---
config/locales/client.pt_BR.yml | 5 ++---
config/locales/client.zh_TW.yml | 5 ++---
6 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml
index cb9629d980c..cd6ed7972e9 100644
--- a/config/locales/client.de.yml
+++ b/config/locales/client.de.yml
@@ -542,7 +542,6 @@ de:
title: "Einladungen"
user: "Eingeladener Benutzer"
sent: "Gesendet"
- none: "Du hast bis jetzt noch niemanden hierher eingeladen."
truncated: "Zeige die ersten {{count}} Einladungen."
redeemed: "Angenommene Einladungen"
redeemed_tab: "Angenommen"
@@ -1432,6 +1431,8 @@ de:
help: "Du hast in diesem Thema ein Lesezeichen gesetzt."
locked:
help: "Dieses Thema ist geschlossen. Das Antworten ist nicht mehr möglich."
+ archived:
+ help: "Dieses Thema ist archiviert; es ist eingefroren und kann nicht mehr geändert werden"
unpinned:
title: "Losgelöst"
help: "Dieses Thema ist für dich losgelöst; es wird in der normalen Reihenfolge angezeigt"
@@ -1441,8 +1442,6 @@ de:
pinned:
title: "Angeheftet"
help: "Dieses Thema ist für dich angeheftet; es wird immer am Anfang seiner Kategorie auftauchen"
- archived:
- help: "Dieses Thema ist archiviert; es ist eingefroren und kann nicht mehr geändert werden"
invisible:
help: "Dieses Thema ist unsichtbar. Es wird in keiner Themenliste angezeigt und kann nur mit einem direkten Link betrachtet werden."
posts: "Beiträge"
diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml
index b1c61b5f47b..39f0d752d38 100644
--- a/config/locales/client.fr.yml
+++ b/config/locales/client.fr.yml
@@ -543,7 +543,7 @@ fr:
title: "Invitations"
user: "Utilisateurs"
sent: "Envoyé"
- none: "Vous n'avez encore invité personne."
+ none: "Il n'y a plus d'invitation en attente à afficher."
truncated: "Affichage des {{count}} premières invitations."
redeemed: "Invitations acceptées"
redeemed_tab: "Utilisés"
@@ -563,6 +563,8 @@ fr:
days_visited: "Ratio de présence"
account_age_days: "Âge du compte en jours"
create: "Envoyer une invitation"
+ generate_link: "Copier le lien d'invitation"
+ generated_link_message: '
Le lien d''invitation a été généré avec succès !
Le lien d''invitation est valide uniquement pour cette adresse : %{invitedEmail}
'
bulk_invite:
none: "Vous n'avez encore invité personne. Vous pouvez envoyé des invitations individuelles, ou en masse en envoyant un fichier d'invitation contenant la liste des courriels."
text: "Invitation massive depuis un fichier"
@@ -619,6 +621,7 @@ fr:
read_only_mode:
enabled: "Le mode lecture seule est activé. Vous pouvez continuer à naviguer sur le site, mais ne pouvez pas prendre part aux discussions."
login_disabled: "Impossible de se connecté quand le site est en mode lecture seule."
+ too_few_topics_notice: "Faites partir la discussion ! Il y a actuellement %{currentTopics} / %{requiredTopics} sujets et %{currentPosts} / %{requiredPosts} messages. Les nouveaux utilisateurs ont besoin de discussions qu'ils peuvent lire et sur lesquelles ils peuvent répondre."
learn_more: "en savoir plus…"
year: 'an'
year_desc: 'sujets créés durant les 365 derniers jours'
@@ -837,6 +840,7 @@ fr:
category: "Rechercher dans la catégorie \"{{category}}\""
topic: "Rechercher dans ce sujet"
private_messages: "Rechercher des messages"
+ hamburger_menu: "se rendre dans une autre liste de sujet ou catégorie"
go_back: 'retour'
not_logged_in_user: 'page utilisateur avec un résumé de l''activité en cours et les préférences '
current_user: 'voir la page de l''utilisateur'
@@ -1037,6 +1041,7 @@ fr:
unpin: "Enlever ce sujet du haut de la catégorie {{categoryLink}}."
unpin_until: "Enlever ce sujet du haut de la catégorie {{categoryLink}} ou attendre jusqu'à %{until}."
pin_note: "Les utilisateurs peuvent enlever l'épingle de ce sujet eux-mêmes."
+ pin_validation: "Une date est requise pour épingler ce sujet."
already_pinned:
zero: "Aucun sujet actuellement épinglé dans {{categoryLink}}."
one: "Sujets actuellement épinglés dans {{categoryLink}}: 1."
@@ -1436,6 +1441,10 @@ fr:
help: "Vous avez ajouté ce sujet à vos signets"
locked:
help: "Ce sujet est fermé; il n'accepte plus de nouvelles réponses"
+ archived:
+ help: "Ce sujet est archivé; il est gelé et ne peut être modifié"
+ locked_and_archived:
+ help: "Ce sujet est fermé et archivé ; il n'accepte plus de nouvelles réponses et ne peut plus être modifié"
unpinned:
title: "Désépinglé"
help: "Ce sujet est désépinglé pour vous; il sera affiché dans l'ordre par défaut"
@@ -1445,8 +1454,6 @@ fr:
pinned:
title: "Épingler"
help: "Ce sujet est épinglé pour vous; il s'affichera en haut de sa catégorie"
- archived:
- help: "Ce sujet est archivé; il est gelé et ne peut être modifié"
invisible:
help: "Ce sujet n'apparait plus dans la liste des sujets et sera seulement accessible via un lien direct"
posts: "Messages"
@@ -2372,6 +2379,7 @@ fr:
title: 'Application'
create: 'c Créer un nouveau sujet'
notifications: 'n Ouvrir les notifications'
+ hamburger_menu: '= Ouvrir le menu hamburger'
user_profile_menu: 'p Ouvrir le menu de votre profil'
show_incoming_updated_topics: '. Afficher les sujets mis à jour'
search: '/ Rechercher'
@@ -2505,6 +2513,15 @@ fr:
reader:
name: Lecteur
description: A lu tous les messages d'un sujet contenant plus de 100 messages
+ popular_link:
+ name: Lien populaire
+ description: A posté un lien externe avec au moins 50 clics
+ hot_link:
+ name: Lien tendance
+ description: A posté un lien externe avec au moins 300 clics
+ famous_link:
+ name: Lien populaire
+ description: A posté un lien externe avec au moins 1000 clics
google_search: |
Rechercher avec Google
diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml
index e93c9d2ddc8..2ab2f8668d3 100644
--- a/config/locales/client.it.yml
+++ b/config/locales/client.it.yml
@@ -543,7 +543,6 @@ it:
title: "Inviti"
user: "Utente Invitato"
sent: "Spedito"
- none: "Non hai ancora invitato nessuno qui."
truncated: "Mostro i primi {{count}} inviti."
redeemed: "Inviti Accettati"
redeemed_tab: "Riscattato"
@@ -1425,6 +1424,8 @@ it:
help: "Hai aggiunto questo argomento ai segnalibri"
locked:
help: "Questo argomento è chiuso; non sono ammesse nuove risposte"
+ archived:
+ help: "Questo argomento è archiviato; è bloccato e non può essere modificato"
unpinned:
title: "Spuntato"
help: "Questo argomento è per te spuntato; verrà mostrato con l'ordinamento di default"
@@ -1434,8 +1435,6 @@ it:
pinned:
title: "Appuntato"
help: "Questo argomento è per te appuntato; verrà mostrato con l'ordinamento di default"
- archived:
- help: "Questo argomento è archiviato; è bloccato e non può essere modificato"
invisible:
help: "Questo argomento è invisibile; non verrà mostrato nella liste di argomenti ed è possibile accedervi solo tramite collegamento diretto"
posts: "Messaggi"
diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml
index 61b7885eab8..c5002097b8f 100644
--- a/config/locales/client.nb_NO.yml
+++ b/config/locales/client.nb_NO.yml
@@ -522,7 +522,6 @@ nb_NO:
search: "skriv for å søke etter invitasjoner..."
title: "invitasjoner"
user: "Invitert bruker"
- none: "Du har ikke invitert noen hit ennå."
truncated: "Viser de første {{count}} invitasjoner."
redeemed: "Løs inn invitasjoner"
redeemed_tab: "Brukt"
@@ -1393,6 +1392,8 @@ nb_NO:
help: "Du lagret dette emnet"
locked:
help: "dette emnet er låst; det aksepterer ikke lenger nye svar"
+ archived:
+ help: "dette emnet er arkivert; det er fryst og kan ikke bli aktivert"
unpinned:
title: "Løsgjort"
help: "Dette emnet er ikke lenger fastsatt, det vil vises i vanlig rekkefølge"
@@ -1402,8 +1403,6 @@ nb_NO:
pinned:
title: "Fastsatt"
help: "Dette emnet er fastsatt for deg; det vil vises i toppen av sin kategori"
- archived:
- help: "dette emnet er arkivert; det er fryst og kan ikke bli aktivert"
invisible:
help: "Dette emnet er ikke listet; det vil ikke vises i emnelister, og kan kun leses via en direktelenke"
posts: "Innlegg"
diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml
index c4a28d3786e..187c9b9d382 100644
--- a/config/locales/client.pt_BR.yml
+++ b/config/locales/client.pt_BR.yml
@@ -505,7 +505,6 @@ pt_BR:
search: "digite para pesquisar convites..."
title: "Convites"
user: "Usuários convidados"
- none: "Você ainda não convidou ninguém."
truncated: "Exibindo os primeiros {{count}} convites."
redeemed: "Convites usados"
redeemed_at: "Usado"
@@ -1369,6 +1368,8 @@ pt_BR:
help: "Você adicionou este tópico aos favoritos"
locked:
help: "Este tópico está fechado; não serão aceitas mais respostas"
+ archived:
+ help: "Este tópico está arquivado; está congelado e não pode ser alterado"
unpinned:
title: "Não fixo"
help: "Este tópico está desfixado para você; ele será mostrado em ordem normal"
@@ -1378,8 +1379,6 @@ pt_BR:
pinned:
title: "Fixo"
help: "Este tópico está fixado para você; ele será mostrado no topo de sua categoria"
- archived:
- help: "Este tópico está arquivado; está congelado e não pode ser alterado"
invisible:
help: "Este tópico está invisível; não aparecerá na listagem dos tópicos, e pode apenas ser acessado por link direto"
posts: "Mensagens"
diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml
index 330d7496972..f9752d50db2 100644
--- a/config/locales/client.zh_TW.yml
+++ b/config/locales/client.zh_TW.yml
@@ -440,7 +440,6 @@ zh_TW:
search: "輸入要搜尋邀請的文字..."
title: "邀請"
user: "受邀請的用戶"
- none: "你尚未邀請任何人。"
truncated: "顯示前 {{count}} 個邀請。"
redeemed: "已接受的邀請"
redeemed_at: "接受日期"
@@ -1196,6 +1195,8 @@ zh_TW:
help: "已將此討論話題加入書籤"
locked:
help: "此討論話題已關閉,不再接受回覆"
+ archived:
+ help: "此討論話題已封存,已被凍結無法再修改"
unpinned:
title: "取消釘選"
help: "此討論話題已取消置頂,將會以預設順序顯示。"
@@ -1205,8 +1206,6 @@ zh_TW:
pinned:
title: "已釘選"
help: "此討論話題已置頂,將顯示在它所屬分類話題列表的最上方"
- archived:
- help: "此討論話題已封存,已被凍結無法再修改"
invisible:
help: "此討論話題已隱藏,將不會出現在討論話題列表,只能以直接連結開啟。"
posts: "文章"
From cbb86f9bf6a4b942843f5b373813f36b4f3d98a8 Mon Sep 17 00:00:00 2001
From: Guo Xiang Tan
Date: Fri, 4 Sep 2015 11:35:25 +0800
Subject: [PATCH 063/224] FEATURE: Add a trigger for plugins to include their
own jobs.
---
app/models/post.rb | 1 +
spec/components/post_creator_spec.rb | 1 +
2 files changed, 2 insertions(+)
diff --git a/app/models/post.rb b/app/models/post.rb
index 940cb17cf10..3a6a8d51591 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -498,6 +498,7 @@ class Post < ActiveRecord::Base
args[:image_sizes] = image_sizes if image_sizes.present?
args[:invalidate_oneboxes] = true if invalidate_oneboxes.present?
Jobs.enqueue(:process_post, args)
+ DiscourseEvent.trigger(:after_trigger_post_process, self)
end
def self.public_posts_count_per_day(start_date, end_date, category_id=nil)
diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb
index 955663e4d25..0595a66e007 100644
--- a/spec/components/post_creator_spec.rb
+++ b/spec/components/post_creator_spec.rb
@@ -75,6 +75,7 @@ describe PostCreator do
DiscourseEvent.expects(:trigger).with(:post_created, anything, anything, user).once
DiscourseEvent.expects(:trigger).with(:after_validate_topic, anything, anything).once
DiscourseEvent.expects(:trigger).with(:before_create_topic, anything, anything).once
+ DiscourseEvent.expects(:trigger).with(:after_trigger_post_process, anything).once
creator.create
end
From 76fac1808589461909dfcd4c1998d7084127105a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9gis=20Hanol?=
Date: Fri, 4 Sep 2015 17:14:42 +0200
Subject: [PATCH 064/224] FIX: link to COPPA in Privacy Policy
---
config/locales/server.en.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 39ca89934e5..b37db153d2f 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -2429,7 +2429,7 @@ en:
## [Children's Online Privacy Protection Act Compliance](#coppa)
- Our site, products and services are all directed to people who are at least 13 years old or older. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA ([Children's Online Privacy Protection Act](http://en.wikipedia.org/wiki/Children)), do not use this site.
+ Our site, products and services are all directed to people who are at least 13 years old or older. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA ([Children's Online Privacy Protection Act](https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act)), do not use this site.
From 064b62199ef0d5c662dfe0e1675ec7b639d5c2e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9gis=20Hanol?=
Date: Fri, 4 Sep 2015 17:38:10 +0200
Subject: [PATCH 065/224] FIX: ensure we show the footer in '/categories' when
coming from a topics list
---
.../javascripts/discourse/routes/discovery-categories.js.es6 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6 b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
index 5bbd7aa66a4..85bbab29f15 100644
--- a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
+++ b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
@@ -61,7 +61,7 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
},
didTransition() {
- this.controllerFor("application").set("showFooter", true);
+ Ember.run.next(() => this.controllerFor("application").set("showFooter", true));
return true;
}
}
From 8e776d0fd79377b0fa1c2003aff4b279ad492a9e Mon Sep 17 00:00:00 2001
From: Robin Ward
Date: Thu, 3 Sep 2015 16:55:55 -0400
Subject: [PATCH 066/224] Move `TopicTrackingState` to injected object
---
.../components/create-topics-notice.js.es6 | 18 ++---
.../components/edit-category-general.js.es6 | 5 +-
.../components/hamburger-menu.js.es6 | 5 --
.../controllers/discovery/categories.js.es6 | 4 +-
.../controllers/discovery/topics.js.es6 | 40 ++++-------
.../discourse/lib/screen-track.js.es6 | 5 +-
.../mixins/bulk-topic-selection.js.es6 | 2 +-
.../discourse/models/category-list.js.es6 | 60 ++++++++++++++++
.../models/{category.js => category.js.es6} | 48 +++++++------
.../discourse/models/category_list.js | 68 ------------------
.../discourse/models/composer.js.es6 | 7 --
.../discourse/models/nav-item.js.es6 | 8 +--
.../javascripts/discourse/models/rest.js.es6 | 5 +-
.../javascripts/discourse/models/site.js.es6 | 11 +--
.../javascripts/discourse/models/store.js.es6 | 4 ++
.../discourse/models/topic-list.js.es6 | 3 -
.../models/topic-tracking-state.js.es6 | 2 +-
.../dynamic-route-builders.js.es6 | 1 +
.../inject-discourse-objects.js.es6 | 10 ++-
.../discourse/routes/application.js.es6 | 9 +--
.../routes/build-category-route.js.es6 | 4 +-
.../discourse/routes/build-topic-route.js.es6 | 6 +-
.../routes/discovery-categories.js.es6 | 6 +-
.../javascripts/discourse/routes/topic.js.es6 | 2 +-
.../javascripts/discourse/views/topic.js.es6 | 4 --
app/assets/javascripts/main_include.js | 1 +
test/javascripts/helpers/create-store.js.es6 | 19 ++++-
test/javascripts/helpers/qunit-helpers.js.es6 | 2 +-
.../lib/category-badge-test.js.es6 | 28 ++++----
test/javascripts/models/category-test.js.es6 | 69 ++++++++++---------
test/javascripts/models/composer-test.js.es6 | 29 ++++----
test/javascripts/models/site-test.js.es6 | 18 ++---
.../models/topic-tracking-state-test.js.es6 | 16 ++---
33 files changed, 256 insertions(+), 263 deletions(-)
create mode 100644 app/assets/javascripts/discourse/models/category-list.js.es6
rename app/assets/javascripts/discourse/models/{category.js => category.js.es6} (85%)
delete mode 100644 app/assets/javascripts/discourse/models/category_list.js
diff --git a/app/assets/javascripts/discourse/components/create-topics-notice.js.es6 b/app/assets/javascripts/discourse/components/create-topics-notice.js.es6
index 9f502ec7ceb..9481bfbd4d0 100644
--- a/app/assets/javascripts/discourse/components/create-topics-notice.js.es6
+++ b/app/assets/javascripts/discourse/components/create-topics-notice.js.es6
@@ -54,23 +54,17 @@ export default Ember.Component.extend({
}));
},
- @computed()
- topicTrackingState() {
- return Discourse.TopicTrackingState.current();
- },
-
@observes('topicTrackingState.incomingCount')
fetchLiveStats() {
if (!this.get('enabled')) { return; }
- var self = this;
- LivePostCounts.find().then(function(stats) {
+ LivePostCounts.find().then((stats) => {
if(stats) {
- self.set('publicTopicCount', stats.get('public_topic_count'));
- self.set('publicPostCount', stats.get('public_post_count'));
- if (self.get('publicTopicCount') >= self.get('requiredTopics')
- && self.get('publicPostCount') >= self.get('requiredPosts')) {
- self.set('enabled', false); // No more checks
+ this.set('publicTopicCount', stats.get('public_topic_count'));
+ this.set('publicPostCount', stats.get('public_post_count'));
+ if (this.get('publicTopicCount') >= this.get('requiredTopics')
+ && this.get('publicPostCount') >= this.get('requiredPosts')) {
+ this.set('enabled', false); // No more checks
}
}
});
diff --git a/app/assets/javascripts/discourse/components/edit-category-general.js.es6 b/app/assets/javascripts/discourse/components/edit-category-general.js.es6
index 5d323ce06a6..e26c24a21ec 100644
--- a/app/assets/javascripts/discourse/components/edit-category-general.js.es6
+++ b/app/assets/javascripts/discourse/components/edit-category-general.js.es6
@@ -1,6 +1,7 @@
import DiscourseURL from 'discourse/lib/url';
import { buildCategoryPanel } from 'discourse/components/edit-category-panel';
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
+import Category from 'discourse/models/category';
export default buildCategoryPanel('general', {
foregroundColors: ['FFFFFF', '000000'],
@@ -31,7 +32,7 @@ export default buildCategoryPanel('general', {
categoryBadgePreview: function() {
const category = this.get('category');
- const c = Discourse.Category.create({
+ const c = Category.create({
name: category.get('categoryName'),
color: category.get('color'),
text_color: category.get('text_color'),
@@ -45,7 +46,7 @@ export default buildCategoryPanel('general', {
// We can change the parent if there are no children
subCategories: function() {
if (Ember.isEmpty(this.get('category.id'))) { return null; }
- return Discourse.Category.list().filterBy('parent_category_id', this.get('category.id'));
+ return Category.list().filterBy('parent_category_id', this.get('category.id'));
}.property('category.id'),
showDescription: function() {
diff --git a/app/assets/javascripts/discourse/components/hamburger-menu.js.es6 b/app/assets/javascripts/discourse/components/hamburger-menu.js.es6
index d3b5cc57a68..a3c07774880 100644
--- a/app/assets/javascripts/discourse/components/hamburger-menu.js.es6
+++ b/app/assets/javascripts/discourse/components/hamburger-menu.js.es6
@@ -22,11 +22,6 @@ export default Ember.Component.extend({
return this.siteSettings.faq_url ? this.siteSettings.faq_url : Discourse.getURL('/faq');
},
- @computed()
- topicTrackingState() {
- return Discourse.TopicTrackingState.current();
- },
-
_lookupCount(type) {
const state = this.get('topicTrackingState');
return state ? state.lookupCount(type) : 0;
diff --git a/app/assets/javascripts/discourse/controllers/discovery/categories.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/categories.js.es6
index ebda2c04a11..ba9877d485d 100644
--- a/app/assets/javascripts/discourse/controllers/discovery/categories.js.es6
+++ b/app/assets/javascripts/discourse/controllers/discovery/categories.js.es6
@@ -19,8 +19,8 @@ export default DiscoveryController.extend({
this.set('controllers.discovery.loading', true);
const parentCategory = this.get('model.parentCategory');
- const promise = parentCategory ? Discourse.CategoryList.listForParent(parentCategory) :
- Discourse.CategoryList.list();
+ const promise = parentCategory ? Discourse.CategoryList.listForParent(this.store, parentCategory) :
+ Discourse.CategoryList.list(this.store);
const self = this;
promise.then(function(list) {
diff --git a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
index 772c8e4c5b7..45716ae2ee0 100644
--- a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
+++ b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
@@ -29,8 +29,8 @@ const controllerOpts = {
},
// Show newly inserted topics
- showInserted: function() {
- const tracker = Discourse.TopicTrackingState.current();
+ showInserted() {
+ const tracker = this.topicTrackingState;
// Move inserted into topics
this.get('content').loadBefore(tracker.get('newIncoming'));
@@ -38,9 +38,8 @@ const controllerOpts = {
return false;
},
- refresh: function() {
- const filter = this.get('model.filter'),
- self = this;
+ refresh() {
+ const filter = this.get('model.filter');
this.setProperties({ order: 'default', ascending: false });
@@ -52,36 +51,27 @@ const controllerOpts = {
// Lesson learned: Don't call `loading` yourself.
this.set('controllers.discovery.loading', true);
- this.store.findFiltered('topicList', {filter}).then(function(list) {
- Discourse.TopicList.hideUniformCategory(list, self.get('category'));
+ this.store.findFiltered('topicList', {filter}).then((list) => {
+ Discourse.TopicList.hideUniformCategory(list, this.get('category'));
- self.setProperties({ model: list });
- self.resetSelected();
+ this.setProperties({ model: list });
+ this.resetSelected();
- const tracking = Discourse.TopicTrackingState.current();
- if (tracking) {
- tracking.sync(list, filter);
+ if (this.topicTrackingState) {
+ this.topicTrackingState.sync(list, filter);
}
- self.send('loadingComplete');
+ this.send('loadingComplete');
});
},
- resetNew: function() {
- const self = this;
-
- Discourse.TopicTrackingState.current().resetNew();
- Discourse.Topic.resetNew().then(function() {
- self.send('refresh');
- });
+ resetNew() {
+ this.topicTrackingState.resetNew();
+ Discourse.Topic.resetNew().then(() => this.send('refresh'));
}
},
- topicTrackingState: function() {
- return Discourse.TopicTrackingState.current();
- }.property(),
-
isFilterPage: function(filter, filterType) {
if (!filter) { return false; }
return filter.match(new RegExp(filterType + '$', 'gi')) ? true : false;
@@ -96,7 +86,7 @@ const controllerOpts = {
}.property('model.filter', 'model.topics.length'),
tooManyTracked: function(){
- return Discourse.TopicTrackingState.current().tooManyTracked();
+ return this.topicTrackingState.tooManyTracked();
}.property(),
showDismissAtTop: function() {
diff --git a/app/assets/javascripts/discourse/lib/screen-track.js.es6 b/app/assets/javascripts/discourse/lib/screen-track.js.es6
index 3d55418cfbf..4bac0e52171 100644
--- a/app/assets/javascripts/discourse/lib/screen-track.js.es6
+++ b/app/assets/javascripts/discourse/lib/screen-track.js.es6
@@ -9,6 +9,9 @@ const ScreenTrack = Ember.Object.extend({
init() {
this.reset();
+
+ // TODO: Move `ScreenTrack` to injection and remove this
+ this.set('topicTrackingState', Discourse.__container__.lookup('topic-tracking-state:main'));
},
start(topicId, topicController) {
@@ -112,7 +115,7 @@ const ScreenTrack = Ember.Object.extend({
highestSeenByTopic[topicId] = highestSeen;
}
- Discourse.TopicTrackingState.current().updateSeen(topicId, highestSeen);
+ this.topicTrackingState.updateSeen(topicId, highestSeen);
if (!$.isEmptyObject(newTimings)) {
Discourse.ajax('/topics/timings', {
diff --git a/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6 b/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6
index a30cba1f939..c0e7650d8b6 100644
--- a/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6
+++ b/app/assets/javascripts/discourse/mixins/bulk-topic-selection.js.es6
@@ -36,7 +36,7 @@ export default Ember.Mixin.create({
}
promise.then(function(result) {
if (result && result.topic_ids) {
- const tracker = Discourse.TopicTrackingState.current();
+ const tracker = self.topicTrackingState;
result.topic_ids.forEach(function(t) {
tracker.removeTopic(t);
});
diff --git a/app/assets/javascripts/discourse/models/category-list.js.es6 b/app/assets/javascripts/discourse/models/category-list.js.es6
new file mode 100644
index 00000000000..fcbe90bd071
--- /dev/null
+++ b/app/assets/javascripts/discourse/models/category-list.js.es6
@@ -0,0 +1,60 @@
+const CategoryList = Ember.ArrayProxy.extend({
+ init() {
+ this.set('content', []);
+ this._super();
+ }
+});
+
+CategoryList.reopenClass({
+ categoriesFrom(store, result) {
+ const categories = Discourse.CategoryList.create();
+ const users = Discourse.Model.extractByKey(result.featured_users, Discourse.User);
+ const list = Discourse.Category.list();
+
+ result.category_list.categories.forEach(function(c) {
+ if (c.parent_category_id) {
+ c.parentCategory = list.findBy('id', c.parent_category_id);
+ }
+
+ if (c.subcategory_ids) {
+ c.subcategories = c.subcategory_ids.map(scid => list.findBy('id', parseInt(scid, 10)));
+ }
+
+ if (c.featured_user_ids) {
+ c.featured_users = c.featured_user_ids.map(u => users[u]);
+ }
+
+ if (c.topics) {
+ c.topics = c.topics.map(t => Discourse.Topic.create(t));
+ }
+
+ categories.pushObject(store.createRecord('category', c));
+ });
+ return categories;
+ },
+
+ listForParent(store, category) {
+ return Discourse.ajax('/categories.json?parent_category_id=' + category.get('id')).then((result) => {
+ return Discourse.CategoryList.create({
+ categories: this.categoriesFrom(store, result),
+ parentCategory: category
+ });
+ });
+ },
+
+ list(store) {
+ const getCategories = () => Discourse.ajax("/categories.json");
+ return PreloadStore.getAndRemove("categories_list", getCategories).then((result) => {
+ return Discourse.CategoryList.create({
+ categories: this.categoriesFrom(store, result),
+ can_create_category: result.category_list.can_create_category,
+ can_create_topic: result.category_list.can_create_topic,
+ draft_key: result.category_list.draft_key,
+ draft: result.category_list.draft,
+ draft_sequence: result.category_list.draft_sequence
+ });
+ });
+ }
+});
+
+export default CategoryList;
diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js.es6
similarity index 85%
rename from app/assets/javascripts/discourse/models/category.js
rename to app/assets/javascripts/discourse/models/category.js.es6
index 33b57894a7f..fd99d494b6f 100644
--- a/app/assets/javascripts/discourse/models/category.js
+++ b/app/assets/javascripts/discourse/models/category.js.es6
@@ -1,4 +1,6 @@
-Discourse.Category = Discourse.Model.extend({
+import RestModel from 'discourse/models/rest';
+
+const Category = RestModel.extend({
init: function() {
this._super();
@@ -26,7 +28,7 @@ Discourse.Category = Discourse.Model.extend({
}.property('id'),
url: function() {
- return Discourse.getURL("/c/") + Discourse.Category.slugFor(this);
+ return Discourse.getURL("/c/") + Category.slugFor(this);
}.property('name'),
fullSlug: function() {
@@ -129,16 +131,12 @@ Discourse.Category = Discourse.Model.extend({
}
}.property('topics'),
- topicTrackingState: function(){
- return Discourse.TopicTrackingState.current();
- }.property(),
-
- unreadTopics: function(){
- return this.get('topicTrackingState').countUnread(this.get('id'));
+ unreadTopics: function() {
+ return this.topicTrackingState.countUnread(this.get('id'));
}.property('topicTrackingState.messageCount'),
- newTopics: function(){
- return this.get('topicTrackingState').countNew(this.get('id'));
+ newTopics: function() {
+ return this.topicTrackingState.countNew(this.get('id'));
}.property('topicTrackingState.messageCount'),
topicStatsTitle: function() {
@@ -193,10 +191,10 @@ Discourse.Category = Discourse.Model.extend({
var _uncategorized;
-Discourse.Category.reopenClass({
+Category.reopenClass({
findUncategorized: function() {
- _uncategorized = _uncategorized || Discourse.Category.list().findBy('id', Discourse.Site.currentProp('uncategorized_category_id'));
+ _uncategorized = _uncategorized || Category.list().findBy('id', Discourse.Site.currentProp('uncategorized_category_id'));
return _uncategorized;
},
@@ -207,7 +205,7 @@ Discourse.Category.reopenClass({
result = "";
if (parentCategory) {
- result = Discourse.Category.slugFor(parentCategory) + "/";
+ result = Category.slugFor(parentCategory) + "/";
}
var id = Em.get(category, 'id'),
@@ -234,20 +232,20 @@ Discourse.Category.reopenClass({
},
findSingleBySlug: function(slug) {
- return Discourse.Category.list().find(function(c) {
- return Discourse.Category.slugFor(c) === slug;
+ return Category.list().find(function(c) {
+ return Category.slugFor(c) === slug;
});
},
findById: function(id) {
if (!id) { return; }
- return Discourse.Category.idMap()[id];
+ return Category.idMap()[id];
},
findByIds: function(ids){
var categories = [];
_.each(ids, function(id){
- var found = Discourse.Category.findById(id);
+ var found = Category.findById(id);
if(found){
categories.push(found);
}
@@ -256,20 +254,20 @@ Discourse.Category.reopenClass({
},
findBySlug: function(slug, parentSlug) {
- var categories = Discourse.Category.list(),
+ var categories = Category.list(),
category;
if (parentSlug) {
- var parentCategory = Discourse.Category.findSingleBySlug(parentSlug);
+ var parentCategory = Category.findSingleBySlug(parentSlug);
if (parentCategory) {
if (slug === 'none') { return parentCategory; }
category = categories.find(function(item) {
- return item && item.get('parentCategory') === parentCategory && Discourse.Category.slugFor(item) === (parentSlug + "/" + slug);
+ return item && item.get('parentCategory') === parentCategory && Category.slugFor(item) === (parentSlug + "/" + slug);
});
}
} else {
- category = Discourse.Category.findSingleBySlug(slug);
+ category = Category.findSingleBySlug(slug);
// If we have a parent category, we need to enforce it
if (category && category.get('parentCategory')) return;
@@ -283,9 +281,9 @@ Discourse.Category.reopenClass({
return category;
},
- reloadById: function(id) {
- return Discourse.ajax("/c/" + id + "/show.json").then(function (result) {
- return Discourse.Category.create(result.category);
- });
+ reloadById(id) {
+ return Discourse.ajax("/c/" + id + "/show.json");
}
});
+
+export default Category;
diff --git a/app/assets/javascripts/discourse/models/category_list.js b/app/assets/javascripts/discourse/models/category_list.js
deleted file mode 100644
index bd1e1711fb3..00000000000
--- a/app/assets/javascripts/discourse/models/category_list.js
+++ /dev/null
@@ -1,68 +0,0 @@
-Discourse.CategoryList = Ember.ArrayProxy.extend({
- init: function() {
- this.set('content', []);
- this._super();
- }
-});
-
-Discourse.CategoryList.reopenClass({
- categoriesFrom: function(result) {
- var categories = Discourse.CategoryList.create(),
- users = Discourse.Model.extractByKey(result.featured_users, Discourse.User),
- list = Discourse.Category.list();
-
- result.category_list.categories.forEach(function(c) {
-
- if (c.parent_category_id) {
- c.parentCategory = list.findBy('id', c.parent_category_id);
- }
-
- if (c.subcategory_ids) {
- c.subcategories = c.subcategory_ids.map(function(scid) { return list.findBy('id', parseInt(scid, 10)); });
- }
-
- if (c.featured_user_ids) {
- c.featured_users = c.featured_user_ids.map(function(u) {
- return users[u];
- });
- }
- if (c.topics) {
- c.topics = c.topics.map(function(t) {
- return Discourse.Topic.create(t);
- });
- }
-
- categories.pushObject(Discourse.Category.create(c));
-
- });
- return categories;
- },
-
- listForParent: function(category) {
- var self = this;
- return Discourse.ajax('/categories.json?parent_category_id=' + category.get('id')).then(function(result) {
- return Discourse.CategoryList.create({
- categories: self.categoriesFrom(result),
- parentCategory: category
- });
- });
- },
-
- list: function() {
- var self = this;
-
- return PreloadStore.getAndRemove("categories_list", function() {
- return Discourse.ajax("/categories.json");
- }).then(function(result) {
- return Discourse.CategoryList.create({
- categories: self.categoriesFrom(result),
- can_create_category: result.category_list.can_create_category,
- can_create_topic: result.category_list.can_create_topic,
- draft_key: result.category_list.draft_key,
- draft: result.category_list.draft,
- draft_sequence: result.category_list.draft_sequence
- });
- });
- }
-
-});
diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6
index ad4115c8bbf..a59a2f8f0f9 100644
--- a/app/assets/javascripts/discourse/models/composer.js.es6
+++ b/app/assets/javascripts/discourse/models/composer.js.es6
@@ -689,13 +689,6 @@ const Composer = RestModel.extend({
});
Composer.reopenClass({
-
- open(opts) {
- const composer = Composer.create();
- composer.open(opts);
- return composer;
- },
-
loadDraft(opts) {
opts = opts || {};
diff --git a/app/assets/javascripts/discourse/models/nav-item.js.es6 b/app/assets/javascripts/discourse/models/nav-item.js.es6
index d4e5ef228b2..2a424c64f95 100644
--- a/app/assets/javascripts/discourse/models/nav-item.js.es6
+++ b/app/assets/javascripts/discourse/models/nav-item.js.es6
@@ -20,10 +20,6 @@ const NavItem = Discourse.Model.extend({
return I18n.t("filters." + name.replace("/", ".") + ".title", extra);
}.property('categoryName', 'name', 'count'),
- topicTrackingState: function() {
- return Discourse.TopicTrackingState.current();
- }.property(),
-
categoryName: function() {
var split = this.get('name').split('/');
return split[0] === 'category' ? split[1] : null;
@@ -100,7 +96,9 @@ NavItem.reopenClass({
extra = cb.call(self, text, opts);
_.merge(args, extra);
});
- return Discourse.NavItem.create(args);
+
+ const store = Discourse.__container__.lookup('store:main');
+ return store.createRecord('nav-item', args);
},
buildList(category, args) {
diff --git a/app/assets/javascripts/discourse/models/rest.js.es6 b/app/assets/javascripts/discourse/models/rest.js.es6
index 60d011bfc31..0c595bb7359 100644
--- a/app/assets/javascripts/discourse/models/rest.js.es6
+++ b/app/assets/javascripts/discourse/models/rest.js.es6
@@ -78,13 +78,10 @@ RestModel.reopenClass({
create(args) {
args = args || {};
- if (!args.store || !args.keyValueStore) {
+ if (!args.store) {
const container = Discourse.__container__;
// Ember.warn('Use `store.createRecord` to create records instead of `.create()`');
args.store = container.lookup('store:main');
-
- // TODO: Remove this when composer is using the store fully
- args.keyValueStore = container.lookup('key-value-store:main');
}
args.__munge = this.munge;
diff --git a/app/assets/javascripts/discourse/models/site.js.es6 b/app/assets/javascripts/discourse/models/site.js.es6
index 226e1ed811d..deddc23d3ca 100644
--- a/app/assets/javascripts/discourse/models/site.js.es6
+++ b/app/assets/javascripts/discourse/models/site.js.es6
@@ -1,8 +1,9 @@
import Archetype from 'discourse/models/archetype';
import PostActionType from 'discourse/models/post-action-type';
import Singleton from 'discourse/mixins/singleton';
+import RestModel from 'discourse/models/rest';
-const Site = Discourse.Model.extend({
+const Site = RestModel.extend({
isReadOnly: Em.computed.alias('is_readonly'),
@@ -80,7 +81,7 @@ const Site = Discourse.Model.extend({
existingCategory.setProperties(newCategory);
} else {
// TODO insert in right order?
- newCategory = Discourse.Category.create(newCategory);
+ newCategory = this.store.createRecord('category', newCategory);
categories.pushObject(newCategory);
this.get('categoriesById')[categoryId] = newCategory;
}
@@ -91,16 +92,18 @@ Site.reopenClass(Singleton, {
// The current singleton will retrieve its attributes from the `PreloadStore`.
createCurrent() {
- return Site.create(PreloadStore.get('site'));
+ const store = Discourse.__container__.lookup('store:main');
+ return store.createRecord('site', PreloadStore.get('site'));
},
create() {
const result = this._super.apply(this, arguments);
+ const store = result.store;
if (result.categories) {
result.categoriesById = {};
result.categories = _.map(result.categories, function(c) {
- return result.categoriesById[c.id] = Discourse.Category.create(c);
+ return result.categoriesById[c.id] = store.createRecord('category', c);
});
// Associate the categories with their parents
diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6
index dec3fcec764..57d60b0bbc3 100644
--- a/app/assets/javascripts/discourse/models/store.js.es6
+++ b/app/assets/javascripts/discourse/models/store.js.es6
@@ -157,6 +157,10 @@ export default Ember.Object.extend({
obj.__type = type;
obj.__state = obj.id ? "created" : "new";
+ // TODO: Have injections be automatic
+ obj.topicTrackingState = this.container.lookup('topic-tracking-state:main');
+ obj.keyValueStore = this.container.lookup('key-value-store:main');
+
const klass = this.container.lookupFactory('model:' + type) || RestModel;
const model = klass.create(obj);
diff --git a/app/assets/javascripts/discourse/models/topic-list.js.es6 b/app/assets/javascripts/discourse/models/topic-list.js.es6
index 742abdac406..a600221e65c 100644
--- a/app/assets/javascripts/discourse/models/topic-list.js.es6
+++ b/app/assets/javascripts/discourse/models/topic-list.js.es6
@@ -147,9 +147,6 @@ TopicList.reopenClass({
json.per_page = json.topic_list.per_page;
json.topics = topicsFrom(json, store);
- if (json.topic_list.filtered_category) {
- json.category = Discourse.Category.create(json.topic_list.filtered_category);
- }
return json;
},
diff --git a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
index 9a4e9d374f8..0005b6e6007 100644
--- a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
+++ b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
@@ -312,7 +312,7 @@ TopicTrackingState.reopenClass({
const container = Discourse.__container__,
messageBus = container.lookup('message-bus:main'),
currentUser = container.lookup('current-user:main'),
- instance = Discourse.TopicTrackingState.create({ messageBus, currentUser });
+ instance = TopicTrackingState.create({ messageBus, currentUser });
instance.loadStates(data);
instance.initialStatesLength = data && data.length;
diff --git a/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 b/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6
index a01c73def8a..812e2aed954 100644
--- a/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6
+++ b/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6
@@ -3,6 +3,7 @@ import buildTopicRoute from 'discourse/routes/build-topic-route';
import DiscoverySortableController from 'discourse/controllers/discovery-sortable';
export default {
+ after: 'inject-discourse-objects',
name: 'dynamic-route-builders',
initialize(container, app) {
diff --git a/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6 b/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6
index 59e7202b3a7..ed2577e231f 100644
--- a/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6
+++ b/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6
@@ -5,6 +5,7 @@ import Store from 'discourse/models/store';
import DiscourseURL from 'discourse/lib/url';
import DiscourseLocation from 'discourse/lib/discourse-location';
import SearchService from 'discourse/services/search';
+import TopicTrackingState from 'discourse/models/topic-tracking-state';
function inject() {
const app = arguments[0],
@@ -30,6 +31,12 @@ export default {
app.register('store:main', Store);
inject(app, 'store', 'route', 'controller');
+ app.register('message-bus:main', window.MessageBus, { instantiate: false });
+ injectAll(app, 'messageBus');
+
+ app.register('topic-tracking-state:main', TopicTrackingState.current(), { instantiate: false });
+ injectAll(app, 'topicTrackingState');
+
const site = Discourse.Site.current();
app.register('site:main', site, { instantiate: false });
injectAll(app, 'site');
@@ -46,9 +53,6 @@ export default {
app.register('current-user:main', Discourse.User.current(), { instantiate: false });
inject(app, 'currentUser', 'component', 'route', 'controller');
- app.register('message-bus:main', window.MessageBus, { instantiate: false });
- injectAll(app, 'messageBus');
-
app.register('location:discourse-location', DiscourseLocation);
const keyValueStore = new KeyValueStore("discourse_");
diff --git a/app/assets/javascripts/discourse/routes/application.js.es6 b/app/assets/javascripts/discourse/routes/application.js.es6
index 5944ccbd272..b8654da919e 100644
--- a/app/assets/javascripts/discourse/routes/application.js.es6
+++ b/app/assets/javascripts/discourse/routes/application.js.es6
@@ -2,6 +2,7 @@ import { setting } from 'discourse/lib/computed';
import logout from 'discourse/lib/logout';
import showModal from 'discourse/lib/show-modal';
import OpenComposer from "discourse/mixins/open-composer";
+import Category from 'discourse/models/category';
function unlessReadOnly(method) {
return function() {
@@ -126,11 +127,11 @@ const ApplicationRoute = Discourse.Route.extend(OpenComposer, {
},
editCategory(category) {
- const self = this;
- Discourse.Category.reloadById(category.get('id')).then(function (model) {
- self.site.updateCategory(model);
+ Category.reloadById(category.get('id')).then((atts) => {
+ const model = this.store.createRecord('category', atts.category);
+ this.site.updateCategory(model);
showModal('editCategory', { model });
- self.controllerFor('editCategory').set('selectedTab', 'general');
+ this.controllerFor('editCategory').set('selectedTab', 'general');
});
},
diff --git a/app/assets/javascripts/discourse/routes/build-category-route.js.es6 b/app/assets/javascripts/discourse/routes/build-category-route.js.es6
index eaf26cc18c5..1921552850e 100644
--- a/app/assets/javascripts/discourse/routes/build-category-route.js.es6
+++ b/app/assets/javascripts/discourse/routes/build-category-route.js.es6
@@ -36,7 +36,7 @@ export default function(filter, params) {
this._categoryList = null;
if (Em.isNone(model.get('parentCategory')) && Discourse.SiteSettings.show_subcategory_list) {
var self = this;
- return Discourse.CategoryList.listForParent(model).then(function(list) {
+ return Discourse.CategoryList.listForParent(this.store, model).then(function(list) {
self._categoryList = list;
});
}
@@ -52,7 +52,7 @@ export default function(filter, params) {
var findOpts = filterQueryParams(transition.queryParams, params),
extras = { cached: this.isPoppedState(transition) };
- return findTopicList(this.store, listFilter, findOpts, extras).then(function(list) {
+ return findTopicList(this.store, this.topicTrackingState, listFilter, findOpts, extras).then(function(list) {
Discourse.TopicList.hideUniformCategory(list, model);
self.set('topics', list);
});
diff --git a/app/assets/javascripts/discourse/routes/build-topic-route.js.es6 b/app/assets/javascripts/discourse/routes/build-topic-route.js.es6
index 0011b030554..36d54fd844f 100644
--- a/app/assets/javascripts/discourse/routes/build-topic-route.js.es6
+++ b/app/assets/javascripts/discourse/routes/build-topic-route.js.es6
@@ -12,9 +12,7 @@ function filterQueryParams(params, defaultParams) {
return findOpts;
}
-function findTopicList(store, filter, filterParams, extras) {
- const tracking = Discourse.TopicTrackingState.current();
-
+function findTopicList(store, tracking, filter, filterParams, extras) {
extras = extras || {};
return new Ember.RSVP.Promise(function(resolve) {
@@ -77,7 +75,7 @@ export default function(filter, extras) {
const findOpts = filterQueryParams(transition.queryParams),
findExtras = { cached: this.isPoppedState(transition) };
- return findTopicList(this.store, filter, findOpts, findExtras);
+ return findTopicList(this.store, this.topicTrackingState, filter, findOpts, findExtras);
},
titleToken() {
diff --git a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6 b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
index 85bbab29f15..d8a34f06253 100644
--- a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
+++ b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
@@ -16,8 +16,8 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
// if default page is categories
PreloadStore.remove("topic_list");
- return Discourse.CategoryList.list("categories").then(function(list) {
- const tracking = Discourse.TopicTrackingState.current();
+ return Discourse.CategoryList.list(this.store, 'categories').then((list) => {
+ const tracking = this.topicTrackingState;
if (tracking) {
tracking.sync(list, "categories");
tracking.trackIncoming("categories");
@@ -46,7 +46,7 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
const groups = this.site.groups,
everyoneName = groups.findBy("id", 0).name;
- const model = Discourse.Category.create({
+ const model = this.store.createRecord('category', {
color: "AB9364", text_color: "FFFFFF", group_permissions: [{group_name: everyoneName, permission_type: 1}],
available_groups: groups.map(g => g.name),
allow_badges: true
diff --git a/app/assets/javascripts/discourse/routes/topic.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6
index 5c9a97733a6..1f0b8098d3f 100644
--- a/app/assets/javascripts/discourse/routes/topic.js.es6
+++ b/app/assets/javascripts/discourse/routes/topic.js.es6
@@ -216,7 +216,7 @@ const TopicRoute = Discourse.Route.extend({
this.controllerFor('topic-admin-menu').set('model', model);
this.controllerFor('composer').set('topic', model);
- Discourse.TopicTrackingState.current().trackIncoming('all');
+ this.topicTrackingState.trackIncoming('all');
controller.subscribe();
this.controllerFor('topic-progress').set('model', model);
diff --git a/app/assets/javascripts/discourse/views/topic.js.es6 b/app/assets/javascripts/discourse/views/topic.js.es6
index bd6265aceac..8a182823629 100644
--- a/app/assets/javascripts/discourse/views/topic.js.es6
+++ b/app/assets/javascripts/discourse/views/topic.js.es6
@@ -121,10 +121,6 @@ const TopicView = Ember.View.extend(AddCategoryClass, AddArchetypeClass, Scrolli
this.appEvents.trigger('topic:scrolled', offset);
},
- topicTrackingState: function() {
- return Discourse.TopicTrackingState.current();
- }.property(),
-
browseMoreMessage: function() {
var opts = { latestLink: "" + I18n.t("topic.view_latest_topics") + "" },
category = this.get('controller.content.category');
diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js
index 1181452b4f5..0ff24305876 100644
--- a/app/assets/javascripts/main_include.js
+++ b/app/assets/javascripts/main_include.js
@@ -31,6 +31,7 @@
//= require ./discourse/models/rest
//= require ./discourse/models/badge-grouping
//= require ./discourse/models/badge
+//= require ./discourse/models/category
//= require_tree ./discourse/mixins
//= require ./discourse/lib/ajax-error
//= require ./discourse/lib/markdown
diff --git a/test/javascripts/helpers/create-store.js.es6 b/test/javascripts/helpers/create-store.js.es6
index 4be1b8feb07..9bfa00be4e6 100644
--- a/test/javascripts/helpers/create-store.js.es6
+++ b/test/javascripts/helpers/create-store.js.es6
@@ -1,16 +1,29 @@
import Store from "discourse/models/store";
import RestAdapter from 'discourse/adapters/rest';
+import KeyValueStore from 'discourse/lib/key-value-store';
+import TopicTrackingState from 'discourse/models/topic-tracking-state';
import Resolver from 'discourse/ember/resolver';
-let _restAdapter;
export default function() {
const resolver = Resolver.create();
return Store.create({
container: {
lookup(type) {
if (type === "adapter:rest") {
- _restAdapter = _restAdapter || RestAdapter.create({ container: this });
- return (_restAdapter);
+ this._restAdapter = this._restAdapter || RestAdapter.create({ container: this });
+ return (this._restAdapter);
+ }
+ if (type === "key-value-store:main") {
+ this._kvs = this._kvs || new KeyValueStore();
+ return (this._kvs);
+ }
+ if (type === "topic-tracking-state:main") {
+ this._tracker = this._tracker || TopicTrackingState.current();
+ return (this._tracker);
+ }
+ if (type === "site-settings:main") {
+ this._settings = this._settings || Discourse.SiteSettings.current();
+ return (this._settings);
}
},
diff --git a/test/javascripts/helpers/qunit-helpers.js.es6 b/test/javascripts/helpers/qunit-helpers.js.es6
index 4b0207e544e..44812f7ec68 100644
--- a/test/javascripts/helpers/qunit-helpers.js.es6
+++ b/test/javascripts/helpers/qunit-helpers.js.es6
@@ -71,7 +71,7 @@ function acceptance(name, options) {
options.teardown.call(this);
}
Discourse.User.resetCurrent();
- Discourse.Site.resetCurrent(Discourse.Site.create(fixtures['site.json'].site));
+ Discourse.Site.resetCurrent(Discourse.Site.create(jQuery.extend(true, {}, fixtures['site.json'].site)));
Discourse.Utilities.avatarImg = oldAvatar;
Discourse.reset();
diff --git a/test/javascripts/lib/category-badge-test.js.es6 b/test/javascripts/lib/category-badge-test.js.es6
index 55e2da58686..385d478c6ad 100644
--- a/test/javascripts/lib/category-badge-test.js.es6
+++ b/test/javascripts/lib/category-badge-test.js.es6
@@ -1,3 +1,4 @@
+import createStore from 'helpers/create-store';
import { blank, present } from 'helpers/qunit-helpers';
module("lib:category-link");
@@ -10,33 +11,36 @@ test("categoryBadge without a category", function() {
});
test("Regular categoryBadge", function() {
- var category = Discourse.Category.create({
- name: 'hello',
- id: 123,
- description_text: 'cool description',
- color: 'ff0',
- text_color: 'f00'
- }),
- tag = parseHTML(categoryBadgeHTML(category))[0];
+ const store = createStore();
+ const category = store.createRecord('category', {
+ name: 'hello',
+ id: 123,
+ description_text: 'cool description',
+ color: 'ff0',
+ text_color: 'f00'
+ });
+ const tag = parseHTML(categoryBadgeHTML(category))[0];
equal(tag.name, 'a', 'it creates a `a` wrapper tag');
equal(tag.attributes['class'].trim(), 'badge-wrapper', 'it has the correct class');
- var label = tag.children[1];
+ const label = tag.children[1];
equal(label.attributes.title, 'cool description', 'it has the correct title');
equal(label.children[0].data, 'hello', 'it has the category name');
});
test("undefined color", function() {
- var noColor = Discourse.Category.create({ name: 'hello', id: 123 }),
- tag = parseHTML(categoryBadgeHTML(noColor))[0];
+ const store = createStore();
+ const noColor = store.createRecord('category', { name: 'hello', id: 123 });
+ const tag = parseHTML(categoryBadgeHTML(noColor))[0];
blank(tag.attributes.style, "it has no color style because there are no colors");
});
test("allowUncategorized", function() {
- var uncategorized = Discourse.Category.create({name: 'uncategorized', id: 345});
+ const store = createStore();
+ const uncategorized = store.createRecord('category', {name: 'uncategorized', id: 345});
sandbox.stub(Discourse.Site, 'currentProp').withArgs('uncategorized_category_id').returns(345);
blank(categoryBadgeHTML(uncategorized), "it doesn't return HTML for uncategorized by default");
diff --git a/test/javascripts/models/category-test.js.es6 b/test/javascripts/models/category-test.js.es6
index b005ec1167c..48735f88e42 100644
--- a/test/javascripts/models/category-test.js.es6
+++ b/test/javascripts/models/category-test.js.es6
@@ -1,27 +1,30 @@
-module("Discourse.Category");
+import createStore from 'helpers/create-store';
+
+module("model:category");
test('slugFor', function(){
+ const store = createStore();
- var slugFor = function(cat, val, text) {
+ const slugFor = function(cat, val, text) {
equal(Discourse.Category.slugFor(cat), val, text);
};
- slugFor(Discourse.Category.create({slug: 'hello'}), "hello", "It calculates the proper slug for hello");
- slugFor(Discourse.Category.create({id: 123, slug: ''}), "123-category", "It returns id-category for empty strings");
- slugFor(Discourse.Category.create({id: 456}), "456-category", "It returns id-category for undefined slugs");
- slugFor(Discourse.Category.create({slug: '熱帶風暴畫眉'}), "熱帶風暴畫眉", "It can be non english characters");
+ slugFor(store.createRecord('category', {slug: 'hello'}), "hello", "It calculates the proper slug for hello");
+ slugFor(store.createRecord('category', {id: 123, slug: ''}), "123-category", "It returns id-category for empty strings");
+ slugFor(store.createRecord('category', {id: 456}), "456-category", "It returns id-category for undefined slugs");
+ slugFor(store.createRecord('category', {slug: '熱帶風暴畫眉'}), "熱帶風暴畫眉", "It can be non english characters");
- var parentCategory = Discourse.Category.create({id: 345, slug: 'darth'});
- slugFor(Discourse.Category.create({slug: 'luke', parentCategory: parentCategory}),
+ const parentCategory = store.createRecord('category', {id: 345, slug: 'darth'});
+ slugFor(store.createRecord('category', {slug: 'luke', parentCategory: parentCategory}),
"darth/luke",
"it uses the parent slug before the child");
- slugFor(Discourse.Category.create({id: 555, parentCategory: parentCategory}),
+ slugFor(store.createRecord('category', {id: 555, parentCategory: parentCategory}),
"darth/555-category",
"it uses the parent slug before the child and then uses id");
parentCategory.set('slug', null);
- slugFor(Discourse.Category.create({id: 555, parentCategory: parentCategory}),
+ slugFor(store.createRecord('category', {id: 555, parentCategory: parentCategory}),
"345-category/555-category",
"it uses the parent before the child and uses ids for both");
});
@@ -30,12 +33,13 @@ test('slugFor', function(){
test('findBySlug', function() {
expect(6);
- var darth = Discourse.Category.create({id: 1, slug: 'darth'}),
- luke = Discourse.Category.create({id: 2, slug: 'luke', parentCategory: darth}),
- hurricane = Discourse.Category.create({id: 3, slug: '熱帶風暴畫眉'}),
- newsFeed = Discourse.Category.create({id: 4, slug: '뉴스피드', parentCategory: hurricane}),
- time = Discourse.Category.create({id: 5, slug: '时间', parentCategory: darth}),
- bah = Discourse.Category.create({id: 6, slug: 'bah', parentCategory: hurricane}),
+ const store = createStore();
+ const darth = store.createRecord('category', {id: 1, slug: 'darth'}),
+ luke = store.createRecord('category', {id: 2, slug: 'luke', parentCategory: darth}),
+ hurricane = store.createRecord('category', {id: 3, slug: '熱帶風暴畫眉'}),
+ newsFeed = store.createRecord('category', {id: 4, slug: '뉴스피드', parentCategory: hurricane}),
+ time = store.createRecord('category', {id: 5, slug: '时间', parentCategory: darth}),
+ bah = store.createRecord('category', {id: 6, slug: 'bah', parentCategory: hurricane}),
categoryList = [darth, luke, hurricane, newsFeed, time, bah];
sandbox.stub(Discourse.Category, 'list').returns(categoryList);
@@ -51,12 +55,13 @@ test('findBySlug', function() {
test('findSingleBySlug', function() {
expect(6);
- var darth = Discourse.Category.create({id: 1, slug: 'darth'}),
- luke = Discourse.Category.create({id: 2, slug: 'luke', parentCategory: darth}),
- hurricane = Discourse.Category.create({id: 3, slug: '熱帶風暴畫眉'}),
- newsFeed = Discourse.Category.create({id: 4, slug: '뉴스피드', parentCategory: hurricane}),
- time = Discourse.Category.create({id: 5, slug: '时间', parentCategory: darth}),
- bah = Discourse.Category.create({id: 6, slug: 'bah', parentCategory: hurricane}),
+ const store = createStore();
+ const darth = store.createRecord('category', {id: 1, slug: 'darth'}),
+ luke = store.createRecord('category', {id: 2, slug: 'luke', parentCategory: darth}),
+ hurricane = store.createRecord('category', {id: 3, slug: '熱帶風暴畫眉'}),
+ newsFeed = store.createRecord('category', {id: 4, slug: '뉴스피드', parentCategory: hurricane}),
+ time = store.createRecord('category', {id: 5, slug: '时间', parentCategory: darth}),
+ bah = store.createRecord('category', {id: 6, slug: 'bah', parentCategory: hurricane}),
categoryList = [darth, luke, hurricane, newsFeed, time, bah];
sandbox.stub(Discourse.Category, 'list').returns(categoryList);
@@ -70,9 +75,10 @@ test('findSingleBySlug', function() {
});
test('findByIds', function() {
- var categories = {
- 1: Discourse.Category.create({id: 1}),
- 2: Discourse.Category.create({id: 2})
+ const store = createStore();
+ const categories = {
+ 1: store.createRecord('category', {id: 1}),
+ 2: store.createRecord('category', {id: 2})
};
sandbox.stub(Discourse.Category, 'idMap').returns(categories);
@@ -80,13 +86,14 @@ test('findByIds', function() {
});
test('postCountStats', function() {
- var category1 = Discourse.Category.create({id: 1, slug: 'unloved', posts_year: 2, posts_month: 0, posts_week: 0, posts_day: 0}),
- category2 = Discourse.Category.create({id: 2, slug: 'hasbeen', posts_year: 50, posts_month: 4, posts_week: 0, posts_day: 0}),
- category3 = Discourse.Category.create({id: 3, slug: 'solastweek', posts_year: 250, posts_month: 200, posts_week: 50, posts_day: 0}),
- category4 = Discourse.Category.create({id: 4, slug: 'hotstuff', posts_year: 500, posts_month: 280, posts_week: 100, posts_day: 22}),
- category5 = Discourse.Category.create({id: 6, slug: 'empty', posts_year: 0, posts_month: 0, posts_week: 0, posts_day: 0});
+ const store = createStore();
+ const category1 = store.createRecord('category', {id: 1, slug: 'unloved', posts_year: 2, posts_month: 0, posts_week: 0, posts_day: 0}),
+ category2 = store.createRecord('category', {id: 2, slug: 'hasbeen', posts_year: 50, posts_month: 4, posts_week: 0, posts_day: 0}),
+ category3 = store.createRecord('category', {id: 3, slug: 'solastweek', posts_year: 250, posts_month: 200, posts_week: 50, posts_day: 0}),
+ category4 = store.createRecord('category', {id: 4, slug: 'hotstuff', posts_year: 500, posts_month: 280, posts_week: 100, posts_day: 22}),
+ category5 = store.createRecord('category', {id: 6, slug: 'empty', posts_year: 0, posts_month: 0, posts_week: 0, posts_day: 0});
- var result = category1.get('postCountStats');
+ let result = category1.get('postCountStats');
equal(result.length, 1, "should only show year");
equal(result[0].value, 2);
equal(result[0].unit, 'year');
diff --git a/test/javascripts/models/composer-test.js.es6 b/test/javascripts/models/composer-test.js.es6
index 08559b8bca8..c9c2e1dc87e 100644
--- a/test/javascripts/models/composer-test.js.es6
+++ b/test/javascripts/models/composer-test.js.es6
@@ -1,6 +1,8 @@
import { blank } from 'helpers/qunit-helpers';
import { currentUser } from 'helpers/qunit-helpers';
import KeyValueStore from 'discourse/lib/key-value-store';
+import Composer from 'discourse/models/composer';
+import createStore from 'helpers/create-store';
module("model:composer");
@@ -9,10 +11,13 @@ const keyValueStore = new KeyValueStore("_test_composer");
function createComposer(opts) {
opts = opts || {};
opts.user = opts.user || currentUser();
- opts.site = Discourse.Site.current();
- opts.siteSettings = Discourse.SiteSettings;
- opts.keyValueStore = keyValueStore;
-; return Discourse.Composer.create(opts);
+ return createStore().createRecord('composer', opts);
+}
+
+function openComposer(opts) {
+ const composer = createComposer(opts);
+ composer.open(opts);
+ return composer;
}
test('replyLength', function() {
@@ -111,7 +116,7 @@ test("Title length for regular topics", function() {
test("Title length for private messages", function() {
Discourse.SiteSettings.min_private_message_title_length = 5;
Discourse.SiteSettings.max_topic_title_length = 10;
- const composer = createComposer({action: Discourse.Composer.PRIVATE_MESSAGE});
+ const composer = createComposer({action: Composer.PRIVATE_MESSAGE});
composer.set('title', 'asdf');
ok(!composer.get('titleLengthValid'), "short titles are not valid");
@@ -126,7 +131,7 @@ test("Title length for private messages", function() {
test("Title length for private messages", function() {
Discourse.SiteSettings.min_private_message_title_length = 5;
Discourse.SiteSettings.max_topic_title_length = 10;
- const composer = createComposer({action: Discourse.Composer.PRIVATE_MESSAGE});
+ const composer = createComposer({action: Composer.PRIVATE_MESSAGE});
composer.set('title', 'asdf');
ok(!composer.get('titleLengthValid'), "short titles are not valid");
@@ -143,7 +148,7 @@ test('editingFirstPost', function() {
ok(!composer.get('editingFirstPost'), "it's false by default");
const post = Discourse.Post.create({id: 123, post_number: 2});
- composer.setProperties({post: post, action: Discourse.Composer.EDIT });
+ composer.setProperties({post: post, action: Composer.EDIT });
ok(!composer.get('editingFirstPost'), "it's false when not editing the first post");
post.set('post_number', 1);
@@ -170,19 +175,19 @@ test('clearState', function() {
test('initial category when uncategorized is allowed', function() {
Discourse.SiteSettings.allow_uncategorized_topics = true;
- const composer = Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
+ const composer = openComposer({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
equal(composer.get('categoryId'),undefined,"Uncategorized by default");
});
test('initial category when uncategorized is not allowed', function() {
Discourse.SiteSettings.allow_uncategorized_topics = false;
- const composer = Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
+ const composer = openComposer({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
ok(composer.get('categoryId') === undefined, "Uncategorized by default. Must choose a category.");
});
test('showPreview', function() {
const newComposer = function() {
- return Discourse.Composer.open({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
+ return openComposer({action: 'createTopic', draftKey: 'asfd', draftSequence: 1});
};
Discourse.Mobile.mobileView = true;
@@ -199,7 +204,7 @@ test('showPreview', function() {
test('open with a quote', function() {
const quote = '[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]';
const newComposer = function() {
- return Discourse.Composer.open({action: Discourse.Composer.REPLY, draftKey: 'asfd', draftSequence: 1, quote: quote});
+ return openComposer({action: Discourse.Composer.REPLY, draftKey: 'asfd', draftSequence: 1, quote: quote});
};
equal(newComposer().get('originalText'), quote, "originalText is the quote" );
@@ -212,7 +217,7 @@ test("Title length for static page topics as admin", function() {
const composer = createComposer();
const post = Discourse.Post.create({id: 123, post_number: 2, static_doc: true});
- composer.setProperties({post: post, action: Discourse.Composer.EDIT });
+ composer.setProperties({post: post, action: Composer.EDIT });
composer.set('title', 'asdf');
ok(composer.get('titleLengthValid'), "admins can use short titles");
diff --git a/test/javascripts/models/site-test.js.es6 b/test/javascripts/models/site-test.js.es6
index 65804504d40..bc1c3c0ac94 100644
--- a/test/javascripts/models/site-test.js.es6
+++ b/test/javascripts/models/site-test.js.es6
@@ -1,14 +1,14 @@
+import createStore from 'helpers/create-store';
import { blank, present } from 'helpers/qunit-helpers';
-module("Discourse.Site");
+module("model:site");
test('create', function() {
ok(Discourse.Site.create(), 'it can create with no parameters');
});
test('instance', function() {
-
- var site = Discourse.Site.current();
+ const site = Discourse.Site.current();
present(site, "We have a current site singleton");
present(site.get('categories'), "The instance has a list of categories");
@@ -18,24 +18,24 @@ test('instance', function() {
});
test('create categories', function() {
-
- var site = Discourse.Site.create({
+ const store = createStore();
+ const site = store.createRecord('site', {
categories: [{ id: 1234, name: 'Test'},
{ id: 3456, name: 'Test Subcategory', parent_category_id: 1234},
- { id: 3456, name: 'Invalid Subcategory', parent_category_id: 6666}]
+ { id: 3458, name: 'Invalid Subcategory', parent_category_id: 6666}]
});
- var categories = site.get('categories');
+ const categories = site.get('categories');
site.get('sortedCategories');
present(categories, "The categories are present");
equal(categories.length, 3, "it loaded all three categories");
- var parent = categories.findBy('id', 1234);
+ const parent = categories.findBy('id', 1234);
present(parent, "it loaded the parent category");
blank(parent.get('parentCategory'), 'it has no parent category');
- var subcategory = categories.findBy('id', 3456);
+ const subcategory = categories.findBy('id', 3456);
present(subcategory, "it loaded the subcategory");
equal(subcategory.get('parentCategory'), parent, "it has associated the child with the parent");
diff --git a/test/javascripts/models/topic-tracking-state-test.js.es6 b/test/javascripts/models/topic-tracking-state-test.js.es6
index 305fc1d1729..f135584ee2f 100644
--- a/test/javascripts/models/topic-tracking-state-test.js.es6
+++ b/test/javascripts/models/topic-tracking-state-test.js.es6
@@ -1,21 +1,19 @@
-module("Discourse.TopicTrackingState");
+import TopicTrackingState from 'discourse/models/topic-tracking-state';
-test("sync", function () {
+module("model:topic-tracking-state");
- var state = Discourse.TopicTrackingState.create();
- // fake track it
+test("sync", function (assert) {
+ const state = TopicTrackingState.create();
state.states["t111"] = {last_read_post_number: null};
state.updateSeen(111, 7);
- var list = {topics: [{
+ const list = {topics: [{
highest_post_number: null,
id: 111,
unread: 10,
new_posts: 10
- }]};
+ }]};
state.sync(list, "new");
-
- equal(list.topics.length, 0, "expect new topic to be removed as it was seen");
-
+ assert.equal(list.topics.length, 0, "expect new topic to be removed as it was seen");
});
From a83312513e27441fb24a1e70a704ddc45d6dadcc Mon Sep 17 00:00:00 2001
From: Robin Ward
Date: Fri, 4 Sep 2015 13:07:51 -0400
Subject: [PATCH 067/224] Move mobile toggle to the bottom of the hamburger
---
.../discourse/templates/components/hamburger-menu.hbs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs b/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs
index afeb60bc450..76289134c0a 100644
--- a/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs
+++ b/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs
@@ -50,10 +50,6 @@
{{plugin-outlet "site-map-links"}}
- {{#if showMobileToggle}}
-
diff --git a/app/assets/javascripts/discourse/templates/navigation/categories.hbs b/app/assets/javascripts/discourse/templates/navigation/categories.hbs
index 6ba5509e9bb..c6b4c1cbbc9 100644
--- a/app/assets/javascripts/discourse/templates/navigation/categories.hbs
+++ b/app/assets/javascripts/discourse/templates/navigation/categories.hbs
@@ -3,7 +3,10 @@
{{navigation-bar navItems=navItems filterMode=filterMode}}
{{#if canCreateCategory}}
-
+ {{d-button action="createCategory" icon="plus" label="category.create"}}
+ {{#if siteSettings.fixed_category_positions}}
+ {{d-button action="reorderCategories" icon="random" label="category.reorder"}}
+ {{/if}}
{{/if}}
{{#if canCreateTopic}}
diff --git a/app/assets/javascripts/discourse/views/reorder-categories.js.es6 b/app/assets/javascripts/discourse/views/reorder-categories.js.es6
new file mode 100644
index 00000000000..570c9ec9637
--- /dev/null
+++ b/app/assets/javascripts/discourse/views/reorder-categories.js.es6
@@ -0,0 +1,80 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+ title: I18n.t('categories.reorder.title'),
+ templateName: 'modal/reorder-categories',
+
+ _setup: function() {
+ this.get('controller').on('scrollIntoView', this, this.scrollIntoView);
+ }.on('didInsertElement'),
+ _teardown: function() {
+ this.get('controller').off('scrollIntoView', this, this.scrollIntoView);
+ this.set('prevScrollElem', null);
+ }.on('willClearRender'),
+
+ scrollIntoView() {
+ const elem = this.$('tr[data-category-id="' + this.get('controller.scrollIntoViewId') + '"]');
+ const scrollParent = this.$('.modal-body');
+ const eoff = elem.position();
+ const poff = $(document.getElementById('rc-scroll-anchor')).position();
+ const currHeight = scrollParent.height();
+
+ elem[0].className = "highlighted";
+
+ const goal = eoff.top - poff.top - currHeight / 2,
+ current = scrollParent.scrollTop();
+ scrollParent.scrollTop(9999999);
+ const max = scrollParent.scrollTop();
+ scrollParent.scrollTop(current);
+
+ const doneTimeout = setTimeout(function() {
+ elem[0].className = "highlighted done";
+ setTimeout(function() {
+ elem[0].className = "";
+ }, 2000);
+ }, 0);
+
+ if (goal > current - currHeight / 4 && goal < current + currHeight / 4) {
+ // Too close to goal
+ return;
+ }
+ if (max - current < 10 && goal > current) {
+ // Too close to bottom
+ return;
+ }
+ if (current < 10 && goal < current) {
+ // Too close to top
+ return;
+ }
+
+ if (!window.requestAnimationFrame) {
+ scrollParent.scrollTop(goal);
+ } else {
+ clearTimeout(doneTimeout);
+ const startTime = performance.now();
+ const duration = 100;
+
+ function doScroll(timestamp) {
+ let progress = (timestamp - startTime) / duration;
+ if (progress > 1) {
+ progress = 1;
+ setTimeout(function() {
+ elem[0].className = "highlighted done";
+ setTimeout(function() {
+ elem[0].className = "";
+ }, 2000);
+ }, 0);
+ } else if (progress < 0) {
+ progress = 0;
+ }
+ if (progress < 1) {
+ window.requestAnimationFrame(doScroll);
+ }
+
+ const iprogress = 1 - progress;
+ scrollParent.scrollTop(goal * progress + current * iprogress);
+ }
+ window.requestAnimationFrame(doScroll);
+ }
+ }
+});
diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js
index 0ff24305876..5ea3455611d 100644
--- a/app/assets/javascripts/main_include.js
+++ b/app/assets/javascripts/main_include.js
@@ -94,13 +94,11 @@
//= require ./discourse/lib/export-result
//= require ./discourse/dialects/dialect
//= require ./discourse/lib/emoji/emoji
-//= require ./discourse/lib/sharing
-//= require discourse/lib/desktop-notifications
+//= require_tree ./discourse/lib
//= require ./discourse/router
//= require_tree ./discourse/dialects
//= require_tree ./discourse/controllers
-//= require_tree ./discourse/lib
//= require_tree ./discourse/models
//= require_tree ./discourse/components
//= require_tree ./discourse/views
diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss
index d1de50da4e4..577f9aba1c5 100644
--- a/app/assets/stylesheets/admin.scss
+++ b/app/assets/stylesheets/admin.scss
@@ -1 +1,2 @@
-@import "common/admin/admin_base"
+@import "common/admin/admin_base";
+@import "common/admin/cat_reorder";
diff --git a/app/assets/stylesheets/common/admin/cat_reorder.scss b/app/assets/stylesheets/common/admin/cat_reorder.scss
new file mode 100644
index 00000000000..d5619e993c3
--- /dev/null
+++ b/app/assets/stylesheets/common/admin/cat_reorder.scss
@@ -0,0 +1,28 @@
+.reorder-categories {
+ input {
+ width: 4em;
+ }
+ .th-pos {
+ width: calc(4em + 150px);
+ }
+ tbody tr {
+ background-color: transparent;
+ transition: background 0s ease;
+ &.highlighted {
+ background-color: rgba($highlight, 0.4);
+ &.done {
+ background-color: transparent;
+ transition-duration: 1s;
+ }
+ }
+ &:first-child td {
+ padding-top: 7px;
+ }
+ }
+ tbody {
+ border-bottom: 1px solid blend-primary-secondary(50%);
+ }
+ table {
+ padding-bottom: 150px;
+ }
+}
diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss
index c8cedb4a6cd..d8b47398043 100644
--- a/app/assets/stylesheets/common/base/discourse.scss
+++ b/app/assets/stylesheets/common/base/discourse.scss
@@ -144,6 +144,9 @@ body {
}
}
+input[type].invalid {
+ background-color: dark-light-choose(scale-color($danger, $lightness: 80%), scale-color($danger, $lightness: -60%));
+}
.wmd-input {
resize: none;
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
index 9d3ac191e9c..b63ec503dbf 100644
--- a/app/controllers/categories_controller.rb
+++ b/app/controllers/categories_controller.rb
@@ -37,7 +37,7 @@ class CategoriesController < ApplicationController
end
def move
- guardian.ensure_can_create!(Category)
+ guardian.ensure_can_create_category!
params.require("category_id")
params.require("position")
@@ -50,6 +50,24 @@ class CategoriesController < ApplicationController
end
end
+ def reorder
+ guardian.ensure_can_create_category!
+
+ params.require(:mapping)
+ change_requests = MultiJson.load(params[:mapping])
+ by_category = Hash[change_requests.map { |cat, pos| [Category.find(cat.to_i), pos] }]
+
+ unless guardian.is_admin?
+ raise Discourse::InvalidAccess unless by_category.keys.all? { |c| guardian.can_see_category? c }
+ end
+
+ by_category.each do |cat, pos|
+ cat.position = pos
+ cat.save if cat.position_changed?
+ end
+ render json: success_json
+ end
+
def show
if Category.topic_create_allowed(guardian).where(id: @category.id).exists?
@category.permission = CategoryGroup.permission_types[:full]
diff --git a/app/serializers/basic_category_serializer.rb b/app/serializers/basic_category_serializer.rb
index 96b0268cfae..f62968a59f2 100644
--- a/app/serializers/basic_category_serializer.rb
+++ b/app/serializers/basic_category_serializer.rb
@@ -7,6 +7,7 @@ class BasicCategorySerializer < ApplicationSerializer
:slug,
:topic_count,
:post_count,
+ :position,
:description,
:description_text,
:topic_url,
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index e92de892cfe..06f06b09f58 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -367,6 +367,10 @@ en:
all_subcategories: "all"
no_subcategory: "none"
category: "Category"
+ reorder:
+ title: "Reorder Categories"
+ save: "Save Order"
+ apply_all: "Apply"
posts: "Posts"
topics: "Topics"
latest: "Latest"
@@ -1546,6 +1550,7 @@ en:
add_permission: "Add Permission"
this_year: "this year"
position: "position"
+ reorder: "Reorder"
default_position: "Default Position"
position_disabled: "Categories will be displayed in order of activity. To control the order of categories in lists, "
position_disabled_click: 'enable the "fixed category positions" setting.'
diff --git a/config/routes.rb b/config/routes.rb
index f10ea5c2e95..d2adc0cfdb4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -382,6 +382,7 @@ Discourse::Application.routes.draw do
resources :categories, :except => :show
post "category/:category_id/move" => "categories#move"
+ post "categories/reorder" => "categories#reorder"
post "category/:category_id/notifications" => "categories#set_notifications"
put "category/:category_id/slug" => "categories#update_slug"
diff --git a/spec/controllers/categories_controller_spec.rb b/spec/controllers/categories_controller_spec.rb
index e838b2e9714..f03cfd05e8c 100644
--- a/spec/controllers/categories_controller_spec.rb
+++ b/spec/controllers/categories_controller_spec.rb
@@ -95,6 +95,42 @@ describe CategoriesController do
end
+ describe "reorder" do
+ it "reorders the categories" do
+ admin = log_in(:admin)
+
+ c1 = Fabricate(:category)
+ c2 = Fabricate(:category)
+ c3 = Fabricate(:category)
+ c4 = Fabricate(:category)
+ if c3.id < c2.id
+ tmp = c3; c2 = c3; c3 = tmp;
+ end
+ c1.position = 8
+ c2.position = 6
+ c3.position = 7
+ c4.position = 5
+
+ payload = {}
+ payload[c1.id] = 4
+ payload[c2.id] = 6
+ payload[c3.id] = 6
+ payload[c4.id] = 5
+
+ xhr :post, :reorder, mapping: MultiJson.dump(payload)
+
+ SiteSetting.fixed_category_positions = true
+ list = CategoryList.new(Guardian.new(admin))
+ expect(list.categories).to eq([
+ Category.find(SiteSetting.uncategorized_category_id),
+ c1,
+ c4,
+ c2,
+ c3
+ ])
+ end
+ end
+
describe "update" do
it "requires the user to be logged in" do
From 335be272ffac925fe84970f460e2d933d8d98832 Mon Sep 17 00:00:00 2001
From: Sam
Date: Mon, 7 Sep 2015 11:57:50 +1000
Subject: [PATCH 080/224] FEATURE: implement capping of new/unread
We cap new and unread at 2/5th of SiteSetting.max_tracked_new_unread
This dynamic capping is applied under 2 conditions:
1. New capping is applied once every 15 minutes in the periodical job, this effectively ensures that usually even super active sites are capped at 200 new items
2. Unread capping is applied if a user hits max_tracked_new_unread,
meaning if new + unread == 500, we defer a job that runs within 15 minutes that will cap user at 200 unread
This logic ensures that at worst case a user gets "bad" numbers for 15 minutes and then the system goes ahead and fixes itself up
---
.../controllers/discovery/topics.js.es6 | 4 --
.../models/topic-tracking-state.js.es6 | 7 ---
.../discourse/templates/discovery/topics.hbs | 4 --
app/controllers/application_controller.rb | 6 ++-
app/jobs/scheduled/periodical_updates.rb | 10 ++++
app/models/topic_tracking_state.rb | 53 ++++++++++++++-----
app/models/topic_user.rb | 37 +++++++++++++
app/models/user.rb | 6 ++-
config/locales/client.en.yml | 1 -
config/site_settings.yml | 6 +++
spec/models/topic_tracking_state_spec.rb | 44 +++++++++++++++
11 files changed, 146 insertions(+), 32 deletions(-)
diff --git a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
index 45716ae2ee0..28a663e3858 100644
--- a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
+++ b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
@@ -85,10 +85,6 @@ const controllerOpts = {
return this.get('model.filter') === 'new' && this.get('model.topics.length') > 0;
}.property('model.filter', 'model.topics.length'),
- tooManyTracked: function(){
- return this.topicTrackingState.tooManyTracked();
- }.property(),
-
showDismissAtTop: function() {
return (this.isFilterPage(this.get('model.filter'), 'new') ||
this.isFilterPage(this.get('model.filter'), 'unread')) &&
diff --git a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
index 0005b6e6007..ded0d824c89 100644
--- a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
+++ b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6
@@ -236,7 +236,6 @@ const TopicTrackingState = Discourse.Model.extend({
},
countNew(category_id) {
- if (this.tooManyTracked()) { return(0); }
return _.chain(this.states)
.where(isNew)
.where(topic => topic.category_id === category_id || !category_id)
@@ -244,10 +243,6 @@ const TopicTrackingState = Discourse.Model.extend({
.length;
},
- tooManyTracked() {
- return this.initialStatesLength >= Discourse.SiteSettings.max_tracked_new_unread;
- },
-
resetNew() {
Object.keys(this.states).forEach(id => {
if (this.states[id].last_read_post_number === null) {
@@ -257,7 +252,6 @@ const TopicTrackingState = Discourse.Model.extend({
},
countUnread(category_id) {
- if (this.tooManyTracked()) { return(0); }
return _.chain(this.states)
.where(isUnread)
.where(topic => topic.category_id === category_id || !category_id)
@@ -266,7 +260,6 @@ const TopicTrackingState = Discourse.Model.extend({
},
countCategory(category_id) {
- if (this.tooManyTracked()) { return(0); }
let sum = 0;
_.each(this.states, function(topic){
if (topic.category_id === category_id) {
diff --git a/app/assets/javascripts/discourse/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/discovery/topics.hbs
index 6afe9d80f82..f3e452a4ab0 100644
--- a/app/assets/javascripts/discourse/templates/discovery/topics.hbs
+++ b/app/assets/javascripts/discourse/templates/discovery/topics.hbs
@@ -1,7 +1,3 @@
-{{#if tooManyTracked}}
-
{{{i18n 'topics.too_many_tracked'}}}
-{{/if}}
-
{{#if redirectedReason}}
{{redirectedReason}}
{{/if}}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 60b6ba9938b..d9fe0a4976b 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -309,7 +309,11 @@ class ApplicationController < ActionController::Base
def preload_current_user_data
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, scope: guardian, root: false)))
- serializer = ActiveModel::ArraySerializer.new(TopicTrackingState.report(current_user.id), each_serializer: TopicTrackingStateSerializer)
+ report = TopicTrackingState.report(current_user.id)
+ if report.length >= SiteSetting.max_tracked_new_unread.to_i
+ TopicUser.cap_unread_later(current_user.id)
+ end
+ serializer = ActiveModel::ArraySerializer.new(report, each_serializer: TopicTrackingStateSerializer)
store_preloaded("topicTrackingStates", MultiJson.dump(serializer))
end
diff --git a/app/jobs/scheduled/periodical_updates.rb b/app/jobs/scheduled/periodical_updates.rb
index 2254235a99d..625d67b8d82 100644
--- a/app/jobs/scheduled/periodical_updates.rb
+++ b/app/jobs/scheduled/periodical_updates.rb
@@ -32,6 +32,16 @@ module Jobs
user_id = hash[:profile].user_id
Discourse.handle_job_exception(hash[:ex], error_context(args, "Rebaking user id #{user_id}", user_id: user_id))
end
+
+ TopicUser.cap_unread_backlog!
+
+ offset = (SiteSetting.max_tracked_new_unread * (2/5.0)).to_i
+ last_new_topic = Topic.order('created_at desc').offset(offset).select(:created_at).first
+ if last_new_topic
+ SiteSetting.min_new_topics_time = last_new_topic.created_at.to_i
+ end
+
+ nil
end
end
diff --git a/app/models/topic_tracking_state.rb b/app/models/topic_tracking_state.rb
index b6fae15187a..0da784219c9 100644
--- a/app/models/topic_tracking_state.rb
+++ b/app/models/topic_tracking_state.rb
@@ -106,11 +106,12 @@ class TopicTrackingState
WHEN COALESCE(u.new_topic_duration_minutes, :default_duration) = :always THEN u.created_at
WHEN COALESCE(u.new_topic_duration_minutes, :default_duration) = :last_visit THEN COALESCE(u.previous_visit_at,u.created_at)
ELSE (:now::timestamp - INTERVAL '1 MINUTE' * COALESCE(u.new_topic_duration_minutes, :default_duration))
- END, us.new_since)",
+ END, us.new_since, :min_date)",
now: DateTime.now,
last_visit: User::NewTopicDuration::LAST_VISIT,
always: User::NewTopicDuration::ALWAYS,
- default_duration: SiteSetting.default_other_new_topic_duration_minutes
+ default_duration: SiteSetting.default_other_new_topic_duration_minutes,
+ min_date: Time.at(SiteSetting.min_new_topics_time).to_datetime
).where_values[0]
end
@@ -125,19 +126,49 @@ class TopicTrackingState
# This code needs to be VERY efficient as it is triggered via the message bus and may steal
# cycles from usual requests
#
-
- unread = TopicQuery.unread_filter(Topic).where_values.join(" AND ")
- new = TopicQuery.new_filter(Topic, "xxx").where_values.join(" AND ").gsub!("'xxx'", treat_as_new_topic_clause)
+ #
+ sql = report_raw_sql(topic_id: topic_id)
sql = <Dismiss New or Dismiss Posts"
bulk:
reset_read: "Reset Read"
delete: "Delete Topics"
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 9a8d8ade1ad..78f4b7b1416 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -910,6 +910,12 @@ uncategorized:
default: false
hidden: true
+ # Nothing past this threshold is ever considered new
+ # this is calculated dynamically every 15 minutes
+ min_new_topics_time:
+ default: 0
+ hidden: true
+
# Category IDs
lounge_category_id:
default: -1
diff --git a/spec/models/topic_tracking_state_spec.rb b/spec/models/topic_tracking_state_spec.rb
index 2d857e320a9..de8e3ff6b9e 100644
--- a/spec/models/topic_tracking_state_spec.rb
+++ b/spec/models/topic_tracking_state_spec.rb
@@ -39,6 +39,50 @@ describe TopicTrackingState do
expect(report.length).to eq(1)
end
+
+ it "correctly handles capping" do
+ $redis.del TopicUser.unread_cap_key
+
+ user = Fabricate(:user)
+
+ post1 = create_post
+ Fabricate(:post, topic: post1.topic)
+
+ post2 = create_post
+ Fabricate(:post, topic: post2.topic)
+
+ post3 = create_post
+ Fabricate(:post, topic: post3.topic)
+
+ tracking = {
+ notification_level: TopicUser.notification_levels[:tracking],
+ last_read_post_number: 1,
+ highest_seen_post_number: 1
+ }
+
+ TopicUser.change(user.id, post1.topic_id, tracking)
+ TopicUser.change(user.id, post2.topic_id, tracking)
+ TopicUser.change(user.id, post3.topic_id, tracking)
+
+ report = TopicTrackingState.report(user.id)
+ expect(report.length).to eq(3)
+
+ SiteSetting.max_tracked_new_unread = 5
+ # business logic, we allow for 2/5th new .. 2/5th unread ... 1/5th buffer
+
+ TopicUser.cap_unread_backlog!
+
+ report = TopicTrackingState.report(user.id)
+ expect(report.length).to eq(3)
+
+ TopicUser.cap_unread_later(user.id)
+ TopicUser.cap_unread_backlog!
+
+ report = TopicTrackingState.report(user.id)
+ expect(report.length).to eq(2)
+
+ end
+
it "correctly gets the tracking state" do
report = TopicTrackingState.report(user.id)
expect(report.length).to eq(0)
From d05bc64df87923c239982dc2a4d272129600f001 Mon Sep 17 00:00:00 2001
From: Arpit Jalan
Date: Mon, 7 Sep 2015 19:21:10 +0530
Subject: [PATCH 081/224] do not default button title tag to label
---
app/assets/javascripts/discourse/components/d-button.js.es6 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/assets/javascripts/discourse/components/d-button.js.es6 b/app/assets/javascripts/discourse/components/d-button.js.es6
index 5fcc00a0fc1..068e814cae2 100644
--- a/app/assets/javascripts/discourse/components/d-button.js.es6
+++ b/app/assets/javascripts/discourse/components/d-button.js.es6
@@ -8,9 +8,9 @@ export default Ember.Component.extend({
noText: Ember.computed.empty('translatedLabel'),
- @computed("title", "translatedLabel")
- translatedTitle(title, translatedLabel) {
- return title ? I18n.t(title) : translatedLabel;
+ @computed("title")
+ translatedTitle(title) {
+ if (title) return I18n.t(title);
},
@computed("label")
From 21f81979cb54b5fc4e95ed48d02a0827d8830f3f Mon Sep 17 00:00:00 2001
From: Arpit Jalan
Date: Mon, 7 Sep 2015 19:48:43 +0530
Subject: [PATCH 082/224] Update Translations
---
config/locales/client.ar.yml | 7 +-
config/locales/client.bs_BA.yml | 5 +-
config/locales/client.cs.yml | 5 +-
config/locales/client.da.yml | 57 +++++++++++-
config/locales/client.es.yml | 55 ++++++++++-
config/locales/client.fa_IR.yml | 5 +-
config/locales/client.fi.yml | 5 +-
config/locales/client.fr.yml | 1 -
config/locales/client.he.yml | 26 +++++-
config/locales/client.ja.yml | 5 +-
config/locales/client.ko.yml | 5 +-
config/locales/client.nl.yml | 32 ++++++-
config/locales/client.pl_PL.yml | 32 ++++++-
config/locales/client.pt.yml | 32 ++++++-
config/locales/client.ro.yml | 5 +-
config/locales/client.ru.yml | 11 ++-
config/locales/client.sq.yml | 5 +-
config/locales/client.sv.yml | 6 +-
config/locales/client.te.yml | 5 +-
config/locales/client.tr_TR.yml | 159 +++++++++++++++++++++++++++++++-
config/locales/client.uk.yml | 4 +-
config/locales/client.zh_CN.yml | 37 ++++++--
config/locales/client.zh_TW.yml | 110 ++++++++++++++++++++++
config/locales/server.ar.yml | 63 ++++++++++---
config/locales/server.bs_BA.yml | 2 -
config/locales/server.cs.yml | 2 -
config/locales/server.da.yml | 2 -
config/locales/server.de.yml | 2 -
config/locales/server.es.yml | 3 -
config/locales/server.fa_IR.yml | 2 -
config/locales/server.fi.yml | 3 -
config/locales/server.fr.yml | 3 -
config/locales/server.he.yml | 82 +++++++++++++++-
config/locales/server.it.yml | 2 -
config/locales/server.ja.yml | 3 -
config/locales/server.ko.yml | 2 -
config/locales/server.nb_NO.yml | 2 -
config/locales/server.nl.yml | 25 ++++-
config/locales/server.pl_PL.yml | 2 -
config/locales/server.pt.yml | 10 +-
config/locales/server.pt_BR.yml | 2 -
config/locales/server.ro.yml | 2 -
config/locales/server.ru.yml | 2 -
config/locales/server.sq.yml | 3 -
config/locales/server.sv.yml | 2 -
config/locales/server.te.yml | 2 -
config/locales/server.tr_TR.yml | 157 +++++++++++++++++++++++++++----
config/locales/server.uk.yml | 2 -
config/locales/server.zh_CN.yml | 10 +-
config/locales/server.zh_TW.yml | 2 -
50 files changed, 846 insertions(+), 162 deletions(-)
diff --git a/config/locales/client.ar.yml b/config/locales/client.ar.yml
index d939df1ad1c..96a75d1353e 100644
--- a/config/locales/client.ar.yml
+++ b/config/locales/client.ar.yml
@@ -123,7 +123,7 @@ ar:
two: "ساعتان"
few: "%{count} ساعات"
many: "%{count} ساعة"
- other: "%{count} ساعة"
+ other: "%{count} ساعات"
x_days:
zero: "0 يوم"
one: "يوم واحد"
@@ -663,7 +663,6 @@ ar:
title: "دعوة"
user: "المستخدمين المدعويين"
sent: "تم الإرسال"
- none: ".لم تقم بدعوة أي شخص إلى هنا حتى الأن"
truncated: "اظهار اوائل {{count}} المدعويين"
redeemed: "دعوات مستخدمة"
redeemed_tab: "محررة"
@@ -1702,6 +1701,8 @@ ar:
help: "قمت بتفضيل هذا الموضوع"
locked:
help: "هذا الموضوع مغلق, لن يتم قبول اي رد "
+ archived:
+ help: "هذا الموضوع مؤرشف,لن تستطيع أن تعدل عليه"
unpinned:
title: "غير مثبت"
help: "هذا الموضوع غير مثبت بالنسبة لك, سيتم عرضه بالترتيب العادي"
@@ -1711,8 +1712,6 @@ ar:
pinned:
title: "مثبت"
help: "هذا الموضوع مثبت لك, سوف يتم عرضه في اول القسم"
- archived:
- help: "هذا الموضوع مؤرشف,لن تستطيع أن تعدل عليه"
invisible:
help: "هذا الموضوع غير مصنف لن يظهر في قائمة التصانيف ولايمكن الدخول عليه الابرابط مباشر."
posts: "مشاركات"
diff --git a/config/locales/client.bs_BA.yml b/config/locales/client.bs_BA.yml
index 68010abc39a..3723992ecee 100644
--- a/config/locales/client.bs_BA.yml
+++ b/config/locales/client.bs_BA.yml
@@ -437,7 +437,6 @@ bs_BA:
search: "kucaj da potražiš pozivnice..."
title: "Pozivnice"
user: "Pozvan Korisnik"
- none: "You haven't invited anyone here yet."
truncated: "Showing the first {{count}} invites."
redeemed: "Redeemed Invites"
redeemed_at: "Redeemed"
@@ -1083,6 +1082,8 @@ bs_BA:
help: "Ovo je zvanično upozorenje."
locked:
help: "Ova tema je zatvorena; zvanično ne prima nove postove"
+ archived:
+ help: "Ova tema je arhivirana; zaleđena je i ne može biti promjenjena"
unpinned:
title: "Unpinned"
help: "This topic is unpinned; it will display in default order"
@@ -1092,8 +1093,6 @@ bs_BA:
pinned:
title: "Zakačena"
help: "Ova tema je zakačena; biće na vrhu svoje kategorije"
- archived:
- help: "Ova tema je arhivirana; zaleđena je i ne može biti promjenjena"
invisible:
help: "Ovu temu sajt ne lista među najnovijim temama. Neće biti prisutna ni među listama tema unutar kategorija. Jedini način da se dođe do ove teme je direktan link"
posts: "Odgovori"
diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml
index a7118147fb3..95dafdc65b5 100644
--- a/config/locales/client.cs.yml
+++ b/config/locales/client.cs.yml
@@ -524,7 +524,6 @@ cs:
search: "pište pro hledání v pozvánkách..."
title: "Pozvánky"
user: "Pozvaný uživatel"
- none: "Zatím jste nikoho nepozvali."
truncated: "Showing the first {{count}} invites."
redeemed: "Uplatněné pozvánky"
redeemed_tab: "Uplatněno"
@@ -1424,6 +1423,8 @@ cs:
help: "V tématu je vložena záložka"
locked:
help: "toto téma je uzavřené; další odpovědi nejsou přijímány"
+ archived:
+ help: "toto téma je archivováno; je zmraženo a nelze ho již měnit"
unpinned:
title: "Nepřipnuté"
help: "Pro vás toto téma není připnuté; bude se zobrazovat v běžném pořadí"
@@ -1433,8 +1434,6 @@ cs:
pinned:
title: "Připnuto"
help: "Pro vás je toto téma připnuté; bude se zobrazovat na vrcholu seznamu ve své kategorii"
- archived:
- help: "toto téma je archivováno; je zmraženo a nelze ho již měnit"
invisible:
help: "Toto téma je neviditelné; nebude se zobrazovat v seznamu témat a lze ho navštívit pouze přes přímý odkaz"
posts: "Příspěvků"
diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml
index 7295b6bc0f9..13eccd91861 100644
--- a/config/locales/client.da.yml
+++ b/config/locales/client.da.yml
@@ -108,6 +108,16 @@ da:
facebook: 'del dette link på Facebook'
google+: 'del dette link på Google+'
email: 'send dette link i en e-mail'
+ action_codes:
+ split_topic: "split dette emne op"
+ autoclosed:
+ enabled: 'lukket %{when}'
+ disabled: 'åbnet %{when}'
+ closed:
+ enabled: 'lukket %{when}'
+ disabled: 'åbnet %{when}'
+ archived:
+ enabled: 'arkiveret %{when}'
topic_admin_menu: "administrationshandlinger på emne"
emails_are_disabled: "Alle udgående emails er blevet deaktiveret globalt af en administrator. Ingen emailnotifikationer af nogen slags vil blive sendt."
edit: 'redigér titel og kategori for dette emne'
@@ -123,6 +133,7 @@ da:
admin_title: "Admin"
flags_title: "Flag"
show_more: "vis mere"
+ show_help: "indstillinger"
links: "Links"
links_lowercase:
one: "link"
@@ -333,6 +344,8 @@ da:
topics_entered: "emner indtastet"
post_count: "# indlæg"
confirm_delete_other_accounts: "Er du sikker på, at du vil slette disse kontoer?"
+ user_fields:
+ none: "(vælg en indstilling)"
user:
said: "{{username}}:"
profile: "Profil"
@@ -349,6 +362,14 @@ da:
invited_by: "Inviteret af"
trust_level: "Tillidsniveau"
notifications: "Underretninger"
+ desktop_notifications:
+ perm_denied_btn: "Tilladelse nægtet"
+ perm_denied_expl: "Du har ikke givet tilladelse til meddelelser. Brug din browser til at aktivere meddelelser og klik derefter på knappen, når du er færdig. (Computer: Ikonet længst til venstre i adresselinjen. Mobil: 'Site info'.)"
+ disable: "Deaktiver meddelelser"
+ currently_enabled: "(slået til)"
+ enable: "Aktiver meddelelser"
+ currently_disabled: "(slået fra)"
+ each_browser_note: "Bemærk: Du skal ændre indstillingen i alle dine browsere."
dismiss_notifications: "Marker alle som læst"
dismiss_notifications_tooltip: "Marker alle ulæste meddelelser som læst"
disable_jump_reply: "Ikke hop til mit indlæg efter jeg svarer"
@@ -361,6 +382,7 @@ da:
admin: "{{user}} er admin"
moderator_tooltip: "Dette bruger er moderator"
admin_tooltip: "Denne bruger er administrator"
+ blocked_tooltip: "Brugeren er blokeret"
suspended_notice: "Denne bruger er suspenderet indtil {{date}}."
suspended_reason: "Begrundelse: "
github_profile: "Github"
@@ -421,6 +443,7 @@ da:
upload_title: "Upload dit profil billede"
upload_picture: "Upload et billede"
image_is_not_a_square: "Advarsel: vi har klippet i billedet; bredde og højde var ikke ens."
+ cache_notice: "Du har ændret dit profilbillede, men der kan godt gå lidt tid inden ændringen træder i kraft."
change_profile_background:
title: "Profil baggrundsbillede"
instructions: "Profil baggrunde vil blive centrerede og have en standard bredde på 850 pixels"
@@ -487,15 +510,27 @@ da:
label: "Betragt emner som nye når"
not_viewed: "Jeg har ikke set dem endnu"
last_here: "oprettet siden jeg var her sidst"
+ after_1_day: "oprettet indenfor den seneste dag"
+ after_2_days: "oprettet i de seneste 2 dage"
+ after_1_week: "oprettet i seneste uge"
+ after_2_weeks: "oprettet i de seneste 2 uger"
auto_track_topics: "Følg automatisk emner jeg åbner"
auto_track_options:
never: "aldrig"
immediately: "med det samme"
+ after_30_seconds: "efter 30 sekunder"
+ after_1_minute: "efter 1 minut"
+ after_2_minutes: "efter 2 minutter"
+ after_3_minutes: "efter 3 minutter"
+ after_4_minutes: "efter 4 minutter"
+ after_5_minutes: "efter 5 minutter"
+ after_10_minutes: "efter 10 minutter"
invited:
search: "tast for at søge invitationer…"
title: "Invitationer"
user: "Inviteret bruger"
- none: "Du har ikke inviteret nogen endnu."
+ sent: "Sendt"
+ none: "Der er ingen afventende invitationer."
truncated: "Viser de første {{count}} invitationer."
redeemed: "Brugte invitationer"
redeemed_tab: "Indløst"
@@ -513,6 +548,7 @@ da:
days_visited: "Besøgsdage"
account_age_days: "Kontoens alder i dage"
create: "Send en invitation"
+ generate_link: "Kopier invitations-link"
bulk_invite:
none: "Du har ikke inviteret nogen her endnu. Du kan sende individuelle invitationer eller invitere en masse mennesker på én gang ved at uploade en samlet liste over invitationer."
text: "Masse invitering fra en fil"
@@ -778,6 +814,8 @@ da:
search:
title: "søg efter emner, indlæg, brugere eller kategorier"
no_results: "Ingen resultater fundet."
+ no_more_results: "Ikke flere resultater."
+ search_help: Hjælp til søgning
searching: "Søger…"
post_format: "#{{post_number}} af {{username}}"
context:
@@ -785,6 +823,7 @@ da:
category: "Søg i kategorien \"{{category}}\""
topic: "Søg i dette emne"
private_messages: "Søg i beskeder"
+ hamburger_menu: "gå til en anden emneliste eller kategori"
go_back: 'gå tilbage'
not_logged_in_user: 'bruger side, med oversigt over aktivitet og indstillinger'
current_user: 'gå til brugerside'
@@ -926,8 +965,10 @@ da:
title: "Følger"
description: "En optælling af nye svar vil blive vist for denne tråd. Du vil modtage en notifikation, hvis nogen nævner dit @name eller svarer dig."
regular:
+ title: "Normal"
description: "Du vil modtage en notifikation, hvis nogen nævner dit @name eller svarer dig."
regular_pm:
+ title: "Normal"
description: "Du vil modtage en notifikation, hvis nogen nævner dit @name eller svarer dig."
muted_pm:
title: "Lydløs"
@@ -1362,6 +1403,8 @@ da:
help: "Du har bogmærket dette emne"
locked:
help: "emnet er låst; det modtager ikke flere svar"
+ archived:
+ help: "emnet er arkiveret; det er frosset og kan ikke ændres"
unpinned:
title: "Ikke fastgjort"
help: "Dette emne er ikke fastgjort for dig; det vil blive vist i den normale rækkefølge"
@@ -1371,8 +1414,6 @@ da:
pinned:
title: "Fastgjort"
help: "Dette emne er fastgjort for dig; det vil blive vist i toppen af dets kategori"
- archived:
- help: "emnet er arkiveret; det er frosset og kan ikke ændres"
invisible:
help: "Dette emne er ulistet; det vil ikke blive vist i listen over emner og kan kun tilgås med et direkte link"
posts: "Indlæg"
@@ -2229,6 +2270,9 @@ da:
name: "Navn"
image: "Billede"
delete_confirm: "Er du sikker på du vil slette emotikonnet: %{name} ?"
+ embedding:
+ feed_polling_enabled: "Importer indlæg via RSS/ATOM"
+ feed_polling_url: "URL på RSS/ATOM feed der skal kravles"
permalink:
title: "Permalinks"
url: "URL"
@@ -2270,6 +2314,7 @@ da:
title: 'Applikation'
create: 'c Opret et nyt emne'
notifications: 'n Åbn notifikationer'
+ hamburger_menu: '= Åbn i hamburgermenu'
user_profile_menu: 'p Åben bruger menu'
show_incoming_updated_topics: '. Vis opdaterede emner'
search: '/ Søg'
@@ -2403,3 +2448,9 @@ da:
reader:
name: Læser
description: Har læst hver eneste indlæg i et emne med mere end 100 posts
+ popular_link:
+ name: Populært link
+ description: Postede et eksternt link med mindst 50 klik
+ hot_link:
+ name: Hot link
+ description: Postede et eksternt link med mindst 300 klik
diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml
index c4e9145217e..e9c723fbcf7 100644
--- a/config/locales/client.es.yml
+++ b/config/locales/client.es.yml
@@ -306,6 +306,9 @@ es:
mods_and_admins: "Solo moderadores y administradores"
members_mods_and_admins: "Solo miembros del grupo, moderadores y administradores"
everyone: "Todos"
+ trust_levels:
+ title: "Nivel de confianza entregado automáticamente a miembros cuando son añadidos:"
+ none: "Ninguno"
user_action_groups:
'1': "'Me gusta' Dados"
'2': "'Me gusta' Recibidos"
@@ -367,6 +370,7 @@ es:
private_messages: "Mensajes"
activity_stream: "Actividad"
preferences: "Preferencias"
+ expand_profile: "Expandir"
bookmarks: "Marcadores"
bio: "Acerca de mí"
invited_by: "Invitado Por"
@@ -543,7 +547,7 @@ es:
title: "Invitaciones"
user: "Invitar Usuario"
sent: "Enviadas"
- none: "No has invitado a nadie todavía."
+ none: "No hay ninguna invitación pendiente que mostrar."
truncated: "Mostrando las primeras {{count}} invitaciones."
redeemed: "Invitaciones aceptadas"
redeemed_tab: "Usado"
@@ -563,6 +567,8 @@ es:
days_visited: "Días Visitados"
account_age_days: "Antigüedad de la cuenta en días"
create: "Enviar una Invitación"
+ generate_link: "Copiar Enlace de Invitación"
+ generated_link_message: '
¡Enlace de Invitación generado con éxito!
Este enlace de Invitación es sólo válido para la siguiente dirección de email: %{invitedEmail}
'
bulk_invite:
none: "No has invitado a nadie todavía. Puedes enviar invitaciones individuales o invitar a un grupo de personas a la vez subiendo un archivo para invitaciones en masa."
text: "Archivo de Invitación en Masa"
@@ -619,6 +625,7 @@ es:
read_only_mode:
enabled: "Modo solo-lectura activado. Puedes continuar navegando por el sitio pero las interacciones podrían no funcionar."
login_disabled: "Iniciar sesión está desactivado mientras el foro esté en modo solo lectura."
+ too_few_topics_notice: "¡Vamos a empezar con la comunidad! Hay actualmente %{currentTopics} / %{requiredTopics} temas y %{currentPosts} / %{requiredPosts} posts. Los nuevos visitantes necesitan algunas conversaciones que leer y a las que responder."
learn_more: "saber más..."
year: 'año'
year_desc: 'temas creados en los últimos 365 días'
@@ -804,6 +811,20 @@ es:
moved_post: "
{{username}} movió {{description}}
"
linked: "
{{username}} {{description}}
"
granted_badge: "
Se te ha concedido '{{description}}'
"
+ alt:
+ mentioned: "Mencionado por"
+ quoted: "Citado por"
+ replied: "Respondido"
+ posted: "Publicado por"
+ edited: "Editado tu post por"
+ liked: "Gustado tu post"
+ private_message: "Mensaje privado de"
+ invited_to_private_message: "Invitado a un mensaje privado de"
+ invited_to_topic: "Invitado a un tema de"
+ invitee_accepted: "Invitación aceptada por"
+ moved_post: "Tu post fue eliminado por"
+ linked: "Enlace a tu post"
+ granted_badge: "Distintivo concedido"
popup:
mentioned: '{{username}} te mencionó en "{{topic}}" - {{site_title}}'
quoted: '{{username}} te citó en "{{topic}}" - {{site_title}}'
@@ -837,10 +858,12 @@ es:
category: "Buscar en la categoría \"{{category}}\""
topic: "Buscar en este tema"
private_messages: "Buscar en mensajes"
+ hamburger_menu: "ir a otra lista de temas o categoría"
go_back: 'volver'
not_logged_in_user: 'página con el resumen de actividad y preferencias'
current_user: 'ir a tu página de usuario'
topics:
+ too_many_tracked: "Atención: tienes demasiados temas seguidos y nuevos, quita algunos usando Ignorar nuevos o Ignorar posts"
bulk:
reset_read: "Restablecer leídos"
delete: "Eliminar temas"
@@ -1032,6 +1055,7 @@ es:
unpin: "Eliminar este tema del top de la categoría {{categoryLink}}."
unpin_until: "Quitar este tema del top de la categoría {{categoryLink}} o esperar al %{until}."
pin_note: "Los usuarios pueden desanclar el tema de forma individual por sí mismos."
+ pin_validation: "Es obligatorio especificar una fecha para destacar este tema."
already_pinned:
zero: "No hay temas destacados en {{categoryLink}}."
one: "Temas anclados actualmente en {{categoryLink}}: 1."
@@ -1366,6 +1390,7 @@ es:
email_in_allow_strangers: "Aceptar emails de usuarios anónimos sin cuenta"
email_in_disabled: "La posibilidad de publicar nuevos temas por email está deshabilitada en los ajustes del sitio. Para habilitar la publicación de nuevos temas por email,"
email_in_disabled_click: 'activa la opción "email in".'
+ suppress_from_homepage: "Ocultar categoría de la página de inicio."
allow_badges_label: "Permitir conceder distintivos en esta categoría"
edit_permissions: "Editar permisos"
add_permission: "Añadir permisos"
@@ -1431,6 +1456,10 @@ es:
help: "Has guardado en marcadores este tema."
locked:
help: "este tema está cerrado; ya no aceptan nuevas respuestas"
+ archived:
+ help: "este tema está archivado; está congelado y no puede ser cambiado"
+ locked_and_archived:
+ help: "Este tema está cerrado y archivado; no acepta nuevas respuestas y no puede ser cambiado de ningún modo."
unpinned:
title: "Deseleccionado como destacado"
help: "Este tema se ha dejado de destacar para ti; en tu listado de temas se mostrará en orden normal"
@@ -1440,8 +1469,6 @@ es:
pinned:
title: "Destacado"
help: "Este tema ha sido destacado para ti; se mostrará en la parte superior de su categoría"
- archived:
- help: "este tema está archivado; está congelado y no puede ser cambiado"
invisible:
help: "Este tema es invisible; no se mostrará en la lista de temas y solo puede acceder a él a través de su enlace directo."
posts: "Posts"
@@ -1892,6 +1919,7 @@ es:
sent_test: "enviado!"
delivery_method: "Método de entrega"
preview_digest: "Vista previa de Resumen"
+ preview_digest_desc: "Previsualiza el contenido del email de resumen enviado a usuarios inactivos."
refresh: "Actualizar"
format: "Formato"
html: "html"
@@ -2228,6 +2256,7 @@ es:
backups: "Copias de seguridad"
login: "Login"
plugins: "Plugins"
+ user_preferences: "Preferencias de los Usuarios"
badges:
title: Distintivos
new_badge: Nuevo distintivo
@@ -2314,6 +2343,13 @@ es:
feed_settings: "Ajustes de Feed"
feed_description: "Discourse podrá importar tu contenido de forma más fácil si proporcionas un feed RSS/ATOM de tu sitio."
crawling_settings: "Ajustes de Crawlers"
+ embed_by_username: "Usuario para la creación de temas"
+ embed_post_limit: "Máximo número de posts a incluir"
+ embed_username_key_from_feed: "Clave para extraer usuario de discourse del feed"
+ embed_truncate: "Truncar los posts insertados"
+ feed_polling_enabled: "Importar posts usando RSS/ATOM"
+ feed_polling_url: "URL del feed RSS/ATOM del que extraer datos"
+ save: "Guardar ajustes de Insertado"
permalink:
title: "Enlaces permanentes"
url: "URL"
@@ -2344,6 +2380,8 @@ es:
categories: 'g, c Categorías'
top: 'g, t Arriba'
bookmarks: 'g, b Marcadores'
+ profile: 'g, p Perfil'
+ messages: 'g, m Mensajes'
navigation:
title: 'Navegación'
jump: '# Ir al post #'
@@ -2355,12 +2393,14 @@ es:
title: 'Aplicación'
create: 'c Crear un tema nuevo'
notifications: 'n Abrir notificaciones'
+ hamburger_menu: '= Abrir Menú'
user_profile_menu: 'p Abrir menú de usuario'
show_incoming_updated_topics: '. Mostrar temas actualizados'
search: '/ Buscar'
help: '? Abrir la guía de atajos de teclado'
dismiss_new_posts: 'x, r Descartar Nuevo/Posts'
dismiss_topics: 'x, t Descartar Temas'
+ log_out: 'shift+zshift+z Cerrar sesión'
actions:
title: 'Acciones'
bookmark_topic: 'f Guardar/Quitar el tema de marcadores'
@@ -2488,6 +2528,15 @@ es:
reader:
name: Lector
description: Leyó todos los posts en un tema con más de 100
+ popular_link:
+ name: Enlace Popular
+ description: Publicó un enlace externo con al menos 50 clicks
+ hot_link:
+ name: Enlace Candente
+ description: Publicó un enlace externo con al menos 300 clicks
+ famous_link:
+ name: Enlace Famoso
+ description: Publicó un enlace externo con al menos 1000 clicks
google_search: |
Buscar con Google
diff --git a/config/locales/client.fa_IR.yml b/config/locales/client.fa_IR.yml
index 29bf483af87..55764ac69db 100644
--- a/config/locales/client.fa_IR.yml
+++ b/config/locales/client.fa_IR.yml
@@ -465,7 +465,6 @@ fa_IR:
search: "بنویسید تا فراخوانهها را جستجو کنید..."
title: "فراخوانهها"
user: "کاربر فراخوانده شده"
- none: "هنوز هیچکسی را به اینجا فرانخواندهاید"
truncated: "نمایش {{count}} فراخوانهٔ نخست"
redeemed: "آزاد سازی دعوتنامه"
redeemed_tab: "آزاد شده"
@@ -1296,6 +1295,8 @@ fa_IR:
help: "شما بر روی این موضوع نشانک گذاشتهاید."
locked:
help: "این موضوع بسته شده؛ پاسخهای تازه اینجا پذیرفته نمیشوند"
+ archived:
+ help: "این موضوع بایگانی شده؛ یخ زده و نمیتواند تغییر کند."
unpinned:
title: "خارج کردن از سنجاق"
help: "این موضوع برای شما شنجاق نشده است، آن طور منظم نمایش داده خواهد شد"
@@ -1305,8 +1306,6 @@ fa_IR:
pinned:
title: "سنجاق شد"
help: "این موضوع برای شما سنجاق شده است، آن طور منظم در بالای دسته بندی نمایش داده خواهد شد."
- archived:
- help: "این موضوع بایگانی شده؛ یخ زده و نمیتواند تغییر کند."
invisible:
help: "این موضوع از لیست خارج شد: آن درلیست موضوعات نمایش داده نخواهد شد، و فقط از طریق لینک مستقیم در دسترس خواهد بود. "
posts: "نوشتهها"
diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml
index 6ba830792b6..068c9c1e501 100644
--- a/config/locales/client.fi.yml
+++ b/config/locales/client.fi.yml
@@ -512,7 +512,6 @@ fi:
search: "kirjoita etsiäksesi kutsuja..."
title: "Kutsut"
user: "Kutsuttu käyttäjä"
- none: "Et ole vielä kutsunut ketään."
truncated: "Näytetään ensimmäiset {{count}} kutsua."
redeemed: "Hyväksytyt kutsut"
redeemed_tab: "Hyväksytyt"
@@ -1387,6 +1386,8 @@ fi:
help: "Olet lisännyt ketjun kirjanmerkkeihisi"
locked:
help: "Tämä ketju on suljettu; siihen ei voi enää vastata."
+ archived:
+ help: "Tämä ketju on arkistoitu; se on jäädytetty eikä sitä voi muuttaa"
unpinned:
title: "Kiinnitys poistettu"
help: "Ketjun kiinnitys on poistettu sinulta; se näytetään tavallisessa järjestyksessä."
@@ -1396,8 +1397,6 @@ fi:
pinned:
title: "Kiinnitetty"
help: "Tämä ketju on kiinnitetty sinulle; se näytetään alueensa ensimmäisenä"
- archived:
- help: "Tämä ketju on arkistoitu; se on jäädytetty eikä sitä voi muuttaa"
invisible:
help: "Tämä ketju on poistettu listauksista; sitä ei näytetä ketjujen listauksissa ja siihen pääsee vain suoralla linkillä"
posts: "Viestejä"
diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml
index 39f0d752d38..fecec8cd58f 100644
--- a/config/locales/client.fr.yml
+++ b/config/locales/client.fr.yml
@@ -845,7 +845,6 @@ fr:
not_logged_in_user: 'page utilisateur avec un résumé de l''activité en cours et les préférences '
current_user: 'voir la page de l''utilisateur'
topics:
- too_many_tracked: "Avertissement: vous avez trop de sujets nouveaux ou non-lus, il faut en libérer avec \"Ignorer nouveaux\" ou \"Ignorer messages\""
bulk:
reset_read: "Réinitialiser la lecture"
delete: "Supprimer les sujets"
diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml
index bb87353c19f..e6cee16c726 100644
--- a/config/locales/client.he.yml
+++ b/config/locales/client.he.yml
@@ -543,7 +543,7 @@ he:
title: "הזמנות"
user: "משתמש/ת שהוזמנו"
sent: "נשלח"
- none: "עוד לא הזמנת לכאן אף אחד."
+ none: "אין הזמנות ממתינות להציג"
truncated: "מראה את {{count}} ההזמנות הראשונות."
redeemed: "הזמנות נוצלו"
redeemed_tab: "נענו"
@@ -845,7 +845,6 @@ he:
not_logged_in_user: 'עמוד משתמש עם סיכום פעילות נוכחית והעדפות'
current_user: 'לך לעמוד המשתמש שלך'
topics:
- too_many_tracked: "אזהרה: יש לך יותר מדי נושאים חדשים שלא נקראו במעקב. נקה חלק בעזרת \"התעלם מחדשים\" או \"התעלם מפרסומים\""
bulk:
reset_read: "איפוס נקראו"
delete: "מחיקת נושאים"
@@ -1437,6 +1436,10 @@ he:
help: "יצרת סימניה לנושא זה"
locked:
help: "הנושא הזה נעול, הוא לא מקבל יותר תגובות חדשות"
+ archived:
+ help: "הנושא הזה אוכסן בארכיון; הוא הוקפא ולא ניתן לשנותו"
+ locked_and_archived:
+ help: "הנושא הזה סגור ומאורכב. לא ניתן להגיב בו יותר או לשנות אותו. "
unpinned:
title: "הורד מנעיצה"
help: "נושא זה אינו מקובע עבורך; הוא יופיע בסדר הרגיל"
@@ -1446,8 +1449,6 @@ he:
pinned:
title: "נעוץ"
help: "נושא זה מקובע עבורך, הוא יופיע בראש הקטגוריה"
- archived:
- help: "הנושא הזה אוכסן בארכיון; הוא הוקפא ולא ניתן לשנותו"
invisible:
help: "נושא זה מוסתר; הוא לא יוצג ברשימות הנושאים, וזמין רק באמצעות קישור ישיר."
posts: "הודעות"
@@ -1543,6 +1544,8 @@ he:
title: "תמיד"
yearly:
title: "שנתי"
+ quarterly:
+ title: "רבעוני"
monthly:
title: "חודשי"
weekly:
@@ -1551,6 +1554,7 @@ he:
title: "יומי"
all_time: "כל הזמנים"
this_year: "שנה"
+ this_quarter: "רבע"
this_month: "חודש"
this_week: "שבוע"
today: "היום"
@@ -1721,6 +1725,9 @@ he:
none_installed: "אין לך הרחבות מותקנות"
version: "גרסה"
enabled: "מאופשר?"
+ is_enabled: "Y"
+ not_enabled: "N"
+ cant_disable: "-"
change_settings: "שינוי הגדרות"
change_settings_short: "הגדרות"
howto: "איך אני מתקין/מתקינה הרחבות?"
@@ -1800,6 +1807,7 @@ he:
header: "כותרת"
top: "למעלה"
footer: "כותרת תחתית"
+ embedded_css: "Embedded CSS"
head_tag:
text: ""
title: "קוד HTML שיוכנס לפני התגית "
@@ -1891,6 +1899,7 @@ he:
sent_test: "נשלח!"
delivery_method: "שיטת העברה"
preview_digest: "תצוגה מקדימה של סיכום"
+ preview_digest_desc: "תצוגה מקדימה של מייל סיכום שנשלח למשתמשים לא פעילים. "
refresh: "רענן"
format: "פורמט"
html: "html"
@@ -1955,6 +1964,7 @@ he:
delete_post: "מחיקת פרסום"
impersonate: "התחזה"
anonymize_user: "הפיכת משתמש/ת לאנונימיים"
+ roll_up: "roll up IP blocks"
screened_emails:
title: "הודעות דואר מסוננות"
description: "כשמישהו מנסה ליצור חשבון חדש, כתובות הדואר האלקטרוני הבאות ייבדקו וההרשמה תחסם או שיבוצו פעולות אחרות."
@@ -2176,6 +2186,7 @@ he:
delete: "מחיקה"
cancel: "ביטול"
delete_confirm: "האם את/ה בטוחים שאתם רוצים למחוק את שדה משתמש/ת הזה?"
+ options: "אפשרויות"
required:
title: "נדרש בעת הרשמה?"
enabled: "נדרש"
@@ -2191,6 +2202,7 @@ he:
field_types:
text: 'שדה טקסט'
confirm: 'אישור'
+ dropdown: "נגלל"
site_text:
none: "בחרו את סוג התוכן לתחילת עריכה."
title: 'תוכן טקסטואלי'
@@ -2224,6 +2236,7 @@ he:
backups: "גיבויים"
login: "התחברות"
plugins: "הרחבות"
+ user_preferences: "הגדרות משתמש"
badges:
title: תגים
new_badge: תג חדש
@@ -2297,6 +2310,11 @@ he:
name: "שם"
image: "תמונה"
delete_confirm: "האם את/ה בטוח/ה שאתם רוצים למחוק את האמוג'י :%{name}:?"
+ embedding:
+ get_started: "אם ברצונך לשלב את דיסקורס באתר אחר, התחל בהוספת המערך שלו (host). "
+ confirm_delete: "האם אתה בטוח שאתה רוצה למחוק את הhost הזה? "
+ sample: "השתמש בקוד HTML הבא באתר שלך על מנת ליצור נושאי דיסקורס משולבים. החלף REPLACE_ME בURL הקאנוני של העמוד שבו אתה מכניס נושא מכונן. "
+ title: "שילוב (embedding)"
permalink:
title: "קישורים קבועים"
url: "כתובת"
diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml
index 12f4276388a..9af856e528a 100644
--- a/config/locales/client.ja.yml
+++ b/config/locales/client.ja.yml
@@ -482,7 +482,6 @@ ja:
search: "招待履歴を検索するためにキーワードを入力してください..."
title: "招待"
user: "招待したユーザ"
- none: "まだ招待を送っていません"
truncated: "最初の {{count}} 個の招待履歴を表示しています。"
redeemed: "受理された招待"
redeemed_tab: "受理"
@@ -1319,6 +1318,8 @@ ja:
help: "このトピックをブックマークしました"
locked:
help: "このトピックは終了しています。新たに回答を投稿することはできません。"
+ archived:
+ help: "このトピックはアーカイブされています。凍結状態のため一切の変更ができません"
unpinned:
title: "ピン留め解除しました"
help: "このトピックはピン留めされていません。 既定の順番に表示されます。"
@@ -1328,8 +1329,6 @@ ja:
pinned:
title: "ピン留め"
help: "このトピックはピン留めされています。常にカテゴリのトップに表示されます"
- archived:
- help: "このトピックはアーカイブされています。凍結状態のため一切の変更ができません"
invisible:
help: "このトピックはリストされていません。トピックリストには表示されません。直接リンクでのみアクセス可能です"
posts: "投稿"
diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml
index e12299d1e10..6ec6c70699f 100644
--- a/config/locales/client.ko.yml
+++ b/config/locales/client.ko.yml
@@ -513,7 +513,6 @@ ko:
title: "초대"
user: "사용자 초대"
sent: "보냄"
- none: "어떤 초대도 발견되지 않았습니다."
truncated: "처음 {{count}}개 초대장 보여주기"
redeemed: "초대를 받았습니다."
redeemed_tab: "Redeemed"
@@ -1358,6 +1357,8 @@ ko:
help: "북마크한 토픽"
locked:
help: "이 토픽은 폐쇄되었습니다. 더 이상 새 답글을 받을 수 없습니다."
+ archived:
+ help: "이 토픽은 보관중입니다. 고정되어 변경이 불가능합니다."
unpinned:
title: "핀 제거"
help: "이 토픽은 핀 제거 되었습니다. 목록에서 일반적인 순서대로 표시됩니다."
@@ -1367,8 +1368,6 @@ ko:
pinned:
title: "핀 지정됨"
help: "이 토픽은 고정되었습니다. 카테고리의 상단에 표시됩니다."
- archived:
- help: "이 토픽은 보관중입니다. 고정되어 변경이 불가능합니다."
invisible:
help: "이 토픽은 목록에서 제외됩니다. 토픽 목록에 표시되지 않으며 링크를 통해서만 접근 할 수 있습니다."
posts: "게시물"
diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml
index 1481dd7fc03..684c83590de 100644
--- a/config/locales/client.nl.yml
+++ b/config/locales/client.nl.yml
@@ -306,6 +306,9 @@ nl:
mods_and_admins: "Alleen moderatoren and admins"
members_mods_and_admins: "Alleen leden van de groep, moderatoren en admins"
everyone: "Iedereen"
+ trust_levels:
+ title: "Trustlevel dat automatisch wordt toegekend aan nieuwe gebruikers:"
+ none: "Geen"
user_action_groups:
'1': "Likes gegeven"
'2': "Likes ontvangen"
@@ -367,6 +370,7 @@ nl:
private_messages: "Berichten"
activity_stream: "Activiteit"
preferences: "Voorkeuren"
+ expand_profile: "Uitklappen"
bookmarks: "Bladwijzers"
bio: "Over mij"
invited_by: "Uitgenodigd door"
@@ -543,7 +547,7 @@ nl:
title: "Uitnodigingen"
user: "Uitgenodigd lid"
sent: "Verzonden"
- none: "Je hebt nog niemand uitgenodigd."
+ none: "Er zijn geen uitstaande uitnodigingen om weer te geven."
truncated: "De eerste {{count}} uitnodigingen."
redeemed: "Verzilverde uitnodigingen"
redeemed_tab: "Verzilverd"
@@ -807,6 +811,20 @@ nl:
moved_post: "
{{username}} verplaatste {{description}}
"
linked: "
{{username}} {{description}}
"
granted_badge: "
'{{description}}' ontvangen
"
+ alt:
+ mentioned: "Genoemd door"
+ quoted: "Gequoot door"
+ replied: "Gereageerd"
+ posted: "Geplaatst door"
+ edited: "Wijzig je bericht door"
+ liked: "Vind je bericht leuk"
+ private_message: "Privébericht van"
+ invited_to_private_message: "Uitgenodigd voor een privébericht van"
+ invited_to_topic: "Uitgenodigd voor een topic door"
+ invitee_accepted: "Uitnodiging geaccepteerd door"
+ moved_post: "Je bericht is verplaatst door"
+ linked: "Link naar je bericht"
+ granted_badge: "Badge toegekend"
popup:
mentioned: '{{username}} heeft je genoemd in "{{topic}}" - {{site_title}}'
quoted: '{{username}} heeft je geciteerd in "{{topic}}" - {{site_title}}'
@@ -845,7 +863,7 @@ nl:
not_logged_in_user: 'gebruikerspagina met samenvatting van huidige activiteit en voorkeuren'
current_user: 'ga naar je gebruikerspagina'
topics:
- too_many_tracked: "Let op: je hebt te veel nieuwe en ongelezen topics gevolgd, verwijder ze met behulp van \"Markeer nieuwe berichten als gelezen\" of \"Verwijder Berichten\""
+ too_many_tracked: "Let op: je hebt te veel topics staan onder nieuw en ongelezen, maak wat ruimte door Dismiss New of Dismiss Posts te gebruiken"
bulk:
reset_read: "markeer als ongelezen"
delete: "Verwijder topics"
@@ -1372,6 +1390,7 @@ nl:
email_in_allow_strangers: "Accepteer mails van anonieme gebruikers zonder account"
email_in_disabled: "Het plaatsen van nieuwe discussies via e-mail is uitgeschakeld in de Site Instellingen. Om het plaatsen van nieuwe discussie via e-mail aan te zetten,"
email_in_disabled_click: 'schakel "e-mail in" instelling in.'
+ suppress_from_homepage: "Negeer deze categorie op de homepage"
allow_badges_label: "Laat badges toekennen voor deze categorie"
edit_permissions: "Wijzig permissies"
add_permission: "Nieuwe permissie"
@@ -1437,6 +1456,10 @@ nl:
help: "Je hebt een bladwijzer aan deze topic toegevoegd"
locked:
help: "Deze topic is gesloten; reageren is niet meer mogelijk"
+ archived:
+ help: "Deze topic is gearchiveerd en kan niet meer gewijzigd worden"
+ locked_and_archived:
+ help: "Deze topic is gesloten en gearchiveerd; reageren of wijzigen is niet langer mogelijk."
unpinned:
title: "Niet vastgepind"
help: "Dit topic is niet langer vastgepind voor je en zal weer in de normale volgorde getoond worden"
@@ -1446,8 +1469,6 @@ nl:
pinned:
title: "Vastgepind"
help: "Dit topic is vastgepind voor je en zal bovenaan de categorie getoond worden"
- archived:
- help: "Deze topic is gearchiveerd en kan niet meer gewijzigd worden"
invisible:
help: "Dit topic is niet zichtbaar; het zal niet verschijnen in de topiclijst en kan alleen bekeken worden met een directe link"
posts: "Berichten"
@@ -2362,6 +2383,8 @@ nl:
categories: 'g, c Categoriën'
top: 'g, t Top'
bookmarks: 'g, b Favorieten'
+ profile: 'g, p Profiel'
+ messages: 'g, m Berichten'
navigation:
title: 'Navigatie'
jump: '# Ga naar bericht #'
@@ -2380,6 +2403,7 @@ nl:
help: '? Open sneltoetsen help'
dismiss_new_posts: 'x, r Seponeer Nieuw/Berichten'
dismiss_topics: 'x, t Seponeer Topics'
+ log_out: 'shift+zshift+z Uitloggen'
actions:
title: 'Acties'
bookmark_topic: 'f Toggle bladwijzer van topic'
diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml
index 0bcfb2cd1f4..37d09250668 100644
--- a/config/locales/client.pl_PL.yml
+++ b/config/locales/client.pl_PL.yml
@@ -334,6 +334,9 @@ pl_PL:
mods_and_admins: "Tylko moderatorzy i administratorzy"
members_mods_and_admins: "Tylko członkowie grupy, moderatorzy i administratorzy"
everyone: "Wszyscy"
+ trust_levels:
+ title: "Domyślny poziom zaufania przyznawany nowych użytkownikom:"
+ none: "Brak"
user_action_groups:
'1': "Przyznane polubienia"
'2': "Otrzymane polubienia"
@@ -397,6 +400,7 @@ pl_PL:
private_messages: "Wiadomości"
activity_stream: "Aktywność"
preferences: "Ustawienia"
+ expand_profile: "Rozwiń"
bookmarks: "Zakładki"
bio: "O mnie"
invited_by: "Zaproszono przez"
@@ -573,7 +577,7 @@ pl_PL:
title: "Zaproszenia"
user: "Zaproszony(-a) użytkownik(-czka)"
sent: "Wysłane"
- none: "Jeszcze nikt nie został przez ciebie zaproszony."
+ none: "Nie ma żadnych zaproszeń do wyświetlenia."
truncated: "Pokaż pierwsze {{count}} zaproszeń."
redeemed: "Cofnięte zaproszenia"
redeemed_tab: "Przyjęte"
@@ -838,6 +842,20 @@ pl_PL:
moved_post: "
{{username}} przenosi {{description}}
"
linked: "
{{username}} {{description}}
"
granted_badge: "
Otrzymujesz '{{description}}'
"
+ alt:
+ mentioned: "Wywołanie przez"
+ quoted: "Cytowanie przez"
+ replied: "Odpowiedź"
+ posted: "Autor wpisu"
+ edited: "Edycja twojego wpisu"
+ liked: "Polubienie twojego wpisu"
+ private_message: "Prywatna wiadomość od"
+ invited_to_private_message: "Zaproszenie do prywatnej wiadomości od"
+ invited_to_topic: "Zaproszenie do tematu od"
+ invitee_accepted: "Zaproszenie zaakceptowane przez"
+ moved_post: "Twój wpis został przeniesiony przez"
+ linked: "Linkownie do twojego wpisu"
+ granted_badge: "Przyznanie odznaki"
popup:
mentioned: '{{username}} wspomina o tobie w "{{topic}}" - {{site_title}}'
quoted: '{{username}} cytuje cie w "{{topic}}" - {{site_title}}'
@@ -876,7 +894,7 @@ pl_PL:
not_logged_in_user: 'strona użytkownika z podsumowaniem bieżących działań i ustawień'
current_user: 'idź do swojej strony użytkowanika'
topics:
- too_many_tracked: "Uwaga: posiadasz zbyt wiele wpisów w nowych i śledzonych tematach. Aby kontynuować, wyczyść listę przyciskiem na dole strony."
+ too_many_tracked: "Uwaga: posiadasz zbyt wiele wpisów w nowych i śledzonych tematach. Aby kontynuować, wyczyść Nowe lub Śledzone"
bulk:
reset_read: "Wyzeruj przeczytane"
delete: "Usuń tematy"
@@ -1440,6 +1458,7 @@ pl_PL:
email_in_allow_strangers: "Akceptuj wiadomości email od anonimowych, nieposiadających kont użytkowników "
email_in_disabled: "Tworzenie nowych tematów emailem jest wyłączone w ustawieniach serwisu. "
email_in_disabled_click: 'Kliknij tu, aby włączyć.'
+ suppress_from_homepage: "Nie wyświetlaj tej kategorii na stronie głównej."
allow_badges_label: "Włącz przyznawanie odznak na podstawie aktywności w tej kategorii"
edit_permissions: "Edytuj uprawnienia"
add_permission: "Dodaj uprawnienie"
@@ -1506,6 +1525,10 @@ pl_PL:
help: "Temat został dodany do zakładek."
locked:
help: "Temat został zamknięty. Dodawanie nowych odpowiedzi nie jest możliwe."
+ archived:
+ help: "Ten temat został zarchiwizowany i nie można go zmieniać"
+ locked_and_archived:
+ help: "Ten temat jest zamknięty i zarchiwizowany. Dodawanie odpowiedzi i jego edycja nie są możliwe."
unpinned:
title: "Nieprzypięty"
help: "Temat nie jest przypięty w ramach twojego konta. Będzie wyświetlany w normalnej kolejności."
@@ -1515,8 +1538,6 @@ pl_PL:
pinned:
title: "Przypięty"
help: "Temat przypięty dla twojego konta. Będzie wyświetlany na początku swojej kategorii."
- archived:
- help: "Ten temat został zarchiwizowany i nie można go zmieniać"
invisible:
help: "Temat jest niewidoczny: nie będzie wyświetlany na listach tematów a dostęp do niego można uzyskać jedynie poprzez link bezpośredni"
posts: "Wpisy"
@@ -2440,6 +2461,8 @@ pl_PL:
categories: 'g, c Kategorie'
top: 'g, t Popularne'
bookmarks: 'g, b Zakładki'
+ profile: 'g, p Profil'
+ messages: 'g, m Wiadomości'
navigation:
title: 'Nawigacja'
jump: '# idź do wpisu #'
@@ -2458,6 +2481,7 @@ pl_PL:
help: '? skróty klawiszowe'
dismiss_new_posts: 'x, r wyczyść listę wpisów'
dismiss_topics: 'x, t wyczyść listę tematów'
+ log_out: 'shift+zshift+z Wylogowanie'
actions:
title: 'Operacje'
bookmark_topic: 'f dodaj/usuń zakładkę na temat'
diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml
index 7c54bcd75c8..33759f113dd 100644
--- a/config/locales/client.pt.yml
+++ b/config/locales/client.pt.yml
@@ -306,6 +306,9 @@ pt:
mods_and_admins: "Apenas moderadores e Administradores"
members_mods_and_admins: "Apenas membros do grupo, moderadores e administradores"
everyone: "Todos"
+ trust_levels:
+ title: "Nível de confiança concedido automaticamente a membros quando são adicionados:"
+ none: "Nenhum"
user_action_groups:
'1': "Gostos Dados"
'2': "Gostos Recebidos"
@@ -367,6 +370,7 @@ pt:
private_messages: "Mensagens"
activity_stream: "Atividade"
preferences: "Preferências"
+ expand_profile: "Expandir"
bookmarks: "Marcadores"
bio: "Sobre mim"
invited_by: "Convidado Por"
@@ -543,7 +547,7 @@ pt:
title: "Convites"
user: "Utilizadores Convidados"
sent: "Enviado"
- none: "Ainda não convidou ninguém."
+ none: "Não há convites pendentes para mostrar."
truncated: "A mostrar os primeiros {{count}} convites."
redeemed: "Convites Resgatados"
redeemed_tab: "Resgatado"
@@ -807,6 +811,20 @@ pt:
moved_post: "
{{username}} moveu {{description}}
"
linked: "
{{username}} {{description}}
"
granted_badge: "
Ganhou '{{description}}'
"
+ alt:
+ mentioned: "Mencionado por"
+ quoted: "Citado por"
+ replied: "Respondido"
+ posted: "Publicado por"
+ edited: "Edição da sua mensagem por"
+ liked: "Gostou da sua mensagem"
+ private_message: "Mensagem privada de"
+ invited_to_private_message: "Convidado para uma mensagem privada de"
+ invited_to_topic: "Convidado para um tópico de"
+ invitee_accepted: "Convite aceite por"
+ moved_post: "A sua mensagem foi movida por"
+ linked: "Hiperligação para a sua mensagem"
+ granted_badge: "Distintivo concedido"
popup:
mentioned: '{{username}} mencionou-o em "{{topic}}" - {{site_title}}'
quoted: '{{username}} citou-o em "{{topic}}" - {{site_title}}'
@@ -845,7 +863,7 @@ pt:
not_logged_in_user: 'página de utilizador com resumo da atividade atual e preferências '
current_user: 'ir para a sua página de utilizador'
topics:
- too_many_tracked: "Aviso: tem demasiados tópicos novos ou não lidos a serem acompanhados, limpe alguns utilizando \"Destituir Novos\" ou \"Destituir Mensagens\""
+ too_many_tracked: "Aviso: tem demasiados tópicos novos e não lidos a serem acompanhados, limpe alguns utilizando Destituir Novos ou Destituir Mensagens"
bulk:
reset_read: "Repor Leitura"
delete: "Eliminar Tópicos"
@@ -1372,6 +1390,7 @@ pt:
email_in_allow_strangers: "Aceitar emails de utilizadores anónimos sem conta"
email_in_disabled: "Publicar novos tópicos através do email está desactivado nas Configurações do Sítio. Para permitir a publicação de novos tópicos através do email,"
email_in_disabled_click: 'ative a definição "email em".'
+ suppress_from_homepage: "Suprimir esta categoria da página principal."
allow_badges_label: "Permitir a atribuição de distintivos nesta categoria"
edit_permissions: "Editar Permissões"
add_permission: "Adicionar Permissões"
@@ -1437,6 +1456,10 @@ pt:
help: "Adicionou este tópico aos marcadores"
locked:
help: "Este tópico está fechado; já não são aceites novas respostas"
+ archived:
+ help: "Este tópico está arquivado; está congelado e não pode ser alterado"
+ locked_and_archived:
+ help: "Este tópico está fechado e arquivado; já não aceita novas respostas e não pode ser modificado"
unpinned:
title: "Desafixado"
help: "Este tópico foi desafixado por si; será mostrado na ordem habitual"
@@ -1446,8 +1469,6 @@ pt:
pinned:
title: "Fixado"
help: "Este tópico foi fixado por si; será mostrado no topo da sua categoria"
- archived:
- help: "Este tópico está arquivado; está congelado e não pode ser alterado"
invisible:
help: "Este tópico não está listado; não será apresentado na lista de tópicos e poderá ser acedido apenas através de uma hiperligação direta"
posts: "Mensagens"
@@ -2362,6 +2383,8 @@ pt:
categories: 'g, c Categorias'
top: 'g, t Os Melhores'
bookmarks: 'g, b Marcadores'
+ profile: 'g, p Perfil'
+ messages: 'g, m Mensagens'
navigation:
title: 'Navegação'
jump: '# Ir para o post #'
@@ -2380,6 +2403,7 @@ pt:
help: '? Abrir ajuda do teclado'
dismiss_new_posts: 'x, r Destituir Novos/Mensagens'
dismiss_topics: 'x, t Destituir Tópicos'
+ log_out: 'shift+zshift+z Terminar Sessão'
actions:
title: 'Ações'
bookmark_topic: 'f Alternar marcador de tópico'
diff --git a/config/locales/client.ro.yml b/config/locales/client.ro.yml
index a352bee2105..b9d195509a8 100644
--- a/config/locales/client.ro.yml
+++ b/config/locales/client.ro.yml
@@ -495,7 +495,6 @@ ro:
search: "Scrie pentru a căuta invitații..."
title: "Invitații"
user: "Utilizatori invitați"
- none: "Nu ai invitat înca pe nimeni."
truncated: "Afișeaza primele {{count}} invitații."
redeemed: "Invitații rascumpărate"
redeemed_at: "Răscumpărate"
@@ -1357,6 +1356,8 @@ ro:
help: "Aţi pus un semn de carte pentru această discuţie"
locked:
help: "Această discuție este închisă; nu mai acceptă răspunsuri noi"
+ archived:
+ help: "Această discuție a fost arhivată; Este închetată și nu poate fi editată"
unpinned:
title: "Desprinde"
help: "Această discuţie va fi afişată în ordinea iniţială, nici un mesaj nu este promovat la inceputul listei."
@@ -1366,8 +1367,6 @@ ro:
pinned:
title: "Fixată"
help: "Aceast mesaj va fi promovat. Va fi afişat la începutul discuţiei."
- archived:
- help: "Această discuție a fost arhivată; Este închetată și nu poate fi editată"
invisible:
help: "Această discuție este invizibilă; nu va fi afișată în listele de discuții și va fi accesată numai prin adresa directă"
posts: "Postări"
diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml
index cd135e8227c..51574007e82 100644
--- a/config/locales/client.ru.yml
+++ b/config/locales/client.ru.yml
@@ -553,7 +553,6 @@ ru:
search: "введите текст для поиска приглашений..."
title: "Приглашения"
user: "Приглашенный пользователь"
- none: "Пока вы еще никого не пригласили."
truncated: "Отображаются первые {{count}} приглашений."
redeemed: "Принятые приглашения"
redeemed_tab: "Принято"
@@ -1490,6 +1489,8 @@ ru:
help: "Вы добавили тему в Избранное "
locked:
help: "Тема закрыта; в ней больше нельзя отвечать"
+ archived:
+ help: "Тема заархивирована и не может быть изменена"
unpinned:
title: "Откреплена"
help: "Эта тема не закреплена; она будет отображаться в обычном порядке"
@@ -1499,8 +1500,6 @@ ru:
pinned:
title: "Закреплена"
help: "Тема закреплена; она будет показана вверху соответствующего раздела"
- archived:
- help: "Тема заархивирована и не может быть изменена"
invisible:
help: "Тема исключена из всех списков тем и доступна только по прямой ссылке"
posts: "Сообщ."
@@ -2546,3 +2545,9 @@ ru:
reader:
name: Читатель
description: Прочитал каждое сообщение в теме с более чем 100 сообщениями
+ popular_link:
+ name: Популярная ссылка
+ description: Оставил внешнюю ссылку с более чем 50 кликов
+ hot_link:
+ name: Горячая ссылка
+ description: Оставил внешнюю ссылку с более чем 300 кликов
diff --git a/config/locales/client.sq.yml b/config/locales/client.sq.yml
index 8c70fd5e048..bf8ffdfa91f 100644
--- a/config/locales/client.sq.yml
+++ b/config/locales/client.sq.yml
@@ -512,7 +512,6 @@ sq:
search: "shkruaj për të kërkuar ftesat..."
title: "Ftesa"
user: "Anëtarët e Ftuar"
- none: "Nuk keni ftuar asnjë deri më tani."
truncated: "Shfaq {{count}} ftesat e para."
redeemed: "Ridërgo ftesat"
redeemed_tab: "Redeemed"
@@ -1387,6 +1386,8 @@ sq:
help: "You bookmarked this topic"
locked:
help: "This topic is closed; it no longer accepts new replies"
+ archived:
+ help: "This topic is archived; it is frozen and cannot be changed"
unpinned:
title: "Unpinned"
help: "This topic is unpinned for you; it will display in regular order"
@@ -1396,8 +1397,6 @@ sq:
pinned:
title: "Pinned"
help: "This topic is pinned for you; it will display at the top of its category"
- archived:
- help: "This topic is archived; it is frozen and cannot be changed"
invisible:
help: "This topic is unlisted; it will not be displayed in topic lists, and can only be accessed via a direct link"
posts: "Postime"
diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml
index 3a3fdf121f1..8f4db72c6cf 100644
--- a/config/locales/client.sv.yml
+++ b/config/locales/client.sv.yml
@@ -101,6 +101,7 @@ sv:
google+: 'dela denna länk på Google+'
email: 'skicka denna länk i ett email'
action_codes:
+ split_topic: "Dela upp denna tråd"
autoclosed:
enabled: 'stängdes %{when}'
disabled: 'öppnades %{when}'
@@ -498,7 +499,6 @@ sv:
search: "sök efter inbjudningar..."
title: "Inbjudningar"
user: "Inbjuden Användare"
- none: "Du har inte bjudit in någon här ännu."
truncated: "Visar de första {{count}} inbjudningarna."
redeemed: "Inlösta Inbjudnignar"
redeemed_at: "Inlöst"
@@ -1356,6 +1356,8 @@ sv:
help: "Du bokmärkte nu detta ämnet."
locked:
help: "Det här ämnet är stängt; det går inte längre att svara på inlägg"
+ archived:
+ help: "Det här ämnet är arkiverat; det är fryst och kan inte ändras"
unpinned:
title: "Avklistrat"
help: "Detta ämne är oklistrat för dig. Det visas i vanlig ordning"
@@ -1365,8 +1367,6 @@ sv:
pinned:
title: "Klistrat"
help: "Detta ämne är klistrat för dig. Det visas i toppen av dess kategori"
- archived:
- help: "Det här ämnet är arkiverat; det är fryst och kan inte ändras"
invisible:
help: "Det här ämnet är olistat; det kommer inte visas i ämneslistorna och kan bara nås via en direktlänk"
posts: "Inlägg"
diff --git a/config/locales/client.te.yml b/config/locales/client.te.yml
index 50dcae64057..e9726bc6b37 100644
--- a/config/locales/client.te.yml
+++ b/config/locales/client.te.yml
@@ -418,7 +418,6 @@ te:
search: "ఆహ్వానాలను వెతకడానికి రాయండి ... "
title: "ఆహ్వానాలు"
user: "ఆహ్వానించిన సభ్యుడు"
- none: "మీరు ఇక్కడ ఇంకా ఎవరినీ ఆహ్వానించలేదు."
truncated: "తొలి {{count}} ఆహ్వానాలను చూపుతున్నాము."
redeemed: "మన్నించిన ఆహ్వానాలు"
redeemed_at: "మన్నించిన"
@@ -1155,6 +1154,8 @@ te:
help: "ఈ విషయానికి పేజీక ఉంచారు"
locked:
help: "ఈ విషయం ముగిసింది. కొత్త జవాబులు అంగీకరించదు. "
+ archived:
+ help: "ఈ విషయం కట్టకట్టబడింది. ఇది గడ్డకట్టుకుంది ఇహ మార్చయిత కాదు"
unpinned:
title: "అగ్గుచ్చిన"
help: "ఈ విషయం మీకు అగ్గుచ్చబడింది. ఇది ఇహ క్రమ వరుసలోనే కనిపిస్తుంది"
@@ -1164,8 +1165,6 @@ te:
pinned:
title: "గుచ్చారు"
help: "ఈ విషయం మీకు గుచ్చబడింది. దాని వర్గంలో అది అగ్రభాగాన కనిపిస్తుంది."
- archived:
- help: "ఈ విషయం కట్టకట్టబడింది. ఇది గడ్డకట్టుకుంది ఇహ మార్చయిత కాదు"
invisible:
help: "ఈ విషయం జాబితాలనుండి తొలగించబడింది. ఇహ కేవలం నేరు లంకె ద్వారా మాత్రమే చూడగలరు."
posts: "టపాలు"
diff --git a/config/locales/client.tr_TR.yml b/config/locales/client.tr_TR.yml
index d359c28e6f1..bb0ab800ece 100644
--- a/config/locales/client.tr_TR.yml
+++ b/config/locales/client.tr_TR.yml
@@ -89,6 +89,26 @@ tr_TR:
facebook: 'bu bağlantıyı Facebook''da paylaşın'
google+: 'bu bağlantıyı Google+''da paylaşın'
email: 'bu bağlantıyı e-posta ile gönderin'
+ action_codes:
+ split_topic: "bu konuyu ayır"
+ autoclosed:
+ enabled: 'kapatıldı %{when}'
+ disabled: 'açıldı %{when}'
+ closed:
+ enabled: 'kapatıldı %{when}'
+ disabled: 'açıldı %{when}'
+ archived:
+ enabled: 'arşivlendi %{when}'
+ disabled: 'arşivden çıkarıldı %{when}'
+ pinned:
+ enabled: 'sabitlendi %{when}'
+ disabled: 'sabitlikten çıkarıldı %{when}'
+ pinned_globally:
+ enabled: 'genel olarak sabitlendi %{when}'
+ disabled: 'genel olarak sabitlikten çıkarıldı %{when}'
+ visible:
+ enabled: 'listelendi %{when}'
+ disabled: 'listelenmedi %{when}'
topic_admin_menu: "konuyla alakalı yönetici işlemleri"
emails_are_disabled: "Tüm giden e-postalar yönetici tarafından evrensel olarak devre dışı bırakıldı. Herhangi bir e-posta bildirimi gönderilmeyecek."
edit: 'bu konunun başlığını ve kategorisini düzenleyin'
@@ -104,6 +124,7 @@ tr_TR:
admin_title: "Yönetici"
flags_title: "Bayraklar"
show_more: "devamını göster"
+ show_help: "seçenekler"
links: "Bağlantılar"
links_lowercase:
other: "bağlantılar"
@@ -157,8 +178,8 @@ tr_TR:
bookmarks:
not_logged_in: "üzgünüz, gönderileri işaretleyebilmeniz için oturum açmanız gerekiyor."
created: "bu gönderiyi işaretlediniz"
- not_bookmarked: "bu gönderiyi okudunuz; işaretlemek için tıklayın"
- last_read: "bu okuduğunuz son gönderi; işaretlemek için tıklayın"
+ not_bookmarked: "bu gönderiyi okudunuz; yer imlerinize eklemek için tıklayın"
+ last_read: "bu okuduğunuz son gönderi; yer imlerinize eklemek için tıklayın"
remove: "İşareti Kaldır"
confirm_clear: "Bu konuya ait tüm işaretleri kaldırmak istediğinize emin misiniz?"
topic_count_latest:
@@ -257,6 +278,9 @@ tr_TR:
mods_and_admins: "Sadece Moderatörler ve Yöneticiler"
members_mods_and_admins: "Sadece Grup Üyeleri, Moderatörler ve Yöneticiler"
everyone: "Herkes"
+ trust_levels:
+ title: "Eklendiklerinde üyelere otomatik olarak güven seviyesi verilir:"
+ none: "Hiç"
user_action_groups:
'1': "Verilen Beğeniler"
'2': "Alınan Beğeniler"
@@ -303,6 +327,8 @@ tr_TR:
topics_entered: "açılan konular"
post_count: "# gönderi"
confirm_delete_other_accounts: "Bu hesapları silmek isteğinize emin misiniz?"
+ user_fields:
+ none: "(bir seçenek seçin)"
user:
said: "{{username}}:"
profile: "Profil"
@@ -314,11 +340,23 @@ tr_TR:
private_messages: "Mesajlar"
activity_stream: "Aktivite"
preferences: "Seçenekler"
+ expand_profile: "Genişlet"
bookmarks: "İşaretlenenler"
bio: "Hakkımda"
invited_by: "Tarafından Davet Edildi"
trust_level: "Güven Seviyesi"
notifications: "Bildirimler"
+ desktop_notifications:
+ label: "Masaüstü Bildirimleri"
+ not_supported: "Bildirimler bu tarayıcıda desteklenmiyor. Üzgünüz."
+ perm_default: "Bildirimleri Etkinleştirin"
+ perm_denied_btn: "Erişim İzni Reddedildi"
+ perm_denied_expl: "Bildirimler için gerekli izne sahip değilsiniz. Bildirimleri etkinleştirmek için tarayıcınızı kullanın, işlem tamamlandığında tuşa basın. (Masaüstü: Adres çubuğunda en soldaki simge. Mobil: 'Site Bilgisi'.)"
+ disable: "Bildirimleri Devre Dışı Bırakın"
+ currently_enabled: "(şu anda etkin)"
+ enable: "Bildirimleri Etkinleştirin"
+ currently_disabled: "(şu anda devre dışı)"
+ each_browser_note: "Not: Bu ayarı kullandığınız her tarayıcıda değiştirmelisiniz."
dismiss_notifications: "Hepsini okunmuş olarak işaretle"
dismiss_notifications_tooltip: "Tüm okunmamış bildirileri okunmuş olarak işaretle"
disable_jump_reply: "Cevapladıktan sonra gönderime atlama"
@@ -331,6 +369,7 @@ tr_TR:
admin: "{{user}} bir yöneticidir"
moderator_tooltip: "Bu kullanıcı bir moderatör"
admin_tooltip: "Bu kullanıcı bir yönetici."
+ blocked_tooltip: "Bu kullanıcı engellendi"
suspended_notice: "Bu kullanıcı {{tarih}} tarihine kadar uzaklaştırıldı."
suspended_reason: "Neden:"
github_profile: "Github"
@@ -391,6 +430,7 @@ tr_TR:
upload_title: "Resminizi yükleyin"
upload_picture: "Resim Yükle"
image_is_not_a_square: "Uyarı: resminizi kırptık; genişlik ve yükseklik eşit değildi."
+ cache_notice: "Profil resminizi başarıyla değiştirdiniz fakat tarayıcı önbelleklemesi nedeniyle görünür olması biraz zaman alabilir."
change_profile_background:
title: "Profil Arkaplanı"
instructions: "Profil arkaplanları ortalanacak ve genişlikleri 850px olacak. "
@@ -457,21 +497,35 @@ tr_TR:
label: "Seçili durumdaki konular yeni sayılsın"
not_viewed: "Onları henüz görüntülemedim"
last_here: "son ziyaretimden beri oluşturulanlar"
+ after_1_day: "son 1 gün içinde oluşturuldu"
+ after_2_days: "son 2 gün içinde oluşturuldu"
+ after_1_week: "son 1 hafta içinde oluşturuldu"
+ after_2_weeks: "son 2 hafta içinde oluşturuldu"
auto_track_topics: "Girdiğim konuları otomatik olarak takip et"
auto_track_options:
never: "asla"
immediately: "hemen"
+ after_30_seconds: "30 saniye sonra"
+ after_1_minute: "1 dakika sonra"
+ after_2_minutes: "2 dakika sonra"
+ after_3_minutes: "3 dakika sonra"
+ after_4_minutes: "4 dakika sonra"
+ after_5_minutes: "5 dakika sonra"
+ after_10_minutes: "10 dakika sonra"
invited:
search: "davetler arasında aramak için yazın..."
title: "Davetler"
user: "Davet Edilen Kullanıcı"
- none: "Henüz buraya kimseyi davet etmediniz."
+ sent: "Gönderildi"
+ none: "Bekleyen davet yok."
truncated: "İlk {{count}} davetler gösteriliyor."
redeemed: "Kabul Edilen Davetler"
redeemed_tab: "Kabul Edildi"
+ redeemed_tab_with_count: "İtfa edilmiş ({{count}})"
redeemed_at: "Kabul Edildi"
pending: "Bekleyen Davetler"
pending_tab: "Bekleyen"
+ pending_tab_with_count: "Beklemede ({{count}})"
topics_entered: "Görüntülenmiş Konular"
posts_read_count: "Okunmuş Yazılar"
expired: "Bu davetin süresi doldu."
@@ -483,6 +537,8 @@ tr_TR:
days_visited: "Ziyaret Edilen Günler"
account_age_days: "Gün içinde Hesap yaş"
create: "Davet Yolla"
+ generate_link: "Davet bağlantısını kopyala"
+ generated_link_message: '
Davet bağlantısı başarılı bir şekilde oluşturuldu!
Davet bağlantısı sadece bu e-posta adresi için geçerlidir: %{invitedEmail}
'
bulk_invite:
none: "Henüz kimseyi buraya davet etmediniz. Tek tek davetiye gönderebilirsiniz, ya da toplu bir davetiye dosyası yükleyerek birçok kişiyi aynı anda davet edebilirsiniz. "
text: "Dosyadan Toplu Davet Gönder"
@@ -539,6 +595,7 @@ tr_TR:
read_only_mode:
enabled: "Salt-okunur modu etkin. Siteyi gezmeye devam edebilirsiniz fakat etkileşimler çalışmayabilir."
login_disabled: "Site salt-okunur modda iken oturum açma devre dışı bırakılır ."
+ too_few_topics_notice: "Hadi bu tartışmayı başlatalım! Şu anda %{currentTopics} / %{requiredTopics} konu ve %{currentPosts} / %{requiredPosts} gönderi var. Yeni ziyaretçiler okumak ve yanıtlamak için birkaç tartışmaya ihtiyaç duyarlar."
learn_more: "daha fazlasını öğren..."
year: 'yıl'
year_desc: 'son 365 günde oluşturulan konular'
@@ -723,6 +780,20 @@ tr_TR:
moved_post: "
{{username}} taşıdı {{description}}
"
linked: "
{{username}} {{description}}
"
granted_badge: "
Kazanıldı'{{description}}'
"
+ alt:
+ mentioned: "Bahsedildi, şu kişi tarafından"
+ quoted: "Alıntılandı, şu kişi tarafından"
+ replied: "Cevaplandı"
+ posted: "Gönderildi, şu kişi tarafından"
+ edited: "Gönderiniz düzenlendi, şu kişi tarafından"
+ liked: "Gönderiniz beğenildi"
+ private_message: "Özel mesaj, şu kişiden"
+ invited_to_private_message: "Bir özel mesaja davet edildiniz, şu kişi tarafından"
+ invited_to_topic: "Bir konuya davet edildiniz, şu kişi tarafından"
+ invitee_accepted: "Davet kabul edildi, şu kişi tarafından"
+ moved_post: "Gönderiniz taşındı, şu kişi tarafından"
+ linked: "Gönderinize bağlantı"
+ granted_badge: "Rozet alındı"
popup:
mentioned: '{{username}}, "{{topic}}" başlıklı konuda sizden bahsetti - {{site_title}}'
quoted: '{{username}}, "{{topic}}" başlıklı konuda sizden alıntı yaptı - {{site_title}}'
@@ -747,6 +818,8 @@ tr_TR:
search:
title: "konular, gönderiler, kullanıcılar, veya kategoriler arasında ara"
no_results: "Hiç bir sonuç bulunamadı."
+ no_more_results: "Başka sonuç yok."
+ search_help: Arama yardımı
searching: "Aranıyor..."
post_format: "{{username}} tarafından #{{post_number}}"
context:
@@ -754,10 +827,12 @@ tr_TR:
category: "\"{{category}}\" kategorisinde ara"
topic: "Bu konuda ara"
private_messages: "Mesajlarda ara"
+ hamburger_menu: "bir diğer konu ya da kategoriye git"
go_back: 'geri dön'
not_logged_in_user: 'güncel aktivitelerin ve ayarların özetinin bulunduğu kullanıcı sayfası'
current_user: 'kendi kullanıcı sayfana git'
topics:
+ too_many_tracked: "Uyarı: takip edilen çok fazla yeni ve okunmamış konunuz var, bazılarını Yeniyi Kaldır ya da Konuları Kaldır'ı kullanarak temizleyin."
bulk:
reset_read: "Okunmuşları Sıfırla"
delete: "Konuları Sil"
@@ -801,6 +876,9 @@ tr_TR:
bookmarks: "Daha fazla işaretlenmiş konu yok."
search: "Daha fazla arama sonucu yok."
topic:
+ unsubscribe:
+ stop_notifications: "Artık {{title}} için daha az bildirim alacaksınız."
+ change_notification_state: "Geçerli bildirim durumunuz"
filter_to: "Bu konuda {{post_count}} gönderi"
create: 'Yeni Konu'
create_long: 'Yeni bir konu oluştur'
@@ -888,8 +966,10 @@ tr_TR:
title: "Takip Ediliyor"
description: "Okunmamış ve yeni gönderi sayısı başlığın yanında belirecek. Birisi @isim şeklinde sizden bahsederse ya da gönderinize cevap verirse bildirim alacaksınız."
regular:
+ title: "Olağan"
description: "Birisi @isim şeklinde sizden bahsederse ya da gönderinize cevap verirse bildirim alacaksınız."
regular_pm:
+ title: "Olağan"
description: "Birisi @isim şeklinde sizden bahsederse ya da gönderinize mesajla cevap verirse bildirim alacaksınız."
muted_pm:
title: "Susturuldu"
@@ -932,15 +1012,20 @@ tr_TR:
success_message: 'Bu konuyu başarıyla bayrakladınız.'
feature_topic:
title: "Bu konuyu ön plana çıkar"
+ pin: "Şu zamana kadar bu konunun {{categoryLink}} kategorisinin başında görünmesini sağla"
confirm_pin: "Zaten başa tutturulan {{count}} konunuz var. Çok fazla konuyu başa tutturmak yeni ve anonim kullanıcılara sıkıntı çektirebilir. Bu kategoride bir konuyu başa tutturmak istediğinize emin misiniz?"
unpin: "Bu konuyu {{categoryLink}} kategorisinin en üstünden kaldır."
+ unpin_until: "Bu konuyu {{categoryLink}} kategorisinin başından kaldır ya da şu zamana kadar bekle: %{until}."
pin_note: "Kullanıcılar kendileri için konunun başa tutturulmasını kaldırabilir."
+ pin_validation: "Bu konuyu sabitlemek için bir tarih gerekli."
already_pinned:
zero: " {{categoryLink}} kategorisinde başa tutturulan herhangi bir konu yok."
one: "Şu an {{categoryLink}} kategorisinde başa tutturulan konular: 1."
other: "Şu an {{categoryLink}} kategorisinde başa tutturulan konular: {{count}}."
+ pin_globally: "Şu zamana kadar bu konunun bütün konu listelerinin başında yer almasını sağla"
confirm_pin_globally: "Zaten her yerde başa tutturulan {{count}} konunuz var. Çok fazla konuyu başa tutturmak yeni ve anonim kullanıcılara sıkıntı çektirebilir. Bir konuyu daha her yerde başa tutturmak istediğinizden emin misiniz?"
unpin_globally: "Bu konuyu tüm konu listelerinin en üstünden kaldır."
+ unpin_globally_until: "Bu konuyu bütün konu listelerinin başından kaldır ya da şu zamana kadar bekle: %{until}."
global_pin_note: "Kullanıcılar kendileri için konunun başa tutturulmasını kaldırabilir."
already_pinned_globally:
zero: "Her yerde başa tutturulan herhangi bir konu yok."
@@ -1005,6 +1090,12 @@ tr_TR:
instructions:
other: "Lütfen {{old_user}} kullanıcısına ait {{count}} gönderinin yeni sahibini seçin."
instructions_warn: "Bu gönderi ile ilgili geriye dönük biriken bildirimler yeni kullanıcıya aktarılmayacak. Uyarı: Şu an, yeni kullanıcıya hiç bir gönderi-tabanlı ek bilgi aktarılmıyor. Dikkatli olun."
+ change_timestamp:
+ title: "Değişiklik Zaman Bilgisi"
+ action: "değişiklik zaman bilgisi"
+ invalid_timestamp: "Zaman bilgisi gelecekte olamaz."
+ error: "Konunun zaman bilgisini değiştirirken bir hata oluştu."
+ instructions: "Lütfen konunun yeni zaman bilgisini seçiniz. Konudaki gönderiler aynı zaman farkına sahip olmaları için güncellenecekler."
multi_select:
select: 'seç'
selected: '({{count}}) seçildi'
@@ -1016,6 +1107,8 @@ tr_TR:
description:
other: {{count}} gönderi seçtiniz.
post:
+ reply: " {{replyAvatar}} {{usernameLink}}"
+ reply_topic: " {{link}}"
quote_reply: "alıntıyla cevapla"
edit: "{{link}} {{replyAvatar}} {{username}} düzenleniyor"
edit_reason: "Neden: "
@@ -1230,6 +1323,7 @@ tr_TR:
email_in_allow_strangers: "Hesabı olmayan, anonim kullanıcılardan e-posta kabul et"
email_in_disabled: "E-posta üzerinden yeni konu oluşturma özelliği Site Ayarları'nda devre dışı bırakılmış. E-posta üzerinden yeni konu oluşturma özelliğini etkinleştirmek için,"
email_in_disabled_click: '"e-postala" ayarını etkinleştir'
+ suppress_from_homepage: "Bu kategoriyi ana sayfadan gizle"
allow_badges_label: "Bu kategoride rozet verilmesine izin ver"
edit_permissions: "İzinleri Düzenle"
add_permission: "İzin Ekle"
@@ -1294,6 +1388,10 @@ tr_TR:
help: "Bu konuyu işaretlediniz"
locked:
help: "Bu konu kapatıldı; artık yeni cevaplar kabul edilmiyor"
+ archived:
+ help: "Bu başlık arşive kaldırıldı; donduruldu ve değiştirilemez"
+ locked_and_archived:
+ help: "Bu konu kapatıldı ve arşivlendi; yeni cevaplar kabul edemez ve değiştirilemez."
unpinned:
title: "Başa tutturma kaldırıldı"
help: "Bu konu sizin için başa tutturulmuyor; normal sıralama içerisinde görünecek"
@@ -1303,8 +1401,6 @@ tr_TR:
pinned:
title: "Başa Tutturuldu"
help: "Bu konu sizin için başa tutturuldu; kendi kategorisinin en üstünde görünecek"
- archived:
- help: "Bu başlık arşive kaldırıldı; donduruldu ve değiştirilemez"
invisible:
help: "Bu konu listelenmemiş; konu listelerinde görünmeyecek, ve sadece doğrudan bağlantı aracılığıyla erişilebilecek"
posts: "Gönderi"
@@ -1397,6 +1493,8 @@ tr_TR:
title: "Tüm Zamanlar"
yearly:
title: "Yıllık"
+ quarterly:
+ title: "Üç aylık"
monthly:
title: "Aylı"
weekly:
@@ -1405,6 +1503,7 @@ tr_TR:
title: "Günlük"
all_time: "Tüm Zamanlar"
this_year: "Yıl"
+ this_quarter: "Çeyrek"
this_month: "Ay"
this_week: "Hafta"
today: "Bugün"
@@ -1652,6 +1751,7 @@ tr_TR:
header: "Başlık"
top: "En Kısım"
footer: "Alt Kısım"
+ embedded_css: "Gömülü CSS"
head_tag:
text: ""
title: " etiketinden önce eklenecek HTML"
@@ -1743,6 +1843,7 @@ tr_TR:
sent_test: "gönderildi!"
delivery_method: "Gönderme Metodu"
preview_digest: "Özeti Önizle"
+ preview_digest_desc: "Durgun kullanıcılara gönderilen özet e-postaların içeriğini önizle."
refresh: "Yenile"
format: "Format"
html: "html"
@@ -2022,6 +2123,7 @@ tr_TR:
delete: "Sil"
cancel: "İptal et"
delete_confirm: "Bu kullanıcı alanını silmek istediğinize emin misiniz?"
+ options: "Seçenekler"
required:
title: "Kayıt olurken zorunlu mu?"
enabled: "gerekli"
@@ -2037,6 +2139,7 @@ tr_TR:
field_types:
text: 'Yazı Alanı'
confirm: 'Onay'
+ dropdown: "Açılır liste"
site_text:
none: "Düzenlemeye başlamak için içerik tipi seçin."
title: 'Yazı İçeriği'
@@ -2070,6 +2173,7 @@ tr_TR:
backups: "Yedekler"
login: "Oturum Açma"
plugins: "Eklentiler"
+ user_preferences: "Kullanıcı Tercihleri"
badges:
title: Rozetler
new_badge: Yeni Rozet
@@ -2143,6 +2247,29 @@ tr_TR:
name: "İsim"
image: "Görsel"
delete_confirm: ":%{name}: emojisini silmek istediğinize emin misiniz?"
+ embedding:
+ get_started: "Eğer Discourse'u bir başka web sitesine gömmek istiyorsanız, bu sitenin hostunu ekleyerek başlayın."
+ confirm_delete: "Bu hostu silmek istediğinize emin misiniz?"
+ sample: "Discourse konuları oluşturmak ve gömmek için aşağıdaki HTML kodunu sitenizde kullanın. REPLACE_ME'yi Discourse'u gömdüğünüz sayfanın tam URL'i ile değiştirin."
+ title: "Gömme"
+ host: "İzin Verilen Hostlar"
+ edit: "düzenle"
+ category: "Kategoriye Gönder"
+ add_host: "Host Ekle"
+ settings: "Ayarları Gömmek"
+ feed_settings: "Ayarları Besle"
+ feed_description: "Siteniz için bir RSS/ATOM beslemesi sağlamanız Discourse'un içeriğinizi içe aktarma yeteneğini geliştirebilir."
+ crawling_settings: "Böcek Ayarları"
+ crawling_description: "Discourse gönderileriniz için konular oluşturduğu zaman, eğer bir RSS/ATOM beslemesi yoksa içeriğinizi HTML'inizden ayrıştırmaya çalışacaktır. Bazen içeriğinizi çıkartmak çok zor olabilir, bu yüzden ayrıştırmayı kolaylaştırmak için CSS kuralları belirtme yeteneği sağlıyoruz."
+ embed_by_username: "Konu oluşturmak için kullanıcı adı"
+ embed_post_limit: "Gömmek için en büyük gönderi sayısı"
+ embed_username_key_from_feed: "Discourse kullanıcı adını beslemeden çekmek için anahtar"
+ embed_truncate: "Gömülü gönderileri buda"
+ embed_whitelist_selector: "Gömülüler içinde izin verilen elementler için CSS seçici"
+ embed_blacklist_selector: "Gömülülerden kaldırılan elementler için CSS seçici"
+ feed_polling_enabled: "Konuları RSS/ATOM aracılığıyla içe aktar"
+ feed_polling_url: "İstila etmek için RSS/ATOM beslemesi URL'i"
+ save: "Gömme Ayarlarını Kaydet"
permalink:
title: "Kalıcı Bağlantılar"
url: "Bağlantı"
@@ -2173,6 +2300,8 @@ tr_TR:
categories: 'g, c Kategoriler'
top: 'g, t En Popüler'
bookmarks: 'g, b İşaretliler'
+ profile: 'g, p Profil'
+ messages: 'g, m İletiler'
navigation:
title: 'Navigasyon'
jump: '# # numaralı gönderiye git'
@@ -2184,12 +2313,14 @@ tr_TR:
title: 'Uygulama'
create: 'c Yeni konu aç'
notifications: 'n Bildirileri aç'
+ hamburger_menu: '= Hamburger menüsünü aç'
user_profile_menu: 'p Kullanıcı menüsünü aç'
show_incoming_updated_topics: '. Güncellenmiş konuları göster'
search: '/ Arama'
help: '? Klavye yardımını göster'
dismiss_new_posts: 'x, r Yeni Konuları/Gönderleri Yoksay'
dismiss_topics: 'x, t Konuları Yoksay'
+ log_out: 'shift+zshift+z Çıkış Yapın'
actions:
title: 'Seçenekler'
bookmark_topic: 'f Konu işaretlenmesini aç/kapa'
@@ -2314,3 +2445,21 @@ tr_TR:
reader:
name: Okuyucu
description: 100'den fazla gönderiye sahip bir konudaki tüm gönderileri oku
+ popular_link:
+ name: Gözde Bağlantı
+ description: En az 50 kere tıklanmış harici bir bağlantı gönderildi
+ hot_link:
+ name: Sıcak Bağlantı
+ description: En az 300 kere tıklanmış harici bir bağlantı gönderildi
+ famous_link:
+ name: Ünlü Bağlantı
+ description: En az 1000 kere tıklanmış harici bir bağlantı gönderildi
+ google_search: |
+
Google'la Ara
+
+
+
diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml
index 52807c6bc4e..545d4d903a6 100644
--- a/config/locales/client.uk.yml
+++ b/config/locales/client.uk.yml
@@ -913,12 +913,12 @@ uk:
topic_statuses:
locked:
help: "цю тему закрито; нові відповіді більше не приймаються"
+ archived:
+ help: "цю тему заархівовано; вона заморожена і її не можна змінити"
unpinned:
title: "Не закріплені"
pinned:
title: "Закріплені"
- archived:
- help: "цю тему заархівовано; вона заморожена і її не можна змінити"
posts: "Дописи"
posts_lowercase: "дописи"
posts_long: "тема містить {{number}} дописів"
diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml
index 3d2ab622b94..b351c12c3c8 100644
--- a/config/locales/client.zh_CN.yml
+++ b/config/locales/client.zh_CN.yml
@@ -278,6 +278,9 @@ zh_CN:
mods_and_admins: "仅版主与管理员"
members_mods_and_admins: "仅组员、版主与管理员"
everyone: "任何人"
+ trust_levels:
+ title: "当这些用户加入时,信任等级将自动赋予给他们:"
+ none: "无"
user_action_groups:
'1': "给赞"
'2': "被赞"
@@ -337,6 +340,7 @@ zh_CN:
private_messages: "消息"
activity_stream: "活动"
preferences: "设置"
+ expand_profile: "展开"
bookmarks: "书签"
bio: "关于我"
invited_by: "邀请者为"
@@ -513,7 +517,7 @@ zh_CN:
title: "邀请"
user: "邀请用户"
sent: "已发送"
- none: "你还没有邀请过任何人。"
+ none: "没有未接受状态的邀请。"
truncated: "只显示前 {{count}} 个邀请。"
redeemed: "确认邀请"
redeemed_tab: "已确认"
@@ -776,6 +780,20 @@ zh_CN:
moved_post: "