hyrax 3.5.0 → 3.6.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.dassie/config/analytics.yml +4 -2
  3. data/.regen +2 -1
  4. data/app/assets/javascripts/hyrax/analytics_events.js +88 -38
  5. data/app/controllers/hyrax/admin/analytics/collection_reports_controller.rb +2 -2
  6. data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +1 -1
  7. data/app/controllers/hyrax/admin/strategies_controller.rb +1 -1
  8. data/app/controllers/hyrax/admin/workflows_controller.rb +48 -3
  9. data/app/services/hyrax/analytics/ga4.rb +204 -0
  10. data/app/services/hyrax/analytics/google.rb +23 -15
  11. data/app/services/hyrax/analytics/matomo.rb +4 -3
  12. data/app/services/hyrax/solr_query_service.rb +4 -4
  13. data/app/services/hyrax/workflow/actionable_objects.rb +28 -3
  14. data/app/services/hyrax/workflow/permission_query.rb +23 -2
  15. data/app/views/hyrax/admin/analytics/collection_reports/index.html.erb +1 -1
  16. data/app/views/hyrax/admin/analytics/work_reports/index.html.erb +1 -1
  17. data/app/views/hyrax/admin/workflows/_tabs.html.erb +9 -0
  18. data/app/views/hyrax/admin/workflows/index.html.erb +53 -78
  19. data/app/views/hyrax/base/_show_actions.html.erb +1 -1
  20. data/app/views/hyrax/base/_work_button_row.html.erb +1 -1
  21. data/app/views/hyrax/dashboard/_user_activity.html.erb +1 -1
  22. data/app/views/hyrax/dashboard/show_admin.html.erb +1 -1
  23. data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +1 -1
  24. data/app/views/hyrax/file_sets/_show_actions.html.erb +1 -1
  25. data/app/views/layouts/_head_tag_content.html.erb +5 -2
  26. data/app/views/shared/_ga4.html.erb +10 -0
  27. data/config/locales/hyrax.en.yml +8 -0
  28. data/documentation/developing-your-hyrax-based-app.md +1 -1
  29. data/documentation/legacyREADME.md +2 -2
  30. data/hyrax.gemspec +1 -1
  31. data/lib/generators/hyrax/templates/config/analytics.yml +3 -0
  32. data/lib/hyrax/version.rb +1 -1
  33. data/template.rb +1 -1
  34. metadata +12 -5
  35. data/.github/workflows/main.yml +0 -17
  36. data/.github/workflows/release.yml +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98e61b637b4f510e877e88c3f2f8020a411b7d83cd0c074e142f666fbda964f1
4
- data.tar.gz: 17a8be1a76c9755d5e8b2b1387e907c2d70722809cdb28a62088d913dc584384
3
+ metadata.gz: 48404af16c40c5a6144f84f4747f391276dbbf03901c49947658904296ad1510
4
+ data.tar.gz: 6836a04cd80c63455b5670c591c102d5ac20224cd3746051eb6df0fc17daaaf5
5
5
  SHA512:
6
- metadata.gz: 445d46c5b5b5ee45ea4262e1809e92356f9536f2f919af929b9d33fafcc13c584ae0ff1c6c2b27cac8ffbcdf35f9be6fd9a726b9bf5c9cafc0632fbd8596a90c
7
- data.tar.gz: d5d5ca3a839a02af1fec27d262eb8301a5880a07caba29b9ce383b37a7c7aea098e57f3ddf9c3fbc1582f5a9ffa227e7d0fc3702833a645ae86b7ab94745a2f9
6
+ metadata.gz: 10235f122db54b7d649c42208982e93eee952d16ee921c4db12ab8e42fb3a30d4834e1a9ae509bec3aa8de9427d05bb78725f06ec9ce0455093ea58cc02c2cd8
7
+ data.tar.gz: 5e8df023cfdff3daedba28bdd012ddb7dbf26d8ef12dab69b6e648d4aca742db5948830510a5890a12f1220ca8f5350991ce95156bbab6f559c2669b6e1f10be
@@ -1,13 +1,15 @@
1
1
  analytics:
2
+ ga4:
3
+ analytics_id: <%= ENV['GOOGLE_ANALYTICS_ID'] %>
2
4
  google:
3
5
  analytics_id: <%= ENV['GOOGLE_ANALYTICS_ID'] %>
4
6
  app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME'] %>
5
7
  app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION'] %>
8
+ privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE'] %>
6
9
  privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH'] %>
7
10
  privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET'] %>
8
11
  client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL'] %>
9
- matomo:
12
+ matomo:
10
13
  base_url: <%= ENV['MATOMO_BASE_URL'] %>
11
14
  site_id: <%= ENV['MATOMO_SITE_ID'] %>
12
15
  auth_token: <%= ENV['MATOMO_AUTH_TOKEN'] %>
