hyrax 3.5.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
-