xref: /plugin/statistics/action.php (revision af93d154494af2cfbf5f1d99e260a56c69ba7152)
1<?php
2
3use dokuwiki\Extension\ActionPlugin;
4use dokuwiki\Extension\Event;
5use dokuwiki\Extension\EventHandler;
6
7/**
8 *
9 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 * @author     Andreas Gohr <gohr@cosmocode.de>
11 */
12class action_plugin_statistics extends ActionPlugin
13{
14    /**
15     * register the eventhandlers and initialize some options
16     */
17    public function register(EventHandler $controller)
18    {
19        global $JSINFO;
20        global $ACT;
21        $JSINFO['act'] = $ACT;
22
23        $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'initSession', []);
24        // FIXME new save event might be better:
25        $controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'logedits', []);
26        $controller->register_hook('SEARCH_QUERY_FULLPAGE', 'AFTER', $this, 'logsearch', []);
27        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'loglogins', []);
28        $controller->register_hook('AUTH_USER_CHANGE', 'AFTER', $this, 'logregistration', []);
29        $controller->register_hook('FETCH_MEDIA_STATUS', 'BEFORE', $this, 'logmedia', []);
30        $controller->register_hook('INDEXER_TASKS_RUN', 'AFTER', $this, 'loghistory', []);
31    }
32
33    /**
34     * This ensures we have a session for the statistics plugin
35     *
36     * We reset this when the user agent changes or the session is too old
37     * (15 minutes).
38     */
39    public function initSession()
40    {
41        global $INPUT;
42
43        // load session data
44        if (isset($_SESSION[DOKU_COOKIE]['statistics'])) {
45            $session = $_SESSION[DOKU_COOKIE]['statistics'];
46        } else {
47            $session = [];
48        }
49        // reset if session is too old
50        if (time() - ($session['time'] ?? 0) > 60 * 15) {
51            $session = [];
52        }
53        // reset if user agent changed
54        if ($INPUT->server->str('HTTP_USER_AGENT') != ($session['user_agent'] ?? '')) {
55            $session = [];
56        }
57
58        // update session data
59        $session['time'] = time();
60        $session['user_agent'] = $INPUT->server->str('HTTP_USER_AGENT');
61        $session['uid'] = get_doku_pref('plgstats', bin2hex(random_bytes(16)));
62        if (!isset($session['id'])) {
63            // generate a new session id if not set
64            $session['id'] = bin2hex(random_bytes(16));
65        }
66
67        // store session and cookie data
68        $_SESSION[DOKU_COOKIE]['statistics'] = $session;
69        set_doku_pref('plgstats', $session['uid']);
70    }
71
72    /**
73     * @fixme call this in the webbug call
74     */
75    public function putpixel()
76    {
77        global $ID, $INPUT;
78        $url = DOKU_BASE . 'lib/plugins/statistics/log.php?p=' . rawurlencode($ID) .
79            '&amp;r=' . rawurlencode($INPUT->server->str('HTTP_REFERER')) . '&rnd=' . time();
80
81        echo '<noscript><img alt="" src="' . $url . '" width="1" height="1" /></noscript>';
82    }
83
84    /**
85     * Log page edits actions
86     */
87    public function logedits(Event $event, $param)
88    {
89        if ($event->data[3]) return; // no revision
90
91        if (file_exists($event->data[0][0])) {
92            if ($event->data[0][1] == '') {
93                $type = 'D';
94            } else {
95                $type = 'E';
96            }
97        } else {
98            $type = 'C';
99        }
100        /** @var helper_plugin_statistics $hlp */
101        $hlp = plugin_load('helper', 'statistics');
102        $hlp->getLogger()->logEdit($event->data[1] . ':' . $event->data[2], $type);
103    }
104
105    /**
106     * Log internal search
107     */
108    public function logsearch(Event $event, $param)
109    {
110        /** @var helper_plugin_statistics $hlp */
111        $hlp = plugin_load('helper', 'statistics');
112        $hlp->getLogger()->logSearch($event->data['query'], $event->data['highlight']);
113    }
114
115    /**
116     * Log login/logouts
117     */
118    public function loglogins(Event $event, $param)
119    {
120        global $INPUT;
121
122        $type = '';
123        $act = $this->actClean($event->data);
124        $user = $INPUT->server->str('REMOTE_USER');
125        if ($act == 'logout') {
126            // logout
127            $type = 'o';
128        } elseif ($INPUT->server->str('REMOTE_USER') && $act == 'login') {
129            if ($INPUT->str('r')) {
130                // permanent login
131                $type = 'p';
132            } else {
133                // normal login
134                $type = 'l';
135            }
136        } elseif ($INPUT->str('u') && !$INPUT->str('http_credentials') && !$INPUT->server->str('REMOTE_USER')) {
137            // failed attempt
138            $user = $INPUT->str('u');
139            $type = 'f';
140        }
141        if (!$type) return;
142
143        /** @var helper_plugin_statistics $hlp */
144        $hlp = plugin_load('helper', 'statistics');
145        $hlp->getLogger()->logLogin($type, $user);
146    }
147
148    /**
149     * Log user creations
150     */
151    public function logregistration(Event $event, $param)
152    {
153        if ($event->data['type'] == 'create') {
154            /** @var helper_plugin_statistics $hlp */
155            $hlp = plugin_load('helper', 'statistics');
156            $hlp->getLogger()->logLogin('C', $event->data['params'][0]);
157        }
158    }
159
160    /**
161     * Log media access
162     */
163    public function logmedia(Event $event, $param)
164    {
165        if ($event->data['status'] < 200) return;
166        if ($event->data['status'] >= 400) return;
167        if (preg_match('/^\w+:\/\//', $event->data['media'])) return;
168
169        // no size for redirect/not modified
170        if ($event->data['status'] >= 300) {
171            $size = 0;
172        } else {
173            $size = @filesize($event->data['file']);
174        }
175
176        /** @var helper_plugin_statistics $hlp */
177        $hlp = plugin_load('helper', 'statistics');
178        $hlp->getLogger()->logMedia(
179            $event->data['media'],
180            $event->data['mime'],
181            !$event->data['download'],
182            $size
183        );
184    }
185
186    /**
187     * Log the daily page and media counts for the history
188     */
189    public function loghistory(Event $event, $param)
190    {
191        echo 'Plugin Statistics: started' . DOKU_LF;
192
193        /** @var helper_plugin_statistics $hlp */
194        $hlp = plugin_load('helper', 'statistics');
195        $db = $hlp->getDB();
196
197        // check if a history was gathered already today
198        $result = $db->queryAll(
199            "SELECT info FROM history WHERE date(dt) = date('now')"
200        );
201
202        $page_ran = false;
203        $media_ran = false;
204        foreach ($result as $row) {
205            if ($row['info'] == 'page_count') $page_ran = true;
206            if ($row['info'] == 'media_count') $media_ran = true;
207        }
208
209        if ($page_ran && $media_ran) {
210            echo 'Plugin Statistics: nothing to do - finished' . DOKU_LF;
211            return;
212        }
213
214        $event->stopPropagation();
215        $event->preventDefault();
216
217        if ($page_ran) {
218            echo 'Plugin Statistics: logging media' . DOKU_LF;
219            $hlp->getLogger()->logHistoryMedia();
220        } else {
221            echo 'Plugin Statistics: logging pages' . DOKU_LF;
222            $hlp->getLogger()->logHistoryPages();
223        }
224        echo 'Plugin Statistics: finished' . DOKU_LF;
225    }
226
227    /**
228     * Pre-Sanitize the action command
229     *
230     * Similar to act_clean in action.php but simplified and without
231     * error messages
232     */
233    protected function actClean($act)
234    {
235        // check if the action was given as array key
236        if (is_array($act)) {
237            [$act] = array_keys($act);
238        }
239
240        //remove all bad chars
241        $act = strtolower($act);
242        $act = preg_replace('/[^a-z_]+/', '', $act);
243
244        return $act;
245    }
246}
247