diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb index 4a6fca12b..bc0dcd298 100644 --- a/app/controllers/admin/application_controller.rb +++ b/app/controllers/admin/application_controller.rb @@ -13,6 +13,7 @@ module Admin { name: "chat_channels", controller: "chat_channels" }, { name: "comments", controller: "comments" }, { name: "config", controller: "config" }, + { name: "display_ads", controller: "display_ads" }, { name: "events", controller: "events" }, { name: "growth", controller: "growth" }, { name: "listings", controller: "listings" }, diff --git a/app/controllers/admin/display_ads_controller.rb b/app/controllers/admin/display_ads_controller.rb new file mode 100644 index 000000000..c8933b01c --- /dev/null +++ b/app/controllers/admin/display_ads_controller.rb @@ -0,0 +1,70 @@ +module Admin + class DisplayAdsController < Admin::ApplicationController + layout "admin" + + def index + @display_ads = DisplayAd.order(id: :desc) + .joins(:organization) + .includes([:organization]) + .page(params[:page]).per(50) + + return if params[:search].blank? + + @display_ads = @display_ads.where("organizations.name ILIKE :search", search: "%#{params[:search]}%") + end + + def new + @display_ad = DisplayAd.new + end + + def edit + @display_ad = DisplayAd.find(params[:id]) + end + + def create + @display_ad = DisplayAd.new(display_ad_params) + + if @display_ad.save + flash[:success] = "Display Ad has been created!" + redirect_to admin_display_ads_path + else + flash[:danger] = @display_ad.errors_as_sentence + render new_admin_display_ad_path + end + end + + def update + @display_ad = DisplayAd.find(params[:id]) + + if @display_ad.update(display_ad_params) + flash[:success] = "Display Ad has been updated!" + redirect_to admin_display_ads_path + else + flash[:danger] = @display_ad.errors_as_sentence + render :edit + end + end + + def destroy + @display_ad = DisplayAd.find(params[:id]) + + if @display_ad.destroy + flash[:success] = "Display Ad has been deleted!" + redirect_to admin_display_ads_path + else + flash[:danger] = "Something went wrong with deleting the Display Ad." + render :edit + end + end + + private + + def display_ad_params + params.permit(:organization_id, :body_markdown, :placement_area, :published, :approved) + end + + def authorize_admin + authorize DisplayAd, :access?, policy_class: InternalPolicy + end + end +end diff --git a/app/dashboards/dashboard_manifest.rb b/app/dashboards/dashboard_manifest.rb index aa25438f0..181497f9f 100644 --- a/app/dashboards/dashboard_manifest.rb +++ b/app/dashboards/dashboard_manifest.rb @@ -22,7 +22,6 @@ class DashboardManifest tags email_messages feedback_messages - display_ads badges badge_achievements html_variants diff --git a/app/lib/constants/role.rb b/app/lib/constants/role.rb index f43fb0188..589a98347 100644 --- a/app/lib/constants/role.rb +++ b/app/lib/constants/role.rb @@ -16,6 +16,7 @@ module Constants "Resource Admin: Page", "Resource Admin: FeedbackMessage", "Resource Admin: Config", - "Resource Admin: Broadcast"].freeze + "Resource Admin: Broadcast", + "Resource Admin: DisplayAd"].freeze end end diff --git a/app/models/display_ad.rb b/app/models/display_ad.rb index 8ec9619ec..552437e26 100644 --- a/app/models/display_ad.rb +++ b/app/models/display_ad.rb @@ -1,4 +1,6 @@ class DisplayAd < ApplicationRecord + resourcify + belongs_to :organization has_many :display_ad_events, dependent: :destroy diff --git a/app/views/admin/display_ads/_form.html.erb b/app/views/admin/display_ads/_form.html.erb new file mode 100644 index 000000000..b86fa4b5a --- /dev/null +++ b/app/views/admin/display_ads/_form.html.erb @@ -0,0 +1,24 @@ +
+ <%= label_tag :organization_id, "Organization ID:" %> + <%= text_field_tag :organization_id, @display_ad.organization_id, class: "form-control" %> +
+ +
+ <%= label_tag :body_markdown, "Body Markdown:" %> + <%= text_area_tag :body_markdown, @display_ad.body_markdown, size: "100x10", class: "form-control" %> +
+ +
+ <%= label_tag :placement_area, "Placement Area:" %> + <%= select_tag :placement_area, options_for_select(%w[sidebar_left sidebar_right], selected: @display_ad.placement_area), include_blank: true %> +
+ +
+ <%= label_tag :published, "Published:" %> + <%= select_tag :published, options_for_select([false, true], selected: @display_ad.published) %> +
+ +
+ <%= label_tag :approved, "Approved:" %> + <%= select_tag :approved, options_for_select([false, true], selected: @display_ad.approved) %> +
diff --git a/app/views/admin/display_ads/edit.html.erb b/app/views/admin/display_ads/edit.html.erb new file mode 100644 index 000000000..04771823c --- /dev/null +++ b/app/views/admin/display_ads/edit.html.erb @@ -0,0 +1,7 @@ +

Edit Display Ad:

