From 571e62a27a5050d234a5c466c5bdbb6e32b4760f Mon Sep 17 00:00:00 2001 From: Michael Kessler Date: Thu, 30 May 2013 13:13:53 +0200 Subject: [PATCH 1/4] Keep German translations in sync. --- config/locales/client.de.yml | 18 +++++++++++++++--- config/locales/server.de.yml | 12 ++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index 7485269dddf..53e74c9b11c 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -316,6 +316,9 @@ de: facebook: title: "Mit Facebook" message: "Authentisierung via Facebook (stelle sicher, dass der Popup-Blocker deaktiviert ist)" + cas: + title: "Mit CAS" + message: "Authentisierung via CAS (stelle sicher, dass der Popup-Blocker deaktiviert ist)" yahoo: title: "Mit Yahoo" message: "Authentisierung via Yahoo (stelle sicher, dass der Popup-Blocker deaktiviert ist)" @@ -432,6 +435,10 @@ de: no_results: "Nichts gefunden." searching: "Suche ..." + prefer: + user: "Die Suche bevorzugt Resultate von @{{username}}" + category: "Die Suche bevorzugt Resultate in der Kategorie {{category}}" + site_map: "Gehe zu einer anderen Themenübersicht oder Kategorie" go_back: 'Zurück' current_user: 'Gehe zu deinem Nutzerprofil' @@ -498,6 +505,11 @@ de: toggle_information: "Themendetails ein-/ausblenden" read_more_in_category: "Möchtest Du mehr lesen? Finde andere Themen in {{catLink}} oder {{latestLink}}." read_more: "Möchtest Du mehr lesen? {{catLink}} oder {{latestLink}}." + + # keys ending with _MF use message format, see /spec/components/js_local_helper_spec.rb for samples + read_more_in_category_MF: "Du {UNREAD, plural, one {hast eine ungelesene} other {hast # ungelesene}} und {NEW, plural, one {ein neues Thema} other {# neue Themen}} übrig, oder entdecke neue Themen in {catLink}" + read_more_MF: "Du {UNREAD, plural, one {hast eine ungelesene} other {hast # ungelesene}} und {NEW, plural, one {ein neues Thema} other {# neue Themen}} übrig, oder {latestLink}" + browse_all_categories: Zeige alle Kategorien view_latest_topics: zeige aktuelle Themen @@ -851,7 +863,7 @@ de: replies: "Antworten" views_long: "Dieses Thema wurde {{number}} aufgerufen" activity: "Aktivität" - likes: "Gefällt mir" + likes: "Gefällt mir" top_contributors: "Teilnehmer" category_title: "Kategorie" history: "Verlauf" @@ -909,8 +921,8 @@ de: moderator: 'Moderator' dashboard: - title: "Administratorkonsole" - version: "Installierte Version" + title: "Übersicht" + version: "Version" up_to_date: "Discourse ist aktuell." critical_available: "Ein kritisches Update ist verfügbar." updates_available: "Updates sind verfügbar." diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index 0d1122b2139..fc91448894f 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -379,6 +379,7 @@ de: queue_size_warning: 'Die Anzahl der Aktionen in der Warteschlange ist mit %{queue_size} ziemlich hoch. Das könnte auf ein Problem mit dem Sidekiq Prozess(en) hinweisen, oder du musst mehr Sidekiq Arbeiter hinzufügen.' memory_warning: 'Dein Server läuft mit weniger als 1 GB Hauptspeicher. Mindestens 1 GB Hauptspeicher werden empfohlen.' facebook_config_warning: 'Der Server erlaubt die Anmeldung mit Facebook (enable_facebook_logins), aber die App ID und der Geheimcode sind nicht gesetzt. Besuche die Einstellungen um die fehlenden Einträge hinzuzufügen. Besuche den Leitfaden um mehr zu erfahren.' + cas_config_warning: 'Der Server erlaubt die Anmeldung mit CAS (enable_cas_logins), aber der Hostname und die Domäne sind nicht gesetzt.' twitter_config_warning: 'Der Server erlaubt die Anmeldung mit Facebook Twitter (enable_twitter_logins), aber der Schlüssel und der Geheimcode sind nicht gesetzt. Besuche die Einstellungen um die fehlenden Einträge hinzuzufügen. Besuche den Leitfaden um mehr zu erfahren.' github_config_warning: 'Der Server erlaubt die Anmeldung mit Facebook GitHub (enable_github_logins), aber die Kunden ID und der Geheimcode sind nicht gesetzt. Besuche die Einstellungen um die fehlenden Einträge hinzuzufügen. Besuche den Leitfaden um mehr zu erfahren.' failing_emails_warning: 'Es konnten insgesamt %{num_failed_jobs} Mails nicht versendet werden. Bitte überprüfe die Einstellungen in config/environments/production.rb und stelle die Richtigkeit der config.action_mailer Einstellungen. Zu den Fehlern in Sidekiq.' @@ -386,6 +387,7 @@ de: contact_email_missing: "Du hast noch keine Kontaktmail für die Seite hinterlegt. Bitte hinterlege diese in den Einstellungen (siehe contact_email)." contact_email_invalid: "Die Kontaktmail der Seite ist ungültig. Bitte bearbeite diese in den Einstellungen (siehe contact_email)." title_nag: "Der Titel der Seite wurde noch nicht angepasst. Bitte bearbeite diesen in den Einstellungen." + gmail_for_email_warning: "Deine Seite verwendet Gmail um Mails zu senden. Gmail hat eine Limite zum Senden von Mails. Um die Mail-Zustellung zu gewährleisten, solltest du einen anderen Mail Service in Erwägung ziehen." content_types: education_new_reply: @@ -488,13 +490,15 @@ de: email_domains_whitelist: "Eine durch senkrechte Striche getrennte Liste von erlaubte Maildomains. WARNUNG: Benutzer mit Mailadressen anderer Domains können sich nicht registrieren." version_checks: "Erfrage Versionsupdate bei Discourse Hub und zeige Versionsbenachrichtigungen auf der Administratorkonsole /admin." - port: "Benutze diesen HTTP-Port anstatt den Standardport 80. Diese Feld leer lassen heißt 'keinen'. Dient hauptsächlich Entwicklungszwecken." - force_hostname: "Spezifiziere einen Hostnamen in der URL. Dieses Feld leer lassen heißt 'keinen'. Dient hauptsächlich Entwicklungszwecken." + port: "NUR FÜR ENTWICKLER! ACHTUNG! Benutze diesen HTTP-Port anstatt den Standardport 80. Diese Feld leer lassen heißt 'keinen'. Dient hauptsächlich Entwicklungszwecken." + force_hostname: "NUR FÜR ENTWICKLER! ACHTUNG! Spezifiziere einen Hostnamen in der URL. Dieses Feld leer lassen heißt 'keinen'. Dient hauptsächlich Entwicklungszwecken." invite_expiry_days: "Tage, die Nutzereinladungen gültig bleiben." # TODO: perhaps we need a way of protcting these settings for hosted solution, global settings ... + enable_local_logins: "Aktiviere lokale Authentisierung" + enable_local_account_create: "Aktiviere lokale Kontoerstellung" enable_google_logins: "Aktiviere Google Authentisierung." enable_yahoo_logins: "Aktiviere Yahoo Authentisierung." @@ -506,6 +510,10 @@ de: facebook_app_id: "App-ID für Facebook Authentisierung, registriert auf https://developers.facebook.com/apps" facebook_app_secret: "App Secret für Facebook Authentisierung, registriert auf https://developers.facebook.com/apps" + enable_cas_logins: "Aktiviere CAS Authentisierung" + cas_hostname: "Hostname für den CAS Server" + cas_domainname: "Domäne für die generierten Mailadresses des CAS Server" + enable_github_logins: "Aktiviere Github Authentisierung (benötigt github_client_id und github_client_secret)." github_client_id: "Client-ID für Github Authentisierung, registriert auf https://github.com/settings/applications" github_client_secret: "Client Secret für Github Authentisierung, registriert auf https://github.com/settings/applications" From 5d444be72b94664059b41dd57708ec364bed6ac6 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Wed, 29 May 2013 18:01:25 -0400 Subject: [PATCH 2/4] Support incomplete topic urls like /t/just-a-slug; fix error when using route /t/:topic_id/:post_number --- app/controllers/topics_controller.rb | 9 ++++- app/views/topics/show.html.erb | 2 +- config/routes.rb | 2 +- spec/controllers/topics_controller_spec.rb | 39 ++++++++++++++++------ 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 50ccc67abf4..5f19c2c6d55 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -21,9 +21,16 @@ class TopicsController < ApplicationController before_filter :consider_user_for_promotion, only: :show - skip_before_filter :check_xhr, only: [:avatar, :show, :feed] + skip_before_filter :check_xhr, only: [:avatar, :show, :feed, :redirect_to_show] caches_action :avatar, cache_path: Proc.new {|c| "#{c.params[:post_number]}-#{c.params[:topic_id]}" } + def redirect_to_show + topic_query = ((num = params[:id].to_i) > 0 and num.to_s == params[:id].to_s) ? Topic.where(id: num) : Topic.where(slug: params[:id]) + topic = topic_query.includes(:category).first + raise Discourse::NotFound unless topic + redirect_to topic.relative_url + end + def show opts = params.slice(:username_filters, :best_of, :page, :post_number, :posts_before, :posts_after, :best) @topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts) diff --git a/app/views/topics/show.html.erb b/app/views/topics/show.html.erb index 9bdb0734970..e6747bcc452 100644 --- a/app/views/topics/show.html.erb +++ b/app/views/topics/show.html.erb @@ -25,7 +25,7 @@

