Skip to content

Commit 9a59784

Browse files
authored
Merge pull request #1877 from codidact/0valt/1876/thread-rename
Disallow renaming of threads to their current titles
2 parents f695514 + 7eb7ea5 commit 9a59784

File tree

8 files changed

+104
-17
lines changed

8 files changed

+104
-17
lines changed

app/assets/javascripts/comments.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,57 @@ $(() => {
255255
});
256256
});
257257

258+
/**
259+
* @param {Element} target
260+
*/
261+
const checkThreadRename = (target) => {
262+
if (target instanceof HTMLInputElement) {
263+
const $tgt = $(target);
264+
const threadID = $tgt.data('thread');
265+
const $form = $tgt.closest(`form[data-thread=${threadID}]`);
266+
const $submitter = $form.find('.js-rename-thread');
267+
268+
const newStripped = QPixel.MD.stripMarkdown($tgt.val(), {
269+
removeLeadingQuote: true
270+
});
271+
272+
if (newStripped === $tgt.data('old')) {
273+
$submitter.attr('disabled', 'true');
274+
}
275+
}
276+
};
277+
278+
document.querySelectorAll('.js-thread-title-field').forEach((el) => {
279+
checkThreadRename(el);
280+
});
281+
282+
$(document).on('keyup change paste', '.js-thread-title-field', (ev) => {
283+
checkThreadRename(ev.target);
284+
});
285+
286+
$(document).on('click', '.js-rename-thread', async (ev) => {
287+
ev.preventDefault();
288+
289+
const $tgt = $(ev.target);
290+
const threadID = $tgt.data('thread');
291+
const form = $tgt.closest(`form[data-thread=${threadID}]`).get(0);
292+
293+
if (form instanceof HTMLFormElement) {
294+
const { value: newTitle, dataset = {} } = form.elements['title'] ?? {};
295+
const { old } = dataset;
296+
297+
const newStripped = QPixel.MD.stripMarkdown(newTitle, {
298+
removeLeadingQuote: true
299+
});
300+
301+
if (newStripped === old) {
302+
return;
303+
}
304+
305+
form.submit();
306+
}
307+
});
308+
258309
$(document).on('click', '.js-lock-thread', async (ev) => {
259310
ev.preventDefault();
260311

app/controllers/application_controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,10 @@ def current_user
351351
helpers.current_user
352352
end
353353

354+
def system_user
355+
helpers.system_user
356+
end
357+
354358
def user_signed_in?
355359
helpers.user_signed_in?
356360
end

app/controllers/comments_controller.rb

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class CommentsController < ApplicationController
2626
before_action :check_unrestrict_access, only: [:thread_unrestrict]
2727
before_action :check_if_target_post_locked, only: [:create, :post_follow]
2828
before_action :check_if_parent_post_locked, only: [:update, :destroy]
29+
before_action :verify_moderator, only: [:thread_followers]
2930

3031
def create_thread
3132
title = params[:title]
@@ -203,8 +204,6 @@ def thread_content
203204
end
204205

205206
def thread_followers
206-
return not_found! unless current_user&.at_least_moderator?
207-
208207
@followers = ThreadFollower.where(comment_thread: @comment_thread).joins(:user, user: :community_user)
209208
.includes(:user, user: [:community_user, :avatar_attachment])
210209
respond_to do |format|
@@ -222,19 +221,24 @@ def thread_rename
222221

223222
orig_title = @comment_thread.title
224223
title = helpers.strip_markdown(params[:title], strip_leading_quote: true)
224+
225+
if orig_title == title
226+
flash[:danger] = I18n.t('comments.errors.no_rename_thread_to_current_title')
227+
redirect_to comment_thread_path(@comment_thread.id)
228+
return
229+
end
230+
225231
status = @comment_thread.update(title: title)
226232

227233
if status
228234
# Comment is owned by System so regular users can't delete it. Without
229235
# this record, the title would be attributed to the thread creator,
230236
# which can be abused.
231-
log_msg =
232-
Comment.new(post: @post,
233-
content:
234-
"Thread renamed from \\\"#{orig_title}\\\" to \\\"#{title}\\\" by @##{current_user.id}",
235-
user: User.find(-1),
236-
comment_thread: @comment_thread,
237-
has_reference: false)
237+
log_msg = Comment.new(post: @post,
238+
content: "Thread renamed from \"#{orig_title}\" to \"#{title}\" by @##{current_user.id}",
239+
user: helpers.system_user,
240+
comment_thread: @comment_thread,
241+
has_reference: false)
238242
comment_status = log_msg.save
239243
end
240244

app/controllers/users_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def filter_json(filter)
111111

112112
def filters_json
113113
system_filters = Rails.cache.fetch 'default_system_filters', expires_in: 1.day do
114-
User.find(-1).filters.to_h { |filter| [filter.name, filter_json(filter)] }
114+
system_user.filters.to_h { |filter| [filter.name, filter_json(filter)] }
115115
end
116116

117117
if user_signed_in?

app/helpers/application_helper.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,12 @@ def current_user
336336
@current_user
337337
end
338338

339+
# Gets the special System user
340+
# @return [User, nil]
341+
def system_user
342+
User.find(-1)
343+
end
344+
339345
##
340346
# Is there a user signed in on this request?
341347
# @return [Boolean]

app/views/comments/_rename_thread_modal.html.erb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"%>
77

88
<div class="modal is-with-backdrop is-small js--rename-thread-<%= thread.id %>">
9-
<%= form_tag rename_comment_thread_path(thread.id), class: 'modal--container' do %>
9+
<%= form_tag rename_comment_thread_path(thread.id),
10+
class: 'modal--container',
11+
data: { thread: thread.id } do %>
1012
<div class="modal--header">
1113
<button type="button"
1214
class="button is-close-button modal--header-button"
@@ -22,6 +24,7 @@
2224
data: { post: thread.post.id,
2325
thread: thread.id,
2426
character_count: ".js-character-count-thread-title-#{thread.id}",
27+
old: thread.title,
2528
markdown: 'strip' } %>
2629
<%= render 'shared/char_count',
2730
type: "thread-title-#{thread.id}",
@@ -30,7 +33,9 @@
3033
max: maximum_thread_title_length %>
3134
</div>
3235
<div class="modal--footer">
33-
<%= submit_tag 'Update thread', class: 'button is-muted is-filled' %>
36+
<%= submit_tag 'Update thread',
37+
class: 'button is-muted is-filled js-rename-thread',
38+
data: { thread: thread.id } %>
3439
<button type="button"
3540
class="button is-muted"
3641
data-modal=".js--rename-thread-<%= thread.id %>">Cancel</button>

config/locales/strings/en.comments.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ en:
3131
Something went wrong when trying to undelete the comment.
3232
rename_thread_generic: >
3333
Failed to rename the thread.
34+
no_rename_thread_to_current_title: >
35+
Cannot rename a thread to its current title.
3436
title_presence: >
3537
can't be empty after Markdown is removed.
3638
comment_not_posted: >

test/controllers/comments/rename_test.rb

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ class CommentsControllerTest < ActionController::TestCase
99
sign_in users(:deleter)
1010

1111
try_rename_thread(comment_threads(:normal))
12+
@thread = assigns(:comment_thread)
1213

1314
assert_response(:found)
14-
assert_not_nil assigns(:comment_thread)
15-
assert_redirected_to comment_thread_path(assigns(:comment_thread))
16-
assert_equal 'new thread title', assigns(:comment_thread).title
15+
assert_not_nil(@thread)
16+
assert_redirected_to comment_thread_path(@thread)
17+
assert_equal 'new thread title', @thread.title
1718
end
1819

1920
test 'non-moderators should not be able to rename restricted threads' do
@@ -26,12 +27,26 @@ class CommentsControllerTest < ActionController::TestCase
2627
@thread = assigns(:comment_thread)
2728

2829
assert_response(:found)
29-
assert_not_nil @thread
30+
assert_not_nil(@thread)
3031
assert_redirected_to comment_thread_path(@thread)
3132
assert_equal before_title, @thread.title
3233
end
3334
end
3435

36+
test 'should not allow renaming to the current title' do
37+
sign_in users(:admin)
38+
39+
thread = comment_threads(:normal)
40+
41+
try_rename_thread(thread, title: thread.title)
42+
@thread = assigns(:comment_thread)
43+
44+
assert_response(:found)
45+
assert_not_nil(@thread)
46+
assert_equal flash[:danger], I18n.t('comments.errors.no_rename_thread_to_current_title')
47+
assert_equal thread.title, @thread.title
48+
end
49+
3550
test 'should correctly handle invalid thread titles' do
3651
sign_in users(:admin)
3752

@@ -40,7 +55,7 @@ class CommentsControllerTest < ActionController::TestCase
4055
@thread = assigns(:comment_thread)
4156

4257
assert_response(:found)
43-
assert_not_nil @thread
58+
assert_not_nil(@thread)
4459
assert_not @thread.valid?
4560
assert_not_nil flash[:danger]
4661
end

0 commit comments

Comments
 (0)