<?php
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* Used to create and manipulate a profile and work directory from the
* command-line for Yioop.
*
* @author Chris Pollett chris@pollett.org
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*/
namespace seekquarry\yioop\configs;
use seekquarry\yioop\configs as C;
use seekquarry\yioop\library as L;
use seekquarry\yioop\library\CrawlConstants;
use seekquarry\yioop\library\wiki\WikiPagesFile;
use seekquarry\yioop\controllers\AdminController;
use seekquarry\yioop\models\GroupModel;
use seekquarry\yioop\models\LocaleModel;
use seekquarry\yioop\models\Model;
use seekquarry\yioop\models\ProfileModel;
use seekquarry\yioop\models\ScraperModel;
use seekquarry\yioop\models\WikiModel;
use seekquarry\yioop\models\datasources\PdoManager;
if (php_sapi_name() != 'cli' ||
defined("seekquarry\\yioop\\configs\\IS_OWN_WEB_SERVER")) {
echo "BAD REQUEST"; exit();
}
/** Loads common utility functions*/
require_once __DIR__."/../library/Utility.php";
/** Loads common constants for web crawling*/
require_once __DIR__."/../library/LocaleFunctions.php";
mb_internal_encoding("UTF-8");
mb_regex_encoding("UTF-8");
/**
* shorthand for echo
*
* @param string $text string to send to the current output
*/
function e($text)
{
echo $text;
}
$locale_tag = L\guessLocale();
$locale = null;
L\setLocaleObject($locale_tag);
/**
* Provides a command-line interface way to configure a Yioop Instance.
* Unlike the web interface this interface is English-only. Besides the
* settings, it makes a fresh pair of databases (createdb), writes the
* Public and Help group wiki pages to PublicHelpPages.php
* (export-public-help), and puts that file's pages back into the wiki
* of a running site (import-public-help).
*/
class ConfigureTool
{
/**
* PUBLIC_HELP_FILE is the name of the file of public and help wiki
* pages, in src/configs as Yioop ships it and in the work directory's
* configs folder once export-public-help has written one
*/
const PUBLIC_HELP_FILE = "PublicHelpPages.php";
/**
* Used to hold an AdminController object used to manipulate the
* Yioop configuration
* @var object
*/
public $admin;
/**
* $export_settings holds what an export of the Public and Help wiki
* pages takes besides the pages, each on or off: ISSUE_PAGES, the
* pages a git repository page keeps its issues, waiting reports and
* bans in; COMMENTS, the posts of each exported page's thread, which
* are the comments on an issue, the comments a page with comments
* shows, and a page's discussion; and HISTORY, every earlier revision
* of each exported page with who saved it. PUBLIC and HELP say which
* of the two groups' pages are exported. All start on, so an export
* imported on another site makes the same pages. The Export screen's
* toggles and the +/-public, +/-help, +/-issues, +/-comments and
* +/-history arguments of export-public-help change them for one run
* of the tool. A kind or a group left out of an export is also left
* alone by an import of it, even one that deletes first.
* @var array
*/
public $export_settings = ["PUBLIC" => true, "HELP" => true,
"ISSUE_PAGES" => true, "COMMENTS" => true, "HISTORY" => true];
/**
* Holds the main menu data for the configuration tool
* @var array
*/
public $menu = ["workDirectory" => "Create/Set Work Directory",
"rootPassword" => "Change root password",
"localeMenu" => "Locales: Set the Default, Upgrade",
"debugDisplay"=> "Debug Display Set-up",
"searchAccess"=> "Search Access Set-up",
"searchPageElementLinks" => "Search Page Elements and Links",
"nameServer" => "Name Server Set-up",
"robotSetUp"=> "Crawl Robot Set-up",
"createDatabase" => "Create Databases (Erases All Site Data)",
"wikiPagesMenu" => "Public and Help Wiki Pages: Export, Import",
"migratePreV10Resources" =>
"Migrate Pre-Version 10 Resource Folders",
"quit" => "Exit program"
];
/**
* Holds the items of the Locales screen, which the main menu's Locales
* item opens
* @var array
*/
public $locale_menu = ["defaultLocale"=> "Set Default Locale",
"upgradeLocales" => "Upgrade Locales",
"upgradeLocalesOverwrite" =>
"Upgrade Locales, Replacing Wiki Pages Edited on This Site",
"configureMenu" => "Return to Main Menu"];
/**
* Holds the items of the Public and Help Wiki Pages screen, which the
* main menu's item of that name opens
* @var array
*/
public $wiki_pages_menu = [
"exportMenu" => "Export Public and Help Wiki Pages",
"importPublicHelp" => "Import Public and Help Wiki Pages",
"importPublicHelpWipe" => "Import Public and Help Wiki Pages, " .
"Deleting Their Existing Pages First",
"configureMenu" => "Return to Main Menu"];
/**
* To change configuration parameters of Yioop, this program
* invokes AdminController methods. These methods expect, data
* passed to them in super globals set up as a result of an HTTP
* request. This program fakes the settings of these variables.
* To keep things simple this constructor initializes each of the
* relevant super globals to be empty arrays.
*/
function __construct()
{
$_REQUEST = [];
$_POST = [];
$_GET = [];
$_SERVER = [];
$_SESSION = [];
$this->admin = new AdminController();
}
/**
* This is the main loop where options of what the user can configure
* are presented, a choice is requested, and so on...
*/
function loop()
{
$done = false;
$activities = array_merge(array_keys($this->menu),
array_keys($this->locale_menu),
array_keys($this->wiki_pages_menu));
$activities[] = "configureMenu";
$state = "configureMenu";
while($state != "quit") {
if ($state == "upgradeLocalesOverwrite") {
$state = $this->upgradeLocales(true);
} else if (in_array($state, ["importPublicHelp",
"importPublicHelpWipe", "migratePreV10Resources"])) {
$_REQUEST["MESSAGE"] = $this->$state();
$state = "configureMenu";
} else if (in_array($state, $activities) ) {
$state = $this->$state();
}
}
}
/**
* This is used to draw the main configuration menu and ask for a
* user selection
* @return string name of the next menu state (e.g.
* "workDirectory", "quit") chosen by the user
*/
function configureMenu()
{
$this->banner();
$data = $this->callConfigure();
e("Checking Yioop configuration...".
"\n===============================\n");
$check_status = str_replace("<br>", "\n", $data["SYSTEM_CHECK"]);
e($check_status."\n===============================\n");
$items = ["workDirectory" => "Create/Set Work Directory",
"quit" => "Exit program"];
if ($data["PROFILE"]) {
$items = $this->menu;
}
return $this->drawChooseItems($items, "configureMenu");
}
/**
* localeMenu draws the Locales screen, which the main menu's Locales
* item opens, and asks which of its steps to take: setting the default
* locale or upgrading the locales. Keeping these on a screen of their
* own keeps the main menu short.
*
* @return string name of the next menu state, one of the screen's
* steps or configureMenu
*/
function localeMenu()
{
$this->banner();
e("LOCALES\n=======\n");
return $this->drawChooseItems($this->locale_menu, "localeMenu");
}
/**
* wikiPagesMenu draws the Public and Help Wiki Pages screen, which the
* main menu's item of that name opens, and asks which of its steps to
* take: exporting the pages, importing them, or importing them after
* deleting the existing ones. Keeping these on a screen of their own
* keeps the main menu short.
*
* @return string name of the next menu state, one of the screen's
* steps or configureMenu
*/
function wikiPagesMenu()
{
$this->banner();
e("PUBLIC AND HELP WIKI PAGES\n==========================\n");
return $this->drawChooseItems($this->wiki_pages_menu,
"wikiPagesMenu");
}
/**
* exportMenu draws the Export screen, which the Public and Help Wiki
* Pages screen's Export item opens. It shows whether the export takes
* the Public group's pages, the Help group's pages, git issue pages,
* the comments on the exported pages and their history, lets each be
* toggled, and exports with those settings when
* asked. The settings last for this run of the tool.
*
* @return string name of the next menu state: exportMenu again after
* a toggle, configureMenu with the export's message after
* exporting, or wikiPagesMenu to go back
*/
function exportMenu()
{
$this->banner();
e("EXPORT PUBLIC AND HELP WIKI PAGES\n" .
"=================================\n");
$settings = ["PUBLIC" => "Public Group Pages",
"HELP" => "Help Group Pages",
"ISSUE_PAGES" => "Git Issue Pages",
"COMMENTS" => "Comments on Exported Pages",
"HISTORY" => "Page History"];
$items = [];
foreach ($settings as $setting => $setting_string) {
$toggle = ($this->export_settings[$setting]) ? "On" : "Off";
e("$setting_string: [$toggle]\n");
$items[$setting] = "Toggle $setting_string";
}
$items["exportPublicHelp"] = "Export Now";
$items["wikiPagesMenu"] = "Return to Public and Help Wiki Pages";
do {
$choice = $this->drawChooseItems($items, "exportMenu");
} while ($choice == "exportMenu");
if (isset($settings[$choice])) {
$this->export_settings[$choice] =
!$this->export_settings[$choice];
return "exportMenu";
}
if ($choice == "exportPublicHelp") {
$_REQUEST["MESSAGE"] = $this->exportPublicHelp();
return "configureMenu";
}
return $choice;
}
/**
* Used to create/change the location of this Yioop instances work
* directory
* @return string name of the next menu state to dispatch to after
* the work-directory step
*/
function workDirectory()
{
$this->banner();
$data = $this->callConfigure();
$directory = (isset($data["WORK_DIRECTORY"]) &&
$data["WORK_DIRECTORY"] != "") ? $data["WORK_DIRECTORY"]
: "";
/* The path an empty entry will use: the directory already in
effect when there is one, otherwise the default location
next to the Yioop folder. */
$default_directory = ($directory != "") ? $directory :
DEFAULT_WORK_DIRECTORY;
$is_initialized = file_exists($default_directory .
PROFILE_FILE_NAME);
if ($directory != "") {
e("CURRENT WORK DIRECTORY: $directory\n\n");
} else {
e("CURRENT WORK DIRECTORY: none set yet\n\n");
}
if (!$is_initialized) {
e("This work directory has not been initialized yet ".
"(no profile found at\n$default_directory).\n\n");
}
e("Enter a new value, or just press return to ".
"create/reinitialize the\ncurrent work directory at ".
"$default_directory:\n");
if (!isset($_SERVER['REQUEST_URI'])) {
$_SERVER['REQUEST_URI'] = "";
}
$this->prepareGlobals($data);
$entered = trim(L\readInput());
if ($entered == "") {
$entered = $default_directory;
}
$_REQUEST["WORK_DIRECTORY"] = $entered;
$_REQUEST["arg"] = "directory";
$next_menu = $this->confirmChange("configure", "workDirectory");
return $next_menu;
}
/**
* Used to change the password of the root account of this Yioop Instance
* @return string name of the next menu state to dispatch to after
* the root-password step
*/
function rootPassword()
{
$this->banner();
$data = $this->callConfigure();
if ($data["PROFILE"] != 1) {
$_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
return "configureMenu";
}
e("Enter old password:");
$_REQUEST["password"] = L\readPassword();
e("Enter new password:");
$_REQUEST["new_password"] = L\readPassword();
e("Re-Enter new password:");
$_REQUEST["retype_password"] = L\readPassword();
$_SESSION['USER_ID'] = ROOT_ID;
$_REQUEST['arg'] = "updateuser";
$_REQUEST['edit_pass'] = "true";
$next_menu = $this->confirmChange("manageAccount", "rootPassword");
return $next_menu;
}
/**
* Changes the default locale (language) used by Yioop when it cannot
* determine that information from the user's browser
* @return string name of the next menu state to dispatch to after
* the default-locale step
*/
function defaultLocale()
{
$this->banner();
$data = $this->callConfigure();
if ($data["PROFILE"] != 1) {
$_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
return "configureMenu";
}
e("CURRENT LANGUAGE: ".$data["LANGUAGES"][
$data["DEFAULT_LOCALE"]]."\n\n");
$_SESSION = [];
$items = $data["LANGUAGES"];
$items["configureMenu"] = "Return to Main Menu";
do {
$choice = $this->drawChooseItems($items, "defaultLocale");
} while( $choice == "defaultLocale");
$this->prepareGlobals($data);
if ($choice == "configureMenu") {
$_REQUEST = [];
$_SERVER = [];
return "configureMenu";
}
$_REQUEST["DEFAULT_LOCALE"] = $choice;
return "defaultLocale";
}
/**
* Used to configure debugging information for this Yioop instance.
* i.e., whether PHP notices, warnings, errors, should be displayed,
* whether query statistics and info should be displayed, and whether
* unit tests should be viewable from the web
* @return string name of the next menu state to dispatch to after
* the debug-display step
*/
function debugDisplay()
{
$this->banner();
$data = $this->callConfigure();
if ($data["PROFILE"] != 1) {
$_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
return "configureMenu";
}
e("CURRENT DEBUG SETTINGS\n======================\n");
$dlevel = $data["DEBUG_LEVEL"];
$setting = ($dlevel & ERROR_INFO) ? "On" : "Off";
e("Error Info: [$setting]\n");
$setting = ($dlevel & QUERY_INFO) ? "On" : "Off";
e("Query Info: [$setting]\n");
$setting = ($dlevel & TEST_INFO) ? "On" : "Off";
e("Test Info: [$setting]\n");
$items = ["ERROR_INFO" => "Toggle Error Info",
"QUERY_INFO" => "Toggle Query Info",
"TEST_INFO" => "Toggle Test Info",
"configureMenu" => "Return to Main Menu"];
do {
$choice = $this->drawChooseItems($items, "debugDisplay");
} while( $choice == "debugDisplay");
$this->prepareGlobals($data);
if ($choice == "configureMenu") {
$_REQUEST = [];
$_SERVER = [];
return "configureMenu";
}
$flag = constant(NS_CONFIGS . $choice);
$dlevel = ($dlevel & $flag) ? $dlevel - $flag : $dlevel + $flag;
if ($dlevel & ERROR_INFO) {$_REQUEST["ERROR_INFO"] = "true";}
if ($dlevel & QUERY_INFO) {$_REQUEST["QUERY_INFO"] = "true";}
if ($dlevel & TEST_INFO) {$_REQUEST["TEST_INFO"] = "true";}
return "debugDisplay";
}
/**
* Configures which methods are allowed by this Yioop instance to access
* search results, (via the web, via open rss search results, via the
* API)
* @return string name of the next menu state to dispatch to after
* the search-access step
*/
function searchAccess()
{
$this->banner();
$data = $this->callConfigure();
if ($data["PROFILE"] != 1) {
$_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
return "configureMenu";
}
e("CURRENT SEARCH ACCESS SETTINGS\n==============================\n");
$settings = ["WEB_ACCESS" => "Web",
"RSS_ACCESS" => "RSS", "API_ACCESS" => "API"];
$items = [];
foreach ($settings as $setting => $setting_string) {
$toggle = ($data[$setting]) ? "On" : "Off";
e("$setting_string: [$toggle]\n");
$items[$setting] = "Toggle $setting_string";
}
$items["configureMenu"] = "Return to Main Menu";
do {
$choice = $this->drawChooseItems($items, "searchAccess");
} while( $choice == "searchAccess");
$this->prepareGlobals($data);
if ($choice == "configureMenu") {
$_REQUEST = [];
$_SERVER = [];
return "configureMenu";
}
$_REQUEST[$choice] = ($data[$choice]) ? false : true;
return "searchAccess";
}
/**
* Configures which of the various links of the SERPS page such as
* Cache, etc should be displayed. Also, configures whether the signin
* links, etc should be displayed.
* @return string name of the next menu state to dispatch to after
* the search-page-elements step
*/
function searchPageElementLinks()
{
$this->banner();
$data = $this->callConfigure();
if ($data["PROFILE"] != 1) {
$_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
return "configureMenu";
}
e("CURRENT SEARCH PAGE ELEMENTS AND LINKS SETTINGS".
"\n===================================================\n");
$settings = ["WORD_SUGGEST" => "Word Suggest",
"SUBSEARCH_LINK" => "Subsearch Links",
"SIGNIN_LINK" => "Sign-in Links",
"MORE_RESULT" => "More Result Info",
"CACHE_LINK" => "Cache Link",
"SIMILAR_LINK" => "Similar Link", "IN_LINK" => "Inlinks",
"IP_LINK"=> "IP Links"];
$items = [];
foreach ($settings as $setting => $setting_string) {
$toggle = ($data[$setting]) ? "On" : "Off";
e("$setting_string: [$toggle]\n");
$items[$setting] = "Toggle $setting_string";
}
$items["configureMenu"] = "Return to Main Menu";
do {
$choice = $this->drawChooseItems($items, "searchPageElementLinks");
} while( $choice == "searchPageElementLinks");
$this->prepareGlobals($data);
if ($choice == "configureMenu") {
$_REQUEST = [];
$_SERVER = [];
return "configureMenu";
}
$_REQUEST[$choice] = ($data[$choice]) ? false : true;
return "searchPageElementLinks";
}
/**
* Configures settings relating to the location of the name server and
* the salt used when communicating with it. Also, configures caching
* mechanisms the name server should use when returning results.
* @return string name of the next menu state to dispatch to after
* the name-server step
*/
function nameServer()
{
$this->banner();
$data = $this->callConfigure();
if ($data["PROFILE"] != 1) {
$_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
return "configureMenu";
}
e("NAME SERVER SETTINGS\n====================\n");
e("Server Key: [".$data["AUTH_KEY"]."]\n");
e("Name Server URL: [".$data["NAME_SERVER"]."]\n");
$settings = ["USE_FILECACHE" => "Use File Cache"];
$items = ["serverKey" => "Edit Server Key",
"nameServer" => "Edit Name Server Url"];
foreach ($settings as $setting => $setting_string) {
$toggle = ($data[$setting]) ? "On" : "Off";
e("$setting_string: [$toggle]\n");
$items[$setting] = "Toggle $setting_string";
}
$items["configureMenu"] = "Return to Main Menu";
do {
$choice = $this->drawChooseItems($items, "nameServerMenu");
} while( $choice == "nameServerMenu");
$this->prepareGlobals($data);
switch ($choice) {
case "configureMenu":
$_REQUEST = [];
$_SERVER = [];
return "configureMenu";
break;
case "serverKey":
e("Enter a new server key: ");
$_REQUEST["AUTH_KEY"] = L\readInput();
break;
case "nameServer":
e("Enter a new name server url: ");
$_REQUEST["NAME_SERVER"] = L\readInput();
break;
default:
$_REQUEST[$choice] = ($data[$choice]) ? false : true;
}
return "nameServer";
}
/**
* Used to set up the name of this instance of the Yioop robot as well
* as its description page.
* @return string name of the next menu state to dispatch to after
* the robot-set-up step
*/
function robotSetUp()
{
$this->banner();
$data = $this->callConfigure();
if ($data["PROFILE"] != 1) {
$_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
return "configureMenu";
}
e("CRAWL ROBOT SETTINGS\n====================\n");
e("Crawl Robot Name: [".$data["USER_AGENT_SHORT"]."]\n");
e("Robot Instance: [".$data["ROBOT_INSTANCE"]."]\n");
e("\nRobot Description:\n=================\n".
$data["ROBOT_DESCRIPTION"] . "\n=================\n");
$items = ["robotName" => "Edit Robot Name",
"robotInstance" => "Edit Robot Instance",
"robotDescription" => "Edit Robot Description",
"configureMenu" => "Return to Main Menu"];
do {
$choice = $this->drawChooseItems($items, "robotSetUp");
} while( $choice == "robotSetUp");
$this->prepareGlobals($data);
switch ($choice) {
case "configureMenu":
$_REQUEST = [];
$_SERVER = [];
return "configureMenu";
break;
case "robotName":
e("Enter a new robot name: ");
$_REQUEST["USER_AGENT_SHORT"] = L\readInput();
break;
case "robotInstance":
e("Enter a new robot instance value: ");
$_REQUEST["ROBOT_INSTANCE"] = L\readInput();
break;
case "robotDescription":
e("Enter a description of your web crawler robot.\n".
"Terminate input with a line with only '.' on it:\n");
$_REQUEST["ROBOT_DESCRIPTION"] = L\readMessage();
break;
}
return "robotSetUp";
}
/**
* Used to select to confirm, cancel, or re-enter the last profile
* change
*
* @param string $admin_method to call if confirmed
* @param string $reenter_method , return value if reenter chosen
* @return string menu name to do to next
*/
function confirmChange($admin_method, $reenter_method)
{
$component_activities = AdminController::$component_activities;
$items = ["confirm" => "Confirm Change",
"reenter" => "Re-enter the information",
"configureMenu" => "Return to the Configure Menu"];
$first = true;
do {
$choice = $this->drawChooseItems($items, "confirmChange");
} while( $choice == "confirmChange");
switch ($choice) {
case "confirm":
$component = "system";
foreach ($component_activities as $available_component =>
$activities) {
if (in_array($admin_method, $activities)) {
$component = $available_component;
break;
}
}
$data = $this->admin->component($component)->$admin_method();
$_SERVER = [];
$_SESSION = [];
$_REQUEST = [];
$_REQUEST["MESSAGE"] = $data["MESSAGE"];
$next_menu = "configureMenu";
break;
case "reenter":
$_SERVER = [];
$_SESSION = [];
$_REQUEST = [];
$next_menu = $reenter_method;
break;
default:
$_SERVER = [];
$_SESSION = [];
$_REQUEST = [];
$next_menu = "configureMenu";
}
return $next_menu;
}
/**
* Interactive menu step, and the action behind the upgrade-locales
* command, that brings the work directory's app/locale up to the
* source tree and force-pushes the wiki and help pages, regardless of
* the stored resources-wiki version. app/locale keeps only what the
* site changed: a resources folder whose every file copies a shipped
* one is deleted, a resources folder holding a changed file is left as
* it is, and each configure.ini keeps only strings that differ from the
* shipped ones. Nothing in the source tree is written. This lets a
* developer see edited help pages during ongoing work without bumping
* the resources-wiki version on every change. The outcome is reported
* through the usual menu message so both the menu and the command
* surface the same line.
*
* A wiki or help page edited on this site is kept unless
* $overwrite_edits is true.
*
* @param bool $overwrite_edits whether shipped wiki and help pages
* also replace pages edited on this site
* @return string name of the next menu state, the main menu
*/
function upgradeLocales($overwrite_edits = false)
{
require_once __DIR__ . "/../library/UpgradeFunctions.php";
L\upgradeLocales(true, $overwrite_edits);
$_REQUEST["MESSAGE"] = "The work directory's locales now keep " .
"only this site's changes, and wiki resources were " .
"force-pushed from the source tree" .
(($overwrite_edits) ? ", replacing pages edited on this site." :
", keeping pages edited on this site.");
return "configureMenu";
}
/**
* Runs one configuration command from values given on the command
* line, with no prompting. It sets up the request the way the
* matching interactive step would, fills in the answer that step
* would ask for from the supplied values, and saves the change.
* The work-directory command is allowed to run on a fresh install;
* every other command first needs a work directory to exist, and
* prints a message and stops when one does not.
*
* @param string $command the command name the user typed
* @param array $spec the command's specification (its activity and
* argument rules)
* @param array $values the arguments the user gave after the
* command name
*/
function runActivityFromArgs($command, $spec, $values)
{
$data = $this->callConfigure();
if ($command != "work-dir" &&
(!isset($data["PROFILE"]) || $data["PROFILE"] != 1)) {
e("A work directory must be set up first. Run:\n");
e(" php ConfigureTool.php work-dir <path>\n");
return;
}
if ($command == "work-dir") {
$directory = (isset($data["WORK_DIRECTORY"]) &&
$data["WORK_DIRECTORY"] != "") ?
$data["WORK_DIRECTORY"] : DEFAULT_WORK_DIRECTORY;
$entered = (count($values) == 1 && trim($values[0]) != "") ?
trim($values[0]) : $directory;
$_REQUEST["WORK_DIRECTORY"] = $entered;
$_REQUEST["arg"] = "directory";
$this->commitActivity("configure", $data);
return;
}
if ($command == "root-password") {
$_REQUEST["password"] = $values[0];
$_REQUEST["new_password"] = $values[1];
$_REQUEST["retype_password"] = $values[1];
$_SESSION['USER_ID'] = ROOT_ID;
$_REQUEST['arg'] = "updateuser";
$_REQUEST['edit_pass'] = "true";
$this->commitActivity("manageAccount", $data);
return;
}
if ($command == "locale") {
$this->prepareGlobals($data);
$_REQUEST["DEFAULT_LOCALE"] = $values[0];
$this->commitActivity("configure", $data);
return;
}
if ($command == "name-server") {
$this->prepareGlobals($data);
$_REQUEST["AUTH_KEY"] = $values[0];
$_REQUEST["NAME_SERVER"] = $values[1];
$this->commitActivity("configure", $data);
return;
}
if ($command == "robot") {
$this->prepareGlobals($data);
$_REQUEST["USER_AGENT_SHORT"] = $values[0];
$_REQUEST["ROBOT_INSTANCE"] = $values[1];
$_REQUEST["ROBOT_DESCRIPTION"] = $values[2];
$this->commitActivity("configure", $data);
return;
}
if ($command == "createdb") {
$this->createDatabases();
return;
}
if ($command == "migrate-pre-v10-resources") {
e($this->migratePreV10Resources() . "\n");
return;
}
if ($command == "export-public-help") {
$export_names = ["public" => "PUBLIC", "help" => "HELP",
"issues" => "ISSUE_PAGES",
"comments" => "COMMENTS", "history" => "HISTORY"];
foreach ($values as $value) {
$sign = substr($value, 0, 1);
$short_name = substr($value, 1);
if (($sign != "+" && $sign != "-") ||
!isset($export_names[$short_name])) {
e("Bad option: $value\n\n");
configureToolUsage();
return;
}
$this->export_settings[$export_names[$short_name]] =
($sign == "+");
}
e($this->exportPublicHelp() . "\n");
return;
}
if ($command == "import-public-help") {
if (!empty($values) && $values[0] != "wipe") {
e("import-public-help takes only the word wipe\n");
return;
}
e($this->importPublicHelp(!empty($values)) . "\n");
return;
}
if ($command == "upgrade-locales") {
if (!empty($values) && $values[0] != "overwrite") {
e("upgrade-locales takes only the word overwrite\n");
return;
}
$this->upgradeLocales(!empty($values));
e($_REQUEST["MESSAGE"] . "\n");
return;
}
/* The remaining commands turn named settings on with +name or
off with -name, leaving anything not listed unchanged. The
name-to-request-key maps below match the labels each
interactive step shows. */
$toggle_settings = [
"debug" => ["error" => "ERROR_INFO",
"query" => "QUERY_INFO", "test" => "TEST_INFO"],
"search-access" => ["web" => "WEB_ACCESS",
"rss" => "RSS_ACCESS", "api" => "API_ACCESS"],
"page-elements" => ["word-suggest" => "WORD_SUGGEST",
"subsearch" => "SUBSEARCH_LINK",
"signin" => "SIGNIN_LINK", "result-info" => "MORE_RESULT",
"cache" => "CACHE_LINK", "similar" => "SIMILAR_LINK",
"inlink" => "IN_LINK", "ip" => "IP_LINK"],
];
$this->runToggleFromArgs($command, $toggle_settings[$command],
$values, $data);
}
/**
* Saves a setting change that turns named on/off options on with
* +name or off with -name. The starting point is the current
* setting, so anything the user does not name keeps its present
* value. An unrecognized name prints the usage help and stops.
*
* @param string $command the command name the user typed
* @param array $setting_names maps each accepted short name to the
* request key that stores its setting
* @param array $values the +name / -name arguments the user gave
* @param array $data the current profile state
*/
function runToggleFromArgs($command, $setting_names, $values, $data)
{
$this->prepareGlobals($data);
/* start every setting at its current value so unnamed ones do
not change. */
foreach ($setting_names as $short_name => $request_key) {
if (!empty($data[$request_key])) {
$_REQUEST[$request_key] = "true";
} else {
unset($_REQUEST[$request_key]);
}
}
foreach ($values as $value) {
$sign = substr($value, 0, 1);
$short_name = substr($value, 1);
if (($sign != "+" && $sign != "-") ||
!isset($setting_names[$short_name])) {
e("Bad option: $value\n\n");
configureToolUsage();
return;
}
$request_key = $setting_names[$short_name];
if ($sign == "+") {
$_REQUEST[$request_key] = "true";
} else {
unset($_REQUEST[$request_key]);
}
}
$this->commitActivity("configure", $data);
}
/**
* Saves the change set up in the request by calling the admin
* activity that handles it, the same one the interactive Confirm
* choice would call. Prints the result message the activity
* returns.
*
* @param string $admin_method the admin activity to run, such as
* configure or manageAccount
* @param array $data the current profile state, used to find which
* part of the admin controller owns the activity
*/
function commitActivity($admin_method, $data)
{
$component_activities = AdminController::$component_activities;
$component = "system";
foreach ($component_activities as $available_component =>
$activities) {
if (in_array($admin_method, $activities)) {
$component = $available_component;
break;
}
}
$result = $this->admin->component($component)->$admin_method();
if (!empty($result["MESSAGE"])) {
e($result["MESSAGE"] . "\n");
}
}
/**
* Draws a list of options to the screen and gets a choice
* from this list from the user.
*
* @param array $items as associative array (return value => description)
* @param string $currentView value to return if invalid choice made
* @return string a choice from the user
*/
function drawChooseItems($items, $currentView)
{
$choice_nums = [];
$i = 1;
e("\nAvailable Options:\n==================\n");
foreach ($items as $name => $description) {
e("($i) $description\n");
$choice_nums[$i] = $name;
$i++;
}
if (!empty($_REQUEST["MESSAGE"])) {
e("\n+++ ".$_REQUEST["MESSAGE"]." +++\n");
unset($_REQUEST["MESSAGE"]);
}
e("\nPlease choose an option:\n");
$user_data = strtolower(trim(L\readInput($ended)));
if ($ended) {
e("\nThe input ended, so the configuration tool stops.\n");
exit();
}
if ($user_data >= 1 && $user_data < $i) {
$_REQUEST["MESSAGE"] = "";
return $choice_nums[$user_data];
} else {
$_REQUEST["MESSAGE"] = "Invalid choice. Please choose again.";
return $currentView;
}
}
/**
* Used to call system components configure method. It detects if
* a redirect happened by the fact that $data['PROFILE'] is not set.
* If so it passes along the redirect message and re-calls configure()
* @return array \$data array as returned by the Admin
* controller's configure activity (system check, current
* profile, locale fields)
*/
function callConfigure()
{
$data = $this->admin->component("system")->configure();
if (!isset($data["PROFILE"])) {
$_REQUEST = [];
$message = (isset($data['MESSAGE'])) ? $data['MESSAGE'] : "";
$data = $this->admin->component("system")->configure();
$data['MESSAGE'] = $message;
}
return $data;
}
/**
* Prints the banner used by this configuration tool
*/
function banner()
{
e(chr(27) . "[2J" . chr(27) . "[;H");
e("\n\nYIOOP! CONFIGURATION TOOL\n");
e("+++++++++++++++++++++++++\n\n");
}
/**
* Sets-up the field values of the super globals used by AdminController
* when changing a profile or managing passwords. These particular
* values don't change with respect to what this tool does.
*
* @param array $data current profile state
*/
function prepareGlobals($data)
{
$_SESSION = [];
$_REQUEST = $this->copyProfileFields($data);
$_REQUEST["arg"] = "profile";
$_REQUEST['YIOOP_TOKEN'] = "";
if (!isset($_SERVER['REQUEST_URI'])) {
if (!empty($data['WEB_URI'])) {
$_SERVER['REQUEST_URI'] = $data['WEB_URI'];
} else {
e("Enter web path for Yioop instance:\n");
$_SERVER['REQUEST_URI'] = L\readInput();
}
}
}
/**
* Used to copy the contents of $data which are profile fields to a
* new array.
*
* @param array $data an array of profile and other fields
* @return array a new array containing a copy of just the profile fields
* from the original array
*/
function copyProfileFields($data)
{
$profile = [];
foreach ($this->admin->model("profile")->profile_fields as $field) {
if (isset($data[$field])) {
$profile[$field] = $data[$field];
}
}
return $profile;
}
/**
* DEFAULT_LOCALES lists the locales a new database starts with, each
* as its locale tag, its name in its own language and its writing
* mode: rl-tb for right to left, lr-tb for left to right
*/
const DEFAULT_LOCALES = [
['ar', 'العربية', 'rl-tb'],
['bn', 'বাংলা', 'lr-tb', ''],
['de', 'Deutsch', 'lr-tb'],
['el-GR', 'Ελληνικά', 'lr-tb'],
['en-US', 'English', 'lr-tb'],
['es', 'Español', 'lr-tb'],
['fa', 'فارسی', 'rl-tb'],
['fr-FR', 'Français', 'lr-tb'],
['he', 'עברית', 'rl-tb'],
['hi', 'हिन्दी', 'lr-tb'],
['id', 'Bahasa', 'lr-tb'],
['it', 'Italiano', 'lr-tb'],
['ja', '日本語', 'lr-tb'],
['kn', 'ಕನ್ನಡ', 'lr-tb'],
['ko', '한국어', 'lr-tb'],
['nl', 'Nederlands', 'lr-tb'],
['pl', 'Polski', 'lr-tb'],
['pt', 'Português', 'lr-tb'],
['ru', 'Русский', 'lr-tb'],
['te', 'తెలుగు', 'lr-tb'],
['th', 'ไทย', 'lr-tb'],
['tl', 'Tagalog', 'lr-tb'],
['tr', 'Türkçe', 'lr-tb'],
['vi-VN', 'Tiếng Việt', 'lr-tb'],
['zh-CN', '中文', 'lr-tb'],
];
/**
* DEFAULT_ACTIVITIES lists the activities of the admin panel a new
* database starts with, each by its method name, as its translation
* identifier and its name in each locale
*/
const DEFAULT_ACTIVITIES = [
"manageAccount" => ['db_activity_manage_account',
[
"ar" => 'إدارة الحساب',
"bn" => 'অ্যাকাউন্ট পরিচালনা করুন',
"de" => 'Konto verwalten',
"el-GR" => 'Διαχείριση λογαριασμού',
"en-US" => 'Manage Account',
"es" => 'Administrar la cuenta',
"fa" => 'مدیریت حساب',
"fr-FR" => 'Gérer votre compte',
"he" => 'נהל חשבון',
"hi" => 'खाते का प्रबंधन करें',
"id" => 'Kelola Akun',
"it" => 'Gestisci account',
"ja" => 'アカウント管理',
"kn" => 'ಖಾತೆ ನಿರ್ವಹಣೆ',
"ko" => '사용자 계정 관리',
"nl" => 'Account Beheren',
"pl" => 'Zarządzaj kontem',
"pt" => 'Gerenciar a conta',
"ru" => 'Управление счетом',
"te" => 'ఖాతాను నిర్వహించండి',
"th" => 'จัดการบัญชี',
"tl" => 'Pamahalaan Ang Mga Account',
"tr" => 'Hesabı Yönet',
"vi-VN" => 'Quản lý tài khoản',
"zh-CN" => '管理帳號',
]],
"manageUsers" => ['db_activity_manage_users',
[
"ar" => 'إدارة المستخدمين',
"bn" => 'ব্যবহারকারীদের পরিচালনা করুন',
"de" => 'Benutzer verwalten',
"el-GR" => 'Διαχείριση χρηστών',
"en-US" => 'Manage Users',
"es" => 'Administrar usuarios',
"fa" => 'مدیریت کاربران',
"fr-FR" => 'Gérer les utilisateurs',
"he" => 'ניהול משתמשים',
"hi" => 'उपयोगकर्ताओं का प्रबंधन करें',
"id" => 'Mengelola pengguna',
"it" => 'Gestire gli utenti',
"ja" => 'ユーザー管理',
"kn" => 'ಬಳಕೆದಾರರು ನಿರ್ವಹಿಸಿ',
"ko" => '사용자 관리',
"nl" => 'Gebruikers beheren',
"pl" => 'Zarządzaj użytkownikami',
"pt" => 'Gerenciar usuários',
"ru" => 'Управление пользователями',
"te" => 'వినియోగదారులను నిర్వహించండి',
"th" => 'จัดการผู้ใช้',
"tl" => 'Pamahalaan Ang Mga User',
"tr" => 'Kullanıcıları Yönet',
"vi-VN" => 'Quản lý tên sử dụng',
"zh-CN" => '管理使用者',
]],
"manageRoles" => ['db_activity_manage_roles',
[
"ar" => 'إدارة أدوار',
"bn" => 'ভূমিকা পরিচালনা করুন',
"de" => 'Rollen verwalten',
"el-GR" => 'Διαχείριση ρόλων',
"en-US" => 'Manage Roles',
"es" => 'Administrar roles',
"fa" => 'مدیریت نقشها',
"fr-FR" => 'Gérer les rôles',
"he" => 'ניהול תפקידים',
"hi" => 'भूमिकाओं का प्रबंधन करें',
"id" => 'Mengelola peran',
"it" => 'Gestire i ruoli',
"ja" => '役割管理',
"kn" => 'ಪಾತ್ರಗಳನ್ನು ನಿರ್ವಹಿಸಿ',
"ko" => '사용자 권한 관리',
"nl" => 'Rollen beheren',
"pl" => 'Zarządzanie rolami',
"pt" => 'Gerenciar funções',
"ru" => 'Управление ролями',
"te" => 'పాత్రలను నిర్వహించండి',
"th" => 'จัดการบทบาท',
"tl" => 'Pamahalaan Ang Mga Tungkulin',
"tr" => 'Rolleri Yönetme',
"vi-VN" => 'Quản lý chức vụ',
"zh-CN" => '管理角色',
]],
"manageCrawls" => ['db_activity_manage_crawl',
[
"ar" => 'إدارة يزحف',
"bn" => 'দল পরিচালনা করুন',
"de" => 'Crawls verwalten',
"el-GR" => 'Διαχείριση Σαρώσεων',
"en-US" => 'Manage Crawls',
"es" => 'Administrar los rastreos',
"fa" => 'مدیریت خزشها',
"fr-FR" => 'Gérer les indexes',
"he" => 'ניהול סריקות',
"hi" => 'क्रॉल का प्रबंधन करें',
"id" => 'Mengelola perayapan',
"it" => 'Gestire le ricerche',
"ja" => '検索管理',
"kn" => 'ನಿರ್ವಹಿಸಿ ಕ್ರಾಲ್',
"ko" => '크롤 관리',
"nl" => 'Beheer Crawls',
"pl" => 'Zarządzanie przeszukuje',
"pt" => 'Gerenciar rastreamentos',
"ru" => 'Управление Ползает',
"te" => 'Crawls నిర్వహించండి',
"th" => 'จัดการการตระเวน',
"tl" => 'Pamahalaan Ang Mga Pag-Crawl',
"tr" => 'Taramaları Yönetme',
"vi-VN" => 'Quản lý sự bò',
"zh-CN" => '管理爬网',
]],
"mixCrawls" => ['db_activity_mix_crawls',
[
"ar" => 'مزيج يزحف',
"bn" => 'ছোটখাটো মেলামেশা',
"de" => 'Crawls mischen',
"el-GR" => 'Μίξη Σαρώσεων',
"en-US" => 'Mix Crawls',
"es" => 'Mezclar De Los Rastreos',
"fa" => 'ترکیبهای خزشها',
"fr-FR" => 'Mélanger les indexes',
"he" => 'מערבבים סריקות',
"hi" => 'मिक्स क्रॉल',
"id" => 'Gabungan merangkak',
"it" => 'Combinare le ricerche',
"ja" => 'クロールを混在させるには',
"kn" => 'ಮಿಶ್ರಣ ಕ್ರಾಲ್',
"ko" => '크롤링을 혼합하려면',
"nl" => 'Mix Crawls',
"pl" => 'Mieszać przeszukuje',
"pt" => 'Misturar crawls',
"ru" => 'Смесь Уползает',
"te" => 'Crawls మిక్స్ చేయడానికి',
"th" => 'การมิกซ์คลาน',
"tl" => 'Paghaluin Ang Mga Pag-Crawl',
"tr" => 'Crawls Karıştırmak için',
"vi-VN" => 'Trộn Bò',
"zh-CN" => '混合爬',
]],
"manageClassifiers" => ['db_activity_manage_classifiers',
[
"ar" => 'إدارة المصنفات',
"bn" => 'শ্রেণীফায়ার পরিচালনা করুন',
"de" => 'Klassifikatoren verwalten',
"el-GR" => 'Διαχείριση Βαθμονομητών',
"en-US" => 'Manage Classifiers',
"es" => 'Administrar los clasificadores',
"fa" => 'مدیریت كلسّفرس',
"fr-FR" => 'Gérer les Classificateurs',
"he" => 'ניהול מסווג',
"hi" => 'क्लासिफायर्स का प्रबंधन करें',
"id" => 'Mengelola Pengklasifikasi',
"it" => 'Gestire i classificatori',
"ja" => '分類子の管理',
"kn" => 'ನಿರ್ವಹಿಸಿ ಪಟ್ಟಿ',
"ko" => '분류자 관리',
"nl" => 'Beheer Classifiers',
"pl" => 'Zarządzanie klasyfikatorami',
"pt" => 'Gerenciar classificadores',
"ru" => 'Управление классификаторами',
"te" => 'క్లాసిఫైయర్ లను నిర్వహించండి',
"th" => 'จัดการลักษณนาม',
"tl" => 'Pamahalaan Ang Mga Classifiers',
"tr" => 'Sınıflandırıcıları Yönetme',
"vi-VN" => 'Quản lý phân loại',
"zh-CN" => '管理分类',
]],
"pageOptions" => ['db_activity_file_options',
[
"ar" => 'خيارات الصفحة',
"bn" => 'পৃষ্ঠার পছন্দসমূহ',
"de" => 'Seitenoptionen',
"el-GR" => 'Επιλογές Σελίδας',
"en-US" => 'Page Options',
"es" => 'Las opciones de la página',
"fa" => 'تنظیمات صفحه',
"fr-FR" => 'Options de fichier',
"he" => 'אפשרויות דף',
"hi" => 'पेज विकल्प',
"id" => 'Pilihan halaman',
"it" => 'Opzioni pagina',
"ja" => 'ページ オプション',
"kn" => 'ಪುಟ ಆಯ್ಕೆಗಳು',
"ko" => '페이지 옵션',
"nl" => 'Opties voor de pagina',
"pl" => 'Opcje strony',
"pt" => 'Opções de página',
"ru" => 'Параметры страницы',
"te" => 'పుట ఐచ్ఛికాలు',
"th" => 'ตัวเลือกเพจ',
"tl" => 'Pahina Ng Mga Pagpipilian',
"tr" => 'Sayfa Seçenekleri',
"vi-VN" => 'Tùy chọn trang',
"zh-CN" => '页面上选择',
]],
"resultsEditor" => ['db_activity_results_editor',
[
"ar" => 'نتائج محرر',
"bn" => 'ফলাফল সম্পাদক',
"de" => 'Ergebniseditor',
"el-GR" => 'Επεξεργασία Αποτελεσμάτων',
"en-US" => 'Results Editor',
"es" => 'Editor de resultados',
"fa" => 'ویرایشگر نتایج',
"fr-FR" => 'Éditeur de résultats',
"he" => 'תוצאות עורך',
"hi" => 'परिणाम संपादक',
"id" => 'Editor hasil',
"it" => 'Editor risultati',
"ja" => '結果エディタ',
"kn" => 'ಫಲಿತಾಂಶಗಳು ಸಂಪಾದಕ',
"ko" => '결과 편집기',
"nl" => 'Resultaten Editor',
"pl" => 'Edytor wyników',
"pt" => 'Editor de Resultados',
"ru" => 'Редактор результатов',
"te" => 'ఫలితాల ఎడిటర్',
"th" => 'ตัวแก้ไขผลลัพธ์',
"tl" => 'Mga Resulta Editor',
"tr" => 'Sonuç Editörü',
"vi-VN" => 'Trình biên tập kết quả',
"zh-CN" => '结果编辑',
]],
"searchSources" => ['db_activity_search_services',
[
"ar" => 'مصادر البحث',
"bn" => 'অনুসন্ধান সূত্র',
"de" => 'Suchquellen',
"el-GR" => 'Πηγές Αναζήτησης',
"en-US" => 'Search Sources',
"es" => 'Fuentes de búsqueda',
"fa" => 'منابع جستجو',
"fr-FR" => 'Sources de recherche',
"he" => 'חיפוש מקורות',
"hi" => 'खोज स्रोत',
"id" => 'Cari sumber',
"it" => 'Origini di ricerca',
"ja" => '検索ソース',
"kn" => 'ಹುಡುಕಾಟ ಮೂಲಗಳು',
"ko" => '소스 검색',
"nl" => 'Zoek Bronnen',
"pl" => 'Źródła wyszukiwania',
"pt" => 'Fontes de pesquisa',
"ru" => 'Источники поиска',
"te" => 'మూలాలు శోధించు',
"th" => 'ค้นหาแหล่งที่มา',
"tl" => 'Maghanap Ng Mga Pinagkukunan',
"tr" => 'Arama Kaynakları',
"vi-VN" => 'Nguồn tìm kiếm',
"zh-CN" => '搜索来源',
]],
"scrapers" => ['db_activity_scrapers',
[
"ar" => 'ويب كاشطات',
"bn" => 'ওয়েব স্ক্রাবের',
"de" => 'Web-Scraper',
"el-GR" => 'Σαρωτές Ιστού',
"en-US" => 'Web Scrapers',
"es" => 'Web Raspadores',
"fa" => 'وب زداینده',
"fr-FR" => 'Web grattoirs',
"he" => 'אינטרנט מגרדים',
"hi" => 'वेब स्क्रैपर्स',
"id" => 'Web Scraper',
"it" => 'Raschiatori Web',
"ja" => 'ウェブスクレーパー',
"kn" => 'ವೆಬ್ ಸ್ಕ್ರೇಪರ್ಗಳು',
"ko" => '웹 스크레이퍼',
"nl" => 'Web scrapers',
"pl" => 'Skrobaki internetowe',
"pt" => 'Raspadores da Web',
"ru" => 'Веб Скребкерс',
"te" => 'వెబ్ స్క్రాప్స్',
"th" => 'ขูดเว็บ',
"tl" => 'Web Scraper',
"tr" => 'Web Kazıyıcılar',
"vi-VN" => 'Web Chọc',
"zh-CN" => '网刮',
]],
"userMessages" => ['db_activity_user_messages',
[
"ar" => 'رسائل',
"bn" => 'বার্তাগুলি',
"de" => 'Nachrichten',
"el-GR" => 'Μηνύματα',
"en-US" => 'Messages',
"es" => 'Mensajes',
"fa" => 'پیام',
"fr-FR" => 'Messages',
"he" => 'הודעות',
"hi" => 'संदेश',
"id" => 'Pesan',
"it" => 'Messaggi',
"ja" => 'メッセージ',
"kn" => 'ಸಂದೇಶಗಳುು',
"ko" => '메시지',
"nl" => 'Berichten',
"pl" => 'Wiadomości',
"pt" => 'Mensagens',
"ru" => 'Сообщения',
"te" => 'సందేశాలుు',
"th" => 'ข้อความ',
"tl" => 'Mga Mensahe',
"tr" => 'Ileti',
"vi-VN" => 'Thư',
"zh-CN" => '私信',
]],
"userMail" => ['db_activity_user_mail',
[
"de" => 'Mail',
"en-US" => 'Mail',
]],
"groupFeeds" => ['db_activity_group_feeds',
[
"ar" => 'يغذي الويكي',
"bn" => 'ফিড এবং উইকিস',
"de" => 'Feeds und Wikis',
"el-GR" => 'Ροές και Wikis',
"en-US" => 'Feeds and Wikis',
"es" => 'Fuentes y wikis',
"fa" => 'تغذیه و ویکیهای',
"fr-FR" => 'Les flux et les wikis',
"he" => 'הזנות במיזמים',
"hi" => 'फ़ीड और विकी',
"id" => 'Feed dan wiki',
"it" => 'Feed e Wiki',
"ja" => 'フィードと Wiki',
"kn" => 'ಆಹಾರ ಮತ್ತು ವಿಕಿಗಳು',
"ko" => '피드 및 위키',
"nl" => 'Feeds en Wikis',
"pl" => 'Kanały i wiki',
"pt" => 'Feeds e Wikis',
"ru" => 'Каналы и Вики',
"te" => 'సారములు మరియు వికీలు',
"th" => 'ฟีดและวิกิ',
"tl" => 'Feed at Wiki',
"tr" => 'Yayınlar ve Vikiler',
"vi-VN" => 'Thức ăn và Wiki',
"zh-CN" => '饲料和维基',
]],
"manageGroups" => ['db_activity_manage_groups',
[
"ar" => 'إدارة المجموعات',
"bn" => 'দল পরিচালনা করুন',
"de" => 'Gruppen verwalten',
"el-GR" => 'Διαχείριση Ομάδων',
"en-US" => 'Manage Groups',
"es" => 'Administrar grupos',
"fa" => 'مدیریت گروه',
"fr-FR" => 'Gérer les groupes',
"he" => 'ניהול קבוצות',
"hi" => 'समूहों का प्रबंधन करें',
"id" => 'Mengelola grup',
"it" => 'Gestisci gruppi',
"ja" => 'グループの管理',
"kn" => 'ಗುಂಪುಗಳು ನಿರ್ವಹಿಸಿ',
"ko" => '그룹 관리',
"nl" => 'Groepen beheren',
"pl" => 'Zarządzanie grupami',
"pt" => 'Grupos gerenciados',
"ru" => 'Управлять группами',
"te" => 'సమూహాలను నిర్వహించండి',
"th" => 'จัดการกลุ่ม',
"tl" => 'Pamahalaan Ang Mga Pangkat',
"tr" => 'Grupları Yönet',
"vi-VN" => 'Quản lý nhóm',
"zh-CN" => '管理组',
]],
"botStory" => ['db_activity_botstory',
[
"ar" => 'بوت القصة',
"bn" => 'বট গল্প',
"de" => 'Bot-Ablauf',
"el-GR" => "ιστορία bot",
"en-US" => 'Bot Story',
"es" => 'Bot historia',
"fa" => 'ربات داستان',
"fr-FR" => 'Bot histoire',
"he" => 'בוט הסיפור',
"hi" => 'बीओटी कहानी',
"id" => 'Bot Cerita',
"it" => 'Bot Storia',
"ja" => 'ボットストーリー',
"kn" => 'ಬೋಟ್ ಕಥೆ',
"ko" => '봇 스토리',
"nl" => 'Bot Story',
"pl" => 'Historia botów',
"pt" => 'História do bot',
"ru" => 'История бота',
"te" => 'బాట్ స్టోరీ',
"th" => 'เรื่องราวของ Bot',
"tl" => 'Bot Kuwento',
"tr" => 'Bot Hikayesi',
"vi-VN" => 'Bot Câu Chuyện',
"zh-CN" => '机器人的故事',
]],
"manageCredits" => ['db_activity_manage_credits',
[
"ar" => 'إدارة الاعتمادات',
"bn" => 'ক্রেডিট পরিচালনা করুন',
"de" => 'Guthaben verwalten',
"el-GR" => 'Διαχείριση Μονάδων',
"en-US" => 'Manage Credits',
"es" => 'Administrar créditos',
"fa" => 'مدیریت اعتبار',
"fr-FR" => 'Gérer les crédits',
"he" => 'ניהול קרדיטים',
"hi" => 'रेडिट का प्रबंधन करें',
"id" => 'Mengelola Kredit',
"it" => 'Gestire i crediti',
"ja" => 'クレジットの管理',
"kn" => 'ನಿರ್ವಹಿಸಿ ಕ್ರೆಡಿಟ್ಸ್',
"ko" => '크레딧 관리',
"nl" => 'Credits beheren',
"pl" => 'Zarządzaj kredytami',
"pt" => 'Gerenciar créditos',
"ru" => 'Управление кредитами',
"te" => 'క్రెడిట్ లను నిర్వహించండి',
"th" => 'จัดการเครดิต',
"tl" => 'Pamahalaan Ang Mga Kredito',
"tr" => 'Kredileri Yönet',
"vi-VN" => 'Quản Lý Các Khoản Tín Dụng',
"zh-CN" => '管理学分',
]],
"manageAdvertisements" => ['db_activity_manage_advertisements',
[
"ar" => 'إدارة إعلانات',
"bn" => 'বিজ্ঞাপন পরিচালনা করুন',
"de" => 'Werbung verwalten',
"el-GR" => 'Διαχείριση Διαφημίσεων',
"en-US" => 'Manage Advertisements',
"es" => 'Administrar anuncios',
"fa" => 'مدیریت تبلیغات',
"fr-FR" => 'Gérer les publicités',
"he" => 'ניהול פרסומות',
"hi" => 'विज्ञापनों का प्रबंधन करें',
"id" => 'Mengelola Iklan',
"it" => 'Gestire gli annunci',
"ja" => '提供情報の管理',
"kn" => 'ಜಾಹೀರಾತುಗಳು ನಿರ್ವಹಿಸಿ',
"ko" => '광고 관리',
"nl" => 'Advertenties beheren',
"pl" => 'Zarządzaj reklamami',
"pt" => 'Gerenciar anúncios',
"ru" => 'Управление рекламой',
"te" => 'ప్రకటనలను నిర్వహించండి',
"th" => 'จัดการโฆษณา',
"tl" => 'Pamahalaan Ang Mga Advertisement',
"tr" => 'Reklamları Yönet',
"vi-VN" => 'Quản Lý Quảng Cáo',
"zh-CN" => '管理的广告',
]],
"manageMachines" => ['db_activity_manage_machines',
[
"ar" => 'إدارة الآلات',
"bn" => 'মেশিন পরিচালনা করুন',
"de" => 'Maschinen verwalten',
"el-GR" => 'Διαχείριση Μηχανημάτων',
"en-US" => 'Manage Machines',
"es" => 'Gestionar las máquinas',
"fa" => 'مدیریت دستگاهها',
"fr-FR" => 'Gérer les machines',
"he" => 'ניהול מכונות',
"hi" => 'मशीनों का प्रबंधन करें',
"id" => 'Mengelola mesin',
"it" => 'Gestire le macchine',
"ja" => 'マシンの管理',
"kn" => 'ನಿರ್ವಹಿಸಿ ಯಂತ್ರಗಳು',
"ko" => '컴퓨터 관리',
"nl" => 'Beheer Machines',
"pl" => 'Zarządzanie maszynami',
"pt" => 'Gerenciar máquinas',
"ru" => 'Управление машинами',
"te" => 'యంత్రాలు నిర్వహించండి',
"th" => 'จัดการเครื่องจักร',
"tl" => 'Pamahalaan Ang Machine',
"tr" => 'Makineleri Yönet',
"vi-VN" => 'Quản Lý Máy',
"zh-CN" => '管理机',
]],
"manageLocales" => ['db_activity_manage_locales',
[
"ar" => 'إدارة مواقع',
"bn" => 'লোকেশনসমূহ পরিচালনা করুন',
"de" => 'Sprachumgebungen verwalten',
"el-GR" => 'Διαχείριση Γλωσσών',
"en-US" => 'Manage Locales',
"es" => 'Administrar configuraciones regionales',
"fa" => 'مدیریت زبانها',
"fr-FR" => 'Gérer les lieux',
"he" => 'ניהול אזורים',
"hi" => 'स्थानों का प्रबंधन करें',
"id" => 'Mengelola locales',
"it" => 'Gestire le impostazioni locali',
"ja" => 'ローケル管理',
"kn" => 'ನಿರ್ವಹಿಸಿ ಪ್ರದೇಶಗಳಲ್ಲಿ',
"ko" => '로케일 관리',
"nl" => 'Beheer varianten',
"pl" => 'Zarządzanie ustawieniami locales',
"pt" => 'Gerenciar locais',
"ru" => 'Управление Локалами',
"te" => 'లోకేల్స్ నిర్వహించండి',
"th" => 'จัดการตำแหน่งที่เป็น',
"tl" => 'Pamahalaan Ang Mga Locale',
"tr" => 'Yerel Leri Yönet',
"vi-VN" => 'Quản lý miền địa phương',
"zh-CN" => '管理选择',
]],
"servers" => ['db_activity_server_settings',
[
"ar" => 'إعدادات الخادم',
"bn" => 'সার্ভার সেটিংস',
"de" => 'Server',
"el-GR" => 'Ρυθμίσεις Διακομιστή',
"en-US" => 'Servers',
"es" => 'La configuración del servidor',
"fa" => 'تنظیمات سرور',
"fr-FR" => 'Paramètres du serveur',
"he" => 'הגדרות שרת',
"hi" => 'सर्वर सेटिंग',
"id" => 'Pengaturan server',
"it" => 'Impostazione server',
"ja" => 'サーバー設定',
"kn" => 'ಸರ್ವರ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು',
"ko" => '서버 설정',
"nl" => 'Server instellingen',
"pl" => 'Ustawienia serwera',
"pt" => 'Configuração do servidor',
"ru" => 'Настройка сервера',
"te" => 'సర్వర్ అమర్పు',
"th" => 'การตั้งค่าเซิร์ฟเวอร์',
"tl" => 'Mga Setting Ng Server',
"tr" => 'Sunucu Ayarı',
"vi-VN" => 'Cài Đặt Máy Chủ',
"zh-CN" => '设置服务器',
]],
"userRolesGroups" => ['db_activity_user_roles_groups',
[
"de" => 'Benutzer, Rollen, Gruppen',
"en-US" => 'User, Roles, Groups',
]],
"security" => ['db_activity_security',
[
"ar" => 'الأمن',
"bn" => 'নিরাপত্তা',
"de" => 'Sicherheit',
"el-GR" => 'Ασφάλεια',
"en-US" => 'Security',
"es" => 'Seguridad',
"fa" => 'امنیت',
"fr-FR" => 'Sécurité',
"he" => 'אבטחה',
"hi" => 'सुरक्षा',
"id" => 'Keamanan',
"it" => 'Sicurezza',
"ja" => 'セキュリティ',
"kn" => 'ಭದ್ರತಾ',
"ko" => '보안',
"nl" => 'Veiligheid',
"pl" => 'Zabezpieczeń',
"pt" => 'Segurança',
"ru" => 'Безопасности',
"te" => 'భద్రతా',
"th" => 'ความปลอดภัย',
"tl" => 'Seguridad',
"tr" => 'Güvenlik',
"vi-VN" => 'An ninh',
"zh-CN" => '安全',
]],
"appearance" => ['db_activity_appearance',
[
"ar" => 'المظهر',
"bn" => 'উপস্থিতি',
"de" => 'Aussehen',
"el-GR" => 'Εμφάνιση',
"en-US" => 'Appearance',
"es" => 'Apariencia',
"fa" => 'ظاهر',
"fr-FR" => 'Aspect',
"he" => 'מראה',
"hi" => 'उपस्थिति',
"id" => 'Penampilan',
"it" => 'Aspetto',
"ja" => '外観',
"kn" => 'ಕಾಣಿಸಿಕೊಂಡ',
"ko" => '모양을',
"nl" => 'Verschijning',
"pl" => 'Wygląd',
"pt" => 'Aparência',
"ru" => 'Внешний вид',
"te" => 'స్వరూపం',
"th" => 'ลักษณะ',
"tl" => 'Hitsura',
"tr" => 'Görünüm',
"vi-VN" => 'Xuất hiện',
"zh-CN" => '外观',
]],
"configure" => ['db_activity_configure',
[
"ar" => 'تكوين',
"bn" => 'কনফিগার',
"de" => 'Konfigurieren',
"el-GR" => 'Ρύθμιση',
"en-US" => 'Configure',
"es" => 'Configurar',
"fa" => 'پیکربندی',
"fr-FR" => 'Configurer',
"he" => 'הגדיר',
"hi" => 'कॉन्फ़िगर',
"id" => 'Mengkonfigurasi',
"it" => 'Configurare',
"ja" => '設定',
"kn" => 'ಸಂರಚಿಸಲು',
"ko" => '구성',
"nl" => 'Configureren',
"pl" => 'Skonfigurować',
"pt" => 'Configurar',
"ru" => 'Настроить',
"te" => 'కాన్ఫిగర్',
"th" => 'ตั้ง ค่า คอน ฟิก',
"tl" => 'I-Configure',
"tr" => 'Yapılandırmak',
"vi-VN" => 'Cấu hình',
"zh-CN" => '配置',
]],
];
/**
* DEFAULT_MEDIA_SOURCES lists the news feeds, podcasts and description
* sources a new database starts with, each as its timestamp, name,
* type, category, url, the paths used to read it, and its language
*/
const DEFAULT_MEDIA_SOURCES = [
['100000000', 'Yahoo News', 'rss', 'news',
'https://news.yahoo.com/rss/',
'//content/@url###/Daily(\s|\-)+Beast/i', 'en-US'],
['100000002', 'Yioop News', 'json', 'news',
'https://www.yioop.com/s/news?f=json',
'//channel###//item###//title###//description###//link###' .
'//image_link',
'en-US'],
['100000003', 'The Hollywood Reporter', 'html', 'news',
'https://www.hollywoodreporter.com/c/news',
"//section[contains(@class, 'latest-stories-news-river')]" .
"###//div[contains(@class, 'story')]###//h3###//p###//h3/a###".
"//img/@data-lazy-src", 'en-US'],
['100000004', 'National Weather Service 4', 'regex', 'weather',
'http://forecast.weather.gov/product.php?'.
'site=NWS&issuedby=04&product=SCS&format=txt&version=1&glossary=0',
'/WEA\s+LO\/HI\s*\n+([^<]+)\n+NATIONAL/mi###/\n/###'.
'/^(.+?)\s\s\s+/###/\s\s\s+(.+?)$/###http://www.weather.gov/###',
'en-US'],
['100000005', 'Ted', 'feed_podcast', '2592000',
'https://pa.tedcdn.com/feeds/talks.rss',
'############enclosure###Public@Podcast Examples/Ted/%Y-%m-%d %F',
'en-US'],
['100000006', 'IMDB', 'description_source', 'TV\s*Show|Movie',
'https://www.imdb.com/find?q=',
"Year/Rating | //h1[contains(@data-testid,'hero__pageTitle')]".
"/following-sibling::ul/li/a\n" .
"IMDB | (.//div[contains(@data-testid," .
"'hero-rating-bar__aggregate-rating__score')]/span)[1]\n" .
"Plot | //span[contains(@data-testid,'plot-l')]\n" .
"Genres | //a[contains(@class,'ipc-chip')]\n" .
"Credits | //div[contains(@data-testid," .
"'title-pc-expanded-section')]" .
"//li[contains(@data-testid,'title-pc-principal-credit')]//a\n".
"###//li[contains(@class,'find-result-item')]###" .
"//a[contains(@class,'pc-metadata-list-summary-item__t')]###" .
"//a[contains(@class,'ipc-metadata-list-summary-item__t')]/@href".
"############" .
"Movie/Catch me if you can.mp4\n" .
"Movie/Inception.mp4\n" .
"Movie/Wrong Turn.mp4\n" .
"TV Shows/Gilligan's Island", 'en-US'],
['100000007', 'AllMusic Artist', 'description_source',
'/Music[^\/]*\/[^\/]*$/',
'https://www.allmusic.com/search/artists/',
'Bio | //*[contains(@id,"bioHeadline")]' . "\n".
'Active Dates | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"activeDates")]' . "\n".
'Formed | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"birth")]' . "\n".
'Genre | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"genre")]' . "\n".
'Styles | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"styles")]' . "\n".
'Group Members | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"group-members")]' . "\n".
"###//div[contains(@class,'artist')]###" .
"//div[contains(@class,'name')]/a###" .
"//div[contains(@class,'name')]/a/@href" .
"############" .
"/Music/Buzzcocks\n/Music/Madonna\n/Music/Madonna/Like a Virgin\n",
'en-US'],
['100000008', 'AllMusic Album', 'description_source',
'Music[^\/]*\/([^\/]+)\/',
'https://www.allmusic.com/search/albums/',
'Artists | //*[contains(@id,"albumArtists")]' . "\n".
'Duration | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"duration")]/span' . "\n".
'Release Date | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"release-date")]/span' . "\n".
'Genre | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"genre")]/div' . "\n".
'Styles | //div[contains(@id,"basicInfoMeta")]' .
'/div[contains(@class,"styles")]/div' . "\n".
"###//div[contains(@class,'album')]###" .
"//div[contains(@class,'title')]/a###" .
"//div[contains(@class,'title')]/a/@href" .
"############" .
"/Music/Buzzcocks\n" .
"/Music/Buzzcocks/Time's up\n/Music/Madonna/Like a Virgin\n",
'en-US']
];
/**
* SUBSEARCH_TRANSLATIONS gives the name of each subsearch a new
* database starts with, by its translation identifier and then by
* locale
*/
const SUBSEARCH_TRANSLATIONS = [
'db_subsearch_images' => [
"ar" => 'لصور',
"bn" => 'প্রতিচ্ছবি',
"de" => 'Bilder',
"el-GR" => 'Εικόνες',
"en-US" => 'Images',
"es" => 'Imágenes',
"fa" => 'تصاویر',
"fr-FR" => 'Images',
"he" => 'תמונות',
"hi" => 'छवियां',
"id" => 'Gambar',
"it" => 'Immagini',
"ja" => '画像',
"kn" => 'ಚಿತ್ರಗಳು',
"ko" => '이미지',
"nl" => 'Beelden',
"pl" => 'Obrazów',
"pt" => 'Imagens',
"ru" => 'Изображения',
"te" => 'చిత్రాలు',
"th" => 'ภาพ',
"tl" => 'Mga larawan',
"tr" => 'Görüntü',
"vi-VN" => 'Hình',
"zh-CN" => '图象',
],
'db_subsearch_videos' => [
"ar" => 'فيدي',
"bn" => 'ভিডিও',
"de" => 'Videos',
"el-GR" => 'Βίντεο',
"en-US" => 'Videos',
"es" => 'Vídeos',
"fa" => 'ویدیوها',
"fr-FR" => 'Vidéos',
"he" => 'קטעי וידאו',
"hi" => 'वीडियो',
"id" => 'Video',
"it" => 'Video',
"ja" => 'ビデオ',
"kn" => 'ವೀಡಿಯೊಗಳು',
"ko" => '동영상',
"nl" => 'Videos',
"pl" => 'Filmy',
"pt" => 'Vídeos',
"ru" => 'Видео',
"te" => 'వీడియోలు',
"th" => 'วิดีโอ',
"tl" => 'Video',
"tr" => 'Video',
"vi-VN" => 'Thâu hình',
"zh-CN" => '录影',
],
'db_subsearch_news' => [
"ar" => 'أخبار',
"bn" => 'সংবাদ',
"de" => 'Nachrichten',
"el-GR" => 'Ειδήσεις',
"en-US" => 'News',
"es" => 'Noticias',
"fa" => 'اخبار',
"fr-FR" => 'Actualités',
"he" => 'חדשות',
"hi" => 'समाचार',
"id" => 'Berita',
"it" => 'Notizie',
"ja" => 'ニュース',
"kn" => 'ಸುದ್ದಿ',
"ko" => '뉴스',
"nl" => 'Nieuws',
"pl" => 'Wiadomości',
"pt" => 'Notícias',
"ru" => 'Новости',
"te" => 'న్యూస్',
"th" => 'ข่าว',
"tl" => 'Balita',
"tr" => 'Haber',
"vi-VN" => 'Tin tức',
"zh-CN" => '新闻',
],
'db_subsearch_trends' => [
"ar" => 'الاتجاهات',
"bn" => 'প্রবণতা',
"de" => 'Trends',
"el-GR" => 'Τάσεις',
"en-US" => 'Trends',
"es" => 'Tendencias',
"fa" => 'روند',
"fr-FR" => 'Tendances',
"he" => 'גמות',
"hi" => 'रुझान',
"id" => 'Tren',
"it" => 'Tendenze',
"ja" => '動向',
"kn" => 'ಪ್ರವೃತ್ತಿಗಳು',
"ko" => '동향',
"nl" => 'Trends',
"pl" => 'Trendy',
"pt" => 'Tendências',
"ru" => 'Тенденции',
"te" => 'ట్రెండ్',
"th" => 'แนว โน้ม',
"tl" => 'Mga uso',
"tr" => 'Eğilim',
"vi-VN" => 'Xu hướng',
"zh-CN" => '趨勢',
]
];
/**
* createDatabases makes the public and private databases of this site
* afresh, wiping any that are there, and fills them with what a new
* Yioop site starts with: the root and public accounts, the Public,
* Help, Search and Moderation groups, the locales, the public and
* help wiki pages, the admin activities and roles, and the default
* media sources, crawl mixes, subsearches and scrapers. The createdb
* command and the menu's Create Databases step call it. It prints
* "Create DB succeeded" when it is done. It writes no file but the
* databases: no profile, no DKIM key and no impressions, which record
* people viewing a page. Rows go in many to an INSERT, which took the
* time to make the databases from 47 seconds to 8.
*/
function createDatabases()
{
require_once __DIR__ . "/../library/UpgradeFunctions.php";
/* The admin controller's models hold the databases open, and
PdoManager keeps each connection it made for reuse. A SQLite
connection left open to a file taken away below makes the new
file's first write fail with a disk input and output error, and
on Windows keeps the file from being taken away at all. So all
are let go first, and the controller made again at the end. */
$this->admin = null;
PdoManager::$active_connections = [];
gc_collect_cycles();
[$db, $private_db, ] = $this->openFreshDatabases();
$this->addDefaultUsersAndGroups($db);
$locale_index = $this->addDefaultLocales($db);
$this->addDefaultWikiPages($db);
$this->addDefaultActivities($db, $locale_index);
$this->addDefaultSearchSources($db);
$lower_dbms = strtolower(DBMS);
if ($lower_dbms == 'pdo' && stristr(DB_HOST, 'pgsql') !== false) {
/* For postgres count initial values of SERIAL sequences
will be screwed up unless do
*/
$auto_tables = ["ACTIVITY" =>"ACTIVITY_ID",
"ADVERTISEMENT" => "ID",
"CHAT_BOT_PATTERN" => "PATTERN_ID", "GROUP_ITEM" =>"ID",
"GROUP_PAGE" => "ID", "SOCIAL_GROUPS" => "GROUP_ID",
"LOCALE"=> "LOCALE_ID", "PAGE_RELATIONSHIP" => "ID",
"QUERY_ITEM" => "ID", "ROLE" => "ROLE_ID", "SCRAPER" => "ID",
"TRANSLATION" => "TRANSLATION_ID", "USERS" => "USER_ID"];
foreach ($auto_tables as $table => $auto_column) {
$sql = "SELECT MAX($auto_column) AS NUM FROM $table";
$result = $db->execute($sql);
$row = $db->fetchArray($result);
$next = $row['NUM'] ?? 1;
$sequence = strtolower("{$table}_{$auto_column}_seq");
$sql = "SELECT setval('$sequence', $next)";
$db->execute($sql);
$sql = "SELECT nextval('$sequence')";
$db->execute($sql);
}
}
$db->disconnect();
$private_db->disconnect();
if (in_array($lower_dbms, ['sqlite','sqlite3'])){
chmod(DATA_DIR . "/" . DB_NAME . ".db", 0666);
chmod(DATA_DIR . "/" . PRIVATE_DB_NAME . ".db", 0666);
}
echo "Create DB succeeded\n";
$this->admin = new AdminController();
}
/**
* createDatabase is the menu's Create Databases step. Since making the
* databases afresh erases every account, group, page and setting kept
* in them, it first asks for the word yes, and makes them only if that
* is what is typed.
*
* @return string name of the next menu state, the main menu
*/
function createDatabase()
{
$this->banner();
e("Creating the databases erases all accounts, groups, wiki pages ".
"and settings\nkept in them. Type yes to go on:\n");
if (strtolower(trim(L\readInput())) != "yes") {
$_REQUEST["MESSAGE"] = "The databases were left as they were.";
return "configureMenu";
}
ob_start();
$this->createDatabases();
$_REQUEST["MESSAGE"] = trim(ob_get_clean());
return "configureMenu";
}
/**
* publicHelpPagesFile gives the file of public and help wiki pages
* this site uses: PublicHelpPages.php in the work directory's configs
* folder where export-public-help has written one, and otherwise the
* one that ships with Yioop in src/configs. createdb and
* import-public-help read their pages from it.
*
* @return string the path of the file
*/
function publicHelpPagesFile()
{
$app_file = APP_DIR . "/configs/" . self::PUBLIC_HELP_FILE;
return file_exists($app_file) ? $app_file :
BASE_DIR . "/configs/" . self::PUBLIC_HELP_FILE;
}
/**
* exportPublicHelp writes the latest version of every page of the
* Public and Help groups' wikis to PublicHelpPages.php in the work
* directory's configs folder, making the folder if it is not there,
* in the form WikiPagesFile reads. A later createdb or
* import-public-help on this site then uses those pages. Each page is
* written with the time of its latest revision. Where export_settings
* says so, the git issue pages are left out, and the posts of each
* page's thread are written beside the page. Where export_settings
* leaves out the Public or the Help group, only the other group's
* pages are written, and an import of the file leaves the group left
* out alone. The export-public-help command and the Export screen's
* Export Now step call it.
*
* @return string the message saying what was written
*/
function exportPublicHelp()
{
$app_config_dir = APP_DIR . "/configs";
if (!file_exists($app_config_dir) && !mkdir($app_config_dir)) {
return "Could not make $app_config_dir, so nothing was exported.";
}
$wiki_model = new WikiModel();
$out_file = "$app_config_dir/" . self::PUBLIC_HELP_FILE;
$with_issue_pages = $this->export_settings["ISSUE_PAGES"];
$groups = [];
foreach (["PUBLIC" => ["public_pages", PUBLIC_GROUP_ID,
"Public wiki pages", "Public"], "HELP" => ["help_pages",
HELP_GROUP_ID, "Help wiki pages", "Help"]] as $setting => $group) {
if ($this->export_settings[$setting]) {
$groups[] = $group;
}
}
if (empty($groups)) {
return "Neither the Public nor the Help group was chosen, so " .
"nothing was exported.";
}
$arrays = [];
foreach ($groups as [$array_name, $group_id, $description]) {
$pubdates = [];
$pages = $wiki_model->latestGroupPages($group_id, "", $pubdates,
$with_issue_pages);
$comments = ($this->export_settings["COMMENTS"]) ?
$wiki_model->groupPageComments($group_id) : [];
$history = ($this->export_settings["HISTORY"]) ?
function ($locale_tag, $page_name) use ($wiki_model,
$group_id) {
$page_id = $wiki_model->getPageId($group_id, $page_name,
$locale_tag);
return ($page_id) ? $wiki_model->pageHistory($page_id) :
[];
} : null;
$arrays[$array_name] = [$description, $pages, $pubdates,
$comments, $history];
}
$part_names = ["ISSUE_PAGES" => "issue_pages",
"COMMENTS" => "comments", "HISTORY" => "history"];
$parts = [];
$taken = [];
foreach ($part_names as $setting => $part) {
if ($this->export_settings[$setting]) {
$parts[] = $part;
}
$taken[$part] = ($this->export_settings[$setting]) ? "with" :
"without";
}
WikiPagesFile::write($out_file, $arrays, "Default Public Wiki Pages",
"ConfigureTool.php export-public-help", $parts);
$group_names = implode(" and ", array_column($groups, 3));
return "Wrote the $group_names pages to $out_file, " .
"{$taken['issue_pages']} " .
"git issue pages, {$taken['comments']} the comments on the " .
"pages and {$taken['history']} the pages' history.";
}
/**
* importPublicHelp writes the pages of PublicHelpPages.php, the one
* publicHelpPagesFile picks, into the Public and Help groups' wikis.
* Only the groups the file holds are touched, so a file of just the
* Public or just the Help group's pages leaves the other group alone.
* A page that is not there is made and a page whose text differs is
* given the file's text as a new revision, whoever edited it last;
* the rest of the database is left as it is. The import-public-help
* command and the menu's Import step call it. Where the database stays
* locked by another program, the import is undone and the message
* says so.
*
* @param bool $wipe_first whether the Public and Help groups are left
* holding just the file's pages, as the menu's deleting Import
* step and import-public-help wipe ask: every other page is
* deleted, with the git issue pages, history and discussion of
* the kinds the file carries, and the deletion is undone with the
* import if it fails
* @return string the message saying which file was imported, or why
* nothing was
*/
function importPublicHelp($wipe_first = false)
{
require_once __DIR__ . "/../library/UpgradeFunctions.php";
$pages_file = $this->publicHelpPagesFile();
$model = new Model();
/* The pages are saved in one transaction, which SQLite can only
finish once no other connection is reading or writing the
database. A running site or crawl may hold it longer than the
database driver waits, and the whole import is then undone. */
try {
L\upgradePublicHelpWiki($model->db, true, $pages_file,
$wipe_first);
} catch (\PDOException $e) {
if (!empty($model->db->pdo) && $model->db->pdo->inTransaction()) {
$model->db->pdo->rollBack();
}
return "Nothing was imported from $pages_file: the database " .
"stayed in use by another program, such as the running " .
"site or a crawl, until the database driver stopped " .
"waiting (" . $e->getMessage() . "). Stop the site and " .
"any crawl, then import again.";
}
$group_names = implode(" and ", array_map(fn($array_name) =>
($array_name == "public_pages") ? "Public" : "Help",
WikiPagesFile::exportedGroups($pages_file)));
return "Imported the $group_names wiki pages of $pages_file" .
($wipe_first ? " and deleted the $group_names pages it " .
"does not hold." : ".");
}
/**
* importPublicHelpWipe is the menu's Import step that deletes the
* Public and Help wiki pages the file does not hold, as
* importPublicHelp does with wipe_first. It makes a site's Public and
* Help wikis the file's pages, such as a copy of another site's
* export. A kind of data the export left out, such as git issue pages,
* comments or history, is kept as it was.
*
* @return string the message saying what was deleted and imported, or
* why nothing was
*/
function importPublicHelpWipe()
{
return $this->importPublicHelp(true);
}
/**
* migratePreV10Resources moves the work directory's app/resources from
* the pre-version 10 layout to the version 10 layout ResourceFolders
* gives, and renames personal groups to the form PERSONAL_GROUP_SUFFIX
* says, as V10ResourceFolderMigration does, and gives its report of
* what it did and how long it took. The migrate-pre-v10-resources
* command prints the report, and the menu's Migrate step shows it as
* the menu's message. Database upgrade 134 does the same during the
* first web request after an upgrade to version 10; a site with many
* posts or files can run this first, from the command line or the
* menu, so that request finds nothing left to do. Running it again
* does no harm.
*
* @return string the report, one line for each thing it did or kept
* and one for each count
*/
function migratePreV10Resources()
{
$model = new Model();
$report = [];
$migration = new L\V10ResourceFolderMigration($model->db, "", "",
function ($line) use (&$report) {
$report[] = $line;
});
$migration->run();
return implode("\n", $report);
}
/**
* clearSqliteDatabase takes away one SQLite database and everything
* SQLite keeps beside it, so that building the databases again is never
* stopped by what an earlier run left.
*
* openFreshDatabases calls this for each of the two databases before it
* connects. Taking away only the database file is not enough: the
* databases run in write-ahead logging, which keeps a log file and a
* shared-memory file whose names end in -wal and -shm, and an older run
* may leave a -journal beside them. A log left over from the database
* that was there belongs to a database that is now gone, and SQLite
* answers the first write to the new one with a disk input and output
* error rather than saying what it found. Where a file cannot be taken
* away this says which one and why, since that is a fault in the
* folder rather than in Yioop.
*
* @param string $database_name name of the database, without the ending,
* as DB_NAME and PRIVATE_DB_NAME give it
*/
function clearSqliteDatabase($database_name)
{
$stem = DATA_DIR . "/" . $database_name . ".db";
foreach (["", "-wal", "-shm", "-journal"] as $ending) {
$path = $stem . $ending;
if (!file_exists($path)) {
continue;
}
if (!@unlink($path)) {
$why = is_writable(dirname($path)) ?
"the file is held open or is not writable" :
"the folder it sits in is not writable";
echo "\n\nCould not take away $path: $why.\n" .
"Stop any Yioop process still running, then run this " .
"again.\n\n";
exit();
}
}
}
/**
* openFreshDatabases connects to the public and private databases with
* all their tables made anew and empty. For SQLite it removes the
* database files first. For other database systems it makes a
* database that is not there yet and drops the tables of one that is.
* createDatabases calls it first.
*
* @return array the public database connection, the private one, and
* the profile model used to make the public tables
*/
function openFreshDatabases()
{
$profile_model = new ProfileModel(DB_NAME, false);
$private_profile_model = new ProfileModel(PRIVATE_DB_NAME, false);
$db_class = NS_DATASOURCES . ucfirst(DBMS)."Manager";
$private_db_class = NS_DATASOURCES . ucfirst(PRIVATE_DBMS) .
"Manager";
$dbinfo = ["DBMS" => DBMS, "DB_HOST" => DB_HOST,
"DB_USER" => DB_USER, "DB_PASSWORD" => DB_PASSWORD,
"DB_NAME" => DB_NAME];
$private_dbinfo = ["DBMS" => PRIVATE_DBMS,
"DB_HOST" => PRIVATE_DB_HOST, "DB_USER" => PRIVATE_DB_USER,
"DB_PASSWORD" => PRIVATE_DB_PASSWORD,
"DB_NAME" => PRIVATE_DB_NAME];
$lower_dbms = strtolower(DBMS);
if (!in_array($lower_dbms, ['sqlite', 'sqlite3'])) {
$db = new $db_class();
$private_db = new $private_db_class();
if (!$db->connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)) {
$host = preg_replace("/\;dbname\=\w+/", "", DB_HOST);
L\crawlLog('Public database doesn\'t exist yet, ' .
'trying to create it');
$db->connect($host, DB_USER, DB_PASSWORD, "");
$is_mysql = (stristr(DBMS, 'Mysql') !== false);
$charset_clause = ($is_mysql) ?
" CHARACTER SET utf8mb4 " : "";
$db->execute("CREATE DATABASE " . DB_NAME . $charset_clause);
$db->disconnect();
$db->connect(); // default connection goes to actual DB
}
if (!$private_db->connect(PRIVATE_DB_HOST, PRIVATE_DB_USER,
PRIVATE_DB_PASSWORD, PRIVATE_DB_NAME)) {
$private_host = preg_replace("/\;dbname\=\w+/", "",
PRIVATE_DB_HOST);
L\crawlLog('Private database doesn\'t exist yet, ' .
'trying to create it');
$private_db->connect($private_host, PRIVATE_DB_USER,
PRIVATE_DB_PASSWORD);
$is_mysql = (stristr(PRIVATE_DBMS, 'Mysql') !== false);
$charset_clause = ($is_mysql) ?
" CHARACTER SET utf8mb4 " : "";
$private_db->execute("CREATE DATABASE " . PRIVATE_DB_NAME .
$charset_clause);
$private_db->disconnect();
$private_db->connect(PRIVATE_DB_HOST, PRIVATE_DB_USER,
PRIVATE_DB_PASSWORD, PRIVATE_DB_NAME);
}
/* postgres doesn't let you drop a database while connected to
it so drop tables instead first */
$profile_model->initializeSql($db, $dbinfo);
$private_profile_model->initializeSqlPrivate($private_db,
$private_dbinfo);
$database_tables = array_keys($profile_model->create_statements);
$private_database_tables = array_keys(
$private_profile_model->private_create_statements);
foreach ($database_tables as $table) {
if ($table == "CURRENT_WEB_INDEX" ||
!str_ends_with($table, "INDEX")) {
$db->execute("DROP TABLE IF EXISTS " . $table);
}
}
foreach ($private_database_tables as $table) {
if ($table == "CURRENT_WEB_INDEX" ||
!str_ends_with($table, "INDEX")) {
$private_db->execute("DROP TABLE IF EXISTS " . $table);
}
}
} else {
$this->clearSqliteDatabase(DB_NAME);
$db = new $db_class();
$db->connect();
$db->execute("PRAGMA journal_mode=WAL");
$this->clearSqliteDatabase(PRIVATE_DB_NAME);
$private_db = new $private_db_class();
$private_db->connect(PRIVATE_DB_HOST, PRIVATE_DB_USER,
PRIVATE_DB_PASSWORD, PRIVATE_DB_NAME);
$private_db->execute("PRAGMA journal_mode=WAL");
}
if (!$profile_model->createDatabaseTables($db, $dbinfo)) {
echo "\n\nCouldn't create database tables!!!\n\n";
exit();
}
if (!$private_profile_model->createDatabaseTablesPrivate($private_db,
$private_dbinfo)) {
echo "\n\nCouldn't create database tables!!!\n\n";
exit();
}
return [$db, $private_db, $profile_model];
}
/**
* addDefaultUsersAndGroups writes the database version, the root and
* public accounts with the root account's own group, the Public, Help,
* Search and Moderation groups with their members, and the roles.
* createDatabases calls it. It writes no file, so the site's profile
* is left as it is.
*
* @param object $db the public database connection
*/
function addDefaultUsersAndGroups($db)
{
$db->execute("INSERT INTO VERSION VALUES (" . DATABASE_VERSION . ")");
/* Naming the columns keeps these inserts working when a column is
added to the table, which a positional list does not. */
$group_columns = "INSERT INTO SOCIAL_GROUPS (GROUP_ID, GROUP_NAME,
CREATED_TIME, OWNER_ID, REGISTER_TYPE, MEMBER_ACCESS, VOTE_ACCESS,
POST_LIFETIME, OPTIONS, RENDER_ENGINE)";
$creation_time = L\microTimestamp();
//default account is root without a password
$sql ="INSERT INTO USERS (USER_ID, FIRST_NAME, LAST_NAME, USER_NAME,
EMAIL, PASSWORD, STATUS, HASH, CREATION_TIME, UPS, DOWNS,
AD_CREDITS_SERVICE_ACCOUNT) VALUES (" . ROOT_ID .
", 'admin', 'admin','" . ROOT_USERNAME . "',
'root@dev.null', '".L\crawlCrypt('')."', '".ACTIVE_STATUS.
"', '".L\crawlCrypt(ROOT_USERNAME . AUTH_KEY . $creation_time).
"', '$creation_time', 0, 0, null)";
$db->execute($sql);
// set up personal group for root account
$creation_time = L\microTimestamp();
$root_group_name = mb_strtolower(ROOT_USERNAME) .
C\PERSONAL_GROUP_SUFFIX;
$group_model = new GroupModel(DB_NAME, false);
$group_model->db = $db;
$group_model->addGroup($root_group_name,
ROOT_ID, C\INVITE_ONLY_JOIN, C\ACTIVE_STATUS,
C\NON_VOTING_GROUP, C\FOREVER, 0, skip_impression: true);
/* public account is an inactive account for used for public permissions
default account is root without a password
*/
$sql ="INSERT INTO USERS (USER_ID, FIRST_NAME, LAST_NAME, USER_NAME,
EMAIL, PASSWORD, STATUS, HASH, CREATION_TIME, UPS, DOWNS,
AD_CREDITS_SERVICE_ACCOUNT) VALUES (".PUBLIC_USER_ID.
", 'all', 'all','public',
'public@dev.null', '".L\crawlCrypt('')."', '".INACTIVE_STATUS.
"', '".L\crawlCrypt('public' . AUTH_KEY . $creation_time)."',
'$creation_time', 0, 0, null)";
$db->execute($sql);
//default public group with group id 1
$creation_time = L\microTimestamp();
$sql = $group_columns . " VALUES(" . PUBLIC_GROUP_ID . ", '" .
C\PUBLIC_GROUP_NAME . "', '" .
$creation_time . "','".ROOT_ID."', '" .PUBLIC_JOIN . "', '" .
GROUP_READ . "', " . NON_VOTING_GROUP.", " . FOREVER . ", " .
GROUP_OPTION_PAGE_CUSTOMIZE_ALLOWED . ", " .
MEDIAWIKI_ENGINE . ")";
$db->execute($sql);
$now = time();
$db->execute("INSERT INTO ROLE VALUES (" . ADMIN_ROLE .
", 'Admin'), (" . USER_ROLE . ", 'User'), (" . BOT_ROLE .
", 'Bot User'), (" . DEVELOPER_ROLE . ", 'Developer')");
$db->execute("INSERT INTO ROLE_LIMITS (ROLE_ID) VALUES (" .
ADMIN_ROLE . "), (" . USER_ROLE . "), (" . BOT_ROLE . "), (" .
DEVELOPER_ROLE . ")");
$db->execute("INSERT INTO USER_ROLE (USER_ID, ROLE_ID) VALUES (" .
ROOT_ID . ", " . ADMIN_ROLE . ")");
$db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", ".
PUBLIC_GROUP_ID.", " . ACTIVE_STATUS . ", 1, $now)");
$db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", ".
PUBLIC_GROUP_ID.", " . ACTIVE_STATUS . ", 1, $now)");
//Create a Group for Wiki HELP.
$sql = $group_columns . " VALUES (" . HELP_GROUP_ID . ",'Help','" .
$creation_time . "','" . ROOT_ID . "',
'" . PUBLIC_BROWSE_REQUEST_JOIN . "', '" . GROUP_READ_WIKI .
"', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ", " .
GROUP_OPTION_PAGE_CUSTOMIZE_ALLOWED . ", " . MEDIAWIKI_ENGINE . ")";
$db->execute($sql);
$now = time();
$db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
HELP_GROUP_ID . ", " . ACTIVE_STATUS . ", 1, $now)");
$db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", " .
HELP_GROUP_ID . ", " . ACTIVE_STATUS . ", 1, $now)");
//Create a Group for Search Result Edits
$sql = $group_columns . " VALUES (" . SEARCH_GROUP_ID . ",'Search','" .
$creation_time . "','" . ROOT_ID . "',
'" . PUBLIC_BROWSE_REQUEST_JOIN . "', '" . GROUP_READ_WIKI .
"', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ", " .
GROUP_OPTION_PAGE_CUSTOMIZE_ALLOWED . ", " . MEDIAWIKI_ENGINE . ")";
$db->execute($sql);
$now = time();
$db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
SEARCH_GROUP_ID . ", " . ACTIVE_STATUS . ", 1, $now)");
$db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", " .
SEARCH_GROUP_ID . ", " . ACTIVE_STATUS . ", 1, $now)");
/* we insert 1 by 1 rather than comma separate as sqlite
does not support comma separated inserts
*/
//Create a group for moderator users
$sql = $group_columns . " VALUES(" . MODERATION_GROUP_ID .
", 'Moderation','" . $creation_time . "','".ROOT_ID."', '" .
INVITE_ONLY_JOIN . "', '" . GROUP_READ . "', " .
NON_VOTING_GROUP . ", " . FOREVER . ", " .
GROUP_OPTION_PAGE_CUSTOMIZE_ALLOWED . ", " .
MEDIAWIKI_ENGINE . ")";
$db->execute($sql);
$now = time();
$db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", 1, $now)");
$db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID .
", " . MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", 1, $now)");
}
/**
* addDefaultLocales writes the DEFAULT_LOCALES into the LOCALE table,
* numbering them from 1 in order. createDatabases calls it.
*
* @param object $db the public database connection
* @return array locale tag to the number it was given, which the
* translations of activities are stored under
*/
function addDefaultLocales($db)
{
$locale_index = [];
$locale_id = 1;
$rows = [];
foreach (self::DEFAULT_LOCALES as $locale) {
$rows[] = [$locale_id, $locale[0], $locale[1], $locale[2], 1,
LocaleModel::DEFAULT_FLAGS[$locale[0]] ?? ""];
$locale_index[$locale[0]] = $locale_id;
$locale_id++;
}
$db->insertRows("LOCALE", ["LOCALE_ID", "LOCALE_TAG", "LOCALE_NAME",
"WRITING_MODE", "ACTIVE", "FLAG"], $rows);
return $locale_index;
}
/**
* addDefaultWikiPages writes the generic page relationship and the
* public and help wiki pages into a new database. The pages come from
* PublicHelpPages.php in the work directory's configs folder where
* there is one, as export-public-help writes it, and otherwise from
* the one that ships with Yioop. A group that file leaves out, as an
* export of just the Public or just the Help group does, takes its
* pages from the file that ships with Yioop. The file is read as text by
* WikiPagesFile, not run. The pages are added together with
* WikiModel::addNewPages, which records no impressions.
* createDatabases calls it.
*
* @param object $db the public database connection
*/
function addDefaultWikiPages($db)
{
$wiki_model = new WikiModel(DB_NAME, false);
$wiki_model->db = $db;
$db->execute("INSERT INTO PAGE_RELATIONSHIP VALUES " .
"(-1, 'generic_links')");
$pages_file = $this->publicHelpPagesFile();
$page_arrays = WikiPagesFile::read($pages_file);
/* An export of just one group leaves the other group's pages to
the file that ships with Yioop. */
$shipped_file = BASE_DIR . "/configs/" . self::PUBLIC_HELP_FILE;
$groups = WikiPagesFile::exportedGroups($pages_file);
if ($pages_file != $shipped_file && count($groups) < 2) {
$shipped_arrays = WikiPagesFile::read($shipped_file);
foreach (["public_pages", "help_pages"] as $array_name) {
if (!in_array($array_name, $groups)) {
$page_arrays[$array_name] =
$shipped_arrays[$array_name] ?? [];
}
}
}
$default_locale = L\getLocaleTag();
$pages = [];
foreach (["public_pages" => PUBLIC_GROUP_ID,
"help_pages" => HELP_GROUP_ID] as $array_name => $group_id) {
foreach ($page_arrays[$array_name] ?? [] as $locale_tag =>
$locale_pages) {
L\setLocaleObject($locale_tag);
foreach ($locale_pages as $page_name => $page_content) {
$page_name = str_replace(" ", "_", $page_name);
$page_content = str_replace("'", "'",
$page_content);
$pages[] = [$group_id, $page_name, $page_content,
$locale_tag, "create",
L\tl('social_component_page_created', $page_name),
L\tl('social_component_page_discuss_here')];
}
}
}
L\setLocaleObject($default_locale);
$wiki_model->addNewPages(ROOT_ID, $pages, WikiModel::WIKI_LINK_BASE);
}
/**
* addDefaultActivities writes the DEFAULT_ACTIVITIES with their
* translations, and which roles may use each: the admin role all but
* the credit and advertisement activities unless the site's
* monetization calls for them, the bot role the bot story, the user
* role the activities a new user needs, and the developer role group
* feeds. createDatabases calls it.
*
* @param object $db the public database connection
* @param array $locale_index locale tag to its number in LOCALE
*/
function addDefaultActivities($db, $locale_index)
{
$activities = self::DEFAULT_ACTIVITIES;
$activity_rows = [];
$role_rows = [];
$translation_rows = [];
$translation_locale_rows = [];
$activity_id = 1;
foreach ($activities as $activity => $translation_info) {
$activity_rows[] = [$activity_id, $activity_id, $activity];
//give admin role the ability to have that activity (except ads)
if (!in_array($activity, ["manageCredits",
"manageAdvertisements"])) {
if ($activity == "botStory") {
$role_rows[] = [BOT_ROLE, $activity_id, "all"];
} else {
$role_rows[] = [ADMIN_ROLE, $activity_id, "all"];
}
} else if (in_array(MONETIZATION_TYPE, ['group_fees',
'keyword_advertisements', 'fees_and_keywords']) &&
$activity == "manageCredits") {
$role_rows[] = [ADMIN_ROLE, $activity_id, "all"];
} else if (in_array(MONETIZATION_TYPE, [
'keyword_advertisements', 'fees_and_keywords']) &&
$activity == "manageAdvertisements") {
$role_rows[] = [ADMIN_ROLE, $activity_id, "all"];
}
$translation_rows[] = [$activity_id, $translation_info[0]];
foreach ($translation_info[1] as $locale_tag => $translation) {
$index = $locale_index[$locale_tag];
$translation_locale_rows[] = [$activity_id, $index,
$translation];
}
$activity_id++;
}
$new_user_activities = [
"manageAccount",
"manageGroups",
"groupFeeds",
"userMessages",
"userMail",
];
if (in_array(MONETIZATION_TYPE, ['group_fees',
'keyword_advertisements', 'fees_and_keywords']) &&
$activity == "manageCredits") {
$new_user_activities[] = "manageCredits";
}
if (in_array(MONETIZATION_TYPE, [
'keyword_advertisements', 'fees_and_keywords']) &&
$activity == "manageAdvertisements") {
$new_user_activities[] = "manageAdvertisements";
}
foreach ($new_user_activities as $new_activity) {
$activity_id = 1;
foreach ($activities as $key => $value) {
if ($new_activity == $key){
//give new user role the ability to have that activity
$arguments = ($new_activity == "groupFeeds") ?
"no_git_repository" : "all";
$role_rows[] = [USER_ROLE, $activity_id, $arguments];
}
$activity_id++;
}
}
$activity_id = 1;
foreach ($activities as $key => $value) {
if ($key == "groupFeeds") {
$role_rows[] = [DEVELOPER_ROLE, $activity_id, "all"];
}
$activity_id++;
}
$db->insertRows("ACTIVITY", ["ACTIVITY_ID", "TRANSLATION_ID",
"METHOD_NAME"], $activity_rows);
$db->insertRows("ROLE_ACTIVITY", ["ROLE_ID", "ACTIVITY_ID",
"ALLOWED_ARGUMENTS"], $role_rows);
$db->insertRows("TRANSLATION", ["TRANSLATION_ID",
"IDENTIFIER_STRING"], $translation_rows);
$db->insertRows("TRANSLATION_LOCALE", ["TRANSLATION_ID",
"LOCALE_ID", "TRANSLATION"], $translation_locale_rows);
}
/**
* addDefaultSearchSources writes the name server's machine row, the
* DEFAULT_MEDIA_SOURCES, the images, videos and news crawl mixes, the
* subsearches with their SUBSEARCH_TRANSLATIONS, and the default
* scrapers. createDatabases calls it.
*
* @param object $db the public database connection
*/
function addDefaultSearchSources($db)
{
$db->execute("INSERT INTO MACHINE VALUES ('NAME_SERVER', " .
"'BASE_URL', 0, 2,'')");
$db->insertRows("MEDIA_SOURCE", ["TIMESTAMP", "NAME", "TYPE",
"CATEGORY", "SOURCE_URL", "AUX_INFO", "LANGUAGE"],
self::DEFAULT_MEDIA_SOURCES);
$db->execute("INSERT INTO CRAWL_MIXES VALUES (2, 'images', " .
ROOT_ID . ", -1)");
$db->execute("INSERT INTO MIX_FRAGMENTS VALUES(2, 0, 1)");
$db->execute("INSERT INTO MIX_COMPONENTS VALUES(
2, 0, 1, 1, 1, 'media:image site:doc')");
$db->execute("INSERT INTO CRAWL_MIXES VALUES (3, 'videos', " .
ROOT_ID . ", -1)");
$db->execute("INSERT INTO MIX_FRAGMENTS VALUES(3, 0, 1)");
$db->execute("INSERT INTO MIX_COMPONENTS VALUES(
3, 0, 1, 1, 1, 'media:video')");
$db->execute("INSERT INTO CRAWL_MIXES VALUES (4, 'news', " .
ROOT_ID . ", -1)");
$db->execute("INSERT INTO MIX_FRAGMENTS VALUES(4, 0, 1)");
$db->execute("INSERT INTO MIX_COMPONENTS VALUES(4, 0, 100, 1, -1,
'media:news')");
$db->execute("INSERT INTO SUBSEARCH VALUES('db_subsearch_images',
'images','m:2', 50, '')");
$db->execute("INSERT INTO TRANSLATION VALUES (1002, " .
"'db_subsearch_images')");
$db->execute("INSERT INTO SUBSEARCH VALUES ('db_subsearch_videos',
'videos','m:3', 10, '')");
$db->execute("INSERT INTO TRANSLATION VALUES (1003, " .
"'db_subsearch_videos')");
$db->execute("INSERT INTO SUBSEARCH VALUES ('db_subsearch_news',
'news','m:4', 20, 'lang:default-major highlight:1')");
$db->execute("INSERT INTO TRANSLATION VALUES (1004, " .
"'db_subsearch_news')");
$db->execute("INSERT INTO SUBSEARCH VALUES ('db_subsearch_trends',
'trends','-1', 10, 'trending:news:score_desc highlight:2')");
$db->execute("INSERT INTO TRANSLATION VALUES (1005, " .
"'db_subsearch_trends')");
$db->insertRows("SCRAPER", ["NAME", "SIGNATURE", "TEXT_PATH",
"DELETE_PATHS", "EXTRACT_FIELDS"], ScraperModel::DEFAULT_SCRAPERS);
foreach (self::SUBSEARCH_TRANSLATIONS as $identifier =>
$locale_translations) {
foreach ($locale_translations as $locale_tag => $translation) {
L\updateTranslationForStringId($db, $identifier, $locale_tag,
$translation);
}
}
}
}
/**
* The set of commands the tool understands from the command line. Each
* entry maps a short command name to: the activity method it runs, the
* list of arguments that command takes (for the help text and for
* checking the count given), the smallest and largest number of
* arguments allowed, and a one-line description. Toggle-style commands
* take any number of +name / -name arguments to turn settings on or
* off, so their range is open-ended.
*
* @return array the command specifications keyed by command name
*/
function configureToolCommands()
{
return [
"work-dir" => ["activity" => "workDirectory",
"args" => "[path]", "min" => 0, "max" => 1,
"help" => "Set or create the work directory (no path ".
"means the default location)."],
"root-password" => ["activity" => "rootPassword",
"args" => "<old> <new>", "min" => 2, "max" => 2,
"help" => "Change the root account password."],
"locale" => ["activity" => "defaultLocale",
"args" => "<tag>", "min" => 1, "max" => 1,
"help" => "Set the default language, such as en-US."],
"debug" => ["activity" => "debugDisplay",
"args" => "[+/-error] [+/-query] [+/-test]",
"min" => 1, "max" => 3,
"help" => "Turn debug outputs on (+) or off (-)."],
"search-access" => ["activity" => "searchAccess",
"args" => "[+/-web] [+/-rss] [+/-api]",
"min" => 1, "max" => 3,
"help" => "Turn search-access methods on (+) or off (-)."],
"page-elements" => ["activity" => "searchPageElementLinks",
"args" => "[+/-word-suggest] [+/-subsearch] ".
"[+/-signin] [+/-result-info]\n ".
"[+/-cache] [+/-similar] [+/-inlink] [+/-ip]",
"min" => 1, "max" => 99,
"help" => "Turn search-page elements and links on (+) ".
"or off (-)."],
"name-server" => ["activity" => "nameServer",
"args" => "<key> <url>", "min" => 2, "max" => 2,
"help" => "Set the server key and name-server URL."],
"robot" => ["activity" => "robotSetUp",
"args" => "<name> <instance> <description>",
"min" => 3, "max" => 3,
"help" => "Set the crawl robot name, instance, and ".
"description."],
"upgrade-locales" => ["activity" => "upgradeLocales",
"args" => "[overwrite]", "min" => 0, "max" => 1,
"help" => "Trim the work directory's locales to this ".
"site's changes and force-push wiki\n resources from ".
"the source tree; with overwrite, also replace wiki ".
"pages\n edited on this site."],
"createdb" => ["activity" => "createDatabases",
"args" => "", "min" => 0, "max" => 0,
"help" => "Make the databases afresh with the accounts, groups ".
"and wiki pages\n a new site starts with. This erases ".
"all data kept in them."],
"migrate-pre-v10-resources" => ["activity" =>
"migratePreV10Resources", "args" => "", "min" => 0, "max" => 0,
"help" => "Move app/resources from the pre-version 10 layout to ".
"the version 10 one,\n named by group, page and user ".
"names, and rename personal groups;\n upgrade 134 does ".
"the same, so a large site can run this first.\n Safe ".
"to run again."],
"export-public-help" => ["activity" => "exportPublicHelp",
"args" => "[+/-public] [+/-help] [+/-issues] [+/-comments] " .
"[+/-history]",
"min" => 0, "max" => 5,
"help" => "Write the Public and Help wiki pages to ".
"PublicHelpPages.php in the\n work directory's configs ".
"folder, with each page's date. -public or\n -help ".
"leaves out that group's pages, and an import of the file ".
"then\n leaves that group alone. -issues leaves out ".
"the pages of\n git repositories' issues, -comments ".
"leaves out the\n comments and discussion posts of ".
"each page, and -history leaves\n out each page's ".
"earlier revisions; all are exported unless left\n ".
"out. An ".
"import that deletes first leaves alone what was left ".
"out."],
"import-public-help" => ["activity" => "importPublicHelp",
"args" => "[wipe]", "min" => 0, "max" => 1,
"help" => "Replace the Public and Help wiki pages with those of ".
"PublicHelpPages.php\n in the work directory's configs ".
"folder, or in src/configs if there is\n none there; ".
"the rest of the databases is kept, and so is a group\n ".
"the file does not hold. With wipe, every\n ".
"Public and Help page the file does not hold is deleted, ".
"with\n the git issue pages, history and discussion ".
"the file carries\n kinds of; the kinds an export ".
"left out are kept."],
];
}
/**
* Prints how to run the configuration tool from the command line: with
* no arguments to see this help, with the word menu to use the
* interactive menu, or with a command name and its arguments to run
* that one step without prompting. Each command is listed with the
* arguments it takes and a one-line description.
*/
function configureToolUsage()
{
e("Yioop Configuration Tool\n");
e("========================\n\n");
e("Usage:\n");
e(" php ConfigureTool.php\n");
e(" show this help\n");
e(" php ConfigureTool.php menu\n");
e(" run the interactive menu\n");
e(" php ConfigureTool.php <command> [arguments...]\n");
e(" run one command without prompting\n\n");
e("Commands:\n");
foreach (configureToolCommands() as $name => $spec) {
e(" $name " . $spec["args"] . "\n");
e(" " . $spec["help"] . "\n");
}
}
$configure_tool = new ConfigureTool();
if (empty($argv[1])) {
configureToolUsage();
} else if ($argv[1] == "menu") {
$configure_tool->loop();
} else {
$commands = configureToolCommands();
if (!isset($commands[$argv[1]])) {
e("Unknown command: " . $argv[1] . "\n\n");
configureToolUsage();
} else {
$spec = $commands[$argv[1]];
$values = array_slice($argv, 2);
if (count($values) < $spec["min"] ||
count($values) > $spec["max"]) {
e("Wrong number of arguments for " . $argv[1] . ".\n\n");
configureToolUsage();
} else {
$configure_tool->runActivityFromArgs($argv[1], $spec,
$values);
}
}
}