discourse/lib/pretty_text/helpers.rb
Martin Brennan b2acc416e7
FIX: Server-side hashtag lookups of secure categories for a user (#19377)
* FIX: Use Category.secured(guardian) for hashtag datasource

Follow up to comments in #19219, changing the category
hashtag datasource to use Category.secured(guardian) instead
of Site.new(guardian).categories here since the latter does
more work for not much benefit, and the query time is the
same. Also eliminates some Hash -> Model back and forth
busywork. Add some more specs too.

* FIX: Server-side hashtag lookup cooking user loading

When we were using the PrettyText.options.currentUser
and parsing back and forth with JSON for the hashtag
lookups server-side, we had a bug where the user's
secure categories were not loaded since we never actually
loaded a User model from the database, only parsed it
from JSON.

This commit fixes the issue by instead using the
PretyText.options.userId and looking up the user directly
from the database when calling hashtag_lookup via the
PrettyText::Helpers code when cooking server-side. Added
the missing spec to check for this as well.
2022-12-09 10:34:25 +10:00

142 lines
4.0 KiB
Ruby

# frozen_string_literal: true
module PrettyText
module Helpers
extend self
TAG_HASHTAG_POSTFIX = "::tag"
# functions here are available to v8
def t(key, opts)
key = "js." + key
unless opts
I18n.t(key)
else
str = I18n.t(key, Hash[opts.entries].symbolize_keys).dup
opts.each { |k, v| str.gsub!("{{#{k.to_s}}}", v.to_s) }
str
end
end
def avatar_template(username)
return "" unless username
user = User.find_by(username_lower: username.downcase)
return "" unless user.present?
# TODO: Add support for ES6 and call `avatar-template` directly
UrlHelper.schemaless(UrlHelper.absolute(user.avatar_template))
end
def lookup_primary_user_group(username)
return "" unless username
user = User.find_by(username_lower: username.downcase)
return "" unless user.present?
user.primary_group.try(:name) || ""
end
# Overwrite this in a plugin to change how markdown can format
# usernames on the server side
def format_username(username)
username
end
def lookup_upload_urls(urls)
map = {}
result = {}
urls.each do |url|
sha1 = Upload.sha1_from_short_url(url)
map[url] = sha1 if sha1
end
if map.length > 0
reverse_map = {}
map.each do |key, value|
reverse_map[value] ||= []
reverse_map[value] << key
end
Upload.where(sha1: map.values).pluck(:sha1, :url, :extension, :original_filename, :secure).each do |row|
sha1, url, extension, original_filename, secure = row
if short_urls = reverse_map[sha1]
secure_uploads = SiteSetting.secure_uploads? && secure
short_urls.each do |short_url|
result[short_url] = {
url: secure_uploads ? Upload.secure_uploads_url_from_upload_url(url) : Discourse.store.cdn_url(url),
short_path: Upload.short_path(sha1: sha1, extension: extension),
base62_sha1: Upload.base62_sha1(sha1)
}
end
end
end
end
result
end
def get_topic_info(topic_id)
return unless topic_id.is_a?(Integer)
# TODO this only handles public topics, secured one do not get this
topic = Topic.find_by(id: topic_id)
if topic && Guardian.new.can_see?(topic)
{
title: Rack::Utils.escape_html(topic.title),
href: topic.url
}
elsif topic
{
title: I18n.t("on_another_topic"),
href: Discourse.base_url + topic.slugless_url
}
end
end
# TODO (martin) Remove this when everything is using hashtag_lookup
# after enable_experimental_hashtag_autocomplete is default.
def category_tag_hashtag_lookup(text)
is_tag = text =~ /#{TAG_HASHTAG_POSTFIX}$/
if !is_tag && category = Category.query_from_hashtag_slug(text)
[category.url, text]
elsif (!is_tag && tag = Tag.find_by(name: text)) ||
(is_tag && tag = Tag.find_by(name: text.gsub!(TAG_HASHTAG_POSTFIX, '')))
[tag.url, text]
else
nil
end
end
def hashtag_lookup(slug, cooking_user_id, types_in_priority_order)
# 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.
if cooking_user_id.blank?
cooking_user = Discourse.system_user
else
cooking_user = User.find(cooking_user_id)
end
result = HashtagAutocompleteService.new(
Guardian.new(cooking_user)
).lookup([slug], types_in_priority_order)
found_hashtag = nil
types_in_priority_order.each do |type|
if result[type.to_sym].any?
found_hashtag = result[type.to_sym].first.to_h
break
end
end
found_hashtag
end
def get_current_user(user_id)
return unless user_id.is_a?(Integer)
{ staff: User.where(id: user_id).where("moderator OR admin").exists? }
end
end
end