<%= t 'powered_by_html' %>

<% content_for :head do %> - <%= auto_discovery_link_tag(@topic_view, {action: :feed, format: :rss}, title: t('rss_posts_in_topic', topic: @topic_view.title), type: 'application/rss+xml') %> + <%= auto_discovery_link_tag(@topic_view, {action: :feed, format: :rss, slug: @topic_view.topic.slug, topic_id: @topic_view.topic.id}, title: t('rss_posts_in_topic', topic: @topic_view.title), type: 'application/rss+xml') %> <%= crawlable_meta_data(title: @topic_view.title, description: @topic_view.summary, image: @topic_view.image_url) %> diff --git a/config/routes.rb b/config/routes.rb index c26799d47fd..3db7e346e8d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -182,7 +182,7 @@ Discourse::Application.routes.draw do get 'search' => 'search#query' # Topics resource - get 't/:id' => 'topics#show' + get 't/:id' => 'topics#redirect_to_show' delete 't/:id' => 'topics#destroy' put 't/:id' => 'topics#update' post 't' => 'topics#create' diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb index ff790c9fc1c..4d5e0afe4be 100644 --- a/spec/controllers/topics_controller_spec.rb +++ b/spec/controllers/topics_controller_spec.rb @@ -369,18 +369,18 @@ describe TopicsController do let!(:p2) { Fabricate(:post, user: topic.user) } it 'shows a topic correctly' do - xhr :get, :show, id: topic.id + xhr :get, :show, topic_id: topic.id, slug: topic.slug response.should be_success end it 'records a view' do - lambda { xhr :get, :show, id: topic.id }.should change(View, :count).by(1) + lambda { xhr :get, :show, topic_id: topic.id, slug: topic.slug }.should change(View, :count).by(1) end it 'tracks a visit for all html requests' do current_user = log_in(:coding_horror) TopicUser.expects(:track_visit!).with(topic, current_user) - get :show, id: topic.id + get :show, topic_id: topic.id, slug: topic.slug end context 'consider for a promotion' do @@ -394,7 +394,7 @@ describe TopicsController do it "reviews the user for a promotion if they're new" do user.update_column(:trust_level, TrustLevel.levels[:newuser]) Promotion.any_instance.expects(:review) - get :show, id: topic.id + get :show, topic_id: topic.id, slug: topic.slug end end @@ -403,40 +403,59 @@ describe TopicsController do it 'grabs first page when no filter is provided' do SiteSetting.stubs(:posts_per_page).returns(20) TopicView.any_instance.expects(:filter_posts_in_range).with(0, 20) - xhr :get, :show, id: topic.id + xhr :get, :show, topic_id: topic.id, slug: topic.slug end it 'grabs first page when first page is provided' do SiteSetting.stubs(:posts_per_page).returns(20) TopicView.any_instance.expects(:filter_posts_in_range).with(0, 20) - xhr :get, :show, id: topic.id, page: 1 + xhr :get, :show, topic_id: topic.id, slug: topic.slug, page: 1 end it 'grabs correct range when a page number is provided' do SiteSetting.stubs(:posts_per_page).returns(20) TopicView.any_instance.expects(:filter_posts_in_range).with(20, 40) - xhr :get, :show, id: topic.id, page: 2 + xhr :get, :show, topic_id: topic.id, slug: topic.slug, page: 2 end it 'delegates a post_number param to TopicView#filter_posts_near' do TopicView.any_instance.expects(:filter_posts_near).with(p2.post_number) - xhr :get, :show, id: topic.id, post_number: p2.post_number + xhr :get, :show, topic_id: topic.id, slug: topic.slug, post_number: p2.post_number end it 'delegates a posts_after param to TopicView#filter_posts_after' do TopicView.any_instance.expects(:filter_posts_after).with(p1.post_number) - xhr :get, :show, id: topic.id, posts_after: p1.post_number + xhr :get, :show, topic_id: topic.id, slug: topic.slug, posts_after: p1.post_number end it 'delegates a posts_before param to TopicView#filter_posts_before' do TopicView.any_instance.expects(:filter_posts_before).with(p2.post_number) - xhr :get, :show, id: topic.id, posts_before: p2.post_number + xhr :get, :show, topic_id: topic.id, slug: topic.slug, posts_before: p2.post_number end end end + describe 'redirect_to_show' do + let(:topic) { Fabricate(:topic) } + + it 'raises an error if topic is not found' do + get :redirect_to_show, id: 121212121212 + expect(response.status).to eq(404) + end + + it 'redirects to the correct topic when given its id' do + get :redirect_to_show, id: topic.id + expect(response).to redirect_to(topic.relative_url) + end + + it 'redirects to the correct topic when given its slug' do + get :redirect_to_show, id: topic.slug + expect(response).to redirect_to(topic.relative_url) + end + end + describe '#feed' do let(:topic) { Fabricate(:post).topic } From 7d5c313456d542eba2e9541811df4729eefc372b Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Thu, 30 May 2013 11:09:09 -0400 Subject: [PATCH 3/4] Don't allow category slugs that are numbers --- app/models/category.rb | 5 +++++ spec/models/category_spec.rb | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/app/models/category.rb b/app/models/category.rb index 971d5d0fb58..d52a6941d25 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -85,6 +85,11 @@ class Category < ActiveRecord::Base if name.present? self.slug = Slug.for(name) + # Reject slugs that only contain numbers, because that's indistinguishable from an id. + self.slug = '' unless self.slug =~ /[^\d]/ + + return if self.slug.blank? + # If a category with that slug already exists, set the slug to nil so the category can be found # another way. category = Category.where(slug: self.slug) diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index cb4f937bee8..0c3f8d12e6b 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -104,6 +104,14 @@ describe Category do end end + describe 'slug would be a number' do + let(:category) { Fabricate(:category, name: "電車男 2") } + + it 'creates a blank slug' do + category.slug.should be_blank + end + end + describe 'after create' do before do @category = Fabricate(:category, name: 'Amazing Category') From b82a5dfd5642b4b8440752d1cb9f612785b94ca7 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Thu, 30 May 2013 11:54:02 -0400 Subject: [PATCH 4/4] Move logic to reject slugs that are just numbers into the slug module --- app/models/category.rb | 3 --- lib/slug.rb | 3 ++- spec/components/slug_spec.rb | 8 ++++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/models/category.rb b/app/models/category.rb index d52a6941d25..c7e3e9d9dff 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -85,9 +85,6 @@ class Category < ActiveRecord::Base if name.present? self.slug = Slug.for(name) - # Reject slugs that only contain numbers, because that's indistinguishable from an id. - self.slug = '' unless self.slug =~ /[^\d]/ - return if self.slug.blank? # If a category with that slug already exists, set the slug to nil so the category can be found diff --git a/lib/slug.rb b/lib/slug.rb index 08555448f60..f44bce6fee1 100644 --- a/lib/slug.rb +++ b/lib/slug.rb @@ -6,7 +6,8 @@ module Slug def self.for(string) - string.parameterize.gsub("_", "-") + slug = string.parameterize.gsub("_", "-") + slug =~ /[^\d]/ ? slug : '' # Reject slugs that only contain numbers, because they would be indistinguishable from id's. end end diff --git a/spec/components/slug_spec.rb b/spec/components/slug_spec.rb index 614dce90810..3c5d23cbcdf 100644 --- a/spec/components/slug_spec.rb +++ b/spec/components/slug_spec.rb @@ -43,5 +43,13 @@ describe Slug do Slug.for("o_o_o").should == "o-o-o" end + it "doesn't generate slugs that are just numbers" do + Slug.for('123').should be_blank + end + + it "doesn't generate slugs that are just numbers" do + Slug.for('電車男 2').should be_blank + end + end