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