18fce80b1SAndreas Gohr<?php 276bbc49cSAnna Dabrowska 3*2fc45e0cSsplitbrainuse dokuwiki\ChangeLog\PageChangeLog; 4*2fc45e0cSsplitbrainuse dokuwiki\File\PageResolver; 5*2fc45e0cSsplitbrain 6*2fc45e0cSsplitbrainuse function dokuwiki\Utf8\PhpString::strlen; 7*2fc45e0cSsplitbrain 88ae469bfSAndreas Gohruse dokuwiki\Utf8\PhpString; 98ae469bfSAndreas Gohr 108fce80b1SAndreas Gohr/** 118fce80b1SAndreas Gohr * The Renderer 128fce80b1SAndreas Gohr */ 1376bbc49cSAnna Dabrowskaclass renderer_plugin_qc extends Doku_Renderer 1476bbc49cSAnna Dabrowska{ 158fce80b1SAndreas Gohr /** 168fce80b1SAndreas Gohr * We store all our data in an array 178fce80b1SAndreas Gohr */ 18*2fc45e0cSsplitbrain public $docArray = [ 198fce80b1SAndreas Gohr // raw statistics 20*2fc45e0cSsplitbrain 'header_count' => [0, 0, 0, 0, 0, 0], 21*2fc45e0cSsplitbrain 'header_struct' => [], 228fce80b1SAndreas Gohr 'linebreak' => 0, 238fce80b1SAndreas Gohr 'quote_nest' => 0, 248fce80b1SAndreas Gohr 'quote_count' => 0, 258fce80b1SAndreas Gohr 'fixme' => 0, 268fce80b1SAndreas Gohr 'hr' => 0, 27d723b313SAndreas Gohr 'formatted' => 0, 284ea373cdSAndreas Gohr 'created' => 0, 294ea373cdSAndreas Gohr 'modified' => 0, 304ea373cdSAndreas Gohr 'changes' => 0, 31*2fc45e0cSsplitbrain 'authors' => [], 32d723b313SAndreas Gohr 'internal_links' => 0, 33d723b313SAndreas Gohr 'broken_links' => 0, 34d723b313SAndreas Gohr 'external_links' => 0, 35*2fc45e0cSsplitbrain 'link_lengths' => [], 364ea373cdSAndreas Gohr 'chars' => 0, 374ea373cdSAndreas Gohr 'words' => 0, 389068e431SAndreas Gohr 'score' => 0, 398fce80b1SAndreas Gohr // calculated error scores 40*2fc45e0cSsplitbrain 'err' => ['fixme' => 0, 'noh1' => 0, 'manyh1' => 0, 'headernest' => 0, 'manyhr' => 0, 'manybr' => 0, 'longformat' => 0, 'multiformat' => 0], 41*2fc45e0cSsplitbrain ]; 428fce80b1SAndreas Gohr 437db0beafSAnna Dabrowska protected $quotelevel = 0; 447db0beafSAnna Dabrowska protected $formatting = 0; 457db0beafSAnna Dabrowska protected $tableopen = false; 468fce80b1SAndreas Gohr 478ae469bfSAndreas Gohr /** @inheritdoc */ 48293182bbSAnna Dabrowska public function document_start() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 4976bbc49cSAnna Dabrowska { 504ea373cdSAndreas Gohr global $ID; 514ea373cdSAndreas Gohr $meta = p_get_metadata($ID); 524ea373cdSAndreas Gohr 534ea373cdSAndreas Gohr // get some dates from meta data 547db0beafSAnna Dabrowska $this->docArray['created'] = $meta['date']['created']; 557db0beafSAnna Dabrowska $this->docArray['modified'] = $meta['date']['modified']; 568fdbbd20SAnna Dabrowska $this->docArray['authors']['*'] = 0; 574ea373cdSAndreas Gohr 584ea373cdSAndreas Gohr // get author info 59*2fc45e0cSsplitbrain $changelog = new PageChangeLog($ID); 605929c83eSMichael Große $revs = $changelog->getRevisions(0, 10000); //FIXME find a good solution for 'get ALL revisions' 618ae469bfSAndreas Gohr $revs[] = $meta['last_change']['date']; 627db0beafSAnna Dabrowska $this->docArray['changes'] = count($revs); 634ea373cdSAndreas Gohr foreach ($revs as $rev) { 64088a1fe7SMichael Grosse $info = $changelog->getRevisionInfo($rev); 658ae469bfSAndreas Gohr if ($info && !empty($info['user'])) { 66*2fc45e0cSsplitbrain $authorUserCnt = empty($this->docArray['authors'][$info['user']]) 67*2fc45e0cSsplitbrain ? 0 68*2fc45e0cSsplitbrain : $this->docArray['authors'][$info['user']]; 698fdbbd20SAnna Dabrowska $this->docArray['authors'][$info['user']] = $authorUserCnt + 1; 704ea373cdSAndreas Gohr } else { 71*2fc45e0cSsplitbrain ++$this->docArray['authors']['*']; 724ea373cdSAndreas Gohr } 734ea373cdSAndreas Gohr } 744ea373cdSAndreas Gohr 754ea373cdSAndreas Gohr // work on raw text 764ea373cdSAndreas Gohr $text = rawWiki($ID); 77*2fc45e0cSsplitbrain $this->docArray['chars'] = PhpString::strlen($text); 787db0beafSAnna Dabrowska $this->docArray['words'] = count(array_filter(preg_split('/[^\w\-_]/u', $text))); 794ea373cdSAndreas Gohr } 804ea373cdSAndreas Gohr 814ea373cdSAndreas Gohr 828fce80b1SAndreas Gohr /** 838fce80b1SAndreas Gohr * Here the score is calculated 848ae469bfSAndreas Gohr * @inheritdoc 858fce80b1SAndreas Gohr */ 86293182bbSAnna Dabrowska public function document_end() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 8776bbc49cSAnna Dabrowska { 88e348ef7cSAndreas Gohr global $ID; 89e348ef7cSAndreas Gohr 90e348ef7cSAndreas Gohr // 2 points for missing backlinks 91*2fc45e0cSsplitbrain if (ft_backlinks($ID) === []) { 927db0beafSAnna Dabrowska $this->docArray['err']['nobacklink'] += 2; 93e348ef7cSAndreas Gohr } 948fce80b1SAndreas Gohr 958fce80b1SAndreas Gohr // 1 point for each FIXME 967db0beafSAnna Dabrowska $this->docArray['err']['fixme'] += $this->docArray['fixme']; 978fce80b1SAndreas Gohr 988fce80b1SAndreas Gohr // 5 points for missing H1 997db0beafSAnna Dabrowska if ($this->docArray['header_count'][1] == 0) { 1007db0beafSAnna Dabrowska $this->docArray['err']['noh1'] += 5; 1018fce80b1SAndreas Gohr } 1028fce80b1SAndreas Gohr // 1 point for each H1 too much 1037db0beafSAnna Dabrowska if ($this->docArray['header_count'][1] > 1) { 1047db0beafSAnna Dabrowska $this->docArray['err']['manyh1'] += $this->docArray['header'][1]; 1058fce80b1SAndreas Gohr } 1068fce80b1SAndreas Gohr 1078fce80b1SAndreas Gohr // 1 point for each incorrectly nested headline 1087db0beafSAnna Dabrowska $cnt = count($this->docArray['header_struct']); 1098fce80b1SAndreas Gohr for ($i = 1; $i < $cnt; $i++) { 1107db0beafSAnna Dabrowska if ($this->docArray['header_struct'][$i] - $this->docArray['header_struct'][$i - 1] > 1) { 111*2fc45e0cSsplitbrain ++$this->docArray['err']['headernest']; 1128fce80b1SAndreas Gohr } 1138fce80b1SAndreas Gohr } 1148fce80b1SAndreas Gohr 1158fce80b1SAndreas Gohr // 1/2 points for deeply nested quotations 1167db0beafSAnna Dabrowska if ($this->docArray['quote_nest'] > 2) { 117af8294b9SAndreas Gohr $this->docArray['err']['deepquote'] = $this->docArray['quote_nest'] / 2; 1188fce80b1SAndreas Gohr } 1198fce80b1SAndreas Gohr 1208fce80b1SAndreas Gohr // FIXME points for many quotes? 1218fce80b1SAndreas Gohr 1228fce80b1SAndreas Gohr // 1/2 points for too many hr 1237db0beafSAnna Dabrowska if ($this->docArray['hr'] > 2) { 1247db0beafSAnna Dabrowska $this->docArray['err']['manyhr'] = ($this->docArray['hr'] - 2) / 2; 1258fce80b1SAndreas Gohr } 1268fce80b1SAndreas Gohr 1278fce80b1SAndreas Gohr // 1 point for too many line breaks 1287db0beafSAnna Dabrowska if ($this->docArray['linebreak'] > 2) { 1297db0beafSAnna Dabrowska $this->docArray['err']['manybr'] = $this->docArray['linebreak'] - 2; 1308fce80b1SAndreas Gohr } 1318fce80b1SAndreas Gohr 1324ea373cdSAndreas Gohr // 1 point for single author only 1337db0beafSAnna Dabrowska if (!$this->getConf('single_author_only') && count($this->docArray['authors']) == 1) { 1347db0beafSAnna Dabrowska $this->docArray['err']['singleauthor'] = 1; 1354ea373cdSAndreas Gohr } 1364ea373cdSAndreas Gohr 137d723b313SAndreas Gohr // 1 point for too small document 1387db0beafSAnna Dabrowska if ($this->docArray['chars'] < 150) { 1397db0beafSAnna Dabrowska $this->docArray['err']['toosmall'] = 1; 140d723b313SAndreas Gohr } 141d723b313SAndreas Gohr 142d723b313SAndreas Gohr // 1 point for too large document 1437db0beafSAnna Dabrowska if ($this->docArray['chars'] > 100000) { 1447db0beafSAnna Dabrowska $this->docArray['err']['toolarge'] = 1; 145d723b313SAndreas Gohr } 146d723b313SAndreas Gohr 147d723b313SAndreas Gohr // header to text ratio 1487db0beafSAnna Dabrowska $hc = $this->docArray['header_count'][1] + 1497db0beafSAnna Dabrowska $this->docArray['header_count'][2] + 1507db0beafSAnna Dabrowska $this->docArray['header_count'][3] + 1517db0beafSAnna Dabrowska $this->docArray['header_count'][4] + 1527db0beafSAnna Dabrowska $this->docArray['header_count'][5]; 15313abf7ccSAndreas Gohr $hc--; //we expect at least 1 15413abf7ccSAndreas Gohr if ($hc > 0) { 1557db0beafSAnna Dabrowska $hr = $this->docArray['chars'] / $hc; 156d723b313SAndreas Gohr 157d723b313SAndreas Gohr // 1 point for too many headers 158d723b313SAndreas Gohr if ($hr < 200) { 1597db0beafSAnna Dabrowska $this->docArray['err']['manyheaders'] = 1; 160d723b313SAndreas Gohr } 161d723b313SAndreas Gohr 162d723b313SAndreas Gohr // 1 point for too few headers 1631b085f03SAndreas Gohr if ($hr > 2000) { 1647db0beafSAnna Dabrowska $this->docArray['err']['fewheaders'] = 1; 165d723b313SAndreas Gohr } 166d723b313SAndreas Gohr } 167d723b313SAndreas Gohr 168d723b313SAndreas Gohr // 1 point when no link at all 1697db0beafSAnna Dabrowska if (!$this->docArray['internal_links']) { 1707db0beafSAnna Dabrowska $this->docArray['err']['nolink'] = 1; 171d723b313SAndreas Gohr } 172d723b313SAndreas Gohr 173d723b313SAndreas Gohr // 0.5 for broken links when too many 1747db0beafSAnna Dabrowska if ($this->docArray['broken_links'] > 2) { 1757db0beafSAnna Dabrowska $this->docArray['err']['brokenlink'] = $this->docArray['broken_links'] * 0.5; 176d723b313SAndreas Gohr } 177d723b313SAndreas Gohr 178d723b313SAndreas Gohr // 2 points for lot's of formatting 1797db0beafSAnna Dabrowska if ($this->docArray['formatted'] && $this->docArray['chars'] / $this->docArray['formatted'] < 3) { 1807db0beafSAnna Dabrowska $this->docArray['err']['manyformat'] = 2; 181d723b313SAndreas Gohr } 182d723b313SAndreas Gohr 1839068e431SAndreas Gohr // add up all scores 184*2fc45e0cSsplitbrain foreach ($this->docArray['err'] as $val) $this->docArray['score'] += $val; 1859068e431SAndreas Gohr 1864ea373cdSAndreas Gohr 1878fce80b1SAndreas Gohr //we're done here 1887db0beafSAnna Dabrowska $this->doc = serialize($this->docArray); 1898fce80b1SAndreas Gohr } 1908fce80b1SAndreas Gohr 1918ae469bfSAndreas Gohr /** @inheritdoc */ 19276bbc49cSAnna Dabrowska public function getFormat() 19376bbc49cSAnna Dabrowska { 1948fce80b1SAndreas Gohr return 'qc'; 1958fce80b1SAndreas Gohr } 1968fce80b1SAndreas Gohr 1978ae469bfSAndreas Gohr /** @inheritdoc */ 19876bbc49cSAnna Dabrowska public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') 19976bbc49cSAnna Dabrowska { 200d723b313SAndreas Gohr global $ID; 2018ae469bfSAndreas Gohr 202*2fc45e0cSsplitbrain $resolver = new PageResolver($ID); 2038ae469bfSAndreas Gohr $id = $resolver->resolveId($id); 2048ae469bfSAndreas Gohr $exists = page_exists($id); 205d723b313SAndreas Gohr 2068d7cf088SAndreas Gohr // calculate link width 207d78155a4SAndreas Gohr $a = explode(':', getNS($ID)); 208d78155a4SAndreas Gohr $b = explode(':', getNS($id)); 209*2fc45e0cSsplitbrain while (isset($a[0]) && $a[0] === $b[0]) { 2108d7cf088SAndreas Gohr array_shift($a); 2118d7cf088SAndreas Gohr array_shift($b); 2128d7cf088SAndreas Gohr } 213d78155a4SAndreas Gohr $length = count($a) + count($b); 2147db0beafSAnna Dabrowska $this->docArray['link_lengths'][] = $length; 2158d7cf088SAndreas Gohr 2167db0beafSAnna Dabrowska $this->docArray['internal_links']++; 2177db0beafSAnna Dabrowska if (!$exists) $this->docArray['broken_links']++; 2180476d180SAndreas Gohr } 2190476d180SAndreas Gohr 2208ae469bfSAndreas Gohr /** @inheritdoc */ 22176bbc49cSAnna Dabrowska public function externallink($url, $name = null) 22276bbc49cSAnna Dabrowska { 2237db0beafSAnna Dabrowska $this->docArray['external_links']++; 224d723b313SAndreas Gohr } 2258fce80b1SAndreas Gohr 2268ae469bfSAndreas Gohr /** @inheritdoc */ 22776bbc49cSAnna Dabrowska public function header($text, $level, $pos) 22876bbc49cSAnna Dabrowska { 2297db0beafSAnna Dabrowska $this->docArray['header_count'][$level]++; 2307db0beafSAnna Dabrowska $this->docArray['header_struct'][] = $level; 2318fce80b1SAndreas Gohr } 2328fce80b1SAndreas Gohr 2338ae469bfSAndreas Gohr /** @inheritdoc */ 23476bbc49cSAnna Dabrowska public function smiley($smiley) 23576bbc49cSAnna Dabrowska { 2367db0beafSAnna Dabrowska if ($smiley == 'FIXME') $this->docArray['fixme']++; 2378fce80b1SAndreas Gohr } 2388fce80b1SAndreas Gohr 2398ae469bfSAndreas Gohr /** @inheritdoc */ 24076bbc49cSAnna Dabrowska public function linebreak() 24176bbc49cSAnna Dabrowska { 2426afc1841Sthesunrise1983 if (!$this->tableopen) { 2437db0beafSAnna Dabrowska $this->docArray['linebreak']++; 2448fce80b1SAndreas Gohr } 2456afc1841Sthesunrise1983 } 2466afc1841Sthesunrise1983 2478ae469bfSAndreas Gohr /** @inheritdoc */ 248293182bbSAnna Dabrowska public function table_open($maxcols = null, $numrows = null, $pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 24976bbc49cSAnna Dabrowska { 2506afc1841Sthesunrise1983 $this->tableopen = true; 2516afc1841Sthesunrise1983 } 2526afc1841Sthesunrise1983 2538ae469bfSAndreas Gohr /** @inheritdoc */ 254293182bbSAnna Dabrowska public function table_close($pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 25576bbc49cSAnna Dabrowska { 2566afc1841Sthesunrise1983 $this->tableopen = false; 2576afc1841Sthesunrise1983 } 2588fce80b1SAndreas Gohr 2598ae469bfSAndreas Gohr /** @inheritdoc */ 26076bbc49cSAnna Dabrowska public function hr() 26176bbc49cSAnna Dabrowska { 2627db0beafSAnna Dabrowska $this->docArray['hr']++; 2638fce80b1SAndreas Gohr } 2648fce80b1SAndreas Gohr 2658ae469bfSAndreas Gohr /** @inheritdoc */ 266293182bbSAnna Dabrowska public function quote_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 26776bbc49cSAnna Dabrowska { 2687db0beafSAnna Dabrowska $this->docArray['quote_count']++; 2698fce80b1SAndreas Gohr $this->quotelevel++; 2707db0beafSAnna Dabrowska $this->docArray['quote_nest'] = max($this->quotelevel, $this->docArray['quote_nest']); 2718fce80b1SAndreas Gohr } 2728fce80b1SAndreas Gohr 2738ae469bfSAndreas Gohr /** @inheritdoc */ 274293182bbSAnna Dabrowska public function quote_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 27576bbc49cSAnna Dabrowska { 2768fce80b1SAndreas Gohr $this->quotelevel--; 2778fce80b1SAndreas Gohr } 2788fce80b1SAndreas Gohr 2798ae469bfSAndreas Gohr /** @inheritdoc */ 280293182bbSAnna Dabrowska public function strong_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 28176bbc49cSAnna Dabrowska { 282d723b313SAndreas Gohr $this->formatting++; 283d723b313SAndreas Gohr } 2848fce80b1SAndreas Gohr 2858ae469bfSAndreas Gohr /** @inheritdoc */ 286293182bbSAnna Dabrowska public function strong_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 28776bbc49cSAnna Dabrowska { 288d723b313SAndreas Gohr $this->formatting--; 289d723b313SAndreas Gohr } 290d723b313SAndreas Gohr 2918ae469bfSAndreas Gohr /** @inheritdoc */ 292293182bbSAnna Dabrowska public function emphasis_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 29376bbc49cSAnna Dabrowska { 294d723b313SAndreas Gohr $this->formatting++; 295d723b313SAndreas Gohr } 296d723b313SAndreas Gohr 2978ae469bfSAndreas Gohr /** @inheritdoc */ 298293182bbSAnna Dabrowska public function emphasis_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 29976bbc49cSAnna Dabrowska { 300d723b313SAndreas Gohr $this->formatting--; 301d723b313SAndreas Gohr } 302d723b313SAndreas Gohr 3038ae469bfSAndreas Gohr /** @inheritdoc */ 304293182bbSAnna Dabrowska public function underline_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 30576bbc49cSAnna Dabrowska { 306d723b313SAndreas Gohr $this->formatting++; 307d723b313SAndreas Gohr } 308d723b313SAndreas Gohr 3098ae469bfSAndreas Gohr /** @inheritdoc */ 310293182bbSAnna Dabrowska public function underline_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 31176bbc49cSAnna Dabrowska { 312d723b313SAndreas Gohr $this->formatting--; 313d723b313SAndreas Gohr } 314d723b313SAndreas Gohr 3158ae469bfSAndreas Gohr /** @inheritdoc */ 31676bbc49cSAnna Dabrowska public function cdata($text) 31776bbc49cSAnna Dabrowska { 318d723b313SAndreas Gohr if (!$this->formatting) return; 319d723b313SAndreas Gohr 3208ae469bfSAndreas Gohr $len = PhpString::strlen($text); 321d723b313SAndreas Gohr 322d723b313SAndreas Gohr // 1 point for formattings longer than 500 chars 3237db0beafSAnna Dabrowska if ($len > 500) $this->docArray['err']['longformat']++; 324d723b313SAndreas Gohr 3254bda998cSAndreas Gohr // 1 point for each multiformatting 3267db0beafSAnna Dabrowska if ($this->formatting > 1) $this->docArray['err']['multiformat'] += 1 * ($this->formatting - 1); 3274bda998cSAndreas Gohr 3287db0beafSAnna Dabrowska $this->docArray['formatted'] += $len; 329d723b313SAndreas Gohr } 3308fce80b1SAndreas Gohr} 331