mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 09:26:54 -06:00
Merge branch 'master' of github.com:discourse/discourse
This commit is contained in:
commit
0edf012dd9
@ -52,6 +52,11 @@ export default {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let href = ($link.attr("href") || $link.data("href") || "").trim();
|
||||||
|
if (!href || href.indexOf("mailto:") === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($link.hasClass("attachment")) {
|
if ($link.hasClass("attachment")) {
|
||||||
// Warn the user if they cannot download the file.
|
// Warn the user if they cannot download the file.
|
||||||
if (
|
if (
|
||||||
@ -59,15 +64,14 @@ export default {
|
|||||||
!Discourse.User.current()
|
!Discourse.User.current()
|
||||||
) {
|
) {
|
||||||
bootbox.alert(I18n.t("post.errors.attachment_download_requires_login"));
|
bootbox.alert(I18n.t("post.errors.attachment_download_requires_login"));
|
||||||
return false;
|
} else if (wantsNewWindow(e)) {
|
||||||
|
const newWindow = window.open(href, "_blank");
|
||||||
|
newWindow.opener = null;
|
||||||
|
newWindow.focus();
|
||||||
|
} else {
|
||||||
|
DiscourseURL.redirectTo(href);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let href = ($link.attr("href") || $link.data("href") || "").trim();
|
|
||||||
if (!href || href.indexOf("mailto:") === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const $article = $link.closest(
|
const $article = $link.closest(
|
||||||
|
@ -5,6 +5,7 @@ import { defaultHomepage } from "discourse/lib/utilities";
|
|||||||
import TopicList from "discourse/models/topic-list";
|
import TopicList from "discourse/models/topic-list";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import PreloadStore from "preload-store";
|
import PreloadStore from "preload-store";
|
||||||
|
import { searchPriorities } from "discourse/components/concerns/category_search_priorities";
|
||||||
|
|
||||||
const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
||||||
renderTemplate() {
|
renderTemplate() {
|
||||||
@ -109,7 +110,8 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
|||||||
available_groups: groups.map(g => g.name),
|
available_groups: groups.map(g => g.name),
|
||||||
allow_badges: true,
|
allow_badges: true,
|
||||||
topic_featured_link_allowed: true,
|
topic_featured_link_allowed: true,
|
||||||
custom_fields: {}
|
custom_fields: {},
|
||||||
|
search_priority: searchPriorities.normal
|
||||||
});
|
});
|
||||||
|
|
||||||
showModal("edit-category", { model });
|
showModal("edit-category", { model });
|
||||||
|
23
app/jobs/regular/notify_post_revision.rb
Normal file
23
app/jobs/regular/notify_post_revision.rb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
module Jobs
|
||||||
|
class NotifyPostRevision < Jobs::Base
|
||||||
|
def execute(args)
|
||||||
|
raise Discourse::InvalidParameters.new(:user_ids) unless args[:user_ids]
|
||||||
|
|
||||||
|
post_revision = PostRevision.find_by(id: args[:post_revision_id])
|
||||||
|
raise Discourse::InvalidParameters.new(:post_revision_id) unless post_revision
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
User.where(id: args[:user_ids]).find_each do |user|
|
||||||
|
PostActionNotifier.alerter.create_notification(
|
||||||
|
user,
|
||||||
|
Notification.types[:edited],
|
||||||
|
post_revision.post,
|
||||||
|
display_username: post_revision.user.username,
|
||||||
|
acting_user_id: post_revision&.user_id,
|
||||||
|
revision_number: post_revision.number
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -7,11 +7,19 @@ class TopicUser < ActiveRecord::Base
|
|||||||
# used for serialization
|
# used for serialization
|
||||||
attr_accessor :post_action_data
|
attr_accessor :post_action_data
|
||||||
|
|
||||||
scope :tracking, lambda { |topic_id|
|
scope :level, lambda { |topic_id, level|
|
||||||
where(topic_id: topic_id)
|
where(topic_id: topic_id)
|
||||||
.where("COALESCE(topic_users.notification_level, :regular) >= :tracking",
|
.where("COALESCE(topic_users.notification_level, :regular) >= :level",
|
||||||
regular: TopicUser.notification_levels[:regular],
|
regular: TopicUser.notification_levels[:regular],
|
||||||
tracking: TopicUser.notification_levels[:tracking])
|
level: TopicUser.notification_levels[level])
|
||||||
|
}
|
||||||
|
|
||||||
|
scope :tracking, lambda { |topic_id|
|
||||||
|
level(topic_id, :tracking)
|
||||||
|
}
|
||||||
|
|
||||||
|
scope :watching, lambda { |topic_id|
|
||||||
|
level(topic_id, :watching)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Class methods
|
# Class methods
|
||||||
|
@ -93,19 +93,31 @@ class PostActionNotifier
|
|||||||
|
|
||||||
return unless post
|
return unless post
|
||||||
return if post_revision.user.blank?
|
return if post_revision.user.blank?
|
||||||
return if post_revision.user_id == post.user_id
|
|
||||||
return if post.topic.blank?
|
return if post.topic.blank?
|
||||||
return if post.topic.private_message?
|
return if post.topic.private_message?
|
||||||
return if SiteSetting.disable_edit_notifications && post_revision.user_id == Discourse::SYSTEM_USER_ID
|
return if SiteSetting.disable_edit_notifications && post_revision.user_id == Discourse::SYSTEM_USER_ID
|
||||||
|
|
||||||
alerter.create_notification(
|
user_ids = []
|
||||||
post.user,
|
|
||||||
Notification.types[:edited],
|
if post_revision.user_id != post.user_id
|
||||||
post,
|
user_ids << post.user_id
|
||||||
display_username: post_revision.user.username,
|
end
|
||||||
acting_user_id: post_revision.try(:user_id),
|
|
||||||
revision_number: post_revision.number
|
if post.wiki && post.is_first_post?
|
||||||
)
|
user_ids.concat(
|
||||||
|
TopicUser.watching(post.topic_id)
|
||||||
|
.where.not(user_id: post_revision.user_id)
|
||||||
|
.where(topic: post.topic)
|
||||||
|
.pluck(:user_id)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
if user_ids.present?
|
||||||
|
Jobs.enqueue(:notify_post_revision,
|
||||||
|
user_ids: user_ids,
|
||||||
|
post_revision_id: post_revision.id
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.after_post_unhide(post, flaggers)
|
def self.after_post_unhide(post, flaggers)
|
||||||
|
@ -12,8 +12,7 @@ Discourse::Application.configure do
|
|||||||
# Disable Rails's static asset server (Apache or nginx will already do this)
|
# Disable Rails's static asset server (Apache or nginx will already do this)
|
||||||
config.public_file_server.enabled = GlobalSetting.serve_static_assets || false
|
config.public_file_server.enabled = GlobalSetting.serve_static_assets || false
|
||||||
|
|
||||||
require 'uglifier'
|
config.assets.js_compressor = :uglifier
|
||||||
config.assets.js_compressor = Uglifier.new(harmony: true)
|
|
||||||
|
|
||||||
# stuff should be pre-compiled
|
# stuff should be pre-compiled
|
||||||
config.assets.compile = false
|
config.assets.compile = false
|
||||||
|
@ -16,7 +16,7 @@ task 'assets:precompile:before' do
|
|||||||
# is recompiled
|
# is recompiled
|
||||||
Emoji.clear_cache
|
Emoji.clear_cache
|
||||||
|
|
||||||
if !`which uglifyjs`.empty? && !ENV['SKIP_NODE_UGLIFY']
|
if Rails.configuration.assets.js_compressor == :uglifier && !`which uglifyjs`.empty? && !ENV['SKIP_NODE_UGLIFY']
|
||||||
$node_uglify = true
|
$node_uglify = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ def compress_node(from, to)
|
|||||||
source_map_root = assets + ((d = File.dirname(from)) == "." ? "" : "/#{d}")
|
source_map_root = assets + ((d = File.dirname(from)) == "." ? "" : "/#{d}")
|
||||||
source_map_url = cdn_path "/assets/#{to}.map"
|
source_map_url = cdn_path "/assets/#{to}.map"
|
||||||
|
|
||||||
cmd = "uglifyjs '#{assets_path}/#{from}' -p relative -m -c -o '#{to_path}' --source-map-root '#{source_map_root}' --source-map '#{assets_path}/#{to}.map' --source-map-url '#{source_map_url}'"
|
cmd = "uglifyjs '#{assets_path}/#{from}' -p relative -c -m -o '#{to_path}' --source-map-root '#{source_map_root}' --source-map '#{assets_path}/#{to}.map' --source-map-url '#{source_map_url}'"
|
||||||
|
|
||||||
STDERR.puts cmd
|
STDERR.puts cmd
|
||||||
result = `#{cmd} 2>&1`
|
result = `#{cmd} 2>&1`
|
||||||
@ -128,16 +128,6 @@ def brotli(path)
|
|||||||
raise "chmod failed: exit code #{$?.exitstatus}" if $?.exitstatus != 0
|
raise "chmod failed: exit code #{$?.exitstatus}" if $?.exitstatus != 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_compress?(path, locales)
|
|
||||||
return false if Rails.configuration.assets.skip_minification.include? path
|
|
||||||
return true unless path.include? "locales/"
|
|
||||||
|
|
||||||
path_locale = path.delete_prefix("locales/").delete_suffix(".js")
|
|
||||||
return true if locales.include? path_locale
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def compress(from, to)
|
def compress(from, to)
|
||||||
if $node_uglify
|
if $node_uglify
|
||||||
compress_node(from, to)
|
compress_node(from, to)
|
||||||
@ -173,15 +163,10 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
|||||||
|
|
||||||
if $bypass_sprockets_uglify
|
if $bypass_sprockets_uglify
|
||||||
puts "Compressing Javascript and Generating Source Maps"
|
puts "Compressing Javascript and Generating Source Maps"
|
||||||
startAll = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
||||||
manifest = Sprockets::Manifest.new(assets_path)
|
manifest = Sprockets::Manifest.new(assets_path)
|
||||||
locales = Set.new(["en"])
|
|
||||||
|
|
||||||
RailsMultisite::ConnectionManagement.each_connection do |db|
|
|
||||||
locales.add(SiteSetting.default_locale)
|
|
||||||
end
|
|
||||||
|
|
||||||
concurrent? do |proc|
|
concurrent? do |proc|
|
||||||
|
to_skip = Rails.configuration.assets.skip_minification || []
|
||||||
manifest.files
|
manifest.files
|
||||||
.select { |k, v| k =~ /\.js$/ }
|
.select { |k, v| k =~ /\.js$/ }
|
||||||
.each do |file, info|
|
.each do |file, info|
|
||||||
@ -197,7 +182,8 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
|||||||
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||||
STDERR.puts "#{start} Compressing: #{file}"
|
STDERR.puts "#{start} Compressing: #{file}"
|
||||||
|
|
||||||
if should_compress?(info["logical_path"], locales)
|
# We can specify some files to never minify
|
||||||
|
unless (ENV["DONT_MINIFY"] == "1") || to_skip.include?(info['logical_path'])
|
||||||
FileUtils.mv(path, _path)
|
FileUtils.mv(path, _path)
|
||||||
compress(_file, file)
|
compress(_file, file)
|
||||||
end
|
end
|
||||||
@ -205,7 +191,7 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
|||||||
info["size"] = File.size(path)
|
info["size"] = File.size(path)
|
||||||
info["mtime"] = File.mtime(path).iso8601
|
info["mtime"] = File.mtime(path).iso8601
|
||||||
gzip(path)
|
gzip(path)
|
||||||
brotli(path) if should_compress?(info["logical_path"], locales)
|
brotli(path)
|
||||||
|
|
||||||
STDERR.puts "Done compressing #{file} : #{(Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).round(2)} secs"
|
STDERR.puts "Done compressing #{file} : #{(Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).round(2)} secs"
|
||||||
STDERR.puts
|
STDERR.puts
|
||||||
@ -214,9 +200,6 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
STDERR.puts "Done compressing all JS files : #{(Process.clock_gettime(Process::CLOCK_MONOTONIC) - startAll).round(2)} secs"
|
|
||||||
STDERR.puts
|
|
||||||
|
|
||||||
# protected
|
# protected
|
||||||
manifest.send :save
|
manifest.send :save
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ module DiscourseNarrativeBot
|
|||||||
valid = false
|
valid = false
|
||||||
|
|
||||||
doc.css(".mention").each do |mention|
|
doc.css(".mention").each do |mention|
|
||||||
if mention.text.downcase == "@#{self.discobot_user.username}".downcase
|
if User.normalize_username(mention.text) == "@#{self.discobot_user.username_lower}"
|
||||||
valid = true
|
valid = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -53,6 +53,7 @@ describe UserAction do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'includes the events correctly' do
|
it 'includes the events correctly' do
|
||||||
|
Jobs.run_immediately!
|
||||||
PostActionNotifier.enable
|
PostActionNotifier.enable
|
||||||
|
|
||||||
mystats = stats_for_user(user)
|
mystats = stats_for_user(user)
|
||||||
|
@ -6,6 +6,7 @@ describe PostActionNotifier do
|
|||||||
|
|
||||||
before do
|
before do
|
||||||
PostActionNotifier.enable
|
PostActionNotifier.enable
|
||||||
|
Jobs.run_immediately!
|
||||||
end
|
end
|
||||||
|
|
||||||
fab!(:evil_trout) { Fabricate(:evil_trout) }
|
fab!(:evil_trout) { Fabricate(:evil_trout) }
|
||||||
@ -15,7 +16,62 @@ describe PostActionNotifier do
|
|||||||
it 'notifies a user of the revision' do
|
it 'notifies a user of the revision' do
|
||||||
expect {
|
expect {
|
||||||
post.revise(evil_trout, raw: "world is the new body of the message")
|
post.revise(evil_trout, raw: "world is the new body of the message")
|
||||||
}.to change(post.user.notifications, :count).by(1)
|
}.to change { post.reload.user.notifications.count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'notifies watching users of revision when post is wiki-ed and first post in topic' do
|
||||||
|
SiteSetting.editing_grace_period_max_diff = 1
|
||||||
|
|
||||||
|
post.update!(wiki: true)
|
||||||
|
user = post.user
|
||||||
|
user2 = Fabricate(:user)
|
||||||
|
user3 = Fabricate(:user)
|
||||||
|
|
||||||
|
TopicUser.change(user2.id, post.topic,
|
||||||
|
notification_level: TopicUser.notification_levels[:watching]
|
||||||
|
)
|
||||||
|
|
||||||
|
TopicUser.change(user3.id, post.topic,
|
||||||
|
notification_level: TopicUser.notification_levels[:tracking]
|
||||||
|
)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post.revise(Fabricate(:user), raw: "I made some changes to the wiki!")
|
||||||
|
end.to change { Notification.count }.by(2)
|
||||||
|
|
||||||
|
edited_notification_type = Notification.types[:edited]
|
||||||
|
|
||||||
|
expect(Notification.exists?(
|
||||||
|
user: user,
|
||||||
|
notification_type: edited_notification_type
|
||||||
|
)).to eq(true)
|
||||||
|
|
||||||
|
expect(Notification.exists?(
|
||||||
|
user: user2,
|
||||||
|
notification_type: edited_notification_type
|
||||||
|
)).to eq(true)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post.revise(user, raw: "I made some changes to the wiki again!")
|
||||||
|
end.to change {
|
||||||
|
Notification.where(notification_type: edited_notification_type).count
|
||||||
|
}.by(1)
|
||||||
|
|
||||||
|
expect(Notification.where(
|
||||||
|
user: user2,
|
||||||
|
notification_type: edited_notification_type
|
||||||
|
).count).to eq(2)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post.revise(user2, raw: "I changed the wiki totally")
|
||||||
|
end.to change {
|
||||||
|
Notification.where(notification_type: edited_notification_type).count
|
||||||
|
}.by(1)
|
||||||
|
|
||||||
|
expect(Notification.where(
|
||||||
|
user: user,
|
||||||
|
notification_type: edited_notification_type
|
||||||
|
).count).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'stores the revision number with the notification' do
|
it 'stores the revision number with the notification' do
|
||||||
|
@ -102,6 +102,7 @@ describe PostAlerter do
|
|||||||
|
|
||||||
context 'edits' do
|
context 'edits' do
|
||||||
it 'notifies correctly on edits' do
|
it 'notifies correctly on edits' do
|
||||||
|
Jobs.run_immediately!
|
||||||
PostActionNotifier.enable
|
PostActionNotifier.enable
|
||||||
|
|
||||||
post = Fabricate(:post, raw: 'I love waffles')
|
post = Fabricate(:post, raw: 'I love waffles')
|
||||||
|
@ -70,13 +70,17 @@ QUnit.test("does not track elements with no href", async assert => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("does not track attachments", async assert => {
|
QUnit.test("does not track attachments", async assert => {
|
||||||
assert.expect(1);
|
|
||||||
sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||||
|
|
||||||
/* global server */
|
/* global server */
|
||||||
server.post("/clicks/track", () => assert.ok(false));
|
server.post("/clicks/track", () => assert.ok(false));
|
||||||
|
|
||||||
assert.ok(track(generateClickEventOn(".attachment")));
|
assert.notOk(track(generateClickEventOn(".attachment")));
|
||||||
|
assert.ok(
|
||||||
|
DiscourseURL.redirectTo.calledWith(
|
||||||
|
"http://discuss.domain.com/uploads/default/1234/1532357280.txt"
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("tracks external URLs", async assert => {
|
QUnit.test("tracks external URLs", async assert => {
|
||||||
|
Loading…
Reference in New Issue
Block a user