18fce80b1SAndreas Gohr<?php 2*3c4e5ff0Ssplitbrain 3c8d45e58SAndreas Gohr// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps 476bbc49cSAnna Dabrowska 52fc45e0cSsplitbrainuse dokuwiki\ChangeLog\PageChangeLog; 62fc45e0cSsplitbrainuse dokuwiki\File\PageResolver; 78ae469bfSAndreas Gohruse dokuwiki\Utf8\PhpString; 88ae469bfSAndreas Gohr 98fce80b1SAndreas Gohr/** 108fce80b1SAndreas Gohr * The Renderer 118fce80b1SAndreas Gohr */ 1276bbc49cSAnna Dabrowskaclass renderer_plugin_qc extends Doku_Renderer 1376bbc49cSAnna Dabrowska{ 148fce80b1SAndreas Gohr /** 158fce80b1SAndreas Gohr * We store all our data in an array 168fce80b1SAndreas Gohr */ 172fc45e0cSsplitbrain public $docArray = [ 188fce80b1SAndreas Gohr // raw statistics 192fc45e0cSsplitbrain 'header_count' => [0, 0, 0, 0, 0, 0], 202fc45e0cSsplitbrain 'header_struct' => [], 218fce80b1SAndreas Gohr 'linebreak' => 0, 228fce80b1SAndreas Gohr 'quote_nest' => 0, 238fce80b1SAndreas Gohr 'quote_count' => 0, 248fce80b1SAndreas Gohr 'fixme' => 0, 258fce80b1SAndreas Gohr 'hr' => 0, 26d723b313SAndreas Gohr 'formatted' => 0, 274ea373cdSAndreas Gohr 'created' => 0, 284ea373cdSAndreas Gohr 'modified' => 0, 294ea373cdSAndreas Gohr 'changes' => 0, 302fc45e0cSsplitbrain 'authors' => [], 31d723b313SAndreas Gohr 'internal_links' => 0, 32d723b313SAndreas Gohr 'broken_links' => 0, 33d723b313SAndreas Gohr 'external_links' => 0, 342fc45e0cSsplitbrain 'link_lengths' => [], 354ea373cdSAndreas Gohr 'chars' => 0, 364ea373cdSAndreas Gohr 'words' => 0, 379068e431SAndreas Gohr 'score' => 0, 388fce80b1SAndreas Gohr // calculated error scores 39c8d45e58SAndreas Gohr 'err' => [ 40c8d45e58SAndreas Gohr 'fixme' => 0, 41c8d45e58SAndreas Gohr 'noh1' => 0, 42c8d45e58SAndreas Gohr 'manyh1' => 0, 43c8d45e58SAndreas Gohr 'headernest' => 0, 44c8d45e58SAndreas Gohr 'manyhr' => 0, 45c8d45e58SAndreas Gohr 'manybr' => 0, 46c8d45e58SAndreas Gohr 'longformat' => 0, 47c8d45e58SAndreas Gohr 'multiformat' => 0 48c8d45e58SAndreas Gohr ], 492fc45e0cSsplitbrain ]; 508fce80b1SAndreas Gohr 517db0beafSAnna Dabrowska protected $quotelevel = 0; 527db0beafSAnna Dabrowska protected $formatting = 0; 537db0beafSAnna Dabrowska protected $tableopen = false; 548fce80b1SAndreas Gohr 558ae469bfSAndreas Gohr /** @inheritdoc */ 56c8d45e58SAndreas Gohr public function document_start() 5776bbc49cSAnna Dabrowska { 584ea373cdSAndreas Gohr global $ID; 594ea373cdSAndreas Gohr $meta = p_get_metadata($ID); 604ea373cdSAndreas Gohr 614ea373cdSAndreas Gohr // get some dates from meta data 627db0beafSAnna Dabrowska $this->docArray['created'] = $meta['date']['created']; 637db0beafSAnna Dabrowska $this->docArray['modified'] = $meta['date']['modified']; 648fdbbd20SAnna Dabrowska $this->docArray['authors']['*'] = 0; 654ea373cdSAndreas Gohr 664ea373cdSAndreas Gohr // get author info 672fc45e0cSsplitbrain $changelog = new PageChangeLog($ID); 685929c83eSMichael Große $revs = $changelog->getRevisions(0, 10000); //FIXME find a good solution for 'get ALL revisions' 698ae469bfSAndreas Gohr $revs[] = $meta['last_change']['date']; 707db0beafSAnna Dabrowska $this->docArray['changes'] = count($revs); 714ea373cdSAndreas Gohr foreach ($revs as $rev) { 72088a1fe7SMichael Grosse $info = $changelog->getRevisionInfo($rev); 738ae469bfSAndreas Gohr if ($info && !empty($info['user'])) { 742fc45e0cSsplitbrain $authorUserCnt = empty($this->docArray['authors'][$info['user']]) 752fc45e0cSsplitbrain ? 0 762fc45e0cSsplitbrain : $this->docArray['authors'][$info['user']]; 778fdbbd20SAnna Dabrowska $this->docArray['authors'][$info['user']] = $authorUserCnt + 1; 784ea373cdSAndreas Gohr } else { 792fc45e0cSsplitbrain ++$this->docArray['authors']['*']; 804ea373cdSAndreas Gohr } 814ea373cdSAndreas Gohr } 824ea373cdSAndreas Gohr 834ea373cdSAndreas Gohr // work on raw text 844ea373cdSAndreas Gohr $text = rawWiki($ID); 852fc45e0cSsplitbrain $this->docArray['chars'] = PhpString::strlen($text); 867db0beafSAnna Dabrowska $this->docArray['words'] = count(array_filter(preg_split('/[^\w\-_]/u', $text))); 874ea373cdSAndreas Gohr } 884ea373cdSAndreas Gohr 894ea373cdSAndreas Gohr 908fce80b1SAndreas Gohr /** 918fce80b1SAndreas Gohr * Here the score is calculated 928ae469bfSAndreas Gohr * @inheritdoc 938fce80b1SAndreas Gohr */ 94c8d45e58SAndreas Gohr public function document_end() 9576bbc49cSAnna Dabrowska { 96e348ef7cSAndreas Gohr global $ID; 97e348ef7cSAndreas Gohr 98e348ef7cSAndreas Gohr // 2 points for missing backlinks 992fc45e0cSsplitbrain if (ft_backlinks($ID) === []) { 1007db0beafSAnna Dabrowska $this->docArray['err']['nobacklink'] += 2; 101e348ef7cSAndreas Gohr } 1028fce80b1SAndreas Gohr 1038fce80b1SAndreas Gohr // 1 point for each FIXME 1047db0beafSAnna Dabrowska $this->docArray['err']['fixme'] += $this->docArray['fixme']; 1058fce80b1SAndreas Gohr 1068fce80b1SAndreas Gohr // 5 points for missing H1 1077db0beafSAnna Dabrowska if ($this->docArray['header_count'][1] == 0) { 1087db0beafSAnna Dabrowska $this->docArray['err']['noh1'] += 5; 1098fce80b1SAndreas Gohr } 1108fce80b1SAndreas Gohr // 1 point for each H1 too much 1117db0beafSAnna Dabrowska if ($this->docArray['header_count'][1] > 1) { 1127db0beafSAnna Dabrowska $this->docArray['err']['manyh1'] += $this->docArray['header'][1]; 1138fce80b1SAndreas Gohr } 1148fce80b1SAndreas Gohr 1158fce80b1SAndreas Gohr // 1 point for each incorrectly nested headline 1167db0beafSAnna Dabrowska $cnt = count($this->docArray['header_struct']); 1178fce80b1SAndreas Gohr for ($i = 1; $i < $cnt; $i++) { 1187db0beafSAnna Dabrowska if ($this->docArray['header_struct'][$i] - $this->docArray['header_struct'][$i - 1] > 1) { 1192fc45e0cSsplitbrain ++$this->docArray['err']['headernest']; 1208fce80b1SAndreas Gohr } 1218fce80b1SAndreas Gohr } 1228fce80b1SAndreas Gohr 1238fce80b1SAndreas Gohr // 1/2 points for deeply nested quotations 1247db0beafSAnna Dabrowska if ($this->docArray['quote_nest'] > 2) { 125af8294b9SAndreas Gohr $this->docArray['err']['deepquote'] = $this->docArray['quote_nest'] / 2; 1268fce80b1SAndreas Gohr } 1278fce80b1SAndreas Gohr 1288fce80b1SAndreas Gohr // FIXME points for many quotes? 1298fce80b1SAndreas Gohr 1308fce80b1SAndreas Gohr // 1/2 points for too many hr 1317db0beafSAnna Dabrowska if ($this->docArray['hr'] > 2) { 1327db0beafSAnna Dabrowska $this->docArray['err']['manyhr'] = ($this->docArray['hr'] - 2) / 2; 1338fce80b1SAndreas Gohr } 1348fce80b1SAndreas Gohr 1358fce80b1SAndreas Gohr // 1 point for too many line breaks 1367db0beafSAnna Dabrowska if ($this->docArray['linebreak'] > 2) { 1377db0beafSAnna Dabrowska $this->docArray['err']['manybr'] = $this->docArray['linebreak'] - 2; 1388fce80b1SAndreas Gohr } 1398fce80b1SAndreas Gohr 1404ea373cdSAndreas Gohr // 1 point for single author only 1417db0beafSAnna Dabrowska if (!$this->getConf('single_author_only') && count($this->docArray['authors']) == 1) { 1427db0beafSAnna Dabrowska $this->docArray['err']['singleauthor'] = 1; 1434ea373cdSAndreas Gohr } 1444ea373cdSAndreas Gohr 145d723b313SAndreas Gohr // 1 point for too small document 1467db0beafSAnna Dabrowska if ($this->docArray['chars'] < 150) { 1477db0beafSAnna Dabrowska $this->docArray['err']['toosmall'] = 1; 148d723b313SAndreas Gohr } 149d723b313SAndreas Gohr 150d723b313SAndreas Gohr // 1 point for too large document 1517db0beafSAnna Dabrowska if ($this->docArray['chars'] > 100000) { 1527db0beafSAnna Dabrowska $this->docArray['err']['toolarge'] = 1; 153d723b313SAndreas Gohr } 154d723b313SAndreas Gohr 155d723b313SAndreas Gohr // header to text ratio 1567db0beafSAnna Dabrowska $hc = $this->docArray['header_count'][1] + 1577db0beafSAnna Dabrowska $this->docArray['header_count'][2] + 1587db0beafSAnna Dabrowska $this->docArray['header_count'][3] + 1597db0beafSAnna Dabrowska $this->docArray['header_count'][4] + 1607db0beafSAnna Dabrowska $this->docArray['header_count'][5]; 16113abf7ccSAndreas Gohr $hc--; //we expect at least 1 16213abf7ccSAndreas Gohr if ($hc > 0) { 1637db0beafSAnna Dabrowska $hr = $this->docArray['chars'] / $hc; 164d723b313SAndreas Gohr 165d723b313SAndreas Gohr // 1 point for too many headers 166d723b313SAndreas Gohr if ($hr < 200) { 1677db0beafSAnna Dabrowska $this->docArray['err']['manyheaders'] = 1; 168d723b313SAndreas Gohr } 169d723b313SAndreas Gohr 170d723b313SAndreas Gohr // 1 point for too few headers 1711b085f03SAndreas Gohr if ($hr > 2000) { 1727db0beafSAnna Dabrowska $this->docArray['err']['fewheaders'] = 1; 173d723b313SAndreas Gohr } 174d723b313SAndreas Gohr } 175d723b313SAndreas Gohr 176d723b313SAndreas Gohr // 1 point when no link at all 1777db0beafSAnna Dabrowska if (!$this->docArray['internal_links']) { 1787db0beafSAnna Dabrowska $this->docArray['err']['nolink'] = 1; 179d723b313SAndreas Gohr } 180d723b313SAndreas Gohr 181d723b313SAndreas Gohr // 0.5 for broken links when too many 1827db0beafSAnna Dabrowska if ($this->docArray['broken_links'] > 2) { 1837db0beafSAnna Dabrowska $this->docArray['err']['brokenlink'] = $this->docArray['broken_links'] * 0.5; 184d723b313SAndreas Gohr } 185d723b313SAndreas Gohr 186d723b313SAndreas Gohr // 2 points for lot's of formatting 1877db0beafSAnna Dabrowska if ($this->docArray['formatted'] && $this->docArray['chars'] / $this->docArray['formatted'] < 3) { 1887db0beafSAnna Dabrowska $this->docArray['err']['manyformat'] = 2; 189d723b313SAndreas Gohr } 190d723b313SAndreas Gohr 1919068e431SAndreas Gohr // add up all scores 1922fc45e0cSsplitbrain foreach ($this->docArray['err'] as $val) $this->docArray['score'] += $val; 1939068e431SAndreas Gohr 1944ea373cdSAndreas Gohr 1958fce80b1SAndreas Gohr //we're done here 1967db0beafSAnna Dabrowska $this->doc = serialize($this->docArray); 1978fce80b1SAndreas Gohr } 1988fce80b1SAndreas Gohr 1998ae469bfSAndreas Gohr /** @inheritdoc */ 20076bbc49cSAnna Dabrowska public function getFormat() 20176bbc49cSAnna Dabrowska { 2028fce80b1SAndreas Gohr return 'qc'; 2038fce80b1SAndreas Gohr } 2048fce80b1SAndreas Gohr 2058ae469bfSAndreas Gohr /** @inheritdoc */ 20676bbc49cSAnna Dabrowska public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') 20776bbc49cSAnna Dabrowska { 208d723b313SAndreas Gohr global $ID; 2098ae469bfSAndreas Gohr 2102fc45e0cSsplitbrain $resolver = new PageResolver($ID); 2118ae469bfSAndreas Gohr $id = $resolver->resolveId($id); 2128ae469bfSAndreas Gohr $exists = page_exists($id); 213d723b313SAndreas Gohr 2148d7cf088SAndreas Gohr // calculate link width 215d78155a4SAndreas Gohr $a = explode(':', getNS($ID)); 216d78155a4SAndreas Gohr $b = explode(':', getNS($id)); 2172fc45e0cSsplitbrain while (isset($a[0]) && $a[0] === $b[0]) { 2188d7cf088SAndreas Gohr array_shift($a); 2198d7cf088SAndreas Gohr array_shift($b); 2208d7cf088SAndreas Gohr } 221d78155a4SAndreas Gohr $length = count($a) + count($b); 2227db0beafSAnna Dabrowska $this->docArray['link_lengths'][] = $length; 2238d7cf088SAndreas Gohr 2247db0beafSAnna Dabrowska $this->docArray['internal_links']++; 2257db0beafSAnna Dabrowska if (!$exists) $this->docArray['broken_links']++; 2260476d180SAndreas Gohr } 2270476d180SAndreas Gohr 2288ae469bfSAndreas Gohr /** @inheritdoc */ 22976bbc49cSAnna Dabrowska public function externallink($url, $name = null) 23076bbc49cSAnna Dabrowska { 2317db0beafSAnna Dabrowska $this->docArray['external_links']++; 232d723b313SAndreas Gohr } 2338fce80b1SAndreas Gohr 2348ae469bfSAndreas Gohr /** @inheritdoc */ 23576bbc49cSAnna Dabrowska public function header($text, $level, $pos) 23676bbc49cSAnna Dabrowska { 2377db0beafSAnna Dabrowska $this->docArray['header_count'][$level]++; 2387db0beafSAnna Dabrowska $this->docArray['header_struct'][] = $level; 2398fce80b1SAndreas Gohr } 2408fce80b1SAndreas Gohr 2418ae469bfSAndreas Gohr /** @inheritdoc */ 24276bbc49cSAnna Dabrowska public function smiley($smiley) 24376bbc49cSAnna Dabrowska { 2447db0beafSAnna Dabrowska if ($smiley == 'FIXME') $this->docArray['fixme']++; 2458fce80b1SAndreas Gohr } 2468fce80b1SAndreas Gohr 2478ae469bfSAndreas Gohr /** @inheritdoc */ 24876bbc49cSAnna Dabrowska public function linebreak() 24976bbc49cSAnna Dabrowska { 2506afc1841Sthesunrise1983 if (!$this->tableopen) { 2517db0beafSAnna Dabrowska $this->docArray['linebreak']++; 2528fce80b1SAndreas Gohr } 2536afc1841Sthesunrise1983 } 2546afc1841Sthesunrise1983 2558ae469bfSAndreas Gohr /** @inheritdoc */ 256c8d45e58SAndreas Gohr public function table_open($maxcols = null, $numrows = null, $pos = null) 25776bbc49cSAnna Dabrowska { 2586afc1841Sthesunrise1983 $this->tableopen = true; 2596afc1841Sthesunrise1983 } 2606afc1841Sthesunrise1983 2618ae469bfSAndreas Gohr /** @inheritdoc */ 262c8d45e58SAndreas Gohr public function table_close($pos = null) 26376bbc49cSAnna Dabrowska { 2646afc1841Sthesunrise1983 $this->tableopen = false; 2656afc1841Sthesunrise1983 } 2668fce80b1SAndreas Gohr 2678ae469bfSAndreas Gohr /** @inheritdoc */ 26876bbc49cSAnna Dabrowska public function hr() 26976bbc49cSAnna Dabrowska { 2707db0beafSAnna Dabrowska $this->docArray['hr']++; 2718fce80b1SAndreas Gohr } 2728fce80b1SAndreas Gohr 2738ae469bfSAndreas Gohr /** @inheritdoc */ 274c8d45e58SAndreas Gohr public function quote_open() 27576bbc49cSAnna Dabrowska { 2767db0beafSAnna Dabrowska $this->docArray['quote_count']++; 2778fce80b1SAndreas Gohr $this->quotelevel++; 2787db0beafSAnna Dabrowska $this->docArray['quote_nest'] = max($this->quotelevel, $this->docArray['quote_nest']); 2798fce80b1SAndreas Gohr } 2808fce80b1SAndreas Gohr 2818ae469bfSAndreas Gohr /** @inheritdoc */ 282c8d45e58SAndreas Gohr public function quote_close() 28376bbc49cSAnna Dabrowska { 2848fce80b1SAndreas Gohr $this->quotelevel--; 2858fce80b1SAndreas Gohr } 2868fce80b1SAndreas Gohr 2878ae469bfSAndreas Gohr /** @inheritdoc */ 288c8d45e58SAndreas Gohr public function strong_open() 28976bbc49cSAnna Dabrowska { 290d723b313SAndreas Gohr $this->formatting++; 291d723b313SAndreas Gohr } 2928fce80b1SAndreas Gohr 2938ae469bfSAndreas Gohr /** @inheritdoc */ 294c8d45e58SAndreas Gohr public function strong_close() 29576bbc49cSAnna Dabrowska { 296d723b313SAndreas Gohr $this->formatting--; 297d723b313SAndreas Gohr } 298d723b313SAndreas Gohr 2998ae469bfSAndreas Gohr /** @inheritdoc */ 300c8d45e58SAndreas Gohr public function emphasis_open() 30176bbc49cSAnna Dabrowska { 302d723b313SAndreas Gohr $this->formatting++; 303d723b313SAndreas Gohr } 304d723b313SAndreas Gohr 3058ae469bfSAndreas Gohr /** @inheritdoc */ 306c8d45e58SAndreas Gohr public function emphasis_close() 30776bbc49cSAnna Dabrowska { 308d723b313SAndreas Gohr $this->formatting--; 309d723b313SAndreas Gohr } 310d723b313SAndreas Gohr 3118ae469bfSAndreas Gohr /** @inheritdoc */ 312c8d45e58SAndreas Gohr public function underline_open() 31376bbc49cSAnna Dabrowska { 314d723b313SAndreas Gohr $this->formatting++; 315d723b313SAndreas Gohr } 316d723b313SAndreas Gohr 3178ae469bfSAndreas Gohr /** @inheritdoc */ 318c8d45e58SAndreas Gohr public function underline_close() 31976bbc49cSAnna Dabrowska { 320d723b313SAndreas Gohr $this->formatting--; 321d723b313SAndreas Gohr } 322d723b313SAndreas Gohr 3238ae469bfSAndreas Gohr /** @inheritdoc */ 32476bbc49cSAnna Dabrowska public function cdata($text) 32576bbc49cSAnna Dabrowska { 326d723b313SAndreas Gohr if (!$this->formatting) return; 327d723b313SAndreas Gohr 3288ae469bfSAndreas Gohr $len = PhpString::strlen($text); 329d723b313SAndreas Gohr 330d723b313SAndreas Gohr // 1 point for formattings longer than 500 chars 3317db0beafSAnna Dabrowska if ($len > 500) $this->docArray['err']['longformat']++; 332d723b313SAndreas Gohr 3334bda998cSAndreas Gohr // 1 point for each multiformatting 3347db0beafSAnna Dabrowska if ($this->formatting > 1) $this->docArray['err']['multiformat'] += 1 * ($this->formatting - 1); 3354bda998cSAndreas Gohr 3367db0beafSAnna Dabrowska $this->docArray['formatted'] += $len; 337d723b313SAndreas Gohr } 3388fce80b1SAndreas Gohr} 339