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