xref: /plugin/qc/renderer.php (revision c8d45e58b7fb355e519f202d4d79b4c8f46772b5)
18fce80b1SAndreas Gohr<?php
2*c8d45e58SAndreas Gohr// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
376bbc49cSAnna Dabrowska
42fc45e0cSsplitbrainuse dokuwiki\ChangeLog\PageChangeLog;
52fc45e0cSsplitbrainuse dokuwiki\File\PageResolver;
68ae469bfSAndreas Gohruse dokuwiki\Utf8\PhpString;
78ae469bfSAndreas Gohr
88fce80b1SAndreas Gohr/**
98fce80b1SAndreas Gohr * The Renderer
108fce80b1SAndreas Gohr */
1176bbc49cSAnna Dabrowskaclass renderer_plugin_qc extends Doku_Renderer
1276bbc49cSAnna Dabrowska{
138fce80b1SAndreas Gohr    /**
148fce80b1SAndreas Gohr     * We store all our data in an array
158fce80b1SAndreas Gohr     */
162fc45e0cSsplitbrain    public $docArray = [
178fce80b1SAndreas Gohr        // raw statistics
182fc45e0cSsplitbrain        'header_count' => [0, 0, 0, 0, 0, 0],
192fc45e0cSsplitbrain        'header_struct' => [],
208fce80b1SAndreas Gohr        'linebreak' => 0,
218fce80b1SAndreas Gohr        'quote_nest' => 0,
228fce80b1SAndreas Gohr        'quote_count' => 0,
238fce80b1SAndreas Gohr        'fixme' => 0,
248fce80b1SAndreas Gohr        'hr' => 0,
25d723b313SAndreas Gohr        'formatted' => 0,
264ea373cdSAndreas Gohr        'created' => 0,
274ea373cdSAndreas Gohr        'modified' => 0,
284ea373cdSAndreas Gohr        'changes' => 0,
292fc45e0cSsplitbrain        'authors' => [],
30d723b313SAndreas Gohr        'internal_links' => 0,
31d723b313SAndreas Gohr        'broken_links' => 0,
32d723b313SAndreas Gohr        'external_links' => 0,
332fc45e0cSsplitbrain        'link_lengths' => [],
344ea373cdSAndreas Gohr        'chars' => 0,
354ea373cdSAndreas Gohr        'words' => 0,
369068e431SAndreas Gohr        'score' => 0,
378fce80b1SAndreas Gohr        // calculated error scores
38*c8d45e58SAndreas Gohr        'err' => [
39*c8d45e58SAndreas Gohr            'fixme' => 0,
40*c8d45e58SAndreas Gohr            'noh1' => 0,
41*c8d45e58SAndreas Gohr            'manyh1' => 0,
42*c8d45e58SAndreas Gohr            'headernest' => 0,
43*c8d45e58SAndreas Gohr            'manyhr' => 0,
44*c8d45e58SAndreas Gohr            'manybr' => 0,
45*c8d45e58SAndreas Gohr            'longformat' => 0,
46*c8d45e58SAndreas Gohr            'multiformat' => 0
47*c8d45e58SAndreas Gohr        ],
482fc45e0cSsplitbrain    ];
498fce80b1SAndreas Gohr
507db0beafSAnna Dabrowska    protected $quotelevel = 0;
517db0beafSAnna Dabrowska    protected $formatting = 0;
527db0beafSAnna Dabrowska    protected $tableopen = false;
538fce80b1SAndreas Gohr
548ae469bfSAndreas Gohr    /** @inheritdoc */
55*c8d45e58SAndreas Gohr    public function document_start()
5676bbc49cSAnna Dabrowska    {
574ea373cdSAndreas Gohr        global $ID;
584ea373cdSAndreas Gohr        $meta = p_get_metadata($ID);
594ea373cdSAndreas Gohr
604ea373cdSAndreas Gohr        // get some dates from meta data
617db0beafSAnna Dabrowska        $this->docArray['created'] = $meta['date']['created'];
627db0beafSAnna Dabrowska        $this->docArray['modified'] = $meta['date']['modified'];
638fdbbd20SAnna Dabrowska        $this->docArray['authors']['*'] = 0;
644ea373cdSAndreas Gohr
654ea373cdSAndreas Gohr        // get author info
662fc45e0cSsplitbrain        $changelog = new PageChangeLog($ID);
675929c83eSMichael Große        $revs = $changelog->getRevisions(0, 10000); //FIXME find a good solution for 'get ALL revisions'
688ae469bfSAndreas Gohr        $revs[] = $meta['last_change']['date'];
697db0beafSAnna Dabrowska        $this->docArray['changes'] = count($revs);
704ea373cdSAndreas Gohr        foreach ($revs as $rev) {
71088a1fe7SMichael Grosse            $info = $changelog->getRevisionInfo($rev);
728ae469bfSAndreas Gohr            if ($info && !empty($info['user'])) {
732fc45e0cSsplitbrain                $authorUserCnt = empty($this->docArray['authors'][$info['user']])
742fc45e0cSsplitbrain                    ? 0
752fc45e0cSsplitbrain                    : $this->docArray['authors'][$info['user']];
768fdbbd20SAnna Dabrowska                $this->docArray['authors'][$info['user']] = $authorUserCnt + 1;
774ea373cdSAndreas Gohr            } else {
782fc45e0cSsplitbrain                ++$this->docArray['authors']['*'];
794ea373cdSAndreas Gohr            }
804ea373cdSAndreas Gohr        }
814ea373cdSAndreas Gohr
824ea373cdSAndreas Gohr        // work on raw text
834ea373cdSAndreas Gohr        $text = rawWiki($ID);
842fc45e0cSsplitbrain        $this->docArray['chars'] = PhpString::strlen($text);
857db0beafSAnna Dabrowska        $this->docArray['words'] = count(array_filter(preg_split('/[^\w\-_]/u', $text)));
864ea373cdSAndreas Gohr    }
874ea373cdSAndreas Gohr
884ea373cdSAndreas Gohr
898fce80b1SAndreas Gohr    /**
908fce80b1SAndreas Gohr     * Here the score is calculated
918ae469bfSAndreas Gohr     * @inheritdoc
928fce80b1SAndreas Gohr     */
93*c8d45e58SAndreas Gohr    public function document_end()
9476bbc49cSAnna Dabrowska    {
95e348ef7cSAndreas Gohr        global $ID;
96e348ef7cSAndreas Gohr
97e348ef7cSAndreas Gohr        // 2 points for missing backlinks
982fc45e0cSsplitbrain        if (ft_backlinks($ID) === []) {
997db0beafSAnna Dabrowska            $this->docArray['err']['nobacklink'] += 2;
100e348ef7cSAndreas Gohr        }
1018fce80b1SAndreas Gohr
1028fce80b1SAndreas Gohr        // 1 point for each FIXME
1037db0beafSAnna Dabrowska        $this->docArray['err']['fixme'] += $this->docArray['fixme'];
1048fce80b1SAndreas Gohr
1058fce80b1SAndreas Gohr        // 5 points for missing H1
1067db0beafSAnna Dabrowska        if ($this->docArray['header_count'][1] == 0) {
1077db0beafSAnna Dabrowska            $this->docArray['err']['noh1'] += 5;
1088fce80b1SAndreas Gohr        }
1098fce80b1SAndreas Gohr        // 1 point for each H1 too much
1107db0beafSAnna Dabrowska        if ($this->docArray['header_count'][1] > 1) {
1117db0beafSAnna Dabrowska            $this->docArray['err']['manyh1'] += $this->docArray['header'][1];
1128fce80b1SAndreas Gohr        }
1138fce80b1SAndreas Gohr
1148fce80b1SAndreas Gohr        // 1 point for each incorrectly nested headline
1157db0beafSAnna Dabrowska        $cnt = count($this->docArray['header_struct']);
1168fce80b1SAndreas Gohr        for ($i = 1; $i < $cnt; $i++) {
1177db0beafSAnna Dabrowska            if ($this->docArray['header_struct'][$i] - $this->docArray['header_struct'][$i - 1] > 1) {
1182fc45e0cSsplitbrain                ++$this->docArray['err']['headernest'];
1198fce80b1SAndreas Gohr            }
1208fce80b1SAndreas Gohr        }
1218fce80b1SAndreas Gohr
1228fce80b1SAndreas Gohr        // 1/2 points for deeply nested quotations
1237db0beafSAnna Dabrowska        if ($this->docArray['quote_nest'] > 2) {
124af8294b9SAndreas Gohr            $this->docArray['err']['deepquote'] = $this->docArray['quote_nest'] / 2;
1258fce80b1SAndreas Gohr        }
1268fce80b1SAndreas Gohr
1278fce80b1SAndreas Gohr        // FIXME points for many quotes?
1288fce80b1SAndreas Gohr
1298fce80b1SAndreas Gohr        // 1/2 points for too many hr
1307db0beafSAnna Dabrowska        if ($this->docArray['hr'] > 2) {
1317db0beafSAnna Dabrowska            $this->docArray['err']['manyhr'] = ($this->docArray['hr'] - 2) / 2;
1328fce80b1SAndreas Gohr        }
1338fce80b1SAndreas Gohr
1348fce80b1SAndreas Gohr        // 1 point for too many line breaks
1357db0beafSAnna Dabrowska        if ($this->docArray['linebreak'] > 2) {
1367db0beafSAnna Dabrowska            $this->docArray['err']['manybr'] = $this->docArray['linebreak'] - 2;
1378fce80b1SAndreas Gohr        }
1388fce80b1SAndreas Gohr
1394ea373cdSAndreas Gohr        // 1 point for single author only
1407db0beafSAnna Dabrowska        if (!$this->getConf('single_author_only') && count($this->docArray['authors']) == 1) {
1417db0beafSAnna Dabrowska            $this->docArray['err']['singleauthor'] = 1;
1424ea373cdSAndreas Gohr        }
1434ea373cdSAndreas Gohr
144d723b313SAndreas Gohr        // 1 point for too small document
1457db0beafSAnna Dabrowska        if ($this->docArray['chars'] < 150) {
1467db0beafSAnna Dabrowska            $this->docArray['err']['toosmall'] = 1;
147d723b313SAndreas Gohr        }
148d723b313SAndreas Gohr
149d723b313SAndreas Gohr        // 1 point for too large document
1507db0beafSAnna Dabrowska        if ($this->docArray['chars'] > 100000) {
1517db0beafSAnna Dabrowska            $this->docArray['err']['toolarge'] = 1;
152d723b313SAndreas Gohr        }
153d723b313SAndreas Gohr
154d723b313SAndreas Gohr        // header to text ratio
1557db0beafSAnna Dabrowska        $hc = $this->docArray['header_count'][1] +
1567db0beafSAnna Dabrowska            $this->docArray['header_count'][2] +
1577db0beafSAnna Dabrowska            $this->docArray['header_count'][3] +
1587db0beafSAnna Dabrowska            $this->docArray['header_count'][4] +
1597db0beafSAnna Dabrowska            $this->docArray['header_count'][5];
16013abf7ccSAndreas Gohr        $hc--; //we expect at least 1
16113abf7ccSAndreas Gohr        if ($hc > 0) {
1627db0beafSAnna Dabrowska            $hr = $this->docArray['chars'] / $hc;
163d723b313SAndreas Gohr
164d723b313SAndreas Gohr            // 1 point for too many headers
165d723b313SAndreas Gohr            if ($hr < 200) {
1667db0beafSAnna Dabrowska                $this->docArray['err']['manyheaders'] = 1;
167d723b313SAndreas Gohr            }
168d723b313SAndreas Gohr
169d723b313SAndreas Gohr            // 1 point for too few headers
1701b085f03SAndreas Gohr            if ($hr > 2000) {
1717db0beafSAnna Dabrowska                $this->docArray['err']['fewheaders'] = 1;
172d723b313SAndreas Gohr            }
173d723b313SAndreas Gohr        }
174d723b313SAndreas Gohr
175d723b313SAndreas Gohr        // 1 point when no link at all
1767db0beafSAnna Dabrowska        if (!$this->docArray['internal_links']) {
1777db0beafSAnna Dabrowska            $this->docArray['err']['nolink'] = 1;
178d723b313SAndreas Gohr        }
179d723b313SAndreas Gohr
180d723b313SAndreas Gohr        // 0.5 for broken links when too many
1817db0beafSAnna Dabrowska        if ($this->docArray['broken_links'] > 2) {
1827db0beafSAnna Dabrowska            $this->docArray['err']['brokenlink'] = $this->docArray['broken_links'] * 0.5;
183d723b313SAndreas Gohr        }
184d723b313SAndreas Gohr
185d723b313SAndreas Gohr        // 2 points for lot's of formatting
1867db0beafSAnna Dabrowska        if ($this->docArray['formatted'] && $this->docArray['chars'] / $this->docArray['formatted'] < 3) {
1877db0beafSAnna Dabrowska            $this->docArray['err']['manyformat'] = 2;
188d723b313SAndreas Gohr        }
189d723b313SAndreas Gohr
1909068e431SAndreas Gohr        // add up all scores
1912fc45e0cSsplitbrain        foreach ($this->docArray['err'] as $val) $this->docArray['score'] += $val;
1929068e431SAndreas Gohr
1934ea373cdSAndreas Gohr
1948fce80b1SAndreas Gohr        //we're done here
1957db0beafSAnna Dabrowska        $this->doc = serialize($this->docArray);
1968fce80b1SAndreas Gohr    }
1978fce80b1SAndreas Gohr
1988ae469bfSAndreas Gohr    /** @inheritdoc */
19976bbc49cSAnna Dabrowska    public function getFormat()
20076bbc49cSAnna Dabrowska    {
2018fce80b1SAndreas Gohr        return 'qc';
2028fce80b1SAndreas Gohr    }
2038fce80b1SAndreas Gohr
2048ae469bfSAndreas Gohr    /** @inheritdoc */
20576bbc49cSAnna Dabrowska    public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content')
20676bbc49cSAnna Dabrowska    {
207d723b313SAndreas Gohr        global $ID;
2088ae469bfSAndreas Gohr
2092fc45e0cSsplitbrain        $resolver = new PageResolver($ID);
2108ae469bfSAndreas Gohr        $id = $resolver->resolveId($id);
2118ae469bfSAndreas Gohr        $exists = page_exists($id);
212d723b313SAndreas Gohr
2138d7cf088SAndreas Gohr        // calculate link width
214d78155a4SAndreas Gohr        $a = explode(':', getNS($ID));
215d78155a4SAndreas Gohr        $b = explode(':', getNS($id));
2162fc45e0cSsplitbrain        while (isset($a[0]) && $a[0] === $b[0]) {
2178d7cf088SAndreas Gohr            array_shift($a);
2188d7cf088SAndreas Gohr            array_shift($b);
2198d7cf088SAndreas Gohr        }
220d78155a4SAndreas Gohr        $length = count($a) + count($b);
2217db0beafSAnna Dabrowska        $this->docArray['link_lengths'][] = $length;
2228d7cf088SAndreas Gohr
2237db0beafSAnna Dabrowska        $this->docArray['internal_links']++;
2247db0beafSAnna Dabrowska        if (!$exists) $this->docArray['broken_links']++;
2250476d180SAndreas Gohr    }
2260476d180SAndreas Gohr
2278ae469bfSAndreas Gohr    /** @inheritdoc */
22876bbc49cSAnna Dabrowska    public function externallink($url, $name = null)
22976bbc49cSAnna Dabrowska    {
2307db0beafSAnna Dabrowska        $this->docArray['external_links']++;
231d723b313SAndreas Gohr    }
2328fce80b1SAndreas Gohr
2338ae469bfSAndreas Gohr    /** @inheritdoc */
23476bbc49cSAnna Dabrowska    public function header($text, $level, $pos)
23576bbc49cSAnna Dabrowska    {
2367db0beafSAnna Dabrowska        $this->docArray['header_count'][$level]++;
2377db0beafSAnna Dabrowska        $this->docArray['header_struct'][] = $level;
2388fce80b1SAndreas Gohr    }
2398fce80b1SAndreas Gohr
2408ae469bfSAndreas Gohr    /** @inheritdoc */
24176bbc49cSAnna Dabrowska    public function smiley($smiley)
24276bbc49cSAnna Dabrowska    {
2437db0beafSAnna Dabrowska        if ($smiley == 'FIXME') $this->docArray['fixme']++;
2448fce80b1SAndreas Gohr    }
2458fce80b1SAndreas Gohr
2468ae469bfSAndreas Gohr    /** @inheritdoc */
24776bbc49cSAnna Dabrowska    public function linebreak()
24876bbc49cSAnna Dabrowska    {
2496afc1841Sthesunrise1983        if (!$this->tableopen) {
2507db0beafSAnna Dabrowska            $this->docArray['linebreak']++;
2518fce80b1SAndreas Gohr        }
2526afc1841Sthesunrise1983    }
2536afc1841Sthesunrise1983
2548ae469bfSAndreas Gohr    /** @inheritdoc */
255*c8d45e58SAndreas Gohr    public function table_open($maxcols = null, $numrows = null, $pos = null)
25676bbc49cSAnna Dabrowska    {
2576afc1841Sthesunrise1983        $this->tableopen = true;
2586afc1841Sthesunrise1983    }
2596afc1841Sthesunrise1983
2608ae469bfSAndreas Gohr    /** @inheritdoc */
261*c8d45e58SAndreas Gohr    public function table_close($pos = null)
26276bbc49cSAnna Dabrowska    {
2636afc1841Sthesunrise1983        $this->tableopen = false;
2646afc1841Sthesunrise1983    }
2658fce80b1SAndreas Gohr
2668ae469bfSAndreas Gohr    /** @inheritdoc */
26776bbc49cSAnna Dabrowska    public function hr()
26876bbc49cSAnna Dabrowska    {
2697db0beafSAnna Dabrowska        $this->docArray['hr']++;
2708fce80b1SAndreas Gohr    }
2718fce80b1SAndreas Gohr
2728ae469bfSAndreas Gohr    /** @inheritdoc */
273*c8d45e58SAndreas Gohr    public function quote_open()
27476bbc49cSAnna Dabrowska    {
2757db0beafSAnna Dabrowska        $this->docArray['quote_count']++;
2768fce80b1SAndreas Gohr        $this->quotelevel++;
2777db0beafSAnna Dabrowska        $this->docArray['quote_nest'] = max($this->quotelevel, $this->docArray['quote_nest']);
2788fce80b1SAndreas Gohr    }
2798fce80b1SAndreas Gohr
2808ae469bfSAndreas Gohr    /** @inheritdoc */
281*c8d45e58SAndreas Gohr    public function quote_close()
28276bbc49cSAnna Dabrowska    {
2838fce80b1SAndreas Gohr        $this->quotelevel--;
2848fce80b1SAndreas Gohr    }
2858fce80b1SAndreas Gohr
2868ae469bfSAndreas Gohr    /** @inheritdoc */
287*c8d45e58SAndreas Gohr    public function strong_open()
28876bbc49cSAnna Dabrowska    {
289d723b313SAndreas Gohr        $this->formatting++;
290d723b313SAndreas Gohr    }
2918fce80b1SAndreas Gohr
2928ae469bfSAndreas Gohr    /** @inheritdoc */
293*c8d45e58SAndreas Gohr    public function strong_close()
29476bbc49cSAnna Dabrowska    {
295d723b313SAndreas Gohr        $this->formatting--;
296d723b313SAndreas Gohr    }
297d723b313SAndreas Gohr
2988ae469bfSAndreas Gohr    /** @inheritdoc */
299*c8d45e58SAndreas Gohr    public function emphasis_open()
30076bbc49cSAnna Dabrowska    {
301d723b313SAndreas Gohr        $this->formatting++;
302d723b313SAndreas Gohr    }
303d723b313SAndreas Gohr
3048ae469bfSAndreas Gohr    /** @inheritdoc */
305*c8d45e58SAndreas Gohr    public function emphasis_close()
30676bbc49cSAnna Dabrowska    {
307d723b313SAndreas Gohr        $this->formatting--;
308d723b313SAndreas Gohr    }
309d723b313SAndreas Gohr
3108ae469bfSAndreas Gohr    /** @inheritdoc */
311*c8d45e58SAndreas Gohr    public function underline_open()
31276bbc49cSAnna Dabrowska    {
313d723b313SAndreas Gohr        $this->formatting++;
314d723b313SAndreas Gohr    }
315d723b313SAndreas Gohr
3168ae469bfSAndreas Gohr    /** @inheritdoc */
317*c8d45e58SAndreas Gohr    public function underline_close()
31876bbc49cSAnna Dabrowska    {
319d723b313SAndreas Gohr        $this->formatting--;
320d723b313SAndreas Gohr    }
321d723b313SAndreas Gohr
3228ae469bfSAndreas Gohr    /** @inheritdoc */
32376bbc49cSAnna Dabrowska    public function cdata($text)
32476bbc49cSAnna Dabrowska    {
325d723b313SAndreas Gohr        if (!$this->formatting) return;
326d723b313SAndreas Gohr
3278ae469bfSAndreas Gohr        $len = PhpString::strlen($text);
328d723b313SAndreas Gohr
329d723b313SAndreas Gohr        // 1 point for formattings longer than 500 chars
3307db0beafSAnna Dabrowska        if ($len > 500) $this->docArray['err']['longformat']++;
331d723b313SAndreas Gohr
3324bda998cSAndreas Gohr        // 1 point for each multiformatting
3337db0beafSAnna Dabrowska        if ($this->formatting > 1) $this->docArray['err']['multiformat'] += 1 * ($this->formatting - 1);
3344bda998cSAndreas Gohr
3357db0beafSAnna Dabrowska        $this->docArray['formatted'] += $len;
336d723b313SAndreas Gohr    }
3378fce80b1SAndreas Gohr}
338