Browse Source

more generic setup for matomo

max.mehl 3 weeks ago
parent
commit
61f1a93e13
No account linked to committer's email address

+ 0
- 1
.gitignore View File

@@ -1,3 +1,2 @@
1 1
 resources/
2 2
 public/
3
-static/proxy.php

+ 4
- 0
config.toml View File

@@ -35,6 +35,10 @@ extensions = [ "hardLineBreak" ]
35 35
   Mastodon      = "mastodon.social/@mxmehl"
36 36
   Diaspora      = "diasp.eu/u/mxmehl"
37 37
 
38
+[params.matomo]
39
+  trackedurl = "//mehl.mx/"
40
+  siteid     = "9"
41
+
38 42
 [params.mastodoncomments]
39 43
   # Link to your Mastodon profile. Please use the format https://<instanceURL>/users/<youruser>
40 44
   user    = "https://mastodon.social/users/mxmehl"

+ 1
- 0
themes/hugo-sustain/.gitignore View File

@@ -0,0 +1 @@
1
+static/config.php

+ 2
- 2
themes/hugo-sustain/layouts/partials/matomo.html View File

@@ -4,9 +4,9 @@
4 4
   _paq.push(['trackPageView']);
5 5
   _paq.push(['enableLinkTracking']);
6 6
   (function() {
7
-    var u="//mehl.mx/";
7
+    var u={{ .Site.Params.matomo.trackedurl }};
8 8
     _paq.push(["setTrackerUrl", u+"piwik.php"]);
9
-    _paq.push(["setSiteId", "9"]);
9
+    _paq.push(["setSiteId", "{{ .Site.Params.matomo.siteid }}"]);
10 10
     var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0];
11 11
     g.type="text/javascript"; g.async=true; g.defer=true; g.src=u+"piwik.php"; s.parentNode.insertBefore(g,s);
12 12
   })();

+ 41
- 0
themes/hugo-sustain/static/config.php.sample View File

@@ -0,0 +1,41 @@
1
+<?php
2
+
3
+// -----
4
+// Important: read the instructions in README.md or at:
5
+// https://github.com/matomo/matomo/tree/master/misc/proxy-hide-matomo-url#matomo-proxy-hide-url
6
+// -----
7
+
8
+// Edit the line below, and replace http://your-matomo-domain.example.org/matomo/
9
+// with your Matomo URL ending with a slash.
10
+// This URL will never be revealed to visitors or search engines.
11
+$PIWIK_URL = 'http://your-matomo-domain.example.org/matomo/';
12
+
13
+// Edit the line below and replace http://your-tracker-proxy.org/ with the URL to your tracker-proxy
14
+// setup. This URL will be used in Matomo output that contains the Matomo URL, so your Matomo is effectively
15
+// hidden.
16
+$PROXY_URL = 'http://your-tracker-proxy.org/';
17
+
18
+// Edit the line below, and replace xyz by the token_auth for the user "UserTrackingAPI"
19
+// which you created when you followed instructions above.
20
+$TOKEN_AUTH = 'xyz';
21
+
22
+// Maximum time, in seconds, to wait for the Matomo server to return the 1*1 GIF
23
+$timeout = 5;
24
+
25
+// By default, the HTTP User Agent will be set to the user agent of the client requesting piwik.php
26
+// Edit the line below to force the proxy to always use a specific user agent string.
27
+$user_agent = '';
28
+
29
+// In some situations the backend takes the sending IP address into account
30
+// which by default is the IP address of the server/service proxy.php is executed from.
31
+// If $http_forward_header is set, the clients IP address is sent over in the
32
+// header field with the given name. An empty string means do not send the header. 
33
+// A common header name is 'X-Forwarded-For'.
34
+//
35
+// In order to work, the http server serving the matomo instance, has to be configured
36
+// to honor the additional header.
37
+//
38
+// For apache http see https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html
39
+// for nginx see https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/
40
+//
41
+$http_ip_forward_header = '';

static/matomo-proxy.php → themes/hugo-sustain/static/matomo-proxy.php View File


static/piwik.php → themes/hugo-sustain/static/piwik.php View File


+ 360
- 0
themes/hugo-sustain/static/proxy.php View File

