18fce80b1SAndreas Gohr<?php 23c4e5ff0Ssplitbrain 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, 4748df69a5Sribsey 'multiformat' => 0, 4848df69a5Sribsey 'nobacklink' => 0 49c8d45e58SAndreas Gohr ], 502fc45e0cSsplitbrain ]; 518fce80b1SAndreas Gohr 527db0beafSAnna Dabrowska protected $quotelevel = 0; 537db0beafSAnna Dabrowska protected $formatting = 0; 547db0beafSAnna Dabrowska protected $tableopen = false; 558fce80b1SAndreas Gohr 568ae469bfSAndreas Gohr /** @inheritdoc */ 57c8d45e58SAndreas Gohr public function document_start() 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 682fc45e0cSsplitbrain $changelog = new PageChangeLog($ID); 695929c83eSMichael Große $revs = $changelog->getRevisions(0, 10000); //FIXME find a good solution for 'get ALL revisions' 70*fd6384fcSribsey $lastChange = $meta['last_change'] ?? null; 71*fd6384fcSribsey $revs[] = is_array($lastChange) ? ($lastChange['date'] ?? []) : []; 727db0beafSAnna Dabrowska $this->docArray['changes'] = count($revs); 734ea373cdSAndreas Gohr foreach ($revs as $rev) { 74088a1fe7SMichael Grosse $info = $changelog->getRevisionInfo($rev); 758ae469bfSAndreas Gohr if ($info && !empty($info['user'])) { 762fc45e0cSsplitbrain $authorUserCnt = empty($this->docArray['authors'][$info['user']]) 772fc45e0cSsplitbrain ? 0 782fc45e0cSsplitbrain : $this->docArray['authors'][$info['user']]; 798fdbbd20SAnna Dabrowska $this->docArray['authors'][$info['user']] = $authorUserCnt + 1; 804ea373cdSAndreas Gohr } else { 812fc45e0cSsplitbrain ++$this->docArray['authors']['*']; 824ea373cdSAndreas Gohr } 834ea373cdSAndreas Gohr } 844ea373cdSAndreas Gohr 854ea373cdSAndreas Gohr // work on raw text 864ea373cdSAndreas Gohr $text = rawWiki($ID); 872fc45e0cSsplitbrain $this->docArray['chars'] = PhpString::strlen($text); 887db0beafSAnna Dabrowska $this->docArray['words'] = count(array_filter(preg_split('/[^\w\-_]/u', $text))); 894ea373cdSAndreas Gohr } 904ea373cdSAndreas Gohr 914ea373cdSAndreas Gohr 928fce80b1SAndreas Gohr /** 938fce80b1SAndreas Gohr * Here the score is calculated 948ae469bfSAndreas Gohr * @inheritdoc 958fce80b1SAndreas Gohr */ 96c8d45e58SAndreas Gohr public function document_end() 9776bbc49cSAnna Dabrowska { 98e348ef7cSAndreas Gohr global $ID; 99e348ef7cSAndreas Gohr 100e348ef7cSAndreas Gohr // 2 points for missing backlinks 1012fc45e0cSsplitbrain if (ft_backlinks($ID) === []) { 1027db0beafSAnna Dabrowska $this->docArray['err']['nobacklink'] += 2; 103e348ef7cSAndreas Gohr } 1048fce80b1SAndreas Gohr 1058fce80b1SAndreas Gohr // 1 point for each FIXME 1067db0beafSAnna Dabrowska $this->docArray['err']['fixme'] += $this->docArray['fixme']; 1078fce80b1SAndreas Gohr 1088fce80b1SAndreas Gohr // 5 points for missing H1 1097db0beafSAnna Dabrowska if ($this->docArray['header_count'][1] == 0) { 1107db0beafSAnna Dabrowska $this->docArray['err']['noh1'] += 5; 1118fce80b1SAndreas Gohr } 1128fce80b1SAndreas Gohr // 1 point for each H1 too much 1137db0beafSAnna Dabrowska if ($this->docArray['header_count'][1] > 1) { 1144aed63edSZweihorn $this->docArray['err']['manyh1'] += $this->docArray['header_count'][1]; 1158fce80b1SAndreas Gohr } 1168fce80b1SAndreas Gohr 1178fce80b1SAndreas Gohr // 1 point for each incorrectly nested headline 1187db0beafSAnna Dabrowska $cnt = count($this->docArray['header_struct']); 1198fce80b1SAndreas Gohr for ($i = 1; $i < $cnt; $i++) { 1207db0beafSAnna Dabrowska if ($this->docArray['header_struct'][$i] - $this->docArray['header_struct'][$i - 1] > 1) { 1212fc45e0cSsplitbrain ++$this->docArray['err']['headernest']; 1228fce80b1SAndreas Gohr } 1238fce80b1SAndreas Gohr } 1248fce80b1SAndreas Gohr 1258fce80b1SAndreas Gohr // 1/2 points for deeply nested quotations 1267db0beafSAnna Dabrowska if ($this->docArray['quote_nest'] > 2) { 127af8294b9SAndreas Gohr $this->docArray['err']['deepquote'] = $this->docArray['quote_nest'] / 2; 1288fce80b1SAndreas Gohr } 1298fce80b1SAndreas Gohr 1308fce80b1SAndreas Gohr // FIXME points for many quotes? 1318fce80b1SAndreas Gohr 1328fce80b1SAndreas Gohr // 1/2 points for too many hr 1337db0beafSAnna Dabrowska if ($this->docArray['hr'] > 2) { 1347db0beafSAnna Dabrowska $this->docArray['err']['manyhr'] = ($this->docArray['hr'] - 2) / 2; 1358fce80b1SAndreas Gohr } 1368fce80b1SAndreas Gohr 1378fce80b1SAndreas Gohr // 1 point for too many line breaks 1387db0beafSAnna Dabrowska if ($this->docArray['linebreak'] > 2) { 1397db0beafSAnna Dabrowska $this->docArray['err']['manybr'] = $this->docArray['linebreak'] - 2; 1408fce80b1SAndreas Gohr } 1418fce80b1SAndreas Gohr 1424ea373cdSAndreas Gohr // 1 point for single author only 1437db0beafSAnna Dabrowska if (!$this->getConf('single_author_only') && count($this->docArray['authors']) == 1) { 1447db0beafSAnna Dabrowska $this->docArray['err']['singleauthor'] = 1; 1454ea373cdSAndreas Gohr } 1464ea373cdSAndreas Gohr 147d723b313SAndreas Gohr // 1 point for too small document 1487db0beafSAnna Dabrowska if ($this->docArray['chars'] < 150) { 1497db0beafSAnna Dabrowska $this->docArray['err']['toosmall'] = 1; 150d723b313SAndreas Gohr } 151d723b313SAndreas Gohr 152d723b313SAndreas Gohr // 1 point for too large document 1537db0beafSAnna Dabrowska if ($this->docArray['chars'] > 100000) { 1547db0beafSAnna Dabrowska $this->docArray['err']['toolarge'] = 1; 155d723b313SAndreas Gohr } 156d723b313SAndreas Gohr 157d723b313SAndreas Gohr // header to text ratio 1587db0beafSAnna Dabrowska $hc = $this->docArray['header_count'][1] + 1597db0beafSAnna Dabrowska $this->docArray['header_count'][2] + 1607db0beafSAnna Dabrowska $this->docArray['header_count'][3] + 1617db0beafSAnna Dabrowska $this->docArray['header_count'][4] + 1627db0beafSAnna Dabrowska $this->docArray['header_count'][5]; 16313abf7ccSAndreas Gohr $hc--; //we expect at least 1 16413abf7ccSAndreas Gohr if ($hc > 0) { 1657db0beafSAnna Dabrowska $hr = $this->docArray['chars'] / $hc; 166d723b313SAndreas Gohr 167d723b313SAndreas Gohr // 1 point for too many headers 168d723b313SAndreas Gohr if ($hr < 200) { 1697db0beafSAnna Dabrowska $this->docArray['err']['manyheaders'] = 1; 170d723b313SAndreas Gohr } 171d723b313SAndreas Gohr 172d723b313SAndreas Gohr // 1 point for too few headers 1731b085f03SAndreas Gohr if ($hr > 2000) { 1747db0beafSAnna Dabrowska $this->docArray['err']['fewheaders'] = 1; 175d723b313SAndreas Gohr } 176d723b313SAndreas Gohr } 177d723b313SAndreas Gohr 178d723b313SAndreas Gohr // 1 point when no link at all 1797db0beafSAnna Dabrowska if (!$this->docArray['internal_links']) { 1807db0beafSAnna Dabrowska $this->docArray['err']['nolink'] = 1; 181d723b313SAndreas Gohr } 182d723b313SAndreas Gohr 183d723b313SAndreas Gohr // 0.5 for broken links when too many 1847db0beafSAnna Dabrowska if ($this->docArray['broken_links'] > 2) { 1857db0beafSAnna Dabrowska $this->docArray['err']['brokenlink'] = $this->docArray['broken_links'] * 0.5; 186d723b313SAndreas Gohr } 187d723b313SAndreas Gohr 188d723b313SAndreas Gohr // 2 points for lot's of formatting 1897db0beafSAnna Dabrowska if ($this->docArray['formatted'] && $this->docArray['chars'] / $this->docArray['formatted'] < 3) { 1907db0beafSAnna Dabrowska $this->docArray['err']['manyformat'] = 2; 191d723b313SAndreas Gohr } 192d723b313SAndreas Gohr 1939068e431SAndreas Gohr // add up all scores 1942fc45e0cSsplitbrain foreach ($this->docArray['err'] as $val) $this->docArray['score'] += $val; 1959068e431SAndreas Gohr 1964ea373cdSAndreas Gohr 1978fce80b1SAndreas Gohr //we're done here 1987db0beafSAnna Dabrowska $this->doc = serialize($this->docArray); 1998fce80b1SAndreas Gohr } 2008fce80b1SAndreas Gohr 2018ae469bfSAndreas Gohr /** @inheritdoc */ 20276bbc49cSAnna Dabrowska public function getFormat() 20376bbc49cSAnna Dabrowska { 2048fce80b1SAndreas Gohr return 'qc'; 2058fce80b1SAndreas Gohr } 2068fce80b1SAndreas Gohr 2078ae469bfSAndreas Gohr /** @inheritdoc */ 20876bbc49cSAnna Dabrowska public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') 20976bbc49cSAnna Dabrowska { 210d723b313SAndreas Gohr global $ID; 2118ae469bfSAndreas Gohr 2122fc45e0cSsplitbrain $resolver = new PageResolver($ID); 2138ae469bfSAndreas Gohr $id = $resolver->resolveId($id); 2148ae469bfSAndreas Gohr $exists = page_exists($id); 215d723b313SAndreas Gohr 2168d7cf088SAndreas Gohr // calculate link width 217d78155a4SAndreas Gohr $a = explode(':', getNS($ID)); 218d78155a4SAndreas Gohr $b = explode(':', getNS($id)); 2192fc45e0cSsplitbrain while (isset($a[0]) && $a[0] === $b[0]) { 2208d7cf088SAndreas Gohr array_shift($a); 2218d7cf088SAndreas Gohr array_shift($b); 2228d7cf088SAndreas Gohr } 223d78155a4SAndreas Gohr $length = count($a) + count($b); 2247db0beafSAnna Dabrowska $this->docArray['link_lengths'][] = $length; 2258d7cf088SAndreas Gohr 2267db0beafSAnna Dabrowska $this->docArray['internal_links']++; 2277db0beafSAnna Dabrowska if (!$exists) $this->docArray['broken_links']++; 2280476d180SAndreas Gohr } 2290476d180SAndreas Gohr 2308ae469bfSAndreas Gohr /** @inheritdoc */ 23176bbc49cSAnna Dabrowska public function externallink($url, $name = null) 23276bbc49cSAnna Dabrowska { 2337db0beafSAnna Dabrowska $this->docArray['external_links']++; 234d723b313SAndreas Gohr } 2358fce80b1SAndreas Gohr 2368ae469bfSAndreas Gohr /** @inheritdoc */ 23776bbc49cSAnna Dabrowska public function header($text, $level, $pos) 23876bbc49cSAnna Dabrowska { 2397db0beafSAnna Dabrowska $this->docArray['header_count'][$level]++; 2407db0beafSAnna Dabrowska $this->docArray['header_struct'][] = $level; 2418fce80b1SAndreas Gohr } 2428fce80b1SAndreas Gohr 2438ae469bfSAndreas Gohr /** @inheritdoc */ 24476bbc49cSAnna Dabrowska public function smiley($smiley) 24576bbc49cSAnna Dabrowska { 2467db0beafSAnna Dabrowska if ($smiley == 'FIXME') $this->docArray['fixme']++; 2478fce80b1SAndreas Gohr } 2488fce80b1SAndreas Gohr 2498ae469bfSAndreas Gohr /** @inheritdoc */ 25076bbc49cSAnna Dabrowska public function linebreak() 25176bbc49cSAnna Dabrowska { 2526afc1841Sthesunrise1983 if (!$this->tableopen) { 2537db0beafSAnna Dabrowska $this->docArray['linebreak']++; 2548fce80b1SAndreas Gohr } 2556afc1841Sthesunrise1983 } 2566afc1841Sthesunrise1983 2578ae469bfSAndreas Gohr /** @inheritdoc */ 258c8d45e58SAndreas Gohr public function table_open($maxcols = null, $numrows = null, $pos = null) 25976bbc49cSAnna Dabrowska { 2606afc1841Sthesunrise1983 $this->tableopen = true; 2616afc1841Sthesunrise1983 } 2626afc1841Sthesunrise1983 2638ae469bfSAndreas Gohr /** @inheritdoc */ 264c8d45e58SAndreas Gohr public function table_close($pos = null) 26576bbc49cSAnna Dabrowska { 2666afc1841Sthesunrise1983 $this->tableopen = false; 2676afc1841Sthesunrise1983 } 2688fce80b1SAndreas Gohr 2698ae469bfSAndreas Gohr /** @inheritdoc */ 27076bbc49cSAnna Dabrowska public function hr() 27176bbc49cSAnna Dabrowska { 2727db0beafSAnna Dabrowska $this->docArray['hr']++; 2738fce80b1SAndreas Gohr } 2748fce80b1SAndreas Gohr 2758ae469bfSAndreas Gohr /** @inheritdoc */ 276c8d45e58SAndreas Gohr public function quote_open() 27776bbc49cSAnna Dabrowska { 2787db0beafSAnna Dabrowska $this->docArray['quote_count']++; 2798fce80b1SAndreas Gohr $this->quotelevel++; 2807db0beafSAnna Dabrowska $this->docArray['quote_nest'] = max($this->quotelevel, $this->docArray['quote_nest']); 2818fce80b1SAndreas Gohr } 2828fce80b1SAndreas Gohr 2838ae469bfSAndreas Gohr /** @inheritdoc */ 284c8d45e58SAndreas Gohr public function quote_close() 28576bbc49cSAnna Dabrowska { 2868fce80b1SAndreas Gohr $this->quotelevel--; 2878fce80b1SAndreas Gohr } 2888fce80b1SAndreas Gohr 2898ae469bfSAndreas Gohr /** @inheritdoc */ 290c8d45e58SAndreas Gohr public function strong_open() 29176bbc49cSAnna Dabrowska { 292d723b313SAndreas Gohr $this->formatting++; 293d723b313SAndreas Gohr } 2948fce80b1SAndreas Gohr 2958ae469bfSAndreas Gohr /** @inheritdoc */ 296c8d45e58SAndreas Gohr public function strong_close() 29776bbc49cSAnna Dabrowska { 298d723b313SAndreas Gohr $this->formatting--; 299d723b313SAndreas Gohr } 300d723b313SAndreas Gohr 3018ae469bfSAndreas Gohr /** @inheritdoc */ 302c8d45e58SAndreas Gohr public function emphasis_open() 30376bbc49cSAnna Dabrowska { 304d723b313SAndreas Gohr $this->formatting++; 305d723b313SAndreas Gohr } 306d723b313SAndreas Gohr 3078ae469bfSAndreas Gohr /** @inheritdoc */ 308c8d45e58SAndreas Gohr public function emphasis_close() 30976bbc49cSAnna Dabrowska { 310d723b313SAndreas Gohr $this->formatting--; 311d723b313SAndreas Gohr } 312d723b313SAndreas Gohr 3138ae469bfSAndreas Gohr /** @inheritdoc */ 314c8d45e58SAndreas Gohr public function underline_open() 31576bbc49cSAnna Dabrowska { 316d723b313SAndreas Gohr $this->formatting++; 317d723b313SAndreas Gohr } 318d723b313SAndreas Gohr 3198ae469bfSAndreas Gohr /** @inheritdoc */ 320c8d45e58SAndreas Gohr public function underline_close() 32176bbc49cSAnna Dabrowska { 322d723b313SAndreas Gohr $this->formatting--; 323d723b313SAndreas Gohr } 324d723b313SAndreas Gohr 3258ae469bfSAndreas Gohr /** @inheritdoc */ 32676bbc49cSAnna Dabrowska public function cdata($text) 32776bbc49cSAnna Dabrowska { 328d723b313SAndreas Gohr if (!$this->formatting) return; 329d723b313SAndreas Gohr 3308ae469bfSAndreas Gohr $len = PhpString::strlen($text); 331d723b313SAndreas Gohr 332d723b313SAndreas Gohr // 1 point for formattings longer than 500 chars 3337db0beafSAnna Dabrowska if ($len > 500) $this->docArray['err']['longformat']++; 334d723b313SAndreas Gohr 3354bda998cSAndreas Gohr // 1 point for each multiformatting 3367db0beafSAnna Dabrowska if ($this->formatting > 1) $this->docArray['err']['multiformat'] += 1 * ($this->formatting - 1); 3374bda998cSAndreas Gohr 3387db0beafSAnna Dabrowska $this->docArray['formatted'] += $len; 339d723b313SAndreas Gohr } 3408fce80b1SAndreas Gohr} 341