+
+ <%= form_for([:admin, @display_ad], method: :patch) do %> + <%= render "form" %> + <%= submit_tag "Update Display Ad", class: "btn btn-primary" %> + <% end %> +
diff --git a/app/views/admin/display_ads/index.html.erb b/app/views/admin/display_ads/index.html.erb new file mode 100644 index 000000000..c5d383aa5 --- /dev/null +++ b/app/views/admin/display_ads/index.html.erb @@ -0,0 +1,43 @@ + + +<%= paginate @display_ads %> + + + + + + + + + + + + + + + <% @display_ads.each do |display_ad| %> + + + + + + + + + + + + <% end %> + +
IDOrganizationPlacement AreaBody MarkdownPublishedApprovedSuccess Rate
<%= link_to display_ad.id, edit_admin_display_ad_path(display_ad) %><%= link_to display_ad.organization.name, admin_organization_path(display_ad.organization) %><%= display_ad.placement_area %><%= display_ad.body_markdown.truncate(30) %><%= display_ad.published %><%= display_ad.approved %><%= display_ad.success_rate %><%= link_to "Edit", edit_admin_display_ad_path(display_ad), class: "crayons-btn" %><%= link_to "Destroy", admin_display_ad_path(display_ad), class: "crayons-btn crayons-btn--danger", method: :delete, data: { confirm: "Are you sure?" } %>
+ +<%= paginate @display_ads %> diff --git a/app/views/admin/display_ads/new.html.erb b/app/views/admin/display_ads/new.html.erb new file mode 100644 index 000000000..5d6c01c4c --- /dev/null +++ b/app/views/admin/display_ads/new.html.erb @@ -0,0 +1,7 @@ +

Make a new Display Ad:

+
+ <%= form_for([:admin, @display_ad], method: :post) do %> + <%= render "form" %> + <%= submit_tag "Create Display Ad", class: "btn btn-primary" %> + <% end %> +
diff --git a/config/routes.rb b/config/routes.rb index fa7cfd65b..856b6f5a3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -121,6 +121,7 @@ Rails.application.routes.draw do resources :webhook_endpoints, only: :index resource :config resources :badges, only: %i[index edit update new create] + resources :display_ads, only: %i[index edit update new create destroy] # These redirects serve as a safegaurd to prevent 404s for any Admins # who have the old badge_achievement URLs bookmarked. get "/badges/badge_achievements", to: redirect("/admin/badge_achievements") diff --git a/spec/requests/admin/display_ads_spec.rb b/spec/requests/admin/display_ads_spec.rb new file mode 100644 index 000000000..d015e7398 --- /dev/null +++ b/spec/requests/admin/display_ads_spec.rb @@ -0,0 +1,140 @@ +require "rails_helper" +require "requests/shared_examples/internal_policy_dependant_request" + +RSpec.describe "/admin/display_ads", type: :request do + let(:get_resource) { get "/admin/display_ads" } + let(:org) { create(:organization) } + let(:params) do + { organization_id: org.id, body_markdown: "[Click here!](https://example.com)", placement_area: "sidebar_left", + approved: true, published: true } + end + let(:post_resource) { post "/admin/display_ads", params: params } + + it_behaves_like "an InternalPolicy dependant request", DisplayAd do + let(:request) { get_resource } + end + + context "when the user is not an admin" do + let(:user) { create(:user) } + + before { sign_in user } + + describe "GET /admin/display_ads" do + it "blocks the request" do + expect { get_resource }.to raise_error(Pundit::NotAuthorizedError) + end + end + + describe "POST /admin/display_ads" do + it "blocks the request" do + expect { post_resource }.to raise_error(Pundit::NotAuthorizedError) + end + end + end + + context "when the user is a super admin" do + let(:super_admin) { create(:user, :super_admin) } + + before { sign_in super_admin } + + describe "GET /admin/display_ads" do + it "allows the request" do + get_resource + expect(response).to have_http_status(:ok) + end + end + + describe "POST /admin/display_ads" do + it "creates a new display_ad" do + expect do + post_resource + end.to change { DisplayAd.all.count }.by(1) + end + end + + describe "PUT /admin/display_ads" do + let!(:display_ad) { create(:display_ad, approved: false) } + + it "updates DisplayAd's approved value" do + Timecop.freeze(Time.current) do + expect do + put "/admin/display_ads/#{display_ad.id}", params: params + end.to change { display_ad.reload.approved }.from(false).to(true) + end + end + end + + describe "DELETE /admin/display_ads/:id" do + let!(:display_ad) { create(:display_ad) } + + it "deletes the Display Ad" do + expect do + delete "/admin/display_ads/#{display_ad.id}" + end.to change { DisplayAd.all.count }.by(-1) + expect(response.body).to redirect_to "/admin/display_ads" + end + end + end + + context "when the user is a single resource admin" do + let(:single_resource_admin) { create(:user, :single_resource_admin, resource: DisplayAd) } + + before { sign_in single_resource_admin } + + describe "GET /admin/display_ads" do + it "allows the request" do + get_resource + expect(response).to have_http_status(:ok) + end + end + + describe "POST /admin/display_ads" do + it "creates a new display_ad" do + expect do + post_resource + end.to change { DisplayAd.all.count }.by(1) + end + end + + describe "PUT /admin/display_ads" do + let!(:display_ad) { create(:display_ad, approved: false) } + + it "updates DisplayAd's approved value" do + Timecop.freeze(Time.current) do + expect do + put "/admin/display_ads/#{display_ad.id}", params: params + end.to change { display_ad.reload.approved }.from(false).to(true) + end + end + end + + describe "DELETE /admin/display_ads/:id" do + let!(:display_ad) { create(:display_ad) } + + it "deletes the Display Ad" do + expect do + delete "/admin/display_ads/#{display_ad.id}" + end.to change { DisplayAd.all.count }.by(-1) + expect(response.body).to redirect_to "/admin/display_ads" + end + end + end + + context "when the user is the wrong single resource admin" do + let(:single_resource_admin) { create(:user, :single_resource_admin, resource: Article) } + + before { sign_in single_resource_admin } + + describe "GET /admin/display_ads" do + it "blocks the request" do + expect { get_resource }.to raise_error(Pundit::NotAuthorizedError) + end + end + + describe "POST /admin/display_ads" do + it "blocks the request" do + expect { post_resource }.to raise_error(Pundit::NotAuthorizedError) + end + end + end +end