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 */ 12007225e5Sgerardnico 134ebc3257Sgerardnicouse ComboStrap\DatabasePageRow; 14c3437056SNickeauuse ComboStrap\Event; 1504fd306cSNickeauuse ComboStrap\ExceptionBadSyntax; 1604fd306cSNickeauuse ComboStrap\ExceptionCompile; 17031d4b49Sgerardnicouse ComboStrap\ExceptionNotExists; 1804fd306cSNickeauuse ComboStrap\ExceptionNotFound; 1904fd306cSNickeauuse ComboStrap\ExceptionRuntime; 20031d4b49Sgerardnicouse ComboStrap\ExecutionContext; 2137748cd8SNickeauuse ComboStrap\FsWikiUtility; 22c3437056SNickeauuse ComboStrap\LogUtility; 2304fd306cSNickeauuse ComboStrap\MarkupPath; 24031d4b49Sgerardnicouse ComboStrap\Meta\Field\BacklinkCount; 2504fd306cSNickeauuse ComboStrap\Meta\Field\PageH1; 26c3437056SNickeauuse ComboStrap\MetadataFrontmatterStore; 2771f916b9Sgerardnicouse ComboStrap\Sqlite; 28*91f20ee4SNicolas GERARDuse dokuwiki\Extension\PluginInterface; 29007225e5Sgerardnicouse splitbrain\phpcli\Options; 30007225e5Sgerardnico 31e8b2ff59SNickeau/** 3204fd306cSNickeau * All dependency are loaded 33e8b2ff59SNickeau */ 3404fd306cSNickeaurequire_once(__DIR__ . '/vendor/autoload.php'); 35007225e5Sgerardnico 36007225e5Sgerardnico/** 37007225e5Sgerardnico * The memory of the server 128 is not enough 38007225e5Sgerardnico */ 39007225e5Sgerardnicoini_set('memory_limit', '256M'); 40007225e5Sgerardnico 41c3437056SNickeau 42007225e5Sgerardnico/** 43007225e5Sgerardnico * Class cli_plugin_combo 44007225e5Sgerardnico * 45007225e5Sgerardnico * This is a cli: 46007225e5Sgerardnico * https://www.dokuwiki.org/devel:cli_plugins#example 47007225e5Sgerardnico * 48007225e5Sgerardnico * Usage: 49007225e5Sgerardnico * 50007225e5Sgerardnico * ``` 51007225e5Sgerardnico * docker exec -ti $(CONTAINER) /bin/bash 52c3437056SNickeau * ``` 53c3437056SNickeau * ``` 54c3437056SNickeau * set animal=animal-directory-name 55c3437056SNickeau * php ./bin/plugin.php combo --help 56007225e5Sgerardnico * ``` 57007225e5Sgerardnico * or via the IDE 58007225e5Sgerardnico * 59007225e5Sgerardnico * 60007225e5Sgerardnico * Example: 61007225e5Sgerardnico * https://www.dokuwiki.org/tips:grapher 62007225e5Sgerardnico * 63007225e5Sgerardnico */ 64007225e5Sgerardnicoclass cli_plugin_combo extends DokuWiki_CLI_Plugin 65007225e5Sgerardnico{ 66c3437056SNickeau 67c3437056SNickeau const METADATA_TO_DATABASE = "metadata-to-database"; 6871f916b9Sgerardnico const ANALYTICS = "analytics"; 69c3437056SNickeau const METADATA_TO_FRONTMATTER = "metadata-to-frontmatter"; 7071f916b9Sgerardnico const SYNC = "sync"; 71c3437056SNickeau const PLUGINS_TO_UPDATE = "plugins-to-update"; 72c3437056SNickeau const FORCE_OPTION = 'force'; 73c3437056SNickeau const PORT_OPTION = 'port'; 74c3437056SNickeau const HOST_OPTION = 'host'; 75edc35203Sgerardnico const CANONICAL = "combo-cli"; 76c3437056SNickeau 77007225e5Sgerardnico 78007225e5Sgerardnico /** 79007225e5Sgerardnico * register options and arguments 80007225e5Sgerardnico * @param Options $options 8182a60d03SNickeau * 8282a60d03SNickeau * Note the animal is set in {@link DokuWikiFarmCore::detectAnimal()} 8382a60d03SNickeau * via the environment variable `animal` that is passed in the $_SERVER variable 84007225e5Sgerardnico */ 85007225e5Sgerardnico protected function setup(Options $options) 86007225e5Sgerardnico { 87c3437056SNickeau $help = <<<EOF 88c3437056SNickeauComboStrap Administrative Commands 89c3437056SNickeau 90c3437056SNickeau 91c3437056SNickeauExample: 92c3437056SNickeau * Replicate all pages into the database 93c3437056SNickeau```bash 9404fd306cSNickeauphp ./bin/plugin.php combo metadata-to-database --host serverHostName --port 80 : 95c3437056SNickeau# or 9604fd306cSNickeauphp ./bin/plugin.php combo metadata-to-database --host serverHostName --port 80 / 97c3437056SNickeau``` 98c3437056SNickeau * Replicate only the page `:namespace:my-page` 99c3437056SNickeau```bash 10004fd306cSNickeauphp ./bin/plugin.php combo metadata-to-database --host serverHostName --port 80 :namespace:my-page 101c3437056SNickeau# or 10204fd306cSNickeauphp ./bin/plugin.php combo metadata-to-database --host serverHostName --port 80 /namespace/my-page 103c3437056SNickeau``` 104c3437056SNickeau 105c3437056SNickeauAnimal: If you want to use it for an animal farm, you need to set first the animal directory name in a environment variable 106c3437056SNickeau```bash 1074cadd4f8SNickeauanimal=animal-directory-name php ./bin/plugin.php combo 108c3437056SNickeau``` 109c3437056SNickeau 110c3437056SNickeauEOF; 111c3437056SNickeau 112c3437056SNickeau $options->setHelp($help); 113007225e5Sgerardnico $options->registerOption('version', 'print version', 'v'); 114c3437056SNickeau $options->registerCommand(self::METADATA_TO_DATABASE, "Replicate the file system metadata into the database"); 115c3437056SNickeau $options->registerCommand(self::ANALYTICS, "Start the analytics and export optionally the data"); 116c3437056SNickeau $options->registerCommand(self::PLUGINS_TO_UPDATE, "List the plugins to update"); 117c3437056SNickeau $options->registerCommand(self::METADATA_TO_FRONTMATTER, "Replicate the file system metadata into the page frontmatter"); 118c3437056SNickeau $options->registerCommand(self::SYNC, "Delete the non-existing pages in the database"); 119007225e5Sgerardnico $options->registerOption( 120007225e5Sgerardnico 'output', 121007225e5Sgerardnico "Optional, where to store the analytical data as csv eg. a filename.", 122c3437056SNickeau 'o', 123c3437056SNickeau true 124c3437056SNickeau ); 125007225e5Sgerardnico $options->registerOption( 126c3437056SNickeau self::HOST_OPTION, 127c3437056SNickeau "The http host name of your server. This value is used by dokuwiki in the rendering cache key", 128c3437056SNickeau null, 129c3437056SNickeau true, 130c3437056SNickeau self::METADATA_TO_DATABASE 131c3437056SNickeau ); 132c3437056SNickeau $options->registerOption( 133c3437056SNickeau self::PORT_OPTION, 134c3437056SNickeau "The http host port of your server. This value is used by dokuwiki in the rendering cache key", 135c3437056SNickeau null, 136c3437056SNickeau true, 137c3437056SNickeau self::METADATA_TO_DATABASE 138c3437056SNickeau ); 139c3437056SNickeau $options->registerOption( 140c3437056SNickeau self::FORCE_OPTION, 141c3437056SNickeau "Replicate with force", 142c3437056SNickeau 'f', 143c3437056SNickeau false, 144c3437056SNickeau self::METADATA_TO_DATABASE 145c3437056SNickeau ); 146*91f20ee4SNicolas GERARD $startPathArgName = 'startPath'; 147*91f20ee4SNicolas GERARD $startPathHelpDescription = "The start path (a page or a directory). For all pages, type the root directory '/' or ':'"; 148*91f20ee4SNicolas GERARD $options->registerArgument( 149*91f20ee4SNicolas GERARD $startPathArgName, 150*91f20ee4SNicolas GERARD $startPathHelpDescription, 151*91f20ee4SNicolas GERARD true, 152*91f20ee4SNicolas GERARD self::METADATA_TO_DATABASE 153*91f20ee4SNicolas GERARD ); 154*91f20ee4SNicolas GERARD $options->registerArgument( 155*91f20ee4SNicolas GERARD $startPathArgName, 156*91f20ee4SNicolas GERARD $startPathHelpDescription, 157*91f20ee4SNicolas GERARD true, 158*91f20ee4SNicolas GERARD self::METADATA_TO_FRONTMATTER 159*91f20ee4SNicolas GERARD ); 160*91f20ee4SNicolas GERARD $options->registerArgument( 161*91f20ee4SNicolas GERARD $startPathArgName, 162*91f20ee4SNicolas GERARD $startPathHelpDescription, 163*91f20ee4SNicolas GERARD true, 164*91f20ee4SNicolas GERARD self::SYNC 165*91f20ee4SNicolas GERARD ); 16671f916b9Sgerardnico $options->registerOption( 16771f916b9Sgerardnico 'dry', 16871f916b9Sgerardnico "Optional, dry-run", 16971f916b9Sgerardnico 'd', false); 170c3437056SNickeau 171007225e5Sgerardnico 172007225e5Sgerardnico } 173007225e5Sgerardnico 174007225e5Sgerardnico /** 175007225e5Sgerardnico * The main entry 176007225e5Sgerardnico * @param Options $options 17704fd306cSNickeau * @throws ExceptionCompile 178007225e5Sgerardnico */ 179007225e5Sgerardnico protected function main(Options $options) 180007225e5Sgerardnico { 181007225e5Sgerardnico 182007225e5Sgerardnico 18382a60d03SNickeau if (isset($_REQUEST['animal'])) { 1844cadd4f8SNickeau // on linux 18582a60d03SNickeau echo "Animal detected: " . $_REQUEST['animal'] . "\n"; 18682a60d03SNickeau } else { 1874cadd4f8SNickeau // on windows 18882a60d03SNickeau echo "No Animal detected\n"; 18982a60d03SNickeau echo "Conf: " . DOKU_CONF . "\n"; 19082a60d03SNickeau } 19182a60d03SNickeau 192c3437056SNickeau $args = $options->getArgs(); 193c3437056SNickeau 194c3437056SNickeau 19571f916b9Sgerardnico $depth = $options->getOpt('depth', 0); 19621913ab3SNickeau $cmd = $options->getCmd(); 1974ebc3257Sgerardnico 1984ebc3257Sgerardnico try { 19921913ab3SNickeau switch ($cmd) { 200c3437056SNickeau case self::METADATA_TO_DATABASE: 201c3437056SNickeau $startPath = $this->getStartPath($args); 202c3437056SNickeau $force = $options->getOpt(self::FORCE_OPTION, false); 203c3437056SNickeau $hostOptionValue = $options->getOpt(self::HOST_OPTION, null); 204c3437056SNickeau if ($hostOptionValue === null) { 205c3437056SNickeau fwrite(STDERR, "The host name is mandatory"); 206c3437056SNickeau return; 207c3437056SNickeau } 208c3437056SNickeau $_SERVER['HTTP_HOST'] = $hostOptionValue; 209c3437056SNickeau $portOptionName = $options->getOpt(self::PORT_OPTION, null); 210c3437056SNickeau if ($portOptionName === null) { 211c3437056SNickeau fwrite(STDERR, "The host port is mandatory"); 212c3437056SNickeau return; 213c3437056SNickeau } 214c3437056SNickeau $_SERVER['SERVER_PORT'] = $portOptionName; 215c3437056SNickeau $this->index($startPath, $force, $depth); 216c3437056SNickeau break; 217c3437056SNickeau case self::METADATA_TO_FRONTMATTER: 218c3437056SNickeau $startPath = $this->getStartPath($args); 219c3437056SNickeau $this->frontmatter($startPath, $depth); 220c3437056SNickeau break; 22171f916b9Sgerardnico case self::ANALYTICS: 222c3437056SNickeau $startPath = $this->getStartPath($args); 223007225e5Sgerardnico $output = $options->getOpt('output', ''); 224007225e5Sgerardnico //if ($output == '-') $output = 'php://stdout'; 225c3437056SNickeau $this->analytics($startPath, $output, $depth); 22671f916b9Sgerardnico break; 22771f916b9Sgerardnico case self::SYNC: 228c3437056SNickeau $this->deleteNonExistingPageFromDatabase(); 229c3437056SNickeau break; 230c3437056SNickeau case self::PLUGINS_TO_UPDATE: 231c3437056SNickeau /** 232c3437056SNickeau * Endpoint: 233c3437056SNickeau * self::EXTENSION_REPOSITORY_API.'?fmt=php&ext[]='.urlencode($name) 234c3437056SNickeau * `http://www.dokuwiki.org/lib/plugins/pluginrepo/api.php?fmt=php&ext[]=`.urlencode($name) 235c3437056SNickeau */ 236c3437056SNickeau $pluginList = plugin_list('', true); 237c3437056SNickeau $extension = $this->loadHelper('extension_extension'); 238*91f20ee4SNicolas GERARD if ($extension instanceof PluginInterface) { 239*91f20ee4SNicolas GERARD /** 240*91f20ee4SNicolas GERARD * Release 2025-05-14 "Librarian" 241*91f20ee4SNicolas GERARD * https://www.dokuwiki.org/changes#release_2025-05-14_librarian 242*91f20ee4SNicolas GERARD * https://www.patreon.com/posts/new-extension-116501986 243*91f20ee4SNicolas GERARD * ./bin/plugin.php extension list 244*91f20ee4SNicolas GERARD * @link lib/plugins/extension/cli.php 245*91f20ee4SNicolas GERARD */ 246*91f20ee4SNicolas GERARD echo "The new extension plugin system is not yet supported"; 247*91f20ee4SNicolas GERARD echo "Check the cli instead: ./bin/plugin.php extension list"; 248*91f20ee4SNicolas GERARD exit(1); 249*91f20ee4SNicolas GERARD } 250*91f20ee4SNicolas GERARD if ($extension == null) { 251*91f20ee4SNicolas GERARD echo "The plugin (extension_extension) could not be loaded"; 252*91f20ee4SNicolas GERARD exit(1); 253*91f20ee4SNicolas GERARD } 254c3437056SNickeau foreach ($pluginList as $name) { 255*91f20ee4SNicolas GERARD 256*91f20ee4SNicolas GERARD /* @var helper_plugin_extension_extension $extension 257*91f20ee4SNicolas GERARD * @noinspection PhpUndefinedClassInspection 258*91f20ee4SNicolas GERARD */ 259c3437056SNickeau $extension->setExtension($name); 260*91f20ee4SNicolas GERARD /** @noinspection PhpUndefinedMethodInspection */ 261c3437056SNickeau if ($extension->updateAvailable()) { 262c3437056SNickeau echo "The extension $name should be updated"; 263c3437056SNickeau } 264*91f20ee4SNicolas GERARD 265c3437056SNickeau } 266*91f20ee4SNicolas GERARD 26771f916b9Sgerardnico break; 26871f916b9Sgerardnico default: 269c3437056SNickeau if ($cmd !== "") { 270c3437056SNickeau fwrite(STDERR, "Combo: Command unknown (" . $cmd . ")"); 271c3437056SNickeau } else { 272c3437056SNickeau echo $options->help(); 273c3437056SNickeau } 274c3437056SNickeau exit(1); 27571f916b9Sgerardnico } 2764ebc3257Sgerardnico } catch (\Exception $exception) { 2774ebc3257Sgerardnico fwrite(STDERR, "An internal error has occured. " . $exception->getMessage() . "\n" . $exception->getTraceAsString()); 2784ebc3257Sgerardnico exit(1); 2794ebc3257Sgerardnico } 280007225e5Sgerardnico 281007225e5Sgerardnico 282007225e5Sgerardnico } 283007225e5Sgerardnico 284007225e5Sgerardnico /** 28571f916b9Sgerardnico * @param array $namespaces 286c3437056SNickeau * @param bool $rebuild 287007225e5Sgerardnico * @param int $depth recursion depth. 0 for unlimited 28804fd306cSNickeau * @throws ExceptionCompile 289007225e5Sgerardnico */ 290c3437056SNickeau private function index($namespaces = array(), $rebuild = false, $depth = 0) 291c3437056SNickeau { 292c3437056SNickeau 293c3437056SNickeau /** 294c3437056SNickeau * Run as admin to overcome the fact that 295c3437056SNickeau * anonymous user cannot see all links and backlinks 296c3437056SNickeau */ 297c3437056SNickeau global $USERINFO; 298c3437056SNickeau $USERINFO['grps'] = array('admin'); 299c3437056SNickeau global $INPUT; 300c3437056SNickeau $INPUT->server->set('REMOTE_USER', "cli"); 301c3437056SNickeau 302c3437056SNickeau $pages = FsWikiUtility::getPages($namespaces, $depth); 303c3437056SNickeau 304c3437056SNickeau $pageCounter = 0; 305c3437056SNickeau $totalNumberOfPages = sizeof($pages); 306c3437056SNickeau while ($pageArray = array_shift($pages)) { 307c3437056SNickeau $id = $pageArray['id']; 3084cadd4f8SNickeau global $ID; 3094cadd4f8SNickeau $ID = $id; 310c3437056SNickeau /** 311c3437056SNickeau * Indexing the page start the database replication 31204fd306cSNickeau * See {@link action_plugin_combo_indexer} 313c3437056SNickeau */ 314c3437056SNickeau $pageCounter++; 315edc35203Sgerardnico $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 316c3437056SNickeau try { 317c3437056SNickeau /** 318c3437056SNickeau * If the page does not need to be indexed, there is no run 319c3437056SNickeau * and false is returned 320c3437056SNickeau */ 321c3437056SNickeau $indexedOrNot = idx_addPage($id, true, true); 322c3437056SNickeau if ($indexedOrNot) { 323c3437056SNickeau LogUtility::msg("The page {$id} ($pageCounter / $totalNumberOfPages) was indexed and replicated", LogUtility::LVL_MSG_INFO); 324c3437056SNickeau } else { 325c3437056SNickeau LogUtility::msg("The page {$id} ($pageCounter / $totalNumberOfPages) has an error", LogUtility::LVL_MSG_ERROR); 326c3437056SNickeau } 32704fd306cSNickeau } catch (ExceptionRuntime $e) { 328c3437056SNickeau LogUtility::msg("The page {$id} ($pageCounter / $totalNumberOfPages) has an error: " . $e->getMessage(), LogUtility::LVL_MSG_ERROR); 329edc35203Sgerardnico } finally { 330edc35203Sgerardnico $executionContext->close(); 331c3437056SNickeau } 332c3437056SNickeau } 333c3437056SNickeau /** 334c3437056SNickeau * Process all backlinks 335c3437056SNickeau */ 336c3437056SNickeau echo "Processing Replication Request\n"; 337c3437056SNickeau Event::dispatchEvent(PHP_INT_MAX); 338c3437056SNickeau 339c3437056SNickeau } 340c3437056SNickeau 341c3437056SNickeau private function analytics($namespaces = array(), $output = null, $depth = 0) 342007225e5Sgerardnico { 343007225e5Sgerardnico 344007225e5Sgerardnico $fileHandle = null; 345007225e5Sgerardnico if (!empty($output)) { 346007225e5Sgerardnico $fileHandle = @fopen($output, 'w'); 347007225e5Sgerardnico if (!$fileHandle) $this->fatal("Failed to open $output"); 348007225e5Sgerardnico } 349007225e5Sgerardnico 35037748cd8SNickeau /** 35137748cd8SNickeau * Run as admin to overcome the fact that 35237748cd8SNickeau * anonymous user cannot see all links and backlinks 35337748cd8SNickeau */ 35437748cd8SNickeau global $USERINFO; 35537748cd8SNickeau $USERINFO['grps'] = array('admin'); 35637748cd8SNickeau global $INPUT; 35737748cd8SNickeau $INPUT->server->set('REMOTE_USER', "cli"); 35837748cd8SNickeau 35937748cd8SNickeau $pages = FsWikiUtility::getPages($namespaces, $depth); 360007225e5Sgerardnico 361007225e5Sgerardnico 362007225e5Sgerardnico if (!empty($fileHandle)) { 363007225e5Sgerardnico $header = array( 364007225e5Sgerardnico 'id', 365007225e5Sgerardnico 'backlinks', 366007225e5Sgerardnico 'broken_links', 367007225e5Sgerardnico 'changes', 368007225e5Sgerardnico 'chars', 369007225e5Sgerardnico 'external_links', 370007225e5Sgerardnico 'external_medias', 371007225e5Sgerardnico 'h1', 372007225e5Sgerardnico 'h2', 373007225e5Sgerardnico 'h3', 374007225e5Sgerardnico 'h4', 375007225e5Sgerardnico 'h5', 376007225e5Sgerardnico 'internal_links', 377007225e5Sgerardnico 'internal_medias', 378007225e5Sgerardnico 'words', 379007225e5Sgerardnico 'score' 380007225e5Sgerardnico ); 381007225e5Sgerardnico fwrite($fileHandle, implode(",", $header) . PHP_EOL); 382007225e5Sgerardnico } 3839da76789Sgerardnico $pageCounter = 0; 384e8b2ff59SNickeau $totalNumberOfPages = sizeof($pages); 385c3437056SNickeau while ($pageArray = array_shift($pages)) { 386c3437056SNickeau $id = $pageArray['id']; 38704fd306cSNickeau $page = MarkupPath::createMarkupFromId($id); 388c3437056SNickeau 389007225e5Sgerardnico 3909da76789Sgerardnico $pageCounter++; 391c3437056SNickeau /** 392c3437056SNickeau * Analytics 393c3437056SNickeau */ 394edc35203Sgerardnico echo "Analytics Processing for the page {$id} ($pageCounter / $totalNumberOfPages)\n"; 395edc35203Sgerardnico $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 396edc35203Sgerardnico try { 39704fd306cSNickeau $analyticsPath = $page->fetchAnalyticsPath(); 398edc35203Sgerardnico } catch (ExceptionNotExists $e) { 399edc35203Sgerardnico LogUtility::error("The analytics document for the page ($page) was not found"); 400edc35203Sgerardnico continue; 401edc35203Sgerardnico } catch (ExceptionCompile $e) { 402edc35203Sgerardnico LogUtility::error("Error when get the analytics.", self::CANONICAL, $e); 403edc35203Sgerardnico continue; 404edc35203Sgerardnico } finally { 405edc35203Sgerardnico $executionContext->close(); 406edc35203Sgerardnico } 407edc35203Sgerardnico 40804fd306cSNickeau try { 40904fd306cSNickeau $data = \ComboStrap\Json::createFromPath($analyticsPath)->toArray(); 41004fd306cSNickeau } catch (ExceptionBadSyntax $e) { 41104fd306cSNickeau LogUtility::error("The analytics json of the page ($page) is not conform"); 41204fd306cSNickeau continue; 413edc35203Sgerardnico } catch (ExceptionNotFound|ExceptionNotExists $e) { 41404fd306cSNickeau LogUtility::error("The analytics document ({$analyticsPath}) for the page ($page) was not found"); 41504fd306cSNickeau continue; 41604fd306cSNickeau } 417c3437056SNickeau 418007225e5Sgerardnico if (!empty($fileHandle)) { 41904fd306cSNickeau $statistics = $data[renderer_plugin_combo_analytics::STATISTICS]; 420007225e5Sgerardnico $row = array( 421007225e5Sgerardnico 'id' => $id, 422c3437056SNickeau 'backlinks' => $statistics[BacklinkCount::getPersistentName()], 42304fd306cSNickeau 'broken_links' => $statistics[renderer_plugin_combo_analytics::INTERNAL_LINK_BROKEN_COUNT], 42404fd306cSNickeau 'changes' => $statistics[renderer_plugin_combo_analytics::EDITS_COUNT], 42504fd306cSNickeau 'chars' => $statistics[renderer_plugin_combo_analytics::CHAR_COUNT], 42604fd306cSNickeau 'external_links' => $statistics[renderer_plugin_combo_analytics::EXTERNAL_LINK_COUNT], 42704fd306cSNickeau 'external_medias' => $statistics[renderer_plugin_combo_analytics::EXTERNAL_MEDIA_COUNT], 42804fd306cSNickeau PageH1::PROPERTY_NAME => $statistics[renderer_plugin_combo_analytics::HEADING_COUNT][PageH1::PROPERTY_NAME], 42904fd306cSNickeau 'h2' => $statistics[renderer_plugin_combo_analytics::HEADING_COUNT]['h2'], 43004fd306cSNickeau 'h3' => $statistics[renderer_plugin_combo_analytics::HEADING_COUNT]['h3'], 43104fd306cSNickeau 'h4' => $statistics[renderer_plugin_combo_analytics::HEADING_COUNT]['h4'], 43204fd306cSNickeau 'h5' => $statistics[renderer_plugin_combo_analytics::HEADING_COUNT]['h5'], 43304fd306cSNickeau 'internal_links' => $statistics[renderer_plugin_combo_analytics::INTERNAL_LINK_COUNT], 43404fd306cSNickeau 'internal_medias' => $statistics[renderer_plugin_combo_analytics::INTERNAL_MEDIA_COUNT], 43504fd306cSNickeau 'words' => $statistics[renderer_plugin_combo_analytics::WORD_COUNT], 43604fd306cSNickeau 'low' => $data[renderer_plugin_combo_analytics::QUALITY]['low'] 437007225e5Sgerardnico ); 438007225e5Sgerardnico fwrite($fileHandle, implode(",", $row) . PHP_EOL); 439007225e5Sgerardnico } 440c3437056SNickeau 441007225e5Sgerardnico } 442007225e5Sgerardnico if (!empty($fileHandle)) { 443007225e5Sgerardnico fclose($fileHandle); 444007225e5Sgerardnico } 445007225e5Sgerardnico 446007225e5Sgerardnico } 44771f916b9Sgerardnico 448325fe0c5Sgerardnico 4494ebc3257Sgerardnico /** 4504ebc3257Sgerardnico * @throws \ComboStrap\ExceptionSqliteNotAvailable 4514ebc3257Sgerardnico */ 452c3437056SNickeau private function deleteNonExistingPageFromDatabase() 45371f916b9Sgerardnico { 454c3437056SNickeau LogUtility::msg("Starting: Deleting non-existing page from database"); 455c3437056SNickeau $sqlite = Sqlite::createOrGetSqlite(); 456c3437056SNickeau $request = $sqlite 457c3437056SNickeau ->createRequest() 458c3437056SNickeau ->setQuery("select id as \"id\" from pages"); 459c3437056SNickeau $rows = []; 460c3437056SNickeau try { 461c3437056SNickeau $rows = $request 462c3437056SNickeau ->execute() 463c3437056SNickeau ->getRows(); 46404fd306cSNickeau } catch (ExceptionCompile $e) { 465c3437056SNickeau LogUtility::msg("Error while getting the id pages. {$e->getMessage()}"); 466c3437056SNickeau return; 467c3437056SNickeau } finally { 468c3437056SNickeau $request->close(); 46971f916b9Sgerardnico } 470c3437056SNickeau $counter = 0; 471031d4b49Sgerardnico 472c3437056SNickeau foreach ($rows as $row) { 473031d4b49Sgerardnico /** 474031d4b49Sgerardnico * Context 475031d4b49Sgerardnico * PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes) 476031d4b49Sgerardnico * in /opt/www/datacadamia.com/inc/ErrorHandler.php on line 102 477031d4b49Sgerardnico */ 478031d4b49Sgerardnico $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 479031d4b49Sgerardnico try { 480c3437056SNickeau $counter++; 481c3437056SNickeau $id = $row['id']; 48271f916b9Sgerardnico if (!page_exists($id)) { 4834ebc3257Sgerardnico echo 'Page does not exist on the file system. Delete from the database (' . $id . ")\n"; 4844ebc3257Sgerardnico try { 485031d4b49Sgerardnico $dbRow = DatabasePageRow::getFromDokuWikiId($id); 486031d4b49Sgerardnico $dbRow->delete(); 4874ebc3257Sgerardnico } catch (ExceptionNotFound $e) { 488031d4b49Sgerardnico // ok 4894ebc3257Sgerardnico } 490c3437056SNickeau } 491031d4b49Sgerardnico } finally { 492031d4b49Sgerardnico $executionContext->close(); 493031d4b49Sgerardnico } 494031d4b49Sgerardnico 495c3437056SNickeau } 496c3437056SNickeau LogUtility::msg("Sync finished ($counter pages checked)"); 497c3437056SNickeau 498c3437056SNickeau } 499c3437056SNickeau 500c3437056SNickeau private function frontmatter($namespaces, $depth) 501c3437056SNickeau { 502c3437056SNickeau $pages = FsWikiUtility::getPages($namespaces, $depth); 503c3437056SNickeau $pageCounter = 0; 504c3437056SNickeau $totalNumberOfPages = sizeof($pages); 505c3437056SNickeau $pagesWithChanges = []; 506c3437056SNickeau $pagesWithError = []; 507c3437056SNickeau $pagesWithOthers = []; 508c3437056SNickeau $notChangedCounter = 0; 509c3437056SNickeau while ($pageArray = array_shift($pages)) { 510c3437056SNickeau $id = $pageArray['id']; 5114cadd4f8SNickeau global $ID; 5124cadd4f8SNickeau $ID = $id; 51304fd306cSNickeau $page = MarkupPath::createMarkupFromId($id); 514c3437056SNickeau $pageCounter++; 515c3437056SNickeau LogUtility::msg("Processing page {$id} ($pageCounter / $totalNumberOfPages) ", LogUtility::LVL_MSG_INFO); 516edc35203Sgerardnico $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 517c3437056SNickeau try { 518c3437056SNickeau $message = MetadataFrontmatterStore::createFromPage($page) 519c3437056SNickeau ->sync(); 520c3437056SNickeau switch ($message->getStatus()) { 521c3437056SNickeau case syntax_plugin_combo_frontmatter::UPDATE_EXIT_CODE_NOT_CHANGED: 522c3437056SNickeau $notChangedCounter++; 523c3437056SNickeau break; 524c3437056SNickeau case syntax_plugin_combo_frontmatter::UPDATE_EXIT_CODE_DONE: 525c3437056SNickeau $pagesWithChanges[] = $id; 526c3437056SNickeau break; 527c3437056SNickeau case syntax_plugin_combo_frontmatter::UPDATE_EXIT_CODE_ERROR: 528c3437056SNickeau $pagesWithError[$id] = $message->getPlainTextContent(); 529c3437056SNickeau break; 530c3437056SNickeau default: 531c3437056SNickeau $pagesWithOthers[$id] = $message->getPlainTextContent(); 532c3437056SNickeau break; 533c3437056SNickeau 534c3437056SNickeau } 53504fd306cSNickeau } catch (ExceptionCompile $e) { 536c3437056SNickeau $pagesWithError[$id] = $e->getMessage(); 537edc35203Sgerardnico } finally { 538edc35203Sgerardnico $executionContext->close(); 539c3437056SNickeau } 540c3437056SNickeau 541c3437056SNickeau } 542c3437056SNickeau 543c3437056SNickeau echo "\n"; 544c3437056SNickeau echo "Result:\n"; 545c3437056SNickeau echo "$notChangedCounter pages without any frontmatter modifications\n"; 546c3437056SNickeau 547c3437056SNickeau if (sizeof($pagesWithError) > 0) { 548c3437056SNickeau echo "\n"; 549c3437056SNickeau echo "The following pages had errors\n"; 550c3437056SNickeau $pageCounter = 0; 551c3437056SNickeau $totalNumberOfPages = sizeof($pagesWithError); 552c3437056SNickeau foreach ($pagesWithError as $id => $message) { 553c3437056SNickeau $pageCounter++; 554c3437056SNickeau LogUtility::msg("Page {$id} ($pageCounter / $totalNumberOfPages): " . $message, LogUtility::LVL_MSG_ERROR); 555c3437056SNickeau } 556c3437056SNickeau } else { 557c3437056SNickeau echo "No error\n"; 558c3437056SNickeau } 559c3437056SNickeau 560c3437056SNickeau if (sizeof($pagesWithChanges) > 0) { 561c3437056SNickeau echo "\n"; 562c3437056SNickeau echo "The following pages had changed:\n"; 563c3437056SNickeau $pageCounter = 0; 564c3437056SNickeau $totalNumberOfPages = sizeof($pagesWithChanges); 565c3437056SNickeau foreach ($pagesWithChanges as $id) { 566c3437056SNickeau $pageCounter++; 567c3437056SNickeau LogUtility::msg("Page {$id} ($pageCounter / $totalNumberOfPages) ", LogUtility::LVL_MSG_ERROR); 568c3437056SNickeau } 569c3437056SNickeau } else { 570c3437056SNickeau echo "No changes\n"; 571c3437056SNickeau } 572c3437056SNickeau 573c3437056SNickeau if (sizeof($pagesWithOthers) > 0) { 574c3437056SNickeau echo "\n"; 575c3437056SNickeau echo "The following pages had an other status"; 576c3437056SNickeau $pageCounter = 0; 577c3437056SNickeau $totalNumberOfPages = sizeof($pagesWithOthers); 578c3437056SNickeau foreach ($pagesWithOthers as $id => $message) { 579c3437056SNickeau $pageCounter++; 580c3437056SNickeau LogUtility::msg("Page {$id} ($pageCounter / $totalNumberOfPages) " . $message, LogUtility::LVL_MSG_ERROR); 581c3437056SNickeau } 58271f916b9Sgerardnico } 58371f916b9Sgerardnico } 58471f916b9Sgerardnico 585c3437056SNickeau private function getStartPath($args) 586c3437056SNickeau { 587c3437056SNickeau $sizeof = sizeof($args); 588c3437056SNickeau switch ($sizeof) { 589c3437056SNickeau case 0: 590c3437056SNickeau fwrite(STDERR, "The start path is mandatory and was not given"); 591c3437056SNickeau exit(1); 592c3437056SNickeau case 1: 593c3437056SNickeau $startPath = $args[0]; 594c3437056SNickeau if (!in_array($startPath, [":", "/"])) { 595c3437056SNickeau // cleanId would return blank for a root 596c3437056SNickeau $startPath = cleanID($startPath); 597c3437056SNickeau } 598c3437056SNickeau break; 599c3437056SNickeau default: 600c3437056SNickeau fwrite(STDERR, "Too much arguments given $sizeof"); 601c3437056SNickeau exit(1); 602c3437056SNickeau } 603c3437056SNickeau return $startPath; 60471f916b9Sgerardnico } 605007225e5Sgerardnico} 606