xref: /plugin/statistics/action.php (revision 04928db47f79a2117caaf6d9ca4114fef94cb8f2)
114d99ec0SAndreas Gohr<?php
2a8acb244SAndreas Gohr
3a8acb244SAndreas Gohruse dokuwiki\Extension\ActionPlugin;
4a8acb244SAndreas Gohruse dokuwiki\Extension\Event;
5*04928db4SAndreas Gohruse dokuwiki\Extension\EventHandler;
6a8acb244SAndreas Gohr
714d99ec0SAndreas Gohr/**
814d99ec0SAndreas Gohr *
914d99ec0SAndreas Gohr * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
1014d99ec0SAndreas Gohr * @author     Andreas Gohr <gohr@cosmocode.de>
1114d99ec0SAndreas Gohr */
12a8acb244SAndreas Gohrclass action_plugin_statistics extends ActionPlugin
13a8acb244SAndreas Gohr{
1414d99ec0SAndreas Gohr    /**
1514d99ec0SAndreas Gohr     * register the eventhandlers and initialize some options
1614d99ec0SAndreas Gohr     */
17a8acb244SAndreas Gohr    public function register(EventHandler $controller)
18a8acb244SAndreas Gohr    {
19eabe0d07SAndreas Gohr        global $JSINFO;
20eabe0d07SAndreas Gohr        global $ACT;
21eabe0d07SAndreas Gohr        $JSINFO['act'] = $ACT;
2214d99ec0SAndreas Gohr
23*04928db4SAndreas Gohr        $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'initSession', []);
2402aa9b73SAndreas Gohr        // FIXME new save event might be better:
252257e39bSAndreas Gohr        $controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'logedits', []);
262257e39bSAndreas Gohr        $controller->register_hook('SEARCH_QUERY_FULLPAGE', 'AFTER', $this, 'logsearch', []);
272257e39bSAndreas Gohr        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'loglogins', []);
282257e39bSAndreas Gohr        $controller->register_hook('AUTH_USER_CHANGE', 'AFTER', $this, 'logregistration', []);
292257e39bSAndreas Gohr        $controller->register_hook('FETCH_MEDIA_STATUS', 'BEFORE', $this, 'logmedia', []);
302257e39bSAndreas Gohr        $controller->register_hook('INDEXER_TASKS_RUN', 'AFTER', $this, 'loghistory', []);
3114d99ec0SAndreas Gohr    }
3214d99ec0SAndreas Gohr
3314d99ec0SAndreas Gohr    /**
34*04928db4SAndreas Gohr     * This ensures we have a session for the statistics plugin
35*04928db4SAndreas Gohr     *
36*04928db4SAndreas Gohr     * We reset this when the user agent changes or the session is too old
37*04928db4SAndreas Gohr     * (15 minutes).
38*04928db4SAndreas Gohr     */
39*04928db4SAndreas Gohr    public function initSession()
40*04928db4SAndreas Gohr    {
41*04928db4SAndreas Gohr        global $INPUT;
42*04928db4SAndreas Gohr
43*04928db4SAndreas Gohr        // load session data
44*04928db4SAndreas Gohr        if (isset($_SESSION[DOKU_COOKIE]['statistics'])) {
45*04928db4SAndreas Gohr            $session = $_SESSION[DOKU_COOKIE]['statistics'];
46*04928db4SAndreas Gohr        } else {
47*04928db4SAndreas Gohr            $session = [];
48*04928db4SAndreas Gohr        }
49*04928db4SAndreas Gohr        // reset if session is too old
50*04928db4SAndreas Gohr        if (time() - ($session['time'] ?? 0) > 60 * 15) {
51*04928db4SAndreas Gohr            $session = [];
52*04928db4SAndreas Gohr        }
53*04928db4SAndreas Gohr        // reset if user agent changed
54*04928db4SAndreas Gohr        if ($INPUT->server->str('HTTP_USER_AGENT') != ($session['user_agent'] ?? '')) {
55*04928db4SAndreas Gohr            $session = [];
56*04928db4SAndreas Gohr        }
57*04928db4SAndreas Gohr
58*04928db4SAndreas Gohr        // update session data
59*04928db4SAndreas Gohr        $session['time'] = time();
60*04928db4SAndreas Gohr        $session['user_agent'] = $INPUT->server->str('HTTP_USER_AGENT');
61*04928db4SAndreas Gohr        $session['uid'] = get_doku_pref('plgstats', bin2hex(random_bytes(16)));
62*04928db4SAndreas Gohr        if (!isset($session['id'])) {
63*04928db4SAndreas Gohr            // generate a new session id if not set
64*04928db4SAndreas Gohr            $session['id'] = bin2hex(random_bytes(16));
65*04928db4SAndreas Gohr        }
66*04928db4SAndreas Gohr
67*04928db4SAndreas Gohr        // store session and cookie data
68*04928db4SAndreas Gohr        $_SESSION[DOKU_COOKIE]['statistics'] = $session;
69*04928db4SAndreas Gohr        set_doku_pref('plgstats', $session['uid']);
70*04928db4SAndreas Gohr    }
71*04928db4SAndreas Gohr
72*04928db4SAndreas Gohr    /**
7314d99ec0SAndreas Gohr     * @fixme call this in the webbug call
7414d99ec0SAndreas Gohr     */
75a8acb244SAndreas Gohr    public function putpixel()
76a8acb244SAndreas Gohr    {
77ed6e7cc1SAndreas Gohr (aider)        global $ID, $INPUT;
7814d99ec0SAndreas Gohr        $url = DOKU_BASE . 'lib/plugins/statistics/log.php?p=' . rawurlencode($ID) .
79ed6e7cc1SAndreas Gohr (aider)            '&amp;r=' . rawurlencode($INPUT->server->str('HTTP_REFERER')) . '&rnd=' . time();
8014d99ec0SAndreas Gohr
812257e39bSAndreas Gohr        echo '<noscript><img alt="" src="' . $url . '" width="1" height="1" /></noscript>';
8214d99ec0SAndreas Gohr    }
8358511ae8SAndreas Gohr
8458511ae8SAndreas Gohr    /**
855bccfe87SAndreas Gohr     * Log page edits actions
8658511ae8SAndreas Gohr     */
87a8acb244SAndreas Gohr    public function logedits(Event $event, $param)
88a8acb244SAndreas Gohr    {
8958511ae8SAndreas Gohr        if ($event->data[3]) return; // no revision
9058511ae8SAndreas Gohr
9158511ae8SAndreas Gohr        if (file_exists($event->data[0][0])) {
9258511ae8SAndreas Gohr            if ($event->data[0][1] == '') {
9358511ae8SAndreas Gohr                $type = 'D';
9458511ae8SAndreas Gohr            } else {
9558511ae8SAndreas Gohr                $type = 'E';
9658511ae8SAndreas Gohr            }
9758511ae8SAndreas Gohr        } else {
9858511ae8SAndreas Gohr            $type = 'C';
9958511ae8SAndreas Gohr        }
1001664ba1dSAndreas Gohr        /** @var helper_plugin_statistics $hlp */
10158511ae8SAndreas Gohr        $hlp = plugin_load('helper', 'statistics');
10202aa9b73SAndreas Gohr        $hlp->getLogger()->logEdit($event->data[1] . ':' . $event->data[2], $type);
10358511ae8SAndreas Gohr    }
1045bccfe87SAndreas Gohr
1055bccfe87SAndreas Gohr    /**
1065bccfe87SAndreas Gohr     * Log internal search
1075bccfe87SAndreas Gohr     */
108a8acb244SAndreas Gohr    public function logsearch(Event $event, $param)
109a8acb244SAndreas Gohr    {
1101664ba1dSAndreas Gohr        /** @var helper_plugin_statistics $hlp */
1115bccfe87SAndreas Gohr        $hlp = plugin_load('helper', 'statistics');
11202aa9b73SAndreas Gohr        $hlp->getLogger()->logSearch($event->data['query'], $event->data['highlight']);
1135bccfe87SAndreas Gohr    }
114b5e880bdSAndreas Gohr
115b5e880bdSAndreas Gohr    /**
116b5e880bdSAndreas Gohr     * Log login/logouts
117b5e880bdSAndreas Gohr     */
118a8acb244SAndreas Gohr    public function loglogins(Event $event, $param)
119a8acb244SAndreas Gohr    {
120ed6e7cc1SAndreas Gohr (aider)        global $INPUT;
121ed6e7cc1SAndreas Gohr (aider)
122b5e880bdSAndreas Gohr        $type = '';
123a5dadbc1SAndreas Gohr        $act = $this->actClean($event->data);
124b5e880bdSAndreas Gohr        if ($act == 'logout') {
125b5e880bdSAndreas Gohr            $type = 'o';
126ed6e7cc1SAndreas Gohr (aider)        } elseif ($INPUT->server->str('REMOTE_USER') && $act == 'login') {
127ed6e7cc1SAndreas Gohr (aider)            if ($INPUT->str('r')) {
128b5e880bdSAndreas Gohr                $type = 'p';
129b5e880bdSAndreas Gohr            } else {
130b5e880bdSAndreas Gohr                $type = 'l';
131b5e880bdSAndreas Gohr            }
132ed6e7cc1SAndreas Gohr (aider)        } elseif ($INPUT->str('u') && !$INPUT->str('http_credentials') && !$INPUT->server->str('REMOTE_USER')) {
133b5e880bdSAndreas Gohr            $type = 'f';
134b5e880bdSAndreas Gohr        }
135b5e880bdSAndreas Gohr        if (!$type) return;
136b5e880bdSAndreas Gohr
1371664ba1dSAndreas Gohr        /** @var helper_plugin_statistics $hlp */
138b5e880bdSAndreas Gohr        $hlp = plugin_load('helper', 'statistics');
139211caa5dSAndreas Gohr        $hlp->getLogger()->logLogin($type);
14014d99ec0SAndreas Gohr    }
14114d99ec0SAndreas Gohr
142535aeea1SAndreas Gohr    /**
143535aeea1SAndreas Gohr     * Log user creations
144535aeea1SAndreas Gohr     */
145a8acb244SAndreas Gohr    public function logregistration(Event $event, $param)
146a8acb244SAndreas Gohr    {
147535aeea1SAndreas Gohr        if ($event->data['type'] == 'create') {
1481664ba1dSAndreas Gohr            /** @var helper_plugin_statistics $hlp */
149535aeea1SAndreas Gohr            $hlp = plugin_load('helper', 'statistics');
150211caa5dSAndreas Gohr            $hlp->getLogger()->logLogin('C', $event->data['params'][0]);
151535aeea1SAndreas Gohr        }
152535aeea1SAndreas Gohr    }
153b5e880bdSAndreas Gohr
154b5e880bdSAndreas Gohr    /**
1551664ba1dSAndreas Gohr     * Log media access
1561664ba1dSAndreas Gohr     */
157a8acb244SAndreas Gohr    public function logmedia(Event $event, $param)
158a8acb244SAndreas Gohr    {
1591664ba1dSAndreas Gohr        if ($event->data['status'] < 200) return;
1601664ba1dSAndreas Gohr        if ($event->data['status'] >= 400) return;
1611664ba1dSAndreas Gohr        if (preg_match('/^\w+:\/\//', $event->data['media'])) return;
1621664ba1dSAndreas Gohr
1631664ba1dSAndreas Gohr        // no size for redirect/not modified
1641664ba1dSAndreas Gohr        if ($event->data['status'] >= 300) {
1651664ba1dSAndreas Gohr            $size = 0;
1661664ba1dSAndreas Gohr        } else {
16717978b38SAndreas Gohr            $size = @filesize($event->data['file']);
1681664ba1dSAndreas Gohr        }
1691664ba1dSAndreas Gohr
1701664ba1dSAndreas Gohr        /** @var helper_plugin_statistics $hlp */
1711664ba1dSAndreas Gohr        $hlp = plugin_load('helper', 'statistics');
172211caa5dSAndreas Gohr        $hlp->getLogger()->logMedia(
1731664ba1dSAndreas Gohr            $event->data['media'],
1741664ba1dSAndreas Gohr            $event->data['mime'],
1751664ba1dSAndreas Gohr            !$event->data['download'],
1761664ba1dSAndreas Gohr            $size
1771664ba1dSAndreas Gohr        );
1781664ba1dSAndreas Gohr    }
1791664ba1dSAndreas Gohr
1801664ba1dSAndreas Gohr    /**
181cae4a1c5SAndreas Gohr     * Log the daily page and media counts for the history
182cae4a1c5SAndreas Gohr     */
183a8acb244SAndreas Gohr    public function loghistory(Event $event, $param)
184a8acb244SAndreas Gohr    {
185cae4a1c5SAndreas Gohr        echo 'Plugin Statistics: started' . DOKU_LF;
186cae4a1c5SAndreas Gohr
187cae4a1c5SAndreas Gohr        /** @var helper_plugin_statistics $hlp */
188cae4a1c5SAndreas Gohr        $hlp = plugin_load('helper', 'statistics');
189a9509e05SAndreas Gohr (aider)        $db = $hlp->getDB();
190cae4a1c5SAndreas Gohr
191cae4a1c5SAndreas Gohr        // check if a history was gathered already today
192a9509e05SAndreas Gohr (aider)        $result = $db->queryAll(
19302aa9b73SAndreas Gohr            "SELECT info FROM history WHERE date(dt) = date('now')"
194a9509e05SAndreas Gohr (aider)        );
195cae4a1c5SAndreas Gohr
196cae4a1c5SAndreas Gohr        $page_ran = false;
197cae4a1c5SAndreas Gohr        $media_ran = false;
198cae4a1c5SAndreas Gohr        foreach ($result as $row) {
199cae4a1c5SAndreas Gohr            if ($row['info'] == 'page_count') $page_ran = true;
200cae4a1c5SAndreas Gohr            if ($row['info'] == 'media_count') $media_ran = true;
201cae4a1c5SAndreas Gohr        }
202cae4a1c5SAndreas Gohr
203cae4a1c5SAndreas Gohr        if ($page_ran && $media_ran) {
204cae4a1c5SAndreas Gohr            echo 'Plugin Statistics: nothing to do - finished' . DOKU_LF;
205cae4a1c5SAndreas Gohr            return;
206cae4a1c5SAndreas Gohr        }
207cae4a1c5SAndreas Gohr
208cae4a1c5SAndreas Gohr        $event->stopPropagation();
209cae4a1c5SAndreas Gohr        $event->preventDefault();
210cae4a1c5SAndreas Gohr
211cae4a1c5SAndreas Gohr        if ($page_ran) {
212cae4a1c5SAndreas Gohr            echo 'Plugin Statistics: logging media' . DOKU_LF;
213211caa5dSAndreas Gohr            $hlp->getLogger()->logHistoryMedia();
214cae4a1c5SAndreas Gohr        } else {
215cae4a1c5SAndreas Gohr            echo 'Plugin Statistics: logging pages' . DOKU_LF;
216211caa5dSAndreas Gohr            $hlp->getLogger()->logHistoryPages();
217cae4a1c5SAndreas Gohr        }
218cae4a1c5SAndreas Gohr        echo 'Plugin Statistics: finished' . DOKU_LF;
219cae4a1c5SAndreas Gohr    }
220cae4a1c5SAndreas Gohr
221cae4a1c5SAndreas Gohr    /**
222b5e880bdSAndreas Gohr     * Pre-Sanitize the action command
223b5e880bdSAndreas Gohr     *
224b5e880bdSAndreas Gohr     * Similar to act_clean in action.php but simplified and without
225b5e880bdSAndreas Gohr     * error messages
226b5e880bdSAndreas Gohr     */
227a5dadbc1SAndreas Gohr    protected function actClean($act)
228a8acb244SAndreas Gohr    {
229b5e880bdSAndreas Gohr        // check if the action was given as array key
230b5e880bdSAndreas Gohr        if (is_array($act)) {
231a8acb244SAndreas Gohr            [$act] = array_keys($act);
232b5e880bdSAndreas Gohr        }
233b5e880bdSAndreas Gohr
234b5e880bdSAndreas Gohr        //remove all bad chars
235b5e880bdSAndreas Gohr        $act = strtolower($act);
236b5e880bdSAndreas Gohr        $act = preg_replace('/[^a-z_]+/', '', $act);
237b5e880bdSAndreas Gohr
238b5e880bdSAndreas Gohr        return $act;
239b5e880bdSAndreas Gohr    }
240b5e880bdSAndreas Gohr}
241