From b79766f697fc52ee042805b8bf1110f7e7a547fd Mon Sep 17 00:00:00 2001 From: Arit Amana <32520970+msarit@users.noreply.github.com> Date: Wed, 2 Sep 2020 17:08:08 -0400 Subject: [PATCH] [deploy] [Admin Tooling] Allow admins unpublish articles from the Mod Actions Panel (#10038) --- app/assets/stylesheets/moderators.scss | 10 ++++ app/controllers/articles_controller.rb | 17 ++++++- app/javascript/actionsPanel/actionsPanel.js | 47 +++++++++++++++++-- app/policies/article_policy.rb | 4 ++ app/views/moderations/actions_panel.html.erb | 14 ++++++ config/routes.rb | 4 +- spec/policies/article_policy_spec.rb | 5 +- .../articles/articles_admin_unpublish_spec.rb | 23 +++++++++ 8 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 spec/requests/articles/articles_admin_unpublish_spec.rb diff --git a/app/assets/stylesheets/moderators.scss b/app/assets/stylesheets/moderators.scss index a46680439..1f125db9e 100644 --- a/app/assets/stylesheets/moderators.scss +++ b/app/assets/stylesheets/moderators.scss @@ -419,6 +419,16 @@ border-top: 1px solid var(--base-20); overflow-y: auto; + div.unpublish-article { + background: inherit; + border: none; + padding: var(--su-3) var(--su-4); + display: flex; + justify-content: center; + align-items: center; + cursor: auto; + } + button.other-things-btn { background: inherit; border: none; diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index b59089686..ad8884ac8 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -2,7 +2,7 @@ class ArticlesController < ApplicationController include ApplicationHelper before_action :authenticate_user!, except: %i[feed new] - before_action :set_article, only: %i[edit manage update destroy stats] + before_action :set_article, only: %i[edit manage update destroy stats admin_unpublish] before_action :raise_suspended, only: %i[new create update] before_action :set_cache_control_headers, only: %i[feed] after_action :verify_authorized @@ -190,6 +190,21 @@ class ArticlesController < ApplicationController @organization_id = @article.organization_id end + def admin_unpublish + authorize @article + if @article.has_frontmatter? + @article.body_markdown.sub!(/\npublished:\s*true\s*\n/, "\npublished: false\n") + else + @article.published = false + end + + if @article.save + render json: { message: "success", path: @article.current_state_path }, status: :ok + else + render json: { message: @article.errors.full_messages }, status: :unprocessable_entity + end + end + private def base_editor_assigments diff --git a/app/javascript/actionsPanel/actionsPanel.js b/app/javascript/actionsPanel/actionsPanel.js index 6b30ebc1c..a8c836874 100644 --- a/app/javascript/actionsPanel/actionsPanel.js +++ b/app/javascript/actionsPanel/actionsPanel.js @@ -17,9 +17,9 @@ export function addCloseListener() { } export function initializeHeight() { - document.documentElement.style.height = '100%'; - document.body.style.cssText = 'height: 100%; margin: 0; padding-top: 0; overflow-y: hidden'; + document.body.style.cssText = + 'height: 100%; margin: 0; padding-top: 0; overflow-y: hidden'; document.getElementById('page-content').style.cssText = 'margin-top: 0 !important; margin-bottom: 0;'; } @@ -34,7 +34,6 @@ function toggleDropdown(type) { } } - function applyReactedClass(category) { const upVote = document.querySelector("[data-category='thumbsup']"); const downVote = document.querySelector("[data-category='thumbsdown']"); @@ -143,6 +142,33 @@ async function updateExperienceLevel(currentUserId, articleId, rating, group) { } } +const adminUnpublishArticle = async (id, username, slug) => { + try { + const response = await request(`/articles/${id}/admin_unpublish`, { + method: 'PATCH', + body: JSON.stringify({ id, username, slug }), + credentials: 'same-origin', + }); + + const outcome = await response.json(); + + /* eslint-disable no-restricted-globals */ + if (outcome.message == 'success') { + window.top.location.assign(`${window.location.origin}${outcome.path}`); + } else { + top.addSnackbarItem({ + message: `Error: ${outcome.message}`, + addCloseButton: true, + }); + } + } catch (error) { + top.addSnackbarItem({ + message: `Error: ${error}`, + addCloseButton: true, + }); + } +}; + function toggleSubmitContainer() { document .getElementById('adjustment-reason-container') @@ -362,6 +388,21 @@ export function addBottomActionsListeners() { ); }); }); + + const unpublishArticleBtn = document.querySelector('#unpublish-article-btn'); + if (unpublishArticleBtn) { + unpublishArticleBtn.addEventListener('click', () => { + const { + articleId: id, + articleAuthor: username, + articleSlug: slug, + } = unpublishArticleBtn.dataset; + + if (confirm('You are unpublishing this article; are you sure?')) { + adminUnpublishArticle(id, username, slug); + } + }); + } } export function initializeActionsPanel() { diff --git a/app/policies/article_policy.rb b/app/policies/article_policy.rb index c5da8c04f..3d511da5f 100644 --- a/app/policies/article_policy.rb +++ b/app/policies/article_policy.rb @@ -3,6 +3,10 @@ class ArticlePolicy < ApplicationPolicy user_is_author? || user_admin? || user_org_admin? || minimal_admin? end + def admin_unpublish? + minimal_admin? + end + def new? true end diff --git a/app/views/moderations/actions_panel.html.erb b/app/views/moderations/actions_panel.html.erb index 34a591fa4..0fb689ca3 100644 --- a/app/views/moderations/actions_panel.html.erb +++ b/app/views/moderations/actions_panel.html.erb @@ -161,6 +161,20 @@ + + <% if current_user.any_admin? && @moderatable.published %> +