xref: /plugin/combo/cli.php (revision 91f20ee45197298be776d6046a62fc936a74cbed)
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