13
-
data/.regen CHANGED
@@ -1 +1,2 @@
1
- 46
1
+ # When updating CI regen seed, set to current date.
2
+ 2023-05-24T10:43:07
@@ -1,75 +1,125 @@
1
1
  class TrackingTags {
2
2
  constructor(provider) {
3
3
  this.provider = provider
4
+ switch(this.provider) {
5
+ case 'matomo':
6
+ this.tracker = new MatomoTagTracker();
7
+ break;
8
+ case 'google':
9
+ this.tracker = new UATagTracker();
10
+ break;
11
+ case 'ga4':
12
+ this.tracker = new GA4TagTracker();
13
+ break;
14
+ default:
15
+ console.error('Unsupport analytics provider ' + this.provider + ', supported values are: matomo, google, ga4');
16
+ }
17
+ }
18
+
19
+ // Track an event with the configured provider
20
+ trackTagEvent(category, action, name) {
21
+ this.tracker.trackEvent(category, action, name);
4
22
  }
5
23
 
24
+ // Track a page view with the configured provider
25
+ trackPageView() {
26
+ this.tracker.trackPageView();
27
+ }
28
+
29
+ // Deprecated: use trackTagEvent and trackPageView instead.
6
30
  analytics() {
7
- if(this.provider === "matomo") {
8
- return _paq;
9
- }
10
- else {
11
- return _gaq;
12
- }
31
+ return this;
13
32
  }
14
33
 
15
- pageView() {
16
- if(this.provider === "matomo") {
17
- return 'trackPageView'
34
+ // Deprecated: use trackTagEvent and trackPageView instead.
35
+ push(params) {
36
+ if (params[0] == 'trackPageView' || params[0] == '_trackPageView') {
37
+ this.tracker.trackPageView();
18
38
  } else {
19
- return '_trackPageview'
39
+ this.tracker.trackTagEvent(params[1], params[2], params[3]);
20
40
  }
21
41
  }
22
42
 
43
+ // Deprecated
44
+ pageView() {
45
+ return 'trackPageView';
46
+ }
47
+
48
+ // Deprecated
23
49
  trackEvent() {
24
- if(this.provider === "matomo") {
25
- return 'trackEvent'
26
- } else {
27
- return '_trackEvent'
28
- }
50
+ return 'trackEvent';
51
+ }
52
+ }
53
+
54
+ class GA4TagTracker {
55
+ trackEvent(category, action, name) {
56
+ gtag('event', action, {
57
+ 'category': category,
58
+ 'label': name
59
+ });
60
+ }
61
+
62
+ trackPageView() {
63
+ // No operation necessary, this event is automatically collected
64
+ }
65
+ }
66
+
67
+ class UATagTracker {
68
+ trackEvent(category, action, name) {
69
+ _gaq.push(['_trackEvent', category, action, name]);
70
+ }
71
+
72
+ trackPageView() {
73
+ _gaq.push(['_trackPageView']);
74
+ }
75
+ }
76
+
77
+ class MatomoTagTracker {
78
+ trackEvent(category, action, name) {
79
+ _paq.push(['trackEvent', category, action, name]);
80
+ }
81
+
82
+ trackPageView() {
83
+ _paq.push(['trackPageView']);
29
84
  }
30
85
  }
31
86
 
32
87
  function trackPageView() {
33
- window.trackingTags.analytics().push([window.trackingTags.pageView()]);
88
+ window.trackingTags.trackPageView();
34
89
  }
35
90
 
36
91
  function trackAnalyticsEvents() {
37
92
  $('span.analytics-event').each(function(){
38
- var eventSpan = $(this)
39
- window.trackingTags.analytics().push([window.trackingTags.trackEvent(), eventSpan.data('category'), eventSpan.data('action'), eventSpan.data('name')]);
93
+ var eventSpan = $(this);
94
+ window.trackingTags.trackTagEvent(eventSpan.data('category'), eventSpan.data('action'), eventSpan.data('name'));
40
95
  })
41
96
  }
42
97
 
43
98
  function setupTracking() {
44
- var provider = $('meta[name="analytics-provider"]').prop('content')
45
- if (provider === undefined) {
46
- return;
47
- }
48
- window.trackingTags = new TrackingTags(provider)
49
- trackPageView()
50
- trackAnalyticsEvents()
99
+ var provider = $('meta[name="analytics-provider"]').prop('content')
100
+ if (provider === undefined) {
101
+ return;
102
+ }
103
+ window.trackingTags = new TrackingTags(provider);
104
+ trackPageView();
105
+ trackAnalyticsEvents();
51
106
  }
52
107
 
53
108
  if (typeof Turbolinks !== 'undefined') {
54
109
  $(document).on('turbolinks:load', function() {
55
- setupTracking()
56
- })
110
+ setupTracking();
111
+ });
57
112
  } else {
58
113
  $(document).on('ready', function() {
59
- setupTracking()
60
- })
114
+ setupTracking();
115
+ });
61
116
  }
62
117
 
63
118
  $(document).on('click', '#file_download', function(e) {
64
- var provider = $('meta[name="analytics-provider"]').prop('content')
65
- if (provider === undefined) {
66
- return;
67
- }
68
- window.trackingTags = new TrackingTags(provider)
69
- window.trackingTags.analytics().push([trackingTags.trackEvent(), 'file-set', 'file-set-download', $(this).data('label')]);
70
- window.trackingTags.analytics().push([trackingTags.trackEvent(), 'file-set-in-work', 'file-set-in-work-download', $(this).data('work-id')]);
119
+ window.trackingTags.trackTagEvent('file-set', 'file-set-download', $(this).data('label'));
120
+ window.trackingTags.trackTagEvent('file-set-in-work', 'file-set-in-work-download', $(this).data('work-id'));
71
121
  $(this).data('collection-ids').forEach(function (collection) {
72
- window.trackingTags.analytics().push([trackingTags.trackEvent(), 'file-set-in-collection', 'file-set-in-collection-download', collection]);
73
- window.trackingTags.analytics().push([trackingTags.trackEvent(), 'work-in-collection', 'work-in-collection-download', collection]);
122
+ window.trackingTags.trackTagEvent('file-set-in-collection', 'file-set-in-collection-download', collection);
123
+ window.trackingTags.trackTagEvent('work-in-collection', 'work-in-collection-download', collection);
74
124
  });
75
125
  });
@@ -5,7 +5,7 @@ module Hyrax
5
5
  class CollectionReportsController < AnalyticsController
6
6
  include Hyrax::BreadcrumbsForCollectionAnalytics
7
7
  def index
8
- return unless Hyrax.config.analytics?
8
+ return unless Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4'
9
9
 
10
10
  @pageviews = Hyrax::Analytics.daily_events('collection-page-view')
11
11
  @work_page_views = Hyrax::Analytics.daily_events('work-in-collection-view')
@@ -21,7 +21,7 @@ module Hyrax
21
21
  end
22
22
 
23
23
  def show
24
- return unless Hyrax.config.analytics?
24
+ return unless Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4'
25
25
  @document = ::SolrDocument.find(params[:id])
26
26
  @pageviews = Hyrax::Analytics.daily_events_for_id(@document.id, 'collection-page-view')
27
27
  @work_page_views = Hyrax::Analytics.daily_events_for_id(@document.id, 'work-in-collection-view')
@@ -6,7 +6,7 @@ module Hyrax
6
6
  include Hyrax::BreadcrumbsForWorksAnalytics
7
7
 
8
8
  def index
9
- return unless Hyrax.config.analytics?
9
+ return unless Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4'
10
10
 
11
11
  @accessible_works ||= accessible_works
12
12
  @accessible_file_sets ||= accessible_file_sets
@@ -7,7 +7,7 @@ module Hyrax
7
7
  end
8
8
 
9
9
  # TODO: we could remove this if we used an isolated engine
10
- def features_url
10
+ def features_url(**_kargs)
11
11
  hyrax.admin_features_path
12
12
  end
13
13
  end
@@ -15,9 +15,8 @@ module Hyrax
15
15
  add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path
16
16
  add_breadcrumb t(:'hyrax.admin.sidebar.tasks'), '#'
17
17
  add_breadcrumb t(:'hyrax.admin.sidebar.workflow_review'), request.path
18
-
19
- @status_list = actionable_objects.reject(&:published?)
20
- @published_list = actionable_objects.select(&:published?)
18
+ assign_action_objects_params
19
+ @response = WorkflowResponse.new(actionable_objects.to_a, actionable_objects.total_count, current_page, per_page, under_review?)
21
20
  end
22
21
 
23
22
  private
