effective_committees 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 701cf249e308e4866ec599fce879509d3a032292e29ba9a6775e49e136135cc8
4
- data.tar.gz: 119e68d045546f7199ca01fe8c1ecdfc2566bcfe45897f7f59e06d277eb7c887
3
+ metadata.gz: e2a318a62bd5057b979e35eb0280f7db899f83367a80726e13471444bd80d758
4
+ data.tar.gz: 01d660c74f301d9a82627cf7d15782db30bac3bee2e717720fd1d54b77bdd3fb
5
5
  SHA512:
6
- metadata.gz: 214657350e87fd4e21059f001b9caa9aba42fedeb0099456fc5e352f9562ec9f0ef22654df0809afbb32b5fcd0318c02175cea29ff98e0ffc5b9f0fe340ad270
7
- data.tar.gz: b36a92d72782adbbb10c6f7458d1de4411a337405d5d8ba347d31f6e6e072e1e9cbb6c9e02f539a8d1fea292adc62b47d1c02ecfb64737689ec8a24aaf9f6703
6
+ metadata.gz: f07fb6ef7c974f646a35186845541752b522a286657efe3158eb99262244274efc09d610d7799218c0a92931cab86394946dd83f5b1ba91a7b3bad4cea43b40d
7
+ data.tar.gz: dfa60d6d7d0926ec9ada024243139fce61088251bff4da53b8a69a94b6db46207535f8ab9c785c46f992d1f346b387b96f6b89ced4697d6805199f23263f42d3
@@ -3,6 +3,7 @@ module Effective
3
3
  before_action(:authenticate_user!) if defined?(Devise)
4
4
 
5
5
  include Effective::CrudController
6
+ log_page_views(if: -> { EffectiveCommittees.log_page_views? })
6
7
 
7
8
  resource_scope -> { Effective::CommitteeFolder.deep }
8
9
 
@@ -3,6 +3,7 @@ module Effective
3
3
  before_action(:authenticate_user!) if defined?(Devise)
4
4
 
5
5
  include Effective::CrudController
6
+ log_page_views(if: -> { EffectiveCommittees.log_page_views? })
6
7
 
