FIX: Add missing user_id args for ChatMessage.cook (#19508)
In both ChatMessage#rebake! and in ChatMessageProcessor when we were calling ChatMessage.cook we were missing the user_id to cook with, which causes missed hashtag cooks because of missing permissions.
This commit is contained in:
parent
09d15d4c7f
commit
baf78d3d91
|
@ -111,8 +111,13 @@ module PrettyText
|
||||||
end
|
end
|
||||||
|
|
||||||
def hashtag_lookup(slug, cooking_user_id, types_in_priority_order)
|
def hashtag_lookup(slug, cooking_user_id, types_in_priority_order)
|
||||||
# This is _somewhat_ expected since we need to be able to cook posts
|
# NOTE: This is _somewhat_ expected since we need to be able to cook posts
|
||||||
# etc. without a user sometimes, but it is still an edge case.
|
# etc. without a user sometimes, but it is still an edge case.
|
||||||
|
#
|
||||||
|
# The Discourse.system_user is usually an admin with access to _all_
|
||||||
|
# categories, however if the suppress_secured_categories_from_admin
|
||||||
|
# site setting is activated then this user will not be able to access
|
||||||
|
# secure categories, so hashtags that are secure will not render.
|
||||||
if cooking_user_id.blank?
|
if cooking_user_id.blank?
|
||||||
cooking_user = Discourse.system_user
|
cooking_user = Discourse.system_user
|
||||||
else
|
else
|
||||||
|
|
|
@ -110,19 +110,20 @@ class ChatMessage < ActiveRecord::Base
|
||||||
def cook
|
def cook
|
||||||
ensure_last_editor_id
|
ensure_last_editor_id
|
||||||
|
|
||||||
# A rule in our Markdown pipeline may have Guardian checks that require a
|
|
||||||
# user to be present. The last editing user of the message will be more
|
|
||||||
# generally up to date than the creating user. For example, we use
|
|
||||||
# this when cooking #hashtags to determine whether we should render
|
|
||||||
# the found hashtag based on whether the user can access the channel it
|
|
||||||
# is referencing.
|
|
||||||
self.cooked = self.class.cook(self.message, user_id: self.last_editor_id)
|
self.cooked = self.class.cook(self.message, user_id: self.last_editor_id)
|
||||||
self.cooked_version = BAKED_VERSION
|
self.cooked_version = BAKED_VERSION
|
||||||
end
|
end
|
||||||
|
|
||||||
def rebake!(invalidate_oneboxes: false, priority: nil)
|
def rebake!(invalidate_oneboxes: false, priority: nil)
|
||||||
|
ensure_last_editor_id
|
||||||
|
|
||||||
previous_cooked = self.cooked
|
previous_cooked = self.cooked
|
||||||
new_cooked = self.class.cook(message, invalidate_oneboxes: invalidate_oneboxes)
|
new_cooked =
|
||||||
|
self.class.cook(
|
||||||
|
message,
|
||||||
|
invalidate_oneboxes: invalidate_oneboxes,
|
||||||
|
user_id: self.last_editor_id,
|
||||||
|
)
|
||||||
update_columns(cooked: new_cooked, cooked_version: BAKED_VERSION)
|
update_columns(cooked: new_cooked, cooked_version: BAKED_VERSION)
|
||||||
args = { chat_message_id: self.id }
|
args = { chat_message_id: self.id }
|
||||||
args[:queue] = priority.to_s if priority && priority != :normal
|
args[:queue] = priority.to_s if priority && priority != :normal
|
||||||
|
@ -177,6 +178,12 @@ class ChatMessage < ActiveRecord::Base
|
||||||
]
|
]
|
||||||
|
|
||||||
def self.cook(message, opts = {})
|
def self.cook(message, opts = {})
|
||||||
|
# A rule in our Markdown pipeline may have Guardian checks that require a
|
||||||
|
# user to be present. The last editing user of the message will be more
|
||||||
|
# generally up to date than the creating user. For example, we use
|
||||||
|
# this when cooking #hashtags to determine whether we should render
|
||||||
|
# the found hashtag based on whether the user can access the channel it
|
||||||
|
# is referencing.
|
||||||
cooked =
|
cooked =
|
||||||
PrettyText.cook(
|
PrettyText.cook(
|
||||||
message,
|
message,
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Chat::ChatMessageProcessor
|
||||||
@size_cache = {}
|
@size_cache = {}
|
||||||
@opts = {}
|
@opts = {}
|
||||||
|
|
||||||
cooked = ChatMessage.cook(chat_message.message)
|
cooked = ChatMessage.cook(chat_message.message, user_id: chat_message.last_editor_id)
|
||||||
@doc = Loofah.fragment(cooked)
|
@doc = Loofah.fragment(cooked)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe Chat::ChatMessageProcessor do
|
||||||
|
fab!(:message) { Fabricate(:chat_message) }
|
||||||
|
|
||||||
|
it "cooks using the last_editor_id of the message" do
|
||||||
|
ChatMessage.expects(:cook).with(message.message, user_id: message.last_editor_id)
|
||||||
|
described_class.new(message)
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,18 @@ require "rails_helper"
|
||||||
describe ChatMessage do
|
describe ChatMessage do
|
||||||
fab!(:message) { Fabricate(:chat_message, message: "hey friend, what's up?!") }
|
fab!(:message) { Fabricate(:chat_message, message: "hey friend, what's up?!") }
|
||||||
|
|
||||||
|
# TODO (martin) Remove this after https://github.com/discourse/discourse/pull/19491 is merged
|
||||||
|
def register_hashtag_contexts
|
||||||
|
# This is annoying, but we need to reset the hashtag data sources inbetween
|
||||||
|
# tests, and since this is normally done in plugin.rb with the plugin API
|
||||||
|
# there is not an easier way to do this.
|
||||||
|
HashtagAutocompleteService.register_data_source("channel", Chat::ChatChannelHashtagDataSource)
|
||||||
|
HashtagAutocompleteService.register_type_in_context("channel", "chat-composer", 200)
|
||||||
|
HashtagAutocompleteService.register_type_in_context("category", "chat-composer", 100)
|
||||||
|
HashtagAutocompleteService.register_type_in_context("tag", "chat-composer", 50)
|
||||||
|
HashtagAutocompleteService.register_type_in_context("channel", "topic-composer", 10)
|
||||||
|
end
|
||||||
|
|
||||||
describe ".cook" do
|
describe ".cook" do
|
||||||
it "does not support HTML tags" do
|
it "does not support HTML tags" do
|
||||||
cooked = ChatMessage.cook("<h1>test</h1>")
|
cooked = ChatMessage.cook("<h1>test</h1>")
|
||||||
|
@ -234,6 +246,7 @@ describe ChatMessage do
|
||||||
expect(cooked).to eq("<p><span class=\"mention\">@mention</span></p>")
|
expect(cooked).to eq("<p><span class=\"mention\">@mention</span></p>")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO (martin) Remove this when enable_experimental_hashtag_autocomplete is default
|
||||||
it "supports category-hashtag plugin" do
|
it "supports category-hashtag plugin" do
|
||||||
category = Fabricate(:category)
|
category = Fabricate(:category)
|
||||||
|
|
||||||
|
@ -244,6 +257,20 @@ describe ChatMessage do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "supports hashtag-autocomplete plugin" do
|
||||||
|
register_hashtag_contexts
|
||||||
|
SiteSetting.enable_experimental_hashtag_autocomplete = true
|
||||||
|
|
||||||
|
category = Fabricate(:category)
|
||||||
|
user = Fabricate(:user)
|
||||||
|
|
||||||
|
cooked = ChatMessage.cook("##{category.slug}", user_id: user.id)
|
||||||
|
|
||||||
|
expect(cooked).to eq(
|
||||||
|
"<p><a class=\"hashtag-cooked\" href=\"#{category.url}\" data-type=\"category\" data-slug=\"#{category.slug}\"><svg class=\"fa d-icon d-icon-folder svg-icon svg-node\"><use href=\"#folder\"></use></svg><span>#{category.name}</span></a></p>",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "supports censored plugin" do
|
it "supports censored plugin" do
|
||||||
watched_word = Fabricate(:watched_word, action: WatchedWord.actions[:censor])
|
watched_word = Fabricate(:watched_word, action: WatchedWord.actions[:censor])
|
||||||
|
|
||||||
|
@ -484,4 +511,34 @@ describe ChatMessage do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#rebake!" do
|
||||||
|
fab!(:chat_message) { Fabricate(:chat_message) }
|
||||||
|
|
||||||
|
describe "hashtags" do
|
||||||
|
fab!(:category) { Fabricate(:category) }
|
||||||
|
fab!(:group) { Fabricate(:group) }
|
||||||
|
fab!(:secure_category) { Fabricate(:private_category, group: group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
register_hashtag_contexts
|
||||||
|
SiteSetting.enable_experimental_hashtag_autocomplete = true
|
||||||
|
SiteSetting.suppress_secured_categories_from_admin = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "keeps the same hashtags the user has permission to after rebake" do
|
||||||
|
group.add(chat_message.user)
|
||||||
|
chat_message.update!(
|
||||||
|
message: "this is the message ##{category.slug} ##{secure_category.slug} ##{chat_message.chat_channel.slug}",
|
||||||
|
)
|
||||||
|
chat_message.cook
|
||||||
|
chat_message.save!
|
||||||
|
|
||||||
|
expect(chat_message.reload.cooked).to include(secure_category.name)
|
||||||
|
|
||||||
|
chat_message.rebake!
|
||||||
|
expect(chat_message.reload.cooked).to include(secure_category.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue