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