@@ -30,5 +29,51 @@ module Hyrax
30
29
  @actionable_objects ||=
31
30
  Hyrax::Workflow::ActionableObjects.new(user: current_user)
32
31
  end
32
+
33
+ def current_page
34
+ @page ||= params.fetch('page', 1).to_i
35
+ end
36
+
37
+ def per_page
38
+ @per_page ||= params.fetch('per_page', 10).to_i
39
+ end
40
+
41
+ def assign_action_objects_params
42
+ actionable_objects.page = current_page
43
+ actionable_objects.per_page = per_page
44
+ actionable_objects.workflow_state_filter = (under_review? ? '!' : '') + deposited_workflow_state_name
45
+ end
46
+
47
+ def under_review?
48
+ @under_review = params['state'] != 'published'
49
+ end
50
+
51
+ class WorkflowResponse
52
+ attr_reader :total_count
53
+ attr_reader :current_page
54
+ attr_reader :per_page
55
+ attr_reader :docs
56
+ attr_reader :under_review
57
+
58
+ def initialize(docs, total_count, page, per_page, under_review)
59
+ @docs = docs
60
+ @total_count = total_count
61
+ @per_page = per_page.to_i
62
+ @current_page = page.to_i
63
+ @under_review = under_review
64
+ end
65
+
66
+ def total_pages
67
+ (total_count.to_f / per_page).ceil
68
+ end
69
+
70
+ def limit_value
71
+ docs.length
72
+ end
73
+
74
+ def viewing_under_review?
75
+ under_review
76
+ end
77
+ end
33
78
  end
