18fce80b1SAndreas Gohr<?php 276bbc49cSAnna Dabrowska 3*8ae469bfSAndreas Gohruse dokuwiki\Utf8\PhpString; 4*8ae469bfSAndreas 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 56*8ae469bfSAndreas 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 68*8ae469bfSAndreas Gohr $changelog = new \dokuwiki\ChangeLog\PageChangeLog($ID); 695929c83eSMichael Große $revs = $changelog->getRevisions(0, 10000); //FIXME find a good solution for 'get ALL revisions' 70*8ae469bfSAndreas 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); 74*8ae469bfSAndreas 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 93*8ae469bfSAndreas 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) { 1267db0beafSAnna Dabrowska $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 200*8ae469bfSAndreas Gohr /** @inheritdoc */ 20176bbc49cSAnna Dabrowska public function getFormat() 20276bbc49cSAnna Dabrowska { 2038fce80b1SAndreas Gohr return 'qc'; 2048fce80b1SAndreas Gohr } 2058fce80b1SAndreas Gohr 206*8ae469bfSAndreas Gohr /** @inheritdoc */ 20776bbc49cSAnna Dabrowska public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') 20876bbc49cSAnna Dabrowska { 209d723b313SAndreas Gohr global $ID; 210*8ae469bfSAndreas Gohr 211*8ae469bfSAndreas Gohr $resolver = new \dokuwiki\File\PageResolver($ID); 212*8ae469bfSAndreas Gohr $id = $resolver->resolveId($id); 213*8ae469bfSAndreas 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 229*8ae469bfSAndreas Gohr /** @inheritdoc */ 23076bbc49cSAnna Dabrowska public function externallink($url, $name = null) 23176bbc49cSAnna Dabrowska { 2327db0beafSAnna Dabrowska $this->docArray['external_links']++; 233d723b313SAndreas Gohr } 2348fce80b1SAndreas Gohr 235*8ae469bfSAndreas 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 242*8ae469bfSAndreas Gohr /** @inheritdoc */ 24376bbc49cSAnna Dabrowska public function smiley($smiley) 24476bbc49cSAnna Dabrowska { 2457db0beafSAnna Dabrowska if ($smiley == 'FIXME') $this->docArray['fixme']++; 2468fce80b1SAndreas Gohr } 2478fce80b1SAndreas Gohr 248*8ae469bfSAndreas Gohr /** @inheritdoc */ 24976bbc49cSAnna Dabrowska public function linebreak() 25076bbc49cSAnna Dabrowska { 2516afc1841Sthesunrise1983 if (!$this->tableopen) { 2527db0beafSAnna Dabrowska $this->docArray['linebreak']++; 2538fce80b1SAndreas Gohr } 2546afc1841Sthesunrise1983 } 2556afc1841Sthesunrise1983 256*8ae469bfSAndreas 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 262*8ae469bfSAndreas 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 268*8ae469bfSAndreas Gohr /** @inheritdoc */ 26976bbc49cSAnna Dabrowska public function hr() 27076bbc49cSAnna Dabrowska { 2717db0beafSAnna Dabrowska $this->docArray['hr']++; 2728fce80b1SAndreas Gohr } 2738fce80b1SAndreas Gohr 274*8ae469bfSAndreas 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 282*8ae469bfSAndreas Gohr /** @inheritdoc */ 283293182bbSAnna Dabrowska public function quote_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 28476bbc49cSAnna Dabrowska { 2858fce80b1SAndreas Gohr $this->quotelevel--; 2868fce80b1SAndreas Gohr } 2878fce80b1SAndreas Gohr 288*8ae469bfSAndreas Gohr /** @inheritdoc */ 289293182bbSAnna Dabrowska public function strong_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 29076bbc49cSAnna Dabrowska { 291d723b313SAndreas Gohr $this->formatting++; 292d723b313SAndreas Gohr } 2938fce80b1SAndreas Gohr 294*8ae469bfSAndreas Gohr /** @inheritdoc */ 295293182bbSAnna Dabrowska public function strong_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 29676bbc49cSAnna Dabrowska { 297d723b313SAndreas Gohr $this->formatting--; 298d723b313SAndreas Gohr } 299d723b313SAndreas Gohr 300*8ae469bfSAndreas Gohr /** @inheritdoc */ 301293182bbSAnna Dabrowska public function emphasis_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 30276bbc49cSAnna Dabrowska { 303d723b313SAndreas Gohr $this->formatting++; 304d723b313SAndreas Gohr } 305d723b313SAndreas Gohr 306*8ae469bfSAndreas Gohr /** @inheritdoc */ 307293182bbSAnna Dabrowska public function emphasis_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 30876bbc49cSAnna Dabrowska { 309d723b313SAndreas Gohr $this->formatting--; 310d723b313SAndreas Gohr } 311d723b313SAndreas Gohr 312*8ae469bfSAndreas Gohr /** @inheritdoc */ 313293182bbSAnna Dabrowska public function underline_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 31476bbc49cSAnna Dabrowska { 315d723b313SAndreas Gohr $this->formatting++; 316d723b313SAndreas Gohr } 317d723b313SAndreas Gohr 318*8ae469bfSAndreas Gohr /** @inheritdoc */ 319293182bbSAnna Dabrowska public function underline_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 32076bbc49cSAnna Dabrowska { 321d723b313SAndreas Gohr $this->formatting--; 322d723b313SAndreas Gohr } 323d723b313SAndreas Gohr 324*8ae469bfSAndreas Gohr /** @inheritdoc */ 32576bbc49cSAnna Dabrowska public function cdata($text) 32676bbc49cSAnna Dabrowska { 327d723b313SAndreas Gohr if (!$this->formatting) return; 328d723b313SAndreas Gohr 329*8ae469bfSAndreas 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