From df74cb23c086005e9aa699b29de9fc7fe231322b Mon Sep 17 00:00:00 2001 From: Gerrit Linnemann Date: Sun, 24 Mar 2019 17:26:26 +0100 Subject: [PATCH] Matomo tracking --- html/Dein.Equipment/app/mailer.js | 10 +- .../node_modules/matomo-tracker/LICENSE | 23 +++ .../node_modules/matomo-tracker/README.md | 70 +++++++++ .../node_modules/matomo-tracker/index.js | 145 ++++++++++++++++++ .../node_modules/matomo-tracker/package.json | 108 +++++++++++++ html/Dein.Equipment/package.json | 1 + .../public/javascripts/actions.js | 13 ++ html/Dein.Equipment/routes/index.js | 42 +++-- html/Dein.Equipment/views/detail.pug | 7 +- html/Dein.Equipment/views/layout.pug | 15 ++ 10 files changed, 415 insertions(+), 19 deletions(-) create mode 100644 html/Dein.Equipment/node_modules/matomo-tracker/LICENSE create mode 100644 html/Dein.Equipment/node_modules/matomo-tracker/README.md create mode 100644 html/Dein.Equipment/node_modules/matomo-tracker/index.js create mode 100644 html/Dein.Equipment/node_modules/matomo-tracker/package.json diff --git a/html/Dein.Equipment/app/mailer.js b/html/Dein.Equipment/app/mailer.js index 6a8e7af..eb093d7 100644 --- a/html/Dein.Equipment/app/mailer.js +++ b/html/Dein.Equipment/app/mailer.js @@ -28,11 +28,11 @@ exports.mail = function(data) { // setup email data with unicode symbols let mailOptions = { - from: extractFrom(data), // sender address - to: app.get('CONF').mail.request.to, // comma separated list of receivers - subject: "Equipment Anfrage", // Subject line - text: "Hello world?", // plain text body - html: "Hello world?" // html body + from: extractFrom(data), // sender address + to: app.get('CONF').mail.request.to, // comma separated list of receivers + subject: "Equipment Anfrage: " + data.title, // Subject line + text: JSON.stringify(data), // plain text body + html: JSON.stringify(data) // html body }; let info = transporter.sendMail(mailOptions) diff --git a/html/Dein.Equipment/node_modules/matomo-tracker/LICENSE b/html/Dein.Equipment/node_modules/matomo-tracker/LICENSE new file mode 100644 index 0000000..6bdf01f --- /dev/null +++ b/html/Dein.Equipment/node_modules/matomo-tracker/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright 2014-2017 Frederic Hemberger +Copyright 2018 Frederic Hemberger, Matomo Team + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/html/Dein.Equipment/node_modules/matomo-tracker/README.md b/html/Dein.Equipment/node_modules/matomo-tracker/README.md new file mode 100644 index 0000000..8a5b108 --- /dev/null +++ b/html/Dein.Equipment/node_modules/matomo-tracker/README.md @@ -0,0 +1,70 @@ +# Matomo Tracker [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] + +> A wrapper for the Matomo Tracking HTTP API + +## Usage + +First, install `matomo-tracker` as a dependency: + +```shell +npm install --save matomo-tracker +``` + +Then, use it in your project: + +```javascript +var MatomoTracker = require('matomo-tracker'); + +// Initialize with your site ID and Matomo URL +var matomo = new MatomoTracker(1, 'http://mywebsite.com/matomo.php'); + +// Optional: Respond to tracking errors +matomo.on('error', function(err) { + console.log('error tracking request: ', err); +}); + +// Track a request URL: +// Either as a simple string … +matomo.track('http://example.com/track/this/url'); + +// … or provide further options: +matomo.track({ + url: 'http://example.com/track/this/url', + action_name: 'This will be shown in your dashboard', + ua: 'Node.js v0.10.24', + cvar: JSON.stringify({ + '1': ['custom variable name', 'custom variable value'] + }) +}); + +// … or trackBulk: +var events = [{ + '_id': 'AA814767-7B1F-5C81-8F1D-8E47AD7D2982', + 'cdt': '2018-03-22T02:32:22.867Z', + 'e_c': 'Buy', + 'e_a': 'rightButton', + 'e_v': '2' +},{ + '_id': 'AA814767-7B1F-5C81-8F1D-8E47AD7D2982', + 'cdt': '2018-03-22T02:33:52.962Z', + 'e_c': 'Buy', + 'e_a': 'leftButton', + 'e_v': '4' +}]; +matomo.trackBulk(events, (resData) => { + // done. +}) +``` + +That's it. For a complete list of options, see [Matomo's Tracking HTTP API Reference](https://developer.matomo.org/api-reference/tracking-api). + + +## License + +[MIT License](http://en.wikipedia.org/wiki/MIT_License) + +[npm-url]: https://npmjs.org/package/matomo-tracker +[npm-image]: https://img.shields.io/npm/v/matomo-tracker.svg + +[travis-url]: https://travis-ci.org/matomo-org/matomo-nodejs-tracker +[travis-image]: https://img.shields.io/travis/matomo-org/matomo-nodejs-tracker.svg diff --git a/html/Dein.Equipment/node_modules/matomo-tracker/index.js b/html/Dein.Equipment/node_modules/matomo-tracker/index.js new file mode 100644 index 0000000..7262140 --- /dev/null +++ b/html/Dein.Equipment/node_modules/matomo-tracker/index.js @@ -0,0 +1,145 @@ +/** + * A Node.js wrapper for the Matomo (http://matomo.org) tracking HTTP API + * https://github.com/matomo-org/matomo-tracker + * + * @author Frederic Hemberger, Matomo Team + * @license MIT + */ + +'use strict'; + +const assert = require('assert'); +const events = require('events'); +const util = require('util'); +const qs = require('querystring'); +const url = require('url'); + + +/** + * @constructor + * @param {Number} siteId Id of the site you want to track + * @param {String} trackerUrl URL of your Matomo instance + * @param {Boolean} [true] noURLValidation Set to true if the `piwik.php` has been renamed + */ +function MatomoTracker (siteId, trackerUrl, noURLValidation) { + if (!(this instanceof MatomoTracker)) { + return new MatomoTracker(siteId, trackerUrl, noURLValidation); + } + events.EventEmitter.call(this); + + assert.ok(siteId && (typeof siteId === 'number' || typeof siteId === 'string'), 'Matomo siteId required.'); + assert.ok(trackerUrl && typeof trackerUrl === 'string', 'Matomo tracker URL required, e.g. http://example.com/matomo.php'); + if (!noURLValidation) { + assert.ok(trackerUrl.endsWith('matomo.php') || trackerUrl.endsWith('piwik.php'), 'A tracker URL must end with "matomo.php" or "piwik.php"'); + } + + this.siteId = siteId; + this.trackerUrl = trackerUrl; + + // Use either HTTPS or HTTP agent according to Matomo tracker URL + this.agent = require(trackerUrl.startsWith('https') ? 'https' : 'http'); +} + +util.inherits(MatomoTracker, events.EventEmitter); + + +/** + * Executes the call to the Matomo tracking API + * + * For a list of tracking option parameters see + * https://developer.matomo.org/api-reference/tracking-api + * + * @param {(String|Object)} options URL to track or options (must contain URL as well) + */ +MatomoTracker.prototype.track = function track (options) { + var hasErrorListeners = this.listeners('error').length; + + if (typeof options === 'string') { + options = { + url: options + }; + } + + // Set mandatory options + options = options || {}; + options.idsite = this.siteId; + options.rec = 1; + + assert.ok(options.url, 'URL to be tracked must be specified.'); + + var requestUrl = this.trackerUrl + '?' + qs.stringify(options); + var self = this; + var req = this.agent.get(requestUrl, function (res) { + // Check HTTP statuscode for 200 and 30x + if (!/^(200|30[12478])$/.test(res.statusCode)) { + if (hasErrorListeners) { + self.emit('error', res.statusCode); + } + } + }); + + req.on('error', function (err) { + hasErrorListeners && self.emit('error', err.message); + }); + + req.end(); +}; + + +MatomoTracker.prototype.trackBulk = function trackBulk (events, callback) { + var hasErrorListeners = this.listeners('error').length; + + assert.ok(events && (events.length > 0), 'Events require at least one.'); + assert.ok(this.siteId !== undefined && this.siteId !== null, 'siteId must be specified.'); + + var body = JSON.stringify({ + requests: events.map(query => { + query.idsite = this.siteId; + query.rec = 1; + return '?' + qs.stringify(query); + }) + }); + + var uri = url.parse(this.trackerUrl); + + const requestOptions = { + protocol: uri.protocol, + hostname: uri.hostname, + port: uri.port, + path: uri.path, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(body), + } + }; + + const req = this.agent.request(requestOptions, res => { + if (!/^(200|30[12478])$/.test(res.statusCode)) { + if (hasErrorListeners) { + this.emit('error', res.statusCode); + } + } + + let data = []; + + res.on('data', chunk => { + data.push(chunk); + }); + + res.on('end', () => { + data = Buffer.concat(data).toString(); + typeof callback === 'function' && callback(data); + }); + }); + + req.on('error', (err) => { + hasErrorListeners && this.emit('error', err.message); + } + ); + + req.write(body); + req.end(); +}; + +module.exports = MatomoTracker; diff --git a/html/Dein.Equipment/node_modules/matomo-tracker/package.json b/html/Dein.Equipment/node_modules/matomo-tracker/package.json new file mode 100644 index 0000000..278bd77 --- /dev/null +++ b/html/Dein.Equipment/node_modules/matomo-tracker/package.json @@ -0,0 +1,108 @@ +{ + "_args": [ + [ + { + "raw": "matomo-tracker", + "scope": null, + "escapedName": "matomo-tracker", + "name": "matomo-tracker", + "rawSpec": "", + "spec": "latest", + "type": "tag" + }, + "/Users/gerrit/Documents/dev/nodejs/Dein.Equipment/html/Dein.Equipment" + ] + ], + "_from": "matomo-tracker@latest", + "_id": "matomo-tracker@2.2.0", + "_inCache": true, + "_location": "/matomo-tracker", + "_nodeVersion": "9.11.1", + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/matomo-tracker_2.2.0_1523541623256_0.5041344136923325" + }, + "_npmUser": { + "name": "lw1", + "email": "npm@lw1.at" + }, + "_npmVersion": "5.8.0", + "_phantomChildren": {}, + "_requested": { + "raw": "matomo-tracker", + "scope": null, + "escapedName": "matomo-tracker", + "name": "matomo-tracker", + "rawSpec": "", + "spec": "latest", + "type": "tag" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/matomo-tracker/-/matomo-tracker-2.2.0.tgz", + "_shasum": "f1064977a7e96e6737f213bf8503b5390aaccfe3", + "_shrinkwrap": null, + "_spec": "matomo-tracker", + "_where": "/Users/gerrit/Documents/dev/nodejs/Dein.Equipment/html/Dein.Equipment", + "author": { + "name": "Frederic Hemberger", + "url": "https://github.com/matomo-org" + }, + "bugs": { + "url": "https://github.com/matomo-org/matomo-tracker/issues" + }, + "dependencies": {}, + "description": "A wrapper for the Matomo tracking HTTP API", + "devDependencies": { + "chai": "^4.1.2", + "eslint": "^4.19.1", + "mocha": "^5.0.5", + "nock": "^9.2.5", + "sinon": "^4.5.0", + "sinon-chai": "^3.0.0" + }, + "directories": {}, + "dist": { + "integrity": "sha512-kZIN4zRmuWRyG4S42kkMawMcsT6BQ/FYPK8IjeLNXAbN/CwPSlxlt7SdSyK/5k8F+S8nlmjXeUXf5SNFAXi8dA==", + "shasum": "f1064977a7e96e6737f213bf8503b5390aaccfe3", + "tarball": "https://registry.npmjs.org/matomo-tracker/-/matomo-tracker-2.2.0.tgz", + "fileCount": 4, + "unpackedSize": 7876 + }, + "engines": { + "node": ">=4.2.0" + }, + "files": [ + "index.js" + ], + "gitHead": "ed888ba22fc542dccd95149940edc5e7ef203bbf", + "homepage": "https://github.com/matomo-org/matomo-tracker", + "keywords": [ + "piwik", + "matomo", + "analytics", + "tracking" + ], + "license": "MIT", + "maintainers": [ + { + "name": "lw1", + "email": "npm@lw1.at" + } + ], + "name": "matomo-tracker", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git://github.com/matomo-org/matomo-nodejs-tracker.git" + }, + "scripts": { + "eslint": "eslint --max-warnings=0 index.js test/index.test.js", + "mocha": "mocha --reporter spec --exit", + "test": "npm run mocha && npm run eslint" + }, + "version": "2.2.0" +} diff --git a/html/Dein.Equipment/package.json b/html/Dein.Equipment/package.json index 6c36205..0d4e5e7 100644 --- a/html/Dein.Equipment/package.json +++ b/html/Dein.Equipment/package.json @@ -18,6 +18,7 @@ "http-errors": "~1.6.2", "jsonlint": "^1.6.3", "less-middleware": "~2.2.1", + "matomo-tracker": "^2.2.0", "morgan": "~1.9.0", "mysql": "^2.16.0", "nodemailer": "^5.1.1", diff --git a/html/Dein.Equipment/public/javascripts/actions.js b/html/Dein.Equipment/public/javascripts/actions.js index fbf36a4..e41980b 100644 --- a/html/Dein.Equipment/public/javascripts/actions.js +++ b/html/Dein.Equipment/public/javascripts/actions.js @@ -4,6 +4,7 @@ $(function() { initCookieInfo(); initCookieAcceptButtonAction(); initTooltips(); + initRequestForm(); }); @@ -51,4 +52,16 @@ function initCookieAcceptButtonAction() { localStorage.setItem('Dein.Equipment.Cookie.Accepted', true); $section.remove(); }); +} + +function initRequestForm() { + var $form = $('form.request'); + var action = $form.attr('data-action-replacement'); + var $submitBtn = $form.find('button[type="submit"]:first'); + var buttonLabel = $form.attr('data-buttonlabel-replacement'); + + setTimeout(function() { + $form.attr('action', action); + $submitBtn.html(buttonLabel); + }, 3000); } \ No newline at end of file diff --git a/html/Dein.Equipment/routes/index.js b/html/Dein.Equipment/routes/index.js index 496b3e5..45dcb8f 100644 --- a/html/Dein.Equipment/routes/index.js +++ b/html/Dein.Equipment/routes/index.js @@ -1,5 +1,6 @@ -var express = require('express'); -var router = express.Router(); +var express = require('express'); +var MatomoTracker = require('matomo-tracker'); +var router = express.Router(); /* GET home page */ router.get('/', function(req, res, next) { @@ -93,19 +94,36 @@ router.post('/request/', function(req, res, next) { var app = req.app var mailer = app.get('MAILER') - const paramStart = req.body.start - const paramEnd = req.body.end - const paramName = req.body.name - const paramContact = req.body.contact + const params = { + id: req.body.itemID, + title: req.body.itemTitle, + start: req.body.start, + end: req.body.end, + name: req.body.name, + contact: req.body.contact + } - mailer.mail({ - name: paramName, - contact: paramContact, - start: paramStart, - end: paramEnd - }) + mailer.mail(params) + + var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl; + trackAtMatomo(app, fullUrl, 'Anfrage', params) res.render('request', { title: 'Dein.Equipment: Anfrage gesendet' }) }) +function trackAtMatomo(app, url, actionName, data) { + var matomo = new MatomoTracker(app.get('CONF').matomo.site.id, app.get('CONF').matomo.url) + + matomo.on('error', function(err) { + app.get('LOG').error('Router | Error tracking request: ' + err); + }) + + matomo.track({ + url: url, + action_name: actionName, // Action, shown on dashboard + ua: 'Dein.Equipment Backend', // UserAgend + data: JSON.stringify(data) + }) +} + module.exports = router; diff --git a/html/Dein.Equipment/views/detail.pug b/html/Dein.Equipment/views/detail.pug index 9f47604..38809ed 100644 --- a/html/Dein.Equipment/views/detail.pug +++ b/html/Dein.Equipment/views/detail.pug @@ -19,7 +19,10 @@ block content input(type="hidden", value=item.unavailableDates) .calendar .col-sm - form.request(action="/request/", method="post") + form.request(action="/fuckyoubot/", method="post", data-action-replacement="/request/", data-buttonlabel-replacement="Anfrage senden") + input(type="hidden", name="itemID", value=item.id) + input(type="hidden", name="itemTitle", value=item.title) + h5 #{item.title} anfragen .form-group .input-group @@ -47,4 +50,4 @@ block content input(type="text", class="input-sm form-control", name="contact", placeholder="E-Mail oder Telefonnummer", data-toggle="tooltip", data-placement="top", title="Wie möchtest du von uns Antwort erhalten? Nenn uns deine Telefonnummer, oder deine E-Mail-Adresse.") .form-group .input-group - button(type="submit", class="btn btn-primary btn-block", value="request") Anfrage \ No newline at end of file + button(type="submit", class="btn btn-primary btn-block", value="request") Bitte warten ... \ No newline at end of file diff --git a/html/Dein.Equipment/views/layout.pug b/html/Dein.Equipment/views/layout.pug index 2af8f94..eb0aca9 100644 --- a/html/Dein.Equipment/views/layout.pug +++ b/html/Dein.Equipment/views/layout.pug @@ -40,3 +40,18 @@ html script(src="/javascripts/moment.min.js", crossorigin="anonymous") script(src="/javascripts/calendar.js", crossorigin="anonymous") script(src="/javascripts/actions.js", crossorigin="anonymous") + + script. + // Matomo + var _paq = window._paq || []; + /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ + _paq.push(['trackPageView']); + _paq.push(['enableLinkTracking']); + (function() { + var u="//matomo.adawim.com/"; + _paq.push(['setTrackerUrl', u+'matomo.php']); + _paq.push(['setSiteId', '2']); + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); + })(); + // End Matomo Code \ No newline at end of file