Google Analytics Dashboard for WP (GADWP) - Version 7.2.0

Version Description

Download this release

Release Info

Developer chriscct7
Plugin Icon 128x128 Google Analytics Dashboard for WP (GADWP)
Version 7.2.0
Comparing to
See all releases

Code changes from version 7.2.1 to 7.2.0

Files changed (38) hide show
  1. assets/css/admin-widget-settings.css +218 -218
  2. assets/images/plugin-pushengage.svg +19 -19
  3. assets/js/admin-widget-settings.js +68 -68
  4. assets/js/frontend-gtag.js +829 -829
  5. assets/js/popular-posts.js +33 -33
  6. gadwp.php +2 -2
  7. includes/admin/em-admin.php +25 -25
  8. includes/admin/notification-event-runner.php +189 -189
  9. includes/admin/notification-event.php +237 -237
  10. includes/admin/notifications/notification-audience.php +101 -101
  11. includes/admin/notifications/notification-bounce-rate.php +54 -54
  12. includes/admin/notifications/notification-events.php +17 -17
  13. includes/admin/notifications/notification-headline-analyzer.php +41 -41
  14. includes/admin/notifications/notification-mobile-device.php +53 -53
  15. includes/admin/notifications/notification-returning-visitors.php +53 -53
  16. includes/admin/notifications/notification-to-add-more-file-extensions.php +48 -48
  17. includes/admin/notifications/notification-to-setup-affiliate-links.php +54 -54
  18. includes/admin/notifications/notification-traffic-dropping.php +52 -52
  19. includes/admin/notifications/notification-upgrade-for-email-summaries.php +41 -41
  20. includes/admin/notifications/notification-upgrade-for-form-conversion.php +41 -41
  21. includes/admin/notifications/notification-upgrade-for-google-optimize.php +41 -41
  22. includes/admin/notifications/notification-upgrade-for-search-console.php +41 -41
  23. includes/admin/notifications/notification-upgrade-to-pro.php +42 -42
  24. includes/admin/notifications/notification-visitors.php +50 -50
  25. includes/admin/sharedcount.php +637 -637
  26. includes/admin/uninstall.php +26 -26
  27. includes/frontend/events/class-gtag-events.php +117 -117
  28. includes/frontend/tracking/class-tracking-gtag.php +486 -486
  29. includes/popular-posts/class-popular-posts-themes.php +850 -850
  30. includes/popular-posts/class-popular-posts.php +742 -742
  31. lite/includes/gutenberg/blocks/blocks.php +162 -162
  32. lite/includes/gutenberg/frontend.php +3 -3
  33. lite/includes/popular-posts/class-popular-posts-ajax.php +259 -259
  34. lite/includes/popular-posts/class-popular-posts-cache.php +131 -131
  35. lite/includes/popular-posts/class-popular-posts-inline.php +270 -270
  36. lite/includes/popular-posts/class-popular-posts-widget-sidebar.php +425 -425
  37. lite/includes/popular-posts/class-popular-posts-widget.php +262 -262
  38. readme.txt +2 -2
assets/css/admin-widget-settings.css CHANGED
@@ -1,218 +1,218 @@
1
- .exactmetrics-label-block,
2
- .exactmetrics-radio-icons {
3
- display: block;
4
- }
5
-
6
- .exactmetrics-radio-icons {
7
- margin: 4px 0;
8
- }
9
-
10
- .exactmetrics-radio-icons * {
11
- box-sizing: border-box;
12
- }
13
-
14
- .exactmetrics-radio-icons > label {
15
- display: inline-block;
16
- }
17
-
18
- .exactmetrics-radio-icons input[type="radio"] {
19
- visibility: hidden;
20
- display: none;
21
- }
22
-
23
- .exactmetrics-wide-column {
24
- display: -webkit-box;
25
- display: -ms-flexbox;
26
- display: flex;
27
- border: 1px solid #b7c9d9;
28
- border-radius: 5px;
29
- width: 40px;
30
- height: 40px;
31
- padding: 10px 5px;
32
- -ms-flex-flow: wrap;
33
- flex-flow: wrap;
34
- -webkit-box-pack: justify;
35
- -ms-flex-pack: justify;
36
- justify-content: space-between;
37
- margin-right: 4px;
38
- }
39
-
40
- .exactmetrics-wide-column > span {
41
- background: #99a1b2;
42
- display: inline-block;
43
- width: 8px;
44
- height: 8px;
45
- border-radius: 1px;
46
- margin-bottom: 2px;
47
- }
48
-
49
- .exactmetrics-wide-column.exactmetrics-wide-column-two > span {
50
- width: 12px;
51
- }
52
-
53
- .exactmetrics-radio-icons input[type="radio"]:checked + .exactmetrics-wide-column {
54
- border: 2px solid #338eef;
55
- background: #ecf4fe;
56
- padding: 9px 4px;
57
- }
58
-
59
- .exactmetrics-radio-icons input[type="radio"]:checked + .exactmetrics-wide-column > span {
60
- background: #338eef;
61
- }
62
-
63
- .exactmetrics-wide-column.exactmetrics-wide-column-one > span {
64
- width: 100%;
65
- }
66
-
67
- .exactmetrics-field-description {
68
- font-family: Helvetica Neue, serif;
69
- font-style: italic;
70
- font-weight: normal;
71
- font-size: 13px;
72
- line-height: 1.5%;
73
- color: #545C66;
74
- }
75
-
76
- /** SELECT2 Styles **/
77
- .select300-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select300-container .select300-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select300-container .select300-selection--single .select300-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select300-container .select300-selection--single .select300-selection__clear{position:relative}.select300-container[dir="rtl"] .select300-selection--single .select300-selection__rendered{padding-right:8px;padding-left:20px}.select300-container .select300-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select300-container .select300-selection--multiple .select300-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select300-container .select300-search--inline{float:left}.select300-container .select300-search--inline .select300-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select300-container .select300-search--inline .select300-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select300-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select300-results{display:block}.select300-results__options{list-style:none;margin:0;padding:0}.select300-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select300-results__option[aria-selected]{cursor:pointer}.select300-container--open .select300-dropdown{left:0}.select300-container--open .select300-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select300-container--open .select300-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select300-search--dropdown{display:block;padding:4px}.select300-search--dropdown .select300-search__field{padding:4px;width:100%;box-sizing:border-box}.select300-search--dropdown .select300-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select300-search--dropdown.select300-search--hide{display:none}.select300-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select300-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select300-container--default .select300-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select300-container--default .select300-selection--single .select300-selection__rendered{color:#444;line-height:28px}.select300-container--default .select300-selection--single .select300-selection__clear{cursor:pointer;float:right;font-weight:bold}.select300-container--default .select300-selection--single .select300-selection__placeholder{color:#999}.select300-container--default .select300-selection--single .select300-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select300-container--default .select300-selection--single .select300-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select300-container--default[dir="rtl"] .select300-selection--single .select300-selection__clear{float:left}.select300-container--default[dir="rtl"] .select300-selection--single .select300-selection__arrow{left:1px;right:auto}.select300-container--default.select300-container--disabled .select300-selection--single{background-color:#eee;cursor:default}.select300-container--default.select300-container--disabled .select300-selection--single .select300-selection__clear{display:none}.select300-container--default.select300-container--open .select300-selection--single .select300-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select300-container--default .select300-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select300-container--default .select300-selection--multiple .select300-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select300-container--default .select300-selection--multiple .select300-selection__rendered li{list-style:none}.select300-container--default .select300-selection--multiple .select300-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;padding:1px}.select300-container--default .select300-selection--multiple .select300-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select300-container--default .select300-selection--multiple .select300-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select300-container--default .select300-selection--multiple .select300-selection__choice__remove:hover{color:#333}.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-selection__choice,.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-search--inline{float:right}.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-selection__choice{margin-left:5px;margin-right:auto}.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-selection__choice__remove{margin-left:2px;margin-right:auto}.select300-container--default.select300-container--focus .select300-selection--multiple{border:solid black 1px;outline:0}.select300-container--default.select300-container--disabled .select300-selection--multiple{background-color:#eee;cursor:default}.select300-container--default.select300-container--disabled .select300-selection__choice__remove{display:none}.select300-container--default.select300-container--open.select300-container--above .select300-selection--single,.select300-container--default.select300-container--open.select300-container--above .select300-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select300-container--default.select300-container--open.select300-container--below .select300-selection--single,.select300-container--default.select300-container--open.select300-container--below .select300-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select300-container--default .select300-search--dropdown .select300-search__field{border:1px solid #aaa}.select300-container--default .select300-search--inline .select300-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select300-container--default .select300-results>.select300-results__options{max-height:200px;overflow-y:auto}.select300-container--default .select300-results__option[role=group]{padding:0}.select300-container--default .select300-results__option[aria-disabled=true]{color:#999}.select300-container--default .select300-results__option[aria-selected=true]{background-color:#ddd}.select300-container--default .select300-results__option .select300-results__option{padding-left:1em}.select300-container--default .select300-results__option .select300-results__option .select300-results__group{padding-left:0}.select300-container--default .select300-results__option .select300-results__option .select300-results__option{margin-left:-1em;padding-left:2em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-2em;padding-left:3em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-3em;padding-left:4em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-4em;padding-left:5em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-5em;padding-left:6em}.select300-container--default .select300-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select300-container--default .select300-results__group{cursor:default;display:block;padding:6px}.select300-container--classic .select300-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select300-container--classic .select300-selection--single:focus{border:1px solid #5897fb}.select300-container--classic .select300-selection--single .select300-selection__rendered{color:#444;line-height:28px}.select300-container--classic .select300-selection--single .select300-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select300-container--classic .select300-selection--single .select300-selection__placeholder{color:#999}.select300-container--classic .select300-selection--single .select300-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select300-container--classic .select300-selection--single .select300-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select300-container--classic[dir="rtl"] .select300-selection--single .select300-selection__clear{float:left}.select300-container--classic[dir="rtl"] .select300-selection--single .select300-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select300-container--classic.select300-container--open .select300-selection--single{border:1px solid #5897fb}.select300-container--classic.select300-container--open .select300-selection--single .select300-selection__arrow{background:transparent;border:none}.select300-container--classic.select300-container--open .select300-selection--single .select300-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select300-container--classic.select300-container--open.select300-container--above .select300-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select300-container--classic.select300-container--open.select300-container--below .select300-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select300-container--classic .select300-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select300-container--classic .select300-selection--multiple:focus{border:1px solid #5897fb}.select300-container--classic .select300-selection--multiple .select300-selection__rendered{list-style:none;margin:0;padding:0 5px}.select300-container--classic .select300-selection--multiple .select300-selection__clear{display:none}.select300-container--classic .select300-selection--multiple .select300-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select300-container--classic .select300-selection--multiple .select300-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select300-container--classic .select300-selection--multiple .select300-selection__choice__remove:hover{color:#555}.select300-container--classic[dir="rtl"] .select300-selection--multiple .select300-selection__choice{float:right;margin-left:5px;margin-right:auto}.select300-container--classic[dir="rtl"] .select300-selection--multiple .select300-selection__choice__remove{margin-left:2px;margin-right:auto}.select300-container--classic.select300-container--open .select300-selection--multiple{border:1px solid #5897fb}.select300-container--classic.select300-container--open.select300-container--above .select300-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select300-container--classic.select300-container--open.select300-container--below .select300-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select300-container--classic .select300-search--dropdown .select300-search__field{border:1px solid #aaa;outline:0}.select300-container--classic .select300-search--inline .select300-search__field{outline:0;box-shadow:none}.select300-container--classic .select300-dropdown{background-color:#fff;border:1px solid transparent}.select300-container--classic .select300-dropdown--above{border-bottom:none}.select300-container--classic .select300-dropdown--below{border-top:none}.select300-container--classic .select300-results>.select300-results__options{max-height:200px;overflow-y:auto}.select300-container--classic .select300-results__option[role=group]{padding:0}.select300-container--classic .select300-results__option[aria-disabled=true]{color:grey}.select300-container--classic .select300-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select300-container--classic .select300-results__group{cursor:default;display:block;padding:6px}.select300-container--classic.select300-container--open .select300-dropdown{border-color:#5897fb}
78
-
79
- .select300-container {
80
- display: block;
81
- width: 100% !important;
82
- }
83
-
84
- .select300-container--default .select300-selection--multiple .select300-selection__choice {
85
- border-radius: 20px;
86
- background: #E2E4E7;
87
- border: none;
88
- color: #32373C;
89
- padding-left: 8px;
90
- font-family: Helvetica Neue, serif;
91
- font-style: normal;
92
- font-weight: normal;
93
- font-size: 13px;
94
- line-height: 24px;
95
- }
96
-
97
- .select300-container--default .select300-selection--multiple .select300-selection__choice__remove {
98
- line-height: 1;
99
- width: 16px;
100
- height: 16px;
101
- float: right;
102
- display: block;
103
- border-radius: 50%;
104
- background-color: #555D66;
105
- color: #E2E4E7;
106
- text-align: center;
107
- margin: 4px 0px 4px 8px;
108
- font-size: 14px;
109
- box-sizing: border-box;
110
- padding-top: 0;
111
- padding-left: 0;
112
- }
113
-
114
- .select300-container--default .select300-selection--multiple .select300-selection__choice__remove:hover {
115
- background-color: #333;
116
- color: #E2E4E7;
117
- }
118
-
119
- .exactmetrics-widget-theme-preview {
120
- background: #F0F2F4;
121
- border-radius: 5px;
122
- padding: 8px 8px 8px 18px;
123
- display: flex;
124
- font-family: Helvetica Neue, serif;
125
- font-style: normal;
126
- font-weight: normal;
127
- font-size: 14px;
128
- line-height: 1;
129
- color: #32373C;
130
- align-items: center;
131
- justify-content: space-between;
132
- }
133
-
134
- .exactmetrics-widget-theme-preview-icon,
135
- .exactmetrics-products-theme-preview-icon {
136
- width: 74px;
137
- height: 46px;
138
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAxAAAAEqCAMAAACyWUYtAAABBVBMVEUAAADw6v6AUPeEU/bw6f7w6v7v5P/w6v/v6vzy6/9lKPWEU/jw6/7v6fzw6/5kKPVlJ/ZlKPZoKPOEU/fw6v7Rv/xkJ/VlKPbx6/9lKPaEU/eEU/fv6P3w6/7w6v7Rv/xkKPVkJvTPv//Pv/dwIPNgIO/v6f2EU/jw6v7Rv/xkJ/RmKPbv5//Sv/zQv/zw6v5lKPWEU/fRv/x5Qvbe0f1uNPbt5v50PfbTwvzTwfyATffs5P3ay/zi1/3NufuIWPdoLPWMXvjWxfyRZPjczv3YyPzNufy8ovt2P/bErfvk2v3g0/1xOfZsMvXo3v23mvt4Q/bKtfymgvmfePmujvp7RvaCUPf1uE5qAAAAL3RSTlMA3yDvr4AQz2Bf369vUK/vr2Agv+/vz7+PgO6+kO/ur29QICAQEI/vv76QjyC/v+TVgYYAAAyJSURBVHja7N3PTttAEMfxweZPi6FA0hJIVFVqizhVM1uFSyWDzAGSSyslff9nqZpDpCRu5djr9azn93mDkfYLsrye0Mrx2eEBd+7k8C158vV2JGpcXxFE5fwjK3HgJ4nBqahyOiCIxw0rckQeKOtB5PSOIBZHrMoNNXYl6twSROINK3NOTV2LOiOCSCh4mt70/pgaEoUI4qDuHwTzGYKAzrxjdQ4RBHTmhNU5oIZYIYI4/nCxQgjCBlGol4eHFSJAEAgCQSAIBYeHFSJAEAgCQSAIBYeHFSJAEAgCQSAIBYeHFSKIAyuEmaAqBGF0JiiHIIzOBOUQhNGZoByCMDoTlEMQRmeCcgjC6EwQCG95fJLAnuYIovquKdqWDFMXQpaQCbxpLh2YI4jKu6Z2ekhdIGMbRfCmmXTgAUFU3jVFWzIXzIQs4A3P0okpgqi6a4q2uHDGZAFveJROvLQcRL4sXEiLnLf52TXVaRCOLLAQRF64wIrcdxCygiB2IYi9g1i44JYIIlIWgnDhFQgiUgiiHQiigsHoQvZzOWjzDUyWIIg1BBE8iMGF7O1i0OYbmHGCINYQROggLqWGUatvYCYIYg1BhA5C6rhodfIxglhDEFEEIe1OjiDWEASC2AliKp14RhB1g0jdfyCIpkHwg3RgxgiibhBDF0xmMYheXv92lcQZRLjr32liMQieh/9A6IURRO0gKMlcCOkkIZNBaOA7iMJVEWkQEZNaNp+eEESNIJYuuAWCaDuIoWtBZiKIPl//jpnU0ubTU5qYCILzhQupWOaMINoKor2np3SS9PNzS1YIQXgLohz2MtmaCUEgCMyEIBAEgkAQqoIIsLnvxyuCqB9EiM19WUKlDAYxlxB+IwjdVzfG5UUYDGImQfxEEJ6CyFwrJlTGXhDPEsYrgtD9PcSYSpkL4lF8URWEz819i9xCEI5KqQqiydNTllgOwu/VjSJHEBqCaPb0dJ8YDmLhvFoiCA1BZK6RoeEgnF8FgtAQhGvmHkF4gyB6EIRDEAgCQSAIBIEgEASCQBAIAkEgCARhJoiR/IUgAgcxlTDmCGJPt7Lyr819BoNIAwQRbHPfFEHs6e5UVgJt7stol7IgGk6eGb7+3Ycgwv5wexrB9e8kbWPCLjb3zX4xgqjh6lrCbO5LJ1F8INRg8nSYWLoqzVuKfgTRO1IDvqnWt7lvgSAQRHUKg+jD9e8+QhA92NxXLHNGEAhiDwqDUIAAQSAIBIEgFBweVogAQSAIBIEgFBweVojAdxBfvn347tenz/08PKwQwR/27Ca1cSAIwGgJLNkG2wnGJllkMCSbBMIU6P6Hm8X8wGxCEkuoJL93g4L6aKl74CBezv3gzs+LXJ4sKBg4iEs/grdFLk8WFAwcRD+G8yKXJwsK5hBEv8jlyYICQQhCEB+5uyqIcz+GaLKctSBuw8N3eriPP372I7jEIctZCeI2HL9xRNwdR7523WU5W0HciOP9V3N4OMY/L5ehc3h7juhOWUzj9Z0J7bOYbVzrNctZB3PxlKW0cbU2y9kGs1Fqf57iel25q7MmmJFNmQU67WMI72UG+q15D2Zlc1jn5JrDrhtsoALz/PXadgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwA3rdqsmJ7debQOmt/+RRTSSYHKPWUgbMKk2S3kMmNAmi9kHTKfA3/T/Tl3AZy3+gMjcBUzlkOWsAj5v4V9MmU3AVLKg4Bd7d9DaNhCEYdi3QmhpcgnkUhJoTznMyLUiRzFY4CY4YAVd7P//UypoVDzbdFf2yuvR7vechcAoL7KtmRh6QhAAexAEwB4EAbAHQQDsQRAAexAEwCmRabbMOaTVDEGAHmSY5RxYPkMQoAYZVhzcEkGAGmTg8HIEAWooCIIRBHbf1SABQWiG3fcPIIhkYff9IwgiVdh9/xCCSJS61cZId99JQBBqKfg0LX2Lc/edBAShlbobxFC7799vLy/47K4vrxDEqMS6+373lZW4uEIQI/KJ1Bli9/2eFblBEONBCk283bAq9whiNEihia8rVuYOQYwFKTTxpeDTtPT1B4IYCVIouhsE8y2CGAlSaOLpktW5jDeIyCZDSaGJp2tW5yLaIGKbDCWFJp5YoViDiG4ylCT77vtqRk4IIqUg4psMJcG1+567i0AQCQWhbvDnS+jd9yW5IIiEglDwadr8VaTAQ5g5uSCIdIJQd4Mg+hw4CCYXBJFOEDFOhpKAIDoIItHJUBIQRAdBJPqdPQkIooMgEEQLQXQQBIJoIYgOgkAQrXSCqH+W2SDKpkYQiiCIY4KottmAmgpBqIEgjgiiKrNBlRWC0AJBHBFEkw1siyBaUfwqEhlytokjiDobXM1WiQQRw68ikWHJVityUx9Ekw2uYatEgojhV5FISGP8u8wGV7JVIkFwePnp3wb+P/N8GceCUHYCbIUgOqN7TRogiD4U/vGw2+hekwaBg9iuN5uiKREEgkAQbQ41/7EpEQSCSD6Igv+qSgSBIEihgEEUvKcqEQSCUChcEA0L1Q5BeD/VtRvda9IgXBAVSwWC8HuqG9wKQQwYxJYN1Q5BYHRjXxTzWeyQdTZs2iIIz6e6IeXLGSHyAYOo2VQgiMTfXpAQx3wWWyEIBKH7m7N8LEGUmwZBIIh/qL9ObGX9DNHYeqiYCwSBIEzqrxM7ZJ01m0prD60CQSAIg/rrxA5ZZ1exVDt6aBUIAkFI6q8TO4jJDaF09dAqjgoCT3V7QRBnDcK8RRS2HuxHsQOe6vaCIFrnC0L+odt7cBzHDlE+8CHT0+Jx6uPXkzyX+/iz/9eNUzjn+He54XfV2taDUBweRBJPdZ8ep57mT/Jc7uMxnzVEEDKJmrmqi521B1cR7JDE24vF1NviwHMtMLoRbKda9iAVCKJFhqm/+YHnmmM+K1wQsgepQBCWIDwcei58URAwCNmDVCAIBNGHguvEDsf2IBUIggzzAYOYI4jTBbF+eaOQXtdpBvE89bY48FwLEhBEnyDWbxTY2zrJIB68bxGPD+JcfY4XEESfIF4puJckg6CHhV8Ozw/iXH2OlxBEnyAovDc2RLmrSwrhNakMglhIZXRDAbymkQYRw64uKYT5rJEGweHlJCCIVOazWEAQ77APkep8FoL43c7drjQOhFEAHqF+4kcFEQQRhV1/+OedzkLrurRgqS22xRDo/V/LrhC6k2yWppmanLw5zx3M0IMyPacMBNK377X3sxgIBgIpELXXUWzGh1SNgagJA1EkEFOp3HtbA9FfjJ17m/ySerCf1djqht1WIwIxT95RnodSC/azipX73qVKH6NsHtryqxtDtzaXOrCfFfozNEEG/NUNT/qd/bkvJeAFQkM/qxGB0FjdmDjPQsrACwSAVgRCw1ZXMsbOM5Yyav/wCKB2BALA1z5RytYQz4SAgcgHd9GSwkAk4M7EQOSr9l+mN9ka4pkQMBD54C5aMhbOM5GtIZ4JQXWBePkR5IWB8KTf2X9qeXYFsPNADKLYhYhee2uvs5HbZLVsZyBk7taGUgLimQDsOhCD2AWavq7zMHIFxMt2BkKGz8nfh7mUgnim5vezbMbKBZv1EpErJGppIKQ/GTs3XvSlJMAzNb+fZTNcuGkv4YqJbYbKra4AYj+rkkC4LQPhbIbKra4AYj+roYHQsNUVQOxnbQ5E7ML1ElNXjM1QudUVQOxnbQ5E5IJFvcTMFbKyGSqniQKIdZQqnl1Hf59dp66AeMlAlAHw4ZEUnYGwg5ULMZr5X8xFbpM4WtoslVtdAcR+1r8sIJVb3T2Bc8h+VkMDoWGrey5wOuxnNTQQGra6pwLnmP2spgYCgAl0cCVg9gz7WQxEaSbUmYA5NuxnMRClmWC3AmXfGL6cMRClmXD7AuTWMBB5Tiyca5UX/ekI5u316swwELm6Fk5X5UUnkTg/lNrtnZ8eGAYi372Fc6nyojUSQCbQ9wsL5kTnRWskgEyoBwvmUudWVyMBZII9WSg3Sre6GsG8Efj9rHA3FsiT1q2uRhr7WZ8uYd5eLx7UbnU10tjPSiLRvba1O+nef9O71dVIYz8LkqTo2OqqpLCfBUlSdEwTddLXz4IkKQwEMKh+1qNRSlJ0bHW1wuln3Z0ZrSRFx1ZXL139LEiSomOrS8StLtEf3OoSebjVJTKGLUqiTwwEkYeBIPIwEEQeBoLIw0AQeRgIolzc6hLtXkfgdAzRdrjVJfoSB3cCRulWlxqCW10i36NA0brVpcbgVpeIW12i/zrqIGx1O5q3utQIvwFN0uiXuNQl8wAAAABJRU5ErkJggg==);
139
- background-repeat: no-repeat;
140
- background-position: 0 0;
141
- background-size: 392px;
142
- display: inline-block;
143
- }
144
-
145
- .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-beta {
146
- background-position-x: -212px;
147
- }
148
-
149
- .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-charlie {
150
- background-position-x: -106px;
151
- }
152
-
153
- .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-delta {
154
- height: 43px;
155
- background-position: -318px 0;
156
- }
157
-
158
- .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-echo {
159
- height: 76px;
160
- background-position: 0 -73px;
161
- }
162
-
163
- .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-foxtrot {
164
- background-position: -106px -90px;
165
- height: 41px;
166
- }
167
-
168
- .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-golf {
169
- background-position: -212px -83px;
170
- height: 56px;
171
- }
172
-
173
- .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-hotel {
174
- height: 50px;
175
- background-position: -318px -86px;
176
- }
177
-
178
- .exactmetrics-products-theme-preview-icon {
179
- background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlAAAACcCAMAAACHiWWRAAAA3lBMVEUAAABlKPX///9oKPNlJ/ZkKPVkKfRlKPZlKPVlKPbRv/zw6v7Pv/vw6/7w6v7w6v7w6/7v6vzy6//w5f/w6v5lJ/Xw6f5lKPXv6fxlH/Pw6f7w6v7Rv/zRv/xkKPVmJ/VlJvZjJvLw6/7w6v/w6v9jJ/NmKfbv6PxlKPXw6v7Rv/yEU/f///95QvZmKfXs5f7Twvx/TPd0PfZuNPbZyv1pLfXbzf11PvZyOvbf0v3e0P2RZPiIWPfNuvzKtftsMvX18v7+/v+NYfh7Rva2mfq5nfuCUPe+pPuvkPqvkPnAI7BMAAAAKHRSTlMA/gEgr+/uv99g798gr5CAb2BfEO/PsIBQEN/Pv69wj1BQ78/OkI5P5JBZSwAAB1NJREFUeNrs3EtqAkEYReFqYhJF8o4vBPMYhFuIgjhTwUH2v6eM09GyUOmckvut4MJ/aHVgByvYVVso7atgJYP1JLVHwcrVEc4gWLl6wrkNVi7xVMHKJR4HVTLxOKiSicdBlUw8Dqpk4nFQJROPgyqZeBxUycQDDurlsd+N/27cfw5Y4uEGNfyMEF1sUuLBBnUfQR4Ck3ioQT1ElPuAJB5oUE8RZhiIxAMNCvBt/LfXlwAkHmZQuAdUjI8BSDzMoO4iTj8AiYcZ1DjidAOQeJhBRaAAJB4H5aAcVA4HlcVB5XJQWRxULgeVxUHlclBZHFQDEkGNJi3Av797rY6DOi2o1WapJq1XO4N6vxFEu+OgTghqtVTDlqsdQb2Joxo4qOODWqtxm79BDYTy5aCODkrNW8Ya3KtOqg8HVVBQijW8dzFdj8TjoPaJNbQHlFRNxOOg9ok1LeEAJzmovWIN8OVeuA9hOaj9CBsOqMTjoHZzUEeqiD89EcckbEhyULkYxyRsSHJQuRjHJGxIclC5GMckbEhyULkYxyRsSHJQuRjHJGxIclC5GMckbEhyULkYxyRsSKIGRUQ4JmFDkoO6/KC2s8X0LBazrQ5xUJce1Px7ekazudIc1IUHNV9Mz2oxV5KDuvCgfti5tyVFYSgKw1YB8iqrxViFaAXPjrb2zPu/0DCtJbZj04GEmMD67+HquwnZG5EY7ojKCKrboHaJ8XaoiqC6DUokxhOoiqC6DSpNjJeiKoLqNqikhVAVQREUQalFUPgP1FHmeS5SgnIi70Edd7iUpwTlQL6DmuLWPCWo1+c5qCnKClEE9fL8BiXwpfmZoGrWT1D5+3ak1unQK1BBPES9wmBwS/1p9bd7ASrfjpTbHHoEKmgAIrqJUni6wdt9APV7VKOPHoEK0aB4cC1EC8U+gBrVadMjUGhS1OqPjaPOgRoR1A+1CcqNeW5boNJcEBRBAVszoNI5IAmKoPBex9OpyhMgCYqgtD8blJ4ASVC9B4X8jyqnj588AZKgeg/qoZLBeY6y0kqVJ0AS1GcRzOc7qHsoqp4AWR/U5M3BNEHFaKHQc1BJmuPaRNUTIAmqKIhgvKEXVy+PPWjJd8BkNz0rewIkQRUFIcw2jP24HH4sqUjJEyAJ6lI/x1ce0vcESIL6jKCKDHgCJEH9i6CKTHgCJEEV9ROUmO3HOm0EniQIajDoJSixH2u2fy6KoAa9BLUZazfDs44E1TqoDJZSBzUZ67fHs6YE1TqoNay3sgDqF0HdMrL1EgaKoBYZLJctCMo6KP2tlyhQAlWIWsFm2XrxRlAvARVCq/gbUA5GUFZAQa+IoAjKJCgQlFFQy3UGm62WBGUK1N5BUEv7h5clQZkBhZk+qA0Ao6BWsN6aoBSzc/WiB8qBG4WMoAyBgtjocZoJAN6DAkEp1nx8ZQrlCOqSqa0XRVBOnF5QRlB3ubX1EqqAcuP0glsEdZ9TWy/DQAWUG6cXXCOorzm09TKMA3fnoTKCUsvL8RW8IB9BuTA3RlAdAuXC3BhBGQeVCq3SpqDcmBsjqKc1+E/56ZCYqTEoJ+bG/rJzRy0Nw0AAxzOtYy19HWPQTn0Q7g5mMgjBFzfQqd//GwmzL7sVasPVXW1+r4V7GH/KGpKkoFrFXDj2+nb1oBRIQbWKuqf8KwU1kqA0fL0A03obuawdpKAmc+oFqIVwUFuYYFBlVZCsuhzDqZe/COoA0wuqLEhcUeI5FV8vwOzpkvgLanpB1TSAChUC5kgXPmT/QT0DwOSCoiEUqFD3ssH+U7Kn91NPKSgRqBAw/J7y/fEFDtud0Ntpe4BGr6Csd9RfsNhIQQ1Hw1oY9AzKOoriBIuK3HNpGgXFSUFF6AwqUCSPYuL2XGamUdEAalQI9LkIimI5FBO153J+O/CygUKgj1xQhGJ677lkOy3LWjqnqhzlqRdZYw5KIeSsdyQpWD6bPx9lUI56m2ZQ1pEwZ/ls/nyMQXmKFPDHVIIKJM53zPbILECdJZ5RsmygETIkz3XMdshkoE6GnA3Un/MWGymoeF2zkdmANrMcFTIa4RkdQa3vQJnFDSpkNELGkbyu2ciYR9BllqegYoPyJC50zA7IGPMEqqxMCmrMywbGmBUocm9SUL+GnA2yOXnLZ/PnrT9UrmbtYP5gUlBtxnPhmDnJsyVc3SLbrE0K6l8EpUoKKgX13a7dpSAIRVEU3vga9qeE2GNBHJ98kOY/tAZgN66CtLutbwiHhaDbTYNKbZ75/2SWeSeCWhdU+uUld1wp9E4EtTao55A0RYZC70RQa4Ma0sbIUOidCGqDoIbIUOidpF3YuckPQeWqw85FfmZBjQT13jHsnOVnFtT0abvMUGxQTRtmehnis0G2Lsw4PqBmQSU3z3H68w+b0j6sHOSI6WWBQxjZyxJBLXHqw0TbyRNBLXOqr/F1u/rYyBRBgaDgi6BAUPBVGW6ed+FnVWyeEJsnbLF5QmLzhK2KzRMSmyd8GW2eD6EEbJ4o2AuunoM0UoU65AAAAABJRU5ErkJggg==');
180
- width: 74px;
181
- height: 39px;
182
- background-size: 188px;
183
- }
184
-
185
- .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-alpha {
186
- background-position: 0 0;
187
- }
188
-
189
- .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-beta {
190
- background-position: -114px 0;
191
- height: 65px;
192
- }
193
-
194
- .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-charlie {
195
- background-position: 0 -102px;
196
- height: 50px;
197
- }
198
-
199
- .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-delta {
200
- background-position: -114px -102px;
201
- height: 78px;
202
- }
203
-
204
- .exactmetrics-pro-pill {
205
- font-style: normal;
206
- font-weight: bold;
207
- font-size: 11px;
208
- background: #1EC185;
209
- border-radius: 5px;
210
- color: #fff;
211
- padding: 3px 8px;
212
- margin-left: 8px;
213
- }
214
-
215
- .select300-container--disabled {
216
- margin-top: 10px;
217
- opacity: 0.6;
218
- }
1
+ .exactmetrics-label-block,
2
+ .exactmetrics-radio-icons {
3
+ display: block;
4
+ }
5
+
6
+ .exactmetrics-radio-icons {
7
+ margin: 4px 0;
8
+ }
9
+
10
+ .exactmetrics-radio-icons * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ .exactmetrics-radio-icons > label {
15
+ display: inline-block;
16
+ }
17
+
18
+ .exactmetrics-radio-icons input[type="radio"] {
19
+ visibility: hidden;
20
+ display: none;
21
+ }
22
+
23
+ .exactmetrics-wide-column {
24
+ display: -webkit-box;
25
+ display: -ms-flexbox;
26
+ display: flex;
27
+ border: 1px solid #b7c9d9;
28
+ border-radius: 5px;
29
+ width: 40px;
30
+ height: 40px;
31
+ padding: 10px 5px;
32
+ -ms-flex-flow: wrap;
33
+ flex-flow: wrap;
34
+ -webkit-box-pack: justify;
35
+ -ms-flex-pack: justify;
36
+ justify-content: space-between;
37
+ margin-right: 4px;
38
+ }
39
+
40
+ .exactmetrics-wide-column > span {
41
+ background: #99a1b2;
42
+ display: inline-block;
43
+ width: 8px;
44
+ height: 8px;
45
+ border-radius: 1px;
46
+ margin-bottom: 2px;
47
+ }
48
+
49
+ .exactmetrics-wide-column.exactmetrics-wide-column-two > span {
50
+ width: 12px;
51
+ }
52
+
53
+ .exactmetrics-radio-icons input[type="radio"]:checked + .exactmetrics-wide-column {
54
+ border: 2px solid #338eef;
55
+ background: #ecf4fe;
56
+ padding: 9px 4px;
57
+ }
58
+
59
+ .exactmetrics-radio-icons input[type="radio"]:checked + .exactmetrics-wide-column > span {
60
+ background: #338eef;
61
+ }
62
+
63
+ .exactmetrics-wide-column.exactmetrics-wide-column-one > span {
64
+ width: 100%;
65
+ }
66
+
67
+ .exactmetrics-field-description {
68
+ font-family: Helvetica Neue, serif;
69
+ font-style: italic;
70
+ font-weight: normal;
71
+ font-size: 13px;
72
+ line-height: 1.5%;
73
+ color: #545C66;
74
+ }
75
+
76
+ /** SELECT2 Styles **/
77
+ .select300-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select300-container .select300-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select300-container .select300-selection--single .select300-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select300-container .select300-selection--single .select300-selection__clear{position:relative}.select300-container[dir="rtl"] .select300-selection--single .select300-selection__rendered{padding-right:8px;padding-left:20px}.select300-container .select300-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select300-container .select300-selection--multiple .select300-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select300-container .select300-search--inline{float:left}.select300-container .select300-search--inline .select300-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select300-container .select300-search--inline .select300-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select300-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select300-results{display:block}.select300-results__options{list-style:none;margin:0;padding:0}.select300-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select300-results__option[aria-selected]{cursor:pointer}.select300-container--open .select300-dropdown{left:0}.select300-container--open .select300-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select300-container--open .select300-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select300-search--dropdown{display:block;padding:4px}.select300-search--dropdown .select300-search__field{padding:4px;width:100%;box-sizing:border-box}.select300-search--dropdown .select300-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select300-search--dropdown.select300-search--hide{display:none}.select300-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select300-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select300-container--default .select300-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select300-container--default .select300-selection--single .select300-selection__rendered{color:#444;line-height:28px}.select300-container--default .select300-selection--single .select300-selection__clear{cursor:pointer;float:right;font-weight:bold}.select300-container--default .select300-selection--single .select300-selection__placeholder{color:#999}.select300-container--default .select300-selection--single .select300-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select300-container--default .select300-selection--single .select300-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select300-container--default[dir="rtl"] .select300-selection--single .select300-selection__clear{float:left}.select300-container--default[dir="rtl"] .select300-selection--single .select300-selection__arrow{left:1px;right:auto}.select300-container--default.select300-container--disabled .select300-selection--single{background-color:#eee;cursor:default}.select300-container--default.select300-container--disabled .select300-selection--single .select300-selection__clear{display:none}.select300-container--default.select300-container--open .select300-selection--single .select300-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select300-container--default .select300-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select300-container--default .select300-selection--multiple .select300-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select300-container--default .select300-selection--multiple .select300-selection__rendered li{list-style:none}.select300-container--default .select300-selection--multiple .select300-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;padding:1px}.select300-container--default .select300-selection--multiple .select300-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select300-container--default .select300-selection--multiple .select300-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select300-container--default .select300-selection--multiple .select300-selection__choice__remove:hover{color:#333}.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-selection__choice,.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-search--inline{float:right}.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-selection__choice{margin-left:5px;margin-right:auto}.select300-container--default[dir="rtl"] .select300-selection--multiple .select300-selection__choice__remove{margin-left:2px;margin-right:auto}.select300-container--default.select300-container--focus .select300-selection--multiple{border:solid black 1px;outline:0}.select300-container--default.select300-container--disabled .select300-selection--multiple{background-color:#eee;cursor:default}.select300-container--default.select300-container--disabled .select300-selection__choice__remove{display:none}.select300-container--default.select300-container--open.select300-container--above .select300-selection--single,.select300-container--default.select300-container--open.select300-container--above .select300-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select300-container--default.select300-container--open.select300-container--below .select300-selection--single,.select300-container--default.select300-container--open.select300-container--below .select300-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select300-container--default .select300-search--dropdown .select300-search__field{border:1px solid #aaa}.select300-container--default .select300-search--inline .select300-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select300-container--default .select300-results>.select300-results__options{max-height:200px;overflow-y:auto}.select300-container--default .select300-results__option[role=group]{padding:0}.select300-container--default .select300-results__option[aria-disabled=true]{color:#999}.select300-container--default .select300-results__option[aria-selected=true]{background-color:#ddd}.select300-container--default .select300-results__option .select300-results__option{padding-left:1em}.select300-container--default .select300-results__option .select300-results__option .select300-results__group{padding-left:0}.select300-container--default .select300-results__option .select300-results__option .select300-results__option{margin-left:-1em;padding-left:2em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-2em;padding-left:3em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-3em;padding-left:4em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-4em;padding-left:5em}.select300-container--default .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option .select300-results__option{margin-left:-5em;padding-left:6em}.select300-container--default .select300-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select300-container--default .select300-results__group{cursor:default;display:block;padding:6px}.select300-container--classic .select300-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select300-container--classic .select300-selection--single:focus{border:1px solid #5897fb}.select300-container--classic .select300-selection--single .select300-selection__rendered{color:#444;line-height:28px}.select300-container--classic .select300-selection--single .select300-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select300-container--classic .select300-selection--single .select300-selection__placeholder{color:#999}.select300-container--classic .select300-selection--single .select300-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select300-container--classic .select300-selection--single .select300-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select300-container--classic[dir="rtl"] .select300-selection--single .select300-selection__clear{float:left}.select300-container--classic[dir="rtl"] .select300-selection--single .select300-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select300-container--classic.select300-container--open .select300-selection--single{border:1px solid #5897fb}.select300-container--classic.select300-container--open .select300-selection--single .select300-selection__arrow{background:transparent;border:none}.select300-container--classic.select300-container--open .select300-selection--single .select300-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select300-container--classic.select300-container--open.select300-container--above .select300-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select300-container--classic.select300-container--open.select300-container--below .select300-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select300-container--classic .select300-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select300-container--classic .select300-selection--multiple:focus{border:1px solid #5897fb}.select300-container--classic .select300-selection--multiple .select300-selection__rendered{list-style:none;margin:0;padding:0 5px}.select300-container--classic .select300-selection--multiple .select300-selection__clear{display:none}.select300-container--classic .select300-selection--multiple .select300-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select300-container--classic .select300-selection--multiple .select300-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select300-container--classic .select300-selection--multiple .select300-selection__choice__remove:hover{color:#555}.select300-container--classic[dir="rtl"] .select300-selection--multiple .select300-selection__choice{float:right;margin-left:5px;margin-right:auto}.select300-container--classic[dir="rtl"] .select300-selection--multiple .select300-selection__choice__remove{margin-left:2px;margin-right:auto}.select300-container--classic.select300-container--open .select300-selection--multiple{border:1px solid #5897fb}.select300-container--classic.select300-container--open.select300-container--above .select300-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select300-container--classic.select300-container--open.select300-container--below .select300-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select300-container--classic .select300-search--dropdown .select300-search__field{border:1px solid #aaa;outline:0}.select300-container--classic .select300-search--inline .select300-search__field{outline:0;box-shadow:none}.select300-container--classic .select300-dropdown{background-color:#fff;border:1px solid transparent}.select300-container--classic .select300-dropdown--above{border-bottom:none}.select300-container--classic .select300-dropdown--below{border-top:none}.select300-container--classic .select300-results>.select300-results__options{max-height:200px;overflow-y:auto}.select300-container--classic .select300-results__option[role=group]{padding:0}.select300-container--classic .select300-results__option[aria-disabled=true]{color:grey}.select300-container--classic .select300-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select300-container--classic .select300-results__group{cursor:default;display:block;padding:6px}.select300-container--classic.select300-container--open .select300-dropdown{border-color:#5897fb}
78
+
79
+ .select300-container {
80
+ display: block;
81
+ width: 100% !important;
82
+ }
83
+
84
+ .select300-container--default .select300-selection--multiple .select300-selection__choice {
85
+ border-radius: 20px;
86
+ background: #E2E4E7;
87
+ border: none;
88
+ color: #32373C;
89
+ padding-left: 8px;
90
+ font-family: Helvetica Neue, serif;
91
+ font-style: normal;
92
+ font-weight: normal;
93
+ font-size: 13px;
94
+ line-height: 24px;
95
+ }
96
+
97
+ .select300-container--default .select300-selection--multiple .select300-selection__choice__remove {
98
+ line-height: 1;
99
+ width: 16px;
100
+ height: 16px;
101
+ float: right;
102
+ display: block;
103
+ border-radius: 50%;
104
+ background-color: #555D66;
105
+ color: #E2E4E7;
106
+ text-align: center;
107
+ margin: 4px 0px 4px 8px;
108
+ font-size: 14px;
109
+ box-sizing: border-box;
110
+ padding-top: 0;
111
+ padding-left: 0;
112
+ }
113
+
114
+ .select300-container--default .select300-selection--multiple .select300-selection__choice__remove:hover {
115
+ background-color: #333;
116
+ color: #E2E4E7;
117
+ }
118
+
119
+ .exactmetrics-widget-theme-preview {
120
+ background: #F0F2F4;
121
+ border-radius: 5px;
122
+ padding: 8px 8px 8px 18px;
123
+ display: flex;
124
+ font-family: Helvetica Neue, serif;
125
+ font-style: normal;
126
+ font-weight: normal;
127
+ font-size: 14px;
128
+ line-height: 1;
129
+ color: #32373C;
130
+ align-items: center;
131
+ justify-content: space-between;
132
+ }
133
+
134
+ .exactmetrics-widget-theme-preview-icon,
135
+ .exactmetrics-products-theme-preview-icon {
136
+ width: 74px;
137
+ height: 46px;
138
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAxAAAAEqCAMAAACyWUYtAAABBVBMVEUAAADw6v6AUPeEU/bw6f7w6v7v5P/w6v/v6vzy6/9lKPWEU/jw6/7v6fzw6/5kKPVlJ/ZlKPZoKPOEU/fw6v7Rv/xkJ/VlKPbx6/9lKPaEU/eEU/fv6P3w6/7w6v7Rv/xkKPVkJvTPv//Pv/dwIPNgIO/v6f2EU/jw6v7Rv/xkJ/RmKPbv5//Sv/zQv/zw6v5lKPWEU/fRv/x5Qvbe0f1uNPbt5v50PfbTwvzTwfyATffs5P3ay/zi1/3NufuIWPdoLPWMXvjWxfyRZPjczv3YyPzNufy8ovt2P/bErfvk2v3g0/1xOfZsMvXo3v23mvt4Q/bKtfymgvmfePmujvp7RvaCUPf1uE5qAAAAL3RSTlMA3yDvr4AQz2Bf369vUK/vr2Agv+/vz7+PgO6+kO/ur29QICAQEI/vv76QjyC/v+TVgYYAAAyJSURBVHja7N3PTttAEMfxweZPi6FA0hJIVFVqizhVM1uFSyWDzAGSSyslff9nqZpDpCRu5djr9azn93mDkfYLsrye0Mrx2eEBd+7k8C158vV2JGpcXxFE5fwjK3HgJ4nBqahyOiCIxw0rckQeKOtB5PSOIBZHrMoNNXYl6twSROINK3NOTV2LOiOCSCh4mt70/pgaEoUI4qDuHwTzGYKAzrxjdQ4RBHTmhNU5oIZYIYI4/nCxQgjCBlGol4eHFSJAEAgCQSAIBYeHFSJAEAgCQSAIBYeHFSJAEAgCQSAIBYeHFSKIAyuEmaAqBGF0JiiHIIzOBOUQhNGZoByCMDoTlEMQRmeCcgjC6EwQCG95fJLAnuYIovquKdqWDFMXQpaQCbxpLh2YI4jKu6Z2ekhdIGMbRfCmmXTgAUFU3jVFWzIXzIQs4A3P0okpgqi6a4q2uHDGZAFveJROvLQcRL4sXEiLnLf52TXVaRCOLLAQRF64wIrcdxCygiB2IYi9g1i44JYIIlIWgnDhFQgiUgiiHQiigsHoQvZzOWjzDUyWIIg1BBE8iMGF7O1i0OYbmHGCINYQROggLqWGUatvYCYIYg1BhA5C6rhodfIxglhDEFEEIe1OjiDWEASC2AliKp14RhB1g0jdfyCIpkHwg3RgxgiibhBDF0xmMYheXv92lcQZRLjr32liMQieh/9A6IURRO0gKMlcCOkkIZNBaOA7iMJVEWkQEZNaNp+eEESNIJYuuAWCaDuIoWtBZiKIPl//jpnU0ubTU5qYCILzhQupWOaMINoKor2np3SS9PNzS1YIQXgLohz2MtmaCUEgCMyEIBAEgkAQqoIIsLnvxyuCqB9EiM19WUKlDAYxlxB+IwjdVzfG5UUYDGImQfxEEJ6CyFwrJlTGXhDPEsYrgtD9PcSYSpkL4lF8URWEz819i9xCEI5KqQqiydNTllgOwu/VjSJHEBqCaPb0dJ8YDmLhvFoiCA1BZK6RoeEgnF8FgtAQhGvmHkF4gyB6EIRDEAgCQSAIBIEgEASCQBAIAkEgCARhJoiR/IUgAgcxlTDmCGJPt7Lyr819BoNIAwQRbHPfFEHs6e5UVgJt7stol7IgGk6eGb7+3Ycgwv5wexrB9e8kbWPCLjb3zX4xgqjh6lrCbO5LJ1F8INRg8nSYWLoqzVuKfgTRO1IDvqnWt7lvgSAQRHUKg+jD9e8+QhA92NxXLHNGEAhiDwqDUIAAQSAIBIEgFBweVogAQSAIBIEgFBweVojAdxBfvn347tenz/08PKwQwR/27Ca1cSAIwGgJLNkG2wnGJllkMCSbBMIU6P6Hm8X8wGxCEkuoJL93g4L6aKl74CBezv3gzs+LXJ4sKBg4iEs/grdFLk8WFAwcRD+G8yKXJwsK5hBEv8jlyYICQQhCEB+5uyqIcz+GaLKctSBuw8N3eriPP372I7jEIctZCeI2HL9xRNwdR7523WU5W0HciOP9V3N4OMY/L5ehc3h7juhOWUzj9Z0J7bOYbVzrNctZB3PxlKW0cbU2y9kGs1Fqf57iel25q7MmmJFNmQU67WMI72UG+q15D2Zlc1jn5JrDrhtsoALz/PXadgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwA3rdqsmJ7debQOmt/+RRTSSYHKPWUgbMKk2S3kMmNAmi9kHTKfA3/T/Tl3AZy3+gMjcBUzlkOWsAj5v4V9MmU3AVLKg4Bd7d9DaNhCEYdi3QmhpcgnkUhJoTznMyLUiRzFY4CY4YAVd7P//UypoVDzbdFf2yuvR7vechcAoL7KtmRh6QhAAexAEwB4EAbAHQQDsQRAAexAEwCmRabbMOaTVDEGAHmSY5RxYPkMQoAYZVhzcEkGAGmTg8HIEAWooCIIRBHbf1SABQWiG3fcPIIhkYff9IwgiVdh9/xCCSJS61cZId99JQBBqKfg0LX2Lc/edBAShlbobxFC7799vLy/47K4vrxDEqMS6+373lZW4uEIQI/KJ1Bli9/2eFblBEONBCk283bAq9whiNEihia8rVuYOQYwFKTTxpeDTtPT1B4IYCVIouhsE8y2CGAlSaOLpktW5jDeIyCZDSaGJp2tW5yLaIGKbDCWFJp5YoViDiG4ylCT77vtqRk4IIqUg4psMJcG1+567i0AQCQWhbvDnS+jd9yW5IIiEglDwadr8VaTAQ5g5uSCIdIJQd4Mg+hw4CCYXBJFOEDFOhpKAIDoIItHJUBIQRAdBJPqdPQkIooMgEEQLQXQQBIJoIYgOgkAQrXSCqH+W2SDKpkYQiiCIY4KottmAmgpBqIEgjgiiKrNBlRWC0AJBHBFEkw1siyBaUfwqEhlytokjiDobXM1WiQQRw68ikWHJVityUx9Ekw2uYatEgojhV5FISGP8u8wGV7JVIkFwePnp3wb+P/N8GceCUHYCbIUgOqN7TRogiD4U/vGw2+hekwaBg9iuN5uiKREEgkAQbQ41/7EpEQSCSD6Igv+qSgSBIEihgEEUvKcqEQSCUChcEA0L1Q5BeD/VtRvda9IgXBAVSwWC8HuqG9wKQQwYxJYN1Q5BYHRjXxTzWeyQdTZs2iIIz6e6IeXLGSHyAYOo2VQgiMTfXpAQx3wWWyEIBKH7m7N8LEGUmwZBIIh/qL9ObGX9DNHYeqiYCwSBIEzqrxM7ZJ01m0prD60CQSAIg/rrxA5ZZ1exVDt6aBUIAkFI6q8TO4jJDaF09dAqjgoCT3V7QRBnDcK8RRS2HuxHsQOe6vaCIFrnC0L+odt7cBzHDlE+8CHT0+Jx6uPXkzyX+/iz/9eNUzjn+He54XfV2taDUBweRBJPdZ8ep57mT/Jc7uMxnzVEEDKJmrmqi521B1cR7JDE24vF1NviwHMtMLoRbKda9iAVCKJFhqm/+YHnmmM+K1wQsgepQBCWIDwcei58URAwCNmDVCAIBNGHguvEDsf2IBUIggzzAYOYI4jTBbF+eaOQXtdpBvE89bY48FwLEhBEnyDWbxTY2zrJIB68bxGPD+JcfY4XEESfIF4puJckg6CHhV8Ozw/iXH2OlxBEnyAovDc2RLmrSwrhNakMglhIZXRDAbymkQYRw64uKYT5rJEGweHlJCCIVOazWEAQ77APkep8FoL43c7drjQOhFEAHqF+4kcFEQQRhV1/+OedzkLrurRgqS22xRDo/V/LrhC6k2yWppmanLw5zx3M0IMyPacMBNK377X3sxgIBgIpELXXUWzGh1SNgagJA1EkEFOp3HtbA9FfjJ17m/ySerCf1djqht1WIwIxT95RnodSC/azipX73qVKH6NsHtryqxtDtzaXOrCfFfozNEEG/NUNT/qd/bkvJeAFQkM/qxGB0FjdmDjPQsrACwSAVgRCw1ZXMsbOM5Yyav/wCKB2BALA1z5RytYQz4SAgcgHd9GSwkAk4M7EQOSr9l+mN9ka4pkQMBD54C5aMhbOM5GtIZ4JQXWBePkR5IWB8KTf2X9qeXYFsPNADKLYhYhee2uvs5HbZLVsZyBk7taGUgLimQDsOhCD2AWavq7zMHIFxMt2BkKGz8nfh7mUgnim5vezbMbKBZv1EpErJGppIKQ/GTs3XvSlJMAzNb+fZTNcuGkv4YqJbYbKra4AYj+rkkC4LQPhbIbKra4AYj+roYHQsNUVQOxnbQ5E7ML1ElNXjM1QudUVQOxnbQ5E5IJFvcTMFbKyGSqniQKIdZQqnl1Hf59dp66AeMlAlAHw4ZEUnYGwg5ULMZr5X8xFbpM4WtoslVtdAcR+1r8sIJVb3T2Bc8h+VkMDoWGrey5wOuxnNTQQGra6pwLnmP2spgYCgAl0cCVg9gz7WQxEaSbUmYA5NuxnMRClmWC3AmXfGL6cMRClmXD7AuTWMBB5Tiyca5UX/ekI5u316swwELm6Fk5X5UUnkTg/lNrtnZ8eGAYi372Fc6nyojUSQCbQ9wsL5kTnRWskgEyoBwvmUudWVyMBZII9WSg3Sre6GsG8Efj9rHA3FsiT1q2uRhr7WZ8uYd5eLx7UbnU10tjPSiLRvba1O+nef9O71dVIYz8LkqTo2OqqpLCfBUlSdEwTddLXz4IkKQwEMKh+1qNRSlJ0bHW1wuln3Z0ZrSRFx1ZXL139LEiSomOrS8StLtEf3OoSebjVJTKGLUqiTwwEkYeBIPIwEEQeBoLIw0AQeRgIolzc6hLtXkfgdAzRdrjVJfoSB3cCRulWlxqCW10i36NA0brVpcbgVpeIW12i/zrqIGx1O5q3utQIvwFN0uiXuNQl8wAAAABJRU5ErkJggg==);
139
+ background-repeat: no-repeat;
140
+ background-position: 0 0;
141
+ background-size: 392px;
142
+ display: inline-block;
143
+ }
144
+
145
+ .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-beta {
146
+ background-position-x: -212px;
147
+ }
148
+
149
+ .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-charlie {
150
+ background-position-x: -106px;
151
+ }
152
+
153
+ .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-delta {
154
+ height: 43px;
155
+ background-position: -318px 0;
156
+ }
157
+
158
+ .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-echo {
159
+ height: 76px;
160
+ background-position: 0 -73px;
161
+ }
162
+
163
+ .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-foxtrot {
164
+ background-position: -106px -90px;
165
+ height: 41px;
166
+ }
167
+
168
+ .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-golf {
169
+ background-position: -212px -83px;
170
+ height: 56px;
171
+ }
172
+
173
+ .exactmetrics-widget-theme-preview-icon.exactmetrics-widget-theme-preview-icon-hotel {
174
+ height: 50px;
175
+ background-position: -318px -86px;
176
+ }
177
+
178
+ .exactmetrics-products-theme-preview-icon {
179
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlAAAACcCAMAAACHiWWRAAAA3lBMVEUAAABlKPX///9oKPNlJ/ZkKPVkKfRlKPZlKPVlKPbRv/zw6v7Pv/vw6/7w6v7w6v7w6/7v6vzy6//w5f/w6v5lJ/Xw6f5lKPXv6fxlH/Pw6f7w6v7Rv/zRv/xkKPVmJ/VlJvZjJvLw6/7w6v/w6v9jJ/NmKfbv6PxlKPXw6v7Rv/yEU/f///95QvZmKfXs5f7Twvx/TPd0PfZuNPbZyv1pLfXbzf11PvZyOvbf0v3e0P2RZPiIWPfNuvzKtftsMvX18v7+/v+NYfh7Rva2mfq5nfuCUPe+pPuvkPqvkPnAI7BMAAAAKHRSTlMA/gEgr+/uv99g798gr5CAb2BfEO/PsIBQEN/Pv69wj1BQ78/OkI5P5JBZSwAAB1NJREFUeNrs3EtqAkEYReFqYhJF8o4vBPMYhFuIgjhTwUH2v6eM09GyUOmckvut4MJ/aHVgByvYVVso7atgJYP1JLVHwcrVEc4gWLl6wrkNVi7xVMHKJR4HVTLxOKiSicdBlUw8Dqpk4nFQJROPgyqZeBxUycQDDurlsd+N/27cfw5Y4uEGNfyMEF1sUuLBBnUfQR4Ck3ioQT1ElPuAJB5oUE8RZhiIxAMNCvBt/LfXlwAkHmZQuAdUjI8BSDzMoO4iTj8AiYcZ1DjidAOQeJhBRaAAJB4H5aAcVA4HlcVB5XJQWRxULgeVxUHlclBZHFQDEkGNJi3Av797rY6DOi2o1WapJq1XO4N6vxFEu+OgTghqtVTDlqsdQb2Joxo4qOODWqtxm79BDYTy5aCODkrNW8Ya3KtOqg8HVVBQijW8dzFdj8TjoPaJNbQHlFRNxOOg9ok1LeEAJzmovWIN8OVeuA9hOaj9CBsOqMTjoHZzUEeqiD89EcckbEhyULkYxyRsSHJQuRjHJGxIclC5GMckbEhyULkYxyRsSHJQuRjHJGxIclC5GMckbEhyULkYxyRsSKIGRUQ4JmFDkoO6/KC2s8X0LBazrQ5xUJce1Px7ekazudIc1IUHNV9Mz2oxV5KDuvCgfti5tyVFYSgKw1YB8iqrxViFaAXPjrb2zPu/0DCtJbZj04GEmMD67+HquwnZG5EY7ojKCKrboHaJ8XaoiqC6DUokxhOoiqC6DSpNjJeiKoLqNqikhVAVQREUQalFUPgP1FHmeS5SgnIi70Edd7iUpwTlQL6DmuLWPCWo1+c5qCnKClEE9fL8BiXwpfmZoGrWT1D5+3ak1unQK1BBPES9wmBwS/1p9bd7ASrfjpTbHHoEKmgAIrqJUni6wdt9APV7VKOPHoEK0aB4cC1EC8U+gBrVadMjUGhS1OqPjaPOgRoR1A+1CcqNeW5boNJcEBRBAVszoNI5IAmKoPBex9OpyhMgCYqgtD8blJ4ASVC9B4X8jyqnj588AZKgeg/qoZLBeY6y0kqVJ0AS1GcRzOc7qHsoqp4AWR/U5M3BNEHFaKHQc1BJmuPaRNUTIAmqKIhgvKEXVy+PPWjJd8BkNz0rewIkQRUFIcw2jP24HH4sqUjJEyAJ6lI/x1ce0vcESIL6jKCKDHgCJEH9i6CKTHgCJEEV9ROUmO3HOm0EniQIajDoJSixH2u2fy6KoAa9BLUZazfDs44E1TqoDJZSBzUZ67fHs6YE1TqoNay3sgDqF0HdMrL1EgaKoBYZLJctCMo6KP2tlyhQAlWIWsFm2XrxRlAvARVCq/gbUA5GUFZAQa+IoAjKJCgQlFFQy3UGm62WBGUK1N5BUEv7h5clQZkBhZk+qA0Ao6BWsN6aoBSzc/WiB8qBG4WMoAyBgtjocZoJAN6DAkEp1nx8ZQrlCOqSqa0XRVBOnF5QRlB3ubX1EqqAcuP0glsEdZ9TWy/DQAWUG6cXXCOorzm09TKMA3fnoTKCUsvL8RW8IB9BuTA3RlAdAuXC3BhBGQeVCq3SpqDcmBsjqKc1+E/56ZCYqTEoJ+bG/rJzRy0Nw0AAxzOtYy19HWPQTn0Q7g5mMgjBFzfQqd//GwmzL7sVasPVXW1+r4V7GH/KGpKkoFrFXDj2+nb1oBRIQbWKuqf8KwU1kqA0fL0A03obuawdpKAmc+oFqIVwUFuYYFBlVZCsuhzDqZe/COoA0wuqLEhcUeI5FV8vwOzpkvgLanpB1TSAChUC5kgXPmT/QT0DwOSCoiEUqFD3ssH+U7Kn91NPKSgRqBAw/J7y/fEFDtud0Ntpe4BGr6Csd9RfsNhIQQ1Hw1oY9AzKOoriBIuK3HNpGgXFSUFF6AwqUCSPYuL2XGamUdEAalQI9LkIimI5FBO153J+O/CygUKgj1xQhGJ677lkOy3LWjqnqhzlqRdZYw5KIeSsdyQpWD6bPx9lUI56m2ZQ1pEwZ/ls/nyMQXmKFPDHVIIKJM53zPbILECdJZ5RsmygETIkz3XMdshkoE6GnA3Un/MWGymoeF2zkdmANrMcFTIa4RkdQa3vQJnFDSpkNELGkbyu2ciYR9BllqegYoPyJC50zA7IGPMEqqxMCmrMywbGmBUocm9SUL+GnA2yOXnLZ/PnrT9UrmbtYP5gUlBtxnPhmDnJsyVc3SLbrE0K6l8EpUoKKgX13a7dpSAIRVEU3vga9qeE2GNBHJ98kOY/tAZgN66CtLutbwiHhaDbTYNKbZ75/2SWeSeCWhdU+uUld1wp9E4EtTao55A0RYZC70RQa4Ma0sbIUOidCGqDoIbIUOidpF3YuckPQeWqw85FfmZBjQT13jHsnOVnFtT0abvMUGxQTRtmehnis0G2Lsw4PqBmQSU3z3H68w+b0j6sHOSI6WWBQxjZyxJBLXHqw0TbyRNBLXOqr/F1u/rYyBRBgaDgi6BAUPBVGW6ed+FnVWyeEJsnbLF5QmLzhK2KzRMSmyd8GW2eD6EEbJ4o2AuunoM0UoU65AAAAABJRU5ErkJggg==');
180
+ width: 74px;
181
+ height: 39px;
182
+ background-size: 188px;
183
+ }
184
+
185
+ .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-alpha {
186
+ background-position: 0 0;
187
+ }
188
+
189
+ .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-beta {
190
+ background-position: -114px 0;
191
+ height: 65px;
192
+ }
193
+
194
+ .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-charlie {
195
+ background-position: 0 -102px;
196
+ height: 50px;
197
+ }
198
+
199
+ .exactmetrics-products-theme-preview-icon.exactmetrics-products-theme-preview-icon-delta {
200
+ background-position: -114px -102px;
201
+ height: 78px;
202
+ }
203
+
204
+ .exactmetrics-pro-pill {
205
+ font-style: normal;
206
+ font-weight: bold;
207
+ font-size: 11px;
208
+ background: #1EC185;
209
+ border-radius: 5px;
210
+ color: #fff;
211
+ padding: 3px 8px;
212
+ margin-left: 8px;
213
+ }
214
+
215
+ .select300-container--disabled {
216
+ margin-top: 10px;
217
+ opacity: 0.6;
218
+ }
assets/images/plugin-pushengage.svg CHANGED
@@ -1,19 +1,19 @@
1
- <svg width="470" height="74" viewBox="0 0 470 74" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M73.1 30.9C73 40.3 70.2 47.8 64.6 54.2C63.6 55.4 62.4 56.5 61.2 57.5C60.1 58.5 58.5 58.5 57.6 57.5C56.7 56.4 56.9 54.9 58.1 53.9C60.6 51.8 62.6 49.4 64.2 46.6C66.6 42.5 68 38.1 68.2 33.3C68.6 25 66 17.9 60.5 11.8C59.7 11 58.8 10.2 58 9.39998C56.9 8.39998 56.7 6.79998 57.6 5.79998C58.5 4.69998 60 4.69998 61.2 5.79998C67.6 11.3 71.4 18.3 72.8 26.6C73 28.3 73 30 73.1 30.9Z" fill="#3B43FF"/>
3
- <path d="M5 31.7C5.2 21.3 9.1 12.6 17 5.69998C18.1 4.69998 19.7 4.79998 20.5 5.79998C21.4 6.79998 21.2 8.39998 20.1 9.29998C14.7 14 11.3 19.8 10.2 26.9C8.7 36.2 11.2 44.4 17.5 51.4C18.3 52.3 19.3 53.2 20.2 54C21.3 55 21.4 56.5 20.5 57.5C19.6 58.5 18.1 58.5 17 57.6C10 51.6 6 43.9 5.2 34.7C5.2 34.2 5.1 33.7 5.1 33.2C5 32.7 5 32.2 5 31.7Z" fill="#3B43FF"/>
4
- <path d="M14.7 31.6C14.9 24.2 17.6 18 23.3 13.1C24.5 12.1 25.8 12.2 26.8 13.3C27.7 14.3 27.6 15.6 26.4 16.7C23.7 19.2 21.5 22.1 20.4 25.7C18.3 33 19.8 39.5 25 45.1C25.5 45.7 26.1 46.2 26.6 46.8C27.5 47.7 27.6 49.1 26.8 50C26 51 24.4 51.2 23.5 50.3C22 48.9 20.6 47.5 19.3 45.9C16.6 42.3 15.1 38.2 14.8 33.6C14.8 33.3 14.7 33 14.7 32.6C14.7 32.3 14.7 32 14.7 31.6Z" fill="#3B43FF"/>
5
- <path d="M63.4 31.7C63.2 38.9 60.5 45 55.1 49.9C54.3 50.7 53.3 51.1 52.2 50.7C50.6 50 50.2 48.2 51.4 46.8C52.8 45.2 54.4 43.7 55.6 41.9C60.4 34.5 59.3 24.4 53 18.1C52.5 17.6 52 17.1 51.5 16.6C50.6 15.7 50.5 14.3 51.3 13.4C52.2 12.4 53.6 12.2 54.7 13C56.7 14.6 58.4 16.5 59.7 18.7C61.8 22.1 63.1 25.8 63.3 29.8C63.3 30.4 63.3 31 63.4 31.7Z" fill="#3B43FF"/>
6
- <path d="M53.5 31.1C53.5 36.1 51.8 39.6 48.6 42.6C47.5 43.7 45.9 43.7 45 42.6C44.1 41.6 44.1 40.1 45.3 39.1C47.3 37.2 48.6 34.9 48.7 32.1C48.8 29.4 48 27.1 46.2 25.1C45.9 24.7 45.5 24.4 45.2 24.1C44.2 23.1 44.1 21.7 45 20.7C45.9 19.7 47.4 19.6 48.5 20.6C50.2 22.1 51.5 23.9 52.4 26C53.2 27.8 53.6 29.7 53.5 31.1Z" fill="#3B43FF"/>
7
- <path d="M30.9 19.9C32.3 19.9 33 20.4 33.5 21.4C34 22.4 33.7 23.4 32.9 24.1C31.6 25.4 30.5 26.7 29.9 28.5C28.7 32.3 29.5 35.6 32.1 38.5C32.3 38.8 32.6 39 32.9 39.3C33.9 40.4 34 41.8 33 42.8C32.1 43.7 30.6 43.8 29.6 42.9C27.1 40.7 25.4 38.1 24.9 34.8C23.9 29.4 25.4 24.7 29.4 21C29.9 20.3 30.6 20.1 30.9 19.9Z" fill="#3B43FF"/>
8
- <path d="M38.9 36.4C36.2 36.4 34.1 34.3 34.1 31.6C34.1 28.9 36.3 26.8 39 26.8C41.7 26.8 43.8 28.9 43.8 31.6C43.8 34.4 41.7 36.4 38.9 36.4Z" fill="#3B43FF"/>
9
- <path d="M89.752 56H100.624V38.648C101.632 38.792 102.928 38.864 104.368 38.864C110.848 38.864 116.392 37.28 120.136 33.752C123.016 31.016 124.6 26.984 124.6 22.232C124.6 17.48 122.512 13.448 119.416 11C116.176 8.40801 111.352 7.11201 104.584 7.11201C97.888 7.11201 93.136 7.54401 89.752 8.12001V56ZM100.624 15.824C101.416 15.608 102.928 15.392 105.16 15.392C110.632 15.392 113.728 18.056 113.728 22.52C113.728 27.488 110.128 30.44 104.296 30.44C102.712 30.44 101.56 30.368 100.624 30.152V15.824Z" fill="#191A35"/>
10
- <path d="M162.277 20.792H151.261V41.744C151.261 42.608 151.189 43.472 150.901 44.12C150.181 45.848 148.453 47.936 145.429 47.936C141.613 47.936 139.957 44.84 139.957 39.728V20.792H128.941V41.456C128.941 52.328 134.197 56.792 141.325 56.792C147.661 56.792 150.973 53.192 152.341 51.032H152.557L153.061 56H162.565C162.421 53.048 162.277 49.304 162.277 44.696V20.792Z" fill="#191A35"/>
11
- <path d="M167.605 54.272C170.269 55.712 174.373 56.72 178.981 56.72C189.061 56.72 194.245 51.896 194.245 45.344C194.173 40.16 191.365 36.776 184.669 34.472C180.349 32.96 178.981 32.096 178.981 30.44C178.981 28.64 180.493 27.56 183.157 27.56C186.181 27.56 189.205 28.712 190.789 29.576L192.733 22.016C190.573 20.936 186.901 20 182.797 20C174.085 20 168.469 24.968 168.469 31.52C168.397 35.696 171.205 39.656 178.477 42.104C182.581 43.472 183.661 44.336 183.661 46.136C183.661 47.936 182.293 49.088 178.981 49.088C175.741 49.088 171.565 47.648 169.549 46.424L167.605 54.272Z" fill="#191A35"/>
12
- <path d="M199.4 56H210.344V35.12C210.344 34.328 210.416 33.392 210.704 32.672C211.496 30.728 213.296 28.856 216.176 28.856C220.28 28.856 221.864 32.024 221.864 36.632V56H232.808V35.408C232.808 24.824 227.48 20 220.496 20C218.48 20 216.392 20.504 214.808 21.368C213.08 22.304 211.64 23.456 210.488 24.968H210.344V4.88H199.4V56Z" fill="#191A35"/>
13
- <path d="M269.38 26.552H251.524V16.472H270.46V7.47201H240.508V56H271.468V47H251.524V35.48H269.38V26.552Z" fill="#191A35"/>
14
- <path d="M277.169 56H288.113V35.696C288.113 34.688 288.257 33.608 288.545 32.96C289.265 30.944 291.065 28.856 294.089 28.856C298.049 28.856 299.633 31.952 299.633 36.488V56H310.577V35.12C310.577 24.752 305.177 20 297.977 20C292.145 20 288.545 23.384 287.105 25.616H286.889L286.385 20.792H276.881C277.025 23.888 277.169 27.632 277.169 32.024V56Z" fill="#191A35"/>
15
- <path d="M342.036 20.792L341.676 24.968H341.532C339.732 22.088 336.708 20 331.884 20C323.244 20 315.9 27.2 315.9 38.504C315.9 48.656 322.092 55.712 330.804 55.712C334.836 55.712 338.22 54.056 340.308 51.104H340.452V53.408C340.452 59.888 336.492 62.696 331.308 62.696C327.204 62.696 323.316 61.328 321.084 60.032L318.924 68.312C322.092 70.112 326.988 71.048 331.596 71.048C336.78 71.048 342.036 70.04 345.924 66.728C349.884 63.2 351.252 57.656 351.252 50.888V31.232C351.252 25.976 351.396 23.024 351.54 20.792H342.036ZM340.308 40.304C340.308 41.24 340.236 42.392 339.948 43.184C339.228 45.776 336.996 47.648 334.188 47.648C329.58 47.648 326.988 43.544 326.988 38.288C326.988 31.88 330.228 28.28 334.188 28.28C337.212 28.28 339.3 30.224 340.092 33.104C340.236 33.68 340.308 34.4 340.308 35.12V40.304Z" fill="#191A35"/>
16
- <path d="M388.473 56C388.113 54.056 387.969 50.816 387.969 47.432V35.192C387.969 26.912 384.225 20 372.489 20C366.081 20 361.257 21.728 358.809 23.096L360.825 30.152C363.129 28.712 366.945 27.488 370.545 27.488C375.945 27.488 376.953 30.152 376.953 32.024V32.528C364.497 32.456 356.289 36.848 356.289 45.992C356.289 51.608 360.537 56.792 367.665 56.792C371.841 56.792 375.441 55.28 377.745 52.472H377.961L378.609 56H388.473ZM377.313 43.184C377.313 43.832 377.241 44.48 377.097 45.056C376.377 47.288 374.145 49.088 371.409 49.088C368.961 49.088 367.089 47.72 367.089 44.912C367.089 40.664 371.553 39.296 377.313 39.368V43.184Z" fill="#191A35"/>
17
- <path d="M419.312 20.792L418.952 24.968H418.808C417.008 22.088 413.984 20 409.16 20C400.52 20 393.176 27.2 393.176 38.504C393.176 48.656 399.368 55.712 408.08 55.712C412.112 55.712 415.496 54.056 417.584 51.104H417.728V53.408C417.728 59.888 413.768 62.696 408.584 62.696C404.48 62.696 400.592 61.328 398.36 60.032L396.2 68.312C399.368 70.112 404.264 71.048 408.872 71.048C414.056 71.048 419.312 70.04 423.2 66.728C427.16 63.2 428.528 57.656 428.528 50.888V31.232C428.528 25.976 428.672 23.024 428.816 20.792H419.312ZM417.584 40.304C417.584 41.24 417.512 42.392 417.224 43.184C416.504 45.776 414.272 47.648 411.464 47.648C406.856 47.648 404.264 43.544 404.264 38.288C404.264 31.88 407.504 28.28 411.464 28.28C414.488 28.28 416.576 30.224 417.368 33.104C417.512 33.68 417.584 34.4 417.584 35.12V40.304Z" fill="#191A35"/>
18
- <path d="M466.973 41.888C467.117 41.096 467.333 39.44 467.333 37.568C467.333 28.856 463.013 20 451.637 20C439.397 20 433.853 29.864 433.853 38.792C433.853 49.808 440.693 56.72 452.645 56.72C457.397 56.72 461.789 56 465.389 54.56L463.949 47.144C460.997 48.08 457.973 48.584 454.229 48.584C449.117 48.584 444.653 46.424 444.293 41.888H466.973ZM444.221 34.4C444.509 31.448 446.381 27.272 450.989 27.272C456.029 27.272 457.181 31.736 457.181 34.4H444.221Z" fill="#191A35"/>
19
- </svg>
1
+ <svg width="470" height="74" viewBox="0 0 470 74" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M73.1 30.9C73 40.3 70.2 47.8 64.6 54.2C63.6 55.4 62.4 56.5 61.2 57.5C60.1 58.5 58.5 58.5 57.6 57.5C56.7 56.4 56.9 54.9 58.1 53.9C60.6 51.8 62.6 49.4 64.2 46.6C66.6 42.5 68 38.1 68.2 33.3C68.6 25 66 17.9 60.5 11.8C59.7 11 58.8 10.2 58 9.39998C56.9 8.39998 56.7 6.79998 57.6 5.79998C58.5 4.69998 60 4.69998 61.2 5.79998C67.6 11.3 71.4 18.3 72.8 26.6C73 28.3 73 30 73.1 30.9Z" fill="#3B43FF"/>
3
+ <path d="M5 31.7C5.2 21.3 9.1 12.6 17 5.69998C18.1 4.69998 19.7 4.79998 20.5 5.79998C21.4 6.79998 21.2 8.39998 20.1 9.29998C14.7 14 11.3 19.8 10.2 26.9C8.7 36.2 11.2 44.4 17.5 51.4C18.3 52.3 19.3 53.2 20.2 54C21.3 55 21.4 56.5 20.5 57.5C19.6 58.5 18.1 58.5 17 57.6C10 51.6 6 43.9 5.2 34.7C5.2 34.2 5.1 33.7 5.1 33.2C5 32.7 5 32.2 5 31.7Z" fill="#3B43FF"/>
4
+ <path d="M14.7 31.6C14.9 24.2 17.6 18 23.3 13.1C24.5 12.1 25.8 12.2 26.8 13.3C27.7 14.3 27.6 15.6 26.4 16.7C23.7 19.2 21.5 22.1 20.4 25.7C18.3 33 19.8 39.5 25 45.1C25.5 45.7 26.1 46.2 26.6 46.8C27.5 47.7 27.6 49.1 26.8 50C26 51 24.4 51.2 23.5 50.3C22 48.9 20.6 47.5 19.3 45.9C16.6 42.3 15.1 38.2 14.8 33.6C14.8 33.3 14.7 33 14.7 32.6C14.7 32.3 14.7 32 14.7 31.6Z" fill="#3B43FF"/>
5
+ <path d="M63.4 31.7C63.2 38.9 60.5 45 55.1 49.9C54.3 50.7 53.3 51.1 52.2 50.7C50.6 50 50.2 48.2 51.4 46.8C52.8 45.2 54.4 43.7 55.6 41.9C60.4 34.5 59.3 24.4 53 18.1C52.5 17.6 52 17.1 51.5 16.6C50.6 15.7 50.5 14.3 51.3 13.4C52.2 12.4 53.6 12.2 54.7 13C56.7 14.6 58.4 16.5 59.7 18.7C61.8 22.1 63.1 25.8 63.3 29.8C63.3 30.4 63.3 31 63.4 31.7Z" fill="#3B43FF"/>
6
+ <path d="M53.5 31.1C53.5 36.1 51.8 39.6 48.6 42.6C47.5 43.7 45.9 43.7 45 42.6C44.1 41.6 44.1 40.1 45.3 39.1C47.3 37.2 48.6 34.9 48.7 32.1C48.8 29.4 48 27.1 46.2 25.1C45.9 24.7 45.5 24.4 45.2 24.1C44.2 23.1 44.1 21.7 45 20.7C45.9 19.7 47.4 19.6 48.5 20.6C50.2 22.1 51.5 23.9 52.4 26C53.2 27.8 53.6 29.7 53.5 31.1Z" fill="#3B43FF"/>
7
+ <path d="M30.9 19.9C32.3 19.9 33 20.4 33.5 21.4C34 22.4 33.7 23.4 32.9 24.1C31.6 25.4 30.5 26.7 29.9 28.5C28.7 32.3 29.5 35.6 32.1 38.5C32.3 38.8 32.6 39 32.9 39.3C33.9 40.4 34 41.8 33 42.8C32.1 43.7 30.6 43.8 29.6 42.9C27.1 40.7 25.4 38.1 24.9 34.8C23.9 29.4 25.4 24.7 29.4 21C29.9 20.3 30.6 20.1 30.9 19.9Z" fill="#3B43FF"/>
8
+ <path d="M38.9 36.4C36.2 36.4 34.1 34.3 34.1 31.6C34.1 28.9 36.3 26.8 39 26.8C41.7 26.8 43.8 28.9 43.8 31.6C43.8 34.4 41.7 36.4 38.9 36.4Z" fill="#3B43FF"/>
9
+ <path d="M89.752 56H100.624V38.648C101.632 38.792 102.928 38.864 104.368 38.864C110.848 38.864 116.392 37.28 120.136 33.752C123.016 31.016 124.6 26.984 124.6 22.232C124.6 17.48 122.512 13.448 119.416 11C116.176 8.40801 111.352 7.11201 104.584 7.11201C97.888 7.11201 93.136 7.54401 89.752 8.12001V56ZM100.624 15.824C101.416 15.608 102.928 15.392 105.16 15.392C110.632 15.392 113.728 18.056 113.728 22.52C113.728 27.488 110.128 30.44 104.296 30.44C102.712 30.44 101.56 30.368 100.624 30.152V15.824Z" fill="#191A35"/>
10
+ <path d="M162.277 20.792H151.261V41.744C151.261 42.608 151.189 43.472 150.901 44.12C150.181 45.848 148.453 47.936 145.429 47.936C141.613 47.936 139.957 44.84 139.957 39.728V20.792H128.941V41.456C128.941 52.328 134.197 56.792 141.325 56.792C147.661 56.792 150.973 53.192 152.341 51.032H152.557L153.061 56H162.565C162.421 53.048 162.277 49.304 162.277 44.696V20.792Z" fill="#191A35"/>
11
+ <path d="M167.605 54.272C170.269 55.712 174.373 56.72 178.981 56.72C189.061 56.72 194.245 51.896 194.245 45.344C194.173 40.16 191.365 36.776 184.669 34.472C180.349 32.96 178.981 32.096 178.981 30.44C178.981 28.64 180.493 27.56 183.157 27.56C186.181 27.56 189.205 28.712 190.789 29.576L192.733 22.016C190.573 20.936 186.901 20 182.797 20C174.085 20 168.469 24.968 168.469 31.52C168.397 35.696 171.205 39.656 178.477 42.104C182.581 43.472 183.661 44.336 183.661 46.136C183.661 47.936 182.293 49.088 178.981 49.088C175.741 49.088 171.565 47.648 169.549 46.424L167.605 54.272Z" fill="#191A35"/>
12
+ <path d="M199.4 56H210.344V35.12C210.344 34.328 210.416 33.392 210.704 32.672C211.496 30.728 213.296 28.856 216.176 28.856C220.28 28.856 221.864 32.024 221.864 36.632V56H232.808V35.408C232.808 24.824 227.48 20 220.496 20C218.48 20 216.392 20.504 214.808 21.368C213.08 22.304 211.64 23.456 210.488 24.968H210.344V4.88H199.4V56Z" fill="#191A35"/>
13
+ <path d="M269.38 26.552H251.524V16.472H270.46V7.47201H240.508V56H271.468V47H251.524V35.48H269.38V26.552Z" fill="#191A35"/>
14
+ <path d="M277.169 56H288.113V35.696C288.113 34.688 288.257 33.608 288.545 32.96C289.265 30.944 291.065 28.856 294.089 28.856C298.049 28.856 299.633 31.952 299.633 36.488V56H310.577V35.12C310.577 24.752 305.177 20 297.977 20C292.145 20 288.545 23.384 287.105 25.616H286.889L286.385 20.792H276.881C277.025 23.888 277.169 27.632 277.169 32.024V56Z" fill="#191A35"/>
15
+ <path d="M342.036 20.792L341.676 24.968H341.532C339.732 22.088 336.708 20 331.884 20C323.244 20 315.9 27.2 315.9 38.504C315.9 48.656 322.092 55.712 330.804 55.712C334.836 55.712 338.22 54.056 340.308 51.104H340.452V53.408C340.452 59.888 336.492 62.696 331.308 62.696C327.204 62.696 323.316 61.328 321.084 60.032L318.924 68.312C322.092 70.112 326.988 71.048 331.596 71.048C336.78 71.048 342.036 70.04 345.924 66.728C349.884 63.2 351.252 57.656 351.252 50.888V31.232C351.252 25.976 351.396 23.024 351.54 20.792H342.036ZM340.308 40.304C340.308 41.24 340.236 42.392 339.948 43.184C339.228 45.776 336.996 47.648 334.188 47.648C329.58 47.648 326.988 43.544 326.988 38.288C326.988 31.88 330.228 28.28 334.188 28.28C337.212 28.28 339.3 30.224 340.092 33.104C340.236 33.68 340.308 34.4 340.308 35.12V40.304Z" fill="#191A35"/>
16
+ <path d="M388.473 56C388.113 54.056 387.969 50.816 387.969 47.432V35.192C387.969 26.912 384.225 20 372.489 20C366.081 20 361.257 21.728 358.809 23.096L360.825 30.152C363.129 28.712 366.945 27.488 370.545 27.488C375.945 27.488 376.953 30.152 376.953 32.024V32.528C364.497 32.456 356.289 36.848 356.289 45.992C356.289 51.608 360.537 56.792 367.665 56.792C371.841 56.792 375.441 55.28 377.745 52.472H377.961L378.609 56H388.473ZM377.313 43.184C377.313 43.832 377.241 44.48 377.097 45.056C376.377 47.288 374.145 49.088 371.409 49.088C368.961 49.088 367.089 47.72 367.089 44.912C367.089 40.664 371.553 39.296 377.313 39.368V43.184Z" fill="#191A35"/>
17
+ <path d="M419.312 20.792L418.952 24.968H418.808C417.008 22.088 413.984 20 409.16 20C400.52 20 393.176 27.2 393.176 38.504C393.176 48.656 399.368 55.712 408.08 55.712C412.112 55.712 415.496 54.056 417.584 51.104H417.728V53.408C417.728 59.888 413.768 62.696 408.584 62.696C404.48 62.696 400.592 61.328 398.36 60.032L396.2 68.312C399.368 70.112 404.264 71.048 408.872 71.048C414.056 71.048 419.312 70.04 423.2 66.728C427.16 63.2 428.528 57.656 428.528 50.888V31.232C428.528 25.976 428.672 23.024 428.816 20.792H419.312ZM417.584 40.304C417.584 41.24 417.512 42.392 417.224 43.184C416.504 45.776 414.272 47.648 411.464 47.648C406.856 47.648 404.264 43.544 404.264 38.288C404.264 31.88 407.504 28.28 411.464 28.28C414.488 28.28 416.576 30.224 417.368 33.104C417.512 33.68 417.584 34.4 417.584 35.12V40.304Z" fill="#191A35"/>
18
+ <path d="M466.973 41.888C467.117 41.096 467.333 39.44 467.333 37.568C467.333 28.856 463.013 20 451.637 20C439.397 20 433.853 29.864 433.853 38.792C433.853 49.808 440.693 56.72 452.645 56.72C457.397 56.72 461.789 56 465.389 54.56L463.949 47.144C460.997 48.08 457.973 48.584 454.229 48.584C449.117 48.584 444.653 46.424 444.293 41.888H466.973ZM444.221 34.4C444.509 31.448 446.381 27.272 450.989 27.272C456.029 27.272 457.181 31.736 457.181 34.4H444.221Z" fill="#191A35"/>
19
+ </svg>
assets/js/admin-widget-settings.js CHANGED
@@ -1,68 +1,68 @@
1
- (
2
- function ( $ ) {
3
-
4
- // Add Color Picker to all inputs that have 'color-field' class.
5
- $( function () {
6
- init_color_picker();
7
- init_multiselect();
8
- $( document ).on( 'widget-updated widget-added', function (e) {
9
- init_color_picker();
10
- init_multiselect();
11
- } );
12
- $( document ).on( 'change', '.exactmetrics-save-on-change', function () {
13
- save_and_refresh_form( $( this ).closest( '.widget' ) );
14
- } );
15
- } );
16
-
17
- function init_color_picker() {
18
- var timeout;
19
- $( '#widgets-right .exactmetrics-color-field' ).wpColorPicker( {
20
- change: function ( event, ui ) {
21
- if ( timeout ) {
22
- clearTimeout( timeout );
23
- }
24
- timeout = setTimeout( function () {
25
- $( event.target ).trigger( 'change' );
26
- }, 300 );
27
- },
28
- } );
29
- }
30
-
31
- function save_and_refresh_form( widget ) {
32
- if ( wpWidgets && 'undefined' !== typeof wpWidgets.save ) {
33
- wpWidgets.save( widget, 0, 0 );
34
- }
35
- }
36
-
37
- function init_multiselect() {
38
- if ( ! jQuery.fn.select300 ) {
39
- return;
40
- }
41
- $('#widgets-right .exactmetrics-multiselect').select300({
42
- ajax: {
43
- type: 'POST',
44
- url: ajaxurl,
45
- delay: 250,
46
- width: 'resolve',
47
- data: function (params) {
48
- var taxonomy = $(this).data('taxonomy');
49
- return {
50
- taxonomy: taxonomy,
51
- keyword: params.term,
52
- action: 'exactmetrics_get_terms',
53
- nonce: exactmetrics_pp.nonce,
54
- };
55
- },
56
- processResults: function (data) {
57
- return {
58
- results: data.data
59
- };
60
- },
61
- dataType: 'json'
62
- }
63
- });
64
-
65
- }
66
-
67
- }
68
- )( jQuery );
1
+ (
2
+ function ( $ ) {
3
+
4
+ // Add Color Picker to all inputs that have 'color-field' class.
5
+ $( function () {
6
+ init_color_picker();
7
+ init_multiselect();
8
+ $( document ).on( 'widget-updated widget-added', function (e) {
9
+ init_color_picker();
10
+ init_multiselect();
11
+ } );
12
+ $( document ).on( 'change', '.exactmetrics-save-on-change', function () {
13
+ save_and_refresh_form( $( this ).closest( '.widget' ) );
14
+ } );
15
+ } );
16
+
17
+ function init_color_picker() {
18
+ var timeout;
19
+ $( '#widgets-right .exactmetrics-color-field' ).wpColorPicker( {
20
+ change: function ( event, ui ) {
21
+ if ( timeout ) {
22
+ clearTimeout( timeout );
23
+ }
24
+ timeout = setTimeout( function () {
25
+ $( event.target ).trigger( 'change' );
26
+ }, 300 );
27
+ },
28
+ } );
29
+ }
30
+
31
+ function save_and_refresh_form( widget ) {
32
+ if ( wpWidgets && 'undefined' !== typeof wpWidgets.save ) {
33
+ wpWidgets.save( widget, 0, 0 );
34
+ }
35
+ }
36
+
37
+ function init_multiselect() {
38
+ if ( ! jQuery.fn.select300 ) {
39
+ return;
40
+ }
41
+ $('#widgets-right .exactmetrics-multiselect').select300({
42
+ ajax: {
43
+ type: 'POST',
44
+ url: ajaxurl,
45
+ delay: 250,
46
+ width: 'resolve',
47
+ data: function (params) {
48
+ var taxonomy = $(this).data('taxonomy');
49
+ return {
50
+ taxonomy: taxonomy,
51
+ keyword: params.term,
52
+ action: 'exactmetrics_get_terms',
53
+ nonce: exactmetrics_pp.nonce,
54
+ };
55
+ },
56
+ processResults: function (data) {
57
+ return {
58
+ results: data.data
59
+ };
60
+ },
61
+ dataType: 'json'
62
+ }
63
+ });
64
+
65
+ }
66
+
67
+ }
68
+ )( jQuery );
assets/js/frontend-gtag.js CHANGED
@@ -1,829 +1,829 @@
1
- /**
2
- * Developer's Notice:
3
- *
4
- * Note: JS in this file (and this file itself) is not guaranteed backwards compatibility. JS can be added, changed or removed at any time without notice.
5
- * For more information see the `Backwards Compatibility Guidelines for Developers` section of the README.md file.
6
- */
7
- /**
8
- * Handles:
9
- * - JS Events handling
10
- *
11
- * @since 7.15.0
12
- */
13
- var ExactMetrics = function () {
14
- /* ExactMetrics JS events tracking works on all major browsers, including IE starting at IE 7, via polyfills for any major JS function used that
15
- is not supported by at least 95% of the global and/or US browser marketshare. Currently, IE 7 & 8 which as of 2/14/17 have under 0.25% global marketshare, require
16
- us to polyfill Array.prototype.lastIndexOf, and if they continue to drop, we might remove this polyfill at some point. In that case note that events tracking
17
- for IE 7/8 will continue to work, with the exception of events tracking of downloads. */
18
- var lastClicked = [];
19
- var internalAsOutboundCategory = '';
20
- var beforeUnloadChanged = false;
21
-
22
- this.setLastClicked = function ( valuesArray, fieldsArray, tracked ) {
23
- valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
24
- fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : [];
25
- tracked = typeof tracked !== 'undefined' ? tracked : false;
26
-
27
- lastClicked.valuesArray = valuesArray;
28
- lastClicked.fieldsArray = fieldsArray;
29
- };
30
-
31
- this.getLastClicked = function () {
32
- return lastClicked;
33
- };
34
-
35
- this.setInternalAsOutboundCategory = function ( category ) {
36
- internalAsOutboundCategory = category;
37
- };
38
-
39
- this.getInternalAsOutboundCategory = function () {
40
- return internalAsOutboundCategory;
41
- };
42
-
43
- this.sendEvent = function ( type, action, fieldsArray ) {
44
- __gtagTrackerSend( type, action, fieldsArray, [] );
45
- };
46
-
47
- function __gtagTrackerIsDebug() {
48
- if ( window.exactmetrics_debug_mode ) {
49
- return true;
50
- } else {
51
- return false;
52
- }
53
- }
54
-
55
- function cloneFields( fields, allowedKeys, disallowedKeys ) {
56
- var clone = {};
57
-
58
- for ( var key in fields ) {
59
- if ( ! fields.hasOwnProperty( key ) ) {
60
- continue
61
- }
62
-
63
- if ( allowedKeys && allowedKeys.indexOf( key ) === -1 ) {
64
- continue
65
- }
66
-
67
- if ( disallowedKeys && disallowedKeys.indexOf( key ) > -1 ) {
68
- continue
69
- }
70
-
71
- clone[ key ] = fields[ key ];
72
- }
73
-
74
- return clone;
75
- }
76
-
77
- function __gtagMaybeTrackerV4( type, action, fieldsArray ) {
78
- if ( ! exactmetrics_frontend.v4_id || type !== 'event' ) {
79
- return;
80
- }
81
-
82
- var eventCategory = fieldsArray.event_category || '';
83
-
84
- var fieldsToRemove = [
85
- 'event_name',
86
- 'event_category',
87
- 'event_label',
88
- 'value',
89
- ];
90
-
91
- var fields = cloneFields( fieldsArray, null, fieldsToRemove );
92
- fields.action = action;
93
-
94
- var eventMap = {
95
- 'outbound-link': 'click',
96
- 'download': 'file_download',
97
- };
98
-
99
- __gtagTracker( type, eventMap[ eventCategory ] || eventCategory.replace( '-', '_' ), fields );
100
- }
101
-
102
- function __gtagMaybeTrackerUA( type, action, fieldsArray ) {
103
- if ( ! exactmetrics_frontend.ua ) {
104
- return;
105
- }
106
-
107
- var allowedFields = [
108
- 'event_category',
109
- 'event_label',
110
- 'value',
111
- ];
112
-
113
- var uaFields = cloneFields(fieldsArray, allowedFields);
114
- uaFields.send_to = exactmetrics_frontend.ua
115
-
116
- __gtagTracker( type, action, uaFields );
117
- }
118
-
119
- function __gtagTrackerSendDual( type, action, fieldsArray, valuesArray ) {
120
- type = typeof type !== 'undefined' ? type : 'event';
121
- action = typeof action !== 'undefined' ? action : '';
122
- valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
123
- fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {};
124
-
125
- __gtagMaybeTrackerUA( type, action, fieldsArray );
126
- __gtagMaybeTrackerV4( type, action, fieldsArray );
127
-
128
- lastClicked.valuesArray = valuesArray;
129
- lastClicked.fieldsArray = fieldsArray;
130
- lastClicked.fieldsArray.event_action = action;
131
- lastClicked.tracked = true;
132
- __gtagTrackerLog( 'Tracked: ' + valuesArray.type );
133
- __gtagTrackerLog( lastClicked );
134
- }
135
-
136
- /**
137
- * This attempts to be compatible with the gtag function.
138
- *
139
- * @see https://developers.google.com/analytics/devguides/collection/gtagjs
140
- * @param type string Type of request, event, timing, config.
141
- * @param action string Event action or UA for config.
142
- * @param fieldsArray object The configuration object.
143
- * @param valuesArray object The values for the log.
144
- * @private
145
- */
146
- function __gtagTrackerSend( type, action, fieldsArray, valuesArray ) {
147
- type = typeof type !== 'undefined' ? type : 'event';
148
- action = typeof action !== 'undefined' ? action : '';
149
- valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
150
- fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {};
151
-
152
- __gtagTracker( type, action, fieldsArray );
153
-
154
- lastClicked.valuesArray = valuesArray;
155
- lastClicked.fieldsArray = fieldsArray;
156
- lastClicked.fieldsArray.event_action = action;
157
- lastClicked.tracked = true;
158
- __gtagTrackerLog( 'Tracked: ' + valuesArray.type );
159
- __gtagTrackerLog( lastClicked );
160
- }
161
-
162
- function __gtagTrackerNotSend( valuesArray ) {
163
- valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
164
-
165
- lastClicked.valuesArray = valuesArray;
166
- lastClicked.fieldsArray = [];
167
- lastClicked.tracked = false;
168
- __gtagTrackerLog( 'Not Tracked: ' + valuesArray.exit );
169
- __gtagTrackerLog( lastClicked );
170
- }
171
-
172
- function __gtagTrackerLog( message ) {
173
- if ( __gtagTrackerIsDebug() ) {
174
- console.dir( message );
175
- }
176
- }
177
-
178
- function __gtagTrackerStringTrim( x ) {
179
- return x.replace( /^\s+|\s+$/gm, '' );
180
- }
181
-
182
- function __gtagTrackerGetDomain() {
183
- var i = 0, currentdomain = document.domain, p = currentdomain.split( '.' ), s = '_gd' + (
184
- new Date()
185
- ).getTime();
186
- while ( i < ( p.length - 1 ) && document.cookie.indexOf( s + '=' + s ) == - 1 ) {
187
- currentdomain = p.slice( - 1 - (
188
- ++ i
189
- ) ).join( '.' );
190
- document.cookie = s + "=" + s + ";domain=" + currentdomain + ";";
191
- }
192
- document.cookie = s + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;domain=" + currentdomain + ";";
193
- return currentdomain;
194
- }
195
-
196
- function __gtagTrackerGetExtension( extension ) {
197
- extension = extension.toString();
198
- extension = extension.substring( 0, (
199
- extension.indexOf( "#" ) == - 1
200
- ) ? extension.length : extension.indexOf( "#" ) ); /* Remove the anchor at the end, if there is one */
201
- extension = extension.substring( 0, (
202
- extension.indexOf( "?" ) == - 1
203
- ) ? extension.length : extension.indexOf( "?" ) ); /* Remove the query after the file name, if there is one */
204
- extension = extension.substring( extension.lastIndexOf( "/" ) + 1, extension.length ); /* Remove everything before the last slash in the path */
205
- if ( extension.length > 0 && extension.indexOf( '.' ) !== - 1 ) { /* If there's a period left in the URL, then there's a extension. Else it is not a extension. */
206
- extension = extension.substring( extension.indexOf( "." ) + 1 ); /* Remove everything but what's after the first period */
207
- return extension;
208
- } else {
209
- return "";
210
- }
211
- }
212
-
213
- function __gtagTrackerTrackedClick( event ) {
214
- return event.which == 1 || event.which == 2 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
215
- }
216
-
217
- function __gtagTrackerGetDownloadExtensions() {
218
- var download_extensions = [];
219
- if ( typeof exactmetrics_frontend.download_extensions == 'string' ) {
220
- download_extensions = exactmetrics_frontend.download_extensions.split( "," );
221
- }
222
- return download_extensions;
223
- }
224
-
225
- function __gtagTrackerGetInboundPaths() {
226
- var inbound_paths = [];
227
- if ( typeof exactmetrics_frontend.inbound_paths == 'string' ) {
228
- inbound_paths = JSON.parse( exactmetrics_frontend.inbound_paths );
229
- }
230
-
231
- return inbound_paths;
232
- }
233
-
234
- function __gtagTrackerTrackedClickType( event ) {
235
- if ( event.which == 1 ) {
236
- return 'event.which=1';
237
- } else if ( event.which == 2 ) {
238
- return 'event.which=2';
239
- } else if ( event.metaKey ) {
240
- return 'metaKey';
241
- } else if ( event.ctrlKey ) {
242
- return 'ctrlKey';
243
- } else if ( event.shiftKey ) {
244
- return 'shiftKey';
245
- } else if ( event.altKey ) {
246
- return 'altKey';
247
- } else {
248
- return '';
249
- }
250
- }
251
-
252
- function __gtagTrackerLinkType( el ) {
253
- var download_extensions = __gtagTrackerGetDownloadExtensions();
254
- var inbound_paths = __gtagTrackerGetInboundPaths();
255
- var type = 'unknown';
256
- var link = el.href;
257
- var extension = __gtagTrackerGetExtension( el.href );
258
- var currentdomain = __gtagTrackerGetDomain();
259
- var hostname = el.hostname;
260
- var protocol = el.protocol;
261
- var pathname = el.pathname;
262
- link = link.toString();
263
- var index, len;
264
- var category = el.getAttribute( "data-vars-ga-category" );
265
-
266
- if ( category ) {
267
- return category;
268
- }
269
-
270
- if ( link.match( /^javascript\:/i ) ) {
271
- type = 'internal'; /* if it's a JS link, it's internal */
272
- } else if ( protocol && protocol.length > 0 && (
273
- __gtagTrackerStringTrim( protocol ) == 'tel' || __gtagTrackerStringTrim( protocol ) == 'tel:'
274
- ) ) { /* If it's a telephone link */
275
- type = "tel";
276
- } else if ( protocol && protocol.length > 0 && (
277
- __gtagTrackerStringTrim( protocol ) == 'mailto' || __gtagTrackerStringTrim( protocol ) == 'mailto:'
278
- ) ) { /* If it's a email */
279
- type = "mailto";
280
- } else if ( hostname && currentdomain && hostname.length > 0 && currentdomain.length > 0 && !hostname.endsWith( '.' + currentdomain ) && hostname !== currentdomain ) { /* If it's a outbound */
281
- type = "external";
282
- } else if ( pathname && JSON.stringify( inbound_paths ) != "{}" && pathname.length > 0 ) { /* If it's an internal as outbound */
283
- var inbound_paths_length = inbound_paths.length;
284
- for ( var inbound_paths_index = 0; inbound_paths_index < inbound_paths_length; inbound_paths_index ++ ) {
285
- if ( inbound_paths[inbound_paths_index].path && inbound_paths[inbound_paths_index].label && inbound_paths[inbound_paths_index].path.length > 0 && inbound_paths[inbound_paths_index].label.length > 0 && pathname.startsWith( inbound_paths[inbound_paths_index].path ) ) {
286
- type = "internal-as-outbound";
287
- internalAsOutboundCategory = "outbound-link-" + inbound_paths[inbound_paths_index].label;
288
- break;
289
- }
290
- }
291
- /* Enable window.exactmetrics_experimental_mode at your own risk. We might eventually remove it. Also you may/can/will burn through GA quota for your property quickly. */
292
- } else if ( hostname && window.exactmetrics_experimental_mode && hostname.length > 0 && document.domain.length > 0 && hostname !== document.domain ) { /* If it's a cross-hostname link */
293
- type = "cross-hostname";
294
- }
295
-
296
- if ( extension && (
297
- type === 'unknown' || 'external' === type
298
- ) && download_extensions.length > 0 && extension.length > 0 ) { /* If it's a download */
299
- for ( index = 0, len = download_extensions.length; index < len; ++ index ) {
300
- if ( download_extensions[index].length > 0 && (
301
- link.endsWith( download_extensions[index] ) || download_extensions[index] == extension
302
- ) ) {
303
- type = "download";
304
- break;
305
- }
306
- }
307
- }
308
-
309
- if ( type === 'unknown' ) {
310
- type = 'internal';
311
- }
312
- return type;
313
- }
314
-
315
- function __gtagTrackerLinkTarget( el, event ) {
316
-
317
- /* Is actual target set and not _(self|parent|top)? */
318
- var target = (
319
- el.target && !el.target.match( /^_(self|parent|top)$/i )
320
- ) ? el.target : false;
321
-
322
- /* Assume a target if Ctrl|shift|meta-click */
323
- if ( event.ctrlKey || event.shiftKey || event.metaKey || event.which == 2 ) {
324
- target = "_blank";
325
- }
326
- return target;
327
- }
328
-
329
- function __gtagTrackerGetTitle( el ) {
330
- if ( el.getAttribute( "data-vars-ga-label" ) && el.getAttribute( "data-vars-ga-label" ).replace( /\n/ig, '' ) ) {
331
- return el.getAttribute( "data-vars-ga-label" ).replace( /\n/ig, '' );
332
- } else if ( el.title && el.title.replace( /\n/ig, '' ) ) {
333
- return el.title.replace( /\n/ig, '' );
334
- } else if ( el.innerText && el.innerText.replace( /\n/ig, '' ) ) {
335
- return el.innerText.replace( /\n/ig, '' );
336
- } else if ( el.getAttribute( 'aria-label' ) && el.getAttribute( 'aria-label' ).replace( /\n/ig, '' ) ) {
337
- return el.getAttribute( 'aria-label' ).replace( /\n/ig, '' );
338
- } else if ( el.alt && el.alt.replace( /\n/ig, '' ) ) {
339
- return el.alt.replace( /\n/ig, '' );
340
- } else if ( el.textContent && el.textContent.replace( /\n/ig, '' ) ) {
341
- return el.textContent.replace( /\n/ig, '' );
342
- } else {
343
- return undefined;
344
- }
345
- }
346
-
347
- function __gtagTrackerGetInnerTitle( el ) {
348
- var children = el.children;
349
- var count = 0;
350
- var child;
351
- var value;
352
- for ( var i = 0; i < children.length; i ++ ) {
353
- child = children[i];
354
- value = __gtagTrackerGetTitle( child );
355
- if ( value ) {
356
- return value;
357
- }
358
- /* max search 100 elements to ensure performance */
359
- if ( count == 99 ) {
360
- return undefined;
361
- }
362
- count ++;
363
- }
364
- return undefined;
365
- }
366
-
367
- function __gtagTrackerClickEvent( event ) {
368
- var el = event.srcElement || event.target;
369
- var valuesArray = [];
370
- var fieldsArray;
371
-
372
- /* Start Values Array */
373
- valuesArray.el = el;
374
- valuesArray.click_type = __gtagTrackerTrackedClickType( event );
375
-
376
- /* If GA is blocked or not loaded, or not main|middle|touch click then don't track */
377
- if ( 'undefined' === typeof __gtagTracker || ! __gtagTrackerTrackedClick( event ) ) {
378
- valuesArray.exit = 'loaded';
379
- __gtagTrackerNotSend( valuesArray );
380
- return;
381
- }
382
-
383
- /* Loop up the DOM tree through parent elements if clicked element is not a link (eg: an image inside a link) */
384
- while ( el && (
385
- typeof el.tagName == 'undefined' || el.tagName.toLowerCase() != 'a' || !el.href
386
- ) ) {
387
- el = el.parentNode;
388
- }
389
-
390
- /* if a link with valid href has been clicked */
391
- if ( el && el.href && !el.hasAttribute( 'xlink:href' ) ) {
392
- var link = el.href; /* What link are we tracking */
393
- var extension = __gtagTrackerGetExtension( el.href ); /* What extension is this link */
394
- var download_extensions = __gtagTrackerGetDownloadExtensions(); /* Let's get the extensions to track */
395
- var inbound_paths = __gtagTrackerGetInboundPaths(); /* Let's get the internal paths to track */
396
- var home_url = exactmetrics_frontend.home_url; /* Let's get the url to compare for external/internal use */
397
- var currentdomain = __gtagTrackerGetDomain(); /* What domain are we on? */
398
- var type = __gtagTrackerLinkType( el ); /* What type of link is this? */
399
- var target = __gtagTrackerLinkTarget( el, event ); /* Is a new tab/window being opened? */
400
- var action = el.getAttribute( "data-vars-ga-action" );
401
- var label = el.getAttribute( "data-vars-ga-label" );
402
-
403
- /* Element */
404
- valuesArray.el = el; /* el is an a element so we can parse it */
405
- valuesArray.el_href = el.href; /* "http://example.com:3000/pathname/?search=test#hash" */
406
- valuesArray.el_protocol = el.protocol; /* "http:" */
407
- valuesArray.el_hostname = el.hostname; /* "example.com" */
408
- valuesArray.el_port = el.port; /* "3000" */
409
- valuesArray.el_pathname = el.pathname; /* "/pathname/" */
410
- valuesArray.el_search = el.search; /* "?search=test" */
411
- valuesArray.el_hash = el.hash; /* "#hash" */
412
- valuesArray.el_host = el.host; /* "example.com:3000" */
413
- valuesArray.el_classes = el.getAttribute('class')
414
- valuesArray.el_id = el.id
415
-
416
- /* Settings */
417
- valuesArray.debug_mode = __gtagTrackerIsDebug(); /* "example.com:3000" */
418
- valuesArray.download_extensions = download_extensions; /* Let's get the extensions to track */
419
- valuesArray.inbound_paths = inbound_paths; /* Let's get the internal paths to track */
420
- valuesArray.home_url = home_url; /* Let's get the url to compare for external/internal use */
421
-
422
- /* Parsed/Logic */
423
- valuesArray.link = link; /* What link are we tracking */
424
- valuesArray.extension = extension; /* What extension is this link */
425
- valuesArray.type = type; /* What type of link is this */
426
- valuesArray.target = target; /* Is a new tab/window being opened? */
427
- valuesArray.title = __gtagTrackerGetTitle( el ); /* Try link title, then text content */
428
-
429
- /* only find innerTitle if we need one */
430
- if ( ! valuesArray.label && !valuesArray.title ) {
431
- valuesArray.title = __gtagTrackerGetInnerTitle( el );
432
- }
433
-
434
- /* Let's track everything but internals (that aren't internal-as-externals) and javascript */
435
- if ( type !== 'internal' && type !== 'javascript' ) {
436
-
437
- var __gtagTrackerHitBackRun = false; /* Tracker has not yet run */
438
-
439
- /* HitCallback to open link in same window after tracker */
440
- var __gtagTrackerHitBack = function () {
441
- /* Run the hitback only once */
442
- if ( __gtagTrackerHitBackRun ) {
443
- return;
444
- }
445
- maybePreventBeforeUnload();
446
- __gtagTrackerHitBackRun = true;
447
- window.location.href = link;
448
- };
449
-
450
- var __gtagTrackerNoRedirectExternal = function () {
451
- valuesArray.exit = 'external';
452
- __gtagTrackerNotSend( valuesArray );
453
- };
454
-
455
- var __gtagTrackerNoRedirectInboundAsExternal = function () {
456
- valuesArray.exit = 'internal-as-outbound';
457
- __gtagTrackerNotSend( valuesArray );
458
- };
459
- var __gtagTrackerNoRedirectCrossHostname = function () {
460
- valuesArray.exit = 'cross-hostname';
461
- __gtagTrackerNotSend( valuesArray );
462
- };
463
-
464
- if ( target || type == 'mailto' || type == 'tel' ) { /* If target opens a new window then just track */
465
- if ( type == 'download' ) {
466
- fieldsArray = {
467
- event_category: 'download',
468
- event_label: label || valuesArray.title,
469
- file_extension: valuesArray.extension,
470
- file_name: valuesArray.link.replace(/^.*\//g, ''),
471
- link_text: label || valuesArray.title,
472
- link_url: link,
473
- link_domain: valuesArray.el_hostname,
474
- link_classes: valuesArray.el_classes,
475
- link_id: valuesArray.el_id,
476
- };
477
- } else if ( type == 'tel' ) {
478
- fieldsArray = {
479
- event_category: 'tel',
480
- event_label: label || valuesArray.title.replace( 'tel:', '' ),
481
- tel_number: link.replace( 'tel:', '' ),
482
- link_text: label || valuesArray.title,
483
- link_url: link,
484
- link_classes: valuesArray.el_classes,
485
- link_id: valuesArray.el_id,
486
- };
487
- } else if ( type == 'mailto' ) {
488
- fieldsArray = {
489
- event_category: 'mailto',
490
- event_label: label || valuesArray.title.replace( 'mailto:', '' ),
491
- email_address: link.replace( 'mailto:', '' ),
492
- link_text: label || valuesArray.title.replace( 'mailto:', ''),
493
- link_url: link,
494
- link_classes: valuesArray.el_classes,
495
- link_id: valuesArray.el_id,
496
- };
497
- } else if ( type == 'internal-as-outbound' ) {
498
- fieldsArray = {
499
- event_category: internalAsOutboundCategory,
500
- event_label: label || valuesArray.title,
501
- event_name: 'click',
502
- is_affiliate_link: true,
503
- affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''),
504
- link_text: label || valuesArray.title,
505
- link_url: link,
506
- link_domain: valuesArray.el_hostname,
507
- link_classes: valuesArray.el_classes,
508
- link_id: valuesArray.el_id,
509
- outbound: true,
510
- };
511
- } else if ( type == 'external' ) {
512
- fieldsArray = {
513
- event_category: 'outbound-link',
514
- event_label: label || valuesArray.title,
515
- is_affiliate_link: false,
516
- link_text: label || valuesArray.title,
517
- link_url: link,
518
- link_domain: valuesArray.el_hostname,
519
- link_classes: valuesArray.el_classes,
520
- link_id: valuesArray.el_id,
521
- outbound: true,
522
- };
523
- } else if ( type == 'cross-hostname' ) {
524
- fieldsArray = {
525
- event_category: 'cross-hostname',
526
- event_label: label || valuesArray.title,
527
- link_text: label || valuesArray.title,
528
- link_url: link,
529
- link_domain: valuesArray.el_hostname,
530
- link_classes: valuesArray.el_classes,
531
- link_id: valuesArray.el_id,
532
- };
533
- }
534
-
535
- if ( fieldsArray ) {
536
- __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
537
- } else {
538
- if ( type && type != 'internal' ) {
539
- fieldsArray = {
540
- event_category: type,
541
- event_label: label || valuesArray.title,
542
- link_text: label || valuesArray.title,
543
- link_url: link,
544
- link_domain: valuesArray.el_hostname,
545
- link_classes: valuesArray.el_classes,
546
- link_id: valuesArray.el_id,
547
- };
548
-
549
- __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
550
- } else {
551
- valuesArray.exit = 'type';
552
- __gtagTrackerNotSend( valuesArray );
553
- }
554
- }
555
- } else {
556
- /* Prevent standard click, track then open */
557
- if ( type != 'cross-hostname' && type != 'external' && type != 'internal-as-outbound' ) {
558
- if ( !event.defaultPrevented ) {
559
- if ( event.preventDefault ) {
560
- event.preventDefault();
561
- } else {
562
- event.returnValue = false;
563
- }
564
- }
565
- }
566
-
567
- if ( type == 'download' ) {
568
- fieldsArray = {
569
- event_category: 'download',
570
- event_label: label || valuesArray.title,
571
- event_callback: __gtagTrackerHitBack,
572
- file_extension: valuesArray.extension,
573
- file_name: valuesArray.link.replace(/^.*\//g, ''),
574
- link_text: label || valuesArray.title,
575
- link_url: link,
576
- link_domain: valuesArray.el_hostname,
577
- link_classes: valuesArray.el_classes,
578
- link_id: valuesArray.el_id,
579
- };
580
-
581
- __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
582
- } else if ( type == 'internal-as-outbound' ) {
583
- beforeUnloadChanged = true;
584
- window.onbeforeunload = function ( e ) {
585
- if ( !event.defaultPrevented ) {
586
- if ( event.preventDefault ) {
587
- event.preventDefault();
588
- } else {
589
- event.returnValue = false;
590
- }
591
- }
592
-
593
- fieldsArray = {
594
- event_category: internalAsOutboundCategory,
595
- event_label: label || valuesArray.title,
596
- event_callback: __gtagTrackerHitBack,
597
- is_affiliate_link: true,
598
- affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''),
599
- link_text: label || valuesArray.title,
600
- link_url: link,
601
- link_domain: valuesArray.el_hostname,
602
- link_classes: valuesArray.el_classes,
603
- link_id: valuesArray.el_id,
604
- outbound: true,
605
- };
606
-
607
- if ( navigator.sendBeacon ) {
608
- fieldsArray.transport = 'beacon';
609
- }
610
-
611
- __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
612
-
613
- setTimeout( __gtagTrackerHitBack, 1000 );
614
- };
615
- } else if ( type == 'external' ) {
616
- beforeUnloadChanged = true;
617
- window.onbeforeunload = function ( e ) {
618
- if ( !event.defaultPrevented ) {
619
- if ( event.preventDefault ) {
620
- event.preventDefault();
621
- } else {
622
- event.returnValue = false;
623
- }
624
- }
625
-
626
- fieldsArray = {
627
- event_category: 'outbound-link',
628
- event_label: label || valuesArray.title,
629
- event_callback: __gtagTrackerHitBack,
630
- is_affiliate_link: false,
631
- link_text: label || valuesArray.title,
632
- link_url: link,
633
- link_domain: valuesArray.el_hostname,
634
- link_classes: valuesArray.el_classes,
635
- link_id: valuesArray.el_id,
636
- outbound: true,
637
- };
638
-
639
- if ( navigator.sendBeacon ) {
640
- fieldsArray.transport = 'beacon';
641
- }
642
-
643
- __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
644
- setTimeout( __gtagTrackerHitBack, 1000 );
645
- };
646
- } else if ( type == 'cross-hostname' ) {
647
- beforeUnloadChanged = true;
648
- window.onbeforeunload = function ( e ) {
649
- if ( !event.defaultPrevented ) {
650
- if ( event.preventDefault ) {
651
- event.preventDefault();
652
- } else {
653
- event.returnValue = false;
654
- }
655
- }
656
-
657
- fieldsArray = {
658
- event_category: 'cross-hostname',
659
- event_label: label || valuesArray.title,
660
- event_callback: __gtagTrackerHitBack,
661
- link_text: label || valuesArray.title,
662
- link_url: link,
663
- link_domain: valuesArray.el_hostname,
664
- link_classes: valuesArray.el_classes,
665
- link_id: valuesArray.el_id,
666
- };
667
-
668
- if ( navigator.sendBeacon ) {
669
- fieldsArray.transport = 'beacon';
670
- }
671
-
672
- __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
673
- setTimeout( __gtagTrackerHitBack, 1000 );
674
- };
675
- } else {
676
- if ( type && type !== 'internal' ) {
677
- fieldsArray = {
678
- event_category: type,
679
- event_label: label || valuesArray.title,
680
- event_callback: __gtagTrackerHitBack,
681
- link_text: label || valuesArray.title,
682
- link_url: link,
683
- link_domain: valuesArray.el_hostname,
684
- link_classes: valuesArray.el_classes,
685
- link_id: valuesArray.el_id,
686
- };
687
-
688
- __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
689
- } else {
690
- valuesArray.exit = 'type';
691
- __gtagTrackerNotSend( valuesArray );
692
- }
693
- }
694
-
695
- if ( type != 'external' && type != 'cross-hostname' && type != 'internal-as-outbound' ) {
696
- /* Run event_callback again if GA takes longer than 1 second */
697
- setTimeout( __gtagTrackerHitBack, 1000 );
698
- } else {
699
- if ( type == 'external' ) {
700
- setTimeout( __gtagTrackerNoRedirectExternal, 1100 );
701
- } else if ( type == 'cross-hostname' ) {
702
- setTimeout( __gtagTrackerNoRedirectCrossHostname, 1100 );
703
- } else {
704
- setTimeout( __gtagTrackerNoRedirectInboundAsExternal, 1100 );
705
- }
706
- }
707
-
708
- // Clear out the beforeunload event if it was set to avoid sending false events.
709
- setTimeout( maybePreventBeforeUnload, 100 );
710
- }
711
- } else {
712
- maybePreventBeforeUnload();
713
- valuesArray.exit = 'internal';
714
- __gtagTrackerNotSend( valuesArray );
715
- }
716
- } else {
717
- valuesArray.exit = 'notlink';
718
- __gtagTrackerNotSend( valuesArray );
719
- }
720
- }
721
-
722
- var prevHash = window.location.hash;
723
-
724
- function __gtagTrackerHashChangeEvent() {
725
- /* Todo: Ready this section for JS unit testing */
726
- if ( exactmetrics_frontend.hash_tracking === "true" && prevHash != window.location.hash && ( exactmetrics_frontend.ua || exactmetrics_frontend.v4_id ) ) {
727
- prevHash = window.location.hash;
728
- if ( exactmetrics_frontend.ua ) {
729
- __gtagTracker( 'config', exactmetrics_frontend.ua, {
730
- page_path: location.pathname + location.search + location.hash,
731
- } );
732
- }
733
-
734
- if ( exactmetrics_frontend.v4_id ) {
735
- __gtagTracker( 'config', exactmetrics_frontend.v4_id, {
736
- page_path: location.pathname + location.search + location.hash,
737
- } );
738
- }
739
- __gtagTrackerLog( "Hash change to: " + location.pathname + location.search + location.hash );
740
- } else {
741
- __gtagTrackerLog( "Hash change to (untracked): " + location.pathname + location.search + location.hash );
742
- }
743
- }
744
-
745
- function maybePreventBeforeUnload() {
746
- if ( beforeUnloadChanged ) {
747
- window.onbeforeunload = null;
748
- }
749
- }
750
-
751
- /* Attach the event to all clicks in the document after page has loaded */
752
- var __gtagTrackerWindow = window;
753
- if ( __gtagTrackerWindow.addEventListener ) {
754
- __gtagTrackerWindow.addEventListener(
755
- "load",
756
- function () {
757
- document.body.addEventListener(
758
- "click",
759
- __gtagTrackerClickEvent,
760
- false
761
- );
762
- },
763
- false
764
- );
765
- window.addEventListener( "hashchange", __gtagTrackerHashChangeEvent, false );
766
- } else {
767
- if ( __gtagTrackerWindow.attachEvent ) {
768
- __gtagTrackerWindow.attachEvent(
769
- "onload",
770
- function () {
771
- document.body.attachEvent( "onclick", __gtagTrackerClickEvent );
772
- }
773
- );
774
- window.attachEvent( "onhashchange", __gtagTrackerHashChangeEvent );
775
- }
776
- }
777
-
778
- if ( typeof String.prototype.endsWith !== 'function' ) {
779
- String.prototype.endsWith = function ( suffix ) {
780
- return this.indexOf( suffix, this.length - suffix.length ) !== - 1;
781
- };
782
- }
783
- if ( typeof String.prototype.startsWith !== 'function' ) {
784
- String.prototype.startsWith = function ( prefix ) {
785
- return this.indexOf( prefix ) === 0;
786
- };
787
- }
788
-
789
- if ( typeof Array.prototype.lastIndexOf !== 'function' ) {
790
- Array.prototype.lastIndexOf = function ( searchElement /*, fromIndex*/ ) {
791
- 'use strict';
792
-
793
- if ( this === void 0 || this === null ) {
794
- throw new TypeError();
795
- }
796
-
797
- var n, k,
798
- t = Object( this ),
799
- len = t.length >>> 0; /* jshint ignore:line */
800
- if ( len === 0 ) {
801
- return - 1;
802
- }
803
-
804
- n = len - 1;
805
- if ( arguments.length > 1 ) {
806
- n = Number( arguments[1] );
807
- if ( n != n ) {
808
- n = 0;
809
- } else if ( n != 0 && n != (
810
- 1 / 0
811
- ) && n != - (
812
- 1 / 0
813
- ) ) { /* jshint ignore:line */
814
- n = (
815
- n > 0 || - 1
816
- ) * Math.floor( Math.abs( n ) );
817
- }
818
- }
819
-
820
- for ( k = n >= 0 ? Math.min( n, len - 1 ) : len - Math.abs( n ); k >= 0; k -- ) {
821
- if ( k in t && t[k] === searchElement ) {
822
- return k;
823
- }
824
- }
825
- return - 1;
826
- };
827
- }
828
- };
829
- var ExactMetricsObject = new ExactMetrics();
1
+ /**
2
+ * Developer's Notice:
3
+ *
4
+ * Note: JS in this file (and this file itself) is not guaranteed backwards compatibility. JS can be added, changed or removed at any time without notice.
5
+ * For more information see the `Backwards Compatibility Guidelines for Developers` section of the README.md file.
6
+ */
7
+ /**
8
+ * Handles:
9
+ * - JS Events handling
10
+ *
11
+ * @since 7.15.0
12
+ */
13
+ var ExactMetrics = function () {
14
+ /* ExactMetrics JS events tracking works on all major browsers, including IE starting at IE 7, via polyfills for any major JS function used that
15
+ is not supported by at least 95% of the global and/or US browser marketshare. Currently, IE 7 & 8 which as of 2/14/17 have under 0.25% global marketshare, require
16
+ us to polyfill Array.prototype.lastIndexOf, and if they continue to drop, we might remove this polyfill at some point. In that case note that events tracking
17
+ for IE 7/8 will continue to work, with the exception of events tracking of downloads. */
18
+ var lastClicked = [];
19
+ var internalAsOutboundCategory = '';
20
+ var beforeUnloadChanged = false;
21
+
22
+ this.setLastClicked = function ( valuesArray, fieldsArray, tracked ) {
23
+ valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
24
+ fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : [];
25
+ tracked = typeof tracked !== 'undefined' ? tracked : false;
26
+
27
+ lastClicked.valuesArray = valuesArray;
28
+ lastClicked.fieldsArray = fieldsArray;
29
+ };
30
+
31
+ this.getLastClicked = function () {
32
+ return lastClicked;
33
+ };
34
+
35
+ this.setInternalAsOutboundCategory = function ( category ) {
36
+ internalAsOutboundCategory = category;
37
+ };
38
+
39
+ this.getInternalAsOutboundCategory = function () {
40
+ return internalAsOutboundCategory;
41
+ };
42
+
43
+ this.sendEvent = function ( type, action, fieldsArray ) {
44
+ __gtagTrackerSend( type, action, fieldsArray, [] );
45
+ };
46
+
47
+ function __gtagTrackerIsDebug() {
48
+ if ( window.exactmetrics_debug_mode ) {
49
+ return true;
50
+ } else {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ function cloneFields( fields, allowedKeys, disallowedKeys ) {
56
+ var clone = {};
57
+
58
+ for ( var key in fields ) {
59
+ if ( ! fields.hasOwnProperty( key ) ) {
60
+ continue
61
+ }
62
+
63
+ if ( allowedKeys && allowedKeys.indexOf( key ) === -1 ) {
64
+ continue
65
+ }
66
+
67
+ if ( disallowedKeys && disallowedKeys.indexOf( key ) > -1 ) {
68
+ continue
69
+ }
70
+
71
+ clone[ key ] = fields[ key ];
72
+ }
73
+
74
+ return clone;
75
+ }
76
+
77
+ function __gtagMaybeTrackerV4( type, action, fieldsArray ) {
78
+ if ( ! exactmetrics_frontend.v4_id || type !== 'event' ) {
79
+ return;
80
+ }
81
+
82
+ var eventCategory = fieldsArray.event_category || '';
83
+
84
+ var fieldsToRemove = [
85
+ 'event_name',
86
+ 'event_category',
87
+ 'event_label',
88
+ 'value',
89
+ ];
90
+
91
+ var fields = cloneFields( fieldsArray, null, fieldsToRemove );
92
+ fields.action = action;
93
+
94
+ var eventMap = {
95
+ 'outbound-link': 'click',
96
+ 'download': 'file_download',
97
+ };
98
+
99
+ __gtagTracker( type, eventMap[ eventCategory ] || eventCategory.replace( '-', '_' ), fields );
100
+ }
101
+
102
+ function __gtagMaybeTrackerUA( type, action, fieldsArray ) {
103
+ if ( ! exactmetrics_frontend.ua ) {
104
+ return;
105
+ }
106
+
107
+ var allowedFields = [
108
+ 'event_category',
109
+ 'event_label',
110
+ 'value',
111
+ ];
112
+
113
+ var uaFields = cloneFields(fieldsArray, allowedFields);
114
+ uaFields.send_to = exactmetrics_frontend.ua
115
+
116
+ __gtagTracker( type, action, uaFields );
117
+ }
118
+
119
+ function __gtagTrackerSendDual( type, action, fieldsArray, valuesArray ) {
120
+ type = typeof type !== 'undefined' ? type : 'event';
121
+ action = typeof action !== 'undefined' ? action : '';
122
+ valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
123
+ fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {};
124
+
125
+ __gtagMaybeTrackerUA( type, action, fieldsArray );
126
+ __gtagMaybeTrackerV4( type, action, fieldsArray );
127
+
128
+ lastClicked.valuesArray = valuesArray;
129
+ lastClicked.fieldsArray = fieldsArray;
130
+ lastClicked.fieldsArray.event_action = action;
131
+ lastClicked.tracked = true;
132
+ __gtagTrackerLog( 'Tracked: ' + valuesArray.type );
133
+ __gtagTrackerLog( lastClicked );
134
+ }
135
+
136
+ /**
137
+ * This attempts to be compatible with the gtag function.
138
+ *
139
+ * @see https://developers.google.com/analytics/devguides/collection/gtagjs
140
+ * @param type string Type of request, event, timing, config.
141
+ * @param action string Event action or UA for config.
142
+ * @param fieldsArray object The configuration object.
143
+ * @param valuesArray object The values for the log.
144
+ * @private
145
+ */
146
+ function __gtagTrackerSend( type, action, fieldsArray, valuesArray ) {
147
+ type = typeof type !== 'undefined' ? type : 'event';
148
+ action = typeof action !== 'undefined' ? action : '';
149
+ valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
150
+ fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {};
151
+
152
+ __gtagTracker( type, action, fieldsArray );
153
+
154
+ lastClicked.valuesArray = valuesArray;
155
+ lastClicked.fieldsArray = fieldsArray;
156
+ lastClicked.fieldsArray.event_action = action;
157
+ lastClicked.tracked = true;
158
+ __gtagTrackerLog( 'Tracked: ' + valuesArray.type );
159
+ __gtagTrackerLog( lastClicked );
160
+ }
161
+
162
+ function __gtagTrackerNotSend( valuesArray ) {
163
+ valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
164
+
165
+ lastClicked.valuesArray = valuesArray;
166
+ lastClicked.fieldsArray = [];
167
+ lastClicked.tracked = false;
168
+ __gtagTrackerLog( 'Not Tracked: ' + valuesArray.exit );
169
+ __gtagTrackerLog( lastClicked );
170
+ }
171
+
172
+ function __gtagTrackerLog( message ) {
173
+ if ( __gtagTrackerIsDebug() ) {
174
+ console.dir( message );
175
+ }
176
+ }
177
+
178
+ function __gtagTrackerStringTrim( x ) {
179
+ return x.replace( /^\s+|\s+$/gm, '' );
180
+ }
181
+
182
+ function __gtagTrackerGetDomain() {
183
+ var i = 0, currentdomain = document.domain, p = currentdomain.split( '.' ), s = '_gd' + (
184
+ new Date()
185
+ ).getTime();
186
+ while ( i < ( p.length - 1 ) && document.cookie.indexOf( s + '=' + s ) == - 1 ) {
187
+ currentdomain = p.slice( - 1 - (
188
+ ++ i
189
+ ) ).join( '.' );
190
+ document.cookie = s + "=" + s + ";domain=" + currentdomain + ";";
191
+ }
192
+ document.cookie = s + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;domain=" + currentdomain + ";";
193
+ return currentdomain;
194
+ }
195
+
196
+ function __gtagTrackerGetExtension( extension ) {
197
+ extension = extension.toString();
198
+ extension = extension.substring( 0, (
199
+ extension.indexOf( "#" ) == - 1
200
+ ) ? extension.length : extension.indexOf( "#" ) ); /* Remove the anchor at the end, if there is one */
201
+ extension = extension.substring( 0, (
202
+ extension.indexOf( "?" ) == - 1
203
+ ) ? extension.length : extension.indexOf( "?" ) ); /* Remove the query after the file name, if there is one */
204
+ extension = extension.substring( extension.lastIndexOf( "/" ) + 1, extension.length ); /* Remove everything before the last slash in the path */
205
+ if ( extension.length > 0 && extension.indexOf( '.' ) !== - 1 ) { /* If there's a period left in the URL, then there's a extension. Else it is not a extension. */
206
+ extension = extension.substring( extension.indexOf( "." ) + 1 ); /* Remove everything but what's after the first period */
207
+ return extension;
208
+ } else {
209
+ return "";
210
+ }
211
+ }
212
+
213
+ function __gtagTrackerTrackedClick( event ) {
214
+ return event.which == 1 || event.which == 2 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
215
+ }
216
+
217
+ function __gtagTrackerGetDownloadExtensions() {
218
+ var download_extensions = [];
219
+ if ( typeof exactmetrics_frontend.download_extensions == 'string' ) {
220
+ download_extensions = exactmetrics_frontend.download_extensions.split( "," );
221
+ }
222
+ return download_extensions;
223
+ }
224
+
225
+ function __gtagTrackerGetInboundPaths() {
226
+ var inbound_paths = [];
227
+ if ( typeof exactmetrics_frontend.inbound_paths == 'string' ) {
228
+ inbound_paths = JSON.parse( exactmetrics_frontend.inbound_paths );
229
+ }
230
+
231
+ return inbound_paths;
232
+ }
233
+
234
+ function __gtagTrackerTrackedClickType( event ) {
235
+ if ( event.which == 1 ) {
236
+ return 'event.which=1';
237
+ } else if ( event.which == 2 ) {
238
+ return 'event.which=2';
239
+ } else if ( event.metaKey ) {
240
+ return 'metaKey';
241
+ } else if ( event.ctrlKey ) {
242
+ return 'ctrlKey';
243
+ } else if ( event.shiftKey ) {
244
+ return 'shiftKey';
245
+ } else if ( event.altKey ) {
246
+ return 'altKey';
247
+ } else {
248
+ return '';
249
+ }
250
+ }
251
+
252
+ function __gtagTrackerLinkType( el ) {
253
+ var download_extensions = __gtagTrackerGetDownloadExtensions();
254
+ var inbound_paths = __gtagTrackerGetInboundPaths();
255
+ var type = 'unknown';
256
+ var link = el.href;
257
+ var extension = __gtagTrackerGetExtension( el.href );
258
+ var currentdomain = __gtagTrackerGetDomain();
259
+ var hostname = el.hostname;
260
+ var protocol = el.protocol;
261
+ var pathname = el.pathname;
262
+ link = link.toString();
263
+ var index, len;
264
+ var category = el.getAttribute( "data-vars-ga-category" );
265
+
266
+ if ( category ) {
267
+ return category;
268
+ }
269
+
270
+ if ( link.match( /^javascript\:/i ) ) {
271
+ type = 'internal'; /* if it's a JS link, it's internal */
272
+ } else if ( protocol && protocol.length > 0 && (
273
+ __gtagTrackerStringTrim( protocol ) == 'tel' || __gtagTrackerStringTrim( protocol ) == 'tel:'
274
+ ) ) { /* If it's a telephone link */
275
+ type = "tel";
276
+ } else if ( protocol && protocol.length > 0 && (
277
+ __gtagTrackerStringTrim( protocol ) == 'mailto' || __gtagTrackerStringTrim( protocol ) == 'mailto:'
278
+ ) ) { /* If it's a email */
279
+ type = "mailto";
280
+ } else if ( hostname && currentdomain && hostname.length > 0 && currentdomain.length > 0 && !hostname.endsWith( '.' + currentdomain ) && hostname !== currentdomain ) { /* If it's a outbound */
281
+ type = "external";
282
+ } else if ( pathname && JSON.stringify( inbound_paths ) != "{}" && pathname.length > 0 ) { /* If it's an internal as outbound */
283
+ var inbound_paths_length = inbound_paths.length;
284
+ for ( var inbound_paths_index = 0; inbound_paths_index < inbound_paths_length; inbound_paths_index ++ ) {
285
+ if ( inbound_paths[inbound_paths_index].path && inbound_paths[inbound_paths_index].label && inbound_paths[inbound_paths_index].path.length > 0 && inbound_paths[inbound_paths_index].label.length > 0 && pathname.startsWith( inbound_paths[inbound_paths_index].path ) ) {
286
+ type = "internal-as-outbound";
287
+ internalAsOutboundCategory = "outbound-link-" + inbound_paths[inbound_paths_index].label;
288
+ break;
289
+ }
290
+ }
291
+ /* Enable window.exactmetrics_experimental_mode at your own risk. We might eventually remove it. Also you may/can/will burn through GA quota for your property quickly. */
292
+ } else if ( hostname && window.exactmetrics_experimental_mode && hostname.length > 0 && document.domain.length > 0 && hostname !== document.domain ) { /* If it's a cross-hostname link */
293
+ type = "cross-hostname";
294
+ }
295
+
296
+ if ( extension && (
297
+ type === 'unknown' || 'external' === type
298
+ ) && download_extensions.length > 0 && extension.length > 0 ) { /* If it's a download */
299
+ for ( index = 0, len = download_extensions.length; index < len; ++ index ) {
300
+ if ( download_extensions[index].length > 0 && (
301
+ link.endsWith( download_extensions[index] ) || download_extensions[index] == extension
302
+ ) ) {
303
+ type = "download";
304
+ break;
305
+ }
306
+ }
307
+ }
308
+
309
+ if ( type === 'unknown' ) {
310
+ type = 'internal';
311
+ }
312
+ return type;
313
+ }
314
+
315
+ function __gtagTrackerLinkTarget( el, event ) {
316
+
317
+ /* Is actual target set and not _(self|parent|top)? */
318
+ var target = (
319
+ el.target && !el.target.match( /^_(self|parent|top)$/i )
320
+ ) ? el.target : false;
321
+
322
+ /* Assume a target if Ctrl|shift|meta-click */
323
+ if ( event.ctrlKey || event.shiftKey || event.metaKey || event.which == 2 ) {
324
+ target = "_blank";
325
+ }
326
+ return target;
327
+ }
328
+
329
+ function __gtagTrackerGetTitle( el ) {
330
+ if ( el.getAttribute( "data-vars-ga-label" ) && el.getAttribute( "data-vars-ga-label" ).replace( /\n/ig, '' ) ) {
331
+ return el.getAttribute( "data-vars-ga-label" ).replace( /\n/ig, '' );
332
+ } else if ( el.title && el.title.replace( /\n/ig, '' ) ) {
333
+ return el.title.replace( /\n/ig, '' );
334
+ } else if ( el.innerText && el.innerText.replace( /\n/ig, '' ) ) {
335
+ return el.innerText.replace( /\n/ig, '' );
336
+ } else if ( el.getAttribute( 'aria-label' ) && el.getAttribute( 'aria-label' ).replace( /\n/ig, '' ) ) {
337
+ return el.getAttribute( 'aria-label' ).replace( /\n/ig, '' );
338
+ } else if ( el.alt && el.alt.replace( /\n/ig, '' ) ) {
339
+ return el.alt.replace( /\n/ig, '' );
340
+ } else if ( el.textContent && el.textContent.replace( /\n/ig, '' ) ) {
341
+ return el.textContent.replace( /\n/ig, '' );
342
+ } else {
343
+ return undefined;
344
+ }
345
+ }
346
+
347
+ function __gtagTrackerGetInnerTitle( el ) {
348
+ var children = el.children;
349
+ var count = 0;
350
+ var child;
351
+ var value;
352
+ for ( var i = 0; i < children.length; i ++ ) {
353
+ child = children[i];
354
+ value = __gtagTrackerGetTitle( child );
355
+ if ( value ) {
356
+ return value;
357
+ }
358
+ /* max search 100 elements to ensure performance */
359
+ if ( count == 99 ) {
360
+ return undefined;
361
+ }
362
+ count ++;
363
+ }
364
+ return undefined;
365
+ }
366
+
367
+ function __gtagTrackerClickEvent( event ) {
368
+ var el = event.srcElement || event.target;
369
+ var valuesArray = [];
370
+ var fieldsArray;
371
+
372
+ /* Start Values Array */
373
+ valuesArray.el = el;
374
+ valuesArray.click_type = __gtagTrackerTrackedClickType( event );
375
+
376
+ /* If GA is blocked or not loaded, or not main|middle|touch click then don't track */
377
+ if ( 'undefined' === typeof __gtagTracker || ! __gtagTrackerTrackedClick( event ) ) {
378
+ valuesArray.exit = 'loaded';
379
+ __gtagTrackerNotSend( valuesArray );
380
+ return;
381
+ }
382
+
383
+ /* Loop up the DOM tree through parent elements if clicked element is not a link (eg: an image inside a link) */
384
+ while ( el && (
385
+ typeof el.tagName == 'undefined' || el.tagName.toLowerCase() != 'a' || !el.href
386
+ ) ) {
387
+ el = el.parentNode;
388
+ }
389
+
390
+ /* if a link with valid href has been clicked */
391
+ if ( el && el.href && !el.hasAttribute( 'xlink:href' ) ) {
392
+ var link = el.href; /* What link are we tracking */
393
+ var extension = __gtagTrackerGetExtension( el.href ); /* What extension is this link */
394
+ var download_extensions = __gtagTrackerGetDownloadExtensions(); /* Let's get the extensions to track */
395
+ var inbound_paths = __gtagTrackerGetInboundPaths(); /* Let's get the internal paths to track */
396
+ var home_url = exactmetrics_frontend.home_url; /* Let's get the url to compare for external/internal use */
397
+ var currentdomain = __gtagTrackerGetDomain(); /* What domain are we on? */
398
+ var type = __gtagTrackerLinkType( el ); /* What type of link is this? */
399
+ var target = __gtagTrackerLinkTarget( el, event ); /* Is a new tab/window being opened? */
400
+ var action = el.getAttribute( "data-vars-ga-action" );
401
+ var label = el.getAttribute( "data-vars-ga-label" );
402
+
403
+ /* Element */
404
+ valuesArray.el = el; /* el is an a element so we can parse it */
405
+ valuesArray.el_href = el.href; /* "http://example.com:3000/pathname/?search=test#hash" */
406
+ valuesArray.el_protocol = el.protocol; /* "http:" */
407
+ valuesArray.el_hostname = el.hostname; /* "example.com" */
408
+ valuesArray.el_port = el.port; /* "3000" */
409
+ valuesArray.el_pathname = el.pathname; /* "/pathname/" */
410
+ valuesArray.el_search = el.search; /* "?search=test" */
411
+ valuesArray.el_hash = el.hash; /* "#hash" */
412
+ valuesArray.el_host = el.host; /* "example.com:3000" */
413
+ valuesArray.el_classes = el.getAttribute('class')
414
+ valuesArray.el_id = el.id
415
+
416
+ /* Settings */
417
+ valuesArray.debug_mode = __gtagTrackerIsDebug(); /* "example.com:3000" */
418
+ valuesArray.download_extensions = download_extensions; /* Let's get the extensions to track */
419
+ valuesArray.inbound_paths = inbound_paths; /* Let's get the internal paths to track */
420
+ valuesArray.home_url = home_url; /* Let's get the url to compare for external/internal use */
421
+
422
+ /* Parsed/Logic */
423
+ valuesArray.link = link; /* What link are we tracking */
424
+ valuesArray.extension = extension; /* What extension is this link */
425
+ valuesArray.type = type; /* What type of link is this */
426
+ valuesArray.target = target; /* Is a new tab/window being opened? */
427
+ valuesArray.title = __gtagTrackerGetTitle( el ); /* Try link title, then text content */
428
+
429
+ /* only find innerTitle if we need one */
430
+ if ( ! valuesArray.label && !valuesArray.title ) {
431
+ valuesArray.title = __gtagTrackerGetInnerTitle( el );
432
+ }
433
+
434
+ /* Let's track everything but internals (that aren't internal-as-externals) and javascript */
435
+ if ( type !== 'internal' && type !== 'javascript' ) {
436
+
437
+ var __gtagTrackerHitBackRun = false; /* Tracker has not yet run */
438
+
439
+ /* HitCallback to open link in same window after tracker */
440
+ var __gtagTrackerHitBack = function () {
441
+ /* Run the hitback only once */
442
+ if ( __gtagTrackerHitBackRun ) {
443
+ return;
444
+ }
445
+ maybePreventBeforeUnload();
446
+ __gtagTrackerHitBackRun = true;
447
+ window.location.href = link;
448
+ };
449
+
450
+ var __gtagTrackerNoRedirectExternal = function () {
451
+ valuesArray.exit = 'external';
452
+ __gtagTrackerNotSend( valuesArray );
453
+ };
454
+
455
+ var __gtagTrackerNoRedirectInboundAsExternal = function () {
456
+ valuesArray.exit = 'internal-as-outbound';
457
+ __gtagTrackerNotSend( valuesArray );
458
+ };
459
+ var __gtagTrackerNoRedirectCrossHostname = function () {
460
+ valuesArray.exit = 'cross-hostname';
461
+ __gtagTrackerNotSend( valuesArray );
462
+ };
463
+
464
+ if ( target || type == 'mailto' || type == 'tel' ) { /* If target opens a new window then just track */
465
+ if ( type == 'download' ) {
466
+ fieldsArray = {
467
+ event_category: 'download',
468
+ event_label: label || valuesArray.title,
469
+ file_extension: valuesArray.extension,
470
+ file_name: valuesArray.link.replace(/^.*\//g, ''),
471
+ link_text: label || valuesArray.title,
472
+ link_url: link,
473
+ link_domain: valuesArray.el_hostname,
474
+ link_classes: valuesArray.el_classes,
475
+ link_id: valuesArray.el_id,
476
+ };
477
+ } else if ( type == 'tel' ) {
478
+ fieldsArray = {
479
+ event_category: 'tel',
480
+ event_label: label || valuesArray.title.replace( 'tel:', '' ),
481
+ tel_number: link.replace( 'tel:', '' ),
482
+ link_text: label || valuesArray.title,
483
+ link_url: link,
484
+ link_classes: valuesArray.el_classes,
485
+ link_id: valuesArray.el_id,
486
+ };
487
+ } else if ( type == 'mailto' ) {
488
+ fieldsArray = {
489
+ event_category: 'mailto',
490
+ event_label: label || valuesArray.title.replace( 'mailto:', '' ),
491
+ email_address: link.replace( 'mailto:', '' ),
492
+ link_text: label || valuesArray.title.replace( 'mailto:', ''),
493
+ link_url: link,
494
+ link_classes: valuesArray.el_classes,
495
+ link_id: valuesArray.el_id,
496
+ };
497
+ } else if ( type == 'internal-as-outbound' ) {
498
+ fieldsArray = {
499
+ event_category: internalAsOutboundCategory,
500
+ event_label: label || valuesArray.title,
501
+ event_name: 'click',
502
+ is_affiliate_link: true,
503
+ affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''),
504
+ link_text: label || valuesArray.title,
505
+ link_url: link,
506
+ link_domain: valuesArray.el_hostname,
507
+ link_classes: valuesArray.el_classes,
508
+ link_id: valuesArray.el_id,
509
+ outbound: true,
510
+ };
511
+ } else if ( type == 'external' ) {
512
+ fieldsArray = {
513
+ event_category: 'outbound-link',
514
+ event_label: label || valuesArray.title,
515
+ is_affiliate_link: false,
516
+ link_text: label || valuesArray.title,
517
+ link_url: link,
518
+ link_domain: valuesArray.el_hostname,
519
+ link_classes: valuesArray.el_classes,
520
+ link_id: valuesArray.el_id,
521
+ outbound: true,
522
+ };
523
+ } else if ( type == 'cross-hostname' ) {
524
+ fieldsArray = {
525
+ event_category: 'cross-hostname',
526
+ event_label: label || valuesArray.title,
527
+ link_text: label || valuesArray.title,
528
+ link_url: link,
529
+ link_domain: valuesArray.el_hostname,
530
+ link_classes: valuesArray.el_classes,
531
+ link_id: valuesArray.el_id,
532
+ };
533
+ }
534
+
535
+ if ( fieldsArray ) {
536
+ __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
537
+ } else {
538
+ if ( type && type != 'internal' ) {
539
+ fieldsArray = {
540
+ event_category: type,
541
+ event_label: label || valuesArray.title,
542
+ link_text: label || valuesArray.title,
543
+ link_url: link,
544
+ link_domain: valuesArray.el_hostname,
545
+ link_classes: valuesArray.el_classes,
546
+ link_id: valuesArray.el_id,
547
+ };
548
+
549
+ __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
550
+ } else {
551
+ valuesArray.exit = 'type';
552
+ __gtagTrackerNotSend( valuesArray );
553
+ }
554
+ }
555
+ } else {
556
+ /* Prevent standard click, track then open */
557
+ if ( type != 'cross-hostname' && type != 'external' && type != 'internal-as-outbound' ) {
558
+ if ( !event.defaultPrevented ) {
559
+ if ( event.preventDefault ) {
560
+ event.preventDefault();
561
+ } else {
562
+ event.returnValue = false;
563
+ }
564
+ }
565
+ }
566
+
567
+ if ( type == 'download' ) {
568
+ fieldsArray = {
569
+ event_category: 'download',
570
+ event_label: label || valuesArray.title,
571
+ event_callback: __gtagTrackerHitBack,
572
+ file_extension: valuesArray.extension,
573
+ file_name: valuesArray.link.replace(/^.*\//g, ''),
574
+ link_text: label || valuesArray.title,
575
+ link_url: link,
576
+ link_domain: valuesArray.el_hostname,
577
+ link_classes: valuesArray.el_classes,
578
+ link_id: valuesArray.el_id,
579
+ };
580
+
581
+ __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
582
+ } else if ( type == 'internal-as-outbound' ) {
583
+ beforeUnloadChanged = true;
584
+ window.onbeforeunload = function ( e ) {
585
+ if ( !event.defaultPrevented ) {
586
+ if ( event.preventDefault ) {
587
+ event.preventDefault();
588
+ } else {
589
+ event.returnValue = false;
590
+ }
591
+ }
592
+
593
+ fieldsArray = {
594
+ event_category: internalAsOutboundCategory,
595
+ event_label: label || valuesArray.title,
596
+ event_callback: __gtagTrackerHitBack,
597
+ is_affiliate_link: true,
598
+ affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''),
599
+ link_text: label || valuesArray.title,
600
+ link_url: link,
601
+ link_domain: valuesArray.el_hostname,
602
+ link_classes: valuesArray.el_classes,
603
+ link_id: valuesArray.el_id,
604
+ outbound: true,
605
+ };
606
+
607
+ if ( navigator.sendBeacon ) {
608
+ fieldsArray.transport = 'beacon';
609
+ }
610
+
611
+ __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
612
+
613
+ setTimeout( __gtagTrackerHitBack, 1000 );
614
+ };
615
+ } else if ( type == 'external' ) {
616
+ beforeUnloadChanged = true;
617
+ window.onbeforeunload = function ( e ) {
618
+ if ( !event.defaultPrevented ) {
619
+ if ( event.preventDefault ) {
620
+ event.preventDefault();
621
+ } else {
622
+ event.returnValue = false;
623
+ }
624
+ }
625
+
626
+ fieldsArray = {
627
+ event_category: 'outbound-link',
628
+ event_label: label || valuesArray.title,
629
+ event_callback: __gtagTrackerHitBack,
630
+ is_affiliate_link: false,
631
+ link_text: label || valuesArray.title,
632
+ link_url: link,
633
+ link_domain: valuesArray.el_hostname,
634
+ link_classes: valuesArray.el_classes,
635
+ link_id: valuesArray.el_id,
636
+ outbound: true,
637
+ };
638
+
639
+ if ( navigator.sendBeacon ) {
640
+ fieldsArray.transport = 'beacon';
641
+ }
642
+
643
+ __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
644
+ setTimeout( __gtagTrackerHitBack, 1000 );
645
+ };
646
+ } else if ( type == 'cross-hostname' ) {
647
+ beforeUnloadChanged = true;
648
+ window.onbeforeunload = function ( e ) {
649
+ if ( !event.defaultPrevented ) {
650
+ if ( event.preventDefault ) {
651
+ event.preventDefault();
652
+ } else {
653
+ event.returnValue = false;
654
+ }
655
+ }
656
+
657
+ fieldsArray = {
658
+ event_category: 'cross-hostname',
659
+ event_label: label || valuesArray.title,
660
+ event_callback: __gtagTrackerHitBack,
661
+ link_text: label || valuesArray.title,
662
+ link_url: link,
663
+ link_domain: valuesArray.el_hostname,
664
+ link_classes: valuesArray.el_classes,
665
+ link_id: valuesArray.el_id,
666
+ };
667
+
668
+ if ( navigator.sendBeacon ) {
669
+ fieldsArray.transport = 'beacon';
670
+ }
671
+
672
+ __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
673
+ setTimeout( __gtagTrackerHitBack, 1000 );
674
+ };
675
+ } else {
676
+ if ( type && type !== 'internal' ) {
677
+ fieldsArray = {
678
+ event_category: type,
679
+ event_label: label || valuesArray.title,
680
+ event_callback: __gtagTrackerHitBack,
681
+ link_text: label || valuesArray.title,
682
+ link_url: link,
683
+ link_domain: valuesArray.el_hostname,
684
+ link_classes: valuesArray.el_classes,
685
+ link_id: valuesArray.el_id,
686
+ };
687
+
688
+ __gtagTrackerSendDual( 'event', action || link, fieldsArray, valuesArray );
689
+ } else {
690
+ valuesArray.exit = 'type';
691
+ __gtagTrackerNotSend( valuesArray );
692
+ }
693
+ }
694
+
695
+ if ( type != 'external' && type != 'cross-hostname' && type != 'internal-as-outbound' ) {
696
+ /* Run event_callback again if GA takes longer than 1 second */
697
+ setTimeout( __gtagTrackerHitBack, 1000 );
698
+ } else {
699
+ if ( type == 'external' ) {
700
+ setTimeout( __gtagTrackerNoRedirectExternal, 1100 );
701
+ } else if ( type == 'cross-hostname' ) {
702
+ setTimeout( __gtagTrackerNoRedirectCrossHostname, 1100 );
703
+ } else {
704
+ setTimeout( __gtagTrackerNoRedirectInboundAsExternal, 1100 );
705
+ }
706
+ }
707
+
708
+ // Clear out the beforeunload event if it was set to avoid sending false events.
709
+ setTimeout( maybePreventBeforeUnload, 100 );
710
+ }
711
+ } else {
712
+ maybePreventBeforeUnload();
713
+ valuesArray.exit = 'internal';
714
+ __gtagTrackerNotSend( valuesArray );
715
+ }
716
+ } else {
717
+ valuesArray.exit = 'notlink';
718
+ __gtagTrackerNotSend( valuesArray );
719
+ }
720
+ }
721
+
722
+ var prevHash = window.location.hash;
723
+
724
+ function __gtagTrackerHashChangeEvent() {
725
+ /* Todo: Ready this section for JS unit testing */
726
+ if ( exactmetrics_frontend.hash_tracking === "true" && prevHash != window.location.hash && ( exactmetrics_frontend.ua || exactmetrics_frontend.v4_id ) ) {
727
+ prevHash = window.location.hash;
728
+ if ( exactmetrics_frontend.ua ) {
729
+ __gtagTracker( 'config', exactmetrics_frontend.ua, {
730
+ page_path: location.pathname + location.search + location.hash,
731
+ } );
732
+ }
733
+
734
+ if ( exactmetrics_frontend.v4_id ) {
735
+ __gtagTracker( 'config', exactmetrics_frontend.v4_id, {
736
+ page_path: location.pathname + location.search + location.hash,
737
+ } );
738
+ }
739
+ __gtagTrackerLog( "Hash change to: " + location.pathname + location.search + location.hash );
740
+ } else {
741
+ __gtagTrackerLog( "Hash change to (untracked): " + location.pathname + location.search + location.hash );
742
+ }
743
+ }
744
+
745
+ function maybePreventBeforeUnload() {
746
+ if ( beforeUnloadChanged ) {
747
+ window.onbeforeunload = null;
748
+ }
749
+ }
750
+
751
+ /* Attach the event to all clicks in the document after page has loaded */
752
+ var __gtagTrackerWindow = window;
753
+ if ( __gtagTrackerWindow.addEventListener ) {
754
+ __gtagTrackerWindow.addEventListener(
755
+ "load",
756
+ function () {
757
+ document.body.addEventListener(
758
+ "click",
759
+ __gtagTrackerClickEvent,
760
+ false
761
+ );
762
+ },
763
+ false
764
+ );
765
+ window.addEventListener( "hashchange", __gtagTrackerHashChangeEvent, false );
766
+ } else {
767
+ if ( __gtagTrackerWindow.attachEvent ) {
768
+ __gtagTrackerWindow.attachEvent(
769
+ "onload",
770
+ function () {
771
+ document.body.attachEvent( "onclick", __gtagTrackerClickEvent );
772
+ }
773
+ );
774
+ window.attachEvent( "onhashchange", __gtagTrackerHashChangeEvent );
775
+ }
776
+ }
777
+
778
+ if ( typeof String.prototype.endsWith !== 'function' ) {
779
+ String.prototype.endsWith = function ( suffix ) {
780
+ return this.indexOf( suffix, this.length - suffix.length ) !== - 1;
781
+ };
782
+ }
783
+ if ( typeof String.prototype.startsWith !== 'function' ) {
784
+ String.prototype.startsWith = function ( prefix ) {
785
+ return this.indexOf( prefix ) === 0;
786
+ };
787
+ }
788
+
789
+ if ( typeof Array.prototype.lastIndexOf !== 'function' ) {
790
+ Array.prototype.lastIndexOf = function ( searchElement /*, fromIndex*/ ) {
791
+ 'use strict';
792
+
793
+ if ( this === void 0 || this === null ) {
794
+ throw new TypeError();
795
+ }
796
+
797
+ var n, k,
798
+ t = Object( this ),
799
+ len = t.length >>> 0; /* jshint ignore:line */
800
+ if ( len === 0 ) {
801
+ return - 1;
802
+ }
803
+
804
+ n = len - 1;
805
+ if ( arguments.length > 1 ) {
806
+ n = Number( arguments[1] );
807
+ if ( n != n ) {
808
+ n = 0;
809
+ } else if ( n != 0 && n != (
810
+ 1 / 0
811
+ ) && n != - (
812
+ 1 / 0
813
+ ) ) { /* jshint ignore:line */
814
+ n = (
815
+ n > 0 || - 1
816
+ ) * Math.floor( Math.abs( n ) );
817
+ }
818
+ }
819
+
820
+ for ( k = n >= 0 ? Math.min( n, len - 1 ) : len - Math.abs( n ); k >= 0; k -- ) {
821
+ if ( k in t && t[k] === searchElement ) {
822
+ return k;
823
+ }
824
+ }
825
+ return - 1;
826
+ };
827
+ }
828
+ };
829
+ var ExactMetricsObject = new ExactMetrics();
assets/js/popular-posts.js CHANGED
@@ -1,33 +1,33 @@
1
- var ExactMetrics_Popular_Posts = {
2
-
3
- init: function () {
4
- this.grab_widgets_with_ajax();
5
- },
6
-
7
- grab_widgets_with_ajax: function () {
8
- var xhr = new XMLHttpRequest();
9
- var url = exactmetrics_pp.ajaxurl;
10
- var widgets_jsons = document.querySelectorAll( '.exactmetrics-popular-posts-widget-json' ),
11
- i,
12
- widgets_length = widgets_jsons.length;
13
-
14
- var params = 'action=exactmetrics_popular_posts_get_widget_output&post_id=' + exactmetrics_pp.post_id;
15
-
16
- for ( i = 0; i < widgets_length; ++ i ) {
17
- params += '&data[]=' + widgets_jsons[i].innerHTML
18
- }
19
- xhr.open( 'POST', url );
20
- xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
21
- xhr.onload = function () {
22
- if ( xhr.status === 200 ) {
23
- let rendered_widgets = JSON.parse( xhr.responseText );
24
- for ( i = 0; i < widgets_length; ++ i ) {
25
- widgets_jsons[i].parentElement.innerHTML = rendered_widgets[i];
26
- }
27
- }
28
- };
29
- xhr.send( params );
30
- },
31
- };
32
-
33
- ExactMetrics_Popular_Posts.init();
1
+ var ExactMetrics_Popular_Posts = {
2
+
3
+ init: function () {
4
+ this.grab_widgets_with_ajax();
5
+ },
6
+
7
+ grab_widgets_with_ajax: function () {
8
+ var xhr = new XMLHttpRequest();
9
+ var url = exactmetrics_pp.ajaxurl;
10
+ var widgets_jsons = document.querySelectorAll( '.exactmetrics-popular-posts-widget-json' ),
11
+ i,
12
+ widgets_length = widgets_jsons.length;
13
+
14
+ var params = 'action=exactmetrics_popular_posts_get_widget_output&post_id=' + exactmetrics_pp.post_id;
15
+
16
+ for ( i = 0; i < widgets_length; ++ i ) {
17
+ params += '&data[]=' + widgets_jsons[i].innerHTML
18
+ }
19
+ xhr.open( 'POST', url );
20
+ xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
21
+ xhr.onload = function () {
22
+ if ( xhr.status === 200 ) {
23
+ let rendered_widgets = JSON.parse( xhr.responseText );
24
+ for ( i = 0; i < widgets_length; ++ i ) {
25
+ widgets_jsons[i].parentElement.innerHTML = rendered_widgets[i];
26
+ }
27
+ }
28
+ };
29
+ xhr.send( params );
30
+ },
31
+ };
32
+
33
+ ExactMetrics_Popular_Posts.init();
gadwp.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin URI: https://exactmetrics.com
5
  * Description: Displays Google Analytics Reports and Real-Time Statistics in your Dashboard. Automatically inserts the tracking code in every page of your website.
6
  * Author: ExactMetrics
7
- * Version: 7.2.1
8
  * Requires at least: 4.8.0
9
  * Requires PHP: 5.5
10
  * Author URI: https://exactmetrics.com
@@ -44,7 +44,7 @@ final class ExactMetrics_Lite {
44
  * @access public
45
  * @var string $version Plugin version.
46
  */
47
- public $version = '7.2.1';
48
 
49
  /**
50
  * Plugin file.
4
  * Plugin URI: https://exactmetrics.com
5
  * Description: Displays Google Analytics Reports and Real-Time Statistics in your Dashboard. Automatically inserts the tracking code in every page of your website.
6
  * Author: ExactMetrics
7
+ * Version: 7.2.0
8
  * Requires at least: 4.8.0
9
  * Requires PHP: 5.5
10
  * Author URI: https://exactmetrics.com
44
  * @access public
45
  * @var string $version Plugin version.
46
  */
47
+ public $version = '7.2.0';
48
 
49
  /**
50
  * Plugin file.
includes/admin/em-admin.php CHANGED
@@ -1,25 +1,25 @@
1
- <?php
2
- /**
3
- * Add menu items in admin bar
4
- *
5
- * @since 6.6.0
6
- *
7
- * @param object $wp_admin_bar WP_Admin_Bar instance, passed by reference
8
- */
9
- function exactmetrics_admin_bar_items( $admin_bar ) {
10
- if ( ! current_user_can( 'exactmetrics_view_dashboard' ) ) {
11
- return;
12
- }
13
-
14
- $admin_bar->add_menu( array(
15
- 'id' => 'exactmetrics-analyltics-reports',
16
- 'parent' => 'wp-logo',
17
- 'group' => null,
18
- 'title' => 'ExactMetrics',
19
- 'href' => add_query_arg( 'page', 'exactmetrics_reports', admin_url( 'admin.php' ) ),
20
- 'meta' => array(
21
- 'title' => 'ExactMetrics',
22
- ),
23
- ) );
24
- }
25
- add_action( 'admin_bar_menu', 'exactmetrics_admin_bar_items', 500 );
1
+ <?php
2
+ /**
3
+ * Add menu items in admin bar
4
+ *
5
+ * @since 6.6.0
6
+ *
7
+ * @param object $wp_admin_bar WP_Admin_Bar instance, passed by reference
8
+ */
9
+ function exactmetrics_admin_bar_items( $admin_bar ) {
10
+ if ( ! current_user_can( 'exactmetrics_view_dashboard' ) ) {
11
+ return;
12
+ }
13
+
14
+ $admin_bar->add_menu( array(
15
+ 'id' => 'exactmetrics-analyltics-reports',
16
+ 'parent' => 'wp-logo',
17
+ 'group' => null,
18
+ 'title' => 'ExactMetrics',
19
+ 'href' => add_query_arg( 'page', 'exactmetrics_reports', admin_url( 'admin.php' ) ),
20
+ 'meta' => array(
21
+ 'title' => 'ExactMetrics',
22
+ ),
23
+ ) );
24
+ }
25
+ add_action( 'admin_bar_menu', 'exactmetrics_admin_bar_items', 500 );
includes/admin/notification-event-runner.php CHANGED
@@ -1,189 +1,189 @@
1
- <?php
2
- /**
3
- * Custom runner system for recurring notifications.
4
- *
5
- * @since 7.14
6
- * @author Mircea Sandu
7
- * @package ExactMetrics
8
- */
9
-
10
- /**
11
- * Class ExactMetrics_Notification_Event_Runner
12
- */
13
- class ExactMetrics_Notification_Event_Runner {
14
-
15
- /**
16
- * The instance of the current class.
17
- *
18
- * @var ExactMetrics_Notification_Event_Runner
19
- */
20
- private static $instance;
21
-
22
- /**
23
- * The static notifications registered.
24
- *
25
- * @var array
26
- */
27
- private static $notifications = array();
28
-
29
- /**
30
- * The key used to store in the options table the last run times for notifications.
31
- *
32
- * @var string
33
- */
34
- private $last_run_key = 'exactmetrics_notifications_run';
35
-
36
- /**
37
- * This will be populated on demand with the last run timestamps for all the notifications.
38
- *
39
- * @var array|bool
40
- */
41
- private $last_run;
42
-
43
- /**
44
- * Only update the option if something changed.
45
- *
46
- * @var bool
47
- */
48
- private $changed = false;
49
-
50
- /**
51
- * ExactMetrics_Notification_Event_Runner constructor.
52
- */
53
- private function __construct() {
54
- add_action( 'wp_ajax_exactmetrics_vue_get_notifications', array( $this, 'maybe_add_notifications' ), 9 );
55
- }
56
-
57
- /**
58
- * Get the singleton instance.
59
- *
60
- * @return ExactMetrics_Notification_Event_Runner
61
- */
62
- public static function get_instance() {
63
- if ( ! isset( self::$instance ) ) {
64
- self::$instance = new self();
65
- }
66
-
67
- return self::$instance;
68
- }
69
-
70
- /**
71
- * Get the stored option for the last run times.
72
- *
73
- * @return false|mixed|void
74
- */
75
- public function get_notifications_last_run() {
76
- if ( ! isset( $this->last_run ) ) {
77
- $this->last_run = get_option( $this->last_run_key );
78
- }
79
-
80
- return $this->last_run;
81
- }
82
-
83
- /**
84
- * Update the last run time with a default of time.
85
- *
86
- * @param string $notification_id The notification id to update the last run time for.
87
- * @param string|int $time The timestamp to store the last run time.
88
- */
89
- public function update_last_run( $notification_id, $time = '' ) {
90
- if ( empty( $time ) ) {
91
- $time = time();
92
- }
93
-
94
- $this->last_run[ $notification_id ] = $time;
95
- $this->changed = true;
96
- }
97
-
98
- /**
99
- * Update the option stored in the db with the last run times.
100
- */
101
- public function save_last_runs() {
102
- if ( $this->changed ) {
103
- update_option( $this->last_run_key, $this->last_run, false );
104
- }
105
- }
106
-
107
- /**
108
- * Loop through notifications and check if they should be added based on the time passed since they were last added.
109
- */
110
- public function maybe_add_notifications() {
111
-
112
- if ( ! current_user_can( 'exactmetrics_view_dashboard' ) ) {
113
- // No need to try adding the notification if the user can't see it.
114
- return;
115
- }
116
-
117
- $notifications = $this->get_registered_notifications();
118
- $last_runs = $this->get_notifications_last_run();
119
-
120
- // Loop through registered notifications.
121
- foreach ( $notifications as $notification ) {
122
- /**¬
123
- * The notification instance.
124
- *
125
- * @var ExactMetrics_Notification_Event $notification
126
- */
127
- if ( empty( $last_runs[ $notification->notification_id ] ) ) {
128
- // If the notification never ran, save current time to show it after the interval.
129
- $this->update_last_run( $notification->notification_id );
130
- } else {
131
- // Has run before so let's check if enough days passed since the last run.
132
- $time_since = $last_runs[ $notification->notification_id ] + $notification->notification_interval * DAY_IN_SECONDS;
133
- $time_now = time();
134
- if ( $time_since < $time_now ) {
135
- // Interval passed since it ran so let's add this one.
136
- $notification->add_notification();
137
- // Update the last run date as right now.
138
- $this->update_last_run( $notification->notification_id );
139
- // Let's not add multiple notifications at the same time.
140
- break;
141
- }
142
- }
143
- }
144
-
145
- // Update the option with the new times.
146
- $this->save_last_runs();
147
-
148
- }
149
-
150
- /**
151
- * Get the static notifications array.
152
- *
153
- * @return array
154
- */
155
- public function get_registered_notifications() {
156
- return self::$notifications;
157
- }
158
-
159
- /**
160
- * Register the notification for running it later.
161
- *
162
- * @param ExactMetrics_Notification_Event $notification The instance of the notification.
163
- */
164
- public function register_notification( $notification ) {
165
-
166
- $notification_id = isset( $notification->notification_id ) ? $notification->notification_id : false;
167
- if ( ! empty( $notification_id ) && ! isset( self::$notifications[ $notification_id ] ) ) {
168
- self::$notifications[ $notification_id ] = $notification;
169
- }
170
-
171
- }
172
-
173
- /**
174
- * Delete the data on uninstall.
175
- */
176
- public function delete_data() {
177
- delete_option( $this->last_run_key );
178
- }
179
-
180
- }
181
-
182
- /**
183
- * Get the single instance of the event runner class.
184
- *
185
- * @return ExactMetrics_Notification_Event_Runner
186
- */
187
- function exactmetrics_notification_event_runner() {
188
- return ExactMetrics_Notification_Event_Runner::get_instance();
189
- }
1
+ <?php
2
+ /**
3
+ * Custom runner system for recurring notifications.
4
+ *
5
+ * @since 7.14
6
+ * @author Mircea Sandu
7
+ * @package ExactMetrics
8
+ */
9
+
10
+ /**
11
+ * Class ExactMetrics_Notification_Event_Runner
12
+ */
13
+ class ExactMetrics_Notification_Event_Runner {
14
+
15
+ /**
16
+ * The instance of the current class.
17
+ *
18
+ * @var ExactMetrics_Notification_Event_Runner
19
+ */
20
+ private static $instance;
21
+
22
+ /**
23
+ * The static notifications registered.
24
+ *
25
+ * @var array
26
+ */
27
+ private static $notifications = array();
28
+
29
+ /**
30
+ * The key used to store in the options table the last run times for notifications.
31
+ *
32
+ * @var string
33
+ */
34
+ private $last_run_key = 'exactmetrics_notifications_run';
35
+
36
+ /**
37
+ * This will be populated on demand with the last run timestamps for all the notifications.
38
+ *
39
+ * @var array|bool
40
+ */
41
+ private $last_run;
42
+
43
+ /**
44
+ * Only update the option if something changed.
45
+ *
46
+ * @var bool
47
+ */
48
+ private $changed = false;
49
+
50
+ /**
51
+ * ExactMetrics_Notification_Event_Runner constructor.
52
+ */
53
+ private function __construct() {
54
+ add_action( 'wp_ajax_exactmetrics_vue_get_notifications', array( $this, 'maybe_add_notifications' ), 9 );
55
+ }
56
+
57
+ /**
58
+ * Get the singleton instance.
59
+ *
60
+ * @return ExactMetrics_Notification_Event_Runner
61
+ */
62
+ public static function get_instance() {
63
+ if ( ! isset( self::$instance ) ) {
64
+ self::$instance = new self();
65
+ }
66
+
67
+ return self::$instance;
68
+ }
69
+
70
+ /**
71
+ * Get the stored option for the last run times.
72
+ *
73
+ * @return false|mixed|void
74
+ */
75
+ public function get_notifications_last_run() {
76
+ if ( ! isset( $this->last_run ) ) {
77
+ $this->last_run = get_option( $this->last_run_key );
78
+ }
79
+
80
+ return $this->last_run;
81
+ }
82
+
83
+ /**
84
+ * Update the last run time with a default of time.
85
+ *
86
+ * @param string $notification_id The notification id to update the last run time for.
87
+ * @param string|int $time The timestamp to store the last run time.
88
+ */
89
+ public function update_last_run( $notification_id, $time = '' ) {
90
+ if ( empty( $time ) ) {
91
+ $time = time();
92
+ }
93
+
94
+ $this->last_run[ $notification_id ] = $time;
95
+ $this->changed = true;
96
+ }
97
+
98
+ /**
99
+ * Update the option stored in the db with the last run times.
100
+ */
101
+ public function save_last_runs() {
102
+ if ( $this->changed ) {
103
+ update_option( $this->last_run_key, $this->last_run, false );
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Loop through notifications and check if they should be added based on the time passed since they were last added.
109
+ */
110
+ public function maybe_add_notifications() {
111
+
112
+ if ( ! current_user_can( 'exactmetrics_view_dashboard' ) ) {
113
+ // No need to try adding the notification if the user can't see it.
114
+ return;
115
+ }
116
+
117
+ $notifications = $this->get_registered_notifications();
118
+ $last_runs = $this->get_notifications_last_run();
119
+
120
+ // Loop through registered notifications.
121
+ foreach ( $notifications as $notification ) {
122
+ /**¬
123
+ * The notification instance.
124
+ *
125
+ * @var ExactMetrics_Notification_Event $notification
126
+ */
127
+ if ( empty( $last_runs[ $notification->notification_id ] ) ) {
128
+ // If the notification never ran, save current time to show it after the interval.
129
+ $this->update_last_run( $notification->notification_id );
130
+ } else {
131
+ // Has run before so let's check if enough days passed since the last run.
132
+ $time_since = $last_runs[ $notification->notification_id ] + $notification->notification_interval * DAY_IN_SECONDS;
133
+ $time_now = time();
134
+ if ( $time_since < $time_now ) {
135
+ // Interval passed since it ran so let's add this one.
136
+ $notification->add_notification();
137
+ // Update the last run date as right now.
138
+ $this->update_last_run( $notification->notification_id );
139
+ // Let's not add multiple notifications at the same time.
140
+ break;
141
+ }
142
+ }
143
+ }
144
+
145
+ // Update the option with the new times.
146
+ $this->save_last_runs();
147
+
148
+ }
149
+
150
+ /**
151
+ * Get the static notifications array.
152
+ *
153
+ * @return array
154
+ */
155
+ public function get_registered_notifications() {
156
+ return self::$notifications;
157
+ }
158
+
159
+ /**
160
+ * Register the notification for running it later.
161
+ *
162
+ * @param ExactMetrics_Notification_Event $notification The instance of the notification.
163
+ */
164
+ public function register_notification( $notification ) {
165
+
166
+ $notification_id = isset( $notification->notification_id ) ? $notification->notification_id : false;
167
+ if ( ! empty( $notification_id ) && ! isset( self::$notifications[ $notification_id ] ) ) {
168
+ self::$notifications[ $notification_id ] = $notification;
169
+ }
170
+
171
+ }
172
+
173
+ /**
174
+ * Delete the data on uninstall.
175
+ */
176
+ public function delete_data() {
177
+ delete_option( $this->last_run_key );
178
+ }
179
+
180
+ }
181
+
182
+ /**
183
+ * Get the single instance of the event runner class.
184
+ *
185
+ * @return ExactMetrics_Notification_Event_Runner
186
+ */
187
+ function exactmetrics_notification_event_runner() {
188
+ return ExactMetrics_Notification_Event_Runner::get_instance();
189
+ }
includes/admin/notification-event.php CHANGED
@@ -1,237 +1,237 @@
1
- <?php
2
- /**
3
- * Parent Class for ExactMetrics Notification Event
4
- *
5
- * @since 7.12.3
6
- *
7
- * @package ExactMetrics
8
- */
9
-
10
- class ExactMetrics_Notification_Event {
11
-
12
- /**
13
- * Generate unique notification id
14
- *
15
- * @var string
16
- *
17
- * @since 7.12.3
18
- */
19
- public $notification_id;
20
-
21
- /**
22
- * When the notification will repeat (e.g: 7) here `7` to repeat the notification after each 7 days
23
- * Only accept numeric value
24
- *
25
- * @var number
26
- *
27
- * @since 7.12.3
28
- */
29
- public $notification_interval;
30
-
31
- /**
32
- * When the notification will active, default: now
33
- *
34
- * @var string
35
- *
36
- * @since 7.12.3
37
- */
38
- public $notification_active_from;
39
-
40
- /**
41
- * For how many days notification will be active
42
- *
43
- * @var string
44
- *
45
- * @since 7.12.3
46
- */
47
- public $notification_active_for;
48
-
49
- /**
50
- * Which type of license is allowed to view this notification
51
- *
52
- * @var array
53
- *
54
- * @since 7.12.3
55
- */
56
- public $notification_type;
57
-
58
- /**
59
- * Report start date if required e.g: "-15 day"(Readable Time)
60
- *
61
- * @var string
62
- *
63
- * @since 7.12.3
64
- */
65
- public $report_start_from;
66
-
67
- /**
68
- * Report end date if required e.g: "-1 day"(Readable Time)
69
- *
70
- * @var string
71
- *
72
- * @since 7.12.3
73
- */
74
- public $report_end_to;
75
-
76
- /**
77
- * Notification icon to display with content
78
- *
79
- * @var string
80
- *
81
- * @since 7.12.3
82
- */
83
- public $notification_icon;
84
-
85
- /**
86
- * Constructor
87
- *
88
- * @since 7.12.3
89
- */
90
- public function __construct() {
91
-
92
- $this->notification_active_from = date( 'm/d/Y g:i a', strtotime( 'now' ) );
93
- $this->report_end_to = '-1 day'; // Yesterday.
94
-
95
- if ( ! empty( $this->notification_id ) && ! empty( $this->notification_interval ) ) {
96
-
97
- // Register notification in our custom runner.
98
- exactmetrics_notification_event_runner()->register_notification( $this );
99
-
100
- $this->notification_active_for = date( 'm/d/Y', strtotime( '+' . ( $this->notification_interval - 2 ) . ' day' ) );
101
- $this->report_start_from = '-' . $this->notification_interval . ' day';
102
-
103
- if ( ! isset( $this->notification_icon ) || empty( $this->notification_icon ) ) {
104
- $this->notification_icon = 'default';
105
- }
106
-
107
- }
108
- }
109
-
110
- /**
111
- * Get the formatted date.
112
- *
113
- * @param string $readable_time Readable time to convert to date.
114
- *
115
- * @return string date, format: Y-m-d
116
- *
117
- * @since 7.12.3
118
- */
119
- public function get_formatted_date( $readable_time ) {
120
- return date( 'Y-m-d', strtotime( $readable_time ) );
121
- }
122
-
123
- /**
124
- * Get the upgrade URL for pro plugin
125
- *
126
- * @return string
127
- */
128
- public function get_upgrade_url() {
129
- return wp_specialchars_decode( exactmetrics_get_upgrade_link( 'exactmetrics-notifications-sidebar', 'notifications', 'https://www.exactmetrics.com/lite/' ) );
130
- }
131
-
132
- /**
133
- * Build external link by including UTM data
134
- *
135
- * @return string
136
- */
137
- public function build_external_link( $url ) {
138
- $build_url = wp_specialchars_decode( exactmetrics_get_url( 'exactmetrics-notifications-sidebar', 'notifications', $url ) );
139
- $host = parse_url( $build_url, PHP_URL_HOST );
140
- $domain_name = preg_replace( '/^www\./', '', $host );
141
-
142
- if ( 'exactmetrics.com' != $domain_name ) {
143
- parse_str( parse_url( $build_url, PHP_URL_QUERY ), $queries );
144
-
145
- if ( isset( $queries['utm_source'] ) ) {
146
- $queries['utm_source'] = 'exactmetrics';
147
- }
148
-
149
- $build_url = add_query_arg(
150
- $queries,
151
- trailingslashit( $url )
152
- );
153
- }
154
-
155
- return $build_url;
156
- }
157
-
158
- /**
159
- * Get the URL for the page where users can see/read notifications.
160
- *
161
- * @return string
162
- */
163
- public function get_view_url( $scroll_to, $page, $tab='' ) {
164
- return ExactMetrics()->notifications->get_view_url( $scroll_to, $page, $tab );
165
- }
166
-
167
- /**
168
- * @param array $data
169
- *
170
- * @return array
171
- */
172
- public function prepare_notification_data( $data ) {
173
- return $data;
174
- }
175
-
176
- /**
177
- * Add Notification not the notifications instance.
178
- *
179
- * @since 7.12.3
180
- */
181
- public function add_notification() {
182
- $notification = array();
183
- $notification['id'] = $this->notification_id . '_' . date( 'Ymd' ); // Make sure we never add the same notification on the same day.
184
- $notification['icon'] = $this->notification_icon;
185
- $notification['title'] = '';
186
- $notification['content'] = '';
187
- $notification['type'] = $this->notification_type;
188
- $notification['btns'] = array();
189
- $notification['start'] = $this->notification_active_from;
190
- $notification['end'] = $this->notification_active_for;
191
- $notification_data = $this->prepare_notification_data( $notification );
192
-
193
- if ( is_array( $notification_data ) && ! empty( $notification_data ) ) {
194
- ExactMetrics()->notifications->add( $notification_data );
195
- }
196
- }
197
-
198
- /**
199
- * Get report
200
- *
201
- * @param string $report_name report name, default overview report.
202
- * @param string $report_start_from report start date, default -30 days/last 30 days.
203
- * @param string $report_end_to report end date, default -1 day/yesterday.
204
- *
205
- * @return array $data Overview data
206
- *
207
- * @since 7.12.3
208
- */
209
- public function get_report( $report_name = 'overview', $report_start_from = '-30 day', $report_end_to = '-1 day' ) {
210
-
211
- $report = ExactMetrics()->reporting->get_report( $report_name );
212
- if ( $report ) {
213
- // Mark the report request as coming from Notifications.
214
- $report->set_report_source( 'notifications' );
215
- }
216
- $isnetwork = ! empty( $_REQUEST['isnetwork'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['isnetwork'] ) ) : '';
217
- $args = array(
218
- 'start' => $this->get_formatted_date( $report_start_from ),
219
- 'end' => $this->get_formatted_date( $report_end_to ),
220
- );
221
-
222
- if ( $isnetwork ) {
223
- $args['network'] = true;
224
- }
225
-
226
- if ( exactmetrics_is_pro_version() && ! ExactMetrics()->license->license_can( $report->level ) ) {
227
- $data = array(
228
- 'success' => false,
229
- 'message' => __( "You don't have permission to view ExactMetrics reports.", 'google-analytics-dashboard-for-wp' ),
230
- );
231
- } else {
232
- $data = $report->get_data( $args );
233
- }
234
-
235
- return $data;
236
- }
237
- }
1
+ <?php
2
+ /**
3
+ * Parent Class for ExactMetrics Notification Event
4
+ *
5
+ * @since 7.12.3
6
+ *
7
+ * @package ExactMetrics
8
+ */
9
+
10
+ class ExactMetrics_Notification_Event {
11
+
12
+ /**
13
+ * Generate unique notification id
14
+ *
15
+ * @var string
16
+ *
17
+ * @since 7.12.3
18
+ */
19
+ public $notification_id;
20
+
21
+ /**
22
+ * When the notification will repeat (e.g: 7) here `7` to repeat the notification after each 7 days
23
+ * Only accept numeric value
24
+ *
25
+ * @var number
26
+ *
27
+ * @since 7.12.3
28
+ */
29
+ public $notification_interval;
30
+
31
+ /**
32
+ * When the notification will active, default: now
33
+ *
34
+ * @var string
35
+ *
36
+ * @since 7.12.3
37
+ */
38
+ public $notification_active_from;
39
+
40
+ /**
41
+ * For how many days notification will be active
42
+ *
43
+ * @var string
44
+ *
45
+ * @since 7.12.3
46
+ */
47
+ public $notification_active_for;
48
+
49
+ /**
50
+ * Which type of license is allowed to view this notification
51
+ *
52
+ * @var array
53
+ *
54
+ * @since 7.12.3
55
+ */
56
+ public $notification_type;
57
+
58
+ /**
59
+ * Report start date if required e.g: "-15 day"(Readable Time)
60
+ *
61
+ * @var string
62
+ *
63
+ * @since 7.12.3
64
+ */
65
+ public $report_start_from;
66
+
67
+ /**
68
+ * Report end date if required e.g: "-1 day"(Readable Time)
69
+ *
70
+ * @var string
71
+ *
72
+ * @since 7.12.3
73
+ */
74
+ public $report_end_to;
75
+
76
+ /**
77
+ * Notification icon to display with content
78
+ *
79
+ * @var string
80
+ *
81
+ * @since 7.12.3
82
+ */
83
+ public $notification_icon;
84
+
85
+ /**
86
+ * Constructor
87
+ *
88
+ * @since 7.12.3
89
+ */
90
+ public function __construct() {
91
+
92
+ $this->notification_active_from = date( 'm/d/Y g:i a', strtotime( 'now' ) );
93
+ $this->report_end_to = '-1 day'; // Yesterday.
94
+
95
+ if ( ! empty( $this->notification_id ) && ! empty( $this->notification_interval ) ) {
96
+
97
+ // Register notification in our custom runner.
98
+ exactmetrics_notification_event_runner()->register_notification( $this );
99
+
100
+ $this->notification_active_for = date( 'm/d/Y', strtotime( '+' . ( $this->notification_interval - 2 ) . ' day' ) );
101
+ $this->report_start_from = '-' . $this->notification_interval . ' day';
102
+
103
+ if ( ! isset( $this->notification_icon ) || empty( $this->notification_icon ) ) {
104
+ $this->notification_icon = 'default';
105
+ }
106
+
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Get the formatted date.
112
+ *
113
+ * @param string $readable_time Readable time to convert to date.
114
+ *
115
+ * @return string date, format: Y-m-d
116
+ *
117
+ * @since 7.12.3
118
+ */
119
+ public function get_formatted_date( $readable_time ) {
120
+ return date( 'Y-m-d', strtotime( $readable_time ) );
121
+ }
122
+
123
+ /**
124
+ * Get the upgrade URL for pro plugin
125
+ *
126
+ * @return string
127
+ */
128
+ public function get_upgrade_url() {
129
+ return wp_specialchars_decode( exactmetrics_get_upgrade_link( 'exactmetrics-notifications-sidebar', 'notifications', 'https://www.exactmetrics.com/lite/' ) );
130
+ }
131
+
132
+ /**
133
+ * Build external link by including UTM data
134
+ *
135
+ * @return string
136
+ */
137
+ public function build_external_link( $url ) {
138
+ $build_url = wp_specialchars_decode( exactmetrics_get_url( 'exactmetrics-notifications-sidebar', 'notifications', $url ) );
139
+ $host = parse_url( $build_url, PHP_URL_HOST );
140
+ $domain_name = preg_replace( '/^www\./', '', $host );
141
+
142
+ if ( 'exactmetrics.com' != $domain_name ) {
143
+ parse_str( parse_url( $build_url, PHP_URL_QUERY ), $queries );
144
+
145
+ if ( isset( $queries['utm_source'] ) ) {
146
+ $queries['utm_source'] = 'exactmetrics';
147
+ }
148
+
149
+ $build_url = add_query_arg(
150
+ $queries,
151
+ trailingslashit( $url )
152
+ );
153
+ }
154
+
155
+ return $build_url;
156
+ }
157
+
158
+ /**
159
+ * Get the URL for the page where users can see/read notifications.
160
+ *
161
+ * @return string
162
+ */
163
+ public function get_view_url( $scroll_to, $page, $tab='' ) {
164
+ return ExactMetrics()->notifications->get_view_url( $scroll_to, $page, $tab );
165
+ }
166
+
167
+ /**
168
+ * @param array $data
169
+ *
170
+ * @return array
171
+ */
172
+ public function prepare_notification_data( $data ) {
173
+ return $data;
174
+ }
175
+
176
+ /**
177
+ * Add Notification not the notifications instance.
178
+ *
179
+ * @since 7.12.3
180
+ */
181
+ public function add_notification() {
182
+ $notification = array();
183
+ $notification['id'] = $this->notification_id . '_' . date( 'Ymd' ); // Make sure we never add the same notification on the same day.
184
+ $notification['icon'] = $this->notification_icon;
185
+ $notification['title'] = '';
186
+ $notification['content'] = '';
187
+ $notification['type'] = $this->notification_type;
188
+ $notification['btns'] = array();
189
+ $notification['start'] = $this->notification_active_from;
190
+ $notification['end'] = $this->notification_active_for;
191
+ $notification_data = $this->prepare_notification_data( $notification );
192
+
193
+ if ( is_array( $notification_data ) && ! empty( $notification_data ) ) {
194
+ ExactMetrics()->notifications->add( $notification_data );
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Get report
200
+ *
201
+ * @param string $report_name report name, default overview report.
202
+ * @param string $report_start_from report start date, default -30 days/last 30 days.
203
+ * @param string $report_end_to report end date, default -1 day/yesterday.
204
+ *
205
+ * @return array $data Overview data
206
+ *
207
+ * @since 7.12.3
208
+ */
209
+ public function get_report( $report_name = 'overview', $report_start_from = '-30 day', $report_end_to = '-1 day' ) {
210
+
211
+ $report = ExactMetrics()->reporting->get_report( $report_name );
212
+ if ( $report ) {
213
+ // Mark the report request as coming from Notifications.
214
+ $report->set_report_source( 'notifications' );
215
+ }
216
+ $isnetwork = ! empty( $_REQUEST['isnetwork'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['isnetwork'] ) ) : '';
217
+ $args = array(
218
+ 'start' => $this->get_formatted_date( $report_start_from ),
219
+ 'end' => $this->get_formatted_date( $report_end_to ),
220
+ );
221
+
222
+ if ( $isnetwork ) {
223
+ $args['network'] = true;
224
+ }
225
+
226
+ if ( exactmetrics_is_pro_version() && ! ExactMetrics()->license->license_can( $report->level ) ) {
227
+ $data = array(
228
+ 'success' => false,
229
+ 'message' => __( "You don't have permission to view ExactMetrics reports.", 'google-analytics-dashboard-for-wp' ),
230
+ );
231
+ } else {
232
+ $data = $report->get_data( $args );
233
+ }
234
+
235
+ return $data;
236
+ }
237
+ }
includes/admin/notifications/notification-audience.php CHANGED
@@ -1,101 +1,101 @@
1
- <?php
2
-
3
- /**
4
- * Add audience notification
5
- * Recurrence: 30 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Audience extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_audience';
12
- public $notification_interval = 30; // in days
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
-
15
- /**
16
- * Build Notification
17
- *
18
- * @param array $notification
19
- * @param array $data
20
- *
21
- * @return array $notification notification is ready to add
22
- *
23
- * @since 7.12.3
24
- */
25
- public function prepare_notification_data( $notification ) {
26
- $data = $this->get_notification_data();
27
-
28
- if ( ! is_array( $data ) || empty( $data ) ) {
29
- return false;
30
- }
31
-
32
- // Translators: Audience notification title
33
- $notification['title'] = sprintf( __( '%s%% of your Audience is from %s', 'google-analytics-dashboard-for-wp' ), $data['percentage'], $data['country'] );
34
- // Translators: Audience notification content
35
- $notification['content'] = sprintf( __( 'Is your site properly translated? By adding translated content specific to your audience you could gain big boosts in pageviews, time spent on page and a reduced bounce rate.<br><br>If you need help choosing a translation plugin to get you started take a look at %sthis article%s for the best options available.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.wpbeginner.com/showcase/9-best-translation-plugins-for-wordpress-websites/' ) . '" target="_blank">', '</a>' );
36
- $notification['btns'] = array(
37
- "view_report" => array(
38
- 'url' => $this->get_view_url( 'exactmetrics-report-top-countries', 'exactmetrics_reports' ),
39
- 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
40
- ),
41
- "learn_more" => array(
42
- 'url' => $this->build_external_link( 'https://www.wpbeginner.com/showcase/9-best-translation-plugins-for-wordpress-websites/' ),
43
- 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
44
- 'is_external' => true,
45
- ),
46
- );
47
-
48
- return $notification;
49
- }
50
-
51
- /**
52
- * Add report to notifications
53
- *
54
- * @since 7.12.3
55
- */
56
- public function get_notification_data() {
57
- require_once( ABSPATH . 'wp-admin/includes/translation-install.php' );
58
-
59
- $data = array();
60
- $report = $this->get_report();
61
- $sessions = isset( $report['data']['infobox']['sessions']['value'] ) ? $report['data']['infobox']['sessions']['value'] : 0;
62
- $countries = isset( $report['data']['countries'] ) ? $report['data']['countries'] : 0;
63
- $english_speaking_countries = exactmetrics_get_english_speaking_countries();
64
-
65
- if ( $sessions > 0 && is_array( $countries ) && ! empty( $countries ) ) {
66
- foreach ( $countries as $country ) {
67
- if ( empty( $country['iso'] ) || array_key_exists( $country['iso'], $english_speaking_countries ) ) {
68
- continue;
69
- }
70
-
71
- if ( $country['sessions'] > 0 ) {
72
- // get the country's session percentage by comparing with the total sessions
73
- $country_session_percentage = round( $country['sessions'] * 100 / $sessions );
74
-
75
- if ( $country_session_percentage < 15 ) {
76
- continue;
77
- }
78
-
79
- $site_language = get_locale();
80
- $translations = wp_get_available_translations();
81
-
82
- if ( is_array( $translations ) && ! empty( $translations ) ) {
83
- $site_iso = isset( $translations[ $site_language ]['iso'] ) ? $translations[ $site_language ]['iso'] : array(); // keep empty array, because site language has no iso setup for en_US language
84
-
85
- if ( is_array( $site_iso ) && ! in_array( $country['iso'], $site_iso ) ) {
86
- $data['country'] = $country['name'];
87
- $data['percentage'] = $country_session_percentage;
88
- break;
89
- }
90
- }
91
- }
92
- }
93
- }
94
-
95
- return $data;
96
- }
97
-
98
- }
99
-
100
- // initiate the class
101
- new ExactMetrics_Notification_Audience();
1
+ <?php
2
+
3
+ /**
4
+ * Add audience notification
5
+ * Recurrence: 30 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Audience extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_audience';
12
+ public $notification_interval = 30; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+
15
+ /**
16
+ * Build Notification
17
+ *
18
+ * @param array $notification
19
+ * @param array $data
20
+ *
21
+ * @return array $notification notification is ready to add
22
+ *
23
+ * @since 7.12.3
24
+ */
25
+ public function prepare_notification_data( $notification ) {
26
+ $data = $this->get_notification_data();
27
+
28
+ if ( ! is_array( $data ) || empty( $data ) ) {
29
+ return false;
30
+ }
31
+
32
+ // Translators: Audience notification title
33
+ $notification['title'] = sprintf( __( '%s%% of your Audience is from %s', 'google-analytics-dashboard-for-wp' ), $data['percentage'], $data['country'] );
34
+ // Translators: Audience notification content
35
+ $notification['content'] = sprintf( __( 'Is your site properly translated? By adding translated content specific to your audience you could gain big boosts in pageviews, time spent on page and a reduced bounce rate.<br><br>If you need help choosing a translation plugin to get you started take a look at %sthis article%s for the best options available.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.wpbeginner.com/showcase/9-best-translation-plugins-for-wordpress-websites/' ) . '" target="_blank">', '</a>' );
36
+ $notification['btns'] = array(
37
+ "view_report" => array(
38
+ 'url' => $this->get_view_url( 'exactmetrics-report-top-countries', 'exactmetrics_reports' ),
39
+ 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
40
+ ),
41
+ "learn_more" => array(
42
+ 'url' => $this->build_external_link( 'https://www.wpbeginner.com/showcase/9-best-translation-plugins-for-wordpress-websites/' ),
43
+ 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
44
+ 'is_external' => true,
45
+ ),
46
+ );
47
+
48
+ return $notification;
49
+ }
50
+
51
+ /**
52
+ * Add report to notifications
53
+ *
54
+ * @since 7.12.3
55
+ */
56
+ public function get_notification_data() {
57
+ require_once( ABSPATH . 'wp-admin/includes/translation-install.php' );
58
+
59
+ $data = array();
60
+ $report = $this->get_report();
61
+ $sessions = isset( $report['data']['infobox']['sessions']['value'] ) ? $report['data']['infobox']['sessions']['value'] : 0;
62
+ $countries = isset( $report['data']['countries'] ) ? $report['data']['countries'] : 0;
63
+ $english_speaking_countries = exactmetrics_get_english_speaking_countries();
64
+
65
+ if ( $sessions > 0 && is_array( $countries ) && ! empty( $countries ) ) {
66
+ foreach ( $countries as $country ) {
67
+ if ( empty( $country['iso'] ) || array_key_exists( $country['iso'], $english_speaking_countries ) ) {
68
+ continue;
69
+ }
70
+
71
+ if ( $country['sessions'] > 0 ) {
72
+ // get the country's session percentage by comparing with the total sessions
73
+ $country_session_percentage = round( $country['sessions'] * 100 / $sessions );
74
+
75
+ if ( $country_session_percentage < 15 ) {
76
+ continue;
77
+ }
78
+
79
+ $site_language = get_locale();
80
+ $translations = wp_get_available_translations();
81
+
82
+ if ( is_array( $translations ) && ! empty( $translations ) ) {
83
+ $site_iso = isset( $translations[ $site_language ]['iso'] ) ? $translations[ $site_language ]['iso'] : array(); // keep empty array, because site language has no iso setup for en_US language
84
+
85
+ if ( is_array( $site_iso ) && ! in_array( $country['iso'], $site_iso ) ) {
86
+ $data['country'] = $country['name'];
87
+ $data['percentage'] = $country_session_percentage;
88
+ break;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ return $data;
96
+ }
97
+
98
+ }
99
+
100
+ // initiate the class
101
+ new ExactMetrics_Notification_Audience();
includes/admin/notifications/notification-bounce-rate.php CHANGED
@@ -1,54 +1,54 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when bounce rate is higher than 70%
5
- * Recurrence: Once weekly
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Bounce_Rate extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_bounce_rate';
12
- public $notification_interval = 7; // In days.
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
-
15
- /**
16
- * Build Notification data.
17
- *
18
- * @param array $notification The notification data array to filter.
19
- *
20
- * @return array|bool $notification notification is ready to add or false if no data.
21
- *
22
- * @since 7.12.3
23
- */
24
- public function prepare_notification_data( $notification ) {
25
- $data = array();
26
- $report = $this->get_report( 'overview' );
27
- $data['bounce_rate'] = isset( $report['data']['infobox']['bounce']['value'] ) ? $report['data']['infobox']['bounce']['value'] : 0;
28
-
29
- if ( ! empty( $data ) && $data['bounce_rate'] > 70 ) {
30
- $notification['title'] = __( 'Your website bounce rate is higher than 70%', 'google-analytics-dashboard-for-wp' );
31
- // Translators: Bounce rate notification content.
32
- $notification['content'] = sprintf( __( 'Your website bounce rate is %1$s. High bounce rates can hurt your site’s conversions rates. A high bounce rate might mean that people aren\'t finding what they\'re looking for on your site. %2$sHere%3$s are some points to remember and steps to follow to get your bounce rates back to manageable levels.', 'google-analytics-dashboard-for-wp' ), $data['bounce_rate'], '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/how-to-reduce-bounce-rate/' ) . '" target="_blank">', '</a>' );
33
- $notification['btns'] = array(
34
- 'view_report' => array(
35
- 'url' => $this->get_view_url( 'exactmetrics-report-infobox-bounce-rate', 'exactmetrics_reports' ),
36
- 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' ),
37
- ),
38
- 'learn_more' => array(
39
- 'url' => $this->build_external_link( 'https://www.exactmetrics.com/how-to-reduce-bounce-rate/' ),
40
- 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
41
- 'is_external' => true,
42
- ),
43
- );
44
-
45
- return $notification;
46
- }
47
-
48
- return false;
49
- }
50
-
51
- }
52
-
53
- // initialize the class
54
- new ExactMetrics_Notification_Bounce_Rate();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when bounce rate is higher than 70%
5
+ * Recurrence: Once weekly
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Bounce_Rate extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_bounce_rate';
12
+ public $notification_interval = 7; // In days.
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+
15
+ /**
16
+ * Build Notification data.
17
+ *
18
+ * @param array $notification The notification data array to filter.
19
+ *
20
+ * @return array|bool $notification notification is ready to add or false if no data.
21
+ *
22
+ * @since 7.12.3
23
+ */
24
+ public function prepare_notification_data( $notification ) {
25
+ $data = array();
26
+ $report = $this->get_report( 'overview' );
27
+ $data['bounce_rate'] = isset( $report['data']['infobox']['bounce']['value'] ) ? $report['data']['infobox']['bounce']['value'] : 0;
28
+
29
+ if ( ! empty( $data ) && $data['bounce_rate'] > 70 ) {
30
+ $notification['title'] = __( 'Your website bounce rate is higher than 70%', 'google-analytics-dashboard-for-wp' );
31
+ // Translators: Bounce rate notification content.
32
+ $notification['content'] = sprintf( __( 'Your website bounce rate is %1$s. High bounce rates can hurt your site’s conversions rates. A high bounce rate might mean that people aren\'t finding what they\'re looking for on your site. %2$sHere%3$s are some points to remember and steps to follow to get your bounce rates back to manageable levels.', 'google-analytics-dashboard-for-wp' ), $data['bounce_rate'], '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/how-to-reduce-bounce-rate/' ) . '" target="_blank">', '</a>' );
33
+ $notification['btns'] = array(
34
+ 'view_report' => array(
35
+ 'url' => $this->get_view_url( 'exactmetrics-report-infobox-bounce-rate', 'exactmetrics_reports' ),
36
+ 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' ),
37
+ ),
38
+ 'learn_more' => array(
39
+ 'url' => $this->build_external_link( 'https://www.exactmetrics.com/how-to-reduce-bounce-rate/' ),
40
+ 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
41
+ 'is_external' => true,
42
+ ),
43
+ );
44
+
45
+ return $notification;
46
+ }
47
+
48
+ return false;
49
+ }
50
+
51
+ }
52
+
53
+ // initialize the class
54
+ new ExactMetrics_Notification_Bounce_Rate();
includes/admin/notifications/notification-events.php CHANGED
@@ -1,17 +1,17 @@
1
- <?php
2
-
3
- $base = ExactMetrics();
4
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-visitors.php';
5
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-audience.php';
6
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-mobile-device.php';
7
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-to-pro.php';
8
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-bounce-rate.php';
9
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-returning-visitors.php';
10
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-traffic-dropping.php';
11
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-search-console.php';
12
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-form-conversion.php';
13
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-email-summaries.php';
14
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-google-optimize.php';
15
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-to-add-more-file-extensions.php';
16
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-to-setup-affiliate-links.php';
17
- require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-headline-analyzer.php';
1
+ <?php
2
+
3
+ $base = ExactMetrics();
4
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-visitors.php';
5
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-audience.php';
6
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-mobile-device.php';
7
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-to-pro.php';
8
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-bounce-rate.php';
9
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-returning-visitors.php';
10
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-traffic-dropping.php';
11
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-search-console.php';
12
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-form-conversion.php';
13
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-email-summaries.php';
14
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-upgrade-for-google-optimize.php';
15
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-to-add-more-file-extensions.php';
16
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-to-setup-affiliate-links.php';
17
+ require_once plugin_dir_path( $base->file ) . '/includes/admin/notifications/notification-headline-analyzer.php';
includes/admin/notifications/notification-headline-analyzer.php CHANGED
@@ -1,41 +1,41 @@
1
- <?php
2
-
3
- /**
4
- * Add notification for headline analyzer
5
- * Recurrence: 60 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Headline_Analyzer extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_headline_analyzer';
12
- public $notification_interval = 60; // in days
13
- public $notification_first_run_time = '+7 day';
14
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
15
-
16
- /**
17
- * Build Notification
18
- *
19
- * @return array $notification notification is ready to add
20
- *
21
- * @since 7.12.3
22
- */
23
- public function prepare_notification_data( $notification ) {
24
- $notification['title'] = __( 'Headline Analyzer to Boost Your Clicks & Traffic', 'google-analytics-dashboard-for-wp' );
25
- // Translators: Headline Analyzer notification content.
26
- $notification['content'] = sprintf( __( 'Did you know that 36%% of SEO experts think the headline is the most important SEO element? Yet many website owners don’t know how to optimize their headlines for SEO and clicks. Instead, they write copy and hope for the best, only to see disappointing results. Now there’s an easier way! <br><br>%1$sWith the ExactMetrics Headline Analyzer%2$s, you can get targeted suggestions to improve your headlines, right in the WordPress editor.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/introducing-exactmetrics-built-in-headline-analyzer/' ) . '" target="_blank">', '</a>' );
27
- $notification['btns'] = array(
28
- "learn_more" => array(
29
- 'url' => $this->build_external_link( 'https://www.exactmetrics.com/introducing-exactmetrics-built-in-headline-analyzer/' ),
30
- 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
31
- 'is_external' => true,
32
- ),
33
- );
34
-
35
- return $notification;
36
- }
37
-
38
- }
39
-
40
- // initialize the class
41
- new ExactMetrics_Notification_Headline_Analyzer();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification for headline analyzer
5
+ * Recurrence: 60 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Headline_Analyzer extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_headline_analyzer';
12
+ public $notification_interval = 60; // in days
13
+ public $notification_first_run_time = '+7 day';
14
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
15
+
16
+ /**
17
+ * Build Notification
18
+ *
19
+ * @return array $notification notification is ready to add
20
+ *
21
+ * @since 7.12.3
22
+ */
23
+ public function prepare_notification_data( $notification ) {
24
+ $notification['title'] = __( 'Headline Analyzer to Boost Your Clicks & Traffic', 'google-analytics-dashboard-for-wp' );
25
+ // Translators: Headline Analyzer notification content.
26
+ $notification['content'] = sprintf( __( 'Did you know that 36%% of SEO experts think the headline is the most important SEO element? Yet many website owners don’t know how to optimize their headlines for SEO and clicks. Instead, they write copy and hope for the best, only to see disappointing results. Now there’s an easier way! <br><br>%1$sWith the ExactMetrics Headline Analyzer%2$s, you can get targeted suggestions to improve your headlines, right in the WordPress editor.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/introducing-exactmetrics-built-in-headline-analyzer/' ) . '" target="_blank">', '</a>' );
27
+ $notification['btns'] = array(
28
+ "learn_more" => array(
29
+ 'url' => $this->build_external_link( 'https://www.exactmetrics.com/introducing-exactmetrics-built-in-headline-analyzer/' ),
30
+ 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
31
+ 'is_external' => true,
32
+ ),
33
+ );
34
+
35
+ return $notification;
36
+ }
37
+
38
+ }
39
+
40
+ // initialize the class
41
+ new ExactMetrics_Notification_Headline_Analyzer();
includes/admin/notifications/notification-mobile-device.php CHANGED
@@ -1,53 +1,53 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when percentage of visitors from mobile device is less than 10%
5
- * Recurrence: 15 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Mobile_Device extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_mobile_device';
12
- public $notification_interval = 15; // in days
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
-
15
- /**
16
- * Prepare Notification
17
- *
18
- * @return array $notification notification is ready to add
19
- *
20
- * @since 7.12.3
21
- */
22
- public function prepare_notification_data( $notification ) {
23
- $data = array();
24
- $report = $this->get_report( 'overview', $this->report_start_from, $this->report_end_to );
25
- $data['percentage_of_mobile_visitors'] = isset( $report['data']['devices']['mobile'] ) ? $report['data']['devices']['mobile'] : 0;
26
-
27
- if ( ! empty( $data ) && $data['percentage_of_mobile_visitors'] < 10 ) {
28
- // Translators: Mobile device notification title
29
- $notification['title'] = sprintf( __( 'Traffic from Mobile Devices is %s%%', 'google-analytics-dashboard-for-wp' ), $data['percentage_of_mobile_visitors'] );
30
- // Translators: Mobile device notification content
31
- $notification['content'] = sprintf( __( 'Traffic from mobile devices is considerably lower on your site compared to desktop devices. This could be an indicator that your site is not optimised for mobile devices.<br><br>Take a look now at %show your site looks%s on mobile and make sure all your content can be accessed correctly.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.wpbeginner.com/beginners-guide/how-to-preview-the-mobile-layout-of-your-site/' ) . '" target="_blank">', '</a>' );
32
- $notification['btns'] = array(
33
- "view_report" => array(
34
- 'url' => $this->get_view_url( 'devices', 'exactmetrics_reports' ),
35
- 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
36
- ),
37
- "learn_more" => array(
38
- 'url' => $this->build_external_link( 'https://www.wpbeginner.com/beginners-guide/how-to-preview-the-mobile-layout-of-your-site/' ),
39
- 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
40
- 'is_external' => true,
41
- ),
42
- );
43
-
44
- return $notification;
45
- }
46
-
47
- return false;
48
- }
49
-
50
- }
51
-
52
- // initialize the class
53
- new ExactMetrics_Notification_Mobile_Device();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when percentage of visitors from mobile device is less than 10%
5
+ * Recurrence: 15 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Mobile_Device extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_mobile_device';
12
+ public $notification_interval = 15; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+
15
+ /**
16
+ * Prepare Notification
17
+ *
18
+ * @return array $notification notification is ready to add
19
+ *
20
+ * @since 7.12.3
21
+ */
22
+ public function prepare_notification_data( $notification ) {
23
+ $data = array();
24
+ $report = $this->get_report( 'overview', $this->report_start_from, $this->report_end_to );
25
+ $data['percentage_of_mobile_visitors'] = isset( $report['data']['devices']['mobile'] ) ? $report['data']['devices']['mobile'] : 0;
26
+
27
+ if ( ! empty( $data ) && $data['percentage_of_mobile_visitors'] < 10 ) {
28
+ // Translators: Mobile device notification title
29
+ $notification['title'] = sprintf( __( 'Traffic from Mobile Devices is %s%%', 'google-analytics-dashboard-for-wp' ), $data['percentage_of_mobile_visitors'] );
30
+ // Translators: Mobile device notification content
31
+ $notification['content'] = sprintf( __( 'Traffic from mobile devices is considerably lower on your site compared to desktop devices. This could be an indicator that your site is not optimised for mobile devices.<br><br>Take a look now at %show your site looks%s on mobile and make sure all your content can be accessed correctly.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.wpbeginner.com/beginners-guide/how-to-preview-the-mobile-layout-of-your-site/' ) . '" target="_blank">', '</a>' );
32
+ $notification['btns'] = array(
33
+ "view_report" => array(
34
+ 'url' => $this->get_view_url( 'devices', 'exactmetrics_reports' ),
35
+ 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
36
+ ),
37
+ "learn_more" => array(
38
+ 'url' => $this->build_external_link( 'https://www.wpbeginner.com/beginners-guide/how-to-preview-the-mobile-layout-of-your-site/' ),
39
+ 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
40
+ 'is_external' => true,
41
+ ),
42
+ );
43
+
44
+ return $notification;
45
+ }
46
+
47
+ return false;
48
+ }
49
+
50
+ }
51
+
52
+ // initialize the class
53
+ new ExactMetrics_Notification_Mobile_Device();
includes/admin/notifications/notification-returning-visitors.php CHANGED
@@ -1,53 +1,53 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when returning visitor rate is lower than 10%
5
- * Recurrence: 15 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Returning_Visitors extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_returning_visitors';
12
- public $notification_interval = 15; // in days
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
-
15
- /**
16
- * Build Notification
17
- *
18
- * @return array $notification notification is ready to add
19
- *
20
- * @since 7.12.3
21
- */
22
- public function prepare_notification_data( $notification ) {
23
- $data = array();
24
- $report = $this->get_report( 'overview', $this->report_start_from, $this->report_end_to );
25
- $data['returning'] = isset( $report['data']['newvsreturn']['returning'] ) ? $report['data']['newvsreturn']['returning'] : 0;
26
-
27
- if ( ! empty( $data ) && $data['returning'] < 10 ) {
28
- // Translators: Returning visitors notification title
29
- $notification['title'] = sprintf( __( 'Only %s%% of your visitors return to your site', 'google-analytics-dashboard-for-wp' ), $data['returning'] );
30
- // Translators: Returning visitors notification content
31
- $notification['content'] = sprintf( __( 'For any website, returning visitors are important because they indicate how successful your marketing campaigns are, who are your loyal customers, and how powerful your brand is. %sIn this article%s, we’ll show you 7 proven ways to increase your returning visitor rate.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/proven-ways-to-increase-your-returning-visitor-rate/' ) . '" target="_blank">', '</a>' );
32
- $notification['btns'] = array(
33
- "view_report" => array(
34
- 'url' => $this->get_view_url( 'newvsreturning', 'exactmetrics_reports' ),
35
- 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
36
- ),
37
- "learn_more" => array(
38
- 'url' => $this->build_external_link( 'https://www.exactmetrics.com/proven-ways-to-increase-your-returning-visitor-rate/' ),
39
- 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
40
- 'is_external' => true,
41
- ),
42
- );
43
-
44
- return $notification;
45
- }
46
-
47
- return false;
48
- }
49
-
50
- }
51
-
52
- // initialize the class
53
- new ExactMetrics_Notification_Returning_Visitors();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when returning visitor rate is lower than 10%
5
+ * Recurrence: 15 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Returning_Visitors extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_returning_visitors';
12
+ public $notification_interval = 15; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+
15
+ /**
16
+ * Build Notification
17
+ *
18
+ * @return array $notification notification is ready to add
19
+ *
20
+ * @since 7.12.3
21
+ */
22
+ public function prepare_notification_data( $notification ) {
23
+ $data = array();
24
+ $report = $this->get_report( 'overview', $this->report_start_from, $this->report_end_to );
25
+ $data['returning'] = isset( $report['data']['newvsreturn']['returning'] ) ? $report['data']['newvsreturn']['returning'] : 0;
26
+
27
+ if ( ! empty( $data ) && $data['returning'] < 10 ) {
28
+ // Translators: Returning visitors notification title
29
+ $notification['title'] = sprintf( __( 'Only %s%% of your visitors return to your site', 'google-analytics-dashboard-for-wp' ), $data['returning'] );
30
+ // Translators: Returning visitors notification content
31
+ $notification['content'] = sprintf( __( 'For any website, returning visitors are important because they indicate how successful your marketing campaigns are, who are your loyal customers, and how powerful your brand is. %sIn this article%s, we’ll show you 7 proven ways to increase your returning visitor rate.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/proven-ways-to-increase-your-returning-visitor-rate/' ) . '" target="_blank">', '</a>' );
32
+ $notification['btns'] = array(
33
+ "view_report" => array(
34
+ 'url' => $this->get_view_url( 'newvsreturning', 'exactmetrics_reports' ),
35
+ 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
36
+ ),
37
+ "learn_more" => array(
38
+ 'url' => $this->build_external_link( 'https://www.exactmetrics.com/proven-ways-to-increase-your-returning-visitor-rate/' ),
39
+ 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
40
+ 'is_external' => true,
41
+ ),
42
+ );
43
+
44
+ return $notification;
45
+ }
46
+
47
+ return false;
48
+ }
49
+
50
+ }
51
+
52
+ // initialize the class
53
+ new ExactMetrics_Notification_Returning_Visitors();
includes/admin/notifications/notification-to-add-more-file-extensions.php CHANGED
@@ -1,48 +1,48 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when there is no extension added or only the default extensions exist
5
- * Recurrence: 20 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_To_Add_More_File_Extensions extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_to_add_more_file_extensions';
12
- public $notification_interval = 20; // in days
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
-
15
- /**
16
- * Build Notification
17
- *
18
- * @return array $notification notification is ready to add
19
- *
20
- * @since 7.12.3
21
- */
22
- public function prepare_notification_data( $notification ) {
23
- $download_extensions = exactmetrics_get_option( 'extensions_of_files', '' );
24
-
25
- if ( empty( $download_extensions ) || "doc,pdf,ppt,zip,xls,docx,pptx,xlsx" === $download_extensions ) {
26
-
27
- $settings_url = is_network_admin() ? $this->get_view_url( 'exactmetrics-settings-block-file-downloads', 'exactmetrics_network', 'engagement' ) : $this->get_view_url( 'exactmetrics-settings-block-file-downloads', 'exactmetrics_settings', 'engagement' );
28
- $publishers_report_url = $this->get_view_url( 'exactmetrics-report-download-links', 'exactmetrics_reports', 'publishers' );
29
- $notification['title'] = __( 'Add More File Extensions to Track as Downloads', 'google-analytics-dashboard-for-wp' );
30
- // Translators: File extensions notification content
31
- $notification['content'] = sprintf( __( 'By default, ExactMetrics automatically tracks downloads of the following file extensions: doc, pdf, ppt, zip, xls, docx, pptx, and xlsx. You can easily add or remove extensions from that list in the %sEngagement settings%s of ExactMetrics.<br><br> You can view your Top Downloads report directly in the ExactMetrics %sPublishers report%s.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $settings_url . '">', '</a>', '<a href="' . $publishers_report_url . '">', '</a>' );
32
- $notification['btns'] = array(
33
- "add_more_file_extensions" => array(
34
- 'url' => $settings_url,
35
- 'text' => __( 'Add File Extensions', 'google-analytics-dashboard-for-wp' )
36
- ),
37
- );
38
-
39
- return $notification;
40
- }
41
-
42
- return false;
43
- }
44
-
45
- }
46
-
47
- // initialize the class
48
- new ExactMetrics_Notification_To_Add_More_File_Extensions();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when there is no extension added or only the default extensions exist
5
+ * Recurrence: 20 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_To_Add_More_File_Extensions extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_to_add_more_file_extensions';
12
+ public $notification_interval = 20; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+
15
+ /**
16
+ * Build Notification
17
+ *
18
+ * @return array $notification notification is ready to add
19
+ *
20
+ * @since 7.12.3
21
+ */
22
+ public function prepare_notification_data( $notification ) {
23
+ $download_extensions = exactmetrics_get_option( 'extensions_of_files', '' );
24
+
25
+ if ( empty( $download_extensions ) || "doc,pdf,ppt,zip,xls,docx,pptx,xlsx" === $download_extensions ) {
26
+
27
+ $settings_url = is_network_admin() ? $this->get_view_url( 'exactmetrics-settings-block-file-downloads', 'exactmetrics_network', 'engagement' ) : $this->get_view_url( 'exactmetrics-settings-block-file-downloads', 'exactmetrics_settings', 'engagement' );
28
+ $publishers_report_url = $this->get_view_url( 'exactmetrics-report-download-links', 'exactmetrics_reports', 'publishers' );
29
+ $notification['title'] = __( 'Add More File Extensions to Track as Downloads', 'google-analytics-dashboard-for-wp' );
30
+ // Translators: File extensions notification content
31
+ $notification['content'] = sprintf( __( 'By default, ExactMetrics automatically tracks downloads of the following file extensions: doc, pdf, ppt, zip, xls, docx, pptx, and xlsx. You can easily add or remove extensions from that list in the %sEngagement settings%s of ExactMetrics.<br><br> You can view your Top Downloads report directly in the ExactMetrics %sPublishers report%s.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $settings_url . '">', '</a>', '<a href="' . $publishers_report_url . '">', '</a>' );
32
+ $notification['btns'] = array(
33
+ "add_more_file_extensions" => array(
34
+ 'url' => $settings_url,
35
+ 'text' => __( 'Add File Extensions', 'google-analytics-dashboard-for-wp' )
36
+ ),
37
+ );
38
+
39
+ return $notification;
40
+ }
41
+
42
+ return false;
43
+ }
44
+
45
+ }
46
+
47
+ // initialize the class
48
+ new ExactMetrics_Notification_To_Add_More_File_Extensions();
includes/admin/notifications/notification-to-setup-affiliate-links.php CHANGED
@@ -1,54 +1,54 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when no links set up for Affiliate tracking or just the default links exist
5
- * Recurrence: 25 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_To_Setup_Affiliate_Links extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_to_setup_affiliate_links';
12
- public $notification_interval = 25; // in days
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
-
15
- /**
16
- * Build Notification
17
- *
18
- * @return array $notification notification is ready to add
19
- *
20
- * @since 7.12.3
21
- */
22
- public function prepare_notification_data( $notification ) {
23
- $affiliate_links = exactmetrics_get_option( 'affiliate_links', array() );
24
- $no_new_links = false;
25
-
26
- if ( is_array( $affiliate_links ) && ! empty( $affiliate_links ) ) {
27
- if ( 2 === count( $affiliate_links ) && isset( $affiliate_links[0]['path'] ) && isset( $affiliate_links[1]['path'] ) ) {
28
- $no_new_links = "/go/" === $affiliate_links[0]['path'] && "/recommend/" === $affiliate_links[1]['path'] ? true : false;
29
- }
30
- }
31
-
32
- if ( true === $no_new_links || ( is_array( $affiliate_links ) && empty( $affiliate_links ) ) ) {
33
-
34
- $notification['title'] = __( 'Set Up Affiliate Link Tracking', 'google-analytics-dashboard-for-wp' );
35
- // Translators: Set up affiliate links notification content
36
- $notification['content'] = sprintf( __( 'By tracking your affiliate links in Google Analytics, you can gather all the data you need to optimize your links for maximizing affiliate revenue. You can track affiliate link clicks on your website with little configuration needed.<br><br>%sIn this article%s, we’ll show you how to set up affiliate link tracking in WordPress.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/how-to-set-up-affiliate-link-tracking-in-wordpress/' ) . '" target="_blank">', '</a>' );
37
- $notification['btns'] = array(
38
- "read_more" => array(
39
- 'url' => $this->build_external_link( 'https://www.exactmetrics.com/how-to-set-up-affiliate-link-tracking-in-wordpress/' ),
40
- 'text' => __( 'Read More', 'google-analytics-dashboard-for-wp' ),
41
- 'is_external' => true,
42
- ),
43
- );
44
-
45
- return $notification;
46
- }
47
-
48
- return false;
49
- }
50
-
51
- }
52
-
53
- // initialize the class
54
- new ExactMetrics_Notification_To_Setup_Affiliate_Links();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when no links set up for Affiliate tracking or just the default links exist
5
+ * Recurrence: 25 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_To_Setup_Affiliate_Links extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_to_setup_affiliate_links';
12
+ public $notification_interval = 25; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+
15
+ /**
16
+ * Build Notification
17
+ *
18
+ * @return array $notification notification is ready to add
19
+ *
20
+ * @since 7.12.3
21
+ */
22
+ public function prepare_notification_data( $notification ) {
23
+ $affiliate_links = exactmetrics_get_option( 'affiliate_links', array() );
24
+ $no_new_links = false;
25
+
26
+ if ( is_array( $affiliate_links ) && ! empty( $affiliate_links ) ) {
27
+ if ( 2 === count( $affiliate_links ) && isset( $affiliate_links[0]['path'] ) && isset( $affiliate_links[1]['path'] ) ) {
28
+ $no_new_links = "/go/" === $affiliate_links[0]['path'] && "/recommend/" === $affiliate_links[1]['path'] ? true : false;
29
+ }
30
+ }
31
+
32
+ if ( true === $no_new_links || ( is_array( $affiliate_links ) && empty( $affiliate_links ) ) ) {
33
+
34
+ $notification['title'] = __( 'Set Up Affiliate Link Tracking', 'google-analytics-dashboard-for-wp' );
35
+ // Translators: Set up affiliate links notification content
36
+ $notification['content'] = sprintf( __( 'By tracking your affiliate links in Google Analytics, you can gather all the data you need to optimize your links for maximizing affiliate revenue. You can track affiliate link clicks on your website with little configuration needed.<br><br>%sIn this article%s, we’ll show you how to set up affiliate link tracking in WordPress.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/how-to-set-up-affiliate-link-tracking-in-wordpress/' ) . '" target="_blank">', '</a>' );
37
+ $notification['btns'] = array(
38
+ "read_more" => array(
39
+ 'url' => $this->build_external_link( 'https://www.exactmetrics.com/how-to-set-up-affiliate-link-tracking-in-wordpress/' ),
40
+ 'text' => __( 'Read More', 'google-analytics-dashboard-for-wp' ),
41
+ 'is_external' => true,
42
+ ),
43
+ );
44
+
45
+ return $notification;
46
+ }
47
+
48
+ return false;
49
+ }
50
+
51
+ }
52
+
53
+ // initialize the class
54
+ new ExactMetrics_Notification_To_Setup_Affiliate_Links();
includes/admin/notifications/notification-traffic-dropping.php CHANGED
@@ -1,52 +1,52 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when the number of total sessions is less than the previous 30 days.
5
- * Recurrence: 30 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Traffic_Dropping extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_traffic_dropping';
12
- public $notification_interval = 30; // in days
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
-
15
- /**
16
- * Build Notification
17
- *
18
- * @return array $notification notification is ready to add
19
- *
20
- * @since 7.12.3
21
- */
22
- public function prepare_notification_data( $notification ) {
23
- $data = array();
24
- $report = $this->get_report();
25
- $data['prev_sessions_difference'] = isset( $report['data']['infobox']['sessions']['prev'] ) ? $report['data']['infobox']['sessions']['prev'] : 0;
26
-
27
- if ( ! empty( $data ) && $data['prev_sessions_difference'] < 0 ) {
28
- $notification['title'] = __( 'Your Website Traffic Is Dropping', 'google-analytics-dashboard-for-wp' );
29
- // Translators: Traffic dropping notification content
30
- $notification['content'] = sprintf( __( 'Your website traffic is decreasing and that’s a reason to take action now. Less traffic means less opportunities to make your brand known, make relationships and ultimately sell your service or product. <br><br>Follow the marketing hacks of %sthis article%s to start growing your traffic again.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/marketing-hacks-guaranteed-to-grow-your-traffic/' ) . '" target="_blank">', '</a>' );
31
- $notification['btns'] = array(
32
- "learn_more" => array(
33
- 'url' => $this->build_external_link( 'https://www.exactmetrics.com/marketing-hacks-guaranteed-to-grow-your-traffic/' ),
34
- 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
35
- 'is_external' => true,
36
- ),
37
- "view_report" => array(
38
- 'url' => $this->get_view_url( 'exactmetrics-report-overview', 'exactmetrics_reports' ),
39
- 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
40
- ),
41
- );
42
-
43
- return $notification;
44
- }
45
-
46
- return false;
47
- }
48
-
49
- }
50
-
51
- // initialize the class
52
- new ExactMetrics_Notification_Traffic_Dropping();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when the number of total sessions is less than the previous 30 days.
5
+ * Recurrence: 30 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Traffic_Dropping extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_traffic_dropping';
12
+ public $notification_interval = 30; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+
15
+ /**
16
+ * Build Notification
17
+ *
18
+ * @return array $notification notification is ready to add
19
+ *
20
+ * @since 7.12.3
21
+ */
22
+ public function prepare_notification_data( $notification ) {
23
+ $data = array();
24
+ $report = $this->get_report();
25
+ $data['prev_sessions_difference'] = isset( $report['data']['infobox']['sessions']['prev'] ) ? $report['data']['infobox']['sessions']['prev'] : 0;
26
+
27
+ if ( ! empty( $data ) && $data['prev_sessions_difference'] < 0 ) {
28
+ $notification['title'] = __( 'Your Website Traffic Is Dropping', 'google-analytics-dashboard-for-wp' );
29
+ // Translators: Traffic dropping notification content
30
+ $notification['content'] = sprintf( __( 'Your website traffic is decreasing and that’s a reason to take action now. Less traffic means less opportunities to make your brand known, make relationships and ultimately sell your service or product. <br><br>Follow the marketing hacks of %sthis article%s to start growing your traffic again.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/marketing-hacks-guaranteed-to-grow-your-traffic/' ) . '" target="_blank">', '</a>' );
31
+ $notification['btns'] = array(
32
+ "learn_more" => array(
33
+ 'url' => $this->build_external_link( 'https://www.exactmetrics.com/marketing-hacks-guaranteed-to-grow-your-traffic/' ),
34
+ 'text' => __( 'Learn More', 'google-analytics-dashboard-for-wp' ),
35
+ 'is_external' => true,
36
+ ),
37
+ "view_report" => array(
38
+ 'url' => $this->get_view_url( 'exactmetrics-report-overview', 'exactmetrics_reports' ),
39
+ 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' )
40
+ ),
41
+ );
42
+
43
+ return $notification;
44
+ }
45
+
46
+ return false;
47
+ }
48
+
49
+ }
50
+
51
+ // initialize the class
52
+ new ExactMetrics_Notification_Traffic_Dropping();
includes/admin/notifications/notification-upgrade-for-email-summaries.php CHANGED
@@ -1,41 +1,41 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when lite version activated
5
- * Recurrence: 30 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Upgrade_For_Email_Summaries extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_upgrade_for_email_summaries';
12
- public $notification_interval = 30; // in days
13
- public $notification_type = array( 'lite' );
14
- public $notification_icon = 'warning';
15
-
16
- /**
17
- * Build Notification
18
- *
19
- * @return array $notification notification is ready to add
20
- *
21
- * @since 7.12.3
22
- */
23
- public function prepare_notification_data( $notification ) {
24
- $notification['title'] = __( 'Upgrade to ExactMetrics Pro to get weekly email reports', 'google-analytics-dashboard-for-wp' );
25
- // Translators: upgrade for email summaries notification content
26
- $notification['content'] = sprintf( __( 'Wouldn’t it be easy if you could get your website’s performance report in your email inbox every week? With our new feature, Email Summaries, you can now view all your important stats in a simple report that’s delivered straight to your inbox. <br><br>You get an overview of your site\'s performance without logging in to WordPress or going through different Analytics reports. %sUpgrade to ExactMetrics Pro%s to enable the Email Summaries feature.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>' );
27
- $notification['btns'] = array(
28
- "get_exactmetrics_pro" => array(
29
- 'url' => $this->get_upgrade_url(),
30
- 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
- 'is_external' => true,
32
- ),
33
- );
34
-
35
- return $notification;
36
- }
37
-
38
- }
39
-
40
- // initialize the class
41
- new ExactMetrics_Notification_Upgrade_For_Email_Summaries();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when lite version activated
5
+ * Recurrence: 30 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Upgrade_For_Email_Summaries extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_upgrade_for_email_summaries';
12
+ public $notification_interval = 30; // in days
13
+ public $notification_type = array( 'lite' );
14
+ public $notification_icon = 'warning';
15
+
16
+ /**
17
+ * Build Notification
18
+ *
19
+ * @return array $notification notification is ready to add
20
+ *
21
+ * @since 7.12.3
22
+ */
23
+ public function prepare_notification_data( $notification ) {
24
+ $notification['title'] = __( 'Upgrade to ExactMetrics Pro to get weekly email reports', 'google-analytics-dashboard-for-wp' );
25
+ // Translators: upgrade for email summaries notification content
26
+ $notification['content'] = sprintf( __( 'Wouldn’t it be easy if you could get your website’s performance report in your email inbox every week? With our new feature, Email Summaries, you can now view all your important stats in a simple report that’s delivered straight to your inbox. <br><br>You get an overview of your site\'s performance without logging in to WordPress or going through different Analytics reports. %sUpgrade to ExactMetrics Pro%s to enable the Email Summaries feature.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>' );
27
+ $notification['btns'] = array(
28
+ "get_exactmetrics_pro" => array(
29
+ 'url' => $this->get_upgrade_url(),
30
+ 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
+ 'is_external' => true,
32
+ ),
33
+ );
34
+
35
+ return $notification;
36
+ }
37
+
38
+ }
39
+
40
+ // initialize the class
41
+ new ExactMetrics_Notification_Upgrade_For_Email_Summaries();
includes/admin/notifications/notification-upgrade-for-form-conversion.php CHANGED
@@ -1,41 +1,41 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when lite version activated
5
- * Recurrence: 20 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Upgrade_For_Form_Conversion extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_upgrade_for_form_conversion';
12
- public $notification_interval = 20; // in days
13
- public $notification_type = array( 'basic', 'lite', 'plus' );
14
- public $notification_icon = 'warning';
15
-
16
- /**
17
- * Build Notification
18
- *
19
- * @return array $notification notification is ready to add
20
- *
21
- * @since 7.12.3
22
- */
23
- public function prepare_notification_data( $notification ) {
24
- $notification['title'] = __( 'Upgrade to ExactMetrics Pro to Track Form Conversion', 'google-analytics-dashboard-for-wp' );
25
- // Translators: upgrade for form conversion notification content
26
- $notification['content'] = sprintf( __( 'Forms are one of the most important points of interaction on your website. When a visitor fills out a form on your site, they’re taking the next step in their customer journey. That’s why it’s so crucial that your WordPress forms are optimized for conversions. Upgrade to %sExactMetrics Pro%s to track %sform conversions in Google Analytics.%s', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>', '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/addon/forms/' ) . '" target="_blank">', '</a>' );
27
- $notification['btns'] = array(
28
- "get_exactmetrics_pro" => array(
29
- 'url' => $this->get_upgrade_url(),
30
- 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
- 'is_external' => true,
32
- ),
33
- );
34
-
35
- return $notification;
36
- }
37
-
38
- }
39
-
40
- // initialize the class
41
- new ExactMetrics_Notification_Upgrade_For_Form_Conversion();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when lite version activated
5
+ * Recurrence: 20 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Upgrade_For_Form_Conversion extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_upgrade_for_form_conversion';
12
+ public $notification_interval = 20; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'plus' );
14
+ public $notification_icon = 'warning';
15
+
16
+ /**
17
+ * Build Notification
18
+ *
19
+ * @return array $notification notification is ready to add
20
+ *
21
+ * @since 7.12.3
22
+ */
23
+ public function prepare_notification_data( $notification ) {
24
+ $notification['title'] = __( 'Upgrade to ExactMetrics Pro to Track Form Conversion', 'google-analytics-dashboard-for-wp' );
25
+ // Translators: upgrade for form conversion notification content
26
+ $notification['content'] = sprintf( __( 'Forms are one of the most important points of interaction on your website. When a visitor fills out a form on your site, they’re taking the next step in their customer journey. That’s why it’s so crucial that your WordPress forms are optimized for conversions. Upgrade to %sExactMetrics Pro%s to track %sform conversions in Google Analytics.%s', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>', '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/addon/forms/' ) . '" target="_blank">', '</a>' );
27
+ $notification['btns'] = array(
28
+ "get_exactmetrics_pro" => array(
29
+ 'url' => $this->get_upgrade_url(),
30
+ 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
+ 'is_external' => true,
32
+ ),
33
+ );
34
+
35
+ return $notification;
36
+ }
37
+
38
+ }
39
+
40
+ // initialize the class
41
+ new ExactMetrics_Notification_Upgrade_For_Form_Conversion();
includes/admin/notifications/notification-upgrade-for-google-optimize.php CHANGED
@@ -1,41 +1,41 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when lite, plus version activated
5
- * Recurrence: 30 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Upgrade_For_Google_Optimize extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_upgrade_for_google_optimize';
12
- public $notification_interval = 30; // in days
13
- public $notification_type = array( 'basic', 'lite', 'plus' );
14
- public $notification_icon = 'warning';
15
-
16
- /**
17
- * Build Notification
18
- *
19
- * @return array $notification notification is ready to add
20
- *
21
- * @since 7.12.3
22
- */
23
- public function prepare_notification_data( $notification ) {
24
- $notification['title'] = __( 'Upgrade to ExactMetrics Pro to Enable Google Optimize', 'google-analytics-dashboard-for-wp' );
25
- // Translators: upgrade for google optimize notification content
26
- $notification['content'] = sprintf( __( '%sGoogle Optimize%s is a free A/B testing and personalization product by Google that lets you easily conduct experiments to see what works best on your site. With Google Optimize, you can use split testing and personalization to create online experiences that engage and delight your customers. %sUpgrade to ExactMetrics Pro%s to unlock the Google Optimize addon.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/addon/google-optimize/' ) . '" target="_blank">', '</a>', '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>' );
27
- $notification['btns'] = array(
28
- "get_exactmetrics_pro" => array(
29
- 'url' => $this->get_upgrade_url(),
30
- 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
- 'is_external' => true,
32
- ),
33
- );
34
-
35
- return $notification;
36
- }
37
-
38
- }
39
-
40
- // initialize the class
41
- new ExactMetrics_Notification_Upgrade_For_Google_Optimize();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when lite, plus version activated
5
+ * Recurrence: 30 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Upgrade_For_Google_Optimize extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_upgrade_for_google_optimize';
12
+ public $notification_interval = 30; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'plus' );
14
+ public $notification_icon = 'warning';
15
+
16
+ /**
17
+ * Build Notification
18
+ *
19
+ * @return array $notification notification is ready to add
20
+ *
21
+ * @since 7.12.3
22
+ */
23
+ public function prepare_notification_data( $notification ) {
24
+ $notification['title'] = __( 'Upgrade to ExactMetrics Pro to Enable Google Optimize', 'google-analytics-dashboard-for-wp' );
25
+ // Translators: upgrade for google optimize notification content
26
+ $notification['content'] = sprintf( __( '%sGoogle Optimize%s is a free A/B testing and personalization product by Google that lets you easily conduct experiments to see what works best on your site. With Google Optimize, you can use split testing and personalization to create online experiences that engage and delight your customers. %sUpgrade to ExactMetrics Pro%s to unlock the Google Optimize addon.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/addon/google-optimize/' ) . '" target="_blank">', '</a>', '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>' );
27
+ $notification['btns'] = array(
28
+ "get_exactmetrics_pro" => array(
29
+ 'url' => $this->get_upgrade_url(),
30
+ 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
+ 'is_external' => true,
32
+ ),
33
+ );
34
+
35
+ return $notification;
36
+ }
37
+
38
+ }
39
+
40
+ // initialize the class
41
+ new ExactMetrics_Notification_Upgrade_For_Google_Optimize();
includes/admin/notifications/notification-upgrade-for-search-console.php CHANGED
@@ -1,41 +1,41 @@
1
- <?php
2
-
3
- /**
4
- * Add notification when lite version activated
5
- * Recurrence: 30 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Upgrade_For_Search_Console extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_upgrade_for_search_console_report';
12
- public $notification_interval = 30; // in days
13
- public $notification_type = array( 'lite' );
14
- public $notification_icon = 'warning';
15
-
16
- /**
17
- * Build Notification
18
- *
19
- * @return array $notification notification is ready to add
20
- *
21
- * @since 7.12.3
22
- */
23
- public function prepare_notification_data( $notification ) {
24
- $notification['title'] = __( 'Get access to Google Search Keywords data by upgrading to ExactMetrics Pro', 'google-analytics-dashboard-for-wp' );
25
- // Translators: upgrade for search console notification content
26
- $notification['content'] = sprintf( __( 'Do you want to find out which search terms from Google bring your site the most visitors? %sUpgrade to ExactMetrics PRO%s today and get access to the %sSearch Console Report%s and more directly in your WordPress admin.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>', '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/feature/search-console-report/' ) . '" target="_blank">', '</a>' );
27
- $notification['btns'] = array(
28
- "get_exactmetrics_pro" => array(
29
- 'url' => $this->get_upgrade_url(),
30
- 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
- 'is_external' => true,
32
- ),
33
- );
34
-
35
- return $notification;
36
- }
37
-
38
- }
39
-
40
- // initialize the class
41
- new ExactMetrics_Notification_Upgrade_For_Search_Console();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification when lite version activated
5
+ * Recurrence: 30 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Upgrade_For_Search_Console extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_upgrade_for_search_console_report';
12
+ public $notification_interval = 30; // in days
13
+ public $notification_type = array( 'lite' );
14
+ public $notification_icon = 'warning';
15
+
16
+ /**
17
+ * Build Notification
18
+ *
19
+ * @return array $notification notification is ready to add
20
+ *
21
+ * @since 7.12.3
22
+ */
23
+ public function prepare_notification_data( $notification ) {
24
+ $notification['title'] = __( 'Get access to Google Search Keywords data by upgrading to ExactMetrics Pro', 'google-analytics-dashboard-for-wp' );
25
+ // Translators: upgrade for search console notification content
26
+ $notification['content'] = sprintf( __( 'Do you want to find out which search terms from Google bring your site the most visitors? %sUpgrade to ExactMetrics PRO%s today and get access to the %sSearch Console Report%s and more directly in your WordPress admin.', 'google-analytics-dashboard-for-wp' ), '<a href="' . $this->get_upgrade_url() . '" target="_blank">', '</a>', '<a href="' . $this->build_external_link( 'https://www.exactmetrics.com/feature/search-console-report/' ) . '" target="_blank">', '</a>' );
27
+ $notification['btns'] = array(
28
+ "get_exactmetrics_pro" => array(
29
+ 'url' => $this->get_upgrade_url(),
30
+ 'text' => __( 'Get ExactMetrics Pro', 'google-analytics-dashboard-for-wp' ),
31
+ 'is_external' => true,
32
+ ),
33
+ );
34
+
35
+ return $notification;
36
+ }
37
+
38
+ }
39
+
40
+ // initialize the class
41
+ new ExactMetrics_Notification_Upgrade_For_Search_Console();
includes/admin/notifications/notification-upgrade-to-pro.php CHANGED
@@ -1,42 +1,42 @@
1
- <?php
2
-
3
- /**
4
- * Add notification after 1 week of lite version installation
5
- * Recurrence: 40 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Upgrade_To_Pro extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_upgrade_to_pro';
12
- public $notification_interval = 40; // in days
13
- public $notification_first_run_time = '+7 day';
14
- public $notification_type = array( 'lite' );
15
- public $notification_icon = 'star';
16
-
17
- /**
18
- * Build Notification
19
- *
20
- * @return array $notification notification is ready to add
21
- *
22
- * @since 7.12.3
23
- */
24
- public function prepare_notification_data( $notification ) {
25
- $notification['title'] = __( 'Upgrade to ExactMetrics Pro and unlock advanced tracking and reports', 'google-analytics-dashboard-for-wp' );
26
- // Translators: upgrade to pro notification content
27
- $notification['content'] = __( 'By upgrading to ExactMetrics Pro you get access to additional reports right in your WordPress dashboard and advanced tracking features like eCommerce, Custom Dimensions, Forms tracking and more!', 'google-analytics-dashboard-for-wp' );
28
- $notification['btns'] = array(
29
- "upgrade_to_pro" => array(
30
- 'url' => $this->get_upgrade_url(),
31
- 'text' => __( 'Upgrade to Pro', 'google-analytics-dashboard-for-wp' ),
32
- 'is_external' => true,
33
- ),
34
- );
35
-
36
- return $notification;
37
- }
38
-
39
- }
40
-
41
- // initialize the class
42
- new ExactMetrics_Notification_Upgrade_To_Pro();
1
+ <?php
2
+
3
+ /**
4
+ * Add notification after 1 week of lite version installation
5
+ * Recurrence: 40 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Upgrade_To_Pro extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_upgrade_to_pro';
12
+ public $notification_interval = 40; // in days
13
+ public $notification_first_run_time = '+7 day';
14
+ public $notification_type = array( 'lite' );
15
+ public $notification_icon = 'star';
16
+
17
+ /**
18
+ * Build Notification
19
+ *
20
+ * @return array $notification notification is ready to add
21
+ *
22
+ * @since 7.12.3
23
+ */
24
+ public function prepare_notification_data( $notification ) {
25
+ $notification['title'] = __( 'Upgrade to ExactMetrics Pro and unlock advanced tracking and reports', 'google-analytics-dashboard-for-wp' );
26
+ // Translators: upgrade to pro notification content
27
+ $notification['content'] = __( 'By upgrading to ExactMetrics Pro you get access to additional reports right in your WordPress dashboard and advanced tracking features like eCommerce, Custom Dimensions, Forms tracking and more!', 'google-analytics-dashboard-for-wp' );
28
+ $notification['btns'] = array(
29
+ "upgrade_to_pro" => array(
30
+ 'url' => $this->get_upgrade_url(),
31
+ 'text' => __( 'Upgrade to Pro', 'google-analytics-dashboard-for-wp' ),
32
+ 'is_external' => true,
33
+ ),
34
+ );
35
+
36
+ return $notification;
37
+ }
38
+
39
+ }
40
+
41
+ // initialize the class
42
+ new ExactMetrics_Notification_Upgrade_To_Pro();
includes/admin/notifications/notification-visitors.php CHANGED
@@ -1,50 +1,50 @@
1
- <?php
2
-
3
- /**
4
- * Add visitors notification
5
- * Recurrence: 30 Days
6
- *
7
- * @since 7.12.3
8
- */
9
- final class ExactMetrics_Notification_Visitors extends ExactMetrics_Notification_Event {
10
-
11
- public $notification_id = 'exactmetrics_notification_visitors';
12
- public $notification_interval = 30; // in days
13
- public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
- public $notification_icon = 'lightning';
15
-
16
- /**
17
- * Build Notification
18
- *
19
- * @param array $report Overview report
20
- *
21
- * @return array $notification notification is ready to add
22
- *
23
- * @since 7.12.3
24
- */
25
- public function prepare_notification_data( $notification ) {
26
- $report = $this->get_report();
27
-
28
- if ( ! is_array( $report ) || empty( $report ) ) {
29
- return false;
30
- }
31
-
32
- $total_visitors = isset( $report['data']['infobox']['sessions']['value'] ) ? $report['data']['infobox']['sessions']['value'] : 0;
33
- // Translators: visitors notification title
34
- $notification['title'] = sprintf( __( 'See how %s visitors found your site!', 'google-analytics-dashboard-for-wp' ), $total_visitors );
35
- // Translators: visitors notification content
36
- $notification['content'] = sprintf( __( 'Your website has been visited by %s visitors in the past 30 days. Click the button below to view the full analytics report.', 'google-analytics-dashboard-for-wp' ), $total_visitors );
37
- $notification['btns'] = array(
38
- "view_report" => array(
39
- 'url' => $this->get_view_url( 'exactmetrics-report-overview', 'exactmetrics_reports' ),
40
- 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' ),
41
- ),
42
- );
43
-
44
- return $notification;
45
- }
46
-
47
- }
48
-
49
- // initialize the class
50
- new ExactMetrics_Notification_Visitors();
1
+ <?php
2
+
3
+ /**
4
+ * Add visitors notification
5
+ * Recurrence: 30 Days
6
+ *
7
+ * @since 7.12.3
8
+ */
9
+ final class ExactMetrics_Notification_Visitors extends ExactMetrics_Notification_Event {
10
+
11
+ public $notification_id = 'exactmetrics_notification_visitors';
12
+ public $notification_interval = 30; // in days
13
+ public $notification_type = array( 'basic', 'lite', 'master', 'plus', 'pro' );
14
+ public $notification_icon = 'lightning';
15
+
16
+ /**
17
+ * Build Notification
18
+ *
19
+ * @param array $report Overview report
20
+ *
21
+ * @return array $notification notification is ready to add
22
+ *
23
+ * @since 7.12.3
24
+ */
25
+ public function prepare_notification_data( $notification ) {
26
+ $report = $this->get_report();
27
+
28
+ if ( ! is_array( $report ) || empty( $report ) ) {
29
+ return false;
30
+ }
31
+
32
+ $total_visitors = isset( $report['data']['infobox']['sessions']['value'] ) ? $report['data']['infobox']['sessions']['value'] : 0;
33
+ // Translators: visitors notification title
34
+ $notification['title'] = sprintf( __( 'See how %s visitors found your site!', 'google-analytics-dashboard-for-wp' ), $total_visitors );
35
+ // Translators: visitors notification content
36
+ $notification['content'] = sprintf( __( 'Your website has been visited by %s visitors in the past 30 days. Click the button below to view the full analytics report.', 'google-analytics-dashboard-for-wp' ), $total_visitors );
37
+ $notification['btns'] = array(
38
+ "view_report" => array(
39
+ 'url' => $this->get_view_url( 'exactmetrics-report-overview', 'exactmetrics_reports' ),
40
+ 'text' => __( 'View Report', 'google-analytics-dashboard-for-wp' ),
41
+ ),
42
+ );
43
+
44
+ return $notification;
45
+ }
46
+
47
+ }
48
+
49
+ // initialize the class
50
+ new ExactMetrics_Notification_Visitors();
includes/admin/sharedcount.php CHANGED
@@ -1,637 +1,637 @@
1
- <?php
2
-
3
- /**
4
- * Handles the SharedCount integration and count grabbing.
5
- *
6
- * Class ExactMetrics_SharedCount
7
- */
8
- class ExactMetrics_SharedCount {
9
-
10
- /**
11
- * The action used to schedule daily events.
12
- *
13
- * @var string
14
- */
15
- public $cron_key = 'exactmetrics_sharedcount_daily_update';
16
- /**
17
- * Index progress key.
18
- *
19
- * @var string
20
- */
21
- public static $progress_key = 'exactmetrics_sharedcount_index_progress';
22
- /**
23
- * Index progress.
24
- *
25
- * @var array
26
- */
27
- public static $progress;
28
- /**
29
- * The error message from the api call.
30
- *
31
- * @var string
32
- */
33
- public $error;
34
- /**
35
- * The API endpoint.
36
- *
37
- * @var string
38
- */
39
- private $endpoint = 'https://api.sharedcount.com/v1.0/';
40
- /**
41
- * The API key to use for the requests.
42
- *
43
- * @var string
44
- */
45
- private $api_key;
46
- /**
47
- * If the current query needs to run again.
48
- *
49
- * @var bool
50
- */
51
- private $more_pages = false;
52
-
53
- /**
54
- * ExactMetrics_SharedCount constructor.
55
- */
56
- public function __construct() {
57
-
58
- add_action( 'wp_ajax_exactmetrics_sharedcount_start_indexing', array( $this, 'ajax_start_indexing' ) );
59
- add_action( 'wp_ajax_exactmetrics_sharedcount_get_index_progress', array(
60
- $this,
61
- 'ajax_get_index_progress'
62
- ) );
63
-
64
- add_action( 'exactmetrics_sharedcount_get_more_posts', array( $this, 'get_more_counts' ) );
65
-
66
- add_action( 'exactmetrics_sharedcount_bulk_grab', array( $this, 'grab_and_store_bulk_by_id' ), 10, 2 );
67
-
68
- add_action( $this->cron_key, array( $this, 'daily_cron_update' ) );
69
- }
70
-
71
- /**
72
- * AJAX handler from the Vue app that checks if the API key is set and handles
73
- * an error message from the SharedCount API call. If the first call is successful it will schedule
74
- * a daily cron to keep the counts fresh.
75
- */
76
- public function ajax_start_indexing() {
77
-
78
- check_ajax_referer( 'mi-admin-nonce', 'nonce' );
79
-
80
- if ( $this->get_api_key() ) {
81
- if ( $this->start_posts_count() ) {
82
- $this->schedule_daily_update();
83
- wp_send_json_success( array(
84
- 'max_pages' => $this->more_pages,
85
- ) );
86
- } else {
87
- wp_send_json_error( array(
88
- 'message' => $this->error,
89
- ) );
90
- }
91
- }
92
-
93
- // No API key, let's send an error message.
94
- wp_send_json_error( array(
95
- 'message' => esc_html__( 'The SharedCount API key is not set', 'google-analytics-dashboard-for-wp' ),
96
- ) );
97
-
98
- }
99
-
100
- /**
101
- * Get the API key.
102
- *
103
- * @return string
104
- */
105
- public function get_api_key() {
106
-
107
- if ( empty( $this->api_key ) ) {
108
- $this->api_key = exactmetrics_get_option( 'sharedcount_key' );
109
- }
110
-
111
- return $this->api_key;
112
- }
113
-
114
- /**
115
- * Start a grabbing process that will schedule events to grab more pages if needed.
116
- *
117
- * @return bool
118
- */
119
- public function start_posts_count() {
120
-
121
- return $this->get_more_counts( 1 );
122
-
123
- }
124
-
125
- /**
126
- * Handler for the scheduled event to grab more data for sites with large number of posts.
127
- * This is also used by the first call and uses the return value to determine if an error was encountered.
128
- * The error gets set in the $error property and used for display.
129
- *
130
- * @param int $page The page number to grab counts for.
131
- *
132
- * @return bool
133
- */
134
- public function get_more_counts( $page ) {
135
-
136
- $urls = $this->get_post_urls( $page );
137
- $urls_as_keys = $this->urls_as_keys( $urls );
138
-
139
- if ( $this->use_bulk_api() ) {
140
- $bulk_request = $this->post_bulk_urls( $urls );
141
-
142
- if ( $bulk_request && ! empty( $bulk_request['bulk_id'] ) ) {
143
- $this->grab_and_store_bulk_by_id( $bulk_request['bulk_id'], $urls_as_keys );
144
- } else {
145
- return false;
146
- }
147
- } else {
148
- $store_counts = $this->grab_counts_one_by_one( $urls );
149
- if ( ! $store_counts ) {
150
- // Error encountered, return error.
151
- return false;
152
- }
153
- }
154
-
155
- $this->save_progress( $page, $this->more_pages );
156
-
157
- if ( $this->more_pages ) {
158
- $page ++;
159
- $this->schedule_next_page( $page );
160
- }
161
-
162
- return true;
163
-
164
- }
165
-
166
- /**
167
- * Save the current indexing progress.
168
- *
169
- * @param int $page The current page.
170
- * @param int $max_pages The total number of pages.
171
- */
172
- public function save_progress( $page, $max_pages ) {
173
- update_option( self::$progress_key, array(
174
- 'page' => $page,
175
- 'max_pages' => $max_pages,
176
- ), false );
177
- }
178
-
179
- /**
180
- * Reset the progress option. Used for when the cron is disabled.
181
- */
182
- public function reset_progress() {
183
- delete_option( self::$progress_key );
184
- }
185
-
186
- /**
187
- * Use WP_Query to get a list of URLs to query SharedCount for share data.
188
- *
189
- * @param int $page The page number.
190
- *
191
- * @return array
192
- */
193
- public function get_post_urls( $page = 1 ) {
194
-
195
- $posts_args = array(
196
- 'posts_per_page' => 100, // Don't try to load more than 500 posts at once.
197
- 'fields' => 'ids', // Load just the ids.
198
- 'paged' => $page,
199
- 'suppress_filters' => true, // Avoid loading additional functionality from other plugins/theme.
200
- );
201
- $posts_query = new WP_Query( $posts_args );
202
- $urls = array();
203
-
204
- if ( $posts_query->have_posts() ) {
205
- while ( $posts_query->have_posts() ) {
206
- $posts_query->the_post();
207
-
208
- $urls[ get_the_ID() ] = get_permalink( get_the_ID() );
209
- }
210
- }
211
-
212
- if ( $posts_query->max_num_pages > $page ) {
213
- $this->more_pages = $posts_query->max_num_pages;
214
- } else {
215
- $this->more_pages = false;
216
- }
217
-
218
- wp_reset_postdata();
219
-
220
- return $urls;
221
- }
222
-
223
- /**
224
- * Use URLs as array keys to make it easier to match with the post id.
225
- *
226
- * @param array $urls The urls with post ids as keys.
227
- *
228
- * @return array
229
- */
230
- public function urls_as_keys( $urls ) {
231
-
232
- $urls_as_keys = array();
233
- foreach ( $urls as $id => $url ) {
234
- $urls_as_keys[ $url ] = $id;
235
- }
236
-
237
- return $urls_as_keys;
238
-
239
- }
240
-
241
- /**
242
- * Helper method for using the bulk API. Disabled by default as the free api doesn't have access to it.
243
- * This can be used by large sites to use less requests to the SharedCount API and grab data more efficiently
244
- * if they have a paid license.
245
- *
246
- * @return mixed|void
247
- */
248
- public function use_bulk_api() {
249
- // Bulk API is not available for free sharedcount accounts so let's set this to off by default.
250
- return apply_filters( 'exactmetrics_sharedcount_use_bulk_api', false );
251
- }
252
-
253
- /**
254
- * Use the bulk API method to post data to the SharedCount API.
255
- *
256
- * @param array $urls An array with the URLs to be sent in the bulk request.
257
- *
258
- * @return bool|mixed
259
- */
260
- public function post_bulk_urls( $urls ) {
261
-
262
- $body = implode( "\n", $urls );
263
-
264
- $request_url = add_query_arg(
265
- array(
266
- 'apikey' => $this->get_api_key(),
267
- ),
268
- $this->get_api_url( 'bulk' )
269
- );
270
-
271
- $request = wp_remote_post( $request_url, array(
272
- 'body' => $body,
273
- ) );
274
-
275
- $response = wp_remote_retrieve_body( $request );
276
- $parsed_response = json_decode( $response, true );
277
- if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
278
- return $parsed_response;
279
- } else {
280
- $this->handle_api_error( $parsed_response );
281
-
282
- return false;
283
- }
284
- }
285
-
286
- /**
287
- * Get the API url.
288
- *
289
- * @param string $path The API path to use e.g. "bulk".
290
- *
291
- * @return string
292
- */
293
- public function get_api_url( $path = '' ) {
294
- // Allow users to override the SharedCount URL if they have a custom URL.
295
- return apply_filters( 'exactmetrics_sharedcount_api_url', $this->endpoint . $path );
296
- }
297
-
298
- /**
299
- * Generic handler for error responses from the SharedCount API.
300
- * This uses the $error property to pass the error back for being displayed.
301
- *
302
- * @param array $parsed_response The response object from a SharedCount API call converted to an Array.
303
- */
304
- public function handle_api_error( $parsed_response ) {
305
- if ( isset( $parsed_response['Error'] ) && isset( $parsed_response['Type'] ) && 'invalid_api_key' === $parsed_response['Type'] ) {
306
- $error = esc_html__( 'The SharedCount API key is invalid', 'google-analytics-dashboard-for-wp' );
307
- } elseif ( ! empty( $parsed_response['quota_exceeded'] ) ) {
308
- $error = $parsed_response['quota_exceeded'];
309
- } else {
310
- $error = isset( $parsed_response['Error'] ) ? $parsed_response['Error'] : esc_html__( 'There was an error grabbing data from SharedCount, please check the API Key', 'google-analytics-dashboard-for-wp' );
311
- }
312
- $this->error = $error;
313
- }
314
-
315
- /**
316
- * Attempt to grab bulk data from the API by bulk id, if the bulk request is not completed
317
- * schedule an event to try again in a minute.
318
- *
319
- * @param string $bulk_id The bulk id from the SharedCount bulk post request.
320
- * @param array $urls_as_keys An array of URLs where the keys are the URLs and the values are the post ids.
321
- */
322
- public function grab_and_store_bulk_by_id( $bulk_id, $urls_as_keys ) {
323
- $bulk_data = $this->get_bulk_data( $bulk_id );
324
- // If the processing for the current bulk id is not completed schedule a single event to try again.
325
- if ( $bulk_data['_meta']['completed'] ) {
326
- $this->store_bulk_data( $bulk_data, $urls_as_keys );
327
- } else {
328
- $this->schedule_bulk_grabbing( $bulk_id, $urls_as_keys );
329
- }
330
- }
331
-
332
- /**
333
- * Grab data from the SharedCount API using their Bulk API.
334
- *
335
- * @param string $bulk_id The bulk id from a POST request to the bulk API.
336
- *
337
- * @return bool|mixed
338
- * @see ExactMetrics_SharedCount::post_bulk_urls()
339
- *
340
- */
341
- public function get_bulk_data( $bulk_id ) {
342
-
343
- $request_url = add_query_arg(
344
- array(
345
- 'bulk_id' => $bulk_id,
346
- 'apikey' => $this->get_api_key(),
347
- ),
348
- $this->get_api_url()
349
- );
350
-
351
- $request = wp_remote_get( $request_url );
352
-
353
- if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
354
- $response = wp_remote_retrieve_body( $request );
355
- $parsed_response = json_decode( $response, true );
356
-
357
- return $parsed_response;
358
- } else {
359
- return false;
360
- }
361
- }
362
-
363
- /**
364
- * Iterate through the bulk data returned and store it in the post meta.
365
- *
366
- * @param array $bulk_data The bulk data response from the SharedCount API.
367
- * @param array $urls_as_keys An array of URLs where the keys are the URLs and the values are the post ids.
368
- */
369
- public function store_bulk_data( $bulk_data, $urls_as_keys ) {
370
- if ( ! empty( $bulk_data['data'] ) && is_array( $bulk_data['data'] ) ) {
371
- foreach ( $bulk_data['data'] as $url => $values ) {
372
- $post_id = array_key_exists( $url, $urls_as_keys ) ? $urls_as_keys[ $url ] : false;
373
-
374
- if ( $post_id ) {
375
- $this->store_post_counts( $post_id, $values );
376
- }
377
- }
378
- }
379
- }
380
-
381
- /**
382
- * Save the post counts response to the post meta.
383
- * The total value is saved separately for querying.
384
- *
385
- * @param int $post_id The post id to save to.
386
- * @param array $values The array of values received from the SharedCount API.
387
- *
388
- * @see ExactMetrics_SharedCount::get_counts_by_url()
389
- */
390
- public function store_post_counts( $post_id, $values ) {
391
- $total_count = $this->combine_counts( $values );
392
- update_post_meta( $post_id, '_exactmetrics_sharedcount_total', $total_count );
393
- update_post_meta( $post_id, '_exactmetrics_sharedcount_values', $values );
394
- }
395
-
396
- /**
397
- * Process a SharedCounts response and compile all counts into one number.
398
- *
399
- * @param array $response Array from decoding the API JSON response.
400
- *
401
- * @return int
402
- */
403
- public function combine_counts( $response ) {
404
-
405
- $total = 0;
406
- if ( ! isset( $response['Error'] ) ) {
407
- foreach ( $response as $count ) {
408
- if ( is_int( $count ) ) {
409
- $total += $count;
410
- } elseif ( is_array( $count ) && isset( $count['share_count'] ) ) {
411
- $total += $count['share_count'];
412
- }
413
- }
414
- }
415
-
416
- return $total;
417
- }
418
-
419
- /**
420
- * If the bulk request is not completed we need to schedule it to try again later.
421
- *
422
- * @param string $bulk_id The bulk id from the SharedCount bulk post request.
423
- * @param array $urls_as_keys An array of URLs where the keys are the URLs and the values are the post ids.
424
- *
425
- * @see ExactMetrics_SharedCount::post_bulk_urls()
426
- * @see ExactMetrics_SharedCount::grab_and_store_bulk_by_id()
427
- */
428
- public function schedule_bulk_grabbing( $bulk_id, $urls_as_keys ) {
429
-
430
- wp_schedule_single_event( time() + 60, 'exactmetrics_sharedcount_bulk_grab', array(
431
- 'bulk_id' => $bulk_id,
432
- 'urls' => $urls_as_keys,
433
- ) );
434
-
435
- }
436
-
437
- /**
438
- * The SharedCount Bulk API is not available for free users so we need
439
- * to use multiple calls to the API to grab data.
440
- *
441
- * @param array $urls An array of urls with the post ids as keys.
442
- *
443
- * @return bool
444
- * @see ExactMetrics_SharedCount::get_post_urls()
445
- *
446
- */
447
- private function grab_counts_one_by_one( $urls ) {
448
-
449
- foreach ( $urls as $id => $url ) {
450
- $counts = $this->get_counts_by_url( $url );
451
-
452
- if ( $counts ) {
453
- $this->store_post_counts( $id, $counts );
454
- } else {
455
- // Return false to display error message from API request.
456
- return false;
457
- }
458
- }
459
-
460
- return true;
461
-
462
- }
463
-
464
- /**
465
- * Request the SharedCount data from the API by URL.
466
- *
467
- * @param string $url The URL to request data for.
468
- *
469
- * @return bool|mixed
470
- */
471
- public function get_counts_by_url( $url ) {
472
-
473
- $url = apply_filters( 'exactmetrics_sharedcount_url_pre_grab', $url );
474
- $request_url = add_query_arg(
475
- array(
476
- 'url' => $url,
477
- 'apikey' => $this->get_api_key(),
478
- ),
479
- $this->get_api_url()
480
- );
481
-
482
- $request = wp_remote_get( $request_url );
483
- $response = wp_remote_retrieve_body( $request );
484
- $parsed_response = json_decode( $response, true );
485
- if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
486
- return $parsed_response;
487
- } else {
488
- $this->handle_api_error( $parsed_response );
489
-
490
- return false;
491
- }
492
-
493
- }
494
-
495
- /**
496
- * Schedule a single event for the next page in the WP Query to be grabbed.
497
- *
498
- * @param int $page The page number.
499
- */
500
- public function schedule_next_page( $page ) {
501
-
502
- wp_schedule_single_event( time() + 60, 'exactmetrics_sharedcount_get_more_posts', array( 'page' => $page ) );
503
-
504
- }
505
-
506
- /**
507
- * This schedules the daily event with the first one in 24hrs from the current time.
508
- */
509
- public function schedule_daily_update() {
510
-
511
- if ( ! wp_next_scheduled( $this->cron_key ) ) {
512
- wp_schedule_event( time() + DAY_IN_SECONDS, 'daily', $this->cron_key );
513
- }
514
-
515
- }
516
-
517
- /**
518
- * Cron handler that checks if the sorting method is still set to SharedCount.
519
- * If the sorting method changed, it will disable the daily cron.
520
- */
521
- public function daily_cron_update() {
522
- $sort_option = exactmetrics_get_option( 'popular_posts_inline_sort', 'comments' );
523
-
524
- if ( 'sharedcount' === $sort_option ) {
525
- $this->start_posts_count();
526
- } else {
527
- $this->disable_counts_updates();
528
- }
529
- }
530
-
531
- /**
532
- * Disable cron and reset progress.
533
- */
534
- public function disable_counts_updates() {
535
- // If we are no longer using this option disable the cron.
536
- wp_clear_scheduled_hook( $this->cron_key );
537
- $this->reset_progress();
538
- }
539
-
540
- /**
541
- * Get the post counts based on a post id.
542
- * Not used currently.
543
- *
544
- * @param int $post_id The id of the post.
545
- *
546
- * @return bool|mixed
547
- */
548
- public function get_post_counts( $post_id ) {
549
- $post_url = get_permalink( $post_id );
550
-
551
- return $this->combine_counts( $this->get_counts_by_url( $post_url ) );
552
- }
553
-
554
- /**
555
- * Get the indexing progress as percent.
556
- *
557
- * @return int
558
- */
559
- public static function get_index_progress_percent() {
560
-
561
- $progress = self::get_index_progress();
562
-
563
- if ( ! empty( $progress ) && ! empty( $progress['page'] ) && ! empty( $progress['max_pages'] ) ) {
564
- $progress = 100 / $progress['max_pages'] * $progress['page'];
565
- $progress = floor( $progress );
566
-
567
- return $progress;
568
- } elseif ( isset( $progress['max_pages'] ) && false === $progress['max_pages'] ) {
569
- return 100;
570
- }
571
-
572
- return 0;
573
-
574
- }
575
-
576
- /**
577
- * Get the current progress.
578
- *
579
- * @return array
580
- */
581
- public static function get_index_progress() {
582
-
583
- if ( empty( self::$progress ) ) {
584
- self::$progress = get_option( self::$progress_key, array() );
585
- }
586
-
587
- return self::$progress;
588
-
589
- }
590
-
591
- /**
592
- * Get the index progress with ajax.
593
- */
594
- public function ajax_get_index_progress() {
595
- wp_send_json( array(
596
- 'progress' => self::get_index_progress_percent(),
597
- ) );
598
- }
599
-
600
- /**
601
- * Get the top popular posts by SharedCount shares.
602
- *
603
- * @param int $count The number of posts to get.
604
- *
605
- * @return array
606
- */
607
- public static function query_popular_posts( $count = 5 ) {
608
-
609
- $popular_posts_args = array(
610
- 'posts_per_page' => $count,
611
- 'meta_value' => 'exactmetrics_sharedcount_total',
612
- 'orderby' => 'meta_value_num',
613
- 'order' => 'DESC',
614
- );
615
- $popular_posts_query = new WP_Query( $popular_posts_args );
616
- $popular_posts = array();
617
-
618
- if ( $popular_posts_query->have_posts() ) {
619
- while ( $popular_posts_query->have_posts() ) {
620
- $popular_posts_query->the_post();
621
- $popular_posts[ get_the_ID() ] = array(
622
- 'post_title' => get_the_title(),
623
- 'permalink' => get_permalink(),
624
- 'thumbnail' => get_the_post_thumbnail_url( get_the_ID(), 'medium' ),
625
- );
626
- }
627
- }
628
-
629
- wp_reset_postdata();
630
-
631
- return $popular_posts;
632
-
633
- }
634
-
635
- }
636
-
637
- new ExactMetrics_SharedCount();
1
+ <?php
2
+
3
+ /**
4
+ * Handles the SharedCount integration and count grabbing.
5
+ *
6
+ * Class ExactMetrics_SharedCount
7
+ */
8
+ class ExactMetrics_SharedCount {
9
+
10
+ /**
11
+ * The action used to schedule daily events.
12
+ *
13
+ * @var string
14
+ */
15
+ public $cron_key = 'exactmetrics_sharedcount_daily_update';
16
+ /**
17
+ * Index progress key.
18
+ *
19
+ * @var string
20
+ */
21
+ public static $progress_key = 'exactmetrics_sharedcount_index_progress';
22
+ /**
23
+ * Index progress.
24
+ *
25
+ * @var array
26
+ */
27
+ public static $progress;
28
+ /**
29
+ * The error message from the api call.
30
+ *
31
+ * @var string
32
+ */
33
+ public $error;
34
+ /**
35
+ * The API endpoint.
36
+ *
37
+ * @var string
38
+ */
39
+ private $endpoint = 'https://api.sharedcount.com/v1.0/';
40
+ /**
41
+ * The API key to use for the requests.
42
+ *
43
+ * @var string
44
+ */
45
+ private $api_key;
46
+ /**
47
+ * If the current query needs to run again.
48
+ *
49
+ * @var bool
50
+ */
51
+ private $more_pages = false;
52
+
53
+ /**
54
+ * ExactMetrics_SharedCount constructor.
55
+ */
56
+ public function __construct() {
57
+
58
+ add_action( 'wp_ajax_exactmetrics_sharedcount_start_indexing', array( $this, 'ajax_start_indexing' ) );
59
+ add_action( 'wp_ajax_exactmetrics_sharedcount_get_index_progress', array(
60
+ $this,
61
+ 'ajax_get_index_progress'
62
+ ) );
63
+
64
+ add_action( 'exactmetrics_sharedcount_get_more_posts', array( $this, 'get_more_counts' ) );
65
+
66
+ add_action( 'exactmetrics_sharedcount_bulk_grab', array( $this, 'grab_and_store_bulk_by_id' ), 10, 2 );
67
+
68
+ add_action( $this->cron_key, array( $this, 'daily_cron_update' ) );
69
+ }
70
+
71
+ /**
72
+ * AJAX handler from the Vue app that checks if the API key is set and handles
73
+ * an error message from the SharedCount API call. If the first call is successful it will schedule
74
+ * a daily cron to keep the counts fresh.
75
+ */
76
+ public function ajax_start_indexing() {
77
+
78
+ check_ajax_referer( 'mi-admin-nonce', 'nonce' );
79
+
80
+ if ( $this->get_api_key() ) {
81
+ if ( $this->start_posts_count() ) {
82
+ $this->schedule_daily_update();
83
+ wp_send_json_success( array(
84
+ 'max_pages' => $this->more_pages,
85
+ ) );
86
+ } else {
87
+ wp_send_json_error( array(
88
+ 'message' => $this->error,
89
+ ) );
90
+ }
91
+ }
92
+
93
+ // No API key, let's send an error message.
94
+ wp_send_json_error( array(
95
+ 'message' => esc_html__( 'The SharedCount API key is not set', 'google-analytics-dashboard-for-wp' ),
96
+ ) );
97
+
98
+ }
99
+
100
+ /**
101
+ * Get the API key.
102
+ *
103
+ * @return string
104
+ */
105
+ public function get_api_key() {
106
+
107
+ if ( empty( $this->api_key ) ) {
108
+ $this->api_key = exactmetrics_get_option( 'sharedcount_key' );
109
+ }
110
+
111
+ return $this->api_key;
112
+ }
113
+
114
+ /**
115
+ * Start a grabbing process that will schedule events to grab more pages if needed.
116
+ *
117
+ * @return bool
118
+ */
119
+ public function start_posts_count() {
120
+
121
+ return $this->get_more_counts( 1 );
122
+
123
+ }
124
+
125
+ /**
126
+ * Handler for the scheduled event to grab more data for sites with large number of posts.
127
+ * This is also used by the first call and uses the return value to determine if an error was encountered.
128
+ * The error gets set in the $error property and used for display.
129
+ *
130
+ * @param int $page The page number to grab counts for.
131
+ *
132
+ * @return bool
133
+ */
134
+ public function get_more_counts( $page ) {
135
+
136
+ $urls = $this->get_post_urls( $page );
137
+ $urls_as_keys = $this->urls_as_keys( $urls );
138
+
139
+ if ( $this->use_bulk_api() ) {
140
+ $bulk_request = $this->post_bulk_urls( $urls );
141
+
142
+ if ( $bulk_request && ! empty( $bulk_request['bulk_id'] ) ) {
143
+ $this->grab_and_store_bulk_by_id( $bulk_request['bulk_id'], $urls_as_keys );
144
+ } else {
145
+ return false;
146
+ }
147
+ } else {
148
+ $store_counts = $this->grab_counts_one_by_one( $urls );
149
+ if ( ! $store_counts ) {
150
+ // Error encountered, return error.
151
+ return false;
152
+ }
153
+ }
154
+
155
+ $this->save_progress( $page, $this->more_pages );
156
+
157
+ if ( $this->more_pages ) {
158
+ $page ++;
159
+ $this->schedule_next_page( $page );
160
+ }
161
+
162
+ return true;
163
+
164
+ }
165
+
166
+ /**
167
+ * Save the current indexing progress.
168
+ *
169
+ * @param int $page The current page.
170
+ * @param int $max_pages The total number of pages.
171
+ */
172
+ public function save_progress( $page, $max_pages ) {
173
+ update_option( self::$progress_key, array(
174
+ 'page' => $page,
175
+ 'max_pages' => $max_pages,
176
+ ), false );
177
+ }
178
+
179
+ /**
180
+ * Reset the progress option. Used for when the cron is disabled.
181
+ */
182
+ public function reset_progress() {
183
+ delete_option( self::$progress_key );
184
+ }
185
+
186
+ /**
187
+ * Use WP_Query to get a list of URLs to query SharedCount for share data.
188
+ *
189
+ * @param int $page The page number.
190
+ *
191
+ * @return array
192
+ */
193
+ public function get_post_urls( $page = 1 ) {
194
+
195
+ $posts_args = array(
196
+ 'posts_per_page' => 100, // Don't try to load more than 500 posts at once.
197
+ 'fields' => 'ids', // Load just the ids.
198
+ 'paged' => $page,
199
+ 'suppress_filters' => true, // Avoid loading additional functionality from other plugins/theme.
200
+ );
201
+ $posts_query = new WP_Query( $posts_args );
202
+ $urls = array();
203
+
204
+ if ( $posts_query->have_posts() ) {
205
+ while ( $posts_query->have_posts() ) {
206
+ $posts_query->the_post();
207
+
208
+ $urls[ get_the_ID() ] = get_permalink( get_the_ID() );
209
+ }
210
+ }
211
+
212
+ if ( $posts_query->max_num_pages > $page ) {
213
+ $this->more_pages = $posts_query->max_num_pages;
214
+ } else {
215
+ $this->more_pages = false;
216
+ }
217
+
218
+ wp_reset_postdata();
219
+
220
+ return $urls;
221
+ }
222
+
223
+ /**
224
+ * Use URLs as array keys to make it easier to match with the post id.
225
+ *
226
+ * @param array $urls The urls with post ids as keys.
227
+ *
228
+ * @return array
229
+ */
230
+ public function urls_as_keys( $urls ) {
231
+
232
+ $urls_as_keys = array();
233
+ foreach ( $urls as $id => $url ) {
234
+ $urls_as_keys[ $url ] = $id;
235
+ }
236
+
237
+ return $urls_as_keys;
238
+
239
+ }
240
+
241
+ /**
242
+ * Helper method for using the bulk API. Disabled by default as the free api doesn't have access to it.
243
+ * This can be used by large sites to use less requests to the SharedCount API and grab data more efficiently
244
+ * if they have a paid license.
245
+ *
246
+ * @return mixed|void
247
+ */
248
+ public function use_bulk_api() {
249
+ // Bulk API is not available for free sharedcount accounts so let's set this to off by default.
250
+ return apply_filters( 'exactmetrics_sharedcount_use_bulk_api', false );
251
+ }
252
+
253
+ /**
254
+ * Use the bulk API method to post data to the SharedCount API.
255
+ *
256
+ * @param array $urls An array with the URLs to be sent in the bulk request.
257
+ *
258
+ * @return bool|mixed
259
+ */
260
+ public function post_bulk_urls( $urls ) {
261
+
262
+ $body = implode( "\n", $urls );
263
+
264
+ $request_url = add_query_arg(
265
+ array(
266
+ 'apikey' => $this->get_api_key(),
267
+ ),
268
+ $this->get_api_url( 'bulk' )
269
+ );
270
+
271
+ $request = wp_remote_post( $request_url, array(
272
+ 'body' => $body,
273
+ ) );
274
+
275
+ $response = wp_remote_retrieve_body( $request );
276
+ $parsed_response = json_decode( $response, true );
277
+ if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
278
+ return $parsed_response;
279
+ } else {
280
+ $this->handle_api_error( $parsed_response );
281
+
282
+ return false;
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Get the API url.
288
+ *
289
+ * @param string $path The API path to use e.g. "bulk".
290
+ *
291
+ * @return string
292
+ */
293
+ public function get_api_url( $path = '' ) {
294
+ // Allow users to override the SharedCount URL if they have a custom URL.
295
+ return apply_filters( 'exactmetrics_sharedcount_api_url', $this->endpoint . $path );
296
+ }
297
+
298
+ /**
299
+ * Generic handler for error responses from the SharedCount API.
300
+ * This uses the $error property to pass the error back for being displayed.
301
+ *
302
+ * @param array $parsed_response The response object from a SharedCount API call converted to an Array.
303
+ */
304
+ public function handle_api_error( $parsed_response ) {
305
+ if ( isset( $parsed_response['Error'] ) && isset( $parsed_response['Type'] ) && 'invalid_api_key' === $parsed_response['Type'] ) {
306
+ $error = esc_html__( 'The SharedCount API key is invalid', 'google-analytics-dashboard-for-wp' );
307
+ } elseif ( ! empty( $parsed_response['quota_exceeded'] ) ) {
308
+ $error = $parsed_response['quota_exceeded'];
309
+ } else {
310
+ $error = isset( $parsed_response['Error'] ) ? $parsed_response['Error'] : esc_html__( 'There was an error grabbing data from SharedCount, please check the API Key', 'google-analytics-dashboard-for-wp' );
311
+ }
312
+ $this->error = $error;
313
+ }
314
+
315
+ /**
316
+ * Attempt to grab bulk data from the API by bulk id, if the bulk request is not completed
317
+ * schedule an event to try again in a minute.
318
+ *
319
+ * @param string $bulk_id The bulk id from the SharedCount bulk post request.
320
+ * @param array $urls_as_keys An array of URLs where the keys are the URLs and the values are the post ids.
321
+ */
322
+ public function grab_and_store_bulk_by_id( $bulk_id, $urls_as_keys ) {
323
+ $bulk_data = $this->get_bulk_data( $bulk_id );
324
+ // If the processing for the current bulk id is not completed schedule a single event to try again.
325
+ if ( $bulk_data['_meta']['completed'] ) {
326
+ $this->store_bulk_data( $bulk_data, $urls_as_keys );
327
+ } else {
328
+ $this->schedule_bulk_grabbing( $bulk_id, $urls_as_keys );
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Grab data from the SharedCount API using their Bulk API.
334
+ *
335
+ * @param string $bulk_id The bulk id from a POST request to the bulk API.
336
+ *
337
+ * @return bool|mixed
338
+ * @see ExactMetrics_SharedCount::post_bulk_urls()
339
+ *
340
+ */
341
+ public function get_bulk_data( $bulk_id ) {
342
+
343
+ $request_url = add_query_arg(
344
+ array(
345
+ 'bulk_id' => $bulk_id,
346
+ 'apikey' => $this->get_api_key(),
347
+ ),
348
+ $this->get_api_url()
349
+ );
350
+
351
+ $request = wp_remote_get( $request_url );
352
+
353
+ if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
354
+ $response = wp_remote_retrieve_body( $request );
355
+ $parsed_response = json_decode( $response, true );
356
+
357
+ return $parsed_response;
358
+ } else {
359
+ return false;
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Iterate through the bulk data returned and store it in the post meta.
365
+ *
366
+ * @param array $bulk_data The bulk data response from the SharedCount API.
367
+ * @param array $urls_as_keys An array of URLs where the keys are the URLs and the values are the post ids.
368
+ */
369
+ public function store_bulk_data( $bulk_data, $urls_as_keys ) {
370
+ if ( ! empty( $bulk_data['data'] ) && is_array( $bulk_data['data'] ) ) {
371
+ foreach ( $bulk_data['data'] as $url => $values ) {
372
+ $post_id = array_key_exists( $url, $urls_as_keys ) ? $urls_as_keys[ $url ] : false;
373
+
374
+ if ( $post_id ) {
375
+ $this->store_post_counts( $post_id, $values );
376
+ }
377
+ }
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Save the post counts response to the post meta.
383
+ * The total value is saved separately for querying.
384
+ *
385
+ * @param int $post_id The post id to save to.
386
+ * @param array $values The array of values received from the SharedCount API.
387
+ *
388
+ * @see ExactMetrics_SharedCount::get_counts_by_url()
389
+ */
390
+ public function store_post_counts( $post_id, $values ) {
391
+ $total_count = $this->combine_counts( $values );
392
+ update_post_meta( $post_id, '_exactmetrics_sharedcount_total', $total_count );
393
+ update_post_meta( $post_id, '_exactmetrics_sharedcount_values', $values );
394
+ }
395
+
396
+ /**
397
+ * Process a SharedCounts response and compile all counts into one number.
398
+ *
399
+ * @param array $response Array from decoding the API JSON response.
400
+ *
401
+ * @return int
402
+ */
403
+ public function combine_counts( $response ) {
404
+
405
+ $total = 0;
406
+ if ( ! isset( $response['Error'] ) ) {
407
+ foreach ( $response as $count ) {
408
+ if ( is_int( $count ) ) {
409
+ $total += $count;
410
+ } elseif ( is_array( $count ) && isset( $count['share_count'] ) ) {
411
+ $total += $count['share_count'];
412
+ }
413
+ }
414
+ }
415
+
416
+ return $total;
417
+ }
418
+
419
+ /**
420
+ * If the bulk request is not completed we need to schedule it to try again later.
421
+ *
422
+ * @param string $bulk_id The bulk id from the SharedCount bulk post request.
423
+ * @param array $urls_as_keys An array of URLs where the keys are the URLs and the values are the post ids.
424
+ *
425
+ * @see ExactMetrics_SharedCount::post_bulk_urls()
426
+ * @see ExactMetrics_SharedCount::grab_and_store_bulk_by_id()
427
+ */
428
+ public function schedule_bulk_grabbing( $bulk_id, $urls_as_keys ) {
429
+
430
+ wp_schedule_single_event( time() + 60, 'exactmetrics_sharedcount_bulk_grab', array(
431
+ 'bulk_id' => $bulk_id,
432
+ 'urls' => $urls_as_keys,
433
+ ) );
434
+
435
+ }
436
+
437
+ /**
438
+ * The SharedCount Bulk API is not available for free users so we need
439
+ * to use multiple calls to the API to grab data.
440
+ *
441
+ * @param array $urls An array of urls with the post ids as keys.
442
+ *
443
+ * @return bool
444
+ * @see ExactMetrics_SharedCount::get_post_urls()
445
+ *
446
+ */
447
+ private function grab_counts_one_by_one( $urls ) {
448
+
449
+ foreach ( $urls as $id => $url ) {
450
+ $counts = $this->get_counts_by_url( $url );
451
+
452
+ if ( $counts ) {
453
+ $this->store_post_counts( $id, $counts );
454
+ } else {
455
+ // Return false to display error message from API request.
456
+ return false;
457
+ }
458
+ }
459
+
460
+ return true;
461
+
462
+ }
463
+
464
+ /**
465
+ * Request the SharedCount data from the API by URL.
466
+ *
467
+ * @param string $url The URL to request data for.
468
+ *
469
+ * @return bool|mixed
470
+ */
471
+ public function get_counts_by_url( $url ) {
472
+
473
+ $url = apply_filters( 'exactmetrics_sharedcount_url_pre_grab', $url );
474
+ $request_url = add_query_arg(
475
+ array(
476
+ 'url' => $url,
477
+ 'apikey' => $this->get_api_key(),
478
+ ),
479
+ $this->get_api_url()
480
+ );
481
+
482
+ $request = wp_remote_get( $request_url );
483
+ $response = wp_remote_retrieve_body( $request );
484
+ $parsed_response = json_decode( $response, true );
485
+ if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
486
+ return $parsed_response;
487
+ } else {
488
+ $this->handle_api_error( $parsed_response );
489
+
490
+ return false;
491
+ }
492
+
493
+ }
494
+
495
+ /**
496
+ * Schedule a single event for the next page in the WP Query to be grabbed.
497
+ *
498
+ * @param int $page The page number.
499
+ */
500
+ public function schedule_next_page( $page ) {
501
+
502
+ wp_schedule_single_event( time() + 60, 'exactmetrics_sharedcount_get_more_posts', array( 'page' => $page ) );
503
+
504
+ }
505
+
506
+ /**
507
+ * This schedules the daily event with the first one in 24hrs from the current time.
508
+ */
509
+ public function schedule_daily_update() {
510
+
511
+ if ( ! wp_next_scheduled( $this->cron_key ) ) {
512
+ wp_schedule_event( time() + DAY_IN_SECONDS, 'daily', $this->cron_key );
513
+ }
514
+
515
+ }
516
+
517
+ /**
518
+ * Cron handler that checks if the sorting method is still set to SharedCount.
519
+ * If the sorting method changed, it will disable the daily cron.
520
+ */
521
+ public function daily_cron_update() {
522
+ $sort_option = exactmetrics_get_option( 'popular_posts_inline_sort', 'comments' );
523
+
524
+ if ( 'sharedcount' === $sort_option ) {
525
+ $this->start_posts_count();
526
+ } else {
527
+ $this->disable_counts_updates();
528
+ }
529
+ }
530
+
531
+ /**
532
+ * Disable cron and reset progress.
533
+ */
534
+ public function disable_counts_updates() {
535
+ // If we are no longer using this option disable the cron.
536
+ wp_clear_scheduled_hook( $this->cron_key );
537
+ $this->reset_progress();
538
+ }
539
+
540
+ /**
541
+ * Get the post counts based on a post id.
542
+ * Not used currently.
543
+ *
544
+ * @param int $post_id The id of the post.
545
+ *
546
+ * @return bool|mixed
547
+ */
548
+ public function get_post_counts( $post_id ) {
549
+ $post_url = get_permalink( $post_id );
550
+
551
+ return $this->combine_counts( $this->get_counts_by_url( $post_url ) );
552
+ }
553
+
554
+ /**
555
+ * Get the indexing progress as percent.
556
+ *
557
+ * @return int
558
+ */
559
+ public static function get_index_progress_percent() {
560
+
561
+ $progress = self::get_index_progress();
562
+
563
+ if ( ! empty( $progress ) && ! empty( $progress['page'] ) && ! empty( $progress['max_pages'] ) ) {
564
+ $progress = 100 / $progress['max_pages'] * $progress['page'];
565
+ $progress = floor( $progress );
566
+
567
+ return $progress;
568
+ } elseif ( isset( $progress['max_pages'] ) && false === $progress['max_pages'] ) {
569
+ return 100;
570
+ }
571
+
572
+ return 0;
573
+
574
+ }
575
+
576
+ /**
577
+ * Get the current progress.
578
+ *
579
+ * @return array
580
+ */
581
+ public static function get_index_progress() {
582
+
583
+ if ( empty( self::$progress ) ) {
584
+ self::$progress = get_option( self::$progress_key, array() );
585
+ }
586
+
587
+ return self::$progress;
588
+
589
+ }
590
+
591
+ /**
592
+ * Get the index progress with ajax.
593
+ */
594
+ public function ajax_get_index_progress() {
595
+ wp_send_json( array(
596
+ 'progress' => self::get_index_progress_percent(),
597
+ ) );
598
+ }
599
+
600
+ /**
601
+ * Get the top popular posts by SharedCount shares.
602
+ *
603
+ * @param int $count The number of posts to get.
604
+ *
605
+ * @return array
606
+ */
607
+ public static function query_popular_posts( $count = 5 ) {
608
+
609
+ $popular_posts_args = array(
610
+ 'posts_per_page' => $count,
611
+ 'meta_value' => 'exactmetrics_sharedcount_total',
612
+ 'orderby' => 'meta_value_num',
613
+ 'order' => 'DESC',
614
+ );
615
+ $popular_posts_query = new WP_Query( $popular_posts_args );
616
+ $popular_posts = array();
617
+
618
+ if ( $popular_posts_query->have_posts() ) {
619
+ while ( $popular_posts_query->have_posts() ) {
620
+ $popular_posts_query->the_post();
621
+ $popular_posts[ get_the_ID() ] = array(
622
+ 'post_title' => get_the_title(),
623
+ 'permalink' => get_permalink(),
624
+ 'thumbnail' => get_the_post_thumbnail_url( get_the_ID(), 'medium' ),
625
+ );
626
+ }
627
+ }
628
+
629
+ wp_reset_postdata();
630
+
631
+ return $popular_posts;
632
+
633
+ }
634
+
635
+ }
636
+
637
+ new ExactMetrics_SharedCount();
includes/admin/uninstall.php CHANGED
@@ -1,26 +1,26 @@
1
- <?php
2
-
3
- /**
4
- * Remove various options used in the plugin.
5
- */
6
- function exactmetrics_uninstall_remove_options() {
7
-
8
- // Remove usage tracking options.
9
- delete_option( 'exactmetrics_usage_tracking_config' );
10
- delete_option( 'exactmetrics_usage_tracking_last_checkin' );
11
-
12
- // Remove version options.
13
- delete_option( 'exactmetrics_db_version' );
14
- delete_option( 'exactmetrics_version_upgraded_from' );
15
-
16
- // Remove other options used for display.
17
- delete_option( 'exactmetrics_email_summaries_infoblocks_sent' );
18
- delete_option( 'exactmetrics_float_bar_hidden' );
19
- delete_option( 'exactmetrics_frontend_tracking_notice_viewed' );
20
- delete_option( 'exactmetrics_admin_menu_tooltip' );
21
- delete_option( 'exactmetrics_review' );
22
-
23
- // Delete addons transient.
24
- delete_transient( 'exactmetrics_addons' );
25
-
26
- }
1
+ <?php
2
+
3
+ /**
4
+ * Remove various options used in the plugin.
5
+ */
6
+ function exactmetrics_uninstall_remove_options() {
7
+
8
+ // Remove usage tracking options.
9
+ delete_option( 'exactmetrics_usage_tracking_config' );
10
+ delete_option( 'exactmetrics_usage_tracking_last_checkin' );
11
+
12
+ // Remove version options.
13
+ delete_option( 'exactmetrics_db_version' );
14
+ delete_option( 'exactmetrics_version_upgraded_from' );
15
+
16
+ // Remove other options used for display.
17
+ delete_option( 'exactmetrics_email_summaries_infoblocks_sent' );
18
+ delete_option( 'exactmetrics_float_bar_hidden' );
19
+ delete_option( 'exactmetrics_frontend_tracking_notice_viewed' );
20
+ delete_option( 'exactmetrics_admin_menu_tooltip' );
21
+ delete_option( 'exactmetrics_review' );
22
+
23
+ // Delete addons transient.
24
+ delete_transient( 'exactmetrics_addons' );
25
+
26
+ }
includes/frontend/events/class-gtag-events.php CHANGED
@@ -1,117 +1,117 @@
1
- <?php
2
- /**
3
- * Events JS class.
4
- *
5
- * @since 6.0.0
6
- *
7
- * @package ExactMetrics
8
- * @subpackage Events
9
- * @author Chris Christoff
10
- */
11
-
12
- // Exit if accessed directly
13
- if ( ! defined( 'ABSPATH' ) ) {
14
- exit;
15
- }
16
-
17
- class ExactMetrics_Gtag_Events {
18
-
19
- /**
20
- * Holds the name of the events type.
21
- *
22
- * @since 6.0.0
23
- * @access public
24
- *
25
- * @var string $name Name of the events type.
26
- */
27
- public $name = 'js';
28
-
29
- /**
30
- * Version of the events class.
31
- *
32
- * @since 6.0.0
33
- * @access public
34
- *
35
- * @var string $version Version of the events class.
36
- */
37
- public $version = '1.0.0';
38
-
39
- /**
40
- * Primary class constructor.
41
- *
42
- * @since 6.0.0
43
- * @access public
44
- */
45
- public function __construct() {
46
- add_action( 'wp_enqueue_scripts', array( $this, 'output_javascript' ), 9 );
47
- }
48
-
49
- /**
50
- * Outputs the Javascript for JS tracking on the page.
51
- *
52
- * @since 6.0.0
53
- * @access public
54
- *
55
- * @return string
56
- */
57
- public function output_javascript() {
58
- // Affiliate Links
59
- $inbound_paths = exactmetrics_get_option( 'affiliate_links', array() );
60
- if ( ! is_array( $inbound_paths ) ) {
61
- $inbound_paths = array();
62
- } else {
63
- foreach( $inbound_paths as $index => $pair ) {
64
- // if empty pair, unset and continue
65
- if ( empty( $pair['path'] ) ) {
66
- unset( $inbound_paths[$index] );
67
- continue;
68
- }
69
-
70
- // if path does not start with a /, start it with that
71
- $path = ! empty( $pair['path'] ) ? $pair['path'] : 'aff';
72
- $inbound_paths[$index]['path'] = trim( $path );
73
-
74
- // js escape the link label
75
- $label = ! empty( $pair['label'] ) ? $pair['label'] : 'aff';
76
- $inbound_paths[$index]['label'] = esc_js( trim( $label ) );
77
- }
78
- }
79
-
80
- $inbound_paths = wp_json_encode( $inbound_paths );
81
-
82
- // Get download extensions to track
83
- $download_extensions = exactmetrics_get_option( 'extensions_of_files', '' );
84
- $download_extensions = explode( ',', str_replace( '.', '', $download_extensions ) );
85
- if ( ! is_array( $download_extensions ) ) {
86
- $download_extensions = array( $download_extensions );
87
- }
88
- $i = 0;
89
- foreach( $download_extensions as $extension ){
90
- $download_extensions[ $i ] = esc_js( trim( $extension ) );
91
- $i++;
92
- }
93
-
94
- $download_extensions = implode( ",", $download_extensions );
95
-
96
- $hash_tracking = exactmetrics_get_option( 'hash_tracking', false ) ? 'true' : 'false';
97
-
98
- $suffix = ( defined( 'WP_DEBUG' ) && WP_DEBUG ) || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
99
- if ( ! file_exists( EXACTMETRICS_PLUGIN_DIR . 'assets/js/frontend-gtag.min.js' ) ) {
100
- $suffix = '';
101
- }
102
- wp_enqueue_script( 'exactmetrics-frontend-script', plugins_url( 'assets/js/frontend-gtag' . $suffix . '.js', EXACTMETRICS_PLUGIN_FILE ), array(), exactmetrics_get_asset_version(), false );
103
- wp_localize_script(
104
- 'exactmetrics-frontend-script',
105
- 'exactmetrics_frontend',
106
- array(
107
- 'js_events_tracking' => 'true',
108
- 'download_extensions' => $download_extensions, /* Let's get the extensions to track */
109
- 'inbound_paths' => $inbound_paths, /* Let's get the internal paths to track */
110
- 'home_url' => home_url(), /* Let's get the url to compare for external/internal use */
111
- 'hash_tracking' => $hash_tracking, /* Should hash track */
112
- 'ua' => exactmetrics_get_ua(), /* UA code used for tracking */
113
- 'v4_id' => exactmetrics_get_v4_id(), /* V4 ID used for tracking */
114
- )
115
- );
116
- }
117
- }
1
+ <?php
2
+ /**
3
+ * Events JS class.
4
+ *
5
+ * @since 6.0.0
6
+ *
7
+ * @package ExactMetrics
8
+ * @subpackage Events
9
+ * @author Chris Christoff
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ class ExactMetrics_Gtag_Events {
18
+
19
+ /**
20
+ * Holds the name of the events type.
21
+ *
22
+ * @since 6.0.0
23
+ * @access public
24
+ *
25
+ * @var string $name Name of the events type.
26
+ */
27
+ public $name = 'js';
28
+
29
+ /**
30
+ * Version of the events class.
31
+ *
32
+ * @since 6.0.0
33
+ * @access public
34
+ *
35
+ * @var string $version Version of the events class.
36
+ */
37
+ public $version = '1.0.0';
38
+
39
+ /**
40
+ * Primary class constructor.
41
+ *
42
+ * @since 6.0.0
43
+ * @access public
44
+ */
45
+ public function __construct() {
46
+ add_action( 'wp_enqueue_scripts', array( $this, 'output_javascript' ), 9 );
47
+ }
48
+
49
+ /**
50
+ * Outputs the Javascript for JS tracking on the page.
51
+ *
52
+ * @since 6.0.0
53
+ * @access public
54
+ *
55
+ * @return string
56
+ */
57
+ public function output_javascript() {
58
+ // Affiliate Links
59
+ $inbound_paths = exactmetrics_get_option( 'affiliate_links', array() );
60
+ if ( ! is_array( $inbound_paths ) ) {
61
+ $inbound_paths = array();
62
+ } else {
63
+ foreach( $inbound_paths as $index => $pair ) {
64
+ // if empty pair, unset and continue
65
+ if ( empty( $pair['path'] ) ) {
66
+ unset( $inbound_paths[$index] );
67
+ continue;
68
+ }
69
+
70
+ // if path does not start with a /, start it with that
71
+ $path = ! empty( $pair['path'] ) ? $pair['path'] : 'aff';
72
+ $inbound_paths[$index]['path'] = trim( $path );
73
+
74
+ // js escape the link label
75
+ $label = ! empty( $pair['label'] ) ? $pair['label'] : 'aff';
76
+ $inbound_paths[$index]['label'] = esc_js( trim( $label ) );
77
+ }
78
+ }
79
+
80
+ $inbound_paths = wp_json_encode( $inbound_paths );
81
+
82
+ // Get download extensions to track
83
+ $download_extensions = exactmetrics_get_option( 'extensions_of_files', '' );
84
+ $download_extensions = explode( ',', str_replace( '.', '', $download_extensions ) );
85
+ if ( ! is_array( $download_extensions ) ) {
86
+ $download_extensions = array( $download_extensions );
87
+ }
88
+ $i = 0;
89
+ foreach( $download_extensions as $extension ){
90
+ $download_extensions[ $i ] = esc_js( trim( $extension ) );
91
+ $i++;
92
+ }
93
+
94
+ $download_extensions = implode( ",", $download_extensions );
95
+
96
+ $hash_tracking = exactmetrics_get_option( 'hash_tracking', false ) ? 'true' : 'false';
97
+
98
+ $suffix = ( defined( 'WP_DEBUG' ) && WP_DEBUG ) || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
99
+ if ( ! file_exists( EXACTMETRICS_PLUGIN_DIR . 'assets/js/frontend-gtag.min.js' ) ) {
100
+ $suffix = '';
101
+ }
102
+ wp_enqueue_script( 'exactmetrics-frontend-script', plugins_url( 'assets/js/frontend-gtag' . $suffix . '.js', EXACTMETRICS_PLUGIN_FILE ), array(), exactmetrics_get_asset_version(), false );
103
+ wp_localize_script(
104
+ 'exactmetrics-frontend-script',
105
+ 'exactmetrics_frontend',
106
+ array(
107
+ 'js_events_tracking' => 'true',
108
+ 'download_extensions' => $download_extensions, /* Let's get the extensions to track */
109
+ 'inbound_paths' => $inbound_paths, /* Let's get the internal paths to track */
110
+ 'home_url' => home_url(), /* Let's get the url to compare for external/internal use */
111
+ 'hash_tracking' => $hash_tracking, /* Should hash track */
112
+ 'ua' => exactmetrics_get_ua(), /* UA code used for tracking */
113
+ 'v4_id' => exactmetrics_get_v4_id(), /* V4 ID used for tracking */
114
+ )
115
+ );
116
+ }
117
+ }
includes/frontend/tracking/class-tracking-gtag.php CHANGED
@@ -1,486 +1,486 @@
1
- <?php
2
- /**
3
- * Tracking gtag.js class.
4
- *
5
- * @since 7.15.0
6
- *
7
- * @package ExactMetrics
8
- * @author Mircea Sandu
9
- */
10
-
11
- // Exit if accessed directly
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
-
16
- class ExactMetrics_Tracking_Gtag extends ExactMetrics_Tracking_Abstract {
17
-
18
- /**
19
- * Holds the name of the tracking type.
20
- *
21
- * @since 7.15.0
22
- * @access public
23
- *
24
- * @var string $name Name of the tracking type.
25
- */
26
- public $name = 'gtag';
27
-
28
- /**
29
- * Version of the tracking class.
30
- *
31
- * @since 7.15.0
32
- * @access public
33
- *
34
- * @var string $version Version of the tracking class.
35
- */
36
- public $version = '1.0.0';
37
-
38
- /**
39
- * Primary class constructor.
40
- *
41
- * @since 7.15.0
42
- * @access public
43
- */
44
- public function __construct() {
45
-
46
- }
47
-
48
- /**
49
- * Array of options that will be made persistent by setting them before the pageview.
50
- *
51
- * @see https://developers.google.com/analytics/devguides/collection/gtagjs/setting-values
52
- * @return array Options for persistent values, like custom dimensions.
53
- * @since 7.15.0
54
- * @access public
55
- */
56
- public function frontend_tracking_options_persistent() {
57
- $options = apply_filters( 'exactmetrics_frontend_tracking_options_persistent_gtag_before_pageview', array() );
58
-
59
- return $options;
60
- }
61
-
62
- /**
63
- * Get frontend tracking options for the gtag script.
64
- *
65
- * This function is used to return an array of parameters
66
- * for the frontend_output() function to output. These are
67
- * generally dimensions and turned on GA features.
68
- *
69
- * @param bool $encoded Whether to return a JavaScript object representation of the options
70
- *
71
- * @return array|string Options for the gtag config.
72
- * @since 7.15.0
73
- * @access public
74
- *
75
- */
76
- public function frontend_tracking_options( $type = 'ua', $encoded = false ) {
77
- global $wp_query;
78
- $options = array();
79
-
80
- $tracking_ids = exactmetrics_get_tracking_ids();
81
- if ( empty( $tracking_ids ) ) {
82
- return $encoded ? wp_json_encode( $options ) : $options;
83
- }
84
-
85
- $placeholder = '';
86
-
87
- if ( $encoded ) {
88
- $placeholder = '!@#';
89
- }
90
-
91
- // $track_user = exactmetrics_track_user();
92
- //
93
- // if ( ! $track_user ) {
94
- // $options['create'] = "'create', '" . esc_js( $ua_code ) . "', '" . esc_js( 'auto' ) . "'";
95
- // $options['forceSSL'] = "'set', 'forceSSL', true";
96
- // $options['send'] = "'send','pageview'";
97
- //
98
- // return $options;
99
- // }
100
-
101
- $cross_domains = exactmetrics_get_option( 'cross_domains', array() );
102
- $allow_anchor = exactmetrics_get_option( 'allow_anchor', false );
103
-
104
- if ( $allow_anchor ) {
105
- $options['allow_anchor'] = 'true';
106
- }
107
-
108
- if ( class_exists( 'ExactMetrics_AMP' ) ) {
109
- $options['use_amp_client_id'] = 'true';
110
- }
111
-
112
-
113
- $options['forceSSL'] = 'true';
114
-
115
- // Anonymous data.
116
- if ( exactmetrics_get_option( 'anonymize_ips', false ) ) {
117
- $options['anonymize_ip'] = 'true';
118
- }
119
-
120
- $options = apply_filters( 'exactmetrics_frontend_tracking_options_gtag_before_scripts', $options );
121
-
122
- // Add Enhanced link attribution.
123
- if ( exactmetrics_get_option( 'link_attribution', false ) ) {
124
- $options['link_attribution'] = 'true';
125
- }
126
-
127
- // Add cross-domain tracking.
128
- if ( is_array( $cross_domains ) && ! empty( $cross_domains ) ) {
129
- $linker_domains = array();
130
- foreach ( $cross_domains as $cross_domain ) {
131
- if ( ! empty( $cross_domain['domain'] ) ) {
132
- $linker_domains[] = $cross_domain['domain'];
133
- }
134
- }
135
- $options['linker'] = array(
136
- 'domains' => $linker_domains,
137
- );
138
- }
139
-
140
- if ( exactmetrics_is_debug_mode() ) {
141
- $options['debug_mode'] = true;
142
- }
143
-
144
- $options = apply_filters( 'exactmetrics_frontend_tracking_options_gtag_before_pageview', $options, $type );
145
- $options = apply_filters( 'exactmetrics_frontend_tracking_options_before_pageview', $options, $this->name, $this->version, $type );
146
-
147
- if ( is_404() ) {
148
- if ( exactmetrics_get_option( 'hash_tracking', false ) ) {
149
- $options['page_path'] = "${placeholder}'/404.html?page=' + document.location.pathname + document.location.search + location.hash + '&from=' + document.referrer${placeholder}";
150
- } else {
151
- $options['page_path'] = "${placeholder}'/404.html?page=' + document.location.pathname + document.location.search + '&from=' + document.referrer${placeholder}";
152
- }
153
- } else if ( $wp_query->is_search ) {
154
- $pushstr = "'/?s=";
155
- if ( 0 === (int) $wp_query->found_posts ) {
156
- $options['page_path'] = $pushstr . 'no-results:' . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=no-results'";
157
- } else if ( (int) $wp_query->found_posts === 1 ) {
158
- $options['page_path'] = $pushstr . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=1-result'";
159
- } else if ( $wp_query->found_posts > 1 && $wp_query->found_posts < 6 ) {
160
- $options['page_path'] = $pushstr . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=2-5-results'";
161
- } else {
162
- $options['page_path'] = $pushstr . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=plus-5-results'";
163
- }
164
- } else if ( exactmetrics_get_option( 'hash_tracking', false ) ) {
165
- $options['page_path'] = "${placeholder}location.pathname + location.search + location.hash${placeholder}";
166
- }
167
-
168
- $options = apply_filters( 'exactmetrics_frontend_tracking_options_gtag_end', $options, $type );
169
-
170
- if ( $encoded ) {
171
- return str_replace(
172
- array( '"' . $placeholder, $placeholder . '"' ),
173
- '',
174
- wp_json_encode( $options )
175
- );
176
- }
177
-
178
- return $options;
179
- }
180
-
181
- /**
182
- * Get frontend output.
183
- *
184
- * This function is used to return the Javascript
185
- * to output in the head of the page for the given
186
- * tracking method.
187
- *
188
- * @return string Javascript to output.
189
- * @since 7.15.0
190
- * @access public
191
- *
192
- */
193
- public function frontend_output() {
194
- $options = $this->frontend_tracking_options( 'ua', true );
195
- $options_v4 = $this->frontend_tracking_options( 'v4', true );
196
- $persistent = $this->frontend_tracking_options_persistent();
197
- $connected_type = ExactMetrics()->auth->get_connected_type();
198
- $v4_id = exactmetrics_get_v4_id_to_output();
199
- $ua = exactmetrics_get_ua_to_output();
200
- $main_id = $connected_type === 'ua' ? $ua : $v4_id;
201
- $src = apply_filters( 'exactmetrics_frontend_output_gtag_src', '//www.googletagmanager.com/gtag/js?id=' . $main_id );
202
- $compat_mode = apply_filters( 'exactmetrics_get_option_gtagtracker_compatibility_mode', true );
203
- $compat = $compat_mode ? 'window.gtag = __gtagTracker;' : '';
204
- $track_user = exactmetrics_track_user();
205
- $output = '';
206
- $reason = '';
207
- $attr_string = exactmetrics_get_frontend_analytics_script_atts();
208
- $gtag_async = apply_filters( 'exactmetrics_frontend_gtag_script_async', true ) ? 'async' : '';
209
- ob_start();
210
- ?>
211
- <!-- This site uses the Google Analytics by ExactMetrics plugin v<?php echo EXACTMETRICS_VERSION; ?> - Using Analytics tracking - https://www.exactmetrics.com/ -->
212
- <?php if ( ! $track_user ) {
213
- if ( empty( $v4_id ) && empty( $ua ) ) {
214
- $reason = __( 'Note: ExactMetrics is not currently configured on this site. The site owner needs to authenticate with Google Analytics in the ExactMetrics settings panel.', 'google-analytics-dashboard-for-wp' );
215
- $output .= '<!-- ' . esc_html( $reason ) . ' -->' . PHP_EOL;
216
- } else if ( current_user_can( 'exactmetrics_save_settings' ) ) {
217
- $reason = __( 'Note: ExactMetrics does not track you as a logged-in site administrator to prevent site owners from accidentally skewing their own Google Analytics data.' . PHP_EOL . 'If you are testing Google Analytics code, please do so either logged out or in the private browsing/incognito mode of your web browser.', 'google-analytics-dashboard-for-wp' );
218
- $output .= '<!-- ' . esc_html( $reason ) . ' -->' . PHP_EOL;
219
- } else {
220
- $reason = __( 'Note: The site owner has disabled Google Analytics tracking for your user role.', 'google-analytics-dashboard-for-wp' );
221
- $output .= '<!-- ' . esc_html( $reason ) . ' -->' . PHP_EOL;
222
- }
223
- echo $output;
224
- } ?>
225
- <?php if ( ! empty( $v4_id ) || ! empty( $ua ) ) { ?>
226
- <script src="<?php echo esc_attr( $src ); ?>" <?php echo $attr_string; ?> <?php echo esc_attr( $gtag_async ); ?>></script>
227
- <script<?php echo $attr_string; ?>>
228
- var em_version = '<?php echo EXACTMETRICS_VERSION; ?>';
229
- var em_track_user = <?php echo( $track_user ? 'true' : 'false' ); ?>;
230
- var em_no_track_reason = <?php echo( $reason ? "'" . esc_js( $reason ) . "'" : "''" ); ?>;
231
- <?php do_action( 'exactmetrics_tracking_gtag_frontend_output_after_em_track_user' ); ?>
232
-
233
- <?php if ( $this->should_do_optout() ) { ?>
234
- var disableStrs = [
235
- <?php if ( ! empty( $v4_id ) ): ?>
236
- 'ga-disable-<?php echo esc_js( $v4_id ); ?>',
237
- <?php endif; ?>
238
- <?php if ( ! empty( $ua ) ): ?>
239
- 'ga-disable-<?php echo esc_js( $ua ); ?>',
240
- <?php endif; ?>
241
- ];
242
-
243
- /* Function to detect opted out users */
244
- function __gtagTrackerIsOptedOut() {
245
- for ( var index = 0; index < disableStrs.length; index++ ) {
246
- if ( document.cookie.indexOf( disableStrs[ index ] + '=true' ) > -1 ) {
247
- return true;
248
- }
249
- }
250
-
251
- return false;
252
- }
253
-
254
- /* Disable tracking if the opt-out cookie exists. */
255
- if ( __gtagTrackerIsOptedOut() ) {
256
- for ( var index = 0; index < disableStrs.length; index++ ) {
257
- window[ disableStrs[ index ] ] = true;
258
- }
259
- }
260
-
261
- /* Opt-out function */
262
- function __gtagTrackerOptout() {
263
- for ( var index = 0; index < disableStrs.length; index++ ) {
264
- document.cookie = disableStrs[ index ] + '=true; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/';
265
- window[ disableStrs[ index ] ] = true;
266
- }
267
- }
268
-
269
- if ( 'undefined' === typeof gaOptout ) {
270
- function gaOptout() {
271
- __gtagTrackerOptout();
272
- }
273
- }
274
- <?php } ?>
275
- window.dataLayer = window.dataLayer || [];
276
-
277
- window.ExactMetricsDualTracker = {
278
- helpers: {},
279
- trackers: {},
280
- };
281
- if ( em_track_user ) {
282
- function __gtagDataLayer() {
283
- dataLayer.push( arguments );
284
- }
285
-
286
- function __gtagTracker( type, name, parameters ) {
287
- if (!parameters) {
288
- parameters = {};
289
- }
290
-
291
- if (parameters.send_to) {
292
- __gtagDataLayer.apply( null, arguments );
293
- return;
294
- }
295
-
296
- if ( type === 'event' ) {
297
- <?php if ( $v4_id ): ?>
298
- parameters.send_to = exactmetrics_frontend.v4_id;
299
- var hookName = name;
300
- if ( typeof parameters[ 'event_category' ] !== 'undefined' ) {
301
- hookName = parameters[ 'event_category' ] + ':' + name;
302
- }
303
-
304
- if ( typeof ExactMetricsDualTracker.trackers[ hookName ] !== 'undefined' ) {
305
- ExactMetricsDualTracker.trackers[ hookName ]( parameters );
306
- } else {
307
- __gtagDataLayer( 'event', name, parameters );
308
- }
309
- <?php endif; ?>
310
-
311
- <?php if ( $ua ): ?>
312
- parameters.send_to = exactmetrics_frontend.ua;
313
- __gtagDataLayer( type, name, parameters );
314
- <?php endif; ?>
315
- } else {
316
- __gtagDataLayer.apply( null, arguments );
317
- }
318
- }
319
- __gtagTracker( 'js', new Date() );
320
- __gtagTracker( 'set', {
321
- 'developer_id.dNDMyYj' : true,
322
- <?php
323
- if ( ! empty( $persistent ) ) {
324
- foreach ( $persistent as $key => $value ) {
325
- echo "'" . esc_js( $key ) . "' : '" . esc_js( $value ) . "',";
326
- }
327
- }
328
- ?>
329
- } );
330
- <?php if ( ! empty( $v4_id ) ): ?>
331
- __gtagTracker( 'config', '<?php echo esc_js( $v4_id ); ?>', <?php echo $options_v4; ?> );
332
- <?php endif; ?>
333
- <?php if ( ! empty( $ua ) ): ?>
334
- __gtagTracker( 'config', '<?php echo esc_js( $ua ); ?>', <?php echo $options; ?> );
335
- <?php
336
- endif;
337
- /**
338
- * Extend or enhance the functionality by adding custom code to frontend
339
- * tracking via this hook.
340
- *
341
- * @since 7.15.0
342
- */
343
- do_action( 'exactmetrics_frontend_tracking_gtag_after_pageview' );
344
- ?>
345
- <?php echo esc_js( $compat ); ?>
346
- <?php if ( apply_filters( 'exactmetrics_tracking_gtag_frontend_gatracker_compatibility', true ) ) { ?>
347
- (
348
- function () {
349
- /* https://developers.google.com/analytics/devguides/collection/analyticsjs/ */
350
- /* ga and __gaTracker compatibility shim. */
351
- var noopfn = function () {
352
- return null;
353
- };
354
- var newtracker = function () {
355
- return new Tracker();
356
- };
357
- var Tracker = function () {
358
- return null;
359
- };
360
- var p = Tracker.prototype;
361
- p.get = noopfn;
362
- p.set = noopfn;
363
- p.send = function (){
364
- var args = Array.prototype.slice.call(arguments);
365
- args.unshift( 'send' );
366
- __gaTracker.apply(null, args);
367
- };
368
- var __gaTracker = function () {
369
- var len = arguments.length;
370
- if ( len === 0 ) {
371
- return;
372
- }
373
- var f = arguments[len - 1];
374
- if ( typeof f !== 'object' || f === null || typeof f.hitCallback !== 'function' ) {
375
- if ( 'send' === arguments[0] ) {
376
- var hitConverted, hitObject = false, action;
377
- if ( 'event' === arguments[1] ) {
378
- if ( 'undefined' !== typeof arguments[3] ) {
379
- hitObject = {
380
- 'eventAction': arguments[3],
381
- 'eventCategory': arguments[2],
382
- 'eventLabel': arguments[4],
383
- 'value': arguments[5] ? arguments[5] : 1,
384
- }
385
- }
386
- }
387
- if ( 'pageview' === arguments[1] ) {
388
- if ( 'undefined' !== typeof arguments[2] ) {
389
- hitObject = {
390
- 'eventAction': 'page_view',
391
- 'page_path' : arguments[2],
392
- }
393
- }
394
- }
395
- if ( typeof arguments[2] === 'object' ) {
396
- hitObject = arguments[2];
397
- }
398
- if ( typeof arguments[5] === 'object' ) {
399
- Object.assign( hitObject, arguments[5] );
400
- }
401
- if ( 'undefined' !== typeof arguments[1].hitType ) {
402
- hitObject = arguments[1];
403
- if ( 'pageview' === hitObject.hitType ) {
404
- hitObject.eventAction = 'page_view';
405
- }
406
- }
407
- if ( hitObject ) {
408
- action = 'timing' === arguments[1].hitType ? 'timing_complete' : hitObject.eventAction;
409
- hitConverted = mapArgs( hitObject );
410
- __gtagTracker( 'event', action, hitConverted );
411
- }
412
- }
413
- return;
414
- }
415
-
416
- function mapArgs( args ) {
417
- var arg, hit = {};
418
- var gaMap = {
419
- 'eventCategory': 'event_category',
420
- 'eventAction': 'event_action',
421
- 'eventLabel': 'event_label',
422
- 'eventValue': 'event_value',
423
- 'nonInteraction': 'non_interaction',
424
- 'timingCategory': 'event_category',
425
- 'timingVar': 'name',
426
- 'timingValue': 'value',
427
- 'timingLabel': 'event_label',
428
- 'page' : 'page_path',
429
- 'location' : 'page_location',
430
- 'title' : 'page_title',
431
- };
432
- for ( arg in args ) {
433
- <?php // Note: we do || instead of && because FBIA can't encode && properly. ?>
434
- if ( ! ( ! args.hasOwnProperty(arg) || ! gaMap.hasOwnProperty(arg) ) ) {
435
- hit[gaMap[arg]] = args[arg];
436
- } else {
437
- hit[arg] = args[arg];
438
- }
439
- }
440
- return hit;
441
- }
442
-
443
- try {
444
- f.hitCallback();
445
- } catch ( ex ) {
446
- }
447
- };
448
- __gaTracker.create = newtracker;
449
- __gaTracker.getByName = newtracker;
450
- __gaTracker.getAll = function () {
451
- return [];
452
- };
453
- __gaTracker.remove = noopfn;
454
- __gaTracker.loaded = true;
455
- window['__gaTracker'] = __gaTracker;
456
- }
457
- )();
458
- <?php } ?>
459
- } else {
460
- <?php if ( $this->should_do_optout() ) { ?>
461
- console.log( "<?php echo esc_js( $reason );?>" );
462
- ( function () {
463
- function __gtagTracker() {
464
- return null;
465
- }
466
- window['__gtagTracker'] = __gtagTracker;
467
- window['gtag'] = __gtagTracker;
468
- } )();
469
- <?php } ?>
470
- }
471
- </script>
472
- <?php } else { ?>
473
- <!-- No UA code set -->
474
- <?php } ?>
475
- <!-- / Google Analytics by ExactMetrics -->
476
- <?php
477
- $output = ob_get_contents();
478
- ob_end_clean();
479
-
480
- return $output;
481
- }
482
-
483
- public function should_do_optout() {
484
- return ! ( defined( 'EM_NO_TRACKING_OPTOUT' ) && EM_NO_TRACKING_OPTOUT );
485
- }
486
- }
1
+ <?php
2
+ /**
3
+ * Tracking gtag.js class.
4
+ *
5
+ * @since 7.15.0
6
+ *
7
+ * @package ExactMetrics
8
+ * @author Mircea Sandu
9
+ */
10
+
11
+ // Exit if accessed directly
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ class ExactMetrics_Tracking_Gtag extends ExactMetrics_Tracking_Abstract {
17
+
18
+ /**
19
+ * Holds the name of the tracking type.
20
+ *
21
+ * @since 7.15.0
22
+ * @access public
23
+ *
24
+ * @var string $name Name of the tracking type.
25
+ */
26
+ public $name = 'gtag';
27
+
28
+ /**
29
+ * Version of the tracking class.
30
+ *
31
+ * @since 7.15.0
32
+ * @access public
33
+ *
34
+ * @var string $version Version of the tracking class.
35
+ */
36
+ public $version = '1.0.0';
37
+
38
+ /**
39
+ * Primary class constructor.
40
+ *
41
+ * @since 7.15.0
42
+ * @access public
43
+ */
44
+ public function __construct() {
45
+
46
+ }
47
+
48
+ /**
49
+ * Array of options that will be made persistent by setting them before the pageview.
50
+ *
51
+ * @see https://developers.google.com/analytics/devguides/collection/gtagjs/setting-values
52
+ * @return array Options for persistent values, like custom dimensions.
53
+ * @since 7.15.0
54
+ * @access public
55
+ */
56
+ public function frontend_tracking_options_persistent() {
57
+ $options = apply_filters( 'exactmetrics_frontend_tracking_options_persistent_gtag_before_pageview', array() );
58
+
59
+ return $options;
60
+ }
61
+
62
+ /**
63
+ * Get frontend tracking options for the gtag script.
64
+ *
65
+ * This function is used to return an array of parameters
66
+ * for the frontend_output() function to output. These are
67
+ * generally dimensions and turned on GA features.
68
+ *
69
+ * @param bool $encoded Whether to return a JavaScript object representation of the options
70
+ *
71
+ * @return array|string Options for the gtag config.
72
+ * @since 7.15.0
73
+ * @access public
74
+ *
75
+ */
76
+ public function frontend_tracking_options( $type = 'ua', $encoded = false ) {
77
+ global $wp_query;
78
+ $options = array();
79
+
80
+ $tracking_ids = exactmetrics_get_tracking_ids();
81
+ if ( empty( $tracking_ids ) ) {
82
+ return $encoded ? wp_json_encode( $options ) : $options;
83
+ }
84
+
85
+ $placeholder = '';
86
+
87
+ if ( $encoded ) {
88
+ $placeholder = '!@#';
89
+ }
90
+
91
+ // $track_user = exactmetrics_track_user();
92
+ //
93
+ // if ( ! $track_user ) {
94
+ // $options['create'] = "'create', '" . esc_js( $ua_code ) . "', '" . esc_js( 'auto' ) . "'";
95
+ // $options['forceSSL'] = "'set', 'forceSSL', true";
96
+ // $options['send'] = "'send','pageview'";
97
+ //
98
+ // return $options;
99
+ // }
100
+
101
+ $cross_domains = exactmetrics_get_option( 'cross_domains', array() );
102
+ $allow_anchor = exactmetrics_get_option( 'allow_anchor', false );
103
+
104
+ if ( $allow_anchor ) {
105
+ $options['allow_anchor'] = 'true';
106
+ }
107
+
108
+ if ( class_exists( 'ExactMetrics_AMP' ) ) {
109
+ $options['use_amp_client_id'] = 'true';
110
+ }
111
+
112
+
113
+ $options['forceSSL'] = 'true';
114
+
115
+ // Anonymous data.
116
+ if ( exactmetrics_get_option( 'anonymize_ips', false ) ) {
117
+ $options['anonymize_ip'] = 'true';
118
+ }
119
+
120
+ $options = apply_filters( 'exactmetrics_frontend_tracking_options_gtag_before_scripts', $options );
121
+
122
+ // Add Enhanced link attribution.
123
+ if ( exactmetrics_get_option( 'link_attribution', false ) ) {
124
+ $options['link_attribution'] = 'true';
125
+ }
126
+
127
+ // Add cross-domain tracking.
128
+ if ( is_array( $cross_domains ) && ! empty( $cross_domains ) ) {
129
+ $linker_domains = array();
130
+ foreach ( $cross_domains as $cross_domain ) {
131
+ if ( ! empty( $cross_domain['domain'] ) ) {
132
+ $linker_domains[] = $cross_domain['domain'];
133
+ }
134
+ }
135
+ $options['linker'] = array(
136
+ 'domains' => $linker_domains,
137
+ );
138
+ }
139
+
140
+ if ( exactmetrics_is_debug_mode() ) {
141
+ $options['debug_mode'] = true;
142
+ }
143
+
144
+ $options = apply_filters( 'exactmetrics_frontend_tracking_options_gtag_before_pageview', $options, $type );
145
+ $options = apply_filters( 'exactmetrics_frontend_tracking_options_before_pageview', $options, $this->name, $this->version, $type );
146
+
147
+ if ( is_404() ) {
148
+ if ( exactmetrics_get_option( 'hash_tracking', false ) ) {
149
+ $options['page_path'] = "${placeholder}'/404.html?page=' + document.location.pathname + document.location.search + location.hash + '&from=' + document.referrer${placeholder}";
150
+ } else {
151
+ $options['page_path'] = "${placeholder}'/404.html?page=' + document.location.pathname + document.location.search + '&from=' + document.referrer${placeholder}";
152
+ }
153
+ } else if ( $wp_query->is_search ) {
154
+ $pushstr = "'/?s=";
155
+ if ( 0 === (int) $wp_query->found_posts ) {
156
+ $options['page_path'] = $pushstr . 'no-results:' . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=no-results'";
157
+ } else if ( (int) $wp_query->found_posts === 1 ) {
158
+ $options['page_path'] = $pushstr . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=1-result'";
159
+ } else if ( $wp_query->found_posts > 1 && $wp_query->found_posts < 6 ) {
160
+ $options['page_path'] = $pushstr . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=2-5-results'";
161
+ } else {
162
+ $options['page_path'] = $pushstr . rawurlencode( $wp_query->query_vars['s'] ) . "&cat=plus-5-results'";
163
+ }
164
+ } else if ( exactmetrics_get_option( 'hash_tracking', false ) ) {
165
+ $options['page_path'] = "${placeholder}location.pathname + location.search + location.hash${placeholder}";
166
+ }
167
+
168
+ $options = apply_filters( 'exactmetrics_frontend_tracking_options_gtag_end', $options, $type );
169
+
170
+ if ( $encoded ) {
171
+ return str_replace(
172
+ array( '"' . $placeholder, $placeholder . '"' ),
173
+ '',
174
+ wp_json_encode( $options )
175
+ );
176
+ }
177
+
178
+ return $options;
179
+ }
180
+
181
+ /**
182
+ * Get frontend output.
183
+ *
184
+ * This function is used to return the Javascript
185
+ * to output in the head of the page for the given
186
+ * tracking method.
187
+ *
188
+ * @return string Javascript to output.
189
+ * @since 7.15.0
190
+ * @access public
191
+ *
192
+ */
193
+ public function frontend_output() {
194
+ $options = $this->frontend_tracking_options( 'ua', true );
195
+ $options_v4 = $this->frontend_tracking_options( 'v4', true );
196
+ $persistent = $this->frontend_tracking_options_persistent();
197
+ $connected_type = ExactMetrics()->auth->get_connected_type();
198
+ $v4_id = exactmetrics_get_v4_id_to_output();
199
+ $ua = exactmetrics_get_ua_to_output();
200
+ $main_id = $connected_type === 'ua' ? $ua : $v4_id;
201
+ $src = apply_filters( 'exactmetrics_frontend_output_gtag_src', '//www.googletagmanager.com/gtag/js?id=' . $main_id );
202
+ $compat_mode = apply_filters( 'exactmetrics_get_option_gtagtracker_compatibility_mode', true );
203
+ $compat = $compat_mode ? 'window.gtag = __gtagTracker;' : '';
204
+ $track_user = exactmetrics_track_user();
205
+ $output = '';
206
+ $reason = '';
207
+ $attr_string = exactmetrics_get_frontend_analytics_script_atts();
208
+ $gtag_async = apply_filters( 'exactmetrics_frontend_gtag_script_async', true ) ? 'async' : '';
209
+ ob_start();
210
+ ?>
211
+ <!-- This site uses the Google Analytics by ExactMetrics plugin v<?php echo EXACTMETRICS_VERSION; ?> - Using Analytics tracking - https://www.exactmetrics.com/ -->
212
+ <?php if ( ! $track_user ) {
213
+ if ( empty( $v4_id ) && empty( $ua ) ) {
214
+ $reason = __( 'Note: ExactMetrics is not currently configured on this site. The site owner needs to authenticate with Google Analytics in the ExactMetrics settings panel.', 'google-analytics-dashboard-for-wp' );
215
+ $output .= '<!-- ' . esc_html( $reason ) . ' -->' . PHP_EOL;
216
+ } else if ( current_user_can( 'exactmetrics_save_settings' ) ) {
217
+ $reason = __( 'Note: ExactMetrics does not track you as a logged-in site administrator to prevent site owners from accidentally skewing their own Google Analytics data.' . PHP_EOL . 'If you are testing Google Analytics code, please do so either logged out or in the private browsing/incognito mode of your web browser.', 'google-analytics-dashboard-for-wp' );
218
+ $output .= '<!-- ' . esc_html( $reason ) . ' -->' . PHP_EOL;
219
+ } else {
220
+ $reason = __( 'Note: The site owner has disabled Google Analytics tracking for your user role.', 'google-analytics-dashboard-for-wp' );
221
+ $output .= '<!-- ' . esc_html( $reason ) . ' -->' . PHP_EOL;
222
+ }
223
+ echo $output;
224
+ } ?>
225
+ <?php if ( ! empty( $v4_id ) || ! empty( $ua ) ) { ?>
226
+ <script src="<?php echo esc_attr( $src ); ?>" <?php echo $attr_string; ?> <?php echo esc_attr( $gtag_async ); ?>></script>
227
+ <script<?php echo $attr_string; ?>>
228
+ var em_version = '<?php echo EXACTMETRICS_VERSION; ?>';
229
+ var em_track_user = <?php echo( $track_user ? 'true' : 'false' ); ?>;
230
+ var em_no_track_reason = <?php echo( $reason ? "'" . esc_js( $reason ) . "'" : "''" ); ?>;
231
+ <?php do_action( 'exactmetrics_tracking_gtag_frontend_output_after_em_track_user' ); ?>
232
+
233
+ <?php if ( $this->should_do_optout() ) { ?>
234
+ var disableStrs = [
235
+ <?php if ( ! empty( $v4_id ) ): ?>
236
+ 'ga-disable-<?php echo esc_js( $v4_id ); ?>',
237
+ <?php endif; ?>
238
+ <?php if ( ! empty( $ua ) ): ?>
239
+ 'ga-disable-<?php echo esc_js( $ua ); ?>',
240
+ <?php endif; ?>
241
+ ];
242
+
243
+ /* Function to detect opted out users */
244
+ function __gtagTrackerIsOptedOut() {
245
+ for ( var index = 0; index < disableStrs.length; index++ ) {
246
+ if ( document.cookie.indexOf( disableStrs[ index ] + '=true' ) > -1 ) {
247
+ return true;
248
+ }
249
+ }
250
+
251
+ return false;
252
+ }
253
+
254
+ /* Disable tracking if the opt-out cookie exists. */
255
+ if ( __gtagTrackerIsOptedOut() ) {
256
+ for ( var index = 0; index < disableStrs.length; index++ ) {
257
+ window[ disableStrs[ index ] ] = true;
258
+ }
259
+ }
260
+
261
+ /* Opt-out function */
262
+ function __gtagTrackerOptout() {
263
+ for ( var index = 0; index < disableStrs.length; index++ ) {
264
+ document.cookie = disableStrs[ index ] + '=true; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/';
265
+ window[ disableStrs[ index ] ] = true;
266
+ }
267
+ }
268
+
269
+ if ( 'undefined' === typeof gaOptout ) {
270
+ function gaOptout() {
271
+ __gtagTrackerOptout();
272
+ }
273
+ }
274
+ <?php } ?>
275
+ window.dataLayer = window.dataLayer || [];
276
+
277
+ window.ExactMetricsDualTracker = {
278
+ helpers: {},
279
+ trackers: {},
280
+ };
281
+ if ( em_track_user ) {
282
+ function __gtagDataLayer() {
283
+ dataLayer.push( arguments );
284
+ }
285
+
286
+ function __gtagTracker( type, name, parameters ) {
287
+ if (!parameters) {
288
+ parameters = {};
289
+ }
290
+
291
+ if (parameters.send_to) {
292
+ __gtagDataLayer.apply( null, arguments );
293
+ return;
294
+ }
295
+
296
+ if ( type === 'event' ) {
297
+ <?php if ( $v4_id ): ?>
298
+ parameters.send_to = exactmetrics_frontend.v4_id;
299
+ var hookName = name;
300
+ if ( typeof parameters[ 'event_category' ] !== 'undefined' ) {
301
+ hookName = parameters[ 'event_category' ] + ':' + name;
302
+ }
303
+
304
+ if ( typeof ExactMetricsDualTracker.trackers[ hookName ] !== 'undefined' ) {
305
+ ExactMetricsDualTracker.trackers[ hookName ]( parameters );
306
+ } else {
307
+ __gtagDataLayer( 'event', name, parameters );
308
+ }
309
+ <?php endif; ?>
310
+
311
+ <?php if ( $ua ): ?>
312
+ parameters.send_to = exactmetrics_frontend.ua;
313
+ __gtagDataLayer( type, name, parameters );
314
+ <?php endif; ?>
315
+ } else {
316
+ __gtagDataLayer.apply( null, arguments );
317
+ }
318
+ }
319
+ __gtagTracker( 'js', new Date() );
320
+ __gtagTracker( 'set', {
321
+ 'developer_id.dNDMyYj' : true,
322
+ <?php
323
+ if ( ! empty( $persistent ) ) {
324
+ foreach ( $persistent as $key => $value ) {
325
+ echo "'" . esc_js( $key ) . "' : '" . esc_js( $value ) . "',";
326
+ }
327
+ }
328
+ ?>
329
+ } );
330
+ <?php if ( ! empty( $v4_id ) ): ?>
331
+ __gtagTracker( 'config', '<?php echo esc_js( $v4_id ); ?>', <?php echo $options_v4; ?> );
332
+ <?php endif; ?>
333
+ <?php if ( ! empty( $ua ) ): ?>
334
+ __gtagTracker( 'config', '<?php echo esc_js( $ua ); ?>', <?php echo $options; ?> );
335
+ <?php
336
+ endif;
337
+ /**
338
+ * Extend or enhance the functionality by adding custom code to frontend
339
+ * tracking via this hook.
340
+ *
341
+ * @since 7.15.0
342
+ */
343
+ do_action( 'exactmetrics_frontend_tracking_gtag_after_pageview' );
344
+ ?>
345
+ <?php echo esc_js( $compat ); ?>
346
+ <?php if ( apply_filters( 'exactmetrics_tracking_gtag_frontend_gatracker_compatibility', true ) ) { ?>
347
+ (
348
+ function () {
349
+ /* https://developers.google.com/analytics/devguides/collection/analyticsjs/ */
350
+ /* ga and __gaTracker compatibility shim. */
351
+ var noopfn = function () {
352
+ return null;
353
+ };
354
+ var newtracker = function () {
355
+ return new Tracker();
356
+ };
357
+ var Tracker = function () {
358
+ return null;
359
+ };
360
+ var p = Tracker.prototype;
361
+ p.get = noopfn;
362
+ p.set = noopfn;
363
+ p.send = function (){
364
+ var args = Array.prototype.slice.call(arguments);
365
+ args.unshift( 'send' );
366
+ __gaTracker.apply(null, args);
367
+ };
368
+ var __gaTracker = function () {
369
+ var len = arguments.length;
370
+ if ( len === 0 ) {
371
+ return;
372
+ }
373
+ var f = arguments[len - 1];
374
+ if ( typeof f !== 'object' || f === null || typeof f.hitCallback !== 'function' ) {
375
+ if ( 'send' === arguments[0] ) {
376
+ var hitConverted, hitObject = false, action;
377
+ if ( 'event' === arguments[1] ) {
378
+ if ( 'undefined' !== typeof arguments[3] ) {
379
+ hitObject = {
380
+ 'eventAction': arguments[3],
381
+ 'eventCategory': arguments[2],
382
+ 'eventLabel': arguments[4],
383
+ 'value': arguments[5] ? arguments[5] : 1,
384
+ }
385
+ }
386
+ }
387
+ if ( 'pageview' === arguments[1] ) {
388
+ if ( 'undefined' !== typeof arguments[2] ) {
389
+ hitObject = {
390
+ 'eventAction': 'page_view',
391
+ 'page_path' : arguments[2],
392
+ }
393
+ }
394
+ }
395
+ if ( typeof arguments[2] === 'object' ) {
396
+ hitObject = arguments[2];
397
+ }
398
+ if ( typeof arguments[5] === 'object' ) {
399
+ Object.assign( hitObject, arguments[5] );
400
+ }
401
+ if ( 'undefined' !== typeof arguments[1].hitType ) {
402
+ hitObject = arguments[1];
403
+ if ( 'pageview' === hitObject.hitType ) {
404
+ hitObject.eventAction = 'page_view';
405
+ }
406
+ }
407
+ if ( hitObject ) {
408
+ action = 'timing' === arguments[1].hitType ? 'timing_complete' : hitObject.eventAction;
409
+ hitConverted = mapArgs( hitObject );
410
+ __gtagTracker( 'event', action, hitConverted );
411
+ }
412
+ }
413
+ return;
414
+ }
415
+
416
+ function mapArgs( args ) {
417
+ var arg, hit = {};
418
+ var gaMap = {
419
+ 'eventCategory': 'event_category',
420
+ 'eventAction': 'event_action',
421
+ 'eventLabel': 'event_label',
422
+ 'eventValue': 'event_value',
423
+ 'nonInteraction': 'non_interaction',
424
+ 'timingCategory': 'event_category',
425
+ 'timingVar': 'name',
426
+ 'timingValue': 'value',
427
+ 'timingLabel': 'event_label',
428
+ 'page' : 'page_path',
429
+ 'location' : 'page_location',
430
+ 'title' : 'page_title',
431
+ };
432
+ for ( arg in args ) {
433
+ <?php // Note: we do || instead of && because FBIA can't encode && properly. ?>
434
+ if ( ! ( ! args.hasOwnProperty(arg) || ! gaMap.hasOwnProperty(arg) ) ) {
435
+ hit[gaMap[arg]] = args[arg];
436
+ } else {
437
+ hit[arg] = args[arg];
438
+ }
439
+ }
440
+ return hit;
441
+ }
442
+
443
+ try {
444
+ f.hitCallback();
445
+ } catch ( ex ) {
446
+ }
447
+ };
448
+ __gaTracker.create = newtracker;
449
+ __gaTracker.getByName = newtracker;
450
+ __gaTracker.getAll = function () {
451
+ return [];
452
+ };
453
+ __gaTracker.remove = noopfn;
454
+ __gaTracker.loaded = true;
455
+ window['__gaTracker'] = __gaTracker;
456
+ }
457
+ )();
458
+ <?php } ?>
459
+ } else {
460
+ <?php if ( $this->should_do_optout() ) { ?>
461
+ console.log( "<?php echo esc_js( $reason );?>" );
462
+ ( function () {
463
+ function __gtagTracker() {
464
+ return null;
465
+ }
466
+ window['__gtagTracker'] = __gtagTracker;
467
+ window['gtag'] = __gtagTracker;
468
+ } )();
469
+ <?php } ?>
470
+ }
471
+ </script>
472
+ <?php } else { ?>
473
+ <!-- No UA code set -->
474
+ <?php } ?>
475
+ <!-- / Google Analytics by ExactMetrics -->
476
+ <?php
477
+ $output = ob_get_contents();
478
+ ob_end_clean();
479
+
480
+ return $output;
481
+ }
482
+
483
+ public function should_do_optout() {
484
+ return ! ( defined( 'EM_NO_TRACKING_OPTOUT' ) && EM_NO_TRACKING_OPTOUT );
485
+ }
486
+ }
includes/popular-posts/class-popular-posts-themes.php CHANGED
@@ -1,850 +1,850 @@
1
- <?php
2
- /**
3
- * Popular posts theme-specific functionality.
4
- *
5
- * @package ExactMetrics
6
- */
7
-
8
- /**
9
- * Class ExactMetrics_Popular_Posts_Themes
10
- */
11
- class ExactMetrics_Popular_Posts_Themes {
12
-
13
- /**
14
- * The type of widget to load themes for (inline, widget, products).
15
- *
16
- * @var string
17
- */
18
- public $type;
19
-
20
- /**
21
- * Holds the array of themes specific to the current type loaded.
22
- *
23
- * @var array
24
- */
25
- public $themes = array();
26
-
27
- /**
28
- * Holds the data for the currently selected theme (if indicated in the constructor).
29
- *
30
- * @var array
31
- */
32
- public $theme = array();
33
-
34
- /**
35
- * The current theme instance with styles from settings applied.
36
- *
37
- * @var array
38
- */
39
- public $styled_theme;
40
-
41
- /**
42
- * The theme options key used to store specific theme styles.
43
- *
44
- * @var string
45
- */
46
- private $themes_styles_key = 'exactmetrics_popular_posts_theme_settings';
47
-
48
- /**
49
- * Variable for the theme settings.
50
- *
51
- * @var array
52
- */
53
- private $themes_styles;
54
-
55
- /**
56
- * ExactMetrics_Popular_Posts_Themes constructor.
57
- *
58
- * @param string $type The type of widget: inline/widget/products.
59
- * @param string $theme The current theme to load details for.
60
- */
61
- public function __construct( $type = 'inline', $theme = '' ) {
62
-
63
- $this->type = $type;
64
- if ( method_exists( $this, 'get_themes_' . $type ) ) {
65
- $this->themes = call_user_func( array( $this, 'get_themes_' . $type ) );
66
- if ( ! empty( $theme ) ) {
67
- $this->theme = isset( $this->themes[ $theme ] ) ? $this->themes[ $theme ] : array();
68
-
69
- return $this->theme;
70
- } else {
71
- return $this->themes;
72
- }
73
- }
74
-
75
- return false;
76
-
77
- }
78
-
79
- /**
80
- * Get the current theme details with the option to load properties already styled.
81
- *
82
- * @return array|mixed
83
- */
84
- public function get_theme() {
85
-
86
- return $this->theme;
87
-
88
- }
89
-
90
- /**
91
- * Get the currently loaded themes for the widget type.
92
- *
93
- * @return array|mixed
94
- */
95
- public function get_themes() {
96
- return $this->themes;
97
- }
98
-
99
- public function get_theme_stored_styles() {
100
- if ( ! isset( $this->themes_styles ) ) {
101
- $this->themes_styles = get_option( $this->themes_styles_key, array() );
102
- }
103
-
104
- return $this->themes_styles;
105
- }
106
-
107
- /**
108
- * Go through the themes and apply styles from the stored settings.
109
- *
110
- * @var string $type The instance type: inline/widget/products.
111
- * @var array $themes The themes to process/apply styles for.
112
- */
113
- public function process_themes_styles( $type, $themes ) {
114
-
115
- $settings = $this->get_theme_stored_styles();
116
-
117
- if ( ! empty( $settings[ $type ] ) ) {
118
- foreach ( $themes as $theme_key => $theme_values ) {
119
- if ( ! empty( $settings[ $type ][ $theme_key ] ) ) {
120
- foreach ( $themes[ $theme_key ]['styles'] as $object => $props ) {
121
- if ( ! empty( $settings[ $type ][ $theme_key ][ $object ] ) ) {
122
- foreach ( $props as $style_key => $style_value ) {
123
- if ( ! empty( $settings[ $type ][ $theme_key ][ $object ][ $style_key ] ) ) {
124
- $themes[ $theme_key ]['styles'][ $object ][ $style_key ] = $settings[ $type ][ $theme_key ][ $object ][ $style_key ];
125
- }
126
- }
127
- }
128
- }
129
- }
130
- }
131
- }
132
-
133
- return $themes;
134
-
135
- }
136
-
137
- /**
138
- * Get the themes for the inline widget type.
139
- *
140
- * @return array
141
- */
142
- public function get_themes_inline() {
143
-
144
- $themes = array(
145
- 'alpha' => array(
146
- 'label' => 'Alpha',
147
- 'styles' => array(
148
- 'title' => array(
149
- 'color' => '#393F4C',
150
- 'size' => 18,
151
- 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
152
- ),
153
- 'label' => array(
154
- 'color' => '#EB5757',
155
- 'text' => 'Trending',
156
- ),
157
- 'background' => array(
158
- 'color' => '#F0F2F4',
159
- ),
160
- ),
161
- 'level' => 'lite',
162
- ),
163
- 'beta' => array(
164
- 'label' => 'Beta',
165
- 'styles' => array(
166
- 'title' => array(
167
- 'color' => '#393F4C',
168
- 'size' => 18,
169
- 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
170
- ),
171
- 'label' => array(
172
- 'color' => '#EB5757',
173
- 'text' => 'Trending',
174
- ),
175
- 'background' => array(
176
- 'border' => '#F0F2F4',
177
- ),
178
- 'image' => 'theme-preview-beta.png',
179
- ),
180
- 'image' => true,
181
- 'level' => 'lite',
182
- ),
183
- 'charlie' => array(
184
- 'label' => 'Charlie',
185
- 'styles' => array(
186
- 'title' => array(
187
- 'color' => '#393f4c',
188
- 'size' => 16,
189
- ),
190
- 'label' => array(
191
- 'color' => '#393f4c',
192
- 'text' => 'Popular Stories Right now',
193
- ),
194
- 'border' => array(
195
- 'color' => '#D3D7DE',
196
- ),
197
- ),
198
- 'list' => array(
199
- '15 Proven Ways to Repurpose Content on Your WordPress Site',
200
- 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
201
- 'How to Set Up Online Ordering for Your Restaurant Website',
202
- ),
203
- 'posts' => 3,
204
- 'level' => 'lite',
205
- ),
206
- 'delta' => array(
207
- 'label' => 'Delta',
208
- 'styles' => array(
209
- 'icon' => array(
210
- 'color' => '#EB5757',
211
- ),
212
- 'title' => array(
213
- 'color' => '#393f4c',
214
- 'size' => 16,
215
- 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
216
- ),
217
- 'label' => array(
218
- 'color' => '#EB5757',
219
- 'text' => 'Trending',
220
- ),
221
- 'background' => array(
222
- 'border' => '#F0F2F4',
223
- ),
224
- ),
225
- 'level' => 'plus',
226
- ),
227
- 'echo' => array(
228
- 'label' => 'Echo',
229
- 'styles' => array(
230
- 'title' => array(
231
- 'color' => '#393f4c',
232
- 'size' => 18,
233
- 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
234
- ),
235
- 'label' => array(
236
- 'color' => '#EB5757',
237
- 'text' => 'Trending:',
238
- ),
239
- 'background' => array(
240
- 'color' => '#F0F2F4',
241
- ),
242
- ),
243
- 'level' => 'plus',
244
- ),
245
- 'foxtrot' => array(
246
- 'label' => 'Foxtrot',
247
- 'styles' => array(
248
- 'title' => array(
249
- 'color' => '#393f4c',
250
- 'size' => 18,
251
- 'text' => 'How to Build an Email List in WordPress – Email Marketing 101',
252
- ),
253
- 'label' => array(
254
- 'color' => '#EB5757',
255
- 'text' => 'Trending',
256
- ),
257
- 'image' => 'theme-preview-image.jpg',
258
- ),
259
- 'image' => true,
260
- 'level' => 'plus',
261
- ),
262
- 'golf' => array(
263
- 'label' => 'Golf',
264
- 'styles' => array(
265
- 'title' => array(
266
- 'color' => '#393f4c',
267
- 'size' => 16,
268
- 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
269
- ),
270
- 'label' => array(
271
- 'color' => '#EB5757',
272
- 'text' => 'Popular now',
273
- ),
274
- 'border' => array(
275
- 'color' => '#EB5757',
276
- 'color2' => '#E2E4E9',
277
- ),
278
- ),
279
- 'level' => 'plus',
280
- ),
281
- 'hotel' => array(
282
- 'label' => 'Hotel',
283
- 'styles' => array(
284
- 'title' => array(
285
- 'color' => '#393f4c',
286
- 'size' => 18,
287
- 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
288
- ),
289
- 'icon' => array(
290
- 'color' => '#EB5757',
291
- ),
292
- ),
293
- 'level' => 'plus',
294
- ),
295
- 'india' => array(
296
- 'label' => 'India',
297
- 'styles' => array(
298
- 'title' => array(
299
- 'color' => '#393f4c',
300
- 'size' => 14,
301
- 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
302
- ),
303
- 'label' => array(
304
- 'color' => '#EB5757',
305
- 'text' => 'Trending:',
306
- ),
307
- 'border' => array(
308
- 'color' => '#EB5757',
309
- ),
310
- 'background' => array(
311
- 'color' => '#f0f2f4',
312
- ),
313
- ),
314
- 'level' => 'plus',
315
- ),
316
- 'juliett' => array(
317
- 'label' => 'Juliett',
318
- 'styles' => array(
319
- 'title' => array(
320
- 'color' => '#393f4c',
321
- 'size' => 18,
322
- 'text' => 'How to Build an Email List in WordPress – Email Marketing 101',
323
- ),
324
- 'label' => array(
325
- 'color' => '#393f4c',
326
- 'background' => '#e2e4e9',
327
- 'text' => 'Trending',
328
- ),
329
- 'border' => array(
330
- 'color' => '#e2e4e9',
331
- ),
332
- ),
333
- 'image' => true,
334
- 'level' => 'plus',
335
- ),
336
- 'kilo' => array(
337
- 'label' => 'Kilo',
338
- 'styles' => array(
339
- 'title' => array(
340
- 'color' => '#393f4c',
341
- 'size' => 18,
342
- 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
343
- ),
344
- 'label' => array(
345
- 'color' => '#EB5757',
346
- 'text' => 'Popular now',
347
- ),
348
- 'border' => array(
349
- 'color' => '#e2e4e9',
350
- 'color2' => '#e2e4e9',
351
- ),
352
- ),
353
- 'level' => 'plus',
354
- ),
355
- 'lima' => array(
356
- 'label' => 'Lima',
357
- 'styles' => array(
358
- 'title' => array(
359
- 'color' => '#393f4c',
360
- 'size' => 18,
361
- 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
362
- ),
363
- 'label' => array(
364
- 'color' => '#EB5757',
365
- 'text' => 'Trending',
366
- ),
367
- 'background' => array(
368
- 'color' => '#f0f2f4',
369
- ),
370
- 'image' => 'theme-preview-image-2.jpg',
371
- ),
372
- 'image' => true,
373
- 'level' => 'plus',
374
- ),
375
- 'mike' => array(
376
- 'label' => 'Mike',
377
- 'styles' => array(
378
- 'title' => array(
379
- 'color' => '#393f4c',
380
- 'size' => 18,
381
- 'text' => 'How to Build an Email List in WordPress – Email Marketing 101',
382
- ),
383
- 'label' => array(
384
- 'color' => '#fff',
385
- 'background' => '#f2994a',
386
- 'text' => 'Trending',
387
- ),
388
- 'background' => array(
389
- 'color' => '#f0f2f4',
390
- ),
391
- 'image' => 'theme-preview-image.jpg',
392
- ),
393
- 'image' => true,
394
- 'level' => 'plus',
395
- ),
396
- 'november' => array(
397
- 'label' => 'November',
398
- 'styles' => array(
399
- 'title' => array(
400
- 'color' => '#393f4c',
401
- 'size' => 16,
402
- 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
403
- ),
404
- 'label' => array(
405
- 'color' => '#eb5757',
406
- 'text' => 'Trending',
407
- ),
408
- 'background' => array(
409
- 'border' => '#f0f2f4',
410
- ),
411
- 'icon' => array(
412
- 'background' => '#eb5757',
413
- 'color' => '#fff',
414
- ),
415
- ),
416
- 'level' => 'plus',
417
- ),
418
- );
419
-
420
- return $this->process_themes_styles( 'inline', $themes );
421
-
422
- }
423
-
424
- /**
425
- * Get the themese for the widget instance.
426
- *
427
- * @return array
428
- */
429
- public function get_themes_widget() {
430
-
431
- $themes = array(
432
- 'alpha' => array(
433
- 'label' => 'Alpha',
434
- 'styles' => array(
435
- 'title' => array(
436
- 'color' => '#393F4C',
437
- 'size' => 16,
438
- ),
439
- 'background' => array(
440
- 'color' => '#F0F2F4',
441
- ),
442
-
443
- ),
444
- 'list' => array(
445
- 'items' => array(
446
- 'How to Set Up WordPress User Activity Tracking in 3 Easy Steps',
447
- 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
448
- '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
449
- '9 Proven Ways to Get Google to Index Your Website Right Away',
450
- ),
451
- ),
452
- 'level' => 'lite',
453
- ),
454
- 'beta' => array(
455
- 'label' => 'Beta',
456
- 'styles' => array(
457
- 'title' => array(
458
- 'color' => '#393F4C',
459
- 'size' => 16,
460
- ),
461
- 'background' => array(
462
- 'border' => '#1EC185',
463
- ),
464
- ),
465
- 'list' => array(
466
- 'items' => array(
467
- '9 Proven Ways to Get Google to Index Your Website Right Away',
468
- 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
469
- '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
470
- 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
471
- ),
472
- ),
473
- 'level' => 'lite',
474
- ),
475
- 'charlie' => array(
476
- 'label' => 'Charlie',
477
- 'styles' => array(
478
- 'title' => array(
479
- 'color' => '#393f4c',
480
- 'size' => 16,
481
- ),
482
- 'background' => array(
483
- 'color' => '#F0F2F4',
484
- 'border' => '#338EEF',
485
- ),
486
- ),
487
- 'list' => array(
488
- 'items' => array(
489
- 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
490
- 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
491
- '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
492
- '9 Proven Ways to Get Google to Index Your Website Right Away',
493
- ),
494
- ),
495
- 'level' => 'lite',
496
- ),
497
- 'delta' => array(
498
- 'label' => 'Delta',
499
- 'styles' => array(
500
- 'title' => array(
501
- 'color' => '#393f4c',
502
- 'size' => 18,
503
- ),
504
- 'background' => array(
505
- 'border' => '#D3D7DE',
506
- ),
507
- 'meta' => array(
508
- 'color' => '#99A1B3',
509
- 'author' => 'on',
510
- 'date' => 'on',
511
- 'separator' => '&#9679;',
512
- ),
513
- ),
514
- 'list' => array(
515
- 'items' => array(
516
- '9 Proven Ways to Get Google to Index Your Website Right Away',
517
- 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
518
- '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
519
- 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
520
- ),
521
- 'images' => array(
522
- 'theme-widget-1.jpg',
523
- 'theme-widget-2.jpg',
524
- 'theme-widget-3.jpg',
525
- 'theme-widget-4.jpg',
526
- ),
527
- ),
528
- 'image' => true,
529
- 'level' => 'pro',
530
- ),
531
- 'echo' => array(
532
- 'label' => 'Echo',
533
- 'styles' => array(
534
- 'title' => array(
535
- 'color' => '#393f4c',
536
- 'size' => 16,
537
- ),
538
- 'meta' => array(
539
- 'color' => '#99A1B3',
540
- 'size' => 12,
541
- 'author' => 'on',
542
- 'date' => 'on',
543
- 'comments' => 'on',
544
- 'separator' => 'on',
545
- ),
546
- 'comments' => array(
547
- 'color' => '#393F4C',
548
- ),
549
- ),
550
- 'list' => array(
551
- 'items' => array(
552
- '9 Proven Ways to Get Google to Index Your Website Right Away',
553
- 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
554
- '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
555
- 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
556
- ),
557
- 'images' => array(
558
- 'theme-widget-5.jpg',
559
- 'theme-widget-5.jpg',
560
- 'theme-widget-5.jpg',
561
- 'theme-widget-5.jpg',
562
- ),
563
- ),
564
- 'image' => true,
565
- 'level' => 'pro',
566
- ),
567
- 'foxtrot' => array(
568
- 'label' => 'Foxtrot',
569
- 'styles' => array(
570
- 'title' => array(
571
- 'color' => '#393f4c',
572
- 'size' => 16,
573
- ),
574
- 'meta' => array(
575
- 'color' => '#99A1B3',
576
- 'size' => 12,
577
- 'author' => 'on',
578
- 'date' => 'on',
579
- 'comments' => 'on',
580
- 'separator' => '|',
581
- ),
582
- 'comments' => array(
583
- 'color' => '#393F4C',
584
- ),
585
- ),
586
- 'list' => array(
587
- 'items' => array(
588
- '9 Proven Ways to Get Google to Index Your Website Right Away',
589
- '12 Best Social Media Analytics Tools for Marketers (Tried & Tested) ',
590
- 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
591
- 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
592
- ),
593
- ),
594
- 'level' => 'pro',
595
- ),
596
- 'golf' => array(
597
- 'label' => 'Golf',
598
- 'styles' => array(
599
- 'title' => array(
600
- 'color' => '#393f4c',
601
- 'size' => 16,
602
- ),
603
- 'label' => array(
604
- 'color' => '#fff',
605
- 'background' => '#EB5757',
606
- 'text' => 'Trending:',
607
- 'editable' => true,
608
- ),
609
- 'meta' => array(
610
- 'color' => '#99A1B3',
611
- 'size' => 12,
612
- 'author' => 'on',
613
- 'date' => 'on',
614
- 'comments' => 'on',
615
- 'separator' => '|',
616
- ),
617
- 'comments' => array(
618
- 'color' => '#393F4C',
619
- ),
620
- ),
621
- 'list' => array(
622
- 'items' => array(
623
- '9 Proven Ways to Get Google to Index Your Website Right Away',
624
- '12 Best Social Media Analytics Tools for Marketers (Tried & Tested) ',
625
- 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
626
- 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
627
- ),
628
- ),
629
- 'level' => 'pro',
630
- ),
631
- 'hotel' => array(
632
- 'label' => 'Hotel',
633
- 'styles' => array(
634
- 'title' => array(
635
- 'color' => '#fff',
636
- 'size' => 16,
637
- ),
638
- 'meta' => array(
639
- 'color' => '#fff',
640
- 'size' => 12,
641
- 'author' => 'on',
642
- 'date' => 'on',
643
- ),
644
- ),
645
- 'list' => array(
646
- 'items' => array(
647
- 'How to Allow WordPress to Upload All File Types (The Easy Way)',
648
- '14 Handy Google Search Operators for SEO (A Complete List)',
649
- 'How to Write Irresistible Meta Descriptions for SEO & More Clicks?',
650
- 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
651
- ),
652
- 'images' => array(
653
- 'theme-widget-5.jpg',
654
- 'theme-widget-6.jpg',
655
- 'theme-widget-7.jpg',
656
- 'theme-widget-8.jpg',
657
- ),
658
- ),
659
- 'image' => true,
660
- 'level' => 'pro',
661
- ),
662
- );
663
-
664
- return $this->process_themes_styles( 'widget', $themes );
665
-
666
- }
667
-
668
- /**
669
- * Get the themes for the products widget.
670
- *
671
- * @return array
672
- */
673
- public function get_themes_products() {
674
- $themes = array(
675
- 'alpha' => array(
676
- 'label' => 'Alpha',
677
- 'styles' => array(
678
- 'title' => array(
679
- 'color' => '#393F4C',
680
- 'size' => 16,
681
- ),
682
- 'background' => array(
683
- 'border' => '#d3d7de',
684
- ),
685
- 'price' => array(
686
- 'color' => '#393F4C',
687
- 'size' => 12,
688
- ),
689
- 'rating' => array(
690
- 'color' => '#EB5757',
691
- ),
692
- 'meta' => array(
693
- 'price' => 'on',
694
- 'rating' => 'on',
695
- 'image' => 'on',
696
- ),
697
- ),
698
- 'list' => array(
699
- 'items' => array(
700
- 'WPBeginner 10-Year Anniversary Gray T-Shirt',
701
- 'WPForms Small White Logo T-Shirt',
702
- 'OptinMonster White Text Color Mascot T-Shirt',
703
- 'WPForms Make Things Simple Gray T-Shirt',
704
- ),
705
- 'images' => array(
706
- 'theme-products-1.jpg',
707
- 'theme-products-2.jpg',
708
- 'theme-products-3.jpg',
709
- 'theme-products-4.jpg',
710
- ),
711
- 'prices' => array(
712
- '$59.99',
713
- '$28.00',
714
- '$65.00',
715
- '$59.50',
716
- ),
717
- ),
718
- ),
719
- 'beta' => array(
720
- 'label' => 'Beta',
721
- 'styles' => array(
722
- 'title' => array(
723
- 'color' => '#393F4C',
724
- 'size' => 16,
725
- ),
726
- 'background' => array(
727
- 'color' => '#F0F2F4',
728
- ),
729
- 'price' => array(
730
- 'color' => '#4C5566',
731
- 'size' => 12,
732
- ),
733
- 'rating' => array(
734
- 'color' => '#F2D74A',
735
- ),
736
- 'meta' => array(
737
- 'price' => 'on',
738
- 'rating' => 'on',
739
- 'image' => 'on',
740
- ),
741
- ),
742
- 'list' => array(
743
- 'items' => array(
744
- 'Admin WPBeginner Black T-Shirt',
745
- 'Black WP Beginner logo T-Shirt',
746
- 'Technically Awesome Groovy White T-Shirt',
747
- 'Technically Awesome Code Comment Black T-Shirt',
748
- ),
749
- 'images' => array(
750
- 'theme-products-5.jpg',
751
- 'theme-products-7.jpg',
752
- 'theme-products-6.jpg',
753
- 'theme-products-8.jpg',
754
- ),
755
- 'prices' => array(
756
- '$29.50',
757
- '$28.00',
758
- '$65.00',
759
- '$59.50',
760
- ),
761
- ),
762
- ),
763
- 'charlie' => array(
764
- 'label' => 'Charlie',
765
- 'styles' => array(
766
- 'title' => array(
767
- 'color' => '#fff',
768
- 'size' => 16,
769
- ),
770
- 'rating' => array(
771
- 'color' => '#F2D74A',
772
- ),
773
- 'price' => array(
774
- 'color' => '#fff',
775
- 'size' => 12,
776
- ),
777
- 'meta' => array(
778
- 'price' => 'on',
779
- 'rating' => 'on',
780
- ),
781
- ),
782
- 'list' => array(
783
- 'items' => array(
784
- 'Admin WPBeginner Black T-Shirt',
785
- 'Black WP Beginner logo T-Shirt',
786
- 'Technically Awesome Groovy White T-Shirt',
787
- 'Technically Awesome Code Comment Black T-Shirt',
788
- ),
789
- 'images' => array(
790
- 'theme-products-5.jpg',
791
- 'theme-products-7.jpg',
792
- 'theme-products-6.jpg',
793
- 'theme-products-8.jpg',
794
- ),
795
- 'prices' => array(
796
- '$29.50',
797
- '$28.00',
798
- '$65.00',
799
- '$59.50',
800
- ),
801
- ),
802
- 'image' => true,
803
- ),
804
- 'delta' => array(
805
- 'label' => 'Delta',
806
- 'styles' => array(
807
- 'title' => array(
808
- 'color' => '#393f4c',
809
- 'size' => 14,
810
- ),
811
- 'rating' => array(
812
- 'color' => '#F2D74A',
813
- ),
814
- 'price' => array(
815
- 'color' => '#4C5566',
816
- 'size' => 12,
817
- ),
818
- 'meta' => array(
819
- 'price' => 'on',
820
- 'rating' => 'on',
821
- 'image' => 'on',
822
- ),
823
- ),
824
- 'list' => array(
825
- 'items' => array(
826
- 'Admin WPBeginner Black T-Shirt',
827
- 'Black WP Beginner logo T-Shirt',
828
- 'Technically Awesome Groovy White T-Shirt',
829
- 'Technically Awesome Code Comment Black T-Shirt',
830
- ),
831
- 'images' => array(
832
- 'theme-products-5.jpg',
833
- 'theme-products-7.jpg',
834
- 'theme-products-6.jpg',
835
- 'theme-products-8.jpg',
836
- ),
837
- 'prices' => array(
838
- '$29.50',
839
- '$28.00',
840
- '$65.00',
841
- '$59.50',
842
- ),
843
- ),
844
- ),
845
- );
846
-
847
- return $this->process_themes_styles( 'products', $themes );
848
- }
849
-
850
- }
1
+ <?php
2
+ /**
3
+ * Popular posts theme-specific functionality.
4
+ *
5
+ * @package ExactMetrics
6
+ */
7
+
8
+ /**
9
+ * Class ExactMetrics_Popular_Posts_Themes
10
+ */
11
+ class ExactMetrics_Popular_Posts_Themes {
12
+
13
+ /**
14
+ * The type of widget to load themes for (inline, widget, products).
15
+ *
16
+ * @var string
17
+ */
18
+ public $type;
19
+
20
+ /**
21
+ * Holds the array of themes specific to the current type loaded.
22
+ *
23
+ * @var array
24
+ */
25
+ public $themes = array();
26
+
27
+ /**
28
+ * Holds the data for the currently selected theme (if indicated in the constructor).
29
+ *
30
+ * @var array
31
+ */
32
+ public $theme = array();
33
+
34
+ /**
35
+ * The current theme instance with styles from settings applied.
36
+ *
37
+ * @var array
38
+ */
39
+ public $styled_theme;
40
+
41
+ /**
42
+ * The theme options key used to store specific theme styles.
43
+ *
44
+ * @var string
45
+ */
46
+ private $themes_styles_key = 'exactmetrics_popular_posts_theme_settings';
47
+
48
+ /**
49
+ * Variable for the theme settings.
50
+ *
51
+ * @var array
52
+ */
53
+ private $themes_styles;
54
+
55
+ /**
56
+ * ExactMetrics_Popular_Posts_Themes constructor.
57
+ *
58
+ * @param string $type The type of widget: inline/widget/products.
59
+ * @param string $theme The current theme to load details for.
60
+ */
61
+ public function __construct( $type = 'inline', $theme = '' ) {
62
+
63
+ $this->type = $type;
64
+ if ( method_exists( $this, 'get_themes_' . $type ) ) {
65
+ $this->themes = call_user_func( array( $this, 'get_themes_' . $type ) );
66
+ if ( ! empty( $theme ) ) {
67
+ $this->theme = isset( $this->themes[ $theme ] ) ? $this->themes[ $theme ] : array();
68
+
69
+ return $this->theme;
70
+ } else {
71
+ return $this->themes;
72
+ }
73
+ }
74
+
75
+ return false;
76
+
77
+ }
78
+
79
+ /**
80
+ * Get the current theme details with the option to load properties already styled.
81
+ *
82
+ * @return array|mixed
83
+ */
84
+ public function get_theme() {
85
+
86
+ return $this->theme;
87
+
88
+ }
89
+
90
+ /**
91
+ * Get the currently loaded themes for the widget type.
92
+ *
93
+ * @return array|mixed
94
+ */
95
+ public function get_themes() {
96
+ return $this->themes;
97
+ }
98
+
99
+ public function get_theme_stored_styles() {
100
+ if ( ! isset( $this->themes_styles ) ) {
101
+ $this->themes_styles = get_option( $this->themes_styles_key, array() );
102
+ }
103
+
104
+ return $this->themes_styles;
105
+ }
106
+
107
+ /**
108
+ * Go through the themes and apply styles from the stored settings.
109
+ *
110
+ * @var string $type The instance type: inline/widget/products.
111
+ * @var array $themes The themes to process/apply styles for.
112
+ */
113
+ public function process_themes_styles( $type, $themes ) {
114
+
115
+ $settings = $this->get_theme_stored_styles();
116
+
117
+ if ( ! empty( $settings[ $type ] ) ) {
118
+ foreach ( $themes as $theme_key => $theme_values ) {
119
+ if ( ! empty( $settings[ $type ][ $theme_key ] ) ) {
120
+ foreach ( $themes[ $theme_key ]['styles'] as $object => $props ) {
121
+ if ( ! empty( $settings[ $type ][ $theme_key ][ $object ] ) ) {
122
+ foreach ( $props as $style_key => $style_value ) {
123
+ if ( ! empty( $settings[ $type ][ $theme_key ][ $object ][ $style_key ] ) ) {
124
+ $themes[ $theme_key ]['styles'][ $object ][ $style_key ] = $settings[ $type ][ $theme_key ][ $object ][ $style_key ];
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ return $themes;
134
+
135
+ }
136
+
137
+ /**
138
+ * Get the themes for the inline widget type.
139
+ *
140
+ * @return array
141
+ */
142
+ public function get_themes_inline() {
143
+
144
+ $themes = array(
145
+ 'alpha' => array(
146
+ 'label' => 'Alpha',
147
+ 'styles' => array(
148
+ 'title' => array(
149
+ 'color' => '#393F4C',
150
+ 'size' => 18,
151
+ 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
152
+ ),
153
+ 'label' => array(
154
+ 'color' => '#EB5757',
155
+ 'text' => 'Trending',
156
+ ),
157
+ 'background' => array(
158
+ 'color' => '#F0F2F4',
159
+ ),
160
+ ),
161
+ 'level' => 'lite',
162
+ ),
163
+ 'beta' => array(
164
+ 'label' => 'Beta',
165
+ 'styles' => array(
166
+ 'title' => array(
167
+ 'color' => '#393F4C',
168
+ 'size' => 18,
169
+ 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
170
+ ),
171
+ 'label' => array(
172
+ 'color' => '#EB5757',
173
+ 'text' => 'Trending',
174
+ ),
175
+ 'background' => array(
176
+ 'border' => '#F0F2F4',
177
+ ),
178
+ 'image' => 'theme-preview-beta.png',
179
+ ),
180
+ 'image' => true,
181
+ 'level' => 'lite',
182
+ ),
183
+ 'charlie' => array(
184
+ 'label' => 'Charlie',
185
+ 'styles' => array(
186
+ 'title' => array(
187
+ 'color' => '#393f4c',
188
+ 'size' => 16,
189
+ ),
190
+ 'label' => array(
191
+ 'color' => '#393f4c',
192
+ 'text' => 'Popular Stories Right now',
193
+ ),
194
+ 'border' => array(
195
+ 'color' => '#D3D7DE',
196
+ ),
197
+ ),
198
+ 'list' => array(
199
+ '15 Proven Ways to Repurpose Content on Your WordPress Site',
200
+ 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
201
+ 'How to Set Up Online Ordering for Your Restaurant Website',
202
+ ),
203
+ 'posts' => 3,
204
+ 'level' => 'lite',
205
+ ),
206
+ 'delta' => array(
207
+ 'label' => 'Delta',
208
+ 'styles' => array(
209
+ 'icon' => array(
210
+ 'color' => '#EB5757',
211
+ ),
212
+ 'title' => array(
213
+ 'color' => '#393f4c',
214
+ 'size' => 16,
215
+ 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
216
+ ),
217
+ 'label' => array(
218
+ 'color' => '#EB5757',
219
+ 'text' => 'Trending',
220
+ ),
221
+ 'background' => array(
222
+ 'border' => '#F0F2F4',
223
+ ),
224
+ ),
225
+ 'level' => 'plus',
226
+ ),
227
+ 'echo' => array(
228
+ 'label' => 'Echo',
229
+ 'styles' => array(
230
+ 'title' => array(
231
+ 'color' => '#393f4c',
232
+ 'size' => 18,
233
+ 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
234
+ ),
235
+ 'label' => array(
236
+ 'color' => '#EB5757',
237
+ 'text' => 'Trending:',
238
+ ),
239
+ 'background' => array(
240
+ 'color' => '#F0F2F4',
241
+ ),
242
+ ),
243
+ 'level' => 'plus',
244
+ ),
245
+ 'foxtrot' => array(
246
+ 'label' => 'Foxtrot',
247
+ 'styles' => array(
248
+ 'title' => array(
249
+ 'color' => '#393f4c',
250
+ 'size' => 18,
251
+ 'text' => 'How to Build an Email List in WordPress – Email Marketing 101',
252
+ ),
253
+ 'label' => array(
254
+ 'color' => '#EB5757',
255
+ 'text' => 'Trending',
256
+ ),
257
+ 'image' => 'theme-preview-image.jpg',
258
+ ),
259
+ 'image' => true,
260
+ 'level' => 'plus',
261
+ ),
262
+ 'golf' => array(
263
+ 'label' => 'Golf',
264
+ 'styles' => array(
265
+ 'title' => array(
266
+ 'color' => '#393f4c',
267
+ 'size' => 16,
268
+ 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
269
+ ),
270
+ 'label' => array(
271
+ 'color' => '#EB5757',
272
+ 'text' => 'Popular now',
273
+ ),
274
+ 'border' => array(
275
+ 'color' => '#EB5757',
276
+ 'color2' => '#E2E4E9',
277
+ ),
278
+ ),
279
+ 'level' => 'plus',
280
+ ),
281
+ 'hotel' => array(
282
+ 'label' => 'Hotel',
283
+ 'styles' => array(
284
+ 'title' => array(
285
+ 'color' => '#393f4c',
286
+ 'size' => 18,
287
+ 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
288
+ ),
289
+ 'icon' => array(
290
+ 'color' => '#EB5757',
291
+ ),
292
+ ),
293
+ 'level' => 'plus',
294
+ ),
295
+ 'india' => array(
296
+ 'label' => 'India',
297
+ 'styles' => array(
298
+ 'title' => array(
299
+ 'color' => '#393f4c',
300
+ 'size' => 14,
301
+ 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
302
+ ),
303
+ 'label' => array(
304
+ 'color' => '#EB5757',
305
+ 'text' => 'Trending:',
306
+ ),
307
+ 'border' => array(
308
+ 'color' => '#EB5757',
309
+ ),
310
+ 'background' => array(
311
+ 'color' => '#f0f2f4',
312
+ ),
313
+ ),
314
+ 'level' => 'plus',
315
+ ),
316
+ 'juliett' => array(
317
+ 'label' => 'Juliett',
318
+ 'styles' => array(
319
+ 'title' => array(
320
+ 'color' => '#393f4c',
321
+ 'size' => 18,
322
+ 'text' => 'How to Build an Email List in WordPress – Email Marketing 101',
323
+ ),
324
+ 'label' => array(
325
+ 'color' => '#393f4c',
326
+ 'background' => '#e2e4e9',
327
+ 'text' => 'Trending',
328
+ ),
329
+ 'border' => array(
330
+ 'color' => '#e2e4e9',
331
+ ),
332
+ ),
333
+ 'image' => true,
334
+ 'level' => 'plus',
335
+ ),
336
+ 'kilo' => array(
337
+ 'label' => 'Kilo',
338
+ 'styles' => array(
339
+ 'title' => array(
340
+ 'color' => '#393f4c',
341
+ 'size' => 18,
342
+ 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
343
+ ),
344
+ 'label' => array(
345
+ 'color' => '#EB5757',
346
+ 'text' => 'Popular now',
347
+ ),
348
+ 'border' => array(
349
+ 'color' => '#e2e4e9',
350
+ 'color2' => '#e2e4e9',
351
+ ),
352
+ ),
353
+ 'level' => 'plus',
354
+ ),
355
+ 'lima' => array(
356
+ 'label' => 'Lima',
357
+ 'styles' => array(
358
+ 'title' => array(
359
+ 'color' => '#393f4c',
360
+ 'size' => 18,
361
+ 'text' => '15 Proven Ways to Repurpose Content on Your WordPress Site',
362
+ ),
363
+ 'label' => array(
364
+ 'color' => '#EB5757',
365
+ 'text' => 'Trending',
366
+ ),
367
+ 'background' => array(
368
+ 'color' => '#f0f2f4',
369
+ ),
370
+ 'image' => 'theme-preview-image-2.jpg',
371
+ ),
372
+ 'image' => true,
373
+ 'level' => 'plus',
374
+ ),
375
+ 'mike' => array(
376
+ 'label' => 'Mike',
377
+ 'styles' => array(
378
+ 'title' => array(
379
+ 'color' => '#393f4c',
380
+ 'size' => 18,
381
+ 'text' => 'How to Build an Email List in WordPress – Email Marketing 101',
382
+ ),
383
+ 'label' => array(
384
+ 'color' => '#fff',
385
+ 'background' => '#f2994a',
386
+ 'text' => 'Trending',
387
+ ),
388
+ 'background' => array(
389
+ 'color' => '#f0f2f4',
390
+ ),
391
+ 'image' => 'theme-preview-image.jpg',
392
+ ),
393
+ 'image' => true,
394
+ 'level' => 'plus',
395
+ ),
396
+ 'november' => array(
397
+ 'label' => 'November',
398
+ 'styles' => array(
399
+ 'title' => array(
400
+ 'color' => '#393f4c',
401
+ 'size' => 16,
402
+ 'text' => 'How to Use Google Trends to Boost Traffic and Sales (9 Simple Ways)',
403
+ ),
404
+ 'label' => array(
405
+ 'color' => '#eb5757',
406
+ 'text' => 'Trending',
407
+ ),
408
+ 'background' => array(
409
+ 'border' => '#f0f2f4',
410
+ ),
411
+ 'icon' => array(
412
+ 'background' => '#eb5757',
413
+ 'color' => '#fff',
414
+ ),
415
+ ),
416
+ 'level' => 'plus',
417
+ ),
418
+ );
419
+
420
+ return $this->process_themes_styles( 'inline', $themes );
421
+
422
+ }
423
+
424
+ /**
425
+ * Get the themese for the widget instance.
426
+ *
427
+ * @return array
428
+ */
429
+ public function get_themes_widget() {
430
+
431
+ $themes = array(
432
+ 'alpha' => array(
433
+ 'label' => 'Alpha',
434
+ 'styles' => array(
435
+ 'title' => array(
436
+ 'color' => '#393F4C',
437
+ 'size' => 16,
438
+ ),
439
+ 'background' => array(
440
+ 'color' => '#F0F2F4',
441
+ ),
442
+
443
+ ),
444
+ 'list' => array(
445
+ 'items' => array(
446
+ 'How to Set Up WordPress User Activity Tracking in 3 Easy Steps',
447
+ 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
448
+ '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
449
+ '9 Proven Ways to Get Google to Index Your Website Right Away',
450
+ ),
451
+ ),
452
+ 'level' => 'lite',
453
+ ),
454
+ 'beta' => array(
455
+ 'label' => 'Beta',
456
+ 'styles' => array(
457
+ 'title' => array(
458
+ 'color' => '#393F4C',
459
+ 'size' => 16,
460
+ ),
461
+ 'background' => array(
462
+ 'border' => '#1EC185',
463
+ ),
464
+ ),
465
+ 'list' => array(
466
+ 'items' => array(
467
+ '9 Proven Ways to Get Google to Index Your Website Right Away',
468
+ 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
469
+ '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
470
+ 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
471
+ ),
472
+ ),
473
+ 'level' => 'lite',
474
+ ),
475
+ 'charlie' => array(
476
+ 'label' => 'Charlie',
477
+ 'styles' => array(
478
+ 'title' => array(
479
+ 'color' => '#393f4c',
480
+ 'size' => 16,
481
+ ),
482
+ 'background' => array(
483
+ 'color' => '#F0F2F4',
484
+ 'border' => '#338EEF',
485
+ ),
486
+ ),
487
+ 'list' => array(
488
+ 'items' => array(
489
+ 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
490
+ 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
491
+ '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
492
+ '9 Proven Ways to Get Google to Index Your Website Right Away',
493
+ ),
494
+ ),
495
+ 'level' => 'lite',
496
+ ),
497
+ 'delta' => array(
498
+ 'label' => 'Delta',
499
+ 'styles' => array(
500
+ 'title' => array(
501
+ 'color' => '#393f4c',
502
+ 'size' => 18,
503
+ ),
504
+ 'background' => array(
505
+ 'border' => '#D3D7DE',
506
+ ),
507
+ 'meta' => array(
508
+ 'color' => '#99A1B3',
509
+ 'author' => 'on',
510
+ 'date' => 'on',
511
+ 'separator' => '&#9679;',
512
+ ),
513
+ ),
514
+ 'list' => array(
515
+ 'items' => array(
516
+ '9 Proven Ways to Get Google to Index Your Website Right Away',
517
+ 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
518
+ '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
519
+ 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
520
+ ),
521
+ 'images' => array(
522
+ 'theme-widget-1.jpg',
523
+ 'theme-widget-2.jpg',
524
+ 'theme-widget-3.jpg',
525
+ 'theme-widget-4.jpg',
526
+ ),
527
+ ),
528
+ 'image' => true,
529
+ 'level' => 'pro',
530
+ ),
531
+ 'echo' => array(
532
+ 'label' => 'Echo',
533
+ 'styles' => array(
534
+ 'title' => array(
535
+ 'color' => '#393f4c',
536
+ 'size' => 16,
537
+ ),
538
+ 'meta' => array(
539
+ 'color' => '#99A1B3',
540
+ 'size' => 12,
541
+ 'author' => 'on',
542
+ 'date' => 'on',
543
+ 'comments' => 'on',
544
+ 'separator' => 'on',
545
+ ),
546
+ 'comments' => array(
547
+ 'color' => '#393F4C',
548
+ ),
549
+ ),
550
+ 'list' => array(
551
+ 'items' => array(
552
+ '9 Proven Ways to Get Google to Index Your Website Right Away',
553
+ 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
554
+ '12 Best Social Media Analytics Tools for Marketers (Tried & Tested)',
555
+ 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
556
+ ),
557
+ 'images' => array(
558
+ 'theme-widget-5.jpg',
559
+ 'theme-widget-5.jpg',
560
+ 'theme-widget-5.jpg',
561
+ 'theme-widget-5.jpg',
562
+ ),
563
+ ),
564
+ 'image' => true,
565
+ 'level' => 'pro',
566
+ ),
567
+ 'foxtrot' => array(
568
+ 'label' => 'Foxtrot',
569
+ 'styles' => array(
570
+ 'title' => array(
571
+ 'color' => '#393f4c',
572
+ 'size' => 16,
573
+ ),
574
+ 'meta' => array(
575
+ 'color' => '#99A1B3',
576
+ 'size' => 12,
577
+ 'author' => 'on',
578
+ 'date' => 'on',
579
+ 'comments' => 'on',
580
+ 'separator' => '|',
581
+ ),
582
+ 'comments' => array(
583
+ 'color' => '#393F4C',
584
+ ),
585
+ ),
586
+ 'list' => array(
587
+ 'items' => array(
588
+ '9 Proven Ways to Get Google to Index Your Website Right Away',
589
+ '12 Best Social Media Analytics Tools for Marketers (Tried & Tested) ',
590
+ 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
591
+ 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
592
+ ),
593
+ ),
594
+ 'level' => 'pro',
595
+ ),
596
+ 'golf' => array(
597
+ 'label' => 'Golf',
598
+ 'styles' => array(
599
+ 'title' => array(
600
+ 'color' => '#393f4c',
601
+ 'size' => 16,
602
+ ),
603
+ 'label' => array(
604
+ 'color' => '#fff',
605
+ 'background' => '#EB5757',
606
+ 'text' => 'Trending:',
607
+ 'editable' => true,
608
+ ),
609
+ 'meta' => array(
610
+ 'color' => '#99A1B3',
611
+ 'size' => 12,
612
+ 'author' => 'on',
613
+ 'date' => 'on',
614
+ 'comments' => 'on',
615
+ 'separator' => '|',
616
+ ),
617
+ 'comments' => array(
618
+ 'color' => '#393F4C',
619
+ ),
620
+ ),
621
+ 'list' => array(
622
+ 'items' => array(
623
+ '9 Proven Ways to Get Google to Index Your Website Right Away',
624
+ '12 Best Social Media Analytics Tools for Marketers (Tried & Tested) ',
625
+ 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
626
+ 'How to Share Your Google Analytics Reports with Others (5 Easy Ways)',
627
+ ),
628
+ ),
629
+ 'level' => 'pro',
630
+ ),
631
+ 'hotel' => array(
632
+ 'label' => 'Hotel',
633
+ 'styles' => array(
634
+ 'title' => array(
635
+ 'color' => '#fff',
636
+ 'size' => 16,
637
+ ),
638
+ 'meta' => array(
639
+ 'color' => '#fff',
640
+ 'size' => 12,
641
+ 'author' => 'on',
642
+ 'date' => 'on',
643
+ ),
644
+ ),
645
+ 'list' => array(
646
+ 'items' => array(
647
+ 'How to Allow WordPress to Upload All File Types (The Easy Way)',
648
+ '14 Handy Google Search Operators for SEO (A Complete List)',
649
+ 'How to Write Irresistible Meta Descriptions for SEO & More Clicks?',
650
+ 'Uncover How Much Traffic Does a Website Get (5 Effortless Ways)',
651
+ ),
652
+ 'images' => array(
653
+ 'theme-widget-5.jpg',
654
+ 'theme-widget-6.jpg',
655
+ 'theme-widget-7.jpg',
656
+ 'theme-widget-8.jpg',
657
+ ),
658
+ ),
659
+ 'image' => true,
660
+ 'level' => 'pro',
661
+ ),
662
+ );
663
+
664
+ return $this->process_themes_styles( 'widget', $themes );
665
+
666
+ }
667
+
668
+ /**
669
+ * Get the themes for the products widget.
670
+ *
671
+ * @return array
672
+ */
673
+ public function get_themes_products() {
674
+ $themes = array(
675
+ 'alpha' => array(
676
+ 'label' => 'Alpha',
677
+ 'styles' => array(
678
+ 'title' => array(
679
+ 'color' => '#393F4C',
680
+ 'size' => 16,
681
+ ),
682
+ 'background' => array(
683
+ 'border' => '#d3d7de',
684
+ ),
685
+ 'price' => array(
686
+ 'color' => '#393F4C',
687
+ 'size' => 12,
688
+ ),
689
+ 'rating' => array(
690
+ 'color' => '#EB5757',
691
+ ),
692
+ 'meta' => array(
693
+ 'price' => 'on',
694
+ 'rating' => 'on',
695
+ 'image' => 'on',
696
+ ),
697
+ ),
698
+ 'list' => array(
699
+ 'items' => array(
700
+ 'WPBeginner 10-Year Anniversary Gray T-Shirt',
701
+ 'WPForms Small White Logo T-Shirt',
702
+ 'OptinMonster White Text Color Mascot T-Shirt',
703
+ 'WPForms Make Things Simple Gray T-Shirt',
704
+ ),
705
+ 'images' => array(
706
+ 'theme-products-1.jpg',
707
+ 'theme-products-2.jpg',
708
+ 'theme-products-3.jpg',
709
+ 'theme-products-4.jpg',
710
+ ),
711
+ 'prices' => array(
712
+ '$59.99',
713
+ '$28.00',
714
+ '$65.00',
715
+ '$59.50',
716
+ ),
717
+ ),
718
+ ),
719
+ 'beta' => array(
720
+ 'label' => 'Beta',
721
+ 'styles' => array(
722
+ 'title' => array(
723
+ 'color' => '#393F4C',
724
+ 'size' => 16,
725
+ ),
726
+ 'background' => array(
727
+ 'color' => '#F0F2F4',
728
+ ),
729
+ 'price' => array(
730
+ 'color' => '#4C5566',
731
+ 'size' => 12,
732
+ ),
733
+ 'rating' => array(
734
+ 'color' => '#F2D74A',
735
+ ),
736
+ 'meta' => array(
737
+ 'price' => 'on',
738
+ 'rating' => 'on',
739
+ 'image' => 'on',
740
+ ),
741
+ ),
742
+ 'list' => array(
743
+ 'items' => array(
744
+ 'Admin WPBeginner Black T-Shirt',
745
+ 'Black WP Beginner logo T-Shirt',
746
+ 'Technically Awesome Groovy White T-Shirt',
747
+ 'Technically Awesome Code Comment Black T-Shirt',
748
+ ),
749
+ 'images' => array(
750
+ 'theme-products-5.jpg',
751
+ 'theme-products-7.jpg',
752
+ 'theme-products-6.jpg',
753
+ 'theme-products-8.jpg',
754
+ ),
755
+ 'prices' => array(
756
+ '$29.50',
757
+ '$28.00',
758
+ '$65.00',
759
+ '$59.50',
760
+ ),
761
+ ),
762
+ ),
763
+ 'charlie' => array(
764
+ 'label' => 'Charlie',
765
+ 'styles' => array(
766
+ 'title' => array(
767
+ 'color' => '#fff',
768
+ 'size' => 16,
769
+ ),
770
+ 'rating' => array(
771
+ 'color' => '#F2D74A',
772
+ ),
773
+ 'price' => array(
774
+ 'color' => '#fff',
775
+ 'size' => 12,
776
+ ),
777
+ 'meta' => array(
778
+ 'price' => 'on',
779
+ 'rating' => 'on',
780
+ ),
781
+ ),
782
+ 'list' => array(
783
+ 'items' => array(
784
+ 'Admin WPBeginner Black T-Shirt',
785
+ 'Black WP Beginner logo T-Shirt',
786
+ 'Technically Awesome Groovy White T-Shirt',
787
+ 'Technically Awesome Code Comment Black T-Shirt',
788
+ ),
789
+ 'images' => array(
790
+ 'theme-products-5.jpg',
791
+ 'theme-products-7.jpg',
792
+ 'theme-products-6.jpg',
793
+ 'theme-products-8.jpg',
794
+ ),
795
+ 'prices' => array(
796
+ '$29.50',
797
+ '$28.00',
798
+ '$65.00',
799
+ '$59.50',
800
+ ),
801
+ ),
802
+ 'image' => true,
803
+ ),
804
+ 'delta' => array(
805
+ 'label' => 'Delta',
806
+ 'styles' => array(
807
+ 'title' => array(
808
+ 'color' => '#393f4c',
809
+ 'size' => 14,
810
+ ),
811
+ 'rating' => array(
812
+ 'color' => '#F2D74A',
813
+ ),
814
+ 'price' => array(
815
+ 'color' => '#4C5566',
816
+ 'size' => 12,
817
+ ),
818
+ 'meta' => array(
819
+ 'price' => 'on',
820
+ 'rating' => 'on',
821
+ 'image' => 'on',
822
+ ),
823
+ ),
824
+ 'list' => array(
825
+ 'items' => array(
826
+ 'Admin WPBeginner Black T-Shirt',
827
+ 'Black WP Beginner logo T-Shirt',
828
+ 'Technically Awesome Groovy White T-Shirt',
829
+ 'Technically Awesome Code Comment Black T-Shirt',
830
+ ),
831
+ 'images' => array(
832
+ 'theme-products-5.jpg',
833
+ 'theme-products-7.jpg',
834
+ 'theme-products-6.jpg',
835
+ 'theme-products-8.jpg',
836
+ ),
837
+ 'prices' => array(
838
+ '$29.50',
839
+ '$28.00',
840
+ '$65.00',
841
+ '$59.50',
842
+ ),
843
+ ),
844
+ ),
845
+ );
846
+
847
+ return $this->process_themes_styles( 'products', $themes );
848
+ }
849
+
850
+ }
includes/popular-posts/class-popular-posts.php CHANGED
@@ -1,742 +1,742 @@
1
- <?php
2
- /**
3
- * This is the base class for the Popular Posts output functionality.
4
- * Each actual Popular Posts option extends this class (inline, widget, products).
5
- *
6
- * @package ExactMetrics
7
- */
8
-
9
- /**
10
- * Class ExactMetrics_Popular_Posts
11
- */
12
- class ExactMetrics_Popular_Posts {
13
-
14
- /**
15
- * The key prefix used to store the settings for the magic __get method.
16
- *
17
- * @var string
18
- */
19
- protected $settings_key;
20
-
21
- /**
22
- * Name of the shortcode
23
- *
24
- * @var string
25
- */
26
- protected $shortcode_key;
27
-
28
- /**
29
- * The popular posts object type, by default inline, widget or products.
30
- *
31
- * @var string
32
- */
33
- protected $type;
34
-
35
- /**
36
- * An array of posts used in the query process.
37
- *
38
- * @var array
39
- */
40
- public $posts = array();
41
-
42
- /**
43
- * An array of posts already displayed. Used to avoid duplicate posts on the same page.
44
- *
45
- * @var array
46
- */
47
- public $shown_posts = array();
48
-
49
- /**
50
- * The inline styles string with theme specifics from the Vue settings.
51
- * Each instance should append to this variable so we print styles for all the instances in the same place.
52
- *
53
- * @var string
54
- */
55
- public static $inline_styles = '';
56
-
57
- /**
58
- * Stores the option to use ajax to display the popular posts widgets on the frontend.
59
- *
60
- * @var string
61
- */
62
- public $ajaxify;
63
-
64
- /**
65
- * Stores the cache instance, specific to the plugin version.
66
- *
67
- * @var ExactMetrics_Popular_Posts_Cache
68
- */
69
- public $cache;
70
-
71
- /**
72
- * Holds the class object.
73
- *
74
- * @since 7.13.0
75
- * @access public
76
- * @var array
77
- */
78
- public static $instances = array();
79
-
80
- /**
81
- * @var ExactMetrics_Popular_Posts_Themes
82
- */
83
- protected $theme_props;
84
-
85
- /**
86
- * Indicator that inline styles have been printed to avoid duplicates.
87
- *
88
- * @var bool
89
- */
90
- private static $styles_printed = false;
91
-
92
- /**
93
- * Number of posts to query from the db. Not all queried posts are used for display in the same widget.
94
- *
95
- * @var int
96
- */
97
- public $posts_count = 15;
98
-
99
- /**
100
- * ExactMetrics_Popular_Posts constructor.
101
- */
102
- public function __construct() {
103
-
104
- $this->hooks();
105
- $this->register_shortcode();
106
-
107
- $this->ajaxify = exactmetrics_get_option( 'popular_posts_ajaxify', false );
108
- }
109
-
110
- /**
111
- * Magic get for different types of popular posts.
112
- *
113
- * @param $name
114
- *
115
- * @return string|array|mixed
116
- */
117
- public function __get( $name ) {
118
- return exactmetrics_get_option( $this->settings_key . '_' . $name );
119
- }
120
-
121
- /**
122
- * Add hooks needed for the output.
123
- */
124
- public function hooks() {
125
- add_action( 'wp_enqueue_scripts', array( $this, 'load_frontend_styles' ) );
126
-
127
- add_action( 'wp_enqueue_scripts', array( $this, 'maybe_load_ajaxify_script' ) );
128
-
129
- $this->add_inline_styles();
130
- }
131
-
132
- /**
133
- * Add inline styles for each widget type to a single variable for printing.
134
- */
135
- protected function add_inline_styles() {
136
- if ( 'no_styles' !== $this->styling ) {
137
- self::$inline_styles .= $this->build_inline_styles();
138
- }
139
- }
140
-
141
- /**
142
- * Should return object-specific inline styles.
143
- *
144
- * @return string
145
- */
146
- public function build_inline_styles() {
147
- return '';
148
- }
149
-
150
- /**
151
- * Register the shortcode for the specific class.
152
- */
153
- public function register_shortcode() {
154
-
155
- if ( ! empty( $this->shortcode_key ) ) {
156
- add_shortcode( $this->shortcode_key, array( $this, 'render_shortcode' ) );
157
- }
158
-
159
- }
160
-
161
- /**
162
- * Load the frontend styles if they are enabled.
163
- */
164
- public function load_frontend_styles() {
165
-
166
- // Only load our styles if enabled.
167
- if ( apply_filters( 'exactmetrics_popular_posts_styles_output', 'no_styles' === $this->styling, $this ) ) {
168
- return;
169
- }
170
- $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
171
-
172
- // Load Popular Posts styles.
173
- wp_register_style( 'exactmetrics-popular-posts-style', plugins_url( 'assets/css/frontend' . $suffix . '.css', EXACTMETRICS_PLUGIN_FILE ), array(), exactmetrics_get_asset_version() );
174
-
175
- $this->add_theme_specific_styles();
176
-
177
- }
178
-
179
- /**
180
- * If the Ajaxify option is enabled, print needed scripts.
181
- */
182
- public function maybe_load_ajaxify_script() {
183
- if ( ! $this->ajaxify ) {
184
- return;
185
- }
186
- $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
187
-
188
- wp_register_script( 'exactmetrics-popular-posts-js', plugins_url( 'assets/js/popular-posts' . $suffix . '.js', EXACTMETRICS_PLUGIN_FILE ), array(), exactmetrics_get_asset_version(), true );
189
-
190
- wp_enqueue_script( 'exactmetrics-popular-posts-js' );
191
-
192
- wp_localize_script( 'exactmetrics-popular-posts-js', 'exactmetrics_pp', array(
193
- 'ajaxurl' => admin_url( 'admin-ajax.php' ),
194
- 'post_id' => get_the_ID(),
195
- ) );
196
-
197
- }
198
-
199
- /**
200
- * Add inline styles based on customizations from the vue panel.
201
- */
202
- public function add_theme_specific_styles() {
203
-
204
- if ( ! self::$styles_printed ) {
205
- wp_add_inline_style( 'exactmetrics-popular-posts-style', $this->get_inline_styles() );
206
- self::$styles_printed = true;
207
- }
208
-
209
- }
210
-
211
- /**
212
- * We have a single static variable for inline styles shared by all instances so we print just once.
213
- *
214
- * @return string
215
- */
216
- public function get_inline_styles() {
217
- return self::$inline_styles;
218
- }
219
-
220
- /**
221
- * Rendering the shortcode.
222
- *
223
- * @return string
224
- */
225
- public function render_shortcode( $args ) {
226
-
227
- return apply_filters( 'exactmetrics_popular_posts_shortcode_output', $this->shortcode_output( $args ), $args, $this );
228
-
229
- }
230
-
231
- /**
232
- * Output of shortcode based on settings.
233
- *
234
- * @param array $args Arguments from shortcode/block.
235
- *
236
- * @return string
237
- */
238
- public function shortcode_output( $args ) {
239
- // Load frontend.css file when shortcode is available
240
- wp_enqueue_style( 'exactmetrics-popular-posts-style' );
241
-
242
- if ( $this->ajaxify ) {
243
- return $this->get_ajax_json_data( $args );
244
- } else {
245
- return $this->get_rendered_html( $args );
246
- }
247
- }
248
-
249
- /**
250
- * Print inline JSON data that with settings that get processed using an AJAX call. Acts similar to printing out
251
- * a shortcode with its settings but actually loading the output for that after the page was loaded, with AJAX.
252
- *
253
- * @param array $args Arguments from shortcode/block.
254
- *
255
- * @return string
256
- */
257
- public function get_ajax_json_data( $args ) {
258
-
259
- $args['type'] = $this->type;
260
-
261
- $data = '<div><script type="application/json" class="exactmetrics-popular-posts-widget-json">';
262
- $data .= wp_json_encode( $args );
263
- $data .= '</script></div>';
264
-
265
- return $data;
266
- }
267
-
268
- /**
269
- * This is replaced with actual HTML output in child classes.
270
- *
271
- * @param array $args Arguments used to build specific html.
272
- *
273
- * @return string
274
- */
275
- public function get_rendered_html( $args ) {
276
- return '';
277
- }
278
-
279
- /**
280
- * Get the cache instance for the set type.
281
- *
282
- * @return ExactMetrics_Popular_Posts_Cache
283
- */
284
- public function get_cache() {
285
- if ( ! isset( $this->cache ) ) {
286
- $this->cache = new ExactMetrics_Popular_Posts_Cache( $this->type );
287
- }
288
-
289
- return $this->cache;
290
- }
291
-
292
- /**
293
- * Use the query args to grab posts from the database.
294
- */
295
- public function get_posts() {
296
-
297
- $posts_args = $this->get_query_args();
298
-
299
- $posts = $this->get_cache()->get_cached_posts( $posts_args );
300
-
301
- if ( empty( $posts ) ) {
302
-
303
- if ( isset( $posts_args['post__in'] ) && empty( $posts_args['post__in'] ) ) {
304
- $this->posts = array();
305
-
306
- return $this->posts;
307
- }
308
- $posts = get_posts( $posts_args );
309
-
310
- $posts = $this->process_posts( $posts );
311
-
312
- $this->get_cache()->save_posts_to_cache( $posts_args, $posts );
313
- }
314
-
315
- return apply_filters( 'exactmetrics_popular_posts_posts', $posts );
316
-
317
- }
318
-
319
- /**
320
- * Go through posts from a WP Query and prepare them for output.
321
- *
322
- * @param array $posts Array of posts from WP Query or similar, also supports array of ids.
323
- *
324
- * @return array
325
- */
326
- private function process_posts( $posts ) {
327
- $processed_posts = array();
328
- foreach ( $posts as $post ) {
329
- if ( is_int( $post ) ) {
330
- $post = get_post( $post );
331
- }
332
- $post_thumbnail = get_post_thumbnail_id( $post->ID );
333
- $post_image = '';
334
- $post_image_srcset = '';
335
- if ( ! empty( $post_thumbnail ) ) {
336
- $post_image = wp_get_attachment_image_src( $post_thumbnail, 'medium' );
337
- if ( is_array( $post_image ) && ! empty( $post_image[0] ) ) {
338
- $post_image = $post_image[0];
339
- }
340
- $post_image_srcset = wp_get_attachment_image_srcset( $post_thumbnail, 'medium' );
341
- }
342
-
343
- $author_data = get_userdata( $post->post_author );
344
-
345
- $processed_posts[] = array(
346
- 'id' => $post->ID,
347
- 'title' => get_the_title( $post->ID ),
348
- 'link' => get_permalink( $post->ID ),
349
- 'image' => $post_image,
350
- 'srcset' => $post_image_srcset,
351
- 'image_id' => $post_thumbnail,
352
- 'author' => $post->post_author,
353
- 'author_name' => $author_data->display_name,
354
- 'date' => get_the_date( '', $post->ID ),
355
- 'comments' => get_comments_number( $post->ID ),
356
- );
357
- }
358
-
359
- return $processed_posts;
360
- }
361
-
362
- /**
363
- * Get the query args for grabbing the posts. This should probably get overwritten in child classes.
364
- *
365
- * @return mixed|void
366
- */
367
- private function get_query_args() {
368
-
369
- $args = array(
370
- 'numberposts' => $this->posts_count,
371
- 'ignore_sticky_posts' => true,
372
- );
373
- $args = wp_parse_args( $this->query_args(), $args );
374
-
375
- return apply_filters( 'exactmetrics_popular_posts_query_args', $args );
376
- }
377
-
378
- /**
379
- * Set the query args specific to this instance.
380
- *
381
- * @return array
382
- */
383
- protected function query_args() {
384
-
385
- if ( 'comments' === $this->sort ) {
386
- return $this->get_query_args_comments();
387
- } elseif ( 'sharedcount' === $this->sort ) {
388
- return $this->get_query_args_sharedcount();
389
- } elseif ( 'curated' === $this->sort ) {
390
- return $this->get_query_args_curated();
391
- }
392
-
393
- }
394
-
395
-
396
- /**
397
- * Get the query args for ordering by comments.
398
- *
399
- * @return array
400
- */
401
- protected function get_query_args_comments() {
402
-
403
- $query_args = array(
404
- 'orderby' => 'comment_count',
405
- 'order' => 'DESC',
406
- );
407
-
408
- return $query_args;
409
- }
410
-
411
- /**
412
- * Get the query args for ordering by sharedcount.
413
- *
414
- * @return array
415
- */
416
- protected function get_query_args_sharedcount() {
417
-
418
- $query_args = array(
419
- 'orderby' => 'meta_value_num',
420
- 'order' => 'DESC',
421
- 'meta_key' => '_exactmetrics_sharedcount_total',
422
- );
423
-
424
- return $query_args;
425
- }
426
-
427
-
428
- /**
429
- * Build the query args for the curated option from the settings in the panel.
430
- *
431
- * @return array
432
- */
433
- protected function get_query_args_curated() {
434
-
435
- $posts = $this->curated;
436
- $post_in = array();
437
-
438
- if ( ! empty( $posts ) && is_array( $posts ) ) {
439
- foreach ( $posts as $post ) {
440
- if ( ! empty( $post['id'] ) ) {
441
- $post_in[] = intval( $post['id'] );
442
- }
443
- }
444
- }
445
-
446
- $query_args = array(
447
- 'post__in' => $post_in,
448
- );
449
-
450
- return $query_args;
451
- }
452
-
453
- /**
454
- * Load theme props for the specific instance.
455
- *
456
- * @param string $theme Theme key.
457
- *
458
- * @return ExactMetrics_Popular_Posts_Themes
459
- */
460
- public function get_theme_props( $theme = '' ) {
461
-
462
- if ( empty( $theme ) ) {
463
- $theme = $this->theme;
464
- }
465
- $theme_props = new ExactMetrics_Popular_Posts_Themes( $this->type, $theme );
466
-
467
- return $theme_props;
468
- }
469
-
470
- /**
471
- * Marks a post as already displayed, by id.
472
- *
473
- * @param $id
474
- */
475
- public function set_post_shown( $id ) {
476
- if ( ! in_array( $id, $this->shown_posts, true ) ) {
477
- $this->shown_posts[] = $id;
478
- }
479
- }
480
-
481
- /**
482
- * Returns an array of posts that were already displayed on the current page.
483
- *
484
- * @return array
485
- */
486
- public function get_shown_posts() {
487
-
488
- return $this->shown_posts;
489
-
490
- }
491
-
492
- /**
493
- * Generic helper function to build style attributes for elements based on shortcode/block parameters.
494
- *
495
- * @param string $theme The theme for which we're building the style.
496
- * @param string $object Object we're styling like title, label, background, etc.
497
- * @param array $atts Attributes passed from shortcode/block.
498
- * @param string $key The key of the style we're going to output.
499
- *
500
- * @return string
501
- */
502
- public function get_element_style( $theme, $object, $atts, $key = '' ) {
503
-
504
- if ( 'no_styles' === $this->styling ) {
505
- // If no styles is selected don't output any styles.
506
- return '';
507
- }
508
-
509
- if ( empty( $theme ) ) {
510
- $theme = $this->theme;
511
- }
512
-
513
- // Find theme-specific available options and check if our attributes have those set.
514
- $theme_styles = $this->get_theme_props( $theme )->get_theme();
515
- $style_output = '';
516
- $style_css = '';
517
-
518
- if ( ! empty( $theme_styles['styles'] ) ) {
519
- foreach ( $theme_styles['styles'] as $element => $options ) {
520
- if ( $object !== $element ) {
521
- continue;
522
- }
523
- foreach ( $options as $style_key => $value ) {
524
- $atts_key = $element . '_' . $style_key;
525
-
526
- if ( ! empty( $key ) && $key !== $style_key ) {
527
- // Allow output for just a specific key.
528
- continue;
529
- }
530
-
531
- if ( ! empty( $atts[ $atts_key ] ) ) {
532
- if ( is_bool( $atts[ $atts_key ] ) || 'on' === $atts[ $atts_key ] ) {
533
- continue;
534
- }
535
- if ( 'size' === $style_key ) {
536
- $style_key = 'font-size';
537
- $atts[ $atts_key ] .= 'px';
538
- }
539
- if ( 'background' === $style_key || 'background' === $element && 'color' === $style_key ) {
540
- $style_key = 'background-color';
541
- }
542
- if ( 'border' === $element || 'border' === $style_key ) {
543
- $style_key = 'border-color';
544
- }
545
- $style_css .= $style_key . ':' . $atts[ $atts_key ] . ';';
546
- }
547
- }
548
- }
549
- }
550
-
551
- if ( ! empty( $style_css ) ) {
552
- $style_output = 'style="' . $style_css . '"';
553
- }
554
-
555
- return $style_output;
556
-
557
- }
558
-
559
- /**
560
- * Get the current instance based on the called class.
561
- *
562
- * @return mixed
563
- */
564
- public static function get_instance() {
565
-
566
- if ( ! function_exists( 'get_called_class' ) ) {
567
- return false;
568
- }
569
-
570
- $class = get_called_class();
571
-
572
- if ( ! isset( self::$instances[ $class ] ) ) {
573
- self::$instances[ $class ] = new $class;
574
- }
575
-
576
- return self::$instances[ $class ];
577
-
578
- }
579
-
580
- /**
581
- * Check if the post is excluded from loading the widget.
582
- *
583
- * @param null|WP_Post $post The post to check if it's excluded.
584
- *
585
- * @return bool
586
- */
587
- public function is_post_excluded( $post = null ) {
588
- if ( is_null( $post ) ) {
589
- $post = get_post( get_the_ID() );
590
- }
591
- $excluded = false;
592
-
593
- $posts_to_exclude = $this->exclude_posts;
594
- if ( ! empty( $posts_to_exclude ) ) {
595
- $post_ids = array();
596
- foreach ( $posts_to_exclude as $exclude_post ) {
597
- if ( ! empty( $exclude_post['id'] ) ) {
598
- $post_ids[] = intval( $exclude_post['id'] );
599
- }
600
- }
601
-
602
- if ( in_array( $post->ID, $post_ids, true ) ) {
603
- $excluded = true;
604
- }
605
- }
606
-
607
- return $excluded;
608
- }
609
-
610
- /**
611
- * Build a wrapper class based on theme, instance and some settings.
612
- *
613
- * @param array $atts Attributes of the shortcode/instance to process for output.
614
- *
615
- * @return string
616
- */
617
- public function get_wrapper_class( $atts ) {
618
- $theme = $this->theme;
619
- if ( ! empty( $atts['theme'] ) ) {
620
- $theme = $atts['theme'];
621
- }
622
- $columns = ! empty( $atts['columns'] ) ? $atts['columns'] : $this->theme_columns;
623
- $classes = array(
624
- 'exactmetrics-' . $this->type . '-popular-posts',
625
- 'exactmetrics-' . $this->type . '-popular-posts-' . $theme,
626
- 'no_styles' !== $this->styling ? 'exactmetrics-popular-posts-styled' : '',
627
- );
628
-
629
- if ( $columns ) {
630
- $classes[] = 'exactmetrics-' . $this->type . '-popular-posts-columns-' . $columns;
631
- }
632
-
633
- if ( isset( $atts['className'] ) ) {
634
- $classes[] = $atts['className'];
635
- }
636
-
637
- $classname = implode( ' ', $classes );
638
-
639
- return $classname;
640
- }
641
-
642
- /**
643
- * Check if the id is of the currently displayed post. Compatible with the Ajaxify functionality.
644
- *
645
- * @param $id
646
- *
647
- * @return bool
648
- */
649
- public function is_current_post( $id ) {
650
-
651
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
652
- $current_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : false;
653
-
654
- return $id === $current_id;
655
- }
656
-
657
- // Only run this check for singular pages.
658
- if ( ! is_singular() ) {
659
- return false;
660
- }
661
-
662
- return get_the_ID() === absint( $id );
663
-
664
- }
665
-
666
- /**
667
- * Helper function that checks if a post should be displayed on the current page.
668
- *
669
- * @param int $id
670
- *
671
- * @return bool
672
- */
673
- public function should_display_post( $id ) {
674
- $shown = $this->get_shown_posts();
675
- if ( in_array( $id, $shown, true ) ) {
676
- return false;
677
- }
678
- if ( $this->is_current_post( $id ) ) {
679
- return false;
680
- }
681
-
682
- return true;
683
- }
684
-
685
- /**
686
- * This function grabs the posts from the cache or a fresh query and runs them through a check if they should be
687
- * displayed on the current page to avoid duplicates.
688
- *
689
- * @return array
690
- */
691
- public function get_posts_to_display() {
692
- $posts = $this->get_posts();
693
-
694
- $returned_posts = array();
695
-
696
- foreach ( $posts as $post ) {
697
- if ( $this->should_display_post( $post['id'] ) ) {
698
- $returned_posts[] = $post;
699
- }
700
- }
701
-
702
- if ( apply_filters( 'exactmetrics_popular_posts_show_duplicates', true ) && count( $posts ) > 0 && count( $this->shown_posts ) > 0 && count( $returned_posts ) === 0 ) {
703
- $this->shown_posts = array(); // Reset shown posts.
704
- return $this->get_posts_to_display(); // Run the function to grab the same posts again.
705
- }
706
-
707
- return $returned_posts;
708
- }
709
-
710
- /**
711
- * Check if the current instance has any posts available to display.
712
- *
713
- * @param array $posts Posts array to check if still available for display.
714
- *
715
- * @return bool
716
- */
717
- public function has_posts_to_show( $posts ) {
718
-
719
- foreach ( $posts as $post ) {
720
- if ( $this->should_display_post( $post['id'] ) ) {
721
- return true;
722
- }
723
- }
724
-
725
- return false;
726
-
727
- }
728
-
729
- /**
730
- * Only inline styles that were customized for the specific instance.
731
- *
732
- * @return array
733
- */
734
- public function get_themes_styles_for_output() {
735
-
736
- $stored_styles = $this->get_theme_props()->get_theme_stored_styles();
737
- $themes = ! empty( $stored_styles[ $this->type ] ) && is_array( $stored_styles[ $this->type ] ) ? $stored_styles[ $this->type ] : array();
738
-
739
- return $themes;
740
-
741
- }
742
- }
1
+ <?php
2
+ /**
3
+ * This is the base class for the Popular Posts output functionality.
4
+ * Each actual Popular Posts option extends this class (inline, widget, products).
5
+ *
6
+ * @package ExactMetrics
7
+ */
8
+
9
+ /**
10
+ * Class ExactMetrics_Popular_Posts
11
+ */
12
+ class ExactMetrics_Popular_Posts {
13
+
14
+ /**
15
+ * The key prefix used to store the settings for the magic __get method.
16
+ *
17
+ * @var string
18
+ */
19
+ protected $settings_key;
20
+
21
+ /**
22
+ * Name of the shortcode
23
+ *
24
+ * @var string
25
+ */
26
+ protected $shortcode_key;
27
+
28
+ /**
29
+ * The popular posts object type, by default inline, widget or products.
30
+ *
31
+ * @var string
32
+ */
33
+ protected $type;
34
+
35
+ /**
36
+ * An array of posts used in the query process.
37
+ *
38
+ * @var array
39
+ */
40
+ public $posts = array();
41
+
42
+ /**
43
+ * An array of posts already displayed. Used to avoid duplicate posts on the same page.
44
+ *
45
+ * @var array
46
+ */
47
+ public $shown_posts = array();
48
+
49
+ /**
50
+ * The inline styles string with theme specifics from the Vue settings.
51
+ * Each instance should append to this variable so we print styles for all the instances in the same place.
52
+ *
53
+ * @var string
54
+ */
55
+ public static $inline_styles = '';
56
+
57
+ /**
58
+ * Stores the option to use ajax to display the popular posts widgets on the frontend.
59
+ *
60
+ * @var string
61
+ */
62
+ public $ajaxify;
63
+
64
+ /**
65
+ * Stores the cache instance, specific to the plugin version.
66
+ *
67
+ * @var ExactMetrics_Popular_Posts_Cache
68
+ */
69
+ public $cache;
70
+
71
+ /**
72
+ * Holds the class object.
73
+ *
74
+ * @since 7.13.0
75
+ * @access public
76
+ * @var array
77
+ */
78
+ public static $instances = array();
79
+
80
+ /**
81
+ * @var ExactMetrics_Popular_Posts_Themes
82
+ */
83
+ protected $theme_props;
84
+
85
+ /**
86
+ * Indicator that inline styles have been printed to avoid duplicates.
87
+ *
88
+ * @var bool
89
+ */
90
+ private static $styles_printed = false;
91
+
92
+ /**
93
+ * Number of posts to query from the db. Not all queried posts are used for display in the same widget.
94
+ *
95
+ * @var int
96
+ */
97
+ public $posts_count = 15;
98
+
99
+ /**
100
+ * ExactMetrics_Popular_Posts constructor.
101
+ */
102
+ public function __construct() {
103
+
104
+ $this->hooks();
105
+ $this->register_shortcode();
106
+
107
+ $this->ajaxify = exactmetrics_get_option( 'popular_posts_ajaxify', false );
108
+ }
109
+
110
+ /**
111
+ * Magic get for different types of popular posts.
112
+ *
113
+ * @param $name
114
+ *
115
+ * @return string|array|mixed
116
+ */
117
+ public function __get( $name ) {
118
+ return exactmetrics_get_option( $this->settings_key . '_' . $name );
119
+ }
120
+
121
+ /**
122
+ * Add hooks needed for the output.
123
+ */
124
+ public function hooks() {
125
+ add_action( 'wp_enqueue_scripts', array( $this, 'load_frontend_styles' ) );
126
+
127
+ add_action( 'wp_enqueue_scripts', array( $this, 'maybe_load_ajaxify_script' ) );
128
+
129
+ $this->add_inline_styles();
130
+ }
131
+
132
+ /**
133
+ * Add inline styles for each widget type to a single variable for printing.
134
+ */
135
+ protected function add_inline_styles() {
136
+ if ( 'no_styles' !== $this->styling ) {
137
+ self::$inline_styles .= $this->build_inline_styles();
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Should return object-specific inline styles.
143
+ *
144
+ * @return string
145
+ */
146
+ public function build_inline_styles() {
147
+ return '';
148
+ }
149
+
150
+ /**
151
+ * Register the shortcode for the specific class.
152
+ */
153
+ public function register_shortcode() {
154
+
155
+ if ( ! empty( $this->shortcode_key ) ) {
156
+ add_shortcode( $this->shortcode_key, array( $this, 'render_shortcode' ) );
157
+ }
158
+
159
+ }
160
+
161
+ /**
162
+ * Load the frontend styles if they are enabled.
163
+ */
164
+ public function load_frontend_styles() {
165
+
166
+ // Only load our styles if enabled.
167
+ if ( apply_filters( 'exactmetrics_popular_posts_styles_output', 'no_styles' === $this->styling, $this ) ) {
168
+ return;
169
+ }
170
+ $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
171
+
172
+ // Load Popular Posts styles.
173
+ wp_register_style( 'exactmetrics-popular-posts-style', plugins_url( 'assets/css/frontend' . $suffix . '.css', EXACTMETRICS_PLUGIN_FILE ), array(), exactmetrics_get_asset_version() );
174
+
175
+ $this->add_theme_specific_styles();
176
+
177
+ }
178
+
179
+ /**
180
+ * If the Ajaxify option is enabled, print needed scripts.
181
+ */
182
+ public function maybe_load_ajaxify_script() {
183
+ if ( ! $this->ajaxify ) {
184
+ return;
185
+ }
186
+ $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
187
+
188
+ wp_register_script( 'exactmetrics-popular-posts-js', plugins_url( 'assets/js/popular-posts' . $suffix . '.js', EXACTMETRICS_PLUGIN_FILE ), array(), exactmetrics_get_asset_version(), true );
189
+
190
+ wp_enqueue_script( 'exactmetrics-popular-posts-js' );
191
+
192
+ wp_localize_script( 'exactmetrics-popular-posts-js', 'exactmetrics_pp', array(
193
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
194
+ 'post_id' => get_the_ID(),
195
+ ) );
196
+
197
+ }
198
+
199
+ /**
200
+ * Add inline styles based on customizations from the vue panel.
201
+ */
202
+ public function add_theme_specific_styles() {
203
+
204
+ if ( ! self::$styles_printed ) {
205
+ wp_add_inline_style( 'exactmetrics-popular-posts-style', $this->get_inline_styles() );
206
+ self::$styles_printed = true;
207
+ }
208
+
209
+ }
210
+
211
+ /**
212
+ * We have a single static variable for inline styles shared by all instances so we print just once.
213
+ *
214
+ * @return string
215
+ */
216
+ public function get_inline_styles() {
217
+ return self::$inline_styles;
218
+ }
219
+
220
+ /**
221
+ * Rendering the shortcode.
222
+ *
223
+ * @return string
224
+ */
225
+ public function render_shortcode( $args ) {
226
+
227
+ return apply_filters( 'exactmetrics_popular_posts_shortcode_output', $this->shortcode_output( $args ), $args, $this );
228
+
229
+ }
230
+
231
+ /**
232
+ * Output of shortcode based on settings.
233
+ *
234
+ * @param array $args Arguments from shortcode/block.
235
+ *
236
+ * @return string
237
+ */
238
+ public function shortcode_output( $args ) {
239
+ // Load frontend.css file when shortcode is available
240
+ wp_enqueue_style( 'exactmetrics-popular-posts-style' );
241
+
242
+ if ( $this->ajaxify ) {
243
+ return $this->get_ajax_json_data( $args );
244
+ } else {
245
+ return $this->get_rendered_html( $args );
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Print inline JSON data that with settings that get processed using an AJAX call. Acts similar to printing out
251
+ * a shortcode with its settings but actually loading the output for that after the page was loaded, with AJAX.
252
+ *
253
+ * @param array $args Arguments from shortcode/block.
254
+ *
255
+ * @return string
256
+ */
257
+ public function get_ajax_json_data( $args ) {
258
+
259
+ $args['type'] = $this->type;
260
+
261
+ $data = '<div><script type="application/json" class="exactmetrics-popular-posts-widget-json">';
262
+ $data .= wp_json_encode( $args );
263
+ $data .= '</script></div>';
264
+
265
+ return $data;
266
+ }
267
+
268
+ /**
269
+ * This is replaced with actual HTML output in child classes.
270
+ *
271
+ * @param array $args Arguments used to build specific html.
272
+ *
273
+ * @return string
274
+ */
275
+ public function get_rendered_html( $args ) {
276
+ return '';
277
+ }
278
+
279
+ /**
280
+ * Get the cache instance for the set type.
281
+ *
282
+ * @return ExactMetrics_Popular_Posts_Cache
283
+ */
284
+ public function get_cache() {
285
+ if ( ! isset( $this->cache ) ) {
286
+ $this->cache = new ExactMetrics_Popular_Posts_Cache( $this->type );
287
+ }
288
+
289
+ return $this->cache;
290
+ }
291
+
292
+ /**
293
+ * Use the query args to grab posts from the database.
294
+ */
295
+ public function get_posts() {
296
+
297
+ $posts_args = $this->get_query_args();
298
+
299
+ $posts = $this->get_cache()->get_cached_posts( $posts_args );
300
+
301
+ if ( empty( $posts ) ) {
302
+
303
+ if ( isset( $posts_args['post__in'] ) && empty( $posts_args['post__in'] ) ) {
304
+ $this->posts = array();
305
+
306
+ return $this->posts;
307
+ }
308
+ $posts = get_posts( $posts_args );
309
+
310
+ $posts = $this->process_posts( $posts );
311
+
312
+ $this->get_cache()->save_posts_to_cache( $posts_args, $posts );
313
+ }
314
+
315
+ return apply_filters( 'exactmetrics_popular_posts_posts', $posts );
316
+
317
+ }
318
+
319
+ /**
320
+ * Go through posts from a WP Query and prepare them for output.
321
+ *
322
+ * @param array $posts Array of posts from WP Query or similar, also supports array of ids.
323
+ *
324
+ * @return array
325
+ */
326
+ private function process_posts( $posts ) {
327
+ $processed_posts = array();
328
+ foreach ( $posts as $post ) {
329
+ if ( is_int( $post ) ) {
330
+ $post = get_post( $post );
331
+ }
332
+ $post_thumbnail = get_post_thumbnail_id( $post->ID );
333
+ $post_image = '';
334
+ $post_image_srcset = '';
335
+ if ( ! empty( $post_thumbnail ) ) {
336
+ $post_image = wp_get_attachment_image_src( $post_thumbnail, 'medium' );
337
+ if ( is_array( $post_image ) && ! empty( $post_image[0] ) ) {
338
+ $post_image = $post_image[0];
339
+ }
340
+ $post_image_srcset = wp_get_attachment_image_srcset( $post_thumbnail, 'medium' );
341
+ }
342
+
343
+ $author_data = get_userdata( $post->post_author );
344
+
345
+ $processed_posts[] = array(
346
+ 'id' => $post->ID,
347
+ 'title' => get_the_title( $post->ID ),
348
+ 'link' => get_permalink( $post->ID ),
349
+ 'image' => $post_image,
350
+ 'srcset' => $post_image_srcset,
351
+ 'image_id' => $post_thumbnail,
352
+ 'author' => $post->post_author,
353
+ 'author_name' => $author_data->display_name,
354
+ 'date' => get_the_date( '', $post->ID ),
355
+ 'comments' => get_comments_number( $post->ID ),
356
+ );
357
+ }
358
+
359
+ return $processed_posts;
360
+ }
361
+
362
+ /**
363
+ * Get the query args for grabbing the posts. This should probably get overwritten in child classes.
364
+ *
365
+ * @return mixed|void
366
+ */
367
+ private function get_query_args() {
368
+
369
+ $args = array(
370
+ 'numberposts' => $this->posts_count,
371
+ 'ignore_sticky_posts' => true,
372
+ );
373
+ $args = wp_parse_args( $this->query_args(), $args );
374
+
375
+ return apply_filters( 'exactmetrics_popular_posts_query_args', $args );
376
+ }
377
+
378
+ /**
379
+ * Set the query args specific to this instance.
380
+ *
381
+ * @return array
382
+ */
383
+ protected function query_args() {
384
+
385
+ if ( 'comments' === $this->sort ) {
386
+ return $this->get_query_args_comments();
387
+ } elseif ( 'sharedcount' === $this->sort ) {
388
+ return $this->get_query_args_sharedcount();
389
+ } elseif ( 'curated' === $this->sort ) {
390
+ return $this->get_query_args_curated();
391
+ }
392
+
393
+ }
394
+
395
+
396
+ /**
397
+ * Get the query args for ordering by comments.
398
+ *
399
+ * @return array
400
+ */
401
+ protected function get_query_args_comments() {
402
+
403
+ $query_args = array(
404
+ 'orderby' => 'comment_count',
405
+ 'order' => 'DESC',
406
+ );
407
+
408
+ return $query_args;
409
+ }
410
+
411
+ /**
412
+ * Get the query args for ordering by sharedcount.
413
+ *
414
+ * @return array
415
+ */
416
+ protected function get_query_args_sharedcount() {
417
+
418
+ $query_args = array(
419
+ 'orderby' => 'meta_value_num',
420
+ 'order' => 'DESC',
421
+ 'meta_key' => '_exactmetrics_sharedcount_total',
422
+ );
423
+
424
+ return $query_args;
425
+ }
426
+
427
+
428
+ /**
429
+ * Build the query args for the curated option from the settings in the panel.
430
+ *
431
+ * @return array
432
+ */
433
+ protected function get_query_args_curated() {
434
+
435
+ $posts = $this->curated;
436
+ $post_in = array();
437
+
438
+ if ( ! empty( $posts ) && is_array( $posts ) ) {
439
+ foreach ( $posts as $post ) {
440
+ if ( ! empty( $post['id'] ) ) {
441
+ $post_in[] = intval( $post['id'] );
442
+ }
443
+ }
444
+ }
445
+
446
+ $query_args = array(
447
+ 'post__in' => $post_in,
448
+ );
449
+
450
+ return $query_args;
451
+ }
452
+
453
+ /**
454
+ * Load theme props for the specific instance.
455
+ *
456
+ * @param string $theme Theme key.
457
+ *
458
+ * @return ExactMetrics_Popular_Posts_Themes
459
+ */
460
+ public function get_theme_props( $theme = '' ) {
461
+
462
+ if ( empty( $theme ) ) {
463
+ $theme = $this->theme;
464
+ }
465
+ $theme_props = new ExactMetrics_Popular_Posts_Themes( $this->type, $theme );
466
+
467
+ return $theme_props;
468
+ }
469
+
470
+ /**
471
+ * Marks a post as already displayed, by id.
472
+ *
473
+ * @param $id
474
+ */
475
+ public function set_post_shown( $id ) {
476
+ if ( ! in_array( $id, $this->shown_posts, true ) ) {
477
+ $this->shown_posts[] = $id;
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Returns an array of posts that were already displayed on the current page.
483
+ *
484
+ * @return array
485
+ */
486
+ public function get_shown_posts() {
487
+
488
+ return $this->shown_posts;
489
+
490
+ }
491
+
492
+ /**
493
+ * Generic helper function to build style attributes for elements based on shortcode/block parameters.
494
+ *
495
+ * @param string $theme The theme for which we're building the style.
496
+ * @param string $object Object we're styling like title, label, background, etc.
497
+ * @param array $atts Attributes passed from shortcode/block.
498
+ * @param string $key The key of the style we're going to output.
499
+ *
500
+ * @return string
501
+ */
502
+ public function get_element_style( $theme, $object, $atts, $key = '' ) {
503
+
504
+ if ( 'no_styles' === $this->styling ) {
505
+ // If no styles is selected don't output any styles.
506
+ return '';
507
+ }
508
+
509
+ if ( empty( $theme ) ) {
510
+ $theme = $this->theme;
511
+ }
512
+
513
+ // Find theme-specific available options and check if our attributes have those set.
514
+ $theme_styles = $this->get_theme_props( $theme )->get_theme();
515
+ $style_output = '';
516
+ $style_css = '';
517
+
518
+ if ( ! empty( $theme_styles['styles'] ) ) {
519
+ foreach ( $theme_styles['styles'] as $element => $options ) {
520
+ if ( $object !== $element ) {
521
+ continue;
522
+ }
523
+ foreach ( $options as $style_key => $value ) {
524
+ $atts_key = $element . '_' . $style_key;
525
+
526
+ if ( ! empty( $key ) && $key !== $style_key ) {
527
+ // Allow output for just a specific key.
528
+ continue;
529
+ }
530
+
531
+ if ( ! empty( $atts[ $atts_key ] ) ) {
532
+ if ( is_bool( $atts[ $atts_key ] ) || 'on' === $atts[ $atts_key ] ) {
533
+ continue;
534
+ }
535
+ if ( 'size' === $style_key ) {
536
+ $style_key = 'font-size';
537
+ $atts[ $atts_key ] .= 'px';
538
+ }
539
+ if ( 'background' === $style_key || 'background' === $element && 'color' === $style_key ) {
540
+ $style_key = 'background-color';
541
+ }
542
+ if ( 'border' === $element || 'border' === $style_key ) {
543
+ $style_key = 'border-color';
544
+ }
545
+ $style_css .= $style_key . ':' . $atts[ $atts_key ] . ';';
546
+ }
547
+ }
548
+ }
549
+ }
550
+
551
+ if ( ! empty( $style_css ) ) {
552
+ $style_output = 'style="' . $style_css . '"';
553
+ }
554
+
555
+ return $style_output;
556
+
557
+ }
558
+
559
+ /**
560
+ * Get the current instance based on the called class.
561
+ *
562
+ * @return mixed
563
+ */
564
+ public static function get_instance() {
565
+
566
+ if ( ! function_exists( 'get_called_class' ) ) {
567
+ return false;
568
+ }
569
+
570
+ $class = get_called_class();
571
+
572
+ if ( ! isset( self::$instances[ $class ] ) ) {
573
+ self::$instances[ $class ] = new $class;
574
+ }
575
+
576
+ return self::$instances[ $class ];
577
+
578
+ }
579
+
580
+ /**
581
+ * Check if the post is excluded from loading the widget.
582
+ *
583
+ * @param null|WP_Post $post The post to check if it's excluded.
584
+ *
585
+ * @return bool
586
+ */
587
+ public function is_post_excluded( $post = null ) {
588
+ if ( is_null( $post ) ) {
589
+ $post = get_post( get_the_ID() );
590
+ }
591
+ $excluded = false;
592
+
593
+ $posts_to_exclude = $this->exclude_posts;
594
+ if ( ! empty( $posts_to_exclude ) ) {
595
+ $post_ids = array();
596
+ foreach ( $posts_to_exclude as $exclude_post ) {
597
+ if ( ! empty( $exclude_post['id'] ) ) {
598
+ $post_ids[] = intval( $exclude_post['id'] );
599
+ }
600
+ }
601
+
602
+ if ( in_array( $post->ID, $post_ids, true ) ) {
603
+ $excluded = true;
604
+ }
605
+ }
606
+
607
+ return $excluded;
608
+ }
609
+
610
+ /**
611
+ * Build a wrapper class based on theme, instance and some settings.
612
+ *
613
+ * @param array $atts Attributes of the shortcode/instance to process for output.
614
+ *
615
+ * @return string
616
+ */
617
+ public function get_wrapper_class( $atts ) {
618
+ $theme = $this->theme;
619
+ if ( ! empty( $atts['theme'] ) ) {
620
+ $theme = $atts['theme'];
621
+ }
622
+ $columns = ! empty( $atts['columns'] ) ? $atts['columns'] : $this->theme_columns;
623
+ $classes = array(
624
+ 'exactmetrics-' . $this->type . '-popular-posts',
625
+ 'exactmetrics-' . $this->type . '-popular-posts-' . $theme,
626
+ 'no_styles' !== $this->styling ? 'exactmetrics-popular-posts-styled' : '',
627
+ );
628
+
629
+ if ( $columns ) {
630
+ $classes[] = 'exactmetrics-' . $this->type . '-popular-posts-columns-' . $columns;
631
+ }
632
+
633
+ if ( isset( $atts['className'] ) ) {
634
+ $classes[] = $atts['className'];
635
+ }
636
+
637
+ $classname = implode( ' ', $classes );
638
+
639
+ return $classname;
640
+ }
641
+
642
+ /**
643
+ * Check if the id is of the currently displayed post. Compatible with the Ajaxify functionality.
644
+ *
645
+ * @param $id
646
+ *
647
+ * @return bool
648
+ */
649
+ public function is_current_post( $id ) {
650
+
651
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
652
+ $current_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : false;
653
+
654
+ return $id === $current_id;
655
+ }
656
+
657
+ // Only run this check for singular pages.
658
+ if ( ! is_singular() ) {
659
+ return false;
660
+ }
661
+
662
+ return get_the_ID() === absint( $id );
663
+
664
+ }
665
+
666
+ /**
667
+ * Helper function that checks if a post should be displayed on the current page.
668
+ *
669
+ * @param int $id
670
+ *
671
+ * @return bool
672
+ */
673
+ public function should_display_post( $id ) {
674
+ $shown = $this->get_shown_posts();
675
+ if ( in_array( $id, $shown, true ) ) {
676
+ return false;
677
+ }
678
+ if ( $this->is_current_post( $id ) ) {
679
+ return false;
680
+ }
681
+
682
+ return true;
683
+ }
684
+
685
+ /**
686
+ * This function grabs the posts from the cache or a fresh query and runs them through a check if they should be
687
+ * displayed on the current page to avoid duplicates.
688
+ *
689
+ * @return array
690
+ */
691
+ public function get_posts_to_display() {
692
+ $posts = $this->get_posts();
693
+
694
+ $returned_posts = array();
695
+
696
+ foreach ( $posts as $post ) {
697
+ if ( $this->should_display_post( $post['id'] ) ) {
698
+ $returned_posts[] = $post;
699
+ }
700
+ }
701
+
702
+ if ( apply_filters( 'exactmetrics_popular_posts_show_duplicates', true ) && count( $posts ) > 0 && count( $this->shown_posts ) > 0 && count( $returned_posts ) === 0 ) {
703
+ $this->shown_posts = array(); // Reset shown posts.
704
+ return $this->get_posts_to_display(); // Run the function to grab the same posts again.
705
+ }
706
+
707
+ return $returned_posts;
708
+ }
709
+
710
+ /**
711
+ * Check if the current instance has any posts available to display.
712
+ *
713
+ * @param array $posts Posts array to check if still available for display.
714
+ *
715
+ * @return bool
716
+ */
717
+ public function has_posts_to_show( $posts ) {
718
+
719
+ foreach ( $posts as $post ) {
720
+ if ( $this->should_display_post( $post['id'] ) ) {
721
+ return true;
722
+ }
723
+ }
724
+
725
+ return false;
726
+
727
+ }
728
+
729
+ /**
730
+ * Only inline styles that were customized for the specific instance.
731
+ *
732
+ * @return array
733
+ */
734
+ public function get_themes_styles_for_output() {
735
+
736
+ $stored_styles = $this->get_theme_props()->get_theme_stored_styles();
737
+ $themes = ! empty( $stored_styles[ $this->type ] ) && is_array( $stored_styles[ $this->type ] ) ? $stored_styles[ $this->type ] : array();
738
+
739
+ return $themes;
740
+
741
+ }
742
+ }
lite/includes/gutenberg/blocks/blocks.php CHANGED
@@ -1,162 +1,162 @@
1
- <?php
2
- /**
3
- * Gutenberg Blocks registration class.
4
- *
5
- * @since 7.13.9
6
- *
7
- * @package ExactMetrics
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Gutenberg Blocks registration class.
17
- *
18
- * @since 7.13.0
19
- */
20
- class ExactMetrics_Blocks {
21
-
22
- /**
23
- * Holds the class object.
24
- *
25
- * @since 7.13.0
26
- *
27
- * @var object
28
- */
29
- public static $instance;
30
-
31
- /**
32
- * Path to the file.
33
- *
34
- * @since 7.13.0
35
- *
36
- * @var string
37
- */
38
- public $file = __FILE__;
39
-
40
- /**
41
- * Holds the base class object.
42
- *
43
- * @since 7.13.0
44
- *
45
- * @var object
46
- */
47
- public $base;
48
-
49
- /**
50
- * Primary class constructor.
51
- *
52
- * @since 7.13.0
53
- */
54
- public function __construct() {
55
-
56
- if ( function_exists( 'register_block_type' ) ) {
57
-
58
- // Set our object.
59
- $this->set();
60
- $this->register_blocks();
61
- }
62
-
63
- }
64
-
65
- /**
66
- * Sets our object instance and base class instance.
67
- *
68
- * @since 7.13.0
69
- */
70
- public function set() {
71
- self::$instance = $this;
72
- }
73
-
74
- /**
75
- * Register ExactMetrics Gutenberg blocks on the backend.
76
- *
77
- * @since 7.13.0
78
- */
79
- public function register_blocks() {
80
- register_block_type(
81
- 'exactmetrics/popular-posts-inline',
82
- array(
83
- 'attributes' => array(
84
- 'slug' => array(
85
- 'type' => 'string',
86
- ),
87
- 'followrules' => array(
88
- 'type' => 'boolean',
89
- ),
90
- ),
91
- 'render_callback' => array( $this, 'popular_posts_inline_output' ),
92
- )
93
- );
94
- register_block_type(
95
- 'exactmetrics/popular-posts-widget',
96
- array(
97
- 'attributes' => array(
98
- 'slug' => array(
99
- 'type' => 'string',
100
- ),
101
- 'followrules' => array(
102
- 'type' => 'boolean',
103
- ),
104
- ),
105
- 'render_callback' => array( $this, 'popular_posts_widget_output' ),
106
- )
107
- );
108
- }
109
-
110
- /**
111
- * Get form HTML to display in a ExactMetrics Gutenberg block.
112
- *
113
- * @param array $atts Attributes passed by ExactMetrics Gutenberg block.
114
- *
115
- * @return string
116
- * @since 7.13.0
117
- *
118
- */
119
- public function popular_posts_inline_output( $atts ) {
120
-
121
- $output = ExactMetrics_Popular_Posts_Inline()->shortcode_output( $atts );
122
-
123
- return $output;
124
- }
125
-
126
- /**
127
- * Get form HTML to display in a ExactMetrics Gutenberg block.
128
- *
129
- * @param array $atts Attributes passed by ExactMetrics Gutenberg block.
130
- *
131
- * @return string
132
- * @since 7.13.0
133
- */
134
- public function popular_posts_widget_output( $atts ) {
135
-
136
- $atts = $this->add_default_values( $atts );
137
- $output = ExactMetrics_Popular_Posts_Widget()->shortcode_output( $atts );
138
-
139
- return $output;
140
- }
141
-
142
- /**
143
- * This ensures that what is displayed as default in the Gutenberg block is reflected in the output.
144
- *
145
- * @param array $atts The attributes from Gutenberg.
146
- *
147
- * @return array
148
- */
149
- private function add_default_values( $atts ) {
150
-
151
- $default_values = array(
152
- 'columns' => 1,
153
- 'widget_title' => false,
154
- );
155
-
156
- return wp_parse_args( $atts, $default_values );
157
-
158
- }
159
-
160
- }
161
-
162
- new ExactMetrics_Blocks();
1
+ <?php
2
+ /**
3
+ * Gutenberg Blocks registration class.
4
+ *
5
+ * @since 7.13.9
6
+ *
7
+ * @package ExactMetrics
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Gutenberg Blocks registration class.
17
+ *
18
+ * @since 7.13.0
19
+ */
20
+ class ExactMetrics_Blocks {
21
+
22
+ /**
23
+ * Holds the class object.
24
+ *
25
+ * @since 7.13.0
26
+ *
27
+ * @var object
28
+ */
29
+ public static $instance;
30
+
31
+ /**
32
+ * Path to the file.
33
+ *
34
+ * @since 7.13.0
35
+ *
36
+ * @var string
37
+ */
38
+ public $file = __FILE__;
39
+
40
+ /**
41
+ * Holds the base class object.
42
+ *
43
+ * @since 7.13.0
44
+ *
45
+ * @var object
46
+ */
47
+ public $base;
48
+
49
+ /**
50
+ * Primary class constructor.
51
+ *
52
+ * @since 7.13.0
53
+ */
54
+ public function __construct() {
55
+
56
+ if ( function_exists( 'register_block_type' ) ) {
57
+
58
+ // Set our object.
59
+ $this->set();
60
+ $this->register_blocks();
61
+ }
62
+
63
+ }
64
+
65
+ /**
66
+ * Sets our object instance and base class instance.
67
+ *
68
+ * @since 7.13.0
69
+ */
70
+ public function set() {
71
+ self::$instance = $this;
72
+ }
73
+
74
+ /**
75
+ * Register ExactMetrics Gutenberg blocks on the backend.
76
+ *
77
+ * @since 7.13.0
78
+ */
79
+ public function register_blocks() {
80
+ register_block_type(
81
+ 'exactmetrics/popular-posts-inline',
82
+ array(
83
+ 'attributes' => array(
84
+ 'slug' => array(
85
+ 'type' => 'string',
86
+ ),
87
+ 'followrules' => array(
88
+ 'type' => 'boolean',
89
+ ),
90
+ ),
91
+ 'render_callback' => array( $this, 'popular_posts_inline_output' ),
92
+ )
93
+ );
94
+ register_block_type(
95
+ 'exactmetrics/popular-posts-widget',
96
+ array(
97
+ 'attributes' => array(
98
+ 'slug' => array(
99
+ 'type' => 'string',
100
+ ),
101
+ 'followrules' => array(
102
+ 'type' => 'boolean',
103
+ ),
104
+ ),
105
+ 'render_callback' => array( $this, 'popular_posts_widget_output' ),
106
+ )
107
+ );
108
+ }
109
+
110
+ /**
111
+ * Get form HTML to display in a ExactMetrics Gutenberg block.
112
+ *
113
+ * @param array $atts Attributes passed by ExactMetrics Gutenberg block.
114
+ *
115
+ * @return string
116
+ * @since 7.13.0
117
+ *
118
+ */
119
+ public function popular_posts_inline_output( $atts ) {
120
+
121
+ $output = ExactMetrics_Popular_Posts_Inline()->shortcode_output( $atts );
122
+
123
+ return $output;
124
+ }
125
+
126
+ /**
127
+ * Get form HTML to display in a ExactMetrics Gutenberg block.
128
+ *
129
+ * @param array $atts Attributes passed by ExactMetrics Gutenberg block.
130
+ *
131
+ * @return string
132
+ * @since 7.13.0
133
+ */
134
+ public function popular_posts_widget_output( $atts ) {
135
+
136
+ $atts = $this->add_default_values( $atts );
137
+ $output = ExactMetrics_Popular_Posts_Widget()->shortcode_output( $atts );
138
+
139
+ return $output;
140
+ }
141
+
142
+ /**
143
+ * This ensures that what is displayed as default in the Gutenberg block is reflected in the output.
144
+ *
145
+ * @param array $atts The attributes from Gutenberg.
146
+ *
147
+ * @return array
148
+ */
149
+ private function add_default_values( $atts ) {
150
+
151
+ $default_values = array(
152
+ 'columns' => 1,
153
+ 'widget_title' => false,
154
+ );
155
+
156
+ return wp_parse_args( $atts, $default_values );
157
+
158
+ }
159
+
160
+ }
161
+
162
+ new ExactMetrics_Blocks();
lite/includes/gutenberg/frontend.php CHANGED
@@ -1,3 +1,3 @@
1
- <?php
2
-
3
- require_once EXACTMETRICS_PLUGIN_DIR . 'lite/includes/gutenberg/blocks/blocks.php';
1
+ <?php
2
+
3
+ require_once EXACTMETRICS_PLUGIN_DIR . 'lite/includes/gutenberg/blocks/blocks.php';
lite/includes/popular-posts/class-popular-posts-ajax.php CHANGED
@@ -1,259 +1,259 @@
1
- <?php
2
- /**
3
- * Used to handle ajax requests specific to popular posts.
4
- *
5
- * @package ExactMetrics
6
- */
7
-
8
- /**
9
- * Class ExactMetrics_Popular_Posts_Ajax
10
- */
11
- class ExactMetrics_Popular_Posts_Ajax {
12
-
13
- /**
14
- * ExactMetrics_Popular_Posts_Ajax constructor.
15
- */
16
- public function __construct() {
17
-
18
- add_action( 'rest_api_init', array( $this, 'register_api_endpoints' ) );
19
-
20
- add_action( 'wp_ajax_exactmetrics_popular_posts_empty_cache', array( $this, 'empty_cache' ) );
21
-
22
- add_action( 'wp_ajax_exactmetrics_popular_posts_get_widget_output', array( $this, 'get_ajax_output' ) );
23
- add_action( 'wp_ajax_nopriv_exactmetrics_popular_posts_get_widget_output', array(
24
- $this,
25
- 'get_ajax_output'
26
- ) );
27
-
28
- add_action( 'wp_ajax_exactmetrics_get_popular_posts_themes', array( $this, 'ajax_get_themes' ) );
29
- }
30
-
31
- /**
32
- * Register the wp-json API endpoints for the Gutenberg blocks.
33
- */
34
- public function register_api_endpoints() {
35
-
36
- register_rest_route( 'exactmetrics/v1', '/popular-posts/themes/(?P<type>[a-zA-Z0-9-]+)', array(
37
- 'methods' => 'GET',
38
- 'callback' => array( $this, 'get_gutenberg_themes' ),
39
- 'permission_callback' => function () {
40
- return current_user_can( 'edit_posts' );
41
- },
42
- 'args' => array(
43
- 'type' => array(),
44
- ),
45
- ) );
46
-
47
- register_rest_route( 'exactmetrics/v1', '/terms/(?P<slug>[a-zA-Z0-9-_]+)', array(
48
- 'methods' => 'GET',
49
- 'callback' => array( $this, 'get_taxonomy_terms' ),
50
- 'permission_callback' => function () {
51
- return current_user_can( 'edit_posts' );
52
- },
53
- 'args' => array(
54
- 'slug' => array(),
55
- ),
56
- ) );
57
-
58
- register_rest_route( 'exactmetrics/v1', '/taxonomy/(?P<slug>[a-zA-Z0-9-_]+)', array(
59
- 'methods' => 'GET',
60
- 'callback' => array( $this, 'get_taxonomy' ),
61
- 'permission_callback' => function () {
62
- return current_user_can( 'edit_posts' );
63
- },
64
- 'args' => array(
65
- 'slug' => array(),
66
- ),
67
- ) );
68
- }
69
-
70
- /**
71
- * Get the themes for Gutenberg (use the specific nonce).
72
- */
73
- public function get_gutenberg_themes( $data ) {
74
-
75
- $type = ! empty( $data['type'] ) ? $data['type'] : 'inline';
76
-
77
- return $this->get_themes_by_type( $type );
78
-
79
- }
80
-
81
- /**
82
- * Get the themes for a specific type using ajax.
83
- */
84
- public function ajax_get_themes() {
85
-
86
- check_ajax_referer( 'mi-admin-nonce', 'nonce' );
87
-
88
- $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : 'inline';
89
-
90
- wp_send_json_success( $this->get_themes_by_type( $type, false ) );
91
-
92
- }
93
-
94
- /**
95
- * Helper to get themes by type.
96
- *
97
- * @param string $type The widget type: inline/widget/products.
98
- * @param bool $styled Whether to style the selected theme or not.
99
- *
100
- * @return array
101
- */
102
- public function get_themes_by_type( $type, $styled = true ) {
103
- $theme = '';
104
-
105
- if ( $styled ) {
106
- if ( 'inline' === $type ) {
107
- $theme = ExactMetrics_Popular_Posts_Inline()->theme;
108
- }
109
-
110
- if ( 'widget' === $type ) {
111
- $theme = ExactMetrics_Popular_Posts_Widget()->theme;
112
- }
113
- }
114
- $themes = $this->get_themes( $type, $theme );
115
- $themes_array = $themes->themes;
116
-
117
- if ( isset( $themes_array[ $theme ] ) && $styled ) {
118
- $themes_array[ $theme ] = $themes->get_theme();
119
- }
120
-
121
- $response = array(
122
- 'themes' => $themes_array,
123
- 'selected' => $theme,
124
- );
125
-
126
- return $response;
127
- }
128
-
129
- /**
130
- * Get themes by type.
131
- *
132
- * @param string $type The widget type: inline/widget/products.
133
- * @param string $theme The selected theme.
134
- *
135
- * @return ExactMetrics_Popular_Posts_Themes
136
- */
137
- public function get_themes( $type, $theme ) {
138
-
139
- $themes_object = new ExactMetrics_Popular_Posts_Themes( $type, $theme );
140
-
141
- return $themes_object;
142
- }
143
-
144
- /**
145
- * Get a specific theme details.
146
- *
147
- * @param string $type The widget type: inline/widget/products.
148
- * @param string $theme The selected theme.
149
- *
150
- * @return array|mixed
151
- */
152
- public function get_theme_details( $type, $theme ) {
153
-
154
- $themes = new ExactMetrics_Popular_Posts_Themes( $type, $theme );
155
-
156
- return $themes->get_theme();
157
-
158
- }
159
-
160
- /**
161
- * Handler for loading taxonomy terms using a custom endpoint.
162
- *
163
- * @param array $data Data passed from the request.
164
- *
165
- * @return array
166
- */
167
- public function get_taxonomy_terms( $data ) {
168
-
169
- $slug = ! empty( $data['slug'] ) ? $data['slug'] : 'category';
170
-
171
- $terms = get_terms( array(
172
- 'taxonomy' => $slug,
173
- ) );
174
-
175
- $return = array();
176
-
177
- if ( ! is_wp_error( $terms ) ) {
178
- foreach ( $terms as $term ) {
179
- $return[] = array(
180
- 'id' => $term->term_id,
181
- 'name' => $term->name,
182
- 'parent' => $term->parent,
183
- );
184
- }
185
- }
186
-
187
- return $return;
188
-
189
- }
190
-
191
- /**
192
- * Get details for a taxonomy so we can use it in the Gutenberg block.
193
- *
194
- * @param array $data Data passed from the request.
195
- *
196
- * @return false|WP_Taxonomy
197
- */
198
- public function get_taxonomy( $data ) {
199
-
200
- $slug = ! empty( $data['slug'] ) ? $data['slug'] : 'category';
201
-
202
- return get_taxonomy( $slug );
203
-
204
- }
205
-
206
- /**
207
- * Ajax handler to empty the Popular Posts cache for all instances.
208
- */
209
- public function empty_cache() {
210
-
211
- check_ajax_referer( 'mi-admin-nonce', 'nonce' );
212
-
213
- if ( ! current_user_can( 'exactmetrics_save_settings' ) ) {
214
- return;
215
- }
216
-
217
- $types = array(
218
- 'inline',
219
- 'widget',
220
- 'products',
221
- );
222
-
223
- foreach ( $types as $type ) {
224
- delete_option( 'exactmetrics_popular_posts_cache_' . $type );
225
- }
226
-
227
- wp_send_json_success();
228
-
229
- }
230
-
231
- /**
232
- * Ajax handler to get the output for Popular Posts widgets from the JSON data on the frontend.
233
- */
234
- public function get_ajax_output() {
235
-
236
- if ( empty( $_POST['data'] ) || ! is_array( $_POST['data'] ) ) {
237
- return;
238
- }
239
-
240
- $html = array();
241
- $widgets_args = $_POST['data'];
242
-
243
- foreach ( $widgets_args as $args ) {
244
- $args = json_decode( sanitize_text_field( wp_unslash( $args ) ), true );
245
- if ( ! empty( $args['type'] ) ) {
246
- $type = ucfirst( $args['type'] );
247
- $widget_function = function_exists( 'ExactMetrics_Popular_Posts_' . $type ) ? call_user_func( 'ExactMetrics_Popular_Posts_' . $type ) : false;
248
- if ( $widget_function ) {
249
- $html[] = $widget_function->get_rendered_html( $args );
250
- }
251
- }
252
- }
253
-
254
- wp_send_json( $html );
255
- }
256
-
257
- }
258
-
259
- new ExactMetrics_Popular_Posts_Ajax();
1
+ <?php
2
+ /**
3
+ * Used to handle ajax requests specific to popular posts.
4
+ *
5
+ * @package ExactMetrics
6
+ */
7
+
8
+ /**
9
+ * Class ExactMetrics_Popular_Posts_Ajax
10
+ */
11
+ class ExactMetrics_Popular_Posts_Ajax {
12
+
13
+ /**
14
+ * ExactMetrics_Popular_Posts_Ajax constructor.
15
+ */
16
+ public function __construct() {
17
+
18
+ add_action( 'rest_api_init', array( $this, 'register_api_endpoints' ) );
19
+
20
+ add_action( 'wp_ajax_exactmetrics_popular_posts_empty_cache', array( $this, 'empty_cache' ) );
21
+
22
+ add_action( 'wp_ajax_exactmetrics_popular_posts_get_widget_output', array( $this, 'get_ajax_output' ) );
23
+ add_action( 'wp_ajax_nopriv_exactmetrics_popular_posts_get_widget_output', array(
24
+ $this,
25
+ 'get_ajax_output'
26
+ ) );
27
+
28
+ add_action( 'wp_ajax_exactmetrics_get_popular_posts_themes', array( $this, 'ajax_get_themes' ) );
29
+ }
30
+
31
+ /**
32
+ * Register the wp-json API endpoints for the Gutenberg blocks.
33
+ */
34
+ public function register_api_endpoints() {
35
+
36
+ register_rest_route( 'exactmetrics/v1', '/popular-posts/themes/(?P<type>[a-zA-Z0-9-]+)', array(
37
+ 'methods' => 'GET',
38
+ 'callback' => array( $this, 'get_gutenberg_themes' ),
39
+ 'permission_callback' => function () {
40
+ return current_user_can( 'edit_posts' );
41
+ },
42
+ 'args' => array(
43
+ 'type' => array(),
44
+ ),
45
+ ) );
46
+
47
+ register_rest_route( 'exactmetrics/v1', '/terms/(?P<slug>[a-zA-Z0-9-_]+)', array(
48
+ 'methods' => 'GET',
49
+ 'callback' => array( $this, 'get_taxonomy_terms' ),
50
+ 'permission_callback' => function () {
51
+ return current_user_can( 'edit_posts' );
52
+ },
53
+ 'args' => array(
54
+ 'slug' => array(),
55
+ ),
56
+ ) );
57
+
58
+ register_rest_route( 'exactmetrics/v1', '/taxonomy/(?P<slug>[a-zA-Z0-9-_]+)', array(
59
+ 'methods' => 'GET',
60
+ 'callback' => array( $this, 'get_taxonomy' ),
61
+ 'permission_callback' => function () {
62
+ return current_user_can( 'edit_posts' );
63
+ },
64
+ 'args' => array(
65
+ 'slug' => array(),
66
+ ),
67
+ ) );
68
+ }
69
+
70
+ /**
71
+ * Get the themes for Gutenberg (use the specific nonce).
72
+ */
73
+ public function get_gutenberg_themes( $data ) {
74
+
75
+ $type = ! empty( $data['type'] ) ? $data['type'] : 'inline';
76
+
77
+ return $this->get_themes_by_type( $type );
78
+
79
+ }
80
+
81
+ /**
82
+ * Get the themes for a specific type using ajax.
83
+ */
84
+ public function ajax_get_themes() {
85
+
86
+ check_ajax_referer( 'mi-admin-nonce', 'nonce' );
87
+
88
+ $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : 'inline';
89
+
90
+ wp_send_json_success( $this->get_themes_by_type( $type, false ) );
91
+
92
+ }
93
+
94
+ /**
95
+ * Helper to get themes by type.
96
+ *
97
+ * @param string $type The widget type: inline/widget/products.
98
+ * @param bool $styled Whether to style the selected theme or not.
99
+ *
100
+ * @return array
101
+ */
102
+ public function get_themes_by_type( $type, $styled = true ) {
103
+ $theme = '';
104
+
105
+ if ( $styled ) {
106
+ if ( 'inline' === $type ) {
107
+ $theme = ExactMetrics_Popular_Posts_Inline()->theme;
108
+ }
109
+
110
+ if ( 'widget' === $type ) {
111
+ $theme = ExactMetrics_Popular_Posts_Widget()->theme;
112
+ }
113
+ }
114
+ $themes = $this->get_themes( $type, $theme );
115
+ $themes_array = $themes->themes;
116
+
117
+ if ( isset( $themes_array[ $theme ] ) && $styled ) {
118
+ $themes_array[ $theme ] = $themes->get_theme();
119
+ }
120
+
121
+ $response = array(
122
+ 'themes' => $themes_array,
123
+ 'selected' => $theme,
124
+ );
125
+
126
+ return $response;
127
+ }
128
+
129
+ /**
130
+ * Get themes by type.
131
+ *
132
+ * @param string $type The widget type: inline/widget/products.
133
+ * @param string $theme The selected theme.
134
+ *
135
+ * @return ExactMetrics_Popular_Posts_Themes
136
+ */
137
+ public function get_themes( $type, $theme ) {
138
+
139
+ $themes_object = new ExactMetrics_Popular_Posts_Themes( $type, $theme );
140
+
141
+ return $themes_object;
142
+ }
143
+
144
+ /**
145
+ * Get a specific theme details.
146
+ *
147
+ * @param string $type The widget type: inline/widget/products.
148
+ * @param string $theme The selected theme.
149
+ *
150
+ * @return array|mixed
151
+ */
152
+ public function get_theme_details( $type, $theme ) {
153
+
154
+ $themes = new ExactMetrics_Popular_Posts_Themes( $type, $theme );
155
+
156
+ return $themes->get_theme();
157
+
158
+ }
159
+
160
+ /**
161
+ * Handler for loading taxonomy terms using a custom endpoint.
162
+ *
163
+ * @param array $data Data passed from the request.
164
+ *
165
+ * @return array
166
+ */
167
+ public function get_taxonomy_terms( $data ) {
168
+
169
+ $slug = ! empty( $data['slug'] ) ? $data['slug'] : 'category';
170
+
171
+ $terms = get_terms( array(
172
+ 'taxonomy' => $slug,
173
+ ) );
174
+
175
+ $return = array();
176
+
177
+ if ( ! is_wp_error( $terms ) ) {
178
+ foreach ( $terms as $term ) {
179
+ $return[] = array(
180
+ 'id' => $term->term_id,
181
+ 'name' => $term->name,
182
+ 'parent' => $term->parent,
183
+ );
184
+ }
185
+ }
186
+
187
+ return $return;
188
+
189
+ }
190
+
191
+ /**
192
+ * Get details for a taxonomy so we can use it in the Gutenberg block.
193
+ *
194
+ * @param array $data Data passed from the request.
195
+ *
196
+ * @return false|WP_Taxonomy
197
+ */
198
+ public function get_taxonomy( $data ) {
199
+
200
+ $slug = ! empty( $data['slug'] ) ? $data['slug'] : 'category';
201
+
202
+ return get_taxonomy( $slug );
203
+
204
+ }
205
+
206
+ /**
207
+ * Ajax handler to empty the Popular Posts cache for all instances.
208
+ */
209
+ public function empty_cache() {
210
+
211
+ check_ajax_referer( 'mi-admin-nonce', 'nonce' );
212
+
213
+ if ( ! current_user_can( 'exactmetrics_save_settings' ) ) {
214
+ return;
215
+ }
216
+
217
+ $types = array(
218
+ 'inline',
219
+ 'widget',
220
+ 'products',
221
+ );
222
+
223
+ foreach ( $types as $type ) {
224
+ delete_option( 'exactmetrics_popular_posts_cache_' . $type );
225
+ }
226
+
227
+ wp_send_json_success();
228
+
229
+ }
230
+
231
+ /**
232
+ * Ajax handler to get the output for Popular Posts widgets from the JSON data on the frontend.
233
+ */
234
+ public function get_ajax_output() {
235
+
236
+ if ( empty( $_POST['data'] ) || ! is_array( $_POST['data'] ) ) {
237
+ return;
238
+ }
239
+
240
+ $html = array();
241
+ $widgets_args = $_POST['data'];
242
+
243
+ foreach ( $widgets_args as $args ) {
244
+ $args = json_decode( sanitize_text_field( wp_unslash( $args ) ), true );
245
+ if ( ! empty( $args['type'] ) ) {
246
+ $type = ucfirst( $args['type'] );
247
+ $widget_function = function_exists( 'ExactMetrics_Popular_Posts_' . $type ) ? call_user_func( 'ExactMetrics_Popular_Posts_' . $type ) : false;
248
+ if ( $widget_function ) {
249
+ $html[] = $widget_function->get_rendered_html( $args );
250
+ }
251
+ }
252
+ }
253
+
254
+ wp_send_json( $html );
255
+ }
256
+
257
+ }
258
+
259
+ new ExactMetrics_Popular_Posts_Ajax();
lite/includes/popular-posts/class-popular-posts-cache.php CHANGED
@@ -1,131 +1,131 @@
1
- <?php
2
- /**
3
- * This class is used for handling Popular Posts caching.
4
- *
5
- * @package ExactMetrics
6
- */
7
-
8
- /**
9
- * Class ExactMetrics_Popular_Posts_Cache
10
- */
11
- class ExactMetrics_Popular_Posts_Cache {
12
-
13
- /**
14
- * Instance type (inline/widget/products).
15
- *
16
- * @var string
17
- */
18
- public $type;
19
-
20
- /**
21
- * ExactMetrics_Popular_Posts_Cache constructor.
22
- *
23
- * @param string $type The instance type (inline/widget/products).
24
- */
25
- public function __construct( $type ) {
26
-
27
- $this->type = $type;
28
- }
29
-
30
- /**
31
- * Build an unique key from the arguments so we can cache different instances.
32
- * This way, the Gutenberg block or the sidebar widget get cached with their own query settings
33
- * if they are different from the ones set in Vue.
34
- *
35
- * @param $args
36
- *
37
- * @return string
38
- */
39
- public function get_args_key( $args ) {
40
- return md5( wp_json_encode( $args ) );
41
- }
42
-
43
- /**
44
- * Get the specific options key for the set type.
45
- *
46
- * @return string
47
- */
48
- public function get_cache_key() {
49
- return 'exactmetrics_popular_posts_cache_' . $this->type;
50
- }
51
-
52
- /**
53
- * Get cached posts data and check expiration. Each query result is stored with the timestamp and that
54
- * is used to compare to the current settings for expiration.
55
- *
56
- * @param array $args This is an array with query parameters for WP_Query used to identify if this query has been cached.
57
- *
58
- * @return array
59
- */
60
- public function get_cached_posts( $args ) {
61
-
62
- $cache_refresh_days = $this->get_cache_interval();
63
- $cached_data = get_option( $this->get_cache_key(), array() );
64
- $args_key = $this->get_args_key( $args ); // Generate an unique key based on the instance settings.
65
-
66
- if ( isset( $cached_data[ $args_key ] ) && isset( $cached_data[ $args_key ]['saved_at'] ) ) {
67
- $time_since = time() - $cached_data[ $args_key ]['saved_at'];
68
-
69
- if ( $time_since < intval( $cache_refresh_days ) * DAY_IN_SECONDS ) {
70
- return $cached_data[ $args_key ]['posts'];
71
- } else {
72
- // It's expired so let's delete it.
73
- unset( $cached_data[ $args_key ] );
74
- update_option( $this->get_cache_key(), $cached_data );
75
- }
76
- }
77
-
78
- return array();
79
-
80
- }
81
-
82
-
83
- /**
84
- * Get the option set in the settings for cache expiration.
85
- *
86
- * @return int
87
- */
88
- private function get_cache_interval() {
89
- $cache_refresh_days = exactmetrics_get_option( 'popular_posts_caching_refresh', 7 );
90
-
91
- // If they downgraded and previously used a custom interval use the default 7 until the update the option.
92
- if ( 'custom' === $cache_refresh_days ) {
93
- $cache_refresh_days = 7;
94
- }
95
-
96
- return intval( $cache_refresh_days );
97
- }
98
-
99
- /**
100
- * Store a query result in the cache along with the arguments used to grab them.
101
- *
102
- * @param array $args Arguments used in WP_Query used to build an unique key for the loaded data.
103
- * @param array $posts An array of posts that resulted from the query to be saved in the cache.
104
- */
105
- public function save_posts_to_cache( $args, $posts ) {
106
-
107
- if ( empty( $posts ) ) {
108
- // Don't save empty posts.
109
- return;
110
- }
111
-
112
- $args_key = md5( wp_json_encode( $args ) ); // Generate an unique key based on the instance settings.
113
- $cached_data = get_option( $this->get_cache_key(), array() );
114
-
115
- $cached_data[ $args_key ] = array(
116
- 'saved_at' => time(),
117
- 'posts' => $posts,
118
- );
119
-
120
- update_option( $this->get_cache_key(), $cached_data );
121
-
122
- }
123
-
124
- /**
125
- * Delete database option for cache.
126
- */
127
- public function delete_data() {
128
- delete_option( $this->get_cache_key() );
129
- }
130
-
131
- }
1
+ <?php
2
+ /**
3
+ * This class is used for handling Popular Posts caching.
4
+ *
5
+ * @package ExactMetrics
6
+ */
7
+
8
+ /**
9
+ * Class ExactMetrics_Popular_Posts_Cache
10
+ */
11
+ class ExactMetrics_Popular_Posts_Cache {
12
+
13
+ /**
14
+ * Instance type (inline/widget/products).
15
+ *
16
+ * @var string
17
+ */
18
+ public $type;
19
+
20
+ /**
21
+ * ExactMetrics_Popular_Posts_Cache constructor.
22
+ *
23
+ * @param string $type The instance type (inline/widget/products).
24
+ */
25
+ public function __construct( $type ) {
26
+
27
+ $this->type = $type;
28
+ }
29
+
30
+ /**
31
+ * Build an unique key from the arguments so we can cache different instances.
32
+ * This way, the Gutenberg block or the sidebar widget get cached with their own query settings
33
+ * if they are different from the ones set in Vue.
34
+ *
35
+ * @param $args
36
+ *
37
+ * @return string
38
+ */
39
+ public function get_args_key( $args ) {
40
+ return md5( wp_json_encode( $args ) );
41
+ }
42
+
43
+ /**
44
+ * Get the specific options key for the set type.
45
+ *
46
+ * @return string
47
+ */
48
+ public function get_cache_key() {
49
+ return 'exactmetrics_popular_posts_cache_' . $this->type;
50
+ }
51
+
52
+ /**
53
+ * Get cached posts data and check expiration. Each query result is stored with the timestamp and that
54
+ * is used to compare to the current settings for expiration.
55
+ *
56
+ * @param array $args This is an array with query parameters for WP_Query used to identify if this query has been cached.
57
+ *
58
+ * @return array
59
+ */
60
+ public function get_cached_posts( $args ) {
61
+
62
+ $cache_refresh_days = $this->get_cache_interval();
63
+ $cached_data = get_option( $this->get_cache_key(), array() );
64
+ $args_key = $this->get_args_key( $args ); // Generate an unique key based on the instance settings.
65
+
66
+ if ( isset( $cached_data[ $args_key ] ) && isset( $cached_data[ $args_key ]['saved_at'] ) ) {
67
+ $time_since = time() - $cached_data[ $args_key ]['saved_at'];
68
+
69
+ if ( $time_since < intval( $cache_refresh_days ) * DAY_IN_SECONDS ) {
70
+ return $cached_data[ $args_key ]['posts'];
71
+ } else {
72
+ // It's expired so let's delete it.
73
+ unset( $cached_data[ $args_key ] );
74
+ update_option( $this->get_cache_key(), $cached_data );
75
+ }
76
+ }
77
+
78
+ return array();
79
+
80
+ }
81
+
82
+
83
+ /**
84
+ * Get the option set in the settings for cache expiration.
85
+ *
86
+ * @return int
87
+ */
88
+ private function get_cache_interval() {
89
+ $cache_refresh_days = exactmetrics_get_option( 'popular_posts_caching_refresh', 7 );
90
+
91
+ // If they downgraded and previously used a custom interval use the default 7 until the update the option.
92
+ if ( 'custom' === $cache_refresh_days ) {
93
+ $cache_refresh_days = 7;
94
+ }
95
+
96
+ return intval( $cache_refresh_days );
97
+ }
98
+
99
+ /**
100
+ * Store a query result in the cache along with the arguments used to grab them.
101
+ *
102
+ * @param array $args Arguments used in WP_Query used to build an unique key for the loaded data.
103
+ * @param array $posts An array of posts that resulted from the query to be saved in the cache.
104
+ */
105
+ public function save_posts_to_cache( $args, $posts ) {
106
+
107
+ if ( empty( $posts ) ) {
108
+ // Don't save empty posts.
109
+ return;
110
+ }
111
+
112
+ $args_key = md5( wp_json_encode( $args ) ); // Generate an unique key based on the instance settings.
113
+ $cached_data = get_option( $this->get_cache_key(), array() );
114
+
115
+ $cached_data[ $args_key ] = array(
116
+ 'saved_at' => time(),
117
+ 'posts' => $posts,
118
+ );
119
+
120
+ update_option( $this->get_cache_key(), $cached_data );
121
+
122
+ }
123
+
124
+ /**
125
+ * Delete database option for cache.
126
+ */
127
+ public function delete_data() {
128
+ delete_option( $this->get_cache_key() );
129
+ }
130
+
131
+ }
lite/includes/popular-posts/class-popular-posts-inline.php CHANGED
@@ -1,270 +1,270 @@
1
- <?php
2
- /**
3
- * Code specific to the inline Popular Posts widget type.
4
- */
5
-
6
- /**
7
- * Class ExactMetrics_Popular_Posts_Inline
8
- */
9
- class ExactMetrics_Popular_Posts_Inline extends ExactMetrics_Popular_Posts {
10
-
11
- /**
12
- * Used to load the setting specific for this class.
13
- *
14
- * @var string
15
- */
16
- protected $settings_key = 'popular_posts_inline';
17
-
18
- /**
19
- * Used for registering the shortcode specific to this class.
20
- *
21
- * @var string
22
- */
23
- protected $shortcode_key = 'exactmetrics_popular_posts_inline';
24
-
25
- /**
26
- * The instance type. Used for loading specific settings.
27
- *
28
- * @var string
29
- */
30
- protected $type = 'inline';
31
-
32
- /**
33
- * Inline-specific hooks.
34
- */
35
- public function hooks() {
36
- parent::hooks();
37
-
38
- add_action( 'wp', array( $this, 'maybe_auto_insert' ) );
39
- }
40
-
41
- /**
42
- * Get the rendered HTML for output.
43
- *
44
- * @param array $atts These are attributes used to build the specific instance, they can be either shortcode
45
- * attributes or Gutenberg block props.
46
- *
47
- * @return string
48
- */
49
- public function get_rendered_html( $atts ) {
50
-
51
- $theme = $this->theme;
52
- if ( ! empty( $atts['theme'] ) ) {
53
- $theme = $atts['theme'];
54
- }
55
-
56
- $theme = $this->is_theme_available( $theme );
57
-
58
- $posts = $this->get_posts_to_display();
59
-
60
- if ( empty( $posts ) ) {
61
- return '';
62
- }
63
-
64
- if ( 'curated' === $this->sort && apply_filters( 'exactmetrics_popular_posts_inline_curated_shuffle', true ) ) {
65
- // Randomize the order.
66
- shuffle( $posts );
67
- }
68
-
69
- $theme_styles = $this->get_theme_props( $theme )->get_theme();
70
- $limit = ! empty( $theme_styles['posts'] ) ? $theme_styles['posts'] : 1;
71
-
72
- $label_text = '';
73
- if ( isset( $theme_styles['styles']['label'] ) ) {
74
- $label_text = isset( $atts['labelText'] ) ? $atts['labelText'] : $theme_styles['styles']['label']['text'];
75
- }
76
-
77
- // Wrap in a P tag to keep the same spacing.
78
- $html = '<div class="' . $this->get_wrapper_class( $atts ) . '" ' . $this->get_element_style( $theme, 'background', $atts ) . '>';
79
-
80
- if ( ! empty( $theme_styles['image'] ) && ! empty( $posts[0]['image'] ) ) {
81
- $html .= '<div class="exactmetrics-inline-popular-posts-image">';
82
- $html .= '<img src="' . $posts[0]['image'] . '" srcset=" ' . $posts[0]['srcset'] . ' " alt="' . esc_attr( $posts[0]['title'] ) . '" />';
83
- $html .= '</div>';
84
- }
85
- $html .= '<div class="exactmetrics-inline-popular-posts-text">';
86
- if ( ! empty( $theme_styles['styles']['icon'] ) ) {
87
- $html .= '<span class="exactmetrics-inline-popular-posts-icon" style=""><svg width="14" height="19" viewBox="0 0 14 19" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.875 0.899463C7.875 1.59183 8.0816 2.24711 8.49479 2.8653C8.93229 3.48349 9.44271 4.06458 10.026 4.60859C10.6337 5.15259 11.2292 5.73369 11.8125 6.35188C12.4201 6.97007 12.9306 7.76135 13.3438 8.72572C13.7812 9.66537 14 10.7163 14 11.8785C14 13.832 13.3073 15.5011 11.9219 16.8858C10.5608 18.2953 8.92014 19 7 19C5.07986 19 3.42708 18.2953 2.04167 16.8858C0.680556 15.5011 0 13.832 0 11.8785C0 9.94973 0.668403 8.28062 2.00521 6.87116C2.27257 6.57443 2.58854 6.50024 2.95312 6.64861C3.31771 6.79697 3.5 7.08134 3.5 7.50171V10.6545C3.5 11.3221 3.71875 11.8908 4.15625 12.3607C4.61806 12.8305 5.16493 13.0654 5.79688 13.0654C6.45312 13.0654 7.01215 12.8428 7.47396 12.3978C7.93576 11.9279 8.16667 11.3592 8.16667 10.6916C8.16667 10.2712 8.04514 9.86318 7.80208 9.46754C7.58333 9.0719 7.31597 8.71336 7 8.3919C6.68403 8.07044 6.34375 7.73662 5.97917 7.39043C5.63889 7.04425 5.34722 6.66097 5.10417 6.2406C4.88542 5.82024 4.73958 5.35041 4.66667 4.83114C4.59375 4.31186 4.67882 3.68131 4.92188 2.93948C5.18924 2.17293 5.63889 1.33219 6.27083 0.417277C6.51389 0.0463641 6.84201 -0.0772735 7.25521 0.0463641C7.6684 0.170002 7.875 0.454368 7.875 0.899463Z" fill="#EB5757"></path></svg></span>';
88
- }
89
- if ( ! empty( $theme_styles['styles']['label'] ) ) {
90
- $html .= '<span class="exactmetrics-inline-popular-posts-label" ' . $this->get_element_style( $theme, 'label', $atts ) . '>' . $label_text . '</span>';
91
- }
92
- if ( ! empty( $theme_styles['styles']['border'] ) ) {
93
- $html .= '<span class="exactmetrics-inline-popular-posts-border" ' . $this->get_element_style( $theme, 'border', $atts, 'color' ) . '></span>';
94
- }
95
- if ( ! empty( $theme_styles['styles']['border']['color2'] ) ) {
96
- $html .= '<span class="exactmetrics-inline-popular-posts-border-2" ' . $this->get_element_style( $theme, 'border', $atts, 'color2' ) . '></span>';
97
- }
98
-
99
- $display_count = 0;
100
- foreach ( $posts as $post ) {
101
- $display_count ++;
102
- if ( $display_count > $limit ) {
103
- break;
104
- }
105
- $this->set_post_shown( $post['id'] );
106
- $html .= '<div class="exactmetrics-inline-popular-posts-post">';
107
- $html .= '<a class="exactmetrics-inline-popular-posts-title" ' . $this->get_element_style( $theme, 'title', $atts ) . ' href="' . $post['link'] . '">' . $post['title'] . '</a>';
108
- $html .= '</div>';
109
- }
110
-
111
- $html .= '</div>';// Text div.
112
- $html .= '</div><p></p>';// Main div.
113
-
114
- return $html;
115
-
116
- }
117
-
118
- /**
119
- * Specific inline styles based on theme settings.
120
- *
121
- * @return string
122
- */
123
- public function build_inline_styles() {
124
-
125
- $themes = $this->get_themes_styles_for_output();
126
- $styles = '';
127
-
128
- foreach ( $themes as $theme_key => $theme_styles ) {
129
- if ( ! empty( $theme_styles['background'] ) ) {
130
- $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' {';
131
-
132
- if ( ! empty( $theme_styles['background']['color'] ) ) {
133
- $styles .= 'background-color:' . $theme_styles['background']['color'] . ';';
134
- }
135
- if ( ! empty( $theme_styles['background']['border'] ) ) {
136
- $styles .= 'border-color:' . $theme_styles['background']['border'] . ';';
137
- }
138
-
139
- $styles .= '}';
140
- }
141
-
142
- if ( ! empty( $theme_styles['label'] ) ) {
143
- $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-label {';
144
-
145
- if ( ! empty( $theme_styles['label']['color'] ) ) {
146
- $styles .= 'color:' . $theme_styles['label']['color'] . ';';
147
- }
148
-
149
- if ( ! empty( $theme_styles['label']['background'] ) ) {
150
- $styles .= 'background-color:' . $theme_styles['label']['background'] . ';';
151
- }
152
-
153
- $styles .= '}';
154
- }
155
-
156
- if ( ! empty( $theme_styles['title'] ) ) {
157
- $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-title {';
158
-
159
- if ( ! empty( $theme_styles['title']['color'] ) ) {
160
- $styles .= 'color:' . $theme_styles['title']['color'] . ';';
161
- }
162
- if ( ! empty( $theme_styles['title']['size'] ) ) {
163
- $styles .= 'font-size:' . $theme_styles['title']['size'] . 'px;';
164
- }
165
-
166
- $styles .= '}';
167
- }
168
-
169
- if ( ! empty( $theme_styles['border'] ) ) {
170
- $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-border {';
171
-
172
- if ( ! empty( $theme_styles['border']['color'] ) ) {
173
- $styles .= 'border-color:' . $theme_styles['border']['color'] . ';';
174
- }
175
-
176
- $styles .= '}';
177
- }
178
- }
179
-
180
- return $styles;
181
- }
182
-
183
- /**
184
- * Check if we should attempt to automatically insert the inline widget.
185
- */
186
- public function maybe_auto_insert() {
187
-
188
- $post_types = $this->post_types;
189
- if ( ! empty( $post_types ) && is_singular( $post_types ) && 'automatic' === $this->placement ) {
190
- add_filter( 'the_content', array( $this, 'add_inline_posts_to_content' ) );
191
- }
192
-
193
- }
194
-
195
- /**
196
- * Insert the widget in the content.
197
- *
198
- * @param string $content The post content.
199
- *
200
- * @return string
201
- */
202
- public function add_inline_posts_to_content(
203
- $content
204
- ) {
205
-
206
- if ( $this->is_post_excluded() ) {
207
- return $content;
208
- }
209
-
210
- $words_count = str_word_count( $content );
211
- $after_count = intval( $this->after_count );
212
-
213
- // Insert only if there are more words then the insert after value.
214
- if ( $words_count > $after_count ) {
215
-
216
- $words = explode( ' ', $content );
217
- $count = 0;
218
-
219
- foreach ( $words as $index => $word ) {
220
- $count ++;
221
- if ( $count > $after_count ) {
222
- $p_index = strpos( $word, '</p>' );
223
- // Make sure the paragraph tag is not wrapped in another element like a blockquote.
224
- if ( false !== $p_index && false === strpos( $word, '</p></' ) ) {
225
- $words[ $index ] = substr_replace( $word, $this->shortcode_output( array() ), $p_index + 4, 0 );
226
- $this->posts = array();
227
- break;
228
- }
229
- }
230
- }
231
-
232
- $content = implode( ' ', $words );
233
-
234
- }
235
-
236
- return $content;
237
- }
238
-
239
- /**
240
- * Check if the selected theme is available with the current license to avoid showing a theme not available.
241
- * Returns the default 'alpha' theme if not available.
242
- *
243
- * @param string $theme Theme slug for which we are checking.
244
- *
245
- * @return string
246
- */
247
- public function is_theme_available( $theme ) {
248
-
249
- $theme_props = $this->get_theme_props( $theme )->get_theme();
250
-
251
- if ( ! empty( $theme_props['level'] ) && 'lite' === $theme_props['level'] ) {
252
- return $theme;
253
- }
254
-
255
- return 'alpha';
256
-
257
- }
258
-
259
- }
260
-
261
- /**
262
- * Get the current class in a function.
263
- *
264
- * @return ExactMetrics_Popular_Posts_Inline Instance of the current class.
265
- */
266
- function ExactMetrics_Popular_Posts_Inline() {
267
- return ExactMetrics_Popular_Posts_Inline::get_instance();
268
- }
269
-
270
- ExactMetrics_Popular_Posts_Inline();
1
+ <?php
2
+ /**
3
+ * Code specific to the inline Popular Posts widget type.
4
+ */
5
+
6
+ /**
7
+ * Class ExactMetrics_Popular_Posts_Inline
8
+ */
9
+ class ExactMetrics_Popular_Posts_Inline extends ExactMetrics_Popular_Posts {
10
+
11
+ /**
12
+ * Used to load the setting specific for this class.
13
+ *
14
+ * @var string
15
+ */
16
+ protected $settings_key = 'popular_posts_inline';
17
+
18
+ /**
19
+ * Used for registering the shortcode specific to this class.
20
+ *
21
+ * @var string
22
+ */
23
+ protected $shortcode_key = 'exactmetrics_popular_posts_inline';
24
+
25
+ /**
26
+ * The instance type. Used for loading specific settings.
27
+ *
28
+ * @var string
29
+ */
30
+ protected $type = 'inline';
31
+
32
+ /**
33
+ * Inline-specific hooks.
34
+ */
35
+ public function hooks() {
36
+ parent::hooks();
37
+
38
+ add_action( 'wp', array( $this, 'maybe_auto_insert' ) );
39
+ }
40
+
41
+ /**
42
+ * Get the rendered HTML for output.
43
+ *
44
+ * @param array $atts These are attributes used to build the specific instance, they can be either shortcode
45
+ * attributes or Gutenberg block props.
46
+ *
47
+ * @return string
48
+ */
49
+ public function get_rendered_html( $atts ) {
50
+
51
+ $theme = $this->theme;
52
+ if ( ! empty( $atts['theme'] ) ) {
53
+ $theme = $atts['theme'];
54
+ }
55
+
56
+ $theme = $this->is_theme_available( $theme );
57
+
58
+ $posts = $this->get_posts_to_display();
59
+
60
+ if ( empty( $posts ) ) {
61
+ return '';
62
+ }
63
+
64
+ if ( 'curated' === $this->sort && apply_filters( 'exactmetrics_popular_posts_inline_curated_shuffle', true ) ) {
65
+ // Randomize the order.
66
+ shuffle( $posts );
67
+ }
68
+
69
+ $theme_styles = $this->get_theme_props( $theme )->get_theme();
70
+ $limit = ! empty( $theme_styles['posts'] ) ? $theme_styles['posts'] : 1;
71
+
72
+ $label_text = '';
73
+ if ( isset( $theme_styles['styles']['label'] ) ) {
74
+ $label_text = isset( $atts['labelText'] ) ? $atts['labelText'] : $theme_styles['styles']['label']['text'];
75
+ }
76
+
77
+ // Wrap in a P tag to keep the same spacing.
78
+ $html = '<div class="' . $this->get_wrapper_class( $atts ) . '" ' . $this->get_element_style( $theme, 'background', $atts ) . '>';
79
+
80
+ if ( ! empty( $theme_styles['image'] ) && ! empty( $posts[0]['image'] ) ) {
81
+ $html .= '<div class="exactmetrics-inline-popular-posts-image">';
82
+ $html .= '<img src="' . $posts[0]['image'] . '" srcset=" ' . $posts[0]['srcset'] . ' " alt="' . esc_attr( $posts[0]['title'] ) . '" />';
83
+ $html .= '</div>';
84
+ }
85
+ $html .= '<div class="exactmetrics-inline-popular-posts-text">';
86
+ if ( ! empty( $theme_styles['styles']['icon'] ) ) {
87
+ $html .= '<span class="exactmetrics-inline-popular-posts-icon" style=""><svg width="14" height="19" viewBox="0 0 14 19" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.875 0.899463C7.875 1.59183 8.0816 2.24711 8.49479 2.8653C8.93229 3.48349 9.44271 4.06458 10.026 4.60859C10.6337 5.15259 11.2292 5.73369 11.8125 6.35188C12.4201 6.97007 12.9306 7.76135 13.3438 8.72572C13.7812 9.66537 14 10.7163 14 11.8785C14 13.832 13.3073 15.5011 11.9219 16.8858C10.5608 18.2953 8.92014 19 7 19C5.07986 19 3.42708 18.2953 2.04167 16.8858C0.680556 15.5011 0 13.832 0 11.8785C0 9.94973 0.668403 8.28062 2.00521 6.87116C2.27257 6.57443 2.58854 6.50024 2.95312 6.64861C3.31771 6.79697 3.5 7.08134 3.5 7.50171V10.6545C3.5 11.3221 3.71875 11.8908 4.15625 12.3607C4.61806 12.8305 5.16493 13.0654 5.79688 13.0654C6.45312 13.0654 7.01215 12.8428 7.47396 12.3978C7.93576 11.9279 8.16667 11.3592 8.16667 10.6916C8.16667 10.2712 8.04514 9.86318 7.80208 9.46754C7.58333 9.0719 7.31597 8.71336 7 8.3919C6.68403 8.07044 6.34375 7.73662 5.97917 7.39043C5.63889 7.04425 5.34722 6.66097 5.10417 6.2406C4.88542 5.82024 4.73958 5.35041 4.66667 4.83114C4.59375 4.31186 4.67882 3.68131 4.92188 2.93948C5.18924 2.17293 5.63889 1.33219 6.27083 0.417277C6.51389 0.0463641 6.84201 -0.0772735 7.25521 0.0463641C7.6684 0.170002 7.875 0.454368 7.875 0.899463Z" fill="#EB5757"></path></svg></span>';
88
+ }
89
+ if ( ! empty( $theme_styles['styles']['label'] ) ) {
90
+ $html .= '<span class="exactmetrics-inline-popular-posts-label" ' . $this->get_element_style( $theme, 'label', $atts ) . '>' . $label_text . '</span>';
91
+ }
92
+ if ( ! empty( $theme_styles['styles']['border'] ) ) {
93
+ $html .= '<span class="exactmetrics-inline-popular-posts-border" ' . $this->get_element_style( $theme, 'border', $atts, 'color' ) . '></span>';
94
+ }
95
+ if ( ! empty( $theme_styles['styles']['border']['color2'] ) ) {
96
+ $html .= '<span class="exactmetrics-inline-popular-posts-border-2" ' . $this->get_element_style( $theme, 'border', $atts, 'color2' ) . '></span>';
97
+ }
98
+
99
+ $display_count = 0;
100
+ foreach ( $posts as $post ) {
101
+ $display_count ++;
102
+ if ( $display_count > $limit ) {
103
+ break;
104
+ }
105
+ $this->set_post_shown( $post['id'] );
106
+ $html .= '<div class="exactmetrics-inline-popular-posts-post">';
107
+ $html .= '<a class="exactmetrics-inline-popular-posts-title" ' . $this->get_element_style( $theme, 'title', $atts ) . ' href="' . $post['link'] . '">' . $post['title'] . '</a>';
108
+ $html .= '</div>';
109
+ }
110
+
111
+ $html .= '</div>';// Text div.
112
+ $html .= '</div><p></p>';// Main div.
113
+
114
+ return $html;
115
+
116
+ }
117
+
118
+ /**
119
+ * Specific inline styles based on theme settings.
120
+ *
121
+ * @return string
122
+ */
123
+ public function build_inline_styles() {
124
+
125
+ $themes = $this->get_themes_styles_for_output();
126
+ $styles = '';
127
+
128
+ foreach ( $themes as $theme_key => $theme_styles ) {
129
+ if ( ! empty( $theme_styles['background'] ) ) {
130
+ $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' {';
131
+
132
+ if ( ! empty( $theme_styles['background']['color'] ) ) {
133
+ $styles .= 'background-color:' . $theme_styles['background']['color'] . ';';
134
+ }
135
+ if ( ! empty( $theme_styles['background']['border'] ) ) {
136
+ $styles .= 'border-color:' . $theme_styles['background']['border'] . ';';
137
+ }
138
+
139
+ $styles .= '}';
140
+ }
141
+
142
+ if ( ! empty( $theme_styles['label'] ) ) {
143
+ $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-label {';
144
+
145
+ if ( ! empty( $theme_styles['label']['color'] ) ) {
146
+ $styles .= 'color:' . $theme_styles['label']['color'] . ';';
147
+ }
148
+
149
+ if ( ! empty( $theme_styles['label']['background'] ) ) {
150
+ $styles .= 'background-color:' . $theme_styles['label']['background'] . ';';
151
+ }
152
+
153
+ $styles .= '}';
154
+ }
155
+
156
+ if ( ! empty( $theme_styles['title'] ) ) {
157
+ $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-title {';
158
+
159
+ if ( ! empty( $theme_styles['title']['color'] ) ) {
160
+ $styles .= 'color:' . $theme_styles['title']['color'] . ';';
161
+ }
162
+ if ( ! empty( $theme_styles['title']['size'] ) ) {
163
+ $styles .= 'font-size:' . $theme_styles['title']['size'] . 'px;';
164
+ }
165
+
166
+ $styles .= '}';
167
+ }
168
+
169
+ if ( ! empty( $theme_styles['border'] ) ) {
170
+ $styles .= '.exactmetrics-inline-popular-posts.exactmetrics-popular-posts-styled.exactmetrics-inline-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-border {';
171
+
172
+ if ( ! empty( $theme_styles['border']['color'] ) ) {
173
+ $styles .= 'border-color:' . $theme_styles['border']['color'] . ';';
174
+ }
175
+
176
+ $styles .= '}';
177
+ }
178
+ }
179
+
180
+ return $styles;
181
+ }
182
+
183
+ /**
184
+ * Check if we should attempt to automatically insert the inline widget.
185
+ */
186
+ public function maybe_auto_insert() {
187
+
188
+ $post_types = $this->post_types;
189
+ if ( ! empty( $post_types ) && is_singular( $post_types ) && 'automatic' === $this->placement ) {
190
+ add_filter( 'the_content', array( $this, 'add_inline_posts_to_content' ) );
191
+ }
192
+
193
+ }
194
+
195
+ /**
196
+ * Insert the widget in the content.
197
+ *
198
+ * @param string $content The post content.
199
+ *
200
+ * @return string
201
+ */
202
+ public function add_inline_posts_to_content(
203
+ $content
204
+ ) {
205
+
206
+ if ( $this->is_post_excluded() ) {
207
+ return $content;
208
+ }
209
+
210
+ $words_count = str_word_count( $content );
211
+ $after_count = intval( $this->after_count );
212
+
213
+ // Insert only if there are more words then the insert after value.
214
+ if ( $words_count > $after_count ) {
215
+
216
+ $words = explode( ' ', $content );
217
+ $count = 0;
218
+
219
+ foreach ( $words as $index => $word ) {
220
+ $count ++;
221
+ if ( $count > $after_count ) {
222
+ $p_index = strpos( $word, '</p>' );
223
+ // Make sure the paragraph tag is not wrapped in another element like a blockquote.
224
+ if ( false !== $p_index && false === strpos( $word, '</p></' ) ) {
225
+ $words[ $index ] = substr_replace( $word, $this->shortcode_output( array() ), $p_index + 4, 0 );
226
+ $this->posts = array();
227
+ break;
228
+ }
229
+ }
230
+ }
231
+
232
+ $content = implode( ' ', $words );
233
+
234
+ }
235
+
236
+ return $content;
237
+ }
238
+
239
+ /**
240
+ * Check if the selected theme is available with the current license to avoid showing a theme not available.
241
+ * Returns the default 'alpha' theme if not available.
242
+ *
243
+ * @param string $theme Theme slug for which we are checking.
244
+ *
245
+ * @return string
246
+ */
247
+ public function is_theme_available( $theme ) {
248
+
249
+ $theme_props = $this->get_theme_props( $theme )->get_theme();
250
+
251
+ if ( ! empty( $theme_props['level'] ) && 'lite' === $theme_props['level'] ) {
252
+ return $theme;
253
+ }
254
+
255
+ return 'alpha';
256
+
257
+ }
258
+
259
+ }
260
+
261
+ /**
262
+ * Get the current class in a function.
263
+ *
264
+ * @return ExactMetrics_Popular_Posts_Inline Instance of the current class.
265
+ */
266
+ function ExactMetrics_Popular_Posts_Inline() {
267
+ return ExactMetrics_Popular_Posts_Inline::get_instance();
268
+ }
269
+
270
+ ExactMetrics_Popular_Posts_Inline();
lite/includes/popular-posts/class-popular-posts-widget-sidebar.php CHANGED
@@ -1,425 +1,425 @@
1
- <?php
2
- /**
3
- * Class used to add the Popular Posts widget to the Appearance > Widget area.
4
- */
5
-
6
- /**
7
- * Class ExactMetrics_Popular_Posts_Widget_Sidebar
8
- */
9
- class ExactMetrics_Popular_Posts_Widget_Sidebar extends WP_Widget {
10
- /**
11
- * Hold widget settings defaults, populated in constructor.
12
- *
13
- * @since 7.12.0
14
- *
15
- * @var array
16
- */
17
- protected $defaults;
18
- /**
19
- * Hold widget options that are theme specific.
20
- *
21
- * @since 7.12.0
22
- *
23
- * @var array
24
- */
25
- protected $conditional_options;
26
-
27
- /**
28
- * Constructor
29
- *
30
- * @since 7.12.0
31
- */
32
- public function __construct() {
33
-
34
- // Widget defaults.
35
- $this->defaults = array(
36
- 'title' => '',
37
- 'display_title' => 'on',
38
- 'post_count' => 5,
39
- 'theme' => 'alpha',
40
- 'title_color' => '#393F4C',
41
- 'title_size' => 12,
42
- 'label_color' => '#EB5757',
43
- 'label_text' => 'Trending',
44
- 'meta_color' => '#99A1B3',
45
- 'meta_size' => '12',
46
- 'meta_author' => 'on',
47
- 'meta_date' => 'on',
48
- 'meta_comments' => 'on',
49
- 'background_color' => '#F0F2F4',
50
- 'border_color' => '#D3D7DE',
51
- 'columns' => '1',
52
- );
53
-
54
- $this->conditional_options = array(
55
- 'title_color' => array( 'title', 'color' ),
56
- 'title_size' => array( 'title', 'size' ),
57
- 'label_color' => array( 'label', 'color' ),
58
- 'label_text' => array( 'label', 'text' ),
59
- 'background_color' => array( 'background', 'color' ),
60
- 'background_border' => array( 'background', 'border' ),
61
- 'meta_color' => array( 'meta', 'color' ),
62
- 'meta_size' => array( 'meta', 'size' ),
63
- 'meta_author' => array( 'meta', 'author' ),
64
- 'meta_date' => array( 'meta', 'date' ),
65
- 'meta_comments' => array( 'meta', 'comments' ),
66
- 'comments_color' => array( 'comments', 'color' ),
67
- );
68
-
69
- // Widget Slug.
70
- $widget_slug = 'exactmetrics-popular-posts-widget';
71
-
72
- // Widget basics.
73
- $widget_ops = array(
74
- 'classname' => $widget_slug,
75
- 'description' => esc_html_x( 'Display popular posts.', 'Widget', 'google-analytics-dashboard-for-wp' ),
76
- );
77
-
78
- // Widget controls.
79
- $control_ops = array(
80
- 'id_base' => $widget_slug,
81
- );
82
-
83
- $this->add_scripts();
84
-
85
- // Load widget.
86
- parent::__construct( $widget_slug, esc_html_x( 'Popular Posts - ExactMetrics', 'Widget', 'google-analytics-dashboard-for-wp' ), $widget_ops, $control_ops );
87
- }
88
-
89
- /**
90
- * Output the HTML for this widget.
91
- *
92
- * @param array $args An array of standard parameters for widgets in this theme.
93
- * @param array $instance An array of settings for this widget instance.
94
- *
95
- * @since 7.12.0
96
- *
97
- */
98
- public function widget( $args, $instance ) {
99
-
100
- echo $args['before_widget'];
101
-
102
- $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
103
-
104
- if ( $instance['display_title'] && ! empty( $instance['title'] ) ) {
105
- echo $args['before_title'];
106
- echo wp_kses_post( $title );
107
- echo $args['after_title'];
108
- }
109
-
110
- $atts = array(
111
- 'theme' => $instance['theme'],
112
- 'post_count' => $instance['post_count'],
113
- 'columns' => 1, // Sidebar is not wide so we always use the 1 column layout.
114
- 'widget_title' => false, // Override this in favor of sidebar-specific markup above.
115
- );
116
-
117
- foreach ( $this->conditional_options as $key => $default ) {
118
- if ( ! empty( $instance[ $key ] ) ) {
119
- $atts[ $key ] = $instance[ $key ];
120
- }
121
- }
122
-
123
- echo ExactMetrics_Popular_Posts_Widget()->shortcode_output( $atts );
124
-
125
- echo $args['after_widget'];
126
-
127
- }
128
-
129
- /**
130
- * Deal with the settings when they are saved by the admin. Here is
131
- * where any validation should be dealt with.
132
- *
133
- * @param array $new_instance An array of new settings as submitted by the admin.
134
- * @param array $old_instance An array of the previous settings.
135
- *
136
- * @return array The validated and (if necessary) amended settings
137
- * @since 7.12.0
138
- *
139
- */
140
- public function update( $new_instance, $old_instance ) {
141
-
142
- $new_instance['title'] = wp_strip_all_tags( $new_instance['title'] );
143
- $new_instance['theme'] = wp_strip_all_tags( $new_instance['theme'] );
144
- $new_instance['display_title'] = isset( $new_instance['display_title'] ) ? wp_strip_all_tags( $new_instance['display_title'] ) : '';
145
- $new_instance['post_count'] = absint( $new_instance['post_count'] );
146
-
147
- // Theme-dependant options.
148
- $themes = new ExactMetrics_Popular_Posts_Themes( 'widget', ! empty( $old_instance['theme'] ) ? $old_instance['theme'] : '' );
149
- $theme = $themes->get_theme();
150
-
151
- foreach ( $this->conditional_options as $key => $obj ) {
152
- $new_instance = $this->maybe_remove_option( ! empty( $theme['styles'][ $obj[0] ][ $obj[1] ] ), $key, $new_instance );
153
- }
154
-
155
- return $new_instance;
156
- }
157
-
158
- /**
159
- * Process dynamic and checkbox values so they are stored correctly and specific to the current theme.
160
- *
161
- * @param bool $is_used A check if this property is used in the currently selected theme.
162
- * @param string $key The key of the property we're checking.
163
- * @param array $instance The current widget instance, new instance.
164
- *
165
- * @return mixed
166
- */
167
- public function maybe_remove_option( $is_used, $key, $instance ) {
168
-
169
- $checkboxes = array(
170
- 'meta_author',
171
- 'meta_date',
172
- 'meta_comments',
173
- );
174
-
175
- if ( $is_used && ! isset( $instance[ $key ] ) && in_array( $key, $checkboxes ) ) {
176
- $instance[ $key ] = 'off';
177
- } elseif ( ! $is_used && isset( $instance[ $key ] ) ) {
178
- unset( $instance[ $key ] );
179
- } elseif ( $is_used && isset( $instance[ $key ] ) ) {
180
- $instance[ $key ] = wp_strip_all_tags( $instance[ $key ] );
181
- }
182
-
183
- return $instance;
184
- }
185
-
186
- /**
187
- * Display the form for this widget on the Widgets page of the WP Admin area.
188
- *
189
- * @param array $instance An array of the current settings for this widget.
190
- *
191
- * @since 7.12.0
192
- */
193
- public function form( $instance ) {
194
-
195
- // Merge with defaults but use theme settings from Vue as defaults.
196
- $theme_name = empty( $instance['theme'] ) ? $this->defaults['theme'] : $instance['theme'];
197
- $themes = new ExactMetrics_Popular_Posts_Themes( 'widget', $theme_name );
198
- $theme = $themes->get_theme();
199
- $this->prepare_defaults_from_theme( $theme );
200
-
201
- $instance = wp_parse_args( (array) $instance, $this->defaults );
202
-
203
- $title_font_sizes = apply_filters( 'exactmetrics_popular_posts_widget_title_sizes', range( 10, 35 ) );
204
-
205
- $this->text_input( 'title', _x( 'Widget Title:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
206
-
207
- $categories = array(
208
- array(
209
- 'name' => 'News',
210
- 'value' => '0',
211
- ),
212
- array(
213
- 'name' => 'Technology',
214
- 'value' => '1',
215
- ),
216
- );
217
- ?>
218
- <p>
219
- <input type="checkbox"
220
- id="<?php echo esc_attr( $this->get_field_id( 'display_title' ) ); ?>"
221
- name="<?php echo esc_attr( $this->get_field_name( 'display_title' ) ); ?>"
222
- value="on" <?php checked( $instance['display_title'], 'on' ); ?> />
223
- <label for="<?php echo esc_attr( $this->get_field_id( 'display_title' ) ); ?>">
224
- <?php echo esc_html( _x( 'Display Widget Title', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
225
- </label>
226
- </p>
227
- <p>
228
- <label for="<?php echo esc_attr( $this->get_field_id( 'post_count' ) ); ?>">
229
- <?php echo esc_html( _x( 'Number of posts to display:', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
230
- </label>
231
- <select id="<?php echo esc_attr( $this->get_field_id( 'post_count' ) ); ?>"
232
- name="<?php echo esc_attr( $this->get_field_name( 'post_count' ) ); ?>">
233
- <option value="5" <?php selected( $instance['post_count'], 5 ); ?>>5</option>
234
- <option value="10" <?php selected( $instance['post_count'], 10 ); ?>>10</option>
235
- </select>
236
- </p>
237
- <p>
238
- <label for="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>">
239
- <?php echo esc_html( _x( 'Theme:', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
240
- </label>
241
- <select id="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>"
242
- class="widefat exactmetrics-save-on-change"
243
- name="<?php echo esc_attr( $this->get_field_name( 'theme' ) ); ?>">
244
- <?php foreach ( $themes->themes as $key => $details ) {
245
- if ( 'lite' !== $details['level'] ) {
246
- continue;
247
- }
248
- ?>
249
- <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $instance['theme'], $key ); ?>>
250
- <?php echo esc_html( ucfirst( $key ) ); ?>
251
- </option>
252
- <?php } ?>
253
- </select>
254
- </p>
255
- <div class="exactmetrics-widget-theme-preview">
256
- <span class="exactmetrics-widget-theme-preview-label">
257
- <?php esc_html_e( 'Theme Preview', 'google-analytics-dashboard-for-wp' ); ?>
258
- </span>
259
- <div class="exactmetrics-widget-theme-preview-icon exactmetrics-widget-theme-preview-icon-<?php echo esc_attr( $instance['theme'] ); ?>"></div>
260
- </div>
261
- <?php if ( ! empty( $theme['styles']['title']['color'] ) ) {
262
- $this->color_input( 'title_color', _x( 'Title Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
263
- ?>
264
- <?php
265
- }
266
- if ( ! empty( $theme['styles']['title']['size'] ) ) {
267
- $this->size_input( 'title_size', _x( 'Title Font Size:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance, $title_font_sizes );
268
- }
269
- if ( ! empty( $theme['styles']['label']['color'] ) ) {
270
- $this->color_input( 'label_color', _x( 'Label Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
271
- }
272
- if ( ! empty( $theme['styles']['label']['editable'] ) && ! empty( $theme['styles']['label']['text'] ) ) {
273
- $this->text_input( 'label_text', _x( 'Label Text:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
274
- }
275
- if ( ! empty( $theme['styles']['background']['border'] ) ) {
276
- $this->color_input( 'background_border', _x( 'Border Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
277
- }
278
- if ( ! empty( $theme['styles']['background']['color'] ) ) {
279
- $this->color_input( 'background_color', _x( 'Background Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
280
- }
281
- if ( ! empty( $theme['styles']['comments']['color'] ) ) {
282
- $this->color_input( 'comments_color', _x( 'Comments Count Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
283
- }
284
- ?>
285
- <p>
286
-
287
- <label class="exactmetrics-label-block">
288
- <?php echo esc_html( _x( 'Only Show Posts from These Categories:', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
289
- <span class="exactmetrics-pro-pill">PRO</span>
290
- </label>
291
- <div class="select300 select300-container select300-container--default select300-container--disabled select300-container--focus"
292
- dir="ltr" data-select300-id="2" style="width: auto;">
293
- <div class="selection">
294
- <div class="select300-selection select300-selection--multiple">
295
- <ul class="select300-selection__rendered">
296
- <li class="select300-selection__choice" title="News" data-select300-id="5">
297
- <span class="select300-selection__choice__remove" role="presentation">×</span>News
298
- </li>
299
- <li class="select300-selection__choice" title="Technology" data-select300-id="6">
300
- <span class="select300-selection__choice__remove" role="presentation">×</span>Technology
301
- </li>
302
- </ul>
303
- </div>
304
- </div>
305
- </div>
306
- </p>
307
- <?php
308
- }
309
-
310
- /**
311
- * Colorpicker input element.
312
- *
313
- * @param string $name Name of the input, for saving/loading.
314
- * @param string $label Label of the element.
315
- * @param array $instance The current widget instance.
316
- */
317
- public function color_input( $name, $label, $instance ) {
318
- ?>
319
- <p>
320
- <label class="exactmetrics-label-block"
321
- for="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>">
322
- <?php echo esc_html( $label ); ?>
323
- </label>
324
- <input type="text"
325
- id="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>"
326
- name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>"
327
- value="<?php echo esc_attr( $instance[ $name ] ); ?>"
328
- class="widefat exactmetrics-color-field"/>
329
- </p>
330
- <?php
331
- }
332
-
333
- /**
334
- * Regular text input.
335
- *
336
- * @param string $name Name of the input, for saving/loading.
337
- * @param string $label Label of the element.
338
- * @param array $instance The current widget instance.
339
- */
340
- public function text_input( $name, $label, $instance ) {
341
- ?>
342
- <p>
343
- <label for="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>">
344
- <?php echo esc_html( $label ); ?>
345
- </label>
346
- <input type="text"
347
- id="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>"
348
- name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>"
349
- value="<?php echo esc_attr( $instance[ $name ] ); ?>" class="widefat"/>
350
- </p>
351
- <?php
352
- }
353
-
354
- /**
355
- * Size input - used for font size inputs.
356
- *
357
- * @param string $name Name of the input, for saving/loading.
358
- * @param string $label Label of the element.
359
- * @param array $instance The current widget instance.
360
- * @param array $range The options available to select.
361
- */
362
- public function size_input( $name, $label, $instance, $range = array() ) {
363
- ?>
364
- <p>
365
- <label for="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>">
366
- <?php echo esc_html( $label ); ?>
367
- </label>
368
- <select id="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>"
369
- name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>" class="widefat">
370
- <?php foreach ( $range as $font_size ) { ?>
371
- <option value="<?php echo absint( $font_size ); ?>" <?php selected( $instance[ $name ], $font_size ); ?>><?php printf( esc_html_x( '%dpx', 'google-analytics-dashboard-for-wp' ), $font_size ); ?></option>
372
- <?php } ?>
373
- </select>
374
- </p>
375
- <?php
376
- }
377
-
378
- /**
379
- * Prepare theme specific options.
380
- *
381
- * @param array $theme The theme options.
382
- */
383
- public function prepare_defaults_from_theme( $theme ) {
384
- foreach ( $this->conditional_options as $key => $obj ) {
385
- if ( ! empty( $theme['styles'][ $obj[0] ][ $obj[1] ] ) ) {
386
- $this->defaults[ $key ] = $theme['styles'][ $obj[0] ][ $obj[1] ];
387
- }
388
- }
389
- }
390
-
391
- /**
392
- * Load specific widget scripts in the admin.
393
- */
394
- public function add_scripts() {
395
- add_action( 'admin_enqueue_scripts', array( $this, 'load_widget_scripts' ) );
396
- }
397
-
398
- /**
399
- * Load admin-specific widget scripts.
400
- */
401
- public function load_widget_scripts() {
402
-
403
- $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : false;
404
-
405
- if ( ! isset( $screen->id ) || 'widgets' !== $screen->id ) {
406
- return;
407
- }
408
-
409
-
410
- $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
411
- wp_enqueue_style( 'exactmetrics-admin-widget-setting-styles', plugins_url( 'assets/css/admin-widget-settings' . $suffix . '.css', EXACTMETRICS_PLUGIN_FILE ), array(
412
- 'wp-color-picker',
413
- ), exactmetrics_get_asset_version() );
414
-
415
- wp_register_script( 'exactmetrics-admin-widget-settings', plugins_url( 'assets/js/admin-widget-settings' . $suffix . '.js', EXACTMETRICS_PLUGIN_FILE ), array(
416
- 'jquery',
417
- 'wp-color-picker',
418
- ), exactmetrics_get_asset_version(), true );
419
- wp_enqueue_script( 'exactmetrics-admin-widget-settings' );
420
-
421
- wp_localize_script( 'exactmetrics-admin-widget-settings', 'exactmetrics_pp', array(
422
- 'nonce' => wp_create_nonce( 'mi-admin-nonce' ),
423
- ) );
424
- }
425
- }
1
+ <?php
2
+ /**
3
+ * Class used to add the Popular Posts widget to the Appearance > Widget area.
4
+ */
5
+
6
+ /**
7
+ * Class ExactMetrics_Popular_Posts_Widget_Sidebar
8
+ */
9
+ class ExactMetrics_Popular_Posts_Widget_Sidebar extends WP_Widget {
10
+ /**
11
+ * Hold widget settings defaults, populated in constructor.
12
+ *
13
+ * @since 7.12.0
14
+ *
15
+ * @var array
16
+ */
17
+ protected $defaults;
18
+ /**
19
+ * Hold widget options that are theme specific.
20
+ *
21
+ * @since 7.12.0
22
+ *
23
+ * @var array
24
+ */
25
+ protected $conditional_options;
26
+
27
+ /**
28
+ * Constructor
29
+ *
30
+ * @since 7.12.0
31
+ */
32
+ public function __construct() {
33
+
34
+ // Widget defaults.
35
+ $this->defaults = array(
36
+ 'title' => '',
37
+ 'display_title' => 'on',
38
+ 'post_count' => 5,
39
+ 'theme' => 'alpha',
40
+ 'title_color' => '#393F4C',
41
+ 'title_size' => 12,
42
+ 'label_color' => '#EB5757',
43
+ 'label_text' => 'Trending',
44
+ 'meta_color' => '#99A1B3',
45
+ 'meta_size' => '12',
46
+ 'meta_author' => 'on',
47
+ 'meta_date' => 'on',
48
+ 'meta_comments' => 'on',
49
+ 'background_color' => '#F0F2F4',
50
+ 'border_color' => '#D3D7DE',
51
+ 'columns' => '1',
52
+ );
53
+
54
+ $this->conditional_options = array(
55
+ 'title_color' => array( 'title', 'color' ),
56
+ 'title_size' => array( 'title', 'size' ),
57
+ 'label_color' => array( 'label', 'color' ),
58
+ 'label_text' => array( 'label', 'text' ),
59
+ 'background_color' => array( 'background', 'color' ),
60
+ 'background_border' => array( 'background', 'border' ),
61
+ 'meta_color' => array( 'meta', 'color' ),
62
+ 'meta_size' => array( 'meta', 'size' ),
63
+ 'meta_author' => array( 'meta', 'author' ),
64
+ 'meta_date' => array( 'meta', 'date' ),
65
+ 'meta_comments' => array( 'meta', 'comments' ),
66
+ 'comments_color' => array( 'comments', 'color' ),
67
+ );
68
+
69
+ // Widget Slug.
70
+ $widget_slug = 'exactmetrics-popular-posts-widget';
71
+
72
+ // Widget basics.
73
+ $widget_ops = array(
74
+ 'classname' => $widget_slug,
75
+ 'description' => esc_html_x( 'Display popular posts.', 'Widget', 'google-analytics-dashboard-for-wp' ),
76
+ );
77
+
78
+ // Widget controls.
79
+ $control_ops = array(
80
+ 'id_base' => $widget_slug,
81
+ );
82
+
83
+ $this->add_scripts();
84
+
85
+ // Load widget.
86
+ parent::__construct( $widget_slug, esc_html_x( 'Popular Posts - ExactMetrics', 'Widget', 'google-analytics-dashboard-for-wp' ), $widget_ops, $control_ops );
87
+ }
88
+
89
+ /**
90
+ * Output the HTML for this widget.
91
+ *
92
+ * @param array $args An array of standard parameters for widgets in this theme.
93
+ * @param array $instance An array of settings for this widget instance.
94
+ *
95
+ * @since 7.12.0
96
+ *
97
+ */
98
+ public function widget( $args, $instance ) {
99
+
100
+ echo $args['before_widget'];
101
+
102
+ $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
103
+
104
+ if ( $instance['display_title'] && ! empty( $instance['title'] ) ) {
105
+ echo $args['before_title'];
106
+ echo wp_kses_post( $title );
107
+ echo $args['after_title'];
108
+ }
109
+
110
+ $atts = array(
111
+ 'theme' => $instance['theme'],
112
+ 'post_count' => $instance['post_count'],
113
+ 'columns' => 1, // Sidebar is not wide so we always use the 1 column layout.
114
+ 'widget_title' => false, // Override this in favor of sidebar-specific markup above.
115
+ );
116
+
117
+ foreach ( $this->conditional_options as $key => $default ) {
118
+ if ( ! empty( $instance[ $key ] ) ) {
119
+ $atts[ $key ] = $instance[ $key ];
120
+ }
121
+ }
122
+
123
+ echo ExactMetrics_Popular_Posts_Widget()->shortcode_output( $atts );
124
+
125
+ echo $args['after_widget'];
126
+
127
+ }
128
+
129
+ /**
130
+ * Deal with the settings when they are saved by the admin. Here is
131
+ * where any validation should be dealt with.
132
+ *
133
+ * @param array $new_instance An array of new settings as submitted by the admin.
134
+ * @param array $old_instance An array of the previous settings.
135
+ *
136
+ * @return array The validated and (if necessary) amended settings
137
+ * @since 7.12.0
138
+ *
139
+ */
140
+ public function update( $new_instance, $old_instance ) {
141
+
142
+ $new_instance['title'] = wp_strip_all_tags( $new_instance['title'] );
143
+ $new_instance['theme'] = wp_strip_all_tags( $new_instance['theme'] );
144
+ $new_instance['display_title'] = isset( $new_instance['display_title'] ) ? wp_strip_all_tags( $new_instance['display_title'] ) : '';
145
+ $new_instance['post_count'] = absint( $new_instance['post_count'] );
146
+
147
+ // Theme-dependant options.
148
+ $themes = new ExactMetrics_Popular_Posts_Themes( 'widget', ! empty( $old_instance['theme'] ) ? $old_instance['theme'] : '' );
149
+ $theme = $themes->get_theme();
150
+
151
+ foreach ( $this->conditional_options as $key => $obj ) {
152
+ $new_instance = $this->maybe_remove_option( ! empty( $theme['styles'][ $obj[0] ][ $obj[1] ] ), $key, $new_instance );
153
+ }
154
+
155
+ return $new_instance;
156
+ }
157
+
158
+ /**
159
+ * Process dynamic and checkbox values so they are stored correctly and specific to the current theme.
160
+ *
161
+ * @param bool $is_used A check if this property is used in the currently selected theme.
162
+ * @param string $key The key of the property we're checking.
163
+ * @param array $instance The current widget instance, new instance.
164
+ *
165
+ * @return mixed
166
+ */
167
+ public function maybe_remove_option( $is_used, $key, $instance ) {
168
+
169
+ $checkboxes = array(
170
+ 'meta_author',
171
+ 'meta_date',
172
+ 'meta_comments',
173
+ );
174
+
175
+ if ( $is_used && ! isset( $instance[ $key ] ) && in_array( $key, $checkboxes ) ) {
176
+ $instance[ $key ] = 'off';
177
+ } elseif ( ! $is_used && isset( $instance[ $key ] ) ) {
178
+ unset( $instance[ $key ] );
179
+ } elseif ( $is_used && isset( $instance[ $key ] ) ) {
180
+ $instance[ $key ] = wp_strip_all_tags( $instance[ $key ] );
181
+ }
182
+
183
+ return $instance;
184
+ }
185
+
186
+ /**
187
+ * Display the form for this widget on the Widgets page of the WP Admin area.
188
+ *
189
+ * @param array $instance An array of the current settings for this widget.
190
+ *
191
+ * @since 7.12.0
192
+ */
193
+ public function form( $instance ) {
194
+
195
+ // Merge with defaults but use theme settings from Vue as defaults.
196
+ $theme_name = empty( $instance['theme'] ) ? $this->defaults['theme'] : $instance['theme'];
197
+ $themes = new ExactMetrics_Popular_Posts_Themes( 'widget', $theme_name );
198
+ $theme = $themes->get_theme();
199
+ $this->prepare_defaults_from_theme( $theme );
200
+
201
+ $instance = wp_parse_args( (array) $instance, $this->defaults );
202
+
203
+ $title_font_sizes = apply_filters( 'exactmetrics_popular_posts_widget_title_sizes', range( 10, 35 ) );
204
+
205
+ $this->text_input( 'title', _x( 'Widget Title:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
206
+
207
+ $categories = array(
208
+ array(
209
+ 'name' => 'News',
210
+ 'value' => '0',
211
+ ),
212
+ array(
213
+ 'name' => 'Technology',
214
+ 'value' => '1',
215
+ ),
216
+ );
217
+ ?>
218
+ <p>
219
+ <input type="checkbox"
220
+ id="<?php echo esc_attr( $this->get_field_id( 'display_title' ) ); ?>"
221
+ name="<?php echo esc_attr( $this->get_field_name( 'display_title' ) ); ?>"
222
+ value="on" <?php checked( $instance['display_title'], 'on' ); ?> />
223
+ <label for="<?php echo esc_attr( $this->get_field_id( 'display_title' ) ); ?>">
224
+ <?php echo esc_html( _x( 'Display Widget Title', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
225
+ </label>
226
+ </p>
227
+ <p>
228
+ <label for="<?php echo esc_attr( $this->get_field_id( 'post_count' ) ); ?>">
229
+ <?php echo esc_html( _x( 'Number of posts to display:', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
230
+ </label>
231
+ <select id="<?php echo esc_attr( $this->get_field_id( 'post_count' ) ); ?>"
232
+ name="<?php echo esc_attr( $this->get_field_name( 'post_count' ) ); ?>">
233
+ <option value="5" <?php selected( $instance['post_count'], 5 ); ?>>5</option>
234
+ <option value="10" <?php selected( $instance['post_count'], 10 ); ?>>10</option>
235
+ </select>
236
+ </p>
237
+ <p>
238
+ <label for="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>">
239
+ <?php echo esc_html( _x( 'Theme:', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
240
+ </label>
241
+ <select id="<?php echo esc_attr( $this->get_field_id( 'theme' ) ); ?>"
242
+ class="widefat exactmetrics-save-on-change"
243
+ name="<?php echo esc_attr( $this->get_field_name( 'theme' ) ); ?>">
244
+ <?php foreach ( $themes->themes as $key => $details ) {
245
+ if ( 'lite' !== $details['level'] ) {
246
+ continue;
247
+ }
248
+ ?>
249
+ <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $instance['theme'], $key ); ?>>
250
+ <?php echo esc_html( ucfirst( $key ) ); ?>
251
+ </option>
252
+ <?php } ?>
253
+ </select>
254
+ </p>
255
+ <div class="exactmetrics-widget-theme-preview">
256
+ <span class="exactmetrics-widget-theme-preview-label">
257
+ <?php esc_html_e( 'Theme Preview', 'google-analytics-dashboard-for-wp' ); ?>
258
+ </span>
259
+ <div class="exactmetrics-widget-theme-preview-icon exactmetrics-widget-theme-preview-icon-<?php echo esc_attr( $instance['theme'] ); ?>"></div>
260
+ </div>
261
+ <?php if ( ! empty( $theme['styles']['title']['color'] ) ) {
262
+ $this->color_input( 'title_color', _x( 'Title Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
263
+ ?>
264
+ <?php
265
+ }
266
+ if ( ! empty( $theme['styles']['title']['size'] ) ) {
267
+ $this->size_input( 'title_size', _x( 'Title Font Size:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance, $title_font_sizes );
268
+ }
269
+ if ( ! empty( $theme['styles']['label']['color'] ) ) {
270
+ $this->color_input( 'label_color', _x( 'Label Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
271
+ }
272
+ if ( ! empty( $theme['styles']['label']['editable'] ) && ! empty( $theme['styles']['label']['text'] ) ) {
273
+ $this->text_input( 'label_text', _x( 'Label Text:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
274
+ }
275
+ if ( ! empty( $theme['styles']['background']['border'] ) ) {
276
+ $this->color_input( 'background_border', _x( 'Border Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
277
+ }
278
+ if ( ! empty( $theme['styles']['background']['color'] ) ) {
279
+ $this->color_input( 'background_color', _x( 'Background Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
280
+ }
281
+ if ( ! empty( $theme['styles']['comments']['color'] ) ) {
282
+ $this->color_input( 'comments_color', _x( 'Comments Count Color:', 'Widget', 'google-analytics-dashboard-for-wp' ), $instance );
283
+ }
284
+ ?>
285
+ <p>
286
+
287
+ <label class="exactmetrics-label-block">
288
+ <?php echo esc_html( _x( 'Only Show Posts from These Categories:', 'Widget', 'google-analytics-dashboard-for-wp' ) ); ?>
289
+ <span class="exactmetrics-pro-pill">PRO</span>
290
+ </label>
291
+ <div class="select300 select300-container select300-container--default select300-container--disabled select300-container--focus"
292
+ dir="ltr" data-select300-id="2" style="width: auto;">
293
+ <div class="selection">
294
+ <div class="select300-selection select300-selection--multiple">
295
+ <ul class="select300-selection__rendered">
296
+ <li class="select300-selection__choice" title="News" data-select300-id="5">
297
+ <span class="select300-selection__choice__remove" role="presentation">×</span>News
298
+ </li>
299
+ <li class="select300-selection__choice" title="Technology" data-select300-id="6">
300
+ <span class="select300-selection__choice__remove" role="presentation">×</span>Technology
301
+ </li>
302
+ </ul>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ </p>
307
+ <?php
308
+ }
309
+
310
+ /**
311
+ * Colorpicker input element.
312
+ *
313
+ * @param string $name Name of the input, for saving/loading.
314
+ * @param string $label Label of the element.
315
+ * @param array $instance The current widget instance.
316
+ */
317
+ public function color_input( $name, $label, $instance ) {
318
+ ?>
319
+ <p>
320
+ <label class="exactmetrics-label-block"
321
+ for="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>">
322
+ <?php echo esc_html( $label ); ?>
323
+ </label>
324
+ <input type="text"
325
+ id="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>"
326
+ name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>"
327
+ value="<?php echo esc_attr( $instance[ $name ] ); ?>"
328
+ class="widefat exactmetrics-color-field"/>
329
+ </p>
330
+ <?php
331
+ }
332
+
333
+ /**
334
+ * Regular text input.
335
+ *
336
+ * @param string $name Name of the input, for saving/loading.
337
+ * @param string $label Label of the element.
338
+ * @param array $instance The current widget instance.
339
+ */
340
+ public function text_input( $name, $label, $instance ) {
341
+ ?>
342
+ <p>
343
+ <label for="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>">
344
+ <?php echo esc_html( $label ); ?>
345
+ </label>
346
+ <input type="text"
347
+ id="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>"
348
+ name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>"
349
+ value="<?php echo esc_attr( $instance[ $name ] ); ?>" class="widefat"/>
350
+ </p>
351
+ <?php
352
+ }
353
+
354
+ /**
355
+ * Size input - used for font size inputs.
356
+ *
357
+ * @param string $name Name of the input, for saving/loading.
358
+ * @param string $label Label of the element.
359
+ * @param array $instance The current widget instance.
360
+ * @param array $range The options available to select.
361
+ */
362
+ public function size_input( $name, $label, $instance, $range = array() ) {
363
+ ?>
364
+ <p>
365
+ <label for="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>">
366
+ <?php echo esc_html( $label ); ?>
367
+ </label>
368
+ <select id="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>"
369
+ name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>" class="widefat">
370
+ <?php foreach ( $range as $font_size ) { ?>
371
+ <option value="<?php echo absint( $font_size ); ?>" <?php selected( $instance[ $name ], $font_size ); ?>><?php printf( esc_html_x( '%dpx', 'google-analytics-dashboard-for-wp' ), $font_size ); ?></option>
372
+ <?php } ?>
373
+ </select>
374
+ </p>
375
+ <?php
376
+ }
377
+
378
+ /**
379
+ * Prepare theme specific options.
380
+ *
381
+ * @param array $theme The theme options.
382
+ */
383
+ public function prepare_defaults_from_theme( $theme ) {
384
+ foreach ( $this->conditional_options as $key => $obj ) {
385
+ if ( ! empty( $theme['styles'][ $obj[0] ][ $obj[1] ] ) ) {
386
+ $this->defaults[ $key ] = $theme['styles'][ $obj[0] ][ $obj[1] ];
387
+ }
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Load specific widget scripts in the admin.
393
+ */
394
+ public function add_scripts() {
395
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_widget_scripts' ) );
396
+ }
397
+
398
+ /**
399
+ * Load admin-specific widget scripts.
400
+ */
401
+ public function load_widget_scripts() {
402
+
403
+ $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : false;
404
+
405
+ if ( ! isset( $screen->id ) || 'widgets' !== $screen->id ) {
406
+ return;
407
+ }
408
+
409
+
410
+ $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
411
+ wp_enqueue_style( 'exactmetrics-admin-widget-setting-styles', plugins_url( 'assets/css/admin-widget-settings' . $suffix . '.css', EXACTMETRICS_PLUGIN_FILE ), array(
412
+ 'wp-color-picker',
413
+ ), exactmetrics_get_asset_version() );
414
+
415
+ wp_register_script( 'exactmetrics-admin-widget-settings', plugins_url( 'assets/js/admin-widget-settings' . $suffix . '.js', EXACTMETRICS_PLUGIN_FILE ), array(
416
+ 'jquery',
417
+ 'wp-color-picker',
418
+ ), exactmetrics_get_asset_version(), true );
419
+ wp_enqueue_script( 'exactmetrics-admin-widget-settings' );
420
+
421
+ wp_localize_script( 'exactmetrics-admin-widget-settings', 'exactmetrics_pp', array(
422
+ 'nonce' => wp_create_nonce( 'mi-admin-nonce' ),
423
+ ) );
424
+ }
425
+ }
lite/includes/popular-posts/class-popular-posts-widget.php CHANGED
@@ -1,262 +1,262 @@
1
- <?php
2
- /**
3
- * Code specific to the widget Popular Posts widget type.
4
- */
5
-
6
- /**
7
- * Class ExactMetrics_Popular_Posts_Widget
8
- */
9
- class ExactMetrics_Popular_Posts_Widget extends ExactMetrics_Popular_Posts {
10
-
11
- /**
12
- * The instance type. Used for loading specific settings.
13
- *
14
- * @var string
15
- */
16
- protected $type = 'widget';
17
-
18
- /**
19
- * Used to load the setting specific for this class.
20
- *
21
- * @var string
22
- */
23
- protected $settings_key = 'popular_posts_widget';
24
-
25
- /**
26
- * Used for registering the shortcode specific to this class.
27
- *
28
- * @var string
29
- */
30
- protected $shortcode_key = 'exactmetrics_popular_posts_widget';
31
-
32
- /**
33
- * Widget-specific hooks.
34
- */
35
- public function hooks() {
36
- parent::hooks();
37
-
38
- add_action( 'wp', array( $this, 'maybe_auto_insert' ) );
39
-
40
- add_action( 'widgets_init', array( $this, 'register_widget' ) );
41
- }
42
-
43
-
44
- /**
45
- * Register Popular Posts widget.
46
- */
47
- public function register_widget() {
48
- register_widget( 'ExactMetrics_Popular_Posts_Widget_Sidebar' );
49
- }
50
-
51
- /**
52
- * Get the rendered HTML for output.
53
- *
54
- * @param array $atts These are attributes used to build the specific instance, they can be either shortcode
55
- * attributes or Gutenberg block props.
56
- *
57
- * @return string
58
- */
59
- public function get_rendered_html( $atts ) {
60
-
61
- $theme = $this->theme;
62
- if ( ! empty( $atts['theme'] ) ) {
63
- $theme = $atts['theme'];
64
- }
65
-
66
- $theme = $this->is_theme_available( $theme );
67
-
68
- if ( ! empty( $atts['post_count'] ) ) {
69
- $limit = intval( $atts['post_count'] );
70
- } else {
71
- $limit = $this->count;
72
- }
73
-
74
- $posts = $this->get_posts_to_display();
75
-
76
- if ( empty( $posts ) ) {
77
- return '';
78
- }
79
-
80
- if ( 'curated' === $this->sort && apply_filters( 'exactmetrics_popular_posts_widget_curated_shuffle', true ) ) {
81
- // Randomize the order.
82
- shuffle( $posts );
83
- }
84
-
85
- $theme_styles = $this->get_theme_props( $theme )->get_theme();
86
-
87
- $label_text = '';
88
- if ( isset( $theme_styles['styles']['label'] ) ) {
89
- $label_text = isset( $atts['label_text'] ) ? $atts['label_text'] : $theme_styles['styles']['label']['text'];
90
- }
91
-
92
- if ( isset( $atts['widget_title'] ) ) {
93
- $show_title = (bool) $atts['widget_title'];
94
- $title_text = empty( $atts['widget_title_text'] ) ? '' : $atts['widget_title_text'];
95
- } else {
96
- $show_title = $this->title;
97
- $title_text = $this->title_text;
98
- }
99
-
100
- $html = '<div class="' . $this->get_wrapper_class( $atts ) . '">';
101
- if ( $show_title ) {
102
- $html .= '<h2 class="exactmetrics-widget-popular-posts-widget-title">' . wp_kses_post( $title_text ) . '</h2>';
103
- }
104
-
105
- $html .= '<ul class="exactmetrics-widget-popular-posts-list">';
106
-
107
- $display_count = 0;
108
- foreach ( $posts as $post ) {
109
- $display_count ++;
110
- if ( $display_count > $limit ) {
111
- break;
112
- }
113
- $this->set_post_shown( $post['id'] );
114
- $html .= '<li ' . $this->get_element_style( $theme, 'background', $atts ) . '>';
115
- $html .= '<a href="' . $post['link'] . '">';
116
- if ( ! empty( $theme_styles['image'] ) && ! empty( $post['image'] ) ) {
117
- $html .= '<div class="exactmetrics-widget-popular-posts-image">';
118
- $html .= '<img src="' . $post['image'] . '" srcset=" ' . $post['srcset'] . ' " alt="' . esc_attr( $post['title'] ) . '" />';
119
- $html .= '</div>';
120
- }
121
- $html .= '<div class="exactmetrics-widget-popular-posts-text">';
122
- if ( isset( $theme_styles['styles']['label'] ) ) {
123
- $html .= '<span class="exactmetrics-widget-popular-posts-label" ' . $this->get_element_style( $theme, 'label', $atts ) . '>' . $label_text . '</span>';
124
- }
125
- $html .= '<span class="exactmetrics-widget-popular-posts-title" ' . $this->get_element_style( $theme, 'title', $atts ) . '>' . $post['title'] . '</span>';
126
- $html .= '</div>';// exactmetrics-widget-popular-posts-text.
127
- $html .= '</a>';
128
- $html .= '</li>';
129
- }
130
-
131
- $html .= '</ul></div><p></p>';// Main div.
132
-
133
- return $html;
134
-
135
- }
136
-
137
- /**
138
- * Add widget-specific styles based on theme settings.
139
- */
140
- public function build_inline_styles() {
141
-
142
- $themes = $this->get_themes_styles_for_output();
143
- $styles = '';
144
-
145
- foreach ( $themes as $theme_key => $theme_styles ) {
146
-
147
- if ( ! empty( $theme_styles['background'] ) ) {
148
- $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-widget-popular-posts-list li {';
149
-
150
- if ( ! empty( $theme_styles['background']['color'] ) ) {
151
- $styles .= 'background-color:' . $theme_styles['background']['color'] . ';';
152
- }
153
- if ( ! empty( $theme_styles['background']['border'] ) ) {
154
- $styles .= 'border-color:' . $theme_styles['background']['border'] . ';';
155
- }
156
-
157
- $styles .= '}';
158
- }
159
-
160
- if ( ! empty( $theme_styles['label'] ) ) {
161
- $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-widget-popular-posts-label {';
162
-
163
- if ( ! empty( $theme_styles['label']['color'] ) ) {
164
- $styles .= 'color:' . $theme_styles['label']['color'] . ';';
165
- }
166
-
167
- if ( ! empty( $theme_styles['label']['background'] ) ) {
168
- $styles .= 'background-color:' . $theme_styles['label']['background'] . ';';
169
- }
170
-
171
- $styles .= '}';
172
- }
173
-
174
- if ( ! empty( $theme_styles['title'] ) ) {
175
- $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-widget-popular-posts-list li .exactmetrics-widget-popular-posts-title {';
176
-
177
- if ( ! empty( $theme_styles['title']['color'] ) ) {
178
- $styles .= 'color:' . $theme_styles['title']['color'] . ';';
179
- }
180
- if ( ! empty( $theme_styles['title']['size'] ) ) {
181
- $styles .= 'font-size:' . $theme_styles['title']['size'] . 'px;';
182
- }
183
-
184
- $styles .= '}';
185
- }
186
-
187
- if ( ! empty( $theme_styles['border'] ) ) {
188
- $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-border {';
189
-
190
- if ( ! empty( $theme_styles['border']['color'] ) ) {
191
- $styles .= 'border-color:' . $theme_styles['border']['color'] . ';';
192
- }
193
-
194
- $styles .= '}';
195
- }
196
- }
197
-
198
- return $styles;
199
- }
200
-
201
- /**
202
- * Check if we should attempt to automatically insert the inline widget.
203
- */
204
- public function maybe_auto_insert() {
205
-
206
- $post_types = $this->post_types;
207
- if ( ! empty( $post_types ) && is_singular( $post_types ) && $this->automatic ) {
208
- add_filter( 'the_content', array( $this, 'add_inline_posts_to_content' ) );
209
- }
210
-
211
- }
212
-
213
- /**
214
- * Insert the widget in the content.
215
- *
216
- * @param string $content The post content.
217
- *
218
- * @return string
219
- */
220
- public function add_inline_posts_to_content( $content ) {
221
-
222
- if ( $this->is_post_excluded() ) {
223
- return $content;
224
- }
225
-
226
- $content .= $this->shortcode_output( array() );
227
-
228
- return $content;
229
- }
230
-
231
- /**
232
- * Check if the selected theme is available with the current license to avoid showing a theme not available.
233
- * Returns the default 'alpha' theme if not available.
234
- *
235
- * @param string $theme Theme slug for which we are checking.
236
- *
237
- * @return string
238
- */
239
- public function is_theme_available( $theme ) {
240
-
241
- $theme_props = $this->get_theme_props( $theme )->get_theme();
242
-
243
- if ( ! empty( $theme_props['level'] ) && 'lite' === $theme_props['level'] ) {
244
- return $theme;
245
- }
246
-
247
- return 'alpha';
248
-
249
- }
250
-
251
- }
252
-
253
- /**
254
- * Get the current class in a function.
255
- *
256
- * @return ExactMetrics_Popular_Posts_Widget Instance of the current class.
257
- */
258
- function ExactMetrics_Popular_Posts_Widget() {
259
- return ExactMetrics_Popular_Posts_Widget::get_instance();
260
- }
261
-
262
- ExactMetrics_Popular_Posts_Widget();
1
+ <?php
2
+ /**
3
+ * Code specific to the widget Popular Posts widget type.
4
+ */
5
+
6
+ /**
7
+ * Class ExactMetrics_Popular_Posts_Widget
8
+ */
9
+ class ExactMetrics_Popular_Posts_Widget extends ExactMetrics_Popular_Posts {
10
+
11
+ /**
12
+ * The instance type. Used for loading specific settings.
13
+ *
14
+ * @var string
15
+ */
16
+ protected $type = 'widget';
17
+
18
+ /**
19
+ * Used to load the setting specific for this class.
20
+ *
21
+ * @var string
22
+ */
23
+ protected $settings_key = 'popular_posts_widget';
24
+
25
+ /**
26
+ * Used for registering the shortcode specific to this class.
27
+ *
28
+ * @var string
29
+ */
30
+ protected $shortcode_key = 'exactmetrics_popular_posts_widget';
31
+
32
+ /**
33
+ * Widget-specific hooks.
34
+ */
35
+ public function hooks() {
36
+ parent::hooks();
37
+
38
+ add_action( 'wp', array( $this, 'maybe_auto_insert' ) );
39
+
40
+ add_action( 'widgets_init', array( $this, 'register_widget' ) );
41
+ }
42
+
43
+
44
+ /**
45
+ * Register Popular Posts widget.
46
+ */
47
+ public function register_widget() {
48
+ register_widget( 'ExactMetrics_Popular_Posts_Widget_Sidebar' );
49
+ }
50
+
51
+ /**
52
+ * Get the rendered HTML for output.
53
+ *
54
+ * @param array $atts These are attributes used to build the specific instance, they can be either shortcode
55
+ * attributes or Gutenberg block props.
56
+ *
57
+ * @return string
58
+ */
59
+ public function get_rendered_html( $atts ) {
60
+
61
+ $theme = $this->theme;
62
+ if ( ! empty( $atts['theme'] ) ) {
63
+ $theme = $atts['theme'];
64
+ }
65
+
66
+ $theme = $this->is_theme_available( $theme );
67
+
68
+ if ( ! empty( $atts['post_count'] ) ) {
69
+ $limit = intval( $atts['post_count'] );
70
+ } else {
71
+ $limit = $this->count;
72
+ }
73
+
74
+ $posts = $this->get_posts_to_display();
75
+
76
+ if ( empty( $posts ) ) {
77
+ return '';
78
+ }
79
+
80
+ if ( 'curated' === $this->sort && apply_filters( 'exactmetrics_popular_posts_widget_curated_shuffle', true ) ) {
81
+ // Randomize the order.
82
+ shuffle( $posts );
83
+ }
84
+
85
+ $theme_styles = $this->get_theme_props( $theme )->get_theme();
86
+
87
+ $label_text = '';
88
+ if ( isset( $theme_styles['styles']['label'] ) ) {
89
+ $label_text = isset( $atts['label_text'] ) ? $atts['label_text'] : $theme_styles['styles']['label']['text'];
90
+ }
91
+
92
+ if ( isset( $atts['widget_title'] ) ) {
93
+ $show_title = (bool) $atts['widget_title'];
94
+ $title_text = empty( $atts['widget_title_text'] ) ? '' : $atts['widget_title_text'];
95
+ } else {
96
+ $show_title = $this->title;
97
+ $title_text = $this->title_text;
98
+ }
99
+
100
+ $html = '<div class="' . $this->get_wrapper_class( $atts ) . '">';
101
+ if ( $show_title ) {
102
+ $html .= '<h2 class="exactmetrics-widget-popular-posts-widget-title">' . wp_kses_post( $title_text ) . '</h2>';
103
+ }
104
+
105
+ $html .= '<ul class="exactmetrics-widget-popular-posts-list">';
106
+
107
+ $display_count = 0;
108
+ foreach ( $posts as $post ) {
109
+ $display_count ++;
110
+ if ( $display_count > $limit ) {
111
+ break;
112
+ }
113
+ $this->set_post_shown( $post['id'] );
114
+ $html .= '<li ' . $this->get_element_style( $theme, 'background', $atts ) . '>';
115
+ $html .= '<a href="' . $post['link'] . '">';
116
+ if ( ! empty( $theme_styles['image'] ) && ! empty( $post['image'] ) ) {
117
+ $html .= '<div class="exactmetrics-widget-popular-posts-image">';
118
+ $html .= '<img src="' . $post['image'] . '" srcset=" ' . $post['srcset'] . ' " alt="' . esc_attr( $post['title'] ) . '" />';
119
+ $html .= '</div>';
120
+ }
121
+ $html .= '<div class="exactmetrics-widget-popular-posts-text">';
122
+ if ( isset( $theme_styles['styles']['label'] ) ) {
123
+ $html .= '<span class="exactmetrics-widget-popular-posts-label" ' . $this->get_element_style( $theme, 'label', $atts ) . '>' . $label_text . '</span>';
124
+ }
125
+ $html .= '<span class="exactmetrics-widget-popular-posts-title" ' . $this->get_element_style( $theme, 'title', $atts ) . '>' . $post['title'] . '</span>';
126
+ $html .= '</div>';// exactmetrics-widget-popular-posts-text.
127
+ $html .= '</a>';
128
+ $html .= '</li>';
129
+ }
130
+
131
+ $html .= '</ul></div><p></p>';// Main div.
132
+
133
+ return $html;
134
+
135
+ }
136
+
137
+ /**
138
+ * Add widget-specific styles based on theme settings.
139
+ */
140
+ public function build_inline_styles() {
141
+
142
+ $themes = $this->get_themes_styles_for_output();
143
+ $styles = '';
144
+
145
+ foreach ( $themes as $theme_key => $theme_styles ) {
146
+
147
+ if ( ! empty( $theme_styles['background'] ) ) {
148
+ $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-widget-popular-posts-list li {';
149
+
150
+ if ( ! empty( $theme_styles['background']['color'] ) ) {
151
+ $styles .= 'background-color:' . $theme_styles['background']['color'] . ';';
152
+ }
153
+ if ( ! empty( $theme_styles['background']['border'] ) ) {
154
+ $styles .= 'border-color:' . $theme_styles['background']['border'] . ';';
155
+ }
156
+
157
+ $styles .= '}';
158
+ }
159
+
160
+ if ( ! empty( $theme_styles['label'] ) ) {
161
+ $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-widget-popular-posts-label {';
162
+
163
+ if ( ! empty( $theme_styles['label']['color'] ) ) {
164
+ $styles .= 'color:' . $theme_styles['label']['color'] . ';';
165
+ }
166
+
167
+ if ( ! empty( $theme_styles['label']['background'] ) ) {
168
+ $styles .= 'background-color:' . $theme_styles['label']['background'] . ';';
169
+ }
170
+
171
+ $styles .= '}';
172
+ }
173
+
174
+ if ( ! empty( $theme_styles['title'] ) ) {
175
+ $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-widget-popular-posts-list li .exactmetrics-widget-popular-posts-title {';
176
+
177
+ if ( ! empty( $theme_styles['title']['color'] ) ) {
178
+ $styles .= 'color:' . $theme_styles['title']['color'] . ';';
179
+ }
180
+ if ( ! empty( $theme_styles['title']['size'] ) ) {
181
+ $styles .= 'font-size:' . $theme_styles['title']['size'] . 'px;';
182
+ }
183
+
184
+ $styles .= '}';
185
+ }
186
+
187
+ if ( ! empty( $theme_styles['border'] ) ) {
188
+ $styles .= '.exactmetrics-popular-posts-styled.exactmetrics-widget-popular-posts-' . $theme_key . ' .exactmetrics-inline-popular-posts-border {';
189
+
190
+ if ( ! empty( $theme_styles['border']['color'] ) ) {
191
+ $styles .= 'border-color:' . $theme_styles['border']['color'] . ';';
192
+ }
193
+
194
+ $styles .= '}';
195
+ }
196
+ }
197
+
198
+ return $styles;
199
+ }
200
+
201
+ /**
202
+ * Check if we should attempt to automatically insert the inline widget.
203
+ */
204
+ public function maybe_auto_insert() {
205
+
206
+ $post_types = $this->post_types;
207
+ if ( ! empty( $post_types ) && is_singular( $post_types ) && $this->automatic ) {
208
+ add_filter( 'the_content', array( $this, 'add_inline_posts_to_content' ) );
209
+ }
210
+
211
+ }
212
+
213
+ /**
214
+ * Insert the widget in the content.
215
+ *
216
+ * @param string $content The post content.
217
+ *
218
+ * @return string
219
+ */
220
+ public function add_inline_posts_to_content( $content ) {
221
+
222
+ if ( $this->is_post_excluded() ) {
223
+ return $content;
224
+ }
225
+
226
+ $content .= $this->shortcode_output( array() );
227
+
228
+ return $content;
229
+ }
230
+
231
+ /**
232
+ * Check if the selected theme is available with the current license to avoid showing a theme not available.
233
+ * Returns the default 'alpha' theme if not available.
234
+ *
235
+ * @param string $theme Theme slug for which we are checking.
236
+ *
237
+ * @return string
238
+ */
239
+ public function is_theme_available( $theme ) {
240
+
241
+ $theme_props = $this->get_theme_props( $theme )->get_theme();
242
+
243
+ if ( ! empty( $theme_props['level'] ) && 'lite' === $theme_props['level'] ) {
244
+ return $theme;
245
+ }
246
+
247
+ return 'alpha';
248
+
249
+ }
250
+
251
+ }
252
+
253
+ /**
254
+ * Get the current class in a function.
255
+ *
256
+ * @return ExactMetrics_Popular_Posts_Widget Instance of the current class.
257
+ */
258
+ function ExactMetrics_Popular_Posts_Widget() {
259
+ return ExactMetrics_Popular_Posts_Widget::get_instance();
260
+ }
261
+
262
+ ExactMetrics_Popular_Posts_Widget();
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://www.wpbeginner.com/wpbeginner-needs-your-help/
4
  Tags: analytics,google analytics,google analytics dashboard,google analytics plugin,google analytics widget,gtag
5
  Requires at least: 4.8.0
6
  Tested up to: 5.8
7
- Stable tag: 7.2.1
8
  Requires PHP: 5.5
9
  License: GPL v3
10
 
@@ -170,7 +170,7 @@ You can translate Google Analytics Dashboard for WP by ExactMetrics on [translat
170
  4. Want more features? <a href="https://www.exactmetrics.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion">Purchase ExactMetrics Pro</a>!
171
 
172
  == Changelog ==
173
- = 7.2.1: November 3, 2021 =
174
  - New In Pro: 7 day traffic Email Summaries
175
  - New In Pro: Compatibility for the Complianz and CookieYes cookie notice plugins.
176
  - Tweak: Cleaned up the outputting of the GA compatibility layer feature.
4
  Tags: analytics,google analytics,google analytics dashboard,google analytics plugin,google analytics widget,gtag
5
  Requires at least: 4.8.0
6
  Tested up to: 5.8
7
+ Stable tag: 7.2.0
8
  Requires PHP: 5.5
9
  License: GPL v3
10
 
170
  4. Want more features? <a href="https://www.exactmetrics.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion">Purchase ExactMetrics Pro</a>!
171
 
172
  == Changelog ==
173
+ = 7.2.0: November 3, 2021 =
174
  - New In Pro: 7 day traffic Email Summaries
175
  - New In Pro: Compatibility for the Complianz and CookieYes cookie notice plugins.
176
  - Tweak: Cleaned up the outputting of the GA compatibility layer feature.