Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Juan de Dios Herrero 2013-05-30 21:42:26 +02:00
commit 807f763fb0
10 changed files with 84 additions and 19 deletions

View File

@ -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)

View File

@ -85,6 +85,8 @@ class Category < ActiveRecord::Base
if name.present?
self.slug = Slug.for(name)
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)

View File

@ -25,7 +25,7 @@
<p><%= t 'powered_by_html' %></p>
<% 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) %>

View File

@ -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 <a href='/unread'>eine ungelesene</a>} other {hast <a href='/unread'># ungelesene</a>}} und {NEW, plural, one {<a href='/new'>ein neues</a> Thema} other {<a href='/new'># neue</a> Themen}} übrig, oder entdecke neue Themen in {catLink}"
read_more_MF: "Du {UNREAD, plural, one {hast <a href='/unread'>eine ungelesene</a>} other {hast <a href='/unread'># ungelesene</a>}} und {NEW, plural, one {<a href='/new'>ein neues</a> Thema} other {<a href='/new'># neue</a> 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&nbsp;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."

View File

@ -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 <a href="/admin/site_settings">die Einstellungen</a> um die fehlenden Einträge hinzuzufügen. <a href="https://github.com/discourse/discourse/wiki/The-Discourse-Admin-Quick-Start-Guide#enable-facebook-logins" target="_blank">Besuche den Leitfaden um mehr zu erfahren</a>.'
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 <a href="/admin/site_settings">die Einstellungen</a> um die fehlenden Einträge hinzuzufügen. <a href="https://github.com/discourse/discourse/wiki/The-Discourse-Admin-Quick-Start-Guide#enable-twitter-logins" target="_blank">Besuche den Leitfaden um mehr zu erfahren</a>.'
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 <a href="/admin/site_settings">die Einstellungen</a> um die fehlenden Einträge hinzuzufügen. <a href="https://github.com/discourse/discourse/wiki/The-Discourse-Admin-Quick-Start-Guide" target="_blank">Besuche den Leitfaden um mehr zu erfahren</a>.'
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. <a href="/sidekiq/retries" target="_blank">Zu den Fehlern in Sidekiq</a>.'
@ -386,6 +387,7 @@ de:
contact_email_missing: "Du hast noch keine Kontaktmail für die Seite hinterlegt. Bitte hinterlege diese in den <a href='/admin/site_settings'>Einstellungen</a> (siehe contact_email)."
contact_email_invalid: "Die Kontaktmail der Seite ist ungültig. Bitte bearbeite diese in den <a href='/admin/site_settings'>Einstellungen</a> (siehe contact_email)."
title_nag: "Der Titel der Seite wurde noch nicht angepasst. Bitte bearbeite diesen in den <a href='/admin/site_settings'>Einstellungen</a>."
gmail_for_email_warning: "Deine Seite verwendet Gmail um Mails zu senden. <a href='http://support.google.com/a/bin/answer.py?hl=en&answer=166852' target='_blank'>Gmail hat eine Limite zum Senden von Mails</a>. 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"

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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 }

View File

@ -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')