1007225e5Sgerardnico<?php 2007225e5Sgerardnico 3007225e5Sgerardnico 4007225e5Sgerardnicouse ComboStrap\Analytics; 5007225e5Sgerardnicouse ComboStrap\LinkUtility; 671f916b9Sgerardnicouse ComboStrap\Page; 737748cd8SNickeauuse ComboStrap\StringUtility; 8007225e5Sgerardnicouse dokuwiki\ChangeLog\PageChangeLog; 9007225e5Sgerardnico 1037748cd8SNickeau 1137748cd8SNickeaurequire_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 9937748cd8SNickeau 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); 195*1fa8c418SNickeau $statExport[Analytics::CHAR_COUNT] = strlen($text); 19637748cd8SNickeau $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); 24437748cd8SNickeau 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"; 24737748cd8SNickeau $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); 25837748cd8SNickeau 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"; 26137748cd8SNickeau $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); 27237748cd8SNickeau 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); 32337748cd8SNickeau $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 } 33137748cd8SNickeau $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 */ 349*1fa8c418SNickeau $headers = $this->stats[Analytics::HEADING_COUNT]; 350007225e5Sgerardnico if ($headers != null) { 351007225e5Sgerardnico $headerCount = array_sum($headers); 352007225e5Sgerardnico $headerCount--; // h1 is supposed to have no words 353007225e5Sgerardnico if ($headerCount > 0) { 354007225e5Sgerardnico 35537748cd8SNickeau $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); 399*1fa8c418SNickeau $statExport[Analytics::INTERNAL_BACKLINK_COUNT] = $countBacklinks; 400d262537cSgerardnico $backlinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_BACKLINK_FACTOR, 1); 401007225e5Sgerardnico if ($countBacklinks == 0) { 402*1fa8c418SNickeau $qualityScores[Analytics::INTERNAL_BACKLINK_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 407*1fa8c418SNickeau $qualityScores[Analytics::INTERNAL_BACKLINK_COUNT] = $countBacklinks * $backlinkScore; 408007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_BACKLINKS_MIN] = self::PASSED; 409007225e5Sgerardnico } 410007225e5Sgerardnico 411007225e5Sgerardnico /** 412007225e5Sgerardnico * Internal links 413007225e5Sgerardnico */ 414*1fa8c418SNickeau $internalLinksCount = $this->stats[Analytics::INTERNAL_LINK_COUNT]; 415d262537cSgerardnico $internalLinkScore = $this->getConf(self::CONF_QUALITY_SCORE_INTERNAL_LINK_FACTOR, 1); 416007225e5Sgerardnico if ($internalLinksCount == 0) { 417*1fa8c418SNickeau $qualityScores[Analytics::INTERNAL_LINK_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; 422*1fa8c418SNickeau $qualityScores[Analytics::INTERNAL_LINK_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; 430*1fa8c418SNickeau if (array_key_exists(Analytics::INTERNAL_LINK_BROKEN_COUNT, $this->stats)) { 431*1fa8c418SNickeau $brokenLinksCount = $this->stats[Analytics::INTERNAL_LINK_BROKEN_COUNT]; 432ebdc69ceSgerardnico } 433007225e5Sgerardnico if ($brokenLinksCount > 2) { 434*1fa8c418SNickeau $qualityScores['no_' . Analytics::INTERNAL_LINK_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 { 438*1fa8c418SNickeau $qualityScores['no_' . Analytics::INTERNAL_LINK_BROKEN_COUNT] = $brokenLinkScore; 439007225e5Sgerardnico $ruleResults[self::RULE_INTERNAL_BROKEN_LINKS_MAX] = self::PASSED; 440007225e5Sgerardnico } 441007225e5Sgerardnico 442007225e5Sgerardnico /** 443e8b2ff59SNickeau * Media 444e8b2ff59SNickeau */ 445e8b2ff59SNickeau $mediasStats = [ 446*1fa8c418SNickeau "total_count" => self::getAndUnset($statExport, Analytics::MEDIA_COUNT, 0), 447*1fa8c418SNickeau "internal_count" => self::getAndUnset($statExport, Analytics::INTERNAL_MEDIA_COUNT, 0), 448*1fa8c418SNickeau "internal_broken_count" => self::getAndUnset($statExport, Analytics::INTERNAL_BROKEN_MEDIA_COUNT, 0), 449*1fa8c418SNickeau "external_count" => self::getAndUnset($statExport, Analytics::EXTERNAL_MEDIA_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 */ 49737748cd8SNickeau 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 { 50737748cd8SNickeau $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 */ 53537748cd8SNickeau $page = Page::createPageFromCurrentId(); 536*1fa8c418SNickeau $meta = $page->getMetadataForRendering(); 53737748cd8SNickeau foreach ($meta as $key => $value) { 53837748cd8SNickeau /** 53937748cd8SNickeau * The metadata may have been set 54037748cd8SNickeau * by frontmatter 54137748cd8SNickeau */ 54237748cd8SNickeau if (!isset($this->metadata[$key])) { 54337748cd8SNickeau $this->metadata[$key] = $value; 544c42a1196Sgerardnico } 54537748cd8SNickeau } 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()); 55437748cd8SNickeau ksort($this->metadata); 55537748cd8SNickeau $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 578*1fa8c418SNickeau $this->page->persistAnalytics($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 { 611*1fa8c418SNickeau if (!array_key_exists(Analytics::HEADING_COUNT, $this->stats)) { 612*1fa8c418SNickeau $this->stats[Analytics::HEADING_COUNT] = []; 613ebdc69ceSgerardnico } 614ebdc69ceSgerardnico $heading = 'h' . $level; 615ebdc69ceSgerardnico if (!array_key_exists( 616ebdc69ceSgerardnico $heading, 617*1fa8c418SNickeau $this->stats[Analytics::HEADING_COUNT])) { 618*1fa8c418SNickeau $this->stats[Analytics::HEADING_COUNT][$heading] = 0; 619ebdc69ceSgerardnico } 620*1fa8c418SNickeau $this->stats[Analytics::HEADING_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 */ 630*1fa8c418SNickeau if (!array_key_exists(Analytics::HEADING_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 { 741*1fa8c418SNickeau $this->stats[Analytics::INTERNAL_MEDIA_COUNT]++; 742007225e5Sgerardnico } 743007225e5Sgerardnico 744007225e5Sgerardnico public function externalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null) 745007225e5Sgerardnico { 746*1fa8c418SNickeau $this->stats[Analytics::EXTERNAL_MEDIA_COUNT]++; 747007225e5Sgerardnico } 748007225e5Sgerardnico 749007225e5Sgerardnico public function reset() 750007225e5Sgerardnico { 751007225e5Sgerardnico $this->stats = array(); 75237748cd8SNickeau $this->metadata = array(); 753007225e5Sgerardnico $this->headerId = 0; 754007225e5Sgerardnico } 755007225e5Sgerardnico 756007225e5Sgerardnico public function setMeta($key, $value) 757007225e5Sgerardnico { 75837748cd8SNickeau $this->metadata[$key] = $value; 759007225e5Sgerardnico } 760007225e5Sgerardnico 761007225e5Sgerardnico 762007225e5Sgerardnico} 763007225e5Sgerardnico 764