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.
- checksums.yaml +4 -4
- data/.dassie/config/analytics.yml +4 -2
- data/.regen +2 -1
- data/app/assets/javascripts/hyrax/analytics_events.js +88 -38
- data/app/controllers/hyrax/admin/analytics/collection_reports_controller.rb +2 -2
- data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +1 -1
- data/app/controllers/hyrax/admin/strategies_controller.rb +1 -1
- data/app/controllers/hyrax/admin/workflows_controller.rb +48 -3
- data/app/services/hyrax/analytics/ga4.rb +204 -0
- data/app/services/hyrax/analytics/google.rb +23 -15
- data/app/services/hyrax/analytics/matomo.rb +4 -3
- data/app/services/hyrax/solr_query_service.rb +4 -4
- data/app/services/hyrax/workflow/actionable_objects.rb +28 -3
- data/app/services/hyrax/workflow/permission_query.rb +23 -2
- data/app/views/hyrax/admin/analytics/collection_reports/index.html.erb +1 -1
- data/app/views/hyrax/admin/analytics/work_reports/index.html.erb +1 -1
- data/app/views/hyrax/admin/workflows/_tabs.html.erb +9 -0
- data/app/views/hyrax/admin/workflows/index.html.erb +53 -78
- data/app/views/hyrax/base/_show_actions.html.erb +1 -1
- data/app/views/hyrax/base/_work_button_row.html.erb +1 -1
- data/app/views/hyrax/dashboard/_user_activity.html.erb +1 -1
- data/app/views/hyrax/dashboard/show_admin.html.erb +1 -1
- data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +1 -1
- data/app/views/hyrax/file_sets/_show_actions.html.erb +1 -1
- data/app/views/layouts/_head_tag_content.html.erb +5 -2
- data/app/views/shared/_ga4.html.erb +10 -0
- data/config/locales/hyrax.en.yml +8 -0
- data/documentation/developing-your-hyrax-based-app.md +1 -1
- data/documentation/legacyREADME.md +2 -2
- data/hyrax.gemspec +1 -1
- data/lib/generators/hyrax/templates/config/analytics.yml +3 -0
- data/lib/hyrax/version.rb +1 -1
- data/template.rb +1 -1
- metadata +12 -5
- data/.github/workflows/main.yml +0 -17
- data/.github/workflows/release.yml +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48404af16c40c5a6144f84f4747f391276dbbf03901c49947658904296ad1510
|
4
|
+
data.tar.gz: 6836a04cd80c63455b5670c591c102d5ac20224cd3746051eb6df0fc17daaaf5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
8
|
-
return _paq;
|
9
|
-
}
|
10
|
-
else {
|
11
|
-
return _gaq;
|
12
|
-
}
|
31
|
+
return this;
|
13
32
|
}
|
14
33
|
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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.
|
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.
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
65
|
-
|
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.
|
73
|
-
window.trackingTags.
|
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
|
@@ -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
|
-
@
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
57
|
-
class_eval %{ def #{key}; @config.fetch('#{key}'); end }
|
59
|
+
REQUIRED_KEYS.all? { |required| @config[required].present? }
|
58
60
|
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
@config['
|
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
|
-
|
81
|
-
private_key
|
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:
|
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['
|
15
|
-
# ` site_id: <%= ENV['
|
16
|
-
# ` auth_token: <%= ENV['
|
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
|
-
|
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))
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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">
|
@@ -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
|
-
<%
|
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
|
-
<%
|
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">
|
data/config/locales/hyrax.en.yml
CHANGED
@@ -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.
|
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.
|
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.
|
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
data/template.rb
CHANGED
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.
|
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:
|
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.
|
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
|
data/.github/workflows/main.yml
DELETED
@@ -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
|
-
|