Please teach me to fish: how do I navigate the edit/suggested-edit code to apply the same error check to both? Question
I was trying to fix what I thought was an easy bug: suggested edits can add restricted tags (the ones only moderators can apply), which creates a suggested edit that most reviewers can't approve. We already prevent you from adding those tags when you directly edit a post, so I figured -- how hard could it be to apply that same logic to suggested edits?
Harder than I thought, apparently. I don't know if my mental block is due to insufficient Ruby/Rails knowledge, insufficient MVC clues, or something about the QPixel code in particular.
Editing through either path leads to the same form, in app/views/posts/edit.html.erb
. For direct saves, there's this check:
<% if @post.errors.any? %>
<div class="notice is-danger is-filled">
<p>The following errors prevented your post from being saved:</p>
<ul>
<% @post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Ok, I said -- how do the errors get there? This question led me to app/controllers/posts_controller.rb
and specifically the update
function. I'm having a little trouble following the logic, but the outline seems to be (in pseudocode):
if current user can edit
if current user can push to network
(lots of stuff we don't care about here)
else (do an edit in a transaction)
else // current user cannot edit
create suggested edit and send notification to owner
That last part, in a little more detail:
data = {
post: @post,
user: current_user,
body: body_rendered == @post.body ? nil : body_rendered,
title: params[:post][:title] == @post.title ? nil : params[:post][:title],
tags_cache: new_tags_cache == @post.tags_cache ? @post.tags_cache : new_tags_cache,
body_markdown: params[:post][:body_markdown] == @post.body_markdown ? nil : params[:post][:body_markdown],
comment: params[:edit_comment],
active: true, accepted: false
}
// NOTE the absence of any error checks here
edit = SuggestedEdit.new(data)
This seems to be the place where I should check for errors, same as we do for direct edits. The first block of code I quoted in this question is how we report errors to the user, but where do they get checked and set?
I grepped for the text of the error message, which led me to app/models/post.rb
, in a function called moderator_tags
. A comment in the source code says "intended to be used for validation" -- sounds like just what I need! So then I grepped for that function name, looking for where it's called.
That's where I got stuck:
$ grep -r moderator_tags app/*
app/controllers/tags_controller.rb: exec_sql([sql.gsub('$TABLENAME', 'categories_moderator_tags'), @primary.id, @subordinate.id])
app/controllers/tags_controller.rb: tables = ['posts_tags', 'categories_moderator_tags', 'categories_required_tags', 'categories_topic_tags',
app/controllers/tags_controller.rb~: exec_sql([sql.gsub('$TABLENAME', 'categories_moderator_tags'), @primary.id, @subordinate.id])
app/controllers/tags_controller.rb~: tables = ['posts_tags', 'categories_moderator_tags', 'categories_required_tags', 'categories_topic_tags',
app/controllers/admin_controller.rb: meta_category.moderator_tags << Tag.unscoped.where(community: @community, name: status_tags)
app/models/category.rb: has_and_belongs_to_many :moderator_tags, class_name: 'Tag', join_table: 'categories_moderator_tags'
app/models/post.rb: validate :moderator_tags, if: -> { post_type.has_tags && post_type.has_category && tags_cache_changed? }
app/models/post.rb: def moderator_tags
app/models/post.rb: mod_tags = category&.moderator_tags&.map(&:name)
app/views/categories/_form.html.erb: <%= f.select :moderator_tag_ids, options_for_select(@category.moderator_tags.map { |t| [t.name, t.id] },
What should I do next? How do I find how that validation is done for regular edits, so I can (I hope) apply it to the suggested-edit case and prevent an unapprovable suggestion from getting created?
0 comment threads