@@ -0,0 +1,360 @@
1
+<?php
2
+/**
3
+ * Piwik - free/libre analytics platform
4
+ * Piwik Proxy Hide URL
5
+ *
6
+ * @link http://piwik.org/faq/how-to/#faq_132
7
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8
+ */
9
+
10
+if (!defined('MATOMO_PROXY_FROM_ENDPOINT')) {
11
+    exit; // this file is not supposed to be accessed directly
12
+}
13
+
14
+// if set to true, will print out more information about request errors so said errors can be more easily debugged.
15
+$DEBUG_PROXY = false;
16
+
17
+// set to true if the target matomo server has a ssl certificate that will fail verification, like when testing.
18
+$NO_VERIFY_SSL = false;
19
+
20
+if (file_exists(dirname(__FILE__) . '/config.php')) {
21
+    include dirname(__FILE__) . '/config.php';
22
+}
23
+
24
+// -----
25
+// Important: read the instructions in README.md or at:
26
+// https://github.com/piwik/tracker-proxy#piwik-tracker-proxy
27
+// -----
28
+
29
+// Edit the line below, and replace http://your-piwik-domain.example.org/piwik/
30
+// with your Piwik URL ending with a slash.
31
+// This URL will never be revealed to visitors or search engines.
32
+if (! isset($PIWIK_URL)) {
33
+    $PIWIK_URL = 'http://your-piwik-domain.example.org/piwik/';
34
+}
35
+
36
+// Edit the line below, and replace xyz by the token_auth for the user "UserTrackingAPI"
37
+// which you created when you followed instructions above.
38
+if (! isset($TOKEN_AUTH)) {
39
+    $TOKEN_AUTH = 'xyz';
40
+}
41
+
42
+// Maximum time, in seconds, to wait for the Piwik server to return the 1*1 GIF
43
+if (! isset($timeout)) {
44
+    $timeout = 5;
45
+}
46
+
47
+// The HTTP User-Agent to set in the request sent to Piwik Tracking API
48
+if (empty($user_agent)) {
49
+    $user_agent = arrayValue($_SERVER, 'HTTP_USER_AGENT', '');
50
+}
51
+
52
+// -----------------------------
53
+// DO NOT MODIFY BELOW THIS LINE
54
+// -----------------------------
55
+
56
+// the HTTP response headers captured via fopen or curl
57
+$httpResponseHeaders = array();
58
+
59
+// 1) PIWIK.JS PROXY: No _GET parameter, we serve the JS file; or we serve a requested js file
60
+if ((empty($_GET) && empty($_POST)) || (isset($filerequest) && substr($filerequest, -3) === '.js')) {
61
+    $modifiedSince = false;
62
+    if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
63
+        $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
64
+        // strip any trailing data appended to header
65
+        if (false !== ($semicolon = strpos($modifiedSince, ';'))) {
66
+            $modifiedSince = substr($modifiedSince, 0, $semicolon);
67
+        }
68
+        $modifiedSince = strtotime($modifiedSince);
69
+    }
70
+    // Re-download the piwik.js once a day maximum
71
+    $lastModified = time() - 86400;
72
+
73
+    // set HTTP response headers
74
+    sendHeader('Vary: Accept-Encoding');
75
+
76
+    // Returns 304 if not modified since
77
+    if (!empty($modifiedSince) && $modifiedSince > $lastModified) {
78
+        sendHeader(sprintf("%s 304 Not Modified", $_SERVER['SERVER_PROTOCOL']));
79
+    } else {
80
+        sendHeader('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
81
+        sendHeader('Content-Type: application/javascript; charset=UTF-8');
82
+
83
+        // Silent fail: hide Warning in 'piwik.js' response
84
+        if (empty($_GET) && empty($_POST)) {
85
+            list($content, $httpStatus) = getHttpContentAndStatus($PIWIK_URL . 'piwik.js', $timeout, $user_agent);
86
+        } else {
87
+            list($content, $httpStatus) = getHttpContentAndStatus($PIWIK_URL . $filerequest, $timeout, $user_agent);
88
+        }
89
+        if ($piwikJs = $content) {
90
+            echo $piwikJs;
91
+        } else {
92
+            echo '/* there was an error loading piwik.js */';
93
+        }
94
+    }
95
+    exit;
96
+}
97
+@ini_set('magic_quotes_runtime', 0);
98
+
99
+// 2) PIWIK.PHP PROXY: GET parameters found, this is a tracking request, we redirect it to Piwik
100
+if (strpos($path, '?') === false) {
101
+    $path = $path . '?';
102
+}
103
+
104
+$extraQueryParams = array();
105
+if (strpos($path, 'piwik.php') === 0) {
106
+    $extraQueryParams = array(
107
+        'cip' => getVisitIp(),
108
+        'token_auth' => $TOKEN_AUTH,
109
+    );
110
+}
111
+
112
+$url = $PIWIK_URL . $path;
113
+$url .= http_build_query(array_merge($extraQueryParams, $_GET));
114
+
115
+if (version_compare(PHP_VERSION, '5.3.0', '<')) {
116
+
117
+    // PHP 5.2 breaks with the new 204 status code so we force returning the image every time
118
+    list($content, $httpStatus) = getHttpContentAndStatus($url . '&send_image=1', $timeout, $user_agent);
119
+    $content = sanitizeContent($content);
120
+
121
+    forwardHeaders($content);
122
+
123
+    echo $content;
124
+
125
+} else {
126
+    // PHP 5.3 and above
127
+    list($content, $httpStatus) = getHttpContentAndStatus($url, $timeout, $user_agent);
128
+    $content = sanitizeContent($content);
129
+
130
+    forwardHeaders($content);
131
+
132
+    // Forward the HTTP response code
133
+    if (!headers_sent() && !empty($httpStatus)) {
134
+        header($httpStatus);
135
+    }
136
+
137
+    echo $content;
138
+}
139
+
140
+function sanitizeContent($content)
141
+{
142
+    global $TOKEN_AUTH;
143
+    global $PIWIK_URL;
144
+    global $PROXY_URL;
145
+    global $VALID_FILES;
146
+
147
+    $matomoHost = parse_url($PIWIK_URL, PHP_URL_HOST);
148
+    $proxyHost = parse_url($PROXY_URL, PHP_URL_HOST);
149
+
150
+    $content = str_replace($TOKEN_AUTH, '<token>', $content);
151
+    $content = str_replace($PIWIK_URL, $PROXY_URL, $content);
152
+    $content = str_replace($matomoHost, $proxyHost, $content);
153
+
154
+    if(isset($VALID_FILES)) {
155
+        foreach($VALID_FILES as $filepath) {
156
+            // replace file paths to match the proxy and discard cb
157
+            $content = preg_replace('^' . $filepath . '(\?cb\=[a-z0-9]*)?^', $PROXY_URL . 'matomo-proxy.php?file=' . $filepath, $content);
158
+        }
159
+    }
160
+
161
+    return $content;
162
+}
163
+
164
+function forwardHeaders($content)
165
+{
166
+    global $httpResponseHeaders;
167
+
168
+    $headersToForward = array(
169
+        'content-type',
170
+        'access-control-allow-origin',
171
+        'access-control-allow-methods',
172
+        'set-cookie',
173
+    );
174
+
175
+    foreach ($httpResponseHeaders as $header) {
176
+        $parts = explode(':', $header);
177
+        if (empty($parts[0])) {
178
+            continue;
179
+        }
180
+
181
+        $name = trim(strtolower($parts[0]));
182
+        if (in_array($name, $headersToForward)) {
183
+            sendHeader($header);
184
+        }
185
+    }
186
+
187
+    sendHeader('content-length: ' . strlen($content));
188
+}
189
+
190
+function getVisitIp()
191
+{
192
+    $ipKeys = array(
193
+        'HTTP_X_FORWARDED_FOR',
194
+        'HTTP_CLIENT_IP',
195
+        'HTTP_CF_CONNECTING_IP',
196
+    );
197
+    foreach($ipKeys as $ipKey) {
198
+        if (isset($_SERVER[$ipKey])
199
+            && filter_var($_SERVER[$ipKey], FILTER_VALIDATE_IP) !== false
200
+        ) {
201
+            return $_SERVER[$ipKey];
202
+        }
203
+    }
204
+    return arrayValue($_SERVER, 'REMOTE_ADDR');
205
+}
206
+
207
+function transformHeaderLine($headerLine)
208
+{
209
+    // if we're not on an https protocol, make sure cookies do not have 'secure;'
210
+    if (empty($_SERVER['HTTPS']) && preg_match('/^set-cookie:/i', $headerLine)) {
211
+        $headerLine = str_replace('secure;', '', $headerLine);
212
+    }
213
+    return $headerLine;
214
+}
215
+
216
+// captures a header line when using a curl request. would be better to use an anonymous function, but that would break
217
+// PHP 5.2 support.
218
+function handleHeaderLine($curl, $headerLine)
219
+{
220
+    global $httpResponseHeaders;
221
+
222
+    $originalByteCount = strlen($headerLine);
223
+
224
+    $headerLine = transformHeaderLine($headerLine);
225
+    $httpResponseHeaders[] = trim($headerLine);
226
+
227
+    return $originalByteCount;
228
+}
229
+
230
+function getHttpContentAndStatus($url, $timeout, $user_agent)
231
+{
232
+    global $httpResponseHeaders;
233
+    global $DEBUG_PROXY;
234
+    global $NO_VERIFY_SSL;
235
+    global $http_ip_forward_header;
236
+
237
+    $useFopen = @ini_get('allow_url_fopen') == '1';
238
+
239
+    $header = array();
240
+    $header[] = sprintf("Accept-Language: %s", str_replace(array("\n", "\t", "\r"), "", arrayValue($_SERVER, 'HTTP_ACCEPT_LANGUAGE', '')));
241
+
242
+    // NOTE: any changes made to Piwik\Plugins\PrivacyManager\DoNotTrackHeaderChecker must be made here as well
243
+    if((isset($_SERVER['HTTP_X_DO_NOT_TRACK']) && $_SERVER['HTTP_X_DO_NOT_TRACK'] === '1')) {
244
+        $header[] = "X-Do-Not-Track: 1";
245
+    }
246
+
247
+    if((isset($_SERVER['HTTP_DNT']) && substr($_SERVER['HTTP_DNT'], 0, 1) === '1')) {
248
+        $header[] = "DNT: 1";
249
+    }
250
+
251
+    if (isset($_SERVER['HTTP_COOKIE'])) {
252
+        $header[] = "Cookie: " . $_SERVER['HTTP_COOKIE'];
253
+    }
254
+
255
+    $stream_options = array(
256
+        'http' => array(
257
+            'user_agent' => $user_agent,
258
+            'header'     => $header,
259
+            'timeout'    => $timeout,
260
+        ),
261
+    );
262
+
263
+    if ($DEBUG_PROXY) {
264
+        $stream_options['http']['ignore_errors'] = true;
265
+    }
266
+
267
+    if ($NO_VERIFY_SSL) {
268
+        $stream_options['ssl'] = array(
269
+            'verify_peer' => false,
270
+            'verify_peer_name' => false,
271
+        );
272
+    }
273
+
274
+    // if there's POST data, send our proxy request as a POST
275
+    if (!empty($_POST)) {
276
+        $postBody = file_get_contents("php://input");
277
+
278
+        $stream_options['http']['method'] = 'POST';
279
+        $stream_options['http']['header'][] = "Content-type: application/x-www-form-urlencoded";
280
+        $stream_options['http']['header'][] = "Content-Length: " . strlen($postBody);
281
+        $stream_options['http']['content'] = $postBody;
282
+        
283
+        if(!empty($http_ip_forward_header)) {
284
+            $visitIp = getVisitIp();
285
+            $stream_options['http']['header'][] = "$http_ip_forward_header: $visitIp";
286
+        }
287
+    }
288
+
289
+    if($useFopen) {
290
+        $ctx = stream_context_create($stream_options);
291
+
292
+        if ($DEBUG_PROXY) {
293
+            $content = file_get_contents($url, 0, $ctx);
294
+        } else {
295
+            $content = @file_get_contents($url, 0, $ctx);
296
+        }
297
+
298
+        $httpStatus = '';
299
+        if (isset($http_response_header[0])) {
300
+            $httpStatus = $http_response_header[0];
301
+            $httpResponseHeaders = array_slice($http_response_header, 1);
302
+            $httpResponseHeaders = array_map('transformHeaderLine', $httpResponseHeaders);
303
+        }
304
+    } else {
305
+        if(!function_exists('curl_init')) {
306
+            throw new Exception("You must either set allow_url_fopen=1 in your PHP configuration, or enable the PHP Curl extension.");
307
+        }
308
+
309
+        $ch = curl_init();
310
+        curl_setopt($ch, CURLOPT_HEADER, 0);
311
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
312
+        curl_setopt($ch, CURLOPT_USERAGENT, $stream_options['http']['user_agent']);
313
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $stream_options['http']['header']);
314
+        curl_setopt($ch, CURLOPT_TIMEOUT, $stream_options['http']['timeout']);
315
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $stream_options['http']['timeout']);
316
+        curl_setopt($ch, CURLOPT_URL, $url);
317
+        curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'handleHeaderLine');
318
+
319
+        if (!empty($stream_options['http']['method'])
320
+            && $stream_options['http']['method'] == 'POST'
321
+        ) {
322
+            curl_setopt($ch, CURLOPT_POST, 1);
323
+            curl_setopt($ch, CURLOPT_POSTFIELDS, $stream_options['http']['content']);
324
+        }
325
+
326
+        if (isset($stream_options['ssl']['verify_peer']) && $stream_options['ssl']['verify_peer'] == false) {
327
+            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
328
+        }
329
+
330
+        if (isset($stream_options['ssl']['verify_peer_name']) && $stream_options['ssl']['verify_peer'] == false) {
331
+            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
332
+        }
333
+
334
+        $content = curl_exec($ch);
335
+        $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
336
+        if(!empty($httpStatus)) {
337
+            $httpStatus = 'HTTP/1.1 ' . $httpStatus;
338
+        }
339
+        curl_close($ch);
340
+    }
341
+
342
+    return array(
343
+        $content,
344
+        $httpStatus,
345
+    );
346
+
347
+}
348
+
349
+function sendHeader($header, $replace = true)
350
+{
351
+    headers_sent() || header($header, $replace);
352
+}
353
+
354
+function arrayValue($array, $key, $value = null)
355
+{
356
+    if (!empty($array[$key])) {
357
+        $value = $array[$key];
358
+    }
359
+    return $value;
360
+}

Loading…
Cancel
Save