From 3c7592657cdb4aeb54bfc6790038c1dfa69177ab Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Mon, 31 May 2021 13:32:31 +0200 Subject: [PATCH] Add activity filters [SCI-5765] --- .../javascripts/global_activities/index.js | 33 +++++++++++- .../global_activities_controller.rb | 19 +++++++ app/models/activity_filter.rb | 6 +++ app/permissions/organization.rb | 4 ++ .../_save_filter_modal.html.erb | 25 +++++++++ .../global_activities/_top_pane.html.erb | 5 ++ app/views/global_activities/index.html.erb | 2 + config/locales/en.yml | 1 + config/locales/global_activities/en.yml | 7 +++ config/routes.rb | 1 + .../20210531114633_create_activity_filters.rb | 10 ++++ db/structure.sql | 52 ++++++++++++++++++- 12 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 app/models/activity_filter.rb create mode 100644 app/views/global_activities/_save_filter_modal.html.erb create mode 100644 db/migrate/20210531114633_create_activity_filters.rb diff --git a/app/assets/javascripts/global_activities/index.js b/app/assets/javascripts/global_activities/index.js index 4efc1e2e1..20c5fab2c 100644 --- a/app/assets/javascripts/global_activities/index.js +++ b/app/assets/javascripts/global_activities/index.js @@ -1,4 +1,4 @@ -/* global animateSpinner globalActivities */ +/* global animateSpinner globalActivities HelperModule */ 'use strict'; @@ -59,6 +59,37 @@ }); } + function validateActivityFilterName() { + let filterName = $('#saveFilterModal .activity-filter-name-input').val(); + $('#saveFilterModal .btn-confirm').prop('disabled', filterName.length === 0); + } + + $('#saveFilterModal') + .on('keyup', '.activity-filter-name-input', function() { + validateActivityFilterName(); + }) + .on('click', '.btn-confirm', function() { + $.ajax({ + url: this.dataset.saveFilterUrl, + type: 'POST', + global: false, + dataType: 'json', + data: { + name: $('#saveFilterModal .activity-filter-name-input').val(), + filter: globalActivities.getFilters() + }, + success: function(data) { + HelperModule.flashAlertMsg(data.message, 'success'); + $('#saveFilterModal .activity-filter-name-input').val(''); + validateActivityFilterName(); + $('#saveFilterModal').modal('hide'); + }, + error: function(response) { + HelperModule.flashAlertMsg(response.responseJSON.errors.join(','), 'danger'); + } + }); + }); + initExpandCollapseAllButtons(); initShowMoreButton(); }()); diff --git a/app/controllers/global_activities_controller.rb b/app/controllers/global_activities_controller.rb index a0b79c68b..67c493147 100644 --- a/app/controllers/global_activities_controller.rb +++ b/app/controllers/global_activities_controller.rb @@ -3,6 +3,8 @@ class GlobalActivitiesController < ApplicationController include InputSanitizeHelper + before_action :check_create_activity_filter_permissions, only: :save_activity_filter + def index # Preload filter format # { @@ -106,8 +108,21 @@ class GlobalActivitiesController < ApplicationController render json: get_objects(Report) end + def save_activity_filter + activity_filter = ActivityFilter.new(activity_filter_params) + if activity_filter.save + render json: { message: t('global_activities.index.activity_filter_saved') } + else + render json: { errors: activity_filter.errors.full_messages }, status: :unprocessable_entity + end + end + private + def check_create_activity_filter_permissions + render_403 && return unless can_create_acitivity_filters? + end + def get_objects(subject) query = subject_search_params[:query] teams = @@ -138,6 +153,10 @@ class GlobalActivitiesController < ApplicationController matched.map { |pr| { value: pr[0], label: escape_input(pr[1]) } } end + def activity_filter_params + params.permit(:name, filter: {}) + end + def activity_filters params.permit( :page, :starting_timestamp, :from_date, :to_date, types: [], subjects: {}, users: [], teams: [] diff --git a/app/models/activity_filter.rb b/app/models/activity_filter.rb new file mode 100644 index 000000000..9006cd447 --- /dev/null +++ b/app/models/activity_filter.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class ActivityFilter < ApplicationRecord + validates :name, presence: true + validates :filter, presence: true +end diff --git a/app/permissions/organization.rb b/app/permissions/organization.rb index 47e7891b9..0c06f1087 100644 --- a/app/permissions/organization.rb +++ b/app/permissions/organization.rb @@ -8,5 +8,9 @@ module Organization can :create_teams do |_| true end + + can :create_acitivity_filters do + ENV['WEBHOOKS_ENABLED'] == 'true' + end end end diff --git a/app/views/global_activities/_save_filter_modal.html.erb b/app/views/global_activities/_save_filter_modal.html.erb new file mode 100644 index 000000000..8014d5d63 --- /dev/null +++ b/app/views/global_activities/_save_filter_modal.html.erb @@ -0,0 +1,25 @@ + diff --git a/app/views/global_activities/_top_pane.html.erb b/app/views/global_activities/_top_pane.html.erb index b4586d2e1..e83abcd3c 100644 --- a/app/views/global_activities/_top_pane.html.erb +++ b/app/views/global_activities/_top_pane.html.erb @@ -17,6 +17,11 @@
+ <% if can_create_acitivity_filters? %> +
+ <%= t('global_activities.index.save_filter') %> +
+ <% end %>
<%= t('global_activities.index.clear_filters') %>
diff --git a/app/views/global_activities/index.html.erb b/app/views/global_activities/index.html.erb index 12dae178c..d4193a172 100644 --- a/app/views/global_activities/index.html.erb +++ b/app/views/global_activities/index.html.erb @@ -32,6 +32,8 @@
+<%= render partial: 'save_filter_modal' %> + diff --git a/config/locales/en.yml b/config/locales/en.yml index f2ed6495c..b4a8cab78 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1682,6 +1682,7 @@ en: activities_counter_label: " activities" expand_all: "Expand all" collapse_all: "Collapse all" + modal: modal_title: "Activities" result_type: diff --git a/config/locales/global_activities/en.yml b/config/locales/global_activities/en.yml index 79d7e3a97..2e13d6786 100644 --- a/config/locales/global_activities/en.yml +++ b/config/locales/global_activities/en.yml @@ -36,6 +36,13 @@ en: select_reports: Select Reports no_name: "(unnamed)" content_generation_error: "Failed to render activity content with id: %{activity_id}" + activity_filter_saved: "Filter successfully saved to the Webhooks page!" + save_filter: "Save filter" + save_filter_modal: + title: "Save filter" + description: "Saved filter will be located in the Webhooks section of the Settings. It can be used to trigger webhooks." + filter_name_label: "Filter name" + filter_name_placeholder: "Name your filter" content: create_project_html: "%{user} created project %{project}." rename_project_html: "%{user} renamed project %{project}." diff --git a/config/routes.rb b/config/routes.rb index e4a3bf31c..ebcad31d0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -761,6 +761,7 @@ Rails.application.routes.draw do get :protocol_filter get :team_filter get :user_filter + post :save_activity_filter end end diff --git a/db/migrate/20210531114633_create_activity_filters.rb b/db/migrate/20210531114633_create_activity_filters.rb new file mode 100644 index 000000000..b517a5671 --- /dev/null +++ b/db/migrate/20210531114633_create_activity_filters.rb @@ -0,0 +1,10 @@ +class CreateActivityFilters < ActiveRecord::Migration[4.2] + def change + create_table :activity_filters do |t| + t.string :name, null: false + t.jsonb :filter, null: false + + t.timestamps null: false + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 5e7b05333..e1bb211dd 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -193,6 +193,39 @@ CREATE SEQUENCE public.activities_id_seq ALTER SEQUENCE public.activities_id_seq OWNED BY public.activities.id; +-- +-- Name: activity_filters; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.activity_filters ( + id integer NOT NULL, + name character varying NOT NULL, + filter jsonb NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: activity_filters_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.activity_filters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: activity_filters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.activity_filters_id_seq OWNED BY public.activity_filters.id; + + -- -- Name: ar_internal_metadata; Type: TABLE; Schema: public; Owner: - -- @@ -2918,6 +2951,13 @@ ALTER TABLE ONLY public.active_storage_variant_records ALTER COLUMN id SET DEFAU ALTER TABLE ONLY public.activities ALTER COLUMN id SET DEFAULT nextval('public.activities_id_seq'::regclass); +-- +-- Name: activity_filters id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity_filters ALTER COLUMN id SET DEFAULT nextval('public.activity_filters_id_seq'::regclass); + + -- -- Name: asset_text_data id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3468,6 +3508,14 @@ ALTER TABLE ONLY public.activities ADD CONSTRAINT activities_pkey PRIMARY KEY (id); +-- +-- Name: activity_filters activity_filters_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity_filters + ADD CONSTRAINT activity_filters_pkey PRIMARY KEY (id); + + -- -- Name: ar_internal_metadata ar_internal_metadata_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -7234,6 +7282,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20210312185911'), ('20210325152257'), ('20210407143303'), -('20210506125657'); +('20210410100006'), +('20210506125657'), +('20210531114633');