Implement basic Pepperminty Wiki CLI & shell :D

The BkTree tester gave me the idea.

No longer will you have to hope that search indexing will complete in 
time and adjust the maximum execution time for larger wikis..... when 
that's implemented.
This commit is contained in:
Starbeamrainbowlabs 2020-03-10 01:47:40 +00:00
parent 6480d72323
commit fa81f0df25
Signed by: sbrl
GPG Key ID: 1BE5172E637709C2
8 changed files with 204 additions and 4 deletions

View File

@ -1,5 +1,8 @@
<?php
// This is the Pepperminty Wiki build environment
define("PEPPERMINTY_WIKI_BUILD", true);
echo("*** Preparing environment ***\n");
ini_set("user_agent", "Pepperminty-Wiki-Downloader PHP/" . phpversion() . "; +https://github.com/sbrl/Pepperminty-Wiki/ Pepperminty-Wiki/" . file_get_contents("version"));

View File

@ -823,3 +823,13 @@ function crypto_id(int $length) : string {
[ "=" => "", "+" => "-", "/" => "_"]
), 0, $length);
}
/**
* Returns whether we are both on the cli AND the cli is enabled.
* @return boolean
*/
function is_cli() {
global $settings;
return php_sapi_name() == "cli" &&
$settings->cli_enabled;
}

View File

@ -1,6 +1,6 @@
<?php
session_start();
if(!is_cli()) session_start();
// Make sure that the login cookie lasts beyond the end of the user's session
setcookie(session_name(), session_id(), time() + $settings->sessionlifetime, "", "", false, true);
///////// Login System /////////

View File

@ -11,6 +11,12 @@ if(!isset($actions->credits))
exit(page_renderer::render_main("Error - $settings->$sitename", "<p>No credits page detected. The credits page is a required module!</p>"));
}
// If we're on the CLI, then start it
if(!defined("PEPPERMINTY_WIKI_BUILD") &&
module_exists("feature-cli") &&
$settings->cli_enabled &&
php_sapi_name() == "cli")
cli();
//////////////////////////////////
/// Final Consistency Measures ///

View File

@ -5,7 +5,7 @@
// Work around an Opera + Syntaxtic bug where there is no margin at the left
// hand side if there isn't a query string when accessing a .php file.
if(!isset($_GET["action"]) and !isset($_GET["page"]) and basename(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)) == "index.php")
if(!is_cli() && !isset($_GET["action"]) && !isset($_GET["page"]) && basename(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)) == "index.php")
{
http_response_code(302);
header("location: " . dirname(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)));

View File

