xref: /plugin/combo/renderer/analytics.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
1007225e5Sgerardnico<?php
2007225e5Sgerardnico
3007225e5Sgerardnico
4007225e5Sgerardnicouse ComboStrap\Analytics;
5007225e5Sgerardnicouse ComboStrap\LinkUtility;
671f916b9Sgerardnicouse ComboStrap\Page;
7*37748cd8SNickeauuse ComboStrap\StringUtility;
8007225e5Sgerardnicouse dokuwiki\ChangeLog\PageChangeLog;
9007225e5Sgerardnico
10*37748cd8SNickeau
11*37748cd8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
12007225e5Sgerardnico
13007225e5Sgerardnico
14007225e5Sgerardnico/**
15007225e5Sgerardnico * A analysis Renderer that exports stats/quality/metadata in a json format
16007225e5Sgerardnico * You can export the data with
17007225e5Sgerardnico * doku.php?id=somepage&do=export_combo_analytics
18007225e5Sgerardnico */
19007225e5Sgerardnicoclass renderer_plugin_combo_analytics extends Doku_Renderer
20007225e5Sgerardnico{
217c33ecc6Sgerardnico
22007225e5Sgerardnico    const PLAINTEXT = 'formatted';
23007225e5Sgerardnico    const RESULT = "result";
24007225e5Sgerardnico    const DESCRIPTION = "description";
25007225e5Sgerardnico    const PASSED = "Passed";
26007225e5Sgerardnico    const FAILED = "Failed";
27007225e5Sgerardnico    const FIXME = 'fixme';
28007225e5Sgerardnico
29007225e5Sgerardnico    /**
30007225e5Sgerardnico     * Rules key
31007225e5Sgerardnico     */
32007225e5Sgerardnico    const RULE_WORDS_MINIMAL = 'words_min';
33007225e5Sgerardnico    const RULE_OUTLINE_STRUCTURE = "outline_structure";
34007225e5Sgerardnico    const RULE_INTERNAL_BACKLINKS_MIN = 'internal_backlinks_min';
35007225e5Sgerardnico    const RULE_WORDS_MAXIMAL = "words_max";
36007225e5Sgerardnico    const RULE_AVERAGE_WORDS_BY_SECTION_MIN = 'words_by_section_avg_min';
37007225e5Sgerardnico    const RULE_AVERAGE_WORDS_BY_SECTION_MAX = 'words_by_section_avg_max';
38007225e5Sgerardnico    const RULE_INTERNAL_LINKS_MIN = 'internal_links_min';
39007225e5Sgerardnico    const RULE_INTERNAL_BROKEN_LINKS_MAX = 'internal_links_broken_max';
40007225e5Sgerardnico    const RULE_DESCRIPTION_PRESENT = 'description_present';
41007225e5Sgerardnico    const RULE_FIXME = "fixme_min";
42007225e5Sgerardnico    const RULE_TITLE_PRESENT = "title_present";
43007225e5Sgerardnico    const RULE_CANONICAL_PRESENT = "canonical_present";
44aa3cb38fSgerardnico    const QUALITY_RULES = [
45aa3cb38fSgerardnico        self::RULE_CANONICAL_PRESENT,
46aa3cb38fSgerardnico        self::RULE_DESCRIPTION_PRESENT,
47aa3cb38fSgerardnico        self::RULE_FIXME,
48aa3cb38fSgerardnico        self::RULE_INTERNAL_BACKLINKS_MIN,
49aa3cb38fSgerardnico        self::RULE_INTERNAL_BROKEN_LINKS_MAX,
50aa3cb38fSgerardnico        self::RULE_INTERNAL_LINKS_MIN,
51aa3cb38fSgerardnico        self::RULE_OUTLINE_STRUCTURE,
52aa3cb38fSgerardnico        self::RULE_TITLE_PRESENT,
53aa3cb38fSgerardnico        self::RULE_WORDS_MINIMAL,
54aa3cb38fSgerardnico        self::RULE_WORDS_MAXIMAL,
55aa3cb38fSgerardnico        self::RULE_AVERAGE_WORDS_BY_SECTION_MIN,
56aa3cb38fSgerardnico        self::RULE_AVERAGE_WORDS_BY_SECTION_MAX
57aa3cb38fSgerardnico    ];
58007225e5Sgerardnico
59007225e5Sgerardnico    /**
60007225e5Sgerardnico     * The default man
61007225e5Sgerardnico     */
62007225e5Sgerardnico    const CONF_MANDATORY_QUALITY_RULES_DEFAULT_VALUE = [
63007225e5Sgerardnico        self::RULE_WORDS_MINIMAL,
64007225e5Sgerardnico        self::RULE_INTERNAL_BACKLINKS_MIN,
65007225e5Sgerardnico        self::RULE_INTERNAL_LINKS_MIN
66007225e5Sgerardnico    ];
67007225e5Sgerardnico    const CONF_MANDATORY_QUALITY_RULES = "mandatoryQualityRules";
68007225e5Sgerardnico
69007225e5Sgerardnico    /**
70007225e5Sgerardnico     * Quality Score factors
71007225e5Sgerardnico     * They are used to calculate the score
72007225e5Sgerardnico     */
73007225e5Sgerardnico    const CONF_QUALITY_SCORE_INTERNAL_BACKLINK_FACTOR = 'qualityScoreInternalBacklinksFactor';
74007225e5Sgerardnico    const CONF_QUALITY_SCORE_INTERNAL_LINK_FACTOR = 'qualityScoreInternalLinksFactor';
75007225e5Sgerardnico    const CONF_QUALITY_SCORE_TITLE_PRESENT = 'qualityScoreTitlePresent';
76007225e5Sgerardnico    const CONF_QUALITY_SCORE_CORRECT_HEADER_STRUCTURE = 'qualityScoreCorrectOutline';
77007225e5Sgerardnico    const CONF_QUALITY_SCORE_CORRECT_CONTENT = 'qualityScoreCorrectContentLength';
78007225e5Sgerardnico    const CONF_QUALITY_SCORE_NO_FIXME = 'qualityScoreNoFixMe';
79007225e5Sgerardnico    const CONF_QUALITY_SCORE_CORRECT_WORD_SECTION_AVERAGE = 'qualityScoreCorrectWordSectionAvg';
80007225e5Sgerardnico    const CONF_QUALITY_SCORE_INTERNAL_LINK_BROKEN_FACTOR = 'qualityScoreNoBrokenLinks';
81007225e5Sgerardnico    const CONF_QUALITY_SCORE_CHANGES_FACTOR = 'qualityScoreChangesFactor';
82007225e5Sgerardnico    const CONF_QUALITY_SCORE_DESCRIPTION_PRESENT = 'qualityScoreDescriptionPresent';
83007225e5Sgerardnico    const CONF_QUALITY_SCORE_CANONICAL_PRESENT = 'qualityScoreCanonicalPresent';
8408ca4f85Sgerardnico    const SCORING = "scoring";
8508ca4f85Sgerardnico    const SCORE = "score";
86ebdc69ceSgerardnico    const HEADER_STRUCT = 'header_struct';
87531e725cSNickeau    const RENDERER_NAME_MODE = "combo_" . renderer_plugin_combo_analytics::RENDERER_FORMAT;
88531e725cSNickeau    /**
89531e725cSNickeau     * The format returned by the renderer
90531e725cSNickeau     */
91531e725cSNickeau    const RENDERER_FORMAT = "analytics";
92007225e5Sgerardnico
93aa3cb38fSgerardnico
94007225e5Sgerardnico    /**
95007225e5Sgerardnico     * The processing data
96007225e5Sgerardnico     * that should be {@link  renderer_plugin_combo_analysis::reset()}
97007225e5Sgerardnico     */
98007225e5Sgerardnico    public $stats = array(); // the stats
99*37748cd8SNickeau    protected $metadata = array(); // the metadata in frontmatter
100007225e5Sgerardnico    protected $headerId = 0; // the id of the header on the page (first, second, ...)
101007225e5Sgerardnico
102007225e5Sgerardnico    /**
103007225e5Sgerardnico     * Don't known this variable ?
104007225e5Sgerardnico     */
105007225e5Sgerardnico    protected $quotelevel = 0;
106007225e5Sgerardnico    protected $formattingBracket = 0;
107007225e5Sgerardnico    protected $tableopen = false;
108007225e5Sgerardnico    private $plainTextId = 0;
1092c067407Sgerardnico    /**
1102c067407Sgerardnico     * @var Page
1112c067407Sgerardnico     */
1122c067407Sgerardnico    private $page;
1132c067407Sgerardnico
114e8b2ff59SNickeau    /**
115e8b2ff59SNickeau     * Get and unset a value from an array
116e8b2ff59SNickeau     * @param array $array
117e8b2ff59SNickeau     * @param $key
118e8b2ff59SNickeau     * @param $default
119e8b2ff59SNickeau     * @return mixed
120e8b2ff59SNickeau     */
121e8b2ff59SNickeau    private static function getAndUnset(array &$array, $key, $default)
122e8b2ff59SNickeau    {
123e8b2ff59SNickeau        if (isset($array[$key])) {
124e8b2ff59SNickeau            $value = $array[$key];
125e8b2ff59SNickeau            unset($array[$key]);
126e8b2ff59SNickeau            return $value;
127e8b2ff59SNickeau        }
128e8b2ff59SNickeau        return $default;
129e8b2ff59SNickeau
130e8b2ff59SNickeau    }
131e8b2ff59SNickeau
1322c067407Sgerardnico    public function document_start()
1332c067407Sgerardnico    {
1347c33ecc6Sgerardnico        $this->reset();
13585e82846SNickeau        $this->page = Page::createPageFromCurrentId();
1362c067407Sgerardnico
1372c067407Sgerardnico    }
138007225e5Sgerardnico
139007225e5Sgerardnico
140007225e5Sgerardnico    /**
141007225e5Sgerardnico     * Here the score is calculated
142007225e5Sgerardnico     */
143007225e5Sgerardnico    public function document_end() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
144007225e5Sgerardnico    {
145007225e5Sgerardnico        /**
146f3748b38Sgerardnico         * The exported object
147f3748b38Sgerardnico         */
148f3748b38Sgerardnico        $statExport = $this->stats;
149f3748b38Sgerardnico
150f3748b38Sgerardnico        /**
151007225e5Sgerardnico         * The metadata
152007225e5Sgerardnico         */
153007225e5Sgerardnico        global $ID;
154fa5961eaSgerardnico        $dokuWikiMetadata = p_get_metadata($ID);
155007225e5Sgerardnico
156007225e5Sgerardnico        /**
157f3748b38Sgerardnico         * Edit author stats
158f3748b38Sgerardnico         */
159f3748b38Sgerardnico        $changelog = new PageChangeLog($ID);
160f3748b38Sgerardnico        $revs = $changelog->getRevisions(0, 10000);
161fa5961eaSgerardnico        array_push($revs, $dokuWikiMetadata['last_change']['date']);
162f3748b38Sgerardnico        $statExport[Analytics::EDITS_COUNT] = count($revs);
163f3748b38Sgerardnico        foreach ($revs as $rev) {
1642128d419Sgerardnico
165ebdc69ceSgerardnico
166ebdc69ceSgerardnico            /**
167ebdc69ceSgerardnico             * Init the authors array
168ebdc69ceSgerardnico             */
169ebdc69ceSgerardnico            if (!array_key_exists('authors', $statExport)) {
170ebdc69ceSgerardnico                $statExport['authors'] = [];
171f3748b38Sgerardnico            }
172ebdc69ceSgerardnico            /**
173ebdc69ceSgerardnico             * Analytics by users
174ebdc69ceSgerardnico             */
1752128d419Sgerardnico            $info = $changelog->getRevisionInfo($rev);
1762128d419Sgerardnico            if (is_array($info)) {
177ebdc69ceSgerardnico                $user = "*";
178ebdc69ceSgerardnico                if (array_key_exists('user', $info)) {
179ebdc69ceSgerardnico                    $user = $info['user'];
180ebdc69ceSgerardnico                }
181ebdc69ceSgerardnico                if (!array_key_exists('authors', $statExport['authors'])) {
182ebdc69ceSgerardnico                    $statExport['authors'][$user] = 0;
183ebdc69ceSgerardnico                }
184ebdc69ceSgerardnico                $statExport['authors'][$user] += 1;
185f3748b38Sgerardnico            }
1862128d419Sgerardnico        }
187f3748b38Sgerardnico
188f3748b38Sgerardnico        /**
189007225e5Sgerardnico         * Word and chars count
190007225e5Sgerardnico         * The word count does not take into account
191007225e5Sgerardnico         * words with non-words characters such as < =
192007225e5Sgerardnico         * Therefore the node and attribute are not taken in the count
193007225e5Sgerardnico         */
194007225e5Sgerardnico        $text = rawWiki($ID);
195f3748b38Sgerardnico        $statExport[Analytics::CHARS_COUNT] = strlen($text);
196*37748cd8SNickeau        $statExport[Analytics::WORD_COUNT] = StringUtility::getWordCount($text);
197007225e5Sgerardnico
198007225e5Sgerardnico
199007225e5Sgerardnico        /**
200007225e5Sgerardnico         * Internal link distance summary calculation
201007225e5Sgerardnico         */
202007225e5Sgerardnico        if (array_key_exists(Analytics::INTERNAL_LINK_DISTANCE, $statExport)) {
203007225e5Sgerardnico            $linkLengths = $statExport[Analytics::INTERNAL_LINK_DISTANCE];
204007225e5Sgerardnico            unset($statExport[Analytics::INTERNAL_LINK_DISTANCE]);
205007225e5Sgerardnico            $countBacklinks = count($linkLengths);
206007225e5Sgerardnico            $statExport[Analytics::INTERNAL_LINK_DISTANCE]['avg'] = null;
207007225e5Sgerardnico            $statExport[Analytics::INTERNAL_LINK_DISTANCE]['max'] = null;
208007225e5Sgerardnico            $statExport[Analytics::INTERNAL_LINK_DISTANCE]['min'] = null;
209007225e5Sgerardnico            if ($countBacklinks > 0) {
210007225e5Sgerardnico                $statExport[Analytics::INTERNAL_LINK_DISTANCE]['avg'] = array_sum($linkLengths) / $countBacklinks;
211007225e5Sgerardnico                $statExport[Analytics::INTERNAL_LINK_DISTANCE]['max'] = max($linkLengths);
212007225e5Sgerardnico                $statExport[Analytics::INTERNAL_LINK_DISTANCE]['min'] = min($linkLengths);
213007225e5Sgerardnico            }
214007225e5Sgerardnico        }
215007225e5Sgerardnico
216007225e5Sgerardnico        /**
217007225e5Sgerardnico         * Quality Report / Rules
218007225e5Sgerardnico         */
219007225e5Sgerardnico        // The array that hold the results of the quality rules
220007225e5Sgerardnico        $ruleResults = array();
221007225e5Sgerardnico        // The array that hold the quality score details
222007225e5Sgerardnico        $qualityScores = array();
223007225e5Sgerardnico
224007225e5Sgerardnico
225007225e5Sgerardnico        /**
226007225e5Sgerardnico         * No fixme
227007225e5Sgerardnico         */
228ebdc69ceSgerardnico        if (array_key_exists(self::FIXME, $this->stats)) {
229007225e5Sgerardnico            $fixmeCount = $this->stats[self::FIXME];
230007225e5Sgerardnico            $statExport[self::FIXME] = $fixmeCount == null ? 0 : $fixmeCount;
231007225e5Sgerardnico            if ($fixmeCount != 0) {
232007225e5Sgerardnico                $ruleResults[self::RULE_FIXME] = self::FAILED;
233007225e5Sgerardnico                $qualityScores['no_' . self::FIXME] = 0;
234007225e5Sgerardnico            } else {
235007225e5Sgerardnico                $ruleResults[self::RULE_FIXME] = self::PASSED;
2367c33ecc6Sgerardnico                $qualityScores['no_' . self::FIXME] = $this->getConf(self::CONF_QUALITY_SCORE_NO_FIXME, 1);
237007225e5Sgerardnico            }
238ebdc69ceSgerardnico        }
239007225e5Sgerardnico
240007225e5Sgerardnico        /**
241007225e5Sgerardnico         * A title should be present
242007225e5Sgerardnico         */
24308ca4f85Sgerardnico        $titleScore = $this->getConf(self::CONF_QUALITY_SCORE_TITLE_PRESENT, 10);
244*37748cd8SNickeau        if (empty($this->metadata[Analytics::TITLE])) {
245007225e5Sgerardnico            $ruleResults[self::RULE_TITLE_PRESENT] = self::FAILED;
24608ca4f85Sgerardnico            $ruleInfo[self::RULE_TITLE_PRESENT] = "Add a title in the frontmatter for {$titleScore} points";
247*37748cd8SNickeau            $this->metadata[Analytics::TITLE] = $dokuWikiMetadata[Analytics::TITLE];
248007225e5Sgerardnico            $qualityScores[self::RULE_TITLE_PRESENT] = 0;
249007225e5Sgerardnico        } else {
2507c33ecc6Sgerardnico            $qualityScores[self::RULE_TITLE_PRESENT] = $titleScore;
251007225e5Sgerardnico            $ruleResults[self::RULE_TITLE_PRESENT] = self::PASSED;
252007225e5Sgerardnico        }
253007225e5Sgerardnico
254007225e5Sgerardnico        /**
255007225e5Sgerardnico         * A description should be present
256007225e5Sgerardnico         */
25708ca4f85Sgerardnico        $descScore = $this->getConf(self::CONF_QUALITY_SCORE_DESCRIPTION_PRESENT, 8);
258*37748cd8SNickeau        if (empty($this->metadata[self::DESCRIPTION])) {
259007225e5Sgerardnico            $ruleResults[self::RULE_DESCRIPTION_PRESENT] = self::FAILED;
26008ca4f85Sgerardnico            $ruleInfo[self::RULE_DESCRIPTION_PRESENT] = "Add a description in the frontmatter for {$descScore} points";
261*37748cd8SNickeau            $this->metadata[self::DESCRIPTION] = $dokuWikiMetadata[self::DESCRIPTION]["abstract"];
262007225e5Sgerardnico            $qualityScores[self::RULE_DESCRIPTION_PRESENT] = 0;
263007225e5Sgerardnico        } else {
2647c33ecc6Sgerardnico            $qualityScores[self::RULE_DESCRIPTION_PRESENT] = $descScore;
265007225e5Sgerardnico            $ruleResults[self::RULE_DESCRIPTION_PRESENT] = self::PASSED;
266007225e5Sgerardnico        }
267007225e5Sgerardnico
268007225e5Sgerardnico        /**
269007225e5Sgerardnico         * A canonical should be present
270007225e5Sgerardnico         */
27108ca4f85Sgerardnico        $canonicalScore = $this->getConf(self::CONF_QUALITY_SCORE_CANONICAL_PRESENT, 5);
272*37748cd8SNickeau        if (empty($this->metadata[Page::CANONICAL_PROPERTY])) {
273f3748b38Sgerardnico            global $conf;
274f3748b38Sgerardnico            $root = $conf['start'];
275f3748b38Sgerardnico            if ($ID != $root) {
276007225e5Sgerardnico                $qualityScores[self::RULE_CANONICAL_PRESENT] = 0;
277007225e5Sgerardnico                $ruleResults[self::RULE_CANONICAL_PRESENT] = self::FAILED;
27808ca4f85Sgerardnico                $ruleInfo[self::RULE_CANONICAL_PRESENT] = "Add a canonical in the frontmatter for {$canonicalScore} points";
279f3748b38Sgerardnico            }
280007225e5Sgerardnico        } else {
2817c33ecc6Sgerardnico            $qualityScores[self::RULE_CANONICAL_PRESENT] = $canonicalScore;
282007225e5Sgerardnico            $ruleResults[self::RULE_CANONICAL_PRESENT] = self::PASSED;
283007225e5Sgerardnico        }
284007225e5Sgerardnico
285007225e5Sgerardnico        /**
286007225e5Sgerardnico         * Outline / Header structure
287007225e5Sgerardnico         */
288007225e5Sgerardnico        $treeError = 0;
289007225e5Sgerardnico        $headersCount = 0;
290007225e5Sgerardnico        if (array_key_exists(Analytics::HEADER_POSITION, $this->stats)) {
291007225e5Sgerardnico            $headersCount = count($this->stats[Analytics::HEADER_POSITION]);
292007225e5Sgerardnico            unset($statExport[Analytics::HEADER_POSITION]);
293007225e5Sgerardnico            for ($i = 1; $i < $headersCount; $i++) {
294ebdc69ceSgerardnico                $currentHeaderLevel = $this->stats[self::HEADER_STRUCT][$i];
295ebdc69ceSgerardnico                $previousHeaderLevel = $this->stats[self::HEADER_STRUCT][$i - 1];
296007225e5Sgerardnico                if ($currentHeaderLevel - $previousHeaderLevel > 1) {
297007225e5Sgerardnico                    $treeError += 1;
298007225e5Sgerardnico                    $ruleInfo[self::RULE_OUTLINE_STRUCTURE] = "The " . $i . " header (h" . $currentHeaderLevel . ") has a level bigger than its precedent (" . $previousHeaderLevel . ")";
299007225e5Sgerardnico                }
300007225e5Sgerardnico            }
301ebdc69ceSgerardnico            unset($statExport[self::HEADER_STRUCT]);
302007225e5Sgerardnico        }
303eee76a3dSgerardnico        $outlinePoints = $this->getConf(self::CONF_QUALITY_SCORE_CORRECT_HEADER_STRUCTURE, 3);
304007225e5Sgerardnico        if ($treeError > 0 || $headersCount == 0) {
305007225e5Sgerardnico            $qualityScores['correct_outline'] = 0;
306007225e5Sgerardnico            $ruleResults[self::RULE_OUTLINE_STRUCTURE] = self::FAILED;
307007225e5Sgerardnico            if ($headersCount == 0) {
308eee76a3dSgerardnico                $ruleInfo[self::RULE_OUTLINE_STRUCTURE] = "Add headings to create a document outline for {$outlinePoints} points";
309007225e5Sgerardnico            }
310007225e5Sgerardnico        } else {
311eee76a3dSgerardnico            $qualityScores['correct_outline'] = $outlinePoints;
312007225e5Sgerardnico            $ruleResults[self::RULE_OUTLINE_STRUCTURE] = self::PASSED;
313007225e5Sgerardnico        }
314007225e5Sgerardnico
315007225e5Sgerardnico
316007225e5Sgerardnico        /**
317007225e5Sgerardnico         * Document length
318007225e5Sgerardnico         */
319007225e5Sgerardnico        $minimalWordCount = 50;
320007225e5Sgerardnico        $maximalWordCount = 1500;
321007225e5Sgerardnico        $correctContentLength = true;
32208ca4f85Sgerardnico        $correctLengthScore = $this->getConf(self::CONF_QUALITY_SCORE_CORRECT_CONTENT, 10);
323*37748cd8SNickeau        $missingWords = $minimalWordCount - $statExport[Analytics::WORD_COUNT];
32408ca4f85Sgerardnico        if ($missingWords > 0) {
325007225e5Sgerardnico            $ruleResults[self::RULE_WORDS_MINIMAL] = self::FAILED;
326007225e5Sgerardnico            $correctContentLength = false;
32708ca4f85Sgerardnico            $ruleInfo[self::RULE_WORDS_MINIMAL] = "Add {$missingWords} words to get {$correctLengthScore} points";
328007225e5Sgerardnico        } else {
329007225e5Sgerardnico            $ruleResults[self::RULE_WORDS_MINIMAL] = self::PASSED;
330007225e5Sgerardnico        }
331*37748cd8SNickeau        $tooMuchWords = $statExport[Analytics::WORD_COUNT] - $maximalWordCount;
33208ca4f85Sgerardnico        if ($tooMuchWords > 0) {
333007225e5Sgerardnico            $ruleResults[self::RULE_WORDS_MAXIMAL] = self::FAILED;
33408ca4f85Sgerardnico            $ruleInfo[self::RULE_WORDS_MAXIMAL] = "Delete {$tooMuchWords} words to get {$correctLengthScore} points";
335007225e5Sgerardnico            $correctContentLength = false;
336007225e5Sgerardnico        } else {
337007225e5Sgerardnico            $ruleResults[self::RULE_WORDS_MAXIMAL] = self::PASSED;
338007225e5Sgerardnico        }
339007225e5Sgerardnico        if ($correctContentLength) {
34008ca4f85Sgerardnico            $qualityScores['correct_content_length'] = $correctLengthScore;
341007225e5Sgerardnico        } else {
342007225e5Sgerardnico            $qualityScores['correct_content_length'] = 0;
343007225e5Sgerardnico        }
344007225e5Sgerardnico
345007225e5Sgerardnico
346007225e5Sgerardnico        /**
347007225e5Sgerardnico         * Average Number of words by header section to text ratio
348007225e5Sgerardnico         */
349007225e5Sgerardnico        $headers = $this->stats[Analytics::HEADERS_COUNT];
350007225e5Sgerardnico        if ($headers != null) {
351007225e5Sgerardnico            $headerCount = array_sum($headers);
352007225e5Sgerardnico            $headerCount--; // h1 is supposed to have no words
353007225e5Sgerardnico            if ($headerCount > 0) {
354007225e5Sgerardnico
355*37748cd8SNickeau                $avgWordsCountBySection = round($this->stats[Analytics::WORD_COUNT] / $headerCount);
356007225e5Sgerardnico                $statExport['word_section_count']['avg'] = $avgWordsCountBySection;
357007225e5Sgerardnico
358007225e5Sgerardnico                /**
359007225e5Sgerardnico                 * Min words by header section
360007225e5Sgerardnico                 */
361007225e5Sgerardnico                $wordsByHeaderMin = 20;
362007225e5Sgerardnico                /**
363007225e5Sgerardnico                 * Max words by header section
364007225e5Sgerardnico                 */
365007225e5Sgerardnico                $wordsByHeaderMax = 300;
366007225e5Sgerardnico                $correctAverageWordsBySection = true;
367007225e5Sgerardnico                if ($avgWordsCountBySection < $wordsByHeaderMin) {
368007225e5Sgerardnico                    $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MIN] = self::FAILED;
369007225e5Sgerardnico                    $correctAverageWordsBySection = false;
37008ca4f85Sgerardnico                    $ruleInfo[self::RULE_AVERAGE_WORDS_BY_SECTION_MIN] = "The number of words by section is less than {$wordsByHeaderMin}";
371007225e5Sgerardnico                } else {
372007225e5Sgerardnico                    $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MIN] = self::PASSED;
373007225e5Sgerardnico                }
374007225e5Sgerardnico                if ($avgWordsCountBySection > $wordsByHeaderMax) {
375007225e5Sgerardnico                    $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MAX] = self::FAILED;
376007225e5Sgerardnico                    $correctAverageWordsBySection = false;
377007225e5Sgerardnico                    $ruleInfo[self::RULE_AVERAGE_WORDS_BY_SECTION_MAX] = "The number of words by section is more than {$wordsByHeaderMax}";
378007225e5Sgerardnico                } else {
379007225e5Sgerardnico                    $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MAX] = self::PASSED;
380007225e5Sgerardnico                }
381007225e5Sgerardnico                if ($correctAverageWordsBySection) {
382007225e5Sgerardnico                    $qualityScores['correct_word_avg_by_section'] = $this->getConf(self::CONF_QUALITY_SCORE_CORRECT_WORD_SECTION_AVERAGE, 10);
383007225e5Sgerardnico                } else {
384007225e5Sgerardnico                    $qualityScores['correct_word_avg_by_section'] = 0;
385007225e5Sgerardnico                }
386007225e5Sgerardnico
387007225e5Sgerardnico            }
388007225e5Sgerardnico        }
389007225e5Sgerardnico
390007225e5Sgerardnico        /**
391007225e5Sgerardnico         * Internal Backlinks rule
392007225e5Sgerardnico         *
393007225e5Sgerardnico         * If a page is a low quality page, if the process run
394007225e5Sgerardnico         * anonymous, we will not see all {@link ft_backlinks()}
395007225e5Sgerardnico         * we use then the index directly to avoid confusion
396007225e5Sgerardnico         */
397007225e5Sgerardnico        $backlinks = idx_get_indexer()->lookupKey('relation_references', $ID);
398007225e5Sgerardnico        $countBacklinks = count($backlinks);
399007225e5Sgerardnico        $statExport[Analytics::INTERNAL_BACKLINKS_COUNT] = $countBacklinks;
400d262537cSgerardnico        $backlinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_BACKLINK_FACTOR, 1);
401007225e5Sgerardnico        if ($countBacklinks == 0) {
402007225e5Sgerardnico            $qualityScores[Analytics::INTERNAL_BACKLINKS_COUNT] = 0;
403007225e5Sgerardnico            $ruleResults[self::RULE_INTERNAL_BACKLINKS_MIN] = self::FAILED;
404d262537cSgerardnico            $ruleInfo[self::RULE_INTERNAL_BACKLINKS_MIN] = "Add backlinks for {$backlinkScore} point each";
405007225e5Sgerardnico        } else {
406d262537cSgerardnico
407d262537cSgerardnico            $qualityScores[Analytics::INTERNAL_BACKLINKS_COUNT] = $countBacklinks * $backlinkScore;
408007225e5Sgerardnico            $ruleResults[self::RULE_INTERNAL_BACKLINKS_MIN] = self::PASSED;
409007225e5Sgerardnico        }
410007225e5Sgerardnico
411007225e5Sgerardnico        /**
412007225e5Sgerardnico         * Internal links
413007225e5Sgerardnico         */
414007225e5Sgerardnico        $internalLinksCount = $this->stats[Analytics::INTERNAL_LINKS_COUNT];
415d262537cSgerardnico        $internalLinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_LINK_FACTOR, 1);
416007225e5Sgerardnico        if ($internalLinksCount == 0) {
417007225e5Sgerardnico            $qualityScores[Analytics::INTERNAL_LINKS_COUNT] = 0;
418007225e5Sgerardnico            $ruleResults[self::RULE_INTERNAL_LINKS_MIN] = self::FAILED;
419d262537cSgerardnico            $ruleInfo[self::RULE_INTERNAL_LINKS_MIN] = "Add internal links for {$internalLinkScore} point each";
420007225e5Sgerardnico        } else {
421007225e5Sgerardnico            $ruleResults[self::RULE_INTERNAL_LINKS_MIN] = self::PASSED;
422d262537cSgerardnico            $qualityScores[Analytics::INTERNAL_LINKS_COUNT] = $countBacklinks * $internalLinkScore;
423007225e5Sgerardnico        }
424007225e5Sgerardnico
425007225e5Sgerardnico        /**
426007225e5Sgerardnico         * Broken Links
427007225e5Sgerardnico         */
428d262537cSgerardnico        $brokenLinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_LINK_BROKEN_FACTOR, 2);
429ebdc69ceSgerardnico        $brokenLinksCount = 0;
430ebdc69ceSgerardnico        if (array_key_exists(Analytics::INTERNAL_LINKS_BROKEN_COUNT, $this->stats)) {
431007225e5Sgerardnico            $brokenLinksCount = $this->stats[Analytics::INTERNAL_LINKS_BROKEN_COUNT];
432ebdc69ceSgerardnico        }
433007225e5Sgerardnico        if ($brokenLinksCount > 2) {
434007225e5Sgerardnico            $qualityScores['no_' . Analytics::INTERNAL_LINKS_BROKEN_COUNT] = 0;
435007225e5Sgerardnico            $ruleResults[self::RULE_INTERNAL_BROKEN_LINKS_MAX] = self::FAILED;
436d262537cSgerardnico            $ruleInfo[self::RULE_INTERNAL_BROKEN_LINKS_MAX] = "Delete the {$brokenLinksCount} broken links and add {$brokenLinkScore} points";
437007225e5Sgerardnico        } else {
438d262537cSgerardnico            $qualityScores['no_' . Analytics::INTERNAL_LINKS_BROKEN_COUNT] = $brokenLinkScore;
439007225e5Sgerardnico            $ruleResults[self::RULE_INTERNAL_BROKEN_LINKS_MAX] = self::PASSED;
440007225e5Sgerardnico        }
441007225e5Sgerardnico
442007225e5Sgerardnico        /**
443e8b2ff59SNickeau         * Media
444e8b2ff59SNickeau         */
445e8b2ff59SNickeau        $mediasStats = [
446e8b2ff59SNickeau            "total_count" => self::getAndUnset($statExport, Analytics::MEDIAS_COUNT, 0),
447e8b2ff59SNickeau            "internal_count" => self::getAndUnset($statExport, Analytics::INTERNAL_MEDIAS_COUNT, 0),
448e8b2ff59SNickeau            "internal_broken_count" => self::getAndUnset($statExport, Analytics::INTERNAL_BROKEN_MEDIAS_COUNT, 0),
449e8b2ff59SNickeau            "external_count" => self::getAndUnset($statExport, Analytics::EXTERNAL_MEDIAS_COUNT, 0)
450e8b2ff59SNickeau        ];
451e8b2ff59SNickeau        $statExport['media'] = $mediasStats;
452e8b2ff59SNickeau
453e8b2ff59SNickeau        /**
454007225e5Sgerardnico         * Changes, the more changes the better
455007225e5Sgerardnico         */
456ebdc69ceSgerardnico        $qualityScores[Analytics::EDITS_COUNT] = $statExport[Analytics::EDITS_COUNT] * $this->getConf(self::CONF_QUALITY_SCORE_CHANGES_FACTOR, 0.25);
457007225e5Sgerardnico
458007225e5Sgerardnico
459007225e5Sgerardnico        /**
460007225e5Sgerardnico         * Quality Score
461007225e5Sgerardnico         */
462007225e5Sgerardnico        ksort($qualityScores);
463007225e5Sgerardnico        $qualityScoring = array();
46408ca4f85Sgerardnico        $qualityScoring[self::SCORE] = array_sum($qualityScores);
465007225e5Sgerardnico        $qualityScoring["scores"] = $qualityScores;
466007225e5Sgerardnico
467007225e5Sgerardnico
468007225e5Sgerardnico        /**
469007225e5Sgerardnico         * The rule that if broken will set the quality level to low
470007225e5Sgerardnico         */
471007225e5Sgerardnico        $brokenRules = array();
472007225e5Sgerardnico        foreach ($ruleResults as $ruleName => $ruleResult) {
473007225e5Sgerardnico            if ($ruleResult == self::FAILED) {
474007225e5Sgerardnico                $brokenRules[] = $ruleName;
475007225e5Sgerardnico            }
476007225e5Sgerardnico        }
477007225e5Sgerardnico        $ruleErrorCount = sizeof($brokenRules);
478007225e5Sgerardnico        if ($ruleErrorCount > 0) {
479007225e5Sgerardnico            $qualityResult = $ruleErrorCount . " quality rules errors";
480007225e5Sgerardnico        } else {
481007225e5Sgerardnico            $qualityResult = "All quality rules passed";
482007225e5Sgerardnico        }
483007225e5Sgerardnico
484007225e5Sgerardnico        /**
485fa5961eaSgerardnico         * Low level Computation
486007225e5Sgerardnico         */
487007225e5Sgerardnico        $mandatoryRules = preg_split("/,/", $this->getConf(self::CONF_MANDATORY_QUALITY_RULES));
488007225e5Sgerardnico        $mandatoryRulesBroken = [];
489007225e5Sgerardnico        foreach ($mandatoryRules as $lowLevelRule) {
490007225e5Sgerardnico            if (in_array($lowLevelRule, $brokenRules)) {
491007225e5Sgerardnico                $mandatoryRulesBroken[] = $lowLevelRule;
492007225e5Sgerardnico            }
493007225e5Sgerardnico        }
494fa5961eaSgerardnico        /**
4956f847fc2Sgerardnico         * If the low level is not set manually
496fa5961eaSgerardnico         */
497*37748cd8SNickeau        if (empty($this->metadata[Page::LOW_QUALITY_PAGE_INDICATOR])) {
498007225e5Sgerardnico            $lowLevel = false;
49985e82846SNickeau            $brokenRulesCount = sizeof($mandatoryRulesBroken);
50085e82846SNickeau            if ($brokenRulesCount > 0) {
501007225e5Sgerardnico                $lowLevel = true;
50285e82846SNickeau                $quality["message"] = "$brokenRulesCount mandatory rules broken.";
50385e82846SNickeau            } else {
50485e82846SNickeau                $quality["message"] = "No mandatory rules broken";
505007225e5Sgerardnico            }
5069b9e6d1fSgerardnico        } else {
507*37748cd8SNickeau            $lowLevel = filter_var($this->metadata[Page::LOW_QUALITY_PAGE_INDICATOR], FILTER_VALIDATE_BOOLEAN);
5089b9e6d1fSgerardnico        }
509531e725cSNickeau        if (!$this->page->isSlot()) {
5106f847fc2Sgerardnico            $this->page->setLowQualityIndicator($lowLevel);
5115f891b7eSNickeau        } else {
5125f891b7eSNickeau            $this->page->setLowQualityIndicator(false);
5135f891b7eSNickeau        }
514007225e5Sgerardnico
515007225e5Sgerardnico        /**
516007225e5Sgerardnico         * Building the quality object in order
517007225e5Sgerardnico         */
518f3748b38Sgerardnico        $quality[Analytics::LOW] = $lowLevel;
519007225e5Sgerardnico        if (sizeof($mandatoryRulesBroken) > 0) {
520007225e5Sgerardnico            ksort($mandatoryRulesBroken);
521722648eaSgerardnico            $quality[Analytics::FAILED_MANDATORY_RULES] = $mandatoryRulesBroken;
522007225e5Sgerardnico        }
52308ca4f85Sgerardnico        $quality[self::SCORING] = $qualityScoring;
524f3748b38Sgerardnico        $quality[Analytics::RULES][self::RESULT] = $qualityResult;
525007225e5Sgerardnico        if (!empty($ruleInfo)) {
526f3748b38Sgerardnico            $quality[Analytics::RULES]["info"] = $ruleInfo;
527007225e5Sgerardnico        }
528007225e5Sgerardnico
529007225e5Sgerardnico        ksort($ruleResults);
530f3748b38Sgerardnico        $quality[Analytics::RULES][Analytics::DETAILS] = $ruleResults;
531007225e5Sgerardnico
532007225e5Sgerardnico        /**
533007225e5Sgerardnico         * Metadata
534007225e5Sgerardnico         */
535*37748cd8SNickeau        $page = Page::createPageFromCurrentId();
536*37748cd8SNickeau        $meta = $page->getMetadataStandard();
537*37748cd8SNickeau        foreach ($meta as $key => $value) {
538*37748cd8SNickeau            /**
539*37748cd8SNickeau             * The metadata may have been set
540*37748cd8SNickeau             * by frontmatter
541*37748cd8SNickeau             */
542*37748cd8SNickeau            if (!isset($this->metadata[$key])) {
543*37748cd8SNickeau                $this->metadata[$key] = $value;
544c42a1196Sgerardnico            }
545*37748cd8SNickeau        }
546007225e5Sgerardnico
547007225e5Sgerardnico
548007225e5Sgerardnico        /**
549007225e5Sgerardnico         * Building the Top JSON in order
550007225e5Sgerardnico         */
551007225e5Sgerardnico        global $ID;
5522c067407Sgerardnico        $finalStats = array();
553c42a1196Sgerardnico        $finalStats["date"] = date('Y-m-d H:i:s', time());
554*37748cd8SNickeau        ksort($this->metadata);
555*37748cd8SNickeau        $finalStats[Analytics::METADATA] = $this->metadata;
556007225e5Sgerardnico        ksort($statExport);
5572c067407Sgerardnico        $finalStats[Analytics::STATISTICS] = $statExport;
5582c067407Sgerardnico        $finalStats[Analytics::QUALITY] = $quality; // Quality after the sort to get them at the end
559007225e5Sgerardnico
560007225e5Sgerardnico
561007225e5Sgerardnico        /**
562007225e5Sgerardnico         * The result can be seen with
563007225e5Sgerardnico         * doku.php?id=somepage&do=export_combo_analysis
5647c33ecc6Sgerardnico         *
5657c33ecc6Sgerardnico         * Set the header temporarily for the export.php file
56685e82846SNickeau         *
56785e82846SNickeau         * The mode in the export is
568007225e5Sgerardnico         */
56985e82846SNickeau        $mode = "combo_" . $this->getPluginComponent();
5707c33ecc6Sgerardnico        p_set_metadata(
5717c33ecc6Sgerardnico            $ID,
57285e82846SNickeau            array("format" => array($mode => array("Content-Type" => 'application/json'))),
5737c33ecc6Sgerardnico            false,
57485e82846SNickeau            true // Persistence is needed because there is a cache
5757c33ecc6Sgerardnico        );
5762c067407Sgerardnico        $json_encoded = json_encode($finalStats, JSON_PRETTY_PRINT);
577007225e5Sgerardnico
5787c33ecc6Sgerardnico        $this->page->saveAnalytics($finalStats);
579007225e5Sgerardnico        $this->doc .= $json_encoded;
580007225e5Sgerardnico
581007225e5Sgerardnico    }
582007225e5Sgerardnico
583007225e5Sgerardnico    /**
584007225e5Sgerardnico     */
585007225e5Sgerardnico    public function getFormat()
586007225e5Sgerardnico    {
587531e725cSNickeau        return self::RENDERER_FORMAT;
588007225e5Sgerardnico    }
589007225e5Sgerardnico
590007225e5Sgerardnico    public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content')
591007225e5Sgerardnico    {
592007225e5Sgerardnico
5939b9e6d1fSgerardnico        $link = new LinkUtility($id);
5949b9e6d1fSgerardnico        $link->setType(LinkUtility::TYPE_INTERNAL);
5959b9e6d1fSgerardnico        $link->processLinkStats($this->stats);
596007225e5Sgerardnico
597007225e5Sgerardnico    }
598007225e5Sgerardnico
599007225e5Sgerardnico    public function externallink($url, $name = null)
600007225e5Sgerardnico    {
601ef295d81Sgerardnico        $link = new LinkUtility($url);
602ef295d81Sgerardnico        $link->setType(LinkUtility::TYPE_EXTERNAL);
603ef295d81Sgerardnico        if ($name != null) {
604ef295d81Sgerardnico            $link->setName($name);
605ef295d81Sgerardnico        }
606ef295d81Sgerardnico        $link->processLinkStats($this->stats);
607007225e5Sgerardnico    }
608007225e5Sgerardnico
609007225e5Sgerardnico    public function header($text, $level, $pos)
610007225e5Sgerardnico    {
611ebdc69ceSgerardnico        if (!array_key_exists(Analytics::HEADERS_COUNT, $this->stats)) {
612ebdc69ceSgerardnico            $this->stats[Analytics::HEADERS_COUNT] = [];
613ebdc69ceSgerardnico        }
614ebdc69ceSgerardnico        $heading = 'h' . $level;
615ebdc69ceSgerardnico        if (!array_key_exists(
616ebdc69ceSgerardnico            $heading,
617ebdc69ceSgerardnico            $this->stats[Analytics::HEADERS_COUNT])) {
618ebdc69ceSgerardnico            $this->stats[Analytics::HEADERS_COUNT][$heading] = 0;
619ebdc69ceSgerardnico        }
620ebdc69ceSgerardnico        $this->stats[Analytics::HEADERS_COUNT][$heading]++;
621ebdc69ceSgerardnico
622007225e5Sgerardnico        $this->headerId++;
623ebdc69ceSgerardnico        $this->stats[Analytics::HEADER_POSITION][$this->headerId] = $heading;
624ebdc69ceSgerardnico
625ebdc69ceSgerardnico        /**
626ebdc69ceSgerardnico         * Store the level of each heading
627ebdc69ceSgerardnico         * They should only go from low to highest value
628ebdc69ceSgerardnico         * for a good outline
629ebdc69ceSgerardnico         */
630ebdc69ceSgerardnico        if (!array_key_exists(Analytics::HEADERS_COUNT, $this->stats)) {
631ebdc69ceSgerardnico            $this->stats[self::HEADER_STRUCT] = [];
632ebdc69ceSgerardnico        }
633ebdc69ceSgerardnico        $this->stats[self::HEADER_STRUCT][] = $level;
634007225e5Sgerardnico
635007225e5Sgerardnico    }
636007225e5Sgerardnico
637007225e5Sgerardnico    public function smiley($smiley)
638007225e5Sgerardnico    {
639007225e5Sgerardnico        if ($smiley == 'FIXME') $this->stats[self::FIXME]++;
640007225e5Sgerardnico    }
641007225e5Sgerardnico
642007225e5Sgerardnico    public function linebreak()
643007225e5Sgerardnico    {
644007225e5Sgerardnico        if (!$this->tableopen) {
645007225e5Sgerardnico            $this->stats['linebreak']++;
646007225e5Sgerardnico        }
647007225e5Sgerardnico    }
648007225e5Sgerardnico
649007225e5Sgerardnico    public function table_open($maxcols = null, $numrows = null, $pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
650007225e5Sgerardnico    {
651007225e5Sgerardnico        $this->tableopen = true;
652007225e5Sgerardnico    }
653007225e5Sgerardnico
654007225e5Sgerardnico    public function table_close($pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
655007225e5Sgerardnico    {
656007225e5Sgerardnico        $this->tableopen = false;
657007225e5Sgerardnico    }
658007225e5Sgerardnico
659007225e5Sgerardnico    public function hr()
660007225e5Sgerardnico    {
661007225e5Sgerardnico        $this->stats['hr']++;
662007225e5Sgerardnico    }
663007225e5Sgerardnico
664007225e5Sgerardnico    public function quote_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
665007225e5Sgerardnico    {
666007225e5Sgerardnico        $this->stats['quote_count']++;
667007225e5Sgerardnico        $this->quotelevel++;
668007225e5Sgerardnico        $this->stats['quote_nest'] = max($this->quotelevel, $this->stats['quote_nest']);
669007225e5Sgerardnico    }
670007225e5Sgerardnico
671007225e5Sgerardnico    public function quote_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
672007225e5Sgerardnico    {
673007225e5Sgerardnico        $this->quotelevel--;
674007225e5Sgerardnico    }
675007225e5Sgerardnico
676007225e5Sgerardnico    public function strong_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
677007225e5Sgerardnico    {
678007225e5Sgerardnico        $this->formattingBracket++;
679007225e5Sgerardnico    }
680007225e5Sgerardnico
681007225e5Sgerardnico    public function strong_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
682007225e5Sgerardnico    {
683007225e5Sgerardnico        $this->formattingBracket--;
684007225e5Sgerardnico    }
685007225e5Sgerardnico
686007225e5Sgerardnico    public function emphasis_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
687007225e5Sgerardnico    {
688007225e5Sgerardnico        $this->formattingBracket++;
689007225e5Sgerardnico    }
690007225e5Sgerardnico
691007225e5Sgerardnico    public function emphasis_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
692007225e5Sgerardnico    {
693007225e5Sgerardnico        $this->formattingBracket--;
694007225e5Sgerardnico    }
695007225e5Sgerardnico
696007225e5Sgerardnico    public function underline_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
697007225e5Sgerardnico    {
698007225e5Sgerardnico        $this->formattingBracket++;
699007225e5Sgerardnico    }
700007225e5Sgerardnico
701007225e5Sgerardnico    public function underline_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
702007225e5Sgerardnico    {
703007225e5Sgerardnico        $this->formattingBracket--;
704007225e5Sgerardnico    }
705007225e5Sgerardnico
706007225e5Sgerardnico    public function cdata($text)
707007225e5Sgerardnico    {
708007225e5Sgerardnico
709007225e5Sgerardnico        /**
710007225e5Sgerardnico         * It seems that you receive cdata
711007225e5Sgerardnico         * when emphasis_open / underline_open / strong_open
712007225e5Sgerardnico         * Stats are not for them
713007225e5Sgerardnico         */
714007225e5Sgerardnico        if (!$this->formattingBracket) return;
715007225e5Sgerardnico
716007225e5Sgerardnico        $this->plainTextId++;
717007225e5Sgerardnico
718007225e5Sgerardnico        /**
719007225e5Sgerardnico         * Length
720007225e5Sgerardnico         */
721007225e5Sgerardnico        $len = strlen($text);
722007225e5Sgerardnico        $this->stats[self::PLAINTEXT][$this->plainTextId]['len'] = $len;
723007225e5Sgerardnico
724007225e5Sgerardnico
725007225e5Sgerardnico        /**
726007225e5Sgerardnico         * Multi-formatting
727007225e5Sgerardnico         */
728007225e5Sgerardnico        if ($this->formattingBracket > 1) {
729007225e5Sgerardnico            $numberOfFormats = 1 * ($this->formattingBracket - 1);
730007225e5Sgerardnico            $this->stats[self::PLAINTEXT][$this->plainTextId]['multiformat'] += $numberOfFormats;
731007225e5Sgerardnico        }
732007225e5Sgerardnico
733007225e5Sgerardnico        /**
734007225e5Sgerardnico         * Total
735007225e5Sgerardnico         */
736007225e5Sgerardnico        $this->stats[self::PLAINTEXT][0] += $len;
737007225e5Sgerardnico    }
738007225e5Sgerardnico
739007225e5Sgerardnico    public function internalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null)
740007225e5Sgerardnico    {
741007225e5Sgerardnico        $this->stats[Analytics::INTERNAL_MEDIAS_COUNT]++;
742007225e5Sgerardnico    }
743007225e5Sgerardnico
744007225e5Sgerardnico    public function externalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null)
745007225e5Sgerardnico    {
746e8b2ff59SNickeau        $this->stats[Analytics::EXTERNAL_MEDIAS_COUNT]++;
747007225e5Sgerardnico    }
748007225e5Sgerardnico
749007225e5Sgerardnico    public function reset()
750007225e5Sgerardnico    {
751007225e5Sgerardnico        $this->stats = array();
752*37748cd8SNickeau        $this->metadata = array();
753007225e5Sgerardnico        $this->headerId = 0;
754007225e5Sgerardnico    }
755007225e5Sgerardnico
756007225e5Sgerardnico    public function setMeta($key, $value)
757007225e5Sgerardnico    {
758*37748cd8SNickeau        $this->metadata[$key] = $value;
759007225e5Sgerardnico    }
760007225e5Sgerardnico
761007225e5Sgerardnico
762007225e5Sgerardnico}
763007225e5Sgerardnico
764