34
79
  end
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+ require 'oauth2'
3
+ require 'signet/oauth_2/client'
4
+
5
+ # rubocop:disable Metrics/ModuleLength
6
+ module Hyrax
7
+ module Analytics
8
+ module Ga4
9
+ extend ActiveSupport::Concern
10
+ # rubocop:disable Metrics/BlockLength
11
+ class_methods do
12
+ # Loads configuration options from config/analytics.yml. Expected structure:
13
+ # `analytics:`
14
+ # ` ga4:`
15
+ # ` app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME']`
16
+ # ` app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION']`
17
+ # ` privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH']`
18
+ # ` privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET']`
19
+ # ` client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL']`
20
+ # @return [Config]
21
+ def config
22
+ @config ||= Config.load_from_yaml
23
+ end
24
+
25
+ class Config
26
+ def self.load_from_yaml
27
+ filename = Rails.root.join('config', 'analytics.yml')
28
+ yaml = YAML.safe_load(ERB.new(File.read(filename)).result)
29
+ unless yaml
30
+ Hyrax.logger.error("Unable to fetch any keys from #{filename}.")
31
+ return new({})
32
+ end
33
+ config = yaml.fetch('analytics')&.fetch('ga4', nil)
34
+ unless config
35
+ Deprecation.warn("Deprecated analytics configuration format found. Please update config/analytics.yml.")
36
+ config = yaml.fetch('analytics')
37
+ # this has to exist here with a placeholder so it can be set in the Hyrax initializer
38
+ # it is only for backward compatibility
39
+ config['analytics_id'] = '-'
40
+ end
41
+ new config
42
+ end
43
+
44
+ REQUIRED_KEYS = %w[analytics_id app_name app_version privkey_path privkey_secret client_email].freeze
45
+
46
+ def initialize(config)
47
+ @config = config
48
+ end
49
+
50
+ # @return [Boolean] are all the required values present?
51
+ def valid?
52
+ config_keys = @config.keys
53
+ REQUIRED_KEYS.all? { |required| config_keys.include?(required) }
54
+ end
55
+
56
+ REQUIRED_KEYS.each do |key|
57
+ class_eval %{ def #{key}; @config.fetch('#{key}'); end }
58
+ end
59
+
60
+ # This method allows setting the analytics id in the initializer
61
+ # @deprecated set the analytics id in either ENV['GOOGLE_ANALYTICS_ID'] or config/analytics.yaml
62
+ def analytics_id=(value)
63
+ @config['analytics_id'] = value
64
+ end
65
+ end
66
+
67
+ # Generate an OAuth2 token for Google Analytics
68
+ # @return [OAuth2::AccessToken] An OAuth2 access token for GA
69
+ def token(scope = 'https://www.googleapis.com/auth/analytics.readonly')
70
+ access_token = auth_client(scope).fetch_access_token!
71
+ OAuth2::AccessToken.new(oauth_client, access_token['access_token'], expires_in: access_token['expires_in'])
72
+ end
73
+
74
+ def oauth_client
75
+ OAuth2::Client.new('', '', authorize_url: 'https://accounts.google.com/o/oauth2/auth',
76
+ token_url: 'https://accounts.google.com/o/oauth2/token')
77
+ end
78
+
79
+ def auth_client(scope)
80
+ raise "Private key file for Google analytics was expected at '#{config.privkey_path}', but no file was found." unless File.exist?(config.privkey_path)
81
+ private_key = File.read(config.privkey_path)
82
+ Signet::OAuth2::Client.new token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
83
+ audience: 'https://accounts.google.com/o/oauth2/token',
84
+ scope: scope,
85
+ issuer: config.client_email,
86
+ signing_key: OpenSSL::PKCS12.new(private_key, config.privkey_secret).key,
87
+ sub: config.client_email
88
+ end
89
+
90
+ # Return a user object linked to a Google Analytics account
91
+ # @return [Legato::User] A user account with GA access
92
+ def user
93
+ Legato::User.new(token)
94
+ end
95
+
96
+ # Return a Google Analytics profile matching specified ID
97
+ # @ return [Legato::Management::Profile] A user profile associated with GA
98
+ def profile
99
+ return unless config.valid?
100
+ @profile = user.profiles.detect do |profile|
101
+ profile.web_property_id == config.analytics_id
102
+ end
103
+ raise 'User does not have access to this property' unless @profile
104
+ @profile
105
+ end
106
+
107
+ # rubocop:disable Metrics/MethodLength
108
+ def to_date_range(period)
109
+ case period
110
+ when "day"
111
+ start_date = Time.zone.today
112
+ end_date = Time.zone.today
113
+ when "week"
114
+ start_date = Time.zone.today - 7.days
115
+ end_date = Time.zone.today
116
+ when "month"
117
+ start_date = Time.zone.today - 1.month
118
+ end_date = Time.zone.today
119
+ when "year"
120
+ start_date = Time.zone.today - 1.year
121
+ end_date = Time.zone.today
122
+ end
123
+
124
+ [start_date, end_date]
125
+ end
126
+ # rubocop:enabl e Metrics/MethodLength
127
+
128
+ def keyword_conversion(date)
129
+ case date
130
+ when "last12"
131
+ start_date = Time.zone.today - 11.months
132
+ end_date = Time.zone.today
133
+
134
+ [start_date, end_date]
135
+ else
136
+ date.split(",")
137
+ end
138
+ end
139
+
140
+ def date_period(period, date)
141
+ if period == "range"
142
+ date.split(",")
143
+ else
144
+ to_date_range(period)
145
+ end
146
+ end
147
+
148
+ # Configure analytics_start_date in ENV file
149
+ def default_date_range
150
+ "#{Hyrax.config.analytics_start_date},#{Time.zone.today + 1.day}"
151
+ end
152
+
153
+ # The number of events by day for an action
154
+ def daily_events(action, date = default_date_range)
155
+ date = date.split(",")
156
+ EventsDaily.summary(profile, date[0], date[1], action)
157
+ end
158
+
159
+ # The number of events by day for an action and ID
160
+ def daily_events_for_id(id, action, date = default_date_range)
161
+ date = date.split(",")
162
+ EventsDaily.by_id(profile, date[0], date[1], id, action)
163
+ end
164
+
165
+ # A list of events sorted by highest event count
166
+ def top_events(action, date = default_date_range)
167
+ date = date.split(",")
168
+ Events.send('list', profile, date[0], date[1], action)
169
+ end
170
+
171
+ def unique_visitors(date = default_date_range); end
172
+
173
+ def unique_visitors_for_id(id, date = default_date_range); end
174
+
175
+ def new_visitors(period = 'month', date = default_date_range)
176
+ date = date_period(period, date)
177
+ Visits.new_visits(profile, date[0], date[1])
178
+ end
179
+
180
+ def new_visits_by_day(date = default_date_range, _period = 'day')
181
+ date = date.split(",")
182
+ VisitsDaily.new_visits(profile, date[0], date[1])
183
+ end
184
+
185
+ def returning_visitors(period = 'month', date = default_date_range)
186
+ date = date_period(period, date)
187
+ Visits.return_visits(profile, date[0], date[1])
188
+ end
189
+
190
+ def returning_visits_by_day(date = default_date_range, _period = 'day')
191
+ date = date.split(",")
192
+ VisitsDaily.return_visits(profile, date[0], date[1])
193
+ end
194
+
195
+ def total_visitors(period = 'month', date = default_date_range)
196
+ date = date_period(period, date)
197
+ Visits.total_visits(profile, date[0], date[1])
198
+ end
199
+ end
200
+ # rubocop:enable Metrics/BlockLength
201
+ end
202
+ end
203
+ end
204
+ # rubocop:enable Metrics/ModuleLength
@@ -1,20 +1,24 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'oauth2'
3
4
  require 'signet/oauth_2/client'
4
5
 
5
- # rubocop:disable Metrics/ModuleLength
6
6
  module Hyrax
7
7
  module Analytics
8
+ # rubocop:disable Metrics/ModuleLength
8
9
  module Google
9
10
  extend ActiveSupport::Concern
10
11
  # rubocop:disable Metrics/BlockLength
11
12
  class_methods do
12
- # Loads configuration options from config/analytics.yml. Expected structure:
13
+ # Loads configuration options from config/analytics.yml. You only need PRIVATE_KEY_PATH or
14
+ # PRIVATE_KEY_VALUE. VALUE takes precedence.
15
+ # Expected structure:
13
16
  # `analytics:`
14
17
  # ` google:`
15
18
  # ` app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME']`
16
19
  # ` app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION']`
17
20
  # ` privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH']`
21
+ # ` privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE']`
18
22
  # ` privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET']`
19
23
  # ` client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL']`
20
24
  # @return [Config]
@@ -41,7 +45,8 @@ module Hyrax
41
45
  new config
42
46
  end
43
47
 
44
- REQUIRED_KEYS = %w[analytics_id app_name app_version privkey_path privkey_secret client_email].freeze
48
+ KEYS = %w[analytics_id app_name app_version privkey_path privkey_value privkey_secret client_email].freeze
49
+ REQUIRED_KEYS = %w[analytics_id app_name app_version privkey_secret client_email].freeze
45
50
 
46
51
  def initialize(config)
47
52
  @config = config
@@ -49,18 +54,16 @@ module Hyrax
49
54
 
50
55
  # @return [Boolean] are all the required values present?
51
56
  def valid?
52
- config_keys = @config.keys
53
- REQUIRED_KEYS.all? { |required| config_keys.include?(required) }
54
- end
57
+ return false unless @config['privkey_value'].present? || @config['privkey_path'].present?
55
58
 
56
- REQUIRED_KEYS.each do |key|
57
- class_eval %{ def #{key}; @config.fetch('#{key}'); end }
59
+ REQUIRED_KEYS.all? { |required| @config[required].present? }
58
60
  end
59
61
 
60
- # This method allows setting the analytics id in the initializer
61
- # @deprecated set the analytics id in either ENV['GOOGLE_ANALYTICS_ID'] or config/analytics.yaml
62
- def analytics_id=(value)
63
- @config['analytics_id'] = value
62
+ KEYS.each do |key|
63
+ # rubocop:disable Style/EvalWithLocation
64
+ class_eval %{ def #{key}; @config.fetch('#{key}'); end }
65
+ class_eval %{ def #{key}=(value); @config['#{key}'] = value; end }
66
+ # rubocop:enable Style/EvalWithLocation
64
67
  end
65
68
  end
66
69
 
@@ -77,8 +80,12 @@ module Hyrax
77
80
  end
78
81
 
79
82
  def auth_client(scope)
80
- raise "Private key file for Google analytics was expected at '#{config.privkey_path}', but no file was found." unless File.exist?(config.privkey_path)
81
- private_key = File.read(config.privkey_path)
83
+ private_key = Base64.decode64(config.privkey_value) if config.privkey_value.present?
84
+ if private_key.blank?
85
+ raise "Private key file for Google analytics was expected at '#{config.privkey_path}', but no file was found." unless File.exist?(config.privkey_path)
86
+
87
+ private_key = File.read(config.privkey_path)
88
+ end
82
89
  Signet::OAuth2::Client.new token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
83
90
  audience: 'https://accounts.google.com/o/oauth2/token',
84
91
  scope: scope,
@@ -123,7 +130,7 @@ module Hyrax
123
130
 
124
131
  [start_date, end_date]
125
132
  end
126
- # rubocop:enabl e Metrics/MethodLength
133
+ # rubocop:enable Metrics/MethodLength
127
134
 
128
135
  def keyword_conversion(date)
129
136
  case date
@@ -199,6 +206,7 @@ module Hyrax
199
206
  end
200
207
  # rubocop:enable Metrics/BlockLength
201
208
  end
209
+ # rubocop:enable Metrics/ModuleLength
202
210
  end
203
211
  end
204
212
  # rubocop:enable Metrics/ModuleLength
@@ -11,15 +11,16 @@ module Hyrax
11
11
  # Loads configuration options from config/analytics.yml. Expected structure:
12
12
  # `analytics:`
13
13
  # ` matomo:`
14
- # ` base_url: <%= ENV['MATOMOT_BASE_URL']`
15
- # ` site_id: <%= ENV['MATOMOT_SITE_ID']`
16
- # ` auth_token: <%= ENV['MATOMOT_AUTH_TOKEN']`
14
+ # ` base_url: <%= ENV['MATOMO_BASE_URL']`
15
+ # ` site_id: <%= ENV['MATOMO_SITE_ID']`
16
+ # ` auth_token: <%= ENV['MATOMO_AUTH_TOKEN']`
17
17
  # @return [Config]
18
18
  def config
19
19
  @config ||= Config.load_from_yaml
20
20
  end
21
21
 
22
22
  class Config
23
+ # TODO: test matomo and see if it needs any of the updates from https://github.com/samvera/hyrax/pull/6063
23
24
  def self.load_from_yaml
24
25
  filename = Rails.root.join('config', 'analytics.yml')
25
26
  yaml = YAML.safe_load(ERB.new(File.read(filename)).result)
@@ -27,14 +27,14 @@ module Hyrax
27
27
 
28
28
  ##
29
29
  # @return [Hash] the results returned from solr for the current query
30
- def get
31
- solr_service.get(build)
30
+ def get(*args)
31
+ solr_service.get(build, *args)
32
32
  end
33
33
 
34
34
  ##
35
35
  # @return [Enumerable<SolrDocument>]
36
- def solr_documents
37
- get['response']['docs'].map { |doc| self.class.document_model.new(doc) }
36
+ def solr_documents(*args)
37
+ get(*args)['response']['docs'].map { |doc| self.class.document_model.new(doc) }
38
38
  end
39
39
 
40
40
  ##
@@ -20,11 +20,27 @@ module Hyrax
20
20
  # @!attribute [rw] user
21
21
  # @return [::User]
22
22
  attr_accessor :user
23
+ ##
24
+ # @!attribute [rw] workflow_state_filter
25
+ # @return [String]
26
+ attr_accessor :workflow_state_filter
27
+ ##
28
+ # @!attribute [rw] page of results to return, 1 based
29
+ # @return [Integer]
30
+ attr_accessor :page
31
+ ##
32
+ # @!attribute [rw] per_page number of results in the page
33
+ # @return [Integer]
34
+ attr_accessor :per_page
23
35
 
24
36
  ##
25
37
  # @param [::User] user the user whose
26
- def initialize(user:)
38
+ # @param [String] optional filter by workstate name
39
+ def initialize(user:, workflow_state_filter: nil)
27
40
  @user = user
41
+ @workflow_state_filter = workflow_state_filter
42
+ @page = 1
43
+ @per_page = 10
28
44
  end
29
45
 
30
46
  ##
@@ -34,7 +50,8 @@ module Hyrax
34
50
  ids_and_states = id_state_pairs
35
51
  return if ids_and_states.none?
36
52
 
37
- docs = Hyrax::SolrQueryService.new.with_ids(ids: ids_and_states.map(&:first)).solr_documents
53
+ docs = Hyrax::SolrQueryService.new.with_ids(ids: ids_and_states.map(&:first))
54
+ .solr_documents(page: @page, rows: @per_page, sort: 'system_create_dtsi ASC')
38
55
 
39
56
  docs.each do |solr_doc|
40
57
  object = ObjectInWorkflowDecorator.new(solr_doc)
@@ -46,6 +63,13 @@ module Hyrax
46
63
  end
47
64
  end
48
65
 
66
+ ##
67
+ # @return [Integer] total number of entities selected
68
+ def total_count
69
+ PermissionQuery.scope_entities_for_the_user(user: user, workflow_state_filter: workflow_state_filter)
70
+ .count
71
+ end
72
+
49
73
  private
50
74
 
51
75
  ##
@@ -53,7 +77,8 @@ module Hyrax
53
77
  # @return [Array[String, Sipity::WorkflowState]]
54
78
  def id_state_pairs
55
79
  gids_and_states = PermissionQuery
56
- .scope_entities_for_the_user(user: user)
80
+ .scope_entities_for_the_user(user: user, page: page, per_page: per_page, workflow_state_filter: workflow_state_filter)
81
+ .order(created_at: :asc)
57
82
  .pluck(:proxy_for_global_id, :workflow_state_id)
58
83
 
59
84
  return [] if gids_and_states.none?
@@ -194,7 +194,7 @@ module Hyrax
194
194
  # @return [ActiveRecord::Relation<Sipity::Entity>]
195
195
  #
196
196
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
197
- def scope_entities_for_the_user(user:)
197
+ def scope_entities_for_the_user(user:, page: 1, per_page: nil, workflow_state_filter: nil)
198
198
  entities = Sipity::Entity.arel_table
199
199
  workflow_state_actions = Sipity::WorkflowStateAction.arel_table
200
200
  workflow_states = Sipity::WorkflowState.arel_table
@@ -227,13 +227,34 @@ module Hyrax
227
227
  entity_specific_where = where_builder.call(entity_responsibilities).and(
228
228
  entities[:id].eq(entity_responsibilities[:entity_id])
229
229
  )
230
+ entity_specific_where = filter_by_workflow_state(entity_specific_where, workflow_states, workflow_state_filter) if workflow_state_filter
230
231
  workflow_specific_where = where_builder.call(workflow_responsibilities)
232
+ workflow_specific_where = filter_by_workflow_state(workflow_specific_where, workflow_states, workflow_state_filter) if workflow_state_filter
231
233
 
232
- Sipity::Entity.where(
234
+ result = Sipity::Entity.where(
233
235
  entities[:id].in(entity_specific_joins.where(entity_specific_where))
234
236
  .or(entities[:id].in(workflow_specific_joins.where(workflow_specific_where)))
235
237
  )
238
+ # Apply paging if provided
239
+ if per_page.nil?
240
+ result
241
+ else
242
+ result.page(page).per(per_page)
243
+ end
236
244
  end
245
+
246
+ # @api private
247
+ #
248
+ # Append a filter by workflow state name to the provided where builder.
249
+ # If the filter begins with a !, it will filter to states not equal to the filter.
250
+ def filter_by_workflow_state(where_builder, workflow_states, filter)
251
+ if filter.start_with?('!')
252
+ where_builder.and(workflow_states[:name].not_eq(filter[1..-1]))
253
+ else
254
+ where_builder.and(workflow_states[:name].eq(filter))
255
+ end
256
+ end
257
+
237
258
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
238
259
 
239
260
  # @api public
@@ -8,7 +8,7 @@
8
8
  <div class="col-sm-12">
9
9
  <div class="collection-reports">
10
10
 
11
- <% if Hyrax.config.analytics? %>
11
+ <% if Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
12
12
  <div class="panel panel-default">
13
13
  <div class="panel-heading"><%= t('.repo_summary') %> <b><%= pluralize(Collection.count, "collection") %></b>, <%= t('.repo_summary_2') %> <b><%= @pageviews.all %> <%= t('.views') %></b> <%= t('.and') %> <b><%= @downloads.all %> <%= t('.downloads') %></b>.</div>
14
14
  <div class="panel-body">
@@ -9,7 +9,7 @@
9
9
  <div class="work-reports">
10
10
 
11
11
  <div class="panel panel-default">
12
- <% if Hyrax.config.analytics? %>
12
+ <% if Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
13
13
  <% if current_user.ability.admin? %>
14
14
  <div class="panel-heading"><b><%= @works_count %> <%= t('.works') %></b> <%= t('.repo_summary') %> <b><%= @pageviews.all if @pageviews %> <%= t('.views') %></b> <%= t('.and') %> <b><%= @downloads.all if @downloads %> <%= t('.downloads') %></b>.</div>
15
15
  <% else %>
@@ -0,0 +1,9 @@
1
+ <ul class="nav nav-tabs" id="my_nav" role="list">
2
+ <li<%= ' class="active"'.html_safe if @response.viewing_under_review? %>>
3
+ <%= link_to t('hyrax.admin.workflows.index.tabs.under_review'),
4
+ hyrax.admin_workflows_path(state: 'under-review'), class: "nav-link#{' active' if @response.viewing_under_review?}" %>
5
+ </li>
6
+ <li<%= ' class="active"'.html_safe if !@response.viewing_under_review? %>>
7
+ <%= link_to t('hyrax.admin.workflows.index.tabs.published'), hyrax.admin_workflows_path(state: 'published'), class: "nav-link#{' active' if !@response.viewing_under_review?}" %>
8
+ </li>
9
+ </ul>
@@ -5,87 +5,62 @@
5
5
  <div class="row">
6
6
  <div class="col-md-12">
7
7
  <div class="panel panel-default tabs">
8
- <ul class="nav nav-tabs" role="tablist">
9
- <li class="active">
10
- <a href="#under-review" role="tab" data-toggle="tab"><%= t('.tabs.under_review') %></a>
11
- </li>
12
- <li>
13
- <a href="#published" role="tab" data-toggle="tab"><%= t('.tabs.published') %></a>
14
- </li>
15
- </ul>
16
- <div class="tab-content">
17
- <div id="under-review" class="tab-pane active">
18
- <div class="panel panel-default labels">
19
- <div class="panel-body">
20
- <div class="table-responsive">
21
- <table class="table table-condensed table-striped datatable">
22
- <thead>
23
- <tr>
24
- <th width="40%">Work</th>
25
- <th width="20%">Depositor</th>
26
- <th width="20%">Submission Date</th>
27
- <th width="20%">Status</th>
28
- </tr>
29
- </thead>
30
- <tbody>
31
- <% @status_list.each do |document| %>
32
- <tr>
33
- <td>
34
- <%= link_to document, [main_app, document] %>
35
- </td>
36
- <td>
37
- <%= safe_join(document.creator, tag(:br)) %>
38
- </td>
39
- <td>
40
- <%= document.date_modified %>
41
- </td>
42
- <td>
43
- <span class="state state-pending"><%= document.workflow_state %></span>
44
- </td>
45
- </tr>
46
- <% end %>
47
- </tbody>
48
- </table>
49
- </div>
50
- </div>
51
- </div>
52
- </div>
53
- <div id="published" class="tab-pane">
54
- <div class="panel panel-default labels">
55
- <div class="panel-body">
56
- <div class="table-responsive">
57
- <table class="table table-condensed table-striped datatable">
58
- <thead>
59
- <tr>
60
- <th width="40%">Work</th>
61
- <th width="20%">Depositor</th>
62
- <th width="20%">Submission Date</th>
63
- <th width="20%">Status</th>
64
- </tr>
65
- </thead>
66
- <tbody>
67
- <% @published_list.each do |document| %>
68
- <tr>
69
- <td>
70
- <%= link_to document, [main_app, document] %>
71
- </td>
72
- <td>
73
- <%= safe_join(document.creator, tag(:br)) %>
74
- </td>
75
- <td>
76
- <%= document.date_modified %>
77
- </td>
78
- <td>
79
- <span class="state state-pending"><%= document.workflow_state %></span>
80
- </td>
81
- </tr>
82
- <% end %>
83
- </tbody>
84
- </table>
85
- </div>
8
+ <%= render 'tabs' %>
9
+ <div class="panel-heading">
10
+ <span class="count-display">
11
+ <% if @response.viewing_under_review? %>
12
+ <%= I18n.t('hyrax.admin.workflows.index.works_under_review', total_count: @response.total_count).html_safe %>
13
+ <% else %>
14
+ <%= I18n.t('hyrax.admin.workflows.index.works_published', total_count: @response.total_count).html_safe %>
15
+ <% end %>
16
+ </span>
17
+ </div>
18
+ <div class="panel-body">
19
+ <div class="row">
20
+ <div class="col-sm-12">
21
+ <div class="sort-toggle mt-2">
22
+ <%= form_tag hyrax.admin_workflows_path, method: :get, class: 'per_page' do %>
23
+ <fieldset class="col-12">
24
+ <legend class="sr-only"><%= t('hyrax.dashboard.my.sr.results_per_page') %></legend>
25
+ <%= label_tag :per_page do %>
26
+ Show <%= select_tag :per_page, options_for_select(Hyrax.config.range_for_number_of_results_to_display_per_page.map(&:to_s), h(params[:per_page])), title: "entries" %> per page
27
+ <% end %>
28
+ <%= render_hash_as_hidden_fields(search_state.params_for_search.except(:per_page, :sort, :utf8)) %>
29
+ </fieldset>
30
+ <% end %>
86
31
  </div>
87
32
  </div>
88
33
  </div>
34
+ <h2 class="sr-only"><%= t('.works_listing') %></h2>
35
+ <table class="table table-sm table-striped works-list">
36
+ <thead>
37
+ <tr>
38
+ <th width="40%"><%= t(".heading.work") %></th>
39
+ <th width="20%"><%= t(".heading.depositor") %></th>
40
+ <th width="20%"><%= t(".heading.submission_date") %></th>
41
+ <th width="20%"><%= t(".heading.status") %></th>
42
+ </tr>
43
+ </thead>
44
+ <tbody>
45
+ <% @response.docs.each do |document| %>
46
+ <tr>
47
+ <td>
48
+ <%= link_to document, [main_app, document] %>
49
+ </td>
50
+ <td>
51
+ <%= safe_join(document.creator, tag(:br)) %>
52
+ </td>
53
+ <td>
54
+ <%= document.date_modified %>
55
+ </td>
56
+ <td>
57
+ <span class="state state-pending"><%= document.workflow_state %></span>
58
+ </td>
59
+ </tr>
60
+ <% end %>
61
+ </tbody>
62
+ </table>
63
+ <%= render 'hyrax/my/results_pagination' %>
89
64
  </div>
90
65
  </div>
91
66
  </div>
@@ -17,7 +17,7 @@
17
17
  class: presenter.display_unfeature_link? ? 'btn btn-default' : 'btn btn-default collapse' %>
18
18
  <% end %>
19
19
  <% end %>
20
- <% if Hyrax.config.analytics? %>
20
+ <% if Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
21
21
  <% # turbolinks needs to be turned off or the page will use the cache and the %>
22
22
  <% # analytics graph will not show unless the page is refreshed. %>
23
23
  <%= link_to t('.analytics'), presenter.stats_path, id: 'stats', class: 'btn btn-default', data: { turbolinks: false } %>
@@ -37,7 +37,7 @@
37
37
  data: { behavior: 'unfeature' },
38
38
  class: presenter.display_feature_link? ? 'btn btn-default collapse' : 'btn btn-default' %>
39
39
  <% end %>
40
- <% if Hyrax.config.analytics? %>
40
+ <% if Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
41
41
  <%= link_to t(".analytics"), presenter.stats_path, id: 'stats', class: 'btn btn-default' %>
42
42
  <% end %>
43
43
  </div>
@@ -2,7 +2,7 @@
2
2
  <h3 class="panel-title"><%= t('.title') %></h3>
3
3
  </div>
4
4
  <div class="panel-body text-center">
5
- <% if Hyrax.config.analytics? %>
5
+ <% if Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
6
6
  <%= render 'user_activity_graph' %>
7
7
 
8
8
  <div class="col-md-3">
@@ -2,7 +2,7 @@
2
2
  <h1><%= t("hyrax.dashboard.title") %></h1>
3
3
  <% end %>
4
4
 
5
- <% if Hyrax.config.analytics? %>
5
+ <% if Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
6
6
  <div class="row">
7
7
  <div class="col-md-12">
8
8
  <div class="panel panel-default">
@@ -32,7 +32,7 @@
32
32
  <% end %>
33
33
  <% end %>
34
34
 
35
- <% if current_ability.can_create_any_work? && Hyrax.config.analytics? %>
35
+ <% if current_ability.can_create_any_work? && Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
36
36
  <li>
37
37
  <%= menu.collapsable_section t('hyrax.admin.sidebar.analytics'),
38
38
  icon_class: "fa fa-pie-chart",
@@ -1,5 +1,5 @@
1
1
  <div class="form-actions">
2
- <% if Hyrax.config.analytics? %>
2
+ <% if Hyrax.config.analytics? && Hyrax.config.analytics_provider != 'ga4' %>
3
3
  <% # turbolinks needs to be turned off or the page will use the cache and the %>
4
4
  <% # analytics graph will not show unless the page is refreshed. %>
5
5
  <%= link_to t('.analytics'), @presenter.stats_path, id: 'stats', class: 'btn btn-default', data: { turbolinks: false } %>
@@ -26,9 +26,12 @@ signed in %>
26
26
  <%= render 'shared/appearance_styles' %>
27
27
 
28
28
  <% if Hyrax.config.analytics? %>
29
- <% if Hyrax.config.analytics_provider == 'google' %>
29
+ <% case Hyrax.config.analytics_provider %>
30
+ <% when 'ga4' %>
31
+ <%= render partial: 'shared/ga4', formats: [:html] %>
32
+ <% when 'google' %>
30
33
  <%= render partial: 'shared/ga', formats: [:html] %>
31
- <% elsif Hyrax.config.analytics_provider == 'matomo' %>
34
+ <% when 'matomo' %>
32
35
  <%= render partial: 'shared/matomo', formats: [:html] %>
33
36
  <% end %>
34
37
  <% end %>
@@ -0,0 +1,10 @@
1
+ <!-- Google tag (gtag.js) -->
2
+ <script async src="https://www.googletagmanager.com/gtag/js?id=<%= Hyrax::Analytics.config.analytics_id %>"></script>
3
+ <script>
4
+ window.dataLayer = window.dataLayer || [];
5
+ function gtag(){dataLayer.push(arguments);}
6
+ gtag('js', new Date());
7
+
8
+ gtag('config', '<%= Hyrax::Analytics.config.analytics_id %>');
9
+ </script>
10
+ <meta name="analytics-provider" content="ga4">
@@ -459,6 +459,14 @@ en:
459
459
  workflows:
460
460
  index:
461
461
  header: Review Submissions
462
+ heading:
463
+ work: Work
464
+ depositor: Depositor
465
+ submission_date: Submission Date
466
+ status: status
467
+ works_published: "<strong>%{total_count} works</strong> published"
468
+ works_under_review: "<strong>%{total_count} works</strong> under review"
469
+ works_listing: Works listing
462
470
  tabs:
463
471
  published: Published
464
472
  under_review: Under Review
@@ -125,7 +125,7 @@ NOTE: The steps need to be done in order to create a new Hyrax based app.
125
125
  Generate a new Rails application using the template.
126
126
 
127
127
  ```
128
- rails _5.2.8.1_ new my_app -m https://raw.githubusercontent.com/samvera/hyrax/v3.5.0/template.rb
128
+ rails _5.2.8.1_ new my_app -m https://raw.githubusercontent.com/samvera/hyrax/v3.6.0/template.rb
129
129
  ```
130
130
 
131
131
  Generating a new Rails application using Hyrax's template above takes cares of a number of steps for you, including:
@@ -50,7 +50,7 @@ The Samvera community is here to help. Please see our [support guide](./.github/
50
50
  # Getting started
51
51
 
52
52
  This document contains instructions specific to setting up an app with __Hyrax
53
- v3.5.0__. If you are looking for instructions on installing a different
53
+ v3.6.0__. If you are looking for instructions on installing a different
54
54
  version, be sure to select the appropriate branch or tag from the drop-down
55
55
  menu above.
56
56
 
@@ -159,7 +159,7 @@ NOTE: The steps need to be done in order to create a new Hyrax based app.
159
159
  Generate a new Rails application using the template.
160
160
 
161
161
  ```
162
- rails _5.2.8.1_ new my_app -m https://raw.githubusercontent.com/samvera/hyrax/v3.5.0/template.rb
162
+ rails _5.2.8.1_ new my_app -m https://raw.githubusercontent.com/samvera/hyrax/v3.6.0/template.rb
163
163
  ```
164
164
 
165
165
  Generating a new Rails application using Hyrax's template above takes cares of a number of steps for you, including:
data/hyrax.gemspec CHANGED
@@ -79,7 +79,7 @@ SUMMARY
79
79
  spec.add_dependency 'rdf-vocab', '~> 3.0'
80
80
  spec.add_dependency 'redis', '~> 4.0'
81
81
  spec.add_dependency 'redis-namespace', '~> 1.5'
82
- spec.add_dependency 'redlock', '>= 0.1.2'
82
+ spec.add_dependency 'redlock', '>= 0.1.2', '< 2.0'
83
83
  spec.add_dependency 'reform', '~> 2.3'
84
84
  spec.add_dependency 'reform-rails', '~> 0.2.0'
85
85
  spec.add_dependency 'retriable', '>= 2.9', '< 4.0'
@@ -2,10 +2,13 @@
2
2
  # You can manually fill in these values or use the ENV variables.
3
3
  #
4
4
  analytics:
5
+ ga4:
6
+ analytics_id: <%= ENV['GOOGLE_ANALYTICS_ID'] %>
5
7
  google:
6
8
  analytics_id: <%= ENV['GOOGLE_ANALYTICS_ID'] %>
7
9
  app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME'] %>
8
10
  app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION'] %>
11
+ privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE'] %>
9
12
  privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH'] %>
10
13
  privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET'] %>
11
14
  client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL'] %>
data/lib/hyrax/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Hyrax
3
- VERSION = '3.5.0'
3
+ VERSION = '3.6.0'
4
4
  end
data/template.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  # Hack for https://github.com/rails/rails/issues/35153
3
3
  gsub_file 'Gemfile', /^gem ["']sqlite3["']$/, 'gem "sqlite3", "~> 1.3.0"'
4
- gem 'hyrax', '3.5.0'
4
+ gem 'hyrax', '3.6.0'
5
5
  run 'bundle install'
6
6
  generate 'hyrax:install', '-f'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyrax
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.0
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2022-12-09 00:00:00.000000000 Z
17
+ date: 2023-06-27 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: rails
@@ -723,6 +723,9 @@ dependencies:
723
723
  - - ">="
724
724
  - !ruby/object:Gem::Version
725
725
  version: 0.1.2
726
+ - - "<"
727
+ - !ruby/object:Gem::Version
728
+ version: '2.0'
726
729
  type: :runtime
727
730
  prerelease: false
728
731
  version_requirements: !ruby/object:Gem::Requirement
@@ -730,6 +733,9 @@ dependencies:
730
733
  - - ">="
731
734
  - !ruby/object:Gem::Version
732
735
  version: 0.1.2
736
+ - - "<"
737
+ - !ruby/object:Gem::Version
738
+ version: '2.0'
733
739
  - !ruby/object:Gem::Dependency
734
740
  name: reform
735
741
  requirement: !ruby/object:Gem::Requirement
@@ -1527,8 +1533,6 @@ files:
1527
1533
  - ".github/SUPPORT.md"
1528
1534
  - ".github/release.yml"
1529
1535
  - ".github/stale.yml"
1530
- - ".github/workflows/main.yml"
1531
- - ".github/workflows/release.yml"
1532
1536
  - ".gitignore"
1533
1537
  - ".hound.yml"
1534
1538
  - ".regen"
@@ -2162,6 +2166,7 @@ files:
2162
2166
  - app/services/hyrax/admin_set_member_service.rb
2163
2167
  - app/services/hyrax/admin_set_service.rb
2164
2168
  - app/services/hyrax/analytics.rb
2169
+ - app/services/hyrax/analytics/ga4.rb
2165
2170
  - app/services/hyrax/analytics/google.rb
2166
2171
  - app/services/hyrax/analytics/google/events.rb
2167
2172
  - app/services/hyrax/analytics/google/events_daily.rb
@@ -2435,6 +2440,7 @@ files:
2435
2440
  - app/views/hyrax/admin/stats/show.html.erb
2436
2441
  - app/views/hyrax/admin/users/index.html.erb
2437
2442
  - app/views/hyrax/admin/workflow_roles/index.html.erb
2443
+ - app/views/hyrax/admin/workflows/_tabs.html.erb
2438
2444
  - app/views/hyrax/admin/workflows/index.html.erb
2439
2445
  - app/views/hyrax/base/_actions.html.erb
2440
2446
  - app/views/hyrax/base/_attribute_rows.html.erb
@@ -2779,6 +2785,7 @@ files:
2779
2785
  - app/views/shared/_citations.html.erb
2780
2786
  - app/views/shared/_footer.html.erb
2781
2787
  - app/views/shared/_ga.html.erb
2788
+ - app/views/shared/_ga4.html.erb
2782
2789
  - app/views/shared/_locale_picker.html.erb
2783
2790
  - app/views/shared/_matomo.html.erb
2784
2791
  - app/views/shared/_nav_safety_modal.html.erb
@@ -3184,7 +3191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
3184
3191
  - !ruby/object:Gem::Version
3185
3192
  version: '0'
3186
3193
  requirements: []
3187
- rubygems_version: 3.3.22
3194
+ rubygems_version: 3.4.12
3188
3195
  signing_key:
3189
3196
  specification_version: 4
3190
3197
  summary: Hyrax is a front-end based on the robust Samvera framework, providing a user
@@ -1,17 +0,0 @@
1
- name: Trigger Nurax build
2
- on:
3
- workflow_dispatch:
4
- push:
5
-
6
- jobs:
7
- trigger:
8
- runs-on: ubuntu-latest
9
- steps:
10
- - uses: peter-evans/repository-dispatch@v1
11
- with:
12
- token: ${{ secrets.NURAX_ACCESS_TOKEN }}
13
- event-type: push
14
- repository: curationexperts/nurax
15
- client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
16
-
17
-
@@ -1,17 +0,0 @@
1
- name: Trigger Nurax build
2
- on:
3
- workflow_dispatch:
4
- release:
5
-
6
- jobs:
7
- trigger:
8
- runs-on: ubuntu-latest
9
- steps:
10
- - uses: peter-evans/repository-dispatch@v1
11
- with:
12
- token: ${{ secrets.NURAX_ACCESS_TOKEN }}
13
- event-type: release
14
- repository: curationexperts/nurax
15
- client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
16
-
17
-