-
Notifications
You must be signed in to change notification settings - Fork 7
/
googleanalytics.admin.inc
977 lines (894 loc) · 48.2 KB
/
googleanalytics.admin.inc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
<?php
/**
* @file
* Administrative page callbacks for the googleanalytics module.
*/
/**
* Implements hook_admin_settings() for module settings configuration.
*/
function googleanalytics_admin_settings_form($form_state) {
$config = config('googleanalytics.settings');
$form['account'] = array(
'#type' => 'fieldset',
'#title' => t('General settings'),
);
$form['account']['googleanalytics_account'] = array(
'#title' => t('Web Property ID'),
'#type' => 'textfield',
'#default_value' => $config->get('account'),
'#attributes' => array('placeholder' => 'UA- or G-'),
'#size' => 40,
'#required' => TRUE,
'#description' => t('This ID is unique to each site you want to track separately, and is in the form UA-xxxxxxx-yy, G-XXXXXXX, DC-XXXXXXX, or AW-XXXXXXX. To get a Web Property ID, <a href="@analytics">register your site with Google Analytics</a>, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. <a href="@webpropertyid">Find more information in the documentation</a>. If you want to track with multiple IDs (for example, using both GA4 and UA for legacy tracking) seperate them with commas.', array('@analytics' => 'https://marketingplatform.google.com/about/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty')))),
);
$form['account']['googleanalytics_premium'] = array(
'#type' => 'checkbox',
'#title' => t('Premium account'),
'#description' => t('If you are a Google Analytics Premium customer, you can use up to 200 instead of 20 custom dimensions and metrics.'),
'#default_value' => $config->get('premium'),
);
// Visibility settings.
$form['tracking_title'] = array(
'#type' => 'item',
'#title' => t('Tracking scope'),
);
$form['tracking'] = array(
'#type' => 'vertical_tabs',
'#attached' => array(
'js' => array(backdrop_get_path('module', 'googleanalytics') . '/js/googleanalytics.admin.js'),
),
);
$form['tracking']['domain_tracking'] = array(
'#type' => 'fieldset',
'#title' => t('Domains'),
);
global $cookie_domain;
$multiple_sub_domains = array();
foreach (array('www', 'app', 'shop') as $subdomain) {
if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
$multiple_sub_domains[] = $subdomain . $cookie_domain;
}
// IP addresses or localhost.
else {
$multiple_sub_domains[] = $subdomain . '.example.com';
}
}
$multiple_toplevel_domains = array();
foreach (array('.com', '.net', '.org') as $tldomain) {
$host = $_SERVER['HTTP_HOST'];
$domain = substr($host, 0, strrpos($host, '.'));
if (count(explode('.', $host)) > 2 && !is_numeric(str_replace('.', '', $host))) {
$multiple_toplevel_domains[] = $domain . $tldomain;
}
// IP addresses or localhost
else {
$multiple_toplevel_domains[] = 'www.example' . $tldomain;
}
}
$form['tracking']['domain_tracking']['googleanalytics_domain_mode'] = array(
'#type' => 'radios',
'#title' => t('What are you tracking?'),
'#options' => array(
0 => t('A single domain (default)') . '<div class="description">' . t('Domain: @domain', array('@domain' => $_SERVER['HTTP_HOST'])) . '</div>',
1 => t('One domain with multiple subdomains') . '<div class="description">' . t('Examples: @domains', array('@domains' => implode(', ', $multiple_sub_domains))) . '</div>',
2 => t('Multiple top-level domains') . '<div class="description">' . t('Examples: @domains', array('@domains' => implode(', ', $multiple_toplevel_domains))) . '</div>',
),
'#default_value' => $config->get('domain_mode'),
);
$form['tracking']['domain_tracking']['googleanalytics_cross_domains'] = array(
'#title' => t('List of top-level domains'),
'#type' => 'textarea',
'#default_value' => $config->get('cross_domains'),
'#description' => t('If you selected "Multiple top-level domains" above, enter all related top-level domains. Add one domain per line. By default, the data in your reports only includes the path and name of the page, and not the domain name. For more information see section <em>Show separate domain names</em> in <a href="@url">Tracking Multiple Domains</a>.', array('@url' => 'https://support.google.com/analytics/answer/1034342')),
'#states' => array(
'enabled' => array(
':input[name="googleanalytics_domain_mode"]' => array('value' => '2'),
),
'required' => array(
':input[name="googleanalytics_domain_mode"]' => array('value' => '2'),
),
),
);
// Page specific visibility configurations.
$visibility = $config->get('visibility_pages');
$pages = $config->get('pages');
$form['tracking']['page_vis_settings'] = array(
'#type' => 'fieldset',
'#title' => t('Pages'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$options = array(
t('Every page except the listed pages'),
t('The listed pages only'),
);
$description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
$title = t('Pages');
$form['tracking']['page_vis_settings']['googleanalytics_visibility_pages'] = array(
'#type' => 'radios',
'#title' => t('Add tracking to specific pages'),
'#options' => $options,
'#default_value' => $visibility,
);
$form['tracking']['page_vis_settings']['googleanalytics_pages'] = array(
'#type' => 'textarea',
'#title' => $title,
'#title_display' => 'invisible',
'#default_value' => $pages,
'#description' => $description,
'#rows' => 10,
);
// Render the role overview.
$form['tracking']['role_vis_settings'] = array(
'#type' => 'fieldset',
'#title' => t('Roles'),
);
$form['tracking']['role_vis_settings']['googleanalytics_visibility_roles'] = array(
'#type' => 'radios',
'#title' => t('Add tracking for specific roles'),
'#options' => array(
t('Add to the selected roles only'),
t('Add to every role except the selected ones'),
),
'#default_value' => $config->get('visibility_roles'),
);
$role_options = array_map('check_plain', user_roles());
$form['tracking']['role_vis_settings']['googleanalytics_roles'] = array(
'#type' => 'checkboxes',
'#title' => t('Roles'),
'#default_value' => $config->get('roles'),
'#options' => $role_options,
'#description' => t('If none of the roles are selected, all users will be tracked. If a user has any of the roles checked, that user will be tracked (or excluded, depending on the setting above).'),
);
// Standard tracking configurations.
$form['tracking']['user_vis_settings'] = array(
'#type' => 'fieldset',
'#title' => t('Users'),
);
$t_permission = array('%permission' => t('Opt-in or out of tracking'));
$form['tracking']['user_vis_settings']['googleanalytics_custom'] = array(
'#type' => 'radios',
'#title' => t('Allow users to customize tracking on their account page'),
'#options' => array(
t('No customization allowed'),
t('Tracking on by default, users with %permission permission can opt out', $t_permission),
t('Tracking off by default, users with %permission permission can opt in', $t_permission),
),
'#default_value' => $config->get('custom'),
);
$form['tracking']['user_vis_settings']['googleanalytics_trackuserid'] = array(
'#type' => 'checkbox',
'#title' => t('Track User ID'),
'#default_value' => $config->get('trackuserid'),
'#description' => t('User ID enables the analysis of groups of sessions, across devices, using a unique, persistent, and non-personally identifiable ID string representing a user. <a href="@url">Learn more about the benefits of using User ID</a>.', array('@url' => 'https://support.google.com/analytics/answer/3123663')),
);
// Link specific configurations.
$form['tracking']['linktracking'] = array(
'#type' => 'fieldset',
'#title' => t('Links and downloads'),
);
$form['tracking']['linktracking']['googleanalytics_trackoutbound'] = array(
'#type' => 'checkbox',
'#title' => t('Track clicks on outbound links'),
'#default_value' => $config->get('trackoutbound'),
);
$form['tracking']['linktracking']['googleanalytics_trackmailto'] = array(
'#type' => 'checkbox',
'#title' => t('Track clicks on mailto links'),
'#default_value' => $config->get('trackmailto'),
);
$form['tracking']['linktracking']['googleanalytics_trackfiles'] = array(
'#type' => 'checkbox',
'#title' => t('Track downloads (clicks on file links) for the following extensions'),
'#default_value' => $config->get('trackfiles'),
);
$form['tracking']['linktracking']['googleanalytics_trackfiles_extensions'] = array(
'#title' => t('List of download file extensions'),
'#title_display' => 'invisible',
'#type' => 'textfield',
'#default_value' => $config->get('trackfiles_extensions'),
'#description' => t('A file extension list separated by the | character that will be tracked as download when clicked. Regular expressions are supported. For example: !extensions', array('!extensions' => GOOGLEANALYTICS_TRACKFILES_EXTENSIONS)),
'#maxlength' => 500,
'#states' => array(
'enabled' => array(
':input[name="googleanalytics_trackfiles"]' => array('checked' => TRUE),
),
// Note: Form required marker is not visible as title is invisible.
'required' => array(
':input[name="googleanalytics_trackfiles"]' => array('checked' => TRUE),
),
),
);
$colorbox_dependencies = '<div class="admin-requirements">';
$colorbox_dependencies .= t('Requires: !module-list', array(
'!module-list' => (module_exists('colorbox') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Colorbox')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Colorbox'))),
));
$colorbox_dependencies .= '</div>';
$form['tracking']['linktracking']['googleanalytics_trackcolorbox'] = array(
'#type' => 'checkbox',
'#title' => t('Track content in colorbox modal dialogs'),
'#default_value' => $config->get('trackcolorbox'),
'#description' => t('Enable to track the content shown in colorbox modal windows.') . $colorbox_dependencies,
'#disabled' => (module_exists('colorbox') ? FALSE : TRUE),
);
$form['tracking']['linktracking']['googleanalytics_tracklinkid'] = array(
'#type' => 'checkbox',
'#title' => t('Track enhanced link attribution'),
'#default_value' => $config->get('tracklinkid'),
'#description' => t('Enhanced Link Attribution improves the accuracy of your In-Page Analytics report by automatically differentiating between multiple links to the same URL on a single page by using link element IDs. <a href="@url">Enable enhanced link attribution</a> in the Admin UI of your Google Analytics account.', array('@url' => 'https://support.google.com/analytics/answer/2558867')),
);
$form['tracking']['linktracking']['googleanalytics_trackurlfragments'] = array(
'#type' => 'checkbox',
'#title' => t('Track changing URL fragments as pageviews'),
'#default_value' => $config->get('trackurlfragments'),
'#description' => t('By default, the URL reported to Google Analytics will not include the "fragment identifier" (i.e. the portion of the URL beginning with a hash sign), and hash changes by themselves will not cause new pageviews to be reported. Checking this box will cause hash changes to be reported as pageviews (in modern browsers) and all pageview URLs to include the fragment where applicable.'),
);
// Message specific configurations.
$form['tracking']['messagetracking'] = array(
'#type' => 'fieldset',
'#title' => t('Messages'),
);
$form['tracking']['messagetracking']['googleanalytics_trackmessages'] = array(
'#type' => 'checkboxes',
'#title' => t('Track messages of type'),
'#default_value' => $config->get('trackmessages'),
'#description' => t('This will track the selected message types shown to users. Tracking of form validation errors may help you identifying usability issues in your site. For each visit (user session), a maximum of approximately 500 combined GATC requests (both events and page views) can be tracked. Every message is tracked as one individual event. Note that - as the number of events in a session approaches the limit - additional events might not be tracked. Messages from excluded pages cannot be tracked.'),
'#options' => array(
'status' => t('Status message'),
'warning' => t('Warning message'),
'error' => t('Error message'),
),
);
$form['tracking']['search_and_advertising'] = array(
'#type' => 'fieldset',
'#title' => t('Search and Advertising'),
);
$site_search_dependencies = '<div class="admin-requirements">';
$site_search_dependencies .= t('Requires: !module-list', array('!module-list' => (module_exists('search') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Search')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Search')))));
$site_search_dependencies .= '</div>';
$form['tracking']['search_and_advertising']['googleanalytics_site_search'] = array(
'#type' => 'checkbox',
'#title' => t('Track internal search'),
'#description' => t('If checked, internal search keywords are tracked. You must configure your Google account to use the internal query parameter <strong>search</strong>. For more information see <a href="@url">Setting Up Site Search for a Profile</a>.', array('@url' => 'https://support.google.com/analytics/answer/1012264')) . $site_search_dependencies,
'#default_value' => $config->get('site_search'),
'#disabled' => (module_exists('search') ? FALSE : TRUE),
);
$form['tracking']['search_and_advertising']['googleanalytics_trackadsense'] = array(
'#type' => 'checkbox',
'#title' => t('Track AdSense ads'),
'#description' => t('If checked, your AdSense ads will be tracked in your Google Analytics account.'),
'#default_value' => $config->get('trackadsense'),
);
$form['tracking']['search_and_advertising']['googleanalytics_trackdoubleclick'] = array(
'#type' => 'checkbox',
'#title' => t('Track display features'),
'#description' => t('The display features plugin can be used to enable Display Advertising Features in Google Analytics, such as Remarketing, Demographics and Interest Reporting, and more. <a href="@displayfeatures">Learn more about Display Advertising Features in Google Analytics</a>. If you choose this option you will need to <a href="@privacy">update your privacy policy</a>.', array('@displayfeatures' => 'https://support.google.com/analytics/answer/3450482', '@privacy' => 'https://support.google.com/analytics/answer/2700409')),
'#default_value' => $config->get('trackdoubleclick'),
);
// Privacy specific configurations.
$form['tracking']['privacy'] = array(
'#type' => 'fieldset',
'#title' => t('Privacy'),
);
$form['tracking']['privacy']['googleanalytics_tracker_anonymizeip'] = array(
'#type' => 'checkbox',
'#title' => t('Anonymize visitors IP address'),
'#description' => t('Tell Google Analytics to anonymize the information sent by the tracker objects by removing the last octet of the IP address prior to its storage. Note that this will slightly reduce the accuracy of geographic reporting. In some countries it is not allowed to collect personally identifying information for privacy reasons and this setting may help you to comply with the local laws.'),
'#default_value' => $config->get('tracker_anonymizeip'),
);
$form['tracking']['privacy']['googleanalytics_privacy_donottrack'] = array(
'#type' => 'checkbox',
'#title' => t('Universal web tracking opt-out'),
'#description' => t('WARNING: Enabling this option will prevent the tracking code from appearing in Google Chrome.<br><br>
If enabled and your server receives the <a href="@donottrack">Do-Not-Track</a> header from the client browser, the Google Analytics module will not embed any tracking code into your site. Compliance with Do Not Track could be purely voluntary, enforced by industry self-regulation, or mandated by state or federal law. Please accept your visitors privacy. If they have opt-out from tracking and advertising, you should accept their personal decision. This feature is currently limited to logged in users and disabled page caching.', array('@donottrack' => 'https://donottrack.us/')),
'#default_value' => $config->get('privacy_donottrack'),
);
// Custom Dimensions.
$form['googleanalytics_custom_dimension'] = array(
'#collapsed' => TRUE,
'#collapsible' => TRUE,
'#description' => t('You can set values for Google Analytics <a href="@custom_var_documentation">Custom Dimensions</a> here. You must have already configured your custom dimensions in the <a href="@setup_documentation">Google Analytics Management Interface</a>. You may use tokens. Global and user tokens are always available; on node pages, node tokens are also available. A dimension <em>value</em> is allowed to have a maximum length of 150 bytes. Expect longer values to get trimmed.', array('@custom_var_documentation' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets', '@setup_documentation' => 'https://support.google.com/analytics/answer/2709829')),
'#theme' => 'googleanalytics_admin_custom_var_table',
'#title' => t('Custom dimensions'),
'#tree' => TRUE,
'#type' => 'fieldset',
);
$form['tracking']['privacy']['googleanalytics_disable_cookies'] = array(
'#type' => 'checkbox',
'#title' => t('Disable cookies'),
'#default_value' => $config->get('disable_cookies'),
);
$googleanalytics_custom_dimension = $config->get('custom_dimension');
// Standard Google Analytics accounts support up to 20 custom dimensions,
// premium accounts support up to 200 custom dimensions.
$limit = ($config->get('account')) ? 200 : 20;
for ($i = 1; $i <= $limit; $i++) {
$form['googleanalytics_custom_dimension']['indexes'][$i]['index'] = array(
'#default_value' => $i,
'#description' => t('Index number'),
'#disabled' => TRUE,
'#size' => 1,
'#title' => t('Custom dimension index #@index', array('@index' => $i)),
'#title_display' => 'invisible',
'#type' => 'textfield',
);
$form['googleanalytics_custom_dimension']['indexes'][$i]['name'] = array(
'#default_value' => isset($googleanalytics_custom_dimension[$i]['name']) ? $googleanalytics_custom_dimension[$i]['name'] : '',
'#description' => t('The custom dimension name.'),
'#maxlength' => 255,
'#title' => t('Custom dimension name #@index', array('@index' => $i)),
'#title_display' => 'invisible',
'#type' => 'textfield',
);
$form['googleanalytics_custom_dimension']['indexes'][$i]['value'] = array(
'#default_value' => isset($googleanalytics_custom_dimension[$i]['value']) ? $googleanalytics_custom_dimension[$i]['value'] : '',
'#description' => t('The custom dimension value.'),
'#maxlength' => 255,
'#title' => t('Custom dimension value #@index', array('@index' => $i)),
'#title_display' => 'invisible',
'#type' => 'textfield',
'#element_validate' => array('googleanalytics_token_element_validate'),
'#token_types' => array('node'),
);
if (module_exists('token')) {
$form['googleanalytics_custom_dimension']['indexes'][$i]['value']['#element_validate'][] = 'token_element_validate';
}
}
$form['googleanalytics_custom_dimension']['googleanalytics_description'] = array(
'#type' => 'item',
'#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom dimensions. Section 7 of the <a href="@ga_tos">Google Analytics terms of service</a> requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'https://www.google.com/analytics/terms/gb.html')),
);
if (module_exists('token')) {
$form['googleanalytics_custom_dimension']['googleanalytics_token_tree'] = array(
'#theme' => 'token_tree',
'#token_types' => array('node'),
'#dialog' => TRUE,
);
}
// Custom Metrics.
$form['googleanalytics_custom_metric'] = array(
'#collapsed' => TRUE,
'#collapsible' => TRUE,
'#description' => t('You can add Google Analytics <a href="@custom_var_documentation">Custom Metrics</a> here. You must have already configured your custom metrics in the <a href="@setup_documentation">Google Analytics Management Interface</a>. You may use tokens. Global and user tokens are always available; on node pages, node tokens are also available.', array('@custom_var_documentation' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets', '@setup_documentation' => 'https://support.google.com/analytics/answer/2709829')),
'#theme' => 'googleanalytics_admin_custom_var_table',
'#title' => t('Custom metrics'),
'#tree' => TRUE,
'#type' => 'fieldset',
);
$googleanalytics_custom_metric = $config->get('custom_metric');
// Standard Google Analytics accounts support up to 20 custom metrics,
// premium accounts support up to 200 custom metrics.
for ($i = 1; $i <= $limit; $i++) {
$form['googleanalytics_custom_metric']['indexes'][$i]['index'] = array(
'#default_value' => $i,
'#description' => t('Index number'),
'#disabled' => TRUE,
'#size' => 1,
'#title' => t('Custom metric index #@index', array('@index' => $i)),
'#title_display' => 'invisible',
'#type' => 'textfield',
);
$form['googleanalytics_custom_metric']['indexes'][$i]['name'] = array(
'#default_value' => isset($googleanalytics_custom_metric[$i]['name']) ? $googleanalytics_custom_metric[$i]['name'] : '',
'#description' => t('The custom metric name.'),
'#maxlength' => 255,
'#title' => t('Custom metric name #@index', array('@index' => $i)),
'#title_display' => 'invisible',
'#type' => 'textfield',
);
$form['googleanalytics_custom_metric']['indexes'][$i]['value'] = array(
'#default_value' => isset($googleanalytics_custom_metric[$i]['value']) ? $googleanalytics_custom_metric[$i]['value'] : '',
'#description' => t('The custom metric value.'),
'#maxlength' => 255,
'#title' => t('Custom metric value #@index', array('@index' => $i)),
'#title_display' => 'invisible',
'#type' => 'textfield',
'#element_validate' => array('googleanalytics_token_element_validate'),
'#token_types' => array('node'),
);
if (module_exists('token')) {
$form['googleanalytics_custom_metric']['indexes'][$i]['value']['#element_validate'][] = 'token_element_validate';
}
}
$form['googleanalytics_custom_metric']['googleanalytics_description'] = array(
'#type' => 'item',
'#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom metrics. Section 7 of the <a href="@ga_tos">Google Analytics terms of service</a> requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'https://www.google.com/analytics/terms/gb.html')),
);
if (module_exists('token')) {
$form['googleanalytics_custom_metric']['googleanalytics_token_tree'] = array(
'#theme' => 'token_tree',
'#token_types' => array('node'),
'#dialog' => TRUE,
);
}
// Advanced feature configurations.
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['advanced']['googleanalytics_cache'] = array(
'#type' => 'checkbox',
'#title' => t('Locally cache tracking code file'),
'#description' => t("If checked, the tracking code file is retrieved from Google Analytics and cached locally. It is updated daily from Google's servers to ensure updates to tracking code are reflected in the local copy. Do not activate this until after Google Analytics has confirmed that site tracking is working!"),
'#default_value' => $config->get('cache'),
);
// Allow for tracking of the originating node when viewing translation sets.
if (module_exists('translation')) {
$form['advanced']['googleanalytics_translation_set'] = array(
'#type' => 'checkbox',
'#title' => t('Track translation sets as one unit'),
'#description' => t('When a node is part of a translation set, record statistics for the originating node instead. This allows for a translation set to be treated as a single unit.'),
'#default_value' => $config->get('translation_set'),
);
}
$user_access_add_js_snippets = !user_access('add JS snippets for google analytics');
$user_access_add_js_snippets_permission_warning = $user_access_add_js_snippets ? ' <em>' . t('This field has been disabled because you do not have sufficient permissions to edit it.') . '</em>' : '';
$form['advanced']['codesnippet'] = array(
'#type' => 'fieldset',
'#title' => t('Custom JavaScript code'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => t('You can add custom Google Analytics <a href="@snippets">code snippets</a> here. These will be added every time tracking is in effect. Before you add your custom code, you should read the <a href="@ga_concepts_overview">Google Analytics Tracking Code - Functional Overview</a> and the <a href="@ga_js_api">Google Analytics Tracking API</a> documentation. <strong>Do not include the <script> tags</strong>, and always end your code with a semicolon (;).', array('@snippets' => 'https://drupal.org/node/248699', '@ga_concepts_overview' => 'https://developers.google.com/analytics/resources/concepts/gaConceptsTrackingOverview', '@ga_js_api' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/method-reference')),
);
$form['advanced']['codesnippet']['googleanalytics_codesnippet_create'] = array(
'#type' => 'textarea',
'#title' => t('Create only fields'),
'#default_value' => _googleanalytics_get_name_value_string($config->get('codesnippet_create')),
'#rows' => 5,
'#description' => t('Enter one value per line, in the format name|value. Settings in this textarea will be added to <code>gtag("config", "UA-XXXX-Y", {"name":"value"});</code>. For more information, read <a href="@url">create only fields</a> documentation in the Analytics.js field reference.', array('@url' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#create')),
'#element_validate' => array('googleanalytics_validate_create_field_values'),
);
$form['advanced']['codesnippet']['googleanalytics_codesnippet_before'] = array(
'#type' => 'textarea',
'#title' => t('Code snippet (before)'),
'#default_value' => $config->get('codesnippet_before'),
'#disabled' => $user_access_add_js_snippets,
'#rows' => 5,
'#description' => t('Code in this textarea will be added <strong>before</strong> <code>gtag("event", "page_view");</code>.') . $user_access_add_js_snippets_permission_warning,
);
$form['advanced']['codesnippet']['googleanalytics_codesnippet_after'] = array(
'#type' => 'textarea',
'#title' => t('Code snippet (after)'),
'#default_value' => $config->get('codesnippet_after'),
'#disabled' => $user_access_add_js_snippets,
'#rows' => 5,
'#description' => t('Code in this textarea will be added <strong>after</strong> <code>gtag("event", "page_view");</code>. This is useful if you\'d like to track a site in two accounts.') . $user_access_add_js_snippets_permission_warning,
);
$form['advanced']['googleanalytics_debug'] = array(
'#type' => 'checkbox',
'#title' => t('Enable debugging'),
'#description' => t('If checked, the Google Universal Analytics debugging script will be loaded. You should not enable your production site to use this version of the JavaScript. The analytics_debug.js script is larger than the analytics.js tracking code and it is not typically cached. Using it in your production site will slow down your site for all of your users. Again, this is only for your own testing purposes. Debug messages are printed to the <code>window.console</code> object.'),
'#default_value' => $config->get('debug'),
);
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
return $form;
}
/**
* Validation handler for googleanalytics_admin_settings_form().
*/
function googleanalytics_admin_settings_form_validate($form, &$form_state) {
// Trim custom dimensions and metrics.
foreach ($form_state['values']['googleanalytics_custom_dimension']['indexes'] as $dimension) {
$form_state['values']['googleanalytics_custom_dimension']['indexes'][$dimension['index']]['name'] = trim($dimension['name']);
$form_state['values']['googleanalytics_custom_dimension']['indexes'][$dimension['index']]['value'] = trim($dimension['value']);
// Remove empty values from the array.
if (!backdrop_strlen($form_state['values']['googleanalytics_custom_dimension']['indexes'][$dimension['index']]['value'])) {
unset($form_state['values']['googleanalytics_custom_dimension']['indexes'][$dimension['index']]);
}
}
$form_state['values']['googleanalytics_custom_dimension'] = $form_state['values']['googleanalytics_custom_dimension']['indexes'];
foreach ($form_state['values']['googleanalytics_custom_metric']['indexes'] as $metric) {
$form_state['values']['googleanalytics_custom_metric']['indexes'][$metric['index']]['name'] = trim($metric['name']);
$form_state['values']['googleanalytics_custom_metric']['indexes'][$metric['index']]['value'] = trim($metric['value']);
// Remove empty values from the array.
if (!backdrop_strlen($form_state['values']['googleanalytics_custom_metric']['indexes'][$metric['index']]['value'])) {
unset($form_state['values']['googleanalytics_custom_metric']['indexes'][$metric['index']]);
}
}
$form_state['values']['googleanalytics_custom_metric'] = $form_state['values']['googleanalytics_custom_metric']['indexes'];
// Trim some text values.
$form_state['values']['googleanalytics_account'] = trim($form_state['values']['googleanalytics_account']);
$form_state['values']['googleanalytics_pages'] = trim($form_state['values']['googleanalytics_pages']);
$form_state['values']['googleanalytics_cross_domains'] = trim($form_state['values']['googleanalytics_cross_domains']);
$form_state['values']['googleanalytics_codesnippet_create'] = _googleanalytics_extract_create_field_values($form_state['values']['googleanalytics_codesnippet_create']);
$form_state['values']['googleanalytics_codesnippet_before'] = trim($form_state['values']['googleanalytics_codesnippet_before']);
$form_state['values']['googleanalytics_codesnippet_after'] = trim($form_state['values']['googleanalytics_codesnippet_after']);
// Replace all type of dashes (n-dash, m-dash, minus) with the normal dashes.
$form_state['values']['googleanalytics_account'] = str_replace(array('–', '—', '−'), '-', $form_state['values']['googleanalytics_account']);
$account_parts = array_filter(array_map("trim", explode(",", $form_state['values']['googleanalytics_account'])));
foreach ($account_parts as $account) {
if (!_google_analytics_valid_property_id($account)) {
form_set_error('googleanalytics_account', t('A valid Web Property ID is case sensitive and formatted like UA-xxxxxxx-yy, G-XXXXXXX, DC-XXXXXXX, or AW-XXXXXXX.'));
}
}
// If multiple top-level domains has been selected, a domain names list is required.
if ($form_state['values']['googleanalytics_domain_mode'] == 2 && empty($form_state['values']['googleanalytics_cross_domains'])) {
form_set_error('googleanalytics_cross_domains', t('A list of top-level domains is required if <em>Multiple top-level domains</em> has been selected.'));
}
// Clear the domain list if cross domains are disabled.
if ($form_state['values']['googleanalytics_domain_mode'] != 2) {
$form_state['values']['googleanalytics_cross_domains'] = '';
}
// Disallow empty list of download file extensions.
if ($form_state['values']['googleanalytics_trackfiles'] && empty($form_state['values']['googleanalytics_trackfiles_extensions'])) {
form_set_error('googleanalytics_trackfiles_extensions', t('List of download file extensions cannot empty.'));
}
// Clear obsolete local cache if cache has been disabled.
if (empty($form_state['values']['googleanalytics_cache']) && $form['advanced']['googleanalytics_cache']['#default_value']) {
googleanalytics_clear_js_cache();
}
// This is for the Newbie's who cannot read a text area description.
if (stristr($form_state['values']['googleanalytics_codesnippet_before'], 'google-analytics.com/analytics.js')) {
form_set_error('googleanalytics_codesnippet_before', t('Do not add the tracker code provided by Google into the javascript code snippets! This module already builds the tracker code based on your Google Analytics account number and settings.'));
}
if (stristr($form_state['values']['googleanalytics_codesnippet_after'], 'google-analytics.com/analytics.js')) {
form_set_error('googleanalytics_codesnippet_after', t('Do not add the tracker code provided by Google into the javascript code snippets! This module already builds the tracker code based on your Google Analytics account number and settings.'));
}
if (preg_match('/(.*)<\/?script(.*)>(.*)/i', $form_state['values']['googleanalytics_codesnippet_before'])) {
form_set_error('googleanalytics_codesnippet_before', t('Do not include the <script> tags in the javascript code snippets.'));
}
if (preg_match('/(.*)<\/?script(.*)>(.*)/i', $form_state['values']['googleanalytics_codesnippet_after'])) {
form_set_error('googleanalytics_codesnippet_after', t('Do not include the <script> tags in the javascript code snippets.'));
}
}
/**
* Submit handler for googleanalytics_admin_settings_form().
*/
function googleanalytics_admin_settings_form_submit($form, &$form_state) {
$config = config('googleanalytics.settings');
$config->set('account', $form_state['values']['googleanalytics_account']);
$config->set('domain_mode', (int) $form_state['values']['googleanalytics_domain_mode']);
$config->set('cross_domains', $form_state['values']['googleanalytics_cross_domains']);
$config->set('visibility_pages', (int) $form_state['values']['googleanalytics_visibility_pages']);
$config->set('pages', $form_state['values']['googleanalytics_pages']);
$config->set('visibility_roles', (int) $form_state['values']['googleanalytics_visibility_roles']);
$config->set('roles', array_keys(array_filter($form_state['values']['googleanalytics_roles'])));
$config->set('custom', $form_state['values']['googleanalytics_custom']);
$config->set('trackuserid', (bool) $form_state['values']['googleanalytics_trackuserid']);
$config->set('trackoutbound', (bool) $form_state['values']['googleanalytics_trackoutbound']);
$config->set('trackmailto', (bool) $form_state['values']['googleanalytics_trackmailto']);
$config->set('trackfiles', (bool) $form_state['values']['googleanalytics_trackfiles']);
$config->set('trackfiles_extensions', $form_state['values']['googleanalytics_trackfiles_extensions']);
$config->set('tracklinkid', (bool) $form_state['values']['googleanalytics_tracklinkid']);
$config->set('trackurlfragments', (bool) $form_state['values']['googleanalytics_trackurlfragments']);
$config->set('trackmessages', $form_state['values']['googleanalytics_trackmessages']);
$config->set('site_search', (bool) $form_state['values']['googleanalytics_site_search']);
$config->set('trackadsense', (bool) $form_state['values']['googleanalytics_trackadsense']);
$config->set('trackdoubleclick', (bool) $form_state['values']['googleanalytics_trackdoubleclick']);
$config->set('tracker_anonymizeip', (bool) $form_state['values']['googleanalytics_tracker_anonymizeip']);
$config->set('privacy_donottrack', (bool) $form_state['values']['googleanalytics_privacy_donottrack']);
$config->set('custom_dimension', $form_state['values']['googleanalytics_custom_dimension']);
$config->set('custom_metric', $form_state['values']['googleanalytics_custom_metric']);
$config->set('cache', (bool) $form_state['values']['googleanalytics_cache']);
$config->set('codesnippet_create', $form_state['values']['googleanalytics_codesnippet_create']);
$config->set('codesnippet_before', $form_state['values']['googleanalytics_codesnippet_before']);
$config->set('codesnippet_after', $form_state['values']['googleanalytics_codesnippet_after']);
$config->set('debug', (bool) $form_state['values']['googleanalytics_debug']);
if (module_exists('translation')) {
$config->set('translation_set', $form_state['values']['googleanalytics_translation_set']);
}
$config->save();
backdrop_set_message(t('The configuration options have been saved.'));
}
/**
* Layout for the custom variables table in the admin settings form.
*
* @param array $variables
* An array contains the form elements.
*
* @return string
* The rendered output.
*/
function theme_googleanalytics_admin_custom_var_table($variables) {
$form = $variables['form'];
$header = array(
array('data' => t('Index')),
array('data' => t('Name')),
array('data' => t('Value')),
);
$rows = array();
foreach (element_children($form['indexes']) as $id) {
$rows[] = array(
'data' => array(
backdrop_render($form['indexes'][$id]['index']),
backdrop_render($form['indexes'][$id]['name']),
backdrop_render($form['indexes'][$id]['value']),
),
);
}
$output = theme('table', array('header' => $header, 'rows' => $rows));
$output .= backdrop_render($form['googleanalytics_description']);
if (isset($form['googleanalytics_token_tree'])) {
$output .= backdrop_render($form['googleanalytics_token_tree']);
}
return $output;
}
/**
* Validate a form element that should have tokens in it.
*
* For example:
* @code
* $form['my_node_text_element'] = array(
* '#type' => 'textfield',
* '#title' => t('Some text to token-ize that has a node context.'),
* '#default_value' => 'The title of this node is [node:title].',
* '#element_validate' => array('googleanalytics_token_element_validate'),
* );
* @endcode
*/
function googleanalytics_token_element_validate(&$element, &$form_state) {
$value = isset($element['#value']) ? $element['#value'] : $element['#default_value'];
if (!backdrop_strlen($value)) {
// Empty value needs no further validation since the element should depend
// on using the '#required' FAPI property.
return $element;
}
$tokens = token_scan($value);
$invalid_tokens = _googleanalytics_get_forbidden_tokens($tokens);
if ($invalid_tokens) {
form_error($element, t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => $element['#title'], '@invalid-tokens' => implode(', ', $invalid_tokens))));
}
return $element;
}
/**
* @param array $value
* An array of token values.
*
* @return array
* A unique array of invalid tokens.
*/
function _googleanalytics_get_forbidden_tokens($value) {
$invalid_tokens = array();
$value_tokens = is_string($value) ? token_scan($value) : $value;
foreach ($value_tokens as $tokens) {
if (array_filter($tokens, '_googleanalytics_contains_forbidden_token')) {
$invalid_tokens = array_merge($invalid_tokens, array_values($tokens));
}
}
array_unique($invalid_tokens);
return $invalid_tokens;
}
/**
* Validate if a string contains forbidden tokens not allowed by privacy rules.
*
* @param string $token_string
* A string with one or more tokens to be validated.
*
* @return boolean
* TRUE if blacklisted token has been found, otherwise FALSE.
*/
function _googleanalytics_contains_forbidden_token($token_string) {
// List of strings in tokens with personal identifying information not allowed
// for privacy reasons. See section 8.1 of the Google Analytics terms of use
// for more detailed information.
//
// This list can never ever be complete. For this reason it tries to use a
// regex and may kill a few other valid tokens, but it's the only way to
// protect users as much as possible from admins with illegal ideas.
//
// User tokens are not prefixed with colon to catch 'current-user' and 'user'.
//
// TODO: If someone have better ideas, share them, please!
$token_blacklist = array(
':author]',
':author:edit-url]',
':author:url]',
':author:path]',
':current-user]',
':current-user:original]',
':fid]',
':mail]',
':name]',
':uid]',
':one-time-login-url]',
':owner]',
':owner:cancel-url]',
':owner:edit-url]',
':owner:url]',
':owner:path]',
'user:cancel-url]',
'user:edit-url]',
'user:url]',
'user:path]',
'user:picture]',
// addressfield_tokens.module
':first-name]',
':last-name]',
':name-line]',
':mc-address]',
':thoroughfare]',
':premise]',
// realname.module
':name-raw]',
// token.module
':ip-address]',
);
return preg_match('/' . implode('|', array_map('preg_quote', $token_blacklist)) . '/i', $token_string);
}
/**
* #element_validate callback for create only fields.
*
* @param $element
* An associative array containing the properties and children of the
* generic form element.
* @param $form_state
* The $form_state array for the form this element belongs to.
*
* @see form_process_pattern()
*/
function googleanalytics_validate_create_field_values(&$element, &$form_state) {
$values = _googleanalytics_extract_create_field_values($element['#value']);
if (!is_array($values)) {
form_error($element, t('The %element-title field contains invalid input.', array('%element-title' => $element['#title'])));
}
else {
// Check that name and value are valid for the field type.
foreach ($values as $name => $value) {
if ($error = _googleanalytics_validate_create_field_name($name)) {
form_error($element, $error);
break;
}
if ($error = _googleanalytics_validate_create_field_value($value)) {
form_error($element, $error);
break;
}
}
return $element;
}
}
/**
* Extracts the values array from the element.
*
* @param string $string
* The raw string to extract values from.
*
* @return array|null
* The array of extracted key/value pairs, or NULL if the string is invalid.
*/
function _googleanalytics_extract_create_field_values($string) {
$values = array();
$list = explode("\n", $string);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
foreach ($list as $text) {
// Check for an explicit key.
$matches = array();
if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
// Trim key and value to avoid unwanted spaces issues.
$name = trim($matches[1]);
$value = trim($matches[2]);
}
else {
return;
}
$values[$name] = $value;
}
return _googleanalytics_convert_form_value_data_types($values);
}
/**
* Checks whether a field name is valid.
*
* @param string $name
* The option value entered by the user.
*
* @return string
* The error message if the specified value is invalid, NULL otherwise.
*/
function _googleanalytics_validate_create_field_name($name) {
// List of supported field names:
// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#create
$create_only_fields = array(
'clientId',
'sampleRate',
'siteSpeedSampleRate',
'useAmpClientId',
'alwaysSendReferrer',
'allowAnchor',
'cookieName',
'cookieDomain',
'cookieExpires',
'legacyCookieDomain',
);
if ($name == 'name') {
return t('Create only field name %name is a disallowed field name. Changing the <em>Tracker Name</em> is currently not supported.', array('%name' => $name));
}
if ($name == 'allowLinker') {
return t('Create only field name %name is a disallowed field name. Please select <em>Multiple top-level domains</em> under <em>What are you tracking</em> to enable cross domain tracking.', array('%name' => $name));
}
if ($name == 'userId') {
return t('Create only field name %name is a disallowed field name. Please enable <em>Track User ID</em> under <em>Tracking scope > Users</em>.', array('%name' => $name));
}
if (!in_array($name, $create_only_fields)) {
return t('Create only field name %name is an unknown field name. Field names are case sensitive. Please see <a href="@url">create only fields</a> documentation for supported field names.', array('%name' => $name, '@url' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#create'));
}
}
/**
* Checks whether a field value is valid.
*
* @param string $value
* The option value entered by the user.
*
* @return string
* The error message if the specified value is invalid, NULL otherwise.
*/
function _googleanalytics_validate_create_field_value($value) {
if (!is_bool($value) && !backdrop_strlen($value)) {
return t('A create only field requires a value.');
}
if (backdrop_strlen($value) > 255) {
return t('The value of a create only field must be a string at most 255 characters long.');
}
}
/**
* Generates a string representation of an array.
*
* This string format is suitable for edition in a textarea.
*
* @param array $values
* An array of values, where array keys are values and array values are
* labels.
*
* @return string
* The string representation of the $values array:
* - Values are separated by a carriage return.
* - Each value is in the format "name|value" or "value".
*/
function _googleanalytics_get_name_value_string($values = NULL) {
$lines = array();
if ($values != NULL) {
foreach ($values as $name => $value) {
// Convert data types.
// @todo: #2251377: Json utility class serializes boolean values to incorrect data type
if (is_bool($value)) {
$value = ($value) ? 'true' : 'false';
}
$lines[] = "$name|$value";
}
}
return implode("\n", $lines);
}
/**
* Prepare form data types for Json conversion.
*
* @param array $values
* Array of name/value pairs.
*
* @return array
* Array of name/value pairs with casted data types.
*/
function _googleanalytics_convert_form_value_data_types($values) {
foreach ($values as $name => $value) {
// Convert data types.
// @todo: #2251377: Json utility class serializes boolean values to incorrect data type
$match = backdrop_strtolower($value);
if ($match == 'true') {
$value = TRUE;
}
elseif ($match == 'false') {
$value = FALSE;
}
// Convert other known fields.
// @todo: #2251343: Json utility class serializes numeric values to incorrect data type
switch ($name) {
case 'sampleRate':
// Float
settype($value, 'float');
break;
case 'siteSpeedSampleRate':
case 'cookieExpires':
// Integer
settype($value, 'integer');
break;
}
$values[$name] = $value;
}
return $values;
}