* */ if (!defined('DOKU_INC')) die(); use ComboStrap\Analytics; use ComboStrap\Page; use ComboStrap\PluginUtility; use ComboStrap\Sqlite; use splitbrain\phpcli\Options; require_once(__DIR__ . '/class/Analytics.php'); /** * The memory of the server 128 is not enough */ ini_set('memory_limit', '256M'); /** * Class cli_plugin_combo * * This is a cli: * https://www.dokuwiki.org/devel:cli_plugins#example * * Usage: * * ``` * docker exec -ti $(CONTAINER) /bin/bash * ./bin/plugin.php combo -c * ``` * or via the IDE * * * Example: * https://www.dokuwiki.org/tips:grapher * */ class cli_plugin_combo extends DokuWiki_CLI_Plugin { const ANALYTICS = "analytics"; const SYNC = "sync"; /** * register options and arguments * @param Options $options */ protected function setup(Options $options) { $options->setHelp( "Manage the analytics database\n\n" . "analytics\n" . "sync" ); $options->registerOption('version', 'print version', 'v'); $options->registerCommand(self::ANALYTICS, "Update the analytics data"); $options->registerOption( 'namespaces', "If no namespace is given, the root namespace is assumed.", 'n', true ); $options->registerOption( 'output', "Optional, where to store the analytical data as csv eg. a filename.", 'o', 'file'); $options->registerOption( 'cache', "Optional, returns from the cache if set", 'c', false); $options->registerOption( 'dry', "Optional, dry-run", 'd', false); $options->registerCommand(self::SYNC, "Sync the database"); } /** * The main entry * @param Options $options */ protected function main(Options $options) { $namespaces = array_map('cleanID', $options->getArgs()); if (!count($namespaces)) $namespaces = array(''); //import from top $cache = $options->getOpt('cache', false); $depth = $options->getOpt('depth', 0); $cmd = $options->getCmd(); if ($cmd == "") { $cmd = self::ANALYTICS; } switch ($cmd) { case self::ANALYTICS: $output = $options->getOpt('output', ''); //if ($output == '-') $output = 'php://stdout'; $this->updateAnalyticsData($namespaces, $output, $cache, $depth); break; case self::SYNC: $this->syncPages(); break; default: throw new \RuntimeException("Combo: Command unknown (" . $cmd . ")"); } } /** * @param array $namespaces * @param $output * @param bool $cache * @param int $depth recursion depth. 0 for unlimited */ private function updateAnalyticsData($namespaces = array(), $output = null, $cache = false, $depth = 0) { $fileHandle = null; if (!empty($output)) { $fileHandle = @fopen($output, 'w'); if (!$fileHandle) $this->fatal("Failed to open $output"); } $pages = $this->findPages($namespaces, $depth); if (!empty($fileHandle)) { $header = array( 'id', 'backlinks', 'broken_links', 'changes', 'chars', 'external_links', 'external_medias', 'h1', 'h2', 'h3', 'h4', 'h5', 'internal_links', 'internal_medias', 'words', 'score' ); fwrite($fileHandle, implode(",", $header) . PHP_EOL); } $pageCounter = 0; while ($page = array_shift($pages)) { $id = $page['id']; $pageCounter++; echo "Processing the page {$id} ($pageCounter)\n"; $data = Analytics::processAndGetDataAsArray($id, $cache); if (!empty($fileHandle)) { $statistics = $data[Analytics::STATISTICS]; $row = array( 'id' => $id, 'backlinks' => $statistics[Analytics::INTERNAL_BACKLINKS_COUNT], 'broken_links' => $statistics[Analytics::INTERNAL_LINKS_BROKEN_COUNT], 'changes' => $statistics[Analytics::EDITS_COUNT], 'chars' => $statistics[Analytics::CHARS_COUNT], 'external_links' => $statistics[Analytics::EXTERNAL_LINKS_COUNT], 'external_medias' => $statistics[Analytics::EXTERNAL_MEDIAS], 'h1' => $statistics[Analytics::HEADERS_COUNT]['h1'], 'h2' => $statistics[Analytics::HEADERS_COUNT]['h2'], 'h3' => $statistics[Analytics::HEADERS_COUNT]['h3'], 'h4' => $statistics[Analytics::HEADERS_COUNT]['h4'], 'h5' => $statistics[Analytics::HEADERS_COUNT]['h5'], 'internal_links' => $statistics[Analytics::INTERNAL_LINKS_COUNT], 'internal_medias' => $statistics[Analytics::INTERNAL_MEDIAS_COUNT], 'words' => $statistics[Analytics::WORDS_COUNT], 'low' => $data[Analytics::QUALITY]['low'] ); fwrite($fileHandle, implode(",", $row) . PHP_EOL); } } if (!empty($fileHandle)) { fclose($fileHandle); } } /** * Find the pages in the tree * @param $namespaces * @param $depth * @return array */ private function findPages($namespaces = array(), $depth = 0) { // Run as admin to overcome the fact that // anonymous user cannot set all links and backlinnks global $conf; $datadir = $conf['datadir']; /** * Run as admin to overcome the fact that * anonymous user cannot see all links and backlinnks */ global $USERINFO; $USERINFO['grps'] = array('admin'); global $INPUT; $INPUT->server->set('REMOTE_USER', "cli"); $pages = array(); foreach ($namespaces as $ns) { search( $pages, $datadir, 'search_universal', array( 'depth' => $depth, 'listfiles' => true, 'listdirs' => false, 'pagesonly' => true, 'skipacl' => true, 'firsthead' => false, 'meta' => false, ), str_replace(':', '/', $ns) ); // add the ns start page if ($ns && page_exists($ns)) { $pages[] = array( 'id' => $ns, 'ns' => getNS($ns), 'title' => p_get_first_heading($ns, false), 'size' => filesize(wikiFN($ns)), 'mtime' => filemtime(wikiFN($ns)), 'perm' => 16, 'type' => 'f', 'level' => 0, 'open' => 1, ); } } return $pages; } private function syncPages() { $sqlite = Sqlite::getSqlite(); $res = $sqlite->query("select ID from pages"); if (!$res) { throw new \RuntimeException("An exception has occurred with the alias selection query"); } $res2arr = $sqlite->res2arr($res); $sqlite->res_close($res); foreach ($res2arr as $row) { $id = $row['ID']; if (!page_exists($id)) { echo 'Page does not exist on the file system. Deleted from the database (' . $id . ")\n"; Page::createPageFromPathId($id)->deleteInDb(); } } } }