xref: /template/strap/cli.php (revision 1fa8c418ed5809db58049141be41b7738471dd32)
1<?php
2/**
3 * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved.
4 *
5 * This source code is licensed under the GPL license found in the
6 * COPYING  file in the root directory of this source tree.
7 *
8 * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
9 * @author   ComboStrap <support@combostrap.com>
10 *
11 */
12if (!defined('DOKU_INC')) die();
13
14use ComboStrap\Analytics;
15use ComboStrap\FsWikiUtility;
16use ComboStrap\Page;
17use ComboStrap\Sqlite;
18use splitbrain\phpcli\Options;
19
20/**
21 * All dependency are loaded in plugin utility
22 */
23require_once(__DIR__ . '/ComboStrap/PluginUtility.php');
24
25/**
26 * The memory of the server 128 is not enough
27 */
28ini_set('memory_limit', '256M');
29
30/**
31 * Class cli_plugin_combo
32 *
33 * This is a cli:
34 * https://www.dokuwiki.org/devel:cli_plugins#example
35 *
36 * Usage:
37 *
38 * ```
39 * docker exec -ti $(CONTAINER) /bin/bash
40 * ./bin/plugin.php combo -c
41 * ```
42 * or via the IDE
43 *
44 *
45 * Example:
46 * https://www.dokuwiki.org/tips:grapher
47 *
48 */
49class cli_plugin_combo extends DokuWiki_CLI_Plugin
50{
51    const ANALYTICS = "analytics";
52    const SYNC = "sync";
53
54    /**
55     * register options and arguments
56     * @param Options $options
57     */
58    protected function setup(Options $options)
59    {
60        $options->setHelp(
61            "Manage the analytics database\n\n" .
62            "analytics\n" .
63            "sync"
64        );
65        $options->registerOption('version', 'print version', 'v');
66        $options->registerCommand(self::ANALYTICS, "Update the analytics data");
67        $options->registerOption(
68            'namespaces',
69            "If no namespace is given, the root namespace is assumed.",
70            'n',
71            true
72        );
73        $options->registerOption(
74            'output',
75            "Optional, where to store the analytical data as csv eg. a filename.",
76            'o', 'file');
77        $options->registerOption(
78            'cache',
79            "Optional, returns from the cache if set",
80            'c', false);
81        $options->registerOption(
82            'dry',
83            "Optional, dry-run",
84            'd', false);
85        $options->registerCommand(self::SYNC, "Sync the database");
86
87    }
88
89    /**
90     * The main entry
91     * @param Options $options
92     */
93    protected function main(Options $options)
94    {
95
96        $namespaces = array_map('cleanID', $options->getArgs());
97        if (!count($namespaces)) $namespaces = array(''); //import from top
98
99        $cache = $options->getOpt('cache', false);
100        $depth = $options->getOpt('depth', 0);
101        $cmd = $options->getCmd();
102        if ($cmd == "") {
103            $cmd = self::ANALYTICS;
104        }
105        switch ($cmd) {
106            case self::ANALYTICS:
107                $output = $options->getOpt('output', '');
108                //if ($output == '-') $output = 'php://stdout';
109                $this->updateAnalyticsData($namespaces, $output, $cache, $depth);
110                break;
111            case self::SYNC:
112                $this->syncPages();
113                break;
114            default:
115                throw new \RuntimeException("Combo: Command unknown (" . $cmd . ")");
116        }
117
118
119    }
120
121    /**
122     * @param array $namespaces
123     * @param $output
124     * @param bool $cache
125     * @param int $depth recursion depth. 0 for unlimited
126     */
127    private function updateAnalyticsData($namespaces = array(), $output = null, $cache = false, $depth = 0)
128    {
129
130        $fileHandle = null;
131        if (!empty($output)) {
132            $fileHandle = @fopen($output, 'w');
133            if (!$fileHandle) $this->fatal("Failed to open $output");
134        }
135
136        /**
137         * Run as admin to overcome the fact that
138         * anonymous user cannot see all links and backlinks
139         */
140        global $USERINFO;
141        $USERINFO['grps'] = array('admin');
142        global $INPUT;
143        $INPUT->server->set('REMOTE_USER', "cli");
144
145        $pages = FsWikiUtility::getPages($namespaces, $depth);
146
147
148        if (!empty($fileHandle)) {
149            $header = array(
150                'id',
151                'backlinks',
152                'broken_links',
153                'changes',
154                'chars',
155                'external_links',
156                'external_medias',
157                'h1',
158                'h2',
159                'h3',
160                'h4',
161                'h5',
162                'internal_links',
163                'internal_medias',
164                'words',
165                'score'
166            );
167            fwrite($fileHandle, implode(",", $header) . PHP_EOL);
168        }
169        $pageCounter = 0;
170        $totalNumberOfPages = sizeof($pages);
171        while ($page = array_shift($pages)) {
172            $id = $page['id'];
173
174            $pageCounter++;
175            echo "Processing the page {$id} ($pageCounter / $totalNumberOfPages)\n";
176
177            $data = Analytics::processAndGetDataAsArray($id, $cache);
178            if (!empty($fileHandle)) {
179                $statistics = $data[Analytics::STATISTICS];
180                $row = array(
181                    'id' => $id,
182                    'backlinks' => $statistics[Analytics::INTERNAL_BACKLINK_COUNT],
183                    'broken_links' => $statistics[Analytics::INTERNAL_LINK_BROKEN_COUNT],
184                    'changes' => $statistics[Analytics::EDITS_COUNT],
185                    'chars' => $statistics[Analytics::CHAR_COUNT],
186                    'external_links' => $statistics[Analytics::EXTERNAL_LINK_COUNT],
187                    'external_medias' => $statistics[Analytics::EXTERNAL_MEDIA_COUNT],
188                    Analytics::H1 => $statistics[Analytics::HEADING_COUNT][Analytics::H1],
189                    'h2' => $statistics[Analytics::HEADING_COUNT]['h2'],
190                    'h3' => $statistics[Analytics::HEADING_COUNT]['h3'],
191                    'h4' => $statistics[Analytics::HEADING_COUNT]['h4'],
192                    'h5' => $statistics[Analytics::HEADING_COUNT]['h5'],
193                    'internal_links' => $statistics[Analytics::INTERNAL_LINK_COUNT],
194                    'internal_medias' => $statistics[Analytics::INTERNAL_MEDIA_COUNT],
195                    'words' => $statistics[Analytics::WORD_COUNT],
196                    'low' => $data[Analytics::QUALITY]['low']
197                );
198                fwrite($fileHandle, implode(",", $row) . PHP_EOL);
199            }
200        }
201        if (!empty($fileHandle)) {
202            fclose($fileHandle);
203        }
204
205    }
206
207
208
209    private function syncPages()
210    {
211        $sqlite = Sqlite::getSqlite();
212        $res = $sqlite->query("select ID from pages");
213        if (!$res) {
214            throw new \RuntimeException("An exception has occurred with the alias selection query");
215        }
216        $res2arr = $sqlite->res2arr($res);
217        $sqlite->res_close($res);
218        foreach ($res2arr as $row) {
219            $id = $row['ID'];
220            if (!page_exists($id)) {
221                echo 'Page does not exist on the file system. Deleted from the database (' . $id . ")\n";
222                Page::createPageFromId($id)->deleteInDb();
223            }
224        }
225
226
227    }
228}
229