DEV: Apply syntax_tree formatting to `app/*`
This commit is contained in:
parent
a641ce4b62
commit
5a003715d3
1
.streerc
1
.streerc
|
@ -1,3 +1,2 @@
|
|||
--print-width=100
|
||||
--plugins=plugin/trailing_comma,disable_ternary
|
||||
--ignore-files=app/*
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AboutController < ApplicationController
|
||||
|
||||
requires_login only: [:live_post_counts]
|
||||
|
||||
skip_before_action :check_xhr, only: [:index]
|
||||
|
||||
def index
|
||||
return redirect_to path('/login') if SiteSetting.login_required? && current_user.nil?
|
||||
return redirect_to path("/login") if SiteSetting.login_required? && current_user.nil?
|
||||
|
||||
@about = About.new(current_user)
|
||||
@title = "#{I18n.t("js.about.simple_title")} - #{SiteSetting.title}"
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render :index
|
||||
end
|
||||
format.json do
|
||||
render_json_dump(AboutSerializer.new(@about, scope: guardian))
|
||||
end
|
||||
format.html { render :index }
|
||||
format.json { render_json_dump(AboutSerializer.new(@about, scope: guardian)) }
|
||||
end
|
||||
end
|
||||
|
||||
def live_post_counts
|
||||
RateLimiter.new(current_user, "live_post_counts", 1, 10.minutes).performed! unless current_user.staff?
|
||||
unless current_user.staff?
|
||||
RateLimiter.new(current_user, "live_post_counts", 1, 10.minutes).performed!
|
||||
end
|
||||
category_topic_ids = Category.pluck(:topic_id).compact!
|
||||
public_topics = Topic.listable_topics.visible.secured(Guardian.new(nil)).where.not(id: category_topic_ids)
|
||||
public_topics =
|
||||
Topic.listable_topics.visible.secured(Guardian.new(nil)).where.not(id: category_topic_ids)
|
||||
stats = { public_topic_count: public_topics.count }
|
||||
stats[:public_post_count] = public_topics.sum(:posts_count) - stats[:public_topic_count]
|
||||
render json: stats
|
||||
|
|
|
@ -7,5 +7,4 @@ class Admin::AdminController < ApplicationController
|
|||
def index
|
||||
render body: nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -8,7 +8,8 @@ class Admin::ApiController < Admin::AdminController
|
|||
offset = (params[:offset] || 0).to_i
|
||||
limit = (params[:limit] || 50).to_i.clamp(1, 50)
|
||||
|
||||
keys = ApiKey
|
||||
keys =
|
||||
ApiKey
|
||||
.where(hidden: false)
|
||||
.includes(:user, :api_key_scopes)
|
||||
# Sort revoked keys by revoked_at and active keys by created_at
|
||||
|
@ -16,28 +17,27 @@ class Admin::ApiController < Admin::AdminController
|
|||
.offset(offset)
|
||||
.limit(limit)
|
||||
|
||||
render_json_dump(
|
||||
keys: serialize_data(keys, ApiKeySerializer),
|
||||
offset: offset,
|
||||
limit: limit
|
||||
)
|
||||
render_json_dump(keys: serialize_data(keys, ApiKeySerializer), offset: offset, limit: limit)
|
||||
end
|
||||
|
||||
def show
|
||||
api_key = ApiKey.includes(:api_key_scopes).find_by!(id: params[:id])
|
||||
render_serialized(api_key, ApiKeySerializer, root: 'key')
|
||||
render_serialized(api_key, ApiKeySerializer, root: "key")
|
||||
end
|
||||
|
||||
def scopes
|
||||
scopes = ApiKeyScope.scope_mappings.reduce({}) do |memo, (resource, actions)|
|
||||
scopes =
|
||||
ApiKeyScope
|
||||
.scope_mappings
|
||||
.reduce({}) do |memo, (resource, actions)|
|
||||
memo.tap do |m|
|
||||
m[resource] = actions.map do |k, v|
|
||||
{
|
||||
scope_id: "#{resource}:#{k}",
|
||||
key: k,
|
||||
name: k.to_s.gsub('_', ' '),
|
||||
name: k.to_s.gsub("_", " "),
|
||||
params: v[:params],
|
||||
urls: v[:urls]
|
||||
urls: v[:urls],
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -52,7 +52,7 @@ class Admin::ApiController < Admin::AdminController
|
|||
api_key.update!(update_params)
|
||||
log_api_key(api_key, UserHistory.actions[:api_key_update], changes: api_key.saved_changes)
|
||||
end
|
||||
render_serialized(api_key, ApiKeySerializer, root: 'key')
|
||||
render_serialized(api_key, ApiKeySerializer, root: "key")
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -76,7 +76,7 @@ class Admin::ApiController < Admin::AdminController
|
|||
api_key.save!
|
||||
log_api_key(api_key, UserHistory.actions[:api_key_create], changes: api_key.saved_changes)
|
||||
end
|
||||
render_serialized(api_key, ApiKeySerializer, root: 'key')
|
||||
render_serialized(api_key, ApiKeySerializer, root: "key")
|
||||
end
|
||||
|
||||
def undo_revoke_key
|
||||
|
@ -105,7 +105,7 @@ class Admin::ApiController < Admin::AdminController
|
|||
|
||||
def build_scopes
|
||||
params.require(:key)[:scopes].to_a.map do |scope_params|
|
||||
resource, action = scope_params[:scope_id].split(':')
|
||||
resource, action = scope_params[:scope_id].split(":")
|
||||
|
||||
mapping = ApiKeyScope.scope_mappings.dig(resource.to_sym, action.to_sym)
|
||||
raise Discourse::InvalidParameters if mapping.nil? # invalid mapping
|
||||
|
@ -113,7 +113,7 @@ class Admin::ApiController < Admin::AdminController
|
|||
ApiKeyScope.new(
|
||||
resource: resource,
|
||||
action: action,
|
||||
allowed_parameters: build_params(scope_params, mapping[:params])
|
||||
allowed_parameters: build_params(scope_params, mapping[:params]),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -121,9 +121,11 @@ class Admin::ApiController < Admin::AdminController
|
|||
def build_params(scope_params, params)
|
||||
return if params.nil?
|
||||
|
||||
scope_params.slice(*params).tap do |allowed_params|
|
||||
scope_params
|
||||
.slice(*params)
|
||||
.tap do |allowed_params|
|
||||
allowed_params.each do |k, v|
|
||||
v.blank? ? allowed_params.delete(k) : allowed_params[k] = v.split(',')
|
||||
v.blank? ? allowed_params.delete(k) : allowed_params[k] = v.split(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -146,5 +148,4 @@ class Admin::ApiController < Admin::AdminController
|
|||
def log_api_key_restore(*args)
|
||||
StaffActionLogger.new(current_user).log_api_key_restore(*args)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ class Admin::BackupsController < Admin::AdminController
|
|||
include ExternalUploadHelpers
|
||||
|
||||
before_action :ensure_backups_enabled
|
||||
skip_before_action :check_xhr, only: [:index, :show, :logs, :check_backup_chunk, :upload_backup_chunk]
|
||||
skip_before_action :check_xhr, only: %i[index show logs check_backup_chunk upload_backup_chunk]
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
|
@ -62,7 +62,7 @@ class Admin::BackupsController < Admin::AdminController
|
|||
Jobs.enqueue(
|
||||
:download_backup_email,
|
||||
user_id: current_user.id,
|
||||
backup_file_path: url_for(controller: 'backups', action: 'show')
|
||||
backup_file_path: url_for(controller: "backups", action: "show"),
|
||||
)
|
||||
|
||||
render body: nil
|
||||
|
@ -73,8 +73,8 @@ class Admin::BackupsController < Admin::AdminController
|
|||
|
||||
def show
|
||||
if !EmailBackupToken.compare(current_user.id, params.fetch(:token))
|
||||
@error = I18n.t('download_backup_mailer.no_token')
|
||||
return render layout: 'no_ember', status: 422, formats: [:html]
|
||||
@error = I18n.t("download_backup_mailer.no_token")
|
||||
return render layout: "no_ember", status: 422, formats: [:html]
|
||||
end
|
||||
|
||||
store = BackupRestore::BackupStore.create
|
||||
|
@ -86,7 +86,7 @@ class Admin::BackupsController < Admin::AdminController
|
|||
if store.remote?
|
||||
redirect_to backup.source
|
||||
else
|
||||
headers['Content-Length'] = File.size(backup.source).to_s
|
||||
headers["Content-Length"] = File.size(backup.source).to_s
|
||||
send_file backup.source
|
||||
end
|
||||
else
|
||||
|
@ -170,9 +170,15 @@ class Admin::BackupsController < Admin::AdminController
|
|||
identifier = params.fetch(:resumableIdentifier)
|
||||
|
||||
raise Discourse::InvalidParameters.new(:resumableIdentifier) unless valid_filename?(identifier)
|
||||
return render status: 415, plain: I18n.t("backup.backup_file_should_be_tar_gz") unless valid_extension?(filename)
|
||||
return render status: 415, plain: I18n.t("backup.not_enough_space_on_disk") unless has_enough_space_on_disk?(total_size)
|
||||
return render status: 415, plain: I18n.t("backup.invalid_filename") unless valid_filename?(filename)
|
||||
unless valid_extension?(filename)
|
||||
return render status: 415, plain: I18n.t("backup.backup_file_should_be_tar_gz")
|
||||
end
|
||||
unless has_enough_space_on_disk?(total_size)
|
||||
return render status: 415, plain: I18n.t("backup.not_enough_space_on_disk")
|
||||
end
|
||||
unless valid_filename?(filename)
|
||||
return render status: 415, plain: I18n.t("backup.invalid_filename")
|
||||
end
|
||||
|
||||
file = params.fetch(:file)
|
||||
chunk_number = params.fetch(:resumableChunkNumber).to_i
|
||||
|
@ -187,7 +193,13 @@ class Admin::BackupsController < Admin::AdminController
|
|||
uploaded_file_size = previous_chunk_number * chunk_size
|
||||
if uploaded_file_size + current_chunk_size >= total_size
|
||||
# merge all the chunks in a background thread
|
||||
Jobs.enqueue_in(5.seconds, :backup_chunks_merger, filename: filename, identifier: identifier, chunks: chunk_number)
|
||||
Jobs.enqueue_in(
|
||||
5.seconds,
|
||||
:backup_chunks_merger,
|
||||
filename: filename,
|
||||
identifier: identifier,
|
||||
chunks: chunk_number,
|
||||
)
|
||||
end
|
||||
|
||||
render body: nil
|
||||
|
@ -197,7 +209,9 @@ class Admin::BackupsController < Admin::AdminController
|
|||
params.require(:filename)
|
||||
filename = params.fetch(:filename)
|
||||
|
||||
return render_json_error(I18n.t("backup.backup_file_should_be_tar_gz")) unless valid_extension?(filename)
|
||||
unless valid_extension?(filename)
|
||||
return render_json_error(I18n.t("backup.backup_file_should_be_tar_gz"))
|
||||
end
|
||||
return render_json_error(I18n.t("backup.invalid_filename")) unless valid_filename?(filename)
|
||||
|
||||
store = BackupRestore::BackupStore.create
|
||||
|
@ -236,8 +250,16 @@ class Admin::BackupsController < Admin::AdminController
|
|||
end
|
||||
|
||||
def validate_before_create_multipart(file_name:, file_size:, upload_type:)
|
||||
raise ExternalUploadHelpers::ExternalUploadValidationError.new(I18n.t("backup.backup_file_should_be_tar_gz")) unless valid_extension?(file_name)
|
||||
raise ExternalUploadHelpers::ExternalUploadValidationError.new(I18n.t("backup.invalid_filename")) unless valid_filename?(file_name)
|
||||
unless valid_extension?(file_name)
|
||||
raise ExternalUploadHelpers::ExternalUploadValidationError.new(
|
||||
I18n.t("backup.backup_file_should_be_tar_gz"),
|
||||
)
|
||||
end
|
||||
unless valid_filename?(file_name)
|
||||
raise ExternalUploadHelpers::ExternalUploadValidationError.new(
|
||||
I18n.t("backup.invalid_filename"),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def self.serialize_upload(_upload)
|
||||
|
@ -248,7 +270,11 @@ class Admin::BackupsController < Admin::AdminController
|
|||
begin
|
||||
yield
|
||||
rescue BackupRestore::BackupStore::StorageError => err
|
||||
message = debug_upload_error(err, I18n.t("upload.create_multipart_failure", additional_detail: err.message))
|
||||
message =
|
||||
debug_upload_error(
|
||||
err,
|
||||
I18n.t("upload.create_multipart_failure", additional_detail: err.message),
|
||||
)
|
||||
raise ExternalUploadHelpers::ExternalUploadValidationError.new(message)
|
||||
rescue BackupRestore::BackupStore::BackupFileExists
|
||||
raise ExternalUploadHelpers::ExternalUploadValidationError.new(I18n.t("backup.file_exists"))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
require "csv"
|
||||
|
||||
class Admin::BadgesController < Admin::AdminController
|
||||
MAX_CSV_LINES = 50_000
|
||||
|
@ -10,25 +10,29 @@ class Admin::BadgesController < Admin::AdminController
|
|||
data = {
|
||||
badge_types: BadgeType.all.order(:id).to_a,
|
||||
badge_groupings: BadgeGrouping.all.order(:position).to_a,
|
||||
badges: Badge.includes(:badge_grouping)
|
||||
badges:
|
||||
Badge
|
||||
.includes(:badge_grouping)
|
||||
.includes(:badge_type, :image_upload)
|
||||
.references(:badge_grouping)
|
||||
.order('badge_groupings.position, badge_type_id, badges.name').to_a,
|
||||
.order("badge_groupings.position, badge_type_id, badges.name")
|
||||
.to_a,
|
||||
protected_system_fields: Badge.protected_system_fields,
|
||||
triggers: Badge.trigger_hash
|
||||
triggers: Badge.trigger_hash,
|
||||
}
|
||||
render_serialized(OpenStruct.new(data), AdminBadgesSerializer)
|
||||
end
|
||||
|
||||
def preview
|
||||
unless SiteSetting.enable_badge_sql
|
||||
return render json: "preview not allowed", status: 403
|
||||
end
|
||||
return render json: "preview not allowed", status: 403 unless SiteSetting.enable_badge_sql
|
||||
|
||||
render json: BadgeGranter.preview(params[:sql],
|
||||
render json:
|
||||
BadgeGranter.preview(
|
||||
params[:sql],
|
||||
target_posts: params[:target_posts] == "true",
|
||||
explain: params[:explain] == "true",
|
||||
trigger: params[:trigger].to_i)
|
||||
trigger: params[:trigger].to_i,
|
||||
)
|
||||
end
|
||||
|
||||
def new
|
||||
|
@ -47,18 +51,21 @@ class Admin::BadgesController < Admin::AdminController
|
|||
|
||||
if !badge.enabled?
|
||||
render_json_error(
|
||||
I18n.t('badges.mass_award.errors.badge_disabled', badge_name: badge.display_name),
|
||||
status: 422
|
||||
I18n.t("badges.mass_award.errors.badge_disabled", badge_name: badge.display_name),
|
||||
status: 422,
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
replace_badge_owners = params[:replace_badge_owners] == 'true'
|
||||
ensure_users_have_badge_once = params[:grant_existing_holders] != 'true'
|
||||
replace_badge_owners = params[:replace_badge_owners] == "true"
|
||||
ensure_users_have_badge_once = params[:grant_existing_holders] != "true"
|
||||
if !ensure_users_have_badge_once && !badge.multiple_grant?
|
||||
render_json_error(
|
||||
I18n.t('badges.mass_award.errors.cant_grant_multiple_times', badge_name: badge.display_name),
|
||||
status: 422
|
||||
I18n.t(
|
||||
"badges.mass_award.errors.cant_grant_multiple_times",
|
||||
badge_name: badge.display_name,
|
||||
),
|
||||
status: 422,
|
||||
)
|
||||
return
|
||||
end
|
||||
|
@ -72,7 +79,7 @@ class Admin::BadgesController < Admin::AdminController
|
|||
line_number += 1
|
||||
|
||||
if line.present?
|
||||
if line.include?('@')
|
||||
if line.include?("@")
|
||||
emails << line
|
||||
else
|
||||
usernames << line
|
||||
|
@ -80,26 +87,35 @@ class Admin::BadgesController < Admin::AdminController
|
|||
end
|
||||
|
||||
if emails.size + usernames.size > MAX_CSV_LINES
|
||||
return render_json_error I18n.t('badges.mass_award.errors.too_many_csv_entries', count: MAX_CSV_LINES), status: 400
|
||||
return(
|
||||
render_json_error I18n.t(
|
||||
"badges.mass_award.errors.too_many_csv_entries",
|
||||
count: MAX_CSV_LINES,
|
||||
),
|
||||
status: 400
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
BadgeGranter.revoke_all(badge) if replace_badge_owners
|
||||
|
||||
results = BadgeGranter.enqueue_mass_grant_for_users(
|
||||
results =
|
||||
BadgeGranter.enqueue_mass_grant_for_users(
|
||||
badge,
|
||||
emails: emails,
|
||||
usernames: usernames,
|
||||
ensure_users_have_badge_once: ensure_users_have_badge_once
|
||||
ensure_users_have_badge_once: ensure_users_have_badge_once,
|
||||
)
|
||||
|
||||
render json: {
|
||||
unmatched_entries: results[:unmatched_entries].first(100),
|
||||
matched_users_count: results[:matched_users_count],
|
||||
unmatched_entries_count: results[:unmatched_entries_count]
|
||||
}, status: :ok
|
||||
unmatched_entries_count: results[:unmatched_entries_count],
|
||||
},
|
||||
status: :ok
|
||||
rescue CSV::MalformedCSVError
|
||||
render_json_error I18n.t('badges.mass_award.errors.invalid_csv', line_number: line_number), status: 400
|
||||
render_json_error I18n.t("badges.mass_award.errors.invalid_csv", line_number: line_number),
|
||||
status: 400
|
||||
end
|
||||
|
||||
def badge_types
|
||||
|
@ -119,9 +135,7 @@ class Admin::BadgesController < Admin::AdminController
|
|||
group.save
|
||||
end
|
||||
|
||||
badge_groupings.each do |g|
|
||||
g.destroy unless g.system? || ids.include?(g.id)
|
||||
end
|
||||
badge_groupings.each { |g| g.destroy unless g.system? || ids.include?(g.id) }
|
||||
|
||||
badge_groupings = BadgeGrouping.all.order(:position).to_a
|
||||
render_serialized(badge_groupings, BadgeGroupingSerializer, root: "badge_groupings")
|
||||
|
@ -174,20 +188,22 @@ class Admin::BadgesController < Admin::AdminController
|
|||
errors = []
|
||||
Badge.transaction do
|
||||
allowed = Badge.column_names.map(&:to_sym)
|
||||
allowed -= [:id, :created_at, :updated_at, :grant_count]
|
||||
allowed -= %i[id created_at updated_at grant_count]
|
||||
allowed -= Badge.protected_system_fields if badge.system?
|
||||
allowed -= [:query] unless SiteSetting.enable_badge_sql
|
||||
|
||||
params.permit(*allowed)
|
||||
|
||||
allowed.each do |key|
|
||||
badge.public_send("#{key}=" , params[key]) if params[key]
|
||||
end
|
||||
allowed.each { |key| badge.public_send("#{key}=", params[key]) if params[key] }
|
||||
|
||||
# Badge query contract checks
|
||||
begin
|
||||
if SiteSetting.enable_badge_sql
|
||||
BadgeGranter.contract_checks!(badge.query, target_posts: badge.target_posts, trigger: badge.trigger)
|
||||
BadgeGranter.contract_checks!(
|
||||
badge.query,
|
||||
target_posts: badge.target_posts,
|
||||
trigger: badge.trigger,
|
||||
)
|
||||
end
|
||||
rescue => e
|
||||
errors << e.message
|
||||
|
@ -203,7 +219,7 @@ class Admin::BadgesController < Admin::AdminController
|
|||
:bulk_user_title_update,
|
||||
new_title: badge.name,
|
||||
granted_badge_id: badge.id,
|
||||
action: Jobs::BulkUserTitleUpdate::UPDATE_ACTION
|
||||
action: Jobs::BulkUserTitleUpdate::UPDATE_ACTION,
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ColorSchemesController < Admin::AdminController
|
||||
|
||||
before_action :fetch_color_scheme, only: [:update, :destroy]
|
||||
before_action :fetch_color_scheme, only: %i[update destroy]
|
||||
|
||||
def index
|
||||
render_serialized(ColorScheme.base_color_schemes + ColorScheme.order('id ASC').all.to_a, ColorSchemeSerializer)
|
||||
render_serialized(
|
||||
ColorScheme.base_color_schemes + ColorScheme.order("id ASC").all.to_a,
|
||||
ColorSchemeSerializer,
|
||||
)
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -38,6 +40,8 @@ class Admin::ColorSchemesController < Admin::AdminController
|
|||
end
|
||||
|
||||
def color_scheme_params
|
||||
params.permit(color_scheme: [:base_scheme_id, :name, :user_selectable, colors: [:name, :hex]])[:color_scheme]
|
||||
params.permit(color_scheme: [:base_scheme_id, :name, :user_selectable, colors: %i[name hex]])[
|
||||
:color_scheme
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,9 +11,12 @@ class Admin::DashboardController < Admin::StaffController
|
|||
render json: data
|
||||
end
|
||||
|
||||
def moderation; end
|
||||
def security; end
|
||||
def reports; end
|
||||
def moderation
|
||||
end
|
||||
def security
|
||||
end
|
||||
def reports
|
||||
end
|
||||
|
||||
def general
|
||||
render json: AdminDashboardGeneralData.fetch_cached_stats
|
||||
|
@ -33,7 +36,7 @@ class Admin::DashboardController < Admin::StaffController
|
|||
data = {
|
||||
new_features: new_features,
|
||||
has_unseen_features: DiscourseUpdates.has_unseen_features?(current_user.id),
|
||||
release_notes_link: AdminDashboardGeneralData.fetch_cached_stats["release_notes_link"]
|
||||
release_notes_link: AdminDashboardGeneralData.fetch_cached_stats["release_notes_link"],
|
||||
}
|
||||
render json: data
|
||||
end
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmailController < Admin::AdminController
|
||||
|
||||
def index
|
||||
data = { delivery_method: delivery_method, settings: delivery_settings }
|
||||
render_json_dump(data)
|
||||
|
@ -35,29 +34,22 @@ class Admin::EmailController < Admin::AdminController
|
|||
else
|
||||
email_logs.where(
|
||||
"replace(post_reply_keys.reply_key::VARCHAR, '-', '') ILIKE ?",
|
||||
"%#{reply_key}%"
|
||||
"%#{reply_key}%",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
email_logs = email_logs.to_a
|
||||
|
||||
tuples = email_logs.map do |email_log|
|
||||
[email_log.post_id, email_log.user_id]
|
||||
end
|
||||
tuples = email_logs.map { |email_log| [email_log.post_id, email_log.user_id] }
|
||||
|
||||
reply_keys = {}
|
||||
|
||||
if tuples.present?
|
||||
PostReplyKey
|
||||
.where(
|
||||
"(post_id,user_id) IN (#{(['(?)'] * tuples.size).join(', ')})",
|
||||
*tuples
|
||||
)
|
||||
.where("(post_id,user_id) IN (#{(["(?)"] * tuples.size).join(", ")})", *tuples)
|
||||
.pluck(:post_id, :user_id, "reply_key::text")
|
||||
.each do |post_id, user_id, key|
|
||||
reply_keys[[post_id, user_id]] = key
|
||||
end
|
||||
.each { |post_id, user_id, key| reply_keys[[post_id, user_id]] = key }
|
||||
end
|
||||
|
||||
render_serialized(email_logs, EmailLogSerializer, reply_keys: reply_keys)
|
||||
|
@ -96,14 +88,10 @@ class Admin::EmailController < Admin::AdminController
|
|||
def advanced_test
|
||||
params.require(:email)
|
||||
|
||||
receiver = Email::Receiver.new(params['email'])
|
||||
receiver = Email::Receiver.new(params["email"])
|
||||
text, elided, format = receiver.select_body
|
||||
|
||||
render json: success_json.merge!(
|
||||
text: text,
|
||||
elided: elided,
|
||||
format: format
|
||||
)
|
||||
render json: success_json.merge!(text: text, elided: elided, format: format)
|
||||
end
|
||||
|
||||
def send_digest
|
||||
|
@ -112,9 +100,8 @@ class Admin::EmailController < Admin::AdminController
|
|||
params.require(:email)
|
||||
user = User.find_by_username(params[:username])
|
||||
|
||||
message, skip_reason = UserNotifications.public_send(:digest, user,
|
||||
since: params[:last_seen_at]
|
||||
)
|
||||
message, skip_reason =
|
||||
UserNotifications.public_send(:digest, user, since: params[:last_seen_at])
|
||||
|
||||
if message
|
||||
message.to = params[:email]
|
||||
|
@ -134,9 +121,16 @@ class Admin::EmailController < Admin::AdminController
|
|||
params.require(:to)
|
||||
# These strings aren't localized; they are sent to an anonymous SMTP user.
|
||||
if !User.with_email(Email.downcase(params[:from])).exists? && !SiteSetting.enable_staged_users
|
||||
render json: { reject: true, reason: "Mail from your address is not accepted. Do you have an account here?" }
|
||||
render json: {
|
||||
reject: true,
|
||||
reason: "Mail from your address is not accepted. Do you have an account here?",
|
||||
}
|
||||
elsif Email::Receiver.check_address(Email.downcase(params[:to])).nil?
|
||||
render json: { reject: true, reason: "Mail to this address is not accepted. Check the address and try to send again?" }
|
||||
render json: {
|
||||
reject: true,
|
||||
reason:
|
||||
"Mail to this address is not accepted. Check the address and try to send again?",
|
||||
}
|
||||
else
|
||||
render json: { reject: false }
|
||||
end
|
||||
|
@ -157,10 +151,15 @@ class Admin::EmailController < Admin::AdminController
|
|||
retry_count = 0
|
||||
|
||||
begin
|
||||
Jobs.enqueue(:process_email, mail: email_raw, retry_on_rate_limit: true, source: "handle_mail")
|
||||
Jobs.enqueue(
|
||||
:process_email,
|
||||
mail: email_raw,
|
||||
retry_on_rate_limit: true,
|
||||
source: "handle_mail",
|
||||
)
|
||||
rescue JSON::GeneratorError, Encoding::UndefinedConversionError => e
|
||||
if retry_count == 0
|
||||
email_raw = email_raw.force_encoding('iso-8859-1').encode("UTF-8")
|
||||
email_raw = email_raw.force_encoding("iso-8859-1").encode("UTF-8")
|
||||
retry_count += 1
|
||||
retry
|
||||
else
|
||||
|
@ -171,7 +170,8 @@ class Admin::EmailController < Admin::AdminController
|
|||
# TODO: 2022-05-01 Remove this route once all sites have migrated over
|
||||
# to using the new email_encoded param.
|
||||
if deprecated_email_param_used
|
||||
render plain: "warning: the email parameter is deprecated. all POST requests to this route should be sent with a base64 strict encoded email_encoded parameter instead. email has been received and is queued for processing"
|
||||
render plain:
|
||||
"warning: the email parameter is deprecated. all POST requests to this route should be sent with a base64 strict encoded email_encoded parameter instead. email has been received and is queued for processing"
|
||||
else
|
||||
render plain: "email has been received and is queued for processing"
|
||||
end
|
||||
|
@ -204,15 +204,15 @@ class Admin::EmailController < Admin::AdminController
|
|||
end
|
||||
|
||||
if incoming_email.nil?
|
||||
email_local_part, email_domain = SiteSetting.notification_email.split('@')
|
||||
email_local_part, email_domain = SiteSetting.notification_email.split("@")
|
||||
bounced_to_address = "#{email_local_part}+verp-#{email_log.bounce_key}@#{email_domain}"
|
||||
incoming_email = IncomingEmail.find_by(to_addresses: bounced_to_address)
|
||||
end
|
||||
|
||||
# Temporary fix until all old format of emails has been purged via lib/email/cleaner.rb
|
||||
if incoming_email.nil?
|
||||
email_local_part, email_domain = SiteSetting.reply_by_email_address.split('@')
|
||||
subdomain, root_domain, extension = email_domain&.split('.')
|
||||
email_local_part, email_domain = SiteSetting.reply_by_email_address.split("@")
|
||||
subdomain, root_domain, extension = email_domain&.split(".")
|
||||
bounced_to_address = "#{subdomain}+verp-#{email_log.bounce_key}@#{root_domain}.#{extension}"
|
||||
incoming_email = IncomingEmail.find_by(to_addresses: bounced_to_address)
|
||||
end
|
||||
|
@ -231,41 +231,61 @@ class Admin::EmailController < Admin::AdminController
|
|||
def filter_logs(logs, params)
|
||||
table_name = logs.table_name
|
||||
|
||||
logs = logs.includes(:user, post: :topic)
|
||||
logs =
|
||||
logs
|
||||
.includes(:user, post: :topic)
|
||||
.references(:user)
|
||||
.order(created_at: :desc)
|
||||
.offset(params[:offset] || 0)
|
||||
.limit(50)
|
||||
|
||||
logs = logs.where("users.username ILIKE ?", "%#{params[:user]}%") if params[:user].present?
|
||||
logs = logs.where("#{table_name}.to_address ILIKE ?", "%#{params[:address]}%") if params[:address].present?
|
||||
logs = logs.where("#{table_name}.email_type ILIKE ?", "%#{params[:type]}%") if params[:type].present?
|
||||
logs = logs.where("#{table_name}.to_address ILIKE ?", "%#{params[:address]}%") if params[
|
||||
:address
|
||||
].present?
|
||||
logs = logs.where("#{table_name}.email_type ILIKE ?", "%#{params[:type]}%") if params[
|
||||
:type
|
||||
].present?
|
||||
|
||||
if table_name == "email_logs" && params[:smtp_transaction_response].present?
|
||||
logs = logs.where("#{table_name}.smtp_transaction_response ILIKE ?", "%#{params[:smtp_transaction_response]}%")
|
||||
logs =
|
||||
logs.where(
|
||||
"#{table_name}.smtp_transaction_response ILIKE ?",
|
||||
"%#{params[:smtp_transaction_response]}%",
|
||||
)
|
||||
end
|
||||
|
||||
logs
|
||||
end
|
||||
|
||||
def filter_incoming_emails(incoming_emails, params)
|
||||
incoming_emails = incoming_emails.includes(:user, post: :topic)
|
||||
incoming_emails =
|
||||
incoming_emails
|
||||
.includes(:user, post: :topic)
|
||||
.order(created_at: :desc)
|
||||
.offset(params[:offset] || 0)
|
||||
.limit(50)
|
||||
|
||||
incoming_emails = incoming_emails.where("from_address ILIKE ?", "%#{params[:from]}%") if params[:from].present?
|
||||
incoming_emails = incoming_emails.where("to_addresses ILIKE :to OR cc_addresses ILIKE :to", to: "%#{params[:to]}%") if params[:to].present?
|
||||
incoming_emails = incoming_emails.where("subject ILIKE ?", "%#{params[:subject]}%") if params[:subject].present?
|
||||
incoming_emails = incoming_emails.where("error ILIKE ?", "%#{params[:error]}%") if params[:error].present?
|
||||
incoming_emails = incoming_emails.where("from_address ILIKE ?", "%#{params[:from]}%") if params[
|
||||
:from
|
||||
].present?
|
||||
incoming_emails =
|
||||
incoming_emails.where(
|
||||
"to_addresses ILIKE :to OR cc_addresses ILIKE :to",
|
||||
to: "%#{params[:to]}%",
|
||||
) if params[:to].present?
|
||||
incoming_emails = incoming_emails.where("subject ILIKE ?", "%#{params[:subject]}%") if params[
|
||||
:subject
|
||||
].present?
|
||||
incoming_emails = incoming_emails.where("error ILIKE ?", "%#{params[:error]}%") if params[
|
||||
:error
|
||||
].present?
|
||||
|
||||
incoming_emails
|
||||
end
|
||||
|
||||
def delivery_settings
|
||||
action_mailer_settings
|
||||
.reject { |k, _| k == :password }
|
||||
.map { |k, v| { name: k, value: v } }
|
||||
action_mailer_settings.reject { |k, _| k == :password }.map { |k, v| { name: k, value: v } }
|
||||
end
|
||||
|
||||
def delivery_method
|
||||
|
|
|
@ -1,70 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmailTemplatesController < Admin::AdminController
|
||||
|
||||
def self.email_keys
|
||||
@email_keys ||= [
|
||||
"custom_invite_forum_mailer",
|
||||
"custom_invite_mailer",
|
||||
"invite_forum_mailer",
|
||||
"invite_mailer",
|
||||
"invite_password_instructions",
|
||||
"new_version_mailer",
|
||||
"new_version_mailer_with_notes",
|
||||
"system_messages.backup_failed",
|
||||
"system_messages.backup_succeeded",
|
||||
"system_messages.bulk_invite_failed",
|
||||
"system_messages.bulk_invite_succeeded",
|
||||
"system_messages.csv_export_failed",
|
||||
"system_messages.csv_export_succeeded",
|
||||
"system_messages.download_remote_images_disabled",
|
||||
"system_messages.email_error_notification",
|
||||
"system_messages.email_reject_auto_generated",
|
||||
"system_messages.email_reject_bad_destination_address",
|
||||
"system_messages.email_reject_empty",
|
||||
"system_messages.email_reject_invalid_access",
|
||||
"system_messages.email_reject_parsing",
|
||||
"system_messages.email_reject_reply_key",
|
||||
"system_messages.email_reject_screened_email",
|
||||
"system_messages.email_reject_topic_closed",
|
||||
"system_messages.email_reject_topic_not_found",
|
||||
"system_messages.email_reject_unrecognized_error",
|
||||
"system_messages.email_reject_user_not_found",
|
||||
"system_messages.email_revoked",
|
||||
"system_messages.pending_users_reminder",
|
||||
"system_messages.post_hidden",
|
||||
"system_messages.post_hidden_again",
|
||||
"system_messages.queued_posts_reminder",
|
||||
"system_messages.restore_failed",
|
||||
"system_messages.restore_succeeded",
|
||||
"system_messages.silenced_by_staff",
|
||||
"system_messages.spam_post_blocked",
|
||||
"system_messages.too_many_spam_flags",
|
||||
"system_messages.unsilenced",
|
||||
"system_messages.user_automatically_silenced",
|
||||
"system_messages.welcome_invite",
|
||||
"system_messages.welcome_user",
|
||||
"system_messages.welcome_staff",
|
||||
"test_mailer",
|
||||
"user_notifications.account_created",
|
||||
"user_notifications.admin_login",
|
||||
"user_notifications.confirm_new_email",
|
||||
"user_notifications.email_login",
|
||||
"user_notifications.forgot_password",
|
||||
"user_notifications.notify_old_email",
|
||||
"user_notifications.set_password",
|
||||
"user_notifications.signup",
|
||||
"user_notifications.signup_after_approval",
|
||||
"user_notifications.suspicious_login",
|
||||
"user_notifications.user_invited_to_private_message_pm",
|
||||
"user_notifications.user_invited_to_private_message_pm_group",
|
||||
"user_notifications.user_invited_to_topic",
|
||||
"user_notifications.user_linked",
|
||||
"user_notifications.user_mentioned",
|
||||
"user_notifications.user_posted",
|
||||
"user_notifications.user_posted_pm",
|
||||
"user_notifications.user_quoted",
|
||||
"user_notifications.user_replied",
|
||||
@email_keys ||= %w[
|
||||
custom_invite_forum_mailer
|
||||
custom_invite_mailer
|
||||
invite_forum_mailer
|
||||
invite_mailer
|
||||
invite_password_instructions
|
||||
new_version_mailer
|
||||
new_version_mailer_with_notes
|
||||
system_messages.backup_failed
|
||||
system_messages.backup_succeeded
|
||||
system_messages.bulk_invite_failed
|
||||
system_messages.bulk_invite_succeeded
|
||||
system_messages.csv_export_failed
|
||||
system_messages.csv_export_succeeded
|
||||
system_messages.download_remote_images_disabled
|
||||
system_messages.email_error_notification
|
||||
system_messages.email_reject_auto_generated
|
||||
system_messages.email_reject_bad_destination_address
|
||||
system_messages.email_reject_empty
|
||||
system_messages.email_reject_invalid_access
|
||||
system_messages.email_reject_parsing
|
||||
system_messages.email_reject_reply_key
|
||||
system_messages.email_reject_screened_email
|
||||
system_messages.email_reject_topic_closed
|
||||
system_messages.email_reject_topic_not_found
|
||||
system_messages.email_reject_unrecognized_error
|
||||
system_messages.email_reject_user_not_found
|
||||
system_messages.email_revoked
|
||||
system_messages.pending_users_reminder
|
||||
system_messages.post_hidden
|
||||
system_messages.post_hidden_again
|
||||
system_messages.queued_posts_reminder
|
||||
system_messages.restore_failed
|
||||
system_messages.restore_succeeded
|
||||
system_messages.silenced_by_staff
|
||||
system_messages.spam_post_blocked
|
||||
system_messages.too_many_spam_flags
|
||||
system_messages.unsilenced
|
||||
system_messages.user_automatically_silenced
|
||||
system_messages.welcome_invite
|
||||
system_messages.welcome_user
|
||||
system_messages.welcome_staff
|
||||
test_mailer
|
||||
user_notifications.account_created
|
||||
user_notifications.admin_login
|
||||
user_notifications.confirm_new_email
|
||||
user_notifications.email_login
|
||||
user_notifications.forgot_password
|
||||
user_notifications.notify_old_email
|
||||
user_notifications.set_password
|
||||
user_notifications.signup
|
||||
user_notifications.signup_after_approval
|
||||
user_notifications.suspicious_login
|
||||
user_notifications.user_invited_to_private_message_pm
|
||||
user_notifications.user_invited_to_private_message_pm_group
|
||||
user_notifications.user_invited_to_topic
|
||||
user_notifications.user_linked
|
||||
user_notifications.user_mentioned
|
||||
user_notifications.user_posted
|
||||
user_notifications.user_posted_pm
|
||||
user_notifications.user_quoted
|
||||
user_notifications.user_replied
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -91,9 +90,18 @@ class Admin::EmailTemplatesController < Admin::AdminController
|
|||
log_site_text_change(subject_result)
|
||||
log_site_text_change(body_result)
|
||||
|
||||
render_serialized(key, AdminEmailTemplateSerializer, root: 'email_template', rest_serializer: true)
|
||||
render_serialized(
|
||||
key,
|
||||
AdminEmailTemplateSerializer,
|
||||
root: "email_template",
|
||||
rest_serializer: true,
|
||||
)
|
||||
else
|
||||
TranslationOverride.upsert!(I18n.locale, "#{key}.subject_template", subject_result[:old_value])
|
||||
TranslationOverride.upsert!(
|
||||
I18n.locale,
|
||||
"#{key}.subject_template",
|
||||
subject_result[:old_value],
|
||||
)
|
||||
TranslationOverride.upsert!(I18n.locale, "#{key}.text_body_template", body_result[:old_value])
|
||||
|
||||
render_json_error(error_messages)
|
||||
|
@ -105,11 +113,22 @@ class Admin::EmailTemplatesController < Admin::AdminController
|
|||
raise Discourse::NotFound unless self.class.email_keys.include?(params[:id])
|
||||
|
||||
revert_and_log("#{key}.subject_template", "#{key}.text_body_template")
|
||||
render_serialized(key, AdminEmailTemplateSerializer, root: 'email_template', rest_serializer: true)
|
||||
render_serialized(
|
||||
key,
|
||||
AdminEmailTemplateSerializer,
|
||||
root: "email_template",
|
||||
rest_serializer: true,
|
||||
)
|
||||
end
|
||||
|
||||
def index
|
||||
render_serialized(self.class.email_keys, AdminEmailTemplateSerializer, root: 'email_templates', rest_serializer: true, overridden_keys: overridden_keys)
|
||||
render_serialized(
|
||||
self.class.email_keys,
|
||||
AdminEmailTemplateSerializer,
|
||||
root: "email_templates",
|
||||
rest_serializer: true,
|
||||
overridden_keys: overridden_keys,
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -121,11 +140,7 @@ class Admin::EmailTemplatesController < Admin::AdminController
|
|||
translation_override = TranslationOverride.upsert!(I18n.locale, key, value)
|
||||
end
|
||||
|
||||
{
|
||||
key: key,
|
||||
old_value: old_value,
|
||||
error_messages: translation_override&.errors&.full_messages
|
||||
}
|
||||
{ key: key, old_value: old_value, error_messages: translation_override&.errors&.full_messages }
|
||||
end
|
||||
|
||||
def revert_and_log(*keys)
|
||||
|
@ -144,7 +159,10 @@ class Admin::EmailTemplatesController < Admin::AdminController
|
|||
def log_site_text_change(update_result)
|
||||
new_value = I18n.t(update_result[:key])
|
||||
StaffActionLogger.new(current_user).log_site_text_change(
|
||||
update_result[:key], new_value, update_result[:old_value])
|
||||
update_result[:key],
|
||||
new_value,
|
||||
update_result[:old_value],
|
||||
)
|
||||
end
|
||||
|
||||
def format_error_message(update_result, attribute_key)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmbeddableHostsController < Admin::AdminController
|
||||
|
||||
def create
|
||||
save_host(EmbeddableHost.new, :create)
|
||||
end
|
||||
|
@ -14,7 +13,10 @@ class Admin::EmbeddableHostsController < Admin::AdminController
|
|||
def destroy
|
||||
host = EmbeddableHost.where(id: params[:id]).first
|
||||
host.destroy
|
||||
StaffActionLogger.new(current_user).log_embeddable_host(host, UserHistory.actions[:embeddable_host_destroy])
|
||||
StaffActionLogger.new(current_user).log_embeddable_host(
|
||||
host,
|
||||
UserHistory.actions[:embeddable_host_destroy],
|
||||
)
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
|
@ -29,11 +31,19 @@ class Admin::EmbeddableHostsController < Admin::AdminController
|
|||
|
||||
if host.save
|
||||
changes = host.saved_changes if action == :update
|
||||
StaffActionLogger.new(current_user).log_embeddable_host(host, UserHistory.actions[:"embeddable_host_#{action}"], changes: changes)
|
||||
render_serialized(host, EmbeddableHostSerializer, root: 'embeddable_host', rest_serializer: true)
|
||||
StaffActionLogger.new(current_user).log_embeddable_host(
|
||||
host,
|
||||
UserHistory.actions[:"embeddable_host_#{action}"],
|
||||
changes: changes,
|
||||
)
|
||||
render_serialized(
|
||||
host,
|
||||
EmbeddableHostSerializer,
|
||||
root: "embeddable_host",
|
||||
rest_serializer: true,
|
||||
)
|
||||
else
|
||||
render_json_error(host)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmbeddingController < Admin::AdminController
|
||||
|
||||
before_action :fetch_embedding
|
||||
|
||||
def show
|
||||
render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
|
||||
render_serialized(@embedding, EmbeddingSerializer, root: "embedding", rest_serializer: true)
|
||||
end
|
||||
|
||||
def update
|
||||
if params[:embedding][:embed_by_username].blank?
|
||||
return render_json_error(I18n.t('site_settings.embed_username_required'))
|
||||
return render_json_error(I18n.t("site_settings.embed_username_required"))
|
||||
end
|
||||
|
||||
Embedding.settings.each do |s|
|
||||
@embedding.public_send("#{s}=", params[:embedding][s])
|
||||
end
|
||||
Embedding.settings.each { |s| @embedding.public_send("#{s}=", params[:embedding][s]) }
|
||||
|
||||
if @embedding.save
|
||||
fetch_embedding
|
||||
render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
|
||||
render_serialized(@embedding, EmbeddingSerializer, root: "embedding", rest_serializer: true)
|
||||
else
|
||||
render_json_error(@embedding)
|
||||
end
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmojisController < Admin::AdminController
|
||||
|
||||
def index
|
||||
render_serialized(Emoji.custom, EmojiSerializer, root: false)
|
||||
end
|
||||
|
@ -17,15 +16,12 @@ class Admin::EmojisController < Admin::AdminController
|
|||
hijack do
|
||||
# fix the name
|
||||
name = File.basename(name, ".*")
|
||||
name = name.gsub(/[^a-z0-9]+/i, '_')
|
||||
.gsub(/_{2,}/, '_')
|
||||
.downcase
|
||||
name = name.gsub(/[^a-z0-9]+/i, "_").gsub(/_{2,}/, "_").downcase
|
||||
|
||||
upload = UploadCreator.new(
|
||||
file.tempfile,
|
||||
file.original_filename,
|
||||
type: 'custom_emoji'
|
||||
).create_for(current_user.id)
|
||||
upload =
|
||||
UploadCreator.new(file.tempfile, file.original_filename, type: "custom_emoji").create_for(
|
||||
current_user.id,
|
||||
)
|
||||
|
||||
good = true
|
||||
|
||||
|
@ -61,5 +57,4 @@ class Admin::EmojisController < Admin::AdminController
|
|||
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -7,27 +7,19 @@ class Admin::GroupsController < Admin::StaffController
|
|||
attributes = group_params.to_h.except(:owner_usernames, :usernames)
|
||||
group = Group.new(attributes)
|
||||
|
||||
unless group_params[:allow_membership_requests]
|
||||
group.membership_request_template = nil
|
||||
end
|
||||
group.membership_request_template = nil unless group_params[:allow_membership_requests]
|
||||
|
||||
if group_params[:owner_usernames].present?
|
||||
owner_ids = User.where(
|
||||
username: group_params[:owner_usernames].split(",")
|
||||
).pluck(:id)
|
||||
owner_ids = User.where(username: group_params[:owner_usernames].split(",")).pluck(:id)
|
||||
|
||||
owner_ids.each do |user_id|
|
||||
group.group_users.build(user_id: user_id, owner: true)
|
||||
end
|
||||
owner_ids.each { |user_id| group.group_users.build(user_id: user_id, owner: true) }
|
||||
end
|
||||
|
||||
if group_params[:usernames].present?
|
||||
user_ids = User.where(username: group_params[:usernames].split(",")).pluck(:id)
|
||||
user_ids -= owner_ids if owner_ids
|
||||
|
||||
user_ids.each do |user_id|
|
||||
group.group_users.build(user_id: user_id)
|
||||
end
|
||||
user_ids.each { |user_id| group.group_users.build(user_id: user_id) }
|
||||
end
|
||||
|
||||
if group.save
|
||||
|
@ -140,45 +132,43 @@ class Admin::GroupsController < Admin::StaffController
|
|||
protected
|
||||
|
||||
def can_not_modify_automatic
|
||||
render_json_error(I18n.t('groups.errors.can_not_modify_automatic'))
|
||||
render_json_error(I18n.t("groups.errors.can_not_modify_automatic"))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def group_params
|
||||
permitted = [
|
||||
:name,
|
||||
:mentionable_level,
|
||||
:messageable_level,
|
||||
:visibility_level,
|
||||
:members_visibility_level,
|
||||
:automatic_membership_email_domains,
|
||||
:title,
|
||||
:primary_group,
|
||||
:grant_trust_level,
|
||||
:incoming_email,
|
||||
:flair_icon,
|
||||
:flair_upload_id,
|
||||
:flair_bg_color,
|
||||
:flair_color,
|
||||
:bio_raw,
|
||||
:public_admission,
|
||||
:public_exit,
|
||||
:allow_membership_requests,
|
||||
:full_name,
|
||||
:default_notification_level,
|
||||
:membership_request_template,
|
||||
:owner_usernames,
|
||||
:usernames,
|
||||
:publish_read_state,
|
||||
:notify_users
|
||||
permitted = %i[
|
||||
name
|
||||
mentionable_level
|
||||
messageable_level
|
||||
visibility_level
|
||||
members_visibility_level
|
||||
automatic_membership_email_domains
|
||||
title
|
||||
primary_group
|
||||
grant_trust_level
|
||||
incoming_email
|
||||
flair_icon
|
||||
flair_upload_id
|
||||
flair_bg_color
|
||||
flair_color
|
||||
bio_raw
|
||||
public_admission
|
||||
public_exit
|
||||
allow_membership_requests
|
||||
full_name
|
||||
default_notification_level
|
||||
membership_request_template
|
||||
owner_usernames
|
||||
usernames
|
||||
publish_read_state
|
||||
notify_users
|
||||
]
|
||||
custom_fields = DiscoursePluginRegistry.editable_group_custom_fields
|
||||
permitted << { custom_fields: custom_fields } unless custom_fields.blank?
|
||||
|
||||
if guardian.can_associate_groups?
|
||||
permitted << { associated_group_ids: [] }
|
||||
end
|
||||
permitted << { associated_group_ids: [] } if guardian.can_associate_groups?
|
||||
|
||||
permitted = permitted | DiscoursePluginRegistry.group_params
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ImpersonateController < Admin::AdminController
|
||||
|
||||
def create
|
||||
params.require(:username_or_email)
|
||||
|
||||
|
@ -18,5 +17,4 @@ class Admin::ImpersonateController < Admin::AdminController
|
|||
|
||||
render body: nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::PermalinksController < Admin::AdminController
|
||||
|
||||
before_action :fetch_permalink, only: [:destroy]
|
||||
|
||||
def index
|
||||
|
@ -20,7 +19,8 @@ class Admin::PermalinksController < Admin::AdminController
|
|||
params[:permalink_type_value] = Tag.find_by_name(params[:permalink_type_value])&.id
|
||||
end
|
||||
|
||||
permalink = Permalink.new(:url => params[:url], params[:permalink_type] => params[:permalink_type_value])
|
||||
permalink =
|
||||
Permalink.new(:url => params[:url], params[:permalink_type] => params[:permalink_type_value])
|
||||
if permalink.save
|
||||
render_serialized(permalink, PermalinkSerializer)
|
||||
else
|
||||
|
@ -38,5 +38,4 @@ class Admin::PermalinksController < Admin::AdminController
|
|||
def fetch_permalink
|
||||
@permalink = Permalink.find(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::PluginsController < Admin::StaffController
|
||||
|
||||
def index
|
||||
render_serialized(Discourse.visible_plugins, AdminPluginSerializer, root: 'plugins')
|
||||
render_serialized(Discourse.visible_plugins, AdminPluginSerializer, root: "plugins")
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -2,22 +2,26 @@
|
|||
|
||||
class Admin::ReportsController < Admin::StaffController
|
||||
def index
|
||||
reports_methods = ['page_view_total_reqs'] +
|
||||
ApplicationRequest.req_types.keys
|
||||
reports_methods =
|
||||
["page_view_total_reqs"] +
|
||||
ApplicationRequest
|
||||
.req_types
|
||||
.keys
|
||||
.select { |r| r =~ /^page_view_/ && r !~ /mobile/ }
|
||||
.map { |r| r + "_reqs" } +
|
||||
Report.singleton_methods.grep(/^report_(?!about|storage_stats)/)
|
||||
|
||||
reports = reports_methods.map do |name|
|
||||
type = name.to_s.gsub('report_', '')
|
||||
description = I18n.t("reports.#{type}.description", default: '')
|
||||
description_link = I18n.t("reports.#{type}.description_link", default: '')
|
||||
reports =
|
||||
reports_methods.map do |name|
|
||||
type = name.to_s.gsub("report_", "")
|
||||
description = I18n.t("reports.#{type}.description", default: "")
|
||||
description_link = I18n.t("reports.#{type}.description_link", default: "")
|
||||
|
||||
{
|
||||
type: type,
|
||||
title: I18n.t("reports.#{type}.title"),
|
||||
description: description.presence ? description : nil,
|
||||
description_link: description_link.presence ? description_link : nil
|
||||
description_link: description_link.presence ? description_link : nil,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -32,18 +36,14 @@ class Admin::ReportsController < Admin::StaffController
|
|||
args = parse_params(report_params)
|
||||
|
||||
report = nil
|
||||
if (report_params[:cache])
|
||||
report = Report.find_cached(report_type, args)
|
||||
end
|
||||
report = Report.find_cached(report_type, args) if (report_params[:cache])
|
||||
|
||||
if report
|
||||
reports << report
|
||||
else
|
||||
report = Report.find(report_type, args)
|
||||
|
||||
if (report_params[:cache]) && report
|
||||
Report.cache(report)
|
||||
end
|
||||
Report.cache(report) if (report_params[:cache]) && report
|
||||
|
||||
if report.blank?
|
||||
report = Report._get(report_type, args)
|
||||
|
@ -66,22 +66,16 @@ class Admin::ReportsController < Admin::StaffController
|
|||
args = parse_params(params)
|
||||
|
||||
report = nil
|
||||
if (params[:cache])
|
||||
report = Report.find_cached(report_type, args)
|
||||
end
|
||||
report = Report.find_cached(report_type, args) if (params[:cache])
|
||||
|
||||
if report
|
||||
return render_json_dump(report: report)
|
||||
end
|
||||
return render_json_dump(report: report) if report
|
||||
|
||||
hijack do
|
||||
report = Report.find(report_type, args)
|
||||
|
||||
raise Discourse::NotFound if report.blank?
|
||||
|
||||
if (params[:cache])
|
||||
Report.cache(report)
|
||||
end
|
||||
Report.cache(report) if (params[:cache])
|
||||
|
||||
render_json_dump(report: report)
|
||||
end
|
||||
|
@ -91,16 +85,28 @@ class Admin::ReportsController < Admin::StaffController
|
|||
|
||||
def parse_params(report_params)
|
||||
begin
|
||||
start_date = (report_params[:start_date].present? ? Time.parse(report_params[:start_date]).to_date : 1.days.ago).beginning_of_day
|
||||
end_date = (report_params[:end_date].present? ? Time.parse(report_params[:end_date]).to_date : start_date + 30.days).end_of_day
|
||||
start_date =
|
||||
(
|
||||
if report_params[:start_date].present?
|
||||
Time.parse(report_params[:start_date]).to_date
|
||||
else
|
||||
1.days.ago
|
||||
end
|
||||
).beginning_of_day
|
||||
end_date =
|
||||
(
|
||||
if report_params[:end_date].present?
|
||||
Time.parse(report_params[:end_date]).to_date
|
||||
else
|
||||
start_date + 30.days
|
||||
end
|
||||
).end_of_day
|
||||
rescue ArgumentError => e
|
||||
raise Discourse::InvalidParameters.new(e.message)
|
||||
end
|
||||
|
||||
facets = nil
|
||||
if Array === report_params[:facets]
|
||||
facets = report_params[:facets].map { |s| s.to_s.to_sym }
|
||||
end
|
||||
facets = report_params[:facets].map { |s| s.to_s.to_sym } if Array === report_params[:facets]
|
||||
|
||||
limit = nil
|
||||
if report_params.has_key?(:limit) && report_params[:limit].to_i > 0
|
||||
|
@ -108,16 +114,8 @@ class Admin::ReportsController < Admin::StaffController
|
|||
end
|
||||
|
||||
filters = nil
|
||||
if report_params.has_key?(:filters)
|
||||
filters = report_params[:filters]
|
||||
end
|
||||
filters = report_params[:filters] if report_params.has_key?(:filters)
|
||||
|
||||
{
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
filters: filters,
|
||||
facets: facets,
|
||||
limit: limit
|
||||
}
|
||||
{ start_date: start_date, end_date: end_date, filters: filters, facets: facets, limit: limit }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::RobotsTxtController < Admin::AdminController
|
||||
|
||||
def show
|
||||
render json: { robots_txt: current_robots_txt, overridden: @overridden }
|
||||
end
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ScreenedEmailsController < Admin::StaffController
|
||||
|
||||
def index
|
||||
screened_emails = ScreenedEmail.limit(200).order('last_match_at desc').to_a
|
||||
screened_emails = ScreenedEmail.limit(200).order("last_match_at desc").to_a
|
||||
render_serialized(screened_emails, ScreenedEmailSerializer)
|
||||
end
|
||||
|
||||
|
@ -12,5 +11,4 @@ class Admin::ScreenedEmailsController < Admin::StaffController
|
|||
screen.destroy!
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ScreenedIpAddressesController < Admin::StaffController
|
||||
|
||||
before_action :fetch_screened_ip_address, only: [:update, :destroy]
|
||||
before_action :fetch_screened_ip_address, only: %i[update destroy]
|
||||
|
||||
def index
|
||||
filter = params[:filter]
|
||||
filter = IPAddr.handle_wildcards(filter)
|
||||
|
||||
screened_ip_addresses = ScreenedIpAddress
|
||||
screened_ip_addresses = screened_ip_addresses.where("cidr :filter >>= ip_address OR ip_address >>= cidr :filter", filter: filter) if filter.present?
|
||||
screened_ip_addresses = screened_ip_addresses.limit(200).order('match_count desc')
|
||||
screened_ip_addresses =
|
||||
screened_ip_addresses.where(
|
||||
"cidr :filter >>= ip_address OR ip_address >>= cidr :filter",
|
||||
filter: filter,
|
||||
) if filter.present?
|
||||
screened_ip_addresses = screened_ip_addresses.limit(200).order("match_count desc")
|
||||
|
||||
begin
|
||||
screened_ip_addresses = screened_ip_addresses.to_a
|
||||
|
@ -54,5 +57,4 @@ class Admin::ScreenedIpAddressesController < Admin::StaffController
|
|||
def fetch_screened_ip_address
|
||||
@screened_ip_address = ScreenedIpAddress.find(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ScreenedUrlsController < Admin::StaffController
|
||||
|
||||
def index
|
||||
screened_urls = ScreenedUrl.select("domain, sum(match_count) as match_count, max(last_match_at) as last_match_at, min(created_at) as created_at").group(:domain).order('last_match_at DESC').to_a
|
||||
screened_urls =
|
||||
ScreenedUrl
|
||||
.select(
|
||||
"domain, sum(match_count) as match_count, max(last_match_at) as last_match_at, min(created_at) as created_at",
|
||||
)
|
||||
.group(:domain)
|
||||
.order("last_match_at DESC")
|
||||
.to_a
|
||||
render_serialized(screened_urls, GroupedScreenedUrlSerializer)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SearchLogsController < Admin::StaffController
|
||||
|
||||
def index
|
||||
period = params[:period] || "all"
|
||||
search_type = params[:search_type] || "all"
|
||||
|
@ -22,5 +21,4 @@ class Admin::SearchLogsController < Admin::StaffController
|
|||
details[:search_result] = serialize_data(result, GroupedSearchResultSerializer, result: result)
|
||||
render_json_dump(term: details)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,10 +6,7 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
end
|
||||
|
||||
def index
|
||||
render_json_dump(
|
||||
site_settings: SiteSetting.all_settings,
|
||||
diags: SiteSetting.diags
|
||||
)
|
||||
render_json_dump(site_settings: SiteSetting.all_settings, diags: SiteSetting.diags)
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -18,10 +15,12 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
value = params[id]
|
||||
value.strip! if value.is_a?(String)
|
||||
|
||||
new_setting_name = SiteSettings::DeprecatedSettings::SETTINGS.find do |old_name, new_name, override, _|
|
||||
new_setting_name =
|
||||
SiteSettings::DeprecatedSettings::SETTINGS.find do |old_name, new_name, override, _|
|
||||
if old_name == id
|
||||
if !override
|
||||
raise Discourse::InvalidParameters, "You cannot change this site setting because it is deprecated, use #{new_name} instead."
|
||||
raise Discourse::InvalidParameters,
|
||||
"You cannot change this site setting because it is deprecated, use #{new_name} instead."
|
||||
end
|
||||
|
||||
break new_name
|
||||
|
@ -36,9 +35,7 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
value = Upload.get_from_urls(value.split("|")).to_a
|
||||
end
|
||||
|
||||
if SiteSetting.type_supervisor.get_type(id) == :upload
|
||||
value = Upload.get_from_url(value) || ""
|
||||
end
|
||||
value = Upload.get_from_url(value) || "" if SiteSetting.type_supervisor.get_type(id) == :upload
|
||||
|
||||
update_existing_users = params[:update_existing_user].present?
|
||||
previous_value = value_or_default(SiteSetting.public_send(id)) if update_existing_users
|
||||
|
@ -68,20 +65,38 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
notification_level = category_notification_level(id)
|
||||
|
||||
categories_to_unwatch = previous_category_ids - new_category_ids
|
||||
CategoryUser.where(category_id: categories_to_unwatch, notification_level: notification_level).delete_all
|
||||
CategoryUser.where(
|
||||
category_id: categories_to_unwatch,
|
||||
notification_level: notification_level,
|
||||
).delete_all
|
||||
TopicUser
|
||||
.joins(:topic)
|
||||
.where(notification_level: TopicUser.notification_levels[:watching],
|
||||
.where(
|
||||
notification_level: TopicUser.notification_levels[:watching],
|
||||
notifications_reason_id: TopicUser.notification_reasons[:auto_watch_category],
|
||||
topics: { category_id: categories_to_unwatch })
|
||||
topics: {
|
||||
category_id: categories_to_unwatch,
|
||||
},
|
||||
)
|
||||
.update_all(notification_level: TopicUser.notification_levels[:regular])
|
||||
|
||||
(new_category_ids - previous_category_ids).each do |category_id|
|
||||
skip_user_ids = CategoryUser.where(category_id: category_id).pluck(:user_id)
|
||||
|
||||
User.real.where(staged: false).where.not(id: skip_user_ids).select(:id).find_in_batches do |users|
|
||||
User
|
||||
.real
|
||||
.where(staged: false)
|
||||
.where.not(id: skip_user_ids)
|
||||
.select(:id)
|
||||
.find_in_batches do |users|
|
||||
category_users = []
|
||||
users.each { |user| category_users << { category_id: category_id, user_id: user.id, notification_level: notification_level } }
|
||||
users.each do |user|
|
||||
category_users << {
|
||||
category_id: category_id,
|
||||
user_id: user.id,
|
||||
notification_level: notification_level,
|
||||
}
|
||||
end
|
||||
CategoryUser.insert_all!(category_users)
|
||||
end
|
||||
end
|
||||
|
@ -92,19 +107,40 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
|
||||
notification_level = tag_notification_level(id)
|
||||
|
||||
TagUser.where(tag_id: (previous_tag_ids - new_tag_ids), notification_level: notification_level).delete_all
|
||||
TagUser.where(
|
||||
tag_id: (previous_tag_ids - new_tag_ids),
|
||||
notification_level: notification_level,
|
||||
).delete_all
|
||||
|
||||
(new_tag_ids - previous_tag_ids).each do |tag_id|
|
||||
skip_user_ids = TagUser.where(tag_id: tag_id).pluck(:user_id)
|
||||
|
||||
User.real.where(staged: false).where.not(id: skip_user_ids).select(:id).find_in_batches do |users|
|
||||
User
|
||||
.real
|
||||
.where(staged: false)
|
||||
.where.not(id: skip_user_ids)
|
||||
.select(:id)
|
||||
.find_in_batches do |users|
|
||||
tag_users = []
|
||||
users.each { |user| tag_users << { tag_id: tag_id, user_id: user.id, notification_level: notification_level, created_at: now, updated_at: now } }
|
||||
users.each do |user|
|
||||
tag_users << {
|
||||
tag_id: tag_id,
|
||||
user_id: user.id,
|
||||
notification_level: notification_level,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
end
|
||||
TagUser.insert_all!(tag_users)
|
||||
end
|
||||
end
|
||||
elsif is_sidebar_default_setting?(id)
|
||||
Jobs.enqueue(:backfill_sidebar_site_settings, setting_name: id, previous_value: previous_value, new_value: new_value)
|
||||
Jobs.enqueue(
|
||||
:backfill_sidebar_site_settings,
|
||||
setting_name: id,
|
||||
previous_value: previous_value,
|
||||
new_value: new_value,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -135,13 +171,24 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
|
||||
notification_level = category_notification_level(id)
|
||||
|
||||
user_ids = CategoryUser.where(category_id: previous_category_ids - new_category_ids, notification_level: notification_level).distinct.pluck(:user_id)
|
||||
user_ids += User
|
||||
user_ids =
|
||||
CategoryUser
|
||||
.where(
|
||||
category_id: previous_category_ids - new_category_ids,
|
||||
notification_level: notification_level,
|
||||
)
|
||||
.distinct
|
||||
.pluck(:user_id)
|
||||
user_ids +=
|
||||
User
|
||||
.real
|
||||
.joins("CROSS JOIN categories c")
|
||||
.joins("LEFT JOIN category_users cu ON users.id = cu.user_id AND c.id = cu.category_id")
|
||||
.where(staged: false)
|
||||
.where("c.id IN (?) AND cu.notification_level IS NULL", new_category_ids - previous_category_ids)
|
||||
.where(
|
||||
"c.id IN (?) AND cu.notification_level IS NULL",
|
||||
new_category_ids - previous_category_ids,
|
||||
)
|
||||
.distinct
|
||||
.pluck("users.id")
|
||||
|
||||
|
@ -152,8 +199,13 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
|
||||
notification_level = tag_notification_level(id)
|
||||
|
||||
user_ids = TagUser.where(tag_id: previous_tag_ids - new_tag_ids, notification_level: notification_level).distinct.pluck(:user_id)
|
||||
user_ids += User
|
||||
user_ids =
|
||||
TagUser
|
||||
.where(tag_id: previous_tag_ids - new_tag_ids, notification_level: notification_level)
|
||||
.distinct
|
||||
.pluck(:user_id)
|
||||
user_ids +=
|
||||
User
|
||||
.real
|
||||
.joins("CROSS JOIN tags t")
|
||||
.joins("LEFT JOIN tag_users tu ON users.id = tu.user_id AND t.id = tu.tag_id")
|
||||
|
@ -164,7 +216,11 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
|
||||
json[:user_count] = user_ids.uniq.count
|
||||
elsif is_sidebar_default_setting?(id)
|
||||
json[:user_count] = SidebarSiteSettingsBackfiller.new(id, previous_value: previous_value, new_value: new_value).number_of_users_to_backfill
|
||||
json[:user_count] = SidebarSiteSettingsBackfiller.new(
|
||||
id,
|
||||
previous_value: previous_value,
|
||||
new_value: new_value,
|
||||
).number_of_users_to_backfill
|
||||
end
|
||||
|
||||
render json: json
|
||||
|
@ -173,7 +229,7 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
private
|
||||
|
||||
def is_sidebar_default_setting?(setting_name)
|
||||
%w{default_sidebar_categories default_sidebar_tags}.include?(setting_name.to_s)
|
||||
%w[default_sidebar_categories default_sidebar_tags].include?(setting_name.to_s)
|
||||
end
|
||||
|
||||
def user_options
|
||||
|
@ -198,7 +254,7 @@ class Admin::SiteSettingsController < Admin::AdminController
|
|||
default_include_tl0_in_digests: "include_tl0_in_digests",
|
||||
default_text_size: "text_size_key",
|
||||
default_title_count_mode: "title_count_mode_key",
|
||||
default_hide_profile_and_presence: "hide_profile_and_presence"
|
||||
default_hide_profile_and_presence: "hide_profile_and_presence",
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SiteTextsController < Admin::AdminController
|
||||
|
||||
def self.preferred_keys
|
||||
['system_messages.usage_tips.text_body_template',
|
||||
'education.new-topic',
|
||||
'education.new-reply',
|
||||
'login_required.welcome_message']
|
||||
%w[
|
||||
system_messages.usage_tips.text_body_template
|
||||
education.new-topic
|
||||
education.new-reply
|
||||
login_required.welcome_message
|
||||
]
|
||||
end
|
||||
|
||||
def self.restricted_keys
|
||||
['user_notifications.confirm_old_email.title',
|
||||
'user_notifications.confirm_old_email.subject_template',
|
||||
'user_notifications.confirm_old_email.text_body_template']
|
||||
%w[
|
||||
user_notifications.confirm_old_email.title
|
||||
user_notifications.confirm_old_email.subject_template
|
||||
user_notifications.confirm_old_email.text_body_template
|
||||
]
|
||||
end
|
||||
|
||||
def index
|
||||
overridden = params[:overridden] == 'true'
|
||||
overridden = params[:overridden] == "true"
|
||||
extras = {}
|
||||
|
||||
query = params[:q] || ""
|
||||
|
@ -61,7 +64,7 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
render_serialized(
|
||||
results[first..last - 1],
|
||||
SiteTextSerializer,
|
||||
root: 'site_texts',
|
||||
root: "site_texts",
|
||||
rest_serializer: true,
|
||||
extras: extras,
|
||||
overridden_keys: overridden,
|
||||
|
@ -71,7 +74,7 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
def show
|
||||
locale = fetch_locale(params[:locale])
|
||||
site_text = find_site_text(locale)
|
||||
render_serialized(site_text, SiteTextSerializer, root: 'site_text', rest_serializer: true)
|
||||
render_serialized(site_text, SiteTextSerializer, root: "site_text", rest_serializer: true)
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -92,14 +95,14 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
:bulk_user_title_update,
|
||||
new_title: value,
|
||||
granted_badge_id: system_badge_id,
|
||||
action: Jobs::BulkUserTitleUpdate::UPDATE_ACTION
|
||||
action: Jobs::BulkUserTitleUpdate::UPDATE_ACTION,
|
||||
)
|
||||
end
|
||||
render_serialized(site_text, SiteTextSerializer, root: 'site_text', rest_serializer: true)
|
||||
render_serialized(site_text, SiteTextSerializer, root: "site_text", rest_serializer: true)
|
||||
else
|
||||
render json: failed_json.merge(
|
||||
message: translation_override.errors.full_messages.join("\n\n")
|
||||
), status: 422
|
||||
render json:
|
||||
failed_json.merge(message: translation_override.errors.full_messages.join("\n\n")),
|
||||
status: 422
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -118,31 +121,27 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
Jobs.enqueue(
|
||||
:bulk_user_title_update,
|
||||
granted_badge_id: system_badge_id,
|
||||
action: Jobs::BulkUserTitleUpdate::RESET_ACTION
|
||||
action: Jobs::BulkUserTitleUpdate::RESET_ACTION,
|
||||
)
|
||||
end
|
||||
render_serialized(site_text, SiteTextSerializer, root: 'site_text', rest_serializer: true)
|
||||
render_serialized(site_text, SiteTextSerializer, root: "site_text", rest_serializer: true)
|
||||
end
|
||||
|
||||
def get_reseed_options
|
||||
render_json_dump(
|
||||
categories: SeedData::Categories.with_default_locale.reseed_options,
|
||||
topics: SeedData::Topics.with_default_locale.reseed_options
|
||||
topics: SeedData::Topics.with_default_locale.reseed_options,
|
||||
)
|
||||
end
|
||||
|
||||
def reseed
|
||||
hijack do
|
||||
if params[:category_ids].present?
|
||||
SeedData::Categories.with_default_locale.update(
|
||||
site_setting_names: params[:category_ids]
|
||||
)
|
||||
SeedData::Categories.with_default_locale.update(site_setting_names: params[:category_ids])
|
||||
end
|
||||
|
||||
if params[:topic_ids].present?
|
||||
SeedData::Topics.with_default_locale.update(
|
||||
site_setting_names: params[:topic_ids]
|
||||
)
|
||||
SeedData::Topics.with_default_locale.update(site_setting_names: params[:topic_ids])
|
||||
end
|
||||
|
||||
render json: success_json
|
||||
|
@ -152,8 +151,8 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
protected
|
||||
|
||||
def is_badge_title?(id = "")
|
||||
badge_parts = id.split('.')
|
||||
badge_parts[0] == 'badges' && badge_parts[2] == 'name'
|
||||
badge_parts = id.split(".")
|
||||
badge_parts[0] == "badges" && badge_parts[2] == "name"
|
||||
end
|
||||
|
||||
def record_for(key:, value: nil, locale:)
|
||||
|
@ -165,10 +164,15 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
|
||||
def find_site_text(locale)
|
||||
if self.class.restricted_keys.include?(params[:id])
|
||||
raise Discourse::InvalidAccess.new(nil, nil, custom_message: 'email_template_cant_be_modified')
|
||||
raise Discourse::InvalidAccess.new(
|
||||
nil,
|
||||
nil,
|
||||
custom_message: "email_template_cant_be_modified",
|
||||
)
|
||||
end
|
||||
|
||||
if I18n.exists?(params[:id], locale) || TranslationOverride.exists?(locale: locale, translation_key: params[:id])
|
||||
if I18n.exists?(params[:id], locale) ||
|
||||
TranslationOverride.exists?(locale: locale, translation_key: params[:id])
|
||||
return record_for(key: params[:id], locale: locale)
|
||||
end
|
||||
|
||||
|
@ -182,9 +186,7 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
|
||||
def find_translations(query, overridden, locale)
|
||||
translations = Hash.new { |hash, key| hash[key] = {} }
|
||||
search_results = I18n.with_locale(locale) do
|
||||
I18n.search(query, only_overridden: overridden)
|
||||
end
|
||||
search_results = I18n.with_locale(locale) { I18n.search(query, only_overridden: overridden) }
|
||||
|
||||
search_results.each do |key, value|
|
||||
if PLURALIZED_REGEX.match(key)
|
||||
|
@ -205,7 +207,9 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
plural_value = plural[1]
|
||||
|
||||
results << record_for(
|
||||
key: "#{key}.#{plural_key}", value: plural_value, locale: plural.last
|
||||
key: "#{key}.#{plural_key}",
|
||||
value: plural_value,
|
||||
locale: plural.last,
|
||||
)
|
||||
end
|
||||
else
|
||||
|
@ -218,7 +222,7 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
|
||||
def fix_plural_keys(key, value, locale)
|
||||
value = value.with_indifferent_access
|
||||
plural_keys = I18n.with_locale(locale) { I18n.t('i18n.plural.keys') }
|
||||
plural_keys = I18n.with_locale(locale) { I18n.t("i18n.plural.keys") }
|
||||
return value if value.keys.size == plural_keys.size && plural_keys.all? { |k| value.key?(k) }
|
||||
|
||||
fallback_value = I18n.t(key, locale: :en, default: {})
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::StaffActionLogsController < Admin::StaffController
|
||||
|
||||
def index
|
||||
filters = params.slice(*UserHistory.staff_filters + [:page, :limit])
|
||||
filters = params.slice(*UserHistory.staff_filters + %i[page limit])
|
||||
|
||||
page = (params[:page] || 0).to_i
|
||||
page_size = (params[:limit] || 200).to_i.clamp(1, 200)
|
||||
|
@ -20,8 +19,8 @@ class Admin::StaffActionLogsController < Admin::StaffController
|
|||
total_rows_staff_action_logs: count,
|
||||
load_more_staff_action_logs: admin_staff_action_logs_path(load_more_params),
|
||||
extras: {
|
||||
user_history_actions: staff_available_actions
|
||||
}
|
||||
user_history_actions: staff_available_actions,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -37,16 +36,10 @@ class Admin::StaffActionLogsController < Admin::StaffController
|
|||
|
||||
output = +"<h2>#{CGI.escapeHTML(cur&.dig("name").to_s)}</h2><p></p>"
|
||||
|
||||
diff_fields["name"] = {
|
||||
prev: prev&.dig("name").to_s,
|
||||
cur: cur&.dig("name").to_s,
|
||||
}
|
||||
diff_fields["name"] = { prev: prev&.dig("name").to_s, cur: cur&.dig("name").to_s }
|
||||
|
||||
["default", "user_selectable"].each do |f|
|
||||
diff_fields[f] = {
|
||||
prev: (!!prev&.dig(f)).to_s,
|
||||
cur: (!!cur&.dig(f)).to_s
|
||||
}
|
||||
%w[default user_selectable].each do |f|
|
||||
diff_fields[f] = { prev: (!!prev&.dig(f)).to_s, cur: (!!cur&.dig(f)).to_s }
|
||||
end
|
||||
|
||||
diff_fields["color scheme"] = {
|
||||
|
@ -54,10 +47,7 @@ class Admin::StaffActionLogsController < Admin::StaffController
|
|||
cur: cur&.dig("color_scheme", "name").to_s,
|
||||
}
|
||||
|
||||
diff_fields["included themes"] = {
|
||||
prev: child_themes(prev),
|
||||
cur: child_themes(cur)
|
||||
}
|
||||
diff_fields["included themes"] = { prev: child_themes(prev), cur: child_themes(cur) }
|
||||
|
||||
load_diff(diff_fields, :cur, cur)
|
||||
load_diff(diff_fields, :prev, prev)
|
||||
|
@ -94,10 +84,7 @@ class Admin::StaffActionLogsController < Admin::StaffController
|
|||
|
||||
def staff_available_actions
|
||||
UserHistory.staff_actions.sort.map do |name|
|
||||
{
|
||||
id: name,
|
||||
action_id: UserHistory.actions[name] || UserHistory.actions[:custom_staff],
|
||||
}
|
||||
{ id: name, action_id: UserHistory.actions[name] || UserHistory.actions[:custom_staff] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'base64'
|
||||
require "base64"
|
||||
|
||||
class Admin::ThemesController < Admin::AdminController
|
||||
|
||||
skip_before_action :check_xhr, only: [:show, :preview, :export]
|
||||
skip_before_action :check_xhr, only: %i[show preview export]
|
||||
before_action :ensure_admin
|
||||
|
||||
def preview
|
||||
|
@ -15,7 +14,6 @@ class Admin::ThemesController < Admin::AdminController
|
|||
end
|
||||
|
||||
def upload_asset
|
||||
|
||||
ban_in_allowlist_mode!
|
||||
|
||||
path = params[:file].path
|
||||
|
@ -34,32 +32,30 @@ class Admin::ThemesController < Admin::AdminController
|
|||
end
|
||||
|
||||
def generate_key_pair
|
||||
require 'sshkey'
|
||||
require "sshkey"
|
||||
k = SSHKey.generate
|
||||
Discourse.redis.setex("ssh_key_#{k.ssh_public_key}", 1.hour, k.private_key)
|
||||
render json: { public_key: k.ssh_public_key }
|
||||
end
|
||||
|
||||
THEME_CONTENT_TYPES ||= %w{
|
||||
THEME_CONTENT_TYPES ||= %w[
|
||||
application/gzip
|
||||
application/x-gzip
|
||||
application/x-zip-compressed
|
||||
application/zip
|
||||
}
|
||||
]
|
||||
|
||||
def import
|
||||
@theme = nil
|
||||
if params[:theme] && params[:theme].content_type == "application/json"
|
||||
|
||||
ban_in_allowlist_mode!
|
||||
|
||||
# .dcstyle.json import. Deprecated, but still available to allow conversion
|
||||
json = JSON::parse(params[:theme].read)
|
||||
theme = json['theme']
|
||||
json = JSON.parse(params[:theme].read)
|
||||
theme = json["theme"]
|
||||
|
||||
@theme = Theme.new(name: theme["name"], user_id: theme_user.id, auto_update: false)
|
||||
theme["theme_fields"]&.each do |field|
|
||||
|
||||
if field["raw_upload"]
|
||||
begin
|
||||
tmp = Tempfile.new
|
||||
|
@ -79,7 +75,7 @@ class Admin::ThemesController < Admin::AdminController
|
|||
name: field["name"],
|
||||
value: field["value"],
|
||||
type_id: field["type_id"],
|
||||
upload_id: field["upload_id"]
|
||||
upload_id: field["upload_id"],
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -93,17 +89,22 @@ class Admin::ThemesController < Admin::AdminController
|
|||
begin
|
||||
guardian.ensure_allowed_theme_repo_import!(remote.strip)
|
||||
rescue Discourse::InvalidAccess
|
||||
render_json_error I18n.t("themes.import_error.not_allowed_theme", { repo: remote.strip }), status: :forbidden
|
||||
render_json_error I18n.t("themes.import_error.not_allowed_theme", { repo: remote.strip }),
|
||||
status: :forbidden
|
||||
return
|
||||
end
|
||||
|
||||
hijack do
|
||||
begin
|
||||
branch = params[:branch] ? params[:branch] : nil
|
||||
private_key = params[:public_key] ? Discourse.redis.get("ssh_key_#{params[:public_key]}") : nil
|
||||
return render_json_error I18n.t("themes.import_error.ssh_key_gone") if params[:public_key].present? && private_key.blank?
|
||||
private_key =
|
||||
params[:public_key] ? Discourse.redis.get("ssh_key_#{params[:public_key]}") : nil
|
||||
if params[:public_key].present? && private_key.blank?
|
||||
return render_json_error I18n.t("themes.import_error.ssh_key_gone")
|
||||
end
|
||||
|
||||
@theme = RemoteTheme.import_theme(remote, theme_user, private_key: private_key, branch: branch)
|
||||
@theme =
|
||||
RemoteTheme.import_theme(remote, theme_user, private_key: private_key, branch: branch)
|
||||
render json: @theme, status: :created
|
||||
rescue RemoteTheme::ImportError => e
|
||||
if params[:force]
|
||||
|
@ -125,8 +126,8 @@ class Admin::ThemesController < Admin::AdminController
|
|||
end
|
||||
end
|
||||
end
|
||||
elsif params[:bundle] || (params[:theme] && THEME_CONTENT_TYPES.include?(params[:theme].content_type))
|
||||
|
||||
elsif params[:bundle] ||
|
||||
(params[:theme] && THEME_CONTENT_TYPES.include?(params[:theme].content_type))
|
||||
ban_in_allowlist_mode!
|
||||
|
||||
# params[:bundle] used by theme CLI. params[:theme] used by admin UI
|
||||
|
@ -135,13 +136,14 @@ class Admin::ThemesController < Admin::AdminController
|
|||
update_components = params[:components]
|
||||
match_theme_by_name = !!params[:bundle] && !params.key?(:theme_id) # Old theme CLI behavior, match by name. Remove Jan 2020
|
||||
begin
|
||||
@theme = RemoteTheme.update_zipped_theme(
|
||||
@theme =
|
||||
RemoteTheme.update_zipped_theme(
|
||||
bundle.path,
|
||||
bundle.original_filename,
|
||||
match_theme: match_theme_by_name,
|
||||
user: theme_user,
|
||||
theme_id: theme_id,
|
||||
update_components: update_components
|
||||
update_components: update_components,
|
||||
)
|
||||
log_theme_change(nil, @theme)
|
||||
render json: @theme, status: :created
|
||||
|
@ -149,7 +151,8 @@ class Admin::ThemesController < Admin::AdminController
|
|||
render_json_error e.message
|
||||
end
|
||||
else
|
||||
render_json_error I18n.t("themes.import_error.unknown_file_type"), status: :unprocessable_entity
|
||||
render_json_error I18n.t("themes.import_error.unknown_file_type"),
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -160,24 +163,25 @@ class Admin::ThemesController < Admin::AdminController
|
|||
payload = {
|
||||
themes: ActiveModel::ArraySerializer.new(@themes, each_serializer: ThemeSerializer),
|
||||
extras: {
|
||||
color_schemes: ActiveModel::ArraySerializer.new(@color_schemes, each_serializer: ColorSchemeSerializer)
|
||||
}
|
||||
color_schemes:
|
||||
ActiveModel::ArraySerializer.new(@color_schemes, each_serializer: ColorSchemeSerializer),
|
||||
},
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.json { render json: payload }
|
||||
end
|
||||
respond_to { |format| format.json { render json: payload } }
|
||||
end
|
||||
|
||||
def create
|
||||
|
||||
ban_in_allowlist_mode!
|
||||
|
||||
@theme = Theme.new(name: theme_params[:name],
|
||||
@theme =
|
||||
Theme.new(
|
||||
name: theme_params[:name],
|
||||
user_id: theme_user.id,
|
||||
user_selectable: theme_params[:user_selectable] || false,
|
||||
color_scheme_id: theme_params[:color_scheme_id],
|
||||
component: [true, "true"].include?(theme_params[:component]))
|
||||
component: [true, "true"].include?(theme_params[:component]),
|
||||
)
|
||||
set_fields
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -199,10 +203,8 @@ class Admin::ThemesController < Admin::AdminController
|
|||
disables_component = [false, "false"].include?(theme_params[:enabled])
|
||||
enables_component = [true, "true"].include?(theme_params[:enabled])
|
||||
|
||||
[:name, :color_scheme_id, :user_selectable, :enabled, :auto_update].each do |field|
|
||||
if theme_params.key?(field)
|
||||
@theme.public_send("#{field}=", theme_params[field])
|
||||
end
|
||||
%i[name color_scheme_id user_selectable enabled auto_update].each do |field|
|
||||
@theme.public_send("#{field}=", theme_params[field]) if theme_params.key?(field)
|
||||
end
|
||||
|
||||
if theme_params.key?(:child_theme_ids)
|
||||
|
@ -218,13 +220,9 @@ class Admin::ThemesController < Admin::AdminController
|
|||
update_translations
|
||||
handle_switch
|
||||
|
||||
if params[:theme][:remote_check]
|
||||
@theme.remote_theme.update_remote_version
|
||||
end
|
||||
@theme.remote_theme.update_remote_version if params[:theme][:remote_check]
|
||||
|
||||
if params[:theme][:remote_update]
|
||||
@theme.remote_theme.update_from_remote
|
||||
end
|
||||
@theme.remote_theme.update_from_remote if params[:theme][:remote_update]
|
||||
|
||||
respond_to do |format|
|
||||
if @theme.save
|
||||
|
@ -245,7 +243,7 @@ class Admin::ThemesController < Admin::AdminController
|
|||
error = I18n.t("themes.bad_color_scheme") if @theme.errors[:color_scheme].present?
|
||||
error ||= I18n.t("themes.other_error")
|
||||
|
||||
render json: { errors: [ error ] }, status: :unprocessable_entity
|
||||
render json: { errors: [error] }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -260,9 +258,7 @@ class Admin::ThemesController < Admin::AdminController
|
|||
StaffActionLogger.new(current_user).log_theme_destroy(@theme)
|
||||
@theme.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.json { head :no_content }
|
||||
end
|
||||
respond_to { |format| format.json { head :no_content } }
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -279,7 +275,7 @@ class Admin::ThemesController < Admin::AdminController
|
|||
exporter = ThemeStore::ZipExporter.new(@theme)
|
||||
file_path = exporter.package_filename
|
||||
|
||||
headers['Content-Length'] = File.size(file_path).to_s
|
||||
headers["Content-Length"] = File.size(file_path).to_s
|
||||
send_data File.read(file_path),
|
||||
filename: File.basename(file_path),
|
||||
content_type: "application/zip"
|
||||
|
@ -330,9 +326,7 @@ class Admin::ThemesController < Admin::AdminController
|
|||
end
|
||||
end
|
||||
|
||||
Theme.where(id: expected).each do |theme|
|
||||
@theme.add_relative_theme!(kind, theme)
|
||||
end
|
||||
Theme.where(id: expected).each { |theme| @theme.add_relative_theme!(kind, theme) }
|
||||
end
|
||||
|
||||
def update_default_theme
|
||||
|
@ -361,11 +355,13 @@ class Admin::ThemesController < Admin::AdminController
|
|||
:component,
|
||||
:enabled,
|
||||
:auto_update,
|
||||
settings: {},
|
||||
translations: {},
|
||||
theme_fields: [:name, :target, :value, :upload_id, :type_id],
|
||||
settings: {
|
||||
},
|
||||
translations: {
|
||||
},
|
||||
theme_fields: %i[name target value upload_id type_id],
|
||||
child_theme_ids: [],
|
||||
parent_theme_ids: []
|
||||
parent_theme_ids: [],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -382,7 +378,7 @@ class Admin::ThemesController < Admin::AdminController
|
|||
name: field[:name],
|
||||
value: field[:value],
|
||||
type_id: field[:type_id],
|
||||
upload_id: field[:upload_id]
|
||||
upload_id: field[:upload_id],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -408,7 +404,12 @@ class Admin::ThemesController < Admin::AdminController
|
|||
end
|
||||
|
||||
def log_theme_setting_change(setting_name, previous_value, new_value)
|
||||
StaffActionLogger.new(current_user).log_theme_setting_change(setting_name, previous_value, new_value, @theme)
|
||||
StaffActionLogger.new(current_user).log_theme_setting_change(
|
||||
setting_name,
|
||||
previous_value,
|
||||
new_value,
|
||||
@theme,
|
||||
)
|
||||
end
|
||||
|
||||
def log_theme_component_disabled
|
||||
|
@ -422,10 +423,14 @@ class Admin::ThemesController < Admin::AdminController
|
|||
def handle_switch
|
||||
param = theme_params[:component]
|
||||
if param.to_s == "false" && @theme.component?
|
||||
raise Discourse::InvalidParameters.new(:component) if @theme.id == SiteSetting.default_theme_id
|
||||
if @theme.id == SiteSetting.default_theme_id
|
||||
raise Discourse::InvalidParameters.new(:component)
|
||||
end
|
||||
@theme.switch_to_theme!
|
||||
elsif param.to_s == "true" && !@theme.component?
|
||||
raise Discourse::InvalidParameters.new(:component) if @theme.id == SiteSetting.default_theme_id
|
||||
if @theme.id == SiteSetting.default_theme_id
|
||||
raise Discourse::InvalidParameters.new(:component)
|
||||
end
|
||||
@theme.switch_to_component!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::UserFieldsController < Admin::AdminController
|
||||
|
||||
def self.columns
|
||||
%i(name field_type editable description required show_on_profile show_on_user_card position searchable)
|
||||
%i[
|
||||
name
|
||||
field_type
|
||||
editable
|
||||
description
|
||||
required
|
||||
show_on_profile
|
||||
show_on_user_card
|
||||
position
|
||||
searchable
|
||||
]
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -13,14 +22,12 @@ class Admin::UserFieldsController < Admin::AdminController
|
|||
field.required = params[:user_field][:required] == "true"
|
||||
update_options(field)
|
||||
|
||||
json_result(field, serializer: UserFieldSerializer) do
|
||||
field.save
|
||||
end
|
||||
json_result(field, serializer: UserFieldSerializer) { field.save }
|
||||
end
|
||||
|
||||
def index
|
||||
user_fields = UserField.all.includes(:user_field_options).order(:position)
|
||||
render_serialized(user_fields, UserFieldSerializer, root: 'user_fields')
|
||||
render_serialized(user_fields, UserFieldSerializer, root: "user_fields")
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -28,9 +35,7 @@ class Admin::UserFieldsController < Admin::AdminController
|
|||
field = UserField.where(id: params.require(:id)).first
|
||||
|
||||
Admin::UserFieldsController.columns.each do |col|
|
||||
unless field_params[col].nil?
|
||||
field.public_send("#{col}=", field_params[col])
|
||||
end
|
||||
field.public_send("#{col}=", field_params[col]) unless field_params[col].nil?
|
||||
end
|
||||
update_options(field)
|
||||
|
||||
|
@ -38,7 +43,7 @@ class Admin::UserFieldsController < Admin::AdminController
|
|||
if !field.show_on_profile && !field.show_on_user_card
|
||||
DirectoryColumn.where(user_field_id: field.id).destroy_all
|
||||
end
|
||||
render_serialized(field, UserFieldSerializer, root: 'user_field')
|
||||
render_serialized(field, UserFieldSerializer, root: "user_field")
|
||||
else
|
||||
render_json_error(field)
|
||||
end
|
||||
|
|
|
@ -3,28 +3,31 @@
|
|||
class Admin::UsersController < Admin::StaffController
|
||||
MAX_SIMILAR_USERS = 10
|
||||
|
||||
before_action :fetch_user, only: [:suspend,
|
||||
:unsuspend,
|
||||
:log_out,
|
||||
:revoke_admin,
|
||||
:revoke_moderation,
|
||||
:grant_moderation,
|
||||
:approve,
|
||||
:activate,
|
||||
:deactivate,
|
||||
:silence,
|
||||
:unsilence,
|
||||
:trust_level,
|
||||
:trust_level_lock,
|
||||
:add_group,
|
||||
:remove_group,
|
||||
:primary_group,
|
||||
:anonymize,
|
||||
:merge,
|
||||
:reset_bounce_score,
|
||||
:disable_second_factor,
|
||||
:delete_posts_batch,
|
||||
:sso_record]
|
||||
before_action :fetch_user,
|
||||
only: %i[
|
||||
suspend
|
||||
unsuspend
|
||||
log_out
|
||||
revoke_admin
|
||||
revoke_moderation
|
||||
grant_moderation
|
||||
approve
|
||||
activate
|
||||
deactivate
|
||||
silence
|
||||
unsilence
|
||||
trust_level
|
||||
trust_level_lock
|
||||
add_group
|
||||
remove_group
|
||||
primary_group
|
||||
anonymize
|
||||
merge
|
||||
reset_bounce_score
|
||||
disable_second_factor
|
||||
delete_posts_batch
|
||||
sso_record
|
||||
]
|
||||
|
||||
def index
|
||||
users = ::AdminUserIndexQuery.new(params).find_users
|
||||
|
@ -42,9 +45,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
@user = User.find_by(id: params[:id])
|
||||
raise Discourse::NotFound unless @user
|
||||
|
||||
similar_users = User.real
|
||||
.where.not(id: @user.id)
|
||||
.where(ip_address: @user.ip_address)
|
||||
similar_users = User.real.where.not(id: @user.id).where(ip_address: @user.ip_address)
|
||||
|
||||
render_serialized(
|
||||
@user,
|
||||
|
@ -64,7 +65,6 @@ class Admin::UsersController < Admin::StaffController
|
|||
|
||||
# DELETE action to delete penalty history for a user
|
||||
def penalty_history
|
||||
|
||||
# We don't delete any history, we merely remove the action type
|
||||
# with a removed type. It can still be viewed in the logs but
|
||||
# will not affect TL3 promotions.
|
||||
|
@ -87,7 +87,9 @@ class Admin::UsersController < Admin::StaffController
|
|||
|
||||
DB.exec(
|
||||
sql,
|
||||
UserHistory.actions.slice(
|
||||
UserHistory
|
||||
.actions
|
||||
.slice(
|
||||
:silence_user,
|
||||
:suspend_user,
|
||||
:unsilence_user,
|
||||
|
@ -95,8 +97,9 @@ class Admin::UsersController < Admin::StaffController
|
|||
:removed_silence_user,
|
||||
:removed_unsilence_user,
|
||||
:removed_suspend_user,
|
||||
:removed_unsuspend_user
|
||||
).merge(user_id: params[:user_id].to_i)
|
||||
:removed_unsuspend_user,
|
||||
)
|
||||
.merge(user_id: params[:user_id].to_i),
|
||||
)
|
||||
|
||||
render json: success_json
|
||||
|
@ -107,14 +110,21 @@ class Admin::UsersController < Admin::StaffController
|
|||
|
||||
if @user.suspended?
|
||||
suspend_record = @user.suspend_record
|
||||
message = I18n.t("user.already_suspended",
|
||||
message =
|
||||
I18n.t(
|
||||
"user.already_suspended",
|
||||
staff: suspend_record.acting_user.username,
|
||||
time_ago: FreedomPatches::Rails4.time_ago_in_words(suspend_record.created_at, true, scope: :'datetime.distance_in_words_verbose')
|
||||
time_ago:
|
||||
FreedomPatches::Rails4.time_ago_in_words(
|
||||
suspend_record.created_at,
|
||||
true,
|
||||
scope: :"datetime.distance_in_words_verbose",
|
||||
),
|
||||
)
|
||||
return render json: failed_json.merge(message: message), status: 409
|
||||
end
|
||||
|
||||
params.require([:suspend_until, :reason])
|
||||
params.require(%i[suspend_until reason])
|
||||
|
||||
all_users = [@user]
|
||||
if Array === params[:other_user_ids]
|
||||
|
@ -133,11 +143,12 @@ class Admin::UsersController < Admin::StaffController
|
|||
User.transaction do
|
||||
user.save!
|
||||
|
||||
user_history = StaffActionLogger.new(current_user).log_user_suspend(
|
||||
user_history =
|
||||
StaffActionLogger.new(current_user).log_user_suspend(
|
||||
user,
|
||||
params[:reason],
|
||||
message: message,
|
||||
post_id: params[:post_id]
|
||||
post_id: params[:post_id],
|
||||
)
|
||||
end
|
||||
user.logged_out
|
||||
|
@ -147,7 +158,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
:critical_user_email,
|
||||
type: "account_suspended",
|
||||
user_id: user.id,
|
||||
user_history_id: user_history.id
|
||||
user_history_id: user_history.id,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -159,7 +170,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
user_history: user_history,
|
||||
post_id: params[:post_id],
|
||||
suspended_till: params[:suspend_until],
|
||||
suspended_at: DateTime.now
|
||||
suspended_at: DateTime.now,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -171,8 +182,8 @@ class Admin::UsersController < Admin::StaffController
|
|||
full_suspend_reason: user_history.try(:details),
|
||||
suspended_till: @user.suspended_till,
|
||||
suspended_at: @user.suspended_at,
|
||||
suspended_by: BasicUserSerializer.new(current_user, root: false).as_json
|
||||
}
|
||||
suspended_by: BasicUserSerializer.new(current_user, root: false).as_json,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -185,12 +196,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
|
||||
DiscourseEvent.trigger(:user_unsuspended, user: @user)
|
||||
|
||||
render_json_dump(
|
||||
suspension: {
|
||||
suspended_till: nil,
|
||||
suspended_at: nil
|
||||
}
|
||||
)
|
||||
render_json_dump(suspension: { suspended_till: nil, suspended_at: nil })
|
||||
end
|
||||
|
||||
def log_out
|
||||
|
@ -199,7 +205,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
@user.logged_out
|
||||
render json: success_json
|
||||
else
|
||||
render json: { error: I18n.t('admin_js.admin.users.id_not_found') }, status: 404
|
||||
render json: { error: I18n.t("admin_js.admin.users.id_not_found") }, status: 404
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -237,7 +243,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
group = Group.find(params[:group_id].to_i)
|
||||
raise Discourse::NotFound unless group
|
||||
|
||||
return render_json_error(I18n.t('groups.errors.can_not_modify_automatic')) if group.automatic
|
||||
return render_json_error(I18n.t("groups.errors.can_not_modify_automatic")) if group.automatic
|
||||
guardian.ensure_can_edit!(group)
|
||||
|
||||
group.add(@user)
|
||||
|
@ -250,7 +256,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
group = Group.find(params[:group_id].to_i)
|
||||
raise Discourse::NotFound unless group
|
||||
|
||||
return render_json_error(I18n.t('groups.errors.can_not_modify_automatic')) if group.automatic
|
||||
return render_json_error(I18n.t("groups.errors.can_not_modify_automatic")) if group.automatic
|
||||
guardian.ensure_can_edit!(group)
|
||||
|
||||
if group.remove(@user)
|
||||
|
@ -266,9 +272,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
if group = Group.find(primary_group_id)
|
||||
guardian.ensure_can_change_primary_group!(@user, group)
|
||||
|
||||
if group.user_ids.include?(@user.id)
|
||||
@user.primary_group_id = primary_group_id
|
||||
end
|
||||
@user.primary_group_id = primary_group_id if group.user_ids.include?(@user.id)
|
||||
end
|
||||
else
|
||||
@user.primary_group_id = nil
|
||||
|
@ -304,9 +308,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
guardian.ensure_can_change_trust_level!(@user)
|
||||
|
||||
new_lock = params[:locked].to_s
|
||||
unless new_lock =~ /true|false/
|
||||
return render_json_error I18n.t('errors.invalid_boolean')
|
||||
end
|
||||
return render_json_error I18n.t("errors.invalid_boolean") unless new_lock =~ /true|false/
|
||||
|
||||
@user.manual_locked_trust_level = (new_lock == "true") ? @user.trust_level : nil
|
||||
@user.save
|
||||
|
@ -320,7 +322,8 @@ class Admin::UsersController < Admin::StaffController
|
|||
def approve
|
||||
guardian.ensure_can_approve!(@user)
|
||||
|
||||
reviewable = ReviewableUser.find_by(target: @user) ||
|
||||
reviewable =
|
||||
ReviewableUser.find_by(target: @user) ||
|
||||
Jobs::CreateUserReviewable.new.execute(user_id: @user.id).reviewable
|
||||
|
||||
reviewable.perform(current_user, :approve_user)
|
||||
|
@ -328,23 +331,29 @@ class Admin::UsersController < Admin::StaffController
|
|||
end
|
||||
|
||||
def approve_bulk
|
||||
Reviewable.bulk_perform_targets(current_user, :approve_user, 'ReviewableUser', params[:users])
|
||||
Reviewable.bulk_perform_targets(current_user, :approve_user, "ReviewableUser", params[:users])
|
||||
render body: nil
|
||||
end
|
||||
|
||||
def activate
|
||||
guardian.ensure_can_activate!(@user)
|
||||
# ensure there is an active email token
|
||||
@user.email_tokens.create!(email: @user.email, scope: EmailToken.scopes[:signup]) if !@user.email_tokens.active.exists?
|
||||
if !@user.email_tokens.active.exists?
|
||||
@user.email_tokens.create!(email: @user.email, scope: EmailToken.scopes[:signup])
|
||||
end
|
||||
@user.activate
|
||||
StaffActionLogger.new(current_user).log_user_activate(@user, I18n.t('user.activated_by_staff'))
|
||||
StaffActionLogger.new(current_user).log_user_activate(@user, I18n.t("user.activated_by_staff"))
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
def deactivate
|
||||
guardian.ensure_can_deactivate!(@user)
|
||||
@user.deactivate(current_user)
|
||||
StaffActionLogger.new(current_user).log_user_deactivate(@user, I18n.t('user.deactivated_by_staff'), params.slice(:context))
|
||||
StaffActionLogger.new(current_user).log_user_deactivate(
|
||||
@user,
|
||||
I18n.t("user.deactivated_by_staff"),
|
||||
params.slice(:context),
|
||||
)
|
||||
refresh_browser @user
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -354,9 +363,16 @@ class Admin::UsersController < Admin::StaffController
|
|||
|
||||
if @user.silenced?
|
||||
silenced_record = @user.silenced_record
|
||||
message = I18n.t("user.already_silenced",
|
||||
message =
|
||||
I18n.t(
|
||||
"user.already_silenced",
|
||||
staff: silenced_record.acting_user.username,
|
||||
time_ago: FreedomPatches::Rails4.time_ago_in_words(silenced_record.created_at, true, scope: :'datetime.distance_in_words_verbose')
|
||||
time_ago:
|
||||
FreedomPatches::Rails4.time_ago_in_words(
|
||||
silenced_record.created_at,
|
||||
true,
|
||||
scope: :"datetime.distance_in_words_verbose",
|
||||
),
|
||||
)
|
||||
return render json: failed_json.merge(message: message), status: 409
|
||||
end
|
||||
|
@ -370,14 +386,15 @@ class Admin::UsersController < Admin::StaffController
|
|||
user_history = nil
|
||||
|
||||
all_users.each do |user|
|
||||
silencer = UserSilencer.new(
|
||||
silencer =
|
||||
UserSilencer.new(
|
||||
user,
|
||||
current_user,
|
||||
silenced_till: params[:silenced_till],
|
||||
reason: params[:reason],
|
||||
message_body: params[:message],
|
||||
keep_posts: true,
|
||||
post_id: params[:post_id]
|
||||
post_id: params[:post_id],
|
||||
)
|
||||
|
||||
if silencer.silence
|
||||
|
@ -386,7 +403,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
:critical_user_email,
|
||||
type: "account_silenced",
|
||||
user_id: user.id,
|
||||
user_history_id: user_history.id
|
||||
user_history_id: user_history.id,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -399,8 +416,8 @@ class Admin::UsersController < Admin::StaffController
|
|||
silence_reason: user_history.try(:details),
|
||||
silenced_till: @user.silenced_till,
|
||||
silenced_at: @user.silenced_at,
|
||||
silenced_by: BasicUserSerializer.new(current_user, root: false).as_json
|
||||
}
|
||||
silenced_by: BasicUserSerializer.new(current_user, root: false).as_json,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -413,8 +430,8 @@ class Admin::UsersController < Admin::StaffController
|
|||
silenced: false,
|
||||
silence_reason: nil,
|
||||
silenced_till: nil,
|
||||
silenced_at: nil
|
||||
}
|
||||
silenced_at: nil,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -428,11 +445,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
user_security_key.destroy_all
|
||||
StaffActionLogger.new(current_user).log_disable_second_factor_auth(@user)
|
||||
|
||||
Jobs.enqueue(
|
||||
:critical_user_email,
|
||||
type: "account_second_factor_disabled",
|
||||
user_id: @user.id
|
||||
)
|
||||
Jobs.enqueue(:critical_user_email, type: "account_second_factor_disabled", user_id: @user.id)
|
||||
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -442,7 +455,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
guardian.ensure_can_delete_user!(user)
|
||||
|
||||
options = params.slice(:context, :delete_as_spammer)
|
||||
[:delete_posts, :block_email, :block_urls, :block_ip].each do |param_name|
|
||||
%i[delete_posts block_email block_urls block_ip].each do |param_name|
|
||||
options[param_name] = ActiveModel::Type::Boolean.new.cast(params[param_name])
|
||||
end
|
||||
options[:prepare_for_destroy] = true
|
||||
|
@ -454,14 +467,20 @@ class Admin::UsersController < Admin::StaffController
|
|||
else
|
||||
render json: {
|
||||
deleted: false,
|
||||
user: AdminDetailedUserSerializer.new(user, root: false).as_json
|
||||
user: AdminDetailedUserSerializer.new(user, root: false).as_json,
|
||||
}
|
||||
end
|
||||
rescue UserDestroyer::PostsExistError
|
||||
render json: {
|
||||
deleted: false,
|
||||
message: I18n.t("user.cannot_delete_has_posts", username: user.username, count: user.posts.joins(:topic).count),
|
||||
}, status: 403
|
||||
message:
|
||||
I18n.t(
|
||||
"user.cannot_delete_has_posts",
|
||||
username: user.username,
|
||||
count: user.posts.joins(:topic).count,
|
||||
),
|
||||
},
|
||||
status: 403
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -482,9 +501,16 @@ class Admin::UsersController < Admin::StaffController
|
|||
return render body: nil, status: 404 unless SiteSetting.enable_discourse_connect
|
||||
|
||||
begin
|
||||
sso = DiscourseConnect.parse("sso=#{params[:sso]}&sig=#{params[:sig]}", secure_session: secure_session)
|
||||
sso =
|
||||
DiscourseConnect.parse(
|
||||
"sso=#{params[:sso]}&sig=#{params[:sig]}",
|
||||
secure_session: secure_session,
|
||||
)
|
||||
rescue DiscourseConnect::ParseError
|
||||
return render json: failed_json.merge(message: I18n.t("discourse_connect.login_error")), status: 422
|
||||
return(
|
||||
render json: failed_json.merge(message: I18n.t("discourse_connect.login_error")),
|
||||
status: 422
|
||||
)
|
||||
end
|
||||
|
||||
begin
|
||||
|
@ -494,7 +520,8 @@ class Admin::UsersController < Admin::StaffController
|
|||
rescue ActiveRecord::RecordInvalid => ex
|
||||
render json: failed_json.merge(message: ex.message), status: 403
|
||||
rescue DiscourseConnect::BlankExternalId => ex
|
||||
render json: failed_json.merge(message: I18n.t('discourse_connect.blank_id_error')), status: 422
|
||||
render json: failed_json.merge(message: I18n.t("discourse_connect.blank_id_error")),
|
||||
status: 422
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -510,12 +537,13 @@ class Admin::UsersController < Admin::StaffController
|
|||
block_urls: true,
|
||||
block_ip: true,
|
||||
delete_as_spammer: true,
|
||||
context: I18n.t("user.destroy_reasons.same_ip_address", ip_address: params[:ip])
|
||||
context: I18n.t("user.destroy_reasons.same_ip_address", ip_address: params[:ip]),
|
||||
}
|
||||
|
||||
AdminUserIndexQuery.new(params).find_users(50).each do |user|
|
||||
user_destroyer.destroy(user, options)
|
||||
end
|
||||
AdminUserIndexQuery
|
||||
.new(params)
|
||||
.find_users(50)
|
||||
.each { |user| user_destroyer.destroy(user, options) }
|
||||
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -536,7 +564,8 @@ class Admin::UsersController < Admin::StaffController
|
|||
if user = UserAnonymizer.new(@user, current_user, opts).make_anonymous
|
||||
render json: success_json.merge(username: user.username)
|
||||
else
|
||||
render json: failed_json.merge(user: AdminDetailedUserSerializer.new(user, root: false).as_json)
|
||||
render json:
|
||||
failed_json.merge(user: AdminDetailedUserSerializer.new(user, root: false).as_json)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -547,7 +576,12 @@ class Admin::UsersController < Admin::StaffController
|
|||
|
||||
guardian.ensure_can_merge_users!(@user, target_user)
|
||||
|
||||
Jobs.enqueue(:merge_user, user_id: @user.id, target_user_id: target_user.id, current_user_id: current_user.id)
|
||||
Jobs.enqueue(
|
||||
:merge_user,
|
||||
user_id: @user.id,
|
||||
target_user_id: target_user.id,
|
||||
current_user_id: current_user.id,
|
||||
)
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
|
@ -566,16 +600,17 @@ class Admin::UsersController < Admin::StaffController
|
|||
private
|
||||
|
||||
def perform_post_action
|
||||
return unless params[:post_id].present? &&
|
||||
params[:post_action].present?
|
||||
return unless params[:post_id].present? && params[:post_action].present?
|
||||
|
||||
if post = Post.where(id: params[:post_id]).first
|
||||
case params[:post_action]
|
||||
when 'delete'
|
||||
when "delete"
|
||||
PostDestroyer.new(current_user, post).destroy if guardian.can_delete_post_or_topic?(post)
|
||||
when "delete_replies"
|
||||
PostDestroyer.delete_with_replies(current_user, post) if guardian.can_delete_post_or_topic?(post)
|
||||
when 'edit'
|
||||
if guardian.can_delete_post_or_topic?(post)
|
||||
PostDestroyer.delete_with_replies(current_user, post)
|
||||
end
|
||||
when "edit"
|
||||
revisor = PostRevisor.new(post)
|
||||
|
||||
# Take what the moderator edited in as gospel
|
||||
|
@ -583,7 +618,7 @@ class Admin::UsersController < Admin::StaffController
|
|||
current_user,
|
||||
{ raw: params[:post_edit] },
|
||||
skip_validations: true,
|
||||
skip_revision: true
|
||||
skip_revision: true,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -597,5 +632,4 @@ class Admin::UsersController < Admin::StaffController
|
|||
def refresh_browser(user)
|
||||
MessageBus.publish "/file-change", ["refresh"], user_ids: [user.id]
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
require "csv"
|
||||
|
||||
class Admin::WatchedWordsController < Admin::StaffController
|
||||
skip_before_action :check_xhr, only: [:download]
|
||||
|
||||
def index
|
||||
watched_words = WatchedWord.by_action
|
||||
watched_words = watched_words.where.not(action: WatchedWord.actions[:tag]) if !SiteSetting.tagging_enabled
|
||||
watched_words =
|
||||
watched_words.where.not(action: WatchedWord.actions[:tag]) if !SiteSetting.tagging_enabled
|
||||
render_json_dump WatchedWordListSerializer.new(watched_words, scope: guardian, root: false)
|
||||
end
|
||||
|
||||
|
@ -38,11 +39,12 @@ class Admin::WatchedWordsController < Admin::StaffController
|
|||
begin
|
||||
CSV.foreach(file.tempfile, encoding: "bom|utf-8") do |row|
|
||||
if row[0].present? && (!has_replacement || row[1].present?)
|
||||
watched_word = WatchedWord.create_or_update_word(
|
||||
watched_word =
|
||||
WatchedWord.create_or_update_word(
|
||||
word: row[0],
|
||||
replacement: has_replacement ? row[1] : nil,
|
||||
action_key: action_key,
|
||||
case_sensitive: "true" == row[2]&.strip&.downcase
|
||||
case_sensitive: "true" == row[2]&.strip&.downcase,
|
||||
)
|
||||
if watched_word.valid?
|
||||
StaffActionLogger.new(current_user).log_watched_words_creation(watched_word)
|
||||
|
@ -50,7 +52,7 @@ class Admin::WatchedWordsController < Admin::StaffController
|
|||
end
|
||||
end
|
||||
|
||||
data = { url: '/ok' }
|
||||
data = { url: "/ok" }
|
||||
rescue => e
|
||||
data = failed_json.merge(errors: [e.message])
|
||||
end
|
||||
|
@ -73,7 +75,7 @@ class Admin::WatchedWordsController < Admin::StaffController
|
|||
content = content.pluck(:word).join("\n")
|
||||
end
|
||||
|
||||
headers['Content-Length'] = content.bytesize.to_s
|
||||
headers["Content-Length"] = content.bytesize.to_s
|
||||
send_data content,
|
||||
filename: "#{Discourse.current_hostname}-watched-words-#{name}.csv",
|
||||
content_type: "text/csv"
|
||||
|
@ -85,7 +87,9 @@ class Admin::WatchedWordsController < Admin::StaffController
|
|||
action = WatchedWord.actions[name]
|
||||
raise Discourse::NotFound if !action
|
||||
|
||||
WatchedWord.where(action: action).find_each do |watched_word|
|
||||
WatchedWord
|
||||
.where(action: action)
|
||||
.find_each do |watched_word|
|
||||
watched_word.destroy!
|
||||
StaffActionLogger.new(current_user).log_watched_words_deletion(watched_word)
|
||||
end
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::WebHooksController < Admin::AdminController
|
||||
before_action :fetch_web_hook, only: %i(show update destroy list_events bulk_events ping)
|
||||
before_action :fetch_web_hook, only: %i[show update destroy list_events bulk_events ping]
|
||||
|
||||
def index
|
||||
limit = 50
|
||||
offset = params[:offset].to_i
|
||||
|
||||
web_hooks = WebHook.limit(limit)
|
||||
web_hooks =
|
||||
WebHook
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.includes(:web_hook_event_types)
|
||||
.includes(:categories)
|
||||
|
@ -19,29 +21,34 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
event_types: WebHookEventType.active,
|
||||
default_event_types: WebHook.default_event_types,
|
||||
content_types: WebHook.content_types.map { |name, id| { id: id, name: name } },
|
||||
delivery_statuses: WebHook.last_delivery_statuses.map { |name, id| { id: id, name: name.to_s } },
|
||||
delivery_statuses:
|
||||
WebHook.last_delivery_statuses.map { |name, id| { id: id, name: name.to_s } },
|
||||
},
|
||||
total_rows_web_hooks: WebHook.count,
|
||||
load_more_web_hooks: admin_web_hooks_path(limit: limit, offset: offset + limit, format: :json)
|
||||
load_more_web_hooks:
|
||||
admin_web_hooks_path(limit: limit, offset: offset + limit, format: :json),
|
||||
}
|
||||
|
||||
render json: MultiJson.dump(json), status: 200
|
||||
end
|
||||
|
||||
def show
|
||||
render_serialized(@web_hook, AdminWebHookSerializer, root: 'web_hook')
|
||||
render_serialized(@web_hook, AdminWebHookSerializer, root: "web_hook")
|
||||
end
|
||||
|
||||
def edit
|
||||
render_serialized(@web_hook, AdminWebHookSerializer, root: 'web_hook')
|
||||
render_serialized(@web_hook, AdminWebHookSerializer, root: "web_hook")
|
||||
end
|
||||
|
||||
def create
|
||||
web_hook = WebHook.new(web_hook_params)
|
||||
|
||||
if web_hook.save
|
||||
StaffActionLogger.new(current_user).log_web_hook(web_hook, UserHistory.actions[:web_hook_create])
|
||||
render_serialized(web_hook, AdminWebHookSerializer, root: 'web_hook')
|
||||
StaffActionLogger.new(current_user).log_web_hook(
|
||||
web_hook,
|
||||
UserHistory.actions[:web_hook_create],
|
||||
)
|
||||
render_serialized(web_hook, AdminWebHookSerializer, root: "web_hook")
|
||||
else
|
||||
render_json_error web_hook.errors.full_messages
|
||||
end
|
||||
|
@ -49,8 +56,12 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
|
||||
def update
|
||||
if @web_hook.update(web_hook_params)
|
||||
StaffActionLogger.new(current_user).log_web_hook(@web_hook, UserHistory.actions[:web_hook_update], changes: @web_hook.saved_changes)
|
||||
render_serialized(@web_hook, AdminWebHookSerializer, root: 'web_hook')
|
||||
StaffActionLogger.new(current_user).log_web_hook(
|
||||
@web_hook,
|
||||
UserHistory.actions[:web_hook_update],
|
||||
changes: @web_hook.saved_changes,
|
||||
)
|
||||
render_serialized(@web_hook, AdminWebHookSerializer, root: "web_hook")
|
||||
else
|
||||
render_json_error @web_hook.errors.full_messages
|
||||
end
|
||||
|
@ -58,7 +69,10 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
|
||||
def destroy
|
||||
@web_hook.destroy!
|
||||
StaffActionLogger.new(current_user).log_web_hook(@web_hook, UserHistory.actions[:web_hook_destroy])
|
||||
StaffActionLogger.new(current_user).log_web_hook(
|
||||
@web_hook,
|
||||
UserHistory.actions[:web_hook_destroy],
|
||||
)
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
|
@ -67,12 +81,17 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
offset = params[:offset].to_i
|
||||
|
||||
json = {
|
||||
web_hook_events: serialize_data(@web_hook.web_hook_events.limit(limit).offset(offset), AdminWebHookEventSerializer),
|
||||
web_hook_events:
|
||||
serialize_data(
|
||||
@web_hook.web_hook_events.limit(limit).offset(offset),
|
||||
AdminWebHookEventSerializer,
|
||||
),
|
||||
total_rows_web_hook_events: @web_hook.web_hook_events.count,
|
||||
load_more_web_hook_events: web_hook_events_admin_api_index_path(limit: limit, offset: offset + limit, format: :json),
|
||||
load_more_web_hook_events:
|
||||
web_hook_events_admin_api_index_path(limit: limit, offset: offset + limit, format: :json),
|
||||
extras: {
|
||||
web_hook_id: @web_hook.id
|
||||
}
|
||||
web_hook_id: @web_hook.id,
|
||||
},
|
||||
}
|
||||
|
||||
render json: MultiJson.dump(json), status: 200
|
||||
|
@ -91,26 +110,37 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
web_hook = web_hook_event.web_hook
|
||||
emitter = WebHookEmitter.new(web_hook, web_hook_event)
|
||||
emitter.emit!(headers: MultiJson.load(web_hook_event.headers), body: web_hook_event.payload)
|
||||
render_serialized(web_hook_event, AdminWebHookEventSerializer, root: 'web_hook_event')
|
||||
render_serialized(web_hook_event, AdminWebHookEventSerializer, root: "web_hook_event")
|
||||
else
|
||||
render json: failed_json
|
||||
end
|
||||
end
|
||||
|
||||
def ping
|
||||
Jobs.enqueue(:emit_web_hook_event, web_hook_id: @web_hook.id, event_type: 'ping', event_name: 'ping')
|
||||
Jobs.enqueue(
|
||||
:emit_web_hook_event,
|
||||
web_hook_id: @web_hook.id,
|
||||
event_type: "ping",
|
||||
event_name: "ping",
|
||||
)
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def web_hook_params
|
||||
params.require(:web_hook).permit(:payload_url, :content_type, :secret,
|
||||
:wildcard_web_hook, :active, :verify_certificate,
|
||||
params.require(:web_hook).permit(
|
||||
:payload_url,
|
||||
:content_type,
|
||||
:secret,
|
||||
:wildcard_web_hook,
|
||||
:active,
|
||||
:verify_certificate,
|
||||
web_hook_event_type_ids: [],
|
||||
group_ids: [],
|
||||
tag_names: [],
|
||||
category_ids: [])
|
||||
category_ids: [],
|
||||
)
|
||||
end
|
||||
|
||||
def fetch_web_hook
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'current_user'
|
||||
require "current_user"
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
include CurrentUser
|
||||
|
@ -48,12 +48,13 @@ class ApplicationController < ActionController::Base
|
|||
after_action :dont_cache_page
|
||||
after_action :conditionally_allow_site_embedding
|
||||
after_action :ensure_vary_header
|
||||
after_action :add_noindex_header, if: -> { is_feed_request? || !SiteSetting.allow_index_in_robots_txt }
|
||||
after_action :add_noindex_header,
|
||||
if: -> { is_feed_request? || !SiteSetting.allow_index_in_robots_txt }
|
||||
after_action :add_noindex_header_to_non_canonical, if: :spa_boot_request?
|
||||
around_action :link_preload, if: -> { spa_boot_request? && GlobalSetting.preload_link_header }
|
||||
|
||||
HONEYPOT_KEY ||= 'HONEYPOT_KEY'
|
||||
CHALLENGE_KEY ||= 'CHALLENGE_KEY'
|
||||
HONEYPOT_KEY ||= "HONEYPOT_KEY"
|
||||
CHALLENGE_KEY ||= "CHALLENGE_KEY"
|
||||
|
||||
layout :set_layout
|
||||
|
||||
|
@ -68,10 +69,10 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def use_crawler_layout?
|
||||
@use_crawler_layout ||=
|
||||
request.user_agent &&
|
||||
(request.media_type.blank? || request.media_type.include?('html')) &&
|
||||
!['json', 'rss'].include?(params[:format]) &&
|
||||
(has_escaped_fragment? || params.key?("print") || show_browser_update? ||
|
||||
request.user_agent && (request.media_type.blank? || request.media_type.include?("html")) &&
|
||||
!%w[json rss].include?(params[:format]) &&
|
||||
(
|
||||
has_escaped_fragment? || params.key?("print") || show_browser_update? ||
|
||||
CrawlerDetection.crawler?(request.user_agent, request.headers["HTTP_VIA"])
|
||||
)
|
||||
end
|
||||
|
@ -91,19 +92,16 @@ class ApplicationController < ActionController::Base
|
|||
response.cache_control[:no_cache] = true
|
||||
response.cache_control[:extras] = ["no-store"]
|
||||
end
|
||||
if SiteSetting.login_required
|
||||
response.headers['Discourse-No-Onebox'] = '1'
|
||||
end
|
||||
response.headers["Discourse-No-Onebox"] = "1" if SiteSetting.login_required
|
||||
end
|
||||
|
||||
def conditionally_allow_site_embedding
|
||||
if SiteSetting.allow_embedding_site_in_an_iframe
|
||||
response.headers.delete('X-Frame-Options')
|
||||
end
|
||||
response.headers.delete("X-Frame-Options") if SiteSetting.allow_embedding_site_in_an_iframe
|
||||
end
|
||||
|
||||
def ember_cli_required?
|
||||
Rails.env.development? && ENV["ALLOW_EMBER_CLI_PROXY_BYPASS"] != "1" && request.headers['X-Discourse-Ember-CLI'] != 'true'
|
||||
Rails.env.development? && ENV["ALLOW_EMBER_CLI_PROXY_BYPASS"] != "1" &&
|
||||
request.headers["X-Discourse-Ember-CLI"] != "true"
|
||||
end
|
||||
|
||||
def application_layout
|
||||
|
@ -118,14 +116,16 @@ class ApplicationController < ActionController::Base
|
|||
return "crawler"
|
||||
end
|
||||
|
||||
use_crawler_layout? ? 'crawler' : application_layout
|
||||
use_crawler_layout? ? "crawler" : application_layout
|
||||
end
|
||||
|
||||
class RenderEmpty < StandardError; end
|
||||
class PluginDisabled < StandardError; end
|
||||
class RenderEmpty < StandardError
|
||||
end
|
||||
class PluginDisabled < StandardError
|
||||
end
|
||||
|
||||
rescue_from RenderEmpty do
|
||||
with_resolved_locale { render 'default/empty' }
|
||||
with_resolved_locale { render "default/empty" }
|
||||
end
|
||||
|
||||
rescue_from ArgumentError do |e|
|
||||
|
@ -147,7 +147,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
rescue_from Discourse::SiteSettingMissing do |e|
|
||||
render_json_error I18n.t('site_setting_missing', name: e.message), status: 500
|
||||
render_json_error I18n.t("site_setting_missing", name: e.message), status: 500
|
||||
end
|
||||
|
||||
rescue_from ActionController::RoutingError, PluginDisabled do
|
||||
|
@ -180,21 +180,20 @@ class ApplicationController < ActionController::Base
|
|||
rescue_from RateLimiter::LimitExceeded do |e|
|
||||
retry_time_in_seconds = e&.available_in
|
||||
|
||||
response_headers = {
|
||||
'Retry-After': retry_time_in_seconds.to_s
|
||||
}
|
||||
response_headers = { "Retry-After": retry_time_in_seconds.to_s }
|
||||
|
||||
if e&.error_code
|
||||
response_headers['Discourse-Rate-Limit-Error-Code'] = e.error_code
|
||||
end
|
||||
response_headers["Discourse-Rate-Limit-Error-Code"] = e.error_code if e&.error_code
|
||||
|
||||
with_resolved_locale do
|
||||
render_json_error(
|
||||
e.description,
|
||||
type: :rate_limit,
|
||||
status: 429,
|
||||
extras: { wait_seconds: retry_time_in_seconds, time_left: e&.time_left },
|
||||
headers: response_headers
|
||||
extras: {
|
||||
wait_seconds: retry_time_in_seconds,
|
||||
time_left: e&.time_left,
|
||||
},
|
||||
headers: response_headers,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -208,10 +207,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
rescue_from Discourse::InvalidParameters do |e|
|
||||
opts = {
|
||||
custom_message: 'invalid_params',
|
||||
custom_message_params: { message: e.message }
|
||||
}
|
||||
opts = { custom_message: "invalid_params", custom_message_params: { message: e.message } }
|
||||
|
||||
if (request.format && request.format.json?) || request.xhr? || !request.get?
|
||||
rescue_discourse_actions(:invalid_parameters, 400, opts.merge(include_ember: true))
|
||||
|
@ -226,14 +222,12 @@ class ApplicationController < ActionController::Base
|
|||
e.status,
|
||||
check_permalinks: e.check_permalinks,
|
||||
original_path: e.original_path,
|
||||
custom_message: e.custom_message
|
||||
custom_message: e.custom_message,
|
||||
)
|
||||
end
|
||||
|
||||
rescue_from Discourse::InvalidAccess do |e|
|
||||
if e.opts[:delete_cookie].present?
|
||||
cookies.delete(e.opts[:delete_cookie])
|
||||
end
|
||||
cookies.delete(e.opts[:delete_cookie]) if e.opts[:delete_cookie].present?
|
||||
|
||||
rescue_discourse_actions(
|
||||
:invalid_access,
|
||||
|
@ -241,7 +235,7 @@ class ApplicationController < ActionController::Base
|
|||
include_ember: true,
|
||||
custom_message: e.custom_message,
|
||||
custom_message_params: e.custom_message_params,
|
||||
group: e.group
|
||||
group: e.group,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -249,20 +243,16 @@ class ApplicationController < ActionController::Base
|
|||
unless response_body
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render_json_error I18n.t('read_only_mode_enabled'), type: :read_only, status: 503
|
||||
end
|
||||
format.html do
|
||||
render status: 503, layout: 'no_ember', template: 'exceptions/read_only'
|
||||
render_json_error I18n.t("read_only_mode_enabled"), type: :read_only, status: 503
|
||||
end
|
||||
format.html { render status: 503, layout: "no_ember", template: "exceptions/read_only" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue_from SecondFactor::AuthManager::SecondFactorRequired do |e|
|
||||
if request.xhr?
|
||||
render json: {
|
||||
second_factor_challenge_nonce: e.nonce
|
||||
}, status: 403
|
||||
render json: { second_factor_challenge_nonce: e.nonce }, status: 403
|
||||
else
|
||||
redirect_to session_2fa_path(nonce: e.nonce)
|
||||
end
|
||||
|
@ -274,7 +264,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def redirect_with_client_support(url, options = {})
|
||||
if request.xhr?
|
||||
response.headers['Discourse-Xhr-Redirect'] = 'true'
|
||||
response.headers["Discourse-Xhr-Redirect"] = "true"
|
||||
render plain: url
|
||||
else
|
||||
redirect_to url, options
|
||||
|
@ -283,9 +273,9 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def rescue_discourse_actions(type, status_code, opts = nil)
|
||||
opts ||= {}
|
||||
show_json_errors = (request.format && request.format.json?) ||
|
||||
(request.xhr?) ||
|
||||
((params[:external_id] || '').ends_with? '.json')
|
||||
show_json_errors =
|
||||
(request.format && request.format.json?) || (request.xhr?) ||
|
||||
((params[:external_id] || "").ends_with? ".json")
|
||||
|
||||
if type == :not_found && opts[:check_permalinks]
|
||||
url = opts[:original_path] || request.fullpath
|
||||
|
@ -295,7 +285,9 @@ class ApplicationController < ActionController::Base
|
|||
# cause category / topic was deleted
|
||||
if permalink.present? && permalink.target_url
|
||||
# permalink present, redirect to that URL
|
||||
redirect_with_client_support permalink.target_url, status: :moved_permanently, allow_other_host: true
|
||||
redirect_with_client_support permalink.target_url,
|
||||
status: :moved_permanently,
|
||||
allow_other_host: true
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -321,11 +313,15 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
with_resolved_locale(check_current_user: false) do
|
||||
# Include error in HTML format for topics#show.
|
||||
if (request.params[:controller] == 'topics' && request.params[:action] == 'show') || (request.params[:controller] == 'categories' && request.params[:action] == 'find_by_slug')
|
||||
if (request.params[:controller] == "topics" && request.params[:action] == "show") ||
|
||||
(
|
||||
request.params[:controller] == "categories" &&
|
||||
request.params[:action] == "find_by_slug"
|
||||
)
|
||||
opts[:extras] = {
|
||||
title: I18n.t('page_not_found.page_title'),
|
||||
title: I18n.t("page_not_found.page_title"),
|
||||
html: build_not_found_page(error_page_opts),
|
||||
group: error_page_opts[:group]
|
||||
group: error_page_opts[:group],
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -340,7 +336,7 @@ class ApplicationController < ActionController::Base
|
|||
return render plain: message, status: status_code
|
||||
end
|
||||
with_resolved_locale do
|
||||
error_page_opts[:layout] = (opts[:include_ember] && @preloaded) ? 'application' : 'no_ember'
|
||||
error_page_opts[:layout] = (opts[:include_ember] && @preloaded) ? "application" : "no_ember"
|
||||
render html: build_not_found_page(error_page_opts)
|
||||
end
|
||||
end
|
||||
|
@ -373,9 +369,8 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def clear_notifications
|
||||
if current_user && !@readonly_mode
|
||||
|
||||
cookie_notifications = cookies['cn']
|
||||
notifications = request.headers['Discourse-Clear-Notifications']
|
||||
cookie_notifications = cookies["cn"]
|
||||
notifications = request.headers["Discourse-Clear-Notifications"]
|
||||
|
||||
if cookie_notifications
|
||||
if notifications.present?
|
||||
|
@ -392,22 +387,28 @@ class ApplicationController < ActionController::Base
|
|||
current_user.publish_notifications_state
|
||||
cookie_args = {}
|
||||
cookie_args[:path] = Discourse.base_path if Discourse.base_path.present?
|
||||
cookies.delete('cn', cookie_args)
|
||||
cookies.delete("cn", cookie_args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def with_resolved_locale(check_current_user: true)
|
||||
if check_current_user && (user = current_user rescue nil)
|
||||
if check_current_user &&
|
||||
(
|
||||
user =
|
||||
begin
|
||||
current_user
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
)
|
||||
locale = user.effective_locale
|
||||
else
|
||||
locale = Discourse.anonymous_locale(request)
|
||||
locale ||= SiteSetting.default_locale
|
||||
end
|
||||
|
||||
if !I18n.locale_available?(locale)
|
||||
locale = SiteSettings::DefaultsProvider::DEFAULT_LOCALE
|
||||
end
|
||||
locale = SiteSettings::DefaultsProvider::DEFAULT_LOCALE if !I18n.locale_available?(locale)
|
||||
|
||||
I18n.ensure_all_loaded!
|
||||
I18n.with_locale(locale) { yield }
|
||||
|
@ -458,7 +459,8 @@ class ApplicationController < ActionController::Base
|
|||
safe_mode = safe_mode.split(",")
|
||||
request.env[NO_THEMES] = safe_mode.include?(NO_THEMES) || safe_mode.include?(LEGACY_NO_THEMES)
|
||||
request.env[NO_PLUGINS] = safe_mode.include?(NO_PLUGINS)
|
||||
request.env[NO_UNOFFICIAL_PLUGINS] = safe_mode.include?(NO_UNOFFICIAL_PLUGINS) || safe_mode.include?(LEGACY_NO_UNOFFICIAL_PLUGINS)
|
||||
request.env[NO_UNOFFICIAL_PLUGINS] = safe_mode.include?(NO_UNOFFICIAL_PLUGINS) ||
|
||||
safe_mode.include?(LEGACY_NO_UNOFFICIAL_PLUGINS)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -472,7 +474,6 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
if (preview_theme_id = request[:preview_theme_id]&.to_i) &&
|
||||
guardian.allow_themes?([preview_theme_id], include_preview: true)
|
||||
|
||||
theme_id = preview_theme_id
|
||||
end
|
||||
|
||||
|
@ -491,7 +492,8 @@ class ApplicationController < ActionController::Base
|
|||
theme_id = ids.first if guardian.allow_themes?(ids)
|
||||
end
|
||||
|
||||
if theme_id.blank? && SiteSetting.default_theme_id != -1 && guardian.allow_themes?([SiteSetting.default_theme_id])
|
||||
if theme_id.blank? && SiteSetting.default_theme_id != -1 &&
|
||||
guardian.allow_themes?([SiteSetting.default_theme_id])
|
||||
theme_id = SiteSetting.default_theme_id
|
||||
end
|
||||
|
||||
|
@ -533,13 +535,11 @@ class ApplicationController < ActionController::Base
|
|||
def render_json_dump(obj, opts = nil)
|
||||
opts ||= {}
|
||||
if opts[:rest_serializer]
|
||||
obj['__rest_serializer'] = "1"
|
||||
opts.each do |k, v|
|
||||
obj[k] = v if k.to_s.start_with?("refresh_")
|
||||
end
|
||||
obj["__rest_serializer"] = "1"
|
||||
opts.each { |k, v| obj[k] = v if k.to_s.start_with?("refresh_") }
|
||||
|
||||
obj['extras'] = opts[:extras] if opts[:extras]
|
||||
obj['meta'] = opts[:meta] if opts[:meta]
|
||||
obj["extras"] = opts[:extras] if opts[:extras]
|
||||
obj["meta"] = opts[:meta] if opts[:meta]
|
||||
end
|
||||
|
||||
render json: MultiJson.dump(obj), status: opts[:status] || 200
|
||||
|
@ -557,8 +557,9 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def fetch_user_from_params(opts = nil, eager_load = [])
|
||||
opts ||= {}
|
||||
user = if params[:username]
|
||||
username_lower = params[:username].downcase.chomp('.json')
|
||||
user =
|
||||
if params[:username]
|
||||
username_lower = params[:username].downcase.chomp(".json")
|
||||
|
||||
if current_user && current_user.username_lower == username_lower
|
||||
current_user
|
||||
|
@ -570,12 +571,15 @@ class ApplicationController < ActionController::Base
|
|||
result.find_by(find_opts)
|
||||
end
|
||||
elsif params[:external_id]
|
||||
external_id = params[:external_id].chomp('.json')
|
||||
external_id = params[:external_id].chomp(".json")
|
||||
if provider_name = params[:external_provider]
|
||||
raise Discourse::InvalidAccess unless guardian.is_admin? # external_id might be something sensitive
|
||||
provider = Discourse.enabled_authenticators.find { |a| a.name == provider_name }
|
||||
raise Discourse::NotFound if !provider&.is_managed? # Only managed authenticators use UserAssociatedAccount
|
||||
UserAssociatedAccount.find_by(provider_name: provider_name, provider_uid: external_id)&.user
|
||||
UserAssociatedAccount.find_by(
|
||||
provider_name: provider_name,
|
||||
provider_uid: external_id,
|
||||
)&.user
|
||||
else
|
||||
SingleSignOnRecord.find_by(external_id: external_id).try(:user)
|
||||
end
|
||||
|
@ -588,14 +592,16 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def post_ids_including_replies
|
||||
post_ids = params[:post_ids].map(&:to_i)
|
||||
post_ids |= PostReply.where(post_id: params[:reply_post_ids]).pluck(:reply_post_id) if params[:reply_post_ids]
|
||||
post_ids |= PostReply.where(post_id: params[:reply_post_ids]).pluck(:reply_post_id) if params[
|
||||
:reply_post_ids
|
||||
]
|
||||
post_ids
|
||||
end
|
||||
|
||||
def no_cookies
|
||||
# do your best to ensure response has no cookies
|
||||
# longer term we may want to push this into middleware
|
||||
headers.delete 'Set-Cookie'
|
||||
headers.delete "Set-Cookie"
|
||||
request.session_options[:skip] = true
|
||||
end
|
||||
|
||||
|
@ -615,9 +621,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
RateLimiter.new(nil, "second-factor-min-#{request.remote_ip}", 6, 1.minute).performed!
|
||||
|
||||
if user
|
||||
RateLimiter.new(nil, "second-factor-min-#{user.username}", 6, 1.minute).performed!
|
||||
end
|
||||
RateLimiter.new(nil, "second-factor-min-#{user.username}", 6, 1.minute).performed! if user
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -634,10 +638,16 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def preload_current_user_data
|
||||
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, scope: guardian, root: false)))
|
||||
store_preloaded(
|
||||
"currentUser",
|
||||
MultiJson.dump(CurrentUserSerializer.new(current_user, scope: guardian, root: false)),
|
||||
)
|
||||
report = TopicTrackingState.report(current_user)
|
||||
serializer = ActiveModel::ArraySerializer.new(
|
||||
report, each_serializer: TopicTrackingStateSerializer, scope: guardian
|
||||
serializer =
|
||||
ActiveModel::ArraySerializer.new(
|
||||
report,
|
||||
each_serializer: TopicTrackingStateSerializer,
|
||||
scope: guardian,
|
||||
)
|
||||
store_preloaded("topicTrackingStates", MultiJson.dump(serializer))
|
||||
end
|
||||
|
@ -649,19 +659,17 @@ class ApplicationController < ActionController::Base
|
|||
if @theme_id.present?
|
||||
{
|
||||
top: Theme.lookup_field(@theme_id, target, "after_header"),
|
||||
footer: Theme.lookup_field(@theme_id, target, "footer")
|
||||
footer: Theme.lookup_field(@theme_id, target, "footer"),
|
||||
}
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
||||
if DiscoursePluginRegistry.custom_html
|
||||
data.merge! DiscoursePluginRegistry.custom_html
|
||||
end
|
||||
data.merge! DiscoursePluginRegistry.custom_html if DiscoursePluginRegistry.custom_html
|
||||
|
||||
DiscoursePluginRegistry.html_builders.each do |name, _|
|
||||
if name.start_with?("client:")
|
||||
data[name.sub(/^client:/, '')] = DiscoursePluginRegistry.build_html(name, self)
|
||||
data[name.sub(/^client:/, "")] = DiscoursePluginRegistry.build_html(name, self)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -703,7 +711,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
render(
|
||||
json: MultiJson.dump(create_errors_json(obj, opts)),
|
||||
status: opts[:status] || status_code(obj)
|
||||
status: opts[:status] || status_code(obj),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -714,11 +722,11 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def success_json
|
||||
{ success: 'OK' }
|
||||
{ success: "OK" }
|
||||
end
|
||||
|
||||
def failed_json
|
||||
{ failed: 'FAILED' }
|
||||
{ failed: "FAILED" }
|
||||
end
|
||||
|
||||
def json_result(obj, opts = {})
|
||||
|
@ -727,14 +735,18 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
# If we were given a serializer, add the class to the json that comes back
|
||||
if opts[:serializer].present?
|
||||
json[obj.class.name.underscore] = opts[:serializer].new(obj, scope: guardian).serializable_hash
|
||||
json[obj.class.name.underscore] = opts[:serializer].new(
|
||||
obj,
|
||||
scope: guardian,
|
||||
).serializable_hash
|
||||
end
|
||||
|
||||
render json: MultiJson.dump(json)
|
||||
else
|
||||
error_obj = nil
|
||||
if opts[:additional_errors]
|
||||
error_target = opts[:additional_errors].find do |o|
|
||||
error_target =
|
||||
opts[:additional_errors].find do |o|
|
||||
target = obj.public_send(o)
|
||||
target && target.errors.present?
|
||||
end
|
||||
|
@ -756,11 +768,15 @@ class ApplicationController < ActionController::Base
|
|||
def check_xhr
|
||||
# bypass xhr check on PUT / POST / DELETE provided api key is there, otherwise calling api is annoying
|
||||
return if !request.get? && (is_api? || is_user_api?)
|
||||
raise ApplicationController::RenderEmpty.new unless ((request.format && request.format.json?) || request.xhr?)
|
||||
unless ((request.format && request.format.json?) || request.xhr?)
|
||||
raise ApplicationController::RenderEmpty.new
|
||||
end
|
||||
end
|
||||
|
||||
def apply_cdn_headers
|
||||
Discourse.apply_cdn_headers(response.headers) if Discourse.is_cdn_request?(request.env, request.method)
|
||||
if Discourse.is_cdn_request?(request.env, request.method)
|
||||
Discourse.apply_cdn_headers(response.headers)
|
||||
end
|
||||
end
|
||||
|
||||
def self.requires_login(arg = {})
|
||||
|
@ -811,8 +827,9 @@ class ApplicationController < ActionController::Base
|
|||
if SiteSetting.auth_immediately && SiteSetting.enable_discourse_connect?
|
||||
# save original URL in a session so we can redirect after login
|
||||
session[:destination_url] = destination_url
|
||||
redirect_to path('/session/sso')
|
||||
elsif SiteSetting.auth_immediately && !SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1 && !cookies[:authentication_data]
|
||||
redirect_to path("/session/sso")
|
||||
elsif SiteSetting.auth_immediately && !SiteSetting.enable_local_logins &&
|
||||
Discourse.enabled_authenticators.length == 1 && !cookies[:authentication_data]
|
||||
# Only one authentication provider, direct straight to it.
|
||||
# If authentication_data is present, then we are halfway though registration. Don't redirect offsite
|
||||
cookies[:destination_url] = destination_url
|
||||
|
@ -831,9 +848,7 @@ class ApplicationController < ActionController::Base
|
|||
# Redirects to provided URL scheme if
|
||||
# - request uses a valid public key and auth_redirect scheme
|
||||
# - one_time_password scope is allowed
|
||||
if !current_user &&
|
||||
params.has_key?(:user_api_public_key) &&
|
||||
params.has_key?(:auth_redirect)
|
||||
if !current_user && params.has_key?(:user_api_public_key) && params.has_key?(:auth_redirect)
|
||||
begin
|
||||
OpenSSL::PKey::RSA.new(params[:user_api_public_key])
|
||||
rescue OpenSSL::PKey::RSAError
|
||||
|
@ -872,26 +887,45 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def should_enforce_2fa?
|
||||
disqualified_from_2fa_enforcement = request.format.json? || is_api? || current_user.anonymous?
|
||||
enforcing_2fa = ((SiteSetting.enforce_second_factor == 'staff' && current_user.staff?) || SiteSetting.enforce_second_factor == 'all')
|
||||
!disqualified_from_2fa_enforcement && enforcing_2fa && !current_user.has_any_second_factor_methods_enabled?
|
||||
enforcing_2fa =
|
||||
(
|
||||
(SiteSetting.enforce_second_factor == "staff" && current_user.staff?) ||
|
||||
SiteSetting.enforce_second_factor == "all"
|
||||
)
|
||||
!disqualified_from_2fa_enforcement && enforcing_2fa &&
|
||||
!current_user.has_any_second_factor_methods_enabled?
|
||||
end
|
||||
|
||||
def build_not_found_page(opts = {})
|
||||
if SiteSetting.bootstrap_error_pages?
|
||||
preload_json
|
||||
opts[:layout] = 'application' if opts[:layout] == 'no_ember'
|
||||
opts[:layout] = "application" if opts[:layout] == "no_ember"
|
||||
end
|
||||
|
||||
@current_user = current_user rescue nil
|
||||
@current_user =
|
||||
begin
|
||||
current_user
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
if !SiteSetting.login_required? || @current_user
|
||||
key = "page_not_found_topics:#{I18n.locale}"
|
||||
@topics_partial = Discourse.cache.fetch(key, expires_in: 10.minutes) do
|
||||
@topics_partial =
|
||||
Discourse
|
||||
.cache
|
||||
.fetch(key, expires_in: 10.minutes) do
|
||||
category_topic_ids = Category.pluck(:topic_id).compact
|
||||
@top_viewed = TopicQuery.new(nil, except_topic_ids: category_topic_ids).list_top_for("monthly").topics.first(10)
|
||||
@top_viewed =
|
||||
TopicQuery
|
||||
.new(nil, except_topic_ids: category_topic_ids)
|
||||
.list_top_for("monthly")
|
||||
.topics
|
||||
.first(10)
|
||||
@recent = Topic.includes(:category).where.not(id: category_topic_ids).recent(10)
|
||||
render_to_string partial: '/exceptions/not_found_topics', formats: [:html]
|
||||
end.html_safe
|
||||
render_to_string partial: "/exceptions/not_found_topics", formats: [:html]
|
||||
end
|
||||
.html_safe
|
||||
end
|
||||
|
||||
@container_class = "wrap not-found-container"
|
||||
|
@ -902,13 +936,16 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
params[:slug] = params[:slug].first if params[:slug].kind_of?(Array)
|
||||
params[:id] = params[:id].first if params[:id].kind_of?(Array)
|
||||
@slug = (params[:slug].presence || params[:id].presence || "").to_s.tr('-', ' ')
|
||||
@slug = (params[:slug].presence || params[:id].presence || "").to_s.tr("-", " ")
|
||||
|
||||
render_to_string status: opts[:status], layout: opts[:layout], formats: [:html], template: '/exceptions/not_found'
|
||||
render_to_string status: opts[:status],
|
||||
layout: opts[:layout],
|
||||
formats: [:html],
|
||||
template: "/exceptions/not_found"
|
||||
end
|
||||
|
||||
def is_asset_path
|
||||
request.env['DISCOURSE_IS_ASSET_PATH'] = 1
|
||||
request.env["DISCOURSE_IS_ASSET_PATH"] = 1
|
||||
end
|
||||
|
||||
def is_feed_request?
|
||||
|
@ -916,19 +953,20 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def add_noindex_header
|
||||
if request.get? && !response.headers['X-Robots-Tag']
|
||||
if request.get? && !response.headers["X-Robots-Tag"]
|
||||
if SiteSetting.allow_index_in_robots_txt
|
||||
response.headers['X-Robots-Tag'] = 'noindex'
|
||||
response.headers["X-Robots-Tag"] = "noindex"
|
||||
else
|
||||
response.headers['X-Robots-Tag'] = 'noindex, nofollow'
|
||||
response.headers["X-Robots-Tag"] = "noindex, nofollow"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_noindex_header_to_non_canonical
|
||||
canonical = (@canonical_url || @default_canonical)
|
||||
if canonical.present? && canonical != request.url && !SiteSetting.allow_indexing_non_canonical_urls
|
||||
response.headers['X-Robots-Tag'] ||= 'noindex'
|
||||
if canonical.present? && canonical != request.url &&
|
||||
!SiteSetting.allow_indexing_non_canonical_urls
|
||||
response.headers["X-Robots-Tag"] ||= "noindex"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -955,7 +993,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
# returns an array of integers given a param key
|
||||
# returns nil if key is not found
|
||||
def param_to_integer_list(key, delimiter = ',')
|
||||
def param_to_integer_list(key, delimiter = ",")
|
||||
case params[key]
|
||||
when String
|
||||
params[key].split(delimiter).map(&:to_i)
|
||||
|
@ -978,16 +1016,15 @@ class ApplicationController < ActionController::Base
|
|||
user_agent = request.user_agent&.downcase
|
||||
return if user_agent.blank?
|
||||
|
||||
SiteSetting.slow_down_crawler_user_agents.downcase.split("|").each do |crawler|
|
||||
SiteSetting
|
||||
.slow_down_crawler_user_agents
|
||||
.downcase
|
||||
.split("|")
|
||||
.each do |crawler|
|
||||
if user_agent.include?(crawler)
|
||||
key = "#{crawler}_crawler_rate_limit"
|
||||
limiter = RateLimiter.new(
|
||||
nil,
|
||||
key,
|
||||
1,
|
||||
SiteSetting.slow_down_crawler_rate,
|
||||
error_code: key
|
||||
)
|
||||
limiter =
|
||||
RateLimiter.new(nil, key, 1, SiteSetting.slow_down_crawler_rate, error_code: key)
|
||||
limiter.performed!
|
||||
break
|
||||
end
|
||||
|
@ -1000,8 +1037,7 @@ class ApplicationController < ActionController::Base
|
|||
yield(manager) if block_given?
|
||||
result = manager.run!(request, params, secure_session)
|
||||
|
||||
if !result.no_second_factors_enabled? &&
|
||||
!result.second_factor_auth_completed? &&
|
||||
if !result.no_second_factors_enabled? && !result.second_factor_auth_completed? &&
|
||||
!result.second_factor_auth_skipped?
|
||||
# should never happen, but I want to know if somehow it does! (osama)
|
||||
raise "2fa process ended up in a bad state!"
|
||||
|
@ -1013,7 +1049,7 @@ class ApplicationController < ActionController::Base
|
|||
def link_preload
|
||||
@links_to_preload = []
|
||||
yield
|
||||
response.headers['Link'] = @links_to_preload.join(', ') if !@links_to_preload.empty?
|
||||
response.headers["Link"] = @links_to_preload.join(", ") if !@links_to_preload.empty?
|
||||
end
|
||||
|
||||
def spa_boot_request?
|
||||
|
|
|
@ -5,6 +5,6 @@ class AssociatedGroupsController < ApplicationController
|
|||
|
||||
def index
|
||||
guardian.ensure_can_associate_groups!
|
||||
render_serialized(AssociatedGroup.all, AssociatedGroupSerializer, root: 'associated_groups')
|
||||
render_serialized(AssociatedGroup.all, AssociatedGroupSerializer, root: "associated_groups")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BadgesController < ApplicationController
|
||||
skip_before_action :check_xhr, only: [:index, :show]
|
||||
skip_before_action :check_xhr, only: %i[index show]
|
||||
after_action :add_noindex_header
|
||||
|
||||
def index
|
||||
|
@ -16,7 +16,9 @@ class BadgesController < ApplicationController
|
|||
|
||||
if (params[:only_listable] == "true") || !request.xhr?
|
||||
# NOTE: this is sorted client side if needed
|
||||
badges = badges.includes(:badge_grouping)
|
||||
badges =
|
||||
badges
|
||||
.includes(:badge_grouping)
|
||||
.includes(:badge_type, :image_upload)
|
||||
.where(enabled: true, listable: true)
|
||||
end
|
||||
|
@ -25,9 +27,18 @@ class BadgesController < ApplicationController
|
|||
|
||||
user_badges = nil
|
||||
if current_user
|
||||
user_badges = Set.new(current_user.user_badges.select('distinct badge_id').pluck(:badge_id))
|
||||
user_badges = Set.new(current_user.user_badges.select("distinct badge_id").pluck(:badge_id))
|
||||
end
|
||||
serialized = MultiJson.dump(serialize_data(badges, BadgeIndexSerializer, root: "badges", user_badges: user_badges, include_long_description: true))
|
||||
serialized =
|
||||
MultiJson.dump(
|
||||
serialize_data(
|
||||
badges,
|
||||
BadgeIndexSerializer,
|
||||
root: "badges",
|
||||
user_badges: user_badges,
|
||||
include_long_description: true,
|
||||
),
|
||||
)
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
store_preloaded "badges", serialized
|
||||
|
@ -42,27 +53,27 @@ class BadgesController < ApplicationController
|
|||
|
||||
params.require(:id)
|
||||
@badge = Badge.enabled.find(params[:id])
|
||||
@rss_title = I18n.t('rss_description.badge', display_name: @badge.display_name, site_title: SiteSetting.title)
|
||||
@rss_title =
|
||||
I18n.t(
|
||||
"rss_description.badge",
|
||||
display_name: @badge.display_name,
|
||||
site_title: SiteSetting.title,
|
||||
)
|
||||
@rss_link = "#{Discourse.base_url}/badges/#{@badge.id}/#{@badge.slug}"
|
||||
|
||||
if current_user
|
||||
user_badge = UserBadge.find_by(user_id: current_user.id, badge_id: @badge.id)
|
||||
if user_badge && user_badge.notification
|
||||
user_badge.notification.update read: true
|
||||
end
|
||||
if user_badge
|
||||
@badge.has_badge = true
|
||||
end
|
||||
user_badge.notification.update read: true if user_badge && user_badge.notification
|
||||
@badge.has_badge = true if user_badge
|
||||
end
|
||||
|
||||
serialized = MultiJson.dump(serialize_data(@badge, BadgeSerializer, root: "badge", include_long_description: true))
|
||||
serialized =
|
||||
MultiJson.dump(
|
||||
serialize_data(@badge, BadgeSerializer, root: "badge", include_long_description: true),
|
||||
)
|
||||
respond_to do |format|
|
||||
format.rss do
|
||||
@rss_description = @badge.long_description
|
||||
end
|
||||
format.html do
|
||||
store_preloaded "badge", serialized
|
||||
end
|
||||
format.rss { @rss_description = @badge.long_description }
|
||||
format.html { store_preloaded "badge", serialized }
|
||||
format.json { render json: serialized }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,26 +6,34 @@ class BookmarksController < ApplicationController
|
|||
def create
|
||||
params.require(:bookmarkable_id)
|
||||
params.require(:bookmarkable_type)
|
||||
params.permit(:bookmarkable_id, :bookmarkable_type, :name, :reminder_at, :auto_delete_preference)
|
||||
params.permit(
|
||||
:bookmarkable_id,
|
||||
:bookmarkable_type,
|
||||
:name,
|
||||
:reminder_at,
|
||||
:auto_delete_preference,
|
||||
)
|
||||
|
||||
RateLimiter.new(
|
||||
current_user, "create_bookmark", SiteSetting.max_bookmarks_per_day, 1.day.to_i
|
||||
current_user,
|
||||
"create_bookmark",
|
||||
SiteSetting.max_bookmarks_per_day,
|
||||
1.day.to_i,
|
||||
).performed!
|
||||
|
||||
bookmark_manager = BookmarkManager.new(current_user)
|
||||
bookmark = bookmark_manager.create_for(
|
||||
bookmark =
|
||||
bookmark_manager.create_for(
|
||||
bookmarkable_id: params[:bookmarkable_id],
|
||||
bookmarkable_type: params[:bookmarkable_type],
|
||||
name: params[:name],
|
||||
reminder_at: params[:reminder_at],
|
||||
options: {
|
||||
auto_delete_preference: params[:auto_delete_preference]
|
||||
}
|
||||
auto_delete_preference: params[:auto_delete_preference],
|
||||
},
|
||||
)
|
||||
|
||||
if bookmark_manager.errors.empty?
|
||||
return render json: success_json.merge(id: bookmark.id)
|
||||
end
|
||||
return render json: success_json.merge(id: bookmark.id) if bookmark_manager.errors.empty?
|
||||
|
||||
render json: failed_json.merge(errors: bookmark_manager.errors.full_messages), status: 400
|
||||
end
|
||||
|
@ -33,7 +41,8 @@ class BookmarksController < ApplicationController
|
|||
def destroy
|
||||
params.require(:id)
|
||||
destroyed_bookmark = BookmarkManager.new(current_user).destroy(params[:id])
|
||||
render json: success_json.merge(BookmarkManager.bookmark_metadata(destroyed_bookmark, current_user))
|
||||
render json:
|
||||
success_json.merge(BookmarkManager.bookmark_metadata(destroyed_bookmark, current_user))
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -46,13 +55,11 @@ class BookmarksController < ApplicationController
|
|||
name: params[:name],
|
||||
reminder_at: params[:reminder_at],
|
||||
options: {
|
||||
auto_delete_preference: params[:auto_delete_preference]
|
||||
}
|
||||
auto_delete_preference: params[:auto_delete_preference],
|
||||
},
|
||||
)
|
||||
|
||||
if bookmark_manager.errors.empty?
|
||||
return render json: success_json
|
||||
end
|
||||
return render json: success_json if bookmark_manager.errors.empty?
|
||||
|
||||
render json: failed_json.merge(errors: bookmark_manager.errors.full_messages), status: 400
|
||||
end
|
||||
|
@ -63,9 +70,7 @@ class BookmarksController < ApplicationController
|
|||
bookmark_manager = BookmarkManager.new(current_user)
|
||||
bookmark_manager.toggle_pin(bookmark_id: params[:bookmark_id])
|
||||
|
||||
if bookmark_manager.errors.empty?
|
||||
return render json: success_json
|
||||
end
|
||||
return render json: success_json if bookmark_manager.errors.empty?
|
||||
|
||||
render json: failed_json.merge(errors: bookmark_manager.errors.full_messages), status: 400
|
||||
end
|
||||
|
|
|
@ -37,35 +37,34 @@ class BootstrapController < ApplicationController
|
|||
assets_fake_request.env["QUERY_STRING"] = query
|
||||
end
|
||||
|
||||
Discourse.find_plugin_css_assets(
|
||||
Discourse
|
||||
.find_plugin_css_assets(
|
||||
include_official: allow_plugins?,
|
||||
include_unofficial: allow_third_party_plugins?,
|
||||
mobile_view: mobile_view?,
|
||||
desktop_view: !mobile_view?,
|
||||
request: assets_fake_request
|
||||
).each do |file|
|
||||
add_style(file, plugin: true)
|
||||
end
|
||||
request: assets_fake_request,
|
||||
)
|
||||
.each { |file| add_style(file, plugin: true) }
|
||||
add_style(mobile_view? ? :mobile_theme : :desktop_theme) if theme_id.present?
|
||||
|
||||
extra_locales = []
|
||||
if ExtraLocalesController.client_overrides_exist?
|
||||
extra_locales << ExtraLocalesController.url('overrides')
|
||||
extra_locales << ExtraLocalesController.url("overrides")
|
||||
end
|
||||
|
||||
if staff?
|
||||
extra_locales << ExtraLocalesController.url('admin')
|
||||
end
|
||||
extra_locales << ExtraLocalesController.url("admin") if staff?
|
||||
|
||||
if admin?
|
||||
extra_locales << ExtraLocalesController.url('wizard')
|
||||
end
|
||||
extra_locales << ExtraLocalesController.url("wizard") if admin?
|
||||
|
||||
plugin_js = Discourse.find_plugin_js_assets(
|
||||
plugin_js =
|
||||
Discourse
|
||||
.find_plugin_js_assets(
|
||||
include_official: allow_plugins?,
|
||||
include_unofficial: allow_third_party_plugins?,
|
||||
request: assets_fake_request
|
||||
).map { |f| script_asset_path(f) }
|
||||
request: assets_fake_request,
|
||||
)
|
||||
.map { |f| script_asset_path(f) }
|
||||
|
||||
plugin_test_js =
|
||||
if Rails.env != "production"
|
||||
|
@ -76,7 +75,7 @@ class BootstrapController < ApplicationController
|
|||
|
||||
bootstrap = {
|
||||
theme_id: theme_id,
|
||||
theme_color: "##{ColorScheme.hex_for_name('header_background', scheme_id)}",
|
||||
theme_color: "##{ColorScheme.hex_for_name("header_background", scheme_id)}",
|
||||
title: SiteSetting.title,
|
||||
current_homepage: current_homepage,
|
||||
locale_script: locale,
|
||||
|
@ -90,7 +89,7 @@ class BootstrapController < ApplicationController
|
|||
html_classes: html_classes,
|
||||
html_lang: html_lang,
|
||||
login_path: main_app.login_path,
|
||||
authentication_data: authentication_data
|
||||
authentication_data: authentication_data,
|
||||
}
|
||||
bootstrap[:extra_locales] = extra_locales if extra_locales.present?
|
||||
bootstrap[:csrf_token] = form_authenticity_token if current_user
|
||||
|
@ -99,39 +98,44 @@ class BootstrapController < ApplicationController
|
|||
end
|
||||
|
||||
def plugin_css_for_tests
|
||||
urls = Discourse.find_plugin_css_assets(
|
||||
include_disabled: true,
|
||||
desktop_view: true,
|
||||
).map do |target|
|
||||
details = Stylesheet::Manager.new().stylesheet_details(target, 'all')
|
||||
urls =
|
||||
Discourse
|
||||
.find_plugin_css_assets(include_disabled: true, desktop_view: true)
|
||||
.map do |target|
|
||||
details = Stylesheet::Manager.new().stylesheet_details(target, "all")
|
||||
details[0][:new_href]
|
||||
end
|
||||
|
||||
stylesheet = <<~CSS
|
||||
/* For use in tests only - `@import`s all plugin stylesheets */
|
||||
#{urls.map { |url| "@import \"#{url}\";" }.join("\n") }
|
||||
#{urls.map { |url| "@import \"#{url}\";" }.join("\n")}
|
||||
CSS
|
||||
|
||||
render plain: stylesheet, content_type: 'text/css'
|
||||
render plain: stylesheet, content_type: "text/css"
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def add_scheme(scheme_id, media, css_class)
|
||||
return if scheme_id.to_i == -1
|
||||
|
||||
if style = Stylesheet::Manager.new(theme_id: theme_id).color_scheme_stylesheet_details(scheme_id, media)
|
||||
if style =
|
||||
Stylesheet::Manager.new(theme_id: theme_id).color_scheme_stylesheet_details(
|
||||
scheme_id,
|
||||
media,
|
||||
)
|
||||
@stylesheets << { href: style[:new_href], media: media, class: css_class }
|
||||
end
|
||||
end
|
||||
|
||||
def add_style(target, opts = nil)
|
||||
if styles = Stylesheet::Manager.new(theme_id: theme_id).stylesheet_details(target, 'all')
|
||||
if styles = Stylesheet::Manager.new(theme_id: theme_id).stylesheet_details(target, "all")
|
||||
styles.each do |style|
|
||||
@stylesheets << {
|
||||
href: style[:new_href],
|
||||
media: 'all',
|
||||
media: "all",
|
||||
theme_id: style[:theme_id],
|
||||
target: style[:target]
|
||||
target: style[:target],
|
||||
}.merge(opts || {})
|
||||
end
|
||||
end
|
||||
|
@ -150,7 +154,11 @@ private
|
|||
end
|
||||
|
||||
def add_plugin_html(html, key)
|
||||
add_if_present(html, key, DiscoursePluginRegistry.build_html("server:#{key.to_s.dasherize}", self))
|
||||
add_if_present(
|
||||
html,
|
||||
key,
|
||||
DiscoursePluginRegistry.build_html("server:#{key.to_s.dasherize}", self),
|
||||
)
|
||||
end
|
||||
|
||||
def create_theme_html
|
||||
|
@ -159,10 +167,14 @@ private
|
|||
|
||||
theme_view = mobile_view? ? :mobile : :desktop
|
||||
|
||||
add_if_present(theme_html, :body_tag, Theme.lookup_field(theme_id, theme_view, 'body_tag'))
|
||||
add_if_present(theme_html, :head_tag, Theme.lookup_field(theme_id, theme_view, 'head_tag'))
|
||||
add_if_present(theme_html, :header, Theme.lookup_field(theme_id, theme_view, 'header'))
|
||||
add_if_present(theme_html, :translations, Theme.lookup_field(theme_id, :translations, I18n.locale))
|
||||
add_if_present(theme_html, :body_tag, Theme.lookup_field(theme_id, theme_view, "body_tag"))
|
||||
add_if_present(theme_html, :head_tag, Theme.lookup_field(theme_id, theme_view, "head_tag"))
|
||||
add_if_present(theme_html, :header, Theme.lookup_field(theme_id, theme_view, "header"))
|
||||
add_if_present(
|
||||
theme_html,
|
||||
:translations,
|
||||
Theme.lookup_field(theme_id, :translations, I18n.locale),
|
||||
)
|
||||
add_if_present(theme_html, :js, Theme.lookup_field(theme_id, :extra_js, nil))
|
||||
|
||||
theme_html
|
||||
|
@ -171,5 +183,4 @@ private
|
|||
def add_if_present(hash, key, val)
|
||||
hash[key] = val if val.present?
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -3,11 +3,19 @@
|
|||
class CategoriesController < ApplicationController
|
||||
include TopicQueryParams
|
||||
|
||||
requires_login except: [:index, :categories_and_latest, :categories_and_top, :show, :redirect, :find_by_slug, :visible_groups]
|
||||
requires_login except: %i[
|
||||
index
|
||||
categories_and_latest
|
||||
categories_and_top
|
||||
show
|
||||
redirect
|
||||
find_by_slug
|
||||
visible_groups
|
||||
]
|
||||
|
||||
before_action :fetch_category, only: [:show, :update, :destroy, :visible_groups]
|
||||
before_action :initialize_staff_action_logger, only: [:create, :update, :destroy]
|
||||
skip_before_action :check_xhr, only: [:index, :categories_and_latest, :categories_and_top, :redirect]
|
||||
before_action :fetch_category, only: %i[show update destroy visible_groups]
|
||||
before_action :initialize_staff_action_logger, only: %i[create update destroy]
|
||||
skip_before_action :check_xhr, only: %i[index categories_and_latest categories_and_top redirect]
|
||||
|
||||
SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR = 1.5
|
||||
MIN_CATEGORIES_TOPICS = 5
|
||||
|
@ -22,9 +30,12 @@ class CategoriesController < ApplicationController
|
|||
|
||||
@description = SiteSetting.site_description
|
||||
|
||||
parent_category = Category.find_by_slug(params[:parent_category_id]) || Category.find_by(id: params[:parent_category_id].to_i)
|
||||
parent_category =
|
||||
Category.find_by_slug(params[:parent_category_id]) ||
|
||||
Category.find_by(id: params[:parent_category_id].to_i)
|
||||
|
||||
include_subcategories = SiteSetting.desktop_category_page_style == "subcategories_with_featured_topics" ||
|
||||
include_subcategories =
|
||||
SiteSetting.desktop_category_page_style == "subcategories_with_featured_topics" ||
|
||||
params[:include_subcategories] == "true"
|
||||
|
||||
category_options = {
|
||||
|
@ -32,7 +43,7 @@ class CategoriesController < ApplicationController
|
|||
parent_category_id: params[:parent_category_id],
|
||||
include_topics: include_topics(parent_category),
|
||||
include_subcategories: include_subcategories,
|
||||
tag: params[:tag]
|
||||
tag: params[:tag],
|
||||
}
|
||||
|
||||
@category_list = CategoryList.new(guardian, category_options)
|
||||
|
@ -40,35 +51,38 @@ class CategoriesController < ApplicationController
|
|||
if category_options[:is_homepage] && SiteSetting.short_site_description.present?
|
||||
@title = "#{SiteSetting.title} - #{SiteSetting.short_site_description}"
|
||||
elsif !category_options[:is_homepage]
|
||||
@title = "#{I18n.t('js.filters.categories.title')} - #{SiteSetting.title}"
|
||||
@title = "#{I18n.t("js.filters.categories.title")} - #{SiteSetting.title}"
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
store_preloaded(@category_list.preload_key, MultiJson.dump(CategoryListSerializer.new(@category_list, scope: guardian)))
|
||||
store_preloaded(
|
||||
@category_list.preload_key,
|
||||
MultiJson.dump(CategoryListSerializer.new(@category_list, scope: guardian)),
|
||||
)
|
||||
|
||||
style = SiteSetting.desktop_category_page_style
|
||||
topic_options = {
|
||||
per_page: CategoriesController.topics_per_page,
|
||||
no_definitions: true,
|
||||
}
|
||||
topic_options = { per_page: CategoriesController.topics_per_page, no_definitions: true }
|
||||
|
||||
if style == "categories_and_latest_topics_created_date"
|
||||
topic_options[:order] = 'created'
|
||||
topic_options[:order] = "created"
|
||||
@topic_list = TopicQuery.new(current_user, topic_options).list_latest
|
||||
@topic_list.more_topics_url = url_for(public_send("latest_path", sort: :created))
|
||||
elsif style == "categories_and_latest_topics"
|
||||
@topic_list = TopicQuery.new(current_user, topic_options).list_latest
|
||||
@topic_list.more_topics_url = url_for(public_send("latest_path"))
|
||||
elsif style == "categories_and_top_topics"
|
||||
@topic_list = TopicQuery.new(current_user, topic_options).list_top_for(SiteSetting.top_page_default_timeframe.to_sym)
|
||||
@topic_list =
|
||||
TopicQuery.new(current_user, topic_options).list_top_for(
|
||||
SiteSetting.top_page_default_timeframe.to_sym,
|
||||
)
|
||||
@topic_list.more_topics_url = url_for(public_send("top_path"))
|
||||
end
|
||||
|
||||
if @topic_list.present? && @topic_list.topics.present?
|
||||
store_preloaded(
|
||||
@topic_list.preload_key,
|
||||
MultiJson.dump(TopicListSerializer.new(@topic_list, scope: guardian))
|
||||
MultiJson.dump(TopicListSerializer.new(@topic_list, scope: guardian)),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -109,7 +123,9 @@ class CategoriesController < ApplicationController
|
|||
by_category = Hash[change_requests.map { |cat, pos| [Category.find(cat.to_i), pos] }]
|
||||
|
||||
unless guardian.is_admin?
|
||||
raise Discourse::InvalidAccess unless by_category.keys.all? { |c| guardian.can_see_category? c }
|
||||
unless by_category.keys.all? { |c| guardian.can_see_category? c }
|
||||
raise Discourse::InvalidAccess
|
||||
end
|
||||
end
|
||||
|
||||
by_category.each do |cat, pos|
|
||||
|
@ -187,14 +203,12 @@ class CategoriesController < ApplicationController
|
|||
@category,
|
||||
old_category_params,
|
||||
old_permissions: old_permissions,
|
||||
old_custom_fields: old_custom_fields
|
||||
old_custom_fields: old_custom_fields,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if result
|
||||
DiscourseEvent.trigger(:category_updated, cat)
|
||||
end
|
||||
DiscourseEvent.trigger(:category_updated, cat) if result
|
||||
|
||||
result
|
||||
end
|
||||
|
@ -207,7 +221,7 @@ class CategoriesController < ApplicationController
|
|||
custom_slug = params[:slug].to_s
|
||||
|
||||
if custom_slug.blank?
|
||||
error = @category.errors.full_message(:slug, I18n.t('errors.messages.blank'))
|
||||
error = @category.errors.full_message(:slug, I18n.t("errors.messages.blank"))
|
||||
render_json_error(error)
|
||||
elsif @category.update(slug: custom_slug)
|
||||
render json: success_json
|
||||
|
@ -221,7 +235,13 @@ class CategoriesController < ApplicationController
|
|||
notification_level = params[:notification_level].to_i
|
||||
|
||||
CategoryUser.set_notification_level_for_category(current_user, notification_level, category_id)
|
||||
render json: success_json.merge({ indirectly_muted_category_ids: CategoryUser.indirectly_muted_category_ids(current_user) })
|
||||
render json:
|
||||
success_json.merge(
|
||||
{
|
||||
indirectly_muted_category_ids:
|
||||
CategoryUser.indirectly_muted_category_ids(current_user),
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -237,32 +257,38 @@ class CategoriesController < ApplicationController
|
|||
|
||||
def find_by_slug
|
||||
params.require(:category_slug)
|
||||
@category = Category.find_by_slug_path(params[:category_slug].split('/'))
|
||||
@category = Category.find_by_slug_path(params[:category_slug].split("/"))
|
||||
|
||||
raise Discourse::NotFound unless @category.present?
|
||||
|
||||
if !guardian.can_see?(@category)
|
||||
if SiteSetting.detailed_404 && group = @category.access_category_via_group
|
||||
raise Discourse::InvalidAccess.new(
|
||||
'not in group',
|
||||
"not in group",
|
||||
@category,
|
||||
custom_message: 'not_in_group.title_category',
|
||||
custom_message_params: { group: group.name },
|
||||
group: group
|
||||
custom_message: "not_in_group.title_category",
|
||||
custom_message_params: {
|
||||
group: group.name,
|
||||
},
|
||||
group: group,
|
||||
)
|
||||
else
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
@category.permission = CategoryGroup.permission_types[:full] if Category.topic_create_allowed(guardian).where(id: @category.id).exists?
|
||||
@category.permission = CategoryGroup.permission_types[:full] if Category
|
||||
.topic_create_allowed(guardian)
|
||||
.where(id: @category.id)
|
||||
.exists?
|
||||
render_serialized(@category, CategorySerializer)
|
||||
end
|
||||
|
||||
def visible_groups
|
||||
@guardian.ensure_can_see!(@category)
|
||||
|
||||
groups = if !@category.groups.exists?(id: Group::AUTO_GROUPS[:everyone])
|
||||
groups =
|
||||
if !@category.groups.exists?(id: Group::AUTO_GROUPS[:everyone])
|
||||
@category.groups.merge(Group.visible_groups(current_user)).pluck("name")
|
||||
end
|
||||
|
||||
|
@ -285,17 +311,14 @@ class CategoriesController < ApplicationController
|
|||
category_options = {
|
||||
is_homepage: current_homepage == "categories",
|
||||
parent_category_id: params[:parent_category_id],
|
||||
include_topics: false
|
||||
include_topics: false,
|
||||
}
|
||||
|
||||
topic_options = {
|
||||
per_page: CategoriesController.topics_per_page,
|
||||
no_definitions: true,
|
||||
}
|
||||
topic_options = { per_page: CategoriesController.topics_per_page, no_definitions: true }
|
||||
|
||||
topic_options.merge!(build_topic_list_options)
|
||||
style = SiteSetting.desktop_category_page_style
|
||||
topic_options[:order] = 'created' if style == "categories_and_latest_topics_created_date"
|
||||
topic_options[:order] = "created" if style == "categories_and_latest_topics_created_date"
|
||||
|
||||
result = CategoryAndTopicLists.new
|
||||
result.category_list = CategoryList.new(guardian, category_options)
|
||||
|
@ -303,8 +326,9 @@ class CategoriesController < ApplicationController
|
|||
if topics_filter == :latest
|
||||
result.topic_list = TopicQuery.new(current_user, topic_options).list_latest
|
||||
elsif topics_filter == :top
|
||||
result.topic_list = TopicQuery.new(current_user, topic_options).list_top_for(
|
||||
SiteSetting.top_page_default_timeframe.to_sym
|
||||
result.topic_list =
|
||||
TopicQuery.new(current_user, topic_options).list_top_for(
|
||||
SiteSetting.top_page_default_timeframe.to_sym,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -316,31 +340,35 @@ class CategoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def required_create_params
|
||||
required_param_keys.each do |key|
|
||||
params.require(key)
|
||||
end
|
||||
required_param_keys.each { |key| params.require(key) }
|
||||
category_params
|
||||
end
|
||||
|
||||
def category_params
|
||||
@category_params ||= begin
|
||||
@category_params ||=
|
||||
begin
|
||||
if p = params[:permissions]
|
||||
p.each do |k, v|
|
||||
p[k] = v.to_i
|
||||
end
|
||||
p.each { |k, v| p[k] = v.to_i }
|
||||
end
|
||||
|
||||
if SiteSetting.tagging_enabled
|
||||
params[:allowed_tags] = params[:allowed_tags].presence || [] if params[:allowed_tags]
|
||||
params[:allowed_tag_groups] = params[:allowed_tag_groups].presence || [] if params[:allowed_tag_groups]
|
||||
params[:required_tag_groups] = params[:required_tag_groups].presence || [] if params[:required_tag_groups]
|
||||
params[:allowed_tag_groups] = params[:allowed_tag_groups].presence || [] if params[
|
||||
:allowed_tag_groups
|
||||
]
|
||||
params[:required_tag_groups] = params[:required_tag_groups].presence || [] if params[
|
||||
:required_tag_groups
|
||||
]
|
||||
end
|
||||
|
||||
if SiteSetting.enable_category_group_moderation?
|
||||
params[:reviewable_by_group_id] = Group.where(name: params[:reviewable_by_group_name]).pluck_first(:id) if params[:reviewable_by_group_name]
|
||||
params[:reviewable_by_group_id] = Group.where(
|
||||
name: params[:reviewable_by_group_name],
|
||||
).pluck_first(:id) if params[:reviewable_by_group_name]
|
||||
end
|
||||
|
||||
result = params.permit(
|
||||
result =
|
||||
params.permit(
|
||||
*required_param_keys,
|
||||
:position,
|
||||
:name,
|
||||
|
@ -380,7 +408,7 @@ class CategoriesController < ApplicationController
|
|||
permissions: [*p.try(:keys)],
|
||||
allowed_tags: [],
|
||||
allowed_tag_groups: [],
|
||||
required_tag_groups: [:name, :min_count]
|
||||
required_tag_groups: %i[name min_count],
|
||||
)
|
||||
|
||||
if result[:required_tag_groups] && !result[:required_tag_groups].is_a?(Array)
|
||||
|
@ -395,9 +423,7 @@ class CategoriesController < ApplicationController
|
|||
keys = params[:custom_fields].try(:keys)
|
||||
return if keys.blank?
|
||||
|
||||
keys.map do |key|
|
||||
params[:custom_fields][key].is_a?(Array) ? { key => [] } : key
|
||||
end
|
||||
keys.map { |key| params[:custom_fields][key].is_a?(Array) ? { key => [] } : key }
|
||||
end
|
||||
|
||||
def fetch_category
|
||||
|
@ -411,12 +437,9 @@ class CategoriesController < ApplicationController
|
|||
|
||||
def include_topics(parent_category = nil)
|
||||
style = SiteSetting.desktop_category_page_style
|
||||
view_context.mobile_view? ||
|
||||
params[:include_topics] ||
|
||||
view_context.mobile_view? || params[:include_topics] ||
|
||||
(parent_category && parent_category.subcategory_list_includes_topics?) ||
|
||||
style == "categories_with_featured_topics" ||
|
||||
style == "subcategories_with_featured_topics" ||
|
||||
style == "categories_boxes_with_topics" ||
|
||||
style == "categories_with_top_topics"
|
||||
style == "categories_with_featured_topics" || style == "subcategories_with_featured_topics" ||
|
||||
style == "categories_boxes_with_topics" || style == "categories_with_top_topics"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,17 +4,16 @@ class ClicksController < ApplicationController
|
|||
skip_before_action :check_xhr, :preload_json, :verify_authenticity_token
|
||||
|
||||
def track
|
||||
params.require([:url, :post_id, :topic_id])
|
||||
params.require(%i[url post_id topic_id])
|
||||
|
||||
TopicLinkClick.create_from(
|
||||
url: params[:url],
|
||||
post_id: params[:post_id],
|
||||
topic_id: params[:topic_id],
|
||||
ip: request.remote_ip,
|
||||
user_id: current_user&.id
|
||||
user_id: current_user&.id,
|
||||
)
|
||||
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,8 +13,9 @@ class ComposerController < ApplicationController
|
|||
end
|
||||
|
||||
# allowed_names is necessary just for new private messages.
|
||||
@allowed_names = if params[:allowed_names].present?
|
||||
raise Discourse::InvalidParameters(:allowed_names) if !params[:allowed_names].is_a?(Array)
|
||||
@allowed_names =
|
||||
if params[:allowed_names].present?
|
||||
raise Discourse.InvalidParameters(:allowed_names) if !params[:allowed_names].is_a?(Array)
|
||||
params[:allowed_names] << current_user.username
|
||||
else
|
||||
[]
|
||||
|
@ -33,29 +34,35 @@ class ComposerController < ApplicationController
|
|||
end
|
||||
|
||||
if @topic && @names.include?(SiteSetting.here_mention) && guardian.can_mention_here?
|
||||
here_count = PostAlerter.new.expand_here_mention(@topic.first_post, exclude_ids: [current_user.id]).size
|
||||
here_count =
|
||||
PostAlerter.new.expand_here_mention(@topic.first_post, exclude_ids: [current_user.id]).size
|
||||
end
|
||||
|
||||
serialized_groups = groups.values.reduce({}) do |hash, group|
|
||||
serialized_groups =
|
||||
groups
|
||||
.values
|
||||
.reduce({}) do |hash, group|
|
||||
serialized_group = { user_count: group.user_count }
|
||||
|
||||
if group_reasons[group.name] == :not_allowed &&
|
||||
members_visible_group_ids.include?(group.id) &&
|
||||
(@topic&.private_message? || @allowed_names.present?)
|
||||
|
||||
# Find users that are notified already because they have been invited
|
||||
# directly or via a group
|
||||
notified_count = GroupUser
|
||||
notified_count =
|
||||
GroupUser
|
||||
# invited directly
|
||||
.where(user_id: topic_allowed_user_ids)
|
||||
.or(
|
||||
# invited via a group
|
||||
GroupUser.where(
|
||||
user_id: GroupUser.where(group_id: topic_allowed_group_ids).select(:user_id)
|
||||
)
|
||||
user_id: GroupUser.where(group_id: topic_allowed_group_ids).select(:user_id),
|
||||
),
|
||||
)
|
||||
.where(group_id: group.id)
|
||||
.select(:user_id).distinct.count
|
||||
.select(:user_id)
|
||||
.distinct
|
||||
.count
|
||||
|
||||
if notified_count > 0
|
||||
group_reasons[group.name] = :some_not_allowed
|
||||
|
@ -80,14 +87,17 @@ class ComposerController < ApplicationController
|
|||
private
|
||||
|
||||
def user_reason(user)
|
||||
reason = if @topic && !user.guardian.can_see?(@topic)
|
||||
reason =
|
||||
if @topic && !user.guardian.can_see?(@topic)
|
||||
@topic.private_message? ? :private : :category
|
||||
elsif @allowed_names.present? && !is_user_allowed?(user, topic_allowed_user_ids, topic_allowed_group_ids)
|
||||
elsif @allowed_names.present? &&
|
||||
!is_user_allowed?(user, topic_allowed_user_ids, topic_allowed_group_ids)
|
||||
# This would normally be handled by the previous if, but that does not work for new private messages.
|
||||
:private
|
||||
elsif topic_muted_by.include?(user.id)
|
||||
:muted_topic
|
||||
elsif @topic&.private_message? && !is_user_allowed?(user, topic_allowed_user_ids, topic_allowed_group_ids)
|
||||
elsif @topic&.private_message? &&
|
||||
!is_user_allowed?(user, topic_allowed_user_ids, topic_allowed_group_ids)
|
||||
# Admins can see the topic, but they will not be mentioned if they were not invited.
|
||||
:not_allowed
|
||||
end
|
||||
|
@ -101,7 +111,8 @@ class ComposerController < ApplicationController
|
|||
def group_reason(group)
|
||||
if !mentionable_group_ids.include?(group.id)
|
||||
:not_mentionable
|
||||
elsif (@topic&.private_message? || @allowed_names.present?) && !topic_allowed_group_ids.include?(group.id)
|
||||
elsif (@topic&.private_message? || @allowed_names.present?) &&
|
||||
!topic_allowed_group_ids.include?(group.id)
|
||||
:not_allowed
|
||||
end
|
||||
end
|
||||
|
@ -111,37 +122,31 @@ class ComposerController < ApplicationController
|
|||
end
|
||||
|
||||
def users
|
||||
@users ||= User
|
||||
.not_staged
|
||||
.where(username_lower: @names.map(&:downcase))
|
||||
.index_by(&:username_lower)
|
||||
@users ||=
|
||||
User.not_staged.where(username_lower: @names.map(&:downcase)).index_by(&:username_lower)
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= Group
|
||||
@groups ||=
|
||||
Group
|
||||
.visible_groups(current_user)
|
||||
.where('lower(name) IN (?)', @names.map(&:downcase))
|
||||
.where("lower(name) IN (?)", @names.map(&:downcase))
|
||||
.index_by(&:name)
|
||||
end
|
||||
|
||||
def mentionable_group_ids
|
||||
@mentionable_group_ids ||= Group
|
||||
.mentionable(current_user, include_public: false)
|
||||
.where(name: @names)
|
||||
.pluck(:id)
|
||||
.to_set
|
||||
@mentionable_group_ids ||=
|
||||
Group.mentionable(current_user, include_public: false).where(name: @names).pluck(:id).to_set
|
||||
end
|
||||
|
||||
def members_visible_group_ids
|
||||
@members_visible_group_ids ||= Group
|
||||
.members_visible_groups(current_user)
|
||||
.where(name: @names)
|
||||
.pluck(:id)
|
||||
.to_set
|
||||
@members_visible_group_ids ||=
|
||||
Group.members_visible_groups(current_user).where(name: @names).pluck(:id).to_set
|
||||
end
|
||||
|
||||
def topic_muted_by
|
||||
@topic_muted_by ||= if @topic.present?
|
||||
@topic_muted_by ||=
|
||||
if @topic.present?
|
||||
TopicUser
|
||||
.where(topic: @topic)
|
||||
.where(user_id: users.values.map(&:id))
|
||||
|
@ -154,31 +159,20 @@ class ComposerController < ApplicationController
|
|||
end
|
||||
|
||||
def topic_allowed_user_ids
|
||||
@topic_allowed_user_ids ||= if @allowed_names.present?
|
||||
User
|
||||
.where(username_lower: @allowed_names.map(&:downcase))
|
||||
.pluck(:id)
|
||||
.to_set
|
||||
@topic_allowed_user_ids ||=
|
||||
if @allowed_names.present?
|
||||
User.where(username_lower: @allowed_names.map(&:downcase)).pluck(:id).to_set
|
||||
elsif @topic&.private_message?
|
||||
TopicAllowedUser
|
||||
.where(topic: @topic)
|
||||
.pluck(:user_id)
|
||||
.to_set
|
||||
TopicAllowedUser.where(topic: @topic).pluck(:user_id).to_set
|
||||
end
|
||||
end
|
||||
|
||||
def topic_allowed_group_ids
|
||||
@topic_allowed_group_ids ||= if @allowed_names.present?
|
||||
Group
|
||||
.messageable(current_user)
|
||||
.where(name: @allowed_names)
|
||||
.pluck(:id)
|
||||
.to_set
|
||||
@topic_allowed_group_ids ||=
|
||||
if @allowed_names.present?
|
||||
Group.messageable(current_user).where(name: @allowed_names).pluck(:id).to_set
|
||||
elsif @topic&.private_message?
|
||||
TopicAllowedGroup
|
||||
.where(topic: @topic)
|
||||
.pluck(:group_id)
|
||||
.to_set
|
||||
TopicAllowedGroup.where(topic: @topic).pluck(:group_id).to_set
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ComposerMessagesController < ApplicationController
|
||||
|
||||
requires_login
|
||||
|
||||
def index
|
||||
finder = ComposerMessagesFinder.new(current_user, params.slice(:composer_action, :topic_id, :post_id))
|
||||
finder =
|
||||
ComposerMessagesFinder.new(current_user, params.slice(:composer_action, :topic_id, :post_id))
|
||||
json = { composer_messages: [finder.find].compact }
|
||||
|
||||
if params[:topic_id].present?
|
||||
|
@ -25,14 +25,24 @@ class ComposerMessagesController < ApplicationController
|
|||
warning_message = nil
|
||||
|
||||
if user_count > 0
|
||||
message_locale = if user_count == 1
|
||||
message_locale =
|
||||
if user_count == 1
|
||||
"education.user_not_seen_in_a_while.single"
|
||||
else
|
||||
"education.user_not_seen_in_a_while.multiple"
|
||||
end
|
||||
end
|
||||
|
||||
json = { user_count: user_count, usernames: users, time_ago: FreedomPatches::Rails4.time_ago_in_words(SiteSetting.pm_warn_user_last_seen_months_ago.month.ago, true, scope: :'datetime.distance_in_words_verbose') }
|
||||
json = {
|
||||
user_count: user_count,
|
||||
usernames: users,
|
||||
time_ago:
|
||||
FreedomPatches::Rails4.time_ago_in_words(
|
||||
SiteSetting.pm_warn_user_last_seen_months_ago.month.ago,
|
||||
true,
|
||||
scope: :"datetime.distance_in_words_verbose",
|
||||
),
|
||||
}
|
||||
render_json_dump(json)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,12 +10,11 @@ class CspReportsController < ApplicationController
|
|||
if report.blank?
|
||||
render_json_error("empty CSP report", status: 422)
|
||||
else
|
||||
Logster.add_to_env(request.env, 'CSP Report', report)
|
||||
Rails.logger.warn("CSP Violation: '#{report['blocked-uri']}' \n\n#{report['script-sample']}")
|
||||
Logster.add_to_env(request.env, "CSP Report", report)
|
||||
Rails.logger.warn("CSP Violation: '#{report["blocked-uri"]}' \n\n#{report["script-sample"]}")
|
||||
|
||||
head :ok
|
||||
end
|
||||
|
||||
rescue JSON::ParserError
|
||||
render_json_error("invalid CSP report", status: 422)
|
||||
end
|
||||
|
@ -25,20 +24,20 @@ class CspReportsController < ApplicationController
|
|||
def parse_report
|
||||
obj = JSON.parse(request.body.read)
|
||||
if Hash === obj
|
||||
obj = obj['csp-report']
|
||||
obj = obj["csp-report"]
|
||||
if Hash === obj
|
||||
obj.slice(
|
||||
'blocked-uri',
|
||||
'disposition',
|
||||
'document-uri',
|
||||
'effective-directive',
|
||||
'original-policy',
|
||||
'referrer',
|
||||
'script-sample',
|
||||
'status-code',
|
||||
'violated-directive',
|
||||
'line-number',
|
||||
'source-file'
|
||||
"blocked-uri",
|
||||
"disposition",
|
||||
"document-uri",
|
||||
"effective-directive",
|
||||
"original-policy",
|
||||
"referrer",
|
||||
"script-sample",
|
||||
"status-code",
|
||||
"violated-directive",
|
||||
"line-number",
|
||||
"source-file",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
class DirectoryColumnsController < ApplicationController
|
||||
def index
|
||||
directory_columns = DirectoryColumn.includes(:user_field).where(enabled: true).order(:position)
|
||||
render_json_dump(directory_columns: serialize_data(directory_columns, DirectoryColumnSerializer))
|
||||
render_json_dump(
|
||||
directory_columns: serialize_data(directory_columns, DirectoryColumnSerializer),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,9 @@ class DirectoryItemsController < ApplicationController
|
|||
PAGE_SIZE = 50
|
||||
|
||||
def index
|
||||
raise Discourse::InvalidAccess.new(:enable_user_directory) unless SiteSetting.enable_user_directory?
|
||||
unless SiteSetting.enable_user_directory?
|
||||
raise Discourse::InvalidAccess.new(:enable_user_directory)
|
||||
end
|
||||
|
||||
period = params.require(:period)
|
||||
period_type = DirectoryItem.period_types[period.to_sym]
|
||||
|
@ -23,30 +25,36 @@ class DirectoryItemsController < ApplicationController
|
|||
end
|
||||
|
||||
if params[:exclude_usernames]
|
||||
result = result.references(:user).where.not(users: { username: params[:exclude_usernames].split(",") })
|
||||
result =
|
||||
result
|
||||
.references(:user)
|
||||
.where.not(users: { username: params[:exclude_usernames].split(",") })
|
||||
end
|
||||
|
||||
order = params[:order] || DirectoryColumn.automatic_column_names.first
|
||||
dir = params[:asc] ? 'ASC' : 'DESC'
|
||||
dir = params[:asc] ? "ASC" : "DESC"
|
||||
active_directory_column_names = DirectoryColumn.active_column_names
|
||||
if active_directory_column_names.include?(order.to_sym)
|
||||
result = result.order("directory_items.#{order} #{dir}, directory_items.id")
|
||||
elsif params[:order] === 'username'
|
||||
elsif params[:order] === "username"
|
||||
result = result.order("users.#{order} #{dir}, directory_items.id")
|
||||
else
|
||||
# Ordering by user field value
|
||||
user_field = UserField.find_by(name: params[:order])
|
||||
if user_field
|
||||
result = result
|
||||
result =
|
||||
result
|
||||
.references(:user)
|
||||
.joins("LEFT OUTER JOIN user_custom_fields ON user_custom_fields.user_id = users.id AND user_custom_fields.name = 'user_field_#{user_field.id}'")
|
||||
.order("user_custom_fields.name = 'user_field_#{user_field.id}' ASC, user_custom_fields.value #{dir}")
|
||||
.joins(
|
||||
"LEFT OUTER JOIN user_custom_fields ON user_custom_fields.user_id = users.id AND user_custom_fields.name = 'user_field_#{user_field.id}'",
|
||||
)
|
||||
.order(
|
||||
"user_custom_fields.name = 'user_field_#{user_field.id}' ASC, user_custom_fields.value #{dir}",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if period_type == DirectoryItem.period_types[:all]
|
||||
result = result.includes(:user_stat)
|
||||
end
|
||||
result = result.includes(:user_stat) if period_type == DirectoryItem.period_types[:all]
|
||||
page = params[:page].to_i
|
||||
|
||||
user_ids = nil
|
||||
|
@ -54,12 +62,10 @@ class DirectoryItemsController < ApplicationController
|
|||
user_ids = UserSearch.new(params[:name], include_staged_users: true).search.pluck(:id)
|
||||
if user_ids.present?
|
||||
# Add the current user if we have at least one other match
|
||||
if current_user && result.dup.where(user_id: user_ids).exists?
|
||||
user_ids << current_user.id
|
||||
end
|
||||
user_ids << current_user.id if current_user && result.dup.where(user_id: user_ids).exists?
|
||||
result = result.where(user_id: user_ids)
|
||||
else
|
||||
result = result.where('false')
|
||||
result = result.where("false")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -68,7 +74,7 @@ class DirectoryItemsController < ApplicationController
|
|||
if user_id
|
||||
result = result.where(user_id: user_id)
|
||||
else
|
||||
result = result.where('false')
|
||||
result = result.where("false")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -82,7 +88,6 @@ class DirectoryItemsController < ApplicationController
|
|||
|
||||
# Put yourself at the top of the first page
|
||||
if result.present? && current_user.present? && page == 0 && !params[:group].present?
|
||||
|
||||
position = result.index { |r| r.user_id == current_user.id }
|
||||
|
||||
# Don't show the record unless you're not in the top positions already
|
||||
|
@ -90,7 +95,6 @@ class DirectoryItemsController < ApplicationController
|
|||
your_item = DirectoryItem.where(period_type: period_type, user_id: current_user.id).first
|
||||
result.insert(0, your_item) if your_item
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
last_updated_at = DirectoryItem.last_updated_at(period_type)
|
||||
|
@ -101,7 +105,9 @@ class DirectoryItemsController < ApplicationController
|
|||
|
||||
user_field_ids = params[:user_field_ids]&.split("|")&.map(&:to_i)
|
||||
user_field_ids.each do |user_field_id|
|
||||
serializer_opts[:user_custom_field_map]["#{User::USER_FIELD_PREFIX}#{user_field_id}"] = user_field_id
|
||||
serializer_opts[:user_custom_field_map][
|
||||
"#{User::USER_FIELD_PREFIX}#{user_field_id}"
|
||||
] = user_field_id
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -112,12 +118,13 @@ class DirectoryItemsController < ApplicationController
|
|||
serializer_opts[:attributes] = active_directory_column_names
|
||||
|
||||
serialized = serialize_data(result, DirectoryItemSerializer, serializer_opts)
|
||||
render_json_dump(directory_items: serialized,
|
||||
render_json_dump(
|
||||
directory_items: serialized,
|
||||
meta: {
|
||||
last_updated_at: last_updated_at,
|
||||
total_rows_directory_items: result_count,
|
||||
load_more_directory_items: load_more_directory_items_json
|
||||
}
|
||||
load_more_directory_items: load_more_directory_items_json,
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,11 +6,23 @@ class DoNotDisturbController < ApplicationController
|
|||
def create
|
||||
raise Discourse::InvalidParameters.new(:duration) if params[:duration].blank?
|
||||
|
||||
duration_minutes = (Integer(params[:duration]) rescue false)
|
||||
duration_minutes =
|
||||
(
|
||||
begin
|
||||
Integer(params[:duration])
|
||||
rescue StandardError
|
||||
false
|
||||
end
|
||||
)
|
||||
|
||||
ends_at = duration_minutes ?
|
||||
ends_at_from_minutes(duration_minutes) :
|
||||
ends_at =
|
||||
(
|
||||
if duration_minutes
|
||||
ends_at_from_minutes(duration_minutes)
|
||||
else
|
||||
ends_at_from_string(params[:duration])
|
||||
end
|
||||
)
|
||||
|
||||
new_timing = current_user.do_not_disturb_timings.new(starts_at: Time.zone.now, ends_at: ends_at)
|
||||
|
||||
|
@ -37,7 +49,7 @@ class DoNotDisturbController < ApplicationController
|
|||
end
|
||||
|
||||
def ends_at_from_string(string)
|
||||
if string == 'tomorrow'
|
||||
if string == "tomorrow"
|
||||
Time.now.end_of_day.utc
|
||||
else
|
||||
raise Discourse::InvalidParameters.new(:duration)
|
||||
|
|
|
@ -9,15 +9,9 @@ class DraftsController < ApplicationController
|
|||
params.permit(:offset)
|
||||
params.permit(:limit)
|
||||
|
||||
stream = Draft.stream(
|
||||
user: current_user,
|
||||
offset: params[:offset],
|
||||
limit: params[:limit]
|
||||
)
|
||||
stream = Draft.stream(user: current_user, offset: params[:offset], limit: params[:limit])
|
||||
|
||||
render json: {
|
||||
drafts: stream ? serialize_data(stream, DraftSerializer) : []
|
||||
}
|
||||
render json: { drafts: stream ? serialize_data(stream, DraftSerializer) : [] }
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -38,10 +32,9 @@ class DraftsController < ApplicationController
|
|||
params[:sequence].to_i,
|
||||
params[:data],
|
||||
params[:owner],
|
||||
force_save: params[:force_save]
|
||||
force_save: params[:force_save],
|
||||
)
|
||||
rescue Draft::OutOfSequence
|
||||
|
||||
begin
|
||||
if !Draft.exists?(user_id: current_user.id, draft_key: params[:draft_key])
|
||||
Draft.set(
|
||||
|
@ -49,17 +42,16 @@ class DraftsController < ApplicationController
|
|||
params[:draft_key],
|
||||
DraftSequence.current(current_user, params[:draft_key]),
|
||||
params[:data],
|
||||
params[:owner]
|
||||
params[:owner],
|
||||
)
|
||||
else
|
||||
raise Draft::OutOfSequence
|
||||
end
|
||||
|
||||
rescue Draft::OutOfSequence
|
||||
render_json_error I18n.t('draft.sequence_conflict_error.title'),
|
||||
render_json_error I18n.t("draft.sequence_conflict_error.title"),
|
||||
status: 409,
|
||||
extras: {
|
||||
description: I18n.t('draft.sequence_conflict_error.description')
|
||||
description: I18n.t("draft.sequence_conflict_error.description"),
|
||||
}
|
||||
return
|
||||
end
|
||||
|
@ -68,7 +60,7 @@ class DraftsController < ApplicationController
|
|||
json = success_json.merge(draft_sequence: sequence)
|
||||
|
||||
begin
|
||||
data = JSON::parse(params[:data])
|
||||
data = JSON.parse(params[:data])
|
||||
rescue JSON::ParserError
|
||||
raise Discourse::InvalidParameters.new(:data)
|
||||
end
|
||||
|
@ -76,7 +68,8 @@ class DraftsController < ApplicationController
|
|||
if data.present?
|
||||
# this is a bit of a kludge we need to remove (all the parsing) too many special cases here
|
||||
# we need to catch action edit and action editSharedDraft
|
||||
if data["postId"].present? && data["originalText"].present? && data["action"].to_s.start_with?("edit")
|
||||
if data["postId"].present? && data["originalText"].present? &&
|
||||
data["action"].to_s.start_with?("edit")
|
||||
post = Post.find_by(id: data["postId"])
|
||||
if post && post.raw != data["originalText"]
|
||||
conflict_user = BasicUserSerializer.new(post.last_editor, root: false)
|
||||
|
|
|
@ -18,14 +18,20 @@ class EditDirectoryColumnsController < ApplicationController
|
|||
directory_column_params = params.permit(directory_columns: {})
|
||||
directory_columns = DirectoryColumn.all
|
||||
|
||||
has_enabled_column = directory_column_params[:directory_columns].values.any? do |column_data|
|
||||
has_enabled_column =
|
||||
directory_column_params[:directory_columns].values.any? do |column_data|
|
||||
column_data[:enabled].to_s == "true"
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Must have at least one column enabled" unless has_enabled_column
|
||||
unless has_enabled_column
|
||||
raise Discourse::InvalidParameters, "Must have at least one column enabled"
|
||||
end
|
||||
|
||||
directory_column_params[:directory_columns].values.each do |column_data|
|
||||
existing_column = directory_columns.detect { |c| c.id == column_data[:id].to_i }
|
||||
if (existing_column.enabled != column_data[:enabled] || existing_column.position != column_data[:position].to_i)
|
||||
if (
|
||||
existing_column.enabled != column_data[:enabled] ||
|
||||
existing_column.position != column_data[:position].to_i
|
||||
)
|
||||
existing_column.update(enabled: column_data[:enabled], position: column_data[:position])
|
||||
end
|
||||
end
|
||||
|
@ -37,7 +43,8 @@ class EditDirectoryColumnsController < ApplicationController
|
|||
|
||||
def ensure_user_fields_have_columns
|
||||
user_fields_without_column =
|
||||
UserField.left_outer_joins(:directory_column)
|
||||
UserField
|
||||
.left_outer_joins(:directory_column)
|
||||
.where(directory_column: { user_field_id: nil })
|
||||
.where("show_on_profile=? OR show_on_user_card=?", true, true)
|
||||
|
||||
|
@ -47,12 +54,14 @@ class EditDirectoryColumnsController < ApplicationController
|
|||
|
||||
new_directory_column_attrs = []
|
||||
user_fields_without_column.each do |user_field|
|
||||
new_directory_column_attrs.push({
|
||||
new_directory_column_attrs.push(
|
||||
{
|
||||
user_field_id: user_field.id,
|
||||
enabled: false,
|
||||
type: DirectoryColumn.types[:user_field],
|
||||
position: next_position
|
||||
})
|
||||
position: next_position,
|
||||
},
|
||||
)
|
||||
|
||||
next_position += 1
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class EmailController < ApplicationController
|
||||
layout 'no_ember'
|
||||
layout "no_ember"
|
||||
|
||||
skip_before_action :check_xhr, :preload_json, :redirect_to_login_if_required
|
||||
|
||||
|
@ -11,9 +11,7 @@ class EmailController < ApplicationController
|
|||
@key_owner_found = key&.user.present?
|
||||
|
||||
if @found && @key_owner_found
|
||||
UnsubscribeKey
|
||||
.get_unsubscribe_strategy_for(key)
|
||||
&.prepare_unsubscribe_options(self)
|
||||
UnsubscribeKey.get_unsubscribe_strategy_for(key)&.prepare_unsubscribe_options(self)
|
||||
|
||||
if current_user.present? && (@user != current_user)
|
||||
@different_user = @user.name
|
||||
|
@ -28,17 +26,14 @@ class EmailController < ApplicationController
|
|||
key = UnsubscribeKey.find_by(key: params[:key])
|
||||
raise Discourse::NotFound if key.nil? || key.user.nil?
|
||||
user = key.user
|
||||
updated = UnsubscribeKey.get_unsubscribe_strategy_for(key)
|
||||
&.unsubscribe(params)
|
||||
updated = UnsubscribeKey.get_unsubscribe_strategy_for(key)&.unsubscribe(params)
|
||||
|
||||
if updated
|
||||
cache_key = "unsub_#{SecureRandom.hex}"
|
||||
Discourse.cache.write cache_key, user.email, expires_in: 1.hour
|
||||
|
||||
url = path("/email/unsubscribed?key=#{cache_key}")
|
||||
if key.associated_topic
|
||||
url += "&topic_id=#{key.associated_topic.id}"
|
||||
end
|
||||
url += "&topic_id=#{key.associated_topic.id}" if key.associated_topic
|
||||
|
||||
redirect_to url
|
||||
else
|
||||
|
|
|
@ -5,10 +5,10 @@ class EmbedController < ApplicationController
|
|||
|
||||
skip_before_action :check_xhr, :preload_json, :verify_authenticity_token
|
||||
|
||||
before_action :prepare_embeddable, except: [ :info ]
|
||||
before_action :ensure_api_request, only: [ :info ]
|
||||
before_action :prepare_embeddable, except: [:info]
|
||||
before_action :ensure_api_request, only: [:info]
|
||||
|
||||
layout 'embed'
|
||||
layout "embed"
|
||||
|
||||
rescue_from Discourse::InvalidAccess do
|
||||
if current_user.try(:admin?)
|
||||
|
@ -16,14 +16,14 @@ class EmbedController < ApplicationController
|
|||
@show_reason = true
|
||||
@hosts = EmbeddableHost.all
|
||||
end
|
||||
render 'embed_error', status: 400
|
||||
render "embed_error", status: 400
|
||||
end
|
||||
|
||||
def topics
|
||||
discourse_expires_in 1.minute
|
||||
|
||||
unless SiteSetting.embed_topics_list?
|
||||
render 'embed_topics_error', status: 400
|
||||
render "embed_topics_error", status: 400
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -32,10 +32,12 @@ class EmbedController < ApplicationController
|
|||
end
|
||||
|
||||
if @embed_class = params[:embed_class]
|
||||
raise Discourse::InvalidParameters.new(:embed_class) unless @embed_class =~ /^[a-zA-Z0-9\-_]+$/
|
||||
unless @embed_class =~ /^[a-zA-Z0-9\-_]+$/
|
||||
raise Discourse::InvalidParameters.new(:embed_class)
|
||||
end
|
||||
end
|
||||
|
||||
response.headers['X-Robots-Tag'] = 'noindex, indexifembedded'
|
||||
response.headers["X-Robots-Tag"] = "noindex, indexifembedded"
|
||||
|
||||
if params.has_key?(:template) && params[:template] == "complete"
|
||||
@template = "complete"
|
||||
|
@ -46,8 +48,7 @@ class EmbedController < ApplicationController
|
|||
list_options = build_topic_list_options
|
||||
|
||||
if params.has_key?(:per_page)
|
||||
list_options[:per_page] =
|
||||
[params[:per_page].to_i, SiteSetting.embed_topic_limit_per_page].min
|
||||
list_options[:per_page] = [params[:per_page].to_i, SiteSetting.embed_topic_limit_per_page].min
|
||||
end
|
||||
|
||||
if params[:allow_create]
|
||||
|
@ -67,7 +68,8 @@ class EmbedController < ApplicationController
|
|||
valid_top_period = false
|
||||
end
|
||||
|
||||
@list = if valid_top_period
|
||||
@list =
|
||||
if valid_top_period
|
||||
topic_query.list_top_for(top_period)
|
||||
else
|
||||
topic_query.list_latest
|
||||
|
@ -80,7 +82,7 @@ class EmbedController < ApplicationController
|
|||
embed_topic_id = params[:topic_id]&.to_i
|
||||
|
||||
unless embed_topic_id || EmbeddableHost.url_allowed?(embed_url)
|
||||
raise Discourse::InvalidAccess.new('invalid embed host')
|
||||
raise Discourse::InvalidAccess.new("invalid embed host")
|
||||
end
|
||||
|
||||
topic_id = nil
|
||||
|
@ -91,28 +93,33 @@ class EmbedController < ApplicationController
|
|||
end
|
||||
|
||||
if topic_id
|
||||
@topic_view = TopicView.new(topic_id,
|
||||
@topic_view =
|
||||
TopicView.new(
|
||||
topic_id,
|
||||
current_user,
|
||||
limit: SiteSetting.embed_post_limit,
|
||||
only_regular: true,
|
||||
exclude_first: true,
|
||||
exclude_deleted_users: true,
|
||||
exclude_hidden: true)
|
||||
exclude_hidden: true,
|
||||
)
|
||||
raise Discourse::NotFound if @topic_view.blank?
|
||||
|
||||
@posts_left = 0
|
||||
@second_post_url = "#{@topic_view.topic.url}/2"
|
||||
@reply_count = @topic_view.filtered_posts.count - 1
|
||||
@reply_count = 0 if @reply_count < 0
|
||||
@posts_left = @reply_count - SiteSetting.embed_post_limit if @reply_count > SiteSetting.embed_post_limit
|
||||
@posts_left = @reply_count - SiteSetting.embed_post_limit if @reply_count >
|
||||
SiteSetting.embed_post_limit
|
||||
elsif embed_url.present?
|
||||
Jobs.enqueue(:retrieve_topic,
|
||||
Jobs.enqueue(
|
||||
:retrieve_topic,
|
||||
user_id: current_user.try(:id),
|
||||
embed_url: embed_url,
|
||||
author_username: embed_username,
|
||||
referer: request.env['HTTP_REFERER']
|
||||
referer: request.env["HTTP_REFERER"],
|
||||
)
|
||||
render 'loading'
|
||||
render "loading"
|
||||
end
|
||||
|
||||
discourse_expires_in 1.minute
|
||||
|
@ -132,16 +139,16 @@ class EmbedController < ApplicationController
|
|||
by_url = {}
|
||||
|
||||
if embed_urls.present?
|
||||
urls = embed_urls.map { |u| u.sub(/#discourse-comments$/, '').sub(/\/$/, '') }
|
||||
urls = embed_urls.map { |u| u.sub(/#discourse-comments$/, "").sub(%r{/$}, "") }
|
||||
topic_embeds = TopicEmbed.where(embed_url: urls).includes(:topic).references(:topic)
|
||||
|
||||
topic_embeds.each do |te|
|
||||
url = te.embed_url
|
||||
url = "#{url}#discourse-comments" unless params[:embed_url].include?(url)
|
||||
if te.topic.present?
|
||||
by_url[url] = I18n.t('embed.replies', count: te.topic.posts_count - 1)
|
||||
by_url[url] = I18n.t("embed.replies", count: te.topic.posts_count - 1)
|
||||
else
|
||||
by_url[url] = I18n.t('embed.replies', count: 0)
|
||||
by_url[url] = I18n.t("embed.replies", count: 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -152,16 +159,18 @@ class EmbedController < ApplicationController
|
|||
private
|
||||
|
||||
def prepare_embeddable
|
||||
response.headers.delete('X-Frame-Options')
|
||||
response.headers.delete("X-Frame-Options")
|
||||
@embeddable_css_class = ""
|
||||
embeddable_host = EmbeddableHost.record_for_url(request.referer)
|
||||
@embeddable_css_class = " class=\"#{embeddable_host.class_name}\"" if embeddable_host.present? && embeddable_host.class_name.present?
|
||||
@embeddable_css_class =
|
||||
" class=\"#{embeddable_host.class_name}\"" if embeddable_host.present? &&
|
||||
embeddable_host.class_name.present?
|
||||
|
||||
@data_referer = request.referer
|
||||
@data_referer = '*' if SiteSetting.embed_any_origin? && @data_referer.blank?
|
||||
@data_referer = "*" if SiteSetting.embed_any_origin? && @data_referer.blank?
|
||||
end
|
||||
|
||||
def ensure_api_request
|
||||
raise Discourse::InvalidAccess.new('api key not set') if !is_api?
|
||||
raise Discourse::InvalidAccess.new("api key not set") if !is_api?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,5 +12,4 @@ class ExceptionsController < ApplicationController
|
|||
def not_found_body
|
||||
render html: build_not_found_page(status: 200)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ExportCsvController < ApplicationController
|
||||
|
||||
skip_before_action :preload_json, :check_xhr, only: [:show]
|
||||
|
||||
def export_entity
|
||||
guardian.ensure_can_export_entity!(export_params[:entity])
|
||||
|
||||
if export_params[:entity] == 'user_archive'
|
||||
if export_params[:entity] == "user_archive"
|
||||
Jobs.enqueue(:export_user_archive, user_id: current_user.id, args: export_params[:args])
|
||||
else
|
||||
Jobs.enqueue(:export_csv_file, entity: export_params[:entity], user_id: current_user.id, args: export_params[:args])
|
||||
Jobs.enqueue(
|
||||
:export_csv_file,
|
||||
entity: export_params[:entity],
|
||||
user_id: current_user.id,
|
||||
args: export_params[:args],
|
||||
)
|
||||
end
|
||||
StaffActionLogger.new(current_user).log_entity_export(export_params[:entity])
|
||||
render json: success_json
|
||||
|
@ -21,7 +25,8 @@ class ExportCsvController < ApplicationController
|
|||
private
|
||||
|
||||
def export_params
|
||||
@_export_params ||= begin
|
||||
@_export_params ||=
|
||||
begin
|
||||
params.require(:entity)
|
||||
params.permit(:entity, args: Report::FILTERS).to_h
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ class ExtraLocalesController < ApplicationController
|
|||
:redirect_to_login_if_required,
|
||||
:verify_authenticity_token
|
||||
|
||||
OVERRIDES_BUNDLE ||= 'overrides'
|
||||
OVERRIDES_BUNDLE ||= "overrides"
|
||||
MD5_HASH_LENGTH ||= 32
|
||||
|
||||
def show
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
class FinishInstallationController < ApplicationController
|
||||
skip_before_action :check_xhr, :preload_json, :redirect_to_login_if_required
|
||||
layout 'finish_installation'
|
||||
layout "finish_installation"
|
||||
|
||||
before_action :ensure_no_admins, except: ['confirm_email', 'resend_email']
|
||||
before_action :ensure_no_admins, except: %w[confirm_email resend_email]
|
||||
|
||||
def index
|
||||
end
|
||||
|
@ -61,7 +61,9 @@ class FinishInstallationController < ApplicationController
|
|||
end
|
||||
|
||||
def find_allowed_emails
|
||||
return [] unless GlobalSetting.respond_to?(:developer_emails) && GlobalSetting.developer_emails.present?
|
||||
unless GlobalSetting.respond_to?(:developer_emails) && GlobalSetting.developer_emails.present?
|
||||
return []
|
||||
end
|
||||
GlobalSetting.developer_emails.split(",").map(&:strip)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,44 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class GroupsController < ApplicationController
|
||||
requires_login only: [
|
||||
:set_notifications,
|
||||
:mentionable,
|
||||
:messageable,
|
||||
:check_name,
|
||||
:update,
|
||||
:histories,
|
||||
:request_membership,
|
||||
:search,
|
||||
:new,
|
||||
:test_email_settings
|
||||
requires_login only: %i[
|
||||
set_notifications
|
||||
mentionable
|
||||
messageable
|
||||
check_name
|
||||
update
|
||||
histories
|
||||
request_membership
|
||||
search
|
||||
new
|
||||
test_email_settings
|
||||
]
|
||||
|
||||
skip_before_action :preload_json, :check_xhr, only: [:posts_feed, :mentions_feed]
|
||||
skip_before_action :preload_json, :check_xhr, only: %i[posts_feed mentions_feed]
|
||||
skip_before_action :check_xhr, only: [:show]
|
||||
after_action :add_noindex_header
|
||||
|
||||
TYPE_FILTERS = {
|
||||
my: Proc.new { |groups, user|
|
||||
my:
|
||||
Proc.new do |groups, user|
|
||||
raise Discourse::NotFound unless user
|
||||
Group.member_of(groups, user)
|
||||
},
|
||||
owner: Proc.new { |groups, user|
|
||||
end,
|
||||
owner:
|
||||
Proc.new do |groups, user|
|
||||
raise Discourse::NotFound unless user
|
||||
Group.owner_of(groups, user)
|
||||
},
|
||||
public: Proc.new { |groups|
|
||||
groups.where(public_admission: true, automatic: false)
|
||||
},
|
||||
close: Proc.new { |groups|
|
||||
groups.where(public_admission: false, automatic: false)
|
||||
},
|
||||
automatic: Proc.new { |groups|
|
||||
groups.where(automatic: true)
|
||||
},
|
||||
non_automatic: Proc.new { |groups|
|
||||
groups.where(automatic: false)
|
||||
}
|
||||
end,
|
||||
public: Proc.new { |groups| groups.where(public_admission: true, automatic: false) },
|
||||
close: Proc.new { |groups| groups.where(public_admission: false, automatic: false) },
|
||||
automatic: Proc.new { |groups| groups.where(automatic: true) },
|
||||
non_automatic: Proc.new { |groups| groups.where(automatic: false) },
|
||||
}
|
||||
ADD_MEMBERS_LIMIT = 1000
|
||||
|
||||
|
@ -47,7 +41,7 @@ class GroupsController < ApplicationController
|
|||
raise Discourse::InvalidAccess.new(:enable_group_directory)
|
||||
end
|
||||
|
||||
order = %w{name user_count}.delete(params[:order])
|
||||
order = %w[name user_count].delete(params[:order])
|
||||
dir = params[:asc].to_s == "true" ? "ASC" : "DESC"
|
||||
sort = order ? "#{order} #{dir}" : nil
|
||||
groups = Group.visible_groups(current_user, sort)
|
||||
|
@ -56,7 +50,7 @@ class GroupsController < ApplicationController
|
|||
if (username = params[:username]).present?
|
||||
raise Discourse::NotFound unless user = User.find_by_username(username)
|
||||
groups = TYPE_FILTERS[:my].call(groups.members_visible_groups(current_user, sort), user)
|
||||
type_filters = type_filters - [:my, :owner]
|
||||
type_filters = type_filters - %i[my owner]
|
||||
end
|
||||
|
||||
if (filter = params[:filter]).present?
|
||||
|
@ -83,7 +77,7 @@ class GroupsController < ApplicationController
|
|||
user_group_ids = group_users.pluck(:group_id)
|
||||
owner_group_ids = group_users.where(owner: true).pluck(:group_id)
|
||||
else
|
||||
type_filters = type_filters - [:my, :owner]
|
||||
type_filters = type_filters - %i[my owner]
|
||||
end
|
||||
|
||||
type_filters.delete(:non_automatic)
|
||||
|
@ -96,22 +90,19 @@ class GroupsController < ApplicationController
|
|||
groups = groups.offset(page * page_size).limit(page_size)
|
||||
|
||||
render_json_dump(
|
||||
groups: serialize_data(groups,
|
||||
groups:
|
||||
serialize_data(
|
||||
groups,
|
||||
BasicGroupSerializer,
|
||||
user_group_ids: user_group_ids || [],
|
||||
owner_group_ids: owner_group_ids || []
|
||||
owner_group_ids: owner_group_ids || [],
|
||||
),
|
||||
extras: {
|
||||
type_filters: type_filters
|
||||
type_filters: type_filters,
|
||||
},
|
||||
total_rows_groups: total,
|
||||
load_more_groups: groups_path(
|
||||
page: page + 1,
|
||||
type: type,
|
||||
order: order,
|
||||
asc: params[:asc],
|
||||
filter: filter
|
||||
)
|
||||
load_more_groups:
|
||||
groups_path(page: page + 1, type: type, order: order, asc: params[:asc], filter: filter),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -122,21 +113,23 @@ class GroupsController < ApplicationController
|
|||
format.html do
|
||||
@title = group.full_name.present? ? group.full_name.capitalize : group.name
|
||||
@full_title = "#{@title} - #{SiteSetting.title}"
|
||||
@description_meta = group.bio_cooked.present? ? PrettyText.excerpt(group.bio_cooked, 300) : @title
|
||||
@description_meta =
|
||||
group.bio_cooked.present? ? PrettyText.excerpt(group.bio_cooked, 300) : @title
|
||||
render :show
|
||||
end
|
||||
|
||||
format.json do
|
||||
groups = Group.visible_groups(current_user)
|
||||
if !guardian.is_staff?
|
||||
groups = groups.where("automatic IS FALSE OR groups.id = ?", Group::AUTO_GROUPS[:moderators])
|
||||
groups =
|
||||
groups.where("automatic IS FALSE OR groups.id = ?", Group::AUTO_GROUPS[:moderators])
|
||||
end
|
||||
|
||||
render_json_dump(
|
||||
group: serialize_data(group, GroupShowSerializer, root: nil),
|
||||
extras: {
|
||||
visible_group_names: groups.pluck(:name)
|
||||
}
|
||||
visible_group_names: groups.pluck(:name),
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -161,7 +154,15 @@ class GroupsController < ApplicationController
|
|||
|
||||
if params[:update_existing_users].blank?
|
||||
user_count = count_existing_users(group.group_users, notification_level, categories, tags)
|
||||
return render status: 422, json: { user_count: user_count, errors: [I18n.t('invalid_params', message: :update_existing_users)] } if user_count > 0
|
||||
if user_count > 0
|
||||
return(
|
||||
render status: 422,
|
||||
json: {
|
||||
user_count: user_count,
|
||||
errors: [I18n.t("invalid_params", message: :update_existing_users)],
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -169,7 +170,9 @@ class GroupsController < ApplicationController
|
|||
GroupActionLogger.new(current_user, group).log_change_group_settings
|
||||
group.record_email_setting_changes!(current_user)
|
||||
group.expire_imap_mailbox_cache
|
||||
update_existing_users(group.group_users, notification_level, categories, tags) if params[:update_existing_users] == "true"
|
||||
if params[:update_existing_users] == "true"
|
||||
update_existing_users(group.group_users, notification_level, categories, tags)
|
||||
end
|
||||
AdminDashboardData.clear_found_problem("group_#{group.id}_email_credentials")
|
||||
|
||||
# Redirect user to groups index page if they can no longer see the group
|
||||
|
@ -185,10 +188,7 @@ class GroupsController < ApplicationController
|
|||
group = find_group(:group_id)
|
||||
guardian.ensure_can_see_group_members!(group)
|
||||
|
||||
posts = group.posts_for(
|
||||
guardian,
|
||||
params.permit(:before_post_id, :category_id)
|
||||
).limit(20)
|
||||
posts = group.posts_for(guardian, params.permit(:before_post_id, :category_id)).limit(20)
|
||||
render_serialized posts.to_a, GroupPostSerializer
|
||||
end
|
||||
|
||||
|
@ -196,37 +196,32 @@ class GroupsController < ApplicationController
|
|||
group = find_group(:group_id)
|
||||
guardian.ensure_can_see_group_members!(group)
|
||||
|
||||
@posts = group.posts_for(
|
||||
guardian,
|
||||
params.permit(:before_post_id, :category_id)
|
||||
).limit(50)
|
||||
@title = "#{SiteSetting.title} - #{I18n.t("rss_description.group_posts", group_name: group.name)}"
|
||||
@posts = group.posts_for(guardian, params.permit(:before_post_id, :category_id)).limit(50)
|
||||
@title =
|
||||
"#{SiteSetting.title} - #{I18n.t("rss_description.group_posts", group_name: group.name)}"
|
||||
@link = Discourse.base_url
|
||||
@description = I18n.t("rss_description.group_posts", group_name: group.name)
|
||||
render 'posts/latest', formats: [:rss]
|
||||
render "posts/latest", formats: [:rss]
|
||||
end
|
||||
|
||||
def mentions
|
||||
raise Discourse::NotFound unless SiteSetting.enable_mentions?
|
||||
group = find_group(:group_id)
|
||||
posts = group.mentioned_posts_for(
|
||||
guardian,
|
||||
params.permit(:before_post_id, :category_id)
|
||||
).limit(20)
|
||||
posts =
|
||||
group.mentioned_posts_for(guardian, params.permit(:before_post_id, :category_id)).limit(20)
|
||||
render_serialized posts.to_a, GroupPostSerializer
|
||||
end
|
||||
|
||||
def mentions_feed
|
||||
raise Discourse::NotFound unless SiteSetting.enable_mentions?
|
||||
group = find_group(:group_id)
|
||||
@posts = group.mentioned_posts_for(
|
||||
guardian,
|
||||
params.permit(:before_post_id, :category_id)
|
||||
).limit(50)
|
||||
@title = "#{SiteSetting.title} - #{I18n.t("rss_description.group_mentions", group_name: group.name)}"
|
||||
@posts =
|
||||
group.mentioned_posts_for(guardian, params.permit(:before_post_id, :category_id)).limit(50)
|
||||
@title =
|
||||
"#{SiteSetting.title} - #{I18n.t("rss_description.group_mentions", group_name: group.name)}"
|
||||
@link = Discourse.base_url
|
||||
@description = I18n.t("rss_description.group_mentions", group_name: group.name)
|
||||
render 'posts/latest', formats: [:rss]
|
||||
render "posts/latest", formats: [:rss]
|
||||
end
|
||||
|
||||
def members
|
||||
|
@ -240,10 +235,14 @@ class GroupsController < ApplicationController
|
|||
raise Discourse::InvalidParameters.new(:limit) if limit < 0 || limit > 1000
|
||||
raise Discourse::InvalidParameters.new(:offset) if offset < 0
|
||||
|
||||
dir = (params[:asc] && params[:asc].present?) ? 'ASC' : 'DESC'
|
||||
dir = (params[:asc] && params[:asc].present?) ? "ASC" : "DESC"
|
||||
if params[:desc]
|
||||
Discourse.deprecate(":desc is deprecated please use :asc instead", output_in_test: true, drop_from: '2.9.0')
|
||||
dir = (params[:desc] && params[:desc].present?) ? 'DESC' : 'ASC'
|
||||
Discourse.deprecate(
|
||||
":desc is deprecated please use :asc instead",
|
||||
output_in_test: true,
|
||||
drop_from: "2.9.0",
|
||||
)
|
||||
dir = (params[:desc] && params[:desc].present?) ? "DESC" : "ASC"
|
||||
end
|
||||
order = "NOT group_users.owner"
|
||||
|
||||
|
@ -254,7 +253,7 @@ class GroupsController < ApplicationController
|
|||
total = users.count
|
||||
|
||||
if (filter = params[:filter]).present?
|
||||
filter = filter.split(',') if filter.include?(',')
|
||||
filter = filter.split(",") if filter.include?(",")
|
||||
|
||||
if current_user&.admin
|
||||
users = users.filter_by_username_or_email(filter)
|
||||
|
@ -263,26 +262,29 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
users = users
|
||||
users =
|
||||
users
|
||||
.select("users.*, group_requests.reason, group_requests.created_at requested_at")
|
||||
.order(params[:order] == 'requested_at' ? "group_requests.created_at #{dir}" : "")
|
||||
.order(params[:order] == "requested_at" ? "group_requests.created_at #{dir}" : "")
|
||||
.order(username_lower: dir)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
|
||||
return render json: {
|
||||
return(
|
||||
render json: {
|
||||
members: serialize_data(users, GroupRequesterSerializer),
|
||||
meta: {
|
||||
total: total,
|
||||
limit: limit,
|
||||
offset: offset
|
||||
}
|
||||
offset: offset,
|
||||
},
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
if params[:order] && %w{last_posted_at last_seen_at}.include?(params[:order])
|
||||
if params[:order] && %w[last_posted_at last_seen_at].include?(params[:order])
|
||||
order = "#{params[:order]} #{dir} NULLS LAST"
|
||||
elsif params[:order] == 'added_at'
|
||||
elsif params[:order] == "added_at"
|
||||
order = "group_users.created_at #{dir}"
|
||||
end
|
||||
|
||||
|
@ -290,7 +292,7 @@ class GroupsController < ApplicationController
|
|||
total = users.count
|
||||
|
||||
if (filter = params[:filter]).present?
|
||||
filter = filter.split(',') if filter.include?(',')
|
||||
filter = filter.split(",") if filter.include?(",")
|
||||
|
||||
if current_user&.admin
|
||||
users = users.filter_by_username_or_email(filter)
|
||||
|
@ -299,15 +301,16 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
users = users
|
||||
users =
|
||||
users
|
||||
.includes(:primary_group)
|
||||
.includes(:user_option)
|
||||
.select('users.*, group_users.created_at as added_at')
|
||||
.select("users.*, group_users.created_at as added_at")
|
||||
.order(order)
|
||||
.order(username_lower: dir)
|
||||
|
||||
members = users.limit(limit).offset(offset)
|
||||
owners = users.where('group_users.owner')
|
||||
owners = users.where("group_users.owner")
|
||||
|
||||
render json: {
|
||||
members: serialize_data(members, GroupUserSerializer),
|
||||
|
@ -315,8 +318,8 @@ class GroupsController < ApplicationController
|
|||
meta: {
|
||||
total: total,
|
||||
limit: limit,
|
||||
offset: offset
|
||||
}
|
||||
offset: offset,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -327,7 +330,9 @@ class GroupsController < ApplicationController
|
|||
users = users_from_params.to_a
|
||||
emails = []
|
||||
if params[:emails]
|
||||
params[:emails].split(",").each do |email|
|
||||
params[:emails]
|
||||
.split(",")
|
||||
.each do |email|
|
||||
existing_user = User.find_by_email(email)
|
||||
existing_user.present? ? users.push(existing_user) : emails.push(email)
|
||||
end
|
||||
|
@ -340,43 +345,43 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
|
||||
if users.length > ADD_MEMBERS_LIMIT
|
||||
return render_json_error(
|
||||
I18n.t("groups.errors.adding_too_many_users", count: ADD_MEMBERS_LIMIT)
|
||||
return(
|
||||
render_json_error(I18n.t("groups.errors.adding_too_many_users", count: ADD_MEMBERS_LIMIT))
|
||||
)
|
||||
end
|
||||
|
||||
usernames_already_in_group = group.users.where(id: users.map(&:id)).pluck(:username)
|
||||
if usernames_already_in_group.present? &&
|
||||
usernames_already_in_group.length == users.length &&
|
||||
if usernames_already_in_group.present? && usernames_already_in_group.length == users.length &&
|
||||
emails.blank?
|
||||
render_json_error(I18n.t(
|
||||
render_json_error(
|
||||
I18n.t(
|
||||
"groups.errors.member_already_exist",
|
||||
username: usernames_already_in_group.sort.join(", "),
|
||||
count: usernames_already_in_group.size
|
||||
))
|
||||
count: usernames_already_in_group.size,
|
||||
),
|
||||
)
|
||||
else
|
||||
notify = params[:notify_users]&.to_s == "true"
|
||||
uniq_users = users.uniq
|
||||
uniq_users.each do |user|
|
||||
add_user_to_group(group, user, notify)
|
||||
end
|
||||
uniq_users.each { |user| add_user_to_group(group, user, notify) }
|
||||
|
||||
emails.each do |email|
|
||||
begin
|
||||
Invite.generate(current_user, email: email, group_ids: [group.id])
|
||||
rescue RateLimiter::LimitExceeded => e
|
||||
return render_json_error(I18n.t(
|
||||
return(
|
||||
render_json_error(
|
||||
I18n.t(
|
||||
"invite.rate_limit",
|
||||
count: SiteSetting.max_invites_per_day,
|
||||
time_left: e.time_left
|
||||
))
|
||||
time_left: e.time_left,
|
||||
),
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
render json: success_json.merge!(
|
||||
usernames: uniq_users.map(&:username),
|
||||
emails: emails
|
||||
)
|
||||
render json: success_json.merge!(usernames: uniq_users.map(&:username), emails: emails)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -412,12 +417,13 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
|
||||
if params[:accept]
|
||||
PostCreator.new(current_user,
|
||||
title: I18n.t('groups.request_accepted_pm.title', group_name: group.name),
|
||||
raw: I18n.t('groups.request_accepted_pm.body', group_name: group.name),
|
||||
PostCreator.new(
|
||||
current_user,
|
||||
title: I18n.t("groups.request_accepted_pm.title", group_name: group.name),
|
||||
raw: I18n.t("groups.request_accepted_pm.body", group_name: group.name),
|
||||
archetype: Archetype.private_message,
|
||||
target_usernames: user.username,
|
||||
skip_validations: true
|
||||
skip_validations: true,
|
||||
).create!
|
||||
end
|
||||
|
||||
|
@ -460,9 +466,9 @@ class GroupsController < ApplicationController
|
|||
params[:user_emails] = params[:user_email] if params[:user_email].present?
|
||||
|
||||
users = users_from_params
|
||||
raise Discourse::InvalidParameters.new(
|
||||
'user_ids or usernames or user_emails must be present'
|
||||
) if users.empty?
|
||||
if users.empty?
|
||||
raise Discourse::InvalidParameters.new("user_ids or usernames or user_emails must be present")
|
||||
end
|
||||
|
||||
removed_users = []
|
||||
skipped_users = []
|
||||
|
@ -480,10 +486,7 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
render json: success_json.merge!(
|
||||
usernames: removed_users,
|
||||
skipped_usernames: skipped_users
|
||||
)
|
||||
render json: success_json.merge!(usernames: removed_users, skipped_usernames: skipped_users)
|
||||
end
|
||||
|
||||
def leave
|
||||
|
@ -511,23 +514,34 @@ class GroupsController < ApplicationController
|
|||
begin
|
||||
GroupRequest.create!(group: group, user: current_user, reason: params[:reason])
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
return render json: failed_json.merge(error: I18n.t("groups.errors.already_requested_membership")), status: 409
|
||||
return(
|
||||
render json: failed_json.merge(error: I18n.t("groups.errors.already_requested_membership")),
|
||||
status: 409
|
||||
)
|
||||
end
|
||||
|
||||
usernames = [current_user.username].concat(
|
||||
group.users.where('group_users.owner')
|
||||
group
|
||||
.users
|
||||
.where("group_users.owner")
|
||||
.order("users.last_seen_at DESC")
|
||||
.limit(MAX_NOTIFIED_OWNERS)
|
||||
.pluck("users.username")
|
||||
.pluck("users.username"),
|
||||
)
|
||||
|
||||
post = PostCreator.new(current_user,
|
||||
title: I18n.t('groups.request_membership_pm.title', group_name: group.name),
|
||||
post =
|
||||
PostCreator.new(
|
||||
current_user,
|
||||
title: I18n.t("groups.request_membership_pm.title", group_name: group.name),
|
||||
raw: params[:reason],
|
||||
archetype: Archetype.private_message,
|
||||
target_usernames: usernames.join(','),
|
||||
topic_opts: { custom_fields: { requested_group_id: group.id } },
|
||||
skip_validations: true
|
||||
target_usernames: usernames.join(","),
|
||||
topic_opts: {
|
||||
custom_fields: {
|
||||
requested_group_id: group.id,
|
||||
},
|
||||
},
|
||||
skip_validations: true,
|
||||
).create!
|
||||
|
||||
render json: success_json.merge(relative_url: post.topic.relative_url)
|
||||
|
@ -538,11 +552,10 @@ class GroupsController < ApplicationController
|
|||
notification_level = params.require(:notification_level)
|
||||
|
||||
user_id = current_user.id
|
||||
if guardian.is_staff?
|
||||
user_id = params[:user_id] || user_id
|
||||
end
|
||||
user_id = params[:user_id] || user_id if guardian.is_staff?
|
||||
|
||||
GroupUser.where(group_id: group.id)
|
||||
GroupUser
|
||||
.where(group_id: group.id)
|
||||
.where(user_id: user_id)
|
||||
.update_all(notification_level: notification_level)
|
||||
|
||||
|
@ -556,18 +569,19 @@ class GroupsController < ApplicationController
|
|||
page_size = 25
|
||||
offset = (params[:offset] && params[:offset].to_i) || 0
|
||||
|
||||
group_histories = GroupHistory.with_filters(group, params[:filters])
|
||||
.limit(page_size)
|
||||
.offset(offset * page_size)
|
||||
group_histories =
|
||||
GroupHistory.with_filters(group, params[:filters]).limit(page_size).offset(offset * page_size)
|
||||
|
||||
render_json_dump(
|
||||
logs: serialize_data(group_histories, BasicGroupHistorySerializer),
|
||||
all_loaded: group_histories.count < page_size
|
||||
all_loaded: group_histories.count < page_size,
|
||||
)
|
||||
end
|
||||
|
||||
def search
|
||||
groups = Group.visible_groups(current_user)
|
||||
groups =
|
||||
Group
|
||||
.visible_groups(current_user)
|
||||
.where("groups.id <> ?", Group::AUTO_GROUPS[:everyone])
|
||||
.includes(:flair_upload)
|
||||
.order(:name)
|
||||
|
@ -576,9 +590,7 @@ class GroupsController < ApplicationController
|
|||
groups = groups.where("name ILIKE :term OR full_name ILIKE :term", term: "%#{term}%")
|
||||
end
|
||||
|
||||
if params[:ignore_automatic].to_s == "true"
|
||||
groups = groups.where(automatic: false)
|
||||
end
|
||||
groups = groups.where(automatic: false) if params[:ignore_automatic].to_s == "true"
|
||||
|
||||
if Group.preloaded_custom_field_names.present?
|
||||
Group.preload_custom_fields(groups, Group.preloaded_custom_field_names)
|
||||
|
@ -589,8 +601,14 @@ class GroupsController < ApplicationController
|
|||
|
||||
def permissions
|
||||
group = find_group(:id)
|
||||
category_groups = group.category_groups.select { |category_group| guardian.can_see_category?(category_group.category) }
|
||||
render_serialized(category_groups.sort_by { |category_group| category_group.category.name }, CategoryGroupSerializer)
|
||||
category_groups =
|
||||
group.category_groups.select do |category_group|
|
||||
guardian.can_see_category?(category_group.category)
|
||||
end
|
||||
render_serialized(
|
||||
category_groups.sort_by { |category_group| category_group.category.name },
|
||||
CategoryGroupSerializer,
|
||||
)
|
||||
end
|
||||
|
||||
def test_email_settings
|
||||
|
@ -611,7 +629,7 @@ class GroupsController < ApplicationController
|
|||
enable_tls = settings[:ssl] == "true"
|
||||
email_host = params[:host]
|
||||
|
||||
if !["smtp", "imap"].include?(params[:protocol])
|
||||
if !%w[smtp imap].include?(params[:protocol])
|
||||
raise Discourse::InvalidParameters.new("Valid protocols to test are smtp and imap")
|
||||
end
|
||||
|
||||
|
@ -622,20 +640,29 @@ class GroupsController < ApplicationController
|
|||
enable_starttls_auto = false
|
||||
settings.delete(:ssl)
|
||||
|
||||
final_settings = settings.merge(enable_tls: enable_tls, enable_starttls_auto: enable_starttls_auto)
|
||||
.permit(:host, :port, :username, :password, :enable_tls, :enable_starttls_auto, :debug)
|
||||
EmailSettingsValidator.validate_as_user(current_user, "smtp", **final_settings.to_h.symbolize_keys)
|
||||
final_settings =
|
||||
settings.merge(
|
||||
enable_tls: enable_tls,
|
||||
enable_starttls_auto: enable_starttls_auto,
|
||||
).permit(:host, :port, :username, :password, :enable_tls, :enable_starttls_auto, :debug)
|
||||
EmailSettingsValidator.validate_as_user(
|
||||
current_user,
|
||||
"smtp",
|
||||
**final_settings.to_h.symbolize_keys,
|
||||
)
|
||||
when "imap"
|
||||
final_settings = settings.merge(ssl: enable_tls)
|
||||
.permit(:host, :port, :username, :password, :ssl, :debug)
|
||||
EmailSettingsValidator.validate_as_user(current_user, "imap", **final_settings.to_h.symbolize_keys)
|
||||
final_settings =
|
||||
settings.merge(ssl: enable_tls).permit(:host, :port, :username, :password, :ssl, :debug)
|
||||
EmailSettingsValidator.validate_as_user(
|
||||
current_user,
|
||||
"imap",
|
||||
**final_settings.to_h.symbolize_keys,
|
||||
)
|
||||
end
|
||||
|
||||
render json: success_json
|
||||
rescue *EmailSettingsExceptionHandler::EXPECTED_EXCEPTIONS, StandardError => err
|
||||
render_json_error(
|
||||
EmailSettingsExceptionHandler.friendly_exception_message(err, email_host)
|
||||
)
|
||||
render_json_error(EmailSettingsExceptionHandler.friendly_exception_message(err, email_host))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -653,7 +680,7 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
|
||||
def group_params(automatic: false)
|
||||
attributes = %i{
|
||||
attributes = %i[
|
||||
bio_raw
|
||||
default_notification_level
|
||||
messageable_level
|
||||
|
@ -662,7 +689,7 @@ class GroupsController < ApplicationController
|
|||
flair_color
|
||||
flair_icon
|
||||
flair_upload_id
|
||||
}
|
||||
]
|
||||
|
||||
if automatic
|
||||
attributes.push(:visibility_level)
|
||||
|
@ -673,7 +700,7 @@ class GroupsController < ApplicationController
|
|||
:full_name,
|
||||
:public_exit,
|
||||
:public_admission,
|
||||
:membership_request_template
|
||||
:membership_request_template,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -703,7 +730,7 @@ class GroupsController < ApplicationController
|
|||
:grant_trust_level,
|
||||
:automatic_membership_email_domains,
|
||||
:publish_read_state,
|
||||
:allow_unknown_sender_topic_replies
|
||||
:allow_unknown_sender_topic_replies,
|
||||
)
|
||||
|
||||
custom_fields = DiscoursePluginRegistry.editable_group_custom_fields
|
||||
|
@ -711,7 +738,7 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
|
||||
if !automatic || current_user.admin
|
||||
[:muted, :regular, :tracking, :watching, :watching_first_post].each do |level|
|
||||
%i[muted regular tracking watching watching_first_post].each do |level|
|
||||
attributes << { "#{level}_category_ids" => [] }
|
||||
attributes << { "#{level}_tags" => [] }
|
||||
end
|
||||
|
@ -770,8 +797,10 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
|
||||
def user_default_notifications(group, params)
|
||||
category_notifications = group.group_category_notification_defaults.pluck(:category_id, :notification_level).to_h
|
||||
tag_notifications = group.group_tag_notification_defaults.pluck(:tag_id, :notification_level).to_h
|
||||
category_notifications =
|
||||
group.group_category_notification_defaults.pluck(:category_id, :notification_level).to_h
|
||||
tag_notifications =
|
||||
group.group_tag_notification_defaults.pluck(:tag_id, :notification_level).to_h
|
||||
categories = {}
|
||||
tags = {}
|
||||
|
||||
|
@ -782,10 +811,7 @@ class GroupsController < ApplicationController
|
|||
category_id = category_id.to_i
|
||||
old_value = category_notifications[category_id]
|
||||
|
||||
metadata = {
|
||||
old_value: old_value,
|
||||
new_value: value
|
||||
}
|
||||
metadata = { old_value: old_value, new_value: value }
|
||||
|
||||
if old_value.blank?
|
||||
metadata[:action] = :create
|
||||
|
@ -805,10 +831,7 @@ class GroupsController < ApplicationController
|
|||
tag_ids.each do |tag_id|
|
||||
old_value = tag_notifications[tag_id]
|
||||
|
||||
metadata = {
|
||||
old_value: old_value,
|
||||
new_value: value
|
||||
}
|
||||
metadata = { old_value: old_value, new_value: value }
|
||||
|
||||
if old_value.blank?
|
||||
metadata[:action] = :create
|
||||
|
@ -834,20 +857,18 @@ class GroupsController < ApplicationController
|
|||
notification_level = nil
|
||||
default_notification_level = params[:default_notification_level]&.to_i
|
||||
|
||||
if default_notification_level.present? && group.default_notification_level != default_notification_level
|
||||
if default_notification_level.present? &&
|
||||
group.default_notification_level != default_notification_level
|
||||
notification_level = {
|
||||
old_value: group.default_notification_level,
|
||||
new_value: default_notification_level
|
||||
new_value: default_notification_level,
|
||||
}
|
||||
end
|
||||
|
||||
[notification_level, categories, tags]
|
||||
end
|
||||
|
||||
%i{
|
||||
count
|
||||
update
|
||||
}.each do |action|
|
||||
%i[count update].each do |action|
|
||||
define_method("#{action}_existing_users") do |group_users, notification_level, categories, tags|
|
||||
return 0 if notification_level.blank? && categories.blank? && tags.blank?
|
||||
|
||||
|
@ -865,7 +886,12 @@ class GroupsController < ApplicationController
|
|||
|
||||
categories.each do |category_id, data|
|
||||
if data[:action] == :update || data[:action] == :delete
|
||||
category_users = CategoryUser.where(category_id: category_id, notification_level: data[:old_value], user_id: group_users.select(:user_id))
|
||||
category_users =
|
||||
CategoryUser.where(
|
||||
category_id: category_id,
|
||||
notification_level: data[:old_value],
|
||||
user_id: group_users.select(:user_id),
|
||||
)
|
||||
|
||||
if action == :update
|
||||
category_users.delete_all
|
||||
|
@ -879,7 +905,12 @@ class GroupsController < ApplicationController
|
|||
|
||||
tags.each do |tag_id, data|
|
||||
if data[:action] == :update || data[:action] == :delete
|
||||
tag_users = TagUser.where(tag_id: tag_id, notification_level: data[:old_value], user_id: group_users.select(:user_id))
|
||||
tag_users =
|
||||
TagUser.where(
|
||||
tag_id: tag_id,
|
||||
notification_level: data[:old_value],
|
||||
user_id: group_users.select(:user_id),
|
||||
)
|
||||
|
||||
if action == :update
|
||||
tag_users.delete_all
|
||||
|
@ -892,17 +923,26 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
|
||||
if categories.present? || tags.present?
|
||||
group_users.select(:id, :user_id).find_in_batches do |batch|
|
||||
group_users
|
||||
.select(:id, :user_id)
|
||||
.find_in_batches do |batch|
|
||||
user_ids = batch.pluck(:user_id)
|
||||
|
||||
categories.each do |category_id, data|
|
||||
category_users = []
|
||||
existing_users = CategoryUser.where(category_id: category_id, user_id: user_ids).where("notification_level IS NOT NULL")
|
||||
existing_users =
|
||||
CategoryUser.where(category_id: category_id, user_id: user_ids).where(
|
||||
"notification_level IS NOT NULL",
|
||||
)
|
||||
skip_user_ids = existing_users.pluck(:user_id)
|
||||
|
||||
batch.each do |group_user|
|
||||
next if skip_user_ids.include?(group_user.user_id)
|
||||
category_users << { category_id: category_id, user_id: group_user.user_id, notification_level: data[:new_value] }
|
||||
category_users << {
|
||||
category_id: category_id,
|
||||
user_id: group_user.user_id,
|
||||
notification_level: data[:new_value],
|
||||
}
|
||||
end
|
||||
|
||||
next if category_users.blank?
|
||||
|
@ -916,12 +956,21 @@ class GroupsController < ApplicationController
|
|||
|
||||
tags.each do |tag_id, data|
|
||||
tag_users = []
|
||||
existing_users = TagUser.where(tag_id: tag_id, user_id: user_ids).where("notification_level IS NOT NULL")
|
||||
existing_users =
|
||||
TagUser.where(tag_id: tag_id, user_id: user_ids).where(
|
||||
"notification_level IS NOT NULL",
|
||||
)
|
||||
skip_user_ids = existing_users.pluck(:user_id)
|
||||
|
||||
batch.each do |group_user|
|
||||
next if skip_user_ids.include?(group_user.user_id)
|
||||
tag_users << { tag_id: tag_id, user_id: group_user.user_id, notification_level: data[:new_value], created_at: Time.now, updated_at: Time.now }
|
||||
tag_users << {
|
||||
tag_id: tag_id,
|
||||
user_id: group_user.user_id,
|
||||
notification_level: data[:new_value],
|
||||
created_at: Time.now,
|
||||
updated_at: Time.now,
|
||||
}
|
||||
end
|
||||
|
||||
next if tag_users.blank?
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HighlightJsController < ApplicationController
|
||||
skip_before_action :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show]
|
||||
skip_before_action :preload_json,
|
||||
:redirect_to_login_if_required,
|
||||
:check_xhr,
|
||||
:verify_authenticity_token,
|
||||
only: [:show]
|
||||
|
||||
before_action :apply_cdn_headers, only: [:show]
|
||||
|
||||
def show
|
||||
|
||||
no_cookies
|
||||
|
||||
RailsMultisite::ConnectionManagement.with_hostname(params[:hostname]) do
|
||||
|
||||
current_version = HighlightJs.version(SiteSetting.highlighted_languages)
|
||||
|
||||
if current_version != params[:version]
|
||||
return redirect_to path(HighlightJs.path)
|
||||
end
|
||||
return redirect_to path(HighlightJs.path) if current_version != params[:version]
|
||||
|
||||
# note, this can be slightly optimised by caching the bundled file, it cuts down on N reads
|
||||
# our nginx config caches this so in practical terms it does not really matter and keeps
|
||||
# code simpler
|
||||
languages = SiteSetting.highlighted_languages.split('|')
|
||||
languages = SiteSetting.highlighted_languages.split("|")
|
||||
|
||||
highlight_js = HighlightJs.bundle(languages)
|
||||
|
||||
|
@ -28,7 +28,7 @@ class HighlightJsController < ApplicationController
|
|||
response.headers["Content-Length"] = highlight_js.bytesize.to_s
|
||||
immutable_for 1.year
|
||||
|
||||
render plain: highlight_js, disposition: nil, content_type: 'application/javascript'
|
||||
render plain: highlight_js, disposition: nil, content_type: "application/javascript"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,11 +5,12 @@ class InlineOneboxController < ApplicationController
|
|||
|
||||
def show
|
||||
hijack do
|
||||
oneboxes = InlineOneboxer.new(
|
||||
oneboxes =
|
||||
InlineOneboxer.new(
|
||||
params[:urls] || [],
|
||||
user_id: current_user.id,
|
||||
category_id: params[:category_id].to_i,
|
||||
topic_id: params[:topic_id].to_i
|
||||
topic_id: params[:topic_id].to_i,
|
||||
).process
|
||||
render json: { "inline-oneboxes" => oneboxes }
|
||||
end
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
require "csv"
|
||||
|
||||
class InvitesController < ApplicationController
|
||||
|
||||
requires_login only: [:create, :retrieve, :destroy, :destroy_all_expired, :resend_invite, :resend_all_invites, :upload_csv]
|
||||
requires_login only: %i[
|
||||
create
|
||||
retrieve
|
||||
destroy
|
||||
destroy_all_expired
|
||||
resend_invite
|
||||
resend_all_invites
|
||||
upload_csv
|
||||
]
|
||||
|
||||
skip_before_action :check_xhr, except: [:perform_accept_invitation]
|
||||
skip_before_action :preload_json, except: [:show]
|
||||
skip_before_action :redirect_to_login_if_required
|
||||
|
||||
before_action :ensure_invites_allowed, only: [:show, :perform_accept_invitation]
|
||||
before_action :ensure_new_registrations_allowed, only: [:show, :perform_accept_invitation]
|
||||
before_action :ensure_invites_allowed, only: %i[show perform_accept_invitation]
|
||||
before_action :ensure_new_registrations_allowed, only: %i[show perform_accept_invitation]
|
||||
|
||||
def show
|
||||
expires_now
|
||||
|
@ -27,7 +34,7 @@ class InvitesController < ApplicationController
|
|||
end
|
||||
rescue RateLimiter::LimitExceeded => e
|
||||
flash.now[:error] = e.description
|
||||
render layout: 'no_ember'
|
||||
render layout: "no_ember"
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -45,10 +52,16 @@ class InvitesController < ApplicationController
|
|||
|
||||
if !groups_can_see_topic?(groups, topic)
|
||||
editable_topic_groups = topic.category.groups.filter { |g| guardian.can_edit_group?(g) }
|
||||
return render_json_error(I18n.t("invite.requires_groups", groups: editable_topic_groups.pluck(:name).join(", ")))
|
||||
return(
|
||||
render_json_error(
|
||||
I18n.t("invite.requires_groups", groups: editable_topic_groups.pluck(:name).join(", ")),
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
invite = Invite.generate(current_user,
|
||||
invite =
|
||||
Invite.generate(
|
||||
current_user,
|
||||
email: params[:email],
|
||||
domain: params[:domain],
|
||||
skip_email: params[:skip_email],
|
||||
|
@ -58,11 +71,18 @@ class InvitesController < ApplicationController
|
|||
topic_id: topic&.id,
|
||||
group_ids: groups&.map(&:id),
|
||||
expires_at: params[:expires_at],
|
||||
invite_to_topic: params[:invite_to_topic]
|
||||
invite_to_topic: params[:invite_to_topic],
|
||||
)
|
||||
|
||||
if invite.present?
|
||||
render_serialized(invite, InviteSerializer, scope: guardian, root: nil, show_emails: params.has_key?(:email), show_warnings: true)
|
||||
render_serialized(
|
||||
invite,
|
||||
InviteSerializer,
|
||||
scope: guardian,
|
||||
root: nil,
|
||||
show_emails: params.has_key?(:email),
|
||||
show_warnings: true,
|
||||
)
|
||||
else
|
||||
render json: failed_json, status: 422
|
||||
end
|
||||
|
@ -81,7 +101,14 @@ class InvitesController < ApplicationController
|
|||
|
||||
guardian.ensure_can_invite_to_forum!(nil)
|
||||
|
||||
render_serialized(invite, InviteSerializer, scope: guardian, root: nil, show_emails: params.has_key?(:email), show_warnings: true)
|
||||
render_serialized(
|
||||
invite,
|
||||
InviteSerializer,
|
||||
scope: guardian,
|
||||
root: nil,
|
||||
show_emails: params.has_key?(:email),
|
||||
show_warnings: true,
|
||||
)
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -108,12 +135,19 @@ class InvitesController < ApplicationController
|
|||
|
||||
if params.has_key?(:group_ids) || params.has_key?(:group_names)
|
||||
invite.invited_groups.destroy_all
|
||||
groups.each { |group| invite.invited_groups.find_or_create_by!(group_id: group.id) } if groups.present?
|
||||
if groups.present?
|
||||
groups.each { |group| invite.invited_groups.find_or_create_by!(group_id: group.id) }
|
||||
end
|
||||
end
|
||||
|
||||
if !groups_can_see_topic?(invite.groups, invite.topics.first)
|
||||
editable_topic_groups = invite.topics.first.category.groups.filter { |g| guardian.can_edit_group?(g) }
|
||||
return render_json_error(I18n.t("invite.requires_groups", groups: editable_topic_groups.pluck(:name).join(", ")))
|
||||
editable_topic_groups =
|
||||
invite.topics.first.category.groups.filter { |g| guardian.can_edit_group?(g) }
|
||||
return(
|
||||
render_json_error(
|
||||
I18n.t("invite.requires_groups", groups: editable_topic_groups.pluck(:name).join(", ")),
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
if params.has_key?(:email)
|
||||
|
@ -121,16 +155,22 @@ class InvitesController < ApplicationController
|
|||
new_email = params[:email].presence
|
||||
|
||||
if new_email
|
||||
if Invite.where.not(id: invite.id).find_by(email: new_email.downcase, invited_by_id: current_user.id)&.redeemable?
|
||||
return render_json_error(
|
||||
if Invite
|
||||
.where.not(id: invite.id)
|
||||
.find_by(email: new_email.downcase, invited_by_id: current_user.id)
|
||||
&.redeemable?
|
||||
return(
|
||||
render_json_error(
|
||||
I18n.t("invite.invite_exists", email: CGI.escapeHTML(new_email)),
|
||||
status: 409
|
||||
status: 409,
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if old_email != new_email
|
||||
invite.emailed_status = if new_email && !params[:skip_email]
|
||||
invite.emailed_status =
|
||||
if new_email && !params[:skip_email]
|
||||
Invite.emailed_status_types[:pending]
|
||||
else
|
||||
Invite.emailed_status_types[:not_required]
|
||||
|
@ -162,9 +202,13 @@ class InvitesController < ApplicationController
|
|||
end
|
||||
|
||||
begin
|
||||
invite.update!(params.permit(:email, :custom_message, :max_redemptions_allowed, :expires_at))
|
||||
invite.update!(
|
||||
params.permit(:email, :custom_message, :max_redemptions_allowed, :expires_at),
|
||||
)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
return render json: {}, status: 200 if SiteSetting.hide_email_address_taken? && e.record.email_already_exists?
|
||||
if SiteSetting.hide_email_address_taken? && e.record.email_already_exists?
|
||||
return render json: {}, status: 200
|
||||
end
|
||||
return render_json_error(e.record.errors.full_messages.first)
|
||||
end
|
||||
end
|
||||
|
@ -174,7 +218,14 @@ class InvitesController < ApplicationController
|
|||
Jobs.enqueue(:invite_email, invite_id: invite.id, invite_to_topic: params[:invite_to_topic])
|
||||
end
|
||||
|
||||
render_serialized(invite, InviteSerializer, scope: guardian, root: nil, show_emails: params.has_key?(:email), show_warnings: true)
|
||||
render_serialized(
|
||||
invite,
|
||||
InviteSerializer,
|
||||
scope: guardian,
|
||||
root: nil,
|
||||
show_emails: params.has_key?(:email),
|
||||
show_warnings: true,
|
||||
)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -192,17 +243,23 @@ class InvitesController < ApplicationController
|
|||
# via the SessionController#sso_login route
|
||||
def perform_accept_invitation
|
||||
params.require(:id)
|
||||
params.permit(:email, :username, :name, :password, :timezone, :email_token, user_custom_fields: {})
|
||||
params.permit(
|
||||
:email,
|
||||
:username,
|
||||
:name,
|
||||
:password,
|
||||
:timezone,
|
||||
:email_token,
|
||||
user_custom_fields: {
|
||||
},
|
||||
)
|
||||
|
||||
invite = Invite.find_by(invite_key: params[:id])
|
||||
redeeming_user = current_user
|
||||
|
||||
if invite.present?
|
||||
begin
|
||||
attrs = {
|
||||
ip_address: request.remote_ip,
|
||||
session: session
|
||||
}
|
||||
attrs = { ip_address: request.remote_ip, session: session }
|
||||
|
||||
if redeeming_user
|
||||
attrs[:redeeming_user] = redeeming_user
|
||||
|
@ -230,12 +287,10 @@ class InvitesController < ApplicationController
|
|||
end
|
||||
|
||||
if user.blank?
|
||||
return render json: failed_json.merge(message: I18n.t('invite.not_found_json')), status: 404
|
||||
return render json: failed_json.merge(message: I18n.t("invite.not_found_json")), status: 404
|
||||
end
|
||||
|
||||
if !redeeming_user && user.active? && user.guardian.can_access_forum?
|
||||
log_on_user(user)
|
||||
end
|
||||
log_on_user(user) if !redeeming_user && user.active? && user.guardian.can_access_forum?
|
||||
|
||||
user.update_timezone_if_missing(params[:timezone])
|
||||
post_process_invite(user)
|
||||
|
@ -246,9 +301,7 @@ class InvitesController < ApplicationController
|
|||
|
||||
if user.present?
|
||||
if user.active? && user.guardian.can_access_forum?
|
||||
if redeeming_user
|
||||
response[:message] = I18n.t("invite.existing_user_success")
|
||||
end
|
||||
response[:message] = I18n.t("invite.existing_user_success") if redeeming_user
|
||||
|
||||
if user.guardian.can_see?(topic)
|
||||
response[:redirect_to] = path(topic.relative_url)
|
||||
|
@ -257,20 +310,18 @@ class InvitesController < ApplicationController
|
|||
end
|
||||
else
|
||||
response[:message] = if user.active?
|
||||
I18n.t('activation.approval_required')
|
||||
I18n.t("activation.approval_required")
|
||||
else
|
||||
I18n.t('invite.confirm_email')
|
||||
I18n.t("invite.confirm_email")
|
||||
end
|
||||
|
||||
if user.guardian.can_see?(topic)
|
||||
cookies[:destination_url] = path(topic.relative_url)
|
||||
end
|
||||
cookies[:destination_url] = path(topic.relative_url) if user.guardian.can_see?(topic)
|
||||
end
|
||||
end
|
||||
|
||||
render json: success_json.merge(response)
|
||||
else
|
||||
render json: failed_json.merge(message: I18n.t('invite.not_found_json')), status: 404
|
||||
render json: failed_json.merge(message: I18n.t("invite.not_found_json")), status: 404
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -279,7 +330,7 @@ class InvitesController < ApplicationController
|
|||
|
||||
Invite
|
||||
.where(invited_by: current_user)
|
||||
.where('expires_at < ?', Time.zone.now)
|
||||
.where("expires_at < ?", Time.zone.now)
|
||||
.find_each { |invite| invite.trash!(current_user) }
|
||||
|
||||
render json: success_json
|
||||
|
@ -301,13 +352,20 @@ class InvitesController < ApplicationController
|
|||
guardian.ensure_can_resend_all_invites!(current_user)
|
||||
|
||||
begin
|
||||
RateLimiter.new(current_user, "bulk-reinvite-per-day", 1, 1.day, apply_limit_to_staff: true).performed!
|
||||
RateLimiter.new(
|
||||
current_user,
|
||||
"bulk-reinvite-per-day",
|
||||
1,
|
||||
1.day,
|
||||
apply_limit_to_staff: true,
|
||||
).performed!
|
||||
rescue RateLimiter::LimitExceeded
|
||||
return render_json_error(I18n.t("rate_limiter.slow_down"))
|
||||
end
|
||||
|
||||
Invite.pending(current_user)
|
||||
.where('invites.email IS NOT NULL')
|
||||
Invite
|
||||
.pending(current_user)
|
||||
.where("invites.email IS NOT NULL")
|
||||
.find_each { |invite| invite.resend_invite }
|
||||
|
||||
render json: success_json
|
||||
|
@ -326,17 +384,15 @@ class InvitesController < ApplicationController
|
|||
CSV.foreach(file.tempfile, encoding: "bom|utf-8") do |row|
|
||||
# Try to extract a CSV header, if it exists
|
||||
if csv_header.nil?
|
||||
if row[0] == 'email'
|
||||
if row[0] == "email"
|
||||
csv_header = row
|
||||
next
|
||||
else
|
||||
csv_header = ["email", "groups", "topic_id"]
|
||||
csv_header = %w[email groups topic_id]
|
||||
end
|
||||
end
|
||||
|
||||
if row[0].present?
|
||||
invites.push(csv_header.zip(row).map.to_h.filter { |k, v| v.present? })
|
||||
end
|
||||
invites.push(csv_header.zip(row).map.to_h.filter { |k, v| v.present? }) if row[0].present?
|
||||
|
||||
break if invites.count >= SiteSetting.max_bulk_invites
|
||||
end
|
||||
|
@ -345,7 +401,16 @@ class InvitesController < ApplicationController
|
|||
Jobs.enqueue(:bulk_invite, invites: invites, current_user_id: current_user.id)
|
||||
|
||||
if invites.count >= SiteSetting.max_bulk_invites
|
||||
render json: failed_json.merge(errors: [I18n.t("bulk_invite.max_rows", max_bulk_invites: SiteSetting.max_bulk_invites)]), status: 422
|
||||
render json:
|
||||
failed_json.merge(
|
||||
errors: [
|
||||
I18n.t(
|
||||
"bulk_invite.max_rows",
|
||||
max_bulk_invites: SiteSetting.max_bulk_invites,
|
||||
),
|
||||
],
|
||||
),
|
||||
status: 422
|
||||
else
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -375,9 +440,7 @@ class InvitesController < ApplicationController
|
|||
|
||||
email_verified_by_link = invite.email_token.present? && params[:t] == invite.email_token
|
||||
|
||||
if email_verified_by_link
|
||||
email = invite.email
|
||||
end
|
||||
email = invite.email if email_verified_by_link
|
||||
|
||||
hidden_email = email != invite.email
|
||||
|
||||
|
@ -393,12 +456,10 @@ class InvitesController < ApplicationController
|
|||
hidden_email: hidden_email,
|
||||
username: username,
|
||||
is_invite_link: invite.is_invite_link?,
|
||||
email_verified_by_link: email_verified_by_link
|
||||
email_verified_by_link: email_verified_by_link,
|
||||
}
|
||||
|
||||
if different_external_email
|
||||
info[:different_external_email] = true
|
||||
end
|
||||
info[:different_external_email] = true if different_external_email
|
||||
|
||||
if staged_user = User.where(staged: true).with_email(invite.email).first
|
||||
info[:username] = staged_user.username
|
||||
|
@ -417,36 +478,46 @@ class InvitesController < ApplicationController
|
|||
|
||||
secure_session["invite-key"] = invite.invite_key
|
||||
|
||||
render layout: 'application'
|
||||
render layout: "application"
|
||||
end
|
||||
|
||||
def show_irredeemable_invite(invite)
|
||||
flash.now[:error] = \
|
||||
if invite.blank?
|
||||
I18n.t('invite.not_found', base_url: Discourse.base_url)
|
||||
flash.now[:error] = if invite.blank?
|
||||
I18n.t("invite.not_found", base_url: Discourse.base_url)
|
||||
elsif invite.redeemed?
|
||||
if invite.is_invite_link?
|
||||
I18n.t('invite.not_found_template_link', site_name: SiteSetting.title, base_url: Discourse.base_url)
|
||||
I18n.t(
|
||||
"invite.not_found_template_link",
|
||||
site_name: SiteSetting.title,
|
||||
base_url: Discourse.base_url,
|
||||
)
|
||||
else
|
||||
I18n.t('invite.not_found_template', site_name: SiteSetting.title, base_url: Discourse.base_url)
|
||||
I18n.t(
|
||||
"invite.not_found_template",
|
||||
site_name: SiteSetting.title,
|
||||
base_url: Discourse.base_url,
|
||||
)
|
||||
end
|
||||
elsif invite.expired?
|
||||
I18n.t('invite.expired', base_url: Discourse.base_url)
|
||||
I18n.t("invite.expired", base_url: Discourse.base_url)
|
||||
end
|
||||
|
||||
render layout: 'no_ember'
|
||||
render layout: "no_ember"
|
||||
end
|
||||
|
||||
def ensure_invites_allowed
|
||||
if (!SiteSetting.enable_local_logins && Discourse.enabled_auth_providers.count == 0 && !SiteSetting.enable_discourse_connect)
|
||||
if (
|
||||
!SiteSetting.enable_local_logins && Discourse.enabled_auth_providers.count == 0 &&
|
||||
!SiteSetting.enable_discourse_connect
|
||||
)
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_new_registrations_allowed
|
||||
unless SiteSetting.allow_new_registrations
|
||||
flash[:error] = I18n.t('login.new_registrations_disabled')
|
||||
render layout: 'no_ember'
|
||||
flash[:error] = I18n.t("login.new_registrations_disabled")
|
||||
render layout: "no_ember"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
@ -461,13 +532,14 @@ class InvitesController < ApplicationController
|
|||
end
|
||||
|
||||
def post_process_invite(user)
|
||||
user.enqueue_welcome_message('welcome_invite') if user.send_welcome_message
|
||||
user.enqueue_welcome_message("welcome_invite") if user.send_welcome_message
|
||||
|
||||
Group.refresh_automatic_groups!(:admins, :moderators, :staff) if user.staff?
|
||||
|
||||
if user.has_password?
|
||||
if !user.active
|
||||
email_token = user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:signup])
|
||||
email_token =
|
||||
user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:signup])
|
||||
EmailToken.enqueue_signup_email(email_token)
|
||||
end
|
||||
elsif !SiteSetting.enable_discourse_connect && SiteSetting.enable_local_logins
|
||||
|
@ -478,17 +550,19 @@ class InvitesController < ApplicationController
|
|||
def create_topic_invite_notifications(invite, user)
|
||||
invite.topics.each do |topic|
|
||||
if user.guardian.can_see?(topic)
|
||||
last_notification = user.notifications
|
||||
last_notification =
|
||||
user
|
||||
.notifications
|
||||
.where(notification_type: Notification.types[:invited_to_topic])
|
||||
.where(topic_id: topic.id)
|
||||
.where(post_number: 1)
|
||||
.where('created_at > ?', 1.hour.ago)
|
||||
.where("created_at > ?", 1.hour.ago)
|
||||
|
||||
if !last_notification.exists?
|
||||
topic.create_invite_notification!(
|
||||
user,
|
||||
Notification.types[:invited_to_topic],
|
||||
invite.invited_by
|
||||
invite.invited_by,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,8 @@ class ListController < ApplicationController
|
|||
|
||||
skip_before_action :check_xhr
|
||||
|
||||
before_action :set_category, only: [
|
||||
before_action :set_category,
|
||||
only: [
|
||||
:category_default,
|
||||
# filtered topics lists
|
||||
Discourse.filters.map { |f| :"category_#{f}" },
|
||||
|
@ -21,7 +22,8 @@ class ListController < ApplicationController
|
|||
:category_feed,
|
||||
].flatten
|
||||
|
||||
before_action :ensure_logged_in, except: [
|
||||
before_action :ensure_logged_in,
|
||||
except: [
|
||||
:topics_by,
|
||||
# anonymous filters
|
||||
Discourse.anonymous_filters,
|
||||
|
@ -43,7 +45,7 @@ class ListController < ApplicationController
|
|||
TopTopic.periods.map { |p| :"top_#{p}_feed" },
|
||||
TopTopic.periods.map { |p| :"category_top_#{p}" },
|
||||
TopTopic.periods.map { |p| :"category_none_top_#{p}" },
|
||||
:group_topics
|
||||
:group_topics,
|
||||
].flatten
|
||||
|
||||
# Create our filters
|
||||
|
@ -52,7 +54,8 @@ class ListController < ApplicationController
|
|||
list_opts = build_topic_list_options
|
||||
list_opts.merge!(options) if options
|
||||
user = list_target_user
|
||||
if params[:category].blank? && filter == :latest && !SiteSetting.show_category_definitions_in_topic_lists
|
||||
if params[:category].blank? && filter == :latest &&
|
||||
!SiteSetting.show_category_definitions_in_topic_lists
|
||||
list_opts[:no_definitions] = true
|
||||
end
|
||||
|
||||
|
@ -61,16 +64,15 @@ class ListController < ApplicationController
|
|||
if guardian.can_create_shared_draft? && @category.present?
|
||||
if @category.id == SiteSetting.shared_drafts_category.to_i
|
||||
# On shared drafts, show the destination category
|
||||
list.topics.each do |t|
|
||||
t.includes_destination_category = t.shared_draft.present?
|
||||
end
|
||||
list.topics.each { |t| t.includes_destination_category = t.shared_draft.present? }
|
||||
else
|
||||
# When viewing a non-shared draft category, find topics whose
|
||||
# destination are this category
|
||||
shared_drafts = TopicQuery.new(
|
||||
shared_drafts =
|
||||
TopicQuery.new(
|
||||
user,
|
||||
category: SiteSetting.shared_drafts_category,
|
||||
destination_category_id: list_opts[:category]
|
||||
destination_category_id: list_opts[:category],
|
||||
).list_latest
|
||||
|
||||
if shared_drafts.present? && shared_drafts.topics.present?
|
||||
|
@ -90,12 +92,14 @@ class ListController < ApplicationController
|
|||
if (filter.to_s != current_homepage) && use_crawler_layout?
|
||||
filter_title = I18n.t("js.filters.#{filter.to_s}.title", count: 0)
|
||||
if list_opts[:category] && @category
|
||||
@title = I18n.t('js.filters.with_category', filter: filter_title, category: @category.name)
|
||||
@title =
|
||||
I18n.t("js.filters.with_category", filter: filter_title, category: @category.name)
|
||||
else
|
||||
@title = I18n.t('js.filters.with_topics', filter: filter_title)
|
||||
@title = I18n.t("js.filters.with_topics", filter: filter_title)
|
||||
end
|
||||
@title << " - #{SiteSetting.title}"
|
||||
elsif @category.blank? && (filter.to_s == current_homepage) && SiteSetting.short_site_description.present?
|
||||
elsif @category.blank? && (filter.to_s == current_homepage) &&
|
||||
SiteSetting.short_site_description.present?
|
||||
@title = "#{SiteSetting.title} - #{SiteSetting.short_site_description}"
|
||||
end
|
||||
end
|
||||
|
@ -116,14 +120,21 @@ class ListController < ApplicationController
|
|||
def category_default
|
||||
canonical_url "#{Discourse.base_url_no_prefix}#{@category.url}"
|
||||
view_method = @category.default_view
|
||||
view_method = 'latest' unless %w(latest top).include?(view_method)
|
||||
view_method = "latest" unless %w[latest top].include?(view_method)
|
||||
|
||||
self.public_send(view_method, category: @category.id)
|
||||
end
|
||||
|
||||
def topics_by
|
||||
list_opts = build_topic_list_options
|
||||
target_user = fetch_user_from_params({ include_inactive: current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts) }, [:user_stat, :user_option])
|
||||
target_user =
|
||||
fetch_user_from_params(
|
||||
{
|
||||
include_inactive:
|
||||
current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts),
|
||||
},
|
||||
%i[user_stat user_option],
|
||||
)
|
||||
ensure_can_see_profile!(target_user)
|
||||
|
||||
list = generate_list_for("topics_by", target_user, list_opts)
|
||||
|
@ -152,14 +163,15 @@ class ListController < ApplicationController
|
|||
end
|
||||
|
||||
def message_route(action)
|
||||
target_user = fetch_user_from_params({ include_inactive: current_user.try(:staff?) }, [:user_stat, :user_option])
|
||||
target_user =
|
||||
fetch_user_from_params(
|
||||
{ include_inactive: current_user.try(:staff?) },
|
||||
%i[user_stat user_option],
|
||||
)
|
||||
|
||||
case action
|
||||
when :private_messages_unread,
|
||||
:private_messages_new,
|
||||
:private_messages_group_new,
|
||||
when :private_messages_unread, :private_messages_new, :private_messages_group_new,
|
||||
:private_messages_group_unread
|
||||
|
||||
raise Discourse::NotFound if target_user.id != current_user.id
|
||||
when :private_messages_tag
|
||||
raise Discourse::NotFound if !guardian.can_tag_pms?
|
||||
|
@ -181,7 +193,7 @@ class ListController < ApplicationController
|
|||
respond_with_list(list)
|
||||
end
|
||||
|
||||
%i{
|
||||
%i[
|
||||
private_messages
|
||||
private_messages_sent
|
||||
private_messages_unread
|
||||
|
@ -193,14 +205,12 @@ class ListController < ApplicationController
|
|||
private_messages_group_archive
|
||||
private_messages_warnings
|
||||
private_messages_tag
|
||||
}.each do |action|
|
||||
generate_message_route(action)
|
||||
end
|
||||
].each { |action| generate_message_route(action) }
|
||||
|
||||
def latest_feed
|
||||
discourse_expires_in 1.minute
|
||||
|
||||
options = { order: 'created' }.merge(build_topic_list_options)
|
||||
options = { order: "created" }.merge(build_topic_list_options)
|
||||
|
||||
@title = "#{SiteSetting.title} - #{I18n.t("rss_description.latest")}"
|
||||
@link = "#{Discourse.base_url}/latest"
|
||||
|
@ -208,7 +218,7 @@ class ListController < ApplicationController
|
|||
@description = I18n.t("rss_description.latest")
|
||||
@topic_list = TopicQuery.new(nil, options).list_latest
|
||||
|
||||
render 'list', formats: [:rss]
|
||||
render "list", formats: [:rss]
|
||||
end
|
||||
|
||||
def top_feed
|
||||
|
@ -223,7 +233,7 @@ class ListController < ApplicationController
|
|||
|
||||
@topic_list = TopicQuery.new(nil).list_top_for(period)
|
||||
|
||||
render 'list', formats: [:rss]
|
||||
render "list", formats: [:rss]
|
||||
end
|
||||
|
||||
def category_feed
|
||||
|
@ -233,10 +243,11 @@ class ListController < ApplicationController
|
|||
@title = "#{@category.name} - #{SiteSetting.title}"
|
||||
@link = "#{Discourse.base_url_no_prefix}#{@category.url}"
|
||||
@atom_link = "#{Discourse.base_url_no_prefix}#{@category.url}.rss"
|
||||
@description = "#{I18n.t('topics_in_category', category: @category.name)} #{@category.description}"
|
||||
@description =
|
||||
"#{I18n.t("topics_in_category", category: @category.name)} #{@category.description}"
|
||||
@topic_list = TopicQuery.new(current_user).list_new_in_category(@category)
|
||||
|
||||
render 'list', formats: [:rss]
|
||||
render "list", formats: [:rss]
|
||||
end
|
||||
|
||||
def user_topics_feed
|
||||
|
@ -244,22 +255,22 @@ class ListController < ApplicationController
|
|||
target_user = fetch_user_from_params
|
||||
ensure_can_see_profile!(target_user)
|
||||
|
||||
@title = "#{SiteSetting.title} - #{I18n.t("rss_description.user_topics", username: target_user.username)}"
|
||||
@title =
|
||||
"#{SiteSetting.title} - #{I18n.t("rss_description.user_topics", username: target_user.username)}"
|
||||
@link = "#{target_user.full_url}/activity/topics"
|
||||
@atom_link = "#{target_user.full_url}/activity/topics.rss"
|
||||
@description = I18n.t("rss_description.user_topics", username: target_user.username)
|
||||
|
||||
@topic_list = TopicQuery
|
||||
.new(nil, order: 'created')
|
||||
.public_send("list_topics_by", target_user)
|
||||
@topic_list = TopicQuery.new(nil, order: "created").public_send("list_topics_by", target_user)
|
||||
|
||||
render 'list', formats: [:rss]
|
||||
render "list", formats: [:rss]
|
||||
end
|
||||
|
||||
def top(options = nil)
|
||||
options ||= {}
|
||||
period = params[:period]
|
||||
period ||= ListController.best_period_for(current_user.try(:previous_visit_at), options[:category])
|
||||
period ||=
|
||||
ListController.best_period_for(current_user.try(:previous_visit_at), options[:category])
|
||||
TopTopic.validate_period(period)
|
||||
public_send("top_#{period}", options)
|
||||
end
|
||||
|
@ -299,10 +310,7 @@ class ListController < ApplicationController
|
|||
end
|
||||
|
||||
define_method("category_none_top_#{period}") do
|
||||
self.public_send("top_#{period}",
|
||||
category: @category.id,
|
||||
no_subcategories: true
|
||||
)
|
||||
self.public_send("top_#{period}", category: @category.id, no_subcategories: true)
|
||||
end
|
||||
|
||||
# rss feed
|
||||
|
@ -315,7 +323,7 @@ class ListController < ApplicationController
|
|||
@atom_link = "#{Discourse.base_url}/top.rss?period=#{period}"
|
||||
@topic_list = TopicQuery.new(nil).list_top_for(period)
|
||||
|
||||
render 'list', formats: [:rss]
|
||||
render "list", formats: [:rss]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -337,16 +345,17 @@ class ListController < ApplicationController
|
|||
private
|
||||
|
||||
def page_params
|
||||
route_params = { format: 'json' }
|
||||
route_params = { format: "json" }
|
||||
|
||||
if @category.present?
|
||||
slug_path = @category.slug_path
|
||||
|
||||
route_params[:category_slug_path_with_id] =
|
||||
(slug_path + [@category.id.to_s]).join("/")
|
||||
route_params[:category_slug_path_with_id] = (slug_path + [@category.id.to_s]).join("/")
|
||||
end
|
||||
|
||||
route_params[:username] = UrlHelper.encode_component(params[:username]) if params[:username].present?
|
||||
route_params[:username] = UrlHelper.encode_component(params[:username]) if params[
|
||||
:username
|
||||
].present?
|
||||
route_params[:period] = params[:period] if params[:period].present?
|
||||
route_params
|
||||
end
|
||||
|
@ -355,9 +364,7 @@ class ListController < ApplicationController
|
|||
category_slug_path_with_id = params.require(:category_slug_path_with_id)
|
||||
|
||||
@category = Category.find_by_slug_path_with_id(category_slug_path_with_id)
|
||||
if @category.nil?
|
||||
raise Discourse::NotFound.new("category not found", check_permalinks: true)
|
||||
end
|
||||
raise Discourse::NotFound.new("category not found", check_permalinks: true) if @category.nil?
|
||||
|
||||
params[:category] = @category.id.to_s
|
||||
|
||||
|
@ -385,7 +392,8 @@ class ListController < ApplicationController
|
|||
return redirect_to path(url), status: 301
|
||||
end
|
||||
|
||||
@description_meta = if @category.uncategorized?
|
||||
@description_meta =
|
||||
if @category.uncategorized?
|
||||
I18n.t("category.uncategorized_description", locale: SiteSetting.default_locale)
|
||||
elsif @category.description_text.present?
|
||||
@category.description_text
|
||||
|
@ -431,7 +439,7 @@ class ListController < ApplicationController
|
|||
|
||||
opts.delete(:category) if page_params.include?(:category_slug_path_with_id)
|
||||
|
||||
url = public_send(method, opts.merge(page_params)).sub('.json?', '?')
|
||||
url = public_send(method, opts.merge(page_params)).sub(".json?", "?")
|
||||
|
||||
# Unicode usernames need to be encoded when calling Rails' path helper. However, it means that the already
|
||||
# encoded username are encoded again which we do not want. As such, we unencode the url once when unicode usernames
|
||||
|
@ -446,16 +454,24 @@ class ListController < ApplicationController
|
|||
end
|
||||
|
||||
def self.best_period_for(previous_visit_at, category_id = nil)
|
||||
default_period = ((category_id && Category.where(id: category_id).pluck_first(:default_top_period)) ||
|
||||
SiteSetting.top_page_default_timeframe).to_sym
|
||||
default_period =
|
||||
(
|
||||
(category_id && Category.where(id: category_id).pluck_first(:default_top_period)) ||
|
||||
SiteSetting.top_page_default_timeframe
|
||||
).to_sym
|
||||
|
||||
best_period_with_topics_for(previous_visit_at, category_id, default_period) || default_period
|
||||
end
|
||||
|
||||
def self.best_period_with_topics_for(previous_visit_at, category_id = nil, default_period = SiteSetting.top_page_default_timeframe)
|
||||
def self.best_period_with_topics_for(
|
||||
previous_visit_at,
|
||||
category_id = nil,
|
||||
default_period = SiteSetting.top_page_default_timeframe
|
||||
)
|
||||
best_periods_for(previous_visit_at, default_period.to_sym).find do |period|
|
||||
top_topics = TopTopic.where("#{period}_score > 0")
|
||||
top_topics = top_topics.joins(:topic).where("topics.category_id = ?", category_id) if category_id
|
||||
top_topics =
|
||||
top_topics.joins(:topic).where("topics.category_id = ?", category_id) if category_id
|
||||
top_topics = top_topics.limit(SiteSetting.topics_per_period_in_top_page)
|
||||
top_topics.count == SiteSetting.topics_per_period_in_top_page
|
||||
end
|
||||
|
@ -473,5 +489,4 @@ class ListController < ApplicationController
|
|||
periods << :all
|
||||
periods
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ class MetadataController < ApplicationController
|
|||
|
||||
def manifest
|
||||
expires_in 1.minutes
|
||||
render json: default_manifest.to_json, content_type: 'application/manifest+json'
|
||||
render json: default_manifest.to_json, content_type: "application/manifest+json"
|
||||
end
|
||||
|
||||
def opensearch
|
||||
|
@ -17,13 +17,13 @@ class MetadataController < ApplicationController
|
|||
def app_association_android
|
||||
raise Discourse::NotFound unless SiteSetting.app_association_android.present?
|
||||
expires_in 1.minutes
|
||||
render plain: SiteSetting.app_association_android, content_type: 'application/json'
|
||||
render plain: SiteSetting.app_association_android, content_type: "application/json"
|
||||
end
|
||||
|
||||
def app_association_ios
|
||||
raise Discourse::NotFound unless SiteSetting.app_association_ios.present?
|
||||
expires_in 1.minutes
|
||||
render plain: SiteSetting.app_association_ios, content_type: 'application/json'
|
||||
render plain: SiteSetting.app_association_ios, content_type: "application/json"
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -32,56 +32,56 @@ class MetadataController < ApplicationController
|
|||
display = "standalone"
|
||||
if request.user_agent
|
||||
regex = Regexp.new(SiteSetting.pwa_display_browser_regex)
|
||||
if regex.match(request.user_agent)
|
||||
display = "browser"
|
||||
end
|
||||
display = "browser" if regex.match(request.user_agent)
|
||||
end
|
||||
|
||||
scheme_id = view_context.scheme_id
|
||||
primary_color = ColorScheme.hex_for_name('primary', scheme_id)
|
||||
icon_url_base = UrlHelper.absolute("/svg-sprite/#{Discourse.current_hostname}/icon/#{primary_color}")
|
||||
primary_color = ColorScheme.hex_for_name("primary", scheme_id)
|
||||
icon_url_base =
|
||||
UrlHelper.absolute("/svg-sprite/#{Discourse.current_hostname}/icon/#{primary_color}")
|
||||
|
||||
manifest = {
|
||||
name: SiteSetting.title,
|
||||
short_name: SiteSetting.short_title.presence || SiteSetting.title.truncate(12, separator: ' ', omission: ''),
|
||||
short_name:
|
||||
SiteSetting.short_title.presence ||
|
||||
SiteSetting.title.truncate(12, separator: " ", omission: ""),
|
||||
description: SiteSetting.site_description,
|
||||
display: display,
|
||||
start_url: Discourse.base_path.present? ? "#{Discourse.base_path}/" : '.',
|
||||
background_color: "##{ColorScheme.hex_for_name('secondary', scheme_id)}",
|
||||
theme_color: "##{ColorScheme.hex_for_name('header_background', scheme_id)}",
|
||||
icons: [
|
||||
],
|
||||
start_url: Discourse.base_path.present? ? "#{Discourse.base_path}/" : ".",
|
||||
background_color: "##{ColorScheme.hex_for_name("secondary", scheme_id)}",
|
||||
theme_color: "##{ColorScheme.hex_for_name("header_background", scheme_id)}",
|
||||
icons: [],
|
||||
share_target: {
|
||||
action: "#{Discourse.base_path}/new-topic",
|
||||
method: "GET",
|
||||
enctype: "application/x-www-form-urlencoded",
|
||||
params: {
|
||||
title: "title",
|
||||
text: "body"
|
||||
}
|
||||
text: "body",
|
||||
},
|
||||
},
|
||||
shortcuts: [
|
||||
{
|
||||
name: I18n.t('js.topic.create_long'),
|
||||
short_name: I18n.t('js.topic.create'),
|
||||
name: I18n.t("js.topic.create_long"),
|
||||
short_name: I18n.t("js.topic.create"),
|
||||
url: "#{Discourse.base_path}/new-topic",
|
||||
},
|
||||
{
|
||||
name: I18n.t('js.user.messages.inbox'),
|
||||
short_name: I18n.t('js.user.messages.inbox'),
|
||||
name: I18n.t("js.user.messages.inbox"),
|
||||
short_name: I18n.t("js.user.messages.inbox"),
|
||||
url: "#{Discourse.base_path}/my/messages",
|
||||
},
|
||||
{
|
||||
name: I18n.t('js.user.bookmarks'),
|
||||
short_name: I18n.t('js.user.bookmarks'),
|
||||
name: I18n.t("js.user.bookmarks"),
|
||||
short_name: I18n.t("js.user.bookmarks"),
|
||||
url: "#{Discourse.base_path}/my/activity/bookmarks",
|
||||
},
|
||||
{
|
||||
name: I18n.t('js.filters.top.title'),
|
||||
short_name: I18n.t('js.filters.top.title'),
|
||||
name: I18n.t("js.filters.top.title"),
|
||||
short_name: I18n.t("js.filters.top.title"),
|
||||
url: "#{Discourse.base_path}/top",
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
logo = SiteSetting.site_manifest_icon_url
|
||||
|
@ -89,14 +89,17 @@ class MetadataController < ApplicationController
|
|||
icon_entry = {
|
||||
src: UrlHelper.absolute(logo),
|
||||
sizes: "512x512",
|
||||
type: MiniMime.lookup_by_filename(logo)&.content_type || "image/png"
|
||||
type: MiniMime.lookup_by_filename(logo)&.content_type || "image/png",
|
||||
}
|
||||
manifest[:icons] << icon_entry.dup
|
||||
icon_entry[:purpose] = "maskable"
|
||||
manifest[:icons] << icon_entry
|
||||
end
|
||||
|
||||
SiteSetting.manifest_screenshots.split('|').each do |image|
|
||||
SiteSetting
|
||||
.manifest_screenshots
|
||||
.split("|")
|
||||
.each do |image|
|
||||
next unless Discourse.store.has_been_uploaded?(image)
|
||||
|
||||
upload = Upload.find_by(sha1: Upload.extract_sha1(image))
|
||||
|
@ -107,23 +110,19 @@ class MetadataController < ApplicationController
|
|||
manifest[:screenshots] << {
|
||||
src: UrlHelper.absolute(image),
|
||||
sizes: "#{upload.width}x#{upload.height}",
|
||||
type: "image/#{upload.extension}"
|
||||
type: "image/#{upload.extension}",
|
||||
}
|
||||
end
|
||||
|
||||
if current_user && current_user.trust_level >= 1 && SiteSetting.native_app_install_banner_android
|
||||
manifest = manifest.merge(
|
||||
if current_user && current_user.trust_level >= 1 &&
|
||||
SiteSetting.native_app_install_banner_android
|
||||
manifest =
|
||||
manifest.merge(
|
||||
prefer_related_applications: true,
|
||||
related_applications: [
|
||||
{
|
||||
platform: "play",
|
||||
id: SiteSetting.android_app_id
|
||||
}
|
||||
]
|
||||
related_applications: [{ platform: "play", id: SiteSetting.android_app_id }],
|
||||
)
|
||||
end
|
||||
|
||||
manifest
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class NewTopicController < ApplicationController
|
||||
def index; end
|
||||
def index
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class NotificationsController < ApplicationController
|
||||
|
||||
requires_login
|
||||
before_action :ensure_admin, only: [:create, :update, :destroy]
|
||||
before_action :set_notification, only: [:update, :destroy]
|
||||
before_action :ensure_admin, only: %i[create update destroy]
|
||||
before_action :set_notification, only: %i[update destroy]
|
||||
|
||||
def index
|
||||
user =
|
||||
|
@ -20,9 +19,8 @@ class NotificationsController < ApplicationController
|
|||
|
||||
if notification_types = params[:filter_by_types]&.split(",").presence
|
||||
notification_types.map! do |type|
|
||||
Notification.types[type.to_sym] || (
|
||||
raise Discourse::InvalidParameters.new("invalid notification type: #{type}")
|
||||
)
|
||||
Notification.types[type.to_sym] ||
|
||||
(raise Discourse::InvalidParameters.new("invalid notification type: #{type}"))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -35,7 +33,8 @@ class NotificationsController < ApplicationController
|
|||
if SiteSetting.legacy_navigation_menu?
|
||||
notifications = Notification.recent_report(current_user, limit, notification_types)
|
||||
else
|
||||
notifications = Notification.prioritized_list(current_user, count: limit, types: notification_types)
|
||||
notifications =
|
||||
Notification.prioritized_list(current_user, count: limit, types: notification_types)
|
||||
# notification_types is blank for the "all notifications" user menu tab
|
||||
include_reviewables = notification_types.blank? && guardian.can_see_review_queue?
|
||||
end
|
||||
|
@ -47,7 +46,8 @@ class NotificationsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
if !params.has_key?(:silent) && params[:bump_last_seen_reviewable] && !@readonly_mode && include_reviewables
|
||||
if !params.has_key?(:silent) && params[:bump_last_seen_reviewable] && !@readonly_mode &&
|
||||
include_reviewables
|
||||
current_user_id = current_user.id
|
||||
Scheduler::Defer.later "bump last seen reviewable for user" do
|
||||
# we lookup current_user again in the background thread to avoid
|
||||
|
@ -62,13 +62,13 @@ class NotificationsController < ApplicationController
|
|||
|
||||
json = {
|
||||
notifications: serialize_data(notifications, NotificationSerializer),
|
||||
seen_notification_id: current_user.seen_notification_id
|
||||
seen_notification_id: current_user.seen_notification_id,
|
||||
}
|
||||
|
||||
if include_reviewables
|
||||
json[:pending_reviewables] = Reviewable.basic_serializers_for_list(
|
||||
Reviewable.user_menu_list_for(current_user),
|
||||
current_user
|
||||
current_user,
|
||||
).as_json
|
||||
end
|
||||
|
||||
|
@ -76,10 +76,8 @@ class NotificationsController < ApplicationController
|
|||
else
|
||||
offset = params[:offset].to_i
|
||||
|
||||
notifications = Notification.where(user_id: user.id)
|
||||
.visible
|
||||
.includes(:topic)
|
||||
.order(created_at: :desc)
|
||||
notifications =
|
||||
Notification.where(user_id: user.id).visible.includes(:topic).order(created_at: :desc)
|
||||
|
||||
notifications = notifications.where(read: true) if params[:filter] == "read"
|
||||
|
||||
|
@ -88,12 +86,14 @@ class NotificationsController < ApplicationController
|
|||
total_rows = notifications.dup.count
|
||||
notifications = notifications.offset(offset).limit(60)
|
||||
notifications = filter_inaccessible_notifications(notifications)
|
||||
render_json_dump(notifications: serialize_data(notifications, NotificationSerializer),
|
||||
render_json_dump(
|
||||
notifications: serialize_data(notifications, NotificationSerializer),
|
||||
total_rows_notifications: total_rows,
|
||||
seen_notification_id: user.seen_notification_id,
|
||||
load_more_notifications: notifications_path(username: user.username, offset: offset + 60, filter: params[:filter]))
|
||||
load_more_notifications:
|
||||
notifications_path(username: user.username, offset: offset + 60, filter: params[:filter]),
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def mark_read
|
||||
|
@ -144,7 +144,15 @@ class NotificationsController < ApplicationController
|
|||
end
|
||||
|
||||
def notification_params
|
||||
params.permit(:notification_type, :user_id, :data, :read, :topic_id, :post_number, :post_action_id)
|
||||
params.permit(
|
||||
:notification_type,
|
||||
:user_id,
|
||||
:data,
|
||||
:read,
|
||||
:topic_id,
|
||||
:post_number,
|
||||
:post_action_id,
|
||||
)
|
||||
end
|
||||
|
||||
def render_notification
|
||||
|
|
|
@ -5,6 +5,6 @@ class OfflineController < ApplicationController
|
|||
skip_before_action :preload_json, :check_xhr, :redirect_to_login_if_required
|
||||
|
||||
def index
|
||||
render :offline, content_type: 'text/html'
|
||||
render :offline, content_type: "text/html"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class OneboxController < ApplicationController
|
|||
requires_login
|
||||
|
||||
def show
|
||||
unless params[:refresh] == 'true'
|
||||
unless params[:refresh] == "true"
|
||||
preview = Oneboxer.cached_preview(params[:url])
|
||||
preview = preview.strip if preview.present?
|
||||
return render(plain: preview) if preview.present?
|
||||
|
@ -16,7 +16,7 @@ class OneboxController < ApplicationController
|
|||
user_id = current_user.id
|
||||
category_id = params[:category_id].to_i
|
||||
topic_id = params[:topic_id].to_i
|
||||
invalidate = params[:refresh] == 'true'
|
||||
invalidate = params[:refresh] == "true"
|
||||
url = params[:url]
|
||||
|
||||
return render(body: nil, status: 404) if Oneboxer.recently_failed?(url)
|
||||
|
@ -24,11 +24,13 @@ class OneboxController < ApplicationController
|
|||
hijack(info: "#{url} topic_id: #{topic_id} user_id: #{user_id}") do
|
||||
Oneboxer.preview_onebox!(user_id)
|
||||
|
||||
preview = Oneboxer.preview(url,
|
||||
preview =
|
||||
Oneboxer.preview(
|
||||
url,
|
||||
invalidate_oneboxes: invalidate,
|
||||
user_id: user_id,
|
||||
category_id: category_id,
|
||||
topic_id: topic_id
|
||||
topic_id: topic_id,
|
||||
)
|
||||
|
||||
preview = preview.strip if preview.present?
|
||||
|
@ -43,5 +45,4 @@ class OneboxController < ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -33,12 +33,8 @@ class PermalinksController < ApplicationController
|
|||
|
||||
render json: MultiJson.dump(data)
|
||||
rescue Discourse::NotFound
|
||||
data = {
|
||||
found: false,
|
||||
html: build_not_found_page(status: 200),
|
||||
}
|
||||
data = { found: false, html: build_not_found_page(status: 200) }
|
||||
render json: MultiJson.dump(data)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -23,10 +23,13 @@ class PostActionUsersController < ApplicationController
|
|||
unknown_user_ids.merge(result)
|
||||
end
|
||||
|
||||
post_actions = post.post_actions.where(post_action_type_id: post_action_type_id)
|
||||
post_actions =
|
||||
post
|
||||
.post_actions
|
||||
.where(post_action_type_id: post_action_type_id)
|
||||
.includes(:user)
|
||||
.offset(page * page_size)
|
||||
.order('post_actions.created_at ASC')
|
||||
.order("post_actions.created_at ASC")
|
||||
.limit(page_size)
|
||||
|
||||
if !guardian.can_see_post_actors?(post.topic, post_action_type_id)
|
||||
|
@ -38,16 +41,15 @@ class PostActionUsersController < ApplicationController
|
|||
total_count = post["#{action_type}_count"].to_i
|
||||
|
||||
data = {
|
||||
post_action_users: serialize_data(
|
||||
post_action_users:
|
||||
serialize_data(
|
||||
post_actions.to_a,
|
||||
PostActionUserSerializer,
|
||||
unknown_user_ids: unknown_user_ids
|
||||
)
|
||||
unknown_user_ids: unknown_user_ids,
|
||||
),
|
||||
}
|
||||
|
||||
if total_count > page_size
|
||||
data[:total_rows_post_action_users] = total_count
|
||||
end
|
||||
data[:total_rows_post_action_users] = total_count if total_count > page_size
|
||||
|
||||
render_json_dump(data)
|
||||
end
|
||||
|
|
|
@ -9,15 +9,16 @@ class PostActionsController < ApplicationController
|
|||
def create
|
||||
raise Discourse::NotFound if @post.blank?
|
||||
|
||||
creator = PostActionCreator.new(
|
||||
creator =
|
||||
PostActionCreator.new(
|
||||
current_user,
|
||||
@post,
|
||||
@post_action_type_id,
|
||||
is_warning: params[:is_warning],
|
||||
message: params[:message],
|
||||
take_action: params[:take_action] == 'true',
|
||||
flag_topic: params[:flag_topic] == 'true',
|
||||
queue_for_review: params[:queue_for_review] == 'true'
|
||||
take_action: params[:take_action] == "true",
|
||||
flag_topic: params[:flag_topic] == "true",
|
||||
queue_for_review: params[:queue_for_review] == "true",
|
||||
)
|
||||
result = creator.perform
|
||||
|
||||
|
@ -29,18 +30,19 @@ class PostActionsController < ApplicationController
|
|||
|
||||
if @post_action_type_id == PostActionType.types[:like]
|
||||
limiter = result.post_action.post_action_rate_limiter
|
||||
response.headers['Discourse-Actions-Remaining'] = limiter.remaining.to_s
|
||||
response.headers['Discourse-Actions-Max'] = limiter.max.to_s
|
||||
response.headers["Discourse-Actions-Remaining"] = limiter.remaining.to_s
|
||||
response.headers["Discourse-Actions-Max"] = limiter.max.to_s
|
||||
end
|
||||
render_post_json(@post, add_raw: false)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
result = PostActionDestroyer.new(
|
||||
result =
|
||||
PostActionDestroyer.new(
|
||||
current_user,
|
||||
Post.find_by(id: params[:id].to_i),
|
||||
@post_action_type_id
|
||||
@post_action_type_id,
|
||||
).perform
|
||||
|
||||
if result.failed?
|
||||
|
@ -58,10 +60,11 @@ class PostActionsController < ApplicationController
|
|||
flag_topic = params[:flag_topic]
|
||||
flag_topic = flag_topic && (flag_topic == true || flag_topic == "true")
|
||||
|
||||
post_id = if flag_topic
|
||||
post_id =
|
||||
if flag_topic
|
||||
begin
|
||||
Topic.find(params[:id]).posts.first.id
|
||||
rescue
|
||||
rescue StandardError
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
else
|
||||
|
|
|
@ -7,21 +7,28 @@ class PostReadersController < ApplicationController
|
|||
post = Post.includes(topic: %i[topic_allowed_groups topic_allowed_users]).find(params[:id])
|
||||
ensure_can_see_readers!(post)
|
||||
|
||||
readers = User
|
||||
readers =
|
||||
User
|
||||
.real
|
||||
.where(staged: false)
|
||||
.where.not(id: post.user_id)
|
||||
.joins(:topic_users)
|
||||
.where.not(topic_users: { last_read_post_number: nil })
|
||||
.where('topic_users.topic_id = ? AND topic_users.last_read_post_number >= ?', post.topic_id, post.post_number)
|
||||
.where(
|
||||
"topic_users.topic_id = ? AND topic_users.last_read_post_number >= ?",
|
||||
post.topic_id,
|
||||
post.post_number,
|
||||
)
|
||||
|
||||
readers = readers.where('admin OR moderator') if post.whisper?
|
||||
readers = readers.where("admin OR moderator") if post.whisper?
|
||||
|
||||
readers = readers.map do |r|
|
||||
readers =
|
||||
readers.map do |r|
|
||||
{
|
||||
id: r.id, avatar_template: r.avatar_template,
|
||||
id: r.id,
|
||||
avatar_template: r.avatar_template,
|
||||
username: r.username,
|
||||
username_lower: r.username_lower
|
||||
username_lower: r.username_lower,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -31,10 +38,17 @@ class PostReadersController < ApplicationController
|
|||
private
|
||||
|
||||
def ensure_can_see_readers!(post)
|
||||
show_readers = GroupUser
|
||||
show_readers =
|
||||
GroupUser
|
||||
.where(user: current_user)
|
||||
.joins(:group)
|
||||
.where(groups: { id: post.topic.topic_allowed_groups.map(&:group_id), publish_read_state: true }).exists?
|
||||
.where(
|
||||
groups: {
|
||||
id: post.topic.topic_allowed_groups.map(&:group_id),
|
||||
publish_read_state: true,
|
||||
},
|
||||
)
|
||||
.exists?
|
||||
|
||||
raise Discourse::InvalidAccess unless show_readers
|
||||
end
|
||||
|
|
|
@ -5,31 +5,27 @@ class PostsController < ApplicationController
|
|||
# see https://github.com/rails/rails/issues/44867
|
||||
self._flash_types -= [:notice]
|
||||
|
||||
requires_login except: [
|
||||
:show,
|
||||
:replies,
|
||||
:by_number,
|
||||
:by_date,
|
||||
:short_link,
|
||||
:reply_history,
|
||||
:reply_ids,
|
||||
:revisions,
|
||||
:latest_revision,
|
||||
:expand_embed,
|
||||
:markdown_id,
|
||||
:markdown_num,
|
||||
:cooked,
|
||||
:latest,
|
||||
:user_posts_feed
|
||||
requires_login except: %i[
|
||||
show
|
||||
replies
|
||||
by_number
|
||||
by_date
|
||||
short_link
|
||||
reply_history
|
||||
reply_ids
|
||||
revisions
|
||||
latest_revision
|
||||
expand_embed
|
||||
markdown_id
|
||||
markdown_num
|
||||
cooked
|
||||
latest
|
||||
user_posts_feed
|
||||
]
|
||||
|
||||
skip_before_action :preload_json, :check_xhr, only: [
|
||||
:markdown_id,
|
||||
:markdown_num,
|
||||
:short_link,
|
||||
:latest,
|
||||
:user_posts_feed
|
||||
]
|
||||
skip_before_action :preload_json,
|
||||
:check_xhr,
|
||||
only: %i[markdown_id markdown_num short_link latest user_posts_feed]
|
||||
|
||||
MARKDOWN_TOPIC_PAGE_SIZE ||= 100
|
||||
|
||||
|
@ -42,13 +38,15 @@ class PostsController < ApplicationController
|
|||
post_revision = find_post_revision_from_topic_id
|
||||
render plain: post_revision.modifications[:raw].last
|
||||
elsif params[:post_number].present?
|
||||
markdown Post.find_by(topic_id: params[:topic_id].to_i, post_number: params[:post_number].to_i)
|
||||
markdown Post.find_by(
|
||||
topic_id: params[:topic_id].to_i,
|
||||
post_number: params[:post_number].to_i,
|
||||
)
|
||||
else
|
||||
opts = params.slice(:page)
|
||||
opts[:limit] = MARKDOWN_TOPIC_PAGE_SIZE
|
||||
topic_view = TopicView.new(params[:topic_id], current_user, opts)
|
||||
content = topic_view.posts.map do |p|
|
||||
<<~MD
|
||||
content = topic_view.posts.map { |p| <<~MD }
|
||||
#{p.user.username} | #{p.updated_at} | ##{p.post_number}
|
||||
|
||||
#{p.raw}
|
||||
|
@ -56,7 +54,6 @@ class PostsController < ApplicationController
|
|||
-------------------------
|
||||
|
||||
MD
|
||||
end
|
||||
render plain: content.join
|
||||
end
|
||||
end
|
||||
|
@ -68,24 +65,28 @@ class PostsController < ApplicationController
|
|||
|
||||
if params[:id] == "private_posts"
|
||||
raise Discourse::NotFound if current_user.nil?
|
||||
posts = Post.private_posts
|
||||
posts =
|
||||
Post
|
||||
.private_posts
|
||||
.order(created_at: :desc)
|
||||
.where('posts.id <= ?', last_post_id)
|
||||
.where('posts.id > ?', last_post_id - 50)
|
||||
.where("posts.id <= ?", last_post_id)
|
||||
.where("posts.id > ?", last_post_id - 50)
|
||||
.includes(topic: :category)
|
||||
.includes(user: [:primary_group, :flair_group])
|
||||
.includes(user: %i[primary_group flair_group])
|
||||
.includes(:reply_to_user)
|
||||
.limit(50)
|
||||
rss_description = I18n.t("rss_description.private_posts")
|
||||
else
|
||||
posts = Post.public_posts
|
||||
posts =
|
||||
Post
|
||||
.public_posts
|
||||
.visible
|
||||
.where(post_type: Post.types[:regular])
|
||||
.order(created_at: :desc)
|
||||
.where('posts.id <= ?', last_post_id)
|
||||
.where('posts.id > ?', last_post_id - 50)
|
||||
.where("posts.id <= ?", last_post_id)
|
||||
.where("posts.id > ?", last_post_id - 50)
|
||||
.includes(topic: :category)
|
||||
.includes(user: [:primary_group, :flair_group])
|
||||
.includes(user: %i[primary_group flair_group])
|
||||
.includes(:reply_to_user)
|
||||
.limit(50)
|
||||
rss_description = I18n.t("rss_description.posts")
|
||||
|
@ -103,16 +104,19 @@ class PostsController < ApplicationController
|
|||
@title = "#{SiteSetting.title} - #{rss_description}"
|
||||
@link = Discourse.base_url
|
||||
@description = rss_description
|
||||
render 'posts/latest', formats: [:rss]
|
||||
render "posts/latest", formats: [:rss]
|
||||
end
|
||||
format.json do
|
||||
render_json_dump(serialize_data(posts,
|
||||
render_json_dump(
|
||||
serialize_data(
|
||||
posts,
|
||||
PostSerializer,
|
||||
scope: guardian,
|
||||
root: params[:id],
|
||||
add_raw: true,
|
||||
add_title: true,
|
||||
all_post_actions: counts)
|
||||
all_post_actions: counts,
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -123,7 +127,9 @@ class PostsController < ApplicationController
|
|||
user = fetch_user_from_params
|
||||
raise Discourse::NotFound unless guardian.can_see_profile?(user)
|
||||
|
||||
posts = Post.public_posts
|
||||
posts =
|
||||
Post
|
||||
.public_posts
|
||||
.visible
|
||||
.where(user_id: user.id)
|
||||
.where(post_type: Post.types[:regular])
|
||||
|
@ -137,21 +143,17 @@ class PostsController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.rss do
|
||||
@posts = posts
|
||||
@title = "#{SiteSetting.title} - #{I18n.t("rss_description.user_posts", username: user.username)}"
|
||||
@title =
|
||||
"#{SiteSetting.title} - #{I18n.t("rss_description.user_posts", username: user.username)}"
|
||||
@link = "#{user.full_url}/activity"
|
||||
@description = I18n.t("rss_description.user_posts", username: user.username)
|
||||
render 'posts/latest', formats: [:rss]
|
||||
render "posts/latest", formats: [:rss]
|
||||
end
|
||||
|
||||
format.json do
|
||||
render_json_dump(serialize_data(posts,
|
||||
PostSerializer,
|
||||
scope: guardian,
|
||||
add_excerpt: true)
|
||||
)
|
||||
render_json_dump(serialize_data(posts, PostSerializer, scope: guardian, add_excerpt: true))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def cooked
|
||||
|
@ -173,7 +175,7 @@ class PostsController < ApplicationController
|
|||
# Stuff the user in the request object, because that's what IncomingLink wants
|
||||
if params[:user_id]
|
||||
user = User.find_by(id: params[:user_id].to_i)
|
||||
request['u'] = user.username_lower if user
|
||||
request["u"] = user.username_lower if user
|
||||
end
|
||||
|
||||
guardian.ensure_can_see!(post)
|
||||
|
@ -188,13 +190,14 @@ class PostsController < ApplicationController
|
|||
manager = NewPostManager.new(current_user, @manager_params)
|
||||
|
||||
if is_api?
|
||||
memoized_payload = DistributedMemoizer.memoize(signature_for(@manager_params), 120) do
|
||||
memoized_payload =
|
||||
DistributedMemoizer.memoize(signature_for(@manager_params), 120) do
|
||||
result = manager.perform
|
||||
MultiJson.dump(serialize_data(result, NewPostResultSerializer, root: false))
|
||||
end
|
||||
|
||||
parsed_payload = JSON.parse(memoized_payload)
|
||||
backwards_compatible_json(parsed_payload, parsed_payload['success'])
|
||||
backwards_compatible_json(parsed_payload, parsed_payload["success"])
|
||||
else
|
||||
result = manager.perform
|
||||
json = serialize_data(result, NewPostResultSerializer, root: false)
|
||||
|
@ -213,27 +216,20 @@ class PostsController < ApplicationController
|
|||
|
||||
post.image_sizes = params[:image_sizes] if params[:image_sizes].present?
|
||||
|
||||
if !guardian.public_send("can_edit?", post) &&
|
||||
post.user_id == current_user.id &&
|
||||
if !guardian.public_send("can_edit?", post) && post.user_id == current_user.id &&
|
||||
post.edit_time_limit_expired?(current_user)
|
||||
|
||||
return render_json_error(I18n.t('too_late_to_edit'))
|
||||
return render_json_error(I18n.t("too_late_to_edit"))
|
||||
end
|
||||
|
||||
guardian.ensure_can_edit!(post)
|
||||
|
||||
changes = {
|
||||
raw: params[:post][:raw],
|
||||
edit_reason: params[:post][:edit_reason]
|
||||
}
|
||||
changes = { raw: params[:post][:raw], edit_reason: params[:post][:edit_reason] }
|
||||
|
||||
Post.plugin_permitted_update_params.keys.each do |param|
|
||||
changes[param] = params[:post][param]
|
||||
end
|
||||
Post.plugin_permitted_update_params.keys.each { |param| changes[param] = params[:post][param] }
|
||||
|
||||
raw_old = params[:post][:raw_old]
|
||||
if raw_old.present? && raw_old != post.raw
|
||||
return render_json_error(I18n.t('edit_conflict'), status: 409)
|
||||
return render_json_error(I18n.t("edit_conflict"), status: 409)
|
||||
end
|
||||
|
||||
# to stay consistent with the create api, we allow for title & category changes here
|
||||
|
@ -246,7 +242,7 @@ class PostsController < ApplicationController
|
|||
if category || (changes[:category_id].to_i == 0)
|
||||
guardian.ensure_can_move_topic_to_category!(category)
|
||||
else
|
||||
return render_json_error(I18n.t('category.errors.not_found'))
|
||||
return render_json_error(I18n.t("category.errors.not_found"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -273,7 +269,11 @@ class PostsController < ApplicationController
|
|||
|
||||
result = { post: post_serializer.as_json }
|
||||
if revisor.category_changed.present?
|
||||
result[:category] = BasicCategorySerializer.new(revisor.category_changed, scope: guardian, root: false).as_json
|
||||
result[:category] = BasicCategorySerializer.new(
|
||||
revisor.category_changed,
|
||||
scope: guardian,
|
||||
root: false,
|
||||
).as_json
|
||||
end
|
||||
|
||||
render_json_dump(result)
|
||||
|
@ -303,11 +303,7 @@ class PostsController < ApplicationController
|
|||
user_custom_fields = User.custom_fields_for_ids(reply_history.pluck(:user_id), added_fields)
|
||||
end
|
||||
|
||||
render_serialized(
|
||||
reply_history,
|
||||
PostSerializer,
|
||||
user_custom_fields: user_custom_fields
|
||||
)
|
||||
render_serialized(reply_history, PostSerializer, user_custom_fields: user_custom_fields)
|
||||
end
|
||||
|
||||
def reply_ids
|
||||
|
@ -335,15 +331,25 @@ class PostsController < ApplicationController
|
|||
end
|
||||
|
||||
unless guardian.can_moderate_topic?(post.topic)
|
||||
RateLimiter.new(current_user, "delete_post_per_min", SiteSetting.max_post_deletions_per_minute, 1.minute).performed!
|
||||
RateLimiter.new(current_user, "delete_post_per_day", SiteSetting.max_post_deletions_per_day, 1.day).performed!
|
||||
RateLimiter.new(
|
||||
current_user,
|
||||
"delete_post_per_min",
|
||||
SiteSetting.max_post_deletions_per_minute,
|
||||
1.minute,
|
||||
).performed!
|
||||
RateLimiter.new(
|
||||
current_user,
|
||||
"delete_post_per_day",
|
||||
SiteSetting.max_post_deletions_per_day,
|
||||
1.day,
|
||||
).performed!
|
||||
end
|
||||
|
||||
PostDestroyer.new(
|
||||
current_user,
|
||||
post,
|
||||
context: params[:context],
|
||||
force_destroy: force_destroy
|
||||
force_destroy: force_destroy,
|
||||
).destroy
|
||||
|
||||
render body: nil
|
||||
|
@ -351,8 +357,8 @@ class PostsController < ApplicationController
|
|||
|
||||
def expand_embed
|
||||
render json: { cooked: TopicEmbed.expanded_for(find_post_from_params) }
|
||||
rescue
|
||||
render_json_error I18n.t('errors.embed.load_from_remote')
|
||||
rescue StandardError
|
||||
render_json_error I18n.t("errors.embed.load_from_remote")
|
||||
end
|
||||
|
||||
def recover
|
||||
|
@ -360,8 +366,18 @@ class PostsController < ApplicationController
|
|||
guardian.ensure_can_recover_post!(post)
|
||||
|
||||
unless guardian.can_moderate_topic?(post.topic)
|
||||
RateLimiter.new(current_user, "delete_post_per_min", SiteSetting.max_post_deletions_per_minute, 1.minute).performed!
|
||||
RateLimiter.new(current_user, "delete_post_per_day", SiteSetting.max_post_deletions_per_day, 1.day).performed!
|
||||
RateLimiter.new(
|
||||
current_user,
|
||||
"delete_post_per_min",
|
||||
SiteSetting.max_post_deletions_per_minute,
|
||||
1.minute,
|
||||
).performed!
|
||||
RateLimiter.new(
|
||||
current_user,
|
||||
"delete_post_per_day",
|
||||
SiteSetting.max_post_deletions_per_day,
|
||||
1.day,
|
||||
).performed!
|
||||
end
|
||||
|
||||
destroyer = PostDestroyer.new(current_user, post)
|
||||
|
@ -383,7 +399,11 @@ class PostsController < ApplicationController
|
|||
|
||||
Post.transaction do
|
||||
posts.each_with_index do |p, i|
|
||||
PostDestroyer.new(current_user, p, defer_flags: !(agree_with_first_reply_flag && i == 0)).destroy
|
||||
PostDestroyer.new(
|
||||
current_user,
|
||||
p,
|
||||
defer_flags: !(agree_with_first_reply_flag && i == 0),
|
||||
).destroy
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -418,7 +438,8 @@ class PostsController < ApplicationController
|
|||
raise Discourse::NotFound if post.hidden && !guardian.can_view_hidden_post_revisions?
|
||||
|
||||
post_revision = find_post_revision_from_params
|
||||
post_revision_serializer = PostRevisionSerializer.new(post_revision, scope: guardian, root: false)
|
||||
post_revision_serializer =
|
||||
PostRevisionSerializer.new(post_revision, scope: guardian, root: false)
|
||||
render_json_dump(post_revision_serializer)
|
||||
end
|
||||
|
||||
|
@ -427,7 +448,8 @@ class PostsController < ApplicationController
|
|||
raise Discourse::NotFound if post.hidden && !guardian.can_view_hidden_post_revisions?
|
||||
|
||||
post_revision = find_latest_post_revision_from_params
|
||||
post_revision_serializer = PostRevisionSerializer.new(post_revision, scope: guardian, root: false)
|
||||
post_revision_serializer =
|
||||
PostRevisionSerializer.new(post_revision, scope: guardian, root: false)
|
||||
render_json_dump(post_revision_serializer)
|
||||
end
|
||||
|
||||
|
@ -473,17 +495,27 @@ class PostsController < ApplicationController
|
|||
post_revision.post = post
|
||||
guardian.ensure_can_see!(post_revision)
|
||||
guardian.ensure_can_edit!(post)
|
||||
return render_json_error(I18n.t('revert_version_same')) if post_revision.modifications["raw"].blank? && post_revision.modifications["title"].blank? && post_revision.modifications["category_id"].blank?
|
||||
if post_revision.modifications["raw"].blank? && post_revision.modifications["title"].blank? &&
|
||||
post_revision.modifications["category_id"].blank?
|
||||
return render_json_error(I18n.t("revert_version_same"))
|
||||
end
|
||||
|
||||
topic = Topic.with_deleted.find(post.topic_id)
|
||||
|
||||
changes = {}
|
||||
changes[:raw] = post_revision.modifications["raw"][0] if post_revision.modifications["raw"].present? && post_revision.modifications["raw"][0] != post.raw
|
||||
changes[:raw] = post_revision.modifications["raw"][0] if post_revision.modifications[
|
||||
"raw"
|
||||
].present? && post_revision.modifications["raw"][0] != post.raw
|
||||
if post.is_first_post?
|
||||
changes[:title] = post_revision.modifications["title"][0] if post_revision.modifications["title"].present? && post_revision.modifications["title"][0] != topic.title
|
||||
changes[:category_id] = post_revision.modifications["category_id"][0] if post_revision.modifications["category_id"].present? && post_revision.modifications["category_id"][0] != topic.category.id
|
||||
changes[:title] = post_revision.modifications["title"][0] if post_revision.modifications[
|
||||
"title"
|
||||
].present? && post_revision.modifications["title"][0] != topic.title
|
||||
changes[:category_id] = post_revision.modifications["category_id"][
|
||||
0
|
||||
] if post_revision.modifications["category_id"].present? &&
|
||||
post_revision.modifications["category_id"][0] != topic.category.id
|
||||
end
|
||||
return render_json_error(I18n.t('revert_version_same')) unless changes.length > 0
|
||||
return render_json_error(I18n.t("revert_version_same")) unless changes.length > 0
|
||||
changes[:edit_reason] = "reverted to version ##{post_revision.number.to_i - 1}"
|
||||
|
||||
revisor = PostRevisor.new(post, topic)
|
||||
|
@ -500,8 +532,14 @@ class PostsController < ApplicationController
|
|||
|
||||
result = { post: post_serializer.as_json }
|
||||
if post.is_first_post?
|
||||
result[:topic] = BasicTopicSerializer.new(topic, scope: guardian, root: false).as_json if post_revision.modifications["title"].present?
|
||||
result[:category_id] = post_revision.modifications["category_id"][0] if post_revision.modifications["category_id"].present?
|
||||
result[:topic] = BasicTopicSerializer.new(
|
||||
topic,
|
||||
scope: guardian,
|
||||
root: false,
|
||||
).as_json if post_revision.modifications["title"].present?
|
||||
result[:category_id] = post_revision.modifications["category_id"][
|
||||
0
|
||||
] if post_revision.modifications["category_id"].present?
|
||||
end
|
||||
|
||||
render_json_dump(result)
|
||||
|
@ -524,7 +562,7 @@ class PostsController < ApplicationController
|
|||
post.custom_fields[Post::NOTICE] = {
|
||||
type: Post.notices[:custom],
|
||||
raw: params[:notice],
|
||||
cooked: PrettyText.cook(params[:notice], features: { onebox: false })
|
||||
cooked: PrettyText.cook(params[:notice], features: { onebox: false }),
|
||||
}
|
||||
else
|
||||
post.custom_fields.delete(Post::NOTICE)
|
||||
|
@ -535,7 +573,7 @@ class PostsController < ApplicationController
|
|||
StaffActionLogger.new(current_user).log_post_staff_note(
|
||||
post,
|
||||
old_value: old_notice&.[]("raw"),
|
||||
new_value: params[:notice]
|
||||
new_value: params[:notice],
|
||||
)
|
||||
|
||||
render body: nil
|
||||
|
@ -544,14 +582,16 @@ class PostsController < ApplicationController
|
|||
def destroy_bookmark
|
||||
params.require(:post_id)
|
||||
|
||||
bookmark_id = Bookmark.where(
|
||||
bookmark_id =
|
||||
Bookmark.where(
|
||||
bookmarkable_id: params[:post_id],
|
||||
bookmarkable_type: "Post",
|
||||
user_id: current_user.id
|
||||
user_id: current_user.id,
|
||||
).pluck_first(:id)
|
||||
destroyed_bookmark = BookmarkManager.new(current_user).destroy(bookmark_id)
|
||||
|
||||
render json: success_json.merge(BookmarkManager.bookmark_metadata(destroyed_bookmark, current_user))
|
||||
render json:
|
||||
success_json.merge(BookmarkManager.bookmark_metadata(destroyed_bookmark, current_user))
|
||||
end
|
||||
|
||||
def wiki
|
||||
|
@ -596,8 +636,9 @@ class PostsController < ApplicationController
|
|||
|
||||
def flagged_posts
|
||||
Discourse.deprecate(
|
||||
'PostsController#flagged_posts is deprecated. Please use /review instead.',
|
||||
since: '2.8.0.beta4', drop_from: '2.9'
|
||||
"PostsController#flagged_posts is deprecated. Please use /review instead.",
|
||||
since: "2.8.0.beta4",
|
||||
drop_from: "2.9",
|
||||
)
|
||||
|
||||
params.permit(:offset, :limit)
|
||||
|
@ -607,10 +648,14 @@ class PostsController < ApplicationController
|
|||
offset = [params[:offset].to_i, 0].max
|
||||
limit = [(params[:limit] || 60).to_i, 100].min
|
||||
|
||||
posts = user_posts(guardian, user.id, offset: offset, limit: limit)
|
||||
.where(id: PostAction.where(post_action_type_id: PostActionType.notify_flag_type_ids)
|
||||
posts =
|
||||
user_posts(guardian, user.id, offset: offset, limit: limit).where(
|
||||
id:
|
||||
PostAction
|
||||
.where(post_action_type_id: PostActionType.notify_flag_type_ids)
|
||||
.where(disagreed_at: nil)
|
||||
.select(:post_id))
|
||||
.select(:post_id),
|
||||
)
|
||||
|
||||
render_serialized(posts, AdminUserActionSerializer)
|
||||
end
|
||||
|
@ -633,7 +678,11 @@ class PostsController < ApplicationController
|
|||
user = fetch_user_from_params
|
||||
raise Discourse::NotFound unless guardian.can_edit_user?(user)
|
||||
|
||||
render_serialized(user.pending_posts.order(created_at: :desc), PendingPostSerializer, root: :pending_posts)
|
||||
render_serialized(
|
||||
user.pending_posts.order(created_at: :desc),
|
||||
PendingPostSerializer,
|
||||
root: :pending_posts,
|
||||
)
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -692,7 +741,8 @@ class PostsController < ApplicationController
|
|||
end
|
||||
|
||||
def find_post_revision_from_topic_id
|
||||
post = Post.find_by(topic_id: params[:topic_id].to_i, post_number: (params[:post_number] || 1).to_i)
|
||||
post =
|
||||
Post.find_by(topic_id: params[:topic_id].to_i, post_number: (params[:post_number] || 1).to_i)
|
||||
raise Discourse::NotFound unless guardian.can_see?(post)
|
||||
|
||||
revision = params[:revision].to_i
|
||||
|
@ -711,26 +761,26 @@ class PostsController < ApplicationController
|
|||
|
||||
def user_posts(guardian, user_id, opts)
|
||||
# Topic.unscoped is necessary to remove the default deleted_at: nil scope
|
||||
posts = Topic.unscoped do
|
||||
Post.includes(:user, :topic, :deleted_by, :user_actions)
|
||||
posts =
|
||||
Topic.unscoped do
|
||||
Post
|
||||
.includes(:user, :topic, :deleted_by, :user_actions)
|
||||
.where(user_id: user_id)
|
||||
.with_deleted
|
||||
.order(created_at: :desc)
|
||||
end
|
||||
|
||||
if guardian.user.moderator?
|
||||
|
||||
# Awful hack, but you can't seem to remove the `default_scope` when joining
|
||||
# So instead I grab the topics separately
|
||||
topic_ids = posts.dup.pluck(:topic_id)
|
||||
topics = Topic.where(id: topic_ids).with_deleted.where.not(archetype: 'private_message')
|
||||
topics = Topic.where(id: topic_ids).with_deleted.where.not(archetype: "private_message")
|
||||
topics = topics.secured(guardian)
|
||||
|
||||
posts = posts.where(topic_id: topics.pluck(:id))
|
||||
end
|
||||
|
||||
posts.offset(opts[:offset])
|
||||
.limit(opts[:limit])
|
||||
posts.offset(opts[:offset]).limit(opts[:limit])
|
||||
end
|
||||
|
||||
def create_params
|
||||
|
@ -747,7 +797,7 @@ class PostsController < ApplicationController
|
|||
:typing_duration_msecs,
|
||||
:composer_open_duration_msecs,
|
||||
:visible,
|
||||
:draft_key
|
||||
:draft_key,
|
||||
]
|
||||
|
||||
Post.plugin_permitted_create_params.each do |key, value|
|
||||
|
@ -785,7 +835,10 @@ class PostsController < ApplicationController
|
|||
permitted << :external_id
|
||||
end
|
||||
|
||||
result = params.permit(*permitted).tap do |allowed|
|
||||
result =
|
||||
params
|
||||
.permit(*permitted)
|
||||
.tap do |allowed|
|
||||
allowed[:image_sizes] = params[:image_sizes]
|
||||
# TODO this does not feel right, we should name what meta_data is allowed
|
||||
allowed[:meta_data] = params[:meta_data]
|
||||
|
@ -804,14 +857,20 @@ class PostsController < ApplicationController
|
|||
result[:no_bump] = true
|
||||
end
|
||||
|
||||
if params[:shared_draft] == 'true'
|
||||
if params[:shared_draft] == "true"
|
||||
raise Discourse::InvalidParameters.new(:shared_draft) unless guardian.can_create_shared_draft?
|
||||
|
||||
result[:shared_draft] = true
|
||||
end
|
||||
|
||||
if params[:whisper] == "true"
|
||||
raise Discourse::InvalidAccess.new("invalid_whisper_access", nil, custom_message: "invalid_whisper_access") unless guardian.can_create_whisper?
|
||||
unless guardian.can_create_whisper?
|
||||
raise Discourse::InvalidAccess.new(
|
||||
"invalid_whisper_access",
|
||||
nil,
|
||||
custom_message: "invalid_whisper_access",
|
||||
)
|
||||
end
|
||||
|
||||
result[:post_type] = Post.types[:whisper]
|
||||
end
|
||||
|
@ -827,14 +886,19 @@ class PostsController < ApplicationController
|
|||
result[:referrer] = request.env["HTTP_REFERER"]
|
||||
|
||||
if recipients = result[:target_usernames]
|
||||
Discourse.deprecate("`target_usernames` is deprecated, use `target_recipients` instead.", output_in_test: true, drop_from: '2.9.0')
|
||||
Discourse.deprecate(
|
||||
"`target_usernames` is deprecated, use `target_recipients` instead.",
|
||||
output_in_test: true,
|
||||
drop_from: "2.9.0",
|
||||
)
|
||||
else
|
||||
recipients = result[:target_recipients]
|
||||
end
|
||||
|
||||
if recipients
|
||||
recipients = recipients.split(",").map(&:downcase)
|
||||
groups = Group.messageable(current_user).where('lower(name) in (?)', recipients).pluck('lower(name)')
|
||||
groups =
|
||||
Group.messageable(current_user).where("lower(name) in (?)", recipients).pluck("lower(name)")
|
||||
recipients -= groups
|
||||
emails = recipients.select { |user| user.match(/@/) }
|
||||
recipients -= emails
|
||||
|
@ -848,13 +912,14 @@ class PostsController < ApplicationController
|
|||
end
|
||||
|
||||
def signature_for(args)
|
||||
+"post##" << Digest::SHA1.hexdigest(args
|
||||
+"post##" << Digest::SHA1.hexdigest(
|
||||
args
|
||||
.to_h
|
||||
.to_a
|
||||
.concat([["user", current_user.id]])
|
||||
.sort { |x, y| x[0] <=> y[0] }.join do |x, y|
|
||||
"#{x}:#{y}"
|
||||
end)
|
||||
.sort { |x, y| x[0] <=> y[0] }
|
||||
.join { |x, y| "#{x}:#{y}" },
|
||||
)
|
||||
end
|
||||
|
||||
def display_post(post)
|
||||
|
@ -873,7 +938,9 @@ class PostsController < ApplicationController
|
|||
end
|
||||
|
||||
def find_post_from_params_by_date
|
||||
by_date_finder = TopicView.new(params[:topic_id], current_user)
|
||||
by_date_finder =
|
||||
TopicView
|
||||
.new(params[:topic_id], current_user)
|
||||
.filtered_posts
|
||||
.where("created_at >= ?", Time.zone.parse(params[:date]))
|
||||
.order("created_at ASC")
|
||||
|
@ -902,5 +969,4 @@ class PostsController < ApplicationController
|
|||
guardian.ensure_can_see!(post)
|
||||
post
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -9,13 +9,18 @@ class PresenceController < ApplicationController
|
|||
|
||||
def get
|
||||
names = params.require(:channels)
|
||||
raise Discourse::InvalidParameters.new(:channels) if !(names.is_a?(Array) && names.all? { |n| n.is_a? String })
|
||||
if !(names.is_a?(Array) && names.all? { |n| n.is_a? String })
|
||||
raise Discourse::InvalidParameters.new(:channels)
|
||||
end
|
||||
|
||||
names.uniq!
|
||||
|
||||
raise Discourse::InvalidParameters.new("Too many channels") if names.length > MAX_CHANNELS_PER_REQUEST
|
||||
if names.length > MAX_CHANNELS_PER_REQUEST
|
||||
raise Discourse::InvalidParameters.new("Too many channels")
|
||||
end
|
||||
|
||||
user_group_ids = if current_user
|
||||
user_group_ids =
|
||||
if current_user
|
||||
GroupUser.where(user_id: current_user.id).pluck("group_id")
|
||||
else
|
||||
[]
|
||||
|
@ -38,19 +43,23 @@ class PresenceController < ApplicationController
|
|||
|
||||
def update
|
||||
client_id = params[:client_id]
|
||||
raise Discourse::InvalidParameters.new(:client_id) if !client_id.is_a?(String) || client_id.blank?
|
||||
if !client_id.is_a?(String) || client_id.blank?
|
||||
raise Discourse::InvalidParameters.new(:client_id)
|
||||
end
|
||||
|
||||
# JS client is designed to throttle to one request per second
|
||||
# When no changes are being made, it makes one request every 30 seconds
|
||||
RateLimiter.new(nil, "update-presence-#{current_user.id}", 20, 10.seconds).performed!
|
||||
|
||||
present_channels = params[:present_channels]
|
||||
if present_channels && !(present_channels.is_a?(Array) && present_channels.all? { |c| c.is_a? String })
|
||||
if present_channels &&
|
||||
!(present_channels.is_a?(Array) && present_channels.all? { |c| c.is_a? String })
|
||||
raise Discourse::InvalidParameters.new(:present_channels)
|
||||
end
|
||||
|
||||
leave_channels = params[:leave_channels]
|
||||
if leave_channels && !(leave_channels.is_a?(Array) && leave_channels.all? { |c| c.is_a? String })
|
||||
if leave_channels &&
|
||||
!(leave_channels.is_a?(Array) && leave_channels.all? { |c| c.is_a? String })
|
||||
raise Discourse::InvalidParameters.new(:leave_channels)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PublishedPagesController < ApplicationController
|
||||
|
||||
skip_before_action :preload_json
|
||||
skip_before_action :check_xhr, :verify_authenticity_token, only: [:show]
|
||||
before_action :ensure_publish_enabled
|
||||
|
@ -19,12 +18,14 @@ class PublishedPagesController < ApplicationController
|
|||
begin
|
||||
guardian.ensure_can_see!(pp.topic)
|
||||
rescue Discourse::InvalidAccess => e
|
||||
return rescue_discourse_actions(
|
||||
return(
|
||||
rescue_discourse_actions(
|
||||
:invalid_access,
|
||||
403,
|
||||
include_ember: false,
|
||||
custom_message: e.custom_message,
|
||||
group: e.group
|
||||
group: e.group,
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -37,18 +38,19 @@ class PublishedPagesController < ApplicationController
|
|||
|
||||
TopicViewItem.add(pp.topic.id, request.remote_ip, current_user ? current_user.id : nil)
|
||||
|
||||
@body_classes = Set.new([
|
||||
'published-page',
|
||||
@body_classes =
|
||||
Set.new(
|
||||
[
|
||||
"published-page",
|
||||
params[:slug],
|
||||
"topic-#{@topic.id}",
|
||||
@topic.tags.pluck(:name)
|
||||
].flatten.compact)
|
||||
@topic.tags.pluck(:name),
|
||||
].flatten.compact,
|
||||
)
|
||||
|
||||
if @topic.category
|
||||
@body_classes << @topic.category.slug
|
||||
end
|
||||
@body_classes << @topic.category.slug if @topic.category
|
||||
|
||||
render layout: 'publish'
|
||||
render layout: "publish"
|
||||
end
|
||||
|
||||
def details
|
||||
|
@ -60,11 +62,12 @@ class PublishedPagesController < ApplicationController
|
|||
def upsert
|
||||
pp_params = params.require(:published_page)
|
||||
|
||||
result, pp = PublishedPage.publish!(
|
||||
result, pp =
|
||||
PublishedPage.publish!(
|
||||
current_user,
|
||||
fetch_topic,
|
||||
pp_params[:slug].strip,
|
||||
pp_params.permit(:public)
|
||||
pp_params.permit(:public),
|
||||
)
|
||||
|
||||
json_result(pp, serializer: PublishedPageSerializer) { result }
|
||||
|
@ -85,7 +88,7 @@ class PublishedPagesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def fetch_topic
|
||||
topic = Topic.find_by(id: params[:topic_id])
|
||||
|
@ -94,18 +97,13 @@ private
|
|||
end
|
||||
|
||||
def ensure_publish_enabled
|
||||
if !SiteSetting.enable_page_publishing? || SiteSetting.secure_uploads
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
raise Discourse::NotFound if !SiteSetting.enable_page_publishing? || SiteSetting.secure_uploads
|
||||
end
|
||||
|
||||
def enforce_login_required!
|
||||
if SiteSetting.login_required? &&
|
||||
!current_user &&
|
||||
!SiteSetting.show_published_pages_login_required? &&
|
||||
redirect_to_login
|
||||
if SiteSetting.login_required? && !current_user &&
|
||||
!SiteSetting.show_published_pages_login_required? && redirect_to_login
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -18,6 +18,6 @@ class PushNotificationController < ApplicationController
|
|||
private
|
||||
|
||||
def push_params
|
||||
params.require(:subscription).permit(:endpoint, keys: [:p256dh, :auth])
|
||||
params.require(:subscription).permit(:endpoint, keys: %i[p256dh auth])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class QunitController < ApplicationController
|
||||
skip_before_action *%i{
|
||||
check_xhr
|
||||
preload_json
|
||||
redirect_to_login_if_required
|
||||
}
|
||||
skip_before_action *%i[check_xhr preload_json redirect_to_login_if_required]
|
||||
layout false
|
||||
|
||||
def theme
|
||||
|
@ -25,13 +21,17 @@ class QunitController < ApplicationController
|
|||
end
|
||||
|
||||
if param_key && theme.blank?
|
||||
return render plain: "Can't find theme with #{param_key} #{get_param(param_key).inspect}", status: :not_found
|
||||
return(
|
||||
render plain: "Can't find theme with #{param_key} #{get_param(param_key).inspect}",
|
||||
status: :not_found
|
||||
)
|
||||
end
|
||||
|
||||
if !param_key
|
||||
@suggested_themes = Theme
|
||||
@suggested_themes =
|
||||
Theme
|
||||
.where(
|
||||
id: ThemeField.where(target_id: Theme.targets[:tests_js]).distinct.pluck(:theme_id)
|
||||
id: ThemeField.where(target_id: Theme.targets[:tests_js]).distinct.pluck(:theme_id),
|
||||
)
|
||||
.order(updated_at: :desc)
|
||||
.pluck(:id, :name)
|
||||
|
|
|
@ -10,12 +10,10 @@ class ReviewableClaimedTopicsController < ApplicationController
|
|||
begin
|
||||
ReviewableClaimedTopic.create!(user_id: current_user.id, topic_id: topic.id)
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
return render_json_error(I18n.t('reviewables.conflict'), status: 409)
|
||||
return render_json_error(I18n.t("reviewables.conflict"), status: 409)
|
||||
end
|
||||
|
||||
topic.reviewables.find_each do |reviewable|
|
||||
reviewable.log_history(:claimed, current_user)
|
||||
end
|
||||
topic.reviewables.find_each { |reviewable| reviewable.log_history(:claimed, current_user) }
|
||||
|
||||
notify_users(topic, current_user)
|
||||
render json: success_json
|
||||
|
@ -27,9 +25,7 @@ class ReviewableClaimedTopicsController < ApplicationController
|
|||
|
||||
guardian.ensure_can_claim_reviewable_topic!(topic)
|
||||
ReviewableClaimedTopic.where(topic_id: topic.id).delete_all
|
||||
topic.reviewables.find_each do |reviewable|
|
||||
reviewable.log_history(:unclaimed, current_user)
|
||||
end
|
||||
topic.reviewables.find_each { |reviewable| reviewable.log_history(:unclaimed, current_user) }
|
||||
|
||||
notify_users(topic, nil)
|
||||
render json: success_json
|
||||
|
@ -40,7 +36,8 @@ class ReviewableClaimedTopicsController < ApplicationController
|
|||
def notify_users(topic, claimed_by)
|
||||
group_ids = Set.new([Group::AUTO_GROUPS[:staff]])
|
||||
|
||||
if SiteSetting.enable_category_group_moderation? && group_id = topic.category&.reviewable_by_group_id.presence
|
||||
if SiteSetting.enable_category_group_moderation? &&
|
||||
group_id = topic.category&.reviewable_by_group_id.presence
|
||||
group_ids.add(group_id)
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class ReviewablesController < ApplicationController
|
|||
|
||||
PER_PAGE = 10
|
||||
|
||||
before_action :version_required, only: [:update, :perform]
|
||||
before_action :version_required, only: %i[update perform]
|
||||
before_action :ensure_can_see, except: [:destroy]
|
||||
|
||||
def index
|
||||
|
@ -15,20 +15,21 @@ class ReviewablesController < ApplicationController
|
|||
raise Discourse::InvalidParameters.new(:type) unless Reviewable.valid_type?(params[:type])
|
||||
end
|
||||
|
||||
status = (params[:status] || 'pending').to_sym
|
||||
status = (params[:status] || "pending").to_sym
|
||||
raise Discourse::InvalidParameters.new(:status) unless allowed_statuses.include?(status)
|
||||
|
||||
topic_id = params[:topic_id] ? params[:topic_id].to_i : nil
|
||||
category_id = params[:category_id] ? params[:category_id].to_i : nil
|
||||
|
||||
custom_keys = Reviewable.custom_filters.map(&:first)
|
||||
additional_filters = JSON.parse(params.fetch(:additional_filters, {}), symbolize_names: true).slice(*custom_keys)
|
||||
additional_filters =
|
||||
JSON.parse(params.fetch(:additional_filters, {}), symbolize_names: true).slice(*custom_keys)
|
||||
filters = {
|
||||
ids: params[:ids],
|
||||
status: status,
|
||||
category_id: category_id,
|
||||
topic_id: topic_id,
|
||||
additional_filters: additional_filters.reject { |_, v| v.blank? }
|
||||
additional_filters: additional_filters.reject { |_, v| v.blank? },
|
||||
}
|
||||
|
||||
%i[priority username reviewed_by from_date to_date type sort_order].each do |filter_key|
|
||||
|
@ -36,7 +37,8 @@ class ReviewablesController < ApplicationController
|
|||
end
|
||||
|
||||
total_rows = Reviewable.list_for(current_user, **filters).count
|
||||
reviewables = Reviewable.list_for(current_user, **filters.merge(limit: PER_PAGE, offset: offset)).to_a
|
||||
reviewables =
|
||||
Reviewable.list_for(current_user, **filters.merge(limit: PER_PAGE, offset: offset)).to_a
|
||||
|
||||
claimed_topics = ReviewableClaimedTopic.claimed_hash(reviewables.map { |r| r.topic_id }.uniq)
|
||||
|
||||
|
@ -44,23 +46,25 @@ class ReviewablesController < ApplicationController
|
|||
# is mutated by the serializer and contains the side loaded records which must be merged in the end.
|
||||
hash = {}
|
||||
json = {
|
||||
reviewables: reviewables.map! do |r|
|
||||
result = r.serializer.new(
|
||||
r,
|
||||
root: nil,
|
||||
hash: hash,
|
||||
scope: guardian,
|
||||
claimed_topics: claimed_topics
|
||||
).as_json
|
||||
reviewables:
|
||||
reviewables.map! do |r|
|
||||
result =
|
||||
r
|
||||
.serializer
|
||||
.new(r, root: nil, hash: hash, scope: guardian, claimed_topics: claimed_topics)
|
||||
.as_json
|
||||
hash[:bundled_actions].uniq!
|
||||
(hash['actions'] || []).uniq!
|
||||
(hash["actions"] || []).uniq!
|
||||
result
|
||||
end,
|
||||
meta: filters.merge(
|
||||
total_rows_reviewables: total_rows, types: meta_types, reviewable_types: Reviewable.types,
|
||||
meta:
|
||||
filters.merge(
|
||||
total_rows_reviewables: total_rows,
|
||||
types: meta_types,
|
||||
reviewable_types: Reviewable.types,
|
||||
reviewable_count: current_user.reviewable_count,
|
||||
unseen_reviewable_count: Reviewable.unseen_reviewable_count(current_user)
|
||||
)
|
||||
unseen_reviewable_count: Reviewable.unseen_reviewable_count(current_user),
|
||||
),
|
||||
}
|
||||
if (offset + PER_PAGE) < total_rows
|
||||
json[:meta][:load_more_reviewables] = review_path(filters.merge(offset: offset + PER_PAGE))
|
||||
|
@ -72,10 +76,11 @@ class ReviewablesController < ApplicationController
|
|||
|
||||
def user_menu_list
|
||||
json = {
|
||||
reviewables: Reviewable.basic_serializers_for_list(
|
||||
reviewables:
|
||||
Reviewable.basic_serializers_for_list(
|
||||
Reviewable.user_menu_list_for(current_user),
|
||||
current_user
|
||||
).as_json
|
||||
current_user,
|
||||
).as_json,
|
||||
}
|
||||
render_json_dump(json, rest_serializer: true)
|
||||
end
|
||||
|
@ -108,17 +113,17 @@ class ReviewablesController < ApplicationController
|
|||
meta[:unique_users] = users.size
|
||||
end
|
||||
|
||||
topics = Topic.where(id: topic_ids).order('reviewable_score DESC')
|
||||
topics = Topic.where(id: topic_ids).order("reviewable_score DESC")
|
||||
render_serialized(
|
||||
topics,
|
||||
ReviewableTopicSerializer,
|
||||
root: 'reviewable_topics',
|
||||
root: "reviewable_topics",
|
||||
stats: stats,
|
||||
claimed_topics: ReviewableClaimedTopic.claimed_hash(topic_ids),
|
||||
rest_serializer: true,
|
||||
meta: {
|
||||
types: meta_types
|
||||
}
|
||||
types: meta_types,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -129,7 +134,7 @@ class ReviewablesController < ApplicationController
|
|||
{ reviewable: reviewable, scores: reviewable.explain_score },
|
||||
ReviewableExplanationSerializer,
|
||||
rest_serializer: true,
|
||||
root: 'reviewable_explanation'
|
||||
root: "reviewable_explanation",
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -141,10 +146,10 @@ class ReviewablesController < ApplicationController
|
|||
reviewable.serializer,
|
||||
rest_serializer: true,
|
||||
claimed_topics: ReviewableClaimedTopic.claimed_hash([reviewable.topic_id]),
|
||||
root: 'reviewable',
|
||||
root: "reviewable",
|
||||
meta: {
|
||||
types: meta_types
|
||||
}
|
||||
types: meta_types,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -186,7 +191,7 @@ class ReviewablesController < ApplicationController
|
|||
render_json_error(reviewable.errors)
|
||||
end
|
||||
rescue Reviewable::UpdateConflict
|
||||
render_json_error(I18n.t('reviewables.conflict'), status: 409)
|
||||
render_json_error(I18n.t("reviewables.conflict"), status: 409)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -201,23 +206,32 @@ class ReviewablesController < ApplicationController
|
|||
return render_json_error(error)
|
||||
end
|
||||
|
||||
args.merge!(reject_reason: params[:reject_reason], send_email: params[:send_email] != "false") if reviewable.type == 'ReviewableUser'
|
||||
if reviewable.type == "ReviewableUser"
|
||||
args.merge!(
|
||||
reject_reason: params[:reject_reason],
|
||||
send_email: params[:send_email] != "false",
|
||||
)
|
||||
end
|
||||
|
||||
plugin_params = DiscoursePluginRegistry.reviewable_params.select do |reviewable_param|
|
||||
plugin_params =
|
||||
DiscoursePluginRegistry.reviewable_params.select do |reviewable_param|
|
||||
reviewable.type == reviewable_param[:type].to_s.classify
|
||||
end
|
||||
args.merge!(params.slice(*plugin_params.map { |pp| pp[:param] }).permit!)
|
||||
|
||||
result = reviewable.perform(current_user, params[:action_id].to_sym, args)
|
||||
rescue Reviewable::InvalidAction => e
|
||||
if reviewable.type == 'ReviewableUser' && !reviewable.pending? && reviewable.target.blank?
|
||||
raise Discourse::NotFound.new(e.message, custom_message: "reviewables.already_handled_and_user_not_exist")
|
||||
if reviewable.type == "ReviewableUser" && !reviewable.pending? && reviewable.target.blank?
|
||||
raise Discourse::NotFound.new(
|
||||
e.message,
|
||||
custom_message: "reviewables.already_handled_and_user_not_exist",
|
||||
)
|
||||
else
|
||||
# Consider InvalidAction an InvalidAccess
|
||||
raise Discourse::InvalidAccess.new(e.message)
|
||||
end
|
||||
rescue Reviewable::UpdateConflict
|
||||
return render_json_error(I18n.t('reviewables.conflict'), status: 409)
|
||||
return render_json_error(I18n.t("reviewables.conflict"), status: 409)
|
||||
end
|
||||
|
||||
if result.success?
|
||||
|
@ -230,7 +244,7 @@ class ReviewablesController < ApplicationController
|
|||
def settings
|
||||
raise Discourse::InvalidAccess.new unless current_user.admin?
|
||||
|
||||
post_action_types = PostActionType.where(id: PostActionType.flag_types.values).order('id')
|
||||
post_action_types = PostActionType.where(id: PostActionType.flag_types.values).order("id")
|
||||
|
||||
if request.put?
|
||||
params[:reviewable_priorities].each do |id, priority|
|
||||
|
@ -239,7 +253,7 @@ class ReviewablesController < ApplicationController
|
|||
# to calculate it a different way.
|
||||
PostActionType.where(id: id).update_all(
|
||||
reviewable_priority: priority.to_i,
|
||||
score_bonus: priority.to_f
|
||||
score_bonus: priority.to_f,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -249,7 +263,7 @@ class ReviewablesController < ApplicationController
|
|||
render_serialized(data, ReviewableSettingsSerializer, rest_serializer: true)
|
||||
end
|
||||
|
||||
protected
|
||||
protected
|
||||
|
||||
def claim_error?(reviewable)
|
||||
return if SiteSetting.reviewable_claiming == "disabled" || reviewable.topic_id.blank?
|
||||
|
@ -257,9 +271,9 @@ protected
|
|||
claimed_by_id = ReviewableClaimedTopic.where(topic_id: reviewable.topic_id).pluck(:user_id)[0]
|
||||
|
||||
if SiteSetting.reviewable_claiming == "required" && claimed_by_id.blank?
|
||||
I18n.t('reviewables.must_claim')
|
||||
I18n.t("reviewables.must_claim")
|
||||
elsif claimed_by_id.present? && claimed_by_id != current_user.id
|
||||
I18n.t('reviewables.user_claimed')
|
||||
I18n.t("reviewables.user_claimed")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -274,18 +288,11 @@ protected
|
|||
end
|
||||
|
||||
def version_required
|
||||
if params[:version].blank?
|
||||
render_json_error(I18n.t('reviewables.missing_version'), status: 422)
|
||||
end
|
||||
render_json_error(I18n.t("reviewables.missing_version"), status: 422) if params[:version].blank?
|
||||
end
|
||||
|
||||
def meta_types
|
||||
{
|
||||
created_by: 'user',
|
||||
target_created_by: 'user',
|
||||
reviewed_by: 'user',
|
||||
claimed_by: 'user'
|
||||
}
|
||||
{ created_by: "user", target_created_by: "user", reviewed_by: "user", claimed_by: "user" }
|
||||
end
|
||||
|
||||
def ensure_can_see
|
||||
|
|
|
@ -7,7 +7,7 @@ class RobotsTxtController < ApplicationController
|
|||
OVERRIDDEN_HEADER = "# This robots.txt file has been customized at /admin/customize/robots\n"
|
||||
|
||||
# NOTE: order is important!
|
||||
DISALLOWED_PATHS ||= %w{
|
||||
DISALLOWED_PATHS ||= %w[
|
||||
/admin/
|
||||
/auth/
|
||||
/assets/browser-update*.js
|
||||
|
@ -16,18 +16,9 @@ class RobotsTxtController < ApplicationController
|
|||
/user-api-key
|
||||
/*?api_key*
|
||||
/*?*api_key*
|
||||
}
|
||||
]
|
||||
|
||||
DISALLOWED_WITH_HEADER_PATHS ||= %w{
|
||||
/badges
|
||||
/u/
|
||||
/my
|
||||
/search
|
||||
/tag/*/l
|
||||
/g
|
||||
/t/*/*.rss
|
||||
/c/*.rss
|
||||
}
|
||||
DISALLOWED_WITH_HEADER_PATHS ||= %w[/badges /u/ /my /search /tag/*/l /g /t/*/*.rss /c/*.rss]
|
||||
|
||||
def index
|
||||
if (overridden = SiteSetting.overridden_robots_txt.dup).present?
|
||||
|
@ -37,9 +28,9 @@ class RobotsTxtController < ApplicationController
|
|||
end
|
||||
if SiteSetting.allow_index_in_robots_txt?
|
||||
@robots_info = self.class.fetch_default_robots_info
|
||||
render :index, content_type: 'text/plain'
|
||||
render :index, content_type: "text/plain"
|
||||
else
|
||||
render :no_index, content_type: 'text/plain'
|
||||
render :no_index, content_type: "text/plain"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -56,32 +47,37 @@ class RobotsTxtController < ApplicationController
|
|||
|
||||
def self.fetch_default_robots_info
|
||||
deny_paths_googlebot = DISALLOWED_PATHS.map { |p| Discourse.base_path + p }
|
||||
deny_paths = deny_paths_googlebot + DISALLOWED_WITH_HEADER_PATHS.map { |p| Discourse.base_path + p }
|
||||
deny_all = [ "#{Discourse.base_path}/" ]
|
||||
deny_paths =
|
||||
deny_paths_googlebot + DISALLOWED_WITH_HEADER_PATHS.map { |p| Discourse.base_path + p }
|
||||
deny_all = ["#{Discourse.base_path}/"]
|
||||
|
||||
result = {
|
||||
header: "# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file",
|
||||
agents: []
|
||||
header:
|
||||
"# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file",
|
||||
agents: [],
|
||||
}
|
||||
|
||||
if SiteSetting.allowed_crawler_user_agents.present?
|
||||
SiteSetting.allowed_crawler_user_agents.split('|').each do |agent|
|
||||
SiteSetting
|
||||
.allowed_crawler_user_agents
|
||||
.split("|")
|
||||
.each do |agent|
|
||||
paths = agent == "Googlebot" ? deny_paths_googlebot : deny_paths
|
||||
result[:agents] << { name: agent, disallow: paths }
|
||||
end
|
||||
|
||||
result[:agents] << { name: '*', disallow: deny_all }
|
||||
result[:agents] << { name: "*", disallow: deny_all }
|
||||
else
|
||||
|
||||
if SiteSetting.blocked_crawler_user_agents.present?
|
||||
SiteSetting.blocked_crawler_user_agents.split('|').each do |agent|
|
||||
result[:agents] << { name: agent, disallow: deny_all }
|
||||
end
|
||||
SiteSetting
|
||||
.blocked_crawler_user_agents
|
||||
.split("|")
|
||||
.each { |agent| result[:agents] << { name: agent, disallow: deny_all } }
|
||||
end
|
||||
|
||||
result[:agents] << { name: '*', disallow: deny_paths }
|
||||
result[:agents] << { name: "*", disallow: deny_paths }
|
||||
|
||||
result[:agents] << { name: 'Googlebot', disallow: deny_paths_googlebot }
|
||||
result[:agents] << { name: "Googlebot", disallow: deny_paths_googlebot }
|
||||
end
|
||||
|
||||
DiscourseEvent.trigger(:robots_info, result)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SafeModeController < ApplicationController
|
||||
layout 'no_ember'
|
||||
layout "no_ember"
|
||||
before_action :ensure_safe_mode_enabled
|
||||
before_action :force_safe_mode_for_route
|
||||
|
||||
|
@ -39,5 +39,4 @@ class SafeModeController < ApplicationController
|
|||
request.env[ApplicationController::NO_THEMES] = true
|
||||
request.env[ApplicationController::NO_PLUGINS] = true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SearchController < ApplicationController
|
||||
|
||||
before_action :cancel_overloaded_search, only: [:query]
|
||||
skip_before_action :check_xhr, only: :show
|
||||
after_action :add_noindex_header
|
||||
|
||||
def self.valid_context_types
|
||||
%w{user topic category private_messages tag}
|
||||
%w[user topic category private_messages tag]
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -16,12 +15,9 @@ class SearchController < ApplicationController
|
|||
|
||||
# a q param has been given but it's not in the correct format
|
||||
# eg: ?q[foo]=bar
|
||||
if params[:q].present? && !@search_term.present?
|
||||
raise Discourse::InvalidParameters.new(:q)
|
||||
end
|
||||
raise Discourse::InvalidParameters.new(:q) if params[:q].present? && !@search_term.present?
|
||||
|
||||
if @search_term.present? &&
|
||||
@search_term.length < SiteSetting.min_search_term_length
|
||||
if @search_term.present? && @search_term.length < SiteSetting.min_search_term_length
|
||||
raise Discourse::InvalidParameters.new(:q)
|
||||
end
|
||||
|
||||
|
@ -31,21 +27,17 @@ class SearchController < ApplicationController
|
|||
|
||||
page = permitted_params[:page]
|
||||
# check for a malformed page parameter
|
||||
if page && (!page.is_a?(String) || page.to_i.to_s != page)
|
||||
raise Discourse::InvalidParameters
|
||||
end
|
||||
raise Discourse::InvalidParameters if page && (!page.is_a?(String) || page.to_i.to_s != page)
|
||||
|
||||
rate_limit_errors = rate_limit_search
|
||||
|
||||
discourse_expires_in 1.minute
|
||||
|
||||
search_args = {
|
||||
type_filter: 'topic',
|
||||
type_filter: "topic",
|
||||
guardian: guardian,
|
||||
blurb_length: 300,
|
||||
page: if page.to_i <= 10
|
||||
[page.to_i, 1].max
|
||||
end
|
||||
page: ([page.to_i, 1].max if page.to_i <= 10),
|
||||
}
|
||||
|
||||
context, type = lookup_search_context
|
||||
|
@ -59,18 +51,20 @@ class SearchController < ApplicationController
|
|||
search_args[:user_id] = current_user.id if current_user.present?
|
||||
|
||||
if rate_limit_errors
|
||||
result = Search::GroupedSearchResults.new(
|
||||
result =
|
||||
Search::GroupedSearchResults.new(
|
||||
type_filter: search_args[:type_filter],
|
||||
term: @search_term,
|
||||
search_context: context
|
||||
search_context: context,
|
||||
)
|
||||
|
||||
result.error = I18n.t("rate_limiter.slow_down")
|
||||
elsif site_overloaded?
|
||||
result = Search::GroupedSearchResults.new(
|
||||
result =
|
||||
Search::GroupedSearchResults.new(
|
||||
type_filter: search_args[:type_filter],
|
||||
term: @search_term,
|
||||
search_context: context
|
||||
search_context: context,
|
||||
)
|
||||
|
||||
result.error = I18n.t("search.extreme_load_error")
|
||||
|
@ -83,12 +77,8 @@ class SearchController < ApplicationController
|
|||
serializer = serialize_data(result, GroupedSearchResultSerializer, result: result)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
store_preloaded("search", MultiJson.dump(serializer))
|
||||
end
|
||||
format.json do
|
||||
render_json_dump(serializer)
|
||||
end
|
||||
format.html { store_preloaded("search", MultiJson.dump(serializer)) }
|
||||
format.json { render_json_dump(serializer) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -118,21 +108,25 @@ class SearchController < ApplicationController
|
|||
search_args[:search_type] = :header
|
||||
search_args[:ip_address] = request.remote_ip
|
||||
search_args[:user_id] = current_user.id if current_user.present?
|
||||
search_args[:restrict_to_archetype] = params[:restrict_to_archetype] if params[:restrict_to_archetype].present?
|
||||
search_args[:restrict_to_archetype] = params[:restrict_to_archetype] if params[
|
||||
:restrict_to_archetype
|
||||
].present?
|
||||
|
||||
if rate_limit_errors
|
||||
result = Search::GroupedSearchResults.new(
|
||||
result =
|
||||
Search::GroupedSearchResults.new(
|
||||
type_filter: search_args[:type_filter],
|
||||
term: params[:term],
|
||||
search_context: context
|
||||
search_context: context,
|
||||
)
|
||||
|
||||
result.error = I18n.t("rate_limiter.slow_down")
|
||||
elsif site_overloaded?
|
||||
result = GroupedSearchResults.new(
|
||||
result =
|
||||
GroupedSearchResults.new(
|
||||
type_filter: search_args[:type_filter],
|
||||
term: params[:term],
|
||||
search_context: context
|
||||
search_context: context,
|
||||
)
|
||||
else
|
||||
search = Search.new(params[:term], search_args)
|
||||
|
@ -163,7 +157,7 @@ class SearchController < ApplicationController
|
|||
|
||||
SearchLog.where(attributes).update_all(
|
||||
search_result_type: SearchLog.search_result_types[search_result_type],
|
||||
search_result_id: search_result_id
|
||||
search_result_id: search_result_id,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -173,7 +167,7 @@ class SearchController < ApplicationController
|
|||
protected
|
||||
|
||||
def site_overloaded?
|
||||
queue_time = request.env['REQUEST_QUEUE_SECONDS']
|
||||
queue_time = request.env["REQUEST_QUEUE_SECONDS"]
|
||||
if queue_time
|
||||
threshold = GlobalSetting.disable_search_queue_threshold.to_f
|
||||
threshold > 0 && queue_time > threshold
|
||||
|
@ -185,10 +179,25 @@ class SearchController < ApplicationController
|
|||
def rate_limit_search
|
||||
begin
|
||||
if current_user.present?
|
||||
RateLimiter.new(current_user, "search-min", SiteSetting.rate_limit_search_user, 1.minute).performed!
|
||||
RateLimiter.new(
|
||||
current_user,
|
||||
"search-min",
|
||||
SiteSetting.rate_limit_search_user,
|
||||
1.minute,
|
||||
).performed!
|
||||
else
|
||||
RateLimiter.new(nil, "search-min-#{request.remote_ip}", SiteSetting.rate_limit_search_anon_user, 1.minute).performed!
|
||||
RateLimiter.new(nil, "search-min-anon-global", SiteSetting.rate_limit_search_anon_global, 1.minute).performed!
|
||||
RateLimiter.new(
|
||||
nil,
|
||||
"search-min-#{request.remote_ip}",
|
||||
SiteSetting.rate_limit_search_anon_user,
|
||||
1.minute,
|
||||
).performed!
|
||||
RateLimiter.new(
|
||||
nil,
|
||||
"search-min-anon-global",
|
||||
SiteSetting.rate_limit_search_anon_global,
|
||||
1.minute,
|
||||
).performed!
|
||||
end
|
||||
rescue RateLimiter::LimitExceeded => e
|
||||
return e
|
||||
|
@ -197,13 +206,10 @@ class SearchController < ApplicationController
|
|||
end
|
||||
|
||||
def cancel_overloaded_search
|
||||
if site_overloaded?
|
||||
render_json_error I18n.t("search.extreme_load_error"), status: 409
|
||||
end
|
||||
render_json_error I18n.t("search.extreme_load_error"), status: 409 if site_overloaded?
|
||||
end
|
||||
|
||||
def lookup_search_context
|
||||
|
||||
return if params[:skip_context] == "true"
|
||||
|
||||
search_context = params[:search_context]
|
||||
|
@ -214,30 +220,29 @@ class SearchController < ApplicationController
|
|||
end
|
||||
|
||||
if search_context.present?
|
||||
raise Discourse::InvalidParameters.new(:search_context) unless SearchController.valid_context_types.include?(search_context[:type])
|
||||
unless SearchController.valid_context_types.include?(search_context[:type])
|
||||
raise Discourse::InvalidParameters.new(:search_context)
|
||||
end
|
||||
raise Discourse::InvalidParameters.new(:search_context) if search_context[:id].blank?
|
||||
|
||||
# A user is found by username
|
||||
context_obj = nil
|
||||
if ['user', 'private_messages'].include? search_context[:type]
|
||||
if %w[user private_messages].include? search_context[:type]
|
||||
context_obj = User.find_by(username_lower: search_context[:id].downcase)
|
||||
elsif 'category' == search_context[:type]
|
||||
elsif "category" == search_context[:type]
|
||||
context_obj = Category.find_by(id: search_context[:id].to_i)
|
||||
elsif 'topic' == search_context[:type]
|
||||
elsif "topic" == search_context[:type]
|
||||
context_obj = Topic.find_by(id: search_context[:id].to_i)
|
||||
elsif 'tag' == search_context[:type]
|
||||
elsif "tag" == search_context[:type]
|
||||
context_obj = Tag.where_name(search_context[:name]).first
|
||||
end
|
||||
|
||||
type_filter = nil
|
||||
if search_context[:type] == 'private_messages'
|
||||
type_filter = 'private_messages'
|
||||
end
|
||||
type_filter = "private_messages" if search_context[:type] == "private_messages"
|
||||
|
||||
guardian.ensure_can_see!(context_obj)
|
||||
|
||||
[context_obj, type_filter]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SessionController < ApplicationController
|
||||
before_action :check_local_login_allowed, only: %i(create forgot_password)
|
||||
before_action :rate_limit_login, only: %i(create email_login)
|
||||
before_action :check_local_login_allowed, only: %i[create forgot_password]
|
||||
before_action :rate_limit_login, only: %i[create email_login]
|
||||
skip_before_action :redirect_to_login_if_required
|
||||
skip_before_action :preload_json, :check_xhr, only: %i(sso sso_login sso_provider destroy one_time_password)
|
||||
skip_before_action :preload_json,
|
||||
:check_xhr,
|
||||
only: %i[sso sso_login sso_provider destroy one_time_password]
|
||||
|
||||
skip_before_action :check_xhr, only: %i(second_factor_auth_show)
|
||||
skip_before_action :check_xhr, only: %i[second_factor_auth_show]
|
||||
|
||||
requires_login only: [:second_factor_auth_show, :second_factor_auth_perform]
|
||||
requires_login only: %i[second_factor_auth_show second_factor_auth_perform]
|
||||
|
||||
allow_in_staff_writes_only_mode :create
|
||||
allow_in_staff_writes_only_mode :email_login
|
||||
|
@ -23,10 +25,10 @@ class SessionController < ApplicationController
|
|||
raise Discourse::NotFound unless SiteSetting.enable_discourse_connect?
|
||||
|
||||
destination_url = cookies[:destination_url] || session[:destination_url]
|
||||
return_path = params[:return_path] || path('/')
|
||||
return_path = params[:return_path] || path("/")
|
||||
|
||||
if destination_url && return_path == path('/')
|
||||
uri = URI::parse(destination_url)
|
||||
if destination_url && return_path == path("/")
|
||||
uri = URI.parse(destination_url)
|
||||
return_path = "#{uri.path}#{uri.query ? "?#{uri.query}" : ""}"
|
||||
end
|
||||
|
||||
|
@ -41,10 +43,11 @@ class SessionController < ApplicationController
|
|||
def sso_provider(payload = nil, confirmed_2fa_during_login = false)
|
||||
raise Discourse::NotFound unless SiteSetting.enable_discourse_connect_provider
|
||||
|
||||
result = run_second_factor!(
|
||||
result =
|
||||
run_second_factor!(
|
||||
SecondFactor::Actions::DiscourseConnectProvider,
|
||||
payload: payload,
|
||||
confirmed_2fa_during_login: confirmed_2fa_during_login
|
||||
confirmed_2fa_during_login: confirmed_2fa_during_login,
|
||||
)
|
||||
|
||||
if result.second_factor_auth_skipped?
|
||||
|
@ -57,7 +60,7 @@ class SessionController < ApplicationController
|
|||
|
||||
if data[:no_current_user]
|
||||
cookies[:sso_payload] = payload || request.query_string
|
||||
redirect_to path('/login')
|
||||
redirect_to path("/login")
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -93,12 +96,11 @@ class SessionController < ApplicationController
|
|||
skip_before_action :check_xhr, only: [:become]
|
||||
|
||||
def become
|
||||
|
||||
raise Discourse::InvalidAccess if Rails.env.production?
|
||||
raise Discourse::ReadOnly if @readonly_mode
|
||||
|
||||
if ENV['DISCOURSE_DEV_ALLOW_ANON_TO_IMPERSONATE'] != "1"
|
||||
render(content_type: 'text/plain', inline: <<~TEXT)
|
||||
if ENV["DISCOURSE_DEV_ALLOW_ANON_TO_IMPERSONATE"] != "1"
|
||||
render(content_type: "text/plain", inline: <<~TEXT)
|
||||
To enable impersonating any user without typing passwords set the following ENV var
|
||||
|
||||
export DISCOURSE_DEV_ALLOW_ANON_TO_IMPERSONATE=1
|
||||
|
@ -132,7 +134,9 @@ class SessionController < ApplicationController
|
|||
begin
|
||||
sso = DiscourseConnect.parse(request.query_string, secure_session: secure_session)
|
||||
rescue DiscourseConnect::ParseError => e
|
||||
connect_verbose_warn { "Verbose SSO log: Signature parse error\n\n#{e.message}\n\n#{sso&.diagnostics}" }
|
||||
connect_verbose_warn do
|
||||
"Verbose SSO log: Signature parse error\n\n#{e.message}\n\n#{sso&.diagnostics}"
|
||||
end
|
||||
|
||||
# Do NOT pass the error text to the client, it would give them the correct signature
|
||||
return render_sso_error(text: I18n.t("discourse_connect.login_error"), status: 422)
|
||||
|
@ -144,7 +148,9 @@ class SessionController < ApplicationController
|
|||
end
|
||||
|
||||
if ScreenedIpAddress.should_block?(request.remote_ip)
|
||||
connect_verbose_warn { "Verbose SSO log: IP address is blocked #{request.remote_ip}\n\n#{sso.diagnostics}" }
|
||||
connect_verbose_warn do
|
||||
"Verbose SSO log: IP address is blocked #{request.remote_ip}\n\n#{sso.diagnostics}"
|
||||
end
|
||||
return render_sso_error(text: I18n.t("discourse_connect.unknown_error"), status: 500)
|
||||
end
|
||||
|
||||
|
@ -163,9 +169,7 @@ class SessionController < ApplicationController
|
|||
end
|
||||
|
||||
if SiteSetting.must_approve_users? && !user.approved?
|
||||
if invite.present? && user.invited_user.blank?
|
||||
redeem_invitation(invite, sso, user)
|
||||
end
|
||||
redeem_invitation(invite, sso, user) if invite.present? && user.invited_user.blank?
|
||||
|
||||
if SiteSetting.discourse_connect_not_approved_url.present?
|
||||
redirect_to SiteSetting.discourse_connect_not_approved_url, allow_other_host: true
|
||||
|
@ -199,7 +203,7 @@ class SessionController < ApplicationController
|
|||
end
|
||||
|
||||
# If it's not a relative URL check the host
|
||||
if return_path !~ /^\/[^\/]/
|
||||
if return_path !~ %r{^/[^/]}
|
||||
begin
|
||||
uri = URI(return_path)
|
||||
if (uri.hostname == Discourse.current_hostname)
|
||||
|
@ -207,7 +211,7 @@ class SessionController < ApplicationController
|
|||
elsif !SiteSetting.discourse_connect_allows_all_return_paths
|
||||
return_path = path("/")
|
||||
end
|
||||
rescue
|
||||
rescue StandardError
|
||||
return_path = path("/")
|
||||
end
|
||||
end
|
||||
|
@ -215,16 +219,13 @@ class SessionController < ApplicationController
|
|||
# this can be done more surgically with a regex
|
||||
# but it the edge case of never supporting redirects back to
|
||||
# any url with `/session/sso` in it anywhere is reasonable
|
||||
if return_path.include?(path("/session/sso"))
|
||||
return_path = path("/")
|
||||
end
|
||||
return_path = path("/") if return_path.include?(path("/session/sso"))
|
||||
|
||||
redirect_to return_path, allow_other_host: true
|
||||
else
|
||||
render_sso_error(text: I18n.t("discourse_connect.not_found"), status: 500)
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
|
||||
connect_verbose_warn { <<~TEXT }
|
||||
Verbose SSO log: Record was invalid: #{e.record.class.name} #{e.record.id}
|
||||
#{e.record.errors.to_h}
|
||||
|
@ -243,7 +244,8 @@ class SessionController < ApplicationController
|
|||
if e.record.email.blank?
|
||||
text = I18n.t("discourse_connect.no_email")
|
||||
else
|
||||
text = I18n.t("discourse_connect.email_error", email: ERB::Util.html_escape(e.record.email))
|
||||
text =
|
||||
I18n.t("discourse_connect.email_error", email: ERB::Util.html_escape(e.record.email))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -270,7 +272,9 @@ class SessionController < ApplicationController
|
|||
end
|
||||
|
||||
def login_sso_user(sso, user)
|
||||
connect_verbose_warn { "Verbose SSO log: User was logged on #{user.username}\n\n#{sso.diagnostics}" }
|
||||
connect_verbose_warn do
|
||||
"Verbose SSO log: User was logged on #{user.username}\n\n#{sso.diagnostics}"
|
||||
end
|
||||
log_on_user(user) if user.id != current_user&.id
|
||||
end
|
||||
|
||||
|
@ -287,7 +291,6 @@ class SessionController < ApplicationController
|
|||
rate_limit_second_factor!(user)
|
||||
|
||||
if user.present?
|
||||
|
||||
# If their password is correct
|
||||
unless user.confirm_password?(params[:password])
|
||||
invalid_credentials
|
||||
|
@ -313,9 +316,7 @@ class SessionController < ApplicationController
|
|||
end
|
||||
|
||||
second_factor_auth_result = authenticate_second_factor(user)
|
||||
if !second_factor_auth_result.ok
|
||||
return render(json: @second_factor_failure_payload)
|
||||
end
|
||||
return render(json: @second_factor_failure_payload) if !second_factor_auth_result.ok
|
||||
|
||||
if user.active && user.email_confirmed?
|
||||
login(user, second_factor_auth_result)
|
||||
|
@ -332,24 +333,22 @@ class SessionController < ApplicationController
|
|||
check_local_login_allowed(user: user, check_login_via_email: true)
|
||||
|
||||
if matched_token
|
||||
response = {
|
||||
can_login: true,
|
||||
token: token,
|
||||
token_email: matched_token.email
|
||||
}
|
||||
response = { can_login: true, token: token, token_email: matched_token.email }
|
||||
|
||||
matched_user = matched_token.user
|
||||
if matched_user&.totp_enabled?
|
||||
response.merge!(
|
||||
second_factor_required: true,
|
||||
backup_codes_enabled: matched_user&.backup_codes_enabled?
|
||||
backup_codes_enabled: matched_user&.backup_codes_enabled?,
|
||||
)
|
||||
end
|
||||
|
||||
if matched_user&.security_keys_enabled?
|
||||
Webauthn.stage_challenge(matched_user, secure_session)
|
||||
response.merge!(
|
||||
Webauthn.allowed_credentials(matched_user, secure_session).merge(security_key_required: true)
|
||||
Webauthn.allowed_credentials(matched_user, secure_session).merge(
|
||||
security_key_required: true,
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -357,7 +356,7 @@ class SessionController < ApplicationController
|
|||
else
|
||||
render json: {
|
||||
can_login: false,
|
||||
error: I18n.t('email_login.invalid_token', base_url: Discourse.base_url)
|
||||
error: I18n.t("email_login.invalid_token", base_url: Discourse.base_url),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -388,7 +387,7 @@ class SessionController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
render json: { error: I18n.t('email_login.invalid_token', base_url: Discourse.base_url) }
|
||||
render json: { error: I18n.t("email_login.invalid_token", base_url: Discourse.base_url) }
|
||||
end
|
||||
|
||||
def one_time_password
|
||||
|
@ -406,10 +405,10 @@ class SessionController < ApplicationController
|
|||
# Display the form
|
||||
end
|
||||
else
|
||||
@error = I18n.t('user_api_key.invalid_token')
|
||||
@error = I18n.t("user_api_key.invalid_token")
|
||||
end
|
||||
|
||||
render layout: 'no_ember', locals: { hide_auth_buttons: true }
|
||||
render layout: "no_ember", locals: { hide_auth_buttons: true }
|
||||
end
|
||||
|
||||
def second_factor_auth_show
|
||||
|
@ -431,7 +430,7 @@ class SessionController < ApplicationController
|
|||
json.merge!(
|
||||
totp_enabled: user.totp_enabled?,
|
||||
backup_enabled: user.backup_codes_enabled?,
|
||||
allowed_methods: challenge[:allowed_methods]
|
||||
allowed_methods: challenge[:allowed_methods],
|
||||
)
|
||||
if user.security_keys_enabled?
|
||||
Webauthn.stage_challenge(user, secure_session)
|
||||
|
@ -440,9 +439,7 @@ class SessionController < ApplicationController
|
|||
else
|
||||
json[:security_keys_enabled] = false
|
||||
end
|
||||
if challenge[:description]
|
||||
json[:description] = challenge[:description]
|
||||
end
|
||||
json[:description] = challenge[:description] if challenge[:description]
|
||||
else
|
||||
json[:error] = I18n.t(error_key)
|
||||
end
|
||||
|
@ -453,9 +450,7 @@ class SessionController < ApplicationController
|
|||
raise ApplicationController::RenderEmpty.new
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: json, status: status_code
|
||||
end
|
||||
format.json { render json: json, status: status_code }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -472,10 +467,11 @@ class SessionController < ApplicationController
|
|||
end
|
||||
|
||||
if error_key
|
||||
json = failed_json.merge(
|
||||
json =
|
||||
failed_json.merge(
|
||||
ok: false,
|
||||
error: I18n.t(error_key),
|
||||
reason: "challenge_not_found_or_expired"
|
||||
reason: "challenge_not_found_or_expired",
|
||||
)
|
||||
render json: failed_json.merge(json), status: status_code
|
||||
return
|
||||
|
@ -505,7 +501,8 @@ class SessionController < ApplicationController
|
|||
challenge[:generated_at] += 1.minute.to_i
|
||||
secure_session["current_second_factor_auth_challenge"] = challenge.to_json
|
||||
else
|
||||
error_json = second_factor_auth_result
|
||||
error_json =
|
||||
second_factor_auth_result
|
||||
.to_h
|
||||
.deep_symbolize_keys
|
||||
.slice(:ok, :error, :reason)
|
||||
|
@ -518,8 +515,9 @@ class SessionController < ApplicationController
|
|||
ok: true,
|
||||
callback_method: challenge[:callback_method],
|
||||
callback_path: challenge[:callback_path],
|
||||
redirect_url: challenge[:redirect_url]
|
||||
}, status: 200
|
||||
redirect_url: challenge[:redirect_url],
|
||||
},
|
||||
status: 200
|
||||
end
|
||||
|
||||
def forgot_password
|
||||
|
@ -532,8 +530,11 @@ class SessionController < ApplicationController
|
|||
RateLimiter.new(nil, "forgot-password-hr-#{request.remote_ip}", 6, 1.hour).performed!
|
||||
RateLimiter.new(nil, "forgot-password-min-#{request.remote_ip}", 3, 1.minute).performed!
|
||||
|
||||
user = if SiteSetting.hide_email_address_taken && !current_user&.staff?
|
||||
raise Discourse::InvalidParameters.new(:login) if !EmailAddressValidator.valid_value?(normalized_login_param)
|
||||
user =
|
||||
if SiteSetting.hide_email_address_taken && !current_user&.staff?
|
||||
if !EmailAddressValidator.valid_value?(normalized_login_param)
|
||||
raise Discourse::InvalidParameters.new(:login)
|
||||
end
|
||||
User.real.where(staged: false).find_by_email(Email.downcase(normalized_login_param))
|
||||
else
|
||||
User.real.where(staged: false).find_by_username_or_email(normalized_login_param)
|
||||
|
@ -541,10 +542,21 @@ class SessionController < ApplicationController
|
|||
|
||||
if user
|
||||
RateLimiter.new(nil, "forgot-password-login-day-#{user.username}", 6, 1.day).performed!
|
||||
email_token = user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:password_reset])
|
||||
Jobs.enqueue(:critical_user_email, type: "forgot_password", user_id: user.id, email_token: email_token.token)
|
||||
email_token =
|
||||
user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:password_reset])
|
||||
Jobs.enqueue(
|
||||
:critical_user_email,
|
||||
type: "forgot_password",
|
||||
user_id: user.id,
|
||||
email_token: email_token.token,
|
||||
)
|
||||
else
|
||||
RateLimiter.new(nil, "forgot-password-login-hour-#{normalized_login_param}", 5, 1.hour).performed!
|
||||
RateLimiter.new(
|
||||
nil,
|
||||
"forgot-password-login-hour-#{normalized_login_param}",
|
||||
5,
|
||||
1.hour,
|
||||
).performed!
|
||||
end
|
||||
|
||||
json = success_json
|
||||
|
@ -566,7 +578,8 @@ class SessionController < ApplicationController
|
|||
redirect_url = params[:return_url].presence || SiteSetting.logout_redirect.presence
|
||||
|
||||
sso = SiteSetting.enable_discourse_connect
|
||||
only_one_authenticator = !SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1
|
||||
only_one_authenticator =
|
||||
!SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1
|
||||
if SiteSetting.login_required && (sso || only_one_authenticator)
|
||||
# In this situation visiting most URLs will start the auth process again
|
||||
# Go to the `/login` page to avoid an immediate redirect
|
||||
|
@ -575,16 +588,19 @@ class SessionController < ApplicationController
|
|||
|
||||
redirect_url ||= path("/")
|
||||
|
||||
event_data = { redirect_url: redirect_url, user: current_user, client_ip: request&.ip, user_agent: request&.user_agent }
|
||||
event_data = {
|
||||
redirect_url: redirect_url,
|
||||
user: current_user,
|
||||
client_ip: request&.ip,
|
||||
user_agent: request&.user_agent,
|
||||
}
|
||||
DiscourseEvent.trigger(:before_session_destroy, event_data)
|
||||
redirect_url = event_data[:redirect_url]
|
||||
|
||||
reset_session
|
||||
log_off_user
|
||||
if request.xhr?
|
||||
render json: {
|
||||
redirect_url: redirect_url
|
||||
}
|
||||
render json: { redirect_url: redirect_url }
|
||||
else
|
||||
redirect_to redirect_url, allow_other_host: true
|
||||
end
|
||||
|
@ -597,7 +613,7 @@ class SessionController < ApplicationController
|
|||
render json: {
|
||||
value: honeypot_value,
|
||||
challenge: challenge_value,
|
||||
expires_in: SecureSession.expiry
|
||||
expires_in: SecureSession.expiry,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -605,7 +621,7 @@ class SessionController < ApplicationController
|
|||
if is_api?
|
||||
key = request.env[Auth::DefaultCurrentUserProvider::HEADER_API_KEY]
|
||||
api_key = ApiKey.active.with_key(key).first
|
||||
render_serialized(api_key.api_key_scopes, ApiKeyScopeSerializer, root: 'scopes')
|
||||
render_serialized(api_key.api_key_scopes, ApiKeyScopeSerializer, root: "scopes")
|
||||
else
|
||||
render body: nil, status: 404
|
||||
end
|
||||
|
@ -628,8 +644,7 @@ class SessionController < ApplicationController
|
|||
return if user&.admin?
|
||||
|
||||
if (check_login_via_email && !SiteSetting.enable_local_logins_via_email) ||
|
||||
SiteSetting.enable_discourse_connect ||
|
||||
!SiteSetting.enable_local_logins
|
||||
SiteSetting.enable_discourse_connect || !SiteSetting.enable_local_logins
|
||||
raise Discourse::InvalidAccess, "SSO takes over local login or the local login is disallowed."
|
||||
end
|
||||
end
|
||||
|
@ -637,9 +652,7 @@ class SessionController < ApplicationController
|
|||
private
|
||||
|
||||
def connect_verbose_warn(&blk)
|
||||
if SiteSetting.verbose_discourse_connect_logging
|
||||
Rails.logger.warn(blk.call)
|
||||
end
|
||||
Rails.logger.warn(blk.call) if SiteSetting.verbose_discourse_connect_logging
|
||||
end
|
||||
|
||||
def authenticate_second_factor(user)
|
||||
|
@ -660,9 +673,7 @@ class SessionController < ApplicationController
|
|||
def login_error_check(user)
|
||||
return failed_to_login(user) if user.suspended?
|
||||
|
||||
if ScreenedIpAddress.should_block?(request.remote_ip)
|
||||
return not_allowed_from_ip_address(user)
|
||||
end
|
||||
return not_allowed_from_ip_address(user) if ScreenedIpAddress.should_block?(request.remote_ip)
|
||||
|
||||
if ScreenedIpAddress.block_admin_login?(user, request.remote_ip)
|
||||
admin_not_allowed_from_ip_address(user)
|
||||
|
@ -685,9 +696,9 @@ class SessionController < ApplicationController
|
|||
session[ACTIVATE_USER_KEY] = user.id
|
||||
render json: {
|
||||
error: I18n.t("login.not_activated"),
|
||||
reason: 'not_activated',
|
||||
reason: "not_activated",
|
||||
sent_to_email: user.find_email || user.email,
|
||||
current_email: user.email
|
||||
current_email: user.email,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -700,10 +711,7 @@ class SessionController < ApplicationController
|
|||
end
|
||||
|
||||
def failed_to_login(user)
|
||||
{
|
||||
error: user.suspended_message,
|
||||
reason: 'suspended'
|
||||
}
|
||||
{ error: user.suspended_message, reason: "suspended" }
|
||||
end
|
||||
|
||||
def login(user, second_factor_auth_result)
|
||||
|
@ -712,9 +720,9 @@ class SessionController < ApplicationController
|
|||
log_on_user(user)
|
||||
|
||||
if payload = cookies.delete(:sso_payload)
|
||||
confirmed_2fa_during_login = (
|
||||
second_factor_auth_result&.ok &&
|
||||
second_factor_auth_result.used_2fa_method.present? &&
|
||||
confirmed_2fa_during_login =
|
||||
(
|
||||
second_factor_auth_result&.ok && second_factor_auth_result.used_2fa_method.present? &&
|
||||
second_factor_auth_result.used_2fa_method != UserSecondFactor.methods[:backup_codes]
|
||||
)
|
||||
sso_provider(payload, confirmed_2fa_during_login)
|
||||
|
@ -728,20 +736,20 @@ class SessionController < ApplicationController
|
|||
nil,
|
||||
"login-hr-#{request.remote_ip}",
|
||||
SiteSetting.max_logins_per_ip_per_hour,
|
||||
1.hour
|
||||
1.hour,
|
||||
).performed!
|
||||
|
||||
RateLimiter.new(
|
||||
nil,
|
||||
"login-min-#{request.remote_ip}",
|
||||
SiteSetting.max_logins_per_ip_per_minute,
|
||||
1.minute
|
||||
1.minute,
|
||||
).performed!
|
||||
end
|
||||
|
||||
def render_sso_error(status:, text:)
|
||||
@sso_error = text
|
||||
render status: status, layout: 'no_ember'
|
||||
render status: status, layout: "no_ember"
|
||||
end
|
||||
|
||||
# extension to allow plugins to customize the SSO URL
|
||||
|
@ -769,9 +777,15 @@ class SessionController < ApplicationController
|
|||
raise Invite::ValidationFailed.new(I18n.t("invite.not_matching_email"))
|
||||
end
|
||||
elsif invite.expired?
|
||||
raise Invite::ValidationFailed.new(I18n.t('invite.expired', base_url: Discourse.base_url))
|
||||
raise Invite::ValidationFailed.new(I18n.t("invite.expired", base_url: Discourse.base_url))
|
||||
elsif invite.redeemed?
|
||||
raise Invite::ValidationFailed.new(I18n.t('invite.not_found_template', site_name: SiteSetting.title, base_url: Discourse.base_url))
|
||||
raise Invite::ValidationFailed.new(
|
||||
I18n.t(
|
||||
"invite.not_found_template",
|
||||
site_name: SiteSetting.title,
|
||||
base_url: Discourse.base_url,
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
invite
|
||||
|
@ -785,7 +799,7 @@ class SessionController < ApplicationController
|
|||
ip_address: request.remote_ip,
|
||||
session: session,
|
||||
email: sso.email,
|
||||
redeeming_user: redeeming_user
|
||||
redeeming_user: redeeming_user,
|
||||
).redeem
|
||||
secure_session["invite-key"] = nil
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SimilarTopicsController < ApplicationController
|
||||
|
||||
class SimilarTopic
|
||||
def initialize(topic)
|
||||
@topic = topic
|
||||
|
@ -26,5 +25,4 @@ class SimilarTopicsController < ApplicationController
|
|||
topics.map! { |t| SimilarTopic.new(t) }
|
||||
render_serialized(topics, SimilarTopicSerializer, root: :similar_topics, rest_serializer: true)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class SiteController < ApplicationController
|
||||
layout false
|
||||
skip_before_action :preload_json, :check_xhr
|
||||
skip_before_action :redirect_to_login_if_required, only: ['basic_info', 'statistics']
|
||||
skip_before_action :redirect_to_login_if_required, only: %w[basic_info statistics]
|
||||
|
||||
def site
|
||||
render json: Site.json_for(guardian)
|
||||
|
@ -33,9 +33,9 @@ class SiteController < ApplicationController
|
|||
favicon_url: UrlHelper.absolute(SiteSetting.site_favicon_url),
|
||||
title: SiteSetting.title,
|
||||
description: SiteSetting.site_description,
|
||||
header_primary_color: ColorScheme.hex_for_name('header_primary') || '333333',
|
||||
header_background_color: ColorScheme.hex_for_name('header_background') || 'ffffff',
|
||||
login_required: SiteSetting.login_required
|
||||
header_primary_color: ColorScheme.hex_for_name("header_primary") || "333333",
|
||||
header_background_color: ColorScheme.hex_for_name("header_background") || "ffffff",
|
||||
login_required: SiteSetting.login_required,
|
||||
}
|
||||
|
||||
if mobile_logo_url = SiteSetting.site_mobile_logo_url.presence
|
||||
|
@ -49,7 +49,7 @@ class SiteController < ApplicationController
|
|||
end
|
||||
|
||||
def statistics
|
||||
return redirect_to path('/') unless SiteSetting.share_anonymized_statistics?
|
||||
return redirect_to path("/") unless SiteSetting.share_anonymized_statistics?
|
||||
render json: About.fetch_cached_stats
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,9 +6,7 @@ class SitemapController < ApplicationController
|
|||
before_action :check_sitemap_enabled
|
||||
|
||||
def index
|
||||
@sitemaps = Sitemap
|
||||
.where(enabled: true)
|
||||
.where.not(name: Sitemap::NEWS_SITEMAP_NAME)
|
||||
@sitemaps = Sitemap.where(enabled: true).where.not(name: Sitemap::NEWS_SITEMAP_NAME)
|
||||
|
||||
render :index
|
||||
end
|
||||
|
@ -18,37 +16,46 @@ class SitemapController < ApplicationController
|
|||
sitemap = Sitemap.find_by(enabled: true, name: index.to_s)
|
||||
raise Discourse::NotFound if sitemap.nil?
|
||||
|
||||
@output = Rails.cache.fetch("sitemap/#{sitemap.name}/#{sitemap.max_page_size}", expires_in: 24.hours) do
|
||||
@output =
|
||||
Rails
|
||||
.cache
|
||||
.fetch("sitemap/#{sitemap.name}/#{sitemap.max_page_size}", expires_in: 24.hours) do
|
||||
@topics = sitemap.topics
|
||||
render :page, content_type: 'text/xml; charset=UTF-8'
|
||||
render :page, content_type: "text/xml; charset=UTF-8"
|
||||
end
|
||||
|
||||
render plain: @output, content_type: 'text/xml; charset=UTF-8' unless performed?
|
||||
render plain: @output, content_type: "text/xml; charset=UTF-8" unless performed?
|
||||
end
|
||||
|
||||
def recent
|
||||
sitemap = Sitemap.touch(Sitemap::RECENT_SITEMAP_NAME)
|
||||
|
||||
@output = Rails.cache.fetch("sitemap/recent/#{sitemap.last_posted_at.to_i}", expires_in: 1.hour) do
|
||||
@output =
|
||||
Rails
|
||||
.cache
|
||||
.fetch("sitemap/recent/#{sitemap.last_posted_at.to_i}", expires_in: 1.hour) do
|
||||
@topics = sitemap.topics
|
||||
render :page, content_type: 'text/xml; charset=UTF-8'
|
||||
render :page, content_type: "text/xml; charset=UTF-8"
|
||||
end
|
||||
|
||||
render plain: @output, content_type: 'text/xml; charset=UTF-8' unless performed?
|
||||
render plain: @output, content_type: "text/xml; charset=UTF-8" unless performed?
|
||||
end
|
||||
|
||||
def news
|
||||
sitemap = Sitemap.touch(Sitemap::NEWS_SITEMAP_NAME)
|
||||
|
||||
@output = Rails.cache.fetch("sitemap/news", expires_in: 5.minutes) do
|
||||
@output =
|
||||
Rails
|
||||
.cache
|
||||
.fetch("sitemap/news", expires_in: 5.minutes) do
|
||||
dlocale = SiteSetting.default_locale.downcase
|
||||
@locale = dlocale.gsub(/_.*/, '')
|
||||
@locale = dlocale.sub('_', '-') if @locale === "zh"
|
||||
@locale = dlocale.gsub(/_.*/, "")
|
||||
@locale = dlocale.sub("_", "-") if @locale === "zh"
|
||||
@topics = sitemap.topics
|
||||
render :news, content_type: 'text/xml; charset=UTF-8'
|
||||
render :news, content_type: "text/xml; charset=UTF-8"
|
||||
end
|
||||
|
||||
render plain: @output, content_type: 'text/xml; charset=UTF-8' unless performed?
|
||||
render plain: @output, content_type: "text/xml; charset=UTF-8" unless performed?
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -58,7 +65,7 @@ class SitemapController < ApplicationController
|
|||
end
|
||||
|
||||
def build_sitemap_topic_url(slug, id, posts_count = nil)
|
||||
base_url = [Discourse.base_url, 't', slug, id].join('/')
|
||||
base_url = [Discourse.base_url, "t", slug, id].join("/")
|
||||
return base_url if posts_count.nil?
|
||||
|
||||
page, mod = posts_count.divmod(TopicView.chunk_size)
|
||||
|
@ -67,5 +74,4 @@ class SitemapController < ApplicationController
|
|||
page > 1 ? "#{base_url}?page=#{page}" : base_url
|
||||
end
|
||||
helper_method :build_sitemap_topic_url
|
||||
|
||||
end
|
||||
|
|
|
@ -2,26 +2,41 @@
|
|||
|
||||
class StaticController < ApplicationController
|
||||
skip_before_action :check_xhr, :redirect_to_login_if_required
|
||||
skip_before_action :verify_authenticity_token, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
|
||||
skip_before_action :preload_json, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
|
||||
skip_before_action :handle_theme, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
|
||||
skip_before_action :verify_authenticity_token,
|
||||
only: %i[brotli_asset cdn_asset enter favicon service_worker_asset]
|
||||
skip_before_action :preload_json,
|
||||
only: %i[brotli_asset cdn_asset enter favicon service_worker_asset]
|
||||
skip_before_action :handle_theme,
|
||||
only: %i[brotli_asset cdn_asset enter favicon service_worker_asset]
|
||||
|
||||
before_action :apply_cdn_headers, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
|
||||
before_action :apply_cdn_headers,
|
||||
only: %i[brotli_asset cdn_asset enter favicon service_worker_asset]
|
||||
|
||||
PAGES_WITH_EMAIL_PARAM = ['login', 'password_reset', 'signup']
|
||||
MODAL_PAGES = ['password_reset', 'signup']
|
||||
PAGES_WITH_EMAIL_PARAM = %w[login password_reset signup]
|
||||
MODAL_PAGES = %w[password_reset signup]
|
||||
DEFAULT_PAGES = {
|
||||
"faq" => { redirect: "faq_url", topic_id: "guidelines_topic_id" },
|
||||
"tos" => { redirect: "tos_url", topic_id: "tos_topic_id" },
|
||||
"privacy" => { redirect: "privacy_policy_url", topic_id: "privacy_topic_id" },
|
||||
"faq" => {
|
||||
redirect: "faq_url",
|
||||
topic_id: "guidelines_topic_id",
|
||||
},
|
||||
"tos" => {
|
||||
redirect: "tos_url",
|
||||
topic_id: "tos_topic_id",
|
||||
},
|
||||
"privacy" => {
|
||||
redirect: "privacy_policy_url",
|
||||
topic_id: "privacy_topic_id",
|
||||
},
|
||||
}
|
||||
CUSTOM_PAGES = {} # Add via `#add_topic_static_page` in plugin API
|
||||
|
||||
def show
|
||||
return redirect_to(path '/') if current_user && (params[:id] == 'login' || params[:id] == 'signup')
|
||||
if current_user && (params[:id] == "login" || params[:id] == "signup")
|
||||
return redirect_to(path "/")
|
||||
end
|
||||
|
||||
if SiteSetting.login_required? && current_user.nil? && ['faq', 'guidelines'].include?(params[:id])
|
||||
return redirect_to path('/login')
|
||||
if SiteSetting.login_required? && current_user.nil? && %w[faq guidelines].include?(params[:id])
|
||||
return redirect_to path("/login")
|
||||
end
|
||||
|
||||
map = DEFAULT_PAGES.deep_merge(CUSTOM_PAGES)
|
||||
|
@ -34,10 +49,10 @@ class StaticController < ApplicationController
|
|||
end
|
||||
|
||||
# The /guidelines route ALWAYS shows our FAQ, ignoring the faq_url site setting.
|
||||
@page = 'faq' if @page == 'guidelines'
|
||||
@page = "faq" if @page == "guidelines"
|
||||
|
||||
# Don't allow paths like ".." or "/" or anything hacky like that
|
||||
@page = @page.gsub(/[^a-z0-9\_\-]/, '')
|
||||
@page = @page.gsub(/[^a-z0-9\_\-]/, "")
|
||||
|
||||
if map.has_key?(@page)
|
||||
topic_id = map[@page][:topic_id]
|
||||
|
@ -46,7 +61,8 @@ class StaticController < ApplicationController
|
|||
@topic = Topic.find_by_id(SiteSetting.get(topic_id))
|
||||
raise Discourse::NotFound unless @topic
|
||||
|
||||
title_prefix = if I18n.exists?("js.#{@page}")
|
||||
title_prefix =
|
||||
if I18n.exists?("js.#{@page}")
|
||||
I18n.t("js.#{@page}")
|
||||
else
|
||||
@topic.title
|
||||
|
@ -104,10 +120,7 @@ class StaticController < ApplicationController
|
|||
forum_uri = URI(Discourse.base_url)
|
||||
uri = URI(redirect_location)
|
||||
|
||||
if uri.path.present? &&
|
||||
(uri.host.blank? || uri.host == forum_uri.host) &&
|
||||
uri.path !~ /\./
|
||||
|
||||
if uri.path.present? && (uri.host.blank? || uri.host == forum_uri.host) && uri.path !~ /\./
|
||||
destination = "#{uri.path}#{uri.query ? "?#{uri.query}" : ""}"
|
||||
end
|
||||
rescue URI::Error
|
||||
|
@ -135,22 +148,24 @@ class StaticController < ApplicationController
|
|||
is_asset_path
|
||||
|
||||
hijack do
|
||||
data = DistributedMemoizer.memoize("FAVICON#{SiteIconManager.favicon_url}", 60 * 30) do
|
||||
data =
|
||||
DistributedMemoizer.memoize("FAVICON#{SiteIconManager.favicon_url}", 60 * 30) do
|
||||
favicon = SiteIconManager.favicon
|
||||
next "" unless favicon
|
||||
|
||||
if Discourse.store.external?
|
||||
begin
|
||||
file = FileHelper.download(
|
||||
file =
|
||||
FileHelper.download(
|
||||
Discourse.store.cdn_url(favicon.url),
|
||||
max_file_size: favicon.filesize,
|
||||
tmp_file_name: FAVICON,
|
||||
follow_redirect: true
|
||||
follow_redirect: true,
|
||||
)
|
||||
|
||||
file&.read || ""
|
||||
rescue => e
|
||||
AdminDashboardData.add_problem_message('dashboard.bad_favicon_url', 1800)
|
||||
AdminDashboardData.add_problem_message("dashboard.bad_favicon_url", 1800)
|
||||
Rails.logger.debug("Failed to fetch favicon #{favicon.url}: #{e}\n#{e.backtrace}")
|
||||
""
|
||||
ensure
|
||||
|
@ -178,9 +193,7 @@ class StaticController < ApplicationController
|
|||
def brotli_asset
|
||||
is_asset_path
|
||||
|
||||
serve_asset(".br") do
|
||||
response.headers["Content-Encoding"] = 'br'
|
||||
end
|
||||
serve_asset(".br") { response.headers["Content-Encoding"] = "br" }
|
||||
end
|
||||
|
||||
def cdn_asset
|
||||
|
@ -199,20 +212,22 @@ class StaticController < ApplicationController
|
|||
# However, ensure that these may be cached and served for longer on servers.
|
||||
immutable_for 1.year
|
||||
|
||||
if Rails.application.assets_manifest.assets['service-worker.js']
|
||||
path = File.expand_path(Rails.root + "public/assets/#{Rails.application.assets_manifest.assets['service-worker.js']}")
|
||||
if Rails.application.assets_manifest.assets["service-worker.js"]
|
||||
path =
|
||||
File.expand_path(
|
||||
Rails.root +
|
||||
"public/assets/#{Rails.application.assets_manifest.assets["service-worker.js"]}",
|
||||
)
|
||||
response.headers["Last-Modified"] = File.ctime(path).httpdate
|
||||
end
|
||||
content = Rails.application.assets_manifest.find_sources('service-worker.js').first
|
||||
content = Rails.application.assets_manifest.find_sources("service-worker.js").first
|
||||
|
||||
base_url = File.dirname(helpers.script_asset_path('service-worker'))
|
||||
content = content.sub(
|
||||
/^\/\/# sourceMappingURL=(service-worker-.+\.map)$/
|
||||
) { "//# sourceMappingURL=#{base_url}/#{Regexp.last_match(1)}" }
|
||||
render(
|
||||
plain: content,
|
||||
content_type: 'application/javascript'
|
||||
)
|
||||
base_url = File.dirname(helpers.script_asset_path("service-worker"))
|
||||
content =
|
||||
content.sub(%r{^//# sourceMappingURL=(service-worker-.+\.map)$}) do
|
||||
"//# sourceMappingURL=#{base_url}/#{Regexp.last_match(1)}"
|
||||
end
|
||||
render(plain: content, content_type: "application/javascript")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -220,7 +235,6 @@ class StaticController < ApplicationController
|
|||
protected
|
||||
|
||||
def serve_asset(suffix = nil)
|
||||
|
||||
path = File.expand_path(Rails.root + "public/assets/#{params[:path]}#{suffix}")
|
||||
|
||||
# SECURITY what if path has /../
|
||||
|
@ -254,12 +268,10 @@ class StaticController < ApplicationController
|
|||
immutable_for 1.year
|
||||
|
||||
# disable NGINX mucking with transfer
|
||||
request.env['sendfile.type'] = ''
|
||||
request.env["sendfile.type"] = ""
|
||||
|
||||
opts = { disposition: nil }
|
||||
opts[:type] = "application/javascript" if params[:path] =~ /\.js$/
|
||||
send_file(path, opts)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class StepsController < ApplicationController
|
|||
updater.update
|
||||
|
||||
if updater.success?
|
||||
result = { success: 'OK' }
|
||||
result = { success: "OK" }
|
||||
result[:refresh_required] = true if updater.refresh_required?
|
||||
render json: result
|
||||
else
|
||||
|
@ -23,5 +23,4 @@ class StepsController < ApplicationController
|
|||
render json: { errors: errors }, status: 422
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StylesheetsController < ApplicationController
|
||||
skip_before_action :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :show_source_map, :color_scheme]
|
||||
skip_before_action :preload_json,
|
||||
:redirect_to_login_if_required,
|
||||
:check_xhr,
|
||||
:verify_authenticity_token,
|
||||
only: %i[show show_source_map color_scheme]
|
||||
|
||||
before_action :apply_cdn_headers, only: [:show, :show_source_map, :color_scheme]
|
||||
before_action :apply_cdn_headers, only: %i[show show_source_map color_scheme]
|
||||
|
||||
def show_source_map
|
||||
show_resource(source_map: true)
|
||||
|
@ -20,14 +24,13 @@ class StylesheetsController < ApplicationController
|
|||
params.permit("theme_id")
|
||||
|
||||
manager = Stylesheet::Manager.new(theme_id: params[:theme_id])
|
||||
stylesheet = manager.color_scheme_stylesheet_details(params[:id], 'all')
|
||||
stylesheet = manager.color_scheme_stylesheet_details(params[:id], "all")
|
||||
render json: stylesheet
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def show_resource(source_map: false)
|
||||
|
||||
extension = source_map ? ".css.map" : ".css"
|
||||
|
||||
no_cookies
|
||||
|
@ -47,7 +50,7 @@ class StylesheetsController < ApplicationController
|
|||
if digest
|
||||
query = query.where(digest: digest)
|
||||
else
|
||||
query = query.order('id desc')
|
||||
query = query.order("id desc")
|
||||
end
|
||||
|
||||
# Security note, safe due to route constraint
|
||||
|
@ -58,9 +61,7 @@ class StylesheetsController < ApplicationController
|
|||
|
||||
stylesheet_time = query.pluck_first(:created_at)
|
||||
|
||||
if !stylesheet_time
|
||||
handle_missing_cache(location, target, digest)
|
||||
end
|
||||
handle_missing_cache(location, target, digest) if !stylesheet_time
|
||||
|
||||
if cache_time && stylesheet_time && stylesheet_time <= cache_time
|
||||
return render body: nil, status: 304
|
||||
|
@ -76,10 +77,10 @@ class StylesheetsController < ApplicationController
|
|||
end
|
||||
|
||||
if Rails.env == "development"
|
||||
response.headers['Last-Modified'] = Time.zone.now.httpdate
|
||||
response.headers["Last-Modified"] = Time.zone.now.httpdate
|
||||
immutable_for(1.second)
|
||||
else
|
||||
response.headers['Last-Modified'] = stylesheet_time.httpdate if stylesheet_time
|
||||
response.headers["Last-Modified"] = stylesheet_time.httpdate if stylesheet_time
|
||||
immutable_for(1.year)
|
||||
end
|
||||
send_file(location, disposition: :inline)
|
||||
|
@ -104,5 +105,4 @@ class StylesheetsController < ApplicationController
|
|||
rescue Errno::ENOENT
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SvgSpriteController < ApplicationController
|
||||
skip_before_action :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :search, :svg_icon]
|
||||
skip_before_action :preload_json,
|
||||
:redirect_to_login_if_required,
|
||||
:check_xhr,
|
||||
:verify_authenticity_token,
|
||||
only: %i[show search svg_icon]
|
||||
|
||||
before_action :apply_cdn_headers, only: [:show, :search, :svg_icon]
|
||||
before_action :apply_cdn_headers, only: %i[show search svg_icon]
|
||||
|
||||
requires_login except: [:show, :svg_icon]
|
||||
requires_login except: %i[show svg_icon]
|
||||
|
||||
def show
|
||||
|
||||
no_cookies
|
||||
|
||||
RailsMultisite::ConnectionManagement.with_hostname(params[:hostname]) do
|
||||
|
@ -24,20 +27,19 @@ class SvgSpriteController < ApplicationController
|
|||
response.headers["Content-Length"] = svg_sprite.bytesize.to_s
|
||||
immutable_for 1.year
|
||||
|
||||
render plain: svg_sprite, disposition: nil, content_type: 'application/javascript'
|
||||
render plain: svg_sprite, disposition: nil, content_type: "application/javascript"
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
RailsMultisite::ConnectionManagement.with_hostname(params[:hostname]) do
|
||||
|
||||
keyword = params.require(:keyword)
|
||||
data = SvgSprite.search(keyword)
|
||||
|
||||
if data.blank?
|
||||
render body: nil, status: 404
|
||||
else
|
||||
render plain: data.inspect, disposition: nil, content_type: 'text/plain'
|
||||
render plain: data.inspect, disposition: nil, content_type: "text/plain"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -65,14 +67,14 @@ class SvgSpriteController < ApplicationController
|
|||
else
|
||||
doc = Nokogiri.XML(icon)
|
||||
doc.at_xpath("symbol").name = "svg"
|
||||
doc.at_xpath("svg")['xmlns'] = "http://www.w3.org/2000/svg"
|
||||
doc.at_xpath("svg")['fill'] = adjust_hex(params[:color]) if params[:color]
|
||||
doc.at_xpath("svg")["xmlns"] = "http://www.w3.org/2000/svg"
|
||||
doc.at_xpath("svg")["fill"] = adjust_hex(params[:color]) if params[:color]
|
||||
|
||||
response.headers["Last-Modified"] = 1.years.ago.httpdate
|
||||
response.headers["Content-Length"] = doc.to_s.bytesize.to_s
|
||||
immutable_for 1.day
|
||||
|
||||
render plain: doc, disposition: nil, content_type: 'image/svg+xml'
|
||||
render plain: doc, disposition: nil, content_type: "image/svg+xml"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,12 +4,17 @@ class TagGroupsController < ApplicationController
|
|||
requires_login except: [:search]
|
||||
before_action :ensure_staff, except: [:search]
|
||||
|
||||
skip_before_action :check_xhr, only: [:index, :show, :new]
|
||||
before_action :fetch_tag_group, only: [:show, :update, :destroy]
|
||||
skip_before_action :check_xhr, only: %i[index show new]
|
||||
before_action :fetch_tag_group, only: %i[show update destroy]
|
||||
|
||||
def index
|
||||
tag_groups = TagGroup.order('name ASC').includes(:parent_tag).preload(:tags).all
|
||||
serializer = ActiveModel::ArraySerializer.new(tag_groups, each_serializer: TagGroupSerializer, root: 'tag_groups')
|
||||
tag_groups = TagGroup.order("name ASC").includes(:parent_tag).preload(:tags).all
|
||||
serializer =
|
||||
ActiveModel::ArraySerializer.new(
|
||||
tag_groups,
|
||||
each_serializer: TagGroupSerializer,
|
||||
root: "tag_groups",
|
||||
)
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
store_preloaded "tagGroups", MultiJson.dump(serializer)
|
||||
|
@ -31,8 +36,13 @@ class TagGroupsController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
tag_groups = TagGroup.order('name ASC').includes(:parent_tag).preload(:tags).all
|
||||
serializer = ActiveModel::ArraySerializer.new(tag_groups, each_serializer: TagGroupSerializer, root: 'tag_groups')
|
||||
tag_groups = TagGroup.order("name ASC").includes(:parent_tag).preload(:tags).all
|
||||
serializer =
|
||||
ActiveModel::ArraySerializer.new(
|
||||
tag_groups,
|
||||
each_serializer: TagGroupSerializer,
|
||||
root: "tag_groups",
|
||||
)
|
||||
store_preloaded "tagGroup", MultiJson.dump(serializer)
|
||||
render "default/empty"
|
||||
end
|
||||
|
@ -63,18 +73,17 @@ class TagGroupsController < ApplicationController
|
|||
def search
|
||||
matches = TagGroup.includes(:tags).visible(guardian).all
|
||||
|
||||
if params[:q].present?
|
||||
matches = matches.where('lower(name) ILIKE ?', "%#{params[:q].strip}%")
|
||||
end
|
||||
matches = matches.where("lower(name) ILIKE ?", "%#{params[:q].strip}%") if params[:q].present?
|
||||
|
||||
if params[:names].present?
|
||||
matches = matches.where('lower(NAME) in (?)', params[:names].map(&:downcase))
|
||||
matches = matches.where("lower(NAME) in (?)", params[:names].map(&:downcase))
|
||||
end
|
||||
|
||||
matches = matches.order('name').limit(params[:limit] || 5)
|
||||
matches = matches.order("name").limit(params[:limit] || 5)
|
||||
|
||||
render json: {
|
||||
results: matches.map { |x| { name: x.name, tag_names: x.tags.base_tags.pluck(:name).sort } }
|
||||
results:
|
||||
matches.map { |x| { name: x.name, tag_names: x.tags.base_tags.pluck(:name).sort } },
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -88,14 +97,8 @@ class TagGroupsController < ApplicationController
|
|||
tag_group = params.delete(:tag_group)
|
||||
params.merge!(tag_group.permit!) if tag_group
|
||||
|
||||
result = params.permit(
|
||||
:id,
|
||||
:name,
|
||||
:one_per_topic,
|
||||
tag_names: [],
|
||||
parent_tag_name: [],
|
||||
permissions: {}
|
||||
)
|
||||
result =
|
||||
params.permit(:id, :name, :one_per_topic, tag_names: [], parent_tag_name: [], permissions: {})
|
||||
|
||||
result[:tag_names] ||= []
|
||||
result[:parent_tag_name] ||= []
|
||||
|
|
|
@ -5,29 +5,32 @@ class TagsController < ::ApplicationController
|
|||
include TopicQueryParams
|
||||
|
||||
before_action :ensure_tags_enabled
|
||||
before_action :ensure_visible, only: [:show, :info]
|
||||
before_action :ensure_visible, only: %i[show info]
|
||||
|
||||
def self.show_methods
|
||||
Discourse.anonymous_filters.map { |f| :"show_#{f}" }
|
||||
end
|
||||
|
||||
requires_login except: [
|
||||
:index,
|
||||
:show,
|
||||
:tag_feed,
|
||||
:search,
|
||||
:info,
|
||||
*show_methods
|
||||
]
|
||||
requires_login except: [:index, :show, :tag_feed, :search, :info, *show_methods]
|
||||
|
||||
skip_before_action :check_xhr, only: [:tag_feed, :show, :index, *show_methods]
|
||||
|
||||
before_action :set_category, except: [:index, :update, :destroy,
|
||||
:tag_feed, :search, :notifications, :update_notifications, :personal_messages, :info]
|
||||
before_action :set_category,
|
||||
except: %i[
|
||||
index
|
||||
update
|
||||
destroy
|
||||
tag_feed
|
||||
search
|
||||
notifications
|
||||
update_notifications
|
||||
personal_messages
|
||||
info
|
||||
]
|
||||
|
||||
before_action :fetch_tag, only: [:info, :create_synonyms, :destroy_synonym]
|
||||
before_action :fetch_tag, only: %i[info create_synonyms destroy_synonym]
|
||||
|
||||
after_action :add_noindex_header, except: [:index, :show]
|
||||
after_action :add_noindex_header, except: %i[index show]
|
||||
|
||||
def index
|
||||
@description_meta = I18n.t("tags.title")
|
||||
|
@ -39,8 +42,21 @@ class TagsController < ::ApplicationController
|
|||
ungrouped_tags = Tag.where("tags.id NOT IN (SELECT tag_id FROM tag_group_memberships)")
|
||||
ungrouped_tags = ungrouped_tags.where("tags.topic_count > 0") unless show_all_tags
|
||||
|
||||
grouped_tag_counts = TagGroup.visible(guardian).order('name ASC').includes(:tags).map do |tag_group|
|
||||
{ id: tag_group.id, name: tag_group.name, tags: self.class.tag_counts_json(tag_group.tags.where(target_tag_id: nil), show_pm_tags: guardian.can_tag_pms?) }
|
||||
grouped_tag_counts =
|
||||
TagGroup
|
||||
.visible(guardian)
|
||||
.order("name ASC")
|
||||
.includes(:tags)
|
||||
.map do |tag_group|
|
||||
{
|
||||
id: tag_group.id,
|
||||
name: tag_group.name,
|
||||
tags:
|
||||
self.class.tag_counts_json(
|
||||
tag_group.tags.where(target_tag_id: nil),
|
||||
show_pm_tags: guardian.can_tag_pms?,
|
||||
),
|
||||
}
|
||||
end
|
||||
|
||||
@tags = self.class.tag_counts_json(ungrouped_tags, show_pm_tags: guardian.can_tag_pms?)
|
||||
|
@ -49,41 +65,40 @@ class TagsController < ::ApplicationController
|
|||
tags = show_all_tags ? Tag.all : Tag.where("tags.topic_count > 0")
|
||||
unrestricted_tags = DiscourseTagging.filter_visible(tags.where(target_tag_id: nil), guardian)
|
||||
|
||||
categories = Category.where("id IN (SELECT category_id FROM category_tags)")
|
||||
categories =
|
||||
Category
|
||||
.where("id IN (SELECT category_id FROM category_tags)")
|
||||
.where("id IN (?)", guardian.allowed_category_ids)
|
||||
.includes(:tags)
|
||||
|
||||
category_tag_counts = categories.map do |c|
|
||||
category_tags = self.class.tag_counts_json(
|
||||
DiscourseTagging.filter_visible(c.tags.where(target_tag_id: nil), guardian)
|
||||
category_tag_counts =
|
||||
categories
|
||||
.map do |c|
|
||||
category_tags =
|
||||
self.class.tag_counts_json(
|
||||
DiscourseTagging.filter_visible(c.tags.where(target_tag_id: nil), guardian),
|
||||
)
|
||||
next if category_tags.empty?
|
||||
{ id: c.id, tags: category_tags }
|
||||
end.compact
|
||||
end
|
||||
.compact
|
||||
|
||||
@tags = self.class.tag_counts_json(unrestricted_tags, show_pm_tags: guardian.can_tag_pms?)
|
||||
@extras = { categories: category_tag_counts }
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :index }
|
||||
|
||||
format.html do
|
||||
render :index
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: {
|
||||
tags: @tags,
|
||||
extras: @extras
|
||||
}
|
||||
end
|
||||
format.json { render json: { tags: @tags, extras: @extras } }
|
||||
end
|
||||
end
|
||||
|
||||
Discourse.filters.each do |filter|
|
||||
define_method("show_#{filter}") do
|
||||
@tag_id = params[:tag_id].force_encoding("UTF-8")
|
||||
@additional_tags = params[:additional_tag_ids].to_s.split('/').map { |t| t.force_encoding("UTF-8") }
|
||||
@additional_tags =
|
||||
params[:additional_tag_ids].to_s.split("/").map { |t| t.force_encoding("UTF-8") }
|
||||
|
||||
list_opts = build_topic_list_options
|
||||
@list = nil
|
||||
|
@ -101,14 +116,14 @@ class TagsController < ::ApplicationController
|
|||
@list.more_topics_url = construct_url_with(:next, list_opts)
|
||||
@list.prev_topics_url = construct_url_with(:prev, list_opts)
|
||||
@rss = "tag"
|
||||
@description_meta = I18n.t("rss_by_tag", tag: tag_params.join(' & '))
|
||||
@description_meta = I18n.t("rss_by_tag", tag: tag_params.join(" & "))
|
||||
@title = @description_meta
|
||||
|
||||
canonical_params = params.slice(:category_slug_path_with_id, :tag_id)
|
||||
canonical_method = url_method(canonical_params)
|
||||
canonical_url "#{Discourse.base_url_no_prefix}#{public_send(canonical_method, *(canonical_params.values.map { |t| t.force_encoding("UTF-8") }))}"
|
||||
|
||||
if @list.topics.size == 0 && params[:tag_id] != 'none' && !Tag.where_name(@tag_id).exists?
|
||||
if @list.topics.size == 0 && params[:tag_id] != "none" && !Tag.where_name(@tag_id).exists?
|
||||
raise Discourse::NotFound.new("tag not found", check_permalinks: true)
|
||||
else
|
||||
respond_with_list(@list)
|
||||
|
@ -121,12 +136,7 @@ class TagsController < ::ApplicationController
|
|||
end
|
||||
|
||||
def info
|
||||
render_serialized(
|
||||
@tag,
|
||||
DetailedTagSerializer,
|
||||
rest_serializer: true,
|
||||
root: :tag_info
|
||||
)
|
||||
render_serialized(@tag, DetailedTagSerializer, rest_serializer: true, root: :tag_info)
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -141,7 +151,11 @@ class TagsController < ::ApplicationController
|
|||
end
|
||||
tag.description = params[:tag][:description] if params[:tag]&.has_key?(:description)
|
||||
if tag.save
|
||||
StaffActionLogger.new(current_user).log_custom('renamed_tag', previous_value: params[:tag_id], new_value: new_tag_name)
|
||||
StaffActionLogger.new(current_user).log_custom(
|
||||
"renamed_tag",
|
||||
previous_value: params[:tag_id],
|
||||
new_value: new_tag_name,
|
||||
)
|
||||
render json: { tag: { id: tag.name, description: tag.description } }
|
||||
else
|
||||
render_json_error tag.errors.full_messages
|
||||
|
@ -149,7 +163,7 @@ class TagsController < ::ApplicationController
|
|||
end
|
||||
|
||||
def upload
|
||||
require 'csv'
|
||||
require "csv"
|
||||
|
||||
guardian.ensure_can_admin_tags!
|
||||
|
||||
|
@ -159,7 +173,9 @@ class TagsController < ::ApplicationController
|
|||
begin
|
||||
Tag.transaction do
|
||||
CSV.foreach(file.tempfile) do |row|
|
||||
raise Discourse::InvalidParameters.new(I18n.t("tags.upload_row_too_long")) if row.length > 2
|
||||
if row.length > 2
|
||||
raise Discourse::InvalidParameters.new(I18n.t("tags.upload_row_too_long"))
|
||||
end
|
||||
|
||||
tag_name = DiscourseTagging.clean_tag(row[0])
|
||||
tag_group_name = row[1] || nil
|
||||
|
@ -167,7 +183,8 @@ class TagsController < ::ApplicationController
|
|||
tag = Tag.find_by_name(tag_name) || Tag.create!(name: tag_name)
|
||||
|
||||
if tag_group_name
|
||||
tag_group = TagGroup.find_by(name: tag_group_name) || TagGroup.create!(name: tag_group_name)
|
||||
tag_group =
|
||||
TagGroup.find_by(name: tag_group_name) || TagGroup.create!(name: tag_group_name)
|
||||
tag.tag_groups << tag_group unless tag.tag_groups.include?(tag_group)
|
||||
end
|
||||
end
|
||||
|
@ -187,7 +204,7 @@ class TagsController < ::ApplicationController
|
|||
def destroy_unused
|
||||
guardian.ensure_can_admin_tags!
|
||||
tags = Tag.unused
|
||||
StaffActionLogger.new(current_user).log_custom('deleted_unused_tags', tags: tags.pluck(:name))
|
||||
StaffActionLogger.new(current_user).log_custom("deleted_unused_tags", tags: tags.pluck(:name))
|
||||
tags.destroy_all
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -200,7 +217,7 @@ class TagsController < ::ApplicationController
|
|||
|
||||
TopicCustomField.transaction do
|
||||
tag.destroy
|
||||
StaffActionLogger.new(current_user).log_custom('deleted_tag', subject: tag_name)
|
||||
StaffActionLogger.new(current_user).log_custom("deleted_tag", subject: tag_name)
|
||||
end
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -218,7 +235,7 @@ class TagsController < ::ApplicationController
|
|||
latest_results = query.latest_results
|
||||
@topic_list = query.create_list(:by_tag, {}, latest_results)
|
||||
|
||||
render 'list/list', formats: [:rss]
|
||||
render "list/list", formats: [:rss]
|
||||
end
|
||||
|
||||
def search
|
||||
|
@ -227,16 +244,14 @@ class TagsController < ::ApplicationController
|
|||
selected_tags: params[:selected_tags],
|
||||
limit: params[:limit],
|
||||
exclude_synonyms: params[:excludeSynonyms],
|
||||
exclude_has_synonyms: params[:excludeHasSynonyms]
|
||||
exclude_has_synonyms: params[:excludeHasSynonyms],
|
||||
}
|
||||
|
||||
if filter_params[:limit] && filter_params[:limit].to_i < 0
|
||||
raise Discourse::InvalidParameters.new(:limit)
|
||||
end
|
||||
|
||||
if params[:categoryId]
|
||||
filter_params[:category] = Category.find_by_id(params[:categoryId])
|
||||
end
|
||||
filter_params[:category] = Category.find_by_id(params[:categoryId]) if params[:categoryId]
|
||||
|
||||
if !params[:q].blank?
|
||||
clean_name = DiscourseTagging.clean_tag(params[:q])
|
||||
|
@ -246,27 +261,35 @@ class TagsController < ::ApplicationController
|
|||
filter_params[:order_popularity] = true
|
||||
end
|
||||
|
||||
tags_with_counts, filter_result_context = DiscourseTagging.filter_allowed_tags(
|
||||
guardian,
|
||||
**filter_params,
|
||||
with_context: true
|
||||
)
|
||||
tags_with_counts, filter_result_context =
|
||||
DiscourseTagging.filter_allowed_tags(guardian, **filter_params, with_context: true)
|
||||
|
||||
tags = self.class.tag_counts_json(tags_with_counts, show_pm_tags: guardian.can_tag_pms?)
|
||||
|
||||
json_response = { results: tags }
|
||||
|
||||
if clean_name && !tags.find { |h| h[:id].downcase == clean_name.downcase } && tag = Tag.where_name(clean_name).first
|
||||
if clean_name && !tags.find { |h| h[:id].downcase == clean_name.downcase } &&
|
||||
tag = Tag.where_name(clean_name).first
|
||||
# filter_allowed_tags determined that the tag entered is not allowed
|
||||
json_response[:forbidden] = params[:q]
|
||||
|
||||
if filter_params[:exclude_synonyms] && tag.synonym?
|
||||
json_response[:forbidden_message] = I18n.t("tags.forbidden.synonym", tag_name: tag.target_tag.name)
|
||||
json_response[:forbidden_message] = I18n.t(
|
||||
"tags.forbidden.synonym",
|
||||
tag_name: tag.target_tag.name,
|
||||
)
|
||||
elsif filter_params[:exclude_has_synonyms] && tag.synonyms.exists?
|
||||
json_response[:forbidden_message] = I18n.t("tags.forbidden.has_synonyms", tag_name: tag.name)
|
||||
json_response[:forbidden_message] = I18n.t(
|
||||
"tags.forbidden.has_synonyms",
|
||||
tag_name: tag.name,
|
||||
)
|
||||
else
|
||||
category_names = tag.categories.where(id: guardian.allowed_category_ids).pluck(:name)
|
||||
category_names += Category.joins(tag_groups: :tags).where(id: guardian.allowed_category_ids, "tags.id": tag.id).pluck(:name)
|
||||
category_names +=
|
||||
Category
|
||||
.joins(tag_groups: :tags)
|
||||
.where(id: guardian.allowed_category_ids, "tags.id": tag.id)
|
||||
.pluck(:name)
|
||||
|
||||
if category_names.present?
|
||||
category_names.uniq!
|
||||
|
@ -274,10 +297,13 @@ class TagsController < ::ApplicationController
|
|||
"tags.forbidden.restricted_to",
|
||||
count: category_names.count,
|
||||
tag_name: tag.name,
|
||||
category_names: category_names.join(", ")
|
||||
category_names: category_names.join(", "),
|
||||
)
|
||||
else
|
||||
json_response[:forbidden_message] = I18n.t("tags.forbidden.in_this_category", tag_name: tag.name)
|
||||
json_response[:forbidden_message] = I18n.t(
|
||||
"tags.forbidden.in_this_category",
|
||||
tag_name: tag.name,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -292,7 +318,9 @@ class TagsController < ::ApplicationController
|
|||
def notifications
|
||||
tag = Tag.where_name(params[:tag_id]).first
|
||||
raise Discourse::NotFound unless tag
|
||||
level = tag.tag_users.where(user: current_user).first.try(:notification_level) || TagUser.notification_levels[:regular]
|
||||
level =
|
||||
tag.tag_users.where(user: current_user).first.try(:notification_level) ||
|
||||
TagUser.notification_levels[:regular]
|
||||
render json: { tag_notification: { id: tag.name, notification_level: level.to_i } }
|
||||
end
|
||||
|
||||
|
@ -318,8 +346,13 @@ class TagsController < ::ApplicationController
|
|||
guardian.ensure_can_admin_tags!
|
||||
value = DiscourseTagging.add_or_create_synonyms_by_name(@tag, params[:synonyms])
|
||||
if value.is_a?(Array)
|
||||
render json: failed_json.merge(
|
||||
failed_tags: value.inject({}) { |h, t| h[t.name] = t.errors.full_messages.first; h }
|
||||
render json:
|
||||
failed_json.merge(
|
||||
failed_tags:
|
||||
value.inject({}) do |h, t|
|
||||
h[t.name] = t.errors.full_messages.first
|
||||
h
|
||||
end,
|
||||
)
|
||||
else
|
||||
render json: success_json
|
||||
|
@ -350,12 +383,15 @@ class TagsController < ::ApplicationController
|
|||
end
|
||||
|
||||
def ensure_visible
|
||||
raise Discourse::NotFound if DiscourseTagging.hidden_tag_names(guardian).include?(params[:tag_id])
|
||||
if DiscourseTagging.hidden_tag_names(guardian).include?(params[:tag_id])
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def self.tag_counts_json(tags, show_pm_tags: true)
|
||||
target_tags = Tag.where(id: tags.map(&:target_tag_id).compact.uniq).select(:id, :name)
|
||||
tags.map do |t|
|
||||
tags
|
||||
.map do |t|
|
||||
next if t.topic_count == 0 && t.pm_topic_count > 0 && !show_pm_tags
|
||||
|
||||
{
|
||||
|
@ -365,9 +401,11 @@ class TagsController < ::ApplicationController
|
|||
description: t.description,
|
||||
count: t.topic_count,
|
||||
pm_count: show_pm_tags ? t.pm_topic_count : 0,
|
||||
target_tag: t.target_tag_id ? target_tags.find { |x| x.id == t.target_tag_id }&.name : nil
|
||||
target_tag:
|
||||
t.target_tag_id ? target_tags.find { |x| x.id == t.target_tag_id }&.name : nil,
|
||||
}
|
||||
end.compact
|
||||
end
|
||||
.compact
|
||||
end
|
||||
|
||||
def set_category
|
||||
|
@ -383,7 +421,10 @@ class TagsController < ::ApplicationController
|
|||
if !@filter_on_category
|
||||
permalink = Permalink.find_by_url("c/#{params[:category_slug_path_with_id]}")
|
||||
if permalink.present? && permalink.category_id
|
||||
return redirect_to "#{Discourse::base_path}/tags#{permalink.target_url}/#{params[:tag_id]}", status: :moved_permanently
|
||||
return(
|
||||
redirect_to "#{Discourse.base_path}/tags#{permalink.target_url}/#{params[:tag_id]}",
|
||||
status: :moved_permanently
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -394,14 +435,15 @@ class TagsController < ::ApplicationController
|
|||
end
|
||||
|
||||
def page_params
|
||||
route_params = { format: 'json' }
|
||||
route_params = { format: "json" }
|
||||
|
||||
if @filter_on_category
|
||||
if request.path_parameters.include?(:category_slug_path_with_id)
|
||||
slug_path = @filter_on_category.slug_path
|
||||
|
||||
route_params[:category_slug_path_with_id] =
|
||||
(slug_path + [@filter_on_category.id.to_s]).join("/")
|
||||
route_params[:category_slug_path_with_id] = (
|
||||
slug_path + [@filter_on_category.id.to_s]
|
||||
).join("/")
|
||||
else
|
||||
route_params[:category] = @filter_on_category.slug_for_url
|
||||
end
|
||||
|
@ -453,11 +495,12 @@ class TagsController < ::ApplicationController
|
|||
raise Discourse::NotFound
|
||||
end
|
||||
|
||||
url.sub('.json?', '?')
|
||||
url.sub(".json?", "?")
|
||||
end
|
||||
|
||||
def build_topic_list_options
|
||||
options = super.merge(
|
||||
options =
|
||||
super.merge(
|
||||
page: params[:page],
|
||||
topic_ids: param_to_integer_list(:topic_ids),
|
||||
category: @filter_on_category ? @filter_on_category.id : params[:category],
|
||||
|
@ -469,12 +512,13 @@ class TagsController < ::ApplicationController
|
|||
filter: params[:filter],
|
||||
state: params[:state],
|
||||
search: params[:search],
|
||||
q: params[:q]
|
||||
q: params[:q],
|
||||
)
|
||||
options[:no_subcategories] = true if params[:no_subcategories] == true || params[:no_subcategories] == 'true'
|
||||
options[:no_subcategories] = true if params[:no_subcategories] == true ||
|
||||
params[:no_subcategories] == "true"
|
||||
options[:per_page] = params[:per_page].to_i.clamp(1, 30) if params[:per_page].present?
|
||||
|
||||
if params[:tag_id] == 'none'
|
||||
if params[:tag_id] == "none"
|
||||
options.delete(:tags)
|
||||
options[:no_tags] = true
|
||||
else
|
||||
|
|
|
@ -9,10 +9,10 @@ class ThemeJavascriptsController < ApplicationController
|
|||
:preload_json,
|
||||
:redirect_to_login_if_required,
|
||||
:verify_authenticity_token,
|
||||
only: [:show, :show_map, :show_tests]
|
||||
only: %i[show show_map show_tests],
|
||||
)
|
||||
|
||||
before_action :is_asset_path, :no_cookies, :apply_cdn_headers, only: [:show, :show_map, :show_tests]
|
||||
before_action :is_asset_path, :no_cookies, :apply_cdn_headers, only: %i[show show_map show_tests]
|
||||
|
||||
def show
|
||||
raise Discourse::NotFound unless last_modified.present?
|
||||
|
@ -24,7 +24,8 @@ class ThemeJavascriptsController < ApplicationController
|
|||
write_if_not_cached(cache_file) do
|
||||
content, has_source_map = query.pluck_first(:content, "source_map IS NOT NULL")
|
||||
if has_source_map
|
||||
content += "\n//# sourceMappingURL=#{params[:digest]}.map?__ws=#{Discourse.current_hostname}\n"
|
||||
content +=
|
||||
"\n//# sourceMappingURL=#{params[:digest]}.map?__ws=#{Discourse.current_hostname}\n"
|
||||
end
|
||||
content
|
||||
end
|
||||
|
@ -39,9 +40,7 @@ class ThemeJavascriptsController < ApplicationController
|
|||
# Security: safe due to route constraint
|
||||
cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.map"
|
||||
|
||||
write_if_not_cached(cache_file) do
|
||||
query.pluck_first(:source_map)
|
||||
end
|
||||
write_if_not_cached(cache_file) { query.pluck_first(:source_map) }
|
||||
|
||||
serve_file(cache_file)
|
||||
end
|
||||
|
@ -59,9 +58,7 @@ class ThemeJavascriptsController < ApplicationController
|
|||
@cache_file = "#{TESTS_DISK_CACHE_PATH}/#{digest}.js"
|
||||
return render body: nil, status: 304 if not_modified?
|
||||
|
||||
write_if_not_cached(@cache_file) do
|
||||
content
|
||||
end
|
||||
write_if_not_cached(@cache_file) { content }
|
||||
|
||||
serve_file @cache_file
|
||||
end
|
||||
|
@ -73,7 +70,8 @@ class ThemeJavascriptsController < ApplicationController
|
|||
end
|
||||
|
||||
def last_modified
|
||||
@last_modified ||= begin
|
||||
@last_modified ||=
|
||||
begin
|
||||
if params[:action].to_s == "show_tests"
|
||||
File.exist?(@cache_file) ? File.ctime(@cache_file) : nil
|
||||
else
|
||||
|
@ -95,10 +93,10 @@ class ThemeJavascriptsController < ApplicationController
|
|||
|
||||
def set_cache_control_headers
|
||||
if Rails.env.development?
|
||||
response.headers['Last-Modified'] = Time.zone.now.httpdate
|
||||
response.headers["Last-Modified"] = Time.zone.now.httpdate
|
||||
immutable_for(1.second)
|
||||
else
|
||||
response.headers['Last-Modified'] = last_modified.httpdate if last_modified
|
||||
response.headers["Last-Modified"] = last_modified.httpdate if last_modified
|
||||
immutable_for(1.year)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TopicsController < ApplicationController
|
||||
requires_login only: [
|
||||
:timings,
|
||||
:destroy_timings,
|
||||
:update,
|
||||
:update_shared_draft,
|
||||
:destroy,
|
||||
:recover,
|
||||
:status,
|
||||
:invite,
|
||||
:mute,
|
||||
:unmute,
|
||||
:set_notifications,
|
||||
:move_posts,
|
||||
:merge_topic,
|
||||
:clear_pin,
|
||||
:re_pin,
|
||||
:status_update,
|
||||
:timer,
|
||||
:bulk,
|
||||
:reset_new,
|
||||
:change_post_owners,
|
||||
:change_timestamps,
|
||||
:archive_message,
|
||||
:move_to_inbox,
|
||||
:convert_topic,
|
||||
:bookmark,
|
||||
:publish,
|
||||
:reset_bump_date,
|
||||
:set_slow_mode
|
||||
requires_login only: %i[
|
||||
timings
|
||||
destroy_timings
|
||||
update
|
||||
update_shared_draft
|
||||
destroy
|
||||
recover
|
||||
status
|
||||
invite
|
||||
mute
|
||||
unmute
|
||||
set_notifications
|
||||
move_posts
|
||||
merge_topic
|
||||
clear_pin
|
||||
re_pin
|
||||
status_update
|
||||
timer
|
||||
bulk
|
||||
reset_new
|
||||
change_post_owners
|
||||
change_timestamps
|
||||
archive_message
|
||||
move_to_inbox
|
||||
convert_topic
|
||||
bookmark
|
||||
publish
|
||||
reset_bump_date
|
||||
set_slow_mode
|
||||
]
|
||||
|
||||
before_action :consider_user_for_promotion, only: :show
|
||||
|
||||
skip_before_action :check_xhr, only: [:show, :feed]
|
||||
skip_before_action :check_xhr, only: %i[show feed]
|
||||
|
||||
def id_for_slug
|
||||
topic = Topic.find_by_slug(params[:slug])
|
||||
|
@ -51,9 +51,7 @@ class TopicsController < ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
if request.referer
|
||||
flash["referer"] ||= request.referer[0..255]
|
||||
end
|
||||
flash["referer"] ||= request.referer[0..255] if request.referer
|
||||
|
||||
# We'd like to migrate the wordpress feed to another url. This keeps up backwards compatibility with
|
||||
# existing installs.
|
||||
|
@ -61,13 +59,27 @@ class TopicsController < ApplicationController
|
|||
|
||||
# work around people somehow sending in arrays,
|
||||
# arrays are not supported
|
||||
params[:page] = params[:page].to_i rescue 1
|
||||
params[:page] = begin
|
||||
params[:page].to_i
|
||||
rescue StandardError
|
||||
1
|
||||
end
|
||||
|
||||
opts = params.slice(:username_filters, :filter, :page, :post_number, :show_deleted, :replies_to_post_number, :filter_upwards_post_id, :filter_top_level_replies)
|
||||
opts =
|
||||
params.slice(
|
||||
:username_filters,
|
||||
:filter,
|
||||
:page,
|
||||
:post_number,
|
||||
:show_deleted,
|
||||
:replies_to_post_number,
|
||||
:filter_upwards_post_id,
|
||||
:filter_top_level_replies,
|
||||
)
|
||||
username_filters = opts[:username_filters]
|
||||
|
||||
opts[:print] = true if params[:print] == 'true'
|
||||
opts[:username_filters] = username_filters.split(',') if username_filters.is_a?(String)
|
||||
opts[:print] = true if params[:print] == "true"
|
||||
opts[:username_filters] = username_filters.split(",") if username_filters.is_a?(String)
|
||||
|
||||
# Special case: a slug with a number in front should look by slug first before looking
|
||||
# up that particular number
|
||||
|
@ -79,7 +91,14 @@ class TopicsController < ApplicationController
|
|||
if opts[:print]
|
||||
raise Discourse::InvalidAccess unless SiteSetting.max_prints_per_hour_per_user > 0
|
||||
begin
|
||||
RateLimiter.new(current_user, "print-topic-per-hour", SiteSetting.max_prints_per_hour_per_user, 1.hour).performed! unless @guardian.is_admin?
|
||||
unless @guardian.is_admin?
|
||||
RateLimiter.new(
|
||||
current_user,
|
||||
"print-topic-per-hour",
|
||||
SiteSetting.max_prints_per_hour_per_user,
|
||||
1.hour,
|
||||
).performed!
|
||||
end
|
||||
rescue RateLimiter::LimitExceeded
|
||||
return render_json_error I18n.t("rate_limiter.slow_down")
|
||||
end
|
||||
|
@ -100,27 +119,28 @@ class TopicsController < ApplicationController
|
|||
# If the user can't see the topic, clean up notifications for it.
|
||||
Notification.remove_for(current_user.id, params[:topic_id]) if current_user
|
||||
|
||||
deleted = guardian.can_see_topic?(ex.obj, false) ||
|
||||
(!guardian.can_see_topic?(ex.obj) &&
|
||||
ex.obj&.access_topic_via_group &&
|
||||
ex.obj.deleted_at)
|
||||
deleted =
|
||||
guardian.can_see_topic?(ex.obj, false) ||
|
||||
(!guardian.can_see_topic?(ex.obj) && ex.obj&.access_topic_via_group && ex.obj.deleted_at)
|
||||
|
||||
if SiteSetting.detailed_404
|
||||
if deleted
|
||||
raise Discourse::NotFound.new(
|
||||
'deleted topic',
|
||||
custom_message: 'deleted_topic',
|
||||
"deleted topic",
|
||||
custom_message: "deleted_topic",
|
||||
status: 410,
|
||||
check_permalinks: true,
|
||||
original_path: ex.obj.relative_url
|
||||
original_path: ex.obj.relative_url,
|
||||
)
|
||||
elsif !guardian.can_see_topic?(ex.obj) && group = ex.obj&.access_topic_via_group
|
||||
raise Discourse::InvalidAccess.new(
|
||||
'not in group',
|
||||
"not in group",
|
||||
ex.obj,
|
||||
custom_message: 'not_in_group.title_topic',
|
||||
custom_message_params: { group: group.name },
|
||||
group: group
|
||||
custom_message: "not_in_group.title_topic",
|
||||
custom_message_params: {
|
||||
group: group.name,
|
||||
},
|
||||
group: group,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -129,7 +149,7 @@ class TopicsController < ApplicationController
|
|||
raise Discourse::NotFound.new(
|
||||
nil,
|
||||
check_permalinks: deleted,
|
||||
original_path: ex.obj.relative_url
|
||||
original_path: ex.obj.relative_url,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -152,9 +172,7 @@ class TopicsController < ApplicationController
|
|||
@topic_view.draft = Draft.get(current_user, @topic_view.draft_key, @topic_view.draft_sequence)
|
||||
end
|
||||
|
||||
unless @topic_view.topic.visible
|
||||
response.headers['X-Robots-Tag'] = 'noindex'
|
||||
end
|
||||
response.headers["X-Robots-Tag"] = "noindex" unless @topic_view.topic.visible
|
||||
|
||||
canonical_url UrlHelper.absolute_without_cdn(@topic_view.canonical_path)
|
||||
|
||||
|
@ -162,7 +180,7 @@ class TopicsController < ApplicationController
|
|||
# we would like to give them a bit more signal about age of data
|
||||
if use_crawler_layout?
|
||||
if last_modified = @topic_view.posts&.map { |p| p.updated_at }&.max&.httpdate
|
||||
response.headers['Last-Modified'] = last_modified
|
||||
response.headers["Last-Modified"] = last_modified
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -186,7 +204,13 @@ class TopicsController < ApplicationController
|
|||
def wordpress
|
||||
params.require(:best)
|
||||
params.require(:topic_id)
|
||||
params.permit(:min_trust_level, :min_score, :min_replies, :bypass_trust_level_score, :only_moderator_liked)
|
||||
params.permit(
|
||||
:min_trust_level,
|
||||
:min_score,
|
||||
:min_replies,
|
||||
:bypass_trust_level_score,
|
||||
:only_moderator_liked,
|
||||
)
|
||||
|
||||
opts = {
|
||||
best: params[:best].to_i,
|
||||
|
@ -195,13 +219,14 @@ class TopicsController < ApplicationController
|
|||
min_replies: params[:min_replies].to_i,
|
||||
bypass_trust_level_score: params[:bypass_trust_level_score].to_i, # safe cause 0 means ignore
|
||||
only_moderator_liked: params[:only_moderator_liked].to_s == "true",
|
||||
exclude_hidden: true
|
||||
exclude_hidden: true,
|
||||
}
|
||||
|
||||
@topic_view = TopicView.new(params[:topic_id], current_user, opts)
|
||||
discourse_expires_in 1.minute
|
||||
|
||||
wordpress_serializer = TopicViewWordpressSerializer.new(@topic_view, scope: guardian, root: false)
|
||||
wordpress_serializer =
|
||||
TopicViewWordpressSerializer.new(@topic_view, scope: guardian, root: false)
|
||||
render_json_dump(wordpress_serializer)
|
||||
end
|
||||
|
||||
|
@ -214,7 +239,7 @@ class TopicsController < ApplicationController
|
|||
filter: params[:filter],
|
||||
skip_limit: true,
|
||||
asc: true,
|
||||
skip_custom_fields: true
|
||||
skip_custom_fields: true,
|
||||
}
|
||||
|
||||
fetch_topic_view(options)
|
||||
|
@ -243,8 +268,8 @@ class TopicsController < ApplicationController
|
|||
@topic_view,
|
||||
scope: guardian,
|
||||
root: false,
|
||||
include_raw: !!params[:include_raw]
|
||||
)
|
||||
include_raw: !!params[:include_raw],
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -266,8 +291,10 @@ class TopicsController < ApplicationController
|
|||
@topic = Topic.with_deleted.where(id: params[:topic_id]).first
|
||||
guardian.ensure_can_see!(@topic)
|
||||
|
||||
@posts = Post.where(hidden: false, deleted_at: nil, topic_id: @topic.id)
|
||||
.where('posts.id in (?)', post_ids)
|
||||
@posts =
|
||||
Post
|
||||
.where(hidden: false, deleted_at: nil, topic_id: @topic.id)
|
||||
.where("posts.id in (?)", post_ids)
|
||||
.joins("LEFT JOIN users u on u.id = posts.user_id")
|
||||
.pluck(:id, :cooked, :username, :action_code, :created_at)
|
||||
.map do |post_id, cooked, username, action_code, created_at|
|
||||
|
@ -297,18 +324,14 @@ class TopicsController < ApplicationController
|
|||
PostTiming.destroy_for(current_user.id, [topic_id])
|
||||
end
|
||||
|
||||
last_notification = Notification
|
||||
.where(
|
||||
user_id: current_user.id,
|
||||
topic_id: topic_id
|
||||
)
|
||||
last_notification =
|
||||
Notification
|
||||
.where(user_id: current_user.id, topic_id: topic_id)
|
||||
.order(created_at: :desc)
|
||||
.limit(1)
|
||||
.first
|
||||
|
||||
if last_notification
|
||||
last_notification.update!(read: false)
|
||||
end
|
||||
last_notification.update!(read: false) if last_notification
|
||||
|
||||
render body: nil
|
||||
end
|
||||
|
@ -321,9 +344,7 @@ class TopicsController < ApplicationController
|
|||
guardian.ensure_can_publish_topic!(topic, category)
|
||||
|
||||
row_count = SharedDraft.where(topic_id: topic.id).update_all(category_id: category.id)
|
||||
if row_count == 0
|
||||
SharedDraft.create(topic_id: topic.id, category_id: category.id)
|
||||
end
|
||||
SharedDraft.create(topic_id: topic.id, category_id: category.id) if row_count == 0
|
||||
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -342,15 +363,14 @@ class TopicsController < ApplicationController
|
|||
if category || (params[:category_id].to_i == 0)
|
||||
guardian.ensure_can_move_topic_to_category!(category)
|
||||
else
|
||||
return render_json_error(I18n.t('category.errors.not_found'))
|
||||
return render_json_error(I18n.t("category.errors.not_found"))
|
||||
end
|
||||
|
||||
if category && topic_tags = (params[:tags] || topic.tags.pluck(:name)).reject { |c| c.empty? }
|
||||
if category &&
|
||||
topic_tags = (params[:tags] || topic.tags.pluck(:name)).reject { |c| c.empty? }
|
||||
if topic_tags.present?
|
||||
allowed_tags = DiscourseTagging.filter_allowed_tags(
|
||||
guardian,
|
||||
category: category
|
||||
).map(&:name)
|
||||
allowed_tags =
|
||||
DiscourseTagging.filter_allowed_tags(guardian, category: category).map(&:name)
|
||||
|
||||
invalid_tags = topic_tags - allowed_tags
|
||||
|
||||
|
@ -367,9 +387,13 @@ class TopicsController < ApplicationController
|
|||
|
||||
if !invalid_tags.empty?
|
||||
if (invalid_tags & DiscourseTagging.hidden_tag_names).present?
|
||||
return render_json_error(I18n.t('category.errors.disallowed_tags_generic'))
|
||||
return render_json_error(I18n.t("category.errors.disallowed_tags_generic"))
|
||||
else
|
||||
return render_json_error(I18n.t('category.errors.disallowed_topic_tags', tags: invalid_tags.join(", ")))
|
||||
return(
|
||||
render_json_error(
|
||||
I18n.t("category.errors.disallowed_topic_tags", tags: invalid_tags.join(", ")),
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -379,9 +403,7 @@ class TopicsController < ApplicationController
|
|||
|
||||
changes = {}
|
||||
|
||||
PostRevisor.tracked_topic_fields.each_key do |f|
|
||||
changes[f] = params[f] if params.has_key?(f)
|
||||
end
|
||||
PostRevisor.tracked_topic_fields.each_key { |f| changes[f] = params[f] if params.has_key?(f) }
|
||||
|
||||
changes.delete(:title) if topic.title == changes[:title]
|
||||
changes.delete(:category_id) if topic.category_id.to_i == changes[:category_id].to_i
|
||||
|
@ -397,17 +419,16 @@ class TopicsController < ApplicationController
|
|||
bypass_bump = should_bypass_bump?(changes)
|
||||
|
||||
first_post = topic.ordered_posts.first
|
||||
success = PostRevisor.new(first_post, topic).revise!(
|
||||
success =
|
||||
PostRevisor.new(first_post, topic).revise!(
|
||||
current_user,
|
||||
changes,
|
||||
validate_post: false,
|
||||
bypass_bump: bypass_bump,
|
||||
keep_existing_draft: params[:keep_existing_draft].to_s == "true"
|
||||
keep_existing_draft: params[:keep_existing_draft].to_s == "true",
|
||||
)
|
||||
|
||||
if !success && topic.errors.blank?
|
||||
topic.errors.add(:base, :unable_to_update)
|
||||
end
|
||||
topic.errors.add(:base, :unable_to_update) if !success && topic.errors.blank?
|
||||
end
|
||||
|
||||
# this is used to return the title to the client as it may have been changed by "TextCleaner"
|
||||
|
@ -419,7 +440,12 @@ class TopicsController < ApplicationController
|
|||
topic = Topic.find_by(id: params[:topic_id])
|
||||
guardian.ensure_can_edit_tags!(topic)
|
||||
|
||||
success = PostRevisor.new(topic.first_post, topic).revise!(current_user, { tags: params[:tags] }, validate_post: false)
|
||||
success =
|
||||
PostRevisor.new(topic.first_post, topic).revise!(
|
||||
current_user,
|
||||
{ tags: params[:tags] },
|
||||
validate_post: false,
|
||||
)
|
||||
|
||||
success ? render_serialized(topic, BasicTopicSerializer) : render_json_error(topic)
|
||||
end
|
||||
|
@ -431,8 +457,14 @@ class TopicsController < ApplicationController
|
|||
visible_topics = Topic.listable_topics.visible
|
||||
|
||||
render json: {
|
||||
pinned_in_category_count: visible_topics.where(category_id: category_id).where(pinned_globally: false).where.not(pinned_at: nil).count,
|
||||
pinned_globally_count: visible_topics.where(pinned_globally: true).where.not(pinned_at: nil).count,
|
||||
pinned_in_category_count:
|
||||
visible_topics
|
||||
.where(category_id: category_id)
|
||||
.where(pinned_globally: false)
|
||||
.where.not(pinned_at: nil)
|
||||
.count,
|
||||
pinned_globally_count:
|
||||
visible_topics.where(pinned_globally: true).where.not(pinned_at: nil).count,
|
||||
banner_count: Topic.listable_topics.where(archetype: Archetype.banner).count,
|
||||
}
|
||||
end
|
||||
|
@ -444,32 +476,32 @@ class TopicsController < ApplicationController
|
|||
|
||||
status = params[:status]
|
||||
topic_id = params[:topic_id].to_i
|
||||
enabled = params[:enabled] == 'true'
|
||||
enabled = params[:enabled] == "true"
|
||||
|
||||
check_for_status_presence(:status, status)
|
||||
@topic = Topic.find_by(id: topic_id)
|
||||
|
||||
case status
|
||||
when 'closed'
|
||||
when "closed"
|
||||
guardian.ensure_can_close_topic!(@topic)
|
||||
when 'archived'
|
||||
when "archived"
|
||||
guardian.ensure_can_archive_topic!(@topic)
|
||||
when 'visible'
|
||||
when "visible"
|
||||
guardian.ensure_can_toggle_topic_visibility!(@topic)
|
||||
when 'pinned'
|
||||
when "pinned"
|
||||
guardian.ensure_can_pin_unpin_topic!(@topic)
|
||||
else
|
||||
guardian.ensure_can_moderate!(@topic)
|
||||
end
|
||||
|
||||
params[:until] === '' ? params[:until] = nil : params[:until]
|
||||
params[:until] === "" ? params[:until] = nil : params[:until]
|
||||
|
||||
@topic.update_status(status, enabled, current_user, until: params[:until])
|
||||
|
||||
render json: success_json.merge!(
|
||||
topic_status_update: TopicTimerSerializer.new(
|
||||
TopicTimer.find_by(topic: @topic), root: false
|
||||
)
|
||||
render json:
|
||||
success_json.merge!(
|
||||
topic_status_update:
|
||||
TopicTimerSerializer.new(TopicTimer.find_by(topic: @topic), root: false),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -488,7 +520,7 @@ class TopicsController < ApplicationController
|
|||
status_type =
|
||||
begin
|
||||
TopicTimer.types.fetch(params[:status_type].to_sym)
|
||||
rescue
|
||||
rescue StandardError
|
||||
invalid_param(:status_type)
|
||||
end
|
||||
based_on_last_post = params[:based_on_last_post]
|
||||
|
@ -497,36 +529,30 @@ class TopicsController < ApplicationController
|
|||
topic = Topic.find_by(id: params[:topic_id])
|
||||
guardian.ensure_can_moderate!(topic)
|
||||
|
||||
if TopicTimer.destructive_types.values.include?(status_type)
|
||||
guardian.ensure_can_delete!(topic)
|
||||
end
|
||||
guardian.ensure_can_delete!(topic) if TopicTimer.destructive_types.values.include?(status_type)
|
||||
|
||||
options = {
|
||||
by_user: current_user,
|
||||
based_on_last_post: based_on_last_post
|
||||
}
|
||||
options = { by_user: current_user, based_on_last_post: based_on_last_post }
|
||||
|
||||
options.merge!(category_id: params[:category_id]) if !params[:category_id].blank?
|
||||
options.merge!(duration_minutes: params[:duration_minutes].to_i) if params[:duration_minutes].present?
|
||||
if params[:duration_minutes].present?
|
||||
options.merge!(duration_minutes: params[:duration_minutes].to_i)
|
||||
end
|
||||
options.merge!(duration: params[:duration].to_i) if params[:duration].present?
|
||||
|
||||
begin
|
||||
topic_timer = topic.set_or_create_timer(
|
||||
status_type,
|
||||
params[:time],
|
||||
**options
|
||||
)
|
||||
topic_timer = topic.set_or_create_timer(status_type, params[:time], **options)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
return render_json_error(e.message)
|
||||
end
|
||||
|
||||
if topic.save
|
||||
render json: success_json.merge!(
|
||||
render json:
|
||||
success_json.merge!(
|
||||
execute_at: topic_timer&.execute_at,
|
||||
duration_minutes: topic_timer&.duration_minutes,
|
||||
based_on_last_post: topic_timer&.based_on_last_post,
|
||||
closed: topic.closed,
|
||||
category_id: topic_timer&.category_id
|
||||
category_id: topic_timer&.category_id,
|
||||
)
|
||||
else
|
||||
render_json_error(topic)
|
||||
|
@ -572,24 +598,16 @@ class TopicsController < ApplicationController
|
|||
|
||||
group_ids = current_user.groups.pluck(:id)
|
||||
if group_ids.present?
|
||||
allowed_groups = topic.allowed_groups
|
||||
.where('topic_allowed_groups.group_id IN (?)', group_ids).pluck(:id)
|
||||
allowed_groups =
|
||||
topic.allowed_groups.where("topic_allowed_groups.group_id IN (?)", group_ids).pluck(:id)
|
||||
|
||||
allowed_groups.each do |id|
|
||||
if archive
|
||||
GroupArchivedMessage.archive!(
|
||||
id,
|
||||
topic,
|
||||
acting_user_id: current_user.id
|
||||
)
|
||||
GroupArchivedMessage.archive!(id, topic, acting_user_id: current_user.id)
|
||||
|
||||
group_id = id
|
||||
else
|
||||
GroupArchivedMessage.move_to_inbox!(
|
||||
id,
|
||||
topic,
|
||||
acting_user_id: current_user.id
|
||||
)
|
||||
GroupArchivedMessage.move_to_inbox!(id, topic, acting_user_id: current_user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -616,9 +634,7 @@ class TopicsController < ApplicationController
|
|||
bookmark_manager = BookmarkManager.new(current_user)
|
||||
bookmark_manager.create_for(bookmarkable_id: topic.id, bookmarkable_type: "Topic")
|
||||
|
||||
if bookmark_manager.errors.any?
|
||||
return render_json_error(bookmark_manager, status: 400)
|
||||
end
|
||||
return render_json_error(bookmark_manager, status: 400) if bookmark_manager.errors.any?
|
||||
|
||||
render body: nil
|
||||
end
|
||||
|
@ -639,7 +655,7 @@ class TopicsController < ApplicationController
|
|||
current_user,
|
||||
topic.ordered_posts.with_deleted.first,
|
||||
context: params[:context],
|
||||
force_destroy: force_destroy
|
||||
force_destroy: force_destroy,
|
||||
).destroy
|
||||
|
||||
render body: nil
|
||||
|
@ -697,15 +713,20 @@ class TopicsController < ApplicationController
|
|||
raise Discourse::NotFound if !topic
|
||||
|
||||
if !pm_has_slots?(topic)
|
||||
return render_json_error(
|
||||
I18n.t("pm_reached_recipients_limit", recipients_limit: SiteSetting.max_allowed_message_recipients)
|
||||
return(
|
||||
render_json_error(
|
||||
I18n.t(
|
||||
"pm_reached_recipients_limit",
|
||||
recipients_limit: SiteSetting.max_allowed_message_recipients,
|
||||
),
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
if topic.private_message?
|
||||
guardian.ensure_can_invite_group_to_private_message!(group, topic)
|
||||
topic.invite_group(current_user, group)
|
||||
render_json_dump BasicGroupSerializer.new(group, scope: guardian, root: 'group')
|
||||
render_json_dump BasicGroupSerializer.new(group, scope: guardian, root: "group")
|
||||
else
|
||||
render json: failed_json, status: 422
|
||||
end
|
||||
|
@ -715,28 +736,31 @@ class TopicsController < ApplicationController
|
|||
topic = Topic.find_by(id: params[:topic_id])
|
||||
raise Discourse::NotFound if !topic
|
||||
|
||||
if !topic.private_message?
|
||||
return render_json_error(I18n.t("topic_invite.not_pm"))
|
||||
end
|
||||
return render_json_error(I18n.t("topic_invite.not_pm")) if !topic.private_message?
|
||||
|
||||
if !pm_has_slots?(topic)
|
||||
return render_json_error(
|
||||
I18n.t("pm_reached_recipients_limit", recipients_limit: SiteSetting.max_allowed_message_recipients)
|
||||
return(
|
||||
render_json_error(
|
||||
I18n.t(
|
||||
"pm_reached_recipients_limit",
|
||||
recipients_limit: SiteSetting.max_allowed_message_recipients,
|
||||
),
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
guardian.ensure_can_invite_to!(topic)
|
||||
|
||||
username_or_email = params[:user] ? fetch_username : fetch_email
|
||||
group_ids = Group.lookup_groups(
|
||||
group_ids: params[:group_ids],
|
||||
group_names: params[:group_names]
|
||||
).pluck(:id)
|
||||
group_ids =
|
||||
Group.lookup_groups(group_ids: params[:group_ids], group_names: params[:group_names]).pluck(
|
||||
:id,
|
||||
)
|
||||
|
||||
begin
|
||||
if topic.invite(current_user, username_or_email, group_ids, params[:custom_message])
|
||||
if user = User.find_by_username_or_email(username_or_email)
|
||||
render_json_dump BasicUserSerializer.new(user, scope: guardian, root: 'user')
|
||||
render_json_dump BasicUserSerializer.new(user, scope: guardian, root: "user")
|
||||
else
|
||||
render json: success_json
|
||||
end
|
||||
|
@ -744,19 +768,16 @@ class TopicsController < ApplicationController
|
|||
json = failed_json
|
||||
|
||||
unless topic.private_message?
|
||||
group_names = topic.category
|
||||
group_names =
|
||||
topic
|
||||
.category
|
||||
.visible_group_names(current_user)
|
||||
.where(automatic: false)
|
||||
.pluck(:name)
|
||||
.join(", ")
|
||||
|
||||
if group_names.present?
|
||||
json.merge!(errors: [
|
||||
I18n.t(
|
||||
"topic_invite.failed_to_invite",
|
||||
group_names: group_names
|
||||
)
|
||||
])
|
||||
json.merge!(errors: [I18n.t("topic_invite.failed_to_invite", group_names: group_names)])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -792,7 +813,8 @@ class TopicsController < ApplicationController
|
|||
|
||||
if params[:archetype].present?
|
||||
args[:archetype] = params[:archetype]
|
||||
args[:participants] = params[:participants] if params[:participants].present? && params[:archetype] == "private_message"
|
||||
args[:participants] = params[:participants] if params[:participants].present? &&
|
||||
params[:archetype] == "private_message"
|
||||
end
|
||||
|
||||
destination_topic = topic.move_posts(current_user, topic.posts.pluck(:id), args)
|
||||
|
@ -814,8 +836,13 @@ class TopicsController < ApplicationController
|
|||
|
||||
if params[:title].present?
|
||||
# when creating a new topic, ensure the 1st post is a regular post
|
||||
if Post.where(topic: topic, id: post_ids).order(:post_number).pluck_first(:post_type) != Post.types[:regular]
|
||||
return render_json_error("When moving posts to a new topic, the first post must be a regular post.")
|
||||
if Post.where(topic: topic, id: post_ids).order(:post_number).pluck_first(:post_type) !=
|
||||
Post.types[:regular]
|
||||
return(
|
||||
render_json_error(
|
||||
"When moving posts to a new topic, the first post must be a regular post.",
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
if params[:category_id].present?
|
||||
|
@ -837,10 +864,12 @@ class TopicsController < ApplicationController
|
|||
guardian.ensure_can_change_post_owner!
|
||||
|
||||
begin
|
||||
PostOwnerChanger.new(post_ids: params[:post_ids].to_a,
|
||||
PostOwnerChanger.new(
|
||||
post_ids: params[:post_ids].to_a,
|
||||
topic_id: params[:topic_id].to_i,
|
||||
new_owner: User.find_by(username: params[:username]),
|
||||
acting_user: current_user).change_owner!
|
||||
acting_user: current_user,
|
||||
).change_owner!
|
||||
render json: success_json
|
||||
rescue ArgumentError
|
||||
render json: failed_json, status: 422
|
||||
|
@ -857,12 +886,13 @@ class TopicsController < ApplicationController
|
|||
previous_timestamp = topic.first_post.created_at
|
||||
|
||||
begin
|
||||
TopicTimestampChanger.new(
|
||||
topic: topic,
|
||||
timestamp: timestamp
|
||||
).change!
|
||||
TopicTimestampChanger.new(topic: topic, timestamp: timestamp).change!
|
||||
|
||||
StaffActionLogger.new(current_user).log_topic_timestamps_changed(topic, Time.zone.at(timestamp), previous_timestamp)
|
||||
StaffActionLogger.new(current_user).log_topic_timestamps_changed(
|
||||
topic,
|
||||
Time.zone.at(timestamp),
|
||||
previous_timestamp,
|
||||
)
|
||||
|
||||
render json: success_json
|
||||
rescue ActiveRecord::RecordInvalid, TopicTimestampChanger::InvalidTimestampError
|
||||
|
@ -900,7 +930,7 @@ class TopicsController < ApplicationController
|
|||
topic_id,
|
||||
topic_time,
|
||||
timings.map { |post_number, t| [post_number.to_i, t.to_i] },
|
||||
mobile: view_context.mobile_view?
|
||||
mobile: view_context.mobile_view?,
|
||||
)
|
||||
render body: nil
|
||||
end
|
||||
|
@ -914,43 +944,48 @@ class TopicsController < ApplicationController
|
|||
rescue Discourse::NotLoggedIn
|
||||
raise Discourse::NotFound
|
||||
rescue Discourse::InvalidAccess => ex
|
||||
|
||||
deleted = guardian.can_see_topic?(ex.obj, false) ||
|
||||
(!guardian.can_see_topic?(ex.obj) &&
|
||||
ex.obj&.access_topic_via_group &&
|
||||
ex.obj.deleted_at)
|
||||
deleted =
|
||||
guardian.can_see_topic?(ex.obj, false) ||
|
||||
(!guardian.can_see_topic?(ex.obj) && ex.obj&.access_topic_via_group && ex.obj.deleted_at)
|
||||
|
||||
raise Discourse::NotFound.new(
|
||||
nil,
|
||||
check_permalinks: deleted,
|
||||
original_path: ex.obj.relative_url
|
||||
original_path: ex.obj.relative_url,
|
||||
)
|
||||
end
|
||||
|
||||
discourse_expires_in 1.minute
|
||||
|
||||
response.headers['X-Robots-Tag'] = 'noindex, nofollow'
|
||||
render 'topics/show', formats: [:rss]
|
||||
response.headers["X-Robots-Tag"] = "noindex, nofollow"
|
||||
render "topics/show", formats: [:rss]
|
||||
end
|
||||
|
||||
def bulk
|
||||
if params[:topic_ids].present?
|
||||
unless Array === params[:topic_ids]
|
||||
raise Discourse::InvalidParameters.new(
|
||||
"Expecting topic_ids to contain a list of topic ids"
|
||||
)
|
||||
raise Discourse::InvalidParameters.new("Expecting topic_ids to contain a list of topic ids")
|
||||
end
|
||||
topic_ids = params[:topic_ids].map { |t| t.to_i }
|
||||
elsif params[:filter] == 'unread'
|
||||
elsif params[:filter] == "unread"
|
||||
topic_ids = bulk_unread_topic_ids
|
||||
else
|
||||
raise ActionController::ParameterMissing.new(:topic_ids)
|
||||
end
|
||||
|
||||
operation = params
|
||||
operation =
|
||||
params
|
||||
.require(:operation)
|
||||
.permit(:type, :group, :category_id, :notification_level_id, *DiscoursePluginRegistry.permitted_bulk_action_parameters, tags: [])
|
||||
.to_h.symbolize_keys
|
||||
.permit(
|
||||
:type,
|
||||
:group,
|
||||
:category_id,
|
||||
:notification_level_id,
|
||||
*DiscoursePluginRegistry.permitted_bulk_action_parameters,
|
||||
tags: [],
|
||||
)
|
||||
.to_h
|
||||
.symbolize_keys
|
||||
|
||||
raise ActionController::ParameterMissing.new(:operation_type) if operation[:type].blank?
|
||||
operator = TopicsBulkAction.new(current_user, topic_ids, operation, group: operation[:group])
|
||||
|
@ -963,14 +998,14 @@ class TopicsController < ApplicationController
|
|||
|
||||
if params[:topic_ids].present?
|
||||
unless Array === params[:topic_ids]
|
||||
raise Discourse::InvalidParameters.new(
|
||||
"Expecting topic_ids to contain a list of topic ids"
|
||||
)
|
||||
raise Discourse::InvalidParameters.new("Expecting topic_ids to contain a list of topic ids")
|
||||
end
|
||||
|
||||
topic_scope = topic_query
|
||||
.private_messages_for(current_user, :all)
|
||||
.where("topics.id IN (?)", params[:topic_ids].map(&:to_i))
|
||||
topic_scope =
|
||||
topic_query.private_messages_for(current_user, :all).where(
|
||||
"topics.id IN (?)",
|
||||
params[:topic_ids].map(&:to_i),
|
||||
)
|
||||
else
|
||||
params.require(:inbox)
|
||||
inbox = params[:inbox].to_s
|
||||
|
@ -978,11 +1013,8 @@ class TopicsController < ApplicationController
|
|||
topic_scope = topic_query.filter_private_message_new(current_user, filter)
|
||||
end
|
||||
|
||||
topic_ids = TopicsBulkAction.new(
|
||||
current_user,
|
||||
topic_scope.pluck(:id),
|
||||
type: "dismiss_topics"
|
||||
).perform!
|
||||
topic_ids =
|
||||
TopicsBulkAction.new(current_user, topic_scope.pluck(:id), type: "dismiss_topics").perform!
|
||||
|
||||
render json: success_json.merge(topic_ids: topic_ids)
|
||||
end
|
||||
|
@ -991,8 +1023,9 @@ class TopicsController < ApplicationController
|
|||
topic_scope =
|
||||
if params[:category_id].present?
|
||||
category_ids = [params[:category_id]]
|
||||
if params[:include_subcategories] == 'true'
|
||||
category_ids = category_ids.concat(Category.where(parent_category_id: params[:category_id]).pluck(:id))
|
||||
if params[:include_subcategories] == "true"
|
||||
category_ids =
|
||||
category_ids.concat(Category.where(parent_category_id: params[:category_id]).pluck(:id))
|
||||
end
|
||||
|
||||
scope = Topic.where(category_id: category_ids)
|
||||
|
@ -1012,16 +1045,15 @@ class TopicsController < ApplicationController
|
|||
|
||||
if params[:topic_ids].present?
|
||||
unless Array === params[:topic_ids]
|
||||
raise Discourse::InvalidParameters.new(
|
||||
"Expecting topic_ids to contain a list of topic ids"
|
||||
)
|
||||
raise Discourse::InvalidParameters.new("Expecting topic_ids to contain a list of topic ids")
|
||||
end
|
||||
|
||||
topic_ids = params[:topic_ids].map { |t| t.to_i }
|
||||
topic_scope = topic_scope.where(id: topic_ids)
|
||||
end
|
||||
|
||||
dismissed_topic_ids = TopicsBulkAction.new(current_user, topic_scope.pluck(:id), type: "dismiss_topics").perform!
|
||||
dismissed_topic_ids =
|
||||
TopicsBulkAction.new(current_user, topic_scope.pluck(:id), type: "dismiss_topics").perform!
|
||||
TopicTrackingState.publish_dismiss_new(current_user.id, topic_ids: dismissed_topic_ids)
|
||||
|
||||
render body: nil
|
||||
|
@ -1034,7 +1066,8 @@ class TopicsController < ApplicationController
|
|||
guardian.ensure_can_convert_topic!(topic)
|
||||
|
||||
if params[:type] == "public"
|
||||
converted_topic = topic.convert_to_public_topic(current_user, category_id: params[:category_id])
|
||||
converted_topic =
|
||||
topic.convert_to_public_topic(current_user, category_id: params[:category_id])
|
||||
else
|
||||
converted_topic = topic.convert_to_private_message(current_user)
|
||||
end
|
||||
|
@ -1065,11 +1098,7 @@ class TopicsController < ApplicationController
|
|||
|
||||
time = enabled && params[:enabled_until].present? ? params[:enabled_until] : nil
|
||||
|
||||
topic.set_or_create_timer(
|
||||
slow_mode_type,
|
||||
time,
|
||||
by_user: timer&.user
|
||||
)
|
||||
topic.set_or_create_timer(slow_mode_type, time, by_user: timer&.user)
|
||||
|
||||
head :ok
|
||||
end
|
||||
|
@ -1077,16 +1106,12 @@ class TopicsController < ApplicationController
|
|||
private
|
||||
|
||||
def topic_params
|
||||
params.permit(
|
||||
:topic_id,
|
||||
:topic_time,
|
||||
timings: {}
|
||||
)
|
||||
params.permit(:topic_id, :topic_time, timings: {})
|
||||
end
|
||||
|
||||
def fetch_topic_view(options)
|
||||
if (username_filters = params[:username_filters]).present?
|
||||
options[:username_filters] = username_filters.split(',')
|
||||
options[:username_filters] = username_filters.split(",")
|
||||
end
|
||||
|
||||
@topic_view = TopicView.new(params[:topic_id], current_user, options)
|
||||
|
@ -1132,7 +1157,7 @@ class TopicsController < ApplicationController
|
|||
url << ".json" if request.format.json?
|
||||
|
||||
opts.each do |k, v|
|
||||
s = url.include?('?') ? '&' : '?'
|
||||
s = url.include?("?") ? "&" : "?"
|
||||
url << "#{s}#{k}=#{v}"
|
||||
end
|
||||
|
||||
|
@ -1152,8 +1177,8 @@ class TopicsController < ApplicationController
|
|||
current_user: current_user,
|
||||
topic_id: @topic_view.topic.id,
|
||||
post_number: @topic_view.current_post_number,
|
||||
username: request['u'],
|
||||
ip_address: request.remote_ip
|
||||
username: request["u"],
|
||||
ip_address: request.remote_ip,
|
||||
}
|
||||
# defer this way so we do not capture the whole controller
|
||||
# in the closure
|
||||
|
@ -1181,32 +1206,33 @@ class TopicsController < ApplicationController
|
|||
end
|
||||
|
||||
def perform_show_response
|
||||
|
||||
if request.head?
|
||||
head :ok
|
||||
return
|
||||
end
|
||||
|
||||
topic_view_serializer = TopicViewSerializer.new(
|
||||
topic_view_serializer =
|
||||
TopicViewSerializer.new(
|
||||
@topic_view,
|
||||
scope: guardian,
|
||||
root: false,
|
||||
include_raw: !!params[:include_raw],
|
||||
exclude_suggested_and_related: !!params[:replies_to_post_number] || !!params[:filter_upwards_post_id] || !!params[:filter_top_level_replies]
|
||||
exclude_suggested_and_related:
|
||||
!!params[:replies_to_post_number] || !!params[:filter_upwards_post_id] ||
|
||||
!!params[:filter_top_level_replies],
|
||||
)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@tags = SiteSetting.tagging_enabled ? @topic_view.topic.tags : []
|
||||
@breadcrumbs = helpers.categories_breadcrumb(@topic_view.topic) || []
|
||||
@description_meta = @topic_view.topic.excerpt.present? ? @topic_view.topic.excerpt : @topic_view.summary
|
||||
@description_meta =
|
||||
@topic_view.topic.excerpt.present? ? @topic_view.topic.excerpt : @topic_view.summary
|
||||
store_preloaded("topic_#{@topic_view.topic.id}", MultiJson.dump(topic_view_serializer))
|
||||
render :show
|
||||
end
|
||||
|
||||
format.json do
|
||||
render_json_dump(topic_view_serializer)
|
||||
end
|
||||
format.json { render_json_dump(topic_view_serializer) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1221,12 +1247,15 @@ class TopicsController < ApplicationController
|
|||
def move_posts_to_destination(topic)
|
||||
args = {}
|
||||
args[:title] = params[:title] if params[:title].present?
|
||||
args[:destination_topic_id] = params[:destination_topic_id].to_i if params[:destination_topic_id].present?
|
||||
args[:destination_topic_id] = params[:destination_topic_id].to_i if params[
|
||||
:destination_topic_id
|
||||
].present?
|
||||
args[:tags] = params[:tags] if params[:tags].present?
|
||||
|
||||
if params[:archetype].present?
|
||||
args[:archetype] = params[:archetype]
|
||||
args[:participants] = params[:participants] if params[:participants].present? && params[:archetype] == "private_message"
|
||||
args[:participants] = params[:participants] if params[:participants].present? &&
|
||||
params[:archetype] == "private_message"
|
||||
else
|
||||
args[:category_id] = params[:category_id].to_i if params[:category_id].present?
|
||||
end
|
||||
|
@ -1235,7 +1264,7 @@ class TopicsController < ApplicationController
|
|||
end
|
||||
|
||||
def check_for_status_presence(key, attr)
|
||||
invalid_param(key) unless %w(pinned pinned_globally visible closed archived).include?(attr)
|
||||
invalid_param(key) unless %w[pinned pinned_globally visible closed archived].include?(attr)
|
||||
end
|
||||
|
||||
def invalid_param(key)
|
||||
|
@ -1264,7 +1293,11 @@ class TopicsController < ApplicationController
|
|||
topic_query.options[:limit] = false
|
||||
topics = topic_query.filter_private_messages_unread(current_user, filter)
|
||||
else
|
||||
topics = TopicQuery.unread_filter(topic_query.joined_topic_user, whisperer: guardian.is_whisperer?).listable_topics
|
||||
topics =
|
||||
TopicQuery.unread_filter(
|
||||
topic_query.joined_topic_user,
|
||||
whisperer: guardian.is_whisperer?,
|
||||
).listable_topics
|
||||
topics = TopicQuery.tracked_filter(topics, current_user.id) if params[:tracked].to_s == "true"
|
||||
|
||||
if params[:category_id]
|
||||
|
@ -1274,7 +1307,7 @@ class TopicsController < ApplicationController
|
|||
category_id = :category_id
|
||||
SQL
|
||||
else
|
||||
topics = topics.where('category_id = ?', params[:category_id])
|
||||
topics = topics.where("category_id = ?", params[:category_id])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,13 +5,18 @@ require "mini_mime"
|
|||
class UploadsController < ApplicationController
|
||||
include ExternalUploadHelpers
|
||||
|
||||
requires_login except: [:show, :show_short, :_show_secure_deprecated, :show_secure]
|
||||
requires_login except: %i[show show_short _show_secure_deprecated show_secure]
|
||||
|
||||
skip_before_action :preload_json, :check_xhr, :redirect_to_login_if_required, only: [:show, :show_short, :_show_secure_deprecated, :show_secure]
|
||||
skip_before_action :preload_json,
|
||||
:check_xhr,
|
||||
:redirect_to_login_if_required,
|
||||
only: %i[show show_short _show_secure_deprecated show_secure]
|
||||
protect_from_forgery except: :show
|
||||
|
||||
before_action :is_asset_path, :apply_cdn_headers, only: [:show, :show_short, :_show_secure_deprecated, :show_secure]
|
||||
before_action :external_store_check, only: [:_show_secure_deprecated, :show_secure]
|
||||
before_action :is_asset_path,
|
||||
:apply_cdn_headers,
|
||||
only: %i[show show_short _show_secure_deprecated show_secure]
|
||||
before_action :external_store_check, only: %i[_show_secure_deprecated show_secure]
|
||||
|
||||
SECURE_REDIRECT_GRACE_SECONDS = 5
|
||||
|
||||
|
@ -20,13 +25,16 @@ class UploadsController < ApplicationController
|
|||
me = current_user
|
||||
|
||||
params.permit(:type, :upload_type)
|
||||
if params[:type].blank? && params[:upload_type].blank?
|
||||
raise Discourse::InvalidParameters
|
||||
end
|
||||
raise Discourse::InvalidParameters if params[:type].blank? && params[:upload_type].blank?
|
||||
# 50 characters ought to be enough for the upload type
|
||||
type = (params[:upload_type].presence || params[:type].presence).parameterize(separator: "_")[0..50]
|
||||
type =
|
||||
(params[:upload_type].presence || params[:type].presence).parameterize(separator: "_")[0..50]
|
||||
|
||||
if type == "avatar" && !me.admin? && (SiteSetting.discourse_connect_overrides_avatar || !TrustLevelAndStaffAndDisabledSetting.matches?(SiteSetting.allow_uploaded_avatars, me))
|
||||
if type == "avatar" && !me.admin? &&
|
||||
(
|
||||
SiteSetting.discourse_connect_overrides_avatar ||
|
||||
!TrustLevelAndStaffAndDisabledSetting.matches?(SiteSetting.allow_uploaded_avatars, me)
|
||||
)
|
||||
return render json: failed_json, status: 422
|
||||
end
|
||||
|
||||
|
@ -42,7 +50,8 @@ class UploadsController < ApplicationController
|
|||
# longer term we may change this
|
||||
hijack do
|
||||
begin
|
||||
info = UploadsController.create_upload(
|
||||
info =
|
||||
UploadsController.create_upload(
|
||||
current_user: me,
|
||||
file: file,
|
||||
url: url,
|
||||
|
@ -51,7 +60,7 @@ class UploadsController < ApplicationController
|
|||
for_site_setting: for_site_setting,
|
||||
pasted: pasted,
|
||||
is_api: is_api,
|
||||
retain_hours: retain_hours
|
||||
retain_hours: retain_hours,
|
||||
)
|
||||
rescue => e
|
||||
render json: failed_json.merge(message: e.message&.split("\n")&.first), status: 422
|
||||
|
@ -66,12 +75,10 @@ class UploadsController < ApplicationController
|
|||
uploads = []
|
||||
|
||||
if (params[:short_urls] && params[:short_urls].length > 0)
|
||||
PrettyText::Helpers.lookup_upload_urls(params[:short_urls]).each do |short_url, paths|
|
||||
uploads << {
|
||||
short_url: short_url,
|
||||
url: paths[:url],
|
||||
short_path: paths[:short_path]
|
||||
}
|
||||
PrettyText::Helpers
|
||||
.lookup_upload_urls(params[:short_urls])
|
||||
.each do |short_url, paths|
|
||||
uploads << { short_url: short_url, url: paths[:url], short_path: paths[:short_path] }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -87,7 +94,9 @@ class UploadsController < ApplicationController
|
|||
RailsMultisite::ConnectionManagement.with_connection(params[:site]) do |db|
|
||||
return render_404 if SiteSetting.prevent_anons_from_downloading_files && current_user.nil?
|
||||
|
||||
if upload = Upload.find_by(sha1: params[:sha]) || Upload.find_by(id: params[:id], url: request.env["PATH_INFO"])
|
||||
if upload =
|
||||
Upload.find_by(sha1: params[:sha]) ||
|
||||
Upload.find_by(id: params[:id], url: request.env["PATH_INFO"])
|
||||
unless Discourse.store.internal?
|
||||
local_store = FileStore::LocalStore.new
|
||||
return render_404 unless local_store.has_been_uploaded?(upload.url)
|
||||
|
@ -104,21 +113,18 @@ class UploadsController < ApplicationController
|
|||
# do not serve uploads requested via XHR to prevent XSS
|
||||
return xhr_not_allowed if request.xhr?
|
||||
|
||||
if SiteSetting.prevent_anons_from_downloading_files && current_user.nil?
|
||||
return render_404
|
||||
end
|
||||
return render_404 if SiteSetting.prevent_anons_from_downloading_files && current_user.nil?
|
||||
|
||||
sha1 = Upload.sha1_from_base62_encoded(params[:base62])
|
||||
|
||||
if upload = Upload.find_by(sha1: sha1)
|
||||
if upload.secure? && SiteSetting.secure_uploads?
|
||||
return handle_secure_upload_request(upload)
|
||||
end
|
||||
return handle_secure_upload_request(upload) if upload.secure? && SiteSetting.secure_uploads?
|
||||
|
||||
if Discourse.store.internal?
|
||||
send_file_local_upload(upload)
|
||||
else
|
||||
redirect_to Discourse.store.url_for(upload, force_download: force_download?), allow_other_host: true
|
||||
redirect_to Discourse.store.url_for(upload, force_download: force_download?),
|
||||
allow_other_host: true
|
||||
end
|
||||
else
|
||||
render_404
|
||||
|
@ -156,7 +162,8 @@ class UploadsController < ApplicationController
|
|||
# private, so we don't want to go to the CDN url just yet otherwise we
|
||||
# will get a 403. if the upload is not secure we assume the ACL is public
|
||||
signed_secure_url = Discourse.store.signed_url_for_path(path_with_ext)
|
||||
redirect_to upload.secure? ? signed_secure_url : Discourse.store.cdn_url(upload.url), allow_other_host: true
|
||||
redirect_to upload.secure? ? signed_secure_url : Discourse.store.cdn_url(upload.url),
|
||||
allow_other_host: true
|
||||
end
|
||||
|
||||
def handle_secure_upload_request(upload, path_with_ext = nil)
|
||||
|
@ -167,20 +174,25 @@ class UploadsController < ApplicationController
|
|||
end
|
||||
|
||||
# defaults to public: false, so only cached by the client browser
|
||||
cache_seconds = SiteSetting.s3_presigned_get_url_expires_after_seconds - SECURE_REDIRECT_GRACE_SECONDS
|
||||
cache_seconds =
|
||||
SiteSetting.s3_presigned_get_url_expires_after_seconds - SECURE_REDIRECT_GRACE_SECONDS
|
||||
expires_in cache_seconds.seconds
|
||||
|
||||
# url_for figures out the full URL, handling multisite DBs,
|
||||
# and will return a presigned URL for the upload
|
||||
if path_with_ext.blank?
|
||||
return redirect_to Discourse.store.url_for(upload, force_download: force_download?), allow_other_host: true
|
||||
return(
|
||||
redirect_to Discourse.store.url_for(upload, force_download: force_download?),
|
||||
allow_other_host: true
|
||||
)
|
||||
end
|
||||
|
||||
redirect_to Discourse.store.signed_url_for_path(
|
||||
path_with_ext,
|
||||
expires_in: SiteSetting.s3_presigned_get_url_expires_after_seconds,
|
||||
force_download: force_download?
|
||||
), allow_other_host: true
|
||||
force_download: force_download?,
|
||||
),
|
||||
allow_other_host: true
|
||||
end
|
||||
|
||||
def metadata
|
||||
|
@ -192,7 +204,7 @@ class UploadsController < ApplicationController
|
|||
original_filename: upload.original_filename,
|
||||
width: upload.width,
|
||||
height: upload.height,
|
||||
human_filesize: upload.human_filesize
|
||||
human_filesize: upload.human_filesize,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -207,16 +219,17 @@ class UploadsController < ApplicationController
|
|||
end
|
||||
|
||||
def validate_file_size(file_name:, file_size:)
|
||||
if file_size.zero?
|
||||
raise ExternalUploadValidationError.new(I18n.t("upload.size_zero_failure"))
|
||||
end
|
||||
raise ExternalUploadValidationError.new(I18n.t("upload.size_zero_failure")) if file_size.zero?
|
||||
|
||||
if file_size_too_big?(file_name, file_size)
|
||||
raise ExternalUploadValidationError.new(
|
||||
I18n.t(
|
||||
"upload.attachments.too_large_humanized",
|
||||
max_size: ActiveSupport::NumberHelper.number_to_human_size(SiteSetting.max_attachment_size_kb.kilobytes)
|
||||
)
|
||||
max_size:
|
||||
ActiveSupport::NumberHelper.number_to_human_size(
|
||||
SiteSetting.max_attachment_size_kb.kilobytes,
|
||||
),
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -236,7 +249,8 @@ class UploadsController < ApplicationController
|
|||
serialized ||= (data || {}).as_json
|
||||
end
|
||||
|
||||
def self.create_upload(current_user:,
|
||||
def self.create_upload(
|
||||
current_user:,
|
||||
file:,
|
||||
url:,
|
||||
type:,
|
||||
|
@ -244,17 +258,25 @@ class UploadsController < ApplicationController
|
|||
for_site_setting:,
|
||||
pasted:,
|
||||
is_api:,
|
||||
retain_hours:)
|
||||
|
||||
retain_hours:
|
||||
)
|
||||
if file.nil?
|
||||
if url.present? && is_api
|
||||
maximum_upload_size = [SiteSetting.max_image_size_kb, SiteSetting.max_attachment_size_kb].max.kilobytes
|
||||
tempfile = FileHelper.download(
|
||||
maximum_upload_size = [
|
||||
SiteSetting.max_image_size_kb,
|
||||
SiteSetting.max_attachment_size_kb,
|
||||
].max.kilobytes
|
||||
tempfile =
|
||||
begin
|
||||
FileHelper.download(
|
||||
url,
|
||||
follow_redirect: true,
|
||||
max_file_size: maximum_upload_size,
|
||||
tmp_file_name: "discourse-upload-#{type}"
|
||||
) rescue nil
|
||||
tmp_file_name: "discourse-upload-#{type}",
|
||||
)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
filename = File.basename(URI.parse(url).path)
|
||||
end
|
||||
else
|
||||
|
@ -288,13 +310,14 @@ class UploadsController < ApplicationController
|
|||
# as they may be further reduced in size by UploadCreator (at this point
|
||||
# they may have already been reduced in size by preprocessors)
|
||||
def file_size_too_big?(file_name, file_size)
|
||||
!FileHelper.is_supported_image?(file_name) && file_size >= SiteSetting.max_attachment_size_kb.kilobytes
|
||||
!FileHelper.is_supported_image?(file_name) &&
|
||||
file_size >= SiteSetting.max_attachment_size_kb.kilobytes
|
||||
end
|
||||
|
||||
def send_file_local_upload(upload)
|
||||
opts = {
|
||||
filename: upload.original_filename,
|
||||
content_type: MiniMime.lookup_by_filename(upload.original_filename)&.content_type
|
||||
content_type: MiniMime.lookup_by_filename(upload.original_filename)&.content_type,
|
||||
}
|
||||
|
||||
if !FileHelper.is_inline_image?(upload.original_filename)
|
||||
|
@ -313,7 +336,11 @@ class UploadsController < ApplicationController
|
|||
begin
|
||||
yield
|
||||
rescue Aws::S3::Errors::ServiceError => err
|
||||
message = debug_upload_error(err, I18n.t("upload.create_multipart_failure", additional_detail: err.message))
|
||||
message =
|
||||
debug_upload_error(
|
||||
err,
|
||||
I18n.t("upload.create_multipart_failure", additional_detail: err.message),
|
||||
)
|
||||
raise ExternalUploadHelpers::ExternalUploadValidationError.new(message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,11 @@ class UserActionsController < ApplicationController
|
|||
def index
|
||||
user_actions_params.require(:username)
|
||||
|
||||
user = fetch_user_from_params(include_inactive: current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts))
|
||||
user =
|
||||
fetch_user_from_params(
|
||||
include_inactive:
|
||||
current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts),
|
||||
)
|
||||
offset = [0, user_actions_params[:offset].to_i].max
|
||||
action_types = (user_actions_params[:filter] || "").split(",").map(&:to_i)
|
||||
limit = user_actions_params.fetch(:limit, 30).to_i
|
||||
|
@ -20,11 +24,11 @@ class UserActionsController < ApplicationController
|
|||
action_types: action_types,
|
||||
guardian: guardian,
|
||||
ignore_private_messages: params[:filter].blank?,
|
||||
acting_username: params[:acting_username]
|
||||
acting_username: params[:acting_username],
|
||||
}
|
||||
|
||||
stream = UserAction.stream(opts).to_a
|
||||
render_serialized(stream, UserActionSerializer, root: 'user_actions')
|
||||
render_serialized(stream, UserActionSerializer, root: "user_actions")
|
||||
end
|
||||
|
||||
def show
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UserApiKeysController < ApplicationController
|
||||
layout "no_ember"
|
||||
|
||||
layout 'no_ember'
|
||||
|
||||
requires_login only: [:create, :create_otp, :revoke, :undo_revoke]
|
||||
skip_before_action :redirect_to_login_if_required, only: [:new, :otp]
|
||||
requires_login only: %i[create create_otp revoke undo_revoke]
|
||||
skip_before_action :redirect_to_login_if_required, only: %i[new otp]
|
||||
skip_before_action :check_xhr, :preload_json
|
||||
|
||||
AUTH_API_VERSION ||= 4
|
||||
|
||||
def new
|
||||
|
||||
if request.head?
|
||||
head :ok, auth_api_version: AUTH_API_VERSION
|
||||
return
|
||||
|
@ -24,9 +22,9 @@ class UserApiKeysController < ApplicationController
|
|||
cookies[:destination_url] = request.fullpath
|
||||
|
||||
if SiteSetting.enable_discourse_connect?
|
||||
redirect_to path('/session/sso')
|
||||
redirect_to path("/session/sso")
|
||||
else
|
||||
redirect_to path('/login')
|
||||
redirect_to path("/login")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
@ -44,13 +42,11 @@ class UserApiKeysController < ApplicationController
|
|||
@push_url = params[:push_url]
|
||||
@localized_scopes = params[:scopes].split(",").map { |s| I18n.t("user_api_key.scopes.#{s}") }
|
||||
@scopes = params[:scopes]
|
||||
|
||||
rescue Discourse::InvalidAccess
|
||||
@generic_error = true
|
||||
end
|
||||
|
||||
def create
|
||||
|
||||
require_params
|
||||
|
||||
if params.key?(:auth_redirect)
|
||||
|
@ -66,12 +62,13 @@ class UserApiKeysController < ApplicationController
|
|||
# destroy any old keys we had
|
||||
UserApiKey.where(user_id: current_user.id, client_id: params[:client_id]).destroy_all
|
||||
|
||||
key = UserApiKey.create!(
|
||||
key =
|
||||
UserApiKey.create!(
|
||||
application_name: @application_name,
|
||||
client_id: params[:client_id],
|
||||
user_id: current_user.id,
|
||||
push_url: params[:push_url],
|
||||
scopes: scopes.map { |name| UserApiKeyScope.new(name: name) }
|
||||
scopes: scopes.map { |name| UserApiKeyScope.new(name: name) },
|
||||
)
|
||||
|
||||
# we keep the payload short so it encrypts easily with public key
|
||||
|
@ -80,7 +77,7 @@ class UserApiKeysController < ApplicationController
|
|||
key: key.key,
|
||||
nonce: params[:nonce],
|
||||
push: key.has_push?,
|
||||
api: AUTH_API_VERSION
|
||||
api: AUTH_API_VERSION,
|
||||
}.to_json
|
||||
|
||||
public_key = OpenSSL::PKey::RSA.new(params[:public_key])
|
||||
|
@ -94,8 +91,10 @@ class UserApiKeysController < ApplicationController
|
|||
if params[:auth_redirect]
|
||||
uri = URI.parse(params[:auth_redirect])
|
||||
query_attributes = [uri.query, "payload=#{CGI.escape(@payload)}"]
|
||||
query_attributes << "oneTimePassword=#{CGI.escape(otp_payload)}" if scopes.include?("one_time_password")
|
||||
uri.query = query_attributes.compact.join('&')
|
||||
if scopes.include?("one_time_password")
|
||||
query_attributes << "oneTimePassword=#{CGI.escape(otp_payload)}"
|
||||
end
|
||||
uri.query = query_attributes.compact.join("&")
|
||||
|
||||
redirect_to(uri.to_s, allow_other_host: true)
|
||||
else
|
||||
|
@ -116,9 +115,9 @@ class UserApiKeysController < ApplicationController
|
|||
cookies[:destination_url] = request.fullpath
|
||||
|
||||
if SiteSetting.enable_discourse_connect?
|
||||
redirect_to path('/session/sso')
|
||||
redirect_to path("/session/sso")
|
||||
else
|
||||
redirect_to path('/login')
|
||||
redirect_to path("/login")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
@ -144,7 +143,7 @@ class UserApiKeysController < ApplicationController
|
|||
def revoke
|
||||
revoke_key = find_key if params[:id]
|
||||
|
||||
if current_key = request.env['HTTP_USER_API_KEY']
|
||||
if current_key = request.env["HTTP_USER_API_KEY"]
|
||||
request_key = UserApiKey.with_key(current_key).first
|
||||
revoke_key ||= request_key
|
||||
end
|
||||
|
@ -168,13 +167,7 @@ class UserApiKeysController < ApplicationController
|
|||
end
|
||||
|
||||
def require_params
|
||||
[
|
||||
:public_key,
|
||||
:nonce,
|
||||
:scopes,
|
||||
:client_id,
|
||||
:application_name
|
||||
].each { |p| params.require(p) }
|
||||
%i[public_key nonce scopes client_id application_name].each { |p| params.require(p) }
|
||||
end
|
||||
|
||||
def validate_params
|
||||
|
@ -186,11 +179,7 @@ class UserApiKeysController < ApplicationController
|
|||
end
|
||||
|
||||
def require_params_otp
|
||||
[
|
||||
:public_key,
|
||||
:auth_redirect,
|
||||
:application_name
|
||||
].each { |p| params.require(p) }
|
||||
%i[public_key auth_redirect application_name].each { |p| params.require(p) }
|
||||
end
|
||||
|
||||
def meets_tl?
|
||||
|
@ -198,7 +187,9 @@ class UserApiKeysController < ApplicationController
|
|||
end
|
||||
|
||||
def one_time_password(public_key, username)
|
||||
raise Discourse::InvalidAccess unless UserApiKey.allowed_scopes.superset?(Set.new(["one_time_password"]))
|
||||
unless UserApiKey.allowed_scopes.superset?(Set.new(["one_time_password"]))
|
||||
raise Discourse::InvalidAccess
|
||||
end
|
||||
|
||||
otp = SecureRandom.hex
|
||||
Discourse.redis.setex "otp_#{otp}", 10.minutes, username
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UserAvatarsController < ApplicationController
|
||||
skip_before_action :preload_json,
|
||||
:redirect_to_login_if_required,
|
||||
:check_xhr,
|
||||
:verify_authenticity_token,
|
||||
only: %i[show show_letter show_proxy_letter]
|
||||
|
||||
skip_before_action :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :show_letter, :show_proxy_letter]
|
||||
|
||||
before_action :apply_cdn_headers, only: [:show, :show_letter, :show_proxy_letter]
|
||||
before_action :apply_cdn_headers, only: %i[show show_letter show_proxy_letter]
|
||||
|
||||
def refresh_gravatar
|
||||
user = User.find_by(username_lower: params[:username].downcase)
|
||||
|
@ -15,16 +18,15 @@ class UserAvatarsController < ApplicationController
|
|||
user.create_user_avatar(user_id: user.id) unless user.user_avatar
|
||||
user.user_avatar.update_gravatar!
|
||||
|
||||
gravatar = if user.user_avatar.gravatar_upload_id
|
||||
gravatar =
|
||||
if user.user_avatar.gravatar_upload_id
|
||||
{
|
||||
gravatar_upload_id: user.user_avatar.gravatar_upload_id,
|
||||
gravatar_avatar_template: User.avatar_template(user.username, user.user_avatar.gravatar_upload_id)
|
||||
gravatar_avatar_template:
|
||||
User.avatar_template(user.username, user.user_avatar.gravatar_upload_id),
|
||||
}
|
||||
else
|
||||
{
|
||||
gravatar_upload_id: nil,
|
||||
gravatar_avatar_template: nil
|
||||
}
|
||||
{ gravatar_upload_id: nil, gravatar_avatar_template: nil }
|
||||
end
|
||||
|
||||
render json: gravatar
|
||||
|
@ -37,7 +39,7 @@ class UserAvatarsController < ApplicationController
|
|||
def show_proxy_letter
|
||||
is_asset_path
|
||||
|
||||
if SiteSetting.external_system_avatars_url !~ /^\/letter_avatar_proxy/
|
||||
if SiteSetting.external_system_avatars_url !~ %r{^/letter_avatar_proxy}
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
|
||||
|
@ -48,7 +50,10 @@ class UserAvatarsController < ApplicationController
|
|||
|
||||
hijack do
|
||||
begin
|
||||
proxy_avatar("https://avatars.discourse-cdn.com/#{params[:version]}/letter/#{params[:letter]}/#{params[:color]}/#{params[:size]}.png", Time.new(1990, 01, 01))
|
||||
proxy_avatar(
|
||||
"https://avatars.discourse-cdn.com/#{params[:version]}/letter/#{params[:letter]}/#{params[:color]}/#{params[:size]}.png",
|
||||
Time.new(1990, 01, 01),
|
||||
)
|
||||
rescue OpenURI::HTTPError
|
||||
render_blank
|
||||
end
|
||||
|
@ -81,16 +86,13 @@ class UserAvatarsController < ApplicationController
|
|||
|
||||
# we need multisite support to keep a single origin pull for CDNs
|
||||
RailsMultisite::ConnectionManagement.with_hostname(params[:hostname]) do
|
||||
hijack do
|
||||
show_in_site(params[:hostname])
|
||||
end
|
||||
hijack { show_in_site(params[:hostname]) }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def show_in_site(hostname)
|
||||
|
||||
username = params[:username].to_s
|
||||
return render_blank unless user = User.find_by(username_lower: username.downcase)
|
||||
|
||||
|
@ -99,9 +101,7 @@ class UserAvatarsController < ApplicationController
|
|||
version = (version || OptimizedImage::VERSION).to_i
|
||||
|
||||
# old versions simply get new avatar
|
||||
if version > OptimizedImage::VERSION
|
||||
return render_blank
|
||||
end
|
||||
return render_blank if version > OptimizedImage::VERSION
|
||||
|
||||
upload_id = upload_id.to_i
|
||||
return render_blank unless upload_id > 0
|
||||
|
@ -111,7 +111,13 @@ class UserAvatarsController < ApplicationController
|
|||
|
||||
if !Discourse.avatar_sizes.include?(size) && Discourse.store.external?
|
||||
closest = Discourse.avatar_sizes.to_a.min { |a, b| (size - a).abs <=> (size - b).abs }
|
||||
avatar_url = UserAvatar.local_avatar_url(hostname, user.encoded_username(lower: true), upload_id, closest)
|
||||
avatar_url =
|
||||
UserAvatar.local_avatar_url(
|
||||
hostname,
|
||||
user.encoded_username(lower: true),
|
||||
upload_id,
|
||||
closest,
|
||||
)
|
||||
return redirect_to cdn_path(avatar_url), allow_other_host: true
|
||||
end
|
||||
|
||||
|
@ -119,7 +125,13 @@ class UserAvatarsController < ApplicationController
|
|||
upload ||= user.uploaded_avatar if user.uploaded_avatar_id == upload_id
|
||||
|
||||
if user.uploaded_avatar && !upload
|
||||
avatar_url = UserAvatar.local_avatar_url(hostname, user.encoded_username(lower: true), user.uploaded_avatar_id, size)
|
||||
avatar_url =
|
||||
UserAvatar.local_avatar_url(
|
||||
hostname,
|
||||
user.encoded_username(lower: true),
|
||||
user.uploaded_avatar_id,
|
||||
size,
|
||||
)
|
||||
return redirect_to cdn_path(avatar_url), allow_other_host: true
|
||||
elsif upload && optimized = get_optimized_image(upload, size)
|
||||
if optimized.local?
|
||||
|
@ -151,10 +163,7 @@ class UserAvatarsController < ApplicationController
|
|||
|
||||
PROXY_PATH = Rails.root + "tmp/avatar_proxy"
|
||||
def proxy_avatar(url, last_modified)
|
||||
|
||||
if url[0..1] == "//"
|
||||
url = (SiteSetting.force_https ? "https:" : "http:") + url
|
||||
end
|
||||
url = (SiteSetting.force_https ? "https:" : "http:") + url if url[0..1] == "//"
|
||||
|
||||
sha = Digest::SHA1.hexdigest(url)
|
||||
filename = "#{sha}#{File.extname(url)}"
|
||||
|
@ -162,12 +171,13 @@ class UserAvatarsController < ApplicationController
|
|||
|
||||
unless File.exist? path
|
||||
FileUtils.mkdir_p PROXY_PATH
|
||||
tmp = FileHelper.download(
|
||||
tmp =
|
||||
FileHelper.download(
|
||||
url,
|
||||
max_file_size: max_file_size,
|
||||
tmp_file_name: filename,
|
||||
follow_redirect: true,
|
||||
read_timeout: 10
|
||||
read_timeout: 10,
|
||||
)
|
||||
|
||||
return render_blank if tmp.nil?
|
||||
|
@ -206,5 +216,4 @@ class UserAvatarsController < ApplicationController
|
|||
upload.get_optimized_image(size, size)
|
||||
# TODO decide if we want to detach here
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,11 +6,18 @@ class UserBadgesController < ApplicationController
|
|||
before_action :ensure_badges_enabled
|
||||
|
||||
def index
|
||||
params.permit [:granted_before, :offset, :username]
|
||||
params.permit %i[granted_before offset username]
|
||||
|
||||
badge = fetch_badge_from_params
|
||||
user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(MAX_BADGES)
|
||||
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic, user: [:primary_group, :flair_group])
|
||||
user_badges = badge.user_badges.order("granted_at DESC, id DESC").limit(MAX_BADGES)
|
||||
user_badges =
|
||||
user_badges.includes(
|
||||
:user,
|
||||
:granted_by,
|
||||
badge: :badge_type,
|
||||
post: :topic,
|
||||
user: %i[primary_group flair_group],
|
||||
)
|
||||
|
||||
grant_count = nil
|
||||
|
||||
|
@ -26,31 +33,38 @@ class UserBadgesController < ApplicationController
|
|||
|
||||
user_badges_topic_ids = user_badges.map { |user_badge| user_badge.post&.topic_id }.compact
|
||||
|
||||
user_badges = UserBadges.new(user_badges: user_badges,
|
||||
user_badges =
|
||||
UserBadges.new(
|
||||
user_badges: user_badges,
|
||||
username: params[:username],
|
||||
grant_count: grant_count)
|
||||
grant_count: grant_count,
|
||||
)
|
||||
|
||||
render_serialized(
|
||||
user_badges,
|
||||
UserBadgesSerializer,
|
||||
root: :user_badge_info,
|
||||
include_long_description: true,
|
||||
allowed_user_badge_topic_ids: guardian.can_see_topic_ids(topic_ids: user_badges_topic_ids)
|
||||
allowed_user_badge_topic_ids: guardian.can_see_topic_ids(topic_ids: user_badges_topic_ids),
|
||||
)
|
||||
end
|
||||
|
||||
def username
|
||||
params.permit [:grouped]
|
||||
|
||||
user = fetch_user_from_params(include_inactive: current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts))
|
||||
user =
|
||||
fetch_user_from_params(
|
||||
include_inactive:
|
||||
current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts),
|
||||
)
|
||||
raise Discourse::NotFound unless guardian.can_see_profile?(user)
|
||||
user_badges = user.user_badges
|
||||
|
||||
if params[:grouped]
|
||||
user_badges = user_badges.group(:badge_id).select_for_grouping
|
||||
end
|
||||
user_badges = user_badges.group(:badge_id).select_for_grouping if params[:grouped]
|
||||
|
||||
user_badges = user_badges.includes(badge: [:badge_grouping, :badge_type, :image_upload])
|
||||
user_badges =
|
||||
user_badges
|
||||
.includes(badge: %i[badge_grouping badge_type image_upload])
|
||||
.includes(post: :topic)
|
||||
.includes(:granted_by)
|
||||
|
||||
|
@ -68,16 +82,17 @@ class UserBadgesController < ApplicationController
|
|||
params.require(:username)
|
||||
user = fetch_user_from_params
|
||||
|
||||
unless can_assign_badge_to_user?(user)
|
||||
return render json: failed_json, status: 403
|
||||
end
|
||||
return render json: failed_json, status: 403 unless can_assign_badge_to_user?(user)
|
||||
|
||||
badge = fetch_badge_from_params
|
||||
post_id = nil
|
||||
|
||||
if params[:reason].present?
|
||||
unless is_badge_reason_valid? params[:reason]
|
||||
return render json: failed_json.merge(message: I18n.t('invalid_grant_badge_reason_link')), status: 400
|
||||
return(
|
||||
render json: failed_json.merge(message: I18n.t("invalid_grant_badge_reason_link")),
|
||||
status: 400
|
||||
)
|
||||
end
|
||||
|
||||
if route = Discourse.route_for(params[:reason])
|
||||
|
@ -112,17 +127,17 @@ class UserBadgesController < ApplicationController
|
|||
user_badge = UserBadge.find(params[:user_badge_id])
|
||||
user_badges = user_badge.user.user_badges
|
||||
|
||||
unless can_favorite_badge?(user_badge)
|
||||
return render json: failed_json, status: 403
|
||||
end
|
||||
return render json: failed_json, status: 403 unless can_favorite_badge?(user_badge)
|
||||
|
||||
if !user_badge.is_favorite && user_badges.select(:badge_id).distinct.where(is_favorite: true).count >= SiteSetting.max_favorite_badges
|
||||
if !user_badge.is_favorite &&
|
||||
user_badges.select(:badge_id).distinct.where(is_favorite: true).count >=
|
||||
SiteSetting.max_favorite_badges
|
||||
return render json: failed_json, status: 400
|
||||
end
|
||||
|
||||
UserBadge
|
||||
.where(user_id: user_badge.user_id, badge_id: user_badge.badge_id)
|
||||
.update_all(is_favorite: !user_badge.is_favorite)
|
||||
UserBadge.where(user_id: user_badge.user_id, badge_id: user_badge.badge_id).update_all(
|
||||
is_favorite: !user_badge.is_favorite,
|
||||
)
|
||||
UserBadge.update_featured_ranks!(user_badge.user_id)
|
||||
end
|
||||
|
||||
|
@ -159,6 +174,6 @@ class UserBadgesController < ApplicationController
|
|||
|
||||
def is_badge_reason_valid?(reason)
|
||||
route = Discourse.route_for(reason)
|
||||
route && (route[:controller] == 'posts' || route[:controller] == 'topics')
|
||||
route && (route[:controller] == "posts" || route[:controller] == "topics")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class Users::AssociateAccountsController < ApplicationController
|
|||
token: params[:token],
|
||||
provider_name: auth_hash.provider,
|
||||
account_description: account_description,
|
||||
existing_account_description: existing_account_description
|
||||
existing_account_description: existing_account_description,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -33,7 +33,8 @@ class Users::AssociateAccountsController < ApplicationController
|
|||
private
|
||||
|
||||
def auth_hash
|
||||
@auth_hash ||= begin
|
||||
@auth_hash ||=
|
||||
begin
|
||||
token = params[:token]
|
||||
json = secure_session[self.class.key(token)]
|
||||
raise Discourse::NotFound if json.nil?
|
||||
|
@ -45,8 +46,10 @@ class Users::AssociateAccountsController < ApplicationController
|
|||
def authenticator
|
||||
provider_name = auth_hash.provider
|
||||
authenticator = Discourse.enabled_authenticators.find { |a| a.name == provider_name }
|
||||
raise Discourse::InvalidAccess.new(I18n.t('authenticator_not_found')) if authenticator.nil?
|
||||
raise Discourse::InvalidAccess.new(I18n.t('authenticator_no_connect')) if !authenticator.can_connect_existing_user?
|
||||
raise Discourse::InvalidAccess.new(I18n.t("authenticator_not_found")) if authenticator.nil?
|
||||
if !authenticator.can_connect_existing_user?
|
||||
raise Discourse::InvalidAccess.new(I18n.t("authenticator_no_connect"))
|
||||
end
|
||||
authenticator
|
||||
end
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Users::OmniauthCallbacksController < ApplicationController
|
||||
|
||||
skip_before_action :redirect_to_login_if_required
|
||||
|
||||
layout 'no_ember'
|
||||
layout "no_ember"
|
||||
|
||||
# need to be able to call this
|
||||
skip_before_action :check_xhr
|
||||
|
@ -40,7 +39,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
DiscourseEvent.trigger(:after_auth, authenticator, @auth_result, session, cookies, request)
|
||||
end
|
||||
|
||||
preferred_origin = request.env['omniauth.origin']
|
||||
preferred_origin = request.env["omniauth.origin"]
|
||||
|
||||
if session[:destination_url].present?
|
||||
preferred_origin = session[:destination_url]
|
||||
|
@ -53,7 +52,8 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
end
|
||||
|
||||
if preferred_origin.present?
|
||||
parsed = begin
|
||||
parsed =
|
||||
begin
|
||||
URI.parse(preferred_origin)
|
||||
rescue URI::Error
|
||||
end
|
||||
|
@ -64,9 +64,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
if @origin.blank?
|
||||
@origin = Discourse.base_path("/")
|
||||
end
|
||||
@origin = Discourse.base_path("/") if @origin.blank?
|
||||
|
||||
@auth_result.destination_url = @origin
|
||||
@auth_result.authenticator_name = authenticator.name
|
||||
|
@ -86,11 +84,8 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
client_hash[:associate_url] = persist_auth_token(auth)
|
||||
end
|
||||
|
||||
cookies['_bypass_cache'] = true
|
||||
cookies[:authentication_data] = {
|
||||
value: client_hash.to_json,
|
||||
path: Discourse.base_path("/")
|
||||
}
|
||||
cookies["_bypass_cache"] = true
|
||||
cookies[:authentication_data] = { value: client_hash.to_json, path: Discourse.base_path("/") }
|
||||
redirect_to @origin
|
||||
end
|
||||
|
||||
|
@ -108,24 +103,24 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
|
||||
flash[:error] = I18n.t(
|
||||
"login.omniauth_error.#{error_key}",
|
||||
default: I18n.t("login.omniauth_error.generic")
|
||||
default: I18n.t("login.omniauth_error.generic"),
|
||||
).html_safe
|
||||
|
||||
render 'failure'
|
||||
render "failure"
|
||||
end
|
||||
|
||||
def self.find_authenticator(name)
|
||||
Discourse.enabled_authenticators.each do |authenticator|
|
||||
return authenticator if authenticator.name == name
|
||||
end
|
||||
raise Discourse::InvalidAccess.new(I18n.t('authenticator_not_found'))
|
||||
raise Discourse::InvalidAccess.new(I18n.t("authenticator_not_found"))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def render_auth_result_failure
|
||||
flash[:error] = @auth_result.failed_reason.html_safe
|
||||
render 'failure'
|
||||
render "failure"
|
||||
end
|
||||
|
||||
def complete_response_data
|
||||
|
@ -160,13 +155,16 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
user.update!(password: SecureRandom.hex)
|
||||
|
||||
# Ensure there is an active email token
|
||||
if !EmailToken.where(email: user.email, confirmed: true).exists? && !user.email_tokens.active.where(email: user.email).exists?
|
||||
if !EmailToken.where(email: user.email, confirmed: true).exists? &&
|
||||
!user.email_tokens.active.where(email: user.email).exists?
|
||||
user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:signup])
|
||||
end
|
||||
|
||||
user.activate
|
||||
end
|
||||
user.update!(registration_ip_address: request.remote_ip) if user.registration_ip_address.blank?
|
||||
if user.registration_ip_address.blank?
|
||||
user.update!(registration_ip_address: request.remote_ip)
|
||||
end
|
||||
end
|
||||
|
||||
if ScreenedIpAddress.should_block?(request.remote_ip)
|
||||
|
@ -198,7 +196,9 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
|
||||
def persist_auth_token(auth)
|
||||
secret = SecureRandom.hex
|
||||
secure_session.set "#{Users::AssociateAccountsController.key(secret)}", auth.to_json, expires: 10.minutes
|
||||
secure_session.set "#{Users::AssociateAccountsController.key(secret)}",
|
||||
auth.to_json,
|
||||
expires: 10.minutes
|
||||
"#{Discourse.base_path}/associate/#{secret}"
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,35 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UsersEmailController < ApplicationController
|
||||
requires_login only: %i[index update]
|
||||
|
||||
requires_login only: [:index, :update]
|
||||
|
||||
skip_before_action :check_xhr, only: [
|
||||
:confirm_old_email,
|
||||
:show_confirm_old_email,
|
||||
:confirm_new_email,
|
||||
:show_confirm_new_email
|
||||
skip_before_action :check_xhr,
|
||||
only: %i[
|
||||
confirm_old_email
|
||||
show_confirm_old_email
|
||||
confirm_new_email
|
||||
show_confirm_new_email
|
||||
]
|
||||
|
||||
skip_before_action :redirect_to_login_if_required, only: [
|
||||
:confirm_old_email,
|
||||
:show_confirm_old_email,
|
||||
:confirm_new_email,
|
||||
:show_confirm_new_email
|
||||
skip_before_action :redirect_to_login_if_required,
|
||||
only: %i[
|
||||
confirm_old_email
|
||||
show_confirm_old_email
|
||||
confirm_new_email
|
||||
show_confirm_new_email
|
||||
]
|
||||
|
||||
before_action :require_login, only: [
|
||||
:confirm_old_email,
|
||||
:show_confirm_old_email
|
||||
]
|
||||
before_action :require_login, only: %i[confirm_old_email show_confirm_old_email]
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
def create
|
||||
if !SiteSetting.enable_secondary_emails
|
||||
return render json: failed_json, status: 410
|
||||
end
|
||||
return render json: failed_json, status: 410 if !SiteSetting.enable_secondary_emails
|
||||
|
||||
params.require(:email)
|
||||
user = fetch_user_from_params
|
||||
|
@ -40,9 +36,7 @@ class UsersEmailController < ApplicationController
|
|||
updater = EmailUpdater.new(guardian: guardian, user: user)
|
||||
updater.change_to(params[:email], add: true)
|
||||
|
||||
if updater.errors.present?
|
||||
return render_json_error(updater.errors.full_messages)
|
||||
end
|
||||
return render_json_error(updater.errors.full_messages) if updater.errors.present?
|
||||
|
||||
render body: nil
|
||||
rescue RateLimiter::LimitExceeded
|
||||
|
@ -59,9 +53,7 @@ class UsersEmailController < ApplicationController
|
|||
updater = EmailUpdater.new(guardian: guardian, user: user)
|
||||
updater.change_to(params[:email])
|
||||
|
||||
if updater.errors.present?
|
||||
return render_json_error(updater.errors.full_messages)
|
||||
end
|
||||
return render_json_error(updater.errors.full_messages) if updater.errors.present?
|
||||
|
||||
render body: nil
|
||||
rescue RateLimiter::LimitExceeded
|
||||
|
@ -119,9 +111,7 @@ class UsersEmailController < ApplicationController
|
|||
def show_confirm_new_email
|
||||
load_change_request(:new)
|
||||
|
||||
if params[:done].to_s == "true"
|
||||
@done = true
|
||||
end
|
||||
@done = true if params[:done].to_s == "true"
|
||||
|
||||
if @change_request&.change_state != EmailChangeRequest.states[:authorizing_new]
|
||||
@error = I18n.t("change_email.already_done")
|
||||
|
@ -135,21 +125,20 @@ class UsersEmailController < ApplicationController
|
|||
if params[:show_backup].to_s == "true" && @backup_codes_enabled
|
||||
@show_backup_codes = true
|
||||
else
|
||||
if @user.totp_enabled?
|
||||
@show_second_factor = true
|
||||
end
|
||||
@show_second_factor = true if @user.totp_enabled?
|
||||
if @user.security_keys_enabled?
|
||||
Webauthn.stage_challenge(@user, secure_session)
|
||||
@show_security_key = params[:show_totp].to_s == "true" ? false : true
|
||||
@security_key_challenge = Webauthn.challenge(@user, secure_session)
|
||||
@security_key_allowed_credential_ids = Webauthn.allowed_credentials(@user, secure_session)[:allowed_credential_ids]
|
||||
@security_key_allowed_credential_ids =
|
||||
Webauthn.allowed_credentials(@user, secure_session)[:allowed_credential_ids]
|
||||
end
|
||||
end
|
||||
|
||||
@to_email = @change_request.new_email
|
||||
end
|
||||
|
||||
render layout: 'no_ember'
|
||||
render layout: "no_ember"
|
||||
end
|
||||
|
||||
def confirm_old_email
|
||||
|
@ -183,16 +172,14 @@ class UsersEmailController < ApplicationController
|
|||
@error = I18n.t("change_email.already_done")
|
||||
end
|
||||
|
||||
if params[:done].to_s == "true"
|
||||
@almost_done = true
|
||||
end
|
||||
@almost_done = true if params[:done].to_s == "true"
|
||||
|
||||
if !@error
|
||||
@from_email = @user.email
|
||||
@to_email = @change_request.new_email
|
||||
end
|
||||
|
||||
render layout: 'no_ember'
|
||||
render layout: "no_ember"
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -204,27 +191,24 @@ class UsersEmailController < ApplicationController
|
|||
|
||||
if token
|
||||
if type == :old
|
||||
@change_request = token.user&.email_change_requests.where(old_email_token_id: token.id).first
|
||||
@change_request =
|
||||
token.user&.email_change_requests.where(old_email_token_id: token.id).first
|
||||
elsif type == :new
|
||||
@change_request = token.user&.email_change_requests.where(new_email_token_id: token.id).first
|
||||
@change_request =
|
||||
token.user&.email_change_requests.where(new_email_token_id: token.id).first
|
||||
end
|
||||
end
|
||||
|
||||
@user = token&.user
|
||||
|
||||
if (!@user || !@change_request)
|
||||
@error = I18n.t("change_email.already_done")
|
||||
end
|
||||
@error = I18n.t("change_email.already_done") if (!@user || !@change_request)
|
||||
|
||||
if current_user && current_user.id != @user&.id
|
||||
@error = I18n.t 'change_email.wrong_account_error'
|
||||
@error = I18n.t "change_email.wrong_account_error"
|
||||
end
|
||||
end
|
||||
|
||||
def require_login
|
||||
if !current_user
|
||||
redirect_to_login
|
||||
redirect_to_login if !current_user
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -152,11 +152,14 @@ class WebhooksController < ActionController::Base
|
|||
return false if (Time.at(timestamp.to_i) - Time.now).abs > 12.hours.to_i
|
||||
|
||||
# check the signature
|
||||
signature == OpenSSL::HMAC.hexdigest("SHA256", SiteSetting.mailgun_api_key, "#{timestamp}#{token}")
|
||||
signature ==
|
||||
OpenSSL::HMAC.hexdigest("SHA256", SiteSetting.mailgun_api_key, "#{timestamp}#{token}")
|
||||
end
|
||||
|
||||
def handle_mailgun_legacy(params)
|
||||
return mailgun_failure unless valid_mailgun_signature?(params["token"], params["timestamp"], params["signature"])
|
||||
unless valid_mailgun_signature?(params["token"], params["timestamp"], params["signature"])
|
||||
return mailgun_failure
|
||||
end
|
||||
|
||||
event = params["event"]
|
||||
message_id = Email::MessageIdService.message_id_clean(params["Message-Id"])
|
||||
|
@ -177,7 +180,13 @@ class WebhooksController < ActionController::Base
|
|||
|
||||
def handle_mailgun_new(params)
|
||||
signature = params["signature"]
|
||||
return mailgun_failure unless valid_mailgun_signature?(signature["token"], signature["timestamp"], signature["signature"])
|
||||
unless valid_mailgun_signature?(
|
||||
signature["token"],
|
||||
signature["timestamp"],
|
||||
signature["signature"],
|
||||
)
|
||||
return mailgun_failure
|
||||
end
|
||||
|
||||
data = params["event-data"]
|
||||
error_code = params.dig("delivery-status", "code")
|
||||
|
@ -207,5 +216,4 @@ class WebhooksController < ActionController::Base
|
|||
|
||||
Email::Receiver.update_bounce_score(email_log.user.email, bounce_score)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,9 +13,7 @@ class WizardController < ApplicationController
|
|||
render_serialized(wizard, WizardSerializer)
|
||||
end
|
||||
|
||||
format.html do
|
||||
render body: nil
|
||||
end
|
||||
format.html { render body: nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# coding: utf-8
|
||||
# frozen_string_literal: true
|
||||
require 'current_user'
|
||||
require 'canonical_url'
|
||||
require "current_user"
|
||||
require "canonical_url"
|
||||
|
||||
module ApplicationHelper
|
||||
include CurrentUser
|
||||
|
@ -14,7 +14,6 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def discourse_config_environment(testing: false)
|
||||
|
||||
# TODO: Can this come from Ember CLI somehow?
|
||||
config = {
|
||||
modulePrefix: "discourse",
|
||||
|
@ -23,24 +22,27 @@ module ApplicationHelper
|
|||
locationType: "history",
|
||||
historySupportMiddleware: false,
|
||||
EmberENV: {
|
||||
FEATURES: {},
|
||||
EXTEND_PROTOTYPES: { "Date": false },
|
||||
FEATURES: {
|
||||
},
|
||||
EXTEND_PROTOTYPES: {
|
||||
Date: false,
|
||||
},
|
||||
_APPLICATION_TEMPLATE_WRAPPER: false,
|
||||
_DEFAULT_ASYNC_OBSERVERS: true,
|
||||
_JQUERY_INTEGRATION: true
|
||||
_JQUERY_INTEGRATION: true,
|
||||
},
|
||||
APP: {
|
||||
name: "discourse",
|
||||
version: "#{Discourse::VERSION::STRING} #{Discourse.git_version}",
|
||||
exportApplicationGlobal: true
|
||||
}
|
||||
exportApplicationGlobal: true,
|
||||
},
|
||||
}
|
||||
|
||||
if testing
|
||||
config[:environment] = "test"
|
||||
config[:locationType] = "none"
|
||||
config[:APP][:autoboot] = false
|
||||
config[:APP][:rootElement] = '#ember-testing'
|
||||
config[:APP][:rootElement] = "#ember-testing"
|
||||
end
|
||||
|
||||
config.to_json
|
||||
|
@ -48,15 +50,9 @@ module ApplicationHelper
|
|||
|
||||
def google_universal_analytics_json(ua_domain_name = nil)
|
||||
result = {}
|
||||
if ua_domain_name
|
||||
result[:cookieDomain] = ua_domain_name.gsub(/^http(s)?:\/\//, '')
|
||||
end
|
||||
if current_user.present?
|
||||
result[:userId] = current_user.id
|
||||
end
|
||||
if SiteSetting.ga_universal_auto_link_domains.present?
|
||||
result[:allowLinker] = true
|
||||
end
|
||||
result[:cookieDomain] = ua_domain_name.gsub(%r{^http(s)?://}, "") if ua_domain_name
|
||||
result[:userId] = current_user.id if current_user.present?
|
||||
result[:allowLinker] = true if SiteSetting.ga_universal_auto_link_domains.present?
|
||||
result.to_json
|
||||
end
|
||||
|
||||
|
@ -73,7 +69,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def shared_session_key
|
||||
if SiteSetting.long_polling_base_url != '/' && current_user
|
||||
if SiteSetting.long_polling_base_url != "/" && current_user
|
||||
sk = "shared_session_key"
|
||||
return request.env[sk] if request.env[sk]
|
||||
|
||||
|
@ -95,10 +91,15 @@ module ApplicationHelper
|
|||
path = ActionController::Base.helpers.asset_path("#{script}.js")
|
||||
|
||||
if GlobalSetting.use_s3? && GlobalSetting.s3_cdn_url
|
||||
resolved_s3_asset_cdn_url = GlobalSetting.s3_asset_cdn_url.presence || GlobalSetting.s3_cdn_url
|
||||
resolved_s3_asset_cdn_url =
|
||||
GlobalSetting.s3_asset_cdn_url.presence || GlobalSetting.s3_cdn_url
|
||||
if GlobalSetting.cdn_url
|
||||
folder = ActionController::Base.config.relative_url_root || "/"
|
||||
path = path.gsub(File.join(GlobalSetting.cdn_url, folder, "/"), File.join(resolved_s3_asset_cdn_url, "/"))
|
||||
path =
|
||||
path.gsub(
|
||||
File.join(GlobalSetting.cdn_url, folder, "/"),
|
||||
File.join(resolved_s3_asset_cdn_url, "/"),
|
||||
)
|
||||
else
|
||||
# we must remove the subfolder path here, assets are uploaded to s3
|
||||
# without it getting involved
|
||||
|
@ -121,8 +122,8 @@ module ApplicationHelper
|
|||
path = path.gsub(/\.([^.]+)$/, '.gz.\1')
|
||||
end
|
||||
end
|
||||
|
||||
elsif GlobalSetting.cdn_url&.start_with?("https") && is_brotli_req? && Rails.env != "development"
|
||||
elsif GlobalSetting.cdn_url&.start_with?("https") && is_brotli_req? &&
|
||||
Rails.env != "development"
|
||||
path = path.gsub("#{GlobalSetting.cdn_url}/assets/", "#{GlobalSetting.cdn_url}/brotli_asset/")
|
||||
end
|
||||
|
||||
|
@ -136,14 +137,17 @@ module ApplicationHelper
|
|||
scripts.push(*chunks)
|
||||
end
|
||||
|
||||
scripts.map do |name|
|
||||
scripts
|
||||
.map do |name|
|
||||
path = script_asset_path(name)
|
||||
preload_script_url(path)
|
||||
end.join("\n").html_safe
|
||||
end
|
||||
.join("\n")
|
||||
.html_safe
|
||||
end
|
||||
|
||||
def preload_script_url(url)
|
||||
add_resource_preload_list(url, 'script')
|
||||
add_resource_preload_list(url, "script")
|
||||
if GlobalSetting.preload_link_header
|
||||
<<~HTML.html_safe
|
||||
<script defer src="#{url}"></script>
|
||||
|
@ -157,43 +161,40 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def add_resource_preload_list(resource_url, type)
|
||||
@links_to_preload << %Q(<#{resource_url}>; rel="preload"; as="#{type}") if !@links_to_preload.nil?
|
||||
if !@links_to_preload.nil?
|
||||
@links_to_preload << %Q(<#{resource_url}>; rel="preload"; as="#{type}")
|
||||
end
|
||||
end
|
||||
|
||||
def discourse_csrf_tags
|
||||
# anon can not have a CSRF token cause these are all pages
|
||||
# that may be cached, causing a mismatch between session CSRF
|
||||
# and CSRF on page and horrible impossible to debug login issues
|
||||
if current_user
|
||||
csrf_meta_tags
|
||||
end
|
||||
csrf_meta_tags if current_user
|
||||
end
|
||||
|
||||
def html_classes
|
||||
list = []
|
||||
list << (mobile_view? ? 'mobile-view' : 'desktop-view')
|
||||
list << (mobile_device? ? 'mobile-device' : 'not-mobile-device')
|
||||
list << 'ios-device' if ios_device?
|
||||
list << 'rtl' if rtl?
|
||||
list << (mobile_view? ? "mobile-view" : "desktop-view")
|
||||
list << (mobile_device? ? "mobile-device" : "not-mobile-device")
|
||||
list << "ios-device" if ios_device?
|
||||
list << "rtl" if rtl?
|
||||
list << text_size_class
|
||||
list << 'anon' unless current_user
|
||||
list.join(' ')
|
||||
list << "anon" unless current_user
|
||||
list.join(" ")
|
||||
end
|
||||
|
||||
def body_classes
|
||||
result = ApplicationHelper.extra_body_classes.to_a
|
||||
|
||||
if @category && @category.url.present?
|
||||
result << "category-#{@category.slug_path.join('-')}"
|
||||
end
|
||||
result << "category-#{@category.slug_path.join("-")}" if @category && @category.url.present?
|
||||
|
||||
if current_user.present? &&
|
||||
current_user.primary_group_id &&
|
||||
if current_user.present? && current_user.primary_group_id &&
|
||||
primary_group_name = Group.where(id: current_user.primary_group_id).pluck_first(:name)
|
||||
result << "primary-group-#{primary_group_name.downcase}"
|
||||
end
|
||||
|
||||
result.join(' ')
|
||||
result.join(" ")
|
||||
end
|
||||
|
||||
def text_size_class
|
||||
|
@ -211,11 +212,11 @@ module ApplicationHelper
|
|||
def escape_unicode(javascript)
|
||||
if javascript
|
||||
javascript = javascript.scrub
|
||||
javascript.gsub!(/\342\200\250/u, '
')
|
||||
javascript.gsub!(/(<\/)/u, '\u003C/')
|
||||
javascript.gsub!(/\342\200\250/u, "
")
|
||||
javascript.gsub!(%r{(</)}u, '\u003C/')
|
||||
javascript
|
||||
else
|
||||
''
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -260,7 +261,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def rtl?
|
||||
["ar", "ur", "fa_IR", "he"].include? I18n.locale.to_s
|
||||
%w[ar ur fa_IR he].include? I18n.locale.to_s
|
||||
end
|
||||
|
||||
def html_lang
|
||||
|
@ -291,18 +292,19 @@ module ApplicationHelper
|
|||
|
||||
# Use the correct scheme for opengraph/twitter image
|
||||
opts[:image] = get_absolute_image_url(opts[:image]) if opts[:image].present?
|
||||
opts[:twitter_summary_large_image] =
|
||||
get_absolute_image_url(opts[:twitter_summary_large_image]) if opts[:twitter_summary_large_image].present?
|
||||
opts[:twitter_summary_large_image] = get_absolute_image_url(
|
||||
opts[:twitter_summary_large_image],
|
||||
) if opts[:twitter_summary_large_image].present?
|
||||
|
||||
result = []
|
||||
result << tag(:meta, property: 'og:site_name', content: SiteSetting.title)
|
||||
result << tag(:meta, property: 'og:type', content: 'website')
|
||||
result << tag(:meta, property: "og:site_name", content: SiteSetting.title)
|
||||
result << tag(:meta, property: "og:type", content: "website")
|
||||
|
||||
generate_twitter_card_metadata(result, opts)
|
||||
|
||||
result << tag(:meta, property: "og:image", content: opts[:image]) if opts[:image].present?
|
||||
|
||||
[:url, :title, :description].each do |property|
|
||||
%i[url title description].each do |property|
|
||||
if opts[property].present?
|
||||
content = (property == :url ? opts[property] : gsub_emoji_to_unicode(opts[property]))
|
||||
result << tag(:meta, { property: "og:#{property}", content: content }, nil, true)
|
||||
|
@ -311,27 +313,30 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
if opts[:read_time] && opts[:read_time] > 0 && opts[:like_count] && opts[:like_count] > 0
|
||||
result << tag(:meta, name: 'twitter:label1', value: I18n.t("reading_time"))
|
||||
result << tag(:meta, name: 'twitter:data1', value: "#{opts[:read_time]} mins 🕑")
|
||||
result << tag(:meta, name: 'twitter:label2', value: I18n.t("likes"))
|
||||
result << tag(:meta, name: 'twitter:data2', value: "#{opts[:like_count]} ❤")
|
||||
result << tag(:meta, name: "twitter:label1", value: I18n.t("reading_time"))
|
||||
result << tag(:meta, name: "twitter:data1", value: "#{opts[:read_time]} mins 🕑")
|
||||
result << tag(:meta, name: "twitter:label2", value: I18n.t("likes"))
|
||||
result << tag(:meta, name: "twitter:data2", value: "#{opts[:like_count]} ❤")
|
||||
end
|
||||
|
||||
if opts[:published_time]
|
||||
result << tag(:meta, property: 'article:published_time', content: opts[:published_time])
|
||||
result << tag(:meta, property: "article:published_time", content: opts[:published_time])
|
||||
end
|
||||
|
||||
if opts[:ignore_canonical]
|
||||
result << tag(:meta, property: 'og:ignore_canonical', content: true)
|
||||
end
|
||||
result << tag(:meta, property: "og:ignore_canonical", content: true) if opts[:ignore_canonical]
|
||||
|
||||
result.join("\n")
|
||||
end
|
||||
|
||||
private def generate_twitter_card_metadata(result, opts)
|
||||
img_url = opts[:twitter_summary_large_image].present? ? \
|
||||
opts[:twitter_summary_large_image] :
|
||||
img_url =
|
||||
(
|
||||
if opts[:twitter_summary_large_image].present?
|
||||
opts[:twitter_summary_large_image]
|
||||
else
|
||||
opts[:image]
|
||||
end
|
||||
)
|
||||
|
||||
# Twitter does not allow SVGs, see https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
|
||||
if img_url.ends_with?(".svg")
|
||||
|
@ -339,29 +344,29 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
if opts[:twitter_summary_large_image].present? && img_url.present?
|
||||
result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
|
||||
result << tag(:meta, name: "twitter:card", content: "summary_large_image")
|
||||
result << tag(:meta, name: "twitter:image", content: img_url)
|
||||
elsif opts[:image].present? && img_url.present?
|
||||
result << tag(:meta, name: 'twitter:card', content: "summary")
|
||||
result << tag(:meta, name: "twitter:card", content: "summary")
|
||||
result << tag(:meta, name: "twitter:image", content: img_url)
|
||||
else
|
||||
result << tag(:meta, name: 'twitter:card', content: "summary")
|
||||
result << tag(:meta, name: "twitter:card", content: "summary")
|
||||
end
|
||||
end
|
||||
|
||||
def render_sitelinks_search_tag
|
||||
if current_page?('/') || current_page?(Discourse.base_path)
|
||||
if current_page?("/") || current_page?(Discourse.base_path)
|
||||
json = {
|
||||
'@context' => 'http://schema.org',
|
||||
'@type' => 'WebSite',
|
||||
url: Discourse.base_url,
|
||||
potentialAction: {
|
||||
'@type' => 'SearchAction',
|
||||
target: "#{Discourse.base_url}/search?q={search_term_string}",
|
||||
'query-input' => 'required name=search_term_string',
|
||||
"@context" => "http://schema.org",
|
||||
"@type" => "WebSite",
|
||||
:url => Discourse.base_url,
|
||||
:potentialAction => {
|
||||
"@type" => "SearchAction",
|
||||
:target => "#{Discourse.base_url}/search?q={search_term_string}",
|
||||
"query-input" => "required name=search_term_string",
|
||||
},
|
||||
}
|
||||
}
|
||||
content_tag(:script, MultiJson.dump(json).html_safe, type: 'application/ld+json')
|
||||
content_tag(:script, MultiJson.dump(json).html_safe, type: "application/ld+json")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -370,7 +375,8 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def application_logo_url
|
||||
@application_logo_url ||= begin
|
||||
@application_logo_url ||=
|
||||
begin
|
||||
if mobile_view?
|
||||
if dark_color_scheme? && SiteSetting.site_mobile_logo_dark_url.present?
|
||||
SiteSetting.site_mobile_logo_dark_url
|
||||
|
@ -388,7 +394,8 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def application_logo_dark_url
|
||||
@application_logo_dark_url ||= begin
|
||||
@application_logo_dark_url ||=
|
||||
begin
|
||||
if dark_scheme_id != -1
|
||||
if mobile_view? && SiteSetting.site_mobile_logo_dark_url != application_logo_url
|
||||
SiteSetting.site_mobile_logo_dark_url
|
||||
|
@ -437,8 +444,11 @@ module ApplicationHelper
|
|||
|
||||
def ios_app_argument
|
||||
# argument only makes sense for DiscourseHub app
|
||||
SiteSetting.ios_app_id == "1173672076" ?
|
||||
", app-argument=discourse://new?siteUrl=#{Discourse.base_url}" : ""
|
||||
if SiteSetting.ios_app_id == "1173672076"
|
||||
", app-argument=discourse://new?siteUrl=#{Discourse.base_url}"
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def include_splash_screen?
|
||||
|
@ -496,9 +506,9 @@ module ApplicationHelper
|
|||
uri = UrlHelper.encode_and_parse(link)
|
||||
uri = URI.parse("http://#{uri}") if uri.scheme.nil?
|
||||
host = uri.host.downcase
|
||||
host.start_with?('www.') ? host[4..-1] : host
|
||||
rescue
|
||||
''
|
||||
host.start_with?("www.") ? host[4..-1] : host
|
||||
rescue StandardError
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -529,7 +539,8 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def dark_scheme_id
|
||||
cookies[:dark_scheme_id] || current_user&.user_option&.dark_scheme_id || SiteSetting.default_dark_mode_color_scheme_id
|
||||
cookies[:dark_scheme_id] || current_user&.user_option&.dark_scheme_id ||
|
||||
SiteSetting.default_dark_mode_color_scheme_id
|
||||
end
|
||||
|
||||
def current_homepage
|
||||
|
@ -556,7 +567,7 @@ module ApplicationHelper
|
|||
theme_id,
|
||||
mobile_view? ? :mobile : :desktop,
|
||||
name,
|
||||
skip_transformation: request.env[:skip_theme_ids_transformation].present?
|
||||
skip_transformation: request.env[:skip_theme_ids_transformation].present?,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -565,7 +576,7 @@ module ApplicationHelper
|
|||
theme_id,
|
||||
:translations,
|
||||
I18n.locale,
|
||||
skip_transformation: request.env[:skip_theme_ids_transformation].present?
|
||||
skip_transformation: request.env[:skip_theme_ids_transformation].present?,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -574,42 +585,41 @@ module ApplicationHelper
|
|||
theme_id,
|
||||
:extra_js,
|
||||
nil,
|
||||
skip_transformation: request.env[:skip_theme_ids_transformation].present?
|
||||
skip_transformation: request.env[:skip_theme_ids_transformation].present?,
|
||||
)
|
||||
end
|
||||
|
||||
def discourse_stylesheet_preload_tag(name, opts = {})
|
||||
manager =
|
||||
if opts.key?(:theme_id)
|
||||
Stylesheet::Manager.new(
|
||||
theme_id: customization_disabled? ? nil : opts[:theme_id]
|
||||
)
|
||||
Stylesheet::Manager.new(theme_id: customization_disabled? ? nil : opts[:theme_id])
|
||||
else
|
||||
stylesheet_manager
|
||||
end
|
||||
|
||||
manager.stylesheet_preload_tag(name, 'all')
|
||||
manager.stylesheet_preload_tag(name, "all")
|
||||
end
|
||||
|
||||
def discourse_stylesheet_link_tag(name, opts = {})
|
||||
manager =
|
||||
if opts.key?(:theme_id)
|
||||
Stylesheet::Manager.new(
|
||||
theme_id: customization_disabled? ? nil : opts[:theme_id]
|
||||
)
|
||||
Stylesheet::Manager.new(theme_id: customization_disabled? ? nil : opts[:theme_id])
|
||||
else
|
||||
stylesheet_manager
|
||||
end
|
||||
|
||||
manager.stylesheet_link_tag(name, 'all', self.method(:add_resource_preload_list))
|
||||
manager.stylesheet_link_tag(name, "all", self.method(:add_resource_preload_list))
|
||||
end
|
||||
|
||||
def discourse_preload_color_scheme_stylesheets
|
||||
result = +""
|
||||
result << stylesheet_manager.color_scheme_stylesheet_preload_tag(scheme_id, 'all')
|
||||
result << stylesheet_manager.color_scheme_stylesheet_preload_tag(scheme_id, "all")
|
||||
|
||||
if dark_scheme_id != -1
|
||||
result << stylesheet_manager.color_scheme_stylesheet_preload_tag(dark_scheme_id, '(prefers-color-scheme: dark)')
|
||||
result << stylesheet_manager.color_scheme_stylesheet_preload_tag(
|
||||
dark_scheme_id,
|
||||
"(prefers-color-scheme: dark)",
|
||||
)
|
||||
end
|
||||
|
||||
result.html_safe
|
||||
|
@ -617,10 +627,18 @@ module ApplicationHelper
|
|||
|
||||
def discourse_color_scheme_stylesheets
|
||||
result = +""
|
||||
result << stylesheet_manager.color_scheme_stylesheet_link_tag(scheme_id, 'all', self.method(:add_resource_preload_list))
|
||||
result << stylesheet_manager.color_scheme_stylesheet_link_tag(
|
||||
scheme_id,
|
||||
"all",
|
||||
self.method(:add_resource_preload_list),
|
||||
)
|
||||
|
||||
if dark_scheme_id != -1
|
||||
result << stylesheet_manager.color_scheme_stylesheet_link_tag(dark_scheme_id, '(prefers-color-scheme: dark)', self.method(:add_resource_preload_list))
|
||||
result << stylesheet_manager.color_scheme_stylesheet_link_tag(
|
||||
dark_scheme_id,
|
||||
"(prefers-color-scheme: dark)",
|
||||
self.method(:add_resource_preload_list),
|
||||
)
|
||||
end
|
||||
|
||||
result.html_safe
|
||||
|
@ -630,12 +648,12 @@ module ApplicationHelper
|
|||
result = +""
|
||||
if dark_scheme_id != -1
|
||||
result << <<~HTML
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="##{ColorScheme.hex_for_name('header_background', scheme_id)}">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="##{ColorScheme.hex_for_name('header_background', dark_scheme_id)}">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="##{ColorScheme.hex_for_name("header_background", scheme_id)}">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="##{ColorScheme.hex_for_name("header_background", dark_scheme_id)}">
|
||||
HTML
|
||||
else
|
||||
result << <<~HTML
|
||||
<meta name="theme-color" media="all" content="##{ColorScheme.hex_for_name('header_background', scheme_id)}">
|
||||
<meta name="theme-color" media="all" content="##{ColorScheme.hex_for_name("header_background", scheme_id)}">
|
||||
HTML
|
||||
end
|
||||
result.html_safe
|
||||
|
@ -647,7 +665,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def preloaded_json
|
||||
return '{}' if @preloaded.blank?
|
||||
return "{}" if @preloaded.blank?
|
||||
@preloaded.transform_values { |value| escape_unicode(value) }.to_json
|
||||
end
|
||||
|
||||
|
@ -658,8 +676,8 @@ module ApplicationHelper
|
|||
base_uri: Discourse.base_path,
|
||||
environment: Rails.env,
|
||||
letter_avatar_version: LetterAvatar.version,
|
||||
markdown_it_url: script_asset_path('markdown-it-bundle'),
|
||||
service_worker_url: 'service-worker.js',
|
||||
markdown_it_url: script_asset_path("markdown-it-bundle"),
|
||||
service_worker_url: "service-worker.js",
|
||||
default_locale: SiteSetting.default_locale,
|
||||
asset_version: Discourse.assets_digest,
|
||||
disable_custom_css: loading_admin?,
|
||||
|
@ -668,16 +686,14 @@ module ApplicationHelper
|
|||
enable_js_error_reporting: GlobalSetting.enable_js_error_reporting,
|
||||
color_scheme_is_dark: dark_color_scheme?,
|
||||
user_color_scheme_id: scheme_id,
|
||||
user_dark_scheme_id: dark_scheme_id
|
||||
user_dark_scheme_id: dark_scheme_id,
|
||||
}
|
||||
|
||||
if Rails.env.development?
|
||||
setup_data[:svg_icon_list] = SvgSprite.all_icons(theme_id)
|
||||
|
||||
if ENV['DEBUG_PRELOADED_APP_DATA']
|
||||
setup_data[:debug_preloaded_app_data] = true
|
||||
end
|
||||
setup_data[:mb_last_file_change_id] = MessageBus.last_id('/file-change')
|
||||
setup_data[:debug_preloaded_app_data] = true if ENV["DEBUG_PRELOADED_APP_DATA"]
|
||||
setup_data[:mb_last_file_change_id] = MessageBus.last_id("/file-change")
|
||||
end
|
||||
|
||||
if guardian.can_enable_safe_mode? && params["safe_mode"]
|
||||
|
@ -694,10 +710,10 @@ module ApplicationHelper
|
|||
|
||||
def get_absolute_image_url(link)
|
||||
absolute_url = link
|
||||
if link.start_with?('//')
|
||||
if link.start_with?("//")
|
||||
uri = URI(Discourse.base_url)
|
||||
absolute_url = "#{uri.scheme}:#{link}"
|
||||
elsif link.start_with?('/uploads/', '/images/', '/user_avatar/')
|
||||
elsif link.start_with?("/uploads/", "/images/", "/user_avatar/")
|
||||
absolute_url = "#{Discourse.base_url}#{link}"
|
||||
elsif GlobalSetting.relative_url_root && link.start_with?(GlobalSetting.relative_url_root)
|
||||
absolute_url = "#{Discourse.base_url_no_prefix}#{link}"
|
||||
|
@ -713,8 +729,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def can_sign_up?
|
||||
SiteSetting.allow_new_registrations &&
|
||||
!SiteSetting.invite_only &&
|
||||
SiteSetting.allow_new_registrations && !SiteSetting.invite_only &&
|
||||
!SiteSetting.enable_discourse_connect
|
||||
end
|
||||
|
||||
|
@ -725,11 +740,10 @@ module ApplicationHelper
|
|||
def authentication_data
|
||||
return @authentication_data if defined?(@authentication_data)
|
||||
|
||||
@authentication_data = begin
|
||||
@authentication_data =
|
||||
begin
|
||||
value = cookies[:authentication_data]
|
||||
if value
|
||||
cookies.delete(:authentication_data, path: Discourse.base_path("/"))
|
||||
end
|
||||
cookies.delete(:authentication_data, path: Discourse.base_path("/")) if value
|
||||
current_user ? nil : value
|
||||
end
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue