xref: /plugin/qc/renderer.php (revision af8294b96197f7485f1ad508ea94a00e72edd33b)
18fce80b1SAndreas Gohr<?php
276bbc49cSAnna Dabrowska
38ae469bfSAndreas Gohruse dokuwiki\Utf8\PhpString;
48ae469bfSAndreas Gohr
58fce80b1SAndreas Gohr/**
68fce80b1SAndreas Gohr * The Renderer
78fce80b1SAndreas Gohr */
876bbc49cSAnna Dabrowskaclass renderer_plugin_qc extends Doku_Renderer
976bbc49cSAnna Dabrowska{
108fce80b1SAndreas Gohr    /**
118fce80b1SAndreas Gohr     * We store all our data in an array
128fce80b1SAndreas Gohr     */
137db0beafSAnna Dabrowska    public $docArray = array(
148fce80b1SAndreas Gohr        // raw statistics
158fce80b1SAndreas Gohr        'header_count' => array(0, 0, 0, 0, 0, 0),
168fce80b1SAndreas Gohr        'header_struct' => array(),
178fce80b1SAndreas Gohr        'linebreak' => 0,
188fce80b1SAndreas Gohr        'quote_nest' => 0,
198fce80b1SAndreas Gohr        'quote_count' => 0,
208fce80b1SAndreas Gohr        'fixme' => 0,
218fce80b1SAndreas Gohr        'hr' => 0,
22d723b313SAndreas Gohr        'formatted' => 0,
238fce80b1SAndreas Gohr
244ea373cdSAndreas Gohr        'created' => 0,
254ea373cdSAndreas Gohr        'modified' => 0,
264ea373cdSAndreas Gohr        'changes' => 0,
274ea373cdSAndreas Gohr        'authors' => array(),
284ea373cdSAndreas Gohr
29d723b313SAndreas Gohr        'internal_links' => 0,
30d723b313SAndreas Gohr        'broken_links' => 0,
31d723b313SAndreas Gohr        'external_links' => 0,
328d7cf088SAndreas Gohr        'link_lengths' => array(),
33d723b313SAndreas Gohr
344ea373cdSAndreas Gohr        'chars' => 0,
354ea373cdSAndreas Gohr        'words' => 0,
364ea373cdSAndreas Gohr
379068e431SAndreas Gohr        'score' => 0,
389068e431SAndreas Gohr
398fce80b1SAndreas Gohr        // calculated error scores
408fce80b1SAndreas Gohr        'err' => array(
418fce80b1SAndreas Gohr            'fixme' => 0,
428fce80b1SAndreas Gohr            'noh1' => 0,
438fce80b1SAndreas Gohr            'manyh1' => 0,
448fce80b1SAndreas Gohr            'headernest' => 0,
458fce80b1SAndreas Gohr            'manyhr' => 0,
468fce80b1SAndreas Gohr            'manybr' => 0,
47d723b313SAndreas Gohr            'longformat' => 0,
484bda998cSAndreas Gohr            'multiformat' => 0,
498fce80b1SAndreas Gohr        ),
508fce80b1SAndreas Gohr    );
518fce80b1SAndreas Gohr
527db0beafSAnna Dabrowska    protected $quotelevel = 0;
537db0beafSAnna Dabrowska    protected $formatting = 0;
547db0beafSAnna Dabrowska    protected $tableopen = false;
558fce80b1SAndreas Gohr
568ae469bfSAndreas Gohr    /** @inheritdoc */
57293182bbSAnna Dabrowska    public function document_start() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
5876bbc49cSAnna Dabrowska    {
594ea373cdSAndreas Gohr        global $ID;
604ea373cdSAndreas Gohr        $meta = p_get_metadata($ID);
614ea373cdSAndreas Gohr
624ea373cdSAndreas Gohr        // get some dates from meta data
637db0beafSAnna Dabrowska        $this->docArray['created'] = $meta['date']['created'];
647db0beafSAnna Dabrowska        $this->docArray['modified'] = $meta['date']['modified'];
658fdbbd20SAnna Dabrowska        $this->docArray['authors']['*'] = 0;
664ea373cdSAndreas Gohr
674ea373cdSAndreas Gohr        // get author info
688ae469bfSAndreas Gohr        $changelog = new \dokuwiki\ChangeLog\PageChangeLog($ID);
695929c83eSMichael Große        $revs = $changelog->getRevisions(0, 10000); //FIXME find a good solution for 'get ALL revisions'
708ae469bfSAndreas Gohr        $revs[] = $meta['last_change']['date'];
717db0beafSAnna Dabrowska        $this->docArray['changes'] = count($revs);
724ea373cdSAndreas Gohr        foreach ($revs as $rev) {
73088a1fe7SMichael Grosse            $info = $changelog->getRevisionInfo($rev);
748ae469bfSAndreas Gohr            if ($info && !empty($info['user'])) {
758fdbbd20SAnna Dabrowska                $authorUserCnt = !empty($this->docArray['authors'][$info['user']])
768fdbbd20SAnna Dabrowska                    ? $this->docArray['authors'][$info['user']]
778fdbbd20SAnna Dabrowska                    : 0;
788fdbbd20SAnna Dabrowska                $this->docArray['authors'][$info['user']] = $authorUserCnt + 1;
794ea373cdSAndreas Gohr            } else {
807db0beafSAnna Dabrowska                $this->docArray['authors']['*'] += 1;
814ea373cdSAndreas Gohr            }
824ea373cdSAndreas Gohr        }
834ea373cdSAndreas Gohr
844ea373cdSAndreas Gohr        // work on raw text
854ea373cdSAndreas Gohr        $text = rawWiki($ID);
867db0beafSAnna Dabrowska        $this->docArray['chars'] = utf8_strlen($text);
877db0beafSAnna Dabrowska        $this->docArray['words'] = count(array_filter(preg_split('/[^\w\-_]/u', $text)));
884ea373cdSAndreas Gohr    }
894ea373cdSAndreas Gohr
904ea373cdSAndreas Gohr
918fce80b1SAndreas Gohr    /**
928fce80b1SAndreas Gohr     * Here the score is calculated
938ae469bfSAndreas Gohr     * @inheritdoc
948fce80b1SAndreas Gohr     */
95293182bbSAnna Dabrowska    public function document_end() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
9676bbc49cSAnna Dabrowska    {
97e348ef7cSAndreas Gohr        global $ID;
98e348ef7cSAndreas Gohr
99e348ef7cSAndreas Gohr        // 2 points for missing backlinks
100e348ef7cSAndreas Gohr        if (!count(ft_backlinks($ID))) {
1017db0beafSAnna Dabrowska            $this->docArray['err']['nobacklink'] += 2;
102e348ef7cSAndreas Gohr        }
1038fce80b1SAndreas Gohr
1048fce80b1SAndreas Gohr        // 1 point for each FIXME
1057db0beafSAnna Dabrowska        $this->docArray['err']['fixme'] += $this->docArray['fixme'];
1068fce80b1SAndreas Gohr
1078fce80b1SAndreas Gohr        // 5 points for missing H1
1087db0beafSAnna Dabrowska        if ($this->docArray['header_count'][1] == 0) {
1097db0beafSAnna Dabrowska            $this->docArray['err']['noh1'] += 5;
1108fce80b1SAndreas Gohr        }
1118fce80b1SAndreas Gohr        // 1 point for each H1 too much
1127db0beafSAnna Dabrowska        if ($this->docArray['header_count'][1] > 1) {
1137db0beafSAnna Dabrowska            $this->docArray['err']['manyh1'] += $this->docArray['header'][1];
1148fce80b1SAndreas Gohr        }
1158fce80b1SAndreas Gohr
1168fce80b1SAndreas Gohr        // 1 point for each incorrectly nested headline
1177db0beafSAnna Dabrowska        $cnt = count($this->docArray['header_struct']);
1188fce80b1SAndreas Gohr        for ($i = 1; $i < $cnt; $i++) {
1197db0beafSAnna Dabrowska            if ($this->docArray['header_struct'][$i] - $this->docArray['header_struct'][$i - 1] > 1) {
1207db0beafSAnna Dabrowska                $this->docArray['err']['headernest'] += 1;
1218fce80b1SAndreas Gohr            }
1228fce80b1SAndreas Gohr        }
1238fce80b1SAndreas Gohr
1248fce80b1SAndreas Gohr        // 1/2 points for deeply nested quotations
1257db0beafSAnna Dabrowska        if ($this->docArray['quote_nest'] > 2) {
126*af8294b9SAndreas Gohr            $this->docArray['err']['deepquote'] = $this->docArray['quote_nest'] / 2;
1278fce80b1SAndreas Gohr        }
1288fce80b1SAndreas Gohr
1298fce80b1SAndreas Gohr        // FIXME points for many quotes?
1308fce80b1SAndreas Gohr
1318fce80b1SAndreas Gohr        // 1/2 points for too many hr
1327db0beafSAnna Dabrowska        if ($this->docArray['hr'] > 2) {
1337db0beafSAnna Dabrowska            $this->docArray['err']['manyhr'] = ($this->docArray['hr'] - 2) / 2;
1348fce80b1SAndreas Gohr        }
1358fce80b1SAndreas Gohr
1368fce80b1SAndreas Gohr        // 1 point for too many line breaks
1377db0beafSAnna Dabrowska        if ($this->docArray['linebreak'] > 2) {
1387db0beafSAnna Dabrowska            $this->docArray['err']['manybr'] = $this->docArray['linebreak'] - 2;
1398fce80b1SAndreas Gohr        }
1408fce80b1SAndreas Gohr
1414ea373cdSAndreas Gohr        // 1 point for single author only
1427db0beafSAnna Dabrowska        if (!$this->getConf('single_author_only') && count($this->docArray['authors']) == 1) {
1437db0beafSAnna Dabrowska            $this->docArray['err']['singleauthor'] = 1;
1444ea373cdSAndreas Gohr        }
1454ea373cdSAndreas Gohr
146d723b313SAndreas Gohr        // 1 point for too small document
1477db0beafSAnna Dabrowska        if ($this->docArray['chars'] < 150) {
1487db0beafSAnna Dabrowska            $this->docArray['err']['toosmall'] = 1;
149d723b313SAndreas Gohr        }
150d723b313SAndreas Gohr
151d723b313SAndreas Gohr        // 1 point for too large document
1527db0beafSAnna Dabrowska        if ($this->docArray['chars'] > 100000) {
1537db0beafSAnna Dabrowska            $this->docArray['err']['toolarge'] = 1;
154d723b313SAndreas Gohr        }
155d723b313SAndreas Gohr
156d723b313SAndreas Gohr        // header to text ratio
1577db0beafSAnna Dabrowska        $hc = $this->docArray['header_count'][1] +
1587db0beafSAnna Dabrowska            $this->docArray['header_count'][2] +
1597db0beafSAnna Dabrowska            $this->docArray['header_count'][3] +
1607db0beafSAnna Dabrowska            $this->docArray['header_count'][4] +
1617db0beafSAnna Dabrowska            $this->docArray['header_count'][5];
16213abf7ccSAndreas Gohr        $hc--; //we expect at least 1
16313abf7ccSAndreas Gohr        if ($hc > 0) {
1647db0beafSAnna Dabrowska            $hr = $this->docArray['chars'] / $hc;
165d723b313SAndreas Gohr
166d723b313SAndreas Gohr            // 1 point for too many headers
167d723b313SAndreas Gohr            if ($hr < 200) {
1687db0beafSAnna Dabrowska                $this->docArray['err']['manyheaders'] = 1;
169d723b313SAndreas Gohr            }
170d723b313SAndreas Gohr
171d723b313SAndreas Gohr            // 1 point for too few headers
1721b085f03SAndreas Gohr            if ($hr > 2000) {
1737db0beafSAnna Dabrowska                $this->docArray['err']['fewheaders'] = 1;
174d723b313SAndreas Gohr            }
175d723b313SAndreas Gohr        }
176d723b313SAndreas Gohr
177d723b313SAndreas Gohr        // 1 point when no link at all
1787db0beafSAnna Dabrowska        if (!$this->docArray['internal_links']) {
1797db0beafSAnna Dabrowska            $this->docArray['err']['nolink'] = 1;
180d723b313SAndreas Gohr        }
181d723b313SAndreas Gohr
182d723b313SAndreas Gohr        // 0.5 for broken links when too many
1837db0beafSAnna Dabrowska        if ($this->docArray['broken_links'] > 2) {
1847db0beafSAnna Dabrowska            $this->docArray['err']['brokenlink'] = $this->docArray['broken_links'] * 0.5;
185d723b313SAndreas Gohr        }
186d723b313SAndreas Gohr
187d723b313SAndreas Gohr        // 2 points for lot's of formatting
1887db0beafSAnna Dabrowska        if ($this->docArray['formatted'] && $this->docArray['chars'] / $this->docArray['formatted'] < 3) {
1897db0beafSAnna Dabrowska            $this->docArray['err']['manyformat'] = 2;
190d723b313SAndreas Gohr        }
191d723b313SAndreas Gohr
1929068e431SAndreas Gohr        // add up all scores
1937db0beafSAnna Dabrowska        foreach ($this->docArray['err'] as $err => $val) $this->docArray['score'] += $val;
1949068e431SAndreas Gohr
1954ea373cdSAndreas Gohr
1968fce80b1SAndreas Gohr        //we're done here
1977db0beafSAnna Dabrowska        $this->doc = serialize($this->docArray);
1988fce80b1SAndreas Gohr    }
1998fce80b1SAndreas Gohr
2008ae469bfSAndreas Gohr    /** @inheritdoc */
20176bbc49cSAnna Dabrowska    public function getFormat()
20276bbc49cSAnna Dabrowska    {
2038fce80b1SAndreas Gohr        return 'qc';
2048fce80b1SAndreas Gohr    }
2058fce80b1SAndreas Gohr
2068ae469bfSAndreas Gohr    /** @inheritdoc */
20776bbc49cSAnna Dabrowska    public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content')
20876bbc49cSAnna Dabrowska    {
209d723b313SAndreas Gohr        global $ID;
2108ae469bfSAndreas Gohr
2118ae469bfSAndreas Gohr        $resolver = new \dokuwiki\File\PageResolver($ID);
2128ae469bfSAndreas Gohr        $id = $resolver->resolveId($id);
2138ae469bfSAndreas Gohr        $exists = page_exists($id);
214d723b313SAndreas Gohr
2158d7cf088SAndreas Gohr        // calculate link width
216d78155a4SAndreas Gohr        $a = explode(':', getNS($ID));
217d78155a4SAndreas Gohr        $b = explode(':', getNS($id));
218d78155a4SAndreas Gohr        while (isset($a[0]) && $a[0] == $b[0]) {
2198d7cf088SAndreas Gohr            array_shift($a);
2208d7cf088SAndreas Gohr            array_shift($b);
2218d7cf088SAndreas Gohr        }
222d78155a4SAndreas Gohr        $length = count($a) + count($b);
2237db0beafSAnna Dabrowska        $this->docArray['link_lengths'][] = $length;
2248d7cf088SAndreas Gohr
2257db0beafSAnna Dabrowska        $this->docArray['internal_links']++;
2267db0beafSAnna Dabrowska        if (!$exists) $this->docArray['broken_links']++;
2270476d180SAndreas Gohr    }
2280476d180SAndreas Gohr
2298ae469bfSAndreas Gohr    /** @inheritdoc */
23076bbc49cSAnna Dabrowska    public function externallink($url, $name = null)
23176bbc49cSAnna Dabrowska    {
2327db0beafSAnna Dabrowska        $this->docArray['external_links']++;
233d723b313SAndreas Gohr    }
2348fce80b1SAndreas Gohr
2358ae469bfSAndreas Gohr    /** @inheritdoc */
23676bbc49cSAnna Dabrowska    public function header($text, $level, $pos)
23776bbc49cSAnna Dabrowska    {
2387db0beafSAnna Dabrowska        $this->docArray['header_count'][$level]++;
2397db0beafSAnna Dabrowska        $this->docArray['header_struct'][] = $level;
2408fce80b1SAndreas Gohr    }
2418fce80b1SAndreas Gohr
2428ae469bfSAndreas Gohr    /** @inheritdoc */
24376bbc49cSAnna Dabrowska    public function smiley($smiley)
24476bbc49cSAnna Dabrowska    {
2457db0beafSAnna Dabrowska        if ($smiley == 'FIXME') $this->docArray['fixme']++;
2468fce80b1SAndreas Gohr    }
2478fce80b1SAndreas Gohr
2488ae469bfSAndreas Gohr    /** @inheritdoc */
24976bbc49cSAnna Dabrowska    public function linebreak()
25076bbc49cSAnna Dabrowska    {
2516afc1841Sthesunrise1983        if (!$this->tableopen) {
2527db0beafSAnna Dabrowska            $this->docArray['linebreak']++;
2538fce80b1SAndreas Gohr        }
2546afc1841Sthesunrise1983    }
2556afc1841Sthesunrise1983
2568ae469bfSAndreas Gohr    /** @inheritdoc */
257293182bbSAnna Dabrowska    public function table_open($maxcols = null, $numrows = null, $pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
25876bbc49cSAnna Dabrowska    {
2596afc1841Sthesunrise1983        $this->tableopen = true;
2606afc1841Sthesunrise1983    }
2616afc1841Sthesunrise1983
2628ae469bfSAndreas Gohr    /** @inheritdoc */
263293182bbSAnna Dabrowska    public function table_close($pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
26476bbc49cSAnna Dabrowska    {
2656afc1841Sthesunrise1983        $this->tableopen = false;
2666afc1841Sthesunrise1983    }
2678fce80b1SAndreas Gohr
2688ae469bfSAndreas Gohr    /** @inheritdoc */
26976bbc49cSAnna Dabrowska    public function hr()
27076bbc49cSAnna Dabrowska    {
2717db0beafSAnna Dabrowska        $this->docArray['hr']++;
2728fce80b1SAndreas Gohr    }
2738fce80b1SAndreas Gohr
2748ae469bfSAndreas Gohr    /** @inheritdoc */
275293182bbSAnna Dabrowska    public function quote_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
27676bbc49cSAnna Dabrowska    {
2777db0beafSAnna Dabrowska        $this->docArray['quote_count']++;
2788fce80b1SAndreas Gohr        $this->quotelevel++;
2797db0beafSAnna Dabrowska        $this->docArray['quote_nest'] = max($this->quotelevel, $this->docArray['quote_nest']);
2808fce80b1SAndreas Gohr    }
2818fce80b1SAndreas Gohr
2828ae469bfSAndreas Gohr    /** @inheritdoc */
283293182bbSAnna Dabrowska    public function quote_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
28476bbc49cSAnna Dabrowska    {
2858fce80b1SAndreas Gohr        $this->quotelevel--;
2868fce80b1SAndreas Gohr    }
2878fce80b1SAndreas Gohr
2888ae469bfSAndreas Gohr    /** @inheritdoc */
289293182bbSAnna Dabrowska    public function strong_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
29076bbc49cSAnna Dabrowska    {
291d723b313SAndreas Gohr        $this->formatting++;
292d723b313SAndreas Gohr    }
2938fce80b1SAndreas Gohr
2948ae469bfSAndreas Gohr    /** @inheritdoc */
295293182bbSAnna Dabrowska    public function strong_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
29676bbc49cSAnna Dabrowska    {
297d723b313SAndreas Gohr        $this->formatting--;
298d723b313SAndreas Gohr    }
299d723b313SAndreas Gohr
3008ae469bfSAndreas Gohr    /** @inheritdoc */
301293182bbSAnna Dabrowska    public function emphasis_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
30276bbc49cSAnna Dabrowska    {
303d723b313SAndreas Gohr        $this->formatting++;
304d723b313SAndreas Gohr    }
305d723b313SAndreas Gohr
3068ae469bfSAndreas Gohr    /** @inheritdoc */
307293182bbSAnna Dabrowska    public function emphasis_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
30876bbc49cSAnna Dabrowska    {
309d723b313SAndreas Gohr        $this->formatting--;
310d723b313SAndreas Gohr    }
311d723b313SAndreas Gohr
3128ae469bfSAndreas Gohr    /** @inheritdoc */
313293182bbSAnna Dabrowska    public function underline_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
31476bbc49cSAnna Dabrowska    {
315d723b313SAndreas Gohr        $this->formatting++;
316d723b313SAndreas Gohr    }
317d723b313SAndreas Gohr
3188ae469bfSAndreas Gohr    /** @inheritdoc */
319293182bbSAnna Dabrowska    public function underline_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
32076bbc49cSAnna Dabrowska    {
321d723b313SAndreas Gohr        $this->formatting--;
322d723b313SAndreas Gohr    }
323d723b313SAndreas Gohr
3248ae469bfSAndreas Gohr    /** @inheritdoc */
32576bbc49cSAnna Dabrowska    public function cdata($text)
32676bbc49cSAnna Dabrowska    {
327d723b313SAndreas Gohr        if (!$this->formatting) return;
328d723b313SAndreas Gohr
3298ae469bfSAndreas Gohr        $len = PhpString::strlen($text);
330d723b313SAndreas Gohr
331d723b313SAndreas Gohr        // 1 point for formattings longer than 500 chars
3327db0beafSAnna Dabrowska        if ($len > 500) $this->docArray['err']['longformat']++;
333d723b313SAndreas Gohr
3344bda998cSAndreas Gohr        // 1 point for each multiformatting
3357db0beafSAnna Dabrowska        if ($this->formatting > 1) $this->docArray['err']['multiformat'] += 1 * ($this->formatting - 1);
3364bda998cSAndreas Gohr
3377db0beafSAnna Dabrowska        $this->docArray['formatted'] += $len;
338d723b313SAndreas Gohr    }
3398fce80b1SAndreas Gohr}
340