1007225e5Sgerardnico<?php 2007225e5Sgerardnico/** 3007225e5Sgerardnico * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4007225e5Sgerardnico * 5007225e5Sgerardnico * This source code is licensed under the GPL license found in the 6007225e5Sgerardnico * COPYING file in the root directory of this source tree. 7007225e5Sgerardnico * 8007225e5Sgerardnico * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9007225e5Sgerardnico * @author ComboStrap <support@combostrap.com> 10007225e5Sgerardnico * 11007225e5Sgerardnico */ 12007225e5Sgerardnicoif (!defined('DOKU_INC')) die(); 13007225e5Sgerardnico 14007225e5Sgerardnicouse ComboStrap\Analytics; 15*37748cd8SNickeauuse ComboStrap\FsWikiUtility; 1671f916b9Sgerardnicouse ComboStrap\Page; 1771f916b9Sgerardnicouse ComboStrap\Sqlite; 18007225e5Sgerardnicouse splitbrain\phpcli\Options; 19007225e5Sgerardnico 20e8b2ff59SNickeau/** 21e8b2ff59SNickeau * All dependency are loaded in plugin utility 22e8b2ff59SNickeau */ 23*37748cd8SNickeaurequire_once(__DIR__ . '/ComboStrap/PluginUtility.php'); 24007225e5Sgerardnico 25007225e5Sgerardnico/** 26007225e5Sgerardnico * The memory of the server 128 is not enough 27007225e5Sgerardnico */ 28007225e5Sgerardnicoini_set('memory_limit', '256M'); 29007225e5Sgerardnico 30007225e5Sgerardnico/** 31007225e5Sgerardnico * Class cli_plugin_combo 32007225e5Sgerardnico * 33007225e5Sgerardnico * This is a cli: 34007225e5Sgerardnico * https://www.dokuwiki.org/devel:cli_plugins#example 35007225e5Sgerardnico * 36007225e5Sgerardnico * Usage: 37007225e5Sgerardnico * 38007225e5Sgerardnico * ``` 39007225e5Sgerardnico * docker exec -ti $(CONTAINER) /bin/bash 40007225e5Sgerardnico * ./bin/plugin.php combo -c 41007225e5Sgerardnico * ``` 42007225e5Sgerardnico * or via the IDE 43007225e5Sgerardnico * 44007225e5Sgerardnico * 45007225e5Sgerardnico * Example: 46007225e5Sgerardnico * https://www.dokuwiki.org/tips:grapher 47007225e5Sgerardnico * 48007225e5Sgerardnico */ 49007225e5Sgerardnicoclass cli_plugin_combo extends DokuWiki_CLI_Plugin 50007225e5Sgerardnico{ 5171f916b9Sgerardnico const ANALYTICS = "analytics"; 5271f916b9Sgerardnico const SYNC = "sync"; 53007225e5Sgerardnico 54007225e5Sgerardnico /** 55007225e5Sgerardnico * register options and arguments 56007225e5Sgerardnico * @param Options $options 57007225e5Sgerardnico */ 58007225e5Sgerardnico protected function setup(Options $options) 59007225e5Sgerardnico { 6071f916b9Sgerardnico $options->setHelp( 6171f916b9Sgerardnico "Manage the analytics database\n\n" . 6271f916b9Sgerardnico "analytics\n" . 6371f916b9Sgerardnico "sync" 6471f916b9Sgerardnico ); 65007225e5Sgerardnico $options->registerOption('version', 'print version', 'v'); 6671f916b9Sgerardnico $options->registerCommand(self::ANALYTICS, "Update the analytics data"); 6771f916b9Sgerardnico $options->registerOption( 68007225e5Sgerardnico 'namespaces', 69007225e5Sgerardnico "If no namespace is given, the root namespace is assumed.", 7071f916b9Sgerardnico 'n', 7171f916b9Sgerardnico true 7271f916b9Sgerardnico ); 73007225e5Sgerardnico $options->registerOption( 74007225e5Sgerardnico 'output', 75007225e5Sgerardnico "Optional, where to store the analytical data as csv eg. a filename.", 76007225e5Sgerardnico 'o', 'file'); 77007225e5Sgerardnico $options->registerOption( 78007225e5Sgerardnico 'cache', 79007225e5Sgerardnico "Optional, returns from the cache if set", 80007225e5Sgerardnico 'c', false); 8171f916b9Sgerardnico $options->registerOption( 8271f916b9Sgerardnico 'dry', 8371f916b9Sgerardnico "Optional, dry-run", 8471f916b9Sgerardnico 'd', false); 8571f916b9Sgerardnico $options->registerCommand(self::SYNC, "Sync the database"); 86007225e5Sgerardnico 87007225e5Sgerardnico } 88007225e5Sgerardnico 89007225e5Sgerardnico /** 90007225e5Sgerardnico * The main entry 91007225e5Sgerardnico * @param Options $options 92007225e5Sgerardnico */ 93007225e5Sgerardnico protected function main(Options $options) 94007225e5Sgerardnico { 95007225e5Sgerardnico 96007225e5Sgerardnico $namespaces = array_map('cleanID', $options->getArgs()); 97007225e5Sgerardnico if (!count($namespaces)) $namespaces = array(''); //import from top 98007225e5Sgerardnico 9971f916b9Sgerardnico $cache = $options->getOpt('cache', false); 10071f916b9Sgerardnico $depth = $options->getOpt('depth', 0); 10121913ab3SNickeau $cmd = $options->getCmd(); 10221913ab3SNickeau if ($cmd == "") { 10321913ab3SNickeau $cmd = self::ANALYTICS; 10421913ab3SNickeau } 10521913ab3SNickeau switch ($cmd) { 10671f916b9Sgerardnico case self::ANALYTICS: 107007225e5Sgerardnico $output = $options->getOpt('output', ''); 108007225e5Sgerardnico //if ($output == '-') $output = 'php://stdout'; 10971f916b9Sgerardnico $this->updateAnalyticsData($namespaces, $output, $cache, $depth); 11071f916b9Sgerardnico break; 11171f916b9Sgerardnico case self::SYNC: 11271f916b9Sgerardnico $this->syncPages(); 11371f916b9Sgerardnico break; 11471f916b9Sgerardnico default: 11521913ab3SNickeau throw new \RuntimeException("Combo: Command unknown (" . $cmd . ")"); 11671f916b9Sgerardnico } 117007225e5Sgerardnico 118007225e5Sgerardnico 119007225e5Sgerardnico } 120007225e5Sgerardnico 121007225e5Sgerardnico /** 12271f916b9Sgerardnico * @param array $namespaces 123007225e5Sgerardnico * @param $output 124007225e5Sgerardnico * @param bool $cache 125007225e5Sgerardnico * @param int $depth recursion depth. 0 for unlimited 126007225e5Sgerardnico */ 12771f916b9Sgerardnico private function updateAnalyticsData($namespaces = array(), $output = null, $cache = false, $depth = 0) 128007225e5Sgerardnico { 129007225e5Sgerardnico 130007225e5Sgerardnico $fileHandle = null; 131007225e5Sgerardnico if (!empty($output)) { 132007225e5Sgerardnico $fileHandle = @fopen($output, 'w'); 133007225e5Sgerardnico if (!$fileHandle) $this->fatal("Failed to open $output"); 134007225e5Sgerardnico } 135007225e5Sgerardnico 136*37748cd8SNickeau /** 137*37748cd8SNickeau * Run as admin to overcome the fact that 138*37748cd8SNickeau * anonymous user cannot see all links and backlinks 139*37748cd8SNickeau */ 140*37748cd8SNickeau global $USERINFO; 141*37748cd8SNickeau $USERINFO['grps'] = array('admin'); 142*37748cd8SNickeau global $INPUT; 143*37748cd8SNickeau $INPUT->server->set('REMOTE_USER', "cli"); 144*37748cd8SNickeau 145*37748cd8SNickeau $pages = FsWikiUtility::getPages($namespaces, $depth); 146007225e5Sgerardnico 147007225e5Sgerardnico 148007225e5Sgerardnico if (!empty($fileHandle)) { 149007225e5Sgerardnico $header = array( 150007225e5Sgerardnico 'id', 151007225e5Sgerardnico 'backlinks', 152007225e5Sgerardnico 'broken_links', 153007225e5Sgerardnico 'changes', 154007225e5Sgerardnico 'chars', 155007225e5Sgerardnico 'external_links', 156007225e5Sgerardnico 'external_medias', 157007225e5Sgerardnico 'h1', 158007225e5Sgerardnico 'h2', 159007225e5Sgerardnico 'h3', 160007225e5Sgerardnico 'h4', 161007225e5Sgerardnico 'h5', 162007225e5Sgerardnico 'internal_links', 163007225e5Sgerardnico 'internal_medias', 164007225e5Sgerardnico 'words', 165007225e5Sgerardnico 'score' 166007225e5Sgerardnico ); 167007225e5Sgerardnico fwrite($fileHandle, implode(",", $header) . PHP_EOL); 168007225e5Sgerardnico } 1699da76789Sgerardnico $pageCounter = 0; 170e8b2ff59SNickeau $totalNumberOfPages = sizeof($pages); 171007225e5Sgerardnico while ($page = array_shift($pages)) { 172007225e5Sgerardnico $id = $page['id']; 173007225e5Sgerardnico 1749da76789Sgerardnico $pageCounter++; 175e8b2ff59SNickeau echo "Processing the page {$id} ($pageCounter / $totalNumberOfPages)\n"; 176007225e5Sgerardnico 1771c5862d3Sgerardnico $data = Analytics::processAndGetDataAsArray($id, $cache); 178007225e5Sgerardnico if (!empty($fileHandle)) { 179007225e5Sgerardnico $statistics = $data[Analytics::STATISTICS]; 180007225e5Sgerardnico $row = array( 181007225e5Sgerardnico 'id' => $id, 182007225e5Sgerardnico 'backlinks' => $statistics[Analytics::INTERNAL_BACKLINKS_COUNT], 183007225e5Sgerardnico 'broken_links' => $statistics[Analytics::INTERNAL_LINKS_BROKEN_COUNT], 184007225e5Sgerardnico 'changes' => $statistics[Analytics::EDITS_COUNT], 185007225e5Sgerardnico 'chars' => $statistics[Analytics::CHARS_COUNT], 186007225e5Sgerardnico 'external_links' => $statistics[Analytics::EXTERNAL_LINKS_COUNT], 187e8b2ff59SNickeau 'external_medias' => $statistics[Analytics::EXTERNAL_MEDIAS_COUNT], 188531e725cSNickeau Analytics::H1 => $statistics[Analytics::HEADERS_COUNT][Analytics::H1], 189007225e5Sgerardnico 'h2' => $statistics[Analytics::HEADERS_COUNT]['h2'], 190007225e5Sgerardnico 'h3' => $statistics[Analytics::HEADERS_COUNT]['h3'], 191007225e5Sgerardnico 'h4' => $statistics[Analytics::HEADERS_COUNT]['h4'], 192007225e5Sgerardnico 'h5' => $statistics[Analytics::HEADERS_COUNT]['h5'], 193007225e5Sgerardnico 'internal_links' => $statistics[Analytics::INTERNAL_LINKS_COUNT], 194007225e5Sgerardnico 'internal_medias' => $statistics[Analytics::INTERNAL_MEDIAS_COUNT], 195*37748cd8SNickeau 'words' => $statistics[Analytics::WORD_COUNT], 196007225e5Sgerardnico 'low' => $data[Analytics::QUALITY]['low'] 197007225e5Sgerardnico ); 198007225e5Sgerardnico fwrite($fileHandle, implode(",", $row) . PHP_EOL); 199007225e5Sgerardnico } 200007225e5Sgerardnico } 201007225e5Sgerardnico if (!empty($fileHandle)) { 202007225e5Sgerardnico fclose($fileHandle); 203007225e5Sgerardnico } 204007225e5Sgerardnico 205007225e5Sgerardnico } 20671f916b9Sgerardnico 207325fe0c5Sgerardnico 208325fe0c5Sgerardnico 20971f916b9Sgerardnico private function syncPages() 21071f916b9Sgerardnico { 21171f916b9Sgerardnico $sqlite = Sqlite::getSqlite(); 21271f916b9Sgerardnico $res = $sqlite->query("select ID from pages"); 21371f916b9Sgerardnico if (!$res) { 21471f916b9Sgerardnico throw new \RuntimeException("An exception has occurred with the alias selection query"); 21571f916b9Sgerardnico } 21671f916b9Sgerardnico $res2arr = $sqlite->res2arr($res); 21771f916b9Sgerardnico $sqlite->res_close($res); 21871f916b9Sgerardnico foreach ($res2arr as $row) { 21971f916b9Sgerardnico $id = $row['ID']; 22071f916b9Sgerardnico if (!page_exists($id)) { 22171f916b9Sgerardnico echo 'Page does not exist on the file system. Deleted from the database (' . $id . ")\n"; 22285e82846SNickeau Page::createPageFromId($id)->deleteInDb(); 22371f916b9Sgerardnico } 22471f916b9Sgerardnico } 22571f916b9Sgerardnico 22671f916b9Sgerardnico 22771f916b9Sgerardnico } 228007225e5Sgerardnico} 229