From 158f56bc9de24962173a2939922691a81dc3f070 Mon Sep 17 00:00:00 2001 From: Ben Halpern Date: Wed, 1 May 2019 09:00:09 -0400 Subject: [PATCH] Add page for users to "manage" their posts after publication (distribution, etc.) (#2627) * Set up basic /manage route * Add basic article manage page * Add tips and finalize permissions for /manage * Add final test and add manage button to article show * Update failing specs --- .../initializers/initializeBaseUserData.js | 4 +- app/assets/stylesheets/article-show.scss | 3 +- app/assets/stylesheets/dashboard.scss | 97 ++++++++++++++++++- app/controllers/articles_controller.rb | 11 ++- app/controllers/buffer_updates_controller.rb | 2 +- app/models/rating_vote.rb | 2 +- app/policies/application_policy.rb | 4 + app/policies/buffer_update_policy.rb | 8 +- app/views/articles/manage.html.erb | 67 +++++++++++++ .../dashboards/_dashboard_article.html.erb | 12 ++- app/views/dashboards/show.html.erb | 4 +- config/routes.rb | 1 + spec/models/rating_vote_spec.rb | 7 ++ spec/policies/article_policy_spec.rb | 10 +- spec/policies/buffer_update_policy_spec.rb | 10 +- spec/requests/articles_spec.rb | 15 +++ spec/requests/buffer_updates_spec.rb | 9 +- .../articles/user_deletes_an_article_spec.rb | 1 + 18 files changed, 240 insertions(+), 27 deletions(-) create mode 100644 app/views/articles/manage.html.erb diff --git a/app/assets/javascripts/initializers/initializeBaseUserData.js b/app/assets/javascripts/initializers/initializeBaseUserData.js index 23d9359dd..c55abb5f0 100644 --- a/app/assets/javascripts/initializers/initializeBaseUserData.js +++ b/app/assets/javascripts/initializers/initializeBaseUserData.js @@ -76,7 +76,9 @@ function addRelevantButtonsToArticle(user) { document.getElementById('action-space').innerHTML = 'EDIT POST'; + '/edit" rel="nofollow">EDITMANAGE'; } else if (user.trusted) { document.getElementById('action-space').innerHTML = ' +
+
+
+ Dashboard πŸ‘‰ Manage Your Post +
+

Tools:

+

Suggest a Tweet: Help get your post in front of more developers by suggesting a tweet that @ThePracticalDev editors can use. +

Experience Level of Post: The best target dev experience levelβ€” a gentle indicator to help show the post to the people who will benefit the most. +

Tips:

+
    +
  1. + Make your own tweet about the post, describing personally why you wrote it or why people might find it useful. @ThePracticalDev may retweet you. +
  2. +
  3. + Share to link aggregators such as Reddit. Try to choose the most topic-specific subreddits, such as /r/javascript instead of /r/programming. Warning: jerks and trolls may be lurking. +
  4. +
  5. + Share with your co-workers or local communities. Ask people to leave questions for you in the comments. It's a great way to spark additional discussion. +
  6. +
+
+
+ +
+ <%= render "dashboards/dashboard_article", article: @article, org_admin: @user.org_admin, manage_view: true %> + <% if @buffer_updates.any? %> +
+ Tweet Suggestion Sent πŸ™ +
+ <% else %> +
+ <%= form_for(BufferUpdate.new) do |f| %> +

Suggest a Tweet

+ <%= f.hidden_field :article_id, value: @article.id %> + <%= f.text_area :body_text, minlength: 10, maxlength: 220 %> + <%= f.submit "Share Tweet Suggestion" %> +

Suggestion can be a quote, summary, or bullet points from the post.

+ <% end %> +
+ <% end %> +
+ <%= form_for(RatingVote.new) do |f| %> +

Experience Level of Post

+ <%= f.hidden_field :article_id, value: @article.id %> + <%= f.hidden_field :group, value: "experience_level" %> + + + + + + + + + + + + + + + + +

Who is this post most relevant for?

+

πŸ‘ˆ Total NewbiesSenior Devs πŸ‘‰

+ <% end %> +
+
diff --git a/app/views/dashboards/_dashboard_article.html.erb b/app/views/dashboards/_dashboard_article.html.erb index e0fa181f7..3538d8358 100644 --- a/app/views/dashboards/_dashboard_article.html.erb +++ b/app/views/dashboards/_dashboard_article.html.erb @@ -21,10 +21,14 @@ DRAFT <% end %> EDIT - DELETE - <%= form_for(article, url: "/article_mutes/#{article.id}", method: "patch", html: { class: "mute-form" }) do |f| %> - <%= f.hidden_field :receive_notifications, value: !article.receive_notifications %> - <%= f.submit article.receive_notifications ? "MUTE NOTIFICATIONS" : "NOTIFICATIONS MUTED", class: "cta pill #{article.receive_notifications ? 'mute' : 'unmute'}" %> + <% if manage_view %> + <%= form_for(article, url: "/article_mutes/#{article.id}", method: "patch", html: { class: "mute-form" }) do |f| %> + <%= f.hidden_field :receive_notifications, value: !article.receive_notifications %> + <%= f.submit article.receive_notifications ? "MUTE NOTIFICATIONS" : "NOTIFICATIONS MUTED", class: "cta pill #{article.receive_notifications ? 'mute' : 'unmute'}" %> + <% end %> + DELETE + <% elsif article.published %> + MANAGE <% end %> <% if article.published %> diff --git a/app/views/dashboards/show.html.erb b/app/views/dashboards/show.html.erb index be7623b41..cb3874cb4 100644 --- a/app/views/dashboards/show.html.erb +++ b/app/views/dashboards/show.html.erb @@ -17,7 +17,7 @@ <% if @user.org_admin && @user.organization && params[:which] == "organization" %> <%= render "analytics" %> <% @articles.each do |article| %> - <%= render "dashboard_article", article: article, org_admin: true %> + <%= render "dashboard_article", article: article, org_admin: true, manage_view: false %> <% end %> <% elsif @articles.any? %> <%= render "analytics" %> @@ -31,7 +31,7 @@ <%= select_tag "dashhboard_sort", options_for_select(sort_options, params[:sort]) %> <% @articles.each do |article| %> - <%= render "dashboard_article", article: article, org_admin: false %> + <%= render "dashboard_article", article: article, org_admin: false, manage_view: false %> <% end %> <% else %>
diff --git a/config/routes.rb b/config/routes.rb index 6d42f8cb6..b6507cb18 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -333,6 +333,7 @@ Rails.application.routes.draw do get "/:username/:slug/:view" => "stories#show", constraints: { view: /moderate/ } get "/:username/:slug/mod" => "moderations#article" + get "/:username/:slug/manage" => "articles#manage" get "/:username/:slug/edit" => "articles#edit" get "/:username/:slug/delete_confirm" => "articles#delete_confirm" get "/:username/:view" => "stories#index", diff --git a/spec/models/rating_vote_spec.rb b/spec/models/rating_vote_spec.rb index d518258f9..05cbc6935 100644 --- a/spec/models/rating_vote_spec.rb +++ b/spec/models/rating_vote_spec.rb @@ -52,5 +52,12 @@ RSpec.describe RatingVote, type: :model do rating = build(:rating_vote, article_id: article.id, user_id: nontrusted_user.id) expect(rating).not_to be_valid end + + it "does allows author to make rating on own post" do + nontrusted_user = create(:user) + article = create(:article, user_id: nontrusted_user.id) + rating = build(:rating_vote, article_id: article.id, user_id: nontrusted_user.id) + expect(rating).to be_valid + end end end diff --git a/spec/policies/article_policy_spec.rb b/spec/policies/article_policy_spec.rb index a75edc62c..509789b78 100644 --- a/spec/policies/article_policy_spec.rb +++ b/spec/policies/article_policy_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" RSpec.describe ArticlePolicy do subject { described_class.new(user, article) } - let(:article) { build(:article) } + let(:article) { create(:article) } let(:valid_attributes) do %i[title body_html body_markdown main_image published description allow_small_edits allow_big_edits tag_list publish_under_org @@ -20,20 +20,20 @@ RSpec.describe ArticlePolicy do let(:user) { create(:user) } it { is_expected.to permit_actions(%i[new create preview]) } - it { is_expected.to forbid_actions(%i[update delete_confirm destroy]) } + it { is_expected.to forbid_actions(%i[update edit manage delete_confirm destroy]) } context "with banned status" do before { user.add_role :banned } it { is_expected.to permit_actions(%i[new preview]) } - it { is_expected.to forbid_actions(%i[create update delete_confirm destroy]) } + it { is_expected.to forbid_actions(%i[create edit manage update delete_confirm destroy]) } end end context "when user is the author" do let(:user) { article.user } - it { is_expected.to permit_actions(%i[update new create delete_confirm destroy preview]) } + it { is_expected.to permit_actions(%i[update edit manage new create delete_confirm destroy preview]) } it { is_expected.to permit_mass_assignment_of(valid_attributes) } context "with banned status" do @@ -46,6 +46,6 @@ RSpec.describe ArticlePolicy do context "when user is a super_admin" do let(:user) { build(:user, :super_admin) } - it { is_expected.to permit_actions(%i[update new create delete_confirm destroy preview]) } + it { is_expected.to permit_actions(%i[update new edit manage create delete_confirm destroy preview]) } end end diff --git a/spec/policies/buffer_update_policy_spec.rb b/spec/policies/buffer_update_policy_spec.rb index 3da642bbd..f7a316623 100644 --- a/spec/policies/buffer_update_policy_spec.rb +++ b/spec/policies/buffer_update_policy_spec.rb @@ -1,19 +1,13 @@ require "rails_helper" RSpec.describe BufferUpdatePolicy do - subject { described_class.new(user, block) } + subject { described_class.new(user, article) } - let(:block) { build(:block) } + let(:article) { build(:article) } context "when user is trusted" do let(:user) { build(:user, :trusted) } it { is_expected.to permit_actions(%i[create]) } end - - context "when user is not trusted" do - let(:user) { build(:user) } - - it { is_expected.to forbid_actions(%i[create]) } - end end diff --git a/spec/requests/articles_spec.rb b/spec/requests/articles_spec.rb index 0c0c3c7b1..9b628fed3 100644 --- a/spec/requests/articles_spec.rb +++ b/spec/requests/articles_spec.rb @@ -73,4 +73,19 @@ RSpec.describe "Articles", type: :request do end end end + + describe "GET /:path/edit" do + before { sign_in user } + + it "returns a new article" do + article = create(:article, user_id: user.id) + get "#{article.path}/manage" + expect(response.body).to include("Manage Your Post") + end + it "returns unauthorized if user not author" do + second_user = create(:user) + article = create(:article, user_id: second_user.id) + expect { get "#{article.path}/manage" }.to raise_error(Pundit::NotAuthorizedError) + end + end end diff --git a/spec/requests/buffer_updates_spec.rb b/spec/requests/buffer_updates_spec.rb index c85496f33..0704b1567 100644 --- a/spec/requests/buffer_updates_spec.rb +++ b/spec/requests/buffer_updates_spec.rb @@ -4,6 +4,7 @@ RSpec.describe "BufferUpdates", type: :request do let(:user) { create(:user) } let(:mod_user) { create(:user) } let(:article) { create(:article, user_id: user.id) } + let(:mod_article) { create(:article, user_id: mod_user.id) } let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) } context "when trusted user is logged in" do @@ -58,9 +59,15 @@ RSpec.describe "BufferUpdates", type: :request do expect do post "/buffer_updates", params: - { buffer_update: { body_text: "This is the text!!!!", tag_id: "javascript", article_id: article.id } } + { buffer_update: { body_text: "This is the text!!!!", tag_id: "javascript", article_id: mod_article.id } } end.to raise_error(Pundit::NotAuthorizedError) end + it "accepts buffer update from author of article" do + post "/buffer_updates", + params: + { buffer_update: { body_text: "This is the text!!!!", tag_id: "javascript", article_id: article.id } } + expect(BufferUpdate.first.body_text).to include(article.path) + end end # it "updates last buffered at" do diff --git a/spec/system/articles/user_deletes_an_article_spec.rb b/spec/system/articles/user_deletes_an_article_spec.rb index 2bf73c2b5..f69330ca8 100644 --- a/spec/system/articles/user_deletes_an_article_spec.rb +++ b/spec/system/articles/user_deletes_an_article_spec.rb @@ -6,6 +6,7 @@ RSpec.describe "Deleting Article", type: :system do it "author of article deletes own article" do sign_in article.user visit "/dashboard" + click_on "MANAGE" click_on "DELETE" click_on "DELETE" # This is for confirming deletion expect(page).to have_text("Write your first post now")