mirror of
https://github.com/discourse/discourse.git
synced 2025-03-26 00:25:39 +08:00
Merge branch 'master' of github.com:discourse/discourse
This commit is contained in:
commit
37fa61ab51
app
assets
javascripts/discourse
controllers
helpers
templates/user
views
stylesheets
controllers
models
views/robots_txt
config
dbs/export
lib
spec
components
controllers
models
@ -129,7 +129,6 @@ window.Discourse.ComposerController = Ember.Controller.extend Discourse.Presence
|
|||||||
click: ->
|
click: ->
|
||||||
if @get('content.composeState') == Discourse.Composer.DRAFT
|
if @get('content.composeState') == Discourse.Composer.DRAFT
|
||||||
@set('content.composeState', Discourse.Composer.OPEN)
|
@set('content.composeState', Discourse.Composer.OPEN)
|
||||||
false
|
|
||||||
|
|
||||||
shrink: ->
|
shrink: ->
|
||||||
if @get('content.reply') == @get('content.originalText') then @close() else @collapse()
|
if @get('content.reply') == @get('content.originalText') then @close() else @collapse()
|
||||||
|
@ -60,12 +60,13 @@ Handlebars.registerHelper 'avatar', (user, options) ->
|
|||||||
user = Ember.Handlebars.get(this, user, options) if typeof user is 'string'
|
user = Ember.Handlebars.get(this, user, options) if typeof user is 'string'
|
||||||
username = Em.get(user, 'username')
|
username = Em.get(user, 'username')
|
||||||
username ||= Em.get(user, options.hash.usernamePath)
|
username ||= Em.get(user, options.hash.usernamePath)
|
||||||
|
title = Em.get(user, 'title') || Em.get(user, 'description') unless options.hash.ignoreTitle
|
||||||
|
|
||||||
new Handlebars.SafeString Discourse.Utilities.avatarImg(
|
new Handlebars.SafeString Discourse.Utilities.avatarImg(
|
||||||
size: options.hash.imageSize
|
size: options.hash.imageSize
|
||||||
extraClasses: Em.get(user, 'extras') || options.hash.extraClasses
|
extraClasses: Em.get(user, 'extras') || options.hash.extraClasses
|
||||||
username: username
|
username: username
|
||||||
title: Em.get(user, 'title') || Em.get(user, 'description') || username
|
title: title || username
|
||||||
avatarTemplate: Ember.get(user, 'avatar_template') || options.hash.avatarTemplate
|
avatarTemplate: Ember.get(user, 'avatar_template') || options.hash.avatarTemplate
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -125,4 +126,9 @@ Handlebars.registerHelper 'date', (property, options) ->
|
|||||||
|
|
||||||
new Handlebars.SafeString("<span class='date' title='#{fullReadable}'>#{displayDate}</span>")
|
new Handlebars.SafeString("<span class='date' title='#{fullReadable}'>#{displayDate}</span>")
|
||||||
|
|
||||||
|
Handlebars.registerHelper 'personalizedName', (property, options) ->
|
||||||
|
name = Ember.Handlebars.get(this, property, options);
|
||||||
|
username = Ember.Handlebars.get(this, options.hash.usernamePath, options) if options.hash.usernamePath
|
||||||
|
|
||||||
|
return name unless username == Discourse.get('currentUser.username')
|
||||||
|
return Em.String.i18n('you')
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
{{#collection contentBinding="stream" itemClass="item"}}
|
{{#collection contentBinding="stream" itemClass="item"}}
|
||||||
{{#with view.content}}
|
{{#with view.content}}
|
||||||
<div class='clearfix info'>
|
<div class='clearfix info'>
|
||||||
<a href="/users/{{unbound username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="large" extraClasses="actor" avatarTemplatePath="avatar_template"}}</div></a>
|
<a href="/users/{{unbound username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="large" extraClasses="actor" avatarTemplatePath="avatar_template" ignoreTitle="true"}}</div></a>
|
||||||
<span class='time'>{{date path="created_at" leaveAgo="true"}}</span>
|
<span class='time'>{{date path="created_at" leaveAgo="true"}}</span>
|
||||||
<a class='name' href="{{unbound postUrl}}">{{unbound name}}</a><br>
|
<a class="title" href="{{unbound postUrl}}">{{unbound title}}</a><br>
|
||||||
|
<a class='name' href="/users/{{unbound username}}">{{personalizedName name usernamePath="username"}}</a>
|
||||||
<span class='type'>{{unbound description}}</span>
|
<span class='type'>{{unbound description}}</span>
|
||||||
<span class='title'><span class="post-number">#{{unbound post_number}}</span> <a href="{{unbound postUrl}}">{{unbound title}}</a></span>
|
<a class="post-number" href="{{unbound postUrl}}">#{{unbound post_number}}</a>
|
||||||
</div>
|
</div>
|
||||||
<p class='excerpt'>
|
<p class='excerpt'>
|
||||||
{{{unbound excerpt}}}
|
{{{unbound excerpt}}}
|
||||||
|
@ -129,7 +129,7 @@ window.Discourse.ComposerView = window.Discourse.View.extend
|
|||||||
Discourse.UserSearch.search
|
Discourse.UserSearch.search
|
||||||
term: term,
|
term: term,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
exclude: selected
|
exclude: selected.concat [Discourse.get('currentUser.username')]
|
||||||
onChangeItems: (items) =>
|
onChangeItems: (items) =>
|
||||||
items = $.map items, (i) -> if i.username then i.username else i
|
items = $.map items, (i) -> if i.username then i.username else i
|
||||||
@set('content.targetUsernames', items.join(","))
|
@set('content.targetUsernames', items.join(","))
|
||||||
|
@ -347,7 +347,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.reply-to-tab {
|
.reply-to-tab {
|
||||||
z-index: 999;
|
z-index: 980;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: $darkish_gray;
|
color: $darkish_gray;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -241,10 +241,6 @@
|
|||||||
color: lighten($black, 30%);
|
color: lighten($black, 30%);
|
||||||
}
|
}
|
||||||
.item {
|
.item {
|
||||||
.post-number {
|
|
||||||
color: lighten($black, 40%);
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid #b9b9b9;
|
border: 1px solid #b9b9b9;
|
||||||
@ -266,7 +262,7 @@
|
|||||||
float: left;
|
float: left;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
.name {
|
.title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -1419,7 +1419,7 @@ body {
|
|||||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
|
||||||
}
|
}
|
||||||
:-moz-placeholder {
|
:-moz-placeholder {
|
||||||
color: #999999;
|
color: #999999 !important;
|
||||||
}
|
}
|
||||||
::-webkit-input-placeholder {
|
::-webkit-input-placeholder {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
|
@ -3,7 +3,7 @@ class Admin::UsersController < Admin::AdminController
|
|||||||
def index
|
def index
|
||||||
# Sort order
|
# Sort order
|
||||||
if params[:query] == "active"
|
if params[:query] == "active"
|
||||||
@users = User.order("COALESCE(last_seen_at, '01-01-1970') DESC, username")
|
@users = User.order("COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC, username")
|
||||||
else
|
else
|
||||||
@users = User.order("created_at DESC, username")
|
@users = User.order("created_at DESC, username")
|
||||||
end
|
end
|
||||||
|
@ -76,7 +76,10 @@ class ApplicationController < ActionController::Base
|
|||||||
|
|
||||||
def store_preloaded(key, json)
|
def store_preloaded(key, json)
|
||||||
@preloaded ||= {}
|
@preloaded ||= {}
|
||||||
@preloaded[key] = json
|
# I dislike that there is a gsub as opposed to a gsub!
|
||||||
|
# but we can not be mucking with user input, I wonder if there is a way
|
||||||
|
# to inject this safty deeper in the library or even in AM serializer
|
||||||
|
@preloaded[key] = json.gsub("</", "<\\/")
|
||||||
end
|
end
|
||||||
|
|
||||||
# If we are rendering HTML, preload the session data
|
# If we are rendering HTML, preload the session data
|
||||||
|
15
app/controllers/robots_txt_controller.rb
Normal file
15
app/controllers/robots_txt_controller.rb
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
class RobotsTxtController < ApplicationController
|
||||||
|
layout false
|
||||||
|
skip_before_filter :check_xhr
|
||||||
|
skip_before_filter :check_restricted_access
|
||||||
|
|
||||||
|
def index
|
||||||
|
path = if SiteSetting.allow_index_in_robots_txt && !SiteSetting.restrict_access
|
||||||
|
:index
|
||||||
|
else
|
||||||
|
:no_index
|
||||||
|
end
|
||||||
|
|
||||||
|
render path, content_type: 'text/plain'
|
||||||
|
end
|
||||||
|
end
|
@ -106,6 +106,7 @@ footer:after{ content: '#{error}' }"
|
|||||||
|
|
||||||
@lock.synchronize do
|
@lock.synchronize do
|
||||||
style = self.where(key: key).first
|
style = self.where(key: key).first
|
||||||
|
style.ensure_stylesheet_on_disk!
|
||||||
@cache[key] = style
|
@cache[key] = style
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -140,9 +141,13 @@ footer:after{ content: '#{error}' }"
|
|||||||
Digest::MD5.hexdigest(self.stylesheet)
|
Digest::MD5.hexdigest(self.stylesheet)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cache_fullpath
|
||||||
|
"#{Rails.root}/public/#{CACHE_PATH}"
|
||||||
|
end
|
||||||
|
|
||||||
def ensure_stylesheet_on_disk!
|
def ensure_stylesheet_on_disk!
|
||||||
path = stylesheet_fullpath
|
path = stylesheet_fullpath
|
||||||
dir = "#{Rails.root}/public/#{CACHE_PATH}"
|
dir = cache_fullpath
|
||||||
FileUtils.mkdir_p(dir)
|
FileUtils.mkdir_p(dir)
|
||||||
unless File.exists?(path)
|
unless File.exists?(path)
|
||||||
File.open(path, "w") do |f|
|
File.open(path, "w") do |f|
|
||||||
@ -152,23 +157,18 @@ footer:after{ content: '#{error}' }"
|
|||||||
end
|
end
|
||||||
|
|
||||||
def stylesheet_filename
|
def stylesheet_filename
|
||||||
file = ""
|
"/#{self.key}.css"
|
||||||
dir = "#{Rails.root}/public/#{CACHE_PATH}"
|
|
||||||
path = dir + file
|
|
||||||
|
|
||||||
"/#{CACHE_PATH}/#{self.key}.css"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def stylesheet_fullpath
|
def stylesheet_fullpath
|
||||||
"#{Rails.root}/public#{self.stylesheet_filename}"
|
"#{self.cache_fullpath}#{self.stylesheet_filename}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def stylesheet_link_tag
|
def stylesheet_link_tag
|
||||||
return "" unless self.stylesheet.present?
|
return "" unless self.stylesheet.present?
|
||||||
return @stylesheet_link_tag if @stylesheet_link_tag
|
return @stylesheet_link_tag if @stylesheet_link_tag
|
||||||
ensure_stylesheet_on_disk!
|
ensure_stylesheet_on_disk!
|
||||||
@stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"#{self.stylesheet_filename}?#{self.stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
|
@stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"/#{CACHE_PATH}#{self.stylesheet_filename}?#{self.stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -30,8 +30,6 @@ class SiteSetting < ActiveRecord::Base
|
|||||||
client_setting(:max_topic_title_length, 255)
|
client_setting(:max_topic_title_length, 255)
|
||||||
client_setting(:flush_timings_secs, 5)
|
client_setting(:flush_timings_secs, 5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# settings only available server side
|
# settings only available server side
|
||||||
setting(:auto_track_topics_after, 60000)
|
setting(:auto_track_topics_after, 60000)
|
||||||
setting(:long_polling_interval, 15000)
|
setting(:long_polling_interval, 15000)
|
||||||
@ -91,6 +89,8 @@ class SiteSetting < ActiveRecord::Base
|
|||||||
|
|
||||||
setting(:allow_duplicate_topic_titles, false)
|
setting(:allow_duplicate_topic_titles, false)
|
||||||
|
|
||||||
|
setting(:add_rel_nofollow_to_user_content, true)
|
||||||
|
setting(:exclude_rel_nofollow_domains, '')
|
||||||
setting(:post_excerpt_maxlength, 300)
|
setting(:post_excerpt_maxlength, 300)
|
||||||
setting(:post_onebox_maxlength, 500)
|
setting(:post_onebox_maxlength, 500)
|
||||||
setting(:best_of_score_threshold, 15)
|
setting(:best_of_score_threshold, 15)
|
||||||
@ -102,6 +102,8 @@ class SiteSetting < ActiveRecord::Base
|
|||||||
# we need to think of a way to force users to enter certain settings, this is a minimal config thing
|
# we need to think of a way to force users to enter certain settings, this is a minimal config thing
|
||||||
setting(:notification_email, 'info@discourse.org')
|
setting(:notification_email, 'info@discourse.org')
|
||||||
|
|
||||||
|
setting(:allow_index_in_robots_txt, true)
|
||||||
|
|
||||||
setting(:send_welcome_message, true)
|
setting(:send_welcome_message, true)
|
||||||
|
|
||||||
setting(:twitter_consumer_key, '')
|
setting(:twitter_consumer_key, '')
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
class UserSearch
|
class UserSearch
|
||||||
|
|
||||||
def self.search term, topic_id = nil
|
def self.search term, topic_id = nil
|
||||||
User.find_by_sql sql(term, topic_id)
|
sql = User.sql_builder(
|
||||||
end
|
"select id, username, name, email from users u
|
||||||
|
/*left_join*/
|
||||||
|
/*where*/
|
||||||
|
/*order_by*/")
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def self.sql term, topic_id
|
|
||||||
sql = "select id, username, name, email from users u "
|
|
||||||
if topic_id
|
if topic_id
|
||||||
sql << "left join (select distinct p.user_id from posts p where topic_id = :topic_id) s on
|
sql.left_join "(select distinct p.user_id from posts p where topic_id = :topic_id) s on s.user_id = u.id", topic_id: topic_id
|
||||||
s.user_id = u.id "
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if term.present?
|
if term.present?
|
||||||
sql << "where username ilike :term_like or
|
sql.where("username_lower like :term_like or
|
||||||
to_tsvector('simple', name) @@
|
to_tsvector('simple', name) @@
|
||||||
to_tsquery('simple',
|
to_tsquery('simple',
|
||||||
regexp_replace(
|
regexp_replace(
|
||||||
@ -22,22 +21,18 @@ class UserSearch
|
|||||||
cast(plainto_tsquery(:term) as text)
|
cast(plainto_tsquery(:term) as text)
|
||||||
,'\''(?: |$)', ':*''', 'g'),
|
,'\''(?: |$)', ':*''', 'g'),
|
||||||
'''', '', 'g')
|
'''', '', 'g')
|
||||||
) "
|
)", term: term, term_like: "#{term.downcase}%")
|
||||||
|
|
||||||
|
sql.order_by "case when username_lower = :term then 0 else 1 end asc"
|
||||||
end
|
end
|
||||||
|
|
||||||
sql << "order by case when username_lower = :term then 0 else 1 end asc, "
|
|
||||||
if topic_id
|
if topic_id
|
||||||
sql << " case when s.user_id is null then 0 else 1 end desc, "
|
sql.order_by "case when s.user_id is null then 0 else 1 end desc"
|
||||||
end
|
end
|
||||||
|
|
||||||
sql << " case when last_seen_at is null then 0 else 1 end desc, last_seen_at desc, username asc limit(20)"
|
sql.order_by "case when last_seen_at is null then 0 else 1 end desc, last_seen_at desc, username asc limit(20)"
|
||||||
|
|
||||||
sanitize_sql_array(sql, topic_id: topic_id, term_like: "#{term}%", term: term)
|
sql.exec
|
||||||
end
|
|
||||||
|
|
||||||
def self.sanitize_sql_array *args
|
|
||||||
ActiveRecord::Base.send(:sanitize_sql_array, args)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
@ -1,5 +1,2 @@
|
|||||||
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
|
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
|
||||||
#
|
#
|
||||||
# To ban all spiders from the entire site uncomment the next two lines:
|
|
||||||
# User-Agent: *
|
|
||||||
# Disallow: /
|
|
6
app/views/robots_txt/no_index.erb
Normal file
6
app/views/robots_txt/no_index.erb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
|
||||||
|
#
|
||||||
|
User-Agent: *
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
|
@ -20,8 +20,6 @@ module Discourse
|
|||||||
# -- all .rb files in that directory are automatically loaded.
|
# -- all .rb files in that directory are automatically loaded.
|
||||||
|
|
||||||
require 'discourse'
|
require 'discourse'
|
||||||
# initializes message bus too early, not picking on redis settings, needs to be fixed
|
|
||||||
# require 'message_bus_diags'
|
|
||||||
|
|
||||||
# Custom directories with classes and modules you want to be autoloadable.
|
# Custom directories with classes and modules you want to be autoloadable.
|
||||||
config.autoload_paths += %W(#{config.root}/app/serializers)
|
config.autoload_paths += %W(#{config.root}/app/serializers)
|
||||||
|
1
config/initializers/sql_builder.rb
Normal file
1
config/initializers/sql_builder.rb
Normal file
@ -0,0 +1 @@
|
|||||||
|
require 'sql_builder'
|
@ -219,6 +219,8 @@ en:
|
|||||||
max_image_width: "maximum width for an image in a post"
|
max_image_width: "maximum width for an image in a post"
|
||||||
category_featured_topics: "number of topics displayed in the category list"
|
category_featured_topics: "number of topics displayed in the category list"
|
||||||
popup_delay: "Length of time in ms before popups appear on the screen"
|
popup_delay: "Length of time in ms before popups appear on the screen"
|
||||||
|
add_rel_nofollow_to_user_content: "Add rel nofollow to all submitted user content, except for internal links (including parent domains) changing this requires you update all your baked markdown"
|
||||||
|
exclude_rel_nofollow_domains: "A comma delimited list of domains where nofollow is not added (tld.com will automatically allow sub.tld.com as well)"
|
||||||
post_excerpt_maxlength: "Maximum length in chars of a post's excerpt."
|
post_excerpt_maxlength: "Maximum length in chars of a post's excerpt."
|
||||||
post_onebox_maxlength: "Maximum length of a oneboxed discourse post."
|
post_onebox_maxlength: "Maximum length of a oneboxed discourse post."
|
||||||
category_post_template: "The post template that appears once you create a category"
|
category_post_template: "The post template that appears once you create a category"
|
||||||
@ -257,6 +259,9 @@ en:
|
|||||||
posts_per_page: "How many posts are returned on a topic page"
|
posts_per_page: "How many posts are returned on a topic page"
|
||||||
system_username: "Username that sends system messages"
|
system_username: "Username that sends system messages"
|
||||||
send_welcome_message: "Do new users get a welcome private message?"
|
send_welcome_message: "Do new users get a welcome private message?"
|
||||||
|
|
||||||
|
allow_index_in_robots_txt: "Site should be indexed by search engines (update robots.txt)"
|
||||||
|
|
||||||
port: "If you'd like to specify a port in the URL. Useful in development mode. Leave blank for none."
|
port: "If you'd like to specify a port in the URL. Useful in development mode. Leave blank for none."
|
||||||
force_hostname: "If you'd like to specify a hostname in the URL. Useful in development mode. Leave blank for none."
|
force_hostname: "If you'd like to specify a hostname in the URL. Useful in development mode. Leave blank for none."
|
||||||
|
|
||||||
@ -435,6 +440,7 @@ en:
|
|||||||
show_more: "show more"
|
show_more: "show more"
|
||||||
links: Links
|
links: Links
|
||||||
faq: "FAQ"
|
faq: "FAQ"
|
||||||
|
you: "You"
|
||||||
|
|
||||||
suggested_topics:
|
suggested_topics:
|
||||||
title: "Suggested Topics"
|
title: "Suggested Topics"
|
||||||
|
@ -207,6 +207,9 @@ Discourse::Application.routes.draw do
|
|||||||
post 'draft' => 'draft#update'
|
post 'draft' => 'draft#update'
|
||||||
delete 'draft' => 'draft#destroy'
|
delete 'draft' => 'draft#destroy'
|
||||||
|
|
||||||
|
|
||||||
|
get 'robots.txt' => 'robots_txt#index'
|
||||||
|
|
||||||
# You can have the root of your site routed with "root"
|
# You can have the root of your site routed with "root"
|
||||||
# just remember to delete public/index.html.
|
# just remember to delete public/index.html.
|
||||||
root :to => 'list#index'
|
root :to => 'list#index'
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -172,7 +172,42 @@ module PrettyText
|
|||||||
cloned = opts.dup
|
cloned = opts.dup
|
||||||
# we have a minor inconsistency
|
# we have a minor inconsistency
|
||||||
cloned[:topicId] = opts[:topic_id]
|
cloned[:topicId] = opts[:topic_id]
|
||||||
Sanitize.clean(markdown(text.dup, cloned), PrettyText.whitelist)
|
sanitized = Sanitize.clean(markdown(text.dup, cloned), PrettyText.whitelist)
|
||||||
|
if SiteSetting.add_rel_nofollow_to_user_content
|
||||||
|
sanitized = add_rel_nofollow_to_user_content(sanitized)
|
||||||
|
end
|
||||||
|
sanitized
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.add_rel_nofollow_to_user_content(html)
|
||||||
|
whitelist = []
|
||||||
|
|
||||||
|
l = SiteSetting.exclude_rel_nofollow_domains
|
||||||
|
if l.present?
|
||||||
|
whitelist = l.split(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
site_uri = nil
|
||||||
|
doc = Nokogiri::HTML.fragment(html)
|
||||||
|
doc.css("a").each do |l|
|
||||||
|
href = l["href"].to_s
|
||||||
|
begin
|
||||||
|
uri = URI(href)
|
||||||
|
site_uri ||= URI(Discourse.base_url)
|
||||||
|
|
||||||
|
if !uri.host.present? ||
|
||||||
|
uri.host.ends_with?(site_uri.host) ||
|
||||||
|
whitelist.any?{|u| uri.host.ends_with?(u)}
|
||||||
|
# we are good no need for nofollow
|
||||||
|
else
|
||||||
|
l["rel"] = "nofollow"
|
||||||
|
end
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
# add a nofollow anyway
|
||||||
|
l["rel"] = "nofollow"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
doc.to_html
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.extract_links(html)
|
def self.extract_links(html)
|
||||||
|
@ -21,7 +21,7 @@ module Search
|
|||||||
NULL AS color
|
NULL AS color
|
||||||
FROM users AS u
|
FROM users AS u
|
||||||
JOIN users_search s on s.id = u.id
|
JOIN users_search s on s.id = u.id
|
||||||
WHERE s.search_data @@ TO_TSQUERY(:query)
|
WHERE s.search_data @@ TO_TSQUERY('english', :query)
|
||||||
ORDER BY last_posted_at desc
|
ORDER BY last_posted_at desc
|
||||||
"
|
"
|
||||||
end
|
end
|
||||||
@ -36,13 +36,13 @@ module Search
|
|||||||
FROM topics AS ft
|
FROM topics AS ft
|
||||||
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number = 1
|
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number = 1
|
||||||
JOIN posts_search s on s.id = p.id
|
JOIN posts_search s on s.id = p.id
|
||||||
WHERE s.search_data @@ TO_TSQUERY(:query)
|
WHERE s.search_data @@ TO_TSQUERY('english', :query)
|
||||||
AND ft.deleted_at IS NULL
|
AND ft.deleted_at IS NULL
|
||||||
AND ft.visible
|
AND ft.visible
|
||||||
AND ft.archetype <> '#{Archetype.private_message}'
|
AND ft.archetype <> '#{Archetype.private_message}'
|
||||||
ORDER BY
|
ORDER BY
|
||||||
TS_RANK_CD(TO_TSVECTOR('english', ft.title), TO_TSQUERY(:query)) desc,
|
TS_RANK_CD(TO_TSVECTOR('english', ft.title), TO_TSQUERY('english', :query)) desc,
|
||||||
TS_RANK_CD(search_data, TO_TSQUERY(:query)) desc,
|
TS_RANK_CD(search_data, TO_TSQUERY('english', :query)) desc,
|
||||||
bumped_at desc"
|
bumped_at desc"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -57,13 +57,13 @@ module Search
|
|||||||
FROM topics AS ft
|
FROM topics AS ft
|
||||||
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number <> 1
|
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number <> 1
|
||||||
JOIN posts_search s on s.id = p.id
|
JOIN posts_search s on s.id = p.id
|
||||||
WHERE s.search_data @@ TO_TSQUERY(:query)
|
WHERE s.search_data @@ TO_TSQUERY('english', :query)
|
||||||
AND ft.deleted_at IS NULL and p.deleted_at IS NULL
|
AND ft.deleted_at IS NULL and p.deleted_at IS NULL
|
||||||
AND ft.visible
|
AND ft.visible
|
||||||
AND ft.archetype <> '#{Archetype.private_message}'
|
AND ft.archetype <> '#{Archetype.private_message}'
|
||||||
ORDER BY
|
ORDER BY
|
||||||
TS_RANK_CD(TO_TSVECTOR('english', ft.title), TO_TSQUERY(:query)) desc,
|
TS_RANK_CD(TO_TSVECTOR('english', ft.title), TO_TSQUERY('english', :query)) desc,
|
||||||
TS_RANK_CD(search_data, TO_TSQUERY(:query)) desc,
|
TS_RANK_CD(search_data, TO_TSQUERY('english', :query)) desc,
|
||||||
bumped_at desc"
|
bumped_at desc"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ module Search
|
|||||||
c.color
|
c.color
|
||||||
FROM categories AS c
|
FROM categories AS c
|
||||||
JOIN categories_search s on s.id = c.id
|
JOIN categories_search s on s.id = c.id
|
||||||
WHERE s.search_data @@ TO_TSQUERY(:query)
|
WHERE s.search_data @@ TO_TSQUERY('english', :query)
|
||||||
ORDER BY topics_month desc
|
ORDER BY topics_month desc
|
||||||
"
|
"
|
||||||
end
|
end
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
class SqlBuilder
|
class SqlBuilder
|
||||||
|
|
||||||
def initialize(template)
|
def initialize(template,klass=nil)
|
||||||
@args = {}
|
@args = {}
|
||||||
@sql = template
|
@sql = template
|
||||||
@sections = {}
|
@sections = {}
|
||||||
|
@klass = klass
|
||||||
end
|
end
|
||||||
|
|
||||||
[:set, :where2,:where,:order_by,:limit,:left_join,:join,:offset].each do |k|
|
[:set, :where2,:where,:order_by,:limit,:left_join,:join,:offset].each do |k|
|
||||||
@ -41,8 +42,16 @@ class SqlBuilder
|
|||||||
sql.sub!("/*#{k}*/", joined)
|
sql.sub!("/*#{k}*/", joined)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if @klass
|
||||||
|
@klass.find_by_sql(ActiveRecord::Base.send(:sanitize_sql_array, [sql, @args]))
|
||||||
|
else
|
||||||
ActiveRecord::Base.exec_sql(sql,@args)
|
ActiveRecord::Base.exec_sql(sql,@args)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ActiveRecord::Base
|
||||||
|
def self.sql_builder(template)
|
||||||
|
SqlBuilder.new(template, self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -76,6 +76,33 @@ test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "rel nofollow" do
|
||||||
|
before do
|
||||||
|
SiteSetting.stubs(:add_rel_nofollow_to_user_content).returns(true)
|
||||||
|
SiteSetting.stubs(:exclude_rel_nofollow_domains).returns("foo.com,bar.com")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should inject nofollow in all user provided links" do
|
||||||
|
PrettyText.cook('<a href="http://cnn.com">cnn</a>').should =~ /nofollow/
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not inject nofollow in all local links" do
|
||||||
|
(PrettyText.cook("<a href='#{Discourse.base_url}/test.html'>cnn</a>") !~ /nofollow/).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not inject nofollow in all subdomain links" do
|
||||||
|
(PrettyText.cook("<a href='#{Discourse.base_url.sub('http://', 'http://bla.')}/test.html'>cnn</a>") !~ /nofollow/).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not inject nofollow for foo.com" do
|
||||||
|
(PrettyText.cook("<a href='http://foo.com/test.html'>cnn</a>") !~ /nofollow/).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not inject nofollow for bar.foo.com" do
|
||||||
|
(PrettyText.cook("<a href='http://bar.foo.com/test.html'>cnn</a>") !~ /nofollow/).should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "Excerpt" do
|
describe "Excerpt" do
|
||||||
it "should preserve links" do
|
it "should preserve links" do
|
||||||
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",100).should == "<a href='http://cnn.com'>cnn</a>"
|
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",100).should == "<a href='http://cnn.com'>cnn</a>"
|
||||||
@ -130,6 +157,7 @@ test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
describe "apply cdn" do
|
describe "apply cdn" do
|
||||||
it "should detect bare links to images and apply a CDN" do
|
it "should detect bare links to images and apply a CDN" do
|
||||||
PrettyText.apply_cdn("<a href='/hello.png'>hello</a><img src='/a.jpeg'>","http://a.com").should ==
|
PrettyText.apply_cdn("<a href='/hello.png'>hello</a><img src='/a.jpeg'>","http://a.com").should ==
|
||||||
|
@ -4,6 +4,21 @@ require_dependency 'sql_builder'
|
|||||||
|
|
||||||
describe SqlBuilder do
|
describe SqlBuilder do
|
||||||
|
|
||||||
|
describe "attached" do
|
||||||
|
before do
|
||||||
|
@builder = Post.sql_builder("select * from posts /*where*/ /*limit*/")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should find a post by id" do
|
||||||
|
p = Fabricate(:post)
|
||||||
|
@builder.where('id = :id and topic_id = :topic_id', id: p.id, topic_id: p.topic_id)
|
||||||
|
p2 = @builder.exec.first
|
||||||
|
p2.id.should == p.id
|
||||||
|
p2.should == p
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "detached" do
|
||||||
before do
|
before do
|
||||||
@builder = SqlBuilder.new("select * from (select :a A union all select :b) as X /*where*/ /*order_by*/ /*limit*/ /*offset*/")
|
@builder = SqlBuilder.new("select * from (select :a A union all select :b) as X /*where*/ /*order_by*/ /*limit*/ /*offset*/")
|
||||||
end
|
end
|
||||||
@ -31,5 +46,6 @@ describe SqlBuilder do
|
|||||||
@builder.order_by("A desc").offset(1)
|
@builder.order_by("A desc").offset(1)
|
||||||
.exec(a:1, b:2).values[0][0].should == "1"
|
.exec(a:1, b:2).values[0][0].should == "1"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
26
spec/controllers/robots_txt_controller_spec.rb
Normal file
26
spec/controllers/robots_txt_controller_spec.rb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe RobotsTxtController do
|
||||||
|
|
||||||
|
context '.index' do
|
||||||
|
it "returns noindex when indexing is disallowed" do
|
||||||
|
SiteSetting.stubs(:allow_index_in_robots_txt).returns(true)
|
||||||
|
get :index
|
||||||
|
response.should render_template :index
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns index when indexing is allowed" do
|
||||||
|
SiteSetting.stubs(:allow_index_in_robots_txt).returns(false)
|
||||||
|
get :index
|
||||||
|
response.should render_template :no_index
|
||||||
|
end
|
||||||
|
|
||||||
|
it "serves it regardless if a site is in private mode" do
|
||||||
|
SiteSetting.stubs(:allow_index_in_robots_txt).returns(true)
|
||||||
|
SiteSetting.stubs(:restrict_access).returns(true)
|
||||||
|
get :index
|
||||||
|
response.should render_template :no_index
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
@ -26,6 +26,7 @@ describe SiteCustomization do
|
|||||||
SiteCustomization.enabled_style_key.should be_nil
|
SiteCustomization.enabled_style_key.should be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
it 'finds the enabled style' do
|
it 'finds the enabled style' do
|
||||||
@customization.enabled = true
|
@customization.enabled = true
|
||||||
@customization.save
|
@customization.save
|
||||||
@ -45,6 +46,16 @@ describe SiteCustomization do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'ensure stylesheet is on disk on first fetch' do
|
||||||
|
c = customization
|
||||||
|
c.remove_from_cache!
|
||||||
|
File.delete(c.stylesheet_fullpath)
|
||||||
|
|
||||||
|
SiteCustomization.custom_stylesheet(c.key)
|
||||||
|
File.exists?(c.stylesheet_fullpath).should == true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
it 'should allow me to lookup a filename containing my preview stylesheet' do
|
it 'should allow me to lookup a filename containing my preview stylesheet' do
|
||||||
SiteCustomization.custom_stylesheet(customization.key).should ==
|
SiteCustomization.custom_stylesheet(customization.key).should ==
|
||||||
"<link class=\"custom-css\" rel=\"stylesheet\" href=\"/stylesheet-cache/#{customization.key}.css?#{customization.stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
|
"<link class=\"custom-css\" rel=\"stylesheet\" href=\"/stylesheet-cache/#{customization.key}.css?#{customization.stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user