/
MarkupGALite.module
234 lines (205 loc) · 8.79 KB
/
MarkupGALite.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
<?php namespace ProcessWire;
/**
* MarkupGALite
*
* A wrapper module for ga-lite, an unofficial Google Analytics client
* https://github.com/jehna/ga-lite
*
* ProcessWire 3.x, Copyright 2017 by Abdussamet Kocak
* https://abdus.co/
*
* Class MarkupGALite
* @package ProcessWire
* @method renderTrackingCode(array $options, bool $inject) string
*/
class MarkupGALite extends WireData implements Module, ConfigurableModule
{
// place scripts before the end of body
const PLACE_BODY = 1;
// place scripts before the end of head
const PLACE_HEAD = 2;
public static function getModuleInfo()
{
return [
'title' => 'Google Analytics Lite',
'summary' => 'Adds Google Analytics tracking to pages using `ga-lite`',
'version' => '0.0.1',
'author' => 'abdus',
'href' => 'https://abdus.co',
'icon' => 'bar-chart',
'requires' => 'ProcessWire>=3.0',
'autoload' => true // set true to load module on every request
];
}
/**
* MarkupGALite constructor.
* summary Sets default options before they are replaced with user provided values
*/
public function __construct()
{
// let parent set defaults first
parent::__construct();
// then override with our values
$this->set('placement', $this::PLACE_HEAD);
$this->set('trackingId', ''); // empty text field
$this->set('anonymizeIp', 1); // checked checkbox
$this->set('inject', ''); // unchecked checkbox
$this->set('selector', ''); // empty selector
}
/**
* summary Decides if tracking code should be included and sets up hooks
*/
public function ready()
{
// do nothing if tracking ID is empty
if (!$this->trackingId) {
// after login warn superuser about missing tracking ID
$this->addHookAfter('Session::loginSuccess', function (HookEvent $e) {
/* @var $user User */
$user = $e->arguments(0);
if (!$user->isSuperuser()) return;
$this->warning("$this: Tracking ID is not filled, tracking code will not be injected");
});
return;
} // do nothing if it is admin page
else if (strpos($this->page->url, $this->config->urls->admin) === 0) return;
// do nothing if current page does not match the selector.
else if (!empty($this->selector) // and it shouldn't be empty
&& !$this->page->matches($this->selector)
) return;
else {
// Hook into ___render() method in Page class
$this->addHookAfter('Page::render', $this, 'onPageRender');
}
}
/**
* summary Modifies page render output
*
* @param HookEvent $e
*/
protected function onPageRender(HookEvent $e)
{
$markup = $e->return;
// figure out the position to place the script
$pointer = $this->placement === $this::PLACE_BODY ? '</body>' : '</head>';
$pos = strpos($markup, $pointer);
if (!$pos) return;
$options = [
'trackingId' => $this->trackingId,
'anonymizeIp' => !!$this->anonymizeIp // force boolean
];
$code = $this->renderTrackingCode($options, !!$this->inject);
// Place the script before head or body
$e->return = str_replace($pointer, $code . $pointer, $e->return);
}
/**
* summary Builds tracking code
*
* @param array $options tracking ID, and other options for ga-lite
* @param bool $inject inject the contents of tracking scripts
* @return string tracking script
*/
public function ___renderTrackingCode(array $options, $inject = false)
{
$code = "";
$scriptPath = __DIR__ . '/ga-lite/ga-lite.min.js';
if ($inject && file_exists($scriptPath)) {
// inject the script
$js = file_get_contents($scriptPath);
$code .= "<script>$js</script>";
} else {
// link the script
$scriptUrl = $this->config->urls->$this . "ga-lite/ga-lite.min.js";
$code .= "<script src='$scriptUrl'></script>";
}
// we're injecting the configuration in JSON format
$json = json_encode($options);
$code .= "<script>
var galite = galite = {};
(function(){
var conf = $json;
galite.UA = conf.trackingId;
galite.anonymizeIp = conf.anonymizeIp;
})();
</script>";
return $code;
}
/**
* summary Sets up module configuration page
*
* @param InputfieldWrapper $wrapper
* @return InputfieldWrapper
*/
public function getModuleConfigInputfields(InputfieldWrapper $wrapper)
{
// TRACKING ID ====================
/* @var $tracking InputfieldText */
$tracking = $this->modules->InputfieldText;
$tracking->name = 'trackingId';
$tracking->label = $this->_('Tracking ID');
// use previously saved tracking ID, defaults to empty string
$tracking->value = $this->trackingId;
$tracking->description = $this->_('The UA code from your Google Analytics admin panel.');
$tracking->notes = $this->_('See [here](https://support.google.com/analytics/answer/1008080#trackingID) to learn how to set up a tracking ID.');
$tracking->placeholder = 'UA-XXXXX';
$tracking->required = true;
$tracking->columnWidth = 50;
$wrapper->add($tracking);
// ANONYMIZE IP ====================
/* @var $anonymize InputfieldCheckbox */
$anonymize = $this->modules->InputfieldCheckbox;
$anonymize->name = 'anonymizeIp';
$anonymize->label = $this->_('Anonymize IPs');
// use previously saved value, defaults to checked
$anonymize->attr('checked', $this->anonymizeIp);
$anonymize->description = $this->_('Sets the aip flag that advices GA to anonymize the IP address');
$anonymize->notes = $this->_('See here for more [info on `anonymizeIp`](Sets the aip flag that advices GA to anonymize the IP address)');
$anonymize->columnWidth = 50;
$wrapper->add($anonymize);
// =================================
// ADVANCED CONFIGURATION
// =================================
/* @var $advanced InputfieldWrapper */
$advanced = $this->modules->InputfieldFieldset;
$advanced->label = $this->_('Advanced Settings');
$advanced->description = $this->_('These settings are not required');
$advanced->collapsed = Inputfield::collapsedBlank;
// SELECTOR =================
/* @var $selector InputfieldSelector */
$selector = $this->modules->InputfieldSelector;
$selector->name = 'selector';
$selector->label = $this->_('Select pages');
// use previously saved value, defaults to empty string
$selector->value = $this->selector;
$selector->description = $this->_('Build a selector for pages to put the tracking code');
$selector->notes = $this->_('By default tracking code will be injected into every page except admin pages');
$advanced->add($selector);
// INJECT SCRIPT ====================
/* @var $inject InputfieldCheckbox */
$inject = $this->modules->InputfieldCheckbox;
$inject->name = 'inject';
$inject->label = $this->_('Inject script');
$inject->description = $this->_('Write GALite js file directly into HTML, instead of linking it.');
$inject->notes = $this->_("Since the library is less than a KB in size, it won't slow down the page a lot, but it won't be cacheable either.");
// use previously saved value, defaults to unchecked
$inject->attr('checked', $this->inject);
$inject->columnWidth = 50;
$advanced->add($inject);
// PLACEMENT ==================
/* @var $place InputfieldRadios */
$place = $this->modules->InputfieldRadios;
$place->name = 'placement';
$place->label = $this->_('Placement');
$place->description = $this->_('Select whether to place scripts before `</head>` or `</body>`');
$place->notes = $this->_("Placing scripts in `</head>` is recommended, but it's render blocking. But putting it before `</body>` may not detect partial loads for heavy pages.");
$place->addOption($this::PLACE_HEAD, $this->_('Before `</head>`'));
$place->addOption($this::PLACE_BODY, $this->_('Before `</body>`'));
// previously saved value, defaults to </head>
$place->value = $this->placement;
$place->columnWidth = 50;
$advanced->add($place);
// add the container to wrapper
$wrapper->add($advanced);
return $wrapper;
}
}