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;
|
||||
}
|
||||
|
||||
let href = ($link.attr("href") || $link.data("href") || "").trim();
|
||||
if (!href || href.indexOf("mailto:") === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($link.hasClass("attachment")) {
|
||||
// Warn the user if they cannot download the file.
|
||||
if (
|
||||
@ -59,17 +64,16 @@ export default {
|
||||
!Discourse.User.current()
|
||||
) {
|
||||
bootbox.alert(I18n.t("post.errors.attachment_download_requires_login"));
|
||||
} 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(
|
||||
"article:not(.onebox-body), .excerpt, #revisions"
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import { defaultHomepage } from "discourse/lib/utilities";
|
||||
import TopicList from "discourse/models/topic-list";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import PreloadStore from "preload-store";
|
||||
import { searchPriorities } from "discourse/components/concerns/category_search_priorities";
|
||||
|
||||
const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
||||
renderTemplate() {
|
||||
@ -109,7 +110,8 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
||||
available_groups: groups.map(g => g.name),
|
||||
allow_badges: true,
|
||||
topic_featured_link_allowed: true,
|
||||
custom_fields: {}
|
||||
custom_fields: {},
|
||||
search_priority: searchPriorities.normal
|
||||
});
|
||||
|
||||
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
|
||||
attr_accessor :post_action_data
|
||||
|
||||
scope :tracking, lambda { |topic_id|
|
||||
scope :level, lambda { |topic_id, level|
|
||||
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],
|
||||
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
|
||||
|
@ -93,21 +93,33 @@ class PostActionNotifier
|
||||
|
||||
return unless post
|
||||
return if post_revision.user.blank?
|
||||
return if post_revision.user_id == post.user_id
|
||||
return if post.topic.blank?
|
||||
return if post.topic.private_message?
|
||||
return if SiteSetting.disable_edit_notifications && post_revision.user_id == Discourse::SYSTEM_USER_ID
|
||||
|
||||
alerter.create_notification(
|
||||
post.user,
|
||||
Notification.types[:edited],
|
||||
post,
|
||||
display_username: post_revision.user.username,
|
||||
acting_user_id: post_revision.try(:user_id),
|
||||
revision_number: post_revision.number
|
||||
user_ids = []
|
||||
|
||||
if post_revision.user_id != post.user_id
|
||||
user_ids << post.user_id
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def self.after_post_unhide(post, flaggers)
|
||||
return if @disabled || post.last_editor.blank? || flaggers.blank?
|
||||
|
||||
|
@ -12,8 +12,7 @@ Discourse::Application.configure do
|
||||
# Disable Rails's static asset server (Apache or nginx will already do this)
|
||||
config.public_file_server.enabled = GlobalSetting.serve_static_assets || false
|
||||
|
||||
require 'uglifier'
|
||||
config.assets.js_compressor = Uglifier.new(harmony: true)
|
||||
config.assets.js_compressor = :uglifier
|
||||
|
||||
# stuff should be pre-compiled
|
||||
config.assets.compile = false
|
||||
|
@ -16,7 +16,7 @@ task 'assets:precompile:before' do
|
||||
# is recompiled
|
||||
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
|
||||
end
|
||||
|
||||
@ -79,7 +79,7 @@ def compress_node(from, to)
|
||||
source_map_root = assets + ((d = File.dirname(from)) == "." ? "" : "/#{d}")
|
||||
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
|
||||
result = `#{cmd} 2>&1`
|
||||
@ -128,16 +128,6 @@ def brotli(path)
|
||||
raise "chmod failed: exit code #{$?.exitstatus}" if $?.exitstatus != 0
|
||||
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)
|
||||
if $node_uglify
|
||||
compress_node(from, to)
|
||||
@ -173,15 +163,10 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
||||
|
||||
if $bypass_sprockets_uglify
|
||||
puts "Compressing Javascript and Generating Source Maps"
|
||||
startAll = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||
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|
|
||||
to_skip = Rails.configuration.assets.skip_minification || []
|
||||
manifest.files
|
||||
.select { |k, v| k =~ /\.js$/ }
|
||||
.each do |file, info|
|
||||
@ -197,7 +182,8 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
||||
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||
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)
|
||||
compress(_file, file)
|
||||
end
|
||||
@ -205,7 +191,7 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
||||
info["size"] = File.size(path)
|
||||
info["mtime"] = File.mtime(path).iso8601
|
||||
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
|
||||
@ -214,9 +200,6 @@ task 'assets:precompile' => 'assets:precompile:before' do
|
||||
end
|
||||
end
|
||||
|
||||
STDERR.puts "Done compressing all JS files : #{(Process.clock_gettime(Process::CLOCK_MONOTONIC) - startAll).round(2)} secs"
|
||||
STDERR.puts
|
||||
|
||||
# protected
|
||||
manifest.send :save
|
||||
|
||||
|
@ -69,7 +69,7 @@ module DiscourseNarrativeBot
|
||||
valid = false
|
||||
|
||||
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
|
||||
break
|
||||
end
|
||||
|
@ -53,6 +53,7 @@ describe UserAction do
|
||||
end
|
||||
|
||||
it 'includes the events correctly' do
|
||||
Jobs.run_immediately!
|
||||
PostActionNotifier.enable
|
||||
|
||||
mystats = stats_for_user(user)
|
||||
|
@ -6,6 +6,7 @@ describe PostActionNotifier do
|
||||
|
||||
before do
|
||||
PostActionNotifier.enable
|
||||
Jobs.run_immediately!
|
||||
end
|
||||
|
||||
fab!(:evil_trout) { Fabricate(:evil_trout) }
|
||||
@ -15,7 +16,62 @@ describe PostActionNotifier do
|
||||
it 'notifies a user of the revision' do
|
||||
expect {
|
||||
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
|
||||
|
||||
it 'stores the revision number with the notification' do
|
||||
|
@ -102,6 +102,7 @@ describe PostAlerter do
|
||||
|
||||
context 'edits' do
|
||||
it 'notifies correctly on edits' do
|
||||
Jobs.run_immediately!
|
||||
PostActionNotifier.enable
|
||||
|
||||
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 => {
|
||||
assert.expect(1);
|
||||
sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
/* global server */
|
||||
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 => {
|
||||
|
Loading…
Reference in New Issue
Block a user