Skip to content

Commit d34c680

Browse files
committed
Merge branch 'develop' into art/admin-error-preload
2 parents 3879bd1 + e93eb4d commit d34c680

13 files changed

Lines changed: 155 additions & 52 deletions

File tree

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,78 @@
1-
$(() => {
2-
$(".post--content pre > code")
3-
.parent()
4-
.each(function() {
5-
const content = $(this).text()
6-
$(this)
7-
.wrap('<div style="position:relative;"></div>')
8-
.parent()
9-
.prepend($('<button class="copy-button button is-muted is-outlined has-margin-2">Copy</button>')
10-
.click(function () {
11-
navigator.clipboard.writeText(content);
12-
$(this).text('Copied!');
13-
setTimeout(() => { $(this).text('Copy'); }, 2000);
14-
}))
1+
document.addEventListener('DOMContentLoaded', () => {
2+
/**
3+
* @param {string} content
4+
* @returns {HTMLButtonElement}
5+
*/
6+
const createCopyButton = (content, isSmall = false) => {
7+
const button = document.createElement('button');
8+
button.classList.add('copy-button', 'button', 'is-muted', 'is-outlined', 'has-margin-2');
9+
button.textContent = 'Copy';
10+
11+
if (isSmall) {
12+
button.classList.add('is-small');
13+
}
14+
15+
button.addEventListener('click', async () => {
16+
const originalButtonText = button.textContent;
17+
18+
try {
19+
await navigator.clipboard.writeText(content);
20+
button.textContent = 'Copied!';
21+
}
22+
catch (e) {
23+
console.warn(e);
24+
button.textContent = 'Failed!';
25+
}
26+
finally {
27+
setTimeout(() => {
28+
button.textContent = originalButtonText;
29+
}, 2000);
30+
}
31+
});
32+
33+
return button;
34+
};
35+
36+
/**
37+
* @param {Element} element
38+
* @returns {HTMLDivElement}
39+
*/
40+
const wrapRelative = (element) => {
41+
const wrapper = document.createElement('div');
42+
wrapper.style.position = 'relative';
43+
wrapper.append(element.cloneNode(true));
44+
element.replaceWith(wrapper);
45+
return wrapper;
46+
};
47+
48+
if (!window.isSecureContext) {
49+
return;
50+
}
51+
52+
document.querySelectorAll('.post--content pre > code').forEach((child) => {
53+
const { parentElement: element } = child;
54+
55+
if (!element) {
56+
return;
57+
}
58+
59+
const { textContent } = element;
60+
61+
// code blocks always have a trailing newline added
62+
const normalizedText = textContent.replace(/\n$/, '');
63+
64+
if (!normalizedText) {
65+
return;
66+
}
67+
68+
const numLines = normalizedText.split(/\r?\n/).length;
69+
70+
if (numLines < 1) {
71+
return;
72+
}
73+
74+
const button = createCopyButton(textContent, numLines === 1);
75+
const wrapper = wrapRelative(element);
76+
wrapper.prepend(button);
1577
});
16-
});
78+
});

app/assets/stylesheets/utilities.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ pre.pre-wrap {
123123
display: none;
124124
position: absolute;
125125
right: 0;
126+
127+
&.is-small {
128+
top: -1px;
129+
}
126130
}
127131

128132
div:hover > .copy-button {

app/controllers/categories_controller.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,15 @@ def verify_view_access
158158
end
159159

160160
def set_list_posts
161-
sort_params = { activity: { last_activity: :desc }, age: { created_at: :desc }, score: { score: :desc },
161+
@default_sort_type = :activity
162+
@sort_type = params[:sort]&.to_sym || @default_sort_type
163+
164+
sort_params = { activity: { last_activity: :desc },
165+
age: { created_at: :desc },
166+
score: { score: :desc },
162167
lottery: [Arel.sql('(RAND() - ? * DATEDIFF(CURRENT_TIMESTAMP, posts.created_at)) DESC'),
163168
SiteSetting['LotteryAgeDeprecationSpeed']] }
164-
sort_param = sort_params[params[:sort]&.to_sym] || { last_activity: :desc }
169+
165170
@posts = @category.posts
166171
.undeleted
167172
.where(post_type_id: @category.display_post_types)
@@ -200,7 +205,8 @@ def set_list_posts
200205

201206
@posts = helpers.qualifiers_to_sql(filter_qualifiers, @posts, current_user)
202207
@filtered = filter_qualifiers.any?
203-
@posts = @posts.paginate(page: params[:page], per_page: 50).order(sort_param)
208+
@posts = @posts.paginate(page: params[:page], per_page: 50)
209+
.order(sort_params[@sort_type])
204210
end
205211

206212
def clear_categories_cache

app/controllers/users_controller.rb

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,13 @@ def index
4545

4646
def show
4747
@abilities = Ability.on_user(@user)
48+
@limit = params[:limit]&.to_i || 15
4849

49-
all_posts = if current_user&.privilege?('flag_curate') || @user == current_user
50-
@user.posts
51-
else
52-
@user.posts.undeleted
53-
end
54-
.list_includes
55-
.joins(:category)
56-
.where('IFNULL(categories.min_view_trust_level, 0) <= ?', current_user&.trust_level || 0)
57-
.user_sort({ term: params[:sort], default: :score },
58-
age: :created_at, score: :score)
59-
60-
@posts = all_posts.first(15)
61-
@total_post_count = all_posts.count
50+
@posts = set_posts.user_sort({ term: params[:sort], default: :score },
51+
age: :created_at, score: :score)
52+
53+
@total_post_count = @posts.count
54+
@posts = @posts.first(@limit)
6255
render layout: 'without_sidebar'
6356
end
6457

@@ -215,17 +208,13 @@ def set_preference
215208
end
216209

217210
def posts
218-
@posts = if current_user&.privilege?('flag_curate') || @user == current_user
219-
Post.all
220-
else
221-
Post.undeleted
222-
end.by(@user).list_includes.joins(:category)
223-
.where('IFNULL(categories.min_view_trust_level, 0) <= ?', current_user&.trust_level || 0)
224-
.user_sort({ term: params[:sort], default: :score },
225-
activity: :last_activity,
226-
age: :created_at,
227-
score: :score)
228-
.paginate(page: params[:page], per_page: 25)
211+
@posts = set_posts.user_sort({ term: params[:sort], default: :score },
212+
activity: :last_activity,
213+
age: :created_at,
214+
score: :score)
215+
.order(created_at: :desc)
216+
.paginate(page: params[:page], per_page: 25)
217+
229218
respond_to do |format|
230219
format.html do
231220
render :posts
@@ -661,6 +650,17 @@ def filter_params
661650
include_tags: [], exclude_tags: [])
662651
end
663652

653+
def set_posts
654+
@posts = if current_user&.privilege?('flag_curate') || @user == current_user
655+
@user.posts
656+
else
657+
@user.posts.undeleted
658+
end
659+
.list_includes
660+
.joins(:category)
661+
.where('IFNULL(categories.min_view_trust_level, 0) <= ?', current_user&.trust_level || 0)
662+
end
663+
664664
def set_user
665665
user_id = if params[:id] == 'me' && user_signed_in?
666666
current_user.id

app/helpers/application_helper.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Provides helper methods for use by views under <tt>ApplicationController</tt> (and by extension, every view).
22
module ApplicationHelper
3-
include Warden::Test::Helpers
4-
53
##
64
# Is the current user a moderator or admin on the current community?
75
# @return [Boolean]
@@ -330,7 +328,7 @@ def current_user
330328
@current_user ||= warden.authenticate(scope: :user)
331329
if @current_user&.deleted? || @current_user&.community_user&.deleted?
332330
scope = Devise::Mapping.find_scope!(:user)
333-
logout scope
331+
warden.logout(scope)
334332
@current_user = nil
335333
end
336334
@current_user

app/helpers/search_helper.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,11 @@ def params_to_qualifiers(params)
114114
filter_qualifiers.append({ param: :source, value: params[:source].to_sym })
115115
end
116116

117-
if params[:include_tags]&.all? { |id| id.match? valid_value[:integer] }
117+
if valid_tags_list?(params[:include_tags])
118118
filter_qualifiers.append({ param: :include_tags, tag_ids: params[:include_tags] })
119119
end
120120

121-
if params[:exclude_tags]&.all? { |id| id.match? valid_value[:integer] }
121+
if valid_tags_list?(params[:exclude_tags])
122122
filter_qualifiers.append({ param: :exclude_tags, tag_ids: params[:exclude_tags] })
123123
end
124124

@@ -244,6 +244,13 @@ def qualifiers_to_sql(qualifiers, query, user)
244244
query
245245
end
246246

247+
# Is a given param a valid list of tags?
248+
# @param param [String, Array<String>, nil] parameter to check
249+
# @return [Boolean] check result
250+
def valid_tags_list?(param)
251+
param.is_a?(Array) && param&.all? { |id| id.match?(/^\d+$/) }
252+
end
253+
247254
# Is a given param a valid source type?
248255
# @param param [String, Symbol, nil] parameter to check
249256
# @return [Boolean] check result

app/models/tag.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def self.search(term)
4949

5050
# Select from the union of the above queries, select only the tag columns such that we can distinct them
5151
from(Arel.sql("((#{q1.to_sql}) UNION (#{q2.to_sql})) tags"))
52-
.order(Arel.sql(sanitize_sql_array(['name LIKE ? DESC, name', value])))
52+
.order(Arel.sql(sanitize_sql_array(['name LIKE ? DESC, char_length(name), name', value])))
5353
.select(Tag.column_names.map { |c| "tags.#{c}" })
5454
.distinct
5555
end

app/views/search/_filters.html.erb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<% allow_delete ||= false %>
22
<% allow_apply = true if allow_apply.nil? %>
33
<% @active_filter ||= {} %>
4+
<% @default_sort_type ||= '' %>
45

56
<div class="form-group">
6-
<input name="sort" type="hidden" value="<%= params[:sort] || '' %>" />
7+
<input name="sort" type="hidden" value="<%= params[:sort] || @default_sort_type %>" />
78

89
<div class="search-filters-filter">
910
<div>

db/migrate/20260208223211_follow_own_posts.rb renamed to db/migrate/20260304104100_follow_own_posts.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
class FollowOwnPosts < ActiveRecord::Migration[7.2]
22
def change
3-
to_insert = Post.where.not(post_type_id: [PolicyDoc.post_type_id, HelpDoc.post_type_id])
3+
to_insert = Post.unscoped
4+
.where.not(post_type_id: [PolicyDoc.post_type_id, HelpDoc.post_type_id])
45
.where.not(user_id: nil)
56
.pluck(:id, :user_id)
67
.map { |post_id, user_id| { post_id: post_id, user_id: user_id } }

db/schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[7.2].define(version: 2025_12_26_185531) do
13+
ActiveRecord::Schema[7.2].define(version: 2026_03_04_104100) do
1414
create_table "abilities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
1515
t.bigint "community_id"
1616
t.string "name"

0 commit comments

Comments
 (0)