Page MenuHomeCode

No OneTemporary

diff --git a/do.php b/do.php
index 62c7395..93e927a 100644
--- a/do.php
+++ b/do.php
@@ -1,473 +1,484 @@
<?php
/**
* AJAX callbacks
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* As main controller could potentially be interrupted (e.g. if site.requests
* flag is at 1, user is redirected to controllers/userrequest.php), all AJAX
* queries should be handled by this script and not directly by the controllers.
*
* Standard return values:
* -7 user is logged but perso isn't selected,
* -9 user is not logged.
*
* @package Zed
* @subpackage EntryPoints
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2010 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @filesource
*/
use Hypership\Geo\Point3D;
+use Keruald\Database\DatabaseEngine;
use Zed\Engines\Database\Database;
use Zed\Engines\Templates\Smarty\Engine as SmartyEngine;
use Zed\Models\Geo\Location;
use Zed\Models\Objects\Content;
use Zed\Models\Objects\Perso;
use Zed\Models\Objects\User;
////////////////////////////////////////////////////////////////////////////////
///
/// Constants
///
//We define one negative number constant by standard erroneous return value.
/**
* Magic number which indicates the user is not logged in.
*/
define('USER_NOT_LOGGED', -9);
/**
* Magic number which indicates the user is logged in, but haven't selected its perso.
*/
define('PERSO_NOT_SELECTED', -7);
////////////////////////////////////////////////////////////////////////////////
///
/// Initialization
///
include('includes/core.php');
//Database
$db = Database::load($Config['database']);
//Session
$IP = $_SERVER["REMOTE_ADDR"];
require_once('includes/story/story.php'); //this class can be stored in session
session_start();
$_SESSION['ID'] = session_id();
session_update(); //updates or creates the session
//Gets current perso
$CurrentUser = get_logged_user(); //Gets current user infos
if ($perso_id = $CurrentUser->session['perso_id']) {
$CurrentPerso = new Perso($db, $perso_id);
}
//Requires user and perso
if ($CurrentUser->id < 1000) {
echo USER_NOT_LOGGED;
exit;
}
if (!$CurrentPerso) {
echo PERSO_NOT_SELECTED;
exit;
}
//Loads Smarty (as it handles l10n, it will be used by lang_get)
$smarty = SmartyEngine::load()->getSmarty();
//Loads language files
initialize_lang();
lang_load('core.conf');
////////////////////////////////////////////////////////////////////////////////
///
/// Actions definitions
///
/**
* Actions class
*
* Each method is called by first part of your URL, other parts are arguments
* e.g. /do.php/validate_quux_request/52 = Actions::validate_quux_request(52);
*
* You can also use $_GET, $_POST or better $_REQUEST.
*
* Don't print the value but return it, so we can in the future implement custom
* formats like api_output();
*/
class Actions {
+
+ ///
+ /// Properties
+ ///
+
+ public Perso $CurrentPerso;
+
+ public User $CurrentUser;
+
+ ///
+ /// Available actions
+ ///
+ public DatabaseEngine $db;
+
/**
* Checks the arguments hash and determines whether it is valid.
*
* @param Array $args the arguments, the last being the hash
* @return boolean true if the hash is valid ; otherwise, false.
*/
static private function is_hash_valid ($args) {
global $Config;
return array_pop($args) == md5($_SESSION['ID'] . $Config['SecretKey'] . implode('', $args));
}
/**
* Handles a allow/deny perso request.
*
* @param string $request_flag the request flag to clear
* @param string $store 'perso' or 'registry'
* @param string $key the perso flag or registry key
* @param string $value the value to store
* @param string $hash the security hash
* @return boolean true if the request is valid and have been processed ; otherwise, false.
*/
- static function perso_request ($request_flag, $store, $key, $value, $hash) {
- global $CurrentPerso;
-
+ public function perso_request ($request_flag, $store, $key, $value, $hash) : bool {
//Ensures we've the correct amount of arguments
if (func_num_args() < 4) {
return false;
}
//Checks hash
$args = func_get_args();
if (!self::is_hash_valid($args)) {
return false;
}
//Sets flag
switch ($store) {
case 'perso':
- $CurrentPerso->set_flag($key, $value);
+ $this->CurrentPerso->set_flag($key, $value);
break;
case 'registry':
- registry_set($key, $value);
+ registry_set($this->db, $key, $value);
break;
default:
//Unknown storage location
return false;
}
//Clears request flag
if ((string)$request_flag !== "0") {
- $CurrentPerso->delete_flag($request_flag);
+ $this->CurrentPerso->delete_flag($request_flag);
}
return true;
}
/**
* Sets current perso's local location.
*
* We don't require a security hash. If the users want to play with it, no problem.
* You generally move inside a global location as you wish.
* So, if you write a story capturing a perso, use flags to handle this escape!
*
* @param string $location_local the local location
* @return Location the current perso's Location object
*/
- static function set_local_location ($location_local) {
- global $CurrentPerso;
-
+ public function set_local_location ($location_local) {
//Ensures we've the correct amount of arguments
if (func_num_args() < 1) {
return null;
}
//Moves current perso to specified location
$location_local = urldecode($location_local);
- $CurrentPerso->move_to(null, $location_local);
+ $this->CurrentPerso->move_to(null, $location_local);
//Returns Location relevant instance
- return $CurrentPerso->location;
+ return $this->CurrentPerso->location;
}
/**
* Moves the current perso's, setting a new local location.
*
* We don't require a security hash. If the users want to play with it, no problem.
* You generally move inside a global location as you wish.
* So, if you write a story capturing a perso, use flags to handle this escape!
*
* @param string $move the move (coordinates or direction)
* @param int $factor a number multiplying the specified move [optional]
* @return Location the current perso's Location object
*
* e.g. to move from 2 units to east, you can use one of those instructions:
* local_move('east', 2);
* local_move('2,0,0');
* local_move('1,0,0', 2);
*
* Valid moves string are north, east, south, west, up and down.
* Valid moves coordinates are x,y,z (3 integers, comma as separator)
*/
- static function local_move ($move, $factor = 1) {
- global $CurrentPerso;
-
+ public function local_move ($move, $factor = 1) {
//Ensures we've the correct amount of arguments
if (func_num_args() < 1) {
return null;
}
//Parses $move
switch ($move) {
case 'north':
$move = [0, 1, 0];
break;
case 'east':
$move = [1, 0, 0];
break;
case 'south':
$move = [0, -1, 0];
break;
case 'west':
$move = [-1, 0, 0];
break;
case 'up':
$move = [0, 0, 1];
break;
case 'down':
$move = [0, 0, -1];
break;
default:
$move = explode(',', $move, 3);
foreach ($move as $coordinate) {
if (!is_numeric($coordinate)) {
return null;
}
}
}
//Moves current perso to specified location
- if ($location_local = Point3D::fromString($CurrentPerso->location->local)) {
+ if ($location_local = Point3D::fromString($this->CurrentPerso->location->local)) {
$location_local->translate(
(float)$move[0] * $factor,
(float)$move[1] * $factor,
(float)$move[2] * $factor
);
- $CurrentPerso->move_to(null, $location_local->sprintf("(%d, %d, %d)"));
+ $this->CurrentPerso->move_to(null, $location_local->sprintf("(%d, %d, %d)"));
//Returns Location relevant instance
- return $CurrentPerso->location;
+ return $this->CurrentPerso->location;
}
//Old local location weren't a Point3D
return null;
}
/**
* Moves the current perso's, setting a new local location, using polar+z coordinates.
* Polar+z coordinates are polar coordinates, plus a cartesian z dimension.
*
* We don't require a security hash. If the users want to play with it, no problem.
* You generally move inside a global location as you wish.
* So, if you write a story capturing a perso, use flags to handle this escape!
*
* @param string $move the move (coordinates or direction)
* @param int $factor a number multiplying the specified move [optional]
* @return Location the current perso's Location object
*
* Valid moves string are cw, ccw, out, in, up and down.
* r: out = +12 in = -12
* °: cw = +20° ccw = -20
* Valid moves coordinates are r,°,z (3 integers, comma as separator)
* (the medium value can also be integer + °)
*
* e.g. to move of two units (the unit is 20°) clockwise:
* polarz_local_move('cw', 2);
* polarz_local_move('(0, 20°, 0)', 2);
* polarz_local_move('(0, 40°, 0)');
* Or if you really want to use radians (PI/9 won't be parsed):
* polarz_local_move('(0, 0.6981317007977318, 0)';
*
*/
- static function polarz_local_move ($move, $factor = 1) {
- global $CurrentPerso;
-
+ public function polarz_local_move ($move, $factor = 1) {
//Ensures we've the correct amount of arguments
if (func_num_args() < 1) {
return null;
}
//Parses $move
$move = urldecode($move);
switch ($move) {
case 'cw':
$move = [0, '20°', 0];
break;
case 'ccw':
$move = [0, '-20°', 0];
break;
case 'in':
$move = [+12, 0, 0];
break;
case 'out':
$move = [-12, 0, 0];
break;
case 'up':
$move = [0, 0, 1];
break;
case 'down':
$move = [0, 0, -1];
break;
default:
$move = explode(',', $move, 3);
foreach ($move as $coordinate) {
if (!is_numeric($coordinate) && !preg_match("/^[0-9]+ *°$/", $coordinate)) {
return null;
}
}
}
dieprint_r($move);
//Moves current perso to specified location
throw new Exception("Move is not implemented.");
//Old local location weren't a 3D point
return null;
}
/**
* Moves the current perso's, setting a new global and local location.
*
* @param string $location_global The global location
* @param string $location_local The local location
* @return Location the current perso's Location object
*/
- static function global_move ($location_global, $location_local = null) {
+ public function global_move ($location_global, $location_local = null) {
//Ensures we've the correct amount of arguments
if (func_num_args() < 1) {
return null;
}
//Checks hash
$args = func_get_args();
if (!self::is_hash_valid($args)) {
return false;
}
//Moves
- global $CurrentPerso;
- $CurrentPerso->move_to($location_global, $location_local);
- return $CurrentPerso->location;
+ $this->CurrentPerso->move_to($location_global, $location_local);
+ return $this->CurrentPerso->location;
}
/**
* Handles upload content form.
*
* @return string new content path
*/
- static function upload_content () {
- global $CurrentPerso, $CurrentUser;
-
+ public function upload_content () {
//Initializes a new content instance
$content = new Content($db);
$content->load_from_form();
- $content->user_id = $CurrentUser->id;
- $content->perso_id = $CurrentPerso->id;
- $content->location_global = $CurrentPerso->location_global;
+ $content->user_id = $this->CurrentUser->id;
+ $content->perso_id = $this->CurrentPerso->id;
+ $content->location_global = $this->CurrentPerso->location_global;
//Saves file
if ($content->handle_uploaded_file($_FILES['artwork'])) {
$content->save_to_database();
$content->generate_thumbnail();
return true;
}
return false;
}
/**
* Gets multimedia content for the specified location
*
* @param string $location_global The global location (local is to specified in ?location_local parameter)
* @return Array an array of Content instances
*/
- static function get_content ($location_global) {
+ public function get_content ($location_global) {
//Ensures we've the correct amount of arguments
if (func_num_args() < 1) {
return null;
}
//Checks hash
$args = func_get_args();
if (!self::is_hash_valid($args)) {
return false;
}
//Checks local location is specified somewhere (usually in $_GET)
if (!array_key_exists('location_local', $_REQUEST)) {
return false;
}
//Gets content
- return Content::get_local_content($location_global, $_REQUEST['location_local']);
- return Content::get_local_content($db, $location_global, $_REQUEST['location_local']);
+ return Content::get_local_content($this->db, $location_global, $_REQUEST['location_local']);
}
}
////////////////////////////////////////////////////////////////////////////////
///
/// Handles request
///
//Parses URL
$Config['SiteURL'] = get_server_url() . $_SERVER["PHP_SELF"];
$args = get_current_url_fragments();
$method = array_shift($args);
+$actions = new Actions();
+$actions->db = $db;
+$actions->CurrentPerso = $CurrentPerso;
+$actions->CurrentUser = $CurrentUser;
+
$debug = $_REQUEST['debug'] ?? $_SESSION['debug'] ?? false;
if ($debug) {
//Debug version
//Most of E_STRICT errors are evaluated at the compile time thus such errors
//are not reported
ini_set('display_errors', 'stderr');
error_reporting(-1);
- if (method_exists('Actions', $method)) {
- $result = call_user_func_array(['Actions', $method], $args);
+ if (method_exists($actions, $method)) {
+ $result = call_user_func_array([$actions, $method], $args);
+
+ header('Content-type: application/json; charset=utf-8');
echo json_encode($result);
} else {
echo "<p>Method doesn't exist: $method</p>";
}
if (array_key_exists('redirectTo', $_REQUEST)) {
//If user JS disabled, you can add ?redirectTo= followed by an URL
echo "<p>Instead to print a callback value, redirects to <a href=\"$_REQUEST[redirectTo]\">$_REQUEST[redirectTo]</a></p>";
}
} else {
//Prod version doesn't prints warning <== silence operator
- if (method_exists('Actions', $method)) {
- $result = @call_user_func_array(['Actions', $method], $args);
+ if (method_exists($actions, $method)) {
+ $result = @call_user_func_array([$actions, $method], $args);
if (array_key_exists('redirectTo', $_REQUEST)) {
//If user JS disabled, you can add ?redirectTo= followed by an URL
header("location: " . $_REQUEST['redirectTo']);
} else {
+ header('Content-type: application/json; charset=utf-8');
echo json_encode($result);
}
}
}
diff --git a/includes/SmartLine/SmartLine.php b/includes/SmartLine/SmartLine.php
index 17dff10..ae572cf 100644
--- a/includes/SmartLine/SmartLine.php
+++ b/includes/SmartLine/SmartLine.php
@@ -1,562 +1,565 @@
<?php
/**
* SmartLine 0.1
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2007-07-28 01:36 [DcK] Initial release
* 2010-07-02 00:39 [Dck] Documentation
*
* @package Zed
* @subpackage SmartLine
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2007 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @link http://bitbucket.org/dereckson/smartline
* @filesource
///////////////////////////////////////////////////////////////////////////////
// SECTION I - INITIALIZATION
///////////////////////////////////////////////////////////////////////////////
//Constants
/**
* The standard, regular output (like STDOUT on POSIX systems)
*/
if (!defined('STDOUT')) {
define('STDOUT', 1);
}
/**
* The error output (like STDERR on POSIX systems)
*/
if (!defined('STDERR')) {
define('STDERR', -1);
}
///////////////////////////////////////////////////////////////////////////////
// SECTION Ibis - L10n
///////////////////////////////////////////////////////////////////////////////
//Ensures $lang is a standard array
if (empty($lang) || !is_array($lang)) {
$lang = [];
}
$lang = array_merge($lang, [
//Errors
'InvalidCommand' => "Invalid command %s. Use <strong>showcommands</strong> to show all commands.",
'RegisteredButNotExistingCommand' => "[CRITICAL ERROR] The command %s has correctly been registered but its method or class doesn't exist.",
'NotYetHelpForThiscommand' => "This command hasn't been documented yet.",
//Help
'DefaultHelp' => "This SmartLine is a command line interface.
<br /><br /><strong>showcommands</strong> prints the list.
<br /><strong>help &lt;command&gt;</strong> prints help for this command.",
'Help' => [
'help' => "<strong>help &lt;command&gt;</strong> prints command help.",
'showcommands' => 'show available commands'
]
]);
///////////////////////////////////////////////////////////////////////////////
// SECTION II - HELPERS FUNCTIONS
///////////////////////////////////////////////////////////////////////////////
/**
* Error handler called during SmartLine command execution.
*
* Any error occurring during command execution will be set in STDERR.
*
* To get an array with all the errors:
* <code>$errors = $yourSmartLine->gets_all(STDERR)</code>
*
* Or to prints all the error:
* <code>$yourSmartLine->prints_all(STDERR)</code>
*
* Or to pops (gets and deletes) only the last error:
* <code>$lastError = $yourSmartLine->gets(STDERR)</code>
*
* @link http://www.php.net/manual/en/function.set-error-handler.php set_error_handler, PHP manual
* @link http://www.php.net/manual/en/errorfunc.examples.php Error handling examples, PHP manual
*
* @param int $level The PHP error level
* @param string $error The error description
* @param string $file The script where the error occurred
* @param int $line The line where the error occurred
*/
function SmartLineHandler($level, $error, $file, $line) {
switch ($level) {
case E_NOTICE:
$type = 'Notice';
break;
case E_WARNING:
$type = 'Warning';
break;
case E_ERROR:
$type = 'Error';
break;
default:
$type = "#$level";
}
$_SESSION['SmartLineOutput'][STDERR][] = "[PHP $type] $error in $file line $line.";
return true;
}
///////////////////////////////////////////////////////////////////////////////
// SECTION III - BASE CLASSES
///////////////////////////////////////////////////////////////////////////////
//SmartLineCommand is a class implementing a SmartLine command.
//If you want to create a more complex command, extends this class.
/**
* The SmartLine command base class.
*
* To add a command, create an instance of the class, like:
* <code>
* class HelloWorldSmartLineCommand extends SmartLineCommand {
* public function run ($argv, $argc) {
* $this->SmartLine->puts('Hello World!');
* }
* }
* </code>
*
* Then, registers your command:
* <code>
* $yourSmartLine->register_object('hello', 'HelloWorldSmartLineCommand');
* </code>
*
* @see SmartLine::register_object
*/
class SmartLineCommand {
/**
* Initializes a new instance of the SmartLine Command
*
* @param SmartLine $SmartLine the SmartLine the command belongs
*/
public function __construct ($SmartLine) {
$this->SmartLine = $SmartLine;
}
/**
* Gets the command help text or indicates help should be fetched from $lang array
*
* @return string|bool a string containing the command help or the bool value false, to enable the default behavior (ie prints $lang['help']['nameOfTheCommand'])
*/
public function help () {
return false;
}
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
}
static protected function parseBoolean (string $arg) : bool {
return match (strtolower($arg)) {
"on", "enable", "enabled", "1", "up", "true" => true,
"off", "disable", "disabled", "0", "down", "false" => false,
default => throw new InvalidArgumentException(
"Boolean string expected, got '$arg' instead."
),
};
}
/**
* The SmartLine where this instance of the command is registered
*
* @var SmartLine
*/
public $SmartLine;
}
/**
* This class represents a SmartLine instance
*
* If you use only register_object, you can use it directly.
* If you use register_method, extends this class in your SmartLine.
*/
class SmartLine {
/**
* Initializes a new instance of the SmartLine object.
*/
public function __construct () {
//Assumes we've an empty array where store registered commands.
$this->commands = [];
//Let's register standard commands
$this->register_object('showcommands', 'ShowCommandsSmartLineCommand');
$this->register_object('help', 'HelpSmartLineCommand');
}
/**
* Registers a private method as command.
*
* @param string $command The name of the command to register
* @param string $method The method to register [OPTIONAL]. If omitted, the method registered will be the method having the same name as the command.
* @param bool $useArgvArgc If true, indicates the method uses $argv, $argc as parameters. If false, indicates the method uses its parameters (default behavior). [OPTIONAL]
*
* @return bool true if the command have successfully been registered ; otherwise, false.
*/
public function register_method ($command, $method = null, $useArgvArgc = false) {
if (is_null($function)) {
$method = $command;
}
if (!method_exists($this, $method)) {
$this->lastError = "Registration failed. Unknown method $method";
return false;
}
$className = ucfirst($method) . 'SmartLineCommand';
//If class exists, add a uniqid after function
while (class_exists($method)) {
$className = uniqid(ucfirst($method)) . 'SmartLineCommand';
}
//Creates the class
if ($useArgvArgc) {
$call = "$this->SmartLine->$method(\$argv, \$argc);";
} else {
//We don't know how many args we've, so we use call_user_func_array
$call = "array_shift(\$argv);
call_user_func_array(
array(&\$this->SmartLine, '$method'),
\$argv
);";
}
$code = "class $className extends SmartLineCommand {
public function run (\$argv, \$argc) {
$call
}
}";
eval($code);
$this->register_object($command, $className);
return true;
}
/**
* Registers an object extending SmartLineCommand as command.
*
* @param string $command The name of the command to register
* @param SmartLineCommand|string $object The object extending SmartLineCommand. This can be the name of the class (string) or an instance already initialized of the object (SmartLineCommand).
* @return bool true if the command have successfully been registered ; otherwise, false.
*/
public function register_object ($command, $object) {
if (is_object($object)) {
//Sets SmartLine property
$object->SmartLine = $this;
} elseif (is_string($object) && class_exists($object)) {
//Creates a new instance of $object
$object = new $object($this);
} else {
$this->lastError = "Registration failed. register_object second parameter must be a class name (string) or an already initialized instance of such class (object) and not a " . gettype($object);
return false;
}
if (!$this->caseSensitive) {
$command = strtolower($command);
}
$this->commands[$command] = $object;
return true;
}
/**
* Determines whether the specified command have been registered.
*
* @param string $command The name of the command to check
* @return true if the specified command have been registered ; otherwise, false.
*/
public function isRegistered ($command) {
if (!$this->caseSensitive) {
$command = strtolower($command);
}
return array_key_exists($command, $this->commands);
}
/**
* Executes the specified expression.
*
* If an error occurs during the command execution:
* the STDERR output will contain the errors,
* the value returned by this methods will be false.
*
* To execute the command and prints error:
* <code>
* $fooSmartLine = new SmartLine();
* //...
* $result = $fooSmartLine->execute($expression);
* $fooSmartLine->prints_all();
* if (!$result) {
* //Errors!
* echo "<h3>Errors</h3>";
* $fooSmartLine->prints_all(STDERR);
* }
* </code>
*
* @param string $expression The expression containing the command to execute
* @return bool true if the command have been successfully executed ; otherwise, false.
*/
public function execute (string $expression) : bool {
//Does nothing if blank line
if (!$expression) {
return true;
}
//Prepares $argv and $argc
$argv = $this->expression2argv($expression);
$argc = count($argv);
//Gets command
$command = $this->caseSensitive ? $argv[0] : strtolower($argv[0]);
//If command doesn't exist, throws an error
if (!array_key_exists($command, $this->commands)) {
global $lang;
$this->puts(sprintf($lang['InvalidCommand'], $command), STDERR);
return false;
}
//Executes command, intercepting error and returns result
set_error_handler("SmartLineHandler");
$result = true;
try {
$result = $this->commands[$command]->run($argv, $argc);
} catch (Exception $ex) {
$this->puts("<pre>$ex</pre>", STDERR);
$result = false;
}
restore_error_handler();
return $result;
}
/**
* Adds a message to the specified output queue.
*
* @param string $message the message to queue
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
*/
public function puts ($message, $output = STDOUT) {
//
$_SESSION['SmartLineOutput'][$output][] = $message;
}
/**
* Truncates the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
*/
public function truncate ($output = STDOUT) {
unset($_SESSION['SmartLineOutput'][$output]);
}
/**
* Pops (gets and clears) the first message from the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
* @return string the message
*/
public function gets ($output = STDOUT) {
if ($this->count($output) > 0) {
return array_pop($_SESSION['SmartLineOutput'][$output]);
}
return "";
}
/**
* Gets the number of messages in the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
*/
public function count ($output = STDOUT) {
return isset($_SESSION['SmartLineOutput'][$output])
? count($_SESSION['SmartLineOutput'][$output])
: 0;
}
/**
* Gets all the message from the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
* @param string $prefix The string to prepend each message with. It's an optional parameter ; if omitted, '<p>'.
* @param string $suffix The string to append each message with. It's an optional parameter ; if omitted, '</p>'.
* @return string HTML representation of the messages from the specified output queue
*/
public function gets_all ($output = STDOUT, $prefix = '<p>', $suffix = '</p>') : string {
$count = $this->count($output);
if ($count == 0) {
return "";
}
$buffer = "";
for ($i = 0 ; $i < $count ; $i++) {
$buffer .= $prefix . $_SESSION['SmartLineOutput'][$output][$i] . $suffix;
}
unset ($_SESSION['SmartLineOutput'][$output]);
return $buffer;
}
/**
* Prints all the message from the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
* @param string $prefix The string to prepend each message with. It's an optional parameter ; if omitted, '<p>'.
* @param string $suffix The string to append each message with. It's an optional parameter ; if omitted, '</p>'.
*/
public function prints_all ($output = STDOUT, $prefix = '<p>', $suffix = '</p>') {
$count = $this->count($output);
if ($count == 0) {
return;
}
for ($i = 0 ; $i < $count ; $i++) {
echo $prefix, $_SESSION['SmartLineOutput'][$output][$i], $suffix;
}
unset ($_SESSION['SmartLineOutput'][$output]);
}
/**
* Gets the command help
*
* @param string $command The command to get help from
* @param string The command help
*/
public function gethelp ($command) {
return $this->commands[$command]->help();
}
/**
* Gets an an argv array from the specified expression
*
* @param string $expression The expression to transform into a argv array
* @return Array An array of string, the first item the command, the others those arguments.
*/
private function expression2argv ($expression) {
//Checks if expression contains "
$pos1 = strpos($expression, '"');
//We isolate "subexpression"
if ($pos1 !== false) {
$pos2 = $pos1;
do {
$pos2 = strpos($expression, '"', $pos2 + 1);
} while ($pos2 !== false && ($expression[$pos2 - 1] == "\\" && $expression[$pos2 - 2] != "\\"));
if ($pos2 === false) {
//If final quote is missing, throws a warning and autoadds it.
$this->puts("[Warning] Final \" missing in $expression.", STDERR);
$argv = $this->expression2argv(substr($expression, 0, $pos1));
$argv[] = substr($expression, $pos1 + 1);
return $argv;
}
return array_merge(
$this->expression2argv(substr($expression, 0, $pos1)),
[substr($expression, $pos1 + 1, $pos2 - $pos1 - 1)],
$this->expression2argv(substr($expression, $pos2 + 1))
);
}
//Standard expression (ie without ")
$argv = [];
$items = explode(' ', $expression);
foreach ($items as $item) {
$item = trim($item);
if (!$item) {
//blank, we ignore
continue;
}
$argv[] = $item;
}
return $argv;
}
//The list of commands
public array $commands = [];
+ //The services exposed to commands
+ public array $services = [];
+
//Contains last error
public $lastError = '';
//If true, command isn't equal to Command
public $caseSensitive = true;
}
///////////////////////////////////////////////////////////////////////////////
// SECTION IV - STANDARD COMMANDS
///////////////////////////////////////////////////////////////////////////////
/*
* These commands are available in all default smartlines instance
*/
/**
* The standard command "showcommands"
*
* This command returns a list, with all the available commands
*/
class ShowCommandsSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
$commands = array_keys($this->SmartLine->commands);
sort($commands);
$this->SmartLine->puts(implode(' ', $commands));
}
}
/**
* The standard command "help"
*
* This command prints command help.
*
* Help could be defined
* in the command classes, as a return value from the help method ;
* in the $lang['Help'] array, at the command key (e.g. $lang['Help']['quux'] for the quux command).
*/
class HelpSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
global $lang;
if ($argc == 1) {
$this->SmartLine->puts($lang['DefaultHelp']);
} elseif (!$this->SmartLine->isRegistered($argv[1])) {
$this->SmartLine->puts(sprintf($lang['InvalidCommand'], str_replace(' ', '&nbsp;', $argv[1])), STDERR);
} else {
$command = strtolower($argv[1]);
if (!$help = $this->SmartLine->gethelp($command)) {
if (array_key_exists($command, $lang['Help'])) {
$help = $lang['Help'][$command];
} else {
$help = $lang['NotYetHelpForThiscommand'];
}
}
$this->SmartLine->puts($help);
}
}
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/includes/SmartLine/ZedCommands.php b/includes/SmartLine/ZedCommands.php
index 05b5ed0..ed4fab7 100644
--- a/includes/SmartLine/ZedCommands.php
+++ b/includes/SmartLine/ZedCommands.php
@@ -1,540 +1,545 @@
<?php
/**
* Zed SmartLine commands.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This is the SmartLine subcontroller.
*
* The SmartLine is a widget allowing to add some basic CLI capability.
*
* It executes any command given in GET or POST request (parameter C).
*
* This files also provides SmartLine history helper: a method log_C to log
* a SmartLine command and some procedural code assigning a SmartLineHistory.
*
* This code is inspired from Viper, a corporate PHP intranet I wrote in 2004.
* There, the SmartLine allowed to change color theme or to find quickly user,
* account, order or server information in a CRM context.
*
* @package Zed
* @subpackage SmartLine
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2010 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @filesource
*
* @todo SettingsSmartLineCommand - understand why dojo floating pane isn't rendered if we est $controller instead to redirect
*/
///
/// Register commands
///
use Zed\Models\Geo\Location;
use Zed\Models\Objects\Invite;
$smartLine->register_object('debug', DebugSmartLineCommand::class);
$smartLine->register_object('goto', 'GotoSmartLineCommand');
$smartLine->register_object('guid', 'GUIDSmartLineCommand');
$smartLine->register_object('invite', 'InviteSmartLineCommand');
$smartLine->register_object('invites', 'InviteSmartLineCommand');
$smartLine->register_object('list', 'ListSmartLineCommand');
$smartLine->register_object('requests', 'RequestsSmartLineCommand');
$smartLine->register_object('settings', 'SettingsSmartLineCommand');
$smartLine->register_object('unixtime', 'UnixTimeSmartLineCommand');
$smartLine->register_object('version', 'VersionSmartLineCommand');
$smartLine->register_object('whereami', 'WhereAmISmartLineCommand');
$smartLine->register_object('whoami', 'WhoAmISmartLineCommand');
///
/// Help (todo: move $lang array in lang folder)
///
$lang['Help']['debug'] = "Enable or disable debugger";
$lang['Help']['goto'] = "Go to a location";
$lang['Help']['guid'] = "Generate a GUID";
$lang['Help']['invite'] = "Generate an invite. To see the generated invites, invite list.";
$lang['Help']['list'] = "Lists specified objects (bodies, locations, or places)";
$lang['Help']['requests'] = "Checks if there are waiting requests";
$lang['Help']['settings'] = 'Go to settings page';
$lang['Help']['unixtime'] = "Prints current unixtime (seconds elapsed since 1970-01-01 00:00, UTC) or the specified unixtime date.";
$lang['Help']['version'] = "Gets Zed's software version info (Mercurial repository version, node id and if you're on the dev or prod site)";
$lang['Help']['whereami'] = "Where am I?";
$lang['Help']['whoami'] = "Who am I?";
/**
* Debugger command
*/
class DebugSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
if ($argc > 1) {
$_SESSION['debug'] = self::parseBoolean($argv[1]);
}
$this->SmartLine->puts("Debugger " . $this->getStatus());
}
private function getStatus () : string {
return $this->isEnabled() ? "enabled" : "disabled";
}
private function isEnabled () : bool {
return $_SESSION['debug'] ?? false;
}
}
/**
* The goto command
*
* Moves to the current perso to the specified location.
*/
class GotoSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*
* @todo allow .goto global local (e.g. .goto B0001001 T2C3)
* @todo determine if we allow rewrite rules to bypass can_travel rules
*/
public function run ($argv, $argc) {
- global $CurrentPerso;
+ $db = $this->SmartLine->services["db"];
+ $CurrentPerso = $this->SmartLine->services["CurrentPerso"];
if ($argc == 1) {
$this->SmartLine->puts("Where do you want to go?", STDERR);
return;
}
if ($argc > 2) {
$ignored_string = implode(" ", array_slice($argv, 2));
$this->SmartLine->puts("Warning: ignoring $ignored_string", STDERR);
}
$here = new Location($db, $CurrentPerso->location_global, $CurrentPerso->location_local);
$travel = Travel::load(); //maps content/travel.xml
//Parses the expression, by order of priority, as :
// - a rewrite rule
// - a new global location
// - a new local location (inside the current global location)
if (!$travel->try_parse_rewrite_rule($argv[1], $here, $place)) {
try {
$place = new Location($db, $argv[1]);
if ($place->equals($CurrentPerso->location_global)) {
$this->SmartLine->puts("You're already there.");
return;
}
} catch (Exception $ex) {
//Global location failed, trying local location
try {
$place = new Location($db, $CurrentPerso->location_global, $argv[1]);
} catch (Exception $ex) {
$this->SmartLine->puts($ex->getMessage(), STDERR);
return;
}
if ($place->equals($here)) {
$this->SmartLine->puts("You're already there.");
return;
}
}
}
//Could we really go there?
if (!$travel->can_travel($here, $place)) {
$this->SmartLine->puts("You can't reach that location.");
return;
}
//Moves
$CurrentPerso->move_to($place->global, $place->local);
$this->SmartLine->puts("You travel to that location.");
return;
}
}
/**
* The GUID command
*
* Prints a new GUID.
*
* guid 8 will print 8 guid
*/
class GUIDSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
if ($argc > 1 && is_numeric($argv[1])) {
for ($i = 0 ; $i < $argv[1] ; $i++) {
$this->SmartLine->puts(new_guid());
}
return;
}
$this->SmartLine->puts(new_guid());
}
}
/**
* The invite command
*
* Manages invites.
*
* invite [add]
* creates a new invite code
*
* invite del <invite code>
* deletes the specified invite
*
* invite list
* prints current invite codes
*/
class InviteSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
- require_once('includes/objects/invite.php');
- global $CurrentUser, $CurrentPerso;
+ $db = $this->SmartLine->services["db"];
+ $CurrentPerso = $this->SmartLine->services["CurrentPerso"];
$command = ($argc > 1) ? strtolower($argv[1]) : '';
switch ($command) {
case 'list':
$codes = Invite::get_invites_from($db, $CurrentPerso);
if (!count($codes)) {
$this->SmartLine->puts("No invite code.");
} else {
foreach ($codes as $code) {
$this->SmartLine->puts($code);
}
}
break;
case 'add':
case '':
$code = Invite::create($db, $CurrentPerso);
$url = get_server_url() . get_url('invite', $code);
$this->SmartLine->puts("New invite code created: $code<br />Invite URL: $url");
break;
case 'del':
$code = $argv[2];
if (!preg_match("/^([A-Z]){3}([0-9]){3}$/i", $code)) {
$this->SmartLine->puts("Invalid code format. Use invite list to get all your invite codes.", STDERR);
} else {
$invite = new Invite($code);
if ($CurrentPerso->id == $invite->from_perso_id) {
$invite->delete();
$this->SmartLine->puts("Deleted");
} else {
$this->SmartLine->puts("Invalid code. Use invite list to get all your invite codes.", STDERR);
}
}
break;
default:
$this->SmartLine->puts("Usage: invite [add|list|del <code>]", STDERR);
break;
}
}
}
/**
* The list command
*
* Prints a list of bodies, locations or places.
*
* This can easily be extended to output any list from any table.
*/
class ListSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
if ($argc == 1) {
$this->SmartLine->puts("Available lists: bodies, locations, places");
return;
}
switch ($objects = $argv[1]) {
case 'bodies':
$list = $this->get_list(TABLE_BODIES, "CONCAT('B', body_code)", "body_name");
$this->SmartLine->puts($list);
break;
case 'locations':
$list = $this->get_list(TABLE_LOCATIONS, "location_code", "location_name");
$this->SmartLine->puts($list);
break;
case 'places':
if ($argv[2] == "-a" || $argv[2] == "--all") {
//Global bodies places list
$list = $this->get_list(TABLE_PLACES, "CONCAT('B', body_code, place_code)", "place_name");
} else {
//Local places (or equivalent) list
global $CurrentPerso;
switch ($CurrentPerso->location_global[0]) {
case 'B':
$body_code = substr($CurrentPerso->location_global, 1, 5);
$list = $this->get_list(TABLE_PLACES, "CONCAT('B', body_code, place_code)", "place_name", "body_code = $body_code");
break;
case 'S':
$this->SmartLine->puts("I don't have a map of the spaceship.", STDERR);
return;
default:
$this->SmartLine->puts("Unknown location type. Can only handle B or S.", STDERR);
return;
}
}
$this->SmartLine->puts($list);
break;
default:
$this->SmartLine->puts("Unknown objects to list: $objects", STDERR);
}
}
/**
* Gets a custom list from the specified table and fields.
*
* The list will be sorted by the specified key, using ascending order.
*
* @param $table the table to query from the database
* @param $key the first field to fetch, as key
* @param $value the second field to fetch, as value
* @param $where the WHERE clause, without the WHERE keyword (optional)
*/
public function get_list ($table, $key, $value, $where = null) {
- global $db;
+ $db = $this->SmartLine->services["db"];
+
$sql = "SELECT $key as `key`, $value as value FROM $table ";
if ($where) {
$sql .= "WHERE $where ";
}
$sql .= "ORDER BY `key` ASC";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to fetch list", '', __LINE__, __FILE__, $sql);
}
while ($row = $db->fetchRow($result)) {
$rows .= "<tr><td>$row[key]</td><td>$row[value]</td></tr>";
}
$this->SmartLine->truncate(STDERR); //kludge
return "<table cellspacing=\"8\"><thead style=\"color: white\" scope=\"row\"><tr><th>Key</th><th>Value</th></thead><tbody>$rows</tbody></table>";
}
}
/**
* The requests command
*
* Redirects user to the requests page.
*
* By default only redirect if a flag indicates there's a new request.
*
* To forcefully goes to the request page, requests --force
*/
class RequestsSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
- global $CurrentPerso;
+ $CurrentPerso = $this->SmartLine->services["CurrentPerso"];
+
$force = ($argc > 1) && ($argv[1] == "-f" || $argv[1] == "--force");
if ($force || (array_key_exists('site.requests', $CurrentPerso->flags) && $CurrentPerso->flags['site.requests'])) {
global $controller;
$controller = 'controllers/persorequest.php';
} else {
$this->SmartLine->puts("No request waiting.");
}
}
}
/**
* The settings command
*
* Redirects user to the settings page.
*/
class SettingsSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
if (headers_sent()) {
global $controller;
$controller = 'controllers/settings.php';
} else {
header('location: ' . get_url('settings'));
}
}
}
/**
* The unixtime command
*
* Prints current unixtime (seconds elapsed since 1970-01-01 00:00, UTC)
* or if an unixtime is specified as argument, the matching date.
*/
class UnixTimeSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
date_default_timezone_set('UTC');
if ($argc == 1) {
$this->SmartLine->puts(time());
} elseif ($argc == 2 && is_numeric($argv[1])) {
$this->SmartLine->puts(strftime("%Y-%m-%d %X", $argv[1]));
$this->SmartLine->puts(get_hypership_time($argv[1]));
} else {
array_shift($argv);
$date = implode(' ', $argv);
if ($time = strtotime($date) !== false) {
$this->SmartLine->puts("Unixtime from $date: <span class=\"highlight\">$time</span>");
} else {
$this->SmartLine->puts("$date isn't a unixtime nor a valid date strtotime is able to parse.", STDERR);
}
}
}
}
/**
* The version command
*
* Prints current hg revision, if we're in prod or dev environment and
* the current revision's hash.
*
* The version and env information is extracted from
* .hg/tags.cache (indicating we're in a Mercurial repo and so in a dev environment), or from
* version.txt file (indicating we've deployed code in a production environment)
*
* e.g. r130 (development environment)
* Hash: 057bf394741706fd2136541e3bb07c9e60b4963d
*/
class VersionSmartLineCommand extends SmartLineCommand {
const GIT_FOLDER = ".git";
private static function getGitHash () : string {
$head = trim(file_get_contents(self::GIT_FOLDER . "/HEAD"));
if (str_starts_with($head, "ref: ")) {
// Follows reference
$ref = substr($head, 5);
return file_get_contents(self::GIT_FOLDER . "/$ref");
}
return $head;
}
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
if (file_exists('.hg/tags.cache')) {
//Gets .hg revision
$content = file_get_contents('.hg/tags.cache');
$info = explode(' ', $content, 2);
$info[] = "development environment";
$this->SmartLine->puts("r$info[0] ($info[2])");
$this->SmartLine->puts("Hash: $info[1]");
} elseif (file_exists('.git/HEAD')) {
$hash = self::getGitHash();
$this->SmartLine->puts("Hash: $hash");
} elseif (file_exists('version.txt')) {
$content = file('version.txt');
foreach ($content as $line) {
$this->SmartLine->puts($line);
}
} else {
$this->SmartLine->puts("No version information available.", STDERR);
return false;
}
return true;
}
}
/**
* The whereami (Where am I?) command
*
* Prints current position, e.g. B00001001 - Tour, Hypership
*/
class WhereAmISmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
- global $CurrentPerso;
+ $db = $this->SmartLine->services["db"];
+ $CurrentPerso = $this->SmartLine->services["CurrentPerso"];
$place = new Location($db, $CurrentPerso->location_global);
$this->SmartLine->puts($CurrentPerso->location_global . ' - ' . $place);
}
}
/**
* The whoami (Who am I?) command
*
* Prints current position, e.g. B00001001 - Tour, Hypership
*/
class WhoAmISmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
- global $CurrentPerso;
+ $CurrentPerso = $this->SmartLine->services["CurrentPerso"];
+
$reply = "<span id=\"whoami.nickname\">$CurrentPerso->nickname</span> (<span id=\"whoami.name\">$CurrentPerso->name</span>), <span id=\"whoami.race\">$CurrentPerso->race</span>";
$this->SmartLine->puts($reply);
}
}
diff --git a/includes/SmartLine/ZedSmartLine.php b/includes/SmartLine/ZedSmartLine.php
index 616789c..a0107aa 100644
--- a/includes/SmartLine/ZedSmartLine.php
+++ b/includes/SmartLine/ZedSmartLine.php
@@ -1,112 +1,117 @@
<?php
/**
* The Zed SmartLine subcontroller.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This is the SmartLine subcontroller.
*
* The SmartLine is a widget allowing to add some basic CLI capability.
*
* It executes any command given in GET or POST request (parameter C).
*
* This files also provides SmartLine history helper: a method log_C to log
* a SmartLine command and some procedural code assigning a SmartLineHistory.
*
* This code is inspired from Viper, a corporate PHP intranet I wrote in 2004.
* There, the SmartLine allowed to change color theme or to find quickly user,
* account, order or server information in a CRM context.
*
* @package Zed
* @subpackage SmartLine
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2010 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @filesource
*
* @todo Caches SmartLine history
*/
///
/// Helpers
///
/**
* Logs a Smartline command
*
* @param string $command the command to log
* @param bool $isError indicates if the command is an error
*/
function log_C ($command, $isError = false) {
global $db, $CurrentPerso;
$isError = $isError ? 1 : 0;
$command = $db->escape($command);
$sql = "INSERT INTO " . TABLE_LOG_SMARTLINE . " (perso_id, command_time, command_text, isError)
VALUES ($CurrentPerso->id, UNIX_TIMESTAMP(), '$command', $isError)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't log SmartLine command", '', __LINE__, __FILE__, $sql);
}
}
///
/// Executes command
///
if (isset($_REQUEST['C'])) {
$command = $_REQUEST['C'];
//Initializes SmartLine object
require_once("SmartLine.php");
$smartLine = new SmartLine();
+ $smartLine->services = [
+ "db" => $db,
+ "CurrentUser" => $CurrentUser,
+ "CurrentPerso" => $CurrentPerso,
+ ];
require_once("ZedCommands.php");
//Executes SmartLine
$controller = '';
$smartLine->execute($command);
$error = $smartLine->count(STDERR) > 0;
if ($smartLine->count(STDOUT) > 0) {
$smarty->assign("SmartLine_STDOUT", $smartLine->gets_all(STDOUT, '', '<br />'));
}
if ($error) {
$smarty->assign("SmartLine_STDERR", $smartLine->gets_all(STDERR, '', '<br />'));
}
if ($controller != '') {
include($controller);
}
log_C($command, $error);
}
///
/// Gets SmartLine history
///
$perso_id = $db->escape($CurrentPerso->id);
$sql = "SELECT command_time, command_text FROM log_smartline
WHERE isError = 0 AND perso_id = '$perso_id'
ORDER BY command_time DESC LIMIT 100";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get SmartLine history", '', __LINE__, __FILE__, $sql);
}
$commands = [];
while ($row = $db->fetchRow($result)) {
$commands[] = [
"time" => get_hypership_time($row['command_time']),
"text" => $row['command_text'],
];
}
$smarty->assign("SmartLineHistory", $commands);
diff --git a/includes/core.php b/includes/core.php
index 76d9d24..ea7ced5 100644
--- a/includes/core.php
+++ b/includes/core.php
@@ -1,654 +1,651 @@
<?php
/**
* Core: helper methods and main libraries loader
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Keruald
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2010 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @filesource
*/
+use Keruald\Database\DatabaseEngine;
use Keruald\OmniTools\Collections\TraversableUtilities;
////////////////////////////////////////////////////////////////////////////////
/// ///
/// Configures PHP and loads site-wide used libraries ///
/// ///
////////////////////////////////////////////////////////////////////////////////
require_once(__DIR__ . "/../vendor/autoload.php");
include_once("autoload.php");
error_reporting(E_ALL & ~E_NOTICE);
include_once("config.php");
include_once("error.php");
include_once("sessions.php");
////////////////////////////////////////////////////////////////////////////////
/// ///
/// Information helper methods ///
/// ///
////////////////////////////////////////////////////////////////////////////////
/**
* Gets the nickname from the specified perso ID
*
* @param integer $perso_id The specified perso ID
* @return string The perso's nickname
*/
function get_name ($perso_id) {
global $db;
$perso_id = $db->escape($perso_id);
$sql = 'SELECT perso_nickname FROM '. TABLE_PERSOS . " WHERE perso_id = '$perso_id'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't query persos table.", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return $row['perso_nickname'];
}
/**
* Gets the user ID from the specified username
*
* @param string $username The username
* @return integer the user ID
*/
function get_userid ($username) {
global $db;
$username = $db->escape($username);
$sql = 'SELECT user_id FROM '. TABLE_USERS . " WHERE username LIKE '$username'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't query users table.", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return $row['user_id'];
}
/**
* Gets an information from the application global registry
- *
- * @param string $key the registry's key
- * @return string The key value
*/
-function registry_get ($key) {
- global $db;
+function registry_get (DatabaseEngine $db, string $key) : string {
$key = $db->escape($key);
$sql = "SELECT registry_value FROM " . TABLE_REGISTRY . " WHERE registry_key = '$key'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't read registry.", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return $row['registry_value'];
}
/**
* Sets an information in the application global registry
*
+ * @param DatabaseEngine $db
* @param string $key the registry key
* @param string $value the value to store at the specified registry key
*/
-function registry_set ($key, $value) {
- global $db;
+function registry_set (DatabaseEngine $db, string $key, string $value) : void {
$key = $db->escape($key);
$value = $db->escape($value);
$sql = "REPLACE INTO " . TABLE_REGISTRY . " (registry_key, registry_value) VALUES ('$key', '$value')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't update registry", '', __LINE__, __FILE__, $sql);
}
}
////////////////////////////////////////////////////////////////////////////////
/// ///
/// Misc helper methods ///
/// ///
////////////////////////////////////////////////////////////////////////////////
//Plural management
/**
* Returns "s" when the $amount request a plural
* This function is a French plural helper.
*
* @return string 's' if $amount implies a plural ; '' if it implies a singular.
*/
function s (int $amount) : string {
if ($amount >= 2 || $amount <= -2) {
return "s";
}
return "";
}
/**
* Returns "x" when the $amount request a plural
* This function is a French plural helper.
*
* @return string 'x' if $amount implies a plural ; '' if it implies a singular.
*/
function x (int $amount) : string {
if ($amount >= 2 || $amount <= -2) {
return "x";
}
return "";
}
//Debug
/**
* Prints human-readable information about a variable.
*
* It behaves like the print_r command, but the output is enclosed in pre tags,
* to have a preformatted HTML output.
*
* @param mixed $expression The expression to be printed
*/
function dprint_r ($expression) {
echo '<pre>';
print_r($expression);
echo '</pre>';
}
//GUID
/**
* Generates a GUID, or more precisely an UUID
* @link http://en.wikipedia.org/wiki/Universally_Unique_Identifier Wikipedia, Universally Unique Identifier.
*
* A UUID is a 36 chars string of 32 hexadecimal and 4 dashes, with a
* very high probability to be unique.
*
* @return string the UUID
*/
function new_guid() {
$characters = explode(",", "a,b,c,d,e,f,0,1,2,3,4,5,6,7,8,9");
$guid = "";
for ($i = 0 ; $i < 36 ; $i++) {
if ($i == 8 || $i == 13 || $i == 18 || $i == 23) {
$guid .= "-";
} else {
$guid .= $characters[mt_rand() % sizeof($characters)];
}
}
return $guid;
}
/**
* Determines if the expression is a valid UUID (a guid without {}).
* @see new_guid
*
* @param string $expression the expression to check
* @return boolean true if the specified expression is a valid UUID ; otherwise, false.
*/
function is_guid ($expression) {
//We avoid regexp to speed up the check
//A guid is a 36 characters string
if (strlen($expression) != 36) {
return false;
}
$expression = strtolower($expression);
for ($i = 0 ; $i < 36 ; $i++) {
if ($i == 8 || $i == 13 || $i == 18 || $i == 23) {
//with dashes
if ($expression[$i] != "-") {
return false;
}
} else {
//and numbers
if (!is_numeric($expression[$i]) && $expression[$i] != 'a' && $expression[$i] != 'b' && $expression[$i] != 'c' && $expression[$i] != 'd' && $expression[$i] != 'e' && $expression[$i] != 'f' ) {
return false;
}
}
}
return true;
}
/**
* Gets file extension
*
* @param string $file the file to get the extension
* @return string the extension from the specified file
*/
function get_extension ($file) {
$dotPosition = strrpos($file, ".");
return substr($file, $dotPosition + 1);
}
/**
* Inserts a message into the supralog
*
* @param string $category the entry category
* @param string $message the message to log
* @param string|null $source the entry source.
*/
function supralog (string $category, string $message, ?string $source = null) {
global $db, $CurrentUser, $CurrentPerso;
$category = $db->escape($category);
$message = $db->escape($message);
$source = $db->escape($source ?: $_SERVER['SERVER_ADDR']);
$ip = $_SERVER['REMOTE_ADDR'];
$sql = "INSERT INTO " . TABLE_LOG .
" (entry_ip, user_id, perso_id, entry_category, entry_message, entry_source) VALUES
('$ip', $CurrentUser->id, $CurrentPerso->id, '$category', '$message', '$source')";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Can't log this entry.", '', __LINE__, __FILE__, $sql);
}
}
////////////////////////////////////////////////////////////////////////////////
/// ///
/// Localization (l10n) ///
/// ///
////////////////////////////////////////////////////////////////////////////////
/**
* Defines the LANG constant, to lang to print
*
* This information is contained in the session, or if not yet defined,
* it's to determine according the user's browser preferences.
* @see find_lang
*/
function initialize_lang () : void {
//If $_SESSION['lang'] doesn't exist yet, find a common language
if (!array_key_exists('lang', $_SESSION)) {
$_SESSION['lang'] = find_lang();
}
define('LANG', $_SESSION['lang']);
}
/**
* Gets a common lang spoken by the site and the user's browser
*
* @param string|null $fallback The language when no common language is found
* @return string The language to use
* @see get_http_accept_languages
*/
function find_lang (string $fallback = null) : string {
if (!file_exists('lang') || !is_dir('lang')) {
throw new RuntimeException(
"A lang/ folder is expected with by language subdirectories"
);
}
//Gets lang/ subdirectories: this is the list of available languages
$handle = opendir('lang');
$langs = [];
while ($file = readdir($handle)) {
if ($file != '.' && $file != '..' && is_dir("lang/$file")) {
$langs[] = $file;
}
}
if (count($langs) === 0) {
throw new RuntimeException(
"A lang/ folder is expected with by language subdirectories"
);
}
//The array $langs contains now the language available.
//Gets the langs the user should want:
if (!$userlangs = get_http_accept_languages()) {
return TraversableUtilities::first($langs);
}
//Gets the intersection between the both languages arrays
//If it matches, returns first result
$intersect = array_intersect($userlangs, $langs);
if (count($intersect)) {
return TraversableUtilities::first($intersect);
}
//Now it's okay with Opera and Firefox but Internet Explorer will
//by default return en-US and not en or fr-BE and not fr, so second pass
$userlangs_simplified = [];
foreach ($userlangs as $userlang) {
$lang = explode('-', $userlang);
if (count($lang) > 1) {
$userlangs_simplified[] = $lang[0];
}
}
$intersect = array_intersect($userlangs_simplified, $langs);
if (count($intersect)) {
return TraversableUtilities::first($intersect);
}
// It's not possible to find a common language, return the first one.
return $fallback ?? TraversableUtilities::first($langs);
}
/**
* Gets the languages accepted by the browser, by order of priority.
*
* This will read the HTTP_ACCEPT_LANGUAGE variable sent by the browser in the
* HTTP request.
*
* @return string[] list of the languages accepted by browser
*/
function get_http_accept_languages () : array {
//What language to print is sent by browser in HTTP_ACCEPT_LANGUAGE var.
//This will be something like en,fr;q=0.8,fr-fr;q=0.5,en-us;q=0.3
if (!array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER)) {
return [];
}
$http_accept_language = explode(',', $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
foreach ($http_accept_language as $language) {
$userlang = explode(';q=', $language);
if (count($userlang) == 1) {
$userlangs[] = [1, $language];
} else {
$userlangs[] = [$userlang[1], $userlang[0]];
}
}
rsort($userlangs);
$result = [];
foreach ($userlangs as $userlang) {
$result[] = $userlang[1];
}
return $result;
}
/**
* Loads specified language Smarty configuration file
*
* @param string $file the file to load
* @param mixed|null $sections array of section names, single section or null
*/
function lang_load (string $file, mixed $sections = null) : void {
global $smarty;
//Loads English file as fallback if some parameters are missing
if (file_exists("lang/en/$file")) {
$smarty->configLoad("lang/en/$file", $sections);
}
//Loads wanted file (if it exists and a language have been defined)
if (defined('LANG') && LANG != 'en' && file_exists('lang/' . LANG . '/' . $file)) {
$smarty->configLoad('lang/' . LANG . '/' . $file, $sections);
}
}
/**
* Gets a specified language expression defined in configuration file
*
* @param string $key the configuration key matching the value to get
* @return string The value in the configuration file
*/
function lang_get (string $key) : string {
global $smarty;
$smartyConfValue = $smarty->config_vars[$key];
return $smartyConfValue ?: "#$key#";
}
////////////////////////////////////////////////////////////////////////////////
/// ///
/// Zed date and time helper methods ///
/// ///
////////////////////////////////////////////////////////////////////////////////
/**
* Converts a YYYYMMDD or YYYY-MM-DD timestamp to unixtime
* @link http://en.wikipedia.org/wiki/Unix_time Unix time
*
* @param string $timestamp the timestamp to convert
* @return integer the unixtime
*/
function to_unixtime ($timestamp) {
switch (strlen($timestamp)) {
case 8:
//YYYYMMDD
return mktime(0, 0, 0, substr($timestamp, 4, 2), substr($timestamp, 6, 2), substr($timestamp, 0, 4));
case 10:
//YYYY-MM-DD
return mktime(0, 0, 0, substr($timestamp, 5, 2), substr($timestamp, 8, 2), substr($timestamp, 0, 4));
default:
throw new Exception("timestamp is not a valid YYYYMMDD or YYYY-MM-DD timestamp: $timestamp");
}
}
/**
* Converts a unixtime to the YYYYMMDD or YYYY-MM-DD timestamp format
* @see to_unixtime
*
* @param int $unixtime the time to convert
* @param int $format 8 or 10. If 8 (default), will output YYYYMMDD. If 10, YYYY-MM-DD.
* @return string the timestamp
*/
function to_timestamp ($unixtime = null, $format = 8) {
//If no parameter is specified (or null, or false), current time is used
//==== allows to_timestamp(0) to return correct 1970-1-1 value.
if ($unixtime === null || $unixtime === false) {
$unixtime = time();
}
switch ($format) {
case 8:
//YYYYMMDD
return date('Ymd', $unixtime);
case 10:
//YYYY-MM-DD
return date('Y-m-d', $unixtime);
default:
throw new Exception("format must be 8 (YYYYMMDD) or 10 (YYYY-MM-DD) and not $format.");
}
}
/**
* Converts a unixtime to the Hypership time format or gets the current hypership time.
* @link http://en.wikipedia.org/wiki/Unix_time
* @link http://www.purl.org/NET/Zed/blog/HyperShipTime
*
* @param int $unixtime The unixtime to convert to HyperShip time. If omitted, the current unixtime.
* @return string The HyperShip time
*/
function get_hypership_time ($unixtime = null) {
//If unixtime is not specified, it's now
if ($unixtime === null) {
$unixtime = time();
}
//Hypership time is a count of days since launch @ 2010-07-03 00:00:00
//Followed by a fraction of the current day /1000, like the internet time
//but in UTC timezone and not Switzerland CET/CEST.
//We don't need to use floor(), as we output the result at int, truncating
//automatically decimal values instead of round it (like in C).
$seconds = $unixtime - 1278115200;
$days = $seconds / 86400;
$fraction = (abs($seconds) % 86400) / 86.4;
return sprintf("%d.%03d", $days, $fraction);
}
////////////////////////////////////////////////////////////////////////////////
/// ///
/// URL helpers functions ///
/// ///
////////////////////////////////////////////////////////////////////////////////
/**
* Gets the URL matching the specified resource.
*
* Example:
* <code>
* $url = get_url('ship', $ship);
* echo $url; //if $ship contains S00001, this should print /ship/S00001
* </code>
*
* @param string $resource,... the resources
* @return string the URL matching the specified resource
*/
function get_url () {
global $Config;
if (func_num_args() > 0) {
$pieces = func_get_args();
return $Config['BaseURL'] . '/' . implode('/', $pieces);
} elseif ($Config['BaseURL'] == "" || $Config['BaseURL'] == $_SERVER["PHP_SELF"]) {
return "/";
} else {
return $Config['BaseURL'];
}
}
/**
* Gets the current page URL
*
* @return string the current page URL
*/
function get_page_url () {
$url = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
if (substr($url, -10) == $_SERVER["PHP_SELF"]) {
return substr($url, 0, -9);
}
return $url;
}
/**
* Gets the server URL
* @todo find a way to detect https:// on non standard port
*
* @return string the server URL
*/
function get_server_url () {
switch ($port = $_SERVER['SERVER_PORT']) {
case '80':
return "http://$_SERVER[SERVER_NAME]";
case '443':
return "https://$_SERVER[SERVER_NAME]";
default:
return "http://$_SERVER[SERVER_NAME]:$_SERVER[SERVER_PORT]";
}
}
/**
* Gets $_SERVER['PATH_INFO'] or computes the equivalent if not defined.
*
* This function allows the entry point controllers to get the current URL
* in a consistent way, for any redirection configuration
*
* So with /foo/bar, /index.php/foo/bar, /zed/index.php/foo/bar or /zed/foo/bar
* get_current_url will return /foo/bar
*
* @return string the relevant URL part
*/
function get_current_url () {
global $Config;
//Gets relevant URL part from relevant $_SERVER variables
if (array_key_exists('PATH_INFO', $_SERVER)) {
//Without mod_rewrite, and url like /index.php/controller
//we use PATH_INFO. It's the easiest case.
return $_SERVER["PATH_INFO"];
}
//In other cases, we'll need to get the relevant part of the URL
$current_url = get_server_url() . $_SERVER['REQUEST_URI'];
//Relevant URL part starts after the site URL
$len = strlen($Config['SiteURL']);
//We need to assert it's the correct site
if (substr($current_url, 0, $len) != $Config['SiteURL']) {
dieprint_r( "Edit includes/config.php and specify the correct site URL<br /><strong>Current value:</strong> $Config[SiteURL]<br /><strong>Expected value:</strong> a string starting by " . get_server_url(), "Setup");
}
if (array_key_exists('REDIRECT_URL', $_SERVER)) {
//With mod_rewrite, we can use REDIRECT_URL
//We takes the end of the URL, ie *FROM* $len position
return substr(get_server_url() . $_SERVER["REDIRECT_URL"], $len);
}
//Last possibility: use REQUEST_URI, but remove QUERY_STRING
//If you need to edit here, use $_SERVER['REQUEST_URI']
//but you need to discard $_SERVER['QUERY_STRING']
//We takes the end of the URL, ie *FROM* $len position
$url = substr(get_server_url() . $_SERVER["REQUEST_URI"], $len);
//But if there are a query string (?action=... we need to discard it)
if ($_SERVER['QUERY_STRING']) {
return substr($url, 0, strlen($url) - strlen($_SERVER['QUERY_STRING']) - 1);
}
return $url;
}
/**
* Gets an array of url fragments to be processed by controller
* @see get_current_url
*
* This method is used by the controllers entry points to know the URL and
* call relevant subcontrollers.
*
* @return Array an array of string, one for each URL fragment
*/
function get_current_url_fragments () {
return explode('/', substr(get_current_url(), 1));
}
////////////////////////////////////////////////////////////////////////////////
/// ///
/// URL xmlHttpRequest helpers functions ///
/// ///
////////////////////////////////////////////////////////////////////////////////
/**
* Gets an hash value to check the integrity of URLs in /do.php calls
*
* @param Array $args the args to compute the hash
* @return string the hash parameter for your xmlHttpRequest url
*/
function get_xhr_hash (array $args) : string {
global $Config;
array_shift($args);
return md5($_SESSION['ID'] . $Config['SecretKey'] . implode('', $args));
}
/**
* Gets the URL to call do.php, the xmlHttpRequest controller
*
* @return string the xmlHttpRequest url, with an integrity hash
*/
function get_xhr_hashed_url () : string {
global $Config;
$args = func_get_args();
$args[] = get_xhr_hash($args);
return $Config['DoURL'] . '/' . implode('/', $args);
}
/**
* Gets the URL to call do.php, the xmlHttpRequest controller
*
* @return string the xmlHttpRequest url
*/
function get_xhr_url () : string {
global $Config;
$args = func_get_args();
return $Config['DoURL'] . '/' .implode('/', $args);
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 25, 05:27 (5 h, 21 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
20761
Default Alt Text
(81 KB)

Event Timeline