Adds ability for admins to create new tags via /admin/tags (#11240)
* Adds ability for admins to create new tags via /admin/tags * Merge branch 'master' of github.com:forem/forem into fdoxyz/admin-new-tags-11026 * Fix rubocop pre-hook * UI Tweaks * replaces show with edit action & other minor changes * Reverse unrelated changes * Removed more unrelated edits * Fix specs * Fix moderator link
This commit is contained in:
parent
201492c95b
commit
c740b4e6dd
11 changed files with 146 additions and 65 deletions
|
|
@ -18,7 +18,7 @@ module Admin
|
|||
flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found,
|
||||
or their account has errors: #{user&.errors_as_sentence}"
|
||||
end
|
||||
redirect_to admin_tag_path(params[:tag_id])
|
||||
redirect_to edit_admin_tag_path(params[:tag_id])
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
|
@ -31,7 +31,7 @@ module Admin
|
|||
flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found,
|
||||
or their account has errors: #{user&.errors_as_sentence}"
|
||||
end
|
||||
redirect_to admin_tag_path(tag.id)
|
||||
redirect_to edit_admin_tag_path(tag.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ module Admin
|
|||
class TagsController < Admin::ApplicationController
|
||||
layout "admin"
|
||||
|
||||
before_action :badges_for_options, only: %i[new create edit update]
|
||||
after_action only: [:update] do
|
||||
Audit::Logger.log(:moderator, current_user, params.dup)
|
||||
end
|
||||
|
|
@ -18,10 +19,26 @@ module Admin
|
|||
@tags = @tags.where("tags.name ILIKE :search", search: "%#{params[:search]}%") if params[:search].present?
|
||||
end
|
||||
|
||||
def show
|
||||
def new
|
||||
@tag = Tag.new
|
||||
end
|
||||
|
||||
def create
|
||||
@tag = Tag.new(tag_params)
|
||||
@tag.name = params[:tag][:name].downcase
|
||||
|
||||
if @tag.save
|
||||
flash[:success] = "Tag has been created!"
|
||||
redirect_to edit_admin_tag_path(@tag)
|
||||
else
|
||||
flash[:danger] = @tag.errors_as_sentence
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@tag = Tag.find(params[:id])
|
||||
@tag_moderators = User.with_role(:tag_moderator, @tag).select(:id, :username)
|
||||
@badges_for_options = Badge.pluck(:title, :id)
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
@ -31,16 +48,20 @@ module Admin
|
|||
else
|
||||
flash[:error] = "The tag update failed: #{@tag.errors_as_sentence}"
|
||||
end
|
||||
redirect_to admin_tag_path(@tag.id)
|
||||
redirect_to edit_admin_tag_path(@tag.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def badges_for_options
|
||||
@badges_for_options = Badge.pluck(:title, :id)
|
||||
end
|
||||
|
||||
def tag_params
|
||||
allowed_params = %i[
|
||||
id supported rules_markdown short_summary pretty_name bg_color_hex
|
||||
text_color_hex user_id alias_for badge_id
|
||||
category social_preview_template wiki_body_markdown
|
||||
text_color_hex user_id alias_for badge_id requires_approval
|
||||
category social_preview_template wiki_body_markdown submission_template
|
||||
]
|
||||
params.require(:tag).permit(allowed_params)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,57 +1,70 @@
|
|||
<div class="crayons-card crayons-card--content-rows">
|
||||
<h4 class="mb-4">Edit details</h4>
|
||||
<%= form_with model: [:admin, tag], data: { remote: false } do |f| %>
|
||||
<div class="crayons-field crayons-field--checkbox">
|
||||
<%= form_with model: [:admin, tag], data: { remote: false } do |f| %>
|
||||
<% if !tag.persisted? %>
|
||||
<div class="crayons-field">
|
||||
<%= f.label :name, class: "crayons-field__label" %>
|
||||
<%= f.text_field :name, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="crayons-field crayons-field--checkbox mt-3">
|
||||
<%= f.check_box :supported, class: "crayons-checkbox" %>
|
||||
<label class="crayons-field__label" for="tag_supported">
|
||||
Supported
|
||||
<p class="crayons-field__description">Allows the tag to be a searchable result when writing a post or a listing</p>
|
||||
</label>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :badge_id, class: "crayons-field__label" %>
|
||||
<%= f.select(:badge_id, options_for_select(badges_for_options, tag.badge_id), { include_blank: true }, { class: "crayons-select" }) %>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :category, class: "crayons-field__label" %>
|
||||
<%= f.select(:category, options_for_select(Tag.valid_categories, tag.category), {}, { class: "crayons-select" }) %>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :social_preview_template, class: "crayons-field__label" %>
|
||||
<%= f.select(:social_preview_template, Tag.social_preview_templates, {}, { class: "crayons-select" }) %>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :alias_for, class: "crayons-field__label" %>
|
||||
<%= f.text_field :alias_for, value: tag.alias_for, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :pretty_name, class: "crayons-field__label" %>
|
||||
<%= f.text_field :pretty_name, value: tag.pretty_name, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :requires_approval, class: "crayons-field__label" %>
|
||||
<%= f.check_box :requires_approval %>
|
||||
</div>
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :short_summary, class: "crayons-field__label" %>
|
||||
<%= f.text_field :short_summary, value: tag.short_summary, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :rules_markdown, class: "crayons-field__label" %>
|
||||
<br>
|
||||
<%= f.text_area :rules_markdown, value: tag.rules_markdown, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div class="crayons-field">
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :submission_template, class: "crayons-field__label" %>
|
||||
<%= f.text_area :submission_template, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :wiki_body_markdown, class: "crayons-field__label" %>
|
||||
<br>
|
||||
<%= f.text_area :wiki_body_markdown, value: tag.wiki_body_markdown, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<%= f.label :bg_color_hex, class: "crayons-field__label" %>
|
||||
<div class="flex items-center w-100 m:w-50">
|
||||
<%= f.text_field :bg_color_hex, class: "crayons-textfield", value: tag.bg_color_hex.presence || "#000000", placeholder: "#000000" %>
|
||||
<%= f.color_field :bg_color_hex, class: "crayons-color-selector ml-2", required: true, value: tag.bg_color_hex.presence || "#000000", placeholder: "#000000" %>
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :bg_color_hex, class: "crayons-field__label" %>
|
||||
<div class="flex items-center w-100 m:w-50">
|
||||
<%= f.text_field :bg_color_hex, class: "crayons-textfield", value: tag.bg_color_hex.presence || "#000000", placeholder: "#000000" %>
|
||||
<%= f.color_field :bg_color_hex, class: "crayons-color-selector ml-2", required: true, value: tag.bg_color_hex.presence || "#000000", placeholder: "#000000" %>
|
||||
</div>
|
||||
</div>
|
||||
<%= f.label :text_color_hex, class: "crayons-field__label" %>
|
||||
<div class="flex items-center w-100 m:w-50">
|
||||
<%= f.text_field :text_color_hex, class: "crayons-textfield", value: tag.text_color_hex.presence || "#ffffff", placeholder: "#ffffff" %>
|
||||
<%= f.color_field :text_color_hex, class: "crayons-color-selector ml-2", required: true, value: tag.text_color_hex.presence || "#ffffff", placeholder: "#ffffff" %>
|
||||
<div class="crayons-field mt-3">
|
||||
<%= f.label :text_color_hex, class: "crayons-field__label" %>
|
||||
<div class="flex items-center w-100 m:w-50">
|
||||
<%= f.text_field :text_color_hex, class: "crayons-textfield", value: tag.text_color_hex.presence || "#ffffff", placeholder: "#ffffff" %>
|
||||
<%= f.color_field :text_color_hex, class: "crayons-color-selector ml-2", required: true, value: tag.text_color_hex.presence || "#ffffff", placeholder: "#ffffff" %>
|
||||
</div>
|
||||
</div>
|
||||
<%= f.submit class: "crayons-btn mt-2" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= f.submit class: "crayons-btn mt-3" %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
</div>
|
||||
|
||||
<div class="ml-auto">
|
||||
<a href="/t/<%= @tag.name %>">View</a>
|
||||
<a href="<%= tag_path(@tag.name) %>" class="crayons-btn crayons-btn--outlined" target="_blank" rel="noopener noreferer">View</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
<% if @tag_moderators.exists? %>
|
||||
<ul class="list-none">
|
||||
<% @tag_moderators.each do |user| %>
|
||||
<li class="flex justify-between">
|
||||
<a href="/<%= URL.user(user) %>">@<%= user.username %></a>
|
||||
<li class="flex justify-between mb-2">
|
||||
<a href="<%= URL.user(user) %>" target="_blank" rel="noopener noreferer">@<%= user.username %></a>
|
||||
<%= form_with url: admin_tag_moderator_path(@tag.id), model: [:admin, @tag], method: :delete, data: { remote: false } do |f| %>
|
||||
<%= f.hidden_field :user_id, value: user.id %>
|
||||
<%= f.submit "Remove", class: "crayons-btn crayons-btn--danger crayons-btn--s" %>
|
||||
|
|
@ -41,5 +41,9 @@
|
|||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render "form", tag: @tag, badges_for_options: @badges_for_options %>
|
||||
|
||||
<div class="crayons-card crayons-card--content-rows">
|
||||
<h4>Edit details</h4>
|
||||
<%= render "form", tag: @tag, badges_for_options: @badges_for_options %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,27 +1,29 @@
|
|||
<div class="flex items-center mb-6">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="nav-item">
|
||||
<a href="/admin/tags" class="nav-link <%= "active" if params[:state].blank? %>">All</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/tags?state=supported" class="nav-link <%= "active" if params[:state] == "supported" %>">Supported</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/tags?state=unsupported" class="nav-link <%= "active" if params[:state] == "unsupported" %>">Unsupported</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="crayons-tabs">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/tags" class="crayons-tabs__item <%= "crayons-tabs__item--current" if params[:state].blank? %>">All</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/tags?state=supported" class="crayons-tabs__item <%= "crayons-tabs__item--current" if params[:state] == "supported" %>">Supported</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/tags?state=unsupported" class="crayons-tabs__item <%= "crayons-tabs__item--current" if params[:state] == "unsupported" %>">Unsupported</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto">
|
||||
<%= form_tag(admin_tags_path, method: "get") do %>
|
||||
<div class="form-group">
|
||||
<%= text_field_tag(:search, params[:search], aria: { label: "Search" }) %>
|
||||
<% if params[:state].present? %>
|
||||
<%= hidden_field_tag(:state, params[:state]) %>
|
||||
<% end %>
|
||||
<%= submit_tag("Search") %>
|
||||
</div>
|
||||
<div class="m-auto">
|
||||
<%= form_tag(admin_tags_path, method: "get", class: "inline-flex") do %>
|
||||
<%= text_field_tag(:search, params[:search], aria: { label: "Search" }, class: "crayons-textfield") %>
|
||||
<% if params[:state].present? %>
|
||||
<%= hidden_field_tag(:state, params[:state]) %>
|
||||
<% end %>
|
||||
<%= submit_tag("Search", class: "crayons-btn ml-2") %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="justify-content-end ml-auto">
|
||||
<%= link_to "Make A Tag", new_admin_tag_path, class: "crayons-btn" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= paginate @tags %>
|
||||
|
|
@ -40,7 +42,7 @@
|
|||
<tbody class="crayons-card">
|
||||
<% @tags.each do |tag| %>
|
||||
<tr>
|
||||
<td><%= link_to tag.name, admin_tag_path(tag.id) %></td>
|
||||
<td><%= link_to tag.name, edit_admin_tag_path(tag.id) %></td>
|
||||
<td><%= tag.id %></td>
|
||||
<td><%= tag.alias_for %></td>
|
||||
<td><%= tag.taggings_count %></td>
|
||||
|
|
|
|||
4
app/views/admin/tags/new.html.erb
Normal file
4
app/views/admin/tags/new.html.erb
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<div class="crayons-card p-6">
|
||||
<h4 class="mb-4">New Tag</h4>
|
||||
<%= render "form", tag: @tag, badges_for_options: @badges_for_options %>
|
||||
</div>
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
<% end %>
|
||||
<%= yield %>
|
||||
<% if @help_url %>
|
||||
<a class="admin-help-button crayons-btn crayons-btn--icon-rounded crayons-btn--s" href="<%= @help_url %>" target="_blank" rel="noopener noreferer" >
|
||||
<a class="admin-help-button crayons-btn crayons-btn--icon-rounded crayons-btn--s" href="<%= @help_url %>" target="_blank" rel="noopener noreferer">
|
||||
<%= inline_svg_tag("circle-question.svg", aria: true, title: "Forem Admin Guide") %>
|
||||
</a>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ Rails.application.routes.draw do
|
|||
post "save_status"
|
||||
end
|
||||
end
|
||||
resources :tags, only: %i[index update show] do
|
||||
resources :tags, only: %i[index new create update edit] do
|
||||
resource :moderator, only: %i[create destroy], module: "tags"
|
||||
end
|
||||
resources :users, only: %i[index show edit update] do
|
||||
|
|
|
|||
|
|
@ -2,8 +2,23 @@ require "rails_helper"
|
|||
|
||||
RSpec.describe "/admin/tags", type: :request do
|
||||
let(:super_admin) { create(:user, :super_admin) }
|
||||
let(:tag) { create(:tag) }
|
||||
let(:badge) { create(:badge) }
|
||||
let(:user) { create(:user) }
|
||||
let!(:tag) { create(:tag) }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
name: "WWW", supported: true, requires_approval: true,
|
||||
wiki_body_markdown: "## In here you'll see WWW posts",
|
||||
short_summary: "Everything WWW related ", rules_markdown: "## NO SPAM",
|
||||
submission_template: "# <TITLE>\n\n<ARTICLE_BODY>",
|
||||
pretty_name: "dubdubdub", bg_color_hex: "#333333",
|
||||
text_color_hex: "#ffffff", badge_id: badge.id, category: "site_mechanic",
|
||||
social_preview_template: "article"
|
||||
}
|
||||
end
|
||||
let(:post_resource) { post "/admin/tags", params: { tag: params } }
|
||||
let(:put_resource) { put "/admin/tags/#{tag.id}", params: { tag: params } }
|
||||
|
||||
before do
|
||||
sign_in super_admin
|
||||
|
|
@ -18,8 +33,32 @@ RSpec.describe "/admin/tags", type: :request do
|
|||
|
||||
describe "GET /admin/tags/:id" do
|
||||
it "responds with 200 OK" do
|
||||
get admin_tag_path(tag.id)
|
||||
get edit_admin_tag_path(tag.id)
|
||||
expect(response.status).to eq 200
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /admin/tags" do
|
||||
it "creates a new tag" do
|
||||
expect do
|
||||
post_resource
|
||||
end.to change { Tag.all.count }.by(1)
|
||||
expect(response.body).to redirect_to edit_admin_tag_path(Tag.last)
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT /admin/tags" do
|
||||
it "updates Tag" do
|
||||
put_resource
|
||||
tag.reload
|
||||
expect(tag.requires_approval).to eq(params[:requires_approval])
|
||||
expect(tag.wiki_body_markdown).to eq(params[:wiki_body_markdown])
|
||||
expect(tag.short_summary).to eq(params[:short_summary])
|
||||
expect(tag.rules_markdown).to eq(params[:rules_markdown])
|
||||
expect(tag.submission_template).to eq(params[:submission_template])
|
||||
expect(tag.pretty_name).to eq(params[:pretty_name])
|
||||
expect(tag.bg_color_hex).to eq(params[:bg_color_hex])
|
||||
expect(tag.text_color_hex).to eq(params[:text_color_hex])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ RSpec.describe "StoriesShow", type: :request do
|
|||
it "preserves internal nav param (i=i) upon redirect" do
|
||||
old_path = article.path
|
||||
article.update(organization: org)
|
||||
|
||||
get "#{old_path}?i=i"
|
||||
expect(response.body).to redirect_to "#{article.path}?i=i"
|
||||
expect(response).to have_http_status(:moved_permanently)
|
||||
|
|
@ -39,7 +38,6 @@ RSpec.describe "StoriesShow", type: :request do
|
|||
it "does not have ?i=i on redirects without that precise param" do
|
||||
old_path = article.path
|
||||
article.update(organization: org)
|
||||
|
||||
get "#{old_path}?i=j"
|
||||
expect(response.body).to redirect_to article.path
|
||||
expect(response.body).not_to redirect_to "#{article.path}?i=j"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ RSpec.describe "Admin updates a tag", type: :system do
|
|||
context "when no colors have been choosen for the tag" do
|
||||
before do
|
||||
sign_in super_admin
|
||||
visit admin_tag_path(tag.id)
|
||||
visit edit_admin_tag_path(tag.id)
|
||||
end
|
||||
|
||||
it "defaults to white text for the color picker" do
|
||||
|
|
@ -26,7 +26,7 @@ RSpec.describe "Admin updates a tag", type: :system do
|
|||
context "when colors have already been choosen for the tag" do
|
||||
before do
|
||||
sign_in super_admin
|
||||
visit "/admin/tags/#{tag.id}"
|
||||
visit edit_admin_tag_path(tag)
|
||||
end
|
||||
|
||||
it "remains the same color it was unless otherwise updated via the color picker" do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue