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