7
8
  resource_scope -> {
8
9
  committees = Effective::Committee.deep.where(id: current_user.committees)
@@ -3,18 +3,24 @@ module Effective
3
3
  before_action(:authenticate_user!) if defined?(Devise)
4
4
 
5
5
  include Effective::CrudController
6
+ log_page_views(if: -> { EffectiveCommittees.log_page_views? })
6
7
 
7
8
  resource_scope -> { Effective::Committee.all.deep }
9
+ page_title "More Activity", only: [:activity]
8
10
 
9
11
  def index
10
12
  @committees = resource_scope.for_index
11
13
  @page_title = EffectiveResources.et('effective_committees.name')
12
14
 
13
15
  EffectiveResources.authorize!(self, :index, Effective::Committee)
16
+ EffectiveResources.authorize!(self, :all_committees, Effective::Committee)
14
17
 
15
18
  render 'index'
16
19
  end
17
20
 
21
+ # activity
22
+ # my_committees
23
+
18
24
  private
19
25
 
20
26
  def permitted_params
@@ -7,7 +7,7 @@ module Admin
7
7
  col :committee
8
8
  col :user
9
9
 
10
- if defined?(EffectiveMemberships)
10
+ if current_user.class.try(:effective_memberships_owner?)
11
11
  col(:member_number, label: 'Member #', sort: false) do |committee_member|
12
12
  committee_member.user.try(:membership).try(:number)
13
13
  end.search do |collection, term|
@@ -34,7 +34,11 @@ module Admin
34
34
  col :roles, search: roles_collection
35
35
  end
36
36
 
37
- actions_col
37
+ actions_col do |committee_member|
38
+ if EffectiveResources.authorized?(self, :impersonate, committee_member.user)
39
+ dropdown_link_to("Impersonate", "/admin/users/#{committee_member.user_id}/impersonate", data: { confirm: "Really impersonate #{committee_member.user}?", method: :post, remote: true })
40
+ end
41
+ end
38
42
  end
39
43
 
40
44
  collection do
@@ -0,0 +1,42 @@
1
+ # For the /committees/activity More Activity page
2
+ # Displays all the logged changes for this user's committees
3
+ class EffectiveCommitteesActivityDatatable < Effective::Datatable
4
+ datatable do
5
+ order :id, :desc
6
+
7
+ col :updated_at, label: 'Date'
8
+ col :id, visible: false
9
+
10
+ col :user, label: 'Changed by', search: :string, sort: false
11
+ col :changes_to, label: committee_label, search: current_user.committees
12
+ col :associated, visible: false
13
+
14
+ col :message, sort: false do |log|
15
+ message = (log.message || '').gsub("\n", '<br>')
16
+ associated_type = log.associated_type.gsub('Effective::', '').gsub('ActiveStorage::', '')
17
+
18
+ if log.associated_id == attributes[:changes_to_id] && log.associated_type == attributes[:changes_to_type]
19
+ message
20
+ else
21
+ "#{associated_type} #{log.associated_to_s} - #{message}"
22
+ end
23
+
24
+ end.search do |collection, term, column, sql_column|
25
+ ilike = effective_resource.ilike
26
+ collection.where("associated_type #{ilike} ? OR associated_to_s #{ilike} ? OR message #{ilike} ?", "%#{term}%", "%#{term}%", "%#{term}%")
27
+ end
28
+
29
+ col :details, visible: false, sort: false do |log|
30
+ tableize_hash(log.details)
31
+ end
32
+
33
+ actions_col
34
+ end
35
+
36
+ collection do
37
+ EffectiveLogging.Log.logged_changes.deep
38
+ .where(changes_to_type: 'Effective::Committee', changes_to_id: current_user.committees.map(&:id))
39
+ .where.not(associated_type: 'ActiveStorage::Attachment')
40
+ end
41
+
42
+ end
@@ -10,7 +10,7 @@ class EffectiveCommitteesDatatable < Effective::Datatable
10
10
  col(:committee_members, label: committee_members_label, visible: false) do |committee|
11
11
  committee.committee_members.select(&:active?).sort_by(&:to_s).map do |member|
12
12
  content_tag(:div, class: 'col-resource_item') do
13
- label = link_to(member.to_s, "/admin/users/#{member.user_id}/edit")
13
+ label = member.to_s
14
14
  badge = badge(member.category) if member.category.present?
15
15
 
16
16
  [label, badge].compact.join(' ').html_safe
@@ -45,4 +45,28 @@ module EffectiveCommitteesUser
45
45
  committee_members.select { |cm| cm.active? && !cm.marked_for_destruction? }.map { |cm| cm.committee }
46
46
  end
47
47
 
48
+ # When activity is for sequential uploaded files, group them together like: "12 files were added to Board of Directors - April 2025 Meeting"
49
+ def committees_recent_activity(limit: 100)
50
+ logs = EffectiveLogging.Log.logged_changes.deep
51
+ .where(changes_to_type: 'Effective::Committee', changes_to_id: committees.map(&:id))
52
+ .order(created_at: :desc)
53
+
54
+ # Recent activity should be for folders being created and files being uploaded/replaced
55
+ logs = logs.select do |log|
56
+ folder_created = (log.associated_type == 'Effective::CommitteeFolder' && log.message == 'Created')
57
+ file_changed = (log.associated_type == 'Effective::CommitteeFile' && (log.message == 'Created' || log.details.dig(:changes, 'File').present?))
58
+ folder_created || file_changed
59
+ end
60
+
61
+ # Returns an Array of Arrays where some are 1 length groups
62
+ # Others are multiple length groups of file changes to one folder
63
+ logs = logs.slice_when do |a, b|
64
+ (a.changes_to_id != b.changes_to_id) ||
65
+ (a.associated_type != b.associated_type) ||
66
+ (b.associated_type == "Effective::CommitteeFolder") ||
67
+ (a.associated.try(:committee_folder_id) != b.associated.try(:committee_folder_id))
68
+ end
69
+
70
+ logs.take(limit)
71
+ end
48
72
  end
@@ -15,6 +15,9 @@ module Effective
15
15
  title :string
16
16
  notes :text
17
17
 
18
+ file_id :integer
19
+ file_created_at :datetime
20
+
18
21
  timestamps
19
22
  end
20
23
 
@@ -23,7 +26,7 @@ module Effective
23
26
  end
24
27
 
25
28
  before_validation(if: -> { file.attached? }) do
26
- self.title ||= file.filename.to_s
29
+ assign_attributes(title: file.filename.to_s, file_id: file.attachment.blob.id, file_created_at: file.attachment.blob.created_at)
27
30
  end
28
31
 
29
32
  scope :deep, -> { with_attached_file.includes(:committee, :committee_folder) }
@@ -1,7 +1,7 @@
1
1
  = render('layout') do
2
2
  %nav{'aria-label': 'breadcrumb'}
3
3
  %ol.breadcrumb
4
- - if EffectiveResources.authorized?(self, :index, Effective::Committee)
4
+ - if EffectiveResources.authorized?(self, :all_committees, Effective::Committee)
5
5
  %li.breadcrumb-item= link_to("All #{committees_label}", effective_committees.committees_path)
6
6
  - elsif EffectiveResources.authorized?(self, :my_committees, Effective::Committee)
7
7
  %li.breadcrumb-item= link_to("My #{committees_label}", effective_committees.my_committees_path)
@@ -0,0 +1,13 @@
1
+ - resource = (@_effective_resource || Effective::Resource.new(controller_path))
2
+ - @resource = instance_variable_get('@' + resource.name) if resource.name
3
+
4
+ - if @resource
5
+ .resource-buttons
6
+ = render_resource_buttons(@resource, show: false)
7
+
8
+ = render_resource_partial(@resource)
9
+
10
+ .my-4
11
+
12
+ -# .form-actions
13
+ -# = link_to 'Continue', return_to_dashboard_path, class: 'btn btn-primary mb-4'
@@ -1,7 +1,7 @@
1
1
  = render('layout') do
2
2
  %nav{'aria-label': 'breadcrumb'}
3
3
  %ol.breadcrumb
4
- - if EffectiveResources.authorized?(self, :index, Effective::Committee)
4
+ - if EffectiveResources.authorized?(self, :all_committees, Effective::Committee)
5
5
  %li.breadcrumb-item= link_to("All #{committees_label}", effective_committees.committees_path)
6
6
  - elsif EffectiveResources.authorized?(self, :my_committees, Effective::Committee)
7
7
  %li.breadcrumb-item= link_to("My #{committees_label}", effective_committees.my_committees_path)
@@ -4,7 +4,7 @@
4
4
  %p
5
5
  You have access to #{pluralize(current_user.committees.length, committee_label.downcase)}.
6
6
 
7
- - if EffectiveResources.authorized?(self, :index, Effective::Committee)
7
+ - if EffectiveResources.authorized?(self, :all_committees, Effective::Committee)
8
8
  Please
9
9
  = link_to('click here', effective_committees.committees_path)
10
10
  to view all #{committees_label.downcase}.
@@ -0,0 +1,30 @@
1
+ %h2 #{committees_label} Recent Activity
2
+
3
+ - if current_user.committees.present?
4
+ - current_user.committees_recent_activity(limit: 5).each do |logs|
5
+ - log = logs.first
6
+
7
+ - committee = log.changes_to
8
+ - link_to_committee = link_to(committee, effective_committees.committee_path(committee))
9
+
10
+ - committee_folder = log.associated.try(:committee_folder) || log.associated
11
+ - link_to_committee_folder = link_to(committee_folder, effective_committees.committee_committee_folder_path(committee, committee_folder))
12
+
13
+ - link_to_file = link_to(log.associated, url_for(log.associated.try(:file)))
14
+
15
+ - if logs.length == 1 && log.associated_type == 'Effective::CommitteeFolder'
16
+ %p Folder #{link_to_committee_folder} was added to #{link_to_committee}.
17
+
18
+ - elsif logs.length == 1 && log.associated_type == 'Effective::CommitteeFile'
19
+ %p File #{link_to_file} was added to #{link_to_committee} - #{link_to_committee_folder}.
20
+
21
+ - elsif logs.length > 1 && logs.all? { |log| log.associated_type == 'Effective::CommitteeFile' }
22
+ %p #{logs.length} files were added to #{link_to_committee} - #{link_to_committee_folder}.
23
+
24
+ - else
25
+ - raise("Unexpected logs: #{logs.inspect}")
26
+
27
+ %p= link_to('View more activity', effective_committees.activity_committees_path)
28
+
29
+ - else
30
+ %p There's no activity to display. When there is, we'll show it here.
@@ -0,0 +1,19 @@
1
+ - committees = @committees.sorted
2
+
3
+ = render('layout') do
4
+ %nav{'aria-label': 'breadcrumb'}
5
+ %ol.breadcrumb
6
+ %li.breadcrumb-item= link_to("Home", root_path)
7
+ %li.breadcrumb-item.active{'aria-current': 'page'}= @page_title
8
+
9
+ %h1= @page_title
10
+
11
+ - if committees.blank?
12
+ %p There are no #{committees_label.downcase} to display.
13
+ - else
14
+ - datatable = EffectiveResources.best('EffectiveCommitteesActivityDatatable').new(self, namespace: :effective)
15
+ = render_datatable(datatable, inline: true)
16
+ .my-4
17
+
18
+ .form-actions
19
+ = link_to 'Continue', return_to_dashboard_path, class: 'btn btn-primary mb-4'
@@ -3,7 +3,7 @@
3
3
  = render('layout') do
4
4
  %nav{'aria-label': 'breadcrumb'}
5
5
  %ol.breadcrumb
6
- - if EffectiveResources.authorized?(self, :index, Effective::Committee)
6
+ - if EffectiveResources.authorized?(self, :all_committees, Effective::Committee)
7
7
  %li.breadcrumb-item= link_to("All #{committees_label}", effective_committees.committees_path)
8
8
  - else
9
9
  %li.breadcrumb-item= link_to("Home", root_path)
@@ -0,0 +1,13 @@
1
+ - resource = (@_effective_resource || Effective::Resource.new(controller_path))
2
+ - @resource = instance_variable_get('@' + resource.name) if resource.name
3
+
4
+ - if @resource
5
+ .resource-buttons
6
+ = render_resource_buttons(@resource, show: false)
7
+
8
+ = render_resource_partial(@resource)
9
+
10
+ .my-4
11
+
12
+ -# .form-actions
13
+ -# = link_to 'Continue', return_to_dashboard_path, class: 'btn btn-primary mb-4'
@@ -5,4 +5,7 @@ EffectiveCommittees.setup do |config|
5
5
 
6
6
  # Committee Members can belong to a committee with a role
7
7
  config.use_effective_roles = true
8
+
9
+ # Uses EffectiveLogging to log page views for front end committees pages
10
+ config.log_page_views = false
8
11
  end
data/config/routes.rb CHANGED
@@ -8,10 +8,13 @@ EffectiveCommittees::Engine.routes.draw do
8
8
  # Public routes
9
9
  scope module: 'effective' do
10
10
  resources :committees, only: [:index, :show] do
11
+ get 'activity', on: :collection
12
+
11
13
  resources :committee_folders, only: [:show]
12
14
  end
13
15
 
14
16
  get 'my-committees', to: 'committees#my_committees'
17
+ get 'my_committees', to: 'committees#my_committees'
15
18
 
16
19
  resources :committee_members, except: [:show]
17
20
  end
@@ -66,6 +66,9 @@ class CreateEffectiveCommittees < ActiveRecord::Migration[6.0]
66
66
 
67
67
  t.integer :committee_folder_id
68
68
 
69
+ t.integer :file_id
70
+ t.datetime :file_created_at
71
+
69
72
  t.string :title
70
73
  t.text :notes
71
74
 
@@ -1,3 +1,3 @@
1
1
  module EffectiveCommittees
2
- VERSION = '0.4.2'.freeze
2
+ VERSION = '0.5.0'.freeze
3
3
  end
@@ -9,10 +9,14 @@ module EffectiveCommittees
9
9
  def self.config_keys
10
10
  [
11
11
  :committees_table_name, :committee_members_table_name, :committee_folders_table_name, :committee_files_table_name,
12
- :layout, :use_effective_roles
12
+ :layout, :use_effective_roles, :log_page_views
13
13
  ]
14
14
  end
15
15
 
16
16
  include EffectiveGem
17
17
 
18
+ def self.log_page_views?
19
+ !!log_page_views
20
+ end
21
+
18
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_committees
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-09-26 00:00:00.000000000 Z
11
+ date: 2025-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -191,6 +191,7 @@ files:
191
191
  - app/datatables/admin/effective_committee_members_datatable.rb
192
192
  - app/datatables/admin/effective_committees_datatable.rb
193
193
  - app/datatables/effective_committee_members_datatable.rb
194
+ - app/datatables/effective_committees_activity_datatable.rb
194
195
  - app/datatables/effective_committees_datatable.rb
195
196
  - app/helpers/effective_committees_helper.rb
196
197
  - app/models/concerns/effective_committees_user.rb
@@ -207,16 +208,20 @@ files:
207
208
  - app/views/admin/committees/_form_committee.html.haml
208
209
  - app/views/effective/committee_folders/_committee_folder.html.haml
209
210
  - app/views/effective/committee_folders/_layout.html.haml
211
+ - app/views/effective/committee_folders/show.html.haml
210
212
  - app/views/effective/committee_members/_form.html.haml
211
213
  - app/views/effective/committee_members/_user_fields.html.haml
212
214
  - app/views/effective/committees/_committee.html.haml
213
215
  - app/views/effective/committees/_dashboard.html.haml
216
+ - app/views/effective/committees/_dashboard_activity.html.haml
214
217
  - app/views/effective/committees/_fields.html.haml
215
218
  - app/views/effective/committees/_form.html.haml
216
219
  - app/views/effective/committees/_form_committee.html.haml
217
220
  - app/views/effective/committees/_layout.html.haml
221
+ - app/views/effective/committees/activity.html.haml
218
222
  - app/views/effective/committees/index.html.haml
219
223
  - app/views/effective/committees/my_committees.html.haml
224
+ - app/views/effective/committees/show.html.haml
220
225
  - config/effective_committees.rb
221
226
  - config/locales/effective_committees.en.yml
222
227
  - config/routes.rb