1007225e5Sgerardnico<?php 2007225e5Sgerardnico 3007225e5Sgerardnico 4*c3437056SNickeauuse ComboStrap\AnalyticsDocument; 5*c3437056SNickeauuse ComboStrap\BacklinkCount; 6*c3437056SNickeauuse ComboStrap\Canonical; 7007225e5Sgerardnicouse ComboStrap\LinkUtility; 8*c3437056SNickeauuse ComboStrap\MetadataDbStore; 971f916b9Sgerardnicouse ComboStrap\Page; 10*c3437056SNickeauuse ComboStrap\PageTitle; 11*c3437056SNickeauuse ComboStrap\PluginUtility; 1237748cd8SNickeauuse ComboStrap\StringUtility; 13007225e5Sgerardnicouse dokuwiki\ChangeLog\PageChangeLog; 14007225e5Sgerardnico 1537748cd8SNickeau 1637748cd8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 17007225e5Sgerardnico 18007225e5Sgerardnico 19007225e5Sgerardnico/** 20007225e5Sgerardnico * A analysis Renderer that exports stats/quality/metadata in a json format 21007225e5Sgerardnico * You can export the data with 22007225e5Sgerardnico * doku.php?id=somepage&do=export_combo_analytics 23007225e5Sgerardnico */ 24007225e5Sgerardnicoclass renderer_plugin_combo_analytics extends Doku_Renderer 25007225e5Sgerardnico{ 267c33ecc6Sgerardnico 27007225e5Sgerardnico const PLAINTEXT = 'formatted'; 28007225e5Sgerardnico const RESULT = "result"; 29007225e5Sgerardnico const DESCRIPTION = "description"; 30007225e5Sgerardnico const PASSED = "Passed"; 31007225e5Sgerardnico const FAILED = "Failed"; 32007225e5Sgerardnico const FIXME = 'fixme'; 33007225e5Sgerardnico 34007225e5Sgerardnico /** 35007225e5Sgerardnico * Rules key 36007225e5Sgerardnico */ 37007225e5Sgerardnico const RULE_WORDS_MINIMAL = 'words_min'; 38007225e5Sgerardnico const RULE_OUTLINE_STRUCTURE = "outline_structure"; 39007225e5Sgerardnico const RULE_INTERNAL_BACKLINKS_MIN = 'internal_backlinks_min'; 40007225e5Sgerardnico const RULE_WORDS_MAXIMAL = "words_max"; 41007225e5Sgerardnico const RULE_AVERAGE_WORDS_BY_SECTION_MIN = 'words_by_section_avg_min'; 42007225e5Sgerardnico const RULE_AVERAGE_WORDS_BY_SECTION_MAX = 'words_by_section_avg_max'; 43007225e5Sgerardnico const RULE_INTERNAL_LINKS_MIN = 'internal_links_min'; 44007225e5Sgerardnico const RULE_INTERNAL_BROKEN_LINKS_MAX = 'internal_links_broken_max'; 45007225e5Sgerardnico const RULE_DESCRIPTION_PRESENT = 'description_present'; 46007225e5Sgerardnico const RULE_FIXME = "fixme_min"; 47007225e5Sgerardnico const RULE_TITLE_PRESENT = "title_present"; 48007225e5Sgerardnico const RULE_CANONICAL_PRESENT = "canonical_present"; 49aa3cb38fSgerardnico const QUALITY_RULES = [ 50aa3cb38fSgerardnico self::RULE_CANONICAL_PRESENT, 51aa3cb38fSgerardnico self::RULE_DESCRIPTION_PRESENT, 52aa3cb38fSgerardnico self::RULE_FIXME, 53aa3cb38fSgerardnico self::RULE_INTERNAL_BACKLINKS_MIN, 54aa3cb38fSgerardnico self::RULE_INTERNAL_BROKEN_LINKS_MAX, 55aa3cb38fSgerardnico self::RULE_INTERNAL_LINKS_MIN, 56aa3cb38fSgerardnico self::RULE_OUTLINE_STRUCTURE, 57aa3cb38fSgerardnico self::RULE_TITLE_PRESENT, 58aa3cb38fSgerardnico self::RULE_WORDS_MINIMAL, 59aa3cb38fSgerardnico self::RULE_WORDS_MAXIMAL, 60aa3cb38fSgerardnico self::RULE_AVERAGE_WORDS_BY_SECTION_MIN, 61aa3cb38fSgerardnico self::RULE_AVERAGE_WORDS_BY_SECTION_MAX 62aa3cb38fSgerardnico ]; 63007225e5Sgerardnico 64007225e5Sgerardnico /** 65007225e5Sgerardnico * The default man 66007225e5Sgerardnico */ 67007225e5Sgerardnico const CONF_MANDATORY_QUALITY_RULES_DEFAULT_VALUE = [ 68007225e5Sgerardnico self::RULE_WORDS_MINIMAL, 69007225e5Sgerardnico self::RULE_INTERNAL_BACKLINKS_MIN, 70007225e5Sgerardnico self::RULE_INTERNAL_LINKS_MIN 71007225e5Sgerardnico ]; 72007225e5Sgerardnico const CONF_MANDATORY_QUALITY_RULES = "mandatoryQualityRules"; 73007225e5Sgerardnico 74007225e5Sgerardnico /** 75007225e5Sgerardnico * Quality Score factors 76007225e5Sgerardnico * They are used to calculate the score 77007225e5Sgerardnico */ 78007225e5Sgerardnico const CONF_QUALITY_SCORE_INTERNAL_BACKLINK_FACTOR = 'qualityScoreInternalBacklinksFactor'; 79007225e5Sgerardnico const CONF_QUALITY_SCORE_INTERNAL_LINK_FACTOR = 'qualityScoreInternalLinksFactor'; 80007225e5Sgerardnico const CONF_QUALITY_SCORE_TITLE_PRESENT = 'qualityScoreTitlePresent'; 81007225e5Sgerardnico const CONF_QUALITY_SCORE_CORRECT_HEADER_STRUCTURE = 'qualityScoreCorrectOutline'; 82007225e5Sgerardnico const CONF_QUALITY_SCORE_CORRECT_CONTENT = 'qualityScoreCorrectContentLength'; 83007225e5Sgerardnico const CONF_QUALITY_SCORE_NO_FIXME = 'qualityScoreNoFixMe'; 84007225e5Sgerardnico const CONF_QUALITY_SCORE_CORRECT_WORD_SECTION_AVERAGE = 'qualityScoreCorrectWordSectionAvg'; 85007225e5Sgerardnico const CONF_QUALITY_SCORE_INTERNAL_LINK_BROKEN_FACTOR = 'qualityScoreNoBrokenLinks'; 86007225e5Sgerardnico const CONF_QUALITY_SCORE_CHANGES_FACTOR = 'qualityScoreChangesFactor'; 87007225e5Sgerardnico const CONF_QUALITY_SCORE_DESCRIPTION_PRESENT = 'qualityScoreDescriptionPresent'; 88007225e5Sgerardnico const CONF_QUALITY_SCORE_CANONICAL_PRESENT = 'qualityScoreCanonicalPresent'; 8908ca4f85Sgerardnico const SCORING = "scoring"; 9008ca4f85Sgerardnico const SCORE = "score"; 91ebdc69ceSgerardnico const HEADER_STRUCT = 'header_struct'; 92531e725cSNickeau const RENDERER_NAME_MODE = "combo_" . renderer_plugin_combo_analytics::RENDERER_FORMAT; 93*c3437056SNickeau 94531e725cSNickeau /** 95531e725cSNickeau * The format returned by the renderer 96531e725cSNickeau */ 97531e725cSNickeau const RENDERER_FORMAT = "analytics"; 98007225e5Sgerardnico 99aa3cb38fSgerardnico 100007225e5Sgerardnico /** 101007225e5Sgerardnico * The processing data 102007225e5Sgerardnico * that should be {@link renderer_plugin_combo_analysis::reset()} 103007225e5Sgerardnico */ 104007225e5Sgerardnico public $stats = array(); // the stats 10537748cd8SNickeau protected $metadata = array(); // the metadata in frontmatter 106007225e5Sgerardnico protected $headerId = 0; // the id of the header on the page (first, second, ...) 107007225e5Sgerardnico 108007225e5Sgerardnico /** 109007225e5Sgerardnico * Don't known this variable ? 110007225e5Sgerardnico */ 111007225e5Sgerardnico protected $quotelevel = 0; 112007225e5Sgerardnico protected $formattingBracket = 0; 113007225e5Sgerardnico protected $tableopen = false; 114007225e5Sgerardnico private $plainTextId = 0; 1152c067407Sgerardnico /** 1162c067407Sgerardnico * @var Page 1172c067407Sgerardnico */ 1182c067407Sgerardnico private $page; 1192c067407Sgerardnico 120e8b2ff59SNickeau /** 121e8b2ff59SNickeau * Get and unset a value from an array 122e8b2ff59SNickeau * @param array $array 123e8b2ff59SNickeau * @param $key 124e8b2ff59SNickeau * @param $default 125e8b2ff59SNickeau * @return mixed 126e8b2ff59SNickeau */ 127e8b2ff59SNickeau private static function getAndUnset(array &$array, $key, $default) 128e8b2ff59SNickeau { 129e8b2ff59SNickeau if (isset($array[$key])) { 130e8b2ff59SNickeau $value = $array[$key]; 131e8b2ff59SNickeau unset($array[$key]); 132e8b2ff59SNickeau return $value; 133e8b2ff59SNickeau } 134e8b2ff59SNickeau return $default; 135e8b2ff59SNickeau 136e8b2ff59SNickeau } 137e8b2ff59SNickeau 1382c067407Sgerardnico public function document_start() 1392c067407Sgerardnico { 1407c33ecc6Sgerardnico $this->reset(); 141*c3437056SNickeau $this->page = Page::createPageFromGlobalDokuwikiId(); 1422c067407Sgerardnico 1432c067407Sgerardnico } 144007225e5Sgerardnico 145007225e5Sgerardnico 146007225e5Sgerardnico /** 147007225e5Sgerardnico * Here the score is calculated 148*c3437056SNickeau * @throws \ComboStrap\ExceptionCombo 149007225e5Sgerardnico */ 150007225e5Sgerardnico public function document_end() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 151007225e5Sgerardnico { 152007225e5Sgerardnico /** 153f3748b38Sgerardnico * The exported object 154f3748b38Sgerardnico */ 155f3748b38Sgerardnico $statExport = $this->stats; 156f3748b38Sgerardnico 157f3748b38Sgerardnico /** 158007225e5Sgerardnico * The metadata 159007225e5Sgerardnico */ 160007225e5Sgerardnico global $ID; 161fa5961eaSgerardnico $dokuWikiMetadata = p_get_metadata($ID); 162007225e5Sgerardnico 163007225e5Sgerardnico /** 164f3748b38Sgerardnico * Edit author stats 165f3748b38Sgerardnico */ 166f3748b38Sgerardnico $changelog = new PageChangeLog($ID); 167f3748b38Sgerardnico $revs = $changelog->getRevisions(0, 10000); 168fa5961eaSgerardnico array_push($revs, $dokuWikiMetadata['last_change']['date']); 169*c3437056SNickeau $statExport[AnalyticsDocument::EDITS_COUNT] = count($revs); 170f3748b38Sgerardnico foreach ($revs as $rev) { 1712128d419Sgerardnico 172ebdc69ceSgerardnico 173ebdc69ceSgerardnico /** 174ebdc69ceSgerardnico * Init the authors array 175ebdc69ceSgerardnico */ 176ebdc69ceSgerardnico if (!array_key_exists('authors', $statExport)) { 177ebdc69ceSgerardnico $statExport['authors'] = []; 178f3748b38Sgerardnico } 179ebdc69ceSgerardnico /** 180ebdc69ceSgerardnico * Analytics by users 181ebdc69ceSgerardnico */ 1822128d419Sgerardnico $info = $changelog->getRevisionInfo($rev); 1832128d419Sgerardnico if (is_array($info)) { 184ebdc69ceSgerardnico $user = "*"; 185ebdc69ceSgerardnico if (array_key_exists('user', $info)) { 186ebdc69ceSgerardnico $user = $info['user']; 187ebdc69ceSgerardnico } 188ebdc69ceSgerardnico if (!array_key_exists('authors', $statExport['authors'])) { 189ebdc69ceSgerardnico $statExport['authors'][$user] = 0; 190ebdc69ceSgerardnico } 191ebdc69ceSgerardnico $statExport['authors'][$user] += 1; 192f3748b38Sgerardnico } 1932128d419Sgerardnico } 194f3748b38Sgerardnico 195f3748b38Sgerardnico /** 196007225e5Sgerardnico * Word and chars count 197007225e5Sgerardnico * The word count does not take into account 198007225e5Sgerardnico * words with non-words characters such as < = 199007225e5Sgerardnico * Therefore the node and attribute are not taken in the count 200007225e5Sgerardnico */ 201007225e5Sgerardnico $text = rawWiki($ID); 202*c3437056SNickeau $statExport[AnalyticsDocument::CHAR_COUNT] = strlen($text); 203*c3437056SNickeau $statExport[AnalyticsDocument::WORD_COUNT] = StringUtility::getWordCount($text); 204007225e5Sgerardnico 205007225e5Sgerardnico 206007225e5Sgerardnico /** 207007225e5Sgerardnico * Internal link distance summary calculation 208007225e5Sgerardnico */ 209*c3437056SNickeau if (array_key_exists(AnalyticsDocument::INTERNAL_LINK_DISTANCE, $statExport)) { 210*c3437056SNickeau $linkLengths = $statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]; 211*c3437056SNickeau unset($statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]); 212007225e5Sgerardnico $countBacklinks = count($linkLengths); 213*c3437056SNickeau $statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]['avg'] = null; 214*c3437056SNickeau $statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]['max'] = null; 215*c3437056SNickeau $statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]['min'] = null; 216007225e5Sgerardnico if ($countBacklinks > 0) { 217*c3437056SNickeau $statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]['avg'] = array_sum($linkLengths) / $countBacklinks; 218*c3437056SNickeau $statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]['max'] = max($linkLengths); 219*c3437056SNickeau $statExport[AnalyticsDocument::INTERNAL_LINK_DISTANCE]['min'] = min($linkLengths); 220007225e5Sgerardnico } 221007225e5Sgerardnico } 222007225e5Sgerardnico 223007225e5Sgerardnico /** 224007225e5Sgerardnico * Quality Report / Rules 225007225e5Sgerardnico */ 226007225e5Sgerardnico // The array that hold the results of the quality rules 227007225e5Sgerardnico $ruleResults = array(); 228007225e5Sgerardnico // The array that hold the quality score details 229007225e5Sgerardnico $qualityScores = array(); 230007225e5Sgerardnico 231007225e5Sgerardnico 232007225e5Sgerardnico /** 233007225e5Sgerardnico * No fixme 234007225e5Sgerardnico */ 235ebdc69ceSgerardnico if (array_key_exists(self::FIXME, $this->stats)) { 236007225e5Sgerardnico $fixmeCount = $this->stats[self::FIXME]; 237007225e5Sgerardnico $statExport[self::FIXME] = $fixmeCount == null ? 0 : $fixmeCount; 238007225e5Sgerardnico if ($fixmeCount != 0) { 239007225e5Sgerardnico $ruleResults[self::RULE_FIXME] = self::FAILED; 240007225e5Sgerardnico $qualityScores['no_' . self::FIXME] = 0; 241007225e5Sgerardnico } else { 242007225e5Sgerardnico $ruleResults[self::RULE_FIXME] = self::PASSED; 2437c33ecc6Sgerardnico $qualityScores['no_' . self::FIXME] = $this->getConf(self::CONF_QUALITY_SCORE_NO_FIXME, 1); 244007225e5Sgerardnico } 245ebdc69ceSgerardnico } 246007225e5Sgerardnico 247007225e5Sgerardnico /** 248007225e5Sgerardnico * A title should be present 249007225e5Sgerardnico */ 25008ca4f85Sgerardnico $titleScore = $this->getConf(self::CONF_QUALITY_SCORE_TITLE_PRESENT, 10); 251*c3437056SNickeau if (empty($this->metadata[PageTitle::TITLE])) { 252007225e5Sgerardnico $ruleResults[self::RULE_TITLE_PRESENT] = self::FAILED; 253*c3437056SNickeau $ruleInfo[self::RULE_TITLE_PRESENT] = "Add a title for {$titleScore} points"; 254*c3437056SNickeau $this->metadata[PageTitle::TITLE] = $dokuWikiMetadata[PageTitle::TITLE]; 255007225e5Sgerardnico $qualityScores[self::RULE_TITLE_PRESENT] = 0; 256007225e5Sgerardnico } else { 2577c33ecc6Sgerardnico $qualityScores[self::RULE_TITLE_PRESENT] = $titleScore; 258007225e5Sgerardnico $ruleResults[self::RULE_TITLE_PRESENT] = self::PASSED; 259007225e5Sgerardnico } 260007225e5Sgerardnico 261007225e5Sgerardnico /** 262007225e5Sgerardnico * A description should be present 263007225e5Sgerardnico */ 26408ca4f85Sgerardnico $descScore = $this->getConf(self::CONF_QUALITY_SCORE_DESCRIPTION_PRESENT, 8); 26537748cd8SNickeau if (empty($this->metadata[self::DESCRIPTION])) { 266007225e5Sgerardnico $ruleResults[self::RULE_DESCRIPTION_PRESENT] = self::FAILED; 267*c3437056SNickeau $ruleInfo[self::RULE_DESCRIPTION_PRESENT] = "Add a description for {$descScore} points"; 26837748cd8SNickeau $this->metadata[self::DESCRIPTION] = $dokuWikiMetadata[self::DESCRIPTION]["abstract"]; 269007225e5Sgerardnico $qualityScores[self::RULE_DESCRIPTION_PRESENT] = 0; 270007225e5Sgerardnico } else { 2717c33ecc6Sgerardnico $qualityScores[self::RULE_DESCRIPTION_PRESENT] = $descScore; 272007225e5Sgerardnico $ruleResults[self::RULE_DESCRIPTION_PRESENT] = self::PASSED; 273007225e5Sgerardnico } 274007225e5Sgerardnico 275007225e5Sgerardnico /** 276007225e5Sgerardnico * A canonical should be present 277007225e5Sgerardnico */ 27808ca4f85Sgerardnico $canonicalScore = $this->getConf(self::CONF_QUALITY_SCORE_CANONICAL_PRESENT, 5); 279*c3437056SNickeau if (empty($this->metadata[Canonical::PROPERTY_NAME])) { 280f3748b38Sgerardnico global $conf; 281f3748b38Sgerardnico $root = $conf['start']; 282f3748b38Sgerardnico if ($ID != $root) { 283007225e5Sgerardnico $qualityScores[self::RULE_CANONICAL_PRESENT] = 0; 284007225e5Sgerardnico $ruleResults[self::RULE_CANONICAL_PRESENT] = self::FAILED; 285*c3437056SNickeau // no link to the documentation because we don't want any html in the json 286*c3437056SNickeau $ruleInfo[self::RULE_CANONICAL_PRESENT] = "Add a canonical for {$canonicalScore} points"; 287f3748b38Sgerardnico } 288007225e5Sgerardnico } else { 2897c33ecc6Sgerardnico $qualityScores[self::RULE_CANONICAL_PRESENT] = $canonicalScore; 290007225e5Sgerardnico $ruleResults[self::RULE_CANONICAL_PRESENT] = self::PASSED; 291007225e5Sgerardnico } 292007225e5Sgerardnico 293007225e5Sgerardnico /** 294007225e5Sgerardnico * Outline / Header structure 295007225e5Sgerardnico */ 296007225e5Sgerardnico $treeError = 0; 297007225e5Sgerardnico $headersCount = 0; 298*c3437056SNickeau if (array_key_exists(AnalyticsDocument::HEADER_POSITION, $this->stats)) { 299*c3437056SNickeau $headersCount = count($this->stats[AnalyticsDocument::HEADER_POSITION]); 300*c3437056SNickeau unset($statExport[AnalyticsDocument::HEADER_POSITION]); 301007225e5Sgerardnico for ($i = 1; $i < $headersCount; $i++) { 302ebdc69ceSgerardnico $currentHeaderLevel = $this->stats[self::HEADER_STRUCT][$i]; 303ebdc69ceSgerardnico $previousHeaderLevel = $this->stats[self::HEADER_STRUCT][$i - 1]; 304007225e5Sgerardnico if ($currentHeaderLevel - $previousHeaderLevel > 1) { 305007225e5Sgerardnico $treeError += 1; 306007225e5Sgerardnico $ruleInfo[self::RULE_OUTLINE_STRUCTURE] = "The " . $i . " header (h" . $currentHeaderLevel . ") has a level bigger than its precedent (" . $previousHeaderLevel . ")"; 307007225e5Sgerardnico } 308007225e5Sgerardnico } 309ebdc69ceSgerardnico unset($statExport[self::HEADER_STRUCT]); 310007225e5Sgerardnico } 311eee76a3dSgerardnico $outlinePoints = $this->getConf(self::CONF_QUALITY_SCORE_CORRECT_HEADER_STRUCTURE, 3); 312007225e5Sgerardnico if ($treeError > 0 || $headersCount == 0) { 313007225e5Sgerardnico $qualityScores['correct_outline'] = 0; 314007225e5Sgerardnico $ruleResults[self::RULE_OUTLINE_STRUCTURE] = self::FAILED; 315007225e5Sgerardnico if ($headersCount == 0) { 316eee76a3dSgerardnico $ruleInfo[self::RULE_OUTLINE_STRUCTURE] = "Add headings to create a document outline for {$outlinePoints} points"; 317007225e5Sgerardnico } 318007225e5Sgerardnico } else { 319eee76a3dSgerardnico $qualityScores['correct_outline'] = $outlinePoints; 320007225e5Sgerardnico $ruleResults[self::RULE_OUTLINE_STRUCTURE] = self::PASSED; 321007225e5Sgerardnico } 322007225e5Sgerardnico 323007225e5Sgerardnico 324007225e5Sgerardnico /** 325007225e5Sgerardnico * Document length 326007225e5Sgerardnico */ 327007225e5Sgerardnico $minimalWordCount = 50; 328007225e5Sgerardnico $maximalWordCount = 1500; 329007225e5Sgerardnico $correctContentLength = true; 33008ca4f85Sgerardnico $correctLengthScore = $this->getConf(self::CONF_QUALITY_SCORE_CORRECT_CONTENT, 10); 331*c3437056SNickeau $missingWords = $minimalWordCount - $statExport[AnalyticsDocument::WORD_COUNT]; 33208ca4f85Sgerardnico if ($missingWords > 0) { 333007225e5Sgerardnico $ruleResults[self::RULE_WORDS_MINIMAL] = self::FAILED; 334007225e5Sgerardnico $correctContentLength = false; 33508ca4f85Sgerardnico $ruleInfo[self::RULE_WORDS_MINIMAL] = "Add {$missingWords} words to get {$correctLengthScore} points"; 336007225e5Sgerardnico } else { 337007225e5Sgerardnico $ruleResults[self::RULE_WORDS_MINIMAL] = self::PASSED; 338007225e5Sgerardnico } 339*c3437056SNickeau $tooMuchWords = $statExport[AnalyticsDocument::WORD_COUNT] - $maximalWordCount; 34008ca4f85Sgerardnico if ($tooMuchWords > 0) { 341007225e5Sgerardnico $ruleResults[self::RULE_WORDS_MAXIMAL] = self::FAILED; 34208ca4f85Sgerardnico $ruleInfo[self::RULE_WORDS_MAXIMAL] = "Delete {$tooMuchWords} words to get {$correctLengthScore} points"; 343007225e5Sgerardnico $correctContentLength = false; 344007225e5Sgerardnico } else { 345007225e5Sgerardnico $ruleResults[self::RULE_WORDS_MAXIMAL] = self::PASSED; 346007225e5Sgerardnico } 347007225e5Sgerardnico if ($correctContentLength) { 34808ca4f85Sgerardnico $qualityScores['correct_content_length'] = $correctLengthScore; 349007225e5Sgerardnico } else { 350007225e5Sgerardnico $qualityScores['correct_content_length'] = 0; 351007225e5Sgerardnico } 352007225e5Sgerardnico 353007225e5Sgerardnico 354007225e5Sgerardnico /** 355007225e5Sgerardnico * Average Number of words by header section to text ratio 356007225e5Sgerardnico */ 357*c3437056SNickeau $headers = $this->stats[AnalyticsDocument::HEADING_COUNT]; 358007225e5Sgerardnico if ($headers != null) { 359007225e5Sgerardnico $headerCount = array_sum($headers); 360007225e5Sgerardnico $headerCount--; // h1 is supposed to have no words 361007225e5Sgerardnico if ($headerCount > 0) { 362007225e5Sgerardnico 363*c3437056SNickeau $avgWordsCountBySection = round($this->stats[AnalyticsDocument::WORD_COUNT] / $headerCount); 364007225e5Sgerardnico $statExport['word_section_count']['avg'] = $avgWordsCountBySection; 365007225e5Sgerardnico 366007225e5Sgerardnico /** 367007225e5Sgerardnico * Min words by header section 368007225e5Sgerardnico */ 369007225e5Sgerardnico $wordsByHeaderMin = 20; 370007225e5Sgerardnico /** 371007225e5Sgerardnico * Max words by header section 372007225e5Sgerardnico */ 373007225e5Sgerardnico $wordsByHeaderMax = 300; 374007225e5Sgerardnico $correctAverageWordsBySection = true; 375007225e5Sgerardnico if ($avgWordsCountBySection < $wordsByHeaderMin) { 376007225e5Sgerardnico $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MIN] = self::FAILED; 377007225e5Sgerardnico $correctAverageWordsBySection = false; 37808ca4f85Sgerardnico $ruleInfo[self::RULE_AVERAGE_WORDS_BY_SECTION_MIN] = "The number of words by section is less than {$wordsByHeaderMin}"; 379007225e5Sgerardnico } else { 380007225e5Sgerardnico $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MIN] = self::PASSED; 381007225e5Sgerardnico } 382007225e5Sgerardnico if ($avgWordsCountBySection > $wordsByHeaderMax) { 383007225e5Sgerardnico $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MAX] = self::FAILED; 384007225e5Sgerardnico $correctAverageWordsBySection = false; 385007225e5Sgerardnico $ruleInfo[self::RULE_AVERAGE_WORDS_BY_SECTION_MAX] = "The number of words by section is more than {$wordsByHeaderMax}"; 386007225e5Sgerardnico } else { 387007225e5Sgerardnico $ruleResults[self::RULE_AVERAGE_WORDS_BY_SECTION_MAX] = self::PASSED; 388007225e5Sgerardnico } 389007225e5Sgerardnico if ($correctAverageWordsBySection) { 390007225e5Sgerardnico $qualityScores['correct_word_avg_by_section'] = $this->getConf(self::CONF_QUALITY_SCORE_CORRECT_WORD_SECTION_AVERAGE, 10); 391007225e5Sgerardnico } else { 392007225e5Sgerardnico $qualityScores['correct_word_avg_by_section'] = 0; 393007225e5Sgerardnico } 394007225e5Sgerardnico 395007225e5Sgerardnico } 396007225e5Sgerardnico } 397007225e5Sgerardnico 398007225e5Sgerardnico /** 399007225e5Sgerardnico * Internal Backlinks rule 400007225e5Sgerardnico * 401*c3437056SNickeau * We used the database table to get the backlinks 402*c3437056SNickeau * because the replication is based on it 403*c3437056SNickeau * If the dokuwiki index is not up to date, we may got 404*c3437056SNickeau * inconsistency 405007225e5Sgerardnico */ 406*c3437056SNickeau $countBacklinks = BacklinkCount::createFromResource($this->page) 407*c3437056SNickeau ->setReadStore(MetadataDbStore::class) 408*c3437056SNickeau ->getValueOrDefault(); 409*c3437056SNickeau $statExport[BacklinkCount::getPersistentName()] = $countBacklinks; 410d262537cSgerardnico $backlinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_BACKLINK_FACTOR, 1); 411007225e5Sgerardnico if ($countBacklinks == 0) { 412*c3437056SNickeau 413*c3437056SNickeau $qualityScores[BacklinkCount::getPersistentName()] = 0; 414007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_BACKLINKS_MIN] = self::FAILED; 415d262537cSgerardnico $ruleInfo[self::RULE_INTERNAL_BACKLINKS_MIN] = "Add backlinks for {$backlinkScore} point each"; 416*c3437056SNickeau 417007225e5Sgerardnico } else { 418d262537cSgerardnico 419*c3437056SNickeau $qualityScores[BacklinkCount::getPersistentName()] = $countBacklinks * $backlinkScore; 420007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_BACKLINKS_MIN] = self::PASSED; 421007225e5Sgerardnico } 422007225e5Sgerardnico 423007225e5Sgerardnico /** 424007225e5Sgerardnico * Internal links 425007225e5Sgerardnico */ 426*c3437056SNickeau $internalLinksCount = $this->stats[AnalyticsDocument::INTERNAL_LINK_COUNT]; 427d262537cSgerardnico $internalLinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_LINK_FACTOR, 1); 428007225e5Sgerardnico if ($internalLinksCount == 0) { 429*c3437056SNickeau $qualityScores[AnalyticsDocument::INTERNAL_LINK_COUNT] = 0; 430007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_LINKS_MIN] = self::FAILED; 431d262537cSgerardnico $ruleInfo[self::RULE_INTERNAL_LINKS_MIN] = "Add internal links for {$internalLinkScore} point each"; 432007225e5Sgerardnico } else { 433007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_LINKS_MIN] = self::PASSED; 434*c3437056SNickeau $qualityScores[AnalyticsDocument::INTERNAL_LINK_COUNT] = $countBacklinks * $internalLinkScore; 435007225e5Sgerardnico } 436007225e5Sgerardnico 437007225e5Sgerardnico /** 438007225e5Sgerardnico * Broken Links 439007225e5Sgerardnico */ 440d262537cSgerardnico $brokenLinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_LINK_BROKEN_FACTOR, 2); 441ebdc69ceSgerardnico $brokenLinksCount = 0; 442*c3437056SNickeau if (array_key_exists(AnalyticsDocument::INTERNAL_LINK_BROKEN_COUNT, $this->stats)) { 443*c3437056SNickeau $brokenLinksCount = $this->stats[AnalyticsDocument::INTERNAL_LINK_BROKEN_COUNT]; 444ebdc69ceSgerardnico } 445007225e5Sgerardnico if ($brokenLinksCount > 2) { 446*c3437056SNickeau $qualityScores['no_' . AnalyticsDocument::INTERNAL_LINK_BROKEN_COUNT] = 0; 447007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_BROKEN_LINKS_MAX] = self::FAILED; 448d262537cSgerardnico $ruleInfo[self::RULE_INTERNAL_BROKEN_LINKS_MAX] = "Delete the {$brokenLinksCount} broken links and add {$brokenLinkScore} points"; 449007225e5Sgerardnico } else { 450*c3437056SNickeau $qualityScores['no_' . AnalyticsDocument::INTERNAL_LINK_BROKEN_COUNT] = $brokenLinkScore; 451007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_BROKEN_LINKS_MAX] = self::PASSED; 452007225e5Sgerardnico } 453007225e5Sgerardnico 454007225e5Sgerardnico /** 455e8b2ff59SNickeau * Media 456e8b2ff59SNickeau */ 457e8b2ff59SNickeau $mediasStats = [ 458*c3437056SNickeau "total_count" => self::getAndUnset($statExport, AnalyticsDocument::MEDIA_COUNT, 0), 459*c3437056SNickeau "internal_count" => self::getAndUnset($statExport, AnalyticsDocument::INTERNAL_MEDIA_COUNT, 0), 460*c3437056SNickeau "internal_broken_count" => self::getAndUnset($statExport, AnalyticsDocument::INTERNAL_BROKEN_MEDIA_COUNT, 0), 461*c3437056SNickeau "external_count" => self::getAndUnset($statExport, AnalyticsDocument::EXTERNAL_MEDIA_COUNT, 0) 462e8b2ff59SNickeau ]; 463e8b2ff59SNickeau $statExport['media'] = $mediasStats; 464e8b2ff59SNickeau 465e8b2ff59SNickeau /** 466007225e5Sgerardnico * Changes, the more changes the better 467007225e5Sgerardnico */ 468*c3437056SNickeau $qualityScores[AnalyticsDocument::EDITS_COUNT] = $statExport[AnalyticsDocument::EDITS_COUNT] * $this->getConf(self::CONF_QUALITY_SCORE_CHANGES_FACTOR, 0.25); 469007225e5Sgerardnico 470007225e5Sgerardnico 471007225e5Sgerardnico /** 472007225e5Sgerardnico * Quality Score 473007225e5Sgerardnico */ 474007225e5Sgerardnico ksort($qualityScores); 475007225e5Sgerardnico $qualityScoring = array(); 47608ca4f85Sgerardnico $qualityScoring[self::SCORE] = array_sum($qualityScores); 477007225e5Sgerardnico $qualityScoring["scores"] = $qualityScores; 478007225e5Sgerardnico 479007225e5Sgerardnico 480007225e5Sgerardnico /** 481007225e5Sgerardnico * The rule that if broken will set the quality level to low 482007225e5Sgerardnico */ 483007225e5Sgerardnico $brokenRules = array(); 484007225e5Sgerardnico foreach ($ruleResults as $ruleName => $ruleResult) { 485007225e5Sgerardnico if ($ruleResult == self::FAILED) { 486007225e5Sgerardnico $brokenRules[] = $ruleName; 487007225e5Sgerardnico } 488007225e5Sgerardnico } 489007225e5Sgerardnico $ruleErrorCount = sizeof($brokenRules); 490007225e5Sgerardnico if ($ruleErrorCount > 0) { 491007225e5Sgerardnico $qualityResult = $ruleErrorCount . " quality rules errors"; 492007225e5Sgerardnico } else { 493007225e5Sgerardnico $qualityResult = "All quality rules passed"; 494007225e5Sgerardnico } 495007225e5Sgerardnico 496007225e5Sgerardnico /** 497fa5961eaSgerardnico * Low level Computation 498007225e5Sgerardnico */ 499007225e5Sgerardnico $mandatoryRules = preg_split("/,/", $this->getConf(self::CONF_MANDATORY_QUALITY_RULES)); 500007225e5Sgerardnico $mandatoryRulesBroken = []; 501007225e5Sgerardnico foreach ($mandatoryRules as $lowLevelRule) { 502007225e5Sgerardnico if (in_array($lowLevelRule, $brokenRules)) { 503007225e5Sgerardnico $mandatoryRulesBroken[] = $lowLevelRule; 504007225e5Sgerardnico } 505007225e5Sgerardnico } 506fa5961eaSgerardnico /** 507*c3437056SNickeau * Low Level 508fa5961eaSgerardnico */ 509007225e5Sgerardnico $lowLevel = false; 51085e82846SNickeau $brokenRulesCount = sizeof($mandatoryRulesBroken); 51185e82846SNickeau if ($brokenRulesCount > 0) { 512007225e5Sgerardnico $lowLevel = true; 51385e82846SNickeau $quality["message"] = "$brokenRulesCount mandatory rules broken."; 51485e82846SNickeau } else { 51585e82846SNickeau $quality["message"] = "No mandatory rules broken"; 516007225e5Sgerardnico } 517*c3437056SNickeau if ($this->page->isSlot()) { 518*c3437056SNickeau $lowLevel = false; 5199b9e6d1fSgerardnico } 520*c3437056SNickeau $this->page->setLowQualityIndicatorCalculation($lowLevel); 521007225e5Sgerardnico 522007225e5Sgerardnico /** 523007225e5Sgerardnico * Building the quality object in order 524007225e5Sgerardnico */ 525*c3437056SNickeau $quality[AnalyticsDocument::LOW] = $lowLevel; 526007225e5Sgerardnico if (sizeof($mandatoryRulesBroken) > 0) { 527007225e5Sgerardnico ksort($mandatoryRulesBroken); 528*c3437056SNickeau $quality[AnalyticsDocument::FAILED_MANDATORY_RULES] = $mandatoryRulesBroken; 529007225e5Sgerardnico } 53008ca4f85Sgerardnico $quality[self::SCORING] = $qualityScoring; 531*c3437056SNickeau $quality[AnalyticsDocument::RULES][self::RESULT] = $qualityResult; 532007225e5Sgerardnico if (!empty($ruleInfo)) { 533*c3437056SNickeau $quality[AnalyticsDocument::RULES]["info"] = $ruleInfo; 534007225e5Sgerardnico } 535007225e5Sgerardnico 536007225e5Sgerardnico ksort($ruleResults); 537*c3437056SNickeau $quality[AnalyticsDocument::RULES][AnalyticsDocument::DETAILS] = $ruleResults; 538007225e5Sgerardnico 539007225e5Sgerardnico /** 540007225e5Sgerardnico * Metadata 541007225e5Sgerardnico */ 542*c3437056SNickeau $page = Page::createPageFromGlobalDokuwikiId(); 5431fa8c418SNickeau $meta = $page->getMetadataForRendering(); 54437748cd8SNickeau foreach ($meta as $key => $value) { 54537748cd8SNickeau /** 54637748cd8SNickeau * The metadata may have been set 54737748cd8SNickeau * by frontmatter 54837748cd8SNickeau */ 54937748cd8SNickeau if (!isset($this->metadata[$key])) { 55037748cd8SNickeau $this->metadata[$key] = $value; 551c42a1196Sgerardnico } 55237748cd8SNickeau } 553007225e5Sgerardnico 554007225e5Sgerardnico 555007225e5Sgerardnico /** 556007225e5Sgerardnico * Building the Top JSON in order 557007225e5Sgerardnico */ 558007225e5Sgerardnico global $ID; 5592c067407Sgerardnico $finalStats = array(); 560c42a1196Sgerardnico $finalStats["date"] = date('Y-m-d H:i:s', time()); 56137748cd8SNickeau ksort($this->metadata); 562*c3437056SNickeau $finalStats[AnalyticsDocument::METADATA] = $this->metadata; 563007225e5Sgerardnico ksort($statExport); 564*c3437056SNickeau $finalStats[AnalyticsDocument::STATISTICS] = $statExport; 565*c3437056SNickeau $finalStats[AnalyticsDocument::QUALITY] = $quality; // Quality after the sort to get them at the end 566007225e5Sgerardnico 567007225e5Sgerardnico 568007225e5Sgerardnico /** 569007225e5Sgerardnico * The result can be seen with 570007225e5Sgerardnico * doku.php?id=somepage&do=export_combo_analysis 5717c33ecc6Sgerardnico * 5727c33ecc6Sgerardnico * Set the header temporarily for the export.php file 57385e82846SNickeau * 57485e82846SNickeau * The mode in the export is 575007225e5Sgerardnico */ 57685e82846SNickeau $mode = "combo_" . $this->getPluginComponent(); 5777c33ecc6Sgerardnico p_set_metadata( 5787c33ecc6Sgerardnico $ID, 57985e82846SNickeau array("format" => array($mode => array("Content-Type" => 'application/json'))), 5807c33ecc6Sgerardnico false, 581*c3437056SNickeau false // Persistence is needed because there is a cache 5827c33ecc6Sgerardnico ); 5832c067407Sgerardnico $json_encoded = json_encode($finalStats, JSON_PRETTY_PRINT); 584007225e5Sgerardnico 585007225e5Sgerardnico $this->doc .= $json_encoded; 586007225e5Sgerardnico 587007225e5Sgerardnico } 588007225e5Sgerardnico 589007225e5Sgerardnico /** 590007225e5Sgerardnico */ 591007225e5Sgerardnico public function getFormat() 592007225e5Sgerardnico { 593531e725cSNickeau return self::RENDERER_FORMAT; 594007225e5Sgerardnico } 595007225e5Sgerardnico 596007225e5Sgerardnico public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') 597007225e5Sgerardnico { 598007225e5Sgerardnico 5999b9e6d1fSgerardnico $link = new LinkUtility($id); 6009b9e6d1fSgerardnico $link->setType(LinkUtility::TYPE_INTERNAL); 6019b9e6d1fSgerardnico $link->processLinkStats($this->stats); 602007225e5Sgerardnico 603007225e5Sgerardnico } 604007225e5Sgerardnico 605007225e5Sgerardnico public function externallink($url, $name = null) 606007225e5Sgerardnico { 607ef295d81Sgerardnico $link = new LinkUtility($url); 608ef295d81Sgerardnico $link->setType(LinkUtility::TYPE_EXTERNAL); 609ef295d81Sgerardnico if ($name != null) { 610ef295d81Sgerardnico $link->setName($name); 611ef295d81Sgerardnico } 612ef295d81Sgerardnico $link->processLinkStats($this->stats); 613007225e5Sgerardnico } 614007225e5Sgerardnico 615007225e5Sgerardnico public function header($text, $level, $pos) 616007225e5Sgerardnico { 617*c3437056SNickeau if (!array_key_exists(AnalyticsDocument::HEADING_COUNT, $this->stats)) { 618*c3437056SNickeau $this->stats[AnalyticsDocument::HEADING_COUNT] = []; 619ebdc69ceSgerardnico } 620ebdc69ceSgerardnico $heading = 'h' . $level; 621ebdc69ceSgerardnico if (!array_key_exists( 622ebdc69ceSgerardnico $heading, 623*c3437056SNickeau $this->stats[AnalyticsDocument::HEADING_COUNT])) { 624*c3437056SNickeau $this->stats[AnalyticsDocument::HEADING_COUNT][$heading] = 0; 625ebdc69ceSgerardnico } 626*c3437056SNickeau $this->stats[AnalyticsDocument::HEADING_COUNT][$heading]++; 627ebdc69ceSgerardnico 628007225e5Sgerardnico $this->headerId++; 629*c3437056SNickeau $this->stats[AnalyticsDocument::HEADER_POSITION][$this->headerId] = $heading; 630ebdc69ceSgerardnico 631ebdc69ceSgerardnico /** 632ebdc69ceSgerardnico * Store the level of each heading 633ebdc69ceSgerardnico * They should only go from low to highest value 634ebdc69ceSgerardnico * for a good outline 635ebdc69ceSgerardnico */ 636*c3437056SNickeau if (!array_key_exists(AnalyticsDocument::HEADING_COUNT, $this->stats)) { 637ebdc69ceSgerardnico $this->stats[self::HEADER_STRUCT] = []; 638ebdc69ceSgerardnico } 639ebdc69ceSgerardnico $this->stats[self::HEADER_STRUCT][] = $level; 640007225e5Sgerardnico 641007225e5Sgerardnico } 642007225e5Sgerardnico 643007225e5Sgerardnico public function smiley($smiley) 644007225e5Sgerardnico { 645007225e5Sgerardnico if ($smiley == 'FIXME') $this->stats[self::FIXME]++; 646007225e5Sgerardnico } 647007225e5Sgerardnico 648007225e5Sgerardnico public function linebreak() 649007225e5Sgerardnico { 650007225e5Sgerardnico if (!$this->tableopen) { 651007225e5Sgerardnico $this->stats['linebreak']++; 652007225e5Sgerardnico } 653007225e5Sgerardnico } 654007225e5Sgerardnico 655007225e5Sgerardnico public function table_open($maxcols = null, $numrows = null, $pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 656007225e5Sgerardnico { 657007225e5Sgerardnico $this->tableopen = true; 658007225e5Sgerardnico } 659007225e5Sgerardnico 660007225e5Sgerardnico public function table_close($pos = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 661007225e5Sgerardnico { 662007225e5Sgerardnico $this->tableopen = false; 663007225e5Sgerardnico } 664007225e5Sgerardnico 665007225e5Sgerardnico public function hr() 666007225e5Sgerardnico { 667007225e5Sgerardnico $this->stats['hr']++; 668007225e5Sgerardnico } 669007225e5Sgerardnico 670007225e5Sgerardnico public function quote_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 671007225e5Sgerardnico { 672007225e5Sgerardnico $this->stats['quote_count']++; 673007225e5Sgerardnico $this->quotelevel++; 674007225e5Sgerardnico $this->stats['quote_nest'] = max($this->quotelevel, $this->stats['quote_nest']); 675007225e5Sgerardnico } 676007225e5Sgerardnico 677007225e5Sgerardnico public function quote_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 678007225e5Sgerardnico { 679007225e5Sgerardnico $this->quotelevel--; 680007225e5Sgerardnico } 681007225e5Sgerardnico 682007225e5Sgerardnico public function strong_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 683007225e5Sgerardnico { 684007225e5Sgerardnico $this->formattingBracket++; 685007225e5Sgerardnico } 686007225e5Sgerardnico 687007225e5Sgerardnico public function strong_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 688007225e5Sgerardnico { 689007225e5Sgerardnico $this->formattingBracket--; 690007225e5Sgerardnico } 691007225e5Sgerardnico 692007225e5Sgerardnico public function emphasis_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 693007225e5Sgerardnico { 694007225e5Sgerardnico $this->formattingBracket++; 695007225e5Sgerardnico } 696007225e5Sgerardnico 697007225e5Sgerardnico public function emphasis_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 698007225e5Sgerardnico { 699007225e5Sgerardnico $this->formattingBracket--; 700007225e5Sgerardnico } 701007225e5Sgerardnico 702007225e5Sgerardnico public function underline_open() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 703007225e5Sgerardnico { 704007225e5Sgerardnico $this->formattingBracket++; 705007225e5Sgerardnico } 706007225e5Sgerardnico 707007225e5Sgerardnico public function underline_close() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 708007225e5Sgerardnico { 709007225e5Sgerardnico $this->formattingBracket--; 710007225e5Sgerardnico } 711007225e5Sgerardnico 712007225e5Sgerardnico public function cdata($text) 713007225e5Sgerardnico { 714007225e5Sgerardnico 715007225e5Sgerardnico /** 716007225e5Sgerardnico * It seems that you receive cdata 717007225e5Sgerardnico * when emphasis_open / underline_open / strong_open 718007225e5Sgerardnico * Stats are not for them 719007225e5Sgerardnico */ 720007225e5Sgerardnico if (!$this->formattingBracket) return; 721007225e5Sgerardnico 722007225e5Sgerardnico $this->plainTextId++; 723007225e5Sgerardnico 724007225e5Sgerardnico /** 725007225e5Sgerardnico * Length 726007225e5Sgerardnico */ 727007225e5Sgerardnico $len = strlen($text); 728007225e5Sgerardnico $this->stats[self::PLAINTEXT][$this->plainTextId]['len'] = $len; 729007225e5Sgerardnico 730007225e5Sgerardnico 731007225e5Sgerardnico /** 732007225e5Sgerardnico * Multi-formatting 733007225e5Sgerardnico */ 734007225e5Sgerardnico if ($this->formattingBracket > 1) { 735007225e5Sgerardnico $numberOfFormats = 1 * ($this->formattingBracket - 1); 736007225e5Sgerardnico $this->stats[self::PLAINTEXT][$this->plainTextId]['multiformat'] += $numberOfFormats; 737007225e5Sgerardnico } 738007225e5Sgerardnico 739007225e5Sgerardnico /** 740007225e5Sgerardnico * Total 741007225e5Sgerardnico */ 742007225e5Sgerardnico $this->stats[self::PLAINTEXT][0] += $len; 743007225e5Sgerardnico } 744007225e5Sgerardnico 745007225e5Sgerardnico public function internalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null) 746007225e5Sgerardnico { 747*c3437056SNickeau $this->stats[AnalyticsDocument::INTERNAL_MEDIA_COUNT]++; 748007225e5Sgerardnico } 749007225e5Sgerardnico 750007225e5Sgerardnico public function externalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null) 751007225e5Sgerardnico { 752*c3437056SNickeau $this->stats[AnalyticsDocument::EXTERNAL_MEDIA_COUNT]++; 753007225e5Sgerardnico } 754007225e5Sgerardnico 755007225e5Sgerardnico public function reset() 756007225e5Sgerardnico { 757007225e5Sgerardnico $this->stats = array(); 75837748cd8SNickeau $this->metadata = array(); 759007225e5Sgerardnico $this->headerId = 0; 760007225e5Sgerardnico } 761007225e5Sgerardnico 762*c3437056SNickeau public function setAnalyticsMetaForReporting($key, $value) 763007225e5Sgerardnico { 76437748cd8SNickeau $this->metadata[$key] = $value; 765007225e5Sgerardnico } 766007225e5Sgerardnico 767007225e5Sgerardnico 768007225e5Sgerardnico} 769007225e5Sgerardnico 770