xref: /plugin/qc/renderer.php (revision 1c8457749c0d73df0789872e745e575b222391d8)
1<?php
2// must be run within Dokuwiki
3if(!defined('DOKU_INC')) die();
4
5require_once DOKU_INC.'inc/parser/renderer.php';
6require_once DOKU_INC.'inc/fulltext.php';
7
8/**
9 * The Renderer
10 */
11class renderer_plugin_qc extends Doku_Renderer {
12    /**
13     * We store all our data in an array
14     */
15    var $doc = array(
16        // raw statistics
17        'header_count'  => array(0,0,0,0,0,0),
18        'header_struct' => array(),
19        'linebreak'     => 0,
20        'quote_nest'    => 0,
21        'quote_count'   => 0,
22        'fixme'         => 0,
23        'hr'            => 0,
24        'formatted'     => 0,
25
26        'created'       => 0,
27        'modified'      => 0,
28        'changes'       => 0,
29        'authors'       => array(),
30
31        'internal_links'=> 0,
32        'broken_links'  => 0,
33        'external_links'=> 0,
34        'link_lengths'  => array(),
35
36        'chars'         => 0,
37        'words'         => 0,
38
39        'score'         => 0,
40
41        // calculated error scores
42        'err' => array(
43            'fixme'      => 0,
44            'noh1'       => 0,
45            'manyh1'     => 0,
46            'headernest' => 0,
47            'manyhr'     => 0,
48            'manybr'     => 0,
49            'longformat' => 0,
50            'multiformat'=> 0,
51        ),
52    );
53
54    var $quotelevel = 0;
55    var $formatting = 0;
56    var $tableopen  = false;
57
58    function document_start() {
59        global $ID;
60        $meta = p_get_metadata($ID);
61
62        // get some dates from meta data
63        $this->doc['created']  = $meta['date']['created'];
64        $this->doc['modified'] = $meta['date']['modified'];
65
66        // get author info
67        $changelog = new PageChangelog($ID);
68        $revs = $changelog->getRevisions(0,10000); //FIXME find a good solution for 'get ALL revisions'
69        array_push($revs,$meta['last_change']['date']);
70        $this->doc['changes'] = count($revs);
71        foreach($revs as $rev){
72            $info = $changelog->getRevisionInfo($rev);
73            if($info['user']){
74                $this->doc['authors'][$info['user']] += 1;
75            }else{
76                $this->doc['authors']['*'] += 1;
77            }
78        }
79
80        // work on raw text
81        $text = rawWiki($ID);
82        $this->doc['chars'] = utf8_strlen($text);
83        $this->doc['words'] = count(array_filter(preg_split('/[^\w\-_]/u',$text)));
84    }
85
86
87    /**
88     * Here the score is calculated
89     */
90    function document_end() {
91        global $ID;
92
93        // 2 points for missing backlinks
94        if(!count(ft_backlinks($ID))){
95            $this->doc['err']['nobacklink'] += 2;
96        }
97
98        // 1 point for each FIXME
99        $this->doc['err']['fixme'] += $this->doc['fixme'];
100
101        // 5 points for missing H1
102        if($this->doc['header_count'][1] == 0){
103            $this->doc['err']['noh1'] += 5;
104        }
105        // 1 point for each H1 too much
106        if($this->doc['header_count'][1] > 1){
107            $this->doc['err']['manyh1'] += $this->doc['header'][1];
108        }
109
110        // 1 point for each incorrectly nested headline
111        $cnt = count($this->doc['header_struct']);
112        for($i = 1; $i < $cnt; $i++){
113            if($this->doc['header_struct'][$i] - $this->doc['header_struct'][$i-1] > 1){
114                $this->doc['err']['headernest'] += 1;
115            }
116        }
117
118        // 1/2 points for deeply nested quotations
119        if($this->doc['quote_nest'] > 2){
120            $this->doc['err']['deepquote'] += $this->doc['quote_nest']/2;
121        }
122
123        // FIXME points for many quotes?
124
125        // 1/2 points for too many hr
126        if($this->doc['hr'] > 2){
127            $this->doc['err']['manyhr'] = ($this->doc['hr'] - 2)/2;
128        }
129
130        // 1 point for too many line breaks
131        if($this->doc['linebreak'] > 2){
132            $this->doc['err']['manybr'] = $this->doc['linebreak'] - 2;
133        }
134
135        // 1 point for single author only
136        if(!$this->getConf('single_author_only') && count($this->doc['authors']) == 1){
137            $this->doc['err']['singleauthor'] = 1;
138        }
139
140        // 1 point for too small document
141        if($this->doc['chars'] < 150){
142            $this->doc['err']['toosmall'] = 1;
143        }
144
145        // 1 point for too large document
146        if($this->doc['chars'] > 100000){
147            $this->doc['err']['toolarge'] = 1;
148        }
149
150        // header to text ratio
151        $hc = $this->doc['header_count'][1] +
152              $this->doc['header_count'][2] +
153              $this->doc['header_count'][3] +
154              $this->doc['header_count'][4] +
155              $this->doc['header_count'][5];
156        $hc--; //we expect at least 1
157        if($hc > 0){
158            $hr = $this->doc['chars']/$hc;
159
160            // 1 point for too many headers
161            if($hr < 200){
162                $this->doc['err']['manyheaders'] = 1;
163            }
164
165            // 1 point for too few headers
166            if($hr > 2000){
167                $this->doc['err']['fewheaders'] = 1;
168            }
169        }
170
171        // 1 point when no link at all
172        if(!$this->doc['internal_links']){
173            $this->doc['err']['nolink'] = 1;
174        }
175
176        // 0.5 for broken links when too many
177        if($this->doc['broken_links'] > 2){
178            $this->doc['err']['brokenlink'] = $this->doc['broken_links']*0.5;
179        }
180
181        // 2 points for lot's of formatting
182        if($this->doc['formatted'] && $this->doc['chars']/$this->doc['formatted'] < 3){
183            $this->doc['err']['manyformat'] = 2;
184        }
185
186        // add up all scores
187        foreach($this->doc['err'] as $err => $val) $this->doc['score'] += $val;
188
189
190        //we're done here
191        $this->doc = serialize($this->doc);
192    }
193
194    /**
195     * the format we produce
196     */
197    function getFormat(){
198        return 'qc';
199    }
200
201    function internallink($id, $name = NULL, $search=NULL,$returnonly=false,$linktype='content') {
202        global $ID;
203        resolve_pageid(getNS($ID),$id,$exists);
204
205        // calculate link width
206        $a = explode(':',getNS($ID));
207        $b = explode(':',getNS($id));
208        while(isset($a[0]) && $a[0] == $b[0]){
209            array_shift($a);
210            array_shift($b);
211        }
212        $length = count($a)+count($b);
213        $this->doc['link_lengths'][] = $length;
214
215        $this->doc['internal_links']++;
216        if(!$exists) $this->doc['broken_links']++;
217    }
218
219    function externallink($url, $name = NULL) {
220        $this->doc['external_links']++;
221    }
222
223    function header($text, $level, $pos){
224        $this->doc['header_count'][$level]++;
225        $this->doc['header_struct'][] = $level;
226    }
227
228    function smiley($smiley) {
229        if($smiley == 'FIXME') $this->doc['fixme']++;
230    }
231
232    function linebreak() {
233        if(!$this->tableopen){
234            $this->doc['linebreak']++;
235        }
236    }
237
238    function table_open($maxcols = null, $numrows = null, $pos = null){
239        $this->tableopen = true;
240    }
241
242    function table_close($pos = null){
243        $this->tableopen = false;
244    }
245
246    function hr() {
247        $this->doc['hr']++;
248    }
249
250    function quote_open() {
251        $this->doc['quote_count']++;
252        $this->quotelevel++;
253        $this->doc['quote_nest'] = max($this->quotelevel,$this->doc['quote_nest']);
254    }
255
256    function quote_close() {
257        $this->quotelevel--;
258    }
259
260    function strong_open() {
261        $this->formatting++;
262    }
263
264    function strong_close() {
265        $this->formatting--;
266    }
267
268    function emphasis_open() {
269        $this->formatting++;
270    }
271
272    function emphasis_close() {
273        $this->formatting--;
274    }
275
276    function underline_open() {
277        $this->formatting++;
278    }
279
280    function underline_close() {
281        $this->formatting--;
282    }
283
284    function cdata($text) {
285        if(!$this->formatting) return;
286
287        $len = utf8_strlen($text);
288
289        // 1 point for formattings longer than 500 chars
290        if($len>500) $this->doc['err']['longformat']++;
291
292        // 1 point for each multiformatting
293        if($this->formatting > 1) $this->doc['err']['multiformat'] += 1*($this->formatting - 1);
294
295        $this->doc['formatted'] += $len;
296    }
297}
298
299//Setup VIM: ex: et ts=4 enc=utf-8 :
300