@ -11,7 +11,9 @@
// - A login is required to view this wiki
// - The user isn't already requesting the login page
// Note we use $_GET here because $env->action isn't populated at this point
if($settings->require_login_view === true && // If this site requires a login in order to view pages
if(
!is_cli() &&
$settings->require_login_view === true && // If this site requires a login in order to view pages
!$env->is_logged_in && // And the user isn't logged in
!in_array($_GET["action"], [ "login", "checklogin", "opensearch-description", "invindex-rebuild", "stats-update" ])) // And the user isn't trying to login, or get the opensearch description, or access actions that apply their own access rules
{

177
modules/feature-cli.php Normal file
View File

@ -0,0 +1,177 @@
<?php
register_module([
"name" => "Command-line interface",
"version" => "0.1",
"author" => "Starbeamrainbowlabs",
"description" => "Allows interaction with Pepperminty Wiki on the command line.",
"id" => "feature-cli",
"code" => function() {
global $settings;
cli_register("version", "Shows the current version of Pepperminty Wiki", function(array $_args) : int {
echo("$version-".substr($commit, 0, 7)."\n");
return 0;
});
cli_register("help", "Displays this message", function(array $_args) : int {
global $version, $commit, $cli_commands;
echo("***** Pepperminty Wiki CLI *****
$version-".substr($commit, 0, 7)."
This is the command-line interface for Pepperminty Wiki.
Commands:
");
foreach($cli_commands as $name => $data) {
echo(" $name {$data->description}\n");
}
return 0;
});
cli_register("shell", "Starts the Pepperminty Wiki shell", function(array $_args) : int {
cli_shell();
return 0;
});
cli_register("exit", "Exits the Pepperminty Wiki shell", function(array $args) {
$exit_code = 0;
if(!empty($args)) $exit_code = intval($args[0]);
exit($exit_code);
});
add_help_section("999-cli", "Command Line Interface", "<p>System administrators can interact with $settings->sitename via a command-line interface if they have console or terminal-level access to the server that $settings->sitename runs on.</p>
<p>To do this, system administrators can display the CLI-specific help by changing directory (with the <code>cd</code> command) to be next to <code>index.php</code>, and executing the following:</p>
<pre><code>php index.php</code></pre>");
}
]);
/**
* Ensures that the current execution environment is the command-line interface.
* This function will not return if thisthe current execution environment is not the CLI.
* @return void
*/
function ensure_cli() {
global $settings;
if(php_sapi_name() == "cli") return true;
header("content-type: text/plain");
exit("Oops! Somewhere along the way Pepperminty Wiki's command-line interface was invoked by accident.
This is unfortunately an unrecoverable fatal error. Please get in touch with $settings->admindetails_name, $settings->sitename's administrator (their email address si $settings->admindetails_email).
");
}
/**
* Parses $_SERVER["argv"] and provides a command-line interface.
* This function kill the process if the current execution environment is not the CLI.
* @return void
*/
function cli() {
global $version, $commit;
ensure_cli();
$args = array_slice($_SERVER["argv"], 1);
switch($args[0] ?? "") {
case "version":
case "shell":
exit(cli_exec($args[0]));
case "exec":
file_put_contents("php://stderr", "Executing {$args[1]}\n");
exit(cli_exec($args[1]) ? 0 : 1);
break;
case "help":
default:
echo("***** Pepperminty Wiki CLI *****
$version-".substr($commit, 0, 7)."
This is the command-line interface for Pepperminty Wiki.
Usage:
php ./index.php {subcommand}
Commands:
help Displays this message
version Shows the current version of Pepperminty Wiki
shell Starts the Pepperminty Wiki shell
exec \"{command}\" Executes a Pepperminty Wiki shell command
");
break;
}
exit(0);
}
/**
* Starts the Pepperminty Wiki CLI Shell.
* This function kill the process if the current execution environment is not the CLI.
* @return [type] [description]
*/
function cli_shell() {
global $settings;
ensure_cli();
echo(wordwrap("Welcome to the Pepperminty Wiki CLI shell!
Type \"help\" (without quotes) to get help.
Be warned that you are effectively the superuser for your wiki right now, with completely unrestricted access!
"));
while(true) {
$next_line = readline($settings->cli_prompt);
if($next_line == false) { echo("\nexit\n"); exit(0); }
if(strlen($next_line) == 0) continue;
$exit_code = cli_exec($next_line);
echo("<<<< $exit_code <<<<\n");
}
}
/**
* Executes a given Pepperminty Wiki shell command.
* This function kill the process if the current execution environment is not the CLI.
* The returned exit code functions as a normal shell process exit code does.
* @param string $string The shell command to execute.
* @return int The exit code of the command executed.
*/
function cli_exec(string $string) : int {
global $settings, $cli_commands;
ensure_cli();
$parts = preg_split("/\s+/", $string);
if(!isset($cli_commands->{$parts[0]})) {
echo("Error: The command with the name {$parts[0]} could not be found (try the help command instead).\n");
return 1;
}
// Apparently you still have to assign a callable to a variable in order to call it dynamically like this. Ref: core/100-run.php
$method = $cli_commands->{$parts[0]}->code;
return $method(array_slice($parts, 1));
}
$cli_commands = new stdClass();
/**
* Registers a new CLI command.
* Throws an error if a CLI command with the specified name already exists.
* @param string $name The name of command.
* @param string $description The description of the command.
* @param callable $function The function to execute when this command is executed. An array is passed as the first and only argument containing the arguments passed when the command was invoked.
* @return void
*/
function cli_register(string $name, string $description, callable $function) {
global $cli_commands;
if(isset($cli_commands->$name))
throw new Exception("Error: A CLI command with the name $name has already been registered (description: {$cli_commands->$name->description})");
$cli_commands->$name = (object) [
"description" => $description,
"code" => $function
];
}
?>

View File

@ -251,5 +251,7 @@
"css_theme_gallery_index_url": { "type": "text", "description": "A url that points to an index file that contains a list of themes. Used to populate the gallary. Multiple urls are allowed - separate them with a space.", "default": "https://starbeamrainbowlabs.com/labs/peppermint/themes/themeindex.json" },
"css_theme_gallery_selected_id": { "type": "text", "description": "The id of the currently selected theme. Defaults to the internal default theme.", "default": "default" },
"css": { "type": "textarea", "description": "A string of css to include. Will be included in the &lt;head&gt; of every page inside a &lt;style&gt; tag. This may also be an absolute url - urls will be referenced via a &lt;link rel='stylesheet' /&gt; tag. If the theme gallery is installed and automatic updates enabled, then the value of this property is managed by the theme gallery and changes may be overwritten (try the css_custom setting instead).", "default": "auto" },
"css_custom": { "type": "textarea", "description": "A string of custom CSS to include on top of the base theme css. Allows for theme customisations while still enabling automatic updates :D Just like the css setting, this one can also be a url.", "default": "/* Enter your custom css here. */" }
"css_custom": { "type": "textarea", "description": "A string of custom CSS to include on top of the base theme css. Allows for theme customisations while still enabling automatic updates :D Just like the css setting, this one can also be a url.", "default": "/* Enter your custom css here. */" },
"cli_enabled": { "type": "text", "description": "Whether the Pepperminty Wiki CLI is enabled or not.", "default": true },
"cli_prompt": { "type": "text", "description": "The string to use as the prompt in the CLI shell.", "default": "# " }
}