xref: /template/strap/cli.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
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