xref: /plugin/statistics/Query.php (revision 44f813308a35e35d99361268b4c7836be3b9f15a)
1b6632b6eSAndreas Gohr<?php
2b6632b6eSAndreas Gohr
3b6632b6eSAndreas Gohrnamespace dokuwiki\plugin\statistics;
4b6632b6eSAndreas Gohr
5f9e60319SAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
6b6632b6eSAndreas Gohruse helper_plugin_statistics;
7b6632b6eSAndreas Gohr
8f9e60319SAndreas Gohr/**
9f9e60319SAndreas Gohr * This class defines a bunch of SQL queries to fetch various statistics from the database
10f9e60319SAndreas Gohr */
11b6632b6eSAndreas Gohrclass Query
12b6632b6eSAndreas Gohr{
13f9e60319SAndreas Gohr    protected helper_plugin_statistics $hlp;
14f9e60319SAndreas Gohr    protected SQLiteDB $db;
15f9e60319SAndreas Gohr    protected string $from;
16f9e60319SAndreas Gohr    protected string $to;
17f9e60319SAndreas Gohr    protected string $limit = '';
18*44f81330SAndreas Gohr    protected string $tz = 'localtime';
19b6632b6eSAndreas Gohr
20f9e60319SAndreas Gohr    /**
21f9e60319SAndreas Gohr     * @param helper_plugin_statistics $hlp
22f9e60319SAndreas Gohr     */
23b6632b6eSAndreas Gohr    public function __construct(helper_plugin_statistics $hlp)
24b6632b6eSAndreas Gohr    {
25b6632b6eSAndreas Gohr        $this->hlp = $hlp;
26f9e60319SAndreas Gohr        $this->db = $hlp->getDB();
277428e816SAndreas Gohr        $today = date('Y-m-d');
287428e816SAndreas Gohr        $this->setTimeFrame($today, $today);
29f9e60319SAndreas Gohr        $this->setPagination(0, 20);
307428e816SAndreas Gohr    }
317428e816SAndreas Gohr
327428e816SAndreas Gohr    /**
337428e816SAndreas Gohr     * Set the time frame for all queries
34f9e60319SAndreas Gohr     *
35f9e60319SAndreas Gohr     * @param string $from The start date as YYYY-MM-DD
36f9e60319SAndreas Gohr     * @param string $to The end date as YYYY-MM-DD
377428e816SAndreas Gohr     */
38f9e60319SAndreas Gohr    public function setTimeFrame(string $from, string $to): void
397428e816SAndreas Gohr    {
40f9e60319SAndreas Gohr        try {
41f9e60319SAndreas Gohr            $from = new \DateTime($from);
42f9e60319SAndreas Gohr            $to = new \DateTime($to);
43f9e60319SAndreas Gohr        } catch (\Exception $e) {
44f9e60319SAndreas Gohr            $from = new \DateTime();
45f9e60319SAndreas Gohr            $to = new \DateTime();
46f9e60319SAndreas Gohr        }
47f9e60319SAndreas Gohr        $from->setTime(0, 0);
48f9e60319SAndreas Gohr        $to->setTime(23, 59, 59);
497428e816SAndreas Gohr
50f9e60319SAndreas Gohr        $this->from = $from->format('Y-m-d H:i:s');
51f9e60319SAndreas Gohr        $this->to = $to->format('Y-m-d H:i:s');
52f9e60319SAndreas Gohr    }
53f9e60319SAndreas Gohr
54f9e60319SAndreas Gohr    /**
55f9e60319SAndreas Gohr     * Set the pagination settings for some queries
56f9e60319SAndreas Gohr     *
57f9e60319SAndreas Gohr     * @param int $start The start offset
58f9e60319SAndreas Gohr     * @param int $limit The number of results. If one more is returned, there is another page
59f9e60319SAndreas Gohr     * @return void
60f9e60319SAndreas Gohr     */
61f9e60319SAndreas Gohr    public function setPagination(int $start, int $limit)
62f9e60319SAndreas Gohr    {
63f9e60319SAndreas Gohr        // when a limit is set, one more is fetched to indicate when a next page exists
64f9e60319SAndreas Gohr        if ($limit) $limit += 1;
65f9e60319SAndreas Gohr
66f9e60319SAndreas Gohr        if ($limit) {
67f9e60319SAndreas Gohr            $this->limit = " LIMIT $start,$limit";
68f9e60319SAndreas Gohr        } elseif ($start) {
69f9e60319SAndreas Gohr            $this->limit = " OFFSET $start";
70f9e60319SAndreas Gohr        }
71b6632b6eSAndreas Gohr    }
72b6632b6eSAndreas Gohr
73b6632b6eSAndreas Gohr    /**
74b6632b6eSAndreas Gohr     * Return some aggregated statistics
75b6632b6eSAndreas Gohr     */
76f9e60319SAndreas Gohr    public function aggregate(): array
77b6632b6eSAndreas Gohr    {
782a30f557SAndreas Gohr        // init some values that might not be set
792a30f557SAndreas Gohr        $data = [
802a30f557SAndreas Gohr            'referers' => 0, // total number of (external) referrers
812a30f557SAndreas Gohr            'external' => 0, // external referrers
822a30f557SAndreas Gohr            'search' => 0, // search engine referrers
832a30f557SAndreas Gohr            'direct' => 0, // direct referrers
842a30f557SAndreas Gohr            'internal' => 0, // internal referrers
852a30f557SAndreas Gohr            'bouncerate' => 0,
862a30f557SAndreas Gohr            'newvisitors' => 0,
872a30f557SAndreas Gohr        ];
88b6632b6eSAndreas Gohr
8910dcb86fSAndreas Gohr        // Count referrer types by joining with referers table
9010dcb86fSAndreas Gohr        $sql = "SELECT
9110dcb86fSAndreas Gohr                    CASE
9210dcb86fSAndreas Gohr                        WHEN R.engine IS NOT NULL THEN 'search'
932a30f557SAndreas Gohr                        WHEN R.url = '' THEN 'direct'
942a30f557SAndreas Gohr                        WHEN R.url IS NOT NULL THEN 'external'
952a30f557SAndreas Gohr                        ELSE 'internal'
9610dcb86fSAndreas Gohr                    END as ref_type,
9710dcb86fSAndreas Gohr                    COUNT(*) as cnt
9810dcb86fSAndreas Gohr                  FROM pageviews as P
9910dcb86fSAndreas Gohr                  LEFT JOIN referers as R ON P.ref_id = R.id
10010dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
101*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
10230cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
103b6632b6eSAndreas Gohr              GROUP BY ref_type";
104*44f81330SAndreas Gohr        $result = $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
105b6632b6eSAndreas Gohr
106f9e60319SAndreas Gohr        foreach ($result as $row) {
1072a30f557SAndreas Gohr            if ($row['ref_type'] == 'search') {
1082a30f557SAndreas Gohr                $data['search'] = $row['cnt'];
1092a30f557SAndreas Gohr                $data['referers'] += $row['cnt'];
1102a30f557SAndreas Gohr            }
1112a30f557SAndreas Gohr            if ($row['ref_type'] == 'direct') {
1122a30f557SAndreas Gohr                $data['direct'] = $row['cnt'];
1132a30f557SAndreas Gohr                $data['referers'] += $row['cnt'];
1142a30f557SAndreas Gohr            }
1152a30f557SAndreas Gohr            if ($row['ref_type'] == 'external') {
1162a30f557SAndreas Gohr                $data['external'] = $row['cnt'];
1172a30f557SAndreas Gohr                $data['referers'] += $row['cnt'];
1182a30f557SAndreas Gohr            }
1192a30f557SAndreas Gohr            if ($row['ref_type'] == 'internal') {
1202a30f557SAndreas Gohr                $data['internal'] = $row['cnt'];
1212a30f557SAndreas Gohr            }
122b6632b6eSAndreas Gohr        }
123b6632b6eSAndreas Gohr
124b6632b6eSAndreas Gohr        // general user and session info
12510dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT P.session) as sessions,
12610dcb86fSAndreas Gohr                       COUNT(P.session) as views,
12710dcb86fSAndreas Gohr                       COUNT(DISTINCT S.user) as users,
128*44f81330SAndreas Gohr                       COUNT(DISTINCT S.uid) as visitors,
129*44f81330SAndreas Gohr                       DATETIME(MAX(P.dt), ?) as last
13010dcb86fSAndreas Gohr                  FROM pageviews as P
13110dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
132*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
13330cf9434SAndreas Gohr                   AND S.ua_type = 'browser'";
134*44f81330SAndreas Gohr        $result = $this->db->queryRecord($sql, [$this->tz, $this->tz, $this->from, $this->tz, $this->to]);
135b6632b6eSAndreas Gohr
13610dcb86fSAndreas Gohr        $data['users'] = $result['users'];
137f9e60319SAndreas Gohr        $data['sessions'] = $result['sessions'];
138f9e60319SAndreas Gohr        $data['pageviews'] = $result['views'];
139f9e60319SAndreas Gohr        $data['visitors'] = $result['visitors'];
140*44f81330SAndreas Gohr        $data['last'] = $result['last'];
141b6632b6eSAndreas Gohr
14210dcb86fSAndreas Gohr        // calculate bounce rate (sessions with only 1 page view)
143b6632b6eSAndreas Gohr        if ($data['sessions']) {
144b6632b6eSAndreas Gohr            $sql = "SELECT COUNT(*) as cnt
14510dcb86fSAndreas Gohr                      FROM (
14610dcb86fSAndreas Gohr                          SELECT P.session, COUNT(*) as views
14710dcb86fSAndreas Gohr                            FROM pageviews as P
14810dcb86fSAndreas Gohr                            LEFT JOIN sessions as S ON P.session = S.session
149*44f81330SAndreas Gohr                           WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
15030cf9434SAndreas Gohr                             AND S.ua_type = 'browser'
15110dcb86fSAndreas Gohr                        GROUP BY P.session
15210dcb86fSAndreas Gohr                          HAVING views = 1
15310dcb86fSAndreas Gohr                      )";
154*44f81330SAndreas Gohr            $count = $this->db->queryValue($sql, [$this->tz, $this->from, $this->tz, $this->to]);
155f9e60319SAndreas Gohr            $data['bouncerate'] = $count * 100 / $data['sessions'];
156f9e60319SAndreas Gohr            $data['newvisitors'] = $count * 100 / $data['sessions'];
157b6632b6eSAndreas Gohr        }
158b6632b6eSAndreas Gohr
159b6632b6eSAndreas Gohr        // calculate avg. number of views per session
160b6632b6eSAndreas Gohr        $sql = "SELECT AVG(views) as cnt
16110dcb86fSAndreas Gohr                  FROM (
16210dcb86fSAndreas Gohr                      SELECT P.session, COUNT(*) as views
16310dcb86fSAndreas Gohr                        FROM pageviews as P
16410dcb86fSAndreas Gohr                        LEFT JOIN sessions as S ON P.session = S.session
165*44f81330SAndreas Gohr                       WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
16630cf9434SAndreas Gohr                         AND S.ua_type = 'browser'
16710dcb86fSAndreas Gohr                    GROUP BY P.session
16810dcb86fSAndreas Gohr                  )";
169*44f81330SAndreas Gohr        $data['avgpages'] = $this->db->queryValue($sql, [$this->tz, $this->from, $this->tz, $this->to]);
170b6632b6eSAndreas Gohr
171b6632b6eSAndreas Gohr        // average time spent on the site
17210dcb86fSAndreas Gohr        $sql = "SELECT AVG((julianday(end) - julianday(dt)) * 24 * 60) as time
17310dcb86fSAndreas Gohr                  FROM sessions as S
17410dcb86fSAndreas Gohr                 WHERE S.dt >= ? AND S.dt <= ?
17510dcb86fSAndreas Gohr                   AND S.dt != S.end
176*44f81330SAndreas Gohr                   AND DATETIME(S.dt, ?) >= ? AND DATETIME(S.dt, ?) <= ?
17730cf9434SAndreas Gohr                   AND S.ua_type = 'browser'";
178*44f81330SAndreas Gohr        $data['timespent'] = $this->db->queryValue($sql, [$this->tz, $this->from, $this->tz, $this->to]);
179b6632b6eSAndreas Gohr
180b6632b6eSAndreas Gohr        // logins
181b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as logins
1827428e816SAndreas Gohr                  FROM logins as A
183*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
18430cf9434SAndreas Gohr                   AND (type = 'l' OR type = 'p')";
185*44f81330SAndreas Gohr        $data['logins'] = $this->db->queryValue($sql, [$this->tz, $this->from, $this->tz, $this->to]);
186b6632b6eSAndreas Gohr
187b6632b6eSAndreas Gohr        // registrations
188b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as registrations
1897428e816SAndreas Gohr                  FROM logins as A
190*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
19130cf9434SAndreas Gohr                   AND type = 'C'";
192*44f81330SAndreas Gohr        $data['registrations'] = $this->db->queryValue($sql, [$this->tz, $this->from, $this->tz, $this->to]);
193b6632b6eSAndreas Gohr
19410dcb86fSAndreas Gohr        // current users (based on recent sessions)
19510dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT uid) as current
19610dcb86fSAndreas Gohr                  FROM sessions
19710dcb86fSAndreas Gohr                 WHERE end >= datetime('now', '-10 minutes')";
198f9e60319SAndreas Gohr        $data['current'] = $this->db->queryValue($sql);
199b6632b6eSAndreas Gohr
200b6632b6eSAndreas Gohr        return $data;
201b6632b6eSAndreas Gohr    }
202b6632b6eSAndreas Gohr
203b6632b6eSAndreas Gohr
204b6632b6eSAndreas Gohr    /**
205b6632b6eSAndreas Gohr     * Return some trend data about visits and edits in the wiki
206f9e60319SAndreas Gohr     *
207f9e60319SAndreas Gohr     * @param bool $hours Use hour resolution rather than days
208f9e60319SAndreas Gohr     * @return array
209b6632b6eSAndreas Gohr     */
210f9e60319SAndreas Gohr    public function dashboardviews(bool $hours = false): array
211b6632b6eSAndreas Gohr    {
212b6632b6eSAndreas Gohr        if ($hours) {
213*44f81330SAndreas Gohr            $TIME = "strftime('%H', DATETIME(P.dt, '$this->tz'))";
214b6632b6eSAndreas Gohr        } else {
215*44f81330SAndreas Gohr            $TIME = "DATE(DATETIME(P.dt, '$this->tz'))";
216b6632b6eSAndreas Gohr        }
217b6632b6eSAndreas Gohr
218b6632b6eSAndreas Gohr        $data = [];
219b6632b6eSAndreas Gohr
220b6632b6eSAndreas Gohr        // access trends
221b6632b6eSAndreas Gohr        $sql = "SELECT $TIME as time,
22210dcb86fSAndreas Gohr                       COUNT(DISTINCT P.session) as sessions,
22310dcb86fSAndreas Gohr                       COUNT(P.session) as pageviews,
22410dcb86fSAndreas Gohr                       COUNT(DISTINCT S.uid) as visitors
22510dcb86fSAndreas Gohr                  FROM pageviews as P
22610dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
227*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
22830cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
229b6632b6eSAndreas Gohr              GROUP BY $TIME
230b6632b6eSAndreas Gohr              ORDER BY time";
231*44f81330SAndreas Gohr        $result = $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
232b6632b6eSAndreas Gohr        foreach ($result as $row) {
233b6632b6eSAndreas Gohr            $data[$row['time']]['sessions'] = $row['sessions'];
234b6632b6eSAndreas Gohr            $data[$row['time']]['pageviews'] = $row['pageviews'];
235b6632b6eSAndreas Gohr            $data[$row['time']]['visitors'] = $row['visitors'];
236b6632b6eSAndreas Gohr        }
237b6632b6eSAndreas Gohr        return $data;
238b6632b6eSAndreas Gohr    }
239b6632b6eSAndreas Gohr
240f9e60319SAndreas Gohr    /**
241f9e60319SAndreas Gohr     * @param bool $hours Use hour resolution rather than days
242f9e60319SAndreas Gohr     * @return array
243f9e60319SAndreas Gohr     */
244f9e60319SAndreas Gohr    public function dashboardwiki(bool $hours = false): array
245b6632b6eSAndreas Gohr    {
246b6632b6eSAndreas Gohr        if ($hours) {
247*44f81330SAndreas Gohr            $TIME = "strftime('%H', DATETIME(dt, '$this->tz'))";
248b6632b6eSAndreas Gohr        } else {
249*44f81330SAndreas Gohr            $TIME = "DATE(DATETIME(dt, '$this->tz'))";
250b6632b6eSAndreas Gohr        }
251b6632b6eSAndreas Gohr
252b6632b6eSAndreas Gohr        $data = [];
253b6632b6eSAndreas Gohr
254b6632b6eSAndreas Gohr        // edit trends
255b6632b6eSAndreas Gohr        foreach (['E', 'C', 'D'] as $type) {
256b6632b6eSAndreas Gohr            $sql = "SELECT $TIME as time,
257b6632b6eSAndreas Gohr                           COUNT(*) as cnt
2587428e816SAndreas Gohr                      FROM edits as A
259*44f81330SAndreas Gohr                     WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
26030cf9434SAndreas Gohr                       AND type = '$type'
261b6632b6eSAndreas Gohr                  GROUP BY $TIME
262b6632b6eSAndreas Gohr                  ORDER BY time";
263*44f81330SAndreas Gohr            $result = $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
264b6632b6eSAndreas Gohr            foreach ($result as $row) {
265b6632b6eSAndreas Gohr                $data[$row['time']][$type] = $row['cnt'];
266b6632b6eSAndreas Gohr            }
267b6632b6eSAndreas Gohr        }
268b6632b6eSAndreas Gohr        ksort($data);
269b6632b6eSAndreas Gohr        return $data;
270b6632b6eSAndreas Gohr    }
271b6632b6eSAndreas Gohr
272f9e60319SAndreas Gohr    /**
273f9e60319SAndreas Gohr     * @param string $info Which type of history to select (FIXME which ones are there?)
274f9e60319SAndreas Gohr     * @param string $interval Group data by this interval (days, weeks, months)
275f9e60319SAndreas Gohr     * @return array
276f9e60319SAndreas Gohr     */
277f9e60319SAndreas Gohr    public function history(string $info, string $interval = 'day'): array
278b6632b6eSAndreas Gohr    {
279b6632b6eSAndreas Gohr        if ($interval == 'weeks') {
280*44f81330SAndreas Gohr            $TIME = "strftime('%Y', DATETIME(dt, '$this->tz')), strftime('%W', DATETIME(dt, '$this->tz'))";
281b6632b6eSAndreas Gohr        } elseif ($interval == 'months') {
282*44f81330SAndreas Gohr            $TIME = "strftime('%Y-%m', DATETIME(dt, '$this->tz'))";
283b6632b6eSAndreas Gohr        } else {
284*44f81330SAndreas Gohr            $TIME = "strftime('%d-%m', DATETIME(dt, '$this->tz'))";
285b6632b6eSAndreas Gohr        }
286b6632b6eSAndreas Gohr
287b6632b6eSAndreas Gohr        $mod = 1;
288b6632b6eSAndreas Gohr        if ($info == 'media_size' || $info == 'page_size') {
289b6632b6eSAndreas Gohr            $mod = 1024 * 1024;
290b6632b6eSAndreas Gohr        }
291b6632b6eSAndreas Gohr
292b6632b6eSAndreas Gohr        $sql = "SELECT $TIME as time,
2937428e816SAndreas Gohr                       AVG(value)/$mod as cnt
2947428e816SAndreas Gohr                  FROM history as A
295*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
2967428e816SAndreas Gohr                   AND info = ?
297b6632b6eSAndreas Gohr                  GROUP BY $TIME
298b6632b6eSAndreas Gohr                  ORDER BY $TIME";
299*44f81330SAndreas Gohr
300*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to, $info]);
301b6632b6eSAndreas Gohr    }
302b6632b6eSAndreas Gohr
303f9e60319SAndreas Gohr    /**
304f9e60319SAndreas Gohr     * @return array
305f9e60319SAndreas Gohr     */
306f9e60319SAndreas Gohr    public function searchengines(): array
307b6632b6eSAndreas Gohr    {
30810dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, R.engine
3096f2bdce1SAndreas Gohr                  FROM pageviews as P,
3106f2bdce1SAndreas Gohr                       referers as R
311*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
3126f2bdce1SAndreas Gohr                   AND P.ref_id = R.id
31310dcb86fSAndreas Gohr                   AND R.engine != ''
31410dcb86fSAndreas Gohr              GROUP BY R.engine
31510dcb86fSAndreas Gohr              ORDER BY cnt DESC, R.engine" .
316f9e60319SAndreas Gohr            $this->limit;
317*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
318b6632b6eSAndreas Gohr    }
319b6632b6eSAndreas Gohr
320f9e60319SAndreas Gohr    /**
321f9e60319SAndreas Gohr     * @return array
322f9e60319SAndreas Gohr     */
3232a30f557SAndreas Gohr    public function searchphrases(): array
324b6632b6eSAndreas Gohr    {
3252a30f557SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, query, query as ilookup
3262a30f557SAndreas Gohr                  FROM search
327*44f81330SAndreas Gohr                 WHERE DATETIME(dt, ?) >= ? AND DATETIME(dt, ?) <= ?
3282a30f557SAndreas Gohr              GROUP BY query
3292a30f557SAndreas Gohr              ORDER BY cnt DESC, query" .
330f9e60319SAndreas Gohr            $this->limit;
331*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
332b6632b6eSAndreas Gohr    }
333b6632b6eSAndreas Gohr
334f9e60319SAndreas Gohr    /**
335f9e60319SAndreas Gohr     * @return array
336f9e60319SAndreas Gohr     */
3372a30f557SAndreas Gohr    public function searchwords(): array
338b6632b6eSAndreas Gohr    {
3392a30f557SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, SW.word, SW.word as ilookup
3402a30f557SAndreas Gohr                  FROM search as S,
3412a30f557SAndreas Gohr                       searchwords as SW
342*44f81330SAndreas Gohr                 WHERE DATETIME(S.dt, ?) >= ? AND DATETIME(S.dt, ?) <= ?
3432a30f557SAndreas Gohr                   AND S.id = SW.sid
34410dcb86fSAndreas Gohr              GROUP BY SW.word
34510dcb86fSAndreas Gohr              ORDER BY cnt DESC, SW.word" .
346f9e60319SAndreas Gohr            $this->limit;
347*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
348b6632b6eSAndreas Gohr    }
349b6632b6eSAndreas Gohr
350f9e60319SAndreas Gohr    /**
351f9e60319SAndreas Gohr     * @return array
352f9e60319SAndreas Gohr     */
353f9e60319SAndreas Gohr    public function outlinks(): array
354b6632b6eSAndreas Gohr    {
355b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, link as url
3567428e816SAndreas Gohr                  FROM outlinks as A
357*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
358b6632b6eSAndreas Gohr              GROUP BY link
359b6632b6eSAndreas Gohr              ORDER BY cnt DESC, link" .
360f9e60319SAndreas Gohr            $this->limit;
361*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
362b6632b6eSAndreas Gohr    }
363b6632b6eSAndreas Gohr
364f9e60319SAndreas Gohr    /**
365f9e60319SAndreas Gohr     * @return array
366f9e60319SAndreas Gohr     */
367f9e60319SAndreas Gohr    public function pages(): array
368b6632b6eSAndreas Gohr    {
36910dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, P.page
3706f2bdce1SAndreas Gohr                  FROM pageviews as P,
3716f2bdce1SAndreas Gohr                       sessions as S
372*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
3736f2bdce1SAndreas Gohr                   AND P.session = S.session
37430cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
37510dcb86fSAndreas Gohr              GROUP BY P.page
37610dcb86fSAndreas Gohr              ORDER BY cnt DESC, P.page" .
377f9e60319SAndreas Gohr            $this->limit;
378*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
379b6632b6eSAndreas Gohr    }
380b6632b6eSAndreas Gohr
381f9e60319SAndreas Gohr    /**
382f9e60319SAndreas Gohr     * @return array
383f9e60319SAndreas Gohr     */
384f9e60319SAndreas Gohr    public function edits(): array
385b6632b6eSAndreas Gohr    {
386b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, page
3877428e816SAndreas Gohr                  FROM edits as A
388*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
389b6632b6eSAndreas Gohr              GROUP BY page
390b6632b6eSAndreas Gohr              ORDER BY cnt DESC, page" .
391f9e60319SAndreas Gohr            $this->limit;
392*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
393b6632b6eSAndreas Gohr    }
394b6632b6eSAndreas Gohr
395f9e60319SAndreas Gohr    /**
396f9e60319SAndreas Gohr     * @return array
397f9e60319SAndreas Gohr     */
398f9e60319SAndreas Gohr    public function images(): array
399b6632b6eSAndreas Gohr    {
400b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, media, SUM(size) as filesize
4017428e816SAndreas Gohr                  FROM media as A
402*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
40330cf9434SAndreas Gohr                   AND mime1 = 'image'
404b6632b6eSAndreas Gohr              GROUP BY media
405b6632b6eSAndreas Gohr              ORDER BY cnt DESC, media" .
406f9e60319SAndreas Gohr            $this->limit;
407*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
408b6632b6eSAndreas Gohr    }
409b6632b6eSAndreas Gohr
410f9e60319SAndreas Gohr    /**
411f9e60319SAndreas Gohr     * @return array
412f9e60319SAndreas Gohr     */
413f9e60319SAndreas Gohr    public function imagessum(): array
414b6632b6eSAndreas Gohr    {
415b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, SUM(size) as filesize
4167428e816SAndreas Gohr                  FROM media as A
417*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
41830cf9434SAndreas Gohr                   AND mime1 = 'image'";
419*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
420b6632b6eSAndreas Gohr    }
421b6632b6eSAndreas Gohr
422f9e60319SAndreas Gohr    /**
423f9e60319SAndreas Gohr     * @return array
424f9e60319SAndreas Gohr     */
425f9e60319SAndreas Gohr    public function downloads(): array
426b6632b6eSAndreas Gohr    {
427b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, media, SUM(size) as filesize
4287428e816SAndreas Gohr                  FROM media as A
429*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
43030cf9434SAndreas Gohr                   AND mime1 != 'image'
431b6632b6eSAndreas Gohr              GROUP BY media
432b6632b6eSAndreas Gohr              ORDER BY cnt DESC, media" .
433f9e60319SAndreas Gohr            $this->limit;
434*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
435b6632b6eSAndreas Gohr    }
436b6632b6eSAndreas Gohr
437f9e60319SAndreas Gohr    /**
438f9e60319SAndreas Gohr     * @return array
439f9e60319SAndreas Gohr     */
440f9e60319SAndreas Gohr    public function downloadssum(): array
441b6632b6eSAndreas Gohr    {
442b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, SUM(size) as filesize
4437428e816SAndreas Gohr                  FROM media as A
444*44f81330SAndreas Gohr                 WHERE DATETIME(A.dt, ?) >= ? AND DATETIME(A.dt, ?) <= ?
44530cf9434SAndreas Gohr                   AND mime1 != 'image'";
446*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
447b6632b6eSAndreas Gohr    }
448b6632b6eSAndreas Gohr
449f9e60319SAndreas Gohr    /**
450f9e60319SAndreas Gohr     * @return array
451f9e60319SAndreas Gohr     */
452f9e60319SAndreas Gohr    public function referer(): array
453b6632b6eSAndreas Gohr    {
45410dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, R.url
45510dcb86fSAndreas Gohr                  FROM pageviews as P
45610dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
45710dcb86fSAndreas Gohr                  LEFT JOIN referers as R ON P.ref_id = R.id
458*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
45930cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
46010dcb86fSAndreas Gohr                   AND R.url IS NOT NULL
46110dcb86fSAndreas Gohr                   AND R.url != ''
46210dcb86fSAndreas Gohr                   AND R.engine IS NULL
46310dcb86fSAndreas Gohr              GROUP BY R.url
46410dcb86fSAndreas Gohr              ORDER BY cnt DESC, R.url" .
465f9e60319SAndreas Gohr            $this->limit;
466*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
467b6632b6eSAndreas Gohr    }
468b6632b6eSAndreas Gohr
469f9e60319SAndreas Gohr    /**
470f9e60319SAndreas Gohr     * @return array
471f9e60319SAndreas Gohr     */
472f9e60319SAndreas Gohr    public function newreferer(): array
473b6632b6eSAndreas Gohr    {
47410dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, R.url
47510dcb86fSAndreas Gohr                  FROM pageviews as P
47610dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
47710dcb86fSAndreas Gohr                  LEFT JOIN referers as R ON P.ref_id = R.id
478*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
47930cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
48010dcb86fSAndreas Gohr                   AND R.url IS NOT NULL
48110dcb86fSAndreas Gohr                   AND R.url != ''
48210dcb86fSAndreas Gohr                   AND R.engine IS NULL
483*44f81330SAndreas Gohr                   AND DATETIME(R.dt, ?) >= ?
48410dcb86fSAndreas Gohr              GROUP BY R.url
48510dcb86fSAndreas Gohr              ORDER BY cnt DESC, R.url" .
486f9e60319SAndreas Gohr            $this->limit;
487*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to, $this->tz, $this->from]);
488b6632b6eSAndreas Gohr    }
489b6632b6eSAndreas Gohr
490f9e60319SAndreas Gohr    /**
491f9e60319SAndreas Gohr     * @return array
492f9e60319SAndreas Gohr     */
493f9e60319SAndreas Gohr    public function countries(): array
494b6632b6eSAndreas Gohr    {
49510dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT P.session) as cnt, I.country
4966f2bdce1SAndreas Gohr                  FROM pageviews as P,
4976f2bdce1SAndreas Gohr                       iplocation as I
498*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
4996f2bdce1SAndreas Gohr                   AND P.ip = I.ip
50010dcb86fSAndreas Gohr                   AND I.country != ''
50110dcb86fSAndreas Gohr              GROUP BY I.code
50210dcb86fSAndreas Gohr              ORDER BY cnt DESC, I.country" .
503f9e60319SAndreas Gohr            $this->limit;
504*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
505b6632b6eSAndreas Gohr    }
506b6632b6eSAndreas Gohr
507f9e60319SAndreas Gohr    /**
508f9e60319SAndreas Gohr     * @param bool $ext return extended information
509f9e60319SAndreas Gohr     * @return array
510f9e60319SAndreas Gohr     */
5112a30f557SAndreas Gohr    public function browsers(bool $ext = false): array
512b6632b6eSAndreas Gohr    {
513b6632b6eSAndreas Gohr        if ($ext) {
51410dcb86fSAndreas Gohr            $sel = 'S.ua_info as browser, S.ua_ver';
51510dcb86fSAndreas Gohr            $grp = 'S.ua_info, S.ua_ver';
516b6632b6eSAndreas Gohr        } else {
5172a30f557SAndreas Gohr            $sel = 'S.ua_info as browser';
51810dcb86fSAndreas Gohr            $grp = 'S.ua_info';
519b6632b6eSAndreas Gohr        }
520b6632b6eSAndreas Gohr
52110dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.session) as cnt, $sel
52210dcb86fSAndreas Gohr                  FROM sessions as S
523*44f81330SAndreas Gohr                 WHERE DATETIME(S.dt, ?) >= ? AND DATETIME(S.dt, ?) <= ?
52430cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
525b6632b6eSAndreas Gohr              GROUP BY $grp
52610dcb86fSAndreas Gohr              ORDER BY cnt DESC, S.ua_info" .
527f9e60319SAndreas Gohr            $this->limit;
528*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
529b6632b6eSAndreas Gohr    }
530b6632b6eSAndreas Gohr
531f9e60319SAndreas Gohr    /**
532f9e60319SAndreas Gohr     * @return array
533f9e60319SAndreas Gohr     */
534f9e60319SAndreas Gohr    public function os(): array
535b6632b6eSAndreas Gohr    {
53610dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.session) as cnt, S.os
53710dcb86fSAndreas Gohr                  FROM sessions as S
538*44f81330SAndreas Gohr                 WHERE DATETIME(S.dt, ?) >= ? AND DATETIME(S.dt, ?) <= ?
53930cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
54010dcb86fSAndreas Gohr              GROUP BY S.os
54110dcb86fSAndreas Gohr              ORDER BY cnt DESC, S.os" .
542f9e60319SAndreas Gohr            $this->limit;
543*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
544b6632b6eSAndreas Gohr    }
545b6632b6eSAndreas Gohr
546f9e60319SAndreas Gohr    /**
547f9e60319SAndreas Gohr     * @return array
548f9e60319SAndreas Gohr     */
5499fdd7e51SAndreas Gohr    public function topdomain(): array
5509fdd7e51SAndreas Gohr    {
5519fdd7e51SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, U.domain
5529fdd7e51SAndreas Gohr                  FROM pageviews as P,
5539fdd7e51SAndreas Gohr                       sessions as S,
5549fdd7e51SAndreas Gohr                       users as U
555*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
5569fdd7e51SAndreas Gohr                   AND P.session = S.session
5579fdd7e51SAndreas Gohr                   AND S.user = U.user
55830cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
5599fdd7e51SAndreas Gohr                   AND S.user IS NOT NULL
5609fdd7e51SAndreas Gohr              GROUP BY U.domain
5619fdd7e51SAndreas Gohr              ORDER BY cnt DESC, U.domain" .
5629fdd7e51SAndreas Gohr            $this->limit;
563*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
5649fdd7e51SAndreas Gohr    }
5659fdd7e51SAndreas Gohr
5669fdd7e51SAndreas Gohr    /**
5679fdd7e51SAndreas Gohr     * @return array
5689fdd7e51SAndreas Gohr     */
569f9e60319SAndreas Gohr    public function topuser(): array
570b6632b6eSAndreas Gohr    {
57110dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, S.user
5726f2bdce1SAndreas Gohr                  FROM pageviews as P,
5736f2bdce1SAndreas Gohr                       sessions as S
574*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
5756f2bdce1SAndreas Gohr                   AND P.session = S.session
57630cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
577b7091bfdSAndreas Gohr                   AND S.user IS NOT NULL
57810dcb86fSAndreas Gohr              GROUP BY S.user
57910dcb86fSAndreas Gohr              ORDER BY cnt DESC, S.user" .
580f9e60319SAndreas Gohr            $this->limit;
581*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
582b6632b6eSAndreas Gohr    }
583b6632b6eSAndreas Gohr
584f9e60319SAndreas Gohr    /**
585f9e60319SAndreas Gohr     * @return array
586f9e60319SAndreas Gohr     */
587f9e60319SAndreas Gohr    public function topeditor(): array
588b6632b6eSAndreas Gohr    {
589b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, user
5906f2bdce1SAndreas Gohr                  FROM edits as E,
5916f2bdce1SAndreas Gohr                       sessions as S
592*44f81330SAndreas Gohr                 WHERE DATETIME(E.dt, ?) >= ? AND DATETIME(E.dt, ?) <= ?
5936f2bdce1SAndreas Gohr                   AND E.session = S.session
594b7091bfdSAndreas Gohr                   AND S.user IS NOT NULL
595b6632b6eSAndreas Gohr              GROUP BY user
596b6632b6eSAndreas Gohr              ORDER BY cnt DESC, user" .
597f9e60319SAndreas Gohr            $this->limit;
598*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
599b6632b6eSAndreas Gohr    }
600b6632b6eSAndreas Gohr
601f9e60319SAndreas Gohr    /**
602f9e60319SAndreas Gohr     * @return array
603f9e60319SAndreas Gohr     */
604f9e60319SAndreas Gohr    public function topgroup(): array
605b6632b6eSAndreas Gohr    {
60610dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, G.`group`
6076f2bdce1SAndreas Gohr                  FROM pageviews as P,
6086f2bdce1SAndreas Gohr                       sessions as S,
6096f2bdce1SAndreas Gohr                       groups as G
610*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
6116f2bdce1SAndreas Gohr                   AND P.session = S.session
6126f2bdce1SAndreas Gohr                   AND S.user = G.user
61330cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
61410dcb86fSAndreas Gohr              GROUP BY G.`group`
61510dcb86fSAndreas Gohr              ORDER BY cnt DESC, G.`group`" .
616f9e60319SAndreas Gohr            $this->limit;
617*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
618b6632b6eSAndreas Gohr    }
619b6632b6eSAndreas Gohr
620f9e60319SAndreas Gohr    /**
621f9e60319SAndreas Gohr     * @return array
622f9e60319SAndreas Gohr     */
623f9e60319SAndreas Gohr    public function topgroupedit(): array
624b6632b6eSAndreas Gohr    {
62510dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, G.`group`
6266f2bdce1SAndreas Gohr                  FROM edits as E,
6276f2bdce1SAndreas Gohr                       sessions as S,
6286f2bdce1SAndreas Gohr                       groups as G
629*44f81330SAndreas Gohr                 WHERE DATETIME(E.dt, ?) >= ? AND DATETIME(E.dt, ?) <= ?
6306f2bdce1SAndreas Gohr                   AND E.session = S.session
6316f2bdce1SAndreas Gohr                   AND S.user = G.user
63210dcb86fSAndreas Gohr              GROUP BY G.`group`
63310dcb86fSAndreas Gohr              ORDER BY cnt DESC, G.`group`" .
634f9e60319SAndreas Gohr            $this->limit;
635*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
636b6632b6eSAndreas Gohr    }
637b6632b6eSAndreas Gohr
638b6632b6eSAndreas Gohr
639f9e60319SAndreas Gohr    /**
640f9e60319SAndreas Gohr     * @return array
641f9e60319SAndreas Gohr     */
642f9e60319SAndreas Gohr    public function resolution(): array
643b6632b6eSAndreas Gohr    {
64410dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.uid) as cnt,
64510dcb86fSAndreas Gohr                       ROUND(P.screen_x/100)*100 as res_x,
64610dcb86fSAndreas Gohr                       ROUND(P.screen_y/100)*100 as res_y,
647bd514593SAndreas Gohr                       CAST(ROUND(P.screen_x/100)*100 AS int)
648bd514593SAndreas Gohr                           || 'x' ||
649bd514593SAndreas Gohr                       CAST(ROUND(P.screen_y/100)*100 AS int) as resolution
6506f2bdce1SAndreas Gohr                  FROM pageviews as P,
6516f2bdce1SAndreas Gohr                       sessions as S
652*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
6536f2bdce1SAndreas Gohr                   AND P.session = S.session
65430cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
65530cf9434SAndreas Gohr                   AND P.screen_x != 0
65630cf9434SAndreas Gohr                   AND P.screen_y != 0
657b6632b6eSAndreas Gohr              GROUP BY resolution
658b6632b6eSAndreas Gohr              ORDER BY cnt DESC" .
659f9e60319SAndreas Gohr            $this->limit;
660*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
661b6632b6eSAndreas Gohr    }
662b6632b6eSAndreas Gohr
663f9e60319SAndreas Gohr    /**
664f9e60319SAndreas Gohr     * @return array
665f9e60319SAndreas Gohr     */
666f9e60319SAndreas Gohr    public function viewport(): array
667b6632b6eSAndreas Gohr    {
66810dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.uid) as cnt,
66910dcb86fSAndreas Gohr                       ROUND(P.view_x/100)*100 as res_x,
67010dcb86fSAndreas Gohr                       ROUND(P.view_y/100)*100 as res_y,
671bd514593SAndreas Gohr                       CAST(ROUND(P.view_x/100)*100 AS int)
672bd514593SAndreas Gohr                           || 'x' ||
673bd514593SAndreas Gohr                       CAST(ROUND(P.view_y/100)*100 AS int) as resolution
6746f2bdce1SAndreas Gohr                  FROM pageviews as P,
6756f2bdce1SAndreas Gohr                       sessions as S
676*44f81330SAndreas Gohr                 WHERE DATETIME(P.dt, ?) >= ? AND DATETIME(P.dt, ?) <= ?
6776f2bdce1SAndreas Gohr                   AND P.session = S.session
67830cf9434SAndreas Gohr                   AND S.ua_type = 'browser'
67930cf9434SAndreas Gohr                   AND P.view_x != 0
68030cf9434SAndreas Gohr                   AND P.view_y != 0
681b6632b6eSAndreas Gohr              GROUP BY resolution
682b6632b6eSAndreas Gohr              ORDER BY cnt DESC" .
683f9e60319SAndreas Gohr            $this->limit;
684b6632b6eSAndreas Gohr
685*44f81330SAndreas Gohr        return $this->db->queryAll($sql, [$this->tz, $this->from, $this->tz, $this->to]);
686b6632b6eSAndreas Gohr    }
687b6632b6eSAndreas Gohr
688f9e60319SAndreas Gohr    /**
689f9e60319SAndreas Gohr     * @return array
690f9e60319SAndreas Gohr     */
691f9e60319SAndreas Gohr    public function seenusers(): array
692b6632b6eSAndreas Gohr    {
69310dcb86fSAndreas Gohr        $sql = "SELECT `user`, MAX(`dt`) as dt
69410dcb86fSAndreas Gohr                  FROM users
69510dcb86fSAndreas Gohr                 WHERE `user` IS NOT NULL
69610dcb86fSAndreas Gohr                   AND `user` != ''
69710dcb86fSAndreas Gohr              GROUP BY `user`
698b6632b6eSAndreas Gohr              ORDER BY `dt` DESC" .
699f9e60319SAndreas Gohr            $this->limit;
700b6632b6eSAndreas Gohr
701f9e60319SAndreas Gohr        return $this->db->queryAll($sql);
702b6632b6eSAndreas Gohr    }
703b6632b6eSAndreas Gohr}
704